@portabletext/editor 1.33.6 → 1.34.1
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.map +1 -1
- package/lib/_chunks-cjs/plugin.event-listener.cjs +13 -6
- package/lib/_chunks-cjs/plugin.event-listener.cjs.map +1 -1
- package/lib/_chunks-es/behavior.core.js.map +1 -1
- package/lib/_chunks-es/plugin.event-listener.js +14 -7
- package/lib/_chunks-es/plugin.event-listener.js.map +1 -1
- package/lib/behaviors/index.d.cts +8 -4
- package/lib/behaviors/index.d.ts +8 -4
- package/lib/index.d.cts +348 -117
- package/lib/index.d.ts +348 -117
- package/lib/plugins/index.cjs +60 -43
- package/lib/plugins/index.cjs.map +1 -1
- package/lib/plugins/index.d.cts +348 -117
- package/lib/plugins/index.d.ts +348 -117
- package/lib/plugins/index.js +63 -45
- package/lib/plugins/index.js.map +1 -1
- package/lib/selectors/index.d.cts +15 -2
- package/lib/selectors/index.d.ts +15 -2
- package/lib/utils/index.cjs +23 -1
- package/lib/utils/index.cjs.map +1 -1
- package/lib/utils/index.d.cts +14 -2
- package/lib/utils/index.d.ts +14 -2
- package/lib/utils/index.js +25 -2
- package/lib/utils/index.js.map +1 -1
- package/package.json +3 -3
- package/src/behavior-actions/behavior.action.decorator.add.ts +30 -8
- package/src/behaviors/behavior.markdown-emphasis.ts +82 -41
- package/src/behaviors/behavior.types.ts +2 -2
- package/src/converters/converter.types.ts +3 -2
- package/src/editor/plugins/create-with-event-listeners.ts +0 -5
- package/src/utils/index.ts +1 -0
- package/src/utils/util.child-selection-point-to-block-offset.ts +55 -0
package/lib/utils/index.js
CHANGED
|
@@ -1,8 +1,30 @@
|
|
|
1
|
-
import { sliceBlocks, isSpan } from "../_chunks-es/util.slice-blocks.js";
|
|
2
|
-
import { blockOffsetToSpanSelectionPoint, getBlockEndPoint, getBlockStartPoint, getTextBlockText, isEmptyTextBlock, isEqualSelectionPoints,
|
|
1
|
+
import { isKeyedSegment, sliceBlocks, isSpan } from "../_chunks-es/util.slice-blocks.js";
|
|
2
|
+
import { blockOffsetToSpanSelectionPoint, getBlockEndPoint, getBlockStartPoint, getTextBlockText, isEmptyTextBlock, isEqualSelectionPoints, spanSelectionPointToBlockOffset } from "../_chunks-es/util.slice-blocks.js";
|
|
3
3
|
import { parseBlock } from "../_chunks-es/util.block-offsets-to-selection.js";
|
|
4
4
|
import { blockOffsetsToSelection } from "../_chunks-es/util.block-offsets-to-selection.js";
|
|
5
|
+
import { isPortableTextTextBlock, isPortableTextSpan } from "@sanity/types";
|
|
5
6
|
import { reverseSelection } from "../_chunks-es/util.reverse-selection.js";
|
|
7
|
+
function childSelectionPointToBlockOffset({
|
|
8
|
+
value,
|
|
9
|
+
selectionPoint
|
|
10
|
+
}) {
|
|
11
|
+
let offset = 0;
|
|
12
|
+
const blockKey = isKeyedSegment(selectionPoint.path[0]) ? selectionPoint.path[0]._key : void 0, childKey = isKeyedSegment(selectionPoint.path[2]) ? selectionPoint.path[2]._key : void 0;
|
|
13
|
+
if (!(!blockKey || !childKey)) {
|
|
14
|
+
for (const block of value)
|
|
15
|
+
if (block._key === blockKey && isPortableTextTextBlock(block))
|
|
16
|
+
for (const child of block.children) {
|
|
17
|
+
if (child._key === childKey)
|
|
18
|
+
return {
|
|
19
|
+
path: [{
|
|
20
|
+
_key: block._key
|
|
21
|
+
}],
|
|
22
|
+
offset: offset + selectionPoint.offset
|
|
23
|
+
};
|
|
24
|
+
isPortableTextSpan(child) && (offset += child.text.length);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
6
28
|
function isTextBlock(context, block) {
|
|
7
29
|
return block._type === context.schema.block.name;
|
|
8
30
|
}
|
|
@@ -68,6 +90,7 @@ function splitTextBlock({
|
|
|
68
90
|
export {
|
|
69
91
|
blockOffsetToSpanSelectionPoint,
|
|
70
92
|
blockOffsetsToSelection,
|
|
93
|
+
childSelectionPointToBlockOffset,
|
|
71
94
|
getBlockEndPoint,
|
|
72
95
|
getBlockStartPoint,
|
|
73
96
|
getTextBlockText,
|
package/lib/utils/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/utils/util.is-text-block.ts","../../src/utils/util.merge-text-blocks.ts","../../src/utils/util.split-text-block.ts"],"sourcesContent":["import type {PortableTextBlock, PortableTextTextBlock} from '@sanity/types'\nimport type {EditorContext} from '../selectors'\n\n/**\n * @public\n */\nexport function isTextBlock(\n context: Pick<EditorContext, 'schema'>,\n block: PortableTextBlock,\n): block is PortableTextTextBlock {\n return block._type === context.schema.block.name\n}\n","import type {PortableTextTextBlock} from '@sanity/types'\nimport {parseBlock} from '../internal-utils/parse-blocks'\nimport type {EditorContext} from '../selectors'\nimport {isTextBlock} from './util.is-text-block'\n\n/**\n * @beta\n */\nexport function mergeTextBlocks({\n context,\n targetBlock,\n incomingBlock,\n}: {\n context: Pick<EditorContext, 'keyGenerator' | 'schema'>\n targetBlock: PortableTextTextBlock\n incomingBlock: PortableTextTextBlock\n}) {\n const parsedIncomingBlock = parseBlock({\n context,\n block: incomingBlock,\n options: {refreshKeys: true},\n })\n\n if (!parsedIncomingBlock || !isTextBlock(context, parsedIncomingBlock)) {\n return targetBlock\n }\n\n return {\n ...targetBlock,\n children: [...targetBlock.children, ...parsedIncomingBlock.children],\n markDefs: [\n ...(targetBlock.markDefs ?? []),\n ...(parsedIncomingBlock.markDefs ?? []),\n ],\n }\n}\n","import type {PortableTextTextBlock} from '@sanity/types'\nimport {isTextBlock, sliceBlocks, type EditorSelectionPoint} from '.'\nimport type {EditorContext} from '../selectors'\nimport {isSpan} from './util.is-span'\n\n/**\n * @beta\n */\nexport function splitTextBlock({\n context,\n block,\n point,\n}: {\n context: Pick<EditorContext, 'schema'>\n block: PortableTextTextBlock\n point: EditorSelectionPoint\n}): {before: PortableTextTextBlock; after: PortableTextTextBlock} | undefined {\n const firstChild = block.children.at(0)\n const lastChild = block.children.at(block.children.length - 1)\n\n if (!firstChild || !lastChild) {\n return undefined\n }\n\n const before = sliceBlocks({\n blocks: [block],\n selection: {\n anchor: {\n path: [{_key: block._key}, 'children', {_key: firstChild._key}],\n offset: 0,\n },\n focus: point,\n },\n }).at(0)\n const after = sliceBlocks({\n blocks: [block],\n selection: {\n anchor: point,\n focus: {\n path: [{_key: block._key}, 'children', {_key: lastChild._key}],\n offset: isSpan(context, lastChild) ? lastChild.text.length : 0,\n },\n },\n }).at(0)\n\n if (!before || !after) {\n return undefined\n }\n\n if (!isTextBlock(context, before) || !isTextBlock(context, after)) {\n return undefined\n }\n\n return {before, after}\n}\n"],"names":["
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/utils/util.child-selection-point-to-block-offset.ts","../../src/utils/util.is-text-block.ts","../../src/utils/util.merge-text-blocks.ts","../../src/utils/util.split-text-block.ts"],"sourcesContent":["import {\n isPortableTextSpan,\n isPortableTextTextBlock,\n type PortableTextBlock,\n} from '@sanity/types'\nimport type {BlockOffset} from '../behaviors/behavior.types'\nimport type {EditorSelectionPoint} from '../types/editor'\nimport {isKeyedSegment} from './util.is-keyed-segment'\n\n/**\n * @public\n */\nexport function childSelectionPointToBlockOffset({\n value,\n selectionPoint,\n}: {\n value: Array<PortableTextBlock>\n selectionPoint: EditorSelectionPoint\n}): BlockOffset | undefined {\n let offset = 0\n\n const blockKey = isKeyedSegment(selectionPoint.path[0])\n ? selectionPoint.path[0]._key\n : undefined\n const childKey = isKeyedSegment(selectionPoint.path[2])\n ? selectionPoint.path[2]._key\n : undefined\n\n if (!blockKey || !childKey) {\n return undefined\n }\n\n for (const block of value) {\n if (block._key !== blockKey) {\n continue\n }\n\n if (!isPortableTextTextBlock(block)) {\n continue\n }\n\n for (const child of block.children) {\n if (child._key === childKey) {\n return {\n path: [{_key: block._key}],\n offset: offset + selectionPoint.offset,\n }\n }\n\n if (isPortableTextSpan(child)) {\n offset += child.text.length\n }\n }\n }\n}\n","import type {PortableTextBlock, PortableTextTextBlock} from '@sanity/types'\nimport type {EditorContext} from '../selectors'\n\n/**\n * @public\n */\nexport function isTextBlock(\n context: Pick<EditorContext, 'schema'>,\n block: PortableTextBlock,\n): block is PortableTextTextBlock {\n return block._type === context.schema.block.name\n}\n","import type {PortableTextTextBlock} from '@sanity/types'\nimport {parseBlock} from '../internal-utils/parse-blocks'\nimport type {EditorContext} from '../selectors'\nimport {isTextBlock} from './util.is-text-block'\n\n/**\n * @beta\n */\nexport function mergeTextBlocks({\n context,\n targetBlock,\n incomingBlock,\n}: {\n context: Pick<EditorContext, 'keyGenerator' | 'schema'>\n targetBlock: PortableTextTextBlock\n incomingBlock: PortableTextTextBlock\n}) {\n const parsedIncomingBlock = parseBlock({\n context,\n block: incomingBlock,\n options: {refreshKeys: true},\n })\n\n if (!parsedIncomingBlock || !isTextBlock(context, parsedIncomingBlock)) {\n return targetBlock\n }\n\n return {\n ...targetBlock,\n children: [...targetBlock.children, ...parsedIncomingBlock.children],\n markDefs: [\n ...(targetBlock.markDefs ?? []),\n ...(parsedIncomingBlock.markDefs ?? []),\n ],\n }\n}\n","import type {PortableTextTextBlock} from '@sanity/types'\nimport {isTextBlock, sliceBlocks, type EditorSelectionPoint} from '.'\nimport type {EditorContext} from '../selectors'\nimport {isSpan} from './util.is-span'\n\n/**\n * @beta\n */\nexport function splitTextBlock({\n context,\n block,\n point,\n}: {\n context: Pick<EditorContext, 'schema'>\n block: PortableTextTextBlock\n point: EditorSelectionPoint\n}): {before: PortableTextTextBlock; after: PortableTextTextBlock} | undefined {\n const firstChild = block.children.at(0)\n const lastChild = block.children.at(block.children.length - 1)\n\n if (!firstChild || !lastChild) {\n return undefined\n }\n\n const before = sliceBlocks({\n blocks: [block],\n selection: {\n anchor: {\n path: [{_key: block._key}, 'children', {_key: firstChild._key}],\n offset: 0,\n },\n focus: point,\n },\n }).at(0)\n const after = sliceBlocks({\n blocks: [block],\n selection: {\n anchor: point,\n focus: {\n path: [{_key: block._key}, 'children', {_key: lastChild._key}],\n offset: isSpan(context, lastChild) ? lastChild.text.length : 0,\n },\n },\n }).at(0)\n\n if (!before || !after) {\n return undefined\n }\n\n if (!isTextBlock(context, before) || !isTextBlock(context, after)) {\n return undefined\n }\n\n return {before, after}\n}\n"],"names":["childSelectionPointToBlockOffset","value","selectionPoint","offset","blockKey","isKeyedSegment","path","_key","undefined","childKey","block","isPortableTextTextBlock","child","children","isPortableTextSpan","text","length","isTextBlock","context","_type","schema","name","mergeTextBlocks","targetBlock","incomingBlock","parsedIncomingBlock","parseBlock","options","refreshKeys","markDefs","splitTextBlock","point","firstChild","at","lastChild","before","sliceBlocks","blocks","selection","anchor","focus","after","isSpan"],"mappings":";;;;;;AAYO,SAASA,iCAAiC;AAAA,EAC/CC;AAAAA,EACAC;AAIF,GAA4B;AAC1B,MAAIC,SAAS;AAEPC,QAAAA,WAAWC,eAAeH,eAAeI,KAAK,CAAC,CAAC,IAClDJ,eAAeI,KAAK,CAAC,EAAEC,OACvBC,QACEC,WAAWJ,eAAeH,eAAeI,KAAK,CAAC,CAAC,IAClDJ,eAAeI,KAAK,CAAC,EAAEC,OACvBC;AAEA,MAAA,EAAA,CAACJ,YAAY,CAACK;AAIlB,eAAWC,SAAST;AAClB,UAAIS,MAAMH,SAASH,YAIdO,wBAAwBD,KAAK;AAIvBE,mBAAAA,SAASF,MAAMG,UAAU;AAClC,cAAID,MAAML,SAASE;AACV,mBAAA;AAAA,cACLH,MAAM,CAAC;AAAA,gBAACC,MAAMG,MAAMH;AAAAA,cAAAA,CAAK;AAAA,cACzBJ,QAAQA,SAASD,eAAeC;AAAAA,YAClC;AAGEW,6BAAmBF,KAAK,MAC1BT,UAAUS,MAAMG,KAAKC;AAAAA,QAAAA;AAAAA;AAI7B;AChDgBC,SAAAA,YACdC,SACAR,OACgC;AAChC,SAAOA,MAAMS,UAAUD,QAAQE,OAAOV,MAAMW;AAC9C;ACHO,SAASC,gBAAgB;AAAA,EAC9BJ;AAAAA,EACAK;AAAAA,EACAC;AAKF,GAAG;AACD,QAAMC,sBAAsBC,WAAW;AAAA,IACrCR;AAAAA,IACAR,OAAOc;AAAAA,IACPG,SAAS;AAAA,MAACC,aAAa;AAAA,IAAA;AAAA,EAAI,CAC5B;AAED,SAAI,CAACH,uBAAuB,CAACR,YAAYC,SAASO,mBAAmB,IAC5DF,cAGF;AAAA,IACL,GAAGA;AAAAA,IACHV,UAAU,CAAC,GAAGU,YAAYV,UAAU,GAAGY,oBAAoBZ,QAAQ;AAAA,IACnEgB,UAAU,CACR,GAAIN,YAAYM,YAAY,CAAA,GAC5B,GAAIJ,oBAAoBI,YAAY,CAAG,CAAA;AAAA,EAE3C;AACF;AC3BO,SAASC,eAAe;AAAA,EAC7BZ;AAAAA,EACAR;AAAAA,EACAqB;AAKF,GAA8E;AAC5E,QAAMC,aAAatB,MAAMG,SAASoB,GAAG,CAAC,GAChCC,YAAYxB,MAAMG,SAASoB,GAAGvB,MAAMG,SAASG,SAAS,CAAC;AAEzD,MAAA,CAACgB,cAAc,CAACE;AAClB;AAGF,QAAMC,SAASC,YAAY;AAAA,IACzBC,QAAQ,CAAC3B,KAAK;AAAA,IACd4B,WAAW;AAAA,MACTC,QAAQ;AAAA,QACNjC,MAAM,CAAC;AAAA,UAACC,MAAMG,MAAMH;AAAAA,WAAO,YAAY;AAAA,UAACA,MAAMyB,WAAWzB;AAAAA,QAAAA,CAAK;AAAA,QAC9DJ,QAAQ;AAAA,MACV;AAAA,MACAqC,OAAOT;AAAAA,IAAAA;AAAAA,EAEV,CAAA,EAAEE,GAAG,CAAC,GACDQ,QAAQL,YAAY;AAAA,IACxBC,QAAQ,CAAC3B,KAAK;AAAA,IACd4B,WAAW;AAAA,MACTC,QAAQR;AAAAA,MACRS,OAAO;AAAA,QACLlC,MAAM,CAAC;AAAA,UAACC,MAAMG,MAAMH;AAAAA,WAAO,YAAY;AAAA,UAACA,MAAM2B,UAAU3B;AAAAA,QAAAA,CAAK;AAAA,QAC7DJ,QAAQuC,OAAOxB,SAASgB,SAAS,IAAIA,UAAUnB,KAAKC,SAAS;AAAA,MAAA;AAAA,IAC/D;AAAA,EACF,CACD,EAAEiB,GAAG,CAAC;AAEP,MAAI,EAACE,CAAAA,UAAU,CAACM,UAIZ,EAACxB,CAAAA,YAAYC,SAASiB,MAAM,KAAK,CAAClB,YAAYC,SAASuB,KAAK;AAIzD,WAAA;AAAA,MAACN;AAAAA,MAAQM;AAAAA,IAAK;AACvB;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@portabletext/editor",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.34.1",
|
|
4
4
|
"description": "Portable Text Editor made in React",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sanity",
|
|
@@ -79,8 +79,8 @@
|
|
|
79
79
|
"slate-react": "0.112.1",
|
|
80
80
|
"use-effect-event": "^1.0.2",
|
|
81
81
|
"xstate": "^5.19.2",
|
|
82
|
-
"@portabletext/
|
|
83
|
-
"@portabletext/
|
|
82
|
+
"@portabletext/patches": "1.1.3",
|
|
83
|
+
"@portabletext/block-tools": "1.1.8"
|
|
84
84
|
},
|
|
85
85
|
"devDependencies": {
|
|
86
86
|
"@portabletext/toolkit": "^2.0.17",
|
|
@@ -11,20 +11,42 @@ export const decoratorAddActionImplementation: BehaviorActionImplementation<
|
|
|
11
11
|
> = ({context, action}) => {
|
|
12
12
|
const editor = action.editor
|
|
13
13
|
const mark = action.decorator
|
|
14
|
-
const selection = action.selection
|
|
15
|
-
? (toSlateRange(action.selection, action.editor) ?? editor.selection)
|
|
16
|
-
: editor.selection
|
|
17
|
-
|
|
18
|
-
if (!selection) {
|
|
19
|
-
return
|
|
20
|
-
}
|
|
21
|
-
|
|
22
14
|
const value = fromSlateValue(
|
|
23
15
|
editor.children,
|
|
24
16
|
context.schema.block.name,
|
|
25
17
|
KEY_TO_VALUE_ELEMENT.get(editor),
|
|
26
18
|
)
|
|
27
19
|
|
|
20
|
+
const manualAnchor = action.offsets?.anchor
|
|
21
|
+
? utils.blockOffsetToSpanSelectionPoint({
|
|
22
|
+
value,
|
|
23
|
+
blockOffset: action.offsets.anchor,
|
|
24
|
+
direction: 'backward',
|
|
25
|
+
})
|
|
26
|
+
: undefined
|
|
27
|
+
const manualFocus = action.offsets?.focus
|
|
28
|
+
? utils.blockOffsetToSpanSelectionPoint({
|
|
29
|
+
value,
|
|
30
|
+
blockOffset: action.offsets.focus,
|
|
31
|
+
direction: 'forward',
|
|
32
|
+
})
|
|
33
|
+
: undefined
|
|
34
|
+
const manualSelection =
|
|
35
|
+
manualAnchor && manualFocus
|
|
36
|
+
? {
|
|
37
|
+
anchor: manualAnchor,
|
|
38
|
+
focus: manualFocus,
|
|
39
|
+
}
|
|
40
|
+
: undefined
|
|
41
|
+
|
|
42
|
+
const selection = manualSelection
|
|
43
|
+
? (toSlateRange(manualSelection, action.editor) ?? editor.selection)
|
|
44
|
+
: editor.selection
|
|
45
|
+
|
|
46
|
+
if (!selection) {
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
|
|
28
50
|
const editorSelection = toPortableTextRange(value, selection, context.schema)
|
|
29
51
|
const anchorOffset = editorSelection
|
|
30
52
|
? utils.spanSelectionPointToBlockOffset({
|
|
@@ -79,10 +79,6 @@ const emphasisListener: CallbackLogicFunction<
|
|
|
79
79
|
return false
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
if (event.text !== '*' && event.text !== '_') {
|
|
83
|
-
return false
|
|
84
|
-
}
|
|
85
|
-
|
|
86
82
|
const focusTextBlock = selectors.getFocusTextBlock({context})
|
|
87
83
|
const selectionStartPoint = selectors.getSelectionStartPoint({context})
|
|
88
84
|
const selectionStartOffset = selectionStartPoint
|
|
@@ -104,44 +100,33 @@ const emphasisListener: CallbackLogicFunction<
|
|
|
104
100
|
const prefixOffsets = {
|
|
105
101
|
anchor: {
|
|
106
102
|
path: focusTextBlock.path,
|
|
107
|
-
|
|
103
|
+
// Example: "foo *bar*".length - "*bar*".length = 4
|
|
104
|
+
offset: `${textBefore}${event.text}`.length - textToItalic.length,
|
|
108
105
|
},
|
|
109
106
|
focus: {
|
|
110
107
|
path: focusTextBlock.path,
|
|
111
|
-
|
|
108
|
+
// Example: "foo *bar*".length - "*bar*".length + 1 = 5
|
|
109
|
+
offset:
|
|
110
|
+
`${textBefore}${event.text}`.length - textToItalic.length + 1,
|
|
112
111
|
},
|
|
113
112
|
}
|
|
114
113
|
const suffixOffsets = {
|
|
115
114
|
anchor: {
|
|
116
115
|
path: focusTextBlock.path,
|
|
117
|
-
|
|
116
|
+
// Example: "foo *bar|" (8) + "*".length - 1 = 8
|
|
117
|
+
offset: selectionStartOffset.offset + event.text.length - 1,
|
|
118
118
|
},
|
|
119
119
|
focus: {
|
|
120
120
|
path: focusTextBlock.path,
|
|
121
|
-
|
|
121
|
+
// Example: "foo *bar|" (8) + "*".length = 9
|
|
122
|
+
offset: selectionStartOffset.offset + event.text.length,
|
|
122
123
|
},
|
|
123
124
|
}
|
|
124
125
|
|
|
125
|
-
const anchor = utils.blockOffsetToSpanSelectionPoint({
|
|
126
|
-
value: context.value,
|
|
127
|
-
blockOffset: prefixOffsets.focus,
|
|
128
|
-
direction: 'backward',
|
|
129
|
-
})
|
|
130
|
-
const focus = utils.blockOffsetToSpanSelectionPoint({
|
|
131
|
-
value: context.value,
|
|
132
|
-
blockOffset: suffixOffsets.anchor,
|
|
133
|
-
direction: 'forward',
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
if (!anchor || !focus) {
|
|
137
|
-
return false
|
|
138
|
-
}
|
|
139
|
-
|
|
140
126
|
return {
|
|
141
127
|
prefixOffsets,
|
|
142
128
|
suffixOffsets,
|
|
143
129
|
decorator: italicDecorator,
|
|
144
|
-
selection: {anchor, focus},
|
|
145
130
|
}
|
|
146
131
|
}
|
|
147
132
|
|
|
@@ -151,35 +136,85 @@ const emphasisListener: CallbackLogicFunction<
|
|
|
151
136
|
const prefixOffsets = {
|
|
152
137
|
anchor: {
|
|
153
138
|
path: focusTextBlock.path,
|
|
154
|
-
|
|
139
|
+
// Example: "foo **bar**".length - "**bar**".length = 4
|
|
140
|
+
offset: `${textBefore}${event.text}`.length - textToBold.length,
|
|
155
141
|
},
|
|
156
142
|
focus: {
|
|
157
143
|
path: focusTextBlock.path,
|
|
158
|
-
|
|
144
|
+
// Example: "foo **bar**".length - "**bar**".length + 2 = 6
|
|
145
|
+
offset:
|
|
146
|
+
`${textBefore}${event.text}`.length - textToBold.length + 2,
|
|
159
147
|
},
|
|
160
148
|
}
|
|
149
|
+
|
|
150
|
+
const prefixSelection = utils.blockOffsetsToSelection({
|
|
151
|
+
value: context.value,
|
|
152
|
+
offsets: prefixOffsets,
|
|
153
|
+
})
|
|
154
|
+
const inlineObjectBeforePrefixFocus =
|
|
155
|
+
selectors.getPreviousInlineObject({
|
|
156
|
+
context: {
|
|
157
|
+
...context,
|
|
158
|
+
selection: prefixSelection
|
|
159
|
+
? {
|
|
160
|
+
anchor: prefixSelection.focus,
|
|
161
|
+
focus: prefixSelection.focus,
|
|
162
|
+
}
|
|
163
|
+
: null,
|
|
164
|
+
},
|
|
165
|
+
})
|
|
166
|
+
const inlineObjectBeforePrefixFocusOffset =
|
|
167
|
+
inlineObjectBeforePrefixFocus
|
|
168
|
+
? utils.childSelectionPointToBlockOffset({
|
|
169
|
+
value: context.value,
|
|
170
|
+
selectionPoint: {
|
|
171
|
+
path: inlineObjectBeforePrefixFocus.path,
|
|
172
|
+
offset: 0,
|
|
173
|
+
},
|
|
174
|
+
})
|
|
175
|
+
: undefined
|
|
176
|
+
|
|
177
|
+
if (
|
|
178
|
+
inlineObjectBeforePrefixFocusOffset &&
|
|
179
|
+
inlineObjectBeforePrefixFocusOffset.offset >
|
|
180
|
+
prefixOffsets.anchor.offset &&
|
|
181
|
+
inlineObjectBeforePrefixFocusOffset.offset <
|
|
182
|
+
prefixOffsets.focus.offset
|
|
183
|
+
) {
|
|
184
|
+
return false
|
|
185
|
+
}
|
|
186
|
+
|
|
161
187
|
const suffixOffsets = {
|
|
162
188
|
anchor: {
|
|
163
189
|
path: focusTextBlock.path,
|
|
164
|
-
|
|
190
|
+
// Example: "foo **bar*|" (10) + "*".length - 2 = 9
|
|
191
|
+
offset: selectionStartOffset.offset + event.text.length - 2,
|
|
165
192
|
},
|
|
166
193
|
focus: {
|
|
167
194
|
path: focusTextBlock.path,
|
|
168
|
-
|
|
195
|
+
// Example: "foo **bar*|" (10) + "*".length = 11
|
|
196
|
+
offset: selectionStartOffset.offset + event.text.length,
|
|
169
197
|
},
|
|
170
198
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
direction: 'backward',
|
|
175
|
-
})
|
|
176
|
-
const focus = utils.blockOffsetToSpanSelectionPoint({
|
|
177
|
-
value: context.value,
|
|
178
|
-
blockOffset: suffixOffsets.anchor,
|
|
179
|
-
direction: 'forward',
|
|
199
|
+
|
|
200
|
+
const previousInlineObject = selectors.getPreviousInlineObject({
|
|
201
|
+
context,
|
|
180
202
|
})
|
|
203
|
+
const previousInlineObjectOffset = previousInlineObject
|
|
204
|
+
? utils.childSelectionPointToBlockOffset({
|
|
205
|
+
value: context.value,
|
|
206
|
+
selectionPoint: {
|
|
207
|
+
path: previousInlineObject.path,
|
|
208
|
+
offset: 0,
|
|
209
|
+
},
|
|
210
|
+
})
|
|
211
|
+
: undefined
|
|
181
212
|
|
|
182
|
-
if (
|
|
213
|
+
if (
|
|
214
|
+
previousInlineObjectOffset &&
|
|
215
|
+
previousInlineObjectOffset.offset > suffixOffsets.anchor.offset &&
|
|
216
|
+
previousInlineObjectOffset.offset < suffixOffsets.focus.offset
|
|
217
|
+
) {
|
|
183
218
|
return false
|
|
184
219
|
}
|
|
185
220
|
|
|
@@ -187,7 +222,6 @@ const emphasisListener: CallbackLogicFunction<
|
|
|
187
222
|
prefixOffsets,
|
|
188
223
|
suffixOffsets,
|
|
189
224
|
decorator: boldDecorator,
|
|
190
|
-
selection: {anchor, focus},
|
|
191
225
|
}
|
|
192
226
|
}
|
|
193
227
|
|
|
@@ -195,11 +229,14 @@ const emphasisListener: CallbackLogicFunction<
|
|
|
195
229
|
},
|
|
196
230
|
actions: [
|
|
197
231
|
({event}) => [event],
|
|
198
|
-
(_, {prefixOffsets, suffixOffsets, decorator
|
|
232
|
+
(_, {prefixOffsets, suffixOffsets, decorator}) => [
|
|
199
233
|
{
|
|
200
234
|
type: 'decorator.add',
|
|
201
235
|
decorator,
|
|
202
|
-
|
|
236
|
+
offsets: {
|
|
237
|
+
anchor: prefixOffsets.focus,
|
|
238
|
+
focus: suffixOffsets.anchor,
|
|
239
|
+
},
|
|
203
240
|
},
|
|
204
241
|
{
|
|
205
242
|
type: 'delete.text',
|
|
@@ -209,6 +246,10 @@ const emphasisListener: CallbackLogicFunction<
|
|
|
209
246
|
type: 'delete.text',
|
|
210
247
|
...prefixOffsets,
|
|
211
248
|
},
|
|
249
|
+
{
|
|
250
|
+
type: 'decorator.remove',
|
|
251
|
+
decorator,
|
|
252
|
+
},
|
|
212
253
|
{
|
|
213
254
|
type: 'effect',
|
|
214
255
|
effect: () => {
|
|
@@ -57,7 +57,7 @@ export type SyntheticBehaviorEvent =
|
|
|
57
57
|
| {
|
|
58
58
|
type: 'decorator.add'
|
|
59
59
|
decorator: string
|
|
60
|
-
|
|
60
|
+
offsets?: {anchor: BlockOffset; focus: BlockOffset}
|
|
61
61
|
}
|
|
62
62
|
| {
|
|
63
63
|
type: 'decorator.remove'
|
|
@@ -236,7 +236,7 @@ export type NativeBehaviorEvent =
|
|
|
236
236
|
}
|
|
237
237
|
| {
|
|
238
238
|
type: 'serialize'
|
|
239
|
-
originEvent: 'copy' | 'cut' | 'unknown'
|
|
239
|
+
originEvent: 'copy' | 'cut' | 'drag' | 'unknown'
|
|
240
240
|
dataTransfer: DataTransfer
|
|
241
241
|
}
|
|
242
242
|
|
|
@@ -18,18 +18,19 @@ export function defineConverter<TMIMEType extends MIMEType>(
|
|
|
18
18
|
export type ConverterEvent<TMIMEType extends MIMEType = MIMEType> =
|
|
19
19
|
| {
|
|
20
20
|
type: 'serialize'
|
|
21
|
-
originEvent: 'copy' | 'cut' | 'unknown'
|
|
21
|
+
originEvent: 'copy' | 'cut' | 'drag' | 'unknown'
|
|
22
22
|
}
|
|
23
23
|
| {
|
|
24
24
|
type: 'serialization.failure'
|
|
25
25
|
mimeType: TMIMEType
|
|
26
|
+
originEvent: 'copy' | 'cut' | 'drag' | 'unknown'
|
|
26
27
|
reason: string
|
|
27
28
|
}
|
|
28
29
|
| {
|
|
29
30
|
type: 'serialization.success'
|
|
30
31
|
data: string
|
|
31
32
|
mimeType: TMIMEType
|
|
32
|
-
originEvent: 'copy' | 'cut' | 'unknown'
|
|
33
|
+
originEvent: 'copy' | 'cut' | 'drag' | 'unknown'
|
|
33
34
|
}
|
|
34
35
|
| {
|
|
35
36
|
type: 'deserialize'
|
|
@@ -236,11 +236,6 @@ export function createWithEventListeners(
|
|
|
236
236
|
}
|
|
237
237
|
|
|
238
238
|
editor.setFragmentData = (dataTransfer, originEvent) => {
|
|
239
|
-
if (originEvent === 'drag') {
|
|
240
|
-
setFragmentData(dataTransfer)
|
|
241
|
-
return
|
|
242
|
-
}
|
|
243
|
-
|
|
244
239
|
if (isApplyingBehaviorActions(editor)) {
|
|
245
240
|
setFragmentData(dataTransfer)
|
|
246
241
|
return
|
package/src/utils/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ export {
|
|
|
5
5
|
spanSelectionPointToBlockOffset,
|
|
6
6
|
} from './util.block-offset'
|
|
7
7
|
export {blockOffsetsToSelection} from './util.block-offsets-to-selection'
|
|
8
|
+
export {childSelectionPointToBlockOffset} from './util.child-selection-point-to-block-offset'
|
|
8
9
|
export {getBlockEndPoint} from './util.get-block-end-point'
|
|
9
10
|
export {getBlockStartPoint} from './util.get-block-start-point'
|
|
10
11
|
export {getTextBlockText} from './util.get-text-block-text'
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isPortableTextSpan,
|
|
3
|
+
isPortableTextTextBlock,
|
|
4
|
+
type PortableTextBlock,
|
|
5
|
+
} from '@sanity/types'
|
|
6
|
+
import type {BlockOffset} from '../behaviors/behavior.types'
|
|
7
|
+
import type {EditorSelectionPoint} from '../types/editor'
|
|
8
|
+
import {isKeyedSegment} from './util.is-keyed-segment'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @public
|
|
12
|
+
*/
|
|
13
|
+
export function childSelectionPointToBlockOffset({
|
|
14
|
+
value,
|
|
15
|
+
selectionPoint,
|
|
16
|
+
}: {
|
|
17
|
+
value: Array<PortableTextBlock>
|
|
18
|
+
selectionPoint: EditorSelectionPoint
|
|
19
|
+
}): BlockOffset | undefined {
|
|
20
|
+
let offset = 0
|
|
21
|
+
|
|
22
|
+
const blockKey = isKeyedSegment(selectionPoint.path[0])
|
|
23
|
+
? selectionPoint.path[0]._key
|
|
24
|
+
: undefined
|
|
25
|
+
const childKey = isKeyedSegment(selectionPoint.path[2])
|
|
26
|
+
? selectionPoint.path[2]._key
|
|
27
|
+
: undefined
|
|
28
|
+
|
|
29
|
+
if (!blockKey || !childKey) {
|
|
30
|
+
return undefined
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
for (const block of value) {
|
|
34
|
+
if (block._key !== blockKey) {
|
|
35
|
+
continue
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!isPortableTextTextBlock(block)) {
|
|
39
|
+
continue
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
for (const child of block.children) {
|
|
43
|
+
if (child._key === childKey) {
|
|
44
|
+
return {
|
|
45
|
+
path: [{_key: block._key}],
|
|
46
|
+
offset: offset + selectionPoint.offset,
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (isPortableTextSpan(child)) {
|
|
51
|
+
offset += child.text.length
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|