* fix(picker): only focus picker search on desktop * detect using touch detection instead
184 lines
5.2 KiB
HTML
184 lines
5.2 KiB
HTML
<ModalDialog
|
|
{id}
|
|
{label}
|
|
{title}
|
|
background="var(--main-bg)"
|
|
>
|
|
<div class="emoji-container" {style} ref:container >
|
|
{#if loaded}
|
|
<emoji-mart props-json={emojiMartPropsJson} ></emoji-mart>
|
|
{:elseif error}
|
|
<div>Failed to load emoji picker: {error}</div>
|
|
{:else}
|
|
<div class="emoji-container-loading" >
|
|
<LoadingSpinner />
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
</ModalDialog>
|
|
<style>
|
|
.emoji-container {
|
|
max-width: calc(100vw - 20px);
|
|
position: relative;
|
|
}
|
|
.emoji-container-loading {
|
|
position: absolute;
|
|
left: 0;
|
|
right: 0;
|
|
top: 0;
|
|
bottom: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
:global(.emoji-mart-category .emoji-mart-emoji-custom span,
|
|
.emoji-mart-preview-emoji .emoji-mart-emoji-custom span) {
|
|
/* some custom emoji look repeated because they aren't tall enough */
|
|
background-repeat: no-repeat;
|
|
background-position: center center;
|
|
}
|
|
:global(.emoji-container .emoji-mart-category .emoji-mart-emoji span, .emoji-container .emoji-mart-anchor) {
|
|
cursor: pointer;
|
|
}
|
|
:global(.emoji-container .emoji-mart-search-icon) {
|
|
top: 6px; /* this looks a bit off-center with the native emoji */
|
|
}
|
|
|
|
:global(.emoji-container .emoji-mart-skin) {
|
|
max-width: 24px;
|
|
}
|
|
|
|
:global(.emoji-container .emoji-mart-skin-swatch.selected) {
|
|
width: 24px;
|
|
}
|
|
|
|
:global(.emoji-container .emoji-mart-skin-swatches.opened .emoji-mart-skin-swatch) {
|
|
width: 24px;
|
|
}
|
|
|
|
@media (max-width: 320px) {
|
|
:global(.emoji-container .emoji-mart-preview) {
|
|
height: 60px;
|
|
}
|
|
}
|
|
</style>
|
|
<script>
|
|
import ModalDialog from './ModalDialog.html'
|
|
import { store } from '../../../_store/store'
|
|
import { insertEmoji } from '../../../_actions/emoji'
|
|
import { show } from '../helpers/showDialog'
|
|
import { close } from '../helpers/closeDialog'
|
|
import { oncreate as onCreateDialog } from '../helpers/onCreateDialog'
|
|
import { define } from 'remount/es6'
|
|
import LoadingSpinner from '../../../_components/LoadingSpinner.html'
|
|
import { on } from '../../../_utils/eventBus'
|
|
import { createEmojiMartPicker } from '../../../_react/createEmojiMartPicker'
|
|
|
|
// Consistency with Mastodon FE, taken from
|
|
// app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js
|
|
const categoriesSort = [
|
|
'recent',
|
|
'custom',
|
|
'people',
|
|
'nature',
|
|
'foods',
|
|
'activity',
|
|
'places',
|
|
'objects',
|
|
'symbols',
|
|
'flags'
|
|
]
|
|
|
|
export default {
|
|
async oncreate () {
|
|
onCreateDialog.call(this)
|
|
on('emoji-selected', this, emoji => this.onEmojiSelected(emoji))
|
|
try {
|
|
let Picker = await createEmojiMartPicker()
|
|
if (!customElements.get('emoji-mart')) {
|
|
define({ 'emoji-mart': Picker })
|
|
}
|
|
this.set({ loaded: true })
|
|
this.focusIfNecessary()
|
|
} catch (error) {
|
|
this.set({ error }) // should never happen, but you never know
|
|
}
|
|
},
|
|
components: {
|
|
ModalDialog,
|
|
LoadingSpinner
|
|
},
|
|
store: () => store,
|
|
data: () => ({
|
|
loading: true,
|
|
loaded: false,
|
|
error: void 0
|
|
}),
|
|
computed: {
|
|
// try to estimate size of emoji-mart based on mobile vs desktop
|
|
style: ({ $isSmallMobileSize }) => (`
|
|
min-width: ${$isSmallMobileSize ? 250 : 300}px;
|
|
min-height: ${$isSmallMobileSize ? 300 : 400}px;
|
|
`),
|
|
emojiMartPropsJson: ({ emojiMartProps }) => JSON.stringify(emojiMartProps),
|
|
emojiMartProps: ({ perLine, custom }) => ({
|
|
perLine,
|
|
custom,
|
|
color: 'var(--nav-bg)',
|
|
emoji: 'sailboat',
|
|
title: 'Emoji',
|
|
include: categoriesSort,
|
|
showPreview: true
|
|
}),
|
|
perLine: ({ $isSmallMobileSize, $isTinyMobileSize, $isMobileSize }) => (
|
|
$isTinyMobileSize
|
|
? 7
|
|
: $isSmallMobileSize
|
|
? 8
|
|
: $isMobileSize
|
|
? 9
|
|
: 10
|
|
),
|
|
custom: ({ $currentCustomEmoji, $autoplayGifs }) => {
|
|
if (!$currentCustomEmoji) {
|
|
return []
|
|
}
|
|
return $currentCustomEmoji.filter(emoji => emoji.visible_in_picker).map(emoji => ({
|
|
name: emoji.shortcode,
|
|
short_names: [emoji.shortcode],
|
|
text: `:${emoji.shortcode}:`,
|
|
emoticons: [],
|
|
keywords: [emoji.shortcode],
|
|
imageUrl: $autoplayGifs ? emoji.url : emoji.static_url
|
|
}))
|
|
},
|
|
// it's jarring on mobile if the emoji picker automatically pops open the keyboard
|
|
autoFocus: ({ $isUserTouching }) => !$isUserTouching
|
|
},
|
|
methods: {
|
|
show,
|
|
close,
|
|
onEmojiSelected (emoji) {
|
|
let { realm } = this.get()
|
|
insertEmoji(realm, emoji)
|
|
this.close()
|
|
},
|
|
focusIfNecessary () {
|
|
let { autoFocus } = this.get()
|
|
if (!autoFocus) {
|
|
return
|
|
}
|
|
requestAnimationFrame(() => {
|
|
let container = this.refs.container
|
|
if (container) {
|
|
let searchInput = container.querySelector('emoji-mart .emoji-mart-search input')
|
|
if (searchInput) {
|
|
// do this manually because emoji-mart's built in autofocus doesn't work consistently
|
|
searchInput.focus()
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
</script>
|