@salla.sa/twilight-components 2.14.341 → 2.14.343

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.
@@ -7822,32 +7822,41 @@ const SallaProductOptions = class {
7822
7822
  * The id of the product to which the options are going to be fetched for.
7823
7823
  */
7824
7824
  this.productId = salla.config.get('page.id');
7825
- this.handleDonationOptions = (event, detail, type) => {
7825
+ this.handleDonationOptions = (event, detail, type, _option) => {
7826
+ // Donating-amount input only: onInput updates price and dispatches native change for form price watcher.
7826
7827
  if (detail === 'custom' && type === 'input') {
7827
- salla.helpers.inputDigitsOnly(event.target);
7828
+ const inputTarget = event.target;
7829
+ salla.helpers.inputDigitsOnly(inputTarget);
7828
7830
  salla.event.emit('product-options::donation-changed', {
7829
7831
  id: this.productId,
7830
- price: event.target.value
7832
+ price: inputTarget.value
7831
7833
  });
7834
+ inputTarget.dispatchEvent(new window.Event('change', { bubbles: true }));
7832
7835
  return;
7833
7836
  }
7834
- event.preventDefault();
7835
- event.stopPropagation();
7836
- this.isCustomDonation = event.target.value === 'custom';
7837
+ // Tab (radio) change: get value from the radio that fired.
7838
+ const value = type === 'option' ? String(event.target?.value ?? '') : '';
7839
+ if (type === 'option' && detail === 'custom') {
7840
+ // "Custom" tab: stop propagation so form does not see change until user types in donating-amount input.
7841
+ event.preventDefault();
7842
+ event.stopPropagation();
7843
+ event.stopImmediatePropagation();
7844
+ }
7845
+ this.isCustomDonation = value === 'custom';
7837
7846
  if (this.donationInput) {
7838
- if (event.target.value === 'custom') {
7847
+ if (value === 'custom') {
7839
7848
  this.donationInput.value = '';
7840
7849
  this.donationInput.focus();
7841
7850
  }
7842
7851
  else {
7843
- this.donationInput.value = event.target.value;
7852
+ this.donationInput.value = value;
7844
7853
  }
7845
7854
  if (detail === 'custom') {
7846
7855
  return;
7847
7856
  }
7848
7857
  salla.event.emit('product-options::donation-changed', {
7849
7858
  id: this.productId,
7850
- price: event.target.value
7859
+ price: value
7851
7860
  });
7852
7861
  }
7853
7862
  };
@@ -8319,15 +8328,15 @@ const SallaProductOptions = class {
8319
8328
  index.h("div", { key: option.id, class: "s-product-options-donation-progress" }, index.h("salla-progress-bar", { donation: option.donation }))
8320
8329
  : '',
8321
8330
  option.details.length ?
8322
- [index.h("h4", { key: option.id }, this.selectAmount), index.h("div", { key: option.id, class: "s-product-options-donation-options" }, option.details.map((detail, i) => index.h("div", { key: option.id, class: "s-product-options-donation-options-item" }, index.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') }), index.h("label", { htmlFor: this.generateUniqueKey(`donation-option-${i}`) }, index.h("span", { innerHTML: salla.money(detail.name) })))), option.donation?.custom_amount_enabled ?
8323
- index.h("div", { class: "s-product-options-donation-options-item" }, index.h("input", { id: this.generateUniqueKey("donation-option-custom"), type: "radio", name: "donating_option", value: "custom", onChange: e => this.handleDonationOptions(e, 'custom', 'option') }), index.h("label", { htmlFor: this.generateUniqueKey("donation-option-custom") }, index.h("span", null, " ", this.selectDonationAmount, " ")))
8331
+ [index.h("h4", { key: option.id }, this.selectAmount), index.h("div", { key: option.id, class: "s-product-options-donation-options" }, option.details.map((detail, i) => index.h("div", { key: option.id, class: "s-product-options-donation-options-item" }, index.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', option) }), index.h("label", { htmlFor: this.generateUniqueKey(`donation-option-${i}`) }, index.h("span", { innerHTML: salla.money(detail.name) })))), option.donation?.custom_amount_enabled ?
8332
+ index.h("div", { class: "s-product-options-donation-options-item" }, index.h("input", { id: this.generateUniqueKey("donation-option-custom"), type: "radio", name: "donating_option", value: "custom", onChange: e => this.handleDonationOptions(e, 'custom', 'option', option) }), index.h("label", { htmlFor: this.generateUniqueKey("donation-option-custom") }, index.h("span", null, " ", this.selectDonationAmount, " ")))
8324
8333
  : '')] : '',
8325
8334
  index.h("div", { key: option.id, class: { "s-product-options-donation-input-group": true, "shown": !option.details.length || (option.details.length && this.isCustomDonation) } }, index.h("input", { type: "text", id: "donating-amount", name: "donation_amount", class: "s-form-control", ref: el => { this.donationInput = el; }, value: option.details.length
8326
8335
  && option.details.some(detail => detail.is_selected)
8327
8336
  ? option.details.find(detail => detail.is_selected).additional_price
8328
8337
  : option.value,
8329
8338
  // required
8330
- placeholder: option.placeholder, onInput: e => this.handleDonationOptions(e, 'custom', 'input'), 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))
8339
+ placeholder: option.placeholder, onInput: e => this.handleDonationOptions(e, 'custom', 'input', option), 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))
8331
8340
  ] :
8332
8341
  this.getExpireDonationMessage(option));
8333
8342
  }
@@ -8800,6 +8809,19 @@ const SallaProductsList = class {
8800
8809
  //as a request they don't want to let the user to open the product details
8801
8810
  //todo:: find a better way to handle this request
8802
8811
  this.getSource() === 'landing-page' && (product.url = '');
8812
+ try {
8813
+ // Add source query param to product URL for tracking in Product Viewed event
8814
+ if (this.source && product.url && !product.url.includes('s-product-source=')) {
8815
+ const [urlWithoutFragment, fragment] = product.url.split('#');
8816
+ const separator = urlWithoutFragment.includes('?') ? '&' : '?';
8817
+ const pageSlug = salla.config?.get('page.slug') || 'unknown';
8818
+ const sourceValue = `${pageSlug}.${this.source}`;
8819
+ product.url = `${urlWithoutFragment}${separator}s-product-source=${sourceValue}${fragment ? '#' + fragment : ''}`;
8820
+ }
8821
+ }
8822
+ catch (error) {
8823
+ Salla.log('Error with source');
8824
+ }
8803
8825
  const customComponentTag = this.hasCustomComponent ? this.productCardComponent : 'salla-product-card';
8804
8826
  const productCard = document.createElement(customComponentTag);
8805
8827
  productCard.product = product;
@@ -9550,7 +9572,7 @@ const SallaQuantityInput = class {
9550
9572
  }
9551
9573
  async componentWillLoad() {
9552
9574
  await salla.onReady();
9553
- this.disableInput = salla.config.get('store.settings.product.manual_quantity');
9575
+ this.disableInput = !salla.config.get('store.settings.product.manual_quantity');
9554
9576
  this.quantity = parseInt(this.host.getAttribute('value')) || 1;
9555
9577
  this.hasIncrementSlot = !!this.host.querySelector('[slot="increment-button"]');
9556
9578
  this.hasDecrementSlot = !!this.host.querySelector('[slot="decrement-button"]');
@@ -9602,7 +9624,7 @@ const SallaQuantityInput = class {
9602
9624
  if (!inputAttributes['aria-label'] && !inputAttributes['aria-labelledby']) {
9603
9625
  inputAttributes['aria-label'] = salla.lang.getWithDefault('common.elements.quantity', 'Quantity');
9604
9626
  }
9605
- return (index.h(index.Host, { key: '4dfc6c39c86047493c7b37057c877657fc6206ab', class: "s-quantity-input" }, index.h("div", { key: '246b30299c5b318d9412d7b065e0a219a77ba342', class: "s-quantity-input-container" }, index.h("button", { key: '1e8f61c6014f36e257fbc7958547228481393624', onClick: () => this.increase(), class: "s-quantity-input-increase-button s-quantity-input-button", type: "button", "aria-label": salla.lang.getWithDefault('common.elements.increase_quantity', 'Increase quantity') }, !this.hasIncrementSlot ? index.h("span", { innerHTML: Add }) : '', index.h("slot", { key: '1da4aa703d8f135437e49b35c8bd664a39c207cc', name: "increment-button" })), index.h("input", { key: 'd781816bc3fa9b2fc5d004c5284a583f0dc4f75b', class: "s-quantity-input-input", ...inputAttributes, ref: (el) => this.textInput = el, onInput: (event) => this.setValue(event.target.value), min: "1", readOnly: this.disableInput, value: this.quantity }), index.h("button", { key: 'a52ed57453eeae844e435de4f1d0e9ae66768892', class: "s-quantity-input-decrease-button s-quantity-input-button", onClick: () => this.decrease(), type: "button", "aria-label": salla.lang.getWithDefault('common.elements.decrease_quantity', 'Decrease quantity') }, !this.hasDecrementSlot ? index.h("span", { innerHTML: Minus }) : '', index.h("slot", { key: '9c2b8ef10df5dc976120249dbc5fb6d3e3b68d31', name: "decrement-button" })))));
9627
+ return (index.h(index.Host, { key: 'ba636a941071cccc06a8fbcadf7a3ff93d88f4d0', class: "s-quantity-input" }, index.h("div", { key: '5fbe227ca172661fb8edc32940f586e7e26c8d46', class: "s-quantity-input-container" }, index.h("button", { key: 'dbdb3cbac199c2fb73ef49dcdb513ebd38f5f3b5', onClick: () => this.increase(), class: "s-quantity-input-increase-button s-quantity-input-button", type: "button", "aria-label": salla.lang.getWithDefault('common.elements.increase_quantity', 'Increase quantity') }, !this.hasIncrementSlot ? index.h("span", { innerHTML: Add }) : '', index.h("slot", { key: 'f117c437e73134ab4fe949c61190e38e4db81e44', name: "increment-button" })), index.h("input", { key: 'c41fd5bbb8760171462d3a5cc54ebe9ae54747fc', class: "s-quantity-input-input", ...inputAttributes, ref: (el) => this.textInput = el, onInput: (event) => this.setValue(event.target.value), min: "1", readOnly: this.disableInput, value: this.quantity }), index.h("button", { key: '94ea2537b3deb379d9a07233e49d60fb3982784e', class: "s-quantity-input-decrease-button s-quantity-input-button", onClick: () => this.decrease(), type: "button", "aria-label": salla.lang.getWithDefault('common.elements.decrease_quantity', 'Decrease quantity') }, !this.hasDecrementSlot ? index.h("span", { innerHTML: Minus }) : '', index.h("slot", { key: '4756e4069e6e16f92de72eefa99d48c3793d58cf', name: "decrement-button" })))));
9606
9628
  }
9607
9629
  get host() { return index.getElement(this); }
9608
9630
  static get watchers() { return {
@@ -36,32 +36,41 @@ export class SallaProductOptions {
36
36
  * The id of the product to which the options are going to be fetched for.
37
37
  */
38
38
  this.productId = salla.config.get('page.id');
39
- this.handleDonationOptions = (event, detail, type) => {
39
+ this.handleDonationOptions = (event, detail, type, _option) => {
40
+ // Donating-amount input only: onInput updates price and dispatches native change for form price watcher.
40
41
  if (detail === 'custom' && type === 'input') {
41
- salla.helpers.inputDigitsOnly(event.target);
42
+ const inputTarget = event.target;
43
+ salla.helpers.inputDigitsOnly(inputTarget);
42
44
  salla.event.emit('product-options::donation-changed', {
43
45
  id: this.productId,
44
- price: event.target.value
46
+ price: inputTarget.value
45
47
  });
48
+ inputTarget.dispatchEvent(new window.Event('change', { bubbles: true }));
46
49
  return;
47
50
  }
48
- event.preventDefault();
49
- event.stopPropagation();
50
- this.isCustomDonation = event.target.value === 'custom';
51
+ // Tab (radio) change: get value from the radio that fired.
52
+ const value = type === 'option' ? String(event.target?.value ?? '') : '';
53
+ if (type === 'option' && detail === 'custom') {
54
+ // "Custom" tab: stop propagation so form does not see change until user types in donating-amount input.
55
+ event.preventDefault();
56
+ event.stopPropagation();
57
+ event.stopImmediatePropagation();
58
+ }
59
+ this.isCustomDonation = value === 'custom';
51
60
  if (this.donationInput) {
52
- if (event.target.value === 'custom') {
61
+ if (value === 'custom') {
53
62
  this.donationInput.value = '';
54
63
  this.donationInput.focus();
55
64
  }
56
65
  else {
57
- this.donationInput.value = event.target.value;
66
+ this.donationInput.value = value;
58
67
  }
59
68
  if (detail === 'custom') {
60
69
  return;
61
70
  }
62
71
  salla.event.emit('product-options::donation-changed', {
63
72
  id: this.productId,
64
- price: event.target.value
73
+ price: value
65
74
  });
66
75
  }
67
76
  };
@@ -533,15 +542,15 @@ export class SallaProductOptions {
533
542
  h("div", { key: option.id, class: "s-product-options-donation-progress" }, h("salla-progress-bar", { donation: option.donation }))
534
543
  : '',
535
544
  option.details.length ?
536
- [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) })))), option.donation?.custom_amount_enabled ?
537
- 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, " ")))
545
+ [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', option) }), h("label", { htmlFor: this.generateUniqueKey(`donation-option-${i}`) }, h("span", { innerHTML: salla.money(detail.name) })))), option.donation?.custom_amount_enabled ?
546
+ 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', option) }), h("label", { htmlFor: this.generateUniqueKey("donation-option-custom") }, h("span", null, " ", this.selectDonationAmount, " ")))
538
547
  : '')] : '',
539
548
  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
540
549
  && option.details.some(detail => detail.is_selected)
541
550
  ? option.details.find(detail => detail.is_selected).additional_price
542
551
  : option.value,
543
552
  // required
544
- 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))
553
+ placeholder: option.placeholder, onInput: e => this.handleDonationOptions(e, 'custom', 'input', option), 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))
545
554
  ] :
546
555
  this.getExpireDonationMessage(option));
547
556
  }
@@ -197,6 +197,19 @@ export class SallaProductsList {
197
197
  //as a request they don't want to let the user to open the product details
198
198
  //todo:: find a better way to handle this request
199
199
  this.getSource() === 'landing-page' && (product.url = '');
200
+ try {
201
+ // Add source query param to product URL for tracking in Product Viewed event
202
+ if (this.source && product.url && !product.url.includes('s-product-source=')) {
203
+ const [urlWithoutFragment, fragment] = product.url.split('#');
204
+ const separator = urlWithoutFragment.includes('?') ? '&' : '?';
205
+ const pageSlug = salla.config?.get('page.slug') || 'unknown';
206
+ const sourceValue = `${pageSlug}.${this.source}`;
207
+ product.url = `${urlWithoutFragment}${separator}s-product-source=${sourceValue}${fragment ? '#' + fragment : ''}`;
208
+ }
209
+ }
210
+ catch (error) {
211
+ Salla.log('Error with source');
212
+ }
200
213
  const customComponentTag = this.hasCustomComponent ? this.productCardComponent : 'salla-product-card';
201
214
  const productCard = document.createElement(customComponentTag);
202
215
  productCard.product = product;
@@ -38,7 +38,7 @@ export class SallaQuantityInput {
38
38
  }
39
39
  async componentWillLoad() {
40
40
  await salla.onReady();
41
- this.disableInput = salla.config.get('store.settings.product.manual_quantity');
41
+ this.disableInput = !salla.config.get('store.settings.product.manual_quantity');
42
42
  this.quantity = parseInt(this.host.getAttribute('value')) || 1;
43
43
  this.hasIncrementSlot = !!this.host.querySelector('[slot="increment-button"]');
44
44
  this.hasDecrementSlot = !!this.host.querySelector('[slot="decrement-button"]');
@@ -90,7 +90,7 @@ export class SallaQuantityInput {
90
90
  if (!inputAttributes['aria-label'] && !inputAttributes['aria-labelledby']) {
91
91
  inputAttributes['aria-label'] = salla.lang.getWithDefault('common.elements.quantity', 'Quantity');
92
92
  }
93
- return (h(Host, { key: '4dfc6c39c86047493c7b37057c877657fc6206ab', class: "s-quantity-input" }, h("div", { key: '246b30299c5b318d9412d7b065e0a219a77ba342', class: "s-quantity-input-container" }, h("button", { key: '1e8f61c6014f36e257fbc7958547228481393624', onClick: () => this.increase(), class: "s-quantity-input-increase-button s-quantity-input-button", type: "button", "aria-label": salla.lang.getWithDefault('common.elements.increase_quantity', 'Increase quantity') }, !this.hasIncrementSlot ? h("span", { innerHTML: Add }) : '', h("slot", { key: '1da4aa703d8f135437e49b35c8bd664a39c207cc', name: "increment-button" })), h("input", { key: 'd781816bc3fa9b2fc5d004c5284a583f0dc4f75b', class: "s-quantity-input-input", ...inputAttributes, ref: (el) => this.textInput = el, onInput: (event) => this.setValue(event.target.value), min: "1", readOnly: this.disableInput, value: this.quantity }), h("button", { key: 'a52ed57453eeae844e435de4f1d0e9ae66768892', class: "s-quantity-input-decrease-button s-quantity-input-button", onClick: () => this.decrease(), type: "button", "aria-label": salla.lang.getWithDefault('common.elements.decrease_quantity', 'Decrease quantity') }, !this.hasDecrementSlot ? h("span", { innerHTML: Minus }) : '', h("slot", { key: '9c2b8ef10df5dc976120249dbc5fb6d3e3b68d31', name: "decrement-button" })))));
93
+ return (h(Host, { key: 'ba636a941071cccc06a8fbcadf7a3ff93d88f4d0', class: "s-quantity-input" }, h("div", { key: '5fbe227ca172661fb8edc32940f586e7e26c8d46', class: "s-quantity-input-container" }, h("button", { key: 'dbdb3cbac199c2fb73ef49dcdb513ebd38f5f3b5', onClick: () => this.increase(), class: "s-quantity-input-increase-button s-quantity-input-button", type: "button", "aria-label": salla.lang.getWithDefault('common.elements.increase_quantity', 'Increase quantity') }, !this.hasIncrementSlot ? h("span", { innerHTML: Add }) : '', h("slot", { key: 'f117c437e73134ab4fe949c61190e38e4db81e44', name: "increment-button" })), h("input", { key: 'c41fd5bbb8760171462d3a5cc54ebe9ae54747fc', class: "s-quantity-input-input", ...inputAttributes, ref: (el) => this.textInput = el, onInput: (event) => this.setValue(event.target.value), min: "1", readOnly: this.disableInput, value: this.quantity }), h("button", { key: '94ea2537b3deb379d9a07233e49d60fb3982784e', class: "s-quantity-input-decrease-button s-quantity-input-button", onClick: () => this.decrease(), type: "button", "aria-label": salla.lang.getWithDefault('common.elements.decrease_quantity', 'Decrease quantity') }, !this.hasDecrementSlot ? h("span", { innerHTML: Minus }) : '', h("slot", { key: '4756e4069e6e16f92de72eefa99d48c3793d58cf', name: "decrement-button" })))));
94
94
  }
95
95
  static get is() { return "salla-quantity-input"; }
96
96
  static get originalStyleUrls() {
@@ -86,32 +86,41 @@ const SallaProductOptions = /*@__PURE__*/ proxyCustomElement(class SallaProductO
86
86
  * The id of the product to which the options are going to be fetched for.
87
87
  */
88
88
  this.productId = salla.config.get('page.id');
89
- this.handleDonationOptions = (event, detail, type) => {
89
+ this.handleDonationOptions = (event, detail, type, _option) => {
90
+ // Donating-amount input only: onInput updates price and dispatches native change for form price watcher.
90
91
  if (detail === 'custom' && type === 'input') {
91
- salla.helpers.inputDigitsOnly(event.target);
92
+ const inputTarget = event.target;
93
+ salla.helpers.inputDigitsOnly(inputTarget);
92
94
  salla.event.emit('product-options::donation-changed', {
93
95
  id: this.productId,
94
- price: event.target.value
96
+ price: inputTarget.value
95
97
  });
98
+ inputTarget.dispatchEvent(new window.Event('change', { bubbles: true }));
96
99
  return;
97
100
  }
98
- event.preventDefault();
99
- event.stopPropagation();
100
- this.isCustomDonation = event.target.value === 'custom';
101
+ // Tab (radio) change: get value from the radio that fired.
102
+ const value = type === 'option' ? String(event.target?.value ?? '') : '';
103
+ if (type === 'option' && detail === 'custom') {
104
+ // "Custom" tab: stop propagation so form does not see change until user types in donating-amount input.
105
+ event.preventDefault();
106
+ event.stopPropagation();
107
+ event.stopImmediatePropagation();
108
+ }
109
+ this.isCustomDonation = value === 'custom';
101
110
  if (this.donationInput) {
102
- if (event.target.value === 'custom') {
111
+ if (value === 'custom') {
103
112
  this.donationInput.value = '';
104
113
  this.donationInput.focus();
105
114
  }
106
115
  else {
107
- this.donationInput.value = event.target.value;
116
+ this.donationInput.value = value;
108
117
  }
109
118
  if (detail === 'custom') {
110
119
  return;
111
120
  }
112
121
  salla.event.emit('product-options::donation-changed', {
113
122
  id: this.productId,
114
- price: event.target.value
123
+ price: value
115
124
  });
116
125
  }
117
126
  };
@@ -583,15 +592,15 @@ const SallaProductOptions = /*@__PURE__*/ proxyCustomElement(class SallaProductO
583
592
  h("div", { key: option.id, class: "s-product-options-donation-progress" }, h("salla-progress-bar", { donation: option.donation }))
584
593
  : '',
585
594
  option.details.length ?
586
- [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) })))), option.donation?.custom_amount_enabled ?
587
- 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, " ")))
595
+ [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', option) }), h("label", { htmlFor: this.generateUniqueKey(`donation-option-${i}`) }, h("span", { innerHTML: salla.money(detail.name) })))), option.donation?.custom_amount_enabled ?
596
+ 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', option) }), h("label", { htmlFor: this.generateUniqueKey("donation-option-custom") }, h("span", null, " ", this.selectDonationAmount, " ")))
588
597
  : '')] : '',
589
598
  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
590
599
  && option.details.some(detail => detail.is_selected)
591
600
  ? option.details.find(detail => detail.is_selected).additional_price
592
601
  : option.value,
593
602
  // required
594
- 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))
603
+ placeholder: option.placeholder, onInput: e => this.handleDonationOptions(e, 'custom', 'input', option), 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))
595
604
  ] :
596
605
  this.getExpireDonationMessage(option));
597
606
  }
@@ -203,6 +203,19 @@ const SallaProductsList = /*@__PURE__*/ proxyCustomElement(class SallaProductsLi
203
203
  //as a request they don't want to let the user to open the product details
204
204
  //todo:: find a better way to handle this request
205
205
  this.getSource() === 'landing-page' && (product.url = '');
206
+ try {
207
+ // Add source query param to product URL for tracking in Product Viewed event
208
+ if (this.source && product.url && !product.url.includes('s-product-source=')) {
209
+ const [urlWithoutFragment, fragment] = product.url.split('#');
210
+ const separator = urlWithoutFragment.includes('?') ? '&' : '?';
211
+ const pageSlug = salla.config?.get('page.slug') || 'unknown';
212
+ const sourceValue = `${pageSlug}.${this.source}`;
213
+ product.url = `${urlWithoutFragment}${separator}s-product-source=${sourceValue}${fragment ? '#' + fragment : ''}`;
214
+ }
215
+ }
216
+ catch (error) {
217
+ Salla.log('Error with source');
218
+ }
206
219
  const customComponentTag = this.hasCustomComponent ? this.productCardComponent : 'salla-product-card';
207
220
  const productCard = document.createElement(customComponentTag);
208
221
  productCard.product = product;
@@ -42,7 +42,7 @@ const SallaQuantityInput$1 = /*@__PURE__*/ proxyCustomElement(class SallaQuantit
42
42
  }
43
43
  async componentWillLoad() {
44
44
  await salla.onReady();
45
- this.disableInput = salla.config.get('store.settings.product.manual_quantity');
45
+ this.disableInput = !salla.config.get('store.settings.product.manual_quantity');
46
46
  this.quantity = parseInt(this.host.getAttribute('value')) || 1;
47
47
  this.hasIncrementSlot = !!this.host.querySelector('[slot="increment-button"]');
48
48
  this.hasDecrementSlot = !!this.host.querySelector('[slot="decrement-button"]');
@@ -94,7 +94,7 @@ const SallaQuantityInput$1 = /*@__PURE__*/ proxyCustomElement(class SallaQuantit
94
94
  if (!inputAttributes['aria-label'] && !inputAttributes['aria-labelledby']) {
95
95
  inputAttributes['aria-label'] = salla.lang.getWithDefault('common.elements.quantity', 'Quantity');
96
96
  }
97
- return (h(Host, { key: '4dfc6c39c86047493c7b37057c877657fc6206ab', class: "s-quantity-input" }, h("div", { key: '246b30299c5b318d9412d7b065e0a219a77ba342', class: "s-quantity-input-container" }, h("button", { key: '1e8f61c6014f36e257fbc7958547228481393624', onClick: () => this.increase(), class: "s-quantity-input-increase-button s-quantity-input-button", type: "button", "aria-label": salla.lang.getWithDefault('common.elements.increase_quantity', 'Increase quantity') }, !this.hasIncrementSlot ? h("span", { innerHTML: Add }) : '', h("slot", { key: '1da4aa703d8f135437e49b35c8bd664a39c207cc', name: "increment-button" })), h("input", { key: 'd781816bc3fa9b2fc5d004c5284a583f0dc4f75b', class: "s-quantity-input-input", ...inputAttributes, ref: (el) => this.textInput = el, onInput: (event) => this.setValue(event.target.value), min: "1", readOnly: this.disableInput, value: this.quantity }), h("button", { key: 'a52ed57453eeae844e435de4f1d0e9ae66768892', class: "s-quantity-input-decrease-button s-quantity-input-button", onClick: () => this.decrease(), type: "button", "aria-label": salla.lang.getWithDefault('common.elements.decrease_quantity', 'Decrease quantity') }, !this.hasDecrementSlot ? h("span", { innerHTML: Minus }) : '', h("slot", { key: '9c2b8ef10df5dc976120249dbc5fb6d3e3b68d31', name: "decrement-button" })))));
97
+ return (h(Host, { key: 'ba636a941071cccc06a8fbcadf7a3ff93d88f4d0', class: "s-quantity-input" }, h("div", { key: '5fbe227ca172661fb8edc32940f586e7e26c8d46', class: "s-quantity-input-container" }, h("button", { key: 'dbdb3cbac199c2fb73ef49dcdb513ebd38f5f3b5', onClick: () => this.increase(), class: "s-quantity-input-increase-button s-quantity-input-button", type: "button", "aria-label": salla.lang.getWithDefault('common.elements.increase_quantity', 'Increase quantity') }, !this.hasIncrementSlot ? h("span", { innerHTML: Add }) : '', h("slot", { key: 'f117c437e73134ab4fe949c61190e38e4db81e44', name: "increment-button" })), h("input", { key: 'c41fd5bbb8760171462d3a5cc54ebe9ae54747fc', class: "s-quantity-input-input", ...inputAttributes, ref: (el) => this.textInput = el, onInput: (event) => this.setValue(event.target.value), min: "1", readOnly: this.disableInput, value: this.quantity }), h("button", { key: '94ea2537b3deb379d9a07233e49d60fb3982784e', class: "s-quantity-input-decrease-button s-quantity-input-button", onClick: () => this.decrease(), type: "button", "aria-label": salla.lang.getWithDefault('common.elements.decrease_quantity', 'Decrease quantity') }, !this.hasDecrementSlot ? h("span", { innerHTML: Minus }) : '', h("slot", { key: '4756e4069e6e16f92de72eefa99d48c3793d58cf', name: "decrement-button" })))));
98
98
  }
99
99
  get host() { return this; }
100
100
  static get watchers() { return {
@@ -7802,32 +7802,41 @@ const SallaProductOptions = class {
7802
7802
  * The id of the product to which the options are going to be fetched for.
7803
7803
  */
7804
7804
  this.productId = salla.config.get('page.id');
7805
- this.handleDonationOptions = (event, detail, type) => {
7805
+ this.handleDonationOptions = (event, detail, type, _option) => {
7806
+ // Donating-amount input only: onInput updates price and dispatches native change for form price watcher.
7806
7807
  if (detail === 'custom' && type === 'input') {
7807
- salla.helpers.inputDigitsOnly(event.target);
7808
+ const inputTarget = event.target;
7809
+ salla.helpers.inputDigitsOnly(inputTarget);
7808
7810
  salla.event.emit('product-options::donation-changed', {
7809
7811
  id: this.productId,
7810
- price: event.target.value
7812
+ price: inputTarget.value
7811
7813
  });
7814
+ inputTarget.dispatchEvent(new window.Event('change', { bubbles: true }));
7812
7815
  return;
7813
7816
  }
7814
- event.preventDefault();
7815
- event.stopPropagation();
7816
- this.isCustomDonation = event.target.value === 'custom';
7817
+ // Tab (radio) change: get value from the radio that fired.
7818
+ const value = type === 'option' ? String(event.target?.value ?? '') : '';
7819
+ if (type === 'option' && detail === 'custom') {
7820
+ // "Custom" tab: stop propagation so form does not see change until user types in donating-amount input.
7821
+ event.preventDefault();
7822
+ event.stopPropagation();
7823
+ event.stopImmediatePropagation();
7824
+ }
7825
+ this.isCustomDonation = value === 'custom';
7817
7826
  if (this.donationInput) {
7818
- if (event.target.value === 'custom') {
7827
+ if (value === 'custom') {
7819
7828
  this.donationInput.value = '';
7820
7829
  this.donationInput.focus();
7821
7830
  }
7822
7831
  else {
7823
- this.donationInput.value = event.target.value;
7832
+ this.donationInput.value = value;
7824
7833
  }
7825
7834
  if (detail === 'custom') {
7826
7835
  return;
7827
7836
  }
7828
7837
  salla.event.emit('product-options::donation-changed', {
7829
7838
  id: this.productId,
7830
- price: event.target.value
7839
+ price: value
7831
7840
  });
7832
7841
  }
7833
7842
  };
@@ -8299,15 +8308,15 @@ const SallaProductOptions = class {
8299
8308
  h("div", { key: option.id, class: "s-product-options-donation-progress" }, h("salla-progress-bar", { donation: option.donation }))
8300
8309
  : '',
8301
8310
  option.details.length ?
8302
- [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) })))), option.donation?.custom_amount_enabled ?
8303
- 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, " ")))
8311
+ [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', option) }), h("label", { htmlFor: this.generateUniqueKey(`donation-option-${i}`) }, h("span", { innerHTML: salla.money(detail.name) })))), option.donation?.custom_amount_enabled ?
8312
+ 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', option) }), h("label", { htmlFor: this.generateUniqueKey("donation-option-custom") }, h("span", null, " ", this.selectDonationAmount, " ")))
8304
8313
  : '')] : '',
8305
8314
  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
8306
8315
  && option.details.some(detail => detail.is_selected)
8307
8316
  ? option.details.find(detail => detail.is_selected).additional_price
8308
8317
  : option.value,
8309
8318
  // required
8310
- 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))
8319
+ placeholder: option.placeholder, onInput: e => this.handleDonationOptions(e, 'custom', 'input', option), 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))
8311
8320
  ] :
8312
8321
  this.getExpireDonationMessage(option));
8313
8322
  }
@@ -8780,6 +8789,19 @@ const SallaProductsList = class {
8780
8789
  //as a request they don't want to let the user to open the product details
8781
8790
  //todo:: find a better way to handle this request
8782
8791
  this.getSource() === 'landing-page' && (product.url = '');
8792
+ try {
8793
+ // Add source query param to product URL for tracking in Product Viewed event
8794
+ if (this.source && product.url && !product.url.includes('s-product-source=')) {
8795
+ const [urlWithoutFragment, fragment] = product.url.split('#');
8796
+ const separator = urlWithoutFragment.includes('?') ? '&' : '?';
8797
+ const pageSlug = salla.config?.get('page.slug') || 'unknown';
8798
+ const sourceValue = `${pageSlug}.${this.source}`;
8799
+ product.url = `${urlWithoutFragment}${separator}s-product-source=${sourceValue}${fragment ? '#' + fragment : ''}`;
8800
+ }
8801
+ }
8802
+ catch (error) {
8803
+ Salla.log('Error with source');
8804
+ }
8783
8805
  const customComponentTag = this.hasCustomComponent ? this.productCardComponent : 'salla-product-card';
8784
8806
  const productCard = document.createElement(customComponentTag);
8785
8807
  productCard.product = product;
@@ -9530,7 +9552,7 @@ const SallaQuantityInput = class {
9530
9552
  }
9531
9553
  async componentWillLoad() {
9532
9554
  await salla.onReady();
9533
- this.disableInput = salla.config.get('store.settings.product.manual_quantity');
9555
+ this.disableInput = !salla.config.get('store.settings.product.manual_quantity');
9534
9556
  this.quantity = parseInt(this.host.getAttribute('value')) || 1;
9535
9557
  this.hasIncrementSlot = !!this.host.querySelector('[slot="increment-button"]');
9536
9558
  this.hasDecrementSlot = !!this.host.querySelector('[slot="decrement-button"]');
@@ -9582,7 +9604,7 @@ const SallaQuantityInput = class {
9582
9604
  if (!inputAttributes['aria-label'] && !inputAttributes['aria-labelledby']) {
9583
9605
  inputAttributes['aria-label'] = salla.lang.getWithDefault('common.elements.quantity', 'Quantity');
9584
9606
  }
9585
- return (h(Host, { key: '4dfc6c39c86047493c7b37057c877657fc6206ab', class: "s-quantity-input" }, h("div", { key: '246b30299c5b318d9412d7b065e0a219a77ba342', class: "s-quantity-input-container" }, h("button", { key: '1e8f61c6014f36e257fbc7958547228481393624', onClick: () => this.increase(), class: "s-quantity-input-increase-button s-quantity-input-button", type: "button", "aria-label": salla.lang.getWithDefault('common.elements.increase_quantity', 'Increase quantity') }, !this.hasIncrementSlot ? h("span", { innerHTML: Add }) : '', h("slot", { key: '1da4aa703d8f135437e49b35c8bd664a39c207cc', name: "increment-button" })), h("input", { key: 'd781816bc3fa9b2fc5d004c5284a583f0dc4f75b', class: "s-quantity-input-input", ...inputAttributes, ref: (el) => this.textInput = el, onInput: (event) => this.setValue(event.target.value), min: "1", readOnly: this.disableInput, value: this.quantity }), h("button", { key: 'a52ed57453eeae844e435de4f1d0e9ae66768892', class: "s-quantity-input-decrease-button s-quantity-input-button", onClick: () => this.decrease(), type: "button", "aria-label": salla.lang.getWithDefault('common.elements.decrease_quantity', 'Decrease quantity') }, !this.hasDecrementSlot ? h("span", { innerHTML: Minus }) : '', h("slot", { key: '9c2b8ef10df5dc976120249dbc5fb6d3e3b68d31', name: "decrement-button" })))));
9607
+ return (h(Host, { key: 'ba636a941071cccc06a8fbcadf7a3ff93d88f4d0', class: "s-quantity-input" }, h("div", { key: '5fbe227ca172661fb8edc32940f586e7e26c8d46', class: "s-quantity-input-container" }, h("button", { key: 'dbdb3cbac199c2fb73ef49dcdb513ebd38f5f3b5', onClick: () => this.increase(), class: "s-quantity-input-increase-button s-quantity-input-button", type: "button", "aria-label": salla.lang.getWithDefault('common.elements.increase_quantity', 'Increase quantity') }, !this.hasIncrementSlot ? h("span", { innerHTML: Add }) : '', h("slot", { key: 'f117c437e73134ab4fe949c61190e38e4db81e44', name: "increment-button" })), h("input", { key: 'c41fd5bbb8760171462d3a5cc54ebe9ae54747fc', class: "s-quantity-input-input", ...inputAttributes, ref: (el) => this.textInput = el, onInput: (event) => this.setValue(event.target.value), min: "1", readOnly: this.disableInput, value: this.quantity }), h("button", { key: '94ea2537b3deb379d9a07233e49d60fb3982784e', class: "s-quantity-input-decrease-button s-quantity-input-button", onClick: () => this.decrease(), type: "button", "aria-label": salla.lang.getWithDefault('common.elements.decrease_quantity', 'Decrease quantity') }, !this.hasDecrementSlot ? h("span", { innerHTML: Minus }) : '', h("slot", { key: '4756e4069e6e16f92de72eefa99d48c3793d58cf', name: "decrement-button" })))));
9586
9608
  }
9587
9609
  get host() { return getElement(this); }
9588
9610
  static get watchers() { return {