commit: 296a6fa4e3d14f0c8ca091870fd4f7e85bc34df8
parent 518fcf856a63c5b50745a6549e10b228d27c10d3
Author: Henry Jameson <me@hjkos.com>
Date: Sun, 9 Oct 2022 23:42:36 +0300
some shitty initial implementation of emoji picker with popover
Diffstat:
6 files changed, 150 insertions(+), 147 deletions(-)
diff --git a/src/components/emoji_input/emoji_input.js b/src/components/emoji_input/emoji_input.js
@@ -115,12 +115,12 @@ const EmojiInput = {
caret: 0,
focused: false,
blurTimeout: null,
- showPicker: false,
temporarilyHideSuggestions: false,
keepOpen: false,
disableClickOutside: false,
suggestions: [],
- overlayStyle: {}
+ overlayStyle: {},
+ pickerShown: false
}
},
components: {
@@ -142,7 +142,6 @@ const EmojiInput = {
return this.focused &&
this.suggestions &&
this.suggestions.length > 0 &&
- !this.showPicker &&
!this.temporarilyHideSuggestions
},
textAtCaret () {
@@ -285,8 +284,8 @@ const EmojiInput = {
if (pickerInput) pickerInput.focus()
},
triggerShowPicker () {
- this.showPicker = true
this.$nextTick(() => {
+ this.$refs.picker.showPicker()
this.scrollIntoView()
this.focusPickerInput()
})
@@ -299,12 +298,16 @@ const EmojiInput = {
}, 0)
},
togglePicker () {
+ console.log('piick')
this.input.focus()
- this.showPicker = !this.showPicker
- if (this.showPicker) {
+ if (!this.pickerShown) {
+ console.log('pick')
this.scrollIntoView()
+ this.$refs.picker.showPicker()
this.$refs.picker.startEmojiLoad()
this.$nextTick(this.focusPickerInput)
+ } else {
+ this.$refs.picker.hidePicker()
}
},
replace (replacement) {
@@ -441,6 +444,12 @@ const EmojiInput = {
}
})
},
+ onPickerShown () {
+ this.pickerShown = true
+ },
+ onPickerClosed () {
+ this.pickerShown = false
+ },
onBlur (e) {
// Clicking on any suggestion removes focus from autocomplete,
// preventing click handler ever executing.
@@ -458,9 +467,6 @@ const EmojiInput = {
this.blurTimeout = null
}
- if (!this.keepOpen) {
- this.showPicker = false
- }
this.focused = true
this.setCaret(e)
this.temporarilyHideSuggestions = false
@@ -523,27 +529,15 @@ const EmojiInput = {
this.input.focus()
}
}
-
- this.showPicker = false
},
onInput (e) {
- this.showPicker = false
this.setCaret(e)
this.$emit('update:modelValue', e.target.value)
},
- onClickInput (e) {
- this.showPicker = false
- },
- onClickOutside (e) {
- if (this.disableClickOutside) return
- this.showPicker = false
- },
onStickerUploaded (e) {
- this.showPicker = false
this.$emit('sticker-uploaded', e)
},
onStickerUploadFailed (e) {
- this.showPicker = false
this.$emit('sticker-upload-Failed', e)
},
setCaret ({ target: { selectionStart } }) {
diff --git a/src/components/emoji_input/emoji_input.vue b/src/components/emoji_input/emoji_input.vue
@@ -1,7 +1,6 @@
<template>
<div
ref="root"
- v-click-outside="onClickOutside"
class="emoji-input"
:class="{ 'with-picker': !hideEmojiButton }"
>
@@ -24,13 +23,13 @@
<EmojiPicker
v-if="enableEmojiPicker"
ref="picker"
- :class="{ hide: !showPicker }"
- :showing="showPicker"
:enable-sticker-picker="enableStickerPicker"
class="emoji-picker-panel"
@emoji="insert"
@sticker-uploaded="onStickerUploaded"
@sticker-upload-failed="onStickerUploadFailed"
+ @show="onPickerShown"
+ @close="onPickerClosed"
/>
</template>
<Popover
diff --git a/src/components/emoji_picker/emoji_picker.js b/src/components/emoji_picker/emoji_picker.js
@@ -1,5 +1,6 @@
import { defineAsyncComponent } from 'vue'
import Checkbox from '../checkbox/checkbox.vue'
+import Popover from 'src/components/popover/popover.vue'
import StillImage from '../still-image/still-image.vue'
import { ensureFinalFallback } from '../../i18n/languages.js'
import lozad from 'lozad'
@@ -87,10 +88,6 @@ const EmojiPicker = {
required: false,
type: Boolean,
default: false
- },
- showing: {
- required: true,
- type: Boolean
}
},
data () {
@@ -111,15 +108,30 @@ const EmojiPicker = {
components: {
StickerPicker: defineAsyncComponent(() => import('../sticker_picker/sticker_picker.vue')),
Checkbox,
- StillImage
+ StillImage,
+ Popover
},
methods: {
+ showPicker () {
+ console.log('pick')
+ this.$refs.popover.showPopover()
+ this.onShowing()
+ },
+ hidePicker () {
+ this.$refs.popover.hidePopover()
+ },
setGroupRef (name) {
return el => { this.groupRefs[name] = el }
},
setEmojiRef (name) {
return el => { this.emojiRefs[name] = el }
},
+ onPopoverShown () {
+ this.$emit('show')
+ },
+ onPopoverClosed () {
+ this.$emit('close')
+ },
onStickerUploaded (e) {
this.$emit('sticker-uploaded', e)
},
@@ -251,16 +263,6 @@ const EmojiPicker = {
allCustomGroups () {
this.waitForDomAndInitializeLazyLoad()
this.filteredEmojiGroups = this.getFilteredEmojiGroups()
- },
- showing (val) {
- if (val) {
- this.onShowing()
- }
- }
- },
- mounted () {
- if (this.showing) {
- this.onShowing()
}
},
destroyed () {
diff --git a/src/components/emoji_picker/emoji_picker.scss b/src/components/emoji_picker/emoji_picker.scss
@@ -6,14 +6,10 @@ $emoji-picker-header-picture-height: 32px;
$emoji-picker-emoji-size: 32px;
.emoji-picker {
+ width: 25em;
+ max-width: 100vw;
display: flex;
flex-direction: column;
- position: absolute;
- right: 0;
- left: 0;
- margin: 0 !important;
- // TODO: actually use popover in emoji picker
- z-index: var(--ZI_popovers);
background-color: $fallback--bg;
background-color: var(--popover, $fallback--bg);
color: $fallback--link;
diff --git a/src/components/emoji_picker/emoji_picker.vue b/src/components/emoji_picker/emoji_picker.vue
@@ -1,129 +1,135 @@
<template>
- <div
- class="emoji-picker panel panel-default panel-body"
+ <Popover
+ trigger="click"
+ popover-class="emoji-picker popover-default"
+ ref="popover"
+ @show="onPopoverShown"
+ @close="onPopoverClosed"
>
- <div class="heading">
- <span
- ref="header"
- class="emoji-tabs"
- >
+ <template #content>
+ <div class="heading">
<span
- v-for="group in filteredEmojiGroups"
- :ref="setGroupRef('group-header-' + group.id)"
- :key="group.id"
- class="emoji-tabs-item"
- :class="{
- active: activeGroupView === group.id
- }"
- :title="group.text"
- @click.prevent="highlight(group.id)"
+ ref="header"
+ class="emoji-tabs"
>
<span
- v-if="group.image"
- class="emoji-picker-header-image"
+ v-for="group in filteredEmojiGroups"
+ :ref="setGroupRef('group-header-' + group.id)"
+ :key="group.id"
+ class="emoji-tabs-item"
+ :class="{
+ active: activeGroupView === group.id
+ }"
+ :title="group.text"
+ @click.prevent="highlight(group.id)"
>
- <still-image
- :alt="group.text"
- :src="group.image"
+ <span
+ v-if="group.image"
+ class="emoji-picker-header-image"
+ >
+ <still-image
+ :alt="group.text"
+ :src="group.image"
+ />
+ </span>
+ <FAIcon
+ v-else
+ :icon="group.icon"
+ fixed-width
/>
</span>
- <FAIcon
- v-else
- :icon="group.icon"
- fixed-width
- />
</span>
- </span>
- <span
- v-if="stickerPickerEnabled"
- class="additional-tabs"
- >
<span
- class="stickers-tab-icon additional-tabs-item"
- :class="{active: showingStickers}"
- :title="$t('emoji.stickers')"
- @click.prevent="toggleStickers"
+ v-if="stickerPickerEnabled"
+ class="additional-tabs"
>
- <FAIcon
- icon="sticky-note"
- fixed-width
- />
+ <span
+ class="stickers-tab-icon additional-tabs-item"
+ :class="{active: showingStickers}"
+ :title="$t('emoji.stickers')"
+ @click.prevent="toggleStickers"
+ >
+ <FAIcon
+ icon="sticky-note"
+ fixed-width
+ />
+ </span>
</span>
- </span>
- </div>
- <div
- v-if="contentLoaded"
- class="content"
- >
+ </div>
<div
- class="emoji-content"
- :class="{hidden: showingStickers}"
+ v-if="contentLoaded"
+ class="content"
>
- <div class="emoji-search">
- <input
- v-model="keyword"
- type="text"
- class="form-control"
- :placeholder="$t('emoji.search_emoji')"
- @input="$event.target.composing = false"
- >
- </div>
<div
- ref="emoji-groups"
- class="emoji-groups"
- :class="groupsScrolledClass"
- @scroll="onScroll"
+ class="emoji-content"
+ :class="{hidden: showingStickers}"
>
+ <div class="emoji-search">
+ <input
+ v-model="keyword"
+ type="text"
+ class="form-control"
+ :placeholder="$t('emoji.search_emoji')"
+ @input="$event.target.composing = false"
+ >
+ </div>
<div
- v-for="group in filteredEmojiGroups"
- :key="group.id"
- class="emoji-group"
+ ref="emoji-groups"
+ class="emoji-groups"
+ :class="groupsScrolledClass"
+ @scroll="onScroll"
>
- <h6
- :ref="setGroupRef('group-' + group.id)"
- class="emoji-group-title"
- >
- {{ group.text }}
- </h6>
- <span
- v-for="emoji in group.emojis"
- :key="group.id + emoji.displayText"
- :title="maybeLocalizedEmojiName(emoji)"
- class="emoji-item"
- @click.stop.prevent="onEmoji(emoji)"
+ <div
+ v-for="group in filteredEmojiGroups"
+ :key="group.id"
+ class="emoji-group"
>
+ <h6
+ :ref="setGroupRef('group-' + group.id)"
+ class="emoji-group-title"
+ >
+ {{ group.text }}
+ </h6>
<span
- v-if="!emoji.imageUrl"
- class="emoji-picker-emoji -unicode"
- >{{ emoji.replacement }}</span>
- <still-image
- v-else
- :ref="setEmojiRef(group.id + emoji.displayText)"
- class="emoji-picker-emoji -custom"
- :data-src="emoji.imageUrl"
- :data-emoji-name="group.id + emoji.displayText"
- />
- </span>
- <span :ref="setGroupRef('group-end-' + group.id)" />
+ v-for="emoji in group.emojis"
+ :key="group.id + emoji.displayText"
+ :title="maybeLocalizedEmojiName(emoji)"
+ class="emoji-item"
+ @click.stop.prevent="onEmoji(emoji)"
+ >
+ <span
+ v-if="!emoji.imageUrl"
+ class="emoji-picker-emoji -unicode"
+ >{{ emoji.replacement }}</span>
+ <still-image
+ v-else
+ :ref="setEmojiRef(group.id + emoji.displayText)"
+ class="emoji-picker-emoji -custom"
+ :data-src="emoji.imageUrl"
+ :data-emoji-name="group.id + emoji.displayText"
+ />
+ </span>
+ <span :ref="setGroupRef('group-end-' + group.id)" />
+ </div>
+ </div>
+ <div class="keep-open">
+ <Checkbox v-model="keepOpen">
+ {{ $t('emoji.keep_open') }}
+ </Checkbox>
</div>
</div>
- <div class="keep-open">
- <Checkbox v-model="keepOpen">
- {{ $t('emoji.keep_open') }}
- </Checkbox>
+ <div
+ v-if="showingStickers"
+ class="stickers-content"
+ >
+ <sticker-picker
+ @uploaded="onStickerUploaded"
+ @upload-failed="onStickerUploadFailed"
+ />
</div>
</div>
- <div
- v-if="showingStickers"
- class="stickers-content"
- >
- <sticker-picker
- @uploaded="onStickerUploaded"
- @upload-failed="onStickerUploadFailed"
- />
- </div>
- </div>
- </div>
+ </template>
+ </Popover>
</template>
<script src="./emoji_picker.js"></script>
diff --git a/src/components/popover/popover.js b/src/components/popover/popover.js
@@ -63,6 +63,7 @@ const Popover = {
// used to avoid blinking if hovered onto popover
graceTimeout: null,
parentPopover: null,
+ disableClickOutside: false,
childrenShown: new Set()
}
},
@@ -234,6 +235,10 @@ const Popover = {
},
showPopover () {
if (this.disabled) return
+ this.disableClickOutside = true
+ setTimeout(() => {
+ this.disableClickOutside = false
+ }, 0)
const wasHidden = this.hidden
this.hidden = false
this.parentPopover && this.parentPopover.onChildPopoverState(this, true)
@@ -294,6 +299,7 @@ const Popover = {
}
},
onClickOutside (e) {
+ if (this.disableClickOutside) return
if (this.hidden) return
if (this.$refs.content && this.$refs.content.contains(e.target)) return
if (this.$el.contains(e.target)) return