@numueg/theme-sdk 0.10.1 → 0.12.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/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-QFFTHIFZ.mjs';
2
+ export { KNOWN_SETTING_TYPES, KNOWN_TEMPLATES, REQUIRED_TEMPLATES, SDK_VERSION, THEME_CONTRACT_VERSION, mergeResults, validateBuiltManifest, validateManifest, validateSectionSchema, validateSettingsAgainstSchema } from './chunk-ZECKSIDL.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 formatMoney(converted, selected);
158
+ return formatMoney2(converted, selected);
92
159
  }
93
- return formatMoney(amount, currencyOverride || shop?.currency);
160
+ return formatMoney2(amount, currencyOverride || shop?.currency);
94
161
  };
95
162
  }
96
163
  var DEFAULT_WIDTHS = [320, 480, 640, 768, 1024, 1280, 1600, 1920];
@@ -2254,13 +2321,13 @@ function pickDemo(ctx, themeSettings) {
2254
2321
  const t = themeSettings.templates;
2255
2322
  return !t || Object.keys(t).length === 0;
2256
2323
  }
2257
- function wrapEntityProviders(app, pageData) {
2324
+ function wrapEntityProviders(app, pageData2) {
2258
2325
  let inner = app;
2259
- if (pageData.collection) {
2260
- inner = /* @__PURE__ */ jsx(CollectionProvider, { collection: pageData.collection, children: inner });
2326
+ if (pageData2.collection) {
2327
+ inner = /* @__PURE__ */ jsx(CollectionProvider, { collection: pageData2.collection, children: inner });
2261
2328
  }
2262
- if (pageData.product) {
2263
- inner = /* @__PURE__ */ jsx(ProductProvider, { product: pageData.product, children: inner });
2329
+ if (pageData2.product) {
2330
+ inner = /* @__PURE__ */ jsx(ProductProvider, { product: pageData2.product, children: inner });
2264
2331
  }
2265
2332
  return inner;
2266
2333
  }
@@ -2283,7 +2350,7 @@ var ThemeMountBridge = forwardRef(function ThemeMountBridge2({ ctx, mountEl, ren
2283
2350
  const store = pickStore(ctx);
2284
2351
  const template = pickTemplate(ctx);
2285
2352
  const demo = pickDemo(ctx, themeSettings);
2286
- const pageData = ctx.page?.data ?? {};
2353
+ const pageData2 = ctx.page?.data ?? {};
2287
2354
  const app = renderApp({
2288
2355
  currentTemplate: template,
2289
2356
  demo,
@@ -2301,11 +2368,11 @@ var ThemeMountBridge = forwardRef(function ThemeMountBridge2({ ctx, mountEl, ren
2301
2368
  locale: ctx.locale,
2302
2369
  translations: ctx.translations,
2303
2370
  navigation: ctx.navigation,
2304
- initialProducts: pageData.products,
2305
- initialCollections: pageData.collections,
2371
+ initialProducts: pageData2.products,
2372
+ initialCollections: pageData2.collections,
2306
2373
  currentTemplate: template,
2307
2374
  pageTemplate: ctx.page?.template,
2308
- children: wrapEntityProviders(app, pageData)
2375
+ children: wrapEntityProviders(app, pageData2)
2309
2376
  }
2310
2377
  );
2311
2378
  });
@@ -2348,7 +2415,7 @@ function Money({
2348
2415
  className,
2349
2416
  as = "span"
2350
2417
  }) {
2351
- const { formatMoney } = useLocalization();
2418
+ const { formatMoney: formatMoney2 } = useLocalization();
2352
2419
  const shop = useShop();
2353
2420
  const { selected, base, autoConvert, convert } = useCurrency();
2354
2421
  const shouldConvert = autoConvert && !currency && !!selected && selected !== base;
@@ -2356,12 +2423,12 @@ function Money({
2356
2423
  const toDisplay = (major) => shouldConvert ? convert(Math.round(major * 100), selected) / 100 : major;
2357
2424
  const showCompare = compareAt != null && compareAt > amount;
2358
2425
  const children = [
2359
- /* @__PURE__ */ jsx("span", { children: formatMoney(toDisplay(amount), ccy) }, "amt")
2426
+ /* @__PURE__ */ jsx("span", { children: formatMoney2(toDisplay(amount), ccy) }, "amt")
2360
2427
  ];
2361
2428
  if (showCompare) {
2362
2429
  children.push(" ");
2363
2430
  children.push(
2364
- /* @__PURE__ */ jsx("s", { style: { opacity: 0.6 }, children: formatMoney(toDisplay(compareAt), ccy) }, "cmp")
2431
+ /* @__PURE__ */ jsx("s", { style: { opacity: 0.6 }, children: formatMoney2(toDisplay(compareAt), ccy) }, "cmp")
2365
2432
  );
2366
2433
  }
2367
2434
  return createElement(
@@ -2381,7 +2448,7 @@ function asImageTransform(v) {
2381
2448
  return void 0;
2382
2449
  }
2383
2450
  function applyImageTransform(t, fit = "cover") {
2384
- if (!t) return { objectFit: fit };
2451
+ if (!t) return {};
2385
2452
  const fx = Math.round(_clampT(t.focal?.x ?? 0.5, 0, 1) * 1e4) / 100;
2386
2453
  const fy = Math.round(_clampT(t.focal?.y ?? 0.5, 0, 1) * 1e4) / 100;
2387
2454
  const zoom = _clampT(t.zoom ?? 1, 1, 4);
@@ -2584,7 +2651,10 @@ function HeroMedia({
2584
2651
  const activeBase = useMobile ? MOBILE_BASE_WIDTH : DESKTOP_BASE_WIDTH;
2585
2652
  const activeSrc = rawFallback ? activeUrl : focalSrc(activeUrl, { width: activeBase, ...activeCrop });
2586
2653
  const activeSrcSet = rawFallback ? void 0 : heroSrcSet(activeUrl, activeCrop);
2587
- const fitStyle = applyImageTransform(useMobile ? mobileTransform ?? transform : transform, fit);
2654
+ const fitStyle = {
2655
+ objectFit: fit,
2656
+ ...applyImageTransform(useMobile ? mobileTransform ?? transform : transform, fit)
2657
+ };
2588
2658
  return /* @__PURE__ */ jsx(
2589
2659
  "img",
2590
2660
  {
@@ -2925,6 +2995,20 @@ function Form({
2925
2995
  }
2926
2996
  );
2927
2997
  }
2998
+
2999
+ // src/utils/routes.ts
3000
+ function productHref(slugOrId) {
3001
+ if (!slugOrId) return "/products";
3002
+ return `/products/${slugOrId}`;
3003
+ }
3004
+ function collectionHref(category) {
3005
+ if (!category) return "/collections";
3006
+ if (typeof category === "string") {
3007
+ return category ? `/collections/${category}` : "/collections";
3008
+ }
3009
+ const key = category.slug || category.id;
3010
+ return key ? `/collections/${key}` : "/collections";
3011
+ }
2928
3012
  function joinClass(...names) {
2929
3013
  return names.filter(Boolean).join(" ");
2930
3014
  }
@@ -2935,7 +3019,7 @@ function ProductCard({
2935
3019
  slots,
2936
3020
  imageSizes
2937
3021
  }) {
2938
- const target = href ?? `/products/${product.slug}`;
3022
+ const target = href ?? productHref(product.slug);
2939
3023
  const firstImage = product.images?.[0];
2940
3024
  const inStock = product.in_stock;
2941
3025
  const badge = slots?.badge !== void 0 ? slots.badge : inStock ? null : /* @__PURE__ */ jsx("span", { className: "numu-product-card__badge", children: "Sold out" });
@@ -3548,7 +3632,19 @@ function resolveSourcePath(path, ctx) {
3548
3632
  return null;
3549
3633
  }
3550
3634
  }
3635
+ function resolveMetafield(metafields, field) {
3636
+ const address = field.slice("metafield:".length);
3637
+ const dot = address.indexOf(".");
3638
+ if (dot === -1) return null;
3639
+ const namespace = address.slice(0, dot);
3640
+ const key = address.slice(dot + 1);
3641
+ const hit = (metafields ?? []).find(
3642
+ (m) => m.namespace === namespace && m.key === key
3643
+ );
3644
+ return hit ? hit.value ?? null : null;
3645
+ }
3551
3646
  function resolveProductField(p, field) {
3647
+ if (field.startsWith("metafield:")) return resolveMetafield(p.metafields, field);
3552
3648
  switch (field) {
3553
3649
  case "title":
3554
3650
  case "name":
@@ -3571,6 +3667,7 @@ function resolveProductField(p, field) {
3571
3667
  }
3572
3668
  }
3573
3669
  function resolveCollectionField(c, field) {
3670
+ if (field.startsWith("metafield:")) return resolveMetafield(c.metafields, field);
3574
3671
  switch (field) {
3575
3672
  case "title":
3576
3673
  case "name":
@@ -3753,6 +3850,76 @@ function assetUrl(name) {
3753
3850
  return `${cleanBase}${filename}`;
3754
3851
  }
3755
3852
 
3853
+ // src/utils/money.ts
3854
+ function resolveLocale(locale) {
3855
+ return locale === "ar" ? "ar-EG" : "en-EG";
3856
+ }
3857
+ function formatMoney(cents, options = {}) {
3858
+ const { currency, locale, fractionDigits = 0 } = options;
3859
+ const safe = Number.isFinite(cents) ? cents : 0;
3860
+ return new Intl.NumberFormat(resolveLocale(locale), {
3861
+ style: "currency",
3862
+ currency: currency || "EGP",
3863
+ maximumFractionDigits: fractionDigits
3864
+ }).format(safe / 100);
3865
+ }
3866
+ function formatMoneyMajor(amount, options = {}) {
3867
+ return formatMoney(
3868
+ (Number.isFinite(amount) ? amount : 0) * 100,
3869
+ options
3870
+ );
3871
+ }
3872
+ function centsToMajor(cents) {
3873
+ return (Number.isFinite(cents) ? cents : 0) / 100;
3874
+ }
3875
+ function majorToCents(amount) {
3876
+ return Math.round((Number.isFinite(amount) ? amount : 0) * 100);
3877
+ }
3878
+
3879
+ // src/utils/templates.ts
3880
+ function resolveSections(group) {
3881
+ if (!group) return [];
3882
+ if (Array.isArray(group.sections)) {
3883
+ return group.sections.map((instance, idx) => ({
3884
+ id: `${instance.type}-${idx}`,
3885
+ instance
3886
+ }));
3887
+ }
3888
+ const map = group.sections ?? {};
3889
+ const order = group.order ?? Object.keys(map);
3890
+ const out = [];
3891
+ for (const id of order) {
3892
+ const instance = map[id];
3893
+ if (instance) out.push({ id, instance });
3894
+ }
3895
+ return out;
3896
+ }
3897
+ function selectTemplateSections(hostTemplate, builtinTemplate, isKnown) {
3898
+ const hostSections = resolveSections(hostTemplate);
3899
+ if (hostSections.length === 0) {
3900
+ return resolveSections(builtinTemplate);
3901
+ }
3902
+ const anyKnown = hostSections.some(({ instance }) => isKnown(instance.type));
3903
+ if (!anyKnown) return resolveSections(builtinTemplate);
3904
+ return hostSections.filter(({ instance }) => isKnown(instance.type));
3905
+ }
3906
+ function selectChromeSections(options) {
3907
+ const { hostGroup, presetGroup, inline, templates, isChrome, isKnown } = options;
3908
+ const known = (list) => list.filter(({ instance }) => isKnown(instance.type));
3909
+ const fromHost = known(resolveSections(hostGroup));
3910
+ if (fromHost.length > 0) return fromHost;
3911
+ if (inline && inline.length > 0) return known(inline);
3912
+ const fromPreset = known(resolveSections(presetGroup));
3913
+ if (fromPreset.length > 0) return fromPreset;
3914
+ for (const template of templates ?? []) {
3915
+ const borrowed = known(resolveSections(template)).filter(
3916
+ ({ instance }) => isChrome(instance.type)
3917
+ );
3918
+ if (borrowed.length > 0) return borrowed;
3919
+ }
3920
+ return [];
3921
+ }
3922
+
3756
3923
  // src/utils/locales.ts
3757
3924
  function flattenMessages(source, prefix = "") {
3758
3925
  const out = {};
@@ -3797,6 +3964,6 @@ function buildLocaleBundle(modules) {
3797
3964
  return bundle;
3798
3965
  }
3799
3966
 
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 };
3967
+ 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, findVariantByOptions, flattenMessages, focalSrc, formatMoney, formatMoneyMajor, getReactSingleton, getSdkSingleton, isDefinedBlock, isDefinedSection, isDynamicSource, isSdkAvailable, logoImgStyle, logoStyleTokens, majorToCents, mountTheme, pickTranslations, productHref, publishVariantSelection, readVariantSelection, registerReactSingleton, registerSdkSingleton, requestNavigate, resolveDynamicValue, resolveFontStack, resolveSections, resolveSettingsMap, resolveSizeChart, resolveSourcePath, sanitizeHtml, selectChromeSections, selectTemplateSections, 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
3968
  //# sourceMappingURL=index.mjs.map
3802
3969
  //# sourceMappingURL=index.mjs.map