feat(new tool): emoji picker (#551)
This commit is contained in:
		
							parent
							
								
									dfa1ba8554
								
							
						
					
					
						commit
						93f7cf0e98
					
				
							
								
								
									
										3
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -53,6 +53,9 @@ declare module '@vue/runtime-core' { | |||||||
|     DockerRunToDockerComposeConverter: typeof import('./src/tools/docker-run-to-docker-compose-converter/docker-run-to-docker-compose-converter.vue')['default'] |     DockerRunToDockerComposeConverter: typeof import('./src/tools/docker-run-to-docker-compose-converter/docker-run-to-docker-compose-converter.vue')['default'] | ||||||
|     DynamicValues: typeof import('./src/tools/benchmark-builder/dynamic-values.vue')['default'] |     DynamicValues: typeof import('./src/tools/benchmark-builder/dynamic-values.vue')['default'] | ||||||
|     Editor: typeof import('./src/tools/html-wysiwyg-editor/editor/editor.vue')['default'] |     Editor: typeof import('./src/tools/html-wysiwyg-editor/editor/editor.vue')['default'] | ||||||
|  |     EmojiCard: typeof import('./src/tools/emoji-picker/emoji-card.vue')['default'] | ||||||
|  |     EmojiGrid: typeof import('./src/tools/emoji-picker/emoji-grid.vue')['default'] | ||||||
|  |     EmojiPicker: typeof import('./src/tools/emoji-picker/emoji-picker.vue')['default'] | ||||||
|     Encryption: typeof import('./src/tools/encryption/encryption.vue')['default'] |     Encryption: typeof import('./src/tools/encryption/encryption.vue')['default'] | ||||||
|     EtaCalculator: typeof import('./src/tools/eta-calculator/eta-calculator.vue')['default'] |     EtaCalculator: typeof import('./src/tools/eta-calculator/eta-calculator.vue')['default'] | ||||||
|     FavoriteButton: typeof import('./src/components/FavoriteButton.vue')['default'] |     FavoriteButton: typeof import('./src/components/FavoriteButton.vue')['default'] | ||||||
|  | |||||||
| @ -53,6 +53,7 @@ | |||||||
|     "cronstrue": "^2.26.0", |     "cronstrue": "^2.26.0", | ||||||
|     "crypto-js": "^4.1.1", |     "crypto-js": "^4.1.1", | ||||||
|     "date-fns": "^2.29.3", |     "date-fns": "^2.29.3", | ||||||
|  |     "emojilib": "^3.0.10", | ||||||
|     "figue": "^1.2.0", |     "figue": "^1.2.0", | ||||||
|     "fuse.js": "^6.6.2", |     "fuse.js": "^6.6.2", | ||||||
|     "highlight.js": "^11.7.0", |     "highlight.js": "^11.7.0", | ||||||
| @ -74,6 +75,7 @@ | |||||||
|     "sql-formatter": "^8.2.0", |     "sql-formatter": "^8.2.0", | ||||||
|     "ts-pattern": "^4.2.2", |     "ts-pattern": "^4.2.2", | ||||||
|     "ua-parser-js": "^1.0.35", |     "ua-parser-js": "^1.0.35", | ||||||
|  |     "unicode-emoji-json": "^0.4.0", | ||||||
|     "unplugin-auto-import": "^0.16.4", |     "unplugin-auto-import": "^0.16.4", | ||||||
|     "uuid": "^8.3.2", |     "uuid": "^8.3.2", | ||||||
|     "vue": "^3.3.4", |     "vue": "^3.3.4", | ||||||
|  | |||||||
							
								
								
									
										21
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										21
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							| @ -65,6 +65,9 @@ dependencies: | |||||||
|   date-fns: |   date-fns: | ||||||
|     specifier: ^2.29.3 |     specifier: ^2.29.3 | ||||||
|     version: 2.29.3 |     version: 2.29.3 | ||||||
|  |   emojilib: | ||||||
|  |     specifier: ^3.0.10 | ||||||
|  |     version: 3.0.10 | ||||||
|   figue: |   figue: | ||||||
|     specifier: ^1.2.0 |     specifier: ^1.2.0 | ||||||
|     version: 1.2.0 |     version: 1.2.0 | ||||||
| @ -128,6 +131,9 @@ dependencies: | |||||||
|   ua-parser-js: |   ua-parser-js: | ||||||
|     specifier: ^1.0.35 |     specifier: ^1.0.35 | ||||||
|     version: 1.0.35 |     version: 1.0.35 | ||||||
|  |   unicode-emoji-json: | ||||||
|  |     specifier: ^0.4.0 | ||||||
|  |     version: 0.4.0 | ||||||
|   unplugin-auto-import: |   unplugin-auto-import: | ||||||
|     specifier: ^0.16.4 |     specifier: ^0.16.4 | ||||||
|     version: 0.16.4(@vueuse/core@10.1.2)(rollup@2.79.1) |     version: 0.16.4(@vueuse/core@10.1.2)(rollup@2.79.1) | ||||||
| @ -4932,6 +4938,10 @@ packages: | |||||||
|   /emoji-regex@8.0.0: |   /emoji-regex@8.0.0: | ||||||
|     resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} |     resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} | ||||||
| 
 | 
 | ||||||
|  |   /emojilib@3.0.10: | ||||||
|  |     resolution: {integrity: sha512-VQtCRroFykPTJaoEBEGFg5tI+rEluabjuaVDDbSftDtiRJ5GuqRG/LGV1mmDzkJP4bh5rzuEBOafMN68/YXQcQ==} | ||||||
|  |     dev: false | ||||||
|  | 
 | ||||||
|   /encode-utf8@1.0.3: |   /encode-utf8@1.0.3: | ||||||
|     resolution: {integrity: sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==} |     resolution: {integrity: sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==} | ||||||
|     dev: false |     dev: false | ||||||
| @ -8454,6 +8464,10 @@ packages: | |||||||
|     engines: {node: '>=4'} |     engines: {node: '>=4'} | ||||||
|     dev: true |     dev: true | ||||||
| 
 | 
 | ||||||
|  |   /unicode-emoji-json@0.4.0: | ||||||
|  |     resolution: {integrity: sha512-lVNOwh2AnmbwqtSrEVjAWKQoVzWgyWmXVqPuPkPfKb0tnA0+uYN/4ILCTdy9IRj/+3drAVhmjwjNJQr2dhCwnA==} | ||||||
|  |     dev: false | ||||||
|  | 
 | ||||||
|   /unicode-match-property-ecmascript@2.0.0: |   /unicode-match-property-ecmascript@2.0.0: | ||||||
|     resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} |     resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} | ||||||
|     engines: {node: '>=4'} |     engines: {node: '>=4'} | ||||||
| @ -8655,6 +8669,7 @@ packages: | |||||||
| 
 | 
 | ||||||
|   /update-browserslist-db@1.0.10(browserslist@4.21.5): |   /update-browserslist-db@1.0.10(browserslist@4.21.5): | ||||||
|     resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==} |     resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==} | ||||||
|  |     hasBin: true | ||||||
|     peerDependencies: |     peerDependencies: | ||||||
|       browserslist: '>= 4.21.0' |       browserslist: '>= 4.21.0' | ||||||
|     dependencies: |     dependencies: | ||||||
| @ -8665,6 +8680,7 @@ packages: | |||||||
| 
 | 
 | ||||||
|   /update-browserslist-db@1.0.11(browserslist@4.21.9): |   /update-browserslist-db@1.0.11(browserslist@4.21.9): | ||||||
|     resolution: {integrity: sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==} |     resolution: {integrity: sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==} | ||||||
|  |     hasBin: true | ||||||
|     peerDependencies: |     peerDependencies: | ||||||
|       browserslist: '>= 4.21.0' |       browserslist: '>= 4.21.0' | ||||||
|     dependencies: |     dependencies: | ||||||
| @ -8795,6 +8811,7 @@ packages: | |||||||
|   /vite@4.3.9(@types/node@18.15.11)(less@4.1.3): |   /vite@4.3.9(@types/node@18.15.11)(less@4.1.3): | ||||||
|     resolution: {integrity: sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==} |     resolution: {integrity: sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==} | ||||||
|     engines: {node: ^14.18.0 || >=16.0.0} |     engines: {node: ^14.18.0 || >=16.0.0} | ||||||
|  |     hasBin: true | ||||||
|     peerDependencies: |     peerDependencies: | ||||||
|       '@types/node': '>= 14' |       '@types/node': '>= 14' | ||||||
|       less: '*' |       less: '*' | ||||||
| @ -8828,6 +8845,7 @@ packages: | |||||||
|   /vitest@0.32.0(jsdom@19.0.0)(less@4.1.3): |   /vitest@0.32.0(jsdom@19.0.0)(less@4.1.3): | ||||||
|     resolution: {integrity: sha512-SW83o629gCqnV3BqBnTxhB10DAwzwEx3z+rqYZESehUB+eWsJxwcBQx7CKy0otuGMJTYh7qCVuUX23HkftGl/Q==} |     resolution: {integrity: sha512-SW83o629gCqnV3BqBnTxhB10DAwzwEx3z+rqYZESehUB+eWsJxwcBQx7CKy0otuGMJTYh7qCVuUX23HkftGl/Q==} | ||||||
|     engines: {node: '>=v14.18.0'} |     engines: {node: '>=v14.18.0'} | ||||||
|  |     hasBin: true | ||||||
|     peerDependencies: |     peerDependencies: | ||||||
|       '@edge-runtime/vm': '*' |       '@edge-runtime/vm': '*' | ||||||
|       '@vitest/browser': '*' |       '@vitest/browser': '*' | ||||||
| @ -8902,6 +8920,7 @@ packages: | |||||||
|   /vue-demi@0.13.11(vue@3.3.4): |   /vue-demi@0.13.11(vue@3.3.4): | ||||||
|     resolution: {integrity: sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==} |     resolution: {integrity: sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==} | ||||||
|     engines: {node: '>=12'} |     engines: {node: '>=12'} | ||||||
|  |     hasBin: true | ||||||
|     requiresBuild: true |     requiresBuild: true | ||||||
|     peerDependencies: |     peerDependencies: | ||||||
|       '@vue/composition-api': ^1.0.0-rc.1 |       '@vue/composition-api': ^1.0.0-rc.1 | ||||||
| @ -8916,6 +8935,7 @@ packages: | |||||||
|   /vue-demi@0.14.1(vue@3.3.4): |   /vue-demi@0.14.1(vue@3.3.4): | ||||||
|     resolution: {integrity: sha512-rt+yuCtXvscYot9SQQj3WKZJVSriPNqVkpVBNEHPzSgBv7QIYzsS410VqVgvx8f9AAPgjg+XPKvmV3vOqqkJQQ==} |     resolution: {integrity: sha512-rt+yuCtXvscYot9SQQj3WKZJVSriPNqVkpVBNEHPzSgBv7QIYzsS410VqVgvx8f9AAPgjg+XPKvmV3vOqqkJQQ==} | ||||||
|     engines: {node: '>=12'} |     engines: {node: '>=12'} | ||||||
|  |     hasBin: true | ||||||
|     requiresBuild: true |     requiresBuild: true | ||||||
|     peerDependencies: |     peerDependencies: | ||||||
|       '@vue/composition-api': ^1.0.0-rc.1 |       '@vue/composition-api': ^1.0.0-rc.1 | ||||||
| @ -8990,6 +9010,7 @@ packages: | |||||||
| 
 | 
 | ||||||
|   /vue-tsc@1.8.1(typescript@4.9.3): |   /vue-tsc@1.8.1(typescript@4.9.3): | ||||||
|     resolution: {integrity: sha512-GxBQrcb0Qvyrj1uZqnTXQyWbXdNDRY2MTa+r7ESgjhf+WzBSdxZfkS3KD/C3WhKYG+aN8hf44Hp5Gqzb6PehAA==} |     resolution: {integrity: sha512-GxBQrcb0Qvyrj1uZqnTXQyWbXdNDRY2MTa+r7ESgjhf+WzBSdxZfkS3KD/C3WhKYG+aN8hf44Hp5Gqzb6PehAA==} | ||||||
|  |     hasBin: true | ||||||
|     peerDependencies: |     peerDependencies: | ||||||
|       typescript: '*' |       typescript: '*' | ||||||
|     dependencies: |     dependencies: | ||||||
|  | |||||||
| @ -1,14 +1,14 @@ | |||||||
| import { type MaybeRef, get, useClipboard } from '@vueuse/core'; | import { type MaybeRef, get, useClipboard } from '@vueuse/core'; | ||||||
| import { useMessage } from 'naive-ui'; | import { useMessage } from 'naive-ui'; | ||||||
| 
 | 
 | ||||||
| export function useCopy({ source, text = 'Copied to the clipboard' }: { source: MaybeRef<unknown>; text?: string }) { | export function useCopy({ source, text = 'Copied to the clipboard' }: { source?: MaybeRef<unknown>; text?: string } = {}) { | ||||||
|   const { copy } = useClipboard({ source: computed(() => String(get(source))) }); |   const { copy } = useClipboard(source ? { source: computed(() => String(get(source))) } : {}); | ||||||
|   const message = useMessage(); |   const message = useMessage(); | ||||||
| 
 | 
 | ||||||
|   return { |   return { | ||||||
|     async copy() { |     async copy(content?: string, { notificationMessage }: { notificationMessage?: string } = {}) { | ||||||
|       await copy(); |       await copy(); | ||||||
|       message.success(text); |       message.success(notificationMessage ?? text); | ||||||
|     }, |     }, | ||||||
|   }; |   }; | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										42
									
								
								src/tools/emoji-picker/emoji-card.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/tools/emoji-picker/emoji-card.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,42 @@ | |||||||
|  | <script setup lang="ts"> | ||||||
|  | import type { EmojiInfo } from './emoji.types'; | ||||||
|  | import { useCopy } from '@/composable/copy'; | ||||||
|  | 
 | ||||||
|  | const props = (defineProps<{ emojiInfo: EmojiInfo }>()); | ||||||
|  | const { emojiInfo } = toRefs(props); | ||||||
|  | 
 | ||||||
|  | const { copy } = useCopy(); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |   <c-card flex items-center gap-3 important:py-8px important:pl-10px important:pr-5px> | ||||||
|  |     <div cursor-pointer text-30px @click="copy(emojiInfo.emoji, { notificationMessage: `Emoji ${emojiInfo.emoji} copied to the clipboard` })"> | ||||||
|  |       {{ emojiInfo.emoji }} | ||||||
|  |     </div> | ||||||
|  | 
 | ||||||
|  |     <div min-w-0 flex-1> | ||||||
|  |       <div truncate font-bold> | ||||||
|  |         {{ emojiInfo.title }} | ||||||
|  |       </div> | ||||||
|  | 
 | ||||||
|  |       <!-- <div> | ||||||
|  |         <c-link> | ||||||
|  |           {{ emojiInfo.codePoints }} | ||||||
|  |         </c-link> | ||||||
|  |       </div> | ||||||
|  |       <div /> | ||||||
|  |       <div rounded op-70> | ||||||
|  |         Unicode:  <span border="1px solid current op-30" b-rd-xl px-12px py-4px>{{ emojiInfo.unicode }}</span> | ||||||
|  |       </div> --> | ||||||
|  | 
 | ||||||
|  |       <div flex gap-2 font-mono text-xs op-70> | ||||||
|  |         <span cursor-pointer transition hover:text-primary @click="copy(emojiInfo.codePoints, { notificationMessage: `Code points '${emojiInfo.codePoints}' copied to the clipboard` })"> | ||||||
|  |           {{ emojiInfo.codePoints }} | ||||||
|  |         </span> | ||||||
|  |         <span cursor-pointer truncate transition hover:text-primary @click="copy(emojiInfo.unicode, { notificationMessage: `Unicode '${emojiInfo.unicode}' copied to the clipboard` })"> | ||||||
|  |           {{ emojiInfo.unicode }} | ||||||
|  |         </span> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   </c-card> | ||||||
|  | </template> | ||||||
							
								
								
									
										12
									
								
								src/tools/emoji-picker/emoji-grid.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/tools/emoji-picker/emoji-grid.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | <script setup lang="ts"> | ||||||
|  | import type { EmojiInfo } from './emoji.types'; | ||||||
|  | 
 | ||||||
|  | const props = withDefaults(defineProps<{ emojiInfos?: EmojiInfo[] }>(), { emojiInfos: () => [] }); | ||||||
|  | const { emojiInfos } = toRefs(props); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |   <div grid grid-cols-1 gap-2 lg:grid-cols-4 md:grid-cols-3 sm:grid-cols-2 xl:grid-cols-6> | ||||||
|  |     <emoji-card v-for="emojiInfo in emojiInfos" :key="emojiInfo.name" :emoji-info="emojiInfo" flex items-center gap-3 /> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
							
								
								
									
										85
									
								
								src/tools/emoji-picker/emoji-picker.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								src/tools/emoji-picker/emoji-picker.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,85 @@ | |||||||
|  | <script setup lang="ts"> | ||||||
|  | import emojiUnicodeData from 'unicode-emoji-json'; | ||||||
|  | import emojiKeywords from 'emojilib'; | ||||||
|  | import _ from 'lodash'; | ||||||
|  | import type { EmojiInfo } from './emoji.types'; | ||||||
|  | import { useFuzzySearch } from '@/composable/fuzzySearch'; | ||||||
|  | 
 | ||||||
|  | const escapeUnicode = ({ emoji }: { emoji: string }) => emoji.split('').map(unit => `\\u${unit.charCodeAt(0).toString(16).padStart(4, '0')}`).join(''); | ||||||
|  | const getEmojiCodePoints = ({ emoji }: { emoji: string }) => emoji.codePointAt(0) ? `0x${emoji.codePointAt(0)?.toString(16)}` : undefined; | ||||||
|  | 
 | ||||||
|  | const emojis = _.map(emojiUnicodeData, (emojiInfo, emoji) => ({ | ||||||
|  |   ...emojiInfo, | ||||||
|  |   emoji, | ||||||
|  |   title: _.capitalize(emojiInfo.name), | ||||||
|  |   keywords: emojiKeywords[emoji as keyof typeof emojiKeywords], | ||||||
|  |   codePoints: getEmojiCodePoints({ emoji }), | ||||||
|  |   unicode: escapeUnicode({ emoji }), | ||||||
|  | })); | ||||||
|  | 
 | ||||||
|  | const emojisGroups: { emojiInfos: EmojiInfo[]; group: string }[] = _ | ||||||
|  |   .chain(emojis) | ||||||
|  |   .groupBy('group') | ||||||
|  |   .map((emojiInfos, group) => ({ group, emojiInfos })) | ||||||
|  |   .value(); | ||||||
|  | 
 | ||||||
|  | const searchQuery = ref(''); | ||||||
|  | 
 | ||||||
|  | const { searchResult } = useFuzzySearch({ | ||||||
|  |   search: searchQuery, | ||||||
|  |   data: emojis, | ||||||
|  |   options: { | ||||||
|  |     keys: ['group', { name: 'name', weight: 3 }, 'keywords', 'unicode', 'codePoints', 'emoji'], | ||||||
|  |     threshold: 0.3, | ||||||
|  |     useExtendedSearch: true, | ||||||
|  |     isCaseSensitive: false, | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
|  | <template> | ||||||
|  |   <div mx-auto max-w-2400px important:flex-1> | ||||||
|  |     <div flex items-center gap-3> | ||||||
|  |       <c-input-text | ||||||
|  |         v-model:value="searchQuery" | ||||||
|  |         placeholder="Search emojis (e.g. 'smile')..." | ||||||
|  |         mx-auto max-w-600px | ||||||
|  |       > | ||||||
|  |         <template #prefix> | ||||||
|  |           <icon-mdi-search mr-6px color-black op-70 dark:color-white /> | ||||||
|  |         </template> | ||||||
|  |       </c-input-text> | ||||||
|  |     </div> | ||||||
|  | 
 | ||||||
|  |     <div v-if="searchQuery.trim().length > 0"> | ||||||
|  |       <div | ||||||
|  |         v-if="searchResult.length === 0" | ||||||
|  |         mt-4 | ||||||
|  |         text-20px | ||||||
|  |         font-bold | ||||||
|  |       > | ||||||
|  |         No results | ||||||
|  |       </div> | ||||||
|  | 
 | ||||||
|  |       <div v-else> | ||||||
|  |         <div mt-4 text-20px font-bold> | ||||||
|  |           Search result | ||||||
|  |         </div> | ||||||
|  | 
 | ||||||
|  |         <emoji-grid :emoji-infos="searchResult" /> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  | 
 | ||||||
|  |     <div | ||||||
|  |       v-for="{ group, emojiInfos } in emojisGroups" | ||||||
|  |       v-else | ||||||
|  |       :key="group" | ||||||
|  |     > | ||||||
|  |       <div mt-4 text-20px font-bold> | ||||||
|  |         {{ group }} | ||||||
|  |       </div> | ||||||
|  | 
 | ||||||
|  |       <emoji-grid :emoji-infos="emojiInfos" /> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
							
								
								
									
										8
									
								
								src/tools/emoji-picker/emoji.types.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/tools/emoji-picker/emoji.types.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | |||||||
|  | import type emojiUnicodeData from 'unicode-emoji-json'; | ||||||
|  | 
 | ||||||
|  | export type EmojiInfo = { | ||||||
|  |   title: string | ||||||
|  |   emoji: string | ||||||
|  |   codePoints: string | undefined | ||||||
|  |   unicode: string | ||||||
|  | } & typeof emojiUnicodeData['\uD83E\uDD10']; | ||||||
							
								
								
									
										12
									
								
								src/tools/emoji-picker/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/tools/emoji-picker/index.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | import { MoodSmile } from '@vicons/tabler'; | ||||||
|  | import { defineTool } from '../tool'; | ||||||
|  | 
 | ||||||
|  | export const tool = defineTool({ | ||||||
|  |   name: 'Emoji picker', | ||||||
|  |   path: '/emoji-picker', | ||||||
|  |   description: 'Copy and paste emojis easily and get the unicode and code points value of each emoji.', | ||||||
|  |   keywords: ['emoji', 'picker', 'unicode', 'copy', 'paste'], | ||||||
|  |   component: () => import('./emoji-picker.vue'), | ||||||
|  |   icon: MoodSmile, | ||||||
|  |   createdAt: new Date('2023-08-07'), | ||||||
|  | }); | ||||||
| @ -1,6 +1,7 @@ | |||||||
| import { tool as base64FileConverter } from './base64-file-converter'; | import { tool as base64FileConverter } from './base64-file-converter'; | ||||||
| import { tool as base64StringConverter } from './base64-string-converter'; | import { tool as base64StringConverter } from './base64-string-converter'; | ||||||
| import { tool as basicAuthGenerator } from './basic-auth-generator'; | import { tool as basicAuthGenerator } from './basic-auth-generator'; | ||||||
|  | import { tool as emojiPicker } from './emoji-picker'; | ||||||
| import { tool as passwordStrengthAnalyser } from './password-strength-analyser'; | import { tool as passwordStrengthAnalyser } from './password-strength-analyser'; | ||||||
| import { tool as yamlToToml } from './yaml-to-toml'; | import { tool as yamlToToml } from './yaml-to-toml'; | ||||||
| import { tool as jsonToToml } from './json-to-toml'; | import { tool as jsonToToml } from './json-to-toml'; | ||||||
| @ -144,7 +145,7 @@ export const toolsByCategory: ToolCategory[] = [ | |||||||
|   }, |   }, | ||||||
|   { |   { | ||||||
|     name: 'Text', |     name: 'Text', | ||||||
|     components: [loremIpsumGenerator, textStatistics], |     components: [loremIpsumGenerator, textStatistics, emojiPicker], | ||||||
|   }, |   }, | ||||||
|   { |   { | ||||||
|     name: 'Data', |     name: 'Data', | ||||||
|  | |||||||
| @ -112,7 +112,7 @@ function Value({ value, status }: { value: unknown; status: string }) { | |||||||
|   const { copy } = useCopy({ source: formatedValue }); |   const { copy } = useCopy({ source: formatedValue }); | ||||||
| 
 | 
 | ||||||
|   return ( |   return ( | ||||||
|     <span class={['value', status]} onClick={copy}> |     <span class={['value', status]} onClick={() => copy()}> | ||||||
|       {formatedValue} |       {formatedValue} | ||||||
|     </span> |     </span> | ||||||
|   ); |   ); | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user