From bfff71c2b871e37afdd6a5e47ee8340411b48ea6 Mon Sep 17 00:00:00 2001 From: Dirk Rudolph Date: Wed, 21 Feb 2024 13:16:04 +0100 Subject: [PATCH] feat: implement missing event handling for seamless authoring experience (#3) * feat: support instant reloading of default content * fix: reload for sections * fix: merge richtexts * fix: listen for content-update * fix: do not listen for content-update * feat: handle content-update the same way as patch * feat: handle section and main updates * feat: handle move * fix: block reload with auto blocks --- scripts/editor-support.js | 115 +++++++++++++++++++++++++++++--------- 1 file changed, 89 insertions(+), 26 deletions(-) diff --git a/scripts/editor-support.js b/scripts/editor-support.js index 359b20d65e..2e5fc7cf7c 100644 --- a/scripts/editor-support.js +++ b/scripts/editor-support.js @@ -1,42 +1,105 @@ import { decorateBlock, + decorateBlocks, decorateButtons, decorateIcons, + decorateSections, loadBlock, + loadBlocks, } from './aem.js'; +// eslint-disable-next-line import/no-unresolved +import { decorateRichtext } from './editor-support-rte.js'; +import { decorateMain } from './scripts.js'; -const connectionPrefix = 'urn:aemconnection:'; - -async function handleEditorUpdate(event) { +async function applyChanges(event) { + // redecorate default content and blocks on patches (in the properties rail) const { detail } = event; - const resource = detail?.request?.target?.resource; - if (!resource) return; + const resource = detail?.request?.target?.resource // update, patch components + || detail?.request?.target?.container?.resource // update, patch, add to sections + || detail?.request?.to?.container?.resource; // move in sections + if (!resource) return false; + const updates = detail?.response?.updates; + if (!updates.length) return false; + const { content } = updates[0]; + if (!content) return false; + const parsedUpdate = new DOMParser().parseFromString(content, 'text/html'); const element = document.querySelector(`[data-aue-resource="${resource}"]`); - const block = element?.parentElement?.closest('.block') || element?.closest('.block'); - const blockResource = block?.getAttribute('data-aue-resource'); - if (!block || !blockResource?.startsWith(connectionPrefix)) return; - const updates = detail?.response?.updates; - if (updates.length > 0) { - const { content } = updates[0]; - const newBlockDocument = new DOMParser().parseFromString(content, 'text/html'); - const newBlock = newBlockDocument?.querySelector(`[data-aue-resource="${blockResource}"]`); - if (newBlock) { - newBlock.style.display = 'none'; - block.insertAdjacentElement('afterend', newBlock); - // decorate buttons and icons - decorateButtons(newBlock); - decorateIcons(newBlock); - // decorate and load the block - decorateBlock(newBlock); - await loadBlock(newBlock); - // remove the old block and show the new one - block.remove(); - newBlock.style.display = null; + if (element) { + if (element.matches('main')) { + const newMain = parsedUpdate.querySelector(`[data-aue-resource="${resource}"]`); + newMain.style.display = 'none'; + element.insertAdjacentElement('afterend', newMain); + decorateMain(newMain); + decorateRichtext(newMain); + await loadBlocks(newMain); + element.remove(); + newMain.style.display = null; + // eslint-disable-next-line no-use-before-define + attachEventListners(newMain); + return true; + } + + const block = element.parentElement?.closest('.block[data-aue-resource]') || element?.closest('.block[data-aue-resource]'); + if (block) { + const blockResource = block.getAttribute('data-aue-resource'); + const newBlock = parsedUpdate.querySelector(`[data-aue-resource="${blockResource}"]`); + if (newBlock) { + newBlock.style.display = 'none'; + block.insertAdjacentElement('afterend', newBlock); + decorateButtons(newBlock); + decorateIcons(newBlock); + decorateBlock(newBlock); + await loadBlock(newBlock); + block.remove(); + newBlock.style.display = null; + return true; + } + } else { + // sections and default content, may be multiple in the case of richtext + const newElements = parsedUpdate.querySelectorAll(`[data-aue-resource="${resource}"],[data-richtext-resource="${resource}"]`); + if (newElements.length) { + const { parentElement } = element; + if (element.matches('.section')) { + const [newSection] = newElements; + newSection.style.display = 'none'; + element.insertAdjacentElement('afterend', newSection); + decorateButtons(newSection); + decorateIcons(newSection); + decorateRichtext(newSection); + decorateSections(parentElement); + decorateBlocks(parentElement); + await loadBlocks(newSection); + element.remove(); + newSection.style.display = null; + } else { + element.replaceWith(...newElements); + decorateButtons(parentElement); + decorateIcons(parentElement); + decorateRichtext(parentElement); + } + return true; + } } } + + return false; +} + +function attachEventListners(main) { + [ + 'aue:content-patch', + 'aue:content-update', + 'aue:content-add', + 'aue:content-move', + 'aue:content-remove', + ].forEach((eventType) => main?.addEventListener(eventType, async (event) => { + event.stopPropagation(); + const applied = await applyChanges(event); + if (!applied) window.location.reload(); + })); } -document.querySelector('main')?.addEventListener('aue:content-patch', handleEditorUpdate); +attachEventListners(document.querySelector('main'));