From c8ccb2bac7a211741222c371bf92caac65fc9979 Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Sun, 22 Sep 2024 16:15:02 +0100 Subject: [PATCH] Lexical: Range of fixes - Prevented ui shortcuts running in editor - Added form modal closing on submit - Fixed ability to escape lists via enter on empty last item --- resources/js/components/shortcuts.js | 2 +- .../lexical/list/LexicalListItemNode.ts | 12 ++++++++++++ resources/js/wysiwyg/todo.md | 8 ++------ .../js/wysiwyg/ui/defaults/forms/objects.ts | 18 ++++++++++++------ resources/js/wysiwyg/ui/framework/forms.ts | 12 ++++++++++-- resources/js/wysiwyg/ui/framework/modals.ts | 1 + 6 files changed, 38 insertions(+), 15 deletions(-) diff --git a/resources/js/components/shortcuts.js b/resources/js/components/shortcuts.js index b22c46731..8bf26fbb5 100644 --- a/resources/js/components/shortcuts.js +++ b/resources/js/components/shortcuts.js @@ -25,7 +25,7 @@ export class Shortcuts extends Component { setupListeners() { window.addEventListener('keydown', event => { - if (event.target.closest('input, select, textarea, .cm-editor')) { + if (event.target.closest('input, select, textarea, .cm-editor, .editor-container')) { return; } diff --git a/resources/js/wysiwyg/lexical/list/LexicalListItemNode.ts b/resources/js/wysiwyg/lexical/list/LexicalListItemNode.ts index 7d12b5bd3..5026a0129 100644 --- a/resources/js/wysiwyg/lexical/list/LexicalListItemNode.ts +++ b/resources/js/wysiwyg/lexical/list/LexicalListItemNode.ts @@ -259,9 +259,21 @@ export class ListItemNode extends ElementNode { _: RangeSelection, restoreSelection = true, ): ListItemNode | ParagraphNode { + + if (this.getTextContent().trim() === '' && this.isLastChild()) { + const list = this.getParentOrThrow(); + if (!$isListItemNode(list.getParent())) { + const paragraph = $createParagraphNode(); + list.insertAfter(paragraph, restoreSelection); + this.remove(); + return paragraph; + } + } + const newElement = $createListItemNode( this.__checked == null ? undefined : false, ); + this.insertAfter(newElement, restoreSelection); return newElement; diff --git a/resources/js/wysiwyg/todo.md b/resources/js/wysiwyg/todo.md index 66878f37d..91e1a9678 100644 --- a/resources/js/wysiwyg/todo.md +++ b/resources/js/wysiwyg/todo.md @@ -7,8 +7,6 @@ ## Main Todo - Mac: Shortcut support via command. -- Translations -- Form closing on submit - Update toolbar overflows to match existing editor, incl. direction dynamic controls ## Secondary Todo @@ -17,11 +15,9 @@ - Color picker for color controls - Table caption text support - Support media src conversions (https://github.com/tinymce/tinymce/blob/release/6.6/modules/tinymce/src/plugins/media/main/ts/core/UrlPatterns.ts) -- Check translation coverage +- Deep check of translation coverage ## Bugs - List selection can get lost on nesting/unnesting -- Can't escape lists when bottom element -- Content not properly saving on new pages -- BookStack UI (non-editor) shortcuts can trigger in editor (`/` for example) \ No newline at end of file +- Content not properly saving on new pages \ No newline at end of file diff --git a/resources/js/wysiwyg/ui/defaults/forms/objects.ts b/resources/js/wysiwyg/ui/defaults/forms/objects.ts index f1575953b..228566d44 100644 --- a/resources/js/wysiwyg/ui/defaults/forms/objects.ts +++ b/resources/js/wysiwyg/ui/defaults/forms/objects.ts @@ -100,13 +100,12 @@ export const image: EditorFormDefinition = { export function $showLinkForm(link: LinkNode|null, context: EditorUiContext) { const linkModal = context.manager.createModal('link'); - let formDefaults = {}; if (link) { - formDefaults = { + const formDefaults: Record = { url: link.getURL(), text: link.getTextContent(), - title: link.getTitle(), - target: link.getTarget(), + title: link.getTitle() || '', + target: link.getTarget() || '', } context.editor.update(() => { @@ -114,9 +113,16 @@ export function $showLinkForm(link: LinkNode|null, context: EditorUiContext) { selection.add(link.getKey()); $setSelection(selection); }); - } - linkModal.show(formDefaults); + linkModal.show(formDefaults); + } else { + context.editor.getEditorState().read(() => { + const selection = $getSelection(); + const text = selection?.getTextContent() || ''; + const formDefaults = {text}; + linkModal.show(formDefaults); + }); + } } export const link: EditorFormDefinition = { diff --git a/resources/js/wysiwyg/ui/framework/forms.ts b/resources/js/wysiwyg/ui/framework/forms.ts index 615d5b4de..36371e302 100644 --- a/resources/js/wysiwyg/ui/framework/forms.ts +++ b/resources/js/wysiwyg/ui/framework/forms.ts @@ -72,6 +72,7 @@ export class EditorFormField extends EditorUiElement { export class EditorForm extends EditorContainerUiElement { protected definition: EditorFormDefinition; protected onCancel: null|(() => void) = null; + protected onSuccessfulSubmit: null|(() => void) = null; constructor(definition: EditorFormDefinition) { let children: (EditorFormField|EditorUiElement)[] = definition.fields.map(fieldDefinition => { @@ -98,6 +99,10 @@ export class EditorForm extends EditorContainerUiElement { this.onCancel = callback; } + setOnSuccessfulSubmit(callback: () => void) { + this.onSuccessfulSubmit = callback; + } + protected getFieldByName(name: string): EditorFormField|null { const search = (children: EditorUiElement[]): EditorFormField|null => { @@ -128,10 +133,13 @@ export class EditorForm extends EditorContainerUiElement { ]) ]); - form.addEventListener('submit', (event) => { + form.addEventListener('submit', async (event) => { event.preventDefault(); const formData = new FormData(form as HTMLFormElement); - this.definition.action(formData, this.getContext()); + const result = await this.definition.action(formData, this.getContext()); + if (result && this.onSuccessfulSubmit) { + this.onSuccessfulSubmit(); + } }); cancelButton.addEventListener('click', (event) => { diff --git a/resources/js/wysiwyg/ui/framework/modals.ts b/resources/js/wysiwyg/ui/framework/modals.ts index ae69302f6..3eea62ebb 100644 --- a/resources/js/wysiwyg/ui/framework/modals.ts +++ b/resources/js/wysiwyg/ui/framework/modals.ts @@ -28,6 +28,7 @@ export class EditorFormModal extends EditorContainerUiElement { const form = this.getForm(); form.setValues(defaultValues); form.setOnCancel(this.hide.bind(this)); + form.setOnSuccessfulSubmit(this.hide.bind(this)); this.getContext().manager.setModalActive(this.key, this); }