@asteby/storefront-react 0.20.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cart.css CHANGED
@@ -32,8 +32,9 @@
32
32
  [data-sf-cart-lines] { margin: 0; padding: 0 1.25rem; list-style: none; }
33
33
  [data-sf-cart-line] { display: flex; gap: .75rem; padding: 1rem 0; }
34
34
  [data-sf-cart-line] + [data-sf-cart-line] { border-top: 1px solid var(--border); }
35
- [data-sf-cart-image] { display: block; flex: none; width: 5rem; height: 5rem; overflow: hidden; border-radius: var(--radius, .375rem); background: color-mix(in srgb, var(--foreground) 6%, transparent); }
36
- [data-sf-cart-image] img { display: block; width: 100%; height: 100%; object-fit: contain; padding: .25rem; box-sizing: border-box; }
35
+ [data-sf-cart-image] { display: block; flex: none; width: 5rem; height: 5rem; }
36
+ [data-sf-cart-image]:not(:has([data-sf-thumb])) { overflow: hidden; border-radius: var(--radius, .375rem); background: color-mix(in srgb, var(--foreground) 6%, transparent); }
37
+ [data-sf-cart-image]:not(:has([data-sf-thumb])) img { display: block; width: 100%; height: 100%; object-fit: contain; padding: .25rem; box-sizing: border-box; }
37
38
  [data-sf-cart-line-main] { display: flex; flex: 1; flex-direction: column; min-width: 0; }
38
39
  [data-sf-cart-line-head] { display: flex; justify-content: space-between; gap: .75rem; }
39
40
  [data-sf-cart-line-text] { min-width: 0; }
@@ -82,3 +83,145 @@
82
83
  }
83
84
  @keyframes sf-cart-pulse { 50% { opacity: .65; } }
84
85
  [data-sf-cart-notices]:not([data-sf-cart-drawer] *):not(:empty) { position: fixed; inset: auto 1rem 1rem auto; z-index: 60; max-width: 24rem; background: var(--card, var(--background)); color: var(--foreground); }
86
+
87
+ /*
88
+ * <ProductThumb>, <ProductPreview>, <ProductThumbStack>: la foto del producto y la línea de producto de la tienda.
89
+ * CSS plano sobre los tokens de la tienda (--foreground, --background, --border, --muted-foreground, --radius); en tema
90
+ * oscuro la superficie y el brillo salen de color-mix, sin colores escritos aquí. Va incluido en cart.css, checkout.css,
91
+ * account.css y ui.css (misma regla, un solo origen: src/ui/product-preview.css).
92
+ */
93
+ [data-sf-thumb] {
94
+ --_surface: color-mix(in srgb, var(--foreground, #111) 5%, var(--background, #fff));
95
+ position: relative;
96
+ display: inline-block;
97
+ flex: none;
98
+ box-sizing: border-box;
99
+ width: var(--sf-preview-size, 3.5rem);
100
+ aspect-ratio: var(--sf-preview-ratio, 1 / 1);
101
+ overflow: hidden;
102
+ border-radius: calc(var(--radius, 0.5rem) * 1.25);
103
+ background: var(--_surface);
104
+ box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--foreground, #111) 9%, transparent);
105
+ vertical-align: middle;
106
+ }
107
+ :root[data-theme="dark"] [data-sf-thumb] { --_surface: color-mix(in srgb, var(--foreground, #fff) 9%, var(--background, #111)); }
108
+ [data-sf-thumb] > img {
109
+ display: block;
110
+ width: 100%;
111
+ height: 100%;
112
+ box-sizing: border-box;
113
+ object-fit: contain;
114
+ padding: 6%;
115
+ transition: transform 220ms cubic-bezier(0.2, 0.7, 0.2, 1), opacity 180ms;
116
+ }
117
+ [data-sf-thumb][data-fit="cover"] > img { object-fit: cover; padding: 0; }
118
+ /* Fotos sobre fondo blanco: se funden con la superficie en tema claro (no hay caja blanca dentro del marco). */
119
+ [data-sf-thumb][data-fit="contain"] > img { mix-blend-mode: multiply; }
120
+ :root[data-theme="dark"] [data-sf-thumb] > img { mix-blend-mode: normal; }
121
+ [data-sf-thumb][data-state="loading"]::before {
122
+ content: "";
123
+ position: absolute;
124
+ inset: 0;
125
+ background: linear-gradient(100deg, transparent 20%, color-mix(in srgb, var(--foreground, #111) 8%, transparent) 50%, transparent 80%);
126
+ background-size: 200% 100%;
127
+ animation: sf-thumb-shimmer 1.4s linear infinite;
128
+ pointer-events: none;
129
+ }
130
+ @keyframes sf-thumb-shimmer { from { background-position: 150% 0; } to { background-position: -50% 0; } }
131
+ [data-sf-thumb][data-zoom]:hover > img { transform: scale(1.07); }
132
+ @media (prefers-reduced-motion: reduce) {
133
+ [data-sf-thumb][data-state="loading"]::before { animation: none; }
134
+ [data-sf-thumb] > img { transition: none; }
135
+ [data-sf-thumb][data-zoom]:hover > img { transform: none; }
136
+ }
137
+
138
+ [data-sf-thumb-fallback] {
139
+ position: absolute;
140
+ inset: 0;
141
+ display: grid;
142
+ place-items: center;
143
+ color: color-mix(in srgb, var(--foreground, #111) 42%, transparent);
144
+ background: linear-gradient(145deg, color-mix(in srgb, var(--foreground, #111) 3%, transparent), color-mix(in srgb, var(--foreground, #111) 9%, transparent));
145
+ }
146
+ [data-sf-thumb-initial] {
147
+ font-size: calc(var(--sf-preview-size, 3.5rem) * 0.36);
148
+ font-weight: 600;
149
+ letter-spacing: 0.02em;
150
+ line-height: 1;
151
+ }
152
+
153
+ [data-sf-thumb-qty] {
154
+ position: absolute;
155
+ top: 0.2rem;
156
+ right: 0.2rem;
157
+ min-width: 1.25rem;
158
+ height: 1.25rem;
159
+ padding: 0 0.3rem;
160
+ box-sizing: border-box;
161
+ display: inline-grid;
162
+ place-items: center;
163
+ border-radius: 999px;
164
+ font-size: 0.7rem;
165
+ font-weight: 700;
166
+ font-variant-numeric: tabular-nums;
167
+ background: var(--sf-summary-qty-bg, color-mix(in srgb, var(--foreground, #111) 78%, transparent));
168
+ color: var(--sf-summary-qty-fg, var(--background, #fff));
169
+ }
170
+
171
+ /* Línea de producto: foto | texto | importe. */
172
+ [data-sf-preview] {
173
+ display: flex;
174
+ align-items: center;
175
+ gap: 0.875rem;
176
+ min-width: 0;
177
+ }
178
+ [data-sf-preview][data-missing] [data-sf-preview-name] { font-style: italic; color: var(--muted-foreground, color-mix(in srgb, currentColor 60%, transparent)); }
179
+ [data-sf-preview-media] { flex: none; display: inline-flex; }
180
+ [data-sf-preview-body] { display: grid; flex: 1 1 auto; gap: 0.125rem; min-width: 0; }
181
+ [data-sf-preview-name] {
182
+ display: -webkit-box;
183
+ -webkit-line-clamp: 2;
184
+ -webkit-box-orient: vertical;
185
+ overflow: hidden;
186
+ overflow-wrap: anywhere;
187
+ font-size: 0.9375rem;
188
+ font-weight: 600;
189
+ line-height: 1.3;
190
+ }
191
+ [data-sf-preview-name] a { color: inherit; text-decoration: none; }
192
+ [data-sf-preview-name] a:hover { text-decoration: underline; }
193
+ [data-sf-preview-variant],
194
+ [data-sf-preview-sku],
195
+ [data-sf-preview-meta] {
196
+ font-size: 0.8125rem;
197
+ line-height: 1.35;
198
+ color: var(--muted-foreground, color-mix(in srgb, currentColor 66%, transparent));
199
+ }
200
+ [data-sf-preview-sku] { font-variant-numeric: tabular-nums; letter-spacing: 0.01em; }
201
+ [data-sf-preview-total] {
202
+ flex: none;
203
+ align-self: flex-start;
204
+ font-size: 0.9375rem;
205
+ font-weight: 700;
206
+ font-variant-numeric: tabular-nums;
207
+ text-align: right;
208
+ }
209
+
210
+ /* Fotos apiladas (tarjeta de pedido). */
211
+ [data-sf-thumb-stack] { display: inline-flex; align-items: center; }
212
+ [data-sf-thumb-stack] > * { box-shadow: 0 0 0 2px var(--card, var(--background, #fff)), inset 0 0 0 1px color-mix(in srgb, var(--foreground, #111) 9%, transparent); }
213
+ [data-sf-thumb-stack] > * + * { margin-inline-start: -0.625rem; }
214
+ [data-sf-thumb-more] {
215
+ display: inline-grid;
216
+ place-items: center;
217
+ flex: none;
218
+ box-sizing: border-box;
219
+ width: var(--sf-preview-size, 2.75rem);
220
+ aspect-ratio: 1 / 1;
221
+ border-radius: calc(var(--radius, 0.5rem) * 1.25);
222
+ font-size: 0.8125rem;
223
+ font-weight: 600;
224
+ font-variant-numeric: tabular-nums;
225
+ color: var(--muted-foreground, color-mix(in srgb, currentColor 66%, transparent));
226
+ background: color-mix(in srgb, var(--foreground, #111) 7%, var(--background, #fff));
227
+ }
package/dist/cart.js CHANGED
@@ -1,4 +1,6 @@
1
1
  import { useModalDialog } from './chunk-ZBH7JYU4.js';
2
+ import { ProductThumb } from './chunk-VHEY5AU3.js';
3
+ import './chunk-6CVKW7F3.js';
2
4
  import { useResolvedLocale } from './chunk-T52H5HL3.js';
3
5
  import './chunk-DQBL2DFJ.js';
4
6
  import { useSyncExternalStore, useCallback, useRef, useEffect, useId } from 'react';
@@ -390,7 +392,7 @@ function DrawerLine({
390
392
  const title = merch.product.title;
391
393
  const href = paths.product?.(merch.product.handle, merch.id);
392
394
  const img = merch.image ?? merch.product.featured_image;
393
- const image = props.renderImage ? props.renderImage(line) : img ? /* @__PURE__ */ jsx("img", { src: img.url, alt: img.alt ?? "", width: 80, height: 80, loading: "lazy", decoding: "async" }) : null;
395
+ const image = props.renderImage ? props.renderImage(line) : /* @__PURE__ */ jsx(ProductThumb, { image: img ?? null, name: title, size: 80, decorative: true });
394
396
  const imageNode = /* @__PURE__ */ jsx("span", { className: cx.image, "data-sf-cart-image": "", children: image });
395
397
  const price = /* @__PURE__ */ jsx("span", { className: cx.price, "data-sf-cart-price": "", children: money(line.cost.total) });
396
398
  const controls = /* @__PURE__ */ jsxs("div", { className: cx.controls, "data-sf-cart-controls": "", children: [
@@ -427,7 +429,7 @@ function PendingLine({ row, props, money, label }) {
427
429
  const { classNames: cx = {} } = props;
428
430
  const isDefault = props.isDefaultVariantTitle ?? defaultIsDefaultVariant;
429
431
  return /* @__PURE__ */ jsxs("li", { className: cx.line, "data-sf-cart-line": "", "data-sf-cart-line-pending": "", "aria-busy": "true", children: [
430
- /* @__PURE__ */ jsx("span", { className: cx.image, "data-sf-cart-image": "", children: row.image ? /* @__PURE__ */ jsx("img", { src: row.image.url, alt: row.image.alt ?? "", width: 80, height: 80, decoding: "async" }) : null }),
432
+ /* @__PURE__ */ jsx("span", { className: cx.image, "data-sf-cart-image": "", children: /* @__PURE__ */ jsx(ProductThumb, { image: row.image ?? null, name: row.title, size: 80, decorative: true }) }),
431
433
  /* @__PURE__ */ jsxs("div", { className: cx.lineMain, "data-sf-cart-line-main": "", children: [
432
434
  /* @__PURE__ */ jsxs("div", { className: cx.lineHead, "data-sf-cart-line-head": "", children: [
433
435
  /* @__PURE__ */ jsxs("div", { className: cx.lineText, "data-sf-cart-line-text": "", children: [
package/dist/cart.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cart-drawer/store.ts","../src/cart-drawer/optimistic.ts","../src/cart-drawer/cart-drawer.tsx"],"names":["listeners","subscribe","useSyncExternalStore","useRef","useEffect","notices"],"mappings":"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","file":"cart.js","sourcesContent":["import { useCallback, useEffect, useRef, useSyncExternalStore } from 'react'\n\n// Estado mínimo del cajón del carrito, fuera de React: cualquier parte de la tienda (cabecera, tarjeta, ficha)\n// lo abre sin pasar props. Es del navegador; en el servidor siempre está cerrado.\n\nlet isOpen = false\nconst listeners = new Set<() => void>()\n\nlet quietOpen = false\n\n/** Marca que el cajón lo abrió una acción optimista (la apertura no mueve el foco al botón de cierre). */\nexport function setQuietOpen(next: boolean) {\n quietOpen = next\n}\n\nexport function isQuietOpen() {\n return quietOpen\n}\n\nfunction set(next: boolean) {\n if (isOpen === next) return\n isOpen = next\n if (!next) quietOpen = false\n for (const l of [...listeners]) l()\n}\n\nconst subscribe = (l: () => void) => {\n listeners.add(l)\n return () => {\n listeners.delete(l)\n }\n}\n\n/** Abre el cajón del carrito desde cualquier parte. */\nexport function openCartDrawer() {\n set(true)\n}\n\n/** Cierra el cajón del carrito. */\nexport function closeCartDrawer() {\n set(false)\n}\n\nexport function toggleCartDrawer() {\n set(!isOpen)\n}\n\nexport function isCartDrawerOpen() {\n return isOpen\n}\n\n/** Estado y acciones del cajón. `open` es `false` en el servidor y en la primera pintura. */\nexport function useCartDrawer() {\n const open = useSyncExternalStore(subscribe, () => isOpen, () => false)\n const toggle = useCallback(() => toggleCartDrawer(), [])\n return { open, openDrawer: openCartDrawer, closeDrawer: closeCartDrawer, toggle }\n}\n\n/**\n * Abre el cajón cuando termina un \"agregar al carrito\". `busy` es verdadero mientras hay un agregado en vuelo\n * (en React Router: algún fetcher con la acción del carrito e intención de agregar). Con `skip` verdadero al\n * terminar (error, agregado silencioso) no se abre.\n */\nexport function useOpenCartDrawerWhenDone(busy: boolean, options: { skip?: boolean } = {}) {\n const was = useRef(false)\n const skip = useRef(false)\n skip.current = !!options.skip\n useEffect(() => {\n if (was.current && !busy && !skip.current) openCartDrawer()\n was.current = busy\n }, [busy])\n}\n\ntype ClickLike = {\n defaultPrevented: boolean\n button: number\n metaKey: boolean\n ctrlKey: boolean\n shiftKey: boolean\n altKey: boolean\n preventDefault: () => void\n}\n\n/**\n * `onClick` para el enlace \"Mi carrito\" de la cabecera: con un clic normal abre el cajón; con clic con\n * modificador, clic central o sin JavaScript el `href` (la página del carrito) sigue funcionando.\n */\nexport function handleCartLinkClick(e: ClickLike) {\n if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return\n e.preventDefault()\n openCartDrawer()\n}\n","import { useEffect, useRef, useSyncExternalStore } from 'react'\nimport type { Money } from '@asteby/storefront'\nimport { openCartDrawer, setQuietOpen } from './store.js'\n\n// Estado optimista del cajón del carrito. Solo guarda FILAS provisionales (producto, imagen, título, precio mostrado\n// y cantidad) mientras OPS responde. Nunca calcula dinero: precios, totales, impuestos y envío los dicta OPS cuando\n// llega el carrito real. Vive fuera de React, como el estado de apertura del cajón.\n\n/** Lo que la tienda sabe del producto al pulsar «Agregar». `price` es el precio unitario ya mostrado en la ficha. */\nexport type OptimisticLineInput = {\n /** Variante agregada; dos acciones con la misma variante comparten una sola fila. */\n variantId: string\n title: string\n variantTitle?: string | null | undefined\n productHandle?: string | undefined\n image?: { url: string; alt?: string | null | undefined } | null | undefined\n /** Precio unitario mostrado (solo se pinta, no se multiplica ni se suma). */\n price?: Money | null | undefined\n /** Unidades de ESTA acción (por omisión 1). */\n quantity?: number | undefined\n /** Clave de idempotencia: repetir `add` con una clave viva devuelve la misma acción sin sumar otra vez. */\n key?: string | undefined\n}\n\nexport type OptimisticAction = Required<Pick<OptimisticLineInput, 'variantId' | 'title'>> &\n Omit<OptimisticLineInput, 'quantity' | 'key'> & { id: string; quantity: number; key?: string | undefined }\n\n/** Fila provisional: todas las acciones en vuelo de una misma variante. */\nexport type OptimisticRow = {\n variantId: string\n title: string\n variantTitle?: string | null | undefined\n productHandle?: string | undefined\n image?: { url: string; alt?: string | null | undefined } | null | undefined\n price?: Money | null | undefined\n /** Unidades agregadas que OPS aún no confirma. */\n quantity: number\n actionIds: string[]\n}\n\nexport type OptimisticNotice = { id: string; title: string; reason: string }\n\nexport type OptimisticSnapshot = {\n rows: readonly OptimisticRow[]\n /** Unidades provisionales en total (para el contador). */\n quantity: number\n notices: readonly OptimisticNotice[]\n /** Último evento a anunciar en la región `aria-live=\"polite\"`; `n` cambia en cada evento. */\n announcement: { kind: 'added' | null; n: number }\n}\n\nexport type AddOutcome = { ok: true } | { ok: false; reason: string }\n\nconst DEFAULT_TIMEOUT_MS = 30_000\n\nlet actions: OptimisticAction[] = []\nlet notices: OptimisticNotice[] = []\nlet announcement: OptimisticSnapshot['announcement'] = { kind: null, n: 0 }\nlet seq = 0\nconst timers = new Map<string, ReturnType<typeof setTimeout>>()\nconst listeners = new Set<() => void>()\nlet snapshot: OptimisticSnapshot = compute()\n\nfunction compute(): OptimisticSnapshot {\n const byVariant = new Map<string, OptimisticRow>()\n for (const a of actions) {\n const row = byVariant.get(a.variantId)\n if (row) {\n row.quantity += a.quantity\n row.actionIds.push(a.id)\n } else {\n byVariant.set(a.variantId, {\n variantId: a.variantId, title: a.title, variantTitle: a.variantTitle, productHandle: a.productHandle,\n image: a.image, price: a.price, quantity: a.quantity, actionIds: [a.id],\n })\n }\n }\n const rows = [...byVariant.values()]\n return { rows, quantity: rows.reduce((n, r) => n + r.quantity, 0), notices, announcement }\n}\n\nfunction emit() {\n snapshot = compute()\n for (const l of [...listeners]) l()\n}\n\nconst subscribe = (l: () => void) => {\n listeners.add(l)\n return () => {\n listeners.delete(l)\n }\n}\nconst EMPTY: OptimisticSnapshot = { rows: [], quantity: 0, notices: [], announcement: { kind: null, n: 0 } }\n\nfunction drop(id: string): OptimisticAction | undefined {\n const a = actions.find((x) => x.id === id)\n if (!a) return undefined\n actions = actions.filter((x) => x.id !== id)\n const t = timers.get(id)\n if (t) clearTimeout(t)\n timers.delete(id)\n return a\n}\n\nfunction announce() {\n announcement = { kind: 'added', n: announcement.n + 1 }\n}\n\nexport const cartOptimistic = {\n /**\n * Inserta YA la fila provisional, sube el contador y abre el cajón sin robar el foco. Devuelve el id de la acción\n * para `confirm` / `rollback`. Una acción por clic: la misma variante suma cantidad en una sola fila.\n */\n add(input: OptimisticLineInput, options: { open?: boolean; timeoutMs?: number } = {}): string {\n if (input.key) {\n const live = actions.find((a) => a.key === input.key)\n if (live) return live.id\n }\n const id = `opt-${++seq}`\n const quantity = Math.max(1, Math.floor(input.quantity ?? 1))\n actions = [...actions, { ...input, id, quantity }]\n const ms = options.timeoutMs ?? DEFAULT_TIMEOUT_MS\n if (ms > 0 && typeof setTimeout === 'function') {\n timers.set(id, setTimeout(() => cartOptimistic.rollback(id, describeAddFailure({ code: 'timeout' })), ms))\n }\n announce()\n if (options.open !== false) {\n setQuietOpen(true)\n openCartDrawer()\n }\n emit()\n return id\n },\n\n /** OPS confirmó: la fila provisional sale y el carrito real (ya en `cart`) ocupa su lugar. */\n confirm(id: string) {\n if (!drop(id)) return\n announce()\n emit()\n },\n\n /** Falló: se quita la acción (y su cantidad) y se avisa «No pudimos agregar X: motivo». */\n rollback(id: string, reason = 'unknown') {\n const a = drop(id)\n if (!a) return\n notices = [...notices, { id: `n-${++seq}`, title: a.title, reason }]\n emit()\n },\n\n /** Quita la acción sin avisar (el componente que la lanzó se desmontó). */\n discard(id: string) {\n if (drop(id)) emit()\n },\n\n dismissNotice(id: string) {\n notices = notices.filter((n) => n.id !== id)\n emit()\n },\n\n /** Limpia todo (pruebas, cierre de sesión). */\n reset() {\n for (const t of timers.values()) clearTimeout(t)\n timers.clear()\n actions = []\n notices = []\n announcement = { kind: null, n: 0 }\n setQuietOpen(false)\n emit()\n },\n\n getSnapshot: (): OptimisticSnapshot => snapshot,\n}\n\n/** Estado optimista (filas, contador, avisos). En el servidor siempre vacío. */\nexport function useOptimisticCart(): OptimisticSnapshot {\n return useSyncExternalStore(subscribe, () => snapshot, () => EMPTY)\n}\n\n/** Contador para la cabecera: unidades del carrito de OPS más las que están en vuelo. */\nexport function useOptimisticCartCount(base: number): number {\n return base + useOptimisticCart().quantity\n}\n\n/** Motivo corto en es-MX a partir de un error de la acción (HTTP, código de OPS o red). */\nexport function describeAddFailure(error: { status?: number | undefined; code?: string | undefined; message?: string | undefined } | null | undefined): string {\n const code = error?.code\n const status = error?.status\n if (code === 'store_not_ready') return 'la tienda está cerrada por ahora'\n if (code === 'timeout') return 'tardó demasiado, intenta de nuevo'\n if (status === 409 || code === 'out_of_stock' || code === 'insufficient_stock') return 'sin existencias suficientes'\n if (status === 0 || code === 'network' || (!status && !code)) return 'revisa tu conexión'\n if (status && status >= 500) return 'hubo un problema, intenta de nuevo'\n if (status === 404) return 'el producto ya no está disponible'\n return error?.message?.trim() || 'intenta de nuevo'\n}\n\ntype FetcherLike = { state: string; data?: unknown }\n\n/** Interpreta `fetcher.data` de la acción de ruta: `{ error }`, `{ success:false }`, `{ ok:false }` o `status >= 400` fallan. */\nexport function defaultAddOutcome(data: unknown): AddOutcome {\n if (!data || typeof data !== 'object') return { ok: true }\n const d = data as Record<string, unknown>\n const status = typeof d.status === 'number' ? d.status : undefined\n const err = (d.error && typeof d.error === 'object' ? d.error : d) as Record<string, unknown>\n const code = typeof err.code === 'string' ? err.code : undefined\n const failed = d.error != null || d.ok === false || d.success === false || (status !== undefined && status >= 400)\n if (!failed) return { ok: true }\n return { ok: false, reason: describeAddFailure({ status: status ?? (typeof err.status === 'number' ? err.status : undefined), code, message: typeof err.message === 'string' ? err.message : undefined }) }\n}\n\n/**\n * Conecta un `useFetcher()` de React Router con el estado optimista. `add(input, submit)` inserta la fila, abre el\n * cajón y llama a `submit()` (el `fetcher.submit(...)` de la tienda); al terminar el fetcher confirma o revierte.\n * Un nuevo `add` con el mismo fetcher antes de terminar da por entregado el anterior (el router lo reemplaza y el\n * carrito real que llega con la revalidación manda). Si el componente se desmonta, las filas se descartan sin aviso.\n */\nexport function useOptimisticAdd(fetcher: FetcherLike, options: { settle?: (data: unknown) => AddOutcome } = {}) {\n const current = useRef<string | null>(null)\n const was = useRef(false)\n const settleRef = useRef(options.settle)\n settleRef.current = options.settle\n\n useEffect(() => {\n const busy = fetcher.state !== 'idle'\n const id = current.current\n if (was.current && !busy && id) {\n current.current = null\n const out = (settleRef.current ?? defaultAddOutcome)(fetcher.data)\n if (out.ok) cartOptimistic.confirm(id)\n else cartOptimistic.rollback(id, out.reason)\n }\n was.current = busy\n }, [fetcher.state, fetcher.data])\n\n useEffect(\n () => () => {\n if (current.current) cartOptimistic.discard(current.current)\n current.current = null\n },\n [],\n )\n\n return {\n add(input: OptimisticLineInput, submit: () => void): string {\n const prev = current.current\n const id = cartOptimistic.add(input)\n if (prev && prev !== id) cartOptimistic.confirm(prev)\n current.current = id\n try {\n submit()\n } catch (e) {\n current.current = null\n cartOptimistic.rollback(id, describeAddFailure({ message: e instanceof Error ? e.message : undefined }))\n }\n return id\n },\n }\n}\n","import { useEffect, useId, type ElementType, type ReactNode } from 'react'\nimport { formatMoney, type Cart, type CartLine, type Money } from '@asteby/storefront'\nimport { useModalDialog } from '../dialog.js'\nimport { useResolvedLocale } from '../locale.js'\nimport { closeCartDrawer, isQuietOpen, useCartDrawer } from './store.js'\nimport { cartOptimistic, useOptimisticCart, type OptimisticNotice, type OptimisticRow } from './optimistic.js'\n\n/** Textos del estado optimista. Por omisión es-MX; la tienda puede traducirlos o cambiarlos. */\nexport type CartDrawerOptimisticLabels = {\n /** Estado discreto en la fila provisional. */\n adding: string\n /** Anuncio `aria-live=\"polite\"` al mostrar/confirmar la fila. */\n added: string\n /** Junto al subtotal mientras OPS no confirma. */\n calculating: string\n /** Aviso de error: «No pudimos agregar X: motivo». */\n failed: (title: string, reason: string) => string\n dismiss: string\n}\n\nexport const DEFAULT_OPTIMISTIC_LABELS: CartDrawerOptimisticLabels = {\n adding: 'Agregando…',\n added: 'Agregado al carrito',\n calculating: 'calculando…',\n failed: (title, reason) => `No pudimos agregar ${title}: ${reason}`,\n dismiss: 'Descartar aviso',\n}\n\nexport type CartDrawerLabels = {\n /** Título; el cajón agrega \" (N)\" con la cantidad de unidades. */\n title: string\n /** Texto accesible del botón de cierre. */\n close: string\n empty: string\n emptyHint?: string\n /** Texto del enlace \"seguir comprando\" del estado vacío (se muestra con `paths.browse`). */\n browse?: string\n subtotal: string\n /** Nota bajo el subtotal. */\n note?: string\n checkout: string\n viewCart: string\n remove: string\n increase: string\n decrease: string\n}\n\nexport type CartDrawerPaths = {\n /** Página del carrito: acción de las mutaciones y destino de \"ver carrito\". */\n cart: string\n checkout: string\n browse?: string\n /** Enlace de la línea al producto; sin esto el título no es enlace. */\n product?: (handle: string, variantId: string) => string\n}\n\n/** Contrato de la acción de ruta del carrito de la tienda (nombres de campo e intenciones). */\nexport type CartDrawerFormContract = {\n intentField?: string\n lineField?: string\n quantityField?: string\n updateIntent?: string\n removeIntent?: string\n}\n\nexport type CartDrawerShipping = {\n /** Mensaje ya calculado por la tienda con lo que manda OPS. */\n message: ReactNode\n /** 0..100 */\n percent: number\n reached?: boolean\n progressLabel: string\n}\n\nexport type CartDrawerLineForm = {\n /** Componente de formulario (`'form'`, o `fetcher.Form` del router para mejorar sin recargar). */\n Form: ElementType\n /** Datos en vuelo del formulario, para pintar la cantidad de forma optimista. */\n formData?: FormData | null | undefined\n}\n\nexport type CartDrawerLinkProps = { href: string; className?: string | undefined; children: ReactNode; onClick?: () => void }\n\nexport type CartDrawerSlot =\n | 'dialog' | 'panel' | 'header' | 'title' | 'close' | 'empty' | 'body' | 'shipping' | 'bar' | 'barFill' | 'lines' | 'line'\n | 'image' | 'lineTitle' | 'variant' | 'price' | 'qty' | 'qtyButton' | 'qtyValue' | 'remove' | 'upsell' | 'footer' | 'subtotal'\n | 'note' | 'actions' | 'checkout' | 'viewCart' | 'browse' | 'emptyTitle' | 'emptyHint' | 'shippingMessage' | 'lineMain'\n | 'notices' | 'lineHead' | 'lineText' | 'controls' | 'upsellTitle' | 'subtotalLabel' | 'subtotalValue'\n\nexport type CartDrawerIcons = { close?: ReactNode; decrease?: ReactNode; increase?: ReactNode; shipping?: ReactNode; shippingReached?: ReactNode }\n\nexport type CartDrawerProps = {\n /** `cart.cost` y cada línea vienen de OPS; el cajón no calcula nada. */\n cart: Cart | null | undefined\n labels: CartDrawerLabels\n paths: CartDrawerPaths\n form?: CartDrawerFormContract\n /** Hook que da el formulario de cada línea. Por omisión `<form method=\"post\">` (funciona sin JavaScript). */\n useLineForm?: () => CartDrawerLineForm\n shipping?: CartDrawerShipping | null\n /** Aviso fijo cuando no hay barra de envío (p. ej. \"Envío gratis\"). */\n notice?: ReactNode\n /** Sección \"podría interesarte\" (la tienda decide qué mostrar). */\n upsell?: ReactNode\n upsellTitle?: string\n renderLine?: (line: CartLine, defaults: { controls: ReactNode; price: ReactNode; image: ReactNode }) => ReactNode\n renderImage?: (line: CartLine) => ReactNode\n renderMoney?: (money: Money) => ReactNode\n renderLink?: (props: CartDrawerLinkProps) => ReactNode\n /** Omite el variante cuando es el valor por defecto de OPS (\"Default Title\"). */\n isDefaultVariantTitle?: (title: string) => boolean\n icons?: CartDrawerIcons\n /** Clases por pieza (además de los `data-sf-cart-*`), para tiendas con Tailwind. */\n classNames?: Partial<Record<CartDrawerSlot, string>>\n /** Textos de las filas provisionales y de los avisos (por omisión es-MX). */\n optimisticLabels?: Partial<CartDrawerOptimisticLabels>\n locale?: string\n /** Se llama al abrir/cerrar por cualquier vía. */\n onOpenChange?: (open: boolean) => void\n}\n\nconst DEFAULT_FORM: Required<CartDrawerFormContract> = {\n intentField: 'intent',\n lineField: 'line_id',\n quantityField: 'quantity',\n updateIntent: 'update',\n removeIntent: 'remove',\n}\n\nconst useNativeForm = (): CartDrawerLineForm => ({ Form: 'form' })\n\nconst defaultIsDefaultVariant = (t: string) => /^default title$/i.test(t.trim())\n\nfunction useBodyScrollLock(locked: boolean) {\n useEffect(() => {\n if (!locked || typeof document === 'undefined') return\n const el = document.documentElement\n const prev = el.style.overflow\n el.style.overflow = 'hidden'\n return () => {\n el.style.overflow = prev\n }\n }, [locked])\n}\n\n/**\n * Cajón lateral del carrito (\"Tu carrito (N)\"). Sin textos ni marca: todo llega por props.\n * Se monta una vez (en la raíz) y se abre con `openCartDrawer()` / `useCartDrawer()`.\n * `<dialog>` modal: `role=\"dialog\"`, `aria-modal`, foco atrapado y devuelto al abridor, Esc y clic fuera cierran,\n * el fondo no hace scroll. Las mutaciones usan la acción de ruta de la tienda (formularios nativos que\n * funcionan sin JavaScript, o el `Form` de un fetcher con `useLineForm`). Estilos: `@asteby/storefront-react/cart.css`\n * (tokens de la tienda) y/o `classNames`.\n */\nexport function CartDrawer(props: CartDrawerProps) {\n const { cart, labels, paths, shipping, notice, upsell, upsellTitle, icons, classNames: cx = {}, onOpenChange } = props\n const locale = useResolvedLocale(props.locale)\n const { open } = useCartDrawer()\n const opt = useOptimisticCart()\n const ol = { ...DEFAULT_OPTIMISTIC_LABELS, ...props.optimisticLabels }\n const quiet = open && isQuietOpen()\n const titleId = useId()\n const upsellId = useId()\n const { ref, onBackdropClick } = useModalDialog({ open, onClose: () => closeCartDrawer() })\n useBodyScrollLock(open)\n useEffect(() => {\n onOpenChange?.(open)\n }, [open]) // eslint-disable-line react-hooks/exhaustive-deps\n\n const realLines = cart?.lines.length ?? 0\n const rollbackEmpty = open && quiet && realLines === 0 && opt.rows.length === 0 && opt.notices.length > 0\n useEffect(() => {\n // Sin filas y con un aviso de fallo, un cajón vacío abierto por la acción sería engañoso: se cierra.\n if (rollbackEmpty) closeCartDrawer()\n }, [rollbackEmpty])\n\n if (!open) return opt.notices.length ? <OptimisticNotices notices={opt.notices} labels={ol} classNames={cx} /> : null\n\n const money = props.renderMoney ?? ((m: Money) => formatMoney(m, locale))\n const link = props.renderLink ?? (({ href, className, children, onClick }: CartDrawerLinkProps) => (\n <a href={href} className={className} onClick={onClick}>{children}</a>\n ))\n const pendingByVariant = new Map(opt.rows.map((r) => [r.variantId, r]))\n const count = (cart?.total_quantity ?? 0) + opt.quantity\n const lines = cart?.lines ?? []\n const extraRows = opt.rows.filter((r) => !lines.some((l) => l.merchandise.id === r.variantId))\n const pending = opt.rows.length > 0\n const empty = lines.length === 0 && extraRows.length === 0\n const close = () => closeCartDrawer()\n\n return (\n <dialog\n ref={ref}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n onClick={onBackdropClick}\n onClose={close}\n className={cx.dialog}\n data-sf-cart-drawer=\"\"\n >\n <div className={cx.panel} data-sf-cart-panel=\"\">\n <div className={cx.header} data-sf-cart-header=\"\">\n <h2 id={titleId} tabIndex={-1} className={cx.title} data-sf-cart-title=\"\">\n {labels.title}{count > 0 ? ` (${count})` : ''}\n </h2>\n <button type=\"button\" onClick={close} aria-label={labels.close} className={cx.close} data-sf-cart-close=\"\" data-sf-autofocus={quiet ? undefined : ''}>\n {icons?.close ?? <span aria-hidden=\"true\">✕</span>}\n </button>\n </div>\n\n <OptimisticNotices notices={opt.notices} labels={ol} classNames={cx} />\n <OptimisticStatus kind={opt.announcement.kind} n={opt.announcement.n} text={ol.added} />\n\n {empty ? (\n <div className={cx.empty} data-sf-cart-empty=\"\">\n <p className={cx.emptyTitle} data-sf-cart-empty-title=\"\">{labels.empty}</p>\n {labels.emptyHint ? <p className={cx.emptyHint} data-sf-cart-empty-hint=\"\">{labels.emptyHint}</p> : null}\n {paths.browse && labels.browse ? link({ href: paths.browse, className: cx.browse, children: labels.browse, onClick: close }) : null}\n </div>\n ) : (\n <>\n <div className={cx.body} data-sf-cart-body=\"\">\n {shipping ? (\n <div className={cx.shipping} data-sf-cart-shipping=\"\" data-reached={shipping.reached || undefined}>\n <p className={cx.shippingMessage}>\n {shipping.reached ? icons?.shippingReached : icons?.shipping}\n {shipping.message}\n </p>\n <div\n role=\"progressbar\"\n aria-valuemin={0}\n aria-valuemax={100}\n aria-valuenow={Math.round(shipping.percent)}\n aria-label={shipping.progressLabel}\n className={cx.bar}\n data-sf-cart-bar=\"\"\n >\n <div className={cx.barFill} data-sf-cart-bar-fill=\"\" style={{ width: `${Math.max(0, Math.min(100, shipping.percent))}%` }} />\n </div>\n </div>\n ) : notice ? (\n <p className={[cx.shipping, cx.shippingMessage].filter(Boolean).join(\" \") || undefined} data-sf-cart-shipping=\"\" data-notice=\"\">{icons?.shippingReached}{notice}</p>\n ) : null}\n\n <ul className={cx.lines} data-sf-cart-lines=\"\">\n {lines.map((line) => (\n <DrawerLine key={line.id} line={line} props={props} money={money} link={link} close={close} pending={pendingByVariant.get(line.merchandise.id)} pendingLabel={ol.adding} />\n ))}\n {extraRows.map((row) => (\n <PendingLine key={`pending-${row.variantId}`} row={row} props={props} money={money} label={ol.adding} />\n ))}\n </ul>\n\n {upsell ? (\n <section className={cx.upsell} data-sf-cart-upsell=\"\" aria-labelledby={upsellTitle ? upsellId : undefined}>\n {upsellTitle ? <h3 id={upsellId} className={cx.upsellTitle}>{upsellTitle}</h3> : null}\n {upsell}\n </section>\n ) : null}\n </div>\n\n <div className={cx.footer} data-sf-cart-footer=\"\">\n <div className={cx.subtotal} data-sf-cart-subtotal=\"\" aria-busy={pending || undefined}>\n <span className={cx.subtotalLabel}>{labels.subtotal}</span>\n <span className={cx.subtotalValue} data-sf-cart-subtotal-value=\"\">{cart ? money(cart.cost.subtotal) : null}{pending ? <span data-sf-cart-calculating=\"\">{cart ? ' ' : ''}{ol.calculating}</span> : null}</span>\n </div>\n {labels.note ? <p className={cx.note} data-sf-cart-note=\"\">{labels.note}</p> : null}\n <div className={cx.actions} data-sf-cart-actions=\"\">\n {link({ href: paths.checkout, className: cx.checkout, children: labels.checkout, onClick: close })}\n {link({ href: paths.cart, className: cx.viewCart, children: labels.viewCart, onClick: close })}\n </div>\n </div>\n </>\n )}\n </div>\n </dialog>\n )\n}\n\nfunction DrawerLine({\n line,\n props,\n money,\n link,\n close,\n pending,\n pendingLabel,\n}: {\n pending?: OptimisticRow | undefined\n pendingLabel: string\n line: CartLine\n props: CartDrawerProps\n money: (m: Money) => ReactNode\n link: (p: CartDrawerLinkProps) => ReactNode\n close: () => void\n}) {\n const { labels, paths, icons, classNames: cx = {} } = props\n const f = { ...DEFAULT_FORM, ...props.form }\n const { Form, formData } = (props.useLineForm ?? useNativeForm)()\n const quantity = formData ? Number(formData.get(f.quantityField) ?? 0) : line.quantity + (pending?.quantity ?? 0)\n if (formData && (formData.get(f.intentField) === f.removeIntent || quantity === 0)) return null\n\n const merch = line.merchandise\n const isDefault = props.isDefaultVariantTitle ?? defaultIsDefaultVariant\n const title = merch.product.title\n const href = paths.product?.(merch.product.handle, merch.id)\n const img = merch.image ?? merch.product.featured_image\n const image = props.renderImage ? props.renderImage(line) : img ? (\n <img src={img.url} alt={img.alt ?? ''} width={80} height={80} loading=\"lazy\" decoding=\"async\" />\n ) : null\n\n const imageNode = <span className={cx.image} data-sf-cart-image=\"\">{image}</span>\n const price = <span className={cx.price} data-sf-cart-price=\"\">{money(line.cost.total)}</span>\n const controls = (\n <div className={cx.controls} data-sf-cart-controls=\"\">\n <Form method=\"post\" action={paths.cart} className={cx.qty} data-sf-cart-qty=\"\">\n <input type=\"hidden\" name={f.intentField} value={f.updateIntent} />\n <input type=\"hidden\" name={f.lineField} value={line.id} />\n <button type=\"submit\" name={f.quantityField} value={quantity - 1} aria-label={labels.decrease} className={cx.qtyButton} data-sf-cart-qty-button=\"\">\n {icons?.decrease ?? <span aria-hidden=\"true\">−</span>}\n </button>\n <span className={cx.qtyValue} aria-live=\"polite\" data-sf-cart-qty-value=\"\">{quantity}</span>\n <button type=\"submit\" name={f.quantityField} value={quantity + 1} aria-label={labels.increase} className={cx.qtyButton} data-sf-cart-qty-button=\"\">\n {icons?.increase ?? <span aria-hidden=\"true\">+</span>}\n </button>\n </Form>\n <Form method=\"post\" action={paths.cart} data-sf-cart-remove-form=\"\">\n <input type=\"hidden\" name={f.intentField} value={f.removeIntent} />\n <input type=\"hidden\" name={f.lineField} value={line.id} />\n <button type=\"submit\" className={cx.remove} data-sf-cart-remove=\"\">{labels.remove}</button>\n </Form>\n </div>\n )\n\n if (props.renderLine) return <li key={line.id} className={cx.line} data-sf-cart-line=\"\">{props.renderLine(line, { controls, price, image: imageNode })}</li>\n\n return (\n <li className={cx.line} data-sf-cart-line=\"\">\n {imageNode}\n <div className={cx.lineMain} data-sf-cart-line-main=\"\">\n <div className={cx.lineHead} data-sf-cart-line-head=\"\">\n <div className={cx.lineText} data-sf-cart-line-text=\"\">\n {href ? link({ href, className: cx.lineTitle, children: title, onClick: close }) : <span className={cx.lineTitle} data-sf-cart-line-title=\"\">{title}</span>}\n {!isDefault(merch.title) ? <p className={cx.variant} data-sf-cart-variant=\"\">{merch.title}</p> : null}\n </div>\n {price}\n </div>\n {controls}\n {pending ? <p className={cx.variant} data-sf-cart-pending-label=\"\">{pendingLabel}</p> : null}\n </div>\n </li>\n )\n}\n\n/** Fila que existe solo en el navegador hasta que OPS la confirma: sin controles, con el precio unitario mostrado. */\nfunction PendingLine({ row, props, money, label }: { row: OptimisticRow; props: CartDrawerProps; money: (m: Money) => ReactNode; label: string }) {\n const { classNames: cx = {} } = props\n const isDefault = props.isDefaultVariantTitle ?? defaultIsDefaultVariant\n return (\n <li className={cx.line} data-sf-cart-line=\"\" data-sf-cart-line-pending=\"\" aria-busy=\"true\">\n <span className={cx.image} data-sf-cart-image=\"\">\n {row.image ? <img src={row.image.url} alt={row.image.alt ?? ''} width={80} height={80} decoding=\"async\" /> : null}\n </span>\n <div className={cx.lineMain} data-sf-cart-line-main=\"\">\n <div className={cx.lineHead} data-sf-cart-line-head=\"\">\n <div className={cx.lineText} data-sf-cart-line-text=\"\">\n <span className={cx.lineTitle} data-sf-cart-line-title=\"\">{row.title}</span>\n {row.variantTitle && !isDefault(row.variantTitle) ? <p className={cx.variant} data-sf-cart-variant=\"\">{row.variantTitle}</p> : null}\n </div>\n {row.price ? <span className={cx.price} data-sf-cart-price=\"\">{money(row.price)}</span> : null}\n </div>\n <p className={cx.variant} data-sf-cart-pending-label=\"\">{label} · ×{row.quantity}</p>\n </div>\n </li>\n )\n}\n\n/** Región `aria-live=\"polite\"`: anuncia «Agregado al carrito». Alterna un espacio fino para repetir el mismo texto. */\nfunction OptimisticStatus({ kind, n, text }: { kind: 'added' | null; n: number; text: string }) {\n return (\n <div role=\"status\" aria-live=\"polite\" data-sf-cart-status=\"\" style={VISUALLY_HIDDEN}>\n {kind === 'added' ? `${text}${n % 2 ? '' : '\\u200B'}` : ''}\n </div>\n )\n}\n\nconst VISUALLY_HIDDEN = { position: 'absolute', width: 1, height: 1, margin: -1, padding: 0, overflow: 'hidden', clip: 'rect(0 0 0 0)', whiteSpace: 'nowrap', border: 0 } as const\n\n/** Avisos de fallo (`role=\"alert\"`, assertive), descartables. Se pintan dentro del cajón abierto o, si está cerrado, sueltos. */\nfunction OptimisticNotices({ notices, labels, classNames: cx }: { notices: readonly OptimisticNotice[]; labels: CartDrawerOptimisticLabels; classNames: Partial<Record<CartDrawerSlot, string>> }) {\n return (\n <div role=\"alert\" aria-live=\"assertive\" data-sf-cart-notices=\"\" className={cx.notices}>\n {notices.map((n) => (\n <p key={n.id} data-sf-cart-notice=\"\">\n <span>{labels.failed(n.title, n.reason)}</span>\n <button type=\"button\" onClick={() => cartOptimistic.dismissNotice(n.id)} aria-label={labels.dismiss} data-sf-cart-notice-dismiss=\"\">\n <span aria-hidden=\"true\">✕</span>\n </button>\n </p>\n ))}\n </div>\n )\n}\n"]}
1
+ {"version":3,"sources":["../src/cart-drawer/store.ts","../src/cart-drawer/optimistic.ts","../src/cart-drawer/cart-drawer.tsx"],"names":["listeners","subscribe","useSyncExternalStore","useRef","useEffect","notices"],"mappings":"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","file":"cart.js","sourcesContent":["import { useCallback, useEffect, useRef, useSyncExternalStore } from 'react'\n\n// Estado mínimo del cajón del carrito, fuera de React: cualquier parte de la tienda (cabecera, tarjeta, ficha)\n// lo abre sin pasar props. Es del navegador; en el servidor siempre está cerrado.\n\nlet isOpen = false\nconst listeners = new Set<() => void>()\n\nlet quietOpen = false\n\n/** Marca que el cajón lo abrió una acción optimista (la apertura no mueve el foco al botón de cierre). */\nexport function setQuietOpen(next: boolean) {\n quietOpen = next\n}\n\nexport function isQuietOpen() {\n return quietOpen\n}\n\nfunction set(next: boolean) {\n if (isOpen === next) return\n isOpen = next\n if (!next) quietOpen = false\n for (const l of [...listeners]) l()\n}\n\nconst subscribe = (l: () => void) => {\n listeners.add(l)\n return () => {\n listeners.delete(l)\n }\n}\n\n/** Abre el cajón del carrito desde cualquier parte. */\nexport function openCartDrawer() {\n set(true)\n}\n\n/** Cierra el cajón del carrito. */\nexport function closeCartDrawer() {\n set(false)\n}\n\nexport function toggleCartDrawer() {\n set(!isOpen)\n}\n\nexport function isCartDrawerOpen() {\n return isOpen\n}\n\n/** Estado y acciones del cajón. `open` es `false` en el servidor y en la primera pintura. */\nexport function useCartDrawer() {\n const open = useSyncExternalStore(subscribe, () => isOpen, () => false)\n const toggle = useCallback(() => toggleCartDrawer(), [])\n return { open, openDrawer: openCartDrawer, closeDrawer: closeCartDrawer, toggle }\n}\n\n/**\n * Abre el cajón cuando termina un \"agregar al carrito\". `busy` es verdadero mientras hay un agregado en vuelo\n * (en React Router: algún fetcher con la acción del carrito e intención de agregar). Con `skip` verdadero al\n * terminar (error, agregado silencioso) no se abre.\n */\nexport function useOpenCartDrawerWhenDone(busy: boolean, options: { skip?: boolean } = {}) {\n const was = useRef(false)\n const skip = useRef(false)\n skip.current = !!options.skip\n useEffect(() => {\n if (was.current && !busy && !skip.current) openCartDrawer()\n was.current = busy\n }, [busy])\n}\n\ntype ClickLike = {\n defaultPrevented: boolean\n button: number\n metaKey: boolean\n ctrlKey: boolean\n shiftKey: boolean\n altKey: boolean\n preventDefault: () => void\n}\n\n/**\n * `onClick` para el enlace \"Mi carrito\" de la cabecera: con un clic normal abre el cajón; con clic con\n * modificador, clic central o sin JavaScript el `href` (la página del carrito) sigue funcionando.\n */\nexport function handleCartLinkClick(e: ClickLike) {\n if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return\n e.preventDefault()\n openCartDrawer()\n}\n","import { useEffect, useRef, useSyncExternalStore } from 'react'\nimport type { Money } from '@asteby/storefront'\nimport { openCartDrawer, setQuietOpen } from './store.js'\n\n// Estado optimista del cajón del carrito. Solo guarda FILAS provisionales (producto, imagen, título, precio mostrado\n// y cantidad) mientras OPS responde. Nunca calcula dinero: precios, totales, impuestos y envío los dicta OPS cuando\n// llega el carrito real. Vive fuera de React, como el estado de apertura del cajón.\n\n/** Lo que la tienda sabe del producto al pulsar «Agregar». `price` es el precio unitario ya mostrado en la ficha. */\nexport type OptimisticLineInput = {\n /** Variante agregada; dos acciones con la misma variante comparten una sola fila. */\n variantId: string\n title: string\n variantTitle?: string | null | undefined\n productHandle?: string | undefined\n image?: { url: string; alt?: string | null | undefined } | null | undefined\n /** Precio unitario mostrado (solo se pinta, no se multiplica ni se suma). */\n price?: Money | null | undefined\n /** Unidades de ESTA acción (por omisión 1). */\n quantity?: number | undefined\n /** Clave de idempotencia: repetir `add` con una clave viva devuelve la misma acción sin sumar otra vez. */\n key?: string | undefined\n}\n\nexport type OptimisticAction = Required<Pick<OptimisticLineInput, 'variantId' | 'title'>> &\n Omit<OptimisticLineInput, 'quantity' | 'key'> & { id: string; quantity: number; key?: string | undefined }\n\n/** Fila provisional: todas las acciones en vuelo de una misma variante. */\nexport type OptimisticRow = {\n variantId: string\n title: string\n variantTitle?: string | null | undefined\n productHandle?: string | undefined\n image?: { url: string; alt?: string | null | undefined } | null | undefined\n price?: Money | null | undefined\n /** Unidades agregadas que OPS aún no confirma. */\n quantity: number\n actionIds: string[]\n}\n\nexport type OptimisticNotice = { id: string; title: string; reason: string }\n\nexport type OptimisticSnapshot = {\n rows: readonly OptimisticRow[]\n /** Unidades provisionales en total (para el contador). */\n quantity: number\n notices: readonly OptimisticNotice[]\n /** Último evento a anunciar en la región `aria-live=\"polite\"`; `n` cambia en cada evento. */\n announcement: { kind: 'added' | null; n: number }\n}\n\nexport type AddOutcome = { ok: true } | { ok: false; reason: string }\n\nconst DEFAULT_TIMEOUT_MS = 30_000\n\nlet actions: OptimisticAction[] = []\nlet notices: OptimisticNotice[] = []\nlet announcement: OptimisticSnapshot['announcement'] = { kind: null, n: 0 }\nlet seq = 0\nconst timers = new Map<string, ReturnType<typeof setTimeout>>()\nconst listeners = new Set<() => void>()\nlet snapshot: OptimisticSnapshot = compute()\n\nfunction compute(): OptimisticSnapshot {\n const byVariant = new Map<string, OptimisticRow>()\n for (const a of actions) {\n const row = byVariant.get(a.variantId)\n if (row) {\n row.quantity += a.quantity\n row.actionIds.push(a.id)\n } else {\n byVariant.set(a.variantId, {\n variantId: a.variantId, title: a.title, variantTitle: a.variantTitle, productHandle: a.productHandle,\n image: a.image, price: a.price, quantity: a.quantity, actionIds: [a.id],\n })\n }\n }\n const rows = [...byVariant.values()]\n return { rows, quantity: rows.reduce((n, r) => n + r.quantity, 0), notices, announcement }\n}\n\nfunction emit() {\n snapshot = compute()\n for (const l of [...listeners]) l()\n}\n\nconst subscribe = (l: () => void) => {\n listeners.add(l)\n return () => {\n listeners.delete(l)\n }\n}\nconst EMPTY: OptimisticSnapshot = { rows: [], quantity: 0, notices: [], announcement: { kind: null, n: 0 } }\n\nfunction drop(id: string): OptimisticAction | undefined {\n const a = actions.find((x) => x.id === id)\n if (!a) return undefined\n actions = actions.filter((x) => x.id !== id)\n const t = timers.get(id)\n if (t) clearTimeout(t)\n timers.delete(id)\n return a\n}\n\nfunction announce() {\n announcement = { kind: 'added', n: announcement.n + 1 }\n}\n\nexport const cartOptimistic = {\n /**\n * Inserta YA la fila provisional, sube el contador y abre el cajón sin robar el foco. Devuelve el id de la acción\n * para `confirm` / `rollback`. Una acción por clic: la misma variante suma cantidad en una sola fila.\n */\n add(input: OptimisticLineInput, options: { open?: boolean; timeoutMs?: number } = {}): string {\n if (input.key) {\n const live = actions.find((a) => a.key === input.key)\n if (live) return live.id\n }\n const id = `opt-${++seq}`\n const quantity = Math.max(1, Math.floor(input.quantity ?? 1))\n actions = [...actions, { ...input, id, quantity }]\n const ms = options.timeoutMs ?? DEFAULT_TIMEOUT_MS\n if (ms > 0 && typeof setTimeout === 'function') {\n timers.set(id, setTimeout(() => cartOptimistic.rollback(id, describeAddFailure({ code: 'timeout' })), ms))\n }\n announce()\n if (options.open !== false) {\n setQuietOpen(true)\n openCartDrawer()\n }\n emit()\n return id\n },\n\n /** OPS confirmó: la fila provisional sale y el carrito real (ya en `cart`) ocupa su lugar. */\n confirm(id: string) {\n if (!drop(id)) return\n announce()\n emit()\n },\n\n /** Falló: se quita la acción (y su cantidad) y se avisa «No pudimos agregar X: motivo». */\n rollback(id: string, reason = 'unknown') {\n const a = drop(id)\n if (!a) return\n notices = [...notices, { id: `n-${++seq}`, title: a.title, reason }]\n emit()\n },\n\n /** Quita la acción sin avisar (el componente que la lanzó se desmontó). */\n discard(id: string) {\n if (drop(id)) emit()\n },\n\n dismissNotice(id: string) {\n notices = notices.filter((n) => n.id !== id)\n emit()\n },\n\n /** Limpia todo (pruebas, cierre de sesión). */\n reset() {\n for (const t of timers.values()) clearTimeout(t)\n timers.clear()\n actions = []\n notices = []\n announcement = { kind: null, n: 0 }\n setQuietOpen(false)\n emit()\n },\n\n getSnapshot: (): OptimisticSnapshot => snapshot,\n}\n\n/** Estado optimista (filas, contador, avisos). En el servidor siempre vacío. */\nexport function useOptimisticCart(): OptimisticSnapshot {\n return useSyncExternalStore(subscribe, () => snapshot, () => EMPTY)\n}\n\n/** Contador para la cabecera: unidades del carrito de OPS más las que están en vuelo. */\nexport function useOptimisticCartCount(base: number): number {\n return base + useOptimisticCart().quantity\n}\n\n/** Motivo corto en es-MX a partir de un error de la acción (HTTP, código de OPS o red). */\nexport function describeAddFailure(error: { status?: number | undefined; code?: string | undefined; message?: string | undefined } | null | undefined): string {\n const code = error?.code\n const status = error?.status\n if (code === 'store_not_ready') return 'la tienda está cerrada por ahora'\n if (code === 'timeout') return 'tardó demasiado, intenta de nuevo'\n if (status === 409 || code === 'out_of_stock' || code === 'insufficient_stock') return 'sin existencias suficientes'\n if (status === 0 || code === 'network' || (!status && !code)) return 'revisa tu conexión'\n if (status && status >= 500) return 'hubo un problema, intenta de nuevo'\n if (status === 404) return 'el producto ya no está disponible'\n return error?.message?.trim() || 'intenta de nuevo'\n}\n\ntype FetcherLike = { state: string; data?: unknown }\n\n/** Interpreta `fetcher.data` de la acción de ruta: `{ error }`, `{ success:false }`, `{ ok:false }` o `status >= 400` fallan. */\nexport function defaultAddOutcome(data: unknown): AddOutcome {\n if (!data || typeof data !== 'object') return { ok: true }\n const d = data as Record<string, unknown>\n const status = typeof d.status === 'number' ? d.status : undefined\n const err = (d.error && typeof d.error === 'object' ? d.error : d) as Record<string, unknown>\n const code = typeof err.code === 'string' ? err.code : undefined\n const failed = d.error != null || d.ok === false || d.success === false || (status !== undefined && status >= 400)\n if (!failed) return { ok: true }\n return { ok: false, reason: describeAddFailure({ status: status ?? (typeof err.status === 'number' ? err.status : undefined), code, message: typeof err.message === 'string' ? err.message : undefined }) }\n}\n\n/**\n * Conecta un `useFetcher()` de React Router con el estado optimista. `add(input, submit)` inserta la fila, abre el\n * cajón y llama a `submit()` (el `fetcher.submit(...)` de la tienda); al terminar el fetcher confirma o revierte.\n * Un nuevo `add` con el mismo fetcher antes de terminar da por entregado el anterior (el router lo reemplaza y el\n * carrito real que llega con la revalidación manda). Si el componente se desmonta, las filas se descartan sin aviso.\n */\nexport function useOptimisticAdd(fetcher: FetcherLike, options: { settle?: (data: unknown) => AddOutcome } = {}) {\n const current = useRef<string | null>(null)\n const was = useRef(false)\n const settleRef = useRef(options.settle)\n settleRef.current = options.settle\n\n useEffect(() => {\n const busy = fetcher.state !== 'idle'\n const id = current.current\n if (was.current && !busy && id) {\n current.current = null\n const out = (settleRef.current ?? defaultAddOutcome)(fetcher.data)\n if (out.ok) cartOptimistic.confirm(id)\n else cartOptimistic.rollback(id, out.reason)\n }\n was.current = busy\n }, [fetcher.state, fetcher.data])\n\n useEffect(\n () => () => {\n if (current.current) cartOptimistic.discard(current.current)\n current.current = null\n },\n [],\n )\n\n return {\n add(input: OptimisticLineInput, submit: () => void): string {\n const prev = current.current\n const id = cartOptimistic.add(input)\n if (prev && prev !== id) cartOptimistic.confirm(prev)\n current.current = id\n try {\n submit()\n } catch (e) {\n current.current = null\n cartOptimistic.rollback(id, describeAddFailure({ message: e instanceof Error ? e.message : undefined }))\n }\n return id\n },\n }\n}\n","import { useEffect, useId, type ElementType, type ReactNode } from 'react'\nimport { formatMoney, type Cart, type CartLine, type Money } from '@asteby/storefront'\nimport { useModalDialog } from '../dialog.js'\nimport { useResolvedLocale } from '../locale.js'\nimport { ProductThumb } from '../ui/product-preview.js'\nimport { closeCartDrawer, isQuietOpen, useCartDrawer } from './store.js'\nimport { cartOptimistic, useOptimisticCart, type OptimisticNotice, type OptimisticRow } from './optimistic.js'\n\n/** Textos del estado optimista. Por omisión es-MX; la tienda puede traducirlos o cambiarlos. */\nexport type CartDrawerOptimisticLabels = {\n /** Estado discreto en la fila provisional. */\n adding: string\n /** Anuncio `aria-live=\"polite\"` al mostrar/confirmar la fila. */\n added: string\n /** Junto al subtotal mientras OPS no confirma. */\n calculating: string\n /** Aviso de error: «No pudimos agregar X: motivo». */\n failed: (title: string, reason: string) => string\n dismiss: string\n}\n\nexport const DEFAULT_OPTIMISTIC_LABELS: CartDrawerOptimisticLabels = {\n adding: 'Agregando…',\n added: 'Agregado al carrito',\n calculating: 'calculando…',\n failed: (title, reason) => `No pudimos agregar ${title}: ${reason}`,\n dismiss: 'Descartar aviso',\n}\n\nexport type CartDrawerLabels = {\n /** Título; el cajón agrega \" (N)\" con la cantidad de unidades. */\n title: string\n /** Texto accesible del botón de cierre. */\n close: string\n empty: string\n emptyHint?: string\n /** Texto del enlace \"seguir comprando\" del estado vacío (se muestra con `paths.browse`). */\n browse?: string\n subtotal: string\n /** Nota bajo el subtotal. */\n note?: string\n checkout: string\n viewCart: string\n remove: string\n increase: string\n decrease: string\n}\n\nexport type CartDrawerPaths = {\n /** Página del carrito: acción de las mutaciones y destino de \"ver carrito\". */\n cart: string\n checkout: string\n browse?: string\n /** Enlace de la línea al producto; sin esto el título no es enlace. */\n product?: (handle: string, variantId: string) => string\n}\n\n/** Contrato de la acción de ruta del carrito de la tienda (nombres de campo e intenciones). */\nexport type CartDrawerFormContract = {\n intentField?: string\n lineField?: string\n quantityField?: string\n updateIntent?: string\n removeIntent?: string\n}\n\nexport type CartDrawerShipping = {\n /** Mensaje ya calculado por la tienda con lo que manda OPS. */\n message: ReactNode\n /** 0..100 */\n percent: number\n reached?: boolean\n progressLabel: string\n}\n\nexport type CartDrawerLineForm = {\n /** Componente de formulario (`'form'`, o `fetcher.Form` del router para mejorar sin recargar). */\n Form: ElementType\n /** Datos en vuelo del formulario, para pintar la cantidad de forma optimista. */\n formData?: FormData | null | undefined\n}\n\nexport type CartDrawerLinkProps = { href: string; className?: string | undefined; children: ReactNode; onClick?: () => void }\n\nexport type CartDrawerSlot =\n | 'dialog' | 'panel' | 'header' | 'title' | 'close' | 'empty' | 'body' | 'shipping' | 'bar' | 'barFill' | 'lines' | 'line'\n | 'image' | 'lineTitle' | 'variant' | 'price' | 'qty' | 'qtyButton' | 'qtyValue' | 'remove' | 'upsell' | 'footer' | 'subtotal'\n | 'note' | 'actions' | 'checkout' | 'viewCart' | 'browse' | 'emptyTitle' | 'emptyHint' | 'shippingMessage' | 'lineMain'\n | 'notices' | 'lineHead' | 'lineText' | 'controls' | 'upsellTitle' | 'subtotalLabel' | 'subtotalValue'\n\nexport type CartDrawerIcons = { close?: ReactNode; decrease?: ReactNode; increase?: ReactNode; shipping?: ReactNode; shippingReached?: ReactNode }\n\nexport type CartDrawerProps = {\n /** `cart.cost` y cada línea vienen de OPS; el cajón no calcula nada. */\n cart: Cart | null | undefined\n labels: CartDrawerLabels\n paths: CartDrawerPaths\n form?: CartDrawerFormContract\n /** Hook que da el formulario de cada línea. Por omisión `<form method=\"post\">` (funciona sin JavaScript). */\n useLineForm?: () => CartDrawerLineForm\n shipping?: CartDrawerShipping | null\n /** Aviso fijo cuando no hay barra de envío (p. ej. \"Envío gratis\"). */\n notice?: ReactNode\n /** Sección \"podría interesarte\" (la tienda decide qué mostrar). */\n upsell?: ReactNode\n upsellTitle?: string\n renderLine?: (line: CartLine, defaults: { controls: ReactNode; price: ReactNode; image: ReactNode }) => ReactNode\n renderImage?: (line: CartLine) => ReactNode\n renderMoney?: (money: Money) => ReactNode\n renderLink?: (props: CartDrawerLinkProps) => ReactNode\n /** Omite el variante cuando es el valor por defecto de OPS (\"Default Title\"). */\n isDefaultVariantTitle?: (title: string) => boolean\n icons?: CartDrawerIcons\n /** Clases por pieza (además de los `data-sf-cart-*`), para tiendas con Tailwind. */\n classNames?: Partial<Record<CartDrawerSlot, string>>\n /** Textos de las filas provisionales y de los avisos (por omisión es-MX). */\n optimisticLabels?: Partial<CartDrawerOptimisticLabels>\n locale?: string\n /** Se llama al abrir/cerrar por cualquier vía. */\n onOpenChange?: (open: boolean) => void\n}\n\nconst DEFAULT_FORM: Required<CartDrawerFormContract> = {\n intentField: 'intent',\n lineField: 'line_id',\n quantityField: 'quantity',\n updateIntent: 'update',\n removeIntent: 'remove',\n}\n\nconst useNativeForm = (): CartDrawerLineForm => ({ Form: 'form' })\n\nconst defaultIsDefaultVariant = (t: string) => /^default title$/i.test(t.trim())\n\nfunction useBodyScrollLock(locked: boolean) {\n useEffect(() => {\n if (!locked || typeof document === 'undefined') return\n const el = document.documentElement\n const prev = el.style.overflow\n el.style.overflow = 'hidden'\n return () => {\n el.style.overflow = prev\n }\n }, [locked])\n}\n\n/**\n * Cajón lateral del carrito (\"Tu carrito (N)\"). Sin textos ni marca: todo llega por props.\n * Se monta una vez (en la raíz) y se abre con `openCartDrawer()` / `useCartDrawer()`.\n * `<dialog>` modal: `role=\"dialog\"`, `aria-modal`, foco atrapado y devuelto al abridor, Esc y clic fuera cierran,\n * el fondo no hace scroll. Las mutaciones usan la acción de ruta de la tienda (formularios nativos que\n * funcionan sin JavaScript, o el `Form` de un fetcher con `useLineForm`). Estilos: `@asteby/storefront-react/cart.css`\n * (tokens de la tienda) y/o `classNames`.\n */\nexport function CartDrawer(props: CartDrawerProps) {\n const { cart, labels, paths, shipping, notice, upsell, upsellTitle, icons, classNames: cx = {}, onOpenChange } = props\n const locale = useResolvedLocale(props.locale)\n const { open } = useCartDrawer()\n const opt = useOptimisticCart()\n const ol = { ...DEFAULT_OPTIMISTIC_LABELS, ...props.optimisticLabels }\n const quiet = open && isQuietOpen()\n const titleId = useId()\n const upsellId = useId()\n const { ref, onBackdropClick } = useModalDialog({ open, onClose: () => closeCartDrawer() })\n useBodyScrollLock(open)\n useEffect(() => {\n onOpenChange?.(open)\n }, [open]) // eslint-disable-line react-hooks/exhaustive-deps\n\n const realLines = cart?.lines.length ?? 0\n const rollbackEmpty = open && quiet && realLines === 0 && opt.rows.length === 0 && opt.notices.length > 0\n useEffect(() => {\n // Sin filas y con un aviso de fallo, un cajón vacío abierto por la acción sería engañoso: se cierra.\n if (rollbackEmpty) closeCartDrawer()\n }, [rollbackEmpty])\n\n if (!open) return opt.notices.length ? <OptimisticNotices notices={opt.notices} labels={ol} classNames={cx} /> : null\n\n const money = props.renderMoney ?? ((m: Money) => formatMoney(m, locale))\n const link = props.renderLink ?? (({ href, className, children, onClick }: CartDrawerLinkProps) => (\n <a href={href} className={className} onClick={onClick}>{children}</a>\n ))\n const pendingByVariant = new Map(opt.rows.map((r) => [r.variantId, r]))\n const count = (cart?.total_quantity ?? 0) + opt.quantity\n const lines = cart?.lines ?? []\n const extraRows = opt.rows.filter((r) => !lines.some((l) => l.merchandise.id === r.variantId))\n const pending = opt.rows.length > 0\n const empty = lines.length === 0 && extraRows.length === 0\n const close = () => closeCartDrawer()\n\n return (\n <dialog\n ref={ref}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n onClick={onBackdropClick}\n onClose={close}\n className={cx.dialog}\n data-sf-cart-drawer=\"\"\n >\n <div className={cx.panel} data-sf-cart-panel=\"\">\n <div className={cx.header} data-sf-cart-header=\"\">\n <h2 id={titleId} tabIndex={-1} className={cx.title} data-sf-cart-title=\"\">\n {labels.title}{count > 0 ? ` (${count})` : ''}\n </h2>\n <button type=\"button\" onClick={close} aria-label={labels.close} className={cx.close} data-sf-cart-close=\"\" data-sf-autofocus={quiet ? undefined : ''}>\n {icons?.close ?? <span aria-hidden=\"true\">✕</span>}\n </button>\n </div>\n\n <OptimisticNotices notices={opt.notices} labels={ol} classNames={cx} />\n <OptimisticStatus kind={opt.announcement.kind} n={opt.announcement.n} text={ol.added} />\n\n {empty ? (\n <div className={cx.empty} data-sf-cart-empty=\"\">\n <p className={cx.emptyTitle} data-sf-cart-empty-title=\"\">{labels.empty}</p>\n {labels.emptyHint ? <p className={cx.emptyHint} data-sf-cart-empty-hint=\"\">{labels.emptyHint}</p> : null}\n {paths.browse && labels.browse ? link({ href: paths.browse, className: cx.browse, children: labels.browse, onClick: close }) : null}\n </div>\n ) : (\n <>\n <div className={cx.body} data-sf-cart-body=\"\">\n {shipping ? (\n <div className={cx.shipping} data-sf-cart-shipping=\"\" data-reached={shipping.reached || undefined}>\n <p className={cx.shippingMessage}>\n {shipping.reached ? icons?.shippingReached : icons?.shipping}\n {shipping.message}\n </p>\n <div\n role=\"progressbar\"\n aria-valuemin={0}\n aria-valuemax={100}\n aria-valuenow={Math.round(shipping.percent)}\n aria-label={shipping.progressLabel}\n className={cx.bar}\n data-sf-cart-bar=\"\"\n >\n <div className={cx.barFill} data-sf-cart-bar-fill=\"\" style={{ width: `${Math.max(0, Math.min(100, shipping.percent))}%` }} />\n </div>\n </div>\n ) : notice ? (\n <p className={[cx.shipping, cx.shippingMessage].filter(Boolean).join(\" \") || undefined} data-sf-cart-shipping=\"\" data-notice=\"\">{icons?.shippingReached}{notice}</p>\n ) : null}\n\n <ul className={cx.lines} data-sf-cart-lines=\"\">\n {lines.map((line) => (\n <DrawerLine key={line.id} line={line} props={props} money={money} link={link} close={close} pending={pendingByVariant.get(line.merchandise.id)} pendingLabel={ol.adding} />\n ))}\n {extraRows.map((row) => (\n <PendingLine key={`pending-${row.variantId}`} row={row} props={props} money={money} label={ol.adding} />\n ))}\n </ul>\n\n {upsell ? (\n <section className={cx.upsell} data-sf-cart-upsell=\"\" aria-labelledby={upsellTitle ? upsellId : undefined}>\n {upsellTitle ? <h3 id={upsellId} className={cx.upsellTitle}>{upsellTitle}</h3> : null}\n {upsell}\n </section>\n ) : null}\n </div>\n\n <div className={cx.footer} data-sf-cart-footer=\"\">\n <div className={cx.subtotal} data-sf-cart-subtotal=\"\" aria-busy={pending || undefined}>\n <span className={cx.subtotalLabel}>{labels.subtotal}</span>\n <span className={cx.subtotalValue} data-sf-cart-subtotal-value=\"\">{cart ? money(cart.cost.subtotal) : null}{pending ? <span data-sf-cart-calculating=\"\">{cart ? ' ' : ''}{ol.calculating}</span> : null}</span>\n </div>\n {labels.note ? <p className={cx.note} data-sf-cart-note=\"\">{labels.note}</p> : null}\n <div className={cx.actions} data-sf-cart-actions=\"\">\n {link({ href: paths.checkout, className: cx.checkout, children: labels.checkout, onClick: close })}\n {link({ href: paths.cart, className: cx.viewCart, children: labels.viewCart, onClick: close })}\n </div>\n </div>\n </>\n )}\n </div>\n </dialog>\n )\n}\n\nfunction DrawerLine({\n line,\n props,\n money,\n link,\n close,\n pending,\n pendingLabel,\n}: {\n pending?: OptimisticRow | undefined\n pendingLabel: string\n line: CartLine\n props: CartDrawerProps\n money: (m: Money) => ReactNode\n link: (p: CartDrawerLinkProps) => ReactNode\n close: () => void\n}) {\n const { labels, paths, icons, classNames: cx = {} } = props\n const f = { ...DEFAULT_FORM, ...props.form }\n const { Form, formData } = (props.useLineForm ?? useNativeForm)()\n const quantity = formData ? Number(formData.get(f.quantityField) ?? 0) : line.quantity + (pending?.quantity ?? 0)\n if (formData && (formData.get(f.intentField) === f.removeIntent || quantity === 0)) return null\n\n const merch = line.merchandise\n const isDefault = props.isDefaultVariantTitle ?? defaultIsDefaultVariant\n const title = merch.product.title\n const href = paths.product?.(merch.product.handle, merch.id)\n const img = merch.image ?? merch.product.featured_image\n const image = props.renderImage ? props.renderImage(line) : <ProductThumb image={img ?? null} name={title} size={80} decorative />\n\n const imageNode = <span className={cx.image} data-sf-cart-image=\"\">{image}</span>\n const price = <span className={cx.price} data-sf-cart-price=\"\">{money(line.cost.total)}</span>\n const controls = (\n <div className={cx.controls} data-sf-cart-controls=\"\">\n <Form method=\"post\" action={paths.cart} className={cx.qty} data-sf-cart-qty=\"\">\n <input type=\"hidden\" name={f.intentField} value={f.updateIntent} />\n <input type=\"hidden\" name={f.lineField} value={line.id} />\n <button type=\"submit\" name={f.quantityField} value={quantity - 1} aria-label={labels.decrease} className={cx.qtyButton} data-sf-cart-qty-button=\"\">\n {icons?.decrease ?? <span aria-hidden=\"true\">−</span>}\n </button>\n <span className={cx.qtyValue} aria-live=\"polite\" data-sf-cart-qty-value=\"\">{quantity}</span>\n <button type=\"submit\" name={f.quantityField} value={quantity + 1} aria-label={labels.increase} className={cx.qtyButton} data-sf-cart-qty-button=\"\">\n {icons?.increase ?? <span aria-hidden=\"true\">+</span>}\n </button>\n </Form>\n <Form method=\"post\" action={paths.cart} data-sf-cart-remove-form=\"\">\n <input type=\"hidden\" name={f.intentField} value={f.removeIntent} />\n <input type=\"hidden\" name={f.lineField} value={line.id} />\n <button type=\"submit\" className={cx.remove} data-sf-cart-remove=\"\">{labels.remove}</button>\n </Form>\n </div>\n )\n\n if (props.renderLine) return <li key={line.id} className={cx.line} data-sf-cart-line=\"\">{props.renderLine(line, { controls, price, image: imageNode })}</li>\n\n return (\n <li className={cx.line} data-sf-cart-line=\"\">\n {imageNode}\n <div className={cx.lineMain} data-sf-cart-line-main=\"\">\n <div className={cx.lineHead} data-sf-cart-line-head=\"\">\n <div className={cx.lineText} data-sf-cart-line-text=\"\">\n {href ? link({ href, className: cx.lineTitle, children: title, onClick: close }) : <span className={cx.lineTitle} data-sf-cart-line-title=\"\">{title}</span>}\n {!isDefault(merch.title) ? <p className={cx.variant} data-sf-cart-variant=\"\">{merch.title}</p> : null}\n </div>\n {price}\n </div>\n {controls}\n {pending ? <p className={cx.variant} data-sf-cart-pending-label=\"\">{pendingLabel}</p> : null}\n </div>\n </li>\n )\n}\n\n/** Fila que existe solo en el navegador hasta que OPS la confirma: sin controles, con el precio unitario mostrado. */\nfunction PendingLine({ row, props, money, label }: { row: OptimisticRow; props: CartDrawerProps; money: (m: Money) => ReactNode; label: string }) {\n const { classNames: cx = {} } = props\n const isDefault = props.isDefaultVariantTitle ?? defaultIsDefaultVariant\n return (\n <li className={cx.line} data-sf-cart-line=\"\" data-sf-cart-line-pending=\"\" aria-busy=\"true\">\n <span className={cx.image} data-sf-cart-image=\"\">\n <ProductThumb image={row.image ?? null} name={row.title} size={80} decorative />\n </span>\n <div className={cx.lineMain} data-sf-cart-line-main=\"\">\n <div className={cx.lineHead} data-sf-cart-line-head=\"\">\n <div className={cx.lineText} data-sf-cart-line-text=\"\">\n <span className={cx.lineTitle} data-sf-cart-line-title=\"\">{row.title}</span>\n {row.variantTitle && !isDefault(row.variantTitle) ? <p className={cx.variant} data-sf-cart-variant=\"\">{row.variantTitle}</p> : null}\n </div>\n {row.price ? <span className={cx.price} data-sf-cart-price=\"\">{money(row.price)}</span> : null}\n </div>\n <p className={cx.variant} data-sf-cart-pending-label=\"\">{label} · ×{row.quantity}</p>\n </div>\n </li>\n )\n}\n\n/** Región `aria-live=\"polite\"`: anuncia «Agregado al carrito». Alterna un espacio fino para repetir el mismo texto. */\nfunction OptimisticStatus({ kind, n, text }: { kind: 'added' | null; n: number; text: string }) {\n return (\n <div role=\"status\" aria-live=\"polite\" data-sf-cart-status=\"\" style={VISUALLY_HIDDEN}>\n {kind === 'added' ? `${text}${n % 2 ? '' : '\\u200B'}` : ''}\n </div>\n )\n}\n\nconst VISUALLY_HIDDEN = { position: 'absolute', width: 1, height: 1, margin: -1, padding: 0, overflow: 'hidden', clip: 'rect(0 0 0 0)', whiteSpace: 'nowrap', border: 0 } as const\n\n/** Avisos de fallo (`role=\"alert\"`, assertive), descartables. Se pintan dentro del cajón abierto o, si está cerrado, sueltos. */\nfunction OptimisticNotices({ notices, labels, classNames: cx }: { notices: readonly OptimisticNotice[]; labels: CartDrawerOptimisticLabels; classNames: Partial<Record<CartDrawerSlot, string>> }) {\n return (\n <div role=\"alert\" aria-live=\"assertive\" data-sf-cart-notices=\"\" className={cx.notices}>\n {notices.map((n) => (\n <p key={n.id} data-sf-cart-notice=\"\">\n <span>{labels.failed(n.title, n.reason)}</span>\n <button type=\"button\" onClick={() => cartOptimistic.dismissNotice(n.id)} aria-label={labels.dismiss} data-sf-cart-notice-dismiss=\"\">\n <span aria-hidden=\"true\">✕</span>\n </button>\n </p>\n ))}\n </div>\n )\n}\n"]}