mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-10-01 01:36:00 -04:00
Lexical: Finished off baseline shortcut implementation
This commit is contained in:
parent
aa1fac62d5
commit
dbb2fe3e59
@ -45,11 +45,12 @@ export function createPageEditorInstance(container: HTMLElement, htmlContent: st
|
|||||||
|
|
||||||
const editor = createEditor(config);
|
const editor = createEditor(config);
|
||||||
editor.setRootElement(editArea);
|
editor.setRootElement(editArea);
|
||||||
|
const context: EditorUiContext = buildEditorUI(container, editArea, editWrap, editor, options);
|
||||||
|
|
||||||
mergeRegister(
|
mergeRegister(
|
||||||
registerRichText(editor),
|
registerRichText(editor),
|
||||||
registerHistory(editor, createEmptyHistoryState(), 300),
|
registerHistory(editor, createEmptyHistoryState(), 300),
|
||||||
registerShortcuts(editor),
|
registerShortcuts(context),
|
||||||
registerTableResizer(editor, editWrap),
|
registerTableResizer(editor, editWrap),
|
||||||
registerTableSelectionHandler(editor),
|
registerTableSelectionHandler(editor),
|
||||||
registerTaskListHandler(editor, editArea),
|
registerTaskListHandler(editor, editArea),
|
||||||
@ -89,7 +90,6 @@ export function createPageEditorInstance(container: HTMLElement, htmlContent: st
|
|||||||
console.log(editor.getEditorState().toJSON());
|
console.log(editor.getEditorState().toJSON());
|
||||||
};
|
};
|
||||||
|
|
||||||
const context: EditorUiContext = buildEditorUI(container, editArea, editWrap, editor, options);
|
|
||||||
registerCommonNodeMutationListeners(context);
|
registerCommonNodeMutationListeners(context);
|
||||||
|
|
||||||
return new SimpleWysiwygEditorInterface(editor);
|
return new SimpleWysiwygEditorInterface(editor);
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
import {COMMAND_PRIORITY_HIGH, FORMAT_TEXT_COMMAND, KEY_ENTER_COMMAND, LexicalEditor} from "lexical";
|
import {$getSelection, COMMAND_PRIORITY_HIGH, FORMAT_TEXT_COMMAND, KEY_ENTER_COMMAND, LexicalEditor} from "lexical";
|
||||||
import {
|
import {
|
||||||
cycleSelectionCalloutFormats,
|
cycleSelectionCalloutFormats,
|
||||||
formatCodeBlock,
|
formatCodeBlock, insertOrUpdateLink,
|
||||||
toggleSelectionAsBlockquote,
|
toggleSelectionAsBlockquote,
|
||||||
toggleSelectionAsHeading,
|
toggleSelectionAsHeading, toggleSelectionAsList,
|
||||||
toggleSelectionAsParagraph
|
toggleSelectionAsParagraph
|
||||||
} from "../utils/formats";
|
} from "../utils/formats";
|
||||||
import {HeadingTagType} from "@lexical/rich-text";
|
import {HeadingTagType} from "@lexical/rich-text";
|
||||||
|
import {EditorUiContext} from "../ui/framework/core";
|
||||||
|
import {$getNodeFromSelection} from "../utils/selection";
|
||||||
|
import {$isLinkNode, LinkNode} from "@lexical/link";
|
||||||
|
import {$showLinkForm} from "../ui/defaults/forms/objects";
|
||||||
|
import {showLinkSelector} from "../utils/links";
|
||||||
|
|
||||||
function headerHandler(editor: LexicalEditor, tag: HeadingTagType): boolean {
|
function headerHandler(editor: LexicalEditor, tag: HeadingTagType): boolean {
|
||||||
toggleSelectionAsHeading(editor, tag);
|
toggleSelectionAsHeading(editor, tag);
|
||||||
@ -25,10 +30,9 @@ function toggleInlineCode(editor: LexicalEditor): boolean {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
type ShortcutAction = (editor: LexicalEditor) => boolean;
|
type ShortcutAction = (editor: LexicalEditor, context: EditorUiContext) => boolean;
|
||||||
|
|
||||||
const actionsByKeys: Record<string, ShortcutAction> = {
|
const actionsByKeys: Record<string, ShortcutAction> = {
|
||||||
// Save draft
|
|
||||||
'ctrl+s': () => {
|
'ctrl+s': () => {
|
||||||
window.$events.emit('editor-save-draft');
|
window.$events.emit('editor-save-draft');
|
||||||
return true;
|
return true;
|
||||||
@ -51,18 +55,35 @@ const actionsByKeys: Record<string, ShortcutAction> = {
|
|||||||
'ctrl+shift+e': toggleInlineCode,
|
'ctrl+shift+e': toggleInlineCode,
|
||||||
'ctrl+9': wrapFormatAction(cycleSelectionCalloutFormats),
|
'ctrl+9': wrapFormatAction(cycleSelectionCalloutFormats),
|
||||||
|
|
||||||
// TODO Lists
|
'ctrl+o': wrapFormatAction((e) => toggleSelectionAsList(e, 'number')),
|
||||||
// TODO Links
|
'ctrl+p': wrapFormatAction((e) => toggleSelectionAsList(e, 'bullet')),
|
||||||
// TODO Link selector
|
'ctrl+k': (editor, context) => {
|
||||||
|
editor.getEditorState().read(() => {
|
||||||
|
const selectedLink = $getNodeFromSelection($getSelection(), $isLinkNode) as LinkNode | null;
|
||||||
|
$showLinkForm(selectedLink, context);
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
'ctrl+shift+k': (editor, context) => {
|
||||||
|
showLinkSelector(entity => {
|
||||||
|
insertOrUpdateLink(editor, {
|
||||||
|
text: entity.name,
|
||||||
|
title: entity.link,
|
||||||
|
target: '',
|
||||||
|
url: entity.link,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function createKeyDownListener(editor: LexicalEditor): (e: KeyboardEvent) => void {
|
function createKeyDownListener(context: EditorUiContext): (e: KeyboardEvent) => void {
|
||||||
return (event: KeyboardEvent) => {
|
return (event: KeyboardEvent) => {
|
||||||
// TODO - Mac Cmd support
|
// TODO - Mac Cmd support
|
||||||
const combo = `${event.ctrlKey ? 'ctrl+' : ''}${event.shiftKey ? 'shift+' : ''}${event.key}`.toLowerCase();
|
const combo = `${event.ctrlKey ? 'ctrl+' : ''}${event.shiftKey ? 'shift+' : ''}${event.key}`.toLowerCase();
|
||||||
console.log(`pressed: ${combo}`);
|
// console.log(`pressed: ${combo}`);
|
||||||
if (actionsByKeys[combo]) {
|
if (actionsByKeys[combo]) {
|
||||||
const handled = actionsByKeys[combo](editor);
|
const handled = actionsByKeys[combo](context.editor, context);
|
||||||
if (handled) {
|
if (handled) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@ -78,11 +99,11 @@ function overrideDefaultCommands(editor: LexicalEditor) {
|
|||||||
}, COMMAND_PRIORITY_HIGH);
|
}, COMMAND_PRIORITY_HIGH);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function registerShortcuts(editor: LexicalEditor) {
|
export function registerShortcuts(context: EditorUiContext) {
|
||||||
const listener = createKeyDownListener(editor);
|
const listener = createKeyDownListener(context);
|
||||||
overrideDefaultCommands(editor);
|
overrideDefaultCommands(context.editor);
|
||||||
|
|
||||||
return editor.registerRootListener((rootElement: null | HTMLElement, prevRootElement: null | HTMLElement) => {
|
return context.editor.registerRootListener((rootElement: null | HTMLElement, prevRootElement: null | HTMLElement) => {
|
||||||
// add the listener to the current root element
|
// add the listener to the current root element
|
||||||
rootElement?.addEventListener('keydown', listener);
|
rootElement?.addEventListener('keydown', listener);
|
||||||
// remove the listener from the old root element
|
// remove the listener from the old root element
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## In progress
|
## In progress
|
||||||
|
|
||||||
- Keyboard shortcuts support
|
//
|
||||||
|
|
||||||
## Main Todo
|
## Main Todo
|
||||||
|
|
||||||
@ -13,6 +13,7 @@
|
|||||||
- Media resize support (like images)
|
- Media resize support (like images)
|
||||||
- Table caption text support
|
- Table caption text support
|
||||||
- Table Cut/Copy/Paste column
|
- Table Cut/Copy/Paste column
|
||||||
|
- Mac: Shortcut support via command.
|
||||||
|
|
||||||
## Secondary Todo
|
## Secondary Todo
|
||||||
|
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import {$isListNode, insertList, ListNode, ListType, removeList} from "@lexical/list";
|
import {$isListNode, ListNode, ListType} from "@lexical/list";
|
||||||
import {EditorButtonDefinition} from "../../framework/buttons";
|
import {EditorButtonDefinition} from "../../framework/buttons";
|
||||||
import {EditorUiContext} from "../../framework/core";
|
import {EditorUiContext} from "../../framework/core";
|
||||||
import {$getSelection, BaseSelection, LexicalNode} from "lexical";
|
import {BaseSelection, LexicalNode} from "lexical";
|
||||||
import listBulletIcon from "@icons/editor/list-bullet.svg";
|
import listBulletIcon from "@icons/editor/list-bullet.svg";
|
||||||
import listNumberedIcon from "@icons/editor/list-numbered.svg";
|
import listNumberedIcon from "@icons/editor/list-numbered.svg";
|
||||||
import listCheckIcon from "@icons/editor/list-check.svg";
|
import listCheckIcon from "@icons/editor/list-check.svg";
|
||||||
import {$selectionContainsNodeType} from "../../../utils/selection";
|
import {$selectionContainsNodeType} from "../../../utils/selection";
|
||||||
|
import {toggleSelectionAsList} from "../../../utils/formats";
|
||||||
|
|
||||||
|
|
||||||
function buildListButton(label: string, type: ListType, icon: string): EditorButtonDefinition {
|
function buildListButton(label: string, type: ListType, icon: string): EditorButtonDefinition {
|
||||||
@ -13,14 +14,7 @@ function buildListButton(label: string, type: ListType, icon: string): EditorBut
|
|||||||
label,
|
label,
|
||||||
icon,
|
icon,
|
||||||
action(context: EditorUiContext) {
|
action(context: EditorUiContext) {
|
||||||
context.editor.getEditorState().read(() => {
|
toggleSelectionAsList(context.editor, type);
|
||||||
const selection = $getSelection();
|
|
||||||
if (this.isActive(selection, context)) {
|
|
||||||
removeList(context.editor);
|
|
||||||
} else {
|
|
||||||
insertList(context.editor, type);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
isActive(selection: BaseSelection|null): boolean {
|
isActive(selection: BaseSelection|null): boolean {
|
||||||
return $selectionContainsNodeType(selection, (node: LexicalNode | null | undefined): boolean => {
|
return $selectionContainsNodeType(selection, (node: LexicalNode | null | undefined): boolean => {
|
||||||
|
@ -2,11 +2,9 @@ import {EditorButtonDefinition} from "../../framework/buttons";
|
|||||||
import linkIcon from "@icons/editor/link.svg";
|
import linkIcon from "@icons/editor/link.svg";
|
||||||
import {EditorUiContext} from "../../framework/core";
|
import {EditorUiContext} from "../../framework/core";
|
||||||
import {
|
import {
|
||||||
$createNodeSelection,
|
|
||||||
$createTextNode,
|
$createTextNode,
|
||||||
$getRoot,
|
$getRoot,
|
||||||
$getSelection, $insertNodes,
|
$getSelection, $insertNodes,
|
||||||
$setSelection,
|
|
||||||
BaseSelection,
|
BaseSelection,
|
||||||
ElementNode
|
ElementNode
|
||||||
} from "lexical";
|
} from "lexical";
|
||||||
@ -17,7 +15,7 @@ import {$isImageNode, ImageNode} from "../../../nodes/image";
|
|||||||
import horizontalRuleIcon from "@icons/editor/horizontal-rule.svg";
|
import horizontalRuleIcon from "@icons/editor/horizontal-rule.svg";
|
||||||
import {$createHorizontalRuleNode, $isHorizontalRuleNode} from "../../../nodes/horizontal-rule";
|
import {$createHorizontalRuleNode, $isHorizontalRuleNode} from "../../../nodes/horizontal-rule";
|
||||||
import codeBlockIcon from "@icons/editor/code-block.svg";
|
import codeBlockIcon from "@icons/editor/code-block.svg";
|
||||||
import {$createCodeBlockNode, $isCodeBlockNode, $openCodeEditorForNode, CodeBlockNode} from "../../../nodes/code-block";
|
import {$isCodeBlockNode} from "../../../nodes/code-block";
|
||||||
import editIcon from "@icons/edit.svg";
|
import editIcon from "@icons/edit.svg";
|
||||||
import diagramIcon from "@icons/editor/diagram.svg";
|
import diagramIcon from "@icons/editor/diagram.svg";
|
||||||
import {$createDiagramNode, DiagramNode} from "../../../nodes/diagram";
|
import {$createDiagramNode, DiagramNode} from "../../../nodes/diagram";
|
||||||
@ -32,35 +30,16 @@ import {
|
|||||||
} from "../../../utils/selection";
|
} from "../../../utils/selection";
|
||||||
import {$isDiagramNode, $openDrawingEditorForNode, showDiagramManagerForInsert} from "../../../utils/diagrams";
|
import {$isDiagramNode, $openDrawingEditorForNode, showDiagramManagerForInsert} from "../../../utils/diagrams";
|
||||||
import {$createLinkedImageNodeFromImageData, showImageManager} from "../../../utils/images";
|
import {$createLinkedImageNodeFromImageData, showImageManager} from "../../../utils/images";
|
||||||
import {$showImageForm} from "../forms/objects";
|
import {$showImageForm, $showLinkForm} from "../forms/objects";
|
||||||
import {formatCodeBlock} from "../../../utils/formats";
|
import {formatCodeBlock} from "../../../utils/formats";
|
||||||
|
|
||||||
export const link: EditorButtonDefinition = {
|
export const link: EditorButtonDefinition = {
|
||||||
label: 'Insert/edit link',
|
label: 'Insert/edit link',
|
||||||
icon: linkIcon,
|
icon: linkIcon,
|
||||||
action(context: EditorUiContext) {
|
action(context: EditorUiContext) {
|
||||||
const linkModal = context.manager.createModal('link');
|
|
||||||
context.editor.getEditorState().read(() => {
|
context.editor.getEditorState().read(() => {
|
||||||
const selection = $getSelection();
|
const selectedLink = $getNodeFromSelection($getSelection(), $isLinkNode) as LinkNode | null;
|
||||||
const selectedLink = $getNodeFromSelection(selection, $isLinkNode) as LinkNode | null;
|
$showLinkForm(selectedLink, context);
|
||||||
|
|
||||||
let formDefaults = {};
|
|
||||||
if (selectedLink) {
|
|
||||||
formDefaults = {
|
|
||||||
url: selectedLink.getURL(),
|
|
||||||
text: selectedLink.getTextContent(),
|
|
||||||
title: selectedLink.getTitle(),
|
|
||||||
target: selectedLink.getTarget(),
|
|
||||||
}
|
|
||||||
|
|
||||||
context.editor.update(() => {
|
|
||||||
const selection = $createNodeSelection();
|
|
||||||
selection.add(selectedLink.getKey());
|
|
||||||
$setSelection(selection);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
linkModal.show(formDefaults);
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
isActive(selection: BaseSelection | null): boolean {
|
isActive(selection: BaseSelection | null): boolean {
|
||||||
|
@ -5,9 +5,9 @@ import {
|
|||||||
EditorSelectFormFieldDefinition
|
EditorSelectFormFieldDefinition
|
||||||
} from "../../framework/forms";
|
} from "../../framework/forms";
|
||||||
import {EditorUiContext} from "../../framework/core";
|
import {EditorUiContext} from "../../framework/core";
|
||||||
import {$createTextNode, $getSelection, $insertNodes} from "lexical";
|
import {$createNodeSelection, $createTextNode, $getSelection, $insertNodes, $setSelection} from "lexical";
|
||||||
import {$isImageNode, ImageNode} from "../../../nodes/image";
|
import {$isImageNode, ImageNode} from "../../../nodes/image";
|
||||||
import {$createLinkNode, $isLinkNode} from "@lexical/link";
|
import {$createLinkNode, $isLinkNode, LinkNode} from "@lexical/link";
|
||||||
import {$createMediaNodeFromHtml, $createMediaNodeFromSrc, $isMediaNode, MediaNode} from "../../../nodes/media";
|
import {$createMediaNodeFromHtml, $createMediaNodeFromSrc, $isMediaNode, MediaNode} from "../../../nodes/media";
|
||||||
import {$insertNodeToNearestRoot} from "@lexical/utils";
|
import {$insertNodeToNearestRoot} from "@lexical/utils";
|
||||||
import {$getNodeFromSelection, getLastSelection} from "../../../utils/selection";
|
import {$getNodeFromSelection, getLastSelection} from "../../../utils/selection";
|
||||||
@ -19,6 +19,7 @@ import searchImageIcon from "@icons/editor/image-search.svg";
|
|||||||
import searchIcon from "@icons/search.svg";
|
import searchIcon from "@icons/search.svg";
|
||||||
import {showLinkSelector} from "../../../utils/links";
|
import {showLinkSelector} from "../../../utils/links";
|
||||||
import {LinkField} from "../../framework/blocks/link-field";
|
import {LinkField} from "../../framework/blocks/link-field";
|
||||||
|
import {insertOrUpdateLink} from "../../../utils/formats";
|
||||||
|
|
||||||
export function $showImageForm(image: ImageNode, context: EditorUiContext) {
|
export function $showImageForm(image: ImageNode, context: EditorUiContext) {
|
||||||
const imageModal: EditorFormModal = context.manager.createModal('image');
|
const imageModal: EditorFormModal = context.manager.createModal('image');
|
||||||
@ -96,37 +97,36 @@ export const image: EditorFormDefinition = {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function $showLinkForm(link: LinkNode|null, context: EditorUiContext) {
|
||||||
|
const linkModal = context.manager.createModal('link');
|
||||||
|
|
||||||
|
let formDefaults = {};
|
||||||
|
if (link) {
|
||||||
|
formDefaults = {
|
||||||
|
url: link.getURL(),
|
||||||
|
text: link.getTextContent(),
|
||||||
|
title: link.getTitle(),
|
||||||
|
target: link.getTarget(),
|
||||||
|
}
|
||||||
|
|
||||||
|
context.editor.update(() => {
|
||||||
|
const selection = $createNodeSelection();
|
||||||
|
selection.add(link.getKey());
|
||||||
|
$setSelection(selection);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
linkModal.show(formDefaults);
|
||||||
|
}
|
||||||
|
|
||||||
export const link: EditorFormDefinition = {
|
export const link: EditorFormDefinition = {
|
||||||
submitText: 'Apply',
|
submitText: 'Apply',
|
||||||
async action(formData, context: EditorUiContext) {
|
async action(formData, context: EditorUiContext) {
|
||||||
context.editor.update(() => {
|
insertOrUpdateLink(context.editor, {
|
||||||
|
url: formData.get('url')?.toString() || '',
|
||||||
const url = formData.get('url')?.toString() || '';
|
title: formData.get('title')?.toString() || '',
|
||||||
const title = formData.get('title')?.toString() || ''
|
target: formData.get('target')?.toString() || '',
|
||||||
const target = formData.get('target')?.toString() || '';
|
text: formData.get('text')?.toString() || '',
|
||||||
const text = formData.get('text')?.toString() || '';
|
|
||||||
|
|
||||||
const selection = $getSelection();
|
|
||||||
let link = $getNodeFromSelection(selection, $isLinkNode);
|
|
||||||
if ($isLinkNode(link)) {
|
|
||||||
link.setURL(url);
|
|
||||||
link.setTarget(target);
|
|
||||||
link.setTitle(title);
|
|
||||||
} else {
|
|
||||||
link = $createLinkNode(url, {
|
|
||||||
title: title,
|
|
||||||
target: target,
|
|
||||||
});
|
|
||||||
|
|
||||||
$insertNodes([link]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($isLinkNode(link)) {
|
|
||||||
for (const child of link.getChildren()) {
|
|
||||||
child.remove(true);
|
|
||||||
}
|
|
||||||
link.append($createTextNode(text));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import {$isQuoteNode, HeadingNode, HeadingTagType} from "@lexical/rich-text";
|
import {$isQuoteNode, HeadingNode, HeadingTagType} from "@lexical/rich-text";
|
||||||
import {$getSelection, LexicalEditor, LexicalNode} from "lexical";
|
import {$createTextNode, $getSelection, $insertNodes, LexicalEditor, LexicalNode} from "lexical";
|
||||||
import {
|
import {
|
||||||
$getBlockElementNodesInSelection,
|
$getBlockElementNodesInSelection,
|
||||||
$getNodeFromSelection,
|
$getNodeFromSelection,
|
||||||
$insertNewBlockNodeAtSelection,
|
$insertNewBlockNodeAtSelection, $selectionContainsNodeType,
|
||||||
$toggleSelectionBlockNodeType,
|
$toggleSelectionBlockNodeType,
|
||||||
getLastSelection
|
getLastSelection
|
||||||
} from "./selection";
|
} from "./selection";
|
||||||
@ -12,6 +12,9 @@ import {$createCustomParagraphNode, $isCustomParagraphNode} from "../nodes/custo
|
|||||||
import {$createCustomQuoteNode} from "../nodes/custom-quote";
|
import {$createCustomQuoteNode} from "../nodes/custom-quote";
|
||||||
import {$createCodeBlockNode, $isCodeBlockNode, $openCodeEditorForNode, CodeBlockNode} from "../nodes/code-block";
|
import {$createCodeBlockNode, $isCodeBlockNode, $openCodeEditorForNode, CodeBlockNode} from "../nodes/code-block";
|
||||||
import {$createCalloutNode, $isCalloutNode, CalloutCategory} from "../nodes/callout";
|
import {$createCalloutNode, $isCalloutNode, CalloutCategory} from "../nodes/callout";
|
||||||
|
import {insertList, ListNode, ListType, removeList} from "@lexical/list";
|
||||||
|
import {$isCustomListNode} from "../nodes/custom-list";
|
||||||
|
import {$createLinkNode, $isLinkNode} from "@lexical/link";
|
||||||
|
|
||||||
const $isHeaderNodeOfTag = (node: LexicalNode | null | undefined, tag: HeadingTagType) => {
|
const $isHeaderNodeOfTag = (node: LexicalNode | null | undefined, tag: HeadingTagType) => {
|
||||||
return $isCustomHeadingNode(node) && (node as HeadingNode).getTag() === tag;
|
return $isCustomHeadingNode(node) && (node as HeadingNode).getTag() === tag;
|
||||||
@ -38,6 +41,21 @@ export function toggleSelectionAsBlockquote(editor: LexicalEditor) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function toggleSelectionAsList(editor: LexicalEditor, type: ListType) {
|
||||||
|
editor.getEditorState().read(() => {
|
||||||
|
const selection = $getSelection();
|
||||||
|
const listSelected = $selectionContainsNodeType(selection, (node: LexicalNode | null | undefined): boolean => {
|
||||||
|
return $isCustomListNode(node) && (node as ListNode).getListType() === type;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (listSelected) {
|
||||||
|
removeList(editor);
|
||||||
|
} else {
|
||||||
|
insertList(editor, type);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function formatCodeBlock(editor: LexicalEditor) {
|
export function formatCodeBlock(editor: LexicalEditor) {
|
||||||
editor.getEditorState().read(() => {
|
editor.getEditorState().read(() => {
|
||||||
const selection = $getSelection();
|
const selection = $getSelection();
|
||||||
@ -85,4 +103,30 @@ export function cycleSelectionCalloutFormats(editor: LexicalEditor) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function insertOrUpdateLink(editor: LexicalEditor, linkDetails: {text: string, title: string, target: string, url: string}) {
|
||||||
|
editor.update(() => {
|
||||||
|
const selection = $getSelection();
|
||||||
|
let link = $getNodeFromSelection(selection, $isLinkNode);
|
||||||
|
if ($isLinkNode(link)) {
|
||||||
|
link.setURL(linkDetails.url);
|
||||||
|
link.setTarget(linkDetails.target);
|
||||||
|
link.setTitle(linkDetails.title);
|
||||||
|
} else {
|
||||||
|
link = $createLinkNode(linkDetails.url, {
|
||||||
|
title: linkDetails.title,
|
||||||
|
target: linkDetails.target,
|
||||||
|
});
|
||||||
|
|
||||||
|
$insertNodes([link]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($isLinkNode(link)) {
|
||||||
|
for (const child of link.getChildren()) {
|
||||||
|
child.remove(true);
|
||||||
|
}
|
||||||
|
link.append($createTextNode(linkDetails.text));
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user