diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/website_sale/static/src/snippets | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/website_sale/static/src/snippets')
4 files changed, 311 insertions, 0 deletions
diff --git a/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/000.js b/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/000.js new file mode 100644 index 00000000..2bd13284 --- /dev/null +++ b/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/000.js @@ -0,0 +1,58 @@ +odoo.define('website_sale.s_dynamic_snippet_products', function (require) { +'use strict'; + +const config = require('web.config'); +const core = require('web.core'); +const publicWidget = require('web.public.widget'); +const DynamicSnippetCarousel = require('website.s_dynamic_snippet_carousel'); + +const DynamicSnippetProducts = DynamicSnippetCarousel.extend({ + selector: '.s_dynamic_snippet_products', + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * Method to be overridden in child components if additional configuration elements + * are required in order to fetch data. + * @override + * @private + */ + _isConfigComplete: function () { + return this._super.apply(this, arguments) && this.$el.get(0).dataset.productCategoryId !== undefined; + }, + /** + * + * @override + * @private + */ + _mustMessageWarningBeHidden: function() { + const isInitialDrop = this.$el.get(0).dataset.templateKey === undefined; + // This snippet has default values obtained after the initial start and render after drop. + // Because of this there is an initial refresh happening right after. + // We want to avoid showing the incomplete config message before this refresh. + // Since the refreshed call will always happen with a defined templateKey, + // if it is not set yet, we know it is the drop call and we can avoid showing the message. + return isInitialDrop || this._super.apply(this, arguments); + }, + /** + * Method to be overridden in child components in order to provide a search + * domain if needed. + * @override + * @private + */ + _getSearchDomain: function () { + const searchDomain = this._super.apply(this, arguments); + const productCategoryId = parseInt(this.$el.get(0).dataset.productCategoryId); + if (productCategoryId >= 0) { + searchDomain.push(['public_categ_ids', 'child_of', productCategoryId]); + } + return searchDomain; + }, + +}); +publicWidget.registry.dynamic_snippet_products = DynamicSnippetProducts; + +return DynamicSnippetProducts; +}); diff --git a/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/000.xml b/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/000.xml new file mode 100644 index 00000000..f45bf3c5 --- /dev/null +++ b/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/000.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<templates xml:space="preserve"> + <t t-name="website_sale.s_dynamic_snippet.products" inherit_id="website.s_dynamic_snippet.carousel"> + <xpath expr="//div" position="attributes"> + <attribute name="data-filter-id" add=""/> + </xpath> + </t> +</templates> diff --git a/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/options.js b/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/options.js new file mode 100644 index 00000000..3dabb014 --- /dev/null +++ b/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/options.js @@ -0,0 +1,109 @@ +odoo.define('website_sale.s_dynamic_snippet_products_options', function (require) { +'use strict'; + +const options = require('web_editor.snippets.options'); +const s_dynamic_snippet_carousel_options = require('website.s_dynamic_snippet_carousel_options'); + +const dynamicSnippetProductsOptions = s_dynamic_snippet_carousel_options.extend({ + + /** + * + * @override + */ + init: function () { + this._super.apply(this, arguments); + this.productCategories = {}; + }, + /** + * + * @override + */ + onBuilt: function () { + this._super.apply(this, arguments); + this._rpc({ + route: '/website_sale/snippet/options_filters' + }).then((data) => { + if (data.length) { + this.$target.get(0).dataset.filterId = data[0].id; + this.$target.get(0).dataset.numberOfRecords = this.dynamicFilters[data[0].id].limit; + this._refreshPublicWidgets(); + // Refresh is needed because default values are obtained after start() + } + }); + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * + * @override + * @private + */ + _computeWidgetVisibility: function (widgetName, params) { + if (widgetName === 'filter_opt') { + return false; + } + return this._super.apply(this, arguments); + }, + /** + * Fetches product categories. + * @private + * @returns {Promise} + */ + _fetchProductCategories: function () { + return this._rpc({ + model: 'product.public.category', + method: 'search_read', + kwargs: { + domain: [], + fields: ['id', 'name'], + } + }); + }, + /** + * + * @override + * @private + */ + _renderCustomXML: async function (uiFragment) { + await this._super.apply(this, arguments); + await this._renderProductCategorySelector(uiFragment); + }, + /** + * Renders the product categories option selector content into the provided uiFragment. + * @private + * @param {HTMLElement} uiFragment + */ + _renderProductCategorySelector: async function (uiFragment) { + const productCategories = await this._fetchProductCategories(); + for (let index in productCategories) { + this.productCategories[productCategories[index].id] = productCategories[index]; + } + const productCategoriesSelectorEl = uiFragment.querySelector('[data-name="product_category_opt"]'); + return this._renderSelectUserValueWidgetButtons(productCategoriesSelectorEl, this.productCategories); + }, + /** + * Sets default options values. + * @override + * @private + */ + _setOptionsDefaultValues: function () { + this._super.apply(this, arguments); + const templateKeys = this.$el.find("we-select[data-attribute-name='templateKey'] we-selection-items we-button"); + if (templateKeys.length > 0) { + this._setOptionValue('templateKey', templateKeys.attr('data-select-data-attribute')); + } + const productCategories = this.$el.find("we-select[data-attribute-name='productCategoryId'] we-selection-items we-button"); + if (productCategories.length > 0) { + this._setOptionValue('productCategoryId', productCategories.attr('data-select-data-attribute')); + } + }, + +}); + +options.registry.dynamic_snippet_products = dynamicSnippetProductsOptions; + +return dynamicSnippetProductsOptions; +}); diff --git a/addons/website_sale/static/src/snippets/s_products_searchbar/000.js b/addons/website_sale/static/src/snippets/s_products_searchbar/000.js new file mode 100644 index 00000000..2aaafca2 --- /dev/null +++ b/addons/website_sale/static/src/snippets/s_products_searchbar/000.js @@ -0,0 +1,136 @@ +odoo.define('website_sale.s_products_searchbar', function (require) { +'use strict'; + +const concurrency = require('web.concurrency'); +const publicWidget = require('web.public.widget'); + +const { qweb } = require('web.core'); + +/** + * @todo maybe the custom autocomplete logic could be extract to be reusable + */ +publicWidget.registry.productsSearchBar = publicWidget.Widget.extend({ + selector: '.o_wsale_products_searchbar_form', + xmlDependencies: ['/website_sale/static/src/xml/website_sale_utils.xml'], + events: { + 'input .search-query': '_onInput', + 'focusout': '_onFocusOut', + 'keydown .search-query': '_onKeydown', + }, + autocompleteMinWidth: 300, + + /** + * @constructor + */ + init: function () { + this._super.apply(this, arguments); + + this._dp = new concurrency.DropPrevious(); + + this._onInput = _.debounce(this._onInput, 400); + this._onFocusOut = _.debounce(this._onFocusOut, 100); + }, + /** + * @override + */ + start: function () { + this.$input = this.$('.search-query'); + + this.order = this.$('.o_wsale_search_order_by').val(); + this.limit = parseInt(this.$input.data('limit')); + this.displayDescription = !!this.$input.data('displayDescription'); + this.displayPrice = !!this.$input.data('displayPrice'); + this.displayImage = !!this.$input.data('displayImage'); + + if (this.limit) { + this.$input.attr('autocomplete', 'off'); + } + + return this._super.apply(this, arguments); + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @private + */ + _fetch: function () { + return this._rpc({ + route: '/shop/products/autocomplete', + params: { + 'term': this.$input.val(), + 'options': { + 'order': this.order, + 'limit': this.limit, + 'display_description': this.displayDescription, + 'display_price': this.displayPrice, + 'max_nb_chars': Math.round(Math.max(this.autocompleteMinWidth, parseInt(this.$el.width())) * 0.22), + }, + }, + }); + }, + /** + * @private + */ + _render: function (res) { + var $prevMenu = this.$menu; + this.$el.toggleClass('dropdown show', !!res); + if (res) { + var products = res['products']; + this.$menu = $(qweb.render('website_sale.productsSearchBar.autocomplete', { + products: products, + hasMoreProducts: products.length < res['products_count'], + currency: res['currency'], + widget: this, + })); + this.$menu.css('min-width', this.autocompleteMinWidth); + this.$el.append(this.$menu); + } + if ($prevMenu) { + $prevMenu.remove(); + } + }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * @private + */ + _onInput: function () { + if (!this.limit) { + return; + } + this._dp.add(this._fetch()).then(this._render.bind(this)); + }, + /** + * @private + */ + _onFocusOut: function () { + if (!this.$el.has(document.activeElement).length) { + this._render(); + } + }, + /** + * @private + */ + _onKeydown: function (ev) { + switch (ev.which) { + case $.ui.keyCode.ESCAPE: + this._render(); + break; + case $.ui.keyCode.UP: + case $.ui.keyCode.DOWN: + ev.preventDefault(); + if (this.$menu) { + let $element = ev.which === $.ui.keyCode.UP ? this.$menu.children().last() : this.$menu.children().first(); + $element.focus(); + } + break; + } + }, +}); +}); |
