BookStack/resources/js/wysiwyg/nodes/index.ts
Dan Brown 111a313d51
Lexical: Added custom alignment handling for blocks
To align with pre-existing use of alignment classes.
2024-08-18 16:51:08 +01:00

128 lines
4.2 KiB
TypeScript

import {HeadingNode, QuoteNode} from '@lexical/rich-text';
import {CalloutNode} from './callout';
import {
ElementNode,
KlassConstructor,
LexicalNode,
LexicalNodeReplacement, NodeMutation,
ParagraphNode
} from "lexical";
import {CustomParagraphNode} from "./custom-paragraph";
import {LinkNode} from "@lexical/link";
import {ImageNode} from "./image";
import {DetailsNode, SummaryNode} from "./details";
import {ListItemNode, ListNode} from "@lexical/list";
import {TableCellNode, TableNode, TableRowNode} from "@lexical/table";
import {CustomTableNode} from "./custom-table";
import {HorizontalRuleNode} from "./horizontal-rule";
import {CodeBlockNode} from "./code-block";
import {DiagramNode} from "./diagram";
import {EditorUiContext} from "../ui/framework/core";
import {MediaNode} from "./media";
import {CustomListItemNode} from "./custom-list-item";
import {CustomTableCellNode} from "./custom-table-cell";
import {CustomTableRowNode} from "./custom-table-row";
import {CustomHeadingNode} from "./custom-heading";
import {CustomQuoteNode} from "./custom-quote";
import {CustomListNode} from "./custom-list";
/**
* Load the nodes for lexical.
*/
export function getNodesForPageEditor(): (KlassConstructor<typeof LexicalNode> | LexicalNodeReplacement)[] {
return [
CalloutNode,
CustomHeadingNode,
CustomQuoteNode,
CustomListNode,
CustomListItemNode, // TODO - Alignment?
CustomTableNode,
CustomTableRowNode,
CustomTableCellNode,
ImageNode, // TODO - Alignment
HorizontalRuleNode,
DetailsNode, SummaryNode,
CodeBlockNode,
DiagramNode,
MediaNode, // TODO - Alignment
CustomParagraphNode,
LinkNode,
{
replace: ParagraphNode,
with: (node: ParagraphNode) => {
return new CustomParagraphNode();
}
},
{
replace: HeadingNode,
with: (node: HeadingNode) => {
return new CustomHeadingNode(node.__tag);
}
},
{
replace: QuoteNode,
with: (node: QuoteNode) => {
return new CustomQuoteNode();
}
},
{
replace: ListNode,
with: (node: ListNode) => {
return new CustomListNode(node.getListType(), node.getStart());
}
},
{
replace: ListItemNode,
with: (node: ListItemNode) => {
return new CustomListItemNode(node.__value, node.__checked);
}
},
{
replace: TableNode,
with(node: TableNode) {
return new CustomTableNode();
}
},
{
replace: TableRowNode,
with(node: TableRowNode) {
return new CustomTableRowNode();
}
},
{
replace: TableCellNode,
with: (node: TableCellNode) => {
const cell = new CustomTableCellNode(
node.__headerState,
node.__colSpan,
node.__width,
);
cell.__rowSpan = node.__rowSpan;
return cell;
}
},
];
}
export function registerCommonNodeMutationListeners(context: EditorUiContext): void {
const decorated = [ImageNode, CodeBlockNode, DiagramNode];
const decorationDestroyListener = (mutations: Map<string, NodeMutation>): void => {
for (let [nodeKey, mutation] of mutations) {
if (mutation === "destroyed") {
const decorator = context.manager.getDecoratorByNodeKey(nodeKey);
if (decorator) {
decorator.destroy(context);
}
}
}
};
for (let decoratedNode of decorated) {
// Have to pass a unique function here since they are stored by lexical keyed on listener function.
context.editor.registerMutationListener(decoratedNode, (mutations) => decorationDestroyListener(mutations));
}
}
export type LexicalNodeMatcher = (node: LexicalNode|null|undefined) => boolean;
export type LexicalElementNodeCreator = () => ElementNode;