@salla.sa/twilight-theme-engine 1.0.84 → 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 (377) hide show
  1. package/dist/{AddToCartForm-JUMFLSKN.js → AddToCartForm-5X2WZDWR.js} +3 -3
  2. package/dist/{AddToCartForm-JUMFLSKN.js.map → AddToCartForm-5X2WZDWR.js.map} +1 -1
  3. package/dist/{BlogAuthorPage-LFAUMKC7.js → BlogAuthorPage-LO7FSSJR.js} +12 -10
  4. package/dist/BlogAuthorPage-LO7FSSJR.js.map +1 -0
  5. package/dist/{BlogCategoryPage-NWYFFPCD.js → BlogCategoryPage-KKFQ3PGR.js} +12 -10
  6. package/dist/BlogCategoryPage-KKFQ3PGR.js.map +1 -0
  7. package/dist/{BlogPage-BOHXFV7E.js → BlogPage-VLF3NSGE.js} +11 -9
  8. package/dist/BlogPage-VLF3NSGE.js.map +1 -0
  9. package/dist/{BlogSinglePage-FYEXQPMB.js → BlogSinglePage-AVK535IW.js} +11 -9
  10. package/dist/BlogSinglePage-AVK535IW.js.map +1 -0
  11. package/dist/{BlogTagPage-DAM3RNVA.js → BlogTagPage-F3T4D7RH.js} +12 -10
  12. package/dist/BlogTagPage-F3T4D7RH.js.map +1 -0
  13. package/dist/{BrandsPage-GAKQCE6O.js → BrandsPage-LVSBZSVC.js} +11 -9
  14. package/dist/BrandsPage-LVSBZSVC.js.map +1 -0
  15. package/dist/{Breadcrumb-NAYSZNRV.js → Breadcrumb-VLFTXRFC.js} +11 -9
  16. package/dist/Breadcrumb-VLFTXRFC.js.map +1 -0
  17. package/dist/{CartItem-ZRVLIZT3.js → CartItem-G3H34GW6.js} +11 -9
  18. package/dist/CartItem-G3H34GW6.js.map +1 -0
  19. package/dist/{CartSummary-U2TVLG35.js → CartSummary-VH37RPUI.js} +11 -9
  20. package/dist/CartSummary-VH37RPUI.js.map +1 -0
  21. package/dist/{Copyright-2LSBADZD.js → Copyright-HE5ZWINZ.js} +3 -3
  22. package/dist/{Copyright-2LSBADZD.js.map → Copyright-HE5ZWINZ.js.map} +1 -1
  23. package/dist/{CustomerLayout-WSKXWI7D.js → CustomerLayout-LKBLOAVW.js} +11 -9
  24. package/dist/CustomerLayout-LKBLOAVW.js.map +1 -0
  25. package/dist/{Footer-ONNRJU4G.js → Footer-HHXAST72.js} +5 -5
  26. package/dist/{Footer-ONNRJU4G.js.map → Footer-HHXAST72.js.map} +1 -1
  27. package/dist/{Header-C6VXLLUC.js → Header-GEVZM2YG.js} +8 -6
  28. package/dist/{Header-C6VXLLUC.js.map → Header-GEVZM2YG.js.map} +1 -1
  29. package/dist/{LoyaltyPage-IG5KSYNO.js → LoyaltyPage-M5CT3CFD.js} +11 -9
  30. package/dist/LoyaltyPage-M5CT3CFD.js.map +1 -0
  31. package/dist/{LoyaltyPointsPage-RKO56ZER.js → LoyaltyPointsPage-SDJB52U6.js} +10 -3
  32. package/dist/LoyaltyPointsPage-SDJB52U6.js.map +1 -0
  33. package/dist/{MainMenu-USOYTKPU.js → MainMenu-I7EYS752.js} +3 -3
  34. package/dist/{MainMenu-USOYTKPU.js.map → MainMenu-I7EYS752.js.map} +1 -1
  35. package/dist/{MasterLayout-2W24ACEK.js → MasterLayout-QDK4T3NE.js} +11 -9
  36. package/dist/{MasterLayout-2W24ACEK.js.map → MasterLayout-QDK4T3NE.js.map} +1 -1
  37. package/dist/{MobileMenu-E4X2GOZ3.js → MobileMenu-7VTCWAVJ.js} +11 -9
  38. package/dist/MobileMenu-7VTCWAVJ.js.map +1 -0
  39. package/dist/{NotificationsPage-BJ5XVWEG.js → NotificationsPage-QCABE6QD.js} +11 -9
  40. package/dist/NotificationsPage-QCABE6QD.js.map +1 -0
  41. package/dist/{OrderSinglePage-544E36FJ.js → OrderSinglePage-2HVBKBG4.js} +11 -9
  42. package/dist/OrderSinglePage-2HVBKBG4.js.map +1 -0
  43. package/dist/{OrdersPage-AFBWVTZU.js → OrdersPage-5MNCPCM3.js} +11 -9
  44. package/dist/OrdersPage-5MNCPCM3.js.map +1 -0
  45. package/dist/{PageSingle-H2EJIL74.js → PageSingle-EN5XIM2B.js} +3 -3
  46. package/dist/{PageSingle-H2EJIL74.js.map → PageSingle-EN5XIM2B.js.map} +1 -1
  47. package/dist/{ProductDetails-SDZHALU6.js → ProductDetails-UEWQPPDZ.js} +13 -6
  48. package/dist/ProductDetails-UEWQPPDZ.js.map +1 -0
  49. package/dist/{ProfilePage-V5KV6GQK.js → ProfilePage-TZSU3GHZ.js} +11 -9
  50. package/dist/ProfilePage-TZSU3GHZ.js.map +1 -0
  51. package/dist/{ReviewsPage-CXKJABXP.js → ReviewsPage-WYZ6CHF2.js} +3 -3
  52. package/dist/{ReviewsPage-CXKJABXP.js.map → ReviewsPage-WYZ6CHF2.js.map} +1 -1
  53. package/dist/{SeoCartWidget-JAV6M3X5.js → SeoCartWidget-5JR5RXQM.js} +11 -9
  54. package/dist/SeoCartWidget-5JR5RXQM.js.map +1 -0
  55. package/dist/{SettingsPage-43SUCLQR.js → SettingsPage-DIDRQXMM.js} +11 -9
  56. package/dist/SettingsPage-DIDRQXMM.js.map +1 -0
  57. package/dist/{StoreClosedNotification-MINBJOGZ.js → StoreClosedNotification-C2G32E34.js} +12 -10
  58. package/dist/{StoreClosedNotification-MINBJOGZ.js.map → StoreClosedNotification-C2G32E34.js.map} +1 -1
  59. package/dist/{TestimonialsPage-RLLIDLOA.js → TestimonialsPage-XPHFRBZ7.js} +3 -3
  60. package/dist/{TestimonialsPage-RLLIDLOA.js.map → TestimonialsPage-XPHFRBZ7.js.map} +1 -1
  61. package/dist/{ThankYouPage-52SSZ7FV.js → ThankYouPage-UA6YYFB2.js} +11 -9
  62. package/dist/ThankYouPage-UA6YYFB2.js.map +1 -0
  63. package/dist/{WalletPage-4AN4UJ52.js → WalletPage-AJVV7LVL.js} +11 -9
  64. package/dist/WalletPage-AJVV7LVL.js.map +1 -0
  65. package/dist/{WishlistPage-L6IYJUJZ.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-R5BQDCDK.js → chunk-2HCCIHU7.js} +4 -4
  79. package/dist/{chunk-R5BQDCDK.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-UNEZZMQA.js → chunk-4WVJJ7SN.js} +20 -4
  83. package/dist/chunk-4WVJJ7SN.js.map +1 -0
  84. package/dist/{chunk-FFWSQJLV.js → chunk-BAYGWTNM.js} +3 -3
  85. package/dist/{chunk-FFWSQJLV.js.map → chunk-BAYGWTNM.js.map} +1 -1
  86. package/dist/{chunk-WSRVPR7V.js → chunk-CLBFF2CY.js} +3 -3
  87. package/dist/{chunk-WSRVPR7V.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-CCUE2FMU.js → chunk-DBXH3EMJ.js} +3 -3
  91. package/dist/{chunk-CCUE2FMU.js.map → chunk-DBXH3EMJ.js.map} +1 -1
  92. package/dist/{chunk-MECSOKS7.js → chunk-E26YG3AO.js} +4 -4
  93. package/dist/{chunk-MECSOKS7.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-IC6XW3PD.js → chunk-HQ7WA37L.js} +7 -7
  97. package/dist/{chunk-IC6XW3PD.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-RRGEASAL.js → chunk-KP3VYG4F.js} +6 -6
  101. package/dist/{chunk-RRGEASAL.js.map → chunk-KP3VYG4F.js.map} +1 -1
  102. package/dist/{chunk-EFF2WO6W.js → chunk-PWTXR4RR.js} +10 -10
  103. package/dist/{chunk-EFF2WO6W.js.map → chunk-PWTXR4RR.js.map} +1 -1
  104. package/dist/chunk-QJYUTRAQ.js +14 -0
  105. package/dist/{chunk-4EA56R6I.js.map → chunk-QJYUTRAQ.js.map} +1 -1
  106. package/dist/{chunk-YFQUSAET.js → chunk-U55ZJC2X.js} +4 -4
  107. package/dist/{chunk-YFQUSAET.js.map → chunk-U55ZJC2X.js.map} +1 -1
  108. package/dist/{chunk-R54GSKJS.js → chunk-WSDG4ZRI.js} +30 -16
  109. package/dist/chunk-WSDG4ZRI.js.map +1 -0
  110. package/dist/{chunk-3PWARKZX.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/HomePageRenderer.d.ts +6 -0
  116. package/dist/components/home/HomePageRenderer.d.ts.map +1 -1
  117. package/dist/components/home/index.js +11 -9
  118. package/dist/components/layout/PageAdvertisements.d.ts +7 -0
  119. package/dist/components/layout/PageAdvertisements.d.ts.map +1 -0
  120. package/dist/components/layout/index.d.ts +1 -0
  121. package/dist/components/layout/index.d.ts.map +1 -1
  122. package/dist/components/layout/index.js +8 -1
  123. package/dist/components/product/index.js +10 -8
  124. package/dist/components/store/index.js +11 -9
  125. package/dist/components/store/index.js.map +1 -1
  126. package/dist/{document-class-SD6BUMZN.js → document-class-7AXAAWGZ.js} +6 -5
  127. package/dist/{document-class-SD6BUMZN.js.map → document-class-7AXAAWGZ.js.map} +1 -1
  128. package/dist/hooks/index.js +9 -7
  129. package/dist/hooks/useGtm.js +9 -7
  130. package/dist/hooks/useMoney.js +9 -7
  131. package/dist/hooks/useStore.js +9 -7
  132. package/dist/index.js +9 -7
  133. package/dist/index.js.map +1 -1
  134. package/dist/{not-found-TUU7WQLL.js → not-found-3EEMQZMU.js} +11 -9
  135. package/dist/not-found-3EEMQZMU.js.map +1 -0
  136. package/dist/providers/index.js +9 -7
  137. package/dist/routes/account/orders.js +1 -1
  138. package/dist/routes/account.js +6 -6
  139. package/dist/routes/blog.js +5 -5
  140. package/dist/routes/brands.js +1 -1
  141. package/dist/routes/cart.js +13 -11
  142. package/dist/routes/cart.js.map +1 -1
  143. package/dist/routes/home.js +12 -10
  144. package/dist/routes/loyalty.js +2 -2
  145. package/dist/routes/page.js +1 -1
  146. package/dist/routes/product-listing.js +11 -9
  147. package/dist/routes/product-listing.js.map +1 -1
  148. package/dist/routes/product.js +11 -9
  149. package/dist/routes/product.js.map +1 -1
  150. package/dist/routes/root/beforeLoad.d.ts +2 -2
  151. package/dist/routes/root/beforeLoad.d.ts.map +1 -1
  152. package/dist/routes/root/head.d.ts.map +1 -1
  153. package/dist/routes/testimonials.js +2 -2
  154. package/dist/routes/thank-you.js +1 -1
  155. package/dist/skeleton.js +12 -10
  156. package/dist/tanstack/document-class.d.ts.map +1 -1
  157. package/dist/tanstack/index.d.ts +1 -0
  158. package/dist/tanstack/index.d.ts.map +1 -1
  159. package/dist/tanstack/stylesheet.d.ts +15 -0
  160. package/dist/tanstack/stylesheet.d.ts.map +1 -0
  161. package/dist/tanstack.js +34 -5
  162. package/dist/tanstack.js.map +1 -1
  163. package/dist/types/index.d.ts +5 -0
  164. package/dist/types/index.d.ts.map +1 -1
  165. package/dist/utils/index.js +1 -1
  166. package/dist/vite/index.js +1 -1
  167. package/package.json +12 -3
  168. package/tailwind/components/Component.tsx.txt +21 -0
  169. package/tailwind/components/ComponentRegistry.ts.txt +66 -0
  170. package/tailwind/components/cart/CartItem.tsx.txt +211 -0
  171. package/tailwind/components/cart/CartSummary.tsx.txt +269 -0
  172. package/tailwind/components/cart/PreOrderNotification.tsx.txt +16 -0
  173. package/tailwind/components/cart/PriceQuoteButton.tsx.txt +21 -0
  174. package/tailwind/components/cart/SeoCartWidget.tsx.txt +99 -0
  175. package/tailwind/components/cart/index.ts.txt +11 -0
  176. package/tailwind/components/cart/lazy.ts.txt +15 -0
  177. package/tailwind/components/collapse/Collapse.tsx.txt +95 -0
  178. package/tailwind/components/collapse/index.ts.txt +41 -0
  179. package/tailwind/components/common/Breadcrumb.tsx.txt +27 -0
  180. package/tailwind/components/common/BreadcrumbJsonLd.tsx.txt +40 -0
  181. package/tailwind/components/common/BreadcrumbTrail.tsx.txt +97 -0
  182. package/tailwind/components/common/CurrencySymbol.tsx.txt +8 -0
  183. package/tailwind/components/common/DeferredData.tsx.txt +122 -0
  184. package/tailwind/components/common/ErrorPage.tsx.txt +69 -0
  185. package/tailwind/components/common/Image.tsx.txt +119 -0
  186. package/tailwind/components/common/Link.tsx.txt +36 -0
  187. package/tailwind/components/common/Link.types.ts.txt +85 -0
  188. package/tailwind/components/common/NoContent.tsx.txt +27 -0
  189. package/tailwind/components/common/RenderWhenVisible.tsx.txt +92 -0
  190. package/tailwind/components/common/index.ts.txt +42 -0
  191. package/tailwind/components/common/lazy.ts.txt +28 -0
  192. package/tailwind/components/defineComponent.ts.txt +32 -0
  193. package/tailwind/components/drawer/Drawer.tsx.txt +219 -0
  194. package/tailwind/components/drawer/index.ts.txt +38 -0
  195. package/tailwind/components/dropdown/Dropdown.tsx.txt +166 -0
  196. package/tailwind/components/dropdown/index.ts.txt +40 -0
  197. package/tailwind/components/home/BundleComponent.tsx.txt +35 -0
  198. package/tailwind/components/home/ComponentErrorBoundary.tsx.txt +98 -0
  199. package/tailwind/components/home/FeaturedProductsStyle1.tsx.txt +125 -0
  200. package/tailwind/components/home/FeaturedProductsStyle2.tsx.txt +127 -0
  201. package/tailwind/components/home/FeaturedProductsStyle3.tsx.txt +71 -0
  202. package/tailwind/components/home/FixedBanner.tsx.txt +68 -0
  203. package/tailwind/components/home/FixedProducts.tsx.txt +72 -0
  204. package/tailwind/components/home/HomePageRenderer.tsx.txt +284 -0
  205. package/tailwind/components/home/PageBlocks.tsx.txt +24 -0
  206. package/tailwind/components/home/ParallaxBackground.tsx.txt +50 -0
  207. package/tailwind/components/home/PhotosSlider.tsx.txt +61 -0
  208. package/tailwind/components/home/ProductsSlider.tsx.txt +87 -0
  209. package/tailwind/components/home/ReorderProducts.tsx.txt +38 -0
  210. package/tailwind/components/home/SquarePhotos.tsx.txt +68 -0
  211. package/tailwind/components/home/StoreFeatures.tsx.txt +34 -0
  212. package/tailwind/components/home/Testimonials.tsx.txt +36 -0
  213. package/tailwind/components/home/Youtube.tsx.txt +48 -0
  214. package/tailwind/components/home/index.ts.txt +50 -0
  215. package/tailwind/components/home/lazy.ts.txt +17 -0
  216. package/tailwind/components/home/register.ts.txt +44 -0
  217. package/tailwind/components/home/reorder-hooks.ts.txt +52 -0
  218. package/tailwind/components/home/types.ts.txt +4 -0
  219. package/tailwind/components/layout/Copyright.tsx.txt +23 -0
  220. package/tailwind/components/layout/CustomerLayout.tsx.txt +91 -0
  221. package/tailwind/components/layout/Footer.tsx.txt +343 -0
  222. package/tailwind/components/layout/Header.tsx.txt +154 -0
  223. package/tailwind/components/layout/MasterLayout.tsx.txt +63 -0
  224. package/tailwind/components/layout/PageAdvertisements.tsx.txt +131 -0
  225. package/tailwind/components/layout/WidgetHead.tsx.txt +53 -0
  226. package/tailwind/components/layout/index.ts.txt +27 -0
  227. package/tailwind/components/layout/lazy.ts.txt +25 -0
  228. package/tailwind/components/layout/types.ts.txt +11 -0
  229. package/tailwind/components/modal/ImageModal.tsx.txt +70 -0
  230. package/tailwind/components/modal/Modal.tsx.txt +245 -0
  231. package/tailwind/components/modal/index.ts.txt +39 -0
  232. package/tailwind/components/modal/lazy.ts.txt +10 -0
  233. package/tailwind/components/navigation/MainMenu.tsx.txt +94 -0
  234. package/tailwind/components/navigation/MobileMenu.tsx.txt +160 -0
  235. package/tailwind/components/navigation/index.ts.txt +4 -0
  236. package/tailwind/components/navigation/lazy.ts.txt +11 -0
  237. package/tailwind/components/product/AddToCartForm.tsx.txt +365 -0
  238. package/tailwind/components/product/ProductCard.tsx.txt +305 -0
  239. package/tailwind/components/product/ProductDetails.tsx.txt +267 -0
  240. package/tailwind/components/product/ProductGallery.tsx.txt +234 -0
  241. package/tailwind/components/product/index.ts.txt +15 -0
  242. package/tailwind/components/product/lazy.ts.txt +9 -0
  243. package/tailwind/components/snippets/AppsSnippets.tsx.txt +225 -0
  244. package/tailwind/components/snippets/SallaSdkScripts.tsx.txt +14 -0
  245. package/tailwind/components/snippets/index.ts.txt +4 -0
  246. package/tailwind/components/store/StoreClosedNotification.tsx.txt +73 -0
  247. package/tailwind/components/store/index.ts.txt +8 -0
  248. package/tailwind/components/store/lazy.ts.txt +8 -0
  249. package/tailwind/components/toast/Toaster.tsx.txt +177 -0
  250. package/tailwind/components/toast/index.ts.txt +10 -0
  251. package/tailwind/components/toast/types.ts.txt +38 -0
  252. package/tailwind/components/toast/useToast.ts.txt +61 -0
  253. package/tailwind/components/tracking/GoogleTag.tsx.txt +77 -0
  254. package/tailwind/components/tracking/SiftSnippet.tsx.txt +49 -0
  255. package/tailwind/components/tracking/TwilightBundles.tsx.txt +67 -0
  256. package/tailwind/components/tracking/index.ts.txt +11 -0
  257. package/tailwind/components/tracking/lazy.ts.txt +11 -0
  258. package/tailwind/components/useComponent.ts.txt +12 -0
  259. package/tailwind/routes/account/index.tsx.txt +16 -0
  260. package/tailwind/routes/account/notifications/NotificationsPage.tsx.txt +54 -0
  261. package/tailwind/routes/account/notifications/index.tsx.txt +39 -0
  262. package/tailwind/routes/account/notifications/loader.ts.txt +22 -0
  263. package/tailwind/routes/account/notifications/types.ts.txt +18 -0
  264. package/tailwind/routes/account/orders/OrderSinglePage.tsx.txt +181 -0
  265. package/tailwind/routes/account/orders/OrdersPage.tsx.txt +28 -0
  266. package/tailwind/routes/account/orders/index.tsx.txt +86 -0
  267. package/tailwind/routes/account/orders/loader.ts.txt +66 -0
  268. package/tailwind/routes/account/orders/single/OrderActions.tsx.txt +197 -0
  269. package/tailwind/routes/account/orders/single/OrderHeader.tsx.txt +143 -0
  270. package/tailwind/routes/account/orders/single/OrderItemRow.tsx.txt +209 -0
  271. package/tailwind/routes/account/orders/single/OrderSections.tsx.txt +215 -0
  272. package/tailwind/routes/account/orders/single/types.ts.txt +38 -0
  273. package/tailwind/routes/account/orders/types.ts.txt +29 -0
  274. package/tailwind/routes/account/profile/ProfilePage.tsx.txt +30 -0
  275. package/tailwind/routes/account/profile/index.tsx.txt +39 -0
  276. package/tailwind/routes/account/profile/loader.ts.txt +16 -0
  277. package/tailwind/routes/account/profile/types.ts.txt +5 -0
  278. package/tailwind/routes/account/settings/SettingsPage.tsx.txt +13 -0
  279. package/tailwind/routes/account/settings/index.tsx.txt +39 -0
  280. package/tailwind/routes/account/settings/loader.ts.txt +13 -0
  281. package/tailwind/routes/account/settings/types.ts.txt +5 -0
  282. package/tailwind/routes/account/types.ts.txt +320 -0
  283. package/tailwind/routes/account/wallet/WalletPage.tsx.txt +195 -0
  284. package/tailwind/routes/account/wallet/index.tsx.txt +38 -0
  285. package/tailwind/routes/account/wallet/loader.ts.txt +27 -0
  286. package/tailwind/routes/account/wallet/types.ts.txt +11 -0
  287. package/tailwind/routes/account/wishlist/WishlistPage.tsx.txt +314 -0
  288. package/tailwind/routes/account/wishlist/WishlistShare.tsx.txt +248 -0
  289. package/tailwind/routes/account/wishlist/index.tsx.txt +40 -0
  290. package/tailwind/routes/account/wishlist/loader.ts.txt +26 -0
  291. package/tailwind/routes/account/wishlist/types.ts.txt +9 -0
  292. package/tailwind/routes/blog/BlogAuthorPage.tsx.txt +82 -0
  293. package/tailwind/routes/blog/BlogCategoryPage.tsx.txt +84 -0
  294. package/tailwind/routes/blog/BlogPage.tsx.txt +191 -0
  295. package/tailwind/routes/blog/BlogSinglePage.tsx.txt +168 -0
  296. package/tailwind/routes/blog/BlogTagPage.tsx.txt +84 -0
  297. package/tailwind/routes/blog/BlogTags.tsx.txt +54 -0
  298. package/tailwind/routes/blog/components.tsx.txt +87 -0
  299. package/tailwind/routes/blog/index.tsx.txt +238 -0
  300. package/tailwind/routes/blog/loader.ts.txt +173 -0
  301. package/tailwind/routes/blog/types.ts.txt +106 -0
  302. package/tailwind/routes/brands/BrandsPage.tsx.txt +179 -0
  303. package/tailwind/routes/brands/index.tsx.txt +47 -0
  304. package/tailwind/routes/brands/loader.ts.txt +14 -0
  305. package/tailwind/routes/brands/types.ts.txt +53 -0
  306. package/tailwind/routes/cart/CartPage.tsx.txt +112 -0
  307. package/tailwind/routes/cart/index.tsx.txt +39 -0
  308. package/tailwind/routes/cart/loader.ts.txt +15 -0
  309. package/tailwind/routes/cart/types.ts.txt +6 -0
  310. package/tailwind/routes/home/HomePage.tsx.txt +53 -0
  311. package/tailwind/routes/home/index.tsx.txt +56 -0
  312. package/tailwind/routes/home/loader.ts.txt +24 -0
  313. package/tailwind/routes/home/types.ts.txt +11 -0
  314. package/tailwind/routes/index.ts.txt +94 -0
  315. package/tailwind/routes/lazy.ts.txt +105 -0
  316. package/tailwind/routes/loyalty/LoyaltyPage.tsx.txt +17 -0
  317. package/tailwind/routes/loyalty/LoyaltyPointsPage.tsx.txt +11 -0
  318. package/tailwind/routes/loyalty/index.tsx.txt +81 -0
  319. package/tailwind/routes/loyalty/loader.ts.txt +53 -0
  320. package/tailwind/routes/loyalty/types.ts.txt +109 -0
  321. package/tailwind/routes/not-found.tsx.txt +50 -0
  322. package/tailwind/routes/page/PageSingle.tsx.txt +34 -0
  323. package/tailwind/routes/page/index.tsx.txt +60 -0
  324. package/tailwind/routes/page/loader.ts.txt +19 -0
  325. package/tailwind/routes/page/types.ts.txt +34 -0
  326. package/tailwind/routes/product/ProductPage.tsx.txt +77 -0
  327. package/tailwind/routes/product/index.tsx.txt +157 -0
  328. package/tailwind/routes/product/loader.ts.txt +35 -0
  329. package/tailwind/routes/product/types.ts.txt +6 -0
  330. package/tailwind/routes/product-listing/ProductListPage.tsx.txt +321 -0
  331. package/tailwind/routes/product-listing/index.tsx.txt +95 -0
  332. package/tailwind/routes/product-listing/loader.ts.txt +323 -0
  333. package/tailwind/routes/product-listing/types.ts.txt +96 -0
  334. package/tailwind/routes/product-listing/useAppliedFilters.ts.txt +130 -0
  335. package/tailwind/routes/product-short-link.ts.txt +104 -0
  336. package/tailwind/routes/root/beforeLoad.ts.txt +354 -0
  337. package/tailwind/routes/root/head.ts.txt +424 -0
  338. package/tailwind/routes/root/index.ts.txt +2 -0
  339. package/tailwind/routes/seo-redirects.ts.txt +82 -0
  340. package/tailwind/routes/testimonials/ReviewsPage.tsx.txt +12 -0
  341. package/tailwind/routes/testimonials/TestimonialsPage.tsx.txt +80 -0
  342. package/tailwind/routes/testimonials/index.tsx.txt +80 -0
  343. package/tailwind/routes/testimonials/loader.ts.txt +29 -0
  344. package/tailwind/routes/testimonials/types.ts.txt +9 -0
  345. package/tailwind/routes/thank-you/ThankYouPage.tsx.txt +241 -0
  346. package/tailwind/routes/thank-you/index.tsx.txt +49 -0
  347. package/tailwind/routes/thank-you/loader.ts.txt +40 -0
  348. package/tailwind/routes/thank-you/types.ts.txt +16 -0
  349. package/tailwind/routes/types.ts.txt +41 -0
  350. package/dist/BlogAuthorPage-LFAUMKC7.js.map +0 -1
  351. package/dist/BlogCategoryPage-NWYFFPCD.js.map +0 -1
  352. package/dist/BlogPage-BOHXFV7E.js.map +0 -1
  353. package/dist/BlogSinglePage-FYEXQPMB.js.map +0 -1
  354. package/dist/BlogTagPage-DAM3RNVA.js.map +0 -1
  355. package/dist/BrandsPage-GAKQCE6O.js.map +0 -1
  356. package/dist/Breadcrumb-NAYSZNRV.js.map +0 -1
  357. package/dist/CartItem-ZRVLIZT3.js.map +0 -1
  358. package/dist/CartSummary-U2TVLG35.js.map +0 -1
  359. package/dist/CustomerLayout-WSKXWI7D.js.map +0 -1
  360. package/dist/LoyaltyPage-IG5KSYNO.js.map +0 -1
  361. package/dist/LoyaltyPointsPage-RKO56ZER.js.map +0 -1
  362. package/dist/MobileMenu-E4X2GOZ3.js.map +0 -1
  363. package/dist/NotificationsPage-BJ5XVWEG.js.map +0 -1
  364. package/dist/OrderSinglePage-544E36FJ.js.map +0 -1
  365. package/dist/OrdersPage-AFBWVTZU.js.map +0 -1
  366. package/dist/ProductDetails-SDZHALU6.js.map +0 -1
  367. package/dist/ProfilePage-V5KV6GQK.js.map +0 -1
  368. package/dist/SeoCartWidget-JAV6M3X5.js.map +0 -1
  369. package/dist/SettingsPage-43SUCLQR.js.map +0 -1
  370. package/dist/ThankYouPage-52SSZ7FV.js.map +0 -1
  371. package/dist/WalletPage-4AN4UJ52.js.map +0 -1
  372. package/dist/WishlistPage-L6IYJUJZ.js.map +0 -1
  373. package/dist/chunk-3PWARKZX.js.map +0 -1
  374. package/dist/chunk-4EA56R6I.js +0 -14
  375. package/dist/chunk-R54GSKJS.js.map +0 -1
  376. package/dist/chunk-UNEZZMQA.js.map +0 -1
  377. package/dist/not-found-TUU7WQLL.js.map +0 -1
@@ -0,0 +1,68 @@
1
+ import { memo } from 'react';
2
+ import { Link } from '../common';
3
+
4
+ export interface FixedBannerComponentProps {
5
+ data: {
6
+ image: {
7
+ url: string;
8
+ alt?: string;
9
+ small_url?: string;
10
+ };
11
+ url?: string;
12
+ title?: string;
13
+ position?: number;
14
+ [key: string]: unknown;
15
+ };
16
+ priority?: boolean;
17
+ }
18
+
19
+ export const FixedBanner = memo(function FixedBanner(props: FixedBannerComponentProps) {
20
+ const { data, priority = false } = props;
21
+ const image = data.image?.url;
22
+
23
+ if (!image) {
24
+ return null;
25
+ }
26
+
27
+ const url = data.url;
28
+ const altText = data.image.alt?.trim() ? data.image.alt : data.title || 'fixed banner';
29
+
30
+ const imgProps = priority
31
+ ? {
32
+ loading: 'eager' as const,
33
+ fetchPriority: 'high' as const,
34
+ decoding: 'sync' as const,
35
+ }
36
+ : {
37
+ loading: 'lazy' as const,
38
+ decoding: 'async' as const,
39
+ };
40
+
41
+ const smallUrl = data.image.small_url;
42
+
43
+ return (
44
+ <>
45
+ <div className="container">
46
+ <Link
47
+ to={url || '#'}
48
+ className="banner banner--fixed overflow-hidden"
49
+ aria-label={`Banner ${altText}`}
50
+ >
51
+ <picture>
52
+ {smallUrl && <source media="(max-width: 767px)" srcSet={smallUrl} />}
53
+ <img
54
+ width="1200"
55
+ height="300"
56
+ src={image}
57
+ alt={altText}
58
+ style={{ maxWidth: '100%', objectFit: 'contain' }}
59
+ loading={imgProps.loading}
60
+ decoding={imgProps.decoding}
61
+ {...(priority && { fetchPriority: imgProps.fetchPriority })}
62
+ />
63
+ </picture>
64
+ </Link>
65
+ </div>
66
+ </>
67
+ );
68
+ });
@@ -0,0 +1,72 @@
1
+ import { Suspense } from 'react';
2
+ import { useQuery } from '@tanstack/react-query';
3
+ import { FixedProductsSkeleton } from '@salla.sa/twilight-components-react';
4
+ import { Link } from '../common';
5
+ import { product, ProductsListSource } from '../../api/product';
6
+ import { ProductCard } from '../../components/product';
7
+ import { useTranslation } from '../../providers/I18nProvider';
8
+
9
+ export interface FixedProductsProps {
10
+ data: {
11
+ products: { source: ProductsListSource; source_value?: number[] | null };
12
+ title?: string;
13
+ limit?: number | string;
14
+ display_all_url?: string | null;
15
+ key?: string | null;
16
+ isVertical?: boolean;
17
+ position?: number;
18
+ [key: string]: unknown;
19
+ };
20
+ }
21
+
22
+ export function FixedProducts({ data }: FixedProductsProps) {
23
+ const { t } = useTranslation();
24
+
25
+ const title = data.title;
26
+ const limit = data.limit ? Number(data.limit) : undefined;
27
+ const isVertical = data.isVertical ?? true;
28
+
29
+ const { data: result, isLoading } = useQuery(
30
+ product.queries.list({
31
+ source: data.products.source,
32
+ sourceValue: data.products?.source_value,
33
+ perPage: limit,
34
+ })
35
+ );
36
+
37
+ const products = result?.items ?? [];
38
+
39
+ if (isLoading) {
40
+ return <FixedProductsSkeleton />;
41
+ }
42
+
43
+ if (!products.length) return null;
44
+
45
+ return (
46
+ <Suspense fallback={<FixedProductsSkeleton />}>
47
+ {title && (
48
+ <div className="s-block__title">
49
+ <div className="right-side">
50
+ <h2>{title}</h2>
51
+ </div>
52
+ {data.display_all_url && (
53
+ <Link to={data.display_all_url} className="s-block__display-all">
54
+ {t('blocks.home.display_all', 'View All')}
55
+ <i className="sicon-arrow-left"></i>
56
+ </Link>
57
+ )}
58
+ </div>
59
+ )}
60
+ <div
61
+ className={
62
+ 's-products-list-wrapper ' +
63
+ (isVertical ? 's-products-list-vertical-cards' : 's-products-list-horizontal-cards')
64
+ }
65
+ >
66
+ {products.slice(0, 4).map((product) => (
67
+ <ProductCard key={product.id} product={product} />
68
+ ))}
69
+ </div>
70
+ </Suspense>
71
+ );
72
+ }
@@ -0,0 +1,284 @@
1
+ import { memo, useEffect, useRef, type ReactNode } from 'react';
2
+ import { registry } from '../ComponentRegistry';
3
+ import { RenderWhenVisible } from '../common';
4
+ import { ComponentErrorBoundary } from './ComponentErrorBoundary';
5
+ import type { AnyHomeComponent } from './index';
6
+ import type { HomeComponentData } from '../../types';
7
+ import {
8
+ BackgroundSliderSkeleton,
9
+ BulletDeliverySkeleton,
10
+ FixedProductsSkeleton,
11
+ ListSkeleton,
12
+ ProductsSliderSkeleton,
13
+ Pulse,
14
+ ReviewItemSkeleton,
15
+ SliderSkeleton,
16
+ } from '@salla.sa/twilight-components-react';
17
+
18
+ function SquareGridSkeleton() {
19
+ return (
20
+ <div className="s-block container grid grid-cols-2 sm:grid-cols-4 gap-4 py-8">
21
+ {[1, 2, 3, 4].map((i) => (
22
+ <Pulse key={i} className="aspect-square" />
23
+ ))}
24
+ </div>
25
+ );
26
+ }
27
+
28
+ export type HomeComponentsProps = {
29
+ data: HomeComponentData & {
30
+ position?: number;
31
+ [key: string]: unknown;
32
+ };
33
+ };
34
+
35
+ /**
36
+ * Fallback component for unknown/unregistered components
37
+ * Only shows in development mode
38
+ */
39
+ const FallbackComponent = memo(function FallbackComponent({ data }: HomeComponentsProps) {
40
+ if (process.env.NODE_ENV === 'production') return null;
41
+
42
+ return (
43
+ <section
44
+ dir="ltr"
45
+ className="s-block container overflow-hidden bg-yellow-50 border border-yellow-200 p-4"
46
+ >
47
+ <p className="text-yellow-800 text-sm">
48
+ <strong>Unknown component:</strong> {data.path}
49
+ </p>
50
+ <p className="text-yellow-700 text-xs mt-1">
51
+ Register it in setup.ts: <code>registry.register('home:{data.path}', YourComponent)</code>
52
+ </p>
53
+ <details className="mt-2">
54
+ <summary className="text-xs cursor-pointer">Component Data</summary>
55
+ <pre className="text-xs mt-1 overflow-auto max-h-32">{JSON.stringify(data, null, 2)}</pre>
56
+ </details>
57
+ </section>
58
+ );
59
+ });
60
+
61
+ /**
62
+ * Get the React component for a given home component path
63
+ * Looks up from registry only - theme must register components
64
+ */
65
+ function getComponentByPath(data: HomeComponentData): AnyHomeComponent {
66
+ const { path, view_style: viewStyle } = data;
67
+
68
+ // 1. Check for view_style variant first
69
+ if (viewStyle) {
70
+ const variantKey = `home:${path}:${viewStyle}`;
71
+ const variant = registry.resolve(variantKey);
72
+ if (variant) {
73
+ return variant as AnyHomeComponent;
74
+ }
75
+ }
76
+
77
+ // 2. Check registry for component
78
+ const registryKey = `home:${path}`;
79
+ const component = registry.resolve(registryKey);
80
+ if (component) {
81
+ return component as AnyHomeComponent;
82
+ }
83
+
84
+ // 3. Fallback for unknown components
85
+ return FallbackComponent;
86
+ }
87
+
88
+ /** Same breakpoint and rules the Store injects for Twig themes (`ComponentVisibilityStyles`). */
89
+ const CHANNEL_VISIBILITY_CSS =
90
+ '@media (max-width:767.98px){.s-hide-on-mobile{display:none!important}}' +
91
+ '@media (min-width:768px){.s-hide-on-desktop{display:none!important}}';
92
+
93
+ /**
94
+ * Hide class for the merchant's per-channel pick. A missing key, or one that
95
+ * hides on both channels, means visible everywhere: hiding a block outright
96
+ * is the `is_visible` toggle's job, and stores without the feature send no key.
97
+ */
98
+ export function channelVisibilityClass(data: HomeComponentData): string {
99
+ const raw = data.channel_visibility;
100
+ if (!raw || typeof raw !== 'object') return '';
101
+ const desktop = raw.desktop !== false;
102
+ const mobile = raw.mobile !== false;
103
+ if (desktop && !mobile) return 's-hide-on-mobile';
104
+ if (mobile && !desktop) return 's-hide-on-desktop';
105
+ return '';
106
+ }
107
+
108
+ /**
109
+ * Render-shell config for a home block: the values the lazy-load wrapper
110
+ * (`RenderWhenVisible`) needs *before* the block mounts — reserved height, the
111
+ * skeleton placeholder, and the wrapper's className/id.
112
+ *
113
+ * Themes register config for blocks they own via `registerHomeComponentConfig`;
114
+ * the engine knows nothing about theme-specific blocks.
115
+ */
116
+ export type HomeComponentConfig = {
117
+ height?: string;
118
+ className?: string | ((data: HomeComponentData) => string);
119
+ id?: string | ((data: HomeComponentData) => string);
120
+ placeholder?: ReactNode;
121
+ /** Mount on page load instead of when the block nears the viewport. */
122
+ renderOnMount?: boolean;
123
+ };
124
+
125
+ const COMPONENT_CONFIG: Record<string, HomeComponentConfig> = {
126
+ 'products-slider': { height: '450px', placeholder: <ProductsSliderSkeleton /> },
127
+ 'fixed-products': { height: '450px', placeholder: <FixedProductsSkeleton /> },
128
+ 'featured-products': {
129
+ height: '450px',
130
+ placeholder: <ProductsSliderSkeleton />,
131
+ className: (data) => {
132
+ const style = data.view_style;
133
+ if (style === 'style1')
134
+ return 's-block s-block--featured-products s-block-tabs s-block--special-products container tabs-initialized';
135
+ if (style === 'style2')
136
+ return `s-block s-block--featured-products s-block-tabs s-block--tabs-produtcs s-block--full-bg tabs-initialized ${data.is_slider ? 'as-slider' : 'as-grid'} bg-gray-100 py-8 sm:py-16`;
137
+ if (style === 'style3')
138
+ return `s-block s-block--featured-products s-block--features-products${Array.isArray(data.items) && data.items.length > 1 ? ' two-cols' : ''} container`;
139
+ return 's-block s-block--featured-products container overflow-hidden';
140
+ },
141
+ id: (data) => `featured-products-${data.view_style}-${data.position ?? 0}`,
142
+ },
143
+ testimonial: { height: '350px', placeholder: <ReviewItemSkeleton /> },
144
+ 'store-features': { height: '200px', placeholder: <BulletDeliverySkeleton /> },
145
+ 'parallax-background': { height: '500px', placeholder: <BackgroundSliderSkeleton /> },
146
+ 'fixed-banner': { height: '350px', placeholder: <BackgroundSliderSkeleton /> },
147
+ 'photos-slider': {
148
+ height: 'clamp(130px, 30vw, 350px)',
149
+ placeholder: <SliderSkeleton />,
150
+ className: (data) =>
151
+ `s-block s-block--photos-slider${data.is_repeated ? ' repeated-block' : ''}`,
152
+ },
153
+ 'square-photos': { height: '250px', placeholder: <SquareGridSkeleton /> },
154
+ youtube: { height: '350px', placeholder: <SliderSkeleton /> },
155
+ 'bundle-component': {
156
+ height: '400px',
157
+ placeholder: <ProductsSliderSkeleton />,
158
+ className: (data) => `s-block s-block--bundle-${data.component_name}`,
159
+ // As in Twig, where merchant scripts wait a limited time for the bundle's markup.
160
+ renderOnMount: true,
161
+ },
162
+ 'main-links': {
163
+ height: '200px',
164
+ placeholder: <ListSkeleton />,
165
+ className: (data) => {
166
+ const mergeWithTop = data.merge_links_with_top_component;
167
+ const position = data.position;
168
+ return [
169
+ 's-block',
170
+ 's-block--main-links',
171
+ mergeWithTop && position ? 'merge-with-top-component' : '',
172
+ !data.title && mergeWithTop ? 'merged-has-no-title' : '',
173
+ ]
174
+ .filter(Boolean)
175
+ .join(' ');
176
+ },
177
+ },
178
+ 'square-links': { height: '200px', placeholder: <SquareGridSkeleton /> },
179
+ };
180
+
181
+ /**
182
+ * Theme-registered render-shell config, keyed by home component path. Themes own
183
+ * the display of their blocks by registering config here; at render time a path's
184
+ * theme entry replaces (not field-merges) the engine's built-in `COMPONENT_CONFIG`
185
+ * entry for that path.
186
+ */
187
+ const themeComponentConfig: Record<string, HomeComponentConfig> = {};
188
+
189
+ /**
190
+ * Register render-shell config for home blocks a theme owns (height, placeholder,
191
+ * className, id). Merges by path and overwrites existing keys, so it is safe to
192
+ * call more than once and idempotent for the same keys.
193
+ */
194
+ export function registerHomeComponentConfig(config: Record<string, HomeComponentConfig>): void {
195
+ Object.assign(themeComponentConfig, config);
196
+ }
197
+
198
+ /**
199
+ * Resolve a block's render-shell config: theme-registered config takes precedence
200
+ * over the engine's built-in config; returns `undefined` when neither defines the
201
+ * path so the renderer can apply generic defaults.
202
+ */
203
+ function resolveComponentConfig(path: string): HomeComponentConfig | undefined {
204
+ return themeComponentConfig[path] ?? COMPONENT_CONFIG[path];
205
+ }
206
+
207
+ /**
208
+ * Memoized component renderer with scroll-based lazy loading
209
+ * Components only mount when scrolled into view - preventing API calls until needed
210
+ */
211
+ export const HomeComponentRenderer = memo(function HomeComponentRenderer({
212
+ data,
213
+ index,
214
+ priority,
215
+ }: {
216
+ data: HomeComponentData;
217
+ index: number;
218
+ /** Mount right away with eager images. Defaults to the first three blocks of the page. */
219
+ priority?: boolean;
220
+ }) {
221
+ const renderCountRef = useRef(0);
222
+ renderCountRef.current += 1;
223
+
224
+ if (process.env.NODE_ENV === 'development') {
225
+ console.log(
226
+ `[HomeComponentRenderer] RENDER: path=${data.path}, index=${index}, renderCount=${renderCountRef.current}`
227
+ );
228
+ }
229
+
230
+ useEffect(() => {
231
+ console.log(
232
+ `[HomeComponentRenderer] MOUNT: path=${data.path}, index=${index}, renderCount=${renderCountRef.current}`
233
+ );
234
+ return () => {
235
+ console.log(`[HomeComponentRenderer] UNMOUNT: path=${data.path}, index=${index}`);
236
+ };
237
+ }, [data.path, index]);
238
+
239
+ const isFirstComponent = priority ?? index <= 2;
240
+ const Component = getComponentByPath(data);
241
+
242
+ const config = resolveComponentConfig(data.path);
243
+
244
+ const resolvedClassName = config?.className
245
+ ? typeof config.className === 'function'
246
+ ? config.className(data)
247
+ : config.className
248
+ : `s-block s-block--${data.path}`;
249
+ const hideClass = channelVisibilityClass(data);
250
+ const resolvedId = config?.id
251
+ ? typeof config.id === 'function'
252
+ ? config.id(data)
253
+ : config.id
254
+ : undefined;
255
+ const height = config?.height || '400px';
256
+ // A theme's config replaces the engine's, but keeps its mount timing unless the theme sets one.
257
+ const renderOnMount = config?.renderOnMount ?? COMPONENT_CONFIG[data.path]?.renderOnMount;
258
+
259
+ const content = (
260
+ <ComponentErrorBoundary data={data} position={index + 1}>
261
+ <Component data={{ ...data, position: index + 1, priority: isFirstComponent }} />
262
+ </ComponentErrorBoundary>
263
+ );
264
+
265
+ return (
266
+ <>
267
+ {hideClass && (
268
+ <style href="twilight-channel-visibility" precedence="default">
269
+ {CHANNEL_VISIBILITY_CSS}
270
+ </style>
271
+ )}
272
+ <RenderWhenVisible
273
+ rootMargin="50px"
274
+ className={hideClass ? `${resolvedClassName} ${hideClass}` : resolvedClassName}
275
+ id={resolvedId}
276
+ estimatedHeight={height}
277
+ renderOnMount={isFirstComponent || renderOnMount === true}
278
+ placeholder={config?.placeholder ?? <ProductsSliderSkeleton />}
279
+ >
280
+ {content}
281
+ </RenderWhenVisible>
282
+ </>
283
+ );
284
+ });
@@ -0,0 +1,24 @@
1
+ import { memo } from 'react';
2
+ import { useQuery } from '@tanstack/react-query';
3
+ import { home, type BlockScope } from '../../api/home';
4
+ import { HomeComponentRenderer } from './HomePageRenderer';
5
+
6
+ export interface PageBlocksProps {
7
+ /** The page hook the merchant placed the blocks at, e.g. `product.single.before_customer_reviews`. */
8
+ hook: string;
9
+ /** The product, category or page being shown. */
10
+ scope: BlockScope;
11
+ }
12
+
13
+ /** Draws the blocks placed at a product, category or page hook, loaded in the browser. */
14
+ export const PageBlocks = memo(function PageBlocks({ hook, scope }: PageBlocksProps) {
15
+ const { data: blocks = [] } = useQuery(home.queries.blocks(hook, scope));
16
+
17
+ return (
18
+ <>
19
+ {blocks.map((data, index) => (
20
+ <HomeComponentRenderer key={index} data={data} index={index} priority={false} />
21
+ ))}
22
+ </>
23
+ );
24
+ });
@@ -0,0 +1,50 @@
1
+ import { memo } from 'react';
2
+ import { Link } from '../common';
3
+
4
+ interface ImageData {
5
+ url?: string;
6
+ }
7
+
8
+ export interface ParallaxBackgroundProps {
9
+ data: {
10
+ image: ImageData;
11
+ url?: string;
12
+ title?: string;
13
+ link_text?: string;
14
+ is_opacity?: boolean;
15
+ position?: number;
16
+ [key: string]: unknown;
17
+ };
18
+ }
19
+
20
+ export const ParallaxBackground = memo(function ParallaxBackground({
21
+ data,
22
+ }: ParallaxBackgroundProps) {
23
+ const { image, url, title, link_text: linkText, is_opacity: isOpacity = false } = data;
24
+
25
+ if (!image?.url) return null;
26
+
27
+ return (
28
+ <>
29
+ <div className="full-banner-entry" style={{ backgroundImage: `url(${image.url})` }}>
30
+ <div
31
+ className={`overlay h-full w-full ${isOpacity ? 'bg-black' : ''} opacity-60 absolute top-0 left-0`}
32
+ ></div>
33
+ <div className="flex h-full items-center justify-center p-5 xs:p-11 relative">
34
+ <div>
35
+ {title && <h2 className="text-2xl font-bold leading-12">{title}</h2>}
36
+
37
+ {url && linkText && (
38
+ <Link
39
+ to={url}
40
+ className="inline-block text-white text-sm border rounded-md font-bold px-8 py-4"
41
+ >
42
+ {linkText}
43
+ </Link>
44
+ )}
45
+ </div>
46
+ </div>
47
+ </div>
48
+ </>
49
+ );
50
+ });
@@ -0,0 +1,61 @@
1
+ import { memo } from 'react';
2
+ import { SallaSlider } from '@salla.sa/twilight-components-react/slider';
3
+ import { Link, Image } from '../common';
4
+
5
+ interface PhotoItem {
6
+ id?: string | number;
7
+ url?: string;
8
+ link_type?: string;
9
+ image?: string;
10
+ }
11
+
12
+ export interface PhotosSliderProps {
13
+ data: {
14
+ items: PhotoItem[];
15
+ position?: number;
16
+ is_repeated?: boolean;
17
+ [key: string]: unknown;
18
+ };
19
+ }
20
+
21
+ export const PhotosSlider = memo(function PhotosSlider({ data }: PhotosSliderProps) {
22
+ const items = data.items || [];
23
+ const position = data.position ?? 1;
24
+
25
+ if (!items.length) return null;
26
+
27
+ return (
28
+ <>
29
+ <SallaSlider
30
+ type="carousel"
31
+ className="home-slider photos-slider"
32
+ centered
33
+ autoPlay
34
+ pagination
35
+ id={`photos-${position}-slider`}
36
+ sliderConfig={{ watchOverflow: true }}
37
+ >
38
+ <div slot="items">
39
+ {items.map((item, index) => (
40
+ <div
41
+ key={item.id ?? `${item.url ?? ''}:${item.image ?? ''}`}
42
+ className="swiper-slide"
43
+ style={{ aspectRatio: '1200/300' }}
44
+ >
45
+ <Link to={item.url || '#'}>
46
+ <Image
47
+ src={item.image}
48
+ className="w-full h-full object-contain rounded-md"
49
+ alt={`image-slider-${index}`}
50
+ width={1200}
51
+ height={300}
52
+ priority={index === 0}
53
+ />
54
+ </Link>
55
+ </div>
56
+ ))}
57
+ </div>
58
+ </SallaSlider>
59
+ </>
60
+ );
61
+ });
@@ -0,0 +1,87 @@
1
+ import { Suspense, useCallback, useMemo } from 'react';
2
+ import { product, ProductsListSource } from '../../api/product';
3
+ import { ProductCard } from '../../components/product';
4
+ import { ProductsSliderSkeleton, SallaProductsSlider } from '@salla.sa/twilight-components-react';
5
+ import { useTranslation } from '../../providers/I18nProvider';
6
+
7
+ interface ProductsSourceConfig {
8
+ source: ProductsListSource;
9
+ source_value?: number | number[] | null;
10
+ }
11
+
12
+ export interface ProductsSliderProps {
13
+ data: {
14
+ products: ProductsSourceConfig;
15
+ title?: string;
16
+ display_all_url?: string;
17
+ position?: number;
18
+ priority?: boolean;
19
+ show_controls?: boolean;
20
+ controls_outer?: boolean;
21
+ loop?: boolean;
22
+ centered?: boolean;
23
+ slides_per_view?: number | 'auto';
24
+ direction?: 'rtl' | 'ltr';
25
+ autoplay?: boolean;
26
+ [key: string]: unknown;
27
+ };
28
+ }
29
+
30
+ export function ProductsSlider({ data }: ProductsSliderProps) {
31
+ const { t } = useTranslation();
32
+ const {
33
+ products,
34
+ title,
35
+ display_all_url: displayAllUrl,
36
+ position = 1,
37
+ priority = false,
38
+ show_controls: showControls,
39
+ controls_outer: controlsOuter,
40
+ loop,
41
+ centered,
42
+ slides_per_view: slidesPerView,
43
+ direction,
44
+ autoplay,
45
+ } = data;
46
+
47
+ const sliderProps = useMemo(
48
+ () => ({
49
+ id: `slider-${position}`,
50
+ displayAllTitle: t('blocks.home.display_all', 'View All'),
51
+ ...(showControls !== undefined && { showControls }),
52
+ ...(controlsOuter !== undefined && { controlsOuter }),
53
+ ...(loop !== undefined && { loop }),
54
+ ...(centered !== undefined && { centered }),
55
+ ...(slidesPerView !== undefined && { slidesPerView }),
56
+ ...(direction !== undefined && { direction }),
57
+ }),
58
+ [position, t, showControls, controlsOuter, loop, centered, slidesPerView, direction]
59
+ );
60
+
61
+ const loader = useCallback(
62
+ () =>
63
+ product.list({
64
+ source: products.source,
65
+ sourceValue: products.source_value,
66
+ }),
67
+ [products.source, products.source_value]
68
+ );
69
+
70
+ return (
71
+ <Suspense fallback={<ProductsSliderSkeleton />}>
72
+ <SallaProductsSlider
73
+ id={`best-offers-${position}-slider`}
74
+ loader={loader}
75
+ sliderProps={sliderProps}
76
+ {...(displayAllUrl ? { displayAllUrl } : {})}
77
+ {...(title ? { blockTitle: title } : {})}
78
+ {...(autoplay !== undefined ? { autoplay } : {})}
79
+ skeleton={<ProductsSliderSkeleton />}
80
+ >
81
+ {(product, index) => (
82
+ <ProductCard product={product} imagePriority={priority && index < 2} />
83
+ )}
84
+ </SallaProductsSlider>
85
+ </Suspense>
86
+ );
87
+ }
@@ -0,0 +1,38 @@
1
+ import { memo, useMemo } from 'react';
2
+ import { useTranslation } from '../../providers/I18nProvider';
3
+ import { getTwilightContext } from '../../twilight/context';
4
+ import type { HomeComponentData } from '../../types';
5
+ import { HomeComponentRenderer } from './HomePageRenderer';
6
+ import { useReorderButtons } from './reorder-hooks';
7
+
8
+ /**
9
+ * Twig's "order again" block (the Store app's ReorderProductsWidget): on the home page, for a
10
+ * signed-in customer of a store with Reorder on, the products they ordered before, drawn by the
11
+ * theme's own `products-slider` like the home's other product rows.
12
+ */
13
+ export const ReorderProducts = memo(function ReorderProducts() {
14
+ const { t } = useTranslation();
15
+ const { authToken, settings } = getTwilightContext();
16
+ // The platform lists `reorder` in the store's features exactly when the Reorder setting is on.
17
+ const enabled = !!authToken && !!settings?.store?.features?.includes('reorder');
18
+ const title = t('pages.orders.repurchase');
19
+ useReorderButtons(enabled);
20
+
21
+ const data = useMemo<HomeComponentData>(
22
+ () => ({
23
+ path: 'products-slider',
24
+ title,
25
+ display_all_url: '/products?source=reorder',
26
+ products: { source: 'reorder' },
27
+ }),
28
+ [title]
29
+ );
30
+
31
+ if (!enabled) return null;
32
+
33
+ return (
34
+ <div id="reorder-section" data-source="reorder">
35
+ <HomeComponentRenderer data={data} index={-1} priority={false} />
36
+ </div>
37
+ );
38
+ });