1 import {EditorButtonDefinition} from "../../framework/buttons";
2 import linkIcon from "@icons/editor/link.svg";
3 import {EditorUiContext} from "../../framework/core";
7 $getSelection, $insertNodes,
9 ElementNode, isCurrentlyReadOnlyMode
11 import {$isLinkNode, LinkNode} from "@lexical/link";
12 import unlinkIcon from "@icons/editor/unlink.svg";
13 import imageIcon from "@icons/editor/image.svg";
14 import {$isImageNode, ImageNode} from "../../../nodes/image";
15 import horizontalRuleIcon from "@icons/editor/horizontal-rule.svg";
16 import {$createHorizontalRuleNode, $isHorizontalRuleNode} from "../../../nodes/horizontal-rule";
17 import codeBlockIcon from "@icons/editor/code-block.svg";
18 import {$isCodeBlockNode} from "../../../nodes/code-block";
19 import editIcon from "@icons/edit.svg";
20 import diagramIcon from "@icons/editor/diagram.svg";
21 import {$createDiagramNode, DiagramNode} from "../../../nodes/diagram";
22 import detailsIcon from "@icons/editor/details.svg";
23 import mediaIcon from "@icons/editor/media.svg";
24 import {$createDetailsNode, $isDetailsNode} from "../../../nodes/details";
25 import {$isMediaNode, MediaNode} from "../../../nodes/media";
27 $getNodeFromSelection,
28 $insertNewBlockNodeAtSelection,
29 $selectionContainsNodeType, getLastSelection
30 } from "../../../utils/selection";
31 import {$isDiagramNode, $openDrawingEditorForNode, showDiagramManagerForInsert} from "../../../utils/diagrams";
32 import {$createLinkedImageNodeFromImageData, showImageManager} from "../../../utils/images";
33 import {$showImageForm, $showLinkForm} from "../forms/objects";
34 import {formatCodeBlock} from "../../../utils/formats";
36 export const link: EditorButtonDefinition = {
37 label: 'Insert/edit link',
39 action(context: EditorUiContext) {
40 context.editor.getEditorState().read(() => {
41 const selectedLink = $getNodeFromSelection($getSelection(), $isLinkNode) as LinkNode | null;
42 $showLinkForm(selectedLink, context);
45 isActive(selection: BaseSelection | null): boolean {
46 return $selectionContainsNodeType(selection, $isLinkNode);
50 export const unlink: EditorButtonDefinition = {
53 action(context: EditorUiContext) {
54 context.editor.update(() => {
55 const selection = getLastSelection(context.editor);
56 const selectedLink = $getNodeFromSelection(selection, $isLinkNode) as LinkNode | null;
59 const contents = selectedLink.getChildren().reverse();
60 for (const child of contents) {
61 selectedLink.insertAfter(child);
63 selectedLink.remove();
65 contents[contents.length - 1].selectStart();
67 context.manager.triggerFutureStateRefresh();
71 isActive(selection: BaseSelection | null): boolean {
77 export const image: EditorButtonDefinition = {
78 label: 'Insert/Edit Image',
80 action(context: EditorUiContext) {
81 context.editor.getEditorState().read(() => {
82 const selection = getLastSelection(context.editor);
83 const selectedImage = $getNodeFromSelection(selection, $isImageNode) as ImageNode | null;
85 $showImageForm(selectedImage, context);
89 showImageManager((image) => {
90 context.editor.update(() => {
91 const link = $createLinkedImageNodeFromImageData(image);
97 isActive(selection: BaseSelection | null): boolean {
98 return $selectionContainsNodeType(selection, $isImageNode);
102 export const horizontalRule: EditorButtonDefinition = {
103 label: 'Insert horizontal line',
104 icon: horizontalRuleIcon,
105 action(context: EditorUiContext) {
106 context.editor.update(() => {
107 $insertNewBlockNodeAtSelection($createHorizontalRuleNode(), false);
110 isActive(selection: BaseSelection | null): boolean {
111 return $selectionContainsNodeType(selection, $isHorizontalRuleNode);
115 export const codeBlock: EditorButtonDefinition = {
116 label: 'Insert code block',
118 action(context: EditorUiContext) {
119 formatCodeBlock(context.editor);
121 isActive(selection: BaseSelection | null): boolean {
122 return $selectionContainsNodeType(selection, $isCodeBlockNode);
126 export const editCodeBlock: EditorButtonDefinition = Object.assign({}, codeBlock, {
127 label: 'Edit code block',
131 export const diagram: EditorButtonDefinition = {
132 label: 'Insert/edit drawing',
134 action(context: EditorUiContext) {
135 context.editor.getEditorState().read(() => {
136 const selection = getLastSelection(context.editor);
137 const diagramNode = $getNodeFromSelection(selection, $isDiagramNode) as (DiagramNode | null);
138 if (diagramNode === null) {
139 context.editor.update(() => {
140 const diagram = $createDiagramNode();
141 $insertNewBlockNodeAtSelection(diagram, true);
142 $openDrawingEditorForNode(context, diagram);
143 diagram.selectStart();
146 $openDrawingEditorForNode(context, diagramNode);
150 isActive(selection: BaseSelection | null): boolean {
151 return $selectionContainsNodeType(selection, $isDiagramNode);
155 export const diagramManager: EditorButtonDefinition = {
156 label: 'Drawing manager',
157 action(context: EditorUiContext) {
158 showDiagramManagerForInsert(context);
160 isActive(): boolean {
165 export const media: EditorButtonDefinition = {
166 label: 'Insert/edit Media',
168 action(context: EditorUiContext) {
169 const mediaModal = context.manager.createModal('media');
171 context.editor.getEditorState().read(() => {
172 const selection = $getSelection();
173 const selectedNode = $getNodeFromSelection(selection, $isMediaNode) as MediaNode | null;
175 let formDefaults = {};
177 const nodeAttrs = selectedNode.getAttributes();
179 src: nodeAttrs.src || nodeAttrs.data || '',
180 width: nodeAttrs.width,
181 height: nodeAttrs.height,
186 mediaModal.show(formDefaults);
189 isActive(selection: BaseSelection | null): boolean {
190 return $selectionContainsNodeType(selection, $isMediaNode);
194 export const details: EditorButtonDefinition = {
195 label: 'Insert collapsible block',
197 action(context: EditorUiContext) {
198 context.editor.update(() => {
199 const selection = $getSelection();
200 const detailsNode = $createDetailsNode();
201 const selectionNodes = selection?.getNodes() || [];
202 const topLevels = selectionNodes.map(n => n.getTopLevelElement())
203 .filter(n => n !== null) as ElementNode[];
204 const uniqueTopLevels = [...new Set(topLevels)];
206 if (uniqueTopLevels.length > 0) {
207 uniqueTopLevels[0].insertAfter(detailsNode);
209 $getRoot().append(detailsNode);
212 for (const node of uniqueTopLevels) {
213 detailsNode.append(node);
217 isActive(selection: BaseSelection | null): boolean {
218 return $selectionContainsNodeType(selection, $isDetailsNode);