@opensite/ui 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/button.cjs +3 -3
- package/dist/button.cjs.map +1 -1
- package/dist/button.js +3 -3
- package/dist/button.js.map +1 -1
- package/dist/components.cjs +10 -5
- package/dist/components.cjs.map +1 -1
- package/dist/components.js +10 -5
- package/dist/components.js.map +1 -1
- package/dist/feature-showcase.cjs +417 -0
- package/dist/feature-showcase.cjs.map +1 -0
- package/dist/feature-showcase.d.cts +46 -0
- package/dist/feature-showcase.d.ts +46 -0
- package/dist/feature-showcase.js +393 -0
- package/dist/feature-showcase.js.map +1 -0
- package/dist/index.cjs +10 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +10 -5
- package/dist/index.js.map +1 -1
- package/dist/page-hero-banner.cjs +7 -2
- package/dist/page-hero-banner.cjs.map +1 -1
- package/dist/page-hero-banner.d.cts +1 -1
- package/dist/page-hero-banner.d.ts +1 -1
- package/dist/page-hero-banner.js +7 -2
- package/dist/page-hero-banner.js.map +1 -1
- package/dist/registry.cjs +460 -4
- package/dist/registry.cjs.map +1 -1
- package/dist/registry.js +441 -3
- package/dist/registry.js.map +1 -1
- package/dist/types.d.cts +68 -1
- package/dist/types.d.ts +68 -1
- package/package.json +7 -2
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useState, useRef, useEffect } from 'react';
|
|
3
|
+
import { clsx } from 'clsx';
|
|
4
|
+
import { twMerge } from 'tailwind-merge';
|
|
5
|
+
import useEmblaCarousel from 'embla-carousel-react';
|
|
6
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
7
|
+
import { Slot } from '@radix-ui/react-slot';
|
|
8
|
+
import { cva } from 'class-variance-authority';
|
|
9
|
+
|
|
10
|
+
// components/blocks/features/feature-showcase.tsx
|
|
11
|
+
function cn(...inputs) {
|
|
12
|
+
return twMerge(clsx(inputs));
|
|
13
|
+
}
|
|
14
|
+
var ArrowLeft = ({
|
|
15
|
+
size = 24,
|
|
16
|
+
className,
|
|
17
|
+
strokeWidth = 2,
|
|
18
|
+
...props
|
|
19
|
+
}) => {
|
|
20
|
+
return /* @__PURE__ */ jsx(
|
|
21
|
+
"svg",
|
|
22
|
+
{
|
|
23
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
24
|
+
width: size,
|
|
25
|
+
height: size,
|
|
26
|
+
viewBox: "0 0 24 24",
|
|
27
|
+
fill: "none",
|
|
28
|
+
stroke: "currentColor",
|
|
29
|
+
strokeWidth,
|
|
30
|
+
strokeLinecap: "round",
|
|
31
|
+
strokeLinejoin: "round",
|
|
32
|
+
className,
|
|
33
|
+
...props,
|
|
34
|
+
children: /* @__PURE__ */ jsx("path", { d: "m12 19l-7-7l7-7m7 7H5" })
|
|
35
|
+
}
|
|
36
|
+
);
|
|
37
|
+
};
|
|
38
|
+
var ArrowRight = ({
|
|
39
|
+
size = 24,
|
|
40
|
+
className,
|
|
41
|
+
strokeWidth = 2,
|
|
42
|
+
...props
|
|
43
|
+
}) => {
|
|
44
|
+
return /* @__PURE__ */ jsx(
|
|
45
|
+
"svg",
|
|
46
|
+
{
|
|
47
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
48
|
+
width: size,
|
|
49
|
+
height: size,
|
|
50
|
+
viewBox: "0 0 24 24",
|
|
51
|
+
fill: "none",
|
|
52
|
+
stroke: "currentColor",
|
|
53
|
+
strokeWidth,
|
|
54
|
+
strokeLinecap: "round",
|
|
55
|
+
strokeLinejoin: "round",
|
|
56
|
+
className,
|
|
57
|
+
...props,
|
|
58
|
+
children: /* @__PURE__ */ jsx("path", { d: "M5 12h14m-7-7l7 7l-7 7" })
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
};
|
|
62
|
+
var buttonVariants = cva(
|
|
63
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-button text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
64
|
+
{
|
|
65
|
+
variants: {
|
|
66
|
+
variant: {
|
|
67
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
68
|
+
destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
|
69
|
+
outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
|
70
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
71
|
+
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
72
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
73
|
+
},
|
|
74
|
+
size: {
|
|
75
|
+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
76
|
+
sm: "h-8 rounded-button gap-1.5 px-3 has-[>svg]:px-2.5",
|
|
77
|
+
lg: "h-10 rounded-button px-6 has-[>svg]:px-4",
|
|
78
|
+
icon: "size-9",
|
|
79
|
+
"icon-sm": "size-8",
|
|
80
|
+
"icon-lg": "size-10"
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
defaultVariants: {
|
|
84
|
+
variant: "default",
|
|
85
|
+
size: "default"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
function Button({
|
|
90
|
+
className,
|
|
91
|
+
variant = "default",
|
|
92
|
+
size = "default",
|
|
93
|
+
asChild = false,
|
|
94
|
+
...props
|
|
95
|
+
}) {
|
|
96
|
+
const Comp = asChild ? Slot : "button";
|
|
97
|
+
return /* @__PURE__ */ jsx(
|
|
98
|
+
Comp,
|
|
99
|
+
{
|
|
100
|
+
"data-slot": "button",
|
|
101
|
+
"data-variant": variant,
|
|
102
|
+
"data-size": size,
|
|
103
|
+
className: cn(buttonVariants({ variant, size, className })),
|
|
104
|
+
...props
|
|
105
|
+
}
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
var CarouselContext = React.createContext(null);
|
|
109
|
+
function useCarousel() {
|
|
110
|
+
const context = React.useContext(CarouselContext);
|
|
111
|
+
if (!context) {
|
|
112
|
+
throw new Error("useCarousel must be used within a <Carousel />");
|
|
113
|
+
}
|
|
114
|
+
return context;
|
|
115
|
+
}
|
|
116
|
+
function Carousel({
|
|
117
|
+
orientation = "horizontal",
|
|
118
|
+
opts,
|
|
119
|
+
setApi,
|
|
120
|
+
plugins,
|
|
121
|
+
className,
|
|
122
|
+
children,
|
|
123
|
+
...props
|
|
124
|
+
}) {
|
|
125
|
+
const [carouselRef, api] = useEmblaCarousel(
|
|
126
|
+
{
|
|
127
|
+
...opts,
|
|
128
|
+
axis: orientation === "horizontal" ? "x" : "y"
|
|
129
|
+
},
|
|
130
|
+
plugins
|
|
131
|
+
);
|
|
132
|
+
const [canScrollPrev, setCanScrollPrev] = React.useState(false);
|
|
133
|
+
const [canScrollNext, setCanScrollNext] = React.useState(false);
|
|
134
|
+
const onSelect = React.useCallback((api2) => {
|
|
135
|
+
if (!api2) return;
|
|
136
|
+
setCanScrollPrev(api2.canScrollPrev());
|
|
137
|
+
setCanScrollNext(api2.canScrollNext());
|
|
138
|
+
}, []);
|
|
139
|
+
const scrollPrev = React.useCallback(() => {
|
|
140
|
+
api?.scrollPrev();
|
|
141
|
+
}, [api]);
|
|
142
|
+
const scrollNext = React.useCallback(() => {
|
|
143
|
+
api?.scrollNext();
|
|
144
|
+
}, [api]);
|
|
145
|
+
const handleKeyDown = React.useCallback(
|
|
146
|
+
(event) => {
|
|
147
|
+
if (event.key === "ArrowLeft") {
|
|
148
|
+
event.preventDefault();
|
|
149
|
+
scrollPrev();
|
|
150
|
+
} else if (event.key === "ArrowRight") {
|
|
151
|
+
event.preventDefault();
|
|
152
|
+
scrollNext();
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
[scrollPrev, scrollNext]
|
|
156
|
+
);
|
|
157
|
+
React.useEffect(() => {
|
|
158
|
+
if (!api || !setApi) return;
|
|
159
|
+
setApi(api);
|
|
160
|
+
}, [api, setApi]);
|
|
161
|
+
React.useEffect(() => {
|
|
162
|
+
if (!api) return;
|
|
163
|
+
onSelect(api);
|
|
164
|
+
api.on("reInit", onSelect);
|
|
165
|
+
api.on("select", onSelect);
|
|
166
|
+
return () => {
|
|
167
|
+
api?.off("select", onSelect);
|
|
168
|
+
};
|
|
169
|
+
}, [api, onSelect]);
|
|
170
|
+
return /* @__PURE__ */ jsx(
|
|
171
|
+
CarouselContext.Provider,
|
|
172
|
+
{
|
|
173
|
+
value: {
|
|
174
|
+
carouselRef,
|
|
175
|
+
api,
|
|
176
|
+
opts,
|
|
177
|
+
orientation: orientation || (opts?.axis === "y" ? "vertical" : "horizontal"),
|
|
178
|
+
scrollPrev,
|
|
179
|
+
scrollNext,
|
|
180
|
+
canScrollPrev,
|
|
181
|
+
canScrollNext
|
|
182
|
+
},
|
|
183
|
+
children: /* @__PURE__ */ jsx(
|
|
184
|
+
"div",
|
|
185
|
+
{
|
|
186
|
+
onKeyDownCapture: handleKeyDown,
|
|
187
|
+
className: cn("relative", className),
|
|
188
|
+
role: "region",
|
|
189
|
+
"aria-roledescription": "carousel",
|
|
190
|
+
"data-slot": "carousel",
|
|
191
|
+
...props,
|
|
192
|
+
children
|
|
193
|
+
}
|
|
194
|
+
)
|
|
195
|
+
}
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
function CarouselContent({ className, ...props }) {
|
|
199
|
+
const { carouselRef, orientation } = useCarousel();
|
|
200
|
+
return /* @__PURE__ */ jsx(
|
|
201
|
+
"div",
|
|
202
|
+
{
|
|
203
|
+
ref: carouselRef,
|
|
204
|
+
className: "overflow-hidden",
|
|
205
|
+
"data-slot": "carousel-content",
|
|
206
|
+
children: /* @__PURE__ */ jsx(
|
|
207
|
+
"div",
|
|
208
|
+
{
|
|
209
|
+
className: cn(
|
|
210
|
+
"flex",
|
|
211
|
+
orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col",
|
|
212
|
+
className
|
|
213
|
+
),
|
|
214
|
+
...props
|
|
215
|
+
}
|
|
216
|
+
)
|
|
217
|
+
}
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
function CarouselItem({ className, ...props }) {
|
|
221
|
+
const { orientation } = useCarousel();
|
|
222
|
+
return /* @__PURE__ */ jsx(
|
|
223
|
+
"div",
|
|
224
|
+
{
|
|
225
|
+
role: "group",
|
|
226
|
+
"aria-roledescription": "slide",
|
|
227
|
+
"data-slot": "carousel-item",
|
|
228
|
+
className: cn(
|
|
229
|
+
"min-w-0 shrink-0 grow-0 basis-full",
|
|
230
|
+
orientation === "horizontal" ? "pl-4" : "pt-4",
|
|
231
|
+
className
|
|
232
|
+
),
|
|
233
|
+
...props
|
|
234
|
+
}
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
function CarouselPrevious({
|
|
238
|
+
className,
|
|
239
|
+
variant = "outline",
|
|
240
|
+
size = "icon",
|
|
241
|
+
...props
|
|
242
|
+
}) {
|
|
243
|
+
const { orientation, scrollPrev, canScrollPrev } = useCarousel();
|
|
244
|
+
return /* @__PURE__ */ jsxs(
|
|
245
|
+
Button,
|
|
246
|
+
{
|
|
247
|
+
"data-slot": "carousel-previous",
|
|
248
|
+
variant,
|
|
249
|
+
size,
|
|
250
|
+
className: cn(
|
|
251
|
+
"absolute size-8 rounded-full",
|
|
252
|
+
orientation === "horizontal" ? "top-1/2 -left-12 -translate-y-1/2" : "-top-12 left-1/2 -translate-x-1/2 rotate-90",
|
|
253
|
+
className
|
|
254
|
+
),
|
|
255
|
+
disabled: !canScrollPrev,
|
|
256
|
+
onClick: scrollPrev,
|
|
257
|
+
...props,
|
|
258
|
+
children: [
|
|
259
|
+
/* @__PURE__ */ jsx(ArrowLeft, {}),
|
|
260
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "Previous slide" })
|
|
261
|
+
]
|
|
262
|
+
}
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
function CarouselNext({
|
|
266
|
+
className,
|
|
267
|
+
variant = "outline",
|
|
268
|
+
size = "icon",
|
|
269
|
+
...props
|
|
270
|
+
}) {
|
|
271
|
+
const { orientation, scrollNext, canScrollNext } = useCarousel();
|
|
272
|
+
return /* @__PURE__ */ jsxs(
|
|
273
|
+
Button,
|
|
274
|
+
{
|
|
275
|
+
"data-slot": "carousel-next",
|
|
276
|
+
variant,
|
|
277
|
+
size,
|
|
278
|
+
className: cn(
|
|
279
|
+
"absolute size-8 rounded-full",
|
|
280
|
+
orientation === "horizontal" ? "top-1/2 -right-12 -translate-y-1/2" : "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",
|
|
281
|
+
className
|
|
282
|
+
),
|
|
283
|
+
disabled: !canScrollNext,
|
|
284
|
+
onClick: scrollNext,
|
|
285
|
+
...props,
|
|
286
|
+
children: [
|
|
287
|
+
/* @__PURE__ */ jsx(ArrowRight, {}),
|
|
288
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "Next slide" })
|
|
289
|
+
]
|
|
290
|
+
}
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
function FeatureShowcase({
|
|
294
|
+
items,
|
|
295
|
+
children,
|
|
296
|
+
className,
|
|
297
|
+
carouselClassName,
|
|
298
|
+
slideClassName,
|
|
299
|
+
contentClassName,
|
|
300
|
+
mediaClassName,
|
|
301
|
+
arrowClassName,
|
|
302
|
+
equalizeOnMobile = true,
|
|
303
|
+
stretchMediaOnMobile = true
|
|
304
|
+
}) {
|
|
305
|
+
const baseArrowClassName = "bottom-4 top-auto size-12 translate-y-0 rounded-full border border-current bg-transparent text-current shadow-sm focus:ring-current focus:ring-offset-2 focus:ring-offset-transparent hover:bg-current/10 md:bottom-6";
|
|
306
|
+
const [mobileSlideHeight, setMobileSlideHeight] = useState(
|
|
307
|
+
null
|
|
308
|
+
);
|
|
309
|
+
const slideRefs = useRef([]);
|
|
310
|
+
const mediaWrapperClassName = equalizeOnMobile && stretchMediaOnMobile ? "flex-1 min-h-0 md:flex-none" : "";
|
|
311
|
+
useEffect(() => {
|
|
312
|
+
if (!equalizeOnMobile) {
|
|
313
|
+
setMobileSlideHeight(null);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
const updateHeights = () => {
|
|
317
|
+
if (typeof window === "undefined") return;
|
|
318
|
+
const isMobile = window.innerWidth < 768;
|
|
319
|
+
if (!isMobile) {
|
|
320
|
+
setMobileSlideHeight(null);
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
const heights = slideRefs.current.slice(0, items.length).map((node) => node?.offsetHeight ?? 0);
|
|
324
|
+
const maxHeight = Math.max(...heights, 0);
|
|
325
|
+
if (maxHeight > 0) {
|
|
326
|
+
setMobileSlideHeight(
|
|
327
|
+
(prev) => prev === maxHeight ? prev : maxHeight
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
updateHeights();
|
|
332
|
+
window.addEventListener("resize", updateHeights);
|
|
333
|
+
let resizeObserver = null;
|
|
334
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
335
|
+
resizeObserver = new ResizeObserver(updateHeights);
|
|
336
|
+
slideRefs.current.slice(0, items.length).forEach((node) => {
|
|
337
|
+
if (node) resizeObserver?.observe(node);
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
return () => {
|
|
341
|
+
window.removeEventListener("resize", updateHeights);
|
|
342
|
+
resizeObserver?.disconnect();
|
|
343
|
+
};
|
|
344
|
+
}, [equalizeOnMobile, items.length]);
|
|
345
|
+
return /* @__PURE__ */ jsxs("div", { className, children: [
|
|
346
|
+
children,
|
|
347
|
+
/* @__PURE__ */ jsxs(Carousel, { className: carouselClassName, children: [
|
|
348
|
+
/* @__PURE__ */ jsx("div", { className: "pb-18 md:pb-24", children: /* @__PURE__ */ jsx(CarouselContent, { className: "ease-in", children: items.map((item, itemIndex) => /* @__PURE__ */ jsx(CarouselItem, { children: /* @__PURE__ */ jsxs(
|
|
349
|
+
"div",
|
|
350
|
+
{
|
|
351
|
+
ref: (node) => {
|
|
352
|
+
slideRefs.current[itemIndex] = node;
|
|
353
|
+
},
|
|
354
|
+
style: equalizeOnMobile && mobileSlideHeight ? { minHeight: mobileSlideHeight } : void 0,
|
|
355
|
+
className: cn(
|
|
356
|
+
"flex flex-col gap-8 md:gap-14 md:flex-row md:items-center md:justify-between",
|
|
357
|
+
slideClassName
|
|
358
|
+
),
|
|
359
|
+
children: [
|
|
360
|
+
/* @__PURE__ */ jsx("div", { className: cn("w-full", contentClassName), children: item.content }),
|
|
361
|
+
/* @__PURE__ */ jsx(
|
|
362
|
+
"div",
|
|
363
|
+
{
|
|
364
|
+
className: cn(
|
|
365
|
+
"w-full",
|
|
366
|
+
mediaWrapperClassName,
|
|
367
|
+
mediaClassName
|
|
368
|
+
),
|
|
369
|
+
children: item.mediaComponent
|
|
370
|
+
}
|
|
371
|
+
)
|
|
372
|
+
]
|
|
373
|
+
}
|
|
374
|
+
) }, `slide-${itemIndex}`)) }) }),
|
|
375
|
+
/* @__PURE__ */ jsx(
|
|
376
|
+
CarouselPrevious,
|
|
377
|
+
{
|
|
378
|
+
className: cn(baseArrowClassName, "left-4 md:left-6", arrowClassName)
|
|
379
|
+
}
|
|
380
|
+
),
|
|
381
|
+
/* @__PURE__ */ jsx(
|
|
382
|
+
CarouselNext,
|
|
383
|
+
{
|
|
384
|
+
className: cn(baseArrowClassName, "right-4 md:right-6", arrowClassName)
|
|
385
|
+
}
|
|
386
|
+
)
|
|
387
|
+
] })
|
|
388
|
+
] });
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
export { FeatureShowcase };
|
|
392
|
+
//# sourceMappingURL=feature-showcase.js.map
|
|
393
|
+
//# sourceMappingURL=feature-showcase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../lib/utils.ts","../icons/arrow-left.tsx","../icons/arrow-right.tsx","../components/ui/button.tsx","../components/ui/carousel.tsx","../components/blocks/features/feature-showcase.tsx"],"names":["jsx","api","useState","useEffect","jsxs"],"mappings":"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","file":"feature-showcase.js","sourcesContent":["import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import type { SVGProps } from \"react\";\n\nexport interface IconProps extends SVGProps<SVGSVGElement> {\n size?: number | string;\n}\n\nexport const ArrowLeft = ({\n size = 24,\n className,\n strokeWidth = 2,\n ...props\n}: IconProps) => {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={strokeWidth}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n {...props}\n >\n <path d=\"m12 19l-7-7l7-7m7 7H5\" />\n </svg>\n );\n};\n","import type { SVGProps } from \"react\";\n\nexport interface IconProps extends SVGProps<SVGSVGElement> {\n size?: number | string;\n}\n\nexport const ArrowRight = ({\n size = 24,\n className,\n strokeWidth = 2,\n ...props\n}: IconProps) => {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={strokeWidth}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n {...props}\n >\n <path d=\"M5 12h14m-7-7l7 7l-7 7\" />\n </svg>\n );\n};\n","import * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"../../lib/utils\"\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-button text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n destructive:\n \"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60\",\n outline:\n \"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50\",\n secondary:\n \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n ghost:\n \"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-9 px-4 py-2 has-[>svg]:px-3\",\n sm: \"h-8 rounded-button gap-1.5 px-3 has-[>svg]:px-2.5\",\n lg: \"h-10 rounded-button px-6 has-[>svg]:px-4\",\n icon: \"size-9\",\n \"icon-sm\": \"size-8\",\n \"icon-lg\": \"size-10\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n)\n\nfunction Button({\n className,\n variant = \"default\",\n size = \"default\",\n asChild = false,\n ...props\n}: React.ComponentProps<\"button\"> &\n VariantProps<typeof buttonVariants> & {\n asChild?: boolean\n }) {\n const Comp = asChild ? Slot : \"button\"\n\n return (\n <Comp\n data-slot=\"button\"\n data-variant={variant}\n data-size={size}\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n />\n )\n}\n\nexport { Button, buttonVariants }\n","import * as React from \"react\"\nimport useEmblaCarousel, {\n type UseEmblaCarouselType,\n} from \"embla-carousel-react\"\nimport { ArrowLeft } from \"../../icons/arrow-left\"\nimport { ArrowRight } from \"../../icons/arrow-right\"\n\nimport { cn } from \"../../lib/utils\"\nimport { Button } from \"./button\"\n\ntype CarouselApi = UseEmblaCarouselType[1]\ntype UseCarouselParameters = Parameters<typeof useEmblaCarousel>\ntype CarouselOptions = UseCarouselParameters[0]\ntype CarouselPlugin = UseCarouselParameters[1]\n\ntype CarouselProps = {\n opts?: CarouselOptions\n plugins?: CarouselPlugin\n orientation?: \"horizontal\" | \"vertical\"\n setApi?: (api: CarouselApi) => void\n}\n\ntype CarouselContextProps = {\n carouselRef: ReturnType<typeof useEmblaCarousel>[0]\n api: ReturnType<typeof useEmblaCarousel>[1]\n scrollPrev: () => void\n scrollNext: () => void\n canScrollPrev: boolean\n canScrollNext: boolean\n} & CarouselProps\n\nconst CarouselContext = React.createContext<CarouselContextProps | null>(null)\n\nfunction useCarousel() {\n const context = React.useContext(CarouselContext)\n\n if (!context) {\n throw new Error(\"useCarousel must be used within a <Carousel />\")\n }\n\n return context\n}\n\nfunction Carousel({\n orientation = \"horizontal\",\n opts,\n setApi,\n plugins,\n className,\n children,\n ...props\n}: React.ComponentProps<\"div\"> & CarouselProps) {\n const [carouselRef, api] = useEmblaCarousel(\n {\n ...opts,\n axis: orientation === \"horizontal\" ? \"x\" : \"y\",\n },\n plugins\n )\n const [canScrollPrev, setCanScrollPrev] = React.useState(false)\n const [canScrollNext, setCanScrollNext] = React.useState(false)\n\n const onSelect = React.useCallback((api: CarouselApi) => {\n if (!api) return\n setCanScrollPrev(api.canScrollPrev())\n setCanScrollNext(api.canScrollNext())\n }, [])\n\n const scrollPrev = React.useCallback(() => {\n api?.scrollPrev()\n }, [api])\n\n const scrollNext = React.useCallback(() => {\n api?.scrollNext()\n }, [api])\n\n const handleKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (event.key === \"ArrowLeft\") {\n event.preventDefault()\n scrollPrev()\n } else if (event.key === \"ArrowRight\") {\n event.preventDefault()\n scrollNext()\n }\n },\n [scrollPrev, scrollNext]\n )\n\n React.useEffect(() => {\n if (!api || !setApi) return\n setApi(api)\n }, [api, setApi])\n\n React.useEffect(() => {\n if (!api) return\n onSelect(api)\n api.on(\"reInit\", onSelect)\n api.on(\"select\", onSelect)\n\n return () => {\n api?.off(\"select\", onSelect)\n }\n }, [api, onSelect])\n\n return (\n <CarouselContext.Provider\n value={{\n carouselRef,\n api: api,\n opts,\n orientation:\n orientation || (opts?.axis === \"y\" ? \"vertical\" : \"horizontal\"),\n scrollPrev,\n scrollNext,\n canScrollPrev,\n canScrollNext,\n }}\n >\n <div\n onKeyDownCapture={handleKeyDown}\n className={cn(\"relative\", className)}\n role=\"region\"\n aria-roledescription=\"carousel\"\n data-slot=\"carousel\"\n {...props}\n >\n {children}\n </div>\n </CarouselContext.Provider>\n )\n}\n\nfunction CarouselContent({ className, ...props }: React.ComponentProps<\"div\">) {\n const { carouselRef, orientation } = useCarousel()\n\n return (\n <div\n ref={carouselRef}\n className=\"overflow-hidden\"\n data-slot=\"carousel-content\"\n >\n <div\n className={cn(\n \"flex\",\n orientation === \"horizontal\" ? \"-ml-4\" : \"-mt-4 flex-col\",\n className\n )}\n {...props}\n />\n </div>\n )\n}\n\nfunction CarouselItem({ className, ...props }: React.ComponentProps<\"div\">) {\n const { orientation } = useCarousel()\n\n return (\n <div\n role=\"group\"\n aria-roledescription=\"slide\"\n data-slot=\"carousel-item\"\n className={cn(\n \"min-w-0 shrink-0 grow-0 basis-full\",\n orientation === \"horizontal\" ? \"pl-4\" : \"pt-4\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction CarouselPrevious({\n className,\n variant = \"outline\",\n size = \"icon\",\n ...props\n}: React.ComponentProps<typeof Button>) {\n const { orientation, scrollPrev, canScrollPrev } = useCarousel()\n\n return (\n <Button\n data-slot=\"carousel-previous\"\n variant={variant}\n size={size}\n className={cn(\n \"absolute size-8 rounded-full\",\n orientation === \"horizontal\"\n ? \"top-1/2 -left-12 -translate-y-1/2\"\n : \"-top-12 left-1/2 -translate-x-1/2 rotate-90\",\n className\n )}\n disabled={!canScrollPrev}\n onClick={scrollPrev}\n {...props}\n >\n <ArrowLeft />\n <span className=\"sr-only\">Previous slide</span>\n </Button>\n )\n}\n\nfunction CarouselNext({\n className,\n variant = \"outline\",\n size = \"icon\",\n ...props\n}: React.ComponentProps<typeof Button>) {\n const { orientation, scrollNext, canScrollNext } = useCarousel()\n\n return (\n <Button\n data-slot=\"carousel-next\"\n variant={variant}\n size={size}\n className={cn(\n \"absolute size-8 rounded-full\",\n orientation === \"horizontal\"\n ? \"top-1/2 -right-12 -translate-y-1/2\"\n : \"-bottom-12 left-1/2 -translate-x-1/2 rotate-90\",\n className\n )}\n disabled={!canScrollNext}\n onClick={scrollNext}\n {...props}\n >\n <ArrowRight />\n <span className=\"sr-only\">Next slide</span>\n </Button>\n )\n}\n\nexport {\n type CarouselApi,\n Carousel,\n CarouselContent,\n CarouselItem,\n CarouselPrevious,\n CarouselNext,\n}\n","\"use client\";\n\nimport { useEffect, useRef, useState, type ReactNode } from \"react\";\nimport { cn } from \"../../../lib/utils\";\nimport {\n Carousel,\n CarouselContent,\n CarouselItem,\n CarouselNext,\n CarouselPrevious,\n} from \"../../ui/carousel\";\n\nexport interface FeatureShowcaseItem {\n content: ReactNode;\n mediaComponent: ReactNode;\n}\n\nexport interface FeatureShowcaseProps {\n items: FeatureShowcaseItem[];\n children?: ReactNode;\n className?: string;\n carouselClassName?: string;\n slideClassName?: string;\n contentClassName?: string;\n mediaClassName?: string;\n arrowClassName?: string;\n equalizeOnMobile?: boolean;\n stretchMediaOnMobile?: boolean;\n}\n\n/**\n * Feature Showcase component with carousel navigation\n *\n * Displays feature content with media in a carousel format. Each slide shows\n * content (text, headings) alongside media (images, videos). Features mobile\n * height equalization for consistent slide heights and customizable styling.\n *\n * @example\n * ```tsx\n * <FeatureShowcase\n * items={[\n * {\n * content: <div><h3>Feature 1</h3><p>Description</p></div>,\n * mediaComponent: <img src=\"/feature1.jpg\" alt=\"Feature 1\" />\n * },\n * {\n * content: <div><h3>Feature 2</h3><p>Description</p></div>,\n * mediaComponent: <img src=\"/feature2.jpg\" alt=\"Feature 2\" />\n * }\n * ]}\n * />\n * ```\n */\nexport function FeatureShowcase({\n items,\n children,\n className,\n carouselClassName,\n slideClassName,\n contentClassName,\n mediaClassName,\n arrowClassName,\n equalizeOnMobile = true,\n stretchMediaOnMobile = true,\n}: FeatureShowcaseProps) {\n const baseArrowClassName =\n \"bottom-4 top-auto size-12 translate-y-0 rounded-full border border-current bg-transparent text-current shadow-sm focus:ring-current focus:ring-offset-2 focus:ring-offset-transparent hover:bg-current/10 md:bottom-6\";\n const [mobileSlideHeight, setMobileSlideHeight] = useState<number | null>(\n null\n );\n const slideRefs = useRef<Array<HTMLDivElement | null>>([]);\n const mediaWrapperClassName =\n equalizeOnMobile && stretchMediaOnMobile\n ? \"flex-1 min-h-0 md:flex-none\"\n : \"\";\n\n useEffect(() => {\n if (!equalizeOnMobile) {\n setMobileSlideHeight(null);\n return;\n }\n\n const updateHeights = () => {\n if (typeof window === \"undefined\") return;\n const isMobile = window.innerWidth < 768;\n\n if (!isMobile) {\n setMobileSlideHeight(null);\n return;\n }\n\n const heights = slideRefs.current\n .slice(0, items.length)\n .map((node) => node?.offsetHeight ?? 0);\n const maxHeight = Math.max(...heights, 0);\n\n if (maxHeight > 0) {\n setMobileSlideHeight((prev) =>\n prev === maxHeight ? prev : maxHeight\n );\n }\n };\n\n updateHeights();\n window.addEventListener(\"resize\", updateHeights);\n\n let resizeObserver: ResizeObserver | null = null;\n if (typeof ResizeObserver !== \"undefined\") {\n resizeObserver = new ResizeObserver(updateHeights);\n slideRefs.current.slice(0, items.length).forEach((node) => {\n if (node) resizeObserver?.observe(node);\n });\n }\n\n return () => {\n window.removeEventListener(\"resize\", updateHeights);\n resizeObserver?.disconnect();\n };\n }, [equalizeOnMobile, items.length]);\n\n return (\n <div className={className}>\n {children}\n <Carousel className={carouselClassName}>\n <div className=\"pb-18 md:pb-24\">\n <CarouselContent className=\"ease-in\">\n {items.map((item, itemIndex) => (\n <CarouselItem key={`slide-${itemIndex}`}>\n <div\n ref={(node) => {\n slideRefs.current[itemIndex] = node;\n }}\n style={\n equalizeOnMobile && mobileSlideHeight\n ? { minHeight: mobileSlideHeight }\n : undefined\n }\n className={cn(\n \"flex flex-col gap-8 md:gap-14 md:flex-row md:items-center md:justify-between\",\n slideClassName\n )}\n >\n <div className={cn(\"w-full\", contentClassName)}>\n {item.content}\n </div>\n <div\n className={cn(\n \"w-full\",\n mediaWrapperClassName,\n mediaClassName\n )}\n >\n {item.mediaComponent}\n </div>\n </div>\n </CarouselItem>\n ))}\n </CarouselContent>\n </div>\n <CarouselPrevious\n className={cn(baseArrowClassName, \"left-4 md:left-6\", arrowClassName)}\n />\n <CarouselNext\n className={cn(baseArrowClassName, \"right-4 md:right-6\", arrowClassName)}\n />\n </Carousel>\n </div>\n );\n}\n"]}
|
package/dist/index.cjs
CHANGED
|
@@ -279,6 +279,8 @@ function PageHeroBanner({
|
|
|
279
279
|
showOverlay = true,
|
|
280
280
|
overlayOpacity = 0.6,
|
|
281
281
|
contentMaxWidth = "4xl",
|
|
282
|
+
overlayClassName,
|
|
283
|
+
contentClassName,
|
|
282
284
|
style,
|
|
283
285
|
...props
|
|
284
286
|
}) {
|
|
@@ -324,7 +326,10 @@ function PageHeroBanner({
|
|
|
324
326
|
showOverlay && /* @__PURE__ */ jsxRuntime.jsx(
|
|
325
327
|
"div",
|
|
326
328
|
{
|
|
327
|
-
className:
|
|
329
|
+
className: cn(
|
|
330
|
+
"absolute inset-0 bg-gradient-to-b from-black via-black to-black",
|
|
331
|
+
overlayClassName
|
|
332
|
+
),
|
|
328
333
|
style: { opacity: overlayOpacity }
|
|
329
334
|
}
|
|
330
335
|
),
|
|
@@ -332,7 +337,7 @@ function PageHeroBanner({
|
|
|
332
337
|
Container,
|
|
333
338
|
{
|
|
334
339
|
maxWidth: contentMaxWidth,
|
|
335
|
-
className: "relative h-full flex items-center",
|
|
340
|
+
className: cn("relative h-full flex items-center", contentClassName),
|
|
336
341
|
style: { minHeight },
|
|
337
342
|
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative text-background drop-shadow-lg py-16 md:py-24", children })
|
|
338
343
|
}
|
|
@@ -342,7 +347,7 @@ function PageHeroBanner({
|
|
|
342
347
|
);
|
|
343
348
|
}
|
|
344
349
|
var buttonVariants = classVarianceAuthority.cva(
|
|
345
|
-
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-
|
|
350
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-button text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
346
351
|
{
|
|
347
352
|
variants: {
|
|
348
353
|
variant: {
|
|
@@ -355,8 +360,8 @@ var buttonVariants = classVarianceAuthority.cva(
|
|
|
355
360
|
},
|
|
356
361
|
size: {
|
|
357
362
|
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
358
|
-
sm: "h-8 rounded-
|
|
359
|
-
lg: "h-10 rounded-
|
|
363
|
+
sm: "h-8 rounded-button gap-1.5 px-3 has-[>svg]:px-2.5",
|
|
364
|
+
lg: "h-10 rounded-button px-6 has-[>svg]:px-4",
|
|
360
365
|
icon: "size-9",
|
|
361
366
|
"icon-sm": "size-8",
|
|
362
367
|
"icon-lg": "size-10"
|