Skip to content

Commit

Permalink
[IMP] web_editor, website: allow text options translation
Browse files Browse the repository at this point in the history
The goal of this commit is to add a minimal and stable compatible code
adaptation that allows to use text options (text animations & text
highlights) in the translation mode. Mainly by allowing the creation of
snippet editors if the target is a text option snippet.

opw-3686777

X-original-commit: 61fcf40
  • Loading branch information
xO-Tx committed Apr 30, 2024
1 parent 8441240 commit aa070b5
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 62 deletions.
128 changes: 83 additions & 45 deletions addons/web_editor/static/src/js/editor/snippets.editor.js
Expand Up @@ -584,14 +584,15 @@ var SnippetEditor = Widget.extend({
await focusOrBlur(editor, styles);
}
await Promise.all(editorUIsToUpdate.map(editor => editor.updateOptionsUI()));
await Promise.all(editorUIsToUpdate.map(editor => editor.updateOptionsUIVisibility()));

// As the 'd-none' class is added to option sections that have no visible
// options with 'updateOptionsUIVisibility', if no option section is
// visible, we prevent the activation of the options.
const optionsSectionVisible = editorUIsToUpdate.some(
editor => !editor.$optionsSection[0].classList.contains('d-none')
);
// A `d-none` class is added to option sections that have no visible
// options with `updateOptionsUIVisibility`. If no option section is
// visible (including the options moved to the toolbar), we prevent
// the activation of the options.
const optionsSectionVisible = await Promise.all(
editorUIsToUpdate.map((editor) => editor.updateOptionsUIVisibility())
).then(editorVisibilityValues => {
return editorVisibilityValues.some(editorVisibilityValue => editorVisibilityValue)
});
if (editorUIsToUpdate.length > 0 && !optionsSectionVisible) {
return null;
}
Expand Down Expand Up @@ -651,7 +652,22 @@ var SnippetEditor = Widget.extend({
// because some options can be located in the overlay.
const $visibleOptions = this.$optionsSection.find('we-top-button-group, we-customizeblock-option')
.children(':not(.d-none)');
this.$optionsSection.toggleClass('d-none', !$visibleOptions.length);
// Some options (e.g., text highlights / animations) may have a special
// way to be displayed in the editor: We add the options in the toolbar
// `onFocus()` and set them back `onBlur()`. Which means that the
// options section will be empty and should be hidden, while editor's
// visible options should be displayed in the toolbar DOM. We need to
// take this scenario into consideration too.
const optionsSectionEmpty = !this.$optionsSection[0].querySelector(":scope > we-customizeblock-option");
const optionsSectionVisible = $visibleOptions.length && !optionsSectionEmpty;
// At this level, we can hide the options section.
this.$optionsSection.toggleClass("d-none", !optionsSectionVisible);
// Even with a hidden options section, the editor is still considered
// visible" if it has visible toolbar options.
const visibleToolbarOptions = Object.values(this.styles).some(
(option) => option.el.closest(".oe-toolbar") && !option.el.classList.contains("d-none")
);
return optionsSectionVisible || visibleToolbarOptions;
},
/**
* Clones the current snippet.
Expand Down Expand Up @@ -1965,33 +1981,6 @@ var SnippetsMenu = Widget.extend({
},
});

if (this.options.enableTranslation) {
// Load the sidebar with the style tab only.
await this._loadSnippetsTemplates();
defs.push(this._updateInvisibleDOM());
this.$el.find('.o_we_website_top_actions').removeClass('d-none');
this.$('.o_snippet_search_filter').addClass('d-none');
this.$('#o_scroll').addClass('d-none');
this.$('button[data-action="mobilePreview"]').addClass('d-none');
this.$('#snippets_menu button').removeClass('active').prop('disabled', true);
this.$('.o_we_customize_snippet_btn').addClass('active').prop('disabled', false);
this.$('o_we_ui_loading').addClass('d-none');
$(this.customizePanel).removeClass('d-none');
this.$('#o_we_editor_toolbar_container').hide();
this.$('#o-we-editor-table-container').addClass('d-none');
return Promise.all(defs);
}

this.emptyOptionsTabContent = document.createElement('div');
this.emptyOptionsTabContent.classList.add('text-center', 'pt-5');
this.emptyOptionsTabContent.append(_t("Select a block on your page to style it."));

// Fetch snippet templates and compute it
defs.push((async () => {
await this._loadSnippetsTemplates(this.options.invalidateSnippetCache);
await this._updateInvisibleDOM();
})());

// Active snippet editor on click in the page
this.$document.on('click.snippets_menu', '*', this._onClick);
// Needed as bootstrap stop the propagation of click events for dropdowns
Expand Down Expand Up @@ -2053,6 +2042,33 @@ var SnippetsMenu = Widget.extend({
// scrolling a modal)
this.$scrollingTarget[0].addEventListener('scroll', this._onScrollingElementScroll, {capture: true});

if (this.options.enableTranslation) {
// Load the sidebar with the style tab only.
await this._loadSnippetsTemplates();
defs.push(this._updateInvisibleDOM());
this.$el.find('.o_we_website_top_actions').removeClass('d-none');
this.$('.o_snippet_search_filter').addClass('d-none');
this.$('#o_scroll').addClass('d-none');
this.$('button[data-action="mobilePreview"]').addClass('d-none');
this.$('#snippets_menu button').removeClass('active').prop('disabled', true);
this.$('.o_we_customize_snippet_btn').addClass('active').prop('disabled', false);
this.$('o_we_ui_loading').addClass('d-none');
$(this.customizePanel).removeClass('d-none');
this.$('#o_we_editor_toolbar_container').hide();
this.$('#o-we-editor-table-container').addClass('d-none');
return Promise.all(defs).then(() => {});
}

this.emptyOptionsTabContent = document.createElement('div');
this.emptyOptionsTabContent.classList.add('text-center', 'pt-5');
this.emptyOptionsTabContent.append(_t("Select a block on your page to style it."));

// Fetch snippet templates and compute it
defs.push((async () => {
await this._loadSnippetsTemplates(this.options.invalidateSnippetCache);
await this._updateInvisibleDOM();
})());

// Auto-selects text elements with a specific class and remove this
// on text changes
const alreadySelectedElements = new Set();
Expand Down Expand Up @@ -2261,7 +2277,18 @@ var SnippetsMenu = Widget.extend({
}
this._mutex.exec(() => {
if (this._currentTab === this.tabs.OPTIONS && !this.snippetEditors.length) {
this._activateEmptyOptionsTab();
const selection = this.$body[0].ownerDocument.getSelection();
const range = selection?.rangeCount && selection.getRangeAt(0);
const currentlySelectedNode = range?.commonAncestorContainer;
// In some cases (e.g. in translation mode) it's possible to have
// all snippet editors destroyed after disabling text options.
// We still want to keep the toolbar available in this case.
const isEditableTextElementSelected =
currentlySelectedNode?.nodeType === Node.TEXT_NODE &&
!!currentlySelectedNode?.parentNode?.isContentEditable;
if (!isEditableTextElementSelected) {
this._activateEmptyOptionsTab();
}
}
});
},
Expand Down Expand Up @@ -2655,12 +2682,6 @@ var SnippetsMenu = Widget.extend({
* (might be async when an editor must be created)
*/
_activateSnippet: async function ($snippet, previewMode, ifInactiveOptions) {
if (this.options.enableTranslation) {
// In translate mode, do not activate the snippet when enabling its
// corresponding invisible element. Indeed, in translate mode, we
// only want to toggle its visibility.
return;
}
if (this._blockPreviewOverlays && previewMode) {
return;
}
Expand Down Expand Up @@ -3039,9 +3060,9 @@ var SnippetsMenu = Widget.extend({
}
return $target;
};
globalSelector.is = function ($from) {
globalSelector.is = function ($from, options = {}) {
for (var i = 0, len = selectors.length; i < len; i++) {
if (selectors[i].is($from)) {
if (options.onlyTextOptions ? $from.is(self.templateOptions[i].data.textSelector) : selectors[i].is($from)) {
return true;
}
}
Expand Down Expand Up @@ -3159,6 +3180,12 @@ var SnippetsMenu = Widget.extend({
return snippetEditor.__isStarted;
}

// In translate mode, only allow creating the editor if the target is a
// text option snippet.
if (this.options.enableTranslation && !this._allowInTranslationMode($snippet)) {
return Promise.resolve(null);
}

var def;
if (this._allowParentsEditors($snippet)) {
var $parent = globalSelector.closest($snippet.parent());
Expand Down Expand Up @@ -3606,6 +3633,11 @@ var SnippetsMenu = Widget.extend({
this._hideTooltips();
this._closeWidgets();

// In translation mode, only the options tab is available.
if (this.options.enableTranslation) {
tab = this.tabs.OPTIONS;
}

this._currentTab = tab || this.tabs.BLOCKS;

if (this._$toolbarContainer) {
Expand Down Expand Up @@ -3769,6 +3801,12 @@ var SnippetsMenu = Widget.extend({
return !this.options.enableTranslation
&& !$snippet[0].classList.contains("o_no_parent_editor");
},
/**
* @private
*/
_allowInTranslationMode($snippet) {
return globalSelector.is($snippet, { onlyTextOptions: true });
},

//--------------------------------------------------------------------------
// Handlers
Expand Down
31 changes: 14 additions & 17 deletions addons/website/static/src/js/editor/snippets.editor.js
Expand Up @@ -202,6 +202,17 @@ const wSnippetMenu = weSnippetEditor.SnippetsMenu.extend({
)[0];
element.remove();
});

// TODO remove in master: should be simply replaced by a
// `data-text-selector` attribute to mark text options.
const AnimationOptionEl = $html.find('[data-js="WebsiteAnimate"]')[0];
const HighlightOptionEl = $html.find('[data-js="TextHighlight"]')[0];
if (AnimationOptionEl) {
AnimationOptionEl.dataset.textSelector = ".o_animated_text";
}
if (HighlightOptionEl) {
HighlightOptionEl.dataset.textSelector = HighlightOptionEl.dataset.selector;
}
},
/**
* Depending of the demand, reconfigure they gmap key or configure it
Expand Down Expand Up @@ -481,6 +492,9 @@ const wSnippetMenu = weSnippetEditor.SnippetsMenu.extend({
this._disableTextOptions(targetEl);
this.options.wysiwyg.odooEditor.historyStep(true);
restoreCursor();
if (this.options.enableTranslation) {
$(selectedTextParent).trigger("content_changed");
}
} else {
if (sel.getRangeAt(0).collapsed) {
return;
Expand Down Expand Up @@ -762,20 +776,6 @@ weSnippetEditor.SnippetEditor.include({
}
return this._super(...arguments);
},
/**
* @override
* @returns {Promise}
*/
async updateOptionsUIVisibility() {
await this._super(...arguments);
// TODO improve this: some website text options (like text animations,
// text highlights...) are moved to the toolbar, which leads to an empty
// "options section". The goal of this override is to hide options
// sections with no option elements.
if (!this.$optionsSection[0].querySelector(":scope > we-customizeblock-option")) {
this.$optionsSection[0].classList.add("d-none");
}
},
/**
* Changes some behaviors before the drag and drop.
*
Expand Down Expand Up @@ -836,9 +836,6 @@ wSnippetMenu.include({
*/
start() {
const _super = this._super(...arguments);
if (this.options.enableTranslation) {
return _super;
}
if (this.$body[0].ownerDocument !== this.ownerDocument) {
this.$body.on('click.snippets_menu', '*', this._onClick);
}
Expand Down

0 comments on commit aa070b5

Please sign in to comment.