@portabletext/editor 2.13.1 → 2.13.3
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/_chunks-cjs/selector.is-selecting-entire-blocks.cjs.map +1 -1
- package/lib/_chunks-dts/behavior.types.action.d.ts +52 -52
- package/lib/_chunks-es/selector.is-selecting-entire-blocks.js.map +1 -1
- package/lib/index.cjs +217 -117
- package/lib/index.cjs.map +1 -1
- package/lib/index.js +219 -119
- package/lib/index.js.map +1 -1
- package/lib/selectors/index.d.cts +4 -1
- package/lib/selectors/index.d.ts +4 -1
- package/lib/utils/index.d.cts +2 -2
- package/package.json +12 -12
- package/src/editor/Editable.tsx +33 -4
- package/src/editor/validate-selection-machine.test.ts +47 -0
- package/src/editor/validate-selection-machine.ts +149 -0
- package/src/selectors/index.ts +1 -1
- package/src/selectors/selector.get-mark-state.ts +3 -0
- package/src/test/gherkin-parameter-types.ts +1 -1
|
@@ -127,6 +127,9 @@ declare function getListIndex({
|
|
|
127
127
|
}: {
|
|
128
128
|
path: BlockPath;
|
|
129
129
|
}): EditorSelector<number | undefined>;
|
|
130
|
+
/**
|
|
131
|
+
* @beta
|
|
132
|
+
*/
|
|
130
133
|
type MarkState = {
|
|
131
134
|
state: 'unchanged';
|
|
132
135
|
marks: Array<string>;
|
|
@@ -319,4 +322,4 @@ declare const isSelectionCollapsed: EditorSelector<boolean>;
|
|
|
319
322
|
* @public
|
|
320
323
|
*/
|
|
321
324
|
declare const isSelectionExpanded: EditorSelector<boolean>;
|
|
322
|
-
export { getActiveAnnotations, getActiveListItem, getActiveStyle, getAnchorBlock, getAnchorChild, getAnchorSpan, getAnchorTextBlock, getBlockOffsets, getBlockTextBefore, getCaretWordSelection, getFirstBlock, getFocusBlock, getFocusBlockObject, getFocusChild, getFocusInlineObject, getFocusListBlock, getFocusSpan, getFocusTextBlock, getLastBlock, getListIndex, getMarkState, getNextBlock, getNextInlineObject, getPreviousBlock, getPreviousInlineObject, getSelectedBlocks, getSelectedSlice, getSelectedSpans, getSelectedTextBlocks, getSelectedValue, getSelection, getSelectionEndBlock, getSelectionEndChild, getSelectionEndPoint, getSelectionStartBlock, getSelectionStartChild, getSelectionStartPoint, getSelectionText, getTrimmedSelection, getValue, isActiveAnnotation, isActiveDecorator, isActiveListItem, isActiveStyle, isAtTheEndOfBlock, isAtTheStartOfBlock, isOverlappingSelection, isPointAfterSelection, isPointBeforeSelection, isSelectingEntireBlocks, isSelectionCollapsed, isSelectionExpanded };
|
|
325
|
+
export { MarkState, getActiveAnnotations, getActiveListItem, getActiveStyle, getAnchorBlock, getAnchorChild, getAnchorSpan, getAnchorTextBlock, getBlockOffsets, getBlockTextBefore, getCaretWordSelection, getFirstBlock, getFocusBlock, getFocusBlockObject, getFocusChild, getFocusInlineObject, getFocusListBlock, getFocusSpan, getFocusTextBlock, getLastBlock, getListIndex, getMarkState, getNextBlock, getNextInlineObject, getPreviousBlock, getPreviousInlineObject, getSelectedBlocks, getSelectedSlice, getSelectedSpans, getSelectedTextBlocks, getSelectedValue, getSelection, getSelectionEndBlock, getSelectionEndChild, getSelectionEndPoint, getSelectionStartBlock, getSelectionStartChild, getSelectionStartPoint, getSelectionText, getTrimmedSelection, getValue, isActiveAnnotation, isActiveDecorator, isActiveListItem, isActiveStyle, isAtTheEndOfBlock, isAtTheStartOfBlock, isOverlappingSelection, isPointAfterSelection, isPointBeforeSelection, isSelectingEntireBlocks, isSelectionCollapsed, isSelectionExpanded };
|
package/lib/selectors/index.d.ts
CHANGED
|
@@ -127,6 +127,9 @@ declare function getListIndex({
|
|
|
127
127
|
}: {
|
|
128
128
|
path: BlockPath;
|
|
129
129
|
}): EditorSelector<number | undefined>;
|
|
130
|
+
/**
|
|
131
|
+
* @beta
|
|
132
|
+
*/
|
|
130
133
|
type MarkState = {
|
|
131
134
|
state: 'unchanged';
|
|
132
135
|
marks: Array<string>;
|
|
@@ -319,4 +322,4 @@ declare const isSelectionCollapsed: EditorSelector<boolean>;
|
|
|
319
322
|
* @public
|
|
320
323
|
*/
|
|
321
324
|
declare const isSelectionExpanded: EditorSelector<boolean>;
|
|
322
|
-
export { getActiveAnnotations, getActiveListItem, getActiveStyle, getAnchorBlock, getAnchorChild, getAnchorSpan, getAnchorTextBlock, getBlockOffsets, getBlockTextBefore, getCaretWordSelection, getFirstBlock, getFocusBlock, getFocusBlockObject, getFocusChild, getFocusInlineObject, getFocusListBlock, getFocusSpan, getFocusTextBlock, getLastBlock, getListIndex, getMarkState, getNextBlock, getNextInlineObject, getPreviousBlock, getPreviousInlineObject, getSelectedBlocks, getSelectedSlice, getSelectedSpans, getSelectedTextBlocks, getSelectedValue, getSelection, getSelectionEndBlock, getSelectionEndChild, getSelectionEndPoint, getSelectionStartBlock, getSelectionStartChild, getSelectionStartPoint, getSelectionText, getTrimmedSelection, getValue, isActiveAnnotation, isActiveDecorator, isActiveListItem, isActiveStyle, isAtTheEndOfBlock, isAtTheStartOfBlock, isOverlappingSelection, isPointAfterSelection, isPointBeforeSelection, isSelectingEntireBlocks, isSelectionCollapsed, isSelectionExpanded };
|
|
325
|
+
export { MarkState, getActiveAnnotations, getActiveListItem, getActiveStyle, getAnchorBlock, getAnchorChild, getAnchorSpan, getAnchorTextBlock, getBlockOffsets, getBlockTextBefore, getCaretWordSelection, getFirstBlock, getFocusBlock, getFocusBlockObject, getFocusChild, getFocusInlineObject, getFocusListBlock, getFocusSpan, getFocusTextBlock, getLastBlock, getListIndex, getMarkState, getNextBlock, getNextInlineObject, getPreviousBlock, getPreviousInlineObject, getSelectedBlocks, getSelectedSlice, getSelectedSpans, getSelectedTextBlocks, getSelectedValue, getSelection, getSelectionEndBlock, getSelectionEndChild, getSelectionEndPoint, getSelectionStartBlock, getSelectionStartChild, getSelectionStartPoint, getSelectionText, getTrimmedSelection, getValue, isActiveAnnotation, isActiveDecorator, isActiveListItem, isActiveStyle, isAtTheEndOfBlock, isAtTheStartOfBlock, isOverlappingSelection, isPointAfterSelection, isPointBeforeSelection, isSelectingEntireBlocks, isSelectionCollapsed, isSelectionExpanded };
|
package/lib/utils/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BlockOffset, BlockPath, ChildPath, EditorContext, EditorSelection, EditorSelectionPoint } from "../_chunks-dts/behavior.types.action.cjs";
|
|
2
|
-
import * as
|
|
2
|
+
import * as _sanity_types9 from "@sanity/types";
|
|
3
3
|
import { KeyedSegment, PortableTextBlock, PortableTextTextBlock } from "@sanity/types";
|
|
4
4
|
import { isSpan, isTextBlock } from "@portabletext/schema";
|
|
5
5
|
/**
|
|
@@ -143,7 +143,7 @@ declare function mergeTextBlocks({
|
|
|
143
143
|
context: Pick<EditorContext, 'keyGenerator' | 'schema'>;
|
|
144
144
|
targetBlock: PortableTextTextBlock;
|
|
145
145
|
incomingBlock: PortableTextTextBlock;
|
|
146
|
-
}): PortableTextTextBlock<
|
|
146
|
+
}): PortableTextTextBlock<_sanity_types9.PortableTextObject | _sanity_types9.PortableTextSpan>;
|
|
147
147
|
/**
|
|
148
148
|
* @public
|
|
149
149
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@portabletext/editor",
|
|
3
|
-
"version": "2.13.
|
|
3
|
+
"version": "2.13.3",
|
|
4
4
|
"description": "Portable Text Editor made in React",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sanity",
|
|
@@ -85,16 +85,16 @@
|
|
|
85
85
|
"slate-dom": "^0.118.1",
|
|
86
86
|
"slate-react": "0.117.4",
|
|
87
87
|
"xstate": "^5.22.0",
|
|
88
|
-
"@portabletext/block-tools": "^3.5.
|
|
89
|
-
"@portabletext/keyboard-shortcuts": "^1.1.1",
|
|
88
|
+
"@portabletext/block-tools": "^3.5.8",
|
|
90
89
|
"@portabletext/patches": "^1.1.8",
|
|
91
|
-
"@portabletext/schema": "^1.2.0"
|
|
90
|
+
"@portabletext/schema": "^1.2.0",
|
|
91
|
+
"@portabletext/keyboard-shortcuts": "^1.1.1"
|
|
92
92
|
},
|
|
93
93
|
"devDependencies": {
|
|
94
94
|
"@sanity/diff-match-patch": "^3.2.0",
|
|
95
95
|
"@sanity/pkg-utils": "^8.1.14",
|
|
96
|
-
"@sanity/schema": "^4.
|
|
97
|
-
"@sanity/types": "^4.
|
|
96
|
+
"@sanity/schema": "^4.10.1",
|
|
97
|
+
"@sanity/types": "^4.10.1",
|
|
98
98
|
"@types/debug": "^4.1.12",
|
|
99
99
|
"@types/lodash": "^4.17.20",
|
|
100
100
|
"@types/lodash.startcase": "^4.4.9",
|
|
@@ -116,14 +116,14 @@
|
|
|
116
116
|
"vite": "^7.1.7",
|
|
117
117
|
"vitest": "^3.2.4",
|
|
118
118
|
"vitest-browser-react": "^1.0.1",
|
|
119
|
-
"@portabletext/sanity-bridge": "1.1.
|
|
120
|
-
"
|
|
121
|
-
"
|
|
119
|
+
"@portabletext/sanity-bridge": "1.1.12",
|
|
120
|
+
"@portabletext/test": "^0.0.0",
|
|
121
|
+
"racejar": "1.3.0"
|
|
122
122
|
},
|
|
123
123
|
"peerDependencies": {
|
|
124
|
-
"@portabletext/sanity-bridge": "^1.1.
|
|
125
|
-
"@sanity/schema": "^4.
|
|
126
|
-
"@sanity/types": "^4.
|
|
124
|
+
"@portabletext/sanity-bridge": "^1.1.12",
|
|
125
|
+
"@sanity/schema": "^4.10.1",
|
|
126
|
+
"@sanity/types": "^4.10.1",
|
|
127
127
|
"react": "^18.3 || ^19",
|
|
128
128
|
"rxjs": "^7.8.2"
|
|
129
129
|
},
|
package/src/editor/Editable.tsx
CHANGED
|
@@ -51,6 +51,7 @@ import {usePortableTextEditor} from './hooks/usePortableTextEditor'
|
|
|
51
51
|
import {createWithHotkeys} from './plugins/createWithHotKeys'
|
|
52
52
|
import {rangeDecorationsMachine} from './range-decorations-machine'
|
|
53
53
|
import {RelayActorContext} from './relay-actor-context'
|
|
54
|
+
import {validateSelectionMachine} from './validate-selection-machine'
|
|
54
55
|
|
|
55
56
|
const debug = debugWithName('component:Editable')
|
|
56
57
|
|
|
@@ -142,6 +143,11 @@ export const PortableTextEditable = forwardRef<
|
|
|
142
143
|
s.matches({'edit mode': 'read only'}),
|
|
143
144
|
)
|
|
144
145
|
const slateEditor = useSlate()
|
|
146
|
+
const validateSelectionActor = useActorRef(validateSelectionMachine, {
|
|
147
|
+
input: {
|
|
148
|
+
slateEditor,
|
|
149
|
+
},
|
|
150
|
+
})
|
|
145
151
|
|
|
146
152
|
const rangeDecorationsActor = useActorRef(rangeDecorationsMachine, {
|
|
147
153
|
input: {
|
|
@@ -936,14 +942,37 @@ export const PortableTextEditable = forwardRef<
|
|
|
936
942
|
)
|
|
937
943
|
|
|
938
944
|
const callbackRef = useCallback(
|
|
939
|
-
(
|
|
945
|
+
(editorElement: HTMLDivElement | null) => {
|
|
940
946
|
if (typeof forwardedRef === 'function') {
|
|
941
|
-
forwardedRef(
|
|
947
|
+
forwardedRef(editorElement)
|
|
942
948
|
} else if (forwardedRef) {
|
|
943
|
-
forwardedRef.current =
|
|
949
|
+
forwardedRef.current = editorElement
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
if (editorElement) {
|
|
953
|
+
// Observe mutations (child list and subtree) to this component's DOM,
|
|
954
|
+
// and make sure the editor selection is valid when that happens.
|
|
955
|
+
const mutationObserver = new MutationObserver(() => {
|
|
956
|
+
validateSelectionActor.send({
|
|
957
|
+
type: 'validate selection',
|
|
958
|
+
editorElement,
|
|
959
|
+
})
|
|
960
|
+
})
|
|
961
|
+
|
|
962
|
+
mutationObserver.observe(editorElement, {
|
|
963
|
+
attributeOldValue: false,
|
|
964
|
+
attributes: false,
|
|
965
|
+
characterData: false,
|
|
966
|
+
childList: true,
|
|
967
|
+
subtree: true,
|
|
968
|
+
})
|
|
969
|
+
|
|
970
|
+
return () => {
|
|
971
|
+
mutationObserver.disconnect()
|
|
972
|
+
}
|
|
944
973
|
}
|
|
945
974
|
},
|
|
946
|
-
[forwardedRef],
|
|
975
|
+
[forwardedRef, validateSelectionActor],
|
|
947
976
|
)
|
|
948
977
|
|
|
949
978
|
if (!portableTextEditor) {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {getTersePt} from '@portabletext/test'
|
|
2
|
+
import {userEvent} from '@vitest/browser/context'
|
|
3
|
+
import {describe, expect, test, vi} from 'vitest'
|
|
4
|
+
import {getSelectionAfterText} from '../internal-utils/text-selection'
|
|
5
|
+
import {createTestEditor} from '../test/vitest'
|
|
6
|
+
import {validateSelectionMachine} from './validate-selection-machine'
|
|
7
|
+
|
|
8
|
+
describe(validateSelectionMachine.id, () => {
|
|
9
|
+
test('Scenario: Does not validate selection while Slate has pending operations', async () => {
|
|
10
|
+
const {editor, locator} = await createTestEditor()
|
|
11
|
+
|
|
12
|
+
await userEvent.click(locator)
|
|
13
|
+
|
|
14
|
+
editor.send({type: 'insert.text', text: 'foo'})
|
|
15
|
+
|
|
16
|
+
await vi.waitFor(() => {
|
|
17
|
+
expect(getTersePt(editor.getSnapshot().context)).toEqual(['foo'])
|
|
18
|
+
expect(editor.getSnapshot().context.selection).toEqual(
|
|
19
|
+
getSelectionAfterText(editor.getSnapshot().context, 'foo'),
|
|
20
|
+
)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
// This event is being sent in before "foo" has been inserted in the DOM
|
|
24
|
+
// This means that when the MutationObserver is finally triggered for the
|
|
25
|
+
// "foo" insertion, "bar" will be in the Slate state but not in the DOM.
|
|
26
|
+
// This causes the selection to be out of sync and this is why we need to
|
|
27
|
+
// make sure the selection is not validated before Slate has committed all
|
|
28
|
+
// pending operations.
|
|
29
|
+
editor.send({type: 'insert.text', text: 'bar'})
|
|
30
|
+
|
|
31
|
+
await vi.waitFor(() => {
|
|
32
|
+
expect(getTersePt(editor.getSnapshot().context)).toEqual(['foobar'])
|
|
33
|
+
expect(editor.getSnapshot().context.selection).toEqual(
|
|
34
|
+
getSelectionAfterText(editor.getSnapshot().context, 'foobar'),
|
|
35
|
+
)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
editor.send({type: 'delete.backward', unit: 'character'})
|
|
39
|
+
|
|
40
|
+
await vi.waitFor(() => {
|
|
41
|
+
expect(getTersePt(editor.getSnapshot().context)).toEqual(['fooba'])
|
|
42
|
+
expect(editor.getSnapshot().context.selection).toEqual(
|
|
43
|
+
getSelectionAfterText(editor.getSnapshot().context, 'fooba'),
|
|
44
|
+
)
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
})
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import {Editor, Transforms} from 'slate'
|
|
2
|
+
import {ReactEditor} from 'slate-react'
|
|
3
|
+
import {setup} from 'xstate'
|
|
4
|
+
import {debugWithName} from '../internal-utils/debug'
|
|
5
|
+
import type {PortableTextSlateEditor} from '../types/editor'
|
|
6
|
+
|
|
7
|
+
const debug = debugWithName('validate selection machine')
|
|
8
|
+
|
|
9
|
+
const validateSelectionSetup = setup({
|
|
10
|
+
types: {
|
|
11
|
+
context: {} as {
|
|
12
|
+
slateEditor: PortableTextSlateEditor
|
|
13
|
+
},
|
|
14
|
+
input: {} as {
|
|
15
|
+
slateEditor: PortableTextSlateEditor
|
|
16
|
+
},
|
|
17
|
+
events: {} as {
|
|
18
|
+
type: 'validate selection'
|
|
19
|
+
editorElement: HTMLDivElement
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
guards: {
|
|
23
|
+
'pending operations': ({context}) =>
|
|
24
|
+
context.slateEditor.operations.length > 0,
|
|
25
|
+
},
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
const validateSelectionAction = validateSelectionSetup.createAction(
|
|
29
|
+
({context, event}) => {
|
|
30
|
+
validateSelection(context.slateEditor, event.editorElement)
|
|
31
|
+
},
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
export const validateSelectionMachine = validateSelectionSetup.createMachine({
|
|
35
|
+
id: 'validate selection',
|
|
36
|
+
context: ({input}) => ({
|
|
37
|
+
slateEditor: input.slateEditor,
|
|
38
|
+
}),
|
|
39
|
+
initial: 'idle',
|
|
40
|
+
states: {
|
|
41
|
+
idle: {
|
|
42
|
+
on: {
|
|
43
|
+
'validate selection': [
|
|
44
|
+
{
|
|
45
|
+
guard: 'pending operations',
|
|
46
|
+
target: 'waiting',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
actions: [validateSelectionAction],
|
|
50
|
+
target: 'idle',
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
waiting: {
|
|
56
|
+
after: {
|
|
57
|
+
0: [
|
|
58
|
+
{
|
|
59
|
+
guard: 'pending operations',
|
|
60
|
+
target: '.',
|
|
61
|
+
reenter: true,
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
target: 'idle',
|
|
65
|
+
actions: [validateSelectionAction],
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
on: {
|
|
70
|
+
'validate selection': {
|
|
71
|
+
target: '.',
|
|
72
|
+
reenter: true,
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
// This function will handle unexpected DOM changes inside the Editable rendering,
|
|
80
|
+
// and make sure that we can maintain a stable slateEditor.selection when that happens.
|
|
81
|
+
//
|
|
82
|
+
// For example, if this Editable is rendered inside something that might re-render
|
|
83
|
+
// this component (hidden contexts) while the user is still actively changing the
|
|
84
|
+
// contentEditable, this could interfere with the intermediate DOM selection,
|
|
85
|
+
// which again could be picked up by ReactEditor's event listeners.
|
|
86
|
+
// If that range is invalid at that point, the slate.editorSelection could be
|
|
87
|
+
// set either wrong, or invalid, to which slateEditor will throw exceptions
|
|
88
|
+
// that are impossible to recover properly from or result in a wrong selection.
|
|
89
|
+
//
|
|
90
|
+
// Also the other way around, when the ReactEditor will try to create a DOM Range
|
|
91
|
+
// from the current slateEditor.selection, it may throw unrecoverable errors
|
|
92
|
+
// if the current editor.selection is invalid according to the DOM.
|
|
93
|
+
// If this is the case, default to selecting the top of the document, if the
|
|
94
|
+
// user already had a selection.
|
|
95
|
+
function validateSelection(
|
|
96
|
+
slateEditor: PortableTextSlateEditor,
|
|
97
|
+
editorElement: HTMLDivElement,
|
|
98
|
+
) {
|
|
99
|
+
if (!slateEditor.selection) {
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
let root: Document | ShadowRoot | undefined
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
root = ReactEditor.findDocumentOrShadowRoot(slateEditor)
|
|
107
|
+
} catch {}
|
|
108
|
+
|
|
109
|
+
if (!root) {
|
|
110
|
+
// The editor has most likely been unmounted
|
|
111
|
+
return
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Return if the editor isn't the active element
|
|
115
|
+
if (editorElement !== root.activeElement) {
|
|
116
|
+
return
|
|
117
|
+
}
|
|
118
|
+
const window = ReactEditor.getWindow(slateEditor)
|
|
119
|
+
const domSelection = window.getSelection()
|
|
120
|
+
if (!domSelection || domSelection.rangeCount === 0) {
|
|
121
|
+
return
|
|
122
|
+
}
|
|
123
|
+
const existingDOMRange = domSelection.getRangeAt(0)
|
|
124
|
+
try {
|
|
125
|
+
const newDOMRange = ReactEditor.toDOMRange(
|
|
126
|
+
slateEditor,
|
|
127
|
+
slateEditor.selection,
|
|
128
|
+
)
|
|
129
|
+
if (
|
|
130
|
+
newDOMRange.startOffset !== existingDOMRange.startOffset ||
|
|
131
|
+
newDOMRange.endOffset !== existingDOMRange.endOffset
|
|
132
|
+
) {
|
|
133
|
+
debug('DOM range out of sync, validating selection')
|
|
134
|
+
// Remove all ranges temporary
|
|
135
|
+
domSelection?.removeAllRanges()
|
|
136
|
+
// Set the correct range
|
|
137
|
+
domSelection.addRange(newDOMRange)
|
|
138
|
+
}
|
|
139
|
+
} catch {
|
|
140
|
+
debug(`Could not resolve selection, selecting top document`)
|
|
141
|
+
// Deselect the editor
|
|
142
|
+
Transforms.deselect(slateEditor)
|
|
143
|
+
// Select top document if there is a top block to select
|
|
144
|
+
if (slateEditor.children.length > 0) {
|
|
145
|
+
Transforms.select(slateEditor, Editor.start(slateEditor, []))
|
|
146
|
+
}
|
|
147
|
+
slateEditor.onChange()
|
|
148
|
+
}
|
|
149
|
+
}
|
package/src/selectors/index.ts
CHANGED
|
@@ -17,7 +17,7 @@ export {getFocusSpan} from './selector.get-focus-span'
|
|
|
17
17
|
export {getFocusTextBlock} from './selector.get-focus-text-block'
|
|
18
18
|
export {getLastBlock} from './selector.get-last-block'
|
|
19
19
|
export {getListIndex} from './selector.get-list-state'
|
|
20
|
-
export {getMarkState} from './selector.get-mark-state'
|
|
20
|
+
export {getMarkState, type MarkState} from './selector.get-mark-state'
|
|
21
21
|
export {getNextBlock} from './selector.get-next-block'
|
|
22
22
|
export {getNextInlineObject} from './selector.get-next-inline-object'
|
|
23
23
|
export {getPreviousBlock} from './selector.get-previous-block'
|
|
@@ -8,6 +8,9 @@ import {getNextSpan} from './selector.get-next-span'
|
|
|
8
8
|
import {getPreviousSpan} from './selector.get-previous-span'
|
|
9
9
|
import {getSelectedSpans} from './selector.get-selected-spans'
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* @beta
|
|
13
|
+
*/
|
|
11
14
|
export type MarkState =
|
|
12
15
|
| {
|
|
13
16
|
state: 'unchanged'
|
|
@@ -70,7 +70,7 @@ const parameterType = {
|
|
|
70
70
|
}),
|
|
71
71
|
tersePt: createParameterType<Array<string>>({
|
|
72
72
|
name: 'terse-pt',
|
|
73
|
-
matcher: /"([
|
|
73
|
+
matcher: /"([A-Za-z-,#>:\\n \d|{}()'"‘’“”?—.…→©]*)"/u,
|
|
74
74
|
type: Array,
|
|
75
75
|
transform: parseTersePtString,
|
|
76
76
|
}),
|