@portabletext/editor 3.0.8 → 3.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.
@@ -115,7 +115,7 @@ declare function getTextBlockText(block: PortableTextTextBlock): string;
115
115
  /**
116
116
  * @public
117
117
  */
118
- declare function isEmptyTextBlock(context: Pick<EditorContext, 'schema'>, block: PortableTextBlock): boolean;
118
+ declare function isEmptyTextBlock(context: Pick<EditorContext, 'schema'>, block: PortableTextBlock | unknown): boolean;
119
119
  /**
120
120
  * @public
121
121
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@portabletext/editor",
3
- "version": "3.0.8",
3
+ "version": "3.1.0",
4
4
  "description": "Portable Text Editor made in React",
5
5
  "keywords": [
6
6
  "sanity",
@@ -145,7 +145,7 @@ export type SyntheticBehaviorEvent =
145
145
  /**
146
146
  * Defaults to character deletion.
147
147
  */
148
- unit?: 'character' | 'word' | 'line' | 'block'
148
+ unit?: 'character' | 'word' | 'line' | 'block' | 'child'
149
149
  }
150
150
  | {
151
151
  type: StrictExtract<SyntheticBehaviorEventType, 'history.redo'>
@@ -25,7 +25,6 @@ import {getEventPosition} from '../internal-utils/event-position'
25
25
  import {normalizeSelection} from '../internal-utils/selection'
26
26
  import {slateRangeToSelection} from '../internal-utils/slate-utils'
27
27
  import {toSlateRange} from '../internal-utils/to-slate-range'
28
- import {isEqualToEmptyEditor} from '../internal-utils/values'
29
28
  import type {
30
29
  EditorSelection,
31
30
  OnCopyFn,
@@ -41,6 +40,7 @@ import type {
41
40
  ScrollSelectionIntoViewFunction,
42
41
  } from '../types/editor'
43
42
  import type {HotkeyOptions} from '../types/options'
43
+ import {isEmptyTextBlock} from '../utils'
44
44
  import {parseBlocks} from '../utils/parse-blocks'
45
45
  import {RenderElement} from './components/render-element'
46
46
  import {RenderLeaf} from './components/render-leaf'
@@ -526,9 +526,10 @@ export const PortableTextEditable = forwardRef<
526
526
 
527
527
  if (
528
528
  !slateEditor.selection &&
529
- isEqualToEmptyEditor(
530
- slateEditor.children,
531
- editorActor.getSnapshot().context.schema,
529
+ slateEditor.children.length === 1 &&
530
+ isEmptyTextBlock(
531
+ editorActor.getSnapshot().context,
532
+ slateEditor.value.at(0),
532
533
  )
533
534
  ) {
534
535
  Transforms.select(slateEditor, Editor.start(slateEditor, []))
@@ -350,6 +350,20 @@ export class PortableTextEditor extends Component<
350
350
  editor.editable?.blur()
351
351
  }
352
352
 
353
+ /**
354
+ * @deprecated
355
+ * Use `editor.send(...)` instead
356
+ *
357
+ * ```
358
+ * const editor = useEditor()
359
+ * editor.send({
360
+ * type: 'delete',
361
+ * at: {...},
362
+ * direction: '...',
363
+ * unit: '...',
364
+ * })
365
+ * ```
366
+ */
353
367
  static delete = (
354
368
  editor: PortableTextEditor,
355
369
  selection: EditorSelection,
@@ -5,12 +5,9 @@ import type {
5
5
  PortableTextChild,
6
6
  PortableTextObject,
7
7
  } from '@sanity/types'
8
- import {Editor, Range, Element as SlateElement, Text, Transforms} from 'slate'
8
+ import {Editor, Range, Text, Transforms} from 'slate'
9
9
  import type {DOMNode} from 'slate-dom'
10
10
  import {ReactEditor} from 'slate-react'
11
- import {buildIndexMaps} from '../../internal-utils/build-index-maps'
12
- import {createPlaceholderBlock} from '../../internal-utils/create-placeholder-block'
13
- import {debugWithName} from '../../internal-utils/debug'
14
11
  import {
15
12
  isListItemActive,
16
13
  isStyleActive,
@@ -37,8 +34,6 @@ import type {EditorActor} from '../editor-machine'
37
34
  import {getEditorSnapshot} from '../editor-selector'
38
35
  import {SLATE_TO_PORTABLE_TEXT_RANGE} from '../weakMaps'
39
36
 
40
- const debug = debugWithName('API:editable')
41
-
42
37
  export function createEditableAPI(
43
38
  editor: PortableTextSlateEditor,
44
39
  editorActor: EditorActor,
@@ -431,84 +426,24 @@ export function createEditableAPI(
431
426
  selection: EditorSelection,
432
427
  options?: EditableAPIDeleteOptions,
433
428
  ): void => {
434
- if (selection) {
435
- const range = toSlateRange({
436
- context: {
437
- schema: editorActor.getSnapshot().context.schema,
438
- value: editor.value,
439
- selection,
440
- },
441
- blockIndexMap: editor.blockIndexMap,
442
- })
443
- const hasRange =
444
- range && range.anchor.path.length > 0 && range.focus.path.length > 0
445
- if (!hasRange) {
446
- throw new Error('Invalid range')
447
- }
448
- if (range) {
449
- if (!options?.mode || options?.mode === 'selected') {
450
- debug(`Deleting content in selection`)
451
- Transforms.delete(editor, {
452
- at: range,
453
- hanging: true,
454
- voids: true,
455
- })
456
- editor.onChange()
457
- return
458
- }
459
- if (options?.mode === 'blocks') {
460
- debug(`Deleting blocks touched by selection`)
461
- Transforms.removeNodes(editor, {
462
- at: range,
463
- voids: true,
464
- match: (node) => {
465
- return (
466
- editor.isTextBlock(node) ||
467
- (!editor.isTextBlock(node) && SlateElement.isElement(node))
468
- )
469
- },
470
- })
471
- }
472
- if (options?.mode === 'children') {
473
- debug(`Deleting children touched by selection`)
474
- Transforms.removeNodes(editor, {
475
- at: range,
476
- voids: true,
477
- match: (node) => {
478
- return (
479
- node._type === types.span.name || // Text children
480
- (!editor.isTextBlock(node) && SlateElement.isElement(node)) // inline blocks
481
- )
482
- },
483
- })
484
- }
485
- // If the editor was emptied, insert a placeholder block
486
- // directly into the editor's children. We don't want to do this
487
- // through a Transform (because that would trigger a change event
488
- // that would insert the placeholder into the actual value
489
- // which should remain empty)
490
- if (editor.children.length === 0) {
491
- const placeholderBlock = createPlaceholderBlock(
492
- editorActor.getSnapshot().context,
493
- )
494
- editor.children = [placeholderBlock]
495
- editor.value = [placeholderBlock]
496
-
497
- buildIndexMaps(
498
- {
499
- schema: editorActor.getSnapshot().context.schema,
500
- value: editor.value,
501
- },
502
- {
503
- blockIndexMap: editor.blockIndexMap,
504
- listIndexMap: editor.listIndexMap,
505
- },
506
- )
507
- }
508
-
509
- editor.onChange()
510
- }
429
+ if (!selection) {
430
+ return
511
431
  }
432
+
433
+ editorActor.send({
434
+ type: 'behavior event',
435
+ behaviorEvent: {
436
+ type: 'delete',
437
+ at: selection,
438
+ unit:
439
+ options?.mode === 'blocks'
440
+ ? 'block'
441
+ : options?.mode === 'children'
442
+ ? 'child'
443
+ : undefined,
444
+ },
445
+ editor,
446
+ })
512
447
  },
513
448
  removeAnnotation: <TSchemaType extends {name: string}>(
514
449
  type: TSchemaType,
@@ -41,7 +41,7 @@ export function createWithPatches({
41
41
  // The editor.value would no longer contain that information if the node is already deleted.
42
42
  let previousValue: PortableTextBlock[]
43
43
 
44
- const applyPatch = createApplyPatch(editorActor.getSnapshot().context.schema)
44
+ const applyPatch = createApplyPatch(editorActor.getSnapshot().context)
45
45
 
46
46
  return function withPatches(editor: PortableTextSlateEditor) {
47
47
  IS_PROCESSING_REMOTE_CHANGES.set(editor, false)
@@ -1,6 +1,7 @@
1
1
  import {Editor} from 'slate'
2
2
  import {isRedoing} from '../../history/slate-plugin.redoing'
3
3
  import {isUndoing} from '../../history/slate-plugin.undoing'
4
+ import {createPlaceholderBlock} from '../../internal-utils/create-placeholder-block'
4
5
  import {debugWithName} from '../../internal-utils/debug'
5
6
  import type {PortableTextSlateEditor} from '../../types/editor'
6
7
  import type {EditorActor} from '../editor-machine'
@@ -55,7 +56,10 @@ export function createWithPlaceholderBlock(
55
56
 
56
57
  if (isLonelyBlock && isBlockObject) {
57
58
  debug('Adding placeholder block')
58
- Editor.insertNode(editor, editor.pteCreateTextBlock({decorators: []}))
59
+ Editor.insertNode(
60
+ editor,
61
+ createPlaceholderBlock(editorActor.getSnapshot().context),
62
+ )
59
63
  }
60
64
  }
61
65
 
@@ -9,7 +9,6 @@ import {createWithPatches} from './createWithPatches'
9
9
  import {createWithPlaceholderBlock} from './createWithPlaceholderBlock'
10
10
  import {createWithPortableTextMarkModel} from './createWithPortableTextMarkModel'
11
11
  import {createWithSchemaTypes} from './createWithSchemaTypes'
12
- import {createWithUtils} from './createWithUtils'
13
12
  import {pluginUpdateSelection} from './slate-plugin.update-selection'
14
13
  import {pluginUpdateValue} from './slate-plugin.update-value'
15
14
 
@@ -48,9 +47,6 @@ export const withPlugins = <T extends Editor>(
48
47
 
49
48
  const withPlaceholderBlock = createWithPlaceholderBlock(editorActor)
50
49
 
51
- const withUtils = createWithUtils({
52
- editorActor,
53
- })
54
50
  const withEventListeners = createWithEventListeners(editorActor)
55
51
 
56
52
  // Ordering is important here, selection dealing last, data manipulation in the middle and core model stuff first.
@@ -59,16 +55,14 @@ export const withPlugins = <T extends Editor>(
59
55
  withObjectKeys(
60
56
  withPortableTextMarkModel(
61
57
  withPlaceholderBlock(
62
- withUtils(
63
- withUndoRedo(
64
- withPatches(
65
- pluginUpdateValue(
66
- editorActor.getSnapshot().context,
67
- pluginUpdateSelection({
68
- editorActor,
69
- editor: e,
70
- }),
71
- ),
58
+ withUndoRedo(
59
+ withPatches(
60
+ pluginUpdateValue(
61
+ editorActor.getSnapshot().context,
62
+ pluginUpdateSelection({
63
+ editorActor,
64
+ editor: e,
65
+ }),
72
66
  ),
73
67
  ),
74
68
  ),
@@ -17,6 +17,7 @@ import {
17
17
  type CallbackLogicFunction,
18
18
  } from 'xstate'
19
19
  import {pluginWithoutHistory} from '../history/slate-plugin.without-history'
20
+ import {createPlaceholderBlock} from '../internal-utils/create-placeholder-block'
20
21
  import {debugWithName} from '../internal-utils/debug'
21
22
  import {validateValue} from '../internal-utils/validateValue'
22
23
  import {toSlateBlock, VOID_CHILD_KEY} from '../internal-utils/values'
@@ -424,6 +425,7 @@ async function updateValue({
424
425
  debug('Value is empty')
425
426
 
426
427
  clearEditor({
428
+ context,
427
429
  slateEditor,
428
430
  doneSyncing,
429
431
  hadSelection,
@@ -573,10 +575,15 @@ async function* getStreamedBlocks({value}: {value: Array<PortableTextBlock>}) {
573
575
  * Remove all blocks and insert a placeholder block
574
576
  */
575
577
  function clearEditor({
578
+ context,
576
579
  slateEditor,
577
580
  doneSyncing,
578
581
  hadSelection,
579
582
  }: {
583
+ context: {
584
+ keyGenerator: () => string
585
+ schema: EditorSchema
586
+ }
580
587
  slateEditor: PortableTextSlateEditor
581
588
  doneSyncing: boolean
582
589
  hadSelection: boolean
@@ -601,11 +608,9 @@ function clearEditor({
601
608
  })
602
609
  })
603
610
 
604
- Transforms.insertNodes(
605
- slateEditor,
606
- slateEditor.pteCreateTextBlock({decorators: []}),
607
- {at: [0]},
608
- )
611
+ Transforms.insertNodes(slateEditor, createPlaceholderBlock(context), {
612
+ at: [0],
613
+ })
609
614
 
610
615
  // Add a new selection in the top of the document
611
616
  if (hadSelection) {
@@ -17,17 +17,18 @@ import {
17
17
  } from '@sanity/diff-match-patch'
18
18
  import type {Path, PortableTextBlock, PortableTextChild} from '@sanity/types'
19
19
  import {Editor, Element, Node, Text, Transforms, type Descendant} from 'slate'
20
- import type {EditorSchema} from '../editor/editor-schema'
20
+ import type {EditorContext} from '../editor/editor-snapshot'
21
21
  import {KEY_TO_SLATE_ELEMENT} from '../editor/weakMaps'
22
22
  import type {PortableTextSlateEditor} from '../types/editor'
23
23
  import {isKeyedSegment} from '../utils/util.is-keyed-segment'
24
+ import {createPlaceholderBlock} from './create-placeholder-block'
24
25
  import {isEqualToEmptyEditor, toSlateBlock} from './values'
25
26
 
26
27
  /**
27
28
  * Creates a function that can apply a patch onto a PortableTextSlateEditor.
28
29
  */
29
30
  export function createApplyPatch(
30
- schema: EditorSchema,
31
+ context: Pick<EditorContext, 'schema' | 'keyGenerator'>,
31
32
  ): (editor: PortableTextSlateEditor, patch: Patch) => boolean {
32
33
  return (editor: PortableTextSlateEditor, patch: Patch): boolean => {
33
34
  let changed = false
@@ -35,10 +36,10 @@ export function createApplyPatch(
35
36
  try {
36
37
  switch (patch.type) {
37
38
  case 'insert':
38
- changed = insertPatch(editor, patch, schema)
39
+ changed = insertPatch(context, editor, patch)
39
40
  break
40
41
  case 'unset':
41
- changed = unsetPatch(editor, patch)
42
+ changed = unsetPatch(context, editor, patch)
42
43
  break
43
44
  case 'set':
44
45
  changed = setPatch(editor, patch)
@@ -117,9 +118,9 @@ function diffMatchPatch(
117
118
  }
118
119
 
119
120
  function insertPatch(
121
+ context: Pick<EditorContext, 'schema' | 'keyGenerator'>,
120
122
  editor: PortableTextSlateEditor,
121
123
  patch: InsertPatch,
122
- schema: EditorSchema,
123
124
  ) {
124
125
  const block = findBlock(editor.children, patch.path)
125
126
 
@@ -137,7 +138,7 @@ function insertPatch(
137
138
  const blocksToInsert = items.map((item) =>
138
139
  toSlateBlock(
139
140
  item as PortableTextBlock,
140
- {schemaTypes: schema},
141
+ {schemaTypes: context.schema},
141
142
  KEY_TO_SLATE_ELEMENT.get(editor),
142
143
  ),
143
144
  )
@@ -145,7 +146,10 @@ function insertPatch(
145
146
  const normalizedIdx =
146
147
  position === 'after' ? targetBlockIndex + 1 : targetBlockIndex
147
148
 
148
- const editorWasEmptyBefore = isEqualToEmptyEditor(editor.value, schema)
149
+ const editorWasEmptyBefore = isEqualToEmptyEditor(
150
+ editor.value,
151
+ context.schema,
152
+ )
149
153
 
150
154
  Transforms.insertNodes(editor, blocksToInsert, {at: [normalizedIdx]})
151
155
 
@@ -177,7 +181,7 @@ function insertPatch(
177
181
 
178
182
  const childrenToInsert = toSlateBlock(
179
183
  {...block.node, children: items as PortableTextChild[]},
180
- {schemaTypes: schema},
184
+ {schemaTypes: context.schema},
181
185
  KEY_TO_SLATE_ELEMENT.get(editor),
182
186
  )
183
187
  const normalizedIdx =
@@ -414,7 +418,11 @@ function setPatch(editor: PortableTextSlateEditor, patch: SetPatch) {
414
418
  return true
415
419
  }
416
420
 
417
- function unsetPatch(editor: PortableTextSlateEditor, patch: UnsetPatch) {
421
+ function unsetPatch(
422
+ context: Pick<EditorContext, 'keyGenerator' | 'schema'>,
423
+ editor: PortableTextSlateEditor,
424
+ patch: UnsetPatch,
425
+ ) {
418
426
  // Value
419
427
  if (patch.path.length === 0) {
420
428
  const previousSelection = editor.selection
@@ -428,7 +436,7 @@ function unsetPatch(editor: PortableTextSlateEditor, patch: UnsetPatch) {
428
436
  Transforms.removeNodes(editor, {at: path})
429
437
  }
430
438
 
431
- Transforms.insertNodes(editor, editor.pteCreateTextBlock({decorators: []}))
439
+ Transforms.insertNodes(editor, createPlaceholderBlock(context))
432
440
  if (previousSelection) {
433
441
  Transforms.select(editor, {
434
442
  anchor: {path: [0, 0], offset: 0},
@@ -455,10 +463,7 @@ function unsetPatch(editor: PortableTextSlateEditor, patch: UnsetPatch) {
455
463
 
456
464
  Transforms.deselect(editor)
457
465
  Transforms.removeNodes(editor, {at: [block.index]})
458
- Transforms.insertNodes(
459
- editor,
460
- editor.pteCreateTextBlock({decorators: []}),
461
- )
466
+ Transforms.insertNodes(editor, createPlaceholderBlock(context))
462
467
 
463
468
  if (previousSelection) {
464
469
  Transforms.select(editor, {
@@ -1,6 +1,7 @@
1
- import {Transforms, type Element as SlateElement} from 'slate'
2
- import {toSlateBlock} from '../internal-utils/values'
3
- import {parseBlock} from '../utils/parse-blocks'
1
+ import {applyAll, set} from '@portabletext/patches'
2
+ import {isTextBlock} from '@portabletext/schema'
3
+ import {Transforms, type Node} from 'slate'
4
+ import {parseMarkDefs} from '../utils/parse-blocks'
4
5
  import type {BehaviorOperationImplementation} from './behavior.operations'
5
6
 
6
7
  export const blockSetOperationImplementation: BehaviorOperationImplementation<
@@ -14,36 +15,90 @@ export const blockSetOperationImplementation: BehaviorOperationImplementation<
14
15
  )
15
16
  }
16
17
 
17
- const block = operation.editor.value.at(blockIndex)
18
+ const slateBlock = operation.editor.children.at(blockIndex)
18
19
 
19
- if (!block) {
20
+ if (!slateBlock) {
20
21
  throw new Error(`Unable to find block at ${JSON.stringify(operation.at)}`)
21
22
  }
22
23
 
23
- const {_type, ...filteredProps} = operation.props
24
+ if (isTextBlock(context, slateBlock)) {
25
+ const filteredProps: Record<string, unknown> = {}
24
26
 
25
- const updatedBlock = {
26
- ...block,
27
- ...filteredProps,
28
- }
27
+ for (const key of Object.keys(operation.props)) {
28
+ if (key === '_type' || key === 'children') {
29
+ continue
30
+ }
29
31
 
30
- const parsedBlock = parseBlock({
31
- context,
32
- block: updatedBlock,
33
- options: {
34
- normalize: false,
35
- removeUnusedMarkDefs: false,
36
- validateFields: true,
37
- },
38
- })
39
-
40
- if (!parsedBlock) {
41
- throw new Error(`Unable to update block at ${JSON.stringify(operation.at)}`)
42
- }
32
+ if (key === 'style') {
33
+ if (
34
+ context.schema.styles.some(
35
+ (style) => style.name === operation.props[key],
36
+ )
37
+ ) {
38
+ filteredProps[key] = operation.props[key]
39
+ }
40
+ continue
41
+ }
42
+
43
+ if (key === 'listItem') {
44
+ if (
45
+ context.schema.lists.some(
46
+ (list) => list.name === operation.props[key],
47
+ )
48
+ ) {
49
+ filteredProps[key] = operation.props[key]
50
+ }
51
+ continue
52
+ }
53
+
54
+ if (key === 'level') {
55
+ filteredProps[key] = operation.props[key]
56
+ continue
57
+ }
58
+
59
+ if (key === 'markDefs') {
60
+ const {markDefs} = parseMarkDefs({
61
+ context,
62
+ markDefs: operation.props[key],
63
+ options: {validateFields: true},
64
+ })
65
+ filteredProps[key] = markDefs
66
+ continue
67
+ }
68
+
69
+ if (context.schema.block.fields?.some((field) => field.name === key)) {
70
+ filteredProps[key] = operation.props[key]
71
+ }
72
+ }
73
+
74
+ Transforms.setNodes(operation.editor, filteredProps, {at: [blockIndex]})
75
+ } else {
76
+ const schemaDefinition = context.schema.blockObjects.find(
77
+ (definition) => definition.name === slateBlock._type,
78
+ )
79
+ const filteredProps: Record<string, unknown> = {}
80
+
81
+ for (const key of Object.keys(operation.props)) {
82
+ if (key === '_type') {
83
+ continue
84
+ }
43
85
 
44
- const slateBlock = toSlateBlock(parsedBlock, {
45
- schemaTypes: context.schema,
46
- }) as SlateElement
86
+ if (key === '_key') {
87
+ filteredProps[key] = operation.props[key]
88
+ continue
89
+ }
47
90
 
48
- Transforms.setNodes(operation.editor, slateBlock, {at: [blockIndex]})
91
+ if (schemaDefinition?.fields.some((field) => field.name === key)) {
92
+ filteredProps[key] = operation.props[key]
93
+ }
94
+ }
95
+
96
+ const patches = Object.entries(filteredProps).map(([key, value]) =>
97
+ key === '_key' ? set(value, ['_key']) : set(value, ['value', key]),
98
+ )
99
+
100
+ const updatedSlateBlock = applyAll(slateBlock, patches) as Partial<Node>
101
+
102
+ Transforms.setNodes(operation.editor, updatedSlateBlock, {at: [blockIndex]})
103
+ }
49
104
  }
@@ -1,7 +1,6 @@
1
+ import {applyAll, set, unset} from '@portabletext/patches'
1
2
  import {isTextBlock} from '@portabletext/schema'
2
- import {omit} from 'lodash'
3
- import {Transforms} from 'slate'
4
- import {parseBlock} from '../utils/parse-blocks'
3
+ import {Transforms, type Node} from 'slate'
5
4
  import type {BehaviorOperationImplementation} from './behavior.operations'
6
5
 
7
6
  export const blockUnsetOperationImplementation: BehaviorOperationImplementation<
@@ -14,73 +13,42 @@ export const blockUnsetOperationImplementation: BehaviorOperationImplementation<
14
13
  throw new Error(`Unable to find block index for block key ${blockKey}`)
15
14
  }
16
15
 
17
- const block =
18
- blockIndex !== undefined ? operation.editor.value.at(blockIndex) : undefined
16
+ const slateBlock =
17
+ blockIndex !== undefined
18
+ ? operation.editor.children.at(blockIndex)
19
+ : undefined
19
20
 
20
- if (!block) {
21
+ if (!slateBlock) {
21
22
  throw new Error(`Unable to find block at ${JSON.stringify(operation.at)}`)
22
23
  }
23
24
 
24
- if (isTextBlock(context, block)) {
25
- const propsToRemove = operation.props.filter((prop) => prop !== '_type')
25
+ if (isTextBlock(context, slateBlock)) {
26
+ const propsToRemove = operation.props.filter(
27
+ (prop) => prop !== '_type' && prop !== '_key' && prop !== 'children',
28
+ )
26
29
 
27
- const updatedTextBlock = parseBlock({
28
- context,
29
- block: omit(block, propsToRemove),
30
- options: {
31
- normalize: false,
32
- removeUnusedMarkDefs: true,
33
- validateFields: true,
34
- },
35
- })
30
+ Transforms.unsetNodes(operation.editor, propsToRemove, {at: [blockIndex]})
36
31
 
37
- if (!updatedTextBlock) {
38
- throw new Error(
39
- `Unable to update block at ${JSON.stringify(operation.at)}`,
32
+ if (operation.props.includes('_key')) {
33
+ Transforms.setNodes(
34
+ operation.editor,
35
+ {_key: context.keyGenerator()},
36
+ {at: [blockIndex]},
40
37
  )
41
38
  }
42
39
 
43
- const propsToSet: Record<string, unknown> = {}
44
-
45
- for (const prop of propsToRemove) {
46
- if (!(prop in updatedTextBlock)) {
47
- propsToSet[prop] = undefined
48
- } else {
49
- propsToSet[prop] = (updatedTextBlock as Record<string, unknown>)[prop]
50
- }
51
- }
52
-
53
- Transforms.setNodes(operation.editor, propsToSet, {at: [blockIndex]})
54
-
55
40
  return
56
41
  }
57
42
 
58
- const updatedBlockObject = parseBlock({
59
- context,
60
- block: omit(
61
- block,
62
- operation.props.filter((prop) => prop !== '_type'),
63
- ),
64
- options: {
65
- normalize: false,
66
- removeUnusedMarkDefs: true,
67
- validateFields: true,
68
- },
69
- })
70
-
71
- if (!updatedBlockObject) {
72
- throw new Error(`Unable to update block at ${JSON.stringify(operation.at)}`)
73
- }
43
+ const patches = operation.props.flatMap((key) =>
44
+ key === '_type'
45
+ ? []
46
+ : key === '_key'
47
+ ? set(context.keyGenerator(), ['_key'])
48
+ : unset(['value', key]),
49
+ )
74
50
 
75
- const {_type, _key, ...props} = updatedBlockObject
51
+ const updatedSlateBlock = applyAll(slateBlock, patches) as Partial<Node>
76
52
 
77
- Transforms.setNodes(
78
- operation.editor,
79
- {
80
- _type,
81
- _key,
82
- value: props,
83
- },
84
- {at: [blockIndex]},
85
- )
53
+ Transforms.setNodes(operation.editor, updatedSlateBlock, {at: [blockIndex]})
86
54
  }