@salla.sa/twilight-theme-engine 1.0.85 → 1.0.86

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 (372) hide show
  1. package/dist/{AddToCartForm-B5C2M4OZ.js → AddToCartForm-5X2WZDWR.js} +3 -3
  2. package/dist/{AddToCartForm-B5C2M4OZ.js.map → AddToCartForm-5X2WZDWR.js.map} +1 -1
  3. package/dist/{BlogAuthorPage-GTF4UGSS.js → BlogAuthorPage-LO7FSSJR.js} +12 -10
  4. package/dist/BlogAuthorPage-LO7FSSJR.js.map +1 -0
  5. package/dist/{BlogCategoryPage-TN7Y4NBP.js → BlogCategoryPage-KKFQ3PGR.js} +12 -10
  6. package/dist/BlogCategoryPage-KKFQ3PGR.js.map +1 -0
  7. package/dist/{BlogPage-TDB3M3PI.js → BlogPage-VLF3NSGE.js} +11 -9
  8. package/dist/BlogPage-VLF3NSGE.js.map +1 -0
  9. package/dist/{BlogSinglePage-4A52Q5QW.js → BlogSinglePage-AVK535IW.js} +11 -9
  10. package/dist/BlogSinglePage-AVK535IW.js.map +1 -0
  11. package/dist/{BlogTagPage-TOVPBETA.js → BlogTagPage-F3T4D7RH.js} +12 -10
  12. package/dist/BlogTagPage-F3T4D7RH.js.map +1 -0
  13. package/dist/{BrandsPage-VUDGYSQI.js → BrandsPage-LVSBZSVC.js} +11 -9
  14. package/dist/BrandsPage-LVSBZSVC.js.map +1 -0
  15. package/dist/{Breadcrumb-CI3SN5YR.js → Breadcrumb-VLFTXRFC.js} +11 -9
  16. package/dist/Breadcrumb-VLFTXRFC.js.map +1 -0
  17. package/dist/{CartItem-BHLEURAB.js → CartItem-G3H34GW6.js} +11 -9
  18. package/dist/CartItem-G3H34GW6.js.map +1 -0
  19. package/dist/{CartSummary-QERVESYN.js → CartSummary-VH37RPUI.js} +11 -9
  20. package/dist/CartSummary-VH37RPUI.js.map +1 -0
  21. package/dist/{Copyright-RL6ALKRC.js → Copyright-HE5ZWINZ.js} +3 -3
  22. package/dist/{Copyright-RL6ALKRC.js.map → Copyright-HE5ZWINZ.js.map} +1 -1
  23. package/dist/{CustomerLayout-VRZUWGQK.js → CustomerLayout-LKBLOAVW.js} +11 -9
  24. package/dist/CustomerLayout-LKBLOAVW.js.map +1 -0
  25. package/dist/{Footer-ZWBZCMDL.js → Footer-HHXAST72.js} +5 -5
  26. package/dist/{Footer-ZWBZCMDL.js.map → Footer-HHXAST72.js.map} +1 -1
  27. package/dist/{Header-WNK4O66B.js → Header-GEVZM2YG.js} +8 -6
  28. package/dist/{Header-WNK4O66B.js.map → Header-GEVZM2YG.js.map} +1 -1
  29. package/dist/{LoyaltyPage-CGTHWJ76.js → LoyaltyPage-M5CT3CFD.js} +11 -9
  30. package/dist/LoyaltyPage-M5CT3CFD.js.map +1 -0
  31. package/dist/{LoyaltyPointsPage-QSU6K3PW.js → LoyaltyPointsPage-SDJB52U6.js} +10 -3
  32. package/dist/LoyaltyPointsPage-SDJB52U6.js.map +1 -0
  33. package/dist/{MainMenu-V3T7F32R.js → MainMenu-I7EYS752.js} +3 -3
  34. package/dist/{MainMenu-V3T7F32R.js.map → MainMenu-I7EYS752.js.map} +1 -1
  35. package/dist/{MasterLayout-XQF644LD.js → MasterLayout-QDK4T3NE.js} +11 -9
  36. package/dist/{MasterLayout-XQF644LD.js.map → MasterLayout-QDK4T3NE.js.map} +1 -1
  37. package/dist/{MobileMenu-U4BVQYKH.js → MobileMenu-7VTCWAVJ.js} +11 -9
  38. package/dist/MobileMenu-7VTCWAVJ.js.map +1 -0
  39. package/dist/{NotificationsPage-ZRRQY7TF.js → NotificationsPage-QCABE6QD.js} +11 -9
  40. package/dist/NotificationsPage-QCABE6QD.js.map +1 -0
  41. package/dist/{OrderSinglePage-O3XYVMG5.js → OrderSinglePage-2HVBKBG4.js} +11 -9
  42. package/dist/OrderSinglePage-2HVBKBG4.js.map +1 -0
  43. package/dist/{OrdersPage-CMXZF5MI.js → OrdersPage-5MNCPCM3.js} +11 -9
  44. package/dist/OrdersPage-5MNCPCM3.js.map +1 -0
  45. package/dist/{PageSingle-IIG2US43.js → PageSingle-EN5XIM2B.js} +3 -3
  46. package/dist/{PageSingle-IIG2US43.js.map → PageSingle-EN5XIM2B.js.map} +1 -1
  47. package/dist/{ProductDetails-65GDABI7.js → ProductDetails-UEWQPPDZ.js} +13 -6
  48. package/dist/ProductDetails-UEWQPPDZ.js.map +1 -0
  49. package/dist/{ProfilePage-SNJ4WXU4.js → ProfilePage-TZSU3GHZ.js} +11 -9
  50. package/dist/ProfilePage-TZSU3GHZ.js.map +1 -0
  51. package/dist/{ReviewsPage-UT7UT55J.js → ReviewsPage-WYZ6CHF2.js} +3 -3
  52. package/dist/{ReviewsPage-UT7UT55J.js.map → ReviewsPage-WYZ6CHF2.js.map} +1 -1
  53. package/dist/{SeoCartWidget-DBJAXJKT.js → SeoCartWidget-5JR5RXQM.js} +11 -9
  54. package/dist/SeoCartWidget-5JR5RXQM.js.map +1 -0
  55. package/dist/{SettingsPage-FLTIOPU5.js → SettingsPage-DIDRQXMM.js} +11 -9
  56. package/dist/SettingsPage-DIDRQXMM.js.map +1 -0
  57. package/dist/{StoreClosedNotification-ZT3QKDKS.js → StoreClosedNotification-C2G32E34.js} +12 -10
  58. package/dist/{StoreClosedNotification-ZT3QKDKS.js.map → StoreClosedNotification-C2G32E34.js.map} +1 -1
  59. package/dist/{TestimonialsPage-CU7HKU4O.js → TestimonialsPage-XPHFRBZ7.js} +3 -3
  60. package/dist/{TestimonialsPage-CU7HKU4O.js.map → TestimonialsPage-XPHFRBZ7.js.map} +1 -1
  61. package/dist/{ThankYouPage-5X6B4X3T.js → ThankYouPage-UA6YYFB2.js} +11 -9
  62. package/dist/ThankYouPage-UA6YYFB2.js.map +1 -0
  63. package/dist/{WalletPage-H2BKIDPA.js → WalletPage-AJVV7LVL.js} +11 -9
  64. package/dist/WalletPage-AJVV7LVL.js.map +1 -0
  65. package/dist/{WishlistPage-RS5UCIQ3.js → WishlistPage-UW6LJLSW.js} +11 -9
  66. package/dist/WishlistPage-UW6LJLSW.js.map +1 -0
  67. package/dist/api/advertisement.d.ts +33 -0
  68. package/dist/api/advertisement.d.ts.map +1 -0
  69. package/dist/api/advertisement.js +8 -0
  70. package/dist/api/advertisement.js.map +1 -0
  71. package/dist/boot/critical-style.d.ts +3 -0
  72. package/dist/boot/critical-style.d.ts.map +1 -0
  73. package/dist/boot/index.d.ts +15 -0
  74. package/dist/boot/index.d.ts.map +1 -0
  75. package/dist/boot.d.ts +1 -0
  76. package/dist/boot.js +69 -0
  77. package/dist/boot.js.map +1 -0
  78. package/dist/{chunk-SE5JYWOK.js → chunk-2HCCIHU7.js} +4 -4
  79. package/dist/{chunk-SE5JYWOK.js.map → chunk-2HCCIHU7.js.map} +1 -1
  80. package/dist/chunk-4FJGZQD3.js +6 -0
  81. package/dist/chunk-4FJGZQD3.js.map +1 -0
  82. package/dist/{chunk-SDFDS4Q3.js → chunk-4WVJJ7SN.js} +20 -4
  83. package/dist/chunk-4WVJJ7SN.js.map +1 -0
  84. package/dist/{chunk-NXQJA7D3.js → chunk-BAYGWTNM.js} +3 -3
  85. package/dist/{chunk-NXQJA7D3.js.map → chunk-BAYGWTNM.js.map} +1 -1
  86. package/dist/{chunk-OWX2CPNQ.js → chunk-CLBFF2CY.js} +3 -3
  87. package/dist/{chunk-OWX2CPNQ.js.map → chunk-CLBFF2CY.js.map} +1 -1
  88. package/dist/chunk-DBW7XXNE.js +103 -0
  89. package/dist/chunk-DBW7XXNE.js.map +1 -0
  90. package/dist/{chunk-KHBSGD2D.js → chunk-DBXH3EMJ.js} +3 -3
  91. package/dist/{chunk-KHBSGD2D.js.map → chunk-DBXH3EMJ.js.map} +1 -1
  92. package/dist/{chunk-JRNH3FUF.js → chunk-E26YG3AO.js} +4 -4
  93. package/dist/{chunk-JRNH3FUF.js.map → chunk-E26YG3AO.js.map} +1 -1
  94. package/dist/{chunk-22SH7OVF.js → chunk-E7WWO3V3.js} +3 -3
  95. package/dist/{chunk-22SH7OVF.js.map → chunk-E7WWO3V3.js.map} +1 -1
  96. package/dist/{chunk-UJKFBZFJ.js → chunk-HQ7WA37L.js} +7 -7
  97. package/dist/{chunk-UJKFBZFJ.js.map → chunk-HQ7WA37L.js.map} +1 -1
  98. package/dist/chunk-IVBOLREE.js +29 -0
  99. package/dist/chunk-IVBOLREE.js.map +1 -0
  100. package/dist/{chunk-6SWDSRIL.js → chunk-KP3VYG4F.js} +6 -6
  101. package/dist/{chunk-6SWDSRIL.js.map → chunk-KP3VYG4F.js.map} +1 -1
  102. package/dist/{chunk-4CZRUAZB.js → chunk-PWTXR4RR.js} +10 -10
  103. package/dist/{chunk-4CZRUAZB.js.map → chunk-PWTXR4RR.js.map} +1 -1
  104. package/dist/chunk-QJYUTRAQ.js +14 -0
  105. package/dist/{chunk-7WAXN2PP.js.map → chunk-QJYUTRAQ.js.map} +1 -1
  106. package/dist/{chunk-JO7JWV34.js → chunk-U55ZJC2X.js} +4 -4
  107. package/dist/{chunk-JO7JWV34.js.map → chunk-U55ZJC2X.js.map} +1 -1
  108. package/dist/{chunk-SX5MF2YA.js → chunk-WSDG4ZRI.js} +4 -4
  109. package/dist/{chunk-SX5MF2YA.js.map → chunk-WSDG4ZRI.js.map} +1 -1
  110. package/dist/{chunk-NZVR4OF7.js → chunk-X54QP332.js} +8 -8
  111. package/dist/chunk-X54QP332.js.map +1 -0
  112. package/dist/components/cart/index.js +1 -1
  113. package/dist/components/common/index.js +1 -1
  114. package/dist/components/drawer/index.js +9 -7
  115. package/dist/components/home/index.js +11 -9
  116. package/dist/components/layout/PageAdvertisements.d.ts +7 -0
  117. package/dist/components/layout/PageAdvertisements.d.ts.map +1 -0
  118. package/dist/components/layout/index.d.ts +1 -0
  119. package/dist/components/layout/index.d.ts.map +1 -1
  120. package/dist/components/layout/index.js +8 -1
  121. package/dist/components/product/index.js +10 -8
  122. package/dist/components/store/index.js +11 -9
  123. package/dist/components/store/index.js.map +1 -1
  124. package/dist/{document-class-67HTP45G.js → document-class-7AXAAWGZ.js} +6 -5
  125. package/dist/{document-class-67HTP45G.js.map → document-class-7AXAAWGZ.js.map} +1 -1
  126. package/dist/hooks/index.js +9 -7
  127. package/dist/hooks/useGtm.js +9 -7
  128. package/dist/hooks/useMoney.js +9 -7
  129. package/dist/hooks/useStore.js +9 -7
  130. package/dist/index.js +9 -7
  131. package/dist/index.js.map +1 -1
  132. package/dist/{not-found-QTHBFPNT.js → not-found-3EEMQZMU.js} +11 -9
  133. package/dist/not-found-3EEMQZMU.js.map +1 -0
  134. package/dist/providers/index.js +9 -7
  135. package/dist/routes/account/orders.js +1 -1
  136. package/dist/routes/account.js +6 -6
  137. package/dist/routes/blog.js +5 -5
  138. package/dist/routes/brands.js +1 -1
  139. package/dist/routes/cart.js +13 -11
  140. package/dist/routes/cart.js.map +1 -1
  141. package/dist/routes/home.js +12 -10
  142. package/dist/routes/loyalty.js +2 -2
  143. package/dist/routes/page.js +1 -1
  144. package/dist/routes/product-listing.js +11 -9
  145. package/dist/routes/product-listing.js.map +1 -1
  146. package/dist/routes/product.js +11 -9
  147. package/dist/routes/product.js.map +1 -1
  148. package/dist/routes/root/beforeLoad.d.ts +2 -2
  149. package/dist/routes/root/beforeLoad.d.ts.map +1 -1
  150. package/dist/routes/root/head.d.ts.map +1 -1
  151. package/dist/routes/testimonials.js +2 -2
  152. package/dist/routes/thank-you.js +1 -1
  153. package/dist/skeleton.js +12 -10
  154. package/dist/tanstack/document-class.d.ts.map +1 -1
  155. package/dist/tanstack/index.d.ts +1 -0
  156. package/dist/tanstack/index.d.ts.map +1 -1
  157. package/dist/tanstack/stylesheet.d.ts +15 -0
  158. package/dist/tanstack/stylesheet.d.ts.map +1 -0
  159. package/dist/tanstack.js +34 -5
  160. package/dist/tanstack.js.map +1 -1
  161. package/dist/utils/index.js +1 -1
  162. package/dist/vite/index.js +1 -1
  163. package/package.json +12 -3
  164. package/tailwind/components/Component.tsx.txt +21 -0
  165. package/tailwind/components/ComponentRegistry.ts.txt +66 -0
  166. package/tailwind/components/cart/CartItem.tsx.txt +211 -0
  167. package/tailwind/components/cart/CartSummary.tsx.txt +269 -0
  168. package/tailwind/components/cart/PreOrderNotification.tsx.txt +16 -0
  169. package/tailwind/components/cart/PriceQuoteButton.tsx.txt +21 -0
  170. package/tailwind/components/cart/SeoCartWidget.tsx.txt +99 -0
  171. package/tailwind/components/cart/index.ts.txt +11 -0
  172. package/tailwind/components/cart/lazy.ts.txt +15 -0
  173. package/tailwind/components/collapse/Collapse.tsx.txt +95 -0
  174. package/tailwind/components/collapse/index.ts.txt +41 -0
  175. package/tailwind/components/common/Breadcrumb.tsx.txt +27 -0
  176. package/tailwind/components/common/BreadcrumbJsonLd.tsx.txt +40 -0
  177. package/tailwind/components/common/BreadcrumbTrail.tsx.txt +97 -0
  178. package/tailwind/components/common/CurrencySymbol.tsx.txt +8 -0
  179. package/tailwind/components/common/DeferredData.tsx.txt +122 -0
  180. package/tailwind/components/common/ErrorPage.tsx.txt +69 -0
  181. package/tailwind/components/common/Image.tsx.txt +119 -0
  182. package/tailwind/components/common/Link.tsx.txt +36 -0
  183. package/tailwind/components/common/Link.types.ts.txt +85 -0
  184. package/tailwind/components/common/NoContent.tsx.txt +27 -0
  185. package/tailwind/components/common/RenderWhenVisible.tsx.txt +92 -0
  186. package/tailwind/components/common/index.ts.txt +42 -0
  187. package/tailwind/components/common/lazy.ts.txt +28 -0
  188. package/tailwind/components/defineComponent.ts.txt +32 -0
  189. package/tailwind/components/drawer/Drawer.tsx.txt +219 -0
  190. package/tailwind/components/drawer/index.ts.txt +38 -0
  191. package/tailwind/components/dropdown/Dropdown.tsx.txt +166 -0
  192. package/tailwind/components/dropdown/index.ts.txt +40 -0
  193. package/tailwind/components/home/BundleComponent.tsx.txt +35 -0
  194. package/tailwind/components/home/ComponentErrorBoundary.tsx.txt +98 -0
  195. package/tailwind/components/home/FeaturedProductsStyle1.tsx.txt +125 -0
  196. package/tailwind/components/home/FeaturedProductsStyle2.tsx.txt +127 -0
  197. package/tailwind/components/home/FeaturedProductsStyle3.tsx.txt +71 -0
  198. package/tailwind/components/home/FixedBanner.tsx.txt +68 -0
  199. package/tailwind/components/home/FixedProducts.tsx.txt +72 -0
  200. package/tailwind/components/home/HomePageRenderer.tsx.txt +284 -0
  201. package/tailwind/components/home/PageBlocks.tsx.txt +24 -0
  202. package/tailwind/components/home/ParallaxBackground.tsx.txt +50 -0
  203. package/tailwind/components/home/PhotosSlider.tsx.txt +61 -0
  204. package/tailwind/components/home/ProductsSlider.tsx.txt +87 -0
  205. package/tailwind/components/home/ReorderProducts.tsx.txt +38 -0
  206. package/tailwind/components/home/SquarePhotos.tsx.txt +68 -0
  207. package/tailwind/components/home/StoreFeatures.tsx.txt +34 -0
  208. package/tailwind/components/home/Testimonials.tsx.txt +36 -0
  209. package/tailwind/components/home/Youtube.tsx.txt +48 -0
  210. package/tailwind/components/home/index.ts.txt +50 -0
  211. package/tailwind/components/home/lazy.ts.txt +17 -0
  212. package/tailwind/components/home/register.ts.txt +44 -0
  213. package/tailwind/components/home/reorder-hooks.ts.txt +52 -0
  214. package/tailwind/components/home/types.ts.txt +4 -0
  215. package/tailwind/components/layout/Copyright.tsx.txt +23 -0
  216. package/tailwind/components/layout/CustomerLayout.tsx.txt +91 -0
  217. package/tailwind/components/layout/Footer.tsx.txt +343 -0
  218. package/tailwind/components/layout/Header.tsx.txt +154 -0
  219. package/tailwind/components/layout/MasterLayout.tsx.txt +63 -0
  220. package/tailwind/components/layout/PageAdvertisements.tsx.txt +131 -0
  221. package/tailwind/components/layout/WidgetHead.tsx.txt +53 -0
  222. package/tailwind/components/layout/index.ts.txt +27 -0
  223. package/tailwind/components/layout/lazy.ts.txt +25 -0
  224. package/tailwind/components/layout/types.ts.txt +11 -0
  225. package/tailwind/components/modal/ImageModal.tsx.txt +70 -0
  226. package/tailwind/components/modal/Modal.tsx.txt +245 -0
  227. package/tailwind/components/modal/index.ts.txt +39 -0
  228. package/tailwind/components/modal/lazy.ts.txt +10 -0
  229. package/tailwind/components/navigation/MainMenu.tsx.txt +94 -0
  230. package/tailwind/components/navigation/MobileMenu.tsx.txt +160 -0
  231. package/tailwind/components/navigation/index.ts.txt +4 -0
  232. package/tailwind/components/navigation/lazy.ts.txt +11 -0
  233. package/tailwind/components/product/AddToCartForm.tsx.txt +365 -0
  234. package/tailwind/components/product/ProductCard.tsx.txt +305 -0
  235. package/tailwind/components/product/ProductDetails.tsx.txt +267 -0
  236. package/tailwind/components/product/ProductGallery.tsx.txt +234 -0
  237. package/tailwind/components/product/index.ts.txt +15 -0
  238. package/tailwind/components/product/lazy.ts.txt +9 -0
  239. package/tailwind/components/snippets/AppsSnippets.tsx.txt +225 -0
  240. package/tailwind/components/snippets/SallaSdkScripts.tsx.txt +14 -0
  241. package/tailwind/components/snippets/index.ts.txt +4 -0
  242. package/tailwind/components/store/StoreClosedNotification.tsx.txt +73 -0
  243. package/tailwind/components/store/index.ts.txt +8 -0
  244. package/tailwind/components/store/lazy.ts.txt +8 -0
  245. package/tailwind/components/toast/Toaster.tsx.txt +177 -0
  246. package/tailwind/components/toast/index.ts.txt +10 -0
  247. package/tailwind/components/toast/types.ts.txt +38 -0
  248. package/tailwind/components/toast/useToast.ts.txt +61 -0
  249. package/tailwind/components/tracking/GoogleTag.tsx.txt +77 -0
  250. package/tailwind/components/tracking/SiftSnippet.tsx.txt +49 -0
  251. package/tailwind/components/tracking/TwilightBundles.tsx.txt +67 -0
  252. package/tailwind/components/tracking/index.ts.txt +11 -0
  253. package/tailwind/components/tracking/lazy.ts.txt +11 -0
  254. package/tailwind/components/useComponent.ts.txt +12 -0
  255. package/tailwind/routes/account/index.tsx.txt +16 -0
  256. package/tailwind/routes/account/notifications/NotificationsPage.tsx.txt +54 -0
  257. package/tailwind/routes/account/notifications/index.tsx.txt +39 -0
  258. package/tailwind/routes/account/notifications/loader.ts.txt +22 -0
  259. package/tailwind/routes/account/notifications/types.ts.txt +18 -0
  260. package/tailwind/routes/account/orders/OrderSinglePage.tsx.txt +181 -0
  261. package/tailwind/routes/account/orders/OrdersPage.tsx.txt +28 -0
  262. package/tailwind/routes/account/orders/index.tsx.txt +86 -0
  263. package/tailwind/routes/account/orders/loader.ts.txt +66 -0
  264. package/tailwind/routes/account/orders/single/OrderActions.tsx.txt +197 -0
  265. package/tailwind/routes/account/orders/single/OrderHeader.tsx.txt +143 -0
  266. package/tailwind/routes/account/orders/single/OrderItemRow.tsx.txt +209 -0
  267. package/tailwind/routes/account/orders/single/OrderSections.tsx.txt +215 -0
  268. package/tailwind/routes/account/orders/single/types.ts.txt +38 -0
  269. package/tailwind/routes/account/orders/types.ts.txt +29 -0
  270. package/tailwind/routes/account/profile/ProfilePage.tsx.txt +30 -0
  271. package/tailwind/routes/account/profile/index.tsx.txt +39 -0
  272. package/tailwind/routes/account/profile/loader.ts.txt +16 -0
  273. package/tailwind/routes/account/profile/types.ts.txt +5 -0
  274. package/tailwind/routes/account/settings/SettingsPage.tsx.txt +13 -0
  275. package/tailwind/routes/account/settings/index.tsx.txt +39 -0
  276. package/tailwind/routes/account/settings/loader.ts.txt +13 -0
  277. package/tailwind/routes/account/settings/types.ts.txt +5 -0
  278. package/tailwind/routes/account/types.ts.txt +320 -0
  279. package/tailwind/routes/account/wallet/WalletPage.tsx.txt +195 -0
  280. package/tailwind/routes/account/wallet/index.tsx.txt +38 -0
  281. package/tailwind/routes/account/wallet/loader.ts.txt +27 -0
  282. package/tailwind/routes/account/wallet/types.ts.txt +11 -0
  283. package/tailwind/routes/account/wishlist/WishlistPage.tsx.txt +314 -0
  284. package/tailwind/routes/account/wishlist/WishlistShare.tsx.txt +248 -0
  285. package/tailwind/routes/account/wishlist/index.tsx.txt +40 -0
  286. package/tailwind/routes/account/wishlist/loader.ts.txt +26 -0
  287. package/tailwind/routes/account/wishlist/types.ts.txt +9 -0
  288. package/tailwind/routes/blog/BlogAuthorPage.tsx.txt +82 -0
  289. package/tailwind/routes/blog/BlogCategoryPage.tsx.txt +84 -0
  290. package/tailwind/routes/blog/BlogPage.tsx.txt +191 -0
  291. package/tailwind/routes/blog/BlogSinglePage.tsx.txt +168 -0
  292. package/tailwind/routes/blog/BlogTagPage.tsx.txt +84 -0
  293. package/tailwind/routes/blog/BlogTags.tsx.txt +54 -0
  294. package/tailwind/routes/blog/components.tsx.txt +87 -0
  295. package/tailwind/routes/blog/index.tsx.txt +238 -0
  296. package/tailwind/routes/blog/loader.ts.txt +173 -0
  297. package/tailwind/routes/blog/types.ts.txt +106 -0
  298. package/tailwind/routes/brands/BrandsPage.tsx.txt +179 -0
  299. package/tailwind/routes/brands/index.tsx.txt +47 -0
  300. package/tailwind/routes/brands/loader.ts.txt +14 -0
  301. package/tailwind/routes/brands/types.ts.txt +53 -0
  302. package/tailwind/routes/cart/CartPage.tsx.txt +112 -0
  303. package/tailwind/routes/cart/index.tsx.txt +39 -0
  304. package/tailwind/routes/cart/loader.ts.txt +15 -0
  305. package/tailwind/routes/cart/types.ts.txt +6 -0
  306. package/tailwind/routes/home/HomePage.tsx.txt +53 -0
  307. package/tailwind/routes/home/index.tsx.txt +56 -0
  308. package/tailwind/routes/home/loader.ts.txt +24 -0
  309. package/tailwind/routes/home/types.ts.txt +11 -0
  310. package/tailwind/routes/index.ts.txt +94 -0
  311. package/tailwind/routes/lazy.ts.txt +105 -0
  312. package/tailwind/routes/loyalty/LoyaltyPage.tsx.txt +17 -0
  313. package/tailwind/routes/loyalty/LoyaltyPointsPage.tsx.txt +11 -0
  314. package/tailwind/routes/loyalty/index.tsx.txt +81 -0
  315. package/tailwind/routes/loyalty/loader.ts.txt +53 -0
  316. package/tailwind/routes/loyalty/types.ts.txt +109 -0
  317. package/tailwind/routes/not-found.tsx.txt +50 -0
  318. package/tailwind/routes/page/PageSingle.tsx.txt +34 -0
  319. package/tailwind/routes/page/index.tsx.txt +60 -0
  320. package/tailwind/routes/page/loader.ts.txt +19 -0
  321. package/tailwind/routes/page/types.ts.txt +34 -0
  322. package/tailwind/routes/product/ProductPage.tsx.txt +77 -0
  323. package/tailwind/routes/product/index.tsx.txt +157 -0
  324. package/tailwind/routes/product/loader.ts.txt +35 -0
  325. package/tailwind/routes/product/types.ts.txt +6 -0
  326. package/tailwind/routes/product-listing/ProductListPage.tsx.txt +321 -0
  327. package/tailwind/routes/product-listing/index.tsx.txt +95 -0
  328. package/tailwind/routes/product-listing/loader.ts.txt +323 -0
  329. package/tailwind/routes/product-listing/types.ts.txt +96 -0
  330. package/tailwind/routes/product-listing/useAppliedFilters.ts.txt +130 -0
  331. package/tailwind/routes/product-short-link.ts.txt +104 -0
  332. package/tailwind/routes/root/beforeLoad.ts.txt +354 -0
  333. package/tailwind/routes/root/head.ts.txt +424 -0
  334. package/tailwind/routes/root/index.ts.txt +2 -0
  335. package/tailwind/routes/seo-redirects.ts.txt +82 -0
  336. package/tailwind/routes/testimonials/ReviewsPage.tsx.txt +12 -0
  337. package/tailwind/routes/testimonials/TestimonialsPage.tsx.txt +80 -0
  338. package/tailwind/routes/testimonials/index.tsx.txt +80 -0
  339. package/tailwind/routes/testimonials/loader.ts.txt +29 -0
  340. package/tailwind/routes/testimonials/types.ts.txt +9 -0
  341. package/tailwind/routes/thank-you/ThankYouPage.tsx.txt +241 -0
  342. package/tailwind/routes/thank-you/index.tsx.txt +49 -0
  343. package/tailwind/routes/thank-you/loader.ts.txt +40 -0
  344. package/tailwind/routes/thank-you/types.ts.txt +16 -0
  345. package/tailwind/routes/types.ts.txt +41 -0
  346. package/dist/BlogAuthorPage-GTF4UGSS.js.map +0 -1
  347. package/dist/BlogCategoryPage-TN7Y4NBP.js.map +0 -1
  348. package/dist/BlogPage-TDB3M3PI.js.map +0 -1
  349. package/dist/BlogSinglePage-4A52Q5QW.js.map +0 -1
  350. package/dist/BlogTagPage-TOVPBETA.js.map +0 -1
  351. package/dist/BrandsPage-VUDGYSQI.js.map +0 -1
  352. package/dist/Breadcrumb-CI3SN5YR.js.map +0 -1
  353. package/dist/CartItem-BHLEURAB.js.map +0 -1
  354. package/dist/CartSummary-QERVESYN.js.map +0 -1
  355. package/dist/CustomerLayout-VRZUWGQK.js.map +0 -1
  356. package/dist/LoyaltyPage-CGTHWJ76.js.map +0 -1
  357. package/dist/LoyaltyPointsPage-QSU6K3PW.js.map +0 -1
  358. package/dist/MobileMenu-U4BVQYKH.js.map +0 -1
  359. package/dist/NotificationsPage-ZRRQY7TF.js.map +0 -1
  360. package/dist/OrderSinglePage-O3XYVMG5.js.map +0 -1
  361. package/dist/OrdersPage-CMXZF5MI.js.map +0 -1
  362. package/dist/ProductDetails-65GDABI7.js.map +0 -1
  363. package/dist/ProfilePage-SNJ4WXU4.js.map +0 -1
  364. package/dist/SeoCartWidget-DBJAXJKT.js.map +0 -1
  365. package/dist/SettingsPage-FLTIOPU5.js.map +0 -1
  366. package/dist/ThankYouPage-5X6B4X3T.js.map +0 -1
  367. package/dist/WalletPage-H2BKIDPA.js.map +0 -1
  368. package/dist/WishlistPage-RS5UCIQ3.js.map +0 -1
  369. package/dist/chunk-7WAXN2PP.js +0 -14
  370. package/dist/chunk-NZVR4OF7.js.map +0 -1
  371. package/dist/chunk-SDFDS4Q3.js.map +0 -1
  372. package/dist/not-found-QTHBFPNT.js.map +0 -1
@@ -0,0 +1,267 @@
1
+ import { Fragment, useState } from 'react';
2
+ import { HookSlot } from '../../hooks/HookSlot';
3
+ import { HookName } from '../../types/hooks';
4
+ import { useTranslation } from '../../providers/I18nProvider';
5
+ import { getActiveCurrency } from '../../api/client';
6
+ import { useWishlist } from '../../hooks/useWishlist';
7
+ import { CurrencySymbol, Link, Image } from '../common';
8
+ import { SallaRatingStars } from '@salla.sa/twilight-components-react/rating-stars';
9
+ import { SallaSocialShare } from '@salla.sa/twilight-components-react/social-share';
10
+ import { SallaButton } from '@salla.sa/twilight-components-react/button';
11
+ import { SallaInstallment } from '@salla.sa/twilight-components-react/installment';
12
+ import { SallaMetadata } from '@salla.sa/twilight-components-react/metadata';
13
+ import { formatSallaPlural } from '../../utils/salla-plural';
14
+ import type { Product } from '../../types';
15
+
16
+ interface ProductDetailsProps {
17
+ product: Product;
18
+ showTags?: boolean;
19
+ }
20
+
21
+ export function ProductDetails({ product, showTags = true }: ProductDetailsProps) {
22
+ const { t } = useTranslation();
23
+ const wishlist = useWishlist();
24
+
25
+ // Context for all product hooks - contains product data
26
+ const productHookContext = { product };
27
+
28
+ return (
29
+ <>
30
+ <HookSlot name={HookName.PRODUCT_DESCRIPTION_START} context={productHookContext} />
31
+
32
+ {product.brand?.name && (
33
+ <div className="product-brand mb-5 w-12">
34
+ <Link className="brand-logo" to={product.brand.url || '#'} title={product.brand.name}>
35
+ <Image
36
+ className="max-h-full object-contain"
37
+ src={product.brand.logo}
38
+ title={product.brand.name}
39
+ alt={product.brand.name}
40
+ aspectRatio="1/1"
41
+ />
42
+ </Link>
43
+ </div>
44
+ )}
45
+
46
+ <h1 className="text-xl md:text-2xl leading-10 font-bold mb-6 text-gray-800">
47
+ {product.name}
48
+ </h1>
49
+
50
+ {product.subtitle && (
51
+ <h2 className="product-entry__sub-title text-sm text-gray-500 leading-6 mb-2.5">
52
+ {product.subtitle}
53
+ </h2>
54
+ )}
55
+
56
+ {product.rating && (
57
+ <SallaRatingStars value={product.rating.stars} reviews={product.rating.count} />
58
+ )}
59
+
60
+ {product.is_taxable && (
61
+ <small className="color-grey">{t('pages.products.tax_included', 'VAT Inclusive')}</small>
62
+ )}
63
+
64
+ <ProductPrice product={product} />
65
+
66
+ <ProductDescription product={product} />
67
+
68
+ {!!product.tags?.length && showTags && (
69
+ <div className="mb-3">
70
+ {product.tags.map((tag, index) => (
71
+ <Fragment key={tag.name}>
72
+ <Link
73
+ to={tag.url}
74
+ className="rtl:ml-2 ltr:mr-2 inline-flex text-gray-500 hover:text-primary underline text-sm mb-1"
75
+ >
76
+ {tag.name}
77
+ </Link>
78
+ {index < product.tags!.length - 1 && ','}
79
+ </Fragment>
80
+ ))}
81
+ </div>
82
+ )}
83
+
84
+ <div className="flex rtl:space-x-reverse space-x-3">
85
+ <SallaSocialShare className="inline-flex mb-5" aria-label="social share" />
86
+
87
+ <SallaButton
88
+ className={`btn--wishlist animated hidden sm:inline-flex ${wishlist.has(Number(product.id)) ? 'is-added' : 'not-added'}`}
89
+ data-id={product.id}
90
+ onClick={() => window.salla?.wishlist.toggle(product.id)}
91
+ shape="icon"
92
+ fill="outline"
93
+ color="light"
94
+ ariaLabel="add to wishlist"
95
+ >
96
+ <i className="sicon-heart"></i>
97
+ </SallaButton>
98
+ </div>
99
+
100
+ <ProductQuantityInfo product={product} />
101
+
102
+ {product.sku && (
103
+ <div className="mb-4 flex justify-between sm:grid sm:grid-cols-3 bg-white rounded-md px-4 py-2">
104
+ <div className="flex items-center">
105
+ <i className="sicon-barcode mx-1"></i>
106
+ {t('pages.products.sku', 'SKU')}
107
+ </div>
108
+ <div className="text-unicode">{product.sku}</div>
109
+ </div>
110
+ )}
111
+
112
+ {product.barcode && (
113
+ <div className="mb-4 flex justify-between sm:grid sm:grid-cols-3 bg-white rounded-md px-4 py-2">
114
+ <div className="flex items-center">
115
+ <i className="sicon-barcode mx-1"></i>
116
+ {t('pages.products.barcode')}
117
+ </div>
118
+ <div className="text-unicode">{product.barcode}</div>
119
+ </div>
120
+ )}
121
+
122
+ {product.show_availability && (
123
+ <section className="bg-white p-5 rounded-md mb-5 last:mb-0">
124
+ <div className="center-between">
125
+ <label className="flex items-center text-base">
126
+ <span className="sicon-location rtl:ml-1 ltr:mr-1"></span>
127
+ <span className="inline-block">
128
+ {t('pages.products.availability', 'Availability')}
129
+ </span>
130
+ </label>
131
+ <div className="mt-1 sm:mt-0 sm:col-span-2 text-end">
132
+ <div className="flex rtl:space-x-reverse space-x-3">
133
+ <a
134
+ href="#!"
135
+ onClick={() =>
136
+ window.salla?.event?.dispatch('scopes::open', {
137
+ mode: 'availability',
138
+ product_id: String(product.id),
139
+ })
140
+ }
141
+ className="group text-primary flex items-center justify-center"
142
+ >
143
+ <span>{t('pages.products.select_branch', 'Select Branch')}</span>
144
+ <span className="sicon-keyboard_arrow_left mr-2 transition-all group-hover:-translate-x-1"></span>
145
+ </a>
146
+ </div>
147
+ </div>
148
+ </div>
149
+ </section>
150
+ )}
151
+
152
+ <HookSlot name={HookName.PRODUCT_DESCRIPTION} context={productHookContext} />
153
+
154
+ <SallaInstallment
155
+ price={String(
156
+ typeof product.base_currency_price === 'object'
157
+ ? product.base_currency_price.amount
158
+ : product.base_currency_price
159
+ )}
160
+ />
161
+
162
+ {product.has_metadata && <SallaMetadata />}
163
+ </>
164
+ );
165
+ }
166
+
167
+ function ProductPrice({ product }: { product: Product }) {
168
+ const { t } = useTranslation();
169
+ // The API converts the amounts but still labels them in the store's own currency.
170
+ const priceCurrency = getActiveCurrency(product.currency);
171
+
172
+ return (
173
+ <div className="flex whitespace-nowrap gap-4 items-center">
174
+ <div
175
+ className={`${product.is_on_sale ? '' : 'hidden'} space-x-2 rtl:space-x-reverse whitespace-nowrap`}
176
+ >
177
+ <p className="text-red-800 font-bold text-xl inline-block">
178
+ {product.sale_price} <CurrencySymbol currency={priceCurrency} />
179
+ </p>
180
+ <span className="text-gray-500 line-through">
181
+ {product.regular_price} <CurrencySymbol currency={priceCurrency} />
182
+ </span>
183
+ </div>
184
+ <div className={`gap-4 ${product.is_on_sale ? 'hidden' : 'flex'}`}>
185
+ {product.starting_price && (
186
+ <span>{t('pages.products.starting_price', 'Starting from')}</span>
187
+ )}
188
+ <p className="font-bold text-xl inline-block">
189
+ {product.starting_price ?? product.price} <CurrencySymbol currency={priceCurrency} />
190
+ </p>
191
+ </div>
192
+ </div>
193
+ );
194
+ }
195
+
196
+ /** An `<iframe>` start tag; quoted values may hold `>`. */
197
+ const IFRAME_TAG = /<iframe\b(?:[^>"']|"[^"]*"|'[^']*')*>/gi;
198
+
199
+ /** Embedded players (YouTube, Vimeo, maps) load only when scrolled near, unless the merchant set `loading`. */
200
+ function lazyIframes(html: string): string {
201
+ return html.replace(IFRAME_TAG, (tag) =>
202
+ /\sloading\s*=/i.test(tag.replace(/"[^"]*"|'[^']*'/g, '""'))
203
+ ? tag
204
+ : tag.replace(/^<iframe/i, '<iframe loading="lazy"')
205
+ );
206
+ }
207
+
208
+ function ProductDescription({ product }: { product: Product }) {
209
+ const { t } = useTranslation();
210
+ const [isExpanded, setIsExpanded] = useState(false);
211
+
212
+ if (!product.description) return null;
213
+
214
+ const description = lazyIframes(product.description.replace(/&nbsp;/g, '\n'));
215
+
216
+ if (product.has_read_more) {
217
+ return (
218
+ <div className="product__description p-2 px-5 sm:p-1 leading-7 mb-3">
219
+ <article
220
+ className="article article--main relative leading-8 overflow-hidden transition-all duration-300 py-4"
221
+ id="more-content"
222
+ style={{ maxHeight: isExpanded ? 'none' : '5.25rem' }}
223
+ dangerouslySetInnerHTML={{ __html: description }}
224
+ />
225
+ <button
226
+ type="button"
227
+ id="btn-show-more"
228
+ className={`link--primary inline-block mt-2 cursor-pointer bg-transparent border-0 p-0 ${isExpanded ? 'is-expanded' : ''}`}
229
+ onClick={() => setIsExpanded(true)}
230
+ >
231
+ {t('pages.products.read_more', 'Read more')}
232
+ </button>
233
+ </div>
234
+ );
235
+ }
236
+
237
+ return (
238
+ <div className="product__description p-2 px-5 sm:p-1 leading-7 mb-3">
239
+ <article className="article--main pb-1" dangerouslySetInnerHTML={{ __html: description }} />
240
+ </div>
241
+ );
242
+ }
243
+
244
+ function ProductQuantityInfo({ product }: { product: Product }) {
245
+ const { t } = useTranslation();
246
+ if (product.sold_quantity == null && !product.can_show_remained_quantity) {
247
+ return null;
248
+ }
249
+
250
+ return (
251
+ <div className="bg-white py-2.5 mb-5 rounded-md inline-flex text-sm">
252
+ {(product.sold_quantity ?? 0) > 0 && (
253
+ <div className="px-4 !text-red-800">
254
+ <i className="sicon-fire rtl:ml-1.5 ltr:mr-1.5"></i> {t('pages.products.sold', 'Sold')}{' '}
255
+ {/* Salla ships `sold_times` in Laravel plural format; resolve it for the count. */}
256
+ <span>{formatSallaPlural(t('pages.products.sold_times'), product.sold_quantity!)}</span>
257
+ </div>
258
+ )}
259
+ {product.can_show_remained_quantity && (
260
+ <div className="px-4 rtl:even:border-r ltr:even:border-l even:border-gray-200">
261
+ <i className="!text-red-800 sicon-box-bankers rtl:ml-1.5 ltr:mr-1.5"></i>
262
+ {t('pages.products.remained', 'Remained')}: {product.quantity}
263
+ </div>
264
+ )}
265
+ </div>
266
+ );
267
+ }
@@ -0,0 +1,234 @@
1
+ import { SallaSlider } from '@salla.sa/twilight-components-react/slider';
2
+ import { Suspense, useEffect, useMemo, useState } from 'react';
3
+ import { registry } from '../ComponentRegistry';
4
+ import { useTwilight } from '../../providers/TwilightProvider';
5
+ import { useTranslation } from '../../providers/I18nProvider';
6
+ import type { Product, ProductImage } from '../../types';
7
+ import { Image } from '../common/Image';
8
+ import { ImageModal } from '../modal';
9
+ import { isNativeApp } from '../../native-app';
10
+
11
+ const PRODUCT_GALLERY_KEY = 'product:gallery';
12
+
13
+ export interface ProductGalleryProps {
14
+ product: Product;
15
+ }
16
+
17
+ type ImagePreview = { src: string; alt: string };
18
+ type GalleryMedia = { url: string; type: 'image' | 'video' | '3d-image' };
19
+
20
+ function ProductGalleryContent({ product }: ProductGalleryProps) {
21
+ const { t } = useTranslation();
22
+ const { theme } = useTwilight();
23
+ const sliderId = `details-slider-${product.id}`;
24
+ const objectFit = theme.settings.slider_background_size ?? 'cover';
25
+
26
+ // Clicking an image opens an in-app lightbox instead of navigating to the CDN URL.
27
+ const [preview, setPreview] = useState<ImagePreview | null>(null);
28
+
29
+ useEffect(() => {
30
+ const raf = requestAnimationFrame(() => {
31
+ const el = document.getElementById(sliderId);
32
+ if (!el?.parentElement) return;
33
+ const { parentElement, nextSibling } = el;
34
+ parentElement.removeChild(el);
35
+ parentElement.insertBefore(el, nextSibling);
36
+ });
37
+ return () => cancelAnimationFrame(raf);
38
+ }, [sliderId]);
39
+
40
+ // The product-details API only returns `image` (singular).
41
+ // Fall back so the main image always renders even without the full gallery array.
42
+ const images: ProductImage[] =
43
+ product.images && product.images.length > 0
44
+ ? product.images
45
+ : product.image?.url
46
+ ? [product.image]
47
+ : [];
48
+
49
+ const hasManyImages = images.length > 1;
50
+ const nativeGallery = images.flatMap<GalleryMedia>((image) => {
51
+ if (image.three_d_image_url) return [{ url: image.three_d_image_url, type: '3d-image' }];
52
+ if (image.video_url) return [{ url: image.video_url, type: 'video' }];
53
+ return image.url ? [{ url: image.url, type: 'image' }] : [];
54
+ });
55
+ const openNativeGallery = () => {
56
+ if (!isNativeApp()) return false;
57
+ window.salla?.event.dispatch('mobile::gallery.open', { images: nativeGallery });
58
+ return true;
59
+ };
60
+
61
+ return (
62
+ <div className="sidebar md:sticky top-24 w-full md:!w-2/4 rtl:ml-8 ltr:mr-8 pb-8 md:pb-16 overflow-hidden shrink-0">
63
+ <SallaSlider
64
+ id={sliderId}
65
+ className="details-slider rounded-md image-slider"
66
+ type="thumbs"
67
+ loop={false}
68
+ autoHeight
69
+ listenToThumbnailsOption
70
+ showThumbsControls={false}
71
+ sliderConfig={{ watchOverflow: true, autoHeight: true }}
72
+ >
73
+ {product.promotion_title && (
74
+ <div className="promotion-title">{product.promotion_title}</div>
75
+ )}
76
+
77
+ {product.calories && (
78
+ <div className="absolute z-[2] top-4 rtl:left-4 ltr:right-4 bg-white shadow-sm flex flex-col justify-center items-center rounded-full w-20 h-20 md:w-24 md:h-24">
79
+ <span className="text-red-500 text-xl leading-none font-bold">{product.calories}</span>
80
+ <span className="text-xs text-gray-500">
81
+ {t('pages.products.calories', 'Calories')}
82
+ </span>
83
+ </div>
84
+ )}
85
+
86
+ <div slot="items">
87
+ {images.map((image, index) => (
88
+ <ImageSlide
89
+ key={image.id ?? image.three_d_image_url ?? image.video_url ?? image.url}
90
+ image={image}
91
+ product={product}
92
+ index={index}
93
+ objectFit={objectFit}
94
+ onPreview={setPreview}
95
+ onNativeOpen={openNativeGallery}
96
+ />
97
+ ))}
98
+ </div>
99
+
100
+ {hasManyImages && (
101
+ <div slot="thumbs">
102
+ {images.map((image, index) => (
103
+ <ThumbSlide
104
+ key={image.id ?? image.three_d_image_url ?? image.video_url ?? image.url}
105
+ image={image}
106
+ index={index}
107
+ />
108
+ ))}
109
+ </div>
110
+ )}
111
+ </SallaSlider>
112
+
113
+ <Suspense fallback={null}>
114
+ <ImageModal
115
+ isOpen={!!preview}
116
+ onClose={() => setPreview(null)}
117
+ src={preview?.src ?? ''}
118
+ alt={preview?.alt}
119
+ />
120
+ </Suspense>
121
+ </div>
122
+ );
123
+ }
124
+
125
+ export function ProductGallery(props: ProductGalleryProps) {
126
+ const Component = useMemo(() => {
127
+ const resolved = registry.resolve<ProductGalleryProps>(PRODUCT_GALLERY_KEY);
128
+ // A theme that seeds the key with this component (register + override) would
129
+ // otherwise recurse into itself when it has not actually overridden anything.
130
+ return resolved === ProductGallery ? null : resolved;
131
+ }, []);
132
+
133
+ if (Component) {
134
+ return <Component {...props} />;
135
+ }
136
+
137
+ return <ProductGalleryContent {...props} />;
138
+ }
139
+
140
+ function ImageSlide({
141
+ image,
142
+ product,
143
+ index,
144
+ objectFit,
145
+ onPreview,
146
+ onNativeOpen,
147
+ }: {
148
+ image: ProductImage;
149
+ product: Product;
150
+ index: number;
151
+ objectFit: 'cover' | 'contain';
152
+ onPreview: (preview: ImagePreview) => void;
153
+ onNativeOpen: () => boolean;
154
+ }) {
155
+ const [loaded, setLoaded] = useState(false);
156
+ const isVideo = Boolean(image.video_url);
157
+
158
+ if (image.three_d_image_url) {
159
+ return (
160
+ // biome-ignore lint/a11y/noStaticElementInteractions: model-viewer is an interactive web component.
161
+ <model-viewer
162
+ style={{ minHeight: '500px' }}
163
+ className="swiper-slide model-entry w-full h-full"
164
+ loading="lazy"
165
+ camera-controls
166
+ touch-action="pan-y"
167
+ auto-rotate
168
+ poster={image.url || undefined}
169
+ src={image.three_d_image_url}
170
+ shadow-intensity="1"
171
+ alt={image.alt}
172
+ onClick={onNativeOpen}
173
+ ></model-viewer>
174
+ );
175
+ }
176
+
177
+ return (
178
+ <a
179
+ data-fslightbox={`product_${product.id}`}
180
+ data-img-id={image.id}
181
+ data-slid-index={index}
182
+ {...(image.video_url ? { 'data-video-src': image.video_url } : {})}
183
+ data-caption={image.alt}
184
+ data-infinite="false"
185
+ data-type={image.video_url ? 'youtube' : 'image'}
186
+ href={image.video_url || image.url}
187
+ // FsLightbox isn't loaded in this React theme, so the bare href would leave the SPA.
188
+ // Open images in an in-app lightbox; let videos open in a new tab.
189
+ {...(isVideo ? { target: '_blank', rel: 'noopener noreferrer' } : {})}
190
+ onClick={(event) => {
191
+ if (onNativeOpen()) {
192
+ event.preventDefault();
193
+ return;
194
+ }
195
+ if (isVideo || !image.url) return;
196
+ event.preventDefault();
197
+ onPreview({ src: image.url, alt: image.alt || product.name });
198
+ }}
199
+ aria-label={product.name}
200
+ className={`swiper-slide magnify-wrapper homeslider__slide ${image.video_url ? 'video-entry' : ''}`}
201
+ >
202
+ <div className={`w-full ${loaded ? '' : 'aspect-square bg-gray-100 animate-pulse'}`}>
203
+ <Image
204
+ id={String(image.id)}
205
+ src={image.url}
206
+ alt={image.alt || product.name}
207
+ className={`h-full w-full object-${objectFit}`}
208
+ aspectRatio="1/1"
209
+ priority={index === 0}
210
+ noWrapper
211
+ onLoad={() => setLoaded(true)}
212
+ />
213
+ </div>
214
+ </a>
215
+ );
216
+ }
217
+
218
+ function ThumbSlide({ image, index }: { image: ProductImage; index: number }) {
219
+ return (
220
+ <div
221
+ className={`slide--one-fourth ${image.video_url ? 'video-entry' : ''} ${image.three_d_image_url ? 'model-entry' : ''}`}
222
+ data-caption={image.alt}
223
+ >
224
+ <Image
225
+ src={image.url}
226
+ alt={image.alt || ''}
227
+ className="object-cover w-full h-full bg-gray-100 rounded-md overflow-hidden"
228
+ title={image.alt}
229
+ priority={index < 5}
230
+ noWrapper
231
+ />
232
+ </div>
233
+ );
234
+ }
@@ -0,0 +1,15 @@
1
+ import { LazyProductDetails, LazyAddToCartForm } from './lazy';
2
+ import { ProductCard } from './ProductCard';
3
+ import { ProductGallery } from './ProductGallery';
4
+
5
+ export { ProductGallery };
6
+ export type { ProductGalleryProps } from './ProductGallery';
7
+
8
+ // Lazy-loaded product components (code-split)
9
+ export { LazyProductDetails as ProductDetails, LazyAddToCartForm as AddToCartForm };
10
+
11
+ // Eager components (lightweight, used frequently)
12
+ export { ProductCard };
13
+ export type { ProductCardProps, ProductCardLayout } from './ProductCard';
14
+
15
+ // Types are co-located with components (not in types.ts)
@@ -0,0 +1,9 @@
1
+ import { lazy } from 'react';
2
+
3
+ export const LazyProductDetails = lazy(() =>
4
+ import('./ProductDetails').then((m) => ({ default: m.ProductDetails }))
5
+ );
6
+
7
+ export const LazyAddToCartForm = lazy(() =>
8
+ import('./AddToCartForm').then((m) => ({ default: m.AddToCartForm }))
9
+ );