Skip to content

Commit

Permalink
[WIP] disable legacy options and keep editors in DOM
Browse files Browse the repository at this point in the history
Prior to this, when dropping a snippets, its editors would be started
but not added to the DOM, which means that when clicking on it, there
would be a significant delay before options would be accessible.
Note that this delay is present when clicking on a Snippet that is not
dropped for the first time.

The reason for this delay is because even thought the widgets where not
in the DOM, they were still stored somewhere inside the started editor,
reducing the need for renders when clicking on a snippet that was
previously enabled.

This commit tries to mimic the old behaviour by keeping every element in
the DOM and mounted, just hidden with a d-none when the editor is not
currently enabled. This could have some side effects, like in tours
where a SnippetEditor selected is not the one currently visible.

For this reason, this commit is kept separate for now.
  • Loading branch information
detrouxdev committed Aug 6, 2024
1 parent 67342bd commit 0565709
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 75 deletions.
117 changes: 70 additions & 47 deletions addons/web_editor/static/src/js/editor/snippets.editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,7 @@ var SnippetEditor = Widget.extend({
if (editorUIsToUpdate.length > 0 && !optionsSectionVisible) {
return null;
}
return this._customize$Elements;
return this.getOptions();
},
/**
* Returns the OWL Options templates to mount their widgets.
Expand Down Expand Up @@ -860,6 +860,7 @@ var SnippetEditor = Widget.extend({
option.isTopOption = true;
}
} else {
return;
option = new (options.registry[optionName] || options.Class)(
this,
val.$el.children(),
Expand Down Expand Up @@ -1892,6 +1893,59 @@ var SnippetEditor = Widget.extend({
},
});

class SnippetOptionsManager extends Component {
static props = {
editors: { type: Object },
enabledEditors: { type: Array },
onOptionMounted: { type: Function },
activateTab: { type: Function },
execWithLoadingEffect: { type: Function },
};
static template = "web_editor.SnippetOptionsManager";

setup() {
useEffect(
() => {
this.props.execWithLoadingEffect(async () => {
const editorToEnable = this.props.enabledEditors[0];
let enabledOptions;
if (editorToEnable) {
enabledOptions = await editorToEnable.toggleOptions(true);
if (!enabledOptions) {
// As some options can only be generated using JavaScript
// (e.g. 'SwitchableViews'), it may happen at this point
// that the overlay is activated even though there are no
// options. That's why we disable the overlay if there are
// no options to enable.
editorToEnable.toggleOverlay(false);
}
}
if (enabledOptions) {
this.props.activateTab(SnippetsMenu.tabs.OPTIONS);
}
});
},
() => {
// The compute dependencies can never return an empty array or
// the effect will not be reapplied, and therefore the tab will
// not change to blocks.
if (this.props.enabledEditors.length) {
return this.props.enabledEditors;
} else {
return [false];
}
}
);
}

getEditors() {
return [...this.props.editors].sort((e1, e2) => {
return this.props.enabledEditors.indexOf(e1) < this.props.enabledEditors.indexOf(e2);
});
}
}


/**
* Management of drag&drop menu and snippet related behaviors in the page.
*/
Expand Down Expand Up @@ -1958,7 +2012,7 @@ class SnippetsMenu extends Component {

static template = "web_editor.SnippetsMenu";

static components = { Toolbar, LinkTools };
static components = { Toolbar, LinkTools, SnippetOptionsManager };

setup() {
super.setup(...arguments);
Expand Down Expand Up @@ -3013,17 +3067,11 @@ class SnippetsMenu extends Component {
|| ifInactiveOptions && this.state.enabledEditorHierarchy.includes(editorToEnable)) {
return editorToEnable;
}
// TODO: @owl-options remove when legacy is completely converted.
let hasOwlOptions = false;

if (!previewMode) {
this.state.enabledEditorHierarchy = [];
let current = editorToEnable;
while (current && current.$target) {
// TODO: @owl-options remove when legacy is completely converted.
hasOwlOptions = current._hasOwlOptions;
// TODO: @owl-options, make sure changes to instances
// of SnippetEditor should not trigger re-renders.
this.state.enabledEditorHierarchy.push(markRaw(current));
current = current.getParent();
}
Expand Down Expand Up @@ -3054,33 +3102,21 @@ class SnippetsMenu extends Component {
parentEditor.toggleOverlay(true, previewMode);
}
}
customize$Elements = await editorToEnable.toggleOptions(true);
this.state.options = editorToEnable.getOptions();
// TODO: this code is handled by SnippetOptionsManager now
// customize$Elements = await editorToEnable.toggleOptions(true);
} else {
for (const editor of this.snippetEditors) {
if (editor.isSticky()) {
editor.toggleOverlay(true, false);
customize$Elements = await editor.toggleOptions(true);
// customize$Elements = await editor.toggleOptions(true);
this.state.enabledEditorHierarchy.push(editor);
}
}
}

if (!previewMode) {
// As some options can only be generated using JavaScript
// (e.g. 'SwitchableViews'), it may happen at this point
// that the overlay is activated even though there are no
// options. That's why we disable the overlay if there are
// no options to enable.
if (editorToEnable && !customize$Elements) {
editorToEnable.toggleOverlay(false);
}
if (!editorToEnable || this.state.enabledEditorHierarchy.lenght === 0) {
this._updateRightPanelContent({
content: customize$Elements || [],
// TODO: @owl-options, disable the change of tab,
// here, instead let the onOptionMounted do it. To review
// tab: customize$Elements ? this.tabs.OPTIONS : this.tabs.BLOCKS,
tab: !hasOwlOptions && customize$Elements ? this.tabs.OPTIONS : this.tabs.BLOCKS,
});
tab: this.tabs.BLOCKS
})
}

return editorToEnable;
Expand Down Expand Up @@ -4027,7 +4063,7 @@ class SnippetsMenu extends Component {
* the new content of the customizePanel
* @param {this.tabs.VALUE} [tab='blocks'] - the tab to select
*/
_updateRightPanelContent({content, tab, ...options}) {
_updateRightPanelContent({tab, ...options}) {
this._hideTooltips();
this._closeWidgets();

Expand All @@ -4036,30 +4072,15 @@ class SnippetsMenu extends Component {
tab = SnippetsMenu.tabs.OPTIONS;
}

this.state.currentTab = tab || SnippetsMenu.tabs.BLOCKS;

if (this._$toolbarContainer) {
this._$toolbarContainer[0].remove();
}
this.state.showToolbar = false;
this._$toolbarContainer = null;
if (content) {
// The toolbar component will be hidden or shown by state.showToolbar
// as it is an OWL Component, OWL is in charge of the HTML for that
// component. So we do not want to remove it.
// TODO: This should be improved when SnippetEditor / SnippetOptions
// are converted to OWL.
while (this.customizePanel.firstChild?.id !== "legacyOptionsLimiter") {
this.customizePanel.removeChild(this.customizePanel.firstChild);
}
$(this.customizePanel).prepend(content);
if (this.state.currentTab === this.tabs.OPTIONS && !options.forceEmptyTab) {
this._addToolbar();
}
if (this.state.currentTab === this.tabs.OPTIONS && !options.forceEmptyTab) {
this._addToolbar();
}
if (options.forceEmptyTab) {
this.state.showToolbar = false;
}
this.state.currentTab = tab;
}
/**
* Scrolls to given snippet.
Expand Down Expand Up @@ -5157,7 +5178,6 @@ class SnippetsMenu extends Component {
this.execWithLoadingEffect(async () => {
await Promise.all(this.state.enabledEditorHierarchy.map(editor => editor.updateOptionsUI()));
await Promise.all(this.state.enabledEditorHierarchy.map(editor => editor.updateOptionsUIVisibility()));
this.state.currentTab = this.tabs.OPTIONS;
});
}

Expand Down Expand Up @@ -5427,6 +5447,9 @@ class SnippetsMenu extends Component {
}
snippet.renaming = false;
}
activateTab(tab) {
this._updateRightPanelContent({tab});
}
}

export default {
Expand Down
60 changes: 32 additions & 28 deletions addons/web_editor/static/src/xml/snippets.xml
Original file line number Diff line number Diff line change
Expand Up @@ -217,34 +217,7 @@
</t>
</div>
<div class="o_we_customize_panel" t-ref="customize-panel" t-att-class="{ 'd-none': state.currentTab === constructor.tabs.BLOCKS }">
<div class="d-none" id="legacyOptionsLimiter"/>
<t t-foreach="[...state.enabledEditorHierarchy].reverse()" t-as="editor" t-key="editor.key">
<t t-set="snippetOptions" t-value="editor.getOptions()"/>
<we-customizeblock-options t-if="snippetOptions.length > 0">
<we-title>
<span>
[OWL] <t t-out="editor.name"/>
<t t-out="editor.extraTitle"/>
</span>
<we-top-button-group>
<t t-foreach="snippetOptions" t-as="option" t-key="option_index">
<t t-if="option.isTopOption" t-component="option.renderingComponent"
snippetOption="option"
onOptionMounted.bind="onOptionMounted"/>
</t>
</we-top-button-group>
</we-title>
<t t-foreach="snippetOptions" t-as="option" t-key="option_index">
<!-- todo: @owl-options this div encapsulation is
there so solve a weird crash -->
<div t-if="!option.isTopOption">
<t t-component="option.renderingComponent"
snippetOption="option"
onOptionMounted.bind="onOptionMounted"/>
</div>
</t>
</we-customizeblock-options>
</t>
<SnippetOptionsManager editors="snippetEditors" enabledEditors="state.enabledEditorHierarchy" onOptionMounted="onOptionMounted" activateTab.bind="activateTab" execWithLoadingEffect.bind="this.execWithLoadingEffect"/>
<we-customizeblock-options id="o_we_editor_toolbar_container" t-att-class="{ 'd-none': !state.showToolbar }">
<we-title>
<span t-out="state.toolbarTitle"/>
Expand Down Expand Up @@ -287,4 +260,35 @@
</ul>
</t>

<t t-name="web_editor.SnippetOptionsManager">
<t t-foreach="getEditors()" t-as="editor" t-key="editor.key">
<t t-set="snippetOptions" t-value="editor.getOptions()"/>
<we-customizeblock-options t-if="snippetOptions.length > 0" t-att-class="{'d-none': props.enabledEditors.indexOf(editor) lt 0 }">
<we-title>
<span>
<t t-out="editor.name"/>
<t t-out="editor.extraTitle"/>
</span>
<we-top-button-group>
<t t-foreach="snippetOptions" t-as="option" t-key="option_index">
<t t-if="option.isTopOption" t-component="option.renderingComponent"
snippetOption="option"
onOptionMounted="props.onOptionMounted"/>
</t>
<we-button class="fa fa-fw fa-clone oe_snippet_clone o_we_link o_we_hover_success" data-title="Duplicate Container" aria-label="Duplicate Container" t-on-click="(ev) => editor._onCloneClick(ev)"/>
<we-button class="fa fa-fw fa-trash oe_snippet_remove o_we_link o_we_hover_danger" data-title="Remove Block" aria-label="Remove Block" t-on-click="(ev) => editor._onRemoveClick(ev)"/>
</we-top-button-group>
</we-title>
<t t-foreach="snippetOptions" t-as="option" t-key="option_index">
<!-- todo: @owl-options this div encapsulation is
there so solve a weird crash -->
<div t-if="!option.isTopOption">
<t t-component="option.renderingComponent"
snippetOption="option"
onOptionMounted="props.onOptionMounted"/>
</div>
</t>
</we-customizeblock-options>
</t>
</t>
</templates>

0 comments on commit 0565709

Please sign in to comment.