@portabletext/editor 1.7.1 → 1.8.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/index.js CHANGED
@@ -296,6 +296,21 @@ const breakingBlockObject = {
296
296
  blockObjects: coreBlockObjectBehaviors,
297
297
  lists: coreListBehaviors
298
298
  };
299
+ function isPortableTextSpan(node) {
300
+ return node._type === "span" && "text" in node && typeof node.text == "string" && (typeof node.marks > "u" || Array.isArray(node.marks) && node.marks.every((mark) => typeof mark == "string"));
301
+ }
302
+ function isPortableTextBlock(node) {
303
+ return (
304
+ // A block doesn't _have_ to be named 'block' - to differentiate between
305
+ // allowed child types and marks, one might name them differently
306
+ typeof node._type == "string" && // Toolkit-types like nested spans are @-prefixed
307
+ node._type[0] !== "@" && // `markDefs` isn't _required_ per say, but if it's there, it needs to be an array
308
+ (!("markDefs" in node) || !node.markDefs || Array.isArray(node.markDefs) && // Every mark definition needs to have an `_key` to be mappable in child spans
309
+ node.markDefs.every((def) => typeof def._key == "string")) && // `children` is required and needs to be an array
310
+ "children" in node && Array.isArray(node.children) && // All children are objects with `_type` (usually spans, but can contain other stuff)
311
+ node.children.every((child) => typeof child == "object" && "_type" in child)
312
+ );
313
+ }
299
314
  function createMarkdownBehaviors(config) {
300
315
  const automaticBlockquoteOnSpace = {
301
316
  on: "insert text",
@@ -308,7 +323,7 @@ function createMarkdownBehaviors(config) {
308
323
  const selectionCollapsed = selectionIsCollapsed(context), focusTextBlock = getFocusTextBlock(context), focusSpan = getFocusSpan(context);
309
324
  if (!selectionCollapsed || !focusTextBlock || !focusSpan)
310
325
  return !1;
311
- const caretAtTheEndOfQuote = context.selection.focus.offset === 1, looksLikeMarkdownQuote = /^>/.test(focusSpan.node.text), blockquoteStyle = config.mapBlockquoteStyle(context.schema);
326
+ const caretAtTheEndOfQuote = context.selection.focus.offset === 1, looksLikeMarkdownQuote = /^>/.test(focusSpan.node.text), blockquoteStyle = config.mapBlockquoteStyle?.(context.schema);
312
327
  return caretAtTheEndOfQuote && looksLikeMarkdownQuote && blockquoteStyle !== void 0 ? {
313
328
  focusTextBlock,
314
329
  focusSpan,
@@ -343,6 +358,48 @@ function createMarkdownBehaviors(config) {
343
358
  }
344
359
  }
345
360
  }]]
361
+ }, automaticBreak = {
362
+ on: "insert text",
363
+ guard: ({
364
+ context,
365
+ event
366
+ }) => {
367
+ if (event.text !== "-")
368
+ return !1;
369
+ const breakObject = config.mapBreakObject?.(context.schema), focusBlock = getFocusTextBlock(context), selectionCollapsed = selectionIsCollapsed(context);
370
+ if (!breakObject || !focusBlock || !selectionCollapsed)
371
+ return !1;
372
+ const onlyText = focusBlock.node.children.every(isPortableTextSpan), blockText = focusBlock.node.children.map((child) => child.text ?? "").join("");
373
+ return onlyText && blockText === "--" ? {
374
+ breakObject,
375
+ focusBlock
376
+ } : !1;
377
+ },
378
+ actions: [() => [{
379
+ type: "insert text",
380
+ text: "-"
381
+ }], (_, {
382
+ breakObject,
383
+ focusBlock
384
+ }) => [{
385
+ type: "insert block object",
386
+ ...breakObject
387
+ }, {
388
+ type: "delete",
389
+ selection: {
390
+ anchor: {
391
+ path: focusBlock.path,
392
+ offset: 0
393
+ },
394
+ focus: {
395
+ path: focusBlock.path,
396
+ offset: 0
397
+ }
398
+ }
399
+ }, {
400
+ type: "insert text block",
401
+ decorators: []
402
+ }]]
346
403
  }, automaticHeadingOnSpace = {
347
404
  on: "insert text",
348
405
  guard: ({
@@ -357,7 +414,7 @@ function createMarkdownBehaviors(config) {
357
414
  const markdownHeadingSearch = /^#+/.exec(focusSpan.node.text), headingLevel = markdownHeadingSearch ? markdownHeadingSearch[0].length : void 0;
358
415
  if (context.selection.focus.offset !== headingLevel)
359
416
  return !1;
360
- const headingStyle = headingLevel !== void 0 ? config.mapHeadingStyle(context.schema, headingLevel) : void 0;
417
+ const headingStyle = headingLevel !== void 0 ? config.mapHeadingStyle?.(context.schema, headingLevel) : void 0;
361
418
  return headingLevel !== void 0 && headingStyle !== void 0 ? {
362
419
  focusTextBlock,
363
420
  focusSpan,
@@ -402,7 +459,7 @@ function createMarkdownBehaviors(config) {
402
459
  const selectionCollapsed = selectionIsCollapsed(context), focusTextBlock = getFocusTextBlock(context), focusSpan = getFocusSpan(context);
403
460
  if (!selectionCollapsed || !focusTextBlock || !focusSpan)
404
461
  return !1;
405
- const atTheBeginningOfBLock = focusTextBlock.node.children[0]._key === focusSpan.node._key && context.selection.focus.offset === 0, defaultStyle = config.mapDefaultStyle(context.schema);
462
+ const atTheBeginningOfBLock = focusTextBlock.node.children[0]._key === focusSpan.node._key && context.selection.focus.offset === 0, defaultStyle = config.mapDefaultStyle?.(context.schema);
406
463
  return atTheBeginningOfBLock && defaultStyle && focusTextBlock.node.style !== defaultStyle ? {
407
464
  defaultStyle,
408
465
  focusTextBlock
@@ -427,7 +484,7 @@ function createMarkdownBehaviors(config) {
427
484
  const selectionCollapsed = selectionIsCollapsed(context), focusTextBlock = getFocusTextBlock(context), focusSpan = getFocusSpan(context);
428
485
  if (!selectionCollapsed || !focusTextBlock || !focusSpan)
429
486
  return !1;
430
- const defaultStyle = config.mapDefaultStyle(context.schema), looksLikeUnorderedList = /^(-|\*)/.test(focusSpan.node.text), unorderedListStyle = config.mapUnorderedListStyle(context.schema), caretAtTheEndOfUnorderedList = context.selection.focus.offset === 1;
487
+ const defaultStyle = config.mapDefaultStyle?.(context.schema), looksLikeUnorderedList = /^(-|\*)/.test(focusSpan.node.text), unorderedListStyle = config.mapUnorderedListStyle?.(context.schema), caretAtTheEndOfUnorderedList = context.selection.focus.offset === 1;
431
488
  if (defaultStyle && caretAtTheEndOfUnorderedList && looksLikeUnorderedList && unorderedListStyle !== void 0)
432
489
  return {
433
490
  focusTextBlock,
@@ -436,7 +493,7 @@ function createMarkdownBehaviors(config) {
436
493
  listItemLength: 1,
437
494
  style: defaultStyle
438
495
  };
439
- const looksLikeOrderedList = /^1./.test(focusSpan.node.text), orderedListStyle = config.mapOrderedListStyle(context.schema), caretAtTheEndOfOrderedList = context.selection.focus.offset === 2;
496
+ const looksLikeOrderedList = /^1./.test(focusSpan.node.text), orderedListStyle = config.mapOrderedListStyle?.(context.schema), caretAtTheEndOfOrderedList = context.selection.focus.offset === 2;
440
497
  return defaultStyle && caretAtTheEndOfOrderedList && looksLikeOrderedList && orderedListStyle !== void 0 ? {
441
498
  focusTextBlock,
442
499
  focusSpan,
@@ -474,7 +531,7 @@ function createMarkdownBehaviors(config) {
474
531
  }
475
532
  }]]
476
533
  };
477
- return [automaticBlockquoteOnSpace, automaticHeadingOnSpace, clearStyleOnBackspace, automaticListOnSpace];
534
+ return [automaticBlockquoteOnSpace, automaticBreak, automaticHeadingOnSpace, clearStyleOnBackspace, automaticListOnSpace];
478
535
  }
479
536
  function getPortableTextMemberSchemaTypes(portableTextType) {
480
537
  if (!portableTextType)
@@ -3904,21 +3961,6 @@ function createWithPortableTextLists(types2) {
3904
3961
  }, editor;
3905
3962
  };
3906
3963
  }
3907
- function isPortableTextSpan(node) {
3908
- return node._type === "span" && "text" in node && typeof node.text == "string" && (typeof node.marks > "u" || Array.isArray(node.marks) && node.marks.every((mark) => typeof mark == "string"));
3909
- }
3910
- function isPortableTextBlock(node) {
3911
- return (
3912
- // A block doesn't _have_ to be named 'block' - to differentiate between
3913
- // allowed child types and marks, one might name them differently
3914
- typeof node._type == "string" && // Toolkit-types like nested spans are @-prefixed
3915
- node._type[0] !== "@" && // `markDefs` isn't _required_ per say, but if it's there, it needs to be an array
3916
- (!("markDefs" in node) || !node.markDefs || Array.isArray(node.markDefs) && // Every mark definition needs to have an `_key` to be mappable in child spans
3917
- node.markDefs.every((def) => typeof def._key == "string")) && // `children` is required and needs to be an array
3918
- "children" in node && Array.isArray(node.children) && // All children are objects with `_type` (usually spans, but can contain other stuff)
3919
- node.children.every((child) => typeof child == "object" && "_type" in child)
3920
- );
3921
- }
3922
3964
  function getPreviousSpan({
3923
3965
  editor,
3924
3966
  blockPath,
@@ -4731,32 +4773,18 @@ function createEditableAPI(editor, editorActor) {
4731
4773
  at: editor.selection
4732
4774
  }), editor.onChange(), toPortableTextRange(fromSlateValue(editor.children, types2.block.name, KEY_TO_VALUE_ELEMENT.get(editor)), editor.selection, types2)?.focus.path || [];
4733
4775
  },
4734
- insertBlock: (type, value) => {
4735
- const block = toSlateValue([{
4736
- _key: editorActor.getSnapshot().context.keyGenerator(),
4737
- _type: type.name,
4738
- ...value || {}
4739
- }], {
4740
- schemaTypes: editorActor.getSnapshot().context.schema
4741
- })[0];
4742
- if (!editor.selection) {
4743
- const lastBlock = Array.from(slate.Editor.nodes(editor, {
4744
- match: (n) => !slate.Editor.isEditor(n),
4745
- at: [],
4746
- reverse: !0
4747
- }))[0];
4748
- return slate.Editor.insertNode(editor, block), lastBlock && isEqualToEmptyEditor([lastBlock[0]], types2) && slate.Transforms.removeNodes(editor, {
4749
- at: lastBlock[1]
4750
- }), editor.onChange(), toPortableTextRange(fromSlateValue(editor.children, types2.block.name, KEY_TO_VALUE_ELEMENT.get(editor)), editor.selection, types2)?.focus.path ?? [];
4776
+ insertBlock: (type, value) => insertBlockObjectActionImplementation({
4777
+ context: {
4778
+ keyGenerator: editorActor.getSnapshot().context.keyGenerator,
4779
+ schema: types2
4780
+ },
4781
+ action: {
4782
+ type: "insert block object",
4783
+ name: type.name,
4784
+ value,
4785
+ editor
4751
4786
  }
4752
- const focusBlock = Array.from(slate.Editor.nodes(editor, {
4753
- at: editor.selection.focus.path.slice(0, 1),
4754
- match: (n) => n._type === types2.block.name
4755
- }))[0];
4756
- return slate.Editor.insertNode(editor, block), focusBlock && isEqualToEmptyEditor([focusBlock[0]], types2) && slate.Transforms.removeNodes(editor, {
4757
- at: focusBlock[1]
4758
- }), editor.onChange(), toPortableTextRange(fromSlateValue(editor.children, types2.block.name, KEY_TO_VALUE_ELEMENT.get(editor)), editor.selection, types2)?.focus.path || [];
4759
- },
4787
+ }),
4760
4788
  hasBlockStyle: (style) => {
4761
4789
  try {
4762
4790
  return editor.pteHasBlockStyle(style);
@@ -4926,6 +4954,35 @@ function createEditableAPI(editor, editorActor) {
4926
4954
  }
4927
4955
  };
4928
4956
  }
4957
+ const insertBlockObjectActionImplementation = ({
4958
+ context,
4959
+ action
4960
+ }) => {
4961
+ const editor = action.editor, types2 = context.schema, block = toSlateValue([{
4962
+ _key: context.keyGenerator(),
4963
+ _type: action.name,
4964
+ ...action.value ? action.value : {}
4965
+ }], {
4966
+ schemaTypes: context.schema
4967
+ })[0];
4968
+ if (!editor.selection) {
4969
+ const lastBlock = Array.from(slate.Editor.nodes(editor, {
4970
+ match: (n) => !slate.Editor.isEditor(n),
4971
+ at: [],
4972
+ reverse: !0
4973
+ }))[0];
4974
+ return slate.Editor.insertNode(editor, block), lastBlock && isEqualToEmptyEditor([lastBlock[0]], types2) && slate.Transforms.removeNodes(editor, {
4975
+ at: lastBlock[1]
4976
+ }), editor.onChange(), toPortableTextRange(fromSlateValue(editor.children, types2.block.name, KEY_TO_VALUE_ELEMENT.get(editor)), editor.selection, types2)?.focus.path ?? [];
4977
+ }
4978
+ const focusBlock = Array.from(slate.Editor.nodes(editor, {
4979
+ at: editor.selection.focus.path.slice(0, 1),
4980
+ match: (n) => n._type === types2.block.name
4981
+ }))[0];
4982
+ return slate.Editor.insertNode(editor, block), focusBlock && isEqualToEmptyEditor([focusBlock[0]], types2) && slate.Transforms.removeNodes(editor, {
4983
+ at: focusBlock[1]
4984
+ }), editor.onChange(), toPortableTextRange(fromSlateValue(editor.children, types2.block.name, KEY_TO_VALUE_ELEMENT.get(editor)), editor.selection, types2)?.focus.path || [];
4985
+ };
4929
4986
  function isAnnotationActive({
4930
4987
  editor,
4931
4988
  annotation
@@ -5306,6 +5363,7 @@ const addAnnotationActionImplementation = ({
5306
5363
  at: location
5307
5364
  });
5308
5365
  },
5366
+ "insert block object": insertBlockObjectActionImplementation,
5309
5367
  "insert break": insertBreakActionImplementation,
5310
5368
  "insert soft break": insertSoftBreakActionImplementation,
5311
5369
  "insert text": ({
@@ -5363,6 +5421,13 @@ function performAction({
5363
5421
  });
5364
5422
  break;
5365
5423
  }
5424
+ case "insert block object": {
5425
+ behaviorActionImplementations["insert block object"]({
5426
+ context,
5427
+ action
5428
+ });
5429
+ break;
5430
+ }
5366
5431
  case "insert text block": {
5367
5432
  behaviorActionImplementations["insert text block"]({
5368
5433
  context,