@asteby/storefront-react 0.9.0 → 0.10.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/README.md +22 -0
- package/dist/cart.js +2 -1
- package/dist/cart.js.map +1 -1
- package/dist/checkout.cjs +440 -0
- package/dist/checkout.cjs.map +1 -0
- package/dist/checkout.css +100 -0
- package/dist/checkout.d.cts +197 -0
- package/dist/checkout.d.ts +197 -0
- package/dist/checkout.js +243 -0
- package/dist/checkout.js.map +1 -0
- package/dist/chunk-DSZVJ2PW.js +180 -0
- package/dist/chunk-DSZVJ2PW.js.map +1 -0
- package/dist/chunk-T52H5HL3.js +11 -0
- package/dist/chunk-T52H5HL3.js.map +1 -0
- package/dist/{chunk-2WZIWIBW.js → chunk-ZBH7JYU4.js} +4 -10
- package/dist/chunk-ZBH7JYU4.js.map +1 -0
- package/dist/payments.cjs +223 -13
- package/dist/payments.cjs.map +1 -1
- package/dist/payments.css +27 -0
- package/dist/payments.d.cts +19 -4
- package/dist/payments.d.ts +19 -4
- package/dist/payments.js +54 -14
- package/dist/payments.js.map +1 -1
- package/dist/promotions.js +2 -1
- package/dist/promotions.js.map +1 -1
- package/package.json +13 -2
- package/dist/chunk-2WZIWIBW.js.map +0 -1
package/dist/promotions.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/promos/promo-logic.ts","../src/promos/promo-popup.tsx","../src/promos/cart-progress-bars.tsx","../src/promos/cross-sell-modal.tsx","../src/promos/order-note.tsx"],"names":["announcement","useId","useState","useEffect","jsxs","jsx","useMemo","promotionMessages","promoProgressView","formatAvailabilityMessage","Fragment","formatMoney","rewardLabel","useRef"],"mappings":"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","file":"promotions.js","sourcesContent":["/**\n * Pure mechanics of the entry pop-up (frequency, exclusions, crawlers, triggers). No DOM, no React:\n * every function takes its inputs, so each rule is a row in a table test.\n */\nexport type PromoFrequency = 'session' | 'days' | 'campaign'\n\n/** Paths where a promotional pop-up is never shown (cart, payment, thanks, account, auth, legal, editor previews). */\nexport const DEFAULT_PROMO_EXCLUDED_PATHS: readonly string[] = [\n '/carrito*',\n '/cart*',\n '/pagar*',\n '/checkout*',\n '/gracias*',\n '/cuenta*',\n '/account*',\n '/login*',\n '/iniciar-sesion*',\n '/registro*',\n '/terminos*',\n '/privacidad*',\n '/legal*',\n '/preview*',\n '/editor*',\n]\n\nexport type PathPattern = string | RegExp\n\n/** `/mx/carrito` and `/carrito` both reduce to `/carrito` (an optional 2-letter market/locale segment is ignored). */\nexport function stripMarketPrefix(pathname: string): string {\n return pathname.replace(/^\\/[a-z]{2}(?:-[a-z]{2})?(?=\\/|$)/i, '') || '/'\n}\n\nfunction matches(path: string, pattern: PathPattern): boolean {\n if (pattern instanceof RegExp) return pattern.test(path)\n const p = pattern.toLowerCase()\n if (p.endsWith('*')) {\n const prefix = p.slice(0, -1)\n return path === prefix.replace(/\\/$/, '') || path.startsWith(prefix)\n }\n return path === p || path === p.replace(/\\/$/, '')\n}\n\n/** True when the pop-up must not appear on `pathname`. Case-insensitive; the market prefix does not matter. */\nexport function isExcludedPath(pathname: string, patterns: readonly PathPattern[] = DEFAULT_PROMO_EXCLUDED_PATHS): boolean {\n const path = (pathname || '/').split(/[?#]/)[0]!.toLowerCase().replace(/\\/+$/, '') || '/'\n const bare = stripMarketPrefix(path)\n return patterns.some((p) => matches(path, p) || matches(bare, p))\n}\n\nconst CRAWLER = /bot|crawl|spider|slurp|bingpreview|facebookexternalhit|embedly|quora link|outbrain|pinterest|vkshare|w3c_validator|lighthouse|headless|prerender|google-structured-data|gtmetrix|pingdom/i\n\n/** Known crawlers and automation (`navigator.webdriver`). They never get the pop-up. */\nexport function isCrawler(userAgent: string | null | undefined, webdriver?: boolean): boolean {\n return webdriver === true || CRAWLER.test(userAgent ?? '')\n}\n\n/** Storage seam: cookie for cross-tab memory, sessionStorage for \"once per session\". Replaceable in tests and shops. */\nexport type PromoSeenStore = {\n get(name: string): string | null\n set(name: string, value: string, options: { days?: number; session?: boolean }): void\n}\n\nexport const PROMO_COOKIE = 'sf_promo'\n\nexport function memorySeenStore(): PromoSeenStore & { data: Map<string, string> } {\n const data = new Map<string, string>()\n return { data, get: (n) => data.get(n) ?? null, set: (n, v) => void data.set(n, v) }\n}\n\n/** Default store: `sessionStorage` for session frequency, a first-party cookie otherwise. Fails closed to memory. */\nexport function browserSeenStore(): PromoSeenStore {\n const fallback = memorySeenStore()\n return {\n get(name) {\n try {\n const fromSession = sessionStorage.getItem(name)\n if (fromSession) return fromSession\n } catch {\n /* blocked */\n }\n try {\n const m = document.cookie.split('; ').find((c) => c.startsWith(`${name}=`))\n if (m) return decodeURIComponent(m.slice(name.length + 1))\n } catch {\n /* blocked */\n }\n return fallback.get(name)\n },\n set(name, value, { days, session }) {\n fallback.set(name, value, {})\n if (session) {\n try {\n sessionStorage.setItem(name, value)\n } catch {\n /* memory only */\n }\n return\n }\n try {\n const maxAge = Math.round((days ?? 7) * 86400)\n document.cookie = `${name}=${encodeURIComponent(value)}; Max-Age=${maxAge}; Path=/; SameSite=Lax${location.protocol === 'https:' ? '; Secure' : ''}`\n } catch {\n /* memory only */\n }\n },\n }\n}\n\nexport type SeenRecord = { id: string; version: number; at: number }\n\nexport function serializeSeen(r: SeenRecord): string {\n return `${r.id}:${r.version}:${r.at}`\n}\n\nexport function parseSeen(raw: string | null | undefined): SeenRecord | null {\n if (!raw) return null\n const m = /^(.+):(\\d+):(\\d+)$/.exec(raw)\n if (!m) return null\n return { id: m[1]!, version: Number(m[2]), at: Number(m[3]) }\n}\n\nexport type FrequencyInput = {\n id: string\n version: number\n frequency: PromoFrequency\n /** Window for `days` (default 7). */\n days?: number\n seen: SeenRecord | null\n now: number\n}\n\n/**\n * Frequency rule. A new `version` of the same announcement shows again.\n * - `session`: once per browser session (the store is the session store).\n * - `days`: once every N days.\n * - `campaign`: once per campaign version.\n */\nexport function frequencyAllows({ id, version, frequency, days = 7, seen, now }: FrequencyInput): boolean {\n if (!seen || seen.id !== id || seen.version !== version) return true\n if (frequency === 'days') return now - seen.at >= days * 86_400_000\n return false\n}\n\nexport type PromoWindow = { starts_at?: string | null; ends_at?: string | null }\n\n/** Inside the campaign dates (unparseable dates are ignored: the server decides, not the clock of a bad value). */\nexport function withinWindow({ starts_at, ends_at }: PromoWindow, now: number): boolean {\n const s = starts_at ? Date.parse(starts_at) : NaN\n const e = ends_at ? Date.parse(ends_at) : NaN\n if (Number.isFinite(s) && now < s) return false\n if (Number.isFinite(e) && now > e) return false\n return true\n}\n\nexport type PromoTrigger = {\n /** Show after N ms. `false` disables the timer. Default 5000. */\n delayMs?: number | false\n /** Show on the Nth page view of the session. `false` disables. Default 2. */\n pageviews?: number | false\n /** Show after scrolling this share of the page (0-100). `false` disables. Default 40. */\n scrollPercent?: number | false\n}\n\nexport const DEFAULT_PROMO_TRIGGER: Required<PromoTrigger> = { delayMs: 5000, pageviews: 2, scrollPercent: 40 }\n\nexport function resolveTrigger(t?: PromoTrigger | null): Required<PromoTrigger> {\n return { ...DEFAULT_PROMO_TRIGGER, ...(t ?? {}) }\n}\n\n/** Scroll depth as a percentage of the scrollable height (UI measure only). */\nexport function scrollDepthPercent(scrollY: number, viewportHeight: number, docHeight: number): number {\n const scrollable = docHeight - viewportHeight\n if (!(scrollable > 0)) return 0\n return Math.min(100, Math.max(0, (scrollY / scrollable) * 100))\n}\n\n/** Only same-site paths are valid CTAs (`/colecciones/x`). Anything else (`//evil`, `https:`, `javascript:`) is dropped. */\nexport function safeInternalPath(href: string | null | undefined): string | null {\n if (!href || !href.startsWith('/') || href.startsWith('//') || /[\\\\\\u0000-\\u001f]/.test(href)) return null\n return href\n}\n","import { useEffect, useId, useMemo, useRef, useState, type ReactNode } from 'react'\nimport {\n formatMoney,\n promotionMessages,\n rewardLabel,\n formatAvailabilityMessage,\n type PromotionAnnouncement,\n type PromotionMessages,\n} from '@asteby/storefront'\nimport { useModalDialog, type DialogCloseReason } from '../dialog.js'\nimport { useResolvedLocale } from '../locale.js'\nimport {\n browserSeenStore,\n frequencyAllows,\n isCrawler,\n isExcludedPath,\n memorySeenStore,\n parseSeen,\n PROMO_COOKIE,\n resolveTrigger,\n safeInternalPath,\n scrollDepthPercent,\n serializeSeen,\n withinWindow,\n type PathPattern,\n type PromoFrequency,\n type PromoSeenStore,\n type PromoTrigger,\n} from './promo-logic.js'\n\nexport type PromoPopupEvent =\n | { type: 'view'; id: string; version: number; via: 'delay' | 'pageviews' | 'scroll' }\n | { type: 'close'; id: string; how: 'esc' | 'x' | 'backdrop' }\n | { type: 'cta'; id: string }\n\nexport type PromoPopupProps = {\n /** Content as data from `GET /storefront/promotions`. `null`/absent: nothing happens. Amounts are the engine's. */\n announcement: PromotionAnnouncement | null | undefined\n /** Current path, from the router (so route changes are seen). Default: `location.pathname`. */\n pathname?: string\n /** Paths where the pop-up never appears. Default: cart, payment, thanks, account, auth, legal, editor. */\n excludePaths?: readonly PathPattern[]\n /** Overrides `announcement.frequency` (default `days`). */\n frequency?: PromoFrequency\n /** Window for `days` (default `announcement.frequency_days` or 7). */\n frequencyDays?: number\n /** Overrides `announcement.trigger`. Defaults: 5 s, second page view or 40 % scroll, whichever comes first. */\n trigger?: PromoTrigger\n locale?: string\n messages?: Partial<PromotionMessages>\n /** Where \"already seen\" is remembered. Default: cookie `sf_promo` (7 days) or sessionStorage for `session`. */\n store?: PromoSeenStore\n /** Analytics hook: view, close (how) and CTA. No personal data. */\n onEvent?: (event: PromoPopupEvent) => void\n /** Renders the CTA (use your router's `Link`). Default: a plain `<a href>`. */\n renderCta?: (props: { href: string; label: string; onClick: () => void; className?: string }) => ReactNode\n /** Test hooks. */\n now?: () => number\n userAgent?: string\n webdriver?: boolean\n className?: string\n}\n\nconst PAGEVIEW_KEY = 'sf_promo_pv'\n\n/**\n * Entry pop-up mechanism. The CONTENT is data (`announcement`); this component only decides when to show it.\n *\n * - Client only: it renders nothing on the server and on the first client render, so SSR/SEO and hydration\n * are untouched and the pop-up never delays the LCP.\n * - Shown at most per `frequency` (session / every N days / once per campaign version), never on excluded\n * paths, to crawlers or outside the campaign dates.\n * - Accessible: native `<dialog>` (focus trap, inert background), Esc, a 44x44 close button, labelled title,\n * focus returns to where it was, no entry animation (nothing to disable for `prefers-reduced-motion`).\n */\nexport function PromoPopup(props: PromoPopupProps) {\n const { announcement, messages, onEvent, className } = props\n const locale = useResolvedLocale(props.locale)\n const [open, setOpen] = useState(false)\n const shown = useRef<string | null>(null)\n const titleId = useId()\n const m = useMemo(() => promotionMessages(locale, messages), [locale, messages])\n const storeRef = useRef<PromoSeenStore | null>(props.store ?? null)\n const live = useRef(props)\n live.current = props\n const pathname = props.pathname ?? (typeof window !== 'undefined' ? window.location.pathname : '/')\n const excluded = isExcludedPath(pathname, props.excludePaths)\n\n const annKey = announcement ? `${announcement.id}:${announcement.version}` : null\n\n // Session page views: one per path, counted once even if the effect re-runs (StrictMode).\n const views = useRef(0)\n const lastCounted = useRef<string | null>(null)\n useEffect(() => {\n if (lastCounted.current === pathname) return\n lastCounted.current = pathname\n try {\n views.current = Number(sessionStorage.getItem(PAGEVIEW_KEY) ?? '0') + 1\n sessionStorage.setItem(PAGEVIEW_KEY, String(views.current))\n } catch {\n views.current += 1\n }\n }, [pathname])\n\n useEffect(() => {\n const announcement = live.current.announcement\n if (!announcement || excluded) {\n setOpen(false)\n return\n }\n const now = () => (live.current.now ? live.current.now() : Date.now())\n const ua = live.current.userAgent ?? (typeof navigator !== 'undefined' ? navigator.userAgent : '')\n const driver = live.current.webdriver ?? (typeof navigator !== 'undefined' ? (navigator as Navigator & { webdriver?: boolean }).webdriver : false)\n if (isCrawler(ua, driver)) return\n if (!withinWindow(announcement, now())) return\n const frequency = live.current.frequency ?? announcement.frequency ?? 'days'\n const store = (storeRef.current ??= typeof window !== 'undefined' ? browserSeenStore() : memorySeenStore())\n const cookieName = PROMO_COOKIE\n const allowed = () =>\n frequencyAllows({\n id: announcement.id,\n version: announcement.version,\n frequency,\n days: live.current.frequencyDays ?? announcement.frequency_days ?? 7,\n seen: parseSeen(store.get(cookieName)),\n now: now(),\n })\n if (!allowed()) return\n if (shown.current === annKey) return\n\n const trigger = resolveTrigger({ ...(announcement.trigger ? {\n ...(announcement.trigger.delay_ms !== undefined ? { delayMs: announcement.trigger.delay_ms } : {}),\n ...(announcement.trigger.pageviews !== undefined ? { pageviews: announcement.trigger.pageviews } : {}),\n ...(announcement.trigger.scroll_percent !== undefined ? { scrollPercent: announcement.trigger.scroll_percent } : {}),\n } : {}), ...(live.current.trigger ?? {}) })\n\n let done = false\n const cleanups: (() => void)[] = []\n const fire = (via: 'delay' | 'pageviews' | 'scroll') => {\n if (done || !allowed()) return\n done = true\n cleanups.forEach((c) => c())\n shown.current = annKey\n store.set(cookieName, serializeSeen({ id: announcement.id, version: announcement.version, at: now() }), frequency === 'session' ? { session: true } : { days: frequency === 'campaign' ? 365 : (live.current.frequencyDays ?? announcement.frequency_days ?? 7) })\n setOpen(true)\n live.current.onEvent?.({ type: 'view', id: announcement.id, version: announcement.version, via })\n }\n\n if (trigger.pageviews !== false && views.current >= trigger.pageviews) {\n // After the effect flush, never in the first paint.\n const t = setTimeout(() => fire('pageviews'), 0)\n cleanups.push(() => clearTimeout(t))\n }\n if (trigger.delayMs !== false) {\n const t = setTimeout(() => fire('delay'), trigger.delayMs)\n cleanups.push(() => clearTimeout(t))\n }\n if (trigger.scrollPercent !== false && typeof window !== 'undefined') {\n const target = trigger.scrollPercent\n const onScroll = () => {\n const depth = scrollDepthPercent(window.scrollY, window.innerHeight, document.documentElement.scrollHeight)\n if (depth >= target) fire('scroll')\n }\n window.addEventListener('scroll', onScroll, { passive: true })\n cleanups.push(() => window.removeEventListener('scroll', onScroll))\n }\n return () => {\n done = true\n cleanups.forEach((c) => c())\n }\n }, [annKey, excluded])\n\n const close = (how: DialogCloseReason) => {\n setOpen(false)\n if (announcement && how !== 'action') onEvent?.({ type: 'close', id: announcement.id, how })\n }\n const { ref, onBackdropClick } = useModalDialog({ open, onClose: close })\n\n if (!announcement || !open || excluded) return null\n\n const cta = safeInternalPath(announcement.cta_url)\n const ctaLabel = announcement.cta_label\n const limited = !!announcement.ends_at\n let validUntil: string | null = null\n if (announcement.ends_at) {\n try {\n validUntil = formatAvailabilityMessage(m['popup.valid_until'], {\n date: new Intl.DateTimeFormat(locale, { dateStyle: 'long' }).format(new Date(announcement.ends_at)),\n })\n } catch {\n validUntil = null\n }\n }\n const onCta = () => {\n onEvent?.({ type: 'cta', id: announcement.id })\n setOpen(false)\n }\n\n return (\n <dialog ref={ref} className={className} aria-labelledby={titleId} data-sf-promo-popup onClick={onBackdropClick}>\n <div data-sf-promo-body>\n <button\n type=\"button\"\n onClick={() => close('x')}\n aria-label={m['popup.close']}\n data-sf-promo-close\n style={{ minWidth: 44, minHeight: 44 }}\n >\n <span aria-hidden=\"true\">×</span>\n </button>\n {limited && <p data-sf-promo-eyebrow>{m['popup.limited_time']}</p>}\n <h2 id={titleId} tabIndex={-1} data-sf-autofocus>\n {announcement.headline}\n </h2>\n {announcement.subheadline && <p>{announcement.subheadline}</p>}\n {announcement.image && <img src={announcement.image.url} alt={announcement.image.alt} loading=\"lazy\" decoding=\"async\" />}\n {(announcement.tiers.length > 0 || announcement.free_shipping_threshold) && (\n <ul data-sf-promo-tiers>\n {announcement.tiers.map((t) => (\n <li key={`${t.threshold.amount}-${rewardLabel(t.reward, locale, messages)}`}>\n {formatAvailabilityMessage(m['popup.discount_tier'], {\n reward: rewardLabel(t.reward, locale, messages),\n threshold: formatMoney(t.threshold, locale, { withoutTrailingZeros: true }),\n })}\n </li>\n ))}\n {announcement.free_shipping_threshold && (\n <li>\n {Number.parseFloat(announcement.free_shipping_threshold.amount) === 0\n ? m['popup.free_shipping_always']\n : formatAvailabilityMessage(m['popup.free_shipping_min'], {\n threshold: formatMoney(announcement.free_shipping_threshold, locale, { withoutTrailingZeros: true }),\n })}\n </li>\n )}\n </ul>\n )}\n {cta && ctaLabel &&\n (props.renderCta ? (\n props.renderCta({ href: cta, label: ctaLabel, onClick: onCta })\n ) : (\n <a href={cta} onClick={onCta} data-sf-promo-cta style={{ minHeight: 44, display: 'inline-flex', alignItems: 'center' }}>\n {ctaLabel}\n </a>\n ))}\n <p data-sf-promo-legal>\n {announcement.legal_text}\n {validUntil && <> {validUntil}.</>}\n </p>\n </div>\n </dialog>\n )\n}\n","import { useEffect, useId, useMemo, useState, type CSSProperties } from 'react'\nimport { promoProgressView, promotionMessages, type CartPromotions, type PromotionMessages } from '@asteby/storefront'\nimport { prefersReducedMotion } from '../dialog.js'\nimport { useResolvedLocale } from '../locale.js'\n\nexport type ProgressBarProps = {\n /** Visible text (also `aria-valuetext`). */\n text: string\n /** 0-100, from the server. */\n percent: number\n label: string\n showBar?: boolean\n milestones?: { id: string; label: string; threshold: string; reached: boolean; position: number }[]\n state: string\n kind: 'discount' | 'shipping'\n className?: string\n}\n\n/**\n * One accessible bar: color is never the only signal (text states the remaining amount; milestones are text).\n * The fill width is the server percentage; the transition is off for `prefers-reduced-motion`.\n */\nexport function PromoProgressBar({ text, percent, label, showBar = true, milestones, state, kind, className }: ProgressBarProps) {\n const id = useId()\n const [reduced, setReduced] = useState(false)\n useEffect(() => setReduced(prefersReducedMotion()), [])\n const fill: CSSProperties = { width: `${percent}%`, transition: reduced ? 'none' : 'width 600ms ease' }\n return (\n <div className={className} data-sf-promo-bar={kind} data-state={state}>\n <p id={`${id}-t`} aria-live=\"polite\" data-sf-promo-bar-text>\n {text}\n </p>\n {showBar && (\n <div role=\"progressbar\" aria-label={label} aria-describedby={`${id}-t`} aria-valuemin={0} aria-valuemax={100} aria-valuenow={percent} aria-valuetext={text} data-sf-promo-track>\n <div style={fill} data-sf-promo-fill />\n </div>\n )}\n {showBar && milestones && milestones.length > 0 && (\n <ul data-sf-promo-milestones>\n {milestones.map((t) => (\n <li key={t.id} data-reached={t.reached} style={{ left: `${t.position}%` }}>\n <span>{t.label}</span> <span>{t.threshold}</span>\n {t.reached && <span data-sf-sr-only style={{ position: 'absolute', width: 1, height: 1, overflow: 'hidden', clip: 'rect(0 0 0 0)' }}> ✓</span>}\n </li>\n ))}\n </ul>\n )}\n </div>\n )\n}\n\nexport type CartProgressBarsProps = {\n /** `Cart.promotions` exactly as the server sent it. */\n promotions: CartPromotions | null | undefined\n locale?: string\n messages?: Partial<PromotionMessages>\n /** Show the tier milestones under the discount bar. Default true. */\n showMilestones?: boolean\n className?: string\n}\n\n/**\n * Automatic-discount bar and free-shipping bar of the cart (or drawer). Pure view of `Cart.promotions`:\n * `remaining`, `percent` and `reached` come from OPS; the shop never subtracts or divides.\n * Renders nothing without promotions.\n */\nexport function CartProgressBars({ promotions, locale: localeProp, messages, showMilestones = true, className }: CartProgressBarsProps) {\n const locale = useResolvedLocale(localeProp)\n const view = useMemo(() => promoProgressView(promotions, locale, messages), [promotions, locale, messages])\n const m = promotionMessages(locale, messages)\n if (!view.discount && !view.shipping) return null\n return (\n <div className={className} data-sf-promo-bars>\n {view.discount && (\n <PromoProgressBar\n kind=\"discount\"\n state={view.discount.state}\n text={view.discount.text}\n percent={view.discount.percent}\n label={m['bar.discount']}\n {...(showMilestones ? { milestones: view.discount.tiers } : {})}\n />\n )}\n {view.shipping && (\n <PromoProgressBar kind=\"shipping\" state={view.shipping.state} text={view.shipping.text} percent={view.shipping.percent} label={m['bar.shipping']} showBar={view.shipping.showBar} />\n )}\n </div>\n )\n}\n","import { useId, useMemo, useRef, useState, type ReactNode } from 'react'\nimport {\n formatAvailabilityMessage,\n formatMoney,\n promoProgressView,\n promotionMessages,\n rewardLabel,\n type CartPromotions,\n type CartRecommendation,\n type Money,\n type PromotionMessages,\n} from '@asteby/storefront'\nimport { useModalDialog, type DialogCloseReason } from '../dialog.js'\nimport { useResolvedLocale } from '../locale.js'\nimport { PromoProgressBar } from './cart-progress-bars.js'\n\nexport type CrossSellAddResult = void | { ok?: boolean; error?: 'sold_out' | 'failed' }\n\nexport type CrossSellModalProps = {\n open: boolean\n /** `Cart.promotions` of the CURRENT cart; after an add, pass the new cart's and the modal updates. */\n promotions: CartPromotions | null | undefined\n recommendations: CartRecommendation[]\n /** Total saved so far, from the server (`cart.cost.discount`). */\n totalSaved: Money\n /**\n * The shop adds the variant (server action / fetcher) and then re-renders with the new `promotions` and\n * `recommendations`. Return `{ error: 'sold_out' }` for a 409 so the card tells the buyer to pick another flavor.\n */\n onAdd: (input: { productId: string; variantId: string; recommendation: CartRecommendation }) => Promise<CrossSellAddResult> | CrossSellAddResult\n /** \"Continuar al pago\". The shop navigates to payment. */\n onContinue: () => void\n /** \"Continuar sin agregar\", X, Esc or backdrop. The shop marks the modal as seen and navigates to payment. */\n onSkip: (reason: 'skip' | 'esc' | 'x' | 'backdrop') => void\n locale?: string\n messages?: Partial<PromotionMessages>\n /** Renders a card image (use your `Image`). Default: a lazy `<img>` of the featured image. */\n renderImage?: (rec: CartRecommendation) => ReactNode\n className?: string\n}\n\n/**\n * Pre-payment upsell modal (\"Completa tu compra y desbloquea más\").\n *\n * Mechanism only: the shop decides when to open it (once per cart, JS only) and what \"continue\" means.\n * Every number comes from the server: remaining amount, rewards, progress, total saved, and the\n * \"Desbloquea tu 5 %\" tag (`unlocks`). Add errors stay on their card; the rest stays usable.\n * Accessible: native dialog, labelled title, focus starts on the title (not on a buy button), Esc = skip,\n * `role=\"progressbar\"` with spoken remaining amount, level changes announced via `aria-live`.\n */\nexport function CrossSellModal(props: CrossSellModalProps) {\n const { open, promotions, recommendations, totalSaved, onAdd, onContinue, onSkip, messages, renderImage, className } = props\n const locale = useResolvedLocale(props.locale)\n const m = promotionMessages(locale, messages)\n const titleId = useId()\n const descId = useId()\n const view = useMemo(() => promoProgressView(promotions, locale, messages).discount, [promotions, locale, messages])\n const { ref, onBackdropClick } = useModalDialog({ open, onClose: (r: DialogCloseReason) => onSkip(r === 'esc' ? 'esc' : r === 'x' ? 'x' : 'backdrop') })\n const nextTier = promotions?.next_tier ?? null\n const max = !!promotions && !nextTier && !!promotions.applied\n\n if (!open) return null\n\n const f = formatAvailabilityMessage\n return (\n <dialog ref={ref} className={className} aria-labelledby={titleId} aria-describedby={descId} onClick={onBackdropClick} data-sf-crosssell>\n <div data-sf-crosssell-body>\n <button type=\"button\" onClick={() => onSkip('x')} aria-label={m['popup.close']} data-sf-crosssell-close style={{ minWidth: 44, minHeight: 44 }}>\n <span aria-hidden=\"true\">×</span>\n </button>\n <p data-sf-crosssell-eyebrow>{m['upsell.subtitle']}</p>\n <h2 id={titleId} tabIndex={-1} data-sf-autofocus>\n {m['upsell.title']}\n </h2>\n\n <section id={descId} data-sf-crosssell-level aria-live=\"polite\">\n {nextTier ? (\n <>\n <p data-sf-crosssell-next>{m['upsell.next_level']}</p>\n <p>\n <strong>{f(m['upsell.you_miss'], { remaining: formatMoney(nextTier.remaining, locale, { withoutTrailingZeros: true }) })}</strong>\n </p>\n <p>{f(m['upsell.unlocks_reward'], { reward: rewardLabel(nextTier.reward, locale, messages) })}</p>\n </>\n ) : max && promotions?.applied ? (\n <p data-sf-crosssell-best>{f(m['upsell.best'], { reward: rewardLabel(promotions.applied.reward, locale, messages) })}</p>\n ) : null}\n {promotions?.applied && nextTier && <p data-sf-crosssell-unlocked>{f(m['upsell.unlocked'], { reward: rewardLabel(promotions.applied.reward, locale, messages) })}</p>}\n </section>\n\n <dl data-sf-crosssell-summary>\n <div>\n <dt>{m['upsell.total_saved']}</dt>\n <dd>{formatMoney(totalSaved, locale, { withoutTrailingZeros: true })}</dd>\n </div>\n <div>\n <dt>{m['upsell.current_benefit']}</dt>\n <dd>{rewardLabel(promotions?.applied?.reward ?? { kind: 'percent', percent: '0' }, locale, messages)}</dd>\n </div>\n <div>\n <dt>{m['upsell.status']}</dt>\n <dd>{promotions?.applied ? m['upsell.status_active'] : m['upsell.status_pending']}</dd>\n </div>\n </dl>\n\n {view && <PromoProgressBar kind=\"discount\" state={view.state} text={view.text} percent={view.percent} label={m['bar.discount']} milestones={view.tiers} />}\n {nextTier && (\n <p data-sf-crosssell-next-reward>\n {f(m['upsell.next_reward'], {\n reward: rewardLabel(nextTier.reward, locale, messages),\n threshold: formatMoney(nextTier.threshold, locale, { withoutTrailingZeros: true }),\n })}\n </p>\n )}\n\n {!max && recommendations.length > 0 && (\n <section aria-label={m['upsell.suggestions']}>\n <h3>{m['upsell.suggestions']}</h3>\n <ul data-sf-crosssell-cards>\n {recommendations.map((rec) => (\n <RecommendationCard key={rec.product.id} rec={rec} onAdd={onAdd} locale={locale} messages={messages} {...(renderImage ? { renderImage } : {})} />\n ))}\n </ul>\n </section>\n )}\n\n <p data-sf-crosssell-footer>{m['upsell.footer']}</p>\n <div data-sf-crosssell-actions>\n <button type=\"button\" onClick={() => onSkip('skip')} style={{ minHeight: 44 }} data-sf-crosssell-skip>\n {m['upsell.skip']}\n </button>\n <button type=\"button\" onClick={onContinue} style={{ minHeight: 44 }} data-sf-crosssell-continue>\n {m['upsell.continue']} →\n </button>\n </div>\n </div>\n </dialog>\n )\n}\n\nfunction RecommendationCard({\n rec,\n onAdd,\n locale,\n messages,\n renderImage,\n}: {\n rec: CartRecommendation\n onAdd: CrossSellModalProps['onAdd']\n locale?: string | undefined\n messages?: Partial<PromotionMessages> | undefined\n renderImage?: (rec: CartRecommendation) => ReactNode\n}) {\n const m = promotionMessages(locale, messages)\n const selectId = useId()\n const [variantId, setVariantId] = useState(rec.variants.some((v) => v.id === rec.default_variant_id) ? rec.default_variant_id : (rec.variants[0]?.id ?? ''))\n const [busy, setBusy] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const inFlight = useRef(false)\n const variant = rec.variants.find((v) => v.id === variantId) ?? rec.variants[0]\n const image = rec.product.featured_image\n\n // Variants refreshed by the server: keep the selection valid.\n if (variant && variant.id !== variantId) setVariantId(variant.id)\n if (!variant) return null\n\n async function add() {\n if (inFlight.current || !variant) return // double click: one add\n inFlight.current = true\n setBusy(true)\n setError(null)\n try {\n const r = await onAdd({ productId: rec.product.id, variantId: variant.id, recommendation: rec })\n if (r && (r.ok === false || r.error)) setError(r.error === 'sold_out' ? m['upsell.sold_out'] : m['upsell.error'])\n } catch {\n setError(m['upsell.error'])\n } finally {\n inFlight.current = false\n setBusy(false)\n }\n }\n\n return (\n <li data-sf-crosssell-card>\n {renderImage ? renderImage(rec) : image ? <img src={image.url} alt={image.alt ?? rec.product.title} width={160} loading=\"lazy\" decoding=\"async\" /> : null}\n {rec.unlocks && <p data-sf-crosssell-unlocks>{formatAvailabilityMessage(m['upsell.unlocks_badge'], { reward: rewardLabel(rec.unlocks.reward, locale, messages) })}</p>}\n <p data-sf-crosssell-name>{rec.product.title}</p>\n <p data-sf-crosssell-price>{formatMoney(variant.price, locale, { withoutTrailingZeros: true })}</p>\n {rec.variants.length > 1 && (\n <>\n <label htmlFor={selectId}>{m['upsell.flavor']}</label>\n <select id={selectId} value={variant.id} onChange={(e) => setVariantId(e.target.value)} style={{ minHeight: 44 }}>\n {rec.variants.map((v) => (\n <option key={v.id} value={v.id}>\n {v.title}\n </option>\n ))}\n </select>\n </>\n )}\n <button type=\"button\" onClick={() => void add()} aria-busy={busy} aria-disabled={busy} style={{ minHeight: 44 }} data-sf-crosssell-add>\n {busy ? m['upsell.adding'] : m['upsell.add']}\n </button>\n {error && (\n <p role=\"alert\" data-sf-crosssell-error>\n {error}\n </p>\n )}\n </li>\n )\n}\n","import { useEffect, useId, useRef, useState } from 'react'\nimport { useResolvedLocale } from '../locale.js'\nimport { NOTE_MAX_LENGTH, formatAvailabilityMessage, normalizeNote, promotionMessages, type PromotionMessages } from '@asteby/storefront'\n\nexport type OrderNoteStatus = 'idle' | 'dirty' | 'saving' | 'saved' | 'error'\n\nexport type OrderNoteProps = {\n /** Saved note (`Cart.note`). */\n value: string | null | undefined\n /** Persists the note (`client.carts.setNote` through the shop action). Must resolve once stored. Throw to show the error. */\n onSave: (note: string | null) => Promise<void> | void\n /** Autosave debounce in ms. Default 800. `false`: save only on blur / button. */\n debounceMs?: number | false\n /**\n * No-JS form: `<form method=\"post\" action>` with a hidden `intent` and a \"Guardar nota\" button, hidden once the\n * component hydrates (the autosave takes over). The shop's route action handles it.\n */\n form?: { action?: string; method?: 'post'; intent?: string; intentName?: string }\n locale?: string\n messages?: Partial<PromotionMessages>\n className?: string\n maxLength?: number\n}\n\n/**\n * \"Añadir nota\" for the order. A real `<textarea>` with a visible label; the server enforces 1000 characters and\n * plain text. With JavaScript it autosaves (debounced, on blur) and announces \"Guardada\"; without it, the form posts.\n */\nexport function OrderNote({ value, onSave, debounceMs = 800, form, locale: localeProp, messages, className, maxLength = NOTE_MAX_LENGTH }: OrderNoteProps) {\n const locale = useResolvedLocale(localeProp)\n const m = promotionMessages(locale, messages)\n const id = useId()\n const [text, setText] = useState(value ?? '')\n const [status, setStatus] = useState<OrderNoteStatus>('idle')\n const [hydrated, setHydrated] = useState(false)\n const saved = useRef(normalizeNote(value))\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null)\n const seq = useRef(0)\n const latest = useRef({ text, onSave })\n latest.current = { text, onSave }\n\n useEffect(() => setHydrated(true), [])\n // A new server value (another tab, cart refresh) replaces the field unless the buyer is typing.\n useEffect(() => {\n if (status === 'dirty' || status === 'saving') return\n const incoming = value ?? ''\n if (normalizeNote(incoming) !== normalizeNote(latest.current.text)) setText(incoming)\n saved.current = normalizeNote(value)\n }, [value])\n useEffect(() => () => void (timer.current && clearTimeout(timer.current)), [])\n\n async function flush() {\n if (timer.current) clearTimeout(timer.current)\n timer.current = null\n const next = normalizeNote(latest.current.text)\n if (next === saved.current) {\n setStatus((s) => (s === 'dirty' ? 'idle' : s))\n return\n }\n const mine = ++seq.current\n setStatus('saving')\n try {\n await latest.current.onSave(next)\n if (mine !== seq.current) return // a newer save superseded this one\n saved.current = next\n setStatus('saved')\n } catch {\n if (mine === seq.current) setStatus('error')\n }\n }\n\n function onChange(v: string) {\n setText(v.slice(0, maxLength))\n setStatus('dirty')\n if (debounceMs !== false) {\n if (timer.current) clearTimeout(timer.current)\n timer.current = setTimeout(() => void flush(), debounceMs)\n }\n }\n\n const statusText = status === 'saving' ? m['note.saving'] : status === 'saved' ? m['note.saved'] : status === 'error' ? m['note.error'] : ''\n const field = (\n <>\n <label htmlFor={id}>{m['note.label']}</label>\n <textarea\n id={id}\n name=\"note\"\n value={text}\n maxLength={maxLength}\n placeholder={m['note.placeholder']}\n onChange={(e) => onChange(e.target.value)}\n onBlur={() => void flush()}\n aria-describedby={`${id}-s`}\n rows={3}\n />\n <p id={`${id}-c`} data-sf-note-counter>\n {formatAvailabilityMessage(m['note.counter'], { n: text.length, max: maxLength })}\n </p>\n <p id={`${id}-s`} role={status === 'error' ? 'alert' : 'status'} aria-live=\"polite\" data-sf-note-status={status}>\n {statusText}\n </p>\n </>\n )\n\n if (!form) return <div className={className} data-sf-order-note>{field}</div>\n return (\n <form method={form.method ?? 'post'} {...(form.action ? { action: form.action } : {})} className={className} data-sf-order-note onSubmit={(e) => hydrated && e.preventDefault()}>\n <input type=\"hidden\" name={form.intentName ?? 'intent'} value={form.intent ?? 'note'} />\n {field}\n {!hydrated && <button type=\"submit\">{m['note.save']}</button>}\n </form>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/promos/promo-logic.ts","../src/promos/promo-popup.tsx","../src/promos/cart-progress-bars.tsx","../src/promos/cross-sell-modal.tsx","../src/promos/order-note.tsx"],"names":["announcement","useId","useState","useEffect","jsxs","jsx","useMemo","promotionMessages","promoProgressView","formatAvailabilityMessage","Fragment","formatMoney","rewardLabel","useRef"],"mappings":"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","file":"promotions.js","sourcesContent":["/**\n * Pure mechanics of the entry pop-up (frequency, exclusions, crawlers, triggers). No DOM, no React:\n * every function takes its inputs, so each rule is a row in a table test.\n */\nexport type PromoFrequency = 'session' | 'days' | 'campaign'\n\n/** Paths where a promotional pop-up is never shown (cart, payment, thanks, account, auth, legal, editor previews). */\nexport const DEFAULT_PROMO_EXCLUDED_PATHS: readonly string[] = [\n '/carrito*',\n '/cart*',\n '/pagar*',\n '/checkout*',\n '/gracias*',\n '/cuenta*',\n '/account*',\n '/login*',\n '/iniciar-sesion*',\n '/registro*',\n '/terminos*',\n '/privacidad*',\n '/legal*',\n '/preview*',\n '/editor*',\n]\n\nexport type PathPattern = string | RegExp\n\n/** `/mx/carrito` and `/carrito` both reduce to `/carrito` (an optional 2-letter market/locale segment is ignored). */\nexport function stripMarketPrefix(pathname: string): string {\n return pathname.replace(/^\\/[a-z]{2}(?:-[a-z]{2})?(?=\\/|$)/i, '') || '/'\n}\n\nfunction matches(path: string, pattern: PathPattern): boolean {\n if (pattern instanceof RegExp) return pattern.test(path)\n const p = pattern.toLowerCase()\n if (p.endsWith('*')) {\n const prefix = p.slice(0, -1)\n return path === prefix.replace(/\\/$/, '') || path.startsWith(prefix)\n }\n return path === p || path === p.replace(/\\/$/, '')\n}\n\n/** True when the pop-up must not appear on `pathname`. Case-insensitive; the market prefix does not matter. */\nexport function isExcludedPath(pathname: string, patterns: readonly PathPattern[] = DEFAULT_PROMO_EXCLUDED_PATHS): boolean {\n const path = (pathname || '/').split(/[?#]/)[0]!.toLowerCase().replace(/\\/+$/, '') || '/'\n const bare = stripMarketPrefix(path)\n return patterns.some((p) => matches(path, p) || matches(bare, p))\n}\n\nconst CRAWLER = /bot|crawl|spider|slurp|bingpreview|facebookexternalhit|embedly|quora link|outbrain|pinterest|vkshare|w3c_validator|lighthouse|headless|prerender|google-structured-data|gtmetrix|pingdom/i\n\n/** Known crawlers and automation (`navigator.webdriver`). They never get the pop-up. */\nexport function isCrawler(userAgent: string | null | undefined, webdriver?: boolean): boolean {\n return webdriver === true || CRAWLER.test(userAgent ?? '')\n}\n\n/** Storage seam: cookie for cross-tab memory, sessionStorage for \"once per session\". Replaceable in tests and shops. */\nexport type PromoSeenStore = {\n get(name: string): string | null\n set(name: string, value: string, options: { days?: number; session?: boolean }): void\n}\n\nexport const PROMO_COOKIE = 'sf_promo'\n\nexport function memorySeenStore(): PromoSeenStore & { data: Map<string, string> } {\n const data = new Map<string, string>()\n return { data, get: (n) => data.get(n) ?? null, set: (n, v) => void data.set(n, v) }\n}\n\n/** Default store: `sessionStorage` for session frequency, a first-party cookie otherwise. Fails closed to memory. */\nexport function browserSeenStore(): PromoSeenStore {\n const fallback = memorySeenStore()\n return {\n get(name) {\n try {\n const fromSession = sessionStorage.getItem(name)\n if (fromSession) return fromSession\n } catch {\n /* blocked */\n }\n try {\n const m = document.cookie.split('; ').find((c) => c.startsWith(`${name}=`))\n if (m) return decodeURIComponent(m.slice(name.length + 1))\n } catch {\n /* blocked */\n }\n return fallback.get(name)\n },\n set(name, value, { days, session }) {\n fallback.set(name, value, {})\n if (session) {\n try {\n sessionStorage.setItem(name, value)\n } catch {\n /* memory only */\n }\n return\n }\n try {\n const maxAge = Math.round((days ?? 7) * 86400)\n document.cookie = `${name}=${encodeURIComponent(value)}; Max-Age=${maxAge}; Path=/; SameSite=Lax${location.protocol === 'https:' ? '; Secure' : ''}`\n } catch {\n /* memory only */\n }\n },\n }\n}\n\nexport type SeenRecord = { id: string; version: number; at: number }\n\nexport function serializeSeen(r: SeenRecord): string {\n return `${r.id}:${r.version}:${r.at}`\n}\n\nexport function parseSeen(raw: string | null | undefined): SeenRecord | null {\n if (!raw) return null\n const m = /^(.+):(\\d+):(\\d+)$/.exec(raw)\n if (!m) return null\n return { id: m[1]!, version: Number(m[2]), at: Number(m[3]) }\n}\n\nexport type FrequencyInput = {\n id: string\n version: number\n frequency: PromoFrequency\n /** Window for `days` (default 7). */\n days?: number\n seen: SeenRecord | null\n now: number\n}\n\n/**\n * Frequency rule. A new `version` of the same announcement shows again.\n * - `session`: once per browser session (the store is the session store).\n * - `days`: once every N days.\n * - `campaign`: once per campaign version.\n */\nexport function frequencyAllows({ id, version, frequency, days = 7, seen, now }: FrequencyInput): boolean {\n if (!seen || seen.id !== id || seen.version !== version) return true\n if (frequency === 'days') return now - seen.at >= days * 86_400_000\n return false\n}\n\nexport type PromoWindow = { starts_at?: string | null; ends_at?: string | null }\n\n/** Inside the campaign dates (unparseable dates are ignored: the server decides, not the clock of a bad value). */\nexport function withinWindow({ starts_at, ends_at }: PromoWindow, now: number): boolean {\n const s = starts_at ? Date.parse(starts_at) : NaN\n const e = ends_at ? Date.parse(ends_at) : NaN\n if (Number.isFinite(s) && now < s) return false\n if (Number.isFinite(e) && now > e) return false\n return true\n}\n\nexport type PromoTrigger = {\n /** Show after N ms. `false` disables the timer. Default 5000. */\n delayMs?: number | false\n /** Show on the Nth page view of the session. `false` disables. Default 2. */\n pageviews?: number | false\n /** Show after scrolling this share of the page (0-100). `false` disables. Default 40. */\n scrollPercent?: number | false\n}\n\nexport const DEFAULT_PROMO_TRIGGER: Required<PromoTrigger> = { delayMs: 5000, pageviews: 2, scrollPercent: 40 }\n\nexport function resolveTrigger(t?: PromoTrigger | null): Required<PromoTrigger> {\n return { ...DEFAULT_PROMO_TRIGGER, ...(t ?? {}) }\n}\n\n/** Scroll depth as a percentage of the scrollable height (UI measure only). */\nexport function scrollDepthPercent(scrollY: number, viewportHeight: number, docHeight: number): number {\n const scrollable = docHeight - viewportHeight\n if (!(scrollable > 0)) return 0\n return Math.min(100, Math.max(0, (scrollY / scrollable) * 100))\n}\n\n/** Only same-site paths are valid CTAs (`/colecciones/x`). Anything else (`//evil`, `https:`, `javascript:`) is dropped. */\nexport function safeInternalPath(href: string | null | undefined): string | null {\n if (!href || !href.startsWith('/') || href.startsWith('//') || /[\\\\\\u0000-\\u001f]/.test(href)) return null\n return href\n}\n","import { useEffect, useId, useMemo, useRef, useState, type ReactNode } from 'react'\nimport {\n formatMoney,\n promotionMessages,\n rewardLabel,\n formatAvailabilityMessage,\n type PromotionAnnouncement,\n type PromotionMessages,\n} from '@asteby/storefront'\nimport { useModalDialog, type DialogCloseReason } from '../dialog.js'\nimport { useResolvedLocale } from '../locale.js'\nimport {\n browserSeenStore,\n frequencyAllows,\n isCrawler,\n isExcludedPath,\n memorySeenStore,\n parseSeen,\n PROMO_COOKIE,\n resolveTrigger,\n safeInternalPath,\n scrollDepthPercent,\n serializeSeen,\n withinWindow,\n type PathPattern,\n type PromoFrequency,\n type PromoSeenStore,\n type PromoTrigger,\n} from './promo-logic.js'\n\nexport type PromoPopupEvent =\n | { type: 'view'; id: string; version: number; via: 'delay' | 'pageviews' | 'scroll' }\n | { type: 'close'; id: string; how: 'esc' | 'x' | 'backdrop' }\n | { type: 'cta'; id: string }\n\nexport type PromoPopupProps = {\n /** Content as data from `GET /storefront/promotions`. `null`/absent: nothing happens. Amounts are the engine's. */\n announcement: PromotionAnnouncement | null | undefined\n /** Current path, from the router (so route changes are seen). Default: `location.pathname`. */\n pathname?: string\n /** Paths where the pop-up never appears. Default: cart, payment, thanks, account, auth, legal, editor. */\n excludePaths?: readonly PathPattern[]\n /** Overrides `announcement.frequency` (default `days`). */\n frequency?: PromoFrequency\n /** Window for `days` (default `announcement.frequency_days` or 7). */\n frequencyDays?: number\n /** Overrides `announcement.trigger`. Defaults: 5 s, second page view or 40 % scroll, whichever comes first. */\n trigger?: PromoTrigger\n locale?: string\n messages?: Partial<PromotionMessages>\n /** Where \"already seen\" is remembered. Default: cookie `sf_promo` (7 days) or sessionStorage for `session`. */\n store?: PromoSeenStore\n /** Analytics hook: view, close (how) and CTA. No personal data. */\n onEvent?: (event: PromoPopupEvent) => void\n /** Renders the CTA (use your router's `Link`). Default: a plain `<a href>`. */\n renderCta?: (props: { href: string; label: string; onClick: () => void; className?: string }) => ReactNode\n /** Test hooks. */\n now?: () => number\n userAgent?: string\n webdriver?: boolean\n className?: string\n}\n\nconst PAGEVIEW_KEY = 'sf_promo_pv'\n\n/**\n * Entry pop-up mechanism. The CONTENT is data (`announcement`); this component only decides when to show it.\n *\n * - Client only: it renders nothing on the server and on the first client render, so SSR/SEO and hydration\n * are untouched and the pop-up never delays the LCP.\n * - Shown at most per `frequency` (session / every N days / once per campaign version), never on excluded\n * paths, to crawlers or outside the campaign dates.\n * - Accessible: native `<dialog>` (focus trap, inert background), Esc, a 44x44 close button, labelled title,\n * focus returns to where it was, no entry animation (nothing to disable for `prefers-reduced-motion`).\n */\nexport function PromoPopup(props: PromoPopupProps) {\n const { announcement, messages, onEvent, className } = props\n const locale = useResolvedLocale(props.locale)\n const [open, setOpen] = useState(false)\n const shown = useRef<string | null>(null)\n const titleId = useId()\n const m = useMemo(() => promotionMessages(locale, messages), [locale, messages])\n const storeRef = useRef<PromoSeenStore | null>(props.store ?? null)\n const live = useRef(props)\n live.current = props\n const pathname = props.pathname ?? (typeof window !== 'undefined' ? window.location.pathname : '/')\n const excluded = isExcludedPath(pathname, props.excludePaths)\n\n const annKey = announcement ? `${announcement.id}:${announcement.version}` : null\n\n // Session page views: one per path, counted once even if the effect re-runs (StrictMode).\n const views = useRef(0)\n const lastCounted = useRef<string | null>(null)\n useEffect(() => {\n if (lastCounted.current === pathname) return\n lastCounted.current = pathname\n try {\n views.current = Number(sessionStorage.getItem(PAGEVIEW_KEY) ?? '0') + 1\n sessionStorage.setItem(PAGEVIEW_KEY, String(views.current))\n } catch {\n views.current += 1\n }\n }, [pathname])\n\n useEffect(() => {\n const announcement = live.current.announcement\n if (!announcement || excluded) {\n setOpen(false)\n return\n }\n const now = () => (live.current.now ? live.current.now() : Date.now())\n const ua = live.current.userAgent ?? (typeof navigator !== 'undefined' ? navigator.userAgent : '')\n const driver = live.current.webdriver ?? (typeof navigator !== 'undefined' ? (navigator as Navigator & { webdriver?: boolean }).webdriver : false)\n if (isCrawler(ua, driver)) return\n if (!withinWindow(announcement, now())) return\n const frequency = live.current.frequency ?? announcement.frequency ?? 'days'\n const store = (storeRef.current ??= typeof window !== 'undefined' ? browserSeenStore() : memorySeenStore())\n const cookieName = PROMO_COOKIE\n const allowed = () =>\n frequencyAllows({\n id: announcement.id,\n version: announcement.version,\n frequency,\n days: live.current.frequencyDays ?? announcement.frequency_days ?? 7,\n seen: parseSeen(store.get(cookieName)),\n now: now(),\n })\n if (!allowed()) return\n if (shown.current === annKey) return\n\n const trigger = resolveTrigger({ ...(announcement.trigger ? {\n ...(announcement.trigger.delay_ms !== undefined ? { delayMs: announcement.trigger.delay_ms } : {}),\n ...(announcement.trigger.pageviews !== undefined ? { pageviews: announcement.trigger.pageviews } : {}),\n ...(announcement.trigger.scroll_percent !== undefined ? { scrollPercent: announcement.trigger.scroll_percent } : {}),\n } : {}), ...(live.current.trigger ?? {}) })\n\n let done = false\n const cleanups: (() => void)[] = []\n const fire = (via: 'delay' | 'pageviews' | 'scroll') => {\n if (done || !allowed()) return\n done = true\n cleanups.forEach((c) => c())\n shown.current = annKey\n store.set(cookieName, serializeSeen({ id: announcement.id, version: announcement.version, at: now() }), frequency === 'session' ? { session: true } : { days: frequency === 'campaign' ? 365 : (live.current.frequencyDays ?? announcement.frequency_days ?? 7) })\n setOpen(true)\n live.current.onEvent?.({ type: 'view', id: announcement.id, version: announcement.version, via })\n }\n\n if (trigger.pageviews !== false && views.current >= trigger.pageviews) {\n // After the effect flush, never in the first paint.\n const t = setTimeout(() => fire('pageviews'), 0)\n cleanups.push(() => clearTimeout(t))\n }\n if (trigger.delayMs !== false) {\n const t = setTimeout(() => fire('delay'), trigger.delayMs)\n cleanups.push(() => clearTimeout(t))\n }\n if (trigger.scrollPercent !== false && typeof window !== 'undefined') {\n const target = trigger.scrollPercent\n const onScroll = () => {\n const depth = scrollDepthPercent(window.scrollY, window.innerHeight, document.documentElement.scrollHeight)\n if (depth >= target) fire('scroll')\n }\n window.addEventListener('scroll', onScroll, { passive: true })\n cleanups.push(() => window.removeEventListener('scroll', onScroll))\n }\n return () => {\n done = true\n cleanups.forEach((c) => c())\n }\n }, [annKey, excluded])\n\n const close = (how: DialogCloseReason) => {\n setOpen(false)\n if (announcement && how !== 'action') onEvent?.({ type: 'close', id: announcement.id, how })\n }\n const { ref, onBackdropClick } = useModalDialog({ open, onClose: close })\n\n if (!announcement || !open || excluded) return null\n\n const cta = safeInternalPath(announcement.cta_url)\n const ctaLabel = announcement.cta_label\n const limited = !!announcement.ends_at\n let validUntil: string | null = null\n if (announcement.ends_at) {\n try {\n validUntil = formatAvailabilityMessage(m['popup.valid_until'], {\n date: new Intl.DateTimeFormat(locale, { dateStyle: 'long' }).format(new Date(announcement.ends_at)),\n })\n } catch {\n validUntil = null\n }\n }\n const onCta = () => {\n onEvent?.({ type: 'cta', id: announcement.id })\n setOpen(false)\n }\n\n return (\n <dialog ref={ref} className={className} aria-labelledby={titleId} data-sf-promo-popup onClick={onBackdropClick}>\n <div data-sf-promo-body>\n <button\n type=\"button\"\n onClick={() => close('x')}\n aria-label={m['popup.close']}\n data-sf-promo-close\n style={{ minWidth: 44, minHeight: 44 }}\n >\n <span aria-hidden=\"true\">×</span>\n </button>\n {limited && <p data-sf-promo-eyebrow>{m['popup.limited_time']}</p>}\n <h2 id={titleId} tabIndex={-1} data-sf-autofocus>\n {announcement.headline}\n </h2>\n {announcement.subheadline && <p>{announcement.subheadline}</p>}\n {announcement.image && <img src={announcement.image.url} alt={announcement.image.alt} loading=\"lazy\" decoding=\"async\" />}\n {(announcement.tiers.length > 0 || announcement.free_shipping_threshold) && (\n <ul data-sf-promo-tiers>\n {announcement.tiers.map((t) => (\n <li key={`${t.threshold.amount}-${rewardLabel(t.reward, locale, messages)}`}>\n {formatAvailabilityMessage(m['popup.discount_tier'], {\n reward: rewardLabel(t.reward, locale, messages),\n threshold: formatMoney(t.threshold, locale, { withoutTrailingZeros: true }),\n })}\n </li>\n ))}\n {announcement.free_shipping_threshold && (\n <li>\n {Number.parseFloat(announcement.free_shipping_threshold.amount) === 0\n ? m['popup.free_shipping_always']\n : formatAvailabilityMessage(m['popup.free_shipping_min'], {\n threshold: formatMoney(announcement.free_shipping_threshold, locale, { withoutTrailingZeros: true }),\n })}\n </li>\n )}\n </ul>\n )}\n {cta && ctaLabel &&\n (props.renderCta ? (\n props.renderCta({ href: cta, label: ctaLabel, onClick: onCta })\n ) : (\n <a href={cta} onClick={onCta} data-sf-promo-cta style={{ minHeight: 44, display: 'inline-flex', alignItems: 'center' }}>\n {ctaLabel}\n </a>\n ))}\n <p data-sf-promo-legal>\n {announcement.legal_text}\n {validUntil && <> {validUntil}.</>}\n </p>\n </div>\n </dialog>\n )\n}\n","import { useEffect, useId, useMemo, useState, type CSSProperties } from 'react'\nimport { promoProgressView, promotionMessages, type CartPromotions, type PromotionMessages } from '@asteby/storefront'\nimport { prefersReducedMotion } from '../dialog.js'\nimport { useResolvedLocale } from '../locale.js'\n\nexport type ProgressBarProps = {\n /** Visible text (also `aria-valuetext`). */\n text: string\n /** 0-100, from the server. */\n percent: number\n label: string\n showBar?: boolean\n milestones?: { id: string; label: string; threshold: string; reached: boolean; position: number }[]\n state: string\n kind: 'discount' | 'shipping'\n className?: string\n}\n\n/**\n * One accessible bar: color is never the only signal (text states the remaining amount; milestones are text).\n * The fill width is the server percentage; the transition is off for `prefers-reduced-motion`.\n */\nexport function PromoProgressBar({ text, percent, label, showBar = true, milestones, state, kind, className }: ProgressBarProps) {\n const id = useId()\n const [reduced, setReduced] = useState(false)\n useEffect(() => setReduced(prefersReducedMotion()), [])\n const fill: CSSProperties = { width: `${percent}%`, transition: reduced ? 'none' : 'width 600ms ease' }\n return (\n <div className={className} data-sf-promo-bar={kind} data-state={state}>\n <p id={`${id}-t`} aria-live=\"polite\" data-sf-promo-bar-text>\n {text}\n </p>\n {showBar && (\n <div role=\"progressbar\" aria-label={label} aria-describedby={`${id}-t`} aria-valuemin={0} aria-valuemax={100} aria-valuenow={percent} aria-valuetext={text} data-sf-promo-track>\n <div style={fill} data-sf-promo-fill />\n </div>\n )}\n {showBar && milestones && milestones.length > 0 && (\n <ul data-sf-promo-milestones>\n {milestones.map((t) => (\n <li key={t.id} data-reached={t.reached} style={{ left: `${t.position}%` }}>\n <span>{t.label}</span> <span>{t.threshold}</span>\n {t.reached && <span data-sf-sr-only style={{ position: 'absolute', width: 1, height: 1, overflow: 'hidden', clip: 'rect(0 0 0 0)' }}> ✓</span>}\n </li>\n ))}\n </ul>\n )}\n </div>\n )\n}\n\nexport type CartProgressBarsProps = {\n /** `Cart.promotions` exactly as the server sent it. */\n promotions: CartPromotions | null | undefined\n locale?: string\n messages?: Partial<PromotionMessages>\n /** Show the tier milestones under the discount bar. Default true. */\n showMilestones?: boolean\n className?: string\n}\n\n/**\n * Automatic-discount bar and free-shipping bar of the cart (or drawer). Pure view of `Cart.promotions`:\n * `remaining`, `percent` and `reached` come from OPS; the shop never subtracts or divides.\n * Renders nothing without promotions.\n */\nexport function CartProgressBars({ promotions, locale: localeProp, messages, showMilestones = true, className }: CartProgressBarsProps) {\n const locale = useResolvedLocale(localeProp)\n const view = useMemo(() => promoProgressView(promotions, locale, messages), [promotions, locale, messages])\n const m = promotionMessages(locale, messages)\n if (!view.discount && !view.shipping) return null\n return (\n <div className={className} data-sf-promo-bars>\n {view.discount && (\n <PromoProgressBar\n kind=\"discount\"\n state={view.discount.state}\n text={view.discount.text}\n percent={view.discount.percent}\n label={m['bar.discount']}\n {...(showMilestones ? { milestones: view.discount.tiers } : {})}\n />\n )}\n {view.shipping && (\n <PromoProgressBar kind=\"shipping\" state={view.shipping.state} text={view.shipping.text} percent={view.shipping.percent} label={m['bar.shipping']} showBar={view.shipping.showBar} />\n )}\n </div>\n )\n}\n","import { useId, useMemo, useRef, useState, type ReactNode } from 'react'\nimport {\n formatAvailabilityMessage,\n formatMoney,\n promoProgressView,\n promotionMessages,\n rewardLabel,\n type CartPromotions,\n type CartRecommendation,\n type Money,\n type PromotionMessages,\n} from '@asteby/storefront'\nimport { useModalDialog, type DialogCloseReason } from '../dialog.js'\nimport { useResolvedLocale } from '../locale.js'\nimport { PromoProgressBar } from './cart-progress-bars.js'\n\nexport type CrossSellAddResult = void | { ok?: boolean; error?: 'sold_out' | 'failed' }\n\nexport type CrossSellModalProps = {\n open: boolean\n /** `Cart.promotions` of the CURRENT cart; after an add, pass the new cart's and the modal updates. */\n promotions: CartPromotions | null | undefined\n recommendations: CartRecommendation[]\n /** Total saved so far, from the server (`cart.cost.discount`). */\n totalSaved: Money\n /**\n * The shop adds the variant (server action / fetcher) and then re-renders with the new `promotions` and\n * `recommendations`. Return `{ error: 'sold_out' }` for a 409 so the card tells the buyer to pick another flavor.\n */\n onAdd: (input: { productId: string; variantId: string; recommendation: CartRecommendation }) => Promise<CrossSellAddResult> | CrossSellAddResult\n /** \"Continuar al pago\". The shop navigates to payment. */\n onContinue: () => void\n /** \"Continuar sin agregar\", X, Esc or backdrop. The shop marks the modal as seen and navigates to payment. */\n onSkip: (reason: 'skip' | 'esc' | 'x' | 'backdrop') => void\n locale?: string\n messages?: Partial<PromotionMessages>\n /** Renders a card image (use your `Image`). Default: a lazy `<img>` of the featured image. */\n renderImage?: (rec: CartRecommendation) => ReactNode\n className?: string\n}\n\n/**\n * Pre-payment upsell modal (\"Completa tu compra y desbloquea más\").\n *\n * Mechanism only: the shop decides when to open it (once per cart, JS only) and what \"continue\" means.\n * Every number comes from the server: remaining amount, rewards, progress, total saved, and the\n * \"Desbloquea tu 5 %\" tag (`unlocks`). Add errors stay on their card; the rest stays usable.\n * Accessible: native dialog, labelled title, focus starts on the title (not on a buy button), Esc = skip,\n * `role=\"progressbar\"` with spoken remaining amount, level changes announced via `aria-live`.\n */\nexport function CrossSellModal(props: CrossSellModalProps) {\n const { open, promotions, recommendations, totalSaved, onAdd, onContinue, onSkip, messages, renderImage, className } = props\n const locale = useResolvedLocale(props.locale)\n const m = promotionMessages(locale, messages)\n const titleId = useId()\n const descId = useId()\n const view = useMemo(() => promoProgressView(promotions, locale, messages).discount, [promotions, locale, messages])\n const { ref, onBackdropClick } = useModalDialog({ open, onClose: (r: DialogCloseReason) => onSkip(r === 'esc' ? 'esc' : r === 'x' ? 'x' : 'backdrop') })\n const nextTier = promotions?.next_tier ?? null\n const max = !!promotions && !nextTier && !!promotions.applied\n\n if (!open) return null\n\n const f = formatAvailabilityMessage\n return (\n <dialog ref={ref} className={className} aria-labelledby={titleId} aria-describedby={descId} onClick={onBackdropClick} data-sf-crosssell>\n <div data-sf-crosssell-body>\n <button type=\"button\" onClick={() => onSkip('x')} aria-label={m['popup.close']} data-sf-crosssell-close style={{ minWidth: 44, minHeight: 44 }}>\n <span aria-hidden=\"true\">×</span>\n </button>\n <p data-sf-crosssell-eyebrow>{m['upsell.subtitle']}</p>\n <h2 id={titleId} tabIndex={-1} data-sf-autofocus>\n {m['upsell.title']}\n </h2>\n\n <section id={descId} data-sf-crosssell-level aria-live=\"polite\">\n {nextTier ? (\n <>\n <p data-sf-crosssell-next>{m['upsell.next_level']}</p>\n <p>\n <strong>{f(m['upsell.you_miss'], { remaining: formatMoney(nextTier.remaining, locale, { withoutTrailingZeros: true }) })}</strong>\n </p>\n <p>{f(m['upsell.unlocks_reward'], { reward: rewardLabel(nextTier.reward, locale, messages) })}</p>\n </>\n ) : max && promotions?.applied ? (\n <p data-sf-crosssell-best>{f(m['upsell.best'], { reward: rewardLabel(promotions.applied.reward, locale, messages) })}</p>\n ) : null}\n {promotions?.applied && nextTier && <p data-sf-crosssell-unlocked>{f(m['upsell.unlocked'], { reward: rewardLabel(promotions.applied.reward, locale, messages) })}</p>}\n </section>\n\n <dl data-sf-crosssell-summary>\n <div>\n <dt>{m['upsell.total_saved']}</dt>\n <dd>{formatMoney(totalSaved, locale, { withoutTrailingZeros: true })}</dd>\n </div>\n <div>\n <dt>{m['upsell.current_benefit']}</dt>\n <dd>{rewardLabel(promotions?.applied?.reward ?? { kind: 'percent', percent: '0' }, locale, messages)}</dd>\n </div>\n <div>\n <dt>{m['upsell.status']}</dt>\n <dd>{promotions?.applied ? m['upsell.status_active'] : m['upsell.status_pending']}</dd>\n </div>\n </dl>\n\n {view && <PromoProgressBar kind=\"discount\" state={view.state} text={view.text} percent={view.percent} label={m['bar.discount']} milestones={view.tiers} />}\n {nextTier && (\n <p data-sf-crosssell-next-reward>\n {f(m['upsell.next_reward'], {\n reward: rewardLabel(nextTier.reward, locale, messages),\n threshold: formatMoney(nextTier.threshold, locale, { withoutTrailingZeros: true }),\n })}\n </p>\n )}\n\n {!max && recommendations.length > 0 && (\n <section aria-label={m['upsell.suggestions']}>\n <h3>{m['upsell.suggestions']}</h3>\n <ul data-sf-crosssell-cards>\n {recommendations.map((rec) => (\n <RecommendationCard key={rec.product.id} rec={rec} onAdd={onAdd} locale={locale} messages={messages} {...(renderImage ? { renderImage } : {})} />\n ))}\n </ul>\n </section>\n )}\n\n <p data-sf-crosssell-footer>{m['upsell.footer']}</p>\n <div data-sf-crosssell-actions>\n <button type=\"button\" onClick={() => onSkip('skip')} style={{ minHeight: 44 }} data-sf-crosssell-skip>\n {m['upsell.skip']}\n </button>\n <button type=\"button\" onClick={onContinue} style={{ minHeight: 44 }} data-sf-crosssell-continue>\n {m['upsell.continue']} →\n </button>\n </div>\n </div>\n </dialog>\n )\n}\n\nfunction RecommendationCard({\n rec,\n onAdd,\n locale,\n messages,\n renderImage,\n}: {\n rec: CartRecommendation\n onAdd: CrossSellModalProps['onAdd']\n locale?: string | undefined\n messages?: Partial<PromotionMessages> | undefined\n renderImage?: (rec: CartRecommendation) => ReactNode\n}) {\n const m = promotionMessages(locale, messages)\n const selectId = useId()\n const [variantId, setVariantId] = useState(rec.variants.some((v) => v.id === rec.default_variant_id) ? rec.default_variant_id : (rec.variants[0]?.id ?? ''))\n const [busy, setBusy] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const inFlight = useRef(false)\n const variant = rec.variants.find((v) => v.id === variantId) ?? rec.variants[0]\n const image = rec.product.featured_image\n\n // Variants refreshed by the server: keep the selection valid.\n if (variant && variant.id !== variantId) setVariantId(variant.id)\n if (!variant) return null\n\n async function add() {\n if (inFlight.current || !variant) return // double click: one add\n inFlight.current = true\n setBusy(true)\n setError(null)\n try {\n const r = await onAdd({ productId: rec.product.id, variantId: variant.id, recommendation: rec })\n if (r && (r.ok === false || r.error)) setError(r.error === 'sold_out' ? m['upsell.sold_out'] : m['upsell.error'])\n } catch {\n setError(m['upsell.error'])\n } finally {\n inFlight.current = false\n setBusy(false)\n }\n }\n\n return (\n <li data-sf-crosssell-card>\n {renderImage ? renderImage(rec) : image ? <img src={image.url} alt={image.alt ?? rec.product.title} width={160} loading=\"lazy\" decoding=\"async\" /> : null}\n {rec.unlocks && <p data-sf-crosssell-unlocks>{formatAvailabilityMessage(m['upsell.unlocks_badge'], { reward: rewardLabel(rec.unlocks.reward, locale, messages) })}</p>}\n <p data-sf-crosssell-name>{rec.product.title}</p>\n <p data-sf-crosssell-price>{formatMoney(variant.price, locale, { withoutTrailingZeros: true })}</p>\n {rec.variants.length > 1 && (\n <>\n <label htmlFor={selectId}>{m['upsell.flavor']}</label>\n <select id={selectId} value={variant.id} onChange={(e) => setVariantId(e.target.value)} style={{ minHeight: 44 }}>\n {rec.variants.map((v) => (\n <option key={v.id} value={v.id}>\n {v.title}\n </option>\n ))}\n </select>\n </>\n )}\n <button type=\"button\" onClick={() => void add()} aria-busy={busy} aria-disabled={busy} style={{ minHeight: 44 }} data-sf-crosssell-add>\n {busy ? m['upsell.adding'] : m['upsell.add']}\n </button>\n {error && (\n <p role=\"alert\" data-sf-crosssell-error>\n {error}\n </p>\n )}\n </li>\n )\n}\n","import { useEffect, useId, useRef, useState } from 'react'\nimport { useResolvedLocale } from '../locale.js'\nimport { NOTE_MAX_LENGTH, formatAvailabilityMessage, normalizeNote, promotionMessages, type PromotionMessages } from '@asteby/storefront'\n\nexport type OrderNoteStatus = 'idle' | 'dirty' | 'saving' | 'saved' | 'error'\n\nexport type OrderNoteProps = {\n /** Saved note (`Cart.note`). */\n value: string | null | undefined\n /** Persists the note (`client.carts.setNote` through the shop action). Must resolve once stored. Throw to show the error. */\n onSave: (note: string | null) => Promise<void> | void\n /** Autosave debounce in ms. Default 800. `false`: save only on blur / button. */\n debounceMs?: number | false\n /**\n * No-JS form: `<form method=\"post\" action>` with a hidden `intent` and a \"Guardar nota\" button, hidden once the\n * component hydrates (the autosave takes over). The shop's route action handles it.\n */\n form?: { action?: string; method?: 'post'; intent?: string; intentName?: string }\n locale?: string\n messages?: Partial<PromotionMessages>\n className?: string\n maxLength?: number\n}\n\n/**\n * \"Añadir nota\" for the order. A real `<textarea>` with a visible label; the server enforces 1000 characters and\n * plain text. With JavaScript it autosaves (debounced, on blur) and announces \"Guardada\"; without it, the form posts.\n */\nexport function OrderNote({ value, onSave, debounceMs = 800, form, locale: localeProp, messages, className, maxLength = NOTE_MAX_LENGTH }: OrderNoteProps) {\n const locale = useResolvedLocale(localeProp)\n const m = promotionMessages(locale, messages)\n const id = useId()\n const [text, setText] = useState(value ?? '')\n const [status, setStatus] = useState<OrderNoteStatus>('idle')\n const [hydrated, setHydrated] = useState(false)\n const saved = useRef(normalizeNote(value))\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null)\n const seq = useRef(0)\n const latest = useRef({ text, onSave })\n latest.current = { text, onSave }\n\n useEffect(() => setHydrated(true), [])\n // A new server value (another tab, cart refresh) replaces the field unless the buyer is typing.\n useEffect(() => {\n if (status === 'dirty' || status === 'saving') return\n const incoming = value ?? ''\n if (normalizeNote(incoming) !== normalizeNote(latest.current.text)) setText(incoming)\n saved.current = normalizeNote(value)\n }, [value])\n useEffect(() => () => void (timer.current && clearTimeout(timer.current)), [])\n\n async function flush() {\n if (timer.current) clearTimeout(timer.current)\n timer.current = null\n const next = normalizeNote(latest.current.text)\n if (next === saved.current) {\n setStatus((s) => (s === 'dirty' ? 'idle' : s))\n return\n }\n const mine = ++seq.current\n setStatus('saving')\n try {\n await latest.current.onSave(next)\n if (mine !== seq.current) return // a newer save superseded this one\n saved.current = next\n setStatus('saved')\n } catch {\n if (mine === seq.current) setStatus('error')\n }\n }\n\n function onChange(v: string) {\n setText(v.slice(0, maxLength))\n setStatus('dirty')\n if (debounceMs !== false) {\n if (timer.current) clearTimeout(timer.current)\n timer.current = setTimeout(() => void flush(), debounceMs)\n }\n }\n\n const statusText = status === 'saving' ? m['note.saving'] : status === 'saved' ? m['note.saved'] : status === 'error' ? m['note.error'] : ''\n const field = (\n <>\n <label htmlFor={id}>{m['note.label']}</label>\n <textarea\n id={id}\n name=\"note\"\n value={text}\n maxLength={maxLength}\n placeholder={m['note.placeholder']}\n onChange={(e) => onChange(e.target.value)}\n onBlur={() => void flush()}\n aria-describedby={`${id}-s`}\n rows={3}\n />\n <p id={`${id}-c`} data-sf-note-counter>\n {formatAvailabilityMessage(m['note.counter'], { n: text.length, max: maxLength })}\n </p>\n <p id={`${id}-s`} role={status === 'error' ? 'alert' : 'status'} aria-live=\"polite\" data-sf-note-status={status}>\n {statusText}\n </p>\n </>\n )\n\n if (!form) return <div className={className} data-sf-order-note>{field}</div>\n return (\n <form method={form.method ?? 'post'} {...(form.action ? { action: form.action } : {})} className={className} data-sf-order-note onSubmit={(e) => hydrated && e.preventDefault()}>\n <input type=\"hidden\" name={form.intentName ?? 'intent'} value={form.intent ?? 'note'} />\n {field}\n {!hydrated && <button type=\"submit\">{m['note.save']}</button>}\n </form>\n )\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asteby/storefront-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.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": {
|
|
@@ -38,6 +38,17 @@
|
|
|
38
38
|
}
|
|
39
39
|
},
|
|
40
40
|
"./cart.css": "./dist/cart.css",
|
|
41
|
+
"./checkout": {
|
|
42
|
+
"import": {
|
|
43
|
+
"types": "./dist/checkout.d.ts",
|
|
44
|
+
"default": "./dist/checkout.js"
|
|
45
|
+
},
|
|
46
|
+
"require": {
|
|
47
|
+
"types": "./dist/checkout.d.cts",
|
|
48
|
+
"default": "./dist/checkout.cjs"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"./checkout.css": "./dist/checkout.css",
|
|
41
52
|
"./fitment": {
|
|
42
53
|
"import": {
|
|
43
54
|
"types": "./dist/fitment.d.ts",
|
|
@@ -110,7 +121,7 @@
|
|
|
110
121
|
"react": "^19.0.0"
|
|
111
122
|
},
|
|
112
123
|
"dependencies": {
|
|
113
|
-
"@asteby/storefront": "0.
|
|
124
|
+
"@asteby/storefront": "0.8.0"
|
|
114
125
|
},
|
|
115
126
|
"devDependencies": {
|
|
116
127
|
"@testing-library/react": "^16.3.2",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/dialog.tsx","../src/locale.ts"],"names":[],"mappings":"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","file":"chunk-2WZIWIBW.js","sourcesContent":["import { useCallback, useEffect, useRef, type RefObject } from 'react'\n\nexport type DialogCloseReason = 'esc' | 'x' | 'backdrop' | 'action'\n\ntype UseModalDialogOptions = {\n open: boolean\n /** Called for Esc and backdrop clicks. The owner decides whether to close. */\n onClose: (reason: DialogCloseReason) => void\n /** Element focused when the dialog opens. Default: the element marked `data-sf-autofocus`, else the dialog itself. */\n initialFocus?: RefObject<HTMLElement | null>\n}\n\n/**\n * Native `<dialog>` wiring: `showModal()` (focus trap and inert background come from the browser),\n * Esc and backdrop clicks routed to `onClose`, and focus restored to the opener on close.\n * Falls back to the `open` attribute where `showModal` does not exist (old browsers, some test DOMs)\n * with an Esc key handler.\n */\nexport function useModalDialog({ open, onClose, initialFocus }: UseModalDialogOptions) {\n const ref = useRef<HTMLDialogElement | null>(null)\n const opener = useRef<Element | null>(null)\n const onCloseRef = useRef(onClose)\n onCloseRef.current = onClose\n\n useEffect(() => {\n const el = ref.current\n if (!el || !open) return\n opener.current = typeof document !== 'undefined' ? document.activeElement : null\n const hasModal = typeof el.showModal === 'function'\n if (!el.open) {\n if (hasModal) {\n try {\n el.showModal()\n } catch {\n el.setAttribute('open', '')\n }\n } else el.setAttribute('open', '')\n }\n const target = initialFocus?.current ?? el.querySelector<HTMLElement>('[data-sf-autofocus]') ?? el\n if (!target.hasAttribute('tabindex') && target === el) el.setAttribute('tabindex', '-1')\n target.focus({ preventScroll: true })\n\n const onCancel = (e: Event) => {\n e.preventDefault() // we close through state so the owner can navigate/track first\n onCloseRef.current('esc')\n }\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n e.preventDefault()\n onCloseRef.current('esc')\n }\n }\n el.addEventListener('cancel', onCancel)\n if (!hasModal) el.addEventListener('keydown', onKey)\n return () => {\n el.removeEventListener('cancel', onCancel)\n el.removeEventListener('keydown', onKey)\n if (el.open) {\n if (typeof el.close === 'function') {\n try {\n el.close()\n } catch {\n el.removeAttribute('open')\n }\n } else el.removeAttribute('open')\n }\n const back = opener.current\n if (back instanceof HTMLElement && back.isConnected) back.focus({ preventScroll: true })\n }\n }, [open])\n\n /** Click on the backdrop (the dialog element itself, outside its content box). */\n const onBackdropClick = useCallback((e: { target: EventTarget; currentTarget: EventTarget }) => {\n if (e.target === e.currentTarget) onCloseRef.current('backdrop')\n }, [])\n\n return { ref, onBackdropClick }\n}\n\n/** `prefers-reduced-motion: reduce`, read after hydration (false on the server). */\nexport function prefersReducedMotion(): boolean {\n try {\n return typeof window !== 'undefined' && !!window.matchMedia?.('(prefers-reduced-motion: reduce)').matches\n } catch {\n return false\n }\n}\n","import { useOptionalStorefront } from './context.js'\n\n/** Locale of a component: its own prop, else the `StorefrontProvider`'s, else `es-MX`. */\nexport function useResolvedLocale(locale?: string | null): string {\n const sf = useOptionalStorefront()\n return locale ?? sf?.locale ?? 'es-MX'\n}\n"]}
|