@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
@@ -1,218 +0,0 @@
1
- import {
2
- isKeySegment,
3
- isPortableTextSpan,
4
- isPortableTextTextBlock,
5
- type KeyedSegment,
6
- type PortableTextBlock,
7
- type PortableTextObject,
8
- type PortableTextSpan,
9
- type PortableTextTextBlock,
10
- } from '@sanity/types'
11
- import type {BehaviorContext} from './behavior.types'
12
-
13
- /**
14
- * Selection utilities
15
- */
16
-
17
- export function selectionIsCollapsed(context: BehaviorContext) {
18
- return (
19
- JSON.stringify(context.selection?.anchor.path) ===
20
- JSON.stringify(context.selection?.focus.path) &&
21
- context.selection?.anchor.offset === context.selection?.focus.offset
22
- )
23
- }
24
-
25
- /**
26
- * Value utilities
27
- */
28
-
29
- export function getFocusBlock(
30
- context: BehaviorContext,
31
- ): {node: PortableTextBlock; path: [KeyedSegment]} | undefined {
32
- const key = context.selection
33
- ? isKeySegment(context.selection.focus.path[0])
34
- ? context.selection.focus.path[0]._key
35
- : undefined
36
- : undefined
37
-
38
- const node = key
39
- ? context.value.find((block) => block._key === key)
40
- : undefined
41
-
42
- return node && key ? {node, path: [{_key: key}]} : undefined
43
- }
44
-
45
- export function getFocusTextBlock(
46
- context: BehaviorContext,
47
- ): {node: PortableTextTextBlock; path: [KeyedSegment]} | undefined {
48
- const focusBlock = getFocusBlock(context)
49
-
50
- return focusBlock && isPortableTextTextBlock(focusBlock.node)
51
- ? {node: focusBlock.node, path: focusBlock.path}
52
- : undefined
53
- }
54
-
55
- export function getFocusBlockObject(
56
- context: BehaviorContext,
57
- ): {node: PortableTextObject; path: [KeyedSegment]} | undefined {
58
- const focusBlock = getFocusBlock(context)
59
-
60
- return focusBlock && !isPortableTextTextBlock(focusBlock.node)
61
- ? {node: focusBlock.node, path: focusBlock.path}
62
- : undefined
63
- }
64
-
65
- export function getFocusChild(context: BehaviorContext):
66
- | {
67
- node: PortableTextObject | PortableTextSpan
68
- path: [KeyedSegment, 'children', KeyedSegment]
69
- }
70
- | undefined {
71
- const focusBlock = getFocusTextBlock(context)
72
-
73
- if (!focusBlock) {
74
- return undefined
75
- }
76
-
77
- const key = context.selection
78
- ? isKeySegment(context.selection.focus.path[2])
79
- ? context.selection.focus.path[2]._key
80
- : undefined
81
- : undefined
82
-
83
- const node = key
84
- ? focusBlock.node.children.find((span) => span._key === key)
85
- : undefined
86
-
87
- return node && key
88
- ? {node, path: [...focusBlock.path, 'children', {_key: key}]}
89
- : undefined
90
- }
91
-
92
- export function getFocusSpan(
93
- context: BehaviorContext,
94
- ):
95
- | {node: PortableTextSpan; path: [KeyedSegment, 'children', KeyedSegment]}
96
- | undefined {
97
- const focusChild = getFocusChild(context)
98
-
99
- return focusChild && isPortableTextSpan(focusChild.node)
100
- ? {node: focusChild.node, path: focusChild.path}
101
- : undefined
102
- }
103
-
104
- export function getSelectionStartBlock(context: BehaviorContext):
105
- | {
106
- node: PortableTextBlock
107
- path: [KeyedSegment]
108
- }
109
- | undefined {
110
- const key = context.selection.backward
111
- ? isKeySegment(context.selection.focus.path[0])
112
- ? context.selection.focus.path[0]._key
113
- : undefined
114
- : isKeySegment(context.selection.anchor.path[0])
115
- ? context.selection.anchor.path[0]._key
116
- : undefined
117
-
118
- const node = key
119
- ? context.value.find((block) => block._key === key)
120
- : undefined
121
-
122
- return node && key ? {node, path: [{_key: key}]} : undefined
123
- }
124
-
125
- export function getSelectionEndBlock(context: BehaviorContext):
126
- | {
127
- node: PortableTextBlock
128
- path: [KeyedSegment]
129
- }
130
- | undefined {
131
- const key = context.selection.backward
132
- ? isKeySegment(context.selection.anchor.path[0])
133
- ? context.selection.anchor.path[0]._key
134
- : undefined
135
- : isKeySegment(context.selection.focus.path[0])
136
- ? context.selection.focus.path[0]._key
137
- : undefined
138
-
139
- const node = key
140
- ? context.value.find((block) => block._key === key)
141
- : undefined
142
-
143
- return node && key ? {node, path: [{_key: key}]} : undefined
144
- }
145
-
146
- export function getPreviousBlock(
147
- context: BehaviorContext,
148
- ): {node: PortableTextBlock; path: [KeyedSegment]} | undefined {
149
- let previousBlock: {node: PortableTextBlock; path: [KeyedSegment]} | undefined
150
- const selectionStartBlock = getSelectionStartBlock(context)
151
-
152
- if (!selectionStartBlock) {
153
- return undefined
154
- }
155
-
156
- let foundSelectionStartBlock = false
157
-
158
- for (const block of context.value) {
159
- if (block._key === selectionStartBlock.node._key) {
160
- foundSelectionStartBlock = true
161
- break
162
- }
163
-
164
- previousBlock = {node: block, path: [{_key: block._key}]}
165
- }
166
-
167
- if (foundSelectionStartBlock && previousBlock) {
168
- return previousBlock
169
- }
170
-
171
- return undefined
172
- }
173
-
174
- export function getNextBlock(
175
- context: BehaviorContext,
176
- ): {node: PortableTextBlock; path: [KeyedSegment]} | undefined {
177
- let nextBlock: {node: PortableTextBlock; path: [KeyedSegment]} | undefined
178
- const selectionEndBlock = getSelectionEndBlock(context)
179
-
180
- if (!selectionEndBlock) {
181
- return undefined
182
- }
183
-
184
- let foundSelectionEndBlock = false
185
-
186
- for (const block of context.value) {
187
- if (block._key === selectionEndBlock.node._key) {
188
- foundSelectionEndBlock = true
189
- continue
190
- }
191
-
192
- if (foundSelectionEndBlock) {
193
- nextBlock = {node: block, path: [{_key: block._key}]}
194
- break
195
- }
196
- }
197
-
198
- if (foundSelectionEndBlock && nextBlock) {
199
- return nextBlock
200
- }
201
-
202
- return undefined
203
- }
204
-
205
- export function isEmptyTextBlock(block: PortableTextBlock) {
206
- if (!isPortableTextTextBlock(block)) {
207
- return false
208
- }
209
-
210
- const onlyText = block.children.every(isPortableTextSpan)
211
- const blockText = getTextBlockText(block)
212
-
213
- return onlyText && blockText === ''
214
- }
215
-
216
- export function getTextBlockText(block: PortableTextTextBlock) {
217
- return block.children.map((child) => child.text ?? '').join('')
218
- }
@@ -1,31 +0,0 @@
1
- import type {PortableTextBlock} from '@sanity/types'
2
- import type {EditorSelectionPoint} from '../../types/editor'
3
- import {getSelectionText} from './behavior.utils.get-selection-text'
4
- import {getStartPoint} from './behavior.utils.get-start-point'
5
- import {isKeyedSegment} from './behavior.utils.is-keyed-segment'
6
-
7
- export function getBlockTextBefore({
8
- value,
9
- point,
10
- }: {
11
- value: Array<PortableTextBlock>
12
- point: EditorSelectionPoint
13
- }) {
14
- const key = isKeyedSegment(point.path[0]) ? point.path[0]._key : undefined
15
-
16
- const block = key ? value.find((block) => block._key === key) : undefined
17
-
18
- if (!block) {
19
- return ''
20
- }
21
-
22
- const startPoint = getStartPoint({node: block, path: [{_key: block._key}]})
23
-
24
- return getSelectionText({
25
- value,
26
- selection: {
27
- anchor: startPoint,
28
- focus: point,
29
- },
30
- })
31
- }
@@ -1,172 +0,0 @@
1
- import {Editor, Element, Text, Transforms, type Node} from 'slate'
2
- import type {
3
- PortableTextMemberSchemaTypes,
4
- PortableTextSlateEditor,
5
- } from '../../types/editor'
6
- import {debugWithName} from '../../utils/debug'
7
-
8
- const debug = debugWithName('plugin:withPortableTextLists')
9
- const MAX_LIST_LEVEL = 10
10
-
11
- export function createWithPortableTextLists(
12
- types: PortableTextMemberSchemaTypes,
13
- ) {
14
- return function withPortableTextLists(
15
- editor: PortableTextSlateEditor,
16
- ): PortableTextSlateEditor {
17
- editor.pteToggleListItem = (listItemStyle: string) => {
18
- const isActive = editor.pteHasListStyle(listItemStyle)
19
- if (isActive) {
20
- debug(`Remove list item '${listItemStyle}'`)
21
- editor.pteUnsetListItem(listItemStyle)
22
- } else {
23
- debug(`Add list item '${listItemStyle}'`)
24
- editor.pteSetListItem(listItemStyle)
25
- }
26
- }
27
-
28
- editor.pteUnsetListItem = (listItemStyle: string) => {
29
- if (!editor.selection) {
30
- return
31
- }
32
- const selectedBlocks = [
33
- ...Editor.nodes(editor, {
34
- at: editor.selection,
35
- match: (node) =>
36
- Element.isElement(node) && node._type === types.block.name,
37
- }),
38
- ]
39
- selectedBlocks.forEach(([node, path]) => {
40
- if (editor.isListBlock(node)) {
41
- const {listItem, level, ...rest} = node
42
- const newNode = {
43
- ...rest,
44
- listItem: undefined,
45
- level: undefined,
46
- } as Partial<Node>
47
- debug(`Unsetting list '${listItemStyle}'`)
48
- Transforms.setNodes(editor, newNode, {at: path})
49
- }
50
- })
51
- }
52
-
53
- editor.pteSetListItem = (listItemStyle: string) => {
54
- if (!editor.selection) {
55
- return
56
- }
57
- const selectedBlocks = [
58
- ...Editor.nodes(editor, {
59
- at: editor.selection,
60
- match: (node) => editor.isTextBlock(node),
61
- }),
62
- ]
63
- selectedBlocks.forEach(([node, path]) => {
64
- debug(`Setting list '${listItemStyle}'`)
65
- Transforms.setNodes(
66
- editor,
67
- {
68
- ...node,
69
- level: 1,
70
- listItem: listItemStyle || (types.lists[0] && types.lists[0].value),
71
- } as Partial<Node>,
72
- {at: path},
73
- )
74
- })
75
- }
76
-
77
- editor.pteEndList = () => {
78
- if (!editor.selection) {
79
- return false
80
- }
81
- const selectedBlocks = [
82
- ...Editor.nodes(editor, {
83
- at: editor.selection,
84
- match: (node) =>
85
- Element.isElement(node) &&
86
- editor.isListBlock(node) &&
87
- node.children.length === 1 &&
88
- Text.isText(node.children[0]) &&
89
- node.children[0].text === '',
90
- }),
91
- ]
92
- if (selectedBlocks.length === 0) {
93
- return false
94
- }
95
- selectedBlocks.forEach(([node, path]) => {
96
- if (Element.isElement(node)) {
97
- debug('Unset list')
98
- Transforms.setNodes(
99
- editor,
100
- {
101
- ...node,
102
- level: undefined,
103
- listItem: undefined,
104
- },
105
- {at: path},
106
- )
107
- }
108
- })
109
- return true // Note: we are exiting the plugin chain by not returning editor (or hotkey plugin 'enter' will fire)
110
- }
111
-
112
- editor.pteIncrementBlockLevels = (reverse?: boolean): boolean => {
113
- if (!editor.selection) {
114
- return false
115
- }
116
- const selectedBlocks = [
117
- ...Editor.nodes(editor, {
118
- at: editor.selection,
119
- match: (node) => !!editor.isListBlock(node),
120
- }),
121
- ]
122
- if (selectedBlocks.length === 0) {
123
- return false
124
- }
125
- selectedBlocks.forEach(([node, path]) => {
126
- if (editor.isListBlock(node)) {
127
- let level = node.level || 1
128
- if (reverse) {
129
- level--
130
- debug(
131
- 'Decrementing list level',
132
- Math.min(MAX_LIST_LEVEL, Math.max(1, level)),
133
- )
134
- } else {
135
- level++
136
- debug(
137
- 'Incrementing list level',
138
- Math.min(MAX_LIST_LEVEL, Math.max(1, level)),
139
- )
140
- }
141
- Transforms.setNodes(
142
- editor,
143
- {level: Math.min(MAX_LIST_LEVEL, Math.max(1, level))},
144
- {at: path},
145
- )
146
- }
147
- })
148
- return true
149
- }
150
-
151
- editor.pteHasListStyle = (listStyle: string): boolean => {
152
- if (!editor.selection) {
153
- return false
154
- }
155
- const selectedBlocks = [
156
- ...Editor.nodes(editor, {
157
- at: editor.selection,
158
- match: (node) => editor.isTextBlock(node),
159
- }),
160
- ]
161
-
162
- if (selectedBlocks.length > 0) {
163
- return selectedBlocks.every(
164
- ([node]) => editor.isListBlock(node) && node.listItem === listStyle,
165
- )
166
- }
167
- return false
168
- }
169
-
170
- return editor
171
- }
172
- }