diff options
| author | Christophe Simonis <chs@odoo.com> | 2019-09-13 18:07:59 +0200 |
|---|---|---|
| committer | Christophe Simonis <chs@odoo.com> | 2019-09-13 18:07:59 +0200 |
| commit | 6c948c36bbf72dd896e0bff03aa7fbddadcbb0cd (patch) | |
| tree | f8620905c011735d8a3c340c46d1417a54a75972 /addons/website/static/src/js/editor | |
| parent | 48503fcf08eac6fed12e080d4f501f9bbc8d257d (diff) | |
| parent | 197474e5b9ad43ab2d587a98136a1a809ce9a2e7 (diff) | |
[MERGE] forward port branch saas-12.5 up to 197474e5b9a
Diffstat (limited to 'addons/website/static/src/js/editor')
| -rw-r--r-- | addons/website/static/src/js/editor/editor_menu.js | 2 | ||||
| -rw-r--r-- | addons/website/static/src/js/editor/snippets.options.js | 76 | ||||
| -rw-r--r-- | addons/website/static/src/js/editor/wysiwyg_multizone.js | 142 |
3 files changed, 183 insertions, 37 deletions
diff --git a/addons/website/static/src/js/editor/editor_menu.js b/addons/website/static/src/js/editor/editor_menu.js index 72c2e6bee81..87422efdb1f 100644 --- a/addons/website/static/src/js/editor/editor_menu.js +++ b/addons/website/static/src/js/editor/editor_menu.js @@ -113,7 +113,7 @@ var EditorMenu = Widget.extend({ save: function (reload) { var self = this; this.trigger_up('edition_will_stopped'); - return this.wysiwyg.save(true).then(function (result) { + return this.wysiwyg.save(false).then(function (result) { var $wrapwrap = $('#wrapwrap'); self.editable($wrapwrap).removeClass('o_editable'); if (result.isDirty && reload !== false) { diff --git a/addons/website/static/src/js/editor/snippets.options.js b/addons/website/static/src/js/editor/snippets.options.js index d659677c478..baa1d8c5a7c 100644 --- a/addons/website/static/src/js/editor/snippets.options.js +++ b/addons/website/static/src/js/editor/snippets.options.js @@ -1297,35 +1297,51 @@ options.registry.gallery = options.Class.extend({ * @override */ _setActive: function () { - this._super(); - var classes = _.uniq((this.$target.attr('class').replace(/(^|\s)o_/g, ' ') || '').split(/\s+/)); - this.$el.find('[data-mode]') - .removeClass('active') - .filter('[data-mode="' + classes.join('"], [data-mode="') + '"]').addClass('active'); - var mode = this.$el.find('[data-mode].active').data('mode'); + this._super(...arguments); - classes = _.uniq((this.$('img:first').attr('class') || '').split(/\s+/)); - this.$el.find('[data-styling]') + var activeModeSelectors = []; + for (const className of this.$target[0].classList) { + if (className.startsWith('o_')) { + activeModeSelectors.push('[data-mode="' + className.substring(2) + '"]'); + } + } + var activeMode = this.$el.find('[data-mode]') .removeClass('active') - .filter('[data-styling="' + classes.join('"], [data-styling="') + '"]').addClass('active'); - - this.$el.find('[data-interval]').removeClass('active') - .filter('[data-interval='+this.$target.find('.carousel:first').attr('data-interval')+']') + .filter(activeModeSelectors.join(', ')) + .addClass('active') + .data('mode'); + + var carousel = this.$target[0].querySelector('.carousel'); + var activeInterval = carousel ? carousel.dataset.interval : undefined; + var $intervalOptions = this.$el.find('[data-interval]'); + $intervalOptions.removeClass('active') + .filter('[data-interval="' + activeInterval + '"]') .addClass('active'); - - var interval = this.$target.find('.carousel:first').attr('data-interval'); - this.$el.find('[data-interval]') - .removeClass('active') - .filter('[data-interval=' + interval + ']').addClass('active'); + $intervalOptions.closest('we-collapse-area')[0] + .classList.toggle('d-none', activeMode !== 'slideshow'); var columns = this._getColumns(); - this.$el.find('[data-columns]') - .removeClass('active') - .filter('[data-columns=' + columns + ']').addClass('active'); - - this.$el.find('[data-columns]:first, [data-select-class="spc-none"]') - .parent().parent().toggle(['grid', 'masonry'].indexOf(mode) !== -1); - this.$el.find('[data-interval]:first').parent().parent().toggle(mode === 'slideshow'); + var $columnOptions = this.$el.find('[data-columns]'); + $columnOptions.removeClass('active') + .filter('[data-columns="' + columns + '"]') + .addClass('active'); + $columnOptions.closest('we-collapse-area')[0] + .classList.toggle('d-none', !(activeMode === 'grid' || activeMode === 'masonry')); + + this.el.querySelector('.o_w_image_spacing_option') + .classList.toggle('d-none', activeMode === 'slideshow'); + + var $stylingOptions = this.$el.find('[data-styling]'); + $stylingOptions.removeClass('active'); + var img = this.$target[0].querySelector('img'); + if (img) { + var activeStyleSelectors = []; + for (const className of img.classList) { + activeStyleSelectors.push('[data-styling="' + className + '"]'); + } + $stylingOptions.filter(activeStyleSelectors.join(', ')) + .addClass('active'); + } }, }); @@ -1410,7 +1426,7 @@ options.registry.topMenuTransparency = options.Class.extend({ enabled = value; }, }); - this.$el.find('[data-transparent]').addBack('[data-transparent]').toggleClass('active', !!enabled); + this.$el.find('[data-transparent]').toggleClass('active', !!enabled); }, }); @@ -1465,7 +1481,6 @@ options.registry.topMenuColor = options.registry.colorpicker.extend({ */ options.registry.anchorName = options.Class.extend({ xmlDependencies: ['/website/static/src/xml/website.editor.xml'], - preventChildPropagation: true, //-------------------------------------------------------------------------- // Public @@ -1493,6 +1508,13 @@ options.registry.anchorName = options.Class.extend({ click: function () { var $input = this.$('.o_input_anchor_name'); var anchorName = $input.val().trim().replace(/\s/g, '_'); + if (self.$target[0].id === anchorName) { + // If the chosen anchor name is already the one used by the + // element, close the dialog and do nothing else + this.close(); + return; + } + var isValid = /^[\w-]+$/.test(anchorName); var alreadyExists = isValid && $('#' + anchorName).length > 0; var anchorOK = isValid && !alreadyExists; @@ -1520,7 +1542,7 @@ options.registry.anchorName = options.Class.extend({ }); } new Dialog(this, { - title: _t("Anchor Name"), + title: _t("Link Anchor"), $content: $(qweb.render('website.dialog.anchorName', { currentAnchor: this.$target.attr('id'), })), diff --git a/addons/website/static/src/js/editor/wysiwyg_multizone.js b/addons/website/static/src/js/editor/wysiwyg_multizone.js index 202257857d2..9ed31d3afe5 100644 --- a/addons/website/static/src/js/editor/wysiwyg_multizone.js +++ b/addons/website/static/src/js/editor/wysiwyg_multizone.js @@ -1,7 +1,38 @@ odoo.define('web_editor.wysiwyg.multizone', function (require) { 'use strict'; + var Wysiwyg = require('web_editor.wysiwyg'); +var snippetsEditor = require('web_editor.snippet.editor'); +/** + * Show/hide the dropdowns associated to the given toggles and allows to wait + * for when it is fully shown/hidden. + * + * Note: this also takes care of the fact the 'toggle' method of bootstrap does + * not properly work in all cases. + * + * @param {jQuery} $toggles + * @param {boolean} [show] + * @returns {Promise<jQuery>} + */ +function toggleDropdown($toggles, show) { + return Promise.all(_.map($toggles, toggle => { + var $toggle = $(toggle); + var $dropdown = $toggle.parent(); + var shown = $dropdown.hasClass('show'); + if (shown === show) { + return; + } + var toShow = !shown; + return new Promise(resolve => { + $dropdown.one( + toShow ? 'shown.bs.dropdown' : 'hidden.bs.dropdown', + () => resolve() + ); + $toggle.dropdown(toShow ? 'show' : 'hide'); + }); + })).then(() => $toggles); +} /** * HtmlEditor @@ -13,7 +44,6 @@ var Wysiwyg = require('web_editor.wysiwyg'); * */ var WysiwygMultizone = Wysiwyg.extend({ - /** * @override */ @@ -24,7 +54,35 @@ var WysiwygMultizone = Wysiwyg.extend({ var outerHTML = this._getEscapedElement($el).prop('outerHTML'); return self._saveElement(outerHTML, self.options.recordInfo, $el[0]); }; - return this._super(); + + // Mega menu initialization: handle dropdown openings by hand + var $megaMenuToggles = this.$('.o_mega_menu_toggle'); + $megaMenuToggles.removeAttr('data-toggle').dropdown('dispose'); + $megaMenuToggles.on('click.wysiwyg_multizone', ev => { + var $toggle = $(ev.currentTarget); + + // Each time we toggle a dropdown, we will destroy the dropdown + // behavior afterwards to keep manual control of it + var dispose = ($els => $els.dropdown('dispose')); + + // First hide all other mega menus + toggleDropdown($megaMenuToggles.not($toggle), false).then(dispose); + + // Then toggle the clicked one + toggleDropdown($toggle) + .then(dispose) + .then($el => { + var isShown = $el.parent().hasClass('show'); + this.editor.snippetsMenu.toggleMegaMenuSnippets(isShown); + }); + }); + + return this._super.apply(this, arguments).then(() => { + // Showing Mega Menu snippets if one dropdown is already opened + if (this.$('.o_mega_menu').hasClass('show')) { + this.editor.snippetsMenu.toggleMegaMenuSnippets(true); + } + }); }, /** * @override @@ -32,20 +90,26 @@ var WysiwygMultizone = Wysiwyg.extend({ */ save: function () { if (this.isDirty()) { - return this.editor.save().then(function() { - return {isDirty: true}; - }); + return this._restoreMegaMenus() + .then(() => this.editor.save(false)) + .then(() => ({isDirty: true})); } else { return {isDirty: false}; } }, - + /** + * @override + */ + destroy: function () { + this._restoreMegaMenus(); + this._super.apply(this, arguments); + }, //-------------------------------------------------------------------------- // Private //-------------------------------------------------------------------------- - _getEditableArea: function() { + _getEditableArea: function () { return $(':o_editable'); }, /** @@ -59,8 +123,9 @@ var WysiwygMultizone = Wysiwyg.extend({ * page element) */ _saveElement: function (outerHTML, recordInfo, editable) { + var promises = []; var $el = $(editable); - return this._rpc({ + promises.push(this._rpc({ model: 'ir.ui.view', method: 'save', args: [ @@ -69,7 +134,66 @@ var WysiwygMultizone = Wysiwyg.extend({ $el.data('oe-xpath') || null, ], context: recordInfo.context, - }); + })); + if ($el.data('oe-field') === 'mega_menu_content') { + // On top of saving the mega menu content like any other field + // content, we must save the custom classes that were set on the + // menu itself. + // FIXME normally removing the 'show' class should not be necessary here + // TODO check that editor classes are removed here as well + var classes = _.without($el.attr('class').split(' '), 'dropdown-menu', 'o_mega_menu', 'show'); + promises.push(this._rpc({ + model: 'website.menu', + method: 'write', + args: [ + [parseInt($el.data('oe-id'))], + { + 'mega_menu_classes': classes.join(' '), + }, + ], + })); + } + return Promise.all(promises); + }, + /** + * Restores mega menu behaviors and closes them (important to do before + * saving otherwise they would be saved opened). + * + * @private + * @returns {Promise} + */ + _restoreMegaMenus: function () { + var $megaMenuToggles = this.$('.o_mega_menu_toggle'); + $megaMenuToggles.off('.wysiwyg_multizone') + .attr('data-toggle', 'dropdown') + .dropdown({}); + return toggleDropdown($megaMenuToggles, false); + }, +}); + +snippetsEditor.Class.include({ + /** + * @private + * @param {boolean} show + */ + toggleMegaMenuSnippets: function (show) { + setTimeout(() => this._activateSnippet(false)); + this.$('#snippet_mega_menu').toggleClass('d-none', !show); + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @override + */ + _insertDropzone: function ($hook) { + var $hookParent = $hook.parent(); + var $dropzone = this._super(...arguments); + $dropzone.attr('data-editor-message', $hookParent.attr('data-editor-message')); + $dropzone.attr('data-editor-sub-message', $hookParent.attr('data-editor-sub-message')); + return $dropzone; }, }); |
