From 922d3e63b597b1ef7fbbb5ebb4641a3ad8bdf4ed Mon Sep 17 00:00:00 2001 From: "Louis (loco)" <loco@odoo.com> Date: Thu, 23 Feb 2023 16:21:14 +0000 Subject: [PATCH] [FIX] web_editor: reorder the elements of the invisible elements panel Steps to reproduce the bug: - Add a form snippet on the footer of the website page. - Change the visibility of the form snippet to "conditionally". - On the footer settings, deactivate the "Page Visibility". => The form snippet is still present on the "Invisible Elements" panel but clicking on it has no effects. Indeed, it is inside the footer which is hidden. The goal of this commit is to reorganize the "Invisible Elements" panel in order to better visualize the hierarchy between the different invisible elements. To do so, the list of the invisible snippet elements (`[...$invisibleSnippets]`) is scanned. Each invisible snippet that is the descendant of another is discarded of the list (to only keep the "root" ones) and a map is created with its `keys` set to invisible snippets that have invisible descendants. The `value` corresponding to an invisible snippet element is a list filled with all its descendant invisible snippets except those that have a closer invisible snippet ancestor. The list of the root snippets is then scanned. Each root snippet is inserted in the DOM as well as their descendant snippets found thanks to the newly created map. Note that thanks to this commit, another problem is solved: - Add a cookie bar on the website. - Add a Text-Image snippet and change its visibility to "conditionally". - Save and edit again. Note that the "Cookies Bar" is above the "Text- Image" on the "Invisible Elements" panel. - Click to display the cookie bar. => The order of the "Cookies Bar" and the "Text-Image" is switched on the "Invisible Elements" panel. Indeed, before this commit, the order of the snippets was influenced by the order of their snippet editor creation. Because this order was not always the same from one call to `_updateInvisibleDOM()` to another, a glitch could happen when toggling a snippet visibility. This is now fixed as the order of the invisible snippets in the "Invisible Elements" panel is determined either by their order in the list `rootInvisibleSnippetEls` or their order in the lists contained as value in the map `descendantPerSnippet`. task-3203914 Part-of: odoo/odoo#113549 --- .../static/src/js/editor/snippets.editor.js | 70 ++++++++++++++++--- .../static/src/scss/wysiwyg_snippets.scss | 15 ++++ 2 files changed, 76 insertions(+), 9 deletions(-) diff --git a/addons/web_editor/static/src/js/editor/snippets.editor.js b/addons/web_editor/static/src/js/editor/snippets.editor.js index 7199c4a254e4..d551682a3567 100644 --- a/addons/web_editor/static/src/js/editor/snippets.editor.js +++ b/addons/web_editor/static/src/js/editor/snippets.editor.js @@ -1912,16 +1912,68 @@ var SnippetsMenu = Widget.extend({ } $invisibleDOMPanelEl.toggleClass('d-none', !$invisibleSnippets.length); - const proms = _.map($invisibleSnippets, async el => { - const editor = await this._createSnippetEditor($(el)); - const $invisEntry = $('<div/>', { - class: 'o_we_invisible_entry d-flex align-items-center justify-content-between', - text: editor.getName(), - }).append($('<i/>', {class: `fa ${editor.isTargetVisible() ? 'fa-eye' : 'fa-eye-slash'} ml-2`})); - $invisibleDOMPanelEl.append($invisEntry); - this.invisibleDOMMap.set($invisEntry[0], el); + // descendantPerSnippet: a map with its keys set to invisible + // snippets that have invisible descendants. The value corresponding + // to an invisible snippet element is a list filled with all its + // descendant invisible snippets except those that have a closer + // invisible snippet ancestor. + const descendantPerSnippet = new Map(); + // Filter the "$invisibleSnippets" to only keep the root snippets + // and create the map ("descendantPerSnippet") of the snippets and + // their descendant snippets. + const rootInvisibleSnippetEls = [...$invisibleSnippets].filter(invisibleSnippetEl => { + const ancestorInvisibleEl = invisibleSnippetEl + .parentElement.closest(".o_snippet_invisible"); + if (!ancestorInvisibleEl) { + return true; + } + const descendantSnippets = descendantPerSnippet.get(ancestorInvisibleEl) || []; + descendantPerSnippet.set(ancestorInvisibleEl, + [...descendantSnippets, invisibleSnippetEl]); + return false; }); - return Promise.all(proms); + // Insert an invisible snippet in its "parentEl" element. + const createInvisibleElement = async (invisibleSnippetEl, isRootParent, isDescendant, + parentEl) => { + const editor = await this._createSnippetEditor($(invisibleSnippetEl)); + const invisibleEntryEl = document.createElement("div"); + invisibleEntryEl.className = `${isRootParent ? "o_we_invisible_root_parent" : ""}`; + invisibleEntryEl.classList.add("o_we_invisible_entry", "d-flex", + "align-items-center", "justify-content-between"); + invisibleEntryEl.innerText = isDescendant ? `â”” ${editor.getName()}` : + editor.getName(); + const iconEl = document.createElement("i"); + const eyeIconClass = editor.isTargetVisible() ? "fa-eye" : "fa-eye-slash"; + iconEl.classList.add("fa", "ml-2", eyeIconClass); + invisibleEntryEl.appendChild(iconEl); + parentEl.appendChild(invisibleEntryEl); + this.invisibleDOMMap.set(invisibleEntryEl, invisibleSnippetEl); + }; + // Insert all the invisible snippets contained in "snippetEls" as + // well as their descendants in the "parentEl" element. If + // "snippetEls" is set to "rootInvisibleSnippetEls" and "parentEl" + // is set to "$invisibleDOMPanelEl[0]", then fills the right + // invisible panel like this: + // rootInvisibleSnippet + // â”” descendantInvisibleSnippet + // â”” descendantOfDescendantInvisibleSnippet + // â”” etc... + const createInvisibleElements = async (snippetEls, isDescendant, parentEl) => { + for (const snippetEl of snippetEls) { + const descendantSnippetEls = descendantPerSnippet.get(snippetEl); + // An element is considered as "RootParent" if it has one or + // more invisible descendants but is not a descendant. + await createInvisibleElement(snippetEl, + !isDescendant && !!descendantSnippetEls, isDescendant, parentEl); + if (descendantSnippetEls) { + // Insert all the descendant snippets in a list. + const listEntryEl = document.createElement("ul"); + await createInvisibleElements(descendantSnippetEls, true, listEntryEl); + parentEl.appendChild(listEntryEl); + } + } + }; + return createInvisibleElements(rootInvisibleSnippetEls, false, $invisibleDOMPanelEl[0]); }, false); }, /** diff --git a/addons/web_editor/static/src/scss/wysiwyg_snippets.scss b/addons/web_editor/static/src/scss/wysiwyg_snippets.scss index ee70d777e121..96437e24a2f3 100644 --- a/addons/web_editor/static/src/scss/wysiwyg_snippets.scss +++ b/addons/web_editor/static/src/scss/wysiwyg_snippets.scss @@ -1702,6 +1702,21 @@ body.editor_enable.editor_has_snippets { background-color: $o-we-sidebar-bg; } } + + div.o_we_invisible_root_parent { + padding-bottom: 3px; + } + + ul { + list-style: none; + padding-inline-start: 15px; + margin-bottom: $o-we-sidebar-content-field-spacing - 3px; + + div.o_we_invisible_entry { + padding-top: 3px; + padding-bottom: 3px; + } + } } &.o_we_backdrop { -- GitLab