2
2
import emojiUnicodeData from ' unicode-emoji-json' ;
3
3
import emojiKeywords from ' emojilib' ;
4
4
import _ from ' lodash' ;
5
+ import { IconChevronRight , IconSearch } from ' @tabler/icons-vue' ;
5
6
import type { EmojiInfo } from ' ./emoji.types' ;
6
7
import { useFuzzySearch } from ' @/composable/fuzzySearch' ;
7
8
import useDebouncedRef from ' @/composable/debouncedref' ;
@@ -36,22 +37,49 @@ const { searchResult } = useFuzzySearch({
36
37
isCaseSensitive: false ,
37
38
},
38
39
});
40
+
41
+ const emojisPerPage = 30 ; // Number of emojis to load per group initially
42
+
43
+ // Tracks how many emojis are shown per group and the collapsed state of each group
44
+ const groupLoadLimits = ref (
45
+ emojisGroups .reduce ((acc , group ) => {
46
+ acc [group .group ] = { limit: emojisPerPage , collapsed: false };
47
+ return acc ;
48
+ }, {} as Record <string , { limit: number ; collapsed: boolean }>),
49
+ );
50
+
51
+ // Toggles the visibility of the emoji group
52
+ function toggleGroup(group : string ) {
53
+ groupLoadLimits .value [group ].collapsed = ! groupLoadLimits .value [group ].collapsed ;
54
+ };
55
+
56
+ // Loads more emojis incrementally
57
+ function loadMoreEmojis(group : string ) {
58
+ groupLoadLimits .value [group ].limit += emojisPerPage ;
59
+ };
60
+
61
+ // Loads all emojis in the group at once
62
+ function loadAllEmojis(group : string ) {
63
+ groupLoadLimits .value [group ].limit = emojisGroups .find (g => g .group === group )?.emojiInfos .length || 0 ;
64
+ };
39
65
</script >
40
66
41
67
<template >
42
68
<div mx-auto max-w-2400px important:flex-1 >
69
+ <!-- Emoji Search Bar -->
43
70
<div flex items-center gap-3 >
44
71
<c-input-text
45
72
v-model:value =" searchQuery"
46
73
placeholder =" Search emojis (e.g. 'smile')..."
47
74
mx-auto max-w-600px
48
75
>
49
76
<template #prefix >
50
- <icon-mdi-search mr-6px color-black op-70 dark:color-white />
77
+ <n-icon :component = " IconSearch " mr-6px color-black op-70 dark:color-white />
51
78
</template >
52
79
</c-input-text >
53
80
</div >
54
81
82
+ <!-- Emoji Search Results -->
55
83
<div v-if =" searchQuery.trim().length > 0" >
56
84
<div
57
85
v-if =" searchResult.length === 0"
@@ -71,16 +99,34 @@ const { searchResult } = useFuzzySearch({
71
99
</div >
72
100
</div >
73
101
74
- <div
75
- v-for =" { group, emojiInfos } in emojisGroups"
76
- v-else
77
- :key =" group"
78
- >
79
- <div mt-4 text-20px font-bold >
80
- {{ group }}
102
+ <div v-for =" { group, emojiInfos } in emojisGroups" :key =" group" >
103
+ <!-- Collapsible Group Header with Chevron Icons -->
104
+ <div
105
+ mt-4 text-20px font-bold
106
+ style =" cursor : pointer ;"
107
+ @click =" toggleGroup(group)"
108
+ >
109
+ <n-icon
110
+ :component =" IconChevronRight"
111
+ :class =" { 'rotate-0': groupLoadLimits[group].collapsed, 'rotate-90': !groupLoadLimits[group].collapsed }"
112
+ mr-1 text-16px lh-1 op-50 transition
113
+ />
114
+ <span >{{ group }}</span >
81
115
</div >
82
116
83
- <emoji-grid :emoji-infos =" emojiInfos" />
117
+ <!-- Emoji Grid, conditionally displayed based on collapse state -->
118
+ <div v-show =" !groupLoadLimits[group].collapsed" >
119
+ <emoji-grid :emoji-infos =" emojiInfos.slice(0, groupLoadLimits[group].limit)" />
120
+
121
+ <div v-if =" groupLoadLimits[group].limit < emojiInfos.length" style =" display : flex ; gap : 8px ; margin-top : 8px ; justify-content : center ;" >
122
+ <c-button @click =" loadMoreEmojis(group)" >
123
+ Load More
124
+ </c-button >
125
+ <c-button @click =" loadAllEmojis(group)" >
126
+ Load All
127
+ </c-button >
128
+ </div >
129
+ </div >
84
130
</div >
85
131
</div >
86
132
</template >
0 commit comments