@enadhq/enad-react-sdk 1.2.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 (85) hide show
  1. package/dist/client/storefront/blocks/card-video.mjs +1 -1
  2. package/dist/client/storefront/blocks/card-video.mjs.map +1 -1
  3. package/dist/client/storefront/blocks/gallery-with-link-blocks.d.ts.map +1 -1
  4. package/dist/client/storefront/blocks/gallery-with-link-blocks.mjs +13 -5
  5. package/dist/client/storefront/blocks/gallery-with-link-blocks.mjs.map +1 -1
  6. package/dist/client/storefront/blocks/gallery.d.ts +10 -1
  7. package/dist/client/storefront/blocks/gallery.d.ts.map +1 -1
  8. package/dist/client/storefront/blocks/gallery.mjs +51 -27
  9. package/dist/client/storefront/blocks/gallery.mjs.map +1 -1
  10. package/dist/client/storefront/blocks/hero.d.ts +12 -1
  11. package/dist/client/storefront/blocks/hero.d.ts.map +1 -1
  12. package/dist/client/storefront/blocks/hero.mjs +143 -145
  13. package/dist/client/storefront/blocks/hero.mjs.map +1 -1
  14. package/dist/client/storefront/blocks/link-block-small.d.ts.map +1 -1
  15. package/dist/client/storefront/blocks/link-block-small.mjs +1 -1
  16. package/dist/client/storefront/blocks/link-block-small.mjs.map +1 -1
  17. package/dist/client/storefront/blocks/link-block.d.ts.map +1 -1
  18. package/dist/client/storefront/blocks/link-block.mjs +4 -4
  19. package/dist/client/storefront/blocks/link-block.mjs.map +1 -1
  20. package/dist/client/storefront/blocks/product-card-parts.d.ts +1 -1
  21. package/dist/client/storefront/blocks/product-card-parts.d.ts.map +1 -1
  22. package/dist/client/storefront/blocks/product-card-parts.mjs +2 -2
  23. package/dist/client/storefront/blocks/product-card-parts.mjs.map +1 -1
  24. package/dist/client/storefront/blocks/product-card.d.ts +10 -1
  25. package/dist/client/storefront/blocks/product-card.d.ts.map +1 -1
  26. package/dist/client/storefront/blocks/product-card.mjs +122 -116
  27. package/dist/client/storefront/blocks/product-card.mjs.map +1 -1
  28. package/dist/client/storefront/blocks/product-image.mjs +2 -2
  29. package/dist/client/storefront/blocks/product-image.mjs.map +1 -1
  30. package/dist/client/storefront/blocks/text-content-with-image.d.ts +14 -1
  31. package/dist/client/storefront/blocks/text-content-with-image.d.ts.map +1 -1
  32. package/dist/client/storefront/blocks/text-content-with-image.mjs +141 -164
  33. package/dist/client/storefront/blocks/text-content-with-image.mjs.map +1 -1
  34. package/dist/client/storefront/carousel/swipeable-carousel.d.ts +5 -1
  35. package/dist/client/storefront/carousel/swipeable-carousel.d.ts.map +1 -1
  36. package/dist/client/storefront/carousel/swipeable-carousel.mjs +2 -1
  37. package/dist/client/storefront/carousel/swipeable-carousel.mjs.map +1 -1
  38. package/dist/client/storefront/components/product-recommendations.d.ts.map +1 -1
  39. package/dist/client/storefront/components/product-recommendations.mjs +29 -37
  40. package/dist/client/storefront/components/product-recommendations.mjs.map +1 -1
  41. package/dist/client/storefront/filters/filter-chip.d.ts +5 -2
  42. package/dist/client/storefront/filters/filter-chip.d.ts.map +1 -1
  43. package/dist/client/storefront/filters/filter-chip.mjs +5 -3
  44. package/dist/client/storefront/filters/filter-chip.mjs.map +1 -1
  45. package/dist/client/storefront/filters/filter-panel.mjs +1 -1
  46. package/dist/client/storefront/index.d.ts +12 -1
  47. package/dist/client/storefront/index.mjs +12 -1
  48. package/dist/client/storefront/layout/header.d.ts.map +1 -1
  49. package/dist/client/storefront/layout/header.mjs +1 -1
  50. package/dist/client/storefront/layout/header.mjs.map +1 -1
  51. package/dist/client/storefront/layout/mobile-menu-drawer.mjs +1 -1
  52. package/dist/client/storefront/primitives/block-heading.d.ts +40 -0
  53. package/dist/client/storefront/primitives/block-heading.d.ts.map +1 -0
  54. package/dist/client/storefront/primitives/block-heading.mjs +43 -0
  55. package/dist/client/storefront/primitives/block-heading.mjs.map +1 -0
  56. package/dist/client/storefront/primitives/cta-group.d.ts +25 -0
  57. package/dist/client/storefront/primitives/cta-group.d.ts.map +1 -0
  58. package/dist/client/storefront/primitives/cta-group.mjs +27 -0
  59. package/dist/client/storefront/primitives/cta-group.mjs.map +1 -0
  60. package/dist/client/storefront/primitives/image-with-hover.d.ts +18 -0
  61. package/dist/client/storefront/primitives/image-with-hover.d.ts.map +1 -0
  62. package/dist/client/storefront/primitives/image-with-hover.mjs +16 -0
  63. package/dist/client/storefront/primitives/image-with-hover.mjs.map +1 -0
  64. package/dist/client/storefront/primitives/index.d.ts +4 -1
  65. package/dist/client/storefront/primitives/index.mjs +4 -1
  66. package/dist/client/theme/apply.d.ts +1 -1
  67. package/dist/client/theme/apply.d.ts.map +1 -1
  68. package/dist/client/theme/apply.mjs +0 -12
  69. package/dist/client/theme/apply.mjs.map +1 -1
  70. package/dist/client/theme/cli.mjs +0 -16
  71. package/dist/client/theme/cli.mjs.map +1 -1
  72. package/dist/client/theme/codec.d.ts.map +1 -1
  73. package/dist/client/theme/codec.mjs +0 -2
  74. package/dist/client/theme/codec.mjs.map +1 -1
  75. package/dist/client/theme/defaults.d.ts +0 -2
  76. package/dist/client/theme/defaults.mjs +0 -2
  77. package/dist/client/theme/defaults.mjs.map +1 -1
  78. package/dist/client/ui/carousel.d.ts +9 -1
  79. package/dist/client/ui/carousel.d.ts.map +1 -1
  80. package/dist/client/ui/carousel.mjs +18 -2
  81. package/dist/client/ui/carousel.mjs.map +1 -1
  82. package/dist/client/ui-resolver/index.d.ts +2 -2
  83. package/dist/client/ui-resolver/index.mjs +2 -2
  84. package/dist/styles.css +1 -1
  85. package/package.json +3 -2
@@ -1,108 +1,111 @@
1
1
  "use client";
2
2
  import { cn } from "../../ui/utils.mjs";
3
- import { StorefrontButton } from "../primitives/button.mjs";
4
- import { StorefrontTextLink } from "../primitives/text-link.mjs";
3
+ import { defineSlotRecipe } from "../../ui-resolver/recipe.mjs";
4
+ import { BlockHeading } from "../primitives/block-heading.mjs";
5
+ import { CTAGroup } from "../primitives/cta-group.mjs";
5
6
  import { colorThemeClasses, isThemeDark } from "../types.mjs";
6
7
  import "react";
7
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
8
9
  //#region src/client/storefront/blocks/text-content-with-image.tsx
9
- function CTAElement({ buttonLabel, buttonHref, textLinkLabel, textLinkHref, dark }) {
10
- if (!buttonLabel && !textLinkLabel) return null;
11
- return /* @__PURE__ */ jsxs("div", {
12
- className: "mt-6 flex flex-wrap items-center gap-4",
13
- children: [buttonLabel && buttonHref && /* @__PURE__ */ jsx(StorefrontButton, {
14
- href: buttonHref,
15
- variant: dark ? "outline" : "primary",
16
- children: buttonLabel
17
- }), textLinkLabel && textLinkHref && /* @__PURE__ */ jsx(StorefrontTextLink, {
18
- href: textLinkHref,
19
- className: dark ? "text-white" : void 0,
20
- children: textLinkLabel
21
- })]
22
- });
23
- }
24
- const headingStyle = {
25
- fontWeight: "var(--enad-heading-weight)",
26
- letterSpacing: "var(--enad-heading-tracking)",
27
- textTransform: "var(--enad-heading-transform)"
28
- };
29
- function TextBlockContent({ preheader, heading, body, dark, preheaderColor = dark ? "text-white/70" : "text-muted-foreground", headingColor = dark ? "text-white" : "text-foreground", bodyColor = dark ? "text-white/80" : "text-muted-foreground", bodyMaxWidth = "" }) {
30
- return /* @__PURE__ */ jsxs(Fragment, { children: [
31
- preheader && /* @__PURE__ */ jsx("p", {
32
- className: cn("mb-2 text-sm uppercase tracking-widest", preheaderColor),
33
- children: preheader
34
- }),
35
- heading && /* @__PURE__ */ jsx("h2", {
36
- className: cn("text-2xl font-heading md:text-3xl", headingColor),
37
- style: headingStyle,
38
- children: heading
39
- }),
40
- body && /* @__PURE__ */ jsx("p", {
41
- className: cn("mt-4 text-base leading-relaxed", bodyColor, bodyMaxWidth),
42
- children: body
43
- })
44
- ] });
45
- }
46
- function StackedVariant({ image, fullWidth = false, preheader, heading, body, buttonLabel, buttonHref, textLinkLabel, textLinkHref, dark, themeClasses, className }) {
10
+ const textContentWithImageRecipe = defineSlotRecipe({
11
+ slots: [
12
+ "root",
13
+ "inner",
14
+ "imageArea",
15
+ "image",
16
+ "textArea"
17
+ ],
18
+ base: {
19
+ root: "",
20
+ inner: "",
21
+ imageArea: "relative overflow-hidden rounded-[var(--enad-image-radius)] bg-muted",
22
+ image: "size-full object-cover",
23
+ textArea: "min-w-0"
24
+ },
25
+ variants: {
26
+ variant: {
27
+ "side-by-side": { imageArea: "aspect-[4/5]" },
28
+ stacked: {
29
+ imageArea: "aspect-[16/9] w-full",
30
+ textArea: "py-8"
31
+ },
32
+ overlap: {
33
+ imageArea: "lg:w-3/5",
34
+ textArea: "relative z-10 min-w-0 rounded-[var(--enad-card-radius)] p-8 shadow-[var(--enad-card-shadow)] lg:p-10"
35
+ }
36
+ },
37
+ fullWidth: {
38
+ true: {},
39
+ false: {}
40
+ },
41
+ imagePosition: {
42
+ first: {},
43
+ last: {}
44
+ }
45
+ },
46
+ defaultVariants: {
47
+ variant: "side-by-side",
48
+ fullWidth: "false",
49
+ imagePosition: "first"
50
+ },
51
+ compoundVariants: [
52
+ {
53
+ variant: "side-by-side",
54
+ fullWidth: "true",
55
+ css: {
56
+ inner: "grid lg:grid-cols-2",
57
+ textArea: "px-8 py-12 lg:px-16 lg:py-20"
58
+ }
59
+ },
60
+ {
61
+ variant: "side-by-side",
62
+ fullWidth: "false",
63
+ css: {
64
+ inner: "mx-auto grid max-w-7xl gap-[var(--enad-text-content-gap,2rem)] px-4 py-12 md:px-8 lg:items-center lg:gap-[calc(var(--enad-text-content-gap,2rem)*1.5)] lg:grid-cols-2",
65
+ textArea: "py-8 lg:py-0"
66
+ }
67
+ },
68
+ {
69
+ variant: "stacked",
70
+ fullWidth: "true",
71
+ css: { textArea: "px-8 lg:px-16" }
72
+ },
73
+ {
74
+ variant: "stacked",
75
+ fullWidth: "false",
76
+ css: { inner: "mx-auto max-w-7xl px-4 py-12 md:px-8" }
77
+ },
78
+ {
79
+ variant: "overlap",
80
+ css: { inner: "mx-auto max-w-7xl px-4 py-12 md:px-8" }
81
+ }
82
+ ]
83
+ });
84
+ function StandardLayout({ image, imagePosition = "first", fullWidth = false, variant, preheader, heading, body, buttonLabel, buttonHref, textLinkLabel, textLinkHref, dark, themeClasses, className }) {
47
85
  const hasImage = Boolean(image?.src);
48
- return /* @__PURE__ */ jsx("section", {
49
- className: cn(themeClasses, className),
50
- children: /* @__PURE__ */ jsxs("div", {
51
- className: cn(fullWidth ? "" : "mx-auto max-w-7xl px-4 py-12 md:px-8"),
52
- children: [hasImage && /* @__PURE__ */ jsx("div", {
53
- className: cn("relative overflow-hidden rounded-[var(--enad-image-radius)] bg-muted", "aspect-[16/9] w-full"),
54
- children: /* @__PURE__ */ jsx("img", {
55
- src: image?.src,
56
- alt: image?.alt ?? "",
57
- loading: "lazy",
58
- className: "size-full object-cover"
59
- })
60
- }), /* @__PURE__ */ jsxs("div", {
61
- className: cn("min-w-0 py-8", fullWidth && "px-8 lg:px-16"),
62
- children: [/* @__PURE__ */ jsx(TextBlockContent, {
63
- preheader,
64
- heading,
65
- body,
66
- dark,
67
- bodyMaxWidth: "max-w-2xl"
68
- }), /* @__PURE__ */ jsx(CTAElement, {
69
- buttonLabel,
70
- buttonHref,
71
- textLinkLabel,
72
- textLinkHref,
73
- dark
74
- })]
75
- })]
76
- })
86
+ const classes = textContentWithImageRecipe({
87
+ variant,
88
+ fullWidth: fullWidth ? "true" : "false",
89
+ imagePosition
77
90
  });
78
- }
79
- function OverlapCTA({ buttonLabel, buttonHref, textLinkLabel, textLinkHref, dark }) {
80
- if (!buttonLabel && !textLinkLabel) return null;
81
- return /* @__PURE__ */ jsxs("div", {
82
- className: "mt-6 flex flex-wrap items-center gap-4",
83
- children: [buttonLabel && buttonHref && /* @__PURE__ */ jsx(StorefrontButton, {
84
- href: buttonHref,
85
- variant: dark ? "outline" : "primary",
86
- children: buttonLabel
87
- }), textLinkLabel && textLinkHref && /* @__PURE__ */ jsx(StorefrontTextLink, {
88
- href: textLinkHref,
89
- className: dark ? "text-background" : void 0,
90
- children: textLinkLabel
91
- })]
92
- });
93
- }
94
- function OverlapTextCard({ preheader, heading, body, buttonLabel, buttonHref, textLinkLabel, textLinkHref, dark, hasImage, overlapPositionClass }) {
95
- return /* @__PURE__ */ jsxs("div", {
96
- className: cn("relative z-10 min-w-0 rounded-[var(--enad-card-radius)] shadow-[var(--enad-card-shadow)]", hasImage ? "lg:w-2/5" : "w-full", dark ? "bg-foreground text-background" : "bg-background", "p-8 lg:p-10", hasImage ? "-mt-16 mx-4 lg:mt-0 lg:mx-0" : "", overlapPositionClass),
97
- children: [/* @__PURE__ */ jsx(TextBlockContent, {
91
+ const imageEl = hasImage ? /* @__PURE__ */ jsx("div", {
92
+ className: cn(classes.imageArea, fullWidth && variant === "side-by-side" && "lg:aspect-auto"),
93
+ children: /* @__PURE__ */ jsx("img", {
94
+ src: image?.src,
95
+ alt: image?.alt ?? "",
96
+ loading: "lazy",
97
+ className: classes.image
98
+ })
99
+ }) : null;
100
+ const textEl = /* @__PURE__ */ jsxs("div", {
101
+ className: cn(classes.textArea, "flex flex-col justify-center"),
102
+ children: [/* @__PURE__ */ jsx(BlockHeading, {
98
103
  preheader,
99
104
  heading,
100
105
  body,
101
106
  dark,
102
- preheaderColor: dark ? "text-background/70" : "text-muted-foreground",
103
- headingColor: dark ? "text-background" : "text-foreground",
104
- bodyColor: dark ? "text-background/80" : "text-muted-foreground"
105
- }), /* @__PURE__ */ jsx(OverlapCTA, {
107
+ bodyMaxWidth: variant === "side-by-side" ? "max-w-lg" : "max-w-2xl"
108
+ }), /* @__PURE__ */ jsx(CTAGroup, {
106
109
  buttonLabel,
107
110
  buttonHref,
108
111
  textLinkLabel,
@@ -110,109 +113,83 @@ function OverlapTextCard({ preheader, heading, body, buttonLabel, buttonHref, te
110
113
  dark
111
114
  })]
112
115
  });
116
+ const showImageFirst = hasImage && imagePosition === "first";
117
+ const showImageLast = hasImage && imagePosition === "last";
118
+ return /* @__PURE__ */ jsx("section", {
119
+ className: cn(themeClasses, className),
120
+ children: /* @__PURE__ */ jsxs("div", {
121
+ className: cn(classes.inner, hasImage && variant === "side-by-side" && fullWidth && "lg:grid-cols-2"),
122
+ children: [
123
+ showImageFirst && imageEl,
124
+ textEl,
125
+ showImageLast && imageEl,
126
+ !hasImage && null
127
+ ]
128
+ })
129
+ });
113
130
  }
114
- function OverlapVariant({ image, imagePosition = "first", preheader, heading, body, buttonLabel, buttonHref, textLinkLabel, textLinkHref, dark, themeClasses, className }) {
131
+ function OverlapLayout({ image, imagePosition = "first", preheader, heading, body, buttonLabel, buttonHref, textLinkLabel, textLinkHref, dark, themeClasses, className }) {
115
132
  const hasImage = Boolean(image?.src);
133
+ const classes = textContentWithImageRecipe({ variant: "overlap" });
116
134
  const overlapPositionClass = (() => {
117
- if (!hasImage) return "";
118
- if (imagePosition === "last") return "lg:order-1 lg:-me-16";
119
- return "lg:-ms-16";
135
+ if (!hasImage) return "w-full";
136
+ if (imagePosition === "last") return "lg:order-1 lg:-me-16 lg:w-2/5";
137
+ return "lg:-ms-16 lg:w-2/5";
120
138
  })();
121
139
  return /* @__PURE__ */ jsx("section", {
122
140
  className: cn(themeClasses, className),
123
141
  children: /* @__PURE__ */ jsx("div", {
124
- className: "mx-auto max-w-7xl px-4 py-12 md:px-8",
142
+ className: classes.inner,
125
143
  children: /* @__PURE__ */ jsxs("div", {
126
144
  className: cn("relative", hasImage && "lg:flex lg:items-center"),
127
145
  children: [hasImage && /* @__PURE__ */ jsx("div", {
128
- className: cn("relative overflow-hidden rounded-[var(--enad-image-radius)] bg-muted lg:w-3/5", imagePosition === "last" ? "lg:order-2" : ""),
146
+ className: cn(classes.imageArea, imagePosition === "last" ? "lg:order-2" : ""),
129
147
  children: /* @__PURE__ */ jsx("div", {
130
148
  className: "aspect-[4/5] lg:aspect-[3/2]",
131
149
  children: /* @__PURE__ */ jsx("img", {
132
150
  src: image?.src,
133
151
  alt: image?.alt ?? "",
134
152
  loading: "lazy",
135
- className: "size-full object-cover"
153
+ className: classes.image
136
154
  })
137
155
  })
138
- }), /* @__PURE__ */ jsx(OverlapTextCard, {
139
- preheader,
140
- heading,
141
- body,
142
- buttonLabel,
143
- buttonHref,
144
- textLinkLabel,
145
- textLinkHref,
146
- dark,
147
- hasImage,
148
- overlapPositionClass
156
+ }), /* @__PURE__ */ jsxs("div", {
157
+ className: cn(classes.textArea, hasImage ? overlapPositionClass : "w-full", hasImage ? "-mt-16 mx-4 lg:mt-0 lg:mx-0" : "", dark ? "bg-foreground text-background" : "bg-background"),
158
+ children: [/* @__PURE__ */ jsx(BlockHeading, {
159
+ preheader,
160
+ heading,
161
+ body,
162
+ dark,
163
+ preheaderColor: dark ? "text-background/70" : "text-muted-foreground",
164
+ headingColor: dark ? "text-background" : "text-foreground",
165
+ bodyColor: dark ? "text-background/80" : "text-muted-foreground"
166
+ }), /* @__PURE__ */ jsx(CTAGroup, {
167
+ buttonLabel,
168
+ buttonHref,
169
+ textLinkLabel,
170
+ textLinkHref,
171
+ dark
172
+ })]
149
173
  })]
150
174
  })
151
175
  })
152
176
  });
153
177
  }
154
- function SideBySideContent({ hasImage, imagePosition, imageEl, textEl }) {
155
- if (!hasImage) return textEl;
156
- if (imagePosition === "first") return /* @__PURE__ */ jsxs(Fragment, { children: [imageEl, textEl] });
157
- return /* @__PURE__ */ jsxs(Fragment, { children: [textEl, imageEl] });
158
- }
159
- function SideBySideVariant({ image, imagePosition = "first", fullWidth = false, preheader, heading, body, buttonLabel, buttonHref, textLinkLabel, textLinkHref, dark, themeClasses, className }) {
160
- const hasImage = Boolean(image?.src);
161
- const imageEl = hasImage ? /* @__PURE__ */ jsx("div", {
162
- className: cn("relative overflow-hidden rounded-[var(--enad-image-radius)] bg-muted", fullWidth ? "aspect-[4/5] lg:aspect-auto" : "aspect-[4/5]"),
163
- children: /* @__PURE__ */ jsx("img", {
164
- src: image?.src,
165
- alt: image?.alt ?? "",
166
- loading: "lazy",
167
- className: "size-full object-cover"
168
- })
169
- }) : null;
170
- const textEl = /* @__PURE__ */ jsxs("div", {
171
- className: cn("flex min-w-0 flex-col justify-center", fullWidth ? "px-8 py-12 lg:px-16 lg:py-20" : "py-8 lg:py-0"),
172
- children: [/* @__PURE__ */ jsx(TextBlockContent, {
173
- preheader,
174
- heading,
175
- body,
176
- dark,
177
- bodyMaxWidth: "max-w-lg"
178
- }), /* @__PURE__ */ jsx(CTAElement, {
179
- buttonLabel,
180
- buttonHref,
181
- textLinkLabel,
182
- textLinkHref,
183
- dark
184
- })]
185
- });
186
- return /* @__PURE__ */ jsx("section", {
187
- className: cn(themeClasses, className),
188
- children: /* @__PURE__ */ jsx("div", {
189
- className: cn(fullWidth ? cn("grid", hasImage && "lg:grid-cols-2") : cn("mx-auto grid max-w-7xl gap-8 px-4 py-12 md:px-8 lg:items-center lg:gap-12", hasImage && "lg:grid-cols-2")),
190
- children: /* @__PURE__ */ jsx(SideBySideContent, {
191
- hasImage,
192
- imagePosition,
193
- imageEl,
194
- textEl
195
- })
196
- })
197
- });
198
- }
199
178
  function TextContentWithImage(props) {
200
179
  const { variant = "side-by-side", colorTheme, className } = props;
201
180
  const dark = isThemeDark(colorTheme);
202
181
  const themeClasses = colorThemeClasses(colorTheme);
203
- const variantProps = {
182
+ const layoutProps = {
204
183
  ...props,
184
+ variant,
205
185
  dark,
206
186
  themeClasses,
207
187
  className
208
188
  };
209
- switch (variant) {
210
- case "stacked": return /* @__PURE__ */ jsx(StackedVariant, { ...variantProps });
211
- case "overlap": return /* @__PURE__ */ jsx(OverlapVariant, { ...variantProps });
212
- default: return /* @__PURE__ */ jsx(SideBySideVariant, { ...variantProps });
213
- }
189
+ if (variant === "overlap") return /* @__PURE__ */ jsx(OverlapLayout, { ...layoutProps });
190
+ return /* @__PURE__ */ jsx(StandardLayout, { ...layoutProps });
214
191
  }
215
192
  //#endregion
216
- export { TextContentWithImage };
193
+ export { TextContentWithImage, textContentWithImageRecipe };
217
194
 
218
195
  //# sourceMappingURL=text-content-with-image.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"text-content-with-image.mjs","names":[],"sources":["../../../../src/client/storefront/blocks/text-content-with-image.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { cn } from \"../../ui/utils\"\nimport { StorefrontButton } from \"../primitives/button\"\nimport { StorefrontTextLink } from \"../primitives/text-link\"\nimport { colorThemeClasses, isThemeDark, type TextContentWithImageProps } from \"../types\"\n\n/* ─── Shared props for variant sub-components ─── */\n\ntype VariantProps = Omit<TextContentWithImageProps, \"variant\" | \"className\"> & {\n dark: boolean\n themeClasses: string\n className?: string\n}\n\n/* ─── Shared CTA element builder ─── */\n\nfunction CTAElement({\n buttonLabel,\n buttonHref,\n textLinkLabel,\n textLinkHref,\n dark,\n}: {\n buttonLabel?: string\n buttonHref?: string\n textLinkLabel?: string\n textLinkHref?: string\n dark: boolean\n}) {\n if (!buttonLabel && !textLinkLabel) return null\n return (\n <div className=\"mt-6 flex flex-wrap items-center gap-4\">\n {buttonLabel && buttonHref && (\n <StorefrontButton href={buttonHref} variant={dark ? \"outline\" : \"primary\"}>\n {buttonLabel}\n </StorefrontButton>\n )}\n {textLinkLabel && textLinkHref && (\n <StorefrontTextLink href={textLinkHref} className={dark ? \"text-white\" : undefined}>\n {textLinkLabel}\n </StorefrontTextLink>\n )}\n </div>\n )\n}\n\n/* ─── Heading style constant ─── */\n\nconst headingStyle = { fontWeight: \"var(--enad-heading-weight)\", letterSpacing: \"var(--enad-heading-tracking)\", textTransform: \"var(--enad-heading-transform)\" } as unknown as React.CSSProperties\n\n/* ─── Shared text block (preheader + heading + body) ─── */\n\nfunction TextBlockContent({\n preheader,\n heading,\n body,\n dark,\n preheaderColor = dark ? \"text-white/70\" : \"text-muted-foreground\",\n headingColor = dark ? \"text-white\" : \"text-foreground\",\n bodyColor = dark ? \"text-white/80\" : \"text-muted-foreground\",\n bodyMaxWidth = \"\",\n}: {\n preheader?: string\n heading?: string\n body?: string\n dark: boolean\n preheaderColor?: string\n headingColor?: string\n bodyColor?: string\n bodyMaxWidth?: string\n}) {\n return (\n <>\n {preheader && (\n <p className={cn(\"mb-2 text-sm uppercase tracking-widest\", preheaderColor)}>\n {preheader}\n </p>\n )}\n {heading && (\n <h2\n className={cn(\"text-2xl font-heading md:text-3xl\", headingColor)}\n style={headingStyle}\n >\n {heading}\n </h2>\n )}\n {body && (\n <p className={cn(\"mt-4 text-base leading-relaxed\", bodyColor, bodyMaxWidth)}>\n {body}\n </p>\n )}\n </>\n )\n}\n\n/* ─── Stacked variant ─── */\n\nfunction StackedVariant({\n image,\n fullWidth = false,\n preheader,\n heading,\n body,\n buttonLabel,\n buttonHref,\n textLinkLabel,\n textLinkHref,\n dark,\n themeClasses,\n className,\n}: VariantProps) {\n const hasImage = Boolean(image?.src)\n\n return (\n <section className={cn(themeClasses, className)}>\n <div className={cn(fullWidth ? \"\" : \"mx-auto max-w-7xl px-4 py-12 md:px-8\")}>\n {hasImage && (\n <div className={cn(\"relative overflow-hidden rounded-[var(--enad-image-radius)] bg-muted\", \"aspect-[16/9] w-full\")}>\n <img src={image?.src} alt={image?.alt ?? \"\"} loading=\"lazy\" className=\"size-full object-cover\" />\n </div>\n )}\n <div className={cn(\"min-w-0 py-8\", fullWidth && \"px-8 lg:px-16\")}>\n <TextBlockContent preheader={preheader} heading={heading} body={body} dark={dark} bodyMaxWidth=\"max-w-2xl\" />\n <CTAElement buttonLabel={buttonLabel} buttonHref={buttonHref} textLinkLabel={textLinkLabel} textLinkHref={textLinkHref} dark={dark} />\n </div>\n </div>\n </section>\n )\n}\n\n/* ─── Overlap text card (extracted to reduce complexity) ─── */\n\nfunction OverlapCTA({\n buttonLabel,\n buttonHref,\n textLinkLabel,\n textLinkHref,\n dark,\n}: {\n buttonLabel?: string\n buttonHref?: string\n textLinkLabel?: string\n textLinkHref?: string\n dark: boolean\n}) {\n if (!buttonLabel && !textLinkLabel) return null\n return (\n <div className=\"mt-6 flex flex-wrap items-center gap-4\">\n {buttonLabel && buttonHref && (\n <StorefrontButton href={buttonHref} variant={dark ? \"outline\" : \"primary\"}>\n {buttonLabel}\n </StorefrontButton>\n )}\n {textLinkLabel && textLinkHref && (\n <StorefrontTextLink href={textLinkHref} className={dark ? \"text-background\" : undefined}>\n {textLinkLabel}\n </StorefrontTextLink>\n )}\n </div>\n )\n}\n\nfunction OverlapTextCard({\n preheader,\n heading,\n body,\n buttonLabel,\n buttonHref,\n textLinkLabel,\n textLinkHref,\n dark,\n hasImage,\n overlapPositionClass,\n}: {\n preheader?: string\n heading?: string\n body?: string\n buttonLabel?: string\n buttonHref?: string\n textLinkLabel?: string\n textLinkHref?: string\n dark: boolean\n hasImage: boolean\n overlapPositionClass: string\n}) {\n return (\n <div className={cn(\n \"relative z-10 min-w-0 rounded-[var(--enad-card-radius)] shadow-[var(--enad-card-shadow)]\",\n hasImage ? \"lg:w-2/5\" : \"w-full\",\n dark ? \"bg-foreground text-background\" : \"bg-background\",\n \"p-8 lg:p-10\",\n hasImage ? \"-mt-16 mx-4 lg:mt-0 lg:mx-0\" : \"\",\n overlapPositionClass,\n )}>\n <TextBlockContent\n preheader={preheader}\n heading={heading}\n body={body}\n dark={dark}\n preheaderColor={dark ? \"text-background/70\" : \"text-muted-foreground\"}\n headingColor={dark ? \"text-background\" : \"text-foreground\"}\n bodyColor={dark ? \"text-background/80\" : \"text-muted-foreground\"}\n />\n <OverlapCTA buttonLabel={buttonLabel} buttonHref={buttonHref} textLinkLabel={textLinkLabel} textLinkHref={textLinkHref} dark={dark} />\n </div>\n )\n}\n\n/* ─── Overlap variant ─── */\n\nfunction OverlapVariant({\n image,\n imagePosition = \"first\",\n preheader,\n heading,\n body,\n buttonLabel,\n buttonHref,\n textLinkLabel,\n textLinkHref,\n dark,\n themeClasses,\n className,\n}: VariantProps) {\n const hasImage = Boolean(image?.src)\n\n const overlapPositionClass = (() => {\n if (!hasImage) return \"\"\n if (imagePosition === \"last\") return \"lg:order-1 lg:-me-16\"\n return \"lg:-ms-16\"\n })()\n\n return (\n <section className={cn(themeClasses, className)}>\n <div className=\"mx-auto max-w-7xl px-4 py-12 md:px-8\">\n <div className={cn(\"relative\", hasImage && \"lg:flex lg:items-center\")}>\n {hasImage && (\n <div className={cn(\n \"relative overflow-hidden rounded-[var(--enad-image-radius)] bg-muted lg:w-3/5\",\n imagePosition === \"last\" ? \"lg:order-2\" : \"\",\n )}>\n <div className=\"aspect-[4/5] lg:aspect-[3/2]\">\n <img src={image?.src} alt={image?.alt ?? \"\"} loading=\"lazy\" className=\"size-full object-cover\" />\n </div>\n </div>\n )}\n\n <OverlapTextCard\n preheader={preheader}\n heading={heading}\n body={body}\n buttonLabel={buttonLabel}\n buttonHref={buttonHref}\n textLinkLabel={textLinkLabel}\n textLinkHref={textLinkHref}\n dark={dark}\n hasImage={hasImage}\n overlapPositionClass={overlapPositionClass}\n />\n </div>\n </div>\n </section>\n )\n}\n\n/* ─── Side-by-side content ordering helper ─── */\n\nfunction SideBySideContent({\n hasImage,\n imagePosition,\n imageEl,\n textEl,\n}: {\n hasImage: boolean\n imagePosition: string\n imageEl: React.ReactNode\n textEl: React.ReactNode\n}) {\n if (!hasImage) return textEl\n if (imagePosition === \"first\") {\n return (\n <>\n {imageEl}\n {textEl}\n </>\n )\n }\n return (\n <>\n {textEl}\n {imageEl}\n </>\n )\n}\n\n/* ─── Side-by-side variant ─── */\n\nfunction SideBySideVariant({\n image,\n imagePosition = \"first\",\n fullWidth = false,\n preheader,\n heading,\n body,\n buttonLabel,\n buttonHref,\n textLinkLabel,\n textLinkHref,\n dark,\n themeClasses,\n className,\n}: VariantProps) {\n const hasImage = Boolean(image?.src)\n\n const imageEl = hasImage ? (\n <div className={cn(\"relative overflow-hidden rounded-[var(--enad-image-radius)] bg-muted\", fullWidth ? \"aspect-[4/5] lg:aspect-auto\" : \"aspect-[4/5]\")}>\n <img src={image?.src} alt={image?.alt ?? \"\"} loading=\"lazy\" className=\"size-full object-cover\" />\n </div>\n ) : null\n\n const textEl = (\n <div className={cn(\"flex min-w-0 flex-col justify-center\", fullWidth ? \"px-8 py-12 lg:px-16 lg:py-20\" : \"py-8 lg:py-0\")}>\n <TextBlockContent preheader={preheader} heading={heading} body={body} dark={dark} bodyMaxWidth=\"max-w-lg\" />\n <CTAElement buttonLabel={buttonLabel} buttonHref={buttonHref} textLinkLabel={textLinkLabel} textLinkHref={textLinkHref} dark={dark} />\n </div>\n )\n\n return (\n <section className={cn(themeClasses, className)}>\n <div className={cn(\n fullWidth\n ? cn(\"grid\", hasImage && \"lg:grid-cols-2\")\n : cn(\"mx-auto grid max-w-7xl gap-8 px-4 py-12 md:px-8 lg:items-center lg:gap-12\", hasImage && \"lg:grid-cols-2\"),\n )}>\n <SideBySideContent\n hasImage={hasImage}\n imagePosition={imagePosition}\n imageEl={imageEl}\n textEl={textEl}\n />\n </div>\n </section>\n )\n}\n\n/* ─── Main component ─── */\n\nfunction TextContentWithImage(props: TextContentWithImageProps) {\n const { variant = \"side-by-side\", colorTheme, className } = props\n const dark = isThemeDark(colorTheme)\n const themeClasses = colorThemeClasses(colorTheme)\n\n const variantProps: VariantProps = { ...props, dark, themeClasses, className }\n\n switch (variant) {\n case \"stacked\":\n return <StackedVariant {...variantProps} />\n case \"overlap\":\n return <OverlapVariant {...variantProps} />\n default:\n return <SideBySideVariant {...variantProps} />\n }\n}\n\nexport { TextContentWithImage, type TextContentWithImageProps }\n"],"mappings":"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"}
1
+ {"version":3,"file":"text-content-with-image.mjs","names":[],"sources":["../../../../src/client/storefront/blocks/text-content-with-image.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../../ui/utils\";\nimport { defineSlotRecipe } from \"../../ui-resolver/recipe\";\nimport { colorThemeClasses, isThemeDark, type TextContentWithImageProps } from \"../types\";\nimport { BlockHeading } from \"../primitives/block-heading\";\nimport { CTAGroup } from \"../primitives/cta-group\";\n\n/* ─── Recipe ─── */\n\nconst textContentWithImageRecipe = defineSlotRecipe({\n slots: [\"root\", \"inner\", \"imageArea\", \"image\", \"textArea\"] as const,\n base: {\n root: \"\",\n inner: \"\",\n imageArea: \"relative overflow-hidden rounded-[var(--enad-image-radius)] bg-muted\",\n image: \"size-full object-cover\",\n textArea: \"min-w-0\",\n },\n variants: {\n variant: {\n \"side-by-side\": {\n imageArea: \"aspect-[4/5]\",\n },\n stacked: {\n imageArea: \"aspect-[16/9] w-full\",\n textArea: \"py-8\",\n },\n overlap: {\n imageArea: \"lg:w-3/5\",\n textArea:\n \"relative z-10 min-w-0 rounded-[var(--enad-card-radius)] p-8 shadow-[var(--enad-card-shadow)] lg:p-10\",\n },\n },\n fullWidth: {\n true: {},\n false: {},\n },\n imagePosition: {\n first: {},\n last: {},\n },\n },\n defaultVariants: {\n variant: \"side-by-side\",\n fullWidth: \"false\",\n imagePosition: \"first\",\n },\n compoundVariants: [\n {\n variant: \"side-by-side\",\n fullWidth: \"true\",\n css: {\n inner: \"grid lg:grid-cols-2\",\n textArea: \"px-8 py-12 lg:px-16 lg:py-20\",\n },\n },\n {\n variant: \"side-by-side\",\n fullWidth: \"false\",\n css: {\n inner:\n \"mx-auto grid max-w-7xl gap-[var(--enad-text-content-gap,2rem)] px-4 py-12 md:px-8 lg:items-center lg:gap-[calc(var(--enad-text-content-gap,2rem)*1.5)] lg:grid-cols-2\",\n textArea: \"py-8 lg:py-0\",\n },\n },\n {\n variant: \"stacked\",\n fullWidth: \"true\",\n css: {\n textArea: \"px-8 lg:px-16\",\n },\n },\n {\n variant: \"stacked\",\n fullWidth: \"false\",\n css: {\n inner: \"mx-auto max-w-7xl px-4 py-12 md:px-8\",\n },\n },\n {\n variant: \"overlap\",\n css: {\n inner: \"mx-auto max-w-7xl px-4 py-12 md:px-8\",\n },\n },\n ],\n});\n\n/* ─── Standard layout (side-by-side + stacked) ─── */\n\nfunction StandardLayout({\n image,\n imagePosition = \"first\",\n fullWidth = false,\n variant,\n preheader,\n heading,\n body,\n buttonLabel,\n buttonHref,\n textLinkLabel,\n textLinkHref,\n dark,\n themeClasses,\n className,\n}: LayoutProps) {\n const hasImage = Boolean(image?.src);\n const classes = textContentWithImageRecipe({\n variant,\n fullWidth: fullWidth ? \"true\" : \"false\",\n imagePosition: imagePosition,\n });\n\n const imageEl = hasImage ? (\n <div\n className={cn(classes.imageArea, fullWidth && variant === \"side-by-side\" && \"lg:aspect-auto\")}\n >\n <img src={image?.src} alt={image?.alt ?? \"\"} loading=\"lazy\" className={classes.image} />\n </div>\n ) : null;\n\n const textEl = (\n <div className={cn(classes.textArea, \"flex flex-col justify-center\")}>\n <BlockHeading\n preheader={preheader}\n heading={heading}\n body={body}\n dark={dark}\n bodyMaxWidth={variant === \"side-by-side\" ? \"max-w-lg\" : \"max-w-2xl\"}\n />\n <CTAGroup\n buttonLabel={buttonLabel}\n buttonHref={buttonHref}\n textLinkLabel={textLinkLabel}\n textLinkHref={textLinkHref}\n dark={dark}\n />\n </div>\n );\n\n const showImageFirst = hasImage && imagePosition === \"first\";\n const showImageLast = hasImage && imagePosition === \"last\";\n\n return (\n <section className={cn(themeClasses, className)}>\n <div\n className={cn(\n classes.inner,\n hasImage && variant === \"side-by-side\" && fullWidth && \"lg:grid-cols-2\",\n )}\n >\n {showImageFirst && imageEl}\n {textEl}\n {showImageLast && imageEl}\n {!hasImage && null}\n </div>\n </section>\n );\n}\n\n/* ─── Overlap layout ─── */\n\nfunction OverlapLayout({\n image,\n imagePosition = \"first\",\n preheader,\n heading,\n body,\n buttonLabel,\n buttonHref,\n textLinkLabel,\n textLinkHref,\n dark,\n themeClasses,\n className,\n}: LayoutProps) {\n const hasImage = Boolean(image?.src);\n const classes = textContentWithImageRecipe({ variant: \"overlap\" });\n\n const overlapPositionClass = (() => {\n if (!hasImage) return \"w-full\";\n if (imagePosition === \"last\") return \"lg:order-1 lg:-me-16 lg:w-2/5\";\n return \"lg:-ms-16 lg:w-2/5\";\n })();\n\n return (\n <section className={cn(themeClasses, className)}>\n <div className={classes.inner}>\n <div className={cn(\"relative\", hasImage && \"lg:flex lg:items-center\")}>\n {hasImage && (\n <div className={cn(classes.imageArea, imagePosition === \"last\" ? \"lg:order-2\" : \"\")}>\n <div className=\"aspect-[4/5] lg:aspect-[3/2]\">\n <img\n src={image?.src}\n alt={image?.alt ?? \"\"}\n loading=\"lazy\"\n className={classes.image}\n />\n </div>\n </div>\n )}\n\n <div\n className={cn(\n classes.textArea,\n hasImage ? overlapPositionClass : \"w-full\",\n hasImage ? \"-mt-16 mx-4 lg:mt-0 lg:mx-0\" : \"\",\n dark ? \"bg-foreground text-background\" : \"bg-background\",\n )}\n >\n <BlockHeading\n preheader={preheader}\n heading={heading}\n body={body}\n dark={dark}\n preheaderColor={dark ? \"text-background/70\" : \"text-muted-foreground\"}\n headingColor={dark ? \"text-background\" : \"text-foreground\"}\n bodyColor={dark ? \"text-background/80\" : \"text-muted-foreground\"}\n />\n <CTAGroup\n buttonLabel={buttonLabel}\n buttonHref={buttonHref}\n textLinkLabel={textLinkLabel}\n textLinkHref={textLinkHref}\n dark={dark}\n />\n </div>\n </div>\n </div>\n </section>\n );\n}\n\n/* ─── Internal layout prop type ─── */\n\ntype LayoutProps = TextContentWithImageProps & {\n variant: \"side-by-side\" | \"stacked\" | \"overlap\";\n dark: boolean;\n themeClasses: string;\n};\n\n/* ─── Main component ─── */\n\nfunction TextContentWithImage(props: TextContentWithImageProps) {\n const { variant = \"side-by-side\", colorTheme, className } = props;\n const dark = isThemeDark(colorTheme);\n const themeClasses = colorThemeClasses(colorTheme);\n\n const layoutProps: LayoutProps = {\n ...props,\n variant,\n dark,\n themeClasses,\n className,\n };\n\n if (variant === \"overlap\") {\n return <OverlapLayout {...layoutProps} />;\n }\n\n return <StandardLayout {...layoutProps} />;\n}\n\nexport { TextContentWithImage, textContentWithImageRecipe, type TextContentWithImageProps };\n"],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { CarouselWheelAxis } from "../../ui/carousel.js";
1
2
  import * as React$1 from "react";
2
3
  import * as react_jsx_runtime0 from "react/jsx-runtime";
3
4
 
@@ -11,6 +12,8 @@ interface SwipeableCarouselProps {
11
12
  dotsClassName?: string;
12
13
  activeSlide?: number;
13
14
  onSlideChange?: (index: number) => void;
15
+ /** Controls which wheel/trackpad axes scroll the carousel. Defaults to "vertical". */
16
+ wheelAxis?: CarouselWheelAxis;
14
17
  }
15
18
  declare function SwipeableCarousel({
16
19
  items,
@@ -19,7 +22,8 @@ declare function SwipeableCarousel({
19
22
  showDots,
20
23
  dotsClassName,
21
24
  activeSlide,
22
- onSlideChange
25
+ onSlideChange,
26
+ wheelAxis
23
27
  }: SwipeableCarouselProps): react_jsx_runtime0.JSX.Element | null;
24
28
  //#endregion
25
29
  export { SwipeableCarousel, type SwipeableCarouselProps };
@@ -1 +1 @@
1
- {"version":3,"file":"swipeable-carousel.d.ts","names":[],"sources":["../../../../src/client/storefront/carousel/swipeable-carousel.tsx"],"mappings":";;;;UAaU,sBAAA;EACR,KAAA,EAAO,OAAA,CAAM,SAAA;EACb,SAAA;EAFQ;EAIR,MAAA;EACA,QAAA;EACA,aAAA;EACA,WAAA;EACA,aAAA,IAAiB,KAAA;AAAA;AAAA,iBAGV,iBAAA,CAAA;EACP,KAAA;EACA,SAAA;EACA,MAAA;EACA,QAAA;EACA,aAAA;EACA,WAAA;EACA;AAAA,GACC,sBAAA,GAAsB,kBAAA,CAAA,GAAA,CAAA,OAAA"}
1
+ {"version":3,"file":"swipeable-carousel.d.ts","names":[],"sources":["../../../../src/client/storefront/carousel/swipeable-carousel.tsx"],"mappings":";;;;;UAcU,sBAAA;EACR,KAAA,EAAO,OAAA,CAAM,SAAA;EACb,SAAA;EAFQ;EAIR,MAAA;EACA,QAAA;EACA,aAAA;EACA,WAAA;EACA,aAAA,IAAiB,KAAA;EAPJ;EASb,SAAA,GAAY,iBAAA;AAAA;AAAA,iBAGL,iBAAA,CAAA;EACP,KAAA;EACA,SAAA;EACA,MAAA;EACA,QAAA;EACA,aAAA;EACA,WAAA;EACA,aAAA;EACA;AAAA,GACC,sBAAA,GAAsB,kBAAA,CAAA,GAAA,CAAA,OAAA"}
@@ -4,7 +4,7 @@ import { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious
4
4
  import * as React$1 from "react";
5
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
6
  //#region src/client/storefront/carousel/swipeable-carousel.tsx
7
- function SwipeableCarousel({ items, className, arrows, showDots = true, dotsClassName, activeSlide, onSlideChange }) {
7
+ function SwipeableCarousel({ items, className, arrows, showDots = true, dotsClassName, activeSlide, onSlideChange, wheelAxis }) {
8
8
  const [api, setApi] = React$1.useState();
9
9
  const [current, setCurrent] = React$1.useState(0);
10
10
  const [count, setCount] = React$1.useState(0);
@@ -29,6 +29,7 @@ function SwipeableCarousel({ items, className, arrows, showDots = true, dotsClas
29
29
  children: [/* @__PURE__ */ jsxs(Carousel, {
30
30
  setApi,
31
31
  opts: { loop: !singleItem },
32
+ wheelAxis,
32
33
  children: [/* @__PURE__ */ jsx(CarouselContent, { children: items.map((item, index) => /* @__PURE__ */ jsx(CarouselItem, { children: item }, index)) }), arrows && !singleItem && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(CarouselPrevious, { className: arrows === "inset" ? "left-3 -translate-x-0" : void 0 }), /* @__PURE__ */ jsx(CarouselNext, { className: arrows === "inset" ? "right-3 translate-x-0" : void 0 })] })]
33
34
  }), showDots && !singleItem && count > 1 && /* @__PURE__ */ jsx("div", {
34
35
  className: cn("absolute inset-x-0 bottom-3 z-10 flex items-center justify-center gap-1.5", dotsClassName),
@@ -1 +1 @@
1
- {"version":3,"file":"swipeable-carousel.mjs","names":["React"],"sources":["../../../../src/client/storefront/carousel/swipeable-carousel.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport {\n Carousel,\n CarouselContent,\n CarouselItem,\n CarouselPrevious,\n CarouselNext,\n type CarouselApi,\n} from \"../../ui-resolver/carousel\"\nimport { cn } from \"../../ui/utils\"\n\ninterface SwipeableCarouselProps {\n items: React.ReactNode[]\n className?: string\n /** 'inset' — arrows float over the image edges; 'outset' — arrows sit outside the image bounds */\n arrows?: \"inset\" | \"outset\"\n showDots?: boolean\n dotsClassName?: string\n activeSlide?: number\n onSlideChange?: (index: number) => void\n}\n\nfunction SwipeableCarousel({\n items,\n className,\n arrows,\n showDots = true,\n dotsClassName,\n activeSlide,\n onSlideChange,\n}: SwipeableCarouselProps) {\n const [api, setApi] = React.useState<CarouselApi>()\n const [current, setCurrent] = React.useState(0)\n const [count, setCount] = React.useState(0)\n\n React.useEffect(() => {\n if (!api) return\n\n setCount(api.scrollSnapList().length)\n setCurrent(api.selectedScrollSnap())\n\n api.on(\"select\", () => {\n const idx = api.selectedScrollSnap()\n setCurrent(idx)\n onSlideChange?.(idx)\n })\n }, [api, onSlideChange])\n\n React.useEffect(() => {\n if (!api || activeSlide === undefined) return\n if (api.selectedScrollSnap() !== activeSlide) {\n api.scrollTo(activeSlide)\n }\n }, [api, activeSlide])\n\n if (items.length === 0) return null\n\n const singleItem = items.length === 1\n\n return (\n <div className={cn(\"relative\", arrows === \"outset\" && \"mx-12\", className)}>\n <Carousel setApi={setApi} opts={{ loop: !singleItem }}>\n <CarouselContent>\n {items.map((item, index) => (\n <CarouselItem key={index}>{item}</CarouselItem>\n ))}\n </CarouselContent>\n\n {arrows && !singleItem && (\n <>\n <CarouselPrevious\n className={arrows === \"inset\" ? \"left-3 -translate-x-0\" : undefined}\n />\n <CarouselNext\n className={arrows === \"inset\" ? \"right-3 translate-x-0\" : undefined}\n />\n </>\n )}\n </Carousel>\n\n {showDots && !singleItem && count > 1 && (\n <div className={cn(\"absolute inset-x-0 bottom-3 z-10 flex items-center justify-center gap-1.5\", dotsClassName)} role=\"tablist\">\n {Array.from({ length: count }).map((_, index) => (\n <button\n key={index}\n type=\"button\"\n role=\"tab\"\n aria-selected={index === current}\n aria-label={`Go to slide ${index + 1}`}\n onClick={() => api?.scrollTo(index)}\n className={cn(\n \"size-2 rounded-full transition-all shadow-sm\",\n index === current\n ? \"bg-primary\"\n : \"bg-background/70 hover:bg-background/90\"\n )}\n />\n ))}\n </div>\n )}\n </div>\n )\n}\n\nexport { SwipeableCarousel, type SwipeableCarouselProps }\n"],"mappings":";;;;;;AAwBA,SAAS,kBAAkB,EACzB,OACA,WACA,QACA,WAAW,MACX,eACA,aACA,iBACyB;CACzB,MAAM,CAAC,KAAK,UAAUA,QAAM,UAAuB;CACnD,MAAM,CAAC,SAAS,cAAcA,QAAM,SAAS,EAAE;CAC/C,MAAM,CAAC,OAAO,YAAYA,QAAM,SAAS,EAAE;AAE3C,SAAM,gBAAgB;AACpB,MAAI,CAAC,IAAK;AAEV,WAAS,IAAI,gBAAgB,CAAC,OAAO;AACrC,aAAW,IAAI,oBAAoB,CAAC;AAEpC,MAAI,GAAG,gBAAgB;GACrB,MAAM,MAAM,IAAI,oBAAoB;AACpC,cAAW,IAAI;AACf,mBAAgB,IAAI;IACpB;IACD,CAAC,KAAK,cAAc,CAAC;AAExB,SAAM,gBAAgB;AACpB,MAAI,CAAC,OAAO,gBAAgB,KAAA,EAAW;AACvC,MAAI,IAAI,oBAAoB,KAAK,YAC/B,KAAI,SAAS,YAAY;IAE1B,CAAC,KAAK,YAAY,CAAC;AAEtB,KAAI,MAAM,WAAW,EAAG,QAAO;CAE/B,MAAM,aAAa,MAAM,WAAW;AAEpC,QACE,qBAAC,OAAD;EAAK,WAAW,GAAG,YAAY,WAAW,YAAY,SAAS,UAAU;YAAzE,CACE,qBAAC,UAAD;GAAkB;GAAQ,MAAM,EAAE,MAAM,CAAC,YAAY;aAArD,CACE,oBAAC,iBAAD,EAAA,UACG,MAAM,KAAK,MAAM,UAChB,oBAAC,cAAD,EAAA,UAA2B,MAAoB,EAA5B,MAA4B,CAC/C,EACc,CAAA,EAEjB,UAAU,CAAC,cACV,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,kBAAD,EACE,WAAW,WAAW,UAAU,0BAA0B,KAAA,GAC1D,CAAA,EACF,oBAAC,cAAD,EACE,WAAW,WAAW,UAAU,0BAA0B,KAAA,GAC1D,CAAA,CACD,EAAA,CAAA,CAEI;MAEV,YAAY,CAAC,cAAc,QAAQ,KAClC,oBAAC,OAAD;GAAK,WAAW,GAAG,6EAA6E,cAAc;GAAE,MAAK;aAClH,MAAM,KAAK,EAAE,QAAQ,OAAO,CAAC,CAAC,KAAK,GAAG,UACrC,oBAAC,UAAD;IAEE,MAAK;IACL,MAAK;IACL,iBAAe,UAAU;IACzB,cAAY,eAAe,QAAQ;IACnC,eAAe,KAAK,SAAS,MAAM;IACnC,WAAW,GACT,gDACA,UAAU,UACN,eACA,0CACL;IACD,EAZK,MAYL,CACF;GACE,CAAA,CAEJ"}
1
+ {"version":3,"file":"swipeable-carousel.mjs","names":["React"],"sources":["../../../../src/client/storefront/carousel/swipeable-carousel.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport {\n Carousel,\n CarouselContent,\n CarouselItem,\n CarouselPrevious,\n CarouselNext,\n type CarouselApi,\n} from \"../../ui-resolver/carousel\";\nimport { type CarouselWheelAxis } from \"../../ui/carousel\";\nimport { cn } from \"../../ui/utils\";\n\ninterface SwipeableCarouselProps {\n items: React.ReactNode[];\n className?: string;\n /** 'inset' — arrows float over the image edges; 'outset' — arrows sit outside the image bounds */\n arrows?: \"inset\" | \"outset\";\n showDots?: boolean;\n dotsClassName?: string;\n activeSlide?: number;\n onSlideChange?: (index: number) => void;\n /** Controls which wheel/trackpad axes scroll the carousel. Defaults to \"vertical\". */\n wheelAxis?: CarouselWheelAxis;\n}\n\nfunction SwipeableCarousel({\n items,\n className,\n arrows,\n showDots = true,\n dotsClassName,\n activeSlide,\n onSlideChange,\n wheelAxis,\n}: SwipeableCarouselProps) {\n const [api, setApi] = React.useState<CarouselApi>();\n const [current, setCurrent] = React.useState(0);\n const [count, setCount] = React.useState(0);\n\n React.useEffect(() => {\n if (!api) return;\n\n setCount(api.scrollSnapList().length);\n setCurrent(api.selectedScrollSnap());\n\n api.on(\"select\", () => {\n const idx = api.selectedScrollSnap();\n setCurrent(idx);\n onSlideChange?.(idx);\n });\n }, [api, onSlideChange]);\n\n React.useEffect(() => {\n if (!api || activeSlide === undefined) return;\n if (api.selectedScrollSnap() !== activeSlide) {\n api.scrollTo(activeSlide);\n }\n }, [api, activeSlide]);\n\n if (items.length === 0) return null;\n\n const singleItem = items.length === 1;\n\n return (\n <div className={cn(\"relative\", arrows === \"outset\" && \"mx-12\", className)}>\n <Carousel setApi={setApi} opts={{ loop: !singleItem }} wheelAxis={wheelAxis}>\n <CarouselContent>\n {items.map((item, index) => (\n <CarouselItem key={index}>{item}</CarouselItem>\n ))}\n </CarouselContent>\n\n {arrows && !singleItem && (\n <>\n <CarouselPrevious\n className={arrows === \"inset\" ? \"left-3 -translate-x-0\" : undefined}\n />\n <CarouselNext className={arrows === \"inset\" ? \"right-3 translate-x-0\" : undefined} />\n </>\n )}\n </Carousel>\n\n {showDots && !singleItem && count > 1 && (\n <div\n className={cn(\n \"absolute inset-x-0 bottom-3 z-10 flex items-center justify-center gap-1.5\",\n dotsClassName,\n )}\n role=\"tablist\"\n >\n {Array.from({ length: count }).map((_, index) => (\n <button\n key={index}\n type=\"button\"\n role=\"tab\"\n aria-selected={index === current}\n aria-label={`Go to slide ${index + 1}`}\n onClick={() => api?.scrollTo(index)}\n className={cn(\n \"size-2 rounded-full transition-all shadow-sm\",\n index === current ? \"bg-primary\" : \"bg-background/70 hover:bg-background/90\",\n )}\n />\n ))}\n </div>\n )}\n </div>\n );\n}\n\nexport { SwipeableCarousel, type SwipeableCarouselProps };\n"],"mappings":";;;;;;AA2BA,SAAS,kBAAkB,EACzB,OACA,WACA,QACA,WAAW,MACX,eACA,aACA,eACA,aACyB;CACzB,MAAM,CAAC,KAAK,UAAUA,QAAM,UAAuB;CACnD,MAAM,CAAC,SAAS,cAAcA,QAAM,SAAS,EAAE;CAC/C,MAAM,CAAC,OAAO,YAAYA,QAAM,SAAS,EAAE;AAE3C,SAAM,gBAAgB;AACpB,MAAI,CAAC,IAAK;AAEV,WAAS,IAAI,gBAAgB,CAAC,OAAO;AACrC,aAAW,IAAI,oBAAoB,CAAC;AAEpC,MAAI,GAAG,gBAAgB;GACrB,MAAM,MAAM,IAAI,oBAAoB;AACpC,cAAW,IAAI;AACf,mBAAgB,IAAI;IACpB;IACD,CAAC,KAAK,cAAc,CAAC;AAExB,SAAM,gBAAgB;AACpB,MAAI,CAAC,OAAO,gBAAgB,KAAA,EAAW;AACvC,MAAI,IAAI,oBAAoB,KAAK,YAC/B,KAAI,SAAS,YAAY;IAE1B,CAAC,KAAK,YAAY,CAAC;AAEtB,KAAI,MAAM,WAAW,EAAG,QAAO;CAE/B,MAAM,aAAa,MAAM,WAAW;AAEpC,QACE,qBAAC,OAAD;EAAK,WAAW,GAAG,YAAY,WAAW,YAAY,SAAS,UAAU;YAAzE,CACE,qBAAC,UAAD;GAAkB;GAAQ,MAAM,EAAE,MAAM,CAAC,YAAY;GAAa;aAAlE,CACE,oBAAC,iBAAD,EAAA,UACG,MAAM,KAAK,MAAM,UAChB,oBAAC,cAAD,EAAA,UAA2B,MAAoB,EAA5B,MAA4B,CAC/C,EACc,CAAA,EAEjB,UAAU,CAAC,cACV,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,kBAAD,EACE,WAAW,WAAW,UAAU,0BAA0B,KAAA,GAC1D,CAAA,EACF,oBAAC,cAAD,EAAc,WAAW,WAAW,UAAU,0BAA0B,KAAA,GAAa,CAAA,CACpF,EAAA,CAAA,CAEI;MAEV,YAAY,CAAC,cAAc,QAAQ,KAClC,oBAAC,OAAD;GACE,WAAW,GACT,6EACA,cACD;GACD,MAAK;aAEJ,MAAM,KAAK,EAAE,QAAQ,OAAO,CAAC,CAAC,KAAK,GAAG,UACrC,oBAAC,UAAD;IAEE,MAAK;IACL,MAAK;IACL,iBAAe,UAAU;IACzB,cAAY,eAAe,QAAQ;IACnC,eAAe,KAAK,SAAS,MAAM;IACnC,WAAW,GACT,gDACA,UAAU,UAAU,eAAe,0CACpC;IACD,EAVK,MAUL,CACF;GACE,CAAA,CAEJ"}
@@ -1 +1 @@
1
- {"version":3,"file":"product-recommendations.d.ts","names":[],"sources":["../../../../src/client/storefront/components/product-recommendations.tsx"],"mappings":";;;;UAUiB,2BAAA;EACf,KAAA;;EAEA,OAAA;EAH0C;EAK1C,QAAA,EAAU,OAAA,CAAM,SAAA;EAAS;EAEzB,WAAA;EACA,YAAA;EAHA;EAKA,UAAA;EALgB;EAOhB,OAAA;EACA,SAAA;AAAA;AAAA,iBAKc,sBAAA,CAAA;EACd,KAAA;EACA,QAAA;EACA,WAAA;EACA,YAAA;EACA,UAAA;EACA,OAAA;EACA;AAAA,GACC,2BAAA,GAA2B,kBAAA,CAAA,GAAA,CAAA,OAAA"}
1
+ {"version":3,"file":"product-recommendations.d.ts","names":[],"sources":["../../../../src/client/storefront/components/product-recommendations.tsx"],"mappings":";;;;UAgBiB,2BAAA;EACf,KAAA;;EAEA,OAAA;EAH0C;EAK1C,QAAA,EAAU,OAAA,CAAM,SAAA;EAAS;EAEzB,WAAA;EACA,YAAA;EAHA;EAKA,UAAA;EALgB;EAOhB,OAAA;EACA,SAAA;AAAA;AAAA,iBAKc,sBAAA,CAAA;EACd,KAAA;EACA,QAAA;EACA,WAAA;EACA,YAAA;EACA,UAAA;EACA,OAAA;EACA;AAAA,GACC,2BAAA,GAA2B,kBAAA,CAAA,GAAA,CAAA,OAAA"}
@@ -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" })