diff --git a/components/publish/PublishEmojiPicker.client.vue b/components/publish/PublishEmojiPicker.client.vue new file mode 100644 index 00000000..3365dd2b --- /dev/null +++ b/components/publish/PublishEmojiPicker.client.vue @@ -0,0 +1,46 @@ +<script setup lang="ts"> +import type { Picker } from 'emoji-mart' + +const emit = defineEmits<{ + (e: 'select', code: string): void +}>() + +const el = $ref<HTMLElement>() +let picker = $ref<Picker>() + +async function openEmojiPicker() { + if (!picker) { + const { Picker } = await import('emoji-mart') + picker = new Picker({ + data: () => import('@emoji-mart/data').then(r => r.default), + onEmojiSelect(e: any) { + emit('select', e.native) + }, + theme: isDark.value ? 'dark' : 'light', + }) + // TODO: custom picker + el?.appendChild(picker as any as HTMLElement) + } +} + +watchEffect(() => { + if (!picker) + return + picker.update({ + theme: isDark.value ? 'dark' : 'light', + }) +}) +</script> + +<template> + <VDropdown + @apply-show="openEmojiPicker()" + > + <button btn-action-icon :title="$t('tooltip.emoji')"> + <div i-ri:emotion-line /> + </button> + <template #popper> + <div ref="el" /> + </template> + </VDropdown> +</template> diff --git a/components/publish/PublishWidget.vue b/components/publish/PublishWidget.vue index 49993cda..c0ae1449 100644 --- a/components/publish/PublishWidget.vue +++ b/components/publish/PublishWidget.vue @@ -64,6 +64,10 @@ async function handlePaste(evt: ClipboardEvent) { await uploadAttachments(Array.from(files)) } +function insertText(text: string) { + editor.value?.chain().insertContent(text).focus().run() +} + async function pickAttachments() { const files = await fileOpen([ { @@ -232,6 +236,8 @@ defineExpose({ v-if="shouldExpanded" flex="~ gap-2 1" m="l--1" pt-2 justify="between" max-full border="t base" > + <PublishEmojiPicker @select="insertText" /> + <CommonTooltip placement="bottom" :content="$t('tooltip.add_media')"> <button btn-action-icon :aria-label="$t('tooltip.add_media')" @click="pickAttachments"> <div i-ri:image-add-line /> diff --git a/locales/en-US.json b/locales/en-US.json index 20beb98a..90ffbbf6 100644 --- a/locales/en-US.json +++ b/locales/en-US.json @@ -300,6 +300,7 @@ "add_content_warning": "Add content warning", "add_media": "Add images, a video or an audio file", "change_content_visibility": "Change content visibility", + "emoji": "Emoji", "explore_links_intro": "These news stories are being talked about by people on this and other servers of the decentralized network right now.", "explore_posts_intro": "These posts from this and other servers in the decentralized network are gaining traction on this server right now.", "explore_tags_intro": "These hashtags are gaining traction among people on this and other servers of the decentralized network right now.", diff --git a/package.json b/package.json index 4c1e0607..16eb6a66 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "devDependencies": { "@antfu/eslint-config": "^0.34.0", "@antfu/ni": "^0.18.8", + "@emoji-mart/data": "^1.1.0", "@iconify-json/carbon": "^1.1.11", "@iconify-json/logos": "^1.1.19", "@iconify-json/material-symbols": "^1.1.25", @@ -74,6 +75,7 @@ "@vitejs/plugin-vue": "^3.2.0", "@vue-macros/nuxt": "^0.1.2", "@vueuse/nuxt": "^9.8.2", + "emoji-mart": "^5.4.0", "eslint": "^8.29.0", "esno": "^0.16.3", "fs-extra": "^11.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index aa0fc4fe..a0a1da9e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3,6 +3,7 @@ lockfileVersion: 5.4 specifiers: '@antfu/eslint-config': ^0.34.0 '@antfu/ni': ^0.18.8 + '@emoji-mart/data': ^1.1.0 '@fnando/sparkline': ^0.3.10 '@iconify-json/carbon': ^1.1.11 '@iconify-json/logos': ^1.1.19 @@ -34,6 +35,7 @@ specifiers: '@vueuse/nuxt': ^9.8.2 blurhash: ^2.0.4 browser-fs-access: ^0.31.1 + emoji-mart: ^5.4.0 eslint: ^8.29.0 esno: ^0.16.3 floating-vue: 2.0.0-beta.20 @@ -105,6 +107,7 @@ dependencies: devDependencies: '@antfu/eslint-config': 0.34.0_s5ps7njkmjlaqajutnox5ntcla '@antfu/ni': 0.18.8 + '@emoji-mart/data': 1.1.0 '@iconify-json/carbon': 1.1.11 '@iconify-json/logos': 1.1.19 '@iconify-json/material-symbols': 1.1.25 @@ -122,6 +125,7 @@ devDependencies: '@vitejs/plugin-vue': 3.2.0 '@vue-macros/nuxt': 0.1.2_bvgxowbvgmi7uddrvyjqbdegoy '@vueuse/nuxt': 9.8.2_nuxt@3.0.0 + emoji-mart: 5.4.0 eslint: 8.29.0 esno: 0.16.3 fs-extra: 11.1.0 @@ -1459,6 +1463,10 @@ packages: mime: 3.0.0 dev: true + /@emoji-mart/data/1.1.0: + resolution: {integrity: sha512-gwwGC0v5+BQM5On8hy0Uw7qT+xBHVLFuamHj8wHLo4JkuYM+XlGbQuQZj/X7JJLQuBiHs4d3Xh2O+h6YlbtCCA==} + dev: true + /@esbuild-kit/cjs-loader/2.4.1: resolution: {integrity: sha512-lhc/XLith28QdW0HpHZvZKkorWgmCNT7sVelMHDj3HFdTfdqkwEKvT+aXVQtNAmCC39VJhunDkWhONWB7335mg==} dependencies: @@ -1624,8 +1632,8 @@ packages: vue-i18n: optional: true dependencies: - '@intlify/message-compiler': 9.3.0-beta.10 - '@intlify/shared': 9.3.0-beta.10 + '@intlify/message-compiler': 9.3.0-beta.11 + '@intlify/shared': 9.3.0-beta.11 jsonc-eslint-parser: 1.4.1 source-map: 0.6.1 vue-i18n: 9.3.0-beta.10 @@ -1657,11 +1665,24 @@ packages: source-map: 0.6.1 dev: true + /@intlify/message-compiler/9.3.0-beta.11: + resolution: {integrity: sha512-gGGfBGzM7JBXp1Q9gbDAy5jELz9ho3ILqnpxp2yp64+gkqohrqc2YXIvCdwZoc6AtKIh/Zmv4sWVqxkvMsBWtQ==} + engines: {node: '>= 14'} + dependencies: + '@intlify/shared': 9.3.0-beta.11 + source-map: 0.6.1 + dev: true + /@intlify/shared/9.3.0-beta.10: resolution: {integrity: sha512-h93uAanbAt/XgjDHclrVB7xix6r7Uz11wx0iGNOCdHP7aA2LCJjUT3uNbekJjjbo+Fl5jzTSJZdm2SexzoqhRA==} engines: {node: '>= 14'} dev: true + /@intlify/shared/9.3.0-beta.11: + resolution: {integrity: sha512-CtbotesxTRiC3bRyXyv1NG39fkqJ790f8z8xFaeIXSZpOdiyxoh5BIyypCzSFQZDGLwz0Q9gyWbW1XpxQJm68Q==} + engines: {node: '>= 14'} + dev: true + /@intlify/unplugin-vue-i18n/0.8.0_vue-i18n@9.3.0-beta.10: resolution: {integrity: sha512-bqMDYrbmV0oMLGHTdYMUXfcEsy2rPwQnGrQAg4gvw5FimvJfTQt3RliLVayT5ldOfeT2g0IUc/0t7LPeGrFUag==} engines: {node: '>= 14.16'} @@ -1678,7 +1699,7 @@ packages: optional: true dependencies: '@intlify/bundle-utils': 3.4.0_vue-i18n@9.3.0-beta.10 - '@intlify/shared': 9.3.0-beta.10 + '@intlify/shared': 9.3.0-beta.11 '@rollup/pluginutils': 4.2.1 '@vue/compiler-sfc': 3.2.45 debug: 4.3.4 @@ -4725,6 +4746,10 @@ packages: resolution: {integrity: sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==} dev: true + /emoji-mart/5.4.0: + resolution: {integrity: sha512-xrRrUmMqZG64oRxmUZcf8zSMUGQtIUYUL3aZD5iMkqAve+I9wMNh3OVOXL7NW9fEm48L2LI3BUPpj/DUIAJrVg==} + dev: true + /emoji-regex/8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} dev: true @@ -9966,7 +9991,7 @@ packages: vue-router: optional: true dependencies: - '@intlify/shared': 9.3.0-beta.10 + '@intlify/shared': 9.3.0-beta.11 '@intlify/vue-i18n-bridge': 0.8.0_vue-i18n@9.3.0-beta.10 '@intlify/vue-router-bridge': 0.8.0 ufo: 1.0.1 diff --git a/styles/global.css b/styles/global.css index aaba9cc4..b7416316 100644 --- a/styles/global.css +++ b/styles/global.css @@ -175,3 +175,7 @@ body { stroke: var(--c-primary); stroke-width: 2; } + +em-emoji-picker { + --border-radius: 0; +}