@portabletext/editor 1.8.0 → 1.9.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
@@ -533,6 +533,65 @@ function createMarkdownBehaviors(config) {
533
533
  };
534
534
  return [automaticBlockquoteOnSpace, automaticBreak, automaticHeadingOnSpace, clearStyleOnBackspace, automaticListOnSpace];
535
535
  }
536
+ function createLinkBehaviors(config) {
537
+ const pasteLinkOnSelection = {
538
+ on: "paste",
539
+ guard: ({
540
+ context,
541
+ event
542
+ }) => {
543
+ const selectionCollapsed = selectionIsCollapsed(context), text = event.clipboardData.getData("text/plain"), url = looksLikeUrl(text) ? text : void 0, annotation = url !== void 0 ? config.mapLinkAnnotation?.({
544
+ url,
545
+ schema: context.schema
546
+ }) : void 0;
547
+ return annotation && !selectionCollapsed ? {
548
+ annotation
549
+ } : !1;
550
+ },
551
+ actions: [(_, {
552
+ annotation
553
+ }) => [{
554
+ type: "annotation.add",
555
+ annotation
556
+ }]]
557
+ }, pasteLinkAtCaret = {
558
+ on: "paste",
559
+ guard: ({
560
+ context,
561
+ event
562
+ }) => {
563
+ const focusSpan = getFocusSpan(context), selectionCollapsed = selectionIsCollapsed(context);
564
+ if (!focusSpan || !selectionCollapsed)
565
+ return !1;
566
+ const text = event.clipboardData.getData("text/plain"), url = looksLikeUrl(text) ? text : void 0, annotation = url !== void 0 ? config.mapLinkAnnotation?.({
567
+ url,
568
+ schema: context.schema
569
+ }) : void 0;
570
+ return url && annotation && selectionCollapsed ? {
571
+ focusSpan,
572
+ annotation,
573
+ url
574
+ } : !1;
575
+ },
576
+ actions: [(_, {
577
+ annotation,
578
+ url
579
+ }) => [{
580
+ type: "insert span",
581
+ text: url,
582
+ annotations: [annotation]
583
+ }]]
584
+ };
585
+ return [pasteLinkOnSelection, pasteLinkAtCaret];
586
+ }
587
+ function looksLikeUrl(text) {
588
+ let looksLikeUrl2 = !1;
589
+ try {
590
+ new URL(text), looksLikeUrl2 = !0;
591
+ } catch {
592
+ }
593
+ return looksLikeUrl2;
594
+ }
536
595
  function getPortableTextMemberSchemaTypes(portableTextType) {
537
596
  if (!portableTextType)
538
597
  throw new Error("Parameter 'portabletextType' missing (required)");
@@ -4447,11 +4506,11 @@ function isDecoratorActive({
4447
4506
  }) {
4448
4507
  if (!editor.selection)
4449
4508
  return !1;
4450
- const selectedNodes = Array.from(slate.Editor.nodes(editor, {
4509
+ const selectedTextNodes = Array.from(slate.Editor.nodes(editor, {
4451
4510
  match: slate.Text.isText,
4452
4511
  at: editor.selection
4453
4512
  }));
4454
- return slate.Range.isExpanded(editor.selection) ? selectedNodes.every((n) => {
4513
+ return selectedTextNodes.length === 0 ? !1 : slate.Range.isExpanded(editor.selection) ? selectedTextNodes.every((n) => {
4455
4514
  const [node] = n;
4456
4515
  return node.marks?.includes(decorator);
4457
4516
  }) : ({
@@ -5279,6 +5338,35 @@ const addAnnotationActionImplementation = ({
5279
5338
  type: "insert break"
5280
5339
  }
5281
5340
  });
5341
+ }, insertSpanActionImplementation = ({
5342
+ context,
5343
+ action
5344
+ }) => {
5345
+ if (!action.editor.selection) {
5346
+ console.error("Unable to perform action without selection", action);
5347
+ return;
5348
+ }
5349
+ const [focusBlock, focusBlockPath] = Array.from(slate.Editor.nodes(action.editor, {
5350
+ at: action.editor.selection.focus.path,
5351
+ match: (node) => action.editor.isTextBlock(node)
5352
+ }))[0] ?? [void 0, void 0];
5353
+ if (!focusBlock || !focusBlockPath) {
5354
+ console.error("Unable to perform action without focus block", action);
5355
+ return;
5356
+ }
5357
+ const markDefs = focusBlock.markDefs ?? [], annotations = action.annotations ? action.annotations.map((annotation) => ({
5358
+ _type: annotation.name,
5359
+ _key: context.keyGenerator(),
5360
+ ...annotation.value
5361
+ })) : void 0;
5362
+ annotations && annotations.length > 0 && slate.Transforms.setNodes(action.editor, {
5363
+ markDefs: [...markDefs, ...annotations]
5364
+ }), slate.Transforms.insertNodes(action.editor, {
5365
+ _type: "span",
5366
+ _key: context.keyGenerator(),
5367
+ text: action.text,
5368
+ marks: [...annotations?.map((annotation) => annotation._key) ?? [], ...action.decorators ?? []]
5369
+ });
5282
5370
  }, behaviorActionImplementations = {
5283
5371
  "annotation.add": addAnnotationActionImplementation,
5284
5372
  "annotation.remove": removeAnnotationActionImplementation,
@@ -5366,6 +5454,7 @@ const addAnnotationActionImplementation = ({
5366
5454
  "insert block object": insertBlockObjectActionImplementation,
5367
5455
  "insert break": insertBreakActionImplementation,
5368
5456
  "insert soft break": insertSoftBreakActionImplementation,
5457
+ "insert span": insertSpanActionImplementation,
5369
5458
  "insert text": ({
5370
5459
  action
5371
5460
  }) => {
@@ -5392,6 +5481,11 @@ const addAnnotationActionImplementation = ({
5392
5481
  }) => {
5393
5482
  action.effect();
5394
5483
  },
5484
+ paste: ({
5485
+ action
5486
+ }) => {
5487
+ action.editor.insertData(action.clipboardData);
5488
+ },
5395
5489
  select: ({
5396
5490
  action
5397
5491
  }) => {
@@ -5428,6 +5522,13 @@ function performAction({
5428
5522
  });
5429
5523
  break;
5430
5524
  }
5525
+ case "insert span": {
5526
+ behaviorActionImplementations["insert span"]({
5527
+ context,
5528
+ action
5529
+ });
5530
+ break;
5531
+ }
5431
5532
  case "insert text block": {
5432
5533
  behaviorActionImplementations["insert text block"]({
5433
5534
  context,
@@ -5559,11 +5660,18 @@ function performDefaultAction({
5559
5660
  });
5560
5661
  break;
5561
5662
  }
5562
- default:
5663
+ case "insert text": {
5563
5664
  behaviorActionImplementations["insert text"]({
5564
5665
  context,
5565
5666
  action
5566
5667
  });
5668
+ break;
5669
+ }
5670
+ default:
5671
+ behaviorActionImplementations.paste({
5672
+ context,
5673
+ action
5674
+ });
5567
5675
  }
5568
5676
  }
5569
5677
  const networkLogic = xstate.fromCallback(({
@@ -6629,19 +6737,13 @@ const debug = debugWithName("component:Editable"), PLACEHOLDER_STYLE = {
6629
6737
  const handleCopy = react.useCallback((event) => {
6630
6738
  onCopy && onCopy(event) !== void 0 && event.preventDefault();
6631
6739
  }, [onCopy]), handlePaste = react.useCallback((event_0) => {
6632
- if (event_0.preventDefault(), !slateEditor.selection)
6633
- return;
6634
- if (!onPaste) {
6635
- debug("Pasting normally"), slateEditor.insertData(event_0.clipboardData);
6636
- return;
6637
- }
6638
- const value_0 = PortableTextEditor.getValue(portableTextEditor), path = toPortableTextRange(value_0, slateEditor.selection, schemaTypes)?.focus.path || [], onPasteResult = onPaste({
6740
+ const value_0 = PortableTextEditor.getValue(portableTextEditor), path = toPortableTextRange(value_0, slateEditor.selection, schemaTypes)?.focus.path || [], onPasteResult = onPaste?.({
6639
6741
  event: event_0,
6640
6742
  value: value_0,
6641
6743
  path,
6642
6744
  schemaTypes
6643
6745
  });
6644
- onPasteResult === void 0 ? (debug("No result from custom paste handler, pasting normally"), slateEditor.insertData(event_0.clipboardData)) : (editorActor.send({
6746
+ onPasteResult || !slateEditor.selection ? (event_0.preventDefault(), editorActor.send({
6645
6747
  type: "loading"
6646
6748
  }), Promise.resolve(onPasteResult).then((result_0) => {
6647
6749
  debug("Custom paste function from client resolved", result_0), !result_0 || !result_0.insert ? (debug("No result from custom paste handler, pasting normally"), slateEditor.insertData(event_0.clipboardData)) : result_0.insert ? slateEditor.insertFragment(toSlateValue(result_0.insert, {
@@ -6651,7 +6753,14 @@ const debug = debugWithName("component:Editable"), PLACEHOLDER_STYLE = {
6651
6753
  editorActor.send({
6652
6754
  type: "done loading"
6653
6755
  });
6654
- }));
6756
+ })) : event_0.nativeEvent.clipboardData && (event_0.preventDefault(), editorActor.send({
6757
+ type: "behavior event",
6758
+ behaviorEvent: {
6759
+ type: "paste",
6760
+ clipboardData: event_0.nativeEvent.clipboardData
6761
+ },
6762
+ editor: slateEditor
6763
+ })), debug("No result from custom paste handler, pasting normally");
6655
6764
  }, [editorActor, onPaste, portableTextEditor, schemaTypes, slateEditor]), handleOnFocus = react.useCallback((event_1) => {
6656
6765
  if (onFocus && onFocus(event_1), !event_1.isDefaultPrevented()) {
6657
6766
  const selection = PortableTextEditor.getSelection(portableTextEditor);
@@ -6666,15 +6775,11 @@ const debug = debugWithName("component:Editable"), PLACEHOLDER_STYLE = {
6666
6775
  });
6667
6776
  }
6668
6777
  }, [editorActor, onFocus, portableTextEditor, slateEditor]), handleClick = react.useCallback((event_2) => {
6669
- if (onClick && onClick(event_2), slateEditor.selection && event_2.target === event_2.currentTarget) {
6670
- const [lastBlock, path_0] = slate.Node.last(slateEditor, []), focusPath = slateEditor.selection.focus.path.slice(0, 1), lastPath = path_0.slice(0, 1);
6671
- if (slate.Path.equals(focusPath, lastPath)) {
6672
- const node = slate.Node.descendant(slateEditor, path_0.slice(0, 1));
6673
- lastBlock && slate.Editor.isVoid(slateEditor, node) && (slate.Transforms.insertNodes(slateEditor, slateEditor.pteCreateTextBlock({
6674
- decorators: []
6675
- })), slateEditor.onChange());
6676
- }
6677
- }
6778
+ onClick && onClick(event_2);
6779
+ const focusBlockPath = slateEditor.selection ? slateEditor.selection.focus.path.slice(0, 1) : void 0, focusBlock = focusBlockPath ? slate.Node.descendant(slateEditor, focusBlockPath) : void 0, [_, lastNodePath] = slate.Node.last(slateEditor, []), lastBlockPath = lastNodePath.slice(0, 1), lastNodeFocused = focusBlockPath ? slate.Path.equals(lastBlockPath, focusBlockPath) : !1, lastBlockIsVoid = focusBlock ? !slateEditor.isTextBlock(focusBlock) : !1;
6780
+ slateEditor.selection && slate.Range.isCollapsed(slateEditor.selection) && lastNodeFocused && lastBlockIsVoid && (slate.Transforms.insertNodes(slateEditor, slateEditor.pteCreateTextBlock({
6781
+ decorators: []
6782
+ })), slateEditor.onChange());
6678
6783
  }, [onClick, slateEditor]), handleOnBlur = react.useCallback((event_3) => {
6679
6784
  onBlur && onBlur(event_3), event_3.isPropagationStopped() || editorActor.send({
6680
6785
  type: "blur",
@@ -6722,7 +6827,7 @@ const debug = debugWithName("component:Editable"), PLACEHOLDER_STYLE = {
6722
6827
  return scrollSelectionIntoView === null ? noop__default.default : (_editor, domRange) => {
6723
6828
  scrollSelectionIntoView(portableTextEditor, domRange);
6724
6829
  };
6725
- }, [portableTextEditor, scrollSelectionIntoView]), decorate = react.useCallback(([, path_1]) => {
6830
+ }, [portableTextEditor, scrollSelectionIntoView]), decorate = react.useCallback(([, path_0]) => {
6726
6831
  if (isEqualToEmptyEditor(slateEditor.children, schemaTypes))
6727
6832
  return [{
6728
6833
  anchor: {
@@ -6735,18 +6840,18 @@ const debug = debugWithName("component:Editable"), PLACEHOLDER_STYLE = {
6735
6840
  },
6736
6841
  placeholder: !0
6737
6842
  }];
6738
- if (path_1.length === 0)
6843
+ if (path_0.length === 0)
6739
6844
  return [];
6740
- const result_1 = rangeDecorationState.filter((item) => slate.Range.isCollapsed(item) ? path_1.length !== 2 ? !1 : slate.Path.equals(item.focus.path, path_1) && slate.Path.equals(item.anchor.path, path_1) : slate.Range.intersection(item, {
6845
+ const result_1 = rangeDecorationState.filter((item) => slate.Range.isCollapsed(item) ? path_0.length !== 2 ? !1 : slate.Path.equals(item.focus.path, path_0) && slate.Path.equals(item.anchor.path, path_0) : slate.Range.intersection(item, {
6741
6846
  anchor: {
6742
- path: path_1,
6847
+ path: path_0,
6743
6848
  offset: 0
6744
6849
  },
6745
6850
  focus: {
6746
- path: path_1,
6851
+ path: path_0,
6747
6852
  offset: 0
6748
6853
  }
6749
- }) || slate.Range.includes(item, path_1));
6854
+ }) || slate.Range.includes(item, path_0));
6750
6855
  return result_1.length > 0 ? result_1 : [];
6751
6856
  }, [slateEditor, schemaTypes, rangeDecorationState]);
6752
6857
  return react.useEffect(() => {
@@ -6816,6 +6921,7 @@ exports.PortableTextEditable = PortableTextEditable;
6816
6921
  exports.PortableTextEditor = PortableTextEditor;
6817
6922
  exports.coreBehavior = coreBehavior;
6818
6923
  exports.coreBehaviors = coreBehaviors;
6924
+ exports.createLinkBehaviors = createLinkBehaviors;
6819
6925
  exports.createMarkdownBehaviors = createMarkdownBehaviors;
6820
6926
  exports.defineBehavior = defineBehavior;
6821
6927
  exports.defineSchema = defineSchema;