-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(frontend): 絵文字ピッカーのカテゴリを多階層フォルダで分類できるように #12132
Changes from 47 commits
7c9330a
40de144
1ba49b6
02c8fd9
647ce17
5bd68aa
b23a9b1
ae517a9
1e67e9c
59768bd
a52bbc7
f54a954
a096f62
8b1362a
75b28d6
d429f81
272e0c8
c5d2dba
94690c8
9ad5732
de6348e
407a965
32c7411
7093662
e64a81a
4e24aff
a8d45d4
f32915b
e57b536
7adc8fc
0e6cd57
f964ef1
7e7138c
3043b52
e1b45bd
808602c
e88c0ed
01af8c2
bf3ca78
0b59236
0637591
9b14644
059c11f
6b3171f
563bd78
0ae2d13
2e05560
d0f9626
d09481d
3f42764
45e39bd
84ed25a
b47b620
b19602d
74cefab
47309b6
ac8be5c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -5,9 +5,10 @@ SPDX-License-Identifier: AGPL-3.0-only | |||||
|
||||||
<template> | ||||||
<!-- このコンポーネントの要素のclassは親から利用されるのでむやみに弄らないこと --> | ||||||
<section> | ||||||
<!-- フォルダの中にはカスタム絵文字だけ(Unicode絵文字もこっち) --> | ||||||
<section v-if="!categoryFolderFlag"> | ||||||
<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 }}) | ||||||
<i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> <slot></slot> (<i class="ti ti-icons ti-fw"></i>:{{emojis.length}}) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
</header> | ||||||
<div v-if="shown" class="body"> | ||||||
<button | ||||||
|
@@ -23,15 +24,52 @@ SPDX-License-Identifier: AGPL-3.0-only | |||||
</button> | ||||||
</div> | ||||||
</section> | ||||||
<!-- フォルダの中にはカスタム絵文字やフォルダがある --> | ||||||
<section v-else> | ||||||
<header class="_acrylic" @click="shown = !shown"> | ||||||
<i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> <slot></slot> (<i class="ti ti-folder ti-fw"></i>:{{customEmojiTree.length}} <i class="ti ti-icons ti-fw"></i>:{{emojis.length}}) | ||||||
</header> | ||||||
<div v-if="shown" 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> | ||||||
<div v-if="shown" style="padding-left: 9px;"> | ||||||
<MkEmojiPickerSection | ||||||
v-for="child in customEmojiTree" | ||||||
:key="`custom:${child.value}`" | ||||||
:initialShown="initialShown" | ||||||
:emojis="computed(() => customEmojis.filter(e => e.category === child.category).map(e => `:${e.name}:`))" | ||||||
:categoryFolderFlag="child.children.length!==0" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
:customEmojiTree="child.children" | ||||||
@chosen="nestedChosen" | ||||||
> | ||||||
{{ child.value }} | ||||||
</MkEmojiPickerSection> | ||||||
</div> | ||||||
</section> | ||||||
</template> | ||||||
|
||||||
<script lang="ts" setup> | ||||||
import { ref, computed, Ref } from 'vue'; | ||||||
import { getEmojiName } from '@/scripts/emojilist.js'; | ||||||
import { CustomEmojiFolderTree, getEmojiName } from '@/scripts/emojilist.js'; | ||||||
import { i18n } from "../i18n.js"; | ||||||
import { customEmojis } from "@/custom-emojis.js"; | ||||||
import MkEmojiPickerSection from "@/components/MkEmojiPicker.section.vue"; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. シングルクォートで統一お願いします |
||||||
|
||||||
const props = defineProps<{ | ||||||
emojis: string[] | Ref<string[]>; | ||||||
initialShown?: boolean; | ||||||
categoryFolderFlag?: boolean; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 〇〇Flagという命名は得てして意味が分かりづらいから、hasChildSectionとかどうかしら There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 承知です |
||||||
customEmojiTree?: CustomEmojiFolderTree[]; | ||||||
}>(); | ||||||
|
||||||
const emit = defineEmits<{ | ||||||
|
@@ -49,4 +87,7 @@ function computeButtonTitle(ev: MouseEvent): void { | |||||
elm.title = getEmojiName(emoji) ?? emoji; | ||||||
} | ||||||
|
||||||
function nestedChosen(emoji: any, ev?: MouseEvent) { | ||||||
emit('chosen', emoji, ev); | ||||||
} | ||||||
</script> |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -73,18 +73,20 @@ | |||||
<div v-once class="group"> | ||||||
<header class="_acrylic">{{ i18n.ts.customEmojis }}</header> | ||||||
<XSection | ||||||
v-for="category in customEmojiCategories" | ||||||
:key="`custom:${category}`" | ||||||
v-for="child in customEmojiFolderRoot.children" | ||||||
:key="`custom:${child.value}`" | ||||||
: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(e => child.value === i18n.ts.other ? (e.category === 'null' || !e.category) : e.category === child.value).filter(filterAvailable).map(e => `:${e.name}:`))" | ||||||
:categoryFolderFlag="child.children.length!==0" | ||||||
:customEmojiTree="child.children" | ||||||
@chosen="chosen" | ||||||
> | ||||||
{{ category || i18n.ts.other }} | ||||||
{{ child.value }} | ||||||
</XSection> | ||||||
</div> | ||||||
<div v-once class="group"> | ||||||
<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) ?? []" :categoryFolderFlag="false" @chosen="chosen">{{ category }}</XSection> | ||||||
</div> | ||||||
</div> | ||||||
<div class="tabs"> | ||||||
|
@@ -100,7 +102,14 @@ | |||||
import { ref, shallowRef, computed, watch, onMounted } from 'vue'; | ||||||
import * as Misskey from 'misskey-js'; | ||||||
import XSection from '@/components/MkEmojiPicker.section.vue'; | ||||||
import { emojilist, emojiCharByCategory, UnicodeEmojiDef, unicodeEmojiCategories as categories, getEmojiName } from '@/scripts/emojilist.js'; | ||||||
import { | ||||||
emojilist, | ||||||
emojiCharByCategory, | ||||||
UnicodeEmojiDef, | ||||||
unicodeEmojiCategories as categories, | ||||||
getEmojiName, | ||||||
CustomEmojiFolderTree | ||||||
} from '@/scripts/emojilist.js'; | ||||||
import MkRippleEffect from '@/components/MkRippleEffect.vue'; | ||||||
import * as os from '@/os.js'; | ||||||
import { isTouchUsing } from '@/scripts/touch.js'; | ||||||
|
@@ -144,6 +153,44 @@ | |||||
const searchResultUnicode = ref<UnicodeEmojiDef[]>([]); | ||||||
const tab = ref<'index' | 'custom' | 'unicode' | 'tags'>('index'); | ||||||
|
||||||
const customEmojiFolderRoot: CustomEmojiFolderTree = { value: "", category: "", children: [] }; | ||||||
|
||||||
function parseAndMergeCategories(input: string, root: CustomEmojiFolderTree): CustomEmojiFolderTree { | ||||||
const parts = input.split('/'); | ||||||
let category = ""; | ||||||
let currentNode: CustomEmojiFolderTree = root; | ||||||
|
||||||
for (let part of parts) { | ||||||
if (part) { | ||||||
category += `/${part}`; | ||||||
} else { | ||||||
part = i18n.ts.other | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Lint落ちてるのでここ修正お願いします🙏 |
||||||
category += `/`; | ||||||
} | ||||||
|
||||||
category = category.replace(/^\//, ''); | ||||||
let existingNode = currentNode.children.find((node) => node.value === part); | ||||||
|
||||||
if (!existingNode) { | ||||||
const newNode: CustomEmojiFolderTree = { value: part, category, children: [] }; | ||||||
currentNode.children.push(newNode); | ||||||
existingNode = newNode; | ||||||
} | ||||||
|
||||||
currentNode = existingNode; | ||||||
} | ||||||
|
||||||
return currentNode; | ||||||
} | ||||||
|
||||||
customEmojiCategories.value.forEach(ec => { | ||||||
if (ec !== null) { | ||||||
parseAndMergeCategories(ec, customEmojiFolderRoot); | ||||||
} | ||||||
}); | ||||||
|
||||||
parseAndMergeCategories(i18n.ts.other, customEmojiFolderRoot); | ||||||
|
||||||
watch(q, () => { | ||||||
if (emojisEl.value) emojisEl.value.scrollTop = 0; | ||||||
|
||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ti-icons は必要かしら?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
数字だけだとフォルダの数なのか、絵文字の数なのか初見だとわかんなくなりそうだなってことで追加してます。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
あ、ti-icons というクラスは必要?という意味
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
使いたいアイコンが
ti-icons
https://tabler-icons.io/i/icons なのでどっちかというとti-fw
が不要かもThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ですです、アイコンとして使いたいのが
ti-icons
なのでti-fw
の方が不要なのではというのはそうかもです。(他で
ti-icons
を利用している場所の記述ほぼそのままパクってるだけなのでもしかしたらこの場合では不要なクラスが混ざってしまってるかも)