1 import {EditorButtonDefinition} from "../../framework/buttons";
2 import linkIcon from "@icons/editor/link.svg";
3 import {EditorUiContext} from "../../framework/core";
13 import {$getNodeFromSelection, $insertNewBlockNodeAtSelection, $selectionContainsNodeType} from "../../../helpers";
14 import {$isLinkNode, LinkNode} from "@lexical/link";
15 import unlinkIcon from "@icons/editor/unlink.svg";
16 import imageIcon from "@icons/editor/image.svg";
17 import {$isImageNode, ImageNode} from "../../../nodes/image";
18 import horizontalRuleIcon from "@icons/editor/horizontal-rule.svg";
19 import {$createHorizontalRuleNode, $isHorizontalRuleNode} from "../../../nodes/horizontal-rule";
20 import codeBlockIcon from "@icons/editor/code-block.svg";
21 import {$createCodeBlockNode, $isCodeBlockNode, $openCodeEditorForNode, CodeBlockNode} from "../../../nodes/code-block";
22 import editIcon from "@icons/edit.svg";
23 import diagramIcon from "@icons/editor/diagram.svg";
24 import {$createDiagramNode, $isDiagramNode, $openDrawingEditorForNode, DiagramNode} from "../../../nodes/diagram";
25 import detailsIcon from "@icons/editor/details.svg";
26 import {$createDetailsNode, $isDetailsNode} from "../../../nodes/details";
28 export const link: EditorButtonDefinition = {
29 label: 'Insert/edit link',
31 action(context: EditorUiContext) {
32 const linkModal = context.manager.createModal('link');
33 context.editor.getEditorState().read(() => {
34 const selection = $getSelection();
35 const selectedLink = $getNodeFromSelection(selection, $isLinkNode) as LinkNode|null;
37 let formDefaults = {};
40 url: selectedLink.getURL(),
41 text: selectedLink.getTextContent(),
42 title: selectedLink.getTitle(),
43 target: selectedLink.getTarget(),
46 context.editor.update(() => {
47 const selection = $createNodeSelection();
48 selection.add(selectedLink.getKey());
49 $setSelection(selection);
53 linkModal.show(formDefaults);
56 isActive(selection: BaseSelection|null): boolean {
57 return $selectionContainsNodeType(selection, $isLinkNode);
61 export const unlink: EditorButtonDefinition = {
64 action(context: EditorUiContext) {
65 context.editor.update(() => {
66 const selection = context.lastSelection;
67 const selectedLink = $getNodeFromSelection(selection, $isLinkNode) as LinkNode|null;
68 const selectionPoints = selection?.getStartEndPoints();
71 const newNode = $createTextNode(selectedLink.getTextContent());
72 selectedLink.replace(newNode);
73 if (selectionPoints?.length === 2) {
74 newNode.select(selectionPoints[0].offset, selectionPoints[1].offset);
81 isActive(selection: BaseSelection|null): boolean {
88 export const image: EditorButtonDefinition = {
89 label: 'Insert/Edit Image',
91 action(context: EditorUiContext) {
92 const imageModal = context.manager.createModal('image');
93 const selection = context.lastSelection;
94 const selectedImage = $getNodeFromSelection(selection, $isImageNode) as ImageNode|null;
96 context.editor.getEditorState().read(() => {
97 let formDefaults = {};
100 src: selectedImage.getSrc(),
101 alt: selectedImage.getAltText(),
102 height: selectedImage.getHeight(),
103 width: selectedImage.getWidth(),
106 context.editor.update(() => {
107 const selection = $createNodeSelection();
108 selection.add(selectedImage.getKey());
109 $setSelection(selection);
113 imageModal.show(formDefaults);
116 isActive(selection: BaseSelection|null): boolean {
117 return $selectionContainsNodeType(selection, $isImageNode);
121 export const horizontalRule: EditorButtonDefinition = {
122 label: 'Insert horizontal line',
123 icon: horizontalRuleIcon,
124 action(context: EditorUiContext) {
125 context.editor.update(() => {
126 $insertNewBlockNodeAtSelection($createHorizontalRuleNode(), false);
129 isActive(selection: BaseSelection|null): boolean {
130 return $selectionContainsNodeType(selection, $isHorizontalRuleNode);
134 export const codeBlock: EditorButtonDefinition = {
135 label: 'Insert code block',
137 action(context: EditorUiContext) {
138 context.editor.getEditorState().read(() => {
139 const selection = $getSelection();
140 const codeBlock = $getNodeFromSelection(context.lastSelection, $isCodeBlockNode) as (CodeBlockNode|null);
141 if (codeBlock === null) {
142 context.editor.update(() => {
143 const codeBlock = $createCodeBlockNode();
144 codeBlock.setCode(selection?.getTextContent() || '');
145 $insertNewBlockNodeAtSelection(codeBlock, true);
146 $openCodeEditorForNode(context.editor, codeBlock);
147 codeBlock.selectStart();
150 $openCodeEditorForNode(context.editor, codeBlock);
154 isActive(selection: BaseSelection|null): boolean {
155 return $selectionContainsNodeType(selection, $isCodeBlockNode);
159 export const editCodeBlock: EditorButtonDefinition = Object.assign({}, codeBlock, {
160 label: 'Edit code block',
164 export const diagram: EditorButtonDefinition = {
165 label: 'Insert/edit drawing',
167 action(context: EditorUiContext) {
168 context.editor.getEditorState().read(() => {
169 const selection = $getSelection();
170 const diagramNode = $getNodeFromSelection(context.lastSelection, $isDiagramNode) as (DiagramNode|null);
171 if (diagramNode === null) {
172 context.editor.update(() => {
173 const diagram = $createDiagramNode();
174 $insertNewBlockNodeAtSelection(diagram, true);
175 $openDrawingEditorForNode(context, diagram);
176 diagram.selectStart();
179 $openDrawingEditorForNode(context, diagramNode);
183 isActive(selection: BaseSelection|null): boolean {
184 return $selectionContainsNodeType(selection, $isDiagramNode);
189 export const details: EditorButtonDefinition = {
190 label: 'Insert collapsible block',
192 action(context: EditorUiContext) {
193 context.editor.update(() => {
194 const selection = $getSelection();
195 const detailsNode = $createDetailsNode();
196 const selectionNodes = selection?.getNodes() || [];
197 const topLevels = selectionNodes.map(n => n.getTopLevelElement())
198 .filter(n => n !== null) as ElementNode[];
199 const uniqueTopLevels = [...new Set(topLevels)];
201 if (uniqueTopLevels.length > 0) {
202 uniqueTopLevels[0].insertAfter(detailsNode);
204 $getRoot().append(detailsNode);
207 for (const node of uniqueTopLevels) {
208 detailsNode.append(node);
212 isActive(selection: BaseSelection|null): boolean {
213 return $selectionContainsNodeType(selection, $isDetailsNode);