@portabletext/editor 3.0.9 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@portabletext/editor",
3
- "version": "3.0.9",
3
+ "version": "3.1.1",
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'>
@@ -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,
@@ -3,7 +3,6 @@ import {withReact} from 'slate-react'
3
3
  import {buildIndexMaps} from '../internal-utils/build-index-maps'
4
4
  import {createPlaceholderBlock} from '../internal-utils/create-placeholder-block'
5
5
  import {debugWithName} from '../internal-utils/debug'
6
- import {toSlateBlock} from '../internal-utils/values'
7
6
  import type {PortableTextSlateEditor} from '../types/editor'
8
7
  import type {EditorActor} from './editor-machine'
9
8
  import {withPlugins} from './plugins/with-plugins'
@@ -56,15 +55,9 @@ export function createSlateEditor(config: SlateEditorConfig): SlateEditor {
56
55
  },
57
56
  )
58
57
 
59
- const initialValue = [
60
- toSlateBlock(placeholderBlock, {
61
- schemaTypes: config.editorActor.getSnapshot().context.schema,
62
- }),
63
- ]
64
-
65
58
  const slateEditor: SlateEditor = {
66
59
  instance,
67
- initialValue,
60
+ initialValue: [placeholderBlock],
68
61
  }
69
62
 
70
63
  return slateEditor
@@ -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)
@@ -110,6 +110,7 @@ export function createWithPatches({
110
110
  previousValue = editor.value
111
111
 
112
112
  const editorWasEmpty = isEqualToEmptyEditor(
113
+ editorActor.getSnapshot().context.initialValue,
113
114
  previousValue,
114
115
  editorActor.getSnapshot().context.schema,
115
116
  )
@@ -118,6 +119,7 @@ export function createWithPatches({
118
119
  apply(operation)
119
120
 
120
121
  const editorIsEmpty = isEqualToEmptyEditor(
122
+ editorActor.getSnapshot().context.initialValue,
121
123
  editor.value,
122
124
  editorActor.getSnapshot().context.schema,
123
125
  )
@@ -10,6 +10,7 @@ import {isEqual, uniq} from 'lodash'
10
10
  import {Editor, Element, Node, Path, Range, Text, Transforms} from 'slate'
11
11
  import {isRedoing} from '../../history/slate-plugin.redoing'
12
12
  import {isUndoing} from '../../history/slate-plugin.undoing'
13
+ import {createPlaceholderBlock} from '../../internal-utils/create-placeholder-block'
13
14
  import {debugWithName} from '../../internal-utils/debug'
14
15
  import {getNextSpan, getPreviousSpan} from '../../internal-utils/sibling-utils'
15
16
  import type {BehaviorOperationImplementation} from '../../operations/behavior.operations'
@@ -19,6 +20,7 @@ import type {EditorActor} from '../editor-machine'
19
20
  import {getEditorSnapshot} from '../editor-selector'
20
21
  import {withNormalizeNode} from '../with-normalizing-node'
21
22
  import {isChangingRemotely} from '../withChanges'
23
+ import {withoutPatching} from '../withoutPatching'
22
24
 
23
25
  const debug = debugWithName('plugin:withPortableTextMarkModel')
24
26
 
@@ -38,6 +40,18 @@ export function createWithPortableTextMarkModel(
38
40
  editor.normalizeNode = (nodeEntry) => {
39
41
  const [node, path] = nodeEntry
40
42
 
43
+ if (Editor.isEditor(node) && node.children.length === 0) {
44
+ withoutPatching(editor, () => {
45
+ withNormalizeNode(editor, () => {
46
+ Transforms.insertNodes(
47
+ editor,
48
+ createPlaceholderBlock(editorActor.getSnapshot().context),
49
+ {at: [0], select: true},
50
+ )
51
+ })
52
+ })
53
+ }
54
+
41
55
  if (editor.isTextBlock(node)) {
42
56
  const children = Node.children(editor, path)
43
57
 
@@ -6,10 +6,8 @@ import type {RelayActor} from '../relay-machine'
6
6
  import {createWithEventListeners} from './create-with-event-listeners'
7
7
  import {createWithObjectKeys} from './createWithObjectKeys'
8
8
  import {createWithPatches} from './createWithPatches'
9
- import {createWithPlaceholderBlock} from './createWithPlaceholderBlock'
10
9
  import {createWithPortableTextMarkModel} from './createWithPortableTextMarkModel'
11
10
  import {createWithSchemaTypes} from './createWithSchemaTypes'
12
- import {createWithUtils} from './createWithUtils'
13
11
  import {pluginUpdateSelection} from './slate-plugin.update-selection'
14
12
  import {pluginUpdateValue} from './slate-plugin.update-value'
15
13
 
@@ -46,11 +44,6 @@ export const withPlugins = <T extends Editor>(
46
44
  })
47
45
  const withPortableTextMarkModel = createWithPortableTextMarkModel(editorActor)
48
46
 
49
- const withPlaceholderBlock = createWithPlaceholderBlock(editorActor)
50
-
51
- const withUtils = createWithUtils({
52
- editorActor,
53
- })
54
47
  const withEventListeners = createWithEventListeners(editorActor)
55
48
 
56
49
  // Ordering is important here, selection dealing last, data manipulation in the middle and core model stuff first.
@@ -58,18 +51,14 @@ export const withPlugins = <T extends Editor>(
58
51
  withSchemaTypes(
59
52
  withObjectKeys(
60
53
  withPortableTextMarkModel(
61
- withPlaceholderBlock(
62
- withUtils(
63
- withUndoRedo(
64
- withPatches(
65
- pluginUpdateValue(
66
- editorActor.getSnapshot().context,
67
- pluginUpdateSelection({
68
- editorActor,
69
- editor: e,
70
- }),
71
- ),
72
- ),
54
+ withUndoRedo(
55
+ withPatches(
56
+ pluginUpdateValue(
57
+ editorActor.getSnapshot().context,
58
+ pluginUpdateSelection({
59
+ editorActor,
60
+ editor: e,
61
+ }),
73
62
  ),
74
63
  ),
75
64
  ),
@@ -18,8 +18,8 @@ import {
18
18
  import {moveRangeByOperation} from '../internal-utils/move-range-by-operation'
19
19
  import {slateRangeToSelection} from '../internal-utils/slate-utils'
20
20
  import {toSlateRange} from '../internal-utils/to-slate-range'
21
- import {isEqualToEmptyEditor} from '../internal-utils/values'
22
21
  import type {PortableTextSlateEditor, RangeDecoration} from '../types/editor'
22
+ import {isEmptyTextBlock} from '../utils'
23
23
  import type {EditorSchema} from './editor-schema'
24
24
 
25
25
  const slateOperationCallback: CallbackLogicFunction<
@@ -282,7 +282,9 @@ export const rangeDecorationsMachine = setup({
282
282
  skipSetup: input.skipSetup,
283
283
  schema: input.schema,
284
284
  slateEditor: input.slateEditor,
285
- decorate: {fn: createDecorate(input.schema, input.slateEditor)},
285
+ decorate: {
286
+ fn: createDecorate(input.schema, input.slateEditor),
287
+ },
286
288
  }),
287
289
  invoke: {
288
290
  src: 'slate operation listener',
@@ -357,7 +359,15 @@ function createDecorate(
357
359
  slateEditor: PortableTextSlateEditor,
358
360
  ) {
359
361
  return function decorate([node, path]: NodeEntry): Array<BaseRange> {
360
- if (isEqualToEmptyEditor(slateEditor.value, schema)) {
362
+ const defaultStyle = schema.styles.at(0)?.name
363
+ const editorOnlyContainsEmptyParagraph =
364
+ slateEditor.value.length === 1 &&
365
+ isEmptyTextBlock({schema}, slateEditor.value[0]) &&
366
+ (!slateEditor.value[0].style ||
367
+ slateEditor.value[0].style === defaultStyle) &&
368
+ !slateEditor.value[0].listItem
369
+
370
+ if (editorOnlyContainsEmptyParagraph) {
361
371
  return [
362
372
  {
363
373
  anchor: {
@@ -426,7 +426,6 @@ async function updateValue({
426
426
  clearEditor({
427
427
  slateEditor,
428
428
  doneSyncing,
429
- hadSelection,
430
429
  })
431
430
 
432
431
  isChanged = true
@@ -575,11 +574,9 @@ async function* getStreamedBlocks({value}: {value: Array<PortableTextBlock>}) {
575
574
  function clearEditor({
576
575
  slateEditor,
577
576
  doneSyncing,
578
- hadSelection,
579
577
  }: {
580
578
  slateEditor: PortableTextSlateEditor
581
579
  doneSyncing: boolean
582
- hadSelection: boolean
583
580
  }) {
584
581
  Editor.withoutNormalizing(slateEditor, () => {
585
582
  pluginWithoutHistory(slateEditor, () => {
@@ -589,10 +586,6 @@ function clearEditor({
589
586
  return
590
587
  }
591
588
 
592
- if (hadSelection) {
593
- Transforms.deselect(slateEditor)
594
- }
595
-
596
589
  const childrenLength = slateEditor.children.length
597
590
 
598
591
  slateEditor.children.forEach((_, index) => {
@@ -600,17 +593,6 @@ function clearEditor({
600
593
  at: [childrenLength - 1 - index],
601
594
  })
602
595
  })
603
-
604
- Transforms.insertNodes(
605
- slateEditor,
606
- slateEditor.pteCreateTextBlock({decorators: []}),
607
- {at: [0]},
608
- )
609
-
610
- // Add a new selection in the top of the document
611
- if (hadSelection) {
612
- Transforms.select(slateEditor, [0, 0])
613
- }
614
596
  })
615
597
  })
616
598
  })
@@ -17,7 +17,7 @@ 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'
@@ -27,7 +27,9 @@ import {isEqualToEmptyEditor, toSlateBlock} from './values'
27
27
  * Creates a function that can apply a patch onto a PortableTextSlateEditor.
28
28
  */
29
29
  export function createApplyPatch(
30
- schema: EditorSchema,
30
+ context: Pick<EditorContext, 'schema' | 'keyGenerator'> & {
31
+ initialValue: Array<PortableTextBlock> | undefined
32
+ },
31
33
  ): (editor: PortableTextSlateEditor, patch: Patch) => boolean {
32
34
  return (editor: PortableTextSlateEditor, patch: Patch): boolean => {
33
35
  let changed = false
@@ -35,7 +37,7 @@ export function createApplyPatch(
35
37
  try {
36
38
  switch (patch.type) {
37
39
  case 'insert':
38
- changed = insertPatch(editor, patch, schema)
40
+ changed = insertPatch(context, editor, patch)
39
41
  break
40
42
  case 'unset':
41
43
  changed = unsetPatch(editor, patch)
@@ -117,13 +119,31 @@ function diffMatchPatch(
117
119
  }
118
120
 
119
121
  function insertPatch(
122
+ context: Pick<EditorContext, 'schema' | 'keyGenerator'> & {
123
+ initialValue: Array<PortableTextBlock> | undefined
124
+ },
120
125
  editor: PortableTextSlateEditor,
121
126
  patch: InsertPatch,
122
- schema: EditorSchema,
123
127
  ) {
124
128
  const block = findBlock(editor.children, patch.path)
125
129
 
126
130
  if (!block) {
131
+ if (patch.path.length === 1 && patch.path[0] === 0) {
132
+ const blocksToInsert = patch.items.map((item) =>
133
+ toSlateBlock(
134
+ item as PortableTextBlock,
135
+ {schemaTypes: context.schema},
136
+ KEY_TO_SLATE_ELEMENT.get(editor),
137
+ ),
138
+ )
139
+
140
+ Transforms.insertNodes(editor, blocksToInsert, {
141
+ at: [0],
142
+ })
143
+
144
+ return true
145
+ }
146
+
127
147
  return false
128
148
  }
129
149
 
@@ -137,7 +157,7 @@ function insertPatch(
137
157
  const blocksToInsert = items.map((item) =>
138
158
  toSlateBlock(
139
159
  item as PortableTextBlock,
140
- {schemaTypes: schema},
160
+ {schemaTypes: context.schema},
141
161
  KEY_TO_SLATE_ELEMENT.get(editor),
142
162
  ),
143
163
  )
@@ -145,7 +165,11 @@ function insertPatch(
145
165
  const normalizedIdx =
146
166
  position === 'after' ? targetBlockIndex + 1 : targetBlockIndex
147
167
 
148
- const editorWasEmptyBefore = isEqualToEmptyEditor(editor.value, schema)
168
+ const editorWasEmptyBefore = isEqualToEmptyEditor(
169
+ context.initialValue,
170
+ editor.value,
171
+ context.schema,
172
+ )
149
173
 
150
174
  Transforms.insertNodes(editor, blocksToInsert, {at: [normalizedIdx]})
151
175
 
@@ -177,7 +201,7 @@ function insertPatch(
177
201
 
178
202
  const childrenToInsert = toSlateBlock(
179
203
  {...block.node, children: items as PortableTextChild[]},
180
- {schemaTypes: schema},
204
+ {schemaTypes: context.schema},
181
205
  KEY_TO_SLATE_ELEMENT.get(editor),
182
206
  )
183
207
  const normalizedIdx =
@@ -417,7 +441,6 @@ function setPatch(editor: PortableTextSlateEditor, patch: SetPatch) {
417
441
  function unsetPatch(editor: PortableTextSlateEditor, patch: UnsetPatch) {
418
442
  // Value
419
443
  if (patch.path.length === 0) {
420
- const previousSelection = editor.selection
421
444
  Transforms.deselect(editor)
422
445
 
423
446
  const children = Node.children(editor, [], {
@@ -428,15 +451,6 @@ function unsetPatch(editor: PortableTextSlateEditor, patch: UnsetPatch) {
428
451
  Transforms.removeNodes(editor, {at: path})
429
452
  }
430
453
 
431
- Transforms.insertNodes(editor, editor.pteCreateTextBlock({decorators: []}))
432
- if (previousSelection) {
433
- Transforms.select(editor, {
434
- anchor: {path: [0, 0], offset: 0},
435
- focus: {path: [0, 0], offset: 0},
436
- })
437
- }
438
- // call OnChange here to emit the new selection
439
- editor.onChange()
440
454
  return true
441
455
  }
442
456
 
@@ -448,30 +462,6 @@ function unsetPatch(editor: PortableTextSlateEditor, patch: UnsetPatch) {
448
462
 
449
463
  // Single blocks
450
464
  if (patch.path.length === 1) {
451
- if (editor.children.length === 1) {
452
- // `unset`ing the last block should be treated similar to `unset`ing the
453
- // entire editor value
454
- const previousSelection = editor.selection
455
-
456
- Transforms.deselect(editor)
457
- Transforms.removeNodes(editor, {at: [block.index]})
458
- Transforms.insertNodes(
459
- editor,
460
- editor.pteCreateTextBlock({decorators: []}),
461
- )
462
-
463
- if (previousSelection) {
464
- Transforms.select(editor, {
465
- anchor: {path: [0, 0], offset: 0},
466
- focus: {path: [0, 0], offset: 0},
467
- })
468
- }
469
-
470
- editor.onChange()
471
-
472
- return true
473
- }
474
-
475
465
  Transforms.removeNodes(editor, {at: [block.index]})
476
466
 
477
467
  return true
@@ -169,9 +169,14 @@ export function fromSlateBlock(
169
169
  }
170
170
 
171
171
  export function isEqualToEmptyEditor(
172
+ initialValue: Array<PortableTextBlock> | undefined,
172
173
  blocks: Array<Descendant> | Array<PortableTextBlock>,
173
174
  schemaTypes: EditorSchema,
174
175
  ): boolean {
176
+ if (!blocks) {
177
+ return false
178
+ }
179
+
175
180
  // Must have exactly one block
176
181
  if (blocks.length !== 1) {
177
182
  return false
@@ -240,5 +245,22 @@ export function isEqualToEmptyEditor(
240
245
  return false
241
246
  }
242
247
 
248
+ if (
249
+ Object.keys(firstBlock).some(
250
+ (key) =>
251
+ key !== '_type' &&
252
+ key !== '_key' &&
253
+ key !== 'children' &&
254
+ key !== 'markDefs' &&
255
+ key !== 'style',
256
+ )
257
+ ) {
258
+ return false
259
+ }
260
+
261
+ if (isEqual(initialValue, [firstBlock])) {
262
+ return false
263
+ }
264
+
243
265
  return true
244
266
  }
@@ -1,9 +1,9 @@
1
- import {isTextBlock} from '@portabletext/schema'
1
+ import {isSpan, isTextBlock} from '@portabletext/schema'
2
2
  import {deleteText, Editor, Element, Range, Transforms} from 'slate'
3
3
  import {DOMEditor} from 'slate-dom'
4
- import {createPlaceholderBlock} from '../internal-utils/create-placeholder-block'
5
4
  import {slateRangeToSelection} from '../internal-utils/slate-utils'
6
5
  import {toSlateRange} from '../internal-utils/to-slate-range'
6
+ import {VOID_CHILD_KEY} from '../internal-utils/values'
7
7
  import type {PortableTextSlateEditor} from '../types/editor'
8
8
  import {getBlockKeyFromSelectionPoint} from '../utils/util.selection-point'
9
9
  import type {BehaviorOperationImplementation} from './behavior.operations'
@@ -67,10 +67,23 @@ export const deleteOperationImplementation: BehaviorOperationImplementation<
67
67
  mode: 'highest',
68
68
  })
69
69
 
70
- if (operation.editor.children.length === 0) {
71
- Transforms.insertNodes(operation.editor, createPlaceholderBlock(context))
70
+ return
71
+ }
72
+
73
+ if (operation.unit === 'child') {
74
+ const range = at ?? operation.editor.selection ?? undefined
75
+
76
+ if (!range) {
77
+ throw new Error('Unable to delete children without a selection')
72
78
  }
73
79
 
80
+ Transforms.removeNodes(operation.editor, {
81
+ at: range,
82
+ match: (node) =>
83
+ (isSpan(context, node) && node._key !== VOID_CHILD_KEY) ||
84
+ ('__inline' in node && node.__inline === true),
85
+ })
86
+
74
87
  return
75
88
  }
76
89
 
@@ -10,6 +10,7 @@ import {
10
10
  type Descendant,
11
11
  } from 'slate'
12
12
  import {DOMEditor} from 'slate-dom'
13
+ import {createPlaceholderBlock} from '../internal-utils/create-placeholder-block'
13
14
  import {getFocusBlock, getFocusChild} from '../internal-utils/slate-utils'
14
15
  import {toSlateRange} from '../internal-utils/to-slate-range'
15
16
  import {toSlateBlock} from '../internal-utils/values'
@@ -70,6 +71,10 @@ export function insertBlock(options: {
70
71
  })
71
72
  : editor.selection
72
73
 
74
+ if (editor.children.length === 0) {
75
+ Transforms.insertNodes(editor, createPlaceholderBlock(context), {at: [0]})
76
+ }
77
+
73
78
  // Fall back to the start and end of the editor if neither an editor
74
79
  // selection nor an `at` range is provided
75
80
  const start = at ? Range.start(at) : Editor.start(editor, [])
@@ -152,15 +152,6 @@ export interface PortableTextSlateEditor extends ReactEditor {
152
152
  */
153
153
  pteWithHotKeys: (event: KeyboardEvent<HTMLDivElement>) => void
154
154
 
155
- /**
156
- * Helper function that creates a text block
157
- */
158
- pteCreateTextBlock: (options: {
159
- decorators: Array<string>
160
- listItem?: string
161
- level?: number
162
- }) => Descendant
163
-
164
155
  /**
165
156
  * Undo
166
157
  */