pinafore/src/routes/_components/dialog/components/EmojiDialog.html

177 lines
4.9 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 })
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()
}
}
})
} 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,
autoFocus: 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
}))
}
},
methods: {
show,
close,
onEmojiSelected (emoji) {
let { realm } = this.get()
insertEmoji(realm, emoji)
this.close()
}
}
}
</script>