@portabletext/editor 1.15.2 → 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 +30 -28
- 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 +12 -10
- 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 +35 -35
- package/lib/behaviors/index.cjs.map +1 -1
- package/lib/behaviors/index.d.cts +40 -45
- package/lib/behaviors/index.d.ts +40 -45
- package/lib/behaviors/index.js +20 -20
- package/lib/behaviors/index.js.map +1 -1
- package/lib/index.cjs +868 -542
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.cts +3791 -4503
- package/lib/index.d.ts +3791 -4503
- package/lib/index.js +865 -541
- 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 +62 -17
- package/lib/selectors/index.d.ts +62 -17
- package/lib/selectors/index.js +154 -3
- package/lib/selectors/index.js.map +1 -1
- package/package.json +11 -11
- package/src/behavior-actions/behavior.action-utils.insert-block.ts +3 -5
- package/src/behavior-actions/behavior.actions.ts +6 -6
- package/src/behavior-actions/behavior.guards.ts +2 -6
- package/src/behaviors/behavior.code-editor.ts +5 -9
- package/src/behaviors/behavior.core.block-objects.ts +14 -20
- package/src/behaviors/behavior.core.lists.ts +13 -19
- package/src/behaviors/behavior.links.ts +6 -6
- package/src/behaviors/behavior.markdown.ts +27 -40
- package/src/behaviors/behavior.types.ts +7 -7
- package/src/behaviors/index.ts +1 -0
- package/src/editor/Editable.tsx +11 -4
- package/src/editor/PortableTextEditor.tsx +4 -5
- package/src/editor/{hooks/useSyncValue.test.tsx → __tests__/sync-value.test.tsx} +42 -23
- package/src/editor/components/Synchronizer.tsx +53 -80
- package/src/{utils/getPortableTextMemberSchemaTypes.ts → editor/create-editor-schema.ts} +3 -3
- package/src/editor/create-editor.ts +2 -2
- package/src/editor/define-schema.ts +8 -3
- package/src/editor/editor-machine.ts +136 -104
- package/src/editor/editor-provider.tsx +0 -3
- package/src/editor/editor-selector.ts +6 -13
- package/src/editor/editor-snapshot.ts +5 -6
- 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/index.ts +3 -5
- package/src/selectors/_exports/index.ts +1 -0
- package/src/selectors/index.ts +10 -4
- 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/__tests__/operationToPatches.test.ts +2 -2
- package/src/utils/__tests__/patchToOperations.test.ts +2 -2
- package/src/utils/__tests__/values.test.ts +2 -2
- 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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/selectors/selector.get-active-list-item.ts"],"sourcesContent":["import type {PortableTextListBlock} from '@sanity/types'\nimport {createGuards} from '../behavior-actions/behavior.guards'\nimport type {EditorSelector} from '../editor/editor-selector'\nimport {getSelectedBlocks} from './selectors'\n\n/**\n * @alpha\n */\nexport const getActiveListItem: EditorSelector<\n PortableTextListBlock['listItem'] | undefined\n> = ({context}) => {\n if (!context.selection) {\n return undefined\n }\n\n const guards = createGuards(context)\n const selectedBlocks = getSelectedBlocks({context}).map((block) => block.node)\n const selectedTextBlocks = selectedBlocks.filter(guards.isTextBlock)\n\n const firstTextBlock = selectedTextBlocks.at(0)\n\n if (!firstTextBlock) {\n return undefined\n }\n\n const firstListItem = firstTextBlock.listItem\n\n if (!firstListItem) {\n return undefined\n }\n\n if (selectedTextBlocks.every((block) => block.listItem === firstListItem)) {\n return firstListItem\n }\n\n return undefined\n}\n"],"names":["getActiveListItem","context","selection","guards","createGuards","selectedTextBlocks","getSelectedBlocks","map","block","node","filter","isTextBlock","firstTextBlock","at","firstListItem","listItem","every"],"mappings":";;;AAQO,MAAMA,oBAETA,CAAC;AAAA,EAACC;AAAO,MAAM;AACjB,MAAI,CAACA,QAAQC;AACX;AAGF,QAAMC,SAASC,aAAaH,OAAO,GAE7BI,qBADiBC,kBAAkB;AAAA,IAACL;AAAAA,EAAQ,CAAA,EAAEM,IAAKC,CAAAA,UAAUA,MAAMC,IAAI,EACnCC,OAAOP,OAAOQ,WAAW,GAE7DC,iBAAiBP,mBAAmBQ,GAAG,CAAC;AAE9C,MAAI,CAACD;AACH;AAGF,QAAME,gBAAgBF,eAAeG;AAErC,MAAKD,iBAIDT,mBAAmBW,MAAOR,CAAUA,UAAAA,MAAMO,aAAaD,aAAa;AAC/DA,WAAAA;AAIX;"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/selectors/selector.get-active-list-item.ts","../../src/selectors/selector.get-active-style.ts","../../src/selectors/selector.get-selected-spans.ts","../../src/selectors/selector.is-active-annotation.ts","../../src/selectors/selector.is-selection-expanded.ts","../../src/selectors/selector.is-active-decorator.ts","../../src/selectors/selector.is-active-list-item.ts","../../src/selectors/selector.is-active-style.ts"],"sourcesContent":["import type {PortableTextListBlock} from '@sanity/types'\nimport {createGuards} from '../behavior-actions/behavior.guards'\nimport type {EditorSelector} from '../editor/editor-selector'\nimport {getSelectedBlocks} from './selectors'\n\n/**\n * @alpha\n */\nexport const getActiveListItem: EditorSelector<\n PortableTextListBlock['listItem'] | undefined\n> = ({context}) => {\n if (!context.selection) {\n return undefined\n }\n\n const guards = createGuards(context)\n const selectedBlocks = getSelectedBlocks({context}).map((block) => block.node)\n const selectedTextBlocks = selectedBlocks.filter(guards.isTextBlock)\n\n const firstTextBlock = selectedTextBlocks.at(0)\n\n if (!firstTextBlock) {\n return undefined\n }\n\n const firstListItem = firstTextBlock.listItem\n\n if (!firstListItem) {\n return undefined\n }\n\n if (selectedTextBlocks.every((block) => block.listItem === firstListItem)) {\n return firstListItem\n }\n\n return undefined\n}\n","import type {PortableTextTextBlock} from '@sanity/types'\nimport {createGuards} from '../behavior-actions/behavior.guards'\nimport type {EditorSelector} from '../editor/editor-selector'\nimport {getSelectedBlocks} from './selectors'\n\n/**\n * @alpha\n */\nexport const getActiveStyle: EditorSelector<PortableTextTextBlock['style']> = ({\n context,\n}) => {\n if (!context.selection) {\n return undefined\n }\n\n const guards = createGuards(context)\n const selectedBlocks = getSelectedBlocks({context}).map((block) => block.node)\n const selectedTextBlocks = selectedBlocks.filter(guards.isTextBlock)\n\n const firstTextBlock = selectedTextBlocks.at(0)\n\n if (!firstTextBlock) {\n return undefined\n }\n\n const firstStyle = firstTextBlock.style\n\n if (!firstStyle) {\n return undefined\n }\n\n if (selectedTextBlocks.every((block) => block.style === firstStyle)) {\n return firstStyle\n }\n\n return undefined\n}\n","import {\n isKeySegment,\n isPortableTextSpan,\n isPortableTextTextBlock,\n type KeyedSegment,\n type PortableTextSpan,\n} from '@sanity/types'\nimport type {EditorSelector} from '../editor/editor-selector'\n\n/**\n * @alpha\n */\nexport const getSelectedSpans: EditorSelector<\n Array<{\n node: PortableTextSpan\n path: [KeyedSegment, 'children', KeyedSegment]\n }>\n> = ({context}) => {\n if (!context.selection) {\n return []\n }\n\n const selectedSpans: Array<{\n node: PortableTextSpan\n path: [KeyedSegment, 'children', KeyedSegment]\n }> = []\n\n const startPoint = context.selection.backward\n ? context.selection.focus\n : context.selection.anchor\n const endPoint = context.selection.backward\n ? context.selection.anchor\n : context.selection.focus\n\n const startBlockKey = isKeySegment(startPoint.path[0])\n ? startPoint.path[0]._key\n : undefined\n const endBlockKey = isKeySegment(endPoint.path[0])\n ? endPoint.path[0]._key\n : undefined\n\n if (!startBlockKey || !endBlockKey) {\n return selectedSpans\n }\n\n const startSpanKey = isKeySegment(startPoint.path[2])\n ? startPoint.path[2]._key\n : undefined\n const endSpanKey = isKeySegment(endPoint.path[2])\n ? endPoint.path[2]._key\n : undefined\n\n for (const block of context.value) {\n if (!isPortableTextTextBlock(block)) {\n continue\n }\n\n if (block._key === startBlockKey) {\n for (const child of block.children) {\n if (!isPortableTextSpan(child)) {\n continue\n }\n\n if (startSpanKey && child._key === startSpanKey) {\n selectedSpans.push({\n node: child,\n path: [{_key: block._key}, 'children', {_key: child._key}],\n })\n\n if (startBlockKey === endBlockKey) {\n break\n }\n }\n\n if (endSpanKey && child._key === endSpanKey) {\n selectedSpans.push({\n node: child,\n path: [{_key: block._key}, 'children', {_key: child._key}],\n })\n break\n }\n\n if (selectedSpans.length > 0) {\n selectedSpans.push({\n node: child,\n path: [{_key: block._key}, 'children', {_key: child._key}],\n })\n }\n }\n\n if (startBlockKey === endBlockKey) {\n break\n }\n\n continue\n }\n\n if (block._key === endBlockKey) {\n for (const child of block.children) {\n if (!isPortableTextSpan(child)) {\n continue\n }\n\n if (endSpanKey && child._key === endSpanKey) {\n selectedSpans.push({\n node: child,\n path: [{_key: block._key}, 'children', {_key: child._key}],\n })\n break\n }\n\n selectedSpans.push({\n node: child,\n path: [{_key: block._key}, 'children', {_key: child._key}],\n })\n }\n\n break\n }\n\n if (selectedSpans.length > 0) {\n for (const child of block.children) {\n if (!isPortableTextSpan(child)) {\n continue\n }\n\n selectedSpans.push({\n node: child,\n path: [{_key: block._key}, 'children', {_key: child._key}],\n })\n }\n }\n }\n\n return selectedSpans\n}\n","import {isPortableTextTextBlock} from '@sanity/types'\nimport type {EditorSelector} from '../editor/editor-selector'\nimport {getSelectedSpans} from './selector.get-selected-spans'\nimport {getSelectedBlocks} from './selectors'\n\n/**\n * @alpha\n */\nexport function isActiveAnnotation(\n annotation: string,\n): EditorSelector<boolean> {\n return (snapshot) => {\n if (!snapshot.context.selection) {\n return false\n }\n\n const selectedBlocks = getSelectedBlocks(snapshot)\n const selectedSpans = getSelectedSpans(snapshot)\n\n if (selectedSpans.length === 0) {\n return false\n }\n\n if (\n selectedSpans.some(\n (span) => !span.node.marks || span.node.marks?.length === 0,\n )\n ) {\n return false\n }\n\n const selectionMarkDefs = selectedBlocks.flatMap((block) =>\n isPortableTextTextBlock(block.node) ? (block.node.markDefs ?? []) : [],\n )\n\n return selectedSpans.every((span) => {\n const spanMarkDefs =\n span.node.marks?.flatMap((mark) => {\n const markDef = selectionMarkDefs.find(\n (markDef) => markDef._key === mark,\n )\n\n return markDef ? [markDef._type] : []\n }) ?? []\n\n return spanMarkDefs.includes(annotation)\n })\n }\n}\n","import type {EditorSelector} from '../editor/editor-selector'\nimport {isSelectionCollapsed} from './selector.is-selection-collapsed'\n\n/**\n * @alpha\n */\nexport const isSelectionExpanded: EditorSelector<boolean> = ({context}) => {\n return !isSelectionCollapsed({context})\n}\n","import type {EditorSelector} from '../editor/editor-selector'\nimport {getSelectedSpans} from './selector.get-selected-spans'\nimport {isSelectionExpanded} from './selector.is-selection-expanded'\n\n/**\n * @alpha\n */\nexport function isActiveDecorator(decorator: string): EditorSelector<boolean> {\n return (snapshot) => {\n if (isSelectionExpanded(snapshot)) {\n const selectedSpans = getSelectedSpans(snapshot)\n\n return (\n selectedSpans.length > 0 &&\n selectedSpans.every((span) => span.node.marks?.includes(decorator))\n )\n }\n\n return snapshot.context.activeDecorators.includes(decorator)\n }\n}\n","import type {EditorSelector} from '../editor/editor-selector'\nimport {getActiveListItem} from './selector.get-active-list-item'\n\n/**\n * @alpha\n */\nexport function isActiveListItem(listItem: string): EditorSelector<boolean> {\n return (snapshot) => {\n const activeListItem = getActiveListItem(snapshot)\n\n return activeListItem === listItem\n }\n}\n","import type {EditorSelector} from '../editor/editor-selector'\nimport {getActiveStyle} from './selector.get-active-style'\n\n/**\n * @alpha\n */\nexport function isActiveStyle(style: string): EditorSelector<boolean> {\n return (snapshot) => {\n const activeStyle = getActiveStyle(snapshot)\n\n return activeStyle === style\n }\n}\n"],"names":["getActiveListItem","context","selection","guards","createGuards","selectedTextBlocks","getSelectedBlocks","map","block","node","filter","isTextBlock","firstTextBlock","at","firstListItem","listItem","every","getActiveStyle","firstStyle","style","getSelectedSpans","selectedSpans","startPoint","backward","focus","anchor","endPoint","startBlockKey","isKeySegment","path","_key","undefined","endBlockKey","startSpanKey","endSpanKey","value","isPortableTextTextBlock","child","children","isPortableTextSpan","push","length","isActiveAnnotation","annotation","snapshot","selectedBlocks","some","span","marks","selectionMarkDefs","flatMap","markDefs","mark","markDef","find","_type","includes","isSelectionExpanded","isSelectionCollapsed","isActiveDecorator","decorator","activeDecorators","isActiveListItem","isActiveStyle"],"mappings":"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"}
|
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,6 @@
|
|
|
1
1
|
import {Editor, Transforms, type Descendant} from 'slate'
|
|
2
|
-
import type {
|
|
3
|
-
|
|
4
|
-
PortableTextSlateEditor,
|
|
5
|
-
} from '../types/editor'
|
|
2
|
+
import type {EditorSchema} from '../editor/define-schema'
|
|
3
|
+
import type {PortableTextSlateEditor} from '../types/editor'
|
|
6
4
|
import {isEqualToEmptyEditor} from '../utils/values'
|
|
7
5
|
|
|
8
6
|
export function insertBlock({
|
|
@@ -14,7 +12,7 @@ export function insertBlock({
|
|
|
14
12
|
block: Descendant
|
|
15
13
|
placement: 'auto' | 'after' | 'before'
|
|
16
14
|
editor: PortableTextSlateEditor
|
|
17
|
-
schema:
|
|
15
|
+
schema: EditorSchema
|
|
18
16
|
}) {
|
|
19
17
|
if (!editor.selection) {
|
|
20
18
|
const lastBlock = Array.from(
|
|
@@ -233,7 +233,7 @@ const behaviorActionImplementations: BehaviorActionImplementations = {
|
|
|
233
233
|
Transforms.deselect(action.editor)
|
|
234
234
|
}
|
|
235
235
|
},
|
|
236
|
-
'select
|
|
236
|
+
'select.previous block': ({action}) => {
|
|
237
237
|
if (!action.editor.selection) {
|
|
238
238
|
console.error('Unable to select previous block without a selection')
|
|
239
239
|
return
|
|
@@ -250,7 +250,7 @@ const behaviorActionImplementations: BehaviorActionImplementations = {
|
|
|
250
250
|
|
|
251
251
|
Transforms.select(action.editor, previousBlockPath)
|
|
252
252
|
},
|
|
253
|
-
'select
|
|
253
|
+
'select.next block': ({action}) => {
|
|
254
254
|
if (!action.editor.selection) {
|
|
255
255
|
console.error('Unable to select next block without a selection')
|
|
256
256
|
return
|
|
@@ -370,15 +370,15 @@ export function performAction({
|
|
|
370
370
|
})
|
|
371
371
|
break
|
|
372
372
|
}
|
|
373
|
-
case 'select
|
|
374
|
-
behaviorActionImplementations['select
|
|
373
|
+
case 'select.previous block': {
|
|
374
|
+
behaviorActionImplementations['select.previous block']({
|
|
375
375
|
context,
|
|
376
376
|
action,
|
|
377
377
|
})
|
|
378
378
|
break
|
|
379
379
|
}
|
|
380
|
-
case 'select
|
|
381
|
-
behaviorActionImplementations['select
|
|
380
|
+
case 'select.next block': {
|
|
381
|
+
behaviorActionImplementations['select.next block']({
|
|
382
382
|
context,
|
|
383
383
|
action,
|
|
384
384
|
})
|
|
@@ -4,18 +4,14 @@ import {
|
|
|
4
4
|
type PortableTextListBlock,
|
|
5
5
|
type PortableTextTextBlock,
|
|
6
6
|
} from '@sanity/types'
|
|
7
|
-
import type {
|
|
7
|
+
import type {EditorSchema} from '../editor/define-schema'
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* @alpha
|
|
11
11
|
*/
|
|
12
12
|
export type BehaviorGuards = ReturnType<typeof createGuards>
|
|
13
13
|
|
|
14
|
-
export function createGuards({
|
|
15
|
-
schema,
|
|
16
|
-
}: {
|
|
17
|
-
schema: PortableTextMemberSchemaTypes
|
|
18
|
-
}) {
|
|
14
|
+
export function createGuards({schema}: {schema: EditorSchema}) {
|
|
19
15
|
function isListBlock(block: unknown): block is PortableTextListBlock {
|
|
20
16
|
return isPortableTextListBlock(block) && block._type === schema.block.name
|
|
21
17
|
}
|
|
@@ -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,15 +20,15 @@ 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
|
},
|
|
34
28
|
actions: [
|
|
35
29
|
() => [
|
|
36
30
|
{type: 'insert.text block', placement: 'before'},
|
|
37
|
-
{type: 'select
|
|
31
|
+
{type: 'select.previous block'},
|
|
38
32
|
],
|
|
39
33
|
],
|
|
40
34
|
})
|
|
@@ -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
|
|
@@ -25,7 +19,7 @@ const clearListOnBackspace = defineBehavior({
|
|
|
25
19
|
|
|
26
20
|
const atTheBeginningOfBLock =
|
|
27
21
|
focusTextBlock.node.children[0]._key === focusSpan.node._key &&
|
|
28
|
-
context.selection
|
|
22
|
+
context.selection?.focus.offset === 0
|
|
29
23
|
|
|
30
24
|
if (atTheBeginningOfBLock && focusTextBlock.node.level === 1) {
|
|
31
25
|
return {focusTextBlock}
|
|
@@ -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
|
|
@@ -57,7 +51,7 @@ const unindentListOnBackspace = defineBehavior({
|
|
|
57
51
|
|
|
58
52
|
const atTheBeginningOfBLock =
|
|
59
53
|
focusTextBlock.node.children[0]._key === focusSpan.node._key &&
|
|
60
|
-
context.selection
|
|
54
|
+
context.selection?.focus.offset === 0
|
|
61
55
|
|
|
62
56
|
if (
|
|
63
57
|
atTheBeginningOfBLock &&
|
|
@@ -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
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import type {EditorSchema} from '../editor/define-schema'
|
|
2
|
+
import * as selectors from '../selectors'
|
|
3
3
|
import {defineBehavior} from './behavior.types'
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -7,7 +7,7 @@ import {defineBehavior} from './behavior.types'
|
|
|
7
7
|
*/
|
|
8
8
|
export type LinkBehaviorsConfig = {
|
|
9
9
|
linkAnnotation?: (context: {
|
|
10
|
-
schema:
|
|
10
|
+
schema: EditorSchema
|
|
11
11
|
url: string
|
|
12
12
|
}) => {name: string; value: {[prop: string]: unknown}} | undefined
|
|
13
13
|
}
|
|
@@ -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
|
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
import {isPortableTextTextBlock} from '@sanity/types'
|
|
2
|
+
import type {EditorSchema} from '../editor/define-schema'
|
|
2
3
|
import {getTextBlockText} from '../editor/utils/utils'
|
|
3
4
|
import {spanSelectionPointToBlockOffset} from '../editor/utils/utils.block-offset'
|
|
5
|
+
import * as selectors from '../selectors'
|
|
4
6
|
import {getBlockTextBefore} from '../selectors/selector.get-text-before'
|
|
5
|
-
import {
|
|
6
|
-
getFocusBlock,
|
|
7
|
-
getFocusSpan,
|
|
8
|
-
getFocusTextBlock,
|
|
9
|
-
selectionIsCollapsed,
|
|
10
|
-
} from '../selectors/selectors'
|
|
11
|
-
import type {PortableTextMemberSchemaTypes} from '../types/editor'
|
|
12
7
|
import {defineBehavior} from './behavior.types'
|
|
13
8
|
|
|
14
9
|
/**
|
|
@@ -16,24 +11,16 @@ import {defineBehavior} from './behavior.types'
|
|
|
16
11
|
*/
|
|
17
12
|
export type MarkdownBehaviorsConfig = {
|
|
18
13
|
horizontalRuleObject?: (context: {
|
|
19
|
-
schema:
|
|
14
|
+
schema: EditorSchema
|
|
20
15
|
}) => {name: string; value?: {[prop: string]: unknown}} | undefined
|
|
21
|
-
defaultStyle?: (context: {
|
|
22
|
-
schema: PortableTextMemberSchemaTypes
|
|
23
|
-
}) => string | undefined
|
|
16
|
+
defaultStyle?: (context: {schema: EditorSchema}) => string | undefined
|
|
24
17
|
headingStyle?: (context: {
|
|
25
|
-
schema:
|
|
18
|
+
schema: EditorSchema
|
|
26
19
|
level: number
|
|
27
20
|
}) => string | undefined
|
|
28
|
-
blockquoteStyle?: (context: {
|
|
29
|
-
|
|
30
|
-
}) => string | undefined
|
|
31
|
-
unorderedListStyle?: (context: {
|
|
32
|
-
schema: PortableTextMemberSchemaTypes
|
|
33
|
-
}) => string | undefined
|
|
34
|
-
orderedListStyle?: (context: {
|
|
35
|
-
schema: PortableTextMemberSchemaTypes
|
|
36
|
-
}) => string | undefined
|
|
21
|
+
blockquoteStyle?: (context: {schema: EditorSchema}) => string | undefined
|
|
22
|
+
unorderedListStyle?: (context: {schema: EditorSchema}) => string | undefined
|
|
23
|
+
orderedListStyle?: (context: {schema: EditorSchema}) => string | undefined
|
|
37
24
|
}
|
|
38
25
|
|
|
39
26
|
/**
|
|
@@ -49,9 +36,9 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
49
36
|
return false
|
|
50
37
|
}
|
|
51
38
|
|
|
52
|
-
const selectionCollapsed =
|
|
53
|
-
const focusTextBlock = getFocusTextBlock({context})
|
|
54
|
-
const focusSpan = getFocusSpan({context})
|
|
39
|
+
const selectionCollapsed = selectors.isSelectionCollapsed({context})
|
|
40
|
+
const focusTextBlock = selectors.getFocusTextBlock({context})
|
|
41
|
+
const focusSpan = selectors.getFocusSpan({context})
|
|
55
42
|
|
|
56
43
|
if (!selectionCollapsed || !focusTextBlock || !focusSpan) {
|
|
57
44
|
return false
|
|
@@ -65,7 +52,7 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
65
52
|
'children',
|
|
66
53
|
{_key: focusSpan.node._key},
|
|
67
54
|
],
|
|
68
|
-
offset: context.selection
|
|
55
|
+
offset: context.selection?.focus.offset ?? 0,
|
|
69
56
|
},
|
|
70
57
|
})
|
|
71
58
|
|
|
@@ -137,8 +124,8 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
137
124
|
}
|
|
138
125
|
|
|
139
126
|
const hrObject = config.horizontalRuleObject?.(context)
|
|
140
|
-
const focusBlock = getFocusTextBlock({context})
|
|
141
|
-
const selectionCollapsed =
|
|
127
|
+
const focusBlock = selectors.getFocusTextBlock({context})
|
|
128
|
+
const selectionCollapsed = selectors.isSelectionCollapsed({context})
|
|
142
129
|
|
|
143
130
|
if (!hrObject || !focusBlock || !selectionCollapsed) {
|
|
144
131
|
return false
|
|
@@ -189,7 +176,7 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
189
176
|
const hrRegExp = /^(---)$|(___)$|(\*\*\*)$/gm
|
|
190
177
|
const hrCharacters = text.match(hrRegExp)?.[0]
|
|
191
178
|
const hrObject = config.horizontalRuleObject?.(context)
|
|
192
|
-
const focusBlock = getFocusBlock({context})
|
|
179
|
+
const focusBlock = selectors.getFocusBlock({context})
|
|
193
180
|
|
|
194
181
|
if (!hrCharacters || !hrObject || !focusBlock) {
|
|
195
182
|
return false
|
|
@@ -237,9 +224,9 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
237
224
|
return false
|
|
238
225
|
}
|
|
239
226
|
|
|
240
|
-
const selectionCollapsed =
|
|
241
|
-
const focusTextBlock = getFocusTextBlock({context})
|
|
242
|
-
const focusSpan = getFocusSpan({context})
|
|
227
|
+
const selectionCollapsed = selectors.isSelectionCollapsed({context})
|
|
228
|
+
const focusTextBlock = selectors.getFocusTextBlock({context})
|
|
229
|
+
const focusSpan = selectors.getFocusSpan({context})
|
|
243
230
|
|
|
244
231
|
if (!selectionCollapsed || !focusTextBlock || !focusSpan) {
|
|
245
232
|
return false
|
|
@@ -253,7 +240,7 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
253
240
|
'children',
|
|
254
241
|
{_key: focusSpan.node._key},
|
|
255
242
|
],
|
|
256
|
-
offset: context.selection
|
|
243
|
+
offset: context.selection?.focus.offset ?? 0,
|
|
257
244
|
},
|
|
258
245
|
})
|
|
259
246
|
|
|
@@ -317,9 +304,9 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
317
304
|
const clearStyleOnBackspace = defineBehavior({
|
|
318
305
|
on: 'delete.backward',
|
|
319
306
|
guard: ({context}) => {
|
|
320
|
-
const selectionCollapsed =
|
|
321
|
-
const focusTextBlock = getFocusTextBlock({context})
|
|
322
|
-
const focusSpan = getFocusSpan({context})
|
|
307
|
+
const selectionCollapsed = selectors.isSelectionCollapsed({context})
|
|
308
|
+
const focusTextBlock = selectors.getFocusTextBlock({context})
|
|
309
|
+
const focusSpan = selectors.getFocusSpan({context})
|
|
323
310
|
|
|
324
311
|
if (!selectionCollapsed || !focusTextBlock || !focusSpan) {
|
|
325
312
|
return false
|
|
@@ -327,7 +314,7 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
327
314
|
|
|
328
315
|
const atTheBeginningOfBLock =
|
|
329
316
|
focusTextBlock.node.children[0]._key === focusSpan.node._key &&
|
|
330
|
-
context.selection
|
|
317
|
+
context.selection?.focus.offset === 0
|
|
331
318
|
|
|
332
319
|
const defaultStyle = config.defaultStyle?.(context)
|
|
333
320
|
|
|
@@ -360,9 +347,9 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
360
347
|
return false
|
|
361
348
|
}
|
|
362
349
|
|
|
363
|
-
const selectionCollapsed =
|
|
364
|
-
const focusTextBlock = getFocusTextBlock({context})
|
|
365
|
-
const focusSpan = getFocusSpan({context})
|
|
350
|
+
const selectionCollapsed = selectors.isSelectionCollapsed({context})
|
|
351
|
+
const focusTextBlock = selectors.getFocusTextBlock({context})
|
|
352
|
+
const focusSpan = selectors.getFocusSpan({context})
|
|
366
353
|
|
|
367
354
|
if (!selectionCollapsed || !focusTextBlock || !focusSpan) {
|
|
368
355
|
return false
|
|
@@ -376,7 +363,7 @@ export function createMarkdownBehaviors(config: MarkdownBehaviorsConfig) {
|
|
|
376
363
|
'children',
|
|
377
364
|
{_key: focusSpan.node._key},
|
|
378
365
|
],
|
|
379
|
-
offset: context.selection
|
|
366
|
+
offset: context.selection?.focus.offset ?? 0,
|
|
380
367
|
},
|
|
381
368
|
})
|
|
382
369
|
|
|
@@ -71,10 +71,10 @@ export type SyntheticBehaviorEvent =
|
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
| {
|
|
74
|
-
type: 'insert.
|
|
74
|
+
type: 'insert.break'
|
|
75
75
|
}
|
|
76
76
|
| {
|
|
77
|
-
type: 'insert.break'
|
|
77
|
+
type: 'insert.soft break'
|
|
78
78
|
}
|
|
79
79
|
| {
|
|
80
80
|
type: 'insert.text'
|
|
@@ -176,17 +176,17 @@ export type BehaviorActionIntend =
|
|
|
176
176
|
effect: () => void
|
|
177
177
|
}
|
|
178
178
|
| {
|
|
179
|
-
type: '
|
|
180
|
-
selection: EditorSelection
|
|
179
|
+
type: 'reselect'
|
|
181
180
|
}
|
|
182
181
|
| {
|
|
183
|
-
type: 'select
|
|
182
|
+
type: 'select'
|
|
183
|
+
selection: EditorSelection
|
|
184
184
|
}
|
|
185
185
|
| {
|
|
186
|
-
type: 'select
|
|
186
|
+
type: 'select.previous block'
|
|
187
187
|
}
|
|
188
188
|
| {
|
|
189
|
-
type: '
|
|
189
|
+
type: 'select.next block'
|
|
190
190
|
}
|
|
191
191
|
| {
|
|
192
192
|
type: 'style.add'
|
package/src/behaviors/index.ts
CHANGED
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(
|