@portabletext/editor 1.47.2 → 1.47.4

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": "1.47.2",
3
+ "version": "1.47.4",
4
4
  "description": "Portable Text Editor made in React",
5
5
  "keywords": [
6
6
  "sanity",
@@ -79,8 +79,8 @@
79
79
  "slate-react": "0.112.1",
80
80
  "use-effect-event": "^1.0.2",
81
81
  "xstate": "^5.19.2",
82
- "@portabletext/patches": "1.1.3",
83
- "@portabletext/block-tools": "1.1.18"
82
+ "@portabletext/block-tools": "1.1.18",
83
+ "@portabletext/patches": "1.1.3"
84
84
  },
85
85
  "devDependencies": {
86
86
  "@portabletext/toolkit": "^2.0.17",
@@ -4,5 +4,14 @@ import type {BehaviorActionImplementation} from './behavior.actions'
4
4
  export const insertTextActionImplementation: BehaviorActionImplementation<
5
5
  'insert.text'
6
6
  > = ({action}) => {
7
- Transforms.insertText(action.editor, action.text)
7
+ if (action.editor.marks) {
8
+ Transforms.insertNodes(action.editor, {
9
+ text: action.text,
10
+ ...action.editor.marks,
11
+ })
12
+ } else {
13
+ Transforms.insertText(action.editor, action.text)
14
+ }
15
+
16
+ action.editor.marks = null
8
17
  }
@@ -12,14 +12,12 @@ import {addAnnotationActionImplementation} from './behavior.action.annotation.ad
12
12
  import {removeAnnotationActionImplementation} from './behavior.action.annotation.remove'
13
13
  import {blockSetBehaviorActionImplementation} from './behavior.action.block.set'
14
14
  import {blockUnsetBehaviorActionImplementation} from './behavior.action.block.unset'
15
- import {blurActionImplementation} from './behavior.action.blur'
16
15
  import {decoratorAddActionImplementation} from './behavior.action.decorator.add'
17
16
  import {deleteActionImplementation} from './behavior.action.delete'
18
17
  import {deleteBackwardActionImplementation} from './behavior.action.delete.backward'
19
18
  import {deleteBlockActionImplementation} from './behavior.action.delete.block'
20
19
  import {deleteForwardActionImplementation} from './behavior.action.delete.forward'
21
20
  import {effectActionImplementation} from './behavior.action.effect'
22
- import {focusActionImplementation} from './behavior.action.focus'
23
21
  import {insertInlineObjectActionImplementation} from './behavior.action.insert-inline-object'
24
22
  import {insertSpanActionImplementation} from './behavior.action.insert-span'
25
23
  import {insertBlockActionImplementation} from './behavior.action.insert.block'
@@ -58,10 +56,8 @@ const behaviorActionImplementations: BehaviorActionImplementations = {
58
56
  'annotation.remove': removeAnnotationActionImplementation,
59
57
  'block.set': blockSetBehaviorActionImplementation,
60
58
  'block.unset': blockUnsetBehaviorActionImplementation,
61
- 'blur': blurActionImplementation,
62
59
  'decorator.add': decoratorAddActionImplementation,
63
60
  'decorator.remove': removeDecoratorActionImplementation,
64
- 'focus': focusActionImplementation,
65
61
  'delete': deleteActionImplementation,
66
62
  'delete.backward': deleteBackwardActionImplementation,
67
63
  'delete.forward': deleteForwardActionImplementation,
@@ -119,13 +115,6 @@ export function performAction({
119
115
  })
120
116
  break
121
117
  }
122
- case 'blur': {
123
- behaviorActionImplementations.blur({
124
- context,
125
- action,
126
- })
127
- break
128
- }
129
118
  case 'decorator.add': {
130
119
  behaviorActionImplementations['decorator.add']({
131
120
  context,
@@ -175,13 +164,6 @@ export function performAction({
175
164
  })
176
165
  break
177
166
  }
178
- case 'focus': {
179
- behaviorActionImplementations.focus({
180
- context,
181
- action,
182
- })
183
- break
184
- }
185
167
  case 'history.redo': {
186
168
  behaviorActionImplementations['history.redo']({
187
169
  context,
@@ -37,7 +37,6 @@ export function performEvent({
37
37
  schema,
38
38
  getSnapshot,
39
39
  nativeEvent,
40
- defaultActionCallback,
41
40
  }: {
42
41
  mode: 'raise' | 'execute'
43
42
  behaviors: Array<Behavior>
@@ -46,7 +45,6 @@ export function performEvent({
46
45
  keyGenerator: () => string
47
46
  schema: EditorSchema
48
47
  getSnapshot: () => EditorSnapshot
49
- defaultActionCallback: (() => void) | undefined
50
48
  nativeEvent:
51
49
  | {
52
50
  preventDefault: () => void
@@ -105,21 +103,6 @@ export function performEvent({
105
103
  })
106
104
 
107
105
  if (eventBehaviors.length === 0) {
108
- if (defaultActionCallback) {
109
- withApplyingBehaviorActions(editor, () => {
110
- try {
111
- defaultActionCallback()
112
- } catch (error) {
113
- console.error(
114
- new Error(
115
- `Performing action "${event.type}" failed due to: ${error.message}`,
116
- ),
117
- )
118
- }
119
- })
120
- return
121
- }
122
-
123
106
  if (!defaultAction) {
124
107
  return
125
108
  }
@@ -133,7 +116,6 @@ export function performEvent({
133
116
  },
134
117
  action: defaultAction,
135
118
  })
136
- editor.onChange()
137
119
  } catch (error) {
138
120
  console.error(
139
121
  new Error(
@@ -142,6 +124,9 @@ export function performEvent({
142
124
  )
143
125
  }
144
126
  })
127
+
128
+ editor.onChange()
129
+
145
130
  return
146
131
  }
147
132
 
@@ -196,7 +181,6 @@ export function performEvent({
196
181
  keyGenerator,
197
182
  schema,
198
183
  getSnapshot,
199
- defaultActionCallback: undefined,
200
184
  nativeEvent: undefined,
201
185
  })
202
186
 
@@ -218,26 +202,39 @@ export function performEvent({
218
202
  keyGenerator,
219
203
  schema,
220
204
  getSnapshot,
221
- defaultActionCallback: undefined,
222
205
  nativeEvent: undefined,
223
206
  })
224
207
  } else {
225
- try {
226
- performAction({
227
- context: {
228
- keyGenerator,
229
- schema,
230
- },
231
- action: {...action.event, editor},
232
- })
233
- } catch (error) {
234
- console.error(
235
- new Error(
236
- `Performing action "${action.event.type}" as a result of "${event.type}" failed due to: ${error.message}`,
237
- ),
238
- )
208
+ const internalAction = {
209
+ ...action.event,
210
+ editor,
211
+ }
212
+ let actionFailed = false
213
+
214
+ withApplyingBehaviorActions(editor, () => {
215
+ try {
216
+ performAction({
217
+ context: {
218
+ keyGenerator,
219
+ schema,
220
+ },
221
+ action: internalAction,
222
+ })
223
+ } catch (error) {
224
+ console.error(
225
+ new Error(
226
+ `Performing action "${action.event.type}" as a result of "${event.type}" failed due to: ${error.message}`,
227
+ ),
228
+ )
229
+ actionFailed = true
230
+ }
231
+ })
232
+
233
+ if (actionFailed) {
239
234
  break
240
235
  }
236
+
237
+ editor.onChange()
241
238
  }
242
239
 
243
240
  continue
@@ -247,25 +244,33 @@ export function performEvent({
247
244
  ...action,
248
245
  editor,
249
246
  }
247
+ let actionFailed = false
250
248
 
251
- try {
252
- performAction({
253
- context: {
254
- keyGenerator,
255
- schema,
256
- },
257
- action: internalAction,
258
- })
259
- } catch (error) {
260
- console.error(
261
- new Error(
262
- `Performing action "${internalAction.type}" as a result of "${event.type}" failed due to: ${error.message}`,
263
- ),
264
- )
249
+ withApplyingBehaviorActions(editor, () => {
250
+ try {
251
+ performAction({
252
+ context: {
253
+ keyGenerator,
254
+ schema,
255
+ },
256
+ action: internalAction,
257
+ })
258
+ } catch (error) {
259
+ console.error(
260
+ new Error(
261
+ `Performing action "${internalAction.type}" as a result of "${event.type}" failed due to: ${error.message}`,
262
+ ),
263
+ )
264
+ actionFailed = true
265
+ }
266
+ })
267
+
268
+ if (actionFailed) {
265
269
  break
266
270
  }
271
+
272
+ editor.onChange()
267
273
  }
268
- editor.onChange()
269
274
  })
270
275
  }
271
276
 
@@ -276,21 +281,6 @@ export function performEvent({
276
281
  }
277
282
 
278
283
  if (!behaviorOverwritten) {
279
- if (defaultActionCallback) {
280
- withApplyingBehaviorActions(editor, () => {
281
- try {
282
- defaultActionCallback()
283
- } catch (error) {
284
- console.error(
285
- new Error(
286
- `Performing "${event.type}" failed due to: ${error.message}`,
287
- ),
288
- )
289
- }
290
- })
291
- return
292
- }
293
-
294
284
  if (!defaultAction) {
295
285
  return
296
286
  }
@@ -304,7 +294,6 @@ export function performEvent({
304
294
  },
305
295
  action: defaultAction,
306
296
  })
307
- editor.onChange()
308
297
  } catch (error) {
309
298
  console.error(
310
299
  new Error(
@@ -313,5 +302,6 @@ export function performEvent({
313
302
  )
314
303
  }
315
304
  })
305
+ editor.onChange()
316
306
  }
317
307
  }
@@ -32,7 +32,7 @@ type NamespacedBehaviorEventType<
32
32
  * External events
33
33
  **************************************/
34
34
 
35
- type ExternalBehaviorEventNamespace = 'insert'
35
+ type ExternalBehaviorEventNamespace = 'blur' | 'focus' | 'insert'
36
36
 
37
37
  type ExternalBehaviorEventType<
38
38
  TNamespace extends ExternalBehaviorEventNamespace,
@@ -40,6 +40,12 @@ type ExternalBehaviorEventType<
40
40
  > = TType extends '' ? `${TNamespace}` : `${TNamespace}.${TType}`
41
41
 
42
42
  export type ExternalBehaviorEvent =
43
+ | {
44
+ type: ExternalBehaviorEventType<'blur'>
45
+ }
46
+ | {
47
+ type: ExternalBehaviorEventType<'focus'>
48
+ }
43
49
  | {
44
50
  type: ExternalBehaviorEventType<'insert', 'block object'>
45
51
  placement: InsertPlacement
@@ -61,14 +67,12 @@ const syntheticBehaviorEventTypes = [
61
67
  'annotation.remove',
62
68
  'block.set',
63
69
  'block.unset',
64
- 'blur',
65
70
  'decorator.add',
66
71
  'decorator.remove',
67
72
  'delete',
68
73
  'delete.backward',
69
74
  'delete.block',
70
75
  'delete.forward',
71
- 'focus',
72
76
  'history.redo',
73
77
  'history.undo',
74
78
  'insert.inline object',
@@ -114,9 +118,6 @@ export type SyntheticBehaviorEvent =
114
118
  at: [KeyedSegment]
115
119
  props: Array<string>
116
120
  }
117
- | {
118
- type: StrictExtract<SyntheticBehaviorEventType, 'blur'>
119
- }
120
121
  | {
121
122
  type: StrictExtract<SyntheticBehaviorEventType, 'decorator.add'>
122
123
  decorator: string
@@ -145,9 +146,6 @@ export type SyntheticBehaviorEvent =
145
146
  type: StrictExtract<SyntheticBehaviorEventType, 'delete.forward'>
146
147
  unit: TextUnit
147
148
  }
148
- | {
149
- type: StrictExtract<SyntheticBehaviorEventType, 'focus'>
150
- }
151
149
  | {
152
150
  type: StrictExtract<SyntheticBehaviorEventType, 'history.redo'>
153
151
  }
@@ -16,7 +16,7 @@ import {
16
16
  type MutableRefObject,
17
17
  type TextareaHTMLAttributes,
18
18
  } from 'react'
19
- import {Editor, Transforms, type Text} from 'slate'
19
+ import {Transforms, type Text} from 'slate'
20
20
  import {
21
21
  ReactEditor,
22
22
  Editable as SlateEditable,
@@ -34,6 +34,7 @@ import {normalizeSelection} from '../internal-utils/selection'
34
34
  import {getSelectionDomNodes} from '../internal-utils/selection-elements'
35
35
  import {slateRangeToSelection} from '../internal-utils/slate-utils'
36
36
  import {fromSlateValue} from '../internal-utils/values'
37
+ import {KEY_TO_VALUE_ELEMENT} from '../internal-utils/weakMaps'
37
38
  import * as selectors from '../selectors'
38
39
  import type {
39
40
  EditorSelection,
@@ -58,7 +59,6 @@ import {EditorActorContext} from './editor-actor-context'
58
59
  import {getEditorSnapshot} from './editor-selector'
59
60
  import {usePortableTextEditor} from './hooks/usePortableTextEditor'
60
61
  import {createWithHotkeys} from './plugins/createWithHotKeys'
61
- import {PortableTextEditor} from './PortableTextEditor'
62
62
  import {
63
63
  createDecorate,
64
64
  rangeDecorationsMachine,
@@ -444,7 +444,11 @@ export const PortableTextEditable = forwardRef<
444
444
  // Handle incoming pasting events in the editor
445
445
  const handlePaste = useCallback(
446
446
  (event: ClipboardEvent<HTMLDivElement>): Promise<void> | void => {
447
- const value = PortableTextEditor.getValue(portableTextEditor)
447
+ const value = fromSlateValue(
448
+ slateEditor.children,
449
+ editorActor.getSnapshot().context.schema.block.name,
450
+ KEY_TO_VALUE_ELEMENT.get(slateEditor),
451
+ )
448
452
  const ptRange = slateEditor.selection
449
453
  ? slateRangeToSelection({
450
454
  schema: editorActor.getSnapshot().context.schema,
@@ -566,16 +570,17 @@ export const PortableTextEditable = forwardRef<
566
570
  onFocus(event)
567
571
  }
568
572
  if (!event.isDefaultPrevented()) {
569
- const selection = PortableTextEditor.getSelection(portableTextEditor)
570
- // Create an editor selection if it does'nt exist
571
- if (selection === null) {
572
- Transforms.select(slateEditor, Editor.start(slateEditor, []))
573
- slateEditor.onChange()
574
- }
575
573
  editorActor.send({type: 'notify.focused', event})
576
- const newSelection = PortableTextEditor.getSelection(portableTextEditor)
577
- // If the selection is the same, emit it explicitly here as there is no actual onChange event triggered.
578
- if (selection === newSelection) {
574
+
575
+ const selection = slateEditor.selection
576
+ ? slateRangeToSelection({
577
+ schema: editorActor.getSnapshot().context.schema,
578
+ editor: slateEditor,
579
+ range: slateEditor.selection,
580
+ })
581
+ : null
582
+
583
+ if (selection) {
579
584
  editorActor.send({
580
585
  type: 'notify.selection',
581
586
  selection,
@@ -583,7 +588,7 @@ export const PortableTextEditable = forwardRef<
583
588
  }
584
589
  }
585
590
  },
586
- [editorActor, onFocus, portableTextEditor, slateEditor],
591
+ [editorActor, onFocus, slateEditor],
587
592
  )
588
593
 
589
594
  const handleClick = useCallback(
@@ -191,6 +191,20 @@ function createInternalEditorFromActor(
191
191
  editorActor.send(event)
192
192
  break
193
193
 
194
+ case 'blur':
195
+ editorActor.send({
196
+ type: 'blur',
197
+ editor: slateEditor.instance,
198
+ })
199
+ break
200
+
201
+ case 'focus':
202
+ editorActor.send({
203
+ type: 'focus',
204
+ editor: slateEditor.instance,
205
+ })
206
+ break
207
+
194
208
  case 'insert.block object':
195
209
  editorActor.send({
196
210
  type: 'behavior event',
@@ -1,6 +1,8 @@
1
1
  import type {Patch} from '@portabletext/patches'
2
2
  import type {PortableTextBlock} from '@sanity/types'
3
3
  import type {FocusEvent} from 'react'
4
+ import {Transforms} from 'slate'
5
+ import {ReactEditor} from 'slate-react'
4
6
  import {
5
7
  assertEvent,
6
8
  assign,
@@ -161,6 +163,14 @@ export type HasTag = ReturnType<EditorActor['getSnapshot']>['hasTag']
161
163
  */
162
164
  export type InternalEditorEvent =
163
165
  | ExternalEditorEvent
166
+ | {
167
+ type: 'blur'
168
+ editor: PortableTextSlateEditor
169
+ }
170
+ | {
171
+ type: 'focus'
172
+ editor: PortableTextSlateEditor
173
+ }
164
174
  | {
165
175
  type: 'normalizing'
166
176
  }
@@ -174,7 +184,6 @@ export type InternalEditorEvent =
174
184
  type: 'behavior event'
175
185
  behaviorEvent: BehaviorEvent
176
186
  editor: PortableTextSlateEditor
177
- defaultActionCallback?: () => void
178
187
  nativeEvent?: {preventDefault: () => void}
179
188
  }
180
189
  | MutationEvent
@@ -217,6 +226,7 @@ export const editorMachine = setup({
217
226
  ghost?: HTMLElement
218
227
  origin: Pick<EventPosition, 'selection'>
219
228
  }
229
+ slateEditor?: PortableTextSlateEditor
220
230
  },
221
231
  events: {} as InternalEditorEvent,
222
232
  emitted: {} as InternalEditorEmittedEvent,
@@ -292,6 +302,31 @@ export const editorMachine = setup({
292
302
  'clear pending events': assign({
293
303
  pendingEvents: [],
294
304
  }),
305
+ 'handle blur': ({event}) => {
306
+ assertEvent(event, 'blur')
307
+
308
+ try {
309
+ ReactEditor.blur(event.editor)
310
+ } catch (error) {
311
+ console.error(new Error(`Failed to blur editor: ${error.message}`))
312
+ }
313
+ },
314
+ 'handle focus': ({context}) => {
315
+ if (!context.slateEditor) {
316
+ console.error('No Slate editor found to focus')
317
+ return
318
+ }
319
+
320
+ try {
321
+ const currentSelection = context.slateEditor.selection
322
+ ReactEditor.focus(context.slateEditor)
323
+ if (currentSelection) {
324
+ Transforms.select(context.slateEditor, currentSelection)
325
+ }
326
+ } catch (error) {
327
+ console.error(new Error(`Failed to focus editor: ${error.message}`))
328
+ }
329
+ },
295
330
  'handle behavior event': ({context, event, self}) => {
296
331
  assertEvent(event, ['behavior event'])
297
332
 
@@ -313,13 +348,18 @@ export const editorMachine = setup({
313
348
  internalDrag: context.internalDrag,
314
349
  }),
315
350
  nativeEvent: event.nativeEvent,
316
- defaultActionCallback:
317
- event.type === 'behavior event'
318
- ? event.defaultActionCallback
319
- : undefined,
320
351
  })
321
352
  },
322
353
  },
354
+ guards: {
355
+ 'slate is busy': ({context}) => {
356
+ if (!context.slateEditor) {
357
+ return false
358
+ }
359
+
360
+ return context.slateEditor.operations.length > 0
361
+ },
362
+ },
323
363
  }).createMachine({
324
364
  id: 'editor',
325
365
  context: ({input}) => ({
@@ -426,6 +466,13 @@ export const editorMachine = setup({
426
466
  'behavior event': {
427
467
  actions: 'handle behavior event',
428
468
  },
469
+ 'blur': {
470
+ actions: 'handle blur',
471
+ },
472
+ 'focus': {
473
+ target: '.focusing',
474
+ actions: [assign({slateEditor: ({event}) => event.editor})],
475
+ },
429
476
  },
430
477
  initial: 'idle',
431
478
  states: {
@@ -444,6 +491,30 @@ export const editorMachine = setup({
444
491
  },
445
492
  },
446
493
  },
494
+ 'focusing': {
495
+ initial: 'checking if busy',
496
+ states: {
497
+ 'checking if busy': {
498
+ always: [
499
+ {
500
+ guard: 'slate is busy',
501
+ target: 'busy',
502
+ },
503
+ {
504
+ target: '#editor.edit mode.editable.idle',
505
+ actions: ['handle focus'],
506
+ },
507
+ ],
508
+ },
509
+ 'busy': {
510
+ after: {
511
+ 10: {
512
+ target: 'checking if busy',
513
+ },
514
+ },
515
+ },
516
+ },
517
+ },
447
518
  'dragging internally': {
448
519
  exit: [
449
520
  ({context}) => {
@@ -116,9 +116,6 @@ export function createWithEventListeners(editorActor: EditorActor) {
116
116
  text,
117
117
  },
118
118
  editor,
119
- defaultActionCallback: () => {
120
- insertText(text, options)
121
- },
122
119
  })
123
120
  return
124
121
  }
@@ -167,9 +164,6 @@ export function createWithEventListeners(editorActor: EditorActor) {
167
164
  }),
168
165
  },
169
166
  editor,
170
- defaultActionCallback: () => {
171
- select(location)
172
- },
173
167
  })
174
168
  return
175
169
  }
@@ -49,19 +49,13 @@ export function createEditableAPI(
49
49
  const editableApi: EditableAPI = {
50
50
  focus: (): void => {
51
51
  editorActor.send({
52
- type: 'behavior event',
53
- behaviorEvent: {
54
- type: 'focus',
55
- },
52
+ type: 'focus',
56
53
  editor,
57
54
  })
58
55
  },
59
56
  blur: (): void => {
60
57
  editorActor.send({
61
- type: 'behavior event',
62
- behaviorEvent: {
63
- type: 'blur',
64
- },
58
+ type: 'blur',
65
59
  editor,
66
60
  })
67
61
  },
@@ -28,7 +28,7 @@ export function withApplyingBehaviorActionSet(editor: Editor, fn: () => void) {
28
28
  const current = CURRENT_BEHAVIOR_ACTION_SET.get(editor)
29
29
 
30
30
  if (current) {
31
- withApplyingBehaviorActions(editor, fn)
31
+ fn()
32
32
  return
33
33
  }
34
34
 
@@ -38,7 +38,7 @@ export function withApplyingBehaviorActionSet(editor: Editor, fn: () => void) {
38
38
  actionSetId: defaultKeyGenerator(),
39
39
  },
40
40
  )
41
- withApplyingBehaviorActions(editor, fn)
41
+ fn()
42
42
  CURRENT_BEHAVIOR_ACTION_SET.set(editor, undefined)
43
43
  }
44
44
 
@@ -1,8 +0,0 @@
1
- import {ReactEditor} from 'slate-react'
2
- import type {BehaviorActionImplementation} from './behavior.actions'
3
-
4
- export const blurActionImplementation: BehaviorActionImplementation<'blur'> = ({
5
- action,
6
- }) => {
7
- ReactEditor.blur(action.editor)
8
- }
@@ -1,8 +0,0 @@
1
- import {ReactEditor} from 'slate-react'
2
- import type {BehaviorActionImplementation} from './behavior.actions'
3
-
4
- export const focusActionImplementation: BehaviorActionImplementation<
5
- 'focus'
6
- > = ({action}) => {
7
- ReactEditor.focus(action.editor)
8
- }