@salla.sa/twilight-theme-engine 1.0.123 → 1.0.125
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-VFCTMUDL.js → AddToCartForm-Q6Y7BTDC.js} +3 -3
- package/dist/{AddToCartForm-VFCTMUDL.js.map → AddToCartForm-Q6Y7BTDC.js.map} +1 -1
- package/dist/{BlogAuthorPage-QVHRFMBQ.js → BlogAuthorPage-N34SKQIU.js} +17 -17
- package/dist/{BlogAuthorPage-QVHRFMBQ.js.map → BlogAuthorPage-N34SKQIU.js.map} +1 -1
- package/dist/{BlogCategoryPage-BP5WIW7X.js → BlogCategoryPage-FIQZZVFS.js} +17 -17
- package/dist/{BlogCategoryPage-BP5WIW7X.js.map → BlogCategoryPage-FIQZZVFS.js.map} +1 -1
- package/dist/{BlogPage-YUZCR2YV.js → BlogPage-HKUIDCPI.js} +15 -15
- package/dist/{BlogPage-YUZCR2YV.js.map → BlogPage-HKUIDCPI.js.map} +1 -1
- package/dist/{BlogSinglePage-BJHC7DVC.js → BlogSinglePage-YGMH5NZ2.js} +15 -15
- package/dist/{BlogSinglePage-BJHC7DVC.js.map → BlogSinglePage-YGMH5NZ2.js.map} +1 -1
- package/dist/{BlogTagPage-ND5KMCZZ.js → BlogTagPage-64BDH4C7.js} +17 -17
- package/dist/{BlogTagPage-ND5KMCZZ.js.map → BlogTagPage-64BDH4C7.js.map} +1 -1
- package/dist/{BrandsPage-FGJBQBFL.js → BrandsPage-TCYFRA6G.js} +16 -16
- package/dist/{BrandsPage-FGJBQBFL.js.map → BrandsPage-TCYFRA6G.js.map} +1 -1
- package/dist/{Breadcrumb-2LPY6KHS.js → Breadcrumb-25EMZG3Z.js} +15 -15
- package/dist/{Breadcrumb-2LPY6KHS.js.map → Breadcrumb-25EMZG3Z.js.map} +1 -1
- package/dist/{CartItem-UQF332H4.js → CartItem-QIGMCSE2.js} +15 -15
- package/dist/{CartItem-UQF332H4.js.map → CartItem-QIGMCSE2.js.map} +1 -1
- package/dist/{CartSummary-FV3I7CJV.js → CartSummary-QWTWFV6R.js} +15 -15
- package/dist/{CartSummary-FV3I7CJV.js.map → CartSummary-QWTWFV6R.js.map} +1 -1
- package/dist/{Copyright-AZHDEIJX.js → Copyright-XUD6PWUP.js} +3 -3
- package/dist/{Copyright-AZHDEIJX.js.map → Copyright-XUD6PWUP.js.map} +1 -1
- package/dist/{CustomerLayout-DYA77OLE.js → CustomerLayout-NIENY4TF.js} +15 -15
- package/dist/{CustomerLayout-DYA77OLE.js.map → CustomerLayout-NIENY4TF.js.map} +1 -1
- package/dist/{Footer-BCFWCWWI.js → Footer-3CF5V7OE.js} +4 -4
- package/dist/{Footer-BCFWCWWI.js.map → Footer-3CF5V7OE.js.map} +1 -1
- package/dist/{Header-HALNPMED.js → Header-O4Z6HYNA.js} +8 -8
- package/dist/{Header-HALNPMED.js.map → Header-O4Z6HYNA.js.map} +1 -1
- package/dist/{LiveEditorBar-6SKEWJOX.js → LiveEditorBar-IQLFFBHE.js} +46 -12
- package/dist/LiveEditorBar-IQLFFBHE.js.map +1 -0
- package/dist/{LoyaltyPage-LUQLKTDF.js → LoyaltyPage-DATO5W6R.js} +15 -15
- package/dist/{LoyaltyPage-LUQLKTDF.js.map → LoyaltyPage-DATO5W6R.js.map} +1 -1
- package/dist/{LoyaltyPointsPage-LJLCJBLR.js → LoyaltyPointsPage-YQAJ6PNQ.js} +6 -6
- package/dist/{LoyaltyPointsPage-LJLCJBLR.js.map → LoyaltyPointsPage-YQAJ6PNQ.js.map} +1 -1
- package/dist/{MainMenu-WPVP2XF6.js → MainMenu-NUJSMS3W.js} +5 -5
- package/dist/{MainMenu-WPVP2XF6.js.map → MainMenu-NUJSMS3W.js.map} +1 -1
- package/dist/{MasterLayout-UPFHTRV6.js → MasterLayout-6SJCANZN.js} +15 -15
- package/dist/{MasterLayout-UPFHTRV6.js.map → MasterLayout-6SJCANZN.js.map} +1 -1
- package/dist/{MobileMenu-KMTRWYHV.js → MobileMenu-N7P6Z6TM.js} +16 -16
- package/dist/{MobileMenu-KMTRWYHV.js.map → MobileMenu-N7P6Z6TM.js.map} +1 -1
- package/dist/{NotificationsPage-5GDS22E6.js → NotificationsPage-N4DAQ7RS.js} +15 -15
- package/dist/{NotificationsPage-5GDS22E6.js.map → NotificationsPage-N4DAQ7RS.js.map} +1 -1
- package/dist/{OrderSinglePage-EXVTMRJI.js → OrderSinglePage-NMWLWFX4.js} +16 -16
- package/dist/{OrderSinglePage-EXVTMRJI.js.map → OrderSinglePage-NMWLWFX4.js.map} +1 -1
- package/dist/{OrdersPage-JNIDVHV3.js → OrdersPage-WGA6SAU7.js} +15 -15
- package/dist/{OrdersPage-JNIDVHV3.js.map → OrdersPage-WGA6SAU7.js.map} +1 -1
- package/dist/{PageSingle-KX5XSBWJ.js → PageSingle-MZQWB3OW.js} +3 -3
- package/dist/{PageSingle-KX5XSBWJ.js.map → PageSingle-MZQWB3OW.js.map} +1 -1
- package/dist/{ProductDetails-7EI5PTFY.js → ProductDetails-HYVU4GWC.js} +4 -4
- package/dist/{ProductDetails-7EI5PTFY.js.map → ProductDetails-HYVU4GWC.js.map} +1 -1
- package/dist/{ProfilePage-6FSWT3RC.js → ProfilePage-M5RBRGMH.js} +15 -15
- package/dist/{ProfilePage-6FSWT3RC.js.map → ProfilePage-M5RBRGMH.js.map} +1 -1
- package/dist/{ReviewsPage-2JSZHLPR.js → ReviewsPage-PEQC4IDJ.js} +3 -3
- package/dist/{ReviewsPage-2JSZHLPR.js.map → ReviewsPage-PEQC4IDJ.js.map} +1 -1
- package/dist/{SeoCartWidget-2MI5P2RT.js → SeoCartWidget-P2MT5BRA.js} +15 -15
- package/dist/{SeoCartWidget-2MI5P2RT.js.map → SeoCartWidget-P2MT5BRA.js.map} +1 -1
- package/dist/{SettingsPage-BX3SZNO5.js → SettingsPage-OMPK72Z7.js} +15 -15
- package/dist/{SettingsPage-BX3SZNO5.js.map → SettingsPage-OMPK72Z7.js.map} +1 -1
- package/dist/{StoreClosedNotification-E7WEFZGO.js → StoreClosedNotification-NWEQHHAR.js} +16 -16
- package/dist/{StoreClosedNotification-E7WEFZGO.js.map → StoreClosedNotification-NWEQHHAR.js.map} +1 -1
- package/dist/{TestimonialsPage-5OEZMBRF.js → TestimonialsPage-4AUF6ZWG.js} +3 -3
- package/dist/{TestimonialsPage-5OEZMBRF.js.map → TestimonialsPage-4AUF6ZWG.js.map} +1 -1
- package/dist/{ThankYouPage-3XJUFGSE.js → ThankYouPage-R3EWHCT7.js} +15 -15
- package/dist/{ThankYouPage-3XJUFGSE.js.map → ThankYouPage-R3EWHCT7.js.map} +1 -1
- package/dist/{WalletPage-LUSSC5TL.js → WalletPage-KHQGRDQB.js} +16 -16
- package/dist/{WalletPage-LUSSC5TL.js.map → WalletPage-KHQGRDQB.js.map} +1 -1
- package/dist/{WishlistPage-JK2JLLMR.js → WishlistPage-YJXOMBQ4.js} +17 -17
- package/dist/{WishlistPage-JK2JLLMR.js.map → WishlistPage-YJXOMBQ4.js.map} +1 -1
- package/dist/api/advertisement.js +2 -2
- package/dist/api/apps.js +2 -2
- package/dist/api/blog.js +3 -3
- package/dist/api/brands.js +3 -3
- package/dist/api/cart.js +3 -3
- package/dist/api/category.js +3 -3
- package/dist/api/client.js +1 -1
- package/dist/api/home.js +2 -2
- package/dist/api/loyalty.js +3 -3
- package/dist/api/menu.js +2 -2
- package/dist/api/notification.js +2 -2
- package/dist/api/order.js +3 -3
- package/dist/api/page.js +3 -3
- package/dist/api/product.js +3 -3
- package/dist/api/profile.js +1 -1
- package/dist/api/store.js +2 -2
- package/dist/api/translations.js +2 -2
- package/dist/api/user.js +2 -2
- package/dist/api/wallet.js +2 -2
- package/dist/api/wishlist.js +4 -4
- package/dist/{chunk-N67C5QRH.js → chunk-2CJGX6QU.js} +3 -3
- package/dist/{chunk-N67C5QRH.js.map → chunk-2CJGX6QU.js.map} +1 -1
- package/dist/{chunk-3L4TXBS7.js → chunk-2YCR7EEN.js} +3 -3
- package/dist/{chunk-3L4TXBS7.js.map → chunk-2YCR7EEN.js.map} +1 -1
- package/dist/{chunk-IXZPQR4M.js → chunk-5D26ZL3R.js} +5 -5
- package/dist/{chunk-IXZPQR4M.js.map → chunk-5D26ZL3R.js.map} +1 -1
- package/dist/{chunk-WAZYTMTB.js → chunk-653SIKQV.js} +3 -3
- package/dist/{chunk-WAZYTMTB.js.map → chunk-653SIKQV.js.map} +1 -1
- package/dist/{chunk-SLXTJLF2.js → chunk-B3KRYCIN.js} +4 -4
- package/dist/{chunk-SLXTJLF2.js.map → chunk-B3KRYCIN.js.map} +1 -1
- package/dist/{chunk-EEZNB6HS.js → chunk-DHBDQF2L.js} +4 -4
- package/dist/{chunk-EEZNB6HS.js.map → chunk-DHBDQF2L.js.map} +1 -1
- package/dist/{chunk-2CORNDFS.js → chunk-DMOBC6K3.js} +3 -3
- package/dist/{chunk-2CORNDFS.js.map → chunk-DMOBC6K3.js.map} +1 -1
- package/dist/{chunk-HGEKTJVV.js → chunk-E6J4TSI4.js} +4 -4
- package/dist/{chunk-HGEKTJVV.js.map → chunk-E6J4TSI4.js.map} +1 -1
- package/dist/{chunk-C4GXSAYT.js → chunk-EADPFZYQ.js} +4 -4
- package/dist/{chunk-C4GXSAYT.js.map → chunk-EADPFZYQ.js.map} +1 -1
- package/dist/chunk-FMDGEAFD.js +14 -0
- package/dist/{chunk-DEY7MDGF.js.map → chunk-FMDGEAFD.js.map} +1 -1
- package/dist/{chunk-UBNOFV7C.js → chunk-HEGWAECO.js} +3 -3
- package/dist/{chunk-UBNOFV7C.js.map → chunk-HEGWAECO.js.map} +1 -1
- package/dist/{chunk-3BIT754E.js → chunk-I6SWCTCV.js} +4 -4
- package/dist/{chunk-3BIT754E.js.map → chunk-I6SWCTCV.js.map} +1 -1
- package/dist/{chunk-4KRALUHW.js → chunk-IVW2XKC3.js} +3 -3
- package/dist/{chunk-4KRALUHW.js.map → chunk-IVW2XKC3.js.map} +1 -1
- package/dist/{chunk-AFERG34I.js → chunk-IZTIOJNJ.js} +7 -7
- package/dist/{chunk-AFERG34I.js.map → chunk-IZTIOJNJ.js.map} +1 -1
- package/dist/{chunk-ESZBWB7X.js → chunk-JWBHXCLD.js} +3 -3
- package/dist/{chunk-ESZBWB7X.js.map → chunk-JWBHXCLD.js.map} +1 -1
- package/dist/{chunk-EPPCSJGJ.js → chunk-K4HJD556.js} +3 -3
- package/dist/{chunk-EPPCSJGJ.js.map → chunk-K4HJD556.js.map} +1 -1
- package/dist/{chunk-FLTPDDL3.js → chunk-LCJSZXXV.js} +7 -7
- package/dist/{chunk-FLTPDDL3.js.map → chunk-LCJSZXXV.js.map} +1 -1
- package/dist/{chunk-YPEWTS4L.js → chunk-MHS2SUIJ.js} +4 -4
- package/dist/{chunk-YPEWTS4L.js.map → chunk-MHS2SUIJ.js.map} +1 -1
- package/dist/{chunk-4X4FBQMO.js → chunk-MIKL53WC.js} +3 -3
- package/dist/{chunk-4X4FBQMO.js.map → chunk-MIKL53WC.js.map} +1 -1
- package/dist/{chunk-PDIFXQDX.js → chunk-MVNWWZWQ.js} +23 -4
- package/dist/chunk-MVNWWZWQ.js.map +1 -0
- package/dist/{chunk-3BCIBYJC.js → chunk-N5PGVFSO.js} +3 -3
- package/dist/{chunk-3BCIBYJC.js.map → chunk-N5PGVFSO.js.map} +1 -1
- package/dist/{chunk-ISTESEV6.js → chunk-N6X7ESEY.js} +7 -7
- package/dist/{chunk-ISTESEV6.js.map → chunk-N6X7ESEY.js.map} +1 -1
- package/dist/{chunk-MQLMYMEQ.js → chunk-NWYOIN5R.js} +3 -3
- package/dist/{chunk-MQLMYMEQ.js.map → chunk-NWYOIN5R.js.map} +1 -1
- package/dist/{chunk-74PTCV46.js → chunk-OBSTSW3E.js} +23 -22
- package/dist/chunk-OBSTSW3E.js.map +1 -0
- package/dist/{chunk-ITXVKNO7.js → chunk-OXUPXXE5.js} +4 -4
- package/dist/{chunk-ITXVKNO7.js.map → chunk-OXUPXXE5.js.map} +1 -1
- package/dist/{chunk-G2BBB5AI.js → chunk-P4M57HMD.js} +3 -3
- package/dist/{chunk-G2BBB5AI.js.map → chunk-P4M57HMD.js.map} +1 -1
- package/dist/{chunk-XK5CEGKA.js → chunk-PD7ZQY6T.js} +4 -4
- package/dist/{chunk-XK5CEGKA.js.map → chunk-PD7ZQY6T.js.map} +1 -1
- package/dist/{chunk-S5FRM4TG.js → chunk-PPTOCIKV.js} +6 -6
- package/dist/{chunk-S5FRM4TG.js.map → chunk-PPTOCIKV.js.map} +1 -1
- package/dist/{chunk-Y2OC2467.js → chunk-QFEJAVGD.js} +10 -10
- package/dist/{chunk-Y2OC2467.js.map → chunk-QFEJAVGD.js.map} +1 -1
- package/dist/{chunk-FXYR563Z.js → chunk-RVKKQGHR.js} +3 -3
- package/dist/{chunk-FXYR563Z.js.map → chunk-RVKKQGHR.js.map} +1 -1
- package/dist/{chunk-Z43BQ7ZH.js → chunk-SRMZHB5G.js} +4 -4
- package/dist/{chunk-Z43BQ7ZH.js.map → chunk-SRMZHB5G.js.map} +1 -1
- package/dist/{chunk-DPLMZQ2Q.js → chunk-ST6FMVXY.js} +3 -3
- package/dist/{chunk-DPLMZQ2Q.js.map → chunk-ST6FMVXY.js.map} +1 -1
- package/dist/{chunk-2Q6M3CE5.js → chunk-SVB3WGEG.js} +3 -3
- package/dist/{chunk-2Q6M3CE5.js.map → chunk-SVB3WGEG.js.map} +1 -1
- package/dist/{chunk-IMCS5L4A.js → chunk-UZXRQZV2.js} +3 -3
- package/dist/{chunk-IMCS5L4A.js.map → chunk-UZXRQZV2.js.map} +1 -1
- package/dist/{chunk-MDJK7WHN.js → chunk-V3E2UIZB.js} +4 -4
- package/dist/{chunk-MDJK7WHN.js.map → chunk-V3E2UIZB.js.map} +1 -1
- package/dist/{chunk-36FXIFZ6.js → chunk-VAX5TXTJ.js} +4 -4
- package/dist/{chunk-36FXIFZ6.js.map → chunk-VAX5TXTJ.js.map} +1 -1
- package/dist/{chunk-4JNLIYCE.js → chunk-XDZHVKBK.js} +3 -3
- package/dist/{chunk-4JNLIYCE.js.map → chunk-XDZHVKBK.js.map} +1 -1
- package/dist/{chunk-XJJMGLHB.js → chunk-YRWGUJOR.js} +12 -8
- package/dist/chunk-YRWGUJOR.js.map +1 -0
- package/dist/{chunk-ZTY326ZR.js → chunk-YYNH6PGX.js} +3 -3
- package/dist/{chunk-ZTY326ZR.js.map → chunk-YYNH6PGX.js.map} +1 -1
- package/dist/{chunk-PGXS2QT5.js → chunk-Z4BZW7QS.js} +3 -3
- package/dist/{chunk-PGXS2QT5.js.map → chunk-Z4BZW7QS.js.map} +1 -1
- package/dist/components/cart/index.js +1 -1
- package/dist/components/common/index.js +1 -1
- package/dist/components/drawer/index.js +13 -13
- package/dist/components/home/HomePageRenderer.d.ts.map +1 -1
- package/dist/components/home/index.js +17 -17
- package/dist/components/layout/index.js +4 -4
- package/dist/components/live-editor/LiveEditorBar.d.ts +5 -1
- package/dist/components/live-editor/LiveEditorBar.d.ts.map +1 -1
- package/dist/components/live-editor/frame.d.ts +7 -0
- package/dist/components/live-editor/frame.d.ts.map +1 -1
- package/dist/components/live-editor/index.d.ts.map +1 -1
- package/dist/components/product/index.js +14 -14
- package/dist/components/store/index.js +14 -14
- package/dist/dev/editor/ThemeEditor.d.ts.map +1 -1
- package/dist/dev/editor/index.js +108 -26
- package/dist/dev/editor/index.js.map +1 -1
- package/dist/dev/editor/live-actions.d.ts +19 -0
- package/dist/dev/editor/live-actions.d.ts.map +1 -0
- package/dist/dev/index.js +1 -1
- package/dist/{document-class-LQ6CAJYV.js → document-class-RKLOCL35.js} +11 -11
- package/dist/{document-class-LQ6CAJYV.js.map → document-class-RKLOCL35.js.map} +1 -1
- package/dist/hooks/index.js +13 -13
- package/dist/hooks/useGtm.js +13 -13
- package/dist/hooks/useMoney.js +13 -13
- package/dist/hooks/useStore.js +13 -13
- package/dist/hooks/useUser.js +3 -3
- package/dist/index.js +13 -13
- package/dist/{navigation-YFRK3OW2.js → navigation-DIKCKMZ6.js} +5 -5
- package/dist/{navigation-YFRK3OW2.js.map → navigation-DIKCKMZ6.js.map} +1 -1
- package/dist/{navigation-MEQGPFG2.js → navigation-HHJTUPN2.js} +4 -4
- package/dist/{navigation-MEQGPFG2.js.map → navigation-HHJTUPN2.js.map} +1 -1
- package/dist/{not-found-62H3K6KC.js → not-found-4KKQNZCQ.js} +16 -16
- package/dist/{not-found-62H3K6KC.js.map → not-found-4KKQNZCQ.js.map} +1 -1
- package/dist/providers/index.js +13 -13
- package/dist/routes/account/orders.js +4 -4
- package/dist/routes/account.js +13 -13
- package/dist/routes/blog.js +8 -8
- package/dist/routes/brands.js +4 -4
- package/dist/routes/cart.js +21 -21
- package/dist/routes/home.js +18 -18
- package/dist/routes/index.js +3 -3
- package/dist/routes/loyalty.js +5 -5
- package/dist/routes/page.js +4 -4
- package/dist/routes/product-listing.js +19 -19
- package/dist/routes/product.js +17 -17
- package/dist/routes/seo-redirects.js +3 -3
- package/dist/routes/testimonials.js +2 -2
- package/dist/routes/thank-you.js +4 -4
- package/dist/skeleton.js +18 -18
- package/dist/{store-lookup-HQYYF3ZS.js → store-lookup-EN7CBHC3.js} +8 -8
- package/dist/{store-lookup-HQYYF3ZS.js.map → store-lookup-EN7CBHC3.js.map} +1 -1
- package/dist/tanstack.js +11 -11
- package/dist/utils/index.js +2 -2
- package/package.json +1 -1
- package/tailwind/components/home/HomePageRenderer.tsx.txt +11 -6
- package/tailwind/components/live-editor/LiveEditorBar.tsx.txt +64 -9
- package/tailwind/components/live-editor/frame.ts.txt +34 -1
- package/tailwind/components/live-editor/index.tsx.txt +9 -7
- package/dist/LiveEditorBar-6SKEWJOX.js.map +0 -1
- package/dist/chunk-74PTCV46.js.map +0 -1
- package/dist/chunk-DEY7MDGF.js +0 -14
- package/dist/chunk-PDIFXQDX.js.map +0 -1
- package/dist/chunk-XJJMGLHB.js.map +0 -1
package/dist/tanstack.js
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
export { tanstackDocumentAdapter as attrs, createRouter, createTwilightRootRoute, useTanstackDocumentAttrs as useAttrs } from './chunk-
|
|
2
|
-
import { requestHostOf } from './chunk-
|
|
3
|
-
export { TanStackLinkAdapter, TanStackNavigationProvider, createTanStackLinkAdapter } from './chunk-
|
|
1
|
+
export { tanstackDocumentAdapter as attrs, createRouter, createTwilightRootRoute, useTanstackDocumentAttrs as useAttrs } from './chunk-QFEJAVGD.js';
|
|
2
|
+
import { requestHostOf } from './chunk-2YCR7EEN.js';
|
|
3
|
+
export { TanStackLinkAdapter, TanStackNavigationProvider, createTanStackLinkAdapter } from './chunk-2YCR7EEN.js';
|
|
4
4
|
import './chunk-Z4Y55HWX.js';
|
|
5
5
|
export { tanstackHeadAdapter as head, withHead } from './chunk-EHTLXK5F.js';
|
|
6
6
|
import { CRITICAL_STYLE_HREF } from './chunk-4FJGZQD3.js';
|
|
7
|
-
import './chunk-
|
|
8
|
-
import { readNativeAppHeaders } from './chunk-
|
|
9
|
-
import './chunk-
|
|
10
|
-
import './chunk-
|
|
7
|
+
import './chunk-N5PGVFSO.js';
|
|
8
|
+
import { readNativeAppHeaders } from './chunk-IVW2XKC3.js';
|
|
9
|
+
import './chunk-JWBHXCLD.js';
|
|
10
|
+
import './chunk-B3KRYCIN.js';
|
|
11
11
|
import { rootHead, maintenanceEvidence, MAINTENANCE_EVIDENCE_HEADER, maintenanceEvidenceHeader } from './chunk-KQEBYXYB.js';
|
|
12
12
|
import './chunk-JYV25VNZ.js';
|
|
13
|
-
import './chunk-
|
|
13
|
+
import './chunk-Z4BZW7QS.js';
|
|
14
14
|
import './chunk-5FJFN3IW.js';
|
|
15
15
|
import './chunk-GD26FI4J.js';
|
|
16
16
|
import './chunk-OM2GRPDH.js';
|
|
17
|
-
import './chunk-
|
|
17
|
+
import './chunk-HEGWAECO.js';
|
|
18
18
|
import './chunk-OUW6WESU.js';
|
|
19
19
|
import './chunk-JWXJPJBO.js';
|
|
20
20
|
export { mergeDocumentDescriptors as mergeAttrs } from './chunk-CW625T2E.js';
|
|
21
|
-
import './chunk-
|
|
22
|
-
import './chunk-
|
|
21
|
+
import './chunk-XDZHVKBK.js';
|
|
22
|
+
import './chunk-UZXRQZV2.js';
|
|
23
23
|
import './chunk-TYNZ5B2B.js';
|
|
24
24
|
import './chunk-BYMOPN2S.js';
|
|
25
25
|
import './chunk-2TLIULJZ.js';
|
package/dist/utils/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { stripUnsafeHtml } from '../chunk-RUHSV4Z6.js';
|
|
2
2
|
export { formatSallaPlural } from '../chunk-BT6HEEAP.js';
|
|
3
|
-
export { dispatchRouteChanged } from '../chunk-
|
|
3
|
+
export { dispatchRouteChanged } from '../chunk-N5PGVFSO.js';
|
|
4
4
|
export { sortOptions } from '../chunk-3LZJXKVO.js';
|
|
5
5
|
export { color } from '../chunk-VVZNW5CB.js';
|
|
6
6
|
export { applyAppScopeMocks, registerThemeHookSlots } from '../chunk-5FJFN3IW.js';
|
|
@@ -8,7 +8,7 @@ export { mergeHead, resolveHead, setHeadAdapter } from '../chunk-GD26FI4J.js';
|
|
|
8
8
|
export { buildBaseHead, buildHreflangAlternates, localeToOgLocale } from '../chunk-OM2GRPDH.js';
|
|
9
9
|
export { asset, cdn, isPlaceholder } from '../chunk-ZCH7W7DZ.js';
|
|
10
10
|
export { mergeDocumentDescriptors, mergeElementAttrs, normalizeClasses, syncAttrsToElement, toReactProps } from '../chunk-CW625T2E.js';
|
|
11
|
-
import '../chunk-
|
|
11
|
+
import '../chunk-UZXRQZV2.js';
|
|
12
12
|
export { getSallaSDK, getStoreIdentifier, onSallaEvent, whenSallaLoaded, whenSallaReady } from '../chunk-GYXUYHKO.js';
|
|
13
13
|
export { AUTH_TOKEN_COOKIE_NAME, clearAuthTokenCookie, extractTokenFromCookies } from '../chunk-FHU2MXZA.js';
|
|
14
14
|
import '../chunk-7VNVCHID.js';
|
package/package.json
CHANGED
|
@@ -205,6 +205,16 @@ function resolveComponentConfig(path: string): HomeComponentConfig | undefined {
|
|
|
205
205
|
return themeComponentConfig[path] ?? COMPONENT_CONFIG[path];
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
+
/**
|
|
209
|
+
* What the theme editor addresses a block by: its row id, the dev editor's
|
|
210
|
+
* layout id (less the remount suffix), or the uuid of one not saved yet.
|
|
211
|
+
*/
|
|
212
|
+
function liveBlockIdOf(data: HomeComponentData): string | undefined {
|
|
213
|
+
const devId = typeof data.__dev_id === 'string' ? data.__dev_id.replace(/~\d+$/, '') : undefined;
|
|
214
|
+
const id = data.id ?? devId ?? data.key;
|
|
215
|
+
return typeof id === 'number' || (typeof id === 'string' && id) ? String(id) : undefined;
|
|
216
|
+
}
|
|
217
|
+
|
|
208
218
|
/**
|
|
209
219
|
* Memoized component renderer with scroll-based lazy loading
|
|
210
220
|
* Components only mount when scrolled into view - preventing API calls until needed
|
|
@@ -253,12 +263,7 @@ export const HomeComponentRenderer = memo(function HomeComponentRenderer({
|
|
|
253
263
|
? config.id(data)
|
|
254
264
|
: config.id
|
|
255
265
|
: undefined;
|
|
256
|
-
|
|
257
|
-
const blockId = data.id ?? data.key;
|
|
258
|
-
const liveBlockId =
|
|
259
|
-
typeof blockId === 'number' || (typeof blockId === 'string' && blockId)
|
|
260
|
-
? String(blockId)
|
|
261
|
-
: undefined;
|
|
266
|
+
const liveBlockId = liveBlockIdOf(data);
|
|
262
267
|
const height = config?.height || '400px';
|
|
263
268
|
// A theme's config replaces the engine's, but keeps its mount timing unless the theme sets one.
|
|
264
269
|
const renderOnMount = config?.renderOnMount ?? COMPONENT_CONFIG[data.path]?.renderOnMount;
|
|
@@ -55,6 +55,8 @@ const BLOCK_ACTIONS: BlockAction[] = [
|
|
|
55
55
|
'delete',
|
|
56
56
|
];
|
|
57
57
|
|
|
58
|
+
const DEV_ACTIONS = BLOCK_ACTIONS.filter((action) => action !== 'hide');
|
|
59
|
+
|
|
58
60
|
/** Lucide-style 24px stroke icons. */
|
|
59
61
|
const ICONS: Record<BlockAction, ReactElement> = {
|
|
60
62
|
edit: (
|
|
@@ -172,6 +174,29 @@ function connected(el: HTMLElement | null): HTMLElement | null {
|
|
|
172
174
|
}
|
|
173
175
|
|
|
174
176
|
/** Re-renders on scroll, resize and layout shifts, so the overlay follows what it outlines. */
|
|
177
|
+
interface Box {
|
|
178
|
+
top: number;
|
|
179
|
+
left: number;
|
|
180
|
+
width: number;
|
|
181
|
+
height: number;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Where `el` is on screen. One with no box of its own (`display: contents`, as
|
|
186
|
+
* Raed's header) measures 0×0 at the corner, so it's measured by its children.
|
|
187
|
+
*/
|
|
188
|
+
function boxOf(el: Element): Box {
|
|
189
|
+
const rect = el.getBoundingClientRect();
|
|
190
|
+
if (rect.width || rect.height) return rect;
|
|
191
|
+
const boxes = Array.from(el.children, boxOf).filter((box) => box.width || box.height);
|
|
192
|
+
if (!boxes.length) return rect;
|
|
193
|
+
const top = Math.min(...boxes.map((box) => box.top));
|
|
194
|
+
const left = Math.min(...boxes.map((box) => box.left));
|
|
195
|
+
const right = Math.max(...boxes.map((box) => box.left + box.width));
|
|
196
|
+
const bottom = Math.max(...boxes.map((box) => box.top + box.height));
|
|
197
|
+
return { top, left, width: right - left, height: bottom - top };
|
|
198
|
+
}
|
|
199
|
+
|
|
175
200
|
function useLayoutTick(active: boolean): void {
|
|
176
201
|
const [, setTick] = useState(0);
|
|
177
202
|
useEffect(() => {
|
|
@@ -253,7 +278,7 @@ function useHeldLinkClicks(barRef: RefObject<HTMLDivElement | null>): void {
|
|
|
253
278
|
}
|
|
254
279
|
|
|
255
280
|
function Outline({ el, label }: { el: HTMLElement; label?: string }) {
|
|
256
|
-
const rect = el
|
|
281
|
+
const rect = boxOf(el);
|
|
257
282
|
return (
|
|
258
283
|
<div
|
|
259
284
|
data-testid="live-editor-outline"
|
|
@@ -269,12 +294,13 @@ interface ToolbarProps {
|
|
|
269
294
|
labels: Labels;
|
|
270
295
|
busy: BlockAction | null;
|
|
271
296
|
barRef: RefObject<HTMLDivElement | null>;
|
|
297
|
+
actions: BlockAction[];
|
|
272
298
|
onAct: (el: HTMLElement, action: BlockAction) => void;
|
|
273
299
|
}
|
|
274
300
|
|
|
275
301
|
/** One edit button for the header or footer; the full set for a block. */
|
|
276
|
-
function Toolbar({ target, labels, busy, barRef, onAct }: ToolbarProps) {
|
|
277
|
-
const rect = target
|
|
302
|
+
function Toolbar({ target, labels, busy, barRef, actions, onAct }: ToolbarProps) {
|
|
303
|
+
const rect = boxOf(target);
|
|
278
304
|
const style: CSSProperties = {
|
|
279
305
|
...BAR,
|
|
280
306
|
top: Math.max(rect.top + 8, 8),
|
|
@@ -312,7 +338,7 @@ function Toolbar({ target, labels, busy, barRef, onAct }: ToolbarProps) {
|
|
|
312
338
|
aria-label={labels.edit}
|
|
313
339
|
style={style}
|
|
314
340
|
>
|
|
315
|
-
{
|
|
341
|
+
{actions.map((action) => (
|
|
316
342
|
<button
|
|
317
343
|
key={action}
|
|
318
344
|
type="button"
|
|
@@ -331,13 +357,34 @@ function Toolbar({ target, labels, busy, barRef, onAct }: ToolbarProps) {
|
|
|
331
357
|
);
|
|
332
358
|
}
|
|
333
359
|
|
|
360
|
+
/**
|
|
361
|
+
* The block an action names. A uuid is shared by every copy of one component,
|
|
362
|
+
* so it comes with which copy, in page order; a row id is unique on its own.
|
|
363
|
+
*/
|
|
364
|
+
function blockRefOf(el: HTMLElement, id: string): { id: string; copy?: number } {
|
|
365
|
+
if (/^\d+$/.test(id)) return { id };
|
|
366
|
+
const copies = Array.from(document.querySelectorAll(`[${BLOCK_ATTRIBUTE}]`)).filter(
|
|
367
|
+
(block) => block.getAttribute(BLOCK_ATTRIBUTE) === id
|
|
368
|
+
);
|
|
369
|
+
return { id, copy: copies.indexOf(el) };
|
|
370
|
+
}
|
|
371
|
+
|
|
334
372
|
/**
|
|
335
373
|
* The live editor's action bar: hovering a home block outlines it and shows
|
|
336
374
|
* edit, hide, move, duplicate, add and delete; hovering the header or footer
|
|
337
375
|
* shows an edit button. Double-clicking either edits it. Each action is a
|
|
338
376
|
* message to the editor, which saves the change and reloads the preview.
|
|
377
|
+
*
|
|
378
|
+
* In the engine's dev editor (`dev`) edits reach the page live, so nothing
|
|
379
|
+
* waits for a reload, and there is no hide: its layout has no hidden blocks.
|
|
339
380
|
*/
|
|
340
|
-
export function LiveEditorBar({
|
|
381
|
+
export function LiveEditorBar({
|
|
382
|
+
origin,
|
|
383
|
+
dev = false,
|
|
384
|
+
}: {
|
|
385
|
+
origin: string;
|
|
386
|
+
dev?: boolean;
|
|
387
|
+
}): ReactElement | null {
|
|
341
388
|
const [hovered, setHovered] = useState<HTMLElement | null>(null);
|
|
342
389
|
const [editing, setEditing] = useState<HTMLElement | null>(null);
|
|
343
390
|
const [busy, setBusy] = useState<BlockAction | null>(null);
|
|
@@ -357,11 +404,12 @@ export function LiveEditorBar({ origin }: { origin: string }): ReactElement | nu
|
|
|
357
404
|
}
|
|
358
405
|
const id = el.getAttribute(BLOCK_ATTRIBUTE);
|
|
359
406
|
if (!id) return;
|
|
360
|
-
postToEditor(origin, `preview::${action}.component`,
|
|
407
|
+
postToEditor(origin, `preview::${action}.component`, blockRefOf(el, id));
|
|
361
408
|
if (action === 'edit') setEditing(el);
|
|
362
|
-
else if (action === 'hide' || action === 'duplicate' || action === 'delete')
|
|
409
|
+
else if (!dev && (action === 'hide' || action === 'duplicate' || action === 'delete'))
|
|
410
|
+
setBusy(action);
|
|
363
411
|
},
|
|
364
|
-
[origin]
|
|
412
|
+
[origin, dev]
|
|
365
413
|
);
|
|
366
414
|
|
|
367
415
|
useEffect(() => {
|
|
@@ -409,7 +457,14 @@ export function LiveEditorBar({ origin }: { origin: string }): ReactElement | nu
|
|
|
409
457
|
{selected && selected !== target && <Outline el={selected} />}
|
|
410
458
|
{target && <Outline el={target} label={targetArea ? labels[targetArea] : undefined} />}
|
|
411
459
|
{target && (
|
|
412
|
-
<Toolbar
|
|
460
|
+
<Toolbar
|
|
461
|
+
target={target}
|
|
462
|
+
labels={labels}
|
|
463
|
+
busy={busy}
|
|
464
|
+
barRef={barRef}
|
|
465
|
+
actions={dev ? DEV_ACTIONS : BLOCK_ACTIONS}
|
|
466
|
+
onAct={act}
|
|
467
|
+
/>
|
|
413
468
|
)}
|
|
414
469
|
</>,
|
|
415
470
|
host
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* protocol the twig themes' `live-themes-editor.js` speaks.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
import { isEditorFrame } from '../../dev/schema-types';
|
|
9
|
+
|
|
8
10
|
/** Where the theme editor runs. Messages go to the one that framed the page, never to `*`. */
|
|
9
11
|
export const EDITOR_ORIGINS = [
|
|
10
12
|
'https://s.salla.sa',
|
|
@@ -47,6 +49,21 @@ function parentOrigin(): string | null {
|
|
|
47
49
|
}
|
|
48
50
|
}
|
|
49
51
|
|
|
52
|
+
/**
|
|
53
|
+
* On a dev server, a page on another localhost port (an editor run locally, or
|
|
54
|
+
* a test harness) can frame the store too. Not its own origin: that is the
|
|
55
|
+
* engine's dev editor, which has its own controls.
|
|
56
|
+
*/
|
|
57
|
+
function isLocalEditor(origin: string): boolean {
|
|
58
|
+
if (origin === window.location.origin) return false;
|
|
59
|
+
try {
|
|
60
|
+
const { protocol, hostname } = new URL(origin);
|
|
61
|
+
return protocol === 'http:' && (hostname === 'localhost' || hostname === '127.0.0.1');
|
|
62
|
+
} catch {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
50
67
|
/** The theme editor's origin when it frames this page, otherwise `null`. Browser only. */
|
|
51
68
|
export function editorOrigin(): string | null {
|
|
52
69
|
if (typeof window === 'undefined') return null;
|
|
@@ -56,7 +73,23 @@ export function editorOrigin(): string | null {
|
|
|
56
73
|
} catch {
|
|
57
74
|
return null;
|
|
58
75
|
}
|
|
59
|
-
|
|
76
|
+
if (!origin) return null;
|
|
77
|
+
if ((EDITOR_ORIGINS as readonly string[]).includes(origin)) return origin;
|
|
78
|
+
return import.meta.env.DEV && isLocalEditor(origin) ? origin : null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface EditorFrame {
|
|
82
|
+
origin: string;
|
|
83
|
+
/** The engine's own dev editor (`/__twilight/editor`): edits apply live, and blocks can't be hidden. */
|
|
84
|
+
dev: boolean;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** The editor framing this page, the merchant's or (on a dev server) the engine's own. Browser only. */
|
|
88
|
+
export function editorFrame(): EditorFrame | null {
|
|
89
|
+
if (typeof window === 'undefined') return null;
|
|
90
|
+
if (import.meta.env.DEV && isEditorFrame()) return { origin: window.location.origin, dev: true };
|
|
91
|
+
const origin = editorOrigin();
|
|
92
|
+
return origin ? { origin, dev: false } : null;
|
|
60
93
|
}
|
|
61
94
|
|
|
62
95
|
export function postToEditor(
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { lazy, Suspense, useEffect, useState, type ReactElement } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { editorFrame, FRAME_LOADED_ACTION, postToEditor, type EditorFrame } from './frame';
|
|
3
3
|
|
|
4
4
|
/** Off the shell chunk: only a store framed by the theme editor ever loads it. */
|
|
5
5
|
const LazyLiveEditorBar = lazy(() =>
|
|
@@ -13,13 +13,15 @@ const LazyLiveEditorBar = lazy(() =>
|
|
|
13
13
|
* nothing on the server, so it changes no markup.
|
|
14
14
|
*/
|
|
15
15
|
export function LiveEditor(): ReactElement | null {
|
|
16
|
-
const [
|
|
16
|
+
const [frame, setFrame] = useState<EditorFrame | null>(null);
|
|
17
17
|
|
|
18
18
|
useEffect(() => {
|
|
19
|
-
const found =
|
|
19
|
+
const found = editorFrame();
|
|
20
20
|
if (!found) return;
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
setFrame(found);
|
|
22
|
+
// The merchant's editor checks the preview's health with it; the dev editor doesn't.
|
|
23
|
+
if (found.dev) return;
|
|
24
|
+
const announce = () => postToEditor(found.origin, FRAME_LOADED_ACTION);
|
|
23
25
|
if (document.readyState === 'complete') {
|
|
24
26
|
announce();
|
|
25
27
|
return;
|
|
@@ -28,10 +30,10 @@ export function LiveEditor(): ReactElement | null {
|
|
|
28
30
|
return () => window.removeEventListener('load', announce);
|
|
29
31
|
}, []);
|
|
30
32
|
|
|
31
|
-
if (!
|
|
33
|
+
if (!frame) return null;
|
|
32
34
|
return (
|
|
33
35
|
<Suspense fallback={null}>
|
|
34
|
-
<LazyLiveEditorBar origin={origin} />
|
|
36
|
+
<LazyLiveEditorBar origin={frame.origin} dev={frame.dev} />
|
|
35
37
|
</Suspense>
|
|
36
38
|
);
|
|
37
39
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/live-editor/LiveEditorBar.tsx"],"names":[],"mappings":"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","file":"LiveEditorBar-6SKEWJOX.js","sourcesContent":["import {\n useCallback,\n useEffect,\n useRef,\n useState,\n type CSSProperties,\n type ReactElement,\n type RefObject,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { useTranslation } from '../../providers/I18nProvider';\nimport { BLOCK_ATTRIBUTE, areaOf, postToEditor, targetOf, type BlockAction } from './frame';\n\nconst ACCENT = '#5dd5c4';\nconst LAYER = 2147483000;\n/** The editor reloads the preview after hide, duplicate and delete; stop waiting if it doesn't. */\nconst BUSY_TIMEOUT_MS = 10_000;\n/** How long a link's click waits for a second one, so a double-click edits instead of navigating. */\nconst DOUBLE_CLICK_MS = 300;\n\n/** Defaults for the `blocks.live_editor.*` keys, in the store's language. */\nconst DEFAULT_LABELS = {\n ar: {\n edit: 'تعديل',\n hide: 'إخفاء',\n moveup: 'تحريك لأعلى',\n movedown: 'تحريك لأسفل',\n duplicate: 'تكرار',\n add: 'إضافة عنصر',\n delete: 'حذف',\n header: 'رأس الصفحة',\n footer: 'ذيل الصفحة',\n },\n en: {\n edit: 'Edit',\n hide: 'Hide',\n moveup: 'Move up',\n movedown: 'Move down',\n duplicate: 'Duplicate',\n add: 'Add element',\n delete: 'Delete',\n header: 'Header',\n footer: 'Footer',\n },\n} as const;\n\n/** Twig's order, read from the start of the line: edit first, delete last. */\nconst BLOCK_ACTIONS: BlockAction[] = [\n 'edit',\n 'hide',\n 'moveup',\n 'movedown',\n 'duplicate',\n 'add',\n 'delete',\n];\n\n/** Lucide-style 24px stroke icons. */\nconst ICONS: Record<BlockAction, ReactElement> = {\n edit: (\n <>\n <path d=\"M12 20h9\" />\n <path d=\"M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4Z\" />\n </>\n ),\n hide: (\n <>\n <path d=\"M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 10 8 10 8a13.16 13.16 0 0 1-1.67 2.68\" />\n <path d=\"M6.61 6.61A13.53 13.53 0 0 0 2 12s3 8 10 8a9.74 9.74 0 0 0 5.39-1.61\" />\n <path d=\"M9.88 9.88a3 3 0 1 0 4.24 4.24\" />\n <path d=\"m2 2 20 20\" />\n </>\n ),\n moveup: <path d=\"m18 15-6-6-6 6\" />,\n movedown: <path d=\"m6 9 6 6 6-6\" />,\n duplicate: (\n <>\n <rect x=\"8\" y=\"8\" width=\"14\" height=\"14\" rx=\"2\" />\n <path d=\"M4 16a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2\" />\n </>\n ),\n add: <path d=\"M12 5v14M5 12h14\" />,\n delete: (\n <>\n <path d=\"M3 6h18\" />\n <path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6\" />\n <path d=\"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\" />\n </>\n ),\n};\n\nconst OUTLINE: CSSProperties = {\n position: 'fixed',\n zIndex: LAYER,\n pointerEvents: 'none',\n boxSizing: 'border-box',\n border: `2px solid ${ACCENT}`,\n};\n\nconst CHIP: CSSProperties = {\n position: 'absolute',\n top: 0,\n insetInlineStart: 0,\n padding: '4px 12px',\n background: ACCENT,\n color: '#0f172a',\n font: '500 14px/1.4 system-ui, sans-serif',\n whiteSpace: 'nowrap',\n};\n\nconst BAR: CSSProperties = {\n position: 'fixed',\n zIndex: LAYER + 1,\n display: 'flex',\n gap: 4,\n padding: 6,\n background: '#fff',\n borderRadius: 8,\n boxShadow: '0 4px 16px rgba(15, 23, 42, 0.18)',\n transform: 'translateX(-50%)',\n};\n\nfunction buttonStyle(action: BlockAction | 'area', disabled: boolean): CSSProperties {\n return {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: 6,\n minWidth: 36,\n height: 36,\n padding: '0 8px',\n border: 0,\n borderRadius: 6,\n background: 'transparent',\n color: action === 'delete' ? '#e11d48' : action === 'edit' ? '#0d9488' : '#475569',\n font: '500 14px/1 system-ui, sans-serif',\n cursor: disabled ? 'default' : 'pointer',\n opacity: disabled ? 0.35 : 1,\n };\n}\n\nfunction Icon({ children }: { children: ReactElement }) {\n return (\n <svg\n width=\"18\"\n height=\"18\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n {children}\n </svg>\n );\n}\n\n/** The blocks `el` can trade places with: its siblings in the same list. */\nfunction blockSiblings(el: HTMLElement): HTMLElement[] {\n const parent = el.parentElement;\n if (!parent) return [el];\n return Array.from(parent.children).filter(\n (child): child is HTMLElement =>\n child instanceof HTMLElement && child.hasAttribute(BLOCK_ATTRIBUTE)\n );\n}\n\nfunction connected(el: HTMLElement | null): HTMLElement | null {\n return el?.isConnected ? el : null;\n}\n\n/** Re-renders on scroll, resize and layout shifts, so the overlay follows what it outlines. */\nfunction useLayoutTick(active: boolean): void {\n const [, setTick] = useState(0);\n useEffect(() => {\n if (!active) return;\n let frame = 0;\n const schedule = () => {\n cancelAnimationFrame(frame);\n frame = requestAnimationFrame(() => setTick((n) => n + 1));\n };\n window.addEventListener('scroll', schedule, { capture: true, passive: true });\n window.addEventListener('resize', schedule);\n const observer = typeof ResizeObserver !== 'undefined' ? new ResizeObserver(schedule) : null;\n observer?.observe(document.body);\n return () => {\n cancelAnimationFrame(frame);\n window.removeEventListener('scroll', schedule, { capture: true });\n window.removeEventListener('resize', schedule);\n observer?.disconnect();\n };\n }, [active]);\n}\n\ntype LabelKey = keyof typeof DEFAULT_LABELS.en;\ntype Labels = Record<LabelKey, string>;\n\nfunction useLabels(): Labels {\n const { t, i18n } = useTranslation();\n const defaults = i18n?.language?.startsWith('ar') ? DEFAULT_LABELS.ar : DEFAULT_LABELS.en;\n return Object.fromEntries(\n (Object.keys(defaults) as LabelKey[]).map((key) => [\n key,\n t(`blocks.live_editor.${key}`, { defaultValue: defaults[key] }),\n ])\n ) as Labels;\n}\n\n/**\n * Holds a link's click inside a block, the header or the footer for\n * {@link DOUBLE_CLICK_MS}: a second click on the same link makes it a\n * double-click, which edits the area, and the link never navigates; otherwise\n * the click is replayed. A click on another link drops the first one.\n */\nfunction useHeldLinkClicks(barRef: RefObject<HTMLDivElement | null>): void {\n useEffect(() => {\n let held: { link: HTMLAnchorElement; timer: number } | null = null;\n let replaying = false;\n\n const onClick = (event: MouseEvent) => {\n if (replaying || event.button !== 0) return;\n if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;\n if (barRef.current?.contains(event.target as Node)) return;\n const link = event.target instanceof Element ? event.target.closest('a[href]') : null;\n if (!(link instanceof HTMLAnchorElement) || !targetOf(link)) return;\n\n // Before the router's own handler, which sits below the document.\n event.preventDefault();\n event.stopPropagation();\n const previous = held;\n if (previous) {\n window.clearTimeout(previous.timer);\n held = null;\n if (previous.link === link) return;\n }\n const timer = window.setTimeout(() => {\n held = null;\n replaying = true;\n link.click();\n replaying = false;\n }, DOUBLE_CLICK_MS);\n held = { link, timer };\n };\n\n document.addEventListener('click', onClick, true);\n return () => {\n document.removeEventListener('click', onClick, true);\n if (held) window.clearTimeout(held.timer);\n };\n }, [barRef]);\n}\n\nfunction Outline({ el, label }: { el: HTMLElement; label?: string }) {\n const rect = el.getBoundingClientRect();\n return (\n <div\n data-testid=\"live-editor-outline\"\n style={{ ...OUTLINE, top: rect.top, left: rect.left, width: rect.width, height: rect.height }}\n >\n {label && <span style={CHIP}>{label}</span>}\n </div>\n );\n}\n\ninterface ToolbarProps {\n target: HTMLElement;\n labels: Labels;\n busy: BlockAction | null;\n barRef: RefObject<HTMLDivElement | null>;\n onAct: (el: HTMLElement, action: BlockAction) => void;\n}\n\n/** One edit button for the header or footer; the full set for a block. */\nfunction Toolbar({ target, labels, busy, barRef, onAct }: ToolbarProps) {\n const rect = target.getBoundingClientRect();\n const style: CSSProperties = {\n ...BAR,\n top: Math.max(rect.top + 8, 8),\n left: rect.left + rect.width / 2,\n };\n\n if (areaOf(target)) {\n return (\n <div ref={barRef} data-testid=\"live-editor-bar\" style={style}>\n <button\n type=\"button\"\n data-testid=\"live-editor-edit\"\n style={buttonStyle('area', false)}\n onClick={() => onAct(target, 'edit')}\n >\n <Icon>{ICONS.edit}</Icon>\n {labels.edit}\n </button>\n </div>\n );\n }\n\n const siblings = blockSiblings(target);\n const index = siblings.indexOf(target);\n const isDisabled = (action: BlockAction) =>\n busy !== null ||\n (action === 'moveup' && index <= 0) ||\n (action === 'movedown' && index >= siblings.length - 1);\n\n return (\n <div\n ref={barRef}\n data-testid=\"live-editor-bar\"\n role=\"toolbar\"\n aria-label={labels.edit}\n style={style}\n >\n {BLOCK_ACTIONS.map((action) => (\n <button\n key={action}\n type=\"button\"\n data-testid={`live-editor-${action}`}\n title={labels[action]}\n aria-label={labels[action]}\n aria-busy={busy === action || undefined}\n disabled={isDisabled(action)}\n style={buttonStyle(action, isDisabled(action))}\n onClick={() => onAct(target, action)}\n >\n <Icon>{ICONS[action]}</Icon>\n </button>\n ))}\n </div>\n );\n}\n\n/**\n * The live editor's action bar: hovering a home block outlines it and shows\n * edit, hide, move, duplicate, add and delete; hovering the header or footer\n * shows an edit button. Double-clicking either edits it. Each action is a\n * message to the editor, which saves the change and reloads the preview.\n */\nexport function LiveEditorBar({ origin }: { origin: string }): ReactElement | null {\n const [hovered, setHovered] = useState<HTMLElement | null>(null);\n const [editing, setEditing] = useState<HTMLElement | null>(null);\n const [busy, setBusy] = useState<BlockAction | null>(null);\n const barRef = useRef<HTMLDivElement>(null);\n // Taken after mount, so nothing reads the DOM while rendering.\n const [host, setHost] = useState<HTMLElement | null>(null);\n const labels = useLabels();\n useHeldLinkClicks(barRef);\n\n const act = useCallback(\n (el: HTMLElement, action: BlockAction) => {\n const area = areaOf(el);\n if (area) {\n postToEditor(origin, `preview::edit.${area}`, { id: `store-${area}` });\n setEditing(el);\n return;\n }\n const id = el.getAttribute(BLOCK_ATTRIBUTE);\n if (!id) return;\n postToEditor(origin, `preview::${action}.component`, { id });\n if (action === 'edit') setEditing(el);\n else if (action === 'hide' || action === 'duplicate' || action === 'delete') setBusy(action);\n },\n [origin]\n );\n\n useEffect(() => {\n const onOver = (event: MouseEvent) => {\n // The bar sits over the block it acts on: reaching it keeps that block.\n if (barRef.current?.contains(event.target as Node)) return;\n setHovered(targetOf(event.target));\n };\n const onOut = (event: MouseEvent) => {\n if (!event.relatedTarget) setHovered(null);\n };\n const onDoubleClick = (event: MouseEvent) => {\n if (barRef.current?.contains(event.target as Node)) return;\n const el = targetOf(event.target);\n if (el) act(el, 'edit');\n };\n document.addEventListener('mouseover', onOver);\n document.addEventListener('mouseout', onOut);\n document.addEventListener('dblclick', onDoubleClick);\n return () => {\n document.removeEventListener('mouseover', onOver);\n document.removeEventListener('mouseout', onOut);\n document.removeEventListener('dblclick', onDoubleClick);\n };\n }, [act]);\n\n useEffect(() => setHost(document.body), []);\n\n useEffect(() => {\n if (!busy) return;\n const timer = window.setTimeout(() => setBusy(null), BUSY_TIMEOUT_MS);\n return () => window.clearTimeout(timer);\n }, [busy]);\n\n const target = connected(hovered);\n const selected = connected(editing);\n useLayoutTick(Boolean(target || selected));\n\n if (!host || (!target && !selected)) return null;\n\n const targetArea = target ? areaOf(target) : null;\n\n return createPortal(\n <>\n {selected && selected !== target && <Outline el={selected} />}\n {target && <Outline el={target} label={targetArea ? labels[targetArea] : undefined} />}\n {target && (\n <Toolbar target={target} labels={labels} busy={busy} barRef={barRef} onAct={act} />\n )}\n </>,\n host\n );\n}\n"]}
|