@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
@@ -1,6 +1,6 @@
1
1
  import type {PortableTextBlock} from '@sanity/types'
2
- import type {BlockOffset} from '../behaviors'
3
2
  import type {EditorSelection} from '../selectors'
3
+ import type {BlockOffset} from '../types/block-offset'
4
4
  import {blockOffsetToSpanSelectionPoint} from './util.block-offset'
5
5
 
6
6
  /**
@@ -3,7 +3,7 @@ import {
3
3
  isPortableTextTextBlock,
4
4
  type PortableTextBlock,
5
5
  } from '@sanity/types'
6
- import type {BlockOffset} from '../behaviors/behavior.types'
6
+ import type {BlockOffset} from '../types/block-offset'
7
7
  import type {EditorSelectionPoint} from '../types/editor'
8
8
  import {isKeyedSegment} from './util.is-keyed-segment'
9
9
 
@@ -1,437 +0,0 @@
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 type {Editor} from '../editor/create-editor'
11
- import {useEditor} from '../editor/editor-provider'
12
- import {
13
- getTextToBold,
14
- getTextToItalic,
15
- } from '../internal-utils/get-text-to-emphasize'
16
- import type {EditorSchema} from '../selectors'
17
- import * as selectors from '../selectors'
18
- import * as utils from '../utils'
19
- import {defineBehavior} from './behavior.types'
20
-
21
- /**
22
- * @beta
23
- */
24
- export type MarkdownEmphasisBehaviorsConfig = {
25
- boldDecorator?: ({schema}: {schema: EditorSchema}) => string | undefined
26
- italicDecorator?: ({schema}: {schema: EditorSchema}) => string | undefined
27
- }
28
-
29
- /**
30
- * @beta
31
- */
32
- export function useMarkdownEmphasisBehaviors(props: {
33
- config: MarkdownEmphasisBehaviorsConfig
34
- }) {
35
- const editor = useEditor()
36
-
37
- useActorRef(emphasisMachine, {
38
- input: {
39
- editor,
40
- boldDecorator: props.config.boldDecorator?.({
41
- schema: editor.getSnapshot().context.schema,
42
- }),
43
- italicDecorator: props.config.italicDecorator?.({
44
- schema: editor.getSnapshot().context.schema,
45
- }),
46
- },
47
- })
48
- }
49
-
50
- type MarkdownEmphasisEvent =
51
- | {
52
- type: 'emphasis.add'
53
- blockOffset: utils.BlockOffset
54
- }
55
- | {
56
- type: 'selection'
57
- blockOffsets?: {
58
- anchor: utils.BlockOffset
59
- focus: utils.BlockOffset
60
- }
61
- }
62
- | {
63
- type: 'delete.backward'
64
- }
65
-
66
- const emphasisListener: CallbackLogicFunction<
67
- AnyEventObject,
68
- MarkdownEmphasisEvent,
69
- {editor: Editor; boldDecorator?: string; italicDecorator?: string}
70
- > = ({sendBack, input}) => {
71
- const unregister = input.editor.registerBehavior({
72
- behavior: defineBehavior({
73
- on: 'insert.text',
74
- guard: ({context, event}) => {
75
- const boldDecorator = input.boldDecorator
76
- const italicDecorator = input.italicDecorator
77
-
78
- if (boldDecorator === undefined && italicDecorator === undefined) {
79
- return false
80
- }
81
-
82
- const focusTextBlock = selectors.getFocusTextBlock({context})
83
- const selectionStartPoint = selectors.getSelectionStartPoint({context})
84
- const selectionStartOffset = selectionStartPoint
85
- ? utils.spanSelectionPointToBlockOffset({
86
- value: context.value,
87
- selectionPoint: selectionStartPoint,
88
- })
89
- : undefined
90
-
91
- if (!focusTextBlock || !selectionStartOffset) {
92
- return false
93
- }
94
-
95
- const textBefore = selectors.getBlockTextBefore({context})
96
-
97
- const textToItalic = getTextToItalic(`${textBefore}${event.text}`)
98
-
99
- if (textToItalic !== undefined && italicDecorator !== undefined) {
100
- const prefixOffsets = {
101
- anchor: {
102
- path: focusTextBlock.path,
103
- // Example: "foo *bar*".length - "*bar*".length = 4
104
- offset: `${textBefore}${event.text}`.length - textToItalic.length,
105
- },
106
- focus: {
107
- path: focusTextBlock.path,
108
- // Example: "foo *bar*".length - "*bar*".length + 1 = 5
109
- offset:
110
- `${textBefore}${event.text}`.length - textToItalic.length + 1,
111
- },
112
- }
113
- const suffixOffsets = {
114
- anchor: {
115
- path: focusTextBlock.path,
116
- // Example: "foo *bar|" (8) + "*".length - 1 = 8
117
- offset: selectionStartOffset.offset + event.text.length - 1,
118
- },
119
- focus: {
120
- path: focusTextBlock.path,
121
- // Example: "foo *bar|" (8) + "*".length = 9
122
- offset: selectionStartOffset.offset + event.text.length,
123
- },
124
- }
125
-
126
- return {
127
- prefixOffsets,
128
- suffixOffsets,
129
- decorator: italicDecorator,
130
- }
131
- }
132
-
133
- const textToBold = getTextToBold(`${textBefore}${event.text}`)
134
-
135
- if (textToBold !== undefined && boldDecorator !== undefined) {
136
- const prefixOffsets = {
137
- anchor: {
138
- path: focusTextBlock.path,
139
- // Example: "foo **bar**".length - "**bar**".length = 4
140
- offset: `${textBefore}${event.text}`.length - textToBold.length,
141
- },
142
- focus: {
143
- path: focusTextBlock.path,
144
- // Example: "foo **bar**".length - "**bar**".length + 2 = 6
145
- offset:
146
- `${textBefore}${event.text}`.length - textToBold.length + 2,
147
- },
148
- }
149
-
150
- const prefixSelection = utils.blockOffsetsToSelection({
151
- value: context.value,
152
- offsets: prefixOffsets,
153
- })
154
- const inlineObjectBeforePrefixFocus =
155
- selectors.getPreviousInlineObject({
156
- context: {
157
- ...context,
158
- selection: prefixSelection
159
- ? {
160
- anchor: prefixSelection.focus,
161
- focus: prefixSelection.focus,
162
- }
163
- : null,
164
- },
165
- })
166
- const inlineObjectBeforePrefixFocusOffset =
167
- inlineObjectBeforePrefixFocus
168
- ? utils.childSelectionPointToBlockOffset({
169
- value: context.value,
170
- selectionPoint: {
171
- path: inlineObjectBeforePrefixFocus.path,
172
- offset: 0,
173
- },
174
- })
175
- : undefined
176
-
177
- if (
178
- inlineObjectBeforePrefixFocusOffset &&
179
- inlineObjectBeforePrefixFocusOffset.offset >
180
- prefixOffsets.anchor.offset &&
181
- inlineObjectBeforePrefixFocusOffset.offset <
182
- prefixOffsets.focus.offset
183
- ) {
184
- return false
185
- }
186
-
187
- const suffixOffsets = {
188
- anchor: {
189
- path: focusTextBlock.path,
190
- // Example: "foo **bar*|" (10) + "*".length - 2 = 9
191
- offset: selectionStartOffset.offset + event.text.length - 2,
192
- },
193
- focus: {
194
- path: focusTextBlock.path,
195
- // Example: "foo **bar*|" (10) + "*".length = 11
196
- offset: selectionStartOffset.offset + event.text.length,
197
- },
198
- }
199
-
200
- const previousInlineObject = selectors.getPreviousInlineObject({
201
- context,
202
- })
203
- const previousInlineObjectOffset = previousInlineObject
204
- ? utils.childSelectionPointToBlockOffset({
205
- value: context.value,
206
- selectionPoint: {
207
- path: previousInlineObject.path,
208
- offset: 0,
209
- },
210
- })
211
- : undefined
212
-
213
- if (
214
- previousInlineObjectOffset &&
215
- previousInlineObjectOffset.offset > suffixOffsets.anchor.offset &&
216
- previousInlineObjectOffset.offset < suffixOffsets.focus.offset
217
- ) {
218
- return false
219
- }
220
-
221
- return {
222
- prefixOffsets,
223
- suffixOffsets,
224
- decorator: boldDecorator,
225
- }
226
- }
227
-
228
- return false
229
- },
230
- actions: [
231
- ({event}) => [event],
232
- (_, {prefixOffsets, suffixOffsets, decorator}) => [
233
- {
234
- type: 'decorator.add',
235
- decorator,
236
- offsets: {
237
- anchor: prefixOffsets.focus,
238
- focus: suffixOffsets.anchor,
239
- },
240
- },
241
- {
242
- type: 'delete.text',
243
- ...suffixOffsets,
244
- },
245
- {
246
- type: 'delete.text',
247
- ...prefixOffsets,
248
- },
249
- {
250
- type: 'decorator.remove',
251
- decorator,
252
- },
253
- {
254
- type: 'effect',
255
- effect: () => {
256
- sendBack({
257
- type: 'emphasis.add',
258
- blockOffset: {
259
- ...suffixOffsets.anchor,
260
- offset:
261
- suffixOffsets.anchor.offset -
262
- (prefixOffsets.focus.offset - prefixOffsets.anchor.offset),
263
- },
264
- })
265
- },
266
- },
267
- ],
268
- ],
269
- }),
270
- })
271
-
272
- return unregister
273
- }
274
-
275
- const selectionListenerCallback: CallbackLogicFunction<
276
- AnyEventObject,
277
- MarkdownEmphasisEvent,
278
- {editor: Editor}
279
- > = ({sendBack, input}) => {
280
- const unregister = input.editor.registerBehavior({
281
- behavior: defineBehavior({
282
- on: 'select',
283
- guard: ({context, event}) => {
284
- if (!event.selection) {
285
- return {blockOffsets: undefined}
286
- }
287
-
288
- const anchor = utils.spanSelectionPointToBlockOffset({
289
- value: context.value,
290
- selectionPoint: event.selection.anchor,
291
- })
292
- const focus = utils.spanSelectionPointToBlockOffset({
293
- value: context.value,
294
- selectionPoint: event.selection.focus,
295
- })
296
-
297
- if (!anchor || !focus) {
298
- return {blockOffsets: undefined}
299
- }
300
-
301
- return {
302
- blockOffsets: {
303
- anchor,
304
- focus,
305
- },
306
- }
307
- },
308
- actions: [
309
- (_, {blockOffsets}) => [
310
- {
311
- type: 'effect',
312
- effect: () => {
313
- sendBack({type: 'selection', blockOffsets})
314
- },
315
- },
316
- ],
317
- ],
318
- }),
319
- })
320
-
321
- return unregister
322
- }
323
-
324
- const deleteBackwardListenerCallback: CallbackLogicFunction<
325
- AnyEventObject,
326
- MarkdownEmphasisEvent,
327
- {editor: Editor}
328
- > = ({sendBack, input}) => {
329
- const unregister = input.editor.registerBehavior({
330
- behavior: defineBehavior({
331
- on: 'delete.backward',
332
- actions: [
333
- () => [
334
- {
335
- type: 'history.undo',
336
- },
337
- {
338
- type: 'effect',
339
- effect: () => {
340
- sendBack({type: 'delete.backward'})
341
- },
342
- },
343
- ],
344
- ],
345
- }),
346
- })
347
-
348
- return unregister
349
- }
350
-
351
- const emphasisMachine = setup({
352
- types: {
353
- context: {} as {
354
- boldDecorator?: string
355
- italicDecorator?: string
356
- offsetAfterEmphasis?: utils.BlockOffset
357
- editor: Editor
358
- },
359
- input: {} as {
360
- boldDecorator?: string
361
- italicDecorator?: string
362
- editor: Editor
363
- },
364
- events: {} as MarkdownEmphasisEvent,
365
- },
366
- actors: {
367
- 'emphasis listener': fromCallback(emphasisListener),
368
- 'delete.backward listener': fromCallback(deleteBackwardListenerCallback),
369
- 'selection listener': fromCallback(selectionListenerCallback),
370
- },
371
- }).createMachine({
372
- id: 'emphasis',
373
- context: ({input}) => ({
374
- boldDecorator: input.boldDecorator,
375
- italicDecorator: input.italicDecorator,
376
- editor: input.editor,
377
- }),
378
- initial: 'idle',
379
- states: {
380
- 'idle': {
381
- invoke: [
382
- {
383
- src: 'emphasis listener',
384
- input: ({context}) => ({
385
- editor: context.editor,
386
- boldDecorator: context.boldDecorator,
387
- italicDecorator: context.italicDecorator,
388
- }),
389
- },
390
- ],
391
- on: {
392
- 'emphasis.add': {
393
- target: 'emphasis added',
394
- actions: assign({
395
- offsetAfterEmphasis: ({event}) => event.blockOffset,
396
- }),
397
- },
398
- },
399
- },
400
- 'emphasis added': {
401
- exit: [
402
- assign({
403
- offsetAfterEmphasis: undefined,
404
- }),
405
- ],
406
- invoke: [
407
- {
408
- src: 'selection listener',
409
- input: ({context}) => ({editor: context.editor}),
410
- },
411
- {
412
- src: 'delete.backward listener',
413
- input: ({context}) => ({editor: context.editor}),
414
- },
415
- ],
416
- on: {
417
- 'selection': {
418
- target: 'idle',
419
- guard: ({context, event}) => {
420
- const selectionChanged = !isEqual(
421
- {
422
- anchor: context.offsetAfterEmphasis,
423
- focus: context.offsetAfterEmphasis,
424
- },
425
- event.blockOffsets,
426
- )
427
-
428
- return selectionChanged
429
- },
430
- },
431
- 'delete.backward': {
432
- target: 'idle',
433
- },
434
- },
435
- },
436
- },
437
- })