@portabletext/editor 1.13.0 → 1.14.1

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 (74) hide show
  1. package/README.md +1 -1
  2. package/lib/_chunks-cjs/selector.get-text-before.cjs +320 -0
  3. package/lib/_chunks-cjs/selector.get-text-before.cjs.map +1 -0
  4. package/lib/_chunks-es/selector.get-text-before.js +321 -0
  5. package/lib/_chunks-es/selector.get-text-before.js.map +1 -0
  6. package/lib/{index.esm.js → index.cjs} +1703 -1431
  7. package/lib/index.cjs.map +1 -0
  8. package/lib/{index.d.mts → index.d.cts} +4038 -313
  9. package/lib/index.d.ts +4038 -313
  10. package/lib/index.js +1724 -1407
  11. package/lib/index.js.map +1 -1
  12. package/lib/selectors/index.cjs +35 -0
  13. package/lib/selectors/index.cjs.map +1 -0
  14. package/lib/selectors/index.d.cts +243 -0
  15. package/lib/selectors/index.d.ts +243 -0
  16. package/lib/selectors/index.js +36 -0
  17. package/lib/selectors/index.js.map +1 -0
  18. package/package.json +21 -13
  19. package/src/editor/Editable.tsx +1 -1
  20. package/src/editor/PortableTextEditor.tsx +19 -4
  21. package/src/editor/__tests__/handleClick.test.tsx +4 -4
  22. package/src/editor/behavior/behavior.action.insert-block-object.ts +1 -1
  23. package/src/editor/behavior/behavior.action.insert-break.ts +3 -3
  24. package/src/editor/behavior/behavior.action.insert-inline-object.ts +58 -0
  25. package/src/editor/behavior/behavior.action.insert-span.ts +1 -1
  26. package/src/editor/behavior/behavior.action.list-item.ts +100 -0
  27. package/src/editor/behavior/behavior.action.style.ts +108 -0
  28. package/src/editor/behavior/behavior.action.text-block.set.ts +25 -0
  29. package/src/editor/behavior/behavior.action.text-block.unset.ts +17 -0
  30. package/src/editor/behavior/behavior.actions.ts +178 -109
  31. package/src/editor/behavior/behavior.code-editor.ts +30 -40
  32. package/src/editor/behavior/behavior.core.block-objects.ts +26 -26
  33. package/src/editor/behavior/behavior.core.decorators.ts +9 -6
  34. package/src/editor/behavior/behavior.core.lists.ts +139 -17
  35. package/src/editor/behavior/behavior.core.ts +5 -2
  36. package/src/editor/behavior/behavior.guards.ts +28 -0
  37. package/src/editor/behavior/behavior.links.ts +7 -7
  38. package/src/editor/behavior/behavior.markdown.ts +68 -79
  39. package/src/editor/behavior/behavior.types.ts +86 -60
  40. package/src/editor/{use-editor.ts → create-editor.ts} +13 -8
  41. package/src/editor/editor-event-listener.tsx +2 -2
  42. package/src/editor/editor-machine.ts +54 -15
  43. package/src/editor/editor-provider.tsx +5 -5
  44. package/src/editor/editor-selector.ts +49 -0
  45. package/src/editor/editor-snapshot.ts +22 -0
  46. package/src/editor/get-value.ts +11 -0
  47. package/src/editor/plugins/create-with-event-listeners.ts +93 -5
  48. package/src/editor/plugins/createWithEditableAPI.ts +69 -20
  49. package/src/editor/plugins/createWithHotKeys.ts +0 -54
  50. package/src/editor/plugins/createWithPortableTextBlockStyle.ts +1 -55
  51. package/src/editor/plugins/with-plugins.ts +4 -8
  52. package/src/editor/{behavior/behavior.utils.block-offset.test.ts → utils/utils.block-offset.test.ts} +1 -1
  53. package/src/editor/{behavior/behavior.utils.block-offset.ts → utils/utils.block-offset.ts} +1 -8
  54. package/src/editor/{behavior/behavior.utils.reverse-selection.ts → utils/utils.reverse-selection.ts} +3 -5
  55. package/src/editor/utils/utils.ts +21 -0
  56. package/src/index.ts +13 -13
  57. package/src/selectors/index.ts +15 -0
  58. package/src/selectors/selector.get-active-list-item.ts +37 -0
  59. package/src/{editor/behavior/behavior.utils.get-selection-text.ts → selectors/selector.get-selection-text.ts} +10 -15
  60. package/src/selectors/selector.get-text-before.ts +41 -0
  61. package/src/selectors/selectors.ts +329 -0
  62. package/src/types/editor.ts +0 -60
  63. package/src/utils/is-hotkey.test.ts +2 -0
  64. package/src/utils/operationToPatches.ts +5 -0
  65. package/src/utils/paths.ts +4 -11
  66. package/src/utils/ranges.ts +3 -3
  67. package/lib/index.esm.js.map +0 -1
  68. package/lib/index.mjs +0 -7541
  69. package/lib/index.mjs.map +0 -1
  70. package/src/editor/behavior/behavior.utils.ts +0 -218
  71. package/src/editor/behavior/behavior.utilts.get-text-before.ts +0 -31
  72. package/src/editor/plugins/createWithPortableTextLists.ts +0 -172
  73. /package/src/editor/{behavior/behavior.utils.get-start-point.ts → utils/utils.get-start-point.ts} +0 -0
  74. /package/src/editor/{behavior/behavior.utils.is-keyed-segment.ts → utils/utils.is-keyed-segment.ts} +0 -0
@@ -28,13 +28,13 @@ import {debugWithName} from '../utils/debug'
28
28
  import {getPortableTextMemberSchemaTypes} from '../utils/getPortableTextMemberSchemaTypes'
29
29
  import {compileType} from '../utils/schema'
30
30
  import {Synchronizer} from './components/Synchronizer'
31
+ import {createEditor, type Editor} from './create-editor'
31
32
  import {EditorActorContext} from './editor-actor-context'
32
33
  import type {EditorActor} from './editor-machine'
33
34
  import {PortableTextEditorContext} from './hooks/usePortableTextEditor'
34
35
  import {PortableTextEditorSelectionProvider} from './hooks/usePortableTextEditorSelection'
35
36
  import {defaultKeyGenerator} from './key-generator'
36
37
  import type {AddedAnnotationPaths} from './plugins/createWithEditableAPI'
37
- import {createEditor, type Editor} from './use-editor'
38
38
 
39
39
  const debug = debugWithName('component:PortableTextEditor')
40
40
 
@@ -148,7 +148,7 @@ export class PortableTextEditor extends Component<
148
148
 
149
149
  this.schemaTypes =
150
150
  this.editor._internal.editorActor.getSnapshot().context.schema
151
- this.editable = this.editor.editable
151
+ this.editable = this.editor._internal.editable
152
152
  }
153
153
 
154
154
  componentDidUpdate(prevProps: PortableTextEditorProps) {
@@ -204,7 +204,10 @@ export class PortableTextEditor extends Component<
204
204
  }
205
205
 
206
206
  public setEditable = (editable: EditableAPI) => {
207
- this.editor.editable = {...this.editor.editable, ...editable}
207
+ this.editor._internal.editable = {
208
+ ...this.editor._internal.editable,
209
+ ...editable,
210
+ }
208
211
  }
209
212
 
210
213
  render() {
@@ -235,7 +238,7 @@ export class PortableTextEditor extends Component<
235
238
  />
236
239
  <Synchronizer
237
240
  editorActor={this.editor._internal.editorActor}
238
- getValue={this.editor.editable.getValue}
241
+ getValue={this.editor._internal.editable.getValue}
239
242
  portableTextEditor={this}
240
243
  slateEditor={this.editor._internal.slateEditor.instance}
241
244
  />
@@ -442,6 +445,10 @@ export function RouteEventsToChanges(props: {
442
445
  debug('Subscribing to editor changes')
443
446
  const sub = props.editorActor.on('*', (event) => {
444
447
  switch (event.type) {
448
+ case 'blurred': {
449
+ handleChange({type: 'blur', event: event.event})
450
+ break
451
+ }
445
452
  case 'patch':
446
453
  handleChange(event)
447
454
  break
@@ -479,7 +486,15 @@ export function RouteEventsToChanges(props: {
479
486
  case 'annotation.add':
480
487
  case 'annotation.remove':
481
488
  case 'annotation.toggle':
489
+ case 'blur':
490
+ case 'decorator.add':
491
+ case 'decorator.remove':
492
+ case 'decorator.toggle':
482
493
  case 'focus':
494
+ case 'insert.block object':
495
+ case 'insert.inline object':
496
+ case 'list item.toggle':
497
+ case 'style.toggle':
483
498
  case 'patches':
484
499
  case 'readOnly toggled':
485
500
  break
@@ -76,7 +76,7 @@ describe('adds empty text block if its needed', () => {
76
76
 
77
77
  const element = await getEditableElement(component)
78
78
 
79
- await waitFor(async () => {
79
+ await waitFor(() => {
80
80
  if (editorRef.current && element) {
81
81
  PortableTextEditor.focus(editorRef.current)
82
82
  PortableTextEditor.select(editorRef.current, initialSelection)
@@ -127,7 +127,7 @@ describe('adds empty text block if its needed', () => {
127
127
  )
128
128
  const element = await getEditableElement(component)
129
129
 
130
- await waitFor(async () => {
130
+ await waitFor(() => {
131
131
  if (editorRef.current && element) {
132
132
  PortableTextEditor.focus(editorRef.current)
133
133
  PortableTextEditor.select(editorRef.current, initialSelection)
@@ -193,7 +193,7 @@ describe('adds empty text block if its needed', () => {
193
193
 
194
194
  const element = await getEditableElement(component)
195
195
 
196
- await waitFor(async () => {
196
+ await waitFor(() => {
197
197
  if (editorRef.current && element) {
198
198
  PortableTextEditor.focus(editorRef.current)
199
199
  PortableTextEditor.select(editorRef.current, initialSelection)
@@ -263,7 +263,7 @@ describe('adds empty text block if its needed', () => {
263
263
  const inlineType = editor?.schemaTypes.inlineObjects.find(
264
264
  (t) => t.name === 'someObject',
265
265
  )
266
- await waitFor(async () => {
266
+ await waitFor(() => {
267
267
  if (editor && inlineType && element) {
268
268
  PortableTextEditor.focus(editor)
269
269
  PortableTextEditor.select(editor, initialSelection)
@@ -3,7 +3,7 @@ import {insertBlock} from './behavior.action-utils.insert-block'
3
3
  import type {BehaviorActionImplementation} from './behavior.actions'
4
4
 
5
5
  export const insertBlockObjectActionImplementation: BehaviorActionImplementation<
6
- 'insert block object'
6
+ 'insert.block object'
7
7
  > = ({context, action}) => {
8
8
  const block = toSlateValue(
9
9
  [
@@ -4,7 +4,7 @@ import type {SlateTextBlock, VoidElement} from '../../types/slate'
4
4
  import type {BehaviorActionImplementation} from './behavior.actions'
5
5
 
6
6
  export const insertBreakActionImplementation: BehaviorActionImplementation<
7
- 'insert break'
7
+ 'insert.break'
8
8
  > = ({context, action}) => {
9
9
  const keyGenerator = context.keyGenerator
10
10
  const schema = context.schema
@@ -202,12 +202,12 @@ export const insertBreakActionImplementation: BehaviorActionImplementation<
202
202
  }
203
203
 
204
204
  export const insertSoftBreakActionImplementation: BehaviorActionImplementation<
205
- 'insert soft break'
205
+ 'insert.soft break'
206
206
  > = ({context, action}) => {
207
207
  // This mimics Slate's internal which also just does a regular insert break
208
208
  // when soft-breaking
209
209
  insertBreakActionImplementation({
210
210
  context,
211
- action: {...action, type: 'insert break'},
211
+ action: {...action, type: 'insert.break'},
212
212
  })
213
213
  }
@@ -0,0 +1,58 @@
1
+ import {Editor, Transforms, type Element} from 'slate'
2
+ import {toSlateValue} from '../../utils/values'
3
+ import type {BehaviorActionImplementation} from './behavior.actions'
4
+
5
+ export const insertInlineObjectActionImplementation: BehaviorActionImplementation<
6
+ 'insert.inline object'
7
+ > = ({context, action}) => {
8
+ if (
9
+ !context.schema.inlineObjects.some(
10
+ (inlineObject) => inlineObject.name === action.inlineObject.name,
11
+ )
12
+ ) {
13
+ console.error('Unable to insert unknown inline object')
14
+ return
15
+ }
16
+
17
+ if (!action.editor.selection) {
18
+ console.error('Unable to insert inline object without selection')
19
+ return
20
+ }
21
+
22
+ const [focusTextBlock] = Array.from(
23
+ Editor.nodes(action.editor, {
24
+ at: action.editor.selection.focus.path,
25
+ match: (node) => action.editor.isTextBlock(node),
26
+ }),
27
+ ).at(0) ?? [undefined, undefined]
28
+
29
+ if (!focusTextBlock) {
30
+ console.error('Unable to perform action without focus text block')
31
+ return
32
+ }
33
+
34
+ const block = toSlateValue(
35
+ [
36
+ {
37
+ _type: context.schema.block.name,
38
+ _key: context.keyGenerator(),
39
+ children: [
40
+ {
41
+ _type: action.inlineObject.name,
42
+ _key: context.keyGenerator(),
43
+ ...(action.inlineObject.value ?? {}),
44
+ },
45
+ ],
46
+ },
47
+ ],
48
+ {schemaTypes: context.schema},
49
+ ).at(0) as unknown as Element
50
+ const child = block?.children.at(0)
51
+
52
+ if (!child) {
53
+ console.error('Unable to insert inline object')
54
+ return
55
+ }
56
+
57
+ Transforms.insertNodes(action.editor, child)
58
+ }
@@ -2,7 +2,7 @@ import {Editor, Transforms} from 'slate'
2
2
  import type {BehaviorActionImplementation} from './behavior.actions'
3
3
 
4
4
  export const insertSpanActionImplementation: BehaviorActionImplementation<
5
- 'insert span'
5
+ 'insert.span'
6
6
  > = ({context, action}) => {
7
7
  if (!action.editor.selection) {
8
8
  console.error('Unable to perform action without selection', action)
@@ -0,0 +1,100 @@
1
+ import {Editor, Transforms} from 'slate'
2
+ import type {BehaviorActionImplementation} from '../behavior/behavior.actions'
3
+ import {createGuards} from '../behavior/behavior.guards'
4
+
5
+ export const toggleListItemActionImplementation: BehaviorActionImplementation<
6
+ 'list item.toggle'
7
+ > = ({context, action}) => {
8
+ const isActive = isListItemActive({
9
+ editor: action.editor,
10
+ listItem: action.listItem,
11
+ })
12
+
13
+ if (isActive) {
14
+ removeListItemActionImplementation({
15
+ context,
16
+ action: {...action, type: 'list item.remove'},
17
+ })
18
+ } else {
19
+ addListItemActionImplementation({
20
+ context,
21
+ action: {...action, type: 'list item.add'},
22
+ })
23
+ }
24
+ }
25
+
26
+ export const removeListItemActionImplementation: BehaviorActionImplementation<
27
+ 'list item.remove'
28
+ > = ({context, action}) => {
29
+ if (!action.editor.selection) {
30
+ return
31
+ }
32
+
33
+ const guards = createGuards(context)
34
+
35
+ const selectedBlocks = [
36
+ ...Editor.nodes(action.editor, {
37
+ at: action.editor.selection,
38
+ match: (node) => guards.isListBlock(node),
39
+ }),
40
+ ]
41
+
42
+ for (const [, at] of selectedBlocks) {
43
+ Transforms.unsetNodes(action.editor, ['listItem', 'level'], {at})
44
+ }
45
+ }
46
+
47
+ export const addListItemActionImplementation: BehaviorActionImplementation<
48
+ 'list item.add'
49
+ > = ({context, action}) => {
50
+ if (!action.editor.selection) {
51
+ return
52
+ }
53
+
54
+ const guards = createGuards(context)
55
+
56
+ const selectedBlocks = [
57
+ ...Editor.nodes(action.editor, {
58
+ at: action.editor.selection,
59
+ match: (node) => guards.isTextBlock(node),
60
+ }),
61
+ ]
62
+
63
+ for (const [, at] of selectedBlocks) {
64
+ Transforms.setNodes(
65
+ action.editor,
66
+ {
67
+ level: 1,
68
+ listItem: action.listItem,
69
+ },
70
+ {at},
71
+ )
72
+ }
73
+ }
74
+
75
+ export function isListItemActive({
76
+ editor,
77
+ listItem,
78
+ }: {
79
+ editor: Editor
80
+ listItem: string
81
+ }): boolean {
82
+ if (!editor.selection) {
83
+ return false
84
+ }
85
+
86
+ const selectedBlocks = [
87
+ ...Editor.nodes(editor, {
88
+ at: editor.selection,
89
+ match: (node) => editor.isTextBlock(node),
90
+ }),
91
+ ]
92
+
93
+ if (selectedBlocks.length > 0) {
94
+ return selectedBlocks.every(
95
+ ([node]) => editor.isListBlock(node) && node.listItem === listItem,
96
+ )
97
+ }
98
+
99
+ return false
100
+ }
@@ -0,0 +1,108 @@
1
+ import {Editor, Transforms} from 'slate'
2
+ import type {BehaviorActionImplementation} from './behavior.actions'
3
+ import {createGuards} from './behavior.guards'
4
+
5
+ export const toggleStyleActionImplementation: BehaviorActionImplementation<
6
+ 'style.toggle'
7
+ > = ({context, action}) => {
8
+ const isActive = isStyleActive({
9
+ editor: action.editor,
10
+ style: action.style,
11
+ })
12
+
13
+ if (isActive) {
14
+ removeStyleActionImplementation({
15
+ context,
16
+ action: {...action, type: 'style.remove'},
17
+ })
18
+ } else {
19
+ addStyleActionImplementation({
20
+ context,
21
+ action: {...action, type: 'style.add'},
22
+ })
23
+ }
24
+ }
25
+
26
+ export const removeStyleActionImplementation: BehaviorActionImplementation<
27
+ 'style.remove'
28
+ > = ({context, action}) => {
29
+ if (!action.editor.selection) {
30
+ return
31
+ }
32
+
33
+ const defaultStyle = context.schema.styles[0].value
34
+ const guards = createGuards(context)
35
+
36
+ const selectedBlocks = [
37
+ ...Editor.nodes(action.editor, {
38
+ at: action.editor.selection,
39
+ match: (node) => guards.isTextBlock(node),
40
+ }),
41
+ ]
42
+
43
+ for (const [, at] of selectedBlocks) {
44
+ Transforms.setNodes(
45
+ action.editor,
46
+ {
47
+ style: defaultStyle,
48
+ },
49
+ {
50
+ at,
51
+ },
52
+ )
53
+ }
54
+ }
55
+
56
+ export const addStyleActionImplementation: BehaviorActionImplementation<
57
+ 'style.add'
58
+ > = ({context, action}) => {
59
+ if (!action.editor.selection) {
60
+ return
61
+ }
62
+
63
+ const guards = createGuards(context)
64
+
65
+ const selectedBlocks = [
66
+ ...Editor.nodes(action.editor, {
67
+ at: action.editor.selection,
68
+ match: (node) => guards.isTextBlock(node),
69
+ }),
70
+ ]
71
+
72
+ for (const [, at] of selectedBlocks) {
73
+ Transforms.setNodes(
74
+ action.editor,
75
+ {
76
+ style: action.style,
77
+ },
78
+ {
79
+ at,
80
+ },
81
+ )
82
+ }
83
+ }
84
+
85
+ export function isStyleActive({
86
+ editor,
87
+ style,
88
+ }: {
89
+ editor: Editor
90
+ style: string
91
+ }): boolean {
92
+ if (!editor.selection) {
93
+ return false
94
+ }
95
+
96
+ const selectedBlocks = [
97
+ ...Editor.nodes(editor, {
98
+ at: editor.selection,
99
+ match: (node) => editor.isTextBlock(node),
100
+ }),
101
+ ]
102
+
103
+ if (selectedBlocks.length > 0) {
104
+ return selectedBlocks.every(([node]) => node.style === style)
105
+ }
106
+
107
+ return false
108
+ }
@@ -0,0 +1,25 @@
1
+ import {Transforms} from 'slate'
2
+ import {toSlateRange} from '../../utils/ranges'
3
+ import type {BehaviorActionImplementation} from './behavior.actions'
4
+
5
+ export const textBlockSetActionImplementation: BehaviorActionImplementation<
6
+ 'text block.set'
7
+ > = ({action}) => {
8
+ const at = toSlateRange(
9
+ {
10
+ anchor: {path: action.at, offset: 0},
11
+ focus: {path: action.at, offset: 0},
12
+ },
13
+ action.editor,
14
+ )!
15
+
16
+ Transforms.setNodes(
17
+ action.editor,
18
+ {
19
+ ...(action.style ? {style: action.style} : {}),
20
+ ...(action.listItem ? {listItem: action.listItem} : {}),
21
+ ...(action.level ? {level: action.level} : {}),
22
+ },
23
+ {at},
24
+ )
25
+ }
@@ -0,0 +1,17 @@
1
+ import {Transforms} from 'slate'
2
+ import {toSlateRange} from '../../utils/ranges'
3
+ import type {BehaviorActionImplementation} from './behavior.actions'
4
+
5
+ export const textBlockUnsetActionImplementation: BehaviorActionImplementation<
6
+ 'text block.unset'
7
+ > = ({action}) => {
8
+ const at = toSlateRange(
9
+ {
10
+ anchor: {path: action.at, offset: 0},
11
+ focus: {path: action.at, offset: 0},
12
+ },
13
+ action.editor,
14
+ )!
15
+
16
+ Transforms.unsetNodes(action.editor, action.props, {at})
17
+ }