@portabletext/editor 1.49.0 → 1.49.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/_chunks-cjs/editor-provider.cjs +22 -11
- package/lib/_chunks-cjs/editor-provider.cjs.map +1 -1
- package/lib/_chunks-cjs/util.merge-text-blocks.cjs +2 -1
- package/lib/_chunks-cjs/util.merge-text-blocks.cjs.map +1 -1
- package/lib/_chunks-cjs/util.slice-blocks.cjs +26 -8
- package/lib/_chunks-cjs/util.slice-blocks.cjs.map +1 -1
- package/lib/_chunks-es/editor-provider.js +22 -11
- package/lib/_chunks-es/editor-provider.js.map +1 -1
- package/lib/_chunks-es/util.merge-text-blocks.js +2 -1
- package/lib/_chunks-es/util.merge-text-blocks.js.map +1 -1
- package/lib/_chunks-es/util.slice-blocks.js +26 -8
- package/lib/_chunks-es/util.slice-blocks.js.map +1 -1
- package/lib/index.cjs +155 -158
- package/lib/index.cjs.map +1 -1
- package/lib/index.js +160 -163
- package/lib/index.js.map +1 -1
- package/package.json +6 -6
- package/src/behaviors/behavior.abstract.split.ts +2 -2
- package/src/converters/converter.portable-text.ts +1 -0
- package/src/converters/converter.text-html.ts +1 -0
- package/src/converters/converter.text-plain.ts +1 -0
- package/src/editor/Editable.tsx +20 -48
- package/src/editor/__tests__/PortableTextEditor.test.tsx +3 -3
- package/src/editor/__tests__/RangeDecorations.test.tsx +2 -2
- package/src/editor/components/render-block-object.tsx +0 -1
- package/src/editor/components/render-element.tsx +3 -3
- package/src/editor/components/render-inline-object.tsx +9 -12
- package/src/editor/components/render-leaf.tsx +64 -0
- package/src/editor/components/render-span.tsx +260 -0
- package/src/editor/components/render-text-block.tsx +0 -1
- package/src/editor/components/render-text.tsx +18 -0
- package/src/internal-utils/parse-blocks.test.ts +20 -20
- package/src/internal-utils/parse-blocks.ts +57 -20
- package/src/operations/behavior.operation.annotation.add.ts +1 -1
- package/src/operations/behavior.operation.block.set.ts +1 -1
- package/src/operations/behavior.operation.block.unset.ts +2 -2
- package/src/operations/behavior.operation.insert-inline-object.ts +1 -1
- package/src/operations/behavior.operation.insert.block.ts +1 -1
- package/src/selectors/selector.get-trimmed-selection.test.ts +1 -0
- package/src/utils/util.merge-text-blocks.ts +1 -1
- package/src/editor/components/Leaf.tsx +0 -336
|
@@ -1,336 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
Path,
|
|
3
|
-
PortableTextObject,
|
|
4
|
-
PortableTextTextBlock,
|
|
5
|
-
} from '@sanity/types'
|
|
6
|
-
import {isEqual, uniq} from 'lodash'
|
|
7
|
-
import {
|
|
8
|
-
startTransition,
|
|
9
|
-
useCallback,
|
|
10
|
-
useEffect,
|
|
11
|
-
useMemo,
|
|
12
|
-
useRef,
|
|
13
|
-
useState,
|
|
14
|
-
type ReactElement,
|
|
15
|
-
} from 'react'
|
|
16
|
-
import {Text} from 'slate'
|
|
17
|
-
import {useSelected, type RenderLeafProps} from 'slate-react'
|
|
18
|
-
import {debugWithName} from '../../internal-utils/debug'
|
|
19
|
-
import type {
|
|
20
|
-
BlockAnnotationRenderProps,
|
|
21
|
-
BlockChildRenderProps,
|
|
22
|
-
BlockDecoratorRenderProps,
|
|
23
|
-
PortableTextMemberSchemaTypes,
|
|
24
|
-
RenderAnnotationFunction,
|
|
25
|
-
RenderChildFunction,
|
|
26
|
-
RenderDecoratorFunction,
|
|
27
|
-
} from '../../types/editor'
|
|
28
|
-
import type {EditorActor} from '../editor-machine'
|
|
29
|
-
import {usePortableTextEditor} from '../hooks/usePortableTextEditor'
|
|
30
|
-
import {PortableTextEditor} from '../PortableTextEditor'
|
|
31
|
-
|
|
32
|
-
const debug = debugWithName('components:Leaf')
|
|
33
|
-
|
|
34
|
-
const EMPTY_MARKS: string[] = []
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* @internal
|
|
38
|
-
*/
|
|
39
|
-
export interface LeafProps extends RenderLeafProps {
|
|
40
|
-
editorActor: EditorActor
|
|
41
|
-
children: ReactElement<any>
|
|
42
|
-
schemaTypes: PortableTextMemberSchemaTypes
|
|
43
|
-
renderAnnotation?: RenderAnnotationFunction
|
|
44
|
-
renderChild?: RenderChildFunction
|
|
45
|
-
renderDecorator?: RenderDecoratorFunction
|
|
46
|
-
readOnly: boolean
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Renders Portable Text span nodes in Slate
|
|
51
|
-
* @internal
|
|
52
|
-
*/
|
|
53
|
-
export const Leaf = (props: LeafProps) => {
|
|
54
|
-
const {
|
|
55
|
-
editorActor,
|
|
56
|
-
attributes,
|
|
57
|
-
children,
|
|
58
|
-
leaf,
|
|
59
|
-
schemaTypes,
|
|
60
|
-
renderChild,
|
|
61
|
-
renderDecorator,
|
|
62
|
-
renderAnnotation,
|
|
63
|
-
} = props
|
|
64
|
-
const spanRef = useRef<HTMLElement>(null)
|
|
65
|
-
const portableTextEditor = usePortableTextEditor()
|
|
66
|
-
const blockSelected = useSelected()
|
|
67
|
-
const [focused, setFocused] = useState(false)
|
|
68
|
-
const [selected, setSelected] = useState(false)
|
|
69
|
-
const block = children.props.parent as PortableTextTextBlock | undefined
|
|
70
|
-
const path: Path = useMemo(
|
|
71
|
-
() => (block ? [{_key: block?._key}, 'children', {_key: leaf._key}] : []),
|
|
72
|
-
[block, leaf._key],
|
|
73
|
-
)
|
|
74
|
-
const decoratorValues = useMemo(
|
|
75
|
-
() => schemaTypes.decorators.map((dec) => dec.value),
|
|
76
|
-
[schemaTypes.decorators],
|
|
77
|
-
)
|
|
78
|
-
const marks: string[] = useMemo(
|
|
79
|
-
() =>
|
|
80
|
-
uniq(
|
|
81
|
-
(leaf.marks || EMPTY_MARKS).filter((mark) =>
|
|
82
|
-
decoratorValues.includes(mark),
|
|
83
|
-
),
|
|
84
|
-
),
|
|
85
|
-
[decoratorValues, leaf.marks],
|
|
86
|
-
)
|
|
87
|
-
const annotationMarks = Array.isArray(leaf.marks) ? leaf.marks : EMPTY_MARKS
|
|
88
|
-
const annotations = useMemo(
|
|
89
|
-
() =>
|
|
90
|
-
annotationMarks
|
|
91
|
-
.map(
|
|
92
|
-
(mark) =>
|
|
93
|
-
!decoratorValues.includes(mark) &&
|
|
94
|
-
block?.markDefs?.find((def) => def._key === mark),
|
|
95
|
-
)
|
|
96
|
-
.filter(Boolean) as PortableTextObject[],
|
|
97
|
-
[annotationMarks, block, decoratorValues],
|
|
98
|
-
)
|
|
99
|
-
|
|
100
|
-
const shouldTrackSelectionAndFocus = annotations.length > 0 && blockSelected
|
|
101
|
-
|
|
102
|
-
useEffect(() => {
|
|
103
|
-
if (!shouldTrackSelectionAndFocus) {
|
|
104
|
-
setFocused(false)
|
|
105
|
-
return
|
|
106
|
-
}
|
|
107
|
-
const sel = PortableTextEditor.getSelection(portableTextEditor)
|
|
108
|
-
if (
|
|
109
|
-
sel &&
|
|
110
|
-
isEqual(sel.focus.path, path) &&
|
|
111
|
-
PortableTextEditor.isCollapsedSelection(portableTextEditor)
|
|
112
|
-
) {
|
|
113
|
-
startTransition(() => {
|
|
114
|
-
setFocused(true)
|
|
115
|
-
})
|
|
116
|
-
}
|
|
117
|
-
}, [shouldTrackSelectionAndFocus, path, portableTextEditor])
|
|
118
|
-
|
|
119
|
-
// Function to check if this leaf is currently inside the user's text selection
|
|
120
|
-
const setSelectedFromRange = useCallback(() => {
|
|
121
|
-
if (!shouldTrackSelectionAndFocus) {
|
|
122
|
-
return
|
|
123
|
-
}
|
|
124
|
-
debug('Setting selection and focus from range')
|
|
125
|
-
const winSelection = window.getSelection()
|
|
126
|
-
if (!winSelection) {
|
|
127
|
-
setSelected(false)
|
|
128
|
-
return
|
|
129
|
-
}
|
|
130
|
-
if (winSelection && winSelection.rangeCount > 0) {
|
|
131
|
-
const range = winSelection.getRangeAt(0)
|
|
132
|
-
if (spanRef.current && range.intersectsNode(spanRef.current)) {
|
|
133
|
-
setSelected(true)
|
|
134
|
-
} else {
|
|
135
|
-
setSelected(false)
|
|
136
|
-
}
|
|
137
|
-
} else {
|
|
138
|
-
setSelected(false)
|
|
139
|
-
}
|
|
140
|
-
}, [shouldTrackSelectionAndFocus])
|
|
141
|
-
|
|
142
|
-
useEffect(() => {
|
|
143
|
-
if (!shouldTrackSelectionAndFocus) {
|
|
144
|
-
return undefined
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const onBlur = editorActor.on('blurred', () => {
|
|
148
|
-
setFocused(false)
|
|
149
|
-
setSelected(false)
|
|
150
|
-
})
|
|
151
|
-
|
|
152
|
-
const onFocus = editorActor.on('focused', () => {
|
|
153
|
-
const sel = PortableTextEditor.getSelection(portableTextEditor)
|
|
154
|
-
if (
|
|
155
|
-
sel &&
|
|
156
|
-
isEqual(sel.focus.path, path) &&
|
|
157
|
-
PortableTextEditor.isCollapsedSelection(portableTextEditor)
|
|
158
|
-
) {
|
|
159
|
-
setFocused(true)
|
|
160
|
-
}
|
|
161
|
-
setSelectedFromRange()
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
const onSelection = editorActor.on('selection', (event) => {
|
|
165
|
-
if (
|
|
166
|
-
event.selection &&
|
|
167
|
-
isEqual(event.selection.focus.path, path) &&
|
|
168
|
-
PortableTextEditor.isCollapsedSelection(portableTextEditor)
|
|
169
|
-
) {
|
|
170
|
-
setFocused(true)
|
|
171
|
-
} else {
|
|
172
|
-
setFocused(false)
|
|
173
|
-
}
|
|
174
|
-
setSelectedFromRange()
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
return () => {
|
|
178
|
-
onBlur.unsubscribe()
|
|
179
|
-
onFocus.unsubscribe()
|
|
180
|
-
onSelection.unsubscribe()
|
|
181
|
-
}
|
|
182
|
-
}, [
|
|
183
|
-
editorActor,
|
|
184
|
-
path,
|
|
185
|
-
portableTextEditor,
|
|
186
|
-
setSelectedFromRange,
|
|
187
|
-
shouldTrackSelectionAndFocus,
|
|
188
|
-
])
|
|
189
|
-
|
|
190
|
-
useEffect(() => setSelectedFromRange(), [setSelectedFromRange])
|
|
191
|
-
|
|
192
|
-
const content = useMemo(() => {
|
|
193
|
-
let returnedChildren = children
|
|
194
|
-
// Render text nodes
|
|
195
|
-
if (Text.isText(leaf) && leaf._type === schemaTypes.span.name) {
|
|
196
|
-
marks.forEach((mark) => {
|
|
197
|
-
const schemaType = schemaTypes.decorators.find(
|
|
198
|
-
(dec) => dec.value === mark,
|
|
199
|
-
)
|
|
200
|
-
if (schemaType && renderDecorator) {
|
|
201
|
-
const _props: Omit<BlockDecoratorRenderProps, 'type'> =
|
|
202
|
-
Object.defineProperty(
|
|
203
|
-
{
|
|
204
|
-
children: returnedChildren,
|
|
205
|
-
editorElementRef: spanRef,
|
|
206
|
-
focused,
|
|
207
|
-
path,
|
|
208
|
-
selected,
|
|
209
|
-
schemaType,
|
|
210
|
-
value: mark,
|
|
211
|
-
},
|
|
212
|
-
'type',
|
|
213
|
-
{
|
|
214
|
-
enumerable: false,
|
|
215
|
-
get() {
|
|
216
|
-
console.warn(
|
|
217
|
-
"Property 'type' is deprecated, use 'schemaType' instead.",
|
|
218
|
-
)
|
|
219
|
-
return schemaType
|
|
220
|
-
},
|
|
221
|
-
},
|
|
222
|
-
)
|
|
223
|
-
returnedChildren = renderDecorator(
|
|
224
|
-
_props as BlockDecoratorRenderProps,
|
|
225
|
-
)
|
|
226
|
-
}
|
|
227
|
-
})
|
|
228
|
-
|
|
229
|
-
if (block && annotations.length > 0) {
|
|
230
|
-
annotations.forEach((annotation) => {
|
|
231
|
-
const schemaType = schemaTypes.annotations.find(
|
|
232
|
-
(t) => t.name === annotation._type,
|
|
233
|
-
)
|
|
234
|
-
if (schemaType) {
|
|
235
|
-
if (renderAnnotation) {
|
|
236
|
-
const _props: Omit<BlockAnnotationRenderProps, 'type'> =
|
|
237
|
-
Object.defineProperty(
|
|
238
|
-
{
|
|
239
|
-
block,
|
|
240
|
-
children: returnedChildren,
|
|
241
|
-
editorElementRef: spanRef,
|
|
242
|
-
focused,
|
|
243
|
-
path,
|
|
244
|
-
selected,
|
|
245
|
-
schemaType,
|
|
246
|
-
value: annotation,
|
|
247
|
-
},
|
|
248
|
-
'type',
|
|
249
|
-
{
|
|
250
|
-
enumerable: false,
|
|
251
|
-
get() {
|
|
252
|
-
console.warn(
|
|
253
|
-
"Property 'type' is deprecated, use 'schemaType' instead.",
|
|
254
|
-
)
|
|
255
|
-
return schemaType
|
|
256
|
-
},
|
|
257
|
-
},
|
|
258
|
-
)
|
|
259
|
-
|
|
260
|
-
returnedChildren = (
|
|
261
|
-
<span ref={spanRef}>
|
|
262
|
-
{renderAnnotation(_props as BlockAnnotationRenderProps)}
|
|
263
|
-
</span>
|
|
264
|
-
)
|
|
265
|
-
} else {
|
|
266
|
-
returnedChildren = <span ref={spanRef}>{returnedChildren}</span>
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
})
|
|
270
|
-
}
|
|
271
|
-
if (block && renderChild) {
|
|
272
|
-
const child = block.children.find((_child) => _child._key === leaf._key) // Ensure object equality
|
|
273
|
-
if (child) {
|
|
274
|
-
const defaultRendered = <>{returnedChildren}</>
|
|
275
|
-
const _props: Omit<BlockChildRenderProps, 'type'> =
|
|
276
|
-
Object.defineProperty(
|
|
277
|
-
{
|
|
278
|
-
annotations,
|
|
279
|
-
children: defaultRendered,
|
|
280
|
-
editorElementRef: spanRef,
|
|
281
|
-
focused,
|
|
282
|
-
path,
|
|
283
|
-
schemaType: schemaTypes.span,
|
|
284
|
-
selected,
|
|
285
|
-
value: child,
|
|
286
|
-
},
|
|
287
|
-
'type',
|
|
288
|
-
{
|
|
289
|
-
enumerable: false,
|
|
290
|
-
get() {
|
|
291
|
-
console.warn(
|
|
292
|
-
"Property 'type' is deprecated, use 'schemaType' instead.",
|
|
293
|
-
)
|
|
294
|
-
return schemaTypes.span
|
|
295
|
-
},
|
|
296
|
-
},
|
|
297
|
-
)
|
|
298
|
-
returnedChildren = renderChild(_props as BlockChildRenderProps)
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
return returnedChildren
|
|
303
|
-
}, [
|
|
304
|
-
annotations,
|
|
305
|
-
block,
|
|
306
|
-
children,
|
|
307
|
-
focused,
|
|
308
|
-
leaf,
|
|
309
|
-
marks,
|
|
310
|
-
path,
|
|
311
|
-
renderAnnotation,
|
|
312
|
-
renderChild,
|
|
313
|
-
renderDecorator,
|
|
314
|
-
schemaTypes.annotations,
|
|
315
|
-
schemaTypes.decorators,
|
|
316
|
-
schemaTypes.span,
|
|
317
|
-
selected,
|
|
318
|
-
])
|
|
319
|
-
return useMemo(
|
|
320
|
-
() => (
|
|
321
|
-
<span
|
|
322
|
-
key={leaf._key}
|
|
323
|
-
{...attributes}
|
|
324
|
-
ref={spanRef}
|
|
325
|
-
data-child-key={leaf._key}
|
|
326
|
-
data-child-name={leaf._type}
|
|
327
|
-
data-child-type="span"
|
|
328
|
-
>
|
|
329
|
-
{content}
|
|
330
|
-
</span>
|
|
331
|
-
),
|
|
332
|
-
[leaf, attributes, content],
|
|
333
|
-
)
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
Leaf.displayName = 'Leaf'
|