1 import {EditorButtonDefinition} from "../../framework/buttons";
2 import linkIcon from "@icons/editor/link.svg";
3 import {EditorUiContext} from "../../framework/core";
8 $getSelection, $insertNodes,
13 import {$isLinkNode, LinkNode} from "@lexical/link";
14 import unlinkIcon from "@icons/editor/unlink.svg";
15 import imageIcon from "@icons/editor/image.svg";
16 import {$isImageNode, ImageNode} from "../../../nodes/image";
17 import horizontalRuleIcon from "@icons/editor/horizontal-rule.svg";
18 import {$createHorizontalRuleNode, $isHorizontalRuleNode} from "../../../nodes/horizontal-rule";
19 import codeBlockIcon from "@icons/editor/code-block.svg";
20 import {$createCodeBlockNode, $isCodeBlockNode, $openCodeEditorForNode, CodeBlockNode} from "../../../nodes/code-block";
21 import editIcon from "@icons/edit.svg";
22 import diagramIcon from "@icons/editor/diagram.svg";
23 import {$createDiagramNode, DiagramNode} from "../../../nodes/diagram";
24 import detailsIcon from "@icons/editor/details.svg";
25 import mediaIcon from "@icons/editor/media.svg";
26 import {$createDetailsNode, $isDetailsNode} from "../../../nodes/details";
27 import {$isMediaNode, MediaNode} from "../../../nodes/media";
29 $getNodeFromSelection,
30 $insertNewBlockNodeAtSelection,
31 $selectionContainsNodeType, getLastSelection
32 } from "../../../utils/selection";
33 import {$isDiagramNode, $openDrawingEditorForNode, showDiagramManagerForInsert} from "../../../utils/diagrams";
34 import {$createLinkedImageNodeFromImageData, showImageManager} from "../../../utils/images";
35 import {$showImageForm} from "../forms/objects";
36 import {formatCodeBlock} from "../../../utils/formats";
38 export const link: EditorButtonDefinition = {
39 label: 'Insert/edit link',
41 action(context: EditorUiContext) {
42 const linkModal = context.manager.createModal('link');
43 context.editor.getEditorState().read(() => {
44 const selection = $getSelection();
45 const selectedLink = $getNodeFromSelection(selection, $isLinkNode) as LinkNode | null;
47 let formDefaults = {};
50 url: selectedLink.getURL(),
51 text: selectedLink.getTextContent(),
52 title: selectedLink.getTitle(),
53 target: selectedLink.getTarget(),
56 context.editor.update(() => {
57 const selection = $createNodeSelection();
58 selection.add(selectedLink.getKey());
59 $setSelection(selection);
63 linkModal.show(formDefaults);
66 isActive(selection: BaseSelection | null): boolean {
67 return $selectionContainsNodeType(selection, $isLinkNode);
71 export const unlink: EditorButtonDefinition = {
74 action(context: EditorUiContext) {
75 context.editor.update(() => {
76 const selection = getLastSelection(context.editor);
77 const selectedLink = $getNodeFromSelection(selection, $isLinkNode) as LinkNode | null;
78 const selectionPoints = selection?.getStartEndPoints();
81 const newNode = $createTextNode(selectedLink.getTextContent());
82 selectedLink.replace(newNode);
83 if (selectionPoints?.length === 2) {
84 newNode.select(selectionPoints[0].offset, selectionPoints[1].offset);
91 isActive(selection: BaseSelection | null): boolean {
97 export const image: EditorButtonDefinition = {
98 label: 'Insert/Edit Image',
100 action(context: EditorUiContext) {
101 context.editor.getEditorState().read(() => {
102 const selection = getLastSelection(context.editor);
103 const selectedImage = $getNodeFromSelection(selection, $isImageNode) as ImageNode | null;
105 $showImageForm(selectedImage, context);
109 showImageManager((image) => {
110 context.editor.update(() => {
111 const link = $createLinkedImageNodeFromImageData(image);
112 $insertNodes([link]);
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 formatCodeBlock(context.editor);
141 isActive(selection: BaseSelection | null): boolean {
142 return $selectionContainsNodeType(selection, $isCodeBlockNode);
146 export const editCodeBlock: EditorButtonDefinition = Object.assign({}, codeBlock, {
147 label: 'Edit code block',
151 export const diagram: EditorButtonDefinition = {
152 label: 'Insert/edit drawing',
154 action(context: EditorUiContext) {
155 context.editor.getEditorState().read(() => {
156 const selection = getLastSelection(context.editor);
157 const diagramNode = $getNodeFromSelection(selection, $isDiagramNode) as (DiagramNode | null);
158 if (diagramNode === null) {
159 context.editor.update(() => {
160 const diagram = $createDiagramNode();
161 $insertNewBlockNodeAtSelection(diagram, true);
162 $openDrawingEditorForNode(context, diagram);
163 diagram.selectStart();
166 $openDrawingEditorForNode(context, diagramNode);
170 isActive(selection: BaseSelection | null): boolean {
171 return $selectionContainsNodeType(selection, $isDiagramNode);
175 export const diagramManager: EditorButtonDefinition = {
176 label: 'Drawing manager',
177 action(context: EditorUiContext) {
178 showDiagramManagerForInsert(context);
180 isActive(): boolean {
185 export const media: EditorButtonDefinition = {
186 label: 'Insert/edit Media',
188 action(context: EditorUiContext) {
189 const mediaModal = context.manager.createModal('media');
191 context.editor.getEditorState().read(() => {
192 const selection = $getSelection();
193 const selectedNode = $getNodeFromSelection(selection, $isMediaNode) as MediaNode | null;
195 let formDefaults = {};
197 const nodeAttrs = selectedNode.getAttributes();
199 src: nodeAttrs.src || nodeAttrs.data || '',
200 width: nodeAttrs.width,
201 height: nodeAttrs.height,
206 mediaModal.show(formDefaults);
209 isActive(selection: BaseSelection | null): boolean {
210 return $selectionContainsNodeType(selection, $isMediaNode);
214 export const details: EditorButtonDefinition = {
215 label: 'Insert collapsible block',
217 action(context: EditorUiContext) {
218 context.editor.update(() => {
219 const selection = $getSelection();
220 const detailsNode = $createDetailsNode();
221 const selectionNodes = selection?.getNodes() || [];
222 const topLevels = selectionNodes.map(n => n.getTopLevelElement())
223 .filter(n => n !== null) as ElementNode[];
224 const uniqueTopLevels = [...new Set(topLevels)];
226 if (uniqueTopLevels.length > 0) {
227 uniqueTopLevels[0].insertAfter(detailsNode);
229 $getRoot().append(detailsNode);
232 for (const node of uniqueTopLevels) {
233 detailsNode.append(node);
237 isActive(selection: BaseSelection | null): boolean {
238 return $selectionContainsNodeType(selection, $isDetailsNode);