import * as React from 'react'; export type RichTextInputAreaStatus = 'error' | 'warning' | 'loading'; export type RichTextInputAreaSize = 'small' | 'large'; export type RichTextInputAreaAction = | 'bold' | 'italic' | 'underline' | 'link' | 'bulletedList' | 'numberedList'; /** An element the editor's content is serialized into. */ export interface RichTextInputAreaConvertedElement { /** Tag name to render around the content. Restricted to an allowlist of elements * considered safe to emit — script-capable, embedding, interactive and void * elements are refused however well they would wrap content. A refused element * renders as though the handler had returned `undefined`. */ element: string; /** Attributes to render. Values are escaped, and anything that could execute * script is dropped. `style` is passed through as written. */ attributes?: Record; } /** An element a block is serialized into, optionally with the element wrapping a run * of sibling blocks. */ export interface RichTextInputAreaConvertedBlock extends RichTextInputAreaConvertedElement { /** Element wrapping a run of consecutive sibling blocks, such as the `ul` around * list items — omit it and a list item still gets the default `ul` or `ol`. A run * is grouped by type and depth alone, so adjacent runs share the first wrapper. */ wrapper?: RichTextInputAreaConvertedElement; } /** A block of the editor's content, as passed to `serializeBlock`, which is called * more than once for the same block and so has to be free of side effects. */ export interface RichTextInputAreaBlock { /** The block's type, such as `unstyled` or `unordered-list-item`. */ type: string; /** The block's text, without the markup its styles and entities render as. */ text: string; /** Nesting level, counted from zero. Carried by list items. */ depth: number; /** Whatever `parseBlock` attached to the block. */ data: Record; } export type RichTextInputAreaEntityMutability = | 'MUTABLE' | 'IMMUTABLE' | 'SEGMENTED'; /** An entity of the editor's content, as passed to `serializeEntity`. */ export interface RichTextInputAreaEntity { /** The entity's type, such as `'LINK'`, as `parseEntity` created it. */ type: string; /** How the entity survives an edit to the text it covers: `'MUTABLE'` keeps it, * `'IMMUTABLE'` drops it from the whole run, `'SEGMENTED'` drops it a word at a * time. */ mutability: RichTextInputAreaEntityMutability; /** Whatever `parseEntity` attached to the entity, which for a link is the data * `onLinkRequest` resolved with. */ data: Record; } /** The data a link carries. `url` is read by the default serialization, and every * key but `text` is passed through untouched, for `converters.serializeEntity` to * render. */ export interface RichTextInputAreaLinkData extends Record { /** The link's address, read by the default serialization and by the anchor the * editor renders. A link modelled without one needs `converters.serializeEntity` * to render at all. */ url?: string; /** The text to write, rather than data the link carries: written at a caret, and * over the selected text when it differs from it. It is never stored on the link. * With no `text`, a caret touching a link retargets the whole of that link, and a * caret elsewhere writes the `url`. */ text?: string; } /** The block `parseBlock` resolves an element to. */ export interface RichTextInputAreaParsedBlock { /** Block type to resolve the element to, such as `unstyled` or * `unordered-list-item`. Omit it to keep the type the built-in parsing resolved. */ type?: string; /** Data to attach to the block, which reaches `serializeBlock` untouched and * survives a return pressed in the block and a paste into it. */ data?: Record; } /** * Handlers that extend how HTML is turned into editor content and back, so that * markup the editor has no model for survives editing instead of being dropped. * * Supplying any `serialize*` handler changes the output formatting: blocks aren't * separated by newlines, lists aren't indented, and text is entity-escaped. */ export interface RichTextInputAreaConverters { /** Resolves the inline styles of an HTML node, or returns `undefined` to keep * `currentStyles`. What you return replaces the set, so seed it from * `currentStyles` to keep what an ancestor contributed. */ parseStyle?: ( nodeName: string, node: HTMLElement, currentStyles: string[], ) => string[] | undefined; /** Maps an HTML element onto a block type and its data, or returns `undefined` to * keep `defaultBlock`, which is `null` for an element with no built-in block type. * Called for every element, inline ones included. */ parseBlock?: ( nodeName: string, node: HTMLElement, defaultBlock: RichTextInputAreaParsedBlock | null, ) => RichTextInputAreaParsedBlock | undefined; /** Turns an HTML node into an entity, returning the key `createEntity` gives you, * or `undefined` to fall through to the built-in link handling. An entity of type * `'LINK'` is the one the link button and `onLinkRequest` act on. Called for every * node, text nodes included. */ parseEntity?: ( nodeName: string, node: Node, createEntity: ( type: 'LINK' | (string & {}), mutability: RichTextInputAreaEntityMutability, data: Record, ) => string, ) => string | undefined; /** Renders one inline style, or returns `undefined` for the default rendering. */ serializeStyle?: ( style: string, ) => RichTextInputAreaConvertedElement | undefined; /** Renders one block, or returns `undefined` for the default rendering. */ serializeBlock?: ( block: RichTextInputAreaBlock, ) => RichTextInputAreaConvertedBlock | undefined; /** Renders one entity. Returns `undefined` for the default rendering, or `null` to * render the entity's text with no element around it. * * The default rendering of a `'LINK'` entity reads its `url`, so an entity parsed * without one — a reference the editor can't resolve to an address — needs this * handler to render at all, and becomes plain text without it. */ serializeEntity?: ( entity: RichTextInputAreaEntity, text: string, ) => RichTextInputAreaConvertedElement | null | undefined; } export interface RichTextInputAreaProps { /** Applies a data-hook HTML attribute that can be used in the tests. */ dataHook?: string; /** Specifies a CSS class name to be appended to the component's root element. * @internal */ className?: string; /** Sets the initial value to be displayed in the editor. Read once, when the * component mounts. To replace the value later, such as after an edit made * elsewhere, hold a ref and call `setValue`. * @default '

' */ initialValue?: string; /** Sets a placeholder message to display. */ placeholder?: string; /** Whether this specific button is disabled (toolbar-level `disabled` also applies) * @default false */ disabled?: boolean; /** Specifies the status of a field. */ status?: RichTextInputAreaStatus; /** Defines the message to display on status icon hover. If not given or empty, no tooltip is displayed. */ statusMessage?: React.ReactNode; /** Defines a standard callback function for changes: `onChange(htmlText, { plainText })` */ onChange?: (htmlText: string, params: { plainText: string }) => void; /** Sets the background color of the editor. Accepts any CSS color value. */ backgroundColor?: string; /** Defines a minimum height for the editor (it grows by default) */ minHeight?: string; /** Defines a maximum height for the editor (it grows by default) */ maxHeight?: string; /** * Enables browsers spell checking. * Doesn't affect IE. * In Safari, autocorrects by default. */ spellCheck?: boolean; /** * Sets the size of the toolbar icons. * @default 'large' */ size?: RichTextInputAreaSize; /** * Sets which built-in toolbar actions to turn off, so that their buttons aren't * rendered. Existing formatting in `initialValue` or in pasted content isn't affected. */ hiddenActions?: RichTextInputAreaAction[]; /** An array of custom toolbar button configs to append after the built-in buttons. */ customButtons?: Array<{ /** Tooltip tooltip text. */ tooltipText?: string; /** Button icon element. */ icon?: React.ReactNode; /** Click handler. */ onClick?: () => void; /** Whether this specific button is disabled (toolbar-level `disabled` also applies) * @default false */ disabled?: boolean; /** Whether the button appears in an active/pressed state. */ active?: boolean; /** data-hook for testing. */ dataHook?: string; }>; /** Defines text styles to be shown. */ texts?: texts; /** Extends how HTML is turned into editor content and back, so that markup the * editor has no model for survives editing. Also applied to pasted HTML. */ converters?: RichTextInputAreaConverters; /** * Replaces the built-in link form with a flow of your own, such as a link picker * panel. Receives the link at the selection, if there is one, and the text the * selection covers — the same text the built-in form offers for editing. * * Resolve with link data to apply it — over the whole of a link the selection * touches, over the selected text, or as text written at a caret — with `null` to * detach, or with `undefined` to leave the content untouched. The data replaces * the link's rather than adding to it. */ onLinkRequest?: ( currentLink: RichTextInputAreaLinkData | undefined, selection: { selectedText: string }, ) => | Promise | RichTextInputAreaLinkData | null | undefined; } export type texts = { toolbarButtons?: { boldButtonLabel?: string; italicButtonLabel?: string; underlineButtonLabel?: string; linkButtonLabel?: string; bulletedListButtonLabel?: string; numberedListButtonLabel?: string; }; insertionForm?: { confirmButtonLabel?: string; cancelButtonLabel?: string; link?: { textInputPlaceholder?: string; urlInputPlaceholder?: string; }; }; }; export class RichTextInputArea extends React.PureComponent { setValue: (value: string) => void; focus: () => void; blur: () => void; } export interface RichTextInputAreaConvertedValue { /** What the editor emits for the value before it is edited. */ html: string; /** What the value's text becomes, with a newline between blocks. */ plainText: string; } /** * Runs an HTML value through the same parsing and serialization the editor uses, * without rendering one. Compare an edit against the returned `html`, not against * the stored value, to tell a real change from the editor's own normalization. * * Call it per value rather than per keystroke: each call registers the entities it * parses in the engine's process-wide store, which is never pruned. */ export function convertRichTextValue( html: string, converters?: RichTextInputAreaConverters, ): RichTextInputAreaConvertedValue;