@portabletext/editor 1.34.0 → 1.35.0

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 (113) hide show
  1. package/lib/_chunks-cjs/behavior.core.cjs +57 -118
  2. package/lib/_chunks-cjs/behavior.core.cjs.map +1 -1
  3. package/lib/_chunks-cjs/behavior.markdown.cjs +27 -67
  4. package/lib/_chunks-cjs/behavior.markdown.cjs.map +1 -1
  5. package/lib/_chunks-cjs/plugin.event-listener.cjs +53 -71
  6. package/lib/_chunks-cjs/plugin.event-listener.cjs.map +1 -1
  7. package/lib/_chunks-cjs/selector.get-text-before.cjs +5 -7
  8. package/lib/_chunks-cjs/selector.get-text-before.cjs.map +1 -1
  9. package/lib/_chunks-cjs/selector.is-active-style.cjs +22 -36
  10. package/lib/_chunks-cjs/selector.is-active-style.cjs.map +1 -1
  11. package/lib/_chunks-cjs/selector.is-at-the-start-of-block.cjs +68 -153
  12. package/lib/_chunks-cjs/selector.is-at-the-start-of-block.cjs.map +1 -1
  13. package/lib/_chunks-cjs/util.block-offsets-to-selection.cjs.map +1 -1
  14. package/lib/_chunks-cjs/util.slice-blocks.cjs.map +1 -1
  15. package/lib/_chunks-es/behavior.core.js +57 -118
  16. package/lib/_chunks-es/behavior.core.js.map +1 -1
  17. package/lib/_chunks-es/behavior.markdown.js +27 -67
  18. package/lib/_chunks-es/behavior.markdown.js.map +1 -1
  19. package/lib/_chunks-es/plugin.event-listener.js +53 -71
  20. package/lib/_chunks-es/plugin.event-listener.js.map +1 -1
  21. package/lib/_chunks-es/selector.get-text-before.js +5 -7
  22. package/lib/_chunks-es/selector.get-text-before.js.map +1 -1
  23. package/lib/_chunks-es/selector.is-active-style.js +22 -36
  24. package/lib/_chunks-es/selector.is-active-style.js.map +1 -1
  25. package/lib/_chunks-es/selector.is-at-the-start-of-block.js +68 -153
  26. package/lib/_chunks-es/selector.is-at-the-start-of-block.js.map +1 -1
  27. package/lib/_chunks-es/util.block-offsets-to-selection.js.map +1 -1
  28. package/lib/_chunks-es/util.slice-blocks.js.map +1 -1
  29. package/lib/behaviors/index.cjs +18 -48
  30. package/lib/behaviors/index.cjs.map +1 -1
  31. package/lib/behaviors/index.d.cts +28 -16
  32. package/lib/behaviors/index.d.ts +28 -16
  33. package/lib/behaviors/index.js +18 -48
  34. package/lib/behaviors/index.js.map +1 -1
  35. package/lib/index.d.cts +132 -71
  36. package/lib/index.d.ts +132 -71
  37. package/lib/plugins/index.cjs +182 -186
  38. package/lib/plugins/index.cjs.map +1 -1
  39. package/lib/plugins/index.d.cts +147 -82
  40. package/lib/plugins/index.d.ts +147 -82
  41. package/lib/plugins/index.js +182 -186
  42. package/lib/plugins/index.js.map +1 -1
  43. package/lib/selectors/index.cjs +22 -50
  44. package/lib/selectors/index.cjs.map +1 -1
  45. package/lib/selectors/index.d.cts +9 -200
  46. package/lib/selectors/index.d.ts +9 -200
  47. package/lib/selectors/index.js +22 -50
  48. package/lib/selectors/index.js.map +1 -1
  49. package/lib/utils/index.cjs.map +1 -1
  50. package/lib/utils/index.d.cts +15 -7
  51. package/lib/utils/index.d.ts +15 -7
  52. package/lib/utils/index.js.map +1 -1
  53. package/package.json +6 -6
  54. package/src/behaviors/behavior.code-editor.ts +6 -6
  55. package/src/behaviors/behavior.core.annotations.ts +5 -4
  56. package/src/behaviors/behavior.core.block-objects.ts +17 -17
  57. package/src/behaviors/behavior.core.decorators.ts +12 -8
  58. package/src/behaviors/behavior.core.insert-break.ts +27 -29
  59. package/src/behaviors/behavior.core.lists.ts +19 -19
  60. package/src/behaviors/behavior.decorator-pair.ts +200 -0
  61. package/src/behaviors/behavior.default.ts +35 -30
  62. package/src/behaviors/behavior.emoji-picker.ts +12 -12
  63. package/src/behaviors/behavior.links.ts +7 -7
  64. package/src/behaviors/behavior.markdown.ts +41 -42
  65. package/src/behaviors/behavior.types.ts +15 -18
  66. package/src/behaviors/index.ts +0 -1
  67. package/src/converters/converter.json.ts +6 -6
  68. package/src/converters/converter.portable-text.deserialize.test.ts +28 -26
  69. package/src/converters/converter.portable-text.ts +6 -6
  70. package/src/converters/converter.text-html.deserialize.test.ts +17 -15
  71. package/src/converters/converter.text-html.serialize.test.ts +57 -53
  72. package/src/converters/converter.text-html.ts +14 -10
  73. package/src/converters/converter.text-plain.test.ts +17 -15
  74. package/src/converters/converter.text-plain.ts +15 -11
  75. package/src/converters/converter.types.ts +8 -7
  76. package/src/editor/editor-machine.ts +6 -1
  77. package/src/editor/plugins/create-with-event-listeners.ts +0 -5
  78. package/src/index.ts +3 -3
  79. package/src/internal-utils/get-text-to-emphasize.ts +29 -7
  80. package/src/plugins/plugin.decorator-shortcut.ts +235 -0
  81. package/src/plugins/plugin.markdown.tsx +56 -8
  82. package/src/plugins/plugin.one-line.tsx +17 -17
  83. package/src/selectors/selector.get-active-list-item.ts +4 -4
  84. package/src/selectors/selector.get-active-style.ts +6 -6
  85. package/src/selectors/selector.get-anchor-block.ts +5 -5
  86. package/src/selectors/selector.get-anchor-child.ts +5 -5
  87. package/src/selectors/selector.get-anchor-span.ts +2 -2
  88. package/src/selectors/selector.get-anchor-text-block.ts +2 -2
  89. package/src/selectors/selector.get-block-offsets.ts +8 -7
  90. package/src/selectors/selector.get-caret-word-selection.ts +19 -16
  91. package/src/selectors/selector.get-next-inline-object.ts +4 -4
  92. package/src/selectors/selector.get-previous-inline-object.ts +4 -4
  93. package/src/selectors/selector.get-selected-slice.ts +7 -4
  94. package/src/selectors/selector.get-selected-spans.ts +9 -9
  95. package/src/selectors/selector.get-selection-end-point.ts +5 -5
  96. package/src/selectors/selector.get-selection-start-point.ts +5 -5
  97. package/src/selectors/selector.get-selection-text.ts +2 -2
  98. package/src/selectors/selector.get-selection.ts +2 -2
  99. package/src/selectors/selector.get-text-before.ts +8 -8
  100. package/src/selectors/selector.get-trimmed-selection.ts +15 -13
  101. package/src/selectors/selector.get-value.ts +4 -4
  102. package/src/selectors/selector.is-at-the-end-of-block.ts +6 -3
  103. package/src/selectors/selector.is-at-the-start-of-block.ts +3 -3
  104. package/src/selectors/selector.is-overlapping-selection.ts +8 -6
  105. package/src/selectors/selector.is-selection-collapsed.ts +6 -5
  106. package/src/selectors/selector.is-selection-expanded.ts +2 -2
  107. package/src/selectors/selectors.ts +59 -59
  108. package/src/types/block-offset.ts +9 -0
  109. package/src/utils/index.ts +0 -1
  110. package/src/utils/util.block-offset.ts +1 -1
  111. package/src/utils/util.block-offsets-to-selection.ts +1 -1
  112. package/src/utils/util.child-selection-point-to-block-offset.ts +1 -1
  113. package/src/behaviors/behavior.markdown-emphasis.ts +0 -437
@@ -0,0 +1,235 @@
1
+ import {useActorRef} from '@xstate/react'
2
+ import {isEqual} from 'lodash'
3
+ import {
4
+ assign,
5
+ fromCallback,
6
+ setup,
7
+ type AnyEventObject,
8
+ type CallbackLogicFunction,
9
+ } from 'xstate'
10
+ import {createDecoratorPairBehavior} from '../behaviors/behavior.decorator-pair'
11
+ import {defineBehavior} from '../behaviors/behavior.types'
12
+ import type {Editor} from '../editor/create-editor'
13
+ import {useEditor} from '../editor/editor-provider'
14
+ import type {EditorSchema} from '../selectors'
15
+ import type {BlockOffset} from '../types/block-offset'
16
+ import * as utils from '../utils'
17
+
18
+ export function DecoratorShortcutPlugin(config: {
19
+ decorator: ({schema}: {schema: EditorSchema}) => string | undefined
20
+ pair: {char: string; amount: number}
21
+ }) {
22
+ const editor = useEditor()
23
+
24
+ useActorRef(decoratorPairMachine, {
25
+ input: {
26
+ editor,
27
+ decorator: config.decorator,
28
+ pair: config.pair,
29
+ },
30
+ })
31
+
32
+ return null
33
+ }
34
+
35
+ type MarkdownEmphasisEvent =
36
+ | {
37
+ type: 'emphasis.add'
38
+ blockOffset: BlockOffset
39
+ }
40
+ | {
41
+ type: 'selection'
42
+ blockOffsets?: {
43
+ anchor: BlockOffset
44
+ focus: BlockOffset
45
+ }
46
+ }
47
+ | {
48
+ type: 'delete.backward'
49
+ }
50
+
51
+ const emphasisListener: CallbackLogicFunction<
52
+ AnyEventObject,
53
+ MarkdownEmphasisEvent,
54
+ {
55
+ decorator: ({schema}: {schema: EditorSchema}) => string | undefined
56
+ editor: Editor
57
+ pair: {char: string; amount: number}
58
+ }
59
+ > = ({sendBack, input}) => {
60
+ const unregister = input.editor.registerBehavior({
61
+ behavior: createDecoratorPairBehavior({
62
+ decorator: input.decorator,
63
+ pair: input.pair,
64
+ onDecorate: (offset) => {
65
+ sendBack({type: 'emphasis.add', blockOffset: offset})
66
+ },
67
+ }),
68
+ })
69
+
70
+ return unregister
71
+ }
72
+
73
+ const selectionListenerCallback: CallbackLogicFunction<
74
+ AnyEventObject,
75
+ MarkdownEmphasisEvent,
76
+ {editor: Editor}
77
+ > = ({sendBack, input}) => {
78
+ const unregister = input.editor.registerBehavior({
79
+ behavior: defineBehavior({
80
+ on: 'select',
81
+ guard: ({snapshot, event}) => {
82
+ if (!event.selection) {
83
+ return {blockOffsets: undefined}
84
+ }
85
+
86
+ const anchor = utils.spanSelectionPointToBlockOffset({
87
+ value: snapshot.context.value,
88
+ selectionPoint: event.selection.anchor,
89
+ })
90
+ const focus = utils.spanSelectionPointToBlockOffset({
91
+ value: snapshot.context.value,
92
+ selectionPoint: event.selection.focus,
93
+ })
94
+
95
+ if (!anchor || !focus) {
96
+ return {blockOffsets: undefined}
97
+ }
98
+
99
+ return {
100
+ blockOffsets: {
101
+ anchor,
102
+ focus,
103
+ },
104
+ }
105
+ },
106
+ actions: [
107
+ (_, {blockOffsets}) => [
108
+ {
109
+ type: 'effect',
110
+ effect: () => {
111
+ sendBack({type: 'selection', blockOffsets})
112
+ },
113
+ },
114
+ ],
115
+ ],
116
+ }),
117
+ })
118
+
119
+ return unregister
120
+ }
121
+
122
+ const deleteBackwardListenerCallback: CallbackLogicFunction<
123
+ AnyEventObject,
124
+ MarkdownEmphasisEvent,
125
+ {editor: Editor}
126
+ > = ({sendBack, input}) => {
127
+ const unregister = input.editor.registerBehavior({
128
+ behavior: defineBehavior({
129
+ on: 'delete.backward',
130
+ actions: [
131
+ () => [
132
+ {
133
+ type: 'history.undo',
134
+ },
135
+ {
136
+ type: 'effect',
137
+ effect: () => {
138
+ sendBack({type: 'delete.backward'})
139
+ },
140
+ },
141
+ ],
142
+ ],
143
+ }),
144
+ })
145
+
146
+ return unregister
147
+ }
148
+
149
+ const decoratorPairMachine = setup({
150
+ types: {
151
+ context: {} as {
152
+ decorator: ({schema}: {schema: EditorSchema}) => string | undefined
153
+ editor: Editor
154
+ offsetAfterEmphasis?: BlockOffset
155
+ pair: {char: string; amount: number}
156
+ },
157
+ input: {} as {
158
+ decorator: ({schema}: {schema: EditorSchema}) => string | undefined
159
+ editor: Editor
160
+ pair: {char: string; amount: number}
161
+ },
162
+ events: {} as MarkdownEmphasisEvent,
163
+ },
164
+ actors: {
165
+ 'emphasis listener': fromCallback(emphasisListener),
166
+ 'delete.backward listener': fromCallback(deleteBackwardListenerCallback),
167
+ 'selection listener': fromCallback(selectionListenerCallback),
168
+ },
169
+ }).createMachine({
170
+ id: 'decorator pair',
171
+ context: ({input}) => ({
172
+ decorator: input.decorator,
173
+ editor: input.editor,
174
+ pair: input.pair,
175
+ }),
176
+ initial: 'idle',
177
+ states: {
178
+ 'idle': {
179
+ invoke: [
180
+ {
181
+ src: 'emphasis listener',
182
+ input: ({context}) => ({
183
+ decorator: context.decorator,
184
+ editor: context.editor,
185
+ pair: context.pair,
186
+ }),
187
+ },
188
+ ],
189
+ on: {
190
+ 'emphasis.add': {
191
+ target: 'emphasis added',
192
+ actions: assign({
193
+ offsetAfterEmphasis: ({event}) => event.blockOffset,
194
+ }),
195
+ },
196
+ },
197
+ },
198
+ 'emphasis added': {
199
+ exit: [
200
+ assign({
201
+ offsetAfterEmphasis: undefined,
202
+ }),
203
+ ],
204
+ invoke: [
205
+ {
206
+ src: 'selection listener',
207
+ input: ({context}) => ({editor: context.editor}),
208
+ },
209
+ {
210
+ src: 'delete.backward listener',
211
+ input: ({context}) => ({editor: context.editor}),
212
+ },
213
+ ],
214
+ on: {
215
+ 'selection': {
216
+ target: 'idle',
217
+ guard: ({context, event}) => {
218
+ const selectionChanged = !isEqual(
219
+ {
220
+ anchor: context.offsetAfterEmphasis,
221
+ focus: context.offsetAfterEmphasis,
222
+ },
223
+ event.blockOffsets,
224
+ )
225
+
226
+ return selectionChanged
227
+ },
228
+ },
229
+ 'delete.backward': {
230
+ target: 'idle',
231
+ },
232
+ },
233
+ },
234
+ },
235
+ })
@@ -3,17 +3,23 @@ import {
3
3
  createMarkdownBehaviors,
4
4
  type MarkdownBehaviorsConfig,
5
5
  } from '../behaviors/behavior.markdown'
6
- import {
7
- useMarkdownEmphasisBehaviors,
8
- type MarkdownEmphasisBehaviorsConfig,
9
- } from '../behaviors/behavior.markdown-emphasis'
6
+ import type {EditorSchema} from '../editor/define-schema'
10
7
  import {useEditor} from '../editor/editor-provider'
8
+ import {DecoratorShortcutPlugin} from './plugin.decorator-shortcut'
11
9
 
12
10
  /**
13
11
  * @beta
14
12
  */
15
- export type MarkdownPluginConfig = MarkdownBehaviorsConfig &
16
- MarkdownEmphasisBehaviorsConfig
13
+ export type MarkdownPluginConfig = MarkdownBehaviorsConfig & {
14
+ boldDecorator?: ({schema}: {schema: EditorSchema}) => string | undefined
15
+ codeDecorator?: ({schema}: {schema: EditorSchema}) => string | undefined
16
+ italicDecorator?: ({schema}: {schema: EditorSchema}) => string | undefined
17
+ strikeThroughDecorator?: ({
18
+ schema,
19
+ }: {
20
+ schema: EditorSchema
21
+ }) => string | undefined
22
+ }
17
23
 
18
24
  /**
19
25
  * @beta
@@ -32,8 +38,12 @@ export type MarkdownPluginConfig = MarkdownBehaviorsConfig &
32
38
  * config={{
33
39
  * boldDecorator: ({schema}) =>
34
40
  * schema.decorators.find((decorator) => decorator.value === 'strong')?.value,
41
+ * codeDecorator: ({schema}) =>
42
+ * schema.decorators.find((decorator) => decorator.value === 'code')?.value,
35
43
  * italicDecorator: ({schema}) =>
36
44
  * schema.decorators.find((decorator) => decorator.value === 'em')?.value,
45
+ * strikeThroughDecorator: ({schema}) =>
46
+ * schema.decorators.find((decorator) => decorator.value === 'strike-through')?.value,
37
47
  * horizontalRuleObject: ({schema}) => {
38
48
  * const name = schema.blockObjects.find(
39
49
  * (object) => object.name === 'break',
@@ -60,7 +70,6 @@ export type MarkdownPluginConfig = MarkdownBehaviorsConfig &
60
70
  */
61
71
  export function MarkdownPlugin(props: {config: MarkdownPluginConfig}) {
62
72
  const editor = useEditor()
63
- useMarkdownEmphasisBehaviors({config: props.config})
64
73
 
65
74
  useEffect(() => {
66
75
  const behaviors = createMarkdownBehaviors(props.config)
@@ -76,5 +85,44 @@ export function MarkdownPlugin(props: {config: MarkdownPluginConfig}) {
76
85
  }
77
86
  }, [editor, props.config])
78
87
 
79
- return null
88
+ return (
89
+ <>
90
+ {props.config.boldDecorator ? (
91
+ <>
92
+ <DecoratorShortcutPlugin
93
+ decorator={props.config.boldDecorator}
94
+ pair={{char: '*', amount: 2}}
95
+ />
96
+ <DecoratorShortcutPlugin
97
+ decorator={props.config.boldDecorator}
98
+ pair={{char: '_', amount: 2}}
99
+ />
100
+ </>
101
+ ) : null}
102
+ {props.config.codeDecorator ? (
103
+ <DecoratorShortcutPlugin
104
+ decorator={props.config.codeDecorator}
105
+ pair={{char: '`', amount: 1}}
106
+ />
107
+ ) : null}
108
+ {props.config.italicDecorator ? (
109
+ <>
110
+ <DecoratorShortcutPlugin
111
+ decorator={props.config.italicDecorator}
112
+ pair={{char: '*', amount: 1}}
113
+ />
114
+ <DecoratorShortcutPlugin
115
+ decorator={props.config.italicDecorator}
116
+ pair={{char: '_', amount: 1}}
117
+ />
118
+ </>
119
+ ) : null}
120
+ {props.config.strikeThroughDecorator ? (
121
+ <DecoratorShortcutPlugin
122
+ decorator={props.config.strikeThroughDecorator}
123
+ pair={{char: '~', amount: 2}}
124
+ />
125
+ ) : null}
126
+ </>
127
+ )
80
128
  }
@@ -10,9 +10,9 @@ const oneLineBehaviors = [
10
10
  */
11
11
  defineBehavior({
12
12
  on: 'insert.break',
13
- guard: ({context}) =>
14
- context.selection && selectors.isSelectionExpanded({context})
15
- ? {selection: context.selection}
13
+ guard: (snapshot) =>
14
+ snapshot.context.selection && selectors.isSelectionExpanded(snapshot)
15
+ ? {selection: snapshot.context.selection}
16
16
  : false,
17
17
  actions: [(_, {selection}) => [{type: 'delete', selection}]],
18
18
  }),
@@ -40,14 +40,14 @@ const oneLineBehaviors = [
40
40
  */
41
41
  defineBehavior({
42
42
  on: 'insert.block',
43
- guard: ({context, event}) => {
44
- const focusTextBlock = selectors.getFocusTextBlock({context})
45
- const selectionStartPoint = selectors.getSelectionStartPoint({context})
46
- const selectionEndPoint = selectors.getSelectionEndPoint({context})
43
+ guard: ({snapshot, event}) => {
44
+ const focusTextBlock = selectors.getFocusTextBlock(snapshot)
45
+ const selectionStartPoint = selectors.getSelectionStartPoint(snapshot)
46
+ const selectionEndPoint = selectors.getSelectionEndPoint(snapshot)
47
47
 
48
48
  if (
49
49
  !focusTextBlock ||
50
- !utils.isTextBlock(context, event.block) ||
50
+ !utils.isTextBlock(snapshot.context, event.block) ||
51
51
  !selectionStartPoint ||
52
52
  !selectionEndPoint
53
53
  ) {
@@ -90,14 +90,14 @@ const oneLineBehaviors = [
90
90
  */
91
91
  defineBehavior({
92
92
  on: 'insert.block',
93
- guard: ({context, event}) => {
94
- const focusTextBlock = selectors.getFocusTextBlock({context})
95
- const selectionStartPoint = selectors.getSelectionStartPoint({context})
96
- const selectionEndPoint = selectors.getSelectionEndPoint({context})
93
+ guard: ({snapshot, event}) => {
94
+ const focusTextBlock = selectors.getFocusTextBlock(snapshot)
95
+ const selectionStartPoint = selectors.getSelectionStartPoint(snapshot)
96
+ const selectionEndPoint = selectors.getSelectionEndPoint(snapshot)
97
97
 
98
98
  if (
99
99
  !focusTextBlock ||
100
- !utils.isTextBlock(context, event.block) ||
100
+ !utils.isTextBlock(snapshot.context, event.block) ||
101
101
  !selectionStartPoint ||
102
102
  !selectionEndPoint
103
103
  ) {
@@ -105,12 +105,12 @@ const oneLineBehaviors = [
105
105
  }
106
106
 
107
107
  const blockBeforeStartPoint = utils.splitTextBlock({
108
- context,
108
+ context: snapshot.context,
109
109
  block: focusTextBlock.node,
110
110
  point: selectionStartPoint,
111
111
  })?.before
112
112
  const blockAfterEndPoint = utils.splitTextBlock({
113
- context,
113
+ context: snapshot.context,
114
114
  block: focusTextBlock.node,
115
115
  point: selectionEndPoint,
116
116
  })?.after
@@ -120,7 +120,7 @@ const oneLineBehaviors = [
120
120
  }
121
121
 
122
122
  const targetBlock = utils.mergeTextBlocks({
123
- context,
123
+ context: snapshot.context,
124
124
  targetBlock: blockBeforeStartPoint,
125
125
  incomingBlock: event.block,
126
126
  })
@@ -131,7 +131,7 @@ const oneLineBehaviors = [
131
131
  })
132
132
 
133
133
  const mergedBlock = utils.mergeTextBlocks({
134
- context,
134
+ context: snapshot.context,
135
135
  targetBlock,
136
136
  incomingBlock: blockAfterEndPoint,
137
137
  })
@@ -8,13 +8,13 @@ import {getSelectedBlocks} from './selectors'
8
8
  */
9
9
  export const getActiveListItem: EditorSelector<
10
10
  PortableTextListBlock['listItem'] | undefined
11
- > = ({context}) => {
12
- if (!context.selection) {
11
+ > = (snapshot) => {
12
+ if (!snapshot.context.selection) {
13
13
  return undefined
14
14
  }
15
15
 
16
- const guards = createGuards(context)
17
- const selectedBlocks = getSelectedBlocks({context}).map((block) => block.node)
16
+ const guards = createGuards(snapshot.context)
17
+ const selectedBlocks = getSelectedBlocks(snapshot).map((block) => block.node)
18
18
  const selectedTextBlocks = selectedBlocks.filter(guards.isTextBlock)
19
19
 
20
20
  const firstTextBlock = selectedTextBlocks.at(0)
@@ -6,15 +6,15 @@ import {getSelectedBlocks} from './selectors'
6
6
  /**
7
7
  * @public
8
8
  */
9
- export const getActiveStyle: EditorSelector<PortableTextTextBlock['style']> = ({
10
- context,
11
- }) => {
12
- if (!context.selection) {
9
+ export const getActiveStyle: EditorSelector<PortableTextTextBlock['style']> = (
10
+ snapshot,
11
+ ) => {
12
+ if (!snapshot.context.selection) {
13
13
  return undefined
14
14
  }
15
15
 
16
- const guards = createGuards(context)
17
- const selectedBlocks = getSelectedBlocks({context}).map((block) => block.node)
16
+ const guards = createGuards(snapshot.context)
17
+ const selectedBlocks = getSelectedBlocks(snapshot).map((block) => block.node)
18
18
  const selectedTextBlocks = selectedBlocks.filter(guards.isTextBlock)
19
19
 
20
20
  const firstTextBlock = selectedTextBlocks.at(0)
@@ -7,15 +7,15 @@ import {isKeyedSegment} from '../utils'
7
7
  */
8
8
  export const getAnchorBlock: EditorSelector<
9
9
  {node: PortableTextBlock; path: [KeyedSegment]} | undefined
10
- > = ({context}) => {
11
- const key = context.selection
12
- ? isKeyedSegment(context.selection.anchor.path[0])
13
- ? context.selection.anchor.path[0]._key
10
+ > = (snapshot) => {
11
+ const key = snapshot.context.selection
12
+ ? isKeyedSegment(snapshot.context.selection.anchor.path[0])
13
+ ? snapshot.context.selection.anchor.path[0]._key
14
14
  : undefined
15
15
  : undefined
16
16
 
17
17
  const node = key
18
- ? context.value.find((block) => block._key === key)
18
+ ? snapshot.context.value.find((block) => block._key === key)
19
19
  : undefined
20
20
 
21
21
  return node && key ? {node, path: [{_key: key}]} : undefined
@@ -13,16 +13,16 @@ export const getAnchorChild: EditorSelector<
13
13
  path: [KeyedSegment, 'children', KeyedSegment]
14
14
  }
15
15
  | undefined
16
- > = ({context}) => {
17
- const anchorBlock = getAnchorTextBlock({context})
16
+ > = (snapshot) => {
17
+ const anchorBlock = getAnchorTextBlock(snapshot)
18
18
 
19
19
  if (!anchorBlock) {
20
20
  return undefined
21
21
  }
22
22
 
23
- const key = context.selection
24
- ? isKeyedSegment(context.selection.anchor.path[2])
25
- ? context.selection.anchor.path[2]._key
23
+ const key = snapshot.context.selection
24
+ ? isKeyedSegment(snapshot.context.selection.anchor.path[2])
25
+ ? snapshot.context.selection.anchor.path[2]._key
26
26
  : undefined
27
27
  : undefined
28
28
 
@@ -9,8 +9,8 @@ import {getAnchorChild} from './selector.get-anchor-child'
9
9
  export const getAnchorSpan: EditorSelector<
10
10
  | {node: PortableTextSpan; path: [KeyedSegment, 'children', KeyedSegment]}
11
11
  | undefined
12
- > = ({context}) => {
13
- const anchorChild = getAnchorChild({context})
12
+ > = (snapshot) => {
13
+ const anchorChild = getAnchorChild(snapshot)
14
14
 
15
15
  return anchorChild && isPortableTextSpan(anchorChild.node)
16
16
  ? {node: anchorChild.node, path: anchorChild.path}
@@ -11,8 +11,8 @@ import {getAnchorBlock} from './selector.get-anchor-block'
11
11
  */
12
12
  export const getAnchorTextBlock: EditorSelector<
13
13
  {node: PortableTextTextBlock; path: [KeyedSegment]} | undefined
14
- > = ({context}) => {
15
- const anchorBlock = getAnchorBlock({context})
14
+ > = (snapshot) => {
15
+ const anchorBlock = getAnchorBlock(snapshot)
16
16
 
17
17
  return anchorBlock && isPortableTextTextBlock(anchorBlock.node)
18
18
  ? {node: anchorBlock.node, path: anchorBlock.path}
@@ -1,4 +1,5 @@
1
1
  import type {EditorSelector} from '../editor/editor-selector'
2
+ import type {BlockOffset} from '../types/block-offset'
2
3
  import * as utils from '../utils'
3
4
  import {getSelectionEndPoint} from './selector.get-selection-end-point'
4
5
  import {getSelectionStartPoint} from './selector.get-selection-start-point'
@@ -7,25 +8,25 @@ import {getSelectionStartPoint} from './selector.get-selection-start-point'
7
8
  * @public
8
9
  */
9
10
  export const getBlockOffsets: EditorSelector<
10
- {start: utils.BlockOffset; end: utils.BlockOffset} | undefined
11
- > = ({context}) => {
12
- if (!context.selection) {
11
+ {start: BlockOffset; end: BlockOffset} | undefined
12
+ > = (snapshot) => {
13
+ if (!snapshot.context.selection) {
13
14
  return undefined
14
15
  }
15
16
 
16
- const selectionStartPoint = getSelectionStartPoint({context})
17
- const selectionEndPoint = getSelectionEndPoint({context})
17
+ const selectionStartPoint = getSelectionStartPoint(snapshot)
18
+ const selectionEndPoint = getSelectionEndPoint(snapshot)
18
19
 
19
20
  if (!selectionStartPoint || !selectionEndPoint) {
20
21
  return undefined
21
22
  }
22
23
 
23
24
  const start = utils.spanSelectionPointToBlockOffset({
24
- value: context.value,
25
+ value: snapshot.context.value,
25
26
  selectionPoint: selectionStartPoint,
26
27
  })
27
28
  const end = utils.spanSelectionPointToBlockOffset({
28
- value: context.value,
29
+ value: snapshot.context.value,
29
30
  selectionPoint: selectionEndPoint,
30
31
  })
31
32
 
@@ -1,11 +1,11 @@
1
1
  import type {EditorSelector} from '../editor/editor-selector'
2
+ import type {BlockOffset} from '../types/block-offset'
2
3
  import type {EditorSelection} from '../types/editor'
3
4
  import {
4
5
  blockOffsetToSpanSelectionPoint,
5
6
  getBlockEndPoint,
6
7
  getBlockStartPoint,
7
8
  spanSelectionPointToBlockOffset,
8
- type BlockOffset,
9
9
  } from '../utils'
10
10
  import {getNextInlineObject} from './selector.get-next-inline-object'
11
11
  import {getPreviousInlineObject} from './selector.get-previous-inline-object'
@@ -20,22 +20,22 @@ import {getFocusTextBlock} from './selectors'
20
20
  * Returns the selection of the of the word the caret is placed in.
21
21
  * Note: Only returns a word selection if the current selection is collapsed
22
22
  */
23
- export const getCaretWordSelection: EditorSelector<EditorSelection> = ({
24
- context,
25
- }) => {
26
- if (!context.selection) {
23
+ export const getCaretWordSelection: EditorSelector<EditorSelection> = (
24
+ snapshot,
25
+ ) => {
26
+ if (!snapshot.context.selection) {
27
27
  return null
28
28
  }
29
29
 
30
- if (!isSelectionCollapsed({context})) {
30
+ if (!isSelectionCollapsed(snapshot)) {
31
31
  return null
32
32
  }
33
33
 
34
- const focusTextBlock = getFocusTextBlock({context})
35
- const selectionStartPoint = getSelectionStartPoint({context})
34
+ const focusTextBlock = getFocusTextBlock(snapshot)
35
+ const selectionStartPoint = getSelectionStartPoint(snapshot)
36
36
  const selectionStartOffset = selectionStartPoint
37
37
  ? spanSelectionPointToBlockOffset({
38
- value: context.value,
38
+ value: snapshot.context.value,
39
39
  selectionPoint: selectionStartPoint,
40
40
  })
41
41
  : undefined
@@ -44,11 +44,12 @@ export const getCaretWordSelection: EditorSelector<EditorSelection> = ({
44
44
  return null
45
45
  }
46
46
 
47
- const previousInlineObject = getPreviousInlineObject({context})
47
+ const previousInlineObject = getPreviousInlineObject(snapshot)
48
48
  const blockStartPoint = getBlockStartPoint(focusTextBlock)
49
49
  const textBefore = getSelectionText({
50
+ ...snapshot,
50
51
  context: {
51
- ...context,
52
+ ...snapshot.context,
52
53
  selection: {
53
54
  anchor: previousInlineObject
54
55
  ? {path: previousInlineObject.path, offset: 0}
@@ -59,11 +60,12 @@ export const getCaretWordSelection: EditorSelector<EditorSelection> = ({
59
60
  })
60
61
  const textDirectlyBefore = textBefore.split(/\s+/).at(-1)
61
62
 
62
- const nextInlineObject = getNextInlineObject({context})
63
+ const nextInlineObject = getNextInlineObject(snapshot)
63
64
  const blockEndPoint = getBlockEndPoint(focusTextBlock)
64
65
  const textAfter = getSelectionText({
66
+ ...snapshot,
65
67
  context: {
66
- ...context,
68
+ ...snapshot.context,
67
69
  selection: {
68
70
  anchor: selectionStartPoint,
69
71
  focus: nextInlineObject
@@ -95,12 +97,12 @@ export const getCaretWordSelection: EditorSelector<EditorSelection> = ({
95
97
  : selectionStartOffset
96
98
 
97
99
  const caretWordStartSelectionPoint = blockOffsetToSpanSelectionPoint({
98
- value: context.value,
100
+ value: snapshot.context.value,
99
101
  blockOffset: caretWordStartOffset,
100
102
  direction: 'backward',
101
103
  })
102
104
  const caretWordEndSelectionPoint = blockOffsetToSpanSelectionPoint({
103
- value: context.value,
105
+ value: snapshot.context.value,
104
106
  blockOffset: caretWordEndOffset,
105
107
  direction: 'forward',
106
108
  })
@@ -115,8 +117,9 @@ export const getCaretWordSelection: EditorSelector<EditorSelection> = ({
115
117
  }
116
118
 
117
119
  return isSelectionExpanded({
120
+ ...snapshot,
118
121
  context: {
119
- ...context,
122
+ ...snapshot.context,
120
123
  selection: caretWordSelection,
121
124
  },
122
125
  })
@@ -17,9 +17,9 @@ export const getNextInlineObject: EditorSelector<
17
17
  path: [KeyedSegment, 'children', KeyedSegment]
18
18
  }
19
19
  | undefined
20
- > = ({context}) => {
21
- const focusTextBlock = getFocusTextBlock({context})
22
- const selectionEndPoint = getSelectionEndPoint({context})
20
+ > = (snapshot) => {
21
+ const focusTextBlock = getFocusTextBlock(snapshot)
22
+ const selectionEndPoint = getSelectionEndPoint(snapshot)
23
23
  const selectionEndPointChildKey =
24
24
  selectionEndPoint && isKeySegment(selectionEndPoint.path[2])
25
25
  ? selectionEndPoint.path[2]._key
@@ -43,7 +43,7 @@ export const getNextInlineObject: EditorSelector<
43
43
  continue
44
44
  }
45
45
 
46
- if (!isSpan(context, child) && endPointChildFound) {
46
+ if (!isSpan(snapshot.context, child) && endPointChildFound) {
47
47
  inlineObject = {
48
48
  node: child,
49
49
  path: [...focusTextBlock.path, 'children', {_key: child._key}],