@numueg/theme-sdk 0.2.3 → 0.3.2

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
  import { createContext, forwardRef, useState, useImperativeHandle, useEffect, useCallback, useMemo, useRef, useContext, StrictMode, createElement, Component } from 'react';
2
- import { createRoot } from 'react-dom/client';
2
+ import { hydrateRoot, createRoot } from 'react-dom/client';
3
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
4
 
5
5
  // src/types/theme.ts
@@ -526,9 +526,8 @@ function toNavigationItem(raw, locale) {
526
526
  url: raw.url || "/",
527
527
  resource_type: mapResourceType(raw.type),
528
528
  resource_handle: raw.resource_id ?? null,
529
- children: (raw.children ?? []).map(
530
- (child) => toNavigationItem(child, locale)
531
- )
529
+ target_visible: raw.target_visible !== false,
530
+ children: (raw.children ?? []).map((child) => toNavigationItem(child, locale)).filter((child) => child.target_visible)
532
531
  };
533
532
  }
534
533
  function useNavigation(handle, options) {
@@ -538,7 +537,8 @@ function useNavigation(handle, options) {
538
537
  const hostProvidedAny = !!navMap && Object.keys(navMap).length > 0;
539
538
  const rawItems = handle ? navMap?.[handle] : void 0;
540
539
  const hostItems = useMemo(() => {
541
- if (rawItems) return rawItems.map((it) => toNavigationItem(it, locale));
540
+ if (rawItems)
541
+ return rawItems.map((it) => toNavigationItem(it, locale)).filter((it) => it.target_visible);
542
542
  if (hostProvidedAny) return [];
543
543
  return null;
544
544
  }, [rawItems, hostProvidedAny, locale]);
@@ -940,6 +940,29 @@ function useRelatedProducts(productId, options = {}) {
940
940
  }, [productId, limit]);
941
941
  return { items, loading, error };
942
942
  }
943
+ function useProductSizeChart(productOverride) {
944
+ const ctxProduct = useProductOptional();
945
+ const product = productOverride ?? ctxProduct;
946
+ const shop = useShop();
947
+ const storeSettings = shop?.settings;
948
+ return useMemo(
949
+ () => resolveSizeChart(product?.attributes, storeSettings),
950
+ [product?.attributes, storeSettings]
951
+ );
952
+ }
953
+ function hasRows(c) {
954
+ return !!c && typeof c === "object" && Array.isArray(c.rows) && c.rows.length > 0;
955
+ }
956
+ function resolveSizeChart(productAttributes, storeSettings) {
957
+ const product = productAttributes?.size_chart;
958
+ const storeDefault = storeSettings?.size_chart;
959
+ if (product?.mode === "off") return null;
960
+ if (product?.mode === "custom") return hasRows(product) ? product : null;
961
+ if (product?.mode === "default") return hasRows(storeDefault) ? storeDefault : null;
962
+ if (hasRows(product)) return product;
963
+ if (hasRows(storeDefault)) return storeDefault;
964
+ return null;
965
+ }
943
966
  var COOKIE_NAME = "numu_currency";
944
967
  var COOKIE_MAX_AGE = 60 * 60 * 24 * 30;
945
968
  function readCookie(name) {
@@ -1452,7 +1475,15 @@ function normalizeCartFromServer(cart) {
1452
1475
  subtotal: toMajor(cart.subtotal),
1453
1476
  total: toMajor(cart.total),
1454
1477
  ...cart.discount_amount != null ? { discount_amount: toMajor(cart.discount_amount) } : {},
1455
- items: Array.isArray(cart.items) ? cart.items.map((it) => ({ ...it, price: toMajor(it.price) })) : []
1478
+ items: Array.isArray(cart.items) ? cart.items.map((it) => {
1479
+ const raw = it;
1480
+ return {
1481
+ ...it,
1482
+ name: raw.name || raw.product_name || "",
1483
+ price: toMajor(raw.price ?? raw.unit_price),
1484
+ variant_name: raw.variant_name ?? void 0
1485
+ };
1486
+ }) : []
1456
1487
  };
1457
1488
  }
1458
1489
  function unwrapCart(json) {
@@ -1989,6 +2020,9 @@ function injectFontLink(href) {
1989
2020
  link.setAttribute("data-numu-font", "");
1990
2021
  document.head.appendChild(link);
1991
2022
  }
2023
+ function lookupFontStack(value) {
2024
+ return FONT_REGISTRY[value]?.stack ?? value;
2025
+ }
1992
2026
  function resolveFontStack(value) {
1993
2027
  const entry = FONT_REGISTRY[value];
1994
2028
  if (entry) {
@@ -1997,39 +2031,69 @@ function resolveFontStack(value) {
1997
2031
  }
1998
2032
  return value;
1999
2033
  }
2000
- function applyGlobalStyleTokens(globalSettings, el) {
2001
- if (!el || !globalSettings || typeof globalSettings !== "object") return;
2002
- const style = el.style;
2034
+ function computeGlobalStyleTokens(globalSettings) {
2035
+ const cssVars = {};
2036
+ const fontHrefs = [];
2037
+ if (!globalSettings || typeof globalSettings !== "object") {
2038
+ return { cssVars, fontHrefs };
2039
+ }
2040
+ const pushHref = (href) => {
2041
+ if (href && !fontHrefs.includes(href)) fontHrefs.push(href);
2042
+ };
2003
2043
  for (const [key, value] of Object.entries(globalSettings)) {
2004
2044
  if (!key || key.startsWith("__")) continue;
2005
2045
  if (value && typeof value === "object" && !Array.isArray(value)) {
2006
2046
  for (const [role, c] of Object.entries(value)) {
2007
- if (isColorValue(c)) style.setProperty(`--scheme-${key}-${role}`, c);
2047
+ if (isColorValue(c)) cssVars[`--scheme-${key}-${role}`] = c;
2008
2048
  }
2009
2049
  continue;
2010
2050
  }
2011
2051
  if (isColorValue(value)) {
2012
- style.setProperty(`--theme-${key}`, value.trim());
2052
+ cssVars[`--theme-${key}`] = value.trim();
2013
2053
  const role = COLOR_ROLE_ALIASES[key];
2014
- if (role) style.setProperty(`--theme-color-${role}`, value.trim());
2054
+ if (role) cssVars[`--theme-color-${role}`] = value.trim();
2015
2055
  continue;
2016
2056
  }
2017
2057
  if (isFontToken(value)) {
2018
- const stack = resolveFontStack(value);
2019
- style.setProperty(`--theme-${key}`, stack);
2058
+ const entry = FONT_REGISTRY[value];
2059
+ cssVars[`--theme-${key}`] = entry.stack;
2020
2060
  const role = FONT_ROLE_ALIASES[key];
2021
- if (role) style.setProperty(`--theme-font-${role}`, stack);
2061
+ if (role) cssVars[`--theme-font-${role}`] = entry.stack;
2062
+ pushHref(entry.href);
2022
2063
  continue;
2023
2064
  }
2024
2065
  if (typeof value === "string" || typeof value === "number") {
2025
2066
  const v = String(value).trim();
2026
- if (v) style.setProperty(`--theme-${key}`, v);
2067
+ if (v) cssVars[`--theme-${key}`] = v;
2068
+ }
2069
+ }
2070
+ for (const id of ["heading_font", "body_font"]) {
2071
+ const value = globalSettings[id];
2072
+ if (typeof value === "string" && value.trim()) {
2073
+ cssVars[`--theme-${id}`] = lookupFontStack(value);
2074
+ pushHref(FONT_REGISTRY[value]?.href);
2027
2075
  }
2028
2076
  }
2077
+ return { cssVars, fontHrefs };
2078
+ }
2079
+ function applyGlobalStyleTokens(globalSettings, el) {
2080
+ if (!el || !globalSettings || typeof globalSettings !== "object") return;
2081
+ const { cssVars, fontHrefs } = computeGlobalStyleTokens(globalSettings);
2082
+ const style = el.style;
2083
+ for (const [prop, value] of Object.entries(cssVars)) {
2084
+ style.setProperty(prop, value);
2085
+ }
2086
+ for (const href of fontHrefs) injectFontLink(href);
2029
2087
  }
2030
2088
  function pickStore(ctx) {
2031
2089
  const s = ctx.storeData ?? ctx.store;
2032
- if (s) return s;
2090
+ if (s) {
2091
+ const raw = s;
2092
+ if (!raw.currency && raw.default_currency) {
2093
+ return { ...raw, currency: raw.default_currency };
2094
+ }
2095
+ return s;
2096
+ }
2033
2097
  return {
2034
2098
  id: "unknown",
2035
2099
  name: "Store",
@@ -2064,19 +2128,9 @@ var ThemeMountBridge = forwardRef(function ThemeMountBridge2({ ctx, mountEl, ren
2064
2128
  []
2065
2129
  );
2066
2130
  useEffect(() => {
2131
+ if (!mountEl) return;
2067
2132
  const gs = themeSettings.global_settings ?? {};
2068
2133
  applyGlobalStyleTokens(gs, mountEl);
2069
- const headingFont = gs.heading_font;
2070
- if (typeof headingFont === "string" && headingFont.trim()) {
2071
- mountEl.style.setProperty(
2072
- "--theme-heading_font",
2073
- resolveFontStack(headingFont)
2074
- );
2075
- }
2076
- const bodyFont = gs.body_font;
2077
- if (typeof bodyFont === "string" && bodyFont.trim()) {
2078
- mountEl.style.setProperty("--theme-body_font", resolveFontStack(bodyFont));
2079
- }
2080
2134
  }, [themeSettings, mountEl]);
2081
2135
  const store = pickStore(ctx);
2082
2136
  const template = pickTemplate(ctx);
@@ -2106,22 +2160,22 @@ var ThemeMountBridge = forwardRef(function ThemeMountBridge2({ ctx, mountEl, ren
2106
2160
  }
2107
2161
  );
2108
2162
  });
2163
+ function buildThemeElement(ctx, mountEl, renderApp, ref) {
2164
+ return /* @__PURE__ */ jsx(StrictMode, { children: /* @__PURE__ */ jsx(ThemeMountBridge, { ctx, mountEl, renderApp, ref }) });
2165
+ }
2109
2166
  function mountTheme(el, ctx, renderApp) {
2110
- const root = createRoot(el);
2111
2167
  const handleRef = { current: null };
2112
- root.render(
2113
- /* @__PURE__ */ jsx(StrictMode, { children: /* @__PURE__ */ jsx(
2114
- ThemeMountBridge,
2115
- {
2116
- ctx,
2117
- mountEl: el,
2118
- renderApp,
2119
- ref: (h) => {
2120
- handleRef.current = h;
2121
- }
2122
- }
2123
- ) })
2124
- );
2168
+ const element = buildThemeElement(ctx, el, renderApp, (h) => {
2169
+ handleRef.current = h;
2170
+ });
2171
+ const shouldHydrate = ctx.hydrate === true && el.firstElementChild !== null;
2172
+ let root;
2173
+ if (shouldHydrate) {
2174
+ root = hydrateRoot(el, element);
2175
+ } else {
2176
+ root = createRoot(el);
2177
+ root.render(element);
2178
+ }
2125
2179
  return {
2126
2180
  applyDraft: (next) => handleRef.current?.applyDraft(next),
2127
2181
  cleanup: () => {
@@ -2130,6 +2184,14 @@ function mountTheme(el, ctx, renderApp) {
2130
2184
  }
2131
2185
  };
2132
2186
  }
2187
+
2188
+ // src/entry.tsx
2189
+ function defineThemeEntry(renderApp) {
2190
+ return {
2191
+ mount: (el, ctx) => mountTheme(el, ctx, renderApp),
2192
+ createApp: (ctx) => buildThemeElement(ctx, null, renderApp)
2193
+ };
2194
+ }
2133
2195
  function CollectionProvider({ collection, children }) {
2134
2196
  return /* @__PURE__ */ jsx(CollectionContext.Provider, { value: collection, children });
2135
2197
  }
@@ -2753,7 +2815,14 @@ function sanitizeHtmlServer(input) {
2753
2815
  return s;
2754
2816
  }
2755
2817
  function RichText({ html, className, as = "div" }) {
2756
- const safe = useMemo(() => sanitizeHtml(html || ""), [html]);
2818
+ const [domReady, setDomReady] = useState(false);
2819
+ useEffect(() => {
2820
+ setDomReady(true);
2821
+ }, []);
2822
+ const safe = useMemo(
2823
+ () => domReady ? sanitizeHtml(html || "") : sanitizeHtmlServer(html || ""),
2824
+ [html, domReady]
2825
+ );
2757
2826
  if (!safe) return null;
2758
2827
  const Tag = as;
2759
2828
  return /* @__PURE__ */ jsx(
@@ -3419,8 +3488,13 @@ function collectBlocks(modules) {
3419
3488
 
3420
3489
  // src/utils/assetUrl.ts
3421
3490
  function getRuntime() {
3422
- if (typeof window === "undefined") return {};
3423
- return window;
3491
+ if (typeof window !== "undefined") {
3492
+ return window;
3493
+ }
3494
+ if (typeof globalThis !== "undefined") {
3495
+ return globalThis;
3496
+ }
3497
+ return {};
3424
3498
  }
3425
3499
  function assetUrl(name) {
3426
3500
  if (!name) return "";
@@ -3481,6 +3555,6 @@ function buildLocaleBundle(modules) {
3481
3555
  return bundle;
3482
3556
  }
3483
3557
 
3484
- export { AddToCartButton, Block, CartContext, CollectionCard, CollectionContext, CollectionProvider, CurrencySwitcher, CustomerContext, EditableImage, EditableText, Form, ICON_NAMES, Icon, IconMap, Image, Link, LocaleSwitcher, LocalizationContext, MAX_BLOCK_DEPTH, Money, NavigationContext, NuMuProvider, PageContext, ProductCard, ProductContext, ProductProvider, RichText, Section, SectionContext, ShopContext, ThemeSettingsContext, applyGlobalStyleTokens, applyImageTransform, asImageTransform, assetUrl, availableValues, buildLocaleBundle, clearSdkSingleton, collectBlocks, collectSections, defaultVariant, defineBlock, defineSection, dynamicSource, findVariantByOptions, flattenMessages, focalSrc, getReactSingleton, getSdkSingleton, isDefinedBlock, isDefinedSection, isDynamicSource, isSdkAvailable, mountTheme, pickTranslations, registerReactSingleton, registerSdkSingleton, resolveDynamicValue, resolveFontStack, resolveSettingsMap, resolveSourcePath, resolveThemeSettings, sanitizeHtml, useAnalytics, useApp, useCart, useCheckout, useCollection, useCollectionOptional, useCollections, useCurrency, useCurrentTemplate, useCustomer, useCustomerActions, useCustomerAddresses, useDirection, useFieldTranslation, useGiftCardBalance, useImage, useLocale, useLocalization, useMoney, useNavigation, useNumberFormat, useOrder, useOrders, usePage, useProduct, useProductOptional, useProducts, useRelatedProducts, useReorder, useResolvedSettings, useSearch, useSection, useSectionOptional, useShippingRates, useShop, useThemeSettings, useTranslation, useVariantSelection, useWishlist };
3558
+ export { AddToCartButton, Block, CartContext, CollectionCard, CollectionContext, CollectionProvider, CurrencySwitcher, CustomerContext, EditableImage, EditableText, Form, ICON_NAMES, Icon, IconMap, Image, Link, LocaleSwitcher, LocalizationContext, MAX_BLOCK_DEPTH, Money, NavigationContext, NuMuProvider, PageContext, ProductCard, ProductContext, ProductProvider, RichText, Section, SectionContext, ShopContext, ThemeSettingsContext, 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, mountTheme, pickTranslations, registerReactSingleton, registerSdkSingleton, resolveDynamicValue, resolveFontStack, resolveSettingsMap, resolveSizeChart, resolveSourcePath, resolveThemeSettings, sanitizeHtml, useAnalytics, useApp, useCart, useCheckout, useCollection, useCollectionOptional, useCollections, useCurrency, useCurrentTemplate, useCustomer, useCustomerActions, useCustomerAddresses, useDirection, useFieldTranslation, useGiftCardBalance, useImage, useLocale, useLocalization, useMoney, useNavigation, useNumberFormat, useOrder, useOrders, usePage, useProduct, useProductOptional, useProductSizeChart, useProducts, useRelatedProducts, useReorder, useResolvedSettings, useSearch, useSection, useSectionOptional, useShippingRates, useShop, useThemeSettings, useTranslation, useVariantSelection, useWishlist };
3485
3559
  //# sourceMappingURL=index.mjs.map
3486
3560
  //# sourceMappingURL=index.mjs.map