mirror of
https://github.com/CorentinTh/it-tools.git
synced 2025-04-24 00:36:14 -04:00
feat(emoji-picker) add lazy loading to improve initial load performance
This commit is contained in:
parent
0b1b98f93e
commit
eb54661949
2 changed files with 57 additions and 11 deletions
|
@ -2,6 +2,7 @@
|
||||||
import emojiUnicodeData from 'unicode-emoji-json';
|
import emojiUnicodeData from 'unicode-emoji-json';
|
||||||
import emojiKeywords from 'emojilib';
|
import emojiKeywords from 'emojilib';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import { IconChevronRight, IconSearch } from '@tabler/icons-vue';
|
||||||
import type { EmojiInfo } from './emoji.types';
|
import type { EmojiInfo } from './emoji.types';
|
||||||
import { useFuzzySearch } from '@/composable/fuzzySearch';
|
import { useFuzzySearch } from '@/composable/fuzzySearch';
|
||||||
import useDebouncedRef from '@/composable/debouncedref';
|
import useDebouncedRef from '@/composable/debouncedref';
|
||||||
|
@ -36,10 +37,36 @@ const { searchResult } = useFuzzySearch({
|
||||||
isCaseSensitive: false,
|
isCaseSensitive: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const emojisPerPage = 30; // Number of emojis to load per group initially
|
||||||
|
|
||||||
|
// Tracks how many emojis are shown per group and the collapsed state of each group
|
||||||
|
const groupLoadLimits = ref(
|
||||||
|
emojisGroups.reduce((acc, group) => {
|
||||||
|
acc[group.group] = { limit: emojisPerPage, collapsed: false };
|
||||||
|
return acc;
|
||||||
|
}, {} as Record<string, { limit: number; collapsed: boolean }>),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Toggles the visibility of the emoji group
|
||||||
|
function toggleGroup(group: string) {
|
||||||
|
groupLoadLimits.value[group].collapsed = !groupLoadLimits.value[group].collapsed;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Loads more emojis incrementally
|
||||||
|
function loadMoreEmojis(group: string) {
|
||||||
|
groupLoadLimits.value[group].limit += emojisPerPage;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Loads all emojis in the group at once
|
||||||
|
function loadAllEmojis(group: string) {
|
||||||
|
groupLoadLimits.value[group].limit = emojisGroups.find(g => g.group === group)?.emojiInfos.length || 0;
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div mx-auto max-w-2400px important:flex-1>
|
<div mx-auto max-w-2400px important:flex-1>
|
||||||
|
<!-- Emoji Search Bar -->
|
||||||
<div flex items-center gap-3>
|
<div flex items-center gap-3>
|
||||||
<c-input-text
|
<c-input-text
|
||||||
v-model:value="searchQuery"
|
v-model:value="searchQuery"
|
||||||
|
@ -47,11 +74,12 @@ const { searchResult } = useFuzzySearch({
|
||||||
mx-auto max-w-600px
|
mx-auto max-w-600px
|
||||||
>
|
>
|
||||||
<template #prefix>
|
<template #prefix>
|
||||||
<icon-mdi-search mr-6px color-black op-70 dark:color-white />
|
<n-icon :component="IconSearch" mr-6px color-black op-70 dark:color-white />
|
||||||
</template>
|
</template>
|
||||||
</c-input-text>
|
</c-input-text>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Emoji Search Results -->
|
||||||
<div v-if="searchQuery.trim().length > 0">
|
<div v-if="searchQuery.trim().length > 0">
|
||||||
<div
|
<div
|
||||||
v-if="searchResult.length === 0"
|
v-if="searchResult.length === 0"
|
||||||
|
@ -71,16 +99,34 @@ const { searchResult } = useFuzzySearch({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div v-for="{ group, emojiInfos } in emojisGroups" :key="group">
|
||||||
v-for="{ group, emojiInfos } in emojisGroups"
|
<!-- Collapsible Group Header with Chevron Icons -->
|
||||||
v-else
|
<div
|
||||||
:key="group"
|
mt-4 text-20px font-bold
|
||||||
>
|
style="cursor: pointer;"
|
||||||
<div mt-4 text-20px font-bold>
|
@click="toggleGroup(group)"
|
||||||
{{ group }}
|
>
|
||||||
|
<n-icon
|
||||||
|
:component="IconChevronRight"
|
||||||
|
:class="{ 'rotate-0': groupLoadLimits[group].collapsed, 'rotate-90': !groupLoadLimits[group].collapsed }"
|
||||||
|
mr-1 text-16px lh-1 op-50 transition
|
||||||
|
/>
|
||||||
|
<span>{{ group }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<emoji-grid :emoji-infos="emojiInfos" />
|
<!-- Emoji Grid, conditionally displayed based on collapse state -->
|
||||||
|
<div v-show="!groupLoadLimits[group].collapsed">
|
||||||
|
<emoji-grid :emoji-infos="emojiInfos.slice(0, groupLoadLimits[group].limit)" />
|
||||||
|
|
||||||
|
<div v-if="groupLoadLimits[group].limit < emojiInfos.length" style="display: flex; gap: 8px; margin-top: 8px; justify-content: center;">
|
||||||
|
<c-button @click="loadMoreEmojis(group)">
|
||||||
|
Load More
|
||||||
|
</c-button>
|
||||||
|
<c-button @click="loadAllEmojis(group)">
|
||||||
|
Load All
|
||||||
|
</c-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { MoodSmile } from '@vicons/tabler';
|
import { IconMoodSmile } from '@tabler/icons-vue';
|
||||||
import { defineTool } from '../tool';
|
import { defineTool } from '../tool';
|
||||||
import { translate } from '@/plugins/i18n.plugin';
|
import { translate } from '@/plugins/i18n.plugin';
|
||||||
|
|
||||||
|
@ -8,6 +8,6 @@ export const tool = defineTool({
|
||||||
description: translate('tools.emoji-picker.description'),
|
description: translate('tools.emoji-picker.description'),
|
||||||
keywords: ['emoji', 'picker', 'unicode', 'copy', 'paste'],
|
keywords: ['emoji', 'picker', 'unicode', 'copy', 'paste'],
|
||||||
component: () => import('./emoji-picker.vue'),
|
component: () => import('./emoji-picker.vue'),
|
||||||
icon: MoodSmile,
|
icon: IconMoodSmile,
|
||||||
createdAt: new Date('2023-08-07'),
|
createdAt: new Date('2023-08-07'),
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue