diff --git a/addons/website/static/src/js/editor/snippets.options.js b/addons/website/static/src/js/editor/snippets.options.js index 0dbe26b024f..9765bc57473 100644 --- a/addons/website/static/src/js/editor/snippets.options.js +++ b/addons/website/static/src/js/editor/snippets.options.js @@ -393,119 +393,6 @@ options.registry.CarouselItem = options.Class.extend({ }, }); -options.registry.navTabs = options.Class.extend({ - /** - * @override - */ - start: function () { - this._findLinksAndPanes(); - return this._super.apply(this, arguments); - }, - /** - * @override - */ - onBuilt: function () { - this._generateUniqueIDs(); - }, - /** - * @override - */ - onClone: function () { - 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 - //-------------------------------------------------------------------------- - - /** - * @override - */ - _computeWidgetVisibility: async function (widgetName, params) { - if (widgetName === 'remove_tab_opt') { - return (this.$tabPanes.length > 2); - } - return this._super(...arguments); - }, - /** - * @private - */ - _findLinksAndPanes: function () { - this.$navLinks = this.$target.find('.nav-link'); - var $el = this.$target; - do { - $el = $el.parent(); - this.$tabPanes = $el.find('.tab-pane'); - } while (this.$tabPanes.length === 0 && !$el.is('body')); - }, - /** - * @private - */ - _generateUniqueIDs: function () { - for (var i = 0; i < this.$navLinks.length; i++) { - var id = _.now() + '_' + _.uniqueId(); - var idLink = 'nav_tabs_link_' + id; - var idContent = 'nav_tabs_content_' + id; - this.$navLinks.eq(i).attr({ - 'id': idLink, - 'href': '#' + idContent, - 'aria-controls': idContent, - }); - this.$tabPanes.eq(i).attr({ - 'id': idContent, - 'aria-labelledby': idLink, - }); - } - }, -}); - options.registry.sizing_x = options.registry.sizing.extend({ /** * @override @@ -1171,6 +1058,17 @@ options.registry.SnippetMove = options.Class.extend({ return this._super(...arguments); }, + /** + * @override + */ + onFocus: function () { + // TODO improve this: hack to hide options section if snippet move is + // the only one. + const $allOptions = this.$el.parent(); + if ($allOptions.find('we-customizeblock-option').length <= 1) { + $allOptions.addClass('d-none'); + } + }, //-------------------------------------------------------------------------- // Options @@ -1182,12 +1080,20 @@ options.registry.SnippetMove = options.Class.extend({ * @see this.selectClass for parameters */ moveSnippet: function (previewMode, widgetValue, params) { + const isNavItem = this.$target[0].classList.contains('nav-item'); + const $tabPane = isNavItem ? $(this.$target.find('.nav-link')[0].hash) : null; switch (widgetValue) { case 'prev': this.$target.prev().before(this.$target); + if (isNavItem) { + $tabPane.prev().before($tabPane); + } break; case 'next': this.$target.next().after(this.$target); + if (isNavItem) { + $tabPane.next().after($tabPane); + } break; } }, diff --git a/addons/website/static/src/scss/website.scss b/addons/website/static/src/scss/website.scss index 68a79f41a13..3637f8d714b 100644 --- a/addons/website/static/src/scss/website.scss +++ b/addons/website/static/src/scss/website.scss @@ -631,7 +631,6 @@ table.table_desc tr td { } } - // // Layout // diff --git a/addons/website/static/src/snippets/s_tabs/001.scss b/addons/website/static/src/snippets/s_tabs/001.scss new file mode 100644 index 00000000000..6e132e91f31 --- /dev/null +++ b/addons/website/static/src/snippets/s_tabs/001.scss @@ -0,0 +1,25 @@ +// Tabs +.s_tabs[data-vcss="001"] { + .s_tabs_content { + &.s_tabs_slide_up, &.s_tabs_slide_down, &.s_tabs_slide_left, &.s_tabs_slide_right { + > .tab-pane.fade { + transition: all 0.2s; + } + > .tab-pane.fade.show { + transform: translateX(0rem) translateY(0rem); + } + } + &.s_tabs_slide_up > .tab-pane.fade { + transform: translateY(-1rem); + } + &.s_tabs_slide_down > .tab-pane.fade { + transform: translateY(1rem); + } + &.s_tabs_slide_left > .tab-pane.fade { + transform: translateX(-1rem); + } + &.s_tabs_slide_right > .tab-pane.fade { + transform: translateX(1rem); + } + } +} diff --git a/addons/website/static/src/snippets/s_tabs/options.js b/addons/website/static/src/snippets/s_tabs/options.js new file mode 100644 index 00000000000..b10a7619f85 --- /dev/null +++ b/addons/website/static/src/snippets/s_tabs/options.js @@ -0,0 +1,167 @@ +odoo.define('website.s_tabs_options', function (require) { +'use strict'; + +const options = require('web_editor.snippets.options'); + +options.registry.NavTabs = options.Class.extend({ + isTopOption: true, + + /** + * @override + */ + start: function () { + this._findLinksAndPanes(); + return this._super.apply(this, arguments); + }, + /** + * @override + */ + onBuilt: function () { + this._generateUniqueIDs(); + }, + /** + * @override + */ + onClone: function () { + 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 + //-------------------------------------------------------------------------- + + /** + * @override + */ + _computeWidgetVisibility: async function (widgetName, params) { + if (widgetName === 'remove_tab_opt') { + return (this.$tabPanes.length > 2); + } + return this._super(...arguments); + }, + /** + * @private + */ + _findLinksAndPanes: function () { + this.$navLinks = this.$target.find('.nav:first .nav-link'); + this.$tabPanes = this.$target.find('.tab-content:first .tab-pane'); + }, + /** + * @private + */ + _generateUniqueIDs: function () { + for (var i = 0; i < this.$navLinks.length; i++) { + var id = _.now() + '_' + _.uniqueId(); + var idLink = 'nav_tabs_link_' + id; + var idContent = 'nav_tabs_content_' + id; + this.$navLinks.eq(i).attr({ + 'id': idLink, + 'href': '#' + idContent, + 'aria-controls': idContent, + }); + this.$tabPanes.eq(i).attr({ + 'id': idContent, + 'aria-labelledby': idLink, + }); + } + }, +}); +options.registry.NavTabsStyle = options.Class.extend({ + + //-------------------------------------------------------------------------- + // Options + //-------------------------------------------------------------------------- + + /** + * Set the style of the tabs. + * + * @see this.selectClass for parameters + */ + setStyle: function (previewMode, widgetValue, params) { + const $nav = this.$target.find('.s_tabs_nav:first .nav'); + const isPills = widgetValue === 'pills'; + $nav.toggleClass('nav-tabs card-header-tabs', !isPills); + $nav.toggleClass('nav-pills', isPills); + this.$target.find('.s_tabs_nav:first').toggleClass('card-header', !isPills).toggleClass('mb-3', isPills); + this.$target.toggleClass('card', !isPills); + this.$target.find('.s_tabs_content:first').toggleClass('card-body', !isPills); + }, + /** + * Horizontal/vertical nav. + * + * @see this.selectClass for parameters + */ + setDirection: function (previewMode, widgetValue, params) { + const isVertical = widgetValue === 'vertical'; + this.$target.toggleClass('row s_col_no_resize s_col_no_bgcolor', isVertical); + this.$target.find('.s_tabs_nav:first .nav').toggleClass('flex-column', isVertical); + this.$target.find('.s_tabs_nav:first > .nav-link').toggleClass('py-2', isVertical); + this.$target.find('.s_tabs_nav:first').toggleClass('col-md-3', isVertical); + this.$target.find('.s_tabs_content:first').toggleClass('col-md-9', isVertical); + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @override + */ + _computeWidgetState: function (methodName, params) { + switch (methodName) { + case 'setStyle': + return this.$target.find('.s_tabs_nav:first .nav').hasClass('nav-pills') ? 'pills' : 'tabs'; + case 'setDirection': + return this.$target.find('.s_tabs_nav:first .nav').hasClass('flex-column') ? 'vertical' : 'horizontal'; + } + return this._super(...arguments); + }, +}); +}); diff --git a/addons/website/views/assets.xml b/addons/website/views/assets.xml index 80e65be488b..45c52729e90 100644 --- a/addons/website/views/assets.xml +++ b/addons/website/views/assets.xml @@ -115,6 +115,7 @@