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 7199c4a254e478f4f29dac7a2c2e600381f5ad27..d551682a3567ebd1bc17184f1bf4eb703e8ea808 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 ee70d777e1213b2314e526cdba3a5f2157a79bce..96437e24a2f37445d6525fb58f687ae8d6e462fe 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 {