@numueg/theme-sdk 0.1.2 → 0.2.1
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/{index-CGx6FNqb.d.ts → entities-iiuRSPpk.d.mts} +1 -3
- package/dist/{index-zSb0FIyh.d.mts → entities-iiuRSPpk.d.ts} +1 -3
- package/dist/index.cjs +775 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +493 -38
- package/dist/index.d.ts +493 -38
- package/dist/index.mjs +759 -47
- package/dist/index.mjs.map +1 -1
- package/dist/normalize.d.mts +1 -1
- package/dist/normalize.d.ts +1 -1
- package/dist/theme-D0QybTQS.d.mts +197 -0
- package/dist/theme-D0QybTQS.d.ts +197 -0
- package/dist/types.d.mts +2 -2
- package/dist/types.d.ts +2 -2
- package/dist/v2-compat.cjs +248 -0
- package/dist/v2-compat.cjs.map +1 -0
- package/dist/v2-compat.d.mts +129 -0
- package/dist/v2-compat.d.ts +129 -0
- package/dist/v2-compat.mjs +242 -0
- package/dist/v2-compat.mjs.map +1 -0
- package/package.json +12 -1
- package/dist/theme-NWJAU9jd.d.mts +0 -128
- package/dist/theme-NWJAU9jd.d.ts +0 -128
package/dist/index.cjs
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var react = require('react');
|
|
4
|
+
var client = require('react-dom/client');
|
|
4
5
|
var jsxRuntime = require('react/jsx-runtime');
|
|
5
6
|
|
|
6
|
-
// src/
|
|
7
|
+
// src/types/theme.ts
|
|
8
|
+
var MAX_BLOCK_DEPTH = 5;
|
|
7
9
|
var ShopContext = react.createContext(null);
|
|
8
10
|
var ProductContext = react.createContext(null);
|
|
9
11
|
var CollectionContext = react.createContext(null);
|
|
@@ -12,6 +14,10 @@ var CustomerContext = react.createContext(null);
|
|
|
12
14
|
var ThemeSettingsContext = react.createContext(null);
|
|
13
15
|
var LocalizationContext = react.createContext(null);
|
|
14
16
|
var PageContext = react.createContext(null);
|
|
17
|
+
var CurrentTemplateContext = react.createContext("home");
|
|
18
|
+
var NavigationContext = react.createContext(
|
|
19
|
+
{}
|
|
20
|
+
);
|
|
15
21
|
function useLocalization() {
|
|
16
22
|
const ctx = react.useContext(LocalizationContext);
|
|
17
23
|
if (!ctx) throw new Error("useLocalization must be used within NuMuProvider");
|
|
@@ -120,6 +126,9 @@ function useThemeSettings() {
|
|
|
120
126
|
if (!ctx) throw new Error("useThemeSettings must be used within NuMuProvider");
|
|
121
127
|
return ctx;
|
|
122
128
|
}
|
|
129
|
+
function useCurrentTemplate() {
|
|
130
|
+
return react.useContext(CurrentTemplateContext);
|
|
131
|
+
}
|
|
123
132
|
function usePage() {
|
|
124
133
|
return react.useContext(PageContext);
|
|
125
134
|
}
|
|
@@ -489,15 +498,67 @@ function useCustomerAddresses() {
|
|
|
489
498
|
};
|
|
490
499
|
}
|
|
491
500
|
var cache = /* @__PURE__ */ new Map();
|
|
501
|
+
function localizeLabel(label, locale) {
|
|
502
|
+
if (!label) return "";
|
|
503
|
+
return label[locale] || label.en || label.ar || Object.values(label).find((v) => !!v) || "";
|
|
504
|
+
}
|
|
505
|
+
function mapResourceType(type) {
|
|
506
|
+
switch (type) {
|
|
507
|
+
case "product":
|
|
508
|
+
return "product";
|
|
509
|
+
case "collection":
|
|
510
|
+
return "collection";
|
|
511
|
+
case "page":
|
|
512
|
+
return "page";
|
|
513
|
+
case "blog":
|
|
514
|
+
return "blog";
|
|
515
|
+
case "article":
|
|
516
|
+
return "article";
|
|
517
|
+
case "http":
|
|
518
|
+
case "url":
|
|
519
|
+
return "url";
|
|
520
|
+
default:
|
|
521
|
+
return null;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
function toNavigationItem(raw, locale) {
|
|
525
|
+
return {
|
|
526
|
+
id: raw.id,
|
|
527
|
+
title: localizeLabel(raw.label, locale),
|
|
528
|
+
url: raw.url || "/",
|
|
529
|
+
resource_type: mapResourceType(raw.type),
|
|
530
|
+
resource_handle: raw.resource_id ?? null,
|
|
531
|
+
children: (raw.children ?? []).map(
|
|
532
|
+
(child) => toNavigationItem(child, locale)
|
|
533
|
+
)
|
|
534
|
+
};
|
|
535
|
+
}
|
|
492
536
|
function useNavigation(handle, options) {
|
|
537
|
+
const navMap = react.useContext(NavigationContext);
|
|
538
|
+
const localization = react.useContext(LocalizationContext);
|
|
539
|
+
const locale = localization?.locale ?? "en";
|
|
540
|
+
const hostProvidedAny = !!navMap && Object.keys(navMap).length > 0;
|
|
541
|
+
const rawItems = handle ? navMap?.[handle] : void 0;
|
|
542
|
+
const hostItems = react.useMemo(() => {
|
|
543
|
+
if (rawItems) return rawItems.map((it) => toNavigationItem(it, locale));
|
|
544
|
+
if (hostProvidedAny) return [];
|
|
545
|
+
return null;
|
|
546
|
+
}, [rawItems, hostProvidedAny, locale]);
|
|
493
547
|
const [items, setItems] = react.useState(
|
|
494
|
-
options?.initialItems ?? cache.get(handle) ?? []
|
|
548
|
+
hostItems ?? options?.initialItems ?? cache.get(handle) ?? []
|
|
495
549
|
);
|
|
496
550
|
const [loading, setLoading] = react.useState(
|
|
497
|
-
!options?.initialItems && !cache.has(handle)
|
|
551
|
+
hostItems === null && !options?.initialItems && !cache.has(handle)
|
|
498
552
|
);
|
|
499
553
|
const [error, setError] = react.useState(null);
|
|
500
554
|
react.useEffect(() => {
|
|
555
|
+
if (hostItems === null) return;
|
|
556
|
+
setItems(hostItems);
|
|
557
|
+
setLoading(false);
|
|
558
|
+
setError(null);
|
|
559
|
+
}, [hostItems]);
|
|
560
|
+
react.useEffect(() => {
|
|
561
|
+
if (hostItems !== null) return;
|
|
501
562
|
if (typeof window === "undefined") return;
|
|
502
563
|
if (!handle) {
|
|
503
564
|
setItems([]);
|
|
@@ -540,7 +601,7 @@ function useNavigation(handle, options) {
|
|
|
540
601
|
return () => {
|
|
541
602
|
cancelled = true;
|
|
542
603
|
};
|
|
543
|
-
}, [handle]);
|
|
604
|
+
}, [handle, hostItems === null]);
|
|
544
605
|
return { items, loading, error };
|
|
545
606
|
}
|
|
546
607
|
var EMPTY = {
|
|
@@ -614,34 +675,92 @@ function useSearch(query, options = {}) {
|
|
|
614
675
|
}, [query, mode, debounceMs, immediate, limit, types?.join(",")]);
|
|
615
676
|
return { query, results, loading, error };
|
|
616
677
|
}
|
|
678
|
+
var EVENT_TO_FUNNEL_STEP = {
|
|
679
|
+
page_view: "page_view",
|
|
680
|
+
view_item: "product_view",
|
|
681
|
+
view_collection: "page_view",
|
|
682
|
+
add_to_cart: "add_to_cart",
|
|
683
|
+
begin_checkout: "checkout_started",
|
|
684
|
+
purchase: "order_completed",
|
|
685
|
+
// Wave 2+ standard Meta events — backend's FUNNEL_STEP_TO_META_EVENT
|
|
686
|
+
// table accepts these as funnel steps directly.
|
|
687
|
+
search: "search",
|
|
688
|
+
lead: "lead",
|
|
689
|
+
sign_up: "complete_registration",
|
|
690
|
+
add_to_wishlist: "add_to_wishlist",
|
|
691
|
+
add_payment_info: "add_payment_info"
|
|
692
|
+
};
|
|
693
|
+
function readAttribution() {
|
|
694
|
+
if (typeof window === "undefined") return null;
|
|
695
|
+
try {
|
|
696
|
+
return window.__numu_attribution?.get?.() ?? null;
|
|
697
|
+
} catch {
|
|
698
|
+
return null;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
function readCustomerId() {
|
|
702
|
+
if (typeof window === "undefined") return null;
|
|
703
|
+
try {
|
|
704
|
+
return window.__numu_customer?.getId?.() ?? null;
|
|
705
|
+
} catch {
|
|
706
|
+
return null;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
function getFingerprint() {
|
|
710
|
+
if (typeof window === "undefined") return "ssr";
|
|
711
|
+
const env = readAttribution();
|
|
712
|
+
if (env?.session_id) return env.session_id;
|
|
713
|
+
const w = window;
|
|
714
|
+
if (w.__numu_session_fp) return w.__numu_session_fp;
|
|
715
|
+
const fp = crypto.randomUUID();
|
|
716
|
+
w.__numu_session_fp = fp;
|
|
717
|
+
return fp;
|
|
718
|
+
}
|
|
719
|
+
function dispatchAnalyticsEvent(eventName, payload = {}) {
|
|
720
|
+
if (typeof window === "undefined") return;
|
|
721
|
+
try {
|
|
722
|
+
window.dispatchEvent(
|
|
723
|
+
new CustomEvent("numu:analytics:event", {
|
|
724
|
+
detail: { event: eventName, payload, ts: Date.now() }
|
|
725
|
+
})
|
|
726
|
+
);
|
|
727
|
+
} catch {
|
|
728
|
+
}
|
|
729
|
+
const step = EVENT_TO_FUNNEL_STEP[eventName] ?? null;
|
|
730
|
+
const customerId = readCustomerId();
|
|
731
|
+
const body = step ? {
|
|
732
|
+
event_id: crypto.randomUUID(),
|
|
733
|
+
path: window.location.pathname,
|
|
734
|
+
fingerprint: getFingerprint(),
|
|
735
|
+
step,
|
|
736
|
+
step_data: payload,
|
|
737
|
+
referrer: typeof document !== "undefined" && document.referrer ? document.referrer : void 0,
|
|
738
|
+
attribution: readAttribution() ?? void 0,
|
|
739
|
+
customer_id: customerId ?? void 0
|
|
740
|
+
} : {
|
|
741
|
+
event: eventName,
|
|
742
|
+
payload,
|
|
743
|
+
ts: Date.now(),
|
|
744
|
+
attribution: readAttribution() ?? void 0,
|
|
745
|
+
customer_id: customerId ?? void 0
|
|
746
|
+
};
|
|
747
|
+
void (async () => {
|
|
748
|
+
try {
|
|
749
|
+
await fetch("/api/storefront/track", {
|
|
750
|
+
method: "POST",
|
|
751
|
+
headers: { "Content-Type": "application/json" },
|
|
752
|
+
body: JSON.stringify(body),
|
|
753
|
+
keepalive: true
|
|
754
|
+
// survive page-unload (e.g. begin_checkout → redirect)
|
|
755
|
+
});
|
|
756
|
+
} catch {
|
|
757
|
+
}
|
|
758
|
+
})();
|
|
759
|
+
}
|
|
617
760
|
function useAnalytics() {
|
|
618
761
|
const track = react.useCallback(
|
|
619
762
|
(eventName, payload = {}) => {
|
|
620
|
-
|
|
621
|
-
try {
|
|
622
|
-
window.dispatchEvent(
|
|
623
|
-
new CustomEvent("numu:analytics:event", {
|
|
624
|
-
detail: { event: eventName, payload, ts: Date.now() }
|
|
625
|
-
})
|
|
626
|
-
);
|
|
627
|
-
} catch {
|
|
628
|
-
}
|
|
629
|
-
void (async () => {
|
|
630
|
-
try {
|
|
631
|
-
await fetch("/api/storefront/track", {
|
|
632
|
-
method: "POST",
|
|
633
|
-
headers: { "Content-Type": "application/json" },
|
|
634
|
-
body: JSON.stringify({
|
|
635
|
-
event: eventName,
|
|
636
|
-
payload,
|
|
637
|
-
ts: Date.now()
|
|
638
|
-
}),
|
|
639
|
-
keepalive: true
|
|
640
|
-
// survive page-unload (e.g. begin_checkout → redirect)
|
|
641
|
-
});
|
|
642
|
-
} catch {
|
|
643
|
-
}
|
|
644
|
-
})();
|
|
763
|
+
dispatchAnalyticsEvent(eventName, payload);
|
|
645
764
|
},
|
|
646
765
|
[]
|
|
647
766
|
);
|
|
@@ -1328,6 +1447,16 @@ var EMPTY_CART = {
|
|
|
1328
1447
|
total: 0,
|
|
1329
1448
|
currency: "EGP"
|
|
1330
1449
|
};
|
|
1450
|
+
function normalizeCartFromServer(cart) {
|
|
1451
|
+
const toMajor = (n) => typeof n === "number" ? n / 100 : 0;
|
|
1452
|
+
return {
|
|
1453
|
+
...cart,
|
|
1454
|
+
subtotal: toMajor(cart.subtotal),
|
|
1455
|
+
total: toMajor(cart.total),
|
|
1456
|
+
...cart.discount_amount != null ? { discount_amount: toMajor(cart.discount_amount) } : {},
|
|
1457
|
+
items: Array.isArray(cart.items) ? cart.items.map((it) => ({ ...it, price: toMajor(it.price) })) : []
|
|
1458
|
+
};
|
|
1459
|
+
}
|
|
1331
1460
|
function readCsrfCookie() {
|
|
1332
1461
|
if (typeof document === "undefined") return null;
|
|
1333
1462
|
const match = document.cookie.match(/(?:^|;\s*)numu_csrf=([^;]+)/);
|
|
@@ -1357,8 +1486,23 @@ function NuMuProvider({
|
|
|
1357
1486
|
customer,
|
|
1358
1487
|
locale: initialLocale,
|
|
1359
1488
|
translations: initialTranslations,
|
|
1489
|
+
currentTemplate = "home",
|
|
1490
|
+
initialProducts,
|
|
1491
|
+
initialCollections,
|
|
1492
|
+
navigation,
|
|
1360
1493
|
children
|
|
1361
1494
|
}) {
|
|
1495
|
+
const pageValue = react.useMemo(
|
|
1496
|
+
() => ({
|
|
1497
|
+
type: currentTemplate,
|
|
1498
|
+
title: store?.name ?? "",
|
|
1499
|
+
data: {
|
|
1500
|
+
products: initialProducts ?? [],
|
|
1501
|
+
collections: initialCollections ?? []
|
|
1502
|
+
}
|
|
1503
|
+
}),
|
|
1504
|
+
[currentTemplate, store?.name, initialProducts, initialCollections]
|
|
1505
|
+
);
|
|
1362
1506
|
const [cart, setCart] = react.useState(
|
|
1363
1507
|
initialCart || { ...EMPTY_CART, currency: store.currency }
|
|
1364
1508
|
);
|
|
@@ -1515,7 +1659,7 @@ function NuMuProvider({
|
|
|
1515
1659
|
if (!res.ok || cancelled) return;
|
|
1516
1660
|
const data = await res.json();
|
|
1517
1661
|
if (data && typeof data === "object") {
|
|
1518
|
-
setCart(data);
|
|
1662
|
+
setCart(normalizeCartFromServer(data));
|
|
1519
1663
|
}
|
|
1520
1664
|
} catch {
|
|
1521
1665
|
}
|
|
@@ -1536,7 +1680,7 @@ function NuMuProvider({
|
|
|
1536
1680
|
return;
|
|
1537
1681
|
}
|
|
1538
1682
|
latestApplied.current = token;
|
|
1539
|
-
setCart(newCart);
|
|
1683
|
+
setCart(normalizeCartFromServer(newCart));
|
|
1540
1684
|
},
|
|
1541
1685
|
[]
|
|
1542
1686
|
);
|
|
@@ -1728,11 +1872,238 @@ function NuMuProvider({
|
|
|
1728
1872
|
defaultNumberFmt
|
|
1729
1873
|
]
|
|
1730
1874
|
);
|
|
1731
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ShopContext.Provider, { value: store, children: /* @__PURE__ */ jsxRuntime.jsx(ThemeSettingsContext.Provider, { value: themeSettings, children: /* @__PURE__ */ jsxRuntime.jsx(LocalizationContext.Provider, { value: localization, children: /* @__PURE__ */ jsxRuntime.jsx(CartContext.Provider, { value: cartValue, children: /* @__PURE__ */ jsxRuntime.jsx(CustomerContext.Provider, { value: customerState, children: /* @__PURE__ */ jsxRuntime.jsx(CustomerActionsContext.Provider, { value: customerActions, children }) }) }) }) }) });
|
|
1875
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ShopContext.Provider, { value: store, children: /* @__PURE__ */ jsxRuntime.jsx(ThemeSettingsContext.Provider, { value: themeSettings, children: /* @__PURE__ */ jsxRuntime.jsx(CurrentTemplateContext.Provider, { value: currentTemplate, children: /* @__PURE__ */ jsxRuntime.jsx(PageContext.Provider, { value: pageValue, children: /* @__PURE__ */ jsxRuntime.jsx(LocalizationContext.Provider, { value: localization, children: /* @__PURE__ */ jsxRuntime.jsx(CartContext.Provider, { value: cartValue, children: /* @__PURE__ */ jsxRuntime.jsx(CustomerContext.Provider, { value: customerState, children: /* @__PURE__ */ jsxRuntime.jsx(CustomerActionsContext.Provider, { value: customerActions, children: /* @__PURE__ */ jsxRuntime.jsx(NavigationContext.Provider, { value: navigation ?? {}, children }) }) }) }) }) }) }) }) });
|
|
1732
1876
|
}
|
|
1733
1877
|
function ProductProvider({ product, children }) {
|
|
1734
1878
|
return /* @__PURE__ */ jsxRuntime.jsx(ProductContext.Provider, { value: product, children });
|
|
1735
1879
|
}
|
|
1880
|
+
|
|
1881
|
+
// src/utils/styleTokens.ts
|
|
1882
|
+
var COLOR_ROLE_ALIASES = {
|
|
1883
|
+
background_color: "background",
|
|
1884
|
+
color_background: "background",
|
|
1885
|
+
bg_color: "background",
|
|
1886
|
+
text_color: "text",
|
|
1887
|
+
color_text: "text",
|
|
1888
|
+
primary_color: "primary",
|
|
1889
|
+
color_primary: "primary",
|
|
1890
|
+
secondary_color: "secondary",
|
|
1891
|
+
color_secondary: "secondary",
|
|
1892
|
+
accent_color: "accent",
|
|
1893
|
+
color_accent: "accent",
|
|
1894
|
+
border_color: "border",
|
|
1895
|
+
color_border: "border",
|
|
1896
|
+
button_color: "button",
|
|
1897
|
+
color_button: "button",
|
|
1898
|
+
button_text_color: "button-text",
|
|
1899
|
+
color_button_text: "button-text"
|
|
1900
|
+
};
|
|
1901
|
+
var FONT_ROLE_ALIASES = {
|
|
1902
|
+
heading_font: "heading",
|
|
1903
|
+
font_heading: "heading",
|
|
1904
|
+
headings_font: "heading",
|
|
1905
|
+
body_font: "body",
|
|
1906
|
+
font_body: "body",
|
|
1907
|
+
text_font: "body"
|
|
1908
|
+
};
|
|
1909
|
+
var FONT_REGISTRY = {
|
|
1910
|
+
cormorant: {
|
|
1911
|
+
stack: '"Cormorant Garamond", Georgia, "Times New Roman", serif',
|
|
1912
|
+
href: "https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,500;0,600;0,700;1,400&display=swap"
|
|
1913
|
+
},
|
|
1914
|
+
"dm-sans": {
|
|
1915
|
+
stack: '"DM Sans", system-ui, -apple-system, "Segoe UI", sans-serif',
|
|
1916
|
+
href: "https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,400;0,9..40,500;0,9..40,700&display=swap"
|
|
1917
|
+
},
|
|
1918
|
+
playfair: {
|
|
1919
|
+
stack: '"Playfair Display", Georgia, serif',
|
|
1920
|
+
href: "https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600;700&display=swap"
|
|
1921
|
+
},
|
|
1922
|
+
inter: {
|
|
1923
|
+
stack: '"Inter", system-ui, -apple-system, sans-serif',
|
|
1924
|
+
href: "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap"
|
|
1925
|
+
},
|
|
1926
|
+
poppins: {
|
|
1927
|
+
stack: '"Poppins", system-ui, sans-serif',
|
|
1928
|
+
href: "https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap"
|
|
1929
|
+
},
|
|
1930
|
+
montserrat: {
|
|
1931
|
+
stack: '"Montserrat", system-ui, sans-serif',
|
|
1932
|
+
href: "https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&display=swap"
|
|
1933
|
+
},
|
|
1934
|
+
lora: {
|
|
1935
|
+
stack: '"Lora", Georgia, serif',
|
|
1936
|
+
href: "https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400;0,500;0,600;1,400&display=swap"
|
|
1937
|
+
},
|
|
1938
|
+
cairo: {
|
|
1939
|
+
stack: '"Cairo", system-ui, sans-serif',
|
|
1940
|
+
href: "https://fonts.googleapis.com/css2?family=Cairo:wght@400;500;600;700&display=swap"
|
|
1941
|
+
},
|
|
1942
|
+
tajawal: {
|
|
1943
|
+
stack: '"Tajawal", system-ui, sans-serif',
|
|
1944
|
+
href: "https://fonts.googleapis.com/css2?family=Tajawal:wght@400;500;700&display=swap"
|
|
1945
|
+
}
|
|
1946
|
+
};
|
|
1947
|
+
var COLOR_RE = /^(#([0-9a-f]{3,8})|rgba?\(|hsla?\(|color\(|var\()/i;
|
|
1948
|
+
function isColorValue(v) {
|
|
1949
|
+
return typeof v === "string" && COLOR_RE.test(v.trim());
|
|
1950
|
+
}
|
|
1951
|
+
function isFontToken(v) {
|
|
1952
|
+
return typeof v === "string" && Object.prototype.hasOwnProperty.call(FONT_REGISTRY, v);
|
|
1953
|
+
}
|
|
1954
|
+
function injectFontLink(href) {
|
|
1955
|
+
if (typeof document === "undefined" || !href) return;
|
|
1956
|
+
const existing = document.querySelector(
|
|
1957
|
+
`link[data-numu-font][href="${href}"]`
|
|
1958
|
+
);
|
|
1959
|
+
if (existing) return;
|
|
1960
|
+
const link = document.createElement("link");
|
|
1961
|
+
link.rel = "stylesheet";
|
|
1962
|
+
link.href = href;
|
|
1963
|
+
link.setAttribute("data-numu-font", "");
|
|
1964
|
+
document.head.appendChild(link);
|
|
1965
|
+
}
|
|
1966
|
+
function resolveFontStack(value) {
|
|
1967
|
+
const entry = FONT_REGISTRY[value];
|
|
1968
|
+
if (entry) {
|
|
1969
|
+
if (entry.href) injectFontLink(entry.href);
|
|
1970
|
+
return entry.stack;
|
|
1971
|
+
}
|
|
1972
|
+
return value;
|
|
1973
|
+
}
|
|
1974
|
+
function applyGlobalStyleTokens(globalSettings, el) {
|
|
1975
|
+
if (!el || !globalSettings || typeof globalSettings !== "object") return;
|
|
1976
|
+
const style = el.style;
|
|
1977
|
+
for (const [key, value] of Object.entries(globalSettings)) {
|
|
1978
|
+
if (!key || key.startsWith("__")) continue;
|
|
1979
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
1980
|
+
for (const [role, c] of Object.entries(value)) {
|
|
1981
|
+
if (isColorValue(c)) style.setProperty(`--scheme-${key}-${role}`, c);
|
|
1982
|
+
}
|
|
1983
|
+
continue;
|
|
1984
|
+
}
|
|
1985
|
+
if (isColorValue(value)) {
|
|
1986
|
+
style.setProperty(`--theme-${key}`, value.trim());
|
|
1987
|
+
const role = COLOR_ROLE_ALIASES[key];
|
|
1988
|
+
if (role) style.setProperty(`--theme-color-${role}`, value.trim());
|
|
1989
|
+
continue;
|
|
1990
|
+
}
|
|
1991
|
+
if (isFontToken(value)) {
|
|
1992
|
+
const stack = resolveFontStack(value);
|
|
1993
|
+
style.setProperty(`--theme-${key}`, stack);
|
|
1994
|
+
const role = FONT_ROLE_ALIASES[key];
|
|
1995
|
+
if (role) style.setProperty(`--theme-font-${role}`, stack);
|
|
1996
|
+
continue;
|
|
1997
|
+
}
|
|
1998
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
1999
|
+
const v = String(value).trim();
|
|
2000
|
+
if (v) style.setProperty(`--theme-${key}`, v);
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
function pickStore(ctx) {
|
|
2005
|
+
const s = ctx.storeData ?? ctx.store;
|
|
2006
|
+
if (s) return s;
|
|
2007
|
+
return {
|
|
2008
|
+
id: "unknown",
|
|
2009
|
+
name: "Store",
|
|
2010
|
+
slug: "store",
|
|
2011
|
+
currency: "EGP",
|
|
2012
|
+
default_language: "en",
|
|
2013
|
+
use_nextjs_storefront: true
|
|
2014
|
+
};
|
|
2015
|
+
}
|
|
2016
|
+
function pickTemplate(ctx) {
|
|
2017
|
+
if (typeof ctx.currentTemplate === "string" && ctx.currentTemplate) {
|
|
2018
|
+
return ctx.currentTemplate;
|
|
2019
|
+
}
|
|
2020
|
+
const pageType = ctx.page?.type;
|
|
2021
|
+
if (typeof pageType === "string" && pageType) return pageType;
|
|
2022
|
+
return "home";
|
|
2023
|
+
}
|
|
2024
|
+
function pickDemo(ctx, themeSettings) {
|
|
2025
|
+
if (typeof ctx.demo === "boolean") return ctx.demo;
|
|
2026
|
+
const t = themeSettings.templates;
|
|
2027
|
+
return !t || Object.keys(t).length === 0;
|
|
2028
|
+
}
|
|
2029
|
+
var ThemeMountBridge = react.forwardRef(function ThemeMountBridge2({ ctx, mountEl, renderApp }, ref) {
|
|
2030
|
+
const [themeSettings, setThemeSettings] = react.useState(
|
|
2031
|
+
ctx.themeSettings
|
|
2032
|
+
);
|
|
2033
|
+
react.useImperativeHandle(
|
|
2034
|
+
ref,
|
|
2035
|
+
() => ({
|
|
2036
|
+
applyDraft: (next) => setThemeSettings((prev) => prev === next ? prev : next)
|
|
2037
|
+
}),
|
|
2038
|
+
[]
|
|
2039
|
+
);
|
|
2040
|
+
react.useEffect(() => {
|
|
2041
|
+
const gs = themeSettings.global_settings ?? {};
|
|
2042
|
+
applyGlobalStyleTokens(gs, mountEl);
|
|
2043
|
+
const headingFont = gs.heading_font;
|
|
2044
|
+
if (typeof headingFont === "string" && headingFont.trim()) {
|
|
2045
|
+
mountEl.style.setProperty(
|
|
2046
|
+
"--theme-heading_font",
|
|
2047
|
+
resolveFontStack(headingFont)
|
|
2048
|
+
);
|
|
2049
|
+
}
|
|
2050
|
+
const bodyFont = gs.body_font;
|
|
2051
|
+
if (typeof bodyFont === "string" && bodyFont.trim()) {
|
|
2052
|
+
mountEl.style.setProperty("--theme-body_font", resolveFontStack(bodyFont));
|
|
2053
|
+
}
|
|
2054
|
+
}, [themeSettings, mountEl]);
|
|
2055
|
+
const store = pickStore(ctx);
|
|
2056
|
+
const template = pickTemplate(ctx);
|
|
2057
|
+
const demo = pickDemo(ctx, themeSettings);
|
|
2058
|
+
const pageData = ctx.page?.data ?? {};
|
|
2059
|
+
const app = renderApp({
|
|
2060
|
+
currentTemplate: template,
|
|
2061
|
+
demo,
|
|
2062
|
+
page: ctx.page ?? null,
|
|
2063
|
+
store,
|
|
2064
|
+
themeSettings
|
|
2065
|
+
});
|
|
2066
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2067
|
+
NuMuProvider,
|
|
2068
|
+
{
|
|
2069
|
+
store,
|
|
2070
|
+
themeSettings,
|
|
2071
|
+
initialCart: ctx.initialCart,
|
|
2072
|
+
customer: ctx.customer,
|
|
2073
|
+
locale: ctx.locale,
|
|
2074
|
+
translations: ctx.translations,
|
|
2075
|
+
navigation: ctx.navigation,
|
|
2076
|
+
initialProducts: pageData.products,
|
|
2077
|
+
initialCollections: pageData.collections,
|
|
2078
|
+
currentTemplate: template,
|
|
2079
|
+
children: pageData.product ? /* @__PURE__ */ jsxRuntime.jsx(ProductProvider, { product: pageData.product, children: app }) : app
|
|
2080
|
+
}
|
|
2081
|
+
);
|
|
2082
|
+
});
|
|
2083
|
+
function mountTheme(el, ctx, renderApp) {
|
|
2084
|
+
const root = client.createRoot(el);
|
|
2085
|
+
const handleRef = { current: null };
|
|
2086
|
+
root.render(
|
|
2087
|
+
/* @__PURE__ */ jsxRuntime.jsx(react.StrictMode, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2088
|
+
ThemeMountBridge,
|
|
2089
|
+
{
|
|
2090
|
+
ctx,
|
|
2091
|
+
mountEl: el,
|
|
2092
|
+
renderApp,
|
|
2093
|
+
ref: (h) => {
|
|
2094
|
+
handleRef.current = h;
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
) })
|
|
2098
|
+
);
|
|
2099
|
+
return {
|
|
2100
|
+
applyDraft: (next) => handleRef.current?.applyDraft(next),
|
|
2101
|
+
cleanup: () => {
|
|
2102
|
+
root.unmount();
|
|
2103
|
+
handleRef.current = null;
|
|
2104
|
+
}
|
|
2105
|
+
};
|
|
2106
|
+
}
|
|
1736
2107
|
function CollectionProvider({ collection, children }) {
|
|
1737
2108
|
return /* @__PURE__ */ jsxRuntime.jsx(CollectionContext.Provider, { value: collection, children });
|
|
1738
2109
|
}
|
|
@@ -2375,6 +2746,239 @@ function LocaleSwitcher({
|
|
|
2375
2746
|
}
|
|
2376
2747
|
);
|
|
2377
2748
|
}
|
|
2749
|
+
function useIsEditor() {
|
|
2750
|
+
const [isEditor, setIsEditor] = react.useState(false);
|
|
2751
|
+
react.useEffect(() => {
|
|
2752
|
+
if (typeof window === "undefined") return;
|
|
2753
|
+
try {
|
|
2754
|
+
const params = new URLSearchParams(window.location.search);
|
|
2755
|
+
const flag = params.get("editor") === "v3" || params.get("preview") === "true";
|
|
2756
|
+
const inFrame = window.parent !== window;
|
|
2757
|
+
setIsEditor(flag && inFrame);
|
|
2758
|
+
} catch {
|
|
2759
|
+
}
|
|
2760
|
+
}, []);
|
|
2761
|
+
return isEditor;
|
|
2762
|
+
}
|
|
2763
|
+
function postFieldSelected(sectionId, settingId, blockId) {
|
|
2764
|
+
if (typeof window === "undefined") return;
|
|
2765
|
+
if (window.parent === window) return;
|
|
2766
|
+
try {
|
|
2767
|
+
window.parent.postMessage(
|
|
2768
|
+
{
|
|
2769
|
+
type: "numu:editor:select-field",
|
|
2770
|
+
payload: {
|
|
2771
|
+
sectionId,
|
|
2772
|
+
blockId: blockId ?? null,
|
|
2773
|
+
settingId
|
|
2774
|
+
}
|
|
2775
|
+
},
|
|
2776
|
+
"*"
|
|
2777
|
+
);
|
|
2778
|
+
} catch {
|
|
2779
|
+
}
|
|
2780
|
+
}
|
|
2781
|
+
var EditableText = react.forwardRef(
|
|
2782
|
+
function EditableText2({
|
|
2783
|
+
sectionId,
|
|
2784
|
+
blockId,
|
|
2785
|
+
settingId,
|
|
2786
|
+
value,
|
|
2787
|
+
as: Component2 = "span",
|
|
2788
|
+
html = false,
|
|
2789
|
+
placeholder,
|
|
2790
|
+
className,
|
|
2791
|
+
onClick,
|
|
2792
|
+
style,
|
|
2793
|
+
...rest
|
|
2794
|
+
}, ref) {
|
|
2795
|
+
const isEditor = useIsEditor();
|
|
2796
|
+
const handleClick = react.useCallback(
|
|
2797
|
+
(e) => {
|
|
2798
|
+
onClick?.(e);
|
|
2799
|
+
if (!isEditor) return;
|
|
2800
|
+
e.stopPropagation();
|
|
2801
|
+
postFieldSelected(sectionId, settingId, blockId);
|
|
2802
|
+
},
|
|
2803
|
+
[isEditor, onClick, sectionId, settingId, blockId]
|
|
2804
|
+
);
|
|
2805
|
+
const editorAttrs = react.useMemo(
|
|
2806
|
+
() => isEditor ? {
|
|
2807
|
+
"data-numu-editable": "text",
|
|
2808
|
+
"data-numu-section-id": sectionId,
|
|
2809
|
+
"data-numu-setting-id": settingId,
|
|
2810
|
+
"data-numu-block-id": blockId ?? void 0,
|
|
2811
|
+
role: "button",
|
|
2812
|
+
tabIndex: 0
|
|
2813
|
+
} : {},
|
|
2814
|
+
[isEditor, sectionId, settingId, blockId]
|
|
2815
|
+
);
|
|
2816
|
+
const effectiveStyle = isEditor ? {
|
|
2817
|
+
...style,
|
|
2818
|
+
cursor: "text",
|
|
2819
|
+
// Subtle dotted underline so the merchant discovers what's
|
|
2820
|
+
// editable. The dashed style + inherited color keeps the
|
|
2821
|
+
// affordance from clashing with the theme's typography.
|
|
2822
|
+
textDecoration: "underline dotted rgba(99, 102, 241, 0.7)",
|
|
2823
|
+
textUnderlineOffset: "4px"
|
|
2824
|
+
} : style;
|
|
2825
|
+
const isEmpty = value == null || value === "";
|
|
2826
|
+
const display = isEmpty && placeholder !== void 0 ? placeholder : value ?? "";
|
|
2827
|
+
if (html && typeof display === "string") {
|
|
2828
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2829
|
+
Component2,
|
|
2830
|
+
{
|
|
2831
|
+
ref,
|
|
2832
|
+
className,
|
|
2833
|
+
style: effectiveStyle,
|
|
2834
|
+
onClick: handleClick,
|
|
2835
|
+
dangerouslySetInnerHTML: { __html: display },
|
|
2836
|
+
...editorAttrs,
|
|
2837
|
+
...rest
|
|
2838
|
+
}
|
|
2839
|
+
);
|
|
2840
|
+
}
|
|
2841
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2842
|
+
Component2,
|
|
2843
|
+
{
|
|
2844
|
+
ref,
|
|
2845
|
+
className,
|
|
2846
|
+
style: effectiveStyle,
|
|
2847
|
+
onClick: handleClick,
|
|
2848
|
+
...editorAttrs,
|
|
2849
|
+
...rest,
|
|
2850
|
+
children: display
|
|
2851
|
+
}
|
|
2852
|
+
);
|
|
2853
|
+
}
|
|
2854
|
+
);
|
|
2855
|
+
var EMPTY_GIF = "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==";
|
|
2856
|
+
var EditableImage = react.forwardRef(
|
|
2857
|
+
function EditableImage2({
|
|
2858
|
+
sectionId,
|
|
2859
|
+
blockId,
|
|
2860
|
+
settingId,
|
|
2861
|
+
src,
|
|
2862
|
+
alt,
|
|
2863
|
+
emptyPlaceholderSrc,
|
|
2864
|
+
className,
|
|
2865
|
+
onClick,
|
|
2866
|
+
style,
|
|
2867
|
+
...rest
|
|
2868
|
+
}, ref) {
|
|
2869
|
+
const isEditor = useIsEditor();
|
|
2870
|
+
const isEmpty = !src;
|
|
2871
|
+
const handleClick = react.useCallback(
|
|
2872
|
+
(e) => {
|
|
2873
|
+
onClick?.(e);
|
|
2874
|
+
if (!isEditor) return;
|
|
2875
|
+
e.stopPropagation();
|
|
2876
|
+
postFieldSelected(sectionId, settingId, blockId);
|
|
2877
|
+
},
|
|
2878
|
+
[isEditor, onClick, sectionId, settingId, blockId]
|
|
2879
|
+
);
|
|
2880
|
+
const editorAttrs = react.useMemo(
|
|
2881
|
+
() => isEditor ? {
|
|
2882
|
+
"data-numu-editable": "image",
|
|
2883
|
+
"data-numu-section-id": sectionId,
|
|
2884
|
+
"data-numu-setting-id": settingId,
|
|
2885
|
+
"data-numu-block-id": blockId ?? void 0,
|
|
2886
|
+
role: "button",
|
|
2887
|
+
tabIndex: 0
|
|
2888
|
+
} : {},
|
|
2889
|
+
[isEditor, sectionId, settingId, blockId]
|
|
2890
|
+
);
|
|
2891
|
+
const effectiveStyle = isEditor ? {
|
|
2892
|
+
...style,
|
|
2893
|
+
cursor: "pointer",
|
|
2894
|
+
outline: isEmpty ? "2px dashed rgba(99, 102, 241, 0.6)" : "2px dashed transparent",
|
|
2895
|
+
outlineOffset: "-2px"
|
|
2896
|
+
} : style;
|
|
2897
|
+
const effectiveSrc = src || emptyPlaceholderSrc || EMPTY_GIF;
|
|
2898
|
+
const effectiveAlt = alt ?? "";
|
|
2899
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2900
|
+
"img",
|
|
2901
|
+
{
|
|
2902
|
+
ref,
|
|
2903
|
+
src: effectiveSrc,
|
|
2904
|
+
alt: effectiveAlt,
|
|
2905
|
+
className,
|
|
2906
|
+
style: effectiveStyle,
|
|
2907
|
+
onClick: handleClick,
|
|
2908
|
+
...editorAttrs,
|
|
2909
|
+
...rest
|
|
2910
|
+
}
|
|
2911
|
+
);
|
|
2912
|
+
}
|
|
2913
|
+
);
|
|
2914
|
+
var ICON_DEFS = {
|
|
2915
|
+
star: '<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>',
|
|
2916
|
+
heart: '<path d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.29 1.51 4.04 3 5.5l7 7Z"/>',
|
|
2917
|
+
check: '<path d="M20 6 9 17l-5-5"/>',
|
|
2918
|
+
"check-circle": '<circle cx="12" cy="12" r="10"/><path d="m9 12 2 2 4-4"/>',
|
|
2919
|
+
shield: '<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10Z"/>',
|
|
2920
|
+
"shield-check": '<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10Z"/><path d="m9 12 2 2 4-4"/>',
|
|
2921
|
+
truck: '<path d="M10 17h4V5H2v12h3"/><path d="M20 17h2v-3.34a4 4 0 0 0-1.17-2.83L19 9h-5v8h1"/><circle cx="7.5" cy="17.5" r="2.5"/><circle cx="17.5" cy="17.5" r="2.5"/>',
|
|
2922
|
+
"shopping-bag": '<path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4Z"/><path d="M3 6h18"/><path d="M16 10a4 4 0 0 1-8 0"/>',
|
|
2923
|
+
"shopping-cart": '<circle cx="8" cy="21" r="1"/><circle cx="19" cy="21" r="1"/><path d="M2.05 2.05h2l2.66 12.42a2 2 0 0 0 2 1.58h9.78a2 2 0 0 0 1.95-1.57l1.65-7.43H5.12"/>',
|
|
2924
|
+
gift: '<rect x="3" y="8" width="18" height="4" rx="1"/><path d="M12 8v13"/><path d="M19 12v7a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2v-7"/><path d="M7.5 8a2.5 2.5 0 0 1 0-5C9 3 10.5 4.5 12 8c1.5-3.5 3-5 4.5-5a2.5 2.5 0 0 1 0 5"/>',
|
|
2925
|
+
tag: '<path d="M20.59 13.41 13.42 20.6a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82Z"/><circle cx="7" cy="7" r="1"/>',
|
|
2926
|
+
zap: '<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>',
|
|
2927
|
+
sparkles: '<path d="M12 3 13.9 9.2 20 11l-6.1 1.8L12 19l-1.9-6.2L4 11l6.1-1.8L12 3Z"/><path d="M5 3v4"/><path d="M3 5h4"/><path d="M19 17v4"/><path d="M17 19h4"/>',
|
|
2928
|
+
flame: '<path d="M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.07-2.14-.22-4.05 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.15.43-2.29 1-3a2.5 2.5 0 0 0 2.5 2.5Z"/>',
|
|
2929
|
+
leaf: '<path d="M11 20A7 7 0 0 1 9.8 6.1C15.5 5 17 4.48 19 2c1 2 2 4.18 2 8 0 5.5-4.78 10-10 10Z"/><path d="M2 21c0-3 1.85-5.36 5.08-6"/>',
|
|
2930
|
+
globe: '<circle cx="12" cy="12" r="10"/><path d="M2 12h20"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10Z"/>',
|
|
2931
|
+
"map-pin": '<path d="M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z"/><circle cx="12" cy="10" r="3"/>',
|
|
2932
|
+
phone: '<path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.13.96.36 1.9.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.91.34 1.85.57 2.81.7A2 2 0 0 1 22 16.92Z"/>',
|
|
2933
|
+
mail: '<rect width="20" height="16" x="2" y="4" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/>',
|
|
2934
|
+
clock: '<circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/>',
|
|
2935
|
+
lock: '<rect width="18" height="11" x="3" y="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>',
|
|
2936
|
+
headphones: '<path d="M3 14h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-7a9 9 0 0 1 18 0v7a2 2 0 0 1-2 2h-1a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h3"/>',
|
|
2937
|
+
percent: '<line x1="19" x2="5" y1="5" y2="19"/><circle cx="6.5" cy="6.5" r="2.5"/><circle cx="17.5" cy="17.5" r="2.5"/>',
|
|
2938
|
+
users: '<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>',
|
|
2939
|
+
home: '<path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/>',
|
|
2940
|
+
store: '<path d="M2 7l2-4h16l2 4"/><path d="M4 7v13a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1V7"/><path d="M2 7h20"/><path d="M9 21v-6h6v6"/>',
|
|
2941
|
+
bell: '<path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/>',
|
|
2942
|
+
camera: '<path d="M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z"/><circle cx="12" cy="13" r="3"/>',
|
|
2943
|
+
image: '<rect width="18" height="18" x="3" y="3" rx="2" ry="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-3.09-3.09a2 2 0 0 0-2.82 0L6 21"/>',
|
|
2944
|
+
smile: '<circle cx="12" cy="12" r="10"/><path d="M8 14s1.5 2 4 2 4-2 4-2"/><line x1="9" x2="9.01" y1="9" y2="9"/><line x1="15" x2="15.01" y1="9" y2="9"/>',
|
|
2945
|
+
send: '<path d="m22 2-7 20-4-9-9-4Z"/><path d="M22 2 11 13"/>',
|
|
2946
|
+
"message-circle": '<path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5Z"/>',
|
|
2947
|
+
search: '<circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/>',
|
|
2948
|
+
package: '<path d="M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z"/><path d="m3.3 7 8.7 5 8.7-5"/><path d="M12 22V12"/>',
|
|
2949
|
+
award: '<circle cx="12" cy="8" r="6"/><path d="M15.477 12.89 17 22l-5-3-5 3 1.523-9.11"/>',
|
|
2950
|
+
coffee: '<path d="M10 2v2"/><path d="M14 2v2"/><path d="M6 2v2"/><path d="M16 8a1 1 0 0 1 1 1v8a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4V9a1 1 0 0 1 1-1h14a4 4 0 1 1 0 8h-1"/>'
|
|
2951
|
+
};
|
|
2952
|
+
var ICON_NAMES = Object.keys(ICON_DEFS);
|
|
2953
|
+
var IconMap = ICON_DEFS;
|
|
2954
|
+
var FALLBACK = ICON_DEFS["check-circle"];
|
|
2955
|
+
function Icon({
|
|
2956
|
+
name,
|
|
2957
|
+
size = 24,
|
|
2958
|
+
strokeWidth = 2,
|
|
2959
|
+
className,
|
|
2960
|
+
title
|
|
2961
|
+
}) {
|
|
2962
|
+
const inner = ICON_DEFS[name] ?? FALLBACK;
|
|
2963
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2964
|
+
"svg",
|
|
2965
|
+
{
|
|
2966
|
+
width: size,
|
|
2967
|
+
height: size,
|
|
2968
|
+
viewBox: "0 0 24 24",
|
|
2969
|
+
fill: "none",
|
|
2970
|
+
stroke: "currentColor",
|
|
2971
|
+
strokeWidth,
|
|
2972
|
+
strokeLinecap: "round",
|
|
2973
|
+
strokeLinejoin: "round",
|
|
2974
|
+
className,
|
|
2975
|
+
role: title ? "img" : void 0,
|
|
2976
|
+
"aria-hidden": title ? void 0 : true,
|
|
2977
|
+
"aria-label": title,
|
|
2978
|
+
dangerouslySetInnerHTML: { __html: inner }
|
|
2979
|
+
}
|
|
2980
|
+
);
|
|
2981
|
+
}
|
|
2378
2982
|
|
|
2379
2983
|
// src/utils/normalize.ts
|
|
2380
2984
|
var DEFAULT_THEME_ID = typeof process !== "undefined" && process.env?.NUMU_DEFAULT_THEME_ID || "modern";
|
|
@@ -2468,22 +3072,11 @@ function resolveThemeSettings(raw) {
|
|
|
2468
3072
|
}
|
|
2469
3073
|
|
|
2470
3074
|
// src/utils/federation.ts
|
|
2471
|
-
var SDK_SYMBOL_KEY = "__NUMU_SDK_SLOT__";
|
|
2472
|
-
var REACT_SYMBOL_KEY = "__NUMU_REACT_SLOT__";
|
|
2473
|
-
function globalSlot(name, slotKey) {
|
|
2474
|
-
const g = globalThis;
|
|
2475
|
-
let sym = g[slotKey];
|
|
2476
|
-
if (!sym) {
|
|
2477
|
-
sym = Symbol.for(name);
|
|
2478
|
-
g[slotKey] = sym;
|
|
2479
|
-
}
|
|
2480
|
-
return sym;
|
|
2481
|
-
}
|
|
2482
3075
|
function sdkSlot() {
|
|
2483
|
-
return
|
|
3076
|
+
return /* @__PURE__ */ Symbol.for("@numueg/theme-sdk:singleton");
|
|
2484
3077
|
}
|
|
2485
3078
|
function reactSlot() {
|
|
2486
|
-
return
|
|
3079
|
+
return /* @__PURE__ */ Symbol.for("@numueg/theme-sdk:react");
|
|
2487
3080
|
}
|
|
2488
3081
|
function registerSdkSingleton(sdk) {
|
|
2489
3082
|
if (typeof globalThis === "undefined") return;
|
|
@@ -2493,6 +3086,10 @@ function getSdkSingleton() {
|
|
|
2493
3086
|
if (typeof globalThis === "undefined") return null;
|
|
2494
3087
|
return globalThis[sdkSlot()] ?? null;
|
|
2495
3088
|
}
|
|
3089
|
+
function clearSdkSingleton() {
|
|
3090
|
+
if (typeof globalThis === "undefined") return;
|
|
3091
|
+
delete globalThis[sdkSlot()];
|
|
3092
|
+
}
|
|
2496
3093
|
function registerReactSingleton(react, reactDom) {
|
|
2497
3094
|
if (typeof globalThis === "undefined") return;
|
|
2498
3095
|
globalThis[reactSlot()] = {
|
|
@@ -2508,6 +3105,121 @@ function isSdkAvailable() {
|
|
|
2508
3105
|
return getSdkSingleton() !== null;
|
|
2509
3106
|
}
|
|
2510
3107
|
|
|
3108
|
+
// src/utils/dynamicSources.ts
|
|
3109
|
+
function isDynamicSource(value) {
|
|
3110
|
+
return typeof value === "object" && value !== null && typeof value.__numu_source === "string";
|
|
3111
|
+
}
|
|
3112
|
+
function dynamicSource(path) {
|
|
3113
|
+
return { __numu_source: path };
|
|
3114
|
+
}
|
|
3115
|
+
function resolveSourcePath(path, ctx) {
|
|
3116
|
+
const [root, ...rest] = path.split(".");
|
|
3117
|
+
switch (root) {
|
|
3118
|
+
case "product": {
|
|
3119
|
+
const p = ctx.product;
|
|
3120
|
+
if (!p) return null;
|
|
3121
|
+
return resolveProductField(p, rest.join("."));
|
|
3122
|
+
}
|
|
3123
|
+
case "collection": {
|
|
3124
|
+
const c = ctx.collection;
|
|
3125
|
+
if (!c) return null;
|
|
3126
|
+
return resolveCollectionField(c, rest.join("."));
|
|
3127
|
+
}
|
|
3128
|
+
case "store": {
|
|
3129
|
+
const s = ctx.store;
|
|
3130
|
+
if (!s) return null;
|
|
3131
|
+
return resolveStoreField(s, rest.join("."));
|
|
3132
|
+
}
|
|
3133
|
+
default:
|
|
3134
|
+
return null;
|
|
3135
|
+
}
|
|
3136
|
+
}
|
|
3137
|
+
function resolveProductField(p, field) {
|
|
3138
|
+
switch (field) {
|
|
3139
|
+
case "title":
|
|
3140
|
+
case "name":
|
|
3141
|
+
return p.name;
|
|
3142
|
+
case "description":
|
|
3143
|
+
return p.description ?? "";
|
|
3144
|
+
case "description_snippet":
|
|
3145
|
+
return (p.description ?? "").replace(/<[^>]+>/g, " ").trim().slice(0, 200);
|
|
3146
|
+
case "price":
|
|
3147
|
+
return p.price;
|
|
3148
|
+
case "sku":
|
|
3149
|
+
return p.variants?.[0]?.sku ?? null;
|
|
3150
|
+
case "image":
|
|
3151
|
+
case "first_image_url":
|
|
3152
|
+
return p.images?.[0]?.url ?? null;
|
|
3153
|
+
case "slug":
|
|
3154
|
+
return p.slug;
|
|
3155
|
+
default:
|
|
3156
|
+
return null;
|
|
3157
|
+
}
|
|
3158
|
+
}
|
|
3159
|
+
function resolveCollectionField(c, field) {
|
|
3160
|
+
switch (field) {
|
|
3161
|
+
case "title":
|
|
3162
|
+
case "name":
|
|
3163
|
+
return c.name;
|
|
3164
|
+
case "description":
|
|
3165
|
+
return c.description ?? "";
|
|
3166
|
+
case "description_snippet":
|
|
3167
|
+
return (c.description ?? "").replace(/<[^>]+>/g, " ").trim().slice(0, 200);
|
|
3168
|
+
case "image":
|
|
3169
|
+
return c.image_url ?? null;
|
|
3170
|
+
case "slug":
|
|
3171
|
+
return c.slug;
|
|
3172
|
+
case "product_count":
|
|
3173
|
+
return c.product_count ?? null;
|
|
3174
|
+
default:
|
|
3175
|
+
return null;
|
|
3176
|
+
}
|
|
3177
|
+
}
|
|
3178
|
+
function resolveStoreField(s, field) {
|
|
3179
|
+
switch (field) {
|
|
3180
|
+
case "name":
|
|
3181
|
+
return s.name ?? "";
|
|
3182
|
+
case "description":
|
|
3183
|
+
return s.description ?? "";
|
|
3184
|
+
case "logo":
|
|
3185
|
+
return s.logo_url ?? null;
|
|
3186
|
+
default:
|
|
3187
|
+
return null;
|
|
3188
|
+
}
|
|
3189
|
+
}
|
|
3190
|
+
function resolveDynamicValue(value, ctx) {
|
|
3191
|
+
if (!isDynamicSource(value)) return value;
|
|
3192
|
+
return resolveSourcePath(value.__numu_source, ctx);
|
|
3193
|
+
}
|
|
3194
|
+
function resolveSettingsMap(settings, ctx) {
|
|
3195
|
+
if (!settings || typeof settings !== "object") return settings;
|
|
3196
|
+
const input = settings;
|
|
3197
|
+
const out = {};
|
|
3198
|
+
for (const key of Object.keys(input)) {
|
|
3199
|
+
const v = input[key];
|
|
3200
|
+
if (isDynamicSource(v)) {
|
|
3201
|
+
const resolved = resolveSourcePath(v.__numu_source, ctx);
|
|
3202
|
+
out[key] = resolved;
|
|
3203
|
+
} else {
|
|
3204
|
+
out[key] = v;
|
|
3205
|
+
}
|
|
3206
|
+
}
|
|
3207
|
+
return out;
|
|
3208
|
+
}
|
|
3209
|
+
function useResolvedSettings(instance) {
|
|
3210
|
+
const product = react.useContext(ProductContext);
|
|
3211
|
+
const collection = react.useContext(CollectionContext);
|
|
3212
|
+
const store = react.useContext(ShopContext);
|
|
3213
|
+
const ctx = react.useMemo(
|
|
3214
|
+
() => ({ product, collection, store }),
|
|
3215
|
+
[product, collection, store]
|
|
3216
|
+
);
|
|
3217
|
+
return react.useMemo(() => {
|
|
3218
|
+
const settings = instance?.settings ?? {};
|
|
3219
|
+
return resolveSettingsMap(settings, ctx);
|
|
3220
|
+
}, [instance, ctx]);
|
|
3221
|
+
}
|
|
3222
|
+
|
|
2511
3223
|
// src/utils/defineSection.ts
|
|
2512
3224
|
var SECTION_MARKER = /* @__PURE__ */ Symbol.for("numu.theme.section");
|
|
2513
3225
|
var BLOCK_MARKER = /* @__PURE__ */ Symbol.for("numu.theme.block");
|
|
@@ -2674,12 +3386,19 @@ exports.CollectionContext = CollectionContext;
|
|
|
2674
3386
|
exports.CollectionProvider = CollectionProvider;
|
|
2675
3387
|
exports.CurrencySwitcher = CurrencySwitcher;
|
|
2676
3388
|
exports.CustomerContext = CustomerContext;
|
|
3389
|
+
exports.EditableImage = EditableImage;
|
|
3390
|
+
exports.EditableText = EditableText;
|
|
2677
3391
|
exports.Form = Form;
|
|
3392
|
+
exports.ICON_NAMES = ICON_NAMES;
|
|
3393
|
+
exports.Icon = Icon;
|
|
3394
|
+
exports.IconMap = IconMap;
|
|
2678
3395
|
exports.Image = Image;
|
|
2679
3396
|
exports.Link = Link;
|
|
2680
3397
|
exports.LocaleSwitcher = LocaleSwitcher;
|
|
2681
3398
|
exports.LocalizationContext = LocalizationContext;
|
|
3399
|
+
exports.MAX_BLOCK_DEPTH = MAX_BLOCK_DEPTH;
|
|
2682
3400
|
exports.Money = Money;
|
|
3401
|
+
exports.NavigationContext = NavigationContext;
|
|
2683
3402
|
exports.NuMuProvider = NuMuProvider;
|
|
2684
3403
|
exports.PageContext = PageContext;
|
|
2685
3404
|
exports.ProductCard = ProductCard;
|
|
@@ -2690,24 +3409,33 @@ exports.Section = Section;
|
|
|
2690
3409
|
exports.SectionContext = SectionContext;
|
|
2691
3410
|
exports.ShopContext = ShopContext;
|
|
2692
3411
|
exports.ThemeSettingsContext = ThemeSettingsContext;
|
|
3412
|
+
exports.applyGlobalStyleTokens = applyGlobalStyleTokens;
|
|
2693
3413
|
exports.assetUrl = assetUrl;
|
|
2694
3414
|
exports.availableValues = availableValues;
|
|
2695
3415
|
exports.buildLocaleBundle = buildLocaleBundle;
|
|
3416
|
+
exports.clearSdkSingleton = clearSdkSingleton;
|
|
2696
3417
|
exports.collectBlocks = collectBlocks;
|
|
2697
3418
|
exports.collectSections = collectSections;
|
|
2698
3419
|
exports.defaultVariant = defaultVariant;
|
|
2699
3420
|
exports.defineBlock = defineBlock;
|
|
2700
3421
|
exports.defineSection = defineSection;
|
|
3422
|
+
exports.dynamicSource = dynamicSource;
|
|
2701
3423
|
exports.findVariantByOptions = findVariantByOptions;
|
|
2702
3424
|
exports.flattenMessages = flattenMessages;
|
|
2703
3425
|
exports.getReactSingleton = getReactSingleton;
|
|
2704
3426
|
exports.getSdkSingleton = getSdkSingleton;
|
|
2705
3427
|
exports.isDefinedBlock = isDefinedBlock;
|
|
2706
3428
|
exports.isDefinedSection = isDefinedSection;
|
|
3429
|
+
exports.isDynamicSource = isDynamicSource;
|
|
2707
3430
|
exports.isSdkAvailable = isSdkAvailable;
|
|
3431
|
+
exports.mountTheme = mountTheme;
|
|
2708
3432
|
exports.pickTranslations = pickTranslations;
|
|
2709
3433
|
exports.registerReactSingleton = registerReactSingleton;
|
|
2710
3434
|
exports.registerSdkSingleton = registerSdkSingleton;
|
|
3435
|
+
exports.resolveDynamicValue = resolveDynamicValue;
|
|
3436
|
+
exports.resolveFontStack = resolveFontStack;
|
|
3437
|
+
exports.resolveSettingsMap = resolveSettingsMap;
|
|
3438
|
+
exports.resolveSourcePath = resolveSourcePath;
|
|
2711
3439
|
exports.resolveThemeSettings = resolveThemeSettings;
|
|
2712
3440
|
exports.sanitizeHtml = sanitizeHtml;
|
|
2713
3441
|
exports.useAnalytics = useAnalytics;
|
|
@@ -2718,6 +3446,7 @@ exports.useCollection = useCollection;
|
|
|
2718
3446
|
exports.useCollectionOptional = useCollectionOptional;
|
|
2719
3447
|
exports.useCollections = useCollections;
|
|
2720
3448
|
exports.useCurrency = useCurrency;
|
|
3449
|
+
exports.useCurrentTemplate = useCurrentTemplate;
|
|
2721
3450
|
exports.useCustomer = useCustomer;
|
|
2722
3451
|
exports.useCustomerActions = useCustomerActions;
|
|
2723
3452
|
exports.useCustomerAddresses = useCustomerAddresses;
|
|
@@ -2738,6 +3467,7 @@ exports.useProductOptional = useProductOptional;
|
|
|
2738
3467
|
exports.useProducts = useProducts;
|
|
2739
3468
|
exports.useRelatedProducts = useRelatedProducts;
|
|
2740
3469
|
exports.useReorder = useReorder;
|
|
3470
|
+
exports.useResolvedSettings = useResolvedSettings;
|
|
2741
3471
|
exports.useSearch = useSearch;
|
|
2742
3472
|
exports.useSection = useSection;
|
|
2743
3473
|
exports.useSectionOptional = useSectionOptional;
|