feat: added emoji picker
This commit is contained in:
parent
b214d76e93
commit
e81d4abd8c
@ -14,6 +14,7 @@
|
||||
"@vueuse/integrations": "^10.3.0",
|
||||
"feather-icons": "^4.28.0",
|
||||
"frappe-ui": "^0.1.52",
|
||||
"gemoji": "^8.1.0",
|
||||
"mime": "^4.0.1",
|
||||
"pinia": "^2.0.33",
|
||||
"socket.io-client": "^4.7.2",
|
||||
|
||||
90
frontend/src/components/IconPicker.vue
Normal file
90
frontend/src/components/IconPicker.vue
Normal file
@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<Popover transition="default">
|
||||
<template #target="{ togglePopover, isOpen }">
|
||||
<slot v-bind="{ isOpen, togglePopover }">
|
||||
<span class="text-base"> {{ modelValue || '' }} </span>
|
||||
</slot>
|
||||
</template>
|
||||
<template #body>
|
||||
<div class="my-3 max-w-max transform bg-white px-4 sm:px-0">
|
||||
<div
|
||||
class="relative max-h-96 overflow-y-auto rounded-lg pb-3 shadow-2xl ring-1 ring-black ring-opacity-5"
|
||||
>
|
||||
<div class="flex gap-2 px-3 pb-1 pt-3">
|
||||
<div class="flex-1">
|
||||
<FormControl
|
||||
type="text"
|
||||
placeholder="Search by keyword"
|
||||
v-model="search"
|
||||
:debounce="300"
|
||||
/>
|
||||
</div>
|
||||
<Button @click="setRandom">Random</Button>
|
||||
</div>
|
||||
<div class="w-96"></div>
|
||||
<div class="px-3" v-for="(emojis, group) in emojiGroups" :key="group">
|
||||
<div class="sticky top-0 bg-white pb-2 pt-3 text-sm text-gray-700">
|
||||
{{ group }}
|
||||
</div>
|
||||
<div class="grid w-96 grid-cols-12 place-items-center">
|
||||
<button
|
||||
class="h-8 w-8 rounded-md p-1 text-2xl hover:bg-gray-100 focus:outline-none focus:ring focus:ring-blue-200"
|
||||
v-for="_emoji in emojis"
|
||||
:key="_emoji.description"
|
||||
@click="emoji = _emoji.emoji"
|
||||
:title="_emoji.description"
|
||||
>
|
||||
{{ _emoji.emoji }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Popover>
|
||||
</template>
|
||||
<script setup>
|
||||
import { gemoji } from 'gemoji'
|
||||
import { Popover } from 'frappe-ui'
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const search = ref('')
|
||||
const emoji = defineModel()
|
||||
|
||||
const emojiGroups = computed(() => {
|
||||
let groups = {}
|
||||
for (let _emoji of gemoji) {
|
||||
if (search.value) {
|
||||
let keywords = [_emoji.description, ..._emoji.names, ..._emoji.tags]
|
||||
.join(' ')
|
||||
.toLowerCase()
|
||||
if (!keywords.includes(search.value.toLowerCase())) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
let group = groups[_emoji.category]
|
||||
if (!group) {
|
||||
groups[_emoji.category] = []
|
||||
group = groups[_emoji.category]
|
||||
}
|
||||
group.push(_emoji)
|
||||
}
|
||||
if (!Object.keys(groups).length) {
|
||||
groups['No results'] = []
|
||||
}
|
||||
return groups
|
||||
})
|
||||
|
||||
function setRandom() {
|
||||
let total = gemoji.length
|
||||
let index = randomInt(0, total - 1)
|
||||
emoji.value = gemoji[index].emoji
|
||||
}
|
||||
|
||||
function randomInt(min, max) {
|
||||
return Math.floor(Math.random() * (max - min + 1) + min)
|
||||
}
|
||||
|
||||
defineExpose({ setRandom })
|
||||
</script>
|
||||
19
frontend/src/components/Icons/SmileIcon.vue
Normal file
19
frontend/src/components/Icons/SmileIcon.vue
Normal file
@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
height="24"
|
||||
width="24"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
class=""
|
||||
version="1.1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
enable-background="new 0 0 24 24"
|
||||
>
|
||||
<title>smiley</title>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M9.153,11.603c0.795,0,1.439-0.879,1.439-1.962S9.948,7.679,9.153,7.679 S7.714,8.558,7.714,9.641S8.358,11.603,9.153,11.603z M5.949,12.965c-0.026-0.307-0.131,5.218,6.063,5.551 c6.066-0.25,6.066-5.551,6.066-5.551C12,14.381,5.949,12.965,5.949,12.965z M17.312,14.073c0,0-0.669,1.959-5.051,1.959 c-3.505,0-5.388-1.164-5.607-1.959C6.654,14.073,12.566,15.128,17.312,14.073z M11.804,1.011c-6.195,0-10.826,5.022-10.826,11.217 s4.826,10.761,11.021,10.761S23.02,18.423,23.02,12.228C23.021,6.033,17.999,1.011,11.804,1.011z M12,21.354 c-5.273,0-9.381-3.886-9.381-9.159s3.942-9.548,9.215-9.548s9.548,4.275,9.548,9.548C21.381,17.467,17.273,21.354,12,21.354z M15.108,11.603c0.795,0,1.439-0.879,1.439-1.962s-0.644-1.962-1.439-1.962s-1.439,0.879-1.439,1.962S14.313,11.603,15.108,11.603z"
|
||||
></path>
|
||||
</svg>
|
||||
</template>
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="flex items-end gap-2 px-10 py-2.5">
|
||||
<div class="flex h-8 items-center">
|
||||
<div class="flex h-8 items-center gap-2">
|
||||
<FileUploader @success="(file) => uploadFile(file)">
|
||||
<template v-slot="{ file, progress, uploading, openFileSelector }">
|
||||
<div class="flex items-center space-x-2">
|
||||
@ -16,6 +16,21 @@
|
||||
</div>
|
||||
</template>
|
||||
</FileUploader>
|
||||
<IconPicker
|
||||
v-model="emoji"
|
||||
v-slot="{ togglePopover }"
|
||||
@update:modelValue="
|
||||
() => {
|
||||
content += emoji
|
||||
$refs.textarea.$el.focus()
|
||||
}
|
||||
"
|
||||
>
|
||||
<SmileIcon
|
||||
@click="togglePopover"
|
||||
class="flex size-4.5 rounded-sm text-xl leading-none text-gray-500 cursor-pointer"
|
||||
/>
|
||||
</IconPicker>
|
||||
</div>
|
||||
<Textarea
|
||||
ref="textarea"
|
||||
@ -41,6 +56,8 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import IconPicker from '@/components/IconPicker.vue'
|
||||
import SmileIcon from '@/components/Icons/SmileIcon.vue'
|
||||
import { createResource, Textarea, FileUploader, Dropdown } from 'frappe-ui'
|
||||
import FeatherIcon from 'frappe-ui/src/components/FeatherIcon.vue'
|
||||
import { ref, computed, nextTick } from 'vue'
|
||||
@ -53,6 +70,7 @@ const doc = defineModel()
|
||||
const whatsapp = defineModel('whatsapp')
|
||||
const rows = ref(1)
|
||||
const textarea = ref(null)
|
||||
const emoji = ref('')
|
||||
|
||||
const content = ref('')
|
||||
const placeholder = ref(__('Type your message here...'))
|
||||
|
||||
@ -1795,6 +1795,11 @@ function-bind@^1.1.2:
|
||||
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
|
||||
integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
|
||||
|
||||
gemoji@^8.1.0:
|
||||
version "8.1.0"
|
||||
resolved "https://registry.yarnpkg.com/gemoji/-/gemoji-8.1.0.tgz#3d47a26e569c51efa95198822a6f483d7a7ae600"
|
||||
integrity sha512-HA4Gx59dw2+tn+UAa7XEV4ufUKI4fH1KgcbenVA9YKSj1QJTT0xh5Mwv5HMFNN3l2OtUe3ZIfuRwSyZS5pLIWw==
|
||||
|
||||
get-east-asian-width@^1.0.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz#5e6ebd9baee6fb8b7b6bd505221065f0cd91f64e"
|
||||
@ -3124,6 +3129,7 @@ string-argv@0.3.2:
|
||||
integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==
|
||||
|
||||
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0:
|
||||
name string-width-cjs
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
@ -3151,6 +3157,7 @@ string-width@^7.0.0:
|
||||
strip-ansi "^7.1.0"
|
||||
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||
name strip-ansi-cjs
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user