@lofcz/platejs-core 53.1.6 → 53.2.1

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 getEditorDOMFromHtmlString, Ao as SlateRenderElement, Ba as stripHtmlClassNames, Co as SlateRenderText, Do as pipeRenderLeafStatic, Eo as SlateRenderLeaf, Ga as getSelectedDomBlocks, Ha as isSelectOutside, Ja as getPluginDataAttributes, Ka as getRenderNodeStaticProps, Oo as pluginRenderLeafStatic, Qa as createStaticEditor, So as PlateStaticProps, To as pluginRenderTextStatic, Ua as getSelectedDomNode, Va as pipeDecorate, Wa as getSelectedDomFragment, Xa as getStaticPlugins, Ya as createStaticString, Za as ViewPlugin, _o as SerializeHtmlOptions, ao as SlateNodeProps, bo as LeafStatic, co as StyledSlateElementProps, do as useNodeAttributes, eo as SlateElement, fo as BoxStaticProps, go as SlateRenderTextProps, ho as SlateRenderNodeProps, io as SlateLeafProps, jo as pluginRenderElementStatic, ko as pipeRenderElementStatic, lo as StyledSlateLeafProps, mo as SlateRenderLeafProps, no as SlateHTMLProps, oo as SlateText, po as SlateRenderElementProps, qa as getNodeDataAttributes, ro as SlateLeaf, so as SlateTextProps, to as SlateElementProps, uo as StyledSlateTextProps, vo as serializeHtml, wo as pipeRenderTextStatic, xo as PlateStatic, yo as ElementStatic, za as stripSlateDataAttributes } from "../index-ClAE30YQ";
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-pfxNb3FA.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-C-jR24Fx.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 { Ct as keyToDataAttribute, Tt as getInjectMatch, bt as getSlateClass, cn as getEditorPlugin, m as isEditOnly, n as withSlate, nn as getPluginByType, st as DOMPlugin, xt as getPluginNodeProps } from "./withSlate-pfxNb3FA.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";
@@ -783,12 +782,13 @@ const resolvePlugins = (editor, plugins = [], createStore = createVanillaStore)
783
782
  node: {
784
783
  isContainer: [],
785
784
  isLeaf: [],
785
+ isMetadataProp: [],
786
786
  isText: [],
787
787
  leafProps: [],
788
788
  textProps: [],
789
789
  types: {}
790
790
  },
791
- normalizeInitialValue: [],
791
+ transformInitialValue: [],
792
792
  render: {
793
793
  aboveEditable: [],
794
794
  aboveNodes: [],
@@ -814,6 +814,7 @@ const resolvePlugins = (editor, plugins = [], createStore = createVanillaStore)
814
814
  }
815
815
  resolvePluginMethods(editor, plugin);
816
816
  if (plugin.node?.isContainer) editor.meta.pluginCache.node.isContainer.push(plugin.key);
817
+ if (plugin.node?.isMetadataProp) editor.meta.pluginCache.node.isMetadataProp.push(plugin.key);
817
818
  editor.meta.pluginCache.node.types[plugin.node.type] = plugin.key;
818
819
  if (plugin.inject?.nodeProps) editor.meta.pluginCache.inject.nodeProps.push(plugin.key);
819
820
  if (plugin.render?.node) editor.meta.components[plugin.key] = plugin.render.node;
@@ -829,7 +830,7 @@ const resolvePlugins = (editor, plugins = [], createStore = createVanillaStore)
829
830
  if (plugin.render.afterContainer) editor.meta.pluginCache.render.afterContainer.push(plugin.key);
830
831
  if (plugin.render.beforeContainer) editor.meta.pluginCache.render.beforeContainer.push(plugin.key);
831
832
  if (plugin.render.belowRootNodes) editor.meta.pluginCache.render.belowRootNodes.push(plugin.key);
832
- if (plugin.normalizeInitialValue) editor.meta.pluginCache.normalizeInitialValue.push(plugin.key);
833
+ if (plugin.transformInitialValue || plugin.normalizeInitialValue) editor.meta.pluginCache.transformInitialValue.push(plugin.key);
833
834
  if (plugin.decorate) editor.meta.pluginCache.decorate.push(plugin.key);
834
835
  if (plugin.render.aboveNodes) editor.meta.pluginCache.render.aboveNodes.push(plugin.key);
835
836
  if (plugin.render.belowNodes) editor.meta.pluginCache.render.belowNodes.push(plugin.key);
@@ -1456,16 +1457,6 @@ const isSlatePluginElement = (element, pluginKey) => element.dataset.slateNode =
1456
1457
  const isSlatePluginNode = (element, pluginKey) => element.classList.contains(`slate-${pluginKey}`);
1457
1458
  const getSlateElements = (element) => Array.from(element.querySelectorAll("[data-slate-node=\"element\"]"));
1458
1459
 
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
1460
  //#endregion
1470
1461
  //#region src/lib/utils/getInjectMatch.ts
1471
1462
  const getInjectMatch = (editor, plugin) => {
@@ -2732,6 +2723,7 @@ const NavigationFeedbackPlugin = createTSlatePlugin({
2732
2723
 
2733
2724
  //#endregion
2734
2725
  //#region src/lib/plugins/node-id/withNodeId.ts
2726
+ const now = () => globalThis.performance?.now() ?? Date.now();
2735
2727
  /** Enables support for inserting nodes with an id key. */
2736
2728
  const withNodeId = ({ editor, getOptions, tf: { apply, insertNode, insertNodes } }) => {
2737
2729
  const idPropsCreator = () => ({ [getOptions().idKey ?? ""]: getOptions().idCreator() });
@@ -2739,23 +2731,42 @@ const withNodeId = ({ editor, getOptions, tf: { apply, insertNode, insertNodes }
2739
2731
  const { filter, filterText } = getOptions();
2740
2732
  return filter(nodeEntry) && (!filterText || nodeEntry[0]?.type !== void 0);
2741
2733
  };
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];
2734
+ const collectDuplicateCandidateIds = ({ disableInsertOverrides, idKey, nodeEntry, query }) => {
2735
+ const duplicateCandidateIds = /* @__PURE__ */ new Set();
2736
+ const collectNodeIds = (entry) => {
2737
+ const [entryNode, path] = entry;
2738
+ if (queryNode(entry, query)) {
2739
+ if (entryNode[idKey] !== void 0) duplicateCandidateIds.add(entryNode[idKey]);
2740
+ if (!disableInsertOverrides && isDefined(entryNode._id)) duplicateCandidateIds.add(entryNode._id);
2741
+ }
2742
+ const children = entryNode.children;
2743
+ if (!children) return;
2744
+ children.forEach((child, index) => {
2745
+ collectNodeIds([child, [...path, index]]);
2746
+ });
2747
+ };
2748
+ collectNodeIds(nodeEntry);
2749
+ return duplicateCandidateIds;
2748
2750
  };
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;
2751
+ const collectExistingEditorIds = ({ duplicateCandidateIds, idKey }) => {
2752
+ if (duplicateCandidateIds.size === 0) return /* @__PURE__ */ new Set();
2753
+ const existingIds = /* @__PURE__ */ new Set();
2754
+ const start = now();
2755
+ let visitedCount = 0;
2756
+ for (const [entryNode] of NodeApi.nodes(editor)) {
2757
+ visitedCount += 1;
2758
+ const id = entryNode?.[idKey];
2759
+ if (id === void 0 || !duplicateCandidateIds.has(id)) continue;
2760
+ existingIds.add(id);
2761
+ if (existingIds.size === duplicateCandidateIds.size) break;
2758
2762
  }
2763
+ getOptions().onDuplicateIdScan?.({
2764
+ candidateCount: duplicateCandidateIds.size,
2765
+ duration: now() - start,
2766
+ existingCount: existingIds.size,
2767
+ visitedCount
2768
+ });
2769
+ return existingIds;
2759
2770
  };
2760
2771
  return { transforms: {
2761
2772
  apply(operation) {
@@ -2767,24 +2778,34 @@ const withNodeId = ({ editor, getOptions, tf: { apply, insertNode, insertNodes }
2767
2778
  };
2768
2779
  if (operation.type === "insert_node") {
2769
2780
  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: {}
2781
+ const existingIds = collectExistingEditorIds({
2782
+ duplicateCandidateIds: collectDuplicateCandidateIds({
2783
+ disableInsertOverrides,
2784
+ idKey,
2785
+ nodeEntry: [node, operation.path],
2786
+ query
2787
+ }),
2788
+ idKey
2787
2789
  });
2790
+ const hasIdInEditor = (id) => existingIds.has(id);
2791
+ const normalizeInsertedNode = (nodeEntry) => {
2792
+ const [entryNode, path] = nodeEntry;
2793
+ if (queryNode(nodeEntry, query)) {
2794
+ if (entryNode[idKey] !== void 0 && hasIdInEditor(entryNode[idKey])) delete entryNode[idKey];
2795
+ if (entryNode[idKey] === void 0) Object.assign(entryNode, idPropsCreator());
2796
+ if (!disableInsertOverrides && isDefined(entryNode._id)) {
2797
+ const id = entryNode._id;
2798
+ entryNode._id = void 0;
2799
+ if (!hasIdInEditor(id)) entryNode[idKey] = id;
2800
+ }
2801
+ }
2802
+ const children = entryNode.children;
2803
+ if (!children) return;
2804
+ children.forEach((child, index) => {
2805
+ normalizeInsertedNode([child, [...path, index]]);
2806
+ });
2807
+ };
2808
+ normalizeInsertedNode([node, operation.path]);
2788
2809
  return apply({
2789
2810
  ...operation,
2790
2811
  node
@@ -2841,29 +2862,103 @@ const withNodeId = ({ editor, getOptions, tf: { apply, insertNode, insertNodes }
2841
2862
 
2842
2863
  //#endregion
2843
2864
  //#region src/lib/plugins/node-id/NodeIdPlugin.ts
2865
+ const defaultNodeIdFilter = () => true;
2866
+ const isDefaultNodeIdFastPath = ({ allow, exclude, filter, filterInline = true, filterText = true }) => allow === void 0 && exclude === void 0 && (filter === void 0 || filter === defaultNodeIdFilter) && filterInline && filterText;
2867
+ const isBlockCandidate = (node, isBlock) => ElementApi.isElement(node) && (isBlock ? isBlock(node) : node.inline !== true);
2868
+ const shouldAssignNodeId = (entry, options = {}) => {
2869
+ const { allow, exclude, filter = () => true, filterInline = true, filterText = true, isBlock, idKey = "id" } = options;
2870
+ const [node, path] = entry;
2871
+ return !node[idKey] && queryNode([node, path], {
2872
+ allow,
2873
+ exclude,
2874
+ filter: (nextEntry) => {
2875
+ const [entryNode] = nextEntry;
2876
+ if (filterText && !ElementApi.isElement(entryNode)) return false;
2877
+ if (filterInline && ElementApi.isElement(entryNode) && !(isBlock ? isBlock(entryNode) : entryNode.inline !== true)) return false;
2878
+ return filter(nextEntry);
2879
+ }
2880
+ });
2881
+ };
2882
+ const resolveInitialValueIds = (options) => {
2883
+ if (options.initialValueIds !== void 0) return options.initialValueIds;
2884
+ if (options.normalizeInitialValue === null) return false;
2885
+ if (options.normalizeInitialValue === true) return "always";
2886
+ return "if-needed";
2887
+ };
2844
2888
  /**
2845
2889
  * Normalize node IDs in a value without using editor operations. This is a pure
2846
- * function that returns a new normalized value.
2890
+ * function that returns the normalized value and preserves references for
2891
+ * unchanged branches.
2847
2892
  */
2848
- const normalizeNodeId = (value, options = {}) => {
2849
- const { allow, exclude, filter = () => true, filterInline = true, filterText = true, idCreator = () => nanoid(10), idKey = "id" } = options;
2893
+ const normalizeNodeIdRuntime = (value, options = {}) => {
2894
+ const { idCreator = () => nanoid(10), idKey = "id" } = options;
2895
+ if (isDefaultNodeIdFastPath(options)) {
2896
+ const normalizeNodeFast = (node) => {
2897
+ if (!ElementApi.isElement(node)) return node;
2898
+ if (!isBlockCandidate(node, options.isBlock)) return node;
2899
+ let nextChildren;
2900
+ node.children.forEach((child, index) => {
2901
+ const nextChild = normalizeNodeFast(child);
2902
+ if (nextChild !== child) {
2903
+ if (!nextChildren) nextChildren = [...node.children];
2904
+ nextChildren[index] = nextChild;
2905
+ }
2906
+ });
2907
+ if (!node[idKey]) return {
2908
+ ...node,
2909
+ ...nextChildren ? { children: nextChildren } : {},
2910
+ [idKey]: idCreator()
2911
+ };
2912
+ if (nextChildren) return {
2913
+ ...node,
2914
+ children: nextChildren
2915
+ };
2916
+ return node;
2917
+ };
2918
+ let valueChanged$1 = false;
2919
+ const nextValue$1 = value.map((node) => {
2920
+ const nextNode = normalizeNodeFast(node);
2921
+ if (nextNode !== node) valueChanged$1 = true;
2922
+ return nextNode;
2923
+ });
2924
+ return valueChanged$1 ? nextValue$1 : value;
2925
+ }
2850
2926
  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;
2927
+ let nextNode = node;
2928
+ let childrenChanged = false;
2929
+ if (shouldAssignNodeId([node, path], options)) nextNode = {
2930
+ ...node,
2931
+ [idKey]: idCreator()
2932
+ };
2933
+ if (ElementApi.isElement(node)) {
2934
+ const nextChildren = node.children.map((child, index) => {
2935
+ const nextChild = normalizeNode(child, [...path, index]);
2936
+ if (nextChild !== child) childrenChanged = true;
2937
+ return nextChild;
2938
+ });
2939
+ if (childrenChanged) nextNode = nextNode === node ? {
2940
+ ...node,
2941
+ children: nextChildren
2942
+ } : {
2943
+ ...nextNode,
2944
+ children: nextChildren
2945
+ };
2946
+ }
2947
+ return nextNode;
2864
2948
  };
2865
- return value.map((node, index) => normalizeNode(node, [index]));
2949
+ let valueChanged = false;
2950
+ const nextValue = value.map((node, index) => {
2951
+ const nextNode = normalizeNode(node, [index]);
2952
+ if (nextNode !== node) valueChanged = true;
2953
+ return nextNode;
2954
+ });
2955
+ return valueChanged ? nextValue : value;
2866
2956
  };
2957
+ const normalizeNodeId = (value, options = {}) => normalizeNodeIdRuntime(value, options);
2958
+ const normalizeNodeIdWithEditor = (editor, value, options = {}) => normalizeNodeIdRuntime(value, {
2959
+ ...options,
2960
+ isBlock: editor.api.isBlock
2961
+ });
2867
2962
  /** @see {@link withNodeId} */
2868
2963
  const NodeIdPlugin = createTSlatePlugin({
2869
2964
  key: "nodeId",
@@ -2871,29 +2966,51 @@ const NodeIdPlugin = createTSlatePlugin({
2871
2966
  filterInline: true,
2872
2967
  filterText: true,
2873
2968
  idKey: "id",
2874
- normalizeInitialValue: false,
2875
- filter: () => true,
2969
+ filter: defaultNodeIdFilter,
2876
2970
  idCreator: () => nanoid(10)
2877
2971
  }
2878
- }).extendTransforms(({ editor, getOptions }) => ({ normalize() {
2879
- const { allow, exclude, filter, filterInline, filterText, idKey } = getOptions();
2972
+ }).extend(({ getOptions }) => ({ node: { isMetadataProp: ({ key }) => key === (getOptions().idKey ?? "id") } })).extendTransforms(({ editor, getOptions }) => ({ normalize() {
2973
+ const options = getOptions();
2974
+ const { idCreator, idKey } = options;
2975
+ const updates = [];
2976
+ if (isDefaultNodeIdFastPath({
2977
+ ...options,
2978
+ isBlock: editor.api.isBlock
2979
+ })) {
2980
+ const path = [];
2981
+ const visitFast = (node) => {
2982
+ if (!ElementApi.isElement(node)) return;
2983
+ if (!isBlockCandidate(node, editor.api.isBlock)) return;
2984
+ if (!node[idKey]) updates.push({
2985
+ at: [...path],
2986
+ props: { [idKey]: idCreator() }
2987
+ });
2988
+ node.children.forEach((child, index) => {
2989
+ path.push(index);
2990
+ visitFast(child);
2991
+ path.pop();
2992
+ });
2993
+ };
2994
+ editor.children.forEach((node, index) => {
2995
+ path.push(index);
2996
+ visitFast(node);
2997
+ path.pop();
2998
+ });
2999
+ if (updates.length === 0) return;
3000
+ editor.tf.withoutSaving(() => {
3001
+ editor.tf.setNodesBatch(updates);
3002
+ });
3003
+ return;
3004
+ }
2880
3005
  const addNodeId = (entry) => {
2881
3006
  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
- }
3007
+ if (shouldAssignNodeId(entry, {
3008
+ ...options,
3009
+ isBlock: editor.api.isBlock
3010
+ })) updates.push({
3011
+ at: path,
3012
+ props: { [idKey]: idCreator() }
3013
+ });
2897
3014
  if (ElementApi.isElement(node)) node.children.forEach((child, index) => {
2898
3015
  addNodeId([child, [...path, index]]);
2899
3016
  });
@@ -2901,14 +3018,21 @@ const NodeIdPlugin = createTSlatePlugin({
2901
3018
  editor.children.forEach((node, index) => {
2902
3019
  addNodeId([node, [index]]);
2903
3020
  });
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;
3021
+ if (updates.length === 0) return;
3022
+ editor.tf.withoutSaving(() => {
3023
+ editor.tf.setNodesBatch(updates);
3024
+ });
3025
+ } })).extend({ transformInitialValue: ({ editor, getOptions, value }) => {
3026
+ const options = getOptions();
3027
+ const { idKey = "id" } = options;
3028
+ const initialValueIds = resolveInitialValueIds(options);
3029
+ if (initialValueIds === false) return value;
3030
+ if (initialValueIds === "if-needed") {
3031
+ const firstNode = value[0];
3032
+ const lastNode = value.at(-1);
3033
+ if (firstNode?.[idKey] && lastNode?.[idKey]) return value;
2910
3034
  }
2911
- tf.nodeId.normalize();
3035
+ return normalizeNodeIdWithEditor(editor, value, options);
2912
3036
  } }).overrideEditor(withNodeId);
2913
3037
 
2914
3038
  //#endregion
@@ -2957,7 +3081,7 @@ const pipeOnTextChange = (editor, node, text, prevText, operation) => {
2957
3081
  const DEFAULT = {
2958
3082
  handlers: true,
2959
3083
  inject: true,
2960
- normalizeInitialValue: false,
3084
+ transformInitialValue: false,
2961
3085
  render: true
2962
3086
  };
2963
3087
  /**
@@ -2967,29 +3091,43 @@ const DEFAULT = {
2967
3091
  * @param plugin The plugin to check
2968
3092
  * @param isReadOnly Whether the editor is in read-only mode
2969
3093
  * @param feature The feature to check ('render' | 'handlers' | 'inject' |
2970
- * 'normalizeInitialValue')
3094
+ * 'transformInitialValue')
2971
3095
  * @returns True if the feature should be disabled
2972
3096
  */
2973
3097
  const isEditOnly = (readOnly, plugin, feature) => {
2974
3098
  if (!readOnly) return false;
2975
- if (plugin.editOnly === true) return DEFAULT[feature];
2976
- if (typeof plugin.editOnly === "object") return plugin.editOnly[feature] ?? DEFAULT[feature];
3099
+ const resolvedFeature = feature === "normalizeInitialValue" ? "transformInitialValue" : feature;
3100
+ if (plugin.editOnly === true) return DEFAULT[resolvedFeature];
3101
+ if (typeof plugin.editOnly === "object") {
3102
+ if (plugin.editOnly[resolvedFeature] !== void 0) return plugin.editOnly[resolvedFeature];
3103
+ if (resolvedFeature === "transformInitialValue") return plugin.editOnly.normalizeInitialValue ?? DEFAULT.transformInitialValue;
3104
+ return DEFAULT[resolvedFeature];
3105
+ }
2977
3106
  return false;
2978
3107
  };
2979
3108
 
2980
3109
  //#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) => {
3110
+ //#region src/internal/plugin/pipeTransformInitialValue.ts
3111
+ /** Transform initial value from editor plugins before the editor is ready. */
3112
+ const pipeTransformInitialValue = (editor) => {
2984
3113
  const value = editor.meta.isNormalizing;
2985
3114
  editor.meta.isNormalizing = true;
2986
- editor.meta.pluginCache.normalizeInitialValue.forEach((key) => {
3115
+ editor.meta.pluginCache.transformInitialValue.forEach((key) => {
2987
3116
  const p = editor.getPlugin({ key });
2988
- if (isEditOnly(editor.dom.readOnly, p, "normalizeInitialValue")) return;
2989
- p.normalizeInitialValue?.({
3117
+ if (isEditOnly(editor.dom.readOnly, p, "transformInitialValue")) return;
3118
+ if (!p.transformInitialValue && !p.normalizeInitialValue) return;
3119
+ const ctx = {
2990
3120
  ...getEditorPlugin(editor, p),
2991
3121
  value: editor.children
2992
- });
3122
+ };
3123
+ if (p.transformInitialValue) {
3124
+ const nextValue$1 = p.transformInitialValue(ctx);
3125
+ if (nextValue$1 === void 0) throw new Error(`Plugin "${key}" transformInitialValue must return the next value.`);
3126
+ editor.children = nextValue$1;
3127
+ return;
3128
+ }
3129
+ const nextValue = p.normalizeInitialValue?.(ctx);
3130
+ if (nextValue !== void 0) editor.children = nextValue;
2993
3131
  });
2994
3132
  editor.meta.isNormalizing = value;
2995
3133
  };
@@ -3004,7 +3142,7 @@ const init = (editor, { autoSelect, selection, shouldNormalizeEditor, value, onR
3004
3142
  const target = (autoSelect === "start" ? "start" : "end") === "start" ? editor.api.start([]) : editor.api.end([]);
3005
3143
  editor.tf.select(target);
3006
3144
  }
3007
- if (editor.children.length > 0) pipeNormalizeInitialValue(editor);
3145
+ if (editor.children.length > 0) pipeTransformInitialValue(editor);
3008
3146
  if (shouldNormalizeEditor) editor.tf.normalize({ force: true });
3009
3147
  if (onReady) onReady({
3010
3148
  editor,
@@ -3129,6 +3267,21 @@ const setValue = (editor, value) => {
3129
3267
  //#region src/lib/plugins/slate-extension/SlateExtensionPlugin.ts
3130
3268
  const NOOP_ON_NODE_CHANGE = () => {};
3131
3269
  const NOOP_ON_TEXT_CHANGE = () => {};
3270
+ const isElementStateEmpty = (editor, element) => {
3271
+ const props = NodeApi.extractProps(element);
3272
+ return Object.entries(props).every(([key, value]) => {
3273
+ if (key === "type") return true;
3274
+ return editor.meta.pluginCache.node.isMetadataProp.some((pluginKey) => {
3275
+ const plugin = editor.plugins[pluginKey];
3276
+ return plugin.node.isMetadataProp?.({
3277
+ ...getEditorPlugin(editor, plugin),
3278
+ key,
3279
+ node: element,
3280
+ value
3281
+ });
3282
+ });
3283
+ });
3284
+ };
3132
3285
  /** Opinionated extension of slate default behavior. */
3133
3286
  const SlateExtensionPlugin = createTSlatePlugin({
3134
3287
  api: { redecorate: () => {} },
@@ -3137,7 +3290,7 @@ const SlateExtensionPlugin = createTSlatePlugin({
3137
3290
  onNodeChange: NOOP_ON_NODE_CHANGE,
3138
3291
  onTextChange: NOOP_ON_TEXT_CHANGE
3139
3292
  }
3140
- }).extendEditorTransforms(({ editor, getOption, tf }) => {
3293
+ }).extendEditorApi(({ editor }) => ({ isElementStateEmpty: bindFirst(isElementStateEmpty, editor) })).extendEditorTransforms(({ editor, getOption, tf }) => {
3141
3294
  const apply = tf?.apply ?? editor.tf.apply;
3142
3295
  return {
3143
3296
  init: bindFirst(init, editor),
@@ -3671,4 +3824,4 @@ const withSlate = (e, { id, affinity = true, autoSelect, chunking = true, maxLen
3671
3824
  const createSlateEditor = ({ editor = createEditor(), ...options } = {}) => withSlate(editor, options);
3672
3825
 
3673
3826
  //#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 };
3827
+ export { isLastNonEmptyTextOfInlineFormattingContext as $, matchDelimitedInline as $t, parseHtmlDocument as A, isSlateNode as At, pluginDeserializeHtml as B, withMergeRules as Bt, clearNavigationFeedbackTarget as C, keyToDataAttribute as Ct, NavigationFeedbackPluginKey as D, isSlateEditor as Dt, NAVIGATION_FEEDBACK_KEY as E, getSlateElements as Et, htmlTextNodeToString as F, isSlateVoid as Ft, collapseWhiteSpace as G, withPlateHistory as Gt, htmlBrToNewLine as H, withBreakRules as Ht, htmlElementToLeaf as I, applyDeepToNodes as It, collapseWhiteSpaceChildren as J, createBlockStartInputRule as Jt, collapseWhiteSpaceElement as K, AstPlugin as Kt, pipeDeserializeHtmlLeaf as L, OverridePlugin as Lt, htmlStringToDOMNode as M, isSlatePluginNode as Mt, deserializeHtmlElement as N, isSlateString as Nt, LengthPlugin as O, isSlateElement as Ot, deserializeHtmlNode as P, isSlateText as Pt, upsertInlineFormattingContext as Q, matchBlockStart as Qt, htmlElementToElement as R, withOverrides as Rt, navigate as S, getNodeDataAttributeKeys as St, resolveNavigationFeedbackTarget as T, getInjectMatch as Tt, htmlBodyToFragment as U, BaseParagraphPlugin as Ut, getDataNodeProps as V, withDeleteRules as Vt, deserializeHtmlNodeChildren as W, HistoryPlugin as Wt, collapseWhiteSpaceText as X, createTextSubstitutionInputRule as Xt, collapseWhiteSpaceNode as Y, createMarkInputRule as Yt, endInlineFormattingContext as Z, matchBlockFence as Zt, NodeIdPlugin as _, getMarkBoundaryAffinity as _t, pipeInsertDataQuery as a, getPluginType as an, isHtmlElement as at, withNodeId as b, getSlateClass as bt, isElementStateEmpty as c, getEditorPlugin as cn, withScrolling as ct, liftBlock as d, ChunkingPlugin as dt, defineInputRule as en, collapseString as et, insertExitBreak as f, withChunking as ft, pipeOnNodeChange as g, isNodesAffinity as gt, pipeOnTextChange as h, isNodeAffinity as ht, ParserPlugin as i, getPluginKeys as in, isHtmlText as it, deserializeHtml as j, isSlatePluginElement as jt, HtmlPlugin as k, isSlateLeaf as kt, setValue as l, createSlatePlugin as ln, DebugPlugin as lt, isEditOnly as m, setAffinitySelection as mt, withSlate as n, getPluginByType as nn, isHtmlInlineElement as nt, normalizeDescendantsToDocumentFragment as o, getPluginTypes as on, AUTO_SCROLL as ot, init as p, AffinityPlugin as pt, inferWhiteSpaceRule as q, createBlockFenceInputRule as qt, getCorePlugins as r, getPluginKey as rn, inlineTagNames as rt, SlateExtensionPlugin as s, getSlatePlugin as sn, DOMPlugin as st, createSlateEditor as t, getContainerTypes as tn, isHtmlBlockElement as tt, resetBlock as u, createTSlatePlugin as un, PlateError as ut, normalizeNodeId as v, getEdgeNodes as vt, flashTarget as w, getInjectedPlugins as wt, NavigationFeedbackPlugin as x, getPluginNodeProps as xt, normalizeNodeIdWithEditor as y, mergeDeepToNodes as yt, pipeDeserializeHtmlElement as z, withNormalizeRules 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.2.1",
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"