wip
This commit is contained in:
		
							
								
								
									
										38
									
								
								packages/frontend/src/components/MkSearchMarker.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								packages/frontend/src/components/MkSearchMarker.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| <!-- | ||||
| SPDX-FileCopyrightText: syuilo and misskey-project | ||||
| SPDX-License-Identifier: AGPL-3.0-only | ||||
| --> | ||||
|  | ||||
| <template> | ||||
| <div :class="[$style.root, { [$style.highlighted]: highlighted }]"> | ||||
| 	<slot></slot> | ||||
| </div> | ||||
| </template> | ||||
|  | ||||
| <script lang="ts" setup> | ||||
| import { onMounted, onUnmounted, ref, shallowRef } from 'vue'; | ||||
|  | ||||
| const props = defineProps<{ | ||||
| 	markerId: string; | ||||
| }>(); | ||||
|  | ||||
| const highlighted = window.location.hash.slice(1) === props.markerId; | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" module> | ||||
| .root { | ||||
| } | ||||
|  | ||||
| .highlighted { | ||||
| 	animation: blink 1s infinite; | ||||
| } | ||||
|  | ||||
| @keyframes blink { | ||||
| 	0%, 100% { | ||||
| 		box-shadow: 0 0 0 2px color(from var(--MI_THEME-accent) srgb r g b / 0.3); | ||||
| 	} | ||||
| 	50% { | ||||
| 		box-shadow: 0 0 0 2px transparent; | ||||
| 	} | ||||
| } | ||||
| </style> | ||||
| @@ -5,26 +5,40 @@ SPDX-License-Identifier: AGPL-3.0-only | ||||
|  | ||||
| <template> | ||||
| <div class="rrevdjwu" :class="{ grid }"> | ||||
| 	<div v-for="group in def" class="group"> | ||||
| 		<div v-if="group.title" class="title">{{ group.title }}</div> | ||||
| 	<MkInput v-model="search" :placeholder="i18n.ts.search" type="search" style="margin-bottom: 16px;" @keydown.enter="searchSubmit"> | ||||
| 		<template #prefix><i class="ti ti-search"></i></template> | ||||
| 	</MkInput> | ||||
|  | ||||
| 		<div class="items"> | ||||
| 			<template v-for="(item, i) in group.items"> | ||||
| 				<a v-if="item.type === 'a'" :href="item.href" :target="item.target" class="_button item" :class="{ danger: item.danger, active: item.active }"> | ||||
| 					<span v-if="item.icon" class="icon"><i :class="item.icon" class="ti-fw"></i></span> | ||||
| 					<span class="text">{{ item.text }}</span> | ||||
| 				</a> | ||||
| 				<button v-else-if="item.type === 'button'" class="_button item" :class="{ danger: item.danger, active: item.active }" :disabled="item.active" @click="ev => item.action(ev)"> | ||||
| 					<span v-if="item.icon" class="icon"><i :class="item.icon" class="ti-fw"></i></span> | ||||
| 					<span class="text">{{ item.text }}</span> | ||||
| 				</button> | ||||
| 				<MkA v-else :to="item.to" class="_button item" :class="{ danger: item.danger, active: item.active }"> | ||||
| 					<span v-if="item.icon" class="icon"><i :class="item.icon" class="ti-fw"></i></span> | ||||
| 					<span class="text">{{ item.text }}</span> | ||||
| 				</MkA> | ||||
| 			</template> | ||||
| 	<template v-if="search == ''"> | ||||
| 		<div v-for="group in def" class="group"> | ||||
| 			<div v-if="group.title" class="title">{{ group.title }}</div> | ||||
|  | ||||
| 			<div class="items"> | ||||
| 				<template v-for="(item, i) in group.items"> | ||||
| 					<a v-if="item.type === 'a'" :href="item.href" :target="item.target" class="_button item" :class="{ danger: item.danger, active: item.active }"> | ||||
| 						<span v-if="item.icon" class="icon"><i :class="item.icon" class="ti-fw"></i></span> | ||||
| 						<span class="text">{{ item.text }}</span> | ||||
| 					</a> | ||||
| 					<button v-else-if="item.type === 'button'" class="_button item" :class="{ danger: item.danger, active: item.active }" :disabled="item.active" @click="ev => item.action(ev)"> | ||||
| 						<span v-if="item.icon" class="icon"><i :class="item.icon" class="ti-fw"></i></span> | ||||
| 						<span class="text">{{ item.text }}</span> | ||||
| 					</button> | ||||
| 					<MkA v-else :to="item.to" class="_button item" :class="{ danger: item.danger, active: item.active }"> | ||||
| 						<span v-if="item.icon" class="icon"><i :class="item.icon" class="ti-fw"></i></span> | ||||
| 						<span class="text">{{ item.text }}</span> | ||||
| 					</MkA> | ||||
| 				</template> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 	</div> | ||||
| 	</template> | ||||
| 	<template v-else> | ||||
| 		<div v-for="item in searchResult"> | ||||
| 			<MkA :to="item.path + '#' + item.id" class="_button searchResultItem"> | ||||
| 				<span v-if="item.icon" class="icon"><i :class="item.icon" class="ti-fw"></i></span> | ||||
| 				<span class="text">{{ item.locationLabel.join(' > ') }}</span> | ||||
| 			</MkA> | ||||
| 		</div> | ||||
| 	</template> | ||||
| </div> | ||||
| </template> | ||||
|  | ||||
| @@ -58,10 +72,52 @@ export type SuperMenuDef = { | ||||
| </script> | ||||
|  | ||||
| <script lang="ts" setup> | ||||
| import { ref, watch } from 'vue'; | ||||
| import MkInput from '@/components/MkInput.vue'; | ||||
| import { i18n } from '@/i18n.js'; | ||||
|  | ||||
| defineProps<{ | ||||
| 	def: SuperMenuDef[]; | ||||
| 	grid?: boolean; | ||||
| }>(); | ||||
|  | ||||
| const search = ref(''); | ||||
| const searchResult = ref<any[]>([]); | ||||
|  | ||||
| const INDEX = [{ | ||||
| 	id: '727cc9e8-ad67-474a-9241-b5a9a6475e47', | ||||
| 	locationLabel: [i18n.ts.profile, i18n.ts._profile.name], | ||||
| 	icon: 'ti ti-user', | ||||
| 	keywords: ['name'], | ||||
| 	path: '/settings/profile', | ||||
| }, { | ||||
| 	id: '1a06c7f9-e85e-46cb-bf5f-b3efa8e71b93', | ||||
| 	locationLabel: [i18n.ts.profile, i18n.ts._profile.description], | ||||
| 	icon: 'ti ti-user', | ||||
| 	keywords: ['bio'], | ||||
| 	path: '/settings/profile', | ||||
| }, { | ||||
| 	id: 'acbfe8cb-c3c9-4d90-8c62-713025814b2e', | ||||
| 	locationLabel: [i18n.ts.privacy, i18n.ts.makeFollowManuallyApprove], | ||||
| 	icon: 'ti ti-lock-open', | ||||
| 	keywords: ['follow', 'lock', i18n.ts.lockedAccountInfo], | ||||
| 	path: '/settings/privacy', | ||||
| }]; | ||||
|  | ||||
| watch(search, (value) => { | ||||
| 	if (value === '') { | ||||
| 		searchResult.value = []; | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	searchResult.value = INDEX.filter((item) => { | ||||
| 		// TODO: 日本語でひらがなカタカナの区別をしない | ||||
| 		return item.locationLabel.some((x) => x.toLowerCase().includes(value.toLowerCase())) || item.keywords.some((x) => x.toLowerCase().includes(value.toLowerCase())); | ||||
| 	}); | ||||
| }); | ||||
|  | ||||
| function searchSubmit() { | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| @@ -184,5 +240,47 @@ defineProps<{ | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	.searchResultItem { | ||||
| 		display: flex; | ||||
| 		align-items: center; | ||||
| 		width: 100%; | ||||
| 		box-sizing: border-box; | ||||
| 		padding: 9px 16px 9px 8px; | ||||
| 		border-radius: 9px; | ||||
| 		font-size: 0.9em; | ||||
|  | ||||
| 		&:hover { | ||||
| 			text-decoration: none; | ||||
| 			background: var(--MI_THEME-panelHighlight); | ||||
| 		} | ||||
|  | ||||
| 		&:focus-visible { | ||||
| 			outline-offset: -2px; | ||||
| 		} | ||||
|  | ||||
| 		&.active { | ||||
| 			color: var(--MI_THEME-accent); | ||||
| 			background: var(--MI_THEME-accentedBg); | ||||
| 		} | ||||
|  | ||||
| 		&.danger { | ||||
| 			color: var(--MI_THEME-error); | ||||
| 		} | ||||
|  | ||||
| 		> .icon { | ||||
| 			width: 32px; | ||||
| 			margin-right: 2px; | ||||
| 			flex-shrink: 0; | ||||
| 			text-align: center; | ||||
| 			opacity: 0.8; | ||||
| 		} | ||||
|  | ||||
| 		> .text { | ||||
| 			white-space: normal; | ||||
| 			padding-right: 12px; | ||||
| 			flex-shrink: 1; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| </style> | ||||
|   | ||||
| @@ -5,7 +5,10 @@ SPDX-License-Identifier: AGPL-3.0-only | ||||
|  | ||||
| <template> | ||||
| <div class="_gaps_m"> | ||||
| 	<MkSwitch v-model="isLocked" @update:modelValue="save()">{{ i18n.ts.makeFollowManuallyApprove }}<template #caption>{{ i18n.ts.lockedAccountInfo }}</template></MkSwitch> | ||||
| 	<MkSearchMarker markerId="acbfe8cb-c3c9-4d90-8c62-713025814b2e"> | ||||
| 		<MkSwitch v-model="isLocked" @update:modelValue="save()">{{ i18n.ts.makeFollowManuallyApprove }}<template #caption>{{ i18n.ts.lockedAccountInfo }}</template></MkSwitch> | ||||
| 	</MkSearchMarker> | ||||
|  | ||||
| 	<MkSwitch v-if="isLocked" v-model="autoAcceptFollowed" @update:modelValue="save()">{{ i18n.ts.autoAcceptFollowed }}</MkSwitch> | ||||
|  | ||||
| 	<MkSwitch v-model="publicReactions" @update:modelValue="save()"> | ||||
| @@ -174,6 +177,7 @@ import FormSlot from '@/components/form/slot.vue'; | ||||
| import { formatDateTimeString } from '@/scripts/format-time-string.js'; | ||||
| import MkInput from '@/components/MkInput.vue'; | ||||
| import * as os from '@/os.js'; | ||||
| import MkSearchMarker from '@/components/MkSearchMarker.vue'; | ||||
|  | ||||
| const $i = signinRequired(); | ||||
|  | ||||
|   | ||||
| @@ -18,14 +18,18 @@ SPDX-License-Identifier: AGPL-3.0-only | ||||
| 		</div> | ||||
| 	</div> | ||||
|  | ||||
| 	<MkInput v-model="profile.name" :max="30" manualSave :mfmAutocomplete="['emoji']"> | ||||
| 		<template #label>{{ i18n.ts._profile.name }}</template> | ||||
| 	</MkInput> | ||||
| 	<MkSearchMarker markerId="727cc9e8-ad67-474a-9241-b5a9a6475e47"> | ||||
| 		<MkInput v-model="profile.name" :max="30" manualSave :mfmAutocomplete="['emoji']"> | ||||
| 			<template #label>{{ i18n.ts._profile.name }}</template> | ||||
| 		</MkInput> | ||||
| 	</MkSearchMarker> | ||||
|  | ||||
| 	<MkTextarea v-model="profile.description" :max="500" tall manualSave mfmAutocomplete :mfmPreview="true"> | ||||
| 		<template #label>{{ i18n.ts._profile.description }}</template> | ||||
| 		<template #caption>{{ i18n.ts._profile.youCanIncludeHashtags }}</template> | ||||
| 	</MkTextarea> | ||||
| 	<MkSearchMarker markerId="1a06c7f9-e85e-46cb-bf5f-b3efa8e71b93"> | ||||
| 		<MkTextarea v-model="profile.description" :max="500" tall manualSave mfmAutocomplete :mfmPreview="true"> | ||||
| 			<template #label>{{ i18n.ts._profile.description }}</template> | ||||
| 			<template #caption>{{ i18n.ts._profile.youCanIncludeHashtags }}</template> | ||||
| 		</MkTextarea> | ||||
| 	</MkSearchMarker> | ||||
|  | ||||
| 	<MkInput v-model="profile.location" manualSave> | ||||
| 		<template #label>{{ i18n.ts.location }}</template> | ||||
| @@ -121,6 +125,7 @@ import MkButton from '@/components/MkButton.vue'; | ||||
| import MkInput from '@/components/MkInput.vue'; | ||||
| import MkSwitch from '@/components/MkSwitch.vue'; | ||||
| import MkSelect from '@/components/MkSelect.vue'; | ||||
| import MkSearchMarker from '@/components/MkSearchMarker.vue'; | ||||
| import FormSplit from '@/components/form/split.vue'; | ||||
| import MkFolder from '@/components/MkFolder.vue'; | ||||
| import FormSlot from '@/components/form/slot.vue'; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 syuilo
					syuilo