Feat:emoji folder
This commit is contained in:
		
							parent
							
								
									120efff6c1
								
							
						
					
					
						commit
						a3743a0622
					
				|  | @ -323,6 +323,7 @@ export interface Locale { | ||||||
|     "createFolder": string; |     "createFolder": string; | ||||||
|     "renameFolder": string; |     "renameFolder": string; | ||||||
|     "deleteFolder": string; |     "deleteFolder": string; | ||||||
|  |     "Folder": string; | ||||||
|     "addFile": string; |     "addFile": string; | ||||||
|     "emptyDrive": string; |     "emptyDrive": string; | ||||||
|     "emptyFolder": string; |     "emptyFolder": string; | ||||||
|  |  | ||||||
|  | @ -320,6 +320,7 @@ folderName: "フォルダー名" | ||||||
| createFolder: "フォルダーを作成" | createFolder: "フォルダーを作成" | ||||||
| renameFolder: "フォルダー名を変更" | renameFolder: "フォルダー名を変更" | ||||||
| deleteFolder: "フォルダーを削除" | deleteFolder: "フォルダーを削除" | ||||||
|  | Folder: "フォルダー" | ||||||
| addFile: "ファイルを追加" | addFile: "ファイルを追加" | ||||||
| emptyDrive: "ドライブは空です" | emptyDrive: "ドライブは空です" | ||||||
| emptyFolder: "フォルダーは空です" | emptyFolder: "フォルダーは空です" | ||||||
|  |  | ||||||
|  | @ -4,49 +4,129 @@ SPDX-License-Identifier: AGPL-3.0-only | ||||||
| --> | --> | ||||||
| 
 | 
 | ||||||
| <template> | <template> | ||||||
| <!-- このコンポーネントの要素のclassは親から利用されるのでむやみに弄らないこと --> |     <!-- このコンポーネントの要素のclassは親から利用されるのでむやみに弄らないこと --> | ||||||
| <section> |     <section> | ||||||
| 	<header class="_acrylic" @click="shown = !shown"> | 
 | ||||||
| 		<i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> <slot></slot> ({{ emojis.length }}) | 
 | ||||||
| 	</header> |         <header v-if="!category" class="_acrylic" @click="shown = !shown"> | ||||||
| 	<div v-if="shown" class="body"> |             <i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> | ||||||
| 		<button |             <slot></slot> | ||||||
| 			v-for="emoji in emojis" |             ({{ emojis.length }}) | ||||||
| 			:key="emoji" |         </header> | ||||||
| 			:data-emoji="emoji" |         <header v-else-if="category.length === 1" class="_acrylic" @click="shown = !shown"> | ||||||
| 			class="_button item" |             <i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> | ||||||
| 			@pointerenter="computeButtonTitle" |             {{ category[0] }} | ||||||
| 			@click="emit('chosen', emoji, $event)" |             ({{ | ||||||
| 		> |                 emojis.filter(e => category === null ? (e.category === 'null' || !e.category) : e.category === category[0]).length | ||||||
| 			<MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> |             }}) | ||||||
| 			<MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> |         </header> | ||||||
| 		</button> |         <header v-else class="_acrylic" style="top:unset;" @click="toggleShown_fol"> | ||||||
| 	</div> |             <i class="toggle ti-fw" :class="shown_fol? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> | ||||||
| </section> |             {{ category[0] || i18n.ts.other }} | ||||||
|  |             <template v-if="category.length !== 1"> | ||||||
|  |                 ({{ i18n.ts.Folder }}) | ||||||
|  |             </template> | ||||||
|  |             <template v-else> | ||||||
|  |                 ({{ | ||||||
|  |                     emojis.filter(e => category === null ? (e.category === 'null' || !e.category) : e.category === category[0]).length | ||||||
|  |                 }}) 2 | ||||||
|  |             </template> | ||||||
|  |         </header> | ||||||
|  |         <template v-for="(n, index) in category" v-if="shown_fol"> | ||||||
|  |             <header | ||||||
|  |                     v-if="emojis.filter(e => category === null ? (e.category === 'null' || !e.category) : e.category === category[0]).length !== 0 || index!==0" | ||||||
|  |                     style="top:unset;padding-left: 18px;" | ||||||
|  |                     class="_acrylic" | ||||||
|  |                     @click="toggleShown(index)" | ||||||
|  |             > | ||||||
|  |                 <i class="toggle ti-fw" :class="shown_fold[index] ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> | ||||||
|  |                 {{ n || i18n.ts.other }} | ||||||
|  |                 ({{ | ||||||
|  |                     emojis.filter(e => category === null ? (e.category === 'null' || !e.category) : e.category === (index === 0 && category !== undefined ? category[0] : `${category[0]}/${n}`)).length | ||||||
|  |                 }}) | ||||||
|  |             </header> | ||||||
|  |                 <div v-if="shown_fold[index]" class="body"> | ||||||
|  |                     <button | ||||||
|  |                             v-for="emoji in emojis.filter(e => category === null ? (e.category === 'null' || !e.category) : e.category ===( index === 0 && category !== undefined ? category[0] : `${category[0]}/${n}`)).map(e => `:${e.name}:`)" | ||||||
|  |                             :key="emoji" | ||||||
|  |                             :data-emoji="emoji" | ||||||
|  |                             class="_button item" | ||||||
|  |                             @pointerenter="computeButtonTitle" | ||||||
|  |                             @click="emit('chosen', emoji, $event)" | ||||||
|  |                     > | ||||||
|  |                         <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> | ||||||
|  |                         <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> | ||||||
|  |                     </button> | ||||||
|  |                 </div> | ||||||
|  |         </template> | ||||||
|  | 
 | ||||||
|  |         <div v-if="shown && category" class="body"> | ||||||
|  |             <button | ||||||
|  |                     v-for="emoji in emojis.filter(e => e.category === category[0]).map(e => `:${e.name}:`)" | ||||||
|  |                     :key="emoji" | ||||||
|  |                     :data-emoji="emoji" | ||||||
|  |                     class="_button item" | ||||||
|  |                     @pointerenter="computeButtonTitle" | ||||||
|  |                     @click="emit('chosen', emoji, $event)" | ||||||
|  |             > | ||||||
|  |                 <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> | ||||||
|  |                 <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> | ||||||
|  |             </button> | ||||||
|  |         </div> | ||||||
|  |         <div v-else-if="shown && !category" class="body"> | ||||||
|  |             <button | ||||||
|  |                     v-for="emoji in emojis" | ||||||
|  |                     :key="emoji" | ||||||
|  |                     :data-emoji="emoji" | ||||||
|  |                     class="_button item" | ||||||
|  |                     @pointerenter="computeButtonTitle" | ||||||
|  |                     @click="emit('chosen', emoji, $event)" | ||||||
|  |             > | ||||||
|  |                 <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> | ||||||
|  |                 <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> | ||||||
|  |             </button> | ||||||
|  |         </div> | ||||||
|  | 
 | ||||||
|  |     </section> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <script lang="ts" setup> | <script lang="ts" setup> | ||||||
| import { ref, computed, Ref } from 'vue'; | import {ref, computed, Ref} from 'vue'; | ||||||
| import { getEmojiName } from '@/scripts/emojilist.js'; | import {getEmojiName} from '@/scripts/emojilist.js'; | ||||||
|  | import {i18n} from "../i18n.js"; | ||||||
| 
 | 
 | ||||||
| const props = defineProps<{ | const props = defineProps<{ | ||||||
| 	emojis: string[] | Ref<string[]>; |     emojis: string[] | Ref<string[]>; | ||||||
| 	initialShown?: boolean; |     initialShown?: boolean; | ||||||
|  |     category?: string[]; | ||||||
| }>(); | }>(); | ||||||
| 
 | 
 | ||||||
| const emit = defineEmits<{ | const emit = defineEmits<{ | ||||||
| 	(ev: 'chosen', v: string, event: MouseEvent): void; |     (ev: 'chosen', v: string, event: MouseEvent): void; | ||||||
| }>(); | }>(); | ||||||
| 
 | 
 | ||||||
| const emojis = computed(() => Array.isArray(props.emojis) ? props.emojis : props.emojis.value); | const toggleShown = (index) => { | ||||||
|  |     shown_fold.value[index] = !shown_fold.value[index]; | ||||||
| 
 | 
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const toggleShown_fol = () => { | ||||||
|  |     for (let i = 0; i < shown_fold.value.length; i++) { | ||||||
|  |         shown_fold.value[i] = false; | ||||||
|  |     } | ||||||
|  |     shown_fol.value = !shown_fol.value; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const emojis = computed(() => Array.isArray(props.emojis) ? props.emojis : props.emojis.value); | ||||||
|  | const shown_fold = ref(Array(props.category === undefined ? 0 : props.category.length).fill(false)); | ||||||
| const shown = ref(!!props.initialShown); | const shown = ref(!!props.initialShown); | ||||||
|  | const shown_fol = ref(!!props.initialShown); | ||||||
| 
 | 
 | ||||||
| /** @see MkEmojiPicker.vue */ | /** @see MkEmojiPicker.vue */ | ||||||
| function computeButtonTitle(ev: MouseEvent): void { | function computeButtonTitle(ev: MouseEvent): void { | ||||||
| 	const elm = ev.target as HTMLElement; |     const elm = ev.target as HTMLElement; | ||||||
| 	const emoji = elm.dataset.emoji as string; |     const emoji = elm.dataset.emoji as string; | ||||||
| 	elm.title = getEmojiName(emoji) ?? emoji; |     elm.title = getEmojiName(emoji) ?? emoji; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|  | @ -72,19 +72,26 @@ SPDX-License-Identifier: AGPL-3.0-only | ||||||
| 		</div> | 		</div> | ||||||
| 		<div v-once class="group"> | 		<div v-once class="group"> | ||||||
| 			<header class="_acrylic">{{ i18n.ts.customEmojis }}</header> | 			<header class="_acrylic">{{ i18n.ts.customEmojis }}</header> | ||||||
|  | 
 | ||||||
| 			<XSection | 			<XSection | ||||||
| 				v-for="category in customEmojiCategories" | 				v-for="category in groupedData" | ||||||
| 				:key="`custom:${category}`" | 				:key="`custom:${category}`" | ||||||
| 				:initialShown="false" | 				:initialShown="false" | ||||||
| 				:emojis="computed(() => customEmojis.filter(e => category === null ? (e.category === 'null' || !e.category) : e.category === category).filter(filterAvailable).map(e => `:${e.name}:`))" | 				:emojis="computed(() => customEmojis.filter(filterAvailable))" | ||||||
| 				@chosen="chosen" |                 :category="category" | ||||||
| 			> |                 @chosen="chosen" | ||||||
| 				{{ category || i18n.ts.other }} | 			/> | ||||||
| 			</XSection> |  | ||||||
| 		</div> | 		</div> | ||||||
| 		<div v-once class="group"> | 		<div v-once class="group"> | ||||||
| 			<header class="_acrylic">{{ i18n.ts.emoji }}</header> | 			<header class="_acrylic">{{ i18n.ts.emoji }}</header> | ||||||
| 			<XSection v-for="category in categories" :key="category" :emojis="emojiCharByCategory.get(category) ?? []" @chosen="chosen">{{ category }}</XSection> | 			<XSection | ||||||
|  | 				v-for="category in categories" | ||||||
|  | 				:key="category" | ||||||
|  | 				:emojis="emojiCharByCategory.get(category) ?? []" | ||||||
|  | 				@chosen="chosen" | ||||||
|  | 			>{{ category }} | ||||||
|  | 			</XSection> | ||||||
|  | 
 | ||||||
| 		</div> | 		</div> | ||||||
| 	</div> | 	</div> | ||||||
| 	<div class="tabs"> | 	<div class="tabs"> | ||||||
|  | @ -107,7 +114,7 @@ import { isTouchUsing } from '@/scripts/touch.js'; | ||||||
| import { deviceKind } from '@/scripts/device-kind.js'; | import { deviceKind } from '@/scripts/device-kind.js'; | ||||||
| import { i18n } from '@/i18n.js'; | import { i18n } from '@/i18n.js'; | ||||||
| import { defaultStore } from '@/store.js'; | import { defaultStore } from '@/store.js'; | ||||||
| import { customEmojiCategories, customEmojis, customEmojisMap } from '@/custom-emojis.js'; | import {customEmojiCategories, customEmojis, customEmojisMap} from '@/custom-emojis.js'; | ||||||
| import { $i } from '@/account.js'; | import { $i } from '@/account.js'; | ||||||
| 
 | 
 | ||||||
| const props = withDefaults(defineProps<{ | const props = withDefaults(defineProps<{ | ||||||
|  | @ -143,6 +150,23 @@ const q = ref<string>(''); | ||||||
| const searchResultCustom = ref<Misskey.entities.CustomEmoji[]>([]); | const searchResultCustom = ref<Misskey.entities.CustomEmoji[]>([]); | ||||||
| const searchResultUnicode = ref<UnicodeEmojiDef[]>([]); | const searchResultUnicode = ref<UnicodeEmojiDef[]>([]); | ||||||
| const tab = ref<'index' | 'custom' | 'unicode' | 'tags'>('index'); | const tab = ref<'index' | 'custom' | 'unicode' | 'tags'>('index'); | ||||||
|  | let split_categories = []; | ||||||
|  | customEmojiCategories.value.forEach(e => { | ||||||
|  |     if (e !== null){ | ||||||
|  |         split_categories.push(e.split('/')) | ||||||
|  |     } | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const groupedData = {}; | ||||||
|  | split_categories.forEach((item) => { | ||||||
|  |         if (!groupedData[item[0]]) { | ||||||
|  |             groupedData[item[0]] = []; | ||||||
|  |             groupedData[item[0]].push(item[0]); | ||||||
|  |         }else{ | ||||||
|  |             groupedData[item[0]].push(item[1]); | ||||||
|  |         } | ||||||
|  | }); | ||||||
|  | console.log(groupedData) | ||||||
| 
 | 
 | ||||||
| watch(q, () => { | watch(q, () => { | ||||||
| 	if (emojisEl.value) emojisEl.value.scrollTop = 0; | 	if (emojisEl.value) emojisEl.value.scrollTop = 0; | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue