This commit is contained in:
syuilo 2021-02-27 19:53:20 +09:00
parent 5d484ee873
commit 5556ebb176
3 changed files with 143 additions and 98 deletions

View file

@ -0,0 +1,52 @@
<template>
<section>
<header class="_acrylic" @click="shown = !shown">
<Fa :icon="shown ? faChevronDown : faChevronUp" :key="shown" fixed-width class="toggle"/> <slot></slot> ({{ emojis.length }})
</header>
<div v-if="shown">
<button v-for="emoji in emojis"
class="_button"
@click="chosen(emoji, $event)"
:key="emoji"
>
<MkEmoji :emoji="emoji" :normal="true"/>
</button>
</div>
</section>
</template>
<script lang="ts">
import { defineComponent, markRaw } from 'vue';
import { faChevronUp, faChevronDown } from '@fortawesome/free-solid-svg-icons';
import { getStaticImageUrl } from '@/scripts/get-static-image-url';
export default defineComponent({
props: {
emojis: {
required: true,
},
initialShown: {
required: false
}
},
emits: ['chosen'],
data() {
return {
getStaticImageUrl,
shown: this.initialShown,
faChevronUp, faChevronDown,
};
},
methods: {
chosen(emoji: any, ev) {
this.$parent.chosen(emoji, ev);
},
}
});
</script>
<style lang="scss" scoped>
</style>

View file

@ -28,7 +28,7 @@
</div> </div>
</section> </section>
<div class="index"> <div class="index" v-if="tab === 'index'">
<section v-if="showPinned"> <section v-if="showPinned">
<div> <div>
<button v-for="emoji in pinned" <button v-for="emoji in pinned"
@ -53,37 +53,31 @@
</button> </button>
</div> </div>
</section> </section>
<div class="arrow"><Fa :icon="faChevronDown"/></div>
</div> </div>
<div v-appear="() => showingCustomEmojis = true">
<section v-for="category in customEmojiCategories" :key="'custom:' + category" class="custom"> <header class="_acrylic">{{ $ts.customEmojis }}</header>
<header class="_acrylic" v-appear="() => visibleCategories[category] = true">{{ category || $ts.other }}</header> <template v-if="showingCustomEmojis">
<div v-if="visibleCategories[category]"> <XSection v-for="category in customEmojiCategories" :key="'custom:' + category" :initial-shown="false" :emojis="customEmojis.filter(e => e.category === category).map(e => ':' + e.name + ':')">{{ category || $ts.other }}</XSection>
<button v-for="emoji in customEmojis.filter(e => e.category === category)" </template>
class="_button" </div>
:title="emoji.name" <div v-appear="() => showingEmojis = true">
@click="chosen(emoji, $event)" <header class="_acrylic">{{ $ts.emoji }}</header>
:key="emoji.name" <template v-if="showingEmojis">
> <XSection v-for="category in categories" :emojis="emojilist.filter(e => e.category === category).map(e => e.char)">{{ category }}</XSection>
<img :src="$store.state.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url"/> </template>
</button> </div>
</div> <div v-appear="() => showingTags = true">
</section> <header class="_acrylic">{{ $ts.tags }}</header>
<template v-if="showingTags">
<section v-for="category in categories" :key="category.name" class="unicode"> <XSection v-for="tag in emojiTags" :emojis="customEmojis.filter(e => e.aliases.includes(tag)).map(e => ':' + e.name + ':')">{{ tag }}</XSection>
<header class="_acrylic" v-appear="() => category.isActive = true"><Fa :icon="category.icon" fixed-width/> {{ category.name }}</header> </template>
<div v-if="category.isActive"> </div>
<button v-for="emoji in emojilist.filter(e => e.category === category.name)" </div>
class="_button" <div class="tabs">
:title="emoji.name" <button class="_button tab" :class="{ active: tab === 'index' }" @click="tab = 'index'"><Fa :icon="faAsterisk" fixed-width/></button>
@click="chosen(emoji, $event)" <button class="_button tab" :class="{ active: tab === 'custom' }" @click="tab = 'custom'"><Fa :icon="faLaugh" fixed-width/></button>
:key="emoji.name" <button class="_button tab" :class="{ active: tab === 'unicode' }" @click="tab = 'unicode'"><Fa :icon="faLeaf" fixed-width/></button>
> <button class="_button tab" :class="{ active: tab === 'tags' }" @click="tab = 'tags'"><Fa :icon="faHashtag" fixed-width/></button>
<MkEmoji :emoji="emoji.char"/>
</button>
</div>
</section>
</div> </div>
</div> </div>
</template> </template>
@ -92,15 +86,20 @@
import { defineComponent, markRaw } from 'vue'; import { defineComponent, markRaw } from 'vue';
import { emojilist } from '../../misc/emojilist'; import { emojilist } from '../../misc/emojilist';
import { getStaticImageUrl } from '@/scripts/get-static-image-url'; import { getStaticImageUrl } from '@/scripts/get-static-image-url';
import { faAsterisk, faLeaf, faUtensils, faFutbol, faCity, faDice, faGlobe, faClock, faUser, faChevronDown } from '@fortawesome/free-solid-svg-icons'; import { faAsterisk, faLeaf, faUtensils, faFutbol, faCity, faDice, faGlobe, faClock, faUser, faChevronDown, faShapes, faBicycle, faHashtag } from '@fortawesome/free-solid-svg-icons';
import { faHeart, faFlag, faLaugh } from '@fortawesome/free-regular-svg-icons'; import { faHeart, faFlag, faLaugh } from '@fortawesome/free-regular-svg-icons';
import Particle from '@/components/particle.vue'; import Particle from '@/components/particle.vue';
import * as os from '@/os'; import * as os from '@/os';
import { isDeviceTouch } from '@/scripts/is-device-touch'; import { isDeviceTouch } from '@/scripts/is-device-touch';
import { isMobile } from '@/scripts/is-mobile'; import { isMobile } from '@/scripts/is-mobile';
import { emojiCategories } from '@/instance'; import { emojiCategories, emojiTags } from '@/instance';
import XSection from './emoji-picker.section.vue';
export default defineComponent({ export default defineComponent({
components: {
XSection
},
props: { props: {
showPinned: { showPinned: {
required: false, required: false,
@ -122,50 +121,17 @@ export default defineComponent({
height: this.asReactionPicker ? this.$store.state.reactionPickerHeight : 2, height: this.asReactionPicker ? this.$store.state.reactionPickerHeight : 2,
big: this.asReactionPicker ? isDeviceTouch : false, big: this.asReactionPicker ? isDeviceTouch : false,
customEmojiCategories: emojiCategories, customEmojiCategories: emojiCategories,
emojiTags,
customEmojis: this.$instance.emojis, customEmojis: this.$instance.emojis,
visibleCategories: {},
q: null, q: null,
searchResultCustom: [], searchResultCustom: [],
searchResultUnicode: [], searchResultUnicode: [],
faGlobe, faClock, faChevronDown, tab: 'index',
categories: [{ showingCustomEmojis: false,
name: 'face', showingEmojis: false,
icon: faLaugh, showingTags: false,
isActive: false categories: ['face', 'people', 'animals_and_nature', 'food_and_drink', 'activity', 'travel_and_places', 'objects', 'symbols', 'flags'],
}, { faGlobe, faClock, faChevronDown, faAsterisk, faLaugh, faUtensils, faLeaf, faShapes, faBicycle, faHashtag,
name: 'people',
icon: faUser,
isActive: false
}, {
name: 'animals_and_nature',
icon: faLeaf,
isActive: false
}, {
name: 'food_and_drink',
icon: faUtensils,
isActive: false
}, {
name: 'activity',
icon: faFutbol,
isActive: false
}, {
name: 'travel_and_places',
icon: faCity,
isActive: false
}, {
name: 'objects',
icon: faDice,
isActive: false
}, {
name: 'symbols',
icon: faHeart,
isActive: false
}, {
name: 'flags',
icon: faFlag,
isActive: false
}],
faAsterisk
}; };
}, },
@ -342,7 +308,7 @@ export default defineComponent({
let recents = this.$store.state.recentlyUsedEmojis; let recents = this.$store.state.recentlyUsedEmojis;
recents = recents.filter((e: any) => e !== key); recents = recents.filter((e: any) => e !== key);
recents.unshift(key); recents.unshift(key);
this.$store.set('recentlyUsedEmojis', recents.splice(0, 16)); this.$store.set('recentlyUsedEmojis', recents.splice(0, 32));
} }
}, },
@ -434,6 +400,22 @@ export default defineComponent({
} }
} }
> .tabs {
display: flex;
display: none;
> .tab {
flex: 1;
height: 38px;
border-top: solid 1px var(--divider);
&.active {
border-top: solid 1px var(--accent);
color: var(--accent);
}
}
}
> .emojis { > .emojis {
height: var(--height); height: var(--height);
overflow-y: auto; overflow-y: auto;
@ -445,34 +427,43 @@ export default defineComponent({
display: none; display: none;
} }
> .index { > div {
min-height: var(--height); &:not(.index) {
position: relative; padding: 4px 0 8px 0;
border-bottom: solid 1px var(--divider); border-top: solid 1px var(--divider);
}
> .arrow {
position: absolute; > header {
bottom: 0; /*position: sticky;
left: 0; top: 0;
width: 100%; left: 0;*/
padding: 16px 0; height: 32px;
text-align: center; line-height: 32px;
opacity: 0.5; z-index: 2;
pointer-events: none; padding: 0 8px;
font-size: 12px;
} }
} }
section { ::v-deep(section) {
> header { > header {
position: sticky; position: sticky;
top: 0; top: 0;
left: 0; left: 0;
height: 32px;
line-height: 32px;
z-index: 1; z-index: 1;
padding: 8px; padding: 0 8px;
font-size: 12px; font-size: 12px;
cursor: pointer;
&:hover {
color: var(--accent);
}
} }
> div { > div {
position: relative;
padding: $pad; padding: $pad;
> button { > button {
@ -512,14 +503,6 @@ export default defineComponent({
display: none; display: none;
} }
} }
&.unicode {
min-height: 384px;
}
&.custom {
min-height: 64px;
}
} }
} }
} }

View file

@ -37,6 +37,16 @@ export const emojiCategories = computed(() => {
return Array.from(categories); return Array.from(categories);
}); });
export const emojiTags = computed(() => {
const tags = new Set();
for (const emoji of instance.emojis) {
for (const tag of emoji.aliases) {
tags.add(tag);
}
}
return Array.from(tags);
});
// このファイルに書きたくないけどここに書かないと何故かVeturが認識しない // このファイルに書きたくないけどここに書かないと何故かVeturが認識しない
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
interface ComponentCustomProperties { interface ComponentCustomProperties {