@numueg/theme-sdk 0.10.1 → 0.13.0
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/CHANGELOG.md +139 -0
- package/dist/{chunk-O4GLTNLE.cjs → chunk-H7RGDWSU.cjs} +3 -3
- package/dist/{chunk-O4GLTNLE.cjs.map → chunk-H7RGDWSU.cjs.map} +1 -1
- package/dist/{chunk-QFFTHIFZ.mjs → chunk-QPXMBK5U.mjs} +3 -3
- package/dist/{chunk-QFFTHIFZ.mjs.map → chunk-QPXMBK5U.mjs.map} +1 -1
- package/dist/{entities-B8378GKp.d.mts → entities-DRKn04q0.d.mts} +77 -1
- package/dist/{entities-B8378GKp.d.ts → entities-DRKn04q0.d.ts} +77 -1
- package/dist/index.cjs +336 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +492 -8
- package/dist/index.d.ts +492 -8
- package/dist/index.mjs +305 -21
- package/dist/index.mjs.map +1 -1
- package/dist/{mount-BDo4a42I.d.mts → mount-BTaDtz8k.d.mts} +1 -1
- package/dist/{mount-DQZu8aBB.d.ts → mount-CLQniVfc.d.ts} +1 -1
- package/dist/types.d.mts +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/v2-compat.d.mts +1 -1
- package/dist/v2-compat.d.ts +1 -1
- package/dist/validation.cjs +11 -11
- package/dist/validation.mjs +1 -1
- package/dist/verify.cjs +3 -3
- package/dist/verify.d.mts +2 -2
- package/dist/verify.d.ts +2 -2
- package/dist/verify.mjs +2 -2
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { resolveThemeSettings } from './chunk-XF2FGIVS.mjs';
|
|
2
|
-
export { KNOWN_SETTING_TYPES, KNOWN_TEMPLATES, REQUIRED_TEMPLATES, SDK_VERSION, THEME_CONTRACT_VERSION, mergeResults, validateBuiltManifest, validateManifest, validateSectionSchema, validateSettingsAgainstSchema } from './chunk-
|
|
2
|
+
export { KNOWN_SETTING_TYPES, KNOWN_TEMPLATES, REQUIRED_TEMPLATES, SDK_VERSION, THEME_CONTRACT_VERSION, mergeResults, validateBuiltManifest, validateManifest, validateSectionSchema, validateSettingsAgainstSchema } from './chunk-QPXMBK5U.mjs';
|
|
3
3
|
import { ShopContext, ThemeSettingsContext, CurrentTemplateContext, PageContext, LocalizationContext, CurrencyContext, CartContext, CustomerContext, NavigationContext, CollectionContext, ProductContext, useShop, useLocalization, useCustomer, useThemeSettings } from './chunk-ZYZZG4JR.mjs';
|
|
4
4
|
export { CartContext, CollectionContext, CurrencyContext, CustomerContext, LocalizationContext, NavigationContext, PageContext, ProductContext, ShopContext, ThemeSettingsContext, useCollections, useCustomer, useDirection, useFieldTranslation, useLocale, useLocalization, useNumberFormat, usePage, useProducts, useShop, useThemeSettings, useTranslation } from './chunk-ZYZZG4JR.mjs';
|
|
5
5
|
import { createContext, forwardRef, useState, useImperativeHandle, useEffect, useCallback, useMemo, useRef, useContext, useSyncExternalStore, StrictMode, createElement, Component } from 'react';
|
|
@@ -24,6 +24,20 @@ function useCollection() {
|
|
|
24
24
|
function useCollectionOptional() {
|
|
25
25
|
return useContext(CollectionContext);
|
|
26
26
|
}
|
|
27
|
+
function useListingHeading(options = {}) {
|
|
28
|
+
const collection = useContext(CollectionContext);
|
|
29
|
+
const clean = (v) => typeof v === "string" && v.trim() !== "" ? v.trim() : "";
|
|
30
|
+
const collectionName = clean(collection?.name);
|
|
31
|
+
const collectionDescription = clean(
|
|
32
|
+
collection?.description
|
|
33
|
+
);
|
|
34
|
+
return {
|
|
35
|
+
collection: collection ?? null,
|
|
36
|
+
isCollection: collectionName !== "",
|
|
37
|
+
title: collectionName || clean(options.title) || clean(options.defaultTitle) || "",
|
|
38
|
+
description: collectionDescription || clean(options.description) || ""
|
|
39
|
+
};
|
|
40
|
+
}
|
|
27
41
|
function useCart() {
|
|
28
42
|
const ctx = useContext(CartContext);
|
|
29
43
|
if (!ctx) throw new Error("useCart must be used within NuMuProvider");
|
|
@@ -32,6 +46,59 @@ function useCart() {
|
|
|
32
46
|
function useCurrentTemplate() {
|
|
33
47
|
return useContext(CurrentTemplateContext);
|
|
34
48
|
}
|
|
49
|
+
function isMetafield(value) {
|
|
50
|
+
return typeof value === "object" && value !== null && typeof value.namespace === "string" && typeof value.key === "string";
|
|
51
|
+
}
|
|
52
|
+
function pageMetafields(data) {
|
|
53
|
+
if (!data) return [];
|
|
54
|
+
const record = data.page;
|
|
55
|
+
for (const candidate of [record?.metafields, data.metafields]) {
|
|
56
|
+
if (Array.isArray(candidate)) return candidate.filter(isMetafield);
|
|
57
|
+
}
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
function useMetafields(owner) {
|
|
61
|
+
const product = useContext(ProductContext);
|
|
62
|
+
const collection = useContext(CollectionContext);
|
|
63
|
+
const page = useContext(PageContext);
|
|
64
|
+
switch (owner) {
|
|
65
|
+
case "product":
|
|
66
|
+
return product?.metafields ?? [];
|
|
67
|
+
case "collection":
|
|
68
|
+
return collection?.metafields ?? [];
|
|
69
|
+
case "page":
|
|
70
|
+
return pageMetafields(page?.data);
|
|
71
|
+
default:
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function useMetafield(owner, namespace, key) {
|
|
76
|
+
const fields = useMetafields(owner);
|
|
77
|
+
return fields.find((m) => m.namespace === namespace && m.key === key) ?? null;
|
|
78
|
+
}
|
|
79
|
+
function pageData() {
|
|
80
|
+
return useContext(PageContext)?.data;
|
|
81
|
+
}
|
|
82
|
+
function useBlogs() {
|
|
83
|
+
const data = pageData();
|
|
84
|
+
const blogs = data?.blogs;
|
|
85
|
+
return Array.isArray(blogs) ? blogs : [];
|
|
86
|
+
}
|
|
87
|
+
function useBlog() {
|
|
88
|
+
const data = pageData();
|
|
89
|
+
const blog = data?.blog ?? data?.article?.blog;
|
|
90
|
+
return blog && typeof blog === "object" ? blog : null;
|
|
91
|
+
}
|
|
92
|
+
function useArticles() {
|
|
93
|
+
const data = pageData();
|
|
94
|
+
const articles = data?.articles;
|
|
95
|
+
return Array.isArray(articles) ? articles : [];
|
|
96
|
+
}
|
|
97
|
+
function useArticle() {
|
|
98
|
+
const data = pageData();
|
|
99
|
+
const article = data?.article;
|
|
100
|
+
return article && typeof article === "object" ? article : null;
|
|
101
|
+
}
|
|
35
102
|
var SectionContext = createContext(null);
|
|
36
103
|
function useSection() {
|
|
37
104
|
const ctx = useContext(SectionContext);
|
|
@@ -81,16 +148,16 @@ function useCurrency() {
|
|
|
81
148
|
|
|
82
149
|
// src/hooks/useMoney.ts
|
|
83
150
|
function useMoney(currencyOverride) {
|
|
84
|
-
const { formatMoney } = useLocalization();
|
|
151
|
+
const { formatMoney: formatMoney2 } = useLocalization();
|
|
85
152
|
const shop = useShop();
|
|
86
153
|
const { selected, base, autoConvert, convert } = useCurrency();
|
|
87
154
|
return (amount) => {
|
|
88
155
|
const shouldConvert = autoConvert && !currencyOverride && !!selected && selected !== base;
|
|
89
156
|
if (shouldConvert) {
|
|
90
157
|
const converted = convert(Math.round(amount * 100), selected) / 100;
|
|
91
|
-
return
|
|
158
|
+
return formatMoney2(converted, selected);
|
|
92
159
|
}
|
|
93
|
-
return
|
|
160
|
+
return formatMoney2(amount, currencyOverride || shop?.currency);
|
|
94
161
|
};
|
|
95
162
|
}
|
|
96
163
|
var DEFAULT_WIDTHS = [320, 480, 640, 768, 1024, 1280, 1600, 1920];
|
|
@@ -955,6 +1022,133 @@ function useRelatedProducts(productId, options = {}) {
|
|
|
955
1022
|
});
|
|
956
1023
|
return { items: data ?? EMPTY4, loading: isLoading, error: error ?? null };
|
|
957
1024
|
}
|
|
1025
|
+
function useActivePromotions(page = "/", locale = "ar") {
|
|
1026
|
+
const key = `numu:promotions:${page}:${locale}`;
|
|
1027
|
+
const fetcher = useCallback(
|
|
1028
|
+
async (signal) => {
|
|
1029
|
+
const qs = new URLSearchParams({ page, locale });
|
|
1030
|
+
const res = await fetch(`/api/storefront/promotions?${qs.toString()}`, {
|
|
1031
|
+
credentials: "include",
|
|
1032
|
+
cache: "no-store",
|
|
1033
|
+
signal
|
|
1034
|
+
});
|
|
1035
|
+
if (!res.ok) return null;
|
|
1036
|
+
const json = await res.json();
|
|
1037
|
+
if (!json) return null;
|
|
1038
|
+
return (json.data ?? json) || null;
|
|
1039
|
+
},
|
|
1040
|
+
[page, locale]
|
|
1041
|
+
);
|
|
1042
|
+
const { data } = useCachedResource(
|
|
1043
|
+
key,
|
|
1044
|
+
fetcher,
|
|
1045
|
+
{ initialData: null }
|
|
1046
|
+
);
|
|
1047
|
+
return data ?? null;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
// src/utils/money.ts
|
|
1051
|
+
function resolveLocale(locale) {
|
|
1052
|
+
return locale === "ar" ? "ar-EG" : "en-EG";
|
|
1053
|
+
}
|
|
1054
|
+
function formatMoney(cents, options = {}) {
|
|
1055
|
+
const { currency, locale, fractionDigits = 0 } = options;
|
|
1056
|
+
const safe = Number.isFinite(cents) ? cents : 0;
|
|
1057
|
+
return new Intl.NumberFormat(resolveLocale(locale), {
|
|
1058
|
+
style: "currency",
|
|
1059
|
+
currency: currency || "EGP",
|
|
1060
|
+
maximumFractionDigits: fractionDigits
|
|
1061
|
+
}).format(safe / 100);
|
|
1062
|
+
}
|
|
1063
|
+
function formatMoneyMajor(amount, options = {}) {
|
|
1064
|
+
return formatMoney(
|
|
1065
|
+
(Number.isFinite(amount) ? amount : 0) * 100,
|
|
1066
|
+
options
|
|
1067
|
+
);
|
|
1068
|
+
}
|
|
1069
|
+
function centsToMajor(cents) {
|
|
1070
|
+
return (Number.isFinite(cents) ? cents : 0) / 100;
|
|
1071
|
+
}
|
|
1072
|
+
function majorToCents(amount) {
|
|
1073
|
+
return Math.round((Number.isFinite(amount) ? amount : 0) * 100);
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
// src/lib/promotions.ts
|
|
1077
|
+
function multibuyOffers(promotions) {
|
|
1078
|
+
if (!promotions) return [];
|
|
1079
|
+
const list = Array.isArray(promotions) ? promotions : promotions.auto_discounts ?? [];
|
|
1080
|
+
const offers = [];
|
|
1081
|
+
for (const promo of list) {
|
|
1082
|
+
const rule = promo?.discount_rule;
|
|
1083
|
+
if (!rule || rule.kind !== "multibuy") continue;
|
|
1084
|
+
const quantity = rule.multibuy_quantity;
|
|
1085
|
+
const groupPriceCents = rule.multibuy_price_cents;
|
|
1086
|
+
if (typeof quantity !== "number" || quantity < 2) continue;
|
|
1087
|
+
if (typeof groupPriceCents !== "number" || groupPriceCents <= 0) continue;
|
|
1088
|
+
const eligibleProductIds = promo.eligible_product_ids ?? [];
|
|
1089
|
+
const eligibleCategoryIds = promo.eligible_category_ids ?? [];
|
|
1090
|
+
offers.push({
|
|
1091
|
+
promotionId: promo.promotion_id,
|
|
1092
|
+
quantity,
|
|
1093
|
+
groupPriceCents,
|
|
1094
|
+
groupPriceMajor: centsToMajor(groupPriceCents),
|
|
1095
|
+
headline: promo.translated_content?.headline,
|
|
1096
|
+
eligibleProductIds,
|
|
1097
|
+
eligibleCategoryIds,
|
|
1098
|
+
// No scoping rows at all ⇒ every product qualifies. Note an older
|
|
1099
|
+
// backend that doesn't send these fields also lands here, which is the
|
|
1100
|
+
// right default: counting everything is what themes did before.
|
|
1101
|
+
isStoreWide: eligibleProductIds.length === 0 && eligibleCategoryIds.length === 0,
|
|
1102
|
+
raw: promo
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
return offers;
|
|
1106
|
+
}
|
|
1107
|
+
function offerIncludesProduct(offer, product) {
|
|
1108
|
+
if (!offer) return false;
|
|
1109
|
+
if (offer.isStoreWide) return true;
|
|
1110
|
+
const id = product?.product_id ?? product?.id;
|
|
1111
|
+
if (id && offer.eligibleProductIds.includes(id)) return true;
|
|
1112
|
+
const category = product?.category_id;
|
|
1113
|
+
return Boolean(category && offer.eligibleCategoryIds.includes(category));
|
|
1114
|
+
}
|
|
1115
|
+
function eligibleUnitsInCart(offer, cart) {
|
|
1116
|
+
if (!offer) return 0;
|
|
1117
|
+
return (cart?.items ?? []).reduce((sum, item) => {
|
|
1118
|
+
if (!item) return sum;
|
|
1119
|
+
if (!offerIncludesProduct(offer, item)) return sum;
|
|
1120
|
+
return sum + (item.quantity || 0);
|
|
1121
|
+
}, 0);
|
|
1122
|
+
}
|
|
1123
|
+
function offerProgress(offer, cart, eligibleUnits) {
|
|
1124
|
+
const empty = {
|
|
1125
|
+
unitsInCart: 0,
|
|
1126
|
+
unitsNeeded: 0,
|
|
1127
|
+
groupsUnlocked: 0,
|
|
1128
|
+
savingMajor: 0
|
|
1129
|
+
};
|
|
1130
|
+
if (!offer) return empty;
|
|
1131
|
+
const unitsInCart = typeof eligibleUnits === "number" ? Math.max(0, eligibleUnits) : eligibleUnitsInCart(offer, cart);
|
|
1132
|
+
const groupsUnlocked = Math.floor(unitsInCart / offer.quantity);
|
|
1133
|
+
const remainder = unitsInCart % offer.quantity;
|
|
1134
|
+
const unitsNeeded = remainder === 0 ? 0 : offer.quantity - remainder;
|
|
1135
|
+
const applied = (cart?.applied_promotions ?? []).find(
|
|
1136
|
+
(p) => p && p.id === offer.promotionId
|
|
1137
|
+
);
|
|
1138
|
+
return {
|
|
1139
|
+
unitsInCart,
|
|
1140
|
+
unitsNeeded,
|
|
1141
|
+
groupsUnlocked,
|
|
1142
|
+
savingMajor: applied ? applied.amount || 0 : 0
|
|
1143
|
+
};
|
|
1144
|
+
}
|
|
1145
|
+
function offerBeatsRegularPrice(offer, unitPriceMajor) {
|
|
1146
|
+
if (!offer || typeof unitPriceMajor !== "number" || unitPriceMajor <= 0) {
|
|
1147
|
+
return false;
|
|
1148
|
+
}
|
|
1149
|
+
const unitCents = Math.round(unitPriceMajor * 100);
|
|
1150
|
+
return unitCents * offer.quantity > offer.groupPriceCents;
|
|
1151
|
+
}
|
|
958
1152
|
function useProductSizeChart(productOverride) {
|
|
959
1153
|
const ctxProduct = useProductOptional();
|
|
960
1154
|
const product = productOverride ?? ctxProduct;
|
|
@@ -1428,11 +1622,27 @@ var EMPTY_CART = {
|
|
|
1428
1622
|
};
|
|
1429
1623
|
function normalizeCartFromServer(cart) {
|
|
1430
1624
|
const toMajor = (n) => typeof n === "number" ? n / 100 : 0;
|
|
1625
|
+
const { automatic_discount_cents: wireAutomaticCents, ...rest } = cart;
|
|
1431
1626
|
return {
|
|
1432
|
-
...
|
|
1627
|
+
...rest,
|
|
1433
1628
|
subtotal: toMajor(cart.subtotal),
|
|
1434
1629
|
total: toMajor(cart.total),
|
|
1435
1630
|
...cart.discount_amount != null ? { discount_amount: toMajor(cart.discount_amount) } : {},
|
|
1631
|
+
// Offers-v2 automatic promotions. These MUST be converted here too —
|
|
1632
|
+
// they arrive in cents next to already-major totals, so forwarding them
|
|
1633
|
+
// raw makes every theme render a saving 100x too large. This function is
|
|
1634
|
+
// the one and only cents→major boundary for the cart.
|
|
1635
|
+
//
|
|
1636
|
+
// The backend field is `automatic_discount_cents`; themes get
|
|
1637
|
+
// `automatic_discount` (major), because a `_cents` name holding pounds
|
|
1638
|
+
// invites exactly the double-divide this conversion exists to prevent.
|
|
1639
|
+
...wireAutomaticCents != null ? { automatic_discount: toMajor(wireAutomaticCents) } : {},
|
|
1640
|
+
...Array.isArray(cart.applied_promotions) ? {
|
|
1641
|
+
applied_promotions: cart.applied_promotions.map((p) => ({
|
|
1642
|
+
...p,
|
|
1643
|
+
amount: toMajor(p.amount)
|
|
1644
|
+
}))
|
|
1645
|
+
} : {},
|
|
1436
1646
|
items: Array.isArray(cart.items) ? cart.items.map((it) => {
|
|
1437
1647
|
const raw = it;
|
|
1438
1648
|
return {
|
|
@@ -2254,13 +2464,13 @@ function pickDemo(ctx, themeSettings) {
|
|
|
2254
2464
|
const t = themeSettings.templates;
|
|
2255
2465
|
return !t || Object.keys(t).length === 0;
|
|
2256
2466
|
}
|
|
2257
|
-
function wrapEntityProviders(app,
|
|
2467
|
+
function wrapEntityProviders(app, pageData2) {
|
|
2258
2468
|
let inner = app;
|
|
2259
|
-
if (
|
|
2260
|
-
inner = /* @__PURE__ */ jsx(CollectionProvider, { collection:
|
|
2469
|
+
if (pageData2.collection) {
|
|
2470
|
+
inner = /* @__PURE__ */ jsx(CollectionProvider, { collection: pageData2.collection, children: inner });
|
|
2261
2471
|
}
|
|
2262
|
-
if (
|
|
2263
|
-
inner = /* @__PURE__ */ jsx(ProductProvider, { product:
|
|
2472
|
+
if (pageData2.product) {
|
|
2473
|
+
inner = /* @__PURE__ */ jsx(ProductProvider, { product: pageData2.product, children: inner });
|
|
2264
2474
|
}
|
|
2265
2475
|
return inner;
|
|
2266
2476
|
}
|
|
@@ -2283,7 +2493,7 @@ var ThemeMountBridge = forwardRef(function ThemeMountBridge2({ ctx, mountEl, ren
|
|
|
2283
2493
|
const store = pickStore(ctx);
|
|
2284
2494
|
const template = pickTemplate(ctx);
|
|
2285
2495
|
const demo = pickDemo(ctx, themeSettings);
|
|
2286
|
-
const
|
|
2496
|
+
const pageData2 = ctx.page?.data ?? {};
|
|
2287
2497
|
const app = renderApp({
|
|
2288
2498
|
currentTemplate: template,
|
|
2289
2499
|
demo,
|
|
@@ -2301,11 +2511,11 @@ var ThemeMountBridge = forwardRef(function ThemeMountBridge2({ ctx, mountEl, ren
|
|
|
2301
2511
|
locale: ctx.locale,
|
|
2302
2512
|
translations: ctx.translations,
|
|
2303
2513
|
navigation: ctx.navigation,
|
|
2304
|
-
initialProducts:
|
|
2305
|
-
initialCollections:
|
|
2514
|
+
initialProducts: pageData2.products,
|
|
2515
|
+
initialCollections: pageData2.collections,
|
|
2306
2516
|
currentTemplate: template,
|
|
2307
2517
|
pageTemplate: ctx.page?.template,
|
|
2308
|
-
children: wrapEntityProviders(app,
|
|
2518
|
+
children: wrapEntityProviders(app, pageData2)
|
|
2309
2519
|
}
|
|
2310
2520
|
);
|
|
2311
2521
|
});
|
|
@@ -2348,7 +2558,7 @@ function Money({
|
|
|
2348
2558
|
className,
|
|
2349
2559
|
as = "span"
|
|
2350
2560
|
}) {
|
|
2351
|
-
const { formatMoney } = useLocalization();
|
|
2561
|
+
const { formatMoney: formatMoney2 } = useLocalization();
|
|
2352
2562
|
const shop = useShop();
|
|
2353
2563
|
const { selected, base, autoConvert, convert } = useCurrency();
|
|
2354
2564
|
const shouldConvert = autoConvert && !currency && !!selected && selected !== base;
|
|
@@ -2356,12 +2566,12 @@ function Money({
|
|
|
2356
2566
|
const toDisplay = (major) => shouldConvert ? convert(Math.round(major * 100), selected) / 100 : major;
|
|
2357
2567
|
const showCompare = compareAt != null && compareAt > amount;
|
|
2358
2568
|
const children = [
|
|
2359
|
-
/* @__PURE__ */ jsx("span", { children:
|
|
2569
|
+
/* @__PURE__ */ jsx("span", { children: formatMoney2(toDisplay(amount), ccy) }, "amt")
|
|
2360
2570
|
];
|
|
2361
2571
|
if (showCompare) {
|
|
2362
2572
|
children.push(" ");
|
|
2363
2573
|
children.push(
|
|
2364
|
-
/* @__PURE__ */ jsx("s", { style: { opacity: 0.6 }, children:
|
|
2574
|
+
/* @__PURE__ */ jsx("s", { style: { opacity: 0.6 }, children: formatMoney2(toDisplay(compareAt), ccy) }, "cmp")
|
|
2365
2575
|
);
|
|
2366
2576
|
}
|
|
2367
2577
|
return createElement(
|
|
@@ -2381,7 +2591,7 @@ function asImageTransform(v) {
|
|
|
2381
2591
|
return void 0;
|
|
2382
2592
|
}
|
|
2383
2593
|
function applyImageTransform(t, fit = "cover") {
|
|
2384
|
-
if (!t) return {
|
|
2594
|
+
if (!t) return {};
|
|
2385
2595
|
const fx = Math.round(_clampT(t.focal?.x ?? 0.5, 0, 1) * 1e4) / 100;
|
|
2386
2596
|
const fy = Math.round(_clampT(t.focal?.y ?? 0.5, 0, 1) * 1e4) / 100;
|
|
2387
2597
|
const zoom = _clampT(t.zoom ?? 1, 1, 4);
|
|
@@ -2584,7 +2794,10 @@ function HeroMedia({
|
|
|
2584
2794
|
const activeBase = useMobile ? MOBILE_BASE_WIDTH : DESKTOP_BASE_WIDTH;
|
|
2585
2795
|
const activeSrc = rawFallback ? activeUrl : focalSrc(activeUrl, { width: activeBase, ...activeCrop });
|
|
2586
2796
|
const activeSrcSet = rawFallback ? void 0 : heroSrcSet(activeUrl, activeCrop);
|
|
2587
|
-
const fitStyle =
|
|
2797
|
+
const fitStyle = {
|
|
2798
|
+
objectFit: fit,
|
|
2799
|
+
...applyImageTransform(useMobile ? mobileTransform ?? transform : transform, fit)
|
|
2800
|
+
};
|
|
2588
2801
|
return /* @__PURE__ */ jsx(
|
|
2589
2802
|
"img",
|
|
2590
2803
|
{
|
|
@@ -2925,6 +3138,20 @@ function Form({
|
|
|
2925
3138
|
}
|
|
2926
3139
|
);
|
|
2927
3140
|
}
|
|
3141
|
+
|
|
3142
|
+
// src/utils/routes.ts
|
|
3143
|
+
function productHref(slugOrId) {
|
|
3144
|
+
if (!slugOrId) return "/products";
|
|
3145
|
+
return `/products/${slugOrId}`;
|
|
3146
|
+
}
|
|
3147
|
+
function collectionHref(category) {
|
|
3148
|
+
if (!category) return "/collections";
|
|
3149
|
+
if (typeof category === "string") {
|
|
3150
|
+
return category ? `/collections/${category}` : "/collections";
|
|
3151
|
+
}
|
|
3152
|
+
const key = category.slug || category.id;
|
|
3153
|
+
return key ? `/collections/${key}` : "/collections";
|
|
3154
|
+
}
|
|
2928
3155
|
function joinClass(...names) {
|
|
2929
3156
|
return names.filter(Boolean).join(" ");
|
|
2930
3157
|
}
|
|
@@ -2935,7 +3162,7 @@ function ProductCard({
|
|
|
2935
3162
|
slots,
|
|
2936
3163
|
imageSizes
|
|
2937
3164
|
}) {
|
|
2938
|
-
const target = href ??
|
|
3165
|
+
const target = href ?? productHref(product.slug);
|
|
2939
3166
|
const firstImage = product.images?.[0];
|
|
2940
3167
|
const inStock = product.in_stock;
|
|
2941
3168
|
const badge = slots?.badge !== void 0 ? slots.badge : inStock ? null : /* @__PURE__ */ jsx("span", { className: "numu-product-card__badge", children: "Sold out" });
|
|
@@ -3548,7 +3775,19 @@ function resolveSourcePath(path, ctx) {
|
|
|
3548
3775
|
return null;
|
|
3549
3776
|
}
|
|
3550
3777
|
}
|
|
3778
|
+
function resolveMetafield(metafields, field) {
|
|
3779
|
+
const address = field.slice("metafield:".length);
|
|
3780
|
+
const dot = address.indexOf(".");
|
|
3781
|
+
if (dot === -1) return null;
|
|
3782
|
+
const namespace = address.slice(0, dot);
|
|
3783
|
+
const key = address.slice(dot + 1);
|
|
3784
|
+
const hit = (metafields ?? []).find(
|
|
3785
|
+
(m) => m.namespace === namespace && m.key === key
|
|
3786
|
+
);
|
|
3787
|
+
return hit ? hit.value ?? null : null;
|
|
3788
|
+
}
|
|
3551
3789
|
function resolveProductField(p, field) {
|
|
3790
|
+
if (field.startsWith("metafield:")) return resolveMetafield(p.metafields, field);
|
|
3552
3791
|
switch (field) {
|
|
3553
3792
|
case "title":
|
|
3554
3793
|
case "name":
|
|
@@ -3571,6 +3810,7 @@ function resolveProductField(p, field) {
|
|
|
3571
3810
|
}
|
|
3572
3811
|
}
|
|
3573
3812
|
function resolveCollectionField(c, field) {
|
|
3813
|
+
if (field.startsWith("metafield:")) return resolveMetafield(c.metafields, field);
|
|
3574
3814
|
switch (field) {
|
|
3575
3815
|
case "title":
|
|
3576
3816
|
case "name":
|
|
@@ -3753,6 +3993,50 @@ function assetUrl(name) {
|
|
|
3753
3993
|
return `${cleanBase}${filename}`;
|
|
3754
3994
|
}
|
|
3755
3995
|
|
|
3996
|
+
// src/utils/templates.ts
|
|
3997
|
+
function resolveSections(group) {
|
|
3998
|
+
if (!group) return [];
|
|
3999
|
+
if (Array.isArray(group.sections)) {
|
|
4000
|
+
return group.sections.map((instance, idx) => ({
|
|
4001
|
+
id: `${instance.type}-${idx}`,
|
|
4002
|
+
instance
|
|
4003
|
+
}));
|
|
4004
|
+
}
|
|
4005
|
+
const map = group.sections ?? {};
|
|
4006
|
+
const order = group.order ?? Object.keys(map);
|
|
4007
|
+
const out = [];
|
|
4008
|
+
for (const id of order) {
|
|
4009
|
+
const instance = map[id];
|
|
4010
|
+
if (instance) out.push({ id, instance });
|
|
4011
|
+
}
|
|
4012
|
+
return out;
|
|
4013
|
+
}
|
|
4014
|
+
function selectTemplateSections(hostTemplate, builtinTemplate, isKnown) {
|
|
4015
|
+
const hostSections = resolveSections(hostTemplate);
|
|
4016
|
+
if (hostSections.length === 0) {
|
|
4017
|
+
return resolveSections(builtinTemplate);
|
|
4018
|
+
}
|
|
4019
|
+
const anyKnown = hostSections.some(({ instance }) => isKnown(instance.type));
|
|
4020
|
+
if (!anyKnown) return resolveSections(builtinTemplate);
|
|
4021
|
+
return hostSections.filter(({ instance }) => isKnown(instance.type));
|
|
4022
|
+
}
|
|
4023
|
+
function selectChromeSections(options) {
|
|
4024
|
+
const { hostGroup, presetGroup, inline, templates, isChrome, isKnown } = options;
|
|
4025
|
+
const known = (list) => list.filter(({ instance }) => isKnown(instance.type));
|
|
4026
|
+
const fromHost = known(resolveSections(hostGroup));
|
|
4027
|
+
if (fromHost.length > 0) return fromHost;
|
|
4028
|
+
if (inline && inline.length > 0) return known(inline);
|
|
4029
|
+
const fromPreset = known(resolveSections(presetGroup));
|
|
4030
|
+
if (fromPreset.length > 0) return fromPreset;
|
|
4031
|
+
for (const template of templates ?? []) {
|
|
4032
|
+
const borrowed = known(resolveSections(template)).filter(
|
|
4033
|
+
({ instance }) => isChrome(instance.type)
|
|
4034
|
+
);
|
|
4035
|
+
if (borrowed.length > 0) return borrowed;
|
|
4036
|
+
}
|
|
4037
|
+
return [];
|
|
4038
|
+
}
|
|
4039
|
+
|
|
3756
4040
|
// src/utils/locales.ts
|
|
3757
4041
|
function flattenMessages(source, prefix = "") {
|
|
3758
4042
|
const out = {};
|
|
@@ -3797,6 +4081,6 @@ function buildLocaleBundle(modules) {
|
|
|
3797
4081
|
return bundle;
|
|
3798
4082
|
}
|
|
3799
4083
|
|
|
3800
|
-
export { AddToCartButton, Block, CollectionCard, CollectionProvider, CurrencySwitcher, EditableImage, EditableText, Form, HeroMedia, ICON_NAMES, Icon, IconMap, Image, LOGO_SHAPE_OPTIONS, LOGO_SIZE_OPTIONS, Link, LocaleSwitcher, Logo, MAX_BLOCK_DEPTH, Money, NAVIGATE_EVENT, NuMuProvider, ProductCard, ProductProvider, RichText, Section, SectionContext, applyGlobalStyleTokens, applyImageTransform, asImageTransform, assetUrl, availableValues, buildLocaleBundle, buildThemeElement, clearSdkSingleton, collectBlocks, collectSections, computeGlobalStyleTokens, defaultVariant, defineBlock, defineSection, defineThemeEntry, dynamicSource, findVariantByOptions, flattenMessages, focalSrc, getReactSingleton, getSdkSingleton, isDefinedBlock, isDefinedSection, isDynamicSource, isSdkAvailable, logoImgStyle, logoStyleTokens, mountTheme, pickTranslations, publishVariantSelection, readVariantSelection, registerReactSingleton, registerSdkSingleton, requestNavigate, resolveDynamicValue, resolveFontStack, resolveSettingsMap, resolveSizeChart, resolveSourcePath, sanitizeHtml, useAnalytics, useApp, useCachedResource, useCart, useCheckout, useCollection, useCollectionOptional, useCurrency, useCurrentTemplate, useCustomerActions, useCustomerAddresses, useGiftCardBalance, useImage, useMoney, useNavigation, useOrder, useOrders, useProduct, useProductOptional, useProductSizeChart, useRelatedProducts, useReorder, useResolvedSettings, useSearch, useSection, useSectionGroup, useSectionOptional, useShippingRates, useVariantSelection, useWishlist };
|
|
4084
|
+
export { AddToCartButton, Block, CollectionCard, CollectionProvider, CurrencySwitcher, EditableImage, EditableText, Form, HeroMedia, ICON_NAMES, Icon, IconMap, Image, LOGO_SHAPE_OPTIONS, LOGO_SIZE_OPTIONS, Link, LocaleSwitcher, Logo, MAX_BLOCK_DEPTH, Money, NAVIGATE_EVENT, NuMuProvider, ProductCard, ProductProvider, RichText, Section, SectionContext, applyGlobalStyleTokens, applyImageTransform, asImageTransform, assetUrl, availableValues, buildLocaleBundle, buildThemeElement, centsToMajor, clearSdkSingleton, collectBlocks, collectSections, collectionHref, computeGlobalStyleTokens, defaultVariant, defineBlock, defineSection, defineThemeEntry, dynamicSource, eligibleUnitsInCart, findVariantByOptions, flattenMessages, focalSrc, formatMoney, formatMoneyMajor, getReactSingleton, getSdkSingleton, isDefinedBlock, isDefinedSection, isDynamicSource, isSdkAvailable, logoImgStyle, logoStyleTokens, majorToCents, mountTheme, multibuyOffers, offerBeatsRegularPrice, offerIncludesProduct, offerProgress, pickTranslations, productHref, publishVariantSelection, readVariantSelection, registerReactSingleton, registerSdkSingleton, requestNavigate, resolveDynamicValue, resolveFontStack, resolveSections, resolveSettingsMap, resolveSizeChart, resolveSourcePath, sanitizeHtml, selectChromeSections, selectTemplateSections, useActivePromotions, useAnalytics, useApp, useArticle, useArticles, useBlog, useBlogs, useCachedResource, useCart, useCheckout, useCollection, useCollectionOptional, useCurrency, useCurrentTemplate, useCustomerActions, useCustomerAddresses, useGiftCardBalance, useImage, useListingHeading, useMetafield, useMetafields, useMoney, useNavigation, useOrder, useOrders, useProduct, useProductOptional, useProductSizeChart, useRelatedProducts, useReorder, useResolvedSettings, useSearch, useSection, useSectionGroup, useSectionOptional, useShippingRates, useVariantSelection, useWishlist };
|
|
3801
4085
|
//# sourceMappingURL=index.mjs.map
|
|
3802
4086
|
//# sourceMappingURL=index.mjs.map
|