@neynar/ui 0.1.1 → 0.1.2
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/components/ui/accordion.d.ts +1 -25
- package/dist/components/ui/accordion.d.ts.map +1 -1
- package/dist/components/ui/alert-dialog.d.ts +240 -46
- package/dist/components/ui/alert-dialog.d.ts.map +1 -1
- package/dist/components/ui/alert.d.ts +73 -11
- package/dist/components/ui/alert.d.ts.map +1 -1
- package/dist/components/ui/aspect-ratio.d.ts +44 -10
- package/dist/components/ui/aspect-ratio.d.ts.map +1 -1
- package/dist/components/ui/avatar.d.ts +117 -33
- package/dist/components/ui/avatar.d.ts.map +1 -1
- package/dist/components/ui/badge.d.ts +50 -71
- package/dist/components/ui/badge.d.ts.map +1 -1
- package/dist/components/ui/breadcrumb.d.ts +231 -49
- package/dist/components/ui/breadcrumb.d.ts.map +1 -1
- package/dist/components/ui/button.d.ts +189 -71
- package/dist/components/ui/button.d.ts.map +1 -1
- package/dist/components/ui/calendar.d.ts +197 -40
- package/dist/components/ui/calendar.d.ts.map +1 -1
- package/dist/components/ui/card.d.ts +7 -22
- package/dist/components/ui/card.d.ts.map +1 -1
- package/dist/components/ui/carousel.d.ts +369 -99
- package/dist/components/ui/carousel.d.ts.map +1 -1
- package/dist/components/ui/chart.d.ts.map +1 -1
- package/dist/components/ui/checkbox.d.ts +110 -38
- package/dist/components/ui/checkbox.d.ts.map +1 -1
- package/dist/components/ui/collapsible.d.ts +246 -61
- package/dist/components/ui/collapsible.d.ts.map +1 -1
- package/dist/components/ui/combobox.d.ts +207 -159
- package/dist/components/ui/combobox.d.ts.map +1 -1
- package/dist/components/ui/command.d.ts +336 -67
- package/dist/components/ui/command.d.ts.map +1 -1
- package/dist/components/ui/container.d.ts +159 -64
- package/dist/components/ui/container.d.ts.map +1 -1
- package/dist/components/ui/context-menu.d.ts +321 -39
- package/dist/components/ui/context-menu.d.ts.map +1 -1
- package/dist/components/ui/date-picker.d.ts +113 -86
- package/dist/components/ui/date-picker.d.ts.map +1 -1
- package/dist/components/ui/dialog.d.ts +106 -25
- package/dist/components/ui/dialog.d.ts.map +1 -1
- package/dist/components/ui/drawer.d.ts +388 -59
- package/dist/components/ui/drawer.d.ts.map +1 -1
- package/dist/components/ui/dropdown-menu.d.ts +521 -74
- package/dist/components/ui/dropdown-menu.d.ts.map +1 -1
- package/dist/components/ui/empty-state.d.ts +148 -76
- package/dist/components/ui/empty-state.d.ts.map +1 -1
- package/dist/components/ui/hover-card.d.ts +253 -34
- package/dist/components/ui/hover-card.d.ts.map +1 -1
- package/dist/components/ui/input.d.ts +143 -44
- package/dist/components/ui/input.d.ts.map +1 -1
- package/dist/components/ui/label.d.ts +0 -8
- package/dist/components/ui/label.d.ts.map +1 -1
- package/dist/components/ui/menubar.d.ts +288 -46
- package/dist/components/ui/menubar.d.ts.map +1 -1
- package/dist/components/ui/navigation-menu.d.ts +444 -127
- package/dist/components/ui/navigation-menu.d.ts.map +1 -1
- package/dist/components/ui/pagination.d.ts +342 -66
- package/dist/components/ui/pagination.d.ts.map +1 -1
- package/dist/components/ui/popover.d.ts +0 -8
- package/dist/components/ui/popover.d.ts.map +1 -1
- package/dist/components/ui/progress.d.ts +88 -30
- package/dist/components/ui/progress.d.ts.map +1 -1
- package/dist/components/ui/radio-group.d.ts +189 -45
- package/dist/components/ui/radio-group.d.ts.map +1 -1
- package/dist/components/ui/resizable.d.ts +178 -62
- package/dist/components/ui/resizable.d.ts.map +1 -1
- package/dist/components/ui/scroll-area.d.ts +180 -21
- package/dist/components/ui/scroll-area.d.ts.map +1 -1
- package/dist/components/ui/select.d.ts +382 -60
- package/dist/components/ui/select.d.ts.map +1 -1
- package/dist/components/ui/separator.d.ts +52 -39
- package/dist/components/ui/separator.d.ts.map +1 -1
- package/dist/components/ui/sheet.d.ts +144 -27
- package/dist/components/ui/sheet.d.ts.map +1 -1
- package/dist/components/ui/sidebar.d.ts +81 -31
- package/dist/components/ui/sidebar.d.ts.map +1 -1
- package/dist/components/ui/skeleton.d.ts +94 -32
- package/dist/components/ui/skeleton.d.ts.map +1 -1
- package/dist/components/ui/slider.d.ts +37 -31
- package/dist/components/ui/slider.d.ts.map +1 -1
- package/dist/components/ui/sonner.d.ts +280 -46
- package/dist/components/ui/sonner.d.ts.map +1 -1
- package/dist/components/ui/stack.d.ts +289 -148
- package/dist/components/ui/stack.d.ts.map +1 -1
- package/dist/components/ui/stories/aspect-ratio.stories.d.ts +1 -2
- package/dist/components/ui/stories/aspect-ratio.stories.d.ts.map +1 -1
- package/dist/components/ui/stories/container.stories.d.ts +2 -3
- package/dist/components/ui/stories/container.stories.d.ts.map +1 -1
- package/dist/components/ui/stories/empty-state.stories.d.ts +2 -2
- package/dist/components/ui/stories/scroll-area.stories.d.ts +1 -2
- package/dist/components/ui/stories/scroll-area.stories.d.ts.map +1 -1
- package/dist/components/ui/stories/stack.stories.d.ts +1 -1
- package/dist/components/ui/stories/text-field.stories.d.ts +7 -1
- package/dist/components/ui/stories/text-field.stories.d.ts.map +1 -1
- package/dist/components/ui/switch.d.ts +44 -38
- package/dist/components/ui/switch.d.ts.map +1 -1
- package/dist/components/ui/table.d.ts +33 -0
- package/dist/components/ui/table.d.ts.map +1 -1
- package/dist/components/ui/tabs.d.ts +4 -22
- package/dist/components/ui/tabs.d.ts.map +1 -1
- package/dist/components/ui/text-field.d.ts +170 -84
- package/dist/components/ui/text-field.d.ts.map +1 -1
- package/dist/components/ui/textarea.d.ts +106 -29
- package/dist/components/ui/textarea.d.ts.map +1 -1
- package/dist/components/ui/theme-toggle.d.ts +190 -65
- package/dist/components/ui/theme-toggle.d.ts.map +1 -1
- package/dist/components/ui/theme.d.ts +107 -23
- package/dist/components/ui/theme.d.ts.map +1 -1
- package/dist/components/ui/toggle-group.d.ts +143 -67
- package/dist/components/ui/toggle-group.d.ts.map +1 -1
- package/dist/components/ui/toggle.d.ts +118 -30
- package/dist/components/ui/toggle.d.ts.map +1 -1
- package/dist/components/ui/tooltip.d.ts +152 -28
- package/dist/components/ui/tooltip.d.ts.map +1 -1
- package/dist/components/ui/typography.d.ts +452 -134
- package/dist/components/ui/typography.d.ts.map +1 -1
- package/dist/index.js +9388 -8281
- package/dist/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/llms.txt +173 -3
- package/package.json +5 -2
- package/src/components/ui/accordion.tsx +112 -27
- package/src/components/ui/alert-dialog.tsx +401 -46
- package/src/components/ui/alert.tsx +114 -11
- package/src/components/ui/aspect-ratio.tsx +69 -14
- package/src/components/ui/avatar.tsx +179 -33
- package/src/components/ui/badge.tsx +74 -75
- package/src/components/ui/breadcrumb.tsx +335 -50
- package/src/components/ui/button.tsx +198 -90
- package/src/components/ui/calendar.tsx +867 -43
- package/src/components/ui/card.tsx +140 -33
- package/src/components/ui/carousel.tsx +529 -98
- package/src/components/ui/chart.tsx +222 -1
- package/src/components/ui/checkbox.tsx +176 -38
- package/src/components/ui/collapsible.tsx +321 -67
- package/src/components/ui/combobox.tsx +284 -83
- package/src/components/ui/command.tsx +527 -67
- package/src/components/ui/container.tsx +217 -65
- package/src/components/ui/context-menu.tsx +716 -51
- package/src/components/ui/date-picker.tsx +228 -38
- package/src/components/ui/dialog.tsx +270 -33
- package/src/components/ui/drawer.tsx +546 -67
- package/src/components/ui/dropdown-menu.tsx +657 -74
- package/src/components/ui/empty-state.tsx +241 -82
- package/src/components/ui/hover-card.tsx +328 -39
- package/src/components/ui/input.tsx +207 -44
- package/src/components/ui/label.tsx +98 -8
- package/src/components/ui/menubar.tsx +587 -54
- package/src/components/ui/navigation-menu.tsx +557 -128
- package/src/components/ui/pagination.tsx +561 -79
- package/src/components/ui/popover.tsx +119 -8
- package/src/components/ui/progress.tsx +131 -29
- package/src/components/ui/radio-group.tsx +260 -51
- package/src/components/ui/resizable.tsx +289 -63
- package/src/components/ui/scroll-area.tsx +377 -66
- package/src/components/ui/select.tsx +545 -60
- package/src/components/ui/separator.tsx +146 -40
- package/src/components/ui/sheet.tsx +348 -31
- package/src/components/ui/sidebar.tsx +471 -29
- package/src/components/ui/skeleton.tsx +114 -32
- package/src/components/ui/slider.tsx +77 -31
- package/src/components/ui/sonner.tsx +574 -46
- package/src/components/ui/stack.tsx +423 -101
- package/src/components/ui/switch.tsx +78 -39
- package/src/components/ui/table.tsx +170 -4
- package/src/components/ui/tabs.tsx +108 -22
- package/src/components/ui/text-field.tsx +226 -81
- package/src/components/ui/textarea.tsx +180 -29
- package/src/components/ui/theme-toggle.tsx +313 -65
- package/src/components/ui/theme.tsx +117 -23
- package/src/components/ui/toggle-group.tsx +280 -69
- package/src/components/ui/toggle.tsx +124 -35
- package/src/components/ui/tooltip.tsx +239 -29
- package/src/components/ui/typography.tsx +1115 -165
|
@@ -8,24 +8,238 @@ import { ArrowLeft, ArrowRight } from "lucide-react";
|
|
|
8
8
|
import { cn } from "@/lib/utils";
|
|
9
9
|
import { Button } from "@/components/ui/button";
|
|
10
10
|
|
|
11
|
+
/** Embla Carousel API instance type for programmatic control */
|
|
11
12
|
type CarouselApi = UseEmblaCarouselType[1];
|
|
13
|
+
|
|
14
|
+
/** Parameters for the useEmblaCarousel hook */
|
|
12
15
|
type UseCarouselParameters = Parameters<typeof useEmblaCarousel>;
|
|
16
|
+
|
|
17
|
+
/** Embla Carousel configuration options */
|
|
13
18
|
type CarouselOptions = UseCarouselParameters[0];
|
|
19
|
+
|
|
20
|
+
/** Array of Embla Carousel plugins */
|
|
14
21
|
type CarouselPlugin = UseCarouselParameters[1];
|
|
15
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Props for Carousel component (Documentation only - NOT used in component implementation)
|
|
25
|
+
* These types are for documentation generation and should not replace embla-carousel inferred types
|
|
26
|
+
*/
|
|
27
|
+
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
28
|
+
type CarouselDocsProps = {
|
|
29
|
+
/**
|
|
30
|
+
* Embla Carousel configuration options
|
|
31
|
+
*
|
|
32
|
+
* Supports all Embla options including:
|
|
33
|
+
* - `active`: Enable/disable the carousel (default: true)
|
|
34
|
+
* - `align`: Slide alignment - "start", "center", "end", or custom function (default: "center")
|
|
35
|
+
* - `axis`: Scroll direction - "x" for horizontal, "y" for vertical (default: "x")
|
|
36
|
+
* - `breakpoints`: Responsive configuration object with media queries
|
|
37
|
+
* - `container`: Custom container element selector or HTMLElement
|
|
38
|
+
* - `containScroll`: Handle empty space - false, "trimSnaps", "keepSnaps" (default: "trimSnaps")
|
|
39
|
+
* - `direction`: Content direction - "ltr" or "rtl" (default: "ltr")
|
|
40
|
+
* - `dragFree`: Enable momentum scrolling (default: false)
|
|
41
|
+
* - `dragThreshold`: Drag threshold in pixels (default: 10)
|
|
42
|
+
* - `duration`: Scroll duration for API methods, 20-60 recommended (default: 25)
|
|
43
|
+
* - `inViewThreshold`: Intersection Observer threshold (default: 0)
|
|
44
|
+
* - `loop`: Enable infinite looping (default: false)
|
|
45
|
+
* - `skipSnaps`: Allow skipping snaps on vigorous drag (default: false)
|
|
46
|
+
* - `slides`: Custom slide elements selector or HTMLElement array
|
|
47
|
+
* - `slidesToScroll`: Number of slides to scroll or "auto" (default: 1)
|
|
48
|
+
* - `startIndex`: Initial scroll snap index (default: 0)
|
|
49
|
+
* - `watchDrag`: Enable drag interactions (default: true)
|
|
50
|
+
* - `watchFocus`: Enable focus-based scrolling (default: true)
|
|
51
|
+
* - `watchResize`: Enable auto-reinit on resize (default: true)
|
|
52
|
+
* - `watchSlides`: Enable auto-reinit when slides change (default: true)
|
|
53
|
+
* @default {}
|
|
54
|
+
*/
|
|
55
|
+
opts?: CarouselOptions;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Array of Embla Carousel plugins for extended functionality
|
|
59
|
+
*
|
|
60
|
+
* Common plugins include:
|
|
61
|
+
* - `Autoplay`: Automatic slide progression
|
|
62
|
+
* - `AutoScroll`: Continuous smooth scrolling
|
|
63
|
+
* - `ClassNames`: CSS class management based on carousel state
|
|
64
|
+
* - `WheelGestures`: Mouse wheel navigation
|
|
65
|
+
* - `Fade`: Fade transition effects
|
|
66
|
+
* @default undefined
|
|
67
|
+
*/
|
|
68
|
+
plugins?: CarouselPlugin;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Carousel orientation/layout direction
|
|
72
|
+
*
|
|
73
|
+
* - "horizontal": Default horizontal layout with left/right navigation
|
|
74
|
+
* - "vertical": Vertical layout with up/down navigation
|
|
75
|
+
* @default "horizontal"
|
|
76
|
+
*/
|
|
77
|
+
orientation?: "horizontal" | "vertical";
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Callback to receive the Embla Carousel API instance
|
|
81
|
+
*
|
|
82
|
+
* The API provides methods for programmatic control:
|
|
83
|
+
* - Navigation: `scrollNext()`, `scrollPrev()`, `scrollTo(index)`
|
|
84
|
+
* - State: `selectedScrollSnap()`, `canScrollNext()`, `canScrollPrev()`
|
|
85
|
+
* - Events: `on(event, callback)`, `off(event, callback)`
|
|
86
|
+
* - Elements: `slideNodes()`, `rootNode()`, `containerNode()`
|
|
87
|
+
* @param api - The Embla Carousel API instance
|
|
88
|
+
*/
|
|
89
|
+
setApi?: (api: CarouselApi) => void;
|
|
90
|
+
|
|
91
|
+
/** Additional CSS classes for styling */
|
|
92
|
+
className?: string;
|
|
93
|
+
|
|
94
|
+
/** Carousel content and navigation elements */
|
|
95
|
+
children?: React.ReactNode;
|
|
96
|
+
} & Omit<React.ComponentProps<"div">, "className" | "children" | "orientation">;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Props for CarouselContent component (Documentation only - NOT used in component implementation)
|
|
100
|
+
* These types are for documentation generation and should not replace inferred types
|
|
101
|
+
*/
|
|
102
|
+
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
103
|
+
type CarouselContentDocsProps = {
|
|
104
|
+
/**
|
|
105
|
+
* Additional CSS classes for custom spacing and styling
|
|
106
|
+
*
|
|
107
|
+
* Common patterns:
|
|
108
|
+
* - Horizontal: `-ml-2 md:-ml-4` for negative margin spacing
|
|
109
|
+
* - Vertical: `-mt-4 h-[300px]` for vertical layout with fixed height
|
|
110
|
+
* - Gap control: Adjust negative margins to control slide spacing
|
|
111
|
+
*/
|
|
112
|
+
className?: string;
|
|
113
|
+
|
|
114
|
+
/** CarouselItem components and other slide content */
|
|
115
|
+
children?: React.ReactNode;
|
|
116
|
+
} & Omit<React.HTMLAttributes<HTMLDivElement>, "className" | "children">;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Props for CarouselItem component (Documentation only - NOT used in component implementation)
|
|
120
|
+
* These types are for documentation generation and should not replace inferred types
|
|
121
|
+
*/
|
|
122
|
+
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
123
|
+
type CarouselItemDocsProps = {
|
|
124
|
+
/**
|
|
125
|
+
* Additional CSS classes for sizing, spacing, and styling
|
|
126
|
+
*
|
|
127
|
+
* Common sizing patterns:
|
|
128
|
+
* - `basis-full`: Full width slide (default)
|
|
129
|
+
* - `basis-1/2`: Half width (2 slides visible)
|
|
130
|
+
* - `basis-1/3`: Third width (3 slides visible)
|
|
131
|
+
* - `basis-auto`: Auto-sizing based on content
|
|
132
|
+
* - `md:basis-1/2 lg:basis-1/3`: Responsive sizing
|
|
133
|
+
*
|
|
134
|
+
* Spacing patterns:
|
|
135
|
+
* - `pl-2 md:pl-4`: Left padding (horizontal carousels)
|
|
136
|
+
* - `pt-4`: Top padding (vertical carousels)
|
|
137
|
+
*/
|
|
138
|
+
className?: string;
|
|
139
|
+
|
|
140
|
+
/** Content to display within the slide */
|
|
141
|
+
children?: React.ReactNode;
|
|
142
|
+
} & Omit<React.HTMLAttributes<HTMLDivElement>, "className" | "children">;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Props for CarouselPrevious component (Documentation only - NOT used in component implementation)
|
|
146
|
+
* These types are for documentation generation and should not replace inferred types
|
|
147
|
+
*/
|
|
148
|
+
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
149
|
+
type CarouselPreviousDocsProps = {
|
|
150
|
+
/**
|
|
151
|
+
* Additional CSS classes for custom positioning and styling
|
|
152
|
+
*
|
|
153
|
+
* Default positioning:
|
|
154
|
+
* - Horizontal: `top-1/2 -left-12 -translate-y-1/2`
|
|
155
|
+
* - Vertical: `-top-12 left-1/2 -translate-x-1/2 rotate-90`
|
|
156
|
+
*/
|
|
157
|
+
className?: string;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Button variant styling
|
|
161
|
+
* @default "outline"
|
|
162
|
+
*/
|
|
163
|
+
variant?:
|
|
164
|
+
| "default"
|
|
165
|
+
| "destructive"
|
|
166
|
+
| "outline"
|
|
167
|
+
| "secondary"
|
|
168
|
+
| "ghost"
|
|
169
|
+
| "link";
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Button size
|
|
173
|
+
* @default "icon"
|
|
174
|
+
*/
|
|
175
|
+
size?: "default" | "sm" | "lg" | "icon";
|
|
176
|
+
} & Omit<
|
|
177
|
+
React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
178
|
+
"className" | "variant" | "size"
|
|
179
|
+
>;
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Props for CarouselNext component (Documentation only - NOT used in component implementation)
|
|
183
|
+
* These types are for documentation generation and should not replace inferred types
|
|
184
|
+
*/
|
|
185
|
+
// eslint-disable-next-line unused-imports/no-unused-vars
|
|
186
|
+
type CarouselNextDocsProps = {
|
|
187
|
+
/**
|
|
188
|
+
* Additional CSS classes for custom positioning and styling
|
|
189
|
+
*
|
|
190
|
+
* Default positioning:
|
|
191
|
+
* - Horizontal: `top-1/2 -right-12 -translate-y-1/2`
|
|
192
|
+
* - Vertical: `-bottom-12 left-1/2 -translate-x-1/2 rotate-90`
|
|
193
|
+
*/
|
|
194
|
+
className?: string;
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Button variant styling
|
|
198
|
+
* @default "outline"
|
|
199
|
+
*/
|
|
200
|
+
variant?:
|
|
201
|
+
| "default"
|
|
202
|
+
| "destructive"
|
|
203
|
+
| "outline"
|
|
204
|
+
| "secondary"
|
|
205
|
+
| "ghost"
|
|
206
|
+
| "link";
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Button size
|
|
210
|
+
* @default "icon"
|
|
211
|
+
*/
|
|
212
|
+
size?: "default" | "sm" | "lg" | "icon";
|
|
213
|
+
} & Omit<
|
|
214
|
+
React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
215
|
+
"className" | "variant" | "size"
|
|
216
|
+
>;
|
|
217
|
+
|
|
218
|
+
/** Props for Carousel component (used in component implementation) */
|
|
16
219
|
type CarouselProps = {
|
|
220
|
+
/** Embla Carousel configuration options */
|
|
17
221
|
opts?: CarouselOptions;
|
|
222
|
+
/** Array of Embla Carousel plugins for extended functionality */
|
|
18
223
|
plugins?: CarouselPlugin;
|
|
224
|
+
/** Carousel orientation/layout direction */
|
|
19
225
|
orientation?: "horizontal" | "vertical";
|
|
226
|
+
/** Callback to receive the Embla Carousel API instance */
|
|
20
227
|
setApi?: (api: CarouselApi) => void;
|
|
21
228
|
};
|
|
22
229
|
|
|
230
|
+
/** Props for the carousel context provider */
|
|
23
231
|
type CarouselContextProps = {
|
|
232
|
+
/** Ref for the Embla carousel viewport element */
|
|
24
233
|
carouselRef: ReturnType<typeof useEmblaCarousel>[0];
|
|
234
|
+
/** Embla Carousel API instance for programmatic control */
|
|
25
235
|
api: ReturnType<typeof useEmblaCarousel>[1];
|
|
236
|
+
/** Function to scroll to the previous slide */
|
|
26
237
|
scrollPrev: () => void;
|
|
238
|
+
/** Function to scroll to the next slide */
|
|
27
239
|
scrollNext: () => void;
|
|
240
|
+
/** Whether the carousel can scroll to previous slide */
|
|
28
241
|
canScrollPrev: boolean;
|
|
242
|
+
/** Whether the carousel can scroll to next slide */
|
|
29
243
|
canScrollNext: boolean;
|
|
30
244
|
} & CarouselProps;
|
|
31
245
|
|
|
@@ -84,15 +298,11 @@ function useCarousel() {
|
|
|
84
298
|
*
|
|
85
299
|
* The Carousel component provides a touch-friendly, keyboard-accessible way to
|
|
86
300
|
* browse through multiple items. Built on Embla Carousel, it supports various
|
|
87
|
-
* configurations including autoplay, loop, drag scrolling,
|
|
88
|
-
* and vertical orientations.
|
|
301
|
+
* configurations including autoplay, loop, drag scrolling, infinite scroll,
|
|
302
|
+
* momentum scrolling, and both horizontal and vertical orientations.
|
|
89
303
|
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* @param orientation - Layout direction: "horizontal" or "vertical" (default: "horizontal")
|
|
93
|
-
* @param setApi - Callback to receive the carousel API instance
|
|
94
|
-
* @param className - Additional CSS classes
|
|
95
|
-
* @param children - Carousel content (typically CarouselContent and navigation buttons)
|
|
304
|
+
* Features include responsive breakpoints, plugin system, smooth animations,
|
|
305
|
+
* focus management, RTL support, and extensive customization options.
|
|
96
306
|
*
|
|
97
307
|
* @example
|
|
98
308
|
* ```tsx
|
|
@@ -112,9 +322,16 @@ function useCarousel() {
|
|
|
112
322
|
*
|
|
113
323
|
* @example
|
|
114
324
|
* ```tsx
|
|
115
|
-
* // Multi-item responsive carousel
|
|
325
|
+
* // Multi-item responsive carousel with custom spacing
|
|
116
326
|
* <Carousel
|
|
117
|
-
* opts={{
|
|
327
|
+
* opts={{
|
|
328
|
+
* align: "start",
|
|
329
|
+
* loop: true,
|
|
330
|
+
* slidesToScroll: "auto",
|
|
331
|
+
* breakpoints: {
|
|
332
|
+
* "(min-width: 768px)": { slidesToScroll: 2 }
|
|
333
|
+
* }
|
|
334
|
+
* }}
|
|
118
335
|
* className="w-full"
|
|
119
336
|
* >
|
|
120
337
|
* <CarouselContent className="-ml-2 md:-ml-4">
|
|
@@ -136,12 +353,13 @@ function useCarousel() {
|
|
|
136
353
|
*
|
|
137
354
|
* @example
|
|
138
355
|
* ```tsx
|
|
139
|
-
* // Vertical carousel with autoplay
|
|
356
|
+
* // Vertical carousel with autoplay plugin
|
|
140
357
|
* import Autoplay from "embla-carousel-autoplay";
|
|
141
358
|
*
|
|
142
359
|
* <Carousel
|
|
143
360
|
* orientation="vertical"
|
|
144
|
-
*
|
|
361
|
+
* opts={{ loop: true, align: "center" }}
|
|
362
|
+
* plugins={[Autoplay({ delay: 3000, stopOnInteraction: true, stopOnMouseEnter: true })]}
|
|
145
363
|
* className="h-[400px]"
|
|
146
364
|
* >
|
|
147
365
|
* <CarouselContent className="h-full">
|
|
@@ -159,47 +377,121 @@ function useCarousel() {
|
|
|
159
377
|
*
|
|
160
378
|
* @example
|
|
161
379
|
* ```tsx
|
|
162
|
-
* // Controlled carousel with custom indicators
|
|
380
|
+
* // Controlled carousel with custom indicators and API access
|
|
381
|
+
* import { useState, useEffect } from "react";
|
|
382
|
+
*
|
|
163
383
|
* function ControlledCarousel() {
|
|
164
384
|
* const [api, setApi] = useState<CarouselApi>();
|
|
165
385
|
* const [current, setCurrent] = useState(0);
|
|
386
|
+
* const [count, setCount] = useState(0);
|
|
166
387
|
*
|
|
167
388
|
* useEffect(() => {
|
|
168
389
|
* if (!api) return;
|
|
169
|
-
*
|
|
170
|
-
* api.
|
|
390
|
+
*
|
|
391
|
+
* setCount(api.scrollSnapList().length);
|
|
392
|
+
* setCurrent(api.selectedScrollSnap() + 1);
|
|
393
|
+
*
|
|
394
|
+
* api.on('select', () => {
|
|
395
|
+
* setCurrent(api.selectedScrollSnap() + 1);
|
|
396
|
+
* });
|
|
171
397
|
* }, [api]);
|
|
172
398
|
*
|
|
173
399
|
* return (
|
|
174
|
-
* <div>
|
|
175
|
-
* <Carousel setApi={setApi}>
|
|
176
|
-
* <CarouselContent
|
|
400
|
+
* <div className="space-y-4">
|
|
401
|
+
* <Carousel setApi={setApi} opts={{ loop: true }}>
|
|
402
|
+
* <CarouselContent>
|
|
403
|
+
* {slides.map((slide, index) => (
|
|
404
|
+
* <CarouselItem key={index}>{slide.content}</CarouselItem>
|
|
405
|
+
* ))}
|
|
406
|
+
* </CarouselContent>
|
|
407
|
+
* <CarouselPrevious />
|
|
408
|
+
* <CarouselNext />
|
|
177
409
|
* </Carousel>
|
|
178
|
-
*
|
|
179
|
-
*
|
|
410
|
+
*
|
|
411
|
+
* // Custom indicators
|
|
412
|
+
* <div className="flex justify-center gap-2">
|
|
413
|
+
* {Array.from({ length: count }).map((_, i) => (
|
|
180
414
|
* <button
|
|
181
415
|
* key={i}
|
|
182
|
-
* className={
|
|
416
|
+
* className={cn(
|
|
417
|
+
* "w-2 h-2 rounded-full transition-colors",
|
|
418
|
+
* current === i + 1 ? "bg-primary" : "bg-muted"
|
|
419
|
+
* )}
|
|
183
420
|
* onClick={() => api?.scrollTo(i)}
|
|
184
421
|
* />
|
|
185
422
|
* ))}
|
|
186
423
|
* </div>
|
|
424
|
+
*
|
|
425
|
+
* <div className="text-center text-sm text-muted-foreground">
|
|
426
|
+
* Slide {current} of {count}
|
|
427
|
+
* </div>
|
|
187
428
|
* </div>
|
|
188
429
|
* );
|
|
189
430
|
* }
|
|
190
431
|
* ```
|
|
191
432
|
*
|
|
433
|
+
* @example
|
|
434
|
+
* ```tsx
|
|
435
|
+
* // Momentum scrolling carousel with drag-free mode
|
|
436
|
+
* <Carousel
|
|
437
|
+
* opts={{
|
|
438
|
+
* dragFree: true,
|
|
439
|
+
* containScroll: false,
|
|
440
|
+
* skipSnaps: true
|
|
441
|
+
* }}
|
|
442
|
+
* className="w-full"
|
|
443
|
+
* >
|
|
444
|
+
* <CarouselContent>
|
|
445
|
+
* {items.map((item, index) => (
|
|
446
|
+
* <CarouselItem key={index} className="basis-auto">
|
|
447
|
+
* <div className="w-32 h-32 bg-muted rounded-lg" />
|
|
448
|
+
* </CarouselItem>
|
|
449
|
+
* ))}
|
|
450
|
+
* </CarouselContent>
|
|
451
|
+
* </Carousel>
|
|
452
|
+
* ```
|
|
453
|
+
*
|
|
454
|
+
* @example
|
|
455
|
+
* ```tsx
|
|
456
|
+
* // Breakpoint-responsive carousel that disables on larger screens
|
|
457
|
+
* <Carousel
|
|
458
|
+
* opts={{
|
|
459
|
+
* active: true,
|
|
460
|
+
* breakpoints: {
|
|
461
|
+
* "(min-width: 768px)": { active: false }
|
|
462
|
+
* }
|
|
463
|
+
* }}
|
|
464
|
+
* className="md:hidden"
|
|
465
|
+
* >
|
|
466
|
+
* <CarouselContent>
|
|
467
|
+
* {mobileItems.map((item, index) => (
|
|
468
|
+
* <CarouselItem key={index}>{item}</CarouselItem>
|
|
469
|
+
* ))}
|
|
470
|
+
* </CarouselContent>
|
|
471
|
+
* <CarouselPrevious />
|
|
472
|
+
* <CarouselNext />
|
|
473
|
+
* </Carousel>
|
|
474
|
+
* ```
|
|
475
|
+
*
|
|
192
476
|
* @accessibility
|
|
193
|
-
* - Keyboard
|
|
194
|
-
* - Touch/
|
|
195
|
-
* - Screen
|
|
196
|
-
* -
|
|
197
|
-
* -
|
|
198
|
-
* -
|
|
199
|
-
* -
|
|
477
|
+
* - **Keyboard Navigation**: Arrow keys for slide navigation, Tab for focus management
|
|
478
|
+
* - **Touch/Drag Support**: Multi-touch gestures, mouse drag, momentum scrolling
|
|
479
|
+
* - **Screen Reader**: ARIA roles, labels, and announcements for slide changes
|
|
480
|
+
* - **Focus Management**: Automatic focus handling, visible focus indicators
|
|
481
|
+
* - **Motion**: Respects `prefers-reduced-motion` for accessibility
|
|
482
|
+
* - **RTL Support**: Right-to-left layout support with `direction: "rtl"` option
|
|
483
|
+
* - **High Contrast**: Proper color contrast for navigation elements
|
|
484
|
+
*
|
|
485
|
+
* **Keyboard Shortcuts:**
|
|
486
|
+
* - `←/→` or `↑/↓`: Navigate slides (based on orientation)
|
|
487
|
+
* - `Home`: Go to first slide
|
|
488
|
+
* - `End`: Go to last slide
|
|
489
|
+
* - `Tab`: Focus navigation elements
|
|
200
490
|
*
|
|
201
491
|
* @see {@link https://ui.shadcn.com/docs/components/carousel} shadcn/ui carousel documentation
|
|
202
492
|
* @see {@link https://www.embla-carousel.com/} Embla Carousel documentation
|
|
493
|
+
* @see {@link https://www.embla-carousel.com/api/options/} Embla options reference
|
|
494
|
+
* @see {@link https://www.embla-carousel.com/plugins/} Available Embla plugins
|
|
203
495
|
* @see {@link useCarousel} Hook for accessing carousel controls
|
|
204
496
|
* @see {@link CarouselContent} Container for carousel items
|
|
205
497
|
* @see {@link CarouselItem} Individual carousel slide
|
|
@@ -215,7 +507,7 @@ function Carousel({
|
|
|
215
507
|
className,
|
|
216
508
|
children,
|
|
217
509
|
...props
|
|
218
|
-
}: React.ComponentProps<"div">
|
|
510
|
+
}: CarouselProps & React.ComponentProps<"div">) {
|
|
219
511
|
const [carouselRef, api] = useEmblaCarousel(
|
|
220
512
|
{
|
|
221
513
|
...opts,
|
|
@@ -294,15 +586,21 @@ function Carousel({
|
|
|
294
586
|
);
|
|
295
587
|
}
|
|
296
588
|
|
|
589
|
+
/** Props for CarouselContent component (used in component implementation) */
|
|
590
|
+
type CarouselContentProps = {
|
|
591
|
+
/** Additional CSS classes for custom spacing and styling */
|
|
592
|
+
className?: string;
|
|
593
|
+
/** CarouselItem components and other slide content */
|
|
594
|
+
children?: React.ReactNode;
|
|
595
|
+
} & Omit<React.HTMLAttributes<HTMLDivElement>, "className" | "children">;
|
|
596
|
+
|
|
297
597
|
/**
|
|
298
598
|
* Container component for carousel items that handles the scrolling viewport
|
|
299
599
|
*
|
|
300
600
|
* Wraps all carousel items and manages the scrollable area. This component
|
|
301
|
-
* must be a direct child of Carousel and handles orientation-specific layouts
|
|
302
|
-
* and overflow
|
|
303
|
-
*
|
|
304
|
-
* @param className - Additional CSS classes for custom spacing and styling
|
|
305
|
-
* @param children - CarouselItem components
|
|
601
|
+
* must be a direct child of Carousel and handles orientation-specific layouts,
|
|
602
|
+
* overflow behavior, and slide spacing. The outer div provides overflow clipping
|
|
603
|
+
* while the inner div contains the flex layout for slides.
|
|
306
604
|
*
|
|
307
605
|
* @example
|
|
308
606
|
* ```tsx
|
|
@@ -316,32 +614,55 @@ function Carousel({
|
|
|
316
614
|
*
|
|
317
615
|
* @example
|
|
318
616
|
* ```tsx
|
|
319
|
-
* //
|
|
617
|
+
* // Multi-item carousel with responsive spacing
|
|
320
618
|
* <CarouselContent className="-ml-2 md:-ml-4">
|
|
321
|
-
* <CarouselItem className="pl-2 md:pl-4
|
|
322
|
-
*
|
|
619
|
+
* <CarouselItem className="pl-2 md:pl-4 md:basis-1/2 lg:basis-1/3">
|
|
620
|
+
* <Card>Product 1</Card>
|
|
621
|
+
* </CarouselItem>
|
|
622
|
+
* <CarouselItem className="pl-2 md:pl-4 md:basis-1/2 lg:basis-1/3">
|
|
623
|
+
* <Card>Product 2</Card>
|
|
624
|
+
* </CarouselItem>
|
|
323
625
|
* </CarouselContent>
|
|
324
626
|
* ```
|
|
325
627
|
*
|
|
326
628
|
* @example
|
|
327
629
|
* ```tsx
|
|
328
|
-
* // Vertical orientation
|
|
630
|
+
* // Vertical orientation with custom height
|
|
329
631
|
* <CarouselContent className="-mt-4 h-[300px]">
|
|
330
|
-
* <CarouselItem className="pt-4
|
|
331
|
-
*
|
|
632
|
+
* <CarouselItem className="pt-4 basis-1/2">
|
|
633
|
+
* <div className="h-full bg-muted rounded-lg">Slide 1</div>
|
|
634
|
+
* </CarouselItem>
|
|
635
|
+
* <CarouselItem className="pt-4 basis-1/2">
|
|
636
|
+
* <div className="h-full bg-muted rounded-lg">Slide 2</div>
|
|
637
|
+
* </CarouselItem>
|
|
638
|
+
* </CarouselContent>
|
|
639
|
+
* ```
|
|
640
|
+
*
|
|
641
|
+
* @example
|
|
642
|
+
* ```tsx
|
|
643
|
+
* // Custom slide spacing with CSS variables
|
|
644
|
+
* <CarouselContent
|
|
645
|
+
* className="-ml-4"
|
|
646
|
+
* style={{ '--slide-spacing': '1rem' } as React.CSSProperties}
|
|
647
|
+
* >
|
|
648
|
+
* <CarouselItem className="pl-4">
|
|
649
|
+
* Content with custom spacing
|
|
650
|
+
* </CarouselItem>
|
|
332
651
|
* </CarouselContent>
|
|
333
652
|
* ```
|
|
334
653
|
*
|
|
335
654
|
* @accessibility
|
|
336
655
|
* - Provides scrollable container with proper overflow handling
|
|
337
656
|
* - Maintains responsive layout for horizontal/vertical orientations
|
|
338
|
-
* -
|
|
657
|
+
* - Supports screen reader navigation through slide content
|
|
658
|
+
* - Preserves focus management within slides
|
|
659
|
+
* - Uses semantic HTML structure for assistive technologies
|
|
339
660
|
*
|
|
340
661
|
* @see {@link Carousel} Main carousel component
|
|
341
662
|
* @see {@link CarouselItem} Individual carousel slides
|
|
342
663
|
* @since 1.0.0
|
|
343
664
|
*/
|
|
344
|
-
function CarouselContent({ className, ...props }:
|
|
665
|
+
function CarouselContent({ className, ...props }: CarouselContentProps) {
|
|
345
666
|
const { carouselRef, orientation } = useCarousel();
|
|
346
667
|
|
|
347
668
|
return (
|
|
@@ -362,21 +683,27 @@ function CarouselContent({ className, ...props }: React.ComponentProps<"div">) {
|
|
|
362
683
|
);
|
|
363
684
|
}
|
|
364
685
|
|
|
686
|
+
/** Props for CarouselItem component (used in component implementation) */
|
|
687
|
+
type CarouselItemProps = {
|
|
688
|
+
/** Additional CSS classes for sizing, spacing, and styling */
|
|
689
|
+
className?: string;
|
|
690
|
+
/** Content to display within the slide */
|
|
691
|
+
children?: React.ReactNode;
|
|
692
|
+
} & Omit<React.HTMLAttributes<HTMLDivElement>, "className" | "children">;
|
|
693
|
+
|
|
365
694
|
/**
|
|
366
695
|
* Individual slide/item component within the carousel
|
|
367
696
|
*
|
|
368
697
|
* Represents a single slide in the carousel that can contain any content.
|
|
369
698
|
* Automatically handles sizing, spacing, and orientation-specific layouts
|
|
370
|
-
* based on the parent carousel configuration.
|
|
371
|
-
*
|
|
372
|
-
* @param className - Additional CSS classes for sizing (basis-*) and spacing
|
|
373
|
-
* @param children - Content to display within the slide
|
|
699
|
+
* based on the parent carousel configuration. Each slide is a flex item
|
|
700
|
+
* with configurable basis for responsive layouts.
|
|
374
701
|
*
|
|
375
702
|
* @example
|
|
376
703
|
* ```tsx
|
|
377
704
|
* // Basic slide with content
|
|
378
705
|
* <CarouselItem>
|
|
379
|
-
* <div className="p-6">
|
|
706
|
+
* <div className="p-6 bg-muted rounded-lg">
|
|
380
707
|
* <h3 className="text-lg font-semibold">Slide Title</h3>
|
|
381
708
|
* <p className="text-muted-foreground">Slide description</p>
|
|
382
709
|
* </div>
|
|
@@ -385,46 +712,74 @@ function CarouselContent({ className, ...props }: React.ComponentProps<"div">) {
|
|
|
385
712
|
*
|
|
386
713
|
* @example
|
|
387
714
|
* ```tsx
|
|
388
|
-
* // Card-based slide
|
|
715
|
+
* // Card-based slide with structured content
|
|
389
716
|
* <CarouselItem>
|
|
390
|
-
* <Card>
|
|
717
|
+
* <Card className="h-full">
|
|
391
718
|
* <CardHeader>
|
|
392
719
|
* <CardTitle>Product Name</CardTitle>
|
|
720
|
+
* <CardDescription>Product category</CardDescription>
|
|
393
721
|
* </CardHeader>
|
|
394
722
|
* <CardContent>
|
|
395
|
-
* <img src="product.jpg" alt="Product" className="w-full" />
|
|
396
|
-
* <p className="mt-2">$99.99</p>
|
|
723
|
+
* <img src="product.jpg" alt="Product" className="w-full aspect-square object-cover" />
|
|
724
|
+
* <p className="mt-2 text-2xl font-bold">$99.99</p>
|
|
397
725
|
* </CardContent>
|
|
726
|
+
* <CardFooter>
|
|
727
|
+
* <Button className="w-full">Add to Cart</Button>
|
|
728
|
+
* </CardFooter>
|
|
398
729
|
* </Card>
|
|
399
730
|
* </CarouselItem>
|
|
400
731
|
* ```
|
|
401
732
|
*
|
|
402
733
|
* @example
|
|
403
734
|
* ```tsx
|
|
404
|
-
* // Responsive
|
|
405
|
-
* <CarouselItem className="
|
|
406
|
-
* <div className="aspect-square bg-
|
|
735
|
+
* // Responsive multi-item carousel
|
|
736
|
+
* <CarouselItem className="pl-4 basis-full sm:basis-1/2 lg:basis-1/3">
|
|
737
|
+
* <div className="aspect-square bg-gradient-to-br from-blue-400 to-purple-600 rounded-lg p-6 text-white">
|
|
738
|
+
* <h4 className="font-semibold">Feature {index + 1}</h4>
|
|
739
|
+
* <p className="text-sm opacity-90">Description text</p>
|
|
740
|
+
* </div>
|
|
741
|
+
* </CarouselItem>
|
|
742
|
+
* ```
|
|
743
|
+
*
|
|
744
|
+
* @example
|
|
745
|
+
* ```tsx
|
|
746
|
+
* // Auto-sized slides for variable content
|
|
747
|
+
* <CarouselItem className="basis-auto">
|
|
748
|
+
* <Badge variant="secondary" className="text-nowrap px-4 py-2">
|
|
749
|
+
* {tag.label}
|
|
750
|
+
* </Badge>
|
|
407
751
|
* </CarouselItem>
|
|
408
752
|
* ```
|
|
409
753
|
*
|
|
410
754
|
* @example
|
|
411
755
|
* ```tsx
|
|
412
|
-
* //
|
|
756
|
+
* // Image gallery slide with aspect ratio
|
|
413
757
|
* <CarouselItem className="basis-4/5">
|
|
414
|
-
* <div className="
|
|
758
|
+
* <div className="relative aspect-video bg-muted rounded-lg overflow-hidden">
|
|
759
|
+
* <img
|
|
760
|
+
* src={image.src}
|
|
761
|
+
* alt={image.alt}
|
|
762
|
+
* className="object-cover w-full h-full"
|
|
763
|
+
* />
|
|
764
|
+
* <div className="absolute inset-0 bg-black/20 flex items-end p-4">
|
|
765
|
+
* <h5 className="text-white font-medium">{image.title}</h5>
|
|
766
|
+
* </div>
|
|
767
|
+
* </div>
|
|
415
768
|
* </CarouselItem>
|
|
416
769
|
* ```
|
|
417
770
|
*
|
|
418
771
|
* @accessibility
|
|
419
|
-
* - Labeled as
|
|
420
|
-
* - Maintains focus behavior and keyboard navigation
|
|
421
|
-
* - Preserves
|
|
772
|
+
* - **ARIA Role**: Labeled as "slide" with proper role for screen readers
|
|
773
|
+
* - **Focus Management**: Maintains focus behavior and keyboard navigation
|
|
774
|
+
* - **Content Structure**: Preserves semantic structure for assistive technologies
|
|
775
|
+
* - **Interactive Elements**: Supports focusable elements within slides
|
|
776
|
+
* - **Screen Reader**: Announces slide position and content appropriately
|
|
422
777
|
*
|
|
423
778
|
* @see {@link Carousel} Main carousel component
|
|
424
779
|
* @see {@link CarouselContent} Container for carousel items
|
|
425
780
|
* @since 1.0.0
|
|
426
781
|
*/
|
|
427
|
-
function CarouselItem({ className, ...props }:
|
|
782
|
+
function CarouselItem({ className, ...props }: CarouselItemProps) {
|
|
428
783
|
const { orientation } = useCarousel();
|
|
429
784
|
|
|
430
785
|
return (
|
|
@@ -442,20 +797,28 @@ function CarouselItem({ className, ...props }: React.ComponentProps<"div">) {
|
|
|
442
797
|
);
|
|
443
798
|
}
|
|
444
799
|
|
|
800
|
+
/** Props for CarouselPrevious component (used in component implementation) */
|
|
801
|
+
type CarouselPreviousProps = {
|
|
802
|
+
/** Additional CSS classes for custom positioning and styling */
|
|
803
|
+
className?: string;
|
|
804
|
+
/** Button variant styling */
|
|
805
|
+
variant?: React.ComponentProps<typeof Button>["variant"];
|
|
806
|
+
/** Button size */
|
|
807
|
+
size?: React.ComponentProps<typeof Button>["size"];
|
|
808
|
+
} & Omit<React.ComponentProps<typeof Button>, "className" | "variant" | "size">;
|
|
809
|
+
|
|
445
810
|
/**
|
|
446
811
|
* Navigation button to go to the previous carousel slide
|
|
447
812
|
*
|
|
448
813
|
* Renders a previous navigation button that automatically handles disabled
|
|
449
|
-
* states when at the beginning of the carousel
|
|
450
|
-
* and vertical orientations with appropriate
|
|
451
|
-
*
|
|
452
|
-
*
|
|
453
|
-
* @param variant - Button variant (default: "outline")
|
|
454
|
-
* @param size - Button size (default: "icon")
|
|
814
|
+
* states when at the beginning of the carousel (unless loop is enabled).
|
|
815
|
+
* Supports both horizontal and vertical orientations with appropriate
|
|
816
|
+
* positioning and icon rotation. Integrates with the carousel context
|
|
817
|
+
* to provide seamless navigation.
|
|
455
818
|
*
|
|
456
819
|
* @example
|
|
457
820
|
* ```tsx
|
|
458
|
-
* // Basic usage
|
|
821
|
+
* // Basic usage with default styling
|
|
459
822
|
* <Carousel>
|
|
460
823
|
* <CarouselContent>
|
|
461
824
|
* <CarouselItem>Slide 1</CarouselItem>
|
|
@@ -468,9 +831,9 @@ function CarouselItem({ className, ...props }: React.ComponentProps<"div">) {
|
|
|
468
831
|
*
|
|
469
832
|
* @example
|
|
470
833
|
* ```tsx
|
|
471
|
-
* // Custom
|
|
834
|
+
* // Custom positioned button
|
|
472
835
|
* <CarouselPrevious
|
|
473
|
-
* className="-left-8 bg-background shadow-md"
|
|
836
|
+
* className="-left-8 bg-background shadow-md hover:shadow-lg"
|
|
474
837
|
* variant="ghost"
|
|
475
838
|
* size="sm"
|
|
476
839
|
* />
|
|
@@ -478,20 +841,47 @@ function CarouselItem({ className, ...props }: React.ComponentProps<"div">) {
|
|
|
478
841
|
*
|
|
479
842
|
* @example
|
|
480
843
|
* ```tsx
|
|
481
|
-
* // Vertical carousel
|
|
482
|
-
* <Carousel orientation="vertical">
|
|
844
|
+
* // Vertical carousel with rotated icons
|
|
845
|
+
* <Carousel orientation="vertical" className="h-[400px]">
|
|
483
846
|
* <CarouselContent>...</CarouselContent>
|
|
484
|
-
* <CarouselPrevious />
|
|
485
|
-
* <CarouselNext />
|
|
847
|
+
* <CarouselPrevious className="-top-8" />
|
|
848
|
+
* <CarouselNext className="-bottom-8" />
|
|
486
849
|
* </Carousel>
|
|
487
850
|
* ```
|
|
488
851
|
*
|
|
852
|
+
* @example
|
|
853
|
+
* ```tsx
|
|
854
|
+
* // Inside carousel bounds positioning
|
|
855
|
+
* <div className="relative">
|
|
856
|
+
* <Carousel>
|
|
857
|
+
* <CarouselContent>...</CarouselContent>
|
|
858
|
+
* <CarouselPrevious className="absolute left-4 top-1/2 -translate-y-1/2 z-10" />
|
|
859
|
+
* <CarouselNext className="absolute right-4 top-1/2 -translate-y-1/2 z-10" />
|
|
860
|
+
* </Carousel>
|
|
861
|
+
* </div>
|
|
862
|
+
* ```
|
|
863
|
+
*
|
|
864
|
+
* @example
|
|
865
|
+
* ```tsx
|
|
866
|
+
* // Custom button content and accessibility
|
|
867
|
+
* <CarouselPrevious
|
|
868
|
+
* variant="secondary"
|
|
869
|
+
* className="w-auto px-4"
|
|
870
|
+
* aria-label="Go to previous product"
|
|
871
|
+
* >
|
|
872
|
+
* <ArrowLeft className="w-4 h-4 mr-2" />
|
|
873
|
+
* Previous
|
|
874
|
+
* </CarouselPrevious>
|
|
875
|
+
* ```
|
|
876
|
+
*
|
|
489
877
|
* @accessibility
|
|
490
|
-
* - Keyboard
|
|
491
|
-
* - Screen
|
|
492
|
-
* - Automatically disabled when cannot scroll further
|
|
493
|
-
* - High contrast focus
|
|
494
|
-
* -
|
|
878
|
+
* - **Keyboard Navigation**: Accessible via Tab and Enter/Space
|
|
879
|
+
* - **Screen Reader**: Built-in "Previous slide" label with sr-only text
|
|
880
|
+
* - **Disabled State**: Automatically disabled when cannot scroll further
|
|
881
|
+
* - **Focus Indicators**: High contrast focus ring for keyboard navigation
|
|
882
|
+
* - **Motion**: Respects `prefers-reduced-motion` for animations
|
|
883
|
+
* - **Touch**: Large enough touch target (44px minimum) for mobile accessibility
|
|
884
|
+
* - **Icon**: ArrowLeft icon rotated appropriately for vertical orientation
|
|
495
885
|
*
|
|
496
886
|
* @see {@link Carousel} Main carousel component
|
|
497
887
|
* @see {@link CarouselNext} Next navigation button
|
|
@@ -503,7 +893,7 @@ function CarouselPrevious({
|
|
|
503
893
|
variant = "outline",
|
|
504
894
|
size = "icon",
|
|
505
895
|
...props
|
|
506
|
-
}:
|
|
896
|
+
}: CarouselPreviousProps) {
|
|
507
897
|
const { orientation, scrollPrev, canScrollPrev } = useCarousel();
|
|
508
898
|
|
|
509
899
|
return (
|
|
@@ -528,20 +918,28 @@ function CarouselPrevious({
|
|
|
528
918
|
);
|
|
529
919
|
}
|
|
530
920
|
|
|
921
|
+
/** Props for CarouselNext component (used in component implementation) */
|
|
922
|
+
type CarouselNextProps = {
|
|
923
|
+
/** Additional CSS classes for custom positioning and styling */
|
|
924
|
+
className?: string;
|
|
925
|
+
/** Button variant styling */
|
|
926
|
+
variant?: React.ComponentProps<typeof Button>["variant"];
|
|
927
|
+
/** Button size */
|
|
928
|
+
size?: React.ComponentProps<typeof Button>["size"];
|
|
929
|
+
} & Omit<React.ComponentProps<typeof Button>, "className" | "variant" | "size">;
|
|
930
|
+
|
|
531
931
|
/**
|
|
532
932
|
* Navigation button to go to the next carousel slide
|
|
533
933
|
*
|
|
534
934
|
* Renders a next navigation button that automatically handles disabled
|
|
535
|
-
* states when at the end of the carousel
|
|
536
|
-
* and vertical orientations with appropriate
|
|
537
|
-
*
|
|
538
|
-
*
|
|
539
|
-
* @param variant - Button variant (default: "outline")
|
|
540
|
-
* @param size - Button size (default: "icon")
|
|
935
|
+
* states when at the end of the carousel (unless loop is enabled).
|
|
936
|
+
* Supports both horizontal and vertical orientations with appropriate
|
|
937
|
+
* positioning and icon rotation. Integrates with the carousel context
|
|
938
|
+
* to provide seamless navigation.
|
|
541
939
|
*
|
|
542
940
|
* @example
|
|
543
941
|
* ```tsx
|
|
544
|
-
* // Basic usage
|
|
942
|
+
* // Basic usage with default styling
|
|
545
943
|
* <Carousel>
|
|
546
944
|
* <CarouselContent>
|
|
547
945
|
* <CarouselItem>Slide 1</CarouselItem>
|
|
@@ -554,9 +952,9 @@ function CarouselPrevious({
|
|
|
554
952
|
*
|
|
555
953
|
* @example
|
|
556
954
|
* ```tsx
|
|
557
|
-
* // Custom
|
|
955
|
+
* // Custom positioned button
|
|
558
956
|
* <CarouselNext
|
|
559
|
-
* className="-right-8 bg-background shadow-md"
|
|
957
|
+
* className="-right-8 bg-background shadow-md hover:shadow-lg"
|
|
560
958
|
* variant="ghost"
|
|
561
959
|
* size="sm"
|
|
562
960
|
* />
|
|
@@ -564,20 +962,47 @@ function CarouselPrevious({
|
|
|
564
962
|
*
|
|
565
963
|
* @example
|
|
566
964
|
* ```tsx
|
|
567
|
-
* // Vertical carousel
|
|
568
|
-
* <Carousel orientation="vertical">
|
|
965
|
+
* // Vertical carousel with rotated icons
|
|
966
|
+
* <Carousel orientation="vertical" className="h-[400px]">
|
|
569
967
|
* <CarouselContent>...</CarouselContent>
|
|
570
|
-
* <CarouselPrevious />
|
|
571
|
-
* <CarouselNext />
|
|
968
|
+
* <CarouselPrevious className="-top-8" />
|
|
969
|
+
* <CarouselNext className="-bottom-8" />
|
|
572
970
|
* </Carousel>
|
|
573
971
|
* ```
|
|
574
972
|
*
|
|
973
|
+
* @example
|
|
974
|
+
* ```tsx
|
|
975
|
+
* // Inside carousel bounds positioning
|
|
976
|
+
* <div className="relative">
|
|
977
|
+
* <Carousel>
|
|
978
|
+
* <CarouselContent>...</CarouselContent>
|
|
979
|
+
* <CarouselPrevious className="absolute left-4 top-1/2 -translate-y-1/2 z-10" />
|
|
980
|
+
* <CarouselNext className="absolute right-4 top-1/2 -translate-y-1/2 z-10" />
|
|
981
|
+
* </Carousel>
|
|
982
|
+
* </div>
|
|
983
|
+
* ```
|
|
984
|
+
*
|
|
985
|
+
* @example
|
|
986
|
+
* ```tsx
|
|
987
|
+
* // Custom button content and accessibility
|
|
988
|
+
* <CarouselNext
|
|
989
|
+
* variant="secondary"
|
|
990
|
+
* className="w-auto px-4"
|
|
991
|
+
* aria-label="Go to next product"
|
|
992
|
+
* >
|
|
993
|
+
* Next
|
|
994
|
+
* <ArrowRight className="w-4 h-4 ml-2" />
|
|
995
|
+
* </CarouselNext>
|
|
996
|
+
* ```
|
|
997
|
+
*
|
|
575
998
|
* @accessibility
|
|
576
|
-
* - Keyboard
|
|
577
|
-
* - Screen
|
|
578
|
-
* - Automatically disabled when cannot scroll further
|
|
579
|
-
* - High contrast focus
|
|
580
|
-
* -
|
|
999
|
+
* - **Keyboard Navigation**: Accessible via Tab and Enter/Space
|
|
1000
|
+
* - **Screen Reader**: Built-in "Next slide" label with sr-only text
|
|
1001
|
+
* - **Disabled State**: Automatically disabled when cannot scroll further
|
|
1002
|
+
* - **Focus Indicators**: High contrast focus ring for keyboard navigation
|
|
1003
|
+
* - **Motion**: Respects `prefers-reduced-motion` for animations
|
|
1004
|
+
* - **Touch**: Large enough touch target (44px minimum) for mobile accessibility
|
|
1005
|
+
* - **Icon**: ArrowRight icon rotated appropriately for vertical orientation
|
|
581
1006
|
*
|
|
582
1007
|
* @see {@link Carousel} Main carousel component
|
|
583
1008
|
* @see {@link CarouselPrevious} Previous navigation button
|
|
@@ -589,7 +1014,7 @@ function CarouselNext({
|
|
|
589
1014
|
variant = "outline",
|
|
590
1015
|
size = "icon",
|
|
591
1016
|
...props
|
|
592
|
-
}:
|
|
1017
|
+
}: CarouselNextProps) {
|
|
593
1018
|
const { orientation, scrollNext, canScrollNext } = useCarousel();
|
|
594
1019
|
|
|
595
1020
|
return (
|
|
@@ -616,9 +1041,15 @@ function CarouselNext({
|
|
|
616
1041
|
|
|
617
1042
|
export {
|
|
618
1043
|
type CarouselApi,
|
|
1044
|
+
type CarouselProps,
|
|
1045
|
+
type CarouselContentProps,
|
|
1046
|
+
type CarouselItemProps,
|
|
1047
|
+
type CarouselPreviousProps,
|
|
1048
|
+
type CarouselNextProps,
|
|
619
1049
|
Carousel,
|
|
620
1050
|
CarouselContent,
|
|
621
1051
|
CarouselItem,
|
|
622
1052
|
CarouselPrevious,
|
|
623
1053
|
CarouselNext,
|
|
1054
|
+
useCarousel,
|
|
624
1055
|
};
|