@portabletext/editor 1.0.19 → 1.1.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.
- package/lib/index.d.mts +140 -66
- package/lib/index.d.ts +140 -66
- package/lib/index.esm.js +1125 -362
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +1125 -362
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +1125 -362
- package/lib/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/editor/Editable.tsx +107 -36
- package/src/editor/PortableTextEditor.tsx +47 -12
- package/src/editor/__tests__/PortableTextEditor.test.tsx +42 -15
- package/src/editor/__tests__/PortableTextEditorTester.tsx +50 -38
- package/src/editor/__tests__/RangeDecorations.test.tsx +0 -1
- package/src/editor/__tests__/handleClick.test.tsx +28 -9
- package/src/editor/__tests__/insert-block.test.tsx +22 -6
- package/src/editor/__tests__/pteWarningsSelfSolving.test.tsx +30 -62
- package/src/editor/__tests__/utils.ts +10 -3
- package/src/editor/components/DraggableBlock.tsx +36 -13
- package/src/editor/components/Element.tsx +59 -17
- package/src/editor/components/Leaf.tsx +106 -68
- package/src/editor/components/SlateContainer.tsx +12 -5
- package/src/editor/components/Synchronizer.tsx +5 -2
- package/src/editor/hooks/usePortableTextEditor.ts +2 -2
- package/src/editor/hooks/usePortableTextEditorSelection.tsx +9 -3
- package/src/editor/hooks/useSyncValue.test.tsx +9 -4
- package/src/editor/hooks/useSyncValue.ts +199 -130
- package/src/editor/nodes/DefaultAnnotation.tsx +6 -3
- package/src/editor/plugins/__tests__/createWithInsertData.test.tsx +25 -7
- package/src/editor/plugins/__tests__/withEditableAPIDelete.test.tsx +26 -9
- package/src/editor/plugins/__tests__/withEditableAPIGetFragment.test.tsx +15 -5
- package/src/editor/plugins/__tests__/withEditableAPIInsert.test.tsx +60 -19
- package/src/editor/plugins/__tests__/withEditableAPISelectionsOverlapping.test.tsx +4 -2
- package/src/editor/plugins/__tests__/withPortableTextLists.test.tsx +4 -2
- package/src/editor/plugins/__tests__/withPortableTextMarkModel.test.tsx +61 -17
- package/src/editor/plugins/__tests__/withPortableTextSelections.test.tsx +6 -3
- package/src/editor/plugins/__tests__/withUndoRedo.test.tsx +30 -13
- package/src/editor/plugins/createWithEditableAPI.ts +354 -124
- package/src/editor/plugins/createWithHotKeys.ts +41 -121
- package/src/editor/plugins/createWithInsertBreak.ts +166 -27
- package/src/editor/plugins/createWithInsertData.ts +60 -23
- package/src/editor/plugins/createWithMaxBlocks.ts +5 -2
- package/src/editor/plugins/createWithObjectKeys.ts +7 -3
- package/src/editor/plugins/createWithPatches.ts +60 -16
- package/src/editor/plugins/createWithPlaceholderBlock.ts +7 -3
- package/src/editor/plugins/createWithPortableTextBlockStyle.ts +17 -7
- package/src/editor/plugins/createWithPortableTextLists.ts +21 -8
- package/src/editor/plugins/createWithPortableTextMarkModel.ts +213 -46
- package/src/editor/plugins/createWithPortableTextSelections.ts +4 -2
- package/src/editor/plugins/createWithSchemaTypes.ts +25 -9
- package/src/editor/plugins/createWithUndoRedo.ts +107 -24
- package/src/editor/plugins/createWithUtils.ts +32 -10
- package/src/editor/plugins/index.ts +31 -10
- package/src/types/editor.ts +44 -15
- package/src/types/options.ts +4 -2
- package/src/types/slate.ts +2 -2
- package/src/utils/__tests__/dmpToOperations.test.ts +38 -13
- package/src/utils/__tests__/operationToPatches.test.ts +3 -2
- package/src/utils/__tests__/patchToOperations.test.ts +15 -4
- package/src/utils/__tests__/ranges.test.ts +8 -3
- package/src/utils/__tests__/valueNormalization.test.tsx +12 -4
- package/src/utils/__tests__/values.test.ts +0 -1
- package/src/utils/applyPatch.ts +71 -20
- package/src/utils/getPortableTextMemberSchemaTypes.ts +30 -15
- package/src/utils/operationToPatches.ts +126 -43
- package/src/utils/paths.ts +24 -7
- package/src/utils/ranges.ts +12 -5
- package/src/utils/selection.ts +19 -7
- package/src/utils/validateValue.ts +118 -45
- package/src/utils/values.ts +31 -9
- package/src/utils/weakMaps.ts +18 -8
- package/src/utils/withChanges.ts +4 -2
|
@@ -1,17 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type Path,
|
|
3
|
+
type PortableTextObject,
|
|
4
|
+
type PortableTextTextBlock,
|
|
5
|
+
} from '@sanity/types'
|
|
2
6
|
import {isEqual, uniq} from 'lodash'
|
|
3
7
|
import {
|
|
4
|
-
type ReactElement,
|
|
5
8
|
startTransition,
|
|
6
9
|
useCallback,
|
|
7
10
|
useEffect,
|
|
8
11
|
useMemo,
|
|
9
12
|
useRef,
|
|
10
13
|
useState,
|
|
14
|
+
type ReactElement,
|
|
11
15
|
} from 'react'
|
|
12
16
|
import {Text} from 'slate'
|
|
13
|
-
import {type RenderLeafProps
|
|
14
|
-
|
|
17
|
+
import {useSelected, type RenderLeafProps} from 'slate-react'
|
|
15
18
|
import {
|
|
16
19
|
type BlockAnnotationRenderProps,
|
|
17
20
|
type BlockChildRenderProps,
|
|
@@ -47,8 +50,15 @@ export interface LeafProps extends RenderLeafProps {
|
|
|
47
50
|
* @internal
|
|
48
51
|
*/
|
|
49
52
|
export const Leaf = (props: LeafProps) => {
|
|
50
|
-
const {
|
|
51
|
-
|
|
53
|
+
const {
|
|
54
|
+
attributes,
|
|
55
|
+
children,
|
|
56
|
+
leaf,
|
|
57
|
+
schemaTypes,
|
|
58
|
+
renderChild,
|
|
59
|
+
renderDecorator,
|
|
60
|
+
renderAnnotation,
|
|
61
|
+
} = props
|
|
52
62
|
const spanRef = useRef<HTMLElement>(null)
|
|
53
63
|
const portableTextEditor = usePortableTextEditor()
|
|
54
64
|
const blockSelected = useSelected()
|
|
@@ -64,7 +74,12 @@ export const Leaf = (props: LeafProps) => {
|
|
|
64
74
|
[schemaTypes.decorators],
|
|
65
75
|
)
|
|
66
76
|
const marks: string[] = useMemo(
|
|
67
|
-
() =>
|
|
77
|
+
() =>
|
|
78
|
+
uniq(
|
|
79
|
+
(leaf.marks || EMPTY_MARKS).filter((mark) =>
|
|
80
|
+
decoratorValues.includes(mark),
|
|
81
|
+
),
|
|
82
|
+
),
|
|
68
83
|
[decoratorValues, leaf.marks],
|
|
69
84
|
)
|
|
70
85
|
const annotationMarks = Array.isArray(leaf.marks) ? leaf.marks : EMPTY_MARKS
|
|
@@ -73,7 +88,8 @@ export const Leaf = (props: LeafProps) => {
|
|
|
73
88
|
annotationMarks
|
|
74
89
|
.map(
|
|
75
90
|
(mark) =>
|
|
76
|
-
!decoratorValues.includes(mark) &&
|
|
91
|
+
!decoratorValues.includes(mark) &&
|
|
92
|
+
block?.markDefs?.find((def) => def._key === mark),
|
|
77
93
|
)
|
|
78
94
|
.filter(Boolean) as PortableTextObject[],
|
|
79
95
|
[annotationMarks, block, decoratorValues],
|
|
@@ -159,7 +175,12 @@ export const Leaf = (props: LeafProps) => {
|
|
|
159
175
|
return () => {
|
|
160
176
|
sub.unsubscribe()
|
|
161
177
|
}
|
|
162
|
-
}, [
|
|
178
|
+
}, [
|
|
179
|
+
path,
|
|
180
|
+
portableTextEditor,
|
|
181
|
+
setSelectedFromRange,
|
|
182
|
+
shouldTrackSelectionAndFocus,
|
|
183
|
+
])
|
|
163
184
|
|
|
164
185
|
useEffect(() => setSelectedFromRange(), [setSelectedFromRange])
|
|
165
186
|
|
|
@@ -168,59 +189,73 @@ export const Leaf = (props: LeafProps) => {
|
|
|
168
189
|
// Render text nodes
|
|
169
190
|
if (Text.isText(leaf) && leaf._type === schemaTypes.span.name) {
|
|
170
191
|
marks.forEach((mark) => {
|
|
171
|
-
const schemaType = schemaTypes.decorators.find(
|
|
192
|
+
const schemaType = schemaTypes.decorators.find(
|
|
193
|
+
(dec) => dec.value === mark,
|
|
194
|
+
)
|
|
172
195
|
if (schemaType && renderDecorator) {
|
|
173
|
-
const _props: Omit<BlockDecoratorRenderProps, 'type'> =
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
196
|
+
const _props: Omit<BlockDecoratorRenderProps, 'type'> =
|
|
197
|
+
Object.defineProperty(
|
|
198
|
+
{
|
|
199
|
+
children: returnedChildren,
|
|
200
|
+
editorElementRef: spanRef,
|
|
201
|
+
focused,
|
|
202
|
+
path,
|
|
203
|
+
selected,
|
|
204
|
+
schemaType,
|
|
205
|
+
value: mark,
|
|
206
|
+
},
|
|
207
|
+
'type',
|
|
208
|
+
{
|
|
209
|
+
enumerable: false,
|
|
210
|
+
get() {
|
|
211
|
+
console.warn(
|
|
212
|
+
"Property 'type' is deprecated, use 'schemaType' instead.",
|
|
213
|
+
)
|
|
214
|
+
return schemaType
|
|
215
|
+
},
|
|
189
216
|
},
|
|
190
|
-
|
|
217
|
+
)
|
|
218
|
+
returnedChildren = renderDecorator(
|
|
219
|
+
_props as BlockDecoratorRenderProps,
|
|
191
220
|
)
|
|
192
|
-
returnedChildren = renderDecorator(_props as BlockDecoratorRenderProps)
|
|
193
221
|
}
|
|
194
222
|
})
|
|
195
223
|
|
|
196
224
|
if (block && annotations.length > 0) {
|
|
197
225
|
annotations.forEach((annotation) => {
|
|
198
|
-
const schemaType = schemaTypes.annotations.find(
|
|
226
|
+
const schemaType = schemaTypes.annotations.find(
|
|
227
|
+
(t) => t.name === annotation._type,
|
|
228
|
+
)
|
|
199
229
|
if (schemaType) {
|
|
200
230
|
if (renderAnnotation) {
|
|
201
|
-
const _props: Omit<BlockAnnotationRenderProps, 'type'> =
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
'type',
|
|
213
|
-
{
|
|
214
|
-
enumerable: false,
|
|
215
|
-
get() {
|
|
216
|
-
console.warn("Property 'type' is deprecated, use 'schemaType' instead.")
|
|
217
|
-
return schemaType
|
|
231
|
+
const _props: Omit<BlockAnnotationRenderProps, 'type'> =
|
|
232
|
+
Object.defineProperty(
|
|
233
|
+
{
|
|
234
|
+
block,
|
|
235
|
+
children: returnedChildren,
|
|
236
|
+
editorElementRef: spanRef,
|
|
237
|
+
focused,
|
|
238
|
+
path,
|
|
239
|
+
selected,
|
|
240
|
+
schemaType,
|
|
241
|
+
value: annotation,
|
|
218
242
|
},
|
|
219
|
-
|
|
220
|
-
|
|
243
|
+
'type',
|
|
244
|
+
{
|
|
245
|
+
enumerable: false,
|
|
246
|
+
get() {
|
|
247
|
+
console.warn(
|
|
248
|
+
"Property 'type' is deprecated, use 'schemaType' instead.",
|
|
249
|
+
)
|
|
250
|
+
return schemaType
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
)
|
|
221
254
|
|
|
222
255
|
returnedChildren = (
|
|
223
|
-
<span ref={spanRef}>
|
|
256
|
+
<span ref={spanRef}>
|
|
257
|
+
{renderAnnotation(_props as BlockAnnotationRenderProps)}
|
|
258
|
+
</span>
|
|
224
259
|
)
|
|
225
260
|
} else {
|
|
226
261
|
returnedChildren = (
|
|
@@ -236,26 +271,29 @@ export const Leaf = (props: LeafProps) => {
|
|
|
236
271
|
const child = block.children.find((_child) => _child._key === leaf._key) // Ensure object equality
|
|
237
272
|
if (child) {
|
|
238
273
|
const defaultRendered = <>{returnedChildren}</>
|
|
239
|
-
const _props: Omit<BlockChildRenderProps, 'type'> =
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
'type',
|
|
251
|
-
{
|
|
252
|
-
enumerable: false,
|
|
253
|
-
get() {
|
|
254
|
-
console.warn("Property 'type' is deprecated, use 'schemaType' instead.")
|
|
255
|
-
return schemaTypes.span
|
|
274
|
+
const _props: Omit<BlockChildRenderProps, 'type'> =
|
|
275
|
+
Object.defineProperty(
|
|
276
|
+
{
|
|
277
|
+
annotations,
|
|
278
|
+
children: defaultRendered,
|
|
279
|
+
editorElementRef: spanRef,
|
|
280
|
+
focused,
|
|
281
|
+
path,
|
|
282
|
+
schemaType: schemaTypes.span,
|
|
283
|
+
selected,
|
|
284
|
+
value: child,
|
|
256
285
|
},
|
|
257
|
-
|
|
258
|
-
|
|
286
|
+
'type',
|
|
287
|
+
{
|
|
288
|
+
enumerable: false,
|
|
289
|
+
get() {
|
|
290
|
+
console.warn(
|
|
291
|
+
"Property 'type' is deprecated, use 'schemaType' instead.",
|
|
292
|
+
)
|
|
293
|
+
return schemaTypes.span
|
|
294
|
+
},
|
|
295
|
+
},
|
|
296
|
+
)
|
|
259
297
|
returnedChildren = renderChild(_props as BlockChildRenderProps)
|
|
260
298
|
}
|
|
261
299
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {useEffect, useMemo, useState, type PropsWithChildren} from 'react'
|
|
2
2
|
import {createEditor} from 'slate'
|
|
3
3
|
import {Slate, withReact} from 'slate-react'
|
|
4
|
-
|
|
5
4
|
import {type PatchObservable} from '../../types/editor'
|
|
6
5
|
import {debugWithName} from '../../utils/debug'
|
|
7
6
|
import {KEY_TO_SLATE_ELEMENT, KEY_TO_VALUE_ELEMENT} from '../../utils/weakMaps'
|
|
@@ -26,7 +25,8 @@ export interface SlateContainerProps extends PropsWithChildren {
|
|
|
26
25
|
* @internal
|
|
27
26
|
*/
|
|
28
27
|
export function SlateContainer(props: SlateContainerProps) {
|
|
29
|
-
const {patches$, portableTextEditor, readOnly, maxBlocks, keyGenerator} =
|
|
28
|
+
const {patches$, portableTextEditor, readOnly, maxBlocks, keyGenerator} =
|
|
29
|
+
props
|
|
30
30
|
|
|
31
31
|
// Create the slate instance, using `useState` ensures setup is only run once, initially
|
|
32
32
|
const [[slateEditor, subscribe]] = useState(() => {
|
|
@@ -60,10 +60,17 @@ export function SlateContainer(props: SlateContainerProps) {
|
|
|
60
60
|
portableTextEditor,
|
|
61
61
|
readOnly,
|
|
62
62
|
})
|
|
63
|
-
}, [
|
|
63
|
+
}, [
|
|
64
|
+
keyGenerator,
|
|
65
|
+
portableTextEditor,
|
|
66
|
+
maxBlocks,
|
|
67
|
+
readOnly,
|
|
68
|
+
patches$,
|
|
69
|
+
slateEditor,
|
|
70
|
+
])
|
|
64
71
|
|
|
65
72
|
const initialValue = useMemo(() => {
|
|
66
|
-
return [slateEditor.
|
|
73
|
+
return [slateEditor.pteCreateTextBlock({decorators: []})]
|
|
67
74
|
}, [slateEditor])
|
|
68
75
|
|
|
69
76
|
useEffect(() => {
|
|
@@ -4,7 +4,6 @@ import {throttle} from 'lodash'
|
|
|
4
4
|
import {useCallback, useEffect, useMemo, useRef} from 'react'
|
|
5
5
|
import {Editor} from 'slate'
|
|
6
6
|
import {useSlate} from 'slate-react'
|
|
7
|
-
|
|
8
7
|
import {type EditorChange, type EditorChanges} from '../../types/editor'
|
|
9
8
|
import {debugWithName} from '../../utils/debug'
|
|
10
9
|
import {IS_PROCESSING_LOCAL_CHANGES} from '../../utils/weakMaps'
|
|
@@ -61,7 +60,11 @@ export function Synchronizer(props: SynchronizerProps) {
|
|
|
61
60
|
debug(`Patches:\n${JSON.stringify(pendingPatches.current, null, 2)}`)
|
|
62
61
|
}
|
|
63
62
|
const snapshot = getValue()
|
|
64
|
-
change$.next({
|
|
63
|
+
change$.next({
|
|
64
|
+
type: 'mutation',
|
|
65
|
+
patches: pendingPatches.current,
|
|
66
|
+
snapshot,
|
|
67
|
+
})
|
|
65
68
|
pendingPatches.current = []
|
|
66
69
|
}
|
|
67
70
|
IS_PROCESSING_LOCAL_CHANGES.set(slateEditor, false)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {createContext, useContext} from 'react'
|
|
2
|
-
|
|
3
2
|
import {type PortableTextEditor} from '../PortableTextEditor'
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* A React context for sharing the editor object.
|
|
7
6
|
*/
|
|
8
|
-
export const PortableTextEditorContext =
|
|
7
|
+
export const PortableTextEditorContext =
|
|
8
|
+
createContext<PortableTextEditor | null>(null)
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* @public
|
|
@@ -1,12 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
createContext,
|
|
3
|
+
startTransition,
|
|
4
|
+
useContext,
|
|
5
|
+
useEffect,
|
|
6
|
+
useState,
|
|
7
|
+
} from 'react'
|
|
3
8
|
import {type EditorChanges, type EditorSelection} from '../../types/editor'
|
|
4
9
|
import {debugWithName} from '../../utils/debug'
|
|
5
10
|
|
|
6
11
|
/**
|
|
7
12
|
* A React context for sharing the editor selection.
|
|
8
13
|
*/
|
|
9
|
-
const PortableTextEditorSelectionContext =
|
|
14
|
+
const PortableTextEditorSelectionContext =
|
|
15
|
+
createContext<EditorSelection | null>(null)
|
|
10
16
|
|
|
11
17
|
/**
|
|
12
18
|
* @public
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import {describe, expect, it, jest} from '@jest/globals'
|
|
2
2
|
import {render, waitFor} from '@testing-library/react'
|
|
3
3
|
import {createRef, type RefObject} from 'react'
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
import {
|
|
5
|
+
PortableTextEditorTester,
|
|
6
|
+
schemaType,
|
|
7
|
+
} from '../__tests__/PortableTextEditorTester'
|
|
6
8
|
import {PortableTextEditor} from '../PortableTextEditor'
|
|
7
9
|
|
|
8
10
|
const initialValue = [
|
|
@@ -60,7 +62,9 @@ describe('useSyncValue', () => {
|
|
|
60
62
|
)
|
|
61
63
|
await waitFor(() => {
|
|
62
64
|
if (editorRef.current) {
|
|
63
|
-
expect(PortableTextEditor.getValue(editorRef.current)).toEqual(
|
|
65
|
+
expect(PortableTextEditor.getValue(editorRef.current)).toEqual(
|
|
66
|
+
syncedValue,
|
|
67
|
+
)
|
|
64
68
|
}
|
|
65
69
|
})
|
|
66
70
|
})
|
|
@@ -101,7 +105,8 @@ describe('useSyncValue', () => {
|
|
|
101
105
|
)
|
|
102
106
|
await waitFor(() => {
|
|
103
107
|
if (editorRef.current) {
|
|
104
|
-
expect(PortableTextEditor.getValue(editorRef.current))
|
|
108
|
+
expect(PortableTextEditor.getValue(editorRef.current))
|
|
109
|
+
.toMatchInlineSnapshot(`
|
|
105
110
|
Array [
|
|
106
111
|
Object {
|
|
107
112
|
"_key": "77071c3af231",
|