@portabletext/editor 1.0.19 → 1.1.0

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 (72) hide show
  1. package/lib/index.d.mts +140 -66
  2. package/lib/index.d.ts +140 -66
  3. package/lib/index.esm.js +1125 -362
  4. package/lib/index.esm.js.map +1 -1
  5. package/lib/index.js +1125 -362
  6. package/lib/index.js.map +1 -1
  7. package/lib/index.mjs +1125 -362
  8. package/lib/index.mjs.map +1 -1
  9. package/package.json +2 -2
  10. package/src/editor/Editable.tsx +107 -36
  11. package/src/editor/PortableTextEditor.tsx +47 -12
  12. package/src/editor/__tests__/PortableTextEditor.test.tsx +42 -15
  13. package/src/editor/__tests__/PortableTextEditorTester.tsx +50 -38
  14. package/src/editor/__tests__/RangeDecorations.test.tsx +0 -1
  15. package/src/editor/__tests__/handleClick.test.tsx +28 -9
  16. package/src/editor/__tests__/insert-block.test.tsx +22 -6
  17. package/src/editor/__tests__/pteWarningsSelfSolving.test.tsx +30 -62
  18. package/src/editor/__tests__/utils.ts +10 -3
  19. package/src/editor/components/DraggableBlock.tsx +36 -13
  20. package/src/editor/components/Element.tsx +59 -17
  21. package/src/editor/components/Leaf.tsx +106 -68
  22. package/src/editor/components/SlateContainer.tsx +12 -5
  23. package/src/editor/components/Synchronizer.tsx +5 -2
  24. package/src/editor/hooks/usePortableTextEditor.ts +2 -2
  25. package/src/editor/hooks/usePortableTextEditorSelection.tsx +9 -3
  26. package/src/editor/hooks/useSyncValue.test.tsx +9 -4
  27. package/src/editor/hooks/useSyncValue.ts +199 -130
  28. package/src/editor/nodes/DefaultAnnotation.tsx +6 -3
  29. package/src/editor/plugins/__tests__/createWithInsertData.test.tsx +25 -7
  30. package/src/editor/plugins/__tests__/withEditableAPIDelete.test.tsx +26 -9
  31. package/src/editor/plugins/__tests__/withEditableAPIGetFragment.test.tsx +15 -5
  32. package/src/editor/plugins/__tests__/withEditableAPIInsert.test.tsx +60 -19
  33. package/src/editor/plugins/__tests__/withEditableAPISelectionsOverlapping.test.tsx +4 -2
  34. package/src/editor/plugins/__tests__/withPortableTextLists.test.tsx +4 -2
  35. package/src/editor/plugins/__tests__/withPortableTextMarkModel.test.tsx +61 -17
  36. package/src/editor/plugins/__tests__/withPortableTextSelections.test.tsx +6 -3
  37. package/src/editor/plugins/__tests__/withUndoRedo.test.tsx +30 -13
  38. package/src/editor/plugins/createWithEditableAPI.ts +354 -124
  39. package/src/editor/plugins/createWithHotKeys.ts +41 -121
  40. package/src/editor/plugins/createWithInsertBreak.ts +166 -27
  41. package/src/editor/plugins/createWithInsertData.ts +60 -23
  42. package/src/editor/plugins/createWithMaxBlocks.ts +5 -2
  43. package/src/editor/plugins/createWithObjectKeys.ts +7 -3
  44. package/src/editor/plugins/createWithPatches.ts +60 -16
  45. package/src/editor/plugins/createWithPlaceholderBlock.ts +7 -3
  46. package/src/editor/plugins/createWithPortableTextBlockStyle.ts +17 -7
  47. package/src/editor/plugins/createWithPortableTextLists.ts +21 -8
  48. package/src/editor/plugins/createWithPortableTextMarkModel.ts +213 -46
  49. package/src/editor/plugins/createWithPortableTextSelections.ts +4 -2
  50. package/src/editor/plugins/createWithSchemaTypes.ts +25 -9
  51. package/src/editor/plugins/createWithUndoRedo.ts +107 -24
  52. package/src/editor/plugins/createWithUtils.ts +32 -10
  53. package/src/editor/plugins/index.ts +31 -10
  54. package/src/types/editor.ts +44 -15
  55. package/src/types/options.ts +4 -2
  56. package/src/types/slate.ts +2 -2
  57. package/src/utils/__tests__/dmpToOperations.test.ts +38 -13
  58. package/src/utils/__tests__/operationToPatches.test.ts +3 -2
  59. package/src/utils/__tests__/patchToOperations.test.ts +15 -4
  60. package/src/utils/__tests__/ranges.test.ts +8 -3
  61. package/src/utils/__tests__/valueNormalization.test.tsx +12 -4
  62. package/src/utils/__tests__/values.test.ts +0 -1
  63. package/src/utils/applyPatch.ts +71 -20
  64. package/src/utils/getPortableTextMemberSchemaTypes.ts +30 -15
  65. package/src/utils/operationToPatches.ts +126 -43
  66. package/src/utils/paths.ts +24 -7
  67. package/src/utils/ranges.ts +12 -5
  68. package/src/utils/selection.ts +19 -7
  69. package/src/utils/validateValue.ts +118 -45
  70. package/src/utils/values.ts +31 -9
  71. package/src/utils/weakMaps.ts +18 -8
  72. package/src/utils/withChanges.ts +4 -2
@@ -1,17 +1,20 @@
1
- import {type Path, type PortableTextObject, type PortableTextTextBlock} from '@sanity/types'
1
+ import {
2
+ type Path,
3
+ type PortableTextObject,
4
+ type PortableTextTextBlock,
5
+ } from '@sanity/types'
2
6
  import {isEqual, uniq} from 'lodash'
3
7
  import {
4
- type ReactElement,
5
8
  startTransition,
6
9
  useCallback,
7
10
  useEffect,
8
11
  useMemo,
9
12
  useRef,
10
13
  useState,
14
+ type ReactElement,
11
15
  } from 'react'
12
16
  import {Text} from 'slate'
13
- import {type RenderLeafProps, useSelected} from 'slate-react'
14
-
17
+ import {useSelected, type RenderLeafProps} from 'slate-react'
15
18
  import {
16
19
  type BlockAnnotationRenderProps,
17
20
  type BlockChildRenderProps,
@@ -47,8 +50,15 @@ export interface LeafProps extends RenderLeafProps {
47
50
  * @internal
48
51
  */
49
52
  export const Leaf = (props: LeafProps) => {
50
- const {attributes, children, leaf, schemaTypes, renderChild, renderDecorator, renderAnnotation} =
51
- props
53
+ const {
54
+ attributes,
55
+ children,
56
+ leaf,
57
+ schemaTypes,
58
+ renderChild,
59
+ renderDecorator,
60
+ renderAnnotation,
61
+ } = props
52
62
  const spanRef = useRef<HTMLElement>(null)
53
63
  const portableTextEditor = usePortableTextEditor()
54
64
  const blockSelected = useSelected()
@@ -64,7 +74,12 @@ export const Leaf = (props: LeafProps) => {
64
74
  [schemaTypes.decorators],
65
75
  )
66
76
  const marks: string[] = useMemo(
67
- () => uniq((leaf.marks || EMPTY_MARKS).filter((mark) => decoratorValues.includes(mark))),
77
+ () =>
78
+ uniq(
79
+ (leaf.marks || EMPTY_MARKS).filter((mark) =>
80
+ decoratorValues.includes(mark),
81
+ ),
82
+ ),
68
83
  [decoratorValues, leaf.marks],
69
84
  )
70
85
  const annotationMarks = Array.isArray(leaf.marks) ? leaf.marks : EMPTY_MARKS
@@ -73,7 +88,8 @@ export const Leaf = (props: LeafProps) => {
73
88
  annotationMarks
74
89
  .map(
75
90
  (mark) =>
76
- !decoratorValues.includes(mark) && block?.markDefs?.find((def) => def._key === mark),
91
+ !decoratorValues.includes(mark) &&
92
+ block?.markDefs?.find((def) => def._key === mark),
77
93
  )
78
94
  .filter(Boolean) as PortableTextObject[],
79
95
  [annotationMarks, block, decoratorValues],
@@ -159,7 +175,12 @@ export const Leaf = (props: LeafProps) => {
159
175
  return () => {
160
176
  sub.unsubscribe()
161
177
  }
162
- }, [path, portableTextEditor, setSelectedFromRange, shouldTrackSelectionAndFocus])
178
+ }, [
179
+ path,
180
+ portableTextEditor,
181
+ setSelectedFromRange,
182
+ shouldTrackSelectionAndFocus,
183
+ ])
163
184
 
164
185
  useEffect(() => setSelectedFromRange(), [setSelectedFromRange])
165
186
 
@@ -168,59 +189,73 @@ export const Leaf = (props: LeafProps) => {
168
189
  // Render text nodes
169
190
  if (Text.isText(leaf) && leaf._type === schemaTypes.span.name) {
170
191
  marks.forEach((mark) => {
171
- const schemaType = schemaTypes.decorators.find((dec) => dec.value === mark)
192
+ const schemaType = schemaTypes.decorators.find(
193
+ (dec) => dec.value === mark,
194
+ )
172
195
  if (schemaType && renderDecorator) {
173
- const _props: Omit<BlockDecoratorRenderProps, 'type'> = Object.defineProperty(
174
- {
175
- children: returnedChildren,
176
- editorElementRef: spanRef,
177
- focused,
178
- path,
179
- selected,
180
- schemaType,
181
- value: mark,
182
- },
183
- 'type',
184
- {
185
- enumerable: false,
186
- get() {
187
- console.warn("Property 'type' is deprecated, use 'schemaType' instead.")
188
- return schemaType
196
+ const _props: Omit<BlockDecoratorRenderProps, 'type'> =
197
+ Object.defineProperty(
198
+ {
199
+ children: returnedChildren,
200
+ editorElementRef: spanRef,
201
+ focused,
202
+ path,
203
+ selected,
204
+ schemaType,
205
+ value: mark,
206
+ },
207
+ 'type',
208
+ {
209
+ enumerable: false,
210
+ get() {
211
+ console.warn(
212
+ "Property 'type' is deprecated, use 'schemaType' instead.",
213
+ )
214
+ return schemaType
215
+ },
189
216
  },
190
- },
217
+ )
218
+ returnedChildren = renderDecorator(
219
+ _props as BlockDecoratorRenderProps,
191
220
  )
192
- returnedChildren = renderDecorator(_props as BlockDecoratorRenderProps)
193
221
  }
194
222
  })
195
223
 
196
224
  if (block && annotations.length > 0) {
197
225
  annotations.forEach((annotation) => {
198
- const schemaType = schemaTypes.annotations.find((t) => t.name === annotation._type)
226
+ const schemaType = schemaTypes.annotations.find(
227
+ (t) => t.name === annotation._type,
228
+ )
199
229
  if (schemaType) {
200
230
  if (renderAnnotation) {
201
- const _props: Omit<BlockAnnotationRenderProps, 'type'> = Object.defineProperty(
202
- {
203
- block,
204
- children: returnedChildren,
205
- editorElementRef: spanRef,
206
- focused,
207
- path,
208
- selected,
209
- schemaType,
210
- value: annotation,
211
- },
212
- 'type',
213
- {
214
- enumerable: false,
215
- get() {
216
- console.warn("Property 'type' is deprecated, use 'schemaType' instead.")
217
- return schemaType
231
+ const _props: Omit<BlockAnnotationRenderProps, 'type'> =
232
+ Object.defineProperty(
233
+ {
234
+ block,
235
+ children: returnedChildren,
236
+ editorElementRef: spanRef,
237
+ focused,
238
+ path,
239
+ selected,
240
+ schemaType,
241
+ value: annotation,
218
242
  },
219
- },
220
- )
243
+ 'type',
244
+ {
245
+ enumerable: false,
246
+ get() {
247
+ console.warn(
248
+ "Property 'type' is deprecated, use 'schemaType' instead.",
249
+ )
250
+ return schemaType
251
+ },
252
+ },
253
+ )
221
254
 
222
255
  returnedChildren = (
223
- <span ref={spanRef}>{renderAnnotation(_props as BlockAnnotationRenderProps)}</span>
256
+ <span ref={spanRef}>
257
+ {renderAnnotation(_props as BlockAnnotationRenderProps)}
258
+ </span>
224
259
  )
225
260
  } else {
226
261
  returnedChildren = (
@@ -236,26 +271,29 @@ export const Leaf = (props: LeafProps) => {
236
271
  const child = block.children.find((_child) => _child._key === leaf._key) // Ensure object equality
237
272
  if (child) {
238
273
  const defaultRendered = <>{returnedChildren}</>
239
- const _props: Omit<BlockChildRenderProps, 'type'> = Object.defineProperty(
240
- {
241
- annotations,
242
- children: defaultRendered,
243
- editorElementRef: spanRef,
244
- focused,
245
- path,
246
- schemaType: schemaTypes.span,
247
- selected,
248
- value: child,
249
- },
250
- 'type',
251
- {
252
- enumerable: false,
253
- get() {
254
- console.warn("Property 'type' is deprecated, use 'schemaType' instead.")
255
- return schemaTypes.span
274
+ const _props: Omit<BlockChildRenderProps, 'type'> =
275
+ Object.defineProperty(
276
+ {
277
+ annotations,
278
+ children: defaultRendered,
279
+ editorElementRef: spanRef,
280
+ focused,
281
+ path,
282
+ schemaType: schemaTypes.span,
283
+ selected,
284
+ value: child,
256
285
  },
257
- },
258
- )
286
+ 'type',
287
+ {
288
+ enumerable: false,
289
+ get() {
290
+ console.warn(
291
+ "Property 'type' is deprecated, use 'schemaType' instead.",
292
+ )
293
+ return schemaTypes.span
294
+ },
295
+ },
296
+ )
259
297
  returnedChildren = renderChild(_props as BlockChildRenderProps)
260
298
  }
261
299
  }
@@ -1,7 +1,6 @@
1
- import {type PropsWithChildren, useEffect, useMemo, useState} from 'react'
1
+ import {useEffect, useMemo, useState, type PropsWithChildren} from 'react'
2
2
  import {createEditor} from 'slate'
3
3
  import {Slate, withReact} from 'slate-react'
4
-
5
4
  import {type PatchObservable} from '../../types/editor'
6
5
  import {debugWithName} from '../../utils/debug'
7
6
  import {KEY_TO_SLATE_ELEMENT, KEY_TO_VALUE_ELEMENT} from '../../utils/weakMaps'
@@ -26,7 +25,8 @@ export interface SlateContainerProps extends PropsWithChildren {
26
25
  * @internal
27
26
  */
28
27
  export function SlateContainer(props: SlateContainerProps) {
29
- const {patches$, portableTextEditor, readOnly, maxBlocks, keyGenerator} = props
28
+ const {patches$, portableTextEditor, readOnly, maxBlocks, keyGenerator} =
29
+ props
30
30
 
31
31
  // Create the slate instance, using `useState` ensures setup is only run once, initially
32
32
  const [[slateEditor, subscribe]] = useState(() => {
@@ -60,10 +60,17 @@ export function SlateContainer(props: SlateContainerProps) {
60
60
  portableTextEditor,
61
61
  readOnly,
62
62
  })
63
- }, [keyGenerator, portableTextEditor, maxBlocks, readOnly, patches$, slateEditor])
63
+ }, [
64
+ keyGenerator,
65
+ portableTextEditor,
66
+ maxBlocks,
67
+ readOnly,
68
+ patches$,
69
+ slateEditor,
70
+ ])
64
71
 
65
72
  const initialValue = useMemo(() => {
66
- return [slateEditor.pteCreateEmptyBlock()]
73
+ return [slateEditor.pteCreateTextBlock({decorators: []})]
67
74
  }, [slateEditor])
68
75
 
69
76
  useEffect(() => {
@@ -4,7 +4,6 @@ import {throttle} from 'lodash'
4
4
  import {useCallback, useEffect, useMemo, useRef} from 'react'
5
5
  import {Editor} from 'slate'
6
6
  import {useSlate} from 'slate-react'
7
-
8
7
  import {type EditorChange, type EditorChanges} from '../../types/editor'
9
8
  import {debugWithName} from '../../utils/debug'
10
9
  import {IS_PROCESSING_LOCAL_CHANGES} from '../../utils/weakMaps'
@@ -61,7 +60,11 @@ export function Synchronizer(props: SynchronizerProps) {
61
60
  debug(`Patches:\n${JSON.stringify(pendingPatches.current, null, 2)}`)
62
61
  }
63
62
  const snapshot = getValue()
64
- change$.next({type: 'mutation', patches: pendingPatches.current, snapshot})
63
+ change$.next({
64
+ type: 'mutation',
65
+ patches: pendingPatches.current,
66
+ snapshot,
67
+ })
65
68
  pendingPatches.current = []
66
69
  }
67
70
  IS_PROCESSING_LOCAL_CHANGES.set(slateEditor, false)
@@ -1,11 +1,11 @@
1
1
  import {createContext, useContext} from 'react'
2
-
3
2
  import {type PortableTextEditor} from '../PortableTextEditor'
4
3
 
5
4
  /**
6
5
  * A React context for sharing the editor object.
7
6
  */
8
- export const PortableTextEditorContext = createContext<PortableTextEditor | null>(null)
7
+ export const PortableTextEditorContext =
8
+ createContext<PortableTextEditor | null>(null)
9
9
 
10
10
  /**
11
11
  * @public
@@ -1,12 +1,18 @@
1
- import {createContext, startTransition, useContext, useEffect, useState} from 'react'
2
-
1
+ import {
2
+ createContext,
3
+ startTransition,
4
+ useContext,
5
+ useEffect,
6
+ useState,
7
+ } from 'react'
3
8
  import {type EditorChanges, type EditorSelection} from '../../types/editor'
4
9
  import {debugWithName} from '../../utils/debug'
5
10
 
6
11
  /**
7
12
  * A React context for sharing the editor selection.
8
13
  */
9
- const PortableTextEditorSelectionContext = createContext<EditorSelection | null>(null)
14
+ const PortableTextEditorSelectionContext =
15
+ createContext<EditorSelection | null>(null)
10
16
 
11
17
  /**
12
18
  * @public
@@ -1,8 +1,10 @@
1
1
  import {describe, expect, it, jest} from '@jest/globals'
2
2
  import {render, waitFor} from '@testing-library/react'
3
3
  import {createRef, type RefObject} from 'react'
4
-
5
- import {PortableTextEditorTester, schemaType} from '../__tests__/PortableTextEditorTester'
4
+ import {
5
+ PortableTextEditorTester,
6
+ schemaType,
7
+ } from '../__tests__/PortableTextEditorTester'
6
8
  import {PortableTextEditor} from '../PortableTextEditor'
7
9
 
8
10
  const initialValue = [
@@ -60,7 +62,9 @@ describe('useSyncValue', () => {
60
62
  )
61
63
  await waitFor(() => {
62
64
  if (editorRef.current) {
63
- expect(PortableTextEditor.getValue(editorRef.current)).toEqual(syncedValue)
65
+ expect(PortableTextEditor.getValue(editorRef.current)).toEqual(
66
+ syncedValue,
67
+ )
64
68
  }
65
69
  })
66
70
  })
@@ -101,7 +105,8 @@ describe('useSyncValue', () => {
101
105
  )
102
106
  await waitFor(() => {
103
107
  if (editorRef.current) {
104
- expect(PortableTextEditor.getValue(editorRef.current)).toMatchInlineSnapshot(`
108
+ expect(PortableTextEditor.getValue(editorRef.current))
109
+ .toMatchInlineSnapshot(`
105
110
  Array [
106
111
  Object {
107
112
  "_key": "77071c3af231",