UNPKG

@udecode/plate-core

Version:

The core of Plate – a plugin system for slate

1,658 lines (1,603 loc) 103 kB
// src/lib/editor/withSlate.ts import { createEditor } from "@udecode/slate"; import { nanoid } from "nanoid"; // src/internal/utils/isFunction.ts function isFunction(value) { return typeof value === "function"; } // src/internal/utils/mergePlugins.ts import mergeWith from "lodash/mergeWith.js"; function mergePlugins(basePlugin, ...sourcePlugins) { return mergeWith( {}, basePlugin, ...sourcePlugins, (objValue, srcValue, key) => { if (Array.isArray(srcValue)) { return srcValue; } if (key === "options") { return { ...objValue, ...srcValue }; } } ); } // src/lib/plugin/createSlatePlugin.ts function createSlatePlugin(config = {}) { let baseConfig; let initialExtension; if (isFunction(config)) { baseConfig = { key: "" }; initialExtension = (editor) => config(editor); } else { baseConfig = config; } const key = baseConfig.key ?? ""; const plugin = mergePlugins( { key, __apiExtensions: [], __configuration: null, __extensions: initialExtension ? [initialExtension] : [], __selectorExtensions: [], api: {}, dependencies: [], editor: {}, handlers: {}, inject: {}, node: { type: key }, options: {}, override: {}, parser: {}, parsers: {}, plugins: [], priority: 100, render: {}, shortcuts: {}, transforms: {} }, config ); plugin.configure = (config2) => { const newPlugin = { ...plugin }; newPlugin.__configuration = (ctx) => isFunction(config2) ? config2(ctx) : config2; return createSlatePlugin(newPlugin); }; plugin.configurePlugin = (p, config2) => { const newPlugin = { ...plugin }; const configureNestedPlugin = (plugins) => { let found = false; const updatedPlugins = plugins.map((nestedPlugin) => { if (nestedPlugin.key === p.key) { found = true; return createSlatePlugin({ ...nestedPlugin, __configuration: (ctx) => isFunction(config2) ? config2(ctx) : config2 }); } if (nestedPlugin.plugins && nestedPlugin.plugins.length > 0) { const result2 = configureNestedPlugin(nestedPlugin.plugins); if (result2.found) { found = true; return { ...nestedPlugin, plugins: result2.plugins }; } } return nestedPlugin; }); return { found, plugins: updatedPlugins }; }; const result = configureNestedPlugin(newPlugin.plugins); newPlugin.plugins = result.plugins; return createSlatePlugin(newPlugin); }; plugin.extendEditorApi = (extension) => { const newPlugin = { ...plugin }; newPlugin.__apiExtensions = [ ...newPlugin.__apiExtensions, { extension, isPluginSpecific: false } ]; return createSlatePlugin(newPlugin); }; plugin.extendSelectors = (extension) => { const newPlugin = { ...plugin }; newPlugin.__selectorExtensions = [ ...newPlugin.__selectorExtensions, extension ]; return createSlatePlugin(newPlugin); }; plugin.extendApi = (extension) => { const newPlugin = { ...plugin }; newPlugin.__apiExtensions = [ ...newPlugin.__apiExtensions, { extension, isPluginSpecific: true } ]; return createSlatePlugin(newPlugin); }; plugin.extendEditorTransforms = (extension) => { const newPlugin = { ...plugin }; newPlugin.__apiExtensions = [ ...newPlugin.__apiExtensions, { extension, isPluginSpecific: false, isTransform: true } ]; return createSlatePlugin(newPlugin); }; plugin.extendTransforms = (extension) => { const newPlugin = { ...plugin }; newPlugin.__apiExtensions = [ ...newPlugin.__apiExtensions, { extension, isPluginSpecific: true, isTransform: true } ]; return createSlatePlugin(newPlugin); }; plugin.overrideEditor = (extension) => { const newPlugin = { ...plugin }; newPlugin.__apiExtensions = [ ...newPlugin.__apiExtensions, { extension, isOverride: true, isPluginSpecific: false, isTransform: true } ]; return createSlatePlugin(newPlugin); }; plugin.extend = (extendConfig) => { let newPlugin = { ...plugin }; if (isFunction(extendConfig)) { newPlugin.__extensions = [ ...newPlugin.__extensions, extendConfig ]; } else { newPlugin = mergePlugins(newPlugin, extendConfig); } return createSlatePlugin(newPlugin); }; plugin.clone = () => mergePlugins(plugin); plugin.extendPlugin = (p, extendConfig) => { const newPlugin = { ...plugin }; const extendNestedPlugin = (plugins) => { let found = false; const updatedPlugins = plugins.map((nestedPlugin) => { if (nestedPlugin.key === p.key) { found = true; return createSlatePlugin({ ...nestedPlugin, __extensions: [ ...nestedPlugin.__extensions, (ctx) => isFunction(extendConfig) ? extendConfig(ctx) : extendConfig ] }); } if (nestedPlugin.plugins && nestedPlugin.plugins.length > 0) { const result2 = extendNestedPlugin(nestedPlugin.plugins); if (result2.found) { found = true; return { ...nestedPlugin, plugins: result2.plugins }; } } return nestedPlugin; }); return { found, plugins: updatedPlugins }; }; const result = extendNestedPlugin(newPlugin.plugins); newPlugin.plugins = result.plugins; if (!result.found) { newPlugin.plugins.push( createSlatePlugin({ key: p.key, __extensions: [ (ctx) => isFunction(extendConfig) ? extendConfig(ctx) : extendConfig ] }) ); } return createSlatePlugin(newPlugin); }; return plugin; } function createTSlatePlugin(config = {}) { return createSlatePlugin(config); } // src/lib/plugin/getEditorPlugin.ts function getEditorPlugin(editor, p) { const plugin = editor.getPlugin(p); return { api: editor.api, editor, plugin, setOption: (keyOrOptions, value) => editor.setOption(plugin, keyOrOptions, value), setOptions: (options) => editor.setOptions(plugin, options), tf: editor.transforms, type: plugin.node.type, getOption: (key, ...args) => editor.getOption(plugin, key, ...args), getOptions: () => editor.getOptions(plugin) }; } // src/internal/plugin/resolvePlugin.ts import merge from "lodash/merge.js"; var resolvePlugin = (editor, _plugin) => { let plugin = mergePlugins({}, _plugin); plugin.__resolved = true; if (plugin.__configuration) { const configResult = plugin.__configuration( getEditorPlugin(editor, plugin) ); plugin = mergePlugins(plugin, configResult); delete plugin.__configuration; } if (plugin.__extensions && plugin.__extensions.length > 0) { plugin.__extensions.forEach((extension) => { plugin = mergePlugins( plugin, extension(getEditorPlugin(editor, plugin)) ); }); plugin.__extensions = []; } const targetPluginToInject = plugin.inject?.targetPluginToInject; const targetPlugins = plugin.inject?.targetPlugins; if (targetPluginToInject && targetPlugins && targetPlugins.length > 0) { plugin.inject = plugin.inject || {}; plugin.inject.plugins = merge( {}, plugin.inject.plugins, Object.fromEntries( targetPlugins.map((targetPlugin) => { const injectedPlugin = targetPluginToInject({ ...getEditorPlugin(editor, plugin), targetPlugin }); return [targetPlugin, injectedPlugin]; }) ) ); } if (plugin.node?.component) { plugin.render.node = plugin.node.component; } if (plugin.render?.node) { plugin.node.component = plugin.render.node; } validatePlugin(editor, plugin); return plugin; }; var validatePlugin = (editor, plugin) => { if (!plugin.__extensions) { editor.api.debug.error( `Invalid plugin '${plugin.key}', you should use createSlatePlugin.`, "USE_CREATE_PLUGIN" ); } if (plugin.node.isElement && plugin.node.isLeaf) { editor.api.debug.error( `Plugin ${plugin.key} cannot be both an element and a leaf.`, "PLUGIN_NODE_TYPE" ); } }; // src/lib/plugin/getSlatePlugin.ts function getSlatePlugin(editor, p) { let plugin = p; const editorPlugin = editor.plugins[p.key]; if (!editorPlugin) { if (!plugin.node) { plugin = createSlatePlugin(plugin); } return plugin.__resolved ? plugin : resolvePlugin(editor, plugin); } return editorPlugin; } function getPluginType(editor, plugin) { const p = editor.getPlugin(plugin); return p.node.type ?? p.key ?? ""; } var getPluginTypes = (editor, plugins) => plugins.map((plugin) => editor.getType(plugin)); // src/internal/plugin/pipeNormalizeInitialValue.ts var pipeNormalizeInitialValue = (editor) => { editor.pluginList.forEach((p) => { p.normalizeInitialValue?.({ ...getEditorPlugin(editor, p), value: editor.children }); }); }; // src/internal/plugin/resolvePlugins.ts import { assignLegacyApi, assignLegacyTransforms, syncLegacyMethods } from "@udecode/slate"; import { isDefined } from "@udecode/utils"; import merge2 from "lodash/merge.js"; import { createZustandStore } from "zustand-x"; var resolvePlugins = (editor, plugins = []) => { editor.pluginList = []; editor.plugins = {}; editor.shortcuts = {}; const resolvedPlugins = resolveAndSortPlugins(editor, plugins); applyPluginsToEditor(editor, resolvedPlugins); resolvePluginOverrides(editor); resolvePluginStores(editor); editor.pluginList.forEach((plugin) => { if (plugin.extendEditor) { editor = plugin.extendEditor(getEditorPlugin(editor, plugin)); syncLegacyMethods(editor); } resolvePluginMethods(editor, plugin); }); resolvePluginShortcuts(editor); return editor; }; var resolvePluginStores = (editor) => { editor.pluginList.forEach((plugin) => { let store = createZustandStore(plugin.options, { mutative: true, name: plugin.key }); if (plugin.__selectorExtensions && plugin.__selectorExtensions.length > 0) { plugin.__selectorExtensions.forEach((extension) => { const extendedOptions = extension(getEditorPlugin(editor, plugin)); store = store.extendSelectors(() => extendedOptions); }); } plugin.optionsStore = store; }); }; var resolvePluginMethods = (editor, plugin) => { Object.entries(plugin.api).forEach(([apiKey, apiFunction]) => { editor.api[apiKey] = apiFunction; }); if (plugin.__apiExtensions && plugin.__apiExtensions.length > 0) { plugin.__apiExtensions.forEach( ({ extension, isOverride, isPluginSpecific, isTransform }) => { const newExtensions = extension(getEditorPlugin(editor, plugin)); if (isOverride) { if (newExtensions.api) { merge2(editor.api, newExtensions.api); merge2(plugin.api, newExtensions.api); assignLegacyApi(editor, editor.api); } if (newExtensions.transforms) { merge2(editor.transforms, newExtensions.transforms); merge2(plugin.transforms, newExtensions.transforms); assignLegacyTransforms(editor, newExtensions.transforms); } } else if (isTransform) { if (isPluginSpecific) { if (!editor.transforms[plugin.key]) { editor.transforms[plugin.key] = {}; } if (!plugin.transforms[plugin.key]) { plugin.transforms[plugin.key] = {}; } merge2(editor.transforms[plugin.key], newExtensions); merge2(plugin.transforms[plugin.key], newExtensions); } else { merge2(editor.transforms, newExtensions); merge2(plugin.transforms, newExtensions); assignLegacyTransforms(editor, newExtensions); } } else { if (isPluginSpecific) { if (!editor.api[plugin.key]) { editor.api[plugin.key] = {}; } if (!plugin.api[plugin.key]) { plugin.api[plugin.key] = {}; } merge2(editor.api[plugin.key], newExtensions); merge2(plugin.api[plugin.key], newExtensions); } else { merge2(editor.api, newExtensions); merge2(plugin.api, newExtensions); assignLegacyApi(editor, editor.api); } } } ); delete plugin.__apiExtensions; } }; var resolvePluginShortcuts = (editor) => { const shortcutsByPriority = []; editor.pluginList.forEach((plugin) => { Object.entries(plugin.shortcuts).forEach(([key, hotkey]) => { if (hotkey === null) { const index = shortcutsByPriority.findIndex((item) => item.key === key); if (index !== -1) { shortcutsByPriority.splice(index, 1); } } else { const priority = hotkey.priority ?? plugin.priority; const existingIndex = shortcutsByPriority.findIndex( (item) => item.key === key ); if (existingIndex === -1 || priority >= shortcutsByPriority[existingIndex].priority) { if (existingIndex !== -1) { shortcutsByPriority.splice(existingIndex, 1); } shortcutsByPriority.push({ key, hotkey, priority }); } } }); }); shortcutsByPriority.sort((a, b) => b.hotkey.priority - a.hotkey.priority); editor.shortcuts = Object.fromEntries( shortcutsByPriority.map(({ key, hotkey }) => { const { priority, ...hotkeyWithoutPriority } = hotkey; return [key, hotkeyWithoutPriority]; }) ); }; var flattenAndResolvePlugins = (editor, plugins) => { const pluginMap = /* @__PURE__ */ new Map(); const processPlugin = (plugin) => { const resolvedPlugin = resolvePlugin(editor, plugin); const existingPlugin = pluginMap.get(resolvedPlugin.key); if (existingPlugin) { pluginMap.set( resolvedPlugin.key, mergePlugins(existingPlugin, resolvedPlugin) ); } else { pluginMap.set(resolvedPlugin.key, resolvedPlugin); } if (resolvedPlugin.plugins && resolvedPlugin.plugins.length > 0) { resolvedPlugin.plugins.forEach(processPlugin); } }; plugins.forEach(processPlugin); return pluginMap; }; var resolveAndSortPlugins = (editor, plugins) => { const pluginMap = flattenAndResolvePlugins(editor, plugins); const enabledPlugins = Array.from(pluginMap.values()).filter( (plugin) => plugin.enabled !== false ); enabledPlugins.sort((a, b) => b.priority - a.priority); const orderedPlugins = []; const visited = /* @__PURE__ */ new Set(); const visit = (plugin) => { if (visited.has(plugin.key)) return; visited.add(plugin.key); plugin.dependencies?.forEach((depKey) => { const depPlugin = pluginMap.get(depKey); if (depPlugin) { visit(depPlugin); } else { editor.api.debug.warn( `Plugin "${plugin.key}" depends on missing plugin "${depKey}"`, "PLUGIN_DEPENDENCY_MISSING" ); } }); orderedPlugins.push(plugin); }; enabledPlugins.forEach(visit); return orderedPlugins; }; var applyPluginsToEditor = (editor, plugins) => { editor.pluginList = plugins; editor.plugins = Object.fromEntries( plugins.map((plugin) => [plugin.key, plugin]) ); }; var resolvePluginOverrides = (editor) => { const applyOverrides = (plugins) => { let overriddenPlugins = [...plugins]; const enabledOverrides = {}; const componentOverrides = {}; const pluginOverrides = {}; for (const plugin of plugins) { if (plugin.override.enabled) { Object.assign(enabledOverrides, plugin.override.enabled); } if (plugin.override.components) { Object.entries(plugin.override.components).forEach( ([key, component]) => { if (!componentOverrides[key] || plugin.priority > componentOverrides[key].priority) { componentOverrides[key] = { component, priority: plugin.priority }; } } ); } if (plugin.override.plugins) { Object.entries(plugin.override.plugins).forEach(([key, value]) => { pluginOverrides[key] = mergePlugins(pluginOverrides[key], value); if (value.enabled !== void 0) { enabledOverrides[key] = value.enabled; } }); } } overriddenPlugins = overriddenPlugins.map((p) => { let updatedPlugin = { ...p }; if (pluginOverrides[p.key]) { updatedPlugin = mergePlugins(updatedPlugin, pluginOverrides[p.key]); } if (componentOverrides[p.key] && (!p.render.node && !p.node.component || componentOverrides[p.key].priority > p.priority)) { updatedPlugin.render.node = componentOverrides[p.key].component; updatedPlugin.node.component = componentOverrides[p.key].component; } const enabled = enabledOverrides[p.key] ?? updatedPlugin.enabled; if (isDefined(enabled)) { updatedPlugin.enabled = enabled; } return updatedPlugin; }); return overriddenPlugins.filter((p) => p.enabled !== false).map((plugin) => ({ ...plugin, plugins: applyOverrides(plugin.plugins || []) })); }; editor.pluginList = applyOverrides(editor.pluginList); editor.plugins = Object.fromEntries( editor.pluginList.map((plugin) => [plugin.key, plugin]) ); }; // src/lib/plugins/AstPlugin.ts var AstPlugin = createSlatePlugin({ key: "ast", parser: { format: "application/x-slate-fragment", deserialize: ({ data }) => { const decoded = decodeURIComponent(window.atob(data)); let parsed; try { parsed = JSON.parse(decoded); } catch { } return parsed; } } }); // src/lib/plugins/DOMPlugin.ts var DOMPlugin = createSlatePlugin({ key: "dom" }); // src/lib/plugins/HistoryPlugin.ts import { withHistory } from "@udecode/slate"; var withPlateHistory = ({ editor }) => withHistory(editor); var HistoryPlugin = createSlatePlugin({ key: "history", extendEditor: withPlateHistory }); // src/lib/plugins/InlineVoidPlugin.ts var withInlineVoid = ({ api: { isInline, isSelectable, isVoid, markableVoid }, editor }) => { const voidTypes = []; const inlineTypes = []; const markableVoidTypes = []; const nonSelectableTypes = []; editor.pluginList.forEach((plugin) => { if (plugin.node.isInline) { inlineTypes.push(plugin.node.type); } if (plugin.node.isVoid) { voidTypes.push(plugin.node.type); } if (plugin.node.isMarkableVoid) { markableVoidTypes.push(plugin.node.type); } if (plugin.node.isSelectable === false) { nonSelectableTypes.push(plugin.node.type); } }); return { api: { isInline(element) { return inlineTypes.includes(element.type) ? true : isInline(element); }, isSelectable(element) { return nonSelectableTypes.includes(element.type) ? false : isSelectable(element); }, isVoid(element) { return voidTypes.includes(element.type) ? true : isVoid(element); }, markableVoid(element) { return markableVoidTypes.includes(element.type) ? true : markableVoid(element); } } }; }; var InlineVoidPlugin = createSlatePlugin({ key: "inlineVoid" }).overrideEditor(withInlineVoid); // src/internal/plugin/pipeInsertFragment.ts var pipeInsertFragment = (editor, injectedPlugins, { fragment, ...options }) => { editor.tf.withoutNormalizing(() => { injectedPlugins.some((p) => { return p.parser?.preInsert?.({ ...getEditorPlugin(editor, p), fragment, ...options }) === true; }); editor.tf.insertFragment(fragment); }); }; // src/internal/plugin/pipeTransformData.ts var pipeTransformData = (editor, plugins, { data, dataTransfer }) => { plugins.forEach((p) => { const transformData = p.parser?.transformData; if (!transformData) return; data = transformData({ ...getEditorPlugin(editor, p), data, dataTransfer }); }); return data; }; // src/internal/plugin/pipeTransformFragment.ts var pipeTransformFragment = (editor, plugins, { fragment, ...options }) => { plugins.forEach((p) => { const transformFragment = p.parser?.transformFragment; if (!transformFragment) return; fragment = transformFragment({ fragment, ...options, ...getEditorPlugin(editor, p) }); }); return fragment; }; // src/lib/utils/applyDeepToNodes.ts import { NodeApi, queryNode } from "@udecode/slate"; var applyDeepToNodes = ({ apply, node, path = [], query, source }) => { const entry = [node, path]; if (queryNode(entry, query)) { if (source instanceof Function) { apply(node, source()); } else { apply(node, source); } } if (!NodeApi.isAncestor(node)) return; node.children.forEach((child, index) => { applyDeepToNodes({ apply, node: child, path: path.concat([index]), query, source }); }); }; // src/lib/utils/defaultsDeepToNodes.ts import defaults from "lodash/defaults.js"; var defaultsDeepToNodes = (options) => { applyDeepToNodes({ ...options, apply: defaults }); }; // src/lib/utils/getInjectMatch.ts import { ElementApi } from "@udecode/slate"; // src/lib/utils/getKeysByTypes.ts var getKeysByTypes = (editor, types) => { return Object.values(editor.plugins).filter((plugin) => types.includes(plugin.node.type)).map((plugin) => plugin.key); }; var getKeyByType = (editor, type) => { const plugin = Object.values(editor.plugins).find( (plugin2) => plugin2.node.type === type ); return plugin?.key ?? type; }; // src/lib/utils/getInjectMatch.ts var getInjectMatch = (editor, plugin) => { return (node, path) => { const { inject: { excludeBelowPlugins, excludePlugins, isBlock: _isBlock, isElement: _isElement, isLeaf, maxLevel, targetPlugins } } = plugin; const element = ElementApi.isElement(node) ? node : void 0; if (_isElement && !element) return false; if (_isBlock && (!element || !editor.api.isBlock(element))) return false; if (isLeaf && element) return false; if (element?.type) { if (excludePlugins?.includes(getKeyByType(editor, element.type))) { return false; } if (targetPlugins && !targetPlugins.includes(getKeyByType(editor, element.type))) { return false; } } if (excludeBelowPlugins || maxLevel) { if (maxLevel && path.length > maxLevel) { return false; } if (excludeBelowPlugins) { const excludeTypes = getKeysByTypes(editor, excludeBelowPlugins); const isBelow = editor.api.above({ at: path, match: (n) => ElementApi.isElement(n) && excludeTypes.includes(n.type) }); if (isBelow) return false; } } return true; }; }; // src/lib/utils/getInjectedPlugins.ts var getInjectedPlugins = (editor, plugin) => { const injectedPlugins = []; [...editor.pluginList].reverse().forEach((p) => { const injectedPlugin = p.inject.plugins?.[plugin.key]; if (injectedPlugin) injectedPlugins.push(injectedPlugin); }); return [plugin, ...injectedPlugins]; }; // src/lib/utils/getPluginNodeProps.ts import pick from "lodash/pick.js"; // src/lib/static/pipeRenderElementStatic.tsx import React4 from "react"; // src/lib/static/components/SlateElement.tsx import React from "react"; import clsx from "clsx"; var SlateElement = (props) => { const { as, attributes, element, elementToAttributes, nodeProps, ...rest } = omitPluginContext(props); const block = !!element.id && props.editor.api.isBlock(element); const rootProps = { ...attributes, ...rest, ...nodeProps, ...elementToAttributes?.(element), className: clsx(props.className, nodeProps?.className), "data-block-id": block ? element.id : void 0, style: { position: "relative", ...props.style, ...nodeProps?.style } }; const Element = as ?? "div"; return /* @__PURE__ */ React.createElement(Element, { ...rootProps, ref: attributes.ref }); }; // src/lib/static/pluginRenderElementStatic.tsx import React3 from "react"; // src/lib/static/utils/createStaticString.ts import React2 from "react"; function createStaticString({ text }) { return React2.createElement( "span", { "data-slate-string": true }, text === "" ? "\uFEFF" : text ); } // src/lib/static/utils/getNodeDataAttributes.ts import { TextApi } from "@udecode/slate"; import kebabCase from "lodash/kebabCase.js"; var getNodeDataAttributes = (node, { isElement, isLeaf }) => { const dataAttributes = Object.keys(node).reduce((acc, key) => { if (typeof node[key] === "object") return acc; if (isElement && key === "children") return acc; if (isLeaf && key === "text") return acc; const attributeName = keyToDataAttribute(key); return { ...acc, [attributeName]: node[key] }; }, {}); return dataAttributes; }; var getPluginDataAttributes = (editor, plugin, node) => { const isElement = plugin.node.isElement; const isLeaf = plugin.node.isLeaf; const dataAttributes = getNodeDataAttributes(node, { isElement, isLeaf }); const customAttributes = plugin.node.toDataAttributes?.({ ...plugin ? getEditorPlugin(editor, plugin) : {}, node }) ?? {}; return { ...dataAttributes, ...customAttributes }; }; var getLeafDataAttributes = (leaf) => getNodeDataAttributes(leaf, { isElement: false, isLeaf: true }); var getNodeDataAttributeKeys = (node) => { return Object.keys(node).filter( (key) => typeof node[key] !== "object" && (!TextApi.isText(node) || key !== "text") ).map((key) => keyToDataAttribute(key)); }; var keyToDataAttribute = (key) => { return `data-slate-${kebabCase(key)}`; }; // src/lib/static/utils/getRenderNodeStaticProps.ts import clsx3 from "clsx"; // src/internal/plugin/pipeInjectNodeProps.tsx import clsx2 from "clsx"; // src/internal/plugin/pluginInjectNodeProps.ts import { isDefined as isDefined2 } from "@udecode/utils"; var pluginInjectNodeProps = (editor, plugin, nodeProps, getElementPath) => { const { key, inject: { nodeProps: injectNodeProps } } = plugin; const { element, text } = nodeProps; const node = element ?? text; if (!node) return; if (!injectNodeProps) return; const { classNames, defaultNodeValue, nodeKey = key, query, styleKey = nodeKey, transformClassName, transformNodeValue, transformProps, transformStyle, validNodeValues } = injectNodeProps; const injectMatch = getInjectMatch(editor, plugin); if (!injectMatch(node, getElementPath(node))) return; const queryResult = query?.({ ...injectNodeProps, ...getEditorPlugin(editor, plugin), nodeProps }); if (query && !queryResult) { return; } const nodeValue = node[nodeKey]; if (!transformProps && (!isDefined2(nodeValue) || validNodeValues && !validNodeValues.includes(nodeValue) || nodeValue === defaultNodeValue)) { return; } const transformOptions = { ...nodeProps, ...getEditorPlugin(editor, plugin), nodeValue }; const value = transformNodeValue?.(transformOptions) ?? nodeValue; transformOptions.value = value; let newProps = {}; if (element && nodeKey) { newProps.className = `slate-${nodeKey}-${nodeValue}`; } if (classNames?.[nodeValue] || transformClassName) { newProps.className = transformClassName?.(transformOptions) ?? classNames?.[value]; } if (styleKey) { newProps.style = transformStyle?.(transformOptions) ?? { [styleKey]: value }; } if (transformProps) { newProps = transformProps({ ...transformOptions, props: newProps }) ?? newProps; } return newProps; }; // src/internal/plugin/pipeInjectNodeProps.tsx var pipeInjectNodeProps = (editor, nodeProps, getElementPath) => { editor.pluginList.forEach((plugin) => { if (plugin.inject.nodeProps) { const newProps = pluginInjectNodeProps( editor, plugin, nodeProps, getElementPath ); if (!newProps) return; nodeProps = { ...nodeProps, ...newProps, className: clsx2(nodeProps.className, newProps.className), style: { ...nodeProps.style, ...newProps.style } }; } }); return nodeProps; }; // src/lib/static/utils/getRenderNodeStaticProps.ts var getRenderNodeStaticProps = ({ attributes, editor, node, plugin, props }) => { let nodeProps = { ...props, ...plugin ? getEditorPlugin(editor, plugin) : {} }; const { className } = props; nodeProps = { ...getPluginNodeProps({ attributes, node, plugin, props: nodeProps }), className: clsx3(getSlateClass(plugin?.node.type), className) }; nodeProps = pipeInjectNodeProps( editor, nodeProps, (node2) => editor.api.findPath(node2) ); if (nodeProps.style && Object.keys(nodeProps.style).length === 0) { delete nodeProps.style; } return nodeProps; }; // src/lib/static/utils/pipeDecorate.ts var pipeDecorate = (editor, decorateProp) => { const relevantPlugins = editor.pluginList.filter((plugin) => plugin.decorate); if (relevantPlugins.length === 0 && !decorateProp) return; return (entry) => { let ranges = []; const addRanges = (newRanges) => { if (newRanges?.length) ranges = [...ranges, ...newRanges]; }; relevantPlugins.forEach((plugin) => { addRanges( plugin.decorate({ ...getEditorPlugin(editor, plugin), entry }) ); }); if (decorateProp) { addRanges( decorateProp({ editor, entry }) ); } return ranges; }; }; // src/lib/static/utils/stripHtmlClassNames.ts var classAttrRegExp = / class="([^"]*)"/g; var stripHtmlClassNames = (html, { preserveClassNames = ["slate-"] }) => { if (preserveClassNames.length === 0) { return html.replaceAll(classAttrRegExp, ""); } const preserveRegExp = new RegExp( preserveClassNames.map((cn) => `^${cn}`).join("|") ); return html.replaceAll( classAttrRegExp, (match, className) => { const classesToKeep = className.split(/\s+/).filter((cn) => preserveRegExp.test(cn)); return classesToKeep.length === 0 ? "" : ` class="${classesToKeep.join(" ")}"`; } ); }; // src/lib/static/utils/stripSlateDataAttributes.ts var stripSlateDataAttributes = (rawHtml) => rawHtml.replaceAll(/ data-slate(?:-node|-type|-leaf|-string)="[^"]+"/g, "").replaceAll(/ data-testid="[^"]+"/g, ""); // src/lib/static/pluginRenderElementStatic.tsx var pluginRenderElementStatic = (editor, plugin, components) => function render(nodeProps) { if (nodeProps.element.type === plugin.node.type) { const element = nodeProps.element; const key = plugin.key; const Element = components?.[plugin.key] ?? SlateElement; let { children } = nodeProps; const aboveNodes = editor.pluginList.flatMap( (o) => o.render?.aboveNodes ?? [] ); const belowNodes = editor.pluginList.flatMap( (o) => o.render?.belowNodes ?? [] ); const dataAttributes = getPluginDataAttributes(editor, plugin, element); nodeProps = getRenderNodeStaticProps({ attributes: { ...element.attributes, ...dataAttributes }, editor, node: element, plugin, props: nodeProps }); belowNodes.forEach((withHOC) => { const hoc = withHOC({ ...nodeProps, key }); if (hoc) { children = hoc({ ...nodeProps, children }); } }); let component = /* @__PURE__ */ React3.createElement(Element, { ...nodeProps }, children); aboveNodes.forEach((withHOC) => { const hoc = withHOC({ ...nodeProps, key }); if (hoc) { component = hoc({ ...nodeProps, children: component }); } }); return component; } }; // src/lib/static/pipeRenderElementStatic.tsx var pipeRenderElementStatic = (editor, { components, renderElement: renderElementProp }) => { const renderElements = []; editor.pluginList.forEach((plugin) => { if (plugin.node.isElement) { renderElements.push( pluginRenderElementStatic(editor, plugin, components) ); } }); return function render(props) { let element; renderElements.some((renderElement) => { element = renderElement(props); return !!element; }); if (element) return element; if (renderElementProp) { return renderElementProp(props); } return /* @__PURE__ */ React4.createElement( SlateElement, { attributes: props.attributes, element: props.element, ...{} }, props.children ); }; }; // src/lib/static/pluginRenderLeafStatic.tsx import React6 from "react"; // src/lib/static/components/SlateLeaf.tsx import React5 from "react"; import clsx4 from "clsx"; function SlateLeaf(props) { const { as, attributes, leaf, leafToAttributes, nodeProps, text, ...rest } = omitPluginContext(props); const rootProps = { ...attributes, ...rest, ...nodeProps, ...leafToAttributes?.(leaf), className: clsx4(props.className, nodeProps?.className) }; const Leaf = as ?? "span"; return /* @__PURE__ */ React5.createElement(Leaf, { ...rootProps }); } // src/lib/static/pluginRenderLeafStatic.tsx var pluginRenderLeafStatic = (editor, plugin, components) => function render(nodeProps) { const { children, leaf } = nodeProps; if (leaf[plugin.node.type ?? plugin.key]) { const Leaf = components?.[plugin.key] ?? SlateLeaf; const dataAttributes = getPluginDataAttributes(editor, plugin, leaf); const ctxProps = getRenderNodeStaticProps({ attributes: { ...leaf.attributes, ...dataAttributes }, editor, node: leaf, plugin, props: nodeProps }); return /* @__PURE__ */ React6.createElement(Leaf, { ...ctxProps }, children); } return children; }; var pipeRenderLeafStatic = (editor, { components, renderLeaf: renderLeafProp }) => { const renderLeafs = []; editor.pluginList.forEach((plugin) => { if (plugin.node.isLeaf && plugin.key) { renderLeafs.push(pluginRenderLeafStatic(editor, plugin, components)); } }); return function render(props) { renderLeafs.forEach((renderLeaf) => { const newChildren = renderLeaf(props); if (newChildren !== void 0) { props.children = newChildren; } }); if (renderLeafProp) { return renderLeafProp(props); } const ctxProps = getRenderNodeStaticProps({ attributes: props.attributes, editor, props }); const leaf = ctxProps.leaf; const dataAttributes = getLeafDataAttributes(leaf); return /* @__PURE__ */ React6.createElement(SlateLeaf, { ...ctxProps, ...dataAttributes }); }; }; // src/lib/static/serializeHtml.tsx import React8 from "react"; import { decode } from "html-entities"; // src/lib/static/components/PlateStatic.tsx import React7 from "react"; import { ElementApi as ElementApi2, isElementDecorationsEqual, isTextDecorationsEqual, RangeApi, TextApi as TextApi2 } from "@udecode/slate"; import clsx5 from "clsx"; function BaseElementStatic({ components, decorate, decorations, editor, element = { children: [], type: "" } }) { const renderElement = pipeRenderElementStatic(editor, { components }); const attributes = { "data-slate-node": "element", ref: null }; let children = /* @__PURE__ */ React7.createElement( Children, { components, decorate, decorations, editor }, element.children ); if (editor.api.isVoid(element)) { attributes["data-slate-void"] = true; children = /* @__PURE__ */ React7.createElement( "span", { style: { color: "transparent", height: "0", outline: "none", position: "absolute" }, "data-slate-spacer": true }, /* @__PURE__ */ React7.createElement( Children, { components, decorate, decorations, editor }, element.children ) ); } if (editor.api.isInline(element)) { attributes["data-slate-inline"] = true; } return /* @__PURE__ */ React7.createElement(React7.Fragment, null, renderElement?.({ attributes, children, element })); } var ElementStatic = React7.memo(BaseElementStatic, (prev, next) => { return (prev.element === next.element || prev.element._memo !== void 0 && prev.element._memo === next.element._memo) && isElementDecorationsEqual(prev.decorations, next.decorations); }); function BaseLeafStatic({ components, decorations, editor, leaf = { text: "" } }) { const renderLeaf = pipeRenderLeafStatic(editor, { components }); const leaves = TextApi2.decorations(leaf, decorations); return /* @__PURE__ */ React7.createElement("span", { "data-slate-node": "text" }, leaves.map((l, index) => { const leafElement = renderLeaf({ attributes: { "data-slate-leaf": true }, children: /* @__PURE__ */ React7.createElement("span", { "data-slate-string": true }, l.text === "" ? "\uFEFF" : l.text), leaf: l, text: l }); return /* @__PURE__ */ React7.createElement(React7.Fragment, { key: index }, leafElement); })); } var LeafStatic = React7.memo(BaseLeafStatic, (prev, next) => { return ( // prev.leaf === next.leaf && TextApi2.equals(next.leaf, prev.leaf) && isTextDecorationsEqual(next.decorations, prev.decorations) ); }); var defaultDecorate = () => []; function Children({ children = [], components, decorate = defaultDecorate, decorations = [], editor }) { return /* @__PURE__ */ React7.createElement(React7.Fragment, null, children.map((child, i) => { const p = editor.api.findPath(child); let ds = []; if (p) { const range = editor.api.range(p); ds = decorate([child, p]); for (const dec of decorations) { const d = RangeApi.intersection(dec, range); if (d) { ds.push(d); } } } return ElementApi2.isElement(child) ? /* @__PURE__ */ React7.createElement( ElementStatic, { key: i, components, decorate, decorations: ds, editor, element: child } ) : /* @__PURE__ */ React7.createElement( LeafStatic, { key: i, components, decorations: ds, editor, leaf: child } ); })); } function PlateStatic(props) { const { className, components, editor, value, ...rest } = props; if (value) { editor.children = value; } const decorate = pipeDecorate(editor); let afterEditable = null; let beforeEditable = null; editor.pluginList.forEach((plugin) => { const { render: { afterEditable: AfterEditable, beforeEditable: BeforeEditable } } = plugin; if (AfterEditable) { afterEditable = /* @__PURE__ */ React7.createElement(React7.Fragment, null, afterEditable, /* @__PURE__ */ React7.createElement(AfterEditable, null)); } if (BeforeEditable) { beforeEditable = /* @__PURE__ */ React7.createElement(React7.Fragment, null, beforeEditable, /* @__PURE__ */ React7.createElement(BeforeEditable, null)); } }); const content = /* @__PURE__ */ React7.createElement( "div", { className: clsx5("slate-editor", className), "data-slate-editor": true, "data-slate-node": "value", ...rest }, /* @__PURE__ */ React7.createElement( Children, { components, decorate, decorations: [], editor }, editor.children ) ); let aboveEditable = /* @__PURE__ */ React7.createElement(React7.Fragment, null, beforeEditable, content, afterEditable); editor.pluginList.forEach((plugin) => { const { render: { aboveEditable: AboveEditable } } = plugin; if (AboveEditable) { aboveEditable = /* @__PURE__ */ React7.createElement(AboveEditable, null, aboveEditable); } }); return aboveEditable; } // src/lib/static/serializeHtml.tsx var getReactDOMServer = async () => { const ReactDOMServer = (await import("react-dom/server")).default; return ReactDOMServer; }; var renderComponentToHtml = (ReactDOMServer, Component, props) => { return decode( ReactDOMServer.renderToStaticMarkup(React8.createElement(Component, props)) ); }; var serializeHtml = async (editor, { components, editorComponent: EditorComponent = PlateStatic, preserveClassNames, props = {}, stripClassNames = false, stripDataAttributes = false }) => { const ReactDOMServer = await getReactDOMServer(); let htmlString = renderComponentToHtml(ReactDOMServer, EditorComponent, { components, editor, ...props }); if (stripClassNames) { htmlString = stripHtmlClassNames(htmlString, { preserveClassNames }); } if (stripDataAttributes) { htmlString = stripSlateDataAttributes(htmlString); } return htmlString; }; // src/lib/static/deserialize/checkUtils.ts var isSlateVoid = (element) => { return element.dataset.slateVoid === "true"; }; var isSlateElement = (element) => { return element.dataset.slateNode === "element"; }; var isSlateText = (element) => { return element.dataset.slateNode === "text"; }; var isSlateString = (element) => { return element.dataset.slateString === "true"; }; var isSlateLeaf = (element) => { return element.dataset.slateLeaf === "true"; }; var isSlateEditor = (element) => { return element.dataset.slateEditor === "true"; }; var isSlateNode = (element) => { return isSlateLeaf(element) || isSlateElement(element) || isSlateVoid(element) || isSlateString(element) || isSlateText(element); }; var isSlatePluginElement = (element, pluginKey) => { return element.dataset.slateNode === "element" && element.classList.contains(`slate-${pluginKey}`); }; var isSlatePluginNode = (element, pluginKey) => { return element.classList.contains(`slate-${pluginKey}`); }; var getSlateElements = (element) => { return Array.from(element.querySelectorAll('[data-slate-node="element"]')); }; // src/lib/static/deserialize/htmlStringToEditorDOM.ts var getEditorDOMFromHtmlString = (html) => { const node = document.createElement("body"); node.innerHTML = html; const editorNode = node.querySelector('[data-slate-editor="true"]'); return editorNode; }; // src/lib/utils/getPluginNodeProps.ts var getPluginNodeProps = ({ attributes, node, plugin, props }) => { let newProps = {}; if (plugin?.node.props) { newProps = (typeof plugin.node.props === "function" ? plugin.node.props(props) : plugin.node.props) ?? {}; } if (!newProps.nodeProps && attributes && plugin) { newProps.nodeProps = pick(attributes, [ ...plugin.node.dangerouslyAllowAttributes ?? [], ...node ? getNodeDataAttributeKeys(node) : [] ]); } else if (newProps.nodeProps && attributes && plugin) { newProps.nodeProps = { ...newProps.nodeProps, ...pick(attributes, [...node ? getNodeDataAttributeKeys(node) : []]) }; } props = { ...props, ...newProps }; if (props.nodeProps) { Object.keys(props.nodeProps).forEach((key) => { if (props.nodeProps?.[key] === void 0) { delete props.nodeProps?.[key]; } }); } return props; }; // src/lib/utils/getSlateClass.ts var getSlateClass = (type) => type ? `slate-${type}` : ""; // src/lib/utils/hotkeys.ts import { IS_APPLE } from "@udecode/utils"; import { isKeyHotkey } from "is-hotkey"; import { isHotkey } from "is-hotkey"; var HOTKEYS = { bold: "mod+b", compose: ["down", "left", "right", "up", "backspace", "enter"], deleteBackward: "shift?+backspace", deleteForward: "shift?+delete", extendBackward: "shift+left", extendForward: "shift+right", insertSoftBreak: "shift+enter", italic: "mod+i", moveBackward: "left", moveForward: "right", moveWordBackward: "ctrl+left", moveWordForward: "ctrl+right", splitBlock: "enter", tab: "tab", undo: "mod+z", untab: "shift+tab" }; var APPLE_HOTKEYS = { deleteBackward: ["ctrl+backspace", "ctrl+h"], deleteForward: ["ctrl+delete", "ctrl+d"], deleteLineBackward: "cmd+shift?+backspace", deleteLineForward: ["cmd+shift?+delete", "ctrl+k"], deleteWordBackward: "opt+shift?+backspace", deleteWordForward: "opt+shift?+delete", extendLineBackward: "opt+shift+up", extendLineForward: "opt+shift+down", moveLineBackward: "opt+up", moveLineForward: "opt+down", moveWordBackward: "opt+left", moveWordForward: "opt+right", redo: "cmd+shift+z", transposeCharacter: "ctrl+t" }; var WINDOWS_HOTKEYS = { deleteWordBackward: "ctrl+shift?+backspace", deleteWordForward: "ctrl+shift?+delete", redo: ["ctrl+y", "ctrl+shift+z"] }; var createHotkey = (key) => { const generic = HOTKEYS[key]; const apple = APPLE_HOTKEYS[key]; const windows = WINDOWS_HOTKEYS[key]; const isGeneric = generic && isKeyHotkey(generic); const isApple = apple && isKeyHotkey(apple); const isWindows = windows && isKeyHotkey(windows); return (event) => { if (isGeneric?.(event)) return true; if (IS_APPLE && isApple?.(event)) return true; if (!IS_APPLE && isWindows?.(event)) return true; return false; }; }; var createComposing = (key) => (editor, event, { composing } = {}) => { if (!createHotkey(key)(event)) return false; if (!!composing !== editor.api.isComposing()) return false; return true; }; var Hotkeys = { isBold: createHotkey("bold"), isCompose: createHotkey("compose"), isDeleteBackward: createHotkey("deleteBackward"), isDeleteForward: createHotkey("deleteForward"), isDeleteLineBackward: createHotkey("deleteLineBackward"), isDeleteLineForward: createHotkey("deleteLineForward"), isDeleteWordBackward: createHotkey("deleteWordBackward"), isDeleteWordForward: createHotkey("deleteWordForward"), isExtendBackward: createHotkey("extendBackward"), isExtendForward: createHotkey("extendForward"), isExtendLineBackward: createHotkey("extendLineBackward"), isExtendLineForward: createHotkey("extendLineForward"), isItalic: createHotkey("italic"), isMoveBackward: createHotkey("moveBackward"), isMoveForward: createHotkey("moveForward"), isMoveLineBackward: createHotkey("moveLineBackward"), isMoveLineForward: createHotkey("moveLineForward"), isMoveWordBackward: createHotkey("moveWordBackward"), isMoveWordForward: createHotkey("moveWordForward"), isRedo: createHotkey("redo"), isSoftBreak: createHotkey("insertSoftBreak"), isSplitBlock: createHotkey("splitBlock"), isTab: createComposing("tab"), isTransposeCharacter: createHotkey("transposeCharacter"), isUndo: createHotkey("undo"), isUntab: createComposing("untab") }; // src/lib/utils/isType.ts import castArray from "lodash/castArray.js"; var isType = (editor, node, key) => { const keys = castArray(key); const types = []; keys.forEach((_key) => types.push(editor.getType({ key: _key }))); return types.includes(node?.type); }; // src/lib/utils/mergeDeepToNodes.ts import merge3 from "lodash/merge.js"; var mergeDeepToNodes = (options) => { applyDeepToNodes({ ...options, apply: merge3 }); }; // src/lib/utils/normalizeDescendantsToDocumentFragment.ts import { ElementApi as ElementApi4, TextApi as TextApi4 } from "@udecode/slate"; // src/lib/plugins/debug/DebugPlugin.ts var PlateError = class extends Error { constructor(message, type = "DEFAULT") { super(`[${type}] ${message}`); this.type = type; this.name = "PlateError"; } }; var DebugPlugin = createTSlatePlugin({ key: "debug", options: { isProduction: process.env.NODE_ENV === "production", logger: { error: (message, type, details) => console.error(`${type ? `[${type}] ` : ""}${message}`, details), info: (message, type, details) => console.info(`${type ? `[${type}] ` : ""}${message}`, details), log: (message, type, details) => console.log(`${type ? `[${type}] ` : ""}${message}`, details), warn: (message, type, details) => console.warn(`${type ? `[${type}] ` : ""}${message}`, details) }, logLevel: process.env.NODE_ENV === "production" ? "error" : "log", throwErrors: true } }).extendEditorApi(({ getOptions }) => { const logLevels = ["error", "warn", "info", "log"]; const log = (level, message, type, details) => { if (process.env.NODE_ENV === "production") return; const options = getOptions(); if (options.isProduction && level === "log") return; if (logLevels.indexOf(level) <= logLevels.indexOf(options.logLevel)) { if (level === "error" && options.throwErrors) { throw new PlateError(message, type); } else { options.logger[level]?.(message, type, details); } } }; return { debug: { error: (message, type, details) => log("error", message, type, details), info: (message, type, details) => log("info", message, type, details), log: (message, type, details) => log("log", message, type, details), warn: (message, type, details) => log("warn", message, type, details) } }; }); // src/lib/plugins/html/HtmlPlugin.ts import { bindFirst } from "@udecode/utils"; // src/lib/plugins/html/constants.ts var CARRIAGE_RETURN = "\r"; var LINE_FEED = "\n"; var NO_BREAK_SPACE = "\xA0"; var SPACE = " "; var TAB = " "; var ZERO_WIDTH_SPACE = "\u200B"; // src/lib/plugins/html/utils/isHtmlElement.ts var isHtmlEle