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