@portabletext/editor 1.1.3 → 1.1.5

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 (32) hide show
  1. package/lib/index.d.mts +228 -30
  2. package/lib/index.d.ts +228 -30
  3. package/lib/index.esm.js +169 -112
  4. package/lib/index.esm.js.map +1 -1
  5. package/lib/index.js +169 -112
  6. package/lib/index.js.map +1 -1
  7. package/lib/index.mjs +169 -112
  8. package/lib/index.mjs.map +1 -1
  9. package/package.json +24 -19
  10. package/src/editor/Editable.tsx +6 -11
  11. package/src/editor/PortableTextEditor.tsx +26 -30
  12. package/src/editor/__tests__/self-solving.test.tsx +1 -1
  13. package/src/editor/components/SlateContainer.tsx +2 -13
  14. package/src/editor/components/Synchronizer.tsx +0 -3
  15. package/src/editor/editor-machine.ts +7 -2
  16. package/src/editor/hooks/useSyncValue.ts +3 -5
  17. package/src/editor/key-generator.ts +6 -0
  18. package/src/editor/plugins/createWithEditableAPI.ts +8 -5
  19. package/src/editor/plugins/createWithHotKeys.ts +1 -0
  20. package/src/editor/plugins/createWithInsertBreak.ts +57 -9
  21. package/src/editor/plugins/createWithInsertData.ts +7 -4
  22. package/src/editor/plugins/createWithObjectKeys.ts +12 -5
  23. package/src/editor/plugins/createWithPatches.ts +0 -1
  24. package/src/editor/plugins/createWithPortableTextMarkModel.ts +106 -23
  25. package/src/editor/plugins/createWithSchemaTypes.ts +3 -4
  26. package/src/editor/plugins/createWithUtils.ts +5 -4
  27. package/src/editor/plugins/index.ts +5 -13
  28. package/src/index.ts +2 -2
  29. package/src/types/options.ts +0 -1
  30. package/src/utils/__tests__/operationToPatches.test.ts +0 -2
  31. package/src/utils/__tests__/patchToOperations.test.ts +1 -2
  32. package/src/editor/hooks/usePortableTextEditorKeyGenerator.ts +0 -27
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@portabletext/editor",
3
- "version": "1.1.3",
3
+ "version": "1.1.5",
4
4
  "description": "Portable Text Editor made in React",
5
5
  "keywords": [
6
6
  "sanity",
@@ -46,8 +46,8 @@
46
46
  "debug": "^4.3.4",
47
47
  "is-hotkey-esm": "^1.0.0",
48
48
  "lodash": "^4.17.21",
49
- "slate": "0.103.0",
50
- "slate-react": "0.110.1",
49
+ "slate": "0.110.2",
50
+ "slate-react": "0.110.2",
51
51
  "xstate": "^5.18.2"
52
52
  },
53
53
  "devDependencies": {
@@ -55,28 +55,31 @@
55
55
  "@babel/preset-react": "^7.24.7",
56
56
  "@jest/globals": "^29.7.0",
57
57
  "@jest/types": "^29.6.3",
58
- "@playwright/test": "1.47.2",
58
+ "@playwright/test": "1.48.1",
59
59
  "@portabletext/toolkit": "^2.0.15",
60
- "@sanity/block-tools": "^3.55.0",
60
+ "@sanity/block-tools": "^3.61.0",
61
61
  "@sanity/diff-match-patch": "^3.1.1",
62
62
  "@sanity/pkg-utils": "^6.11.2",
63
- "@sanity/schema": "^3.55.0",
64
- "@sanity/types": "^3.55.0",
63
+ "@sanity/schema": "^3.61.0",
64
+ "@sanity/types": "^3.61.0",
65
65
  "@sanity/ui": "^2.8.9",
66
- "@sanity/util": "^3.55.0",
66
+ "@sanity/util": "^3.61.0",
67
67
  "@testing-library/dom": "^10.4.0",
68
- "@testing-library/jest-dom": "^6.5.0",
68
+ "@testing-library/jest-dom": "^6.6.2",
69
69
  "@testing-library/react": "^16.0.1",
70
+ "@testing-library/user-event": "^14.5.2",
70
71
  "@types/debug": "^4.1.5",
71
72
  "@types/express": "^4.17.21",
72
73
  "@types/express-ws": "^3.0.5",
73
74
  "@types/lodash": "^4.17.7",
74
75
  "@types/node": "^18.19.8",
75
76
  "@types/node-ipc": "^9.2.3",
76
- "@types/react": "^18.3.3",
77
- "@types/react-dom": "^18.3.0",
77
+ "@types/react": "^18.3.11",
78
+ "@types/react-dom": "^18.3.1",
78
79
  "@types/ws": "~8.5.12",
79
- "@vitejs/plugin-react": "^4.3.1",
80
+ "@vitejs/plugin-react": "^4.3.3",
81
+ "@vitest/browser": "^2.1.3",
82
+ "@xstate/react": "^4.1.3",
80
83
  "dotenv": "^16.4.5",
81
84
  "express": "^4.19.2",
82
85
  "express-ws": "^5.0.2",
@@ -85,21 +88,23 @@
85
88
  "jest-environment-node": "^29.7.0",
86
89
  "jsdom": "^25.0.1",
87
90
  "node-ipc": "npm:@node-ipc/compat@9.2.5",
91
+ "playwright": "^1.48.1",
88
92
  "react": "^18.3.1",
89
93
  "react-dom": "^18.3.1",
90
94
  "rxjs": "^7.8.1",
91
95
  "styled-components": "^6.1.13",
92
96
  "ts-node": "^10.9.2",
93
- "typescript": "5.6.2",
94
- "vite": "^5.4.2",
95
- "vitest": "^2.1.1",
97
+ "typescript": "5.6.3",
98
+ "vite": "^5.4.9",
99
+ "vitest": "^2.1.3",
100
+ "vitest-browser-react": "^0.0.1",
96
101
  "@sanity/gherkin-driver": "^0.0.1"
97
102
  },
98
103
  "peerDependencies": {
99
- "@sanity/block-tools": "^3.47.1",
100
- "@sanity/schema": "^3.47.1",
101
- "@sanity/types": "^3.47.1",
102
- "@sanity/util": "^3.47.1",
104
+ "@sanity/block-tools": "^3.61.0",
105
+ "@sanity/schema": "^3.61.0",
106
+ "@sanity/types": "^3.61.0",
107
+ "@sanity/util": "^3.61.0",
103
108
  "react": "^16.9 || ^17 || ^18",
104
109
  "rxjs": "^7.8.1",
105
110
  "styled-components": "^6.1.13"
@@ -11,8 +11,6 @@ import {
11
11
  type ClipboardEvent,
12
12
  type CSSProperties,
13
13
  type FocusEventHandler,
14
- type ForwardedRef,
15
- type HTMLProps,
16
14
  type KeyboardEvent,
17
15
  type MutableRefObject,
18
16
  type TextareaHTMLAttributes,
@@ -66,7 +64,6 @@ import {
66
64
  import {Element} from './components/Element'
67
65
  import {Leaf} from './components/Leaf'
68
66
  import {usePortableTextEditor} from './hooks/usePortableTextEditor'
69
- import {usePortableTextEditorKeyGenerator} from './hooks/usePortableTextEditorKeyGenerator'
70
67
  import {usePortableTextEditorReadOnlyStatus} from './hooks/usePortableTextReadOnly'
71
68
  import {createWithHotkeys, createWithInsertData} from './plugins'
72
69
  import {PortableTextEditor} from './PortableTextEditor'
@@ -113,11 +110,10 @@ export type PortableTextEditableProps = Omit<
113
110
  /**
114
111
  * @public
115
112
  */
116
- export const PortableTextEditable = forwardRef(function PortableTextEditable(
117
- props: PortableTextEditableProps &
118
- Omit<HTMLProps<HTMLDivElement>, 'as' | 'onPaste' | 'onBeforeInput'>,
119
- forwardedRef: ForwardedRef<HTMLDivElement>,
120
- ) {
113
+ export const PortableTextEditable = forwardRef<
114
+ Omit<HTMLDivElement, 'as' | 'onPaste' | 'onBeforeInput'>,
115
+ PortableTextEditableProps
116
+ >(function PortableTextEditable(props, forwardedRef) {
121
117
  const {
122
118
  hotkeys,
123
119
  onBlur,
@@ -142,7 +138,6 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(
142
138
 
143
139
  const portableTextEditor = usePortableTextEditor()
144
140
  const readOnly = usePortableTextEditorReadOnlyStatus()
145
- const keyGenerator = usePortableTextEditorKeyGenerator()
146
141
  const ref = useRef<HTMLDivElement | null>(null)
147
142
  const [editableElement, setEditableElement] = useState<HTMLDivElement | null>(
148
143
  null,
@@ -167,8 +162,8 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(
167
162
 
168
163
  // React/UI-specific plugins
169
164
  const withInsertData = useMemo(
170
- () => createWithInsertData(editorActor, schemaTypes, keyGenerator),
171
- [editorActor, keyGenerator, schemaTypes],
165
+ () => createWithInsertData(editorActor, schemaTypes),
166
+ [editorActor, schemaTypes],
172
167
  )
173
168
  const withHotKeys = useMemo(
174
169
  () => createWithHotkeys(schemaTypes, portableTextEditor, hotkeys),
@@ -28,12 +28,9 @@ import {SlateContainer} from './components/SlateContainer'
28
28
  import {Synchronizer} from './components/Synchronizer'
29
29
  import {editorMachine, type EditorActor} from './editor-machine'
30
30
  import {PortableTextEditorContext} from './hooks/usePortableTextEditor'
31
- import {
32
- defaultKeyGenerator,
33
- PortableTextEditorKeyGeneratorContext,
34
- } from './hooks/usePortableTextEditorKeyGenerator'
35
31
  import {PortableTextEditorSelectionProvider} from './hooks/usePortableTextEditorSelection'
36
32
  import {PortableTextEditorReadOnlyContext} from './hooks/usePortableTextReadOnly'
33
+ import {defaultKeyGenerator} from './key-generator'
37
34
 
38
35
  const debug = debugWithName('component:PortableTextEditor')
39
36
 
@@ -125,7 +122,11 @@ export class PortableTextEditor extends Component<PortableTextEditorProps> {
125
122
  )
126
123
  }
127
124
 
128
- this.editorActor = createActor(editorMachine)
125
+ this.editorActor = createActor(editorMachine, {
126
+ input: {
127
+ keyGenerator: props.keyGenerator || defaultKeyGenerator,
128
+ },
129
+ })
129
130
  this.editorActor.start()
130
131
 
131
132
  this.schemaTypes = getPortableTextMemberSchemaTypes(
@@ -171,39 +172,34 @@ export class PortableTextEditor extends Component<PortableTextEditorProps> {
171
172
  : Number.parseInt(this.props.maxBlocks.toString(), 10) || undefined
172
173
 
173
174
  const readOnly = Boolean(this.props.readOnly)
174
- const keyGenerator = this.props.keyGenerator || defaultKeyGenerator
175
+
175
176
  return (
176
177
  <SlateContainer
177
- keyGenerator={keyGenerator}
178
178
  maxBlocks={maxBlocks}
179
179
  patches$={_patches$}
180
180
  portableTextEditor={this}
181
181
  readOnly={readOnly}
182
182
  >
183
- <PortableTextEditorKeyGeneratorContext.Provider value={keyGenerator}>
184
- <PortableTextEditorContext.Provider value={this}>
185
- <PortableTextEditorReadOnlyContext.Provider value={readOnly}>
186
- <PortableTextEditorSelectionProvider
183
+ <PortableTextEditorContext.Provider value={this}>
184
+ <PortableTextEditorReadOnlyContext.Provider value={readOnly}>
185
+ <PortableTextEditorSelectionProvider editorActor={this.editorActor}>
186
+ <Synchronizer
187
187
  editorActor={this.editorActor}
188
- >
189
- <Synchronizer
190
- editorActor={this.editorActor}
191
- getValue={this.getValue}
192
- onChange={(change) => {
193
- this.props.onChange(change)
194
- /**
195
- * For backwards compatibility, we relay all changes to the
196
- * `change$` Subject as well.
197
- */
198
- this.change$.next(change)
199
- }}
200
- value={value}
201
- />
202
- {children}
203
- </PortableTextEditorSelectionProvider>
204
- </PortableTextEditorReadOnlyContext.Provider>
205
- </PortableTextEditorContext.Provider>
206
- </PortableTextEditorKeyGeneratorContext.Provider>
188
+ getValue={this.getValue}
189
+ onChange={(change) => {
190
+ this.props.onChange(change)
191
+ /**
192
+ * For backwards compatibility, we relay all changes to the
193
+ * `change$` Subject as well.
194
+ */
195
+ this.change$.next(change)
196
+ }}
197
+ value={value}
198
+ />
199
+ {children}
200
+ </PortableTextEditorSelectionProvider>
201
+ </PortableTextEditorReadOnlyContext.Provider>
202
+ </PortableTextEditorContext.Provider>
207
203
  </SlateContainer>
208
204
  )
209
205
  }
@@ -4,7 +4,7 @@ import type {PortableTextBlock, PortableTextSpan} from '@sanity/types'
4
4
  import {render, waitFor} from '@testing-library/react'
5
5
  import {createRef, type ComponentProps, type RefObject} from 'react'
6
6
  import {describe, expect, it, vi} from 'vitest'
7
- import {getTextSelection} from '../../../e2e-tests/__tests__/gherkin-step-helpers'
7
+ import {getTextSelection} from '../../../gherkin-spec/gherkin-step-helpers'
8
8
  import {PortableTextEditable} from '../Editable'
9
9
  import {PortableTextEditor} from '../PortableTextEditor'
10
10
 
@@ -13,7 +13,6 @@ const debug = debugWithName('component:PortableTextEditor:SlateContainer')
13
13
  * @internal
14
14
  */
15
15
  export interface SlateContainerProps extends PropsWithChildren {
16
- keyGenerator: () => string
17
16
  maxBlocks: number | undefined
18
17
  patches$?: PatchObservable
19
18
  portableTextEditor: PortableTextEditor
@@ -25,14 +24,12 @@ export interface SlateContainerProps extends PropsWithChildren {
25
24
  * @internal
26
25
  */
27
26
  export function SlateContainer(props: SlateContainerProps) {
28
- const {patches$, portableTextEditor, readOnly, maxBlocks, keyGenerator} =
29
- props
27
+ const {patches$, portableTextEditor, readOnly, maxBlocks} = props
30
28
 
31
29
  // Create the slate instance, using `useState` ensures setup is only run once, initially
32
30
  const [[slateEditor, subscribe]] = useState(() => {
33
31
  debug('Creating new Slate editor instance')
34
32
  const {editor, subscribe: _sub} = withPlugins(withReact(createEditor()), {
35
- keyGenerator,
36
33
  maxBlocks,
37
34
  patches$,
38
35
  portableTextEditor,
@@ -54,20 +51,12 @@ export function SlateContainer(props: SlateContainerProps) {
54
51
  useEffect(() => {
55
52
  debug('Re-initializing plugin chain')
56
53
  withPlugins(slateEditor, {
57
- keyGenerator,
58
54
  maxBlocks,
59
55
  patches$,
60
56
  portableTextEditor,
61
57
  readOnly,
62
58
  })
63
- }, [
64
- keyGenerator,
65
- portableTextEditor,
66
- maxBlocks,
67
- readOnly,
68
- patches$,
69
- slateEditor,
70
- ])
59
+ }, [portableTextEditor, maxBlocks, readOnly, patches$, slateEditor])
71
60
 
72
61
  const initialValue = useMemo(() => {
73
62
  return [slateEditor.pteCreateTextBlock({decorators: []})]
@@ -9,7 +9,6 @@ import {debugWithName} from '../../utils/debug'
9
9
  import {IS_PROCESSING_LOCAL_CHANGES} from '../../utils/weakMaps'
10
10
  import type {EditorActor} from '../editor-machine'
11
11
  import {usePortableTextEditor} from '../hooks/usePortableTextEditor'
12
- import {usePortableTextEditorKeyGenerator} from '../hooks/usePortableTextEditorKeyGenerator'
13
12
  import {usePortableTextEditorReadOnlyStatus} from '../hooks/usePortableTextReadOnly'
14
13
  import {useSyncValue} from '../hooks/useSyncValue'
15
14
 
@@ -36,14 +35,12 @@ export interface SynchronizerProps {
36
35
  */
37
36
  export function Synchronizer(props: SynchronizerProps) {
38
37
  const portableTextEditor = usePortableTextEditor()
39
- const keyGenerator = usePortableTextEditorKeyGenerator()
40
38
  const readOnly = usePortableTextEditorReadOnlyStatus()
41
39
  const {editorActor, getValue, onChange, value} = props
42
40
  const pendingPatches = useRef<Patch[]>([])
43
41
 
44
42
  const syncValue = useSyncValue({
45
43
  editorActor,
46
- keyGenerator,
47
44
  portableTextEditor,
48
45
  readOnly,
49
46
  })
@@ -90,10 +90,14 @@ type EditorEmittedEvent =
90
90
  export const editorMachine = setup({
91
91
  types: {
92
92
  context: {} as {
93
+ keyGenerator: () => string
93
94
  pendingEvents: Array<PatchEvent | MutationEvent>
94
95
  },
95
96
  events: {} as EditorEvent,
96
97
  emitted: {} as EditorEmittedEvent,
98
+ input: {} as {
99
+ keyGenerator: () => string
100
+ },
97
101
  },
98
102
  actions: {
99
103
  'emit patch event': emit(({event}) => {
@@ -124,9 +128,10 @@ export const editorMachine = setup({
124
128
  },
125
129
  }).createMachine({
126
130
  id: 'editor',
127
- context: {
131
+ context: ({input}) => ({
132
+ keyGenerator: input.keyGenerator,
128
133
  pendingEvents: [],
129
- },
134
+ }),
130
135
  invoke: {
131
136
  id: 'networkLogic',
132
137
  src: 'networkLogic',
@@ -24,7 +24,6 @@ const debug = debugWithName('hook:useSyncValue')
24
24
  */
25
25
  export interface UseSyncValueProps {
26
26
  editorActor: EditorActor
27
- keyGenerator: () => string
28
27
  portableTextEditor: PortableTextEditor
29
28
  readOnly: boolean
30
29
  }
@@ -52,7 +51,7 @@ export function useSyncValue(
52
51
  value: PortableTextBlock[] | undefined,
53
52
  userCallbackFn?: () => void,
54
53
  ) => void {
55
- const {editorActor, portableTextEditor, readOnly, keyGenerator} = props
54
+ const {editorActor, portableTextEditor, readOnly} = props
56
55
  const {schemaTypes} = portableTextEditor
57
56
  const previousValue = useRef<PortableTextBlock[] | undefined>()
58
57
  const slateEditor = useSlate()
@@ -162,7 +161,7 @@ export function useSyncValue(
162
161
  const validation = validateValue(
163
162
  validationValue,
164
163
  schemaTypes,
165
- keyGenerator,
164
+ editorActor.getSnapshot().context.keyGenerator,
166
165
  )
167
166
  // Resolve validations that can be resolved automatically, without involving the user (but only if the value was changed)
168
167
  if (
@@ -222,7 +221,7 @@ export function useSyncValue(
222
221
  const validation = validateValue(
223
222
  validationValue,
224
223
  schemaTypes,
225
- keyGenerator,
224
+ editorActor.getSnapshot().context.keyGenerator,
226
225
  )
227
226
  if (debug.enabled)
228
227
  debug(
@@ -288,7 +287,6 @@ export function useSyncValue(
288
287
  return updateFunction
289
288
  }, [
290
289
  editorActor,
291
- keyGenerator,
292
290
  portableTextEditor,
293
291
  readOnly,
294
292
  schemaTypes,
@@ -0,0 +1,6 @@
1
+ import {randomKey} from '@sanity/util/content'
2
+
3
+ /**
4
+ * @public
5
+ */
6
+ export const defaultKeyGenerator = (): string => randomKey(12)
@@ -38,14 +38,15 @@ import {
38
38
  KEY_TO_VALUE_ELEMENT,
39
39
  SLATE_TO_PORTABLE_TEXT_RANGE,
40
40
  } from '../../utils/weakMaps'
41
+ import type {EditorActor} from '../editor-machine'
41
42
  import type {PortableTextEditor} from '../PortableTextEditor'
42
43
 
43
44
  const debug = debugWithName('API:editable')
44
45
 
45
46
  export function createWithEditableAPI(
47
+ editorActor: EditorActor,
46
48
  portableTextEditor: PortableTextEditor,
47
49
  types: PortableTextMemberSchemaTypes,
48
- keyGenerator: () => string,
49
50
  ) {
50
51
  return function withEditableAPI(
51
52
  editor: PortableTextSlateEditor,
@@ -151,11 +152,11 @@ export function createWithEditableAPI(
151
152
  const block = toSlateValue(
152
153
  [
153
154
  {
154
- _key: keyGenerator(),
155
+ _key: editorActor.getSnapshot().context.keyGenerator(),
155
156
  _type: types.block.name,
156
157
  children: [
157
158
  {
158
- _key: keyGenerator(),
159
+ _key: editorActor.getSnapshot().context.keyGenerator(),
159
160
  _type: type.name,
160
161
  ...(value ? value : {}),
161
162
  },
@@ -199,7 +200,7 @@ export function createWithEditableAPI(
199
200
  const block = toSlateValue(
200
201
  [
201
202
  {
202
- _key: keyGenerator(),
203
+ _key: editorActor.getSnapshot().context.keyGenerator(),
203
204
  _type: type.name,
204
205
  ...(value ? value : {}),
205
206
  },
@@ -469,7 +470,9 @@ export function createWithEditableAPI(
469
470
  continue
470
471
  }
471
472
 
472
- const annotationKey = keyGenerator()
473
+ const annotationKey = editorActor
474
+ .getSnapshot()
475
+ .context.keyGenerator()
473
476
  const markDefs = block.markDefs ?? []
474
477
  const existingMarkDef = markDefs.find(
475
478
  (markDef) =>
@@ -96,6 +96,7 @@ export function createWithHotkeys(
96
96
  at: nextPath,
97
97
  },
98
98
  )
99
+ Transforms.select(editor, {path: [...nextPath, 0], offset: 0})
99
100
  editor.onChange()
100
101
  return
101
102
  }
@@ -5,10 +5,11 @@ import type {
5
5
  PortableTextSlateEditor,
6
6
  } from '../../types/editor'
7
7
  import type {SlateTextBlock, VoidElement} from '../../types/slate'
8
+ import type {EditorActor} from '../editor-machine'
8
9
 
9
10
  export function createWithInsertBreak(
11
+ editorActor: EditorActor,
10
12
  types: PortableTextMemberSchemaTypes,
11
- keyGenerator: () => string,
12
13
  ): (editor: PortableTextSlateEditor) => PortableTextSlateEditor {
13
14
  return function withInsertBreak(
14
15
  editor: PortableTextSlateEditor,
@@ -21,6 +22,24 @@ export function createWithInsertBreak(
21
22
  return
22
23
  }
23
24
 
25
+ const [focusSpan] = Array.from(
26
+ Editor.nodes(editor, {
27
+ mode: 'lowest',
28
+ at: editor.selection.focus,
29
+ match: (n) => editor.isTextSpan(n),
30
+ voids: false,
31
+ }),
32
+ )[0] ?? [undefined]
33
+ const focusDecorators =
34
+ focusSpan.marks?.filter((mark) =>
35
+ types.decorators.some((decorator) => decorator.value === mark),
36
+ ) ?? []
37
+ const focusAnnotations =
38
+ focusSpan.marks?.filter(
39
+ (mark) =>
40
+ !types.decorators.some((decorator) => decorator.value === mark),
41
+ ) ?? []
42
+
24
43
  const focusBlockPath = editor.selection.focus.path.slice(0, 1)
25
44
  const focusBlock = Node.descendant(editor, focusBlockPath) as
26
45
  | SlateTextBlock
@@ -34,15 +53,11 @@ export function createWithInsertBreak(
34
53
  })
35
54
 
36
55
  if (isEndAtStartOfBlock && Range.isCollapsed(editor.selection)) {
37
- const focusDecorators = editor.isTextSpan(focusBlock.children[0])
38
- ? (focusBlock.children[0].marks ?? []).filter((mark) =>
39
- types.decorators.some((decorator) => decorator.value === mark),
40
- )
41
- : []
42
-
43
56
  Editor.insertNode(
44
57
  editor,
45
- editor.pteCreateTextBlock({decorators: focusDecorators}),
58
+ editor.pteCreateTextBlock({
59
+ decorators: focusAnnotations.length === 0 ? focusDecorators : [],
60
+ }),
46
61
  )
47
62
 
48
63
  const [nextBlockPath] = Path.next(focusBlockPath)
@@ -64,6 +79,36 @@ export function createWithInsertBreak(
64
79
  ? lastFocusBlockChild.text.length
65
80
  : 0,
66
81
  })
82
+
83
+ if (
84
+ isStartAtEndOfBlock &&
85
+ Range.isCollapsed(editor.selection) &&
86
+ focusDecorators.length > 0 &&
87
+ focusAnnotations.length > 0
88
+ ) {
89
+ Editor.withoutNormalizing(editor, () => {
90
+ if (!editor.selection) {
91
+ return
92
+ }
93
+
94
+ Editor.insertNode(
95
+ editor,
96
+ editor.pteCreateTextBlock({
97
+ decorators: [],
98
+ }),
99
+ )
100
+
101
+ const [nextBlockPath] = Path.next(focusBlockPath)
102
+
103
+ Transforms.setSelection(editor, {
104
+ anchor: {path: [nextBlockPath, 0], offset: 0},
105
+ focus: {path: [nextBlockPath, 0], offset: 0},
106
+ })
107
+ })
108
+ editor.onChange()
109
+ return
110
+ }
111
+
67
112
  const isInTheMiddleOfNode = !isEndAtStartOfBlock && !isStartAtEndOfBlock
68
113
 
69
114
  if (isInTheMiddleOfNode) {
@@ -131,7 +176,10 @@ export function createWithInsertBreak(
131
176
  ) {
132
177
  // This annotation is both present in the previous block
133
178
  // and this block, so let's assign a new key to it
134
- newMarkDefKeys.set(mark, keyGenerator())
179
+ newMarkDefKeys.set(
180
+ mark,
181
+ editorActor.getSnapshot().context.keyGenerator(),
182
+ )
135
183
  }
136
184
  }
137
185
 
@@ -25,7 +25,6 @@ const debug = debugWithName('plugin:withInsertData')
25
25
  export function createWithInsertData(
26
26
  editorActor: EditorActor,
27
27
  schemaTypes: PortableTextMemberSchemaTypes,
28
- keyGenerator: () => string,
29
28
  ) {
30
29
  return function withInsertData(
31
30
  editor: PortableTextSlateEditor,
@@ -149,12 +148,16 @@ export function createWithInsertData(
149
148
  const slateValue = _regenerateKeys(
150
149
  editor,
151
150
  toSlateValue(parsed, {schemaTypes}),
152
- keyGenerator,
151
+ editorActor.getSnapshot().context.keyGenerator,
153
152
  spanTypeName,
154
153
  schemaTypes,
155
154
  )
156
155
  // Validate the result
157
- const validation = validateValue(parsed, schemaTypes, keyGenerator)
156
+ const validation = validateValue(
157
+ parsed,
158
+ schemaTypes,
159
+ editorActor.getSnapshot().context.keyGenerator,
160
+ )
158
161
  // Bail out if it's not valid
159
162
  if (!validation.valid && !validation.resolution?.autoResolve) {
160
163
  const errorDescription = `${validation.resolution?.description}`
@@ -224,7 +227,7 @@ export function createWithInsertData(
224
227
  const validation = validateValue(
225
228
  portableText,
226
229
  schemaTypes,
227
- keyGenerator,
230
+ editorActor.getSnapshot().context.keyGenerator,
228
231
  )
229
232
 
230
233
  // Bail out if it's not valid
@@ -14,7 +14,6 @@ import type {EditorActor} from '../editor-machine'
14
14
  export function createWithObjectKeys(
15
15
  editorActor: EditorActor,
16
16
  schemaTypes: PortableTextMemberSchemaTypes,
17
- keyGenerator: () => string,
18
17
  ) {
19
18
  return function withKeys(
20
19
  editor: PortableTextSlateEditor,
@@ -48,7 +47,7 @@ export function createWithObjectKeys(
48
47
  ...operation,
49
48
  properties: {
50
49
  ...operation.properties,
51
- _key: keyGenerator(),
50
+ _key: editorActor.getSnapshot().context.keyGenerator(),
52
51
  },
53
52
  })
54
53
 
@@ -61,7 +60,7 @@ export function createWithObjectKeys(
61
60
  ...operation,
62
61
  node: {
63
62
  ...operation.node,
64
- _key: keyGenerator(),
63
+ _key: editorActor.getSnapshot().context.keyGenerator(),
65
64
  },
66
65
  })
67
66
 
@@ -78,7 +77,11 @@ export function createWithObjectKeys(
78
77
  // Set key on block itself
79
78
  if (!node._key) {
80
79
  editorActor.send({type: 'normalizing'})
81
- Transforms.setNodes(editor, {_key: keyGenerator()}, {at: path})
80
+ Transforms.setNodes(
81
+ editor,
82
+ {_key: editorActor.getSnapshot().context.keyGenerator()},
83
+ {at: path},
84
+ )
82
85
  editorActor.send({type: 'done normalizing'})
83
86
  return
84
87
  }
@@ -86,7 +89,11 @@ export function createWithObjectKeys(
86
89
  for (const [child, childPath] of Node.children(editor, path)) {
87
90
  if (!child._key) {
88
91
  editorActor.send({type: 'normalizing'})
89
- Transforms.setNodes(editor, {_key: keyGenerator()}, {at: childPath})
92
+ Transforms.setNodes(
93
+ editor,
94
+ {_key: editorActor.getSnapshot().context.keyGenerator()},
95
+ {at: childPath},
96
+ )
90
97
  editorActor.send({type: 'done normalizing'})
91
98
  return
92
99
  }
@@ -81,7 +81,6 @@ export interface PatchFunctions {
81
81
 
82
82
  interface Options {
83
83
  editorActor: EditorActor
84
- keyGenerator: () => string
85
84
  patches$?: PatchObservable
86
85
  patchFunctions: PatchFunctions
87
86
  readOnly: boolean