@enadhq/enad-react-sdk 1.1.0 → 1.3.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.
Files changed (232) hide show
  1. package/dist/client/cart/components/cart-drawer.mjs +3 -3
  2. package/dist/client/cart/components/cart-drawer.mjs.map +1 -1
  3. package/dist/client/cart/components/cart-trigger.mjs +1 -1
  4. package/dist/client/cart/components/cart-trigger.mjs.map +1 -1
  5. package/dist/client/storefront/blocks/card-video.mjs +1 -1
  6. package/dist/client/storefront/blocks/card-video.mjs.map +1 -1
  7. package/dist/client/storefront/blocks/gallery-with-link-blocks.d.ts.map +1 -1
  8. package/dist/client/storefront/blocks/gallery-with-link-blocks.mjs +13 -5
  9. package/dist/client/storefront/blocks/gallery-with-link-blocks.mjs.map +1 -1
  10. package/dist/client/storefront/blocks/gallery.d.ts +10 -1
  11. package/dist/client/storefront/blocks/gallery.d.ts.map +1 -1
  12. package/dist/client/storefront/blocks/gallery.mjs +51 -27
  13. package/dist/client/storefront/blocks/gallery.mjs.map +1 -1
  14. package/dist/client/storefront/blocks/hero.d.ts +12 -1
  15. package/dist/client/storefront/blocks/hero.d.ts.map +1 -1
  16. package/dist/client/storefront/blocks/hero.mjs +143 -145
  17. package/dist/client/storefront/blocks/hero.mjs.map +1 -1
  18. package/dist/client/storefront/blocks/link-block-small.d.ts.map +1 -1
  19. package/dist/client/storefront/blocks/link-block-small.mjs +1 -1
  20. package/dist/client/storefront/blocks/link-block-small.mjs.map +1 -1
  21. package/dist/client/storefront/blocks/link-block.d.ts.map +1 -1
  22. package/dist/client/storefront/blocks/link-block.mjs +4 -4
  23. package/dist/client/storefront/blocks/link-block.mjs.map +1 -1
  24. package/dist/client/storefront/blocks/product-card-parts.d.ts +1 -1
  25. package/dist/client/storefront/blocks/product-card-parts.d.ts.map +1 -1
  26. package/dist/client/storefront/blocks/product-card-parts.mjs +2 -2
  27. package/dist/client/storefront/blocks/product-card-parts.mjs.map +1 -1
  28. package/dist/client/storefront/blocks/product-card.d.ts +10 -1
  29. package/dist/client/storefront/blocks/product-card.d.ts.map +1 -1
  30. package/dist/client/storefront/blocks/product-card.mjs +122 -116
  31. package/dist/client/storefront/blocks/product-card.mjs.map +1 -1
  32. package/dist/client/storefront/blocks/product-image.mjs +2 -2
  33. package/dist/client/storefront/blocks/product-image.mjs.map +1 -1
  34. package/dist/client/storefront/blocks/text-content-with-image.d.ts +14 -1
  35. package/dist/client/storefront/blocks/text-content-with-image.d.ts.map +1 -1
  36. package/dist/client/storefront/blocks/text-content-with-image.mjs +141 -164
  37. package/dist/client/storefront/blocks/text-content-with-image.mjs.map +1 -1
  38. package/dist/client/storefront/carousel/swipeable-carousel.d.ts +5 -1
  39. package/dist/client/storefront/carousel/swipeable-carousel.d.ts.map +1 -1
  40. package/dist/client/storefront/carousel/swipeable-carousel.mjs +2 -1
  41. package/dist/client/storefront/carousel/swipeable-carousel.mjs.map +1 -1
  42. package/dist/client/storefront/checkout/cart-summary.mjs +1 -1
  43. package/dist/client/storefront/checkout/cart-summary.mjs.map +1 -1
  44. package/dist/client/storefront/components/language-selector.d.ts.map +1 -1
  45. package/dist/client/storefront/components/language-selector.mjs +1 -1
  46. package/dist/client/storefront/components/language-selector.mjs.map +1 -1
  47. package/dist/client/storefront/components/product-recommendations.d.ts.map +1 -1
  48. package/dist/client/storefront/components/product-recommendations.mjs +29 -37
  49. package/dist/client/storefront/components/product-recommendations.mjs.map +1 -1
  50. package/dist/client/storefront/filters/filter-chip.d.ts +5 -2
  51. package/dist/client/storefront/filters/filter-chip.d.ts.map +1 -1
  52. package/dist/client/storefront/filters/filter-chip.mjs +6 -4
  53. package/dist/client/storefront/filters/filter-chip.mjs.map +1 -1
  54. package/dist/client/storefront/filters/filter-panel.mjs +2 -2
  55. package/dist/client/storefront/filters/filter-panel.mjs.map +1 -1
  56. package/dist/client/storefront/filters/toggle-list-view.mjs +1 -1
  57. package/dist/client/storefront/filters/toggle-list-view.mjs.map +1 -1
  58. package/dist/client/storefront/index.d.ts +12 -1
  59. package/dist/client/storefront/index.mjs +12 -1
  60. package/dist/client/storefront/layout/header.d.ts.map +1 -1
  61. package/dist/client/storefront/layout/header.mjs +1 -1
  62. package/dist/client/storefront/layout/header.mjs.map +1 -1
  63. package/dist/client/storefront/layout/mobile-menu-drawer.mjs +1 -1
  64. package/dist/client/storefront/layout/promotion-bar.d.ts.map +1 -1
  65. package/dist/client/storefront/layout/promotion-bar.mjs +3 -3
  66. package/dist/client/storefront/layout/promotion-bar.mjs.map +1 -1
  67. package/dist/client/storefront/primitives/block-heading.d.ts +40 -0
  68. package/dist/client/storefront/primitives/block-heading.d.ts.map +1 -0
  69. package/dist/client/storefront/primitives/block-heading.mjs +43 -0
  70. package/dist/client/storefront/primitives/block-heading.mjs.map +1 -0
  71. package/dist/client/storefront/primitives/button.d.ts +2 -2
  72. package/dist/client/storefront/primitives/button.d.ts.map +1 -1
  73. package/dist/client/storefront/primitives/button.mjs +4 -4
  74. package/dist/client/storefront/primitives/button.mjs.map +1 -1
  75. package/dist/client/storefront/primitives/cta-group.d.ts +25 -0
  76. package/dist/client/storefront/primitives/cta-group.d.ts.map +1 -0
  77. package/dist/client/storefront/primitives/cta-group.mjs +27 -0
  78. package/dist/client/storefront/primitives/cta-group.mjs.map +1 -0
  79. package/dist/client/storefront/primitives/image-with-hover.d.ts +18 -0
  80. package/dist/client/storefront/primitives/image-with-hover.d.ts.map +1 -0
  81. package/dist/client/storefront/primitives/image-with-hover.mjs +16 -0
  82. package/dist/client/storefront/primitives/image-with-hover.mjs.map +1 -0
  83. package/dist/client/storefront/primitives/index.d.ts +4 -1
  84. package/dist/client/storefront/primitives/index.mjs +4 -1
  85. package/dist/client/storefront/primitives/input.d.ts +1 -1
  86. package/dist/client/storefront/primitives/input.mjs.map +1 -1
  87. package/dist/client/storefront/primitives/pagination.mjs +2 -2
  88. package/dist/client/storefront/primitives/pagination.mjs.map +1 -1
  89. package/dist/client/storefront/product/quantity-picker.mjs +2 -2
  90. package/dist/client/storefront/product/quantity-picker.mjs.map +1 -1
  91. package/dist/client/storefront/types.d.ts +1 -1
  92. package/dist/client/storefront/types.d.ts.map +1 -1
  93. package/dist/client/storefront/types.mjs.map +1 -1
  94. package/dist/client/theme/apply.d.ts +1 -1
  95. package/dist/client/theme/apply.d.ts.map +1 -1
  96. package/dist/client/theme/apply.mjs +0 -12
  97. package/dist/client/theme/apply.mjs.map +1 -1
  98. package/dist/client/theme/cli.mjs +0 -16
  99. package/dist/client/theme/cli.mjs.map +1 -1
  100. package/dist/client/theme/codec.d.ts.map +1 -1
  101. package/dist/client/theme/codec.mjs +0 -2
  102. package/dist/client/theme/codec.mjs.map +1 -1
  103. package/dist/client/theme/defaults.d.ts +0 -2
  104. package/dist/client/theme/defaults.mjs +0 -2
  105. package/dist/client/theme/defaults.mjs.map +1 -1
  106. package/dist/client/ui/accordion.d.ts +12 -1
  107. package/dist/client/ui/accordion.d.ts.map +1 -1
  108. package/dist/client/ui/accordion.mjs +23 -5
  109. package/dist/client/ui/accordion.mjs.map +1 -1
  110. package/dist/client/ui/alert.d.ts +16 -7
  111. package/dist/client/ui/alert.d.ts.map +1 -1
  112. package/dist/client/ui/alert.mjs +21 -8
  113. package/dist/client/ui/alert.mjs.map +1 -1
  114. package/dist/client/ui/avatar.d.ts +10 -1
  115. package/dist/client/ui/avatar.d.ts.map +1 -1
  116. package/dist/client/ui/avatar.mjs +18 -4
  117. package/dist/client/ui/avatar.mjs.map +1 -1
  118. package/dist/client/ui/breadcrumb.d.ts +13 -1
  119. package/dist/client/ui/breadcrumb.d.ts.map +1 -1
  120. package/dist/client/ui/breadcrumb.mjs +27 -7
  121. package/dist/client/ui/breadcrumb.mjs.map +1 -1
  122. package/dist/client/ui/button.d.ts +28 -10
  123. package/dist/client/ui/button.d.ts.map +1 -1
  124. package/dist/client/ui/button.mjs +45 -20
  125. package/dist/client/ui/button.mjs.map +1 -1
  126. package/dist/client/ui/card.d.ts +20 -1
  127. package/dist/client/ui/card.d.ts.map +1 -1
  128. package/dist/client/ui/card.mjs +36 -8
  129. package/dist/client/ui/card.mjs.map +1 -1
  130. package/dist/client/ui/carousel.d.ts +9 -1
  131. package/dist/client/ui/carousel.d.ts.map +1 -1
  132. package/dist/client/ui/carousel.mjs +20 -4
  133. package/dist/client/ui/carousel.mjs.map +1 -1
  134. package/dist/client/ui/checkbox.d.ts +9 -1
  135. package/dist/client/ui/checkbox.d.ts.map +1 -1
  136. package/dist/client/ui/checkbox.mjs +12 -3
  137. package/dist/client/ui/checkbox.mjs.map +1 -1
  138. package/dist/client/ui/dialog.d.ts +13 -1
  139. package/dist/client/ui/dialog.d.ts.map +1 -1
  140. package/dist/client/ui/dialog.mjs +27 -7
  141. package/dist/client/ui/dialog.mjs.map +1 -1
  142. package/dist/client/ui/hover-card.d.ts +6 -1
  143. package/dist/client/ui/hover-card.d.ts.map +1 -1
  144. package/dist/client/ui/hover-card.mjs +4 -2
  145. package/dist/client/ui/hover-card.mjs.map +1 -1
  146. package/dist/client/ui/input.d.ts +20 -7
  147. package/dist/client/ui/input.d.ts.map +1 -1
  148. package/dist/client/ui/input.mjs +33 -9
  149. package/dist/client/ui/input.mjs.map +1 -1
  150. package/dist/client/ui/label.d.ts +6 -1
  151. package/dist/client/ui/label.d.ts.map +1 -1
  152. package/dist/client/ui/label.mjs +4 -2
  153. package/dist/client/ui/label.mjs.map +1 -1
  154. package/dist/client/ui/navigation-menu.d.ts +20 -3
  155. package/dist/client/ui/navigation-menu.d.ts.map +1 -1
  156. package/dist/client/ui/navigation-menu.mjs +34 -12
  157. package/dist/client/ui/navigation-menu.mjs.map +1 -1
  158. package/dist/client/ui/pagination.d.ts.map +1 -1
  159. package/dist/client/ui/pagination.mjs +3 -3
  160. package/dist/client/ui/pagination.mjs.map +1 -1
  161. package/dist/client/ui/popover.d.ts +11 -1
  162. package/dist/client/ui/popover.d.ts.map +1 -1
  163. package/dist/client/ui/popover.mjs +21 -5
  164. package/dist/client/ui/popover.mjs.map +1 -1
  165. package/dist/client/ui/progress.d.ts +9 -1
  166. package/dist/client/ui/progress.d.ts.map +1 -1
  167. package/dist/client/ui/progress.mjs +12 -3
  168. package/dist/client/ui/progress.mjs.map +1 -1
  169. package/dist/client/ui/select.d.ts +14 -2
  170. package/dist/client/ui/select.d.ts.map +1 -1
  171. package/dist/client/ui/select.mjs +35 -9
  172. package/dist/client/ui/select.mjs.map +1 -1
  173. package/dist/client/ui/separator.d.ts +6 -1
  174. package/dist/client/ui/separator.d.ts.map +1 -1
  175. package/dist/client/ui/separator.mjs +4 -2
  176. package/dist/client/ui/separator.mjs.map +1 -1
  177. package/dist/client/ui/sheet.d.ts +13 -1
  178. package/dist/client/ui/sheet.d.ts.map +1 -1
  179. package/dist/client/ui/sheet.mjs +35 -7
  180. package/dist/client/ui/sheet.mjs.map +1 -1
  181. package/dist/client/ui/slot-wrapper.d.ts +28 -0
  182. package/dist/client/ui/slot-wrapper.d.ts.map +1 -0
  183. package/dist/client/ui/slot-wrapper.mjs +38 -0
  184. package/dist/client/ui/slot-wrapper.mjs.map +1 -0
  185. package/dist/client/ui/tabs.d.ts +11 -1
  186. package/dist/client/ui/tabs.d.ts.map +1 -1
  187. package/dist/client/ui/tabs.mjs +21 -5
  188. package/dist/client/ui/tabs.mjs.map +1 -1
  189. package/dist/client/ui/toggle-group.d.ts +7 -4
  190. package/dist/client/ui/toggle-group.d.ts.map +1 -1
  191. package/dist/client/ui/toggle-group.mjs +4 -4
  192. package/dist/client/ui/toggle-group.mjs.map +1 -1
  193. package/dist/client/ui/toggle.d.ts +17 -8
  194. package/dist/client/ui/toggle.d.ts.map +1 -1
  195. package/dist/client/ui/toggle.mjs +11 -9
  196. package/dist/client/ui/toggle.mjs.map +1 -1
  197. package/dist/client/ui/tooltip.d.ts +6 -1
  198. package/dist/client/ui/tooltip.d.ts.map +1 -1
  199. package/dist/client/ui/tooltip.mjs +4 -2
  200. package/dist/client/ui/tooltip.mjs.map +1 -1
  201. package/dist/client/ui-resolver/button.d.ts +3 -4
  202. package/dist/client/ui-resolver/button.d.ts.map +1 -1
  203. package/dist/client/ui-resolver/button.mjs +2 -2
  204. package/dist/client/ui-resolver/button.mjs.map +1 -1
  205. package/dist/client/ui-resolver/card.d.ts +14 -1
  206. package/dist/client/ui-resolver/card.d.ts.map +1 -1
  207. package/dist/client/ui-resolver/card.mjs +3 -2
  208. package/dist/client/ui-resolver/card.mjs.map +1 -1
  209. package/dist/client/ui-resolver/context.mjs +1 -1
  210. package/dist/client/ui-resolver/context.mjs.map +1 -1
  211. package/dist/client/ui-resolver/index.d.ts +7 -4
  212. package/dist/client/ui-resolver/index.mjs +8 -6
  213. package/dist/client/ui-resolver/input.d.ts +3 -4
  214. package/dist/client/ui-resolver/input.d.ts.map +1 -1
  215. package/dist/client/ui-resolver/input.mjs +2 -2
  216. package/dist/client/ui-resolver/input.mjs.map +1 -1
  217. package/dist/client/ui-resolver/navigation-menu.d.ts +1 -2
  218. package/dist/client/ui-resolver/navigation-menu.d.ts.map +1 -1
  219. package/dist/client/ui-resolver/recipe.d.ts +95 -0
  220. package/dist/client/ui-resolver/recipe.d.ts.map +1 -0
  221. package/dist/client/ui-resolver/recipe.mjs +134 -0
  222. package/dist/client/ui-resolver/recipe.mjs.map +1 -0
  223. package/dist/client/ui-resolver/toggle.d.ts +2 -2
  224. package/dist/client/ui-resolver/toggle.mjs +2 -2
  225. package/dist/client/ui-resolver/toggle.mjs.map +1 -1
  226. package/dist/client/ui-resolver/types.d.ts +14 -0
  227. package/dist/client/ui-resolver/types.d.ts.map +1 -0
  228. package/dist/client/ui-resolver/types.mjs +1 -0
  229. package/dist/client/wishlist/wishlist-drawer.mjs +4 -4
  230. package/dist/client/wishlist/wishlist-drawer.mjs.map +1 -1
  231. package/dist/styles.css +1 -1
  232. package/package.json +4 -3
@@ -3,43 +3,31 @@ import { useIcon } from "../../icons/icon-context.mjs";
3
3
  import { cn } from "../../ui/utils.mjs";
4
4
  import { getMotionConfig } from "../../motion/config.mjs";
5
5
  import { staggerChildStyle, staggerEntryClass } from "../../motion/stagger.mjs";
6
+ import { Carousel, CarouselContent, CarouselItem } from "../../ui-resolver/carousel.mjs";
6
7
  import * as React$1 from "react";
7
8
  import { jsx, jsxs } from "react/jsx-runtime";
8
9
  //#region src/client/storefront/components/product-recommendations.tsx
9
10
  function ProductRecommendations({ title, children, viewAllHref, viewAllLabel = "View all", showArrows = true, delight = 0, className }) {
10
- const scrollRef = React$1.useRef(null);
11
+ const [api, setApi] = React$1.useState();
12
+ const [canScrollPrev, setCanScrollPrev] = React$1.useState(false);
13
+ const [canScrollNext, setCanScrollNext] = React$1.useState(false);
11
14
  const motion = React$1.useMemo(() => getMotionConfig(delight), [delight]);
12
- const [canScrollLeft, setCanScrollLeft] = React$1.useState(false);
13
- const [canScrollRight, setCanScrollRight] = React$1.useState(false);
14
15
  const ChevronLeftIcon = useIcon("chevronLeft");
15
16
  const ChevronRightIcon = useIcon("chevronRight");
16
- const updateScrollState = React$1.useCallback(() => {
17
- const el = scrollRef.current;
18
- if (!el) return;
19
- setCanScrollLeft(el.scrollLeft > 1);
20
- setCanScrollRight(el.scrollLeft + el.clientWidth < el.scrollWidth - 1);
21
- }, []);
22
17
  React$1.useEffect(() => {
23
- const el = scrollRef.current;
24
- if (!el) return;
25
- updateScrollState();
26
- el.addEventListener("scroll", updateScrollState, { passive: true });
27
- const ro = new ResizeObserver(updateScrollState);
28
- ro.observe(el);
18
+ if (!api) return;
19
+ const onSelect = () => {
20
+ setCanScrollPrev(api.canScrollPrev());
21
+ setCanScrollNext(api.canScrollNext());
22
+ };
23
+ onSelect();
24
+ api.on("reInit", onSelect);
25
+ api.on("select", onSelect);
29
26
  return () => {
30
- el.removeEventListener("scroll", updateScrollState);
31
- ro.disconnect();
27
+ api.off("reInit", onSelect);
28
+ api.off("select", onSelect);
32
29
  };
33
- }, [updateScrollState]);
34
- const scroll = (direction) => {
35
- const el = scrollRef.current;
36
- if (!el) return;
37
- const distance = el.clientWidth * .8;
38
- el.scrollBy({
39
- left: direction === "left" ? -distance : distance,
40
- behavior: "smooth"
41
- });
42
- };
30
+ }, [api]);
43
31
  return /* @__PURE__ */ jsxs("section", {
44
32
  className: cn("flex flex-col gap-4", className),
45
33
  children: [(title || viewAllHref) && /* @__PURE__ */ jsxs("div", {
@@ -55,25 +43,29 @@ function ProductRecommendations({ title, children, viewAllHref, viewAllLabel = "
55
43
  }), /* @__PURE__ */ jsxs("div", {
56
44
  className: "relative group/recs",
57
45
  children: [
58
- showArrows && canScrollLeft && /* @__PURE__ */ jsx("button", {
46
+ showArrows && canScrollPrev && /* @__PURE__ */ jsx("button", {
59
47
  type: "button",
60
- onClick: () => scroll("left"),
48
+ onClick: () => api?.scrollPrev(),
61
49
  className: "absolute left-0 top-1/2 -translate-y-1/2 z-10 hidden md:flex size-10 items-center justify-center rounded-full bg-(--enad-surface)/90 shadow-md border border-(--enad-border-color)/50 opacity-0 group-hover/recs:opacity-100 focus-visible:opacity-100 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 transition-opacity hover:bg-(--enad-surface)",
62
50
  "aria-label": "Scroll left",
63
51
  children: /* @__PURE__ */ jsx(ChevronLeftIcon, { className: "size-5" })
64
52
  }),
65
- /* @__PURE__ */ jsx("div", {
66
- ref: scrollRef,
67
- className: "flex gap-4 overflow-x-auto scroll-smooth snap-x snap-mandatory scrollbar-none pb-1",
68
- children: React$1.Children.map(children, (child, i) => /* @__PURE__ */ jsx("div", {
69
- className: cn("w-[260px] shrink-0 snap-start sm:w-[280px] lg:w-[300px]", staggerEntryClass(motion)),
53
+ /* @__PURE__ */ jsx(Carousel, {
54
+ setApi,
55
+ opts: {
56
+ align: "start",
57
+ slidesToScroll: "auto",
58
+ containScroll: "trimSnaps"
59
+ },
60
+ children: /* @__PURE__ */ jsx(CarouselContent, { children: React$1.Children.map(children, (child, i) => /* @__PURE__ */ jsx(CarouselItem, {
61
+ className: cn("basis-auto w-[260px] sm:w-[280px] lg:w-[300px]", staggerEntryClass(motion)),
70
62
  style: staggerChildStyle(i, motion),
71
63
  children: child
72
- }, i))
64
+ }, i)) })
73
65
  }),
74
- showArrows && canScrollRight && /* @__PURE__ */ jsx("button", {
66
+ showArrows && canScrollNext && /* @__PURE__ */ jsx("button", {
75
67
  type: "button",
76
- onClick: () => scroll("right"),
68
+ onClick: () => api?.scrollNext(),
77
69
  className: "absolute right-0 top-1/2 -translate-y-1/2 z-10 hidden md:flex size-10 items-center justify-center rounded-full bg-(--enad-surface)/90 shadow-md border border-(--enad-border-color)/50 opacity-0 group-hover/recs:opacity-100 focus-visible:opacity-100 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 transition-opacity hover:bg-(--enad-surface)",
78
70
  "aria-label": "Scroll right",
79
71
  children: /* @__PURE__ */ jsx(ChevronRightIcon, { className: "size-5" })
@@ -1 +1 @@
1
- {"version":3,"file":"product-recommendations.mjs","names":["React"],"sources":["../../../../src/client/storefront/components/product-recommendations.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../../ui/utils\";\nimport { useIcon } from \"../../icons/icon-context\";\nimport { getMotionConfig } from \"../../motion/config\";\nimport { staggerEntryClass, staggerChildStyle } from \"../../motion/stagger\";\n\n/* ─── Types ─── */\n\nexport interface ProductRecommendationsProps {\n title?: string;\n /** Optional variant label for styling/semantics */\n variant?: \"related\" | \"recent\" | \"complete-the-look\";\n /** Product cards to render — pass <ProductCard> elements */\n children: React.ReactNode;\n /** \"View all\" link */\n viewAllHref?: string;\n viewAllLabel?: string;\n /** Show navigation arrows on desktop */\n showArrows?: boolean;\n /** Delight level (0-100) for stagger entry animation. 0 = no animation. */\n delight?: number;\n className?: string;\n}\n\n/* ─── Component ─── */\n\nexport function ProductRecommendations({\n title,\n children,\n viewAllHref,\n viewAllLabel = \"View all\",\n showArrows = true,\n delight = 0,\n className,\n}: ProductRecommendationsProps) {\n const scrollRef = React.useRef<HTMLDivElement>(null);\n const motion = React.useMemo(() => getMotionConfig(delight), [delight]);\n const [canScrollLeft, setCanScrollLeft] = React.useState(false);\n const [canScrollRight, setCanScrollRight] = React.useState(false);\n const ChevronLeftIcon = useIcon(\"chevronLeft\");\n const ChevronRightIcon = useIcon(\"chevronRight\");\n\n const updateScrollState = React.useCallback(() => {\n const el = scrollRef.current;\n if (!el) return;\n setCanScrollLeft(el.scrollLeft > 1);\n setCanScrollRight(el.scrollLeft + el.clientWidth < el.scrollWidth - 1);\n }, []);\n\n React.useEffect(() => {\n const el = scrollRef.current;\n if (!el) return;\n updateScrollState();\n el.addEventListener(\"scroll\", updateScrollState, { passive: true });\n const ro = new ResizeObserver(updateScrollState);\n ro.observe(el);\n return () => {\n el.removeEventListener(\"scroll\", updateScrollState);\n ro.disconnect();\n };\n }, [updateScrollState]);\n\n const scroll = (direction: \"left\" | \"right\") => {\n const el = scrollRef.current;\n if (!el) return;\n const distance = el.clientWidth * 0.8;\n el.scrollBy({\n left: direction === \"left\" ? -distance : distance,\n behavior: \"smooth\",\n });\n };\n\n return (\n <section className={cn(\"flex flex-col gap-4\", className)}>\n {/* Header */}\n {(title || viewAllHref) && (\n <div className=\"flex items-center justify-between\">\n {title && <h2 className=\"font-enad text-xl font-semibold\">{title}</h2>}\n {viewAllHref && (\n <a\n href={viewAllHref}\n className=\"text-sm font-medium underline underline-offset-4 hover:text-(--enad-button-bg) transition-colors\"\n >\n {viewAllLabel}\n </a>\n )}\n </div>\n )}\n\n {/* Scrollable row */}\n <div className=\"relative group/recs\">\n {showArrows && canScrollLeft && (\n <button\n type=\"button\"\n onClick={() => scroll(\"left\")}\n className=\"absolute left-0 top-1/2 -translate-y-1/2 z-10 hidden md:flex size-10 items-center justify-center rounded-full bg-(--enad-surface)/90 shadow-md border border-(--enad-border-color)/50 opacity-0 group-hover/recs:opacity-100 focus-visible:opacity-100 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 transition-opacity hover:bg-(--enad-surface)\"\n aria-label=\"Scroll left\"\n >\n <ChevronLeftIcon className=\"size-5\" />\n </button>\n )}\n\n <div\n ref={scrollRef}\n className=\"flex gap-4 overflow-x-auto scroll-smooth snap-x snap-mandatory scrollbar-none pb-1\"\n >\n {React.Children.map(children, (child, i) => (\n <div\n key={i}\n className={cn(\n \"w-[260px] shrink-0 snap-start sm:w-[280px] lg:w-[300px]\",\n staggerEntryClass(motion),\n )}\n style={staggerChildStyle(i, motion)}\n >\n {child}\n </div>\n ))}\n </div>\n\n {showArrows && canScrollRight && (\n <button\n type=\"button\"\n onClick={() => scroll(\"right\")}\n className=\"absolute right-0 top-1/2 -translate-y-1/2 z-10 hidden md:flex size-10 items-center justify-center rounded-full bg-(--enad-surface)/90 shadow-md border border-(--enad-border-color)/50 opacity-0 group-hover/recs:opacity-100 focus-visible:opacity-100 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 transition-opacity hover:bg-(--enad-surface)\"\n aria-label=\"Scroll right\"\n >\n <ChevronRightIcon className=\"size-5\" />\n </button>\n )}\n </div>\n </section>\n );\n}\n"],"mappings":";;;;;;;;AA4BA,SAAgB,uBAAuB,EACrC,OACA,UACA,aACA,eAAe,YACf,aAAa,MACb,UAAU,GACV,aAC8B;CAC9B,MAAM,YAAYA,QAAM,OAAuB,KAAK;CACpD,MAAM,SAASA,QAAM,cAAc,gBAAgB,QAAQ,EAAE,CAAC,QAAQ,CAAC;CACvE,MAAM,CAAC,eAAe,oBAAoBA,QAAM,SAAS,MAAM;CAC/D,MAAM,CAAC,gBAAgB,qBAAqBA,QAAM,SAAS,MAAM;CACjE,MAAM,kBAAkB,QAAQ,cAAc;CAC9C,MAAM,mBAAmB,QAAQ,eAAe;CAEhD,MAAM,oBAAoBA,QAAM,kBAAkB;EAChD,MAAM,KAAK,UAAU;AACrB,MAAI,CAAC,GAAI;AACT,mBAAiB,GAAG,aAAa,EAAE;AACnC,oBAAkB,GAAG,aAAa,GAAG,cAAc,GAAG,cAAc,EAAE;IACrE,EAAE,CAAC;AAEN,SAAM,gBAAgB;EACpB,MAAM,KAAK,UAAU;AACrB,MAAI,CAAC,GAAI;AACT,qBAAmB;AACnB,KAAG,iBAAiB,UAAU,mBAAmB,EAAE,SAAS,MAAM,CAAC;EACnE,MAAM,KAAK,IAAI,eAAe,kBAAkB;AAChD,KAAG,QAAQ,GAAG;AACd,eAAa;AACX,MAAG,oBAAoB,UAAU,kBAAkB;AACnD,MAAG,YAAY;;IAEhB,CAAC,kBAAkB,CAAC;CAEvB,MAAM,UAAU,cAAgC;EAC9C,MAAM,KAAK,UAAU;AACrB,MAAI,CAAC,GAAI;EACT,MAAM,WAAW,GAAG,cAAc;AAClC,KAAG,SAAS;GACV,MAAM,cAAc,SAAS,CAAC,WAAW;GACzC,UAAU;GACX,CAAC;;AAGJ,QACE,qBAAC,WAAD;EAAS,WAAW,GAAG,uBAAuB,UAAU;YAAxD,EAEI,SAAS,gBACT,qBAAC,OAAD;GAAK,WAAU;aAAf,CACG,SAAS,oBAAC,MAAD;IAAI,WAAU;cAAmC;IAAW,CAAA,EACrE,eACC,oBAAC,KAAD;IACE,MAAM;IACN,WAAU;cAET;IACC,CAAA,CAEF;MAIR,qBAAC,OAAD;GAAK,WAAU;aAAf;IACG,cAAc,iBACb,oBAAC,UAAD;KACE,MAAK;KACL,eAAe,OAAO,OAAO;KAC7B,WAAU;KACV,cAAW;eAEX,oBAAC,iBAAD,EAAiB,WAAU,UAAW,CAAA;KAC/B,CAAA;IAGX,oBAAC,OAAD;KACE,KAAK;KACL,WAAU;eAETA,QAAM,SAAS,IAAI,WAAW,OAAO,MACpC,oBAAC,OAAD;MAEE,WAAW,GACT,2DACA,kBAAkB,OAAO,CAC1B;MACD,OAAO,kBAAkB,GAAG,OAAO;gBAElC;MACG,EARC,EAQD,CACN;KACE,CAAA;IAEL,cAAc,kBACb,oBAAC,UAAD;KACE,MAAK;KACL,eAAe,OAAO,QAAQ;KAC9B,WAAU;KACV,cAAW;eAEX,oBAAC,kBAAD,EAAkB,WAAU,UAAW,CAAA;KAChC,CAAA;IAEP;KACE"}
1
+ {"version":3,"file":"product-recommendations.mjs","names":["React"],"sources":["../../../../src/client/storefront/components/product-recommendations.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../../ui/utils\";\nimport { useIcon } from \"../../icons/icon-context\";\nimport { getMotionConfig } from \"../../motion/config\";\nimport { staggerEntryClass, staggerChildStyle } from \"../../motion/stagger\";\nimport {\n Carousel,\n CarouselContent,\n CarouselItem,\n type CarouselApi,\n} from \"../../ui-resolver/carousel\";\n\n/* ─── Types ─── */\n\nexport interface ProductRecommendationsProps {\n title?: string;\n /** Optional variant label for styling/semantics */\n variant?: \"related\" | \"recent\" | \"complete-the-look\";\n /** Product cards to render — pass <ProductCard> elements */\n children: React.ReactNode;\n /** \"View all\" link */\n viewAllHref?: string;\n viewAllLabel?: string;\n /** Show navigation arrows on desktop */\n showArrows?: boolean;\n /** Delight level (0-100) for stagger entry animation. 0 = no animation. */\n delight?: number;\n className?: string;\n}\n\n/* ─── Component ─── */\n\nexport function ProductRecommendations({\n title,\n children,\n viewAllHref,\n viewAllLabel = \"View all\",\n showArrows = true,\n delight = 0,\n className,\n}: ProductRecommendationsProps) {\n const [api, setApi] = React.useState<CarouselApi>();\n const [canScrollPrev, setCanScrollPrev] = React.useState(false);\n const [canScrollNext, setCanScrollNext] = React.useState(false);\n const motion = React.useMemo(() => getMotionConfig(delight), [delight]);\n const ChevronLeftIcon = useIcon(\"chevronLeft\");\n const ChevronRightIcon = useIcon(\"chevronRight\");\n\n React.useEffect(() => {\n if (!api) return;\n\n const onSelect = () => {\n setCanScrollPrev(api.canScrollPrev());\n setCanScrollNext(api.canScrollNext());\n };\n\n onSelect();\n api.on(\"reInit\", onSelect);\n api.on(\"select\", onSelect);\n return () => {\n api.off(\"reInit\", onSelect);\n api.off(\"select\", onSelect);\n };\n }, [api]);\n\n return (\n <section className={cn(\"flex flex-col gap-4\", className)}>\n {/* Header */}\n {(title || viewAllHref) && (\n <div className=\"flex items-center justify-between\">\n {title && <h2 className=\"font-enad text-xl font-semibold\">{title}</h2>}\n {viewAllHref && (\n <a\n href={viewAllHref}\n className=\"text-sm font-medium underline underline-offset-4 hover:text-(--enad-button-bg) transition-colors\"\n >\n {viewAllLabel}\n </a>\n )}\n </div>\n )}\n\n {/* Scrollable row */}\n <div className=\"relative group/recs\">\n {showArrows && canScrollPrev && (\n <button\n type=\"button\"\n onClick={() => api?.scrollPrev()}\n className=\"absolute left-0 top-1/2 -translate-y-1/2 z-10 hidden md:flex size-10 items-center justify-center rounded-full bg-(--enad-surface)/90 shadow-md border border-(--enad-border-color)/50 opacity-0 group-hover/recs:opacity-100 focus-visible:opacity-100 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 transition-opacity hover:bg-(--enad-surface)\"\n aria-label=\"Scroll left\"\n >\n <ChevronLeftIcon className=\"size-5\" />\n </button>\n )}\n\n <Carousel\n setApi={setApi}\n opts={{ align: \"start\", slidesToScroll: \"auto\", containScroll: \"trimSnaps\" }}\n >\n <CarouselContent>\n {React.Children.map(children, (child, i) => (\n <CarouselItem\n key={i}\n className={cn(\n \"basis-auto w-[260px] sm:w-[280px] lg:w-[300px]\",\n staggerEntryClass(motion),\n )}\n style={staggerChildStyle(i, motion)}\n >\n {child}\n </CarouselItem>\n ))}\n </CarouselContent>\n </Carousel>\n\n {showArrows && canScrollNext && (\n <button\n type=\"button\"\n onClick={() => api?.scrollNext()}\n className=\"absolute right-0 top-1/2 -translate-y-1/2 z-10 hidden md:flex size-10 items-center justify-center rounded-full bg-(--enad-surface)/90 shadow-md border border-(--enad-border-color)/50 opacity-0 group-hover/recs:opacity-100 focus-visible:opacity-100 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 transition-opacity hover:bg-(--enad-surface)\"\n aria-label=\"Scroll right\"\n >\n <ChevronRightIcon className=\"size-5\" />\n </button>\n )}\n </div>\n </section>\n );\n}\n"],"mappings":";;;;;;;;;AAkCA,SAAgB,uBAAuB,EACrC,OACA,UACA,aACA,eAAe,YACf,aAAa,MACb,UAAU,GACV,aAC8B;CAC9B,MAAM,CAAC,KAAK,UAAUA,QAAM,UAAuB;CACnD,MAAM,CAAC,eAAe,oBAAoBA,QAAM,SAAS,MAAM;CAC/D,MAAM,CAAC,eAAe,oBAAoBA,QAAM,SAAS,MAAM;CAC/D,MAAM,SAASA,QAAM,cAAc,gBAAgB,QAAQ,EAAE,CAAC,QAAQ,CAAC;CACvE,MAAM,kBAAkB,QAAQ,cAAc;CAC9C,MAAM,mBAAmB,QAAQ,eAAe;AAEhD,SAAM,gBAAgB;AACpB,MAAI,CAAC,IAAK;EAEV,MAAM,iBAAiB;AACrB,oBAAiB,IAAI,eAAe,CAAC;AACrC,oBAAiB,IAAI,eAAe,CAAC;;AAGvC,YAAU;AACV,MAAI,GAAG,UAAU,SAAS;AAC1B,MAAI,GAAG,UAAU,SAAS;AAC1B,eAAa;AACX,OAAI,IAAI,UAAU,SAAS;AAC3B,OAAI,IAAI,UAAU,SAAS;;IAE5B,CAAC,IAAI,CAAC;AAET,QACE,qBAAC,WAAD;EAAS,WAAW,GAAG,uBAAuB,UAAU;YAAxD,EAEI,SAAS,gBACT,qBAAC,OAAD;GAAK,WAAU;aAAf,CACG,SAAS,oBAAC,MAAD;IAAI,WAAU;cAAmC;IAAW,CAAA,EACrE,eACC,oBAAC,KAAD;IACE,MAAM;IACN,WAAU;cAET;IACC,CAAA,CAEF;MAIR,qBAAC,OAAD;GAAK,WAAU;aAAf;IACG,cAAc,iBACb,oBAAC,UAAD;KACE,MAAK;KACL,eAAe,KAAK,YAAY;KAChC,WAAU;KACV,cAAW;eAEX,oBAAC,iBAAD,EAAiB,WAAU,UAAW,CAAA;KAC/B,CAAA;IAGX,oBAAC,UAAD;KACU;KACR,MAAM;MAAE,OAAO;MAAS,gBAAgB;MAAQ,eAAe;MAAa;eAE5E,oBAAC,iBAAD,EAAA,UACGA,QAAM,SAAS,IAAI,WAAW,OAAO,MACpC,oBAAC,cAAD;MAEE,WAAW,GACT,kDACA,kBAAkB,OAAO,CAC1B;MACD,OAAO,kBAAkB,GAAG,OAAO;gBAElC;MACY,EARR,EAQQ,CACf,EACc,CAAA;KACT,CAAA;IAEV,cAAc,iBACb,oBAAC,UAAD;KACE,MAAK;KACL,eAAe,KAAK,YAAY;KAChC,WAAU;KACV,cAAW;eAEX,oBAAC,kBAAD,EAAkB,WAAU,UAAW,CAAA;KAChC,CAAA;IAEP;KACE"}
@@ -1,19 +1,22 @@
1
+ import * as React$1 from "react";
1
2
  import * as react_jsx_runtime0 from "react/jsx-runtime";
2
3
 
3
4
  //#region src/client/storefront/filters/filter-chip.d.ts
4
5
  interface FilterChipProps {
5
- label: string;
6
+ label?: string;
6
7
  active: boolean;
7
8
  onClick: () => void;
8
9
  onRemove?: () => void;
9
10
  className?: string;
11
+ children?: React$1.ReactNode;
10
12
  }
11
13
  declare function FilterChip({
12
14
  label,
13
15
  active,
14
16
  onClick,
15
17
  onRemove,
16
- className
18
+ className,
19
+ children
17
20
  }: FilterChipProps): react_jsx_runtime0.JSX.Element;
18
21
  //#endregion
19
22
  export { FilterChip, type FilterChipProps };
@@ -1 +1 @@
1
- {"version":3,"file":"filter-chip.d.ts","names":[],"sources":["../../../../src/client/storefront/filters/filter-chip.tsx"],"mappings":";;;UAOU,eAAA;EACR,KAAA;EACA,MAAA;EACA,OAAA;EACA,QAAA;EACA,SAAA;AAAA;AAAA,iBAGO,UAAA,CAAA;EAAa,KAAA;EAAO,MAAA;EAAQ,OAAA;EAAS,QAAA;EAAU;AAAA,GAAa,eAAA,GAAe,kBAAA,CAAA,GAAA,CAAA,OAAA"}
1
+ {"version":3,"file":"filter-chip.d.ts","names":[],"sources":["../../../../src/client/storefront/filters/filter-chip.tsx"],"mappings":";;;;UAOU,eAAA;EACR,KAAA;EACA,MAAA;EACA,OAAA;EACA,QAAA;EACA,SAAA;EACA,QAAA,GAAW,OAAA,CAAM,SAAA;AAAA;AAAA,iBAGV,UAAA,CAAA;EAAa,KAAA;EAAO,MAAA;EAAQ,OAAA;EAAS,QAAA;EAAU,SAAA;EAAW;AAAA,GAAY,eAAA,GAAe,kBAAA,CAAA,GAAA,CAAA,OAAA"}
@@ -5,15 +5,17 @@ import { Toggle } from "../../ui-resolver/toggle.mjs";
5
5
  import "react";
6
6
  import { jsx, jsxs } from "react/jsx-runtime";
7
7
  //#region src/client/storefront/filters/filter-chip.tsx
8
- function FilterChip({ label, active, onClick, onRemove, className }) {
8
+ function FilterChip({ label, active, onClick, onRemove, className, children }) {
9
9
  const CloseIcon = useIcon("close");
10
+ const content = label ?? children;
11
+ const accessibleLabel = label ?? (typeof children === "string" ? children : void 0);
10
12
  return /* @__PURE__ */ jsxs(Toggle, {
11
13
  pressed: active,
12
14
  onPressedChange: () => active && onRemove ? onRemove() : onClick(),
13
- variant: "outline",
15
+ variant: "outlined",
14
16
  className: cn("enad-interactive rounded-[var(--enad-button-radius)] px-3 text-sm", active && "bg-primary text-primary-foreground hover:bg-primary-hover hover:text-primary-foreground", className),
15
- "aria-label": active ? `Remove ${label}` : label,
16
- children: [label, active && onRemove && /* @__PURE__ */ jsx(CloseIcon, { className: "relative size-3 hit-area-6" })]
17
+ "aria-label": accessibleLabel ? active ? `Remove ${accessibleLabel}` : accessibleLabel : void 0,
18
+ children: [content, active && onRemove && /* @__PURE__ */ jsx(CloseIcon, { className: "relative size-3 hit-area-6" })]
17
19
  });
18
20
  }
19
21
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"filter-chip.mjs","names":[],"sources":["../../../../src/client/storefront/filters/filter-chip.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { useIcon } from \"../../icons/icon-context\";\nimport { Toggle } from \"../../ui-resolver/toggle\";\nimport { cn } from \"../../ui/utils\";\n\ninterface FilterChipProps {\n label: string;\n active: boolean;\n onClick: () => void;\n onRemove?: () => void;\n className?: string;\n}\n\nfunction FilterChip({ label, active, onClick, onRemove, className }: FilterChipProps) {\n const CloseIcon = useIcon(\"close\");\n return (\n <Toggle\n pressed={active}\n onPressedChange={() => (active && onRemove ? onRemove() : onClick())}\n variant=\"outline\"\n className={cn(\n \"enad-interactive rounded-[var(--enad-button-radius)] px-3 text-sm\",\n active &&\n \"bg-primary text-primary-foreground hover:bg-primary-hover hover:text-primary-foreground\",\n className,\n )}\n aria-label={active ? `Remove ${label}` : label}\n >\n {label}\n {active && onRemove && <CloseIcon className=\"relative size-3 hit-area-6\" />}\n </Toggle>\n );\n}\n\nexport { FilterChip, type FilterChipProps };\n"],"mappings":";;;;;;;AAeA,SAAS,WAAW,EAAE,OAAO,QAAQ,SAAS,UAAU,aAA8B;CACpF,MAAM,YAAY,QAAQ,QAAQ;AAClC,QACE,qBAAC,QAAD;EACE,SAAS;EACT,uBAAwB,UAAU,WAAW,UAAU,GAAG,SAAS;EACnE,SAAQ;EACR,WAAW,GACT,qEACA,UACE,2FACF,UACD;EACD,cAAY,SAAS,UAAU,UAAU;YAV3C,CAYG,OACA,UAAU,YAAY,oBAAC,WAAD,EAAW,WAAU,8BAA+B,CAAA,CACpE"}
1
+ {"version":3,"file":"filter-chip.mjs","names":[],"sources":["../../../../src/client/storefront/filters/filter-chip.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { useIcon } from \"../../icons/icon-context\";\nimport { Toggle } from \"../../ui-resolver/toggle\";\nimport { cn } from \"../../ui/utils\";\n\ninterface FilterChipProps {\n label?: string;\n active: boolean;\n onClick: () => void;\n onRemove?: () => void;\n className?: string;\n children?: React.ReactNode;\n}\n\nfunction FilterChip({ label, active, onClick, onRemove, className, children }: FilterChipProps) {\n const CloseIcon = useIcon(\"close\");\n const content = label ?? children;\n const accessibleLabel = label ?? (typeof children === \"string\" ? children : undefined);\n return (\n <Toggle\n pressed={active}\n onPressedChange={() => (active && onRemove ? onRemove() : onClick())}\n variant=\"outlined\"\n className={cn(\n \"enad-interactive rounded-[var(--enad-button-radius)] px-3 text-sm\",\n active &&\n \"bg-primary text-primary-foreground hover:bg-primary-hover hover:text-primary-foreground\",\n className,\n )}\n aria-label={\n accessibleLabel ? (active ? `Remove ${accessibleLabel}` : accessibleLabel) : undefined\n }\n >\n {content}\n {active && onRemove && <CloseIcon className=\"relative size-3 hit-area-6\" />}\n </Toggle>\n );\n}\n\nexport { FilterChip, type FilterChipProps };\n"],"mappings":";;;;;;;AAgBA,SAAS,WAAW,EAAE,OAAO,QAAQ,SAAS,UAAU,WAAW,YAA6B;CAC9F,MAAM,YAAY,QAAQ,QAAQ;CAClC,MAAM,UAAU,SAAS;CACzB,MAAM,kBAAkB,UAAU,OAAO,aAAa,WAAW,WAAW,KAAA;AAC5E,QACE,qBAAC,QAAD;EACE,SAAS;EACT,uBAAwB,UAAU,WAAW,UAAU,GAAG,SAAS;EACnE,SAAQ;EACR,WAAW,GACT,qEACA,UACE,2FACF,UACD;EACD,cACE,kBAAmB,SAAS,UAAU,oBAAoB,kBAAmB,KAAA;YAXjF,CAcG,SACA,UAAU,YAAY,oBAAC,WAAD,EAAW,WAAU,8BAA+B,CAAA,CACpE"}
@@ -2,8 +2,8 @@
2
2
  import { useIcon } from "../../icons/icon-context.mjs";
3
3
  import { cn } from "../../ui/utils.mjs";
4
4
  import { Button } from "../../ui/button.mjs";
5
- import { Separator } from "../../ui-resolver/separator.mjs";
6
5
  import { Sheet, SheetContent, SheetTitle } from "../../ui-resolver/sheet.mjs";
6
+ import { Separator } from "../../ui-resolver/separator.mjs";
7
7
  import { FilterGroup } from "./filter-group.mjs";
8
8
  import * as React$1 from "react";
9
9
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -34,7 +34,7 @@ function FilterPanelContent({ filters, activeFilters, onFilterChange, productCou
34
34
  })
35
35
  ]
36
36
  }), /* @__PURE__ */ jsx(Button, {
37
- variant: "ghost",
37
+ variant: "plain",
38
38
  size: "icon",
39
39
  onClick: onClose,
40
40
  "aria-label": "Close filters",
@@ -1 +1 @@
1
- {"version":3,"file":"filter-panel.mjs","names":["React"],"sources":["../../../../src/client/storefront/filters/filter-panel.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { useIcon } from \"../../icons/icon-context\";\nimport { Button } from \"../../ui/button\";\nimport { Separator } from \"../../ui-resolver/separator\";\nimport { Sheet, SheetContent, SheetTitle } from \"../../ui-resolver/sheet\";\nimport { cn } from \"../../ui/utils\";\nimport { FilterGroup, type FilterOption } from \"./filter-group\";\n\ninterface FilterDefinition {\n key: string;\n title: string;\n type: \"checkbox\" | \"color\" | \"price\";\n options: FilterOption[];\n}\n\ntype ActiveFilters = Record<string, string[]>;\n\ninterface FilterPanelProps {\n filters: FilterDefinition[];\n activeFilters: ActiveFilters;\n onFilterChange: (key: string, selected: string[]) => void;\n productCount?: number;\n open: boolean;\n onOpenChange: (open: boolean) => void;\n labels?: {\n title?: string;\n clearAll?: string;\n showResults?: string;\n };\n className?: string;\n}\n\nfunction FilterPanelContent({\n filters,\n activeFilters,\n onFilterChange,\n productCount,\n onClose,\n labels,\n}: {\n filters: FilterDefinition[];\n activeFilters: ActiveFilters;\n onFilterChange: (key: string, selected: string[]) => void;\n productCount?: number;\n onClose: () => void;\n labels?: FilterPanelProps[\"labels\"];\n}) {\n const SlidersIcon = useIcon(\"sliders\");\n const CloseIcon = useIcon(\"close\");\n const totalActive = Object.values(activeFilters).reduce(\n (sum, arr) => sum + arr.filter(Boolean).length,\n 0,\n );\n\n const clearAll = () => {\n for (const filter of filters) {\n onFilterChange(filter.key, []);\n }\n };\n\n return (\n <div className=\"flex h-full flex-col\">\n <div className=\"flex items-center justify-between p-4\">\n <div className=\"flex items-center gap-2\">\n <SlidersIcon className=\"size-5\" />\n <span className=\"text-sm font-medium\">{labels?.title ?? \"Filter & Sort\"}</span>\n {totalActive > 0 && (\n <span className=\"flex size-5 items-center justify-center rounded-full bg-primary text-xs text-primary-foreground\">\n {totalActive}\n </span>\n )}\n </div>\n <Button variant=\"ghost\" size=\"icon\" onClick={onClose} aria-label=\"Close filters\">\n <CloseIcon />\n </Button>\n </div>\n\n <Separator />\n\n <div className=\"flex-1 overflow-y-auto p-4\">\n {totalActive > 0 && (\n <button\n type=\"button\"\n onClick={clearAll}\n className=\"mb-4 text-sm text-muted-foreground underline underline-offset-4 hover:text-foreground\"\n >\n {labels?.clearAll ?? \"Clear all\"}\n </button>\n )}\n\n {filters.map((filter) => (\n <React.Fragment key={filter.key}>\n <FilterGroup\n title={filter.title}\n type={filter.type}\n options={filter.options}\n selected={activeFilters[filter.key] ?? []}\n onChange={(selected) => onFilterChange(filter.key, selected)}\n />\n <Separator />\n </React.Fragment>\n ))}\n </div>\n\n {productCount != null && (\n <>\n <Separator />\n <div className=\"p-4\">\n <Button className=\"w-full\" onClick={onClose}>\n {labels?.showResults\n ? labels.showResults.replace(\"{count}\", String(productCount))\n : `Show ${productCount} results`}\n </Button>\n </div>\n </>\n )}\n </div>\n );\n}\n\nfunction FilterPanel({\n filters,\n activeFilters,\n onFilterChange,\n productCount,\n open,\n onOpenChange,\n labels,\n className,\n}: FilterPanelProps) {\n return (\n <>\n {/* Mobile: Sheet */}\n <div className={cn(\"md:hidden\", className)}>\n <Sheet open={open} onOpenChange={onOpenChange}>\n <SheetContent side=\"left\" className=\"w-full max-w-sm p-0\">\n <SheetTitle className=\"sr-only\" showClose={false}>\n {labels?.title ?? \"Filters\"}\n </SheetTitle>\n <FilterPanelContent\n filters={filters}\n activeFilters={activeFilters}\n onFilterChange={onFilterChange}\n productCount={productCount}\n onClose={() => onOpenChange(false)}\n labels={labels}\n />\n </SheetContent>\n </Sheet>\n </div>\n\n {/* Desktop: Sidebar */}\n {open && (\n <aside className={cn(\"hidden w-full max-w-[280px] md:block\", className)}>\n <FilterPanelContent\n filters={filters}\n activeFilters={activeFilters}\n onFilterChange={onFilterChange}\n productCount={productCount}\n onClose={() => onOpenChange(false)}\n labels={labels}\n />\n </aside>\n )}\n </>\n );\n}\n\nexport { FilterPanel, type FilterPanelProps, type FilterDefinition, type ActiveFilters };\n"],"mappings":";;;;;;;;;;AAkCA,SAAS,mBAAmB,EAC1B,SACA,eACA,gBACA,cACA,SACA,UAQC;CACD,MAAM,cAAc,QAAQ,UAAU;CACtC,MAAM,YAAY,QAAQ,QAAQ;CAClC,MAAM,cAAc,OAAO,OAAO,cAAc,CAAC,QAC9C,KAAK,QAAQ,MAAM,IAAI,OAAO,QAAQ,CAAC,QACxC,EACD;CAED,MAAM,iBAAiB;AACrB,OAAK,MAAM,UAAU,QACnB,gBAAe,OAAO,KAAK,EAAE,CAAC;;AAIlC,QACE,qBAAC,OAAD;EAAK,WAAU;YAAf;GACE,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,qBAAC,OAAD;KAAK,WAAU;eAAf;MACE,oBAAC,aAAD,EAAa,WAAU,UAAW,CAAA;MAClC,oBAAC,QAAD;OAAM,WAAU;iBAAuB,QAAQ,SAAS;OAAuB,CAAA;MAC9E,cAAc,KACb,oBAAC,QAAD;OAAM,WAAU;iBACb;OACI,CAAA;MAEL;QACN,oBAAC,QAAD;KAAQ,SAAQ;KAAQ,MAAK;KAAO,SAAS;KAAS,cAAW;eAC/D,oBAAC,WAAD,EAAa,CAAA;KACN,CAAA,CACL;;GAEN,oBAAC,WAAD,EAAa,CAAA;GAEb,qBAAC,OAAD;IAAK,WAAU;cAAf,CACG,cAAc,KACb,oBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,WAAU;eAET,QAAQ,YAAY;KACd,CAAA,EAGV,QAAQ,KAAK,WACZ,qBAACA,QAAM,UAAP,EAAA,UAAA,CACE,oBAAC,aAAD;KACE,OAAO,OAAO;KACd,MAAM,OAAO;KACb,SAAS,OAAO;KAChB,UAAU,cAAc,OAAO,QAAQ,EAAE;KACzC,WAAW,aAAa,eAAe,OAAO,KAAK,SAAS;KAC5D,CAAA,EACF,oBAAC,WAAD,EAAa,CAAA,CACE,EAAA,EATI,OAAO,IASX,CACjB,CACE;;GAEL,gBAAgB,QACf,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,WAAD,EAAa,CAAA,EACb,oBAAC,OAAD;IAAK,WAAU;cACb,oBAAC,QAAD;KAAQ,WAAU;KAAS,SAAS;eACjC,QAAQ,cACL,OAAO,YAAY,QAAQ,WAAW,OAAO,aAAa,CAAC,GAC3D,QAAQ,aAAa;KAClB,CAAA;IACL,CAAA,CACL,EAAA,CAAA;GAED;;;AAIV,SAAS,YAAY,EACnB,SACA,eACA,gBACA,cACA,MACA,cACA,QACA,aACmB;AACnB,QACE,qBAAA,UAAA,EAAA,UAAA,CAEE,oBAAC,OAAD;EAAK,WAAW,GAAG,aAAa,UAAU;YACxC,oBAAC,OAAD;GAAa;GAAoB;aAC/B,qBAAC,cAAD;IAAc,MAAK;IAAO,WAAU;cAApC,CACE,oBAAC,YAAD;KAAY,WAAU;KAAU,WAAW;eACxC,QAAQ,SAAS;KACP,CAAA,EACb,oBAAC,oBAAD;KACW;KACM;KACC;KACF;KACd,eAAe,aAAa,MAAM;KAC1B;KACR,CAAA,CACW;;GACT,CAAA;EACJ,CAAA,EAGL,QACC,oBAAC,SAAD;EAAO,WAAW,GAAG,wCAAwC,UAAU;YACrE,oBAAC,oBAAD;GACW;GACM;GACC;GACF;GACd,eAAe,aAAa,MAAM;GAC1B;GACR,CAAA;EACI,CAAA,CAET,EAAA,CAAA"}
1
+ {"version":3,"file":"filter-panel.mjs","names":["React"],"sources":["../../../../src/client/storefront/filters/filter-panel.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { useIcon } from \"../../icons/icon-context\";\nimport { Button } from \"../../ui/button\";\nimport { Separator } from \"../../ui-resolver/separator\";\nimport { Sheet, SheetContent, SheetTitle } from \"../../ui-resolver/sheet\";\nimport { cn } from \"../../ui/utils\";\nimport { FilterGroup, type FilterOption } from \"./filter-group\";\n\ninterface FilterDefinition {\n key: string;\n title: string;\n type: \"checkbox\" | \"color\" | \"price\";\n options: FilterOption[];\n}\n\ntype ActiveFilters = Record<string, string[]>;\n\ninterface FilterPanelProps {\n filters: FilterDefinition[];\n activeFilters: ActiveFilters;\n onFilterChange: (key: string, selected: string[]) => void;\n productCount?: number;\n open: boolean;\n onOpenChange: (open: boolean) => void;\n labels?: {\n title?: string;\n clearAll?: string;\n showResults?: string;\n };\n className?: string;\n}\n\nfunction FilterPanelContent({\n filters,\n activeFilters,\n onFilterChange,\n productCount,\n onClose,\n labels,\n}: {\n filters: FilterDefinition[];\n activeFilters: ActiveFilters;\n onFilterChange: (key: string, selected: string[]) => void;\n productCount?: number;\n onClose: () => void;\n labels?: FilterPanelProps[\"labels\"];\n}) {\n const SlidersIcon = useIcon(\"sliders\");\n const CloseIcon = useIcon(\"close\");\n const totalActive = Object.values(activeFilters).reduce(\n (sum, arr) => sum + arr.filter(Boolean).length,\n 0,\n );\n\n const clearAll = () => {\n for (const filter of filters) {\n onFilterChange(filter.key, []);\n }\n };\n\n return (\n <div className=\"flex h-full flex-col\">\n <div className=\"flex items-center justify-between p-4\">\n <div className=\"flex items-center gap-2\">\n <SlidersIcon className=\"size-5\" />\n <span className=\"text-sm font-medium\">{labels?.title ?? \"Filter & Sort\"}</span>\n {totalActive > 0 && (\n <span className=\"flex size-5 items-center justify-center rounded-full bg-primary text-xs text-primary-foreground\">\n {totalActive}\n </span>\n )}\n </div>\n <Button variant=\"plain\" size=\"icon\" onClick={onClose} aria-label=\"Close filters\">\n <CloseIcon />\n </Button>\n </div>\n\n <Separator />\n\n <div className=\"flex-1 overflow-y-auto p-4\">\n {totalActive > 0 && (\n <button\n type=\"button\"\n onClick={clearAll}\n className=\"mb-4 text-sm text-muted-foreground underline underline-offset-4 hover:text-foreground\"\n >\n {labels?.clearAll ?? \"Clear all\"}\n </button>\n )}\n\n {filters.map((filter) => (\n <React.Fragment key={filter.key}>\n <FilterGroup\n title={filter.title}\n type={filter.type}\n options={filter.options}\n selected={activeFilters[filter.key] ?? []}\n onChange={(selected) => onFilterChange(filter.key, selected)}\n />\n <Separator />\n </React.Fragment>\n ))}\n </div>\n\n {productCount != null && (\n <>\n <Separator />\n <div className=\"p-4\">\n <Button className=\"w-full\" onClick={onClose}>\n {labels?.showResults\n ? labels.showResults.replace(\"{count}\", String(productCount))\n : `Show ${productCount} results`}\n </Button>\n </div>\n </>\n )}\n </div>\n );\n}\n\nfunction FilterPanel({\n filters,\n activeFilters,\n onFilterChange,\n productCount,\n open,\n onOpenChange,\n labels,\n className,\n}: FilterPanelProps) {\n return (\n <>\n {/* Mobile: Sheet */}\n <div className={cn(\"md:hidden\", className)}>\n <Sheet open={open} onOpenChange={onOpenChange}>\n <SheetContent side=\"left\" className=\"w-full max-w-sm p-0\">\n <SheetTitle className=\"sr-only\" showClose={false}>\n {labels?.title ?? \"Filters\"}\n </SheetTitle>\n <FilterPanelContent\n filters={filters}\n activeFilters={activeFilters}\n onFilterChange={onFilterChange}\n productCount={productCount}\n onClose={() => onOpenChange(false)}\n labels={labels}\n />\n </SheetContent>\n </Sheet>\n </div>\n\n {/* Desktop: Sidebar */}\n {open && (\n <aside className={cn(\"hidden w-full max-w-[280px] md:block\", className)}>\n <FilterPanelContent\n filters={filters}\n activeFilters={activeFilters}\n onFilterChange={onFilterChange}\n productCount={productCount}\n onClose={() => onOpenChange(false)}\n labels={labels}\n />\n </aside>\n )}\n </>\n );\n}\n\nexport { FilterPanel, type FilterPanelProps, type FilterDefinition, type ActiveFilters };\n"],"mappings":";;;;;;;;;;AAkCA,SAAS,mBAAmB,EAC1B,SACA,eACA,gBACA,cACA,SACA,UAQC;CACD,MAAM,cAAc,QAAQ,UAAU;CACtC,MAAM,YAAY,QAAQ,QAAQ;CAClC,MAAM,cAAc,OAAO,OAAO,cAAc,CAAC,QAC9C,KAAK,QAAQ,MAAM,IAAI,OAAO,QAAQ,CAAC,QACxC,EACD;CAED,MAAM,iBAAiB;AACrB,OAAK,MAAM,UAAU,QACnB,gBAAe,OAAO,KAAK,EAAE,CAAC;;AAIlC,QACE,qBAAC,OAAD;EAAK,WAAU;YAAf;GACE,qBAAC,OAAD;IAAK,WAAU;cAAf,CACE,qBAAC,OAAD;KAAK,WAAU;eAAf;MACE,oBAAC,aAAD,EAAa,WAAU,UAAW,CAAA;MAClC,oBAAC,QAAD;OAAM,WAAU;iBAAuB,QAAQ,SAAS;OAAuB,CAAA;MAC9E,cAAc,KACb,oBAAC,QAAD;OAAM,WAAU;iBACb;OACI,CAAA;MAEL;QACN,oBAAC,QAAD;KAAQ,SAAQ;KAAQ,MAAK;KAAO,SAAS;KAAS,cAAW;eAC/D,oBAAC,WAAD,EAAa,CAAA;KACN,CAAA,CACL;;GAEN,oBAAC,WAAD,EAAa,CAAA;GAEb,qBAAC,OAAD;IAAK,WAAU;cAAf,CACG,cAAc,KACb,oBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,WAAU;eAET,QAAQ,YAAY;KACd,CAAA,EAGV,QAAQ,KAAK,WACZ,qBAACA,QAAM,UAAP,EAAA,UAAA,CACE,oBAAC,aAAD;KACE,OAAO,OAAO;KACd,MAAM,OAAO;KACb,SAAS,OAAO;KAChB,UAAU,cAAc,OAAO,QAAQ,EAAE;KACzC,WAAW,aAAa,eAAe,OAAO,KAAK,SAAS;KAC5D,CAAA,EACF,oBAAC,WAAD,EAAa,CAAA,CACE,EAAA,EATI,OAAO,IASX,CACjB,CACE;;GAEL,gBAAgB,QACf,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,WAAD,EAAa,CAAA,EACb,oBAAC,OAAD;IAAK,WAAU;cACb,oBAAC,QAAD;KAAQ,WAAU;KAAS,SAAS;eACjC,QAAQ,cACL,OAAO,YAAY,QAAQ,WAAW,OAAO,aAAa,CAAC,GAC3D,QAAQ,aAAa;KAClB,CAAA;IACL,CAAA,CACL,EAAA,CAAA;GAED;;;AAIV,SAAS,YAAY,EACnB,SACA,eACA,gBACA,cACA,MACA,cACA,QACA,aACmB;AACnB,QACE,qBAAA,UAAA,EAAA,UAAA,CAEE,oBAAC,OAAD;EAAK,WAAW,GAAG,aAAa,UAAU;YACxC,oBAAC,OAAD;GAAa;GAAoB;aAC/B,qBAAC,cAAD;IAAc,MAAK;IAAO,WAAU;cAApC,CACE,oBAAC,YAAD;KAAY,WAAU;KAAU,WAAW;eACxC,QAAQ,SAAS;KACP,CAAA,EACb,oBAAC,oBAAD;KACW;KACM;KACC;KACF;KACd,eAAe,aAAa,MAAM;KAC1B;KACR,CAAA,CACW;;GACT,CAAA;EACJ,CAAA,EAGL,QACC,oBAAC,SAAD;EAAO,WAAW,GAAG,wCAAwC,UAAU;YACrE,oBAAC,oBAAD;GACW;GACM;GACC;GACF;GACd,eAAe,aAAa,MAAM;GAC1B;GACR,CAAA;EACI,CAAA,CAET,EAAA,CAAA"}
@@ -14,7 +14,7 @@ function ToggleListView({ mode, onChange, className }) {
14
14
  onValueChange: (value) => {
15
15
  if (value) onChange(value);
16
16
  },
17
- variant: "outline",
17
+ variant: "outlined",
18
18
  className: cn(className),
19
19
  "aria-label": "View mode",
20
20
  children: [/* @__PURE__ */ jsx(ToggleGroupItem, {
@@ -1 +1 @@
1
- {"version":3,"file":"toggle-list-view.mjs","names":[],"sources":["../../../../src/client/storefront/filters/toggle-list-view.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { useIcon } from \"../../icons/icon-context\";\nimport { ToggleGroup, ToggleGroupItem } from \"../../ui-resolver/toggle-group\";\nimport { cn } from \"../../ui/utils\";\n\ninterface ToggleListViewProps {\n mode: \"grid\" | \"list\";\n onChange: (mode: \"grid\" | \"list\") => void;\n className?: string;\n}\n\nfunction ToggleListView({ mode, onChange, className }: ToggleListViewProps) {\n const GridIcon = useIcon(\"grid\");\n const ListIcon = useIcon(\"list\");\n return (\n <ToggleGroup\n type=\"single\"\n value={mode}\n onValueChange={(value) => {\n if (value) onChange(value as \"grid\" | \"list\");\n }}\n variant=\"outline\"\n className={cn(className)}\n aria-label=\"View mode\"\n >\n <ToggleGroupItem value=\"grid\" aria-label=\"Grid view\" className=\"relative hit-area-2\">\n <GridIcon className=\"size-4\" />\n </ToggleGroupItem>\n <ToggleGroupItem value=\"list\" aria-label=\"List view\" className=\"relative hit-area-2\">\n <ListIcon className=\"size-4\" />\n </ToggleGroupItem>\n </ToggleGroup>\n );\n}\n\nexport { ToggleListView, type ToggleListViewProps };\n"],"mappings":";;;;;;;AAaA,SAAS,eAAe,EAAE,MAAM,UAAU,aAAkC;CAC1E,MAAM,WAAW,QAAQ,OAAO;CAChC,MAAM,WAAW,QAAQ,OAAO;AAChC,QACE,qBAAC,aAAD;EACE,MAAK;EACL,OAAO;EACP,gBAAgB,UAAU;AACxB,OAAI,MAAO,UAAS,MAAyB;;EAE/C,SAAQ;EACR,WAAW,GAAG,UAAU;EACxB,cAAW;YARb,CAUE,oBAAC,iBAAD;GAAiB,OAAM;GAAO,cAAW;GAAY,WAAU;aAC7D,oBAAC,UAAD,EAAU,WAAU,UAAW,CAAA;GACf,CAAA,EAClB,oBAAC,iBAAD;GAAiB,OAAM;GAAO,cAAW;GAAY,WAAU;aAC7D,oBAAC,UAAD,EAAU,WAAU,UAAW,CAAA;GACf,CAAA,CACN"}
1
+ {"version":3,"file":"toggle-list-view.mjs","names":[],"sources":["../../../../src/client/storefront/filters/toggle-list-view.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { useIcon } from \"../../icons/icon-context\";\nimport { ToggleGroup, ToggleGroupItem } from \"../../ui-resolver/toggle-group\";\nimport { cn } from \"../../ui/utils\";\n\ninterface ToggleListViewProps {\n mode: \"grid\" | \"list\";\n onChange: (mode: \"grid\" | \"list\") => void;\n className?: string;\n}\n\nfunction ToggleListView({ mode, onChange, className }: ToggleListViewProps) {\n const GridIcon = useIcon(\"grid\");\n const ListIcon = useIcon(\"list\");\n return (\n <ToggleGroup\n type=\"single\"\n value={mode}\n onValueChange={(value) => {\n if (value) onChange(value as \"grid\" | \"list\");\n }}\n variant=\"outlined\"\n className={cn(className)}\n aria-label=\"View mode\"\n >\n <ToggleGroupItem value=\"grid\" aria-label=\"Grid view\" className=\"relative hit-area-2\">\n <GridIcon className=\"size-4\" />\n </ToggleGroupItem>\n <ToggleGroupItem value=\"list\" aria-label=\"List view\" className=\"relative hit-area-2\">\n <ListIcon className=\"size-4\" />\n </ToggleGroupItem>\n </ToggleGroup>\n );\n}\n\nexport { ToggleListView, type ToggleListViewProps };\n"],"mappings":";;;;;;;AAaA,SAAS,eAAe,EAAE,MAAM,UAAU,aAAkC;CAC1E,MAAM,WAAW,QAAQ,OAAO;CAChC,MAAM,WAAW,QAAQ,OAAO;AAChC,QACE,qBAAC,aAAD;EACE,MAAK;EACL,OAAO;EACP,gBAAgB,UAAU;AACxB,OAAI,MAAO,UAAS,MAAyB;;EAE/C,SAAQ;EACR,WAAW,GAAG,UAAU;EACxB,cAAW;YARb,CAUE,oBAAC,iBAAD;GAAiB,OAAM;GAAO,cAAW;GAAY,WAAU;aAC7D,oBAAC,UAAD,EAAU,WAAU,UAAW,CAAA;GACf,CAAA,EAClB,oBAAC,iBAAD;GAAiB,OAAM;GAAO,cAAW;GAAY,WAAU;aAC7D,oBAAC,UAAD,EAAU,WAAU,UAAW,CAAA;GACf,CAAA,CACN"}
@@ -3,17 +3,28 @@ import { SelectOption, StorefrontSelect, StorefrontSelectProps } from "./primiti
3
3
  import { AddressFieldGroup, AddressFieldGroupProps } from "./components/address-field-group.js";
4
4
  import { Badge, BadgeProps } from "./components/badge.js";
5
5
  import { CountryInfo, CountryRedirect, CountryRedirectProps } from "./components/country-redirect.js";
6
+ import { EmptyState, EmptyStateProps } from "./components/empty-state.js";
7
+ import { ImageLightbox, ImageLightboxProps } from "./components/image-lightbox.js";
8
+ import { InfiniteScroll, InfiniteScrollProps, UseInfiniteScrollOptions, UseInfiniteScrollReturn, useInfiniteScroll } from "./components/infinite-scroll.js";
6
9
  import { LanguageSelector } from "./components/language-selector.js";
7
10
  import { NewsletterSignup, NewsletterSignupProps } from "./components/newsletter-signup.js";
8
11
  import { Price, PriceProps } from "./components/price.js";
12
+ import { ProductRecommendations, ProductRecommendationsProps } from "./components/product-recommendations.js";
9
13
  import { ProductTab, ProductTabs, ProductTabsProps } from "./components/product-tabs.js";
14
+ import { QuickView, QuickViewProduct, QuickViewProps } from "./components/quick-view.js";
15
+ import { ReviewDistribution, ReviewSummary, ReviewSummaryProps } from "./components/review-summary.js";
16
+ import { SearchAutocomplete, SearchAutocompleteProps, SearchSuggestion } from "./components/search-autocomplete.js";
10
17
  import { SectionLink, SectionNav, SectionNavProps } from "./components/section-nav.js";
18
+ import { ShareButton, ShareButtonProps } from "./components/share-button.js";
11
19
  import { ProductCardSkeleton, Skeleton, SkeletonProps } from "./components/skeleton.js";
12
20
  import { StarRating, StarRatingProps } from "./components/star-rating.js";
21
+ import { Testimonial, TestimonialGrid, TestimonialGridProps, TestimonialProps } from "./components/testimonial.js";
22
+ import { TrustBadge, TrustBadges, TrustBadgesProps } from "./components/trust-badges.js";
13
23
  import { VariantAxis, VariantOption, VariantSelector, VariantSelectorProps } from "./components/variant-selector.js";
24
+ import { WishlistToggle, WishlistToggleProps } from "./components/wishlist-toggle.js";
14
25
  import { StorefrontInput, StorefrontInputProps } from "./primitives/input.js";
15
26
  import { PasswordInput, PasswordInputProps } from "./primitives/password-input.js";
16
27
  import { StorefrontCheckbox, StorefrontCheckboxProps } from "./primitives/checkbox.js";
17
28
  import { CountrySelect, CountrySelectProps, NORDIC_COUNTRIES } from "./primitives/country-select.js";
18
29
  import { SimpleSelect, SimpleSelectOption, SimpleSelectProps } from "../ui/simple-select.js";
19
- export { AddressFieldGroup, type AddressFieldGroupProps, Badge, type BadgeProps, type ColorTheme, type CountryInfo, CountryRedirect, type CountryRedirectProps, CountrySelect, type CountrySelectProps, type GalleryVariant, type LanguageOption, LanguageSelector, type LanguageSelectorProps, type LinkBlockVariant, NORDIC_COUNTRIES, NewsletterSignup, type NewsletterSignupProps, PasswordInput, type PasswordInputProps, Price, type PriceProps, ProductCardSkeleton, type ProductTab, ProductTabs, type ProductTabsProps, type SectionLink, SectionNav, type SectionNavProps, type SelectOption, SimpleSelect, type SimpleSelectOption, type SimpleSelectProps, Skeleton, type SkeletonProps, StarRating, type StarRatingProps, StorefrontCheckbox, type StorefrontCheckboxProps, StorefrontInput, type StorefrontInputProps, StorefrontSelect, type StorefrontSelectProps, type TextContentVariant, type TextContentWithImageVariant, type VariantAxis, type VariantOption, VariantSelector, type VariantSelectorProps };
30
+ export { AddressFieldGroup, type AddressFieldGroupProps, Badge, type BadgeProps, type ColorTheme, type CountryInfo, CountryRedirect, type CountryRedirectProps, CountrySelect, type CountrySelectProps, EmptyState, type EmptyStateProps, type GalleryVariant, ImageLightbox, type ImageLightboxProps, InfiniteScroll, type InfiniteScrollProps, type LanguageOption, LanguageSelector, type LanguageSelectorProps, type LinkBlockVariant, NORDIC_COUNTRIES, NewsletterSignup, type NewsletterSignupProps, PasswordInput, type PasswordInputProps, Price, type PriceProps, ProductCardSkeleton, ProductRecommendations, type ProductRecommendationsProps, type ProductTab, ProductTabs, type ProductTabsProps, QuickView, type QuickViewProduct, type QuickViewProps, type ReviewDistribution, ReviewSummary, type ReviewSummaryProps, SearchAutocomplete, type SearchAutocompleteProps, type SearchSuggestion, type SectionLink, SectionNav, type SectionNavProps, type SelectOption, ShareButton, type ShareButtonProps, SimpleSelect, type SimpleSelectOption, type SimpleSelectProps, Skeleton, type SkeletonProps, StarRating, type StarRatingProps, StorefrontCheckbox, type StorefrontCheckboxProps, StorefrontInput, type StorefrontInputProps, StorefrontSelect, type StorefrontSelectProps, Testimonial, TestimonialGrid, type TestimonialGridProps, type TestimonialProps, type TextContentVariant, type TextContentWithImageVariant, type TrustBadge, TrustBadges, type TrustBadgesProps, type UseInfiniteScrollOptions, type UseInfiniteScrollReturn, type VariantAxis, type VariantOption, VariantSelector, type VariantSelectorProps, WishlistToggle, type WishlistToggleProps, useInfiniteScroll };
@@ -15,4 +15,15 @@ import { SimpleSelect } from "../ui/simple-select.mjs";
15
15
  import { LanguageSelector } from "./components/language-selector.mjs";
16
16
  import { CountryRedirect } from "./components/country-redirect.mjs";
17
17
  import { SectionNav } from "./components/section-nav.mjs";
18
- export { AddressFieldGroup, Badge, CountryRedirect, CountrySelect, LanguageSelector, NORDIC_COUNTRIES, NewsletterSignup, PasswordInput, Price, ProductCardSkeleton, ProductTabs, SectionNav, SimpleSelect, Skeleton, StarRating, StorefrontCheckbox, StorefrontInput, StorefrontSelect, VariantSelector };
18
+ import { EmptyState } from "./components/empty-state.mjs";
19
+ import { ImageLightbox } from "./components/image-lightbox.mjs";
20
+ import { InfiniteScroll, useInfiniteScroll } from "./components/infinite-scroll.mjs";
21
+ import { ProductRecommendations } from "./components/product-recommendations.mjs";
22
+ import { QuickView } from "./components/quick-view.mjs";
23
+ import { ReviewSummary } from "./components/review-summary.mjs";
24
+ import { SearchAutocomplete } from "./components/search-autocomplete.mjs";
25
+ import { ShareButton } from "./components/share-button.mjs";
26
+ import { Testimonial, TestimonialGrid } from "./components/testimonial.mjs";
27
+ import { TrustBadges } from "./components/trust-badges.mjs";
28
+ import { WishlistToggle } from "./components/wishlist-toggle.mjs";
29
+ export { AddressFieldGroup, Badge, CountryRedirect, CountrySelect, EmptyState, ImageLightbox, InfiniteScroll, LanguageSelector, NORDIC_COUNTRIES, NewsletterSignup, PasswordInput, Price, ProductCardSkeleton, ProductRecommendations, ProductTabs, QuickView, ReviewSummary, SearchAutocomplete, SectionNav, ShareButton, SimpleSelect, Skeleton, StarRating, StorefrontCheckbox, StorefrontInput, StorefrontSelect, Testimonial, TestimonialGrid, TrustBadges, VariantSelector, WishlistToggle, useInfiniteScroll };
@@ -1 +1 @@
1
- {"version":3,"file":"header.d.ts","names":[],"sources":["../../../../src/client/storefront/layout/header.tsx"],"mappings":";;;;iBA8US,MAAA,CAAA;EACP,OAAA;EACA,QAAA;EACA,YAAA;EACA,gBAAA;EACA,iBAAA;EACA,IAAA;EACA,aAAA;EACA,WAAA;EACA,QAAA;EACA,MAAA;EACA;AAAA,GACC,WAAA,GAAW,kBAAA,CAAA,GAAA,CAAA,OAAA"}
1
+ {"version":3,"file":"header.d.ts","names":[],"sources":["../../../../src/client/storefront/layout/header.tsx"],"mappings":";;;;iBAsbS,MAAA,CAAA;EACP,OAAA;EACA,QAAA;EACA,YAAA;EACA,gBAAA;EACA,iBAAA;EACA,IAAA;EACA,aAAA;EACA,WAAA;EACA,QAAA;EACA,MAAA;EACA;AAAA,GACC,WAAA,GAAW,kBAAA,CAAA,GAAA,CAAA,OAAA"}
@@ -80,7 +80,7 @@ function MegaMenuFeatured({ featured, ChevronRightIcon }) {
80
80
  children: feat.image?.src && /* @__PURE__ */ jsx("img", {
81
81
  src: feat.image.src,
82
82
  alt: feat.image.alt ?? feat.label,
83
- className: "size-full object-cover transition-transform duration-[var(--enad-image-hover-duration)] group-hover:scale-[var(--enad-image-hover-scale)]"
83
+ className: "size-full object-cover"
84
84
  })
85
85
  }), /* @__PURE__ */ jsxs("span", {
86
86
  className: "flex items-center gap-1 text-sm text-foreground transition-colors duration-200 group-hover:text-muted-foreground",
@@ -1 +1 @@
1
- {"version":3,"file":"header.mjs","names":["React"],"sources":["../../../../src/client/storefront/layout/header.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { useIcon } from \"../../icons/icon-context\"\nimport { cn } from \"../../ui/utils\"\nimport { MobileMenuDrawer } from \"./mobile-menu-drawer\"\nimport { PromotionBar } from \"./promotion-bar\"\nimport type { HeaderProps, NavItem, NavChild } from \"../types\"\n\ntype IconComponent = React.ComponentType<{ className?: string }>\nconst CLOSE_DELAY = 150\nconst EMPTY_NAV: NavItem[] = []\nconst EMPTY_UTILITY: NavChild[] = []\nconst EMPTY_MESSAGES: string[] = []\n\nfunction CartElement({ cartHref, onCartClick, linkColor, label, CartIcon }: {\n cartHref?: string; onCartClick?: () => void; linkColor: string; label: string; CartIcon: IconComponent\n}) {\n if (cartHref) {\n return (\n <a href={cartHref} className={cn(\"relative p-1 transition-colors duration-200 hit-area-2\", linkColor)} aria-label={label}>\n <CartIcon className=\"size-5\" />\n </a>\n )\n }\n if (onCartClick) {\n return (\n <button onClick={onCartClick} className={cn(\"relative p-1 transition-colors duration-200 hit-area-2\", linkColor)} aria-label={label}>\n <CartIcon className=\"size-5\" />\n </button>\n )\n }\n return null\n}\n\nfunction MegaMenuPanel({ activeItem, showPanel, onMouseEnter, onMouseLeave, ChevronRightIcon }: {\n activeItem: NavItem | null; showPanel: boolean; onMouseEnter: () => void; onMouseLeave: () => void; ChevronRightIcon: IconComponent\n}) {\n const categories = activeItem?.megaMenuCategories\n const featured = activeItem?.megaMenuFeatured\n\n return (\n <div\n className={cn(\n \"hidden overflow-hidden transition-all duration-300 ease-out lg:block\",\n showPanel ? \"max-h-125 translate-y-0 opacity-100\" : \"max-h-0 -translate-y-1 opacity-0\",\n )}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n role=\"region\"\n aria-label=\"Submenu\"\n >\n <div className=\"border-b border-border bg-background p-8\">\n {categories && categories.length > 0 ? (\n <div className=\"flex gap-12\">\n <MegaMenuCategories categories={categories} />\n {featured && featured.length > 0 && (\n <MegaMenuFeatured featured={featured} ChevronRightIcon={ChevronRightIcon} />\n )}\n </div>\n ) : null}\n </div>\n </div>\n )\n}\n\nfunction MegaMenuCategories({ categories }: { categories: NonNullable<NavItem[\"megaMenuCategories\"]> }) {\n const columnGroups: (typeof categories)[] = []\n for (const cat of categories) {\n if (cat.stackWithPrevious && columnGroups.length > 0) {\n columnGroups[columnGroups.length - 1]!.push(cat)\n } else {\n columnGroups.push([cat])\n }\n }\n\n return (\n <div className=\"flex flex-1 justify-between gap-10\">\n {columnGroups.map((group) => (\n <div\n key={group[0]!.id ?? group[0]!.title}\n className=\"flex w-50 min-w-0 flex-col gap-6\"\n >\n {group.map((cat) => (\n <div key={cat.id ?? cat.title}>\n <a\n href={cat.href}\n className=\"mb-3 block text-sm font-normal text-foreground transition-colors duration-200 hover:text-muted-foreground\"\n >\n {cat.title}\n </a>\n <ul className=\"flex flex-col gap-1.5\">\n {cat.items?.map((item) => (\n <li key={item.href}>\n <a\n href={item.href}\n className=\"text-sm text-muted-foreground transition-colors duration-200 hover:text-foreground\"\n >\n {item.label}\n </a>\n </li>\n ))}\n </ul>\n </div>\n ))}\n </div>\n ))}\n </div>\n )\n}\n\nfunction MegaMenuFeatured({ featured, ChevronRightIcon }: {\n featured: NonNullable<NavItem[\"megaMenuFeatured\"]>; ChevronRightIcon: IconComponent\n}) {\n return (\n <div className=\"flex shrink-0 gap-6\">\n {featured.map((feat) => (\n <a key={feat.id ?? feat.href} href={feat.href} className=\"group w-40\">\n <div className=\"relative mb-2 aspect-[4/5] overflow-hidden rounded-[var(--enad-card-radius)] bg-muted\">\n {feat.image?.src && (\n <img\n src={feat.image.src}\n alt={feat.image.alt ?? feat.label}\n className=\"size-full object-cover transition-transform duration-[var(--enad-image-hover-duration)] group-hover:scale-[var(--enad-image-hover-scale)]\"\n />\n )}\n </div>\n <span className=\"flex items-center gap-1 text-sm text-foreground transition-colors duration-200 group-hover:text-muted-foreground\">\n {feat.label}\n <ChevronRightIcon className=\"mt-px size-4\" />\n </span>\n </a>\n ))}\n </div>\n )\n}\n\ninterface MobileHeaderProps {\n logo: React.ReactNode; linkColor: string; onSearchClick?: () => void\n cartHref?: string; onCartClick?: () => void; menuOpen: boolean\n setMenuOpen: (open: boolean) => void; labels?: HeaderProps[\"labels\"]\n SearchIcon: IconComponent; CartIcon: IconComponent; MenuIcon: IconComponent\n}\n\nfunction MobileHeader({ logo, linkColor, onSearchClick, cartHref, onCartClick, menuOpen, setMenuOpen, labels, SearchIcon, CartIcon, MenuIcon }: MobileHeaderProps) {\n return (\n <div className=\"flex items-center justify-between px-4 py-3 lg:hidden\">\n <a href=\"/\" className=\"shrink-0\">{logo}</a>\n\n <div className=\"flex items-center gap-3\">\n {onSearchClick && (\n <button\n onClick={onSearchClick}\n className={cn(\"relative p-1 transition-colors duration-200 hit-area-2\", linkColor)}\n aria-label={labels?.search ?? \"Search\"}\n >\n <SearchIcon className=\"size-5\" />\n </button>\n )}\n <CartElement\n cartHref={cartHref}\n onCartClick={onCartClick}\n linkColor={linkColor}\n label={labels?.cart ?? \"Cart\"}\n CartIcon={CartIcon}\n />\n <button\n onClick={() => setMenuOpen(true)}\n className={cn(\"relative p-1 transition-colors duration-200 hit-area-2\", linkColor)}\n aria-label={labels?.menu ?? \"Open menu\"}\n aria-expanded={menuOpen}\n >\n <MenuIcon className=\"size-5\" />\n </button>\n </div>\n </div>\n )\n}\n\ninterface DesktopHeaderProps {\n logo: React.ReactNode; navItems: NavItem[]; utilityLinks: NavChild[]\n linkColor: string; navLinkClasses: string; hasBackground: boolean\n activeIndex: number | null; onSearchClick?: () => void\n cartHref?: string; onCartClick?: () => void; labels?: HeaderProps[\"labels\"]\n handleTriggerEnter: (index: number) => void; scheduleClose: () => void\n SearchIcon: IconComponent; CartIcon: IconComponent\n}\n\nfunction DesktopHeader({ logo, navItems, utilityLinks, linkColor, navLinkClasses, hasBackground, activeIndex, onSearchClick, cartHref, onCartClick, labels, handleTriggerEnter, scheduleClose, SearchIcon, CartIcon }: DesktopHeaderProps) {\n return (\n <div\n className={cn(\n \"hidden items-center justify-between px-8 py-4 lg:flex\",\n hasBackground && \"border-b border-border\",\n )}\n >\n <a href=\"/\" className=\"shrink-0\">{logo}</a>\n\n <nav className=\"flex flex-1 items-center justify-center gap-6\">\n {navItems.map((item, index) => {\n const hasDropdown = (item.megaMenuCategories?.length ?? 0) > 0\n return (\n <a\n key={item.id ?? item.label}\n href={item.href ?? \"\"}\n className={navLinkClasses}\n onMouseEnter={() => handleTriggerEnter(index)}\n onMouseLeave={scheduleClose}\n onFocus={() => handleTriggerEnter(index)}\n {...(hasDropdown && {\n \"aria-expanded\": activeIndex === index,\n \"aria-haspopup\": \"true\" as const,\n })}\n >\n {item.label}\n </a>\n )\n })}\n </nav>\n\n <div className=\"flex shrink-0 items-center gap-5\">\n {utilityLinks.map((link) => (\n <a key={link.label} href={link.href} className={navLinkClasses}>\n {link.label}\n </a>\n ))}\n\n {onSearchClick && (\n <button\n onClick={onSearchClick}\n className={cn(\"relative p-1 transition-colors duration-200 hit-area-2\", linkColor)}\n aria-label={labels?.search ?? \"Search\"}\n >\n <SearchIcon className=\"size-5\" />\n </button>\n )}\n\n <CartElement\n cartHref={cartHref}\n onCartClick={onCartClick}\n linkColor={linkColor}\n label={labels?.cart ?? \"Cart\"}\n CartIcon={CartIcon}\n />\n </div>\n </div>\n )\n}\n\nfunction useHeaderState(variant: HeaderProps[\"variant\"], navItems: NavItem[]) {\n const [menuOpen, setMenuOpen] = React.useState(false)\n const [activeIndex, setActiveIndex] = React.useState<number | null>(null)\n const [scrolled, setScrolled] = React.useState(false)\n const closeTimer = React.useRef<ReturnType<typeof setTimeout> | null>(null)\n const headerRef = React.useRef<HTMLElement>(null)\n\n const isTransparent = variant === \"transparent\"\n\n React.useEffect(() => {\n if (!isTransparent) return\n const handleScroll = () => setScrolled(window.scrollY > 50)\n handleScroll()\n window.addEventListener(\"scroll\", handleScroll, { passive: true })\n return () => window.removeEventListener(\"scroll\", handleScroll)\n }, [isTransparent])\n\n const cancelClose = React.useCallback(() => {\n if (closeTimer.current) {\n clearTimeout(closeTimer.current)\n closeTimer.current = null\n }\n }, [])\n\n const scheduleClose = React.useCallback(() => {\n cancelClose()\n closeTimer.current = setTimeout(() => {\n setActiveIndex(null)\n }, CLOSE_DELAY)\n }, [cancelClose])\n\n const handleTriggerEnter = React.useCallback(\n (index: number) => {\n cancelClose()\n const item = navItems[index]\n if ((item?.megaMenuCategories?.length ?? 0) > 0) {\n setActiveIndex(index)\n } else {\n setActiveIndex(null)\n }\n },\n [cancelClose, navItems],\n )\n\n const handlePanelEnter = React.useCallback(() => cancelClose(), [cancelClose])\n const handlePanelLeave = React.useCallback(() => scheduleClose(), [scheduleClose])\n\n React.useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") setActiveIndex(null)\n }\n document.addEventListener(\"keydown\", handleKeyDown)\n return () => document.removeEventListener(\"keydown\", handleKeyDown)\n }, [])\n\n const handleFocusOut = React.useCallback((e: React.FocusEvent) => {\n if (\n headerRef.current &&\n e.relatedTarget instanceof Node &&\n !headerRef.current.contains(e.relatedTarget)\n ) {\n setActiveIndex(null)\n }\n }, [])\n\n const activeItem = activeIndex !== null ? navItems[activeIndex] : null\n const showPanel = activeItem !== null && (activeItem?.megaMenuCategories?.length ?? 0) > 0\n\n return { menuOpen, setMenuOpen, activeIndex, scrolled, headerRef, isTransparent,\n activeItem, showPanel, scheduleClose, handleTriggerEnter, handlePanelEnter, handlePanelLeave, handleFocusOut }\n}\n\nfunction resolveLinkColor(variant: HeaderProps[\"variant\"], isTransparent: boolean, scrolled: boolean): string {\n const isOverlay = variant === \"overlay\" || isTransparent\n if (isOverlay && !(isTransparent && scrolled)) return \"text-white hover:text-white/70\"\n return \"text-foreground hover:text-muted-foreground\"\n}\n\nfunction useMobileItems(navItems: NavItem[]) {\n return React.useMemo(() => navItems.map((item) => ({\n label: item.label, href: item.href ?? \"\",\n children: item.megaMenuCategories?.map((cat) => ({ label: cat.title, href: cat.href })) ?? [],\n })), [navItems])\n}\n\nfunction Header({\n variant = \"default\",\n navItems = EMPTY_NAV,\n utilityLinks = EMPTY_UTILITY,\n promotionEnabled = true,\n promotionMessages = EMPTY_MESSAGES,\n logo,\n onSearchClick,\n onCartClick,\n cartHref,\n labels,\n className,\n}: HeaderProps) {\n const MenuIcon = useIcon(\"menu\")\n const SearchIcon = useIcon(\"search\")\n const CartIcon = useIcon(\"cart\")\n const ChevronRightIcon = useIcon(\"chevronRight\")\n\n const state = useHeaderState(variant, navItems)\n const { isTransparent, scrolled, menuOpen, setMenuOpen, activeIndex } = state\n const { headerRef, activeItem, showPanel, handleFocusOut } = state\n const { scheduleClose, handleTriggerEnter, handlePanelEnter, handlePanelLeave } = state\n\n const hasBackground = variant === \"default\" || (isTransparent && scrolled)\n const linkColor = resolveLinkColor(variant, isTransparent, scrolled)\n const navLinkClasses = `text-sm font-sans tracking-wide transition-colors duration-200 ${linkColor}`\n const mobileItems = useMobileItems(navItems)\n\n return (\n <>\n <header\n ref={headerRef}\n className={cn(\n \"fixed inset-x-0 top-0 z-50 transition-colors duration-200\",\n hasBackground ? \"bg-background\" : \"bg-transparent\",\n className,\n )}\n onBlur={handleFocusOut}\n >\n <PromotionBar enabled={promotionEnabled} messages={promotionMessages} />\n\n <MobileHeader\n logo={logo}\n linkColor={linkColor}\n onSearchClick={onSearchClick}\n cartHref={cartHref}\n onCartClick={onCartClick}\n menuOpen={menuOpen}\n setMenuOpen={setMenuOpen}\n labels={labels}\n SearchIcon={SearchIcon}\n CartIcon={CartIcon}\n MenuIcon={MenuIcon}\n />\n\n <DesktopHeader\n logo={logo}\n navItems={navItems}\n utilityLinks={utilityLinks}\n linkColor={linkColor}\n navLinkClasses={navLinkClasses}\n hasBackground={hasBackground}\n activeIndex={activeIndex}\n onSearchClick={onSearchClick}\n cartHref={cartHref}\n onCartClick={onCartClick}\n labels={labels}\n handleTriggerEnter={handleTriggerEnter}\n scheduleClose={scheduleClose}\n SearchIcon={SearchIcon}\n CartIcon={CartIcon}\n />\n\n <MegaMenuPanel\n activeItem={activeItem ?? null}\n showPanel={showPanel}\n onMouseEnter={handlePanelEnter}\n onMouseLeave={handlePanelLeave}\n ChevronRightIcon={ChevronRightIcon}\n />\n </header>\n\n <MobileMenuDrawer\n open={menuOpen}\n onOpenChange={setMenuOpen}\n items={mobileItems}\n labels={labels ? { close: labels.closeMenu, back: undefined } : undefined}\n />\n </>\n )\n}\n\nexport { Header, type HeaderProps }\n"],"mappings":"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"}
1
+ {"version":3,"file":"header.mjs","names":["React"],"sources":["../../../../src/client/storefront/layout/header.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { useIcon } from \"../../icons/icon-context\";\nimport { cn } from \"../../ui/utils\";\nimport { MobileMenuDrawer } from \"./mobile-menu-drawer\";\nimport { PromotionBar } from \"./promotion-bar\";\nimport type { HeaderProps, NavItem, NavChild } from \"../types\";\n\ntype IconComponent = React.ComponentType<{ className?: string }>;\nconst CLOSE_DELAY = 150;\nconst EMPTY_NAV: NavItem[] = [];\nconst EMPTY_UTILITY: NavChild[] = [];\nconst EMPTY_MESSAGES: string[] = [];\n\nfunction CartElement({\n cartHref,\n onCartClick,\n linkColor,\n label,\n CartIcon,\n}: {\n cartHref?: string;\n onCartClick?: () => void;\n linkColor: string;\n label: string;\n CartIcon: IconComponent;\n}) {\n if (cartHref) {\n return (\n <a\n href={cartHref}\n className={cn(\"relative p-1 transition-colors duration-200 hit-area-2\", linkColor)}\n aria-label={label}\n >\n <CartIcon className=\"size-5\" />\n </a>\n );\n }\n if (onCartClick) {\n return (\n <button\n onClick={onCartClick}\n className={cn(\"relative p-1 transition-colors duration-200 hit-area-2\", linkColor)}\n aria-label={label}\n >\n <CartIcon className=\"size-5\" />\n </button>\n );\n }\n return null;\n}\n\nfunction MegaMenuPanel({\n activeItem,\n showPanel,\n onMouseEnter,\n onMouseLeave,\n ChevronRightIcon,\n}: {\n activeItem: NavItem | null;\n showPanel: boolean;\n onMouseEnter: () => void;\n onMouseLeave: () => void;\n ChevronRightIcon: IconComponent;\n}) {\n const categories = activeItem?.megaMenuCategories;\n const featured = activeItem?.megaMenuFeatured;\n\n return (\n <div\n className={cn(\n \"hidden overflow-hidden transition-all duration-300 ease-out lg:block\",\n showPanel ? \"max-h-125 translate-y-0 opacity-100\" : \"max-h-0 -translate-y-1 opacity-0\",\n )}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n role=\"region\"\n aria-label=\"Submenu\"\n >\n <div className=\"border-b border-border bg-background p-8\">\n {categories && categories.length > 0 ? (\n <div className=\"flex gap-12\">\n <MegaMenuCategories categories={categories} />\n {featured && featured.length > 0 && (\n <MegaMenuFeatured featured={featured} ChevronRightIcon={ChevronRightIcon} />\n )}\n </div>\n ) : null}\n </div>\n </div>\n );\n}\n\nfunction MegaMenuCategories({\n categories,\n}: {\n categories: NonNullable<NavItem[\"megaMenuCategories\"]>;\n}) {\n const columnGroups: (typeof categories)[] = [];\n for (const cat of categories) {\n if (cat.stackWithPrevious && columnGroups.length > 0) {\n columnGroups[columnGroups.length - 1]!.push(cat);\n } else {\n columnGroups.push([cat]);\n }\n }\n\n return (\n <div className=\"flex flex-1 justify-between gap-10\">\n {columnGroups.map((group) => (\n <div key={group[0]!.id ?? group[0]!.title} className=\"flex w-50 min-w-0 flex-col gap-6\">\n {group.map((cat) => (\n <div key={cat.id ?? cat.title}>\n <a\n href={cat.href}\n className=\"mb-3 block text-sm font-normal text-foreground transition-colors duration-200 hover:text-muted-foreground\"\n >\n {cat.title}\n </a>\n <ul className=\"flex flex-col gap-1.5\">\n {cat.items?.map((item) => (\n <li key={item.href}>\n <a\n href={item.href}\n className=\"text-sm text-muted-foreground transition-colors duration-200 hover:text-foreground\"\n >\n {item.label}\n </a>\n </li>\n ))}\n </ul>\n </div>\n ))}\n </div>\n ))}\n </div>\n );\n}\n\nfunction MegaMenuFeatured({\n featured,\n ChevronRightIcon,\n}: {\n featured: NonNullable<NavItem[\"megaMenuFeatured\"]>;\n ChevronRightIcon: IconComponent;\n}) {\n return (\n <div className=\"flex shrink-0 gap-6\">\n {featured.map((feat) => (\n <a key={feat.id ?? feat.href} href={feat.href} className=\"group w-40\">\n <div className=\"relative mb-2 aspect-[4/5] overflow-hidden rounded-[var(--enad-card-radius)] bg-muted\">\n {feat.image?.src && (\n <img\n src={feat.image.src}\n alt={feat.image.alt ?? feat.label}\n className=\"size-full object-cover\"\n />\n )}\n </div>\n <span className=\"flex items-center gap-1 text-sm text-foreground transition-colors duration-200 group-hover:text-muted-foreground\">\n {feat.label}\n <ChevronRightIcon className=\"mt-px size-4\" />\n </span>\n </a>\n ))}\n </div>\n );\n}\n\ninterface MobileHeaderProps {\n logo: React.ReactNode;\n linkColor: string;\n onSearchClick?: () => void;\n cartHref?: string;\n onCartClick?: () => void;\n menuOpen: boolean;\n setMenuOpen: (open: boolean) => void;\n labels?: HeaderProps[\"labels\"];\n SearchIcon: IconComponent;\n CartIcon: IconComponent;\n MenuIcon: IconComponent;\n}\n\nfunction MobileHeader({\n logo,\n linkColor,\n onSearchClick,\n cartHref,\n onCartClick,\n menuOpen,\n setMenuOpen,\n labels,\n SearchIcon,\n CartIcon,\n MenuIcon,\n}: MobileHeaderProps) {\n return (\n <div className=\"flex items-center justify-between px-4 py-3 lg:hidden\">\n <a href=\"/\" className=\"shrink-0\">\n {logo}\n </a>\n\n <div className=\"flex items-center gap-3\">\n {onSearchClick && (\n <button\n onClick={onSearchClick}\n className={cn(\"relative p-1 transition-colors duration-200 hit-area-2\", linkColor)}\n aria-label={labels?.search ?? \"Search\"}\n >\n <SearchIcon className=\"size-5\" />\n </button>\n )}\n <CartElement\n cartHref={cartHref}\n onCartClick={onCartClick}\n linkColor={linkColor}\n label={labels?.cart ?? \"Cart\"}\n CartIcon={CartIcon}\n />\n <button\n onClick={() => setMenuOpen(true)}\n className={cn(\"relative p-1 transition-colors duration-200 hit-area-2\", linkColor)}\n aria-label={labels?.menu ?? \"Open menu\"}\n aria-expanded={menuOpen}\n >\n <MenuIcon className=\"size-5\" />\n </button>\n </div>\n </div>\n );\n}\n\ninterface DesktopHeaderProps {\n logo: React.ReactNode;\n navItems: NavItem[];\n utilityLinks: NavChild[];\n linkColor: string;\n navLinkClasses: string;\n hasBackground: boolean;\n activeIndex: number | null;\n onSearchClick?: () => void;\n cartHref?: string;\n onCartClick?: () => void;\n labels?: HeaderProps[\"labels\"];\n handleTriggerEnter: (index: number) => void;\n scheduleClose: () => void;\n SearchIcon: IconComponent;\n CartIcon: IconComponent;\n}\n\nfunction DesktopHeader({\n logo,\n navItems,\n utilityLinks,\n linkColor,\n navLinkClasses,\n hasBackground,\n activeIndex,\n onSearchClick,\n cartHref,\n onCartClick,\n labels,\n handleTriggerEnter,\n scheduleClose,\n SearchIcon,\n CartIcon,\n}: DesktopHeaderProps) {\n return (\n <div\n className={cn(\n \"hidden items-center justify-between px-8 py-4 lg:flex\",\n hasBackground && \"border-b border-border\",\n )}\n >\n <a href=\"/\" className=\"shrink-0\">\n {logo}\n </a>\n\n <nav className=\"flex flex-1 items-center justify-center gap-6\">\n {navItems.map((item, index) => {\n const hasDropdown = (item.megaMenuCategories?.length ?? 0) > 0;\n return (\n <a\n key={item.id ?? item.label}\n href={item.href ?? \"\"}\n className={navLinkClasses}\n onMouseEnter={() => handleTriggerEnter(index)}\n onMouseLeave={scheduleClose}\n onFocus={() => handleTriggerEnter(index)}\n {...(hasDropdown && {\n \"aria-expanded\": activeIndex === index,\n \"aria-haspopup\": \"true\" as const,\n })}\n >\n {item.label}\n </a>\n );\n })}\n </nav>\n\n <div className=\"flex shrink-0 items-center gap-5\">\n {utilityLinks.map((link) => (\n <a key={link.label} href={link.href} className={navLinkClasses}>\n {link.label}\n </a>\n ))}\n\n {onSearchClick && (\n <button\n onClick={onSearchClick}\n className={cn(\"relative p-1 transition-colors duration-200 hit-area-2\", linkColor)}\n aria-label={labels?.search ?? \"Search\"}\n >\n <SearchIcon className=\"size-5\" />\n </button>\n )}\n\n <CartElement\n cartHref={cartHref}\n onCartClick={onCartClick}\n linkColor={linkColor}\n label={labels?.cart ?? \"Cart\"}\n CartIcon={CartIcon}\n />\n </div>\n </div>\n );\n}\n\nfunction useHeaderState(variant: HeaderProps[\"variant\"], navItems: NavItem[]) {\n const [menuOpen, setMenuOpen] = React.useState(false);\n const [activeIndex, setActiveIndex] = React.useState<number | null>(null);\n const [scrolled, setScrolled] = React.useState(false);\n const closeTimer = React.useRef<ReturnType<typeof setTimeout> | null>(null);\n const headerRef = React.useRef<HTMLElement>(null);\n\n const isTransparent = variant === \"transparent\";\n\n React.useEffect(() => {\n if (!isTransparent) return;\n const handleScroll = () => setScrolled(window.scrollY > 50);\n handleScroll();\n window.addEventListener(\"scroll\", handleScroll, { passive: true });\n return () => window.removeEventListener(\"scroll\", handleScroll);\n }, [isTransparent]);\n\n const cancelClose = React.useCallback(() => {\n if (closeTimer.current) {\n clearTimeout(closeTimer.current);\n closeTimer.current = null;\n }\n }, []);\n\n const scheduleClose = React.useCallback(() => {\n cancelClose();\n closeTimer.current = setTimeout(() => {\n setActiveIndex(null);\n }, CLOSE_DELAY);\n }, [cancelClose]);\n\n const handleTriggerEnter = React.useCallback(\n (index: number) => {\n cancelClose();\n const item = navItems[index];\n if ((item?.megaMenuCategories?.length ?? 0) > 0) {\n setActiveIndex(index);\n } else {\n setActiveIndex(null);\n }\n },\n [cancelClose, navItems],\n );\n\n const handlePanelEnter = React.useCallback(() => cancelClose(), [cancelClose]);\n const handlePanelLeave = React.useCallback(() => scheduleClose(), [scheduleClose]);\n\n React.useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") setActiveIndex(null);\n };\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => document.removeEventListener(\"keydown\", handleKeyDown);\n }, []);\n\n const handleFocusOut = React.useCallback((e: React.FocusEvent) => {\n if (\n headerRef.current &&\n e.relatedTarget instanceof Node &&\n !headerRef.current.contains(e.relatedTarget)\n ) {\n setActiveIndex(null);\n }\n }, []);\n\n const activeItem = activeIndex !== null ? navItems[activeIndex] : null;\n const showPanel = activeItem !== null && (activeItem?.megaMenuCategories?.length ?? 0) > 0;\n\n return {\n menuOpen,\n setMenuOpen,\n activeIndex,\n scrolled,\n headerRef,\n isTransparent,\n activeItem,\n showPanel,\n scheduleClose,\n handleTriggerEnter,\n handlePanelEnter,\n handlePanelLeave,\n handleFocusOut,\n };\n}\n\nfunction resolveLinkColor(\n variant: HeaderProps[\"variant\"],\n isTransparent: boolean,\n scrolled: boolean,\n): string {\n const isOverlay = variant === \"overlay\" || isTransparent;\n if (isOverlay && !(isTransparent && scrolled)) return \"text-white hover:text-white/70\";\n return \"text-foreground hover:text-muted-foreground\";\n}\n\nfunction useMobileItems(navItems: NavItem[]) {\n return React.useMemo(\n () =>\n navItems.map((item) => ({\n label: item.label,\n href: item.href ?? \"\",\n children:\n item.megaMenuCategories?.map((cat) => ({ label: cat.title, href: cat.href })) ?? [],\n })),\n [navItems],\n );\n}\n\nfunction Header({\n variant = \"default\",\n navItems = EMPTY_NAV,\n utilityLinks = EMPTY_UTILITY,\n promotionEnabled = true,\n promotionMessages = EMPTY_MESSAGES,\n logo,\n onSearchClick,\n onCartClick,\n cartHref,\n labels,\n className,\n}: HeaderProps) {\n const MenuIcon = useIcon(\"menu\");\n const SearchIcon = useIcon(\"search\");\n const CartIcon = useIcon(\"cart\");\n const ChevronRightIcon = useIcon(\"chevronRight\");\n\n const state = useHeaderState(variant, navItems);\n const { isTransparent, scrolled, menuOpen, setMenuOpen, activeIndex } = state;\n const { headerRef, activeItem, showPanel, handleFocusOut } = state;\n const { scheduleClose, handleTriggerEnter, handlePanelEnter, handlePanelLeave } = state;\n\n const hasBackground = variant === \"default\" || (isTransparent && scrolled);\n const linkColor = resolveLinkColor(variant, isTransparent, scrolled);\n const navLinkClasses = `text-sm font-sans tracking-wide transition-colors duration-200 ${linkColor}`;\n const mobileItems = useMobileItems(navItems);\n\n return (\n <>\n <header\n ref={headerRef}\n className={cn(\n \"fixed inset-x-0 top-0 z-50 transition-colors duration-200\",\n hasBackground ? \"bg-background\" : \"bg-transparent\",\n className,\n )}\n onBlur={handleFocusOut}\n >\n <PromotionBar enabled={promotionEnabled} messages={promotionMessages} />\n\n <MobileHeader\n logo={logo}\n linkColor={linkColor}\n onSearchClick={onSearchClick}\n cartHref={cartHref}\n onCartClick={onCartClick}\n menuOpen={menuOpen}\n setMenuOpen={setMenuOpen}\n labels={labels}\n SearchIcon={SearchIcon}\n CartIcon={CartIcon}\n MenuIcon={MenuIcon}\n />\n\n <DesktopHeader\n logo={logo}\n navItems={navItems}\n utilityLinks={utilityLinks}\n linkColor={linkColor}\n navLinkClasses={navLinkClasses}\n hasBackground={hasBackground}\n activeIndex={activeIndex}\n onSearchClick={onSearchClick}\n cartHref={cartHref}\n onCartClick={onCartClick}\n labels={labels}\n handleTriggerEnter={handleTriggerEnter}\n scheduleClose={scheduleClose}\n SearchIcon={SearchIcon}\n CartIcon={CartIcon}\n />\n\n <MegaMenuPanel\n activeItem={activeItem ?? null}\n showPanel={showPanel}\n onMouseEnter={handlePanelEnter}\n onMouseLeave={handlePanelLeave}\n ChevronRightIcon={ChevronRightIcon}\n />\n </header>\n\n <MobileMenuDrawer\n open={menuOpen}\n onOpenChange={setMenuOpen}\n items={mobileItems}\n labels={labels ? { close: labels.closeMenu, back: undefined } : undefined}\n />\n </>\n );\n}\n\nexport { Header, type HeaderProps };\n"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { useIcon } from "../../icons/icon-context.mjs";
3
3
  import { cn } from "../../ui/utils.mjs";
4
- import { Separator } from "../../ui-resolver/separator.mjs";
5
4
  import { Sheet, SheetContent, SheetTitle } from "../../ui-resolver/sheet.mjs";
5
+ import { Separator } from "../../ui-resolver/separator.mjs";
6
6
  import * as React$1 from "react";
7
7
  import { jsx, jsxs } from "react/jsx-runtime";
8
8
  //#region src/client/storefront/layout/mobile-menu-drawer.tsx
@@ -1 +1 @@
1
- {"version":3,"file":"promotion-bar.d.ts","names":[],"sources":["../../../../src/client/storefront/layout/promotion-bar.tsx"],"mappings":";;;;iBAQS,YAAA,CAAA;EACP,OAAA;EACA,QAAA;EACA,SAAA;EACA;AAAA,GACC,iBAAA,GAAiB,kBAAA,CAAA,GAAA,CAAA,OAAA"}
1
+ {"version":3,"file":"promotion-bar.d.ts","names":[],"sources":["../../../../src/client/storefront/layout/promotion-bar.tsx"],"mappings":";;;;iBAQS,YAAA,CAAA;EAAe,OAAA;EAAgB,QAAA;EAAe,SAAA;EAAW;AAAA,GAAa,iBAAA,GAAiB,kBAAA,CAAA,GAAA,CAAA,OAAA"}
@@ -27,7 +27,7 @@ function PromotionBar({ enabled = true, messages = [], onDismiss, className }) {
27
27
  className: cn("relative flex items-center justify-center bg-accent text-accent-foreground px-4 py-2 text-center text-sm", className),
28
28
  children: [
29
29
  hasMultiple && /* @__PURE__ */ jsx(Button, {
30
- variant: "ghost",
30
+ variant: "plain",
31
31
  size: "icon",
32
32
  className: "absolute left-1 size-7 text-accent-foreground hover:bg-accent-foreground/10",
33
33
  onClick: prev,
@@ -39,7 +39,7 @@ function PromotionBar({ enabled = true, messages = [], onDismiss, className }) {
39
39
  children: messages[currentIndex]
40
40
  }),
41
41
  hasMultiple && /* @__PURE__ */ jsx(Button, {
42
- variant: "ghost",
42
+ variant: "plain",
43
43
  size: "icon",
44
44
  className: "absolute right-8 size-7 text-accent-foreground hover:bg-accent-foreground/10",
45
45
  onClick: next,
@@ -47,7 +47,7 @@ function PromotionBar({ enabled = true, messages = [], onDismiss, className }) {
47
47
  children: /* @__PURE__ */ jsx(ChevronRightIcon, { className: "size-4" })
48
48
  }),
49
49
  onDismiss && /* @__PURE__ */ jsx(Button, {
50
- variant: "ghost",
50
+ variant: "plain",
51
51
  size: "icon",
52
52
  className: "absolute right-1 size-7 text-accent-foreground hover:bg-accent-foreground/10",
53
53
  onClick: handleDismiss,
@@ -1 +1 @@
1
- {"version":3,"file":"promotion-bar.mjs","names":["React"],"sources":["../../../../src/client/storefront/layout/promotion-bar.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { useIcon } from \"../../icons/icon-context\"\nimport { Button } from \"../../ui/button\"\nimport { cn } from \"../../ui/utils\"\nimport type { PromotionBarProps } from \"../types\"\n\nfunction PromotionBar({\n enabled = true,\n messages = [],\n onDismiss,\n className,\n}: PromotionBarProps) {\n const ChevronLeftIcon = useIcon(\"chevronLeft\")\n const ChevronRightIcon = useIcon(\"chevronRight\")\n const CloseIcon = useIcon(\"close\")\n\n const [currentIndex, setCurrentIndex] = React.useState(0)\n const [dismissed, setDismissed] = React.useState(false)\n\n if (!enabled || dismissed || messages.length === 0) return null\n\n const hasMultiple = messages.length > 1\n\n const prev = () => {\n setCurrentIndex((i) => (i - 1 + messages.length) % messages.length)\n }\n\n const next = () => {\n setCurrentIndex((i) => (i + 1) % messages.length)\n }\n\n const handleDismiss = () => {\n setDismissed(true)\n onDismiss?.()\n }\n\n return (\n <div\n className={cn(\n \"relative flex items-center justify-center bg-accent text-accent-foreground px-4 py-2 text-center text-sm\",\n className,\n )}\n >\n {hasMultiple && (\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"absolute left-1 size-7 text-accent-foreground hover:bg-accent-foreground/10\"\n onClick={prev}\n aria-label=\"Previous message\"\n >\n <ChevronLeftIcon className=\"size-4\" />\n </Button>\n )}\n\n <span className=\"px-8\">{messages[currentIndex]}</span>\n\n {hasMultiple && (\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"absolute right-8 size-7 text-accent-foreground hover:bg-accent-foreground/10\"\n onClick={next}\n aria-label=\"Next message\"\n >\n <ChevronRightIcon className=\"size-4\" />\n </Button>\n )}\n\n {onDismiss && (\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className=\"absolute right-1 size-7 text-accent-foreground hover:bg-accent-foreground/10\"\n onClick={handleDismiss}\n aria-label=\"Dismiss\"\n >\n <CloseIcon className=\"size-4\" />\n </Button>\n )}\n </div>\n )\n}\n\nexport { PromotionBar, type PromotionBarProps }\n"],"mappings":";;;;;;;AAQA,SAAS,aAAa,EACpB,UAAU,MACV,WAAW,EAAE,EACb,WACA,aACoB;CACpB,MAAM,kBAAkB,QAAQ,cAAc;CAC9C,MAAM,mBAAmB,QAAQ,eAAe;CAChD,MAAM,YAAY,QAAQ,QAAQ;CAElC,MAAM,CAAC,cAAc,mBAAmBA,QAAM,SAAS,EAAE;CACzD,MAAM,CAAC,WAAW,gBAAgBA,QAAM,SAAS,MAAM;AAEvD,KAAI,CAAC,WAAW,aAAa,SAAS,WAAW,EAAG,QAAO;CAE3D,MAAM,cAAc,SAAS,SAAS;CAEtC,MAAM,aAAa;AACjB,mBAAiB,OAAO,IAAI,IAAI,SAAS,UAAU,SAAS,OAAO;;CAGrE,MAAM,aAAa;AACjB,mBAAiB,OAAO,IAAI,KAAK,SAAS,OAAO;;CAGnD,MAAM,sBAAsB;AAC1B,eAAa,KAAK;AAClB,eAAa;;AAGf,QACE,qBAAC,OAAD;EACE,WAAW,GACT,4GACA,UACD;YAJH;GAMG,eACC,oBAAC,QAAD;IACE,SAAQ;IACR,MAAK;IACL,WAAU;IACV,SAAS;IACT,cAAW;cAEX,oBAAC,iBAAD,EAAiB,WAAU,UAAW,CAAA;IAC/B,CAAA;GAGX,oBAAC,QAAD;IAAM,WAAU;cAAQ,SAAS;IAAqB,CAAA;GAErD,eACC,oBAAC,QAAD;IACE,SAAQ;IACR,MAAK;IACL,WAAU;IACV,SAAS;IACT,cAAW;cAEX,oBAAC,kBAAD,EAAkB,WAAU,UAAW,CAAA;IAChC,CAAA;GAGV,aACC,oBAAC,QAAD;IACE,SAAQ;IACR,MAAK;IACL,WAAU;IACV,SAAS;IACT,cAAW;cAEX,oBAAC,WAAD,EAAW,WAAU,UAAW,CAAA;IACzB,CAAA;GAEP"}
1
+ {"version":3,"file":"promotion-bar.mjs","names":["React"],"sources":["../../../../src/client/storefront/layout/promotion-bar.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { useIcon } from \"../../icons/icon-context\";\nimport { Button } from \"../../ui/button\";\nimport { cn } from \"../../ui/utils\";\nimport type { PromotionBarProps } from \"../types\";\n\nfunction PromotionBar({ enabled = true, messages = [], onDismiss, className }: PromotionBarProps) {\n const ChevronLeftIcon = useIcon(\"chevronLeft\");\n const ChevronRightIcon = useIcon(\"chevronRight\");\n const CloseIcon = useIcon(\"close\");\n\n const [currentIndex, setCurrentIndex] = React.useState(0);\n const [dismissed, setDismissed] = React.useState(false);\n\n if (!enabled || dismissed || messages.length === 0) return null;\n\n const hasMultiple = messages.length > 1;\n\n const prev = () => {\n setCurrentIndex((i) => (i - 1 + messages.length) % messages.length);\n };\n\n const next = () => {\n setCurrentIndex((i) => (i + 1) % messages.length);\n };\n\n const handleDismiss = () => {\n setDismissed(true);\n onDismiss?.();\n };\n\n return (\n <div\n className={cn(\n \"relative flex items-center justify-center bg-accent text-accent-foreground px-4 py-2 text-center text-sm\",\n className,\n )}\n >\n {hasMultiple && (\n <Button\n variant=\"plain\"\n size=\"icon\"\n className=\"absolute left-1 size-7 text-accent-foreground hover:bg-accent-foreground/10\"\n onClick={prev}\n aria-label=\"Previous message\"\n >\n <ChevronLeftIcon className=\"size-4\" />\n </Button>\n )}\n\n <span className=\"px-8\">{messages[currentIndex]}</span>\n\n {hasMultiple && (\n <Button\n variant=\"plain\"\n size=\"icon\"\n className=\"absolute right-8 size-7 text-accent-foreground hover:bg-accent-foreground/10\"\n onClick={next}\n aria-label=\"Next message\"\n >\n <ChevronRightIcon className=\"size-4\" />\n </Button>\n )}\n\n {onDismiss && (\n <Button\n variant=\"plain\"\n size=\"icon\"\n className=\"absolute right-1 size-7 text-accent-foreground hover:bg-accent-foreground/10\"\n onClick={handleDismiss}\n aria-label=\"Dismiss\"\n >\n <CloseIcon className=\"size-4\" />\n </Button>\n )}\n </div>\n );\n}\n\nexport { PromotionBar, type PromotionBarProps };\n"],"mappings":";;;;;;;AAQA,SAAS,aAAa,EAAE,UAAU,MAAM,WAAW,EAAE,EAAE,WAAW,aAAgC;CAChG,MAAM,kBAAkB,QAAQ,cAAc;CAC9C,MAAM,mBAAmB,QAAQ,eAAe;CAChD,MAAM,YAAY,QAAQ,QAAQ;CAElC,MAAM,CAAC,cAAc,mBAAmBA,QAAM,SAAS,EAAE;CACzD,MAAM,CAAC,WAAW,gBAAgBA,QAAM,SAAS,MAAM;AAEvD,KAAI,CAAC,WAAW,aAAa,SAAS,WAAW,EAAG,QAAO;CAE3D,MAAM,cAAc,SAAS,SAAS;CAEtC,MAAM,aAAa;AACjB,mBAAiB,OAAO,IAAI,IAAI,SAAS,UAAU,SAAS,OAAO;;CAGrE,MAAM,aAAa;AACjB,mBAAiB,OAAO,IAAI,KAAK,SAAS,OAAO;;CAGnD,MAAM,sBAAsB;AAC1B,eAAa,KAAK;AAClB,eAAa;;AAGf,QACE,qBAAC,OAAD;EACE,WAAW,GACT,4GACA,UACD;YAJH;GAMG,eACC,oBAAC,QAAD;IACE,SAAQ;IACR,MAAK;IACL,WAAU;IACV,SAAS;IACT,cAAW;cAEX,oBAAC,iBAAD,EAAiB,WAAU,UAAW,CAAA;IAC/B,CAAA;GAGX,oBAAC,QAAD;IAAM,WAAU;cAAQ,SAAS;IAAqB,CAAA;GAErD,eACC,oBAAC,QAAD;IACE,SAAQ;IACR,MAAK;IACL,WAAU;IACV,SAAS;IACT,cAAW;cAEX,oBAAC,kBAAD,EAAkB,WAAU,UAAW,CAAA;IAChC,CAAA;GAGV,aACC,oBAAC,QAAD;IACE,SAAQ;IACR,MAAK;IACL,WAAU;IACV,SAAS;IACT,cAAW;cAEX,oBAAC,WAAD,EAAW,WAAU,UAAW,CAAA;IACzB,CAAA;GAEP"}
@@ -0,0 +1,40 @@
1
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
2
+
3
+ //#region src/client/storefront/primitives/block-heading.d.ts
4
+ interface BlockHeadingProps {
5
+ preheader?: string;
6
+ heading?: string;
7
+ body?: string;
8
+ dark?: boolean;
9
+ align?: "left" | "center" | "right";
10
+ headingClamp?: string;
11
+ headingTag?: "h1" | "h2" | "h3";
12
+ headingSize?: "sm" | "md" | "lg" | "xl";
13
+ /** Override classes for preheader color */
14
+ preheaderColor?: string;
15
+ /** Override classes for heading color */
16
+ headingColor?: string;
17
+ /** Override classes for body color */
18
+ bodyColor?: string;
19
+ /** Max width constraint for body text */
20
+ bodyMaxWidth?: string;
21
+ className?: string;
22
+ }
23
+ declare function BlockHeading({
24
+ preheader,
25
+ heading,
26
+ body,
27
+ dark,
28
+ align,
29
+ headingClamp,
30
+ headingTag: Tag,
31
+ headingSize,
32
+ preheaderColor,
33
+ headingColor,
34
+ bodyColor,
35
+ bodyMaxWidth,
36
+ className
37
+ }: BlockHeadingProps): react_jsx_runtime0.JSX.Element;
38
+ //#endregion
39
+ export { BlockHeading, type BlockHeadingProps };
40
+ //# sourceMappingURL=block-heading.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-heading.d.ts","names":[],"sources":["../../../../src/client/storefront/primitives/block-heading.tsx"],"mappings":";;;UAWU,iBAAA;EACR,SAAA;EACA,OAAA;EACA,IAAA;EACA,IAAA;EACA,KAAA;EACA,YAAA;EACA,UAAA;EACA,WAAA;EALA;EAOA,cAAA;EALA;EAOA,YAAA;EALA;EAOA,SAAA;EAJA;EAMA,YAAA;EACA,SAAA;AAAA;AAAA,iBAUO,YAAA,CAAA;EACP,SAAA;EACA,OAAA;EACA,IAAA;EACA,IAAA;EACA,KAAA;EACA,YAAA;EACA,UAAA,EAAY,GAAA;EACZ,WAAA;EACA,cAAA;EACA,YAAA;EACA,SAAA;EACA,YAAA;EACA;AAAA,GACC,iBAAA,GAAiB,kBAAA,CAAA,GAAA,CAAA,OAAA"}