@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.
- package/README.md +4 -3
- package/bin/twilight-check-manifest.mjs +16 -0
- package/dist/{AddToCartForm-B5C2M4OZ.js → AddToCartForm-P644O2QL.js} +4 -4
- package/dist/{AddToCartForm-B5C2M4OZ.js.map → AddToCartForm-P644O2QL.js.map} +1 -1
- package/dist/{BlogAuthorPage-GTF4UGSS.js → BlogAuthorPage-VWSO6MPY.js} +13 -11
- package/dist/BlogAuthorPage-VWSO6MPY.js.map +1 -0
- package/dist/{BlogCategoryPage-TN7Y4NBP.js → BlogCategoryPage-K2KSQ6T6.js} +13 -11
- package/dist/BlogCategoryPage-K2KSQ6T6.js.map +1 -0
- package/dist/{BlogPage-TDB3M3PI.js → BlogPage-XRM7VWOZ.js} +12 -10
- package/dist/BlogPage-XRM7VWOZ.js.map +1 -0
- package/dist/{BlogSinglePage-4A52Q5QW.js → BlogSinglePage-XIAXT54H.js} +12 -10
- package/dist/BlogSinglePage-XIAXT54H.js.map +1 -0
- package/dist/{BlogTagPage-TOVPBETA.js → BlogTagPage-IHOA267V.js} +13 -11
- package/dist/BlogTagPage-IHOA267V.js.map +1 -0
- package/dist/{BrandsPage-VUDGYSQI.js → BrandsPage-ZFEILRT3.js} +12 -10
- package/dist/BrandsPage-ZFEILRT3.js.map +1 -0
- package/dist/{Breadcrumb-CI3SN5YR.js → Breadcrumb-CVSISL2K.js} +12 -10
- package/dist/Breadcrumb-CVSISL2K.js.map +1 -0
- package/dist/{CartItem-BHLEURAB.js → CartItem-EOI7P3JA.js} +12 -10
- package/dist/CartItem-EOI7P3JA.js.map +1 -0
- package/dist/{CartSummary-QERVESYN.js → CartSummary-RZGUBDKJ.js} +12 -10
- package/dist/CartSummary-RZGUBDKJ.js.map +1 -0
- package/dist/{Copyright-RL6ALKRC.js → Copyright-5BLYSP24.js} +4 -4
- package/dist/{Copyright-RL6ALKRC.js.map → Copyright-5BLYSP24.js.map} +1 -1
- package/dist/{CustomerLayout-VRZUWGQK.js → CustomerLayout-6F4BJAMU.js} +12 -10
- package/dist/CustomerLayout-6F4BJAMU.js.map +1 -0
- package/dist/{Footer-ZWBZCMDL.js → Footer-NF2NLDLR.js} +6 -6
- package/dist/{Footer-ZWBZCMDL.js.map → Footer-NF2NLDLR.js.map} +1 -1
- package/dist/{Header-WNK4O66B.js → Header-6DQJ5X7J.js} +9 -7
- package/dist/{Header-WNK4O66B.js.map → Header-6DQJ5X7J.js.map} +1 -1
- package/dist/{LoyaltyPage-CGTHWJ76.js → LoyaltyPage-YHWHIWCS.js} +12 -10
- package/dist/LoyaltyPage-YHWHIWCS.js.map +1 -0
- package/dist/{LoyaltyPointsPage-QSU6K3PW.js → LoyaltyPointsPage-T6K3H5A5.js} +10 -3
- package/dist/LoyaltyPointsPage-T6K3H5A5.js.map +1 -0
- package/dist/{MainMenu-V3T7F32R.js → MainMenu-Y2TAM7DK.js} +4 -4
- package/dist/{MainMenu-V3T7F32R.js.map → MainMenu-Y2TAM7DK.js.map} +1 -1
- package/dist/{MasterLayout-XQF644LD.js → MasterLayout-GBEEFFPG.js} +12 -10
- package/dist/{MasterLayout-XQF644LD.js.map → MasterLayout-GBEEFFPG.js.map} +1 -1
- package/dist/{MobileMenu-U4BVQYKH.js → MobileMenu-RE5XVGZL.js} +12 -10
- package/dist/MobileMenu-RE5XVGZL.js.map +1 -0
- package/dist/{NotificationsPage-ZRRQY7TF.js → NotificationsPage-QAFWZAS3.js} +12 -10
- package/dist/NotificationsPage-QAFWZAS3.js.map +1 -0
- package/dist/{OrderSinglePage-O3XYVMG5.js → OrderSinglePage-T4PRXTKG.js} +12 -10
- package/dist/OrderSinglePage-T4PRXTKG.js.map +1 -0
- package/dist/{OrdersPage-CMXZF5MI.js → OrdersPage-3S6ZS56E.js} +12 -10
- package/dist/OrdersPage-3S6ZS56E.js.map +1 -0
- package/dist/{PageSingle-IIG2US43.js → PageSingle-W3UTXDOW.js} +4 -4
- package/dist/{PageSingle-IIG2US43.js.map → PageSingle-W3UTXDOW.js.map} +1 -1
- package/dist/{ProductDetails-65GDABI7.js → ProductDetails-X5A7TG3F.js} +14 -7
- package/dist/ProductDetails-X5A7TG3F.js.map +1 -0
- package/dist/{ProfilePage-SNJ4WXU4.js → ProfilePage-DUH3AILJ.js} +12 -10
- package/dist/ProfilePage-DUH3AILJ.js.map +1 -0
- package/dist/{ReviewsPage-UT7UT55J.js → ReviewsPage-2C2P7VS3.js} +4 -4
- package/dist/{ReviewsPage-UT7UT55J.js.map → ReviewsPage-2C2P7VS3.js.map} +1 -1
- package/dist/{SeoCartWidget-DBJAXJKT.js → SeoCartWidget-VYLGEW2I.js} +12 -10
- package/dist/SeoCartWidget-VYLGEW2I.js.map +1 -0
- package/dist/{SettingsPage-FLTIOPU5.js → SettingsPage-OZJEW6CW.js} +12 -10
- package/dist/SettingsPage-OZJEW6CW.js.map +1 -0
- package/dist/{StoreClosedNotification-ZT3QKDKS.js → StoreClosedNotification-QHIZBNXO.js} +13 -11
- package/dist/{StoreClosedNotification-ZT3QKDKS.js.map → StoreClosedNotification-QHIZBNXO.js.map} +1 -1
- package/dist/{TestimonialsPage-CU7HKU4O.js → TestimonialsPage-K6WJKKME.js} +4 -4
- package/dist/{TestimonialsPage-CU7HKU4O.js.map → TestimonialsPage-K6WJKKME.js.map} +1 -1
- package/dist/{ThankYouPage-5X6B4X3T.js → ThankYouPage-H6QQEI5R.js} +12 -10
- package/dist/ThankYouPage-H6QQEI5R.js.map +1 -0
- package/dist/{WalletPage-H2BKIDPA.js → WalletPage-G5IBGN6B.js} +12 -10
- package/dist/WalletPage-G5IBGN6B.js.map +1 -0
- package/dist/{WishlistPage-RS5UCIQ3.js → WishlistPage-AIOTAB6L.js} +12 -10
- package/dist/WishlistPage-AIOTAB6L.js.map +1 -0
- package/dist/api/advertisement.d.ts +33 -0
- package/dist/api/advertisement.d.ts.map +1 -0
- package/dist/api/advertisement.js +8 -0
- package/dist/api/advertisement.js.map +1 -0
- package/dist/boot/critical-style.d.ts +3 -0
- package/dist/boot/critical-style.d.ts.map +1 -0
- package/dist/boot/index.d.ts +15 -0
- package/dist/boot/index.d.ts.map +1 -0
- package/dist/boot.d.ts +1 -0
- package/dist/boot.js +69 -0
- package/dist/boot.js.map +1 -0
- package/dist/chunk-4FJGZQD3.js +6 -0
- package/dist/chunk-4FJGZQD3.js.map +1 -0
- package/dist/{chunk-SDFDS4Q3.js → chunk-BCJTF3V7.js} +20 -4
- package/dist/chunk-BCJTF3V7.js.map +1 -0
- package/dist/chunk-DBW7XXNE.js +103 -0
- package/dist/chunk-DBW7XXNE.js.map +1 -0
- package/dist/{chunk-22SH7OVF.js → chunk-E7WWO3V3.js} +3 -3
- package/dist/{chunk-22SH7OVF.js.map → chunk-E7WWO3V3.js.map} +1 -1
- package/dist/{chunk-SE5JYWOK.js → chunk-EZHXVHXY.js} +4 -4
- package/dist/{chunk-SE5JYWOK.js.map → chunk-EZHXVHXY.js.map} +1 -1
- package/dist/{chunk-JO7JWV34.js → chunk-FWYKOQV5.js} +5 -5
- package/dist/chunk-FWYKOQV5.js.map +1 -0
- package/dist/{chunk-JRNH3FUF.js → chunk-FYHF62KI.js} +4 -4
- package/dist/{chunk-JRNH3FUF.js.map → chunk-FYHF62KI.js.map} +1 -1
- package/dist/{chunk-UJKFBZFJ.js → chunk-GJ5IMPNY.js} +7 -7
- package/dist/{chunk-UJKFBZFJ.js.map → chunk-GJ5IMPNY.js.map} +1 -1
- package/dist/chunk-IVBOLREE.js +29 -0
- package/dist/chunk-IVBOLREE.js.map +1 -0
- package/dist/chunk-LWPKBVIE.js +14 -0
- package/dist/{chunk-7WAXN2PP.js.map → chunk-LWPKBVIE.js.map} +1 -1
- package/dist/{chunk-SX5MF2YA.js → chunk-N3BCZIGS.js} +4 -4
- package/dist/{chunk-SX5MF2YA.js.map → chunk-N3BCZIGS.js.map} +1 -1
- package/dist/{chunk-NZVR4OF7.js → chunk-NXQZ4RLM.js} +8 -8
- package/dist/chunk-NXQZ4RLM.js.map +1 -0
- package/dist/{chunk-NXQJA7D3.js → chunk-PCURRKIH.js} +3 -3
- package/dist/{chunk-NXQJA7D3.js.map → chunk-PCURRKIH.js.map} +1 -1
- package/dist/{chunk-4CZRUAZB.js → chunk-PU2LHFDQ.js} +10 -11
- package/dist/chunk-PU2LHFDQ.js.map +1 -0
- package/dist/{chunk-OWX2CPNQ.js → chunk-UQIVX4YA.js} +3 -3
- package/dist/{chunk-OWX2CPNQ.js.map → chunk-UQIVX4YA.js.map} +1 -1
- package/dist/chunk-X5NK4TZH.js +228 -0
- package/dist/chunk-X5NK4TZH.js.map +1 -0
- package/dist/{chunk-6SWDSRIL.js → chunk-XKNXYQ2K.js} +6 -6
- package/dist/{chunk-6SWDSRIL.js.map → chunk-XKNXYQ2K.js.map} +1 -1
- package/dist/{chunk-KHBSGD2D.js → chunk-XO7B3VWS.js} +3 -3
- package/dist/{chunk-KHBSGD2D.js.map → chunk-XO7B3VWS.js.map} +1 -1
- package/dist/cli/check-manifest.d.ts +10 -0
- package/dist/cli/check-manifest.d.ts.map +1 -0
- package/dist/cli/check-manifest.js +44 -0
- package/dist/cli/check-manifest.js.map +1 -0
- package/dist/components/cart/index.js +1 -1
- package/dist/components/common/index.js +2 -2
- package/dist/components/drawer/index.js +10 -8
- package/dist/components/home/index.js +12 -10
- package/dist/components/layout/Footer.d.ts.map +1 -1
- package/dist/components/layout/MasterLayout.d.ts.map +1 -1
- package/dist/components/layout/PageAdvertisements.d.ts +7 -0
- package/dist/components/layout/PageAdvertisements.d.ts.map +1 -0
- package/dist/components/layout/index.d.ts +1 -0
- package/dist/components/layout/index.d.ts.map +1 -1
- package/dist/components/layout/index.js +8 -1
- package/dist/components/product/index.js +11 -9
- package/dist/components/store/index.js +12 -10
- package/dist/components/store/index.js.map +1 -1
- package/dist/{document-class-67HTP45G.js → document-class-FRL7JHWD.js} +6 -5
- package/dist/{document-class-67HTP45G.js.map → document-class-FRL7JHWD.js.map} +1 -1
- package/dist/hooks/index.js +10 -8
- package/dist/hooks/useGtm.js +10 -8
- package/dist/hooks/useMoney.js +10 -8
- package/dist/hooks/useStore.js +10 -8
- package/dist/index.js +10 -8
- package/dist/index.js.map +1 -1
- package/dist/{not-found-QTHBFPNT.js → not-found-E6AR4CZ4.js} +12 -10
- package/dist/not-found-E6AR4CZ4.js.map +1 -0
- package/dist/providers/index.js +10 -8
- package/dist/routes/account/orders.js +1 -1
- package/dist/routes/account.js +6 -6
- package/dist/routes/blog.js +5 -5
- package/dist/routes/brands.js +1 -1
- package/dist/routes/cart.js +14 -12
- package/dist/routes/cart.js.map +1 -1
- package/dist/routes/home.js +13 -11
- package/dist/routes/loyalty.js +2 -2
- package/dist/routes/page.js +1 -1
- package/dist/routes/product-listing.js +12 -10
- package/dist/routes/product-listing.js.map +1 -1
- package/dist/routes/product.js +12 -10
- package/dist/routes/product.js.map +1 -1
- package/dist/routes/root/beforeLoad.d.ts +2 -2
- package/dist/routes/root/beforeLoad.d.ts.map +1 -1
- package/dist/routes/root/head.d.ts.map +1 -1
- package/dist/routes/testimonials.js +2 -2
- package/dist/routes/thank-you.js +1 -1
- package/dist/skeleton.js +13 -11
- package/dist/tanstack/document-class.d.ts.map +1 -1
- package/dist/tanstack/index.d.ts +1 -0
- package/dist/tanstack/index.d.ts.map +1 -1
- package/dist/tanstack/stylesheet.d.ts +15 -0
- package/dist/tanstack/stylesheet.d.ts.map +1 -0
- package/dist/tanstack.js +33 -4
- package/dist/tanstack.js.map +1 -1
- package/dist/utils/index.js +2 -2
- package/dist/vite/index.d.ts +1 -1
- package/dist/vite/index.d.ts.map +1 -1
- package/dist/vite/index.js +14 -25
- package/dist/vite/index.js.map +1 -1
- package/dist/vite/plugins/twilight-manifest.d.ts +23 -0
- package/dist/vite/plugins/twilight-manifest.d.ts.map +1 -1
- package/dist/vite/plugins/twilight-schema.plugin.d.ts.map +1 -1
- package/package.json +16 -3
- package/tailwind/components/Component.tsx.txt +21 -0
- package/tailwind/components/ComponentRegistry.ts.txt +66 -0
- package/tailwind/components/cart/CartItem.tsx.txt +211 -0
- package/tailwind/components/cart/CartSummary.tsx.txt +269 -0
- package/tailwind/components/cart/PreOrderNotification.tsx.txt +16 -0
- package/tailwind/components/cart/PriceQuoteButton.tsx.txt +21 -0
- package/tailwind/components/cart/SeoCartWidget.tsx.txt +99 -0
- package/tailwind/components/cart/index.ts.txt +11 -0
- package/tailwind/components/cart/lazy.ts.txt +15 -0
- package/tailwind/components/collapse/Collapse.tsx.txt +95 -0
- package/tailwind/components/collapse/index.ts.txt +41 -0
- package/tailwind/components/common/Breadcrumb.tsx.txt +27 -0
- package/tailwind/components/common/BreadcrumbJsonLd.tsx.txt +40 -0
- package/tailwind/components/common/BreadcrumbTrail.tsx.txt +97 -0
- package/tailwind/components/common/CurrencySymbol.tsx.txt +8 -0
- package/tailwind/components/common/DeferredData.tsx.txt +122 -0
- package/tailwind/components/common/ErrorPage.tsx.txt +69 -0
- package/tailwind/components/common/Image.tsx.txt +119 -0
- package/tailwind/components/common/Link.tsx.txt +36 -0
- package/tailwind/components/common/Link.types.ts.txt +85 -0
- package/tailwind/components/common/NoContent.tsx.txt +27 -0
- package/tailwind/components/common/RenderWhenVisible.tsx.txt +92 -0
- package/tailwind/components/common/index.ts.txt +42 -0
- package/tailwind/components/common/lazy.ts.txt +28 -0
- package/tailwind/components/defineComponent.ts.txt +32 -0
- package/tailwind/components/drawer/Drawer.tsx.txt +219 -0
- package/tailwind/components/drawer/index.ts.txt +38 -0
- package/tailwind/components/dropdown/Dropdown.tsx.txt +166 -0
- package/tailwind/components/dropdown/index.ts.txt +40 -0
- package/tailwind/components/home/BundleComponent.tsx.txt +35 -0
- package/tailwind/components/home/ComponentErrorBoundary.tsx.txt +98 -0
- package/tailwind/components/home/FeaturedProductsStyle1.tsx.txt +125 -0
- package/tailwind/components/home/FeaturedProductsStyle2.tsx.txt +127 -0
- package/tailwind/components/home/FeaturedProductsStyle3.tsx.txt +71 -0
- package/tailwind/components/home/FixedBanner.tsx.txt +68 -0
- package/tailwind/components/home/FixedProducts.tsx.txt +72 -0
- package/tailwind/components/home/HomePageRenderer.tsx.txt +284 -0
- package/tailwind/components/home/PageBlocks.tsx.txt +24 -0
- package/tailwind/components/home/ParallaxBackground.tsx.txt +50 -0
- package/tailwind/components/home/PhotosSlider.tsx.txt +61 -0
- package/tailwind/components/home/ProductsSlider.tsx.txt +87 -0
- package/tailwind/components/home/ReorderProducts.tsx.txt +38 -0
- package/tailwind/components/home/SquarePhotos.tsx.txt +68 -0
- package/tailwind/components/home/StoreFeatures.tsx.txt +34 -0
- package/tailwind/components/home/Testimonials.tsx.txt +36 -0
- package/tailwind/components/home/Youtube.tsx.txt +48 -0
- package/tailwind/components/home/index.ts.txt +50 -0
- package/tailwind/components/home/lazy.ts.txt +17 -0
- package/tailwind/components/home/register.ts.txt +44 -0
- package/tailwind/components/home/reorder-hooks.ts.txt +52 -0
- package/tailwind/components/home/types.ts.txt +4 -0
- package/tailwind/components/layout/Copyright.tsx.txt +23 -0
- package/tailwind/components/layout/CustomerLayout.tsx.txt +91 -0
- package/tailwind/components/layout/Footer.tsx.txt +341 -0
- package/tailwind/components/layout/Header.tsx.txt +154 -0
- package/tailwind/components/layout/MasterLayout.tsx.txt +62 -0
- package/tailwind/components/layout/PageAdvertisements.tsx.txt +131 -0
- package/tailwind/components/layout/WidgetHead.tsx.txt +53 -0
- package/tailwind/components/layout/index.ts.txt +27 -0
- package/tailwind/components/layout/lazy.ts.txt +25 -0
- package/tailwind/components/layout/types.ts.txt +11 -0
- package/tailwind/components/modal/ImageModal.tsx.txt +70 -0
- package/tailwind/components/modal/Modal.tsx.txt +245 -0
- package/tailwind/components/modal/index.ts.txt +39 -0
- package/tailwind/components/modal/lazy.ts.txt +10 -0
- package/tailwind/components/navigation/MainMenu.tsx.txt +94 -0
- package/tailwind/components/navigation/MobileMenu.tsx.txt +160 -0
- package/tailwind/components/navigation/index.ts.txt +4 -0
- package/tailwind/components/navigation/lazy.ts.txt +11 -0
- package/tailwind/components/product/AddToCartForm.tsx.txt +365 -0
- package/tailwind/components/product/ProductCard.tsx.txt +305 -0
- package/tailwind/components/product/ProductDetails.tsx.txt +267 -0
- package/tailwind/components/product/ProductGallery.tsx.txt +234 -0
- package/tailwind/components/product/index.ts.txt +15 -0
- package/tailwind/components/product/lazy.ts.txt +9 -0
- package/tailwind/components/snippets/AppsSnippets.tsx.txt +225 -0
- package/tailwind/components/snippets/SallaSdkScripts.tsx.txt +14 -0
- package/tailwind/components/snippets/index.ts.txt +4 -0
- package/tailwind/components/store/StoreClosedNotification.tsx.txt +73 -0
- package/tailwind/components/store/index.ts.txt +8 -0
- package/tailwind/components/store/lazy.ts.txt +8 -0
- package/tailwind/components/toast/Toaster.tsx.txt +177 -0
- package/tailwind/components/toast/index.ts.txt +10 -0
- package/tailwind/components/toast/types.ts.txt +38 -0
- package/tailwind/components/toast/useToast.ts.txt +61 -0
- package/tailwind/components/tracking/GoogleTag.tsx.txt +77 -0
- package/tailwind/components/tracking/SiftSnippet.tsx.txt +49 -0
- package/tailwind/components/tracking/TwilightBundles.tsx.txt +67 -0
- package/tailwind/components/tracking/index.ts.txt +11 -0
- package/tailwind/components/tracking/lazy.ts.txt +11 -0
- package/tailwind/components/useComponent.ts.txt +12 -0
- package/tailwind/routes/account/index.tsx.txt +16 -0
- package/tailwind/routes/account/notifications/NotificationsPage.tsx.txt +54 -0
- package/tailwind/routes/account/notifications/index.tsx.txt +39 -0
- package/tailwind/routes/account/notifications/loader.ts.txt +22 -0
- package/tailwind/routes/account/notifications/types.ts.txt +18 -0
- package/tailwind/routes/account/orders/OrderSinglePage.tsx.txt +181 -0
- package/tailwind/routes/account/orders/OrdersPage.tsx.txt +28 -0
- package/tailwind/routes/account/orders/index.tsx.txt +86 -0
- package/tailwind/routes/account/orders/loader.ts.txt +66 -0
- package/tailwind/routes/account/orders/single/OrderActions.tsx.txt +197 -0
- package/tailwind/routes/account/orders/single/OrderHeader.tsx.txt +143 -0
- package/tailwind/routes/account/orders/single/OrderItemRow.tsx.txt +209 -0
- package/tailwind/routes/account/orders/single/OrderSections.tsx.txt +215 -0
- package/tailwind/routes/account/orders/single/types.ts.txt +38 -0
- package/tailwind/routes/account/orders/types.ts.txt +29 -0
- package/tailwind/routes/account/profile/ProfilePage.tsx.txt +30 -0
- package/tailwind/routes/account/profile/index.tsx.txt +39 -0
- package/tailwind/routes/account/profile/loader.ts.txt +16 -0
- package/tailwind/routes/account/profile/types.ts.txt +5 -0
- package/tailwind/routes/account/settings/SettingsPage.tsx.txt +13 -0
- package/tailwind/routes/account/settings/index.tsx.txt +39 -0
- package/tailwind/routes/account/settings/loader.ts.txt +13 -0
- package/tailwind/routes/account/settings/types.ts.txt +5 -0
- package/tailwind/routes/account/types.ts.txt +320 -0
- package/tailwind/routes/account/wallet/WalletPage.tsx.txt +195 -0
- package/tailwind/routes/account/wallet/index.tsx.txt +38 -0
- package/tailwind/routes/account/wallet/loader.ts.txt +27 -0
- package/tailwind/routes/account/wallet/types.ts.txt +11 -0
- package/tailwind/routes/account/wishlist/WishlistPage.tsx.txt +314 -0
- package/tailwind/routes/account/wishlist/WishlistShare.tsx.txt +248 -0
- package/tailwind/routes/account/wishlist/index.tsx.txt +40 -0
- package/tailwind/routes/account/wishlist/loader.ts.txt +26 -0
- package/tailwind/routes/account/wishlist/types.ts.txt +9 -0
- package/tailwind/routes/blog/BlogAuthorPage.tsx.txt +82 -0
- package/tailwind/routes/blog/BlogCategoryPage.tsx.txt +84 -0
- package/tailwind/routes/blog/BlogPage.tsx.txt +191 -0
- package/tailwind/routes/blog/BlogSinglePage.tsx.txt +168 -0
- package/tailwind/routes/blog/BlogTagPage.tsx.txt +84 -0
- package/tailwind/routes/blog/BlogTags.tsx.txt +54 -0
- package/tailwind/routes/blog/components.tsx.txt +87 -0
- package/tailwind/routes/blog/index.tsx.txt +238 -0
- package/tailwind/routes/blog/loader.ts.txt +173 -0
- package/tailwind/routes/blog/types.ts.txt +106 -0
- package/tailwind/routes/brands/BrandsPage.tsx.txt +179 -0
- package/tailwind/routes/brands/index.tsx.txt +47 -0
- package/tailwind/routes/brands/loader.ts.txt +14 -0
- package/tailwind/routes/brands/types.ts.txt +53 -0
- package/tailwind/routes/cart/CartPage.tsx.txt +112 -0
- package/tailwind/routes/cart/index.tsx.txt +39 -0
- package/tailwind/routes/cart/loader.ts.txt +15 -0
- package/tailwind/routes/cart/types.ts.txt +6 -0
- package/tailwind/routes/home/HomePage.tsx.txt +53 -0
- package/tailwind/routes/home/index.tsx.txt +56 -0
- package/tailwind/routes/home/loader.ts.txt +24 -0
- package/tailwind/routes/home/types.ts.txt +11 -0
- package/tailwind/routes/index.ts.txt +94 -0
- package/tailwind/routes/lazy.ts.txt +105 -0
- package/tailwind/routes/loyalty/LoyaltyPage.tsx.txt +17 -0
- package/tailwind/routes/loyalty/LoyaltyPointsPage.tsx.txt +11 -0
- package/tailwind/routes/loyalty/index.tsx.txt +81 -0
- package/tailwind/routes/loyalty/loader.ts.txt +53 -0
- package/tailwind/routes/loyalty/types.ts.txt +109 -0
- package/tailwind/routes/not-found.tsx.txt +50 -0
- package/tailwind/routes/page/PageSingle.tsx.txt +34 -0
- package/tailwind/routes/page/index.tsx.txt +60 -0
- package/tailwind/routes/page/loader.ts.txt +19 -0
- package/tailwind/routes/page/types.ts.txt +34 -0
- package/tailwind/routes/product/ProductPage.tsx.txt +77 -0
- package/tailwind/routes/product/index.tsx.txt +157 -0
- package/tailwind/routes/product/loader.ts.txt +35 -0
- package/tailwind/routes/product/types.ts.txt +6 -0
- package/tailwind/routes/product-listing/ProductListPage.tsx.txt +321 -0
- package/tailwind/routes/product-listing/index.tsx.txt +95 -0
- package/tailwind/routes/product-listing/loader.ts.txt +323 -0
- package/tailwind/routes/product-listing/types.ts.txt +96 -0
- package/tailwind/routes/product-listing/useAppliedFilters.ts.txt +130 -0
- package/tailwind/routes/product-short-link.ts.txt +104 -0
- package/tailwind/routes/root/beforeLoad.ts.txt +354 -0
- package/tailwind/routes/root/head.ts.txt +424 -0
- package/tailwind/routes/root/index.ts.txt +2 -0
- package/tailwind/routes/seo-redirects.ts.txt +82 -0
- package/tailwind/routes/testimonials/ReviewsPage.tsx.txt +12 -0
- package/tailwind/routes/testimonials/TestimonialsPage.tsx.txt +80 -0
- package/tailwind/routes/testimonials/index.tsx.txt +80 -0
- package/tailwind/routes/testimonials/loader.ts.txt +29 -0
- package/tailwind/routes/testimonials/types.ts.txt +9 -0
- package/tailwind/routes/thank-you/ThankYouPage.tsx.txt +241 -0
- package/tailwind/routes/thank-you/index.tsx.txt +49 -0
- package/tailwind/routes/thank-you/loader.ts.txt +40 -0
- package/tailwind/routes/thank-you/types.ts.txt +16 -0
- package/tailwind/routes/types.ts.txt +41 -0
- package/dist/BlogAuthorPage-GTF4UGSS.js.map +0 -1
- package/dist/BlogCategoryPage-TN7Y4NBP.js.map +0 -1
- package/dist/BlogPage-TDB3M3PI.js.map +0 -1
- package/dist/BlogSinglePage-4A52Q5QW.js.map +0 -1
- package/dist/BlogTagPage-TOVPBETA.js.map +0 -1
- package/dist/BrandsPage-VUDGYSQI.js.map +0 -1
- package/dist/Breadcrumb-CI3SN5YR.js.map +0 -1
- package/dist/CartItem-BHLEURAB.js.map +0 -1
- package/dist/CartSummary-QERVESYN.js.map +0 -1
- package/dist/CustomerLayout-VRZUWGQK.js.map +0 -1
- package/dist/LoyaltyPage-CGTHWJ76.js.map +0 -1
- package/dist/LoyaltyPointsPage-QSU6K3PW.js.map +0 -1
- package/dist/MobileMenu-U4BVQYKH.js.map +0 -1
- package/dist/NotificationsPage-ZRRQY7TF.js.map +0 -1
- package/dist/OrderSinglePage-O3XYVMG5.js.map +0 -1
- package/dist/OrdersPage-CMXZF5MI.js.map +0 -1
- package/dist/ProductDetails-65GDABI7.js.map +0 -1
- package/dist/ProfilePage-SNJ4WXU4.js.map +0 -1
- package/dist/SeoCartWidget-DBJAXJKT.js.map +0 -1
- package/dist/SettingsPage-FLTIOPU5.js.map +0 -1
- package/dist/ThankYouPage-5X6B4X3T.js.map +0 -1
- package/dist/WalletPage-H2BKIDPA.js.map +0 -1
- package/dist/WishlistPage-RS5UCIQ3.js.map +0 -1
- package/dist/chunk-4CZRUAZB.js.map +0 -1
- package/dist/chunk-7WAXN2PP.js +0 -14
- package/dist/chunk-JO7JWV34.js.map +0 -1
- package/dist/chunk-NZVR4OF7.js.map +0 -1
- package/dist/chunk-SDFDS4Q3.js.map +0 -1
- 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,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
|
+
}
|