@portabletext/editor 1.15.3 → 1.16.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-cjs/behavior.core.cjs +25 -25
- package/lib/_chunks-cjs/behavior.core.cjs.map +1 -1
- package/lib/_chunks-cjs/selector.get-text-before.cjs +14 -14
- package/lib/_chunks-cjs/selector.get-text-before.cjs.map +1 -1
- package/lib/_chunks-cjs/{selectors.cjs → selector.is-selection-collapsed.cjs} +8 -8
- package/lib/_chunks-cjs/selector.is-selection-collapsed.cjs.map +1 -0
- package/lib/_chunks-es/behavior.core.js +7 -7
- package/lib/_chunks-es/behavior.core.js.map +1 -1
- package/lib/_chunks-es/selector.get-text-before.js +14 -14
- package/lib/_chunks-es/selector.get-text-before.js.map +1 -1
- package/lib/_chunks-es/{selectors.js → selector.is-selection-collapsed.js} +8 -8
- package/lib/_chunks-es/selector.is-selection-collapsed.js.map +1 -0
- package/lib/behaviors/index.cjs +23 -23
- package/lib/behaviors/index.cjs.map +1 -1
- package/lib/behaviors/index.d.cts +1 -0
- package/lib/behaviors/index.d.ts +1 -0
- package/lib/behaviors/index.js +8 -8
- package/lib/behaviors/index.js.map +1 -1
- package/lib/index.cjs +855 -515
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +4126 -4834
- package/lib/index.d.ts +4126 -4834
- package/lib/index.js +852 -514
- package/lib/index.js.map +1 -1
- package/lib/selectors/index.cjs +166 -16
- package/lib/selectors/index.cjs.map +1 -1
- package/lib/selectors/index.d.cts +54 -5
- package/lib/selectors/index.d.ts +54 -5
- package/lib/selectors/index.js +154 -3
- package/lib/selectors/index.js.map +1 -1
- package/package.json +11 -11
- package/src/behaviors/behavior.code-editor.ts +5 -9
- package/src/behaviors/behavior.core.block-objects.ts +13 -19
- package/src/behaviors/behavior.core.lists.ts +11 -17
- package/src/behaviors/behavior.links.ts +4 -4
- package/src/behaviors/behavior.markdown.ts +16 -21
- package/src/editor/Editable.tsx +11 -4
- package/src/editor/PortableTextEditor.tsx +2 -3
- package/src/editor/{hooks/useSyncValue.test.tsx → __tests__/sync-value.test.tsx} +42 -23
- package/src/editor/components/Synchronizer.tsx +53 -80
- package/src/editor/editor-machine.ts +129 -80
- package/src/editor/editor-provider.tsx +0 -3
- package/src/editor/editor-selector.ts +5 -0
- package/src/editor/editor-snapshot.ts +1 -0
- package/src/editor/get-active-decorators.ts +20 -0
- package/src/editor/mutation-machine.ts +100 -0
- package/src/editor/plugins/__tests__/withPortableTextMarkModel.test.tsx +21 -15
- package/src/editor/plugins/createWithMaxBlocks.ts +1 -1
- package/src/editor/plugins/createWithPatches.ts +0 -4
- package/src/editor/plugins/createWithPlaceholderBlock.ts +1 -1
- package/src/editor/plugins/createWithPortableTextSelections.ts +4 -1
- package/src/editor/plugins/createWithUndoRedo.ts +3 -3
- package/src/editor/sync-machine.ts +657 -0
- package/src/editor/withSyncRangeDecorations.ts +17 -5
- package/src/selectors/_exports/index.ts +1 -0
- package/src/selectors/index.ts +9 -1
- package/src/selectors/selector.get-active-style.ts +37 -0
- package/src/selectors/selector.get-selected-spans.ts +136 -0
- package/src/selectors/selector.is-active-annotation.ts +49 -0
- package/src/selectors/selector.is-active-decorator.ts +21 -0
- package/src/selectors/selector.is-active-list-item.ts +13 -0
- package/src/selectors/selector.is-active-style.ts +13 -0
- package/src/selectors/selector.is-selection-collapsed.ts +12 -0
- package/src/selectors/selector.is-selection-expanded.ts +9 -0
- package/src/selectors/selectors.ts +0 -11
- package/src/utils/weakMaps.ts +0 -3
- package/src/utils/withChanges.ts +1 -8
- package/lib/_chunks-cjs/selectors.cjs.map +0 -1
- package/lib/_chunks-es/selectors.js.map +0 -1
- package/src/editor/hooks/useSyncValue.ts +0 -426
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@portabletext/editor",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.16.0",
|
|
4
4
|
"description": "Portable Text Editor made in React",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sanity",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"default": "./lib/behaviors/index.js"
|
|
41
41
|
},
|
|
42
42
|
"./selectors": {
|
|
43
|
-
"source": "./src/selectors/index.ts",
|
|
43
|
+
"source": "./src/selectors/_exports/index.ts",
|
|
44
44
|
"import": "./lib/selectors/index.js",
|
|
45
45
|
"require": "./lib/selectors/index.cjs",
|
|
46
46
|
"default": "./lib/selectors/index.js"
|
|
@@ -70,18 +70,18 @@
|
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@portabletext/toolkit": "^2.0.16",
|
|
73
|
-
"@sanity/block-tools": "^3.
|
|
73
|
+
"@sanity/block-tools": "^3.67.1",
|
|
74
74
|
"@sanity/diff-match-patch": "^3.1.1",
|
|
75
|
-
"@sanity/pkg-utils": "^6.
|
|
76
|
-
"@sanity/schema": "^3.
|
|
77
|
-
"@sanity/types": "^3.
|
|
75
|
+
"@sanity/pkg-utils": "^6.12.0",
|
|
76
|
+
"@sanity/schema": "^3.67.1",
|
|
77
|
+
"@sanity/types": "^3.67.1",
|
|
78
78
|
"@testing-library/jest-dom": "^6.6.3",
|
|
79
79
|
"@testing-library/react": "^16.1.0",
|
|
80
80
|
"@types/debug": "^4.1.5",
|
|
81
81
|
"@types/lodash": "^4.17.13",
|
|
82
82
|
"@types/lodash.startcase": "^4.4.9",
|
|
83
83
|
"@types/react": "^19.0.1",
|
|
84
|
-
"@types/react-dom": "^19.0.
|
|
84
|
+
"@types/react-dom": "^19.0.2",
|
|
85
85
|
"@typescript-eslint/eslint-plugin": "^8.17.0",
|
|
86
86
|
"@typescript-eslint/parser": "^8.17.0",
|
|
87
87
|
"@vitejs/plugin-react": "^4.3.4",
|
|
@@ -100,12 +100,12 @@
|
|
|
100
100
|
"vite": "^6.0.3",
|
|
101
101
|
"vitest": "^2.1.8",
|
|
102
102
|
"vitest-browser-react": "^0.0.4",
|
|
103
|
-
"racejar": "1.1.
|
|
103
|
+
"racejar": "1.1.1"
|
|
104
104
|
},
|
|
105
105
|
"peerDependencies": {
|
|
106
|
-
"@sanity/block-tools": "^3.
|
|
107
|
-
"@sanity/schema": "^3.
|
|
108
|
-
"@sanity/types": "^3.
|
|
106
|
+
"@sanity/block-tools": "^3.67.1",
|
|
107
|
+
"@sanity/schema": "^3.67.1",
|
|
108
|
+
"@sanity/types": "^3.67.1",
|
|
109
109
|
"react": "^16.9 || ^17 || ^18 || ^19",
|
|
110
110
|
"rxjs": "^7.8.1",
|
|
111
111
|
"styled-components": "^6.1.13"
|
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
getFirstBlock,
|
|
3
|
-
getLastBlock,
|
|
4
|
-
getSelectedBlocks,
|
|
5
|
-
} from '../selectors/selectors'
|
|
1
|
+
import * as selectors from '../selectors'
|
|
6
2
|
import {isHotkey} from '../utils/is-hotkey'
|
|
7
3
|
import {defineBehavior} from './behavior.types'
|
|
8
4
|
|
|
@@ -26,8 +22,8 @@ export function createCodeEditorBehaviors(config: CodeEditorBehaviorsConfig) {
|
|
|
26
22
|
config.moveBlockUpShortcut,
|
|
27
23
|
event.keyboardEvent,
|
|
28
24
|
)
|
|
29
|
-
const firstBlock = getFirstBlock({context})
|
|
30
|
-
const selectedBlocks = getSelectedBlocks({context})
|
|
25
|
+
const firstBlock = selectors.getFirstBlock({context})
|
|
26
|
+
const selectedBlocks = selectors.getSelectedBlocks({context})
|
|
31
27
|
const blocksAbove =
|
|
32
28
|
firstBlock?.node._key !== selectedBlocks[0]?.node._key
|
|
33
29
|
|
|
@@ -52,8 +48,8 @@ export function createCodeEditorBehaviors(config: CodeEditorBehaviorsConfig) {
|
|
|
52
48
|
config.moveBlockDownShortcut,
|
|
53
49
|
event.keyboardEvent,
|
|
54
50
|
)
|
|
55
|
-
const lastBlock = getLastBlock({context})
|
|
56
|
-
const selectedBlocks = getSelectedBlocks({context})
|
|
51
|
+
const lastBlock = selectors.getLastBlock({context})
|
|
52
|
+
const selectedBlocks = selectors.getSelectedBlocks({context})
|
|
57
53
|
const blocksBelow =
|
|
58
54
|
lastBlock?.node._key !==
|
|
59
55
|
selectedBlocks[selectedBlocks.length - 1]?.node._key
|
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
import {isPortableTextTextBlock} from '@sanity/types'
|
|
2
2
|
import {isEmptyTextBlock} from '../editor/utils/utils'
|
|
3
|
-
import
|
|
4
|
-
getFocusBlockObject,
|
|
5
|
-
getFocusTextBlock,
|
|
6
|
-
getNextBlock,
|
|
7
|
-
getPreviousBlock,
|
|
8
|
-
selectionIsCollapsed,
|
|
9
|
-
} from '../selectors/selectors'
|
|
3
|
+
import * as selectors from '../selectors'
|
|
10
4
|
import {isHotkey} from '../utils/is-hotkey'
|
|
11
5
|
import {defineBehavior} from './behavior.types'
|
|
12
6
|
|
|
@@ -14,8 +8,8 @@ const arrowDownOnLonelyBlockObject = defineBehavior({
|
|
|
14
8
|
on: 'key.down',
|
|
15
9
|
guard: ({context, event}) => {
|
|
16
10
|
const isArrowDown = isHotkey('ArrowDown', event.keyboardEvent)
|
|
17
|
-
const focusBlockObject = getFocusBlockObject({context})
|
|
18
|
-
const nextBlock = getNextBlock({context})
|
|
11
|
+
const focusBlockObject = selectors.getFocusBlockObject({context})
|
|
12
|
+
const nextBlock = selectors.getNextBlock({context})
|
|
19
13
|
|
|
20
14
|
return isArrowDown && focusBlockObject && !nextBlock
|
|
21
15
|
},
|
|
@@ -26,8 +20,8 @@ const arrowUpOnLonelyBlockObject = defineBehavior({
|
|
|
26
20
|
on: 'key.down',
|
|
27
21
|
guard: ({context, event}) => {
|
|
28
22
|
const isArrowUp = isHotkey('ArrowUp', event.keyboardEvent)
|
|
29
|
-
const focusBlockObject = getFocusBlockObject({context})
|
|
30
|
-
const previousBlock = getPreviousBlock({context})
|
|
23
|
+
const focusBlockObject = selectors.getFocusBlockObject({context})
|
|
24
|
+
const previousBlock = selectors.getPreviousBlock({context})
|
|
31
25
|
|
|
32
26
|
return isArrowUp && focusBlockObject && !previousBlock
|
|
33
27
|
},
|
|
@@ -42,8 +36,8 @@ const arrowUpOnLonelyBlockObject = defineBehavior({
|
|
|
42
36
|
const breakingBlockObject = defineBehavior({
|
|
43
37
|
on: 'insert.break',
|
|
44
38
|
guard: ({context}) => {
|
|
45
|
-
const focusBlockObject = getFocusBlockObject({context})
|
|
46
|
-
const collapsedSelection =
|
|
39
|
+
const focusBlockObject = selectors.getFocusBlockObject({context})
|
|
40
|
+
const collapsedSelection = selectors.isSelectionCollapsed({context})
|
|
47
41
|
|
|
48
42
|
return collapsedSelection && focusBlockObject !== undefined
|
|
49
43
|
},
|
|
@@ -53,9 +47,9 @@ const breakingBlockObject = defineBehavior({
|
|
|
53
47
|
const deletingEmptyTextBlockAfterBlockObject = defineBehavior({
|
|
54
48
|
on: 'delete.backward',
|
|
55
49
|
guard: ({context}) => {
|
|
56
|
-
const focusTextBlock = getFocusTextBlock({context})
|
|
57
|
-
const selectionCollapsed =
|
|
58
|
-
const previousBlock = getPreviousBlock({context})
|
|
50
|
+
const focusTextBlock = selectors.getFocusTextBlock({context})
|
|
51
|
+
const selectionCollapsed = selectors.isSelectionCollapsed({context})
|
|
52
|
+
const previousBlock = selectors.getPreviousBlock({context})
|
|
59
53
|
|
|
60
54
|
if (!focusTextBlock || !selectionCollapsed || !previousBlock) {
|
|
61
55
|
return false
|
|
@@ -90,9 +84,9 @@ const deletingEmptyTextBlockAfterBlockObject = defineBehavior({
|
|
|
90
84
|
const deletingEmptyTextBlockBeforeBlockObject = defineBehavior({
|
|
91
85
|
on: 'delete.forward',
|
|
92
86
|
guard: ({context}) => {
|
|
93
|
-
const focusTextBlock = getFocusTextBlock({context})
|
|
94
|
-
const selectionCollapsed =
|
|
95
|
-
const nextBlock = getNextBlock({context})
|
|
87
|
+
const focusTextBlock = selectors.getFocusTextBlock({context})
|
|
88
|
+
const selectionCollapsed = selectors.isSelectionCollapsed({context})
|
|
89
|
+
const nextBlock = selectors.getNextBlock({context})
|
|
96
90
|
|
|
97
91
|
if (!focusTextBlock || !selectionCollapsed || !nextBlock) {
|
|
98
92
|
return false
|
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
import {createGuards} from '../behavior-actions/behavior.guards'
|
|
2
2
|
import {isEmptyTextBlock} from '../editor/utils/utils'
|
|
3
|
-
import
|
|
4
|
-
getFocusListBlock,
|
|
5
|
-
getFocusSpan,
|
|
6
|
-
getFocusTextBlock,
|
|
7
|
-
getSelectedBlocks,
|
|
8
|
-
selectionIsCollapsed,
|
|
9
|
-
} from '../selectors/selectors'
|
|
3
|
+
import * as selectors from '../selectors'
|
|
10
4
|
import {isHotkey} from '../utils/is-hotkey'
|
|
11
5
|
import {defineBehavior} from './behavior.types'
|
|
12
6
|
|
|
@@ -15,9 +9,9 @@ const MAX_LIST_LEVEL = 10
|
|
|
15
9
|
const clearListOnBackspace = defineBehavior({
|
|
16
10
|
on: 'delete.backward',
|
|
17
11
|
guard: ({context}) => {
|
|
18
|
-
const selectionCollapsed =
|
|
19
|
-
const focusTextBlock = getFocusTextBlock({context})
|
|
20
|
-
const focusSpan = getFocusSpan({context})
|
|
12
|
+
const selectionCollapsed = selectors.isSelectionCollapsed({context})
|
|
13
|
+
const focusTextBlock = selectors.getFocusTextBlock({context})
|
|
14
|
+
const focusSpan = selectors.getFocusSpan({context})
|
|
21
15
|
|
|
22
16
|
if (!selectionCollapsed || !focusTextBlock || !focusSpan) {
|
|
23
17
|
return false
|
|
@@ -47,9 +41,9 @@ const clearListOnBackspace = defineBehavior({
|
|
|
47
41
|
const unindentListOnBackspace = defineBehavior({
|
|
48
42
|
on: 'delete.backward',
|
|
49
43
|
guard: ({context}) => {
|
|
50
|
-
const selectionCollapsed =
|
|
51
|
-
const focusTextBlock = getFocusTextBlock({context})
|
|
52
|
-
const focusSpan = getFocusSpan({context})
|
|
44
|
+
const selectionCollapsed = selectors.isSelectionCollapsed({context})
|
|
45
|
+
const focusTextBlock = selectors.getFocusTextBlock({context})
|
|
46
|
+
const focusSpan = selectors.getFocusSpan({context})
|
|
53
47
|
|
|
54
48
|
if (!selectionCollapsed || !focusTextBlock || !focusSpan) {
|
|
55
49
|
return false
|
|
@@ -83,8 +77,8 @@ const unindentListOnBackspace = defineBehavior({
|
|
|
83
77
|
const clearListOnEnter = defineBehavior({
|
|
84
78
|
on: 'insert.break',
|
|
85
79
|
guard: ({context}) => {
|
|
86
|
-
const selectionCollapsed =
|
|
87
|
-
const focusListBlock = getFocusListBlock({context})
|
|
80
|
+
const selectionCollapsed = selectors.isSelectionCollapsed({context})
|
|
81
|
+
const focusListBlock = selectors.getFocusListBlock({context})
|
|
88
82
|
|
|
89
83
|
if (
|
|
90
84
|
!selectionCollapsed ||
|
|
@@ -116,7 +110,7 @@ const indentListOnTab = defineBehavior({
|
|
|
116
110
|
return false
|
|
117
111
|
}
|
|
118
112
|
|
|
119
|
-
const selectedBlocks = getSelectedBlocks({context})
|
|
113
|
+
const selectedBlocks = selectors.getSelectedBlocks({context})
|
|
120
114
|
const guards = createGuards(context)
|
|
121
115
|
const selectedListBlocks = selectedBlocks.flatMap((block) =>
|
|
122
116
|
guards.isListBlock(block.node)
|
|
@@ -157,7 +151,7 @@ const unindentListOnShiftTab = defineBehavior({
|
|
|
157
151
|
return false
|
|
158
152
|
}
|
|
159
153
|
|
|
160
|
-
const selectedBlocks = getSelectedBlocks({context})
|
|
154
|
+
const selectedBlocks = selectors.getSelectedBlocks({context})
|
|
161
155
|
const guards = createGuards(context)
|
|
162
156
|
const selectedListBlocks = selectedBlocks.flatMap((block) =>
|
|
163
157
|
guards.isListBlock(block.node)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type {EditorSchema} from '../editor/define-schema'
|
|
2
|
-
import
|
|
2
|
+
import * as selectors from '../selectors'
|
|
3
3
|
import {defineBehavior} from './behavior.types'
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -19,7 +19,7 @@ export function createLinkBehaviors(config: LinkBehaviorsConfig) {
|
|
|
19
19
|
const pasteLinkOnSelection = defineBehavior({
|
|
20
20
|
on: 'paste',
|
|
21
21
|
guard: ({context, event}) => {
|
|
22
|
-
const selectionCollapsed =
|
|
22
|
+
const selectionCollapsed = selectors.isSelectionCollapsed({context})
|
|
23
23
|
const text = event.data.getData('text/plain')
|
|
24
24
|
const url = looksLikeUrl(text) ? text : undefined
|
|
25
25
|
const annotation =
|
|
@@ -45,8 +45,8 @@ export function createLinkBehaviors(config: LinkBehaviorsConfig) {
|
|
|
45
45
|
const pasteLinkAtCaret = defineBehavior({
|
|
46
46
|
on: 'paste',
|
|
47
47
|
guard: ({context, event}) => {
|
|
48
|
-
const focusSpan = getFocusSpan({context})
|
|
49
|
-
const selectionCollapsed =
|
|
48
|
+
const focusSpan = selectors.getFocusSpan({context})
|
|
49
|
+
const selectionCollapsed = selectors.isSelectionCollapsed({context})
|
|
50
50
|
|
|
51
51
|
if (!focusSpan || !selectionCollapsed) {
|
|
52
52
|
return false
|
|
@@ -2,13 +2,8 @@ import {isPortableTextTextBlock} from '@sanity/types'
|
|
|
2
2
|
import type {EditorSchema} from '../editor/define-schema'
|
|
3
3
|
import {getTextBlockText} from '../editor/utils/utils'
|
|
4
4
|
import {spanSelectionPointToBlockOffset} from '../editor/utils/utils.block-offset'
|
|
5
|
+
import * as selectors from '../selectors'
|
|
5
6
|
import {getBlockTextBefore} from '../selectors/selector.get-text-before'
|
|
6
|
-
import {
|
|
7
|
-
getFocusBlock,
|
|
8
|
-
getFocusSpan,
|
|
9
|
-
getFocusTextBlock,
|
|
10
|
-
selectionIsCollapsed,
|
|
11
|
-
} from '../selectors/selectors'
|
|
12
7
|
import {defineBehavior} from './behavior.types'
|
|
13
8
|
|
|
14
9
|
/**
|
|
@@ -41,9 +36,9 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
41
36
|
return false
|
|
42
37
|
}
|
|
43
38
|
|
|
44
|
-
const selectionCollapsed =
|
|
45
|
-
const focusTextBlock = getFocusTextBlock({context})
|
|
46
|
-
const focusSpan = getFocusSpan({context})
|
|
39
|
+
const selectionCollapsed = selectors.isSelectionCollapsed({context})
|
|
40
|
+
const focusTextBlock = selectors.getFocusTextBlock({context})
|
|
41
|
+
const focusSpan = selectors.getFocusSpan({context})
|
|
47
42
|
|
|
48
43
|
if (!selectionCollapsed || !focusTextBlock || !focusSpan) {
|
|
49
44
|
return false
|
|
@@ -129,8 +124,8 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
129
124
|
}
|
|
130
125
|
|
|
131
126
|
const hrObject = config.horizontalRuleObject?.(context)
|
|
132
|
-
const focusBlock = getFocusTextBlock({context})
|
|
133
|
-
const selectionCollapsed =
|
|
127
|
+
const focusBlock = selectors.getFocusTextBlock({context})
|
|
128
|
+
const selectionCollapsed = selectors.isSelectionCollapsed({context})
|
|
134
129
|
|
|
135
130
|
if (!hrObject || !focusBlock || !selectionCollapsed) {
|
|
136
131
|
return false
|
|
@@ -181,7 +176,7 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
181
176
|
const hrRegExp = /^(---)$|(___)$|(\*\*\*)$/gm
|
|
182
177
|
const hrCharacters = text.match(hrRegExp)?.[0]
|
|
183
178
|
const hrObject = config.horizontalRuleObject?.(context)
|
|
184
|
-
const focusBlock = getFocusBlock({context})
|
|
179
|
+
const focusBlock = selectors.getFocusBlock({context})
|
|
185
180
|
|
|
186
181
|
if (!hrCharacters || !hrObject || !focusBlock) {
|
|
187
182
|
return false
|
|
@@ -229,9 +224,9 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
229
224
|
return false
|
|
230
225
|
}
|
|
231
226
|
|
|
232
|
-
const selectionCollapsed =
|
|
233
|
-
const focusTextBlock = getFocusTextBlock({context})
|
|
234
|
-
const focusSpan = getFocusSpan({context})
|
|
227
|
+
const selectionCollapsed = selectors.isSelectionCollapsed({context})
|
|
228
|
+
const focusTextBlock = selectors.getFocusTextBlock({context})
|
|
229
|
+
const focusSpan = selectors.getFocusSpan({context})
|
|
235
230
|
|
|
236
231
|
if (!selectionCollapsed || !focusTextBlock || !focusSpan) {
|
|
237
232
|
return false
|
|
@@ -309,9 +304,9 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
309
304
|
const clearStyleOnBackspace = defineBehavior({
|
|
310
305
|
on: 'delete.backward',
|
|
311
306
|
guard: ({context}) => {
|
|
312
|
-
const selectionCollapsed =
|
|
313
|
-
const focusTextBlock = getFocusTextBlock({context})
|
|
314
|
-
const focusSpan = getFocusSpan({context})
|
|
307
|
+
const selectionCollapsed = selectors.isSelectionCollapsed({context})
|
|
308
|
+
const focusTextBlock = selectors.getFocusTextBlock({context})
|
|
309
|
+
const focusSpan = selectors.getFocusSpan({context})
|
|
315
310
|
|
|
316
311
|
if (!selectionCollapsed || !focusTextBlock || !focusSpan) {
|
|
317
312
|
return false
|
|
@@ -352,9 +347,9 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
352
347
|
return false
|
|
353
348
|
}
|
|
354
349
|
|
|
355
|
-
const selectionCollapsed =
|
|
356
|
-
const focusTextBlock = getFocusTextBlock({context})
|
|
357
|
-
const focusSpan = getFocusSpan({context})
|
|
350
|
+
const selectionCollapsed = selectors.isSelectionCollapsed({context})
|
|
351
|
+
const focusTextBlock = selectors.getFocusTextBlock({context})
|
|
352
|
+
const focusSpan = selectors.getFocusSpan({context})
|
|
358
353
|
|
|
359
354
|
if (!selectionCollapsed || !focusTextBlock || !focusSpan) {
|
|
360
355
|
return false
|
package/src/editor/Editable.tsx
CHANGED
|
@@ -159,7 +159,9 @@ export const PortableTextEditable = forwardRef<
|
|
|
159
159
|
const rangeDecorationsRef = useRef(rangeDecorations)
|
|
160
160
|
|
|
161
161
|
const editorActor = useContext(EditorActorContext)
|
|
162
|
-
const readOnly = useSelector(editorActor, (s) =>
|
|
162
|
+
const readOnly = useSelector(editorActor, (s) =>
|
|
163
|
+
s.matches({'edit mode': 'read only'}),
|
|
164
|
+
)
|
|
163
165
|
const schemaTypes = useSelector(editorActor, (s) => s.context.schema)
|
|
164
166
|
const slateEditor = useSlate()
|
|
165
167
|
|
|
@@ -366,6 +368,7 @@ export const PortableTextEditable = forwardRef<
|
|
|
366
368
|
// Restore selection from props when the editor has been initialized properly with it's value
|
|
367
369
|
useEffect(() => {
|
|
368
370
|
const onReady = editorActor.on('ready', () => {
|
|
371
|
+
syncRangeDecorations()
|
|
369
372
|
restoreSelectionFromProps()
|
|
370
373
|
})
|
|
371
374
|
const onInvalidValue = editorActor.on('invalid value', () => {
|
|
@@ -380,7 +383,7 @@ export const PortableTextEditable = forwardRef<
|
|
|
380
383
|
onInvalidValue.unsubscribe()
|
|
381
384
|
onValueChanged.unsubscribe()
|
|
382
385
|
}
|
|
383
|
-
}, [editorActor, restoreSelectionFromProps])
|
|
386
|
+
}, [editorActor, restoreSelectionFromProps, syncRangeDecorations])
|
|
384
387
|
|
|
385
388
|
// Restore selection from props when it changes
|
|
386
389
|
useEffect(() => {
|
|
@@ -407,9 +410,13 @@ export const PortableTextEditable = forwardRef<
|
|
|
407
410
|
|
|
408
411
|
// Sync range decorations after an operation is applied
|
|
409
412
|
useEffect(() => {
|
|
410
|
-
const teardown = withSyncRangeDecorations(
|
|
413
|
+
const teardown = withSyncRangeDecorations({
|
|
414
|
+
editorActor,
|
|
415
|
+
slateEditor,
|
|
416
|
+
syncRangeDecorations,
|
|
417
|
+
})
|
|
411
418
|
return () => teardown()
|
|
412
|
-
}, [slateEditor, syncRangeDecorations])
|
|
419
|
+
}, [editorActor, slateEditor, syncRangeDecorations])
|
|
413
420
|
|
|
414
421
|
// Handle from props onCopy function
|
|
415
422
|
const handleCopy = useCallback(
|
|
@@ -238,8 +238,6 @@ export class PortableTextEditor extends Component<
|
|
|
238
238
|
/>
|
|
239
239
|
<Synchronizer
|
|
240
240
|
editorActor={this.editor._internal.editorActor}
|
|
241
|
-
getValue={this.editor._internal.editable.getValue}
|
|
242
|
-
portableTextEditor={this}
|
|
243
241
|
slateEditor={this.editor._internal.slateEditor.instance}
|
|
244
242
|
/>
|
|
245
243
|
<EditorActorContext.Provider value={this.editor._internal.editorActor}>
|
|
@@ -496,7 +494,8 @@ export function RouteEventsToChanges(props: {
|
|
|
496
494
|
case 'list item.toggle':
|
|
497
495
|
case 'style.toggle':
|
|
498
496
|
case 'patches':
|
|
499
|
-
case '
|
|
497
|
+
case 'editable':
|
|
498
|
+
case 'read only':
|
|
500
499
|
break
|
|
501
500
|
default:
|
|
502
501
|
handleChange(event)
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import {render, waitFor} from '@testing-library/react'
|
|
2
2
|
import {createRef, type RefObject} from 'react'
|
|
3
3
|
import {describe, expect, it, vi} from 'vitest'
|
|
4
|
-
import {
|
|
5
|
-
PortableTextEditorTester,
|
|
6
|
-
schemaType,
|
|
7
|
-
} from '../__tests__/PortableTextEditorTester'
|
|
8
4
|
import {PortableTextEditor} from '../PortableTextEditor'
|
|
5
|
+
import {PortableTextEditorTester, schemaType} from './PortableTextEditorTester'
|
|
9
6
|
|
|
10
7
|
const initialValue = [
|
|
11
8
|
{
|
|
@@ -52,6 +49,17 @@ describe('useSyncValue', () => {
|
|
|
52
49
|
value={initialValue}
|
|
53
50
|
/>,
|
|
54
51
|
)
|
|
52
|
+
|
|
53
|
+
await waitFor(() => {
|
|
54
|
+
if (editorRef.current) {
|
|
55
|
+
expect(onChange).toHaveBeenCalledWith({
|
|
56
|
+
type: 'value',
|
|
57
|
+
value: initialValue,
|
|
58
|
+
})
|
|
59
|
+
expect(onChange).toHaveBeenCalledWith({type: 'ready'})
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
|
|
55
63
|
rerender(
|
|
56
64
|
<PortableTextEditorTester
|
|
57
65
|
onChange={onChange}
|
|
@@ -60,6 +68,7 @@ describe('useSyncValue', () => {
|
|
|
60
68
|
value={syncedValue}
|
|
61
69
|
/>,
|
|
62
70
|
)
|
|
71
|
+
|
|
63
72
|
await waitFor(() => {
|
|
64
73
|
if (editorRef.current) {
|
|
65
74
|
expect(PortableTextEditor.getValue(editorRef.current)).toEqual(
|
|
@@ -68,6 +77,7 @@ describe('useSyncValue', () => {
|
|
|
68
77
|
}
|
|
69
78
|
})
|
|
70
79
|
})
|
|
80
|
+
|
|
71
81
|
it('replaces span nodes with different keys inside the same children array', async () => {
|
|
72
82
|
const editorRef: RefObject<PortableTextEditor | null> = createRef()
|
|
73
83
|
const onChange = vi.fn()
|
|
@@ -95,6 +105,17 @@ describe('useSyncValue', () => {
|
|
|
95
105
|
value={initialValue}
|
|
96
106
|
/>,
|
|
97
107
|
)
|
|
108
|
+
|
|
109
|
+
await waitFor(() => {
|
|
110
|
+
if (editorRef.current) {
|
|
111
|
+
expect(onChange).toHaveBeenCalledWith({
|
|
112
|
+
type: 'value',
|
|
113
|
+
value: initialValue,
|
|
114
|
+
})
|
|
115
|
+
expect(onChange).toHaveBeenCalledWith({type: 'ready'})
|
|
116
|
+
}
|
|
117
|
+
})
|
|
118
|
+
|
|
98
119
|
rerender(
|
|
99
120
|
<PortableTextEditorTester
|
|
100
121
|
onChange={onChange}
|
|
@@ -103,27 +124,25 @@ describe('useSyncValue', () => {
|
|
|
103
124
|
value={syncedValue}
|
|
104
125
|
/>,
|
|
105
126
|
)
|
|
127
|
+
|
|
106
128
|
await waitFor(() => {
|
|
107
129
|
if (editorRef.current) {
|
|
108
|
-
expect(PortableTextEditor.getValue(editorRef.current))
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
},
|
|
125
|
-
]
|
|
126
|
-
`)
|
|
130
|
+
expect(PortableTextEditor.getValue(editorRef.current)).toEqual([
|
|
131
|
+
{
|
|
132
|
+
_key: '77071c3af231',
|
|
133
|
+
_type: 'myTestBlockType',
|
|
134
|
+
children: [
|
|
135
|
+
{
|
|
136
|
+
_key: 'c001f0e92c1f0__NEW_KEY_YA!',
|
|
137
|
+
_type: 'span',
|
|
138
|
+
marks: [],
|
|
139
|
+
text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. ',
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
markDefs: [],
|
|
143
|
+
style: 'normal',
|
|
144
|
+
},
|
|
145
|
+
])
|
|
127
146
|
}
|
|
128
147
|
})
|
|
129
148
|
})
|