@portabletext/editor 1.33.2 → 1.33.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/lib/_chunks-cjs/behavior.core.cjs +11 -204
  2. package/lib/_chunks-cjs/behavior.core.cjs.map +1 -1
  3. package/lib/_chunks-cjs/behavior.markdown.cjs +7 -7
  4. package/lib/_chunks-cjs/behavior.markdown.cjs.map +1 -1
  5. package/lib/_chunks-cjs/plugin.event-listener.cjs +208 -33
  6. package/lib/_chunks-cjs/plugin.event-listener.cjs.map +1 -1
  7. package/lib/_chunks-cjs/selector.get-text-before.cjs +3 -3
  8. package/lib/_chunks-cjs/selector.get-text-before.cjs.map +1 -1
  9. package/lib/_chunks-cjs/selector.is-active-style.cjs +246 -0
  10. package/lib/_chunks-cjs/selector.is-active-style.cjs.map +1 -0
  11. package/lib/_chunks-cjs/selector.is-at-the-start-of-block.cjs +31 -200
  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 +7 -5
  14. package/lib/_chunks-cjs/util.block-offsets-to-selection.cjs.map +1 -1
  15. package/lib/_chunks-cjs/util.reverse-selection.cjs +0 -116
  16. package/lib/_chunks-cjs/util.reverse-selection.cjs.map +1 -1
  17. package/lib/_chunks-cjs/util.slice-blocks.cjs +138 -1
  18. package/lib/_chunks-cjs/util.slice-blocks.cjs.map +1 -1
  19. package/lib/_chunks-es/behavior.core.js +9 -202
  20. package/lib/_chunks-es/behavior.core.js.map +1 -1
  21. package/lib/_chunks-es/behavior.markdown.js +1 -1
  22. package/lib/_chunks-es/plugin.event-listener.js +205 -31
  23. package/lib/_chunks-es/plugin.event-listener.js.map +1 -1
  24. package/lib/_chunks-es/selector.get-text-before.js +2 -1
  25. package/lib/_chunks-es/selector.get-text-before.js.map +1 -1
  26. package/lib/_chunks-es/selector.is-active-style.js +249 -0
  27. package/lib/_chunks-es/selector.is-active-style.js.map +1 -0
  28. package/lib/_chunks-es/selector.is-at-the-start-of-block.js +20 -189
  29. package/lib/_chunks-es/selector.is-at-the-start-of-block.js.map +1 -1
  30. package/lib/_chunks-es/util.block-offsets-to-selection.js +5 -3
  31. package/lib/_chunks-es/util.block-offsets-to-selection.js.map +1 -1
  32. package/lib/_chunks-es/util.reverse-selection.js +1 -117
  33. package/lib/_chunks-es/util.reverse-selection.js.map +1 -1
  34. package/lib/_chunks-es/util.slice-blocks.js +140 -3
  35. package/lib/_chunks-es/util.slice-blocks.js.map +1 -1
  36. package/lib/index.d.cts +1 -115
  37. package/lib/index.d.ts +1 -115
  38. package/lib/plugins/index.cjs +19 -15
  39. package/lib/plugins/index.cjs.map +1 -1
  40. package/lib/plugins/index.d.cts +1 -115
  41. package/lib/plugins/index.d.ts +1 -115
  42. package/lib/plugins/index.js +9 -5
  43. package/lib/plugins/index.js.map +1 -1
  44. package/lib/selectors/index.cjs +16 -21
  45. package/lib/selectors/index.cjs.map +1 -1
  46. package/lib/selectors/index.d.cts +2 -0
  47. package/lib/selectors/index.d.ts +2 -0
  48. package/lib/selectors/index.js +7 -11
  49. package/lib/selectors/index.js.map +1 -1
  50. package/lib/utils/index.cjs +13 -13
  51. package/lib/utils/index.cjs.map +1 -1
  52. package/lib/utils/index.d.cts +2 -0
  53. package/lib/utils/index.d.ts +2 -0
  54. package/lib/utils/index.js +3 -3
  55. package/package.json +2 -2
  56. package/src/behaviors/behavior.core.annotations.ts +0 -24
  57. package/src/behaviors/behavior.core.decorators.ts +0 -19
  58. package/src/behaviors/behavior.core.insert-break.ts +4 -4
  59. package/src/behaviors/behavior.core.lists.ts +0 -30
  60. package/src/behaviors/behavior.core.ts +2 -17
  61. package/src/behaviors/behavior.default.ts +198 -0
  62. package/src/behaviors/behavior.foundational.ts +12 -12
  63. package/src/behaviors/behavior.markdown-emphasis.ts +4 -0
  64. package/src/converters/converter.text-html.serialize.test.ts +1 -1
  65. package/src/editor/PortableTextEditor.tsx +1 -1
  66. package/src/editor/editor-machine.ts +8 -8
  67. package/src/plugins/plugin.event-listener.tsx +1 -1
  68. package/src/selectors/selector.get-caret-word-selection.ts +9 -0
  69. package/src/selectors/selector.get-selection-text.test.ts +383 -36
  70. package/src/selectors/selector.get-selection-text.ts +13 -73
  71. package/src/utils/util.block-offset.test.ts +312 -0
  72. package/src/utils/util.block-offset.ts +39 -7
  73. package/src/utils/util.block-offsets-to-selection.ts +2 -0
  74. package/src/utils/util.slice-blocks.ts +12 -1
  75. package/lib/_chunks-cjs/selector.get-trimmed-selection.cjs +0 -97
  76. package/lib/_chunks-cjs/selector.get-trimmed-selection.cjs.map +0 -1
  77. package/lib/_chunks-es/selector.get-trimmed-selection.js +0 -100
  78. package/lib/_chunks-es/selector.get-trimmed-selection.js.map +0 -1
  79. package/src/behaviors/behavior.core.deserialize.ts +0 -60
  80. package/src/behaviors/behavior.core.serialize.ts +0 -44
  81. package/src/behaviors/behavior.core.style.ts +0 -19
@@ -0,0 +1,198 @@
1
+ import * as selectors from '../selectors'
2
+ import {defineBehavior, raise} from './behavior.types'
3
+
4
+ const toggleAnnotationOff = defineBehavior({
5
+ on: 'annotation.toggle',
6
+ guard: ({context, event}) =>
7
+ selectors.isActiveAnnotation(event.annotation.name)({context}),
8
+ actions: [
9
+ ({event}) => [
10
+ raise({type: 'annotation.remove', annotation: event.annotation}),
11
+ ],
12
+ ],
13
+ })
14
+
15
+ const toggleAnnotationOn = defineBehavior({
16
+ on: 'annotation.toggle',
17
+ guard: ({context, event}) =>
18
+ !selectors.isActiveAnnotation(event.annotation.name)({context}),
19
+ actions: [
20
+ ({event}) => [
21
+ raise({type: 'annotation.add', annotation: event.annotation}),
22
+ ],
23
+ ],
24
+ })
25
+
26
+ const toggleDecoratorOff = defineBehavior({
27
+ on: 'decorator.toggle',
28
+ guard: ({context, event}) =>
29
+ selectors.isActiveDecorator(event.decorator)({context}),
30
+ actions: [
31
+ ({event}) => [
32
+ raise({type: 'decorator.remove', decorator: event.decorator}),
33
+ ],
34
+ ],
35
+ })
36
+
37
+ const toggleDecoratorOn = defineBehavior({
38
+ on: 'decorator.toggle',
39
+ guard: ({context, event}) =>
40
+ !selectors.isActiveDecorator(event.decorator)({context}),
41
+ actions: [
42
+ ({event}) => [raise({type: 'decorator.add', decorator: event.decorator})],
43
+ ],
44
+ })
45
+
46
+ const toggleListItemOff = defineBehavior({
47
+ on: 'list item.toggle',
48
+ guard: ({context, event}) =>
49
+ selectors.isActiveListItem(event.listItem)({context}),
50
+ actions: [
51
+ ({event}) => [
52
+ raise({
53
+ type: 'list item.remove',
54
+ listItem: event.listItem,
55
+ }),
56
+ ],
57
+ ],
58
+ })
59
+
60
+ const toggleListItemOn = defineBehavior({
61
+ on: 'list item.toggle',
62
+ guard: ({context, event}) =>
63
+ !selectors.isActiveListItem(event.listItem)({context}),
64
+ actions: [
65
+ ({event}) => [
66
+ raise({
67
+ type: 'list item.add',
68
+ listItem: event.listItem,
69
+ }),
70
+ ],
71
+ ],
72
+ })
73
+
74
+ const toggleStyleOff = defineBehavior({
75
+ on: 'style.toggle',
76
+ guard: ({context, event}) => selectors.isActiveStyle(event.style)({context}),
77
+ actions: [({event}) => [raise({type: 'style.remove', style: event.style})]],
78
+ })
79
+
80
+ const toggleStyleOn = defineBehavior({
81
+ on: 'style.toggle',
82
+ guard: ({context, event}) => !selectors.isActiveStyle(event.style)({context}),
83
+ actions: [({event}) => [raise({type: 'style.add', style: event.style})]],
84
+ })
85
+
86
+ const raiseDeserializationSuccessOrFailure = defineBehavior({
87
+ on: 'deserialize',
88
+ guard: ({context, event}) => {
89
+ const deserializeEvents = context.converters.flatMap((converter) => {
90
+ const data = event.dataTransfer.getData(converter.mimeType)
91
+
92
+ if (!data) {
93
+ return []
94
+ }
95
+
96
+ return [
97
+ converter.deserialize({context, event: {type: 'deserialize', data}}),
98
+ ]
99
+ })
100
+
101
+ const firstSuccess = deserializeEvents.find(
102
+ (deserializeEvent) => deserializeEvent.type === 'deserialization.success',
103
+ )
104
+
105
+ if (!firstSuccess) {
106
+ return {
107
+ type: 'deserialization.failure',
108
+ mimeType: '*/*',
109
+ reason: deserializeEvents
110
+ .map((deserializeEvent) =>
111
+ deserializeEvent.type === 'deserialization.failure'
112
+ ? deserializeEvent.reason
113
+ : '',
114
+ )
115
+ .join(', '),
116
+ } as const
117
+ }
118
+
119
+ return firstSuccess
120
+ },
121
+ actions: [
122
+ ({event}, deserializeEvent) => [
123
+ raise({
124
+ ...deserializeEvent,
125
+ dataTransfer: event.dataTransfer,
126
+ }),
127
+ ],
128
+ ],
129
+ })
130
+
131
+ const raiseInsertBlocks = defineBehavior({
132
+ on: 'deserialization.success',
133
+ actions: [
134
+ ({event}) => [
135
+ raise({
136
+ type: 'insert.blocks',
137
+ blocks: event.data,
138
+ }),
139
+ ],
140
+ ],
141
+ })
142
+
143
+ const raiseSerializationSuccessOrFailure = defineBehavior({
144
+ on: 'serialize',
145
+ guard: ({context, event}) => {
146
+ if (context.converters.length === 0) {
147
+ return false
148
+ }
149
+
150
+ const serializeEvents = context.converters.map((converter) =>
151
+ converter.serialize({context, event}),
152
+ )
153
+
154
+ if (serializeEvents.length === 0) {
155
+ return false
156
+ }
157
+
158
+ return serializeEvents
159
+ },
160
+ actions: [
161
+ ({event}, serializeEvents) =>
162
+ serializeEvents.map((serializeEvent) =>
163
+ raise({
164
+ ...serializeEvent,
165
+ dataTransfer: event.dataTransfer,
166
+ }),
167
+ ),
168
+ ],
169
+ })
170
+
171
+ const raiseDataTransferSet = defineBehavior({
172
+ on: 'serialization.success',
173
+ actions: [
174
+ ({event}) => [
175
+ raise({
176
+ type: 'data transfer.set',
177
+ data: event.data,
178
+ dataTransfer: event.dataTransfer,
179
+ mimeType: event.mimeType,
180
+ }),
181
+ ],
182
+ ],
183
+ })
184
+
185
+ export const defaultBehaviors = [
186
+ toggleAnnotationOff,
187
+ toggleAnnotationOn,
188
+ toggleDecoratorOff,
189
+ toggleDecoratorOn,
190
+ toggleListItemOff,
191
+ toggleListItemOn,
192
+ toggleStyleOff,
193
+ toggleStyleOn,
194
+ raiseDeserializationSuccessOrFailure,
195
+ raiseInsertBlocks,
196
+ raiseSerializationSuccessOrFailure,
197
+ raiseDataTransferSet,
198
+ ]
@@ -1,15 +1,15 @@
1
1
  import {keyIs} from '../internal-utils/key-is'
2
2
  import {defineBehavior, raise} from './behavior.types'
3
3
 
4
- export const foundationalBehaviors = [
5
- /**
6
- * On WebKit, Shift+Enter results in an `insertParagraph` input event rather
7
- * than an `insertLineBreak` input event. This Behavior makes sure we catch
8
- * that `key.down` event beforehand and raise an `insert.soft break` manually.
9
- */
10
- defineBehavior({
11
- on: 'key.down',
12
- guard: ({event}) => keyIs.lineBreak(event.keyboardEvent),
13
- actions: [() => [raise({type: 'insert.soft break'})]],
14
- }),
15
- ]
4
+ /**
5
+ * On WebKit, Shift+Enter results in an `insertParagraph` input event rather
6
+ * than an `insertLineBreak` input event. This Behavior makes sure we catch
7
+ * that `key.down` event beforehand and raise an `insert.soft break` manually.
8
+ */
9
+ const raiseSoftBreak = defineBehavior({
10
+ on: 'key.down',
11
+ guard: ({event}) => keyIs.lineBreak(event.keyboardEvent),
12
+ actions: [() => [raise({type: 'insert.soft break'})]],
13
+ })
14
+
15
+ export const foundationalBehaviors = [raiseSoftBreak]
@@ -125,10 +125,12 @@ const emphasisListener: CallbackLogicFunction<
125
125
  const anchor = utils.blockOffsetToSpanSelectionPoint({
126
126
  value: context.value,
127
127
  blockOffset: prefixOffsets.focus,
128
+ direction: 'backward',
128
129
  })
129
130
  const focus = utils.blockOffsetToSpanSelectionPoint({
130
131
  value: context.value,
131
132
  blockOffset: suffixOffsets.anchor,
133
+ direction: 'forward',
132
134
  })
133
135
 
134
136
  if (!anchor || !focus) {
@@ -169,10 +171,12 @@ const emphasisListener: CallbackLogicFunction<
169
171
  const anchor = utils.blockOffsetToSpanSelectionPoint({
170
172
  value: context.value,
171
173
  blockOffset: prefixOffsets.focus,
174
+ direction: 'backward',
172
175
  })
173
176
  const focus = utils.blockOffsetToSpanSelectionPoint({
174
177
  value: context.value,
175
178
  blockOffset: suffixOffsets.anchor,
179
+ direction: 'forward',
176
180
  })
177
181
 
178
182
  if (!anchor || !focus) {
@@ -168,7 +168,7 @@ describe(converterTextHtml.serialize.name, () => {
168
168
  },
169
169
  }),
170
170
  ).toMatchObject({
171
- data: '<p>fizz buzz</p>',
171
+ data: '<p>fizz buz</p>',
172
172
  })
173
173
  })
174
174
 
@@ -812,7 +812,7 @@ export function RouteEventsToChanges(props: {
812
812
  debug('Unsubscribing to changes')
813
813
  sub.unsubscribe()
814
814
  }
815
- }, [props.editorActor, handleChange])
815
+ }, [props.editorActor])
816
816
 
817
817
  return null
818
818
  }
@@ -11,6 +11,7 @@ import {
11
11
  } from 'xstate'
12
12
  import {performAction} from '../behavior-actions/behavior.actions'
13
13
  import {coreBehaviors} from '../behaviors/behavior.core'
14
+ import {defaultBehaviors} from '../behaviors/behavior.default'
14
15
  import {foundationalBehaviors} from '../behaviors/behavior.foundational'
15
16
  import {
16
17
  isCustomBehaviorEvent,
@@ -255,7 +256,7 @@ export const editorMachine = setup({
255
256
  'assign behaviors': assign({
256
257
  behaviors: ({event}) => {
257
258
  assertEvent(event, 'update behaviors')
258
- return new Set([...foundationalBehaviors, ...event.behaviors])
259
+ return new Set([...event.behaviors])
259
260
  },
260
261
  }),
261
262
  'assign schema': assign({
@@ -310,9 +311,11 @@ export const editorMachine = setup({
310
311
  ? event.defaultActionCallback
311
312
  : undefined
312
313
 
313
- const eventBehaviors = [...context.behaviors.values()].filter(
314
- (behavior) => behavior.on === event.behaviorEvent.type,
315
- )
314
+ const eventBehaviors = [
315
+ ...foundationalBehaviors,
316
+ ...context.behaviors.values(),
317
+ ...defaultBehaviors,
318
+ ].filter((behavior) => behavior.on === event.behaviorEvent.type)
316
319
 
317
320
  if (eventBehaviors.length === 0) {
318
321
  if (defaultActionCallback) {
@@ -474,10 +477,7 @@ export const editorMachine = setup({
474
477
  }).createMachine({
475
478
  id: 'editor',
476
479
  context: ({input}) => ({
477
- behaviors: new Set([
478
- ...foundationalBehaviors,
479
- ...(input.behaviors ?? coreBehaviors),
480
- ]),
480
+ behaviors: new Set([...(input.behaviors ?? coreBehaviors)]),
481
481
  converters: new Set(input.converters ?? []),
482
482
  keyGenerator: input.keyGenerator,
483
483
  pendingEvents: [],
@@ -64,7 +64,7 @@ export function EventListenerPlugin(props: {
64
64
  return () => {
65
65
  subscription.unsubscribe()
66
66
  }
67
- }, [editor, on])
67
+ }, [editor])
68
68
 
69
69
  return null
70
70
  }
@@ -74,6 +74,13 @@ export const getCaretWordSelection: EditorSelector<EditorSelection> = ({
74
74
  })
75
75
  const textDirectlyAfter = textAfter.split(/\s+/).at(0)
76
76
 
77
+ if (
78
+ (textDirectlyBefore === undefined || textDirectlyBefore === '') &&
79
+ (textDirectlyAfter === undefined || textDirectlyAfter === '')
80
+ ) {
81
+ return null
82
+ }
83
+
77
84
  const caretWordStartOffset: BlockOffset = textDirectlyBefore
78
85
  ? {
79
86
  ...selectionStartOffset,
@@ -90,10 +97,12 @@ export const getCaretWordSelection: EditorSelector<EditorSelection> = ({
90
97
  const caretWordStartSelectionPoint = blockOffsetToSpanSelectionPoint({
91
98
  value: context.value,
92
99
  blockOffset: caretWordStartOffset,
100
+ direction: 'backward',
93
101
  })
94
102
  const caretWordEndSelectionPoint = blockOffsetToSpanSelectionPoint({
95
103
  value: context.value,
96
104
  blockOffset: caretWordEndOffset,
105
+ direction: 'forward',
97
106
  })
98
107
 
99
108
  if (!caretWordStartSelectionPoint || !caretWordEndSelectionPoint) {