@lofcz/platejs-core 53.1.6 → 53.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.
@@ -1,2 +1,2 @@
1
- import { $a as SlateHTMLProps, Ba as getSelectedDomNode, Co as SlateRenderLeaf, Do as SlateRenderElement, Eo as pipeRenderElementStatic, Ga as getPluginDataAttributes, Ha as getSelectedDomBlocks, Ia as stripSlateDataAttributes, Ja as ViewPlugin, Ka as createStaticString, La as stripHtmlClassNames, Oo as pluginRenderElementStatic, Qa as SlateElementProps, Ra as pipeDecorate, So as pluginRenderTextStatic, To as pluginRenderLeafStatic, Ua as getRenderNodeStaticProps, Va as getSelectedDomFragment, Wa as getNodeDataAttributes, Xa as getEditorDOMFromHtmlString, Ya as createStaticEditor, Za as SlateElement, _o as LeafStatic, ao as StyledSlateElementProps, bo as SlateRenderText, co as useNodeAttributes, do as SlateRenderLeafProps, eo as SlateLeaf, fo as SlateRenderNodeProps, go as ElementStatic, ho as serializeHtml, io as SlateTextProps, lo as BoxStaticProps, mo as SerializeHtmlOptions, no as SlateNodeProps, oo as StyledSlateLeafProps, po as SlateRenderTextProps, qa as getStaticPlugins, ro as SlateText, so as StyledSlateTextProps, to as SlateLeafProps, uo as SlateRenderElementProps, vo as PlateStatic, wo as pipeRenderLeafStatic, xo as pipeRenderTextStatic, yo as PlateStaticProps, za as isSelectOutside } from "../index-BooMghRU";
1
+ import { $a as SlateElement, Ao as pluginRenderElementStatic, Ba as pipeDecorate, Co as pipeRenderTextStatic, Do as pluginRenderLeafStatic, Eo as pipeRenderLeafStatic, Ga as getRenderNodeStaticProps, Ha as getSelectedDomNode, Ja as createStaticString, Ka as getNodeDataAttributes, Oo as pipeRenderElementStatic, Qa as getEditorDOMFromHtmlString, Ra as stripSlateDataAttributes, So as SlateRenderText, To as SlateRenderLeaf, Ua as getSelectedDomFragment, Va as isSelectOutside, Wa as getSelectedDomBlocks, Xa as ViewPlugin, Ya as getStaticPlugins, Za as createStaticEditor, _o as serializeHtml, ao as SlateText, bo as PlateStatic, co as StyledSlateLeafProps, do as BoxStaticProps, eo as SlateElementProps, fo as SlateRenderElementProps, go as SerializeHtmlOptions, ho as SlateRenderTextProps, io as SlateNodeProps, ko as SlateRenderElement, lo as StyledSlateTextProps, mo as SlateRenderNodeProps, no as SlateLeaf, oo as SlateTextProps, po as SlateRenderLeafProps, qa as getPluginDataAttributes, ro as SlateLeafProps, so as StyledSlateElementProps, to as SlateHTMLProps, uo as useNodeAttributes, vo as ElementStatic, wo as pluginRenderTextStatic, xo as PlateStaticProps, yo as LeafStatic, za as stripHtmlClassNames } from "../index-BDSzA1zP";
2
2
  export { BoxStaticProps, ElementStatic, LeafStatic, PlateStatic, PlateStaticProps, SerializeHtmlOptions, SlateElement, SlateElementProps, SlateHTMLProps, SlateLeaf, SlateLeafProps, SlateNodeProps, SlateRenderElement, SlateRenderElementProps, SlateRenderLeaf, SlateRenderLeafProps, SlateRenderNodeProps, SlateRenderText, SlateRenderTextProps, SlateText, SlateTextProps, StyledSlateElementProps, StyledSlateLeafProps, StyledSlateTextProps, ViewPlugin, createStaticEditor, createStaticString, getEditorDOMFromHtmlString, getNodeDataAttributes, getPluginDataAttributes, getRenderNodeStaticProps, getSelectedDomBlocks, getSelectedDomFragment, getSelectedDomNode, getStaticPlugins, isSelectOutside, pipeDecorate, pipeRenderElementStatic, pipeRenderLeafStatic, pipeRenderTextStatic, pluginRenderElementStatic, pluginRenderLeafStatic, pluginRenderTextStatic, serializeHtml, stripHtmlClassNames, stripSlateDataAttributes, useNodeAttributes };
@@ -1,4 +1,4 @@
1
- import "../withSlate-Ck8dLhUt.js";
2
- import { C as createStaticEditor, D as getSelectedDomNode, E as isSelectOutside, O as getSelectedDomFragment, S as useNodeAttributes, T as ViewPlugin, _ as getPluginDataAttributes, a as ElementStatic, b as SlateLeaf, c as pipeRenderTextStatic, d as pluginRenderElementStatic, f as stripSlateDataAttributes, g as getNodeDataAttributes, h as getRenderNodeStaticProps, i as pluginRenderLeafStatic, k as pipeDecorate, l as pluginRenderTextStatic, m as getSelectedDomBlocks, n as serializeHtml, o as LeafStatic, p as stripHtmlClassNames, r as pipeRenderLeafStatic, s as PlateStatic, t as getEditorDOMFromHtmlString, u as pipeRenderElementStatic, v as createStaticString, w as getStaticPlugins, x as SlateText, y as SlateElement } from "../static-DAs0P1Ms.js";
1
+ import "../withSlate-NVP0S9vL.js";
2
+ import { C as createStaticEditor, D as getSelectedDomNode, E as isSelectOutside, O as getSelectedDomFragment, S as useNodeAttributes, T as ViewPlugin, _ as getPluginDataAttributes, a as ElementStatic, b as SlateLeaf, c as pipeRenderTextStatic, d as pluginRenderElementStatic, f as stripSlateDataAttributes, g as getNodeDataAttributes, h as getRenderNodeStaticProps, i as pluginRenderLeafStatic, k as pipeDecorate, l as pluginRenderTextStatic, m as getSelectedDomBlocks, n as serializeHtml, o as LeafStatic, p as stripHtmlClassNames, r as pipeRenderLeafStatic, s as PlateStatic, t as getEditorDOMFromHtmlString, u as pipeRenderElementStatic, v as createStaticString, w as getStaticPlugins, x as SlateText, y as SlateElement } from "../static-KMalmfNw.js";
3
3
 
4
4
  export { ElementStatic, LeafStatic, PlateStatic, SlateElement, SlateLeaf, SlateText, ViewPlugin, createStaticEditor, createStaticString, getEditorDOMFromHtmlString, getNodeDataAttributes, getPluginDataAttributes, getRenderNodeStaticProps, getSelectedDomBlocks, getSelectedDomFragment, getSelectedDomNode, getStaticPlugins, isSelectOutside, pipeDecorate, pipeRenderElementStatic, pipeRenderLeafStatic, pipeRenderTextStatic, pluginRenderElementStatic, pluginRenderLeafStatic, pluginRenderTextStatic, serializeHtml, stripHtmlClassNames, stripSlateDataAttributes, useNodeAttributes };
@@ -1,4 +1,4 @@
1
- import { Ct as getInjectMatch, at as DOMPlugin, n as withSlate, p as isEditOnly, sn as getEditorPlugin, tn as getPluginByType, vt as getSlateClass, xt as keyToDataAttribute, yt as getPluginNodeProps } from "./withSlate-Ck8dLhUt.js";
1
+ import { St as keyToDataAttribute, bt as getPluginNodeProps, n as withSlate, ot as DOMPlugin, p as isEditOnly, sn as getEditorPlugin, tn as getPluginByType, wt as getInjectMatch, yt as getSlateClass } from "./withSlate-NVP0S9vL.js";
2
2
  import { ElementApi, NodeApi, RangeApi, TextApi, createEditor, isElementDecorationsEqual, isTextDecorationsEqual } from "@platejs/slate";
3
3
  import { isDefined } from "@udecode/utils";
4
4
  import React from "react";
@@ -14,13 +14,13 @@ import { decode } from "html-entities";
14
14
  * `[styleKey]: value`.
15
15
  */
16
16
  const pluginInjectNodeProps = (editor, plugin, nodeProps, getElementPath) => {
17
- const { key, inject: { nodeProps: injectNodeProps } } = plugin;
17
+ const { key, inject: { excludeBelowPlugins, maxLevel, nodeProps: injectNodeProps } } = plugin;
18
18
  const { element, text } = nodeProps;
19
19
  const node = element ?? text;
20
20
  if (!node) return;
21
21
  if (!injectNodeProps) return;
22
22
  const { classNames, defaultNodeValue, nodeKey = editor.getType(key), query, styleKey = nodeKey, transformClassName, transformNodeValue, transformProps, transformStyle, validNodeValues } = injectNodeProps;
23
- if (!getInjectMatch(editor, plugin)(node, !!(plugin.inject.excludeBelowPlugins || plugin.inject.maxLevel) ? getElementPath(node) : void 0)) return;
23
+ if (!getInjectMatch(editor, plugin)(node, !!(excludeBelowPlugins || maxLevel) ? getElementPath(node) : void 0)) return;
24
24
  const queryResult = query?.({
25
25
  ...injectNodeProps,
26
26
  ...getEditorPlugin(editor, plugin),
@@ -4,7 +4,6 @@ import { bindFirst, isDefined } from "@udecode/utils";
4
4
  import merge from "lodash/merge.js";
5
5
  import { createVanillaStore } from "zustand-x/vanilla";
6
6
  import mergeWith from "lodash/mergeWith.js";
7
- import defaults from "lodash/defaults.js";
8
7
  import kebabCase from "lodash/kebabCase.js";
9
8
  import pick from "lodash/pick.js";
10
9
  import castArray from "lodash/castArray.js";
@@ -788,7 +787,7 @@ const resolvePlugins = (editor, plugins = [], createStore = createVanillaStore)
788
787
  textProps: [],
789
788
  types: {}
790
789
  },
791
- normalizeInitialValue: [],
790
+ transformInitialValue: [],
792
791
  render: {
793
792
  aboveEditable: [],
794
793
  aboveNodes: [],
@@ -829,7 +828,7 @@ const resolvePlugins = (editor, plugins = [], createStore = createVanillaStore)
829
828
  if (plugin.render.afterContainer) editor.meta.pluginCache.render.afterContainer.push(plugin.key);
830
829
  if (plugin.render.beforeContainer) editor.meta.pluginCache.render.beforeContainer.push(plugin.key);
831
830
  if (plugin.render.belowRootNodes) editor.meta.pluginCache.render.belowRootNodes.push(plugin.key);
832
- if (plugin.normalizeInitialValue) editor.meta.pluginCache.normalizeInitialValue.push(plugin.key);
831
+ if (plugin.transformInitialValue || plugin.normalizeInitialValue) editor.meta.pluginCache.transformInitialValue.push(plugin.key);
833
832
  if (plugin.decorate) editor.meta.pluginCache.decorate.push(plugin.key);
834
833
  if (plugin.render.aboveNodes) editor.meta.pluginCache.render.aboveNodes.push(plugin.key);
835
834
  if (plugin.render.belowNodes) editor.meta.pluginCache.render.belowNodes.push(plugin.key);
@@ -1456,16 +1455,6 @@ const isSlatePluginElement = (element, pluginKey) => element.dataset.slateNode =
1456
1455
  const isSlatePluginNode = (element, pluginKey) => element.classList.contains(`slate-${pluginKey}`);
1457
1456
  const getSlateElements = (element) => Array.from(element.querySelectorAll("[data-slate-node=\"element\"]"));
1458
1457
 
1459
- //#endregion
1460
- //#region src/lib/utils/defaultsDeepToNodes.ts
1461
- /** Recursively merge a source object to children nodes with a query. */
1462
- const defaultsDeepToNodes = (options) => {
1463
- applyDeepToNodes({
1464
- ...options,
1465
- apply: defaults
1466
- });
1467
- };
1468
-
1469
1458
  //#endregion
1470
1459
  //#region src/lib/utils/getInjectMatch.ts
1471
1460
  const getInjectMatch = (editor, plugin) => {
@@ -2732,6 +2721,7 @@ const NavigationFeedbackPlugin = createTSlatePlugin({
2732
2721
 
2733
2722
  //#endregion
2734
2723
  //#region src/lib/plugins/node-id/withNodeId.ts
2724
+ const now = () => globalThis.performance?.now() ?? Date.now();
2735
2725
  /** Enables support for inserting nodes with an id key. */
2736
2726
  const withNodeId = ({ editor, getOptions, tf: { apply, insertNode, insertNodes } }) => {
2737
2727
  const idPropsCreator = () => ({ [getOptions().idKey ?? ""]: getOptions().idCreator() });
@@ -2739,23 +2729,42 @@ const withNodeId = ({ editor, getOptions, tf: { apply, insertNode, insertNodes }
2739
2729
  const { filter, filterText } = getOptions();
2740
2730
  return filter(nodeEntry) && (!filterText || nodeEntry[0]?.type !== void 0);
2741
2731
  };
2742
- const removeIdFromNodeIfDuplicate = (node) => {
2743
- const { idKey = "", reuseId } = getOptions();
2744
- if (!reuseId && editor.api.some({
2745
- at: [],
2746
- match: { [idKey]: node[idKey] }
2747
- })) delete node[idKey];
2732
+ const collectDuplicateCandidateIds = ({ disableInsertOverrides, idKey, nodeEntry, query }) => {
2733
+ const duplicateCandidateIds = /* @__PURE__ */ new Set();
2734
+ const collectNodeIds = (entry) => {
2735
+ const [entryNode, path] = entry;
2736
+ if (queryNode(entry, query)) {
2737
+ if (entryNode[idKey] !== void 0) duplicateCandidateIds.add(entryNode[idKey]);
2738
+ if (!disableInsertOverrides && isDefined(entryNode._id)) duplicateCandidateIds.add(entryNode._id);
2739
+ }
2740
+ const children = entryNode.children;
2741
+ if (!children) return;
2742
+ children.forEach((child, index) => {
2743
+ collectNodeIds([child, [...path, index]]);
2744
+ });
2745
+ };
2746
+ collectNodeIds(nodeEntry);
2747
+ return duplicateCandidateIds;
2748
2748
  };
2749
- const overrideIdIfSet = (node) => {
2750
- const { idKey = "" } = getOptions();
2751
- if (isDefined(node._id)) {
2752
- const id = node._id;
2753
- node._id = void 0;
2754
- if (!editor.api.some({
2755
- at: [],
2756
- match: { [idKey]: id }
2757
- })) node[idKey] = id;
2749
+ const collectExistingEditorIds = ({ duplicateCandidateIds, idKey }) => {
2750
+ if (duplicateCandidateIds.size === 0) return /* @__PURE__ */ new Set();
2751
+ const existingIds = /* @__PURE__ */ new Set();
2752
+ const start = now();
2753
+ let visitedCount = 0;
2754
+ for (const [entryNode] of NodeApi.nodes(editor)) {
2755
+ visitedCount += 1;
2756
+ const id = entryNode?.[idKey];
2757
+ if (id === void 0 || !duplicateCandidateIds.has(id)) continue;
2758
+ existingIds.add(id);
2759
+ if (existingIds.size === duplicateCandidateIds.size) break;
2758
2760
  }
2761
+ getOptions().onDuplicateIdScan?.({
2762
+ candidateCount: duplicateCandidateIds.size,
2763
+ duration: now() - start,
2764
+ existingCount: existingIds.size,
2765
+ visitedCount
2766
+ });
2767
+ return existingIds;
2759
2768
  };
2760
2769
  return { transforms: {
2761
2770
  apply(operation) {
@@ -2767,24 +2776,34 @@ const withNodeId = ({ editor, getOptions, tf: { apply, insertNode, insertNodes }
2767
2776
  };
2768
2777
  if (operation.type === "insert_node") {
2769
2778
  const node = cloneDeep(operation.node);
2770
- applyDeepToNodes({
2771
- apply: removeIdFromNodeIfDuplicate,
2772
- node,
2773
- query,
2774
- source: {}
2775
- });
2776
- defaultsDeepToNodes({
2777
- node,
2778
- path: operation.path,
2779
- query,
2780
- source: idPropsCreator
2781
- });
2782
- if (!disableInsertOverrides) applyDeepToNodes({
2783
- apply: overrideIdIfSet,
2784
- node,
2785
- query,
2786
- source: {}
2779
+ const existingIds = collectExistingEditorIds({
2780
+ duplicateCandidateIds: collectDuplicateCandidateIds({
2781
+ disableInsertOverrides,
2782
+ idKey,
2783
+ nodeEntry: [node, operation.path],
2784
+ query
2785
+ }),
2786
+ idKey
2787
2787
  });
2788
+ const hasIdInEditor = (id) => existingIds.has(id);
2789
+ const normalizeInsertedNode = (nodeEntry) => {
2790
+ const [entryNode, path] = nodeEntry;
2791
+ if (queryNode(nodeEntry, query)) {
2792
+ if (entryNode[idKey] !== void 0 && hasIdInEditor(entryNode[idKey])) delete entryNode[idKey];
2793
+ if (entryNode[idKey] === void 0) Object.assign(entryNode, idPropsCreator());
2794
+ if (!disableInsertOverrides && isDefined(entryNode._id)) {
2795
+ const id = entryNode._id;
2796
+ entryNode._id = void 0;
2797
+ if (!hasIdInEditor(id)) entryNode[idKey] = id;
2798
+ }
2799
+ }
2800
+ const children = entryNode.children;
2801
+ if (!children) return;
2802
+ children.forEach((child, index) => {
2803
+ normalizeInsertedNode([child, [...path, index]]);
2804
+ });
2805
+ };
2806
+ normalizeInsertedNode([node, operation.path]);
2788
2807
  return apply({
2789
2808
  ...operation,
2790
2809
  node
@@ -2841,29 +2860,103 @@ const withNodeId = ({ editor, getOptions, tf: { apply, insertNode, insertNodes }
2841
2860
 
2842
2861
  //#endregion
2843
2862
  //#region src/lib/plugins/node-id/NodeIdPlugin.ts
2863
+ const defaultNodeIdFilter = () => true;
2864
+ const isDefaultNodeIdFastPath = ({ allow, exclude, filter, filterInline = true, filterText = true }) => allow === void 0 && exclude === void 0 && (filter === void 0 || filter === defaultNodeIdFilter) && filterInline && filterText;
2865
+ const isBlockCandidate = (node, isBlock) => ElementApi.isElement(node) && (isBlock ? isBlock(node) : node.inline !== true);
2866
+ const shouldAssignNodeId = (entry, options = {}) => {
2867
+ const { allow, exclude, filter = () => true, filterInline = true, filterText = true, isBlock, idKey = "id" } = options;
2868
+ const [node, path] = entry;
2869
+ return !node[idKey] && queryNode([node, path], {
2870
+ allow,
2871
+ exclude,
2872
+ filter: (nextEntry) => {
2873
+ const [entryNode] = nextEntry;
2874
+ if (filterText && !ElementApi.isElement(entryNode)) return false;
2875
+ if (filterInline && ElementApi.isElement(entryNode) && !(isBlock ? isBlock(entryNode) : entryNode.inline !== true)) return false;
2876
+ return filter(nextEntry);
2877
+ }
2878
+ });
2879
+ };
2880
+ const resolveInitialValueIds = (options) => {
2881
+ if (options.initialValueIds !== void 0) return options.initialValueIds;
2882
+ if (options.normalizeInitialValue === null) return false;
2883
+ if (options.normalizeInitialValue === true) return "always";
2884
+ return "if-needed";
2885
+ };
2844
2886
  /**
2845
2887
  * Normalize node IDs in a value without using editor operations. This is a pure
2846
- * function that returns a new normalized value.
2888
+ * function that returns the normalized value and preserves references for
2889
+ * unchanged branches.
2847
2890
  */
2848
- const normalizeNodeId = (value, options = {}) => {
2849
- const { allow, exclude, filter = () => true, filterInline = true, filterText = true, idCreator = () => nanoid(10), idKey = "id" } = options;
2891
+ const normalizeNodeIdRuntime = (value, options = {}) => {
2892
+ const { idCreator = () => nanoid(10), idKey = "id" } = options;
2893
+ if (isDefaultNodeIdFastPath(options)) {
2894
+ const normalizeNodeFast = (node) => {
2895
+ if (!ElementApi.isElement(node)) return node;
2896
+ if (!isBlockCandidate(node, options.isBlock)) return node;
2897
+ let nextChildren;
2898
+ node.children.forEach((child, index) => {
2899
+ const nextChild = normalizeNodeFast(child);
2900
+ if (nextChild !== child) {
2901
+ if (!nextChildren) nextChildren = [...node.children];
2902
+ nextChildren[index] = nextChild;
2903
+ }
2904
+ });
2905
+ if (!node[idKey]) return {
2906
+ ...node,
2907
+ ...nextChildren ? { children: nextChildren } : {},
2908
+ [idKey]: idCreator()
2909
+ };
2910
+ if (nextChildren) return {
2911
+ ...node,
2912
+ children: nextChildren
2913
+ };
2914
+ return node;
2915
+ };
2916
+ let valueChanged$1 = false;
2917
+ const nextValue$1 = value.map((node) => {
2918
+ const nextNode = normalizeNodeFast(node);
2919
+ if (nextNode !== node) valueChanged$1 = true;
2920
+ return nextNode;
2921
+ });
2922
+ return valueChanged$1 ? nextValue$1 : value;
2923
+ }
2850
2924
  const normalizeNode = (node, path) => {
2851
- const clonedNode = { ...node };
2852
- if (!clonedNode[idKey] && queryNode([clonedNode, path], {
2853
- allow,
2854
- exclude,
2855
- filter: (entry) => {
2856
- const [node$1] = entry;
2857
- if (filterText && !ElementApi.isElement(node$1)) return false;
2858
- if (filterInline && ElementApi.isElement(node$1) && node$1.inline === true) return false;
2859
- return filter(entry);
2860
- }
2861
- })) clonedNode[idKey] = idCreator();
2862
- if (ElementApi.isElement(clonedNode)) clonedNode.children = clonedNode.children.map((child, index) => normalizeNode(child, [...path, index]));
2863
- return clonedNode;
2925
+ let nextNode = node;
2926
+ let childrenChanged = false;
2927
+ if (shouldAssignNodeId([node, path], options)) nextNode = {
2928
+ ...node,
2929
+ [idKey]: idCreator()
2930
+ };
2931
+ if (ElementApi.isElement(node)) {
2932
+ const nextChildren = node.children.map((child, index) => {
2933
+ const nextChild = normalizeNode(child, [...path, index]);
2934
+ if (nextChild !== child) childrenChanged = true;
2935
+ return nextChild;
2936
+ });
2937
+ if (childrenChanged) nextNode = nextNode === node ? {
2938
+ ...node,
2939
+ children: nextChildren
2940
+ } : {
2941
+ ...nextNode,
2942
+ children: nextChildren
2943
+ };
2944
+ }
2945
+ return nextNode;
2864
2946
  };
2865
- return value.map((node, index) => normalizeNode(node, [index]));
2947
+ let valueChanged = false;
2948
+ const nextValue = value.map((node, index) => {
2949
+ const nextNode = normalizeNode(node, [index]);
2950
+ if (nextNode !== node) valueChanged = true;
2951
+ return nextNode;
2952
+ });
2953
+ return valueChanged ? nextValue : value;
2866
2954
  };
2955
+ const normalizeNodeId = (value, options = {}) => normalizeNodeIdRuntime(value, options);
2956
+ const normalizeNodeIdWithEditor = (editor, value, options = {}) => normalizeNodeIdRuntime(value, {
2957
+ ...options,
2958
+ isBlock: editor.api.isBlock
2959
+ });
2867
2960
  /** @see {@link withNodeId} */
2868
2961
  const NodeIdPlugin = createTSlatePlugin({
2869
2962
  key: "nodeId",
@@ -2871,29 +2964,51 @@ const NodeIdPlugin = createTSlatePlugin({
2871
2964
  filterInline: true,
2872
2965
  filterText: true,
2873
2966
  idKey: "id",
2874
- normalizeInitialValue: false,
2875
- filter: () => true,
2967
+ filter: defaultNodeIdFilter,
2876
2968
  idCreator: () => nanoid(10)
2877
2969
  }
2878
2970
  }).extendTransforms(({ editor, getOptions }) => ({ normalize() {
2879
- const { allow, exclude, filter, filterInline, filterText, idKey } = getOptions();
2971
+ const options = getOptions();
2972
+ const { idCreator, idKey } = options;
2973
+ const updates = [];
2974
+ if (isDefaultNodeIdFastPath({
2975
+ ...options,
2976
+ isBlock: editor.api.isBlock
2977
+ })) {
2978
+ const path = [];
2979
+ const visitFast = (node) => {
2980
+ if (!ElementApi.isElement(node)) return;
2981
+ if (!isBlockCandidate(node, editor.api.isBlock)) return;
2982
+ if (!node[idKey]) updates.push({
2983
+ at: [...path],
2984
+ props: { [idKey]: idCreator() }
2985
+ });
2986
+ node.children.forEach((child, index) => {
2987
+ path.push(index);
2988
+ visitFast(child);
2989
+ path.pop();
2990
+ });
2991
+ };
2992
+ editor.children.forEach((node, index) => {
2993
+ path.push(index);
2994
+ visitFast(node);
2995
+ path.pop();
2996
+ });
2997
+ if (updates.length === 0) return;
2998
+ editor.tf.withoutSaving(() => {
2999
+ editor.tf.setNodesBatch(updates);
3000
+ });
3001
+ return;
3002
+ }
2880
3003
  const addNodeId = (entry) => {
2881
3004
  const [node, path] = entry;
2882
- if (!node[idKey] && queryNode([node, path], {
2883
- allow,
2884
- exclude,
2885
- filter: (entry$1) => {
2886
- const [node$1] = entry$1;
2887
- if (filterText && !ElementApi.isElement(node$1)) return false;
2888
- if (filterInline && ElementApi.isElement(node$1) && !editor.api.isBlock(node$1)) return false;
2889
- return filter(entry$1);
2890
- }
2891
- })) {
2892
- if (!editor.api.node(path)) return;
2893
- editor.tf.withoutSaving(() => {
2894
- editor.tf.setNodes({ [idKey]: getOptions().idCreator() }, { at: path });
2895
- });
2896
- }
3005
+ if (shouldAssignNodeId(entry, {
3006
+ ...options,
3007
+ isBlock: editor.api.isBlock
3008
+ })) updates.push({
3009
+ at: path,
3010
+ props: { [idKey]: idCreator() }
3011
+ });
2897
3012
  if (ElementApi.isElement(node)) node.children.forEach((child, index) => {
2898
3013
  addNodeId([child, [...path, index]]);
2899
3014
  });
@@ -2901,14 +3016,21 @@ const NodeIdPlugin = createTSlatePlugin({
2901
3016
  editor.children.forEach((node, index) => {
2902
3017
  addNodeId([node, [index]]);
2903
3018
  });
2904
- } })).extend({ normalizeInitialValue: ({ editor, getOptions, tf }) => {
2905
- const { normalizeInitialValue } = getOptions();
2906
- if (!normalizeInitialValue) {
2907
- const firstNode = editor.children[0];
2908
- const lastNode = editor.children.at(-1);
2909
- if (firstNode?.id && lastNode?.id) return;
3019
+ if (updates.length === 0) return;
3020
+ editor.tf.withoutSaving(() => {
3021
+ editor.tf.setNodesBatch(updates);
3022
+ });
3023
+ } })).extend({ transformInitialValue: ({ editor, getOptions, value }) => {
3024
+ const options = getOptions();
3025
+ const { idKey = "id" } = options;
3026
+ const initialValueIds = resolveInitialValueIds(options);
3027
+ if (initialValueIds === false) return value;
3028
+ if (initialValueIds === "if-needed") {
3029
+ const firstNode = value[0];
3030
+ const lastNode = value.at(-1);
3031
+ if (firstNode?.[idKey] && lastNode?.[idKey]) return value;
2910
3032
  }
2911
- tf.nodeId.normalize();
3033
+ return normalizeNodeIdWithEditor(editor, value, options);
2912
3034
  } }).overrideEditor(withNodeId);
2913
3035
 
2914
3036
  //#endregion
@@ -2957,7 +3079,7 @@ const pipeOnTextChange = (editor, node, text, prevText, operation) => {
2957
3079
  const DEFAULT = {
2958
3080
  handlers: true,
2959
3081
  inject: true,
2960
- normalizeInitialValue: false,
3082
+ transformInitialValue: false,
2961
3083
  render: true
2962
3084
  };
2963
3085
  /**
@@ -2967,29 +3089,43 @@ const DEFAULT = {
2967
3089
  * @param plugin The plugin to check
2968
3090
  * @param isReadOnly Whether the editor is in read-only mode
2969
3091
  * @param feature The feature to check ('render' | 'handlers' | 'inject' |
2970
- * 'normalizeInitialValue')
3092
+ * 'transformInitialValue')
2971
3093
  * @returns True if the feature should be disabled
2972
3094
  */
2973
3095
  const isEditOnly = (readOnly, plugin, feature) => {
2974
3096
  if (!readOnly) return false;
2975
- if (plugin.editOnly === true) return DEFAULT[feature];
2976
- if (typeof plugin.editOnly === "object") return plugin.editOnly[feature] ?? DEFAULT[feature];
3097
+ const resolvedFeature = feature === "normalizeInitialValue" ? "transformInitialValue" : feature;
3098
+ if (plugin.editOnly === true) return DEFAULT[resolvedFeature];
3099
+ if (typeof plugin.editOnly === "object") {
3100
+ if (plugin.editOnly[resolvedFeature] !== void 0) return plugin.editOnly[resolvedFeature];
3101
+ if (resolvedFeature === "transformInitialValue") return plugin.editOnly.normalizeInitialValue ?? DEFAULT.transformInitialValue;
3102
+ return DEFAULT[resolvedFeature];
3103
+ }
2977
3104
  return false;
2978
3105
  };
2979
3106
 
2980
3107
  //#endregion
2981
- //#region src/internal/plugin/pipeNormalizeInitialValue.ts
2982
- /** Normalize initial value from editor plugins. Set into plate store if diff. */
2983
- const pipeNormalizeInitialValue = (editor) => {
3108
+ //#region src/internal/plugin/pipeTransformInitialValue.ts
3109
+ /** Transform initial value from editor plugins before the editor is ready. */
3110
+ const pipeTransformInitialValue = (editor) => {
2984
3111
  const value = editor.meta.isNormalizing;
2985
3112
  editor.meta.isNormalizing = true;
2986
- editor.meta.pluginCache.normalizeInitialValue.forEach((key) => {
3113
+ editor.meta.pluginCache.transformInitialValue.forEach((key) => {
2987
3114
  const p = editor.getPlugin({ key });
2988
- if (isEditOnly(editor.dom.readOnly, p, "normalizeInitialValue")) return;
2989
- p.normalizeInitialValue?.({
3115
+ if (isEditOnly(editor.dom.readOnly, p, "transformInitialValue")) return;
3116
+ if (!p.transformInitialValue && !p.normalizeInitialValue) return;
3117
+ const ctx = {
2990
3118
  ...getEditorPlugin(editor, p),
2991
3119
  value: editor.children
2992
- });
3120
+ };
3121
+ if (p.transformInitialValue) {
3122
+ const nextValue$1 = p.transformInitialValue(ctx);
3123
+ if (nextValue$1 === void 0) throw new Error(`Plugin "${key}" transformInitialValue must return the next value.`);
3124
+ editor.children = nextValue$1;
3125
+ return;
3126
+ }
3127
+ const nextValue = p.normalizeInitialValue?.(ctx);
3128
+ if (nextValue !== void 0) editor.children = nextValue;
2993
3129
  });
2994
3130
  editor.meta.isNormalizing = value;
2995
3131
  };
@@ -3004,7 +3140,7 @@ const init = (editor, { autoSelect, selection, shouldNormalizeEditor, value, onR
3004
3140
  const target = (autoSelect === "start" ? "start" : "end") === "start" ? editor.api.start([]) : editor.api.end([]);
3005
3141
  editor.tf.select(target);
3006
3142
  }
3007
- if (editor.children.length > 0) pipeNormalizeInitialValue(editor);
3143
+ if (editor.children.length > 0) pipeTransformInitialValue(editor);
3008
3144
  if (shouldNormalizeEditor) editor.tf.normalize({ force: true });
3009
3145
  if (onReady) onReady({
3010
3146
  editor,
@@ -3671,4 +3807,4 @@ const withSlate = (e, { id, affinity = true, autoSelect, chunking = true, maxLen
3671
3807
  const createSlateEditor = ({ editor = createEditor(), ...options } = {}) => withSlate(editor, options);
3672
3808
 
3673
3809
  //#endregion
3674
- export { isHtmlBlockElement as $, defineInputRule as $t, htmlStringToDOMNode as A, isSlatePluginElement as At, htmlBrToNewLine as B, withDeleteRules as Bt, resolveNavigationFeedbackTarget as C, getInjectMatch as Ct, HtmlPlugin as D, isSlateElement as Dt, LengthPlugin as E, isSlateEditor as Et, pipeDeserializeHtmlLeaf as F, applyDeepToNodes as Ft, inferWhiteSpaceRule as G, AstPlugin as Gt, deserializeHtmlNodeChildren as H, BaseParagraphPlugin as Ht, htmlElementToElement as I, OverridePlugin as It, collapseWhiteSpaceText as J, createMarkInputRule as Jt, collapseWhiteSpaceChildren as K, createBlockFenceInputRule as Kt, pipeDeserializeHtmlElement as L, withOverrides as Lt, deserializeHtmlNode as M, isSlateString as Mt, htmlTextNodeToString as N, isSlateText as Nt, parseHtmlDocument as O, isSlateLeaf as Ot, htmlElementToLeaf as P, isSlateVoid as Pt, collapseString as Q, matchDelimitedInline as Qt, pluginDeserializeHtml as R, withNormalizeRules as Rt, flashTarget as S, getInjectedPlugins as St, NavigationFeedbackPluginKey as T, getSlateElements as Tt, collapseWhiteSpace as U, HistoryPlugin as Ut, htmlBodyToFragment as V, withBreakRules as Vt, collapseWhiteSpaceElement as W, withPlateHistory as Wt, upsertInlineFormattingContext as X, matchBlockFence as Xt, endInlineFormattingContext as Y, createTextSubstitutionInputRule as Yt, isLastNonEmptyTextOfInlineFormattingContext as Z, matchBlockStart as Zt, normalizeNodeId as _, mergeDeepToNodes as _t, pipeInsertDataQuery as a, getPluginTypes as an, DOMPlugin as at, navigate as b, getNodeDataAttributeKeys as bt, setValue as c, createSlatePlugin as cn, PlateError as ct, insertExitBreak as d, AffinityPlugin as dt, getContainerTypes as en, isHtmlInlineElement as et, init as f, setAffinitySelection as ft, NodeIdPlugin as g, getEdgeNodes as gt, pipeOnNodeChange as h, getMarkBoundaryAffinity as ht, ParserPlugin as i, getPluginType as in, AUTO_SCROLL as it, deserializeHtmlElement as j, isSlatePluginNode as jt, deserializeHtml as k, isSlateNode as kt, resetBlock as l, createTSlatePlugin as ln, ChunkingPlugin as lt, pipeOnTextChange as m, isNodesAffinity as mt, withSlate as n, getPluginKey as nn, isHtmlText as nt, normalizeDescendantsToDocumentFragment as o, getSlatePlugin as on, withScrolling as ot, isEditOnly as p, isNodeAffinity as pt, collapseWhiteSpaceNode as q, createBlockStartInputRule as qt, getCorePlugins as r, getPluginKeys as rn, isHtmlElement as rt, SlateExtensionPlugin as s, getEditorPlugin as sn, DebugPlugin as st, createSlateEditor as t, getPluginByType as tn, inlineTagNames as tt, liftBlock as u, withChunking as ut, withNodeId as v, getSlateClass as vt, NAVIGATION_FEEDBACK_KEY as w, defaultsDeepToNodes as wt, clearNavigationFeedbackTarget as x, keyToDataAttribute as xt, NavigationFeedbackPlugin as y, getPluginNodeProps as yt, getDataNodeProps as z, withMergeRules as zt };
3810
+ export { collapseString as $, defineInputRule as $t, deserializeHtml as A, isSlatePluginElement as At, getDataNodeProps as B, withDeleteRules as Bt, flashTarget as C, getInjectedPlugins as Ct, LengthPlugin as D, isSlateElement as Dt, NavigationFeedbackPluginKey as E, isSlateEditor as Et, htmlElementToLeaf as F, applyDeepToNodes as Ft, collapseWhiteSpaceElement as G, AstPlugin as Gt, htmlBodyToFragment as H, BaseParagraphPlugin as Ht, pipeDeserializeHtmlLeaf as I, OverridePlugin as It, collapseWhiteSpaceNode as J, createMarkInputRule as Jt, inferWhiteSpaceRule as K, createBlockFenceInputRule as Kt, htmlElementToElement as L, withOverrides as Lt, deserializeHtmlElement as M, isSlateString as Mt, deserializeHtmlNode as N, isSlateText as Nt, HtmlPlugin as O, isSlateLeaf as Ot, htmlTextNodeToString as P, isSlateVoid as Pt, isLastNonEmptyTextOfInlineFormattingContext as Q, matchDelimitedInline as Qt, pipeDeserializeHtmlElement as R, withNormalizeRules as Rt, clearNavigationFeedbackTarget as S, keyToDataAttribute as St, NAVIGATION_FEEDBACK_KEY as T, getSlateElements as Tt, deserializeHtmlNodeChildren as U, HistoryPlugin as Ut, htmlBrToNewLine as V, withBreakRules as Vt, collapseWhiteSpace as W, withPlateHistory as Wt, endInlineFormattingContext as X, matchBlockFence as Xt, collapseWhiteSpaceText as Y, createTextSubstitutionInputRule as Yt, upsertInlineFormattingContext as Z, matchBlockStart as Zt, normalizeNodeId as _, getEdgeNodes as _t, pipeInsertDataQuery as a, getPluginTypes as an, AUTO_SCROLL as at, NavigationFeedbackPlugin as b, getPluginNodeProps as bt, setValue as c, createSlatePlugin as cn, DebugPlugin as ct, insertExitBreak as d, withChunking as dt, getContainerTypes as en, isHtmlBlockElement as et, init as f, AffinityPlugin as ft, NodeIdPlugin as g, getMarkBoundaryAffinity as gt, pipeOnNodeChange as h, isNodesAffinity as ht, ParserPlugin as i, getPluginType as in, isHtmlElement as it, htmlStringToDOMNode as j, isSlatePluginNode as jt, parseHtmlDocument as k, isSlateNode as kt, resetBlock as l, createTSlatePlugin as ln, PlateError as lt, pipeOnTextChange as m, isNodeAffinity as mt, withSlate as n, getPluginKey as nn, inlineTagNames as nt, normalizeDescendantsToDocumentFragment as o, getSlatePlugin as on, DOMPlugin as ot, isEditOnly as p, setAffinitySelection as pt, collapseWhiteSpaceChildren as q, createBlockStartInputRule as qt, getCorePlugins as r, getPluginKeys as rn, isHtmlText as rt, SlateExtensionPlugin as s, getEditorPlugin as sn, withScrolling as st, createSlateEditor as t, getPluginByType as tn, isHtmlInlineElement as tt, liftBlock as u, ChunkingPlugin as ut, normalizeNodeIdWithEditor as v, mergeDeepToNodes as vt, resolveNavigationFeedbackTarget as w, getInjectMatch as wt, navigate as x, getNodeDataAttributeKeys as xt, withNodeId as y, getSlateClass as yt, pluginDeserializeHtml as z, withMergeRules as zt };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lofcz/platejs-core",
3
- "version": "53.1.6",
3
+ "version": "53.1.8",
4
4
  "description": "The core of Plate – a plugin system for slate",
5
5
  "keywords": [
6
6
  "contentEditable",
@@ -41,19 +41,19 @@
41
41
  "is-hotkey": "^0.2.0",
42
42
  "jotai": "~2.8.4",
43
43
  "jotai-optics": "0.4.0",
44
- "jotai-x": "2.3.3",
44
+ "jotai-x": "2.3.4",
45
45
  "lodash": "^4.17.21",
46
46
  "nanoid": "^5.1.5",
47
47
  "optics-ts": "2.4.1",
48
48
  "react-compiler-runtime": "^1.0.0",
49
49
  "slate": "0.124.1",
50
50
  "slate-dom": "0.124.1",
51
- "slate-hyperscript": "0.100.0",
51
+ "slate-hyperscript": "0.125.0",
52
52
  "slate-react": "0.124.2",
53
53
  "use-deep-compare": "^1.3.0",
54
54
  "zustand": "^5.0.5",
55
55
  "zustand-x": "6.2.1",
56
- "@platejs/slate": "npm:@lofcz/platejs-slate@53.1.1",
56
+ "@platejs/slate": "npm:@lofcz/platejs-slate@53.1.7",
57
57
  "@udecode/react-hotkeys": "npm:@lofcz/udecode-react-hotkeys@52.3.6",
58
58
  "@udecode/react-utils": "npm:@lofcz/udecode-react-utils@52.3.4",
59
59
  "@udecode/utils": "npm:@lofcz/udecode-utils@52.3.4"