@portabletext/editor 3.3.2 → 3.3.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.
Files changed (307) hide show
  1. package/lib/_chunks-dts/index.d.ts +7 -1
  2. package/package.json +16 -19
  3. package/src/behaviors/_exports/index.ts +0 -1
  4. package/src/behaviors/behavior.abstract.annotation.ts +0 -77
  5. package/src/behaviors/behavior.abstract.decorator.ts +0 -39
  6. package/src/behaviors/behavior.abstract.delete.ts +0 -273
  7. package/src/behaviors/behavior.abstract.deserialize.ts +0 -232
  8. package/src/behaviors/behavior.abstract.insert.ts +0 -525
  9. package/src/behaviors/behavior.abstract.keyboard.ts +0 -189
  10. package/src/behaviors/behavior.abstract.list-item.ts +0 -70
  11. package/src/behaviors/behavior.abstract.move.ts +0 -79
  12. package/src/behaviors/behavior.abstract.select.ts +0 -118
  13. package/src/behaviors/behavior.abstract.serialize.ts +0 -96
  14. package/src/behaviors/behavior.abstract.split.ts +0 -170
  15. package/src/behaviors/behavior.abstract.style.ts +0 -55
  16. package/src/behaviors/behavior.abstract.ts +0 -139
  17. package/src/behaviors/behavior.config.ts +0 -7
  18. package/src/behaviors/behavior.core.annotations.ts +0 -62
  19. package/src/behaviors/behavior.core.block-element.ts +0 -116
  20. package/src/behaviors/behavior.core.block-objects.ts +0 -285
  21. package/src/behaviors/behavior.core.decorators.ts +0 -44
  22. package/src/behaviors/behavior.core.dnd.ts +0 -356
  23. package/src/behaviors/behavior.core.insert-break.ts +0 -266
  24. package/src/behaviors/behavior.core.insert.ts +0 -52
  25. package/src/behaviors/behavior.core.lists.ts +0 -691
  26. package/src/behaviors/behavior.core.ts +0 -44
  27. package/src/behaviors/behavior.perform-event.ts +0 -354
  28. package/src/behaviors/behavior.types.action.ts +0 -102
  29. package/src/behaviors/behavior.types.behavior.ts +0 -83
  30. package/src/behaviors/behavior.types.event.ts +0 -667
  31. package/src/behaviors/behavior.types.guard.ts +0 -11
  32. package/src/behaviors/index.ts +0 -17
  33. package/src/converters/converter.json.ts +0 -53
  34. package/src/converters/converter.portable-text.deserialize.test.ts +0 -680
  35. package/src/converters/converter.portable-text.ts +0 -75
  36. package/src/converters/converter.text-html.deserialize.test.ts +0 -406
  37. package/src/converters/converter.text-html.serialize.test.ts +0 -246
  38. package/src/converters/converter.text-html.ts +0 -87
  39. package/src/converters/converter.text-markdown.ts +0 -67
  40. package/src/converters/converter.text-plain.test.ts +0 -245
  41. package/src/converters/converter.text-plain.ts +0 -126
  42. package/src/converters/converter.types.ts +0 -72
  43. package/src/converters/converters.core.ts +0 -18
  44. package/src/editor/Editable.tsx +0 -1012
  45. package/src/editor/PortableTextEditor.tsx +0 -791
  46. package/src/editor/components/drop-indicator.tsx +0 -17
  47. package/src/editor/components/render-block-object.tsx +0 -121
  48. package/src/editor/components/render-default-object.tsx +0 -21
  49. package/src/editor/components/render-element.tsx +0 -88
  50. package/src/editor/components/render-inline-object.tsx +0 -129
  51. package/src/editor/components/render-leaf.tsx +0 -64
  52. package/src/editor/components/render-span.tsx +0 -303
  53. package/src/editor/components/render-text-block.tsx +0 -265
  54. package/src/editor/components/render-text.tsx +0 -18
  55. package/src/editor/components/use-core-block-element-behaviors.ts +0 -39
  56. package/src/editor/create-editor.ts +0 -323
  57. package/src/editor/create-slate-editor.tsx +0 -64
  58. package/src/editor/editor-actor-context.ts +0 -4
  59. package/src/editor/editor-context.tsx +0 -7
  60. package/src/editor/editor-dom.ts +0 -220
  61. package/src/editor/editor-machine.ts +0 -751
  62. package/src/editor/editor-provider.tsx +0 -111
  63. package/src/editor/editor-schema.ts +0 -6
  64. package/src/editor/editor-selector.ts +0 -89
  65. package/src/editor/editor-snapshot.ts +0 -68
  66. package/src/editor/event-to-change.tsx +0 -49
  67. package/src/editor/hooks/usePortableTextEditor.ts +0 -25
  68. package/src/editor/hooks/usePortableTextEditorSelection.tsx +0 -28
  69. package/src/editor/mutation-machine.ts +0 -322
  70. package/src/editor/plugins/create-with-event-listeners.ts +0 -271
  71. package/src/editor/plugins/createWithEditableAPI.ts +0 -529
  72. package/src/editor/plugins/createWithHotKeys.ts +0 -68
  73. package/src/editor/plugins/createWithObjectKeys.ts +0 -289
  74. package/src/editor/plugins/createWithPatches.ts +0 -272
  75. package/src/editor/plugins/createWithPortableTextMarkModel.ts +0 -559
  76. package/src/editor/plugins/createWithSchemaTypes.ts +0 -121
  77. package/src/editor/plugins/slate-plugin.update-selection.ts +0 -51
  78. package/src/editor/plugins/slate-plugin.update-value.ts +0 -46
  79. package/src/editor/plugins/with-plugins.ts +0 -69
  80. package/src/editor/range-decorations-machine.ts +0 -421
  81. package/src/editor/relay-actor-context.ts +0 -4
  82. package/src/editor/relay-machine.ts +0 -152
  83. package/src/editor/sync-machine.ts +0 -961
  84. package/src/editor/use-editor.ts +0 -27
  85. package/src/editor/validate-selection-machine.test.ts +0 -47
  86. package/src/editor/validate-selection-machine.ts +0 -149
  87. package/src/editor/weakMaps.ts +0 -15
  88. package/src/editor/with-normalizing-node.ts +0 -14
  89. package/src/editor/with-performing-behavior-operation.ts +0 -21
  90. package/src/editor/withChanges.ts +0 -13
  91. package/src/editor/without-normalizing-conditional.ts +0 -13
  92. package/src/editor/withoutPatching.ts +0 -14
  93. package/src/editor.ts +0 -59
  94. package/src/history/behavior.operation.history.redo.ts +0 -67
  95. package/src/history/behavior.operation.history.undo.ts +0 -71
  96. package/src/history/event.history.undo.test.tsx +0 -672
  97. package/src/history/history.preserving-keys.test.tsx +0 -112
  98. package/src/history/remote-patches.ts +0 -20
  99. package/src/history/slate-plugin.history.ts +0 -142
  100. package/src/history/slate-plugin.redoing.ts +0 -21
  101. package/src/history/slate-plugin.undoing.ts +0 -21
  102. package/src/history/slate-plugin.without-history.ts +0 -23
  103. package/src/history/transform-operation.ts +0 -245
  104. package/src/history/undo-redo-collaboration.test.tsx +0 -541
  105. package/src/history/undo-redo.feature +0 -125
  106. package/src/history/undo-redo.test.tsx +0 -195
  107. package/src/history/undo-step.ts +0 -148
  108. package/src/index.ts +0 -98
  109. package/src/internal-utils/__tests__/ranges.test.ts +0 -23
  110. package/src/internal-utils/__tests__/values.test.ts +0 -110
  111. package/src/internal-utils/apply-operation-to-portable-text.test.ts +0 -1861
  112. package/src/internal-utils/apply-operation-to-portable-text.ts +0 -615
  113. package/src/internal-utils/applyPatch.ts +0 -644
  114. package/src/internal-utils/block-keys.ts +0 -9
  115. package/src/internal-utils/build-index-maps.test.ts +0 -464
  116. package/src/internal-utils/build-index-maps.ts +0 -131
  117. package/src/internal-utils/collapse-selection.ts +0 -36
  118. package/src/internal-utils/compound-client-rect.ts +0 -28
  119. package/src/internal-utils/create-placeholder-block.ts +0 -21
  120. package/src/internal-utils/create-test-snapshot.ts +0 -28
  121. package/src/internal-utils/debug.ts +0 -12
  122. package/src/internal-utils/editor-selection.test.ts +0 -44
  123. package/src/internal-utils/editor-selection.ts +0 -56
  124. package/src/internal-utils/event-position.ts +0 -318
  125. package/src/internal-utils/global-scope.ts +0 -27
  126. package/src/internal-utils/globally-scoped-context.ts +0 -39
  127. package/src/internal-utils/is-hotkey.test.ts +0 -114
  128. package/src/internal-utils/is-hotkey.ts +0 -209
  129. package/src/internal-utils/mime-type.ts +0 -1
  130. package/src/internal-utils/move-range-by-operation.ts +0 -19
  131. package/src/internal-utils/operation-to-patches.test.ts +0 -522
  132. package/src/internal-utils/operation-to-patches.ts +0 -571
  133. package/src/internal-utils/portable-text-node.ts +0 -209
  134. package/src/internal-utils/schema.ts +0 -8
  135. package/src/internal-utils/selection-block-keys.ts +0 -20
  136. package/src/internal-utils/selection-focus-text.ts +0 -40
  137. package/src/internal-utils/selection-text.test.ts +0 -32
  138. package/src/internal-utils/selection-text.ts +0 -21
  139. package/src/internal-utils/selection.ts +0 -77
  140. package/src/internal-utils/sibling-utils.ts +0 -55
  141. package/src/internal-utils/slate-utils.test.tsx +0 -121
  142. package/src/internal-utils/slate-utils.ts +0 -417
  143. package/src/internal-utils/split-string.ts +0 -12
  144. package/src/internal-utils/stop-actor.ts +0 -43
  145. package/src/internal-utils/string-overlap.test.ts +0 -14
  146. package/src/internal-utils/string-overlap.ts +0 -28
  147. package/src/internal-utils/string-utils.ts +0 -7
  148. package/src/internal-utils/text-block-key.test.ts +0 -41
  149. package/src/internal-utils/text-block-key.ts +0 -26
  150. package/src/internal-utils/text-marks.test.ts +0 -41
  151. package/src/internal-utils/text-marks.ts +0 -22
  152. package/src/internal-utils/text-selection.test.ts +0 -211
  153. package/src/internal-utils/text-selection.ts +0 -121
  154. package/src/internal-utils/to-slate-range.test.ts +0 -278
  155. package/src/internal-utils/to-slate-range.ts +0 -171
  156. package/src/internal-utils/validateValue.ts +0 -443
  157. package/src/internal-utils/value-annotations.ts +0 -33
  158. package/src/internal-utils/values.test.ts +0 -282
  159. package/src/internal-utils/values.ts +0 -266
  160. package/src/keyboard-shortcuts/default-keyboard-shortcuts.ts +0 -146
  161. package/src/operations/behavior.operation.annotation.add.ts +0 -99
  162. package/src/operations/behavior.operation.annotation.remove.ts +0 -150
  163. package/src/operations/behavior.operation.block.set.ts +0 -104
  164. package/src/operations/behavior.operation.block.unset.ts +0 -54
  165. package/src/operations/behavior.operation.child.set.ts +0 -107
  166. package/src/operations/behavior.operation.child.unset.ts +0 -116
  167. package/src/operations/behavior.operation.decorator.add.ts +0 -131
  168. package/src/operations/behavior.operation.delete.ts +0 -294
  169. package/src/operations/behavior.operation.insert.block.ts +0 -495
  170. package/src/operations/behavior.operation.insert.child.ts +0 -129
  171. package/src/operations/behavior.operation.insert.text.ts +0 -8
  172. package/src/operations/behavior.operation.move.backward.ts +0 -12
  173. package/src/operations/behavior.operation.move.block.ts +0 -44
  174. package/src/operations/behavior.operation.move.forward.ts +0 -11
  175. package/src/operations/behavior.operation.select.ts +0 -27
  176. package/src/operations/behavior.operations.ts +0 -221
  177. package/src/plugins/_exports/index.ts +0 -1
  178. package/src/plugins/index.ts +0 -3
  179. package/src/plugins/plugin.behavior.tsx +0 -24
  180. package/src/plugins/plugin.editor-ref.tsx +0 -17
  181. package/src/plugins/plugin.event-listener.tsx +0 -68
  182. package/src/plugins/plugin.internal.auto-close-brackets.test.tsx +0 -71
  183. package/src/plugins/plugin.internal.auto-close-brackets.ts +0 -62
  184. package/src/plugins/plugin.internal.change-ref.tsx +0 -19
  185. package/src/plugins/plugin.internal.portable-text-editor-ref.tsx +0 -16
  186. package/src/plugins/plugin.internal.slate-editor-ref.tsx +0 -15
  187. package/src/priority/priority.core.ts +0 -3
  188. package/src/priority/priority.sort.test.ts +0 -319
  189. package/src/priority/priority.sort.ts +0 -123
  190. package/src/priority/priority.types.ts +0 -24
  191. package/src/selectors/_exports/index.ts +0 -1
  192. package/src/selectors/drag-selection.test.ts +0 -578
  193. package/src/selectors/drag-selection.ts +0 -118
  194. package/src/selectors/index.ts +0 -54
  195. package/src/selectors/selector.get-active-annotation-marks.ts +0 -12
  196. package/src/selectors/selector.get-active-annotations.ts +0 -36
  197. package/src/selectors/selector.get-active-decorators.ts +0 -29
  198. package/src/selectors/selector.get-active-list-item.ts +0 -38
  199. package/src/selectors/selector.get-active-style.ts +0 -38
  200. package/src/selectors/selector.get-anchor-block.ts +0 -22
  201. package/src/selectors/selector.get-anchor-child.ts +0 -36
  202. package/src/selectors/selector.get-anchor-span.ts +0 -17
  203. package/src/selectors/selector.get-anchor-text-block.ts +0 -18
  204. package/src/selectors/selector.get-block-offsets.ts +0 -34
  205. package/src/selectors/selector.get-caret-word-selection.test.ts +0 -284
  206. package/src/selectors/selector.get-caret-word-selection.ts +0 -134
  207. package/src/selectors/selector.get-first-block.ts +0 -14
  208. package/src/selectors/selector.get-focus-block-object.ts +0 -18
  209. package/src/selectors/selector.get-focus-block.ts +0 -23
  210. package/src/selectors/selector.get-focus-child.ts +0 -36
  211. package/src/selectors/selector.get-focus-inline-object.ts +0 -17
  212. package/src/selectors/selector.get-focus-list-block.ts +0 -18
  213. package/src/selectors/selector.get-focus-span.ts +0 -18
  214. package/src/selectors/selector.get-focus-text-block.ts +0 -18
  215. package/src/selectors/selector.get-last-block.ts +0 -16
  216. package/src/selectors/selector.get-mark-state.test.ts +0 -325
  217. package/src/selectors/selector.get-mark-state.ts +0 -263
  218. package/src/selectors/selector.get-next-block.ts +0 -29
  219. package/src/selectors/selector.get-next-inline-object.ts +0 -53
  220. package/src/selectors/selector.get-next-inline-objects.ts +0 -50
  221. package/src/selectors/selector.get-next-span.ts +0 -56
  222. package/src/selectors/selector.get-previous-block.ts +0 -29
  223. package/src/selectors/selector.get-previous-inline-object.ts +0 -50
  224. package/src/selectors/selector.get-previous-inline-objects.ts +0 -47
  225. package/src/selectors/selector.get-previous-span.ts +0 -53
  226. package/src/selectors/selector.get-selected-blocks.ts +0 -61
  227. package/src/selectors/selector.get-selected-spans.test.ts +0 -347
  228. package/src/selectors/selector.get-selected-spans.ts +0 -155
  229. package/src/selectors/selector.get-selected-text-blocks.ts +0 -73
  230. package/src/selectors/selector.get-selected-value.test.ts +0 -834
  231. package/src/selectors/selector.get-selected-value.ts +0 -66
  232. package/src/selectors/selector.get-selection-end-block.ts +0 -33
  233. package/src/selectors/selector.get-selection-end-child.ts +0 -33
  234. package/src/selectors/selector.get-selection-end-point.ts +0 -17
  235. package/src/selectors/selector.get-selection-start-block.ts +0 -33
  236. package/src/selectors/selector.get-selection-start-child.ts +0 -33
  237. package/src/selectors/selector.get-selection-start-point.ts +0 -17
  238. package/src/selectors/selector.get-selection-text.test.ts +0 -421
  239. package/src/selectors/selector.get-selection-text.ts +0 -27
  240. package/src/selectors/selector.get-selection.ts +0 -9
  241. package/src/selectors/selector.get-text-after.ts +0 -46
  242. package/src/selectors/selector.get-text-before.ts +0 -46
  243. package/src/selectors/selector.get-value.ts +0 -11
  244. package/src/selectors/selector.is-active-annotation.test.ts +0 -320
  245. package/src/selectors/selector.is-active-annotation.ts +0 -52
  246. package/src/selectors/selector.is-active-decorator.test.ts +0 -136
  247. package/src/selectors/selector.is-active-decorator.ts +0 -24
  248. package/src/selectors/selector.is-active-list-item.ts +0 -13
  249. package/src/selectors/selector.is-active-style.ts +0 -13
  250. package/src/selectors/selector.is-at-the-end-of-block.ts +0 -30
  251. package/src/selectors/selector.is-at-the-start-of-block.ts +0 -30
  252. package/src/selectors/selector.is-overlapping-selection.test.ts +0 -304
  253. package/src/selectors/selector.is-overlapping-selection.ts +0 -181
  254. package/src/selectors/selector.is-point-after-selection.ts +0 -97
  255. package/src/selectors/selector.is-point-before-selection.ts +0 -97
  256. package/src/selectors/selector.is-selecting-entire-blocks.ts +0 -43
  257. package/src/selectors/selector.is-selection-collapsed.ts +0 -17
  258. package/src/selectors/selector.is-selection-expanded.test.ts +0 -63
  259. package/src/selectors/selector.is-selection-expanded.ts +0 -9
  260. package/src/test/_exports/index.ts +0 -1
  261. package/src/test/gherkin-parameter-types.ts +0 -112
  262. package/src/test/index.ts +0 -1
  263. package/src/test/vitest/_exports/index.ts +0 -1
  264. package/src/test/vitest/index.ts +0 -3
  265. package/src/test/vitest/step-context.ts +0 -13
  266. package/src/test/vitest/step-definitions.tsx +0 -960
  267. package/src/test/vitest/test-editor.tsx +0 -198
  268. package/src/type-utils.ts +0 -29
  269. package/src/types/block-offset.ts +0 -9
  270. package/src/types/block-with-optional-key.ts +0 -25
  271. package/src/types/editor.ts +0 -509
  272. package/src/types/options.ts +0 -13
  273. package/src/types/paths.ts +0 -35
  274. package/src/types/slate-editor.ts +0 -50
  275. package/src/types/slate.ts +0 -27
  276. package/src/utils/_exports/index.ts +0 -1
  277. package/src/utils/asserters.ts +0 -9
  278. package/src/utils/index.ts +0 -24
  279. package/src/utils/key-generator.ts +0 -33
  280. package/src/utils/parse-blocks.test.ts +0 -836
  281. package/src/utils/parse-blocks.ts +0 -504
  282. package/src/utils/util.at-the-beginning-of-block.ts +0 -32
  283. package/src/utils/util.block-offset-to-block-selection-point.ts +0 -28
  284. package/src/utils/util.block-offset-to-selection-point.ts +0 -33
  285. package/src/utils/util.block-offset.test.ts +0 -375
  286. package/src/utils/util.block-offset.ts +0 -136
  287. package/src/utils/util.block-offsets-to-selection.ts +0 -38
  288. package/src/utils/util.child-selection-point-to-block-offset.ts +0 -51
  289. package/src/utils/util.get-block-end-point.ts +0 -35
  290. package/src/utils/util.get-block-start-point.ts +0 -31
  291. package/src/utils/util.get-selection-end-point.ts +0 -20
  292. package/src/utils/util.get-selection-start-point.ts +0 -20
  293. package/src/utils/util.get-text-block-text.ts +0 -8
  294. package/src/utils/util.is-empty-text-block.ts +0 -21
  295. package/src/utils/util.is-equal-selection-points.ts +0 -13
  296. package/src/utils/util.is-equal-selections.ts +0 -20
  297. package/src/utils/util.is-keyed-segment.ts +0 -8
  298. package/src/utils/util.is-selection-collapsed.ts +0 -16
  299. package/src/utils/util.is-selection-expanded.ts +0 -13
  300. package/src/utils/util.merge-text-blocks.ts +0 -40
  301. package/src/utils/util.reverse-selection.ts +0 -26
  302. package/src/utils/util.selection-point-to-block-offset.ts +0 -30
  303. package/src/utils/util.selection-point.ts +0 -22
  304. package/src/utils/util.slice-blocks.ts +0 -221
  305. package/src/utils/util.slice-text-block.test.ts +0 -190
  306. package/src/utils/util.slice-text-block.ts +0 -89
  307. package/src/utils/util.split-text-block.ts +0 -54
@@ -1,961 +0,0 @@
1
- import type {Patch} from '@portabletext/patches'
2
- import {isSpan} from '@portabletext/schema'
3
- import type {PortableTextBlock} from '@sanity/types'
4
- import {isEqual} from 'lodash'
5
- import {deleteText, Editor, Transforms, type Descendant, type Node} from 'slate'
6
- import type {ActorRefFrom} from 'xstate'
7
- import {
8
- and,
9
- assertEvent,
10
- assign,
11
- emit,
12
- fromCallback,
13
- not,
14
- raise,
15
- setup,
16
- type AnyEventObject,
17
- type CallbackLogicFunction,
18
- } from 'xstate'
19
- import {pluginWithoutHistory} from '../history/slate-plugin.without-history'
20
- import {debugWithName} from '../internal-utils/debug'
21
- import {validateValue} from '../internal-utils/validateValue'
22
- import {toSlateBlock, VOID_CHILD_KEY} from '../internal-utils/values'
23
- import type {PickFromUnion} from '../type-utils'
24
- import type {InvalidValueResolution} from '../types/editor'
25
- import type {PortableTextSlateEditor} from '../types/slate-editor'
26
- import type {EditorSchema} from './editor-schema'
27
- import {isChangingRemotely, withRemoteChanges} from './withChanges'
28
- import {withoutPatching} from './withoutPatching'
29
-
30
- const debug = debugWithName('sync machine')
31
-
32
- type SyncValueEvent =
33
- | {
34
- type: 'patch'
35
- patch: Patch
36
- }
37
- | {
38
- type: 'invalid value'
39
- resolution: InvalidValueResolution | null
40
- value: Array<PortableTextBlock> | undefined
41
- }
42
- | {
43
- type: 'value changed'
44
- value: Array<PortableTextBlock> | undefined
45
- }
46
- | {
47
- type: 'done syncing'
48
- value: Array<PortableTextBlock> | undefined
49
- }
50
-
51
- const syncValueCallback: CallbackLogicFunction<
52
- AnyEventObject,
53
- SyncValueEvent,
54
- {
55
- context: {
56
- keyGenerator: () => string
57
- previousValue: Array<PortableTextBlock> | undefined
58
- readOnly: boolean
59
- schema: EditorSchema
60
- }
61
- slateEditor: PortableTextSlateEditor
62
- streamBlocks: boolean
63
- value: Array<PortableTextBlock> | undefined
64
- }
65
- > = ({sendBack, input}) => {
66
- updateValue({
67
- context: input.context,
68
- sendBack,
69
- slateEditor: input.slateEditor,
70
- value: input.value,
71
- streamBlocks: input.streamBlocks,
72
- })
73
- }
74
-
75
- const syncValueLogic = fromCallback(syncValueCallback)
76
-
77
- export type SyncActor = ActorRefFrom<typeof syncMachine>
78
-
79
- /**
80
- * Sync value with the editor state
81
- *
82
- * Normally nothing here should apply, and the editor and the real world are perfectly aligned.
83
- *
84
- * Inconsistencies could happen though, so we need to check the editor state when the value changes.
85
- *
86
- * For performance reasons, it makes sense to also do the content validation here, as we already
87
- * iterate over the value and can validate only the new content that is actually changed.
88
- *
89
- * @internal
90
- */
91
- export const syncMachine = setup({
92
- types: {
93
- context: {} as {
94
- initialValue: Array<PortableTextBlock> | undefined
95
- initialValueSynced: boolean
96
- isProcessingLocalChanges: boolean
97
- keyGenerator: () => string
98
- schema: EditorSchema
99
- readOnly: boolean
100
- slateEditor: PortableTextSlateEditor
101
- pendingValue: Array<PortableTextBlock> | undefined
102
- previousValue: Array<PortableTextBlock> | undefined
103
- },
104
- input: {} as {
105
- initialValue: Array<PortableTextBlock> | undefined
106
- keyGenerator: () => string
107
- schema: EditorSchema
108
- readOnly: boolean
109
- slateEditor: PortableTextSlateEditor
110
- },
111
- events: {} as
112
- | {
113
- type: 'has pending mutations'
114
- }
115
- | {
116
- type: 'mutation'
117
- }
118
- | {
119
- type: 'update value'
120
- value: Array<PortableTextBlock> | undefined
121
- }
122
- | {
123
- type: 'update readOnly'
124
- readOnly: boolean
125
- }
126
- | SyncValueEvent,
127
- emitted: {} as
128
- | PickFromUnion<
129
- SyncValueEvent,
130
- 'type',
131
- 'invalid value' | 'patch' | 'value changed'
132
- >
133
- | {type: 'done syncing value'}
134
- | {type: 'syncing value'},
135
- },
136
- actions: {
137
- 'assign initial value synced': assign({
138
- initialValueSynced: true,
139
- }),
140
- 'assign readOnly': assign({
141
- readOnly: ({event}) => {
142
- assertEvent(event, 'update readOnly')
143
- return event.readOnly
144
- },
145
- }),
146
- 'assign pending value': assign({
147
- pendingValue: ({event}) => {
148
- assertEvent(event, 'update value')
149
- return event.value
150
- },
151
- }),
152
- 'clear pending value': assign({
153
- pendingValue: undefined,
154
- }),
155
- 'assign previous value': assign({
156
- previousValue: ({event}) => {
157
- assertEvent(event, 'done syncing')
158
- return event.value
159
- },
160
- }),
161
- 'emit done syncing value': emit({
162
- type: 'done syncing value',
163
- }),
164
- 'emit syncing value': emit({
165
- type: 'syncing value',
166
- }),
167
- },
168
- guards: {
169
- 'initial value synced': ({context}) => context.initialValueSynced,
170
- 'is busy': ({context}) => {
171
- const isProcessingLocalChanges = context.isProcessingLocalChanges
172
- const isChanging = isChangingRemotely(context.slateEditor) ?? false
173
- const isBusy = isProcessingLocalChanges || isChanging
174
-
175
- debug('isBusy', {isBusy, isProcessingLocalChanges, isChanging})
176
-
177
- return isBusy
178
- },
179
- 'is empty value': ({event}) => {
180
- return event.type === 'update value' && event.value === undefined
181
- },
182
- 'is empty array': ({event}) => {
183
- return (
184
- event.type === 'update value' &&
185
- Array.isArray(event.value) &&
186
- event.value.length === 0
187
- )
188
- },
189
- 'is new value': ({context, event}) => {
190
- return (
191
- event.type === 'update value' && context.previousValue !== event.value
192
- )
193
- },
194
- 'value changed while syncing': ({context, event}) => {
195
- assertEvent(event, 'done syncing')
196
- return context.pendingValue !== event.value
197
- },
198
- 'pending value equals previous value': ({context}) => {
199
- return isEqual(context.pendingValue, context.previousValue)
200
- },
201
- },
202
- actors: {
203
- 'sync value': syncValueLogic,
204
- },
205
- }).createMachine({
206
- id: 'sync',
207
- context: ({input}) => ({
208
- initialValue: input.initialValue,
209
- initialValueSynced: false,
210
- isProcessingLocalChanges: false,
211
- keyGenerator: input.keyGenerator,
212
- schema: input.schema,
213
- readOnly: input.readOnly,
214
- slateEditor: input.slateEditor,
215
- pendingValue: undefined,
216
- previousValue: undefined,
217
- }),
218
- entry: [
219
- raise(({context}) => {
220
- return {type: 'update value', value: context.initialValue}
221
- }),
222
- ],
223
- on: {
224
- 'has pending mutations': {
225
- actions: assign({
226
- isProcessingLocalChanges: true,
227
- }),
228
- },
229
- 'mutation': {
230
- actions: assign({
231
- isProcessingLocalChanges: false,
232
- }),
233
- },
234
- 'update readOnly': {
235
- actions: ['assign readOnly'],
236
- },
237
- },
238
- initial: 'idle',
239
- states: {
240
- idle: {
241
- entry: [
242
- () => {
243
- debug('entry: syncing->idle')
244
- },
245
- ],
246
- exit: [
247
- () => {
248
- debug('exit: syncing->idle')
249
- },
250
- ],
251
- on: {
252
- 'update value': [
253
- {
254
- guard: and(['is empty value', not('initial value synced')]),
255
- actions: ['assign initial value synced', 'emit done syncing value'],
256
- },
257
- {
258
- guard: and(['is empty array', not('initial value synced')]),
259
- actions: [
260
- 'assign initial value synced',
261
- emit({type: 'value changed', value: []}),
262
- 'emit done syncing value',
263
- ],
264
- },
265
- {
266
- guard: and(['is busy', 'is new value']),
267
- target: 'busy',
268
- actions: ['assign pending value'],
269
- },
270
- {
271
- guard: 'is new value',
272
- target: 'syncing',
273
- actions: ['assign pending value'],
274
- },
275
- {
276
- guard: not('initial value synced'),
277
- actions: [
278
- () => {
279
- debug('no new value – setting initial value as synced')
280
- },
281
- 'assign initial value synced',
282
- 'emit done syncing value',
283
- ],
284
- },
285
- {
286
- actions: [
287
- () => {
288
- debug('no new value and initial value already synced')
289
- },
290
- ],
291
- },
292
- ],
293
- },
294
- },
295
- busy: {
296
- entry: [
297
- () => {
298
- debug('entry: syncing->busy')
299
- },
300
- ],
301
- exit: [
302
- () => {
303
- debug('exit: syncing->busy')
304
- },
305
- ],
306
- after: {
307
- 1000: [
308
- {
309
- guard: 'is busy',
310
- target: '.',
311
- reenter: true,
312
- actions: [
313
- () => {
314
- debug('reenter: syncing->busy')
315
- },
316
- ],
317
- },
318
- {
319
- target: 'syncing',
320
- },
321
- ],
322
- },
323
- on: {
324
- 'update value': [
325
- {
326
- guard: 'is new value',
327
- actions: ['assign pending value'],
328
- },
329
- ],
330
- },
331
- },
332
- syncing: {
333
- entry: [
334
- () => {
335
- debug('entry: syncing->syncing')
336
- },
337
- 'emit syncing value',
338
- ],
339
- exit: [
340
- () => {
341
- debug('exit: syncing->syncing')
342
- },
343
- 'emit done syncing value',
344
- ],
345
- invoke: {
346
- src: 'sync value',
347
- id: 'sync value',
348
- input: ({context}) => {
349
- return {
350
- context: {
351
- keyGenerator: context.keyGenerator,
352
- previousValue: context.previousValue,
353
- readOnly: context.readOnly,
354
- schema: context.schema,
355
- },
356
- slateEditor: context.slateEditor,
357
- streamBlocks: !context.initialValueSynced,
358
- value: context.pendingValue,
359
- }
360
- },
361
- },
362
- on: {
363
- 'update value': {
364
- guard: 'is new value',
365
- actions: ['assign pending value'],
366
- },
367
- 'patch': {
368
- actions: [emit(({event}) => event)],
369
- },
370
- 'invalid value': {
371
- actions: [emit(({event}) => event)],
372
- },
373
- 'value changed': {
374
- actions: [emit(({event}) => event)],
375
- },
376
- 'done syncing': [
377
- {
378
- guard: 'value changed while syncing',
379
- actions: ['assign previous value', 'assign initial value synced'],
380
- target: 'syncing',
381
- reenter: true,
382
- },
383
- {
384
- target: 'idle',
385
- actions: [
386
- 'clear pending value',
387
- 'assign previous value',
388
- 'assign initial value synced',
389
- ],
390
- },
391
- ],
392
- },
393
- },
394
- },
395
- })
396
-
397
- async function updateValue({
398
- context,
399
- sendBack,
400
- slateEditor,
401
- streamBlocks,
402
- value,
403
- }: {
404
- context: {
405
- keyGenerator: () => string
406
- previousValue: Array<PortableTextBlock> | undefined
407
- readOnly: boolean
408
- schema: EditorSchema
409
- }
410
- sendBack: (event: SyncValueEvent) => void
411
- slateEditor: PortableTextSlateEditor
412
- streamBlocks: boolean
413
- value: PortableTextBlock[] | undefined
414
- }) {
415
- let doneSyncing = false
416
- let isChanged = false
417
- let isValid = true
418
-
419
- const hadSelection = !!slateEditor.selection
420
-
421
- if (!value || value.length === 0) {
422
- debug('Value is empty')
423
-
424
- clearEditor({
425
- slateEditor,
426
- doneSyncing,
427
- })
428
-
429
- isChanged = true
430
- }
431
-
432
- // Remove, replace or add nodes according to what is changed.
433
- if (value && value.length > 0) {
434
- if (streamBlocks) {
435
- await new Promise<void>((resolve) => {
436
- if (doneSyncing) {
437
- resolve()
438
- return
439
- }
440
-
441
- isChanged = removeExtraBlocks({
442
- slateEditor,
443
- value,
444
- })
445
-
446
- const processBlocks = async () => {
447
- for await (const [
448
- currentBlock,
449
- currentBlockIndex,
450
- ] of getStreamedBlocks({
451
- value,
452
- })) {
453
- const {blockChanged, blockValid} = syncBlock({
454
- context,
455
- sendBack,
456
- block: currentBlock,
457
- index: currentBlockIndex,
458
- slateEditor,
459
- value,
460
- })
461
-
462
- isChanged = blockChanged || isChanged
463
- isValid = isValid && blockValid
464
-
465
- if (!isValid) {
466
- break
467
- }
468
- }
469
-
470
- resolve()
471
- }
472
-
473
- processBlocks()
474
- })
475
- } else {
476
- if (doneSyncing) {
477
- return
478
- }
479
-
480
- isChanged = removeExtraBlocks({
481
- slateEditor,
482
- value,
483
- })
484
-
485
- let index = 0
486
-
487
- for (const block of value) {
488
- const {blockChanged, blockValid} = syncBlock({
489
- context,
490
- sendBack,
491
- block,
492
- index,
493
- slateEditor,
494
- value,
495
- })
496
-
497
- isChanged = blockChanged || isChanged
498
- isValid = isValid && blockValid
499
-
500
- if (!blockValid) {
501
- break
502
- }
503
-
504
- index++
505
- }
506
- }
507
- }
508
-
509
- if (!isValid) {
510
- debug('Invalid value, returning')
511
-
512
- doneSyncing = true
513
-
514
- sendBack({type: 'done syncing', value})
515
-
516
- return
517
- }
518
-
519
- if (isChanged) {
520
- debug('Server value changed, syncing editor')
521
- try {
522
- slateEditor.onChange()
523
- } catch (err) {
524
- console.error(err)
525
-
526
- sendBack({
527
- type: 'invalid value',
528
- resolution: null,
529
- value,
530
- })
531
-
532
- doneSyncing = true
533
-
534
- sendBack({type: 'done syncing', value})
535
-
536
- return
537
- }
538
-
539
- if (hadSelection && !slateEditor.selection) {
540
- Transforms.select(slateEditor, {
541
- anchor: {path: [0, 0], offset: 0},
542
- focus: {path: [0, 0], offset: 0},
543
- })
544
-
545
- slateEditor.onChange()
546
- }
547
-
548
- sendBack({type: 'value changed', value})
549
- } else {
550
- debug('Server value and editor value is equal, no need to sync.')
551
- }
552
-
553
- doneSyncing = true
554
-
555
- sendBack({type: 'done syncing', value})
556
- }
557
-
558
- async function* getStreamedBlocks({value}: {value: Array<PortableTextBlock>}) {
559
- let index = 0
560
- for await (const block of value) {
561
- if (index % 10 === 0) {
562
- await new Promise<void>((resolve) => setTimeout(resolve, 0))
563
- }
564
- yield [block, index] as const
565
- index++
566
- }
567
- }
568
-
569
- /**
570
- * Remove all blocks and insert a placeholder block
571
- */
572
- function clearEditor({
573
- slateEditor,
574
- doneSyncing,
575
- }: {
576
- slateEditor: PortableTextSlateEditor
577
- doneSyncing: boolean
578
- }) {
579
- Editor.withoutNormalizing(slateEditor, () => {
580
- pluginWithoutHistory(slateEditor, () => {
581
- withRemoteChanges(slateEditor, () => {
582
- withoutPatching(slateEditor, () => {
583
- if (doneSyncing) {
584
- return
585
- }
586
-
587
- const childrenLength = slateEditor.children.length
588
-
589
- slateEditor.children.forEach((_, index) => {
590
- Transforms.removeNodes(slateEditor, {
591
- at: [childrenLength - 1 - index],
592
- })
593
- })
594
- })
595
- })
596
- })
597
- })
598
- }
599
-
600
- /**
601
- * Compare the length of the value and the length of the editor's children, and
602
- * remove blocks that have become superfluous
603
- */
604
- function removeExtraBlocks({
605
- slateEditor,
606
- value,
607
- }: {
608
- slateEditor: PortableTextSlateEditor
609
- value: Array<PortableTextBlock>
610
- }) {
611
- let isChanged = false
612
-
613
- Editor.withoutNormalizing(slateEditor, () => {
614
- withRemoteChanges(slateEditor, () => {
615
- withoutPatching(slateEditor, () => {
616
- const childrenLength = slateEditor.children.length
617
-
618
- if (value.length < childrenLength) {
619
- for (let i = childrenLength - 1; i > value.length - 1; i--) {
620
- Transforms.removeNodes(slateEditor, {
621
- at: [i],
622
- })
623
- }
624
-
625
- isChanged = true
626
- }
627
- })
628
- })
629
- })
630
-
631
- return isChanged
632
- }
633
-
634
- function syncBlock({
635
- context,
636
- sendBack,
637
- block,
638
- index,
639
- slateEditor,
640
- value,
641
- }: {
642
- context: {
643
- keyGenerator: () => string
644
- previousValue: Array<PortableTextBlock> | undefined
645
- readOnly: boolean
646
- schema: EditorSchema
647
- }
648
- sendBack: (event: SyncValueEvent) => void
649
- block: PortableTextBlock
650
- index: number
651
- slateEditor: PortableTextSlateEditor
652
- value: Array<PortableTextBlock>
653
- }) {
654
- const oldBlock = slateEditor.children.at(index)
655
-
656
- if (!oldBlock) {
657
- // Insert the new block
658
- const validation = validateValue(
659
- [block],
660
- context.schema,
661
- context.keyGenerator,
662
- )
663
-
664
- if (debug.enabled) {
665
- debug('Validating and inserting new block in the end of the value', block)
666
- }
667
-
668
- if (validation.valid || validation.resolution?.autoResolve) {
669
- const slateBlock = toSlateBlock(block, {
670
- schemaTypes: context.schema,
671
- })
672
-
673
- Editor.withoutNormalizing(slateEditor, () => {
674
- withRemoteChanges(slateEditor, () => {
675
- withoutPatching(slateEditor, () => {
676
- Transforms.insertNodes(slateEditor, slateBlock, {
677
- at: [index],
678
- })
679
- })
680
- })
681
- })
682
-
683
- return {
684
- blockChanged: true,
685
- blockValid: true,
686
- }
687
- }
688
-
689
- debug('Invalid', validation)
690
-
691
- sendBack({
692
- type: 'invalid value',
693
- resolution: validation.resolution,
694
- value,
695
- })
696
-
697
- return {
698
- blockChanged: false,
699
- blockValid: false,
700
- }
701
- }
702
-
703
- if (isEqual(block, oldBlock)) {
704
- // Nothing to sync, skipping the block
705
- return {
706
- blockChanged: false,
707
- blockValid: true,
708
- }
709
- }
710
-
711
- const validationValue = [value[index]]
712
- const validation = validateValue(
713
- validationValue,
714
- context.schema,
715
- context.keyGenerator,
716
- )
717
-
718
- // Resolve validations that can be resolved automatically, without involving the user (but only if the value was changed)
719
- if (
720
- !validation.valid &&
721
- validation.resolution?.autoResolve &&
722
- validation.resolution?.patches.length > 0
723
- ) {
724
- // Only apply auto resolution if the value has been populated before and is different from the last one.
725
- if (
726
- !context.readOnly &&
727
- context.previousValue &&
728
- context.previousValue !== value
729
- ) {
730
- // Give a console warning about the fact that it did an auto resolution
731
- console.warn(
732
- `${validation.resolution.action} for block with _key '${validationValue[0]._key}'. ${validation.resolution?.description}`,
733
- )
734
- validation.resolution.patches.forEach((patch) => {
735
- sendBack({type: 'patch', patch})
736
- })
737
- }
738
- }
739
-
740
- if (validation.valid || validation.resolution?.autoResolve) {
741
- if (oldBlock._key === block._key) {
742
- if (debug.enabled) {
743
- debug('Updating block', oldBlock, block)
744
- }
745
-
746
- Editor.withoutNormalizing(slateEditor, () => {
747
- withRemoteChanges(slateEditor, () => {
748
- withoutPatching(slateEditor, () => {
749
- updateBlock({
750
- context,
751
- slateEditor,
752
- oldBlock,
753
- block,
754
- index,
755
- })
756
- })
757
- })
758
- })
759
- } else {
760
- if (debug.enabled) {
761
- debug('Replacing block', oldBlock, block)
762
- }
763
-
764
- Editor.withoutNormalizing(slateEditor, () => {
765
- withRemoteChanges(slateEditor, () => {
766
- withoutPatching(slateEditor, () => {
767
- replaceBlock({
768
- context,
769
- slateEditor,
770
- block,
771
- index,
772
- })
773
- })
774
- })
775
- })
776
- }
777
-
778
- return {
779
- blockChanged: true,
780
- blockValid: true,
781
- }
782
- } else {
783
- sendBack({
784
- type: 'invalid value',
785
- resolution: validation.resolution,
786
- value,
787
- })
788
-
789
- return {
790
- blockChanged: false,
791
- blockValid: false,
792
- }
793
- }
794
- }
795
-
796
- function replaceBlock({
797
- context,
798
- slateEditor,
799
- block,
800
- index,
801
- }: {
802
- context: {
803
- keyGenerator: () => string
804
- previousValue: Array<PortableTextBlock> | undefined
805
- readOnly: boolean
806
- schema: EditorSchema
807
- }
808
- slateEditor: PortableTextSlateEditor
809
- block: PortableTextBlock
810
- index: number
811
- }) {
812
- const slateBlock = toSlateBlock(block, {
813
- schemaTypes: context.schema,
814
- })
815
-
816
- // While replacing the block and the current selection focus is on the replaced block,
817
- // temporarily deselect the editor then optimistically try to restore the selection afterwards.
818
- const currentSelection = slateEditor.selection
819
- const selectionFocusOnBlock =
820
- currentSelection && currentSelection.focus.path[0] === index
821
-
822
- if (selectionFocusOnBlock) {
823
- Transforms.deselect(slateEditor)
824
- }
825
-
826
- Transforms.removeNodes(slateEditor, {at: [index]})
827
- Transforms.insertNodes(slateEditor, slateBlock, {at: [index]})
828
-
829
- slateEditor.onChange()
830
-
831
- if (selectionFocusOnBlock) {
832
- Transforms.select(slateEditor, currentSelection)
833
- }
834
- }
835
-
836
- function updateBlock({
837
- context,
838
- slateEditor,
839
- oldBlock,
840
- block,
841
- index,
842
- }: {
843
- context: {
844
- keyGenerator: () => string
845
- previousValue: Array<PortableTextBlock> | undefined
846
- readOnly: boolean
847
- schema: EditorSchema
848
- }
849
- slateEditor: PortableTextSlateEditor
850
- oldBlock: Descendant
851
- block: PortableTextBlock
852
- index: number
853
- }) {
854
- const slateBlock = toSlateBlock(block, {
855
- schemaTypes: context.schema,
856
- })
857
-
858
- // Update the root props on the block
859
- Transforms.setNodes(slateEditor, slateBlock as Partial<Node>, {
860
- at: [index],
861
- })
862
-
863
- // Text block's need to have their children updated as well (setNode does not target a node's children)
864
- if (
865
- slateEditor.isTextBlock(slateBlock) &&
866
- slateEditor.isTextBlock(oldBlock)
867
- ) {
868
- const oldBlockChildrenLength = oldBlock.children.length
869
- if (slateBlock.children.length < oldBlockChildrenLength) {
870
- // Remove any children that have become superfluous
871
- Array.from(
872
- Array(oldBlockChildrenLength - slateBlock.children.length),
873
- ).forEach((_, index) => {
874
- const childIndex = oldBlockChildrenLength - 1 - index
875
-
876
- if (childIndex > 0) {
877
- debug('Removing child')
878
-
879
- Transforms.removeNodes(slateEditor, {
880
- at: [index, childIndex],
881
- })
882
- }
883
- })
884
- }
885
-
886
- slateBlock.children.forEach((currentBlockChild, currentBlockChildIndex) => {
887
- const oldBlockChild = oldBlock.children[currentBlockChildIndex]
888
- const isChildChanged = !isEqual(currentBlockChild, oldBlockChild)
889
- const isTextChanged = !isEqual(
890
- currentBlockChild.text,
891
- oldBlockChild?.text,
892
- )
893
- const path = [index, currentBlockChildIndex]
894
-
895
- if (isChildChanged) {
896
- // Update if this is the same child
897
- if (currentBlockChild._key === oldBlockChild?._key) {
898
- debug('Updating changed child', currentBlockChild, oldBlockChild)
899
-
900
- Transforms.setNodes(slateEditor, currentBlockChild as Partial<Node>, {
901
- at: path,
902
- })
903
-
904
- const isSpanNode =
905
- isSpan({schema: context.schema}, currentBlockChild) &&
906
- isSpan({schema: context.schema}, oldBlockChild)
907
-
908
- if (isSpanNode && isTextChanged) {
909
- if (oldBlockChild.text.length > 0) {
910
- deleteText(slateEditor, {
911
- at: {
912
- focus: {path, offset: 0},
913
- anchor: {path, offset: oldBlockChild.text.length},
914
- },
915
- })
916
- }
917
-
918
- Transforms.insertText(slateEditor, currentBlockChild.text, {
919
- at: path,
920
- })
921
-
922
- slateEditor.onChange()
923
- } else if (!isSpanNode) {
924
- // If it's a inline block, also update the void text node key
925
- debug('Updating changed inline object child', currentBlockChild)
926
-
927
- Transforms.setNodes(
928
- slateEditor,
929
- {_key: VOID_CHILD_KEY},
930
- {
931
- at: [...path, 0],
932
- voids: true,
933
- },
934
- )
935
- }
936
- } else if (oldBlockChild) {
937
- // Replace the child if _key's are different
938
- debug('Replacing child', currentBlockChild)
939
-
940
- Transforms.removeNodes(slateEditor, {
941
- at: [index, currentBlockChildIndex],
942
- })
943
- Transforms.insertNodes(slateEditor, currentBlockChild as Node, {
944
- at: [index, currentBlockChildIndex],
945
- })
946
-
947
- slateEditor.onChange()
948
- } else if (!oldBlockChild) {
949
- // Insert it if it didn't exist before
950
- debug('Inserting new child', currentBlockChild)
951
-
952
- Transforms.insertNodes(slateEditor, currentBlockChild as Node, {
953
- at: [index, currentBlockChildIndex],
954
- })
955
-
956
- slateEditor.onChange()
957
- }
958
- }
959
- })
960
- }
961
- }