@portabletext/editor 1.1.7 → 1.1.9

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.esm.js CHANGED
@@ -9,7 +9,7 @@ import { isKeySegment, isPortableTextTextBlock, isPortableTextSpan, isPortableTe
9
9
  import { styled } from "styled-components";
10
10
  import uniq from "lodash/uniq.js";
11
11
  import { Subject } from "rxjs";
12
- import { fromCallback, setup, assertEvent, assign, emit, enqueueActions, createActor } from "xstate";
12
+ import { fromCallback, setup, assign, assertEvent, emit, enqueueActions, createActor } from "xstate";
13
13
  import { Schema } from "@sanity/schema";
14
14
  import { isHotkey } from "is-hotkey-esm";
15
15
  import { diffMatchPatch as diffMatchPatch$1, set, insert, setIfMissing, unset, applyAll } from "@portabletext/patches";
@@ -1611,11 +1611,11 @@ function createWithInsertBreak(editorActor, types) {
1611
1611
  (mark) => !types.decorators.some((decorator) => decorator.value === mark)
1612
1612
  ) ?? [], focusBlockPath = editor.selection.focus.path.slice(0, 1), focusBlock = Node.descendant(editor, focusBlockPath);
1613
1613
  if (editor.isTextBlock(focusBlock)) {
1614
- const [start, end] = Range.edges(editor.selection), isEndAtStartOfBlock = isEqual(end, {
1614
+ const [start, end] = Range.edges(editor.selection), atTheStartOfBlock = isEqual(end, {
1615
1615
  path: [...focusBlockPath, 0],
1616
1616
  offset: 0
1617
1617
  });
1618
- if (isEndAtStartOfBlock && Range.isCollapsed(editor.selection)) {
1618
+ if (atTheStartOfBlock && Range.isCollapsed(editor.selection)) {
1619
1619
  Editor.insertNode(
1620
1620
  editor,
1621
1621
  editor.pteCreateTextBlock({
@@ -1626,32 +1626,28 @@ function createWithInsertBreak(editorActor, types) {
1626
1626
  Transforms.select(editor, {
1627
1627
  anchor: { path: [nextBlockPath, 0], offset: 0 },
1628
1628
  focus: { path: [nextBlockPath, 0], offset: 0 }
1629
- }), editor.onChange();
1629
+ });
1630
1630
  return;
1631
1631
  }
1632
- const lastFocusBlockChild = focusBlock.children[focusBlock.children.length - 1], isStartAtEndOfBlock = isEqual(start, {
1632
+ const lastFocusBlockChild = focusBlock.children[focusBlock.children.length - 1], atTheEndOfBlock = isEqual(start, {
1633
1633
  path: [...focusBlockPath, focusBlock.children.length - 1],
1634
1634
  offset: editor.isTextSpan(lastFocusBlockChild) ? lastFocusBlockChild.text.length : 0
1635
1635
  });
1636
- if (isStartAtEndOfBlock && Range.isCollapsed(editor.selection) && focusDecorators.length > 0 && focusAnnotations.length > 0) {
1637
- Editor.withoutNormalizing(editor, () => {
1638
- if (!editor.selection)
1639
- return;
1640
- Editor.insertNode(
1641
- editor,
1642
- editor.pteCreateTextBlock({
1643
- decorators: []
1644
- })
1645
- );
1646
- const [nextBlockPath] = Path.next(focusBlockPath);
1647
- Transforms.setSelection(editor, {
1648
- anchor: { path: [nextBlockPath, 0], offset: 0 },
1649
- focus: { path: [nextBlockPath, 0], offset: 0 }
1650
- });
1651
- }), editor.onChange();
1636
+ if (atTheEndOfBlock && Range.isCollapsed(editor.selection)) {
1637
+ Editor.insertNode(
1638
+ editor,
1639
+ editor.pteCreateTextBlock({
1640
+ decorators: []
1641
+ })
1642
+ );
1643
+ const [nextBlockPath] = Path.next(focusBlockPath);
1644
+ Transforms.setSelection(editor, {
1645
+ anchor: { path: [nextBlockPath, 0], offset: 0 },
1646
+ focus: { path: [nextBlockPath, 0], offset: 0 }
1647
+ });
1652
1648
  return;
1653
1649
  }
1654
- if (!isEndAtStartOfBlock && !isStartAtEndOfBlock) {
1650
+ if (!atTheStartOfBlock && !atTheEndOfBlock) {
1655
1651
  Editor.withoutNormalizing(editor, () => {
1656
1652
  if (!editor.selection)
1657
1653
  return;
@@ -3511,6 +3507,17 @@ function createWithPortableTextMarkModel(editorActor, types) {
3511
3507
  }
3512
3508
  }
3513
3509
  }
3510
+ if (atTheBeginningOfSpan && !spanIsEmpty && previousSpan) {
3511
+ Transforms.insertNodes(editor, {
3512
+ _type: "span",
3513
+ _key: editorActor.getSnapshot().context.keyGenerator(),
3514
+ text: op.text,
3515
+ marks: (previousSpan.marks ?? []).filter(
3516
+ (mark) => decorators.includes(mark)
3517
+ )
3518
+ });
3519
+ return;
3520
+ }
3514
3521
  }
3515
3522
  }
3516
3523
  if (op.type === "remove_text") {
@@ -3814,7 +3821,7 @@ const debug$8 = debugWithName("plugin:withHotKeys"), DEFAULT_HOTKEYS = {
3814
3821
  },
3815
3822
  custom: {}
3816
3823
  };
3817
- function createWithHotkeys(types, portableTextEditor, hotkeysFromOptions) {
3824
+ function createWithHotkeys(portableTextEditor, hotkeysFromOptions) {
3818
3825
  const reservedHotkeys = ["enter", "tab", "shift", "delete", "end"], activeHotkeys = hotkeysFromOptions || DEFAULT_HOTKEYS;
3819
3826
  return function(editor) {
3820
3827
  return editor.pteWithHotKeys = (event) => {
@@ -3895,16 +3902,6 @@ function createWithHotkeys(types, portableTextEditor, hotkeysFromOptions) {
3895
3902
  editor.pteEndList() && event.preventDefault();
3896
3903
  return;
3897
3904
  }
3898
- if (editor.isTextBlock(focusBlock) && focusBlock.style && focusBlock.style !== types.styles[0].value) {
3899
- const [, end] = Range.edges(editor.selection);
3900
- if (Editor.isEnd(editor, end, end.path)) {
3901
- Editor.insertNode(
3902
- editor,
3903
- editor.pteCreateTextBlock({ decorators: [] })
3904
- ), event.preventDefault(), editor.onChange();
3905
- return;
3906
- }
3907
- }
3908
3905
  }
3909
3906
  }, editor;
3910
3907
  };
@@ -4388,7 +4385,7 @@ function _insertFragment(editor, fragment, schemaTypes) {
4388
4385
  }), editor.onChange();
4389
4386
  }
4390
4387
  const originalFnMap = /* @__PURE__ */ new WeakMap(), withPlugins = (editor, options) => {
4391
- const e = editor, { portableTextEditor, patches$, readOnly, maxBlocks } = options, { editorActor, schemaTypes } = portableTextEditor;
4388
+ const e = editor, { editorActor, portableTextEditor, patches$, readOnly, maxBlocks } = options, { schemaTypes } = portableTextEditor;
4392
4389
  e.subscriptions = [], e.destroy ? e.destroy() : originalFnMap.set(e, {
4393
4390
  apply: e.apply,
4394
4391
  onChange: e.onChange,
@@ -4486,9 +4483,10 @@ const originalFnMap = /* @__PURE__ */ new WeakMap(), withPlugins = (editor, opti
4486
4483
  };
4487
4484
  }, debug$6 = debugWithName("component:PortableTextEditor:SlateContainer");
4488
4485
  function SlateContainer(props) {
4489
- const { patches$, portableTextEditor, readOnly, maxBlocks } = props, [[slateEditor, subscribe]] = useState(() => {
4486
+ const { editorActor, patches$, portableTextEditor, readOnly, maxBlocks } = props, [[slateEditor, subscribe]] = useState(() => {
4490
4487
  debug$6("Creating new Slate editor instance");
4491
4488
  const { editor, subscribe: _sub } = withPlugins(withReact(createEditor()), {
4489
+ editorActor,
4492
4490
  maxBlocks,
4493
4491
  patches$,
4494
4492
  portableTextEditor,
@@ -4503,6 +4501,7 @@ function SlateContainer(props) {
4503
4501
  };
4504
4502
  }, [subscribe]), useEffect(() => {
4505
4503
  debug$6("Re-initializing plugin chain"), withPlugins(slateEditor, {
4504
+ editorActor,
4506
4505
  maxBlocks,
4507
4506
  patches$,
4508
4507
  portableTextEditor,
@@ -4523,7 +4522,7 @@ const PortableTextEditorReadOnlyContext = createContext(!1), usePortableTextEdit
4523
4522
  return readOnly;
4524
4523
  }, debug$5 = debugWithName("hook:useSyncValue"), CURRENT_VALUE = /* @__PURE__ */ new WeakMap();
4525
4524
  function useSyncValue(props) {
4526
- const { editorActor, portableTextEditor, readOnly } = props, { schemaTypes } = portableTextEditor, previousValue = useRef(), slateEditor = useSlate(), updateValueFunctionRef = useRef(), updateFromCurrentValue = useCallback(() => {
4525
+ const { editorActor, portableTextEditor, readOnly } = props, schemaTypes = editorActor.getSnapshot().context.schema, previousValue = useRef(), slateEditor = useSlate(), updateValueFunctionRef = useRef(), updateFromCurrentValue = useCallback(() => {
4527
4526
  const currentValue = CURRENT_VALUE.get(portableTextEditor);
4528
4527
  if (previousValue.current === currentValue) {
4529
4528
  debug$5("Value is the same object as previous, not need to sync");
@@ -4830,30 +4829,40 @@ ${JSON.stringify(pendingPatches.current, null, 2)}`);
4830
4829
  debug$4("Value from props changed, syncing new value"), syncValue(value), isInitialValueFromProps.current && (editorActor.send({ type: "ready" }), isInitialValueFromProps.current = !1);
4831
4830
  }, [editorActor, syncValue, value]), null;
4832
4831
  }
4833
- function inserText({
4834
- event
4835
- }) {
4836
- Editor.insertText(event.editor, event.text);
4837
- }
4838
- function inserTextBlock({
4839
- context,
4840
- event
4841
- }) {
4842
- Editor.insertNode(event.editor, {
4843
- _key: context.keyGenerator(),
4844
- _type: context.schema.block.name,
4845
- style: context.schema.styles[0].value ?? "normal",
4846
- markDefs: [],
4847
- children: [
4848
- {
4849
- _key: context.keyGenerator(),
4850
- _type: "span",
4851
- text: ""
4852
- }
4853
- ]
4854
- });
4855
- }
4856
- const networkLogic = fromCallback(({ sendBack }) => {
4832
+ const EditorActorContext = createContext({}), behaviorActionImplementations = {
4833
+ "apply block style": ({ event }) => {
4834
+ for (const path of event.paths) {
4835
+ const at = toSlateRange(
4836
+ { anchor: { path, offset: 0 }, focus: { path, offset: 0 } },
4837
+ event.editor
4838
+ );
4839
+ Transforms.setNodes(event.editor, { style: event.style }, { at });
4840
+ }
4841
+ },
4842
+ "delete text": ({ event }) => {
4843
+ Transforms.delete(event.editor, {
4844
+ at: toSlateRange(event.selection, event.editor)
4845
+ });
4846
+ },
4847
+ "insert text": ({ event }) => {
4848
+ Editor.insertText(event.editor, event.text);
4849
+ },
4850
+ "insert text block": ({ context, event }) => {
4851
+ Editor.insertNode(event.editor, {
4852
+ _key: context.keyGenerator(),
4853
+ _type: context.schema.block.name,
4854
+ style: context.schema.styles[0].value ?? "normal",
4855
+ markDefs: [],
4856
+ children: [
4857
+ {
4858
+ _key: context.keyGenerator(),
4859
+ _type: "span",
4860
+ text: ""
4861
+ }
4862
+ ]
4863
+ });
4864
+ }
4865
+ }, networkLogic = fromCallback(({ sendBack }) => {
4857
4866
  const onlineHandler = () => {
4858
4867
  sendBack({ type: "online" });
4859
4868
  }, offlineHandler = () => {
@@ -4870,12 +4879,6 @@ const networkLogic = fromCallback(({ sendBack }) => {
4870
4879
  input: {}
4871
4880
  },
4872
4881
  actions: {
4873
- "apply:insert text": ({ context, event }) => {
4874
- assertEvent(event, "insert text"), inserText({ context, event });
4875
- },
4876
- "apply:insert text block": ({ context, event }) => {
4877
- assertEvent(event, "insert text block"), inserTextBlock({ context, event });
4878
- },
4879
4882
  "assign schema": assign({
4880
4883
  schema: ({ event }) => (assertEvent(event, "update schema"), event.schema)
4881
4884
  }),
@@ -4892,7 +4895,7 @@ const networkLogic = fromCallback(({ sendBack }) => {
4892
4895
  pendingEvents: []
4893
4896
  }),
4894
4897
  "handle behavior event": enqueueActions(({ context, event, enqueue }) => {
4895
- assertEvent(event, ["key down"]);
4898
+ assertEvent(event, ["key down", "before insert text"]);
4896
4899
  const eventBehaviors = context.behaviors.filter(
4897
4900
  (behavior) => behavior.on === event.type
4898
4901
  );
@@ -4968,11 +4971,20 @@ const networkLogic = fromCallback(({ sendBack }) => {
4968
4971
  "key down": {
4969
4972
  actions: ["handle behavior event"]
4970
4973
  },
4974
+ "before insert text": {
4975
+ actions: ["handle behavior event"]
4976
+ },
4977
+ "apply block style": {
4978
+ actions: [behaviorActionImplementations["apply block style"]]
4979
+ },
4980
+ "delete text": {
4981
+ actions: [behaviorActionImplementations["delete text"]]
4982
+ },
4971
4983
  "insert text": {
4972
- actions: ["apply:insert text"]
4984
+ actions: [behaviorActionImplementations["insert text"]]
4973
4985
  },
4974
4986
  "insert text block": {
4975
- actions: ["apply:insert text block"]
4987
+ actions: [behaviorActionImplementations["insert text block"]]
4976
4988
  }
4977
4989
  },
4978
4990
  initial: "pristine",
@@ -5046,11 +5058,6 @@ function PortableTextEditorSelectionProvider(props) {
5046
5058
  }
5047
5059
  const defaultKeyGenerator = () => randomKey(12), debug$2 = debugWithName("component:PortableTextEditor");
5048
5060
  class PortableTextEditor extends Component {
5049
- /**
5050
- * @internal
5051
- * Don't use this API directly. It's subject to change.
5052
- */
5053
- editorActor;
5054
5061
  /**
5055
5062
  * An observable of all the editor changes.
5056
5063
  */
@@ -5063,6 +5070,7 @@ class PortableTextEditor extends Component {
5063
5070
  * The editor API (currently implemented with Slate).
5064
5071
  */
5065
5072
  editable;
5073
+ editorActor;
5066
5074
  constructor(props) {
5067
5075
  if (super(props), !props.schemaType)
5068
5076
  throw new Error('PortableTextEditor: missing "schemaType" property');
@@ -5095,29 +5103,36 @@ class PortableTextEditor extends Component {
5095
5103
  };
5096
5104
  render() {
5097
5105
  const { value, children, patches$, incomingPatches$ } = this.props, _patches$ = incomingPatches$ || patches$, maxBlocks = typeof this.props.maxBlocks > "u" ? void 0 : Number.parseInt(this.props.maxBlocks.toString(), 10) || void 0, readOnly = !!this.props.readOnly;
5098
- return /* @__PURE__ */ jsx(
5106
+ return /* @__PURE__ */ jsx(EditorActorContext.Provider, { value: this.editorActor, children: /* @__PURE__ */ jsx(
5099
5107
  SlateContainer,
5100
5108
  {
5109
+ editorActor: this.editorActor,
5101
5110
  maxBlocks,
5102
5111
  patches$: _patches$,
5103
5112
  portableTextEditor: this,
5104
5113
  readOnly,
5105
- children: /* @__PURE__ */ jsx(PortableTextEditorContext.Provider, { value: this, children: /* @__PURE__ */ jsx(PortableTextEditorReadOnlyContext.Provider, { value: readOnly, children: /* @__PURE__ */ jsxs(PortableTextEditorSelectionProvider, { editorActor: this.editorActor, children: [
5106
- /* @__PURE__ */ jsx(
5107
- Synchronizer,
5108
- {
5109
- editorActor: this.editorActor,
5110
- getValue: this.getValue,
5111
- onChange: (change) => {
5112
- this.props.onChange(change), this.change$.next(change);
5113
- },
5114
- value
5115
- }
5116
- ),
5117
- children
5118
- ] }) }) })
5114
+ children: /* @__PURE__ */ jsx(PortableTextEditorContext.Provider, { value: this, children: /* @__PURE__ */ jsx(PortableTextEditorReadOnlyContext.Provider, { value: readOnly, children: /* @__PURE__ */ jsxs(
5115
+ PortableTextEditorSelectionProvider,
5116
+ {
5117
+ editorActor: this.editorActor,
5118
+ children: [
5119
+ /* @__PURE__ */ jsx(
5120
+ Synchronizer,
5121
+ {
5122
+ editorActor: this.editorActor,
5123
+ getValue: this.getValue,
5124
+ onChange: (change) => {
5125
+ this.props.onChange(change), this.change$.next(change);
5126
+ },
5127
+ value
5128
+ }
5129
+ ),
5130
+ children
5131
+ ]
5132
+ }
5133
+ ) }) })
5119
5134
  }
5120
- );
5135
+ ) });
5121
5136
  }
5122
5137
  // Static API methods
5123
5138
  static activeAnnotations = (editor) => editor && editor.editable ? editor.editable.activeAnnotations() : [];
@@ -5171,6 +5186,7 @@ class PortableTextEditor extends Component {
5171
5186
  }
5172
5187
  const debug$1 = debugWithName("components:Leaf"), EMPTY_MARKS = [], Leaf = (props) => {
5173
5188
  const {
5189
+ editorActor,
5174
5190
  attributes,
5175
5191
  children,
5176
5192
  leaf,
@@ -5225,17 +5241,14 @@ const debug$1 = debugWithName("components:Leaf"), EMPTY_MARKS = [], Leaf = (prop
5225
5241
  useEffect(() => {
5226
5242
  if (!shouldTrackSelectionAndFocus)
5227
5243
  return;
5228
- const onBlur = portableTextEditor.editorActor.on("blur", () => {
5244
+ const onBlur = editorActor.on("blur", () => {
5229
5245
  setFocused(!1), setSelected(!1);
5230
- }), onFocus = portableTextEditor.editorActor.on("focus", () => {
5246
+ }), onFocus = editorActor.on("focus", () => {
5231
5247
  const sel = PortableTextEditor.getSelection(portableTextEditor);
5232
5248
  sel && isEqual(sel.focus.path, path) && PortableTextEditor.isCollapsedSelection(portableTextEditor) && setFocused(!0), setSelectedFromRange();
5233
- }), onSelection = portableTextEditor.editorActor.on(
5234
- "selection",
5235
- (event) => {
5236
- event.selection && isEqual(event.selection.focus.path, path) && PortableTextEditor.isCollapsedSelection(portableTextEditor) ? setFocused(!0) : setFocused(!1), setSelectedFromRange();
5237
- }
5238
- );
5249
+ }), onSelection = editorActor.on("selection", (event) => {
5250
+ event.selection && isEqual(event.selection.focus.path, path) && PortableTextEditor.isCollapsedSelection(portableTextEditor) ? setFocused(!0) : setFocused(!1), setSelectedFromRange();
5251
+ });
5239
5252
  return () => {
5240
5253
  onBlur.unsubscribe(), onFocus.unsubscribe(), onSelection.unsubscribe();
5241
5254
  };
@@ -5388,12 +5401,12 @@ const debug$1 = debugWithName("components:Leaf"), EMPTY_MARKS = [], Leaf = (prop
5388
5401
  forwardedRef,
5389
5402
  () => ref.current
5390
5403
  );
5391
- const rangeDecorationsRef = useRef(rangeDecorations), { editorActor, schemaTypes } = portableTextEditor, slateEditor = useSlate(), blockTypeName = schemaTypes.block.name, withInsertData = useMemo(
5404
+ const rangeDecorationsRef = useRef(rangeDecorations), editorActor = useContext(EditorActorContext), { schemaTypes } = portableTextEditor, slateEditor = useSlate(), blockTypeName = schemaTypes.block.name, withInsertData = useMemo(
5392
5405
  () => createWithInsertData(editorActor, schemaTypes),
5393
5406
  [editorActor, schemaTypes]
5394
5407
  ), withHotKeys = useMemo(
5395
- () => createWithHotkeys(schemaTypes, portableTextEditor, hotkeys),
5396
- [hotkeys, portableTextEditor, schemaTypes]
5408
+ () => createWithHotkeys(portableTextEditor, hotkeys),
5409
+ [hotkeys, portableTextEditor]
5397
5410
  );
5398
5411
  useMemo(() => readOnly ? (debug("Editable is in read only mode"), withInsertData(slateEditor)) : (debug("Editable is in edit mode"), withInsertData(withHotKeys(slateEditor))), [readOnly, slateEditor, withHotKeys, withInsertData]);
5399
5412
  const renderElement = useCallback(
@@ -5426,6 +5439,7 @@ const debug$1 = debugWithName("components:Leaf"), EMPTY_MARKS = [], Leaf = (prop
5426
5439
  Leaf,
5427
5440
  {
5428
5441
  ...lProps,
5442
+ editorActor,
5429
5443
  schemaTypes,
5430
5444
  renderAnnotation,
5431
5445
  renderChild,
@@ -5602,7 +5616,11 @@ const debug$1 = debugWithName("components:Leaf"), EMPTY_MARKS = [], Leaf = (prop
5602
5616
  [editorActor, onBlur]
5603
5617
  ), handleOnBeforeInput = useCallback(
5604
5618
  (event) => {
5605
- onBeforeInput && onBeforeInput(event);
5619
+ onBeforeInput && onBeforeInput(event), !event.defaultPrevented && event.inputType === "insertText" && editorActor.send({
5620
+ type: "before insert text",
5621
+ nativeEvent: event,
5622
+ editor: slateEditor
5623
+ });
5606
5624
  },
5607
5625
  [onBeforeInput]
5608
5626
  ), validateSelection = useCallback(() => {