feat(emoji-picker) add lazy loading to improve initial load performance
This commit is contained in:
		
							parent
							
								
									0b1b98f93e
								
							
						
					
					
						commit
						eb54661949
					
				| @ -2,6 +2,7 @@ | ||||
| import emojiUnicodeData from 'unicode-emoji-json'; | ||||
| import emojiKeywords from 'emojilib'; | ||||
| import _ from 'lodash'; | ||||
| import { IconChevronRight, IconSearch } from '@tabler/icons-vue'; | ||||
| import type { EmojiInfo } from './emoji.types'; | ||||
| import { useFuzzySearch } from '@/composable/fuzzySearch'; | ||||
| import useDebouncedRef from '@/composable/debouncedref'; | ||||
| @ -36,10 +37,36 @@ const { searchResult } = useFuzzySearch({ | ||||
|     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> | ||||
| 
 | ||||
| <template> | ||||
|   <div mx-auto max-w-2400px important:flex-1> | ||||
|     <!-- Emoji Search Bar --> | ||||
|     <div flex items-center gap-3> | ||||
|       <c-input-text | ||||
|         v-model:value="searchQuery" | ||||
| @ -47,11 +74,12 @@ const { searchResult } = useFuzzySearch({ | ||||
|         mx-auto max-w-600px | ||||
|       > | ||||
|         <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> | ||||
|       </c-input-text> | ||||
|     </div> | ||||
| 
 | ||||
|     <!-- Emoji Search Results --> | ||||
|     <div v-if="searchQuery.trim().length > 0"> | ||||
|       <div | ||||
|         v-if="searchResult.length === 0" | ||||
| @ -71,16 +99,34 @@ const { searchResult } = useFuzzySearch({ | ||||
|       </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <div v-for="{ group, emojiInfos } in emojisGroups" :key="group"> | ||||
|       <!-- Collapsible Group Header with Chevron Icons --> | ||||
|       <div | ||||
|       v-for="{ group, emojiInfos } in emojisGroups" | ||||
|       v-else | ||||
|       :key="group" | ||||
|         mt-4 text-20px font-bold | ||||
|         style="cursor: pointer;" | ||||
|         @click="toggleGroup(group)" | ||||
|       > | ||||
|       <div mt-4 text-20px font-bold> | ||||
|         {{ 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> | ||||
| 
 | ||||
|       <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> | ||||
| </template> | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| import { MoodSmile } from '@vicons/tabler'; | ||||
| import { IconMoodSmile } from '@tabler/icons-vue'; | ||||
| import { defineTool } from '../tool'; | ||||
| import { translate } from '@/plugins/i18n.plugin'; | ||||
| 
 | ||||
| @ -8,6 +8,6 @@ export const tool = defineTool({ | ||||
|   description: translate('tools.emoji-picker.description'), | ||||
|   keywords: ['emoji', 'picker', 'unicode', 'copy', 'paste'], | ||||
|   component: () => import('./emoji-picker.vue'), | ||||
|   icon: MoodSmile, | ||||
|   icon: IconMoodSmile, | ||||
|   createdAt: new Date('2023-08-07'), | ||||
| }); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user