@portabletext/editor 1.36.6 → 1.38.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 (119) hide show
  1. package/lib/_chunks-cjs/behavior.core.cjs +84 -49
  2. package/lib/_chunks-cjs/behavior.core.cjs.map +1 -1
  3. package/lib/_chunks-cjs/behavior.markdown.cjs +1 -1
  4. package/lib/_chunks-cjs/editor-provider.cjs +919 -526
  5. package/lib/_chunks-cjs/editor-provider.cjs.map +1 -1
  6. package/lib/_chunks-cjs/{util.block-offsets-to-selection.cjs → parse-blocks.cjs} +36 -21
  7. package/lib/_chunks-cjs/parse-blocks.cjs.map +1 -0
  8. package/lib/_chunks-cjs/selector.get-text-before.cjs +2 -2
  9. package/lib/_chunks-cjs/selector.get-text-before.cjs.map +1 -1
  10. package/lib/_chunks-cjs/{selector.is-active-style.cjs → selector.is-overlapping-selection.cjs} +144 -3
  11. package/lib/_chunks-cjs/selector.is-overlapping-selection.cjs.map +1 -0
  12. package/lib/_chunks-cjs/util.slice-blocks.cjs +12 -0
  13. package/lib/_chunks-cjs/util.slice-blocks.cjs.map +1 -1
  14. package/lib/_chunks-es/behavior.core.js +84 -49
  15. package/lib/_chunks-es/behavior.core.js.map +1 -1
  16. package/lib/_chunks-es/behavior.markdown.js +1 -1
  17. package/lib/_chunks-es/editor-provider.js +911 -517
  18. package/lib/_chunks-es/editor-provider.js.map +1 -1
  19. package/lib/_chunks-es/{util.block-offsets-to-selection.js → parse-blocks.js} +37 -22
  20. package/lib/_chunks-es/parse-blocks.js.map +1 -0
  21. package/lib/_chunks-es/selector.get-text-before.js +1 -2
  22. package/lib/_chunks-es/selector.get-text-before.js.map +1 -1
  23. package/lib/_chunks-es/{selector.is-active-style.js → selector.is-overlapping-selection.js} +146 -5
  24. package/lib/_chunks-es/selector.is-overlapping-selection.js.map +1 -0
  25. package/lib/_chunks-es/util.slice-blocks.js +12 -0
  26. package/lib/_chunks-es/util.slice-blocks.js.map +1 -1
  27. package/lib/behaviors/index.d.cts +10535 -4689
  28. package/lib/behaviors/index.d.ts +10535 -4689
  29. package/lib/index.cjs +582 -209
  30. package/lib/index.cjs.map +1 -1
  31. package/lib/index.d.cts +5297 -1178
  32. package/lib/index.d.ts +5297 -1178
  33. package/lib/index.js +591 -213
  34. package/lib/index.js.map +1 -1
  35. package/lib/plugins/index.cjs +2 -2
  36. package/lib/plugins/index.cjs.map +1 -1
  37. package/lib/plugins/index.d.cts +5297 -1178
  38. package/lib/plugins/index.d.ts +5297 -1178
  39. package/lib/plugins/index.js +2 -2
  40. package/lib/selectors/index.cjs +21 -103
  41. package/lib/selectors/index.cjs.map +1 -1
  42. package/lib/selectors/index.d.cts +5313 -1178
  43. package/lib/selectors/index.d.ts +5313 -1178
  44. package/lib/selectors/index.js +13 -96
  45. package/lib/selectors/index.js.map +1 -1
  46. package/lib/utils/index.cjs +4 -4
  47. package/lib/utils/index.cjs.map +1 -1
  48. package/lib/utils/index.d.cts +5297 -1178
  49. package/lib/utils/index.d.ts +5297 -1178
  50. package/lib/utils/index.js +3 -4
  51. package/lib/utils/index.js.map +1 -1
  52. package/package.json +15 -14
  53. package/src/behavior-actions/behavior.action.blur.ts +8 -0
  54. package/src/behavior-actions/behavior.action.decorator.add.ts +2 -1
  55. package/src/behavior-actions/behavior.action.delete.backward.ts +7 -0
  56. package/src/behavior-actions/behavior.action.delete.block.ts +24 -0
  57. package/src/behavior-actions/behavior.action.delete.forward.ts +7 -0
  58. package/src/behavior-actions/behavior.action.delete.text.ts +2 -1
  59. package/src/behavior-actions/behavior.action.delete.ts +1 -3
  60. package/src/behavior-actions/behavior.action.deserialization.failure.ts +9 -0
  61. package/src/behavior-actions/behavior.action.deserialization.success.ts +16 -0
  62. package/src/behavior-actions/behavior.action.effect.ts +7 -0
  63. package/src/behavior-actions/behavior.action.focus.ts +8 -0
  64. package/src/behavior-actions/behavior.action.insert-blocks.ts +118 -74
  65. package/src/behavior-actions/behavior.action.insert-break.ts +1 -0
  66. package/src/behavior-actions/{behavior.action.insert-block-object.ts → behavior.action.insert.block-object.ts} +9 -14
  67. package/src/behavior-actions/behavior.action.insert.block.ts +247 -2
  68. package/src/behavior-actions/behavior.action.insert.text-block.ts +33 -0
  69. package/src/behavior-actions/behavior.action.insert.text.ts +7 -0
  70. package/src/behavior-actions/behavior.action.move.block-down.ts +48 -0
  71. package/src/behavior-actions/behavior.action.move.block-up.ts +53 -0
  72. package/src/behavior-actions/behavior.action.move.block.ts +16 -0
  73. package/src/behavior-actions/behavior.action.noop.ts +5 -0
  74. package/src/behavior-actions/behavior.action.select.next-block.ts +44 -0
  75. package/src/behavior-actions/behavior.action.select.previous-block.ts +48 -0
  76. package/src/behavior-actions/behavior.action.select.ts +15 -0
  77. package/src/behavior-actions/behavior.action.serialization.failure.ts +9 -0
  78. package/src/behavior-actions/behavior.action.serialization.success.ts +14 -0
  79. package/src/behavior-actions/behavior.actions.ts +54 -212
  80. package/src/behaviors/behavior.core.block-objects.ts +35 -6
  81. package/src/behaviors/behavior.core.insert-break.ts +1 -0
  82. package/src/behaviors/behavior.core.ts +2 -0
  83. package/src/behaviors/behavior.default.ts +241 -33
  84. package/src/behaviors/behavior.types.ts +138 -20
  85. package/src/converters/converter.portable-text.ts +5 -2
  86. package/src/converters/converter.text-html.serialize.test.ts +4 -4
  87. package/src/converters/converter.text-html.ts +5 -2
  88. package/src/converters/converter.text-plain.test.ts +6 -6
  89. package/src/converters/converter.text-plain.ts +5 -2
  90. package/src/converters/converter.types.ts +3 -3
  91. package/src/editor/Editable.tsx +403 -48
  92. package/src/editor/components/Element.tsx +133 -18
  93. package/src/editor/components/use-draggable.ts +34 -102
  94. package/src/editor/editor-machine.ts +66 -10
  95. package/src/editor/editor-selector.ts +2 -0
  96. package/src/editor/editor-snapshot.ts +17 -0
  97. package/src/editor/plugins/create-with-event-listeners.ts +6 -40
  98. package/src/internal-utils/create-test-snapshot.ts +2 -0
  99. package/src/internal-utils/event-position.ts +210 -0
  100. package/src/internal-utils/slate-utils.ts +56 -0
  101. package/src/internal-utils/weakMaps.ts +1 -15
  102. package/src/selectors/index.ts +2 -0
  103. package/src/selectors/selector.get-focus-inline-object.ts +21 -0
  104. package/src/selectors/selector.is-overlapping-selection.test.ts +171 -0
  105. package/src/selectors/selector.is-overlapping-selection.ts +108 -4
  106. package/src/selectors/selector.is-point-after-selection.ts +3 -1
  107. package/src/selectors/selector.is-point-before-selection.ts +3 -1
  108. package/src/selectors/selector.is-selecting-entire-blocks.ts +34 -0
  109. package/lib/_chunks-cjs/selector.is-active-style.cjs.map +0 -1
  110. package/lib/_chunks-cjs/util.block-offsets-to-selection.cjs.map +0 -1
  111. package/lib/_chunks-cjs/util.reverse-selection.cjs +0 -14
  112. package/lib/_chunks-cjs/util.reverse-selection.cjs.map +0 -1
  113. package/lib/_chunks-es/selector.is-active-style.js.map +0 -1
  114. package/lib/_chunks-es/util.block-offsets-to-selection.js.map +0 -1
  115. package/lib/_chunks-es/util.reverse-selection.js +0 -15
  116. package/lib/_chunks-es/util.reverse-selection.js.map +0 -1
  117. package/src/behavior-actions/behavior.action-utils.insert-block.ts +0 -61
  118. package/src/editor/__tests__/handleClick.test.tsx +0 -277
  119. package/src/editor/components/use-droppable.ts +0 -135
@@ -1,277 +0,0 @@
1
- import {fireEvent, render, waitFor} from '@testing-library/react'
2
- import {act, createRef, type RefObject} from 'react'
3
- import {describe, expect, it, vi} from 'vitest'
4
- import {PortableTextEditor} from '../PortableTextEditor'
5
- import {PortableTextEditorTester, schemaType} from './PortableTextEditorTester'
6
-
7
- async function getEditableElement(
8
- component: ReturnType<typeof render>,
9
- ): Promise<Element> {
10
- await act(async () => component)
11
- const element = component.container.querySelector(
12
- '[data-slate-editor="true"]',
13
- )
14
- if (!element) {
15
- throw new Error('Could not find element')
16
- }
17
- /**
18
- * Manually add this because JSDom doesn't implement this and Slate checks for it
19
- * internally before doing stuff.
20
- *
21
- * https://github.com/jsdom/jsdom/issues/1670
22
- */
23
- // @ts-ignore
24
- element.isContentEditable = true
25
- return element
26
- }
27
-
28
- describe('adds empty text block if its needed', () => {
29
- const newBlock = {
30
- _type: 'myTestBlockType',
31
- _key: '1',
32
- style: 'normal',
33
- markDefs: [],
34
- children: [
35
- {
36
- _type: 'span',
37
- _key: '2',
38
- text: '',
39
- marks: [],
40
- },
41
- ],
42
- }
43
- it('adds a new block at the bottom, when clicking on the portable text editor, because the only block is void and user is focused on that one', async () => {
44
- const initialValue = [
45
- {
46
- _key: 'b',
47
- _type: 'someObject',
48
- },
49
- ]
50
-
51
- const initialSelection = {
52
- focus: {path: [{_key: 'b'}], offset: 0},
53
- anchor: {path: [{_key: 'b'}], offset: 0},
54
- }
55
-
56
- const editorRef: RefObject<PortableTextEditor | null> = createRef()
57
- const onChange = vi.fn()
58
- const component = render(
59
- <PortableTextEditorTester
60
- onChange={onChange}
61
- ref={editorRef}
62
- schemaType={schemaType}
63
- value={initialValue}
64
- />,
65
- )
66
-
67
- await waitFor(() => {
68
- if (editorRef.current) {
69
- expect(onChange).toHaveBeenCalledWith({
70
- type: 'value',
71
- value: initialValue,
72
- })
73
- expect(onChange).toHaveBeenCalledWith({type: 'ready'})
74
- }
75
- })
76
-
77
- const element = await getEditableElement(component)
78
-
79
- await waitFor(() => {
80
- if (editorRef.current && element) {
81
- PortableTextEditor.focus(editorRef.current)
82
- PortableTextEditor.select(editorRef.current, initialSelection)
83
- fireEvent.click(element)
84
- expect(PortableTextEditor.getValue(editorRef.current)).toEqual([
85
- initialValue[0],
86
- newBlock,
87
- ])
88
- }
89
- })
90
- })
91
- it('should not add blocks if the last element is a text block', async () => {
92
- const initialValue = [
93
- {
94
- _key: 'b',
95
- _type: 'someObject',
96
- },
97
- {
98
- _type: 'myTestBlockType',
99
- _key: '1',
100
- style: 'normal',
101
- markDefs: [],
102
- children: [
103
- {
104
- _type: 'span',
105
- _key: '2',
106
- text: '',
107
- marks: [],
108
- },
109
- ],
110
- },
111
- ]
112
-
113
- const initialSelection = {
114
- focus: {path: [{_key: 'b'}], offset: 0},
115
- anchor: {path: [{_key: 'b'}], offset: 0},
116
- }
117
-
118
- const editorRef: RefObject<PortableTextEditor | null> = createRef()
119
- const onChange = vi.fn()
120
- const component = render(
121
- <PortableTextEditorTester
122
- onChange={onChange}
123
- ref={editorRef}
124
- schemaType={schemaType}
125
- value={initialValue}
126
- />,
127
- )
128
- const element = await getEditableElement(component)
129
-
130
- await waitFor(() => {
131
- if (editorRef.current && element) {
132
- PortableTextEditor.focus(editorRef.current)
133
- PortableTextEditor.select(editorRef.current, initialSelection)
134
- fireEvent.click(element)
135
- expect(PortableTextEditor.getValue(editorRef.current)).toEqual(
136
- initialValue,
137
- )
138
- }
139
- })
140
- })
141
-
142
- it('should not add blocks if the last element is void, but its not focused on that one', async () => {
143
- const initialValue = [
144
- {
145
- _key: 'a',
146
- _type: 'someObject',
147
- },
148
- {
149
- _type: 'myTestBlockType',
150
- _key: 'b',
151
- style: 'normal',
152
- markDefs: [],
153
- children: [
154
- {
155
- _type: 'span',
156
- _key: 'b1',
157
- text: '',
158
- marks: [],
159
- },
160
- ],
161
- },
162
- {
163
- _key: 'c',
164
- _type: 'someObject',
165
- },
166
- ]
167
-
168
- const initialSelection = {
169
- focus: {path: [{_key: 'b'}, 'children', {_key: 'b1'}], offset: 0},
170
- anchor: {path: [{_key: 'b'}, 'children', {_key: 'b1'}], offset: 0},
171
- }
172
-
173
- const editorRef: RefObject<PortableTextEditor | null> = createRef()
174
- const onChange = vi.fn()
175
- const component = render(
176
- <PortableTextEditorTester
177
- onChange={onChange}
178
- ref={editorRef}
179
- schemaType={schemaType}
180
- value={initialValue}
181
- />,
182
- )
183
-
184
- await waitFor(() => {
185
- if (editorRef.current) {
186
- expect(onChange).toHaveBeenCalledWith({
187
- type: 'value',
188
- value: initialValue,
189
- })
190
- expect(onChange).toHaveBeenCalledWith({type: 'ready'})
191
- }
192
- })
193
-
194
- const element = await getEditableElement(component)
195
-
196
- await waitFor(() => {
197
- if (editorRef.current && element) {
198
- PortableTextEditor.focus(editorRef.current)
199
- PortableTextEditor.select(editorRef.current, initialSelection)
200
- fireEvent.click(element)
201
- expect(PortableTextEditor.getValue(editorRef.current)).toEqual(
202
- initialValue,
203
- )
204
- }
205
- })
206
- })
207
-
208
- it('should not add blocks if the last element is void, and its focused on that one when clicking', async () => {
209
- const initialValue = [
210
- {
211
- _key: 'a',
212
- _type: 'someObject',
213
- },
214
- {
215
- _type: 'myTestBlockType',
216
- _key: 'b',
217
- style: 'normal',
218
- markDefs: [],
219
- children: [
220
- {
221
- _type: 'span',
222
- _key: 'b1',
223
- text: '',
224
- marks: [],
225
- },
226
- ],
227
- },
228
- {
229
- _key: 'c',
230
- _type: 'someObject',
231
- },
232
- ]
233
-
234
- const initialSelection = {
235
- focus: {path: [{_key: 'c'}], offset: 0},
236
- anchor: {path: [{_key: 'c'}], offset: 0},
237
- }
238
-
239
- const editorRef: RefObject<PortableTextEditor | null> = createRef()
240
- const onChange = vi.fn()
241
- const component = render(
242
- <PortableTextEditorTester
243
- onChange={onChange}
244
- ref={editorRef}
245
- schemaType={schemaType}
246
- value={initialValue}
247
- />,
248
- )
249
-
250
- await waitFor(() => {
251
- if (editorRef.current) {
252
- expect(onChange).toHaveBeenCalledWith({
253
- type: 'value',
254
- value: initialValue,
255
- })
256
- expect(onChange).toHaveBeenCalledWith({type: 'ready'})
257
- }
258
- })
259
-
260
- const element = await getEditableElement(component)
261
-
262
- const editor = editorRef.current
263
- const inlineType = editor?.schemaTypes.inlineObjects.find(
264
- (t) => t.name === 'someObject',
265
- )
266
- await waitFor(() => {
267
- if (editor && inlineType && element) {
268
- PortableTextEditor.focus(editor)
269
- PortableTextEditor.select(editor, initialSelection)
270
- fireEvent.click(element)
271
- expect(PortableTextEditor.getValue(editor)).toEqual(
272
- initialValue.concat(newBlock),
273
- )
274
- }
275
- })
276
- })
277
- })
@@ -1,135 +0,0 @@
1
- import type React from 'react'
2
- import {useCallback, useEffect, useState, type DragEvent} from 'react'
3
- import type {Element as SlateElement} from 'slate'
4
- import {ReactEditor, useSlateStatic} from 'slate-react'
5
- import {debugWithName} from '../../internal-utils/debug'
6
- import {
7
- IS_DRAGGING_BLOCK_ELEMENT,
8
- IS_DRAGGING_BLOCK_TARGET_POSITION,
9
- IS_DRAGGING_ELEMENT_TARGET,
10
- } from '../../internal-utils/weakMaps'
11
-
12
- const debug = debugWithName('useDroppable')
13
-
14
- type Droppable = {
15
- droppableProps: {
16
- onDragOver?: (event: DragEvent) => void
17
- onDragLeave?: () => void
18
- onDrop?: (event: DragEvent) => void
19
- }
20
- isDraggingOverTop: boolean
21
- isDraggingOverBottom: boolean
22
- }
23
-
24
- export function useDroppable(props: {
25
- element: SlateElement
26
- blockRef: React.RefObject<HTMLDivElement | null>
27
- readOnly: boolean
28
- }): Droppable {
29
- const editor = useSlateStatic()
30
- const [isDragOver, setIsDragOver] = useState(false)
31
- const [blockElement, setBlockElement] = useState<HTMLElement | null>(null)
32
-
33
- useEffect(
34
- () =>
35
- setBlockElement(
36
- props.blockRef
37
- ? props.blockRef.current
38
- : ReactEditor.toDOMNode(editor, props.element),
39
- ),
40
- [editor, props.element, props.blockRef],
41
- )
42
-
43
- const handleDragOver = useCallback(
44
- (event: DragEvent) => {
45
- const isMyDragOver = IS_DRAGGING_BLOCK_ELEMENT.get(editor)
46
- // debug('Drag over', blockElement)
47
- if (!isMyDragOver || !blockElement) {
48
- return
49
- }
50
- event.preventDefault()
51
- event.dataTransfer.dropEffect = 'move'
52
- IS_DRAGGING_ELEMENT_TARGET.set(editor, props.element)
53
- const elementRect = blockElement.getBoundingClientRect()
54
- const offset = elementRect.top
55
- const height = elementRect.height
56
- const Y = event.pageY
57
- const loc = Math.abs(offset - Y)
58
- let position: 'top' | 'bottom' = 'bottom'
59
- if (props.element === editor.children[0]) {
60
- position = 'top'
61
- } else if (loc < height / 2) {
62
- position = 'top'
63
- IS_DRAGGING_BLOCK_TARGET_POSITION.set(editor, position)
64
- } else {
65
- position = 'bottom'
66
- IS_DRAGGING_BLOCK_TARGET_POSITION.set(editor, position)
67
- }
68
- if (isMyDragOver === props.element) {
69
- event.dataTransfer.dropEffect = 'none'
70
- return
71
- }
72
- setIsDragOver(true)
73
- },
74
- [blockElement, editor, props.element],
75
- )
76
-
77
- const handleDragLeave = useCallback(() => {
78
- setIsDragOver(false)
79
- }, [])
80
-
81
- const handleDrop = useCallback(
82
- (event: DragEvent) => {
83
- if (IS_DRAGGING_BLOCK_ELEMENT.get(editor)) {
84
- debug('On drop (prevented)', props.element)
85
- event.preventDefault()
86
- event.stopPropagation()
87
- setIsDragOver(false)
88
- }
89
- },
90
- [editor, props.element],
91
- )
92
-
93
- const isDraggingOverFirstBlock =
94
- isDragOver && editor.children[0] === IS_DRAGGING_ELEMENT_TARGET.get(editor)
95
- const isDraggingOverLastBlock =
96
- isDragOver &&
97
- editor.children[editor.children.length - 1] ===
98
- IS_DRAGGING_ELEMENT_TARGET.get(editor)
99
- const dragPosition = IS_DRAGGING_BLOCK_TARGET_POSITION.get(editor)
100
-
101
- const isDraggingOverTop =
102
- isDraggingOverFirstBlock ||
103
- (isDragOver &&
104
- !isDraggingOverFirstBlock &&
105
- !isDraggingOverLastBlock &&
106
- dragPosition === 'top')
107
- const isDraggingOverBottom =
108
- isDraggingOverLastBlock ||
109
- (isDragOver &&
110
- !isDraggingOverFirstBlock &&
111
- !isDraggingOverLastBlock &&
112
- dragPosition === 'bottom')
113
-
114
- if (props.readOnly) {
115
- return {
116
- droppableProps: {
117
- onDragOver: undefined,
118
- onDragLeave: undefined,
119
- onDrop: undefined,
120
- },
121
- isDraggingOverTop: false,
122
- isDraggingOverBottom: false,
123
- }
124
- }
125
-
126
- return {
127
- droppableProps: {
128
- onDragOver: handleDragOver,
129
- onDragLeave: handleDragLeave,
130
- onDrop: handleDrop,
131
- },
132
- isDraggingOverTop,
133
- isDraggingOverBottom,
134
- }
135
- }