@develemit/storefront 0.7.0 → 0.8.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/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## 0.8.0 (2026-11-12)
2
+
3
+ ### Features
4
+
5
+ - `ProductDetailView` gains `backHref` and `backLabel` (default "Back to the shop") for a link above the gallery. The title is now display-size and bold, the detail column spacing is looser, and a sold product mutes its gallery image like the shop card.
6
+ - **Changed:** `AddToCartButton` renders a non-interactive `.sf-sold-note` span (label `labels.sold`, default "Sold") instead of a disabled button when the product is sold. Reserved products still show the disabled button with `labels.unavailable`.
7
+ - Shop grid: more space between the category chips and the grid, equal-height cards with aligned bodies, and the empty-state content is centred.
8
+ - **Changed:** `CheckoutSuccessView` fills the `labels.reference` `{id}` placeholder with the short reference from `formatOrderRef` (for example `#A1B2C3D4`) instead of the full order id. A custom label that already contains a `#` before `{id}` now shows two; drop it.
9
+
1
10
  ## 0.7.0 (2026-11-04)
2
11
 
3
12
  ### Features
@@ -3,6 +3,7 @@ export interface AddToCartLabels {
3
3
  add: string;
4
4
  inCart: string;
5
5
  unavailable: string;
6
+ sold: string;
6
7
  }
7
8
  export declare const defaultAddToCartLabels: AddToCartLabels;
8
9
  export interface AddToCartButtonProps {
package/dist/index.js CHANGED
@@ -2,9 +2,9 @@
2
2
  import { n as e, t } from "./sort-for-display-Cj6GMeCY.js";
3
3
  import { createContext as n, useCallback as r, useContext as i, useEffect as a, useMemo as o, useReducer as s, useRef as c, useState as l } from "react";
4
4
  import { Fragment as u, jsx as d, jsxs as f } from "react/jsx-runtime";
5
- import { cartLineSchema as p } from "@develemit/commerce-contracts";
5
+ import { cartLineSchema as p, formatOrderRef as m } from "@develemit/commerce-contracts";
6
6
  // src/lib/theme.ts
7
- var m = {
7
+ var h = {
8
8
  colorBg: "--sf-color-bg",
9
9
  colorSurface: "--sf-color-surface",
10
10
  colorFg: "--sf-color-fg",
@@ -18,16 +18,16 @@ var m = {
18
18
  fontBody: "--sf-font-body",
19
19
  spaceUnit: "--sf-space-unit"
20
20
  };
21
- function h(e) {
21
+ function g(e) {
22
22
  let t = {};
23
- for (let n of Object.keys(m)) {
23
+ for (let n of Object.keys(h)) {
24
24
  let r = e[n];
25
- r !== void 0 && (t[m[n]] = r);
25
+ r !== void 0 && (t[h[n]] = r);
26
26
  }
27
27
  return t;
28
28
  }
29
29
  // src/lib/provider.tsx
30
- var g = {
30
+ var _ = {
31
31
  renderLink: ({ href: e, children: t, className: n }) => /* @__PURE__ */ d("a", {
32
32
  href: e,
33
33
  className: n,
@@ -47,27 +47,27 @@ var g = {
47
47
  product: (e) => `/shop/${e}`,
48
48
  category: (e) => `/shop?category=${e}`
49
49
  }
50
- }, _ = n(g);
51
- function v({ children: e, hrefs: t, ...n }) {
50
+ }, v = n(_);
51
+ function y({ children: e, hrefs: t, ...n }) {
52
52
  let r = {
53
- ...g,
53
+ ..._,
54
54
  ...n,
55
55
  hrefs: {
56
- ...g.hrefs,
56
+ ..._.hrefs,
57
57
  ...t
58
58
  }
59
59
  };
60
- return /* @__PURE__ */ d(_.Provider, {
60
+ return /* @__PURE__ */ d(v.Provider, {
61
61
  value: r,
62
62
  children: e
63
63
  });
64
64
  }
65
- function y() {
66
- return i(_);
65
+ function b() {
66
+ return i(v);
67
67
  }
68
68
  // src/components/price-tag/price-tag.tsx
69
- function b({ priceCents: t, currency: n, sold: r = !1, className: i }) {
70
- let { locale: a } = y(), o = [
69
+ function x({ priceCents: t, currency: n, sold: r = !1, className: i }) {
70
+ let { locale: a } = b(), o = [
71
71
  "sf-price",
72
72
  r && "sf-price--sold",
73
73
  i
@@ -78,15 +78,15 @@ function b({ priceCents: t, currency: n, sold: r = !1, className: i }) {
78
78
  });
79
79
  }
80
80
  // src/components/availability-badge/availability-badge.tsx
81
- var x = {
81
+ var S = {
82
82
  oneOfAKind: "One of a kind",
83
83
  sold: "Sold",
84
84
  reserved: "On hold",
85
85
  available: "Available"
86
86
  };
87
- function S({ availability: e, quantity: t, labels: n, className: r }) {
87
+ function C({ availability: e, quantity: t, labels: n, className: r }) {
88
88
  let i = {
89
- ...x,
89
+ ...S,
90
90
  ...n
91
91
  }, a, o;
92
92
  e === "sold" ? (a = "sold", o = i.sold) : e === "reserved" ? (a = "reserved", o = i.reserved) : t === 1 ? (a = "unique", o = i.oneOfAKind) : (a = "available", o = i.available);
@@ -102,8 +102,8 @@ function S({ availability: e, quantity: t, labels: n, className: r }) {
102
102
  });
103
103
  }
104
104
  // src/components/product-card/product-card.tsx
105
- function C({ product: e, labels: t, sizes: n, priority: r }) {
106
- let { renderLink: i, renderImage: a, hrefs: o } = y(), s = e.availability === "sold", c = e.images[0], l = ["sf-card", s && "sf-card--sold"].filter(Boolean).join(" ");
105
+ function w({ product: e, labels: t, sizes: n, priority: r }) {
106
+ let { renderLink: i, renderImage: a, hrefs: o } = b(), s = e.availability === "sold", c = e.images[0], l = ["sf-card", s && "sf-card--sold"].filter(Boolean).join(" ");
107
107
  return /* @__PURE__ */ d("article", {
108
108
  className: l,
109
109
  children: i({
@@ -128,12 +128,12 @@ function C({ product: e, labels: t, sizes: n, priority: r }) {
128
128
  className: "sf-card__title",
129
129
  children: e.name
130
130
  }),
131
- /* @__PURE__ */ d(b, {
131
+ /* @__PURE__ */ d(x, {
132
132
  priceCents: e.priceCents,
133
133
  currency: e.currency,
134
134
  sold: s
135
135
  }),
136
- /* @__PURE__ */ d(S, {
136
+ /* @__PURE__ */ d(C, {
137
137
  availability: e.availability,
138
138
  quantity: e.quantity,
139
139
  labels: t
@@ -144,7 +144,7 @@ function C({ product: e, labels: t, sizes: n, priority: r }) {
144
144
  });
145
145
  }
146
146
  // src/components/product-grid/product-grid.tsx
147
- function w({ products: e, emptyState: t, labels: n, priorityCount: r = 0 }) {
147
+ function T({ products: e, emptyState: t, labels: n, priorityCount: r = 0 }) {
148
148
  return e.length === 0 ? /* @__PURE__ */ d("div", {
149
149
  className: "sf-grid-empty",
150
150
  children: t ?? "No products found."
@@ -152,7 +152,7 @@ function w({ products: e, emptyState: t, labels: n, priorityCount: r = 0 }) {
152
152
  className: "sf-grid",
153
153
  children: e.map((e, t) => /* @__PURE__ */ d("li", {
154
154
  className: "sf-grid__item",
155
- children: /* @__PURE__ */ d(C, {
155
+ children: /* @__PURE__ */ d(w, {
156
156
  product: e,
157
157
  labels: n,
158
158
  priority: t < r
@@ -161,8 +161,8 @@ function w({ products: e, emptyState: t, labels: n, priorityCount: r = 0 }) {
161
161
  });
162
162
  }
163
163
  // src/components/category-filter/category-filter.tsx
164
- function T({ categories: e, selected: t, onSelect: n, allLabel: r = "All", label: i = "Categories", formatLabel: a, sortCategories: o }) {
165
- let { renderLink: s, hrefs: c } = y(), l = o ? [...e].sort(o) : e, u = n !== void 0, p = (e, r, i) => {
164
+ function E({ categories: e, selected: t, onSelect: n, allLabel: r = "All", label: i = "Categories", formatLabel: a, sortCategories: o }) {
165
+ let { renderLink: s, hrefs: c } = b(), l = o ? [...e].sort(o) : e, u = n !== void 0, p = (e, r, i) => {
166
166
  let a = t === e, o = `sf-filter__item${a ? " sf-filter__item--active" : ""}`;
167
167
  return /* @__PURE__ */ d("li", { children: u ? /* @__PURE__ */ d("button", {
168
168
  type: "button",
@@ -186,8 +186,8 @@ function T({ categories: e, selected: t, onSelect: n, allLabel: r = "All", label
186
186
  });
187
187
  }
188
188
  // src/components/product-gallery/product-gallery.tsx
189
- function E({ images: e, sizes: t }) {
190
- let { renderImage: n } = y(), [r, i] = l(0);
189
+ function D({ images: e, sizes: t }) {
190
+ let { renderImage: n } = b(), [r, i] = l(0);
191
191
  return e.length === 0 ? null : /* @__PURE__ */ f("div", {
192
192
  className: "sf-gallery",
193
193
  role: "group",
@@ -233,21 +233,21 @@ function E({ images: e, sizes: t }) {
233
233
  });
234
234
  }
235
235
  // src/views/shop-view.tsx
236
- function D({ products: e, categories: n, activeCategory: r, slots: i = {}, labels: a, allLabel: o, formatCategoryLabel: s, sortCategories: c, nextHref: l, loadMoreLabel: u = "Load more" }) {
237
- let { renderLink: p } = y();
236
+ function O({ products: e, categories: n, activeCategory: r, slots: i = {}, labels: a, allLabel: o, formatCategoryLabel: s, sortCategories: c, nextHref: l, loadMoreLabel: u = "Load more" }) {
237
+ let { renderLink: p } = b();
238
238
  return /* @__PURE__ */ f("div", {
239
239
  className: "sf-shop",
240
240
  children: [
241
241
  i.header,
242
242
  i.intro,
243
- /* @__PURE__ */ d(T, {
243
+ /* @__PURE__ */ d(E, {
244
244
  categories: n,
245
245
  selected: r,
246
246
  allLabel: o,
247
247
  formatLabel: s,
248
248
  sortCategories: c
249
249
  }),
250
- /* @__PURE__ */ d(w, {
250
+ /* @__PURE__ */ d(T, {
251
251
  products: t(e),
252
252
  emptyState: i.emptyState,
253
253
  labels: a,
@@ -265,50 +265,59 @@ function D({ products: e, categories: n, activeCategory: r, slots: i = {}, label
265
265
  });
266
266
  }
267
267
  // src/views/product-detail-view.tsx
268
- function O({ product: e, slots: t = {}, labels: n }) {
268
+ function k({ product: e, slots: t = {}, labels: n, backHref: r, backLabel: i = "Back to the shop" }) {
269
+ let { renderLink: a } = b(), o = e.availability === "sold";
269
270
  return /* @__PURE__ */ f("article", {
270
- className: "sf-detail",
271
- children: [/* @__PURE__ */ d(E, { images: e.images }), /* @__PURE__ */ f("div", {
272
- className: "sf-detail__body",
273
- children: [
274
- /* @__PURE__ */ d("h1", {
275
- className: "sf-detail__title",
276
- children: e.name
277
- }),
278
- /* @__PURE__ */ d(b, {
279
- priceCents: e.priceCents,
280
- currency: e.currency
281
- }),
282
- /* @__PURE__ */ d(S, {
283
- availability: e.availability,
284
- quantity: e.quantity,
285
- labels: n
286
- }),
287
- /* @__PURE__ */ d("p", {
288
- className: "sf-detail__description",
289
- children: e.description
290
- }),
291
- (e.materials || e.dimensions) && /* @__PURE__ */ f("dl", {
292
- className: "sf-detail__specs",
293
- children: [e.materials && /* @__PURE__ */ f(u, { children: [/* @__PURE__ */ d("dt", { children: "Materials" }), /* @__PURE__ */ d("dd", { children: e.materials })] }), e.dimensions && /* @__PURE__ */ f(u, { children: [/* @__PURE__ */ d("dt", { children: "Dimensions" }), /* @__PURE__ */ d("dd", { children: e.dimensions })] })]
294
- }),
295
- t.actions && /* @__PURE__ */ d("div", {
296
- className: "sf-detail__actions",
297
- children: t.actions
298
- }),
299
- t.afterDetails
300
- ]
301
- })]
271
+ className: o ? "sf-detail sf-detail--sold" : "sf-detail",
272
+ children: [
273
+ r && a({
274
+ href: r,
275
+ className: "sf-detail__back",
276
+ children: i
277
+ }),
278
+ /* @__PURE__ */ d(D, { images: e.images }),
279
+ /* @__PURE__ */ f("div", {
280
+ className: "sf-detail__body",
281
+ children: [
282
+ /* @__PURE__ */ d("h1", {
283
+ className: "sf-detail__title",
284
+ children: e.name
285
+ }),
286
+ /* @__PURE__ */ d(x, {
287
+ priceCents: e.priceCents,
288
+ currency: e.currency
289
+ }),
290
+ /* @__PURE__ */ d(C, {
291
+ availability: e.availability,
292
+ quantity: e.quantity,
293
+ labels: n
294
+ }),
295
+ /* @__PURE__ */ d("p", {
296
+ className: "sf-detail__description",
297
+ children: e.description
298
+ }),
299
+ (e.materials || e.dimensions) && /* @__PURE__ */ f("dl", {
300
+ className: "sf-detail__specs",
301
+ children: [e.materials && /* @__PURE__ */ f(u, { children: [/* @__PURE__ */ d("dt", { children: "Materials" }), /* @__PURE__ */ d("dd", { children: e.materials })] }), e.dimensions && /* @__PURE__ */ f(u, { children: [/* @__PURE__ */ d("dt", { children: "Dimensions" }), /* @__PURE__ */ d("dd", { children: e.dimensions })] })]
302
+ }),
303
+ t.actions && /* @__PURE__ */ d("div", {
304
+ className: "sf-detail__actions",
305
+ children: t.actions
306
+ }),
307
+ t.afterDetails
308
+ ]
309
+ })
310
+ ]
302
311
  });
303
312
  }
304
313
  // src/views/featured-products.tsx
305
- function k({ products: e, heading: t = "Featured", labels: n }) {
314
+ function A({ products: e, heading: t = "Featured", labels: n }) {
306
315
  return e.length === 0 ? null : /* @__PURE__ */ f("section", {
307
316
  className: "sf-featured",
308
317
  children: [/* @__PURE__ */ d("h2", {
309
318
  className: "sf-featured__heading",
310
319
  children: t
311
- }), /* @__PURE__ */ d(w, {
320
+ }), /* @__PURE__ */ d(T, {
312
321
  products: e,
313
322
  labels: n,
314
323
  priorityCount: 6
@@ -316,8 +325,8 @@ function k({ products: e, heading: t = "Featured", labels: n }) {
316
325
  });
317
326
  }
318
327
  // src/cart/cart-reducer.ts
319
- var A = { lines: [] };
320
- function j(e, t) {
328
+ var j = { lines: [] };
329
+ function M(e, t) {
321
330
  let n = e.lines[0];
322
331
  if (n && n.currency !== t.currency) return {
323
332
  state: e,
@@ -352,42 +361,42 @@ function j(e, t) {
352
361
  capped: r.quantity + t.quantity > r.maxQuantity
353
362
  };
354
363
  }
355
- function M(e, t) {
364
+ function N(e, t) {
356
365
  switch (t.type) {
357
- case "add": return j(e, t.line).state;
366
+ case "add": return M(e, t.line).state;
358
367
  case "remove": return { lines: e.lines.filter((e) => e.productId !== t.productId) };
359
- case "setQuantity": return t.quantity <= 0 ? M(e, {
368
+ case "setQuantity": return t.quantity <= 0 ? N(e, {
360
369
  type: "remove",
361
370
  productId: t.productId
362
371
  }) : { lines: e.lines.map((e) => e.productId === t.productId ? {
363
372
  ...e,
364
373
  quantity: Math.min(t.quantity, e.maxQuantity)
365
374
  } : e) };
366
- case "clear": return A;
375
+ case "clear": return j;
367
376
  case "hydrate": return t.state;
368
377
  }
369
378
  }
370
- function N(e) {
379
+ function P(e) {
371
380
  return e.lines.reduce((e, t) => e + t.priceCents * t.quantity, 0);
372
381
  }
373
- function P(e) {
382
+ function F(e) {
374
383
  return e.lines.reduce((e, t) => e + t.quantity, 0);
375
384
  }
376
385
  // src/cart/cart-storage.ts
377
- function F(e = "sf-cart") {
386
+ function I(e = "sf-cart") {
378
387
  return {
379
388
  load() {
380
- if (typeof window > "u") return A;
389
+ if (typeof window > "u") return j;
381
390
  try {
382
391
  let t = window.localStorage.getItem(e);
383
- if (!t) return A;
392
+ if (!t) return j;
384
393
  let n = JSON.parse(t);
385
394
  return { lines: (Array.isArray(n?.lines) ? n.lines : []).flatMap((e) => {
386
395
  let t = p.safeParse(e);
387
396
  return t.success ? [t.data] : [];
388
397
  }) };
389
398
  } catch {
390
- return A;
399
+ return j;
391
400
  }
392
401
  },
393
402
  save(t) {
@@ -398,9 +407,9 @@ function F(e = "sf-cart") {
398
407
  };
399
408
  }
400
409
  // src/cart/cart-provider.tsx
401
- var I = n(null), L = F();
402
- function ee({ storage: e = L, children: t }) {
403
- let [n, i] = s(M, A), [u, f] = l(!1), [p, m] = l(!1), h = c(n);
410
+ var L = n(null), R = I();
411
+ function z({ storage: e = R, children: t }) {
412
+ let [n, i] = s(N, j), [u, f] = l(!1), [p, m] = l(!1), h = c(n);
404
413
  a(() => {
405
414
  h.current = n;
406
415
  }, [n]), a(() => {
@@ -421,15 +430,15 @@ function ee({ storage: e = L, children: t }) {
421
430
  e
422
431
  ]);
423
432
  let g = r((e) => {
424
- let t = j(h.current, e);
433
+ let t = M(h.current, e);
425
434
  return t.added && (h.current = t.state, i({
426
435
  type: "add",
427
436
  line: e
428
437
  })), t;
429
438
  }, []), _ = o(() => ({
430
439
  lines: n.lines,
431
- itemCount: P(n),
432
- subtotalCents: N(n),
440
+ itemCount: F(n),
441
+ subtotalCents: P(n),
433
442
  currency: n.lines[0]?.currency ?? null,
434
443
  hydrated: u,
435
444
  isOpen: p,
@@ -452,18 +461,18 @@ function ee({ storage: e = L, children: t }) {
452
461
  p,
453
462
  g
454
463
  ]);
455
- return /* @__PURE__ */ d(I.Provider, {
464
+ return /* @__PURE__ */ d(L.Provider, {
456
465
  value: _,
457
466
  children: t
458
467
  });
459
468
  }
460
- function R() {
461
- let e = i(I);
469
+ function B() {
470
+ let e = i(L);
462
471
  if (!e) throw Error("useCart must be used inside <CartProvider>");
463
472
  return e;
464
473
  }
465
474
  // src/components/cart-summary/discount-types.ts
466
- var z = {
475
+ var V = {
467
476
  toggle: "Have a code?",
468
477
  field: "Discount code",
469
478
  apply: "Apply",
@@ -480,17 +489,17 @@ var z = {
480
489
  currency: "That code does not apply to this cart."
481
490
  }
482
491
  };
483
- function B(e, t) {
484
- return t?.[e] ?? z.reasons[e];
492
+ function H(e, t) {
493
+ return t?.[e] ?? V.reasons[e];
485
494
  }
486
495
  // src/cart/use-checkout.ts
487
- var V = {
496
+ var U = {
488
497
  outOfStock: "Some items just sold or are on hold, so we removed them from your cart.",
489
498
  unavailable: "Checkout is unavailable right now. Please try again."
490
- }, te = (e) => window.location.assign(e);
491
- function ne({ startCheckout: e, onRedirect: t = te, onDiscountInvalid: n, labels: i }) {
492
- let a = R(), [o, s] = l(!1), [c, u] = l(null), { lines: d, remove: f } = a, p = {
493
- ...V,
499
+ }, W = (e) => window.location.assign(e);
500
+ function G({ startCheckout: e, onRedirect: t = W, onDiscountInvalid: n, labels: i }) {
501
+ let a = B(), [o, s] = l(!1), [c, u] = l(null), { lines: d, remove: f } = a, p = {
502
+ ...U,
494
503
  ...i
495
504
  };
496
505
  return {
@@ -505,7 +514,7 @@ function ne({ startCheckout: e, onRedirect: t = te, onDiscountInvalid: n, labels
505
514
  t(i.url);
506
515
  return;
507
516
  }
508
- i.error === "discount_invalid" ? (u(B(i.reason, p.discountReasons)), n?.(i.reason)) : (i.productIds.forEach(f), u(p.outOfStock));
517
+ i.error === "discount_invalid" ? (u(H(i.reason, p.discountReasons)), n?.(i.reason)) : (i.productIds.forEach(f), u(p.outOfStock));
509
518
  } catch {
510
519
  u(p.unavailable);
511
520
  }
@@ -524,17 +533,18 @@ function ne({ startCheckout: e, onRedirect: t = te, onDiscountInvalid: n, labels
524
533
  };
525
534
  }
526
535
  // src/components/add-to-cart-button/add-to-cart-button.tsx
527
- var H = {
536
+ var K = {
528
537
  add: "Add to cart",
529
538
  inCart: "In your cart",
530
- unavailable: "Unavailable"
539
+ unavailable: "Unavailable",
540
+ sold: "Sold"
531
541
  };
532
- function U({ product: e, labels: t, openOnAdd: n = !0, className: r }) {
533
- let i = R(), a = {
534
- ...H,
542
+ function q({ product: e, labels: t, openOnAdd: n = !0, className: r }) {
543
+ let i = B(), a = {
544
+ ...K,
535
545
  ...t
536
- }, o = i.lines.find((t) => t.productId === e.id)?.quantity ?? 0, s = e.availability !== "available", c = o >= e.quantity, l = !i.hydrated || s || c, u = s ? a.unavailable : c ? a.inCart : a.add;
537
- function f() {
546
+ }, o = i.lines.find((t) => t.productId === e.id)?.quantity ?? 0, s = e.availability === "sold", c = e.availability !== "available", l = o >= e.quantity, u = !i.hydrated || c || l, f = c ? a.unavailable : l ? a.inCart : a.add;
547
+ function p() {
538
548
  i.add({
539
549
  productId: e.id,
540
550
  slug: e.slug,
@@ -546,22 +556,25 @@ function U({ product: e, labels: t, openOnAdd: n = !0, className: r }) {
546
556
  maxQuantity: e.quantity
547
557
  }).added && n && i.open();
548
558
  }
549
- return /* @__PURE__ */ d("button", {
559
+ return s ? /* @__PURE__ */ d("span", {
560
+ className: ["sf-sold-note", r].filter(Boolean).join(" "),
561
+ children: a.sold
562
+ }) : /* @__PURE__ */ d("button", {
550
563
  type: "button",
551
564
  className: ["sf-button", r].filter(Boolean).join(" "),
552
- disabled: l,
553
- onClick: f,
554
- children: u
565
+ disabled: u,
566
+ onClick: p,
567
+ children: f
555
568
  });
556
569
  }
557
570
  // src/components/cart-line-item/cart-line-item.tsx
558
- var W = {
571
+ var J = {
559
572
  remove: "Remove",
560
573
  quantity: "Quantity"
561
574
  };
562
- function G({ line: t, labels: n }) {
563
- let { remove: r, setQuantity: i } = R(), { locale: a, renderLink: o, renderImage: s, hrefs: c } = y(), l = {
564
- ...W,
575
+ function Y({ line: t, labels: n }) {
576
+ let { remove: r, setQuantity: i } = B(), { locale: a, renderLink: o, renderImage: s, hrefs: c } = b(), l = {
577
+ ...J,
565
578
  ...n
566
579
  }, u = t.maxQuantity > 1;
567
580
  return /* @__PURE__ */ f("li", {
@@ -605,17 +618,17 @@ function G({ line: t, labels: n }) {
605
618
  });
606
619
  }
607
620
  // src/lib/parse-duration.ts
608
- function K(e) {
621
+ function ee(e) {
609
622
  let t = /^\s*(-?\d*\.?\d+)\s*(ms|s)\s*$/.exec(e);
610
623
  if (!t) return 0;
611
624
  let n = Number(t[1]);
612
625
  return Math.max(0, t[2] === "s" ? n * 1e3 : n);
613
626
  }
614
627
  // src/components/cart-drawer/use-drawer-presence.ts
615
- function q(e, t) {
628
+ function te(e, t) {
616
629
  let [n, r] = l(0), [i, o] = l(e), [s, c] = l(!1);
617
630
  return i !== e && (o(e), c(!e && n > 0)), a(() => {
618
- e && t.current && r(K(getComputedStyle(t.current).getPropertyValue("--sf-motion-duration")));
631
+ e && t.current && r(ee(getComputedStyle(t.current).getPropertyValue("--sf-motion-duration")));
619
632
  }, [e, t]), a(() => {
620
633
  if (!s) return;
621
634
  let e = setTimeout(() => c(!1), n);
@@ -626,7 +639,7 @@ function q(e, t) {
626
639
  };
627
640
  }
628
641
  // src/components/cart-summary/discount-field.tsx
629
- function J({ labels: t, applied: n, error: r, pending: i, currency: a, locale: o, onApply: s, onRemove: c }) {
642
+ function ne({ labels: t, applied: n, error: r, pending: i, currency: a, locale: o, onApply: s, onRemove: c }) {
630
643
  let [u, p] = l(!1), [m, h] = l("");
631
644
  function g(e) {
632
645
  e.preventDefault();
@@ -684,8 +697,8 @@ function J({ labels: t, applied: n, error: r, pending: i, currency: a, locale: o
684
697
  });
685
698
  }
686
699
  // src/components/cart-summary/use-discount.ts
687
- function Y(e) {
688
- let { lines: t } = R(), n = t.map((e) => ({
700
+ function re(e) {
701
+ let { lines: t } = B(), n = t.map((e) => ({
689
702
  productId: e.productId,
690
703
  quantity: e.quantity
691
704
  })), i = n.map((e) => `${e.productId}:${e.quantity}`).join(","), [o, s] = l(null), [u, d] = l(null), [f, p] = l(!1), m = c({
@@ -745,11 +758,11 @@ function Y(e) {
745
758
  }
746
759
  // src/components/cart-summary/cart-summary.tsx
747
760
  function X({ onCheckout: t, checkoutLabel: n = "Checkout", checkoutDisabled: r = !1, labels: i, discount: a }) {
748
- let o = R(), { locale: s } = y(), c = i?.subtotal ?? "Subtotal", l = Y(a), u = {
749
- ...z,
761
+ let o = B(), { locale: s } = b(), c = i?.subtotal ?? "Subtotal", l = re(a), u = {
762
+ ...V,
750
763
  ...i?.discount,
751
764
  reasons: {
752
- ...z.reasons,
765
+ ...V.reasons,
753
766
  ...i?.discount?.reasons
754
767
  }
755
768
  };
@@ -763,7 +776,7 @@ function X({ onCheckout: t, checkoutLabel: n = "Checkout", checkoutDisabled: r =
763
776
  children: e(o.subtotalCents, o.currency ?? "USD", s)
764
777
  })]
765
778
  }),
766
- a && o.lines.length > 0 && /* @__PURE__ */ d(J, {
779
+ a && o.lines.length > 0 && /* @__PURE__ */ d(ne, {
767
780
  labels: u,
768
781
  currency: o.currency ?? "USD",
769
782
  locale: s,
@@ -789,11 +802,11 @@ var Z = {
789
802
  close: "Close",
790
803
  empty: "Your cart is empty."
791
804
  }, Q = "a[href], button:not([disabled]), input:not([disabled]), [tabindex]:not([tabindex=\"-1\"])";
792
- function re({ labels: e, summary: t, slots: n }) {
793
- let r = R(), i = {
805
+ function ie({ labels: e, summary: t, slots: n }) {
806
+ let r = B(), i = {
794
807
  ...Z,
795
808
  ...e
796
- }, o = c(null), s = c(null), { isOpen: l } = r, p = q(l, s);
809
+ }, o = c(null), s = c(null), { isOpen: l } = r, p = te(l, s);
797
810
  if (a(() => {
798
811
  if (!l) return;
799
812
  let e = document.activeElement;
@@ -846,7 +859,7 @@ function re({ labels: e, summary: t, slots: n }) {
846
859
  }) : /* @__PURE__ */ f(u, { children: [
847
860
  /* @__PURE__ */ d("ul", {
848
861
  className: "sf-drawer__lines",
849
- children: r.lines.map((e) => /* @__PURE__ */ d(G, { line: e }, e.productId))
862
+ children: r.lines.map((e) => /* @__PURE__ */ d(Y, { line: e }, e.productId))
850
863
  }),
851
864
  /* @__PURE__ */ d(X, { ...t }),
852
865
  n?.summaryNote && /* @__PURE__ */ d("div", {
@@ -858,8 +871,8 @@ function re({ labels: e, summary: t, slots: n }) {
858
871
  });
859
872
  }
860
873
  // src/components/cart-button/cart-button.tsx
861
- function ie({ label: e = "Cart", className: t, icon: n, labelHidden: r = !1, hideZeroCount: i = !1 }) {
862
- let a = R(), o = a.hydrated ? a.itemCount : 0, s = !(i && o === 0), c = s ? r ? `${e}, ${a.itemCount} items` : void 0 : `${e}, empty`;
874
+ function ae({ label: e = "Cart", className: t, icon: n, labelHidden: r = !1, hideZeroCount: i = !1 }) {
875
+ let a = B(), o = a.hydrated ? a.itemCount : 0, s = !(i && o === 0), c = s ? r ? `${e}, ${a.itemCount} items` : void 0 : `${e}, empty`;
863
876
  return /* @__PURE__ */ f("button", {
864
877
  type: "button",
865
878
  className: ["sf-cart-button", t].filter(Boolean).join(" "),
@@ -903,7 +916,7 @@ var $ = {
903
916
  }
904
917
  };
905
918
  // src/components/checkout-success/checkout-success-view.tsx
906
- function ae(e) {
919
+ function oe(e) {
907
920
  let t = [
908
921
  e.city,
909
922
  e.state,
@@ -917,7 +930,7 @@ function ae(e) {
917
930
  e.country
918
931
  ].filter((e) => !!e);
919
932
  }
920
- function oe({ order: t, locale: n, labels: r, className: i, slots: a, showReference: o, showSummaryHeading: s, continueHref: c, renderLink: l }) {
933
+ function se({ order: t, locale: n, labels: r, className: i, slots: a, showReference: o, showSummaryHeading: s, continueHref: c, renderLink: l }) {
921
934
  let u = {
922
935
  ...$,
923
936
  ...r,
@@ -925,11 +938,11 @@ function oe({ order: t, locale: n, labels: r, className: i, slots: a, showRefere
925
938
  ...$.status,
926
939
  ...r?.status
927
940
  }
928
- }, p = (r) => e(r, t.currency, n), m = t.shippingAddress ? ae(t.shippingAddress) : [], { fulfillment: h } = t, g = {
941
+ }, p = (r) => e(r, t.currency, n), h = t.shippingAddress ? oe(t.shippingAddress) : [], { fulfillment: g } = t, _ = {
929
942
  href: c ?? "",
930
943
  className: "sf-checkout-success__continue",
931
944
  children: u.continue
932
- }, _ = c ? l ? l(g) : /* @__PURE__ */ d("a", { ...g }) : null, v = a?.actions ?? _;
945
+ }, v = c ? l ? l(_) : /* @__PURE__ */ d("a", { ..._ }) : null, y = a?.actions ?? v;
933
946
  return /* @__PURE__ */ f("section", {
934
947
  className: ["sf-checkout-success", i].filter(Boolean).join(" "),
935
948
  "data-status": t.status,
@@ -940,7 +953,7 @@ function oe({ order: t, locale: n, labels: r, className: i, slots: a, showRefere
940
953
  }),
941
954
  o && /* @__PURE__ */ d("p", {
942
955
  className: "sf-checkout-success__reference",
943
- children: u.reference.replace("{id}", t.id)
956
+ children: u.reference.replace("{id}", m(t.id))
944
957
  }),
945
958
  a?.header,
946
959
  /* @__PURE__ */ d("p", {
@@ -991,35 +1004,35 @@ function oe({ order: t, locale: n, labels: r, className: i, slots: a, showRefere
991
1004
  ]
992
1005
  }),
993
1006
  a?.afterTotals,
994
- m.length > 0 && /* @__PURE__ */ f("address", {
1007
+ h.length > 0 && /* @__PURE__ */ f("address", {
995
1008
  className: "sf-checkout-success__address",
996
- children: [/* @__PURE__ */ d("strong", { children: u.shipTo }), m.map((e) => /* @__PURE__ */ d("span", { children: e }, e))]
1009
+ children: [/* @__PURE__ */ d("strong", { children: u.shipTo }), h.map((e) => /* @__PURE__ */ d("span", { children: e }, e))]
997
1010
  }),
998
- h?.trackingNumber && /* @__PURE__ */ f("p", {
1011
+ g?.trackingNumber && /* @__PURE__ */ f("p", {
999
1012
  className: "sf-checkout-success__tracking",
1000
- children: [h.carrier && `${h.carrier} · `, h.trackingUrl ? /* @__PURE__ */ f("a", {
1001
- href: h.trackingUrl,
1013
+ children: [g.carrier && `${g.carrier} · `, g.trackingUrl ? /* @__PURE__ */ f("a", {
1014
+ href: g.trackingUrl,
1002
1015
  children: [
1003
1016
  u.tracking,
1004
1017
  " ",
1005
- h.trackingNumber
1018
+ g.trackingNumber
1006
1019
  ]
1007
- }) : h.trackingNumber]
1020
+ }) : g.trackingNumber]
1008
1021
  }),
1009
- v && /* @__PURE__ */ d("div", {
1022
+ y && /* @__PURE__ */ d("div", {
1010
1023
  className: "sf-checkout-success__actions",
1011
- children: v
1024
+ children: y
1012
1025
  })
1013
1026
  ]
1014
1027
  });
1015
1028
  }
1016
1029
  // src/cart/clear-cart-on-mount.tsx
1017
- function se() {
1018
- let { hydrated: e, clear: t } = R(), n = c(!1);
1030
+ function ce() {
1031
+ let { hydrated: e, clear: t } = B(), n = c(!1);
1019
1032
  return a(() => {
1020
1033
  e && !n.current && (n.current = !0, t());
1021
1034
  }, [e, t]), null;
1022
1035
  }
1023
- export { U as AddToCartButton, S as AvailabilityBadge, ie as CartButton, re as CartDrawer, G as CartLineItem, ee as CartProvider, X as CartSummary, T as CategoryFilter, oe as CheckoutSuccessView, se as ClearCartOnMount, k as FeaturedProducts, b as PriceTag, C as ProductCard, O as ProductDetailView, E as ProductGallery, w as ProductGrid, D as ShopView, v as StorefrontProvider, j as addLine, M as cartReducer, H as defaultAddToCartLabels, x as defaultAvailabilityLabels, Z as defaultCartDrawerLabels, W as defaultCartLineItemLabels, V as defaultCheckoutLabels, $ as defaultCheckoutSuccessLabels, B as discountReasonMessage, A as emptyCart, e as formatPrice, P as itemCount, F as localStorageCartStorage, t as sortForDisplay, N as subtotalCents, h as themeToCssVars, R as useCart, ne as useCheckout, y as useStorefront };
1036
+ export { q as AddToCartButton, C as AvailabilityBadge, ae as CartButton, ie as CartDrawer, Y as CartLineItem, z as CartProvider, X as CartSummary, E as CategoryFilter, se as CheckoutSuccessView, ce as ClearCartOnMount, A as FeaturedProducts, x as PriceTag, w as ProductCard, k as ProductDetailView, D as ProductGallery, T as ProductGrid, O as ShopView, y as StorefrontProvider, M as addLine, N as cartReducer, K as defaultAddToCartLabels, S as defaultAvailabilityLabels, Z as defaultCartDrawerLabels, J as defaultCartLineItemLabels, U as defaultCheckoutLabels, $ as defaultCheckoutSuccessLabels, H as discountReasonMessage, j as emptyCart, e as formatPrice, F as itemCount, I as localStorageCartStorage, t as sortForDisplay, P as subtotalCents, g as themeToCssVars, B as useCart, G as useCheckout, b as useStorefront };
1024
1037
 
1025
1038
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["_Fragment","_Fragment","_Fragment","formatPrice","VAR_NAMES","themeToCssVars","vars","key","value","theme","defaults","priority","slug","StorefrontContext","createContext","StorefrontProvider","overrides","hrefs","useStorefront","useContext","PriceTag","classes","sold","className","priceCents","currency","locale","defaultAvailabilityLabels","AvailabilityBadge","text","labels","variant","label","availability","quantity","ProductCard","product","image","renderLink","Fragment","renderImage","ProductGrid","products","emptyState","index","priorityCount","CategoryFilter","ordered","sortCategories","categories","controlled","onSelect","item","active","selected","allLabel","c","formatLabel","ProductGallery","setActive","useState","images","event","i","ShopView","slots","activeCategory","formatCategoryLabel","sortForDisplay","nextHref","loadMoreLabel","ProductDetailView","FeaturedProducts","heading","emptyCart","addLine","first","state","line","existing","l","cartReducer","action","subtotalCents","sum","itemCount","localStorageCartStorage","raw","parsed","result","cartLineSchema","CartContext","defaultStorage","CartProvider","dispatch","useReducer","hydrated","setHydrated","isOpen","setOpen","stateRef","useRef","useEffect","cancelled","storage","loaded","add","useCallback","useMemo","useCart","ctx","defaultDiscountLabels","discountReasonMessage","reason","defaultCheckoutLabels","assign","url","useCheckout","cart","busy","setBusy","message","setMessage","startCheckout","lines","options","onDiscountInvalid","remove","onRedirect","defaultAddToCartLabels","AddToCartButton","inCart","unavailable","atCap","disabled","handleClick","openOnAdd","defaultCartLineItemLabels","CartLineItem","editable","setQuantity","parseDurationMs","match","amount","useDrawerPresence","durationMs","setDurationMs","wasOpen","setWasOpen","lingering","setLingering","backdropRef","timer","DiscountField","open","setValue","submit","code","onApply","applied","onRemove","error","pending","useDiscount","items","signature","setApplied","setError","setPending","latest","request","previewedSignature","clear","run","current","id","onReject","apply","delay","discount","CartSummary","subtotal","discountLabels","checkoutDisabled","onCheckout","checkoutLabel","defaultCartDrawerLabels","FOCUSABLE","CartDrawer","panelRef","presence","trigger","handleKeyDown","last","summary","CartButton","count","showCount","hideZeroCount","ariaLabel","labelHidden","icon","defaultCheckoutSuccessLabels","addressLines","region","address","CheckoutSuccessView","money","cents","order","continueProps","continueHref","continueLink","actions","showReference","showSummaryHeading","fulfillment","ClearCartOnMount","cleared"],"sources":["../src/lib/theme.ts","../src/lib/provider.tsx","../src/components/price-tag/price-tag.tsx","../src/components/availability-badge/availability-badge.tsx","../src/components/product-card/product-card.tsx","../src/components/product-grid/product-grid.tsx","../src/components/category-filter/category-filter.tsx","../src/components/product-gallery/product-gallery.tsx","../src/views/shop-view.tsx","../src/views/product-detail-view.tsx","../src/views/featured-products.tsx","../src/cart/cart-reducer.ts","../src/cart/cart-storage.ts","../src/cart/cart-provider.tsx","../src/components/cart-summary/discount-types.ts","../src/cart/use-checkout.ts","../src/components/add-to-cart-button/add-to-cart-button.tsx","../src/components/cart-line-item/cart-line-item.tsx","../src/lib/parse-duration.ts","../src/components/cart-drawer/use-drawer-presence.ts","../src/components/cart-summary/discount-field.tsx","../src/components/cart-summary/use-discount.ts","../src/components/cart-summary/cart-summary.tsx","../src/components/cart-drawer/cart-drawer.tsx","../src/components/cart-button/cart-button.tsx","../src/components/checkout-success/labels.ts","../src/components/checkout-success/checkout-success-view.tsx","../src/cart/clear-cart-on-mount.tsx"],"sourcesContent":["export interface StorefrontTheme {\n colorBg?: string;\n colorSurface?: string;\n colorFg?: string;\n colorMuted?: string;\n colorAccent?: string;\n colorAccentFg?: string;\n colorBorder?: string;\n colorSold?: string;\n radius?: string;\n fontDisplay?: string;\n fontBody?: string;\n spaceUnit?: string;\n}\n\nconst VAR_NAMES: Record<keyof StorefrontTheme, string> = {\n colorBg: '--sf-color-bg',\n colorSurface: '--sf-color-surface',\n colorFg: '--sf-color-fg',\n colorMuted: '--sf-color-muted',\n colorAccent: '--sf-color-accent',\n colorAccentFg: '--sf-color-accent-fg',\n colorBorder: '--sf-color-border',\n colorSold: '--sf-color-sold',\n radius: '--sf-radius',\n fontDisplay: '--sf-font-display',\n fontBody: '--sf-font-body',\n spaceUnit: '--sf-space-unit',\n};\n\nexport function themeToCssVars(theme: StorefrontTheme): Record<string, string> {\n const vars: Record<string, string> = {};\n for (const key of Object.keys(VAR_NAMES) as (keyof StorefrontTheme)[]) {\n const value = theme[key];\n if (value !== undefined) vars[VAR_NAMES[key]] = value;\n }\n return vars;\n}\n","import { createContext, useContext, type ReactNode } from 'react';\n\nexport interface LinkProps {\n href: string;\n children: ReactNode;\n className?: string;\n}\n\nexport interface ImageProps {\n src: string;\n alt: string;\n sizes?: string;\n className?: string;\n priority?: boolean;\n}\n\nexport interface StorefrontConfig {\n renderLink: (props: LinkProps) => ReactNode;\n renderImage: (props: ImageProps) => ReactNode;\n locale: string;\n hrefs: {\n product: (slug: string) => string;\n category: (slug: string) => string;\n };\n}\n\nconst defaults: StorefrontConfig = {\n renderLink: ({ href, children, className }) => (\n <a href={href} className={className}>\n {children}\n </a>\n ),\n renderImage: ({ src, alt, sizes, className, priority }) => (\n <img\n src={src}\n alt={alt}\n sizes={sizes}\n className={className}\n loading={priority ? 'eager' : 'lazy'}\n decoding=\"async\"\n fetchPriority={priority ? 'high' : undefined}\n />\n ),\n locale: 'en-US',\n hrefs: {\n product: (slug) => `/shop/${slug}`,\n category: (slug) => `/shop?category=${slug}`,\n },\n};\n\nconst StorefrontContext = createContext<StorefrontConfig>(defaults);\n\nexport interface StorefrontProviderProps extends Partial<\n Omit<StorefrontConfig, 'hrefs'>\n> {\n hrefs?: Partial<StorefrontConfig['hrefs']>;\n children: ReactNode;\n}\n\nexport function StorefrontProvider({\n children,\n hrefs,\n ...overrides\n}: StorefrontProviderProps) {\n const value: StorefrontConfig = {\n ...defaults,\n ...overrides,\n hrefs: { ...defaults.hrefs, ...hrefs },\n };\n return (\n <StorefrontContext.Provider value={value}>\n {children}\n </StorefrontContext.Provider>\n );\n}\n\nexport function useStorefront(): StorefrontConfig {\n return useContext(StorefrontContext);\n}\n","import { formatPrice } from '../../lib/format-price';\nimport { useStorefront } from '../../lib/provider';\n\nexport interface PriceTagProps {\n priceCents: number;\n currency: string;\n sold?: boolean;\n className?: string;\n}\n\nexport function PriceTag({\n priceCents,\n currency,\n sold = false,\n className,\n}: PriceTagProps) {\n const { locale } = useStorefront();\n const classes = ['sf-price', sold && 'sf-price--sold', className]\n .filter(Boolean)\n .join(' ');\n return (\n <span className={classes}>{formatPrice(priceCents, currency, locale)}</span>\n );\n}\n","import type { ProductAvailability } from '@develemit/commerce-contracts';\n\nexport interface AvailabilityLabels {\n oneOfAKind: string;\n sold: string;\n reserved: string;\n available: string;\n}\n\nexport const defaultAvailabilityLabels: AvailabilityLabels = {\n oneOfAKind: 'One of a kind',\n sold: 'Sold',\n reserved: 'On hold',\n available: 'Available',\n};\n\nexport interface AvailabilityBadgeProps {\n availability: ProductAvailability;\n quantity: number;\n labels?: Partial<AvailabilityLabels>;\n className?: string;\n}\n\nexport function AvailabilityBadge({\n availability,\n quantity,\n labels,\n className,\n}: AvailabilityBadgeProps) {\n const text = { ...defaultAvailabilityLabels, ...labels };\n let variant: string;\n let label: string;\n if (availability === 'sold') {\n variant = 'sold';\n label = text.sold;\n } else if (availability === 'reserved') {\n variant = 'reserved';\n label = text.reserved;\n } else if (quantity === 1) {\n variant = 'unique';\n label = text.oneOfAKind;\n } else {\n variant = 'available';\n label = text.available;\n }\n const classes = ['sf-badge', `sf-badge--${variant}`, className]\n .filter(Boolean)\n .join(' ');\n return (\n <span role=\"status\" className={classes}>\n {label}\n </span>\n );\n}\n","import type { ProductCard as ProductCardData } from '@develemit/commerce-contracts';\nimport { useStorefront } from '../../lib/provider';\nimport {\n AvailabilityBadge,\n type AvailabilityLabels,\n} from '../availability-badge/availability-badge';\nimport { PriceTag } from '../price-tag/price-tag';\n\nexport interface ProductCardProps {\n product: ProductCardData;\n labels?: Partial<AvailabilityLabels>;\n sizes?: string;\n priority?: boolean;\n}\n\nexport function ProductCard({\n product,\n labels,\n sizes,\n priority,\n}: ProductCardProps) {\n const { renderLink, renderImage, hrefs } = useStorefront();\n const sold = product.availability === 'sold';\n const image = product.images[0];\n const classes = ['sf-card', sold && 'sf-card--sold']\n .filter(Boolean)\n .join(' ');\n return (\n <article className={classes}>\n {renderLink({\n href: hrefs.product(product.slug),\n className: 'sf-card__link',\n children: (\n <>\n <div className=\"sf-card__media\">\n {image ? (\n renderImage({\n src: image.url,\n alt: image.alt,\n sizes,\n className: 'sf-card__image',\n priority,\n })\n ) : (\n <div className=\"sf-card__placeholder\" aria-hidden=\"true\" />\n )}\n </div>\n <div className=\"sf-card__body\">\n <h3 className=\"sf-card__title\">{product.name}</h3>\n <PriceTag\n priceCents={product.priceCents}\n currency={product.currency}\n sold={sold}\n />\n <AvailabilityBadge\n availability={product.availability}\n quantity={product.quantity}\n labels={labels}\n />\n </div>\n </>\n ),\n })}\n </article>\n );\n}\n","import type { ReactNode } from 'react';\nimport type { ProductCard as ProductCardData } from '@develemit/commerce-contracts';\nimport type { AvailabilityLabels } from '../availability-badge/availability-badge';\nimport { ProductCard } from '../product-card/product-card';\n\nexport interface ProductGridProps {\n products: ProductCardData[];\n emptyState?: ReactNode;\n labels?: Partial<AvailabilityLabels>;\n priorityCount?: number;\n}\n\nexport function ProductGrid({\n products,\n emptyState,\n labels,\n priorityCount = 0,\n}: ProductGridProps) {\n if (products.length === 0) {\n return (\n <div className=\"sf-grid-empty\">{emptyState ?? 'No products found.'}</div>\n );\n }\n return (\n <ul className=\"sf-grid\">\n {products.map((product, index) => (\n <li key={product.id} className=\"sf-grid__item\">\n <ProductCard\n product={product}\n labels={labels}\n priority={index < priorityCount}\n />\n </li>\n ))}\n </ul>\n );\n}\n","import type { Category } from '@develemit/commerce-contracts';\nimport { useStorefront } from '../../lib/provider';\n\nexport interface CategoryFilterProps {\n categories: Category[];\n selected?: string;\n onSelect?: (slug: string | undefined) => void;\n allLabel?: string;\n label?: string;\n formatLabel?: (category: Category) => string;\n sortCategories?: (a: Category, b: Category) => number;\n}\n\nexport function CategoryFilter({\n categories,\n selected,\n onSelect,\n allLabel = 'All',\n label = 'Categories',\n formatLabel,\n sortCategories,\n}: CategoryFilterProps) {\n const { renderLink, hrefs } = useStorefront();\n const ordered = sortCategories\n ? [...categories].sort(sortCategories)\n : categories;\n const controlled = onSelect !== undefined;\n\n const item = (slug: string | undefined, text: string, href: string) => {\n const active = selected === slug;\n const className = `sf-filter__item${active ? ' sf-filter__item--active' : ''}`;\n return (\n <li key={slug ?? '__all'}>\n {controlled ? (\n <button\n type=\"button\"\n className={className}\n aria-pressed={active}\n onClick={() => onSelect(slug)}\n >\n {text}\n </button>\n ) : (\n renderLink({\n href,\n className,\n children: text,\n })\n )}\n </li>\n );\n };\n\n return (\n <nav aria-label={label} className=\"sf-filter\">\n <ul className=\"sf-filter__list\">\n {item(undefined, allLabel, hrefs.category(''))}\n {ordered.map((c) =>\n item(\n c.slug,\n formatLabel ? formatLabel(c) : c.label,\n hrefs.category(c.slug),\n ),\n )}\n </ul>\n </nav>\n );\n}\n","import { useState, type KeyboardEvent } from 'react';\nimport type { ProductImage } from '@develemit/commerce-contracts';\nimport { useStorefront } from '../../lib/provider';\n\nexport interface ProductGalleryProps {\n images: ProductImage[];\n sizes?: string;\n}\n\nexport function ProductGallery({ images, sizes }: ProductGalleryProps) {\n const { renderImage } = useStorefront();\n const [active, setActive] = useState(0);\n\n if (images.length === 0) return null;\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'ArrowRight') {\n setActive((i) => (i + 1) % images.length);\n } else if (event.key === 'ArrowLeft') {\n setActive((i) => (i - 1 + images.length) % images.length);\n } else if (event.key === 'Home') {\n setActive(0);\n } else if (event.key === 'End') {\n setActive(images.length - 1);\n } else {\n return;\n }\n event.preventDefault();\n };\n\n return (\n <div\n className=\"sf-gallery\"\n role=\"group\"\n aria-roledescription=\"carousel\"\n aria-label=\"Product images\"\n tabIndex={0}\n onKeyDown={onKeyDown}\n >\n <div className=\"sf-gallery__stage\">\n {images.map((image, index) => (\n <div\n key={image.url}\n className=\"sf-gallery__slide\"\n hidden={index !== active}\n >\n {renderImage({\n src: image.url,\n alt: image.alt,\n sizes,\n priority: index === active,\n className: 'sf-gallery__image',\n })}\n </div>\n ))}\n </div>\n {images.length > 1 && (\n <div className=\"sf-gallery__thumbs\">\n {images.map((image, index) => (\n <button\n key={image.url}\n type=\"button\"\n className={`sf-gallery__thumb${index === active ? ' sf-gallery__thumb--active' : ''}`}\n aria-label={`Show image ${index + 1} of ${images.length}`}\n aria-current={index === active}\n onClick={() => setActive(index)}\n >\n {renderImage({\n src: image.url,\n alt: '',\n className: 'sf-gallery__thumb-image',\n })}\n </button>\n ))}\n </div>\n )}\n </div>\n );\n}\n","import type { ReactNode } from 'react';\nimport type { Category, ProductCard } from '@develemit/commerce-contracts';\nimport { CategoryFilter } from '../components/category-filter/category-filter';\nimport { ProductGrid } from '../components/product-grid/product-grid';\nimport type { AvailabilityLabels } from '../components/availability-badge/availability-badge';\nimport { useStorefront } from '../lib/provider';\nimport { sortForDisplay } from './sort-for-display';\n\nexport interface ShopViewSlots {\n header?: ReactNode;\n intro?: ReactNode;\n emptyState?: ReactNode;\n}\n\nexport interface ShopViewProps {\n products: ProductCard[];\n categories: Category[];\n activeCategory?: string;\n slots?: ShopViewSlots;\n labels?: Partial<AvailabilityLabels>;\n allLabel?: string;\n formatCategoryLabel?: (category: Category) => string;\n sortCategories?: (a: Category, b: Category) => number;\n nextHref?: string;\n loadMoreLabel?: string;\n}\n\nexport function ShopView({\n products,\n categories,\n activeCategory,\n slots = {},\n labels,\n allLabel,\n formatCategoryLabel,\n sortCategories,\n nextHref,\n loadMoreLabel = 'Load more',\n}: ShopViewProps) {\n const { renderLink } = useStorefront();\n return (\n <div className=\"sf-shop\">\n {slots.header}\n {slots.intro}\n <CategoryFilter\n categories={categories}\n selected={activeCategory}\n allLabel={allLabel}\n formatLabel={formatCategoryLabel}\n sortCategories={sortCategories}\n />\n <ProductGrid\n products={sortForDisplay(products)}\n emptyState={slots.emptyState}\n labels={labels}\n priorityCount={6}\n />\n {nextHref && (\n <div className=\"sf-shop__pager\">\n {renderLink({\n href: nextHref,\n className: 'sf-shop__more',\n children: loadMoreLabel,\n })}\n </div>\n )}\n </div>\n );\n}\n","import type { ReactNode } from 'react';\nimport type { Product } from '@develemit/commerce-contracts';\nimport {\n AvailabilityBadge,\n type AvailabilityLabels,\n} from '../components/availability-badge/availability-badge';\nimport { PriceTag } from '../components/price-tag/price-tag';\nimport { ProductGallery } from '../components/product-gallery/product-gallery';\n\nexport interface ProductDetailViewSlots {\n actions?: ReactNode;\n afterDetails?: ReactNode;\n}\n\nexport interface ProductDetailViewProps {\n product: Product;\n slots?: ProductDetailViewSlots;\n labels?: Partial<AvailabilityLabels>;\n}\n\nexport function ProductDetailView({\n product,\n slots = {},\n labels,\n}: ProductDetailViewProps) {\n return (\n <article className=\"sf-detail\">\n <ProductGallery images={product.images} />\n <div className=\"sf-detail__body\">\n <h1 className=\"sf-detail__title\">{product.name}</h1>\n <PriceTag priceCents={product.priceCents} currency={product.currency} />\n <AvailabilityBadge\n availability={product.availability}\n quantity={product.quantity}\n labels={labels}\n />\n <p className=\"sf-detail__description\">{product.description}</p>\n {(product.materials || product.dimensions) && (\n <dl className=\"sf-detail__specs\">\n {product.materials && (\n <>\n <dt>Materials</dt>\n <dd>{product.materials}</dd>\n </>\n )}\n {product.dimensions && (\n <>\n <dt>Dimensions</dt>\n <dd>{product.dimensions}</dd>\n </>\n )}\n </dl>\n )}\n {slots.actions && (\n <div className=\"sf-detail__actions\">{slots.actions}</div>\n )}\n {slots.afterDetails}\n </div>\n </article>\n );\n}\n","import type { ProductCard } from '@develemit/commerce-contracts';\nimport type { AvailabilityLabels } from '../components/availability-badge/availability-badge';\nimport { ProductGrid } from '../components/product-grid/product-grid';\n\nexport interface FeaturedProductsProps {\n products: ProductCard[];\n heading?: string;\n labels?: Partial<AvailabilityLabels>;\n}\n\nexport function FeaturedProducts({\n products,\n heading = 'Featured',\n labels,\n}: FeaturedProductsProps) {\n if (products.length === 0) return null;\n return (\n <section className=\"sf-featured\">\n <h2 className=\"sf-featured__heading\">{heading}</h2>\n <ProductGrid products={products} labels={labels} priorityCount={6} />\n </section>\n );\n}\n","import type { CartLine } from '@develemit/commerce-contracts';\n\nexport interface CartState {\n lines: CartLine[];\n}\n\nexport type CartAction =\n | { type: 'add'; line: CartLine }\n | { type: 'remove'; productId: string }\n | { type: 'setQuantity'; productId: string; quantity: number }\n | { type: 'clear' }\n | { type: 'hydrate'; state: CartState };\n\nexport interface AddResult {\n state: CartState;\n added: boolean;\n capped: boolean;\n reason?: 'currency-mismatch';\n}\n\nexport const emptyCart: CartState = { lines: [] };\n\nexport function addLine(state: CartState, line: CartLine): AddResult {\n const first = state.lines[0];\n if (first && first.currency !== line.currency) {\n return { state, added: false, capped: false, reason: 'currency-mismatch' };\n }\n const existing = state.lines.find((l) => l.productId === line.productId);\n if (!existing) {\n const quantity = Math.min(line.quantity, line.maxQuantity);\n return {\n state: { lines: [...state.lines, { ...line, quantity }] },\n added: true,\n capped: quantity < line.quantity,\n };\n }\n if (existing.quantity >= existing.maxQuantity) {\n return { state, added: false, capped: true };\n }\n const quantity = Math.min(\n existing.quantity + line.quantity,\n existing.maxQuantity,\n );\n return {\n state: {\n lines: state.lines.map((l) =>\n l.productId === line.productId ? { ...l, quantity } : l,\n ),\n },\n added: true,\n capped: existing.quantity + line.quantity > existing.maxQuantity,\n };\n}\n\nexport function cartReducer(state: CartState, action: CartAction): CartState {\n switch (action.type) {\n case 'add':\n return addLine(state, action.line).state;\n case 'remove':\n return {\n lines: state.lines.filter((l) => l.productId !== action.productId),\n };\n case 'setQuantity': {\n if (action.quantity <= 0) {\n return cartReducer(state, {\n type: 'remove',\n productId: action.productId,\n });\n }\n return {\n lines: state.lines.map((l) =>\n l.productId === action.productId\n ? { ...l, quantity: Math.min(action.quantity, l.maxQuantity) }\n : l,\n ),\n };\n }\n case 'clear':\n return emptyCart;\n case 'hydrate':\n return action.state;\n }\n}\n\nexport function subtotalCents(state: CartState): number {\n return state.lines.reduce((sum, l) => sum + l.priceCents * l.quantity, 0);\n}\n\nexport function itemCount(state: CartState): number {\n return state.lines.reduce((sum, l) => sum + l.quantity, 0);\n}\n","import { cartLineSchema } from '@develemit/commerce-contracts';\nimport { emptyCart, type CartState } from './cart-reducer';\n\nexport interface CartStorage {\n load: () => CartState | Promise<CartState>;\n save: (state: CartState) => void | Promise<void>;\n}\n\nexport function localStorageCartStorage(key = 'sf-cart'): CartStorage {\n return {\n load() {\n if (typeof window === 'undefined') return emptyCart;\n try {\n const raw = window.localStorage.getItem(key);\n if (!raw) return emptyCart;\n const parsed: unknown = JSON.parse(raw);\n const lines = Array.isArray((parsed as CartState)?.lines)\n ? (parsed as CartState).lines\n : [];\n const valid = lines.flatMap((line) => {\n const result = cartLineSchema.safeParse(line);\n return result.success ? [result.data] : [];\n });\n return { lines: valid };\n } catch {\n return emptyCart;\n }\n },\n save(state) {\n if (typeof window === 'undefined') return;\n try {\n window.localStorage.setItem(key, JSON.stringify(state));\n } catch {\n // storage full or blocked: the cart simply stays in memory\n }\n },\n };\n}\n","import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useReducer,\n useRef,\n useState,\n type ReactNode,\n} from 'react';\nimport type { CartLine } from '@develemit/commerce-contracts';\nimport {\n addLine,\n cartReducer,\n emptyCart,\n itemCount,\n subtotalCents,\n type AddResult,\n type CartState,\n} from './cart-reducer';\nimport { localStorageCartStorage, type CartStorage } from './cart-storage';\n\nexport interface CartContextValue {\n lines: CartLine[];\n itemCount: number;\n subtotalCents: number;\n currency: string | null;\n hydrated: boolean;\n isOpen: boolean;\n open: () => void;\n close: () => void;\n add: (line: CartLine) => AddResult;\n remove: (productId: string) => void;\n setQuantity: (productId: string, quantity: number) => void;\n clear: () => void;\n}\n\nconst CartContext = createContext<CartContextValue | null>(null);\n\nexport interface CartProviderProps {\n storage?: CartStorage;\n children: ReactNode;\n}\n\nconst defaultStorage = localStorageCartStorage();\n\nexport function CartProvider({\n storage = defaultStorage,\n children,\n}: CartProviderProps) {\n const [state, dispatch] = useReducer(cartReducer, emptyCart);\n const [hydrated, setHydrated] = useState(false);\n const [isOpen, setOpen] = useState(false);\n const stateRef = useRef<CartState>(state);\n useEffect(() => {\n stateRef.current = state;\n }, [state]);\n\n useEffect(() => {\n let cancelled = false;\n Promise.resolve(storage.load()).then((loaded) => {\n if (cancelled) return;\n dispatch({ type: 'hydrate', state: loaded });\n setHydrated(true);\n });\n return () => {\n cancelled = true;\n };\n }, [storage]);\n\n useEffect(() => {\n if (hydrated) void storage.save(state);\n }, [hydrated, state, storage]);\n\n const add = useCallback((line: CartLine) => {\n const result = addLine(stateRef.current, line);\n if (result.added) {\n stateRef.current = result.state;\n dispatch({ type: 'add', line });\n }\n return result;\n }, []);\n\n const value = useMemo<CartContextValue>(\n () => ({\n lines: state.lines,\n itemCount: itemCount(state),\n subtotalCents: subtotalCents(state),\n currency: state.lines[0]?.currency ?? null,\n hydrated,\n isOpen,\n open: () => setOpen(true),\n close: () => setOpen(false),\n add,\n remove: (productId) => dispatch({ type: 'remove', productId }),\n setQuantity: (productId, quantity) =>\n dispatch({ type: 'setQuantity', productId, quantity }),\n clear: () => dispatch({ type: 'clear' }),\n }),\n [state, hydrated, isOpen, add],\n );\n\n return <CartContext.Provider value={value}>{children}</CartContext.Provider>;\n}\n\nexport function useCart(): CartContextValue {\n const ctx = useContext(CartContext);\n if (!ctx) throw new Error('useCart must be used inside <CartProvider>');\n return ctx;\n}\n","import type { DiscountRejectionReason } from '@develemit/commerce-contracts';\n\nexport type DiscountPreviewResult =\n | { ok: true; code: string; discountCents: number }\n | { ok: false; reason: DiscountRejectionReason };\n\nexport interface CartSummaryDiscount {\n onPreview: (\n code: string,\n items: { productId: string; quantity: number }[],\n ) => Promise<DiscountPreviewResult>;\n onApply?: (code: string | null) => void;\n debounceMs?: number;\n}\n\nexport interface DiscountLabels {\n toggle: string;\n field: string;\n apply: string;\n remove: string;\n line: string;\n reasons: Record<DiscountRejectionReason, string>;\n unavailable: string;\n}\n\nexport const defaultDiscountLabels: DiscountLabels = {\n toggle: 'Have a code?',\n field: 'Discount code',\n apply: 'Apply',\n remove: 'Remove',\n line: 'Discount',\n unavailable: 'We could not check that code. Please try again.',\n reasons: {\n not_found: 'That code is not valid.',\n inactive: 'That code is not valid.',\n not_started: 'That code is not active yet.',\n expired: 'That code has expired.',\n min_subtotal: 'Your cart is below the minimum for that code.',\n exhausted: 'That code is no longer available.',\n currency: 'That code does not apply to this cart.',\n },\n};\n\nexport function discountReasonMessage(\n reason: DiscountRejectionReason,\n overrides?: Partial<DiscountLabels['reasons']>,\n): string {\n return overrides?.[reason] ?? defaultDiscountLabels.reasons[reason];\n}\n","import { useCallback, useState } from 'react';\nimport type { DiscountRejectionReason } from '@develemit/commerce-contracts';\nimport type { CheckoutResult } from '../client/checkout-result';\nimport { discountReasonMessage } from '../components/cart-summary/discount-types';\nimport { useCart } from './cart-provider';\n\nexport interface CheckoutLabels {\n outOfStock: string;\n unavailable: string;\n discountReasons?: Partial<Record<DiscountRejectionReason, string>>;\n}\n\nexport const defaultCheckoutLabels: CheckoutLabels = {\n outOfStock:\n 'Some items just sold or are on hold, so we removed them from your cart.',\n unavailable: 'Checkout is unavailable right now. Please try again.',\n};\n\nexport interface StartCheckoutOptions {\n discountCode?: string;\n}\n\nexport interface UseCheckoutOptions {\n startCheckout: (\n items: { productId: string; quantity: number }[],\n options: StartCheckoutOptions,\n ) => Promise<CheckoutResult>;\n onRedirect?: (url: string) => void;\n onDiscountInvalid?: (reason: DiscountRejectionReason) => void;\n labels?: Partial<CheckoutLabels>;\n}\n\nexport interface UseCheckoutValue {\n checkout: (options?: StartCheckoutOptions) => Promise<void>;\n busy: boolean;\n message: string | null;\n clearMessage: () => void;\n}\n\nconst assign = (url: string) => window.location.assign(url);\n\nexport function useCheckout({\n startCheckout,\n onRedirect = assign,\n onDiscountInvalid,\n labels,\n}: UseCheckoutOptions): UseCheckoutValue {\n const cart = useCart();\n const [busy, setBusy] = useState(false);\n const [message, setMessage] = useState<string | null>(null);\n const { lines, remove } = cart;\n const text = { ...defaultCheckoutLabels, ...labels };\n\n const checkout = useCallback(\n async (options: StartCheckoutOptions = {}) => {\n setBusy(true);\n setMessage(null);\n try {\n const result = await startCheckout(\n lines.map((l) => ({ productId: l.productId, quantity: l.quantity })),\n options,\n );\n if ('url' in result) {\n onRedirect(result.url);\n return;\n }\n if (result.error === 'discount_invalid') {\n setMessage(\n discountReasonMessage(result.reason, text.discountReasons),\n );\n onDiscountInvalid?.(result.reason);\n } else {\n result.productIds.forEach(remove);\n setMessage(text.outOfStock);\n }\n } catch {\n setMessage(text.unavailable);\n }\n setBusy(false);\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [startCheckout, onRedirect, onDiscountInvalid, lines, remove, labels],\n );\n\n const clearMessage = useCallback(() => setMessage(null), []);\n\n return { checkout, busy, message, clearMessage };\n}\n","import type { Product } from '@develemit/commerce-contracts';\nimport { useCart } from '../../cart/cart-provider';\n\nexport interface AddToCartLabels {\n add: string;\n inCart: string;\n unavailable: string;\n}\n\nexport const defaultAddToCartLabels: AddToCartLabels = {\n add: 'Add to cart',\n inCart: 'In your cart',\n unavailable: 'Unavailable',\n};\n\nexport interface AddToCartButtonProps {\n product: Product;\n labels?: Partial<AddToCartLabels>;\n openOnAdd?: boolean;\n className?: string;\n}\n\nexport function AddToCartButton({\n product,\n labels,\n openOnAdd = true,\n className,\n}: AddToCartButtonProps) {\n const cart = useCart();\n const text = { ...defaultAddToCartLabels, ...labels };\n const inCart =\n cart.lines.find((l) => l.productId === product.id)?.quantity ?? 0;\n const unavailable = product.availability !== 'available';\n const atCap = inCart >= product.quantity;\n const disabled = !cart.hydrated || unavailable || atCap;\n const label = unavailable ? text.unavailable : atCap ? text.inCart : text.add;\n\n function handleClick() {\n const result = cart.add({\n productId: product.id,\n slug: product.slug,\n name: product.name,\n priceCents: product.priceCents,\n currency: product.currency,\n imageUrl: product.images[0]?.url,\n quantity: 1,\n maxQuantity: product.quantity,\n });\n if (result.added && openOnAdd) cart.open();\n }\n\n return (\n <button\n type=\"button\"\n className={['sf-button', className].filter(Boolean).join(' ')}\n disabled={disabled}\n onClick={handleClick}\n >\n {label}\n </button>\n );\n}\n","import type { CartLine } from '@develemit/commerce-contracts';\nimport { useCart } from '../../cart/cart-provider';\nimport { formatPrice } from '../../lib/format-price';\nimport { useStorefront } from '../../lib/provider';\n\nexport interface CartLineItemLabels {\n remove: string;\n quantity: string;\n}\n\nexport const defaultCartLineItemLabels: CartLineItemLabels = {\n remove: 'Remove',\n quantity: 'Quantity',\n};\n\nexport interface CartLineItemProps {\n line: CartLine;\n labels?: Partial<CartLineItemLabels>;\n}\n\nexport function CartLineItem({ line, labels }: CartLineItemProps) {\n const { remove, setQuantity } = useCart();\n const { locale, renderLink, renderImage, hrefs } = useStorefront();\n const text = { ...defaultCartLineItemLabels, ...labels };\n const editable = line.maxQuantity > 1;\n return (\n <li className=\"sf-cart-line\">\n {line.imageUrl &&\n renderImage({\n src: line.imageUrl,\n alt: '',\n sizes: '80px',\n className: 'sf-cart-line__image',\n })}\n <div className=\"sf-cart-line__body\">\n {renderLink({\n href: hrefs.product(line.slug),\n className: 'sf-cart-line__name',\n children: line.name,\n })}\n <span className=\"sf-cart-line__price\">\n {formatPrice(line.priceCents * line.quantity, line.currency, locale)}\n </span>\n {editable && (\n <label className=\"sf-cart-line__qty\">\n {text.quantity}\n <input\n type=\"number\"\n min={1}\n max={line.maxQuantity}\n value={line.quantity}\n onChange={(e) =>\n setQuantity(line.productId, Number(e.target.value))\n }\n />\n </label>\n )}\n <button\n type=\"button\"\n className=\"sf-link-button sf-cart-line__remove\"\n onClick={() => remove(line.productId)}\n aria-label={`${text.remove} ${line.name}`}\n >\n {text.remove}\n </button>\n </div>\n </li>\n );\n}\n","export function parseDurationMs(value: string): number {\n const match = /^\\s*(-?\\d*\\.?\\d+)\\s*(ms|s)\\s*$/.exec(value);\n if (!match) return 0;\n const amount = Number(match[1]);\n return Math.max(0, match[2] === 's' ? amount * 1000 : amount);\n}\n","import { useEffect, useState, type RefObject } from 'react';\nimport { parseDurationMs } from '../../lib/parse-duration';\n\nexport function useDrawerPresence(\n isOpen: boolean,\n backdropRef: RefObject<HTMLElement | null>,\n) {\n const [durationMs, setDurationMs] = useState(0);\n const [wasOpen, setWasOpen] = useState(isOpen);\n const [lingering, setLingering] = useState(false);\n\n if (wasOpen !== isOpen) {\n setWasOpen(isOpen);\n setLingering(!isOpen && durationMs > 0);\n }\n\n useEffect(() => {\n if (!isOpen || !backdropRef.current) return;\n setDurationMs(\n parseDurationMs(\n getComputedStyle(backdropRef.current).getPropertyValue(\n '--sf-motion-duration',\n ),\n ),\n );\n }, [isOpen, backdropRef]);\n\n useEffect(() => {\n if (!lingering) return;\n const timer = setTimeout(() => setLingering(false), durationMs);\n return () => clearTimeout(timer);\n }, [lingering, durationMs]);\n\n return { mounted: isOpen || lingering, state: isOpen ? 'open' : 'closed' };\n}\n","import { useState, type FormEvent } from 'react';\nimport { formatPrice } from '../../lib/format-price';\nimport type { DiscountLabels } from './discount-types';\nimport type { AppliedDiscount, DiscountError } from './use-discount';\n\nexport interface DiscountFieldProps {\n labels: DiscountLabels;\n applied: AppliedDiscount | null;\n error: DiscountError | null;\n pending: boolean;\n currency: string;\n locale: string;\n onApply: (code: string) => void;\n onRemove: () => void;\n}\n\nexport function DiscountField({\n labels,\n applied,\n error,\n pending,\n currency,\n locale,\n onApply,\n onRemove,\n}: DiscountFieldProps) {\n const [open, setOpen] = useState(false);\n const [value, setValue] = useState('');\n\n function submit(event: FormEvent) {\n event.preventDefault();\n const code = value.trim();\n if (code) onApply(code);\n }\n\n if (applied) {\n return (\n <p className=\"sf-cart-summary__row sf-cart-summary__discount\">\n <span>\n {labels.line} ({applied.code}){' '}\n <button type=\"button\" className=\"sf-link-button\" onClick={onRemove}>\n {labels.remove}\n </button>\n </span>\n <span className=\"sf-price\">\n −{formatPrice(applied.discountCents, currency, locale)}\n </span>\n </p>\n );\n }\n\n const message =\n error === 'unavailable'\n ? labels.unavailable\n : error\n ? labels.reasons[error]\n : null;\n return (\n <div className=\"sf-cart-summary__code\">\n {open ? (\n <form onSubmit={submit} className=\"sf-cart-summary__code-form\">\n <input\n aria-label={labels.field}\n value={value}\n onChange={(e) => setValue(e.target.value)}\n autoCapitalize=\"characters\"\n autoComplete=\"off\"\n />\n <button type=\"submit\" className=\"sf-button\" disabled={pending}>\n {labels.apply}\n </button>\n </form>\n ) : (\n <button\n type=\"button\"\n className=\"sf-link-button\"\n aria-expanded={false}\n onClick={() => setOpen(true)}\n >\n {labels.toggle}\n </button>\n )}\n {message && (\n <p role=\"alert\" className=\"sf-cart-summary__code-error\">\n {message}\n </p>\n )}\n </div>\n );\n}\n","import type { DiscountRejectionReason } from '@develemit/commerce-contracts';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { useCart } from '../../cart/cart-provider';\nimport type { CartSummaryDiscount } from './discount-types';\n\nexport interface AppliedDiscount {\n code: string;\n discountCents: number;\n}\n\nexport type DiscountError = DiscountRejectionReason | 'unavailable';\n\nexport function useDiscount(discount: CartSummaryDiscount | undefined) {\n const { lines } = useCart();\n const items = lines.map((l) => ({\n productId: l.productId,\n quantity: l.quantity,\n }));\n const signature = items.map((i) => `${i.productId}:${i.quantity}`).join(',');\n const [applied, setApplied] = useState<AppliedDiscount | null>(null);\n const [error, setError] = useState<DiscountError | null>(null);\n const [pending, setPending] = useState(false);\n const latest = useRef({ discount, items });\n useEffect(() => {\n latest.current = { discount, items };\n });\n const request = useRef(0);\n const previewedSignature = useRef(signature);\n\n const clear = useCallback(() => {\n request.current += 1;\n setApplied(null);\n latest.current.discount?.onApply?.(null);\n }, []);\n\n const run = useCallback(\n async (code: string, onReject: (reason: DiscountError) => void) => {\n const current = latest.current;\n if (!current.discount) return;\n const id = ++request.current;\n previewedSignature.current = current.items\n .map((i) => `${i.productId}:${i.quantity}`)\n .join(',');\n setPending(true);\n let result;\n try {\n result = await current.discount.onPreview(code, current.items);\n } catch {\n result = { ok: false as const, reason: 'unavailable' as const };\n }\n if (id !== request.current) return;\n setPending(false);\n if (result.ok) {\n setError(null);\n setApplied({ code: result.code, discountCents: result.discountCents });\n current.discount.onApply?.(result.code);\n } else {\n onReject(result.reason);\n }\n },\n [],\n );\n\n const apply = useCallback(\n (code: string) => run(code, (reason) => setError(reason)),\n [run],\n );\n\n const remove = useCallback(() => {\n setError(null);\n setPending(false);\n clear();\n }, [clear]);\n\n const code = applied?.code;\n const delay = discount?.debounceMs ?? 300;\n useEffect(() => {\n if (!code || signature === previewedSignature.current) return;\n const timer = setTimeout(() => {\n if (latest.current.items.length === 0) {\n clear();\n return;\n }\n void run(code, (reason) => {\n setError(reason);\n clear();\n });\n }, delay);\n return () => clearTimeout(timer);\n }, [code, signature, delay, run, clear]);\n\n return { applied, error, pending, apply, remove };\n}\n","import { useCart } from '../../cart/cart-provider';\nimport { formatPrice } from '../../lib/format-price';\nimport { useStorefront } from '../../lib/provider';\nimport { DiscountField } from './discount-field';\nimport {\n defaultDiscountLabels,\n type CartSummaryDiscount,\n type DiscountLabels,\n} from './discount-types';\nimport { useDiscount } from './use-discount';\n\nexport type {\n CartSummaryDiscount,\n DiscountPreviewResult,\n} from './discount-types';\nexport { discountReasonMessage } from './discount-types';\n\nexport interface CartSummaryLabels {\n subtotal: string;\n discount: Partial<Omit<DiscountLabels, 'reasons'>> & {\n reasons?: Partial<DiscountLabels['reasons']>;\n };\n}\n\nexport interface CartSummaryProps {\n onCheckout?: () => void;\n checkoutLabel?: string;\n checkoutDisabled?: boolean;\n labels?: Partial<CartSummaryLabels>;\n discount?: CartSummaryDiscount;\n}\n\nexport function CartSummary({\n onCheckout,\n checkoutLabel = 'Checkout',\n checkoutDisabled = false,\n labels,\n discount,\n}: CartSummaryProps) {\n const cart = useCart();\n const { locale } = useStorefront();\n const subtotal = labels?.subtotal ?? 'Subtotal';\n const state = useDiscount(discount);\n const discountLabels: DiscountLabels = {\n ...defaultDiscountLabels,\n ...labels?.discount,\n reasons: {\n ...defaultDiscountLabels.reasons,\n ...labels?.discount?.reasons,\n },\n };\n return (\n <div className=\"sf-cart-summary\">\n <p className=\"sf-cart-summary__row\">\n <span>{subtotal}</span>\n <span className=\"sf-price\">\n {formatPrice(cart.subtotalCents, cart.currency ?? 'USD', locale)}\n </span>\n </p>\n {discount && cart.lines.length > 0 && (\n <DiscountField\n labels={discountLabels}\n currency={cart.currency ?? 'USD'}\n locale={locale}\n onApply={(code) => void state.apply(code)}\n onRemove={state.remove}\n applied={state.applied}\n error={state.error}\n pending={state.pending}\n />\n )}\n <button\n type=\"button\"\n className=\"sf-button\"\n disabled={checkoutDisabled || !onCheckout || cart.lines.length === 0}\n onClick={onCheckout}\n >\n {checkoutLabel}\n </button>\n </div>\n );\n}\n","import { useEffect, useRef, type KeyboardEvent, type ReactNode } from 'react';\nimport { useCart } from '../../cart/cart-provider';\nimport { CartLineItem } from '../cart-line-item/cart-line-item';\nimport { useDrawerPresence } from './use-drawer-presence';\nimport {\n CartSummary,\n type CartSummaryProps,\n} from '../cart-summary/cart-summary';\n\nexport interface CartDrawerLabels {\n title: string;\n close: string;\n empty: string;\n}\n\nexport const defaultCartDrawerLabels: CartDrawerLabels = {\n title: 'Your cart',\n close: 'Close',\n empty: 'Your cart is empty.',\n};\n\nexport interface CartDrawerProps {\n labels?: Partial<CartDrawerLabels>;\n summary?: CartSummaryProps;\n slots?: CartDrawerSlots;\n}\n\nexport interface CartDrawerSlots {\n empty?: ReactNode;\n summaryNote?: ReactNode;\n}\n\nconst FOCUSABLE =\n 'a[href], button:not([disabled]), input:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\nexport function CartDrawer({ labels, summary, slots }: CartDrawerProps) {\n const cart = useCart();\n const text = { ...defaultCartDrawerLabels, ...labels };\n const panelRef = useRef<HTMLDivElement>(null);\n const backdropRef = useRef<HTMLDivElement>(null);\n const { isOpen } = cart;\n const presence = useDrawerPresence(isOpen, backdropRef);\n\n useEffect(() => {\n if (!isOpen) return;\n const trigger = document.activeElement as HTMLElement | null;\n panelRef.current?.querySelector<HTMLElement>(FOCUSABLE)?.focus();\n return () => trigger?.focus();\n }, [isOpen]);\n\n if (!presence.mounted) return null;\n\n function handleKeyDown(event: KeyboardEvent<HTMLDivElement>) {\n if (event.key === 'Escape') {\n event.stopPropagation();\n cart.close();\n return;\n }\n if (event.key !== 'Tab') return;\n const items = Array.from(\n panelRef.current?.querySelectorAll<HTMLElement>(FOCUSABLE) ?? [],\n );\n if (items.length === 0) return;\n const first = items[0];\n const last = items[items.length - 1];\n if (event.shiftKey && document.activeElement === first) {\n event.preventDefault();\n last.focus();\n } else if (!event.shiftKey && document.activeElement === last) {\n event.preventDefault();\n first.focus();\n }\n }\n\n return (\n <div\n ref={backdropRef}\n className=\"sf-drawer-backdrop\"\n data-state={presence.state}\n onClick={cart.close}\n >\n <div\n ref={panelRef}\n className=\"sf-drawer\"\n data-state={presence.state}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"sf-drawer-title\"\n onKeyDown={handleKeyDown}\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"sf-drawer__header\">\n <h2 id=\"sf-drawer-title\" className=\"sf-drawer__title\">\n {text.title}\n </h2>\n <button\n type=\"button\"\n className=\"sf-drawer__close\"\n aria-label={text.close}\n onClick={cart.close}\n >\n <span aria-hidden=\"true\">×</span>\n </button>\n </div>\n {cart.lines.length === 0 ? (\n <div className=\"sf-drawer__empty\">\n {slots?.empty ?? <p>{text.empty}</p>}\n </div>\n ) : (\n <>\n <ul className=\"sf-drawer__lines\">\n {cart.lines.map((line) => (\n <CartLineItem key={line.productId} line={line} />\n ))}\n </ul>\n <CartSummary {...summary} />\n {slots?.summaryNote && (\n <div className=\"sf-drawer__note\">{slots.summaryNote}</div>\n )}\n </>\n )}\n </div>\n </div>\n );\n}\n","import type { ReactNode } from 'react';\nimport { useCart } from '../../cart/cart-provider';\n\nexport interface CartButtonProps {\n label?: string;\n className?: string;\n icon?: ReactNode;\n labelHidden?: boolean;\n hideZeroCount?: boolean;\n}\n\nexport function CartButton({\n label = 'Cart',\n className,\n icon,\n labelHidden = false,\n hideZeroCount = false,\n}: CartButtonProps) {\n const cart = useCart();\n const count = cart.hydrated ? cart.itemCount : 0;\n const showCount = !(hideZeroCount && count === 0);\n const ariaLabel = showCount\n ? labelHidden\n ? `${label}, ${cart.itemCount} items`\n : undefined\n : `${label}, empty`;\n return (\n <button\n type=\"button\"\n className={['sf-cart-button', className].filter(Boolean).join(' ')}\n aria-haspopup=\"dialog\"\n aria-label={ariaLabel}\n onClick={cart.open}\n >\n {icon}\n <span className={labelHidden ? 'sf-visually-hidden' : undefined}>\n {label}\n </span>\n {showCount && (\n <span\n className=\"sf-cart-button__count\"\n aria-label={`${cart.itemCount} items`}\n >\n {count}\n </span>\n )}\n </button>\n );\n}\n","import type { OrderStatus } from '@develemit/commerce-contracts';\n\nexport interface CheckoutSuccessLabels {\n heading: string;\n summaryHeading: string;\n reference: string;\n continue: string;\n subtotal: string;\n discount: string;\n shipping: string;\n total: string;\n shipTo: string;\n tracking: string;\n status: Record<OrderStatus, string>;\n}\n\nexport const defaultCheckoutSuccessLabels: CheckoutSuccessLabels = {\n heading: 'Order received',\n summaryHeading: 'Order summary',\n reference: 'Order {id}',\n continue: 'Continue shopping',\n subtotal: 'Subtotal',\n discount: 'Discount',\n shipping: 'Shipping',\n total: 'Total',\n shipTo: 'Ship to',\n tracking: 'Track',\n status: {\n pending: \"We're confirming your payment and will email you once it's done.\",\n paid: 'Payment received. Thank you!',\n shipped: 'Your order is on its way.',\n cancelled: 'This checkout was cancelled.',\n expired: 'This checkout expired before payment.',\n refunded: 'This order has been refunded.',\n needs_attention:\n \"We couldn't confirm one of your items. We'll be in touch about a refund.\",\n },\n};\n","import type { ReactNode } from 'react';\nimport type { OrderAddress, OrderSummary } from '@develemit/commerce-contracts';\nimport { formatPrice } from '../../lib/format-price';\nimport {\n defaultCheckoutSuccessLabels,\n type CheckoutSuccessLabels,\n} from './labels';\n\nexport interface CheckoutSuccessViewProps {\n order: OrderSummary & { shippingAddress?: OrderAddress | null };\n locale?: string;\n labels?: Partial<Omit<CheckoutSuccessLabels, 'status'>> & {\n status?: Partial<CheckoutSuccessLabels['status']>;\n };\n className?: string;\n slots?: {\n header?: ReactNode;\n beforeLines?: ReactNode;\n afterTotals?: ReactNode;\n actions?: ReactNode;\n };\n showReference?: boolean;\n showSummaryHeading?: boolean;\n continueHref?: string;\n renderLink?: (props: {\n href: string;\n children: ReactNode;\n className?: string;\n }) => ReactNode;\n}\n\nfunction addressLines(address: OrderAddress): string[] {\n const region = [address.city, address.state, address.postalCode]\n .filter(Boolean)\n .join(' ');\n return [\n address.name,\n address.line1,\n address.line2,\n region,\n address.country,\n ].filter((line): line is string => Boolean(line));\n}\n\nexport function CheckoutSuccessView({\n order,\n locale,\n labels,\n className,\n slots,\n showReference,\n showSummaryHeading,\n continueHref,\n renderLink,\n}: CheckoutSuccessViewProps) {\n const text = {\n ...defaultCheckoutSuccessLabels,\n ...labels,\n status: { ...defaultCheckoutSuccessLabels.status, ...labels?.status },\n };\n const money = (cents: number) => formatPrice(cents, order.currency, locale);\n const lines = order.shippingAddress\n ? addressLines(order.shippingAddress)\n : [];\n const { fulfillment } = order;\n const continueProps = {\n href: continueHref ?? '',\n className: 'sf-checkout-success__continue',\n children: text.continue,\n };\n const continueLink = continueHref ? (\n renderLink ? (\n renderLink(continueProps)\n ) : (\n <a {...continueProps} />\n )\n ) : null;\n const actions = slots?.actions ?? continueLink;\n return (\n <section\n className={['sf-checkout-success', className].filter(Boolean).join(' ')}\n data-status={order.status}\n >\n <h1 className=\"sf-checkout-success__heading\">{text.heading}</h1>\n {showReference && (\n <p className=\"sf-checkout-success__reference\">\n {text.reference.replace('{id}', order.id)}\n </p>\n )}\n {slots?.header}\n <p className=\"sf-checkout-success__status\">{text.status[order.status]}</p>\n {showSummaryHeading && (\n <h2 className=\"sf-checkout-success__summary-heading\">\n {text.summaryHeading}\n </h2>\n )}\n {slots?.beforeLines}\n <ul className=\"sf-checkout-success__lines\">\n {order.items.map((item) => (\n <li key={item.productId} className=\"sf-checkout-success__line\">\n <span>\n {item.name} × {item.quantity}\n </span>\n <span>{money(item.priceCents * item.quantity)}</span>\n </li>\n ))}\n </ul>\n <dl className=\"sf-checkout-success__totals\">\n <div className=\"sf-checkout-success__row\">\n <dt>{text.subtotal}</dt>\n <dd>{money(order.subtotalCents)}</dd>\n </div>\n {order.discountCents > 0 && (\n <div\n className=\"sf-checkout-success__row sf-checkout-success__row--discount\"\n data-discount\n >\n <dt>\n {text.discount}\n {order.discountCode && ` (${order.discountCode})`}\n </dt>\n <dd>−{money(order.discountCents)}</dd>\n </div>\n )}\n {order.shippingLabel && (\n <div className=\"sf-checkout-success__row\">\n <dt>\n {text.shipping} ({order.shippingLabel})\n </dt>\n <dd>{money(order.shippingCents)}</dd>\n </div>\n )}\n <div className=\"sf-checkout-success__row sf-checkout-success__row--total\">\n <dt>{text.total}</dt>\n <dd>{money(order.totalCents)}</dd>\n </div>\n </dl>\n {slots?.afterTotals}\n {lines.length > 0 && (\n <address className=\"sf-checkout-success__address\">\n <strong>{text.shipTo}</strong>\n {lines.map((line) => (\n <span key={line}>{line}</span>\n ))}\n </address>\n )}\n {fulfillment?.trackingNumber && (\n <p className=\"sf-checkout-success__tracking\">\n {fulfillment.carrier && `${fulfillment.carrier} · `}\n {fulfillment.trackingUrl ? (\n <a href={fulfillment.trackingUrl}>\n {text.tracking} {fulfillment.trackingNumber}\n </a>\n ) : (\n fulfillment.trackingNumber\n )}\n </p>\n )}\n {actions && <div className=\"sf-checkout-success__actions\">{actions}</div>}\n </section>\n );\n}\n","import { useEffect, useRef } from 'react';\nimport { useCart } from './cart-provider';\n\nexport function ClearCartOnMount() {\n const { hydrated, clear } = useCart();\n const cleared = useRef(false);\n useEffect(() => {\n if (!hydrated || cleared.current) return;\n cleared.current = true;\n clear();\n }, [hydrated, clear]);\n return null;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["_Fragment","_Fragment","_Fragment","formatPrice","VAR_NAMES","themeToCssVars","vars","key","value","theme","defaults","priority","slug","StorefrontContext","createContext","StorefrontProvider","overrides","hrefs","useStorefront","useContext","PriceTag","classes","sold","className","priceCents","currency","locale","defaultAvailabilityLabels","AvailabilityBadge","text","labels","variant","label","availability","quantity","ProductCard","product","image","renderLink","Fragment","renderImage","ProductGrid","products","emptyState","index","priorityCount","CategoryFilter","ordered","sortCategories","categories","controlled","onSelect","item","active","selected","allLabel","c","formatLabel","ProductGallery","setActive","useState","images","event","i","ShopView","slots","activeCategory","formatCategoryLabel","sortForDisplay","nextHref","loadMoreLabel","ProductDetailView","backHref","backLabel","FeaturedProducts","heading","emptyCart","addLine","first","state","line","existing","l","cartReducer","action","subtotalCents","sum","itemCount","localStorageCartStorage","raw","parsed","result","cartLineSchema","CartContext","defaultStorage","CartProvider","dispatch","useReducer","hydrated","setHydrated","isOpen","setOpen","stateRef","useRef","useEffect","cancelled","storage","loaded","add","useCallback","useMemo","useCart","ctx","defaultDiscountLabels","discountReasonMessage","reason","defaultCheckoutLabels","assign","url","useCheckout","cart","busy","setBusy","message","setMessage","startCheckout","lines","options","onDiscountInvalid","remove","onRedirect","defaultAddToCartLabels","AddToCartButton","inCart","unavailable","atCap","disabled","handleClick","openOnAdd","defaultCartLineItemLabels","CartLineItem","editable","setQuantity","parseDurationMs","match","amount","useDrawerPresence","durationMs","setDurationMs","wasOpen","setWasOpen","lingering","setLingering","backdropRef","timer","DiscountField","open","setValue","submit","code","onApply","applied","onRemove","error","pending","useDiscount","items","signature","setApplied","setError","setPending","latest","request","previewedSignature","clear","run","current","id","onReject","apply","delay","discount","CartSummary","subtotal","discountLabels","checkoutDisabled","onCheckout","checkoutLabel","defaultCartDrawerLabels","FOCUSABLE","CartDrawer","panelRef","presence","trigger","handleKeyDown","last","summary","CartButton","count","showCount","hideZeroCount","ariaLabel","labelHidden","icon","defaultCheckoutSuccessLabels","addressLines","region","address","CheckoutSuccessView","money","cents","order","continueProps","continueHref","continueLink","actions","showReference","formatOrderRef","showSummaryHeading","fulfillment","ClearCartOnMount","cleared"],"sources":["../src/lib/theme.ts","../src/lib/provider.tsx","../src/components/price-tag/price-tag.tsx","../src/components/availability-badge/availability-badge.tsx","../src/components/product-card/product-card.tsx","../src/components/product-grid/product-grid.tsx","../src/components/category-filter/category-filter.tsx","../src/components/product-gallery/product-gallery.tsx","../src/views/shop-view.tsx","../src/views/product-detail-view.tsx","../src/views/featured-products.tsx","../src/cart/cart-reducer.ts","../src/cart/cart-storage.ts","../src/cart/cart-provider.tsx","../src/components/cart-summary/discount-types.ts","../src/cart/use-checkout.ts","../src/components/add-to-cart-button/add-to-cart-button.tsx","../src/components/cart-line-item/cart-line-item.tsx","../src/lib/parse-duration.ts","../src/components/cart-drawer/use-drawer-presence.ts","../src/components/cart-summary/discount-field.tsx","../src/components/cart-summary/use-discount.ts","../src/components/cart-summary/cart-summary.tsx","../src/components/cart-drawer/cart-drawer.tsx","../src/components/cart-button/cart-button.tsx","../src/components/checkout-success/labels.ts","../src/components/checkout-success/checkout-success-view.tsx","../src/cart/clear-cart-on-mount.tsx"],"sourcesContent":["export interface StorefrontTheme {\n colorBg?: string;\n colorSurface?: string;\n colorFg?: string;\n colorMuted?: string;\n colorAccent?: string;\n colorAccentFg?: string;\n colorBorder?: string;\n colorSold?: string;\n radius?: string;\n fontDisplay?: string;\n fontBody?: string;\n spaceUnit?: string;\n}\n\nconst VAR_NAMES: Record<keyof StorefrontTheme, string> = {\n colorBg: '--sf-color-bg',\n colorSurface: '--sf-color-surface',\n colorFg: '--sf-color-fg',\n colorMuted: '--sf-color-muted',\n colorAccent: '--sf-color-accent',\n colorAccentFg: '--sf-color-accent-fg',\n colorBorder: '--sf-color-border',\n colorSold: '--sf-color-sold',\n radius: '--sf-radius',\n fontDisplay: '--sf-font-display',\n fontBody: '--sf-font-body',\n spaceUnit: '--sf-space-unit',\n};\n\nexport function themeToCssVars(theme: StorefrontTheme): Record<string, string> {\n const vars: Record<string, string> = {};\n for (const key of Object.keys(VAR_NAMES) as (keyof StorefrontTheme)[]) {\n const value = theme[key];\n if (value !== undefined) vars[VAR_NAMES[key]] = value;\n }\n return vars;\n}\n","import { createContext, useContext, type ReactNode } from 'react';\n\nexport interface LinkProps {\n href: string;\n children: ReactNode;\n className?: string;\n}\n\nexport interface ImageProps {\n src: string;\n alt: string;\n sizes?: string;\n className?: string;\n priority?: boolean;\n}\n\nexport interface StorefrontConfig {\n renderLink: (props: LinkProps) => ReactNode;\n renderImage: (props: ImageProps) => ReactNode;\n locale: string;\n hrefs: {\n product: (slug: string) => string;\n category: (slug: string) => string;\n };\n}\n\nconst defaults: StorefrontConfig = {\n renderLink: ({ href, children, className }) => (\n <a href={href} className={className}>\n {children}\n </a>\n ),\n renderImage: ({ src, alt, sizes, className, priority }) => (\n <img\n src={src}\n alt={alt}\n sizes={sizes}\n className={className}\n loading={priority ? 'eager' : 'lazy'}\n decoding=\"async\"\n fetchPriority={priority ? 'high' : undefined}\n />\n ),\n locale: 'en-US',\n hrefs: {\n product: (slug) => `/shop/${slug}`,\n category: (slug) => `/shop?category=${slug}`,\n },\n};\n\nconst StorefrontContext = createContext<StorefrontConfig>(defaults);\n\nexport interface StorefrontProviderProps extends Partial<\n Omit<StorefrontConfig, 'hrefs'>\n> {\n hrefs?: Partial<StorefrontConfig['hrefs']>;\n children: ReactNode;\n}\n\nexport function StorefrontProvider({\n children,\n hrefs,\n ...overrides\n}: StorefrontProviderProps) {\n const value: StorefrontConfig = {\n ...defaults,\n ...overrides,\n hrefs: { ...defaults.hrefs, ...hrefs },\n };\n return (\n <StorefrontContext.Provider value={value}>\n {children}\n </StorefrontContext.Provider>\n );\n}\n\nexport function useStorefront(): StorefrontConfig {\n return useContext(StorefrontContext);\n}\n","import { formatPrice } from '../../lib/format-price';\nimport { useStorefront } from '../../lib/provider';\n\nexport interface PriceTagProps {\n priceCents: number;\n currency: string;\n sold?: boolean;\n className?: string;\n}\n\nexport function PriceTag({\n priceCents,\n currency,\n sold = false,\n className,\n}: PriceTagProps) {\n const { locale } = useStorefront();\n const classes = ['sf-price', sold && 'sf-price--sold', className]\n .filter(Boolean)\n .join(' ');\n return (\n <span className={classes}>{formatPrice(priceCents, currency, locale)}</span>\n );\n}\n","import type { ProductAvailability } from '@develemit/commerce-contracts';\n\nexport interface AvailabilityLabels {\n oneOfAKind: string;\n sold: string;\n reserved: string;\n available: string;\n}\n\nexport const defaultAvailabilityLabels: AvailabilityLabels = {\n oneOfAKind: 'One of a kind',\n sold: 'Sold',\n reserved: 'On hold',\n available: 'Available',\n};\n\nexport interface AvailabilityBadgeProps {\n availability: ProductAvailability;\n quantity: number;\n labels?: Partial<AvailabilityLabels>;\n className?: string;\n}\n\nexport function AvailabilityBadge({\n availability,\n quantity,\n labels,\n className,\n}: AvailabilityBadgeProps) {\n const text = { ...defaultAvailabilityLabels, ...labels };\n let variant: string;\n let label: string;\n if (availability === 'sold') {\n variant = 'sold';\n label = text.sold;\n } else if (availability === 'reserved') {\n variant = 'reserved';\n label = text.reserved;\n } else if (quantity === 1) {\n variant = 'unique';\n label = text.oneOfAKind;\n } else {\n variant = 'available';\n label = text.available;\n }\n const classes = ['sf-badge', `sf-badge--${variant}`, className]\n .filter(Boolean)\n .join(' ');\n return (\n <span role=\"status\" className={classes}>\n {label}\n </span>\n );\n}\n","import type { ProductCard as ProductCardData } from '@develemit/commerce-contracts';\nimport { useStorefront } from '../../lib/provider';\nimport {\n AvailabilityBadge,\n type AvailabilityLabels,\n} from '../availability-badge/availability-badge';\nimport { PriceTag } from '../price-tag/price-tag';\n\nexport interface ProductCardProps {\n product: ProductCardData;\n labels?: Partial<AvailabilityLabels>;\n sizes?: string;\n priority?: boolean;\n}\n\nexport function ProductCard({\n product,\n labels,\n sizes,\n priority,\n}: ProductCardProps) {\n const { renderLink, renderImage, hrefs } = useStorefront();\n const sold = product.availability === 'sold';\n const image = product.images[0];\n const classes = ['sf-card', sold && 'sf-card--sold']\n .filter(Boolean)\n .join(' ');\n return (\n <article className={classes}>\n {renderLink({\n href: hrefs.product(product.slug),\n className: 'sf-card__link',\n children: (\n <>\n <div className=\"sf-card__media\">\n {image ? (\n renderImage({\n src: image.url,\n alt: image.alt,\n sizes,\n className: 'sf-card__image',\n priority,\n })\n ) : (\n <div className=\"sf-card__placeholder\" aria-hidden=\"true\" />\n )}\n </div>\n <div className=\"sf-card__body\">\n <h3 className=\"sf-card__title\">{product.name}</h3>\n <PriceTag\n priceCents={product.priceCents}\n currency={product.currency}\n sold={sold}\n />\n <AvailabilityBadge\n availability={product.availability}\n quantity={product.quantity}\n labels={labels}\n />\n </div>\n </>\n ),\n })}\n </article>\n );\n}\n","import type { ReactNode } from 'react';\nimport type { ProductCard as ProductCardData } from '@develemit/commerce-contracts';\nimport type { AvailabilityLabels } from '../availability-badge/availability-badge';\nimport { ProductCard } from '../product-card/product-card';\n\nexport interface ProductGridProps {\n products: ProductCardData[];\n emptyState?: ReactNode;\n labels?: Partial<AvailabilityLabels>;\n priorityCount?: number;\n}\n\nexport function ProductGrid({\n products,\n emptyState,\n labels,\n priorityCount = 0,\n}: ProductGridProps) {\n if (products.length === 0) {\n return (\n <div className=\"sf-grid-empty\">{emptyState ?? 'No products found.'}</div>\n );\n }\n return (\n <ul className=\"sf-grid\">\n {products.map((product, index) => (\n <li key={product.id} className=\"sf-grid__item\">\n <ProductCard\n product={product}\n labels={labels}\n priority={index < priorityCount}\n />\n </li>\n ))}\n </ul>\n );\n}\n","import type { Category } from '@develemit/commerce-contracts';\nimport { useStorefront } from '../../lib/provider';\n\nexport interface CategoryFilterProps {\n categories: Category[];\n selected?: string;\n onSelect?: (slug: string | undefined) => void;\n allLabel?: string;\n label?: string;\n formatLabel?: (category: Category) => string;\n sortCategories?: (a: Category, b: Category) => number;\n}\n\nexport function CategoryFilter({\n categories,\n selected,\n onSelect,\n allLabel = 'All',\n label = 'Categories',\n formatLabel,\n sortCategories,\n}: CategoryFilterProps) {\n const { renderLink, hrefs } = useStorefront();\n const ordered = sortCategories\n ? [...categories].sort(sortCategories)\n : categories;\n const controlled = onSelect !== undefined;\n\n const item = (slug: string | undefined, text: string, href: string) => {\n const active = selected === slug;\n const className = `sf-filter__item${active ? ' sf-filter__item--active' : ''}`;\n return (\n <li key={slug ?? '__all'}>\n {controlled ? (\n <button\n type=\"button\"\n className={className}\n aria-pressed={active}\n onClick={() => onSelect(slug)}\n >\n {text}\n </button>\n ) : (\n renderLink({\n href,\n className,\n children: text,\n })\n )}\n </li>\n );\n };\n\n return (\n <nav aria-label={label} className=\"sf-filter\">\n <ul className=\"sf-filter__list\">\n {item(undefined, allLabel, hrefs.category(''))}\n {ordered.map((c) =>\n item(\n c.slug,\n formatLabel ? formatLabel(c) : c.label,\n hrefs.category(c.slug),\n ),\n )}\n </ul>\n </nav>\n );\n}\n","import { useState, type KeyboardEvent } from 'react';\nimport type { ProductImage } from '@develemit/commerce-contracts';\nimport { useStorefront } from '../../lib/provider';\n\nexport interface ProductGalleryProps {\n images: ProductImage[];\n sizes?: string;\n}\n\nexport function ProductGallery({ images, sizes }: ProductGalleryProps) {\n const { renderImage } = useStorefront();\n const [active, setActive] = useState(0);\n\n if (images.length === 0) return null;\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'ArrowRight') {\n setActive((i) => (i + 1) % images.length);\n } else if (event.key === 'ArrowLeft') {\n setActive((i) => (i - 1 + images.length) % images.length);\n } else if (event.key === 'Home') {\n setActive(0);\n } else if (event.key === 'End') {\n setActive(images.length - 1);\n } else {\n return;\n }\n event.preventDefault();\n };\n\n return (\n <div\n className=\"sf-gallery\"\n role=\"group\"\n aria-roledescription=\"carousel\"\n aria-label=\"Product images\"\n tabIndex={0}\n onKeyDown={onKeyDown}\n >\n <div className=\"sf-gallery__stage\">\n {images.map((image, index) => (\n <div\n key={image.url}\n className=\"sf-gallery__slide\"\n hidden={index !== active}\n >\n {renderImage({\n src: image.url,\n alt: image.alt,\n sizes,\n priority: index === active,\n className: 'sf-gallery__image',\n })}\n </div>\n ))}\n </div>\n {images.length > 1 && (\n <div className=\"sf-gallery__thumbs\">\n {images.map((image, index) => (\n <button\n key={image.url}\n type=\"button\"\n className={`sf-gallery__thumb${index === active ? ' sf-gallery__thumb--active' : ''}`}\n aria-label={`Show image ${index + 1} of ${images.length}`}\n aria-current={index === active}\n onClick={() => setActive(index)}\n >\n {renderImage({\n src: image.url,\n alt: '',\n className: 'sf-gallery__thumb-image',\n })}\n </button>\n ))}\n </div>\n )}\n </div>\n );\n}\n","import type { ReactNode } from 'react';\nimport type { Category, ProductCard } from '@develemit/commerce-contracts';\nimport { CategoryFilter } from '../components/category-filter/category-filter';\nimport { ProductGrid } from '../components/product-grid/product-grid';\nimport type { AvailabilityLabels } from '../components/availability-badge/availability-badge';\nimport { useStorefront } from '../lib/provider';\nimport { sortForDisplay } from './sort-for-display';\n\nexport interface ShopViewSlots {\n header?: ReactNode;\n intro?: ReactNode;\n emptyState?: ReactNode;\n}\n\nexport interface ShopViewProps {\n products: ProductCard[];\n categories: Category[];\n activeCategory?: string;\n slots?: ShopViewSlots;\n labels?: Partial<AvailabilityLabels>;\n allLabel?: string;\n formatCategoryLabel?: (category: Category) => string;\n sortCategories?: (a: Category, b: Category) => number;\n nextHref?: string;\n loadMoreLabel?: string;\n}\n\nexport function ShopView({\n products,\n categories,\n activeCategory,\n slots = {},\n labels,\n allLabel,\n formatCategoryLabel,\n sortCategories,\n nextHref,\n loadMoreLabel = 'Load more',\n}: ShopViewProps) {\n const { renderLink } = useStorefront();\n return (\n <div className=\"sf-shop\">\n {slots.header}\n {slots.intro}\n <CategoryFilter\n categories={categories}\n selected={activeCategory}\n allLabel={allLabel}\n formatLabel={formatCategoryLabel}\n sortCategories={sortCategories}\n />\n <ProductGrid\n products={sortForDisplay(products)}\n emptyState={slots.emptyState}\n labels={labels}\n priorityCount={6}\n />\n {nextHref && (\n <div className=\"sf-shop__pager\">\n {renderLink({\n href: nextHref,\n className: 'sf-shop__more',\n children: loadMoreLabel,\n })}\n </div>\n )}\n </div>\n );\n}\n","import type { ReactNode } from 'react';\nimport type { Product } from '@develemit/commerce-contracts';\nimport {\n AvailabilityBadge,\n type AvailabilityLabels,\n} from '../components/availability-badge/availability-badge';\nimport { PriceTag } from '../components/price-tag/price-tag';\nimport { useStorefront } from '../lib/provider';\nimport { ProductGallery } from '../components/product-gallery/product-gallery';\n\nexport interface ProductDetailViewSlots {\n actions?: ReactNode;\n afterDetails?: ReactNode;\n}\n\nexport interface ProductDetailViewProps {\n product: Product;\n slots?: ProductDetailViewSlots;\n labels?: Partial<AvailabilityLabels>;\n backHref?: string;\n backLabel?: string;\n}\n\nexport function ProductDetailView({\n product,\n slots = {},\n labels,\n backHref,\n backLabel = 'Back to the shop',\n}: ProductDetailViewProps) {\n const { renderLink } = useStorefront();\n const sold = product.availability === 'sold';\n return (\n <article className={sold ? 'sf-detail sf-detail--sold' : 'sf-detail'}>\n {backHref &&\n renderLink({\n href: backHref,\n className: 'sf-detail__back',\n children: backLabel,\n })}\n <ProductGallery images={product.images} />\n <div className=\"sf-detail__body\">\n <h1 className=\"sf-detail__title\">{product.name}</h1>\n <PriceTag priceCents={product.priceCents} currency={product.currency} />\n <AvailabilityBadge\n availability={product.availability}\n quantity={product.quantity}\n labels={labels}\n />\n <p className=\"sf-detail__description\">{product.description}</p>\n {(product.materials || product.dimensions) && (\n <dl className=\"sf-detail__specs\">\n {product.materials && (\n <>\n <dt>Materials</dt>\n <dd>{product.materials}</dd>\n </>\n )}\n {product.dimensions && (\n <>\n <dt>Dimensions</dt>\n <dd>{product.dimensions}</dd>\n </>\n )}\n </dl>\n )}\n {slots.actions && (\n <div className=\"sf-detail__actions\">{slots.actions}</div>\n )}\n {slots.afterDetails}\n </div>\n </article>\n );\n}\n","import type { ProductCard } from '@develemit/commerce-contracts';\nimport type { AvailabilityLabels } from '../components/availability-badge/availability-badge';\nimport { ProductGrid } from '../components/product-grid/product-grid';\n\nexport interface FeaturedProductsProps {\n products: ProductCard[];\n heading?: string;\n labels?: Partial<AvailabilityLabels>;\n}\n\nexport function FeaturedProducts({\n products,\n heading = 'Featured',\n labels,\n}: FeaturedProductsProps) {\n if (products.length === 0) return null;\n return (\n <section className=\"sf-featured\">\n <h2 className=\"sf-featured__heading\">{heading}</h2>\n <ProductGrid products={products} labels={labels} priorityCount={6} />\n </section>\n );\n}\n","import type { CartLine } from '@develemit/commerce-contracts';\n\nexport interface CartState {\n lines: CartLine[];\n}\n\nexport type CartAction =\n | { type: 'add'; line: CartLine }\n | { type: 'remove'; productId: string }\n | { type: 'setQuantity'; productId: string; quantity: number }\n | { type: 'clear' }\n | { type: 'hydrate'; state: CartState };\n\nexport interface AddResult {\n state: CartState;\n added: boolean;\n capped: boolean;\n reason?: 'currency-mismatch';\n}\n\nexport const emptyCart: CartState = { lines: [] };\n\nexport function addLine(state: CartState, line: CartLine): AddResult {\n const first = state.lines[0];\n if (first && first.currency !== line.currency) {\n return { state, added: false, capped: false, reason: 'currency-mismatch' };\n }\n const existing = state.lines.find((l) => l.productId === line.productId);\n if (!existing) {\n const quantity = Math.min(line.quantity, line.maxQuantity);\n return {\n state: { lines: [...state.lines, { ...line, quantity }] },\n added: true,\n capped: quantity < line.quantity,\n };\n }\n if (existing.quantity >= existing.maxQuantity) {\n return { state, added: false, capped: true };\n }\n const quantity = Math.min(\n existing.quantity + line.quantity,\n existing.maxQuantity,\n );\n return {\n state: {\n lines: state.lines.map((l) =>\n l.productId === line.productId ? { ...l, quantity } : l,\n ),\n },\n added: true,\n capped: existing.quantity + line.quantity > existing.maxQuantity,\n };\n}\n\nexport function cartReducer(state: CartState, action: CartAction): CartState {\n switch (action.type) {\n case 'add':\n return addLine(state, action.line).state;\n case 'remove':\n return {\n lines: state.lines.filter((l) => l.productId !== action.productId),\n };\n case 'setQuantity': {\n if (action.quantity <= 0) {\n return cartReducer(state, {\n type: 'remove',\n productId: action.productId,\n });\n }\n return {\n lines: state.lines.map((l) =>\n l.productId === action.productId\n ? { ...l, quantity: Math.min(action.quantity, l.maxQuantity) }\n : l,\n ),\n };\n }\n case 'clear':\n return emptyCart;\n case 'hydrate':\n return action.state;\n }\n}\n\nexport function subtotalCents(state: CartState): number {\n return state.lines.reduce((sum, l) => sum + l.priceCents * l.quantity, 0);\n}\n\nexport function itemCount(state: CartState): number {\n return state.lines.reduce((sum, l) => sum + l.quantity, 0);\n}\n","import { cartLineSchema } from '@develemit/commerce-contracts';\nimport { emptyCart, type CartState } from './cart-reducer';\n\nexport interface CartStorage {\n load: () => CartState | Promise<CartState>;\n save: (state: CartState) => void | Promise<void>;\n}\n\nexport function localStorageCartStorage(key = 'sf-cart'): CartStorage {\n return {\n load() {\n if (typeof window === 'undefined') return emptyCart;\n try {\n const raw = window.localStorage.getItem(key);\n if (!raw) return emptyCart;\n const parsed: unknown = JSON.parse(raw);\n const lines = Array.isArray((parsed as CartState)?.lines)\n ? (parsed as CartState).lines\n : [];\n const valid = lines.flatMap((line) => {\n const result = cartLineSchema.safeParse(line);\n return result.success ? [result.data] : [];\n });\n return { lines: valid };\n } catch {\n return emptyCart;\n }\n },\n save(state) {\n if (typeof window === 'undefined') return;\n try {\n window.localStorage.setItem(key, JSON.stringify(state));\n } catch {\n // storage full or blocked: the cart simply stays in memory\n }\n },\n };\n}\n","import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useReducer,\n useRef,\n useState,\n type ReactNode,\n} from 'react';\nimport type { CartLine } from '@develemit/commerce-contracts';\nimport {\n addLine,\n cartReducer,\n emptyCart,\n itemCount,\n subtotalCents,\n type AddResult,\n type CartState,\n} from './cart-reducer';\nimport { localStorageCartStorage, type CartStorage } from './cart-storage';\n\nexport interface CartContextValue {\n lines: CartLine[];\n itemCount: number;\n subtotalCents: number;\n currency: string | null;\n hydrated: boolean;\n isOpen: boolean;\n open: () => void;\n close: () => void;\n add: (line: CartLine) => AddResult;\n remove: (productId: string) => void;\n setQuantity: (productId: string, quantity: number) => void;\n clear: () => void;\n}\n\nconst CartContext = createContext<CartContextValue | null>(null);\n\nexport interface CartProviderProps {\n storage?: CartStorage;\n children: ReactNode;\n}\n\nconst defaultStorage = localStorageCartStorage();\n\nexport function CartProvider({\n storage = defaultStorage,\n children,\n}: CartProviderProps) {\n const [state, dispatch] = useReducer(cartReducer, emptyCart);\n const [hydrated, setHydrated] = useState(false);\n const [isOpen, setOpen] = useState(false);\n const stateRef = useRef<CartState>(state);\n useEffect(() => {\n stateRef.current = state;\n }, [state]);\n\n useEffect(() => {\n let cancelled = false;\n Promise.resolve(storage.load()).then((loaded) => {\n if (cancelled) return;\n dispatch({ type: 'hydrate', state: loaded });\n setHydrated(true);\n });\n return () => {\n cancelled = true;\n };\n }, [storage]);\n\n useEffect(() => {\n if (hydrated) void storage.save(state);\n }, [hydrated, state, storage]);\n\n const add = useCallback((line: CartLine) => {\n const result = addLine(stateRef.current, line);\n if (result.added) {\n stateRef.current = result.state;\n dispatch({ type: 'add', line });\n }\n return result;\n }, []);\n\n const value = useMemo<CartContextValue>(\n () => ({\n lines: state.lines,\n itemCount: itemCount(state),\n subtotalCents: subtotalCents(state),\n currency: state.lines[0]?.currency ?? null,\n hydrated,\n isOpen,\n open: () => setOpen(true),\n close: () => setOpen(false),\n add,\n remove: (productId) => dispatch({ type: 'remove', productId }),\n setQuantity: (productId, quantity) =>\n dispatch({ type: 'setQuantity', productId, quantity }),\n clear: () => dispatch({ type: 'clear' }),\n }),\n [state, hydrated, isOpen, add],\n );\n\n return <CartContext.Provider value={value}>{children}</CartContext.Provider>;\n}\n\nexport function useCart(): CartContextValue {\n const ctx = useContext(CartContext);\n if (!ctx) throw new Error('useCart must be used inside <CartProvider>');\n return ctx;\n}\n","import type { DiscountRejectionReason } from '@develemit/commerce-contracts';\n\nexport type DiscountPreviewResult =\n | { ok: true; code: string; discountCents: number }\n | { ok: false; reason: DiscountRejectionReason };\n\nexport interface CartSummaryDiscount {\n onPreview: (\n code: string,\n items: { productId: string; quantity: number }[],\n ) => Promise<DiscountPreviewResult>;\n onApply?: (code: string | null) => void;\n debounceMs?: number;\n}\n\nexport interface DiscountLabels {\n toggle: string;\n field: string;\n apply: string;\n remove: string;\n line: string;\n reasons: Record<DiscountRejectionReason, string>;\n unavailable: string;\n}\n\nexport const defaultDiscountLabels: DiscountLabels = {\n toggle: 'Have a code?',\n field: 'Discount code',\n apply: 'Apply',\n remove: 'Remove',\n line: 'Discount',\n unavailable: 'We could not check that code. Please try again.',\n reasons: {\n not_found: 'That code is not valid.',\n inactive: 'That code is not valid.',\n not_started: 'That code is not active yet.',\n expired: 'That code has expired.',\n min_subtotal: 'Your cart is below the minimum for that code.',\n exhausted: 'That code is no longer available.',\n currency: 'That code does not apply to this cart.',\n },\n};\n\nexport function discountReasonMessage(\n reason: DiscountRejectionReason,\n overrides?: Partial<DiscountLabels['reasons']>,\n): string {\n return overrides?.[reason] ?? defaultDiscountLabels.reasons[reason];\n}\n","import { useCallback, useState } from 'react';\nimport type { DiscountRejectionReason } from '@develemit/commerce-contracts';\nimport type { CheckoutResult } from '../client/checkout-result';\nimport { discountReasonMessage } from '../components/cart-summary/discount-types';\nimport { useCart } from './cart-provider';\n\nexport interface CheckoutLabels {\n outOfStock: string;\n unavailable: string;\n discountReasons?: Partial<Record<DiscountRejectionReason, string>>;\n}\n\nexport const defaultCheckoutLabels: CheckoutLabels = {\n outOfStock:\n 'Some items just sold or are on hold, so we removed them from your cart.',\n unavailable: 'Checkout is unavailable right now. Please try again.',\n};\n\nexport interface StartCheckoutOptions {\n discountCode?: string;\n}\n\nexport interface UseCheckoutOptions {\n startCheckout: (\n items: { productId: string; quantity: number }[],\n options: StartCheckoutOptions,\n ) => Promise<CheckoutResult>;\n onRedirect?: (url: string) => void;\n onDiscountInvalid?: (reason: DiscountRejectionReason) => void;\n labels?: Partial<CheckoutLabels>;\n}\n\nexport interface UseCheckoutValue {\n checkout: (options?: StartCheckoutOptions) => Promise<void>;\n busy: boolean;\n message: string | null;\n clearMessage: () => void;\n}\n\nconst assign = (url: string) => window.location.assign(url);\n\nexport function useCheckout({\n startCheckout,\n onRedirect = assign,\n onDiscountInvalid,\n labels,\n}: UseCheckoutOptions): UseCheckoutValue {\n const cart = useCart();\n const [busy, setBusy] = useState(false);\n const [message, setMessage] = useState<string | null>(null);\n const { lines, remove } = cart;\n const text = { ...defaultCheckoutLabels, ...labels };\n\n const checkout = useCallback(\n async (options: StartCheckoutOptions = {}) => {\n setBusy(true);\n setMessage(null);\n try {\n const result = await startCheckout(\n lines.map((l) => ({ productId: l.productId, quantity: l.quantity })),\n options,\n );\n if ('url' in result) {\n onRedirect(result.url);\n return;\n }\n if (result.error === 'discount_invalid') {\n setMessage(\n discountReasonMessage(result.reason, text.discountReasons),\n );\n onDiscountInvalid?.(result.reason);\n } else {\n result.productIds.forEach(remove);\n setMessage(text.outOfStock);\n }\n } catch {\n setMessage(text.unavailable);\n }\n setBusy(false);\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [startCheckout, onRedirect, onDiscountInvalid, lines, remove, labels],\n );\n\n const clearMessage = useCallback(() => setMessage(null), []);\n\n return { checkout, busy, message, clearMessage };\n}\n","import type { Product } from '@develemit/commerce-contracts';\nimport { useCart } from '../../cart/cart-provider';\n\nexport interface AddToCartLabels {\n add: string;\n inCart: string;\n unavailable: string;\n sold: string;\n}\n\nexport const defaultAddToCartLabels: AddToCartLabels = {\n add: 'Add to cart',\n inCart: 'In your cart',\n unavailable: 'Unavailable',\n sold: 'Sold',\n};\n\nexport interface AddToCartButtonProps {\n product: Product;\n labels?: Partial<AddToCartLabels>;\n openOnAdd?: boolean;\n className?: string;\n}\n\nexport function AddToCartButton({\n product,\n labels,\n openOnAdd = true,\n className,\n}: AddToCartButtonProps) {\n const cart = useCart();\n const text = { ...defaultAddToCartLabels, ...labels };\n const inCart =\n cart.lines.find((l) => l.productId === product.id)?.quantity ?? 0;\n const sold = product.availability === 'sold';\n const unavailable = product.availability !== 'available';\n const atCap = inCart >= product.quantity;\n const disabled = !cart.hydrated || unavailable || atCap;\n const label = unavailable ? text.unavailable : atCap ? text.inCart : text.add;\n\n function handleClick() {\n const result = cart.add({\n productId: product.id,\n slug: product.slug,\n name: product.name,\n priceCents: product.priceCents,\n currency: product.currency,\n imageUrl: product.images[0]?.url,\n quantity: 1,\n maxQuantity: product.quantity,\n });\n if (result.added && openOnAdd) cart.open();\n }\n\n if (sold) {\n return (\n <span className={['sf-sold-note', className].filter(Boolean).join(' ')}>\n {text.sold}\n </span>\n );\n }\n\n return (\n <button\n type=\"button\"\n className={['sf-button', className].filter(Boolean).join(' ')}\n disabled={disabled}\n onClick={handleClick}\n >\n {label}\n </button>\n );\n}\n","import type { CartLine } from '@develemit/commerce-contracts';\nimport { useCart } from '../../cart/cart-provider';\nimport { formatPrice } from '../../lib/format-price';\nimport { useStorefront } from '../../lib/provider';\n\nexport interface CartLineItemLabels {\n remove: string;\n quantity: string;\n}\n\nexport const defaultCartLineItemLabels: CartLineItemLabels = {\n remove: 'Remove',\n quantity: 'Quantity',\n};\n\nexport interface CartLineItemProps {\n line: CartLine;\n labels?: Partial<CartLineItemLabels>;\n}\n\nexport function CartLineItem({ line, labels }: CartLineItemProps) {\n const { remove, setQuantity } = useCart();\n const { locale, renderLink, renderImage, hrefs } = useStorefront();\n const text = { ...defaultCartLineItemLabels, ...labels };\n const editable = line.maxQuantity > 1;\n return (\n <li className=\"sf-cart-line\">\n {line.imageUrl &&\n renderImage({\n src: line.imageUrl,\n alt: '',\n sizes: '80px',\n className: 'sf-cart-line__image',\n })}\n <div className=\"sf-cart-line__body\">\n {renderLink({\n href: hrefs.product(line.slug),\n className: 'sf-cart-line__name',\n children: line.name,\n })}\n <span className=\"sf-cart-line__price\">\n {formatPrice(line.priceCents * line.quantity, line.currency, locale)}\n </span>\n {editable && (\n <label className=\"sf-cart-line__qty\">\n {text.quantity}\n <input\n type=\"number\"\n min={1}\n max={line.maxQuantity}\n value={line.quantity}\n onChange={(e) =>\n setQuantity(line.productId, Number(e.target.value))\n }\n />\n </label>\n )}\n <button\n type=\"button\"\n className=\"sf-link-button sf-cart-line__remove\"\n onClick={() => remove(line.productId)}\n aria-label={`${text.remove} ${line.name}`}\n >\n {text.remove}\n </button>\n </div>\n </li>\n );\n}\n","export function parseDurationMs(value: string): number {\n const match = /^\\s*(-?\\d*\\.?\\d+)\\s*(ms|s)\\s*$/.exec(value);\n if (!match) return 0;\n const amount = Number(match[1]);\n return Math.max(0, match[2] === 's' ? amount * 1000 : amount);\n}\n","import { useEffect, useState, type RefObject } from 'react';\nimport { parseDurationMs } from '../../lib/parse-duration';\n\nexport function useDrawerPresence(\n isOpen: boolean,\n backdropRef: RefObject<HTMLElement | null>,\n) {\n const [durationMs, setDurationMs] = useState(0);\n const [wasOpen, setWasOpen] = useState(isOpen);\n const [lingering, setLingering] = useState(false);\n\n if (wasOpen !== isOpen) {\n setWasOpen(isOpen);\n setLingering(!isOpen && durationMs > 0);\n }\n\n useEffect(() => {\n if (!isOpen || !backdropRef.current) return;\n setDurationMs(\n parseDurationMs(\n getComputedStyle(backdropRef.current).getPropertyValue(\n '--sf-motion-duration',\n ),\n ),\n );\n }, [isOpen, backdropRef]);\n\n useEffect(() => {\n if (!lingering) return;\n const timer = setTimeout(() => setLingering(false), durationMs);\n return () => clearTimeout(timer);\n }, [lingering, durationMs]);\n\n return { mounted: isOpen || lingering, state: isOpen ? 'open' : 'closed' };\n}\n","import { useState, type FormEvent } from 'react';\nimport { formatPrice } from '../../lib/format-price';\nimport type { DiscountLabels } from './discount-types';\nimport type { AppliedDiscount, DiscountError } from './use-discount';\n\nexport interface DiscountFieldProps {\n labels: DiscountLabels;\n applied: AppliedDiscount | null;\n error: DiscountError | null;\n pending: boolean;\n currency: string;\n locale: string;\n onApply: (code: string) => void;\n onRemove: () => void;\n}\n\nexport function DiscountField({\n labels,\n applied,\n error,\n pending,\n currency,\n locale,\n onApply,\n onRemove,\n}: DiscountFieldProps) {\n const [open, setOpen] = useState(false);\n const [value, setValue] = useState('');\n\n function submit(event: FormEvent) {\n event.preventDefault();\n const code = value.trim();\n if (code) onApply(code);\n }\n\n if (applied) {\n return (\n <p className=\"sf-cart-summary__row sf-cart-summary__discount\">\n <span>\n {labels.line} ({applied.code}){' '}\n <button type=\"button\" className=\"sf-link-button\" onClick={onRemove}>\n {labels.remove}\n </button>\n </span>\n <span className=\"sf-price\">\n −{formatPrice(applied.discountCents, currency, locale)}\n </span>\n </p>\n );\n }\n\n const message =\n error === 'unavailable'\n ? labels.unavailable\n : error\n ? labels.reasons[error]\n : null;\n return (\n <div className=\"sf-cart-summary__code\">\n {open ? (\n <form onSubmit={submit} className=\"sf-cart-summary__code-form\">\n <input\n aria-label={labels.field}\n value={value}\n onChange={(e) => setValue(e.target.value)}\n autoCapitalize=\"characters\"\n autoComplete=\"off\"\n />\n <button type=\"submit\" className=\"sf-button\" disabled={pending}>\n {labels.apply}\n </button>\n </form>\n ) : (\n <button\n type=\"button\"\n className=\"sf-link-button\"\n aria-expanded={false}\n onClick={() => setOpen(true)}\n >\n {labels.toggle}\n </button>\n )}\n {message && (\n <p role=\"alert\" className=\"sf-cart-summary__code-error\">\n {message}\n </p>\n )}\n </div>\n );\n}\n","import type { DiscountRejectionReason } from '@develemit/commerce-contracts';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { useCart } from '../../cart/cart-provider';\nimport type { CartSummaryDiscount } from './discount-types';\n\nexport interface AppliedDiscount {\n code: string;\n discountCents: number;\n}\n\nexport type DiscountError = DiscountRejectionReason | 'unavailable';\n\nexport function useDiscount(discount: CartSummaryDiscount | undefined) {\n const { lines } = useCart();\n const items = lines.map((l) => ({\n productId: l.productId,\n quantity: l.quantity,\n }));\n const signature = items.map((i) => `${i.productId}:${i.quantity}`).join(',');\n const [applied, setApplied] = useState<AppliedDiscount | null>(null);\n const [error, setError] = useState<DiscountError | null>(null);\n const [pending, setPending] = useState(false);\n const latest = useRef({ discount, items });\n useEffect(() => {\n latest.current = { discount, items };\n });\n const request = useRef(0);\n const previewedSignature = useRef(signature);\n\n const clear = useCallback(() => {\n request.current += 1;\n setApplied(null);\n latest.current.discount?.onApply?.(null);\n }, []);\n\n const run = useCallback(\n async (code: string, onReject: (reason: DiscountError) => void) => {\n const current = latest.current;\n if (!current.discount) return;\n const id = ++request.current;\n previewedSignature.current = current.items\n .map((i) => `${i.productId}:${i.quantity}`)\n .join(',');\n setPending(true);\n let result;\n try {\n result = await current.discount.onPreview(code, current.items);\n } catch {\n result = { ok: false as const, reason: 'unavailable' as const };\n }\n if (id !== request.current) return;\n setPending(false);\n if (result.ok) {\n setError(null);\n setApplied({ code: result.code, discountCents: result.discountCents });\n current.discount.onApply?.(result.code);\n } else {\n onReject(result.reason);\n }\n },\n [],\n );\n\n const apply = useCallback(\n (code: string) => run(code, (reason) => setError(reason)),\n [run],\n );\n\n const remove = useCallback(() => {\n setError(null);\n setPending(false);\n clear();\n }, [clear]);\n\n const code = applied?.code;\n const delay = discount?.debounceMs ?? 300;\n useEffect(() => {\n if (!code || signature === previewedSignature.current) return;\n const timer = setTimeout(() => {\n if (latest.current.items.length === 0) {\n clear();\n return;\n }\n void run(code, (reason) => {\n setError(reason);\n clear();\n });\n }, delay);\n return () => clearTimeout(timer);\n }, [code, signature, delay, run, clear]);\n\n return { applied, error, pending, apply, remove };\n}\n","import { useCart } from '../../cart/cart-provider';\nimport { formatPrice } from '../../lib/format-price';\nimport { useStorefront } from '../../lib/provider';\nimport { DiscountField } from './discount-field';\nimport {\n defaultDiscountLabels,\n type CartSummaryDiscount,\n type DiscountLabels,\n} from './discount-types';\nimport { useDiscount } from './use-discount';\n\nexport type {\n CartSummaryDiscount,\n DiscountPreviewResult,\n} from './discount-types';\nexport { discountReasonMessage } from './discount-types';\n\nexport interface CartSummaryLabels {\n subtotal: string;\n discount: Partial<Omit<DiscountLabels, 'reasons'>> & {\n reasons?: Partial<DiscountLabels['reasons']>;\n };\n}\n\nexport interface CartSummaryProps {\n onCheckout?: () => void;\n checkoutLabel?: string;\n checkoutDisabled?: boolean;\n labels?: Partial<CartSummaryLabels>;\n discount?: CartSummaryDiscount;\n}\n\nexport function CartSummary({\n onCheckout,\n checkoutLabel = 'Checkout',\n checkoutDisabled = false,\n labels,\n discount,\n}: CartSummaryProps) {\n const cart = useCart();\n const { locale } = useStorefront();\n const subtotal = labels?.subtotal ?? 'Subtotal';\n const state = useDiscount(discount);\n const discountLabels: DiscountLabels = {\n ...defaultDiscountLabels,\n ...labels?.discount,\n reasons: {\n ...defaultDiscountLabels.reasons,\n ...labels?.discount?.reasons,\n },\n };\n return (\n <div className=\"sf-cart-summary\">\n <p className=\"sf-cart-summary__row\">\n <span>{subtotal}</span>\n <span className=\"sf-price\">\n {formatPrice(cart.subtotalCents, cart.currency ?? 'USD', locale)}\n </span>\n </p>\n {discount && cart.lines.length > 0 && (\n <DiscountField\n labels={discountLabels}\n currency={cart.currency ?? 'USD'}\n locale={locale}\n onApply={(code) => void state.apply(code)}\n onRemove={state.remove}\n applied={state.applied}\n error={state.error}\n pending={state.pending}\n />\n )}\n <button\n type=\"button\"\n className=\"sf-button\"\n disabled={checkoutDisabled || !onCheckout || cart.lines.length === 0}\n onClick={onCheckout}\n >\n {checkoutLabel}\n </button>\n </div>\n );\n}\n","import { useEffect, useRef, type KeyboardEvent, type ReactNode } from 'react';\nimport { useCart } from '../../cart/cart-provider';\nimport { CartLineItem } from '../cart-line-item/cart-line-item';\nimport { useDrawerPresence } from './use-drawer-presence';\nimport {\n CartSummary,\n type CartSummaryProps,\n} from '../cart-summary/cart-summary';\n\nexport interface CartDrawerLabels {\n title: string;\n close: string;\n empty: string;\n}\n\nexport const defaultCartDrawerLabels: CartDrawerLabels = {\n title: 'Your cart',\n close: 'Close',\n empty: 'Your cart is empty.',\n};\n\nexport interface CartDrawerProps {\n labels?: Partial<CartDrawerLabels>;\n summary?: CartSummaryProps;\n slots?: CartDrawerSlots;\n}\n\nexport interface CartDrawerSlots {\n empty?: ReactNode;\n summaryNote?: ReactNode;\n}\n\nconst FOCUSABLE =\n 'a[href], button:not([disabled]), input:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\nexport function CartDrawer({ labels, summary, slots }: CartDrawerProps) {\n const cart = useCart();\n const text = { ...defaultCartDrawerLabels, ...labels };\n const panelRef = useRef<HTMLDivElement>(null);\n const backdropRef = useRef<HTMLDivElement>(null);\n const { isOpen } = cart;\n const presence = useDrawerPresence(isOpen, backdropRef);\n\n useEffect(() => {\n if (!isOpen) return;\n const trigger = document.activeElement as HTMLElement | null;\n panelRef.current?.querySelector<HTMLElement>(FOCUSABLE)?.focus();\n return () => trigger?.focus();\n }, [isOpen]);\n\n if (!presence.mounted) return null;\n\n function handleKeyDown(event: KeyboardEvent<HTMLDivElement>) {\n if (event.key === 'Escape') {\n event.stopPropagation();\n cart.close();\n return;\n }\n if (event.key !== 'Tab') return;\n const items = Array.from(\n panelRef.current?.querySelectorAll<HTMLElement>(FOCUSABLE) ?? [],\n );\n if (items.length === 0) return;\n const first = items[0];\n const last = items[items.length - 1];\n if (event.shiftKey && document.activeElement === first) {\n event.preventDefault();\n last.focus();\n } else if (!event.shiftKey && document.activeElement === last) {\n event.preventDefault();\n first.focus();\n }\n }\n\n return (\n <div\n ref={backdropRef}\n className=\"sf-drawer-backdrop\"\n data-state={presence.state}\n onClick={cart.close}\n >\n <div\n ref={panelRef}\n className=\"sf-drawer\"\n data-state={presence.state}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"sf-drawer-title\"\n onKeyDown={handleKeyDown}\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"sf-drawer__header\">\n <h2 id=\"sf-drawer-title\" className=\"sf-drawer__title\">\n {text.title}\n </h2>\n <button\n type=\"button\"\n className=\"sf-drawer__close\"\n aria-label={text.close}\n onClick={cart.close}\n >\n <span aria-hidden=\"true\">×</span>\n </button>\n </div>\n {cart.lines.length === 0 ? (\n <div className=\"sf-drawer__empty\">\n {slots?.empty ?? <p>{text.empty}</p>}\n </div>\n ) : (\n <>\n <ul className=\"sf-drawer__lines\">\n {cart.lines.map((line) => (\n <CartLineItem key={line.productId} line={line} />\n ))}\n </ul>\n <CartSummary {...summary} />\n {slots?.summaryNote && (\n <div className=\"sf-drawer__note\">{slots.summaryNote}</div>\n )}\n </>\n )}\n </div>\n </div>\n );\n}\n","import type { ReactNode } from 'react';\nimport { useCart } from '../../cart/cart-provider';\n\nexport interface CartButtonProps {\n label?: string;\n className?: string;\n icon?: ReactNode;\n labelHidden?: boolean;\n hideZeroCount?: boolean;\n}\n\nexport function CartButton({\n label = 'Cart',\n className,\n icon,\n labelHidden = false,\n hideZeroCount = false,\n}: CartButtonProps) {\n const cart = useCart();\n const count = cart.hydrated ? cart.itemCount : 0;\n const showCount = !(hideZeroCount && count === 0);\n const ariaLabel = showCount\n ? labelHidden\n ? `${label}, ${cart.itemCount} items`\n : undefined\n : `${label}, empty`;\n return (\n <button\n type=\"button\"\n className={['sf-cart-button', className].filter(Boolean).join(' ')}\n aria-haspopup=\"dialog\"\n aria-label={ariaLabel}\n onClick={cart.open}\n >\n {icon}\n <span className={labelHidden ? 'sf-visually-hidden' : undefined}>\n {label}\n </span>\n {showCount && (\n <span\n className=\"sf-cart-button__count\"\n aria-label={`${cart.itemCount} items`}\n >\n {count}\n </span>\n )}\n </button>\n );\n}\n","import type { OrderStatus } from '@develemit/commerce-contracts';\n\nexport interface CheckoutSuccessLabels {\n heading: string;\n summaryHeading: string;\n reference: string;\n continue: string;\n subtotal: string;\n discount: string;\n shipping: string;\n total: string;\n shipTo: string;\n tracking: string;\n status: Record<OrderStatus, string>;\n}\n\nexport const defaultCheckoutSuccessLabels: CheckoutSuccessLabels = {\n heading: 'Order received',\n summaryHeading: 'Order summary',\n reference: 'Order {id}',\n continue: 'Continue shopping',\n subtotal: 'Subtotal',\n discount: 'Discount',\n shipping: 'Shipping',\n total: 'Total',\n shipTo: 'Ship to',\n tracking: 'Track',\n status: {\n pending: \"We're confirming your payment and will email you once it's done.\",\n paid: 'Payment received. Thank you!',\n shipped: 'Your order is on its way.',\n cancelled: 'This checkout was cancelled.',\n expired: 'This checkout expired before payment.',\n refunded: 'This order has been refunded.',\n needs_attention:\n \"We couldn't confirm one of your items. We'll be in touch about a refund.\",\n },\n};\n","import type { ReactNode } from 'react';\nimport {\n formatOrderRef,\n type OrderAddress,\n type OrderSummary,\n} from '@develemit/commerce-contracts';\nimport { formatPrice } from '../../lib/format-price';\nimport {\n defaultCheckoutSuccessLabels,\n type CheckoutSuccessLabels,\n} from './labels';\n\nexport interface CheckoutSuccessViewProps {\n order: OrderSummary & { shippingAddress?: OrderAddress | null };\n locale?: string;\n labels?: Partial<Omit<CheckoutSuccessLabels, 'status'>> & {\n status?: Partial<CheckoutSuccessLabels['status']>;\n };\n className?: string;\n slots?: {\n header?: ReactNode;\n beforeLines?: ReactNode;\n afterTotals?: ReactNode;\n actions?: ReactNode;\n };\n showReference?: boolean;\n showSummaryHeading?: boolean;\n continueHref?: string;\n renderLink?: (props: {\n href: string;\n children: ReactNode;\n className?: string;\n }) => ReactNode;\n}\n\nfunction addressLines(address: OrderAddress): string[] {\n const region = [address.city, address.state, address.postalCode]\n .filter(Boolean)\n .join(' ');\n return [\n address.name,\n address.line1,\n address.line2,\n region,\n address.country,\n ].filter((line): line is string => Boolean(line));\n}\n\nexport function CheckoutSuccessView({\n order,\n locale,\n labels,\n className,\n slots,\n showReference,\n showSummaryHeading,\n continueHref,\n renderLink,\n}: CheckoutSuccessViewProps) {\n const text = {\n ...defaultCheckoutSuccessLabels,\n ...labels,\n status: { ...defaultCheckoutSuccessLabels.status, ...labels?.status },\n };\n const money = (cents: number) => formatPrice(cents, order.currency, locale);\n const lines = order.shippingAddress\n ? addressLines(order.shippingAddress)\n : [];\n const { fulfillment } = order;\n const continueProps = {\n href: continueHref ?? '',\n className: 'sf-checkout-success__continue',\n children: text.continue,\n };\n const continueLink = continueHref ? (\n renderLink ? (\n renderLink(continueProps)\n ) : (\n <a {...continueProps} />\n )\n ) : null;\n const actions = slots?.actions ?? continueLink;\n return (\n <section\n className={['sf-checkout-success', className].filter(Boolean).join(' ')}\n data-status={order.status}\n >\n <h1 className=\"sf-checkout-success__heading\">{text.heading}</h1>\n {showReference && (\n <p className=\"sf-checkout-success__reference\">\n {text.reference.replace('{id}', formatOrderRef(order.id))}\n </p>\n )}\n {slots?.header}\n <p className=\"sf-checkout-success__status\">{text.status[order.status]}</p>\n {showSummaryHeading && (\n <h2 className=\"sf-checkout-success__summary-heading\">\n {text.summaryHeading}\n </h2>\n )}\n {slots?.beforeLines}\n <ul className=\"sf-checkout-success__lines\">\n {order.items.map((item) => (\n <li key={item.productId} className=\"sf-checkout-success__line\">\n <span>\n {item.name} × {item.quantity}\n </span>\n <span>{money(item.priceCents * item.quantity)}</span>\n </li>\n ))}\n </ul>\n <dl className=\"sf-checkout-success__totals\">\n <div className=\"sf-checkout-success__row\">\n <dt>{text.subtotal}</dt>\n <dd>{money(order.subtotalCents)}</dd>\n </div>\n {order.discountCents > 0 && (\n <div\n className=\"sf-checkout-success__row sf-checkout-success__row--discount\"\n data-discount\n >\n <dt>\n {text.discount}\n {order.discountCode && ` (${order.discountCode})`}\n </dt>\n <dd>−{money(order.discountCents)}</dd>\n </div>\n )}\n {order.shippingLabel && (\n <div className=\"sf-checkout-success__row\">\n <dt>\n {text.shipping} ({order.shippingLabel})\n </dt>\n <dd>{money(order.shippingCents)}</dd>\n </div>\n )}\n <div className=\"sf-checkout-success__row sf-checkout-success__row--total\">\n <dt>{text.total}</dt>\n <dd>{money(order.totalCents)}</dd>\n </div>\n </dl>\n {slots?.afterTotals}\n {lines.length > 0 && (\n <address className=\"sf-checkout-success__address\">\n <strong>{text.shipTo}</strong>\n {lines.map((line) => (\n <span key={line}>{line}</span>\n ))}\n </address>\n )}\n {fulfillment?.trackingNumber && (\n <p className=\"sf-checkout-success__tracking\">\n {fulfillment.carrier && `${fulfillment.carrier} · `}\n {fulfillment.trackingUrl ? (\n <a href={fulfillment.trackingUrl}>\n {text.tracking} {fulfillment.trackingNumber}\n </a>\n ) : (\n fulfillment.trackingNumber\n )}\n </p>\n )}\n {actions && <div className=\"sf-checkout-success__actions\">{actions}</div>}\n </section>\n );\n}\n","import { useEffect, useRef } from 'react';\nimport { useCart } from './cart-provider';\n\nexport function ClearCartOnMount() {\n const { hydrated, clear } = useCart();\n const cleared = useRef(false);\n useEffect(() => {\n if (!hydrated || cleared.current) return;\n cleared.current = true;\n clear();\n }, [hydrated, clear]);\n return null;\n}\n"],"mappings":"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"}
package/dist/styles.css CHANGED
@@ -68,13 +68,22 @@
68
68
  grid-template-columns: repeat(3, 1fr);
69
69
  }
70
70
  }
71
+ .sf-grid__item {
72
+ display: flex;
73
+ }
71
74
  .sf-grid-empty {
75
+ display: flex;
76
+ flex-direction: column;
77
+ align-items: center;
72
78
  padding: calc(var(--sf-space-unit) * 12) 0;
73
79
  text-align: center;
74
80
  color: var(--sf-color-muted);
75
81
  }
76
82
 
77
83
  .sf-card {
84
+ display: flex;
85
+ flex-direction: column;
86
+ flex: 1;
78
87
  background: var(--sf-color-surface);
79
88
  border-radius: var(--sf-radius);
80
89
  overflow: hidden;
@@ -92,7 +101,9 @@
92
101
  transform: scale(var(--sf-card-image-hover-scale));
93
102
  }
94
103
  .sf-card__link {
95
- display: block;
104
+ display: flex;
105
+ flex: 1;
106
+ flex-direction: column;
96
107
  color: inherit;
97
108
  text-decoration: none;
98
109
  }
@@ -109,6 +120,7 @@
109
120
  transition: transform var(--sf-motion-duration) ease;
110
121
  }
111
122
  .sf-card__body {
123
+ flex: 1;
112
124
  display: flex;
113
125
  flex-direction: column;
114
126
  align-items: flex-start;
@@ -218,6 +230,9 @@
218
230
  display: grid;
219
231
  gap: calc(var(--sf-space-unit) * 3);
220
232
  }
233
+ .sf-shop {
234
+ gap: calc(var(--sf-space-unit) * 6);
235
+ }
221
236
 
222
237
  .sf-featured__heading {
223
238
  font-family: var(--sf-font-display);
@@ -226,20 +241,54 @@
226
241
 
227
242
  .sf-detail {
228
243
  display: grid;
229
- gap: calc(var(--sf-space-unit) * 3);
244
+ gap: calc(var(--sf-space-unit) * 4);
245
+ }
246
+ .sf-detail__back {
247
+ grid-column: 1 / -1;
248
+ justify-self: start;
249
+ color: var(--sf-color-muted);
250
+ font-family: var(--sf-font-body);
251
+ text-decoration: none;
252
+ }
253
+ .sf-detail__back:hover {
254
+ text-decoration: underline;
230
255
  }
231
256
 
232
257
  .sf-detail__body {
233
258
  display: grid;
234
- gap: calc(var(--sf-space-unit) * 2);
259
+ gap: calc(var(--sf-space-unit) * 3);
235
260
  align-content: start;
236
261
  }
237
262
 
238
263
  .sf-detail__title {
239
264
  font-family: var(--sf-font-display);
265
+ font-size: 2rem;
266
+ font-weight: 700;
267
+ line-height: 1.15;
268
+ margin: 0;
269
+ }
270
+
271
+ .sf-detail__description {
240
272
  margin: 0;
241
273
  }
242
274
 
275
+ .sf-detail__actions {
276
+ margin-top: calc(var(--sf-space-unit) * 3);
277
+ }
278
+
279
+ .sf-detail--sold .sf-gallery__image {
280
+ opacity: var(--sf-sold-opacity);
281
+ filter: var(--sf-sold-filter);
282
+ }
283
+
284
+ .sf-sold-note {
285
+ display: inline-block;
286
+ font-family: var(--sf-font-body);
287
+ letter-spacing: 0.04em;
288
+ text-transform: uppercase;
289
+ color: var(--sf-color-sold);
290
+ }
291
+
243
292
  .sf-detail__specs {
244
293
  display: grid;
245
294
  grid-template-columns: max-content 1fr;
@@ -9,5 +9,7 @@ export interface ProductDetailViewProps {
9
9
  product: Product;
10
10
  slots?: ProductDetailViewSlots;
11
11
  labels?: Partial<AvailabilityLabels>;
12
+ backHref?: string;
13
+ backLabel?: string;
12
14
  }
13
- export declare function ProductDetailView({ product, slots, labels, }: ProductDetailViewProps): import('react').JSX.Element;
15
+ export declare function ProductDetailView({ product, slots, labels, backHref, backLabel, }: ProductDetailViewProps): import('react').JSX.Element;
package/docs/cart.md CHANGED
@@ -59,7 +59,8 @@ once, even under React strict mode. Styles are `.sf-checkout-success*`.
59
59
  Extension points, all optional: `slots={{ header, beforeLines, afterTotals,
60
60
  actions }}` (ReactNode) put your own content under the heading, above the
61
61
  lines, below the totals, and in the footer. `showReference` adds a
62
- `labels.reference` row (default `Order {id}`), and `showSummaryHeading` adds an
62
+ `labels.reference` row (default `Order {id}`; `{id}` becomes the short
63
+ reference such as `#A1B2C3D4`), and `showSummaryHeading` adds an
63
64
  `<h2>` from `labels.summaryHeading`. `continueHref` renders a link labelled
64
65
  `labels.continue` unless `slots.actions` is given; pass `renderLink` to route it
65
66
  through your framework's link (the view uses no hooks, so it can't read the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@develemit/storefront",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Brand-agnostic React storefront: components, page views, cart, checkout and a typed API client.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -53,6 +53,6 @@
53
53
  "access": "public"
54
54
  },
55
55
  "dependencies": {
56
- "@develemit/commerce-contracts": "0.7.0"
56
+ "@develemit/commerce-contracts": "0.8.0"
57
57
  }
58
58
  }