@salla.sa/twilight-theme-engine 1.0.85 → 1.0.86
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{AddToCartForm-B5C2M4OZ.js → AddToCartForm-5X2WZDWR.js} +3 -3
- package/dist/{AddToCartForm-B5C2M4OZ.js.map → AddToCartForm-5X2WZDWR.js.map} +1 -1
- package/dist/{BlogAuthorPage-GTF4UGSS.js → BlogAuthorPage-LO7FSSJR.js} +12 -10
- package/dist/BlogAuthorPage-LO7FSSJR.js.map +1 -0
- package/dist/{BlogCategoryPage-TN7Y4NBP.js → BlogCategoryPage-KKFQ3PGR.js} +12 -10
- package/dist/BlogCategoryPage-KKFQ3PGR.js.map +1 -0
- package/dist/{BlogPage-TDB3M3PI.js → BlogPage-VLF3NSGE.js} +11 -9
- package/dist/BlogPage-VLF3NSGE.js.map +1 -0
- package/dist/{BlogSinglePage-4A52Q5QW.js → BlogSinglePage-AVK535IW.js} +11 -9
- package/dist/BlogSinglePage-AVK535IW.js.map +1 -0
- package/dist/{BlogTagPage-TOVPBETA.js → BlogTagPage-F3T4D7RH.js} +12 -10
- package/dist/BlogTagPage-F3T4D7RH.js.map +1 -0
- package/dist/{BrandsPage-VUDGYSQI.js → BrandsPage-LVSBZSVC.js} +11 -9
- package/dist/BrandsPage-LVSBZSVC.js.map +1 -0
- package/dist/{Breadcrumb-CI3SN5YR.js → Breadcrumb-VLFTXRFC.js} +11 -9
- package/dist/Breadcrumb-VLFTXRFC.js.map +1 -0
- package/dist/{CartItem-BHLEURAB.js → CartItem-G3H34GW6.js} +11 -9
- package/dist/CartItem-G3H34GW6.js.map +1 -0
- package/dist/{CartSummary-QERVESYN.js → CartSummary-VH37RPUI.js} +11 -9
- package/dist/CartSummary-VH37RPUI.js.map +1 -0
- package/dist/{Copyright-RL6ALKRC.js → Copyright-HE5ZWINZ.js} +3 -3
- package/dist/{Copyright-RL6ALKRC.js.map → Copyright-HE5ZWINZ.js.map} +1 -1
- package/dist/{CustomerLayout-VRZUWGQK.js → CustomerLayout-LKBLOAVW.js} +11 -9
- package/dist/CustomerLayout-LKBLOAVW.js.map +1 -0
- package/dist/{Footer-ZWBZCMDL.js → Footer-HHXAST72.js} +5 -5
- package/dist/{Footer-ZWBZCMDL.js.map → Footer-HHXAST72.js.map} +1 -1
- package/dist/{Header-WNK4O66B.js → Header-GEVZM2YG.js} +8 -6
- package/dist/{Header-WNK4O66B.js.map → Header-GEVZM2YG.js.map} +1 -1
- package/dist/{LoyaltyPage-CGTHWJ76.js → LoyaltyPage-M5CT3CFD.js} +11 -9
- package/dist/LoyaltyPage-M5CT3CFD.js.map +1 -0
- package/dist/{LoyaltyPointsPage-QSU6K3PW.js → LoyaltyPointsPage-SDJB52U6.js} +10 -3
- package/dist/LoyaltyPointsPage-SDJB52U6.js.map +1 -0
- package/dist/{MainMenu-V3T7F32R.js → MainMenu-I7EYS752.js} +3 -3
- package/dist/{MainMenu-V3T7F32R.js.map → MainMenu-I7EYS752.js.map} +1 -1
- package/dist/{MasterLayout-XQF644LD.js → MasterLayout-QDK4T3NE.js} +11 -9
- package/dist/{MasterLayout-XQF644LD.js.map → MasterLayout-QDK4T3NE.js.map} +1 -1
- package/dist/{MobileMenu-U4BVQYKH.js → MobileMenu-7VTCWAVJ.js} +11 -9
- package/dist/MobileMenu-7VTCWAVJ.js.map +1 -0
- package/dist/{NotificationsPage-ZRRQY7TF.js → NotificationsPage-QCABE6QD.js} +11 -9
- package/dist/NotificationsPage-QCABE6QD.js.map +1 -0
- package/dist/{OrderSinglePage-O3XYVMG5.js → OrderSinglePage-2HVBKBG4.js} +11 -9
- package/dist/OrderSinglePage-2HVBKBG4.js.map +1 -0
- package/dist/{OrdersPage-CMXZF5MI.js → OrdersPage-5MNCPCM3.js} +11 -9
- package/dist/OrdersPage-5MNCPCM3.js.map +1 -0
- package/dist/{PageSingle-IIG2US43.js → PageSingle-EN5XIM2B.js} +3 -3
- package/dist/{PageSingle-IIG2US43.js.map → PageSingle-EN5XIM2B.js.map} +1 -1
- package/dist/{ProductDetails-65GDABI7.js → ProductDetails-UEWQPPDZ.js} +13 -6
- package/dist/ProductDetails-UEWQPPDZ.js.map +1 -0
- package/dist/{ProfilePage-SNJ4WXU4.js → ProfilePage-TZSU3GHZ.js} +11 -9
- package/dist/ProfilePage-TZSU3GHZ.js.map +1 -0
- package/dist/{ReviewsPage-UT7UT55J.js → ReviewsPage-WYZ6CHF2.js} +3 -3
- package/dist/{ReviewsPage-UT7UT55J.js.map → ReviewsPage-WYZ6CHF2.js.map} +1 -1
- package/dist/{SeoCartWidget-DBJAXJKT.js → SeoCartWidget-5JR5RXQM.js} +11 -9
- package/dist/SeoCartWidget-5JR5RXQM.js.map +1 -0
- package/dist/{SettingsPage-FLTIOPU5.js → SettingsPage-DIDRQXMM.js} +11 -9
- package/dist/SettingsPage-DIDRQXMM.js.map +1 -0
- package/dist/{StoreClosedNotification-ZT3QKDKS.js → StoreClosedNotification-C2G32E34.js} +12 -10
- package/dist/{StoreClosedNotification-ZT3QKDKS.js.map → StoreClosedNotification-C2G32E34.js.map} +1 -1
- package/dist/{TestimonialsPage-CU7HKU4O.js → TestimonialsPage-XPHFRBZ7.js} +3 -3
- package/dist/{TestimonialsPage-CU7HKU4O.js.map → TestimonialsPage-XPHFRBZ7.js.map} +1 -1
- package/dist/{ThankYouPage-5X6B4X3T.js → ThankYouPage-UA6YYFB2.js} +11 -9
- package/dist/ThankYouPage-UA6YYFB2.js.map +1 -0
- package/dist/{WalletPage-H2BKIDPA.js → WalletPage-AJVV7LVL.js} +11 -9
- package/dist/WalletPage-AJVV7LVL.js.map +1 -0
- package/dist/{WishlistPage-RS5UCIQ3.js → WishlistPage-UW6LJLSW.js} +11 -9
- package/dist/WishlistPage-UW6LJLSW.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-SE5JYWOK.js → chunk-2HCCIHU7.js} +4 -4
- package/dist/{chunk-SE5JYWOK.js.map → chunk-2HCCIHU7.js.map} +1 -1
- package/dist/chunk-4FJGZQD3.js +6 -0
- package/dist/chunk-4FJGZQD3.js.map +1 -0
- package/dist/{chunk-SDFDS4Q3.js → chunk-4WVJJ7SN.js} +20 -4
- package/dist/chunk-4WVJJ7SN.js.map +1 -0
- package/dist/{chunk-NXQJA7D3.js → chunk-BAYGWTNM.js} +3 -3
- package/dist/{chunk-NXQJA7D3.js.map → chunk-BAYGWTNM.js.map} +1 -1
- package/dist/{chunk-OWX2CPNQ.js → chunk-CLBFF2CY.js} +3 -3
- package/dist/{chunk-OWX2CPNQ.js.map → chunk-CLBFF2CY.js.map} +1 -1
- package/dist/chunk-DBW7XXNE.js +103 -0
- package/dist/chunk-DBW7XXNE.js.map +1 -0
- package/dist/{chunk-KHBSGD2D.js → chunk-DBXH3EMJ.js} +3 -3
- package/dist/{chunk-KHBSGD2D.js.map → chunk-DBXH3EMJ.js.map} +1 -1
- package/dist/{chunk-JRNH3FUF.js → chunk-E26YG3AO.js} +4 -4
- package/dist/{chunk-JRNH3FUF.js.map → chunk-E26YG3AO.js.map} +1 -1
- 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-UJKFBZFJ.js → chunk-HQ7WA37L.js} +7 -7
- package/dist/{chunk-UJKFBZFJ.js.map → chunk-HQ7WA37L.js.map} +1 -1
- package/dist/chunk-IVBOLREE.js +29 -0
- package/dist/chunk-IVBOLREE.js.map +1 -0
- package/dist/{chunk-6SWDSRIL.js → chunk-KP3VYG4F.js} +6 -6
- package/dist/{chunk-6SWDSRIL.js.map → chunk-KP3VYG4F.js.map} +1 -1
- package/dist/{chunk-4CZRUAZB.js → chunk-PWTXR4RR.js} +10 -10
- package/dist/{chunk-4CZRUAZB.js.map → chunk-PWTXR4RR.js.map} +1 -1
- package/dist/chunk-QJYUTRAQ.js +14 -0
- package/dist/{chunk-7WAXN2PP.js.map → chunk-QJYUTRAQ.js.map} +1 -1
- package/dist/{chunk-JO7JWV34.js → chunk-U55ZJC2X.js} +4 -4
- package/dist/{chunk-JO7JWV34.js.map → chunk-U55ZJC2X.js.map} +1 -1
- package/dist/{chunk-SX5MF2YA.js → chunk-WSDG4ZRI.js} +4 -4
- package/dist/{chunk-SX5MF2YA.js.map → chunk-WSDG4ZRI.js.map} +1 -1
- package/dist/{chunk-NZVR4OF7.js → chunk-X54QP332.js} +8 -8
- package/dist/chunk-X54QP332.js.map +1 -0
- package/dist/components/cart/index.js +1 -1
- package/dist/components/common/index.js +1 -1
- package/dist/components/drawer/index.js +9 -7
- package/dist/components/home/index.js +11 -9
- 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 +10 -8
- package/dist/components/store/index.js +11 -9
- package/dist/components/store/index.js.map +1 -1
- package/dist/{document-class-67HTP45G.js → document-class-7AXAAWGZ.js} +6 -5
- package/dist/{document-class-67HTP45G.js.map → document-class-7AXAAWGZ.js.map} +1 -1
- package/dist/hooks/index.js +9 -7
- package/dist/hooks/useGtm.js +9 -7
- package/dist/hooks/useMoney.js +9 -7
- package/dist/hooks/useStore.js +9 -7
- package/dist/index.js +9 -7
- package/dist/index.js.map +1 -1
- package/dist/{not-found-QTHBFPNT.js → not-found-3EEMQZMU.js} +11 -9
- package/dist/not-found-3EEMQZMU.js.map +1 -0
- package/dist/providers/index.js +9 -7
- 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 +13 -11
- package/dist/routes/cart.js.map +1 -1
- package/dist/routes/home.js +12 -10
- package/dist/routes/loyalty.js +2 -2
- package/dist/routes/page.js +1 -1
- package/dist/routes/product-listing.js +11 -9
- package/dist/routes/product-listing.js.map +1 -1
- package/dist/routes/product.js +11 -9
- 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 +12 -10
- 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 +34 -5
- package/dist/tanstack.js.map +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/vite/index.js +1 -1
- package/package.json +12 -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 +343 -0
- package/tailwind/components/layout/Header.tsx.txt +154 -0
- package/tailwind/components/layout/MasterLayout.tsx.txt +63 -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-7WAXN2PP.js +0 -14
- 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,28 @@
|
|
|
1
|
+
import { lazy } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Lazy-loaded common components
|
|
5
|
+
* Use these for code-splitting non-critical components
|
|
6
|
+
*
|
|
7
|
+
* NOTE: Skeleton components are sourced from @salla.sa/twilight-components-react
|
|
8
|
+
* because they must render instantly during route transitions.
|
|
9
|
+
*/
|
|
10
|
+
export const LazyBreadcrumb = lazy(() =>
|
|
11
|
+
import('./Breadcrumb').then((m) => ({ default: m.Breadcrumb }))
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
export const LazyNoContent = lazy(() =>
|
|
15
|
+
import('./NoContent').then((m) => ({ default: m.NoContent }))
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
export const LazyRenderWhenVisible = lazy(() =>
|
|
19
|
+
import('./RenderWhenVisible').then((m) => ({ default: m.RenderWhenVisible }))
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
export const LazyCurrencySymbol = lazy(() =>
|
|
23
|
+
import('./CurrencySymbol').then((m) => ({ default: m.CurrencySymbol }))
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
export const LazyErrorPage = lazy(() =>
|
|
27
|
+
import('./ErrorPage').then((m) => ({ default: m.ErrorPage }))
|
|
28
|
+
);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { registry } from './ComponentRegistry';
|
|
2
|
+
import type { ComponentType } from '../types';
|
|
3
|
+
|
|
4
|
+
interface DefineComponentConfig<P = Record<string, unknown>> {
|
|
5
|
+
name: string;
|
|
6
|
+
component: ComponentType<P>;
|
|
7
|
+
override?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function defineComponent<P = Record<string, unknown>>(
|
|
11
|
+
config: DefineComponentConfig<P>
|
|
12
|
+
): ComponentType<P> {
|
|
13
|
+
if (config.override) {
|
|
14
|
+
registry.override(config.name, config.component);
|
|
15
|
+
} else {
|
|
16
|
+
registry.register(config.name, config.component);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return config.component;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function registerComponents(components: Record<string, ComponentType>): void {
|
|
23
|
+
for (const [name, component] of Object.entries(components)) {
|
|
24
|
+
registry.register(name, component);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function overrideComponents(components: Record<string, ComponentType>): void {
|
|
29
|
+
for (const [name, component] of Object.entries(components)) {
|
|
30
|
+
registry.override(name, component);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drawer Component
|
|
3
|
+
*
|
|
4
|
+
* A slide-in panel from the side of the screen, commonly used for mobile menus.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import React, { useEffect, useCallback, useRef, type ReactNode, type HTMLAttributes } from 'react';
|
|
8
|
+
import { createPortal } from 'react-dom';
|
|
9
|
+
import { useTwilight } from '../../providers/TwilightProvider';
|
|
10
|
+
|
|
11
|
+
export interface DrawerProps {
|
|
12
|
+
isOpen: boolean;
|
|
13
|
+
onClose: () => void;
|
|
14
|
+
children: ReactNode;
|
|
15
|
+
/**
|
|
16
|
+
* Width of the drawer
|
|
17
|
+
* @default 'md'
|
|
18
|
+
*/
|
|
19
|
+
size?: 'sm' | 'md' | 'lg' | 'full';
|
|
20
|
+
/**
|
|
21
|
+
* Close drawer when clicking backdrop
|
|
22
|
+
* @default true
|
|
23
|
+
*/
|
|
24
|
+
closeOnBackdropClick?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Close drawer on Escape key
|
|
27
|
+
* @default true
|
|
28
|
+
*/
|
|
29
|
+
closeOnEscape?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Prevent body scroll when drawer is open
|
|
32
|
+
* @default true
|
|
33
|
+
*/
|
|
34
|
+
preventBodyScroll?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Additional className for the drawer content
|
|
37
|
+
*/
|
|
38
|
+
className?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const sizeClasses = {
|
|
42
|
+
sm: 'w-64',
|
|
43
|
+
md: 'w-80',
|
|
44
|
+
lg: 'w-96',
|
|
45
|
+
full: 'w-full',
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export function Drawer({
|
|
49
|
+
isOpen,
|
|
50
|
+
onClose,
|
|
51
|
+
children,
|
|
52
|
+
size = 'md',
|
|
53
|
+
closeOnBackdropClick = true,
|
|
54
|
+
closeOnEscape = true,
|
|
55
|
+
preventBodyScroll = true,
|
|
56
|
+
className,
|
|
57
|
+
}: DrawerProps) {
|
|
58
|
+
const drawerRef = useRef<HTMLDivElement>(null);
|
|
59
|
+
const previousActiveElement = useRef<HTMLElement | null>(null);
|
|
60
|
+
// Use TwilightProvider to get theme direction
|
|
61
|
+
// Position is automatically determined: left for LTR, right for RTL
|
|
62
|
+
const { theme } = useTwilight();
|
|
63
|
+
const isRtl = theme?.is_rtl ?? false;
|
|
64
|
+
const position = isRtl ? 'right' : 'left';
|
|
65
|
+
|
|
66
|
+
// Handle Escape key
|
|
67
|
+
const handleKeyDown = useCallback(
|
|
68
|
+
(event: KeyboardEvent) => {
|
|
69
|
+
if (event.key === 'Escape' && closeOnEscape) {
|
|
70
|
+
onClose();
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
[closeOnEscape, onClose]
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
// Handle backdrop click
|
|
77
|
+
const handleBackdropClick = useCallback(
|
|
78
|
+
(_event: React.MouseEvent) => {
|
|
79
|
+
if (closeOnBackdropClick) {
|
|
80
|
+
onClose();
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
[closeOnBackdropClick, onClose]
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
// Focus management and body scroll
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
if (isOpen) {
|
|
89
|
+
// Store the previously focused element
|
|
90
|
+
previousActiveElement.current = document.activeElement as HTMLElement;
|
|
91
|
+
|
|
92
|
+
// Focus the drawer
|
|
93
|
+
drawerRef.current?.focus();
|
|
94
|
+
|
|
95
|
+
// Prevent body scroll
|
|
96
|
+
if (preventBodyScroll) {
|
|
97
|
+
document.body.classList.add('menu-opened');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Add keyboard listener
|
|
101
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return () => {
|
|
105
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
106
|
+
|
|
107
|
+
// Restore body scroll
|
|
108
|
+
if (preventBodyScroll) {
|
|
109
|
+
document.body.classList.remove('menu-opened');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Restore focus
|
|
113
|
+
previousActiveElement.current?.focus();
|
|
114
|
+
};
|
|
115
|
+
}, [isOpen, handleKeyDown, preventBodyScroll]);
|
|
116
|
+
|
|
117
|
+
if (!isOpen || typeof document === 'undefined') {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Translate classes: left drawer slides from -100%, right drawer slides from +100%
|
|
122
|
+
const translateClass = isOpen
|
|
123
|
+
? 'translate-x-0'
|
|
124
|
+
: position === 'left'
|
|
125
|
+
? '-translate-x-full'
|
|
126
|
+
: 'translate-x-full';
|
|
127
|
+
|
|
128
|
+
return createPortal(
|
|
129
|
+
<div className="fixed inset-0 z-[200]" aria-modal="true" role="dialog">
|
|
130
|
+
{/* Backdrop */}
|
|
131
|
+
<div
|
|
132
|
+
className={`absolute inset-0 bg-black/50 transition-opacity duration-300 ${isOpen ? 'opacity-100' : 'opacity-0'}`}
|
|
133
|
+
onClick={handleBackdropClick}
|
|
134
|
+
onKeyDown={(e) => {
|
|
135
|
+
if ((e.key === 'Enter' || e.key === ' ') && closeOnBackdropClick) {
|
|
136
|
+
e.preventDefault();
|
|
137
|
+
onClose();
|
|
138
|
+
}
|
|
139
|
+
}}
|
|
140
|
+
role="presentation"
|
|
141
|
+
/>
|
|
142
|
+
|
|
143
|
+
{/* Drawer Content */}
|
|
144
|
+
<div
|
|
145
|
+
ref={drawerRef}
|
|
146
|
+
tabIndex={-1}
|
|
147
|
+
className={`fixed top-0 bottom-0 flex flex-col bg-white shadow-xl ${sizeClasses[size]} ${position === 'left' ? 'left-0' : 'right-0'} transform transition-transform duration-300 ease-out ${translateClass} ${className || ''}`}
|
|
148
|
+
>
|
|
149
|
+
{children}
|
|
150
|
+
</div>
|
|
151
|
+
</div>,
|
|
152
|
+
document.body
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Drawer Sub-components
|
|
157
|
+
export interface DrawerHeaderProps extends HTMLAttributes<HTMLDivElement> {
|
|
158
|
+
children: ReactNode;
|
|
159
|
+
showCloseButton?: boolean;
|
|
160
|
+
onClose?: () => void;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
Drawer.Header = function DrawerHeader({
|
|
164
|
+
children,
|
|
165
|
+
showCloseButton = true,
|
|
166
|
+
onClose,
|
|
167
|
+
className,
|
|
168
|
+
...props
|
|
169
|
+
}: DrawerHeaderProps) {
|
|
170
|
+
return (
|
|
171
|
+
<div
|
|
172
|
+
className={`flex items-center justify-between border-b border-gray-200 px-4 py-3 ${className || ''}`}
|
|
173
|
+
{...props}
|
|
174
|
+
>
|
|
175
|
+
<h2 className="text-lg font-semibold text-gray-900">{children}</h2>
|
|
176
|
+
{showCloseButton && onClose && (
|
|
177
|
+
<button
|
|
178
|
+
type="button"
|
|
179
|
+
onClick={onClose}
|
|
180
|
+
className="rounded-md p-2 text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-500"
|
|
181
|
+
>
|
|
182
|
+
<span className="sr-only">Close</span>
|
|
183
|
+
<svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
184
|
+
<path
|
|
185
|
+
strokeLinecap="round"
|
|
186
|
+
strokeLinejoin="round"
|
|
187
|
+
strokeWidth={2}
|
|
188
|
+
d="M6 18L18 6M6 6l12 12"
|
|
189
|
+
/>
|
|
190
|
+
</svg>
|
|
191
|
+
</button>
|
|
192
|
+
)}
|
|
193
|
+
</div>
|
|
194
|
+
);
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
export interface DrawerBodyProps extends HTMLAttributes<HTMLDivElement> {
|
|
198
|
+
children: ReactNode;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
Drawer.Body = function DrawerBody({ children, className, ...props }: DrawerBodyProps) {
|
|
202
|
+
return (
|
|
203
|
+
<div className={`flex-1 overflow-y-auto px-4 py-3 ${className || ''}`} {...props}>
|
|
204
|
+
{children}
|
|
205
|
+
</div>
|
|
206
|
+
);
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
export interface DrawerFooterProps extends HTMLAttributes<HTMLDivElement> {
|
|
210
|
+
children: ReactNode;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
Drawer.Footer = function DrawerFooter({ children, className, ...props }: DrawerFooterProps) {
|
|
214
|
+
return (
|
|
215
|
+
<div className={`border-t border-gray-200 px-4 py-3 ${className || ''}`} {...props}>
|
|
216
|
+
{children}
|
|
217
|
+
</div>
|
|
218
|
+
);
|
|
219
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drawer Component System
|
|
3
|
+
*
|
|
4
|
+
* A slide-in panel for mobile navigation and side content.
|
|
5
|
+
* Eager-loaded because it uses compound component pattern (Drawer.Header, Drawer.Body).
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* import { Drawer } from '@salla.sa/twilight-theme-engine/components/drawer';
|
|
10
|
+
*
|
|
11
|
+
* function MobileNav() {
|
|
12
|
+
* const [isOpen, setIsOpen] = useState(false);
|
|
13
|
+
*
|
|
14
|
+
* return (
|
|
15
|
+
* <>
|
|
16
|
+
* <button onClick={() => setIsOpen(true)}>☰ Menu</button>
|
|
17
|
+
* <Drawer isOpen={isOpen} onClose={() => setIsOpen(false)} position="left">
|
|
18
|
+
* <Drawer.Header onClose={() => setIsOpen(false)}>Menu</Drawer.Header>
|
|
19
|
+
* <Drawer.Body>
|
|
20
|
+
* <nav className="space-y-2">
|
|
21
|
+
* <a href="/" className="block py-2">Home</a>
|
|
22
|
+
* <a href="/products" className="block py-2">Products</a>
|
|
23
|
+
* </nav>
|
|
24
|
+
* </Drawer.Body>
|
|
25
|
+
* </Drawer>
|
|
26
|
+
* </>
|
|
27
|
+
* );
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
export {
|
|
33
|
+
Drawer,
|
|
34
|
+
type DrawerProps,
|
|
35
|
+
type DrawerHeaderProps,
|
|
36
|
+
type DrawerBodyProps,
|
|
37
|
+
type DrawerFooterProps,
|
|
38
|
+
} from './Drawer';
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dropdown Component
|
|
3
|
+
*
|
|
4
|
+
* A controlled dropdown menu with keyboard navigation and click-away support.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```tsx
|
|
8
|
+
* function UserMenu() {
|
|
9
|
+
* const [isOpen, setIsOpen] = useState(false);
|
|
10
|
+
*
|
|
11
|
+
* return (
|
|
12
|
+
* <Dropdown isOpen={isOpen} onClose={() => setIsOpen(false)}>
|
|
13
|
+
* <Dropdown.Trigger onClick={() => setIsOpen(!isOpen)}>
|
|
14
|
+
* <button>Account ▼</button>
|
|
15
|
+
* </Dropdown.Trigger>
|
|
16
|
+
* <Dropdown.Menu>
|
|
17
|
+
* <Dropdown.Item href="/profile">Profile</Dropdown.Item>
|
|
18
|
+
* <Dropdown.Item href="/orders">Orders</Dropdown.Item>
|
|
19
|
+
* <Dropdown.Item onClick={handleLogout}>Logout</Dropdown.Item>
|
|
20
|
+
* </Dropdown.Menu>
|
|
21
|
+
* </Dropdown>
|
|
22
|
+
* );
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { type ReactNode, type HTMLAttributes } from 'react';
|
|
28
|
+
import { useClickAway } from '@uidotdev/usehooks';
|
|
29
|
+
|
|
30
|
+
export interface DropdownProps {
|
|
31
|
+
isOpen: boolean;
|
|
32
|
+
onClose: () => void;
|
|
33
|
+
children: ReactNode;
|
|
34
|
+
/**
|
|
35
|
+
* Position of the dropdown menu
|
|
36
|
+
* @default 'bottom-start'
|
|
37
|
+
*/
|
|
38
|
+
position?: 'bottom-start' | 'bottom-end' | 'top-start' | 'top-end';
|
|
39
|
+
/**
|
|
40
|
+
* Additional className for the dropdown container
|
|
41
|
+
*/
|
|
42
|
+
className?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const positionClasses = {
|
|
46
|
+
'bottom-start': 'top-full start-0',
|
|
47
|
+
'bottom-end': 'top-full end-0',
|
|
48
|
+
'top-start': 'bottom-full start-0',
|
|
49
|
+
'top-end': 'bottom-full end-0',
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export function Dropdown({ isOpen, onClose, children, className }: DropdownProps) {
|
|
53
|
+
// useClickAway returns a ref that we attach to the container
|
|
54
|
+
// It calls the callback when clicking outside the element
|
|
55
|
+
const dropdownRef = useClickAway<HTMLDivElement>(() => {
|
|
56
|
+
if (isOpen) {
|
|
57
|
+
onClose();
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<div ref={dropdownRef} className={`relative inline-block ${className || ''}`}>
|
|
63
|
+
{children}
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Trigger Component
|
|
69
|
+
export interface DropdownTriggerProps extends HTMLAttributes<HTMLDivElement> {
|
|
70
|
+
children: ReactNode;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
Dropdown.Trigger = function DropdownTrigger({
|
|
74
|
+
children,
|
|
75
|
+
className,
|
|
76
|
+
...props
|
|
77
|
+
}: DropdownTriggerProps) {
|
|
78
|
+
return (
|
|
79
|
+
<div className={`inline-block ${className || ''}`} {...props}>
|
|
80
|
+
{children}
|
|
81
|
+
</div>
|
|
82
|
+
);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// Menu Component
|
|
86
|
+
export interface DropdownMenuProps extends HTMLAttributes<HTMLUListElement> {
|
|
87
|
+
children: ReactNode;
|
|
88
|
+
isOpen?: boolean;
|
|
89
|
+
position?: DropdownProps['position'];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
Dropdown.Menu = function DropdownMenu({
|
|
93
|
+
children,
|
|
94
|
+
isOpen = false,
|
|
95
|
+
position = 'bottom-start',
|
|
96
|
+
className,
|
|
97
|
+
...props
|
|
98
|
+
}: DropdownMenuProps) {
|
|
99
|
+
if (!isOpen) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<ul
|
|
105
|
+
className={`absolute z-40 mt-1 min-w-[10rem] rounded-lg bg-white py-1 shadow-lg ring-1 ring-black/5 animate-in fade-in slide-in-from-top-1 duration-100 ${positionClasses[position]} ${className || ''}`}
|
|
106
|
+
role="menu"
|
|
107
|
+
{...props}
|
|
108
|
+
>
|
|
109
|
+
{children}
|
|
110
|
+
</ul>
|
|
111
|
+
);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// Item Component
|
|
115
|
+
export interface DropdownItemProps extends HTMLAttributes<HTMLLIElement> {
|
|
116
|
+
children: ReactNode;
|
|
117
|
+
href?: string;
|
|
118
|
+
onClick?: () => void;
|
|
119
|
+
disabled?: boolean;
|
|
120
|
+
active?: boolean;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
Dropdown.Item = function DropdownItem({
|
|
124
|
+
children,
|
|
125
|
+
href,
|
|
126
|
+
onClick,
|
|
127
|
+
disabled = false,
|
|
128
|
+
active = false,
|
|
129
|
+
className,
|
|
130
|
+
...props
|
|
131
|
+
}: DropdownItemProps) {
|
|
132
|
+
const classes = `flex cursor-pointer items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900 ${active ? 'bg-gray-100 text-gray-900' : ''} ${disabled ? 'cursor-not-allowed opacity-50 hover:bg-transparent' : ''} ${className || ''}`;
|
|
133
|
+
|
|
134
|
+
if (href) {
|
|
135
|
+
return (
|
|
136
|
+
<li role="menuitem" {...props}>
|
|
137
|
+
<a href={href} className={classes}>
|
|
138
|
+
{children}
|
|
139
|
+
</a>
|
|
140
|
+
</li>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return (
|
|
145
|
+
<li
|
|
146
|
+
role="menuitem"
|
|
147
|
+
onClick={disabled ? undefined : onClick}
|
|
148
|
+
onKeyDown={(e) => {
|
|
149
|
+
if (!disabled && (e.key === 'Enter' || e.key === ' ')) {
|
|
150
|
+
e.preventDefault();
|
|
151
|
+
onClick?.();
|
|
152
|
+
}
|
|
153
|
+
}}
|
|
154
|
+
tabIndex={disabled ? -1 : 0}
|
|
155
|
+
className={classes}
|
|
156
|
+
{...props}
|
|
157
|
+
>
|
|
158
|
+
{children}
|
|
159
|
+
</li>
|
|
160
|
+
);
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
// Divider Component
|
|
164
|
+
Dropdown.Divider = function DropdownDivider({ className }: HTMLAttributes<HTMLLIElement>) {
|
|
165
|
+
return <li className={`my-1 h-px bg-gray-200 ${className || ''}`} />;
|
|
166
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dropdown Component System
|
|
3
|
+
*
|
|
4
|
+
* A controlled dropdown menu with keyboard navigation.
|
|
5
|
+
* Eager-loaded because it uses compound component pattern (Dropdown.Trigger, Dropdown.Menu).
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* import { Dropdown } from '@salla.sa/twilight-theme-engine/components/dropdown';
|
|
10
|
+
*
|
|
11
|
+
* function SortDropdown() {
|
|
12
|
+
* const [isOpen, setIsOpen] = useState(false);
|
|
13
|
+
* const [sort, setSort] = useState('newest');
|
|
14
|
+
*
|
|
15
|
+
* return (
|
|
16
|
+
* <Dropdown isOpen={isOpen} onClose={() => setIsOpen(false)}>
|
|
17
|
+
* <Dropdown.Trigger onClick={() => setIsOpen(!isOpen)}>
|
|
18
|
+
* <button>Sort: {sort}</button>
|
|
19
|
+
* </Dropdown.Trigger>
|
|
20
|
+
* <Dropdown.Menu isOpen={isOpen}>
|
|
21
|
+
* <Dropdown.Item onClick={() => { setSort('newest'); setIsOpen(false); }}>
|
|
22
|
+
* Newest
|
|
23
|
+
* </Dropdown.Item>
|
|
24
|
+
* <Dropdown.Item onClick={() => { setSort('price-asc'); setIsOpen(false); }}>
|
|
25
|
+
* Price: Low to High
|
|
26
|
+
* </Dropdown.Item>
|
|
27
|
+
* </Dropdown.Menu>
|
|
28
|
+
* </Dropdown>
|
|
29
|
+
* );
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
export {
|
|
35
|
+
Dropdown,
|
|
36
|
+
type DropdownProps,
|
|
37
|
+
type DropdownTriggerProps,
|
|
38
|
+
type DropdownMenuProps,
|
|
39
|
+
type DropdownItemProps,
|
|
40
|
+
} from './Dropdown';
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { memo, useState, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
export interface BundleComponentProps {
|
|
4
|
+
data: {
|
|
5
|
+
component_name: string;
|
|
6
|
+
bundle_id: string;
|
|
7
|
+
[key: string]: unknown;
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const BundleComponent = memo(function BundleComponent(props: BundleComponentProps) {
|
|
12
|
+
const [mounted, setMounted] = useState(false);
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
setMounted(true);
|
|
16
|
+
}, []);
|
|
17
|
+
|
|
18
|
+
if (!mounted) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const { component_name: componentName, bundle_id: bundleId, ...rest } = props.data;
|
|
23
|
+
const config = JSON.stringify(rest);
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<>
|
|
27
|
+
<salla-custom-component
|
|
28
|
+
component-name={componentName}
|
|
29
|
+
bundle-id={bundleId}
|
|
30
|
+
config={config}
|
|
31
|
+
suppressHydrationWarning
|
|
32
|
+
/>
|
|
33
|
+
</>
|
|
34
|
+
);
|
|
35
|
+
});
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { Component, type ReactNode, type ErrorInfo } from 'react';
|
|
2
|
+
import { TwilightContext } from '../../providers/twilight-context';
|
|
3
|
+
import type { TwilightContextValue } from '../../types/twilight';
|
|
4
|
+
import type { HomeComponentData } from '../../types';
|
|
5
|
+
|
|
6
|
+
interface ComponentErrorBoundaryProps {
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
data: HomeComponentData;
|
|
9
|
+
position: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface ComponentErrorBoundaryState {
|
|
13
|
+
hasError: boolean;
|
|
14
|
+
error: Error | null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function DevelopmentErrorCard({ error, data }: { error: Error; data: HomeComponentData }) {
|
|
18
|
+
return (
|
|
19
|
+
<section
|
|
20
|
+
dir="ltr"
|
|
21
|
+
className="s-block container overflow-hidden bg-red-50 border border-red-300 p-4"
|
|
22
|
+
>
|
|
23
|
+
<p className="text-red-800 text-sm font-bold">Home Component Error: {data.path}</p>
|
|
24
|
+
<p className="text-red-700 text-sm mt-2">{error.message}</p>
|
|
25
|
+
<details className="mt-2">
|
|
26
|
+
<summary className="text-xs cursor-pointer text-red-600">Stack Trace</summary>
|
|
27
|
+
<pre className="text-xs mt-1 overflow-auto max-h-32 text-red-700">{error.stack}</pre>
|
|
28
|
+
</details>
|
|
29
|
+
<details className="mt-2">
|
|
30
|
+
<summary className="text-xs cursor-pointer text-red-600">Component Data</summary>
|
|
31
|
+
<pre className="text-xs mt-1 overflow-auto max-h-32 text-red-700">
|
|
32
|
+
{JSON.stringify(data, null, 2)}
|
|
33
|
+
</pre>
|
|
34
|
+
</details>
|
|
35
|
+
</section>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export class ComponentErrorBoundary extends Component<
|
|
40
|
+
ComponentErrorBoundaryProps,
|
|
41
|
+
ComponentErrorBoundaryState
|
|
42
|
+
> {
|
|
43
|
+
static contextType = TwilightContext;
|
|
44
|
+
|
|
45
|
+
declare context: TwilightContextValue | null;
|
|
46
|
+
|
|
47
|
+
constructor(props: ComponentErrorBoundaryProps) {
|
|
48
|
+
super(props);
|
|
49
|
+
this.state = { hasError: false, error: null };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
static getDerivedStateFromError(error: Error): ComponentErrorBoundaryState {
|
|
53
|
+
return { hasError: true, error };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Blocks are keyed by position, so a replacement block at the same position reuses
|
|
57
|
+
// this boundary. An error belongs to the block that threw it, not the position.
|
|
58
|
+
componentDidUpdate(prevProps: ComponentErrorBoundaryProps): void {
|
|
59
|
+
if (this.state.hasError && prevProps.data !== this.props.data) {
|
|
60
|
+
this.setState({ hasError: false, error: null });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
componentDidCatch(error: Error, errorInfo: ErrorInfo): void {
|
|
65
|
+
if (!this.context) {
|
|
66
|
+
console.error('[ComponentErrorBoundary] Must be used within TwilightProvider');
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const { log } = this.context;
|
|
70
|
+
const { data, position } = this.props;
|
|
71
|
+
|
|
72
|
+
log.groupCollapsed(`HomeComponent Error: ${data.path || 'unknown'}`);
|
|
73
|
+
log.error('ERROR', error.message);
|
|
74
|
+
log.label('COMPONENT', data.path || 'unknown');
|
|
75
|
+
log.label('POSITION', position);
|
|
76
|
+
log.label('URL', typeof window !== 'undefined' ? window.location.href : 'SSR');
|
|
77
|
+
log.label('TIME', new Date().toISOString());
|
|
78
|
+
log.trace('STACK TRACE', error.stack || '');
|
|
79
|
+
log.trace('COMPONENT STACK', errorInfo.componentStack || '');
|
|
80
|
+
log.dir('COMPONENT DATA', data);
|
|
81
|
+
log.groupEnd();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
render(): ReactNode {
|
|
85
|
+
const { hasError, error } = this.state;
|
|
86
|
+
const { children, data } = this.props;
|
|
87
|
+
|
|
88
|
+
if (hasError && error) {
|
|
89
|
+
if (process.env.NODE_ENV === 'production') {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return <DevelopmentErrorCard error={error} data={data} />;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return children;
|
|
97
|
+
}
|
|
98
|
+
}
|