/
Leaf.tsx
128 lines (122 loc) · 4.12 KB
/
Leaf.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import React, {ReactElement} from 'react'
import {Range, Text} from 'slate'
import {RenderLeafProps, useSelected, useSlateStatic} from '@sanity/slate-react'
import {uniq} from 'lodash'
import {PortableTextObject, PortableTextTextBlock} from '@sanity/types'
import {
RenderChildFunction,
PortableTextMemberSchemaTypes,
RenderAnnotationFunction,
RenderDecoratorFunction,
} from '../types/editor'
import {debugWithName} from '../utils/debug'
import {DefaultAnnotation} from './nodes/DefaultAnnotation'
import {DraggableChild} from './DraggableChild'
const debug = debugWithName('components:Leaf')
const debugRenders = false
interface LeafProps extends RenderLeafProps {
children: ReactElement
keyGenerator: () => string
schemaTypes: PortableTextMemberSchemaTypes
renderAnnotation?: RenderAnnotationFunction
renderChild?: RenderChildFunction
renderDecorator?: RenderDecoratorFunction
readOnly: boolean
}
export const Leaf = (props: LeafProps) => {
const editor = useSlateStatic()
const selected = useSelected()
const {attributes, children, leaf, schemaTypes, keyGenerator, renderChild, readOnly} = props
const spanRef = React.useRef(null)
let returnedChildren = children
const focused = (selected && editor.selection && Range.isCollapsed(editor.selection)) || false
// Render text nodes
if (Text.isText(leaf) && leaf._type === schemaTypes.span.name) {
const block = children.props.parent as PortableTextTextBlock | undefined
const path = block ? [{_key: block._key}, 'children', {_key: leaf._key}] : []
const decoratorValues = schemaTypes.decorators.map((dec) => dec.value)
const marks: string[] = uniq(
(Array.isArray(leaf.marks) ? leaf.marks : []).filter((mark) => decoratorValues.includes(mark))
)
marks.forEach((mark) => {
const type = schemaTypes.decorators.find((dec) => dec.value === mark)
if (type && props.renderDecorator) {
returnedChildren = props.renderDecorator({
children: returnedChildren,
editorElementRef: spanRef,
focused,
path,
selected,
type,
value: mark,
})
}
})
const annotationMarks = Array.isArray(leaf.marks) ? leaf.marks : []
const annotations = annotationMarks
.map(
(mark) =>
!decoratorValues.includes(mark) &&
block &&
block.markDefs &&
block.markDefs.find((def) => def._key === mark)
)
.filter(Boolean) as PortableTextObject[]
if (block && annotations.length > 0) {
annotations.forEach((annotation) => {
const type = schemaTypes.annotations.find((t) => t.name === annotation._type)
if (type) {
if (props.renderAnnotation) {
returnedChildren = (
<span ref={spanRef}>
{props.renderAnnotation({
block,
children: returnedChildren,
editorElementRef: spanRef,
focused,
path,
selected,
type,
value: annotation,
})}
</span>
)
} else {
returnedChildren = (
<DefaultAnnotation annotation={annotation}>
<span ref={spanRef}>{returnedChildren}</span>
</DefaultAnnotation>
)
}
}
})
}
if (block && renderChild) {
const child = block.children.find((_child) => _child._key === leaf._key) // Ensure object equality
if (child) {
const defaultRendered = <>{returnedChildren}</>
returnedChildren = renderChild({
children: defaultRendered,
value: child,
schemaType: schemaTypes.span,
focused,
selected,
path,
annotations,
editorElementRef: spanRef,
})
}
}
}
if (debugRenders) {
debug(`Render ${leaf._key} (span)`)
}
const key = leaf._key || keyGenerator()
return (
<span key={key} {...attributes} ref={spanRef}>
<DraggableChild element={leaf} readOnly={readOnly}>
{returnedChildren}
</DraggableChild>
</span>
)
}