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