@portabletext/editor 2.21.2 → 3.0.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/_chunks-dts/index.d.ts +50 -210
- package/lib/_chunks-es/selector.is-at-the-start-of-block.js +103 -20
- package/lib/_chunks-es/selector.is-at-the-start-of-block.js.map +1 -1
- package/lib/_chunks-es/{util.get-text-block-text.js → util.slice-blocks.js} +29 -5
- package/lib/_chunks-es/util.slice-blocks.js.map +1 -0
- package/lib/_chunks-es/util.slice-text-block.js +13 -2
- package/lib/_chunks-es/util.slice-text-block.js.map +1 -1
- package/lib/behaviors/index.d.ts +1 -1
- package/lib/index.d.ts +2 -2
- package/lib/index.js +323 -320
- package/lib/index.js.map +1 -1
- package/lib/plugins/index.d.ts +2 -133
- package/lib/plugins/index.js +2 -796
- package/lib/plugins/index.js.map +1 -1
- package/lib/selectors/index.d.ts +2 -24
- package/lib/selectors/index.js +28 -130
- package/lib/selectors/index.js.map +1 -1
- package/lib/utils/index.d.ts +3 -3
- package/lib/utils/index.js +97 -9
- package/lib/utils/index.js.map +1 -1
- package/package.json +7 -9
- package/src/behaviors/behavior.perform-event.ts +7 -7
- package/src/editor/PortableTextEditor.tsx +0 -19
- package/src/editor/create-editor.ts +0 -3
- package/src/editor/editor-machine.ts +0 -10
- package/src/editor/event-to-change.tsx +5 -1
- package/src/editor/plugins/create-with-event-listeners.ts +0 -4
- package/src/editor/plugins/createWithObjectKeys.ts +2 -1
- package/src/editor/plugins/createWithPatches.ts +3 -3
- package/src/editor/plugins/createWithPlaceholderBlock.ts +2 -1
- package/src/editor/plugins/createWithPortableTextMarkModel.ts +2 -1
- package/src/editor/plugins/with-plugins.ts +10 -14
- package/src/editor/relay-machine.ts +0 -4
- package/src/editor/sync-machine.ts +2 -2
- package/src/editor.ts +0 -4
- package/src/history/behavior.operation.history.redo.ts +67 -0
- package/src/history/behavior.operation.history.undo.ts +71 -0
- package/src/history/event.history.undo.test.tsx +672 -0
- package/src/history/history.preserving-keys.test.tsx +112 -0
- package/src/history/remote-patches.ts +20 -0
- package/src/history/slate-plugin.history.ts +146 -0
- package/src/history/slate-plugin.redoing.ts +21 -0
- package/src/history/slate-plugin.undoing.ts +21 -0
- package/src/history/slate-plugin.without-history.ts +23 -0
- package/src/history/transform-operation.ts +245 -0
- package/src/history/undo-redo-collaboration.test.tsx +541 -0
- package/src/history/undo-redo.feature +125 -0
- package/src/history/undo-redo.test.tsx +195 -0
- package/src/history/undo-step.ts +148 -0
- package/src/index.ts +0 -1
- package/src/internal-utils/applyPatch.ts +46 -1
- package/src/operations/behavior.operations.ts +2 -4
- package/src/plugins/index.ts +0 -3
- package/src/selectors/index.ts +0 -3
- package/src/test/vitest/step-definitions.tsx +88 -8
- package/src/test/vitest/test-editor.tsx +1 -1
- package/lib/_chunks-es/selector.get-selection-text.js +0 -92
- package/lib/_chunks-es/selector.get-selection-text.js.map +0 -1
- package/lib/_chunks-es/selector.get-text-before.js +0 -36
- package/lib/_chunks-es/selector.get-text-before.js.map +0 -1
- package/lib/_chunks-es/util.get-text-block-text.js.map +0 -1
- package/lib/_chunks-es/util.is-empty-text-block.js +0 -40
- package/lib/_chunks-es/util.is-empty-text-block.js.map +0 -1
- package/lib/_chunks-es/util.merge-text-blocks.js +0 -101
- package/lib/_chunks-es/util.merge-text-blocks.js.map +0 -1
- package/src/editor/plugins/createWithMaxBlocks.ts +0 -53
- package/src/editor/plugins/createWithUndoRedo.ts +0 -628
- package/src/editor/with-undo-step.ts +0 -37
- package/src/editor/withUndoRedo.ts +0 -34
- package/src/editor-event-listener.tsx +0 -28
- package/src/plugins/plugin.decorator-shortcut.ts +0 -238
- package/src/plugins/plugin.markdown.test.tsx +0 -42
- package/src/plugins/plugin.markdown.tsx +0 -131
- package/src/plugins/plugin.one-line.tsx +0 -123
- package/src/selectors/selector.get-list-state.test.ts +0 -189
- package/src/selectors/selector.get-list-state.ts +0 -96
- package/src/selectors/selector.get-selected-slice.ts +0 -13
- package/src/selectors/selector.get-trimmed-selection.test.ts +0 -657
- package/src/selectors/selector.get-trimmed-selection.ts +0 -189
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import {Before} from 'racejar'
|
|
2
|
+
import {Feature} from 'racejar/vitest'
|
|
3
|
+
import {defineSchema} from '..'
|
|
4
|
+
import {defineBehavior, forward, raise} from '../behaviors'
|
|
5
|
+
import {BehaviorPlugin} from '../plugins/plugin.behavior'
|
|
6
|
+
import {getFocusTextBlock, isSelectionExpanded} from '../selectors'
|
|
7
|
+
import {parameterTypes} from '../test'
|
|
8
|
+
import {createTestEditor, stepDefinitions, type Context} from '../test/vitest'
|
|
9
|
+
import undoRedoFeature from './undo-redo.feature?raw'
|
|
10
|
+
|
|
11
|
+
Feature({
|
|
12
|
+
featureText: undoRedoFeature,
|
|
13
|
+
stepDefinitions,
|
|
14
|
+
parameterTypes,
|
|
15
|
+
hooks: [
|
|
16
|
+
Before(async (context: Context) => {
|
|
17
|
+
const {editor, locator} = await createTestEditor({
|
|
18
|
+
schemaDefinition: defineSchema({
|
|
19
|
+
annotations: [{name: 'link'}, {name: 'comment'}],
|
|
20
|
+
}),
|
|
21
|
+
children: (
|
|
22
|
+
<>
|
|
23
|
+
<ArrowTransformPlugin />
|
|
24
|
+
<CopyrightTransformPlugin />
|
|
25
|
+
</>
|
|
26
|
+
),
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
context.locator = locator
|
|
30
|
+
context.editor = editor
|
|
31
|
+
}),
|
|
32
|
+
],
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Native behavior to transform `>` into `→`.
|
|
37
|
+
* Ony for testing purposes.
|
|
38
|
+
*/
|
|
39
|
+
function ArrowTransformPlugin() {
|
|
40
|
+
return (
|
|
41
|
+
<BehaviorPlugin
|
|
42
|
+
behaviors={[
|
|
43
|
+
defineBehavior({
|
|
44
|
+
on: 'insert.text',
|
|
45
|
+
guard: ({snapshot, event}) => {
|
|
46
|
+
if (event.text !== '>' || isSelectionExpanded(snapshot)) {
|
|
47
|
+
return false
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const focusTextBlock = getFocusTextBlock(snapshot)
|
|
51
|
+
|
|
52
|
+
if (!focusTextBlock) {
|
|
53
|
+
return false
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {focusTextBlock}
|
|
57
|
+
},
|
|
58
|
+
actions: [
|
|
59
|
+
({event}) => [forward(event)],
|
|
60
|
+
({snapshot}, {focusTextBlock}) => [
|
|
61
|
+
raise({
|
|
62
|
+
type: 'select',
|
|
63
|
+
at: {
|
|
64
|
+
anchor: {
|
|
65
|
+
path: focusTextBlock.path,
|
|
66
|
+
offset: 0,
|
|
67
|
+
},
|
|
68
|
+
focus: {
|
|
69
|
+
path: focusTextBlock.path,
|
|
70
|
+
offset: 2,
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
}),
|
|
74
|
+
raise({
|
|
75
|
+
type: 'delete',
|
|
76
|
+
at: {
|
|
77
|
+
anchor: {
|
|
78
|
+
path: focusTextBlock.path,
|
|
79
|
+
offset: 0,
|
|
80
|
+
},
|
|
81
|
+
focus: {
|
|
82
|
+
path: focusTextBlock.path,
|
|
83
|
+
offset: 2,
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
}),
|
|
87
|
+
raise({
|
|
88
|
+
type: 'insert.child',
|
|
89
|
+
child: {
|
|
90
|
+
_type: snapshot.context.schema.span.name,
|
|
91
|
+
text: '→',
|
|
92
|
+
marks: [],
|
|
93
|
+
},
|
|
94
|
+
}),
|
|
95
|
+
raise({
|
|
96
|
+
type: 'select',
|
|
97
|
+
at: {
|
|
98
|
+
anchor: {
|
|
99
|
+
path: focusTextBlock.path,
|
|
100
|
+
offset: 2,
|
|
101
|
+
},
|
|
102
|
+
focus: {
|
|
103
|
+
path: focusTextBlock.path,
|
|
104
|
+
offset: 2,
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
}),
|
|
108
|
+
],
|
|
109
|
+
],
|
|
110
|
+
}),
|
|
111
|
+
]}
|
|
112
|
+
/>
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Native behavior to transform `(c)` into `©`.
|
|
118
|
+
* Ony for testing purposes.
|
|
119
|
+
*/
|
|
120
|
+
function CopyrightTransformPlugin() {
|
|
121
|
+
return (
|
|
122
|
+
<BehaviorPlugin
|
|
123
|
+
behaviors={[
|
|
124
|
+
defineBehavior({
|
|
125
|
+
on: 'insert.text',
|
|
126
|
+
guard: ({snapshot, event}) => {
|
|
127
|
+
if (event.text !== ')' || isSelectionExpanded(snapshot)) {
|
|
128
|
+
return false
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const focusTextBlock = getFocusTextBlock(snapshot)
|
|
132
|
+
|
|
133
|
+
if (!focusTextBlock) {
|
|
134
|
+
return false
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return {focusTextBlock}
|
|
138
|
+
},
|
|
139
|
+
actions: [
|
|
140
|
+
({event}) => [forward(event)],
|
|
141
|
+
({snapshot}, {focusTextBlock}) => [
|
|
142
|
+
raise({
|
|
143
|
+
type: 'select',
|
|
144
|
+
at: {
|
|
145
|
+
anchor: {
|
|
146
|
+
path: focusTextBlock.path,
|
|
147
|
+
offset: 0,
|
|
148
|
+
},
|
|
149
|
+
focus: {
|
|
150
|
+
path: focusTextBlock.path,
|
|
151
|
+
offset: 3,
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
}),
|
|
155
|
+
raise({
|
|
156
|
+
type: 'delete',
|
|
157
|
+
at: {
|
|
158
|
+
anchor: {
|
|
159
|
+
path: focusTextBlock.path,
|
|
160
|
+
offset: 0,
|
|
161
|
+
},
|
|
162
|
+
focus: {
|
|
163
|
+
path: focusTextBlock.path,
|
|
164
|
+
offset: 3,
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
}),
|
|
168
|
+
raise({
|
|
169
|
+
type: 'insert.child',
|
|
170
|
+
child: {
|
|
171
|
+
_type: snapshot.context.schema.span.name,
|
|
172
|
+
text: '©',
|
|
173
|
+
marks: [],
|
|
174
|
+
},
|
|
175
|
+
}),
|
|
176
|
+
raise({
|
|
177
|
+
type: 'select',
|
|
178
|
+
at: {
|
|
179
|
+
anchor: {
|
|
180
|
+
path: focusTextBlock.path,
|
|
181
|
+
offset: 3,
|
|
182
|
+
},
|
|
183
|
+
focus: {
|
|
184
|
+
path: focusTextBlock.path,
|
|
185
|
+
offset: 3,
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
}),
|
|
189
|
+
],
|
|
190
|
+
],
|
|
191
|
+
}),
|
|
192
|
+
]}
|
|
193
|
+
/>
|
|
194
|
+
)
|
|
195
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import {Path, type Editor, type Operation} from 'slate'
|
|
2
|
+
import {isNormalizingNode} from '../editor/with-normalizing-node'
|
|
3
|
+
import {defaultKeyGenerator} from '../utils/key-generator'
|
|
4
|
+
|
|
5
|
+
const CURRENT_UNDO_STEP_ID: WeakMap<Editor, {undoStepId: string} | undefined> =
|
|
6
|
+
new WeakMap()
|
|
7
|
+
|
|
8
|
+
export function getCurrentUndoStepId(editor: Editor) {
|
|
9
|
+
return CURRENT_UNDO_STEP_ID.get(editor)?.undoStepId
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function createUndoStepId(editor: Editor) {
|
|
13
|
+
CURRENT_UNDO_STEP_ID.set(editor, {
|
|
14
|
+
undoStepId: defaultKeyGenerator(),
|
|
15
|
+
})
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function clearUndoStepId(editor: Editor) {
|
|
19
|
+
CURRENT_UNDO_STEP_ID.set(editor, undefined)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
type UndoStep = {
|
|
23
|
+
operations: Array<Operation>
|
|
24
|
+
timestamp: Date
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function createUndoSteps({
|
|
28
|
+
steps,
|
|
29
|
+
op,
|
|
30
|
+
editor,
|
|
31
|
+
currentUndoStepId,
|
|
32
|
+
previousUndoStepId,
|
|
33
|
+
}: {
|
|
34
|
+
steps: Array<UndoStep>
|
|
35
|
+
op: Operation
|
|
36
|
+
editor: Editor
|
|
37
|
+
currentUndoStepId: string | undefined
|
|
38
|
+
previousUndoStepId: string | undefined
|
|
39
|
+
}): Array<UndoStep> {
|
|
40
|
+
const lastStep = steps.at(-1)
|
|
41
|
+
|
|
42
|
+
if (!lastStep) {
|
|
43
|
+
return createNewStep(steps, op, editor)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (editor.operations.length > 0) {
|
|
47
|
+
// The editor has operations in progress
|
|
48
|
+
|
|
49
|
+
if (currentUndoStepId === previousUndoStepId || isNormalizingNode(editor)) {
|
|
50
|
+
return mergeIntoLastStep(steps, lastStep, op)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return createNewStep(steps, op, editor)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (
|
|
57
|
+
op.type === 'set_selection' &&
|
|
58
|
+
currentUndoStepId === undefined &&
|
|
59
|
+
previousUndoStepId !== undefined
|
|
60
|
+
) {
|
|
61
|
+
// Selecting without undo step ID
|
|
62
|
+
return mergeIntoLastStep(steps, lastStep, op)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (
|
|
66
|
+
op.type === 'set_selection' &&
|
|
67
|
+
currentUndoStepId !== undefined &&
|
|
68
|
+
previousUndoStepId !== undefined &&
|
|
69
|
+
previousUndoStepId !== currentUndoStepId
|
|
70
|
+
) {
|
|
71
|
+
// Selecting with different undo step ID
|
|
72
|
+
return mergeIntoLastStep(steps, lastStep, op)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Handle case when both IDs are undefined
|
|
76
|
+
if (currentUndoStepId === undefined && previousUndoStepId === undefined) {
|
|
77
|
+
if (op.type === 'set_selection') {
|
|
78
|
+
return mergeIntoLastStep(steps, lastStep, op)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const lastOp = lastStep.operations.at(-1)
|
|
82
|
+
|
|
83
|
+
if (
|
|
84
|
+
lastOp &&
|
|
85
|
+
op.type === 'insert_text' &&
|
|
86
|
+
lastOp.type === 'insert_text' &&
|
|
87
|
+
op.offset === lastOp.offset + lastOp.text.length &&
|
|
88
|
+
Path.equals(op.path, lastOp.path) &&
|
|
89
|
+
op.text !== ' '
|
|
90
|
+
) {
|
|
91
|
+
return mergeIntoLastStep(steps, lastStep, op)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (
|
|
95
|
+
lastOp &&
|
|
96
|
+
op.type === 'remove_text' &&
|
|
97
|
+
lastOp.type === 'remove_text' &&
|
|
98
|
+
op.offset + op.text.length === lastOp.offset &&
|
|
99
|
+
Path.equals(op.path, lastOp.path)
|
|
100
|
+
) {
|
|
101
|
+
return mergeIntoLastStep(steps, lastStep, op)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return createNewStep(steps, op, editor)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return createNewStep(steps, op, editor)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function createNewStep(
|
|
111
|
+
steps: Array<UndoStep>,
|
|
112
|
+
op: Operation,
|
|
113
|
+
editor: Editor,
|
|
114
|
+
): Array<UndoStep> {
|
|
115
|
+
const operations =
|
|
116
|
+
editor.selection === null
|
|
117
|
+
? [op]
|
|
118
|
+
: [
|
|
119
|
+
{
|
|
120
|
+
type: 'set_selection' as const,
|
|
121
|
+
properties: {...editor.selection},
|
|
122
|
+
newProperties: {...editor.selection},
|
|
123
|
+
},
|
|
124
|
+
op,
|
|
125
|
+
]
|
|
126
|
+
|
|
127
|
+
return [
|
|
128
|
+
...steps,
|
|
129
|
+
{
|
|
130
|
+
operations,
|
|
131
|
+
timestamp: new Date(),
|
|
132
|
+
},
|
|
133
|
+
]
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function mergeIntoLastStep(
|
|
137
|
+
steps: Array<UndoStep>,
|
|
138
|
+
lastStep: UndoStep,
|
|
139
|
+
op: Operation,
|
|
140
|
+
): Array<UndoStep> {
|
|
141
|
+
return [
|
|
142
|
+
...steps.slice(0, -1),
|
|
143
|
+
{
|
|
144
|
+
timestamp: lastStep.timestamp,
|
|
145
|
+
operations: [...lastStep.operations, op],
|
|
146
|
+
},
|
|
147
|
+
]
|
|
148
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -7,7 +7,6 @@ export type {
|
|
|
7
7
|
PortableTextSpan,
|
|
8
8
|
} from '@sanity/types'
|
|
9
9
|
export type {Editor, EditorConfig, EditorEvent} from './editor'
|
|
10
|
-
export {EditorEventListener} from './editor-event-listener'
|
|
11
10
|
export {PortableTextEditable} from './editor/Editable'
|
|
12
11
|
export type {PortableTextEditableProps} from './editor/Editable'
|
|
13
12
|
export type {PatchesEvent} from './editor/editor-machine'
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
parsePatch,
|
|
17
17
|
} from '@sanity/diff-match-patch'
|
|
18
18
|
import type {Path, PortableTextBlock, PortableTextChild} from '@sanity/types'
|
|
19
|
-
import {Element, Node, Text, Transforms, type Descendant} from 'slate'
|
|
19
|
+
import {Editor, Element, Node, Text, Transforms, type Descendant} from 'slate'
|
|
20
20
|
import type {EditorSchema} from '../editor/editor-schema'
|
|
21
21
|
import {KEY_TO_SLATE_ELEMENT} from '../editor/weakMaps'
|
|
22
22
|
import type {PortableTextSlateEditor} from '../types/editor'
|
|
@@ -202,6 +202,51 @@ function setPatch(editor: PortableTextSlateEditor, patch: SetPatch) {
|
|
|
202
202
|
|
|
203
203
|
const isTextBlock = editor.isTextBlock(block.node)
|
|
204
204
|
|
|
205
|
+
if (patch.path.length === 1) {
|
|
206
|
+
const updatedBlock = applyAll(block.node, [
|
|
207
|
+
{
|
|
208
|
+
...patch,
|
|
209
|
+
path: patch.path.slice(1),
|
|
210
|
+
},
|
|
211
|
+
])
|
|
212
|
+
|
|
213
|
+
if (editor.isTextBlock(block.node) && Element.isElement(updatedBlock)) {
|
|
214
|
+
Transforms.setNodes(editor, updatedBlock, {at: [block.index]})
|
|
215
|
+
|
|
216
|
+
const previousSelection = editor.selection
|
|
217
|
+
|
|
218
|
+
// Remove the previous children
|
|
219
|
+
for (const [_, childPath] of Editor.nodes(editor, {
|
|
220
|
+
at: [block.index],
|
|
221
|
+
reverse: true,
|
|
222
|
+
mode: 'lowest',
|
|
223
|
+
})) {
|
|
224
|
+
Transforms.removeNodes(editor, {at: childPath})
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Insert the new children
|
|
228
|
+
Transforms.insertNodes(editor, updatedBlock.children, {
|
|
229
|
+
at: [block.index, 0],
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
// Restore the selection
|
|
233
|
+
if (previousSelection) {
|
|
234
|
+
// Update the selection on the editor object
|
|
235
|
+
Transforms.setSelection(editor, previousSelection)
|
|
236
|
+
// Actively select the previous selection
|
|
237
|
+
Transforms.select(editor, previousSelection)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return true
|
|
241
|
+
} else {
|
|
242
|
+
Transforms.setNodes(editor, updatedBlock as Partial<Node>, {
|
|
243
|
+
at: [block.index],
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
return true
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
205
250
|
if (isTextBlock && patch.path[1] !== 'children') {
|
|
206
251
|
const updatedBlock = applyAll(block.node, [
|
|
207
252
|
{
|
|
@@ -5,10 +5,8 @@ import type {
|
|
|
5
5
|
} from '../behaviors/behavior.types.event'
|
|
6
6
|
import type {EditorContext} from '../editor/editor-snapshot'
|
|
7
7
|
import {removeDecoratorOperationImplementation} from '../editor/plugins/createWithPortableTextMarkModel'
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
historyUndoOperationImplementation,
|
|
11
|
-
} from '../editor/plugins/createWithUndoRedo'
|
|
8
|
+
import {historyRedoOperationImplementation} from '../history/behavior.operation.history.redo'
|
|
9
|
+
import {historyUndoOperationImplementation} from '../history/behavior.operation.history.undo'
|
|
12
10
|
import type {OmitFromUnion, PickFromUnion} from '../type-utils'
|
|
13
11
|
import type {PortableTextSlateEditor} from '../types/editor'
|
|
14
12
|
import {addAnnotationOperationImplementation} from './behavior.operation.annotation.add'
|
package/src/plugins/index.ts
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
1
|
export {BehaviorPlugin} from './plugin.behavior'
|
|
2
|
-
export {DecoratorShortcutPlugin} from './plugin.decorator-shortcut'
|
|
3
2
|
export {EditorRefPlugin} from './plugin.editor-ref'
|
|
4
3
|
export {EventListenerPlugin} from './plugin.event-listener'
|
|
5
|
-
export {MarkdownPlugin, type MarkdownPluginConfig} from './plugin.markdown'
|
|
6
|
-
export {OneLinePlugin} from './plugin.one-line'
|
package/src/selectors/index.ts
CHANGED
|
@@ -16,7 +16,6 @@ export {getFocusListBlock} from './selector.get-focus-list-block'
|
|
|
16
16
|
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
|
-
export {getListIndex} from './selector.get-list-state'
|
|
20
19
|
export {getMarkState, type MarkState} from './selector.get-mark-state'
|
|
21
20
|
export {getNextBlock} from './selector.get-next-block'
|
|
22
21
|
export {getNextInlineObject} from './selector.get-next-inline-object'
|
|
@@ -27,7 +26,6 @@ export {getPreviousInlineObject} from './selector.get-previous-inline-object'
|
|
|
27
26
|
export {getPreviousInlineObjects} from './selector.get-previous-inline-objects'
|
|
28
27
|
export {getPreviousSpan} from './selector.get-previous-span'
|
|
29
28
|
export {getSelectedBlocks} from './selector.get-selected-blocks'
|
|
30
|
-
export {getSelectedSlice} from './selector.get-selected-slice'
|
|
31
29
|
export {getSelectedSpans} from './selector.get-selected-spans'
|
|
32
30
|
export {getSelectedTextBlocks} from './selector.get-selected-text-blocks'
|
|
33
31
|
export {getSelectedValue} from './selector.get-selected-value'
|
|
@@ -41,7 +39,6 @@ export {getSelectionStartPoint} from './selector.get-selection-start-point'
|
|
|
41
39
|
export {getSelectionText} from './selector.get-selection-text'
|
|
42
40
|
export {getBlockTextAfter} from './selector.get-text-after'
|
|
43
41
|
export {getBlockTextBefore} from './selector.get-text-before'
|
|
44
|
-
export {getTrimmedSelection} from './selector.get-trimmed-selection'
|
|
45
42
|
export {getValue} from './selector.get-value'
|
|
46
43
|
export {isActiveAnnotation} from './selector.is-active-annotation'
|
|
47
44
|
export {isActiveDecorator} from './selector.is-active-decorator'
|
|
@@ -190,7 +190,7 @@ export const stepDefinitions = [
|
|
|
190
190
|
await userEvent.type(context.locator, text)
|
|
191
191
|
}),
|
|
192
192
|
When(
|
|
193
|
-
'{string} is typed
|
|
193
|
+
'{string} is typed in Editor B',
|
|
194
194
|
async (context: Context, text: string) => {
|
|
195
195
|
await userEvent.type(context.locatorB, text)
|
|
196
196
|
},
|
|
@@ -222,23 +222,74 @@ export const stepDefinitions = [
|
|
|
222
222
|
*/
|
|
223
223
|
When(
|
|
224
224
|
'{button} is pressed',
|
|
225
|
-
async (
|
|
225
|
+
async (context: Context, button: Parameter['button']) => {
|
|
226
|
+
const previousSelection = context.editor.getSnapshot().context.selection
|
|
226
227
|
await userEvent.keyboard(button)
|
|
227
|
-
|
|
228
|
+
|
|
229
|
+
await vi.waitFor(() => {
|
|
230
|
+
const currentSelection = context.editor.getSnapshot().context.selection
|
|
231
|
+
|
|
232
|
+
if (currentSelection) {
|
|
233
|
+
expect(currentSelection).not.toBe(previousSelection)
|
|
234
|
+
}
|
|
235
|
+
})
|
|
236
|
+
},
|
|
237
|
+
),
|
|
238
|
+
When(
|
|
239
|
+
'{button} is pressed in Editor B',
|
|
240
|
+
async (context: Context, button: Parameter['button']) => {
|
|
241
|
+
const previousSelection = context.editorB.getSnapshot().context.selection
|
|
242
|
+
await userEvent.keyboard(button)
|
|
243
|
+
|
|
244
|
+
await vi.waitFor(() => {
|
|
245
|
+
const currentSelection = context.editorB.getSnapshot().context.selection
|
|
246
|
+
|
|
247
|
+
if (currentSelection) {
|
|
248
|
+
expect(currentSelection).not.toBe(previousSelection)
|
|
249
|
+
}
|
|
250
|
+
})
|
|
228
251
|
},
|
|
229
252
|
),
|
|
230
253
|
When(
|
|
231
254
|
'{button} is pressed {int} times',
|
|
232
|
-
async (
|
|
255
|
+
async (context: Context, button: Parameter['button'], times: number) => {
|
|
233
256
|
for (let i = 0; i < times; i++) {
|
|
257
|
+
const previousSelection = context.editor.getSnapshot().context.selection
|
|
234
258
|
await userEvent.keyboard(button)
|
|
235
|
-
|
|
259
|
+
|
|
260
|
+
await vi.waitFor(() => {
|
|
261
|
+
const currentSelection =
|
|
262
|
+
context.editor.getSnapshot().context.selection
|
|
263
|
+
|
|
264
|
+
if (currentSelection) {
|
|
265
|
+
expect(currentSelection).not.toBe(previousSelection)
|
|
266
|
+
}
|
|
267
|
+
})
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
),
|
|
271
|
+
When(
|
|
272
|
+
'{button} is pressed {int} times in Editor B',
|
|
273
|
+
async (context: Context, button: Parameter['button'], times: number) => {
|
|
274
|
+
for (let i = 0; i < times; i++) {
|
|
275
|
+
const previousSelection =
|
|
276
|
+
context.editorB.getSnapshot().context.selection
|
|
277
|
+
await userEvent.keyboard(button)
|
|
278
|
+
|
|
279
|
+
await vi.waitFor(() => {
|
|
280
|
+
const currentSelection =
|
|
281
|
+
context.editorB.getSnapshot().context.selection
|
|
282
|
+
|
|
283
|
+
if (currentSelection) {
|
|
284
|
+
expect(currentSelection).not.toBe(previousSelection)
|
|
285
|
+
}
|
|
286
|
+
})
|
|
236
287
|
}
|
|
237
288
|
},
|
|
238
289
|
),
|
|
239
290
|
When(
|
|
240
291
|
'{shortcut} is pressed',
|
|
241
|
-
async (
|
|
292
|
+
async (context: Context, shortcut: Parameter['shortcut']) => {
|
|
242
293
|
const shortcuts: Record<Parameter['shortcut'], string> = {
|
|
243
294
|
'deleteWord.backward': IS_MAC
|
|
244
295
|
? '{Alt>}{Backspace}{/Alt}'
|
|
@@ -248,8 +299,16 @@ export const stepDefinitions = [
|
|
|
248
299
|
: '{Control>}{Delete}{/Control}',
|
|
249
300
|
}
|
|
250
301
|
|
|
302
|
+
const previousSelection = context.editor.getSnapshot().context.selection
|
|
251
303
|
await userEvent.keyboard(shortcuts[shortcut])
|
|
252
|
-
|
|
304
|
+
|
|
305
|
+
await vi.waitFor(() => {
|
|
306
|
+
const currentSelection = context.editor.getSnapshot().context.selection
|
|
307
|
+
|
|
308
|
+
if (currentSelection) {
|
|
309
|
+
expect(currentSelection).not.toBe(previousSelection)
|
|
310
|
+
}
|
|
311
|
+
})
|
|
253
312
|
},
|
|
254
313
|
),
|
|
255
314
|
|
|
@@ -277,6 +336,27 @@ export const stepDefinitions = [
|
|
|
277
336
|
})
|
|
278
337
|
},
|
|
279
338
|
),
|
|
339
|
+
When(
|
|
340
|
+
'the caret is put before {string} in Editor B',
|
|
341
|
+
async (context: Context, text: string) => {
|
|
342
|
+
await vi.waitFor(() => {
|
|
343
|
+
const selection = getSelectionBeforeText(
|
|
344
|
+
context.editorB.getSnapshot().context,
|
|
345
|
+
text,
|
|
346
|
+
)
|
|
347
|
+
expect(selection).not.toBeNull()
|
|
348
|
+
|
|
349
|
+
context.editorB.send({
|
|
350
|
+
type: 'select',
|
|
351
|
+
at: selection,
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
expect(context.editorB.getSnapshot().context.selection).toEqual(
|
|
355
|
+
selection,
|
|
356
|
+
)
|
|
357
|
+
})
|
|
358
|
+
},
|
|
359
|
+
),
|
|
280
360
|
Then(
|
|
281
361
|
'the caret is before {string}',
|
|
282
362
|
async (context: Context, text: string) => {
|
|
@@ -315,7 +395,7 @@ export const stepDefinitions = [
|
|
|
315
395
|
},
|
|
316
396
|
),
|
|
317
397
|
When(
|
|
318
|
-
'the caret is put after {string}
|
|
398
|
+
'the caret is put after {string} in Editor B',
|
|
319
399
|
async (context: Context, text: string) => {
|
|
320
400
|
await vi.waitFor(() => {
|
|
321
401
|
const selection = getSelectionAfterText(
|