UNPKG

@mkljczk/lexical-remark

Version:

This package contains Markdown helpers and functionality for Lexical using remark-parse.

144 lines (125 loc) 3.63 kB
/* eslint-disable @typescript-eslint/no-use-before-define */ import { DOMConversionMap, DOMConversionOutput, DOMExportOutput, EditorConfig, ElementNode, LexicalEditor, LexicalNode, NodeKey, SerializedElementNode, Spread, } from 'lexical'; type SerializedCollapsibleContainerNode = Spread< { open: boolean; }, SerializedElementNode >; export function convertDetailsElement(domNode: HTMLDetailsElement): DOMConversionOutput | null { const openAttr = domNode.getAttribute('open'); const isOpen = openAttr !== null ? openAttr === 'true' : true; const node = $createCollapsibleContainerNode(isOpen); return { node, }; } /** * A Lexical node to represent an HTML details container */ export class CollapsibleContainerNode extends ElementNode { __open: boolean; constructor(open: boolean, key?: NodeKey) { super(key); this.__open = open; } static getType(): string { return 'collapsible-container'; } static clone(node: CollapsibleContainerNode): CollapsibleContainerNode { return new CollapsibleContainerNode(node.__open, node.__key); } createDOM(config: EditorConfig, editor: LexicalEditor): HTMLElement { const dom = document.createElement('details'); dom.classList.add('Collapsible__container'); dom.open = this.__open; dom.addEventListener('toggle', () => { const open = editor.getEditorState().read(() => this.getOpen()); if (open !== dom.open) { editor.update(() => this.toggleOpen()); } }); return dom; } updateDOM(prevNode: CollapsibleContainerNode, dom: HTMLDetailsElement): boolean { if (prevNode.__open !== this.__open) { dom.open = this.__open; } return false; } static importDOM(): DOMConversionMap<HTMLDetailsElement> | null { return { details: (domNode: HTMLDetailsElement) => { return { conversion: convertDetailsElement, priority: 1, }; }, }; } static importJSON(serializedNode: SerializedCollapsibleContainerNode): CollapsibleContainerNode { const node = $createCollapsibleContainerNode(serializedNode.open); return node; } exportDOM(): DOMExportOutput { const element = document.createElement('details'); element.setAttribute('open', this.__open.toString()); return { element }; } exportJSON(): SerializedCollapsibleContainerNode { return { ...super.exportJSON(), open: this.__open, type: 'collapsible-container', version: 1, }; } /** * Sets the open state of the details container */ setOpen(open: boolean): void { const writable = this.getWritable(); writable.__open = open; } /** * Gets the open state of the details container */ getOpen(): boolean { return this.getLatest().__open; } /** * Toggles the open state of the details container */ toggleOpen(): void { this.setOpen(!this.getOpen()); } } /** * Creates a Collapsible Container node with an initial open state * * @param isOpen The initial open state of the container * @returns A Collapsible Container node */ export function $createCollapsibleContainerNode(isOpen: boolean): CollapsibleContainerNode { return new CollapsibleContainerNode(isOpen); } /** * A typeguard function to assert on a Collapsible Container node * * @param node A Lexical node * @returns true if the node is a Collapsible Container node, otherwise false */ export function $isCollapsibleContainerNode(node: LexicalNode | null | undefined): node is CollapsibleContainerNode { return node instanceof CollapsibleContainerNode; }