Skip to content

Commit

Permalink
feat: add emoji node
Browse files Browse the repository at this point in the history
  • Loading branch information
madebyfabian committed Sep 8, 2023
1 parent d8f77d6 commit 53f7ca1
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 3 deletions.
16 changes: 15 additions & 1 deletion packages/storyblok-rich-text-types/src/nodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export enum NodeTypes {
HR = 'horizontal_rule',
BR = 'hard_break',
IMAGE = 'image',
EMOJI = 'emoji',
// Marks
BOLD = 'bold',
STRONG = 'strong',
Expand Down Expand Up @@ -43,6 +44,7 @@ export const blockNodeTypes = [
NodeTypes.HR,
NodeTypes.BR,
NodeTypes.IMAGE,
NodeTypes.EMOJI,
]

export interface NodeAttributes {}
Expand Down Expand Up @@ -158,6 +160,17 @@ export interface ImageNode extends Node {
attrs: ImageAttributes
}

export interface EmojiAttributes extends NodeAttributes {
name: string | null
emoji: string | null
fallbackImage: string | null
}

export interface EmojiNode extends Node {
type: NodeTypes.EMOJI
attrs: EmojiAttributes
}

// Marks
export interface BoldNode extends Node {
type: NodeTypes.BOLD
Expand Down Expand Up @@ -264,6 +277,7 @@ export type BlockNodes =
| HorizontalRuleNode
| BreakNode
| ImageNode
| EmojiNode

export type MarkNodes =
| BoldNode
Expand Down Expand Up @@ -297,7 +311,7 @@ export type BlockNodesWithContent =
| ListItemNode

export type BlockNodesWithoutOptions = HorizontalRuleNode | BreakNode
export type BlockNodesWithAttributes = ImageNode
export type BlockNodesWithAttributes = ImageNode | EmojiNode
export type BlockNodesWithContentAndAttributes =
| HeadingNode
| OrderedListNode
Expand Down
26 changes: 26 additions & 0 deletions packages/storyblok-rich-text-vue-renderer/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,32 @@ const doc = shallowReactive({
},
],
},
{
type: 'paragraph',
content: [
{
text: 'And this is an emoji ',
type: 'text',
marks: [
{
type: 'textStyle',
attrs: {
color: null,
},
},
],
},
{
type: 'emoji',
attrs: {
name: 'innocent',
emoji: '😇',
fallbackImage:
'https://cdn.jsdelivr.net/npm/emoji-datasource-apple/img/apple/64/1f607.png',
},
},
],
},
{
type: 'blok',
attrs: {
Expand Down
9 changes: 7 additions & 2 deletions packages/storyblok-rich-text-vue-renderer/src/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export function createRenderer(options?: Partial<RendererOptions>) {

// With attributes only
case NodeTypes.IMAGE:
case NodeTypes.EMOJI:
return resolveBlockNodeWithAttributes(node)

default:
Expand Down Expand Up @@ -186,9 +187,11 @@ export function createRenderer(options?: Partial<RendererOptions>) {
function resolveBlockNodeWithAttributes(node: BlockNodesWithAttributes) {
const resolver = resolvers[node.type]

if (isComponentResolver(resolver)) return h(resolver, node.attrs)
if (isComponentResolver(resolver))
// @ts-expect-error Internal type mismatch
return h(resolver, node.attrs)

return resolver({ attrs: node.attrs })
return resolver({ attrs: node.attrs as never })
}

function resolveBlockNodeWithContentAndAttributes(
Expand All @@ -198,6 +201,7 @@ export function createRenderer(options?: Partial<RendererOptions>) {
const children = renderChildren(node)

if (isComponentResolver(resolver))
// @ts-expect-error Internal type mismatch
return h(resolver, node.attrs, { default: () => children })

return resolver({
Expand Down Expand Up @@ -230,6 +234,7 @@ export function createRenderer(options?: Partial<RendererOptions>) {
const resolver = resolvers[node.type]

if (isComponentResolver(resolver))
// @ts-expect-error Internal type mismatch
return h(resolver, node.attrs, { default: () => text })

return resolver({ text, attrs: node.attrs as never })
Expand Down
26 changes: 26 additions & 0 deletions packages/storyblok-rich-text-vue-renderer/src/resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
import {
type AnchorAttributes,
type CodeBlockAttributes,
type EmojiAttributes,
type HeadingAttributes,
type HighlightAttributes,
type ImageAttributes,
Expand Down Expand Up @@ -84,6 +85,7 @@ export interface Resolvers {
[NodeTypes.HR]: BlockResolver
[NodeTypes.BR]: BlockResolver
[NodeTypes.IMAGE]: BlockResolverWithAttributes<ImageAttributes>
[NodeTypes.EMOJI]: BlockResolverWithAttributes<EmojiAttributes>
// Marks
[NodeTypes.BOLD]: MarkResolver
[NodeTypes.STRONG]: MarkResolver
Expand Down Expand Up @@ -117,6 +119,30 @@ export const defaultResolvers: Resolvers = {
[NodeTypes.HR]: () => h('hr'),
[NodeTypes.BR]: () => h('br'),
[NodeTypes.IMAGE]: ({ attrs }) => h('img', attrs),
[NodeTypes.EMOJI]: ({ attrs }) => {
const props = {
'data-type': 'emoji',
'data-name': attrs.name,
emoji: attrs.emoji,
}

// TODO: Very optionated fallback, should be configurable
const fallbackProps = {
src: attrs.fallbackImage,
draggable: 'false',
loading: 'lazy',
align: 'absmiddle',
alt: attrs.name,
// Same size as font-size
style: `height: 1em; width: 1em;`,
// 1/1 Aspect ratio, so we don't cause layout shifts
height: 16,
width: 16,
}
const fallback = h('img', fallbackProps)

return h('span', props, attrs.emoji || fallback)
},
// Marks
[NodeTypes.BOLD]: ({ text }) => h('b', text),
[NodeTypes.STRONG]: ({ text }) => h('strong', text),
Expand Down
26 changes: 26 additions & 0 deletions playground/vue/src/routes/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,32 @@ const doc = shallowReactive({
},
],
},
{
type: 'paragraph',
content: [
{
text: 'And this is an emoji ',
type: 'text',
marks: [
{
type: 'textStyle',
attrs: {
color: null,
},
},
],
},
{
type: 'emoji',
attrs: {
name: 'innocent',
emoji: '😇',
fallbackImage:
'https://cdn.jsdelivr.net/npm/emoji-datasource-apple/img/apple/64/1f607.png',
},
},
],
},
{
type: 'blok',
attrs: {
Expand Down

0 comments on commit 53f7ca1

Please sign in to comment.