@portabletext/editor 1.1.6 → 1.1.8

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.mjs 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;
@@ -3814,7 +3810,7 @@ const debug$8 = debugWithName("plugin:withHotKeys"), DEFAULT_HOTKEYS = {
3814
3810
  },
3815
3811
  custom: {}
3816
3812
  };
3817
- function createWithHotkeys(types, portableTextEditor, hotkeysFromOptions) {
3813
+ function createWithHotkeys(portableTextEditor, hotkeysFromOptions) {
3818
3814
  const reservedHotkeys = ["enter", "tab", "shift", "delete", "end"], activeHotkeys = hotkeysFromOptions || DEFAULT_HOTKEYS;
3819
3815
  return function(editor) {
3820
3816
  return editor.pteWithHotKeys = (event) => {
@@ -3895,16 +3891,6 @@ function createWithHotkeys(types, portableTextEditor, hotkeysFromOptions) {
3895
3891
  editor.pteEndList() && event.preventDefault();
3896
3892
  return;
3897
3893
  }
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
3894
  }
3909
3895
  }, editor;
3910
3896
  };
@@ -4388,7 +4374,7 @@ function _insertFragment(editor, fragment, schemaTypes) {
4388
4374
  }), editor.onChange();
4389
4375
  }
4390
4376
  const originalFnMap = /* @__PURE__ */ new WeakMap(), withPlugins = (editor, options) => {
4391
- const e = editor, { portableTextEditor, patches$, readOnly, maxBlocks } = options, { editorActor, schemaTypes } = portableTextEditor;
4377
+ const e = editor, { editorActor, portableTextEditor, patches$, readOnly, maxBlocks } = options, { schemaTypes } = portableTextEditor;
4392
4378
  e.subscriptions = [], e.destroy ? e.destroy() : originalFnMap.set(e, {
4393
4379
  apply: e.apply,
4394
4380
  onChange: e.onChange,
@@ -4486,9 +4472,10 @@ const originalFnMap = /* @__PURE__ */ new WeakMap(), withPlugins = (editor, opti
4486
4472
  };
4487
4473
  }, debug$6 = debugWithName("component:PortableTextEditor:SlateContainer");
4488
4474
  function SlateContainer(props) {
4489
- const { patches$, portableTextEditor, readOnly, maxBlocks } = props, [[slateEditor, subscribe]] = useState(() => {
4475
+ const { editorActor, patches$, portableTextEditor, readOnly, maxBlocks } = props, [[slateEditor, subscribe]] = useState(() => {
4490
4476
  debug$6("Creating new Slate editor instance");
4491
4477
  const { editor, subscribe: _sub } = withPlugins(withReact(createEditor()), {
4478
+ editorActor,
4492
4479
  maxBlocks,
4493
4480
  patches$,
4494
4481
  portableTextEditor,
@@ -4503,6 +4490,7 @@ function SlateContainer(props) {
4503
4490
  };
4504
4491
  }, [subscribe]), useEffect(() => {
4505
4492
  debug$6("Re-initializing plugin chain"), withPlugins(slateEditor, {
4493
+ editorActor,
4506
4494
  maxBlocks,
4507
4495
  patches$,
4508
4496
  portableTextEditor,
@@ -4523,7 +4511,7 @@ const PortableTextEditorReadOnlyContext = createContext(!1), usePortableTextEdit
4523
4511
  return readOnly;
4524
4512
  }, debug$5 = debugWithName("hook:useSyncValue"), CURRENT_VALUE = /* @__PURE__ */ new WeakMap();
4525
4513
  function useSyncValue(props) {
4526
- const { editorActor, portableTextEditor, readOnly } = props, { schemaTypes } = portableTextEditor, previousValue = useRef(), slateEditor = useSlate(), updateValueFunctionRef = useRef(), updateFromCurrentValue = useCallback(() => {
4514
+ const { editorActor, portableTextEditor, readOnly } = props, schemaTypes = editorActor.getSnapshot().context.schema, previousValue = useRef(), slateEditor = useSlate(), updateValueFunctionRef = useRef(), updateFromCurrentValue = useCallback(() => {
4527
4515
  const currentValue = CURRENT_VALUE.get(portableTextEditor);
4528
4516
  if (previousValue.current === currentValue) {
4529
4517
  debug$5("Value is the same object as previous, not need to sync");
@@ -4830,30 +4818,40 @@ ${JSON.stringify(pendingPatches.current, null, 2)}`);
4830
4818
  debug$4("Value from props changed, syncing new value"), syncValue(value), isInitialValueFromProps.current && (editorActor.send({ type: "ready" }), isInitialValueFromProps.current = !1);
4831
4819
  }, [editorActor, syncValue, value]), null;
4832
4820
  }
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 }) => {
4821
+ const EditorActorContext = createContext({}), behaviorActionImplementations = {
4822
+ "apply block style": ({ event }) => {
4823
+ for (const path of event.paths) {
4824
+ const at = toSlateRange(
4825
+ { anchor: { path, offset: 0 }, focus: { path, offset: 0 } },
4826
+ event.editor
4827
+ );
4828
+ Transforms.setNodes(event.editor, { style: event.style }, { at });
4829
+ }
4830
+ },
4831
+ "delete text": ({ event }) => {
4832
+ Transforms.delete(event.editor, {
4833
+ at: toSlateRange(event.selection, event.editor)
4834
+ });
4835
+ },
4836
+ "insert text": ({ event }) => {
4837
+ Editor.insertText(event.editor, event.text);
4838
+ },
4839
+ "insert text block": ({ context, event }) => {
4840
+ Editor.insertNode(event.editor, {
4841
+ _key: context.keyGenerator(),
4842
+ _type: context.schema.block.name,
4843
+ style: context.schema.styles[0].value ?? "normal",
4844
+ markDefs: [],
4845
+ children: [
4846
+ {
4847
+ _key: context.keyGenerator(),
4848
+ _type: "span",
4849
+ text: ""
4850
+ }
4851
+ ]
4852
+ });
4853
+ }
4854
+ }, networkLogic = fromCallback(({ sendBack }) => {
4857
4855
  const onlineHandler = () => {
4858
4856
  sendBack({ type: "online" });
4859
4857
  }, offlineHandler = () => {
@@ -4870,12 +4868,6 @@ const networkLogic = fromCallback(({ sendBack }) => {
4870
4868
  input: {}
4871
4869
  },
4872
4870
  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
4871
  "assign schema": assign({
4880
4872
  schema: ({ event }) => (assertEvent(event, "update schema"), event.schema)
4881
4873
  }),
@@ -4892,7 +4884,7 @@ const networkLogic = fromCallback(({ sendBack }) => {
4892
4884
  pendingEvents: []
4893
4885
  }),
4894
4886
  "handle behavior event": enqueueActions(({ context, event, enqueue }) => {
4895
- assertEvent(event, ["key down"]);
4887
+ assertEvent(event, ["key down", "before insert text"]);
4896
4888
  const eventBehaviors = context.behaviors.filter(
4897
4889
  (behavior) => behavior.on === event.type
4898
4890
  );
@@ -4968,11 +4960,20 @@ const networkLogic = fromCallback(({ sendBack }) => {
4968
4960
  "key down": {
4969
4961
  actions: ["handle behavior event"]
4970
4962
  },
4963
+ "before insert text": {
4964
+ actions: ["handle behavior event"]
4965
+ },
4966
+ "apply block style": {
4967
+ actions: [behaviorActionImplementations["apply block style"]]
4968
+ },
4969
+ "delete text": {
4970
+ actions: [behaviorActionImplementations["delete text"]]
4971
+ },
4971
4972
  "insert text": {
4972
- actions: ["apply:insert text"]
4973
+ actions: [behaviorActionImplementations["insert text"]]
4973
4974
  },
4974
4975
  "insert text block": {
4975
- actions: ["apply:insert text block"]
4976
+ actions: [behaviorActionImplementations["insert text block"]]
4976
4977
  }
4977
4978
  },
4978
4979
  initial: "pristine",
@@ -5046,11 +5047,6 @@ function PortableTextEditorSelectionProvider(props) {
5046
5047
  }
5047
5048
  const defaultKeyGenerator = () => randomKey(12), debug$2 = debugWithName("component:PortableTextEditor");
5048
5049
  class PortableTextEditor extends Component {
5049
- /**
5050
- * @internal
5051
- * Don't use this API directly. It's subject to change.
5052
- */
5053
- editorActor;
5054
5050
  /**
5055
5051
  * An observable of all the editor changes.
5056
5052
  */
@@ -5063,6 +5059,7 @@ class PortableTextEditor extends Component {
5063
5059
  * The editor API (currently implemented with Slate).
5064
5060
  */
5065
5061
  editable;
5062
+ editorActor;
5066
5063
  constructor(props) {
5067
5064
  if (super(props), !props.schemaType)
5068
5065
  throw new Error('PortableTextEditor: missing "schemaType" property');
@@ -5095,29 +5092,36 @@ class PortableTextEditor extends Component {
5095
5092
  };
5096
5093
  render() {
5097
5094
  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(
5095
+ return /* @__PURE__ */ jsx(EditorActorContext.Provider, { value: this.editorActor, children: /* @__PURE__ */ jsx(
5099
5096
  SlateContainer,
5100
5097
  {
5098
+ editorActor: this.editorActor,
5101
5099
  maxBlocks,
5102
5100
  patches$: _patches$,
5103
5101
  portableTextEditor: this,
5104
5102
  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
- ] }) }) })
5103
+ children: /* @__PURE__ */ jsx(PortableTextEditorContext.Provider, { value: this, children: /* @__PURE__ */ jsx(PortableTextEditorReadOnlyContext.Provider, { value: readOnly, children: /* @__PURE__ */ jsxs(
5104
+ PortableTextEditorSelectionProvider,
5105
+ {
5106
+ editorActor: this.editorActor,
5107
+ children: [
5108
+ /* @__PURE__ */ jsx(
5109
+ Synchronizer,
5110
+ {
5111
+ editorActor: this.editorActor,
5112
+ getValue: this.getValue,
5113
+ onChange: (change) => {
5114
+ this.props.onChange(change), this.change$.next(change);
5115
+ },
5116
+ value
5117
+ }
5118
+ ),
5119
+ children
5120
+ ]
5121
+ }
5122
+ ) }) })
5119
5123
  }
5120
- );
5124
+ ) });
5121
5125
  }
5122
5126
  // Static API methods
5123
5127
  static activeAnnotations = (editor) => editor && editor.editable ? editor.editable.activeAnnotations() : [];
@@ -5171,6 +5175,7 @@ class PortableTextEditor extends Component {
5171
5175
  }
5172
5176
  const debug$1 = debugWithName("components:Leaf"), EMPTY_MARKS = [], Leaf = (props) => {
5173
5177
  const {
5178
+ editorActor,
5174
5179
  attributes,
5175
5180
  children,
5176
5181
  leaf,
@@ -5225,17 +5230,14 @@ const debug$1 = debugWithName("components:Leaf"), EMPTY_MARKS = [], Leaf = (prop
5225
5230
  useEffect(() => {
5226
5231
  if (!shouldTrackSelectionAndFocus)
5227
5232
  return;
5228
- const onBlur = portableTextEditor.editorActor.on("blur", () => {
5233
+ const onBlur = editorActor.on("blur", () => {
5229
5234
  setFocused(!1), setSelected(!1);
5230
- }), onFocus = portableTextEditor.editorActor.on("focus", () => {
5235
+ }), onFocus = editorActor.on("focus", () => {
5231
5236
  const sel = PortableTextEditor.getSelection(portableTextEditor);
5232
5237
  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
- );
5238
+ }), onSelection = editorActor.on("selection", (event) => {
5239
+ event.selection && isEqual(event.selection.focus.path, path) && PortableTextEditor.isCollapsedSelection(portableTextEditor) ? setFocused(!0) : setFocused(!1), setSelectedFromRange();
5240
+ });
5239
5241
  return () => {
5240
5242
  onBlur.unsubscribe(), onFocus.unsubscribe(), onSelection.unsubscribe();
5241
5243
  };
@@ -5388,12 +5390,12 @@ const debug$1 = debugWithName("components:Leaf"), EMPTY_MARKS = [], Leaf = (prop
5388
5390
  forwardedRef,
5389
5391
  () => ref.current
5390
5392
  );
5391
- const rangeDecorationsRef = useRef(rangeDecorations), { editorActor, schemaTypes } = portableTextEditor, slateEditor = useSlate(), blockTypeName = schemaTypes.block.name, withInsertData = useMemo(
5393
+ const rangeDecorationsRef = useRef(rangeDecorations), editorActor = useContext(EditorActorContext), { schemaTypes } = portableTextEditor, slateEditor = useSlate(), blockTypeName = schemaTypes.block.name, withInsertData = useMemo(
5392
5394
  () => createWithInsertData(editorActor, schemaTypes),
5393
5395
  [editorActor, schemaTypes]
5394
5396
  ), withHotKeys = useMemo(
5395
- () => createWithHotkeys(schemaTypes, portableTextEditor, hotkeys),
5396
- [hotkeys, portableTextEditor, schemaTypes]
5397
+ () => createWithHotkeys(portableTextEditor, hotkeys),
5398
+ [hotkeys, portableTextEditor]
5397
5399
  );
5398
5400
  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
5401
  const renderElement = useCallback(
@@ -5426,6 +5428,7 @@ const debug$1 = debugWithName("components:Leaf"), EMPTY_MARKS = [], Leaf = (prop
5426
5428
  Leaf,
5427
5429
  {
5428
5430
  ...lProps,
5431
+ editorActor,
5429
5432
  schemaTypes,
5430
5433
  renderAnnotation,
5431
5434
  renderChild,
@@ -5602,7 +5605,11 @@ const debug$1 = debugWithName("components:Leaf"), EMPTY_MARKS = [], Leaf = (prop
5602
5605
  [editorActor, onBlur]
5603
5606
  ), handleOnBeforeInput = useCallback(
5604
5607
  (event) => {
5605
- onBeforeInput && onBeforeInput(event);
5608
+ onBeforeInput && onBeforeInput(event), !event.defaultPrevented && event.inputType === "insertText" && editorActor.send({
5609
+ type: "before insert text",
5610
+ nativeEvent: event,
5611
+ editor: slateEditor
5612
+ });
5606
5613
  },
5607
5614
  [onBeforeInput]
5608
5615
  ), validateSelection = useCallback(() => {