@portabletext/editor 2.9.2 → 2.11.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 (42) hide show
  1. package/lib/_chunks-cjs/util.merge-text-blocks.cjs +1 -0
  2. package/lib/_chunks-cjs/util.merge-text-blocks.cjs.map +1 -1
  3. package/lib/_chunks-cjs/util.slice-blocks.cjs +6 -1
  4. package/lib/_chunks-cjs/util.slice-blocks.cjs.map +1 -1
  5. package/lib/_chunks-dts/behavior.types.action.d.cts +79 -69
  6. package/lib/_chunks-dts/behavior.types.action.d.ts +79 -69
  7. package/lib/_chunks-es/util.merge-text-blocks.js +1 -0
  8. package/lib/_chunks-es/util.merge-text-blocks.js.map +1 -1
  9. package/lib/_chunks-es/util.slice-blocks.js +6 -1
  10. package/lib/_chunks-es/util.slice-blocks.js.map +1 -1
  11. package/lib/index.cjs +232 -68
  12. package/lib/index.cjs.map +1 -1
  13. package/lib/index.js +234 -70
  14. package/lib/index.js.map +1 -1
  15. package/lib/plugins/index.d.cts +3 -3
  16. package/lib/plugins/index.d.ts +3 -3
  17. package/lib/utils/index.d.cts +2 -2
  18. package/package.json +10 -10
  19. package/src/behaviors/behavior.abstract.insert.ts +109 -24
  20. package/src/behaviors/behavior.abstract.split.ts +1 -0
  21. package/src/behaviors/behavior.perform-event.ts +89 -67
  22. package/src/behaviors/behavior.types.event.ts +10 -3
  23. package/src/converters/converter.portable-text.ts +1 -0
  24. package/src/converters/converter.text-html.ts +1 -0
  25. package/src/converters/converter.text-plain.ts +1 -0
  26. package/src/editor/Editable.tsx +1 -0
  27. package/src/editor/editor-selector.ts +10 -1
  28. package/src/editor/without-normalizing-conditional.ts +13 -0
  29. package/src/internal-utils/parse-blocks.test.ts +14 -14
  30. package/src/internal-utils/parse-blocks.ts +9 -2
  31. package/src/internal-utils/test-editor.tsx +1 -25
  32. package/src/operations/behavior.operation.block.set.ts +4 -3
  33. package/src/operations/behavior.operation.block.unset.ts +8 -2
  34. package/src/operations/behavior.operation.insert.block.ts +4 -1
  35. package/src/operations/behavior.operation.insert.child.ts +95 -0
  36. package/src/operations/behavior.operations.ts +9 -0
  37. package/src/selectors/selector.get-trimmed-selection.test.ts +1 -0
  38. package/src/types/block-with-optional-key.ts +13 -1
  39. package/src/utils/util.merge-text-blocks.ts +1 -1
  40. package/src/utils/util.slice-blocks.ts +3 -3
  41. package/src/utils/util.slice-text-block.test.ts +54 -28
  42. package/src/plugins/plugin.internal.editor-actor-ref.tsx +0 -15
@@ -1,5 +1,5 @@
1
1
  import { Behavior, Editor, EditorEmittedEvent, EditorSchema } from "../_chunks-dts/behavior.types.action.cjs";
2
- import * as react12 from "react";
2
+ import * as react22 from "react";
3
3
  import React from "react";
4
4
  /**
5
5
  * @beta
@@ -181,7 +181,7 @@ type MarkdownPluginConfig = MarkdownBehaviorsConfig & {
181
181
  */
182
182
  declare function MarkdownPlugin(props: {
183
183
  config: MarkdownPluginConfig;
184
- }): react12.JSX.Element;
184
+ }): react22.JSX.Element;
185
185
  /**
186
186
  * @beta
187
187
  * Restrict the editor to one line. The plugin takes care of blocking
@@ -192,5 +192,5 @@ declare function MarkdownPlugin(props: {
192
192
  *
193
193
  * @deprecated Install the plugin from `@portabletext/plugin-one-line`
194
194
  */
195
- declare function OneLinePlugin(): react12.JSX.Element;
195
+ declare function OneLinePlugin(): react22.JSX.Element;
196
196
  export { BehaviorPlugin, DecoratorShortcutPlugin, EditorRefPlugin, EventListenerPlugin, MarkdownPlugin, type MarkdownPluginConfig, OneLinePlugin };
@@ -1,5 +1,5 @@
1
1
  import { Behavior, Editor, EditorEmittedEvent, EditorSchema } from "../_chunks-dts/behavior.types.action.js";
2
- import * as react22 from "react";
2
+ import * as react21 from "react";
3
3
  import React from "react";
4
4
  /**
5
5
  * @beta
@@ -181,7 +181,7 @@ type MarkdownPluginConfig = MarkdownBehaviorsConfig & {
181
181
  */
182
182
  declare function MarkdownPlugin(props: {
183
183
  config: MarkdownPluginConfig;
184
- }): react22.JSX.Element;
184
+ }): react21.JSX.Element;
185
185
  /**
186
186
  * @beta
187
187
  * Restrict the editor to one line. The plugin takes care of blocking
@@ -192,5 +192,5 @@ declare function MarkdownPlugin(props: {
192
192
  *
193
193
  * @deprecated Install the plugin from `@portabletext/plugin-one-line`
194
194
  */
195
- declare function OneLinePlugin(): react22.JSX.Element;
195
+ declare function OneLinePlugin(): react21.JSX.Element;
196
196
  export { BehaviorPlugin, DecoratorShortcutPlugin, EditorRefPlugin, EventListenerPlugin, MarkdownPlugin, type MarkdownPluginConfig, OneLinePlugin };
@@ -1,5 +1,5 @@
1
1
  import { BlockOffset, BlockPath, ChildPath, EditorContext, EditorSelection, EditorSelectionPoint } from "../_chunks-dts/behavior.types.action.cjs";
2
- import * as _sanity_types9 from "@sanity/types";
2
+ import * as _sanity_types8 from "@sanity/types";
3
3
  import { KeyedSegment, PortableTextBlock, PortableTextTextBlock } from "@sanity/types";
4
4
  import { isSpan, isTextBlock } from "@portabletext/schema";
5
5
  /**
@@ -143,7 +143,7 @@ declare function mergeTextBlocks({
143
143
  context: Pick<EditorContext, 'keyGenerator' | 'schema'>;
144
144
  targetBlock: PortableTextTextBlock;
145
145
  incomingBlock: PortableTextTextBlock;
146
- }): PortableTextTextBlock<_sanity_types9.PortableTextObject | _sanity_types9.PortableTextSpan>;
146
+ }): PortableTextTextBlock<_sanity_types8.PortableTextObject | _sanity_types8.PortableTextSpan>;
147
147
  /**
148
148
  * @public
149
149
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@portabletext/editor",
3
- "version": "2.9.2",
3
+ "version": "2.11.0",
4
4
  "description": "Portable Text Editor made in React",
5
5
  "keywords": [
6
6
  "sanity",
@@ -79,16 +79,16 @@
79
79
  "slate-dom": "^0.118.1",
80
80
  "slate-react": "0.117.4",
81
81
  "xstate": "^5.21.0",
82
- "@portabletext/block-tools": "^3.5.5",
82
+ "@portabletext/block-tools": "^3.5.6",
83
+ "@portabletext/patches": "^1.1.8",
83
84
  "@portabletext/schema": "^1.2.0",
84
- "@portabletext/keyboard-shortcuts": "^1.1.1",
85
- "@portabletext/patches": "^1.1.8"
85
+ "@portabletext/keyboard-shortcuts": "^1.1.1"
86
86
  },
87
87
  "devDependencies": {
88
88
  "@sanity/diff-match-patch": "^3.2.0",
89
89
  "@sanity/pkg-utils": "^8.1.4",
90
- "@sanity/schema": "^4.8.1",
91
- "@sanity/types": "^4.8.1",
90
+ "@sanity/schema": "^4.9.0",
91
+ "@sanity/types": "^4.9.0",
92
92
  "@types/debug": "^4.1.12",
93
93
  "@types/lodash": "^4.17.20",
94
94
  "@types/lodash.startcase": "^4.4.9",
@@ -110,14 +110,14 @@
110
110
  "vite": "^7.1.3",
111
111
  "vitest": "^3.2.4",
112
112
  "vitest-browser-react": "^1.0.1",
113
- "@portabletext/sanity-bridge": "1.1.9",
113
+ "@portabletext/sanity-bridge": "1.1.10",
114
114
  "@portabletext/test": "^0.0.0",
115
115
  "racejar": "1.2.15"
116
116
  },
117
117
  "peerDependencies": {
118
- "@portabletext/sanity-bridge": "^1.1.9",
119
- "@sanity/schema": "^4.8.1",
120
- "@sanity/types": "^4.8.1",
118
+ "@portabletext/sanity-bridge": "^1.1.10",
119
+ "@sanity/schema": "^4.9.0",
120
+ "@sanity/types": "^4.9.0",
121
121
  "react": "^18.3 || ^19",
122
122
  "rxjs": "^7.8.2"
123
123
  },
@@ -1,4 +1,4 @@
1
- import {getFocusTextBlock} from '../selectors'
1
+ import {getFocusTextBlock, getLastBlock} from '../selectors'
2
2
  import {
3
3
  getBlockEndPoint,
4
4
  getBlockStartPoint,
@@ -6,7 +6,7 @@ import {
6
6
  isEqualSelectionPoints,
7
7
  } from '../utils'
8
8
  import {sliceTextBlock} from '../utils/util.slice-text-block'
9
- import {execute, raise} from './behavior.types.action'
9
+ import {raise} from './behavior.types.action'
10
10
  import {defineBehavior} from './behavior.types.behavior'
11
11
 
12
12
  export const abstractInsertBehaviors = [
@@ -328,23 +328,77 @@ export const abstractInsertBehaviors = [
328
328
  actions: [() => [raise({type: 'split'})]],
329
329
  }),
330
330
  defineBehavior({
331
- on: 'insert.inline object',
331
+ on: 'insert.child',
332
+ guard: ({snapshot}) => {
333
+ const lastBlock = getLastBlock(snapshot)
334
+
335
+ if (!lastBlock) {
336
+ return false
337
+ }
338
+
339
+ if (snapshot.context.selection) {
340
+ return false
341
+ }
342
+
343
+ const lastBlockEndPoint = getBlockEndPoint({
344
+ context: snapshot.context,
345
+ block: lastBlock,
346
+ })
347
+
348
+ return {lastBlockEndPoint}
349
+ },
350
+ actions: [
351
+ ({event}, {lastBlockEndPoint}) => [
352
+ raise({
353
+ type: 'select',
354
+ at: {
355
+ anchor: lastBlockEndPoint,
356
+ focus: lastBlockEndPoint,
357
+ },
358
+ }),
359
+ raise(event),
360
+ ],
361
+ ],
362
+ }),
363
+ defineBehavior({
364
+ on: 'insert.child',
365
+ guard: ({snapshot}) => {
366
+ const focusTextBlock = getFocusTextBlock(snapshot)
367
+
368
+ return snapshot.context.selection && !focusTextBlock
369
+ },
332
370
  actions: [
333
371
  ({snapshot, event}) => [
334
- execute({
372
+ raise({
335
373
  type: 'insert.block',
336
374
  block: {
337
375
  _type: snapshot.context.schema.block.name,
338
376
  children: [
339
377
  {
340
- _type: event.inlineObject.name,
341
- ...event.inlineObject.value,
378
+ _type: snapshot.context.schema.span.name,
379
+ text: '',
380
+ marks: [],
342
381
  },
343
382
  ],
344
383
  },
345
384
  placement: 'auto',
346
385
  select: 'end',
347
386
  }),
387
+ raise(event),
388
+ ],
389
+ ],
390
+ }),
391
+ defineBehavior({
392
+ on: 'insert.inline object',
393
+ actions: [
394
+ ({event}) => [
395
+ raise({
396
+ type: 'insert.child',
397
+ child: {
398
+ _type: event.inlineObject.name,
399
+ ...event.inlineObject.value,
400
+ },
401
+ }),
348
402
  ],
349
403
  ],
350
404
  }),
@@ -354,37 +408,68 @@ export const abstractInsertBehaviors = [
354
408
  }),
355
409
  defineBehavior({
356
410
  on: 'insert.span',
357
- guard: ({snapshot, event}) => {
358
- const markDefs =
359
- event.annotations?.map((annotation) => ({
360
- _type: annotation.name,
361
- _key: snapshot.context.keyGenerator(),
362
- ...annotation.value,
363
- })) ?? []
364
-
365
- return {markDefs}
366
- },
411
+ guard: ({snapshot}) => !getFocusTextBlock(snapshot),
367
412
  actions: [
368
- ({snapshot, event}, {markDefs}) => [
369
- execute({
413
+ ({snapshot, event}) => [
414
+ raise({
370
415
  type: 'insert.block',
371
416
  block: {
372
417
  _type: snapshot.context.schema.block.name,
373
418
  children: [
374
419
  {
375
420
  _type: snapshot.context.schema.span.name,
376
- text: event.text,
377
- marks: [
378
- ...(event.decorators ?? []),
379
- ...markDefs.map((markDef) => markDef._key),
380
- ],
421
+ text: '',
422
+ marks: [],
381
423
  },
382
424
  ],
383
- markDefs,
384
425
  },
385
426
  placement: 'auto',
386
427
  select: 'end',
387
428
  }),
429
+ raise(event),
430
+ ],
431
+ ],
432
+ }),
433
+ defineBehavior({
434
+ on: 'insert.span',
435
+ guard: ({snapshot, event}) => {
436
+ const focusTextBlock = getFocusTextBlock(snapshot)
437
+ const markDefs =
438
+ event.annotations?.map((annotation) => ({
439
+ _type: annotation.name,
440
+ _key: snapshot.context.keyGenerator(),
441
+ ...annotation.value,
442
+ })) ?? []
443
+
444
+ return {markDefs, focusTextBlock}
445
+ },
446
+ actions: [
447
+ ({snapshot, event}, {markDefs, focusTextBlock}) => [
448
+ ...(focusTextBlock
449
+ ? [
450
+ raise({
451
+ type: 'block.set',
452
+ at: focusTextBlock.path,
453
+ props: {
454
+ markDefs: [
455
+ ...(focusTextBlock.node.markDefs ?? []),
456
+ ...markDefs,
457
+ ],
458
+ },
459
+ }),
460
+ ]
461
+ : []),
462
+ raise({
463
+ type: 'insert.child',
464
+ child: {
465
+ _type: snapshot.context.schema.span.name,
466
+ text: event.text,
467
+ marks: [
468
+ ...(event.decorators ?? []),
469
+ ...markDefs.map((markDef) => markDef._key),
470
+ ],
471
+ },
472
+ }),
388
473
  ],
389
474
  ],
390
475
  }),
@@ -186,6 +186,7 @@ export const abstractSplitBehaviors = [
186
186
  }),
187
187
  context: snapshot.context,
188
188
  options: {
189
+ removeUnusedMarkDefs: true,
189
190
  validateFields: false,
190
191
  },
191
192
  })
@@ -3,6 +3,7 @@ import type {EditorSchema} from '../editor/editor-schema'
3
3
  import type {EditorSnapshot} from '../editor/editor-snapshot'
4
4
  import {withPerformingBehaviorOperation} from '../editor/with-performing-behavior-operation'
5
5
  import {clearUndoStep, createUndoStep} from '../editor/with-undo-step'
6
+ import {withoutNormalizingConditional} from '../editor/without-normalizing-conditional'
6
7
  import {debugWithName} from '../internal-utils/debug'
7
8
  import {performOperation} from '../operations/behavior.operations'
8
9
  import type {PortableTextSlateEditor} from '../types/editor'
@@ -210,75 +211,96 @@ export function performEvent({
210
211
  undoStepCreated = true
211
212
  }
212
213
 
213
- for (const action of actions) {
214
- if (action.type === 'effect') {
215
- try {
216
- action.effect({
217
- send: sendBack,
214
+ const actionTypes = actions.map((action) => action.type)
215
+ const uniqueActionTypes = new Set(actionTypes)
216
+
217
+ // The set of actions are all `raise` actions
218
+ const raiseGroup =
219
+ actionTypes.length > 1 &&
220
+ uniqueActionTypes.size === 1 &&
221
+ uniqueActionTypes.has('raise')
222
+
223
+ // The set of actions are all `execute` actions
224
+ const executeGroup =
225
+ actionTypes.length > 1 &&
226
+ uniqueActionTypes.size === 1 &&
227
+ uniqueActionTypes.has('execute')
228
+
229
+ withoutNormalizingConditional(
230
+ editor,
231
+ () => raiseGroup || executeGroup,
232
+ () => {
233
+ for (const action of actions) {
234
+ if (action.type === 'effect') {
235
+ try {
236
+ action.effect({
237
+ send: sendBack,
238
+ })
239
+ } catch (error) {
240
+ console.error(
241
+ new Error(
242
+ `Executing effect as a result of "${event.type}" failed due to: ${error.message}`,
243
+ ),
244
+ )
245
+ }
246
+
247
+ continue
248
+ }
249
+
250
+ if (action.type === 'forward') {
251
+ const remainingEventBehaviors = eventBehaviors.slice(
252
+ eventBehaviorIndex + 1,
253
+ )
254
+
255
+ performEvent({
256
+ mode: mode === 'execute' ? 'execute' : 'forward',
257
+ behaviors,
258
+ remainingEventBehaviors: remainingEventBehaviors,
259
+ event: action.event,
260
+ editor,
261
+ keyGenerator,
262
+ schema,
263
+ getSnapshot,
264
+ nativeEvent,
265
+ sendBack,
266
+ })
267
+
268
+ continue
269
+ }
270
+
271
+ if (action.type === 'raise') {
272
+ performEvent({
273
+ mode: mode === 'execute' ? 'execute' : 'raise',
274
+ behaviors,
275
+ remainingEventBehaviors:
276
+ mode === 'execute' ? remainingEventBehaviors : behaviors,
277
+ event: action.event,
278
+ editor,
279
+ keyGenerator,
280
+ schema,
281
+ getSnapshot,
282
+ nativeEvent,
283
+ sendBack,
284
+ })
285
+
286
+ continue
287
+ }
288
+
289
+ performEvent({
290
+ mode: 'execute',
291
+ behaviors,
292
+ remainingEventBehaviors: [],
293
+ event: action.event,
294
+ editor,
295
+ keyGenerator,
296
+ schema,
297
+ getSnapshot,
298
+ nativeEvent: undefined,
299
+ sendBack,
218
300
  })
219
- } catch (error) {
220
- console.error(
221
- new Error(
222
- `Executing effect as a result of "${event.type}" failed due to: ${error.message}`,
223
- ),
224
- )
225
301
  }
226
-
227
- continue
228
- }
229
-
230
- if (action.type === 'forward') {
231
- const remainingEventBehaviors = eventBehaviors.slice(
232
- eventBehaviorIndex + 1,
233
- )
234
-
235
- performEvent({
236
- mode: mode === 'execute' ? 'execute' : 'forward',
237
- behaviors,
238
- remainingEventBehaviors: remainingEventBehaviors,
239
- event: action.event,
240
- editor,
241
- keyGenerator,
242
- schema,
243
- getSnapshot,
244
- nativeEvent,
245
- sendBack,
246
- })
247
-
248
- continue
249
- }
250
-
251
- if (action.type === 'raise') {
252
- performEvent({
253
- mode: mode === 'execute' ? 'execute' : 'raise',
254
- behaviors,
255
- remainingEventBehaviors:
256
- mode === 'execute' ? remainingEventBehaviors : behaviors,
257
- event: action.event,
258
- editor,
259
- keyGenerator,
260
- schema,
261
- getSnapshot,
262
- nativeEvent,
263
- sendBack,
264
- })
265
-
266
- continue
267
- }
268
-
269
- performEvent({
270
- mode: 'execute',
271
- behaviors,
272
- remainingEventBehaviors: [],
273
- event: action.event,
274
- editor,
275
- keyGenerator,
276
- schema,
277
- getSnapshot,
278
- nativeEvent: undefined,
279
- sendBack,
280
- })
281
- }
302
+ },
303
+ )
282
304
 
283
305
  if (undoStepCreated) {
284
306
  clearUndoStep(editor)
@@ -3,7 +3,10 @@ import type {EventPosition} from '../internal-utils/event-position'
3
3
  import type {MIMEType} from '../internal-utils/mime-type'
4
4
  import type {OmitFromUnion, PickFromUnion, StrictExtract} from '../type-utils'
5
5
  import type {BlockOffset} from '../types/block-offset'
6
- import type {BlockWithOptionalKey} from '../types/block-with-optional-key'
6
+ import type {
7
+ BlockWithOptionalKey,
8
+ ChildWithOptionalKey,
9
+ } from '../types/block-with-optional-key'
7
10
  import type {EditorSelection} from '../types/editor'
8
11
  import type {AnnotationPath, BlockPath, ChildPath} from '../types/paths'
9
12
 
@@ -52,8 +55,7 @@ export type ExternalBehaviorEvent =
52
55
  value?: {[prop: string]: unknown}
53
56
  }
54
57
  }
55
- | SyntheticBehaviorEvent
56
- | CustomBehaviorEvent
58
+ | BehaviorEvent
57
59
 
58
60
  /**************************************
59
61
  * Synthetic events
@@ -72,6 +74,7 @@ const syntheticBehaviorEventTypes = [
72
74
  'history.redo',
73
75
  'history.undo',
74
76
  'insert.block',
77
+ 'insert.child',
75
78
  'insert.text',
76
79
  'move.backward',
77
80
  'move.block',
@@ -159,6 +162,10 @@ export type SyntheticBehaviorEvent =
159
162
  placement: InsertPlacement
160
163
  select?: 'start' | 'end' | 'none'
161
164
  }
165
+ | {
166
+ type: StrictExtract<SyntheticBehaviorEventType, 'insert.child'>
167
+ child: ChildWithOptionalKey
168
+ }
162
169
  | {
163
170
  type: StrictExtract<SyntheticBehaviorEventType, 'insert.text'>
164
171
  text: string
@@ -50,6 +50,7 @@ export const converterPortableText = defineConverter({
50
50
  context: snapshot.context,
51
51
  block,
52
52
  options: {
53
+ removeUnusedMarkDefs: true,
53
54
  validateFields: false,
54
55
  },
55
56
  })
@@ -61,6 +61,7 @@ export function createConverterTextHtml(
61
61
  context: snapshot.context,
62
62
  block,
63
63
  options: {
64
+ removeUnusedMarkDefs: true,
64
65
  validateFields: false,
65
66
  },
66
67
  })
@@ -85,6 +85,7 @@ export function createConverterTextPlain(
85
85
  context: snapshot.context,
86
86
  block,
87
87
  options: {
88
+ removeUnusedMarkDefs: true,
88
89
  validateFields: false,
89
90
  },
90
91
  })
@@ -461,6 +461,7 @@ export const PortableTextEditable = forwardRef<
461
461
  },
462
462
  blocks: result.insert,
463
463
  options: {
464
+ removeUnusedMarkDefs: true,
464
465
  validateFields: false,
465
466
  },
466
467
  }),
@@ -1,5 +1,6 @@
1
1
  import {useSelector} from '@xstate/react'
2
2
  import type {Editor} from '../editor'
3
+ import {slateRangeToSelection} from '../internal-utils/slate-utils'
3
4
  import type {PortableTextSlateEditor} from '../types/editor'
4
5
  import type {InternalEditor} from './create-editor'
5
6
  import type {EditorActor} from './editor-machine'
@@ -65,6 +66,14 @@ export function getEditorSnapshot({
65
66
  editorActorSnapshot: ReturnType<EditorActor['getSnapshot']>
66
67
  slateEditorInstance: PortableTextSlateEditor
67
68
  }): EditorSnapshot {
69
+ const selection = slateEditorInstance.selection
70
+ ? slateRangeToSelection({
71
+ schema: editorActorSnapshot.context.schema,
72
+ editor: slateEditorInstance,
73
+ range: slateEditorInstance.selection,
74
+ })
75
+ : null
76
+
68
77
  return {
69
78
  blockIndexMap: slateEditorInstance.blockIndexMap,
70
79
  context: {
@@ -72,7 +81,7 @@ export function getEditorSnapshot({
72
81
  keyGenerator: editorActorSnapshot.context.keyGenerator,
73
82
  readOnly: editorActorSnapshot.matches({'edit mode': 'read only'}),
74
83
  schema: editorActorSnapshot.context.schema,
75
- selection: editorActorSnapshot.context.selection,
84
+ selection,
76
85
  value: slateEditorInstance.value,
77
86
  },
78
87
  decoratorState: slateEditorInstance.decoratorState,
@@ -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
+ }