@portabletext/editor 1.0.18 → 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 (75) hide show
  1. package/lib/index.d.mts +140 -66
  2. package/lib/index.d.ts +140 -66
  3. package/lib/index.esm.js +1164 -410
  4. package/lib/index.esm.js.map +1 -1
  5. package/lib/index.js +1164 -410
  6. package/lib/index.js.map +1 -1
  7. package/lib/index.mjs +1164 -410
  8. package/lib/index.mjs.map +1 -1
  9. package/package.json +8 -4
  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 -550
  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 -115
  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 +301 -155
  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 -10
  71. package/src/utils/weakMaps.ts +18 -8
  72. package/src/utils/withChanges.ts +4 -2
  73. package/src/editor/plugins/__tests__/withHotkeys.test.tsx +0 -212
  74. package/src/editor/plugins/__tests__/withInsertBreak.test.tsx +0 -220
  75. package/src/editor/plugins/__tests__/withPlaceholderBlock.test.tsx +0 -133
@@ -10,7 +10,6 @@ import {
10
10
  type PortableTextTextBlock,
11
11
  } from '@sanity/types'
12
12
  import {type Descendant, type Operation} from 'slate'
13
-
14
13
  import {type PortableTextSlateEditor} from '../../types/editor'
15
14
  import {diffMatchPatch} from '../applyPatch'
16
15
 
@@ -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
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
  })
@@ -2,7 +2,6 @@ import {beforeEach, describe, expect, it} from '@jest/globals'
2
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
2
  import {type InsertTextOperation, type Range} from 'slate'
3
-
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'
@@ -8,11 +8,11 @@ import {
8
8
  type UnsetPatch,
9
9
  } from '@portabletext/patches'
10
10
  import {
11
- applyPatches as diffMatchPatchApplyPatches,
12
11
  cleanupEfficiency,
13
12
  DIFF_DELETE,
14
13
  DIFF_EQUAL,
15
14
  DIFF_INSERT,
15
+ applyPatches as diffMatchPatchApplyPatches,
16
16
  makeDiff,
17
17
  parsePatch,
18
18
  } from '@sanity/diff-match-patch'
@@ -23,9 +23,18 @@ import {
23
23
  type PortableTextBlock,
24
24
  type PortableTextChild,
25
25
  } 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'
26
+ import {
27
+ Element,
28
+ Text,
29
+ Transforms,
30
+ type Descendant,
31
+ type Node,
32
+ type Path as SlatePath,
33
+ } from 'slate'
34
+ import {
35
+ type PortableTextMemberSchemaTypes,
36
+ type PortableTextSlateEditor,
37
+ } from '../types/editor'
29
38
  import {debugWithName} from './debug'
30
39
  import {toSlateValue} from './values'
31
40
  import {KEY_TO_SLATE_ELEMENT} from './weakMaps'
@@ -46,7 +55,9 @@ export function createApplyPatch(
46
55
 
47
56
  // Save some CPU cycles by not stringifying unless enabled
48
57
  if (debugVerbose) {
49
- debug('\n\nNEW PATCH =============================================================')
58
+ debug(
59
+ '\n\nNEW PATCH =============================================================',
60
+ )
50
61
  debug(JSON.stringify(patch, null, 2))
51
62
  }
52
63
 
@@ -91,7 +102,10 @@ export function diffMatchPatch(
91
102
  >,
92
103
  patch: DiffMatchPatch,
93
104
  ): boolean {
94
- const {block, child, childPath} = findBlockAndChildFromPath(editor, patch.path)
105
+ const {block, child, childPath} = findBlockAndChildFromPath(
106
+ editor,
107
+ patch.path,
108
+ )
95
109
  if (!block) {
96
110
  debug('Block not found')
97
111
  return false
@@ -112,7 +126,9 @@ export function diffMatchPatch(
112
126
  }
113
127
 
114
128
  const patches = parsePatch(patch.value)
115
- const [newValue] = diffMatchPatchApplyPatches(patches, child.text, {allowExceedingIndices: true})
129
+ const [newValue] = diffMatchPatchApplyPatches(patches, child.text, {
130
+ allowExceedingIndices: true,
131
+ })
116
132
  const diff = cleanupEfficiency(makeDiff(child.text, newValue), 5)
117
133
 
118
134
  debugState(editor, 'before')
@@ -160,7 +176,8 @@ function insertPatch(
160
176
  KEY_TO_SLATE_ELEMENT.get(editor),
161
177
  ) as Descendant[]
162
178
  const targetBlockIndex = targetBlockPath[0]
163
- const normalizedIdx = position === 'after' ? targetBlockIndex + 1 : targetBlockIndex
179
+ const normalizedIdx =
180
+ position === 'after' ? targetBlockIndex + 1 : targetBlockIndex
164
181
  debug(`Inserting blocks at path [${normalizedIdx}]`)
165
182
  debugState(editor, 'before')
166
183
  Transforms.insertNodes(editor, blocksToInsert, {at: [normalizedIdx]})
@@ -181,12 +198,15 @@ function insertPatch(
181
198
  KEY_TO_SLATE_ELEMENT.get(editor),
182
199
  )
183
200
  const targetChildIndex = targetChildPath[1]
184
- const normalizedIdx = position === 'after' ? targetChildIndex + 1 : targetChildIndex
201
+ const normalizedIdx =
202
+ position === 'after' ? targetChildIndex + 1 : targetChildIndex
185
203
  const childInsertPath = [targetChildPath[0], normalizedIdx]
186
204
  debug(`Inserting children at path ${childInsertPath}`)
187
205
  debugState(editor, 'before')
188
206
  if (childrenToInsert && Element.isElement(childrenToInsert[0])) {
189
- Transforms.insertNodes(editor, childrenToInsert[0].children, {at: childInsertPath})
207
+ Transforms.insertNodes(editor, childrenToInsert[0].children, {
208
+ at: childInsertPath,
209
+ })
190
210
  }
191
211
  debugState(editor, 'after')
192
212
  return true
@@ -198,7 +218,10 @@ function setPatch(editor: PortableTextSlateEditor, patch: SetPatch) {
198
218
  value = {}
199
219
  value[patch.path[3]] = patch.value
200
220
  }
201
- const {block, blockPath, child, childPath} = findBlockAndChildFromPath(editor, patch.path)
221
+ const {block, blockPath, child, childPath} = findBlockAndChildFromPath(
222
+ editor,
223
+ patch.path,
224
+ )
202
225
 
203
226
  if (!block) {
204
227
  debug('Block not found')
@@ -287,7 +310,11 @@ function setPatch(editor: PortableTextSlateEditor, patch: SetPatch) {
287
310
  return true
288
311
  }
289
312
 
290
- function unsetPatch(editor: PortableTextSlateEditor, patch: UnsetPatch, previousPatch?: Patch) {
313
+ function unsetPatch(
314
+ editor: PortableTextSlateEditor,
315
+ patch: UnsetPatch,
316
+ previousPatch?: Patch,
317
+ ) {
291
318
  // Value
292
319
  if (patch.path.length === 0) {
293
320
  debug('Removing everything')
@@ -297,7 +324,7 @@ function unsetPatch(editor: PortableTextSlateEditor, patch: UnsetPatch, previous
297
324
  editor.children.forEach((c, i) => {
298
325
  Transforms.removeNodes(editor, {at: [i]})
299
326
  })
300
- Transforms.insertNodes(editor, editor.pteCreateEmptyBlock())
327
+ Transforms.insertNodes(editor, editor.pteCreateTextBlock({decorators: []}))
301
328
  if (previousSelection) {
302
329
  Transforms.select(editor, {
303
330
  anchor: {path: [0, 0], offset: 0},
@@ -309,7 +336,10 @@ function unsetPatch(editor: PortableTextSlateEditor, patch: UnsetPatch, previous
309
336
  debugState(editor, 'after')
310
337
  return true
311
338
  }
312
- const {block, blockPath, child, childPath} = findBlockAndChildFromPath(editor, patch.path)
339
+ const {block, blockPath, child, childPath} = findBlockAndChildFromPath(
340
+ editor,
341
+ patch.path,
342
+ )
313
343
 
314
344
  // Single blocks
315
345
  if (patch.path.length === 1) {
@@ -327,7 +357,11 @@ function unsetPatch(editor: PortableTextSlateEditor, patch: UnsetPatch, previous
327
357
  }
328
358
 
329
359
  // Unset on text block children
330
- if (editor.isTextBlock(block) && patch.path[1] === 'children' && patch.path.length === 3) {
360
+ if (
361
+ editor.isTextBlock(block) &&
362
+ patch.path[1] === 'children' &&
363
+ patch.path.length === 3
364
+ ) {
331
365
  if (!child || !childPath) {
332
366
  debug('Child not found')
333
367
  return false
@@ -349,7 +383,10 @@ function isKeyedSegment(segment: PathSegment): segment is KeyedSegment {
349
383
  }
350
384
 
351
385
  function debugState(
352
- editor: Pick<PortableTextSlateEditor, 'children' | 'isTextBlock' | 'apply' | 'selection'>,
386
+ editor: Pick<
387
+ PortableTextSlateEditor,
388
+ 'children' | 'isTextBlock' | 'apply' | 'selection'
389
+ >,
353
390
  stateName: string,
354
391
  ) {
355
392
  if (!debugVerbose) {
@@ -369,7 +406,9 @@ function findBlockFromPath(
369
406
  ): {block?: Descendant; path?: SlatePath} {
370
407
  let blockIndex = -1
371
408
  const block = editor.children.find((node: Descendant, index: number) => {
372
- const isMatch = isKeyedSegment(path[0]) ? node._key === path[0]._key : index === path[0]
409
+ const isMatch = isKeyedSegment(path[0])
410
+ ? node._key === path[0]._key
411
+ : index === path[0]
373
412
  if (isMatch) {
374
413
  blockIndex = index
375
414
  }
@@ -387,14 +426,21 @@ function findBlockAndChildFromPath(
387
426
  'children' | 'isTextBlock' | 'apply' | 'selection' | 'onChange'
388
427
  >,
389
428
  path: Path,
390
- ): {child?: Descendant; childPath?: SlatePath; block?: Descendant; blockPath?: SlatePath} {
429
+ ): {
430
+ child?: Descendant
431
+ childPath?: SlatePath
432
+ block?: Descendant
433
+ blockPath?: SlatePath
434
+ } {
391
435
  const {block, path: blockPath} = findBlockFromPath(editor, path)
392
436
  if (!(Element.isElement(block) && path[1] === 'children')) {
393
437
  return {block, blockPath, child: undefined, childPath: undefined}
394
438
  }
395
439
  let childIndex = -1
396
440
  const child = block.children.find((node, index: number) => {
397
- const isMatch = isKeyedSegment(path[2]) ? node._key === path[2]._key : index === path[2]
441
+ const isMatch = isKeyedSegment(path[2])
442
+ ? node._key === path[2]._key
443
+ : index === path[2]
398
444
  if (isMatch) {
399
445
  childIndex = index
400
446
  }
@@ -403,5 +449,10 @@ function findBlockAndChildFromPath(
403
449
  if (!child) {
404
450
  return {block, blockPath, child: undefined, childPath: undefined}
405
451
  }
406
- return {block, child, blockPath, childPath: blockPath?.concat(childIndex) as SlatePath}
452
+ return {
453
+ block,
454
+ child,
455
+ blockPath,
456
+ childPath: blockPath?.concat(childIndex) as SlatePath,
457
+ }
407
458
  }
@@ -6,7 +6,6 @@ import {
6
6
  type SchemaType,
7
7
  type SpanSchemaType,
8
8
  } from '@sanity/types'
9
-
10
9
  import {type PortableTextMemberSchemaTypes} from '../types/editor'
11
10
 
12
11
  export function getPortableTextMemberSchemaTypes(
@@ -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 &&