@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.
Files changed (41) hide show
  1. package/lib/_chunks-cjs/editor-provider.cjs +22 -11
  2. package/lib/_chunks-cjs/editor-provider.cjs.map +1 -1
  3. package/lib/_chunks-cjs/util.merge-text-blocks.cjs +2 -1
  4. package/lib/_chunks-cjs/util.merge-text-blocks.cjs.map +1 -1
  5. package/lib/_chunks-cjs/util.slice-blocks.cjs +26 -8
  6. package/lib/_chunks-cjs/util.slice-blocks.cjs.map +1 -1
  7. package/lib/_chunks-es/editor-provider.js +22 -11
  8. package/lib/_chunks-es/editor-provider.js.map +1 -1
  9. package/lib/_chunks-es/util.merge-text-blocks.js +2 -1
  10. package/lib/_chunks-es/util.merge-text-blocks.js.map +1 -1
  11. package/lib/_chunks-es/util.slice-blocks.js +26 -8
  12. package/lib/_chunks-es/util.slice-blocks.js.map +1 -1
  13. package/lib/index.cjs +155 -158
  14. package/lib/index.cjs.map +1 -1
  15. package/lib/index.js +160 -163
  16. package/lib/index.js.map +1 -1
  17. package/package.json +6 -6
  18. package/src/behaviors/behavior.abstract.split.ts +2 -2
  19. package/src/converters/converter.portable-text.ts +1 -0
  20. package/src/converters/converter.text-html.ts +1 -0
  21. package/src/converters/converter.text-plain.ts +1 -0
  22. package/src/editor/Editable.tsx +20 -48
  23. package/src/editor/__tests__/PortableTextEditor.test.tsx +3 -3
  24. package/src/editor/__tests__/RangeDecorations.test.tsx +2 -2
  25. package/src/editor/components/render-block-object.tsx +0 -1
  26. package/src/editor/components/render-element.tsx +3 -3
  27. package/src/editor/components/render-inline-object.tsx +9 -12
  28. package/src/editor/components/render-leaf.tsx +64 -0
  29. package/src/editor/components/render-span.tsx +260 -0
  30. package/src/editor/components/render-text-block.tsx +0 -1
  31. package/src/editor/components/render-text.tsx +18 -0
  32. package/src/internal-utils/parse-blocks.test.ts +20 -20
  33. package/src/internal-utils/parse-blocks.ts +57 -20
  34. package/src/operations/behavior.operation.annotation.add.ts +1 -1
  35. package/src/operations/behavior.operation.block.set.ts +1 -1
  36. package/src/operations/behavior.operation.block.unset.ts +2 -2
  37. package/src/operations/behavior.operation.insert-inline-object.ts +1 -1
  38. package/src/operations/behavior.operation.insert.block.ts +1 -1
  39. package/src/selectors/selector.get-trimmed-selection.test.ts +1 -0
  40. package/src/utils/util.merge-text-blocks.ts +1 -1
  41. 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'