diff --git a/addons/web_editor/static/src/js/editor/snippets.options.js b/addons/web_editor/static/src/js/editor/snippets.options.js index 3c930b1fa20..8f1009bb88a 100644 --- a/addons/web_editor/static/src/js/editor/snippets.options.js +++ b/addons/web_editor/static/src/js/editor/snippets.options.js @@ -6363,6 +6363,75 @@ registry.DynamicSvg = SnippetOptionWidget.extend({ }, }); +/** + * Allows to handle snippets with a list of items. + */ +registry.MultipleItems = SnippetOptionWidget.extend({ + + //-------------------------------------------------------------------------- + // Options + //-------------------------------------------------------------------------- + + /** + * @see this.selectClass for parameters + */ + async addItem(previewMode, widgetValue, params) { + const $target = this.$(params.item); + const addBeforeItem = params.addBefore === 'true'; + if ($target.length) { + await new Promise(resolve => { + this.trigger_up('clone_snippet', { + $snippet: $target, + onSuccess: resolve, + }); + }); + if (addBeforeItem) { + $target.before($target.next()); + } + if (params.selectItem !== 'false') { + this.trigger_up('activate_snippet', { + $snippet: addBeforeItem ? $target.prev() : $target.next(), + }); + } + this._addItemCallback($target); + } + }, + /** + * @see this.selectClass for parameters + */ + async removeItem(previewMode, widgetValue, params) { + const $target = this.$(params.item); + if ($target.length) { + await new Promise(resolve => { + this.trigger_up('remove_snippet', { + $snippet: $target, + onSuccess: resolve, + }); + }); + this._removeItemCallback($target); + } + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * Allows to add behaviour when item added. + * + * @private + * @abstract + * @param {jQueryElement} $target + */ + _addItemCallback($target) {}, + /** + * @private + * @abstract + * @param {jQueryElement} $target + */ + _removeItemCallback($target) {}, +}); + return { SnippetOptionWidget: SnippetOptionWidget, snippetOptionRegistry: registry, diff --git a/addons/website/static/src/snippets/s_tabs/options.js b/addons/website/static/src/snippets/s_tabs/options.js index b10a7619f85..20d44a9fa8f 100644 --- a/addons/website/static/src/snippets/s_tabs/options.js +++ b/addons/website/static/src/snippets/s_tabs/options.js @@ -3,7 +3,7 @@ odoo.define('website.s_tabs_options', function (require) { const options = require('web_editor.snippets.options'); -options.registry.NavTabs = options.Class.extend({ +options.registry.NavTabs = options.registry.MultipleItems.extend({ isTopOption: true, /** @@ -26,53 +26,6 @@ options.registry.NavTabs = options.Class.extend({ this._generateUniqueIDs(); }, - //-------------------------------------------------------------------------- - // Options - //-------------------------------------------------------------------------- - - /** - * Creates a new tab and tab-pane. - * - * @see this.selectClass for parameters - */ - addTab: function (previewMode, widgetValue, params) { - var $activeItem = this.$navLinks.filter('.active').parent(); - var $activePane = this.$tabPanes.filter('.active'); - - var $navItem = $activeItem.clone(); - var $navLink = $navItem.find('.nav-link').removeClass('active show'); - var $tabPane = $activePane.clone().removeClass('active show'); - $navItem.insertAfter($activeItem); - $tabPane.insertAfter($activePane); - this._findLinksAndPanes(); - this._generateUniqueIDs(); - - $navLink.tab('show'); - }, - /** - * Removes the current active tab and its content. - * - * @see this.selectClass for parameters - */ - removeTab: function (previewMode, widgetValue, params) { - var self = this; - - var $activeLink = this.$navLinks.filter('.active'); - var $activePane = this.$tabPanes.filter('.active'); - - var $next = this.$navLinks.eq((this.$navLinks.index($activeLink) + 1) % this.$navLinks.length); - - return new Promise(resolve => { - $next.one('shown.bs.tab', function () { - $activeLink.parent().remove(); - $activePane.remove(); - self._findLinksAndPanes(); - resolve(); - }); - $next.tab('show'); - }); - }, - //-------------------------------------------------------------------------- // Private //-------------------------------------------------------------------------- @@ -112,6 +65,29 @@ options.registry.NavTabs = options.Class.extend({ }); } }, + /** + * @override + */ + _addItemCallback($target) { + $target.removeClass('active show'); + const $targetNavItem = this.$(`.nav-item a[href="#${$target.attr('id')}"]`) + .removeClass('active show').parent(); + const $navLink = $targetNavItem.clone().insertAfter($targetNavItem) + .find('.nav-link'); + this._findLinksAndPanes(); + this._generateUniqueIDs(); + $navLink.tab('show'); + }, + /** + * @override + */ + _removeItemCallback($target) { + const $targetNavLink = this.$(`.nav-item a[href="#${$target.attr('id')}"]`); + const $navLinkToShow = this.$navLinks.eq((this.$navLinks.index($targetNavLink) + 1) % this.$navLinks.length); + $targetNavLink.parent().remove(); + this._findLinksAndPanes(); + $navLinkToShow.tab('show'); + }, }); options.registry.NavTabsStyle = options.Class.extend({ diff --git a/addons/website/views/snippets/s_media_list.xml b/addons/website/views/snippets/s_media_list.xml index c08fc0b37a4..b786ab95f51 100644 --- a/addons/website/views/snippets/s_media_list.xml +++ b/addons/website/views/snippets/s_media_list.xml @@ -46,6 +46,15 @@