UNPKG

@udecode/plate-core

Version:

The core of Plate – a plugin system for slate

1,129 lines (1,108 loc) 67.1 kB
import React$1, { JSX } from 'react'; import * as _udecode_slate from '@udecode/slate'; import { TElement, TText, LeafPosition, NodeEntry, TRange, Editor, DOMRange, Path, DecoratedRange, Value, EditorApi, EditorTransforms, Descendant, ScrollIntoViewOptions, Operation, TSelection, EditorAboveOptions, QueryNodeOptions, EditorBase } from '@udecode/slate'; import { AnyObject, UnknownObject, Nullable, Deep2Partial, OmitFirst, UnionToIntersection } from '@udecode/utils'; import { Draft } from 'mutative'; import { TStateApi } from 'zustand-x'; import { KeyboardEventLike } from 'is-hotkey'; /** If true, the next handlers will be skipped. */ type HandlerReturnType = boolean | void; type RenderElementFn = (props: RenderElementProps) => React$1.ReactElement<any>; interface RenderElementProps<N extends TElement = TElement> { attributes: { 'data-slate-node': 'element'; ref: any; className?: string; 'data-slate-inline'?: true; 'data-slate-void'?: true; dir?: 'rtl'; style?: React$1.CSSProperties; }; children: any; element: N; } type RenderLeafFn = (props: RenderLeafProps) => React.ReactElement<any>; interface RenderLeafProps<N extends TText = TText> { attributes: { className?: string; 'data-slate-leaf'?: true; style?: React.CSSProperties; }; children: any; leaf: N; text: N; /** * The position of the leaf within the Text node, only present when the text * node is split by decorations. */ leafPosition?: LeafPosition; } type RenderTextFn = (props: RenderTextProps) => React$1.ReactElement<any>; interface RenderTextProps<N extends TText = TText> { /** The text node being rendered. */ text: N; /** The children (leaves) rendered within this text node. */ children: any; /** * HTML attributes to be spread onto the rendered container element. Includes * `data-slate-node="text"` and `ref`. */ attributes: { 'data-slate-node': 'text'; ref: any; className?: string; style?: React$1.CSSProperties; }; } /** `EditableProps` are passed to the <Editable> component. */ type EditableProps = { as?: React.ElementType; disableDefaultStyles?: boolean; placeholder?: string; readOnly?: boolean; renderElement?: RenderElementFn; renderLeaf?: RenderLeafFn; renderText?: RenderTextFn; role?: string; style?: React.CSSProperties; decorate?: (entry: NodeEntry) => TRange[]; renderPlaceholder?: (props: { attributes: { contentEditable: boolean; 'data-slate-placeholder': boolean; ref: React.RefCallback<any>; style: React.CSSProperties; dir?: 'rtl'; }; children: any; }) => JSX.Element; scrollSelectionIntoView?: (editor: Editor, domRange: DOMRange) => void; onDOMBeforeInput?: (event: InputEvent) => void; } & React.TextareaHTMLAttributes<HTMLDivElement>; type BoxStaticProps = React.ComponentProps<'div'> & { as?: React.ElementType; }; type SlateRenderElementProps<N extends TElement = TElement, C extends AnyPluginConfig = PluginConfig> = SlateRenderNodeProps<C> & RenderElementProps<N>; type SlateRenderLeafProps<N extends TText = TText, C extends AnyPluginConfig = PluginConfig> = SlateRenderNodeProps<C> & RenderLeafProps<N>; type SlateRenderNodeProps<C extends AnyPluginConfig = PluginConfig> = SlatePluginContext<C> & { attributes?: AnyObject; className?: string; /** @see {@link NodeProps} */ nodeProps?: AnyObject; }; type SlateRenderTextProps<N extends TText = TText, C extends AnyPluginConfig = PluginConfig> = SlateRenderNodeProps<C> & RenderTextProps<N>; declare const useNodeAttributes: (props: any, ref?: any) => any; type SlateElementProps<N extends TElement = TElement, C extends AnyPluginConfig = PluginConfig> = SlateNodeProps<C> & RenderElementProps<N> & { path: Path; } & DeprecatedNodeProps; type DeprecatedNodeProps = { /** * @deprecated Optional class to be merged with `attributes.className`. * @default undefined */ className?: string; /** * @deprecated Optional style to be merged with `attributes.style` * @default undefined */ style?: React$1.CSSProperties; }; type SlateNodeProps<C extends AnyPluginConfig = PluginConfig> = SlatePluginContext<C> & { /** * Optional ref to be merged with `attributes.ref` * * @default undefined */ ref?: any; }; type SlateHTMLProps<C extends AnyPluginConfig = PluginConfig, T extends keyof HTMLElementTagNameMap = 'div'> = SlateNodeProps<C> & { /** HTML attributes to pass to the underlying HTML element */ attributes: React$1.PropsWithoutRef<React$1.JSX.IntrinsicElements[T]> & UnknownObject; as?: T; /** Class to be merged with `attributes.className` */ className?: string; /** Style to be merged with `attributes.style` */ style?: React$1.CSSProperties; }; type StyledSlateElementProps<N extends TElement = TElement, C extends AnyPluginConfig = PluginConfig, T extends keyof HTMLElementTagNameMap = 'div'> = Omit<SlateElementProps<N, C>, keyof DeprecatedNodeProps> & SlateHTMLProps<C, T>; declare const SlateElement: <N extends TElement = TElement, C extends AnyPluginConfig = PluginConfig, T extends keyof HTMLElementTagNameMap = "div">(props: StyledSlateElementProps<N, C, T>) => React$1.ReactElement; type SlateTextProps<N extends TText = TText, C extends AnyPluginConfig = PluginConfig> = SlateNodeProps<C> & RenderTextProps<N> & DeprecatedNodeProps; type StyledSlateTextProps<N extends TText = TText, C extends AnyPluginConfig = PluginConfig, T extends keyof HTMLElementTagNameMap = 'span'> = Omit<SlateTextProps<N, C>, keyof DeprecatedNodeProps> & SlateHTMLProps<C, T>; declare const SlateText: <N extends TText = TText, C extends AnyPluginConfig = PluginConfig, T extends keyof HTMLElementTagNameMap = "span">(props: StyledSlateTextProps<N, C, T>) => React$1.ReactElement; type SlateLeafProps<N extends TText = TText, C extends AnyPluginConfig = PluginConfig> = SlateNodeProps<C> & RenderLeafProps<N> & DeprecatedNodeProps & { inset?: boolean; }; type StyledSlateLeafProps<N extends TText = TText, C extends AnyPluginConfig = PluginConfig, T extends keyof HTMLElementTagNameMap = 'span'> = Omit<SlateLeafProps<N, C>, keyof DeprecatedNodeProps> & SlateHTMLProps<C, T>; declare const SlateLeaf: <N extends TText = TText, C extends AnyPluginConfig = PluginConfig, T extends keyof HTMLElementTagNameMap = "span">({ className, ...props }: StyledSlateLeafProps<N, C, T>) => React$1.ReactElement; type AnyEditorPlugin = EditorPlugin<AnyPluginConfig>; type AnySlatePlugin = SlatePlugin<AnyPluginConfig>; /** * Property used by Plate to decorate editor ranges. If the function returns * undefined then no ranges are modified. If the function returns an array the * returned ranges are merged with the ranges called by other plugins. */ type Decorate<C extends AnyPluginConfig = PluginConfig> = (ctx: SlatePluginContext<C> & { entry: NodeEntry; }) => DecoratedRange[] | undefined; type Deserializer<C extends AnyPluginConfig = PluginConfig> = BaseDeserializer & { parse?: (options: AnyObject & SlatePluginContext<C> & { element: any; }) => Partial<Descendant> | undefined | void; query?: (options: AnyObject & SlatePluginContext<C> & { element: any; }) => boolean; }; type EditorPlugin<C extends AnyPluginConfig = PluginConfig> = Omit<SlatePlugin<C>, keyof SlatePluginMethods | 'override' | 'plugins'>; /** Plate plugin overriding the `editor` methods. Naming convention is `with*`. */ type ExtendEditor<C extends AnyPluginConfig = PluginConfig> = (ctx: SlatePluginContext<C>) => SlateEditor; type ExtendEditorApi<C extends AnyPluginConfig = PluginConfig, EA = {}> = (ctx: SlatePluginContext<C>) => EA & Deep2Partial<EditorApi & CorePluginApi> & { [K in keyof InferApi<C>]?: InferApi<C>[K] extends (...args: any[]) => any ? (...args: Parameters<InferApi<C>[K]>) => ReturnType<InferApi<C>[K]> : InferApi<C>[K] extends Record<string, (...args: any[]) => any> ? { [N in keyof InferApi<C>[K]]?: (...args: Parameters<InferApi<C>[K][N]>) => ReturnType<InferApi<C>[K][N]>; } : never; }; type ExtendEditorTransforms<C extends AnyPluginConfig = PluginConfig, EA = {}> = (ctx: SlatePluginContext<C>) => EA & Deep2Partial<EditorTransforms & CorePluginTransforms> & { [K in keyof InferTransforms<C>]?: InferTransforms<C>[K] extends (...args: any[]) => any ? (...args: Parameters<InferTransforms<C>[K]>) => ReturnType<InferTransforms<C>[K]> : InferTransforms<C>[K] extends Record<string, (...args: any[]) => any> ? { [N in keyof InferTransforms<C>[K]]?: (...args: Parameters<InferTransforms<C>[K][N]>) => ReturnType<InferTransforms<C>[K][N]>; } : never; }; type HtmlDeserializer<C extends AnyPluginConfig = PluginConfig> = BaseHtmlDeserializer & { /** * Whether to disable the default node props parsing logic. By default, all * data-slate-* attributes will be parsed into node props. * * @default false */ disableDefaultNodeProps?: boolean; parse?: (options: SlatePluginContext<C> & { element: HTMLElement; node: AnyObject; }) => Partial<Descendant> | undefined | void; query?: (options: SlatePluginContext<C> & { element: HTMLElement; }) => boolean; toNodeProps?: (options: SlatePluginContext<C> & { element: HTMLElement; }) => Partial<Descendant> | undefined | void; }; type HtmlSerializer<C extends AnyPluginConfig = PluginConfig> = BaseSerializer & { parse?: (options: SlatePluginContext<C> & { node: Descendant; }) => string; query?: (options: SlatePluginContext<C> & { node: Descendant; }) => boolean; }; type InferConfig<P> = P extends SlatePlugin<infer C> ? C : never; type InjectNodeProps<C extends AnyPluginConfig = PluginConfig> = BaseInjectProps & { query?: (options: NonNullable<NonNullable<InjectNodeProps>> & SlatePluginContext<C> & { nodeProps: GetInjectNodePropsOptions; }) => boolean; transformClassName?: (options: TransformOptions<C>) => any; transformNodeValue?: (options: TransformOptions<C>) => any; transformProps?: (options: TransformOptions<C> & { props: GetInjectNodePropsReturnType; }) => AnyObject | undefined; transformStyle?: (options: TransformOptions<C>) => CSSStyleDeclaration; }; type LeafStaticProps<C extends AnyPluginConfig = PluginConfig> = ((props: SlateRenderLeafProps<TText, C>) => AnyObject | undefined) | AnyObject; type NodeStaticProps<C extends AnyPluginConfig = PluginConfig> = ((props: SlateRenderElementProps<TElement, C> & SlateRenderLeafProps<TText, C>) => AnyObject | undefined) | AnyObject; /** @deprecated Use {@link RenderStaticNodeWrapper} instead. */ type NodeStaticWrapperComponent<C extends AnyPluginConfig = PluginConfig> = (props: NodeStaticWrapperComponentProps<C>) => NodeStaticWrapperComponentReturnType<C>; /** @deprecated Use {@link RenderStaticNodeWrapperProps} instead. */ interface NodeStaticWrapperComponentProps<C extends AnyPluginConfig = PluginConfig> extends SlateRenderElementProps<TElement, C> { key: string; } /** @deprecated Use {@link RenderStaticNodeWrapperFunction} instead. */ type NodeStaticWrapperComponentReturnType<C extends AnyPluginConfig = PluginConfig> = React.FC<SlateRenderElementProps<TElement, C>> | undefined; type NormalizeInitialValue<C extends AnyPluginConfig = PluginConfig> = (ctx: SlatePluginContext<C> & { value: Value; }) => void; type OverrideEditor<C extends AnyPluginConfig = PluginConfig> = (ctx: SlatePluginContext<C>) => { api?: Deep2Partial<EditorApi & CorePluginApi> & { [K in keyof InferApi<C>]?: InferApi<C>[K] extends (...args: any[]) => any ? (...args: Parameters<InferApi<C>[K]>) => ReturnType<InferApi<C>[K]> : InferApi<C>[K] extends Record<string, (...args: any[]) => any> ? { [N in keyof InferApi<C>[K]]?: (...args: Parameters<InferApi<C>[K][N]>) => ReturnType<InferApi<C>[K][N]>; } : never; }; transforms?: Deep2Partial<EditorTransforms & CorePluginTransforms> & { [K in keyof InferTransforms<C>]?: InferTransforms<C>[K] extends (...args: any[]) => any ? (...args: Parameters<InferTransforms<C>[K]>) => ReturnType<InferTransforms<C>[K]> : InferTransforms<C>[K] extends Record<string, (...args: any[]) => any> ? { [N in keyof InferTransforms<C>[K]]?: (...args: Parameters<InferTransforms<C>[K][N]>) => ReturnType<InferTransforms<C>[K][N]>; } : never; }; }; type Parser<C extends AnyPluginConfig = PluginConfig> = { format?: string[] | string; mimeTypes?: string[]; deserialize?: (options: ParserOptions & SlatePluginContext<C>) => Descendant[] | undefined; preInsert?: (options: ParserOptions & SlatePluginContext<C> & { fragment: Descendant[]; }) => HandlerReturnType; query?: (options: ParserOptions & SlatePluginContext<C>) => boolean; transformData?: (options: ParserOptions & SlatePluginContext<C>) => string; transformFragment?: (options: ParserOptions & SlatePluginContext<C> & { fragment: Descendant[]; }) => Descendant[]; }; type PartialEditorPlugin<C extends AnyPluginConfig = PluginConfig> = Omit<Partial<EditorPlugin<C>>, 'node'> & { node?: Partial<EditorPlugin<C>['node']>; }; type RenderStaticNodeWrapper<C extends AnyPluginConfig = PluginConfig> = (props: RenderStaticNodeWrapperProps<C>) => RenderStaticNodeWrapperFunction; type RenderStaticNodeWrapperFunction = ((hocProps: SlateRenderElementProps) => React.ReactNode) | undefined; interface RenderStaticNodeWrapperProps<C extends AnyPluginConfig = PluginConfig> extends SlateRenderElementProps<TElement, C> { key: string; } type Serializer<C extends AnyPluginConfig = PluginConfig> = BaseSerializer & { parse?: (options: AnyObject & SlatePluginContext<C> & { node: Descendant; }) => any; query?: (options: AnyObject & SlatePluginContext<C> & { node: Descendant; }) => boolean; }; /** The `PlatePlugin` interface is a base interface for all plugins. */ type SlatePlugin<C extends AnyPluginConfig = PluginConfig> = BasePlugin<C> & Nullable<{ decorate?: Decorate<WithAnyKey<C>>; extendEditor?: ExtendEditor<WithAnyKey<C>>; normalizeInitialValue?: NormalizeInitialValue<WithAnyKey<C>>; }> & SlatePluginMethods<C> & { handlers: Nullable<{}>; inject: Nullable<{ nodeProps?: InjectNodeProps<WithAnyKey<C>>; plugins?: Record<string, PartialEditorPlugin<AnyPluginConfig>>; targetPluginToInject?: (ctx: SlatePluginContext<C> & { targetPlugin: string; }) => Partial<SlatePlugin<AnyPluginConfig>>; }>; node: { /** Override `data-slate-leaf` element attributes */ leafProps?: LeafStaticProps<WithAnyKey<C>>; /** Override node attributes */ props?: NodeStaticProps<WithAnyKey<C>>; /** Override `data-slate-node="text"` element attributes */ textProps?: TextStaticProps<WithAnyKey<C>>; }; override: { components?: NodeComponents; plugins?: Record<string, PartialEditorPlugin<AnyPluginConfig>>; }; parser: Nullable<Parser<WithAnyKey<C>>>; parsers: (Record<string, { deserializer?: Deserializer<WithAnyKey<C>>; serializer?: Serializer<WithAnyKey<C>>; }> & { html?: never; }) | { html?: Nullable<{ deserializer?: HtmlDeserializer<WithAnyKey<C>>; serializer?: HtmlSerializer<WithAnyKey<C>>; }>; }; /** * Recursive plugin support to allow having multiple plugins in a single * plugin. Plate eventually flattens all the plugins into the editor. */ plugins: any[]; render: Nullable<{ /** * When other plugins' `node` components are rendered, this function can * return an optional wrapper function that turns a `node`'s props to a * wrapper React node as its parent. Useful for wrapping or decorating * nodes with additional UI elements. * * NOTE: The function can run React hooks. NOTE: Do not run React hooks * in the wrapper function. It is not equivalent to a React component. */ aboveNodes?: RenderStaticNodeWrapper<WithAnyKey<C>>; /** * When other plugins' `node` components are rendered, this function can * return an optional wrapper function that turns a `node`'s props to a * wrapper React node. The wrapper node is the `node`'s child and its * original children's parent. Useful for wrapping or decorating nodes * with additional UI elements. * * NOTE: The function can run React hooks. NOTE: Do not run React hooks * in the wrapper function. It is not equivalent to a React component. */ belowNodes?: RenderStaticNodeWrapper<WithAnyKey<C>>; /** Renders a component above the main Slate component, as its sibling. */ aboveSlate?: () => React.ReactElement<any> | null; /** Renders a component after the main editor container. */ afterContainer?: () => React.ReactElement<any> | null; /** * Renders a component after the `Editable` component. This is the last * render position within the editor structure. */ afterEditable?: () => React.ReactElement<any> | null; /** Renders a component before the main editor container. */ beforeContainer?: () => React.ReactElement<any> | null; /** Renders a component before the `Editable` component. */ beforeEditable?: () => React.ReactElement<any> | null; /** * Function to render content below the root element but above its * children. Similar to belowNodes but renders directly in the element * rather than wrapping. Multiple plugins can provide this, and all * their content will be rendered in sequence. */ belowRootNodes?: (props: SlateElementProps<TElement, AnySlatePlugin>) => React.ReactNode; }>; rules: { /** * Function to determine if this plugin's rules should apply to a node. * Used to override behavior based on node properties beyond just type * matching. * * Example: List plugin sets `match: ({ node }) => !!node.listStyleType` * to override paragraph behavior when the paragraph is a list item. * * @default type === node.type */ match?: (options: { node: TElement; path: Path; rule: MatchRules; } & SlatePluginContext<C>) => boolean; }; /** * Keyboard shortcuts configuration mapping shortcut names to their key * combinations and handlers. Each shortcut can link to a transform * method, an API method, or use a custom handler function. */ shortcuts: Partial<Record<(string & {}) | Exclude<keyof InferApi<C>[C['key']], keyof InferTransforms<C>[C['key']]> | keyof InferTransforms<C>[C['key']], SlateShortcut | null>>; }; type SlatePluginConfig<K extends string = any, O = {}, A = {}, T = {}, S = {}, EO = {}, EA = {}, ET = {}, ES = {}> = Partial<Omit<SlatePlugin<PluginConfig<K, Partial<O>, A, T, S>>, keyof SlatePluginMethods | 'api' | 'node' | 'optionsStore' | 'transforms'> & { api: EA; node: Partial<SlatePlugin['node']>; options: EO; selectors: ES; transforms: ET; }>; type SlatePluginContext<C extends AnyPluginConfig = PluginConfig> = BasePluginContext<C> & { editor: SlateEditor; plugin: EditorPlugin<C>; }; type SlatePluginMethods<C extends AnyPluginConfig = PluginConfig> = { __apiExtensions: ((ctx: SlatePluginContext<AnyPluginConfig>) => any)[]; __configuration: ((ctx: SlatePluginContext<AnyPluginConfig>) => any) | null; __extensions: ((ctx: SlatePluginContext<AnyPluginConfig>) => any)[]; __selectorExtensions: ((ctx: SlatePluginContext<AnyPluginConfig>) => any)[]; clone: () => SlatePlugin<C>; configure: (config: ((ctx: SlatePluginContext<C>) => SlatePluginConfig<C['key'], InferOptions<C>, InferApi<C>, InferTransforms<C>, InferSelectors<C>>) | SlatePluginConfig<C['key'], InferOptions<C>, InferApi<C>, InferTransforms<C>, InferSelectors<C>>) => SlatePlugin<C>; configurePlugin: <P extends AnySlatePlugin>(plugin: Partial<P>, config: ((ctx: SlatePluginContext<P>) => SlatePluginConfig<any, InferOptions<P>, InferApi<P>, InferTransforms<P>, InferSelectors<P>>) | SlatePluginConfig<any, InferOptions<P>, InferApi<P>, InferTransforms<P>, InferSelectors<P>>) => SlatePlugin<C>; extend: <EO = {}, EA = {}, ET = {}, ES = {}>(extendConfig: ((ctx: SlatePluginContext<C>) => SlatePluginConfig<C['key'], InferOptions<C>, InferApi<C>, InferTransforms<C>, InferSelectors<C>, EO, EA, ET, ES>) | SlatePluginConfig<C['key'], InferOptions<C>, InferApi<C>, InferTransforms<C>, InferSelectors<C>, EO, EA, ET, ES>) => SlatePlugin<PluginConfig<C['key'], EO & InferOptions<C>, EA & InferApi<C>, ET & InferTransforms<C>, ES & InferSelectors<C>>>; extendApi: <EA extends Record<string, (...args: any[]) => any> = Record<string, never>>(extension: (ctx: SlatePluginContext<C>) => EA) => SlatePlugin<PluginConfig<C['key'], InferOptions<C>, InferApi<C> & Record<C['key'], EA>, InferTransforms<C>, InferSelectors<C>>>; extendEditorApi: <EA extends Record<string, ((...args: any[]) => any) | Record<string, (...args: any[]) => any>> = Record<string, never>>(extension: ExtendEditorApi<C, EA>) => SlatePlugin<PluginConfig<C['key'], InferOptions<C>, { [K in keyof (EA & InferApi<C>)]: (EA & InferApi<C>)[K] extends (...args: any[]) => any ? (EA & InferApi<C>)[K] : { [N in keyof (EA & InferApi<C>)[K]]: (EA & InferApi<C>)[K][N]; }; }, InferTransforms<C>, InferSelectors<C>>>; extendEditorTransforms: <ET extends Record<string, ((...args: any[]) => any) | Record<string, (...args: any[]) => any>> = Record<string, never>>(extension: ExtendEditorTransforms<C, ET>) => SlatePlugin<PluginConfig<C['key'], InferOptions<C>, InferApi<C>, { [K in keyof (ET & InferTransforms<C>)]: (ET & InferTransforms<C>)[K] extends (...args: any[]) => any ? (ET & InferTransforms<C>)[K] : { [N in keyof (ET & InferTransforms<C>)[K]]: (ET & InferTransforms<C>)[K][N]; }; }, InferSelectors<C>>>; extendPlugin: <P extends AnySlatePlugin, EO = {}, EA = {}, ET = {}, ES = {}>(plugin: Partial<P>, extendConfig: ((ctx: SlatePluginContext<P>) => SlatePluginConfig<any, InferOptions<P>, InferApi<P>, InferTransforms<P>, InferSelectors<P>, EO, EA, ET, ES>) | SlatePluginConfig<any, InferOptions<P>, InferApi<P>, InferTransforms<P>, InferSelectors<P>, EO, EA, ET, ES>) => SlatePlugin<C>; extendSelectors: <ES extends Record<string, (...args: any[]) => any> = Record<string, never>>(extension: (ctx: SlatePluginContext<C>) => ES) => SlatePlugin<PluginConfig<C['key'], InferOptions<C>, InferApi<C>, InferTransforms<C>, ES & InferSelectors<C>>>; extendTransforms: <ET extends Record<string, (...args: any[]) => any> = Record<string, never>>(extension: (ctx: SlatePluginContext<C>) => ET) => SlatePlugin<PluginConfig<C['key'], InferOptions<C>, InferApi<C>, InferTransforms<C> & Record<C['key'], ET>, InferSelectors<C>>>; overrideEditor: (override: OverrideEditor<C>) => SlatePlugin<C>; /** Returns a new instance of the plugin with the component. */ withComponent: (component: NodeComponent) => SlatePlugin<C>; __resolved?: boolean; }; type SlatePlugins = AnySlatePlugin[]; type TextStaticProps<C extends AnyPluginConfig = PluginConfig> = ((props: SlateRenderTextProps<TText, C>) => AnyObject | undefined) | AnyObject; type TransformOptions<C extends AnyPluginConfig = PluginConfig> = BaseTransformOptions & SlatePluginContext<C>; type SlateShortcut = { keys?: (({} & string)[][] | readonly string[] | string) | null; delimiter?: string; description?: string; document?: Document; enabled?: Trigger; enableOnContentEditable?: boolean; enableOnFormTags?: boolean; ignoreEventWhenPrevented?: boolean; ignoreModifiers?: boolean; keydown?: boolean; keyup?: boolean; preventDefault?: Trigger; priority?: number; scopes?: readonly string[] | string; splitKey?: string; useKey?: boolean; handler?: (ctx: { editor: SlateEditor; event: KeyboardEvent; eventDetails: any; }) => boolean | void; ignoreEventWhen?: (e: KeyboardEvent) => boolean; }; type Trigger = ((keyboardEvent: KeyboardEvent, hotkeysEvent: any) => boolean) | boolean; type ElementAffinity = { affinity: 'backward' | 'forward'; at: Path; type: string; }; type AffinityConfig = PluginConfig<'affinity'>; declare const AffinityPlugin: SlatePlugin<AffinityConfig>; interface WithAutoScrollOptions { mode?: ScrollMode; operations?: AutoScrollOperationsMap; scrollOptions?: ScrollIntoViewOptions; } declare const withScrolling: (editor: SlateEditor, fn: () => void, options?: WithAutoScrollOptions) => void; declare const AUTO_SCROLL: WeakMap<SlateEditor, boolean>; type AutoScrollOperationsMap = Partial<Record<Operation['type'], boolean>>; type DomConfig = PluginConfig<'dom', { /** Choose the first or last matching operation as the scroll target */ scrollMode?: ScrollMode; /** * Operations map; false to disable an operation, true or undefined to * enable */ scrollOperations?: AutoScrollOperationsMap; /** Options passed to scrollIntoView */ scrollOptions?: ScrollIntoViewOptions; }>; /** Mode for picking target op when multiple enabled */ type ScrollMode = 'first' | 'last'; /** * Placeholder plugin for DOM interaction, that could be replaced with * ReactPlugin. */ declare const DOMPlugin: SlatePlugin<PluginConfig<"dom", { /** Choose the first or last matching operation as the scroll target */ scrollMode?: ScrollMode; /** * Operations map; false to disable an operation, true or undefined to * enable */ scrollOperations?: AutoScrollOperationsMap; /** Options passed to scrollIntoView */ scrollOptions?: ScrollIntoViewOptions; }, { isScrolling: () => boolean; }, { withScrolling: (fn: () => void, options?: WithAutoScrollOptions | undefined) => void; }, {}>>; type InitOptions = { autoSelect?: boolean | 'end' | 'start'; selection?: TSelection; shouldNormalizeEditor?: boolean; value?: any; }; declare const init: (editor: SlateEditor, { autoSelect, selection, shouldNormalizeEditor, value }: InitOptions) => Promise<void>; type InsertExitBreakOptions = { match?: EditorAboveOptions['match']; reverse?: boolean; }; /** * Exits the current block structure by creating a new block next to the * appropriate ancestor. * * This function automatically determines the exit point by finding the first * ancestor that doesn't have strict sibling constraints (`isStrictSiblings: * false`), allowing standard text blocks to be inserted as siblings. * * For example: * * - In `column_group > column > codeblock > codeline`, exits after `codeblock`, * then after `column_group` * - In `table > tr > td > p`, exits after `table` */ declare const insertExitBreak: (editor: SlateEditor, { match, reverse }?: InsertExitBreakOptions) => true | undefined; /** * Reset the current block to a paragraph, removing all properties except id and * type. */ declare const resetBlock: (editor: SlateEditor, { at }?: { at?: Path; }) => true | undefined; declare const setValue: <V extends Value>(editor: SlateEditor, value?: V | string) => void; type SlateExtensionConfig = PluginConfig<'slateExtension', {}, {}, { init: OmitFirst<typeof init>; insertExitBreak: OmitFirst<typeof insertExitBreak>; resetBlock: OmitFirst<typeof resetBlock>; setValue: OmitFirst<typeof setValue>; }>; /** Opinionated extension of slate default behavior. */ declare const SlateExtensionPlugin: SlatePlugin<PluginConfig<"slateExtension", {}, {}, { init: (args_0: InitOptions) => Promise<void>; insertExitBreak: (args_0?: InsertExitBreakOptions | undefined) => true | undefined; resetBlock: (args_0?: { at?: _udecode_slate.Path; } | undefined) => true | undefined; setValue: (value?: string | _udecode_slate.Value | undefined) => void; }, {}>>; type DebugErrorType = (string & {}) | 'DEFAULT' | 'OPTION_UNDEFINED' | 'OVERRIDE_MISSING' | 'PLUGIN_DEPENDENCY_MISSING' | 'PLUGIN_MISSING' | 'USE_CREATE_PLUGIN' | 'USE_ELEMENT_CONTEXT'; type LogLevel = 'error' | 'info' | 'log' | 'warn'; declare class PlateError extends Error { type: DebugErrorType; constructor(message: string, type?: DebugErrorType); } declare const DebugPlugin: SlatePlugin<PluginConfig<"debug", { isProduction: boolean; logger: Partial<Record<LogLevel, (message: string, type?: DebugErrorType, details?: any) => void>>; logLevel: LogLevel; throwErrors: boolean; }, { debug: { error: (message: string | unknown, type?: DebugErrorType, details?: any) => void; info: (message: string, type?: DebugErrorType, details?: any) => void; log: (message: string, type?: DebugErrorType, details?: any) => void; warn: (message: string, type?: DebugErrorType, details?: any) => void; }; }, {}, {}>>; type NodeIdConfig = PluginConfig<'nodeId', { /** * By default, when a node inserted using editor.tf.insertNode(s) has an id, * it will be used instead of the id generator, except if it already exists * in the document. Set this option to true to disable this behavior. */ disableInsertOverrides?: boolean; /** * Filter inline `Element` nodes. * * @default true */ filterInline?: boolean; /** * Filter `Text` nodes. * * @default true */ filterText?: boolean; /** * Node key to store the id. * * @default 'id' */ idKey?: string; /** * Normalize initial value. If false, normalize only the first and last node * are missing id. To disable this behavior, use `NodeIdPlugin.configure({ * normalizeInitialValue: null })`. * * @default false */ normalizeInitialValue?: boolean; /** * Reuse ids on undo/redo and copy/pasting if not existing in the document. * This is disabled by default to avoid duplicate ids across documents. * * @default false */ reuseId?: boolean; /** * A function that generates and returns a unique ID. * * @default () => nanoid(10) */ idCreator?: () => any; } & QueryNodeOptions>; /** @see {@link withNodeId} */ declare const NodeIdPlugin: SlatePlugin<NodeIdConfig>; type CorePlugin = ReturnType<typeof getCorePlugins>[number]; type GetCorePluginsOptions = { /** Enable mark/element affinity. */ affinity?: boolean; /** Specifies the maximum number of characters allowed in the editor. */ maxLength?: number; /** Configure the node id plugin. */ nodeId?: NodeIdConfig['options'] | boolean; /** Override the core plugins using the same key. */ plugins?: AnyPluginConfig[]; }; declare const getCorePlugins: ({ affinity, maxLength, nodeId, plugins, }: GetCorePluginsOptions) => (SlatePlugin<DebugConfig> | SlatePlugin<PluginConfig<"slateExtension", {}, {}, { init: (args_0: InitOptions) => Promise<void>; insertExitBreak: (args_0?: InsertExitBreakOptions | undefined) => true | undefined; resetBlock: (args_0?: { at?: _udecode_slate.Path; } | undefined) => true | undefined; setValue: (value?: string | _udecode_slate.Value | undefined) => void; }, {}>> | SlatePlugin<PluginConfig<"dom", { scrollMode?: ScrollMode; scrollOperations?: AutoScrollOperationsMap; scrollOptions?: _udecode_slate.ScrollIntoViewOptions; }, { isScrolling: () => boolean; }, { withScrolling: (fn: () => void, options?: WithAutoScrollOptions | undefined) => void; }, {}>> | SlatePlugin<PluginConfig<"history", {}, {}, {}, {}>> | SlatePlugin<PluginConfig<"override", {}, {}, {}, {}>> | SlatePlugin<PluginConfig<"parser", {}, {}, {}, {}>> | SlatePlugin<LengthConfig> | SlatePlugin<PluginConfig<"html", {}, Record<"html", { deserialize: (args_0: { element: HTMLElement | string; collapseWhiteSpace?: boolean; defaultElementPlugin?: WithRequiredKey; }) => _udecode_slate.Descendant[]; }>, {}, {}>> | SlatePlugin<PluginConfig<"ast", {}, {}, {}, {}>> | SlatePlugin<NodeIdConfig> | SlatePlugin<AffinityConfig> | SlatePlugin<PluginConfig<"p", {}, {}, {}, {}>>)[]; type CorePluginTransforms = SlateExtensionConfig['transforms']; type CorePluginApi = SlateExtensionConfig['api']; type DebugConfig = PluginConfig<'debug', { isProduction: boolean; logger: Partial<Record<LogLevel, LogFunction>>; logLevel: LogLevel; throwErrors: boolean; }, { debug: { error: (message: string | unknown, type?: DebugErrorType, details?: any) => void; info: (message: string, type?: DebugErrorType, details?: any) => void; log: (message: string, type?: DebugErrorType, details?: any) => void; warn: (message: string, type?: DebugErrorType, details?: any) => void; }; }>; type LengthConfig = PluginConfig<'length', { maxLength: number; }>; type LogFunction = (message: string, type?: DebugErrorType, details?: any) => void; type ParagraphConfig = PluginConfig<'p'>; declare const BaseParagraphPlugin: SlatePlugin<PluginConfig<"p", {}, {}, {}, {}>>; type AnyPluginConfig = { key: any; api: any; options: any; selectors: any; transforms: any; }; type BaseDeserializer = AnyObject & { /** * Deserialize an element. Overrides plugin.isElement. * * @default plugin.isElement */ isElement?: boolean; /** * Deserialize a leaf. Overrides plugin.isLeaf. * * @default plugin.isLeaf */ isLeaf?: boolean; }; type BaseHtmlDeserializer = BaseDeserializer & { /** List of HTML attribute names to store their values in `node.attributes`. */ attributeNames?: string[]; rules?: { /** * Deserialize an element: * * - If this option (string) is in the element attribute names. * - If this option (object) values match the element attributes. */ validAttribute?: Record<string, string[] | string> | string; /** Valid element `className`. */ validClassName?: string; /** Valid element `nodeName`. Set '*' to allow any node name. */ validNodeName?: string[] | string; /** * Valid element style values. Can be a list of string (only one match is * needed). */ validStyle?: Partial<Record<keyof CSSStyleDeclaration, string[] | string | undefined>>; }[]; /** Whether or not to include deserialized children on this node */ withoutChildren?: boolean; }; type BaseInjectProps = { /** * Object whose keys are node values and values are classNames which will be * extended. */ classNames?: AnyObject; /** * Default node value. The node key would be unset if the node value = * defaultNodeValue. */ defaultNodeValue?: any; /** Node key to map to the styles. */ nodeKey?: string; /** * Style key to override. * * @default nodeKey */ styleKey?: keyof CSSStyleDeclaration; /** List of supported node values. */ validNodeValues?: any[]; }; type BasePlugin<C extends AnyPluginConfig = PluginConfig> = { /** Unique identifier for this plugin. */ key: C['key']; /** API methods provided by this plugin. */ api: InferApi<C>; /** * An array of plugin keys that this plugin depends on. These plugins will be * loaded before this plugin. */ dependencies: string[]; inject: Nullable<{ /** Plugin keys of elements to exclude the children from */ excludeBelowPlugins?: string[]; /** Plugin keys of elements to exclude */ excludePlugins?: string[]; /** Whether to filter blocks */ isBlock?: boolean; /** Whether to filter elements */ isElement?: boolean; /** Whether to filter leaves */ isLeaf?: boolean; /** Filter nodes with path above this level. */ maxLevel?: number; /** * Plugin keys used by {@link InjectNodeProps} and the targetPluginToInject * function. For plugin injection by key, use the inject.plugins property. * * @default [ParagraphPlugin.key] */ targetPlugins?: string[]; }>; /** Node-specific configuration for this plugin. */ node: BasePluginNode<C>; /** Extended properties used by any plugin as options. */ options: InferOptions<C>; /** Store for managing plugin options. */ optionsStore: TStateApi<C['options'], [ ['zustand/mutative-x', never] ], {}, C['selectors']>; override: { /** Enable or disable plugins */ enabled?: Partial<Record<string, boolean>>; }; /** * Defines the order in which plugins are registered and executed. * * Plugins with higher priority values are registered and executed before * those with lower values. This affects two main aspects: * * 1. Plugin Order: Plugins with higher priority will be added to the editor * earlier. * 2. Execution Order: For operations that involve multiple plugins (e.g., editor * methods), plugins with higher priority will be processed first. * * @default 100 */ priority: number; render: Nullable<{ /** * Renders a component above the `Editable` component but within the `Slate` * wrapper. Useful for adding UI elements that should appear above the * editable area. */ aboveEditable?: React.FC<{ children: React.ReactNode; }>; /** * Renders a component above the `Slate` wrapper. This is the outermost * render position in the editor structure. */ aboveSlate?: React.FC<{ children: React.ReactNode; }>; /** * Specifies the HTML tag name to use when rendering the node component. * Only used when no custom `component` is provided for the plugin. * * @default 'div' for elements, 'span' for leaves */ as?: keyof HTMLElementTagNameMap; /** * Renders a component below leaf nodes when `isLeaf: true` and * `isDecoration: false`. Use `render.node` instead when `isDecoration: * true`. */ leaf?: NodeComponent; /** * Renders a component for: * * - Elements nodes if `isElement: true` * - Below text nodes if `isLeaf: true` and `isDecoration: false` * - Below leaf if `isLeaf: true` and `isDecoration: true` */ node?: NodeComponent; }>; rules: { /** * Defines actions on insert break based on block state. * * - `'default'`: Default behavior * - `'exit'`: Exit the current block * - `'reset'`: Reset block to default paragraph type * - `'lineBreak'`: Insert newline character * - `'deleteExit'`: Delete backward then exit */ break?: BreakRules; /** * Defines actions on delete based on block state. * * - `'default'`: Default behavior * - `'reset'`: Reset block to default paragraph type */ delete?: DeleteRules; /** Defines the behavior of merging nodes. */ merge?: MergeRules; /** Defines the behavior of normalizing nodes. */ normalize?: NormalizeRules; /** Defines the behavior of selection. */ selection?: SelectionRules; }; /** Selectors for the plugin. */ selectors: InferSelectors<C>; /** Transforms (state-modifying operations) that can be applied to the editor. */ transforms: InferTransforms<C>; /** * Configures edit-only behavior for various plugin functionalities. * * - If `true` (boolean): * * - `render`, `handlers`, and `inject.nodeProps` are active only when the * editor is NOT read-only. * - If an object ({@link EditOnlyConfig}): Allows fine-grained control: * * - `render`: Edit-only by default (true if not specified). Set to `false` to * always be active. * - `handlers`: Edit-only by default (true if not specified). Set to `false` to * always be active. * - `inject` (for `inject.nodeProps`): Edit-only by default (true if not * specified). Set to `false` to always be active. * - `normalizeInitialValue`: NOT edit-only by default (false if not specified). * Set to `true` to make it edit-only. */ editOnly?: EditOnlyConfig | boolean; /** * Enables or disables the plugin. Used by Plate to determine if the plugin * should be used. */ enabled?: boolean; }; type BasePluginContext<C extends AnyPluginConfig = PluginConfig> = { api: C['api'] & EditorApi & CorePluginApi; setOptions: { (options: (state: Draft<Partial<InferOptions<C>>>) => void): void; (options: Partial<InferOptions<C>>): void; }; tf: C['transforms'] & EditorTransforms & CorePluginTransforms; type: string; getOption: <K extends keyof InferOptions<C> | keyof InferSelectors<C> | 'state'>(key: K, ...args: K extends keyof InferSelectors<C> ? Parameters<InferSelectors<C>[K]> : unknown[]) => K extends 'state' ? InferOptions<C> : K extends keyof InferSelectors<C> ? ReturnType<InferSelectors<C>[K]> : K extends keyof InferOptions<C> ? InferOptions<C>[K] : never; getOptions: () => InferOptions<C>; setOption: <K extends keyof InferOptions<C>>(optionKey: K, value: InferOptions<C>[K]) => void; }; type BasePluginNode<C extends AnyPluginConfig = PluginConfig> = { /** * Specifies the type identifier for this plugin's nodes. * * For elements (when {@link isElement} is `true`): * * - The {@link NodeComponent} will be used for any node where `node.type === * type`. * * For leaves/marks (when {@link isLeaf} is `true`): * * - The {@link NodeComponent} will be used for any leaf where `node[type] === * true`. * * This property is crucial for Plate to correctly match nodes to their * respective plugins. * * @default plugin.key */ type: string; component?: NodeComponent | null; /** * Controls which (if any) attribute names in the `attributes` property of an * element will be passed as `nodeProps` to the {@link NodeComponent}, and * subsequently rendered as DOM attributes. * * WARNING: If used improperly, this property WILL make your application * vulnerable to cross-site scripting (XSS) or information exposure attacks. * * For example, if the `href` attribute is allowed and the component passes * `nodeProps` to an `<a>` element, then attackers can direct users to open a * document containing a malicious link element: * * { type: 'link', url: 'https://safesite.com/', attributes: { href: * 'javascript:alert("xss")' }, children: [{ text: 'Click me' }], } * * The same is true of the `src` attribute when passed to certain HTML * elements, such as `<iframe>`. * * If the `style` attribute (or another attribute that can load URLs, such as * `background`) is allowed, then attackers can direct users to open a * document that will send a HTTP request to an arbitrary URL. This can leak * the victim's IP address or confirm to the attacker that the victim opened * the document. * * Before allowing any attribute name, ensure that you thoroughly research and * assess any potential risks associated with it. * * @default [ ] */ dangerouslyAllowAttributes?: string[]; /** * Indicates if this plugin's elements are primarily containers for other * content. Container elements are typically unwrapped when querying * fragments. * * Examples: table, tr, td, column, column_group * * @default false */ isContainer?: boolean; /** * Indicates if this plugin's nodes can be rendered as decorated leaf. Set to * false to render node component only once per text node. * * @default true */ isDecoration?: boolean; /** * Indicates if this plugin's nodes should be rendered as elements. Used by * Plate for {@link NodeComponent} rendering as elements. */ isElement?: boolean; /** * Indicates if this plugin's elements should be treated as inline. Used by * the inlineVoid core plugin. */ isInline?: boolean; /** * Indicates if this plugin's nodes should be rendered as leaves. Used by * Plate for {@link NodeComponent} rendering as leaves. */ isLeaf?: boolean; /** * Indicates if this plugin's void elements should be markable. Used by the * inlineVoid core plugin. */ isMarkableVoid?: boolean; /** * Whether the node is selectable. * * @default true */ isSelectable?: boolean; /** * Indicates whether this element enforces strict sibling type constraints. * Set to true `true` when the element only allows specific siblings (e.g., * `td` can only have `td` siblings, `column` can only have `column` siblings) * and prevents standard text blocks like paragraphs from being inserted as * siblings. */ isStrictSiblings?: boolean; /** * Property used by `inlineVoid` core plugin to set elements of this `type` as * void. */ isVoid?: boolean; /** * Function that returns an object of data attributes to be added to the * element. */ toDataAttributes?: (options: BasePluginContext<C> & { node: TElement; }) => AnyObject | undefined; }; type BaseSerializer = AnyObject; type BaseTransformOptions = GetInjectNodePropsOptions & { nodeValue?: any; value?: any; }; interface BreakRules { /** Action when Enter is pressed in an empty block. */ empty?: 'default' | 'deleteExit' | 'exit' | 'reset'; /** * Action when Enter is pressed at the end of an empty line. This is typically * used with `default: 'lineBreak'`. * * Example: * * ```tsx * <blockquote> * This is some text\n * | * </blockquote> * ``` */ emptyLineEnd?: 'default' | 'deleteExit' | 'exit'; /** Default action when Enter is pressed. Defaults to splitting the block. */ default?: 'default' | 'deleteExit' | 'exit' | 'lineBreak'; /** If true, the new block after splitting will be reset to the default type. */ splitReset?: boolean; } interface MergeRules { /** Whether to remove the node when it's empty. */ removeEmpty?: boolean; } interface NormalizeRules { /** Whether to remove nodes with empty text. */ removeEmpty?: boolean; } interface DeleteRules { /** * Action when Backspace is pressed at the start of the block. This applies * whether the block is empty or not. * * Example: * * ```tsx * <blockquote> * |Text * </blockquote> * ``` */ start?: 'default' | 'reset'; /** Action when Backspace is pressed and the block is empty. */ empty?: 'default' | 'reset'; } interface SelectionRules { /** * Defines the selection behavior at the boundaries of nodes. * * - `directional`: Selection affinity is determined by the direction of cursor * movement. Maintains inward or outward affinity based on approach. * - `outward`: Forces outward affinity. Typing at the edge of a mark will not * apply the mark to new text. * - `hard`: Creates a 'hard' edge that requires two key presses to move across. * Uses offset-based navigation. * - `default`: Uses Slate's default behavior. */ affinity?: 'default' | 'directional' | 'hard' | 'outward'; } type MatchRules = 'break.default' | 'break.empty' | 'break.emptyLineEnd' | 'break.splitReset' | 'delete.empty' | 'delete.start' | 'merge.removeEmpty' | 'normalize.removeEmpty' | 'selection.affinity'; type EditOnlyConfig = { /** * If true, `handlers` are only active when the editor is not read-only. * * @default true (when `editOnly` is an object or `true` boolean) */ handlers?: boolean; /** * If true, `inject.nodeProps` is only active when the editor is not * read-only. * * @default true (when `editOnly` is an object or `true` boolean) */ inject?: boolean; /** * If true, `normalizeInitialValue` is only called when the editor is not * read-only. * * @default false (This is an exception. It's not edit-only by default, even if `editOnly` is true or an object, unless explicitly set to true here). */ normalizeInitialValue?: boolean; /** * If true, `render` functions are only active when the editor is not * read-only. * * @default true (when `editOnly` is an object or `true` boolean) */ render?: boolean; }; type ExtendConfig<C extends PluginConfig, EO = {}, EA = {}, ET = {}, ES = {}> = { key: C['key']; api: C['api'] & EA; options: C['options'] & EO; selectors: C['selectors'] & ES; transforms: C['transforms'] & ET; }; interface GetInjectNodePropsOptions { /** Existing className. */ className?: string; /** Style value or className key. */ element?: TElement; /** Existing style. */ style?: CSSStyleDeclara