@cntyclub/ui-react 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-HDGMSYQS.js +26461 -0
- package/dist/chunk-HDGMSYQS.js.map +1 -0
- package/dist/chunk-PR4QN5HX.js +39 -0
- package/dist/chunk-PR4QN5HX.js.map +1 -0
- package/dist/form.d.ts +175 -0
- package/dist/form.js +5207 -0
- package/dist/form.js.map +1 -0
- package/dist/index.d.ts +1462 -0
- package/dist/index.js +81862 -0
- package/dist/index.js.map +1 -0
- package/dist/input-CZvh825j.d.ts +24 -0
- package/dist/qr-code-styling-3Y6LZH6V.js +1123 -0
- package/dist/qr-code-styling-3Y6LZH6V.js.map +1 -0
- package/package.json +79 -0
- package/src/components/form/checkbox-group-field.tsx +101 -0
- package/src/components/form/date-field.tsx +79 -0
- package/src/components/form/date-range-field.tsx +106 -0
- package/src/components/form/form-context.ts +10 -0
- package/src/components/form/form.tsx +54 -0
- package/src/components/form/number-field.tsx +69 -0
- package/src/components/form/select-field.tsx +76 -0
- package/src/components/form/submit-button.tsx +28 -0
- package/src/components/form/text-field.tsx +107 -0
- package/src/components/layout/dashboard-header.tsx +54 -0
- package/src/components/layout/dashboard-panel.tsx +34 -0
- package/src/components/theme-provider.tsx +403 -0
- package/src/components/ui/accordion.tsx +69 -0
- package/src/components/ui/alert-dialog.tsx +169 -0
- package/src/components/ui/alert.tsx +80 -0
- package/src/components/ui/animated-theme-toggler.tsx +265 -0
- package/src/components/ui/app-store-buttons.tsx +182 -0
- package/src/components/ui/aspect-ratio.tsx +23 -0
- package/src/components/ui/autocomplete.tsx +296 -0
- package/src/components/ui/avatar-group.tsx +95 -0
- package/src/components/ui/avatar.tsx +285 -0
- package/src/components/ui/badge-group.tsx +160 -0
- package/src/components/ui/badge.tsx +172 -0
- package/src/components/ui/breadcrumb.tsx +112 -0
- package/src/components/ui/button.tsx +77 -0
- package/src/components/ui/calendar.tsx +137 -0
- package/src/components/ui/card.tsx +244 -0
- package/src/components/ui/carousel.tsx +258 -0
- package/src/components/ui/chart.tsx +379 -0
- package/src/components/ui/checkbox-group.tsx +16 -0
- package/src/components/ui/checkbox.tsx +82 -0
- package/src/components/ui/collapsible.tsx +45 -0
- package/src/components/ui/combobox.tsx +411 -0
- package/src/components/ui/command.tsx +264 -0
- package/src/components/ui/context-menu.tsx +271 -0
- package/src/components/ui/credit-card.tsx +214 -0
- package/src/components/ui/dialog.tsx +196 -0
- package/src/components/ui/drawer.tsx +135 -0
- package/src/components/ui/empty.tsx +127 -0
- package/src/components/ui/featured-icon.tsx +149 -0
- package/src/components/ui/field.tsx +88 -0
- package/src/components/ui/fieldset.tsx +29 -0
- package/src/components/ui/form.tsx +17 -0
- package/src/components/ui/frame.tsx +82 -0
- package/src/components/ui/generic-empty.tsx +142 -0
- package/src/components/ui/group.tsx +97 -0
- package/src/components/ui/horizontal-scroll-fader.tsx +228 -0
- package/src/components/ui/input-group.tsx +102 -0
- package/src/components/ui/input-otp.tsx +96 -0
- package/src/components/ui/input.tsx +66 -0
- package/src/components/ui/item.tsx +198 -0
- package/src/components/ui/kbd.tsx +30 -0
- package/src/components/ui/label.tsx +28 -0
- package/src/components/ui/menu.tsx +312 -0
- package/src/components/ui/menubar.tsx +93 -0
- package/src/components/ui/meter.tsx +67 -0
- package/src/components/ui/multi-select.tsx +308 -0
- package/src/components/ui/navigation-menu.tsx +143 -0
- package/src/components/ui/number-field.tsx +160 -0
- package/src/components/ui/pagination-controls.tsx +74 -0
- package/src/components/ui/pagination.tsx +149 -0
- package/src/components/ui/popover.tsx +119 -0
- package/src/components/ui/preview-card.tsx +55 -0
- package/src/components/ui/progress.tsx +289 -0
- package/src/components/ui/qr-code.tsx +150 -0
- package/src/components/ui/radio-group.tsx +103 -0
- package/src/components/ui/resizable.tsx +56 -0
- package/src/components/ui/scroll-area.tsx +90 -0
- package/src/components/ui/scroller.tsx +38 -0
- package/src/components/ui/section-header.tsx +118 -0
- package/src/components/ui/select.tsx +181 -0
- package/src/components/ui/separator.tsx +23 -0
- package/src/components/ui/sheet.tsx +224 -0
- package/src/components/ui/sidebar.tsx +744 -0
- package/src/components/ui/skeleton.tsx +16 -0
- package/src/components/ui/slider.tsx +108 -0
- package/src/components/ui/smooth-scroll.tsx +143 -0
- package/src/components/ui/social-button.tsx +247 -0
- package/src/components/ui/spinner-on-demand.tsx +32 -0
- package/src/components/ui/spinner.tsx +18 -0
- package/src/components/ui/stat.tsx +187 -0
- package/src/components/ui/stepper.tsx +167 -0
- package/src/components/ui/switch.tsx +56 -0
- package/src/components/ui/table.tsx +126 -0
- package/src/components/ui/tabs.tsx +90 -0
- package/src/components/ui/tag.tsx +229 -0
- package/src/components/ui/target-countdown.tsx +46 -0
- package/src/components/ui/text-editor.tsx +313 -0
- package/src/components/ui/textarea.tsx +51 -0
- package/src/components/ui/timeline.tsx +116 -0
- package/src/components/ui/toast.tsx +268 -0
- package/src/components/ui/toggle-group.tsx +101 -0
- package/src/components/ui/toggle.tsx +45 -0
- package/src/components/ui/toolbar.tsx +89 -0
- package/src/components/ui/tooltip.tsx +102 -0
- package/src/components/ui/vertical-scroll-fader.tsx +250 -0
- package/src/components/ui/video-player.tsx +275 -0
- package/src/components/upload/avatar-upload-base.tsx +131 -0
- package/src/components/upload/image-upload-base.tsx +112 -0
- package/src/form.ts +17 -0
- package/src/index.ts +125 -0
- package/src/lib/hooks/use-callback-ref.ts +15 -0
- package/src/lib/hooks/use-first-render.ts +11 -0
- package/src/lib/hooks/use-hover.ts +53 -0
- package/src/lib/hooks/use-is-tab-active.ts +17 -0
- package/src/lib/hooks/use-media-query.ts +164 -0
- package/src/lib/utils/css.ts +6 -0
- package/src/styles.css +300 -0
- package/src/types/helpers.ts +24 -0
- package/src/types/react.d.ts +7 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { mergeProps } from "@base-ui/react/merge-props";
|
|
4
|
+
import { useRender } from "@base-ui/react/use-render";
|
|
5
|
+
|
|
6
|
+
import { cn } from "../../lib/utils/css";
|
|
7
|
+
|
|
8
|
+
function Card({
|
|
9
|
+
className,
|
|
10
|
+
render,
|
|
11
|
+
...props
|
|
12
|
+
}: useRender.ComponentProps<"div">) {
|
|
13
|
+
const defaultProps = {
|
|
14
|
+
className: cn(
|
|
15
|
+
"relative flex flex-col gap-4 rounded-2xl border bg-card not-dark:bg-clip-padding text-card-foreground shadow-xs/5 before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-2xl)-1px)] before:shadow-[0_1px_--theme(--color-black/6%)] dark:before:shadow-[0_-1px_--theme(--color-white/6%)]",
|
|
16
|
+
className,
|
|
17
|
+
),
|
|
18
|
+
"data-slot": "card",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
return useRender({
|
|
22
|
+
defaultTagName: "div",
|
|
23
|
+
props: mergeProps<"div">(defaultProps, props),
|
|
24
|
+
render,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function CardFrame({
|
|
29
|
+
className,
|
|
30
|
+
render,
|
|
31
|
+
...props
|
|
32
|
+
}: useRender.ComponentProps<"div">) {
|
|
33
|
+
const defaultProps = {
|
|
34
|
+
className: cn(
|
|
35
|
+
"flex flex-col relative rounded-2xl border bg-background before:absolute before:inset-0 before:rounded-[inherit] before:bg-muted/72 before:pointer-events-none not-dark:bg-clip-padding text-card-foreground shadow-xs/5 before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-2xl)-1px)] before:shadow-[0_1px_--theme(--color-black/6%)] dark:before:shadow-[0_-1px_--theme(--color-white/6%)] *:data-[slot=card]:-m-px *:not-last:data-[slot=card]:rounded-b-lg *:not-last:data-[slot=card]:before:rounded-b-[calc(var(--radius-lg)-1px)] *:not-first:data-[slot=card]:rounded-t-lg *:not-first:data-[slot=card]:before:rounded-t-[calc(var(--radius-lg)-1px)] *:data-[slot=card]:[clip-path:inset(-1rem_1px)] *:data-[slot=card]:first:[clip-path:inset(1px_1px_-1rem_1px_round_calc(var(--radius-2xl)-1px))] *:data-[slot=card]:last:[clip-path:inset(-1rem_1px_1px_1px_round_calc(var(--radius-2xl)-1px))] *:data-[slot=card]:shadow-none *:data-[slot=card]:before:hidden *:data-[slot=card]:bg-clip-padding",
|
|
36
|
+
className,
|
|
37
|
+
),
|
|
38
|
+
"data-slot": "card-frame",
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
return useRender({
|
|
42
|
+
defaultTagName: "div",
|
|
43
|
+
props: mergeProps<"div">(defaultProps, props),
|
|
44
|
+
render,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function CardFrameHeader({
|
|
49
|
+
className,
|
|
50
|
+
render,
|
|
51
|
+
...props
|
|
52
|
+
}: useRender.ComponentProps<"div">) {
|
|
53
|
+
const defaultProps = {
|
|
54
|
+
className: cn("flex flex-col px-6 py-4", className),
|
|
55
|
+
"data-slot": "card-frame-header",
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
return useRender({
|
|
59
|
+
defaultTagName: "div",
|
|
60
|
+
props: mergeProps<"div">(defaultProps, props),
|
|
61
|
+
render,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function CardFrameTitle({
|
|
66
|
+
className,
|
|
67
|
+
render,
|
|
68
|
+
...props
|
|
69
|
+
}: useRender.ComponentProps<"div">) {
|
|
70
|
+
const defaultProps = {
|
|
71
|
+
className: cn("font-semibold text-sm", className),
|
|
72
|
+
"data-slot": "card-frame-title",
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
return useRender({
|
|
76
|
+
defaultTagName: "div",
|
|
77
|
+
props: mergeProps<"div">(defaultProps, props),
|
|
78
|
+
render,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function CardFrameDescription({
|
|
83
|
+
className,
|
|
84
|
+
render,
|
|
85
|
+
...props
|
|
86
|
+
}: useRender.ComponentProps<"div">) {
|
|
87
|
+
const defaultProps = {
|
|
88
|
+
className: cn("text-muted-foreground text-sm", className),
|
|
89
|
+
"data-slot": "card-frame-description",
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
return useRender({
|
|
93
|
+
defaultTagName: "div",
|
|
94
|
+
props: mergeProps<"div">(defaultProps, props),
|
|
95
|
+
render,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function CardFrameFooter({
|
|
100
|
+
className,
|
|
101
|
+
render,
|
|
102
|
+
...props
|
|
103
|
+
}: useRender.ComponentProps<"div">) {
|
|
104
|
+
const defaultProps = {
|
|
105
|
+
className: cn("px-6 py-4", className),
|
|
106
|
+
"data-slot": "card-frame-footer",
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
return useRender({
|
|
110
|
+
defaultTagName: "div",
|
|
111
|
+
props: mergeProps<"div">(defaultProps, props),
|
|
112
|
+
render,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function CardHeader({
|
|
117
|
+
className,
|
|
118
|
+
render,
|
|
119
|
+
...props
|
|
120
|
+
}: useRender.ComponentProps<"div">) {
|
|
121
|
+
const defaultProps = {
|
|
122
|
+
className: cn(
|
|
123
|
+
"grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 p-6 in-[[data-slot=card]:has(>[data-slot=card-panel])]:pb-4 has-data-[slot=card-action]:grid-cols-[1fr_auto]",
|
|
124
|
+
className,
|
|
125
|
+
),
|
|
126
|
+
"data-slot": "card-header",
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
return useRender({
|
|
130
|
+
defaultTagName: "div",
|
|
131
|
+
props: mergeProps<"div">(defaultProps, props),
|
|
132
|
+
render,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function CardTitle({
|
|
137
|
+
className,
|
|
138
|
+
render,
|
|
139
|
+
...props
|
|
140
|
+
}: useRender.ComponentProps<"div">) {
|
|
141
|
+
const defaultProps = {
|
|
142
|
+
className: cn("font-semibold text-lg leading-none", className),
|
|
143
|
+
"data-slot": "card-title",
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
return useRender({
|
|
147
|
+
defaultTagName: "div",
|
|
148
|
+
props: mergeProps<"div">(defaultProps, props),
|
|
149
|
+
render,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function CardDescription({
|
|
154
|
+
className,
|
|
155
|
+
render,
|
|
156
|
+
...props
|
|
157
|
+
}: useRender.ComponentProps<"div">) {
|
|
158
|
+
const defaultProps = {
|
|
159
|
+
className: cn("text-muted-foreground text-sm", className),
|
|
160
|
+
"data-slot": "card-description",
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
return useRender({
|
|
164
|
+
defaultTagName: "div",
|
|
165
|
+
props: mergeProps<"div">(defaultProps, props),
|
|
166
|
+
render,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function CardAction({
|
|
171
|
+
className,
|
|
172
|
+
render,
|
|
173
|
+
...props
|
|
174
|
+
}: useRender.ComponentProps<"div">) {
|
|
175
|
+
const defaultProps = {
|
|
176
|
+
className: cn(
|
|
177
|
+
"col-start-2 row-span-2 row-start-1 self-start justify-self-end inline-flex",
|
|
178
|
+
className,
|
|
179
|
+
),
|
|
180
|
+
"data-slot": "card-action",
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
return useRender({
|
|
184
|
+
defaultTagName: "div",
|
|
185
|
+
props: mergeProps<"div">(defaultProps, props),
|
|
186
|
+
render,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function CardPanel({
|
|
191
|
+
className,
|
|
192
|
+
render,
|
|
193
|
+
...props
|
|
194
|
+
}: useRender.ComponentProps<"div">) {
|
|
195
|
+
const defaultProps = {
|
|
196
|
+
className: cn(
|
|
197
|
+
"flex-1 p-6 in-[[data-slot=card]:has(>[data-slot=card-header]:not(.border-b))]:pt-0 in-[[data-slot=card]:has(>[data-slot=card-footer]:not(.border-t))]:pb-0",
|
|
198
|
+
className,
|
|
199
|
+
),
|
|
200
|
+
"data-slot": "card-panel",
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
return useRender({
|
|
204
|
+
defaultTagName: "div",
|
|
205
|
+
props: mergeProps<"div">(defaultProps, props),
|
|
206
|
+
render,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function CardFooter({
|
|
211
|
+
className,
|
|
212
|
+
render,
|
|
213
|
+
...props
|
|
214
|
+
}: useRender.ComponentProps<"div">) {
|
|
215
|
+
const defaultProps = {
|
|
216
|
+
className: cn(
|
|
217
|
+
"flex items-center p-6 in-[[data-slot=card]:has(>[data-slot=card-panel])]:pt-4",
|
|
218
|
+
className,
|
|
219
|
+
),
|
|
220
|
+
"data-slot": "card-footer",
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
return useRender({
|
|
224
|
+
defaultTagName: "div",
|
|
225
|
+
props: mergeProps<"div">(defaultProps, props),
|
|
226
|
+
render,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export {
|
|
231
|
+
Card,
|
|
232
|
+
CardFrame,
|
|
233
|
+
CardFrameHeader,
|
|
234
|
+
CardFrameTitle,
|
|
235
|
+
CardFrameDescription,
|
|
236
|
+
CardFrameFooter,
|
|
237
|
+
CardAction,
|
|
238
|
+
CardDescription,
|
|
239
|
+
CardFooter,
|
|
240
|
+
CardHeader,
|
|
241
|
+
CardPanel,
|
|
242
|
+
CardPanel as CardContent,
|
|
243
|
+
CardTitle,
|
|
244
|
+
};
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import useEmblaCarousel, {
|
|
4
|
+
type UseEmblaCarouselType,
|
|
5
|
+
} from "embla-carousel-react";
|
|
6
|
+
import { ArrowLeftIcon, ArrowRightIcon } from "lucide-react";
|
|
7
|
+
import * as React from "react";
|
|
8
|
+
|
|
9
|
+
import { cn } from "../../lib/utils/css";
|
|
10
|
+
import { Button } from "./button";
|
|
11
|
+
|
|
12
|
+
type CarouselApi = UseEmblaCarouselType[1];
|
|
13
|
+
type UseCarouselParameters = Parameters<typeof useEmblaCarousel>;
|
|
14
|
+
type CarouselOptions = UseCarouselParameters[0];
|
|
15
|
+
type CarouselPlugin = UseCarouselParameters[1];
|
|
16
|
+
|
|
17
|
+
type CarouselProps = {
|
|
18
|
+
opts?: CarouselOptions;
|
|
19
|
+
plugins?: CarouselPlugin;
|
|
20
|
+
orientation?: "horizontal" | "vertical";
|
|
21
|
+
setApi?: (api: CarouselApi) => void;
|
|
22
|
+
/** Fades the leading/trailing edges of the viewport for a soft overflow. */
|
|
23
|
+
fade?: boolean;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
type CarouselContextProps = {
|
|
27
|
+
carouselRef: ReturnType<typeof useEmblaCarousel>[0];
|
|
28
|
+
api: ReturnType<typeof useEmblaCarousel>[1];
|
|
29
|
+
scrollPrev: () => void;
|
|
30
|
+
scrollNext: () => void;
|
|
31
|
+
canScrollPrev: boolean;
|
|
32
|
+
canScrollNext: boolean;
|
|
33
|
+
} & CarouselProps;
|
|
34
|
+
|
|
35
|
+
const CarouselContext = React.createContext<CarouselContextProps | null>(null);
|
|
36
|
+
|
|
37
|
+
function useCarousel() {
|
|
38
|
+
const context = React.useContext(CarouselContext);
|
|
39
|
+
|
|
40
|
+
if (!context) {
|
|
41
|
+
throw new Error("useCarousel must be used within a <Carousel />");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return context;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function Carousel({
|
|
48
|
+
orientation = "horizontal",
|
|
49
|
+
opts,
|
|
50
|
+
setApi,
|
|
51
|
+
plugins,
|
|
52
|
+
fade = false,
|
|
53
|
+
className,
|
|
54
|
+
children,
|
|
55
|
+
...props
|
|
56
|
+
}: React.ComponentProps<"div"> & CarouselProps) {
|
|
57
|
+
const [carouselRef, api] = useEmblaCarousel(
|
|
58
|
+
{
|
|
59
|
+
...opts,
|
|
60
|
+
axis: orientation === "horizontal" ? "x" : "y",
|
|
61
|
+
},
|
|
62
|
+
plugins,
|
|
63
|
+
);
|
|
64
|
+
const [canScrollPrev, setCanScrollPrev] = React.useState(false);
|
|
65
|
+
const [canScrollNext, setCanScrollNext] = React.useState(false);
|
|
66
|
+
|
|
67
|
+
const onSelect = React.useCallback((api: CarouselApi) => {
|
|
68
|
+
if (!api) return;
|
|
69
|
+
setCanScrollPrev(api.canScrollPrev());
|
|
70
|
+
setCanScrollNext(api.canScrollNext());
|
|
71
|
+
}, []);
|
|
72
|
+
|
|
73
|
+
const scrollPrev = React.useCallback(() => {
|
|
74
|
+
api?.scrollPrev();
|
|
75
|
+
}, [api]);
|
|
76
|
+
|
|
77
|
+
const scrollNext = React.useCallback(() => {
|
|
78
|
+
api?.scrollNext();
|
|
79
|
+
}, [api]);
|
|
80
|
+
|
|
81
|
+
const handleKeyDown = React.useCallback(
|
|
82
|
+
(event: React.KeyboardEvent<HTMLDivElement>) => {
|
|
83
|
+
if (event.key === "ArrowLeft") {
|
|
84
|
+
event.preventDefault();
|
|
85
|
+
scrollPrev();
|
|
86
|
+
} else if (event.key === "ArrowRight") {
|
|
87
|
+
event.preventDefault();
|
|
88
|
+
scrollNext();
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
[scrollPrev, scrollNext],
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
React.useEffect(() => {
|
|
95
|
+
if (!api || !setApi) return;
|
|
96
|
+
setApi(api);
|
|
97
|
+
}, [api, setApi]);
|
|
98
|
+
|
|
99
|
+
React.useEffect(() => {
|
|
100
|
+
if (!api) return;
|
|
101
|
+
onSelect(api);
|
|
102
|
+
api.on("reInit", onSelect);
|
|
103
|
+
api.on("select", onSelect);
|
|
104
|
+
|
|
105
|
+
return () => {
|
|
106
|
+
api?.off("select", onSelect);
|
|
107
|
+
};
|
|
108
|
+
}, [api, onSelect]);
|
|
109
|
+
|
|
110
|
+
return (
|
|
111
|
+
<CarouselContext.Provider
|
|
112
|
+
value={{
|
|
113
|
+
carouselRef,
|
|
114
|
+
api: api,
|
|
115
|
+
opts,
|
|
116
|
+
orientation:
|
|
117
|
+
orientation || (opts?.axis === "y" ? "vertical" : "horizontal"),
|
|
118
|
+
scrollPrev,
|
|
119
|
+
scrollNext,
|
|
120
|
+
canScrollPrev,
|
|
121
|
+
canScrollNext,
|
|
122
|
+
fade,
|
|
123
|
+
}}
|
|
124
|
+
>
|
|
125
|
+
{/* biome-ignore lint/a11y/useSemanticElements: carousel region with keyboard support */}
|
|
126
|
+
<div
|
|
127
|
+
aria-roledescription="carousel"
|
|
128
|
+
className={cn("relative", className)}
|
|
129
|
+
data-slot="carousel"
|
|
130
|
+
onKeyDownCapture={handleKeyDown}
|
|
131
|
+
role="region"
|
|
132
|
+
{...props}
|
|
133
|
+
>
|
|
134
|
+
{children}
|
|
135
|
+
</div>
|
|
136
|
+
</CarouselContext.Provider>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function CarouselContent({
|
|
141
|
+
className,
|
|
142
|
+
...props
|
|
143
|
+
}: React.ComponentProps<"div">) {
|
|
144
|
+
const { carouselRef, orientation, fade } = useCarousel();
|
|
145
|
+
|
|
146
|
+
const maskImage = fade
|
|
147
|
+
? orientation === "horizontal"
|
|
148
|
+
? "linear-gradient(to right, transparent 0, #000 var(--carousel-fade), #000 calc(100% - var(--carousel-fade)), transparent 100%)"
|
|
149
|
+
: "linear-gradient(to bottom, transparent 0, #000 var(--carousel-fade), #000 calc(100% - var(--carousel-fade)), transparent 100%)"
|
|
150
|
+
: undefined;
|
|
151
|
+
|
|
152
|
+
return (
|
|
153
|
+
<div
|
|
154
|
+
className="overflow-hidden [--carousel-fade:--spacing(8)]"
|
|
155
|
+
data-slot="carousel-content"
|
|
156
|
+
ref={carouselRef}
|
|
157
|
+
style={maskImage ? { maskImage, WebkitMaskImage: maskImage } : undefined}
|
|
158
|
+
>
|
|
159
|
+
<div
|
|
160
|
+
className={cn(
|
|
161
|
+
"flex",
|
|
162
|
+
orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col",
|
|
163
|
+
className,
|
|
164
|
+
)}
|
|
165
|
+
{...props}
|
|
166
|
+
/>
|
|
167
|
+
</div>
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function CarouselItem({ className, ...props }: React.ComponentProps<"div">) {
|
|
172
|
+
const { orientation } = useCarousel();
|
|
173
|
+
|
|
174
|
+
return (
|
|
175
|
+
// biome-ignore lint/a11y/useSemanticElements: embla slide
|
|
176
|
+
<div
|
|
177
|
+
aria-roledescription="slide"
|
|
178
|
+
className={cn(
|
|
179
|
+
"min-w-0 shrink-0 grow-0 basis-full",
|
|
180
|
+
orientation === "horizontal" ? "pl-4" : "pt-4",
|
|
181
|
+
className,
|
|
182
|
+
)}
|
|
183
|
+
data-slot="carousel-item"
|
|
184
|
+
role="group"
|
|
185
|
+
{...props}
|
|
186
|
+
/>
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function CarouselPrevious({
|
|
191
|
+
className,
|
|
192
|
+
variant = "outline",
|
|
193
|
+
size = "icon",
|
|
194
|
+
...props
|
|
195
|
+
}: React.ComponentProps<typeof Button>) {
|
|
196
|
+
const { orientation, scrollPrev, canScrollPrev } = useCarousel();
|
|
197
|
+
|
|
198
|
+
return (
|
|
199
|
+
<Button
|
|
200
|
+
className={cn(
|
|
201
|
+
"absolute size-8 rounded-full before:rounded-full",
|
|
202
|
+
orientation === "horizontal"
|
|
203
|
+
? "-left-12 -translate-y-1/2 top-1/2"
|
|
204
|
+
: "-top-12 -translate-x-1/2 left-1/2 rotate-90",
|
|
205
|
+
className,
|
|
206
|
+
)}
|
|
207
|
+
data-slot="carousel-previous"
|
|
208
|
+
disabled={!canScrollPrev}
|
|
209
|
+
onClick={scrollPrev}
|
|
210
|
+
size={size}
|
|
211
|
+
variant={variant}
|
|
212
|
+
{...props}
|
|
213
|
+
>
|
|
214
|
+
<ArrowLeftIcon />
|
|
215
|
+
<span className="sr-only">Previous slide</span>
|
|
216
|
+
</Button>
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function CarouselNext({
|
|
221
|
+
className,
|
|
222
|
+
variant = "outline",
|
|
223
|
+
size = "icon",
|
|
224
|
+
...props
|
|
225
|
+
}: React.ComponentProps<typeof Button>) {
|
|
226
|
+
const { orientation, scrollNext, canScrollNext } = useCarousel();
|
|
227
|
+
|
|
228
|
+
return (
|
|
229
|
+
<Button
|
|
230
|
+
className={cn(
|
|
231
|
+
"absolute size-8 rounded-full before:rounded-full",
|
|
232
|
+
orientation === "horizontal"
|
|
233
|
+
? "-right-12 -translate-y-1/2 top-1/2"
|
|
234
|
+
: "-bottom-12 -translate-x-1/2 left-1/2 rotate-90",
|
|
235
|
+
className,
|
|
236
|
+
)}
|
|
237
|
+
data-slot="carousel-next"
|
|
238
|
+
disabled={!canScrollNext}
|
|
239
|
+
onClick={scrollNext}
|
|
240
|
+
size={size}
|
|
241
|
+
variant={variant}
|
|
242
|
+
{...props}
|
|
243
|
+
>
|
|
244
|
+
<ArrowRightIcon />
|
|
245
|
+
<span className="sr-only">Next slide</span>
|
|
246
|
+
</Button>
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export {
|
|
251
|
+
type CarouselApi,
|
|
252
|
+
Carousel,
|
|
253
|
+
CarouselContent,
|
|
254
|
+
CarouselItem,
|
|
255
|
+
CarouselPrevious,
|
|
256
|
+
CarouselNext,
|
|
257
|
+
useCarousel,
|
|
258
|
+
};
|