@portabletext/editor 1.0.18 → 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 +1164 -410
- package/lib/index.esm.js.map +1 -1
- package/lib/index.js +1164 -410
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +1164 -410
- package/lib/index.mjs.map +1 -1
- package/package.json +8 -4
- 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 -550
- 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 -115
- 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 +301 -155
- 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 -10
- package/src/utils/weakMaps.ts +18 -8
- package/src/utils/withChanges.ts +4 -2
- package/src/editor/plugins/__tests__/withHotkeys.test.tsx +0 -212
- package/src/editor/plugins/__tests__/withInsertBreak.test.tsx +0 -220
- package/src/editor/plugins/__tests__/withPlaceholderBlock.test.tsx +0 -133
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@portabletext/editor",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Portable Text Editor made in React",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sanity",
|
|
@@ -50,6 +50,9 @@
|
|
|
50
50
|
"slate-react": "0.108.0"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
+
"@cucumber/cucumber-expressions": "^17.1.0",
|
|
54
|
+
"@cucumber/gherkin": "^29.0.0",
|
|
55
|
+
"@cucumber/messages": "^26.0.0",
|
|
53
56
|
"@jest/globals": "^29.7.0",
|
|
54
57
|
"@jest/types": "^29.6.3",
|
|
55
58
|
"@playwright/test": "1.46.1",
|
|
@@ -100,6 +103,7 @@
|
|
|
100
103
|
"react-dom": "^18.3.1",
|
|
101
104
|
"rxjs": "^7.8.1",
|
|
102
105
|
"styled-components": "^6.1.12",
|
|
106
|
+
"ts-node": "^10.9.2",
|
|
103
107
|
"tsx": "^4.17.0",
|
|
104
108
|
"typescript": "5.5.4",
|
|
105
109
|
"vite": "^5.4.2"
|
|
@@ -122,14 +126,14 @@
|
|
|
122
126
|
"scripts": {
|
|
123
127
|
"build": "pkg-utils build --strict --check --clean",
|
|
124
128
|
"check:lint": "eslint .",
|
|
125
|
-
"check:types": "tsc
|
|
129
|
+
"check:types": "tsc",
|
|
126
130
|
"clean": "del .turbo && del lib && del node_modules",
|
|
127
131
|
"dev": "pkg-utils watch",
|
|
128
132
|
"dev:e2e-server": "cd ./e2e-tests/ && tsx serve",
|
|
129
133
|
"lint:fix": "eslint . --fix",
|
|
130
134
|
"test": "jest",
|
|
131
|
-
"test:e2e": "jest --config=e2e-tests/e2e.config.
|
|
132
|
-
"test:e2e:watch": "jest --config=e2e-tests/e2e.config.
|
|
135
|
+
"test:e2e": "jest --config=e2e-tests/e2e.config.ts",
|
|
136
|
+
"test:e2e:watch": "jest --config=e2e-tests/e2e.config.ts --watch",
|
|
133
137
|
"test:watch": "jest --watch"
|
|
134
138
|
}
|
|
135
139
|
}
|
package/src/editor/Editable.tsx
CHANGED
|
@@ -1,41 +1,40 @@
|
|
|
1
1
|
import {type PortableTextBlock} from '@sanity/types'
|
|
2
2
|
import {isEqual, noop} from 'lodash'
|
|
3
3
|
import {
|
|
4
|
+
forwardRef,
|
|
5
|
+
useCallback,
|
|
6
|
+
useEffect,
|
|
7
|
+
useImperativeHandle,
|
|
8
|
+
useMemo,
|
|
9
|
+
useRef,
|
|
10
|
+
useState,
|
|
4
11
|
type ClipboardEvent,
|
|
5
12
|
type CSSProperties,
|
|
6
13
|
type FocusEventHandler,
|
|
7
14
|
type ForwardedRef,
|
|
8
|
-
forwardRef,
|
|
9
15
|
type HTMLProps,
|
|
10
16
|
type KeyboardEvent,
|
|
11
17
|
type MutableRefObject,
|
|
12
18
|
type TextareaHTMLAttributes,
|
|
13
|
-
useCallback,
|
|
14
|
-
useEffect,
|
|
15
|
-
useImperativeHandle,
|
|
16
|
-
useMemo,
|
|
17
|
-
useRef,
|
|
18
|
-
useState,
|
|
19
19
|
} from 'react'
|
|
20
20
|
import {
|
|
21
|
-
type BaseRange,
|
|
22
21
|
Editor,
|
|
23
22
|
Node,
|
|
24
|
-
type NodeEntry,
|
|
25
|
-
type Operation,
|
|
26
23
|
Path,
|
|
27
24
|
Range as SlateRange,
|
|
28
|
-
type Text,
|
|
29
25
|
Transforms,
|
|
26
|
+
type BaseRange,
|
|
27
|
+
type NodeEntry,
|
|
28
|
+
type Operation,
|
|
29
|
+
type Text,
|
|
30
30
|
} from 'slate'
|
|
31
31
|
import {
|
|
32
|
-
Editable as SlateEditable,
|
|
33
32
|
ReactEditor,
|
|
33
|
+
Editable as SlateEditable,
|
|
34
|
+
useSlate,
|
|
34
35
|
type RenderElementProps,
|
|
35
36
|
type RenderLeafProps,
|
|
36
|
-
useSlate,
|
|
37
37
|
} from 'slate-react'
|
|
38
|
-
|
|
39
38
|
import {
|
|
40
39
|
type EditorChange,
|
|
41
40
|
type EditorSelection,
|
|
@@ -54,9 +53,17 @@ import {
|
|
|
54
53
|
import {type HotkeyOptions} from '../types/options'
|
|
55
54
|
import {type SlateTextBlock, type VoidElement} from '../types/slate'
|
|
56
55
|
import {debugWithName} from '../utils/debug'
|
|
57
|
-
import {
|
|
56
|
+
import {
|
|
57
|
+
moveRangeByOperation,
|
|
58
|
+
toPortableTextRange,
|
|
59
|
+
toSlateRange,
|
|
60
|
+
} from '../utils/ranges'
|
|
58
61
|
import {normalizeSelection} from '../utils/selection'
|
|
59
|
-
import {
|
|
62
|
+
import {
|
|
63
|
+
fromSlateValue,
|
|
64
|
+
isEqualToEmptyEditor,
|
|
65
|
+
toSlateValue,
|
|
66
|
+
} from '../utils/values'
|
|
60
67
|
import {Element} from './components/Element'
|
|
61
68
|
import {Leaf} from './components/Leaf'
|
|
62
69
|
import {usePortableTextEditor} from './hooks/usePortableTextEditor'
|
|
@@ -138,12 +145,19 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(
|
|
|
138
145
|
const readOnly = usePortableTextEditorReadOnlyStatus()
|
|
139
146
|
const keyGenerator = usePortableTextEditorKeyGenerator()
|
|
140
147
|
const ref = useRef<HTMLDivElement | null>(null)
|
|
141
|
-
const [editableElement, setEditableElement] = useState<HTMLDivElement | null>(
|
|
148
|
+
const [editableElement, setEditableElement] = useState<HTMLDivElement | null>(
|
|
149
|
+
null,
|
|
150
|
+
)
|
|
142
151
|
const [hasInvalidValue, setHasInvalidValue] = useState(false)
|
|
143
|
-
const [rangeDecorationState, setRangeDecorationsState] = useState<
|
|
152
|
+
const [rangeDecorationState, setRangeDecorationsState] = useState<
|
|
153
|
+
BaseRangeWithDecoration[]
|
|
154
|
+
>([])
|
|
144
155
|
|
|
145
156
|
// Forward ref to parent component
|
|
146
|
-
useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(
|
|
157
|
+
useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(
|
|
158
|
+
forwardedRef,
|
|
159
|
+
() => ref.current,
|
|
160
|
+
)
|
|
147
161
|
|
|
148
162
|
const rangeDecorationsRef = useRef(rangeDecorations)
|
|
149
163
|
|
|
@@ -187,7 +201,15 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(
|
|
|
187
201
|
spellCheck={spellCheck}
|
|
188
202
|
/>
|
|
189
203
|
),
|
|
190
|
-
[
|
|
204
|
+
[
|
|
205
|
+
schemaTypes,
|
|
206
|
+
spellCheck,
|
|
207
|
+
readOnly,
|
|
208
|
+
renderBlock,
|
|
209
|
+
renderChild,
|
|
210
|
+
renderListItem,
|
|
211
|
+
renderStyle,
|
|
212
|
+
],
|
|
191
213
|
)
|
|
192
214
|
|
|
193
215
|
const renderLeaf = useCallback(
|
|
@@ -207,7 +229,11 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(
|
|
|
207
229
|
readOnly={readOnly}
|
|
208
230
|
/>
|
|
209
231
|
)
|
|
210
|
-
if (
|
|
232
|
+
if (
|
|
233
|
+
renderPlaceholder &&
|
|
234
|
+
lProps.leaf.placeholder &&
|
|
235
|
+
lProps.text.text === ''
|
|
236
|
+
) {
|
|
211
237
|
return (
|
|
212
238
|
<>
|
|
213
239
|
<span style={PLACEHOLDER_STYLE} contentEditable={false}>
|
|
@@ -225,7 +251,14 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(
|
|
|
225
251
|
}
|
|
226
252
|
return lProps.children
|
|
227
253
|
},
|
|
228
|
-
[
|
|
254
|
+
[
|
|
255
|
+
readOnly,
|
|
256
|
+
renderAnnotation,
|
|
257
|
+
renderChild,
|
|
258
|
+
renderDecorator,
|
|
259
|
+
renderPlaceholder,
|
|
260
|
+
schemaTypes,
|
|
261
|
+
],
|
|
229
262
|
)
|
|
230
263
|
|
|
231
264
|
const restoreSelectionFromProps = useCallback(() => {
|
|
@@ -236,7 +269,9 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(
|
|
|
236
269
|
fromSlateValue(slateEditor.children, blockTypeName),
|
|
237
270
|
)
|
|
238
271
|
if (normalizedSelection !== null) {
|
|
239
|
-
debug(
|
|
272
|
+
debug(
|
|
273
|
+
`Normalized selection from props ${JSON.stringify(normalizedSelection)}`,
|
|
274
|
+
)
|
|
240
275
|
const slateRange = toSlateRange(normalizedSelection, slateEditor)
|
|
241
276
|
if (slateRange) {
|
|
242
277
|
Transforms.select(slateEditor, slateRange)
|
|
@@ -256,7 +291,10 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(
|
|
|
256
291
|
if (rangeDecorations && rangeDecorations.length > 0) {
|
|
257
292
|
const newSlateRanges: BaseRangeWithDecoration[] = []
|
|
258
293
|
rangeDecorations.forEach((rangeDecorationItem) => {
|
|
259
|
-
const slateRange = toSlateRange(
|
|
294
|
+
const slateRange = toSlateRange(
|
|
295
|
+
rangeDecorationItem.selection,
|
|
296
|
+
slateEditor,
|
|
297
|
+
)
|
|
260
298
|
if (!SlateRange.isRange(slateRange)) {
|
|
261
299
|
if (rangeDecorationItem.onMoved) {
|
|
262
300
|
rangeDecorationItem.onMoved({
|
|
@@ -270,9 +308,16 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(
|
|
|
270
308
|
let newRange: BaseRange | null | undefined
|
|
271
309
|
if (operation) {
|
|
272
310
|
newRange = moveRangeByOperation(slateRange, operation)
|
|
273
|
-
if (
|
|
311
|
+
if (
|
|
312
|
+
(newRange && newRange !== slateRange) ||
|
|
313
|
+
(newRange === null && slateRange)
|
|
314
|
+
) {
|
|
274
315
|
const value = PortableTextEditor.getValue(portableTextEditor)
|
|
275
|
-
const newRangeSelection = toPortableTextRange(
|
|
316
|
+
const newRangeSelection = toPortableTextRange(
|
|
317
|
+
value,
|
|
318
|
+
newRange,
|
|
319
|
+
schemaTypes,
|
|
320
|
+
)
|
|
276
321
|
if (rangeDecorationItem.onMoved) {
|
|
277
322
|
rangeDecorationItem.onMoved({
|
|
278
323
|
newSelection: newRangeSelection,
|
|
@@ -285,7 +330,10 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(
|
|
|
285
330
|
// If the newRange is null, it means that the range is not valid anymore and should be removed
|
|
286
331
|
// If it's undefined, it means that the slateRange is still valid and should be kept
|
|
287
332
|
if (newRange !== null) {
|
|
288
|
-
newSlateRanges.push({
|
|
333
|
+
newSlateRanges.push({
|
|
334
|
+
...(newRange || slateRange),
|
|
335
|
+
rangeDecoration: rangeDecorationItem,
|
|
336
|
+
})
|
|
289
337
|
}
|
|
290
338
|
})
|
|
291
339
|
if (newSlateRanges.length > 0) {
|
|
@@ -388,7 +436,11 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(
|
|
|
388
436
|
}
|
|
389
437
|
|
|
390
438
|
const value = PortableTextEditor.getValue(portableTextEditor)
|
|
391
|
-
const ptRange = toPortableTextRange(
|
|
439
|
+
const ptRange = toPortableTextRange(
|
|
440
|
+
value,
|
|
441
|
+
slateEditor.selection,
|
|
442
|
+
schemaTypes,
|
|
443
|
+
)
|
|
392
444
|
const path = ptRange?.focus.path || []
|
|
393
445
|
const onPasteResult = onPaste({event, value, path, schemaTypes})
|
|
394
446
|
|
|
@@ -406,10 +458,15 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(
|
|
|
406
458
|
slateEditor.insertData(event.clipboardData)
|
|
407
459
|
} else if (result.insert) {
|
|
408
460
|
slateEditor.insertFragment(
|
|
409
|
-
toSlateValue(result.insert as PortableTextBlock[], {
|
|
461
|
+
toSlateValue(result.insert as PortableTextBlock[], {
|
|
462
|
+
schemaTypes,
|
|
463
|
+
}),
|
|
410
464
|
)
|
|
411
465
|
} else {
|
|
412
|
-
console.warn(
|
|
466
|
+
console.warn(
|
|
467
|
+
'Your onPaste function returned something unexpected:',
|
|
468
|
+
result,
|
|
469
|
+
)
|
|
413
470
|
}
|
|
414
471
|
})
|
|
415
472
|
.catch((error) => {
|
|
@@ -465,7 +522,10 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(
|
|
|
465
522
|
| SlateTextBlock
|
|
466
523
|
| VoidElement
|
|
467
524
|
if (lastBlock && Editor.isVoid(slateEditor, node)) {
|
|
468
|
-
Transforms.insertNodes(
|
|
525
|
+
Transforms.insertNodes(
|
|
526
|
+
slateEditor,
|
|
527
|
+
slateEditor.pteCreateTextBlock({decorators: []}),
|
|
528
|
+
)
|
|
469
529
|
slateEditor.onChange()
|
|
470
530
|
}
|
|
471
531
|
}
|
|
@@ -528,7 +588,10 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(
|
|
|
528
588
|
}
|
|
529
589
|
const existingDOMRange = domSelection.getRangeAt(0)
|
|
530
590
|
try {
|
|
531
|
-
const newDOMRange = ReactEditor.toDOMRange(
|
|
591
|
+
const newDOMRange = ReactEditor.toDOMRange(
|
|
592
|
+
slateEditor,
|
|
593
|
+
slateEditor.selection,
|
|
594
|
+
)
|
|
532
595
|
if (
|
|
533
596
|
newDOMRange.startOffset !== existingDOMRange.startOffset ||
|
|
534
597
|
newDOMRange.endOffset !== existingDOMRange.endOffset
|
|
@@ -625,12 +688,17 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(
|
|
|
625
688
|
if (path.length !== 2) {
|
|
626
689
|
return false
|
|
627
690
|
}
|
|
628
|
-
return
|
|
691
|
+
return (
|
|
692
|
+
Path.equals(item.focus.path, path) &&
|
|
693
|
+
Path.equals(item.anchor.path, path)
|
|
694
|
+
)
|
|
629
695
|
}
|
|
630
696
|
// Include decorations that either include or intersects with this path
|
|
631
697
|
return (
|
|
632
|
-
SlateRange.intersection(item, {
|
|
633
|
-
|
|
698
|
+
SlateRange.intersection(item, {
|
|
699
|
+
anchor: {path, offset: 0},
|
|
700
|
+
focus: {path, offset: 0},
|
|
701
|
+
}) || SlateRange.includes(item, path)
|
|
634
702
|
)
|
|
635
703
|
})
|
|
636
704
|
if (result.length > 0) {
|
|
@@ -645,7 +713,10 @@ export const PortableTextEditable = forwardRef(function PortableTextEditable(
|
|
|
645
713
|
// Also set the editable element in a state so that the MutationObserver
|
|
646
714
|
// is setup when this element is ready.
|
|
647
715
|
useEffect(() => {
|
|
648
|
-
ref.current = ReactEditor.toDOMNode(
|
|
716
|
+
ref.current = ReactEditor.toDOMNode(
|
|
717
|
+
slateEditor,
|
|
718
|
+
slateEditor,
|
|
719
|
+
) as HTMLDivElement | null
|
|
649
720
|
setEditableElement(ref.current)
|
|
650
721
|
}, [slateEditor, ref])
|
|
651
722
|
|
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
} from '@sanity/types'
|
|
12
12
|
import {Component, type MutableRefObject, type PropsWithChildren} from 'react'
|
|
13
13
|
import {Subject} from 'rxjs'
|
|
14
|
-
|
|
15
14
|
import {
|
|
16
15
|
type EditableAPI,
|
|
17
16
|
type EditableAPIDeleteOptions,
|
|
@@ -114,7 +113,9 @@ export class PortableTextEditor extends Component<PortableTextEditorProps> {
|
|
|
114
113
|
}
|
|
115
114
|
|
|
116
115
|
if (props.incomingPatches$) {
|
|
117
|
-
console.warn(
|
|
116
|
+
console.warn(
|
|
117
|
+
`The prop 'incomingPatches$' is deprecated and renamed to 'patches$'`,
|
|
118
|
+
)
|
|
118
119
|
}
|
|
119
120
|
|
|
120
121
|
this.change$.next({type: 'loading', isLoading: true})
|
|
@@ -192,20 +193,39 @@ export class PortableTextEditor extends Component<PortableTextEditorProps> {
|
|
|
192
193
|
}
|
|
193
194
|
|
|
194
195
|
// Static API methods
|
|
195
|
-
static activeAnnotations = (
|
|
196
|
+
static activeAnnotations = (
|
|
197
|
+
editor: PortableTextEditor,
|
|
198
|
+
): PortableTextObject[] => {
|
|
196
199
|
return editor && editor.editable ? editor.editable.activeAnnotations() : []
|
|
197
200
|
}
|
|
198
201
|
static isAnnotationActive = (
|
|
199
202
|
editor: PortableTextEditor,
|
|
200
203
|
annotationType: PortableTextObject['_type'],
|
|
201
204
|
): boolean => {
|
|
202
|
-
return editor && editor.editable
|
|
205
|
+
return editor && editor.editable
|
|
206
|
+
? editor.editable.isAnnotationActive(annotationType)
|
|
207
|
+
: false
|
|
203
208
|
}
|
|
204
209
|
static addAnnotation = (
|
|
205
210
|
editor: PortableTextEditor,
|
|
206
211
|
type: ObjectSchemaType,
|
|
207
212
|
value?: {[prop: string]: unknown},
|
|
208
|
-
):
|
|
213
|
+
):
|
|
214
|
+
| {
|
|
215
|
+
/**
|
|
216
|
+
* @deprecated An annotation may be applied to multiple blocks, resulting
|
|
217
|
+
* in multiple `markDef`'s being created. Use `markDefPaths` instead.
|
|
218
|
+
*/
|
|
219
|
+
markDefPath: Path
|
|
220
|
+
markDefPaths: Array<Path>
|
|
221
|
+
/**
|
|
222
|
+
* @deprecated Does not return anything meaningful since an annotation
|
|
223
|
+
* can span multiple blocks and spans. If references the span closest
|
|
224
|
+
* to the focus point of the selection.
|
|
225
|
+
*/
|
|
226
|
+
spanPath: Path
|
|
227
|
+
}
|
|
228
|
+
| undefined => editor.editable?.addAnnotation(type, value)
|
|
209
229
|
static blur = (editor: PortableTextEditor): void => {
|
|
210
230
|
debug('Host blurred')
|
|
211
231
|
editor.editable?.blur()
|
|
@@ -232,7 +252,9 @@ export class PortableTextEditor extends Component<PortableTextEditorProps> {
|
|
|
232
252
|
static focusBlock = (editor: PortableTextEditor) => {
|
|
233
253
|
return editor.editable?.focusBlock()
|
|
234
254
|
}
|
|
235
|
-
static focusChild = (
|
|
255
|
+
static focusChild = (
|
|
256
|
+
editor: PortableTextEditor,
|
|
257
|
+
): PortableTextChild | undefined => {
|
|
236
258
|
return editor.editable?.focusChild()
|
|
237
259
|
}
|
|
238
260
|
static getSelection = (editor: PortableTextEditor) => {
|
|
@@ -271,7 +293,10 @@ export class PortableTextEditor extends Component<PortableTextEditorProps> {
|
|
|
271
293
|
static insertBreak = (editor: PortableTextEditor): void => {
|
|
272
294
|
return editor.editable?.insertBreak()
|
|
273
295
|
}
|
|
274
|
-
static isVoid = (
|
|
296
|
+
static isVoid = (
|
|
297
|
+
editor: PortableTextEditor,
|
|
298
|
+
element: PortableTextBlock | PortableTextChild,
|
|
299
|
+
) => {
|
|
275
300
|
return editor.editable?.isVoid(element)
|
|
276
301
|
}
|
|
277
302
|
static isObjectPath = (editor: PortableTextEditor, path: Path): boolean => {
|
|
@@ -283,13 +308,21 @@ export class PortableTextEditor extends Component<PortableTextEditorProps> {
|
|
|
283
308
|
static marks = (editor: PortableTextEditor) => {
|
|
284
309
|
return editor.editable?.marks()
|
|
285
310
|
}
|
|
286
|
-
static select = (
|
|
311
|
+
static select = (
|
|
312
|
+
editor: PortableTextEditor,
|
|
313
|
+
selection: EditorSelection | null,
|
|
314
|
+
) => {
|
|
287
315
|
debug(`Host setting selection`, selection)
|
|
288
316
|
editor.editable?.select(selection)
|
|
289
317
|
}
|
|
290
|
-
static removeAnnotation = (
|
|
291
|
-
editor
|
|
292
|
-
|
|
318
|
+
static removeAnnotation = (
|
|
319
|
+
editor: PortableTextEditor,
|
|
320
|
+
type: ObjectSchemaType,
|
|
321
|
+
) => editor.editable?.removeAnnotation(type)
|
|
322
|
+
static toggleBlockStyle = (
|
|
323
|
+
editor: PortableTextEditor,
|
|
324
|
+
blockStyle: string,
|
|
325
|
+
) => {
|
|
293
326
|
debug(`Host is toggling block style`)
|
|
294
327
|
return editor.editable?.toggleBlockStyle(blockStyle)
|
|
295
328
|
}
|
|
@@ -300,7 +333,9 @@ export class PortableTextEditor extends Component<PortableTextEditorProps> {
|
|
|
300
333
|
debug(`Host toggling mark`, mark)
|
|
301
334
|
editor.editable?.toggleMark(mark)
|
|
302
335
|
}
|
|
303
|
-
static getFragment = (
|
|
336
|
+
static getFragment = (
|
|
337
|
+
editor: PortableTextEditor,
|
|
338
|
+
): PortableTextBlock[] | undefined => {
|
|
304
339
|
debug(`Host getting fragment`)
|
|
305
340
|
return editor.editable?.getFragment()
|
|
306
341
|
}
|
|
@@ -3,7 +3,6 @@ import {describe, expect, it, jest} from '@jest/globals'
|
|
|
3
3
|
import {type PortableTextBlock} from '@sanity/types'
|
|
4
4
|
import {render, waitFor} from '@testing-library/react'
|
|
5
5
|
import {createRef, type RefObject} from 'react'
|
|
6
|
-
|
|
7
6
|
import {type EditorSelection} from '../..'
|
|
8
7
|
import {PortableTextEditor} from '../PortableTextEditor'
|
|
9
8
|
import {PortableTextEditorTester, schemaType} from './PortableTextEditorTester'
|
|
@@ -102,10 +101,15 @@ describe('initialization', () => {
|
|
|
102
101
|
)
|
|
103
102
|
const normalizedEditorValue = [{...initialValue[0], style: 'normal'}]
|
|
104
103
|
await waitFor(() => {
|
|
105
|
-
expect(onChange).toHaveBeenCalledWith({
|
|
104
|
+
expect(onChange).toHaveBeenCalledWith({
|
|
105
|
+
type: 'value',
|
|
106
|
+
value: initialValue,
|
|
107
|
+
})
|
|
106
108
|
})
|
|
107
109
|
if (editorRef.current) {
|
|
108
|
-
expect(PortableTextEditor.getValue(editorRef.current)).toStrictEqual(
|
|
110
|
+
expect(PortableTextEditor.getValue(editorRef.current)).toStrictEqual(
|
|
111
|
+
normalizedEditorValue,
|
|
112
|
+
)
|
|
109
113
|
}
|
|
110
114
|
})
|
|
111
115
|
|
|
@@ -130,7 +134,10 @@ describe('initialization', () => {
|
|
|
130
134
|
|
|
131
135
|
await waitFor(() => {
|
|
132
136
|
if (editorRef.current) {
|
|
133
|
-
expect(onChange).toHaveBeenCalledWith({
|
|
137
|
+
expect(onChange).toHaveBeenCalledWith({
|
|
138
|
+
type: 'value',
|
|
139
|
+
value: initialValue,
|
|
140
|
+
})
|
|
134
141
|
expect(onChange).toHaveBeenCalledWith({type: 'ready'})
|
|
135
142
|
}
|
|
136
143
|
})
|
|
@@ -138,7 +145,9 @@ describe('initialization', () => {
|
|
|
138
145
|
await waitFor(() => {
|
|
139
146
|
if (editorRef.current) {
|
|
140
147
|
PortableTextEditor.focus(editorRef.current)
|
|
141
|
-
expect(
|
|
148
|
+
expect(
|
|
149
|
+
PortableTextEditor.getSelection(editorRef.current),
|
|
150
|
+
).toStrictEqual(initialSelection)
|
|
142
151
|
}
|
|
143
152
|
})
|
|
144
153
|
})
|
|
@@ -169,7 +178,10 @@ describe('initialization', () => {
|
|
|
169
178
|
|
|
170
179
|
await waitFor(() => {
|
|
171
180
|
if (editorRef.current) {
|
|
172
|
-
expect(onChange).toHaveBeenCalledWith({
|
|
181
|
+
expect(onChange).toHaveBeenCalledWith({
|
|
182
|
+
type: 'value',
|
|
183
|
+
value: initialValue,
|
|
184
|
+
})
|
|
173
185
|
expect(onChange).toHaveBeenCalledWith({type: 'ready'})
|
|
174
186
|
}
|
|
175
187
|
})
|
|
@@ -181,7 +193,9 @@ describe('initialization', () => {
|
|
|
181
193
|
|
|
182
194
|
// Test for object equality here!
|
|
183
195
|
const anotherSel = PortableTextEditor.getSelection(editorRef.current)
|
|
184
|
-
expect(
|
|
196
|
+
expect(
|
|
197
|
+
PortableTextEditor.getSelection(editorRef.current),
|
|
198
|
+
).toStrictEqual(initialSelection)
|
|
185
199
|
expect(sel).toBe(anotherSel)
|
|
186
200
|
}
|
|
187
201
|
})
|
|
@@ -196,7 +210,9 @@ describe('initialization', () => {
|
|
|
196
210
|
)
|
|
197
211
|
waitFor(() => {
|
|
198
212
|
if (editorRef.current) {
|
|
199
|
-
expect(PortableTextEditor.getSelection(editorRef.current)).toEqual(
|
|
213
|
+
expect(PortableTextEditor.getSelection(editorRef.current)).toEqual(
|
|
214
|
+
newSelection,
|
|
215
|
+
)
|
|
200
216
|
}
|
|
201
217
|
})
|
|
202
218
|
})
|
|
@@ -225,7 +241,8 @@ describe('initialization', () => {
|
|
|
225
241
|
value: initialValue,
|
|
226
242
|
resolution: {
|
|
227
243
|
action: 'Unset the value',
|
|
228
|
-
description:
|
|
244
|
+
description:
|
|
245
|
+
'Editor value must be an array of Portable Text blocks, or undefined.',
|
|
229
246
|
item: initialValue,
|
|
230
247
|
patches: [
|
|
231
248
|
{
|
|
@@ -235,7 +252,10 @@ describe('initialization', () => {
|
|
|
235
252
|
],
|
|
236
253
|
},
|
|
237
254
|
})
|
|
238
|
-
expect(onChange).toHaveBeenCalledWith({
|
|
255
|
+
expect(onChange).toHaveBeenCalledWith({
|
|
256
|
+
type: 'value',
|
|
257
|
+
value: initialValue,
|
|
258
|
+
})
|
|
239
259
|
expect(onChange).toHaveBeenCalledWith({type: 'ready'})
|
|
240
260
|
}
|
|
241
261
|
})
|
|
@@ -267,7 +287,8 @@ describe('initialization', () => {
|
|
|
267
287
|
value,
|
|
268
288
|
resolution: {
|
|
269
289
|
action: 'Unset the value',
|
|
270
|
-
description:
|
|
290
|
+
description:
|
|
291
|
+
'Editor value must be an array of Portable Text blocks, or undefined.',
|
|
271
292
|
item: value,
|
|
272
293
|
patches: [
|
|
273
294
|
{
|
|
@@ -306,7 +327,8 @@ describe('initialization', () => {
|
|
|
306
327
|
],
|
|
307
328
|
i18n: {
|
|
308
329
|
action: 'inputs.portable-text.invalid-value.disallowed-type.action',
|
|
309
|
-
description:
|
|
330
|
+
description:
|
|
331
|
+
'inputs.portable-text.invalid-value.disallowed-type.description',
|
|
310
332
|
values: {
|
|
311
333
|
key: '123',
|
|
312
334
|
typeName: 'banana',
|
|
@@ -351,8 +373,10 @@ describe('initialization', () => {
|
|
|
351
373
|
description:
|
|
352
374
|
"Child with _key 'def' in block with key 'abc' has missing or invalid text property!",
|
|
353
375
|
i18n: {
|
|
354
|
-
action:
|
|
355
|
-
|
|
376
|
+
action:
|
|
377
|
+
'inputs.portable-text.invalid-value.invalid-span-text.action',
|
|
378
|
+
description:
|
|
379
|
+
'inputs.portable-text.invalid-value.invalid-span-text.description',
|
|
356
380
|
values: {
|
|
357
381
|
key: 'abc',
|
|
358
382
|
childKey: 'def',
|
|
@@ -394,7 +418,10 @@ describe('initialization', () => {
|
|
|
394
418
|
})
|
|
395
419
|
}
|
|
396
420
|
})
|
|
397
|
-
expect(onChange).not.toHaveBeenCalledWith({
|
|
421
|
+
expect(onChange).not.toHaveBeenCalledWith({
|
|
422
|
+
type: 'value',
|
|
423
|
+
value: initialValue,
|
|
424
|
+
})
|
|
398
425
|
expect(onChange).toHaveBeenCalledWith({type: 'ready'})
|
|
399
426
|
})
|
|
400
427
|
})
|