@storyblok/richtext
Version:
Storyblok RichText Resolver
364 lines • 10.8 kB
TypeScript
//#region src/types/index.d.ts
declare enum BlockTypes {
DOCUMENT = "doc",
HEADING = "heading",
PARAGRAPH = "paragraph",
QUOTE = "blockquote",
OL_LIST = "ordered_list",
UL_LIST = "bullet_list",
LIST_ITEM = "list_item",
CODE_BLOCK = "code_block",
HR = "horizontal_rule",
BR = "hard_break",
IMAGE = "image",
EMOJI = "emoji",
COMPONENT = "blok",
TABLE = "table",
TABLE_ROW = "tableRow",
TABLE_CELL = "tableCell",
TABLE_HEADER = "tableHeader",
}
declare enum MarkTypes {
BOLD = "bold",
STRONG = "strong",
STRIKE = "strike",
UNDERLINE = "underline",
ITALIC = "italic",
CODE = "code",
LINK = "link",
ANCHOR = "anchor",
STYLED = "styled",
SUPERSCRIPT = "superscript",
SUBSCRIPT = "subscript",
TEXT_STYLE = "textStyle",
HIGHLIGHT = "highlight",
}
declare enum TextTypes {
TEXT = "text",
}
declare enum LinkTargets {
SELF = "_self",
BLANK = "_blank",
}
declare enum LinkTypes {
URL = "url",
STORY = "story",
ASSET = "asset",
EMAIL = "email",
}
interface StoryblokRichTextDocumentNode {
type: string;
content?: StoryblokRichTextDocumentNode[];
attrs?: Record<string, any>;
text?: string;
marks?: StoryblokRichTextDocumentNode[];
}
type StoryblokRichTextNodeTypes = BlockTypes | MarkTypes | TextTypes;
interface StoryblokRichTextNode<T = string> {
type: StoryblokRichTextNodeTypes;
content: StoryblokRichTextNode<T>[];
children?: T;
attrs?: Record<string, any>;
text?: string;
}
interface LinkNode<T = string> extends StoryblokRichTextNode<T> {
type: MarkTypes.LINK | MarkTypes.ANCHOR;
linktype: LinkTypes.ASSET | LinkTypes.EMAIL | LinkTypes.STORY | LinkTypes.URL;
attrs: Record<string, any>;
}
interface MarkNode<T = string> extends StoryblokRichTextNode<T> {
type: MarkTypes.BOLD | MarkTypes.ITALIC | MarkTypes.UNDERLINE | MarkTypes.STRIKE | MarkTypes.CODE | MarkTypes.LINK | MarkTypes.ANCHOR | MarkTypes.STYLED | MarkTypes.SUPERSCRIPT | MarkTypes.SUBSCRIPT | MarkTypes.TEXT_STYLE | MarkTypes.HIGHLIGHT;
attrs?: Record<string, any>;
linkType: LinkTypes;
}
interface TextNode<T = string> extends StoryblokRichTextNode<T> {
type: TextTypes.TEXT;
text: string;
marks?: MarkNode<T>[];
}
/**
* Represents the render context provided to resolvers
* @template T - The type of the resolved value
*/
interface StoryblokRichTextContext<T = string> {
/**
* Render function that automatically handles key generation
* @param tag - The HTML tag to render
* @param attrs - The attributes for the tag
* @param children - Optional children content
*/
render: (tag: string, attrs?: Record<string, any>, children?: T) => T;
/**
* Original resolvers map
*/
originalResolvers: Map<StoryblokRichTextNodeTypes, StoryblokRichTextNodeResolver<T>>;
/**
* Merged resolvers map
*/
mergedResolvers: Map<StoryblokRichTextNodeTypes, StoryblokRichTextNodeResolver<T>>;
}
/**
* Represents a resolver function for a Storyblok rich text node.
* @template T - The type of the resolved value.
* @param node - The rich text node to resolve.
* @param context - The render context with utilities
* @returns The resolved value of type T.
*/
type StoryblokRichTextNodeResolver<T = string> = (node: StoryblokRichTextNode<T> | TextNode<T> | MarkNode<T> | LinkNode<T>, context: StoryblokRichTextContext<T>) => T;
/**
* 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[];
}
/**
* Resolvers for Storyblok RichText nodes.
*
* @template T - The type of the resolved value.
*/
type StoryblokRichTextResolvers<T = string> = Partial<Record<StoryblokRichTextNodeTypes, StoryblokRichTextNodeResolver<T>>>;
/**
* Represents the options for rendering rich text.
*/
interface StoryblokRichTextOptions<T = string, S = (tag: string, attrs: Record<string, any>, children?: T) => T> {
/**
* Defines the function that will be used to render the final HTML string (vanilla) or Framework component (React, Vue).
*
* @example
*
* ```typescript
* const renderFn = (tag: string, attrs: Record<string, any>, text?: string) => {
* return `<${tag} ${Object.keys(attrs).map(key => `${key}="${attrs[key]}"`).join(' ')}>${text}</${tag}>`
* }
*
* const options: StoryblokRichTextOptions = {
* renderFn
* }
* ```
*/
renderFn?: S;
/**
* Defines the function that will be used to render HTML text.
*
* @example
*
* ```typescript
* import { h, createTextVNode } from 'vue'
*
* const options: StoryblokRichTextOptions = {
* renderFn: h,
* textFn: createTextVNode
* }
* ```
*/
textFn?: (text: string, attrs?: Record<string, any>) => T;
/**
* Defines the resolvers for each type of node.
*
* @example
*
* ```typescript
* const options: StoryblokRichTextOptions = {
* resolvers: {
* [MarkTypes.LINK]: (node: StoryblokRichTextNode) => {
* return `<a href="${node.attrs.href}">${node.text}</a>`
* }
* }
* }
* ```
*/
resolvers?: StoryblokRichTextResolvers<T>;
/**
* Defines opt-out image optimization options.
*
* @example
*
* ```typescript
* const options: StoryblokRichTextOptions = {
* optimizeImages: true
* }
* ```
*
* @example
*
* ```typescript
* const options: StoryblokRichTextOptions = {
* optimizeImages: {
* class: 'my-image',
* width: 800,
* height: 600,
* loading: 'lazy',
* }
* ```
*/
optimizeImages?: boolean | Partial<StoryblokRichTextImageOptimizationOptions>;
/**
* Defines whether to use the key attribute in the resolvers for framework use cases.
* @default false
* @example
*
* ```typescript
*
* const options: StoryblokRichTextOptions = {
* renderFn: h,
* keyedResolvers: true
* }
* ```
*/
keyedResolvers?: boolean;
}
//#endregion
//#region src/markdown-parser.d.ts
/**
* markdown-it Token interface definition
*/
interface MarkdownToken {
type: string;
tag: string;
attrs: Array<[string, string]> | null;
map: [number, number] | null;
nesting: 1 | 0 | -1;
level: number;
children: MarkdownToken[] | null;
content: string;
markup: string;
info: string;
meta: any;
block: boolean;
hidden: boolean;
attrGet: (name: string) => string | null;
attrSet: (name: string, value: string) => void;
attrPush: (attrData: [string, string]) => void;
attrJoin: (name: string, value: string) => void;
attrIndex: (name: string) => number;
}
/**
* Type for a Markdown element resolver.
*/
type MarkdownNodeResolver = (token: MarkdownToken, children: StoryblokRichTextDocumentNode[] | undefined) => StoryblokRichTextDocumentNode | null;
/**
* Options for the markdown parser, allowing custom resolvers.
*/
interface MarkdownParserOptions {
resolvers?: Partial<Record<string, MarkdownNodeResolver>>;
}
/**
* Supported Markdown token types as constants for maintainability and type safety.
* @see https://markdown-it.github.io/token-class.html
*/
declare const MarkdownTokenTypes: {
readonly HEADING: "heading_open";
readonly PARAGRAPH: "paragraph_open";
readonly TEXT: "text";
readonly STRONG: "strong_open";
readonly EMP: "em_open";
readonly ORDERED_LIST: "ordered_list_open";
readonly BULLET_LIST: "bullet_list_open";
readonly LIST_ITEM: "list_item_open";
readonly IMAGE: "image";
readonly BLOCKQUOTE: "blockquote_open";
readonly CODE_INLINE: "code_inline";
readonly CODE_BLOCK: "code_block";
readonly FENCE: "fence";
readonly LINK: "link_open";
readonly HR: "hr";
readonly DEL: "del_open";
readonly HARD_BREAK: "hardbreak";
readonly SOFT_BREAK: "softbreak";
readonly TABLE: "table_open";
readonly THEAD: "thead_open";
readonly TBODY: "tbody_open";
readonly TR: "tr_open";
readonly TH: "th_open";
readonly TD: "td_open";
readonly S: "s_open";
};
type MarkdownTokenType = keyof typeof MarkdownTokenTypes;
/**
* Converts Markdown string to Storyblok Richtext Document Node using resolvers.
* @param markdown - The markdown string to convert
* @param options - Optional custom resolvers
* @returns StoryblokRichTextDocumentNode
*/
declare function markdownToStoryblokRichtext(markdown: string, options?: MarkdownParserOptions): StoryblokRichTextDocumentNode;
//#endregion
//#region src/richtext.d.ts
/**
* Creates a rich text resolver with the given options.
*
* @export
* @template T
* @param {StoryblokRichTextOptions<T>} [options]
* @return {*}
*/
declare function richTextResolver<T>(options?: StoryblokRichTextOptions<T>): {
render: (node: StoryblokRichTextNode<T>) => T;
};
//#endregion
export { BlockTypes, LinkNode, LinkTargets, LinkTypes, MarkNode, MarkTypes, MarkdownNodeResolver, MarkdownParserOptions, MarkdownToken, MarkdownTokenType, MarkdownTokenTypes, StoryblokRichTextContext, StoryblokRichTextDocumentNode, StoryblokRichTextImageOptimizationOptions, StoryblokRichTextNode, StoryblokRichTextNodeResolver, StoryblokRichTextNodeTypes, StoryblokRichTextOptions, StoryblokRichTextResolvers, TextNode, TextTypes, markdownToStoryblokRichtext, richTextResolver };
//# sourceMappingURL=index.d.ts.map