@portabletext/editor 2.9.1 → 2.10.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 (53) hide show
  1. package/lib/_chunks-cjs/selector.is-selecting-entire-blocks.cjs +9 -1
  2. package/lib/_chunks-cjs/selector.is-selecting-entire-blocks.cjs.map +1 -1
  3. package/lib/_chunks-cjs/util.merge-text-blocks.cjs +1 -0
  4. package/lib/_chunks-cjs/util.merge-text-blocks.cjs.map +1 -1
  5. package/lib/_chunks-cjs/util.slice-blocks.cjs +6 -1
  6. package/lib/_chunks-cjs/util.slice-blocks.cjs.map +1 -1
  7. package/lib/_chunks-dts/behavior.types.action.d.cts +141 -131
  8. package/lib/_chunks-dts/behavior.types.action.d.ts +71 -61
  9. package/lib/_chunks-es/selector.is-selecting-entire-blocks.js +9 -1
  10. package/lib/_chunks-es/selector.is-selecting-entire-blocks.js.map +1 -1
  11. package/lib/_chunks-es/util.merge-text-blocks.js +1 -0
  12. package/lib/_chunks-es/util.merge-text-blocks.js.map +1 -1
  13. package/lib/_chunks-es/util.slice-blocks.js +6 -1
  14. package/lib/_chunks-es/util.slice-blocks.js.map +1 -1
  15. package/lib/index.cjs +419 -309
  16. package/lib/index.cjs.map +1 -1
  17. package/lib/index.js +422 -312
  18. package/lib/index.js.map +1 -1
  19. package/lib/plugins/index.d.cts +3 -3
  20. package/lib/plugins/index.d.ts +3 -3
  21. package/lib/utils/index.d.ts +2 -2
  22. package/package.json +8 -8
  23. package/src/behaviors/behavior.abstract.insert.ts +109 -24
  24. package/src/behaviors/behavior.abstract.split.ts +1 -0
  25. package/src/behaviors/behavior.perform-event.ts +84 -118
  26. package/src/behaviors/behavior.types.event.ts +9 -1
  27. package/src/converters/converter.portable-text.ts +1 -0
  28. package/src/converters/converter.text-html.ts +1 -0
  29. package/src/converters/converter.text-plain.ts +1 -0
  30. package/src/editor/Editable.tsx +1 -0
  31. package/src/editor/editor-selector.ts +10 -1
  32. package/src/editor/plugins/create-with-event-listeners.ts +13 -14
  33. package/src/editor/sync-machine.ts +9 -0
  34. package/src/editor/with-performing-behavior-operation.ts +21 -0
  35. package/src/editor/without-normalizing-conditional.ts +13 -0
  36. package/src/internal-utils/parse-blocks.test.ts +14 -14
  37. package/src/internal-utils/parse-blocks.ts +9 -2
  38. package/src/internal-utils/slate-utils.test.tsx +119 -0
  39. package/src/internal-utils/slate-utils.ts +14 -1
  40. package/src/internal-utils/text-marks.ts +1 -1
  41. package/src/internal-utils/values.ts +1 -55
  42. package/src/operations/behavior.operation.block.set.ts +18 -36
  43. package/src/operations/behavior.operation.block.unset.ts +8 -2
  44. package/src/operations/behavior.operation.insert.block.ts +4 -1
  45. package/src/operations/behavior.operation.insert.child.ts +95 -0
  46. package/src/operations/behavior.operations.ts +140 -128
  47. package/src/selectors/selector.get-mark-state.ts +19 -5
  48. package/src/selectors/selector.get-trimmed-selection.test.ts +1 -0
  49. package/src/types/block-with-optional-key.ts +13 -1
  50. package/src/utils/util.merge-text-blocks.ts +1 -1
  51. package/src/utils/util.slice-blocks.ts +3 -3
  52. package/src/utils/util.slice-text-block.test.ts +54 -28
  53. package/src/editor/with-applying-behavior-operations.ts +0 -18
@@ -1,9 +1,8 @@
1
1
  import {Editor} from 'slate'
2
2
  import {slateRangeToSelection} from '../../internal-utils/slate-utils'
3
- import {insertTextOperationImplementation} from '../../operations/behavior.operation.insert.text'
4
3
  import {performOperation} from '../../operations/behavior.operations'
5
4
  import type {EditorActor} from '../editor-machine'
6
- import {isApplyingBehaviorOperations} from '../with-applying-behavior-operations'
5
+ import {isPerformingBehaviorOperation} from '../with-performing-behavior-operation'
7
6
 
8
7
  export function createWithEventListeners(editorActor: EditorActor) {
9
8
  return function withEventListeners(editor: Editor) {
@@ -14,7 +13,7 @@ export function createWithEventListeners(editorActor: EditorActor) {
14
13
  const {delete: editorDelete, select} = editor
15
14
 
16
15
  editor.delete = (options) => {
17
- if (isApplyingBehaviorOperations(editor)) {
16
+ if (isPerformingBehaviorOperation(editor)) {
18
17
  editorDelete(options)
19
18
  return
20
19
  }
@@ -54,7 +53,7 @@ export function createWithEventListeners(editorActor: EditorActor) {
54
53
  }
55
54
 
56
55
  editor.deleteBackward = (unit) => {
57
- if (isApplyingBehaviorOperations(editor)) {
56
+ if (isPerformingBehaviorOperation(editor)) {
58
57
  console.error('Unexpected call to .deleteBackward(...)')
59
58
  return
60
59
  }
@@ -71,7 +70,7 @@ export function createWithEventListeners(editorActor: EditorActor) {
71
70
  }
72
71
 
73
72
  editor.deleteForward = (unit) => {
74
- if (isApplyingBehaviorOperations(editor)) {
73
+ if (isPerformingBehaviorOperation(editor)) {
75
74
  console.error('Unexpected call to .deleteForward(...)')
76
75
  return
77
76
  }
@@ -88,7 +87,7 @@ export function createWithEventListeners(editorActor: EditorActor) {
88
87
  }
89
88
 
90
89
  editor.insertBreak = () => {
91
- if (isApplyingBehaviorOperations(editor)) {
90
+ if (isPerformingBehaviorOperation(editor)) {
92
91
  console.error('Unexpected call to .insertBreak(...)')
93
92
  return
94
93
  }
@@ -104,7 +103,7 @@ export function createWithEventListeners(editorActor: EditorActor) {
104
103
  }
105
104
 
106
105
  editor.insertData = (dataTransfer) => {
107
- if (isApplyingBehaviorOperations(editor)) {
106
+ if (isPerformingBehaviorOperation(editor)) {
108
107
  console.error('Unexpected call to .insertData(...)')
109
108
  return
110
109
  }
@@ -122,8 +121,8 @@ export function createWithEventListeners(editorActor: EditorActor) {
122
121
  }
123
122
 
124
123
  editor.insertSoftBreak = () => {
125
- if (isApplyingBehaviorOperations(editor)) {
126
- insertTextOperationImplementation({
124
+ if (isPerformingBehaviorOperation(editor)) {
125
+ performOperation({
127
126
  context: {
128
127
  keyGenerator: editorActor.getSnapshot().context.keyGenerator,
129
128
  schema: editorActor.getSnapshot().context.schema,
@@ -144,8 +143,8 @@ export function createWithEventListeners(editorActor: EditorActor) {
144
143
  }
145
144
 
146
145
  editor.insertText = (text) => {
147
- if (isApplyingBehaviorOperations(editor)) {
148
- insertTextOperationImplementation({
146
+ if (isPerformingBehaviorOperation(editor)) {
147
+ performOperation({
149
148
  context: {
150
149
  keyGenerator: editorActor.getSnapshot().context.keyGenerator,
151
150
  schema: editorActor.getSnapshot().context.schema,
@@ -167,7 +166,7 @@ export function createWithEventListeners(editorActor: EditorActor) {
167
166
  }
168
167
 
169
168
  editor.redo = () => {
170
- if (isApplyingBehaviorOperations(editor)) {
169
+ if (isPerformingBehaviorOperation(editor)) {
171
170
  performOperation({
172
171
  context: {
173
172
  keyGenerator: editorActor.getSnapshot().context.keyGenerator,
@@ -192,7 +191,7 @@ export function createWithEventListeners(editorActor: EditorActor) {
192
191
  }
193
192
 
194
193
  editor.select = (location) => {
195
- if (isApplyingBehaviorOperations(editor)) {
194
+ if (isPerformingBehaviorOperation(editor)) {
196
195
  select(location)
197
196
  return
198
197
  }
@@ -220,7 +219,7 @@ export function createWithEventListeners(editorActor: EditorActor) {
220
219
  }
221
220
 
222
221
  editor.undo = () => {
223
- if (isApplyingBehaviorOperations(editor)) {
222
+ if (isPerformingBehaviorOperation(editor)) {
224
223
  performOperation({
225
224
  context: {
226
225
  keyGenerator: editorActor.getSnapshot().context.keyGenerator,
@@ -507,6 +507,10 @@ async function updateValue({
507
507
 
508
508
  isChanged = blockChanged || isChanged
509
509
  isValid = isValid && blockValid
510
+
511
+ if (!isValid) {
512
+ break
513
+ }
510
514
  }
511
515
 
512
516
  resolve()
@@ -544,6 +548,11 @@ async function updateValue({
544
548
 
545
549
  isChanged = blockChanged || isChanged
546
550
  isValid = isValid && blockValid
551
+
552
+ if (!blockValid) {
553
+ break
554
+ }
555
+
547
556
  index++
548
557
  }
549
558
  })
@@ -0,0 +1,21 @@
1
+ import type {Editor} from 'slate'
2
+
3
+ const IS_PERFORMING_OPERATION: WeakMap<Editor, boolean | undefined> =
4
+ new WeakMap()
5
+
6
+ export function withPerformingBehaviorOperation(
7
+ editor: Editor,
8
+ fn: () => void,
9
+ ) {
10
+ const prev = IS_PERFORMING_OPERATION.get(editor)
11
+
12
+ IS_PERFORMING_OPERATION.set(editor, true)
13
+
14
+ fn()
15
+
16
+ IS_PERFORMING_OPERATION.set(editor, prev)
17
+ }
18
+
19
+ export function isPerformingBehaviorOperation(editor: Editor) {
20
+ return IS_PERFORMING_OPERATION.get(editor) ?? false
21
+ }
@@ -0,0 +1,13 @@
1
+ import {Editor} from 'slate'
2
+
3
+ export function withoutNormalizingConditional(
4
+ editor: Editor,
5
+ predicate: () => boolean,
6
+ fn: () => void,
7
+ ) {
8
+ if (predicate()) {
9
+ Editor.withoutNormalizing(editor, fn)
10
+ } else {
11
+ fn()
12
+ }
13
+ }
@@ -12,7 +12,7 @@ describe(parseBlock.name, () => {
12
12
  keyGenerator: createTestKeyGenerator(),
13
13
  schema: compileSchema(defineSchema({})),
14
14
  },
15
- options: {validateFields: true},
15
+ options: {removeUnusedMarkDefs: true, validateFields: true},
16
16
  }),
17
17
  ).toBe(undefined)
18
18
  })
@@ -25,7 +25,7 @@ describe(parseBlock.name, () => {
25
25
  keyGenerator: createTestKeyGenerator(),
26
26
  schema: compileSchema(defineSchema({})),
27
27
  },
28
- options: {validateFields: true},
28
+ options: {removeUnusedMarkDefs: true, validateFields: true},
29
29
  }),
30
30
  ).toBe(undefined)
31
31
  })
@@ -39,7 +39,7 @@ describe(parseBlock.name, () => {
39
39
  keyGenerator: createTestKeyGenerator(),
40
40
  schema: compileSchema(defineSchema({})),
41
41
  },
42
- options: {validateFields: true},
42
+ options: {removeUnusedMarkDefs: true, validateFields: true},
43
43
  }),
44
44
  ).toBe(undefined)
45
45
  })
@@ -54,7 +54,7 @@ describe(parseBlock.name, () => {
54
54
  defineSchema({blockObjects: [{name: 'image'}]}),
55
55
  ),
56
56
  },
57
- options: {validateFields: true},
57
+ options: {removeUnusedMarkDefs: true, validateFields: true},
58
58
  }),
59
59
  ).toBe(undefined)
60
60
  })
@@ -69,7 +69,7 @@ describe(parseBlock.name, () => {
69
69
  defineSchema({blockObjects: [{name: 'image'}]}),
70
70
  ),
71
71
  },
72
- options: {validateFields: true},
72
+ options: {removeUnusedMarkDefs: true, validateFields: true},
73
73
  }),
74
74
  ).toEqual({
75
75
  _key: 'k0',
@@ -87,7 +87,7 @@ describe(parseBlock.name, () => {
87
87
  keyGenerator: createTestKeyGenerator(),
88
88
  schema: compileSchema(defineSchema({})),
89
89
  },
90
- options: {validateFields: true},
90
+ options: {removeUnusedMarkDefs: true, validateFields: true},
91
91
  }),
92
92
  ).toEqual({
93
93
  _key: 'k0',
@@ -114,7 +114,7 @@ describe(parseBlock.name, () => {
114
114
  keyGenerator: createTestKeyGenerator(),
115
115
  schema: {...schema, block: {...schema.block, name: 'text'}},
116
116
  },
117
- options: {validateFields: true},
117
+ options: {removeUnusedMarkDefs: true, validateFields: true},
118
118
  }),
119
119
  ).toEqual({
120
120
  _key: 'k0',
@@ -149,7 +149,7 @@ describe(parseBlock.name, () => {
149
149
  keyGenerator: createTestKeyGenerator(),
150
150
  schema: compileSchema(defineSchema({})),
151
151
  },
152
- options: {validateFields: true},
152
+ options: {removeUnusedMarkDefs: true, validateFields: true},
153
153
  }),
154
154
  ).toBe(undefined)
155
155
  })
@@ -188,7 +188,7 @@ describe(parseBlock.name, () => {
188
188
  }),
189
189
  ),
190
190
  },
191
- options: {validateFields: true},
191
+ options: {removeUnusedMarkDefs: true, validateFields: true},
192
192
  }),
193
193
  ).toEqual({
194
194
  _key: 'k0',
@@ -236,7 +236,7 @@ describe(parseBlock.name, () => {
236
236
  keyGenerator: createTestKeyGenerator(),
237
237
  schema: compileSchema(defineSchema({lists: [{name: 'bullet'}]})),
238
238
  },
239
- options: {validateFields: true},
239
+ options: {removeUnusedMarkDefs: true, validateFields: true},
240
240
  }),
241
241
  ).toEqual({
242
242
  _key: 'k0',
@@ -263,7 +263,7 @@ describe(parseBlock.name, () => {
263
263
  keyGenerator: createTestKeyGenerator(),
264
264
  schema: compileSchema(defineSchema({lists: [{name: 'bullet'}]})),
265
265
  },
266
- options: {validateFields: true},
266
+ options: {removeUnusedMarkDefs: true, validateFields: true},
267
267
  }),
268
268
  ).toEqual({
269
269
  _key: 'k0',
@@ -290,7 +290,7 @@ describe(parseBlock.name, () => {
290
290
  keyGenerator: createTestKeyGenerator(),
291
291
  schema: compileSchema(defineSchema({})),
292
292
  },
293
- options: {validateFields: true},
293
+ options: {removeUnusedMarkDefs: true, validateFields: true},
294
294
  }),
295
295
  ).toEqual({
296
296
  _type: 'block',
@@ -320,7 +320,7 @@ describe(parseBlock.name, () => {
320
320
  }),
321
321
  ),
322
322
  },
323
- options: {validateFields: true},
323
+ options: {removeUnusedMarkDefs: true, validateFields: true},
324
324
  }),
325
325
  ).toEqual({
326
326
  _type: 'block',
@@ -351,7 +351,7 @@ describe(parseBlock.name, () => {
351
351
  }),
352
352
  ),
353
353
  },
354
- options: {validateFields: true},
354
+ options: {removeUnusedMarkDefs: true, validateFields: true},
355
355
  }),
356
356
  ).toEqual({
357
357
  _type: 'block',
@@ -19,6 +19,7 @@ export function parseBlocks({
19
19
  context: Pick<EditorContext, 'keyGenerator' | 'schema'>
20
20
  blocks: unknown
21
21
  options: {
22
+ removeUnusedMarkDefs: boolean
22
23
  validateFields: boolean
23
24
  }
24
25
  }): Array<PortableTextBlock> {
@@ -41,6 +42,7 @@ export function parseBlock({
41
42
  context: Pick<EditorContext, 'keyGenerator' | 'schema'>
42
43
  block: unknown
43
44
  options: {
45
+ removeUnusedMarkDefs: boolean
44
46
  validateFields: boolean
45
47
  }
46
48
  }): PortableTextBlock | undefined {
@@ -99,7 +101,10 @@ export function parseTextBlock({
99
101
  }: {
100
102
  block: unknown
101
103
  context: Pick<EditorContext, 'keyGenerator' | 'schema'>
102
- options: {validateFields: boolean}
104
+ options: {
105
+ removeUnusedMarkDefs: boolean
106
+ validateFields: boolean
107
+ }
103
108
  }): PortableTextTextBlock | undefined {
104
109
  if (!isTypedObject(block)) {
105
110
  return undefined
@@ -204,7 +209,9 @@ export function parseTextBlock({
204
209
  marks: [],
205
210
  },
206
211
  ],
207
- markDefs: markDefs.filter((markDef) => marks.includes(markDef._key)),
212
+ markDefs: options.removeUnusedMarkDefs
213
+ ? markDefs.filter((markDef) => marks.includes(markDef._key))
214
+ : markDefs,
208
215
  ...customFields,
209
216
  }
210
217
 
@@ -0,0 +1,119 @@
1
+ import {defineSchema} from '@portabletext/schema'
2
+ import {createTestKeyGenerator} from '@portabletext/test'
3
+ import React from 'react'
4
+ import {describe, expect, test} from 'vitest'
5
+ import {InternalSlateEditorRefPlugin} from '../plugins/plugin.internal.slate-editor-ref'
6
+ import type {PortableTextSlateEditor} from '../types/editor'
7
+ import {getFocusSpan} from './slate-utils'
8
+ import {createTestEditor} from './test-editor'
9
+
10
+ describe(getFocusSpan.name, () => {
11
+ const keyGenerator = createTestKeyGenerator()
12
+ const blockKey = keyGenerator()
13
+ const fooSpanKey = keyGenerator()
14
+ const stockTickerKey = keyGenerator()
15
+ const barSpanKey = keyGenerator()
16
+ const imageKey = keyGenerator()
17
+ const initialValue = [
18
+ {
19
+ _key: blockKey,
20
+ _type: 'block',
21
+ children: [
22
+ {
23
+ _type: 'span',
24
+ _key: fooSpanKey,
25
+ text: 'foo',
26
+ },
27
+ {
28
+ _type: 'stock-ticker',
29
+ _key: stockTickerKey,
30
+ },
31
+ {
32
+ _type: 'span',
33
+ _key: barSpanKey,
34
+ text: 'bar',
35
+ },
36
+ ],
37
+ },
38
+ {
39
+ _key: imageKey,
40
+ _type: 'image',
41
+ },
42
+ ]
43
+ const schemaDefinition = defineSchema({
44
+ blockObjects: [{name: 'image'}],
45
+ inlineObjects: [{name: 'stock-ticker'}],
46
+ })
47
+
48
+ test('Scenario: Text block span is selected', async () => {
49
+ const slateEditorRef = React.createRef<PortableTextSlateEditor>()
50
+ await createTestEditor({
51
+ children: <InternalSlateEditorRefPlugin ref={slateEditorRef} />,
52
+ schemaDefinition,
53
+ initialValue,
54
+ editableProps: {
55
+ selection: {
56
+ anchor: {
57
+ path: [{_key: blockKey}, 'children', {_key: fooSpanKey}],
58
+ offset: 0,
59
+ },
60
+ focus: {
61
+ path: [{_key: blockKey}, 'children', {_key: fooSpanKey}],
62
+ offset: 0,
63
+ },
64
+ },
65
+ },
66
+ })
67
+
68
+ expect(getFocusSpan({editor: slateEditorRef.current!})).toEqual([
69
+ {_type: 'span', _key: fooSpanKey, text: 'foo', marks: []},
70
+ [0, 0],
71
+ ])
72
+ })
73
+
74
+ test('Scenario: Inline object is selected', async () => {
75
+ const slateEditorRef = React.createRef<PortableTextSlateEditor>()
76
+ await createTestEditor({
77
+ children: <InternalSlateEditorRefPlugin ref={slateEditorRef} />,
78
+ schemaDefinition,
79
+ initialValue,
80
+ editableProps: {
81
+ selection: {
82
+ anchor: {
83
+ path: [{_key: blockKey}, 'children', {_key: stockTickerKey}],
84
+ offset: 0,
85
+ },
86
+ focus: {
87
+ path: [{_key: blockKey}, 'children', {_key: stockTickerKey}],
88
+ offset: 0,
89
+ },
90
+ },
91
+ },
92
+ })
93
+
94
+ expect(getFocusSpan({editor: slateEditorRef.current!})).toEqual([
95
+ undefined,
96
+ undefined,
97
+ ])
98
+ })
99
+
100
+ test('Scenario: Block object is selected', async () => {
101
+ const slateEditorRef = React.createRef<PortableTextSlateEditor>()
102
+ await createTestEditor({
103
+ children: <InternalSlateEditorRefPlugin ref={slateEditorRef} />,
104
+ schemaDefinition,
105
+ initialValue,
106
+ editableProps: {
107
+ selection: {
108
+ anchor: {path: [{_key: imageKey}], offset: 0},
109
+ focus: {path: [{_key: imageKey}], offset: 0},
110
+ },
111
+ },
112
+ })
113
+
114
+ expect(getFocusSpan({editor: slateEditorRef.current!})).toEqual([
115
+ undefined,
116
+ undefined,
117
+ ])
118
+ })
119
+ })
@@ -79,7 +79,20 @@ export function getFocusSpan({
79
79
  }
80
80
 
81
81
  try {
82
- const [node, path] = Editor.node(editor, editor.selection.focus.path)
82
+ const [focusBlock] = getFocusBlock({editor})
83
+
84
+ if (!focusBlock) {
85
+ return [undefined, undefined]
86
+ }
87
+
88
+ if (!editor.isTextBlock(focusBlock)) {
89
+ return [undefined, undefined]
90
+ }
91
+
92
+ const [node, path] = Editor.node(
93
+ editor,
94
+ editor.selection.focus.path.slice(0, 2),
95
+ )
83
96
 
84
97
  if (editor.isTextSpan(node)) {
85
98
  return [node, path]
@@ -5,7 +5,7 @@ export function getTextMarks(
5
5
  context: Pick<EditorContext, 'schema' | 'value'>,
6
6
  text: string,
7
7
  ) {
8
- let marks: Array<string> | undefined
8
+ let marks: Array<string> = []
9
9
 
10
10
  for (const block of context.value) {
11
11
  if (isTextBlock(context, block)) {
@@ -1,22 +1,17 @@
1
1
  import type {
2
- PathSegment,
3
2
  PortableTextBlock,
4
3
  PortableTextChild,
5
4
  PortableTextObject,
6
5
  PortableTextTextBlock,
7
6
  } from '@sanity/types'
8
7
  import {isEqual} from 'lodash'
9
- import {Element, Text, type Descendant, type Node} from 'slate'
8
+ import {Element, Text, type Descendant} from 'slate'
10
9
  import type {EditorSchema} from '../editor/editor-schema'
11
10
 
12
11
  export const EMPTY_MARKDEFS: PortableTextObject[] = []
13
12
 
14
13
  export const VOID_CHILD_KEY = 'void-child'
15
14
 
16
- type Partial<T> = {
17
- [P in keyof T]?: T[P]
18
- }
19
-
20
15
  function keepObjectEquality(
21
16
  object: PortableTextBlock | PortableTextChild,
22
17
  keyMap: Record<string, PortableTextBlock | PortableTextChild>,
@@ -188,52 +183,3 @@ export function isEqualToEmptyEditor(
188
183
  children[0].children[0].text === '')
189
184
  )
190
185
  }
191
-
192
- export function findBlockAndIndexFromPath(
193
- firstPathSegment: PathSegment,
194
- children: (Node | Partial<Node>)[],
195
- ): [Element | undefined, number | undefined] {
196
- let blockIndex = -1
197
- const isNumber = Number.isInteger(Number(firstPathSegment))
198
- if (isNumber) {
199
- blockIndex = Number(firstPathSegment)
200
- } else if (children) {
201
- blockIndex = children.findIndex(
202
- (blk) =>
203
- Element.isElement(blk) && isEqual({_key: blk._key}, firstPathSegment),
204
- )
205
- }
206
- if (blockIndex > -1) {
207
- return [children[blockIndex] as Element, blockIndex]
208
- }
209
- return [undefined, -1]
210
- }
211
-
212
- export function findChildAndIndexFromPath(
213
- secondPathSegment: PathSegment,
214
- block: Element,
215
- ): [Element | Text | undefined, number] {
216
- let childIndex = -1
217
- const isNumber = Number.isInteger(Number(secondPathSegment))
218
- if (isNumber) {
219
- childIndex = Number(secondPathSegment)
220
- } else {
221
- childIndex = block.children.findIndex((child) =>
222
- isEqual({_key: child._key}, secondPathSegment),
223
- )
224
- }
225
- if (childIndex > -1) {
226
- return [block.children[childIndex] as Element | Text, childIndex]
227
- }
228
- return [undefined, -1]
229
- }
230
-
231
- export function getValueOrInitialValue(
232
- value: unknown,
233
- initialValue: PortableTextBlock[],
234
- ): PortableTextBlock[] | undefined {
235
- if (value && Array.isArray(value) && value.length > 0) {
236
- return value
237
- }
238
- return initialValue
239
- }
@@ -1,64 +1,46 @@
1
- import {Editor, Transforms, type Element as SlateElement} from 'slate'
1
+ import {Transforms, type Element as SlateElement} from 'slate'
2
2
  import {parseBlock} from '../internal-utils/parse-blocks'
3
- import {toSlateRange} from '../internal-utils/to-slate-range'
4
- import {fromSlateValue, toSlateValue} from '../internal-utils/values'
5
- import {KEY_TO_VALUE_ELEMENT} from '../internal-utils/weakMaps'
3
+ import {toSlateValue} from '../internal-utils/values'
6
4
  import type {BehaviorOperationImplementation} from './behavior.operations'
7
5
 
8
6
  export const blockSetOperationImplementation: BehaviorOperationImplementation<
9
7
  'block.set'
10
8
  > = ({context, operation}) => {
11
- const location = toSlateRange({
12
- context: {
13
- schema: context.schema,
14
- value: operation.editor.value,
15
- selection: {
16
- anchor: {path: operation.at, offset: 0},
17
- focus: {path: operation.at, offset: 0},
18
- },
19
- },
20
- blockIndexMap: operation.editor.blockIndexMap,
21
- })
9
+ const blockIndex = operation.editor.blockIndexMap.get(operation.at[0]._key)
22
10
 
23
- if (!location) {
11
+ if (blockIndex === undefined) {
24
12
  throw new Error(
25
- `Unable to convert ${JSON.stringify(operation.at)} into a Slate Range`,
13
+ `Unable to find block index for block at ${JSON.stringify(operation.at)}`,
26
14
  )
27
15
  }
28
16
 
29
- const blockEntry = Editor.node(operation.editor, location, {depth: 1})
30
- const block = blockEntry?.[0]
17
+ const block = operation.editor.value.at(blockIndex)
31
18
 
32
19
  if (!block) {
33
20
  throw new Error(`Unable to find block at ${JSON.stringify(operation.at)}`)
34
21
  }
35
22
 
36
- const parsedBlock = fromSlateValue(
37
- [block],
38
- context.schema.block.name,
39
- KEY_TO_VALUE_ELEMENT.get(operation.editor),
40
- ).at(0)
23
+ const {_type, ...filteredProps} = operation.props
41
24
 
42
- if (!parsedBlock) {
43
- throw new Error(`Unable to parse block at ${JSON.stringify(operation.at)}`)
25
+ const updatedBlock = {
26
+ ...block,
27
+ ...filteredProps,
44
28
  }
45
29
 
46
- const {_type, ...filteredProps} = operation.props
47
-
48
- const updatedBlock = parseBlock({
30
+ const parsedBlock = parseBlock({
49
31
  context,
50
- block: {
51
- ...parsedBlock,
52
- ...filteredProps,
32
+ block: updatedBlock,
33
+ options: {
34
+ removeUnusedMarkDefs: false,
35
+ validateFields: true,
53
36
  },
54
- options: {validateFields: true},
55
37
  })
56
38
 
57
- if (!updatedBlock) {
39
+ if (!parsedBlock) {
58
40
  throw new Error(`Unable to update block at ${JSON.stringify(operation.at)}`)
59
41
  }
60
42
 
61
- const slateBlock = toSlateValue([updatedBlock], {
43
+ const slateBlock = toSlateValue([parsedBlock], {
62
44
  schemaTypes: context.schema,
63
45
  })?.at(0) as SlateElement | undefined
64
46
 
@@ -66,5 +48,5 @@ export const blockSetOperationImplementation: BehaviorOperationImplementation<
66
48
  throw new Error(`Unable to convert block to Slate value`)
67
49
  }
68
50
 
69
- Transforms.setNodes(operation.editor, slateBlock, {at: location})
51
+ Transforms.setNodes(operation.editor, slateBlock, {at: [blockIndex]})
70
52
  }
@@ -51,7 +51,10 @@ export const blockUnsetOperationImplementation: BehaviorOperationImplementation<
51
51
  const updatedTextBlock = parseBlock({
52
52
  context,
53
53
  block: omit(parsedBlock, propsToRemove),
54
- options: {validateFields: true},
54
+ options: {
55
+ removeUnusedMarkDefs: true,
56
+ validateFields: true,
57
+ },
55
58
  })
56
59
 
57
60
  if (!updatedTextBlock) {
@@ -81,7 +84,10 @@ export const blockUnsetOperationImplementation: BehaviorOperationImplementation<
81
84
  parsedBlock,
82
85
  operation.props.filter((prop) => prop !== '_type'),
83
86
  ),
84
- options: {validateFields: true},
87
+ options: {
88
+ removeUnusedMarkDefs: true,
89
+ validateFields: true,
90
+ },
85
91
  })
86
92
 
87
93
  if (!updatedBlockObject) {
@@ -24,7 +24,10 @@ export const insertBlockOperationImplementation: BehaviorOperationImplementation
24
24
  const parsedBlock = parseBlock({
25
25
  block: operation.block,
26
26
  context,
27
- options: {validateFields: true},
27
+ options: {
28
+ removeUnusedMarkDefs: true,
29
+ validateFields: true,
30
+ },
28
31
  })
29
32
 
30
33
  if (!parsedBlock) {