diff --git a/resources/js/wysiwyg/actions.ts b/resources/js/wysiwyg/actions.ts new file mode 100644 index 000000000..bca31e51b --- /dev/null +++ b/resources/js/wysiwyg/actions.ts @@ -0,0 +1,26 @@ +import {$getRoot, LexicalEditor} from "lexical"; +import {$generateHtmlFromNodes, $generateNodesFromDOM} from "@lexical/html"; + + +export function setEditorContentFromHtml(editor: LexicalEditor, html: string) { + const parser = new DOMParser(); + const dom = parser.parseFromString(html, 'text/html'); + + editor.update(() => { + const nodes = $generateNodesFromDOM(editor, dom); + const root = $getRoot(); + for (const child of root.getChildren()) { + child.remove(true); + } + root.append(...nodes); + }); +} + +export function getEditorContentAsHtml(editor: LexicalEditor): Promise { + return new Promise((resolve, reject) => { + editor.getEditorState().read(() => { + const html = $generateHtmlFromNodes(editor); + resolve(html); + }); + }); +} \ No newline at end of file diff --git a/resources/js/wysiwyg/index.ts b/resources/js/wysiwyg/index.ts index 0dcbf27f5..41207b706 100644 --- a/resources/js/wysiwyg/index.ts +++ b/resources/js/wysiwyg/index.ts @@ -1,10 +1,10 @@ -import {$getRoot, createEditor, CreateEditorArgs} from 'lexical'; +import {createEditor, CreateEditorArgs} from 'lexical'; import {createEmptyHistoryState, registerHistory} from '@lexical/history'; import {registerRichText} from '@lexical/rich-text'; import {mergeRegister} from '@lexical/utils'; -import {$generateNodesFromDOM} from '@lexical/html'; import {getNodesForPageEditor} from './nodes'; import {buildEditorUI} from "./ui"; +import {setEditorContentFromHtml} from "./actions"; export function createPageEditorInstance(editArea: HTMLElement) { const config: CreateEditorArgs = { @@ -14,8 +14,6 @@ export function createPageEditorInstance(editArea: HTMLElement) { }; const startingHtml = editArea.innerHTML; - const parser = new DOMParser(); - const dom = parser.parseFromString(startingHtml, 'text/html'); const editor = createEditor(config); editor.setRootElement(editArea); @@ -25,11 +23,7 @@ export function createPageEditorInstance(editArea: HTMLElement) { registerHistory(editor, createEmptyHistoryState(), 300), ); - editor.update(() => { - const startingNodes = $generateNodesFromDOM(editor, dom); - const root = $getRoot(); - root.append(...startingNodes); - }); + setEditorContentFromHtml(editor, startingHtml); const debugView = document.getElementById('lexical-debug'); editor.registerUpdateListener(({editorState}) => { diff --git a/resources/js/wysiwyg/ui/defaults/button-definitions.ts b/resources/js/wysiwyg/ui/defaults/button-definitions.ts index e549e69a2..077bcae21 100644 --- a/resources/js/wysiwyg/ui/defaults/button-definitions.ts +++ b/resources/js/wysiwyg/ui/defaults/button-definitions.ts @@ -28,6 +28,7 @@ import {EditorUiContext} from "../framework/core"; import {$isImageNode, ImageNode} from "../../nodes/image"; import {$createDetailsNode, $isDetailsNode} from "../../nodes/details"; import {$insertNodeToNearestRoot} from "@lexical/utils"; +import {getEditorContentAsHtml} from "../../actions"; export const undo: EditorButtonDefinition = { label: 'Undo', @@ -230,3 +231,14 @@ export const details: EditorButtonDefinition = { } } +export const source: EditorButtonDefinition = { + label: 'Source code', + async action(context: EditorUiContext) { + const modal = context.manager.createModal('source'); + const source = await getEditorContentAsHtml(context.editor); + modal.show({source}); + }, + isActive() { + return false; + } +}; diff --git a/resources/js/wysiwyg/ui/defaults/form-definitions.ts b/resources/js/wysiwyg/ui/defaults/form-definitions.ts index 13e7a9c9f..04147a4f0 100644 --- a/resources/js/wysiwyg/ui/defaults/form-definitions.ts +++ b/resources/js/wysiwyg/ui/defaults/form-definitions.ts @@ -3,6 +3,7 @@ import {EditorUiContext} from "../framework/core"; import {$createLinkNode} from "@lexical/link"; import {$createTextNode, $getSelection} from "lexical"; import {$createImageNode} from "../../nodes/image"; +import {setEditorContentFromHtml} from "../../actions"; export const link: EditorFormDefinition = { @@ -86,4 +87,19 @@ export const image: EditorFormDefinition = { type: 'text', }, ], +}; + +export const source: EditorFormDefinition = { + submitText: 'Save', + action(formData, context: EditorUiContext) { + setEditorContentFromHtml(context.editor, formData.get('source')?.toString() || ''); + return true; + }, + fields: [ + { + label: 'Source', + name: 'source', + type: 'textarea', + }, + ], }; \ No newline at end of file diff --git a/resources/js/wysiwyg/ui/framework/forms.ts b/resources/js/wysiwyg/ui/framework/forms.ts index c6338f798..a7fcb45ba 100644 --- a/resources/js/wysiwyg/ui/framework/forms.ts +++ b/resources/js/wysiwyg/ui/framework/forms.ts @@ -5,7 +5,7 @@ import {el} from "../../helpers"; export interface EditorFormFieldDefinition { label: string; name: string; - type: 'text' | 'select'; + type: 'text' | 'select' | 'textarea'; } export interface EditorSelectFormFieldDefinition extends EditorFormFieldDefinition { @@ -28,7 +28,7 @@ export class EditorFormField extends EditorUiElement { } setValue(value: string) { - const input = this.getDOMElement().querySelector('input,select') as HTMLInputElement; + const input = this.getDOMElement().querySelector('input,select,textarea') as HTMLInputElement; input.value = value; } @@ -45,6 +45,8 @@ export class EditorFormField extends EditorUiElement { const labels = Object.keys(options); const optionElems = labels.map(label => el('option', {value: options[label]}, [label])); input = el('select', {id, name: this.definition.name, class: 'editor-form-field-input'}, optionElems); + } else if (this.definition.type === 'textarea') { + input = el('textarea', {id, name: this.definition.name, class: 'editor-form-field-input'}); } else { input = el('input', {id, name: this.definition.name, class: 'editor-form-field-input'}); } diff --git a/resources/js/wysiwyg/ui/index.ts b/resources/js/wysiwyg/ui/index.ts index 8227dec68..b2fd7e05a 100644 --- a/resources/js/wysiwyg/ui/index.ts +++ b/resources/js/wysiwyg/ui/index.ts @@ -6,7 +6,7 @@ import { } from "lexical"; import {getMainEditorFullToolbar} from "./toolbars"; import {EditorUIManager} from "./framework/manager"; -import {image as imageFormDefinition, link as linkFormDefinition} from "./defaults/form-definitions"; +import {image as imageFormDefinition, link as linkFormDefinition, source as sourceFormDefinition} from "./defaults/form-definitions"; import {DecoratorListener} from "lexical/LexicalEditor"; import type {NodeKey} from "lexical/LexicalNode"; import {EditorDecoratorAdapter} from "./framework/decorator"; @@ -36,7 +36,11 @@ export function buildEditorUI(element: HTMLElement, editor: LexicalEditor) { manager.registerModal('image', { title: 'Insert/Edit Image', form: imageFormDefinition - }) + }); + manager.registerModal('source', { + title: 'Source code', + form: sourceFormDefinition, + }); // Register decorator listener // Maybe move to manager? diff --git a/resources/js/wysiwyg/ui/toolbars.ts b/resources/js/wysiwyg/ui/toolbars.ts index b5d151fc1..63ff8a053 100644 --- a/resources/js/wysiwyg/ui/toolbars.ts +++ b/resources/js/wysiwyg/ui/toolbars.ts @@ -4,7 +4,7 @@ import { dangerCallout, details, h2, h3, h4, h5, image, infoCallout, italic, link, paragraph, - redo, strikethrough, subscript, + redo, source, strikethrough, subscript, successCallout, superscript, underline, undo, warningCallout @@ -42,5 +42,7 @@ export function getMainEditorFullToolbar(): EditorContainerUiElement { new EditorButton(link), new EditorButton(image), new EditorButton(details), + + new EditorButton(source), ]); } \ No newline at end of file