@portabletext/editor 3.3.3 → 3.3.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 (309) hide show
  1. package/lib/_chunks-dts/index.d.ts +21 -1667
  2. package/lib/index.js +511 -9010
  3. package/lib/index.js.map +1 -1
  4. package/package.json +14 -15
  5. package/src/behaviors/_exports/index.ts +0 -1
  6. package/src/behaviors/behavior.abstract.annotation.ts +0 -77
  7. package/src/behaviors/behavior.abstract.decorator.ts +0 -39
  8. package/src/behaviors/behavior.abstract.delete.ts +0 -273
  9. package/src/behaviors/behavior.abstract.deserialize.ts +0 -232
  10. package/src/behaviors/behavior.abstract.insert.ts +0 -525
  11. package/src/behaviors/behavior.abstract.keyboard.ts +0 -189
  12. package/src/behaviors/behavior.abstract.list-item.ts +0 -70
  13. package/src/behaviors/behavior.abstract.move.ts +0 -79
  14. package/src/behaviors/behavior.abstract.select.ts +0 -118
  15. package/src/behaviors/behavior.abstract.serialize.ts +0 -96
  16. package/src/behaviors/behavior.abstract.split.ts +0 -170
  17. package/src/behaviors/behavior.abstract.style.ts +0 -55
  18. package/src/behaviors/behavior.abstract.ts +0 -139
  19. package/src/behaviors/behavior.config.ts +0 -7
  20. package/src/behaviors/behavior.core.annotations.ts +0 -62
  21. package/src/behaviors/behavior.core.block-element.ts +0 -116
  22. package/src/behaviors/behavior.core.block-objects.ts +0 -285
  23. package/src/behaviors/behavior.core.decorators.ts +0 -44
  24. package/src/behaviors/behavior.core.dnd.ts +0 -356
  25. package/src/behaviors/behavior.core.insert-break.ts +0 -266
  26. package/src/behaviors/behavior.core.insert.ts +0 -52
  27. package/src/behaviors/behavior.core.lists.ts +0 -691
  28. package/src/behaviors/behavior.core.ts +0 -44
  29. package/src/behaviors/behavior.perform-event.ts +0 -354
  30. package/src/behaviors/behavior.types.action.ts +0 -102
  31. package/src/behaviors/behavior.types.behavior.ts +0 -83
  32. package/src/behaviors/behavior.types.event.ts +0 -667
  33. package/src/behaviors/behavior.types.guard.ts +0 -11
  34. package/src/behaviors/index.ts +0 -17
  35. package/src/converters/converter.json.ts +0 -53
  36. package/src/converters/converter.portable-text.deserialize.test.ts +0 -680
  37. package/src/converters/converter.portable-text.ts +0 -75
  38. package/src/converters/converter.text-html.deserialize.test.ts +0 -406
  39. package/src/converters/converter.text-html.serialize.test.ts +0 -246
  40. package/src/converters/converter.text-html.ts +0 -87
  41. package/src/converters/converter.text-markdown.ts +0 -67
  42. package/src/converters/converter.text-plain.test.ts +0 -245
  43. package/src/converters/converter.text-plain.ts +0 -126
  44. package/src/converters/converter.types.ts +0 -72
  45. package/src/converters/converters.core.ts +0 -18
  46. package/src/editor/Editable.tsx +0 -1012
  47. package/src/editor/PortableTextEditor.tsx +0 -791
  48. package/src/editor/components/drop-indicator.tsx +0 -17
  49. package/src/editor/components/render-block-object.tsx +0 -121
  50. package/src/editor/components/render-default-object.tsx +0 -21
  51. package/src/editor/components/render-element.tsx +0 -88
  52. package/src/editor/components/render-inline-object.tsx +0 -129
  53. package/src/editor/components/render-leaf.tsx +0 -64
  54. package/src/editor/components/render-span.tsx +0 -303
  55. package/src/editor/components/render-text-block.tsx +0 -265
  56. package/src/editor/components/render-text.tsx +0 -18
  57. package/src/editor/components/use-core-block-element-behaviors.ts +0 -39
  58. package/src/editor/create-editor.ts +0 -323
  59. package/src/editor/create-slate-editor.tsx +0 -64
  60. package/src/editor/editor-actor-context.ts +0 -4
  61. package/src/editor/editor-context.tsx +0 -7
  62. package/src/editor/editor-dom.ts +0 -220
  63. package/src/editor/editor-machine.ts +0 -751
  64. package/src/editor/editor-provider.tsx +0 -111
  65. package/src/editor/editor-schema.ts +0 -6
  66. package/src/editor/editor-selector.ts +0 -89
  67. package/src/editor/editor-snapshot.ts +0 -68
  68. package/src/editor/event-to-change.tsx +0 -49
  69. package/src/editor/hooks/usePortableTextEditor.ts +0 -25
  70. package/src/editor/hooks/usePortableTextEditorSelection.tsx +0 -28
  71. package/src/editor/mutation-machine.ts +0 -322
  72. package/src/editor/plugins/create-with-event-listeners.ts +0 -271
  73. package/src/editor/plugins/createWithEditableAPI.ts +0 -529
  74. package/src/editor/plugins/createWithHotKeys.ts +0 -68
  75. package/src/editor/plugins/createWithObjectKeys.ts +0 -289
  76. package/src/editor/plugins/createWithPatches.ts +0 -272
  77. package/src/editor/plugins/createWithPortableTextMarkModel.ts +0 -559
  78. package/src/editor/plugins/createWithSchemaTypes.ts +0 -121
  79. package/src/editor/plugins/slate-plugin.update-selection.ts +0 -51
  80. package/src/editor/plugins/slate-plugin.update-value.ts +0 -46
  81. package/src/editor/plugins/with-plugins.ts +0 -69
  82. package/src/editor/range-decorations-machine.ts +0 -421
  83. package/src/editor/relay-actor-context.ts +0 -4
  84. package/src/editor/relay-machine.ts +0 -152
  85. package/src/editor/sync-machine.ts +0 -961
  86. package/src/editor/use-editor.ts +0 -27
  87. package/src/editor/validate-selection-machine.test.ts +0 -47
  88. package/src/editor/validate-selection-machine.ts +0 -149
  89. package/src/editor/weakMaps.ts +0 -15
  90. package/src/editor/with-normalizing-node.ts +0 -14
  91. package/src/editor/with-performing-behavior-operation.ts +0 -21
  92. package/src/editor/withChanges.ts +0 -13
  93. package/src/editor/without-normalizing-conditional.ts +0 -13
  94. package/src/editor/withoutPatching.ts +0 -14
  95. package/src/editor.ts +0 -59
  96. package/src/history/behavior.operation.history.redo.ts +0 -67
  97. package/src/history/behavior.operation.history.undo.ts +0 -71
  98. package/src/history/event.history.undo.test.tsx +0 -672
  99. package/src/history/history.preserving-keys.test.tsx +0 -112
  100. package/src/history/remote-patches.ts +0 -20
  101. package/src/history/slate-plugin.history.ts +0 -142
  102. package/src/history/slate-plugin.redoing.ts +0 -21
  103. package/src/history/slate-plugin.undoing.ts +0 -21
  104. package/src/history/slate-plugin.without-history.ts +0 -23
  105. package/src/history/transform-operation.ts +0 -245
  106. package/src/history/undo-redo-collaboration.test.tsx +0 -541
  107. package/src/history/undo-redo.feature +0 -125
  108. package/src/history/undo-redo.test.tsx +0 -195
  109. package/src/history/undo-step.ts +0 -148
  110. package/src/index.ts +0 -107
  111. package/src/internal-utils/__tests__/ranges.test.ts +0 -23
  112. package/src/internal-utils/__tests__/values.test.ts +0 -110
  113. package/src/internal-utils/apply-operation-to-portable-text.test.ts +0 -1861
  114. package/src/internal-utils/apply-operation-to-portable-text.ts +0 -615
  115. package/src/internal-utils/applyPatch.ts +0 -644
  116. package/src/internal-utils/block-keys.ts +0 -9
  117. package/src/internal-utils/build-index-maps.test.ts +0 -464
  118. package/src/internal-utils/build-index-maps.ts +0 -131
  119. package/src/internal-utils/collapse-selection.ts +0 -36
  120. package/src/internal-utils/compound-client-rect.ts +0 -28
  121. package/src/internal-utils/create-placeholder-block.ts +0 -21
  122. package/src/internal-utils/create-test-snapshot.ts +0 -28
  123. package/src/internal-utils/debug.ts +0 -12
  124. package/src/internal-utils/editor-selection.test.ts +0 -44
  125. package/src/internal-utils/editor-selection.ts +0 -56
  126. package/src/internal-utils/event-position.ts +0 -318
  127. package/src/internal-utils/global-scope.ts +0 -27
  128. package/src/internal-utils/globally-scoped-context.ts +0 -39
  129. package/src/internal-utils/is-hotkey.test.ts +0 -114
  130. package/src/internal-utils/is-hotkey.ts +0 -209
  131. package/src/internal-utils/mime-type.ts +0 -1
  132. package/src/internal-utils/move-range-by-operation.ts +0 -19
  133. package/src/internal-utils/operation-to-patches.test.ts +0 -522
  134. package/src/internal-utils/operation-to-patches.ts +0 -571
  135. package/src/internal-utils/portable-text-node.ts +0 -209
  136. package/src/internal-utils/schema.ts +0 -8
  137. package/src/internal-utils/selection-block-keys.ts +0 -20
  138. package/src/internal-utils/selection-focus-text.ts +0 -40
  139. package/src/internal-utils/selection-text.test.ts +0 -32
  140. package/src/internal-utils/selection-text.ts +0 -21
  141. package/src/internal-utils/selection.ts +0 -77
  142. package/src/internal-utils/sibling-utils.ts +0 -55
  143. package/src/internal-utils/slate-utils.test.tsx +0 -121
  144. package/src/internal-utils/slate-utils.ts +0 -417
  145. package/src/internal-utils/split-string.ts +0 -12
  146. package/src/internal-utils/stop-actor.ts +0 -43
  147. package/src/internal-utils/string-overlap.test.ts +0 -14
  148. package/src/internal-utils/string-overlap.ts +0 -28
  149. package/src/internal-utils/string-utils.ts +0 -7
  150. package/src/internal-utils/text-block-key.test.ts +0 -41
  151. package/src/internal-utils/text-block-key.ts +0 -26
  152. package/src/internal-utils/text-marks.test.ts +0 -41
  153. package/src/internal-utils/text-marks.ts +0 -22
  154. package/src/internal-utils/text-selection.test.ts +0 -211
  155. package/src/internal-utils/text-selection.ts +0 -121
  156. package/src/internal-utils/to-slate-range.test.ts +0 -278
  157. package/src/internal-utils/to-slate-range.ts +0 -171
  158. package/src/internal-utils/validateValue.ts +0 -443
  159. package/src/internal-utils/value-annotations.ts +0 -33
  160. package/src/internal-utils/values.test.ts +0 -282
  161. package/src/internal-utils/values.ts +0 -266
  162. package/src/keyboard-shortcuts/default-keyboard-shortcuts.ts +0 -146
  163. package/src/operations/behavior.operation.annotation.add.ts +0 -99
  164. package/src/operations/behavior.operation.annotation.remove.ts +0 -150
  165. package/src/operations/behavior.operation.block.set.ts +0 -104
  166. package/src/operations/behavior.operation.block.unset.ts +0 -54
  167. package/src/operations/behavior.operation.child.set.ts +0 -107
  168. package/src/operations/behavior.operation.child.unset.ts +0 -116
  169. package/src/operations/behavior.operation.decorator.add.ts +0 -131
  170. package/src/operations/behavior.operation.delete.ts +0 -294
  171. package/src/operations/behavior.operation.insert.block.ts +0 -495
  172. package/src/operations/behavior.operation.insert.child.ts +0 -129
  173. package/src/operations/behavior.operation.insert.text.ts +0 -8
  174. package/src/operations/behavior.operation.move.backward.ts +0 -12
  175. package/src/operations/behavior.operation.move.block.ts +0 -44
  176. package/src/operations/behavior.operation.move.forward.ts +0 -11
  177. package/src/operations/behavior.operation.select.ts +0 -27
  178. package/src/operations/behavior.operations.ts +0 -221
  179. package/src/plugins/_exports/index.ts +0 -1
  180. package/src/plugins/index.ts +0 -3
  181. package/src/plugins/plugin.behavior.tsx +0 -24
  182. package/src/plugins/plugin.editor-ref.tsx +0 -17
  183. package/src/plugins/plugin.event-listener.tsx +0 -68
  184. package/src/plugins/plugin.internal.auto-close-brackets.test.tsx +0 -71
  185. package/src/plugins/plugin.internal.auto-close-brackets.ts +0 -62
  186. package/src/plugins/plugin.internal.change-ref.tsx +0 -19
  187. package/src/plugins/plugin.internal.portable-text-editor-ref.tsx +0 -16
  188. package/src/plugins/plugin.internal.slate-editor-ref.tsx +0 -15
  189. package/src/priority/priority.core.ts +0 -3
  190. package/src/priority/priority.sort.test.ts +0 -319
  191. package/src/priority/priority.sort.ts +0 -123
  192. package/src/priority/priority.types.ts +0 -24
  193. package/src/selectors/_exports/index.ts +0 -1
  194. package/src/selectors/drag-selection.test.ts +0 -578
  195. package/src/selectors/drag-selection.ts +0 -118
  196. package/src/selectors/index.ts +0 -54
  197. package/src/selectors/selector.get-active-annotation-marks.ts +0 -12
  198. package/src/selectors/selector.get-active-annotations.ts +0 -36
  199. package/src/selectors/selector.get-active-decorators.ts +0 -29
  200. package/src/selectors/selector.get-active-list-item.ts +0 -38
  201. package/src/selectors/selector.get-active-style.ts +0 -38
  202. package/src/selectors/selector.get-anchor-block.ts +0 -22
  203. package/src/selectors/selector.get-anchor-child.ts +0 -36
  204. package/src/selectors/selector.get-anchor-span.ts +0 -17
  205. package/src/selectors/selector.get-anchor-text-block.ts +0 -18
  206. package/src/selectors/selector.get-block-offsets.ts +0 -34
  207. package/src/selectors/selector.get-caret-word-selection.test.ts +0 -284
  208. package/src/selectors/selector.get-caret-word-selection.ts +0 -134
  209. package/src/selectors/selector.get-first-block.ts +0 -14
  210. package/src/selectors/selector.get-focus-block-object.ts +0 -18
  211. package/src/selectors/selector.get-focus-block.ts +0 -23
  212. package/src/selectors/selector.get-focus-child.ts +0 -36
  213. package/src/selectors/selector.get-focus-inline-object.ts +0 -17
  214. package/src/selectors/selector.get-focus-list-block.ts +0 -18
  215. package/src/selectors/selector.get-focus-span.ts +0 -18
  216. package/src/selectors/selector.get-focus-text-block.ts +0 -18
  217. package/src/selectors/selector.get-last-block.ts +0 -16
  218. package/src/selectors/selector.get-mark-state.test.ts +0 -325
  219. package/src/selectors/selector.get-mark-state.ts +0 -263
  220. package/src/selectors/selector.get-next-block.ts +0 -29
  221. package/src/selectors/selector.get-next-inline-object.ts +0 -53
  222. package/src/selectors/selector.get-next-inline-objects.ts +0 -50
  223. package/src/selectors/selector.get-next-span.ts +0 -56
  224. package/src/selectors/selector.get-previous-block.ts +0 -29
  225. package/src/selectors/selector.get-previous-inline-object.ts +0 -50
  226. package/src/selectors/selector.get-previous-inline-objects.ts +0 -47
  227. package/src/selectors/selector.get-previous-span.ts +0 -53
  228. package/src/selectors/selector.get-selected-blocks.ts +0 -61
  229. package/src/selectors/selector.get-selected-spans.test.ts +0 -347
  230. package/src/selectors/selector.get-selected-spans.ts +0 -155
  231. package/src/selectors/selector.get-selected-text-blocks.ts +0 -73
  232. package/src/selectors/selector.get-selected-value.test.ts +0 -834
  233. package/src/selectors/selector.get-selected-value.ts +0 -66
  234. package/src/selectors/selector.get-selection-end-block.ts +0 -33
  235. package/src/selectors/selector.get-selection-end-child.ts +0 -33
  236. package/src/selectors/selector.get-selection-end-point.ts +0 -17
  237. package/src/selectors/selector.get-selection-start-block.ts +0 -33
  238. package/src/selectors/selector.get-selection-start-child.ts +0 -33
  239. package/src/selectors/selector.get-selection-start-point.ts +0 -17
  240. package/src/selectors/selector.get-selection-text.test.ts +0 -421
  241. package/src/selectors/selector.get-selection-text.ts +0 -27
  242. package/src/selectors/selector.get-selection.ts +0 -9
  243. package/src/selectors/selector.get-text-after.ts +0 -46
  244. package/src/selectors/selector.get-text-before.ts +0 -46
  245. package/src/selectors/selector.get-value.ts +0 -11
  246. package/src/selectors/selector.is-active-annotation.test.ts +0 -320
  247. package/src/selectors/selector.is-active-annotation.ts +0 -52
  248. package/src/selectors/selector.is-active-decorator.test.ts +0 -136
  249. package/src/selectors/selector.is-active-decorator.ts +0 -24
  250. package/src/selectors/selector.is-active-list-item.ts +0 -13
  251. package/src/selectors/selector.is-active-style.ts +0 -13
  252. package/src/selectors/selector.is-at-the-end-of-block.ts +0 -30
  253. package/src/selectors/selector.is-at-the-start-of-block.ts +0 -30
  254. package/src/selectors/selector.is-overlapping-selection.test.ts +0 -304
  255. package/src/selectors/selector.is-overlapping-selection.ts +0 -181
  256. package/src/selectors/selector.is-point-after-selection.ts +0 -97
  257. package/src/selectors/selector.is-point-before-selection.ts +0 -97
  258. package/src/selectors/selector.is-selecting-entire-blocks.ts +0 -43
  259. package/src/selectors/selector.is-selection-collapsed.ts +0 -17
  260. package/src/selectors/selector.is-selection-expanded.test.ts +0 -63
  261. package/src/selectors/selector.is-selection-expanded.ts +0 -9
  262. package/src/test/_exports/index.ts +0 -1
  263. package/src/test/gherkin-parameter-types.ts +0 -112
  264. package/src/test/index.ts +0 -1
  265. package/src/test/vitest/_exports/index.ts +0 -1
  266. package/src/test/vitest/index.ts +0 -3
  267. package/src/test/vitest/step-context.ts +0 -13
  268. package/src/test/vitest/step-definitions.tsx +0 -960
  269. package/src/test/vitest/test-editor.tsx +0 -198
  270. package/src/type-utils.ts +0 -29
  271. package/src/types/block-offset.ts +0 -9
  272. package/src/types/block-with-optional-key.ts +0 -25
  273. package/src/types/editor.ts +0 -509
  274. package/src/types/options.ts +0 -13
  275. package/src/types/paths.ts +0 -35
  276. package/src/types/slate-editor.ts +0 -50
  277. package/src/types/slate.ts +0 -27
  278. package/src/utils/_exports/index.ts +0 -1
  279. package/src/utils/asserters.ts +0 -9
  280. package/src/utils/index.ts +0 -24
  281. package/src/utils/key-generator.ts +0 -33
  282. package/src/utils/parse-blocks.test.ts +0 -836
  283. package/src/utils/parse-blocks.ts +0 -504
  284. package/src/utils/util.at-the-beginning-of-block.ts +0 -32
  285. package/src/utils/util.block-offset-to-block-selection-point.ts +0 -28
  286. package/src/utils/util.block-offset-to-selection-point.ts +0 -33
  287. package/src/utils/util.block-offset.test.ts +0 -375
  288. package/src/utils/util.block-offset.ts +0 -136
  289. package/src/utils/util.block-offsets-to-selection.ts +0 -38
  290. package/src/utils/util.child-selection-point-to-block-offset.ts +0 -51
  291. package/src/utils/util.get-block-end-point.ts +0 -35
  292. package/src/utils/util.get-block-start-point.ts +0 -31
  293. package/src/utils/util.get-selection-end-point.ts +0 -20
  294. package/src/utils/util.get-selection-start-point.ts +0 -20
  295. package/src/utils/util.get-text-block-text.ts +0 -8
  296. package/src/utils/util.is-empty-text-block.ts +0 -21
  297. package/src/utils/util.is-equal-selection-points.ts +0 -13
  298. package/src/utils/util.is-equal-selections.ts +0 -20
  299. package/src/utils/util.is-keyed-segment.ts +0 -8
  300. package/src/utils/util.is-selection-collapsed.ts +0 -16
  301. package/src/utils/util.is-selection-expanded.ts +0 -13
  302. package/src/utils/util.merge-text-blocks.ts +0 -40
  303. package/src/utils/util.reverse-selection.ts +0 -26
  304. package/src/utils/util.selection-point-to-block-offset.ts +0 -30
  305. package/src/utils/util.selection-point.ts +0 -22
  306. package/src/utils/util.slice-blocks.ts +0 -221
  307. package/src/utils/util.slice-text-block.test.ts +0 -190
  308. package/src/utils/util.slice-text-block.ts +0 -89
  309. 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
- }