@asteby/storefront-react 0.14.0 → 0.16.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 +78 -0
- package/dist/account-server.cjs +41 -8
- package/dist/account-server.cjs.map +1 -1
- package/dist/account-server.d.cts +2 -2
- package/dist/account-server.d.ts +2 -2
- package/dist/account-server.js +42 -9
- package/dist/account-server.js.map +1 -1
- package/dist/account.cjs +3 -1
- package/dist/account.cjs.map +1 -1
- package/dist/account.d.cts +2 -2
- package/dist/account.d.ts +2 -2
- package/dist/account.js +3 -1
- package/dist/account.js.map +1 -1
- package/dist/cart.cjs +266 -10
- package/dist/cart.cjs.map +1 -1
- package/dist/cart.css +14 -0
- package/dist/cart.d.cts +125 -2
- package/dist/cart.d.ts +125 -2
- package/dist/cart.js +260 -11
- package/dist/cart.js.map +1 -1
- package/dist/checkout.cjs +24 -3
- package/dist/checkout.cjs.map +1 -1
- package/dist/checkout.d.cts +7 -5
- package/dist/checkout.d.ts +7 -5
- package/dist/checkout.js +24 -3
- package/dist/checkout.js.map +1 -1
- package/dist/chunk-Q7UTJIKY.js +122 -0
- package/dist/chunk-Q7UTJIKY.js.map +1 -0
- package/dist/fiscal-server.cjs +288 -0
- package/dist/fiscal-server.cjs.map +1 -0
- package/dist/fiscal-server.d.cts +97 -0
- package/dist/fiscal-server.d.ts +97 -0
- package/dist/fiscal-server.js +241 -0
- package/dist/fiscal-server.js.map +1 -0
- package/dist/fiscal.cjs +1345 -0
- package/dist/fiscal.cjs.map +1 -0
- package/dist/fiscal.css +343 -0
- package/dist/fiscal.d.cts +313 -0
- package/dist/fiscal.d.ts +313 -0
- package/dist/fiscal.js +1106 -0
- package/dist/fiscal.js.map +1 -0
- package/dist/types-BnvSbV6t.d.cts +68 -0
- package/dist/types-BnvSbV6t.d.ts +68 -0
- package/dist/{types-HcEUd9hW.d.cts → types-D7pVK0GK.d.cts} +5 -0
- package/dist/{types-HcEUd9hW.d.ts → types-D7pVK0GK.d.ts} +5 -0
- package/package.json +23 -2
package/dist/cart.d.cts
CHANGED
|
@@ -2,6 +2,19 @@ import * as react from 'react';
|
|
|
2
2
|
import { ElementType, ReactNode } from 'react';
|
|
3
3
|
import { Cart, CartLine, Money } from '@asteby/storefront';
|
|
4
4
|
|
|
5
|
+
/** Textos del estado optimista. Por omisión es-MX; la tienda puede traducirlos o cambiarlos. */
|
|
6
|
+
type CartDrawerOptimisticLabels = {
|
|
7
|
+
/** Estado discreto en la fila provisional. */
|
|
8
|
+
adding: string;
|
|
9
|
+
/** Anuncio `aria-live="polite"` al mostrar/confirmar la fila. */
|
|
10
|
+
added: string;
|
|
11
|
+
/** Junto al subtotal mientras OPS no confirma. */
|
|
12
|
+
calculating: string;
|
|
13
|
+
/** Aviso de error: «No pudimos agregar X: motivo». */
|
|
14
|
+
failed: (title: string, reason: string) => string;
|
|
15
|
+
dismiss: string;
|
|
16
|
+
};
|
|
17
|
+
declare const DEFAULT_OPTIMISTIC_LABELS: CartDrawerOptimisticLabels;
|
|
5
18
|
type CartDrawerLabels = {
|
|
6
19
|
/** Título; el cajón agrega " (N)" con la cantidad de unidades. */
|
|
7
20
|
title: string;
|
|
@@ -56,7 +69,7 @@ type CartDrawerLinkProps = {
|
|
|
56
69
|
children: ReactNode;
|
|
57
70
|
onClick?: () => void;
|
|
58
71
|
};
|
|
59
|
-
type CartDrawerSlot = 'dialog' | 'panel' | 'header' | 'title' | 'close' | 'empty' | 'body' | 'shipping' | 'bar' | 'barFill' | 'lines' | 'line' | 'image' | 'lineTitle' | 'variant' | 'price' | 'qty' | 'qtyButton' | 'qtyValue' | 'remove' | 'upsell' | 'footer' | 'subtotal' | 'note' | 'actions' | 'checkout' | 'viewCart' | 'browse' | 'emptyTitle' | 'emptyHint' | 'shippingMessage' | 'lineMain' | 'lineHead' | 'lineText' | 'controls' | 'upsellTitle' | 'subtotalLabel' | 'subtotalValue';
|
|
72
|
+
type CartDrawerSlot = 'dialog' | 'panel' | 'header' | 'title' | 'close' | 'empty' | 'body' | 'shipping' | 'bar' | 'barFill' | 'lines' | 'line' | 'image' | 'lineTitle' | 'variant' | 'price' | 'qty' | 'qtyButton' | 'qtyValue' | 'remove' | 'upsell' | 'footer' | 'subtotal' | 'note' | 'actions' | 'checkout' | 'viewCart' | 'browse' | 'emptyTitle' | 'emptyHint' | 'shippingMessage' | 'lineMain' | 'notices' | 'lineHead' | 'lineText' | 'controls' | 'upsellTitle' | 'subtotalLabel' | 'subtotalValue';
|
|
60
73
|
type CartDrawerIcons = {
|
|
61
74
|
close?: ReactNode;
|
|
62
75
|
decrease?: ReactNode;
|
|
@@ -91,6 +104,8 @@ type CartDrawerProps = {
|
|
|
91
104
|
icons?: CartDrawerIcons;
|
|
92
105
|
/** Clases por pieza (además de los `data-sf-cart-*`), para tiendas con Tailwind. */
|
|
93
106
|
classNames?: Partial<Record<CartDrawerSlot, string>>;
|
|
107
|
+
/** Textos de las filas provisionales y de los avisos (por omisión es-MX). */
|
|
108
|
+
optimisticLabels?: Partial<CartDrawerOptimisticLabels>;
|
|
94
109
|
locale?: string;
|
|
95
110
|
/** Se llama al abrir/cerrar por cualquier vía. */
|
|
96
111
|
onOpenChange?: (open: boolean) => void;
|
|
@@ -105,6 +120,114 @@ type CartDrawerProps = {
|
|
|
105
120
|
*/
|
|
106
121
|
declare function CartDrawer(props: CartDrawerProps): react.JSX.Element | null;
|
|
107
122
|
|
|
123
|
+
/** Lo que la tienda sabe del producto al pulsar «Agregar». `price` es el precio unitario ya mostrado en la ficha. */
|
|
124
|
+
type OptimisticLineInput = {
|
|
125
|
+
/** Variante agregada; dos acciones con la misma variante comparten una sola fila. */
|
|
126
|
+
variantId: string;
|
|
127
|
+
title: string;
|
|
128
|
+
variantTitle?: string | null | undefined;
|
|
129
|
+
productHandle?: string | undefined;
|
|
130
|
+
image?: {
|
|
131
|
+
url: string;
|
|
132
|
+
alt?: string | null | undefined;
|
|
133
|
+
} | null | undefined;
|
|
134
|
+
/** Precio unitario mostrado (solo se pinta, no se multiplica ni se suma). */
|
|
135
|
+
price?: Money | null | undefined;
|
|
136
|
+
/** Unidades de ESTA acción (por omisión 1). */
|
|
137
|
+
quantity?: number | undefined;
|
|
138
|
+
/** Clave de idempotencia: repetir `add` con una clave viva devuelve la misma acción sin sumar otra vez. */
|
|
139
|
+
key?: string | undefined;
|
|
140
|
+
};
|
|
141
|
+
type OptimisticAction = Required<Pick<OptimisticLineInput, 'variantId' | 'title'>> & Omit<OptimisticLineInput, 'quantity' | 'key'> & {
|
|
142
|
+
id: string;
|
|
143
|
+
quantity: number;
|
|
144
|
+
key?: string | undefined;
|
|
145
|
+
};
|
|
146
|
+
/** Fila provisional: todas las acciones en vuelo de una misma variante. */
|
|
147
|
+
type OptimisticRow = {
|
|
148
|
+
variantId: string;
|
|
149
|
+
title: string;
|
|
150
|
+
variantTitle?: string | null | undefined;
|
|
151
|
+
productHandle?: string | undefined;
|
|
152
|
+
image?: {
|
|
153
|
+
url: string;
|
|
154
|
+
alt?: string | null | undefined;
|
|
155
|
+
} | null | undefined;
|
|
156
|
+
price?: Money | null | undefined;
|
|
157
|
+
/** Unidades agregadas que OPS aún no confirma. */
|
|
158
|
+
quantity: number;
|
|
159
|
+
actionIds: string[];
|
|
160
|
+
};
|
|
161
|
+
type OptimisticNotice = {
|
|
162
|
+
id: string;
|
|
163
|
+
title: string;
|
|
164
|
+
reason: string;
|
|
165
|
+
};
|
|
166
|
+
type OptimisticSnapshot = {
|
|
167
|
+
rows: readonly OptimisticRow[];
|
|
168
|
+
/** Unidades provisionales en total (para el contador). */
|
|
169
|
+
quantity: number;
|
|
170
|
+
notices: readonly OptimisticNotice[];
|
|
171
|
+
/** Último evento a anunciar en la región `aria-live="polite"`; `n` cambia en cada evento. */
|
|
172
|
+
announcement: {
|
|
173
|
+
kind: 'added' | null;
|
|
174
|
+
n: number;
|
|
175
|
+
};
|
|
176
|
+
};
|
|
177
|
+
type AddOutcome = {
|
|
178
|
+
ok: true;
|
|
179
|
+
} | {
|
|
180
|
+
ok: false;
|
|
181
|
+
reason: string;
|
|
182
|
+
};
|
|
183
|
+
declare const cartOptimistic: {
|
|
184
|
+
/**
|
|
185
|
+
* Inserta YA la fila provisional, sube el contador y abre el cajón sin robar el foco. Devuelve el id de la acción
|
|
186
|
+
* para `confirm` / `rollback`. Una acción por clic: la misma variante suma cantidad en una sola fila.
|
|
187
|
+
*/
|
|
188
|
+
add(input: OptimisticLineInput, options?: {
|
|
189
|
+
open?: boolean;
|
|
190
|
+
timeoutMs?: number;
|
|
191
|
+
}): string;
|
|
192
|
+
/** OPS confirmó: la fila provisional sale y el carrito real (ya en `cart`) ocupa su lugar. */
|
|
193
|
+
confirm(id: string): void;
|
|
194
|
+
/** Falló: se quita la acción (y su cantidad) y se avisa «No pudimos agregar X: motivo». */
|
|
195
|
+
rollback(id: string, reason?: string): void;
|
|
196
|
+
/** Quita la acción sin avisar (el componente que la lanzó se desmontó). */
|
|
197
|
+
discard(id: string): void;
|
|
198
|
+
dismissNotice(id: string): void;
|
|
199
|
+
/** Limpia todo (pruebas, cierre de sesión). */
|
|
200
|
+
reset(): void;
|
|
201
|
+
getSnapshot: () => OptimisticSnapshot;
|
|
202
|
+
};
|
|
203
|
+
/** Estado optimista (filas, contador, avisos). En el servidor siempre vacío. */
|
|
204
|
+
declare function useOptimisticCart(): OptimisticSnapshot;
|
|
205
|
+
/** Contador para la cabecera: unidades del carrito de OPS más las que están en vuelo. */
|
|
206
|
+
declare function useOptimisticCartCount(base: number): number;
|
|
207
|
+
/** Motivo corto en es-MX a partir de un error de la acción (HTTP, código de OPS o red). */
|
|
208
|
+
declare function describeAddFailure(error: {
|
|
209
|
+
status?: number | undefined;
|
|
210
|
+
code?: string | undefined;
|
|
211
|
+
message?: string | undefined;
|
|
212
|
+
} | null | undefined): string;
|
|
213
|
+
type FetcherLike = {
|
|
214
|
+
state: string;
|
|
215
|
+
data?: unknown;
|
|
216
|
+
};
|
|
217
|
+
/** Interpreta `fetcher.data` de la acción de ruta: `{ error }`, `{ success:false }`, `{ ok:false }` o `status >= 400` fallan. */
|
|
218
|
+
declare function defaultAddOutcome(data: unknown): AddOutcome;
|
|
219
|
+
/**
|
|
220
|
+
* Conecta un `useFetcher()` de React Router con el estado optimista. `add(input, submit)` inserta la fila, abre el
|
|
221
|
+
* cajón y llama a `submit()` (el `fetcher.submit(...)` de la tienda); al terminar el fetcher confirma o revierte.
|
|
222
|
+
* Un nuevo `add` con el mismo fetcher antes de terminar da por entregado el anterior (el router lo reemplaza y el
|
|
223
|
+
* carrito real que llega con la revalidación manda). Si el componente se desmonta, las filas se descartan sin aviso.
|
|
224
|
+
*/
|
|
225
|
+
declare function useOptimisticAdd(fetcher: FetcherLike, options?: {
|
|
226
|
+
settle?: (data: unknown) => AddOutcome;
|
|
227
|
+
}): {
|
|
228
|
+
add(input: OptimisticLineInput, submit: () => void): string;
|
|
229
|
+
};
|
|
230
|
+
|
|
108
231
|
/** Abre el cajón del carrito desde cualquier parte. */
|
|
109
232
|
declare function openCartDrawer(): void;
|
|
110
233
|
/** Cierra el cajón del carrito. */
|
|
@@ -141,4 +264,4 @@ type ClickLike = {
|
|
|
141
264
|
*/
|
|
142
265
|
declare function handleCartLinkClick(e: ClickLike): void;
|
|
143
266
|
|
|
144
|
-
export { CartDrawer, type CartDrawerFormContract, type CartDrawerIcons, type CartDrawerLabels, type CartDrawerLineForm, type CartDrawerLinkProps, type CartDrawerPaths, type CartDrawerProps, type CartDrawerShipping, type CartDrawerSlot, closeCartDrawer, handleCartLinkClick, isCartDrawerOpen, openCartDrawer, toggleCartDrawer, useCartDrawer, useOpenCartDrawerWhenDone };
|
|
267
|
+
export { type AddOutcome, CartDrawer, type CartDrawerFormContract, type CartDrawerIcons, type CartDrawerLabels, type CartDrawerLineForm, type CartDrawerLinkProps, type CartDrawerOptimisticLabels, type CartDrawerPaths, type CartDrawerProps, type CartDrawerShipping, type CartDrawerSlot, DEFAULT_OPTIMISTIC_LABELS, type OptimisticAction, type OptimisticLineInput, type OptimisticNotice, type OptimisticRow, type OptimisticSnapshot, cartOptimistic, closeCartDrawer, defaultAddOutcome, describeAddFailure, handleCartLinkClick, isCartDrawerOpen, openCartDrawer, toggleCartDrawer, useCartDrawer, useOpenCartDrawerWhenDone, useOptimisticAdd, useOptimisticCart, useOptimisticCartCount };
|
package/dist/cart.d.ts
CHANGED
|
@@ -2,6 +2,19 @@ import * as react from 'react';
|
|
|
2
2
|
import { ElementType, ReactNode } from 'react';
|
|
3
3
|
import { Cart, CartLine, Money } from '@asteby/storefront';
|
|
4
4
|
|
|
5
|
+
/** Textos del estado optimista. Por omisión es-MX; la tienda puede traducirlos o cambiarlos. */
|
|
6
|
+
type CartDrawerOptimisticLabels = {
|
|
7
|
+
/** Estado discreto en la fila provisional. */
|
|
8
|
+
adding: string;
|
|
9
|
+
/** Anuncio `aria-live="polite"` al mostrar/confirmar la fila. */
|
|
10
|
+
added: string;
|
|
11
|
+
/** Junto al subtotal mientras OPS no confirma. */
|
|
12
|
+
calculating: string;
|
|
13
|
+
/** Aviso de error: «No pudimos agregar X: motivo». */
|
|
14
|
+
failed: (title: string, reason: string) => string;
|
|
15
|
+
dismiss: string;
|
|
16
|
+
};
|
|
17
|
+
declare const DEFAULT_OPTIMISTIC_LABELS: CartDrawerOptimisticLabels;
|
|
5
18
|
type CartDrawerLabels = {
|
|
6
19
|
/** Título; el cajón agrega " (N)" con la cantidad de unidades. */
|
|
7
20
|
title: string;
|
|
@@ -56,7 +69,7 @@ type CartDrawerLinkProps = {
|
|
|
56
69
|
children: ReactNode;
|
|
57
70
|
onClick?: () => void;
|
|
58
71
|
};
|
|
59
|
-
type CartDrawerSlot = 'dialog' | 'panel' | 'header' | 'title' | 'close' | 'empty' | 'body' | 'shipping' | 'bar' | 'barFill' | 'lines' | 'line' | 'image' | 'lineTitle' | 'variant' | 'price' | 'qty' | 'qtyButton' | 'qtyValue' | 'remove' | 'upsell' | 'footer' | 'subtotal' | 'note' | 'actions' | 'checkout' | 'viewCart' | 'browse' | 'emptyTitle' | 'emptyHint' | 'shippingMessage' | 'lineMain' | 'lineHead' | 'lineText' | 'controls' | 'upsellTitle' | 'subtotalLabel' | 'subtotalValue';
|
|
72
|
+
type CartDrawerSlot = 'dialog' | 'panel' | 'header' | 'title' | 'close' | 'empty' | 'body' | 'shipping' | 'bar' | 'barFill' | 'lines' | 'line' | 'image' | 'lineTitle' | 'variant' | 'price' | 'qty' | 'qtyButton' | 'qtyValue' | 'remove' | 'upsell' | 'footer' | 'subtotal' | 'note' | 'actions' | 'checkout' | 'viewCart' | 'browse' | 'emptyTitle' | 'emptyHint' | 'shippingMessage' | 'lineMain' | 'notices' | 'lineHead' | 'lineText' | 'controls' | 'upsellTitle' | 'subtotalLabel' | 'subtotalValue';
|
|
60
73
|
type CartDrawerIcons = {
|
|
61
74
|
close?: ReactNode;
|
|
62
75
|
decrease?: ReactNode;
|
|
@@ -91,6 +104,8 @@ type CartDrawerProps = {
|
|
|
91
104
|
icons?: CartDrawerIcons;
|
|
92
105
|
/** Clases por pieza (además de los `data-sf-cart-*`), para tiendas con Tailwind. */
|
|
93
106
|
classNames?: Partial<Record<CartDrawerSlot, string>>;
|
|
107
|
+
/** Textos de las filas provisionales y de los avisos (por omisión es-MX). */
|
|
108
|
+
optimisticLabels?: Partial<CartDrawerOptimisticLabels>;
|
|
94
109
|
locale?: string;
|
|
95
110
|
/** Se llama al abrir/cerrar por cualquier vía. */
|
|
96
111
|
onOpenChange?: (open: boolean) => void;
|
|
@@ -105,6 +120,114 @@ type CartDrawerProps = {
|
|
|
105
120
|
*/
|
|
106
121
|
declare function CartDrawer(props: CartDrawerProps): react.JSX.Element | null;
|
|
107
122
|
|
|
123
|
+
/** Lo que la tienda sabe del producto al pulsar «Agregar». `price` es el precio unitario ya mostrado en la ficha. */
|
|
124
|
+
type OptimisticLineInput = {
|
|
125
|
+
/** Variante agregada; dos acciones con la misma variante comparten una sola fila. */
|
|
126
|
+
variantId: string;
|
|
127
|
+
title: string;
|
|
128
|
+
variantTitle?: string | null | undefined;
|
|
129
|
+
productHandle?: string | undefined;
|
|
130
|
+
image?: {
|
|
131
|
+
url: string;
|
|
132
|
+
alt?: string | null | undefined;
|
|
133
|
+
} | null | undefined;
|
|
134
|
+
/** Precio unitario mostrado (solo se pinta, no se multiplica ni se suma). */
|
|
135
|
+
price?: Money | null | undefined;
|
|
136
|
+
/** Unidades de ESTA acción (por omisión 1). */
|
|
137
|
+
quantity?: number | undefined;
|
|
138
|
+
/** Clave de idempotencia: repetir `add` con una clave viva devuelve la misma acción sin sumar otra vez. */
|
|
139
|
+
key?: string | undefined;
|
|
140
|
+
};
|
|
141
|
+
type OptimisticAction = Required<Pick<OptimisticLineInput, 'variantId' | 'title'>> & Omit<OptimisticLineInput, 'quantity' | 'key'> & {
|
|
142
|
+
id: string;
|
|
143
|
+
quantity: number;
|
|
144
|
+
key?: string | undefined;
|
|
145
|
+
};
|
|
146
|
+
/** Fila provisional: todas las acciones en vuelo de una misma variante. */
|
|
147
|
+
type OptimisticRow = {
|
|
148
|
+
variantId: string;
|
|
149
|
+
title: string;
|
|
150
|
+
variantTitle?: string | null | undefined;
|
|
151
|
+
productHandle?: string | undefined;
|
|
152
|
+
image?: {
|
|
153
|
+
url: string;
|
|
154
|
+
alt?: string | null | undefined;
|
|
155
|
+
} | null | undefined;
|
|
156
|
+
price?: Money | null | undefined;
|
|
157
|
+
/** Unidades agregadas que OPS aún no confirma. */
|
|
158
|
+
quantity: number;
|
|
159
|
+
actionIds: string[];
|
|
160
|
+
};
|
|
161
|
+
type OptimisticNotice = {
|
|
162
|
+
id: string;
|
|
163
|
+
title: string;
|
|
164
|
+
reason: string;
|
|
165
|
+
};
|
|
166
|
+
type OptimisticSnapshot = {
|
|
167
|
+
rows: readonly OptimisticRow[];
|
|
168
|
+
/** Unidades provisionales en total (para el contador). */
|
|
169
|
+
quantity: number;
|
|
170
|
+
notices: readonly OptimisticNotice[];
|
|
171
|
+
/** Último evento a anunciar en la región `aria-live="polite"`; `n` cambia en cada evento. */
|
|
172
|
+
announcement: {
|
|
173
|
+
kind: 'added' | null;
|
|
174
|
+
n: number;
|
|
175
|
+
};
|
|
176
|
+
};
|
|
177
|
+
type AddOutcome = {
|
|
178
|
+
ok: true;
|
|
179
|
+
} | {
|
|
180
|
+
ok: false;
|
|
181
|
+
reason: string;
|
|
182
|
+
};
|
|
183
|
+
declare const cartOptimistic: {
|
|
184
|
+
/**
|
|
185
|
+
* Inserta YA la fila provisional, sube el contador y abre el cajón sin robar el foco. Devuelve el id de la acción
|
|
186
|
+
* para `confirm` / `rollback`. Una acción por clic: la misma variante suma cantidad en una sola fila.
|
|
187
|
+
*/
|
|
188
|
+
add(input: OptimisticLineInput, options?: {
|
|
189
|
+
open?: boolean;
|
|
190
|
+
timeoutMs?: number;
|
|
191
|
+
}): string;
|
|
192
|
+
/** OPS confirmó: la fila provisional sale y el carrito real (ya en `cart`) ocupa su lugar. */
|
|
193
|
+
confirm(id: string): void;
|
|
194
|
+
/** Falló: se quita la acción (y su cantidad) y se avisa «No pudimos agregar X: motivo». */
|
|
195
|
+
rollback(id: string, reason?: string): void;
|
|
196
|
+
/** Quita la acción sin avisar (el componente que la lanzó se desmontó). */
|
|
197
|
+
discard(id: string): void;
|
|
198
|
+
dismissNotice(id: string): void;
|
|
199
|
+
/** Limpia todo (pruebas, cierre de sesión). */
|
|
200
|
+
reset(): void;
|
|
201
|
+
getSnapshot: () => OptimisticSnapshot;
|
|
202
|
+
};
|
|
203
|
+
/** Estado optimista (filas, contador, avisos). En el servidor siempre vacío. */
|
|
204
|
+
declare function useOptimisticCart(): OptimisticSnapshot;
|
|
205
|
+
/** Contador para la cabecera: unidades del carrito de OPS más las que están en vuelo. */
|
|
206
|
+
declare function useOptimisticCartCount(base: number): number;
|
|
207
|
+
/** Motivo corto en es-MX a partir de un error de la acción (HTTP, código de OPS o red). */
|
|
208
|
+
declare function describeAddFailure(error: {
|
|
209
|
+
status?: number | undefined;
|
|
210
|
+
code?: string | undefined;
|
|
211
|
+
message?: string | undefined;
|
|
212
|
+
} | null | undefined): string;
|
|
213
|
+
type FetcherLike = {
|
|
214
|
+
state: string;
|
|
215
|
+
data?: unknown;
|
|
216
|
+
};
|
|
217
|
+
/** Interpreta `fetcher.data` de la acción de ruta: `{ error }`, `{ success:false }`, `{ ok:false }` o `status >= 400` fallan. */
|
|
218
|
+
declare function defaultAddOutcome(data: unknown): AddOutcome;
|
|
219
|
+
/**
|
|
220
|
+
* Conecta un `useFetcher()` de React Router con el estado optimista. `add(input, submit)` inserta la fila, abre el
|
|
221
|
+
* cajón y llama a `submit()` (el `fetcher.submit(...)` de la tienda); al terminar el fetcher confirma o revierte.
|
|
222
|
+
* Un nuevo `add` con el mismo fetcher antes de terminar da por entregado el anterior (el router lo reemplaza y el
|
|
223
|
+
* carrito real que llega con la revalidación manda). Si el componente se desmonta, las filas se descartan sin aviso.
|
|
224
|
+
*/
|
|
225
|
+
declare function useOptimisticAdd(fetcher: FetcherLike, options?: {
|
|
226
|
+
settle?: (data: unknown) => AddOutcome;
|
|
227
|
+
}): {
|
|
228
|
+
add(input: OptimisticLineInput, submit: () => void): string;
|
|
229
|
+
};
|
|
230
|
+
|
|
108
231
|
/** Abre el cajón del carrito desde cualquier parte. */
|
|
109
232
|
declare function openCartDrawer(): void;
|
|
110
233
|
/** Cierra el cajón del carrito. */
|
|
@@ -141,4 +264,4 @@ type ClickLike = {
|
|
|
141
264
|
*/
|
|
142
265
|
declare function handleCartLinkClick(e: ClickLike): void;
|
|
143
266
|
|
|
144
|
-
export { CartDrawer, type CartDrawerFormContract, type CartDrawerIcons, type CartDrawerLabels, type CartDrawerLineForm, type CartDrawerLinkProps, type CartDrawerPaths, type CartDrawerProps, type CartDrawerShipping, type CartDrawerSlot, closeCartDrawer, handleCartLinkClick, isCartDrawerOpen, openCartDrawer, toggleCartDrawer, useCartDrawer, useOpenCartDrawerWhenDone };
|
|
267
|
+
export { type AddOutcome, CartDrawer, type CartDrawerFormContract, type CartDrawerIcons, type CartDrawerLabels, type CartDrawerLineForm, type CartDrawerLinkProps, type CartDrawerOptimisticLabels, type CartDrawerPaths, type CartDrawerProps, type CartDrawerShipping, type CartDrawerSlot, DEFAULT_OPTIMISTIC_LABELS, type OptimisticAction, type OptimisticLineInput, type OptimisticNotice, type OptimisticRow, type OptimisticSnapshot, cartOptimistic, closeCartDrawer, defaultAddOutcome, describeAddFailure, handleCartLinkClick, isCartDrawerOpen, openCartDrawer, toggleCartDrawer, useCartDrawer, useOpenCartDrawerWhenDone, useOptimisticAdd, useOptimisticCart, useOptimisticCartCount };
|
package/dist/cart.js
CHANGED
|
@@ -7,9 +7,17 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
|
7
7
|
|
|
8
8
|
var isOpen = false;
|
|
9
9
|
var listeners = /* @__PURE__ */ new Set();
|
|
10
|
+
var quietOpen = false;
|
|
11
|
+
function setQuietOpen(next) {
|
|
12
|
+
quietOpen = next;
|
|
13
|
+
}
|
|
14
|
+
function isQuietOpen() {
|
|
15
|
+
return quietOpen;
|
|
16
|
+
}
|
|
10
17
|
function set(next) {
|
|
11
18
|
if (isOpen === next) return;
|
|
12
19
|
isOpen = next;
|
|
20
|
+
if (!next) quietOpen = false;
|
|
13
21
|
for (const l of [...listeners]) l();
|
|
14
22
|
}
|
|
15
23
|
var subscribe = (l) => {
|
|
@@ -49,6 +57,191 @@ function handleCartLinkClick(e) {
|
|
|
49
57
|
e.preventDefault();
|
|
50
58
|
openCartDrawer();
|
|
51
59
|
}
|
|
60
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
61
|
+
var actions = [];
|
|
62
|
+
var notices = [];
|
|
63
|
+
var announcement = { kind: null, n: 0 };
|
|
64
|
+
var seq = 0;
|
|
65
|
+
var timers = /* @__PURE__ */ new Map();
|
|
66
|
+
var listeners2 = /* @__PURE__ */ new Set();
|
|
67
|
+
var snapshot = compute();
|
|
68
|
+
function compute() {
|
|
69
|
+
const byVariant = /* @__PURE__ */ new Map();
|
|
70
|
+
for (const a of actions) {
|
|
71
|
+
const row = byVariant.get(a.variantId);
|
|
72
|
+
if (row) {
|
|
73
|
+
row.quantity += a.quantity;
|
|
74
|
+
row.actionIds.push(a.id);
|
|
75
|
+
} else {
|
|
76
|
+
byVariant.set(a.variantId, {
|
|
77
|
+
variantId: a.variantId,
|
|
78
|
+
title: a.title,
|
|
79
|
+
variantTitle: a.variantTitle,
|
|
80
|
+
productHandle: a.productHandle,
|
|
81
|
+
image: a.image,
|
|
82
|
+
price: a.price,
|
|
83
|
+
quantity: a.quantity,
|
|
84
|
+
actionIds: [a.id]
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const rows = [...byVariant.values()];
|
|
89
|
+
return { rows, quantity: rows.reduce((n, r) => n + r.quantity, 0), notices, announcement };
|
|
90
|
+
}
|
|
91
|
+
function emit() {
|
|
92
|
+
snapshot = compute();
|
|
93
|
+
for (const l of [...listeners2]) l();
|
|
94
|
+
}
|
|
95
|
+
var subscribe2 = (l) => {
|
|
96
|
+
listeners2.add(l);
|
|
97
|
+
return () => {
|
|
98
|
+
listeners2.delete(l);
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
var EMPTY = { rows: [], quantity: 0, notices: [], announcement: { kind: null, n: 0 } };
|
|
102
|
+
function drop(id) {
|
|
103
|
+
const a = actions.find((x) => x.id === id);
|
|
104
|
+
if (!a) return void 0;
|
|
105
|
+
actions = actions.filter((x) => x.id !== id);
|
|
106
|
+
const t = timers.get(id);
|
|
107
|
+
if (t) clearTimeout(t);
|
|
108
|
+
timers.delete(id);
|
|
109
|
+
return a;
|
|
110
|
+
}
|
|
111
|
+
function announce() {
|
|
112
|
+
announcement = { kind: "added", n: announcement.n + 1 };
|
|
113
|
+
}
|
|
114
|
+
var cartOptimistic = {
|
|
115
|
+
/**
|
|
116
|
+
* Inserta YA la fila provisional, sube el contador y abre el cajón sin robar el foco. Devuelve el id de la acción
|
|
117
|
+
* para `confirm` / `rollback`. Una acción por clic: la misma variante suma cantidad en una sola fila.
|
|
118
|
+
*/
|
|
119
|
+
add(input, options = {}) {
|
|
120
|
+
if (input.key) {
|
|
121
|
+
const live = actions.find((a) => a.key === input.key);
|
|
122
|
+
if (live) return live.id;
|
|
123
|
+
}
|
|
124
|
+
const id = `opt-${++seq}`;
|
|
125
|
+
const quantity = Math.max(1, Math.floor(input.quantity ?? 1));
|
|
126
|
+
actions = [...actions, { ...input, id, quantity }];
|
|
127
|
+
const ms = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
128
|
+
if (ms > 0 && typeof setTimeout === "function") {
|
|
129
|
+
timers.set(id, setTimeout(() => cartOptimistic.rollback(id, describeAddFailure({ code: "timeout" })), ms));
|
|
130
|
+
}
|
|
131
|
+
announce();
|
|
132
|
+
if (options.open !== false) {
|
|
133
|
+
setQuietOpen(true);
|
|
134
|
+
openCartDrawer();
|
|
135
|
+
}
|
|
136
|
+
emit();
|
|
137
|
+
return id;
|
|
138
|
+
},
|
|
139
|
+
/** OPS confirmó: la fila provisional sale y el carrito real (ya en `cart`) ocupa su lugar. */
|
|
140
|
+
confirm(id) {
|
|
141
|
+
if (!drop(id)) return;
|
|
142
|
+
announce();
|
|
143
|
+
emit();
|
|
144
|
+
},
|
|
145
|
+
/** Falló: se quita la acción (y su cantidad) y se avisa «No pudimos agregar X: motivo». */
|
|
146
|
+
rollback(id, reason = "unknown") {
|
|
147
|
+
const a = drop(id);
|
|
148
|
+
if (!a) return;
|
|
149
|
+
notices = [...notices, { id: `n-${++seq}`, title: a.title, reason }];
|
|
150
|
+
emit();
|
|
151
|
+
},
|
|
152
|
+
/** Quita la acción sin avisar (el componente que la lanzó se desmontó). */
|
|
153
|
+
discard(id) {
|
|
154
|
+
if (drop(id)) emit();
|
|
155
|
+
},
|
|
156
|
+
dismissNotice(id) {
|
|
157
|
+
notices = notices.filter((n) => n.id !== id);
|
|
158
|
+
emit();
|
|
159
|
+
},
|
|
160
|
+
/** Limpia todo (pruebas, cierre de sesión). */
|
|
161
|
+
reset() {
|
|
162
|
+
for (const t of timers.values()) clearTimeout(t);
|
|
163
|
+
timers.clear();
|
|
164
|
+
actions = [];
|
|
165
|
+
notices = [];
|
|
166
|
+
announcement = { kind: null, n: 0 };
|
|
167
|
+
setQuietOpen(false);
|
|
168
|
+
emit();
|
|
169
|
+
},
|
|
170
|
+
getSnapshot: () => snapshot
|
|
171
|
+
};
|
|
172
|
+
function useOptimisticCart() {
|
|
173
|
+
return useSyncExternalStore(subscribe2, () => snapshot, () => EMPTY);
|
|
174
|
+
}
|
|
175
|
+
function useOptimisticCartCount(base) {
|
|
176
|
+
return base + useOptimisticCart().quantity;
|
|
177
|
+
}
|
|
178
|
+
function describeAddFailure(error) {
|
|
179
|
+
const code = error?.code;
|
|
180
|
+
const status = error?.status;
|
|
181
|
+
if (code === "store_not_ready") return "la tienda est\xE1 cerrada por ahora";
|
|
182
|
+
if (code === "timeout") return "tard\xF3 demasiado, intenta de nuevo";
|
|
183
|
+
if (status === 409 || code === "out_of_stock" || code === "insufficient_stock") return "sin existencias suficientes";
|
|
184
|
+
if (status === 0 || code === "network" || !status && !code) return "revisa tu conexi\xF3n";
|
|
185
|
+
if (status && status >= 500) return "hubo un problema, intenta de nuevo";
|
|
186
|
+
if (status === 404) return "el producto ya no est\xE1 disponible";
|
|
187
|
+
return error?.message?.trim() || "intenta de nuevo";
|
|
188
|
+
}
|
|
189
|
+
function defaultAddOutcome(data) {
|
|
190
|
+
if (!data || typeof data !== "object") return { ok: true };
|
|
191
|
+
const d = data;
|
|
192
|
+
const status = typeof d.status === "number" ? d.status : void 0;
|
|
193
|
+
const err = d.error && typeof d.error === "object" ? d.error : d;
|
|
194
|
+
const code = typeof err.code === "string" ? err.code : void 0;
|
|
195
|
+
const failed = d.error != null || d.ok === false || d.success === false || status !== void 0 && status >= 400;
|
|
196
|
+
if (!failed) return { ok: true };
|
|
197
|
+
return { ok: false, reason: describeAddFailure({ status: status ?? (typeof err.status === "number" ? err.status : void 0), code, message: typeof err.message === "string" ? err.message : void 0 }) };
|
|
198
|
+
}
|
|
199
|
+
function useOptimisticAdd(fetcher, options = {}) {
|
|
200
|
+
const current = useRef(null);
|
|
201
|
+
const was = useRef(false);
|
|
202
|
+
const settleRef = useRef(options.settle);
|
|
203
|
+
settleRef.current = options.settle;
|
|
204
|
+
useEffect(() => {
|
|
205
|
+
const busy = fetcher.state !== "idle";
|
|
206
|
+
const id = current.current;
|
|
207
|
+
if (was.current && !busy && id) {
|
|
208
|
+
current.current = null;
|
|
209
|
+
const out = (settleRef.current ?? defaultAddOutcome)(fetcher.data);
|
|
210
|
+
if (out.ok) cartOptimistic.confirm(id);
|
|
211
|
+
else cartOptimistic.rollback(id, out.reason);
|
|
212
|
+
}
|
|
213
|
+
was.current = busy;
|
|
214
|
+
}, [fetcher.state, fetcher.data]);
|
|
215
|
+
useEffect(
|
|
216
|
+
() => () => {
|
|
217
|
+
if (current.current) cartOptimistic.discard(current.current);
|
|
218
|
+
current.current = null;
|
|
219
|
+
},
|
|
220
|
+
[]
|
|
221
|
+
);
|
|
222
|
+
return {
|
|
223
|
+
add(input, submit) {
|
|
224
|
+
const prev = current.current;
|
|
225
|
+
const id = cartOptimistic.add(input);
|
|
226
|
+
if (prev && prev !== id) cartOptimistic.confirm(prev);
|
|
227
|
+
current.current = id;
|
|
228
|
+
try {
|
|
229
|
+
submit();
|
|
230
|
+
} catch (e) {
|
|
231
|
+
current.current = null;
|
|
232
|
+
cartOptimistic.rollback(id, describeAddFailure({ message: e instanceof Error ? e.message : void 0 }));
|
|
233
|
+
}
|
|
234
|
+
return id;
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
var DEFAULT_OPTIMISTIC_LABELS = {
|
|
239
|
+
adding: "Agregando\u2026",
|
|
240
|
+
added: "Agregado al carrito",
|
|
241
|
+
calculating: "calculando\u2026",
|
|
242
|
+
failed: (title, reason) => `No pudimos agregar ${title}: ${reason}`,
|
|
243
|
+
dismiss: "Descartar aviso"
|
|
244
|
+
};
|
|
52
245
|
var DEFAULT_FORM = {
|
|
53
246
|
intentField: "intent",
|
|
54
247
|
lineField: "line_id",
|
|
@@ -73,6 +266,9 @@ function CartDrawer(props) {
|
|
|
73
266
|
const { cart, labels, paths, shipping, notice, upsell, upsellTitle, icons, classNames: cx = {}, onOpenChange } = props;
|
|
74
267
|
const locale = useResolvedLocale(props.locale);
|
|
75
268
|
const { open } = useCartDrawer();
|
|
269
|
+
const opt = useOptimisticCart();
|
|
270
|
+
const ol = { ...DEFAULT_OPTIMISTIC_LABELS, ...props.optimisticLabels };
|
|
271
|
+
const quiet = open && isQuietOpen();
|
|
76
272
|
const titleId = useId();
|
|
77
273
|
const upsellId = useId();
|
|
78
274
|
const { ref, onBackdropClick } = useModalDialog({ open, onClose: () => closeCartDrawer() });
|
|
@@ -80,12 +276,20 @@ function CartDrawer(props) {
|
|
|
80
276
|
useEffect(() => {
|
|
81
277
|
onOpenChange?.(open);
|
|
82
278
|
}, [open]);
|
|
83
|
-
|
|
279
|
+
const realLines = cart?.lines.length ?? 0;
|
|
280
|
+
const rollbackEmpty = open && quiet && realLines === 0 && opt.rows.length === 0 && opt.notices.length > 0;
|
|
281
|
+
useEffect(() => {
|
|
282
|
+
if (rollbackEmpty) closeCartDrawer();
|
|
283
|
+
}, [rollbackEmpty]);
|
|
284
|
+
if (!open) return opt.notices.length ? /* @__PURE__ */ jsx(OptimisticNotices, { notices: opt.notices, labels: ol, classNames: cx }) : null;
|
|
84
285
|
const money = props.renderMoney ?? ((m) => formatMoney(m, locale));
|
|
85
286
|
const link = props.renderLink ?? (({ href, className, children, onClick }) => /* @__PURE__ */ jsx("a", { href, className, onClick, children }));
|
|
86
|
-
const
|
|
287
|
+
const pendingByVariant = new Map(opt.rows.map((r) => [r.variantId, r]));
|
|
288
|
+
const count = (cart?.total_quantity ?? 0) + opt.quantity;
|
|
87
289
|
const lines = cart?.lines ?? [];
|
|
88
|
-
const
|
|
290
|
+
const extraRows = opt.rows.filter((r) => !lines.some((l) => l.merchandise.id === r.variantId));
|
|
291
|
+
const pending = opt.rows.length > 0;
|
|
292
|
+
const empty = lines.length === 0 && extraRows.length === 0;
|
|
89
293
|
const close = () => closeCartDrawer();
|
|
90
294
|
return /* @__PURE__ */ jsx(
|
|
91
295
|
"dialog",
|
|
@@ -104,8 +308,10 @@ function CartDrawer(props) {
|
|
|
104
308
|
labels.title,
|
|
105
309
|
count > 0 ? ` (${count})` : ""
|
|
106
310
|
] }),
|
|
107
|
-
/* @__PURE__ */ jsx("button", { type: "button", onClick: close, "aria-label": labels.close, className: cx.close, "data-sf-cart-close": "", "data-sf-autofocus": "", children: icons?.close ?? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\u2715" }) })
|
|
311
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: close, "aria-label": labels.close, className: cx.close, "data-sf-cart-close": "", "data-sf-autofocus": quiet ? void 0 : "", children: icons?.close ?? /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\u2715" }) })
|
|
108
312
|
] }),
|
|
313
|
+
/* @__PURE__ */ jsx(OptimisticNotices, { notices: opt.notices, labels: ol, classNames: cx }),
|
|
314
|
+
/* @__PURE__ */ jsx(OptimisticStatus, { kind: opt.announcement.kind, n: opt.announcement.n, text: ol.added }),
|
|
109
315
|
empty ? /* @__PURE__ */ jsxs("div", { className: cx.empty, "data-sf-cart-empty": "", children: [
|
|
110
316
|
/* @__PURE__ */ jsx("p", { className: cx.emptyTitle, "data-sf-cart-empty-title": "", children: labels.empty }),
|
|
111
317
|
labels.emptyHint ? /* @__PURE__ */ jsx("p", { className: cx.emptyHint, "data-sf-cart-empty-hint": "", children: labels.emptyHint }) : null,
|
|
@@ -134,16 +340,25 @@ function CartDrawer(props) {
|
|
|
134
340
|
icons?.shippingReached,
|
|
135
341
|
notice
|
|
136
342
|
] }) : null,
|
|
137
|
-
/* @__PURE__ */
|
|
343
|
+
/* @__PURE__ */ jsxs("ul", { className: cx.lines, "data-sf-cart-lines": "", children: [
|
|
344
|
+
lines.map((line) => /* @__PURE__ */ jsx(DrawerLine, { line, props, money, link, close, pending: pendingByVariant.get(line.merchandise.id), pendingLabel: ol.adding }, line.id)),
|
|
345
|
+
extraRows.map((row) => /* @__PURE__ */ jsx(PendingLine, { row, props, money, label: ol.adding }, `pending-${row.variantId}`))
|
|
346
|
+
] }),
|
|
138
347
|
upsell ? /* @__PURE__ */ jsxs("section", { className: cx.upsell, "data-sf-cart-upsell": "", "aria-labelledby": upsellTitle ? upsellId : void 0, children: [
|
|
139
348
|
upsellTitle ? /* @__PURE__ */ jsx("h3", { id: upsellId, className: cx.upsellTitle, children: upsellTitle }) : null,
|
|
140
349
|
upsell
|
|
141
350
|
] }) : null
|
|
142
351
|
] }),
|
|
143
352
|
/* @__PURE__ */ jsxs("div", { className: cx.footer, "data-sf-cart-footer": "", children: [
|
|
144
|
-
/* @__PURE__ */ jsxs("div", { className: cx.subtotal, "data-sf-cart-subtotal": "", children: [
|
|
353
|
+
/* @__PURE__ */ jsxs("div", { className: cx.subtotal, "data-sf-cart-subtotal": "", "aria-busy": pending || void 0, children: [
|
|
145
354
|
/* @__PURE__ */ jsx("span", { className: cx.subtotalLabel, children: labels.subtotal }),
|
|
146
|
-
/* @__PURE__ */
|
|
355
|
+
/* @__PURE__ */ jsxs("span", { className: cx.subtotalValue, "data-sf-cart-subtotal-value": "", children: [
|
|
356
|
+
cart ? money(cart.cost.subtotal) : null,
|
|
357
|
+
pending ? /* @__PURE__ */ jsxs("span", { "data-sf-cart-calculating": "", children: [
|
|
358
|
+
cart ? " " : "",
|
|
359
|
+
ol.calculating
|
|
360
|
+
] }) : null
|
|
361
|
+
] })
|
|
147
362
|
] }),
|
|
148
363
|
labels.note ? /* @__PURE__ */ jsx("p", { className: cx.note, "data-sf-cart-note": "", children: labels.note }) : null,
|
|
149
364
|
/* @__PURE__ */ jsxs("div", { className: cx.actions, "data-sf-cart-actions": "", children: [
|
|
@@ -161,12 +376,14 @@ function DrawerLine({
|
|
|
161
376
|
props,
|
|
162
377
|
money,
|
|
163
378
|
link,
|
|
164
|
-
close
|
|
379
|
+
close,
|
|
380
|
+
pending,
|
|
381
|
+
pendingLabel
|
|
165
382
|
}) {
|
|
166
383
|
const { labels, paths, icons, classNames: cx = {} } = props;
|
|
167
384
|
const f = { ...DEFAULT_FORM, ...props.form };
|
|
168
385
|
const { Form, formData } = (props.useLineForm ?? useNativeForm)();
|
|
169
|
-
const quantity = formData ? Number(formData.get(f.quantityField) ?? 0) : line.quantity;
|
|
386
|
+
const quantity = formData ? Number(formData.get(f.quantityField) ?? 0) : line.quantity + (pending?.quantity ?? 0);
|
|
170
387
|
if (formData && (formData.get(f.intentField) === f.removeIntent || quantity === 0)) return null;
|
|
171
388
|
const merch = line.merchandise;
|
|
172
389
|
const isDefault = props.isDefaultVariantTitle ?? defaultIsDefaultVariant;
|
|
@@ -201,11 +418,43 @@ function DrawerLine({
|
|
|
201
418
|
] }),
|
|
202
419
|
price
|
|
203
420
|
] }),
|
|
204
|
-
controls
|
|
421
|
+
controls,
|
|
422
|
+
pending ? /* @__PURE__ */ jsx("p", { className: cx.variant, "data-sf-cart-pending-label": "", children: pendingLabel }) : null
|
|
423
|
+
] })
|
|
424
|
+
] });
|
|
425
|
+
}
|
|
426
|
+
function PendingLine({ row, props, money, label }) {
|
|
427
|
+
const { classNames: cx = {} } = props;
|
|
428
|
+
const isDefault = props.isDefaultVariantTitle ?? defaultIsDefaultVariant;
|
|
429
|
+
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 }),
|
|
431
|
+
/* @__PURE__ */ jsxs("div", { className: cx.lineMain, "data-sf-cart-line-main": "", children: [
|
|
432
|
+
/* @__PURE__ */ jsxs("div", { className: cx.lineHead, "data-sf-cart-line-head": "", children: [
|
|
433
|
+
/* @__PURE__ */ jsxs("div", { className: cx.lineText, "data-sf-cart-line-text": "", children: [
|
|
434
|
+
/* @__PURE__ */ jsx("span", { className: cx.lineTitle, "data-sf-cart-line-title": "", children: row.title }),
|
|
435
|
+
row.variantTitle && !isDefault(row.variantTitle) ? /* @__PURE__ */ jsx("p", { className: cx.variant, "data-sf-cart-variant": "", children: row.variantTitle }) : null
|
|
436
|
+
] }),
|
|
437
|
+
row.price ? /* @__PURE__ */ jsx("span", { className: cx.price, "data-sf-cart-price": "", children: money(row.price) }) : null
|
|
438
|
+
] }),
|
|
439
|
+
/* @__PURE__ */ jsxs("p", { className: cx.variant, "data-sf-cart-pending-label": "", children: [
|
|
440
|
+
label,
|
|
441
|
+
" \xB7 \xD7",
|
|
442
|
+
row.quantity
|
|
443
|
+
] })
|
|
205
444
|
] })
|
|
206
445
|
] });
|
|
207
446
|
}
|
|
447
|
+
function OptimisticStatus({ kind, n, text }) {
|
|
448
|
+
return /* @__PURE__ */ jsx("div", { role: "status", "aria-live": "polite", "data-sf-cart-status": "", style: VISUALLY_HIDDEN, children: kind === "added" ? `${text}${n % 2 ? "" : "\u200B"}` : "" });
|
|
449
|
+
}
|
|
450
|
+
var VISUALLY_HIDDEN = { position: "absolute", width: 1, height: 1, margin: -1, padding: 0, overflow: "hidden", clip: "rect(0 0 0 0)", whiteSpace: "nowrap", border: 0 };
|
|
451
|
+
function OptimisticNotices({ notices: notices2, labels, classNames: cx }) {
|
|
452
|
+
return /* @__PURE__ */ jsx("div", { role: "alert", "aria-live": "assertive", "data-sf-cart-notices": "", className: cx.notices, children: notices2.map((n) => /* @__PURE__ */ jsxs("p", { "data-sf-cart-notice": "", children: [
|
|
453
|
+
/* @__PURE__ */ jsx("span", { children: labels.failed(n.title, n.reason) }),
|
|
454
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => cartOptimistic.dismissNotice(n.id), "aria-label": labels.dismiss, "data-sf-cart-notice-dismiss": "", children: /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\u2715" }) })
|
|
455
|
+
] }, n.id)) });
|
|
456
|
+
}
|
|
208
457
|
|
|
209
|
-
export { CartDrawer, closeCartDrawer, handleCartLinkClick, isCartDrawerOpen, openCartDrawer, toggleCartDrawer, useCartDrawer, useOpenCartDrawerWhenDone };
|
|
458
|
+
export { CartDrawer, DEFAULT_OPTIMISTIC_LABELS, cartOptimistic, closeCartDrawer, defaultAddOutcome, describeAddFailure, handleCartLinkClick, isCartDrawerOpen, openCartDrawer, toggleCartDrawer, useCartDrawer, useOpenCartDrawerWhenDone, useOptimisticAdd, useOptimisticCart, useOptimisticCartCount };
|
|
210
459
|
//# sourceMappingURL=cart.js.map
|
|
211
460
|
//# sourceMappingURL=cart.js.map
|