5 EditorSelectFormFieldDefinition
6 } from "../../framework/forms";
7 import {EditorUiContext} from "../../framework/core";
8 import {$createNodeSelection, $getSelection, $insertNodes, $setSelection} from "lexical";
9 import {$isImageNode, ImageNode} from "@lexical/rich-text/LexicalImageNode";
10 import {LinkNode} from "@lexical/link";
11 import {$createMediaNodeFromHtml, $createMediaNodeFromSrc, $isMediaNode, MediaNode} from "@lexical/rich-text/LexicalMediaNode";
12 import {$getNodeFromSelection, getLastSelection} from "../../../utils/selection";
13 import {EditorFormModal} from "../../framework/modals";
14 import {EditorActionField} from "../../framework/blocks/action-field";
15 import {EditorButton} from "../../framework/buttons";
16 import {showImageManager} from "../../../utils/images";
17 import searchImageIcon from "@icons/editor/image-search.svg";
18 import searchIcon from "@icons/search.svg";
19 import {showLinkSelector} from "../../../utils/links";
20 import {LinkField} from "../../framework/blocks/link-field";
21 import {insertOrUpdateLink} from "../../../utils/formats";
22 import {$isDetailsNode, DetailsNode} from "@lexical/rich-text/LexicalDetailsNode";
24 export function $showImageForm(image: ImageNode, context: EditorUiContext) {
25 const imageModal: EditorFormModal = context.manager.createModal('image');
26 const height = image.getHeight();
27 const width = image.getWidth();
31 alt: image.getAltText(),
32 height: height === 0 ? '' : String(height),
33 width: width === 0 ? '' : String(width),
36 imageModal.show(formData);
39 export const image: EditorFormDefinition = {
41 async action(formData, context: EditorUiContext) {
42 context.editor.update(() => {
43 const selection = getLastSelection(context.editor);
44 const selectedImage = $getNodeFromSelection(selection, $isImageNode);
45 if ($isImageNode(selectedImage)) {
46 selectedImage.setSrc(formData.get('src')?.toString() || '');
47 selectedImage.setAltText(formData.get('alt')?.toString() || '');
49 selectedImage.setWidth(Number(formData.get('width')?.toString() || '0'));
50 selectedImage.setHeight(Number(formData.get('height')?.toString() || '0'));
58 return new EditorActionField(
65 label: 'Browse files',
66 icon: searchImageIcon,
67 action(context: EditorUiContext) {
68 showImageManager((image) => {
69 const modal = context.manager.getActiveModal('image');
71 modal.getForm().setValues({
72 src: image.thumbs?.display || image.url,
83 label: 'Alternative description',
100 export function $showLinkForm(link: LinkNode|null, context: EditorUiContext) {
101 const linkModal = context.manager.createModal('link');
104 const formDefaults: Record<string, string> = {
106 text: link.getTextContent(),
107 title: link.getTitle() || '',
108 target: link.getTarget() || '',
111 context.editor.update(() => {
112 const selection = $createNodeSelection();
113 selection.add(link.getKey());
114 $setSelection(selection);
117 linkModal.show(formDefaults);
119 context.editor.getEditorState().read(() => {
120 const selection = $getSelection();
121 const text = selection?.getTextContent() || '';
122 const formDefaults = {text};
123 linkModal.show(formDefaults);
128 export const link: EditorFormDefinition = {
130 async action(formData, context: EditorUiContext) {
131 insertOrUpdateLink(context.editor, {
132 url: formData.get('url')?.toString() || '',
133 title: formData.get('title')?.toString() || '',
134 target: formData.get('target')?.toString() || '',
135 text: formData.get('text')?.toString() || '',
142 return new EditorActionField(
143 new LinkField(new EditorFormField({
149 label: 'Browse links',
151 action(context: EditorUiContext) {
152 showLinkSelector(entity => {
153 const modal = context.manager.getActiveModal('link');
155 modal.getForm().setValues({
168 label: 'Text to display',
178 label: 'Open link in...',
182 'Current window': '',
183 'New window': '_blank',
185 } as EditorSelectFormFieldDefinition,
189 export function $showMediaForm(media: MediaNode|null, context: EditorUiContext): void {
190 const mediaModal = context.manager.createModal('media');
192 let formDefaults = {};
194 const nodeAttrs = media.getAttributes();
195 const nodeDOM = media.exportDOM(context.editor).element;
196 const nodeHtml = (nodeDOM instanceof HTMLElement) ? nodeDOM.outerHTML : '';
199 src: nodeAttrs.src || nodeAttrs.data || media.getSources()[0]?.src || '',
200 width: nodeAttrs.width,
201 height: nodeAttrs.height,
204 // This is used so we can check for edits against the embed field on submit
205 embed_check: nodeHtml,
209 mediaModal.show(formDefaults);
212 export const media: EditorFormDefinition = {
214 async action(formData, context: EditorUiContext) {
215 const selectedNode: MediaNode|null = await (new Promise((res, rej) => {
216 context.editor.getEditorState().read(() => {
217 const node = $getNodeFromSelection($getSelection(), $isMediaNode);
218 res(node as MediaNode|null);
222 const embedCode = (formData.get('embed') || '').toString().trim();
223 const embedCheck = (formData.get('embed_check') || '').toString().trim();
224 if (embedCode && embedCode !== embedCheck) {
225 context.editor.update(() => {
226 const node = $createMediaNodeFromHtml(embedCode);
227 if (selectedNode && node) {
228 selectedNode.replace(node)
230 $insertNodes([node]);
237 context.editor.update(() => {
238 const src = (formData.get('src') || '').toString().trim();
239 const height = (formData.get('height') || '').toString().trim();
240 const width = (formData.get('width') || '').toString().trim();
244 selectedNode.setSrc(src);
245 selectedNode.setWidthAndHeight(width, height);
246 context.manager.triggerFutureStateRefresh();
251 const node = $createMediaNodeFromSrc(src);
252 if (width || height) {
253 node.setWidthAndHeight(width, height);
255 $insertNodes([node]);
263 return new EditorFormTabs([
288 label: 'Paste your embed code below:',
305 export function $showDetailsForm(details: DetailsNode|null, context: EditorUiContext) {
306 const linkModal = context.manager.createModal('details');
312 summary: details.getSummary()
316 export const details: EditorFormDefinition = {
318 async action(formData, context: EditorUiContext) {
319 context.editor.update(() => {
320 const node = $getNodeFromSelection($getSelection(), $isDetailsNode);
321 const summary = (formData.get('summary') || '').toString().trim();
322 if ($isDetailsNode(node)) {
323 node.setSummary(summary);
331 label: 'Toggle label',