mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-10-01 01:36:00 -04:00
Lexical: Standardised helper function format
This commit is contained in:
parent
b367490edc
commit
5002a89754
@ -4,7 +4,7 @@ import {
|
||||
$getSelection, $isElementNode,
|
||||
$isTextNode, $setSelection,
|
||||
BaseSelection, ElementFormatType, ElementNode,
|
||||
LexicalEditor, LexicalNode, TextFormatType
|
||||
LexicalNode, TextFormatType
|
||||
} from "lexical";
|
||||
import {LexicalElementNodeCreator, LexicalNodeMatcher} from "./nodes";
|
||||
import {$findMatchingParent, $getNearestBlockElementAncestorOrThrow} from "@lexical/utils";
|
||||
@ -30,11 +30,11 @@ export function el(tag: string, attrs: Record<string, string|null> = {}, childre
|
||||
return el;
|
||||
}
|
||||
|
||||
export function selectionContainsNodeType(selection: BaseSelection|null, matcher: LexicalNodeMatcher): boolean {
|
||||
return getNodeFromSelection(selection, matcher) !== null;
|
||||
export function $selectionContainsNodeType(selection: BaseSelection|null, matcher: LexicalNodeMatcher): boolean {
|
||||
return $getNodeFromSelection(selection, matcher) !== null;
|
||||
}
|
||||
|
||||
export function getNodeFromSelection(selection: BaseSelection|null, matcher: LexicalNodeMatcher): LexicalNode|null {
|
||||
export function $getNodeFromSelection(selection: BaseSelection|null, matcher: LexicalNodeMatcher): LexicalNode|null {
|
||||
if (!selection) {
|
||||
return null;
|
||||
}
|
||||
@ -54,7 +54,7 @@ export function getNodeFromSelection(selection: BaseSelection|null, matcher: Lex
|
||||
return null;
|
||||
}
|
||||
|
||||
export function selectionContainsTextFormat(selection: BaseSelection|null, format: TextFormatType): boolean {
|
||||
export function $selectionContainsTextFormat(selection: BaseSelection|null, format: TextFormatType): boolean {
|
||||
if (!selection) {
|
||||
return false;
|
||||
}
|
||||
@ -68,8 +68,7 @@ export function selectionContainsTextFormat(selection: BaseSelection|null, forma
|
||||
return false;
|
||||
}
|
||||
|
||||
export function toggleSelectionBlockNodeType(editor: LexicalEditor, matcher: LexicalNodeMatcher, creator: LexicalElementNodeCreator) {
|
||||
editor.update(() => {
|
||||
export function $toggleSelectionBlockNodeType(matcher: LexicalNodeMatcher, creator: LexicalElementNodeCreator) {
|
||||
const selection = $getSelection();
|
||||
const blockElement = selection ? $getNearestBlockElementAncestorOrThrow(selection.getNodes()[0]) : null;
|
||||
if (selection && matcher(blockElement)) {
|
||||
@ -77,10 +76,9 @@ export function toggleSelectionBlockNodeType(editor: LexicalEditor, matcher: Lex
|
||||
} else {
|
||||
$setBlocksType(selection, creator);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function insertNewBlockNodeAtSelection(node: LexicalNode, insertAfter: boolean = true) {
|
||||
export function $insertNewBlockNodeAtSelection(node: LexicalNode, insertAfter: boolean = true) {
|
||||
const selection = $getSelection();
|
||||
const blockElement = selection ? $getNearestBlockElementAncestorOrThrow(selection.getNodes()[0]) : null;
|
||||
|
||||
@ -95,13 +93,13 @@ export function insertNewBlockNodeAtSelection(node: LexicalNode, insertAfter: bo
|
||||
}
|
||||
}
|
||||
|
||||
export function selectSingleNode(node: LexicalNode) {
|
||||
export function $selectSingleNode(node: LexicalNode) {
|
||||
const nodeSelection = $createNodeSelection();
|
||||
nodeSelection.add(node.getKey());
|
||||
$setSelection(nodeSelection);
|
||||
}
|
||||
|
||||
export function selectionContainsNode(selection: BaseSelection|null, node: LexicalNode): boolean {
|
||||
export function $selectionContainsNode(selection: BaseSelection|null, node: LexicalNode): boolean {
|
||||
if (!selection) {
|
||||
return false;
|
||||
}
|
||||
@ -116,8 +114,8 @@ export function selectionContainsNode(selection: BaseSelection|null, node: Lexic
|
||||
return false;
|
||||
}
|
||||
|
||||
export function selectionContainsElementFormat(selection: BaseSelection|null, format: ElementFormatType): boolean {
|
||||
const nodes = getBlockElementNodesInSelection(selection);
|
||||
export function $selectionContainsElementFormat(selection: BaseSelection|null, format: ElementFormatType): boolean {
|
||||
const nodes = $getBlockElementNodesInSelection(selection);
|
||||
for (const node of nodes) {
|
||||
if (node.getFormatType() === format) {
|
||||
return true;
|
||||
@ -127,7 +125,7 @@ export function selectionContainsElementFormat(selection: BaseSelection|null, fo
|
||||
return false;
|
||||
}
|
||||
|
||||
export function getBlockElementNodesInSelection(selection: BaseSelection|null): ElementNode[] {
|
||||
export function $getBlockElementNodesInSelection(selection: BaseSelection|null): ElementNode[] {
|
||||
if (!selection) {
|
||||
return [];
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {EditorDecorator} from "../framework/decorator";
|
||||
import {EditorUiContext} from "../framework/core";
|
||||
import {$openCodeEditorForNode, CodeBlockNode} from "../../nodes/code-block";
|
||||
import {selectionContainsNode, selectSingleNode} from "../../helpers";
|
||||
import {$selectionContainsNode, $selectSingleNode} from "../../helpers";
|
||||
import {context} from "esbuild";
|
||||
import {BaseSelection} from "lexical";
|
||||
|
||||
@ -36,7 +36,7 @@ export class CodeBlockDecorator extends EditorDecorator {
|
||||
|
||||
element.addEventListener('click', event => {
|
||||
context.editor.update(() => {
|
||||
selectSingleNode(this.getNode());
|
||||
$selectSingleNode(this.getNode());
|
||||
})
|
||||
});
|
||||
|
||||
@ -47,7 +47,7 @@ export class CodeBlockDecorator extends EditorDecorator {
|
||||
});
|
||||
|
||||
const selectionChange = (selection: BaseSelection|null): void => {
|
||||
element.classList.toggle('selected', selectionContainsNode(selection, codeNode));
|
||||
element.classList.toggle('selected', $selectionContainsNode(selection, codeNode));
|
||||
};
|
||||
context.manager.onSelectionChange(selectionChange);
|
||||
this.onDestroy(() => {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {EditorDecorator} from "../framework/decorator";
|
||||
import {el, selectSingleNode} from "../../helpers";
|
||||
import {el, $selectSingleNode} from "../../helpers";
|
||||
import {$createNodeSelection, $setSelection} from "lexical";
|
||||
import {EditorUiContext} from "../framework/core";
|
||||
import {ImageNode} from "../../nodes/image";
|
||||
@ -41,7 +41,7 @@ export class ImageDecorator extends EditorDecorator {
|
||||
tracker = this.setupTracker(decorateEl, context);
|
||||
|
||||
context.editor.update(() => {
|
||||
selectSingleNode(this.getNode());
|
||||
$selectSingleNode(this.getNode());
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -21,11 +21,11 @@ import {
|
||||
UNDO_COMMAND
|
||||
} from "lexical";
|
||||
import {
|
||||
getBlockElementNodesInSelection,
|
||||
getNodeFromSelection, insertNewBlockNodeAtSelection, selectionContainsElementFormat,
|
||||
selectionContainsNodeType,
|
||||
selectionContainsTextFormat,
|
||||
toggleSelectionBlockNodeType
|
||||
$getBlockElementNodesInSelection,
|
||||
$getNodeFromSelection, $insertNewBlockNodeAtSelection, $selectionContainsElementFormat,
|
||||
$selectionContainsNodeType,
|
||||
$selectionContainsTextFormat,
|
||||
$toggleSelectionBlockNodeType
|
||||
} from "../../helpers";
|
||||
import {$createCalloutNode, $isCalloutNodeOfCategory, CalloutCategory} from "../../nodes/callout";
|
||||
import {
|
||||
@ -116,14 +116,15 @@ function buildCalloutButton(category: CalloutCategory, name: string): EditorButt
|
||||
return {
|
||||
label: `${name} Callout`,
|
||||
action(context: EditorUiContext) {
|
||||
toggleSelectionBlockNodeType(
|
||||
context.editor,
|
||||
context.editor.update(() => {
|
||||
$toggleSelectionBlockNodeType(
|
||||
(node) => $isCalloutNodeOfCategory(node, category),
|
||||
() => $createCalloutNode(category),
|
||||
)
|
||||
});
|
||||
},
|
||||
isActive(selection: BaseSelection|null): boolean {
|
||||
return selectionContainsNodeType(selection, (node) => $isCalloutNodeOfCategory(node, category));
|
||||
return $selectionContainsNodeType(selection, (node) => $isCalloutNodeOfCategory(node, category));
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -141,14 +142,15 @@ function buildHeaderButton(tag: HeadingTagType, name: string): EditorButtonDefin
|
||||
return {
|
||||
label: name,
|
||||
action(context: EditorUiContext) {
|
||||
toggleSelectionBlockNodeType(
|
||||
context.editor,
|
||||
context.editor.update(() => {
|
||||
$toggleSelectionBlockNodeType(
|
||||
(node) => isHeaderNodeOfTag(node, tag),
|
||||
() => $createHeadingNode(tag),
|
||||
)
|
||||
});
|
||||
},
|
||||
isActive(selection: BaseSelection|null): boolean {
|
||||
return selectionContainsNodeType(selection, (node) => isHeaderNodeOfTag(node, tag));
|
||||
return $selectionContainsNodeType(selection, (node) => isHeaderNodeOfTag(node, tag));
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -161,20 +163,24 @@ export const h5: EditorButtonDefinition = buildHeaderButton('h5', 'Tiny Header')
|
||||
export const blockquote: EditorButtonDefinition = {
|
||||
label: 'Blockquote',
|
||||
action(context: EditorUiContext) {
|
||||
toggleSelectionBlockNodeType(context.editor, $isQuoteNode, $createQuoteNode);
|
||||
context.editor.update(() => {
|
||||
$toggleSelectionBlockNodeType($isQuoteNode, $createQuoteNode);
|
||||
});
|
||||
},
|
||||
isActive(selection: BaseSelection|null): boolean {
|
||||
return selectionContainsNodeType(selection, $isQuoteNode);
|
||||
return $selectionContainsNodeType(selection, $isQuoteNode);
|
||||
}
|
||||
};
|
||||
|
||||
export const paragraph: EditorButtonDefinition = {
|
||||
label: 'Paragraph',
|
||||
action(context: EditorUiContext) {
|
||||
toggleSelectionBlockNodeType(context.editor, $isParagraphNode, $createParagraphNode);
|
||||
context.editor.update(() => {
|
||||
$toggleSelectionBlockNodeType($isParagraphNode, $createParagraphNode);
|
||||
});
|
||||
},
|
||||
isActive(selection: BaseSelection|null): boolean {
|
||||
return selectionContainsNodeType(selection, $isParagraphNode);
|
||||
return $selectionContainsNodeType(selection, $isParagraphNode);
|
||||
}
|
||||
}
|
||||
|
||||
@ -186,7 +192,7 @@ function buildFormatButton(label: string, format: TextFormatType, icon: string):
|
||||
context.editor.dispatchCommand(FORMAT_TEXT_COMMAND, format);
|
||||
},
|
||||
isActive(selection: BaseSelection|null): boolean {
|
||||
return selectionContainsTextFormat(selection, format);
|
||||
return $selectionContainsTextFormat(selection, format);
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -222,7 +228,7 @@ export const clearFormating: EditorButtonDefinition = {
|
||||
|
||||
function setAlignmentForSection(alignment: ElementFormatType): void {
|
||||
const selection = $getSelection();
|
||||
const elements = getBlockElementNodesInSelection(selection);
|
||||
const elements = $getBlockElementNodesInSelection(selection);
|
||||
for (const node of elements) {
|
||||
node.setFormat(alignment);
|
||||
}
|
||||
@ -235,7 +241,7 @@ export const alignLeft: EditorButtonDefinition = {
|
||||
context.editor.update(() => setAlignmentForSection('left'));
|
||||
},
|
||||
isActive(selection: BaseSelection|null) {
|
||||
return selectionContainsElementFormat(selection, 'left');
|
||||
return $selectionContainsElementFormat(selection, 'left');
|
||||
}
|
||||
};
|
||||
|
||||
@ -246,7 +252,7 @@ export const alignCenter: EditorButtonDefinition = {
|
||||
context.editor.update(() => setAlignmentForSection('center'));
|
||||
},
|
||||
isActive(selection: BaseSelection|null) {
|
||||
return selectionContainsElementFormat(selection, 'center');
|
||||
return $selectionContainsElementFormat(selection, 'center');
|
||||
}
|
||||
};
|
||||
|
||||
@ -257,7 +263,7 @@ export const alignRight: EditorButtonDefinition = {
|
||||
context.editor.update(() => setAlignmentForSection('right'));
|
||||
},
|
||||
isActive(selection: BaseSelection|null) {
|
||||
return selectionContainsElementFormat(selection, 'right');
|
||||
return $selectionContainsElementFormat(selection, 'right');
|
||||
}
|
||||
};
|
||||
|
||||
@ -268,7 +274,7 @@ export const alignJustify: EditorButtonDefinition = {
|
||||
context.editor.update(() => setAlignmentForSection('justify'));
|
||||
},
|
||||
isActive(selection: BaseSelection|null) {
|
||||
return selectionContainsElementFormat(selection, 'justify');
|
||||
return $selectionContainsElementFormat(selection, 'justify');
|
||||
}
|
||||
};
|
||||
|
||||
@ -288,7 +294,7 @@ function buildListButton(label: string, type: ListType, icon: string): EditorBut
|
||||
});
|
||||
},
|
||||
isActive(selection: BaseSelection|null): boolean {
|
||||
return selectionContainsNodeType(selection, (node: LexicalNode | null | undefined): boolean => {
|
||||
return $selectionContainsNodeType(selection, (node: LexicalNode | null | undefined): boolean => {
|
||||
return $isListNode(node) && (node as ListNode).getListType() === type;
|
||||
});
|
||||
}
|
||||
@ -307,7 +313,7 @@ export const link: EditorButtonDefinition = {
|
||||
const linkModal = context.manager.createModal('link');
|
||||
context.editor.getEditorState().read(() => {
|
||||
const selection = $getSelection();
|
||||
const selectedLink = getNodeFromSelection(selection, $isLinkNode) as LinkNode|null;
|
||||
const selectedLink = $getNodeFromSelection(selection, $isLinkNode) as LinkNode|null;
|
||||
|
||||
let formDefaults = {};
|
||||
if (selectedLink) {
|
||||
@ -329,7 +335,7 @@ export const link: EditorButtonDefinition = {
|
||||
});
|
||||
},
|
||||
isActive(selection: BaseSelection|null): boolean {
|
||||
return selectionContainsNodeType(selection, $isLinkNode);
|
||||
return $selectionContainsNodeType(selection, $isLinkNode);
|
||||
}
|
||||
};
|
||||
|
||||
@ -339,7 +345,7 @@ export const unlink: EditorButtonDefinition = {
|
||||
action(context: EditorUiContext) {
|
||||
context.editor.update(() => {
|
||||
const selection = context.lastSelection;
|
||||
const selectedLink = getNodeFromSelection(selection, $isLinkNode) as LinkNode|null;
|
||||
const selectedLink = $getNodeFromSelection(selection, $isLinkNode) as LinkNode|null;
|
||||
const selectionPoints = selection?.getStartEndPoints();
|
||||
|
||||
if (selectedLink) {
|
||||
@ -369,7 +375,7 @@ export const image: EditorButtonDefinition = {
|
||||
action(context: EditorUiContext) {
|
||||
const imageModal = context.manager.createModal('image');
|
||||
const selection = context.lastSelection;
|
||||
const selectedImage = getNodeFromSelection(selection, $isImageNode) as ImageNode|null;
|
||||
const selectedImage = $getNodeFromSelection(selection, $isImageNode) as ImageNode|null;
|
||||
|
||||
context.editor.getEditorState().read(() => {
|
||||
let formDefaults = {};
|
||||
@ -392,7 +398,7 @@ export const image: EditorButtonDefinition = {
|
||||
});
|
||||
},
|
||||
isActive(selection: BaseSelection|null): boolean {
|
||||
return selectionContainsNodeType(selection, $isImageNode);
|
||||
return $selectionContainsNodeType(selection, $isImageNode);
|
||||
}
|
||||
};
|
||||
|
||||
@ -401,11 +407,11 @@ export const horizontalRule: EditorButtonDefinition = {
|
||||
icon: horizontalRuleIcon,
|
||||
action(context: EditorUiContext) {
|
||||
context.editor.update(() => {
|
||||
insertNewBlockNodeAtSelection($createHorizontalRuleNode(), false);
|
||||
$insertNewBlockNodeAtSelection($createHorizontalRuleNode(), false);
|
||||
});
|
||||
},
|
||||
isActive(selection: BaseSelection|null): boolean {
|
||||
return selectionContainsNodeType(selection, $isHorizontalRuleNode);
|
||||
return $selectionContainsNodeType(selection, $isHorizontalRuleNode);
|
||||
}
|
||||
};
|
||||
|
||||
@ -415,12 +421,12 @@ export const codeBlock: EditorButtonDefinition = {
|
||||
action(context: EditorUiContext) {
|
||||
context.editor.getEditorState().read(() => {
|
||||
const selection = $getSelection();
|
||||
const codeBlock = getNodeFromSelection(context.lastSelection, $isCodeBlockNode) as (CodeBlockNode|null);
|
||||
const codeBlock = $getNodeFromSelection(context.lastSelection, $isCodeBlockNode) as (CodeBlockNode|null);
|
||||
if (codeBlock === null) {
|
||||
context.editor.update(() => {
|
||||
const codeBlock = $createCodeBlockNode();
|
||||
codeBlock.setCode(selection?.getTextContent() || '');
|
||||
insertNewBlockNodeAtSelection(codeBlock, true);
|
||||
$insertNewBlockNodeAtSelection(codeBlock, true);
|
||||
$openCodeEditorForNode(context.editor, codeBlock);
|
||||
codeBlock.selectStart();
|
||||
});
|
||||
@ -430,7 +436,7 @@ export const codeBlock: EditorButtonDefinition = {
|
||||
});
|
||||
},
|
||||
isActive(selection: BaseSelection|null): boolean {
|
||||
return selectionContainsNodeType(selection, $isCodeBlockNode);
|
||||
return $selectionContainsNodeType(selection, $isCodeBlockNode);
|
||||
}
|
||||
};
|
||||
|
||||
@ -463,7 +469,7 @@ export const details: EditorButtonDefinition = {
|
||||
});
|
||||
},
|
||||
isActive(selection: BaseSelection|null): boolean {
|
||||
return selectionContainsNodeType(selection, $isDetailsNode);
|
||||
return $selectionContainsNodeType(selection, $isDetailsNode);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {el, insertNewBlockNodeAtSelection} from "../../../helpers";
|
||||
import {el, $insertNewBlockNodeAtSelection} from "../../../helpers";
|
||||
import {EditorUiElement} from "../core";
|
||||
import {$createTableNodeWithDimensions} from "@lexical/table";
|
||||
import {CustomTableNode} from "../../../nodes/custom-table";
|
||||
@ -75,7 +75,7 @@ export class EditorTableCreator extends EditorUiElement {
|
||||
|
||||
this.getContext().editor.update(() => {
|
||||
const table = $createTableNodeWithDimensions(rows, columns, false) as CustomTableNode;
|
||||
insertNewBlockNodeAtSelection(table);
|
||||
$insertNewBlockNodeAtSelection(table);
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user