@salla.sa/twilight-components 2.14.590 → 2.14.592

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 (390) hide show
  1. package/dist/cjs/{filepond-Dt4Nihdd.js → filepond-YeXzkn77.js} +1 -1
  2. package/dist/cjs/{filepond-plugin-file-poster-DwwSoJTP.js → filepond-plugin-file-poster-BEU99tEc.js} +1 -1
  3. package/dist/cjs/{filepond-plugin-file-validate-size-DsVYh3H7.js → filepond-plugin-file-validate-size-BSsf6VIk.js} +1 -1
  4. package/dist/cjs/{filepond-plugin-file-validate-type-D5SqBZyw.js → filepond-plugin-file-validate-type-BkxQQF1Y.js} +1 -1
  5. package/dist/cjs/{filepond-plugin-image-edit-CTTRxQHm.js → filepond-plugin-image-edit-h9C4LxoR.js} +1 -1
  6. package/dist/cjs/{filepond-plugin-image-exif-orientation-CN52oBp4.js → filepond-plugin-image-exif-orientation-nDfSwZEM.js} +1 -1
  7. package/dist/cjs/{filepond-plugin-image-preview-DgSWBJVB.js → filepond-plugin-image-preview-C-FDU_4k.js} +1 -1
  8. package/dist/cjs/{helpers-DBynEhOO.js → helpers-DSseNQ1K.js} +1 -1
  9. package/dist/cjs/{index-CgZ8Pldm.js → index-DI5yx1uj.js} +11 -15
  10. package/dist/cjs/{intlTelInput-DBBilzGk.js → intlTelInput-AgidDx18.js} +1 -1
  11. package/dist/cjs/loader.cjs.js +2 -2
  12. package/dist/cjs/salla-accordion-body_2.cjs.entry.js +1 -1
  13. package/dist/cjs/salla-accordion_6.cjs.entry.js +1 -1
  14. package/dist/cjs/salla-add-product-button_5.cjs.entry.js +37 -4
  15. package/dist/cjs/salla-advertisement.cjs.entry.js +1 -1
  16. package/dist/cjs/salla-alert_2.cjs.entry.js +1 -1
  17. package/dist/cjs/salla-app-install-alert.cjs.entry.js +1 -1
  18. package/dist/cjs/salla-applied-filters.cjs.entry.js +1 -1
  19. package/dist/cjs/salla-apps-icons.cjs.entry.js +1 -1
  20. package/dist/cjs/salla-attachment-view_9.cjs.entry.js +11 -11
  21. package/dist/cjs/salla-badge.cjs.entry.js +1 -1
  22. package/dist/cjs/salla-basket-gap.cjs.entry.js +1 -1
  23. package/dist/cjs/salla-bullet-delivery_3.cjs.entry.js +45 -34
  24. package/dist/cjs/salla-cart-coupons.cjs.entry.js +1 -1
  25. package/dist/cjs/salla-cart-item-offers_2.cjs.entry.js +1 -1
  26. package/dist/cjs/salla-cart-summary-card.cjs.entry.js +1 -1
  27. package/dist/cjs/salla-cashback-banner.cjs.entry.js +1 -1
  28. package/dist/cjs/salla-comment-factors.cjs.entry.js +1 -1
  29. package/dist/cjs/salla-comment-form_9.cjs.entry.js +3 -3
  30. package/dist/cjs/salla-conditional-offer.cjs.entry.js +2664 -106
  31. package/dist/cjs/salla-contacts.cjs.entry.js +1 -1
  32. package/dist/cjs/salla-cookies-bar.cjs.entry.js +1 -1
  33. package/dist/cjs/salla-count-down.cjs.entry.js +1 -1
  34. package/dist/cjs/salla-custom-fields.cjs.entry.js +1 -1
  35. package/dist/cjs/salla-delivery-promise.cjs.entry.js +2 -2
  36. package/dist/cjs/salla-edit-order-button.cjs.entry.js +1 -1
  37. package/dist/cjs/salla-filters-widget.cjs.entry.js +1 -1
  38. package/dist/cjs/salla-filters.cjs.entry.js +1 -1
  39. package/dist/cjs/salla-fulfillment-methods.cjs.entry.js +1 -1
  40. package/dist/cjs/{salla-gift-card-redeem.cjs.entry.js → salla-gift-card-redeem_2.cjs.entry.js} +36 -1
  41. package/dist/cjs/salla-gifting.cjs.entry.js +1 -1
  42. package/dist/cjs/salla-hook.cjs.entry.js +1 -1
  43. package/dist/cjs/salla-infinite-scroll.cjs.entry.js +1 -1
  44. package/dist/cjs/salla-installment.cjs.entry.js +1 -1
  45. package/dist/cjs/salla-list-tile.cjs.entry.js +1 -1
  46. package/dist/cjs/salla-localization-modal.cjs.entry.js +1 -1
  47. package/dist/cjs/salla-login-modal.cjs.entry.js +1 -1
  48. package/dist/cjs/salla-loyalty-banner_10.cjs.entry.js +1 -1
  49. package/dist/cjs/salla-loyalty-exchange-product-card.cjs.entry.js +1 -1
  50. package/dist/cjs/salla-loyalty-points-banner.cjs.entry.js +1 -1
  51. package/dist/cjs/salla-loyalty-points-history.cjs.entry.js +1 -1
  52. package/dist/cjs/salla-loyalty-prize-item.cjs.entry.js +1 -1
  53. package/dist/cjs/salla-loyalty.cjs.entry.js +1 -1
  54. package/dist/cjs/salla-maintenance-alert.cjs.entry.js +1 -1
  55. package/dist/cjs/salla-map_3.cjs.entry.js +1 -1
  56. package/dist/cjs/salla-menu.cjs.entry.js +1 -1
  57. package/dist/cjs/salla-metadata.cjs.entry.js +1 -1
  58. package/dist/cjs/salla-multiple-bundle-product-cart_2.cjs.entry.js +1 -1
  59. package/dist/cjs/salla-multiple-bundle-product-options-modal_2.cjs.entry.js +1 -1
  60. package/dist/cjs/salla-multiple-bundle-product.cjs.entry.js +1 -1
  61. package/dist/cjs/salla-next-order-coupon.cjs.entry.js +1 -1
  62. package/dist/cjs/salla-notification-item_2.cjs.entry.js +1 -1
  63. package/dist/cjs/salla-offer-modal.cjs.entry.js +1 -1
  64. package/dist/cjs/salla-offer.cjs.entry.js +1 -1
  65. package/dist/cjs/salla-order-branch.cjs.entry.js +1 -1
  66. package/dist/cjs/salla-order-cancel.cjs.entry.js +1 -1
  67. package/dist/cjs/salla-order-details-multiple-bundle-product.cjs.entry.js +1 -1
  68. package/dist/cjs/salla-order-details-options.cjs.entry.js +1 -1
  69. package/dist/cjs/salla-order-details.cjs.entry.js +1 -1
  70. package/dist/cjs/salla-order-edit-item.cjs.entry.js +1 -1
  71. package/dist/cjs/salla-order-edit-product-card.cjs.entry.js +1 -1
  72. package/dist/cjs/salla-order-edit.cjs.entry.js +1 -1
  73. package/dist/cjs/salla-order-options-modal.cjs.entry.js +1 -1
  74. package/dist/cjs/salla-order-shipments.cjs.entry.js +1 -1
  75. package/dist/cjs/salla-order-summary.cjs.entry.js +1 -1
  76. package/dist/cjs/salla-order-totals-card.cjs.entry.js +1 -1
  77. package/dist/cjs/salla-orders.cjs.entry.js +1 -1
  78. package/dist/cjs/salla-payments.cjs.entry.js +1 -1
  79. package/dist/cjs/salla-placeholder.cjs.entry.js +1 -1
  80. package/dist/cjs/salla-price-range.cjs.entry.js +1 -1
  81. package/dist/cjs/salla-product-card-embed.cjs.entry.js +1 -1
  82. package/dist/cjs/salla-product-card.cjs.entry.js +1 -1
  83. package/dist/cjs/salla-product-size-guide.cjs.entry.js +1 -1
  84. package/dist/cjs/salla-products-list.cjs.entry.js +1 -1
  85. package/dist/cjs/salla-products-slider.cjs.entry.js +1 -1
  86. package/dist/cjs/salla-quick-order.cjs.entry.js +1 -1
  87. package/dist/cjs/salla-rating-modal-v2.cjs.entry.js +1 -1
  88. package/dist/cjs/salla-rating-modal.cjs.entry.js +1 -1
  89. package/dist/cjs/salla-review-factors-tags.cjs.entry.js +2 -2
  90. package/dist/cjs/salla-review-order-item.cjs.entry.js +1 -1
  91. package/dist/cjs/salla-scopes.cjs.entry.js +1 -1
  92. package/dist/cjs/salla-search-product-card.cjs.entry.js +3 -3
  93. package/dist/cjs/salla-search.cjs.entry.js +2 -2
  94. package/dist/cjs/salla-skeleton.cjs.entry.js +2 -2
  95. package/dist/cjs/salla-social-share.cjs.entry.js +2 -2
  96. package/dist/cjs/salla-social.cjs.entry.js +2 -2
  97. package/dist/cjs/salla-tab-content_3.cjs.entry.js +4 -4
  98. package/dist/cjs/salla-tiered-offer.cjs.entry.js +1 -1
  99. package/dist/cjs/salla-trust-badges.cjs.entry.js +1 -1
  100. package/dist/cjs/salla-user-menu.cjs.entry.js +1 -1
  101. package/dist/cjs/salla-user-profile.cjs.entry.js +1 -1
  102. package/dist/cjs/salla-user-settings.cjs.entry.js +2 -2
  103. package/dist/cjs/salla-verify.cjs.entry.js +1 -1
  104. package/dist/cjs/salla-wallet.cjs.entry.js +1 -1
  105. package/dist/cjs/salla-wishlist-actions.cjs.entry.js +2 -2
  106. package/dist/cjs/salla-wishlist-summary.cjs.entry.js +1 -1
  107. package/dist/cjs/twilight.cjs.js +2 -2
  108. package/dist/cjs/{vanilla-picker-DMOjok7F.js → vanilla-picker-DLfPr_3c.js} +1 -1
  109. package/dist/collection/components/salla-add-product-button/salla-add-product-button.js +4 -1
  110. package/dist/collection/components/salla-bullet-delivery/helpers.js +1 -1
  111. package/dist/collection/components/salla-bullet-delivery/salla-bullet-delivery.js +39 -28
  112. package/dist/collection/components/salla-conditional-offer/cart-update-window.js +34 -0
  113. package/dist/collection/components/salla-conditional-offer/interfaces.js +1 -0
  114. package/dist/collection/components/salla-conditional-offer/salla-conditional-offer.css +456 -191
  115. package/dist/collection/components/salla-conditional-offer/salla-conditional-offer.js +1790 -106
  116. package/dist/collection/components/salla-quick-buy/salla-quick-buy.js +17 -4
  117. package/dist/collection/components/salla-review-card/salla-review-card.js +1 -1
  118. package/dist/collection/components/salla-review-factors-tags/salla-review-factors-tags.js +1 -1
  119. package/dist/collection/components/salla-reviews-page/salla-reviews-page.js +1 -1
  120. package/dist/collection/components/salla-search/salla-search.js +1 -1
  121. package/dist/collection/components/salla-search-product-card/salla-search-product-card.js +2 -2
  122. package/dist/collection/components/salla-searchable-dropdown/salla-searchable-dropdown.js +4 -4
  123. package/dist/collection/components/salla-skeleton/salla-skeleton.js +1 -1
  124. package/dist/collection/components/salla-social/salla-social.js +1 -1
  125. package/dist/collection/components/salla-social-share/salla-social-share.js +1 -1
  126. package/dist/collection/components/salla-tabs/salla-tab-content.js +1 -1
  127. package/dist/collection/components/salla-tabs/salla-tab-header.js +1 -1
  128. package/dist/collection/components/salla-tabs/salla-tabs.js +1 -1
  129. package/dist/collection/components/salla-tel-input/salla-tel-input.js +1 -1
  130. package/dist/collection/components/salla-tooltip/salla-tooltip.js +1 -1
  131. package/dist/collection/components/salla-user-settings/salla-user-settings.js +1 -1
  132. package/dist/collection/components/salla-wishlist-actions/salla-wishlist-actions.js +1 -1
  133. package/dist/collection/shared/cart-free-gifts.js +448 -0
  134. package/dist/collection/shared/offer-stages.js +453 -0
  135. package/dist/components/helpers.js +2 -2
  136. package/dist/components/index.js +3 -3
  137. package/dist/components/salla-add-product-button2.js +4 -1
  138. package/dist/components/salla-bullet-delivery.js +40 -29
  139. package/dist/components/salla-conditional-offer.js +2672 -116
  140. package/dist/components/salla-delivery-promise.js +1 -1
  141. package/dist/components/salla-quick-buy2.js +34 -4
  142. package/dist/components/salla-review-card2.js +1 -1
  143. package/dist/components/salla-review-factors-tags2.js +1 -1
  144. package/dist/components/salla-reviews-page.js +1 -1
  145. package/dist/components/salla-search-product-card2.js +2 -2
  146. package/dist/components/salla-search.js +1 -1
  147. package/dist/components/salla-searchable-dropdown2.js +4 -4
  148. package/dist/components/salla-skeleton2.js +1 -1
  149. package/dist/components/salla-social-share2.js +1 -1
  150. package/dist/components/salla-social.js +1 -1
  151. package/dist/components/salla-tab-content2.js +1 -1
  152. package/dist/components/salla-tab-header2.js +1 -1
  153. package/dist/components/salla-tabs2.js +1 -1
  154. package/dist/components/salla-tel-input2.js +1 -1
  155. package/dist/components/salla-tooltip2.js +1 -1
  156. package/dist/components/salla-user-settings.js +1 -1
  157. package/dist/components/salla-wishlist-actions.js +1 -1
  158. package/dist/docs.json +21 -8
  159. package/dist/esm/{filepond-wzh5HaPr.js → filepond-C3uLKJlo.js} +1 -1
  160. package/dist/esm/{filepond-plugin-file-poster-DInGlcq6.js → filepond-plugin-file-poster-BNO8nuwN.js} +1 -1
  161. package/dist/esm/{filepond-plugin-file-validate-size-BaLOl1L7.js → filepond-plugin-file-validate-size-CMMYF-rL.js} +1 -1
  162. package/dist/esm/{filepond-plugin-file-validate-type-D_DVE5xS.js → filepond-plugin-file-validate-type-DSYhKJ7B.js} +1 -1
  163. package/dist/esm/{filepond-plugin-image-edit-B9vWgHyi.js → filepond-plugin-image-edit-lJgRMQAo.js} +1 -1
  164. package/dist/esm/{filepond-plugin-image-exif-orientation-lJrZF6xG.js → filepond-plugin-image-exif-orientation-B0tYK1GE.js} +1 -1
  165. package/dist/esm/{filepond-plugin-image-preview-Bbk7m1Yt.js → filepond-plugin-image-preview-kWmnxhXu.js} +1 -1
  166. package/dist/esm/{helpers-BJxBnTgv.js → helpers-DgRxTSyJ.js} +2 -2
  167. package/dist/esm/{index-klFskvc_.js → index-Bk24FlDL.js} +11 -15
  168. package/dist/esm/{intlTelInput-pS6pc-iA.js → intlTelInput-nODQyuq0.js} +1 -1
  169. package/dist/esm/loader.js +3 -3
  170. package/dist/esm/salla-accordion-body_2.entry.js +1 -1
  171. package/dist/esm/salla-accordion_6.entry.js +1 -1
  172. package/dist/esm/salla-add-product-button_5.entry.js +37 -4
  173. package/dist/esm/salla-advertisement.entry.js +1 -1
  174. package/dist/esm/salla-alert_2.entry.js +1 -1
  175. package/dist/esm/salla-app-install-alert.entry.js +1 -1
  176. package/dist/esm/salla-applied-filters.entry.js +1 -1
  177. package/dist/esm/salla-apps-icons.entry.js +1 -1
  178. package/dist/esm/salla-attachment-view_9.entry.js +11 -11
  179. package/dist/esm/salla-badge.entry.js +1 -1
  180. package/dist/esm/salla-basket-gap.entry.js +1 -1
  181. package/dist/esm/salla-bullet-delivery_3.entry.js +45 -34
  182. package/dist/esm/salla-cart-coupons.entry.js +1 -1
  183. package/dist/esm/salla-cart-item-offers_2.entry.js +1 -1
  184. package/dist/esm/salla-cart-summary-card.entry.js +1 -1
  185. package/dist/esm/salla-cashback-banner.entry.js +1 -1
  186. package/dist/esm/salla-comment-factors.entry.js +1 -1
  187. package/dist/esm/salla-comment-form_9.entry.js +3 -3
  188. package/dist/esm/salla-conditional-offer.entry.js +2664 -106
  189. package/dist/esm/salla-contacts.entry.js +1 -1
  190. package/dist/esm/salla-cookies-bar.entry.js +1 -1
  191. package/dist/esm/salla-count-down.entry.js +1 -1
  192. package/dist/esm/salla-custom-fields.entry.js +1 -1
  193. package/dist/esm/salla-delivery-promise.entry.js +2 -2
  194. package/dist/esm/salla-edit-order-button.entry.js +1 -1
  195. package/dist/esm/salla-filters-widget.entry.js +1 -1
  196. package/dist/esm/salla-filters.entry.js +1 -1
  197. package/dist/esm/salla-fulfillment-methods.entry.js +1 -1
  198. package/dist/esm/{salla-gift-card-redeem.entry.js → salla-gift-card-redeem_2.entry.js} +36 -2
  199. package/dist/esm/salla-gifting.entry.js +1 -1
  200. package/dist/esm/salla-hook.entry.js +1 -1
  201. package/dist/esm/salla-infinite-scroll.entry.js +1 -1
  202. package/dist/esm/salla-installment.entry.js +1 -1
  203. package/dist/esm/salla-list-tile.entry.js +1 -1
  204. package/dist/esm/salla-localization-modal.entry.js +1 -1
  205. package/dist/esm/salla-login-modal.entry.js +1 -1
  206. package/dist/esm/salla-loyalty-banner_10.entry.js +1 -1
  207. package/dist/esm/salla-loyalty-exchange-product-card.entry.js +1 -1
  208. package/dist/esm/salla-loyalty-points-banner.entry.js +1 -1
  209. package/dist/esm/salla-loyalty-points-history.entry.js +1 -1
  210. package/dist/esm/salla-loyalty-prize-item.entry.js +1 -1
  211. package/dist/esm/salla-loyalty.entry.js +1 -1
  212. package/dist/esm/salla-maintenance-alert.entry.js +1 -1
  213. package/dist/esm/salla-map_3.entry.js +1 -1
  214. package/dist/esm/salla-menu.entry.js +1 -1
  215. package/dist/esm/salla-metadata.entry.js +1 -1
  216. package/dist/esm/salla-multiple-bundle-product-cart_2.entry.js +1 -1
  217. package/dist/esm/salla-multiple-bundle-product-options-modal_2.entry.js +1 -1
  218. package/dist/esm/salla-multiple-bundle-product.entry.js +1 -1
  219. package/dist/esm/salla-next-order-coupon.entry.js +1 -1
  220. package/dist/esm/salla-notification-item_2.entry.js +1 -1
  221. package/dist/esm/salla-offer-modal.entry.js +1 -1
  222. package/dist/esm/salla-offer.entry.js +1 -1
  223. package/dist/esm/salla-order-branch.entry.js +1 -1
  224. package/dist/esm/salla-order-cancel.entry.js +1 -1
  225. package/dist/esm/salla-order-details-multiple-bundle-product.entry.js +1 -1
  226. package/dist/esm/salla-order-details-options.entry.js +1 -1
  227. package/dist/esm/salla-order-details.entry.js +1 -1
  228. package/dist/esm/salla-order-edit-item.entry.js +1 -1
  229. package/dist/esm/salla-order-edit-product-card.entry.js +1 -1
  230. package/dist/esm/salla-order-edit.entry.js +1 -1
  231. package/dist/esm/salla-order-options-modal.entry.js +1 -1
  232. package/dist/esm/salla-order-shipments.entry.js +1 -1
  233. package/dist/esm/salla-order-summary.entry.js +1 -1
  234. package/dist/esm/salla-order-totals-card.entry.js +1 -1
  235. package/dist/esm/salla-orders.entry.js +1 -1
  236. package/dist/esm/salla-payments.entry.js +1 -1
  237. package/dist/esm/salla-placeholder.entry.js +1 -1
  238. package/dist/esm/salla-price-range.entry.js +1 -1
  239. package/dist/esm/salla-product-card-embed.entry.js +1 -1
  240. package/dist/esm/salla-product-card.entry.js +1 -1
  241. package/dist/esm/salla-product-size-guide.entry.js +1 -1
  242. package/dist/esm/salla-products-list.entry.js +1 -1
  243. package/dist/esm/salla-products-slider.entry.js +1 -1
  244. package/dist/esm/salla-quick-order.entry.js +1 -1
  245. package/dist/esm/salla-rating-modal-v2.entry.js +1 -1
  246. package/dist/esm/salla-rating-modal.entry.js +1 -1
  247. package/dist/esm/salla-review-factors-tags.entry.js +2 -2
  248. package/dist/esm/salla-review-order-item.entry.js +1 -1
  249. package/dist/esm/salla-scopes.entry.js +1 -1
  250. package/dist/esm/salla-search-product-card.entry.js +3 -3
  251. package/dist/esm/salla-search.entry.js +2 -2
  252. package/dist/esm/salla-skeleton.entry.js +2 -2
  253. package/dist/esm/salla-social-share.entry.js +2 -2
  254. package/dist/esm/salla-social.entry.js +2 -2
  255. package/dist/esm/salla-tab-content_3.entry.js +4 -4
  256. package/dist/esm/salla-tiered-offer.entry.js +1 -1
  257. package/dist/esm/salla-trust-badges.entry.js +1 -1
  258. package/dist/esm/salla-user-menu.entry.js +1 -1
  259. package/dist/esm/salla-user-profile.entry.js +1 -1
  260. package/dist/esm/salla-user-settings.entry.js +2 -2
  261. package/dist/esm/salla-verify.entry.js +1 -1
  262. package/dist/esm/salla-wallet.entry.js +1 -1
  263. package/dist/esm/salla-wishlist-actions.entry.js +2 -2
  264. package/dist/esm/salla-wishlist-summary.entry.js +1 -1
  265. package/dist/esm/twilight.js +3 -3
  266. package/dist/esm/{vanilla-picker-BhmFKXIB.js → vanilla-picker-B1bTUgHP.js} +1 -1
  267. package/dist/twilight/{p-482fee31.entry.js → p-02202feb.entry.js} +1 -1
  268. package/dist/twilight/{p-f6bae25a.entry.js → p-03dffb99.entry.js} +1 -1
  269. package/dist/twilight/{p-efeae895.entry.js → p-06643011.entry.js} +1 -1
  270. package/dist/twilight/p-07174244.entry.js +4 -0
  271. package/dist/twilight/{p-55f765dc.entry.js → p-08256e67.entry.js} +1 -1
  272. package/dist/twilight/{p-a0869ee6.entry.js → p-0dc90c55.entry.js} +1 -1
  273. package/dist/twilight/{p-dd775629.entry.js → p-0e122915.entry.js} +1 -1
  274. package/dist/twilight/{p-089f0d34.entry.js → p-1b1bebf6.entry.js} +1 -1
  275. package/dist/twilight/{p-6536466c.entry.js → p-1d05ff72.entry.js} +1 -1
  276. package/dist/twilight/{p-c33ed919.entry.js → p-21802082.entry.js} +1 -1
  277. package/dist/twilight/{p-317048c4.entry.js → p-2191d792.entry.js} +1 -1
  278. package/dist/twilight/{p-32ea6fff.entry.js → p-21b3877b.entry.js} +1 -1
  279. package/dist/twilight/{p-9897606c.entry.js → p-22135b7c.entry.js} +1 -1
  280. package/dist/twilight/{p-7836ad45.entry.js → p-250262a9.entry.js} +1 -1
  281. package/dist/twilight/{p-23ccf145.entry.js → p-2fc68bfd.entry.js} +1 -1
  282. package/dist/twilight/{p-027b5233.entry.js → p-320d6b04.entry.js} +1 -1
  283. package/dist/twilight/{p-8fbf8044.entry.js → p-377642ea.entry.js} +1 -1
  284. package/dist/twilight/{p-0b10b315.entry.js → p-38418821.entry.js} +1 -1
  285. package/dist/twilight/{p-21f2167e.entry.js → p-3bf37615.entry.js} +1 -1
  286. package/dist/twilight/{p-39325cfc.entry.js → p-3e6f2bf8.entry.js} +1 -1
  287. package/dist/twilight/{p-864959d8.entry.js → p-42150f97.entry.js} +1 -1
  288. package/dist/twilight/{p-a64f2fff.entry.js → p-42e02411.entry.js} +1 -1
  289. package/dist/twilight/{p-3acbfe00.entry.js → p-43c5ec34.entry.js} +1 -1
  290. package/dist/twilight/{p-7495249f.entry.js → p-43ca88d0.entry.js} +1 -1
  291. package/dist/twilight/{p-009048d0.entry.js → p-45035389.entry.js} +1 -1
  292. package/dist/twilight/{p-48f26c20.entry.js → p-46721e68.entry.js} +1 -1
  293. package/dist/twilight/{p-0446f8e3.entry.js → p-4f76e9e2.entry.js} +1 -1
  294. package/dist/twilight/p-4fd73440.entry.js +4 -0
  295. package/dist/twilight/{p-6ea4f06c.entry.js → p-523f9816.entry.js} +1 -1
  296. package/dist/twilight/{p-a4b9f482.entry.js → p-540029a5.entry.js} +1 -1
  297. package/dist/twilight/{p-c273ff37.entry.js → p-5435dbcd.entry.js} +1 -1
  298. package/dist/twilight/{p-23e45bd7.entry.js → p-5f6a5a4b.entry.js} +1 -1
  299. package/dist/twilight/{p-bd1dbd4b.entry.js → p-5fc75260.entry.js} +1 -1
  300. package/dist/twilight/{p-d266e9ad.entry.js → p-612b6d87.entry.js} +1 -1
  301. package/dist/twilight/{p-6fcf17d4.entry.js → p-658de43b.entry.js} +1 -1
  302. package/dist/twilight/{p-31b7fbdc.entry.js → p-6673a16f.entry.js} +1 -1
  303. package/dist/twilight/{p-5b65fffc.entry.js → p-6760ec19.entry.js} +1 -1
  304. package/dist/twilight/{p-a97a0636.entry.js → p-68411931.entry.js} +1 -1
  305. package/dist/twilight/{p-85f185cc.entry.js → p-6a76acd8.entry.js} +1 -1
  306. package/dist/twilight/{p-0f951af1.entry.js → p-7038e1af.entry.js} +1 -1
  307. package/dist/twilight/{p-d35c3f7d.entry.js → p-75f32b91.entry.js} +1 -1
  308. package/dist/twilight/{p-e28f4afc.entry.js → p-7750be26.entry.js} +1 -1
  309. package/dist/twilight/{p-d524d26e.entry.js → p-79676f24.entry.js} +1 -1
  310. package/dist/twilight/{p-36f4e851.entry.js → p-7b4a8237.entry.js} +1 -1
  311. package/dist/twilight/p-7ff0c546.entry.js +4 -0
  312. package/dist/twilight/{p-294f6c8e.entry.js → p-813a40cc.entry.js} +1 -1
  313. package/dist/twilight/{p-a0f0a176.entry.js → p-83ccddd5.entry.js} +1 -1
  314. package/dist/twilight/{p-ddb65e87.entry.js → p-872d6d68.entry.js} +1 -1
  315. package/dist/twilight/{p-1988457b.entry.js → p-9048520b.entry.js} +1 -1
  316. package/dist/twilight/{p-799a0589.entry.js → p-91a1baf1.entry.js} +1 -1
  317. package/dist/twilight/{p-d566b402.entry.js → p-9e6f4ec4.entry.js} +1 -1
  318. package/dist/twilight/{p-PcxCrM8A.js → p-B-LS1xDz.js} +1 -1
  319. package/dist/twilight/p-Bk24FlDL.js +5 -0
  320. package/dist/twilight/{p-Ba21M-o3.js → p-BnmP7F4y.js} +1 -1
  321. package/dist/twilight/{p-4asjbsmx.js → p-BvUmZYa9.js} +1 -1
  322. package/dist/twilight/{p-rKbQj-u0.js → p-CHqh7vb6.js} +1 -1
  323. package/dist/twilight/{p-HZVqfCuE.js → p-CXo-IH2o.js} +1 -1
  324. package/dist/twilight/{p-7foc7ca2.js → p-CcGfWKIx.js} +1 -1
  325. package/dist/twilight/{p-BHXx247M.js → p-Ct18qgaq.js} +1 -1
  326. package/dist/twilight/{p-ClbVN5Nv.js → p-DBjpz_eG.js} +1 -1
  327. package/dist/twilight/{p-BJxBnTgv.js → p-DgRxTSyJ.js} +1 -1
  328. package/dist/twilight/{p-BcMiEHs_.js → p-DvlAEiaH.js} +1 -1
  329. package/dist/twilight/{p-78ff082d.entry.js → p-a057f532.entry.js} +1 -1
  330. package/dist/twilight/{p-a1d43865.entry.js → p-a0f1b867.entry.js} +1 -1
  331. package/dist/twilight/{p-6e45ec66.entry.js → p-a75680ae.entry.js} +1 -1
  332. package/dist/twilight/{p-6ea8b4a8.entry.js → p-ad52dcf7.entry.js} +1 -1
  333. package/dist/twilight/{p-cb47fc80.entry.js → p-b009d316.entry.js} +1 -1
  334. package/dist/twilight/{p-45a13d19.entry.js → p-b1b096d7.entry.js} +1 -1
  335. package/dist/twilight/{p-f407afee.entry.js → p-b1b345d6.entry.js} +1 -1
  336. package/dist/twilight/{p-0de54240.entry.js → p-b2c41e36.entry.js} +1 -1
  337. package/dist/twilight/{p-0746fded.entry.js → p-b37ae926.entry.js} +1 -1
  338. package/dist/twilight/{p-52d1d467.entry.js → p-b5e774db.entry.js} +1 -1
  339. package/dist/twilight/{p-7ddae4df.entry.js → p-b6dc3e12.entry.js} +1 -1
  340. package/dist/twilight/{p-9950bd4f.entry.js → p-b95a6e20.entry.js} +1 -1
  341. package/dist/twilight/{p-655c7f90.entry.js → p-bb152b39.entry.js} +1 -1
  342. package/dist/twilight/{p-d21e8092.entry.js → p-be729ca2.entry.js} +1 -1
  343. package/dist/twilight/{p-788ffe88.entry.js → p-becb6580.entry.js} +1 -1
  344. package/dist/twilight/{p-3ba09a8e.entry.js → p-c1d18b73.entry.js} +1 -1
  345. package/dist/twilight/{p-2667efd6.entry.js → p-c1d815c9.entry.js} +1 -1
  346. package/dist/twilight/{p-a0f2b1b7.entry.js → p-c313bd11.entry.js} +1 -1
  347. package/dist/twilight/{p-3de1d01b.entry.js → p-c3a2696b.entry.js} +1 -1
  348. package/dist/twilight/{p-c62b3757.entry.js → p-c9ebe466.entry.js} +1 -1
  349. package/dist/twilight/{p-0fca7ee0.entry.js → p-cc74d38b.entry.js} +1 -1
  350. package/dist/twilight/{p-34b91d42.entry.js → p-cd432c49.entry.js} +1 -1
  351. package/dist/twilight/{p-94eddc5c.entry.js → p-ce6c709b.entry.js} +1 -1
  352. package/dist/twilight/{p-b98b2cda.entry.js → p-ced74b88.entry.js} +1 -1
  353. package/dist/twilight/{p-8c7614fb.entry.js → p-d601977e.entry.js} +1 -1
  354. package/dist/twilight/{p-1d8c6adc.entry.js → p-d73307c7.entry.js} +1 -1
  355. package/dist/twilight/{p-8bc3a449.entry.js → p-d9ccdd8f.entry.js} +1 -1
  356. package/dist/twilight/{p-945eec73.entry.js → p-dc51993c.entry.js} +1 -1
  357. package/dist/twilight/{p-56ce31f2.entry.js → p-dfd212d5.entry.js} +1 -1
  358. package/dist/twilight/{p-62b8c2cc.entry.js → p-e2aa0e72.entry.js} +1 -1
  359. package/dist/twilight/{p-1a1f04ac.entry.js → p-e58b7ef9.entry.js} +1 -1
  360. package/dist/twilight/p-e7127704.entry.js +4 -0
  361. package/dist/twilight/{p-da05ae96.entry.js → p-e7ca6d06.entry.js} +1 -1
  362. package/dist/twilight/{p-6b767b19.entry.js → p-eaae379c.entry.js} +1 -1
  363. package/dist/twilight/{p-0156dfe8.entry.js → p-ed6ea140.entry.js} +1 -1
  364. package/dist/twilight/{p-53f8972b.entry.js → p-f263bba9.entry.js} +1 -1
  365. package/dist/twilight/p-f3ffe84f.entry.js +4 -0
  366. package/dist/twilight/{p-f757ba41.entry.js → p-f4f7ed38.entry.js} +1 -1
  367. package/dist/twilight/{p-0ccf7bde.entry.js → p-f5c9e513.entry.js} +1 -1
  368. package/dist/twilight/{p-ce0a4802.entry.js → p-f74ccae4.entry.js} +1 -1
  369. package/dist/twilight/{p-45cc68ce.entry.js → p-f7ee4910.entry.js} +1 -1
  370. package/dist/twilight/{p-09300a8d.entry.js → p-f8f9c563.entry.js} +1 -1
  371. package/dist/twilight/{p-74ba0473.entry.js → p-f9f0a5ee.entry.js} +1 -1
  372. package/dist/twilight/{p-a7a64c45.entry.js → p-fbee9e50.entry.js} +1 -1
  373. package/dist/twilight/twilight.esm.js +1 -1
  374. package/dist/types/components/salla-bullet-delivery/salla-bullet-delivery.d.ts +12 -0
  375. package/dist/types/components/salla-conditional-offer/cart-update-window.d.ts +31 -0
  376. package/dist/types/components/salla-conditional-offer/interfaces.d.ts +70 -13
  377. package/dist/types/components/salla-conditional-offer/salla-conditional-offer.d.ts +656 -10
  378. package/dist/types/components/salla-search/search-response.d.ts +1 -0
  379. package/dist/types/shared/cart-free-gifts.d.ts +141 -0
  380. package/dist/types/shared/offer-stages.d.ts +288 -0
  381. package/package.json +5 -5
  382. package/dist/cjs/salla-tooltip.cjs.entry.js +0 -42
  383. package/dist/esm/salla-tooltip.entry.js +0 -40
  384. package/dist/twilight/p-0e206ed7.entry.js +0 -4
  385. package/dist/twilight/p-1a875a94.entry.js +0 -4
  386. package/dist/twilight/p-8f5929a9.entry.js +0 -4
  387. package/dist/twilight/p-917ce4f9.entry.js +0 -4
  388. package/dist/twilight/p-d4f3905f.entry.js +0 -4
  389. package/dist/twilight/p-e47f5ec9.entry.js +0 -4
  390. package/dist/twilight/p-klFskvc_.js +0 -5
@@ -0,0 +1,453 @@
1
+ /*!
2
+ * Crafted with ❤ by Salla
3
+ */
4
+ /**
5
+ * Multi-stage promotion progress.
6
+ *
7
+ * Pure, component-agnostic helpers for turning a shopper's current cart value
8
+ * plus a list of stage thresholds into "which stage is Locked / Current /
9
+ * Completed, and how full is each segment".
10
+ *
11
+ * Deliberately NOT shared with `salla-tiered-offer`'s `TierUtils`:
12
+ * - `TierUtils.findCurrentTierIndex` is 1-based with `0` overloaded to mean
13
+ * "no tier reached", which cannot express "stage 0 is reached".
14
+ * - It takes `TieredOfferTier[]`, a shape a conditional-offer `Discount` does
15
+ * not share a single field with.
16
+ * - It assumes its caller already sorted; the conditional-offer payload is
17
+ * emitted UNSORTED by the backend.
18
+ *
19
+ * So this module takes plain numbers, is 0-based, uses an explicit
20
+ * `NO_STAGE_REACHED` sentinel, and sorts defensively on every call.
21
+ *
22
+ * ONE deliberate exception to "pure": `getCurrencyDecimals()` reads the store's money
23
+ * precision off `salla.config` so that `getRemainingToStage()` can round AT THE SOURCE
24
+ * and every consumer — including the `products_count` path that never touches
25
+ * `salla.money()` — benefits without each of them remembering to. It is fully guarded and
26
+ * falls back to `DEFAULT_CURRENCY_DECIMALS`, so a missing SDK degrades rather than throws;
27
+ * callers that want it hermetic can pass the measure and stub `salla.config`.
28
+ *
29
+ * ---------------------------------------------------------------------------
30
+ * WHO DECIDES A TIER IS UNLOCKED
31
+ * ---------------------------------------------------------------------------
32
+ * The ENGINE does, and it now says so: every `details.discounts[]` entry carries
33
+ * `is_applied`. Threshold arithmetic here is a PROJECTION ("how far to go"), never a
34
+ * CLAIM about what the engine granted, because the engine's formula
35
+ *
36
+ * ($subtotal - $alreadyAppliedDiscount) - $excludedItemsAmount >= min_spend
37
+ *
38
+ * uses two terms that are not in the payload at all. On beta a gift tier at `min_spend`
39
+ * 300 with `current_value` 300 rendered as unlocked while the engine had refused it (the
40
+ * gift's own price is in that tier's excluded set), so no arithmetic over shipped keys
41
+ * could ever have agreed with it.
42
+ *
43
+ * BUT `is_applied` IS NOT "THE SHOPPER IS PAST THIS TIER". It answers a narrower
44
+ * question, and reading it as the broader one is what produced the second beta report.
45
+ * `CartOfferDiscount::apply()` walks the tiers, keeps the LAST one whose conditions pass
46
+ * and calls `discountItem()` ONCE after the loop, so a cart offer grants EXACTLY ONE
47
+ * reward — the best qualifying tier. Every earlier tier is therefore `is_applied: false`
48
+ * even for a shopper who sailed past it: its reward is SUPERSEDED, not out of reach. A
49
+ * cart at `current_value` 510 came back as `false / false / true` and the badge row read
50
+ * LOCKED / LOCKED / CHECKED, which is unreadable as a progress stepper.
51
+ *
52
+ * So there are TWO questions and this module answers both, separately:
53
+ * - "which tier gave the reward" — the single winner, `appliedIndex`.
54
+ * - "is the shopper past this tier" — cumulative, `met`, which is what the ✓, the
55
+ * strikethrough, the lock and the "N of M" count render from.
56
+ *
57
+ * The passed tiers carry a ✓ AND a strikethrough while the tier ahead carries a lock.
58
+ * Struck-through means superseded, so a ✓ means THRESHOLD PASSED, not REWARD GRANTED.
59
+ *
60
+ * Hence the split, and it is load-bearing:
61
+ * - `met` / `metCount` / `nextUnmetIndex` / `allMet` are the ENGINE-DERIVED verdict.
62
+ * Render from these.
63
+ * - `appliedIndex` names the one tier that actually paid out. Diagnostics only so far.
64
+ * - `lastReachedIndex` / `currentIndex` / `isComplete` / `fills` / `overallPercentage`
65
+ * are pure threshold projections. Use them for distance and bar geometry only.
66
+ *
67
+ * `is_applied` is TRI-STATE, because twilight and the Store package deploy independently:
68
+ * absent means "this backend does not report it yet" and falls back to the arithmetic,
69
+ * while `false` is a real verdict and outranks the arithmetic. See `readAppliedFlag`.
70
+ *
71
+ * `salla-tiered-offer` keeps its own copy of the defensive ascending sort and the
72
+ * completed/total ratio; that duplication is deliberate and not migrated here.
73
+ */
74
+ /** Explicit sentinel: no stage has been reached yet (or there is no next stage). */
75
+ export const NO_STAGE_REACHED = -1;
76
+ export var OfferStageState;
77
+ (function (OfferStageState) {
78
+ OfferStageState["LOCKED"] = "locked";
79
+ OfferStageState["CURRENT"] = "current";
80
+ OfferStageState["COMPLETED"] = "completed";
81
+ })(OfferStageState || (OfferStageState = {}));
82
+ function parseNumeric(value) {
83
+ const parsed = typeof value === 'number' ? value : parseFloat(value);
84
+ return Number.isFinite(parsed) ? parsed : null;
85
+ }
86
+ /**
87
+ * Read one tier's `is_applied` as a tri-state.
88
+ *
89
+ * `true`/`false` booleans are what `CartOffersTransformer` casts and emits. The numeric
90
+ * and string spellings are accepted too because the `s-source: mobile` pipeline already
91
+ * stringifies numeric fields in this very payload (`end_value`), and reading a `0` there
92
+ * as "absent" would silently restore the bug this exists to fix.
93
+ *
94
+ * EVERYTHING ELSE — key missing, `undefined`, `null`, `''`, an object — is ABSENT, not
95
+ * `false`: a value we cannot interpret is not the engine saying no, and must degrade to
96
+ * the arithmetic fallback rather than lock a tier the shopper has genuinely earned.
97
+ */
98
+ export function readAppliedFlag(value) {
99
+ if (typeof value === 'boolean')
100
+ return value;
101
+ if (value === 1 || value === 0)
102
+ return value === 1;
103
+ if (value === 'true' || value === '1')
104
+ return true;
105
+ if (value === 'false' || value === '0')
106
+ return false;
107
+ return undefined;
108
+ }
109
+ /**
110
+ * Coerce a cart VALUE. Falling back to 0 is correct here: an unreadable
111
+ * `current_value`/`start_value` means "the shopper has made no progress".
112
+ */
113
+ function toFiniteNumber(value) {
114
+ return parseNumeric(value) ?? 0;
115
+ }
116
+ /**
117
+ * Whether a THRESHOLD is usable.
118
+ *
119
+ * Thresholds get the opposite policy to values: an unparseable threshold is DROPPED,
120
+ * never coerced to 0, because a 0 threshold is satisfied by an empty cart and would
121
+ * render as an already-COMPLETED stage — reintroducing exactly the synthetic-zeroth-
122
+ * checkpoint bug this component was rewritten to remove.
123
+ *
124
+ * Callers that keep their own parallel array of stage objects MUST filter it with this
125
+ * predicate so their index alignment with `progress.thresholds` survives the drop.
126
+ */
127
+ export function isUsableThreshold(value) {
128
+ return parseNumeric(value) !== null;
129
+ }
130
+ function clamp(value, min, max) {
131
+ return Math.max(min, Math.min(value, max));
132
+ }
133
+ /**
134
+ * Fill of one segment as a 0..100 percentage.
135
+ * A zero-width (or inverted) segment — which duplicate thresholds produce — has
136
+ * no gradient to show, so it is all-or-nothing rather than a division by zero.
137
+ */
138
+ function segmentFill(value, from, to) {
139
+ if (to <= from) {
140
+ return value >= to ? 100 : 0;
141
+ }
142
+ return clamp(((value - from) / (to - from)) * 100, 0, 100);
143
+ }
144
+ /**
145
+ * Stable ascending sort of arbitrary items by a numeric threshold accessor.
146
+ *
147
+ * Ties are broken by `getIdentity` when supplied and readable on BOTH sides — the tier
148
+ * `id` (0 = base tier, 1..N = configured options in the order the engine walks them), so
149
+ * two tiers sharing a `min_spend` order by what they ARE rather than by where they
150
+ * happened to land in an unsorted payload. Without it (or when either id is unreadable)
151
+ * ties keep their original relative order, which is the previous behaviour exactly.
152
+ */
153
+ export function sortByThreshold(items, getThreshold, getIdentity) {
154
+ return (items ?? [])
155
+ .map((item, index) => ({
156
+ item,
157
+ index,
158
+ threshold: toFiniteNumber(getThreshold(item)),
159
+ identity: getIdentity ? parseNumeric(getIdentity(item)) : null,
160
+ }))
161
+ .sort((a, b) => {
162
+ if (a.threshold !== b.threshold)
163
+ return a.threshold - b.threshold;
164
+ if (a.identity !== null && b.identity !== null && a.identity !== b.identity) {
165
+ return a.identity - b.identity;
166
+ }
167
+ return a.index - b.index;
168
+ })
169
+ .map(({ item }) => item);
170
+ }
171
+ /**
172
+ * Derive the full stage picture from the server's `current_value`.
173
+ *
174
+ * @param currentValue the server-computed cart value — never recomputed client-side.
175
+ * @param rawThresholds each stage's `min_spend`, in any order.
176
+ * @param rawStartValue the baseline the first segment fills from (`details.start_value`).
177
+ * @param rawApplied each stage's `is_applied`, POSITIONALLY ALIGNED WITH `rawThresholds`
178
+ * before any sorting or dropping happens here. Omit it and every tier reads as
179
+ * absent, i.e. the threshold behaviour, unchanged.
180
+ * @param rawTierValues each stage's OWN `current_value`, aligned the same way. A tier
181
+ * without a readable one is measured by `currentValue`, so omitting it — an older
182
+ * backend — changes nothing. See `OfferStageProgress.values`.
183
+ */
184
+ export function computeOfferStageProgress(currentValue, rawThresholds, rawStartValue = 0, rawApplied = [], rawTierValues = []) {
185
+ // Unparseable thresholds are dropped, not zeroed — see `isUsableThreshold`.
186
+ //
187
+ // The flag travels WITH its threshold through both the drop and the sort. Sorting two
188
+ // parallel arrays separately is how an `is_applied` ends up on the wrong tier, and a
189
+ // misattributed verdict is worse than no verdict at all. The tier's own value rides
190
+ // along for the same reason.
191
+ const entries = (rawThresholds ?? [])
192
+ .map((threshold, index) => ({
193
+ index,
194
+ threshold: parseNumeric(threshold),
195
+ applied: readAppliedFlag(rawApplied?.[index]),
196
+ tierValue: parseNumeric(rawTierValues?.[index]),
197
+ }))
198
+ .filter((entry) => entry.threshold !== null)
199
+ .sort((a, b) => a.threshold - b.threshold || a.index - b.index);
200
+ const thresholds = entries.map(entry => entry.threshold);
201
+ const applied = entries.map(entry => entry.applied);
202
+ const hasEngineVerdict = applied.some(flag => flag !== undefined);
203
+ const startValue = toFiniteNumber(rawStartValue);
204
+ if (!thresholds.length) {
205
+ return {
206
+ thresholds,
207
+ met: [],
208
+ applied,
209
+ appliedIndex: NO_STAGE_REACHED,
210
+ hasEngineVerdict,
211
+ metCount: 0,
212
+ nextUnmetIndex: NO_STAGE_REACHED,
213
+ allMet: false,
214
+ lastReachedIndex: NO_STAGE_REACHED,
215
+ currentIndex: NO_STAGE_REACHED,
216
+ isComplete: false,
217
+ value: Math.max(0, toFiniteNumber(currentValue)),
218
+ values: [],
219
+ startValue,
220
+ fills: [],
221
+ overallPercentage: 0,
222
+ };
223
+ }
224
+ const finalThreshold = thresholds[thresholds.length - 1];
225
+ // THE CLAMP: a stale or overshooting `current_value` must never produce a
226
+ // >100% bar or an out-of-range stage index.
227
+ const value = clamp(toFiniteNumber(currentValue), 0, Math.max(0, finalThreshold));
228
+ // Each tier against ITS OWN progress where the backend sent one — see `values`. A tier
229
+ // value that is missing or unreadable is not "no progress": it is "not reported", and
230
+ // falls back to the offer-level value rather than to 0.
231
+ const values = entries.map(({ tierValue }) => tierValue === null ? value : clamp(tierValue, 0, Math.max(0, finalThreshold)));
232
+ let lastReachedIndex = NO_STAGE_REACHED;
233
+ for (let index = 0; index < thresholds.length; index++) {
234
+ if (values[index] >= thresholds[index]) {
235
+ lastReachedIndex = index;
236
+ }
237
+ }
238
+ const isComplete = lastReachedIndex === thresholds.length - 1;
239
+ const currentIndex = isComplete ? NO_STAGE_REACHED : lastReachedIndex + 1;
240
+ const fills = thresholds.map((threshold, index) => segmentFill(values[index], index === 0 ? startValue : thresholds[index - 1], threshold));
241
+ const overallPercentage = isComplete
242
+ ? 100
243
+ : clamp(((lastReachedIndex + 1 + (fills[currentIndex] ?? 0) / 100) / thresholds.length) * 100, 0, 100);
244
+ // ---------------------------------------------------------------------------------
245
+ // THE DECISION — three cases, in this order.
246
+ //
247
+ // (1) THE ENGINE GRANTED A TIER. `appliedIndex` is the HIGHEST tier flagged `true`, and
248
+ // every tier up to and including it is met. A cart offer pays out ONCE, at the best
249
+ // qualifying tier, so the earlier tiers are `false` because they were SUPERSEDED —
250
+ // the shopper is past them. Closing the prefix is what stops a lock rendering below
251
+ // a ✓ (the beta screenshot: `false / false / true` at `current_value` 510).
252
+ //
253
+ // HIGHEST, not first, and read AFTER the ascending sort: the payload arrives
254
+ // unsorted, so "the last tier the engine kept" is only "the furthest tier reached"
255
+ // once the rows are in threshold order. A NON-CONTIGUOUS set — `[true, false, true]`
256
+ // — should be impossible for a single-winner engine, but if one ever arrives the
257
+ // highest wins and the whole prefix reads met. That is deliberate: the alternative
258
+ // is the un-steppable row this fix exists to remove, and a superset of ✓ merely
259
+ // over-credits a tier the shopper has demonstrably paid past.
260
+ //
261
+ // (2) THE ENGINE SPOKE AND GRANTED NOTHING. Everything is locked, whatever the
262
+ // thresholds say. This is the OTHER beta cart — `current_value` 510, zero discount,
263
+ // every tier `false` — and it is the case `is_applied` was introduced for. The
264
+ // arithmetic MUST NOT be consulted here or the bug comes straight back.
265
+ //
266
+ // (3) THIS BACKEND REPORTS NOTHING (`hasEngineVerdict` false — no tier carried a
267
+ // readable flag). Only then does the cumulative threshold comparison run, exactly
268
+ // as before `is_applied` shipped. Twilight and the Store package deploy
269
+ // independently, so this path is live, not theoretical.
270
+ //
271
+ // Cases (1) and (2) are split on the WINNER, not on each tier's own flag, so `??` vs
272
+ // `||` no longer decides a single row on its own. The tri-state still decides which
273
+ // CASE we are in: an unreadable flag is absent (case 3), an explicit `false` is an
274
+ // answer that keeps us in cases 1-2. A mixed payload — some tiers flagged, some not,
275
+ // which no current backend emits — is treated as having spoken: only a `true` can name
276
+ // the winner, so an unflagged tier can never be one.
277
+ // ---------------------------------------------------------------------------------
278
+ let appliedIndex = NO_STAGE_REACHED;
279
+ for (let index = 0; index < applied.length; index++) {
280
+ if (applied[index] === true) {
281
+ appliedIndex = index;
282
+ }
283
+ }
284
+ // Case (3) closes the prefix at `lastReachedIndex` rather than comparing tier by tier.
285
+ // With one value for every tier the two are the same statement; with per-tier values
286
+ // it is what keeps `met` a prefix, on the same single-winner reasoning as case (1).
287
+ const met = hasEngineVerdict
288
+ ? thresholds.map((_, index) => index <= appliedIndex)
289
+ : thresholds.map((_, index) => index <= lastReachedIndex);
290
+ const metCount = met.filter(Boolean).length;
291
+ const nextUnmetIndex = met.findIndex(isMet => !isMet); // -1 === NO_STAGE_REACHED
292
+ const allMet = nextUnmetIndex === NO_STAGE_REACHED;
293
+ return {
294
+ thresholds,
295
+ met,
296
+ applied,
297
+ appliedIndex,
298
+ hasEngineVerdict,
299
+ metCount,
300
+ nextUnmetIndex,
301
+ allMet,
302
+ lastReachedIndex,
303
+ currentIndex,
304
+ isComplete,
305
+ value,
306
+ values,
307
+ startValue,
308
+ fills,
309
+ overallPercentage,
310
+ };
311
+ }
312
+ /**
313
+ * Locked / Current / Completed for one 0-based stage index — from `met`, so a tier the
314
+ * engine withheld outright reads LOCKED even with its threshold met, and every tier the
315
+ * shopper has passed on the way to a granted one reads COMPLETED.
316
+ *
317
+ * - Completed: `met[index]` — index at or below the winner.
318
+ * - Current: exactly `nextUnmetIndex`, the first tier ABOVE the winner.
319
+ * - Locked: every other unmet stage, and any out-of-range index.
320
+ */
321
+ export function getStageState(index, progress) {
322
+ if (index >= 0 && progress.met[index]) {
323
+ return OfferStageState.COMPLETED;
324
+ }
325
+ if (index === progress.nextUnmetIndex && index !== NO_STAGE_REACHED) {
326
+ return OfferStageState.CURRENT;
327
+ }
328
+ return OfferStageState.LOCKED;
329
+ }
330
+ /**
331
+ * How the offer MEASURES its thresholds, which is also what its remainder rounds to.
332
+ *
333
+ * Mirrors `OfferType` in `salla-conditional-offer/interfaces.ts` without importing it —
334
+ * this module stays free of the component's payload types (see the header) — so a caller
335
+ * maps `details.based_on` onto it at the call site.
336
+ */
337
+ export var OfferStageMeasure;
338
+ (function (OfferStageMeasure) {
339
+ /** `order_amount` — a money value, rounded to the store's currency precision. */
340
+ OfferStageMeasure["CURRENCY"] = "currency";
341
+ /** `products_count` — whole products, so the remainder is a whole number. */
342
+ OfferStageMeasure["COUNT"] = "count";
343
+ })(OfferStageMeasure || (OfferStageMeasure = {}));
344
+ /**
345
+ * The precision to fall back to when the store publishes none.
346
+ *
347
+ * 2 is the platform default (SAR/USD/AED); a store on a 3-decimal currency (KWD, BHD)
348
+ * that reports its precision gets 3, and only a store that reports NOTHING readable
349
+ * lands here.
350
+ */
351
+ export const DEFAULT_CURRENCY_DECIMALS = 2;
352
+ /**
353
+ * Hard ceiling on the precision a store can ask for.
354
+ *
355
+ * The value comes off `salla.config`, i.e. off the wire, and `Math.pow(10, n)` with a
356
+ * large `n` reintroduces the very float noise this rounding exists to remove (and with
357
+ * an absurd `n`, an `Infinity` factor and a `NaN` remainder). 4 matches the cap
358
+ * `salla-cart-summary-card.roundToCurrency()` already applies to the same problem.
359
+ */
360
+ const MAX_CURRENCY_DECIMALS = 4;
361
+ /**
362
+ * Read one candidate precision, or `null` when it is not a usable one.
363
+ *
364
+ * Strict on purpose. `salla.config.get()` returns THE KEY ITSELF for a key the store did
365
+ * not publish (`config.ts:50`), so an unpublished `store.currency.decimals` arrives as
366
+ * the string `"store.currency.decimals"` — truthy, non-null, and fatal to a `?? 2`
367
+ * fallback, which is why this parses and range-checks rather than nullish-coalescing.
368
+ */
369
+ function readDecimals(raw) {
370
+ const parsed = typeof raw === 'number' ? raw : parseFloat(raw);
371
+ if (!Number.isFinite(parsed))
372
+ return null;
373
+ const decimals = Math.trunc(parsed);
374
+ return decimals >= 0 && decimals <= MAX_CURRENCY_DECIMALS ? decimals : null;
375
+ }
376
+ /**
377
+ * The store's money precision — THE ONE IMPURITY IN THIS MODULE (see the header note).
378
+ *
379
+ * Asked in the order the platform answers it:
380
+ * 1. `salla.config.currency()` — the ACTIVE currency's own precision, which is the only
381
+ * source that can differ per currency (a store displaying KWD wants 3 while its SAR
382
+ * prices want 2).
383
+ * 2. `salla.config.get('store.currency.decimals')` — the store-level key
384
+ * `salla-bought-together.tsx:142` already rounds its basket total by.
385
+ * 3. `DEFAULT_CURRENCY_DECIMALS`.
386
+ *
387
+ * Every step is guarded: this runs inside `render()`, where an SDK that is not on the
388
+ * page yet (or a `config` that throws) must degrade to the default, never take the banner
389
+ * down.
390
+ */
391
+ export function getCurrencyDecimals() {
392
+ try {
393
+ const sdk = typeof salla !== 'undefined' ? salla : undefined;
394
+ const fromCurrency = readDecimals(sdk?.config?.currency?.()?.decimals);
395
+ if (fromCurrency !== null)
396
+ return fromCurrency;
397
+ const fromConfig = readDecimals(sdk?.config?.get?.('store.currency.decimals'));
398
+ if (fromConfig !== null)
399
+ return fromConfig;
400
+ }
401
+ catch (_) {
402
+ // No SDK, or a config that threw — the default is the answer.
403
+ }
404
+ return DEFAULT_CURRENCY_DECIMALS;
405
+ }
406
+ /**
407
+ * Float noise allowed when rounding a remainder UP, in units of the display precision.
408
+ * `300 - 289.6` is `10.399999999999977`, which must still read as exactly 10.40 rather
409
+ * than tip over to 10.41.
410
+ */
411
+ const CEIL_TOLERANCE = 1e-6;
412
+ /**
413
+ * How much more of the cart value is needed to reach the given stage, rounded UP at the
414
+ * display precision. Never negative.
415
+ *
416
+ * `threshold - value` is raw IEEE-754 subtraction over two server-sent floats, so
417
+ * `300 - 289.599993` is `10.400007000000012`. Nothing downstream cleans that up:
418
+ * `salla.money()` only swaps digits and appends a symbol (`base/src/helpers/numbers.ts:74-84`).
419
+ * Rounding here, at the source, covers every consumer, including the `products_count`
420
+ * path that never reaches `salla.money`.
421
+ *
422
+ * Rounding goes UP, never to the nearest unit: the displayed amount must be enough to
423
+ * unlock the tier. Rounding 10.400007 to 10.40 asks for an amount that leaves the cart
424
+ * at 299.999993, still below the threshold. Float noise is guarded (see `CEIL_TOLERANCE`)
425
+ * so an exact 10.40 stays 10.40.
426
+ *
427
+ * A count remainder is a WHOLE number, because `salla.lang.choice('blocks.header.products_count', 10.4)`
428
+ * matches no plural interval and a shopper cannot add 0.4 of a product.
429
+ *
430
+ * Only the derived remainder is rounded. `progress.values` and `progress.thresholds` are
431
+ * server facts and stay as sent: rounding them would move the unlocked/locked boundary,
432
+ * which is the engine's call.
433
+ *
434
+ * A positive remainder never becomes 0: `getGuidanceMessage` drops its line on
435
+ * `remaining <= 0`, so a genuinely unmet tier must still ask for at least one unit of the
436
+ * precision (0.01 for a 2-decimal currency, 1 for a count). Exact zero, a met tier,
437
+ * still returns 0.
438
+ */
439
+ export function getRemainingToStage(index, progress, measure = OfferStageMeasure.CURRENCY) {
440
+ const threshold = progress.thresholds[index];
441
+ if (threshold === undefined) {
442
+ return 0;
443
+ }
444
+ // The distance to a tier is measured in THAT tier's own progress — see `values`.
445
+ const raw = Math.max(0, threshold - (progress.values?.[index] ?? progress.value));
446
+ if (raw === 0)
447
+ return 0;
448
+ const decimals = measure === OfferStageMeasure.COUNT ? 0 : getCurrencyDecimals();
449
+ const factor = Math.pow(10, decimals);
450
+ const rounded = Math.ceil(raw * factor - CEIL_TOLERANCE) / factor;
451
+ // Still owed something, but only float noise above zero: ask for one unit.
452
+ return rounded > 0 ? rounded : 1 / factor;
453
+ }
@@ -240,7 +240,7 @@ function findNearestBranch(branches, userLat, userLon) {
240
240
  for (const b of branches) {
241
241
  const lat = b.location?.lat != null ? Number(b.location.lat) : Number.NaN;
242
242
  const lng = b.location?.lng != null ? Number(b.location.lng) : Number.NaN;
243
- if (Number.isNaN(lat) || Number.isNaN(lng) || b.is_open === false)
243
+ if (Number.isNaN(lat) || Number.isNaN(lng) || !isBranchSelectable(b))
244
244
  continue;
245
245
  const d = calculateDistance(userLat, userLon, lat, lng);
246
246
  if (d < minD) {
@@ -302,4 +302,4 @@ const GEO_ERROR_MESSAGES = {
302
302
  };
303
303
  const getGeolocationErrorMessage = (errorCode) => GEO_ERROR_MESSAGES[errorCode] ?? 'An error occurred while detecting location';
304
304
 
305
- export { getIntentLongitude as a, bulletDeliveryAPI as b, getIntentCityId as c, getIntentCountryId as d, getIntentRegionId as e, getIntentDistrictId as f, getIntentLatitude as g, hasSessionAddressIntent as h, isSaudiArabia as i, filterBranches as j, findNearestBranch as k, getGeolocationErrorMessage as l, clearApiCache as m, getIntentSubtitle as n, getIntentBranchId as o, isMapOriginIntent as p, isBranchSelectable as q, requireRegionAndDistrictForSA as r, getIntentCountryCode as s };
305
+ export { getIntentLongitude as a, bulletDeliveryAPI as b, getIntentCityId as c, getIntentCountryId as d, getIntentRegionId as e, getIntentDistrictId as f, getIntentLatitude as g, hasSessionAddressIntent as h, isSaudiArabia as i, isBranchSelectable as j, filterBranches as k, getIntentBranchId as l, findNearestBranch as m, getGeolocationErrorMessage as n, clearApiCache as o, getIntentSubtitle as p, isMapOriginIntent as q, requireRegionAndDistrictForSA as r, getIntentCountryCode as s };