feat: show emoji tooltip (#2485)
This commit is contained in:
parent
74138a9a58
commit
c0bb6e293c
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -2,6 +2,7 @@ node_modules
|
|||
*.log
|
||||
dist
|
||||
.output
|
||||
.pnpm-store
|
||||
.nuxt
|
||||
.env
|
||||
.DS_Store
|
||||
|
|
30
components/emoji/Emoji.vue
Normal file
30
components/emoji/Emoji.vue
Normal file
|
@ -0,0 +1,30 @@
|
|||
<script setup lang="ts">
|
||||
const { as, alt, dataEmojiId } = $defineProps<{
|
||||
as: string
|
||||
alt?: string
|
||||
dataEmojiId?: string
|
||||
}>()
|
||||
|
||||
let title = $ref<string | undefined>()
|
||||
|
||||
if (alt) {
|
||||
if (alt.startsWith(':')) {
|
||||
title = alt.replace(/:/g, '')
|
||||
}
|
||||
else {
|
||||
import('node-emoji').then(({ find }) => {
|
||||
title = find(alt)?.key.replace(/_/g, ' ')
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// if it has a data-emoji-id, use that as the title instead
|
||||
if (dataEmojiId)
|
||||
title = dataEmojiId
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<component :is="as" v-bind="$attrs" :alt="alt" :data-emoji-id="dataEmojiId" :title="title">
|
||||
<slot />
|
||||
</component>
|
||||
</template>
|
|
@ -6,6 +6,7 @@ import { RouterLink } from 'vue-router'
|
|||
import { decode } from 'tiny-decode'
|
||||
import type { ContentParseOptions } from './content-parse'
|
||||
import { parseMastodonHTML } from './content-parse'
|
||||
import Emoji from '~/components/emoji/Emoji.vue'
|
||||
import ContentCode from '~/components/content/ContentCode.vue'
|
||||
import ContentMentionGroup from '~/components/content/ContentMentionGroup.vue'
|
||||
import AccountHoverWrapper from '~/components/account/AccountHoverWrapper.vue'
|
||||
|
@ -19,8 +20,11 @@ function getTextualAstComponents(astChildren: Node[]): string {
|
|||
}
|
||||
|
||||
/**
|
||||
* Raw HTML to VNodes
|
||||
*/
|
||||
* Raw HTML to VNodes.
|
||||
*
|
||||
* @param content HTML content.
|
||||
* @param options Options.
|
||||
*/
|
||||
export function contentToVNode(
|
||||
content: string,
|
||||
options?: ContentParseOptions,
|
||||
|
@ -43,6 +47,17 @@ export function nodeToVNode(node: Node): VNode | string | null {
|
|||
if (node.name === 'mention-group')
|
||||
return h(ContentMentionGroup, node.attributes, () => node.children.map(treeToVNode))
|
||||
|
||||
// add tooltip to emojis
|
||||
if (node.name === 'picture' || (node.name === 'img' && node.attributes?.alt)) {
|
||||
const props = node.attributes ?? {}
|
||||
props.as = node.name
|
||||
return h(
|
||||
Emoji,
|
||||
props,
|
||||
() => node.children.map(treeToVNode),
|
||||
)
|
||||
}
|
||||
|
||||
if ('children' in node) {
|
||||
if (node.name === 'a' && (node.attributes.href?.startsWith('/') || node.attributes.href?.startsWith('.'))) {
|
||||
node.attributes.to = node.attributes.href
|
||||
|
|
|
@ -79,6 +79,7 @@
|
|||
"js-yaml": "^4.1.0",
|
||||
"lru-cache": "^10.0.0",
|
||||
"masto": "^5.11.3",
|
||||
"node-emoji": "^2.1.3",
|
||||
"nuxt-security": "^0.13.1",
|
||||
"page-lifecycle": "^0.1.2",
|
||||
"pinia": "^2.1.4",
|
||||
|
|
|
@ -164,6 +164,9 @@ importers:
|
|||
masto:
|
||||
specifier: ^5.11.3
|
||||
version: 5.11.3
|
||||
node-emoji:
|
||||
specifier: ^2.1.3
|
||||
version: 2.1.3
|
||||
nuxt-security:
|
||||
specifier: ^0.13.1
|
||||
version: 0.13.1(patch_hash=bd6cmp7ukwwiwrxafbbotwkihe)(rollup@2.79.1)
|
||||
|
@ -4141,6 +4144,11 @@ packages:
|
|||
/@sinclair/typebox@0.27.8:
|
||||
resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
|
||||
|
||||
/@sindresorhus/is@4.6.0:
|
||||
resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==}
|
||||
engines: {node: '>=10'}
|
||||
dev: false
|
||||
|
||||
/@sindresorhus/merge-streams@1.0.0:
|
||||
resolution: {integrity: sha512-rUV5WyJrJLoloD4NDN1V1+LDMDWOa4OTsT4yYJwQNpTU6FWxkxHpL7eu4w+DmiH8x/EAM1otkPE1+LaspIbplw==}
|
||||
engines: {node: '>=18'}
|
||||
|
@ -6865,6 +6873,11 @@ packages:
|
|||
snake-case: 3.0.4
|
||||
tslib: 2.6.0
|
||||
|
||||
/char-regex@1.0.2:
|
||||
resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==}
|
||||
engines: {node: '>=10'}
|
||||
dev: false
|
||||
|
||||
/char-regex@2.0.1:
|
||||
resolution: {integrity: sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw==}
|
||||
engines: {node: '>=12.20'}
|
||||
|
@ -7573,6 +7586,10 @@ packages:
|
|||
/emoji-regex@9.2.2:
|
||||
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
|
||||
|
||||
/emojilib@2.4.0:
|
||||
resolution: {integrity: sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==}
|
||||
dev: false
|
||||
|
||||
/emoticon@4.0.1:
|
||||
resolution: {integrity: sha512-dqx7eA9YaqyvYtUhJwT4rC1HIp82j5ybS1/vQ42ur+jBe17dJMwZE4+gvL1XadSFfxaPFFGt3Xsw+Y8akThDlw==}
|
||||
dev: true
|
||||
|
@ -10773,6 +10790,16 @@ packages:
|
|||
lodash: 4.17.21
|
||||
dev: true
|
||||
|
||||
/node-emoji@2.1.3:
|
||||
resolution: {integrity: sha512-E2WEOVsgs7O16zsURJ/eH8BqhF029wGpEOnv7Urwdo2wmQanOACwJQh0devF9D9RhoZru0+9JXIS0dBXIAz+lA==}
|
||||
engines: {node: '>=18'}
|
||||
dependencies:
|
||||
'@sindresorhus/is': 4.6.0
|
||||
char-regex: 1.0.2
|
||||
emojilib: 2.4.0
|
||||
skin-tone: 2.0.0
|
||||
dev: false
|
||||
|
||||
/node-fetch-native@1.4.1:
|
||||
resolution: {integrity: sha512-NsXBU0UgBxo2rQLOeWNZqS3fvflWePMECr8CoSWoSTqCqGbVVsvl9vZu1HfQicYN0g5piV9Gh8RTEvo/uP752w==}
|
||||
|
||||
|
@ -13007,6 +13034,13 @@ packages:
|
|||
/sisteransi@1.0.5:
|
||||
resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
|
||||
|
||||
/skin-tone@2.0.0:
|
||||
resolution: {integrity: sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
unicode-emoji-modifier-base: 1.0.0
|
||||
dev: false
|
||||
|
||||
/slash@3.0.0:
|
||||
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
|
||||
engines: {node: '>=8'}
|
||||
|
@ -13837,6 +13871,11 @@ packages:
|
|||
engines: {node: '>=4'}
|
||||
dev: false
|
||||
|
||||
/unicode-emoji-modifier-base@1.0.0:
|
||||
resolution: {integrity: sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==}
|
||||
engines: {node: '>=4'}
|
||||
dev: false
|
||||
|
||||
/unicode-match-property-ecmascript@2.0.0:
|
||||
resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==}
|
||||
engines: {node: '>=4'}
|
||||
|
|
|
@ -58,7 +58,7 @@ exports[`content-rich > code frame no lang 1`] = `"<p><pre class="code-block">he
|
|||
|
||||
exports[`content-rich > custom emoji 1`] = `
|
||||
"Daniel Roe
|
||||
<picture alt=":nuxt:" class="custom-emoji" data-emoji-id="nuxt"
|
||||
<picture class="custom-emoji" alt=":nuxt:" data-emoji-id="nuxt" title="nuxt"
|
||||
><source
|
||||
srcset="
|
||||
https://media.webtoo.ls/custom_emojis/images/000/000/366/original/73330dfc9dda4078.png
|
||||
|
@ -67,6 +67,7 @@ exports[`content-rich > custom emoji 1`] = `
|
|||
<img
|
||||
src="https://media.webtoo.ls/custom_emojis/images/000/000/366/original/73330dfc9dda4078.png"
|
||||
alt=":nuxt:"
|
||||
title="nuxt"
|
||||
/></picture>
|
||||
"
|
||||
`;
|
||||
|
@ -129,8 +130,8 @@ exports[`content-rich > link + mention 1`] = `
|
|||
Happy
|
||||
<img
|
||||
src="/emojis/twemoji/1f917.svg"
|
||||
alt="🤗"
|
||||
class="iconify-emoji iconify-emoji--twemoji"
|
||||
alt="🤗"
|
||||
/>
|
||||
we’re now using
|
||||
<span class="h-card"
|
||||
|
|
Loading…
Reference in a new issue