@latte-macchiat-io/latte-vanilla-components 0.0.177 → 0.0.178

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/package.json +3 -1
  2. package/src/assets/styles/mediaqueries.tsx +24 -0
  3. package/src/components/Actions/Actions.tsx +132 -0
  4. package/src/components/Actions/export.tsx +4 -0
  5. package/src/components/{Main/stories.ts → Actions/stories.tsx} +14 -13
  6. package/src/components/Button/Button.tsx +132 -0
  7. package/src/components/Button/export.tsx +5 -0
  8. package/src/components/Carousel/Carousel.tsx +328 -0
  9. package/src/components/Carousel/export.tsx +4 -0
  10. package/src/components/Columns/Columns.tsx +142 -0
  11. package/src/components/Columns/export.tsx +5 -0
  12. package/src/components/ConsentCookie/ConsentCookie.tsx +202 -0
  13. package/src/components/ConsentCookie/export.tsx +4 -0
  14. package/src/components/{Icon/stories.ts → ConsentCookie/stories.tsx} +7 -8
  15. package/src/components/Footer/Footer.tsx +130 -0
  16. package/src/components/Footer/export.tsx +4 -0
  17. package/src/components/Footer/stories.tsx +26 -0
  18. package/src/components/Form/Form.tsx +127 -0
  19. package/src/components/Form/Row/Row.tsx +137 -0
  20. package/src/components/Form/Row/index.tsx +0 -0
  21. package/src/components/Form/Row/stories.tsx +41 -0
  22. package/src/components/Form/TextField/Input/Input.tsx +139 -0
  23. package/src/components/Form/TextField/Input/export.tsx +6 -0
  24. package/src/components/Form/TextField/Label/Label.tsx +133 -0
  25. package/src/components/Form/TextField/Label/export.tsx +4 -0
  26. package/src/components/Form/TextField/TextField.tsx +200 -0
  27. package/src/components/Form/TextField/Textarea/Textarea.tsx +135 -0
  28. package/src/components/Form/TextField/Textarea/export.tsx +6 -0
  29. package/src/components/Form/TextField/Textarea/stories.tsx +44 -0
  30. package/src/components/Form/TextField/export.tsx +4 -0
  31. package/src/components/Form/export.tsx +4 -0
  32. package/src/components/Header/Header.tsx +158 -0
  33. package/src/components/Header/HeaderOverlay/index.tsx +32 -0
  34. package/src/components/Header/ToggleNav/index.tsx +32 -0
  35. package/src/components/Header/export.tsx +4 -0
  36. package/src/components/Header/stories.tsx +26 -0
  37. package/src/components/Icon/Icon.tsx +159 -0
  38. package/src/components/Icon/export.tsx +4 -0
  39. package/src/components/KeyNumber/KeyNumber.tsx +166 -0
  40. package/src/components/KeyNumber/export.tsx +4 -0
  41. package/src/components/LanguageSwitcher/LanguageSwitcher.tsx +168 -0
  42. package/src/components/LanguageSwitcher/export.tsx +4 -0
  43. package/src/components/Logo/Logo.tsx +137 -0
  44. package/src/components/Logo/export.tsx +4 -0
  45. package/src/components/Logo/stories.tsx +28 -0
  46. package/src/components/Main/Main.tsx +130 -0
  47. package/src/components/Main/export.tsx +4 -0
  48. package/src/components/Modal/Modal.tsx +194 -0
  49. package/src/components/Modal/export.tsx +4 -0
  50. package/src/components/Modal/types.tsx +5 -0
  51. package/src/components/Nav/Nav.tsx +129 -0
  52. package/src/components/Nav/export.tsx +4 -0
  53. package/src/components/Nav/stories.tsx +28 -0
  54. package/src/components/NavLegal/NavLegal.tsx +133 -0
  55. package/src/components/NavLegal/export.tsx +4 -0
  56. package/src/components/NavLegal/stories.tsx +28 -0
  57. package/src/components/NavLegal/types.tsx +1 -0
  58. package/src/components/NavSocial/NavSocial.tsx +169 -0
  59. package/src/components/NavSocial/export.tsx +5 -0
  60. package/src/components/{Columns/stories.ts → NavSocial/stories.tsx} +12 -14
  61. package/src/components/NavSocial/types.tsx +1 -0
  62. package/src/components/Section/Section.tsx +130 -0
  63. package/src/components/Section/export.tsx +6 -0
  64. package/src/components/ToRemove/ToRemove.tsx +3 -0
  65. package/src/components/Video/Video.tsx +243 -0
  66. package/src/components/Video/export.tsx +2 -0
  67. package/src/components/VideoFullWidth/VideoFullWidth.tsx +152 -0
  68. package/src/components/VideoFullWidth/export.tsx +2 -0
  69. package/src/components/Button/stories.ts +0 -127
  70. package/src/components/Section/stories.ts +0 -64
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@latte-macchiat-io/latte-vanilla-components",
3
- "version": "0.0.177",
3
+ "version": "0.0.178",
4
4
  "description": "Beautiful components for amazing projects, with a touch of Vanilla 🥤",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -14,7 +14,9 @@
14
14
  "files": [
15
15
  "src/**/*.css.ts",
16
16
  "src/**/*.ts",
17
+ "src/**/*.tsx",
17
18
  "!src/**/*.stories.ts",
19
+ "!src/**/stories.ts",
18
20
  "!src/**/*.test.ts"
19
21
  ],
20
22
  "peerDependencies": {
@@ -0,0 +1,24 @@
1
+ export const breakpoints1 = [
2
+ 640 /* Small devices (portrait tablets and large phones, 640px and up) */, 768 /* Medium devices (landscape tablets, 768px and up) */,
3
+ 1024 /* Large devices (laptops/desktops, 1024px and up) */, 1680 /* Extra large devices (large laptops and desktops, 1280px and up) */,
4
+ ];
5
+
6
+ export const mq = breakpoints1.map((bp) => `(min-width: ${bp}px)`);
7
+
8
+ export const breakpoints = {
9
+ mobile: 0, // Mobile par défaut
10
+ sm: 640, // Mobile large
11
+ md: 768, // Tablette
12
+ lg: 1024, // Petit laptop
13
+ xl: 1280, // Desktop
14
+ '2xl': 1536, // Grand écran
15
+ } as const;
16
+
17
+ export const queries = {
18
+ mobile: `screen and (min-width: ${breakpoints.mobile}px)`,
19
+ sm: `screen and (min-width: ${breakpoints.sm}px)`,
20
+ md: `screen and (min-width: ${breakpoints.md}px)`,
21
+ lg: `screen and (min-width: ${breakpoints.lg}px)`,
22
+ xl: `screen and (min-width: ${breakpoints.xl}px)`,
23
+ '2xl': `screen and (min-width: ${breakpoints['2xl']}px)`,
24
+ };
@@ -0,0 +1,132 @@
1
+ import { clsx } from 'clsx';
2
+ import { forwardRef } from 'react';
3
+ import { actionsRecipe, type ActionsVariants } from './Actions.css';
4
+ import { sprinkles, type Sprinkles } from '../../styles/sprinkles.css';
5
+
6
+ export interface ActionsProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'color'>, Sprinkles, NonNullable<ActionsVariants> {
7
+ css?: string;
8
+ as?: 'div' | 'section' | 'footer';
9
+ }
10
+
11
+ export const Actions = forwardRef<HTMLDivElement, ActionsProps>(
12
+ (
13
+ {
14
+ children,
15
+ align,
16
+ direction,
17
+ spacing,
18
+ wrap,
19
+ fullWidth,
20
+ as: Component = 'div',
21
+ css,
22
+ className,
23
+ // Extract sprinkles props
24
+ margin,
25
+ marginTop,
26
+ marginBottom,
27
+ marginLeft,
28
+ marginRight,
29
+ padding,
30
+ paddingTop,
31
+ paddingBottom,
32
+ paddingLeft,
33
+ paddingRight,
34
+ gap,
35
+ display,
36
+ flexDirection,
37
+ justifyContent,
38
+ flexWrap,
39
+ flex,
40
+ width,
41
+ height,
42
+ minWidth,
43
+ maxWidth,
44
+ minHeight,
45
+ position,
46
+ top,
47
+ bottom,
48
+ left,
49
+ right,
50
+ zIndex,
51
+ fontSize,
52
+ fontFamily,
53
+ lineHeight,
54
+ textAlign,
55
+ fontWeight,
56
+ color,
57
+ backgroundColor,
58
+ borderRadius,
59
+ borderWidth,
60
+ borderStyle,
61
+ borderColor,
62
+ boxShadow,
63
+ opacity,
64
+ overflow,
65
+ overflowX,
66
+ overflowY,
67
+ ...htmlProps
68
+ },
69
+ ref
70
+ ) => {
71
+ return (
72
+ <Component
73
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
74
+ ref={ref as any}
75
+ className={clsx(
76
+ actionsRecipe({ align, direction, spacing, wrap, fullWidth }),
77
+ sprinkles({
78
+ margin,
79
+ marginTop,
80
+ marginBottom,
81
+ marginLeft,
82
+ marginRight,
83
+ padding,
84
+ paddingTop,
85
+ paddingBottom,
86
+ paddingLeft,
87
+ paddingRight,
88
+ gap,
89
+ display,
90
+ flexDirection,
91
+ justifyContent,
92
+ flexWrap,
93
+ flex,
94
+ width,
95
+ height,
96
+ minWidth,
97
+ maxWidth,
98
+ minHeight,
99
+ position,
100
+ top,
101
+ bottom,
102
+ left,
103
+ right,
104
+ zIndex,
105
+ fontSize,
106
+ fontFamily,
107
+ lineHeight,
108
+ textAlign,
109
+ fontWeight,
110
+ color,
111
+ backgroundColor,
112
+ borderRadius,
113
+ borderWidth,
114
+ borderStyle,
115
+ borderColor,
116
+ boxShadow,
117
+ opacity,
118
+ overflow,
119
+ overflowX,
120
+ overflowY,
121
+ }),
122
+ css,
123
+ className
124
+ )}
125
+ {...htmlProps}>
126
+ {children}
127
+ </Component>
128
+ );
129
+ }
130
+ );
131
+
132
+ Actions.displayName = 'Actions';
@@ -0,0 +1,4 @@
1
+ // export { Actions } from '.';
2
+ // export type { ActionsProps } from '.';
3
+
4
+ // export { styles as ActionsStyles } from './styles.css';
@@ -1,13 +1,15 @@
1
1
  // import type { Meta, StoryObj } from '@storybook/react';
2
- //
2
+
3
+ // import { Actions, Button } from '../../index';
4
+ // import { actionsStyle } from './styles.css';
5
+
3
6
  // import { defaultTheme } from '../../assets/styles/default-theme';
4
- // import { Main } from '.';
5
- // import { mainStyle } from './styles.css';
6
- //
7
+ // import { buttonStyle } from '../Button/styles.css';
8
+
7
9
  // // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
8
- // const meta = {
9
- // title: 'Latte Components / Layout / Main',
10
- // component: Main,
10
+ // const meta: Meta<typeof Actions> = {
11
+ // title: 'Latte Components / 3. Buttons / Actions',
12
+ // component: Actions,
11
13
  // parameters: {
12
14
  // // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
13
15
  // layout: 'centered',
@@ -17,17 +19,16 @@
17
19
  // // More on argTypes: https://storybook.js.org/docs/api/argtypes
18
20
  // argTypes: {},
19
21
  // // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
20
- // } satisfies Meta<typeof Main>;
21
- //
22
+ // };
23
+
22
24
  // export default meta;
23
25
  // type Story = StoryObj<typeof meta>;
24
- //
26
+
25
27
  // // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
26
28
  // export const Default: Story = {
27
29
  // args: {
28
- // className: mainStyle,
29
30
  // theme: defaultTheme,
30
- // children:
31
- // 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
31
+ // className: actionsStyle,
32
+ // children: <Button className={buttonStyle}>Button</Button>,
32
33
  // },
33
34
  // };
@@ -0,0 +1,132 @@
1
+ import { clsx } from 'clsx';
2
+ import { forwardRef } from 'react';
3
+ import { buttonRecipe, type ButtonVariants } from './Button.css';
4
+ import { sprinkles, Sprinkles } from '../../styles/sprinkles.css';
5
+
6
+ export interface ButtonProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'color'>, Sprinkles, NonNullable<ButtonVariants> {
7
+ css?: string;
8
+ isPending?: boolean;
9
+ asChild?: boolean;
10
+ }
11
+
12
+ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
13
+ (
14
+ {
15
+ variant,
16
+ size,
17
+ fullWidth,
18
+ isPending = false,
19
+ disabled,
20
+ css,
21
+ className,
22
+ children,
23
+ // Extract sprinkles props
24
+ margin,
25
+ marginTop,
26
+ marginBottom,
27
+ marginLeft,
28
+ marginRight,
29
+ padding,
30
+ paddingTop,
31
+ paddingBottom,
32
+ paddingLeft,
33
+ paddingRight,
34
+ gap,
35
+ display,
36
+ flexDirection,
37
+ justifyContent,
38
+ flexWrap,
39
+ flex,
40
+ width,
41
+ height,
42
+ minWidth,
43
+ maxWidth,
44
+ minHeight,
45
+ position,
46
+ top,
47
+ bottom,
48
+ left,
49
+ right,
50
+ zIndex,
51
+ fontSize,
52
+ fontFamily,
53
+ lineHeight,
54
+ textAlign,
55
+ fontWeight,
56
+ color,
57
+ backgroundColor,
58
+ borderRadius,
59
+ borderWidth,
60
+ borderStyle,
61
+ borderColor,
62
+ boxShadow,
63
+ opacity,
64
+ overflow,
65
+ overflowX,
66
+ overflowY,
67
+ ...htmlProps
68
+ },
69
+ ref
70
+ ) => {
71
+ return (
72
+ <button
73
+ ref={ref}
74
+ className={clsx(
75
+ buttonRecipe({ variant, size, fullWidth }),
76
+ sprinkles({
77
+ margin,
78
+ marginTop,
79
+ marginBottom,
80
+ marginLeft,
81
+ marginRight,
82
+ padding,
83
+ paddingTop,
84
+ paddingBottom,
85
+ paddingLeft,
86
+ paddingRight,
87
+ gap,
88
+ display,
89
+ flexDirection,
90
+ justifyContent,
91
+ flexWrap,
92
+ flex,
93
+ width,
94
+ height,
95
+ minWidth,
96
+ maxWidth,
97
+ minHeight,
98
+ position,
99
+ top,
100
+ bottom,
101
+ left,
102
+ right,
103
+ zIndex,
104
+ fontSize,
105
+ fontFamily,
106
+ lineHeight,
107
+ textAlign,
108
+ fontWeight,
109
+ color,
110
+ backgroundColor,
111
+ borderRadius,
112
+ borderWidth,
113
+ borderStyle,
114
+ borderColor,
115
+ boxShadow,
116
+ opacity,
117
+ overflow,
118
+ overflowX,
119
+ overflowY,
120
+ }),
121
+ css,
122
+ className
123
+ )}
124
+ disabled={disabled || isPending}
125
+ {...htmlProps}>
126
+ {isPending ? 'Loading...' : children}
127
+ </button>
128
+ );
129
+ }
130
+ );
131
+
132
+ Button.displayName = 'Button';
@@ -0,0 +1,5 @@
1
+ // export { Button } from './Button';
2
+ // export type { ButtonProps } from '.';
3
+ // export { Type as ButtonType, Size as ButtonSize, Style as ButtonStyle, Variant as ButtonVariant } from './types';
4
+
5
+ // export { styles as ButtonStyles } from './styles.css';
@@ -0,0 +1,328 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { clsx } from 'clsx';
3
+ import { forwardRef, ReactNode, useEffect, useRef, useState } from 'react';
4
+ import {
5
+ carouselBullet,
6
+ carouselBulletActive,
7
+ carouselBullets,
8
+ carouselContent,
9
+ carouselItem,
10
+ carouselNav,
11
+ carouselNavButton,
12
+ carouselRecipe,
13
+ carouselSlide,
14
+ type CarouselVariants,
15
+ } from './Carousel.css';
16
+ import { breakpoints } from '../../styles/mediaqueries';
17
+ import { sprinkles, type Sprinkles } from '../../styles/sprinkles.css';
18
+ import { Icon } from '../Icon/Icon';
19
+
20
+ interface UseWindowSizeReturn {
21
+ width: number | undefined;
22
+ height: number | undefined;
23
+ }
24
+
25
+ const useWindowSize = (): UseWindowSizeReturn => {
26
+ const [windowSize, setWindowSize] = useState<UseWindowSizeReturn>({
27
+ width: undefined,
28
+ height: undefined,
29
+ });
30
+
31
+ useEffect(() => {
32
+ const handleResize = () => {
33
+ setWindowSize({
34
+ width: window.innerWidth,
35
+ height: window.innerHeight,
36
+ });
37
+ };
38
+
39
+ window.addEventListener('resize', handleResize);
40
+ handleResize();
41
+
42
+ return () => window.removeEventListener('resize', handleResize);
43
+ }, []);
44
+
45
+ return windowSize;
46
+ };
47
+
48
+ export interface CarouselProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'color' | 'gap'>, Sprinkles, NonNullable<CarouselVariants> {
49
+ css?: string;
50
+ data: ReactNode[];
51
+ itemsPerView?: number;
52
+ showNavButtons?: boolean;
53
+ showBullets?: boolean;
54
+ autoplay?: boolean;
55
+ autoplayInterval?: number;
56
+ gap?: any;
57
+ as?: 'div' | 'section';
58
+ }
59
+
60
+ export const Carousel = forwardRef<HTMLDivElement, CarouselProps>(
61
+ (
62
+ {
63
+ data,
64
+ itemsPerView = 1,
65
+ showNavButtons = false,
66
+ showBullets = false,
67
+ autoplay = false,
68
+ autoplayInterval = 3000,
69
+ gap = 16,
70
+ fullWidth,
71
+ as: Component = 'div',
72
+ css,
73
+ className,
74
+ // Extract sprinkles props
75
+ margin,
76
+ marginTop,
77
+ marginBottom,
78
+ marginLeft,
79
+ marginRight,
80
+ padding,
81
+ paddingTop,
82
+ paddingBottom,
83
+ paddingLeft,
84
+ paddingRight,
85
+ display,
86
+ flexDirection,
87
+ justifyContent,
88
+ flexWrap,
89
+ flex,
90
+ width,
91
+ height,
92
+ minWidth,
93
+ maxWidth,
94
+ minHeight,
95
+ position,
96
+ top,
97
+ bottom,
98
+ left,
99
+ right,
100
+ zIndex,
101
+ fontSize,
102
+ fontFamily,
103
+ lineHeight,
104
+ textAlign,
105
+ fontWeight,
106
+ color,
107
+ backgroundColor,
108
+ borderRadius,
109
+ borderWidth,
110
+ borderStyle,
111
+ borderColor,
112
+ boxShadow,
113
+ opacity,
114
+ overflow,
115
+ overflowX,
116
+ overflowY,
117
+ ...htmlProps
118
+ },
119
+ ref
120
+ ) => {
121
+ const { width: windowWidth } = useWindowSize();
122
+ const [currentIndex, setCurrentIndex] = useState(0);
123
+ const [itemWidth, setItemWidth] = useState(0);
124
+ const [visibleItems, setVisibleItems] = useState(itemsPerView);
125
+
126
+ const carouselRef = useRef<HTMLDivElement>(null);
127
+ const slideRef = useRef<HTMLDivElement>(null);
128
+
129
+ const isTablet = windowWidth !== undefined && windowWidth > breakpoints.md;
130
+ const isDesktop = windowWidth !== undefined && windowWidth > breakpoints.lg;
131
+
132
+ // Calculate visible items based on screen size
133
+ useEffect(() => {
134
+ if (isDesktop) {
135
+ setVisibleItems(itemsPerView);
136
+ } else if (isTablet) {
137
+ setVisibleItems(Math.max(1, Math.floor(itemsPerView / 2)));
138
+ } else {
139
+ setVisibleItems(1);
140
+ }
141
+ }, [isTablet, isDesktop, itemsPerView]);
142
+
143
+ // Calculate item width
144
+ useEffect(() => {
145
+ const calculateItemWidth = () => {
146
+ if (carouselRef.current) {
147
+ const containerWidth = carouselRef.current.getBoundingClientRect().width;
148
+ const totalGap = (visibleItems - 1) * gap;
149
+ setItemWidth((containerWidth - totalGap) / visibleItems);
150
+ }
151
+ };
152
+
153
+ calculateItemWidth();
154
+ window.addEventListener('resize', calculateItemWidth);
155
+ return () => window.removeEventListener('resize', calculateItemWidth);
156
+ }, [visibleItems, gap]);
157
+
158
+ // Autoplay functionality
159
+ useEffect(() => {
160
+ if (!autoplay) return;
161
+
162
+ const interval = setInterval(() => {
163
+ setCurrentIndex((prev) => {
164
+ const maxIndex = Math.max(0, data.length - visibleItems);
165
+ return prev >= maxIndex ? 0 : prev + 1;
166
+ });
167
+ }, autoplayInterval);
168
+
169
+ return () => clearInterval(interval);
170
+ }, [autoplay, autoplayInterval, data.length, visibleItems]);
171
+
172
+ // Touch/swipe handling
173
+ useEffect(() => {
174
+ const carousel = carouselRef.current;
175
+ if (!carousel) return;
176
+
177
+ let touchStartX = 0;
178
+ let touchEndX = 0;
179
+
180
+ const handleTouchStart = (e: TouchEvent) => {
181
+ touchStartX = e.changedTouches[0].screenX;
182
+ };
183
+
184
+ const handleTouchEnd = (e: TouchEvent) => {
185
+ touchEndX = e.changedTouches[0].screenX;
186
+ handleSwipe();
187
+ };
188
+
189
+ const handleSwipe = () => {
190
+ const swipeThreshold = 100;
191
+ const diff = touchStartX - touchEndX;
192
+
193
+ if (Math.abs(diff) > swipeThreshold) {
194
+ if (diff > 0) {
195
+ // Swipe left - next
196
+ handleNext();
197
+ } else {
198
+ // Swipe right - previous
199
+ handlePrevious();
200
+ }
201
+ }
202
+ };
203
+
204
+ carousel.addEventListener('touchstart', handleTouchStart, { passive: true });
205
+ carousel.addEventListener('touchend', handleTouchEnd, { passive: true });
206
+
207
+ return () => {
208
+ carousel.removeEventListener('touchstart', handleTouchStart);
209
+ carousel.removeEventListener('touchend', handleTouchEnd);
210
+ };
211
+ }, []);
212
+
213
+ const handlePrevious = () => {
214
+ setCurrentIndex((prev) => Math.max(0, prev - 1));
215
+ };
216
+
217
+ const handleNext = () => {
218
+ const maxIndex = Math.max(0, data.length - visibleItems);
219
+ setCurrentIndex((prev) => Math.min(maxIndex, prev + 1));
220
+ };
221
+
222
+ const handleBulletClick = (index: number) => {
223
+ const maxIndex = Math.max(0, data.length - visibleItems);
224
+ setCurrentIndex(Math.min(index, maxIndex));
225
+ };
226
+
227
+ const translateX = -(currentIndex * (itemWidth + gap));
228
+ const maxIndex = Math.max(0, data.length - visibleItems);
229
+
230
+ return (
231
+ <Component
232
+ ref={ref as any}
233
+ className={clsx(
234
+ carouselRecipe({ fullWidth }),
235
+ sprinkles({
236
+ margin,
237
+ marginTop,
238
+ marginBottom,
239
+ marginLeft,
240
+ marginRight,
241
+ padding,
242
+ paddingTop,
243
+ paddingBottom,
244
+ paddingLeft,
245
+ paddingRight,
246
+ display,
247
+ flexDirection,
248
+ justifyContent,
249
+ flexWrap,
250
+ flex,
251
+ width,
252
+ height,
253
+ minWidth,
254
+ maxWidth,
255
+ minHeight,
256
+ position,
257
+ top,
258
+ bottom,
259
+ left,
260
+ right,
261
+ zIndex,
262
+ fontSize,
263
+ fontFamily,
264
+ lineHeight,
265
+ textAlign,
266
+ fontWeight,
267
+ color,
268
+ backgroundColor,
269
+ borderRadius,
270
+ borderWidth,
271
+ borderStyle,
272
+ borderColor,
273
+ boxShadow,
274
+ opacity,
275
+ overflow,
276
+ overflowX,
277
+ overflowY,
278
+ }),
279
+ css,
280
+ className
281
+ )}
282
+ {...htmlProps}>
283
+ <div ref={carouselRef} className={carouselContent}>
284
+ <div
285
+ ref={slideRef}
286
+ className={carouselSlide}
287
+ style={{
288
+ transform: `translateX(${translateX}px)`,
289
+ gap: `${gap}px`,
290
+ }}>
291
+ {data.map((item, index) => (
292
+ <div key={index} className={carouselItem} style={{ width: `${itemWidth}px` }}>
293
+ {item}
294
+ </div>
295
+ ))}
296
+ </div>
297
+ </div>
298
+
299
+ {showNavButtons && (
300
+ <div className={carouselNav}>
301
+ <button type="button" className={carouselNavButton} onClick={handlePrevious} disabled={currentIndex === 0} aria-label="Previous slide">
302
+ <Icon icon="arrowBack" size="md" />
303
+ </button>
304
+ <button type="button" className={carouselNavButton} onClick={handleNext} disabled={currentIndex >= maxIndex} aria-label="Next slide">
305
+ <Icon icon="arrowForward" size="md" />
306
+ </button>
307
+ </div>
308
+ )}
309
+
310
+ {showBullets && (
311
+ <div className={carouselBullets}>
312
+ {Array.from({ length: maxIndex + 1 }, (_, index) => (
313
+ <button
314
+ key={index}
315
+ type="button"
316
+ className={clsx(carouselBullet, index === currentIndex && carouselBulletActive)}
317
+ onClick={() => handleBulletClick(index)}
318
+ aria-label={`Go to slide ${index + 1}`}
319
+ />
320
+ ))}
321
+ </div>
322
+ )}
323
+ </Component>
324
+ );
325
+ }
326
+ );
327
+
328
+ Carousel.displayName = 'Carousel';
@@ -0,0 +1,4 @@
1
+ // export { Carousel } from '.';
2
+ // export type { CarouselProps } from '.';
3
+
4
+ // export { styles as CarouselStyles } from './styles.css';