@portabletext/editor 1.28.0 → 1.30.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.
Files changed (61) hide show
  1. package/lib/_chunks-cjs/behavior.core.cjs +40 -37
  2. package/lib/_chunks-cjs/behavior.core.cjs.map +1 -1
  3. package/lib/_chunks-cjs/parse-blocks.cjs +79 -0
  4. package/lib/_chunks-cjs/parse-blocks.cjs.map +1 -0
  5. package/lib/_chunks-cjs/plugin.event-listener.cjs +55 -97
  6. package/lib/_chunks-cjs/plugin.event-listener.cjs.map +1 -1
  7. package/lib/_chunks-cjs/selector.get-selection-start-point.cjs +15 -0
  8. package/lib/_chunks-cjs/selector.get-selection-start-point.cjs.map +1 -0
  9. package/lib/_chunks-es/behavior.core.js +40 -37
  10. package/lib/_chunks-es/behavior.core.js.map +1 -1
  11. package/lib/_chunks-es/parse-blocks.js +80 -0
  12. package/lib/_chunks-es/parse-blocks.js.map +1 -0
  13. package/lib/_chunks-es/plugin.event-listener.js +57 -98
  14. package/lib/_chunks-es/plugin.event-listener.js.map +1 -1
  15. package/lib/_chunks-es/selector.get-selection-start-point.js +16 -0
  16. package/lib/_chunks-es/selector.get-selection-start-point.js.map +1 -0
  17. package/lib/behaviors/index.d.cts +196 -124
  18. package/lib/behaviors/index.d.ts +196 -124
  19. package/lib/index.d.cts +248 -0
  20. package/lib/index.d.ts +248 -0
  21. package/lib/plugins/index.cjs +249 -1
  22. package/lib/plugins/index.cjs.map +1 -1
  23. package/lib/plugins/index.d.cts +246 -1
  24. package/lib/plugins/index.d.ts +246 -1
  25. package/lib/plugins/index.js +257 -3
  26. package/lib/plugins/index.js.map +1 -1
  27. package/lib/selectors/index.cjs +28 -1
  28. package/lib/selectors/index.cjs.map +1 -1
  29. package/lib/selectors/index.d.cts +21 -0
  30. package/lib/selectors/index.d.ts +21 -0
  31. package/lib/selectors/index.js +28 -0
  32. package/lib/selectors/index.js.map +1 -1
  33. package/lib/utils/index.cjs +70 -1
  34. package/lib/utils/index.cjs.map +1 -1
  35. package/lib/utils/index.d.cts +168 -2
  36. package/lib/utils/index.d.ts +168 -2
  37. package/lib/utils/index.js +71 -1
  38. package/lib/utils/index.js.map +1 -1
  39. package/package.json +2 -2
  40. package/src/behavior-actions/behavior.action.delete.ts +18 -0
  41. package/src/behavior-actions/behavior.action.insert-break.ts +3 -8
  42. package/src/behavior-actions/behavior.actions.ts +9 -0
  43. package/src/behaviors/_exports/index.ts +1 -0
  44. package/src/behaviors/behavior.core.deserialize.ts +52 -38
  45. package/src/behaviors/behavior.core.ts +4 -11
  46. package/src/behaviors/behavior.types.ts +4 -0
  47. package/src/editor/PortableTextEditor.tsx +20 -0
  48. package/src/internal-utils/__tests__/patchToOperations.test.ts +19 -21
  49. package/src/internal-utils/applyPatch.ts +11 -3
  50. package/src/plugins/index.ts +2 -0
  51. package/src/plugins/plugin.behavior.tsx +22 -0
  52. package/src/plugins/plugin.one-line.tsx +225 -0
  53. package/src/selectors/index.ts +3 -0
  54. package/src/selectors/selector.get-selection-end-point.ts +17 -0
  55. package/src/selectors/selector.get-selection-start-point.ts +17 -0
  56. package/src/selectors/selector.is-overlapping-selection.ts +46 -0
  57. package/src/utils/index.ts +4 -0
  58. package/src/utils/util.is-span.ts +12 -0
  59. package/src/utils/util.is-text-block.ts +12 -0
  60. package/src/utils/util.merge-text-blocks.ts +36 -0
  61. package/src/utils/util.split-text-block.ts +55 -0
@@ -1,6 +1,71 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: !0 });
3
- var util_isEmptyTextBlock = require("../_chunks-cjs/util.is-empty-text-block.cjs"), util_isEqualSelectionPoints = require("../_chunks-cjs/util.is-equal-selection-points.cjs"), util_reverseSelection = require("../_chunks-cjs/util.reverse-selection.cjs"), util_sliceBlocks = require("../_chunks-cjs/util.slice-blocks.cjs");
3
+ var util_isEmptyTextBlock = require("../_chunks-cjs/util.is-empty-text-block.cjs"), util_isEqualSelectionPoints = require("../_chunks-cjs/util.is-equal-selection-points.cjs"), parseBlocks = require("../_chunks-cjs/parse-blocks.cjs"), util_reverseSelection = require("../_chunks-cjs/util.reverse-selection.cjs"), util_sliceBlocks = require("../_chunks-cjs/util.slice-blocks.cjs");
4
+ function isSpan(context, child) {
5
+ return child._type === context.schema.span.name;
6
+ }
7
+ function isTextBlock(context, block) {
8
+ return block._type === context.schema.block.name;
9
+ }
10
+ function mergeTextBlocks({
11
+ context,
12
+ targetBlock,
13
+ incomingBlock
14
+ }) {
15
+ const parsedIncomingBlock = parseBlocks.parseBlock({
16
+ context,
17
+ block: incomingBlock,
18
+ options: {
19
+ refreshKeys: !0
20
+ }
21
+ });
22
+ return !parsedIncomingBlock || !isTextBlock(context, parsedIncomingBlock) ? targetBlock : {
23
+ ...targetBlock,
24
+ children: [...targetBlock.children, ...parsedIncomingBlock.children],
25
+ markDefs: [...targetBlock.markDefs ?? [], ...parsedIncomingBlock.markDefs ?? []]
26
+ };
27
+ }
28
+ function splitTextBlock({
29
+ context,
30
+ block,
31
+ point
32
+ }) {
33
+ const firstChild = block.children.at(0), lastChild = block.children.at(block.children.length - 1);
34
+ if (!firstChild || !lastChild)
35
+ return;
36
+ const before = util_sliceBlocks.sliceBlocks({
37
+ blocks: [block],
38
+ selection: {
39
+ anchor: {
40
+ path: [{
41
+ _key: block._key
42
+ }, "children", {
43
+ _key: firstChild._key
44
+ }],
45
+ offset: 0
46
+ },
47
+ focus: point
48
+ }
49
+ }).at(0), after = util_sliceBlocks.sliceBlocks({
50
+ blocks: [block],
51
+ selection: {
52
+ anchor: point,
53
+ focus: {
54
+ path: [{
55
+ _key: block._key
56
+ }, "children", {
57
+ _key: lastChild._key
58
+ }],
59
+ offset: isSpan(context, lastChild) ? lastChild.text.length : 0
60
+ }
61
+ }
62
+ }).at(0);
63
+ if (!(!before || !after) && !(!isTextBlock(context, before) || !isTextBlock(context, after)))
64
+ return {
65
+ before,
66
+ after
67
+ };
68
+ }
4
69
  exports.blockOffsetToSpanSelectionPoint = util_isEmptyTextBlock.blockOffsetToSpanSelectionPoint;
5
70
  exports.getTextBlockText = util_isEmptyTextBlock.getTextBlockText;
6
71
  exports.isEmptyTextBlock = util_isEmptyTextBlock.isEmptyTextBlock;
@@ -11,4 +76,8 @@ exports.isEqualSelectionPoints = util_isEqualSelectionPoints.isEqualSelectionPoi
11
76
  exports.isKeyedSegment = util_isEqualSelectionPoints.isKeyedSegment;
12
77
  exports.reverseSelection = util_reverseSelection.reverseSelection;
13
78
  exports.sliceBlocks = util_sliceBlocks.sliceBlocks;
79
+ exports.isSpan = isSpan;
80
+ exports.isTextBlock = isTextBlock;
81
+ exports.mergeTextBlocks = mergeTextBlocks;
82
+ exports.splitTextBlock = splitTextBlock;
14
83
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":["../../src/utils/util.is-span.ts","../../src/utils/util.is-text-block.ts","../../src/utils/util.merge-text-blocks.ts","../../src/utils/util.split-text-block.ts"],"sourcesContent":["import type {PortableTextChild, PortableTextSpan} from '@sanity/types'\nimport type {EditorContext} from '../selectors'\n\n/**\n * @public\n */\nexport function isSpan(\n context: Pick<EditorContext, 'schema'>,\n child: PortableTextChild,\n): child is PortableTextSpan {\n return child._type === context.schema.span.name\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":["isSpan","context","child","_type","schema","span","name","isTextBlock","block","mergeTextBlocks","targetBlock","incomingBlock","parsedIncomingBlock","parseBlock","options","refreshKeys","children","markDefs","splitTextBlock","point","firstChild","at","lastChild","length","before","sliceBlocks","blocks","selection","anchor","path","_key","offset","focus","after","text"],"mappings":";;;AAMgBA,SAAAA,OACdC,SACAC,OAC2B;AAC3B,SAAOA,MAAMC,UAAUF,QAAQG,OAAOC,KAAKC;AAC7C;ACLgBC,SAAAA,YACdN,SACAO,OACgC;AAChC,SAAOA,MAAML,UAAUF,QAAQG,OAAOI,MAAMF;AAC9C;ACHO,SAASG,gBAAgB;AAAA,EAC9BR;AAAAA,EACAS;AAAAA,EACAC;AAKF,GAAG;AACD,QAAMC,sBAAsBC,YAAAA,WAAW;AAAA,IACrCZ;AAAAA,IACAO,OAAOG;AAAAA,IACPG,SAAS;AAAA,MAACC,aAAa;AAAA,IAAA;AAAA,EAAI,CAC5B;AAED,SAAI,CAACH,uBAAuB,CAACL,YAAYN,SAASW,mBAAmB,IAC5DF,cAGF;AAAA,IACL,GAAGA;AAAAA,IACHM,UAAU,CAAC,GAAGN,YAAYM,UAAU,GAAGJ,oBAAoBI,QAAQ;AAAA,IACnEC,UAAU,CACR,GAAIP,YAAYO,YAAY,CAAA,GAC5B,GAAIL,oBAAoBK,YAAY,CAAG,CAAA;AAAA,EAE3C;AACF;AC3BO,SAASC,eAAe;AAAA,EAC7BjB;AAAAA,EACAO;AAAAA,EACAW;AAKF,GAA8E;AAC5E,QAAMC,aAAaZ,MAAMQ,SAASK,GAAG,CAAC,GAChCC,YAAYd,MAAMQ,SAASK,GAAGb,MAAMQ,SAASO,SAAS,CAAC;AAEzD,MAAA,CAACH,cAAc,CAACE;AAClB;AAGF,QAAME,SAASC,iBAAAA,YAAY;AAAA,IACzBC,QAAQ,CAAClB,KAAK;AAAA,IACdmB,WAAW;AAAA,MACTC,QAAQ;AAAA,QACNC,MAAM,CAAC;AAAA,UAACC,MAAMtB,MAAMsB;AAAAA,WAAO,YAAY;AAAA,UAACA,MAAMV,WAAWU;AAAAA,QAAAA,CAAK;AAAA,QAC9DC,QAAQ;AAAA,MACV;AAAA,MACAC,OAAOb;AAAAA,IAAAA;AAAAA,EAEV,CAAA,EAAEE,GAAG,CAAC,GACDY,QAAQR,iBAAAA,YAAY;AAAA,IACxBC,QAAQ,CAAClB,KAAK;AAAA,IACdmB,WAAW;AAAA,MACTC,QAAQT;AAAAA,MACRa,OAAO;AAAA,QACLH,MAAM,CAAC;AAAA,UAACC,MAAMtB,MAAMsB;AAAAA,WAAO,YAAY;AAAA,UAACA,MAAMR,UAAUQ;AAAAA,QAAAA,CAAK;AAAA,QAC7DC,QAAQ/B,OAAOC,SAASqB,SAAS,IAAIA,UAAUY,KAAKX,SAAS;AAAA,MAAA;AAAA,IAC/D;AAAA,EACF,CACD,EAAEF,GAAG,CAAC;AAEP,MAAI,EAACG,CAAAA,UAAU,CAACS,UAIZ,EAAC1B,CAAAA,YAAYN,SAASuB,MAAM,KAAK,CAACjB,YAAYN,SAASgC,KAAK;AAIzD,WAAA;AAAA,MAACT;AAAAA,MAAQS;AAAAA,IAAK;AACvB;;;;;;;;;;;;;;;"}
@@ -1,5 +1,20 @@
1
- import {KeyedSegment, PortableTextBlock} from '@sanity/types'
2
- import type {Path, PathSegment, PortableTextTextBlock} from '@sanity/types'
1
+ import type {
2
+ ArraySchemaType,
3
+ BlockDecoratorDefinition,
4
+ BlockListDefinition,
5
+ BlockStyleDefinition,
6
+ ObjectSchemaType,
7
+ Path,
8
+ PathSegment,
9
+ PortableTextChild,
10
+ PortableTextTextBlock,
11
+ } from '@sanity/types'
12
+ import {
13
+ KeyedSegment,
14
+ PortableTextBlock,
15
+ PortableTextObject,
16
+ PortableTextSpan,
17
+ } from '@sanity/types'
3
18
 
4
19
  /**
5
20
  * @beta
@@ -25,6 +40,72 @@ export declare function blockOffsetToSpanSelectionPoint({
25
40
  }
26
41
  | undefined
27
42
 
43
+ declare type Converter<TMIMEType extends MIMEType = MIMEType> = {
44
+ mimeType: TMIMEType
45
+ serialize: Serializer<TMIMEType>
46
+ deserialize: Deserializer<TMIMEType>
47
+ }
48
+
49
+ declare type ConverterEvent<TMIMEType extends MIMEType = MIMEType> =
50
+ | {
51
+ type: 'serialize'
52
+ originEvent: 'copy' | 'cut' | 'unknown'
53
+ }
54
+ | {
55
+ type: 'serialization.failure'
56
+ mimeType: TMIMEType
57
+ reason: string
58
+ }
59
+ | {
60
+ type: 'serialization.success'
61
+ data: string
62
+ mimeType: TMIMEType
63
+ originEvent: 'copy' | 'cut' | 'unknown'
64
+ }
65
+ | {
66
+ type: 'deserialize'
67
+ data: string
68
+ }
69
+ | {
70
+ type: 'deserialization.failure'
71
+ mimeType: TMIMEType
72
+ reason: string
73
+ }
74
+ | {
75
+ type: 'deserialization.success'
76
+ data: Array<PortableTextBlock>
77
+ mimeType: TMIMEType
78
+ }
79
+
80
+ declare type Deserializer<TMIMEType extends MIMEType> = ({
81
+ context,
82
+ event,
83
+ }: {
84
+ context: EditorContext
85
+ event: PickFromUnion<ConverterEvent<TMIMEType>, 'type', 'deserialize'>
86
+ }) => PickFromUnion<
87
+ ConverterEvent<TMIMEType>,
88
+ 'type',
89
+ 'deserialization.success' | 'deserialization.failure'
90
+ >
91
+
92
+ /**
93
+ * @public
94
+ */
95
+ declare type EditorContext = {
96
+ activeDecorators: Array<string>
97
+ converters: Array<Converter>
98
+ keyGenerator: () => string
99
+ schema: EditorSchema
100
+ selection: EditorSelection
101
+ value: Array<PortableTextBlock>
102
+ }
103
+
104
+ /**
105
+ * @public
106
+ */
107
+ declare type EditorSchema = PortableTextMemberSchemaTypes
108
+
28
109
  /** @public */
29
110
  export declare type EditorSelection = {
30
111
  anchor: EditorSelectionPoint
@@ -85,6 +166,61 @@ export declare function isKeyedSegment(
85
166
  segment: PathSegment,
86
167
  ): segment is KeyedSegment
87
168
 
169
+ /**
170
+ * @public
171
+ */
172
+ export declare function isSpan(
173
+ context: Pick<EditorContext, 'schema'>,
174
+ child: PortableTextChild,
175
+ ): child is PortableTextSpan
176
+
177
+ /**
178
+ * @public
179
+ */
180
+ export declare function isTextBlock(
181
+ context: Pick<EditorContext, 'schema'>,
182
+ block: PortableTextBlock,
183
+ ): block is PortableTextTextBlock
184
+
185
+ /**
186
+ * @beta
187
+ */
188
+ export declare function mergeTextBlocks({
189
+ context,
190
+ targetBlock,
191
+ incomingBlock,
192
+ }: {
193
+ context: Pick<EditorContext, 'keyGenerator' | 'schema'>
194
+ targetBlock: PortableTextTextBlock
195
+ incomingBlock: PortableTextTextBlock
196
+ }): PortableTextTextBlock<PortableTextSpan | PortableTextObject>
197
+
198
+ declare type MIMEType = `${string}/${string}`
199
+
200
+ /**
201
+ * @internal
202
+ */
203
+ declare type PickFromUnion<
204
+ TUnion,
205
+ TTagKey extends keyof TUnion,
206
+ TPickedTags extends TUnion[TTagKey],
207
+ > = TUnion extends Record<TTagKey, TPickedTags> ? TUnion : never
208
+
209
+ /** @internal */
210
+ declare type PortableTextMemberSchemaTypes = {
211
+ annotations: (ObjectSchemaType & {
212
+ i18nTitleKey?: string
213
+ })[]
214
+ block: ObjectSchemaType
215
+ blockObjects: ObjectSchemaType[]
216
+ decorators: BlockDecoratorDefinition[]
217
+ inlineObjects: ObjectSchemaType[]
218
+ portableText: ArraySchemaType<PortableTextBlock>
219
+ span: ObjectSchemaType
220
+ styles: BlockStyleDefinition[]
221
+ lists: BlockListDefinition[]
222
+ }
223
+
88
224
  /**
89
225
  * @public
90
226
  */
@@ -92,6 +228,18 @@ export declare function reverseSelection(
92
228
  selection: NonNullable<EditorSelection>,
93
229
  ): NonNullable<EditorSelection>
94
230
 
231
+ declare type Serializer<TMIMEType extends MIMEType> = ({
232
+ context,
233
+ event,
234
+ }: {
235
+ context: EditorContext
236
+ event: PickFromUnion<ConverterEvent<TMIMEType>, 'type', 'serialize'>
237
+ }) => PickFromUnion<
238
+ ConverterEvent<TMIMEType>,
239
+ 'type',
240
+ 'serialization.success' | 'serialization.failure'
241
+ >
242
+
95
243
  /**
96
244
  * @public
97
245
  */
@@ -114,4 +262,22 @@ export declare function spanSelectionPointToBlockOffset({
114
262
  selectionPoint: EditorSelectionPoint
115
263
  }): BlockOffset | undefined
116
264
 
265
+ /**
266
+ * @beta
267
+ */
268
+ export declare function splitTextBlock({
269
+ context,
270
+ block,
271
+ point,
272
+ }: {
273
+ context: Pick<EditorContext, 'schema'>
274
+ block: PortableTextTextBlock
275
+ point: EditorSelectionPoint
276
+ }):
277
+ | {
278
+ before: PortableTextTextBlock
279
+ after: PortableTextTextBlock
280
+ }
281
+ | undefined
282
+
117
283
  export {}
@@ -1,5 +1,20 @@
1
- import {KeyedSegment, PortableTextBlock} from '@sanity/types'
2
- import type {Path, PathSegment, PortableTextTextBlock} from '@sanity/types'
1
+ import type {
2
+ ArraySchemaType,
3
+ BlockDecoratorDefinition,
4
+ BlockListDefinition,
5
+ BlockStyleDefinition,
6
+ ObjectSchemaType,
7
+ Path,
8
+ PathSegment,
9
+ PortableTextChild,
10
+ PortableTextTextBlock,
11
+ } from '@sanity/types'
12
+ import {
13
+ KeyedSegment,
14
+ PortableTextBlock,
15
+ PortableTextObject,
16
+ PortableTextSpan,
17
+ } from '@sanity/types'
3
18
 
4
19
  /**
5
20
  * @beta
@@ -25,6 +40,72 @@ export declare function blockOffsetToSpanSelectionPoint({
25
40
  }
26
41
  | undefined
27
42
 
43
+ declare type Converter<TMIMEType extends MIMEType = MIMEType> = {
44
+ mimeType: TMIMEType
45
+ serialize: Serializer<TMIMEType>
46
+ deserialize: Deserializer<TMIMEType>
47
+ }
48
+
49
+ declare type ConverterEvent<TMIMEType extends MIMEType = MIMEType> =
50
+ | {
51
+ type: 'serialize'
52
+ originEvent: 'copy' | 'cut' | 'unknown'
53
+ }
54
+ | {
55
+ type: 'serialization.failure'
56
+ mimeType: TMIMEType
57
+ reason: string
58
+ }
59
+ | {
60
+ type: 'serialization.success'
61
+ data: string
62
+ mimeType: TMIMEType
63
+ originEvent: 'copy' | 'cut' | 'unknown'
64
+ }
65
+ | {
66
+ type: 'deserialize'
67
+ data: string
68
+ }
69
+ | {
70
+ type: 'deserialization.failure'
71
+ mimeType: TMIMEType
72
+ reason: string
73
+ }
74
+ | {
75
+ type: 'deserialization.success'
76
+ data: Array<PortableTextBlock>
77
+ mimeType: TMIMEType
78
+ }
79
+
80
+ declare type Deserializer<TMIMEType extends MIMEType> = ({
81
+ context,
82
+ event,
83
+ }: {
84
+ context: EditorContext
85
+ event: PickFromUnion<ConverterEvent<TMIMEType>, 'type', 'deserialize'>
86
+ }) => PickFromUnion<
87
+ ConverterEvent<TMIMEType>,
88
+ 'type',
89
+ 'deserialization.success' | 'deserialization.failure'
90
+ >
91
+
92
+ /**
93
+ * @public
94
+ */
95
+ declare type EditorContext = {
96
+ activeDecorators: Array<string>
97
+ converters: Array<Converter>
98
+ keyGenerator: () => string
99
+ schema: EditorSchema
100
+ selection: EditorSelection
101
+ value: Array<PortableTextBlock>
102
+ }
103
+
104
+ /**
105
+ * @public
106
+ */
107
+ declare type EditorSchema = PortableTextMemberSchemaTypes
108
+
28
109
  /** @public */
29
110
  export declare type EditorSelection = {
30
111
  anchor: EditorSelectionPoint
@@ -85,6 +166,61 @@ export declare function isKeyedSegment(
85
166
  segment: PathSegment,
86
167
  ): segment is KeyedSegment
87
168
 
169
+ /**
170
+ * @public
171
+ */
172
+ export declare function isSpan(
173
+ context: Pick<EditorContext, 'schema'>,
174
+ child: PortableTextChild,
175
+ ): child is PortableTextSpan
176
+
177
+ /**
178
+ * @public
179
+ */
180
+ export declare function isTextBlock(
181
+ context: Pick<EditorContext, 'schema'>,
182
+ block: PortableTextBlock,
183
+ ): block is PortableTextTextBlock
184
+
185
+ /**
186
+ * @beta
187
+ */
188
+ export declare function mergeTextBlocks({
189
+ context,
190
+ targetBlock,
191
+ incomingBlock,
192
+ }: {
193
+ context: Pick<EditorContext, 'keyGenerator' | 'schema'>
194
+ targetBlock: PortableTextTextBlock
195
+ incomingBlock: PortableTextTextBlock
196
+ }): PortableTextTextBlock<PortableTextSpan | PortableTextObject>
197
+
198
+ declare type MIMEType = `${string}/${string}`
199
+
200
+ /**
201
+ * @internal
202
+ */
203
+ declare type PickFromUnion<
204
+ TUnion,
205
+ TTagKey extends keyof TUnion,
206
+ TPickedTags extends TUnion[TTagKey],
207
+ > = TUnion extends Record<TTagKey, TPickedTags> ? TUnion : never
208
+
209
+ /** @internal */
210
+ declare type PortableTextMemberSchemaTypes = {
211
+ annotations: (ObjectSchemaType & {
212
+ i18nTitleKey?: string
213
+ })[]
214
+ block: ObjectSchemaType
215
+ blockObjects: ObjectSchemaType[]
216
+ decorators: BlockDecoratorDefinition[]
217
+ inlineObjects: ObjectSchemaType[]
218
+ portableText: ArraySchemaType<PortableTextBlock>
219
+ span: ObjectSchemaType
220
+ styles: BlockStyleDefinition[]
221
+ lists: BlockListDefinition[]
222
+ }
223
+
88
224
  /**
89
225
  * @public
90
226
  */
@@ -92,6 +228,18 @@ export declare function reverseSelection(
92
228
  selection: NonNullable<EditorSelection>,
93
229
  ): NonNullable<EditorSelection>
94
230
 
231
+ declare type Serializer<TMIMEType extends MIMEType> = ({
232
+ context,
233
+ event,
234
+ }: {
235
+ context: EditorContext
236
+ event: PickFromUnion<ConverterEvent<TMIMEType>, 'type', 'serialize'>
237
+ }) => PickFromUnion<
238
+ ConverterEvent<TMIMEType>,
239
+ 'type',
240
+ 'serialization.success' | 'serialization.failure'
241
+ >
242
+
95
243
  /**
96
244
  * @public
97
245
  */
@@ -114,4 +262,22 @@ export declare function spanSelectionPointToBlockOffset({
114
262
  selectionPoint: EditorSelectionPoint
115
263
  }): BlockOffset | undefined
116
264
 
265
+ /**
266
+ * @beta
267
+ */
268
+ export declare function splitTextBlock({
269
+ context,
270
+ block,
271
+ point,
272
+ }: {
273
+ context: Pick<EditorContext, 'schema'>
274
+ block: PortableTextTextBlock
275
+ point: EditorSelectionPoint
276
+ }):
277
+ | {
278
+ before: PortableTextTextBlock
279
+ after: PortableTextTextBlock
280
+ }
281
+ | undefined
282
+
117
283
  export {}
@@ -1,7 +1,73 @@
1
1
  import { blockOffsetToSpanSelectionPoint, getTextBlockText, isEmptyTextBlock, spanSelectionPointToBlockOffset } from "../_chunks-es/util.is-empty-text-block.js";
2
2
  import { getBlockEndPoint, getBlockStartPoint, isEqualSelectionPoints, isKeyedSegment } from "../_chunks-es/util.is-equal-selection-points.js";
3
+ import { parseBlock } from "../_chunks-es/parse-blocks.js";
3
4
  import { reverseSelection } from "../_chunks-es/util.reverse-selection.js";
4
5
  import { sliceBlocks } from "../_chunks-es/util.slice-blocks.js";
6
+ function isSpan(context, child) {
7
+ return child._type === context.schema.span.name;
8
+ }
9
+ function isTextBlock(context, block) {
10
+ return block._type === context.schema.block.name;
11
+ }
12
+ function mergeTextBlocks({
13
+ context,
14
+ targetBlock,
15
+ incomingBlock
16
+ }) {
17
+ const parsedIncomingBlock = parseBlock({
18
+ context,
19
+ block: incomingBlock,
20
+ options: {
21
+ refreshKeys: !0
22
+ }
23
+ });
24
+ return !parsedIncomingBlock || !isTextBlock(context, parsedIncomingBlock) ? targetBlock : {
25
+ ...targetBlock,
26
+ children: [...targetBlock.children, ...parsedIncomingBlock.children],
27
+ markDefs: [...targetBlock.markDefs ?? [], ...parsedIncomingBlock.markDefs ?? []]
28
+ };
29
+ }
30
+ function splitTextBlock({
31
+ context,
32
+ block,
33
+ point
34
+ }) {
35
+ const firstChild = block.children.at(0), lastChild = block.children.at(block.children.length - 1);
36
+ if (!firstChild || !lastChild)
37
+ return;
38
+ const before = sliceBlocks({
39
+ blocks: [block],
40
+ selection: {
41
+ anchor: {
42
+ path: [{
43
+ _key: block._key
44
+ }, "children", {
45
+ _key: firstChild._key
46
+ }],
47
+ offset: 0
48
+ },
49
+ focus: point
50
+ }
51
+ }).at(0), after = sliceBlocks({
52
+ blocks: [block],
53
+ selection: {
54
+ anchor: point,
55
+ focus: {
56
+ path: [{
57
+ _key: block._key
58
+ }, "children", {
59
+ _key: lastChild._key
60
+ }],
61
+ offset: isSpan(context, lastChild) ? lastChild.text.length : 0
62
+ }
63
+ }
64
+ }).at(0);
65
+ if (!(!before || !after) && !(!isTextBlock(context, before) || !isTextBlock(context, after)))
66
+ return {
67
+ before,
68
+ after
69
+ };
70
+ }
5
71
  export {
6
72
  blockOffsetToSpanSelectionPoint,
7
73
  getBlockEndPoint,
@@ -10,8 +76,12 @@ export {
10
76
  isEmptyTextBlock,
11
77
  isEqualSelectionPoints,
12
78
  isKeyedSegment,
79
+ isSpan,
80
+ isTextBlock,
81
+ mergeTextBlocks,
13
82
  reverseSelection,
14
83
  sliceBlocks,
15
- spanSelectionPointToBlockOffset
84
+ spanSelectionPointToBlockOffset,
85
+ splitTextBlock
16
86
  };
17
87
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
1
+ {"version":3,"file":"index.js","sources":["../../src/utils/util.is-span.ts","../../src/utils/util.is-text-block.ts","../../src/utils/util.merge-text-blocks.ts","../../src/utils/util.split-text-block.ts"],"sourcesContent":["import type {PortableTextChild, PortableTextSpan} from '@sanity/types'\nimport type {EditorContext} from '../selectors'\n\n/**\n * @public\n */\nexport function isSpan(\n context: Pick<EditorContext, 'schema'>,\n child: PortableTextChild,\n): child is PortableTextSpan {\n return child._type === context.schema.span.name\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":["isSpan","context","child","_type","schema","span","name","isTextBlock","block","mergeTextBlocks","targetBlock","incomingBlock","parsedIncomingBlock","parseBlock","options","refreshKeys","children","markDefs","splitTextBlock","point","firstChild","at","lastChild","length","before","sliceBlocks","blocks","selection","anchor","path","_key","offset","focus","after","text"],"mappings":";;;;;AAMgBA,SAAAA,OACdC,SACAC,OAC2B;AAC3B,SAAOA,MAAMC,UAAUF,QAAQG,OAAOC,KAAKC;AAC7C;ACLgBC,SAAAA,YACdN,SACAO,OACgC;AAChC,SAAOA,MAAML,UAAUF,QAAQG,OAAOI,MAAMF;AAC9C;ACHO,SAASG,gBAAgB;AAAA,EAC9BR;AAAAA,EACAS;AAAAA,EACAC;AAKF,GAAG;AACD,QAAMC,sBAAsBC,WAAW;AAAA,IACrCZ;AAAAA,IACAO,OAAOG;AAAAA,IACPG,SAAS;AAAA,MAACC,aAAa;AAAA,IAAA;AAAA,EAAI,CAC5B;AAED,SAAI,CAACH,uBAAuB,CAACL,YAAYN,SAASW,mBAAmB,IAC5DF,cAGF;AAAA,IACL,GAAGA;AAAAA,IACHM,UAAU,CAAC,GAAGN,YAAYM,UAAU,GAAGJ,oBAAoBI,QAAQ;AAAA,IACnEC,UAAU,CACR,GAAIP,YAAYO,YAAY,CAAA,GAC5B,GAAIL,oBAAoBK,YAAY,CAAG,CAAA;AAAA,EAE3C;AACF;AC3BO,SAASC,eAAe;AAAA,EAC7BjB;AAAAA,EACAO;AAAAA,EACAW;AAKF,GAA8E;AAC5E,QAAMC,aAAaZ,MAAMQ,SAASK,GAAG,CAAC,GAChCC,YAAYd,MAAMQ,SAASK,GAAGb,MAAMQ,SAASO,SAAS,CAAC;AAEzD,MAAA,CAACH,cAAc,CAACE;AAClB;AAGF,QAAME,SAASC,YAAY;AAAA,IACzBC,QAAQ,CAAClB,KAAK;AAAA,IACdmB,WAAW;AAAA,MACTC,QAAQ;AAAA,QACNC,MAAM,CAAC;AAAA,UAACC,MAAMtB,MAAMsB;AAAAA,WAAO,YAAY;AAAA,UAACA,MAAMV,WAAWU;AAAAA,QAAAA,CAAK;AAAA,QAC9DC,QAAQ;AAAA,MACV;AAAA,MACAC,OAAOb;AAAAA,IAAAA;AAAAA,EAEV,CAAA,EAAEE,GAAG,CAAC,GACDY,QAAQR,YAAY;AAAA,IACxBC,QAAQ,CAAClB,KAAK;AAAA,IACdmB,WAAW;AAAA,MACTC,QAAQT;AAAAA,MACRa,OAAO;AAAA,QACLH,MAAM,CAAC;AAAA,UAACC,MAAMtB,MAAMsB;AAAAA,WAAO,YAAY;AAAA,UAACA,MAAMR,UAAUQ;AAAAA,QAAAA,CAAK;AAAA,QAC7DC,QAAQ/B,OAAOC,SAASqB,SAAS,IAAIA,UAAUY,KAAKX,SAAS;AAAA,MAAA;AAAA,IAC/D;AAAA,EACF,CACD,EAAEF,GAAG,CAAC;AAEP,MAAI,EAACG,CAAAA,UAAU,CAACS,UAIZ,EAAC1B,CAAAA,YAAYN,SAASuB,MAAM,KAAK,CAACjB,YAAYN,SAASgC,KAAK;AAIzD,WAAA;AAAA,MAACT;AAAAA,MAAQS;AAAAA,IAAK;AACvB;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@portabletext/editor",
3
- "version": "1.28.0",
3
+ "version": "1.30.0",
4
4
  "description": "Portable Text Editor made in React",
5
5
  "keywords": [
6
6
  "sanity",
@@ -34,7 +34,7 @@
34
34
  "default": "./lib/index.js"
35
35
  },
36
36
  "./behaviors": {
37
- "source": "./src/behaviors/index.ts",
37
+ "source": "./src/behaviors/_exports/index.ts",
38
38
  "import": "./lib/behaviors/index.js",
39
39
  "require": "./lib/behaviors/index.cjs",
40
40
  "default": "./lib/behaviors/index.js"
@@ -0,0 +1,18 @@
1
+ import {deleteFragment, select} from 'slate'
2
+ import {toSlateRange} from '../internal-utils/ranges'
3
+ import type {BehaviorActionImplementation} from './behavior.actions'
4
+
5
+ export const deleteActionImplementation: BehaviorActionImplementation<
6
+ 'delete'
7
+ > = ({action}) => {
8
+ const range = toSlateRange(action.selection, action.editor)
9
+
10
+ if (!range) {
11
+ throw new Error(
12
+ `Failed to get Slate Range for selection ${JSON.stringify(action.selection)}`,
13
+ )
14
+ }
15
+
16
+ select(action.editor, range)
17
+ deleteFragment(action.editor)
18
+ }
@@ -1,5 +1,5 @@
1
1
  import {isEqual} from 'lodash'
2
- import {Editor, Node, Path, Transforms} from 'slate'
2
+ import {Editor, insertText, Node, Path, Transforms} from 'slate'
3
3
  import type {SlateTextBlock, VoidElement} from '../types/slate'
4
4
  import type {BehaviorActionImplementation} from './behavior.actions'
5
5
 
@@ -146,11 +146,6 @@ export const insertBreakActionImplementation: BehaviorActionImplementation<
146
146
 
147
147
  export const insertSoftBreakActionImplementation: BehaviorActionImplementation<
148
148
  'insert.soft break'
149
- > = ({context, action}) => {
150
- // This mimics Slate's internal which also just does a regular insert break
151
- // when soft-breaking
152
- insertBreakActionImplementation({
153
- context,
154
- action: {...action, type: 'insert.break'},
155
- })
149
+ > = ({action}) => {
150
+ insertText(action.editor, '\n')
156
151
  }
@@ -31,6 +31,7 @@ import {insertBlock} from './behavior.action-utils.insert-block'
31
31
  import {blockSetBehaviorActionImplementation} from './behavior.action.block.set'
32
32
  import {blockUnsetBehaviorActionImplementation} from './behavior.action.block.unset'
33
33
  import {dataTransferSetActionImplementation} from './behavior.action.data-transfer-set'
34
+ import {deleteActionImplementation} from './behavior.action.delete'
34
35
  import {insertBlockObjectActionImplementation} from './behavior.action.insert-block-object'
35
36
  import {insertBlocksActionImplementation} from './behavior.action.insert-blocks'
36
37
  import {
@@ -89,6 +90,7 @@ const behaviorActionImplementations: BehaviorActionImplementations = {
89
90
  'focus': ({action}) => {
90
91
  ReactEditor.focus(action.editor)
91
92
  },
93
+ 'delete': deleteActionImplementation,
92
94
  'delete.backward': ({action}) => {
93
95
  deleteBackward(action.editor, action.unit)
94
96
  },
@@ -412,6 +414,13 @@ function performDefaultAction({
412
414
  })
413
415
  break
414
416
  }
417
+ case 'delete': {
418
+ behaviorActionImplementations.delete({
419
+ context,
420
+ action,
421
+ })
422
+ break
423
+ }
415
424
  case 'delete.backward': {
416
425
  behaviorActionImplementations['delete.backward']({
417
426
  context,
@@ -0,0 +1 @@
1
+ export * from '../index'