@salla.sa/twilight-components 2.14.589 → 2.14.591

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 (345) hide show
  1. package/dist/cjs/{filepond-BbRL5Spe.js → filepond-DkObEO32.js} +1 -1
  2. package/dist/cjs/{filepond-plugin-file-poster-DVUBjaQx.js → filepond-plugin-file-poster-wXgeY5cU.js} +1 -1
  3. package/dist/cjs/{filepond-plugin-file-validate-size-BR7SoIq0.js → filepond-plugin-file-validate-size-CWleid1C.js} +1 -1
  4. package/dist/cjs/{filepond-plugin-file-validate-type-CEkMfiUA.js → filepond-plugin-file-validate-type-CJLl2hbe.js} +1 -1
  5. package/dist/cjs/{filepond-plugin-image-edit-p-XQj23Q.js → filepond-plugin-image-edit-Bp6AxXM9.js} +1 -1
  6. package/dist/cjs/{filepond-plugin-image-exif-orientation-DeRO_gvX.js → filepond-plugin-image-exif-orientation-abWKSX9-.js} +1 -1
  7. package/dist/cjs/{filepond-plugin-image-preview-Bfm8Swyp.js → filepond-plugin-image-preview-DxprNycS.js} +1 -1
  8. package/dist/cjs/{index-CfGnMoB7.js → index-DBN0LueR.js} +10 -14
  9. package/dist/cjs/{intlTelInput-Pqo18emd.js → intlTelInput-B6H18Td2.js} +1 -1
  10. package/dist/cjs/loader.cjs.js +2 -2
  11. package/dist/cjs/salla-accordion-body_2.cjs.entry.js +1 -1
  12. package/dist/cjs/salla-accordion_6.cjs.entry.js +1 -1
  13. package/dist/cjs/salla-add-product-button_5.cjs.entry.js +1 -1
  14. package/dist/cjs/salla-advertisement.cjs.entry.js +1 -1
  15. package/dist/cjs/salla-alert_2.cjs.entry.js +1 -1
  16. package/dist/cjs/salla-app-install-alert.cjs.entry.js +1 -1
  17. package/dist/cjs/salla-applied-filters.cjs.entry.js +1 -1
  18. package/dist/cjs/salla-apps-icons.cjs.entry.js +1 -1
  19. package/dist/cjs/salla-attachment-view_9.cjs.entry.js +10 -10
  20. package/dist/cjs/salla-badge.cjs.entry.js +1 -1
  21. package/dist/cjs/salla-basket-gap.cjs.entry.js +1 -1
  22. package/dist/cjs/salla-bullet-delivery_3.cjs.entry.js +1 -1
  23. package/dist/cjs/salla-cart-coupons.cjs.entry.js +1 -1
  24. package/dist/cjs/salla-cart-item-offers_2.cjs.entry.js +1 -1
  25. package/dist/cjs/salla-cart-summary-card.cjs.entry.js +1 -1
  26. package/dist/cjs/salla-cashback-banner.cjs.entry.js +1 -1
  27. package/dist/cjs/salla-comment-factors.cjs.entry.js +1 -1
  28. package/dist/cjs/salla-comment-form_9.cjs.entry.js +1 -1
  29. package/dist/cjs/salla-conditional-offer.cjs.entry.js +2664 -106
  30. package/dist/cjs/salla-contacts.cjs.entry.js +1 -1
  31. package/dist/cjs/salla-cookies-bar.cjs.entry.js +1 -1
  32. package/dist/cjs/salla-count-down.cjs.entry.js +1 -1
  33. package/dist/cjs/salla-custom-fields.cjs.entry.js +1 -1
  34. package/dist/cjs/salla-delivery-promise.cjs.entry.js +1 -1
  35. package/dist/cjs/salla-edit-order-button.cjs.entry.js +1 -1
  36. package/dist/cjs/salla-filters-widget.cjs.entry.js +1 -1
  37. package/dist/cjs/salla-filters.cjs.entry.js +1 -1
  38. package/dist/cjs/salla-fulfillment-methods.cjs.entry.js +1 -1
  39. package/dist/cjs/{salla-gift-card-redeem.cjs.entry.js → salla-gift-card-redeem_2.cjs.entry.js} +36 -1
  40. package/dist/cjs/salla-gifting.cjs.entry.js +1 -1
  41. package/dist/cjs/salla-hook.cjs.entry.js +1 -1
  42. package/dist/cjs/salla-infinite-scroll.cjs.entry.js +1 -1
  43. package/dist/cjs/salla-installment.cjs.entry.js +1 -1
  44. package/dist/cjs/salla-list-tile.cjs.entry.js +1 -1
  45. package/dist/cjs/salla-localization-modal.cjs.entry.js +1 -1
  46. package/dist/cjs/salla-login-modal.cjs.entry.js +1 -1
  47. package/dist/cjs/salla-loyalty-banner_10.cjs.entry.js +1 -1
  48. package/dist/cjs/salla-loyalty-exchange-product-card.cjs.entry.js +1 -1
  49. package/dist/cjs/salla-loyalty-points-banner.cjs.entry.js +1 -1
  50. package/dist/cjs/salla-loyalty-points-history.cjs.entry.js +1 -1
  51. package/dist/cjs/salla-loyalty-prize-item.cjs.entry.js +1 -1
  52. package/dist/cjs/salla-loyalty.cjs.entry.js +1 -1
  53. package/dist/cjs/salla-maintenance-alert.cjs.entry.js +1 -1
  54. package/dist/cjs/salla-map_3.cjs.entry.js +1 -1
  55. package/dist/cjs/salla-menu.cjs.entry.js +1 -1
  56. package/dist/cjs/salla-metadata.cjs.entry.js +1 -1
  57. package/dist/cjs/salla-multiple-bundle-product-cart_2.cjs.entry.js +1 -1
  58. package/dist/cjs/salla-multiple-bundle-product-options-modal_2.cjs.entry.js +1 -1
  59. package/dist/cjs/salla-multiple-bundle-product.cjs.entry.js +1 -1
  60. package/dist/cjs/salla-next-order-coupon.cjs.entry.js +1 -1
  61. package/dist/cjs/salla-notification-item_2.cjs.entry.js +1 -1
  62. package/dist/cjs/salla-offer-modal.cjs.entry.js +1 -1
  63. package/dist/cjs/salla-offer.cjs.entry.js +1 -1
  64. package/dist/cjs/salla-order-branch.cjs.entry.js +1 -1
  65. package/dist/cjs/salla-order-cancel.cjs.entry.js +1 -1
  66. package/dist/cjs/salla-order-details-multiple-bundle-product.cjs.entry.js +1 -1
  67. package/dist/cjs/salla-order-details-options.cjs.entry.js +1 -1
  68. package/dist/cjs/salla-order-details.cjs.entry.js +1 -1
  69. package/dist/cjs/salla-order-edit-item.cjs.entry.js +1 -1
  70. package/dist/cjs/salla-order-edit-product-card.cjs.entry.js +1 -1
  71. package/dist/cjs/salla-order-edit.cjs.entry.js +1 -1
  72. package/dist/cjs/salla-order-options-modal.cjs.entry.js +1 -1
  73. package/dist/cjs/salla-order-shipments.cjs.entry.js +1 -1
  74. package/dist/cjs/salla-order-summary.cjs.entry.js +1 -1
  75. package/dist/cjs/salla-order-totals-card.cjs.entry.js +1 -1
  76. package/dist/cjs/salla-orders.cjs.entry.js +1 -1
  77. package/dist/cjs/salla-payments.cjs.entry.js +1 -1
  78. package/dist/cjs/salla-placeholder.cjs.entry.js +1 -1
  79. package/dist/cjs/salla-price-range.cjs.entry.js +1 -1
  80. package/dist/cjs/salla-product-card-embed.cjs.entry.js +1 -1
  81. package/dist/cjs/salla-product-card.cjs.entry.js +1 -1
  82. package/dist/cjs/salla-product-size-guide.cjs.entry.js +1 -1
  83. package/dist/cjs/salla-products-list.cjs.entry.js +35 -6
  84. package/dist/cjs/salla-products-slider.cjs.entry.js +1 -1
  85. package/dist/cjs/salla-quick-order.cjs.entry.js +1 -1
  86. package/dist/cjs/salla-rating-modal-v2.cjs.entry.js +1 -1
  87. package/dist/cjs/salla-rating-modal.cjs.entry.js +1 -1
  88. package/dist/cjs/salla-review-factors-tags.cjs.entry.js +1 -1
  89. package/dist/cjs/salla-review-order-item.cjs.entry.js +1 -1
  90. package/dist/cjs/salla-scopes.cjs.entry.js +1 -1
  91. package/dist/cjs/salla-search-product-card.cjs.entry.js +1 -1
  92. package/dist/cjs/salla-search.cjs.entry.js +1 -1
  93. package/dist/cjs/salla-skeleton.cjs.entry.js +1 -1
  94. package/dist/cjs/salla-social-share.cjs.entry.js +1 -1
  95. package/dist/cjs/salla-social.cjs.entry.js +1 -1
  96. package/dist/cjs/salla-tab-content_3.cjs.entry.js +1 -1
  97. package/dist/cjs/salla-tiered-offer.cjs.entry.js +1 -1
  98. package/dist/cjs/salla-trust-badges.cjs.entry.js +1 -1
  99. package/dist/cjs/salla-user-menu.cjs.entry.js +1 -1
  100. package/dist/cjs/salla-user-profile.cjs.entry.js +1 -1
  101. package/dist/cjs/salla-user-settings.cjs.entry.js +1 -1
  102. package/dist/cjs/salla-verify.cjs.entry.js +1 -1
  103. package/dist/cjs/salla-wallet.cjs.entry.js +1 -1
  104. package/dist/cjs/salla-wishlist-actions.cjs.entry.js +1 -1
  105. package/dist/cjs/salla-wishlist-summary.cjs.entry.js +1 -1
  106. package/dist/cjs/twilight.cjs.js +2 -2
  107. package/dist/cjs/{vanilla-picker-DWuMUfiW.js → vanilla-picker-Cm71HMBs.js} +1 -1
  108. package/dist/collection/components/salla-conditional-offer/cart-update-window.js +34 -0
  109. package/dist/collection/components/salla-conditional-offer/interfaces.js +1 -0
  110. package/dist/collection/components/salla-conditional-offer/salla-conditional-offer.css +456 -191
  111. package/dist/collection/components/salla-conditional-offer/salla-conditional-offer.js +1790 -106
  112. package/dist/collection/components/salla-products-list/salla-products-list.js +35 -6
  113. package/dist/collection/shared/cart-free-gifts.js +448 -0
  114. package/dist/collection/shared/offer-stages.js +453 -0
  115. package/dist/components/index.js +2 -2
  116. package/dist/components/salla-conditional-offer.js +2672 -116
  117. package/dist/components/salla-products-list2.js +34 -5
  118. package/dist/docs.json +22 -9
  119. package/dist/esm/{filepond-CJqzOtq7.js → filepond-Cstf5r6K.js} +1 -1
  120. package/dist/esm/{filepond-plugin-file-poster-CPydcEjJ.js → filepond-plugin-file-poster-GibBgGHW.js} +1 -1
  121. package/dist/esm/{filepond-plugin-file-validate-size-Yy4pNy1Q.js → filepond-plugin-file-validate-size-CLc6zco6.js} +1 -1
  122. package/dist/esm/{filepond-plugin-file-validate-type-Ck1P2E4e.js → filepond-plugin-file-validate-type-Ca3dc8p0.js} +1 -1
  123. package/dist/esm/{filepond-plugin-image-edit-DEUmnuGf.js → filepond-plugin-image-edit-CNGBLi7d.js} +1 -1
  124. package/dist/esm/{filepond-plugin-image-exif-orientation-BPgJye8E.js → filepond-plugin-image-exif-orientation-Dmmu9-UQ.js} +1 -1
  125. package/dist/esm/{filepond-plugin-image-preview-CqUSx6BO.js → filepond-plugin-image-preview-KX15tx60.js} +1 -1
  126. package/dist/esm/{index-BnDYuqv2.js → index-UnmOIma5.js} +10 -14
  127. package/dist/esm/{intlTelInput-Z8OsAFtr.js → intlTelInput-C-F-rxZ3.js} +1 -1
  128. package/dist/esm/loader.js +3 -3
  129. package/dist/esm/salla-accordion-body_2.entry.js +1 -1
  130. package/dist/esm/salla-accordion_6.entry.js +1 -1
  131. package/dist/esm/salla-add-product-button_5.entry.js +1 -1
  132. package/dist/esm/salla-advertisement.entry.js +1 -1
  133. package/dist/esm/salla-alert_2.entry.js +1 -1
  134. package/dist/esm/salla-app-install-alert.entry.js +1 -1
  135. package/dist/esm/salla-applied-filters.entry.js +1 -1
  136. package/dist/esm/salla-apps-icons.entry.js +1 -1
  137. package/dist/esm/salla-attachment-view_9.entry.js +10 -10
  138. package/dist/esm/salla-badge.entry.js +1 -1
  139. package/dist/esm/salla-basket-gap.entry.js +1 -1
  140. package/dist/esm/salla-bullet-delivery_3.entry.js +1 -1
  141. package/dist/esm/salla-cart-coupons.entry.js +1 -1
  142. package/dist/esm/salla-cart-item-offers_2.entry.js +1 -1
  143. package/dist/esm/salla-cart-summary-card.entry.js +1 -1
  144. package/dist/esm/salla-cashback-banner.entry.js +1 -1
  145. package/dist/esm/salla-comment-factors.entry.js +1 -1
  146. package/dist/esm/salla-comment-form_9.entry.js +1 -1
  147. package/dist/esm/salla-conditional-offer.entry.js +2664 -106
  148. package/dist/esm/salla-contacts.entry.js +1 -1
  149. package/dist/esm/salla-cookies-bar.entry.js +1 -1
  150. package/dist/esm/salla-count-down.entry.js +1 -1
  151. package/dist/esm/salla-custom-fields.entry.js +1 -1
  152. package/dist/esm/salla-delivery-promise.entry.js +1 -1
  153. package/dist/esm/salla-edit-order-button.entry.js +1 -1
  154. package/dist/esm/salla-filters-widget.entry.js +1 -1
  155. package/dist/esm/salla-filters.entry.js +1 -1
  156. package/dist/esm/salla-fulfillment-methods.entry.js +1 -1
  157. package/dist/esm/{salla-gift-card-redeem.entry.js → salla-gift-card-redeem_2.entry.js} +36 -2
  158. package/dist/esm/salla-gifting.entry.js +1 -1
  159. package/dist/esm/salla-hook.entry.js +1 -1
  160. package/dist/esm/salla-infinite-scroll.entry.js +1 -1
  161. package/dist/esm/salla-installment.entry.js +1 -1
  162. package/dist/esm/salla-list-tile.entry.js +1 -1
  163. package/dist/esm/salla-localization-modal.entry.js +1 -1
  164. package/dist/esm/salla-login-modal.entry.js +1 -1
  165. package/dist/esm/salla-loyalty-banner_10.entry.js +1 -1
  166. package/dist/esm/salla-loyalty-exchange-product-card.entry.js +1 -1
  167. package/dist/esm/salla-loyalty-points-banner.entry.js +1 -1
  168. package/dist/esm/salla-loyalty-points-history.entry.js +1 -1
  169. package/dist/esm/salla-loyalty-prize-item.entry.js +1 -1
  170. package/dist/esm/salla-loyalty.entry.js +1 -1
  171. package/dist/esm/salla-maintenance-alert.entry.js +1 -1
  172. package/dist/esm/salla-map_3.entry.js +1 -1
  173. package/dist/esm/salla-menu.entry.js +1 -1
  174. package/dist/esm/salla-metadata.entry.js +1 -1
  175. package/dist/esm/salla-multiple-bundle-product-cart_2.entry.js +1 -1
  176. package/dist/esm/salla-multiple-bundle-product-options-modal_2.entry.js +1 -1
  177. package/dist/esm/salla-multiple-bundle-product.entry.js +1 -1
  178. package/dist/esm/salla-next-order-coupon.entry.js +1 -1
  179. package/dist/esm/salla-notification-item_2.entry.js +1 -1
  180. package/dist/esm/salla-offer-modal.entry.js +1 -1
  181. package/dist/esm/salla-offer.entry.js +1 -1
  182. package/dist/esm/salla-order-branch.entry.js +1 -1
  183. package/dist/esm/salla-order-cancel.entry.js +1 -1
  184. package/dist/esm/salla-order-details-multiple-bundle-product.entry.js +1 -1
  185. package/dist/esm/salla-order-details-options.entry.js +1 -1
  186. package/dist/esm/salla-order-details.entry.js +1 -1
  187. package/dist/esm/salla-order-edit-item.entry.js +1 -1
  188. package/dist/esm/salla-order-edit-product-card.entry.js +1 -1
  189. package/dist/esm/salla-order-edit.entry.js +1 -1
  190. package/dist/esm/salla-order-options-modal.entry.js +1 -1
  191. package/dist/esm/salla-order-shipments.entry.js +1 -1
  192. package/dist/esm/salla-order-summary.entry.js +1 -1
  193. package/dist/esm/salla-order-totals-card.entry.js +1 -1
  194. package/dist/esm/salla-orders.entry.js +1 -1
  195. package/dist/esm/salla-payments.entry.js +1 -1
  196. package/dist/esm/salla-placeholder.entry.js +1 -1
  197. package/dist/esm/salla-price-range.entry.js +1 -1
  198. package/dist/esm/salla-product-card-embed.entry.js +1 -1
  199. package/dist/esm/salla-product-card.entry.js +1 -1
  200. package/dist/esm/salla-product-size-guide.entry.js +1 -1
  201. package/dist/esm/salla-products-list.entry.js +35 -6
  202. package/dist/esm/salla-products-slider.entry.js +1 -1
  203. package/dist/esm/salla-quick-order.entry.js +1 -1
  204. package/dist/esm/salla-rating-modal-v2.entry.js +1 -1
  205. package/dist/esm/salla-rating-modal.entry.js +1 -1
  206. package/dist/esm/salla-review-factors-tags.entry.js +1 -1
  207. package/dist/esm/salla-review-order-item.entry.js +1 -1
  208. package/dist/esm/salla-scopes.entry.js +1 -1
  209. package/dist/esm/salla-search-product-card.entry.js +1 -1
  210. package/dist/esm/salla-search.entry.js +1 -1
  211. package/dist/esm/salla-skeleton.entry.js +1 -1
  212. package/dist/esm/salla-social-share.entry.js +1 -1
  213. package/dist/esm/salla-social.entry.js +1 -1
  214. package/dist/esm/salla-tab-content_3.entry.js +1 -1
  215. package/dist/esm/salla-tiered-offer.entry.js +1 -1
  216. package/dist/esm/salla-trust-badges.entry.js +1 -1
  217. package/dist/esm/salla-user-menu.entry.js +1 -1
  218. package/dist/esm/salla-user-profile.entry.js +1 -1
  219. package/dist/esm/salla-user-settings.entry.js +1 -1
  220. package/dist/esm/salla-verify.entry.js +1 -1
  221. package/dist/esm/salla-wallet.entry.js +1 -1
  222. package/dist/esm/salla-wishlist-actions.entry.js +1 -1
  223. package/dist/esm/salla-wishlist-summary.entry.js +1 -1
  224. package/dist/esm/twilight.js +3 -3
  225. package/dist/esm/{vanilla-picker-DnAUrfGC.js → vanilla-picker-DMAcm04D.js} +1 -1
  226. package/dist/twilight/{p-fdb16a84.entry.js → p-06268aeb.entry.js} +1 -1
  227. package/dist/twilight/{p-cff5dd0b.entry.js → p-0bee12ba.entry.js} +1 -1
  228. package/dist/twilight/{p-dfdd4c78.entry.js → p-13215fa2.entry.js} +1 -1
  229. package/dist/twilight/{p-ef5a0e71.entry.js → p-170a29db.entry.js} +1 -1
  230. package/dist/twilight/{p-179c76df.entry.js → p-1762cfe3.entry.js} +1 -1
  231. package/dist/twilight/{p-3319b19b.entry.js → p-18b786f4.entry.js} +1 -1
  232. package/dist/twilight/{p-9932f7a0.entry.js → p-1a7bee69.entry.js} +1 -1
  233. package/dist/twilight/{p-152cac66.entry.js → p-1aaed68f.entry.js} +1 -1
  234. package/dist/twilight/{p-1bafeac8.entry.js → p-1dc68727.entry.js} +1 -1
  235. package/dist/twilight/{p-d38ae081.entry.js → p-2059ac26.entry.js} +1 -1
  236. package/dist/twilight/{p-45b9c237.entry.js → p-21358994.entry.js} +1 -1
  237. package/dist/twilight/{p-c895701a.entry.js → p-2308e70c.entry.js} +1 -1
  238. package/dist/twilight/{p-35dabed0.entry.js → p-27366663.entry.js} +1 -1
  239. package/dist/twilight/{p-80ead7f0.entry.js → p-2d983198.entry.js} +1 -1
  240. package/dist/twilight/{p-c5948d07.entry.js → p-2e204cbc.entry.js} +1 -1
  241. package/dist/twilight/{p-eb0c5e39.entry.js → p-2e971549.entry.js} +1 -1
  242. package/dist/twilight/{p-721b6019.entry.js → p-2f1c8e60.entry.js} +1 -1
  243. package/dist/twilight/{p-7eeb788a.entry.js → p-328e352d.entry.js} +1 -1
  244. package/dist/twilight/{p-53dc0615.entry.js → p-35163279.entry.js} +1 -1
  245. package/dist/twilight/{p-df933610.entry.js → p-36ac5a06.entry.js} +1 -1
  246. package/dist/twilight/{p-2cdcd70d.entry.js → p-3845541d.entry.js} +1 -1
  247. package/dist/twilight/{p-e0b0e90f.entry.js → p-38956156.entry.js} +1 -1
  248. package/dist/twilight/{p-ad7814d7.entry.js → p-398e864d.entry.js} +1 -1
  249. package/dist/twilight/{p-c2858bed.entry.js → p-3d535509.entry.js} +1 -1
  250. package/dist/twilight/{p-827fa966.entry.js → p-3d7fdd11.entry.js} +1 -1
  251. package/dist/twilight/p-40a7f8c6.entry.js +4 -0
  252. package/dist/twilight/{p-ba61073e.entry.js → p-426ac7ab.entry.js} +1 -1
  253. package/dist/twilight/{p-0ecf936e.entry.js → p-42d6eb6b.entry.js} +1 -1
  254. package/dist/twilight/{p-1c812d90.entry.js → p-46ef2b4e.entry.js} +1 -1
  255. package/dist/twilight/{p-700f2c8b.entry.js → p-47e519e4.entry.js} +1 -1
  256. package/dist/twilight/{p-08a8a1ff.entry.js → p-48f9cd45.entry.js} +1 -1
  257. package/dist/twilight/{p-47d94d77.entry.js → p-49f2ad4a.entry.js} +1 -1
  258. package/dist/twilight/{p-652ab33d.entry.js → p-4b135194.entry.js} +1 -1
  259. package/dist/twilight/p-4b175320.entry.js +4 -0
  260. package/dist/twilight/{p-4d0df0fe.entry.js → p-51d7a9f2.entry.js} +1 -1
  261. package/dist/twilight/{p-ed7c4f53.entry.js → p-52b99743.entry.js} +1 -1
  262. package/dist/twilight/{p-11a1c557.entry.js → p-540992c1.entry.js} +1 -1
  263. package/dist/twilight/{p-1a598e9b.entry.js → p-5c313f74.entry.js} +1 -1
  264. package/dist/twilight/{p-cdad2ba2.entry.js → p-62842639.entry.js} +1 -1
  265. package/dist/twilight/{p-f5ccd5d1.entry.js → p-635cf0ae.entry.js} +1 -1
  266. package/dist/twilight/{p-4e50bc09.entry.js → p-6a68c551.entry.js} +1 -1
  267. package/dist/twilight/{p-085aac95.entry.js → p-6b277129.entry.js} +1 -1
  268. package/dist/twilight/{p-81cdd5c5.entry.js → p-6dcfc119.entry.js} +1 -1
  269. package/dist/twilight/{p-5537738b.entry.js → p-6e7a12dd.entry.js} +1 -1
  270. package/dist/twilight/{p-018629e9.entry.js → p-700cf9fc.entry.js} +1 -1
  271. package/dist/twilight/{p-7c9e88d3.entry.js → p-71372771.entry.js} +1 -1
  272. package/dist/twilight/{p-760d1b6c.entry.js → p-78d1e145.entry.js} +1 -1
  273. package/dist/twilight/{p-bf7e7835.entry.js → p-7f23b98d.entry.js} +1 -1
  274. package/dist/twilight/{p-80228706.entry.js → p-7f93ea69.entry.js} +1 -1
  275. package/dist/twilight/{p-bd559920.entry.js → p-81294230.entry.js} +1 -1
  276. package/dist/twilight/{p-c0304211.entry.js → p-833fb416.entry.js} +1 -1
  277. package/dist/twilight/{p-cbeff415.entry.js → p-8e938bfa.entry.js} +1 -1
  278. package/dist/twilight/{p-ae8b1d8b.entry.js → p-8ec3b9f0.entry.js} +1 -1
  279. package/dist/twilight/{p-56f0cfcb.entry.js → p-9b01e5c2.entry.js} +1 -1
  280. package/dist/twilight/{p-85f0dcd7.entry.js → p-9c284764.entry.js} +1 -1
  281. package/dist/twilight/{p-0efb20f7.entry.js → p-9d0658a0.entry.js} +1 -1
  282. package/dist/twilight/{p-444e6c0b.entry.js → p-9d680306.entry.js} +1 -1
  283. package/dist/twilight/{p-CR5J1D0q.js → p-BOWrmsKT.js} +1 -1
  284. package/dist/twilight/{p-B7eX2j-t.js → p-C3HaKgMG.js} +1 -1
  285. package/dist/twilight/{p-G2sLbl_C.js → p-CjeoBv5q.js} +1 -1
  286. package/dist/twilight/{p-DTfPuMCT.js → p-CtQLzZ1X.js} +1 -1
  287. package/dist/twilight/{p-BXv9XzTR.js → p-DVatlmKV.js} +1 -1
  288. package/dist/twilight/{p-DV1rxosP.js → p-D_C0F9n5.js} +1 -1
  289. package/dist/twilight/{p-3jS5Dm-u.js → p-Dq91sWJh.js} +1 -1
  290. package/dist/twilight/{p-BnDYuqv2.js → p-UnmOIma5.js} +1 -1
  291. package/dist/twilight/{p-0b53b377.entry.js → p-a0e1d5f7.entry.js} +1 -1
  292. package/dist/twilight/{p-6f26c1cd.entry.js → p-a192abb7.entry.js} +1 -1
  293. package/dist/twilight/{p-6ca5c1b6.entry.js → p-a1d0c650.entry.js} +1 -1
  294. package/dist/twilight/{p-171e4a2f.entry.js → p-a1d251d4.entry.js} +1 -1
  295. package/dist/twilight/{p-71c70e04.entry.js → p-a32fb61e.entry.js} +1 -1
  296. package/dist/twilight/{p-cfe6fbe3.entry.js → p-a5c693c1.entry.js} +1 -1
  297. package/dist/twilight/{p-11b351be.entry.js → p-acd9c538.entry.js} +1 -1
  298. package/dist/twilight/{p-d38e101f.entry.js → p-adfc54e4.entry.js} +1 -1
  299. package/dist/twilight/{p-ea8379b8.entry.js → p-b5d6ddd7.entry.js} +1 -1
  300. package/dist/twilight/{p-e1a8f943.entry.js → p-b70cdf88.entry.js} +1 -1
  301. package/dist/twilight/{p-8cf71ac9.entry.js → p-b7399739.entry.js} +1 -1
  302. package/dist/twilight/{p-9bc5c3d5.entry.js → p-bb4a95d9.entry.js} +1 -1
  303. package/dist/twilight/{p-66ae8f3b.entry.js → p-c1eab306.entry.js} +1 -1
  304. package/dist/twilight/{p-3b14f3d4.entry.js → p-c1eaf8c3.entry.js} +1 -1
  305. package/dist/twilight/{p-67e24689.entry.js → p-ca47204f.entry.js} +1 -1
  306. package/dist/twilight/{p-612fb2e8.entry.js → p-caec4a90.entry.js} +1 -1
  307. package/dist/twilight/{p-7ccdd251.entry.js → p-ce0c6667.entry.js} +1 -1
  308. package/dist/twilight/{p-df3feae1.entry.js → p-ce7450a9.entry.js} +1 -1
  309. package/dist/twilight/{p-4262b4d7.entry.js → p-d11b211f.entry.js} +1 -1
  310. package/dist/twilight/{p-8e679d21.entry.js → p-d62c6529.entry.js} +1 -1
  311. package/dist/twilight/{p-8ef3ef7d.entry.js → p-da14ac3f.entry.js} +1 -1
  312. package/dist/twilight/{p-0f5846fe.entry.js → p-e0ce5d4c.entry.js} +1 -1
  313. package/dist/twilight/{p-aac26dda.entry.js → p-e1d59b7e.entry.js} +1 -1
  314. package/dist/twilight/{p-00bc0e76.entry.js → p-e3066c95.entry.js} +1 -1
  315. package/dist/twilight/{p-1d1315d7.entry.js → p-e34ff202.entry.js} +1 -1
  316. package/dist/twilight/{p-ff5362e3.entry.js → p-e3c46eea.entry.js} +1 -1
  317. package/dist/twilight/{p-661755d7.entry.js → p-e6de2f79.entry.js} +1 -1
  318. package/dist/twilight/{p-331fe074.entry.js → p-ea07afaa.entry.js} +1 -1
  319. package/dist/twilight/{p-f70bf6b0.entry.js → p-efb67446.entry.js} +1 -1
  320. package/dist/twilight/p-efb9f953.entry.js +4 -0
  321. package/dist/twilight/{p-343a9ec3.entry.js → p-f08ff103.entry.js} +1 -1
  322. package/dist/twilight/{p-ba5f879e.entry.js → p-f5605df6.entry.js} +1 -1
  323. package/dist/twilight/{p-9fd926a2.entry.js → p-f69a3e84.entry.js} +1 -1
  324. package/dist/twilight/{p-0e1f8e61.entry.js → p-f7b65338.entry.js} +1 -1
  325. package/dist/twilight/{p-dcaaa19e.entry.js → p-f92f10c7.entry.js} +1 -1
  326. package/dist/twilight/{p-e49ce881.entry.js → p-fa3c4bcf.entry.js} +1 -1
  327. package/dist/twilight/{p-3771c21e.entry.js → p-fb8395de.entry.js} +1 -1
  328. package/dist/twilight/{p-3d08b459.entry.js → p-ff65be76.entry.js} +1 -1
  329. package/dist/twilight/{p-DpkirgB4.js → p-iC3SM6Ji.js} +1 -1
  330. package/dist/twilight/{p-8Y4Ctfsa.js → p-xX3a9YdQ.js} +1 -1
  331. package/dist/twilight/twilight.esm.js +1 -1
  332. package/dist/types/components/salla-conditional-offer/cart-update-window.d.ts +31 -0
  333. package/dist/types/components/salla-conditional-offer/interfaces.d.ts +70 -13
  334. package/dist/types/components/salla-conditional-offer/salla-conditional-offer.d.ts +656 -10
  335. package/dist/types/components/salla-products-list/salla-products-list.d.ts +4 -0
  336. package/dist/types/components.d.ts +2 -2
  337. package/dist/types/shared/cart-free-gifts.d.ts +141 -0
  338. package/dist/types/shared/offer-stages.d.ts +288 -0
  339. package/package.json +5 -5
  340. package/dist/cjs/salla-tooltip.cjs.entry.js +0 -42
  341. package/dist/esm/salla-tooltip.entry.js +0 -40
  342. package/dist/twilight/p-483551e7.entry.js +0 -4
  343. package/dist/twilight/p-648a4d8d.entry.js +0 -4
  344. package/dist/twilight/p-afc3d17b.entry.js +0 -4
  345. package/dist/twilight/p-bdf47e97.entry.js +0 -4
@@ -1,20 +1,666 @@
1
1
  import { type Offer } from "./interfaces";
2
- import type { Product } from "@salla.sa/twilight/types/common";
2
+ import { type CartFreeGiftItem } from "../../shared/cart-free-gifts";
3
3
  export declare class SallaConditionalOffer {
4
4
  offer: Offer;
5
- products: Record<string, Pick<Product, 'image' | 'url' | 'name' | 'price'>>;
6
5
  isLoading: boolean;
7
6
  canRender: boolean;
7
+ /** Free-gift cart LINES belonging to this offer — the "ادارة العرض" card. */
8
+ gifts: CartFreeGiftItem[];
9
+ /**
10
+ * How many LINES the cart holds — `null` while that is genuinely UNKNOWN.
11
+ *
12
+ * TRI-STATE on purpose, and the whole of the empty-cart fix rests on it. `0` renders
13
+ * NOTHING; `null` renders normally. Collapsing the two would hide the banner on every
14
+ * populated cart for as long as the first `details()` request is in flight, which is
15
+ * the flicker this must not trade one bug for.
16
+ */
17
+ cartItemCount: number | null;
18
+ /** The tooltip is open while hovered OR while pinned by a click (the mobile path). */
19
+ isTooltipHovered: boolean;
20
+ isTooltipPinned: boolean;
21
+ /** "ادارة العرض" — open by default, the gifts card starts expanded. */
22
+ isGiftsOpen: boolean;
23
+ /**
24
+ * The ⓘ TRIGGER's own wrapper — the popover's positioning context, and the element
25
+ * "inside vs outside" is measured against.
26
+ *
27
+ * Deliberately NOT the heading. The heading spans the offer TITLE as well, so
28
+ * anchoring there put the popover's edge at the start of the text and left the caret
29
+ * pointing at a word instead of at the icon that opened it.
30
+ */
31
+ private infoAnchorEl?;
32
+ private infoTriggerEl?;
33
+ /** The popover itself, measured (never re-rendered) by `positionTooltip`. */
34
+ private tooltipEl?;
35
+ /**
36
+ * What makes this instance's `id`s its OWN. The banner can legitimately mount more than
37
+ * once on a page, and with fixed ids the second instance's `aria-describedby` /
38
+ * `aria-controls` resolve to the FIRST one's popover and gifts card.
39
+ */
40
+ private instanceId;
41
+ /**
42
+ * Monotonic generation counter guarding `refresh()`. Two cart mutations in quick succession issue
43
+ * two `offers()` requests, and nothing guarantees they SETTLE in the order they were
44
+ * sent — without this, a slow first response can land after a fast second one and
45
+ * render a `current_value` that is already stale.
46
+ */
47
+ private refreshGeneration;
48
+ /** The same guard for the independent gift-lines lookup. */
49
+ private giftsGeneration;
50
+ /**
51
+ * The open coalescing window: the timer that will run the ONE refresh pass a burst of
52
+ * `cart::updated` events is entitled to. `null` whenever no burst is in flight.
53
+ *
54
+ * The generation guards above are untouched by this and still do their own job — the
55
+ * window collapses events that have not been ASKED about yet, they discard answers to
56
+ * questions that have since been superseded, and a burst that straddles the window
57
+ * (an event arriving while a pass is already in flight) needs both.
58
+ */
59
+ private cartRefreshTimer;
60
+ /**
61
+ * "A burst has been seen and its pass has not run yet."
62
+ *
63
+ * Survives a disconnect on purpose, exactly as `skeletonDeadline` does: a theme that
64
+ * MOVES the host in the DOM mid-burst (disconnect + reconnect, same instance) would
65
+ * otherwise drop the trailing pass and rest on pre-burst data forever, since nothing
66
+ * re-runs `refresh()` but another `cart::updated`. `connectedCallback` re-arms it.
67
+ */
68
+ private cartRefreshPending;
69
+ /**
70
+ * Set once `load()` has asked for `cart::updated`, and never cleared: it is what lets
71
+ * `connectedCallback` tell a RECONNECT (put the listener back) from the first connect,
72
+ * which runs before `componentWillLoad` and must leave the subscription to it.
73
+ */
74
+ private wantsCartUpdates;
75
+ /** Whether `cartUpdatedHandler` is on `cart::updated` RIGHT NOW — the double-registration guard. */
76
+ private isListeningToCartUpdates;
77
+ /** The armed cart-lookup retry; `null` whenever none is. See `CART_LOOKUP_RETRY_DELAYS_MS`. */
78
+ private cartLookupRetryTimer;
79
+ /** How many of those retries this episode has SPENT — the bound. */
80
+ private cartLookupRetries;
81
+ /** "A retry is owed" — survives a disconnect for the same reason `cartRefreshPending` does. */
82
+ private cartLookupRetryPending;
83
+ /**
84
+ * Set by the ONE branch of `refresh()` that returns without asking for the offer: the
85
+ * cart was reported empty.
86
+ *
87
+ * That branch is right whenever the report is, and unrecoverable whenever it is not —
88
+ * nothing re-runs `refresh()` except `cart::updated`, which a shopper who never
89
+ * touches the cart never emits. So the branch now leaves this behind, and the cart
90
+ * lookup it kicks off clears it by re-running `refresh()` if the count comes back
91
+ * `>= 1`. See both sites; the latch is what keeps that to a recovery rather than a
92
+ * second offer request on every load.
93
+ */
94
+ private deferredByEmptyCart;
95
+ /** Set by `disconnectedCallback`, so a late response cannot set state on a dead host. */
96
+ private isDisconnected;
97
+ /** Backstop timer that guarantees the skeleton is never a resting state. */
98
+ private skeletonWatchdog;
99
+ /**
100
+ * WHEN the current skeleton must be gone by — the authority, with `skeletonWatchdog`
101
+ * demoted to a mere handle.
102
+ *
103
+ * A timer handle answers "is a timer pending", which is not the question. The question
104
+ * is "is this skeleton past its deadline", and the two come apart precisely when it
105
+ * matters: a handle that was cleared, consumed, or never armed reads exactly like one
106
+ * that is still counting down. Keeping the deadline as a timestamp means the watchdog
107
+ * can always be REBUILT from state — which is what `ensureSkeletonIsWatched` does, on
108
+ * every render, so a skeleton on screen can never be one nothing is watching.
109
+ *
110
+ * `0` means "not loading"; anything else is an absolute `Date.now()` deadline.
111
+ */
112
+ private skeletonDeadline;
113
+ /** The host element — the thing that gets MOVED; see `relocateIntoItemsColumn`. */
114
+ hostEl: HTMLElement;
115
+ /** Latched once the host sits in the items column, so it can never be moved twice. */
116
+ private hasRelocated;
117
+ /** `componentDidRender` fires BEFORE the first `componentDidLoad`; this orders them. */
118
+ private hasLoaded;
119
+ /** Observer waiting for a column that the parser has not emitted yet. */
120
+ private relocationObserver;
121
+ /** Deadline that bounds that wait. */
122
+ private relocationWatchTimer;
123
+ /** Set when the wait expired: stop re-arming, keep the cheap per-render retry. */
124
+ private relocationWatchExpired;
125
+ /** "No column found" is said ONCE per instance, not on every render. */
126
+ private relocationLogged;
127
+ /**
128
+ * SYNCHRONOUS on purpose — an `async` version of this hook is what made the whole
129
+ * component invisible.
130
+ *
131
+ * Stencil awaits whatever `componentWillLoad` returns BEFORE it calls `render()` for
132
+ * the first time (`dispatchHooks` → `enqueue(maybePromise, () => updateComponent(...))`).
133
+ * An `async componentWillLoad` chaining round trips inside that promise leaves the host
134
+ * element literally empty for the whole chain, and FOREVER if the last of them never
135
+ * settles.
136
+ *
137
+ * Same posture as `salla-cart-summary-card` ("Sync: returning a promise would make
138
+ * Stencil defer first paint"): paint now, fill in as the data lands.
139
+ */
8
140
  componentWillLoad(): void;
9
- private getProducts;
10
- private updateInitialOfferValue;
11
- private updateOfferValues;
141
+ /**
142
+ * Logging that CANNOT throw.
143
+ *
144
+ * `salla.logger.warn` is not safe to call during init: `base/src/logger.ts` opens every
145
+ * call with `if (!salla.config.isDebug()) return;`, so it raises a TypeError whenever
146
+ * `salla.config` has not been installed yet — precisely the window `componentWillLoad`
147
+ * runs in. Every recovery path in this component used to log BEFORE it recovered, so a
148
+ * single throw in here silently skipped the recovery it was announcing.
149
+ */
150
+ private safeLog;
151
+ /**
152
+ * Cleared on RECONNECT, not just set on disconnect. `componentWillLoad` runs once per
153
+ * instance, so an element that a theme moves in the DOM (disconnect + reconnect keeps
154
+ * the same instance) would otherwise stay permanently latched as "dead" and stop
155
+ * updating on every later `cart::updated`.
156
+ */
157
+ connectedCallback(): void;
158
+ disconnectedCallback(): void;
159
+ componentDidLoad(): void;
160
+ /**
161
+ * Whether the host currently puts anything on the page.
162
+ *
163
+ * Gates the relocation: a host that renders nothing contributes no layout and must not
164
+ * be moved around the document either. It becomes true on the render that first paints
165
+ * real content, and `componentDidRender` runs inside the same frame as that render —
166
+ * before the browser paints it — so the move still lands ahead of the first visible
167
+ * pixel and there is no jump.
168
+ */
169
+ private isShowingSomething;
170
+ /**
171
+ * Every later render re-attempts the move. Free once it has happened (one boolean
172
+ * read), and it is what covers the ordinary late-arrival case without any timer: the
173
+ * skeleton paints while the row is still being parsed, and the render that replaces it
174
+ * with the offer runs against a complete DOM.
175
+ */
176
+ componentDidRender(): void;
177
+ /**
178
+ * Move the host into the cart-items column.
179
+ *
180
+ * The theme mounts this component ABOVE the two-column row (`cart.twig`), so it spans
181
+ * the items column AND the order summary; the design puts it above the ITEMS only,
182
+ * with the summary starting level with it. Theme changes are out of scope, so the
183
+ * component places itself.
184
+ *
185
+ * MOVING rather than re-sizing is deliberate. A moved host inherits whatever width the
186
+ * column has — at every breakpoint, in RTL and LTR, and on a resize — and on mobile,
187
+ * where the column is the only column, the move is a no-op visually, which is exactly
188
+ * the mobile layout. A hard-coded `calc(100% - 384px)` is raed's number and would
189
+ * be wrong in any other theme.
190
+ *
191
+ * NEVER throws, and never leaves the component unrendered: a cart whose column cannot
192
+ * be identified keeps the full-width banner. That is a degraded LAYOUT, not a degraded
193
+ * component.
194
+ */
195
+ private relocateIntoItemsColumn;
196
+ /**
197
+ * The cart-items column, found WITHOUT depending on any single theme's class names.
198
+ *
199
+ * From the host OUTWARDS: at each level look at the elements that FOLLOW it — the row
200
+ * is always a later sibling, never an ancestor — and inside each of them look for a
201
+ * cart ITEM row. The column is then that row's own child which contains the item, i.e.
202
+ * precisely the element whose width the banner should inherit.
203
+ *
204
+ * Scoping to following siblings is what stops an unrelated `form[id^="item-"]` (a
205
+ * mini-cart drawer, a footer widget) from being mistaken for the cart.
206
+ */
207
+ private findItemsColumn;
208
+ private getFollowingSiblings;
209
+ /** The child of `row` that holds the cart items, if `row` is the two-column row. */
210
+ private findColumnWithin;
211
+ /**
212
+ * Walk up from `node` to the DIRECT CHILD of `ancestor` that contains it.
213
+ *
214
+ * Not "the parent of the item rows": a theme that nests them (`col > ul > li > form`)
215
+ * would hand back the `<ul>`, and a banner is not a list item. The row's own child is
216
+ * the column in every shape.
217
+ */
218
+ private climbToChildOf;
219
+ private matches;
220
+ /**
221
+ * Wait for a column that does not exist YET, then stop waiting.
222
+ *
223
+ * Bounded on both sides: the observer is disconnected the moment the move succeeds, and
224
+ * a deadline retires it if it never does. Where there is no `MutationObserver` (older
225
+ * browsers, and the spec DOM) this degrades to the per-render retry in
226
+ * `componentDidRender`, which is the path that matters anyway.
227
+ */
228
+ private watchForItemsColumn;
229
+ private stopWatchingForItemsColumn;
230
+ /** Said ONCE per instance: the full-width banner is a layout note, not a per-render alarm. */
231
+ private logNoItemsColumn;
232
+ /**
233
+ * Guarantee that a raised skeleton has a live deadline behind it — idempotent, cheap,
234
+ * and called from EVERY path that can leave one on screen (`startLoading`,
235
+ * `connectedCallback`, and `componentDidRender`).
236
+ *
237
+ * This is the structural answer to "a skeleton with nothing watching it". The old shape
238
+ * armed the timer only inside `startLoading()`, and only when the HANDLE happened to be
239
+ * null — so any path that raised `isLoading` without going through that guard, or that
240
+ * left a stale-looking handle behind, produced a shimmer that nothing would ever end.
241
+ * Re-checking on every render means the invariant is restored by the very act of
242
+ * painting the skeleton, whatever raised it.
243
+ *
244
+ * The deadline is set ONCE per loading episode and never pushed out, so a stream of
245
+ * `cart::updated` events cannot extend a skeleton indefinitely.
246
+ */
247
+ private ensureSkeletonIsWatched;
248
+ private clearSkeletonWatchdog;
249
+ /**
250
+ * The ONLY way INTO the skeleton. Raising it and arming the watchdog that ends it are
251
+ * deliberately the SAME operation — keeping them apart is what let `refresh()` show a
252
+ * skeleton that nothing was watching. The existing deadline is kept rather than
253
+ * refreshed, so a stream of `cart::updated` events cannot push it out indefinitely.
254
+ */
255
+ private startLoading;
256
+ /**
257
+ * The ONLY way out of the skeleton, so the watchdog can never outlive it. Every
258
+ * terminal state — loaded, no-eligible-offer, error — goes through here.
259
+ */
260
+ private stopLoading;
261
+ /**
262
+ * The async half of the load, deliberately NOT awaited by `componentWillLoad` so it
263
+ * can never gate the first paint. Every state it sets is `@State`, so each step
264
+ * re-renders on its own.
265
+ *
266
+ * NOTHING here is allowed to disable the component permanently.
267
+ */
268
+ private load;
269
+ /**
270
+ * `salla.onReady()`, bounded.
271
+ *
272
+ * The previous shape caught a REJECTED bootstrap but could not catch a never-settling
273
+ * one, even though its comment claimed to — and an unbounded await here is the single
274
+ * point at which the whole component can hang with nothing logged. Racing a timer
275
+ * turns "the SDK never signals ready" from fatal into "we ask a few seconds early",
276
+ * which `refresh()` already handles non-terminally and retries on `cart::updated`.
277
+ *
278
+ * Never rejects, and absorbs a synchronous throw from `onReady` itself.
279
+ */
280
+ private waitForSdk;
281
+ /**
282
+ * The `cart::updated` payload is ignored for everything EXCEPT the item count — it may
283
+ * legitimately arrive with no `items` at all (`api.cart.latest` is not in
284
+ * `CartPresenter::$routesWithItems`). Refetch and take the server's `current_value`.
285
+ *
286
+ * `count` is the one field that is safe to read here, and reading it is what fixes the
287
+ * live removal path. `SallaCartEvent.updated()` dispatches `{ ...data.cart }`
288
+ * (twilight/src/event/cart.js:114) and writes `count` from that same object into
289
+ * `cart.summary` one line above it, so the field is first-class — unlike `items`, which
290
+ * genuinely is not. Taking it SYNCHRONOUSLY, before any request goes out, is the point:
291
+ * see `readCartCountFromEvent`.
292
+ *
293
+ * Two independent refetches fan out from here and are NOT chained: the offer payload
294
+ * (thresholds + reward labels) and the gift cart LINES. The gift lookup is shared
295
+ * process-wide (`shared/cart-free-gifts`), so the item-offer badges, the summary card
296
+ * and this banner still cost ONE `details()` request between them per mutation.
297
+ *
298
+ * Both of those refetches are COALESCED — see `CART_UPDATE_COALESCE_MS`. Only the
299
+ * count read below is per-event.
300
+ *
301
+ * ONE reference per instance rather than an inline closure, because `salla.event.off`
302
+ * can only remove the exact function `on` was given.
303
+ */
304
+ private cartUpdatedHandler;
305
+ /**
306
+ * Put `cartUpdatedHandler` on `cart::updated` — at most ONCE, however many paths ask
307
+ * (`load()`, the SDK-ready retry, a reconnect).
308
+ *
309
+ * Never throws, and reports whether the listener is on: `salla.event.on` can throw
310
+ * this early (see `load()`), and the caller decides what a failure is worth.
311
+ */
312
+ private subscribeToCartUpdates;
313
+ /** The other half of `subscribeToCartUpdates` — see `disconnectedCallback`. */
314
+ private unsubscribeFromCartUpdates;
315
+ /**
316
+ * The second attempt for a subscription that threw before the SDK was up.
317
+ *
318
+ * Hung off `salla.onReady()` ITSELF, not off `waitForSdk()`: that wait gives up after
319
+ * `ON_READY_TIMEOUT_MS` so that it cannot hold the first paint, and an SDK that comes
320
+ * up later than that is exactly the one this is for. Floating for the same reason —
321
+ * nothing in `load()` awaits it, so a bootstrap that never settles costs the listener
322
+ * and nothing else.
323
+ */
324
+ private subscribeWhenSdkIsReady;
325
+ /**
326
+ * Open (or extend) the coalescing window, so a burst of `cart::updated` events buys ONE
327
+ * refresh pass instead of two server round trips each. See `CART_UPDATE_COALESCE_MS`.
328
+ *
329
+ * Restarting the timer on every event is what makes this trailing-edge: the pass runs
330
+ * `CART_UPDATE_COALESCE_MS` after the LAST event, never after the first, so the burst
331
+ * can never settle on a cart state the shopper has already moved past.
332
+ */
333
+ private scheduleCartRefresh;
334
+ /**
335
+ * The one pass a burst is entitled to — byte for byte what the `cart::updated` handler
336
+ * used to do inline, minus the count read that now happens per event.
337
+ *
338
+ * The disconnect guard comes FIRST and leaves the flag ALONE, so a host that went away
339
+ * between the last event and the end of its window stays owed the pass rather than
340
+ * silently forfeiting it. Unreachable in practice — `disconnectedCallback` clears the
341
+ * timer — but the two must not disagree about who owes what.
342
+ *
343
+ * Past that, the flag is cleared BEFORE the work, not after: an event that arrives
344
+ * while this pass is in flight must be able to open a NEW window (and it does —
345
+ * `scheduleCartRefresh` sets the flag again), and clearing afterwards would swallow it.
346
+ */
347
+ private runCartRefresh;
348
+ /**
349
+ * Take the cart's item count straight off the `cart::updated` payload.
350
+ *
351
+ * THIS IS THE FIX FOR THE LIVE REMOVAL PATH, and it exists because the request-based
352
+ * count cannot answer there. When the shopper deletes their last line the cart id goes
353
+ * away, and BOTH cart endpoints then reject rather than reporting an empty cart:
354
+ * `details()` resolves its id through `getCurrentCartId(false, …, allowCreate=false)`,
355
+ * which returns `null` for a guest with no stored cart id, and throws `CartNotCreated`
356
+ * (twilight/src/api/cart.js:192-199); `offers()` throws through the same door
357
+ * (cart.js:869-874). `refreshCartFreeGifts()` has meanwhile invalidated the shared
358
+ * cache, so the count resolves to `null` — "unknown" — and an unknown count renders the
359
+ * banner. That is the beta regression: a cart that is empty, reported as unknowable.
360
+ *
361
+ * The payload has none of that dependency. It is delivered synchronously, by the same
362
+ * event that told us the cart changed, and it cannot fail, hang, or lose a race.
363
+ *
364
+ * Two sources, in order: the payload's own `count`, then the `cart.summary.count` the
365
+ * same emitter wrote a line earlier. Both are strict — see `toCartCount` — so a payload
366
+ * that states nothing leaves the previous value untouched rather than reading as empty.
367
+ */
368
+ private readCartCountFromEvent;
369
+ /**
370
+ * The SECONDARY count, for a `cart::updated` that carried no usable `count` of its own.
371
+ *
372
+ * `SallaCartEvent.updated()` writes `cart.summary.count` from `data.cart`
373
+ * (twilight/src/event/cart.js:102-110) SYNCHRONOUSLY, immediately before it dispatches
374
+ * the event — so inside a `cart::updated` handler this value is as fresh as the payload
375
+ * and comes from the same object. `mobileBridge._cart` falls back to exactly this, for
376
+ * exactly this reason.
377
+ *
378
+ * ONLY safe from inside that handler, which is why it is private to
379
+ * `readCartCountFromEvent` and never consulted at mount: on a cold load the same key
380
+ * can hold a count left over from a previous session, and trusting it there would hide
381
+ * the banner on a cart that is not empty.
382
+ *
383
+ * Read through the public `salla.storage` facade; wrapped, because storage is not
384
+ * guaranteed to be installed and a throw here must cost nothing.
385
+ */
386
+ private readCartCountFromStorage;
387
+ /**
388
+ * Coerce a reported count, STRICTLY. Only a finite, non-negative number is an answer;
389
+ * `undefined`, `null`, a string and a negative all mean "not stated", which leaves the
390
+ * previous value alone — the degenerate `{ fromLatestFetched }` payload
391
+ * (event/cart.js:85) must never read as an empty cart.
392
+ */
393
+ private toCartCount;
394
+ private getFreeLabel;
395
+ /**
396
+ * Read a `blocks.cart_offer.*` translation.
397
+ *
398
+ * `salla.lang.get` returns the KEY VERBATIM when a translation is missing, which would
399
+ * put "blocks.cart_offer.congrats" in front of a shopper. A missing key reads as its
400
+ * `EN_FALLBACKS` entry instead, with the same `:placeholder` substitution.
401
+ */
402
+ private trans;
403
+ /**
404
+ * The ONLY eligibility question worth asking: does this store have the feature?
405
+ *
406
+ * `salla.config.get` is a plain object read and cannot throw, but the whole thing is
407
+ * still wrapped: this predicate is called from a lifecycle hook, and "the SDK is not
408
+ * on the page yet" must degrade to "not eligible *yet*", never to an exception.
409
+ */
410
+ private isEnabled;
411
+ /**
412
+ * ONE banner, even when several cart offers are live the FIRST conditional
413
+ * offer carrying at least one checkpoint wins and the rest are ignored. Merging them
414
+ * would produce a badge row whose tiers belong to different promotions.
415
+ */
416
+ private fetchOffer;
417
+ /**
418
+ * The single entry point for "ask the server what this cart is entitled to".
419
+ *
420
+ * Runs on the initial load AND on every `cart::updated`, and is IDEMPOTENT and
421
+ * REVERSIBLE in both directions — `canRender` is derived from this pass alone, so a
422
+ * cart that becomes eligible after starting out ineligible turns the component back
423
+ * on. No state set here is a one-way door.
424
+ */
425
+ private refresh;
426
+ /**
427
+ * The single gate on rendering anything at all: an AFFIRMATIVE "the cart holds at
428
+ * least one item".
429
+ *
430
+ * Deliberately not `!== 0` and not a truthy test. `cartItemCount` is `null` until
431
+ * something answers, and `null` must read as "do not render" — the default is hidden,
432
+ * and only a real, non-negative count of 1 or more opens the gate.
433
+ */
434
+ private hasCartItems;
435
+ /**
436
+ * Whether this pass's response should be discarded: a newer `refresh()` has since
437
+ * been issued (so this one describes an older cart), or the host is gone. Answers
438
+ * "whose data wins" ONLY — never "should the skeleton stay up"; see the `finally`
439
+ * in `refresh()`.
440
+ */
441
+ private isSuperseded;
442
+ /**
443
+ * The two things this component needs from the CART itself: how many lines it holds,
444
+ * and which of them are this offer's free gifts.
445
+ *
446
+ * ONE lookup for both. `getCartItemCount` and `getCartFreeGiftItems` are two
447
+ * projections of the same shared `details()` response and share its cache, its
448
+ * in-flight promise and its generation guard, so asking for both costs exactly one
449
+ * request — and, more importantly, the count and the gift lines can never describe
450
+ * two different cart states.
451
+ *
452
+ * Never rejects: a failing lookup is a missing "ادارة العرض" card and an UNKNOWN
453
+ * count, not a missing promotion and not an empty cart.
454
+ */
455
+ private loadCart;
456
+ /**
457
+ * Arm the next cart-lookup retry, unless one is already armed or the episode has spent
458
+ * its attempts. See `CART_LOOKUP_RETRY_DELAYS_MS`.
459
+ *
460
+ * The attempt is SPENT when the timer fires, not when it is armed, so a timer a
461
+ * disconnect cleared is re-armed by `connectedCallback` without costing one.
462
+ */
463
+ private scheduleCartLookupRetry;
464
+ /**
465
+ * End the retry episode: a lookup answered, or the cart changed under it. The count
466
+ * goes back to zero so a LATER failure gets its own full set of attempts.
467
+ */
468
+ private clearCartLookupRetry;
469
+ /**
470
+ * Keep only the gift lines this offer granted.
471
+ *
472
+ * `CartItemTransformer::getFreeGiftData` emits `offer_id` OPTIMUS-ENCODED, exactly
473
+ * like `CartOffersTransformer` emits `id`, so the two join directly. When the offer id
474
+ * is unreadable we fall back to every gift line rather than hiding the card: only one
475
+ * cart offer can be applied to a cart at a time, so an unmatched gift is far more
476
+ * likely to be an id-shape surprise than another promotion's product.
477
+ */
478
+ private selectOwnGifts;
479
+ /**
480
+ * Stages in ascending threshold order — the payload itself is UNSORTED.
481
+ *
482
+ * LOAD-BEARING: this filter and sort must mirror what `computeOfferStageProgress`
483
+ * does internally, because every renderer pairs `stages[i]` with
484
+ * `progress.thresholds[i]`. Both drop unusable thresholds via `isUsableThreshold` and
485
+ * both sort ascending and stably, so the two arrays stay index-aligned.
486
+ */
487
+ private getStages;
488
+ /**
489
+ * The tier picture: the ENGINE'S verdict per tier plus the threshold projection.
490
+ *
491
+ * `is_applied` is passed positionally from THIS array, so the flag can never be
492
+ * attributed to the wrong tier — `stages[i]`, `progress.thresholds[i]`,
493
+ * `progress.met[i]` and `progress.applied[i]` are one row. Alignment matters more
494
+ * than ever now: the winner's INDEX decides where the ✓ run stops.
495
+ *
496
+ * Each tier's OWN `current_value` rides along the same way. It is what the distance in
497
+ * the guidance line is measured against, and a tier that carries none — every tier, on
498
+ * an older backend — is measured by the offer-level value exactly as before.
499
+ */
500
+ private getProgress;
501
+ /**
502
+ * A tier's render key. `id` is the stable identity across refetches; the array
503
+ * position is the last resort for a backend that does not send one.
504
+ */
505
+ private tierKey;
506
+ /**
507
+ * How this offer MEASURES its thresholds, as `offer-stages` names it .
508
+ *
509
+ * The remainder is rounded to the measure — whole products for `products_count`,
510
+ * store currency precision for everything else — so `formatRequirement` is handed a
511
+ * value its formatter can actually render. A `products_count` offer is the one that
512
+ * MUST be told apart: `salla.lang.choice` matches a fraction against no plural
513
+ * interval and answers "no products".
514
+ */
515
+ private stageMeasure;
516
+ /**
517
+ * Format a cart value (a threshold, or a remaining amount/quantity) the way the
518
+ * offer is measured: money for `order_amount`, a pluralised item count for
519
+ * `products_count`. May return HTML (`salla.money`), so callers use `innerHTML`.
520
+ */
521
+ private formatRequirement;
522
+ /**
523
+ * The shopper-facing reward text, SERVER-RENDERED.
524
+ *
525
+ * Never re-derived here. `reward_label` already carries the type-specific wording,
526
+ * the currency the value was converted to, and the merchant's own theme-translation
527
+ * overrides. Empty (free_shipping, unknown types) means "render nothing", which is
528
+ * why every caller has a reward-less sentence to fall back to.
529
+ */
530
+ private getRewardText;
531
+ /**
532
+ * Guidance toward the first tier ABOVE the one the engine granted. Empty once
533
+ * that is the last tier (then the congratulations line renders instead).
534
+ *
535
+ * The TARGET comes from the verdict (`nextUnmetIndex`), the DISTANCE from the
536
+ * arithmetic — which is legitimate here, because "add :remaining" is a projection
537
+ * about the shopper's cart, not a claim about what the engine did. It is measured in
538
+ * the TARGET tier's own `current_value` when the backend sends one (see `getProgress`).
539
+ *
540
+ * Without that the two can disagree: the engine can withhold a tier whose threshold is
541
+ * already met, because its own excluded-product set is not in the payload. Then the distance
542
+ * is 0 and "Add SAR 0 and get a free gift" is false in both halves, so the line is
543
+ * dropped rather than rendered — and we do NOT skip ahead to a farther tier, which
544
+ * would advertise a reward past a nearer one the shopper cannot have. Nothing is the
545
+ * honest answer; the tooltip still shows the tier as locked.
546
+ */
547
+ private getGuidanceMessage;
548
+ /** One tooltip row's sentence: "spend X, get Y", both halves from the offer payload. */
549
+ private getTierSentence;
550
+ /**
551
+ * The reward-TYPE icon shown in a tooltip row's circle.
552
+ *
553
+ * `fixed` is the awkward one: the design draws the Saudi Riyal glyph, but a store
554
+ * selling in USD must not. `sicon-sar` is used on exactly the same condition
555
+ * `salla.money()` uses to swap the currency for that glyph
556
+ * (`base/src/helpers/numbers.ts:78-86`), and a currency-neutral banknote otherwise.
557
+ */
558
+ private getRewardIcon;
559
+ private usesSarSymbol;
560
+ private get isTooltipOpen();
561
+ private toggleTooltip;
562
+ private closeTooltip;
563
+ /**
564
+ * WHY THIS IS NOT `salla-tooltip`.
565
+ *
566
+ * It is a fair question — the component exists, it is shared, and this component used
567
+ * to depend on it (master renders a `<salla-tooltip theme="dark">` product preview off
568
+ * the badge avatars). That usage was hover-only, non-interactive and string-sized, and
569
+ * it is exactly the shape `salla-tooltip` is built for. The ⓘ popover this design asks
570
+ * for is not, and the gap is not one prop wide:
571
+ *
572
+ * - VISIBILITY IS PRIVATE TO IT. `show` is `@State`, flipped only by the component's
573
+ * own `mouseover`/`mouseout` window listeners (salla-tooltip.tsx:26-38). There is no
574
+ * `open` prop and no way for an owner to pin it, so the CLICK path — which is the
575
+ * ONLY way this popover opens on a touch device — cannot be expressed at all.
576
+ * - IT IS DELIBERATELY UNREACHABLE. `pointer-events: none` (salla-tooltip.scss:20).
577
+ * A pinned panel the shopper cannot hover or scroll is not a mobile path.
578
+ * - NO DISMISSAL MODEL. Escape and outside-click do not exist there, because with
579
+ * hover-only visibility there is nothing to dismiss.
580
+ * - NO POSITIONING TO REUSE. It is pinned by two hand-tuned magic percentages —
581
+ * `translateX(-63%)` and `translateX(-72%)` under `:dir(ltr)` — with a caret nailed
582
+ * to `left: 50%` and never re-aimed. There is no viewport clamp, so the exact
583
+ * behaviour this method exists for is the one piece it does not have.
584
+ * - IT IS SIZED FOR A STRING. `max-width: 150px` / `width: 190px` default,
585
+ * `max-width: 330px` dark. This panel is a title, a description and one row per
586
+ * tier.
587
+ * - IT ANCHORS BY GLOBAL ID. `document.getElementById(targetId)` at `componentDidLoad`
588
+ * (salla-tooltip.tsx:40-43). The ⓘ trigger has no stable document-unique id, and the
589
+ * banner can legitimately mount more than once on a page.
590
+ *
591
+ * Closing all six is not "a prop, not a hack" — it is a controlled-visibility API, a
592
+ * dismissal model, an anchoring change and a positioning engine, i.e. a rewrite of all
593
+ * 55 lines of it. And `salla-tooltip` is SHARED: `salla-comment-item` and `salla-wallet`
594
+ * both pass a plain `text` + `targetId` and depend on precisely the hover-only,
595
+ * `pointer-events: none`, magic-percentage behaviour above, with a single spec between
596
+ * them that asserts its exact rendered HTML. Rewriting it under them to serve this
597
+ * banner trades a scoped 90 lines here for a silent regression surface there.
598
+ *
599
+ * So the bespoke implementation stays, and stays SMALL: everything below is the clamp
600
+ * and the caret. The resting position is plain CSS.
601
+ *
602
+ * Keep the popover pointing at the ⓘ icon AND inside the viewport.
603
+ *
604
+ * The CSS already centres it on the trigger — that is the resting answer, and it is
605
+ * correct in RTL and LTR alike because centring is symmetric, which is exactly why the
606
+ * rules are written with physical `left`/`translateX` rather than `inset-inline-*`.
607
+ * Anything logical would have to be mirrored again here, in a coordinate space
608
+ * (`getBoundingClientRect`) that is physical no matter what the document direction is.
609
+ *
610
+ * All this adds on top is the CLAMP: at narrow widths a popover centred on an icon
611
+ * that sits near the edge of the screen would hang off it, so the box is nudged back
612
+ * inside and the caret is shifted by the same amount in the opposite direction, so it
613
+ * stays over the icon while the box moves out from under it.
614
+ *
615
+ * Written as CSS CUSTOM PROPERTIES on the element, never as `@State`: this runs from
616
+ * `componentDidRender`, and anything that re-entered render() from here would loop.
617
+ */
618
+ private positionTooltip;
619
+ private setTooltipOffsets;
620
+ /**
621
+ * A resize changes which edge the popover is up against, so the clamp has to be
622
+ * re-derived. Cheap: `positionTooltip` returns on its first line unless the popover is
623
+ * actually open.
624
+ */
625
+ handleWindowResize(): void;
626
+ handleDocumentKeydown(event: KeyboardEvent): void;
627
+ /**
628
+ * Outside click dismisses a PINNED tooltip. `composedPath` rather than
629
+ * `event.target`, so a click on a slotted/nested element still resolves to the
630
+ * anchor; `contains` is the fallback for the (test) environments that lack it.
631
+ */
632
+ handleDocumentClick(event: MouseEvent): void;
633
+ /**
634
+ * The `id` of one of this instance's own elements. EVERY id and every reference to it
635
+ * (`aria-describedby`, `aria-controls`) goes through here, so the two cannot drift.
636
+ */
637
+ private elementId;
638
+ /**
639
+ * The badge row: one small circle per tier, joined by short connectors.
640
+ *
641
+ * Tier count is VARIABLE, so the row is a nowrap flex line that scrolls
642
+ * horizontally inside a bounded width instead of squeezing the message beside it.
643
+ * RTL correctness comes from the markup: a plain flex row inherits the document's
644
+ * `dir`, so tier 1 lands on the leading edge and the scroll origin follows it in both
645
+ * directions. Do not add physical left/right offsets here.
646
+ */
647
+ private renderBadges;
648
+ /**
649
+ * Spec §4 — the ⓘ popover: title, the merchant's offer message, then one row per tier.
650
+ *
651
+ * The prose is `details.rules`, NEVER `offer.description` — see `readOfferRules`.
652
+ */
12
653
  private renderTooltip;
13
- private getCheckpointContent;
14
- private getOfferType;
15
- private clamp;
16
- private mapValueRanges;
17
- private getCheckPointView;
654
+ /**
655
+ * "ادارة العرض". Rendered TWICE with complementary responsive visibility rather than
656
+ * moved with `order:`: desktop wants it inline with the banner row, mobile wants a
657
+ * full-width bar BELOW the gifts card, and those are different places
658
+ * in the flow, not a different order within one flex line.
659
+ */
660
+ private renderGiftsToggle;
661
+ /** Spec §5 — the gifts card: one read-only row per gift line. */
662
+ private renderGiftsCard;
663
+ private renderGiftRow;
18
664
  private getLoadingSkeletonView;
19
665
  render(): any;
20
666
  }