UNPKG

@salla.sa/twilight-components

Version:
780 lines (773 loc) 47.1 kB
/*! * Crafted with ❤ by Salla */ import { proxyCustomElement, HTMLElement, createEvent, h, Host } from '@stencil/core/internal/client'; import { C as CheckCircleIcon } from './check.js'; import { C as CameraIcon } from './camera.js'; import { d as defineCustomElement$b } from './salla-booking-field2.js'; import { d as defineCustomElement$a } from './salla-button2.js'; import { d as defineCustomElement$9 } from './salla-color-picker2.js'; import { d as defineCustomElement$8 } from './salla-conditional-fields2.js'; import { d as defineCustomElement$7 } from './salla-datetime-picker2.js'; import { d as defineCustomElement$6 } from './salla-file-upload2.js'; import { d as defineCustomElement$5 } from './salla-loading2.js'; import { d as defineCustomElement$4 } from './salla-map2.js'; import { d as defineCustomElement$3 } from './salla-modal2.js'; import { d as defineCustomElement$2 } from './salla-progress-bar2.js'; var DisplayType; (function (DisplayType) { DisplayType["COLOR"] = "color"; DisplayType["DATE"] = "date"; DisplayType["DATETIME"] = "datetime"; DisplayType["DONATION"] = "donation"; DisplayType["IMAGE"] = "image"; DisplayType["MULTIPLE_OPTIONS"] = "multiple-options"; DisplayType["NUMBER"] = "number"; DisplayType["SINGLE_OPTION"] = "single-option"; DisplayType["DIGITAL_CARD_VALUE"] = "digital-code-value"; DisplayType["COUNTRY"] = "country"; DisplayType["SPLITTER"] = "splitter"; DisplayType["TEXT"] = "text"; DisplayType["TEXTAREA"] = "textarea"; DisplayType["THUMBNAIL"] = "thumbnail"; DisplayType["TIME"] = "time"; DisplayType["RADIO"] = "radio"; DisplayType["CHECKBOX"] = "checkbox"; DisplayType["MAP"] = "map"; DisplayType["FILE"] = "file"; DisplayType["COLOR_PICKER"] = "color_picker"; DisplayType["BOOKING"] = "booking"; })(DisplayType || (DisplayType = {})); var Currency; (function (Currency) { Currency["Sar"] = "SAR"; })(Currency || (Currency = {})); var FileIcon = `<!-- Generated by IcoMoon.io --> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"> <title>file-upload</title> <path d="M21.333 24c0.341 0 0.683-0.131 0.943-0.391 0.521-0.521 0.521-1.364 0-1.885l-5.333-5.333c-0.123-0.123-0.271-0.22-0.433-0.288-0.327-0.135-0.693-0.135-1.019 0-0.163 0.068-0.311 0.165-0.433 0.288l-5.333 5.333c-0.521 0.521-0.521 1.364 0 1.885s1.364 0.521 1.885 0l3.057-3.057v10.115c0 0.736 0.597 1.333 1.333 1.333s1.333-0.597 1.333-1.333v-10.115l3.057 3.057c0.26 0.26 0.601 0.391 0.943 0.391zM28.943 9.724l-9.333-9.333c-0.249-0.251-0.589-0.391-0.943-0.391h-12c-2.205 0-4 1.795-4 4v24c0 2.205 1.795 4 4 4h4c0.736 0 1.333-0.597 1.333-1.333s-0.597-1.333-1.333-1.333h-4c-0.735 0-1.333-0.599-1.333-1.333v-24c0-0.735 0.599-1.333 1.333-1.333h11.448l8.552 8.552v16.781c0 0.735-0.599 1.333-1.333 1.333h-4c-0.736 0-1.333 0.597-1.333 1.333s0.597 1.333 1.333 1.333h4c2.205 0 4-1.795 4-4v-17.333c0-0.353-0.14-0.693-0.391-0.943z"></path> </svg> `; const sallaProductOptionsCss = ""; const SallaProductOptions$1 = /*@__PURE__*/ proxyCustomElement(class SallaProductOptions extends HTMLElement { constructor() { super(); this.__registerHost(); this.changed = createEvent(this, "changed", 7); this.fileTypes = { pdf: 'application/pdf', png: 'image/png', jpg: 'image/jpeg', word: 'application/doc,application/ms-doc,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document', exl: 'application/excel,application/vnd.ms-excel,application/x-excel,application/x-msexcel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', txt: 'text/plain', }; this.outOfStockText = ""; this.donationAmount = salla.lang.get('pages.products.donation_amount'); this.selectDonationAmount = salla.lang.getWithDefault('pages.products.select_donation_amount', 'تحديد مبلغ التبرع'); this.selectAmount = salla.lang.getWithDefault('pages.products.select_amount', 'اختر المبلغ'); this.isCustomDonation = false; this.selectedOptions = []; this.disableCardValue = true; this.availableDigitalCardValues = []; this.outSkus = []; /** * Avoid selection of previous default or selected card value option * when switching between digital card country options for the 1st time */ this.ignoreDefaultCardValue = false; /** * The id of the product to which the options are going to be fetched for. */ this.productId = salla.config.get('page.id'); this.handleDonationOptions = (event, detail, type) => { if (detail === 'custom' && type === 'input') { salla.helpers.inputDigitsOnly(event.target); salla.event.emit('product-options::donation-changed', { id: this.productId, price: event.target.value }); return; } event.preventDefault(); event.stopPropagation(); this.isCustomDonation = event.target.value === 'custom'; if (this.donationInput) { if (event.target.value === 'custom') { this.donationInput.value = ''; this.donationInput.focus(); } else { this.donationInput.value = event.target.value; } if (detail === 'custom') { return; } salla.event.emit('product-options::donation-changed', { id: this.productId, price: event.target.value }); } }; this.hideLabel = (option) => { if (option.type === DisplayType.DONATION && (option.donation && !option.donation.can_donate)) { return true; } return false; }; this.getExpireDonationMessage = (option) => { if (!option.donation) { return; } const completed = option.donation.target_amount <= option.donation.collected_amount; return h("div", { class: { "s-product-options-donation-message": true, "s-product-options-donation-completed": completed, "s-product-options-donation-expired": !completed } }, h("p", null, option.donation.target_message), h("span", { innerHTML: completed ? salla.money(option.donation.target_amount) : '' })); }; this.canDisabled = !salla.config.get('store.settings.product.notify_options_availability'); salla.lang.onLoaded(() => { this.outOfStockText = salla.lang.get("pages.products.out_of_stock"); this.donationAmount = salla.lang.get('pages.products.donation_amount'); this.selectDonationAmount = salla.lang.getWithDefault('pages.products.select_donation_amount', 'تحديد مبلغ التبرع'); this.selectAmount = salla.lang.getWithDefault('pages.products.select_amount', 'اختر المبلغ'); }); if (this.options) { try { this.setOptionsData(Array.isArray(this.options) ? this.options : JSON.parse(this.options)); return; } catch (e) { salla.log('Bad json passed via options prop'); } } if (!Array.isArray(this.optionsData)) { salla.log('Options is not an array[] ---> ', this.optionsData); this.setOptionsData([]); } if (this.productId && !salla.url.is_page('cart')) { salla.api.product.getDetails(this.productId, ['options']).then(resp => this.setOptionsData(resp.data.options)); } } /** * Sets the options data for the product * @param optionsData - Array of product options */ async setOptionsData(optionsData) { var _a, _b; this.optionsData = optionsData; const that = this; (_b = (_a = this.optionsData[0]) === null || _a === void 0 ? void 0 : _a.details) === null || _b === void 0 ? void 0 : _b.forEach(function (detail) { Object.entries(detail.skus_availability || {}) .filter(sku => !sku[1]) .map(sku => that.outSkus.push(Number(sku[0]))); }); } /** * Get the id's of the selected options. * */ async getSelectedOptionsData() { const selectedOptions = {}; const formData = this.host.getElementSallaData(); formData.forEach((value, key) => { if (key.startsWith('options[')) { (selectedOptions[key.replace('options[', '').replace(']', '')] = value); } }); return selectedOptions; } /** * Report options form validity. * */ async reportValidity() { const requiredElements = this.host.querySelectorAll('[required]'); let pass = true; for (let i = 0; i < requiredElements.length; i++) { //if there is only one invalid option, return false if ('reportValidity' in requiredElements[i] && !requiredElements[i].reportValidity()) { pass = false; } } return pass; } /** * Return true if there is any out of stock options are selected and vise versa. * */ async hasOutOfStockOption() { var _a, _b; return this.selectedOptions.some(option => option.is_out) || (((_a = this.selectedSkus) === null || _a === void 0 ? void 0 : _a.length) && ((_b = this.selectedSkus) === null || _b === void 0 ? void 0 : _b.every(sku => this.outSkus.includes(sku)))); } /** * Get selected options. * */ async getSelectedOptions() { return this.selectedOptions; } /** * Get a specific option by its id. * */ async getOption(option_id) { return this.optionsData.find(option => option.id === option_id); } // @ts-ignore invalidHandler(event, option) { const closestProductOption = event.target.closest('.s-product-options-option'); if (!closestProductOption.classList.contains('s-product-options-option-error')) { closestProductOption.classList.add('s-product-options-option-error'); } if (!salla.url.is_page('cart')) { const firstInvalidElement = this.host.querySelector('.s-product-options-option-error'); if (firstInvalidElement === closestProductOption) { this.scrollToElement(closestProductOption); } } } scrollToElement(element) { if (element) { element.scrollIntoView({ behavior: 'smooth', block: 'center' }); } } changedHandler(event, option, fireChangeEvent = true) { const data = { event: event, option: option, detail: null, productId: this.productId }; if (option.details) { const detail = option.details.find((detail) => { return Number(detail.id) === Number(event.target.value); }); data.detail = detail; } if (option.type === 'country') { this.handleCountryOptionChange(event, data.detail); } const optionElement = event.target.closest('.s-product-options-option'); if (event.target.value || ((option.type === DisplayType.FILE || option.type === DisplayType.IMAGE) && event.type === 'added') || (option.type === DisplayType.MAP && event.type === 'selected' && (event.target.lat && event.target.lng))) { setTimeout(() => { optionElement.classList.remove('s-product-options-option-error'); }, 200); } if (option.type === DisplayType.DONATION) { salla.event.emit('product-options::donation-changed', { id: this.productId, price: event.target.value }); } this.setSelectedSkus(); this.handleRequiredMultipleOptions(option); const index = this.selectedOptions.findIndex(opt => opt.option_id === data.option.id); if (data.option.type === DisplayType.MULTIPLE_OPTIONS) { // Handle multiple selections const detailIndex = this.selectedOptions.findIndex(opt => { var _a; return opt.option_id === data.option.id && (opt === null || opt === void 0 ? void 0 : opt.id) === ((_a = data.detail) === null || _a === void 0 ? void 0 : _a.id); }); if (detailIndex > -1) { // If the option is already selected, remove it (unselect) this.selectedOptions.splice(detailIndex, 1); } else { // If the option is not selected, add it to the selectedOptions array this.selectedOptions.push(Object.assign(Object.assign({}, data.detail), { option_id: data.option.id })); } } else { // Handle single selection if (!data.detail || Object.keys(data.detail).length === 0) { // If there is no value for the single-select, remove it from the selectedOptions array if (index > -1) { this.selectedOptions.splice(index, 1); } } else { // If a value exists, update or add the selection if (index > -1) { // Replace the existing selection with the new one this.selectedOptions[index] = Object.assign(Object.assign({}, data.detail), { option_id: data.option.id }); } else { // If no selection exists for this input, add the new selection this.selectedOptions.push(Object.assign(Object.assign({}, data.detail), { option_id: data.option.id })); } } } // Update optionsData directly this.optionsData = this.optionsData.map(opt => { if (opt.id === data.option.id) { return Object.assign(Object.assign({}, opt), { details: opt.details.map(detail => { var _a, _b; return (Object.assign(Object.assign({}, detail), { is_selected: data.option.type === DisplayType.MULTIPLE_OPTIONS ? this.selectedOptions.some(selected => selected.id === detail.id) : Number(detail.id) === Number((_a = data.detail) === null || _a === void 0 ? void 0 : _a.id), value: (_b = data.detail) === null || _b === void 0 ? void 0 : _b.value })); }) }); } return opt; }); // Emit the event only if fireChangeEvent is true if (fireChangeEvent) { this.changed.emit(data); salla.event.emit('product-options::change', data); } } /** * loop throw all selected details, then get common sku, if it's only one, means we selected all of them; */ setSelectedSkus() { this.selectedSkus = this.selectedOptions.map(detail => Object.keys(detail.skus_availability || {})) .reduce((p, c) => p.filter(e => c.includes(e)), []) // Initialize accumulator as an empty array .map(sku => Number(sku)); } handleRequiredMultipleOptions(option) { if (option.type !== DisplayType.MULTIPLE_OPTIONS || !option.required) { return; } const optionContainer = this.host.querySelector(`[data-option-id="${option.id}"]`); const hasChecked = optionContainer.querySelectorAll('input:checked').length; optionContainer.querySelectorAll('input').forEach(input => input.toggleAttribute('required', !hasChecked)); } getLatLng(value, type) { return value ? value.split(',')[type === 'lat' ? 0 : 1] : ''; } getDisplayForType(option) { if (this[`${option.type}Option`]) { return this[`${option.type}Option`](option); } if (option.type === DisplayType.COLOR_PICKER) { return this.colorPickerOption(option); } if (option.type === DisplayType.MULTIPLE_OPTIONS) { return this.multipleOptions(option); } if (option.type === DisplayType.SINGLE_OPTION) { return this.singleOption(option); } if (option.type === DisplayType.DIGITAL_CARD_VALUE) { return this.digitalCardValuesOption(option); } if (option.type === DisplayType.COUNTRY) { return this.countryOption(option); } if (option.type === DisplayType.BOOKING && salla.url.is_page("cart")) { return h("salla-booking-field", { onInvalidInput: (e) => this.invalidHandler(e, option), option: option, productId: option.value }); } salla.log(`Couldn't find options type(${option.type})😢`); return ''; } getOptionShownWhen(option) { return option.visibility_condition ? { "data-show-when": `options[${option.visibility_condition.option}] ${option.visibility_condition.operator} ${option.visibility_condition.value}` } : {}; } getAvailableDigitalCardSKUs(detail) { const digitalCardOption = this.optionsData.find(({ type }) => type === 'digital-code-value'); if (!digitalCardOption) throw new Error('product-options:: No digital card options found'); const outofStockSKUs = Object.keys(detail.skus_availability).filter(key => detail.skus_availability[key] === false); this.availableDigitalCardValues = digitalCardOption.details.filter((op) => { return !Object.keys(op.skus_availability).filter(SKU_key => outofStockSKUs.includes(SKU_key)).length; }); } handleCountryOptionChange(event, detail) { event.stopImmediatePropagation(); this.ignoreDefaultCardValue = true; const currentCardValue = this.host.querySelector("input[data-code-value]:checked"); if (currentCardValue) currentCardValue.checked = false; const digitalCardOption = this.optionsData.find(({ type }) => type === 'digital-code-value'); if (!digitalCardOption) throw new Error('product-options:: No digital card options found'); this.getAvailableDigitalCardSKUs(detail); } getSelectedDigitalCardOptions(option) { const selectedOption = option.details.find(detail => detail.is_selected); const defaultOption = option.details.find(detail => !!detail.is_default) || option.details[0]; /*option.details[0] only applys for counrty options*/ if (!['digital-code-value', 'country'].includes(option.type)) return; return selectedOption || defaultOption; } //we need the cart Id for productOption Image componentWillLoad() { if (salla.url.is_page("cart")) { this.disableCardValue = false; this.fillSelectedOptions(); } if (this.config) { try { this.optionConfig = typeof this.config === 'string' ? JSON.parse(this.config) : this.config; } catch (error) { console.error('Failed to parse JSON in config prop:', error); } } const shouldSelectDefaultOption = this.optionsData.filter(({ type }) => ["country", "digital-card-value"].includes(type)).length > 0 && salla.url.is_page('cart'); if (shouldSelectDefaultOption) { const countryOption = this.optionsData.find(option => option.type === 'country'); const defaultSelection = countryOption && this.getSelectedDigitalCardOptions(countryOption); if (defaultSelection) { this.getAvailableDigitalCardSKUs(defaultSelection); } } this.outOfStockText = salla.lang.get('pages.products.out_of_stock'); return salla.onReady(() => { const needsCartId = (!salla.storage.get('cart.id') && this.optionsData.some(option => ['file', 'image'].includes(option.type))); return needsCartId ? salla.api.cart.getCurrentCartId(false, "salla-product-options") : null; }); } hideDigitalCardsOptions(option) { return (this.disableCardValue && option.type === DisplayType.DIGITAL_CARD_VALUE && !salla.url.is_page("cart")); } render() { var _a; if (((_a = this.optionsData) === null || _a === void 0 ? void 0 : _a.length) === 0) { return; } return (h(Host, { class: "s-product-options-wrapper" }, h("salla-conditional-fields", null, this.optionsData.map((option) => h("div", Object.assign({ key: option.id, class: `s-product-options-option-container${option.visibility_condition || this.hideDigitalCardsOptions(option) ? ' hidden' : ''}`, "data-option-id": option.id }, this.getOptionShownWhen(option)), option.name === 'splitter' ? this.splitterOption() : h("div", { class: { "s-product-options-option": true, "s-product-options-option-booking": option.type === DisplayType.BOOKING && salla.url.is_page("cart") }, "data-option-type": option.type, "data-option-required": `${option.required}` }, h("label", { htmlFor: `options[${option.id}]`, class: `s-product-options-option-label ${this.hideLabel(option) ? 's-product-options-option-label-hidden' : ''}` }, h("b", null, option.name, option.required && h("span", null, " * "), " "), h("small", null, option.placeholder)), h("div", { class: `s-product-options-option-content ${this.hideLabel(option) || (option.type === DisplayType.BOOKING && salla.url.is_page("cart")) ? 's-product-options-option-content-full-width' : ''}` }, this.getDisplayForType(option)))))))); } generateUniqueKey(defaultValue) { return this.uniqueKey ? `${defaultValue}-${this.uniqueKey}` : defaultValue; } fillSelectedOptions() { this.selectedOptions = this.optionsData.reduce((acc, opt) => { const selectedDetails = opt.details.filter(detail => detail.is_selected); const mappedDetails = selectedDetails.map(detail => (Object.assign(Object.assign({}, detail), { option_id: opt.id }))); return acc.concat(mappedDetails); }, []); } componentDidLoad() { var _a, _b; if (((_a = this.optionsData) === null || _a === void 0 ? void 0 : _a.length) === 0 && !this.optionsData.some(option => option.type === DisplayType.DONATION)) { return; } const selectedDonationOption = (_b = this.optionsData.find(option => option.type === DisplayType.DONATION)) === null || _b === void 0 ? void 0 : _b.details.find(detail => detail.is_selected); if (!selectedDonationOption) { return; } setTimeout(() => { salla.event.emit('product-options::donation-changed', { id: this.productId, price: selectedDonationOption.additional_price }); }, 1000); } //@ts-ignore donationOption(option, product) { var _a, _b; return h("div", { class: "s-product-options-donation-wrapper" }, ((_a = option.donation) === null || _a === void 0 ? void 0 : _a.can_donate) ? [ option.donation ? h("div", { key: option.id, class: "s-product-options-donation-progress" }, h("salla-progress-bar", { donation: option.donation })) : '', option.details.length ? [h("h4", { key: option.id }, this.selectAmount), h("div", { key: option.id, class: "s-product-options-donation-options" }, option.details.map((detail, i) => h("div", { key: option.id, class: "s-product-options-donation-options-item" }, h("input", { id: this.generateUniqueKey(`donation-option-${i}`), type: "radio", name: "donating_option", checked: detail.is_selected, value: detail.additional_price, onChange: e => this.handleDonationOptions(e, detail, 'option') }), h("label", { htmlFor: this.generateUniqueKey(`donation-option-${i}`) }, h("span", { innerHTML: salla.money(detail.name) })))), ((_b = option.donation) === null || _b === void 0 ? void 0 : _b.custom_amount_enabled) ? h("div", { class: "s-product-options-donation-options-item" }, h("input", { id: this.generateUniqueKey("donation-option-custom"), type: "radio", name: "donating_option", value: "custom", onChange: e => this.handleDonationOptions(e, 'custom', 'option') }), h("label", { htmlFor: this.generateUniqueKey("donation-option-custom") }, h("span", null, " ", this.selectDonationAmount, " "))) : '')] : '', h("div", { key: option.id, class: { "s-product-options-donation-input-group": true, "shown": !option.details.length || (option.details.length && this.isCustomDonation) } }, h("input", { type: "text", id: "donating-amount", name: "donation_amount", class: "s-form-control", ref: el => { this.donationInput = el; }, value: option.details.length && option.details.some(detail => detail.is_selected) ? option.details.find(detail => detail.is_selected).additional_price : option.value, // required placeholder: option.placeholder, onInput: e => this.handleDonationOptions(e, 'custom', 'input'), onBlur: e => this.changedHandler(e, option), onInvalid: (e) => this.invalidHandler(e, option) }), h("span", { class: "s-product-options-donation-amount-currency" }, salla.config.currency(salla.config.get('user.currency_code')).symbol)) ] : this.getExpireDonationMessage(option)); } fileUploader(option, additions = null) { var _a; return h("salla-file-upload", Object.assign({}, (additions || {}), { "payload-name": "file", value: option.value, "instant-upload": true, name: `options[${option.id}]`, required: !option.visibility_condition && option.required, height: "120px", onAdded: (e) => this.changedHandler(e, option), url: salla.cart.api.getUploadImageEndpoint(), "form-data": { cart_item_id: this.productId, product_id: this.productId }, onInvalidInput: (e) => this.invalidHandler(e, option), class: { "s-product-options-image-input": true, required: option.required } }), h("div", { class: "s-product-options-filepond-placeholder" }, h("span", { class: "s-product-options-filepond-placeholder-icon", innerHTML: ((_a = additions.accept) === null || _a === void 0 ? void 0 : _a.split(',').every(type => type.includes('image'))) ? CameraIcon : FileIcon }), h("p", { class: "s-product-options-filepond-placeholder-text" }, salla.lang.get('common.uploader.drag_and_drop')), h("span", { class: "filepond--label-action" }, salla.lang.get('common.uploader.browse')))); } //@ts-ignore imageOption(option) { return this.fileUploader(option, { accept: 'image/png,image/jpeg,image/jpg,image/gif' }); } //@ts-ignore fileOption(option) { const types = option.details.map(detail => this.fileTypes[detail.name]).filter(Boolean); return (types === null || types === void 0 ? void 0 : types.length) ? this.fileUploader(option, { accept: types.join(',') }) : 'File types not selected.'; } // TODO: (ONLY FOR TESTING!) find a better way to make it testable, e.g. wrap it with a unique class like textOption //@ts-ignore numberOption(option) { return h("input", { type: "text", value: option.value, class: "s-form-control", required: !option.visibility_condition && option.required, name: `options[${option.id}]`, placeholder: option.placeholder, onBlur: e => this.changedHandler(e, option), onInvalid: (e) => this.invalidHandler(e, option), onInput: e => salla.helpers.inputDigitsOnly(e.target) }); } //@ts-ignore splitterOption() { return h("div", { class: "s-product-options-splitter" }); } //@ts-ignore textOption(option) { return h("div", { class: "s-product-options-text" }, h("input", { type: "text", value: option.value, maxLength: option === null || option === void 0 ? void 0 : option.length, class: 's-form-control', required: !option.visibility_condition && option.required, name: `options[${option.id}]`, placeholder: option.placeholder, onInvalid: (e) => this.invalidHandler(e, option), onChange: e => this.changedHandler(e, option) })); } //@ts-ignore textareaOption(option) { //todo::remove mt-1 class, and if it's okay to remove the tag itself will be great return h("div", { class: "s-product-options-textarea" }, h("div", { class: "mt-1" }, h("textarea", { rows: 4, value: option.value, maxLength: option === null || option === void 0 ? void 0 : option.length, class: "s-form-control", required: !option.visibility_condition && option.required, id: `options[${option.id}]`, name: `options[${option.id}]`, placeholder: option.placeholder, onInvalid: (e) => this.invalidHandler(e, option), onChange: (e) => this.changedHandler(e, option) }))); } //@ts-ignore mapOption(option) { return h("salla-map", { zoom: 15, lat: this.getLatLng(option.value, 'lat'), lng: this.getLatLng(option.value, 'lng'), name: `options[${option.id}]`, searchable: true, required: option.required, onInvalidInput: (e) => this.invalidHandler(e, option), onSelected: e => this.changedHandler(e, option) }); } colorPickerOption(option) { return h("salla-color-picker", { onSubmitted: e => this.changedHandler(e, option), name: `options[${option.id}]`, required: !option.visibility_condition && option.required, onInvalidInput: (e) => this.invalidHandler(e, option), color: option.value }); } /** * ============= Date Time options ============= */ //@ts-ignore timeOption(option) { return h("salla-datetime-picker", { noCalendar: true, enableTime: true, dateFormat: "h:i K", value: option.value, placeholder: option.name, required: !option.visibility_condition && option.required, name: `options[${option.id}]`, class: "s-product-options-time-element", onInvalidInput: (e) => this.invalidHandler(e, option), onPicked: e => this.changedHandler(e, option) }); } //@ts-ignore dateOption(option) { //todo:: consider date-range @see https://github.com/SallaApp/theme-raed/blob/master/src/assets/js/partials/product-options.js#L8-L23 return h("div", { class: "s-product-options-date-element" }, h("salla-datetime-picker", { value: option.value, placeholder: option.name, required: !option.visibility_condition && option.required, minDate: new Date(), name: `options[${option.id}]`, onInvalidInput: (e) => this.invalidHandler(e, option), onPicked: e => this.changedHandler(e, option) })); } //@ts-ignore datetimeOption(option) { //todo:: consider date-range @see https://github.com/SallaApp/theme-raed/blob/master/src/assets/js/partials/product-options.js#L8-L23 return h("div", { class: "s-product-options-datetime-element" }, h("salla-datetime-picker", { enableTime: true, value: option.value, dateFormat: "Y-m-d G:i:K", placeholder: option.name, required: !option.visibility_condition && option.required, name: `options[${option.id}]`, maxDate: option.to_date_time, minDate: option.from_date_time, onInvalidInput: (e) => this.invalidHandler(e, option), onPicked: e => this.changedHandler(e, option) })); } /** * ============= Advanced options ============= */ getOptionDetailName(detail, outOfStock = true, optionType) { var _a; let detailName; if (optionType && optionType === DisplayType.COLOR) { detailName = detail.name + ((outOfStock && this.isOptionDetailOut(detail) && !salla.url.is_page("cart")) && !this.hideOutLabel ? ` <br/> <p> ${this.outOfStockText} </p>` : '') + (detail.additional_price ? ` <p> (${salla.money(detail.additional_price, false)}) </p>` : ''); } if (!detailName) { detailName = detail.name + ((outOfStock && this.isOptionDetailOut(detail) && !salla.url.is_page("cart")) && !this.hideOutLabel ? ` - ${this.outOfStockText}` : '') + (detail.additional_price ? ` (${salla.money(detail.additional_price, false)})` : ''); } //Some merchants adding price to the names of the options, //and because we are using this inside select option, we need to replace the html currency symbol with the store currency symbol return detailName.replace('<i class=sicon-sar></i>', ((_a = salla.config.currency()) === null || _a === void 0 ? void 0 : _a.symbol) || 'ر.س'); } isOptionDetailOut(detail) { var _a; if (detail.is_out || !detail.skus_availability || !((_a = this.selectedSkus) === null || _a === void 0 ? void 0 : _a.length)) { return detail.is_out; } const isDetailSelected = this.selectedOptions.filter(option => option.id === detail.id).length; //if the current options is the only selected option, so we are sure that it's not out, because there is no other options selected yet if (isDetailSelected && this.selectedOptions.length === 1) { return false; } //if current details has sku in the possible outSkus it's out for sure if (isDetailSelected) { //here we will get the possible outSkus for current selected options const outSelectableSkus = this.selectedSkus.filter(sku => this.outSkus.includes(sku)); return Object.keys(detail.skus_availability).some(sku => outSelectableSkus.includes(Number(sku))); } return this.selectedOptions.some(option => option.is_out && option.option_id !== detail.option_id); } /** * Renders a single input element (radio or checkbox) for an option detail. * @param type - The type of input element ('radio' or 'checkbox'). * @param detail - The detail object representing an option detail. * @param option - The parent option object containing the details. * @param isRequired - Indicates if the input is required based on the option's rules. * @param name - The name attribute for the input element. * @returns HTMLElement - A labeled input element. */ renderInput(type, detail, option, isRequired, name, buttonStyle) { const id = this.generateUniqueKey(`${type}-${option.id}-${detail.id}`); const isDisabled = this.isOptionDetailOut(detail); return (h("label", { class: { "s-product-options-disabled": isDisabled, } }, h("input", { id: id, type: type, name: name, value: detail.id, disabled: isDisabled, required: isRequired, checked: detail.is_selected, onInvalid: (e) => this.invalidHandler(e, option), onChange: (e) => this.changedHandler(e, option) }), h("div", { class: { "s-product-options-grid-mode-span": buttonStyle, "s-product-options-disabled": isDisabled } }, this.getOptionDetailName(detail)))); } /** * Renders a collection of input elements for all details of an option. * @param type - The type of input elements ('radio' or 'checkbox'). * @param option - The parent option object containing the details. * @param isRequired - Indicates if the inputs are required based on the option's rules. * @returns HTMLElement[] - An array of labeled input elements. */ renderOptionDetails(type, option, isRequired, buttonStyle = false) { const name = type === 'radio' ? `options[${option.id}]` : `options[${option.id}][]`; return option === null || option === void 0 ? void 0 : option.details.map((detail) => this.renderInput(type, detail, option, isRequired, name, buttonStyle)); } /** * Renders a dropdown (select) element for a single-option selection. * @param option - The parent option object. * @returns HTMLElement - A select dropdown element with all option details. */ renderSelect(option) { return (h("div", null, h("select", { name: `options[${option.id}]`, required: !option.visibility_condition && option.required, class: "s-form-control", onInvalid: (e) => this.invalidHandler(e, option), onChange: (e) => this.changedHandler(e, option) }, h("option", { value: "" }, option.placeholder), option === null || option === void 0 ? void 0 : option.details.map((detail) => (h("option", { key: detail.id, value: detail.id, disabled: this.canDisabled && this.isOptionDetailOut(detail), selected: detail.is_selected }, this.getOptionDetailName(detail))))))); } /** * Renders a grid-based layout for option inputs (radio or checkbox). * @param type - The type of input elements ('radio' or 'checkbox'). * @param option - The parent option object containing the details. * @param isRequired - Indicates if the inputs are required based on the option's rules. * @returns HTMLElement - A grid-based container with input elements. */ renderButtonStyle(type, option, isRequired) { return (h("div", { class: "s-product-options-grid-mode" }, this.renderOptionDetails(type, option, isRequired, true))); } /** * Renders a single-option selection, either as a grid or dropdown, based on configuration. * @param option - The parent option object. * @returns HTMLElement - The rendered single-option element. */ singleOption(option) { var _a, _b; const buttonStyle = ((_b = (_a = this.optionConfig) === null || _a === void 0 ? void 0 : _a['single-option']) === null || _b === void 0 ? void 0 : _b.type) === 'button'; const isRequired = !option.visibility_condition && option.required; return buttonStyle ? this.renderButtonStyle('radio', option, isRequired) : this.renderSelect(option); } /** * Renders a multiple-option selection, either as a grid or list, based on configuration. * @param option - The parent option object. * @returns HTMLElement - The rendered multiple-option element. */ multipleOptions(option) { var _a, _b; const buttonStyle = ((_b = (_a = this.optionConfig) === null || _a === void 0 ? void 0 : _a['multiple-option']) === null || _b === void 0 ? void 0 : _b.type) === 'button'; const isRequired = option.required && !option.details.some((detail) => detail.is_selected) && !option.visibility_condition; return buttonStyle ? this.renderButtonStyle('checkbox', option, isRequired) : (h("div", { class: { 's-product-options-multiple-options-wrapper': true, required: option.required, } }, this.renderOptionDetails('checkbox', option, isRequired))); } //@ts-ignore colorOption(option) { return (h("fieldset", { class: "s-product-options-colors-wrapper" }, option === null || option === void 0 ? void 0 : option.details.map((detail) => (h("div", { class: "s-product-options-colors-item", key: detail.id }, h("input", { type: "radio", value: detail.id, required: !option.visibility_condition && option.required, checked: detail.is_selected, name: `options[${option.id}]`, disabled: this.canDisabled && this.isOptionDetailOut(detail), id: this.generateUniqueKey(`color-${this.productId}-${option.id}-${detail.id}`), onInvalid: (e) => this.invalidHandler(e, option), onChange: (e) => this.changedHandler(e, option) }), h("label", { htmlFor: this.generateUniqueKey(`color-${this.productId}-${option.id}-${detail.id}`) }, h("span", { style: { backgroundColor: detail.color } }), h("div", { innerHTML: this.getOptionDetailName(detail, true, option.type) }))))))); } //@ts-ignore thumbnailOption(option) { return h("div", { class: "s-product-options-thumbnails-wrapper" }, option.details.map((detail) => { return h("div", { key: detail.id }, h("input", { type: "radio", value: detail.id, "data-itemid": detail.id, required: !option.visibility_condition && option.required, checked: detail.is_selected, name: `options[${option.id}]`, "data-img-id": detail.option_value, disabled: this.canDisabled && this.isOptionDetailOut(detail), id: this.generateUniqueKey(`option_${this.productId}-${option.id}_${detail.id}`), onInvalid: (e) => this.invalidHandler(e, option), onChange: (e) => this.changedHandler(e, option) }), h("label", { htmlFor: this.generateUniqueKey(`option_${this.productId}-${option.id}_${detail.id}`), "data-img-id": detail.option_value, class: "go-to-slide" }, h("img", { "data-src": detail.image, src: detail.image, title: detail.name, alt: detail.name }), h("span", { innerHTML: CheckCircleIcon, class: "s-product-options-thumbnails-icon" }), this.isOptionDetailOut(detail) ? [ h("small", { key: detail.id, class: "s-product-options-thumbnails-stock-badge" }, this.outOfStockText), this.canDisabled ? h("div", { key: detail.id, class: "s-product-options-thumbnails-badge-overlay" }) : '', ] : ''), h("p", null, this.getOptionDetailName(detail, false), " ")); })); } // Digital card options digitalCardValuesOption(option) { return h("div", { class: "s-product-options-digital-card-wrapper" }, this.availableDigitalCardValues.length > 0 ? this.availableDigitalCardValues.map((detail) => { var _a, _b, _c; const id = String(detail.id); return h("label", { htmlFor: this.generateUniqueKey(id.toString()), key: id, class: "s-product-options-digital-card-option" }, h("input", Object.assign({ type: "radio", "data-code-value": true, class: "s-form-control s-product-options-digital-card-input", value: detail.id, name: `options[${option.id}]`, id: this.generateUniqueKey(id.toString()), required: !option.visibility_condition && option.required, onInvalid: (e) => this.invalidHandler(e, option) }, (!this.ignoreDefaultCardValue ? { defaultChecked: ((_a = this.getSelectedDigitalCardOptions(option)) === null || _a === void 0 ? void 0 : _a.id) === detail.id } : {}))), h("span", null, detail.name, " ", (_c = (_b = salla.config) === null || _b === void 0 ? void 0 : _b.currency()) === null || _c === void 0 ? void 0 : _c.symbol)); }) : h("div", { class: "s-product-options-digital-card-out-of-stock" })); } countryOption(option) { return h("div", { class: "s-product-options-digital-card-wrapper" }, option.details.map((detail) => { var _a; return h("label", { htmlFor: this.generateUniqueKey(detail.id.toString()), key: detail.id, class: { "s-product-options-digital-card-option": true, "s-product-options-digital-card-option-stock-out": detail.is_out } }, h("input", Object.assign({ id: this.generateUniqueKey(detail.id.toString()), type: "radio", class: "s-form-control s-product-options-digital-card-input", value: detail.id, name: `options[${option.id}]`, disabled: detail.is_out, required: !option.visibility_condition && option.required, onInvalid: (e) => this.invalidHandler(e, option), onChange: e => this.changedHandler(e, option), onClick: () => { this.disableCardValue = false; } }, (salla.url.is_page("cart") ? { defaultChecked: ((_a = this.getSelectedDigitalCardOptions(option)) === null || _a === void 0 ? void 0 : _a.id) === detail.id } : {}))), h("img", { loading: 'lazy', alt: detail.code, height: 24, width: 24, class: "s-product-options-country-flag", src: `https://cdn.assets.salla.network/prod/admin/cp/assets/flags/1x1/${String(detail.code).toLocaleLowerCase()}.svg` }), h("span", null, detail.name)); })); } get host() { return this; } static get style() { return sallaProductOptionsCss; } }, [0, "salla-product-options", { "productId": [2, "product-id"], "options": [1], "hideOutLabel": [1, "hide-out-label"], "uniqueKey": [1, "unique-key"], "config": [1], "optionsData": [32], "outOfStockText": [32], "donationAmount": [32], "selectDonationAmount": [32], "selectAmount": [32], "isCustomDonation": [32], "selectedOptions": [32], "canDisabled": [32], "selectedSkus": [32], "selectedOutSkus": [32], "disableCardValue": [32], "availableDigitalCardValues": [32], "setOptionsData": [64], "getSelectedOptionsData": [64], "reportValidity": [64], "hasOutOfStockOption": [64], "getSelectedOptions": [64], "getOption": [64] }]); function defineCustomElement$1() { if (typeof customElements === "undefined") { return; } const components = ["salla-product-options", "salla-booking-field", "salla-button", "salla-color-picker", "salla-conditional-fields", "salla-datetime-picker", "salla-file-upload", "salla-loading", "salla-map", "salla-modal", "salla-progress-bar"]; components.forEach(tagName => { switch (tagName) { case "salla-product-options": if (!customElements.get(tagName)) { customElements.define(tagName, SallaProductOptions$1); } break; case "salla-booking-field": if (!customElements.get(tagName)) { defineCustomElement$b(); } break; case "salla-button": if (!customElements.get(tagName)) { defineCustomElement$a(); } break; case "salla-color-picker": if (!customElements.get(tagName)) { defineCustomElement$9(); } break; case "salla-conditional-fields": if (!customElements.get(tagName)) { defineCustomElement$8(); } break; case "salla-datetime-picker": if (!customElements.get(tagName)) { defineCustomElement$7(); } break; case "salla-file-upload": if (!customElements.get(tagName)) { defineCustomElement$6(); } break; case "salla-loading": if (!customElements.get(tagName)) { defineCustomElement$5(); } break; case "salla-map": if (!customElements.get(tagName)) { defineCustomElement$4(); } break; case "salla-modal": if (!customElements.get(tagName)) { defineCustomElement$3(); } break; case "salla-progress-bar": if (!customElements.get(tagName)) { defineCustomElement$2(); } break; } }); } const SallaProductOptions = SallaProductOptions$1; const defineCustomElement = defineCustomElement$1; export { SallaProductOptions, defineCustomElement }; //# sourceMappingURL=salla-product-options.js.map //# sourceMappingURL=salla-product-options.js.map