@portabletext/editor 1.23.0 → 1.25.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 (71) hide show
  1. package/lib/_chunks-cjs/behavior.core.cjs +249 -62
  2. package/lib/_chunks-cjs/behavior.core.cjs.map +1 -1
  3. package/lib/_chunks-cjs/{selector.is-selection-collapsed.cjs → selector.is-active-style.cjs} +158 -3
  4. package/lib/_chunks-cjs/selector.is-active-style.cjs.map +1 -0
  5. package/lib/_chunks-cjs/util.slice-blocks.cjs +23 -9
  6. package/lib/_chunks-cjs/util.slice-blocks.cjs.map +1 -1
  7. package/lib/_chunks-es/behavior.core.js +225 -38
  8. package/lib/_chunks-es/behavior.core.js.map +1 -1
  9. package/lib/_chunks-es/{selector.is-selection-collapsed.js → selector.is-active-style.js} +159 -4
  10. package/lib/_chunks-es/selector.is-active-style.js.map +1 -0
  11. package/lib/_chunks-es/util.slice-blocks.js +23 -9
  12. package/lib/_chunks-es/util.slice-blocks.js.map +1 -1
  13. package/lib/behaviors/index.cjs +27 -27
  14. package/lib/behaviors/index.cjs.map +1 -1
  15. package/lib/behaviors/index.d.cts +2830 -139
  16. package/lib/behaviors/index.d.ts +2830 -139
  17. package/lib/behaviors/index.js +1 -1
  18. package/lib/index.cjs +695 -526
  19. package/lib/index.cjs.map +1 -1
  20. package/lib/index.d.cts +8950 -246
  21. package/lib/index.d.ts +8950 -246
  22. package/lib/index.js +696 -525
  23. package/lib/index.js.map +1 -1
  24. package/lib/selectors/index.cjs +24 -171
  25. package/lib/selectors/index.cjs.map +1 -1
  26. package/lib/selectors/index.d.cts +73 -0
  27. package/lib/selectors/index.d.ts +73 -0
  28. package/lib/selectors/index.js +3 -151
  29. package/lib/selectors/index.js.map +1 -1
  30. package/package.json +11 -10
  31. package/src/behavior-actions/behavior.action.data-transfer-set.ts +7 -0
  32. package/src/behavior-actions/behavior.action.insert-blocks.ts +61 -0
  33. package/src/behavior-actions/behavior.actions.ts +159 -83
  34. package/src/behaviors/behavior.core.annotations.ts +29 -0
  35. package/src/behaviors/behavior.core.block-objects.ts +13 -13
  36. package/src/behaviors/behavior.core.decorators.ts +19 -0
  37. package/src/behaviors/behavior.core.deserialize.ts +46 -0
  38. package/src/behaviors/behavior.core.lists.ts +57 -23
  39. package/src/behaviors/behavior.core.serialize.ts +44 -0
  40. package/src/behaviors/behavior.core.style.ts +19 -0
  41. package/src/behaviors/behavior.core.ts +19 -0
  42. package/src/behaviors/behavior.types.ts +126 -89
  43. package/src/converters/converter.json.ts +53 -0
  44. package/src/converters/converter.portable-text.deserialize.test.ts +686 -0
  45. package/src/converters/converter.portable-text.ts +59 -0
  46. package/src/converters/converter.text-html.deserialize.test.ts +349 -0
  47. package/src/converters/converter.text-html.serialize.test.ts +233 -0
  48. package/src/converters/converter.text-html.ts +61 -0
  49. package/src/converters/converter.text-plain.test.ts +241 -0
  50. package/src/converters/converter.text-plain.ts +91 -0
  51. package/src/converters/converter.ts +65 -0
  52. package/src/converters/converters.ts +11 -0
  53. package/src/editor/Editable.tsx +3 -13
  54. package/src/editor/create-editor.ts +48 -6
  55. package/src/editor/editor-machine.ts +56 -2
  56. package/src/editor/editor-selector.ts +1 -0
  57. package/src/editor/editor-snapshot.ts +5 -0
  58. package/src/editor/plugins/create-with-event-listeners.ts +82 -106
  59. package/src/internal-utils/asserters.ts +9 -0
  60. package/src/internal-utils/mime-type.ts +1 -0
  61. package/src/internal-utils/parse-blocks.ts +136 -0
  62. package/src/internal-utils/test-key-generator.ts +9 -0
  63. package/src/selectors/selector.get-selected-spans.test.ts +1 -0
  64. package/src/selectors/selector.get-selection-text.test.ts +1 -0
  65. package/src/selectors/selector.is-active-decorator.test.ts +1 -0
  66. package/src/utils/util.slice-blocks.test.ts +87 -0
  67. package/src/utils/util.slice-blocks.ts +27 -10
  68. package/lib/_chunks-cjs/selector.is-selection-collapsed.cjs.map +0 -1
  69. package/lib/_chunks-es/selector.is-selection-collapsed.js.map +0 -1
  70. package/src/editor/plugins/__tests__/createWithInsertData.test.tsx +0 -181
  71. package/src/editor/plugins/createWithInsertData.ts +0 -425
@@ -0,0 +1,29 @@
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
+ export const coreAnnotationBehaviors = {
27
+ toggleAnnotationOff,
28
+ toggleAnnotationOn,
29
+ }
@@ -2,7 +2,7 @@ import {isPortableTextTextBlock} from '@sanity/types'
2
2
  import {isHotkey} from '../internal-utils/is-hotkey'
3
3
  import * as selectors from '../selectors'
4
4
  import {isEmptyTextBlock} from '../utils/util.is-empty-text-block'
5
- import {defineBehavior} from './behavior.types'
5
+ import {defineBehavior, raise} from './behavior.types'
6
6
 
7
7
  const arrowDownOnLonelyBlockObject = defineBehavior({
8
8
  on: 'key.down',
@@ -13,7 +13,7 @@ const arrowDownOnLonelyBlockObject = defineBehavior({
13
13
 
14
14
  return isArrowDown && focusBlockObject && !nextBlock
15
15
  },
16
- actions: [() => [{type: 'insert.text block', placement: 'after'}]],
16
+ actions: [() => [raise({type: 'insert.text block', placement: 'after'})]],
17
17
  })
18
18
 
19
19
  const arrowUpOnLonelyBlockObject = defineBehavior({
@@ -27,8 +27,8 @@ const arrowUpOnLonelyBlockObject = defineBehavior({
27
27
  },
28
28
  actions: [
29
29
  () => [
30
- {type: 'insert.text block', placement: 'before'},
31
- {type: 'select.previous block'},
30
+ raise({type: 'insert.text block', placement: 'before'}),
31
+ raise({type: 'select.previous block'}),
32
32
  ],
33
33
  ],
34
34
  })
@@ -41,7 +41,7 @@ const breakingBlockObject = defineBehavior({
41
41
 
42
42
  return collapsedSelection && focusBlockObject !== undefined
43
43
  },
44
- actions: [() => [{type: 'insert.text block', placement: 'after'}]],
44
+ actions: [() => [raise({type: 'insert.text block', placement: 'after'})]],
45
45
  })
46
46
 
47
47
  const deletingEmptyTextBlockAfterBlockObject = defineBehavior({
@@ -66,17 +66,17 @@ const deletingEmptyTextBlockAfterBlockObject = defineBehavior({
66
66
  },
67
67
  actions: [
68
68
  (_, {focusTextBlock, previousBlock}) => [
69
- {
69
+ raise({
70
70
  type: 'delete.block',
71
71
  blockPath: focusTextBlock.path,
72
- },
73
- {
72
+ }),
73
+ raise({
74
74
  type: 'select',
75
75
  selection: {
76
76
  anchor: {path: previousBlock.path, offset: 0},
77
77
  focus: {path: previousBlock.path, offset: 0},
78
78
  },
79
- },
79
+ }),
80
80
  ],
81
81
  ],
82
82
  })
@@ -103,17 +103,17 @@ const deletingEmptyTextBlockBeforeBlockObject = defineBehavior({
103
103
  },
104
104
  actions: [
105
105
  (_, {focusTextBlock, nextBlock}) => [
106
- {
106
+ raise({
107
107
  type: 'delete.block',
108
108
  blockPath: focusTextBlock.path,
109
- },
110
- {
109
+ }),
110
+ raise({
111
111
  type: 'select',
112
112
  selection: {
113
113
  anchor: {path: nextBlock.path, offset: 0},
114
114
  focus: {path: nextBlock.path, offset: 0},
115
115
  },
116
- },
116
+ }),
117
117
  ],
118
118
  ],
119
119
  })
@@ -1,7 +1,26 @@
1
1
  import {isHotkey} from '../internal-utils/is-hotkey'
2
+ import * as selectors from '../selectors'
2
3
  import {defineBehavior, raise} from './behavior.types'
3
4
 
4
5
  export const coreDecoratorBehaviors = {
6
+ toggleDecoratorOff: defineBehavior({
7
+ on: 'decorator.toggle',
8
+ guard: ({context, event}) =>
9
+ selectors.isActiveDecorator(event.decorator)({context}),
10
+ actions: [
11
+ ({event}) => [
12
+ raise({type: 'decorator.remove', decorator: event.decorator}),
13
+ ],
14
+ ],
15
+ }),
16
+ toggleDecoratorOn: defineBehavior({
17
+ on: 'decorator.toggle',
18
+ guard: ({context, event}) =>
19
+ !selectors.isActiveDecorator(event.decorator)({context}),
20
+ actions: [
21
+ ({event}) => [raise({type: 'decorator.add', decorator: event.decorator})],
22
+ ],
23
+ }),
5
24
  strongShortcut: defineBehavior({
6
25
  on: 'key.down',
7
26
  guard: ({context, event}) =>
@@ -0,0 +1,46 @@
1
+ import {defineBehavior, raise} from './behavior.types'
2
+
3
+ export const coreDeserializeBehavior = defineBehavior({
4
+ on: 'deserialize',
5
+ guard: ({context, event}) => {
6
+ const deserializeEvents = context.converters.flatMap((converter) => {
7
+ const data = event.dataTransfer.getData(converter.mimeType)
8
+
9
+ if (!data) {
10
+ return []
11
+ }
12
+
13
+ return [
14
+ converter.deserialize({context, event: {type: 'deserialize', data}}),
15
+ ]
16
+ })
17
+
18
+ const firstSuccess = deserializeEvents.find(
19
+ (deserializeEvent) => deserializeEvent.type === 'deserialization.success',
20
+ )
21
+
22
+ if (!firstSuccess) {
23
+ return {
24
+ type: 'deserialization.failure',
25
+ mimeType: '*/*',
26
+ reason: deserializeEvents
27
+ .map((deserializeEvent) =>
28
+ deserializeEvent.type === 'deserialization.failure'
29
+ ? deserializeEvent.reason
30
+ : '',
31
+ )
32
+ .join(', '),
33
+ } as const
34
+ }
35
+
36
+ return firstSuccess
37
+ },
38
+ actions: [
39
+ ({event}, deserializeEvent) => [
40
+ raise({
41
+ ...deserializeEvent,
42
+ dataTransfer: event.dataTransfer,
43
+ }),
44
+ ],
45
+ ],
46
+ })
@@ -2,10 +2,38 @@ import {createGuards} from '../behavior-actions/behavior.guards'
2
2
  import {isHotkey} from '../internal-utils/is-hotkey'
3
3
  import * as selectors from '../selectors'
4
4
  import {isEmptyTextBlock} from '../utils/util.is-empty-text-block'
5
- import {defineBehavior} from './behavior.types'
5
+ import {defineBehavior, raise} from './behavior.types'
6
6
 
7
7
  const MAX_LIST_LEVEL = 10
8
8
 
9
+ const toggleListItemOff = defineBehavior({
10
+ on: 'list item.toggle',
11
+ guard: ({context, event}) =>
12
+ selectors.isActiveListItem(event.listItem)({context}),
13
+ actions: [
14
+ ({event}) => [
15
+ raise({
16
+ type: 'list item.remove',
17
+ listItem: event.listItem,
18
+ }),
19
+ ],
20
+ ],
21
+ })
22
+
23
+ const toggleListItemOn = defineBehavior({
24
+ on: 'list item.toggle',
25
+ guard: ({context, event}) =>
26
+ !selectors.isActiveListItem(event.listItem)({context}),
27
+ actions: [
28
+ ({event}) => [
29
+ raise({
30
+ type: 'list item.add',
31
+ listItem: event.listItem,
32
+ }),
33
+ ],
34
+ ],
35
+ })
36
+
9
37
  const clearListOnBackspace = defineBehavior({
10
38
  on: 'delete.backward',
11
39
  guard: ({context}) => {
@@ -29,11 +57,11 @@ const clearListOnBackspace = defineBehavior({
29
57
  },
30
58
  actions: [
31
59
  (_, {focusTextBlock}) => [
32
- {
60
+ raise({
33
61
  type: 'text block.unset',
34
62
  props: ['listItem', 'level'],
35
63
  at: focusTextBlock.path,
36
- },
64
+ }),
37
65
  ],
38
66
  ],
39
67
  })
@@ -65,11 +93,11 @@ const unindentListOnBackspace = defineBehavior({
65
93
  },
66
94
  actions: [
67
95
  (_, {focusTextBlock, level}) => [
68
- {
96
+ raise({
69
97
  type: 'text block.set',
70
98
  level,
71
99
  at: focusTextBlock.path,
72
- },
100
+ }),
73
101
  ],
74
102
  ],
75
103
  })
@@ -92,11 +120,11 @@ const clearListOnEnter = defineBehavior({
92
120
  },
93
121
  actions: [
94
122
  (_, {focusListBlock}) => [
95
- {
123
+ raise({
96
124
  type: 'text block.unset',
97
125
  props: ['listItem', 'level'],
98
126
  at: focusListBlock.path,
99
- },
127
+ }),
100
128
  ],
101
129
  ],
102
130
  })
@@ -131,14 +159,16 @@ const indentListOnTab = defineBehavior({
131
159
  },
132
160
  actions: [
133
161
  (_, {selectedListBlocks}) =>
134
- selectedListBlocks.map((selectedListBlock) => ({
135
- type: 'text block.set',
136
- level: Math.min(
137
- MAX_LIST_LEVEL,
138
- Math.max(1, selectedListBlock.node.level + 1),
139
- ),
140
- at: selectedListBlock.path,
141
- })),
162
+ selectedListBlocks.map((selectedListBlock) =>
163
+ raise({
164
+ type: 'text block.set',
165
+ level: Math.min(
166
+ MAX_LIST_LEVEL,
167
+ Math.max(1, selectedListBlock.node.level + 1),
168
+ ),
169
+ at: selectedListBlock.path,
170
+ }),
171
+ ),
142
172
  ],
143
173
  })
144
174
 
@@ -172,18 +202,22 @@ const unindentListOnShiftTab = defineBehavior({
172
202
  },
173
203
  actions: [
174
204
  (_, {selectedListBlocks}) =>
175
- selectedListBlocks.map((selectedListBlock) => ({
176
- type: 'text block.set',
177
- level: Math.min(
178
- MAX_LIST_LEVEL,
179
- Math.max(1, selectedListBlock.node.level - 1),
180
- ),
181
- at: selectedListBlock.path,
182
- })),
205
+ selectedListBlocks.map((selectedListBlock) =>
206
+ raise({
207
+ type: 'text block.set',
208
+ level: Math.min(
209
+ MAX_LIST_LEVEL,
210
+ Math.max(1, selectedListBlock.node.level - 1),
211
+ ),
212
+ at: selectedListBlock.path,
213
+ }),
214
+ ),
183
215
  ],
184
216
  })
185
217
 
186
218
  export const coreListBehaviors = {
219
+ toggleListItemOff,
220
+ toggleListItemOn,
187
221
  clearListOnBackspace,
188
222
  unindentListOnBackspace,
189
223
  clearListOnEnter,
@@ -0,0 +1,44 @@
1
+ import {defineBehavior, raise} from './behavior.types'
2
+
3
+ export const coreSerializeBehaviors = {
4
+ 'serialize': defineBehavior({
5
+ on: 'serialize',
6
+ guard: ({context, event}) => {
7
+ if (context.converters.length === 0) {
8
+ return false
9
+ }
10
+
11
+ const serializeEvents = context.converters.map((converter) =>
12
+ converter.serialize({context, event}),
13
+ )
14
+
15
+ if (serializeEvents.length === 0) {
16
+ return false
17
+ }
18
+
19
+ return serializeEvents
20
+ },
21
+ actions: [
22
+ ({event}, serializeEvents) =>
23
+ serializeEvents.map((serializeEvent) =>
24
+ raise({
25
+ ...serializeEvent,
26
+ dataTransfer: event.dataTransfer,
27
+ }),
28
+ ),
29
+ ],
30
+ }),
31
+ 'serialization.success': defineBehavior({
32
+ on: 'serialization.success',
33
+ actions: [
34
+ ({event}) => [
35
+ raise({
36
+ type: 'data transfer.set',
37
+ data: event.data,
38
+ dataTransfer: event.dataTransfer,
39
+ mimeType: event.mimeType,
40
+ }),
41
+ ],
42
+ ],
43
+ }),
44
+ }
@@ -0,0 +1,19 @@
1
+ import * as selectors from '../selectors'
2
+ import {defineBehavior, raise} from './behavior.types'
3
+
4
+ const toggleStyleOff = defineBehavior({
5
+ on: 'style.toggle',
6
+ guard: ({context, event}) => selectors.isActiveStyle(event.style)({context}),
7
+ actions: [({event}) => [raise({type: 'style.remove', style: event.style})]],
8
+ })
9
+
10
+ const toggleStyleOn = defineBehavior({
11
+ on: 'style.toggle',
12
+ guard: ({context, event}) => !selectors.isActiveStyle(event.style)({context}),
13
+ actions: [({event}) => [raise({type: 'style.add', style: event.style})]],
14
+ })
15
+
16
+ export const coreStyleBehaviors = {
17
+ toggleStyleOff,
18
+ toggleStyleOn,
19
+ }
@@ -1,6 +1,10 @@
1
+ import {coreAnnotationBehaviors} from './behavior.core.annotations'
1
2
  import {coreBlockObjectBehaviors} from './behavior.core.block-objects'
2
3
  import {coreDecoratorBehaviors} from './behavior.core.decorators'
4
+ import {coreDeserializeBehavior} from './behavior.core.deserialize'
3
5
  import {coreListBehaviors} from './behavior.core.lists'
6
+ import {coreSerializeBehaviors} from './behavior.core.serialize'
7
+ import {coreStyleBehaviors} from './behavior.core.style'
4
8
  import {defineBehavior} from './behavior.types'
5
9
 
6
10
  const softReturn = defineBehavior({
@@ -13,20 +17,31 @@ const softReturn = defineBehavior({
13
17
  */
14
18
  export const coreBehaviors = [
15
19
  softReturn,
20
+ coreAnnotationBehaviors.toggleAnnotationOff,
21
+ coreAnnotationBehaviors.toggleAnnotationOn,
22
+ coreDecoratorBehaviors.toggleDecoratorOff,
23
+ coreDecoratorBehaviors.toggleDecoratorOn,
16
24
  coreDecoratorBehaviors.strongShortcut,
17
25
  coreDecoratorBehaviors.emShortcut,
18
26
  coreDecoratorBehaviors.underlineShortcut,
19
27
  coreDecoratorBehaviors.codeShortcut,
28
+ coreDeserializeBehavior,
20
29
  coreBlockObjectBehaviors.arrowDownOnLonelyBlockObject,
21
30
  coreBlockObjectBehaviors.arrowUpOnLonelyBlockObject,
22
31
  coreBlockObjectBehaviors.breakingBlockObject,
23
32
  coreBlockObjectBehaviors.deletingEmptyTextBlockAfterBlockObject,
24
33
  coreBlockObjectBehaviors.deletingEmptyTextBlockBeforeBlockObject,
34
+ coreListBehaviors.toggleListItemOff,
35
+ coreListBehaviors.toggleListItemOn,
25
36
  coreListBehaviors.clearListOnBackspace,
26
37
  coreListBehaviors.unindentListOnBackspace,
27
38
  coreListBehaviors.clearListOnEnter,
28
39
  coreListBehaviors.indentListOnTab,
29
40
  coreListBehaviors.unindentListOnShiftTab,
41
+ coreSerializeBehaviors.serialize,
42
+ coreSerializeBehaviors['serialization.success'],
43
+ coreStyleBehaviors.toggleStyleOff,
44
+ coreStyleBehaviors.toggleStyleOn,
30
45
  ]
31
46
 
32
47
  /**
@@ -34,7 +49,11 @@ export const coreBehaviors = [
34
49
  */
35
50
  export const coreBehavior = {
36
51
  softReturn,
52
+ annotation: coreAnnotationBehaviors,
37
53
  decorators: coreDecoratorBehaviors,
54
+ deserialize: coreDeserializeBehavior,
38
55
  blockObjects: coreBlockObjectBehaviors,
39
56
  lists: coreListBehaviors,
57
+ ...coreSerializeBehaviors,
58
+ style: coreSerializeBehaviors,
40
59
  }