@asteby/storefront-react 0.3.0 → 0.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asteby/storefront-react",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "React bindings for @asteby/storefront — SSR-safe provider, optimistic cart, Money, responsive Image, prices, variant selection, SEO meta and JSON-LD, section registry and server-rendered theme CSS.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -37,6 +37,26 @@
37
37
  "default": "./dist/mockups.cjs"
38
38
  }
39
39
  },
40
+ "./payments": {
41
+ "import": {
42
+ "types": "./dist/payments.d.ts",
43
+ "default": "./dist/payments.js"
44
+ },
45
+ "require": {
46
+ "types": "./dist/payments.d.cts",
47
+ "default": "./dist/payments.cjs"
48
+ }
49
+ },
50
+ "./promotions": {
51
+ "import": {
52
+ "types": "./dist/promotions.d.ts",
53
+ "default": "./dist/promotions.js"
54
+ },
55
+ "require": {
56
+ "types": "./dist/promotions.d.cts",
57
+ "default": "./dist/promotions.cjs"
58
+ }
59
+ },
40
60
  "./package.json": "./package.json"
41
61
  },
42
62
  "sideEffects": false,
@@ -47,12 +67,13 @@
47
67
  "react": "^19.0.0"
48
68
  },
49
69
  "dependencies": {
50
- "@asteby/storefront": "0.5.0"
70
+ "@asteby/storefront": "0.6.0"
51
71
  },
52
72
  "devDependencies": {
53
73
  "@testing-library/react": "^16.3.2",
54
74
  "@types/react": "^19.0.0",
55
75
  "@types/react-dom": "^19.0.0",
76
+ "axe-core": "4.11.4",
56
77
  "happy-dom": "^20.11.2",
57
78
  "react": "^19.2.4",
58
79
  "react-dom": "^19.2.4",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/context.tsx","../src/components.tsx"],"names":["jsx"],"mappings":"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","file":"chunk-C6QZNKRE.js","sourcesContent":["import { createContext, useContext, useMemo, type ReactNode } from 'react'\nimport { createStorefrontClient, type Shop, type StorefrontClient, type StorefrontClientOptions } from '@asteby/storefront'\n\nexport type StorefrontContextValue = {\n /** Client for browser-side calls (use a public `pk_` token). */\n client: StorefrontClient | null\n shop: Shop | null\n /** BCP 47 locale used for formatting (defaults to `shop.locale`, then `es-MX`). */\n locale: string\n /** ISO 4217 currency (defaults to `shop.currency_code`, then `MXN`). */\n currency: string\n}\n\nconst StorefrontContext = createContext<StorefrontContextValue | null>(null)\n\nexport type StorefrontProviderProps = {\n /** A ready client. Takes precedence over `config`. */\n client?: StorefrontClient\n /** Options to create the browser client (use a `pk_` token). */\n config?: StorefrontClientOptions\n /** Shop info loaded on the server (root loader). */\n shop?: Shop | null\n locale?: string\n currency?: string\n children?: ReactNode\n}\n\n/**\n * Makes the storefront client, shop, locale and currency available to the\n * tree. SSR-safe: creating the client does not touch `window`.\n */\nexport function StorefrontProvider({ client, config, shop = null, locale, currency, children }: StorefrontProviderProps) {\n const configKey = config ? `${config.baseUrl}|${config.token}|${config.locale ?? ''}|${config.customerToken ?? ''}` : ''\n const resolvedClient = useMemo(\n () => client ?? (config ? createStorefrontClient(config) : null),\n [client, configKey],\n )\n const value = useMemo<StorefrontContextValue>(\n () => ({\n client: resolvedClient,\n shop,\n locale: locale ?? config?.locale ?? shop?.locale ?? 'es-MX',\n currency: currency ?? shop?.currency_code ?? 'MXN',\n }),\n [resolvedClient, shop, locale, currency, config?.locale],\n )\n return <StorefrontContext.Provider value={value}>{children}</StorefrontContext.Provider>\n}\n\n/** Storefront context. Throws outside `<StorefrontProvider>`. */\nexport function useStorefront(): StorefrontContextValue {\n const ctx = useContext(StorefrontContext)\n if (!ctx) throw new Error('useStorefront must be used inside <StorefrontProvider>')\n return ctx\n}\n\n/** Storefront context or `null` outside a provider (for components that work standalone). */\nexport function useOptionalStorefront(): StorefrontContextValue | null {\n return useContext(StorefrontContext)\n}\n\n/** The client from context; throws when the provider has none. */\nexport function useStorefrontClient(): StorefrontClient {\n const { client } = useStorefront()\n if (!client) throw new Error('<StorefrontProvider> needs a `client` or `config` for browser-side calls')\n return client\n}\n","import { createElement, type ComponentPropsWithoutRef, type ElementType, type ReactNode } from 'react'\nimport { formatMoney, parseMoney, type FormatMoneyOptions, type Image as ImageData, type Money as MoneyData, type Product, type ProductVariant } from '@asteby/storefront'\nimport { useOptionalStorefront } from './context.js'\n\n// ---------------------------------------------------------------------------\n// Money\n// ---------------------------------------------------------------------------\n\nexport type MoneyProps<As extends ElementType = 'span'> = FormatMoneyOptions & {\n data: MoneyData\n /** Element to render (default `span`). */\n as?: As\n /** Overrides the provider locale. */\n locale?: string\n} & Omit<ComponentPropsWithoutRef<As>, 'children' | 'as'>\n\n/** Formats `Money` with the provider locale (or `locale`). */\nexport function Money<As extends ElementType = 'span'>({\n data,\n as,\n locale,\n withoutTrailingZeros,\n withoutCurrency,\n currencyDisplay,\n ...rest\n}: MoneyProps<As>) {\n const sf = useOptionalStorefront()\n const text = formatMoney(data, locale ?? sf?.locale, {\n ...(withoutTrailingZeros !== undefined ? { withoutTrailingZeros } : {}),\n ...(withoutCurrency !== undefined ? { withoutCurrency } : {}),\n ...(currencyDisplay !== undefined ? { currencyDisplay } : {}),\n })\n return createElement(as ?? 'span', rest, text)\n}\n\n// ---------------------------------------------------------------------------\n// Image\n// ---------------------------------------------------------------------------\n\nexport const DEFAULT_IMAGE_WIDTHS = [320, 480, 640, 768, 1024, 1280, 1600] as const\n\n/** Builds the URL for one srcset width. */\nexport type ImageLoader = (src: string, width: number, height?: number) => string\n\n/**\n * Default loader: adds `?width=<w>` (the convention of most image CDNs and of\n * the OPS image proxy). URLs that cannot be parsed are returned unchanged.\n */\nexport const defaultImageLoader: ImageLoader = (src, width, height) => {\n try {\n const isAbsolute = /^[a-z][a-z0-9+.-]*:/i.test(src)\n const url = new URL(src, 'http://relative.invalid')\n url.searchParams.set('width', String(width))\n if (height) url.searchParams.set('height', String(height))\n return isAbsolute ? url.toString() : url.pathname + url.search + url.hash\n } catch {\n return src\n }\n}\n\nexport type ImageProps = Omit<ComponentPropsWithoutRef<'img'>, 'src' | 'srcSet' | 'width' | 'height'> & {\n data: ImageData\n /** `sizes` attribute (default `100vw`). */\n sizes?: string\n /** Candidate widths for `srcset` (capped at the intrinsic width when known). */\n widths?: readonly number[]\n /** URL builder per width (default adds `?width=`). Pass `null` to disable srcset. */\n loader?: ImageLoader | null\n /** Above-the-fold image: eager loading and `fetchpriority=\"high\"`. */\n priority?: boolean\n /** Forces an aspect ratio, e.g. `1/1` or `4/3`, used for width/height and srcset heights. */\n aspectRatio?: string\n}\n\nfunction parseRatio(ratio: string | undefined): number | null {\n if (!ratio) return null\n const [w, h] = ratio.split(/[/:]/).map((n) => Number.parseFloat(n))\n return w && h ? w / h : null\n}\n\n/** Responsive `<img>` with `srcset`, lazy by default, intrinsic size to avoid layout shift. */\nexport function Image({\n data,\n sizes = '100vw',\n widths = DEFAULT_IMAGE_WIDTHS,\n loader = defaultImageLoader,\n priority = false,\n aspectRatio,\n alt,\n loading,\n decoding,\n ...rest\n}: ImageProps) {\n const ratio = parseRatio(aspectRatio) ?? (data.width && data.height ? data.width / data.height : null)\n let candidates = [...widths].sort((a, b) => a - b)\n if (data.width) {\n const capped = candidates.filter((w) => w <= data.width!)\n candidates = capped.length ? capped : [data.width]\n }\n const srcSet = loader\n ? candidates.map((w) => `${loader(data.url, w, ratio ? Math.round(w / ratio) : undefined)} ${w}w`).join(', ')\n : undefined\n const width = data.width ?? candidates[candidates.length - 1]\n const height = data.height ?? (ratio && width ? Math.round(width / ratio) : undefined)\n return (\n <img\n src={data.url}\n alt={alt ?? data.alt ?? ''}\n {...(srcSet ? { srcSet, sizes } : {})}\n {...(width ? { width } : {})}\n {...(height ? { height } : {})}\n loading={loading ?? (priority ? 'eager' : 'lazy')}\n decoding={decoding ?? (priority ? 'auto' : 'async')}\n {...(priority ? { fetchPriority: 'high' as const } : {})}\n {...rest}\n />\n )\n}\n\n// ---------------------------------------------------------------------------\n// ProductPrice\n// ---------------------------------------------------------------------------\n\nconst VISUALLY_HIDDEN = {\n position: 'absolute',\n width: 1,\n height: 1,\n padding: 0,\n margin: -1,\n overflow: 'hidden',\n clip: 'rect(0, 0, 0, 0)',\n whiteSpace: 'nowrap',\n border: 0,\n} as const\n\nexport type ProductPriceProps = Omit<ComponentPropsWithoutRef<'div'>, 'children'> & {\n product?: Pick<Product, 'price_range' | 'compare_at_price_range'> | null\n variant?: Pick<ProductVariant, 'price' | 'compare_at_price'> | null\n /** Prefix shown when the product has a price range and no variant is selected (default `Desde`). */\n fromLabel?: ReactNode\n /** Accessible label for the compare-at price (default `Precio anterior`). */\n compareAtLabel?: string\n locale?: string\n withoutTrailingZeros?: boolean\n}\n\n/**\n * Price with optional compare-at (strikethrough). With a `variant` it shows\n * that variant; otherwise the product's minimum price (prefixed with\n * `fromLabel` when prices vary).\n */\nexport function ProductPrice({\n product,\n variant,\n fromLabel = 'Desde',\n compareAtLabel = 'Precio anterior',\n locale,\n withoutTrailingZeros,\n ...rest\n}: ProductPriceProps) {\n let price: MoneyData | null = null\n let compareAt: MoneyData | null = null\n let isRange = false\n if (variant) {\n price = variant.price\n compareAt = variant.compare_at_price\n } else if (product) {\n price = product.price_range.min\n compareAt = product.compare_at_price_range?.min ?? null\n isRange = product.price_range.min.amount !== product.price_range.max.amount\n }\n if (!price) return null\n const onSale = compareAt !== null && parseMoney(compareAt) > parseMoney(price)\n const opts = { ...(locale ? { locale } : {}), ...(withoutTrailingZeros !== undefined ? { withoutTrailingZeros } : {}) }\n return (\n <div data-on-sale={onSale || undefined} {...rest}>\n {isRange ? <span data-price-prefix=\"\">{fromLabel} </span> : null}\n <Money data={price} data-price=\"\" {...opts} />\n {onSale && compareAt ? (\n <>\n {' '}\n <s data-compare-at-price=\"\">\n <span style={VISUALLY_HIDDEN}>{compareAtLabel}: </span>\n <Money data={compareAt} {...opts} />\n </s>\n </>\n ) : null}\n </div>\n )\n}\n"]}