UNPKG

@salla.sa/twilight-components

Version:
249 lines (244 loc) 13.3 kB
/*! * Crafted with ❤ by Salla */ import { proxyCustomElement, HTMLElement, createEvent, h, Host } from '@stencil/core/internal/client'; import { H as Helper } from './Helper.js'; import { d as defineCustomElement$3 } from './salla-button2.js'; import { d as defineCustomElement$2 } from './salla-price-range2.js'; import { d as defineCustomElement$1 } from './salla-rating-stars2.js'; var FilterOptionTypes; (function (FilterOptionTypes) { // CATEGORIES = "categories", // BRANDs = "brands", // RATING = "rating", // PRICE = "price", // RADIO = "radio", FilterOptionTypes["VALUES"] = "values"; FilterOptionTypes["VARIANTS"] = "variants"; FilterOptionTypes["MINIMUM"] = "minimum"; FilterOptionTypes["RANGE"] = "range"; })(FilterOptionTypes || (FilterOptionTypes = {})); var FilterOptionInputType; (function (FilterOptionInputType) { FilterOptionInputType["VALUES"] = "values"; FilterOptionInputType["VARIANT"] = "variants"; FilterOptionInputType["RANGE"] = "range"; })(FilterOptionInputType || (FilterOptionInputType = {})); const sallaFiltersWidgetCss = ":host{display:block}"; const SallaFiltersWidget = /*@__PURE__*/ proxyCustomElement(class SallaFiltersWidget extends HTMLElement { constructor() { super(); this.__registerHost(); this.changed = createEvent(this, "changed", 7); this.initialItemCount = 10; // Show 10 items initially this.expandedItemCount = 15; // Show 15 items when expanded this.calculatedInitialHeight = 0; // Calculated based on actual content this.calculatedExpandedHeight = 0; // Calculated based on actual content this.isOpen = true; this.isShowMore = false; this.showMoreLabel = "عرض المزيد"; this.showLessLabel = "عرض أقل"; this.page = salla.config.get('page'); this.searchQuery = ''; this.filteredCount = 0; // Track filtered results count } connectedCallback() { // Show "Show More" button if there are more than 10 items this.withLoadMore = this.option.key != 'price' && Array.isArray(this.option.values) && this.option.values.length > this.initialItemCount; salla.onReady(() => { this.page = salla.config.get('page'); }); salla.lang.onLoaded(() => { this.showMoreLabel = salla.lang.getWithDefault('common.titles.more', this.showMoreLabel); this.showLessLabel = salla.lang.getWithDefault('common.elements.show_less', this.showLessLabel); }); } componentDidLoad() { // Use fixed height per item for predictable calculations // Each item is approximately 20px, so we calculate based on item count const itemHeight = 20; // Fixed height per item const paddingPerItem = 15; // Additional padding per item for margins/spacing const allItems = this.widgetValues.querySelectorAll('.s-filters-label'); if (allItems.length > 0) { const itemsToMeasure = Math.min(this.initialItemCount, allItems.length); // Calculate initial height: (items × height) + (items × padding) this.calculatedInitialHeight = this.initialItemCount * (itemHeight + paddingPerItem); // Calculate expanded height based on first 15 items if (allItems.length > this.initialItemCount) { this.calculatedExpandedHeight = this.expandedItemCount * (itemHeight + paddingPerItem); } else { this.calculatedExpandedHeight = itemsToMeasure * (itemHeight + paddingPerItem); } } this.widgetValues.scrollHeight < this.calculatedInitialHeight && (this.withLoadMore = false); (this.withLoadMore && this.widgetValues) && (this.widgetValues.style.maxHeight = `${this.calculatedInitialHeight}px`); this.widgetContent.style.height = `${this.widgetContent.scrollHeight}px`; } /** * Asynchronously sets the height of a widget element to its current height, allowing for smooth transitions. * This function is often used in scenarios where the widget's content changes dynamically, and animating * the height adjustment is desired for a smoother user experience. * * @param {number} [delay=250] - Optional. The delay (in milliseconds) before updating the widget height. * Defaults to 250 milliseconds. * * @returns {Promise<void>} - A Promise that resolves once the widget height is set after the specified delay. * * @example * // Set widget height with the default delay (250 milliseconds) * await setWidgetHeight(); * * // Set widget height with a custom delay (e.g., 500 milliseconds) * await setWidgetHeight(500); */ async setWidgetHeight(delay = 250) { this.widgetContent.removeAttribute('style'); setTimeout(() => { let currentWidgetHeight = this.widgetContent.scrollHeight; this.widgetContent.style.height = currentWidgetHeight + 'px'; }, delay); } /** * Reset selected filter options. */ async reset() { if (this.option.type === FilterOptionTypes.RANGE) { this.priceRange.reset(); } Array.from(this.host.querySelectorAll('input')).forEach(input => input.checked = false); } /** * Action to show more or less filter options. */ async showMore() { this.isShowMore = !this.isShowMore; this.widgetContent.style.height = 'auto'; // Reset scroll position to top when collapsing to prevent content cutting if (!this.isShowMore) { this.widgetValues.scrollTop = 0; } // Use calculated heights based on actual content const maxHeight = this.isShowMore ? this.calculatedExpandedHeight : this.calculatedInitialHeight; // Calculate actual item count const actualCount = this.searchQuery ? this.filteredCount : (Array.isArray(this.option.values) ? this.option.values.length : 0); // When showing more: expand to 15 items height with scrollbar for items beyond 15 // When showing less: collapse to 10 items height this.widgetValues.style.maxHeight = `${maxHeight}px`; this.widgetValues.style.overflowY = this.isShowMore && actualCount > this.expandedItemCount ? 'auto' : 'hidden'; setTimeout(() => { this.widgetContent.style.height = `${this.widgetContent.scrollHeight}px`; }, 400); // get height after time of collapse animation (duration-300) } /** * Action to toggle widget open or closed (expand/ collapse). */ async toggleWidget() { this.isOpen = !this.isOpen; Helper.toggleElementClassIf(this.widgetContent, 's-filters-widget-opened', 's-filters-widget-closed', () => this.isOpen); } renderFilterOption(option) { if (![FilterOptionTypes.VALUES, FilterOptionTypes.MINIMUM, FilterOptionTypes.VARIANTS].includes(option.type)) { return ''; } //@ts-ignore const filteredValues = option.values.filter((filterOption) => { if (!this.searchQuery) return true; const label = typeof filterOption === 'number' ? String(filterOption) : (filterOption?.value || filterOption?.key || ''); return String(label).toLowerCase().includes(this.searchQuery.toLowerCase()); }); // Update filtered count for show more button logic this.filteredCount = filteredValues.length; // Show empty state if no results if (filteredValues.length === 0) { return (h("div", { class: "s-filters-widget-empty-state" }, salla.lang.get('common.elements.no_options') || 'لا توجد نتائج')); } //@ts-ignore return filteredValues.map((filterOption, index) => { let value = typeof filterOption == 'number' ? filterOption : (filterOption.key || filterOption.value); return h("label", { class: "s-filters-label", htmlFor: `${option.key}-option-${value}`, key: `${option.key}-option-${value}` }, h("input", { id: `${option.key}-option-${value}`, name: option.key, type: "radio", checked: this.isSelectedOption(option, value), class: `s-filters-radio`, onChange: event => this.changed.emit({ event, option, value }) }), this.getOptionLabel(option, filterOption)); }); } isSelectedOption(option, value) { if (option.type === FilterOptionTypes.MINIMUM) { return this.filtersData[option.key] == value; } if (option.type === FilterOptionTypes.VARIANTS) { return this.filtersData[option.type] && this.filtersData[option.type][Object.keys(this.filtersData[option.type])[0]] == value; } if (option.type === FilterOptionTypes.RANGE) { return this.filtersData[option.key] && this.filtersData[option.key].min == value.min && this.filtersData[option.key].max == value.max; } if (option.type === FilterOptionTypes.VALUES) { return this.filtersData[option.key] && Number(this.filtersData[option.key]) == Number(value); } return false; } getOptionLabel(option, filterOption) { if (option.key == 'rating') { //in amazon has stars & up, should we add it, to avoid those people who will come to say I selected 4 why I see 5 sars products return h("salla-rating-stars", { size: "small", value: filterOption }); } let label = filterOption.value || 'null'; //label+=filterOption.count ? ` (${salla.helpers.number(filterOption.count)})` : ''; return h("span", { class: "s-filters-option-name" }, label); } render() { return (h(Host, { key: 'e93cfb9f1f6daeb8a2121ccb36a3cf135a55e902', class: "s-filters-widget-container" }, h("h3", { key: 'd5ef45d78e0119e2ac997ded417e310c0a797c34', class: "s-filters-widget-title", onClick: () => this.toggleWidget() }, h("span", { key: 'fe4df56fad0d290395d78588456fb7d00a20ed43' }, this.option.label), h("span", { key: 'c961f86b5e06437dfdd145e164da5fe326500032', class: `s-filters-widget-plusminus ${this.isOpen ? 's-filters-widget-plusminus-active' : ''}` })), h("div", { key: '7cf1ed1b562bf16acc481f682965567d8bc1813c', class: "s-filters-widget-content", ref: (el) => this.widgetContent = el }, Array.isArray(this.option.values) && this.option.values.length > 5 && (h("div", { key: '275427371f48fdfc8ef8b80b9d3e11fa247f7d7a', class: "s-filters-widget-search-wrapper", style: { padding: '8px 0', marginBottom: '8px' } }, h("input", { key: '473c082ed9f7415beba445d36a121027eaadbbe1', type: "text", class: "s-filters-widget-search-input", placeholder: salla.lang.get('blocks.header.search_placeholder') || 'بحث...', value: this.searchQuery, onInput: (e) => this.searchQuery = e.target.value }))), h("div", { key: 'e45654731cdd801345acc5b3311d9ddab3020035', class: "s-filters-widget-values", ref: (el) => this.widgetValues = el }, h("slot", { key: '5ae673157a4b579a000901b04db8e375a52565e7' }), this.option.type !== FilterOptionTypes.RANGE ? this.renderFilterOption(this.option) : h("salla-price-range", { onChanged: (event) => { this.changed.emit(event.detail); }, ref: price => this.priceRange = price, filtersData: this.filtersData, option: this.option })), this.withLoadMore && (!this.searchQuery || this.filteredCount > this.initialItemCount) && h("a", { key: '8ec08429d8fb31b338e6f9f9cf455b5ba2155cb8', class: "s-filters-widget-more", onClick: () => this.showMore() }, !this.isShowMore ? this.showMoreLabel : this.showLessLabel)))); } get host() { return this; } static get style() { return sallaFiltersWidgetCss; } }, [4, "salla-filters-widget", { "withLoadMore": [1028, "with-load-more"], "filtersData": [16, "filters-data"], "option": [16], "isOpen": [32], "isShowMore": [32], "showMoreLabel": [32], "showLessLabel": [32], "page": [32], "searchQuery": [32], "filteredCount": [32], "setWidgetHeight": [64], "reset": [64], "showMore": [64], "toggleWidget": [64] }]); function defineCustomElement() { if (typeof customElements === "undefined") { return; } const components = ["salla-filters-widget", "salla-button", "salla-price-range", "salla-rating-stars"]; components.forEach(tagName => { switch (tagName) { case "salla-filters-widget": if (!customElements.get(tagName)) { customElements.define(tagName, SallaFiltersWidget); } break; case "salla-button": if (!customElements.get(tagName)) { defineCustomElement$3(); } break; case "salla-price-range": if (!customElements.get(tagName)) { defineCustomElement$2(); } break; case "salla-rating-stars": if (!customElements.get(tagName)) { defineCustomElement$1(); } break; } }); } defineCustomElement(); export { FilterOptionTypes as F, SallaFiltersWidget as S, defineCustomElement as d };