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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (390) hide show
  1. package/README.md +4 -3
  2. package/bin/twilight-check-manifest.mjs +16 -0
  3. package/dist/{AddToCartForm-B5C2M4OZ.js → AddToCartForm-P644O2QL.js} +4 -4
  4. package/dist/{AddToCartForm-B5C2M4OZ.js.map → AddToCartForm-P644O2QL.js.map} +1 -1
  5. package/dist/{BlogAuthorPage-GTF4UGSS.js → BlogAuthorPage-VWSO6MPY.js} +13 -11
  6. package/dist/BlogAuthorPage-VWSO6MPY.js.map +1 -0
  7. package/dist/{BlogCategoryPage-TN7Y4NBP.js → BlogCategoryPage-K2KSQ6T6.js} +13 -11
  8. package/dist/BlogCategoryPage-K2KSQ6T6.js.map +1 -0
  9. package/dist/{BlogPage-TDB3M3PI.js → BlogPage-XRM7VWOZ.js} +12 -10
  10. package/dist/BlogPage-XRM7VWOZ.js.map +1 -0
  11. package/dist/{BlogSinglePage-4A52Q5QW.js → BlogSinglePage-XIAXT54H.js} +12 -10
  12. package/dist/BlogSinglePage-XIAXT54H.js.map +1 -0
  13. package/dist/{BlogTagPage-TOVPBETA.js → BlogTagPage-IHOA267V.js} +13 -11
  14. package/dist/BlogTagPage-IHOA267V.js.map +1 -0
  15. package/dist/{BrandsPage-VUDGYSQI.js → BrandsPage-ZFEILRT3.js} +12 -10
  16. package/dist/BrandsPage-ZFEILRT3.js.map +1 -0
  17. package/dist/{Breadcrumb-CI3SN5YR.js → Breadcrumb-CVSISL2K.js} +12 -10
  18. package/dist/Breadcrumb-CVSISL2K.js.map +1 -0
  19. package/dist/{CartItem-BHLEURAB.js → CartItem-EOI7P3JA.js} +12 -10
  20. package/dist/CartItem-EOI7P3JA.js.map +1 -0
  21. package/dist/{CartSummary-QERVESYN.js → CartSummary-RZGUBDKJ.js} +12 -10
  22. package/dist/CartSummary-RZGUBDKJ.js.map +1 -0
  23. package/dist/{Copyright-RL6ALKRC.js → Copyright-5BLYSP24.js} +4 -4
  24. package/dist/{Copyright-RL6ALKRC.js.map → Copyright-5BLYSP24.js.map} +1 -1
  25. package/dist/{CustomerLayout-VRZUWGQK.js → CustomerLayout-6F4BJAMU.js} +12 -10
  26. package/dist/CustomerLayout-6F4BJAMU.js.map +1 -0
  27. package/dist/{Footer-ZWBZCMDL.js → Footer-NF2NLDLR.js} +6 -6
  28. package/dist/{Footer-ZWBZCMDL.js.map → Footer-NF2NLDLR.js.map} +1 -1
  29. package/dist/{Header-WNK4O66B.js → Header-6DQJ5X7J.js} +9 -7
  30. package/dist/{Header-WNK4O66B.js.map → Header-6DQJ5X7J.js.map} +1 -1
  31. package/dist/{LoyaltyPage-CGTHWJ76.js → LoyaltyPage-YHWHIWCS.js} +12 -10
  32. package/dist/LoyaltyPage-YHWHIWCS.js.map +1 -0
  33. package/dist/{LoyaltyPointsPage-QSU6K3PW.js → LoyaltyPointsPage-T6K3H5A5.js} +10 -3
  34. package/dist/LoyaltyPointsPage-T6K3H5A5.js.map +1 -0
  35. package/dist/{MainMenu-V3T7F32R.js → MainMenu-Y2TAM7DK.js} +4 -4
  36. package/dist/{MainMenu-V3T7F32R.js.map → MainMenu-Y2TAM7DK.js.map} +1 -1
  37. package/dist/{MasterLayout-XQF644LD.js → MasterLayout-GBEEFFPG.js} +12 -10
  38. package/dist/{MasterLayout-XQF644LD.js.map → MasterLayout-GBEEFFPG.js.map} +1 -1
  39. package/dist/{MobileMenu-U4BVQYKH.js → MobileMenu-RE5XVGZL.js} +12 -10
  40. package/dist/MobileMenu-RE5XVGZL.js.map +1 -0
  41. package/dist/{NotificationsPage-ZRRQY7TF.js → NotificationsPage-QAFWZAS3.js} +12 -10
  42. package/dist/NotificationsPage-QAFWZAS3.js.map +1 -0
  43. package/dist/{OrderSinglePage-O3XYVMG5.js → OrderSinglePage-T4PRXTKG.js} +12 -10
  44. package/dist/OrderSinglePage-T4PRXTKG.js.map +1 -0
  45. package/dist/{OrdersPage-CMXZF5MI.js → OrdersPage-3S6ZS56E.js} +12 -10
  46. package/dist/OrdersPage-3S6ZS56E.js.map +1 -0
  47. package/dist/{PageSingle-IIG2US43.js → PageSingle-W3UTXDOW.js} +4 -4
  48. package/dist/{PageSingle-IIG2US43.js.map → PageSingle-W3UTXDOW.js.map} +1 -1
  49. package/dist/{ProductDetails-65GDABI7.js → ProductDetails-X5A7TG3F.js} +14 -7
  50. package/dist/ProductDetails-X5A7TG3F.js.map +1 -0
  51. package/dist/{ProfilePage-SNJ4WXU4.js → ProfilePage-DUH3AILJ.js} +12 -10
  52. package/dist/ProfilePage-DUH3AILJ.js.map +1 -0
  53. package/dist/{ReviewsPage-UT7UT55J.js → ReviewsPage-2C2P7VS3.js} +4 -4
  54. package/dist/{ReviewsPage-UT7UT55J.js.map → ReviewsPage-2C2P7VS3.js.map} +1 -1
  55. package/dist/{SeoCartWidget-DBJAXJKT.js → SeoCartWidget-VYLGEW2I.js} +12 -10
  56. package/dist/SeoCartWidget-VYLGEW2I.js.map +1 -0
  57. package/dist/{SettingsPage-FLTIOPU5.js → SettingsPage-OZJEW6CW.js} +12 -10
  58. package/dist/SettingsPage-OZJEW6CW.js.map +1 -0
  59. package/dist/{StoreClosedNotification-ZT3QKDKS.js → StoreClosedNotification-QHIZBNXO.js} +13 -11
  60. package/dist/{StoreClosedNotification-ZT3QKDKS.js.map → StoreClosedNotification-QHIZBNXO.js.map} +1 -1
  61. package/dist/{TestimonialsPage-CU7HKU4O.js → TestimonialsPage-K6WJKKME.js} +4 -4
  62. package/dist/{TestimonialsPage-CU7HKU4O.js.map → TestimonialsPage-K6WJKKME.js.map} +1 -1
  63. package/dist/{ThankYouPage-5X6B4X3T.js → ThankYouPage-H6QQEI5R.js} +12 -10
  64. package/dist/ThankYouPage-H6QQEI5R.js.map +1 -0
  65. package/dist/{WalletPage-H2BKIDPA.js → WalletPage-G5IBGN6B.js} +12 -10
  66. package/dist/WalletPage-G5IBGN6B.js.map +1 -0
  67. package/dist/{WishlistPage-RS5UCIQ3.js → WishlistPage-AIOTAB6L.js} +12 -10
  68. package/dist/WishlistPage-AIOTAB6L.js.map +1 -0
  69. package/dist/api/advertisement.d.ts +33 -0
  70. package/dist/api/advertisement.d.ts.map +1 -0
  71. package/dist/api/advertisement.js +8 -0
  72. package/dist/api/advertisement.js.map +1 -0
  73. package/dist/boot/critical-style.d.ts +3 -0
  74. package/dist/boot/critical-style.d.ts.map +1 -0
  75. package/dist/boot/index.d.ts +15 -0
  76. package/dist/boot/index.d.ts.map +1 -0
  77. package/dist/boot.d.ts +1 -0
  78. package/dist/boot.js +69 -0
  79. package/dist/boot.js.map +1 -0
  80. package/dist/chunk-4FJGZQD3.js +6 -0
  81. package/dist/chunk-4FJGZQD3.js.map +1 -0
  82. package/dist/{chunk-SDFDS4Q3.js → chunk-BCJTF3V7.js} +20 -4
  83. package/dist/chunk-BCJTF3V7.js.map +1 -0
  84. package/dist/chunk-DBW7XXNE.js +103 -0
  85. package/dist/chunk-DBW7XXNE.js.map +1 -0
  86. package/dist/{chunk-22SH7OVF.js → chunk-E7WWO3V3.js} +3 -3
  87. package/dist/{chunk-22SH7OVF.js.map → chunk-E7WWO3V3.js.map} +1 -1
  88. package/dist/{chunk-SE5JYWOK.js → chunk-EZHXVHXY.js} +4 -4
  89. package/dist/{chunk-SE5JYWOK.js.map → chunk-EZHXVHXY.js.map} +1 -1
  90. package/dist/{chunk-JO7JWV34.js → chunk-FWYKOQV5.js} +5 -5
  91. package/dist/chunk-FWYKOQV5.js.map +1 -0
  92. package/dist/{chunk-JRNH3FUF.js → chunk-FYHF62KI.js} +4 -4
  93. package/dist/{chunk-JRNH3FUF.js.map → chunk-FYHF62KI.js.map} +1 -1
  94. package/dist/{chunk-UJKFBZFJ.js → chunk-GJ5IMPNY.js} +7 -7
  95. package/dist/{chunk-UJKFBZFJ.js.map → chunk-GJ5IMPNY.js.map} +1 -1
  96. package/dist/chunk-IVBOLREE.js +29 -0
  97. package/dist/chunk-IVBOLREE.js.map +1 -0
  98. package/dist/chunk-LWPKBVIE.js +14 -0
  99. package/dist/{chunk-7WAXN2PP.js.map → chunk-LWPKBVIE.js.map} +1 -1
  100. package/dist/{chunk-SX5MF2YA.js → chunk-N3BCZIGS.js} +4 -4
  101. package/dist/{chunk-SX5MF2YA.js.map → chunk-N3BCZIGS.js.map} +1 -1
  102. package/dist/{chunk-NZVR4OF7.js → chunk-NXQZ4RLM.js} +8 -8
  103. package/dist/chunk-NXQZ4RLM.js.map +1 -0
  104. package/dist/{chunk-NXQJA7D3.js → chunk-PCURRKIH.js} +3 -3
  105. package/dist/{chunk-NXQJA7D3.js.map → chunk-PCURRKIH.js.map} +1 -1
  106. package/dist/{chunk-4CZRUAZB.js → chunk-PU2LHFDQ.js} +10 -11
  107. package/dist/chunk-PU2LHFDQ.js.map +1 -0
  108. package/dist/{chunk-OWX2CPNQ.js → chunk-UQIVX4YA.js} +3 -3
  109. package/dist/{chunk-OWX2CPNQ.js.map → chunk-UQIVX4YA.js.map} +1 -1
  110. package/dist/chunk-X5NK4TZH.js +228 -0
  111. package/dist/chunk-X5NK4TZH.js.map +1 -0
  112. package/dist/{chunk-6SWDSRIL.js → chunk-XKNXYQ2K.js} +6 -6
  113. package/dist/{chunk-6SWDSRIL.js.map → chunk-XKNXYQ2K.js.map} +1 -1
  114. package/dist/{chunk-KHBSGD2D.js → chunk-XO7B3VWS.js} +3 -3
  115. package/dist/{chunk-KHBSGD2D.js.map → chunk-XO7B3VWS.js.map} +1 -1
  116. package/dist/cli/check-manifest.d.ts +10 -0
  117. package/dist/cli/check-manifest.d.ts.map +1 -0
  118. package/dist/cli/check-manifest.js +44 -0
  119. package/dist/cli/check-manifest.js.map +1 -0
  120. package/dist/components/cart/index.js +1 -1
  121. package/dist/components/common/index.js +2 -2
  122. package/dist/components/drawer/index.js +10 -8
  123. package/dist/components/home/index.js +12 -10
  124. package/dist/components/layout/Footer.d.ts.map +1 -1
  125. package/dist/components/layout/MasterLayout.d.ts.map +1 -1
  126. package/dist/components/layout/PageAdvertisements.d.ts +7 -0
  127. package/dist/components/layout/PageAdvertisements.d.ts.map +1 -0
  128. package/dist/components/layout/index.d.ts +1 -0
  129. package/dist/components/layout/index.d.ts.map +1 -1
  130. package/dist/components/layout/index.js +8 -1
  131. package/dist/components/product/index.js +11 -9
  132. package/dist/components/store/index.js +12 -10
  133. package/dist/components/store/index.js.map +1 -1
  134. package/dist/{document-class-67HTP45G.js → document-class-FRL7JHWD.js} +6 -5
  135. package/dist/{document-class-67HTP45G.js.map → document-class-FRL7JHWD.js.map} +1 -1
  136. package/dist/hooks/index.js +10 -8
  137. package/dist/hooks/useGtm.js +10 -8
  138. package/dist/hooks/useMoney.js +10 -8
  139. package/dist/hooks/useStore.js +10 -8
  140. package/dist/index.js +10 -8
  141. package/dist/index.js.map +1 -1
  142. package/dist/{not-found-QTHBFPNT.js → not-found-E6AR4CZ4.js} +12 -10
  143. package/dist/not-found-E6AR4CZ4.js.map +1 -0
  144. package/dist/providers/index.js +10 -8
  145. package/dist/routes/account/orders.js +1 -1
  146. package/dist/routes/account.js +6 -6
  147. package/dist/routes/blog.js +5 -5
  148. package/dist/routes/brands.js +1 -1
  149. package/dist/routes/cart.js +14 -12
  150. package/dist/routes/cart.js.map +1 -1
  151. package/dist/routes/home.js +13 -11
  152. package/dist/routes/loyalty.js +2 -2
  153. package/dist/routes/page.js +1 -1
  154. package/dist/routes/product-listing.js +12 -10
  155. package/dist/routes/product-listing.js.map +1 -1
  156. package/dist/routes/product.js +12 -10
  157. package/dist/routes/product.js.map +1 -1
  158. package/dist/routes/root/beforeLoad.d.ts +2 -2
  159. package/dist/routes/root/beforeLoad.d.ts.map +1 -1
  160. package/dist/routes/root/head.d.ts.map +1 -1
  161. package/dist/routes/testimonials.js +2 -2
  162. package/dist/routes/thank-you.js +1 -1
  163. package/dist/skeleton.js +13 -11
  164. package/dist/tanstack/document-class.d.ts.map +1 -1
  165. package/dist/tanstack/index.d.ts +1 -0
  166. package/dist/tanstack/index.d.ts.map +1 -1
  167. package/dist/tanstack/stylesheet.d.ts +15 -0
  168. package/dist/tanstack/stylesheet.d.ts.map +1 -0
  169. package/dist/tanstack.js +33 -4
  170. package/dist/tanstack.js.map +1 -1
  171. package/dist/utils/index.js +2 -2
  172. package/dist/vite/index.d.ts +1 -1
  173. package/dist/vite/index.d.ts.map +1 -1
  174. package/dist/vite/index.js +14 -25
  175. package/dist/vite/index.js.map +1 -1
  176. package/dist/vite/plugins/twilight-manifest.d.ts +23 -0
  177. package/dist/vite/plugins/twilight-manifest.d.ts.map +1 -1
  178. package/dist/vite/plugins/twilight-schema.plugin.d.ts.map +1 -1
  179. package/package.json +16 -3
  180. package/tailwind/components/Component.tsx.txt +21 -0
  181. package/tailwind/components/ComponentRegistry.ts.txt +66 -0
  182. package/tailwind/components/cart/CartItem.tsx.txt +211 -0
  183. package/tailwind/components/cart/CartSummary.tsx.txt +269 -0
  184. package/tailwind/components/cart/PreOrderNotification.tsx.txt +16 -0
  185. package/tailwind/components/cart/PriceQuoteButton.tsx.txt +21 -0
  186. package/tailwind/components/cart/SeoCartWidget.tsx.txt +99 -0
  187. package/tailwind/components/cart/index.ts.txt +11 -0
  188. package/tailwind/components/cart/lazy.ts.txt +15 -0
  189. package/tailwind/components/collapse/Collapse.tsx.txt +95 -0
  190. package/tailwind/components/collapse/index.ts.txt +41 -0
  191. package/tailwind/components/common/Breadcrumb.tsx.txt +27 -0
  192. package/tailwind/components/common/BreadcrumbJsonLd.tsx.txt +40 -0
  193. package/tailwind/components/common/BreadcrumbTrail.tsx.txt +97 -0
  194. package/tailwind/components/common/CurrencySymbol.tsx.txt +8 -0
  195. package/tailwind/components/common/DeferredData.tsx.txt +122 -0
  196. package/tailwind/components/common/ErrorPage.tsx.txt +69 -0
  197. package/tailwind/components/common/Image.tsx.txt +119 -0
  198. package/tailwind/components/common/Link.tsx.txt +36 -0
  199. package/tailwind/components/common/Link.types.ts.txt +85 -0
  200. package/tailwind/components/common/NoContent.tsx.txt +27 -0
  201. package/tailwind/components/common/RenderWhenVisible.tsx.txt +92 -0
  202. package/tailwind/components/common/index.ts.txt +42 -0
  203. package/tailwind/components/common/lazy.ts.txt +28 -0
  204. package/tailwind/components/defineComponent.ts.txt +32 -0
  205. package/tailwind/components/drawer/Drawer.tsx.txt +219 -0
  206. package/tailwind/components/drawer/index.ts.txt +38 -0
  207. package/tailwind/components/dropdown/Dropdown.tsx.txt +166 -0
  208. package/tailwind/components/dropdown/index.ts.txt +40 -0
  209. package/tailwind/components/home/BundleComponent.tsx.txt +35 -0
  210. package/tailwind/components/home/ComponentErrorBoundary.tsx.txt +98 -0
  211. package/tailwind/components/home/FeaturedProductsStyle1.tsx.txt +125 -0
  212. package/tailwind/components/home/FeaturedProductsStyle2.tsx.txt +127 -0
  213. package/tailwind/components/home/FeaturedProductsStyle3.tsx.txt +71 -0
  214. package/tailwind/components/home/FixedBanner.tsx.txt +68 -0
  215. package/tailwind/components/home/FixedProducts.tsx.txt +72 -0
  216. package/tailwind/components/home/HomePageRenderer.tsx.txt +284 -0
  217. package/tailwind/components/home/PageBlocks.tsx.txt +24 -0
  218. package/tailwind/components/home/ParallaxBackground.tsx.txt +50 -0
  219. package/tailwind/components/home/PhotosSlider.tsx.txt +61 -0
  220. package/tailwind/components/home/ProductsSlider.tsx.txt +87 -0
  221. package/tailwind/components/home/ReorderProducts.tsx.txt +38 -0
  222. package/tailwind/components/home/SquarePhotos.tsx.txt +68 -0
  223. package/tailwind/components/home/StoreFeatures.tsx.txt +34 -0
  224. package/tailwind/components/home/Testimonials.tsx.txt +36 -0
  225. package/tailwind/components/home/Youtube.tsx.txt +48 -0
  226. package/tailwind/components/home/index.ts.txt +50 -0
  227. package/tailwind/components/home/lazy.ts.txt +17 -0
  228. package/tailwind/components/home/register.ts.txt +44 -0
  229. package/tailwind/components/home/reorder-hooks.ts.txt +52 -0
  230. package/tailwind/components/home/types.ts.txt +4 -0
  231. package/tailwind/components/layout/Copyright.tsx.txt +23 -0
  232. package/tailwind/components/layout/CustomerLayout.tsx.txt +91 -0
  233. package/tailwind/components/layout/Footer.tsx.txt +341 -0
  234. package/tailwind/components/layout/Header.tsx.txt +154 -0
  235. package/tailwind/components/layout/MasterLayout.tsx.txt +62 -0
  236. package/tailwind/components/layout/PageAdvertisements.tsx.txt +131 -0
  237. package/tailwind/components/layout/WidgetHead.tsx.txt +53 -0
  238. package/tailwind/components/layout/index.ts.txt +27 -0
  239. package/tailwind/components/layout/lazy.ts.txt +25 -0
  240. package/tailwind/components/layout/types.ts.txt +11 -0
  241. package/tailwind/components/modal/ImageModal.tsx.txt +70 -0
  242. package/tailwind/components/modal/Modal.tsx.txt +245 -0
  243. package/tailwind/components/modal/index.ts.txt +39 -0
  244. package/tailwind/components/modal/lazy.ts.txt +10 -0
  245. package/tailwind/components/navigation/MainMenu.tsx.txt +94 -0
  246. package/tailwind/components/navigation/MobileMenu.tsx.txt +160 -0
  247. package/tailwind/components/navigation/index.ts.txt +4 -0
  248. package/tailwind/components/navigation/lazy.ts.txt +11 -0
  249. package/tailwind/components/product/AddToCartForm.tsx.txt +365 -0
  250. package/tailwind/components/product/ProductCard.tsx.txt +305 -0
  251. package/tailwind/components/product/ProductDetails.tsx.txt +267 -0
  252. package/tailwind/components/product/ProductGallery.tsx.txt +234 -0
  253. package/tailwind/components/product/index.ts.txt +15 -0
  254. package/tailwind/components/product/lazy.ts.txt +9 -0
  255. package/tailwind/components/snippets/AppsSnippets.tsx.txt +225 -0
  256. package/tailwind/components/snippets/SallaSdkScripts.tsx.txt +14 -0
  257. package/tailwind/components/snippets/index.ts.txt +4 -0
  258. package/tailwind/components/store/StoreClosedNotification.tsx.txt +73 -0
  259. package/tailwind/components/store/index.ts.txt +8 -0
  260. package/tailwind/components/store/lazy.ts.txt +8 -0
  261. package/tailwind/components/toast/Toaster.tsx.txt +177 -0
  262. package/tailwind/components/toast/index.ts.txt +10 -0
  263. package/tailwind/components/toast/types.ts.txt +38 -0
  264. package/tailwind/components/toast/useToast.ts.txt +61 -0
  265. package/tailwind/components/tracking/GoogleTag.tsx.txt +77 -0
  266. package/tailwind/components/tracking/SiftSnippet.tsx.txt +49 -0
  267. package/tailwind/components/tracking/TwilightBundles.tsx.txt +67 -0
  268. package/tailwind/components/tracking/index.ts.txt +11 -0
  269. package/tailwind/components/tracking/lazy.ts.txt +11 -0
  270. package/tailwind/components/useComponent.ts.txt +12 -0
  271. package/tailwind/routes/account/index.tsx.txt +16 -0
  272. package/tailwind/routes/account/notifications/NotificationsPage.tsx.txt +54 -0
  273. package/tailwind/routes/account/notifications/index.tsx.txt +39 -0
  274. package/tailwind/routes/account/notifications/loader.ts.txt +22 -0
  275. package/tailwind/routes/account/notifications/types.ts.txt +18 -0
  276. package/tailwind/routes/account/orders/OrderSinglePage.tsx.txt +181 -0
  277. package/tailwind/routes/account/orders/OrdersPage.tsx.txt +28 -0
  278. package/tailwind/routes/account/orders/index.tsx.txt +86 -0
  279. package/tailwind/routes/account/orders/loader.ts.txt +66 -0
  280. package/tailwind/routes/account/orders/single/OrderActions.tsx.txt +197 -0
  281. package/tailwind/routes/account/orders/single/OrderHeader.tsx.txt +143 -0
  282. package/tailwind/routes/account/orders/single/OrderItemRow.tsx.txt +209 -0
  283. package/tailwind/routes/account/orders/single/OrderSections.tsx.txt +215 -0
  284. package/tailwind/routes/account/orders/single/types.ts.txt +38 -0
  285. package/tailwind/routes/account/orders/types.ts.txt +29 -0
  286. package/tailwind/routes/account/profile/ProfilePage.tsx.txt +30 -0
  287. package/tailwind/routes/account/profile/index.tsx.txt +39 -0
  288. package/tailwind/routes/account/profile/loader.ts.txt +16 -0
  289. package/tailwind/routes/account/profile/types.ts.txt +5 -0
  290. package/tailwind/routes/account/settings/SettingsPage.tsx.txt +13 -0
  291. package/tailwind/routes/account/settings/index.tsx.txt +39 -0
  292. package/tailwind/routes/account/settings/loader.ts.txt +13 -0
  293. package/tailwind/routes/account/settings/types.ts.txt +5 -0
  294. package/tailwind/routes/account/types.ts.txt +320 -0
  295. package/tailwind/routes/account/wallet/WalletPage.tsx.txt +195 -0
  296. package/tailwind/routes/account/wallet/index.tsx.txt +38 -0
  297. package/tailwind/routes/account/wallet/loader.ts.txt +27 -0
  298. package/tailwind/routes/account/wallet/types.ts.txt +11 -0
  299. package/tailwind/routes/account/wishlist/WishlistPage.tsx.txt +314 -0
  300. package/tailwind/routes/account/wishlist/WishlistShare.tsx.txt +248 -0
  301. package/tailwind/routes/account/wishlist/index.tsx.txt +40 -0
  302. package/tailwind/routes/account/wishlist/loader.ts.txt +26 -0
  303. package/tailwind/routes/account/wishlist/types.ts.txt +9 -0
  304. package/tailwind/routes/blog/BlogAuthorPage.tsx.txt +82 -0
  305. package/tailwind/routes/blog/BlogCategoryPage.tsx.txt +84 -0
  306. package/tailwind/routes/blog/BlogPage.tsx.txt +191 -0
  307. package/tailwind/routes/blog/BlogSinglePage.tsx.txt +168 -0
  308. package/tailwind/routes/blog/BlogTagPage.tsx.txt +84 -0
  309. package/tailwind/routes/blog/BlogTags.tsx.txt +54 -0
  310. package/tailwind/routes/blog/components.tsx.txt +87 -0
  311. package/tailwind/routes/blog/index.tsx.txt +238 -0
  312. package/tailwind/routes/blog/loader.ts.txt +173 -0
  313. package/tailwind/routes/blog/types.ts.txt +106 -0
  314. package/tailwind/routes/brands/BrandsPage.tsx.txt +179 -0
  315. package/tailwind/routes/brands/index.tsx.txt +47 -0
  316. package/tailwind/routes/brands/loader.ts.txt +14 -0
  317. package/tailwind/routes/brands/types.ts.txt +53 -0
  318. package/tailwind/routes/cart/CartPage.tsx.txt +112 -0
  319. package/tailwind/routes/cart/index.tsx.txt +39 -0
  320. package/tailwind/routes/cart/loader.ts.txt +15 -0
  321. package/tailwind/routes/cart/types.ts.txt +6 -0
  322. package/tailwind/routes/home/HomePage.tsx.txt +53 -0
  323. package/tailwind/routes/home/index.tsx.txt +56 -0
  324. package/tailwind/routes/home/loader.ts.txt +24 -0
  325. package/tailwind/routes/home/types.ts.txt +11 -0
  326. package/tailwind/routes/index.ts.txt +94 -0
  327. package/tailwind/routes/lazy.ts.txt +105 -0
  328. package/tailwind/routes/loyalty/LoyaltyPage.tsx.txt +17 -0
  329. package/tailwind/routes/loyalty/LoyaltyPointsPage.tsx.txt +11 -0
  330. package/tailwind/routes/loyalty/index.tsx.txt +81 -0
  331. package/tailwind/routes/loyalty/loader.ts.txt +53 -0
  332. package/tailwind/routes/loyalty/types.ts.txt +109 -0
  333. package/tailwind/routes/not-found.tsx.txt +50 -0
  334. package/tailwind/routes/page/PageSingle.tsx.txt +34 -0
  335. package/tailwind/routes/page/index.tsx.txt +60 -0
  336. package/tailwind/routes/page/loader.ts.txt +19 -0
  337. package/tailwind/routes/page/types.ts.txt +34 -0
  338. package/tailwind/routes/product/ProductPage.tsx.txt +77 -0
  339. package/tailwind/routes/product/index.tsx.txt +157 -0
  340. package/tailwind/routes/product/loader.ts.txt +35 -0
  341. package/tailwind/routes/product/types.ts.txt +6 -0
  342. package/tailwind/routes/product-listing/ProductListPage.tsx.txt +321 -0
  343. package/tailwind/routes/product-listing/index.tsx.txt +95 -0
  344. package/tailwind/routes/product-listing/loader.ts.txt +323 -0
  345. package/tailwind/routes/product-listing/types.ts.txt +96 -0
  346. package/tailwind/routes/product-listing/useAppliedFilters.ts.txt +130 -0
  347. package/tailwind/routes/product-short-link.ts.txt +104 -0
  348. package/tailwind/routes/root/beforeLoad.ts.txt +354 -0
  349. package/tailwind/routes/root/head.ts.txt +424 -0
  350. package/tailwind/routes/root/index.ts.txt +2 -0
  351. package/tailwind/routes/seo-redirects.ts.txt +82 -0
  352. package/tailwind/routes/testimonials/ReviewsPage.tsx.txt +12 -0
  353. package/tailwind/routes/testimonials/TestimonialsPage.tsx.txt +80 -0
  354. package/tailwind/routes/testimonials/index.tsx.txt +80 -0
  355. package/tailwind/routes/testimonials/loader.ts.txt +29 -0
  356. package/tailwind/routes/testimonials/types.ts.txt +9 -0
  357. package/tailwind/routes/thank-you/ThankYouPage.tsx.txt +241 -0
  358. package/tailwind/routes/thank-you/index.tsx.txt +49 -0
  359. package/tailwind/routes/thank-you/loader.ts.txt +40 -0
  360. package/tailwind/routes/thank-you/types.ts.txt +16 -0
  361. package/tailwind/routes/types.ts.txt +41 -0
  362. package/dist/BlogAuthorPage-GTF4UGSS.js.map +0 -1
  363. package/dist/BlogCategoryPage-TN7Y4NBP.js.map +0 -1
  364. package/dist/BlogPage-TDB3M3PI.js.map +0 -1
  365. package/dist/BlogSinglePage-4A52Q5QW.js.map +0 -1
  366. package/dist/BlogTagPage-TOVPBETA.js.map +0 -1
  367. package/dist/BrandsPage-VUDGYSQI.js.map +0 -1
  368. package/dist/Breadcrumb-CI3SN5YR.js.map +0 -1
  369. package/dist/CartItem-BHLEURAB.js.map +0 -1
  370. package/dist/CartSummary-QERVESYN.js.map +0 -1
  371. package/dist/CustomerLayout-VRZUWGQK.js.map +0 -1
  372. package/dist/LoyaltyPage-CGTHWJ76.js.map +0 -1
  373. package/dist/LoyaltyPointsPage-QSU6K3PW.js.map +0 -1
  374. package/dist/MobileMenu-U4BVQYKH.js.map +0 -1
  375. package/dist/NotificationsPage-ZRRQY7TF.js.map +0 -1
  376. package/dist/OrderSinglePage-O3XYVMG5.js.map +0 -1
  377. package/dist/OrdersPage-CMXZF5MI.js.map +0 -1
  378. package/dist/ProductDetails-65GDABI7.js.map +0 -1
  379. package/dist/ProfilePage-SNJ4WXU4.js.map +0 -1
  380. package/dist/SeoCartWidget-DBJAXJKT.js.map +0 -1
  381. package/dist/SettingsPage-FLTIOPU5.js.map +0 -1
  382. package/dist/ThankYouPage-5X6B4X3T.js.map +0 -1
  383. package/dist/WalletPage-H2BKIDPA.js.map +0 -1
  384. package/dist/WishlistPage-RS5UCIQ3.js.map +0 -1
  385. package/dist/chunk-4CZRUAZB.js.map +0 -1
  386. package/dist/chunk-7WAXN2PP.js +0 -14
  387. package/dist/chunk-JO7JWV34.js.map +0 -1
  388. package/dist/chunk-NZVR4OF7.js.map +0 -1
  389. package/dist/chunk-SDFDS4Q3.js.map +0 -1
  390. package/dist/not-found-QTHBFPNT.js.map +0 -1
@@ -0,0 +1,53 @@
1
+ import { useEffect } from 'react';
2
+ import { useTwilight } from '../../providers/twilight-context';
3
+ import { HookSlot } from '../../hooks/HookSlot';
4
+
5
+ /**
6
+ * Pushes the equal-height product card configuration to the GTM dataLayer.
7
+ * Mirrors PHP `HeadWidget::run()` -> `GoogleTagManager::set('equalHeight', ...)`.
8
+ *
9
+ * @param settings - Flat settings map from the store (keyed by dot-path strings).
10
+ */
11
+ export function pushEqualHeightConfig(settings: Record<string, unknown>): void {
12
+ const isEnabled = settings['products::card.equal-height.enabled'];
13
+ if (isEnabled === undefined || isEnabled === null) return;
14
+
15
+ window.dataLayer = window.dataLayer ?? [];
16
+ window.dataLayer.push({
17
+ equalHeight: {
18
+ isEnabled: Boolean(isEnabled),
19
+ max: Number(settings['products::card.equal-height.max-pixel'] ?? 300),
20
+ classType: String(settings['products::card.equal-height.type'] ?? 'min'),
21
+ },
22
+ });
23
+ }
24
+
25
+ /**
26
+ * WidgetHead - Client-side head infrastructure widget.
27
+ *
28
+ * Handles runtime-only head operations that cannot be done in SSR head():
29
+ * - Pushes `equalHeight` config to GTM dataLayer (requires window).
30
+ * - Exposes `head:start`, `head`, and `head:end` HookSlots for theme customisation.
31
+ *
32
+ * Static head elements (favicon, msapplication tiles, theme colors, hreflang, custom CSS/JS)
33
+ * are handled in __root.tsx head() function.
34
+ *
35
+ * Renders no visible UI.
36
+ */
37
+ export function WidgetHead() {
38
+ const { store } = useTwilight();
39
+
40
+ // ── equalHeight → GTM dataLayer ───────────────────────────────────────────
41
+ useEffect(() => {
42
+ const settings = (store.settings as unknown as Record<string, unknown>) ?? {};
43
+ pushEqualHeightConfig(settings);
44
+ }, [store.id, store.settings]);
45
+
46
+ return (
47
+ <>
48
+ <HookSlot name="head:start" ssr />
49
+ <HookSlot name="head" ssr />
50
+ <HookSlot name="head:end" ssr />
51
+ </>
52
+ );
53
+ }
@@ -0,0 +1,27 @@
1
+ import {
2
+ LazyHeader,
3
+ LazyFooter,
4
+ LazyCopyright,
5
+ LazyMasterLayout,
6
+ LazyWidgetHead,
7
+ LazyCustomerLayout,
8
+ } from './lazy';
9
+
10
+ // Lazy-loaded layout components (code-split)
11
+ export {
12
+ LazyHeader as Header,
13
+ LazyFooter as Footer,
14
+ LazyCopyright as Copyright,
15
+ LazyMasterLayout as MasterLayout,
16
+ LazyWidgetHead as WidgetHead,
17
+ LazyCustomerLayout as CustomerLayout,
18
+ };
19
+
20
+ // Eager: the header renders it on the server, so it can't suspend.
21
+ export { PageAdvertisements } from './PageAdvertisements';
22
+
23
+ // Export utilities directly (not components)
24
+ export { pushEqualHeightConfig } from './WidgetHead';
25
+
26
+ // Shared types
27
+ export type { LayoutProps } from './types';
@@ -0,0 +1,25 @@
1
+ import { lazy } from 'react';
2
+
3
+ /**
4
+ * Lazy-loaded layout components
5
+ * Use these for code-splitting layout sections
6
+ */
7
+ export const LazyHeader = lazy(() => import('./Header').then((m) => ({ default: m.Header })));
8
+
9
+ export const LazyFooter = lazy(() => import('./Footer').then((m) => ({ default: m.Footer })));
10
+
11
+ export const LazyCopyright = lazy(() =>
12
+ import('./Copyright').then((m) => ({ default: m.Copyright }))
13
+ );
14
+
15
+ export const LazyMasterLayout = lazy(() =>
16
+ import('./MasterLayout').then((m) => ({ default: m.MasterLayout }))
17
+ );
18
+
19
+ export const LazyWidgetHead = lazy(() =>
20
+ import('./WidgetHead').then((m) => ({ default: m.WidgetHead }))
21
+ );
22
+
23
+ export const LazyCustomerLayout = lazy(() =>
24
+ import('./CustomerLayout').then((m) => ({ default: m.CustomerLayout }))
25
+ );
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Layout Component Types
3
+ * Only types shared across multiple components.
4
+ */
5
+
6
+ import type { ReactNode } from 'react';
7
+
8
+ /** Base props for layout components with children */
9
+ export interface LayoutProps {
10
+ children: ReactNode;
11
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * ImageModal - Modal for displaying images in full scale
3
+ *
4
+ * Used for previewing images like tax certificates, etc.
5
+ *
6
+ * @example
7
+ * ```tsx
8
+ * const [isOpen, setIsOpen] = useState(false);
9
+ * const [imageSrc, setImageSrc] = useState('');
10
+ *
11
+ * <a onClick={() => { setImageSrc('/images/tax-certificate.jpg'); setIsOpen(true); }}>
12
+ * <img src="/images/tax-thumb.png" />
13
+ * </a>
14
+ *
15
+ * <ImageModal
16
+ * isOpen={isOpen}
17
+ * onClose={() => setIsOpen(false)}
18
+ * src={imageSrc}
19
+ * alt="Tax Certificate"
20
+ * />
21
+ * ```
22
+ */
23
+
24
+ import { useState, type ImgHTMLAttributes } from 'react';
25
+ import { Modal } from './Modal';
26
+
27
+ export interface ImageModalProps {
28
+ isOpen: boolean;
29
+ onClose: () => void;
30
+ /** Image source URL */
31
+ src: string;
32
+ /** Alt text for the image */
33
+ alt?: string;
34
+ /** Title shown in modal header */
35
+ title?: string;
36
+ /** Additional props for the img element */
37
+ imgProps?: Omit<ImgHTMLAttributes<HTMLImageElement>, 'src' | 'alt'>;
38
+ }
39
+
40
+ function ModalImage({ src, alt, imgProps }: Pick<ImageModalProps, 'src' | 'alt' | 'imgProps'>) {
41
+ const [isLoaded, setIsLoaded] = useState(false);
42
+
43
+ return (
44
+ <>
45
+ {!isLoaded && <div className="animate-pulse bg-gray-200 w-full h-64 rounded" />}
46
+ <img
47
+ src={src}
48
+ alt={alt}
49
+ className={`max-w-full max-h-[70vh] object-contain ${isLoaded ? 'block' : 'hidden'}`}
50
+ onLoad={() => setIsLoaded(true)}
51
+ {...imgProps}
52
+ />
53
+ </>
54
+ );
55
+ }
56
+
57
+ export function ImageModal({ isOpen, onClose, src, alt = '', title, imgProps }: ImageModalProps) {
58
+ if (!src) return null;
59
+
60
+ return (
61
+ <Modal isOpen={isOpen} onClose={onClose} size="lg" position="center">
62
+ <Modal.Header onClose={onClose} showCloseButton>
63
+ {title || ''}
64
+ </Modal.Header>
65
+ <Modal.Body className="flex items-center justify-center min-h-64">
66
+ <ModalImage key={src} src={src} alt={alt} imgProps={imgProps} />
67
+ </Modal.Body>
68
+ </Modal>
69
+ );
70
+ }
@@ -0,0 +1,245 @@
1
+ /**
2
+ * Modal Component
3
+ *
4
+ * A controlled modal dialog with backdrop, close button, and keyboard support.
5
+ *
6
+ * @example
7
+ * ```tsx
8
+ * function MyComponent() {
9
+ * const [isOpen, setIsOpen] = useState(false);
10
+ *
11
+ * return (
12
+ * <>
13
+ * <button onClick={() => setIsOpen(true)}>Open Modal</button>
14
+ * <Modal isOpen={isOpen} onClose={() => setIsOpen(false)}>
15
+ * <Modal.Header>Modal Title</Modal.Header>
16
+ * <Modal.Body>Modal content here</Modal.Body>
17
+ * <Modal.Footer>
18
+ * <Button onClick={() => setIsOpen(false)}>Close</Button>
19
+ * </Modal.Footer>
20
+ * </Modal>
21
+ * </>
22
+ * );
23
+ * }
24
+ * ```
25
+ */
26
+
27
+ import React, { useEffect, useCallback, useRef, type ReactNode, type HTMLAttributes } from 'react';
28
+ import { createPortal } from 'react-dom';
29
+
30
+ export interface ModalProps {
31
+ isOpen: boolean;
32
+ onClose: () => void;
33
+ children: ReactNode;
34
+ /**
35
+ * Close modal when clicking backdrop
36
+ * @default true
37
+ */
38
+ closeOnBackdropClick?: boolean;
39
+ /**
40
+ * Close modal on Escape key
41
+ * @default true
42
+ */
43
+ closeOnEscape?: boolean;
44
+ /**
45
+ * Prevent body scroll when modal is open
46
+ * @default true
47
+ */
48
+ preventBodyScroll?: boolean;
49
+ /**
50
+ * Size of the modal
51
+ * @default 'md'
52
+ */
53
+ size?: 'sm' | 'md' | 'lg' | 'xl' | 'full';
54
+ /**
55
+ * Position of the modal
56
+ * @default 'center'
57
+ */
58
+ position?: 'center' | 'top';
59
+ /**
60
+ * Additional className for the modal content
61
+ */
62
+ className?: string;
63
+ }
64
+
65
+ const sizeClasses = {
66
+ sm: 'max-w-sm',
67
+ md: 'max-w-md',
68
+ lg: 'max-w-lg',
69
+ xl: 'max-w-xl',
70
+ full: 'max-w-full mx-4',
71
+ };
72
+
73
+ const positionClasses = {
74
+ center: 'items-center',
75
+ top: 'items-start pt-10',
76
+ };
77
+
78
+ export function Modal({
79
+ isOpen,
80
+ onClose,
81
+ children,
82
+ closeOnBackdropClick = true,
83
+ closeOnEscape = true,
84
+ preventBodyScroll = true,
85
+ size = 'md',
86
+ position = 'center',
87
+ className,
88
+ }: ModalProps) {
89
+ const modalRef = useRef<HTMLDivElement>(null);
90
+ const previousActiveElement = useRef<HTMLElement | null>(null);
91
+
92
+ // Handle Escape key
93
+ const handleKeyDown = useCallback(
94
+ (event: KeyboardEvent) => {
95
+ if (event.key === 'Escape' && closeOnEscape) {
96
+ onClose();
97
+ }
98
+ },
99
+ [closeOnEscape, onClose]
100
+ );
101
+
102
+ // Bound to the backdrop, which covers the wrapper and is a sibling of the panel
103
+ const handleBackdropClick = useCallback(
104
+ (event: React.MouseEvent) => {
105
+ if (closeOnBackdropClick && event.target === event.currentTarget) {
106
+ onClose();
107
+ }
108
+ },
109
+ [closeOnBackdropClick, onClose]
110
+ );
111
+
112
+ // Focus management and body scroll
113
+ useEffect(() => {
114
+ if (isOpen) {
115
+ // Store the previously focused element
116
+ previousActiveElement.current = document.activeElement as HTMLElement;
117
+
118
+ // Focus the modal
119
+ modalRef.current?.focus();
120
+
121
+ // Prevent body scroll
122
+ if (preventBodyScroll) {
123
+ document.body.classList.add('menu-opened');
124
+ }
125
+
126
+ // Add keyboard listener
127
+ document.addEventListener('keydown', handleKeyDown);
128
+ }
129
+
130
+ return () => {
131
+ document.removeEventListener('keydown', handleKeyDown);
132
+
133
+ // Restore body scroll
134
+ if (preventBodyScroll) {
135
+ document.body.classList.remove('menu-opened');
136
+ }
137
+
138
+ // Restore focus
139
+ previousActiveElement.current?.focus();
140
+ };
141
+ }, [isOpen, handleKeyDown, preventBodyScroll]);
142
+
143
+ if (!isOpen || typeof document === 'undefined') {
144
+ return null;
145
+ }
146
+
147
+ return createPortal(
148
+ <div
149
+ className={`fixed inset-0 z-50 flex justify-center ${positionClasses[position]}`}
150
+ aria-modal="true"
151
+ role="dialog"
152
+ >
153
+ {/* Backdrop */}
154
+ <div
155
+ className="absolute inset-0 bg-black/50 transition-opacity"
156
+ role="presentation"
157
+ onClick={handleBackdropClick}
158
+ onKeyDown={(e) => {
159
+ if ((e.key === 'Enter' || e.key === ' ') && closeOnBackdropClick) {
160
+ e.preventDefault();
161
+ onClose();
162
+ }
163
+ }}
164
+ />
165
+
166
+ {/* Modal Content */}
167
+ <div
168
+ ref={modalRef}
169
+ tabIndex={-1}
170
+ className={`relative z-10 w-full rounded-lg bg-white shadow-xl transform transition-all duration-200 ease-out ${sizeClasses[size]} ${isOpen ? 'scale-100 opacity-100' : 'scale-95 opacity-0'} ${className || ''}`}
171
+ >
172
+ {children}
173
+ </div>
174
+ </div>,
175
+ document.body
176
+ );
177
+ }
178
+
179
+ // Modal Sub-components
180
+ export interface ModalHeaderProps extends HTMLAttributes<HTMLDivElement> {
181
+ children: ReactNode;
182
+ showCloseButton?: boolean;
183
+ onClose?: () => void;
184
+ }
185
+
186
+ Modal.Header = function ModalHeader({
187
+ children,
188
+ showCloseButton = true,
189
+ onClose,
190
+ className,
191
+ ...props
192
+ }: ModalHeaderProps) {
193
+ return (
194
+ <div
195
+ className={`flex items-center justify-between border-b border-gray-200 px-6 py-4 ${className || ''}`}
196
+ {...props}
197
+ >
198
+ <h2 className="text-lg font-semibold text-gray-900">{children}</h2>
199
+ {showCloseButton && onClose && (
200
+ <button
201
+ type="button"
202
+ onClick={onClose}
203
+ className="rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-gray-500"
204
+ >
205
+ <span className="sr-only">Close</span>
206
+ <svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
207
+ <path
208
+ strokeLinecap="round"
209
+ strokeLinejoin="round"
210
+ strokeWidth={2}
211
+ d="M6 18L18 6M6 6l12 12"
212
+ />
213
+ </svg>
214
+ </button>
215
+ )}
216
+ </div>
217
+ );
218
+ };
219
+
220
+ export interface ModalBodyProps extends HTMLAttributes<HTMLDivElement> {
221
+ children: ReactNode;
222
+ }
223
+
224
+ Modal.Body = function ModalBody({ children, className, ...props }: ModalBodyProps) {
225
+ return (
226
+ <div className={`px-6 py-4 ${className || ''}`} {...props}>
227
+ {children}
228
+ </div>
229
+ );
230
+ };
231
+
232
+ export interface ModalFooterProps extends HTMLAttributes<HTMLDivElement> {
233
+ children: ReactNode;
234
+ }
235
+
236
+ Modal.Footer = function ModalFooter({ children, className, ...props }: ModalFooterProps) {
237
+ return (
238
+ <div
239
+ className={`flex items-center justify-end gap-3 border-t border-gray-200 px-6 py-4 ${className || ''}`}
240
+ {...props}
241
+ >
242
+ {children}
243
+ </div>
244
+ );
245
+ };
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Modal Component System
3
+ *
4
+ * Controlled modal dialog with composable parts.
5
+ * Eager-loaded because Modal uses compound component pattern (Modal.Header, Modal.Body).
6
+ * ImageModal is lazy-loaded since it's a standalone component.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * import { Modal } from '@salla.sa/twilight-theme-engine/components/modal';
11
+ *
12
+ * function ConfirmDialog({ isOpen, onClose, onConfirm }) {
13
+ * return (
14
+ * <Modal isOpen={isOpen} onClose={onClose} size="sm">
15
+ * <Modal.Header onClose={onClose}>Confirm Action</Modal.Header>
16
+ * <Modal.Body>Are you sure you want to proceed?</Modal.Body>
17
+ * <Modal.Footer>
18
+ * <Button variant="outline" onClick={onClose}>Cancel</Button>
19
+ * <Button onClick={onConfirm}>Confirm</Button>
20
+ * </Modal.Footer>
21
+ * </Modal>
22
+ * );
23
+ * }
24
+ * ```
25
+ */
26
+
27
+ // Modal is eager (compound component pattern)
28
+ export {
29
+ Modal,
30
+ type ModalProps,
31
+ type ModalHeaderProps,
32
+ type ModalBodyProps,
33
+ type ModalFooterProps,
34
+ } from './Modal';
35
+
36
+ // ImageModal is lazy (standalone component)
37
+ import { LazyImageModal } from './lazy';
38
+ export { LazyImageModal as ImageModal };
39
+ export type { ImageModalProps } from './ImageModal';
@@ -0,0 +1,10 @@
1
+ import { lazy } from 'react';
2
+
3
+ /**
4
+ * Lazy-loaded modal components
5
+ * ImageModal is lazy-loaded since it's a standalone component.
6
+ * Modal is kept eager because it uses compound component pattern.
7
+ */
8
+ export const LazyImageModal = lazy(() =>
9
+ import('./ImageModal').then((m) => ({ default: m.ImageModal }))
10
+ );
@@ -0,0 +1,94 @@
1
+ import { useMemo, memo } from 'react';
2
+ import { useQuery } from '@tanstack/react-query';
3
+ import { useTwilight } from '../../providers/twilight-context';
4
+ import { useTranslation } from '../../providers/I18nProvider';
5
+ import { Link } from '../common';
6
+ import { menu } from '../../api/menu';
7
+ import type { MenuItem } from '../../types';
8
+
9
+ const SKELETON_ITEMS = [
10
+ { id: 'first', width: 'w-16' },
11
+ { id: 'second', width: 'w-20' },
12
+ { id: 'third', width: 'w-24' },
13
+ { id: 'fourth', width: 'w-16' },
14
+ { id: 'fifth', width: 'w-20' },
15
+ ];
16
+
17
+ const MainMenuSkeleton = memo(function MainMenuSkeleton() {
18
+ return (
19
+ <nav>
20
+ <ul className="main-menu">
21
+ {SKELETON_ITEMS.map(({ id, width }) => (
22
+ <li key={id} className="!hidden lg:!block root-level">
23
+ <Link to="#">
24
+ <span className={`inline-block animate-pulse rounded bg-gray-200 h-4 ${width}`} />
25
+ </Link>
26
+ </li>
27
+ ))}
28
+ </ul>
29
+ </nav>
30
+ );
31
+ });
32
+
33
+ export function MainMenu() {
34
+ const { theme } = useTwilight();
35
+ const { data: menus = [], isLoading } = useQuery(menu.queries.header());
36
+ const { t } = useTranslation();
37
+
38
+ const { visibleMenus, hiddenMenus } = useMemo(() => {
39
+ const enableMore = theme?.settings?.is_more_button_enabled;
40
+ const visibleCount = 7;
41
+
42
+ if (enableMore && menus.length > visibleCount) {
43
+ return {
44
+ visibleMenus: menus.slice(0, visibleCount),
45
+ hiddenMenus: menus.slice(visibleCount),
46
+ };
47
+ }
48
+
49
+ return { visibleMenus: menus, hiddenMenus: [] };
50
+ }, [menus, theme?.settings?.enable_more_menu, theme?.settings?.is_more_button_enabled]);
51
+
52
+ if (isLoading) {
53
+ return <MainMenuSkeleton />;
54
+ }
55
+
56
+ const renderItem = (item: MenuItem, isChild = false, inMoreMenu = false) => {
57
+ const hasChildren = item.has_children && item.children && item.children.length > 0;
58
+ const showDropdown = hasChildren && !inMoreMenu;
59
+
60
+ return (
61
+ <li
62
+ key={item.id}
63
+ className={`${isChild ? 'relative' : '!hidden lg:!block root-level'} ${hasChildren ? 'has-children' : ''}`}
64
+ >
65
+ <Link to={item.url}>
66
+ <span>{item.title}</span>
67
+ </Link>
68
+ {showDropdown && (
69
+ <div className="sub-menu w-56">
70
+ <ul>{item?.children?.map((child: MenuItem) => renderItem(child, true))}</ul>
71
+ </div>
72
+ )}
73
+ </li>
74
+ );
75
+ };
76
+
77
+ return (
78
+ <nav>
79
+ <ul className="main-menu">
80
+ {visibleMenus.map((item) => renderItem(item))}
81
+ {hiddenMenus.length > 0 && (
82
+ <li id="more-menu-dropdown" className="!hidden lg:!block root-level has-children">
83
+ <Link to="#">
84
+ <span>{t('common.titles.more', 'More')}</span>
85
+ </Link>
86
+ <div className="sub-menu w-56">
87
+ <ul>{hiddenMenus.map((item) => renderItem(item, true, true))}</ul>
88
+ </div>
89
+ </li>
90
+ )}
91
+ </ul>
92
+ </nav>
93
+ );
94
+ }