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 mediaIcon from "@icons/editor/media.svg";
27 import {$createDetailsNode, $isDetailsNode} from "../../../nodes/details";
28 import {$isMediaNode, MediaNode} from "../../../nodes/media";
30 export const link: EditorButtonDefinition = {
31 label: 'Insert/edit link',
33 action(context: EditorUiContext) {
34 const linkModal = context.manager.createModal('link');
35 context.editor.getEditorState().read(() => {
36 const selection = $getSelection();
37 const selectedLink = $getNodeFromSelection(selection, $isLinkNode) as LinkNode | null;
39 let formDefaults = {};
42 url: selectedLink.getURL(),
43 text: selectedLink.getTextContent(),
44 title: selectedLink.getTitle(),
45 target: selectedLink.getTarget(),
48 context.editor.update(() => {
49 const selection = $createNodeSelection();
50 selection.add(selectedLink.getKey());
51 $setSelection(selection);
55 linkModal.show(formDefaults);
58 isActive(selection: BaseSelection | null): boolean {
59 return $selectionContainsNodeType(selection, $isLinkNode);
63 export const unlink: EditorButtonDefinition = {
66 action(context: EditorUiContext) {
67 context.editor.update(() => {
68 const selection = context.lastSelection;
69 const selectedLink = $getNodeFromSelection(selection, $isLinkNode) as LinkNode | null;
70 const selectionPoints = selection?.getStartEndPoints();
73 const newNode = $createTextNode(selectedLink.getTextContent());
74 selectedLink.replace(newNode);
75 if (selectionPoints?.length === 2) {
76 newNode.select(selectionPoints[0].offset, selectionPoints[1].offset);
83 isActive(selection: BaseSelection | null): boolean {
89 export const image: EditorButtonDefinition = {
90 label: 'Insert/Edit Image',
92 action(context: EditorUiContext) {
93 const imageModal = context.manager.createModal('image');
94 const selection = context.lastSelection;
95 const selectedImage = $getNodeFromSelection(selection, $isImageNode) as ImageNode | null;
97 context.editor.getEditorState().read(() => {
98 let formDefaults = {};
101 src: selectedImage.getSrc(),
102 alt: selectedImage.getAltText(),
103 height: selectedImage.getHeight(),
104 width: selectedImage.getWidth(),
107 context.editor.update(() => {
108 const selection = $createNodeSelection();
109 selection.add(selectedImage.getKey());
110 $setSelection(selection);
114 imageModal.show(formDefaults);
117 isActive(selection: BaseSelection | null): boolean {
118 return $selectionContainsNodeType(selection, $isImageNode);
122 export const horizontalRule: EditorButtonDefinition = {
123 label: 'Insert horizontal line',
124 icon: horizontalRuleIcon,
125 action(context: EditorUiContext) {
126 context.editor.update(() => {
127 $insertNewBlockNodeAtSelection($createHorizontalRuleNode(), false);
130 isActive(selection: BaseSelection | null): boolean {
131 return $selectionContainsNodeType(selection, $isHorizontalRuleNode);
135 export const codeBlock: EditorButtonDefinition = {
136 label: 'Insert code block',
138 action(context: EditorUiContext) {
139 context.editor.getEditorState().read(() => {
140 const selection = $getSelection();
141 const codeBlock = $getNodeFromSelection(context.lastSelection, $isCodeBlockNode) as (CodeBlockNode | null);
142 if (codeBlock === null) {
143 context.editor.update(() => {
144 const codeBlock = $createCodeBlockNode();
145 codeBlock.setCode(selection?.getTextContent() || '');
146 $insertNewBlockNodeAtSelection(codeBlock, true);
147 $openCodeEditorForNode(context.editor, codeBlock);
148 codeBlock.selectStart();
151 $openCodeEditorForNode(context.editor, codeBlock);
155 isActive(selection: BaseSelection | null): boolean {
156 return $selectionContainsNodeType(selection, $isCodeBlockNode);
160 export const editCodeBlock: EditorButtonDefinition = Object.assign({}, codeBlock, {
161 label: 'Edit code block',
165 export const diagram: EditorButtonDefinition = {
166 label: 'Insert/edit drawing',
168 action(context: EditorUiContext) {
169 context.editor.getEditorState().read(() => {
170 const selection = $getSelection();
171 const diagramNode = $getNodeFromSelection(context.lastSelection, $isDiagramNode) as (DiagramNode | null);
172 if (diagramNode === null) {
173 context.editor.update(() => {
174 const diagram = $createDiagramNode();
175 $insertNewBlockNodeAtSelection(diagram, true);
176 $openDrawingEditorForNode(context, diagram);
177 diagram.selectStart();
180 $openDrawingEditorForNode(context, diagramNode);
184 isActive(selection: BaseSelection | null): boolean {
185 return $selectionContainsNodeType(selection, $isDiagramNode);
189 export const media: EditorButtonDefinition = {
190 label: 'Insert/edit Media',
192 action(context: EditorUiContext) {
193 const mediaModal = context.manager.createModal('media');
195 context.editor.getEditorState().read(() => {
196 const selection = $getSelection();
197 const selectedNode = $getNodeFromSelection(selection, $isMediaNode) as MediaNode | null;
199 let formDefaults = {};
201 const nodeAttrs = selectedNode.getAttributes();
203 src: nodeAttrs.src || nodeAttrs.data || '',
204 width: nodeAttrs.width,
205 height: nodeAttrs.height,
210 mediaModal.show(formDefaults);
213 isActive(selection: BaseSelection | null): boolean {
214 return $selectionContainsNodeType(selection, $isMediaNode);
218 export const details: EditorButtonDefinition = {
219 label: 'Insert collapsible block',
221 action(context: EditorUiContext) {
222 context.editor.update(() => {
223 const selection = $getSelection();
224 const detailsNode = $createDetailsNode();
225 const selectionNodes = selection?.getNodes() || [];
226 const topLevels = selectionNodes.map(n => n.getTopLevelElement())
227 .filter(n => n !== null) as ElementNode[];
228 const uniqueTopLevels = [...new Set(topLevels)];
230 if (uniqueTopLevels.length > 0) {
231 uniqueTopLevels[0].insertAfter(detailsNode);
233 $getRoot().append(detailsNode);
236 for (const node of uniqueTopLevels) {
237 detailsNode.append(node);
241 isActive(selection: BaseSelection | null): boolean {
242 return $selectionContainsNodeType(selection, $isDetailsNode);