@salla.sa/twilight-components 2.11.53 → 2.11.54

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/cjs/salla-button_36.cjs.entry.js +40 -1
  2. package/dist/cjs/salla-product-options.cjs.entry.js +25 -16
  3. package/dist/collection/components/salla-color-picker/salla-color-picker.js +22 -0
  4. package/dist/collection/components/salla-datetime-picker/salla-datetime-picker.js +23 -1
  5. package/dist/collection/components/salla-file-upload/salla-file-upload.js +31 -1
  6. package/dist/collection/components/salla-map/salla-map.js +22 -0
  7. package/dist/collection/components/salla-product-options/salla-product-options.js +25 -16
  8. package/dist/components/salla-color-picker2.js +8 -0
  9. package/dist/components/salla-datetime-picker2.js +8 -0
  10. package/dist/components/salla-file-upload2.js +16 -1
  11. package/dist/components/salla-map2.js +8 -0
  12. package/dist/components/salla-product-options.js +25 -16
  13. package/dist/esm/salla-button_36.entry.js +40 -1
  14. package/dist/esm/salla-product-options.entry.js +25 -16
  15. package/dist/esm-es5/salla-button_36.entry.js +5 -5
  16. package/dist/esm-es5/salla-product-options.entry.js +2 -2
  17. package/dist/twilight/p-368bbdf1.entry.js +4 -0
  18. package/dist/twilight/p-7075226e.system.entry.js +4 -0
  19. package/dist/twilight/p-84d8b36b.system.js +1 -1
  20. package/dist/twilight/p-b4d54536.entry.js +36 -0
  21. package/dist/twilight/p-bebccd3d.system.entry.js +53 -0
  22. package/dist/twilight/twilight.esm.js +1 -1
  23. package/dist/types/components/salla-color-picker/salla-color-picker.d.ts +4 -0
  24. package/dist/types/components/salla-datetime-picker/salla-datetime-picker.d.ts +5 -1
  25. package/dist/types/components/salla-file-upload/salla-file-upload.d.ts +5 -0
  26. package/dist/types/components/salla-map/salla-map.d.ts +4 -0
  27. package/dist/types/components/salla-product-options/salla-product-options.d.ts +1 -0
  28. package/dist/types/components.d.ts +17 -1
  29. package/package.json +3 -3
  30. package/dist/twilight/p-126f5fed.system.entry.js +0 -4
  31. package/dist/twilight/p-96e39318.system.entry.js +0 -53
  32. package/dist/twilight/p-ae6ad245.entry.js +0 -36
  33. package/dist/twilight/p-d01e39b0.entry.js +0 -4
@@ -2519,6 +2519,7 @@ const SallaColorPicker = class {
2519
2519
  constructor(hostRef) {
2520
2520
  index.registerInstance(this, hostRef);
2521
2521
  this.colorChanged = index.createEvent(this, "colorChanged", 7);
2522
+ this.invalidInput = index.createEvent(this, "invalidInput", 7);
2522
2523
  this.submitted = index.createEvent(this, "submitted", 7);
2523
2524
  this.popupOpened = index.createEvent(this, "popupOpened", 7);
2524
2525
  this.popupClosed = index.createEvent(this, "popupClosed", 7);
@@ -2625,6 +2626,13 @@ const SallaColorPicker = class {
2625
2626
  componentDidLoad() {
2626
2627
  this.canvas.style.backgroundColor = this.color;
2627
2628
  this.initColorPicker();
2629
+ this.colorInput.addEventListener('invalid', e => {
2630
+ this.invalidInput.emit(e);
2631
+ });
2632
+ this.colorInput.addEventListener('input', () => {
2633
+ this.colorInput.setCustomValidity('');
2634
+ this.colorInput.reportValidity();
2635
+ });
2628
2636
  }
2629
2637
  setPopIpPosition() {
2630
2638
  const popup = this.host.querySelector('.picker_wrapper');
@@ -5304,6 +5312,7 @@ const SallaDatetimePicker = class {
5304
5312
  constructor(hostRef) {
5305
5313
  index.registerInstance(this, hostRef);
5306
5314
  this.picked = index.createEvent(this, "picked", 7);
5315
+ this.invalidInput = index.createEvent(this, "invalidInput", 7);
5307
5316
  /**
5308
5317
  * Two way data binding to retrieve the selected date[time] value
5309
5318
  */
@@ -5528,6 +5537,13 @@ const SallaDatetimePicker = class {
5528
5537
  // onClose: this.handleOnClose(selectedDates, dateStr, instance)
5529
5538
  };
5530
5539
  flatpickr(this.dateInput, options);
5540
+ this.dateInput.addEventListener('invalid', e => {
5541
+ this.invalidInput.emit(e);
5542
+ });
5543
+ this.dateInput.addEventListener('input', () => {
5544
+ this.dateInput.setCustomValidity('');
5545
+ this.dateInput.reportValidity();
5546
+ });
5531
5547
  }
5532
5548
  };
5533
5549
  SallaDatetimePicker.style = sallaDatetimePickerCss;
@@ -20637,6 +20653,7 @@ const SallaFileUpload = class {
20637
20653
  constructor(hostRef) {
20638
20654
  index.registerInstance(this, hostRef);
20639
20655
  this.added = index.createEvent(this, "added", 7);
20656
+ this.invalidInput = index.createEvent(this, "invalidInput", 7);
20640
20657
  this.uploaded = index.createEvent(this, "uploaded", 7);
20641
20658
  this.removed = index.createEvent(this, "removed", 7);
20642
20659
  /**
@@ -20797,15 +20814,18 @@ const SallaFileUpload = class {
20797
20814
  return this.uploaded.emit(this.value);
20798
20815
  }
20799
20816
  removedHandler(deletedFile) {
20817
+ var _a;
20800
20818
  let fileInput = this.getFormDataFileInput();
20801
20819
  fileInput.type = 'hidden';
20802
20820
  fileInput.value = '';
20821
+ (_a = this.host.closest('.s-product-options-option')) === null || _a === void 0 ? void 0 : _a.removeAttribute('data-has-value');
20803
20822
  if (deletedFile.getMetadata('id')) {
20804
20823
  salla.cart.api.deleteImage(deletedFile.getMetadata('id'));
20805
20824
  }
20806
20825
  if (this.height) {
20807
20826
  setTimeout(() => this.host.querySelector('.filepond--root').style.height = this.height, 1000);
20808
20827
  }
20828
+ this.hiddenInput.value = '';
20809
20829
  fileInput.dispatchEvent(new window.Event('change', { bubbles: true }));
20810
20830
  return this.removed.emit(deletedFile);
20811
20831
  }
@@ -20838,6 +20858,7 @@ const SallaFileUpload = class {
20838
20858
  return this.host.querySelector('.filepond--data input');
20839
20859
  }
20840
20860
  getFiles() {
20861
+ var _a;
20841
20862
  if (!this.value && !this.files) {
20842
20863
  return [];
20843
20864
  }
@@ -20845,6 +20866,9 @@ const SallaFileUpload = class {
20845
20866
  let files = this.files
20846
20867
  ? JSON.parse(this.files)
20847
20868
  : this.value.split(',').map(file => ({ url: file }));
20869
+ if (files.length) {
20870
+ (_a = this.host.closest('.s-product-options-option')) === null || _a === void 0 ? void 0 : _a.setAttribute('data-has-value', 'true');
20871
+ }
20848
20872
  return files.map(file => ({
20849
20873
  source: file.id ? `${file.id}` : file.url,
20850
20874
  options: {
@@ -20872,7 +20896,7 @@ const SallaFileUpload = class {
20872
20896
  return (index.h(index.Host, { class: {
20873
20897
  "s-file-upload": true,
20874
20898
  "s-file-upload-profile-image": this.profileImage,
20875
- } }, index.h("input", { type: "file", name: this.name, value: this.value, ref: ele => this.fileUploader = ele, required: this.required, class: "s-file-upload-wrapper s-file-upload-input", accept: this.accept })));
20899
+ } }, index.h("input", { type: "file", name: this.name, value: this.value, ref: ele => this.fileUploader = ele, required: this.required, class: "s-file-upload-wrapper s-file-upload-input", accept: this.accept }), index.h("input", { class: "s-file-upload-hidden-input", name: 'hidden-' + this.name, required: this.required, value: this.value, ref: input => this.hiddenInput = input })));
20876
20900
  }
20877
20901
  componentDidLoad() {
20878
20902
  let files = this.getFiles();
@@ -20964,6 +20988,13 @@ const SallaFileUpload = class {
20964
20988
  resolve(true);
20965
20989
  }),
20966
20990
  });
20991
+ this.hiddenInput.addEventListener('invalid', e => {
20992
+ this.invalidInput.emit(e);
20993
+ });
20994
+ this.hiddenInput.addEventListener('change', () => {
20995
+ this.hiddenInput.setCustomValidity('');
20996
+ this.hiddenInput.reportValidity();
20997
+ });
20967
20998
  }
20968
20999
  get host() { return index.getElement(this); }
20969
21000
  };
@@ -22574,6 +22605,7 @@ const SallaMap = class {
22574
22605
  this.selected = index.createEvent(this, "selected", 7);
22575
22606
  this.mapClicked = index.createEvent(this, "mapClicked", 7);
22576
22607
  this.currentLocationChanged = index.createEvent(this, "currentLocationChanged", 7);
22608
+ this.invalidInput = index.createEvent(this, "invalidInput", 7);
22577
22609
  this.defaultLat = 21.419421; //Mecca 🕋
22578
22610
  this.defaultLng = 39.82553; //Mecca 🕋
22579
22611
  // state variables
@@ -22778,6 +22810,13 @@ const SallaMap = class {
22778
22810
  }
22779
22811
  });
22780
22812
  }
22813
+ this.mapInput.addEventListener('invalid', e => {
22814
+ this.invalidInput.emit(e);
22815
+ });
22816
+ this.mapInput.addEventListener('input', () => {
22817
+ this.mapInput.setCustomValidity('');
22818
+ this.mapInput.reportValidity();
22819
+ });
22781
22820
  }
22782
22821
  /**
22783
22822
  * Open location component
@@ -145,6 +145,12 @@ const SallaProductOptions = class {
145
145
  async getOption(option_id) {
146
146
  return this.optionsData.find(option => option.id === option_id);
147
147
  }
148
+ // @ts-ignore
149
+ invalidHandler(event, option) {
150
+ const closestProductOption = event.target.closest('.s-product-options-option');
151
+ closestProductOption.scrollIntoView({ behavior: 'smooth', block: 'center' });
152
+ closestProductOption.classList.add('s-product-options-option-error');
153
+ }
148
154
  changedHandler(event, option) {
149
155
  let data = { event: event, option: option, detail: null };
150
156
  if (option.details) {
@@ -153,6 +159,10 @@ const SallaProductOptions = class {
153
159
  });
154
160
  data.detail = detail;
155
161
  }
162
+ let optionElement = event.target.closest('.s-product-options-option');
163
+ if (event.target.value || event.type == 'added') {
164
+ optionElement.classList.remove('s-product-options-option-error');
165
+ }
156
166
  const index = this.selectedOptions.findIndex(option => option.option_id === data.option.id);
157
167
  index > -1 ? this.selectedOptions[index] = Object.assign(Object.assign({}, data.detail), { option_id: data.option.id }) : this.selectedOptions.push(Object.assign(Object.assign({}, data.detail), { option_id: data.option.id }));
158
168
  this.setSelectedSkus();
@@ -213,16 +223,16 @@ const SallaProductOptions = class {
213
223
  }
214
224
  return (index.h(index.Host, { class: "s-product-options-wrapper" }, index.h("salla-conditional-fields", null, this.optionsData.map((option) => index.h("div", Object.assign({ class: `s-product-options-option-container${option.visibility_condition ? ' hidden' : ''}`, "data-option-id": option.id }, this.getOptionShownWhen(option)), option.name == 'splitter' ?
215
225
  this.splitterOption()
216
- : index.h("div", { class: "s-product-options-option" }, index.h("label", { htmlFor: 'options[' + option.id + ']', class: "s-product-options-option-label" }, index.h("b", null, option.name, option.required && index.h("span", null, " * "), " "), index.h("small", null, option.placeholder)), index.h("div", { class: "s-product-options-option-content" }, this.getDisplayForType(option))))))));
226
+ : index.h("div", { class: "s-product-options-option", "data-option-type": option.type, "data-option-required": `${option.required}` }, index.h("label", { htmlFor: 'options[' + option.id + ']', class: "s-product-options-option-label" }, index.h("b", null, option.name, option.required && index.h("span", null, " * "), " "), index.h("small", null, option.placeholder)), index.h("div", { class: "s-product-options-option-content" }, this.getDisplayForType(option))))))));
217
227
  }
218
228
  //@ts-ignore
219
229
  donationOption(option, product) {
220
230
  return index.h("div", { class: "s-product-options-donation-wrapper" }, option.donation ?
221
231
  index.h("div", { class: "s-product-options-donation-progress" }, index.h("salla-progress-bar", { donation: option.donation }))
222
- : '', index.h("div", { class: "s-product-options-donation-input-group" }, index.h("input", { type: "text", id: "donating-amount", name: "donating_amount", class: "s-form-control", value: option.value, required: true, placeholder: option.placeholder, onInput: e => salla.helpers.inputDigitsOnly(e.target), onBlur: e => this.changedHandler(e, option) }), index.h("span", { class: "s-product-options-donation-amount-currency" }, salla.config.currency(salla.config.get('user.currency_code')).symbol)));
232
+ : '', index.h("div", { class: "s-product-options-donation-input-group" }, index.h("input", { type: "text", id: "donating-amount", name: "donating_amount", class: "s-form-control", value: option.value, required: true, placeholder: option.placeholder, onInput: e => salla.helpers.inputDigitsOnly(e.target), onBlur: e => this.changedHandler(e, option), onInvalid: (e) => this.invalidHandler(e, option) }), index.h("span", { class: "s-product-options-donation-amount-currency" }, salla.config.currency(salla.config.get('user.currency_code')).symbol)));
223
233
  }
224
234
  fileUploader(option, additions = null) {
225
- return index.h("salla-file-upload", Object.assign({}, (additions || {}), { "payload-name": "file", value: option.value, "instant-upload": true, name: `options[${option.id}]`, required: option.required, height: "120px", url: salla.cart.api.getUploadImageEndpoint(), "form-data": { cart_item_id: this.productId, product_id: this.productId }, class: { "s-product-options-image-input": true, required: option.required } }), index.h("div", { class: "s-product-options-filepond-placeholder" }, index.h("span", { class: "s-product-options-filepond-placeholder-icon", innerHTML: additions.accept && additions.accept.split(',').every(type => type.includes('image'))
235
+ return index.h("salla-file-upload", Object.assign({}, (additions || {}), { "payload-name": "file", value: option.value, "instant-upload": true, name: `options[${option.id}]`, required: 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 } }), index.h("div", { class: "s-product-options-filepond-placeholder" }, index.h("span", { class: "s-product-options-filepond-placeholder-icon", innerHTML: additions.accept && additions.accept.split(',').every(type => type.includes('image'))
226
236
  ? camera.CameraIcon
227
237
  : FileIcon }), index.h("p", { class: "s-product-options-filepond-placeholder-text" }, salla.lang.get('common.uploader.drag_and_drop')), index.h("span", { class: "filepond--label-action" }, salla.lang.get('common.uploader.browse'))));
228
238
  }
@@ -239,7 +249,7 @@ const SallaProductOptions = class {
239
249
  }
240
250
  //@ts-ignore
241
251
  numberOption(option) {
242
- return index.h("input", { type: "text", value: option.value, class: "s-form-control", required: option.required, name: `options[${option.id}]`, placeholder: option.placeholder, onBlur: e => this.changedHandler(e, option), onInput: e => salla.helpers.inputDigitsOnly(e.target) });
252
+ return index.h("input", { type: "text", value: option.value, class: "s-form-control", required: 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) });
243
253
  }
244
254
  //@ts-ignore
245
255
  splitterOption() {
@@ -247,41 +257,40 @@ const SallaProductOptions = class {
247
257
  }
248
258
  //@ts-ignore
249
259
  textOption(option) {
250
- return index.h("div", { class: "s-product-options-text" }, index.h("input", { type: "text", value: option.value, class: 's-form-control', required: option.required, name: `options[${option.id}]`, placeholder: option.placeholder, onInput: e => this.changedHandler(e, option) }));
260
+ return index.h("div", { class: "s-product-options-text" }, index.h("input", { type: "text", value: option.value, class: 's-form-control', required: option.required, name: `options[${option.id}]`, placeholder: option.placeholder, onInvalid: (e) => this.invalidHandler(e, option), onInput: e => this.changedHandler(e, option) }));
251
261
  }
252
262
  //@ts-ignore
253
263
  textareaOption(option) {
254
264
  //todo::remove mt-1 class, and if it's okay to remove the tag itself will be great
255
- return index.h("div", { class: "s-product-options-textarea" }, index.h("div", { class: "mt-1" }, index.h("textarea", { rows: 4, value: option.value, class: "s-form-control", required: option.required, id: `options[${option.id}]`, name: `options[${option.id}]`, placeholder: option.placeholder, onInput: (e) => this.changedHandler(e, option) })));
265
+ return index.h("div", { class: "s-product-options-textarea" }, index.h("div", { class: "mt-1" }, index.h("textarea", { rows: 4, value: option.value, class: "s-form-control", required: option.required, id: `options[${option.id}]`, name: `options[${option.id}]`, placeholder: option.placeholder, onInvalid: (e) => this.invalidHandler(e, option), onInput: (e) => this.changedHandler(e, option) })));
256
266
  }
257
267
  //@ts-ignore
258
268
  mapOption(option) {
259
- return index.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, onSelected: e => this.changedHandler(e, option) });
269
+ return index.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) });
260
270
  }
261
271
  colorPickerOption(option) {
262
- return index.h("salla-color-picker", { onSubmitted: e => this.changedHandler(e, option), name: `options[${option.id}]`, required: option.required, color: option.value || '#5dd5c4' });
272
+ return index.h("salla-color-picker", { onSubmitted: e => this.changedHandler(e, option), name: `options[${option.id}]`, required: option.required, onInvalidInput: (e) => this.invalidHandler(e, option), color: option.value || '#5dd5c4' });
263
273
  }
264
274
  /**
265
275
  * ============= Date Time options =============
266
276
  */
267
277
  //@ts-ignore
268
278
  timeOption(option) {
269
- return index.h("salla-datetime-picker", { noCalendar: true, enableTime: true, dateFormat: "h:i K", value: option.value, placeholder: option.name, required: option.required, name: `options[${option.id}]`, class: "s-product-options-time-element", onPicked: e => this.changedHandler(e, option) });
279
+ return index.h("salla-datetime-picker", { noCalendar: true, enableTime: true, dateFormat: "h:i K", value: option.value, placeholder: option.name, required: option.required, name: `options[${option.id}]`, class: "s-product-options-time-element", onInvalidInput: (e) => this.invalidHandler(e, option), onPicked: e => this.changedHandler(e, option) });
270
280
  }
271
281
  //@ts-ignore
272
282
  dateOption(option) {
273
283
  //todo:: consider date-range @see https://github.com/SallaApp/theme-raed/blob/master/src/assets/js/partials/product-options.js#L8-L23
274
- return index.h("div", { class: "s-product-options-date-element" }, index.h("salla-datetime-picker", { value: option.value, placeholder: option.name, required: option.required, minDate: new Date(), name: `options[${option.id}]`, onPicked: e => this.changedHandler(e, option) }));
284
+ return index.h("div", { class: "s-product-options-date-element" }, index.h("salla-datetime-picker", { value: option.value, placeholder: option.name, required: option.required, minDate: new Date(), name: `options[${option.id}]`, onInvalidInput: (e) => this.invalidHandler(e, option), onPicked: e => this.changedHandler(e, option) }));
275
285
  }
276
286
  //@ts-ignore
277
287
  datetimeOption(option) {
278
288
  //todo:: consider date-range @see https://github.com/SallaApp/theme-raed/blob/master/src/assets/js/partials/product-options.js#L8-L23
279
- return index.h("div", { class: "s-product-options-datetime-element" }, index.h("salla-datetime-picker", { enableTime: true, value: option.value, dateFormat: "Y-m-d G:i:K", placeholder: option.name, required: option.required, name: `options[${option.id}]`, maxDate: option.to_date_time, minDate: option.from_date_time, onPicked: e => this.changedHandler(e, option) }));
289
+ return index.h("div", { class: "s-product-options-datetime-element" }, index.h("salla-datetime-picker", { enableTime: true, value: option.value, dateFormat: "Y-m-d G:i:K", placeholder: option.name, required: 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) }));
280
290
  }
281
291
  /**
282
292
  * ============= Advanced options =============
283
293
  */
284
- // <<<<<<< HEAD
285
294
  getOptionDetailName(detail, outOfStock = true, optionType) {
286
295
  if (optionType && optionType == DisplayType.COLOR) {
287
296
  return detail.name
@@ -311,24 +320,24 @@ const SallaProductOptions = class {
311
320
  return this.selectedOptions.some(option => option.is_out && option.option_id !== detail.option_id);
312
321
  }
313
322
  singleOption(option) {
314
- return index.h("div", null, index.h("select", { name: `options[${option.id}]`, required: option.required, class: "s-form-control", onChange: e => this.changedHandler(e, option) }, index.h("option", { value: "" }, option.placeholder), option === null || option === void 0 ? void 0 :
323
+ return index.h("div", null, index.h("select", { name: `options[${option.id}]`, required: option.required, class: "s-form-control", onInvalid: (e) => this.invalidHandler(e, option), onChange: e => this.changedHandler(e, option) }, index.h("option", { value: "" }, option.placeholder), option === null || option === void 0 ? void 0 :
315
324
  option.details.map((detail) => {
316
325
  return index.h("option", { value: detail.id, disabled: this.canDisabled && this.isOptionDetailOut(detail), selected: detail.is_selected }, this.getOptionDetailName(detail));
317
326
  })));
318
327
  }
319
328
  multipleOptions(option) {
320
329
  return index.h("div", { class: { "s-product-options-multiple-options-wrapper": true, 'required': option.required } }, option === null || option === void 0 ? void 0 : option.details.map((detail) => {
321
- return index.h("div", null, index.h("input", { type: "checkbox", value: detail.id, disabled: this.isOptionDetailOut(detail), checked: detail.is_selected, required: option.required, name: `options[${option.id}][]`, id: `field-${option.id}-${detail.id}`, onChange: (e) => this.changedHandler(e, option), "aria-describedby": `options[${option.id}]-description` }), index.h("label", { htmlFor: `field-${option.id}-${detail.id}` }, this.getOptionDetailName(detail)));
330
+ return index.h("div", null, index.h("input", { type: "checkbox", value: detail.id, disabled: this.isOptionDetailOut(detail), checked: detail.is_selected, required: option.required, name: `options[${option.id}][]`, id: `field-${option.id}-${detail.id}`, onChange: (e) => this.changedHandler(e, option), onInvalid: (e) => this.invalidHandler(e, option), "aria-describedby": `options[${option.id}]-description` }), index.h("label", { htmlFor: `field-${option.id}-${detail.id}` }, this.getOptionDetailName(detail)));
322
331
  }));
323
332
  }
324
333
  //@ts-ignore
325
334
  colorOption(option) {
326
- return index.h("fieldset", { class: "s-product-options-colors-wrapper" }, option === null || option === void 0 ? void 0 : option.details.map((detail) => index.h("div", { class: "s-product-options-colors-item" }, index.h("input", { type: "radio", value: detail.id, required: option.required, checked: detail.is_selected, name: `options[${option.id}]`, disabled: this.canDisabled && this.isOptionDetailOut(detail), id: `color-${this.productId}-${option.id}-${detail.id}`, onChange: e => this.changedHandler(e, option) }), index.h("label", { htmlFor: `color-${this.productId}-${option.id}-${detail.id}` }, index.h("span", { style: { "background-color": detail.color } }), index.h("div", { innerHTML: this.getOptionDetailName(detail, true, option.type) })))));
335
+ return index.h("fieldset", { class: "s-product-options-colors-wrapper" }, option === null || option === void 0 ? void 0 : option.details.map((detail) => index.h("div", { class: "s-product-options-colors-item" }, index.h("input", { type: "radio", value: detail.id, required: option.required, checked: detail.is_selected, name: `options[${option.id}]`, disabled: this.canDisabled && this.isOptionDetailOut(detail), id: `color-${this.productId}-${option.id}-${detail.id}`, onInvalid: (e) => this.invalidHandler(e, option), onChange: e => this.changedHandler(e, option) }), index.h("label", { htmlFor: `color-${this.productId}-${option.id}-${detail.id}` }, index.h("span", { style: { "background-color": detail.color } }), index.h("div", { innerHTML: this.getOptionDetailName(detail, true, option.type) })))));
327
336
  }
328
337
  //@ts-ignore
329
338
  thumbnailOption(option) {
330
339
  return index.h("div", { class: "s-product-options-thumbnails-wrapper" }, option.details.map((detail) => {
331
- return index.h("div", null, index.h("input", { type: "radio", value: detail.id, "data-itemid": detail.id, required: option.required, checked: detail.is_selected, name: `options[${option.id}]`, "data-img-id": detail.option_value, disabled: this.canDisabled && this.isOptionDetailOut(detail), id: `option_${this.productId}-${option.id}_${detail.id}`, onChange: (e) => this.changedHandler(e, option) }), index.h("label", { htmlFor: `option_${this.productId}-${option.id}_${detail.id}`, "data-img-id": detail.option_value, class: "go-to-slide" }, index.h("img", { "data-src": detail.image, src: detail.image, title: detail.name, alt: detail.name }), index.h("span", { innerHTML: CheckCircleIcon, class: "s-product-options-thumbnails-icon" }), this.isOptionDetailOut(detail) ?
340
+ return index.h("div", null, index.h("input", { type: "radio", value: detail.id, "data-itemid": detail.id, required: option.required, checked: detail.is_selected, name: `options[${option.id}]`, "data-img-id": detail.option_value, disabled: this.canDisabled && this.isOptionDetailOut(detail), id: `option_${this.productId}-${option.id}_${detail.id}`, onInvalid: (e) => this.invalidHandler(e, option), onChange: (e) => this.changedHandler(e, option) }), index.h("label", { htmlFor: `option_${this.productId}-${option.id}_${detail.id}`, "data-img-id": detail.option_value, class: "go-to-slide" }, index.h("img", { "data-src": detail.image, src: detail.image, title: detail.name, alt: detail.name }), index.h("span", { innerHTML: CheckCircleIcon, class: "s-product-options-thumbnails-icon" }), this.isOptionDetailOut(detail) ?
332
341
  [
333
342
  index.h("small", { class: "s-product-options-thumbnails-stock-badge" }, this.outOfStockText),
334
343
  this.canDisabled ? index.h("div", { class: "s-product-options-thumbnails-badge-overlay" }) : '',
@@ -109,6 +109,13 @@ export class SallaColorPicker {
109
109
  componentDidLoad() {
110
110
  this.canvas.style.backgroundColor = this.color;
111
111
  this.initColorPicker();
112
+ this.colorInput.addEventListener('invalid', e => {
113
+ this.invalidInput.emit(e);
114
+ });
115
+ this.colorInput.addEventListener('input', () => {
116
+ this.colorInput.setCustomValidity('');
117
+ this.colorInput.reportValidity();
118
+ });
112
119
  }
113
120
  setPopIpPosition() {
114
121
  const popup = this.host.querySelector('.picker_wrapper');
@@ -311,6 +318,21 @@ export class SallaColorPicker {
311
318
  }
312
319
  }
313
320
  }
321
+ }, {
322
+ "method": "invalidInput",
323
+ "name": "invalidInput",
324
+ "bubbles": true,
325
+ "cancelable": true,
326
+ "composed": true,
327
+ "docs": {
328
+ "tags": [],
329
+ "text": "Event emitted when the input is invalid."
330
+ },
331
+ "complexType": {
332
+ "original": "any",
333
+ "resolved": "any",
334
+ "references": {}
335
+ }
314
336
  }, {
315
337
  "method": "submitted",
316
338
  "name": "submitted",
@@ -230,6 +230,13 @@ export class SallaDatetimePicker {
230
230
  // onClose: this.handleOnClose(selectedDates, dateStr, instance)
231
231
  };
232
232
  flatpickr(this.dateInput, options);
233
+ this.dateInput.addEventListener('invalid', e => {
234
+ this.invalidInput.emit(e);
235
+ });
236
+ this.dateInput.addEventListener('input', () => {
237
+ this.dateInput.setCustomValidity('');
238
+ this.dateInput.reportValidity();
239
+ });
233
240
  }
234
241
  static get is() { return "salla-datetime-picker"; }
235
242
  static get originalStyleUrls() { return {
@@ -1163,7 +1170,22 @@ export class SallaDatetimePicker {
1163
1170
  "composed": true,
1164
1171
  "docs": {
1165
1172
  "tags": [],
1166
- "text": "Event emitted when the file input gets changed by the user when selecting file(s)."
1173
+ "text": "Event emitted when the date input gets changed by the user when selecting file(s)."
1174
+ },
1175
+ "complexType": {
1176
+ "original": "any",
1177
+ "resolved": "any",
1178
+ "references": {}
1179
+ }
1180
+ }, {
1181
+ "method": "invalidInput",
1182
+ "name": "invalidInput",
1183
+ "bubbles": true,
1184
+ "cancelable": true,
1185
+ "composed": true,
1186
+ "docs": {
1187
+ "tags": [],
1188
+ "text": "Event emitted when the input is invalid."
1167
1189
  },
1168
1190
  "complexType": {
1169
1191
  "original": "any",
@@ -169,15 +169,18 @@ export class SallaFileUpload {
169
169
  return this.uploaded.emit(this.value);
170
170
  }
171
171
  removedHandler(deletedFile) {
172
+ var _a;
172
173
  let fileInput = this.getFormDataFileInput();
173
174
  fileInput.type = 'hidden';
174
175
  fileInput.value = '';
176
+ (_a = this.host.closest('.s-product-options-option')) === null || _a === void 0 ? void 0 : _a.removeAttribute('data-has-value');
175
177
  if (deletedFile.getMetadata('id')) {
176
178
  salla.cart.api.deleteImage(deletedFile.getMetadata('id'));
177
179
  }
178
180
  if (this.height) {
179
181
  setTimeout(() => this.host.querySelector('.filepond--root').style.height = this.height, 1000);
180
182
  }
183
+ this.hiddenInput.value = '';
181
184
  fileInput.dispatchEvent(new window.Event('change', { bubbles: true }));
182
185
  return this.removed.emit(deletedFile);
183
186
  }
@@ -210,6 +213,7 @@ export class SallaFileUpload {
210
213
  return this.host.querySelector('.filepond--data input');
211
214
  }
212
215
  getFiles() {
216
+ var _a;
213
217
  if (!this.value && !this.files) {
214
218
  return [];
215
219
  }
@@ -217,6 +221,9 @@ export class SallaFileUpload {
217
221
  let files = this.files
218
222
  ? JSON.parse(this.files)
219
223
  : this.value.split(',').map(file => ({ url: file }));
224
+ if (files.length) {
225
+ (_a = this.host.closest('.s-product-options-option')) === null || _a === void 0 ? void 0 : _a.setAttribute('data-has-value', 'true');
226
+ }
220
227
  return files.map(file => ({
221
228
  source: file.id ? `${file.id}` : file.url,
222
229
  options: {
@@ -245,7 +252,8 @@ export class SallaFileUpload {
245
252
  "s-file-upload": true,
246
253
  "s-file-upload-profile-image": this.profileImage,
247
254
  } },
248
- h("input", { type: "file", name: this.name, value: this.value, ref: ele => this.fileUploader = ele, required: this.required, class: "s-file-upload-wrapper s-file-upload-input", accept: this.accept })));
255
+ h("input", { type: "file", name: this.name, value: this.value, ref: ele => this.fileUploader = ele, required: this.required, class: "s-file-upload-wrapper s-file-upload-input", accept: this.accept }),
256
+ h("input", { class: "s-file-upload-hidden-input", name: 'hidden-' + this.name, required: this.required, value: this.value, ref: input => this.hiddenInput = input })));
249
257
  }
250
258
  componentDidLoad() {
251
259
  let files = this.getFiles();
@@ -337,6 +345,13 @@ export class SallaFileUpload {
337
345
  resolve(true);
338
346
  }),
339
347
  });
348
+ this.hiddenInput.addEventListener('invalid', e => {
349
+ this.invalidInput.emit(e);
350
+ });
351
+ this.hiddenInput.addEventListener('change', () => {
352
+ this.hiddenInput.setCustomValidity('');
353
+ this.hiddenInput.reportValidity();
354
+ });
340
355
  }
341
356
  static get is() { return "salla-file-upload"; }
342
357
  static get originalStyleUrls() { return {
@@ -1223,6 +1238,21 @@ export class SallaFileUpload {
1223
1238
  }
1224
1239
  }
1225
1240
  }
1241
+ }, {
1242
+ "method": "invalidInput",
1243
+ "name": "invalidInput",
1244
+ "bubbles": true,
1245
+ "cancelable": true,
1246
+ "composed": true,
1247
+ "docs": {
1248
+ "tags": [],
1249
+ "text": "Event emitted when the input is invalid"
1250
+ },
1251
+ "complexType": {
1252
+ "original": "any",
1253
+ "resolved": "any",
1254
+ "references": {}
1255
+ }
1226
1256
  }, {
1227
1257
  "method": "uploaded",
1228
1258
  "name": "uploaded",
@@ -215,6 +215,13 @@ export class SallaMap {
215
215
  }
216
216
  });
217
217
  }
218
+ this.mapInput.addEventListener('invalid', e => {
219
+ this.invalidInput.emit(e);
220
+ });
221
+ this.mapInput.addEventListener('input', () => {
222
+ this.mapInput.setCustomValidity('');
223
+ this.mapInput.reportValidity();
224
+ });
218
225
  }
219
226
  /**
220
227
  * Open location component
@@ -524,6 +531,21 @@ export class SallaMap {
524
531
  "resolved": "any",
525
532
  "references": {}
526
533
  }
534
+ }, {
535
+ "method": "invalidInput",
536
+ "name": "invalidInput",
537
+ "bubbles": true,
538
+ "cancelable": true,
539
+ "composed": true,
540
+ "docs": {
541
+ "tags": [],
542
+ "text": "Event emitted when the input is invalid."
543
+ },
544
+ "complexType": {
545
+ "original": "any",
546
+ "resolved": "any",
547
+ "references": {}
548
+ }
527
549
  }]; }
528
550
  static get methods() { return {
529
551
  "open": {
@@ -99,6 +99,12 @@ export class SallaProductOptions {
99
99
  async getOption(option_id) {
100
100
  return this.optionsData.find(option => option.id === option_id);
101
101
  }
102
+ // @ts-ignore
103
+ invalidHandler(event, option) {
104
+ const closestProductOption = event.target.closest('.s-product-options-option');
105
+ closestProductOption.scrollIntoView({ behavior: 'smooth', block: 'center' });
106
+ closestProductOption.classList.add('s-product-options-option-error');
107
+ }
102
108
  changedHandler(event, option) {
103
109
  let data = { event: event, option: option, detail: null };
104
110
  if (option.details) {
@@ -107,6 +113,10 @@ export class SallaProductOptions {
107
113
  });
108
114
  data.detail = detail;
109
115
  }
116
+ let optionElement = event.target.closest('.s-product-options-option');
117
+ if (event.target.value || event.type == 'added') {
118
+ optionElement.classList.remove('s-product-options-option-error');
119
+ }
110
120
  const index = this.selectedOptions.findIndex(option => option.option_id === data.option.id);
111
121
  index > -1 ? this.selectedOptions[index] = Object.assign(Object.assign({}, data.detail), { option_id: data.option.id }) : this.selectedOptions.push(Object.assign(Object.assign({}, data.detail), { option_id: data.option.id }));
112
122
  this.setSelectedSkus();
@@ -168,7 +178,7 @@ export class SallaProductOptions {
168
178
  return (h(Host, { class: "s-product-options-wrapper" },
169
179
  h("salla-conditional-fields", null, this.optionsData.map((option) => h("div", Object.assign({ class: `s-product-options-option-container${option.visibility_condition ? ' hidden' : ''}`, "data-option-id": option.id }, this.getOptionShownWhen(option)), option.name == 'splitter' ?
170
180
  this.splitterOption()
171
- : h("div", { class: "s-product-options-option" },
181
+ : h("div", { class: "s-product-options-option", "data-option-type": option.type, "data-option-required": `${option.required}` },
172
182
  h("label", { htmlFor: 'options[' + option.id + ']', class: "s-product-options-option-label" },
173
183
  h("b", null,
174
184
  option.name,
@@ -185,11 +195,11 @@ export class SallaProductOptions {
185
195
  h("salla-progress-bar", { donation: option.donation }))
186
196
  : '',
187
197
  h("div", { class: "s-product-options-donation-input-group" },
188
- h("input", { type: "text", id: "donating-amount", name: "donating_amount", class: "s-form-control", value: option.value, required: true, placeholder: option.placeholder, onInput: e => salla.helpers.inputDigitsOnly(e.target), onBlur: e => this.changedHandler(e, option) }),
198
+ h("input", { type: "text", id: "donating-amount", name: "donating_amount", class: "s-form-control", value: option.value, required: true, placeholder: option.placeholder, onInput: e => salla.helpers.inputDigitsOnly(e.target), onBlur: e => this.changedHandler(e, option), onInvalid: (e) => this.invalidHandler(e, option) }),
189
199
  h("span", { class: "s-product-options-donation-amount-currency" }, salla.config.currency(salla.config.get('user.currency_code')).symbol)));
190
200
  }
191
201
  fileUploader(option, additions = null) {
192
- return h("salla-file-upload", Object.assign({}, (additions || {}), { "payload-name": "file", value: option.value, "instant-upload": true, name: `options[${option.id}]`, required: option.required, height: "120px", url: salla.cart.api.getUploadImageEndpoint(), "form-data": { cart_item_id: this.productId, product_id: this.productId }, class: { "s-product-options-image-input": true, required: option.required } }),
202
+ return h("salla-file-upload", Object.assign({}, (additions || {}), { "payload-name": "file", value: option.value, "instant-upload": true, name: `options[${option.id}]`, required: 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 } }),
193
203
  h("div", { class: "s-product-options-filepond-placeholder" },
194
204
  h("span", { class: "s-product-options-filepond-placeholder-icon", innerHTML: additions.accept && additions.accept.split(',').every(type => type.includes('image'))
195
205
  ? CameraIcon
@@ -210,7 +220,7 @@ export class SallaProductOptions {
210
220
  }
211
221
  //@ts-ignore
212
222
  numberOption(option) {
213
- return h("input", { type: "text", value: option.value, class: "s-form-control", required: option.required, name: `options[${option.id}]`, placeholder: option.placeholder, onBlur: e => this.changedHandler(e, option), onInput: e => salla.helpers.inputDigitsOnly(e.target) });
223
+ return h("input", { type: "text", value: option.value, class: "s-form-control", required: 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) });
214
224
  }
215
225
  //@ts-ignore
216
226
  splitterOption() {
@@ -219,45 +229,44 @@ export class SallaProductOptions {
219
229
  //@ts-ignore
220
230
  textOption(option) {
221
231
  return h("div", { class: "s-product-options-text" },
222
- h("input", { type: "text", value: option.value, class: 's-form-control', required: option.required, name: `options[${option.id}]`, placeholder: option.placeholder, onInput: e => this.changedHandler(e, option) }));
232
+ h("input", { type: "text", value: option.value, class: 's-form-control', required: option.required, name: `options[${option.id}]`, placeholder: option.placeholder, onInvalid: (e) => this.invalidHandler(e, option), onInput: e => this.changedHandler(e, option) }));
223
233
  }
224
234
  //@ts-ignore
225
235
  textareaOption(option) {
226
236
  //todo::remove mt-1 class, and if it's okay to remove the tag itself will be great
227
237
  return h("div", { class: "s-product-options-textarea" },
228
238
  h("div", { class: "mt-1" },
229
- h("textarea", { rows: 4, value: option.value, class: "s-form-control", required: option.required, id: `options[${option.id}]`, name: `options[${option.id}]`, placeholder: option.placeholder, onInput: (e) => this.changedHandler(e, option) })));
239
+ h("textarea", { rows: 4, value: option.value, class: "s-form-control", required: option.required, id: `options[${option.id}]`, name: `options[${option.id}]`, placeholder: option.placeholder, onInvalid: (e) => this.invalidHandler(e, option), onInput: (e) => this.changedHandler(e, option) })));
230
240
  }
231
241
  //@ts-ignore
232
242
  mapOption(option) {
233
- 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, onSelected: e => this.changedHandler(e, option) });
243
+ 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) });
234
244
  }
235
245
  colorPickerOption(option) {
236
- return h("salla-color-picker", { onSubmitted: e => this.changedHandler(e, option), name: `options[${option.id}]`, required: option.required, color: option.value || '#5dd5c4' });
246
+ return h("salla-color-picker", { onSubmitted: e => this.changedHandler(e, option), name: `options[${option.id}]`, required: option.required, onInvalidInput: (e) => this.invalidHandler(e, option), color: option.value || '#5dd5c4' });
237
247
  }
238
248
  /**
239
249
  * ============= Date Time options =============
240
250
  */
241
251
  //@ts-ignore
242
252
  timeOption(option) {
243
- return h("salla-datetime-picker", { noCalendar: true, enableTime: true, dateFormat: "h:i K", value: option.value, placeholder: option.name, required: option.required, name: `options[${option.id}]`, class: "s-product-options-time-element", onPicked: e => this.changedHandler(e, option) });
253
+ return h("salla-datetime-picker", { noCalendar: true, enableTime: true, dateFormat: "h:i K", value: option.value, placeholder: option.name, required: option.required, name: `options[${option.id}]`, class: "s-product-options-time-element", onInvalidInput: (e) => this.invalidHandler(e, option), onPicked: e => this.changedHandler(e, option) });
244
254
  }
245
255
  //@ts-ignore
246
256
  dateOption(option) {
247
257
  //todo:: consider date-range @see https://github.com/SallaApp/theme-raed/blob/master/src/assets/js/partials/product-options.js#L8-L23
248
258
  return h("div", { class: "s-product-options-date-element" },
249
- h("salla-datetime-picker", { value: option.value, placeholder: option.name, required: option.required, minDate: new Date(), name: `options[${option.id}]`, onPicked: e => this.changedHandler(e, option) }));
259
+ h("salla-datetime-picker", { value: option.value, placeholder: option.name, required: option.required, minDate: new Date(), name: `options[${option.id}]`, onInvalidInput: (e) => this.invalidHandler(e, option), onPicked: e => this.changedHandler(e, option) }));
250
260
  }
251
261
  //@ts-ignore
252
262
  datetimeOption(option) {
253
263
  //todo:: consider date-range @see https://github.com/SallaApp/theme-raed/blob/master/src/assets/js/partials/product-options.js#L8-L23
254
264
  return h("div", { class: "s-product-options-datetime-element" },
255
- h("salla-datetime-picker", { enableTime: true, value: option.value, dateFormat: "Y-m-d G:i:K", placeholder: option.name, required: option.required, name: `options[${option.id}]`, maxDate: option.to_date_time, minDate: option.from_date_time, onPicked: e => this.changedHandler(e, option) }));
265
+ h("salla-datetime-picker", { enableTime: true, value: option.value, dateFormat: "Y-m-d G:i:K", placeholder: option.name, required: 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) }));
256
266
  }
257
267
  /**
258
268
  * ============= Advanced options =============
259
269
  */
260
- // <<<<<<< HEAD
261
270
  getOptionDetailName(detail, outOfStock = true, optionType) {
262
271
  if (optionType && optionType == DisplayType.COLOR) {
263
272
  return detail.name
@@ -288,7 +297,7 @@ export class SallaProductOptions {
288
297
  }
289
298
  singleOption(option) {
290
299
  return h("div", null,
291
- h("select", { name: `options[${option.id}]`, required: option.required, class: "s-form-control", onChange: e => this.changedHandler(e, option) },
300
+ h("select", { name: `options[${option.id}]`, required: option.required, class: "s-form-control", onInvalid: (e) => this.invalidHandler(e, option), onChange: e => this.changedHandler(e, option) },
292
301
  h("option", { value: "" }, option.placeholder), option === null || option === void 0 ? void 0 :
293
302
  option.details.map((detail) => {
294
303
  return h("option", { value: detail.id, disabled: this.canDisabled && this.isOptionDetailOut(detail), selected: detail.is_selected }, this.getOptionDetailName(detail));
@@ -297,14 +306,14 @@ export class SallaProductOptions {
297
306
  multipleOptions(option) {
298
307
  return h("div", { class: { "s-product-options-multiple-options-wrapper": true, 'required': option.required } }, option === null || option === void 0 ? void 0 : option.details.map((detail) => {
299
308
  return h("div", null,
300
- h("input", { type: "checkbox", value: detail.id, disabled: this.isOptionDetailOut(detail), checked: detail.is_selected, required: option.required, name: `options[${option.id}][]`, id: `field-${option.id}-${detail.id}`, onChange: (e) => this.changedHandler(e, option), "aria-describedby": `options[${option.id}]-description` }),
309
+ h("input", { type: "checkbox", value: detail.id, disabled: this.isOptionDetailOut(detail), checked: detail.is_selected, required: option.required, name: `options[${option.id}][]`, id: `field-${option.id}-${detail.id}`, onChange: (e) => this.changedHandler(e, option), onInvalid: (e) => this.invalidHandler(e, option), "aria-describedby": `options[${option.id}]-description` }),
301
310
  h("label", { htmlFor: `field-${option.id}-${detail.id}` }, this.getOptionDetailName(detail)));
302
311
  }));
303
312
  }
304
313
  //@ts-ignore
305
314
  colorOption(option) {
306
315
  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" },
307
- h("input", { type: "radio", value: detail.id, required: option.required, checked: detail.is_selected, name: `options[${option.id}]`, disabled: this.canDisabled && this.isOptionDetailOut(detail), id: `color-${this.productId}-${option.id}-${detail.id}`, onChange: e => this.changedHandler(e, option) }),
316
+ h("input", { type: "radio", value: detail.id, required: option.required, checked: detail.is_selected, name: `options[${option.id}]`, disabled: this.canDisabled && this.isOptionDetailOut(detail), id: `color-${this.productId}-${option.id}-${detail.id}`, onInvalid: (e) => this.invalidHandler(e, option), onChange: e => this.changedHandler(e, option) }),
308
317
  h("label", { htmlFor: `color-${this.productId}-${option.id}-${detail.id}` },
309
318
  h("span", { style: { "background-color": detail.color } }),
310
319
  h("div", { innerHTML: this.getOptionDetailName(detail, true, option.type) })))));
@@ -313,7 +322,7 @@ export class SallaProductOptions {
313
322
  thumbnailOption(option) {
314
323
  return h("div", { class: "s-product-options-thumbnails-wrapper" }, option.details.map((detail) => {
315
324
  return h("div", null,
316
- h("input", { type: "radio", value: detail.id, "data-itemid": detail.id, required: option.required, checked: detail.is_selected, name: `options[${option.id}]`, "data-img-id": detail.option_value, disabled: this.canDisabled && this.isOptionDetailOut(detail), id: `option_${this.productId}-${option.id}_${detail.id}`, onChange: (e) => this.changedHandler(e, option) }),
325
+ h("input", { type: "radio", value: detail.id, "data-itemid": detail.id, required: option.required, checked: detail.is_selected, name: `options[${option.id}]`, "data-img-id": detail.option_value, disabled: this.canDisabled && this.isOptionDetailOut(detail), id: `option_${this.productId}-${option.id}_${detail.id}`, onInvalid: (e) => this.invalidHandler(e, option), onChange: (e) => this.changedHandler(e, option) }),
317
326
  h("label", { htmlFor: `option_${this.productId}-${option.id}_${detail.id}`, "data-img-id": detail.option_value, class: "go-to-slide" },
318
327
  h("img", { "data-src": detail.image, src: detail.image, title: detail.name, alt: detail.name }),
319
328
  h("span", { innerHTML: CheckCircleIcon, class: "s-product-options-thumbnails-icon" }),