@portabletext/editor 1.0.19 → 1.1.1

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 (76) hide show
  1. package/lib/index.d.mts +142 -67
  2. package/lib/index.d.ts +142 -67
  3. package/lib/index.esm.js +1130 -371
  4. package/lib/index.esm.js.map +1 -1
  5. package/lib/index.js +1130 -371
  6. package/lib/index.js.map +1 -1
  7. package/lib/index.mjs +1130 -371
  8. package/lib/index.mjs.map +1 -1
  9. package/package.json +4 -18
  10. package/src/editor/Editable.tsx +128 -55
  11. package/src/editor/PortableTextEditor.tsx +66 -32
  12. package/src/editor/__tests__/PortableTextEditor.test.tsx +44 -18
  13. package/src/editor/__tests__/PortableTextEditorTester.tsx +50 -38
  14. package/src/editor/__tests__/RangeDecorations.test.tsx +4 -6
  15. package/src/editor/__tests__/handleClick.test.tsx +28 -9
  16. package/src/editor/__tests__/insert-block.test.tsx +24 -8
  17. package/src/editor/__tests__/pteWarningsSelfSolving.test.tsx +31 -63
  18. package/src/editor/__tests__/utils.ts +10 -4
  19. package/src/editor/components/DraggableBlock.tsx +36 -13
  20. package/src/editor/components/Element.tsx +73 -33
  21. package/src/editor/components/Leaf.tsx +114 -76
  22. package/src/editor/components/SlateContainer.tsx +14 -7
  23. package/src/editor/components/Synchronizer.tsx +8 -5
  24. package/src/editor/hooks/usePortableTextEditor.ts +3 -3
  25. package/src/editor/hooks/usePortableTextEditorSelection.tsx +10 -4
  26. package/src/editor/hooks/useSyncValue.test.tsx +9 -4
  27. package/src/editor/hooks/useSyncValue.ts +198 -133
  28. package/src/editor/nodes/DefaultAnnotation.tsx +6 -4
  29. package/src/editor/nodes/DefaultObject.tsx +1 -1
  30. package/src/editor/plugins/__tests__/createWithInsertData.test.tsx +23 -8
  31. package/src/editor/plugins/__tests__/withEditableAPIDelete.test.tsx +26 -9
  32. package/src/editor/plugins/__tests__/withEditableAPIGetFragment.test.tsx +15 -5
  33. package/src/editor/plugins/__tests__/withEditableAPIInsert.test.tsx +60 -19
  34. package/src/editor/plugins/__tests__/withEditableAPISelectionsOverlapping.test.tsx +5 -3
  35. package/src/editor/plugins/__tests__/withPortableTextLists.test.tsx +4 -2
  36. package/src/editor/plugins/__tests__/withPortableTextMarkModel.test.tsx +61 -19
  37. package/src/editor/plugins/__tests__/withPortableTextSelections.test.tsx +6 -3
  38. package/src/editor/plugins/__tests__/withUndoRedo.test.tsx +30 -13
  39. package/src/editor/plugins/createWithEditableAPI.ts +361 -131
  40. package/src/editor/plugins/createWithHotKeys.ts +46 -130
  41. package/src/editor/plugins/createWithInsertBreak.ts +167 -28
  42. package/src/editor/plugins/createWithInsertData.ts +66 -30
  43. package/src/editor/plugins/createWithMaxBlocks.ts +6 -3
  44. package/src/editor/plugins/createWithObjectKeys.ts +7 -3
  45. package/src/editor/plugins/createWithPatches.ts +66 -24
  46. package/src/editor/plugins/createWithPlaceholderBlock.ts +9 -5
  47. package/src/editor/plugins/createWithPortableTextBlockStyle.ts +17 -7
  48. package/src/editor/plugins/createWithPortableTextLists.ts +21 -9
  49. package/src/editor/plugins/createWithPortableTextMarkModel.ts +217 -52
  50. package/src/editor/plugins/createWithPortableTextSelections.ts +11 -9
  51. package/src/editor/plugins/createWithSchemaTypes.ts +26 -10
  52. package/src/editor/plugins/createWithUndoRedo.ts +106 -27
  53. package/src/editor/plugins/createWithUtils.ts +33 -11
  54. package/src/editor/plugins/index.ts +34 -13
  55. package/src/types/editor.ts +73 -44
  56. package/src/types/options.ts +7 -5
  57. package/src/types/slate.ts +6 -6
  58. package/src/utils/__tests__/dmpToOperations.test.ts +41 -16
  59. package/src/utils/__tests__/operationToPatches.test.ts +4 -3
  60. package/src/utils/__tests__/patchToOperations.test.ts +16 -5
  61. package/src/utils/__tests__/ranges.test.ts +9 -4
  62. package/src/utils/__tests__/valueNormalization.test.tsx +12 -4
  63. package/src/utils/__tests__/values.test.ts +0 -1
  64. package/src/utils/applyPatch.ts +78 -29
  65. package/src/utils/getPortableTextMemberSchemaTypes.ts +38 -23
  66. package/src/utils/operationToPatches.ts +123 -44
  67. package/src/utils/paths.ts +26 -9
  68. package/src/utils/ranges.ts +16 -10
  69. package/src/utils/selection.ts +21 -9
  70. package/src/utils/ucs2Indices.ts +2 -2
  71. package/src/utils/validateValue.ts +118 -45
  72. package/src/utils/values.ts +38 -17
  73. package/src/utils/weakMaps.ts +20 -10
  74. package/src/utils/withChanges.ts +5 -3
  75. package/src/utils/withUndoRedo.ts +1 -1
  76. package/src/utils/withoutPatching.ts +1 -1
@@ -1,5 +1,5 @@
1
1
  import {describe, expect, test} from '@jest/globals'
2
- import {type DiffMatchPatch} from '@portabletext/patches'
2
+ import type {DiffMatchPatch} from '@portabletext/patches'
3
3
  import {makeDiff, makePatches, stringifyPatches} from '@sanity/diff-match-patch'
4
4
  import {
5
5
  isPortableTextSpan,
@@ -9,9 +9,8 @@ import {
9
9
  type PortableTextSpan,
10
10
  type PortableTextTextBlock,
11
11
  } from '@sanity/types'
12
- import {type Descendant, type Operation} from 'slate'
13
-
14
- import {type PortableTextSlateEditor} from '../../types/editor'
12
+ import type {Descendant, Operation} from 'slate'
13
+ import type {PortableTextSlateEditor} from '../../types/editor'
15
14
  import {diffMatchPatch} from '../applyPatch'
16
15
 
17
16
  describe('operationToPatches: diffMatchPatch', () => {
@@ -23,7 +22,9 @@ describe('operationToPatches: diffMatchPatch', () => {
23
22
  test('should apply the most basic additive operation correctly', () => {
24
23
  const source = 'Hello'
25
24
  const target = 'Hello there'
26
- const patch = getPteDmpPatch(stringifyPatches(makePatches(makeDiff(source, target))))
25
+ const patch = getPteDmpPatch(
26
+ stringifyPatches(makePatches(makeDiff(source, target))),
27
+ )
27
28
  const editor = getMockEditor({text: source})
28
29
  expect(diffMatchPatch(editor, patch)).toBe(true)
29
30
  expect(editor.getText()).toBe(target)
@@ -32,7 +33,9 @@ describe('operationToPatches: diffMatchPatch', () => {
32
33
  test('should apply the most basic removal operation correctly', () => {
33
34
  const source = 'Hello there'
34
35
  const target = 'Hello'
35
- const patch = getPteDmpPatch(stringifyPatches(makePatches(makeDiff(source, target))))
36
+ const patch = getPteDmpPatch(
37
+ stringifyPatches(makePatches(makeDiff(source, target))),
38
+ )
36
39
  const editor = getMockEditor({text: source})
37
40
  expect(diffMatchPatch(editor, patch)).toBe(true)
38
41
  expect(editor.getText()).toBe(target)
@@ -41,7 +44,9 @@ describe('operationToPatches: diffMatchPatch', () => {
41
44
  test('should treat equality as noops', () => {
42
45
  const source = 'Hello'
43
46
  const target = 'Hello'
44
- const patch = getPteDmpPatch(stringifyPatches(makePatches(makeDiff(source, target))))
47
+ const patch = getPteDmpPatch(
48
+ stringifyPatches(makePatches(makeDiff(source, target))),
49
+ )
45
50
  const editor = getMockEditor({text: source})
46
51
  expect(diffMatchPatch(editor, patch)).toBe(true)
47
52
  expect(editor.getText()).toBe(target)
@@ -50,7 +55,9 @@ describe('operationToPatches: diffMatchPatch', () => {
50
55
  test('should apply combined add + remove operations', () => {
51
56
  const source = 'A quick brown fox jumps over the very lazy dog'
52
57
  const target = 'The quick brown fox jumps over the lazy dog'
53
- const patch = getPteDmpPatch(stringifyPatches(makePatches(makeDiff(source, target))))
58
+ const patch = getPteDmpPatch(
59
+ stringifyPatches(makePatches(makeDiff(source, target))),
60
+ )
54
61
  const editor = getMockEditor({text: source})
55
62
  expect(diffMatchPatch(editor, patch)).toBe(true)
56
63
  expect(editor.getText()).toBe(target)
@@ -58,8 +65,11 @@ describe('operationToPatches: diffMatchPatch', () => {
58
65
 
59
66
  test('should apply combined add + remove operations', () => {
60
67
  const source = 'Many quick brown fox jumps over the very lazy dog'
61
- const target = 'The many, quick, brown, foxes jumps over all of the lazy dogs'
62
- const patch = getPteDmpPatch(stringifyPatches(makePatches(makeDiff(source, target))))
68
+ const target =
69
+ 'The many, quick, brown, foxes jumps over all of the lazy dogs'
70
+ const patch = getPteDmpPatch(
71
+ stringifyPatches(makePatches(makeDiff(source, target))),
72
+ )
63
73
  const editor = getMockEditor({text: source})
64
74
  expect(diffMatchPatch(editor, patch)).toBe(true)
65
75
  expect(editor.getText()).toBe(target)
@@ -70,7 +80,9 @@ describe('operationToPatches: diffMatchPatch', () => {
70
80
  const line2 = 'But the slow green frog jumps over the wild cat'
71
81
  const source = [line1, line2, line1, line2].join('\n')
72
82
  const target = [line2, line1, line2, line1].join('\n')
73
- const patch = getPteDmpPatch(stringifyPatches(makePatches(makeDiff(source, target))))
83
+ const patch = getPteDmpPatch(
84
+ stringifyPatches(makePatches(makeDiff(source, target))),
85
+ )
74
86
  const editor = getMockEditor({text: source})
75
87
  expect(diffMatchPatch(editor, patch)).toBe(true)
76
88
  expect(editor.getText()).toBe(target)
@@ -79,7 +91,9 @@ describe('operationToPatches: diffMatchPatch', () => {
79
91
  test('should apply larger text differences correctly', () => {
80
92
  const source = `Portable Text is a agnostic abstraction of "rich text" that can be stringified into any markup language, for instance HTML, Markdown, SSML, XML, etc. It's designed to be efficient for collaboration, and makes it possible to enrich rich text with data structures in depth.\n\nPortable Text is built on the idea of rich text as an array of blocks, themselves arrays of children spans. Each block can have a style and a set of mark dfinitions, which describe data structures distributed on the children spans. Portable Text also allows for inserting arbitrary data objects in the array, only requiring _type-key. Portable Text also allows for custom objects in the root array, enabling rendering environments to mix rich text with custom content types.\n\nPortable Text is a combination of arrays and objects. In its simplest form it's an array of objects with an array of children. Some definitions: \n- Block: Typically recognized as a section of a text, e.g. a paragraph or a heading.\n- Span: Piece of text with a set of marks, e.g. bold or italic.\n- Mark: A mark is a data structure that can be appliad to a span, e.g. a link or a comment.\n- Mark definition: A mark definition is a structure that describes a mark, a link or a comment.`
81
93
  const target = `Portable Text is an agnostic abstraction of rich text that can be serialized into pretty much any markup language, be it HTML, Markdown, SSML, XML, etc. It is designed to be efficient for real-time collaborative interfaces, and makes it possible to annotate rich text with additional data structures recursively.\n\nPortable Text is built on the idea of rich text as an array of blocks, themselves arrays of child spans. Each block can have a style and a set of mark definitions, which describe data structures that can be applied on the children spans. Portable Text also allows for inserting arbitrary data objects in the array, only requiring _type-key. Portable Text also allows for custom content objects in the root array, enabling editing- and rendering environments to mix rich text with custom content types.\n\nPortable Text is a recursive composition of arrays and objects. In its simplest form it's an array of objects of a type with an array of children. Some definitions: \n- Block: A block is what's typically recognized as a section of a text, e.g. a paragraph or a heading.\n- Span: A span is a piece of text with a set of marks, e.g. bold or italic.\n- Mark: A mark is a data structure that can be applied to a span, e.g. a link or a comment.\n- Mark definition: A mark definition is a data structure that describes a mark, e.g. a link or a comment.`
82
- const patch = getPteDmpPatch(stringifyPatches(makePatches(makeDiff(source, target))))
94
+ const patch = getPteDmpPatch(
95
+ stringifyPatches(makePatches(makeDiff(source, target))),
96
+ )
83
97
  const editor = getMockEditor({text: source})
84
98
  expect(diffMatchPatch(editor, patch)).toBe(true)
85
99
  expect(editor.getText()).toBe(target)
@@ -88,7 +102,9 @@ describe('operationToPatches: diffMatchPatch', () => {
88
102
  test('should apply offset text differences correctly', () => {
89
103
  const source = `This string has changes, but they occur somewhere near the end. That means we need to use an offset to get at the change, we cannot just rely on equality segaments in the generated diff.`
90
104
  const target = `This string has changes, but they occur somewhere near the end. That means we need to use an offset to get at the change, we cannot just rely on equality segments in the generated diff.`
91
- const patch = getPteDmpPatch(stringifyPatches(makePatches(makeDiff(source, target))))
105
+ const patch = getPteDmpPatch(
106
+ stringifyPatches(makePatches(makeDiff(source, target))),
107
+ )
92
108
  const editor = getMockEditor({text: source})
93
109
  expect(diffMatchPatch(editor, patch)).toBe(true)
94
110
  expect(editor.getText()).toBe(target)
@@ -115,13 +131,20 @@ function getMockEditor(options: MockEditorOptions): Pick<
115
131
  > & {
116
132
  getText: () => string
117
133
  } {
118
- let children: PortableTextBlock[] = 'children' in options ? options.children : []
134
+ let children: PortableTextBlock[] =
135
+ 'children' in options ? options.children : []
119
136
  if (!('children' in options)) {
120
137
  children = [
121
138
  {
122
139
  _type: 'block',
123
140
  _key: 'bA',
124
- children: [{_type: 'span', _key: 's1', text: 'text' in options ? options.text : ''}],
141
+ children: [
142
+ {
143
+ _type: 'span',
144
+ _key: 's1',
145
+ text: 'text' in options ? options.text : '',
146
+ },
147
+ ],
125
148
  markDefs: [],
126
149
  },
127
150
  ]
@@ -129,7 +152,9 @@ function getMockEditor(options: MockEditorOptions): Pick<
129
152
 
130
153
  function getText(blockKey?: string) {
131
154
  return children
132
- .filter((child): child is PortableTextTextBlock => isPortableTextTextBlock(child))
155
+ .filter((child): child is PortableTextTextBlock =>
156
+ isPortableTextTextBlock(child),
157
+ )
133
158
  .filter((child) => (blockKey ? child._key === blockKey : true))
134
159
  .flatMap((block) =>
135
160
  block.children
@@ -1,7 +1,6 @@
1
1
  import {beforeEach, describe, expect, it} from '@jest/globals'
2
- import {type PortableTextTextBlock} from '@sanity/types'
2
+ import type {PortableTextTextBlock} from '@sanity/types'
3
3
  import {createEditor, type Descendant} from 'slate'
4
-
5
4
  import {PortableTextEditor, type PortableTextEditorProps} from '../..'
6
5
  import {schemaType} from '../../editor/__tests__/PortableTextEditorTester'
7
6
  import {defaultKeyGenerator} from '../../editor/hooks/usePortableTextEditorKeyGenerator'
@@ -14,7 +13,9 @@ const portableTextFeatures = getPortableTextMemberSchemaTypes(schemaType)
14
13
  const operationToPatches = createOperationToPatches(portableTextFeatures)
15
14
 
16
15
  const {editor} = withPlugins(createEditor(), {
17
- portableTextEditor: new PortableTextEditor({schemaType} as PortableTextEditorProps),
16
+ portableTextEditor: new PortableTextEditor({
17
+ schemaType,
18
+ } as PortableTextEditorProps),
18
19
  keyGenerator: defaultKeyGenerator,
19
20
  readOnly: false,
20
21
  })
@@ -1,8 +1,7 @@
1
1
  import {beforeEach, describe, expect, it} from '@jest/globals'
2
- import {type Patch} from '@portabletext/patches'
2
+ import type {Patch} from '@portabletext/patches'
3
3
  import {noop} from 'lodash'
4
4
  import {createEditor, type Descendant} from 'slate'
5
-
6
5
  import {keyGenerator, PortableTextEditor} from '../..'
7
6
  import {schemaType} from '../../editor/__tests__/PortableTextEditorTester'
8
7
  import {withPlugins} from '../../editor/plugins'
@@ -51,7 +50,11 @@ describe('operationToPatches', () => {
51
50
  it('makes the correct operations for block objects', () => {
52
51
  editor.children = createDefaultValue()
53
52
  const patches = [
54
- {type: 'unset', path: [{_key: 'c01739b0d03b'}, 'hotspot'], origin: 'remote'},
53
+ {
54
+ type: 'unset',
55
+ path: [{_key: 'c01739b0d03b'}, 'hotspot'],
56
+ origin: 'remote',
57
+ },
55
58
  {type: 'unset', path: [{_key: 'c01739b0d03b'}, 'crop'], origin: 'remote'},
56
59
  {
57
60
  type: 'set',
@@ -114,7 +117,11 @@ describe('operationToPatches', () => {
114
117
  },
115
118
  ]
116
119
  const patches = [
117
- {type: 'insert', path: [{_key: 'c01739b0d03b'}, 'nestedArray'], origin: 'remote'},
120
+ {
121
+ type: 'insert',
122
+ path: [{_key: 'c01739b0d03b'}, 'nestedArray'],
123
+ origin: 'remote',
124
+ },
118
125
  ] as Patch[]
119
126
  patches.forEach((p) => {
120
127
  patchToOperations(editor, p)
@@ -173,7 +180,11 @@ describe('operationToPatches', () => {
173
180
  },
174
181
  ]
175
182
  const patches = [
176
- {type: 'unset', path: [{_key: 'c01739b0d03b'}, 'nestedArray', 0], origin: 'remote'},
183
+ {
184
+ type: 'unset',
185
+ path: [{_key: 'c01739b0d03b'}, 'nestedArray', 0],
186
+ origin: 'remote',
187
+ },
177
188
  ] as Patch[]
178
189
  patches.forEach((p) => {
179
190
  patchToOperations(editor, p)
@@ -1,11 +1,13 @@
1
1
  import {describe, expect, it} from '@jest/globals'
2
- import {type InsertTextOperation, type Range} from 'slate'
3
-
2
+ import type {InsertTextOperation, Range} from 'slate'
4
3
  import {moveRangeByOperation} from '../ranges'
5
4
 
6
5
  describe('moveRangeByOperation', () => {
7
6
  it('should move range when inserting text in front of it', () => {
8
- const range: Range = {anchor: {path: [0, 0], offset: 1}, focus: {path: [0, 0], offset: 3}}
7
+ const range: Range = {
8
+ anchor: {path: [0, 0], offset: 1},
9
+ focus: {path: [0, 0], offset: 3},
10
+ }
9
11
  const operation: InsertTextOperation = {
10
12
  type: 'insert_text',
11
13
  path: [0, 0],
@@ -13,6 +15,9 @@ describe('moveRangeByOperation', () => {
13
15
  text: 'foo',
14
16
  }
15
17
  const newRange = moveRangeByOperation(range, operation)
16
- expect(newRange).toEqual({anchor: {path: [0, 0], offset: 4}, focus: {path: [0, 0], offset: 6}})
18
+ expect(newRange).toEqual({
19
+ anchor: {path: [0, 0], offset: 4},
20
+ focus: {path: [0, 0], offset: 6},
21
+ })
17
22
  })
18
23
  })
@@ -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 '../../editor/__tests__/PortableTextEditorTester'
4
+ import {
5
+ PortableTextEditorTester,
6
+ schemaType,
7
+ } from '../../editor/__tests__/PortableTextEditorTester'
6
8
  import {PortableTextEditor} from '../../editor/PortableTextEditor'
7
9
 
8
10
  describe('values: normalization', () => {
@@ -36,8 +38,14 @@ describe('values: normalization', () => {
36
38
  if (editorRef.current) {
37
39
  PortableTextEditor.focus(editorRef.current)
38
40
  PortableTextEditor.select(editorRef.current, {
39
- focus: {path: [{_key: '5fc57af23597'}, 'children', {_key: 'be1c67c6971a'}], offset: 0},
40
- anchor: {path: [{_key: '5fc57af23597'}, 'children', {_key: 'be1c67c6971a'}], offset: 5},
41
+ focus: {
42
+ path: [{_key: '5fc57af23597'}, 'children', {_key: 'be1c67c6971a'}],
43
+ offset: 0,
44
+ },
45
+ anchor: {
46
+ path: [{_key: '5fc57af23597'}, 'children', {_key: 'be1c67c6971a'}],
47
+ offset: 5,
48
+ },
41
49
  })
42
50
  PortableTextEditor.toggleMark(editorRef.current, 'strong')
43
51
  expect(PortableTextEditor.getValue(editorRef.current)).toEqual([
@@ -1,5 +1,4 @@
1
1
  import {describe, expect, it} from '@jest/globals'
2
-
3
2
  import {schemaType} from '../../editor/__tests__/PortableTextEditorTester'
4
3
  import {getPortableTextMemberSchemaTypes} from '../getPortableTextMemberSchemaTypes'
5
4
  import {fromSlateValue, toSlateValue} from '../values'
@@ -1,4 +1,3 @@
1
- /* eslint-disable max-statements */
2
1
  import {
3
2
  applyAll,
4
3
  type DiffMatchPatch,
@@ -8,24 +7,33 @@ import {
8
7
  type UnsetPatch,
9
8
  } from '@portabletext/patches'
10
9
  import {
11
- applyPatches as diffMatchPatchApplyPatches,
12
10
  cleanupEfficiency,
13
11
  DIFF_DELETE,
14
12
  DIFF_EQUAL,
15
13
  DIFF_INSERT,
14
+ applyPatches as diffMatchPatchApplyPatches,
16
15
  makeDiff,
17
16
  parsePatch,
18
17
  } from '@sanity/diff-match-patch'
19
- import {
20
- type KeyedSegment,
21
- type Path,
22
- type PathSegment,
23
- type PortableTextBlock,
24
- type PortableTextChild,
18
+ import type {
19
+ KeyedSegment,
20
+ Path,
21
+ PathSegment,
22
+ PortableTextBlock,
23
+ PortableTextChild,
25
24
  } from '@sanity/types'
26
- import {type Descendant, Element, type Node, type Path as SlatePath, Text, Transforms} from 'slate'
27
-
28
- import {type PortableTextMemberSchemaTypes, type PortableTextSlateEditor} from '../types/editor'
25
+ import {
26
+ Element,
27
+ Text,
28
+ Transforms,
29
+ type Descendant,
30
+ type Node,
31
+ type Path as SlatePath,
32
+ } from 'slate'
33
+ import type {
34
+ PortableTextMemberSchemaTypes,
35
+ PortableTextSlateEditor,
36
+ } from '../types/editor'
29
37
  import {debugWithName} from './debug'
30
38
  import {toSlateValue} from './values'
31
39
  import {KEY_TO_SLATE_ELEMENT} from './weakMaps'
@@ -41,12 +49,14 @@ export function createApplyPatch(
41
49
  ): (editor: PortableTextSlateEditor, patch: Patch) => boolean {
42
50
  let previousPatch: Patch | undefined
43
51
 
44
- return function (editor: PortableTextSlateEditor, patch: Patch): boolean {
52
+ return (editor: PortableTextSlateEditor, patch: Patch): boolean => {
45
53
  let changed = false
46
54
 
47
55
  // Save some CPU cycles by not stringifying unless enabled
48
56
  if (debugVerbose) {
49
- debug('\n\nNEW PATCH =============================================================')
57
+ debug(
58
+ '\n\nNEW PATCH =============================================================',
59
+ )
50
60
  debug(JSON.stringify(patch, null, 2))
51
61
  }
52
62
 
@@ -91,7 +101,10 @@ export function diffMatchPatch(
91
101
  >,
92
102
  patch: DiffMatchPatch,
93
103
  ): boolean {
94
- const {block, child, childPath} = findBlockAndChildFromPath(editor, patch.path)
104
+ const {block, child, childPath} = findBlockAndChildFromPath(
105
+ editor,
106
+ patch.path,
107
+ )
95
108
  if (!block) {
96
109
  debug('Block not found')
97
110
  return false
@@ -112,7 +125,9 @@ export function diffMatchPatch(
112
125
  }
113
126
 
114
127
  const patches = parsePatch(patch.value)
115
- const [newValue] = diffMatchPatchApplyPatches(patches, child.text, {allowExceedingIndices: true})
128
+ const [newValue] = diffMatchPatchApplyPatches(patches, child.text, {
129
+ allowExceedingIndices: true,
130
+ })
116
131
  const diff = cleanupEfficiency(makeDiff(child.text, newValue), 5)
117
132
 
118
133
  debugState(editor, 'before')
@@ -160,7 +175,8 @@ function insertPatch(
160
175
  KEY_TO_SLATE_ELEMENT.get(editor),
161
176
  ) as Descendant[]
162
177
  const targetBlockIndex = targetBlockPath[0]
163
- const normalizedIdx = position === 'after' ? targetBlockIndex + 1 : targetBlockIndex
178
+ const normalizedIdx =
179
+ position === 'after' ? targetBlockIndex + 1 : targetBlockIndex
164
180
  debug(`Inserting blocks at path [${normalizedIdx}]`)
165
181
  debugState(editor, 'before')
166
182
  Transforms.insertNodes(editor, blocksToInsert, {at: [normalizedIdx]})
@@ -181,12 +197,15 @@ function insertPatch(
181
197
  KEY_TO_SLATE_ELEMENT.get(editor),
182
198
  )
183
199
  const targetChildIndex = targetChildPath[1]
184
- const normalizedIdx = position === 'after' ? targetChildIndex + 1 : targetChildIndex
200
+ const normalizedIdx =
201
+ position === 'after' ? targetChildIndex + 1 : targetChildIndex
185
202
  const childInsertPath = [targetChildPath[0], normalizedIdx]
186
203
  debug(`Inserting children at path ${childInsertPath}`)
187
204
  debugState(editor, 'before')
188
205
  if (childrenToInsert && Element.isElement(childrenToInsert[0])) {
189
- Transforms.insertNodes(editor, childrenToInsert[0].children, {at: childInsertPath})
206
+ Transforms.insertNodes(editor, childrenToInsert[0].children, {
207
+ at: childInsertPath,
208
+ })
190
209
  }
191
210
  debugState(editor, 'after')
192
211
  return true
@@ -198,7 +217,10 @@ function setPatch(editor: PortableTextSlateEditor, patch: SetPatch) {
198
217
  value = {}
199
218
  value[patch.path[3]] = patch.value
200
219
  }
201
- const {block, blockPath, child, childPath} = findBlockAndChildFromPath(editor, patch.path)
220
+ const {block, blockPath, child, childPath} = findBlockAndChildFromPath(
221
+ editor,
222
+ patch.path,
223
+ )
202
224
 
203
225
  if (!block) {
204
226
  debug('Block not found')
@@ -250,7 +272,6 @@ function setPatch(editor: PortableTextSlateEditor, patch: SetPatch) {
250
272
  } else if (Element.isElement(block) && patch.path.length === 1 && blockPath) {
251
273
  debug('Setting block property')
252
274
  const {children, ...nextRest} = value as unknown as PortableTextBlock
253
- // eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-vars
254
275
  const {children: prevChildren, ...prevRest} = block || {children: undefined}
255
276
  // Set any block properties
256
277
  editor.apply({
@@ -287,7 +308,11 @@ function setPatch(editor: PortableTextSlateEditor, patch: SetPatch) {
287
308
  return true
288
309
  }
289
310
 
290
- function unsetPatch(editor: PortableTextSlateEditor, patch: UnsetPatch, previousPatch?: Patch) {
311
+ function unsetPatch(
312
+ editor: PortableTextSlateEditor,
313
+ patch: UnsetPatch,
314
+ previousPatch?: Patch,
315
+ ) {
291
316
  // Value
292
317
  if (patch.path.length === 0) {
293
318
  debug('Removing everything')
@@ -297,7 +322,7 @@ function unsetPatch(editor: PortableTextSlateEditor, patch: UnsetPatch, previous
297
322
  editor.children.forEach((c, i) => {
298
323
  Transforms.removeNodes(editor, {at: [i]})
299
324
  })
300
- Transforms.insertNodes(editor, editor.pteCreateEmptyBlock())
325
+ Transforms.insertNodes(editor, editor.pteCreateTextBlock({decorators: []}))
301
326
  if (previousSelection) {
302
327
  Transforms.select(editor, {
303
328
  anchor: {path: [0, 0], offset: 0},
@@ -309,7 +334,10 @@ function unsetPatch(editor: PortableTextSlateEditor, patch: UnsetPatch, previous
309
334
  debugState(editor, 'after')
310
335
  return true
311
336
  }
312
- const {block, blockPath, child, childPath} = findBlockAndChildFromPath(editor, patch.path)
337
+ const {block, blockPath, child, childPath} = findBlockAndChildFromPath(
338
+ editor,
339
+ patch.path,
340
+ )
313
341
 
314
342
  // Single blocks
315
343
  if (patch.path.length === 1) {
@@ -327,7 +355,11 @@ function unsetPatch(editor: PortableTextSlateEditor, patch: UnsetPatch, previous
327
355
  }
328
356
 
329
357
  // Unset on text block children
330
- if (editor.isTextBlock(block) && patch.path[1] === 'children' && patch.path.length === 3) {
358
+ if (
359
+ editor.isTextBlock(block) &&
360
+ patch.path[1] === 'children' &&
361
+ patch.path.length === 3
362
+ ) {
331
363
  if (!child || !childPath) {
332
364
  debug('Child not found')
333
365
  return false
@@ -349,7 +381,10 @@ function isKeyedSegment(segment: PathSegment): segment is KeyedSegment {
349
381
  }
350
382
 
351
383
  function debugState(
352
- editor: Pick<PortableTextSlateEditor, 'children' | 'isTextBlock' | 'apply' | 'selection'>,
384
+ editor: Pick<
385
+ PortableTextSlateEditor,
386
+ 'children' | 'isTextBlock' | 'apply' | 'selection'
387
+ >,
353
388
  stateName: string,
354
389
  ) {
355
390
  if (!debugVerbose) {
@@ -369,7 +404,9 @@ function findBlockFromPath(
369
404
  ): {block?: Descendant; path?: SlatePath} {
370
405
  let blockIndex = -1
371
406
  const block = editor.children.find((node: Descendant, index: number) => {
372
- const isMatch = isKeyedSegment(path[0]) ? node._key === path[0]._key : index === path[0]
407
+ const isMatch = isKeyedSegment(path[0])
408
+ ? node._key === path[0]._key
409
+ : index === path[0]
373
410
  if (isMatch) {
374
411
  blockIndex = index
375
412
  }
@@ -387,14 +424,21 @@ function findBlockAndChildFromPath(
387
424
  'children' | 'isTextBlock' | 'apply' | 'selection' | 'onChange'
388
425
  >,
389
426
  path: Path,
390
- ): {child?: Descendant; childPath?: SlatePath; block?: Descendant; blockPath?: SlatePath} {
427
+ ): {
428
+ child?: Descendant
429
+ childPath?: SlatePath
430
+ block?: Descendant
431
+ blockPath?: SlatePath
432
+ } {
391
433
  const {block, path: blockPath} = findBlockFromPath(editor, path)
392
434
  if (!(Element.isElement(block) && path[1] === 'children')) {
393
435
  return {block, blockPath, child: undefined, childPath: undefined}
394
436
  }
395
437
  let childIndex = -1
396
438
  const child = block.children.find((node, index: number) => {
397
- const isMatch = isKeyedSegment(path[2]) ? node._key === path[2]._key : index === path[2]
439
+ const isMatch = isKeyedSegment(path[2])
440
+ ? node._key === path[2]._key
441
+ : index === path[2]
398
442
  if (isMatch) {
399
443
  childIndex = index
400
444
  }
@@ -403,5 +447,10 @@ function findBlockAndChildFromPath(
403
447
  if (!child) {
404
448
  return {block, blockPath, child: undefined, childPath: undefined}
405
449
  }
406
- return {block, child, blockPath, childPath: blockPath?.concat(childIndex) as SlatePath}
450
+ return {
451
+ block,
452
+ child,
453
+ blockPath,
454
+ childPath: blockPath?.concat(childIndex) as SlatePath,
455
+ }
407
456
  }
@@ -1,13 +1,12 @@
1
- import {
2
- type ArraySchemaType,
3
- type BlockSchemaType,
4
- type ObjectSchemaType,
5
- type PortableTextBlock,
6
- type SchemaType,
7
- type SpanSchemaType,
1
+ import type {
2
+ ArraySchemaType,
3
+ BlockSchemaType,
4
+ ObjectSchemaType,
5
+ PortableTextBlock,
6
+ SchemaType,
7
+ SpanSchemaType,
8
8
  } from '@sanity/types'
9
-
10
- import {type PortableTextMemberSchemaTypes} from '../types/editor'
9
+ import type {PortableTextMemberSchemaTypes} from '../types/editor'
11
10
 
12
11
  export function getPortableTextMemberSchemaTypes(
13
12
  portableTextType: ArraySchemaType<PortableTextBlock>,
@@ -15,19 +14,23 @@ export function getPortableTextMemberSchemaTypes(
15
14
  if (!portableTextType) {
16
15
  throw new Error("Parameter 'portabletextType' missing (required)")
17
16
  }
18
- const blockType = portableTextType.of?.find(findBlockType) as BlockSchemaType | undefined
17
+ const blockType = portableTextType.of?.find(findBlockType) as
18
+ | BlockSchemaType
19
+ | undefined
19
20
  if (!blockType) {
20
21
  throw new Error('Block type is not defined in this schema (required)')
21
22
  }
22
- const childrenField = blockType.fields?.find((field) => field.name === 'children') as
23
- | {type: ArraySchemaType}
24
- | undefined
23
+ const childrenField = blockType.fields?.find(
24
+ (field) => field.name === 'children',
25
+ ) as {type: ArraySchemaType} | undefined
25
26
  if (!childrenField) {
26
27
  throw new Error('Children field for block type found in schema (required)')
27
28
  }
28
29
  const ofType = childrenField.type.of
29
30
  if (!ofType) {
30
- throw new Error('Valid types for block children not found in schema (required)')
31
+ throw new Error(
32
+ 'Valid types for block children not found in schema (required)',
33
+ )
31
34
  }
32
35
  const spanType = ofType.find((memberType) => memberType.name === 'span') as
33
36
  | ObjectSchemaType
@@ -35,10 +38,12 @@ export function getPortableTextMemberSchemaTypes(
35
38
  if (!spanType) {
36
39
  throw new Error('Span type not found in schema (required)')
37
40
  }
38
- const inlineObjectTypes = (ofType.filter((memberType) => memberType.name !== 'span') ||
39
- []) as ObjectSchemaType[]
40
- const blockObjectTypes = (portableTextType.of?.filter((field) => field.name !== blockType.name) ||
41
- []) as ObjectSchemaType[]
41
+ const inlineObjectTypes = (ofType.filter(
42
+ (memberType) => memberType.name !== 'span',
43
+ ) || []) as ObjectSchemaType[]
44
+ const blockObjectTypes = (portableTextType.of?.filter(
45
+ (field) => field.name !== blockType.name,
46
+ ) || []) as ObjectSchemaType[]
42
47
  return {
43
48
  styles: resolveEnabledStyles(blockType),
44
49
  decorators: resolveEnabledDecorators(spanType),
@@ -53,13 +58,19 @@ export function getPortableTextMemberSchemaTypes(
53
58
  }
54
59
 
55
60
  function resolveEnabledStyles(blockType: ObjectSchemaType) {
56
- const styleField = blockType.fields?.find((btField) => btField.name === 'style')
61
+ const styleField = blockType.fields?.find(
62
+ (btField) => btField.name === 'style',
63
+ )
57
64
  if (!styleField) {
58
- throw new Error("A field with name 'style' is not defined in the block type (required).")
65
+ throw new Error(
66
+ "A field with name 'style' is not defined in the block type (required).",
67
+ )
59
68
  }
60
69
  const textStyles =
61
70
  styleField.type.options?.list &&
62
- styleField.type.options.list?.filter((style: {value: string}) => style.value)
71
+ styleField.type.options.list?.filter(
72
+ (style: {value: string}) => style.value,
73
+ )
63
74
  if (!textStyles || textStyles.length === 0) {
64
75
  throw new Error(
65
76
  'The style fields need at least one style ' +
@@ -74,9 +85,13 @@ function resolveEnabledDecorators(spanType: ObjectSchemaType) {
74
85
  }
75
86
 
76
87
  function resolveEnabledListItems(blockType: ObjectSchemaType) {
77
- const listField = blockType.fields?.find((btField) => btField.name === 'listItem')
88
+ const listField = blockType.fields?.find(
89
+ (btField) => btField.name === 'listItem',
90
+ )
78
91
  if (!listField) {
79
- throw new Error("A field with name 'listItem' is not defined in the block type (required).")
92
+ throw new Error(
93
+ "A field with name 'listItem' is not defined in the block type (required).",
94
+ )
80
95
  }
81
96
  const listItems =
82
97
  listField.type.options?.list &&