UNPKG

@storyblok/richtext

Version:
570 lines 17 kB
//#region src/types/index.d.ts /** * Represents the configuration options for optimizing images in rich text content. */ interface StoryblokRichTextImageOptimizationOptions { /** * CSS class to be applied to the image. */ class: string; /** * Width of the image in pixels. */ width: number; /** * Height of the image in pixels. */ height: number; /** * Loading strategy for the image. 'lazy' loads the image when it enters the viewport. 'eager' loads the image immediately. */ loading: 'lazy' | 'eager'; /** * Optional filters that can be applied to the image to adjust its appearance. * * @example * * ```typescript * const filters: Partial<StoryblokRichTextImageOptimizationOptions['filters']> = { * blur: 5, * brightness: 150, * grayscale: true * } * ``` */ filters: Partial<{ blur: number; brightness: number; fill: 'transparent'; format: 'webp' | 'png' | 'jpg'; grayscale: boolean; quality: number; rotate: 0 | 90 | 180 | 270; }>; /** * Defines a set of source set values that tell the browser different image sizes to load based on screen conditions. * The entries can be just the width in pixels or a tuple of width and pixel density. * * @example * * ```typescript * const srcset: (number | [number, number])[] = [ * 320, * [640, 2] * ] * ``` */ srcset: (number | [number, number])[]; /** * A list of sizes that correspond to different viewport widths, instructing the browser on which srcset source to use. * * @example * * ```typescript * const sizes: string[] = [ * '(max-width: 320px) 280px', * '(max-width: 480px) 440px', * '800px' * ] * ``` */ sizes: string[]; } //#endregion //#region src/extensions/richtext-attrs.d.ts /** Node Attribute Types */ interface ParagraphAttrs { textAlign: 'left' | 'center' | 'right' | 'justify' | null; [key: string]: unknown; } interface HeadingAttrs { textAlign: 'left' | 'center' | 'right' | 'justify' | null; level?: 1 | 2 | 3 | 4 | 5 | 6; [key: string]: unknown; } interface CodeBlockAttrs { class: string | null; [key: string]: unknown; } interface OrderedListAttrs { order?: number; [key: string]: unknown; } interface TableCellAttrs { colspan?: number; rowspan?: number; colwidth?: number[] | null; backgroundColor?: string | null; [key: string]: unknown; } interface TableHeaderAttrs { colspan?: number; rowspan?: number; colwidth?: number[] | null; [key: string]: unknown; } interface ImageAttrs { id: number | null; alt: string | null; src: string; title: string | null; source: string | null; copyright: string | null; meta_data: { alt: string | null; title: string | null; source: string | null; copyright: string | null; } | null; [key: string]: unknown; } interface EmojiAttrs { name: string; emoji: string; fallbackImage: string; [key: string]: unknown; } /** Mark Attribute Types */ interface LinkAttrs { href: string | null; uuid: string | null; anchor: string | null; target: '_self' | '_blank' | '_parent' | '_top' | null; linktype: 'story' | 'url' | 'email' | 'asset' | null; custom?: Record<string, unknown>; [key: string]: unknown; } interface HighlightAttrs { color: string; [key: string]: unknown; } interface TextStyleAttrs { color?: string | null; id?: string | null; class?: string | null; [key: string]: unknown; } interface AnchorAttrs { id: string; [key: string]: unknown; } interface StyledAttrs { class: string | null; [key: string]: unknown; } /** Maps node names to their attribute types. */ interface NodeAttrTypeMap { paragraph: ParagraphAttrs; heading: HeadingAttrs; code_block: CodeBlockAttrs; ordered_list: OrderedListAttrs; tableCell: TableCellAttrs; tableHeader: TableHeaderAttrs; image: ImageAttrs; emoji: EmojiAttrs; } /** Maps mark names to their attribute types. */ interface MarkAttrTypeMap { link: LinkAttrs; highlight: HighlightAttrs; textStyle: TextStyleAttrs; anchor: AnchorAttrs; styled: StyledAttrs; } //#endregion //#region src/static/types.generated.d.ts /** Attribute types for all Tiptap node extensions */ interface TiptapNodeAttributes { paragraph: NodeAttrTypeMap['paragraph']; doc: Record<string, never>; text: Record<string, never>; blockquote: Record<string, never>; heading: NodeAttrTypeMap['heading']; bullet_list: Record<string, never>; ordered_list: NodeAttrTypeMap['ordered_list']; list_item: Record<string, never>; code_block: NodeAttrTypeMap['code_block']; hard_break: Record<string, never>; horizontal_rule: Record<string, never>; image: NodeAttrTypeMap['image']; emoji: NodeAttrTypeMap['emoji']; table: Record<string, never>; tableRow: Record<string, never>; tableCell: NodeAttrTypeMap['tableCell']; tableHeader: NodeAttrTypeMap['tableHeader']; blok: { id?: string | null; body?: SbBlokData[] | null; }; details: Record<string, never>; detailsContent: Record<string, never>; detailsSummary: Record<string, never>; } /** Attribute types for all Tiptap mark extensions */ interface TiptapMarkAttributes { link: MarkAttrTypeMap['link']; bold: Record<string, never>; italic: Record<string, never>; strike: Record<string, never>; underline: Record<string, never>; code: Record<string, never>; superscript: Record<string, never>; subscript: Record<string, never>; highlight: MarkAttrTypeMap['highlight']; textStyle: MarkAttrTypeMap['textStyle']; anchor: MarkAttrTypeMap['anchor']; styled: MarkAttrTypeMap['styled']; reporter: Record<string, never>; } type TiptapNodeName = keyof TiptapNodeAttributes; type TiptapMarkName = keyof TiptapMarkAttributes; type PMNode = { type: 'paragraph'; attrs?: TiptapNodeAttributes['paragraph']; content?: PMNode[]; marks?: PMMark[]; } | { type: 'doc'; attrs?: TiptapNodeAttributes['doc']; content?: PMNode[]; marks?: PMMark[]; } | { type: 'text'; attrs?: TiptapNodeAttributes['text']; content?: PMNode[]; marks?: PMMark[]; text: string; } | { type: 'blockquote'; attrs?: TiptapNodeAttributes['blockquote']; content?: PMNode[]; marks?: PMMark[]; } | { type: 'heading'; attrs?: TiptapNodeAttributes['heading']; content?: PMNode[]; marks?: PMMark[]; } | { type: 'bullet_list'; attrs?: TiptapNodeAttributes['bullet_list']; content?: PMNode[]; marks?: PMMark[]; } | { type: 'ordered_list'; attrs?: TiptapNodeAttributes['ordered_list']; content?: PMNode[]; marks?: PMMark[]; } | { type: 'list_item'; attrs?: TiptapNodeAttributes['list_item']; content?: PMNode[]; marks?: PMMark[]; } | { type: 'code_block'; attrs?: TiptapNodeAttributes['code_block']; content?: PMNode[]; marks?: PMMark[]; } | { type: 'hard_break'; attrs?: TiptapNodeAttributes['hard_break']; content?: PMNode[]; marks?: PMMark[]; } | { type: 'horizontal_rule'; attrs?: TiptapNodeAttributes['horizontal_rule']; content?: PMNode[]; marks?: PMMark[]; } | { type: 'image'; attrs?: TiptapNodeAttributes['image']; content?: PMNode[]; marks?: PMMark[]; } | { type: 'emoji'; attrs?: TiptapNodeAttributes['emoji']; content?: PMNode[]; marks?: PMMark[]; } | { type: 'table'; attrs?: TiptapNodeAttributes['table']; content?: PMNode[]; marks?: PMMark[]; } | { type: 'tableRow'; attrs?: TiptapNodeAttributes['tableRow']; content?: PMNode[]; marks?: PMMark[]; } | { type: 'tableCell'; attrs?: TiptapNodeAttributes['tableCell']; content?: PMNode[]; marks?: PMMark[]; } | { type: 'tableHeader'; attrs?: TiptapNodeAttributes['tableHeader']; content?: PMNode[]; marks?: PMMark[]; } | { type: 'blok'; attrs?: TiptapNodeAttributes['blok']; content?: PMNode[]; marks?: PMMark[]; } | { type: 'details'; attrs?: TiptapNodeAttributes['details']; content?: PMNode[]; marks?: PMMark[]; } | { type: 'detailsContent'; attrs?: TiptapNodeAttributes['detailsContent']; content?: PMNode[]; marks?: PMMark[]; } | { type: 'detailsSummary'; attrs?: TiptapNodeAttributes['detailsSummary']; content?: PMNode[]; marks?: PMMark[]; }; type PMMark = { type: 'link'; attrs?: TiptapMarkAttributes['link']; } | { type: 'bold'; attrs?: TiptapMarkAttributes['bold']; } | { type: 'italic'; attrs?: TiptapMarkAttributes['italic']; } | { type: 'strike'; attrs?: TiptapMarkAttributes['strike']; } | { type: 'underline'; attrs?: TiptapMarkAttributes['underline']; } | { type: 'code'; attrs?: TiptapMarkAttributes['code']; } | { type: 'superscript'; attrs?: TiptapMarkAttributes['superscript']; } | { type: 'subscript'; attrs?: TiptapMarkAttributes['subscript']; } | { type: 'highlight'; attrs?: TiptapMarkAttributes['highlight']; } | { type: 'textStyle'; attrs?: TiptapMarkAttributes['textStyle']; } | { type: 'anchor'; attrs?: TiptapMarkAttributes['anchor']; } | { type: 'styled'; attrs?: TiptapMarkAttributes['styled']; } | { type: 'reporter'; attrs?: TiptapMarkAttributes['reporter']; }; //#endregion //#region src/static/types.d.ts type SbRichTextElement = Exclude<TiptapNodeName | TiptapMarkName, 'text'>; /** Valid attribute values for DOM elements */ type AttrValue = string | number | boolean; type HtmlTag = keyof HTMLElementTagNameMap; interface ISbComponentType<T extends string> { _uid?: string; component?: T; _editable?: string; } type SbBlokKeyDataTypes = string | number | object | boolean | undefined; interface SbBlokData extends ISbComponentType<string> { [index: string]: SbBlokKeyDataTypes; } interface RenderSpec { tag: string; attrs?: Record<string, AttrValue> & { style?: string; }; content?: boolean; children?: RenderSpec[]; resolve?: (attrs: unknown) => string; } /** Canonical type for a Storyblok RichText JSON root */ type SbRichTextDoc = PMNode; /** Base props for node/mark components */ type SbRichTextProps<T extends SbRichTextElement> = T extends PMNode['type'] ? Extract<PMNode, { type: T; }> : T extends PMMark['type'] ? Extract<PMMark, { type: T; }> & { children: string; } : never; /** Generic component map for any renderer target */ type SbRichTextComponents<TComponent = string> = { [K in SbRichTextElement]?: (props: SbRichTextProps<K>) => TComponent }; interface SbRichTextOptions { renderers?: SbRichTextComponents<string>; optimizeImages?: boolean | Partial<StoryblokRichTextImageOptimizationOptions>; } //#endregion //#region src/static/attribute.d.ts type AttrMap = Record<string, string>; /** * Process Tiptap attributes into HTML attributes and inline styles. * Applies internal style mappings and allows extending or overriding * default attribute mappings via `extendAttrMap`. * * @param type - {@link SbRichTextElement} * @param attrs - Attributes from the node/mark * @param extendAttrMap - {@link AttrMap} Additional attribute mappings (overrides defaults) * @returns Processed attributes with optional `style` object */ declare function processAttrs(type: SbRichTextElement, attrs?: Record<string, unknown>, extendAttrMap?: AttrMap): Record<string, unknown>; //#endregion //#region src/static/node-helpers.d.ts /** * Gets the link mark from a text node, or null if not present. * @param node - The node to check * @returns The link mark if found, null otherwise */ declare function getTextNodeLinkMark(node: SbRichTextDoc): LinkMark | null; type LinkMark = PMMark & { type: 'link'; }; /** * Checks if two link marks have identical attributes. * Used for merging adjacent text nodes with the same link. * @param markA - First link mark * @param markB - Second link mark * @returns True if the marks have identical attributes */ declare function areLinkMarksEqual(markA: LinkMark | null, markB: LinkMark | null): boolean; /** * Gets non-link marks from a text node. * Used when rendering text inside a merged link group. * @param node - The text node * @returns Array of marks excluding the link mark */ declare function getInnerMarks(node: SbRichTextDoc): PMMark[]; /** * Identifies groups of adjacent text nodes that share the same link mark. * Returns an array of groups where each group is either: * - A single non-text node or text node without link * - Multiple consecutive text nodes with identical link marks * * @param children - Array of child nodes to group * @returns Array of node groups for rendering */ declare function groupLinkNodes(children: SbRichTextDoc[]): Array<{ nodes: SbRichTextDoc[]; linkMark: LinkMark | null; }>; /** * Checks if a table row contains only tableHeader cells. * Used to determine which rows belong in thead vs tbody. * @param row - The table row node to check * @returns True if all cells are tableHeader type */ declare function isTableHeaderRow(row: SbRichTextDoc): boolean; /** * Splits table rows into header rows and body rows. * Header rows are contiguous tableHeader rows at the start. * @param rows - Array of table row nodes * @returns Object with headerRows and bodyRows arrays */ declare function splitTableRows(rows: SbRichTextDoc[] | undefined): { headerRows: SbRichTextDoc[]; bodyRows: SbRichTextDoc[]; }; //#endregion //#region src/static/render-richtext.d.ts /** * Renders a Storyblok RichText JSON document to an HTML string. * * @param document - RichText JSON document, array of nodes, or nullish value * @param options - Renderer configuration with custom node/mark renderers * @returns Rendered HTML string * * @example * ```ts * const html = renderRichText({ * type: 'doc', * content: [{ type: 'paragraph', content: [{ type: 'text', text: 'Hello' }] }] * }); * // => '<p>Hello</p>' * ``` */ declare function renderRichText(document: SbRichTextDoc | SbRichTextDoc[] | null | undefined, options?: SbRichTextOptions): string; //#endregion //#region src/static/style.d.ts /** * Converts a style object to a CSS string. * @param style - The style object to convert. * @returns A CSS string representation of the style object. * @example * const styleObj = { color: 'red', fontSize: '16px' }; * const cssString = styleToString(styleObj); * console.log(cssString); // Output: "color: red; font-size: 16px" */ declare function styleToString(style: Record<string, AttrValue>): string; /** * Converts a CSS string to a style object. * @param style - The CSS string to convert. * @returns A style object representation of the CSS string. * @example * const cssString = "color: red; font-size: 16px"; * const styleObj = stringToStyle(cssString); * console.log(styleObj); // Output: { color: 'red', fontSize: '16px' } */ declare function stringToStyle(style: string): Record<string, string>; //#endregion //#region src/static/util.d.ts /** * Resolves a component from the provided components map based on the type. * @param type - The type of the component to resolve. * @param components - The components map to search in. * @returns The resolved component or undefined if not found. * @example * const components = { * 'heading': MyCustomHeading, * }; * const resolvedComponent = resolveComponent('heading', components); * console.log(resolvedComponent); // Output: MyCustomHeading */ declare function resolveComponent<K extends SbRichTextElement, TComponent>(type: K, components?: SbRichTextComponents<TComponent>): SbRichTextComponents<TComponent>[K] | undefined; /** * Resolves the HTML tag for a given Richtext node or mark. * @param node - The Richtext node or mark to resolve the tag for. * @returns The resolved HTML tag as a string, or null if no tag could be resolved. * @example * const node = { type: 'paragraph', attrs: {} }; * const tag = resolveTag(node); * console.log(tag); // Output: "p" */ declare function resolveTag(node: PMNode | PMMark): HtmlTag | null; /** * Checks if a given HTML tag is self-closing. * @param tag - The HTML tag to check. * @returns True if the tag is self-closing, false otherwise. * @example * console.log(isSelfClosing('img')); // Output: true * console.log(isSelfClosing('div')); // Output: false * */ declare function isSelfClosing(tag: HtmlTag | string): boolean; /** * Returns static child definitions for a given RichText node. * * @param node - The RichText node * @returns Static child render specs, or null if none exist * * @example * const children = getStaticChildren({ type: 'table', attrs: {} }); * // [{ tag: 'tbody', content: true }] */ declare function getStaticChildren(node: PMNode): readonly [{ readonly tag: "code"; readonly content: true; }] | null; //#endregion export { type PMMark, type PMNode, type RenderSpec, type SbRichTextComponents, type SbRichTextDoc, type SbRichTextElement, type SbRichTextProps, areLinkMarksEqual, getInnerMarks, getStaticChildren, getTextNodeLinkMark, groupLinkNodes, isSelfClosing, isTableHeaderRow, processAttrs, renderRichText, resolveComponent, resolveTag, splitTableRows, stringToStyle, styleToString }; //# sourceMappingURL=static.d.cts.map