@central-ui/react 0.2.0 → 0.4.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.
Files changed (79) hide show
  1. package/dist/components/accordion/index.d.ts +14 -14
  2. package/dist/components/accordion/index.js +19 -19
  3. package/dist/components/autocomplete/index.d.ts +57 -49
  4. package/dist/components/autocomplete/index.js +69 -60
  5. package/dist/components/avatar/index.d.ts +7 -6
  6. package/dist/components/avatar/index.js +9 -9
  7. package/dist/components/banner/index.js +1 -1
  8. package/dist/components/carousel/index.d.ts +189 -0
  9. package/dist/components/carousel/index.js +303 -0
  10. package/dist/components/checkbox/index.d.ts +12 -11
  11. package/dist/components/checkbox/index.js +21 -21
  12. package/dist/components/combobox/index.d.ts +67 -71
  13. package/dist/components/combobox/index.js +80 -72
  14. package/dist/components/datepicker/index.d.ts +8 -9
  15. package/dist/components/datepicker/index.js +13 -13
  16. package/dist/components/dialog/index.d.ts +18 -18
  17. package/dist/components/dialog/index.js +22 -21
  18. package/dist/components/drawer/index.d.ts +24 -24
  19. package/dist/components/drawer/index.js +28 -27
  20. package/dist/components/empty/index.d.ts +2 -1
  21. package/dist/components/empty/index.js +2 -1
  22. package/dist/components/exception/index.d.ts +98 -0
  23. package/dist/components/exception/index.js +116 -0
  24. package/dist/components/fileupload/index.d.ts +8 -8
  25. package/dist/components/input/index.d.ts +14 -14
  26. package/dist/components/input/index.js +27 -27
  27. package/dist/components/menu/index.d.ts +37 -37
  28. package/dist/components/menu/index.js +47 -47
  29. package/dist/components/numberinput/index.d.ts +22 -22
  30. package/dist/components/numberinput/index.js +35 -35
  31. package/dist/components/otpinput/index.d.ts +12 -12
  32. package/dist/components/otpinput/index.js +21 -21
  33. package/dist/components/passwordinput/index.d.ts +15 -15
  34. package/dist/components/passwordinput/index.js +27 -27
  35. package/dist/components/popover/index.d.ts +16 -16
  36. package/dist/components/popover/index.js +20 -19
  37. package/dist/components/radio/index.d.ts +12 -12
  38. package/dist/components/radio/index.js +25 -25
  39. package/dist/components/ratinginput/index.d.ts +1 -1
  40. package/dist/components/ratinginput/index.js +3 -3
  41. package/dist/components/select/index.d.ts +32 -29
  42. package/dist/components/select/index.js +45 -35
  43. package/dist/components/slider/index.d.ts +20 -20
  44. package/dist/components/slider/index.js +27 -27
  45. package/dist/components/stepper/index.d.ts +8 -7
  46. package/dist/components/stepper/index.js +4 -2
  47. package/dist/components/switch/index.d.ts +9 -9
  48. package/dist/components/switch/index.js +17 -17
  49. package/dist/components/table/index.d.ts +2 -2
  50. package/dist/components/table/index.js +16 -16
  51. package/dist/components/tabs/index.d.ts +10 -10
  52. package/dist/components/tabs/index.js +11 -11
  53. package/dist/components/textarea/index.d.ts +13 -13
  54. package/dist/components/textarea/index.js +25 -25
  55. package/dist/components/toast/index.d.ts +11 -11
  56. package/dist/components/toast/index.js +11 -11
  57. package/dist/components/toggle/index.d.ts +7 -7
  58. package/dist/components/toggle/index.js +9 -9
  59. package/dist/components/tooltip/index.d.ts +6 -6
  60. package/dist/components/tooltip/index.js +6 -7
  61. package/dist/components/tour/index.d.ts +2 -2
  62. package/dist/components/tour/index.js +5 -4
  63. package/dist/index.d.ts +2 -0
  64. package/dist/index.js +2 -0
  65. package/dist/utils/icons.d.ts +92 -23
  66. package/dist/utils/icons.js +3 -3
  67. package/package.json +69 -67
  68. package/src/presets/shopify-polaris/shopify-polaris-accordion.css +28 -28
  69. package/src/presets/shopify-polaris/shopify-polaris-carousel.css +62 -0
  70. package/src/presets/shopify-polaris/shopify-polaris-exception.css +40 -0
  71. package/src/presets/shopify-polaris/shopify-polaris.css +2 -0
  72. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-accordion.css +28 -28
  73. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-carousel.css +55 -0
  74. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy-exception.css +40 -0
  75. package/src/presets/shopify-polaris-legacy/shopify-polaris-legacy.css +2 -0
  76. package/dist/types/Tone.d.ts +0 -3
  77. package/dist/types/Tone.js +0 -1
  78. package/dist/utils/Tone.d.ts +0 -2
  79. package/dist/utils/Tone.js +0 -6
@@ -1,5 +1,6 @@
1
1
  import type { StyleProps } from '../../types/Style.ts';
2
2
  import { Avatar as BaseAvatar } from '@base-ui/react/avatar';
3
+ import type { ComponentProps } from 'react';
3
4
  export type RootProps = Omit<BaseAvatar.Root.Props, 'render' | 'className'> & {
4
5
  /** Choose the shared size for the visible parts in this composition. */
5
6
  size?: 'big' | 'medium' | 'small';
@@ -33,15 +34,15 @@ export type RootProps = Omit<BaseAvatar.Root.Props, 'render' | 'className'> & {
33
34
  * </Avatar.List>
34
35
  */
35
36
  export declare function Root({ children, size, ...props }: RootProps): import("react").JSX.Element;
36
- export declare const List: import("react").ForwardRefExoticComponent<Omit<import("react").ClassAttributes<HTMLDivElement> & import("react").HTMLAttributes<HTMLDivElement> & StyleProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
37
- export declare const Image: import("react").ForwardRefExoticComponent<Omit<Omit<import("@base-ui/react").AvatarImageProps, "className"> & {
37
+ export declare function List({ disableStyle, className, ref, ...props }: ComponentProps<'div'> & StyleProps): import("react").JSX.Element;
38
+ export declare function Image({ disableStyle, className, ref, ...props }: (Omit<BaseAvatar.Image.Props, 'className' | 'render'> & {
38
39
  className?: string;
39
- } & StyleProps, "ref"> & import("react").RefAttributes<HTMLImageElement>>;
40
- export declare const FallbackNameInitials: import("react").ForwardRefExoticComponent<Omit<Omit<import("@base-ui/react").AvatarFallbackProps, "className" | "children"> & {
40
+ }) & StyleProps): import("react").JSX.Element;
41
+ export declare function FallbackNameInitials({ disableStyle, color, children, className, style, ref, ...props }: (Omit<BaseAvatar.Fallback.Props, 'className' | 'children' | 'render'> & {
41
42
  className?: string;
42
43
  /** Choose the initials background. The name-to-initials conversion is automatic. */
43
44
  color: string;
44
45
  /** Pass the full name; initials are derived automatically. Do not shorten it yourself. */
45
46
  children: string;
46
- } & StyleProps, "ref"> & import("react").RefAttributes<HTMLSpanElement>>;
47
- export declare const FallbackImage: import("react").ForwardRefExoticComponent<Omit<import("react").ClassAttributes<HTMLImageElement> & import("react").ImgHTMLAttributes<HTMLImageElement> & Pick<import("@base-ui/react").AvatarFallbackProps, "delay"> & StyleProps, "ref"> & import("react").RefAttributes<HTMLImageElement>>;
47
+ }) & StyleProps): import("react").JSX.Element;
48
+ export declare function FallbackImage({ disableStyle, className, delay, ref, ...props }: (ComponentProps<'img'> & Pick<BaseAvatar.Fallback.Props, 'delay'>) & StyleProps): import("react").JSX.Element;
@@ -1,6 +1,6 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Avatar as BaseAvatar } from '@base-ui/react/avatar';
3
- import { createContext, forwardRef, useContext } from 'react';
3
+ import { createContext, useContext } from 'react';
4
4
  /**
5
5
  * Root supplies sizing and image/fallback coordination. Image loading chooses the fallback automatically; do not implement your own image-error state. Choose initials or an image fallback.
6
6
  *
@@ -33,14 +33,14 @@ export function Root({ children, size = 'medium', ...props }) {
33
33
  // Native Root renders a span; the fragment preserves our wrapper-free Root contract.
34
34
  return (_jsx(SizeContext.Provider, { value: size, children: _jsx(BaseAvatar.Root, { ...props, render: (rootProps) => _jsx(_Fragment, { children: rootProps.children }), children: children }) }));
35
35
  }
36
- export const List = /* @__PURE__ */ forwardRef(function List({ disableStyle, className, ...props }, ref) {
36
+ export function List({ disableStyle, className, ref, ...props }) {
37
37
  return (_jsx("div", { ...props, ref: ref, "data-component": "avatar-list", "data-style-disabled": disableStyle ? '' : undefined, className: className }));
38
- });
39
- export const Image = /* @__PURE__ */ forwardRef(function Image({ disableStyle, className, ...props }, ref) {
38
+ }
39
+ export function Image({ disableStyle, className, ref, ...props }) {
40
40
  const size = useContext(SizeContext);
41
41
  return (_jsx(BaseAvatar.Image, { ...props, ref: ref, "data-size": size, "data-component": "avatar-image", "data-style-disabled": disableStyle ? '' : undefined, className: className }));
42
- });
43
- export const FallbackNameInitials = /* @__PURE__ */ forwardRef(function FallbackNameInitials({ disableStyle, color, children, className, style, ...props }, ref) {
42
+ }
43
+ export function FallbackNameInitials({ disableStyle, color, children, className, style, ref, ...props }) {
44
44
  const size = useContext(SizeContext);
45
45
  return (_jsx(BaseAvatar.Fallback, { ...props, ref: ref, "data-size": size, role: "img", "aria-label": children, style: typeof style === 'function'
46
46
  ? (state) => ({ backgroundColor: color, ...style(state) })
@@ -51,10 +51,10 @@ export const FallbackNameInitials = /* @__PURE__ */ forwardRef(function Fallback
51
51
  .map((word) => word[0].toUpperCase())
52
52
  .join('')
53
53
  .slice(0, 2) }));
54
- });
55
- export const FallbackImage = /* @__PURE__ */ forwardRef(function FallbackImage({ disableStyle, className, delay, ...props }, ref) {
54
+ }
55
+ export function FallbackImage({ disableStyle, className, delay, ref, ...props }) {
56
56
  const size = useContext(SizeContext);
57
57
  return (_jsx(BaseAvatar.Fallback, { delay: delay, render: _jsx("img", { ...props, ref: ref }), "data-size": size, "data-component": "avatar-fallbackimage", "data-style-disabled": disableStyle ? '' : undefined, className: className }));
58
- });
58
+ }
59
59
  const SizeContext =
60
60
  /* @__PURE__ */ createContext('medium');
@@ -104,7 +104,7 @@ export function CloseButtonIcon({ disableStyle, children = _jsx(X, {}), classNam
104
104
  'data-style-disabled': disableStyle ? '' : undefined,
105
105
  },
106
106
  className: twMerge(children.props.className, className),
107
- strokeWidth: props.strokeWidth ?? children.props.strokeWidth ?? 1.5,
107
+ strokeWidth: props.strokeWidth ?? children.props.strokeWidth ?? 2,
108
108
  });
109
109
  }
110
110
  export function Body({ disableStyle, className, ...props }) {
@@ -0,0 +1,189 @@
1
+ import type { StyleProps } from '../../types/Style.ts';
2
+ import type { SVGIconProps } from '../../types/Icon.ts';
3
+ import type { AsChildProps } from '../../utils/asChild.ts';
4
+ import type { AutoplayOptionsType } from 'embla-carousel-autoplay';
5
+ import type { ComponentProps, ReactElement, ReactNode } from 'react';
6
+ type IconProps = Omit<SVGIconProps, 'strokeWidth'> & {
7
+ /** @default 1.5 */
8
+ strokeWidth?: SVGIconProps['strokeWidth'];
9
+ };
10
+ type Orientation = 'horizontal' | 'vertical';
11
+ export type RootProps = {
12
+ /** The carousel parts, like SlidesList, the buttons and IndicatorList. */
13
+ children: ReactNode;
14
+ /**
15
+ * The direction slides move in. A vertical carousel needs a height on SlidesList.
16
+ * @default 'horizontal'
17
+ */
18
+ orientation?: Orientation;
19
+ /**
20
+ * Whether the last slide leads back to the first, and the first back to the last.
21
+ * @default false
22
+ */
23
+ loop?: boolean;
24
+ /**
25
+ * Where the selected slide sits inside SlidesList, when slides are narrower than it.
26
+ * @default 'center'
27
+ */
28
+ align?: 'start' | 'center' | 'end';
29
+ /**
30
+ * How many slides one Previous/Next press moves. 'auto' moves by as many slides as are fully visible.
31
+ * @default 1
32
+ */
33
+ slidesToScroll?: number | 'auto';
34
+ /**
35
+ * Whether a drag glides freely with momentum instead of stopping on a slide.
36
+ * @default false
37
+ */
38
+ dragFree?: boolean;
39
+ /**
40
+ * How far a drag must move, in pixels, before it counts as a drag instead of a click on the slide's content.
41
+ * @default 10
42
+ */
43
+ dragThreshold?: number;
44
+ /**
45
+ * Whether a hard swipe may fly past several slides instead of stopping on the next one.
46
+ * @default false
47
+ */
48
+ skipSnaps?: boolean;
49
+ /**
50
+ * How long a move to another slide takes, from 20 (fast) to 60 (slow). Not in milliseconds.
51
+ * @default 25
52
+ */
53
+ duration?: number;
54
+ /**
55
+ * Removes the empty space at the start and end when slides are narrower than SlidesList. 'trimSnaps' also drops the positions landing in that space, 'keepSnaps' keeps them, false keeps the space. Ignored with loop.
56
+ * @default 'trimSnaps'
57
+ */
58
+ containScroll?: false | 'trimSnaps' | 'keepSnaps';
59
+ /**
60
+ * Space between slides, as a number of pixels or any CSS length. Size slides with a className on Slide, like `basis-1/3`; the gap is already subtracted from it.
61
+ * @default 0
62
+ */
63
+ gap?: number | string;
64
+ /**
65
+ * Turns off dragging and every button.
66
+ * @default false
67
+ */
68
+ disabled?: boolean;
69
+ /**
70
+ * Moves to the next slide on its own. Pass true for the defaults, or an object to change them:
71
+ * - delay: milliseconds between slides (default 4000)
72
+ * - jump: jump instead of animating (default false)
73
+ * - playOnInit: start playing as soon as the carousel mounts (default true)
74
+ * - stopOnInteraction: whether a drag, hover or focus stops it for good; set false to resume once the user lets go, moves the mouse away, or moves focus out (default true)
75
+ * - stopOnMouseEnter: stop while the mouse is over SlidesList; it resumes on mouse leave only with stopOnInteraction false (default false)
76
+ * - stopOnFocusIn: stop when focus moves inside SlidesList; it resumes on focus out only with stopOnInteraction false (default true)
77
+ * - stopOnLastSnap: stop on the last slide (default false)
78
+ * @default false
79
+ */
80
+ autoplay?: boolean | Omit<AutoplayOptionsType, 'active' | 'breakpoints' | 'rootNode'>;
81
+ /** The selected slide's position, from 0. Use with onIndexChange to control it yourself. */
82
+ index?: number;
83
+ /**
84
+ * The slide the carousel starts on, when `index` isn't controlled.
85
+ * @default 0
86
+ */
87
+ defaultIndex?: number;
88
+ /** Called with the new position whenever another slide gets selected, by a drag, a button, or autoplay. */
89
+ onIndexChange?: (index: number) => void;
90
+ };
91
+ /**
92
+ * A row of slides users swipe through, built on Embla Carousel. Root holds the carousel's state and renders no element;
93
+ * SlidesList is the visible window and Slide is one item, written by hand so each can hold different content.
94
+ * Previous/Next and Indicator buttons work on their own: no click handlers needed.
95
+ * Every part carries data-orientation, data-disabled, and data-playing (while autoplay runs), so any part can be styled by the carousel's state.
96
+ *
97
+ * @example Complete composition
98
+ * import { Carousel } from '@central-ui/react'
99
+ *
100
+ * function Example() {
101
+ * return (
102
+ * <Carousel.Root loop>
103
+ * <Carousel.SlidesList>
104
+ * {products.map((product) => (
105
+ * <Carousel.Slide key={product.id}>
106
+ * <img src={product.image} alt={product.title} />
107
+ * </Carousel.Slide>
108
+ * ))}
109
+ * </Carousel.SlidesList>
110
+ * <Carousel.PreviousButton aria-label="Previous slide">
111
+ * <Carousel.PreviousButtonIcon />
112
+ * </Carousel.PreviousButton>
113
+ * <Carousel.NextButton aria-label="Next slide">
114
+ * <Carousel.NextButtonIcon />
115
+ * </Carousel.NextButton>
116
+ * <Carousel.IndicatorList>
117
+ * {(index) => <Carousel.IndicatorButton index={index} />}
118
+ * </Carousel.IndicatorList>
119
+ * </Carousel.Root>
120
+ * )
121
+ * }
122
+ *
123
+ * @example Several slides at once
124
+ * // Size slides with Tailwind (responsive classes work); gap is subtracted from that size.
125
+ * ;<Carousel.Root gap={12} align="start" slidesToScroll="auto">
126
+ * <Carousel.SlidesList>
127
+ * <Carousel.Slide className="basis-full md:basis-1/3">…</Carousel.Slide>
128
+ * </Carousel.SlidesList>
129
+ * </Carousel.Root>
130
+ *
131
+ * @example Autoplay, pausing while hovered and resuming on mouse leave
132
+ * ;<Carousel.Root loop autoplay={{ delay: 5000, stopOnMouseEnter: true, stopOnInteraction: false }}>
133
+ *
134
+ * @example Controlled position
135
+ * const [index, setIndex] = useState(0)
136
+ * ;<Carousel.Root index={index} onIndexChange={setIndex}>
137
+ */
138
+ export declare function Root({ orientation, loop, align, slidesToScroll, dragFree, dragThreshold, skipSnaps, duration, containScroll, gap, disabled, autoplay, index, defaultIndex, onIndexChange, children, }: RootProps): import("react").JSX.Element;
139
+ /** The visible window the slides move inside, announced to screen readers as the carousel. Style it freely (background, border, radius); a vertical carousel needs a height here. Put only Slides inside. */
140
+ export declare function SlidesList({ disableStyle, style, children, ref, ...props }: ComponentProps<'div'> & StyleProps): ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
141
+ /**
142
+ * One slide; each can hold different content. Size it with a className, like `basis-1/3`; it fills SlidesList by default.
143
+ * Its padding makes the gap between slides, so put backgrounds, borders and radius on its content, not on the Slide itself.
144
+ */
145
+ export declare function Slide({ disableStyle, style, ref, ...props }: ComponentProps<'div'> & StyleProps): ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
146
+ type ButtonPartProps = ComponentProps<'button'> & AsChildProps & StyleProps;
147
+ /** Moves to the previous slide; no click handler needed. Disabled when there's no slide before (never with loop). Supply its content, normally PreviousButtonIcon, and an accessible label. Pass asChild to give this behavior to your own button instead. */
148
+ export declare function PreviousButton({ ref, ...props }: ButtonPartProps): ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
149
+ /** Moves to the next slide; no click handler needed. Disabled when there's no slide after (never with loop). Supply its content, normally NextButtonIcon, and an accessible label. Pass asChild to give this behavior to your own button instead. */
150
+ export declare function NextButton({ ref, ...props }: ButtonPartProps): ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
151
+ /**
152
+ * Renders one IndicatorButton per position through its children function, which receives that position's index.
153
+ * The count comes from the carousel itself: with several slides per view, it counts positions, not slides.
154
+ *
155
+ * @example Dots
156
+ * ;<Carousel.IndicatorList>
157
+ * {(index) => <Carousel.IndicatorButton index={index} />}
158
+ * </Carousel.IndicatorList>
159
+ */
160
+ export declare function IndicatorList({ disableStyle, children, ref, ...props }: Omit<ComponentProps<'div'>, 'children'> & StyleProps & {
161
+ /** Called once per position with its index; return an IndicatorButton. */
162
+ children: (index: number) => ReactNode;
163
+ }): ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
164
+ /**
165
+ * Moves to its position when clicked, and carries data-selected while that position is shown. Shows as a dot when empty.
166
+ * Pass asChild to give this behavior to your own button instead.
167
+ *
168
+ * @example Thumbnails instead of dots
169
+ * ;<Carousel.IndicatorList>
170
+ * {(index) => (
171
+ * <Carousel.IndicatorButton index={index}>
172
+ * <img src={images[index].thumbnail} alt="" />
173
+ * </Carousel.IndicatorButton>
174
+ * )}
175
+ * </Carousel.IndicatorList>
176
+ */
177
+ export declare function IndicatorButton({ index, ref, ...props }: ButtonPartProps & {
178
+ /** The position this button moves to, from IndicatorList's children function. */
179
+ index: number;
180
+ }): ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
181
+ type BuiltInIconProps = Omit<IconProps, 'children'> & StyleProps & {
182
+ /** Omit for the supplied default icon; pass a child icon only to replace it. */
183
+ children?: ReactElement<SVGIconProps>;
184
+ };
185
+ /** Built-in decorative previous icon. Place inside PreviousButton. Omit children for the supplied default icon; pass an icon only to replace it. Defaults to strokeWidth 1.5. */
186
+ export declare function PreviousButtonIcon({ ref, ...props }: BuiltInIconProps): import("react").JSX.Element;
187
+ /** Built-in decorative next icon. Place inside NextButton. Omit children for the supplied default icon; pass an icon only to replace it. Defaults to strokeWidth 1.5. */
188
+ export declare function NextButtonIcon({ ref, ...props }: BuiltInIconProps): import("react").JSX.Element;
189
+ export {};
@@ -0,0 +1,303 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { asChildStyleAttributes } from "../../utils/asChild.js";
3
+ import { ChevronLeft, ChevronRight } from "../../utils/icons.js";
4
+ import { useDirection } from '@base-ui/react/direction-provider';
5
+ import { mergeProps } from '@base-ui/react/merge-props';
6
+ import { useRender } from '@base-ui/react/use-render';
7
+ import useEmblaCarousel from 'embla-carousel-react';
8
+ import Autoplay from 'embla-carousel-autoplay';
9
+ import { Fragment, cloneElement, createContext, useContext, useEffect, useState } from 'react';
10
+ import { twMerge } from 'tailwind-merge';
11
+ const CarouselContext = /* @__PURE__ */ createContext(null);
12
+ /**
13
+ * A row of slides users swipe through, built on Embla Carousel. Root holds the carousel's state and renders no element;
14
+ * SlidesList is the visible window and Slide is one item, written by hand so each can hold different content.
15
+ * Previous/Next and Indicator buttons work on their own: no click handlers needed.
16
+ * Every part carries data-orientation, data-disabled, and data-playing (while autoplay runs), so any part can be styled by the carousel's state.
17
+ *
18
+ * @example Complete composition
19
+ * import { Carousel } from '@central-ui/react'
20
+ *
21
+ * function Example() {
22
+ * return (
23
+ * <Carousel.Root loop>
24
+ * <Carousel.SlidesList>
25
+ * {products.map((product) => (
26
+ * <Carousel.Slide key={product.id}>
27
+ * <img src={product.image} alt={product.title} />
28
+ * </Carousel.Slide>
29
+ * ))}
30
+ * </Carousel.SlidesList>
31
+ * <Carousel.PreviousButton aria-label="Previous slide">
32
+ * <Carousel.PreviousButtonIcon />
33
+ * </Carousel.PreviousButton>
34
+ * <Carousel.NextButton aria-label="Next slide">
35
+ * <Carousel.NextButtonIcon />
36
+ * </Carousel.NextButton>
37
+ * <Carousel.IndicatorList>
38
+ * {(index) => <Carousel.IndicatorButton index={index} />}
39
+ * </Carousel.IndicatorList>
40
+ * </Carousel.Root>
41
+ * )
42
+ * }
43
+ *
44
+ * @example Several slides at once
45
+ * // Size slides with Tailwind (responsive classes work); gap is subtracted from that size.
46
+ * ;<Carousel.Root gap={12} align="start" slidesToScroll="auto">
47
+ * <Carousel.SlidesList>
48
+ * <Carousel.Slide className="basis-full md:basis-1/3">…</Carousel.Slide>
49
+ * </Carousel.SlidesList>
50
+ * </Carousel.Root>
51
+ *
52
+ * @example Autoplay, pausing while hovered and resuming on mouse leave
53
+ * ;<Carousel.Root loop autoplay={{ delay: 5000, stopOnMouseEnter: true, stopOnInteraction: false }}>
54
+ *
55
+ * @example Controlled position
56
+ * const [index, setIndex] = useState(0)
57
+ * ;<Carousel.Root index={index} onIndexChange={setIndex}>
58
+ */
59
+ export function Root({ orientation = 'horizontal', loop = false, align = 'center', slidesToScroll = 1, dragFree = false, dragThreshold = 10, skipSnaps = false, duration = 25, containScroll = 'trimSnaps', gap = 0, disabled = false, autoplay = false, index, defaultIndex = 0, onIndexChange, children, }) {
60
+ const direction = useDirection();
61
+ // Embla reads the start only once; keeping it fixed stops a controlled index from restarting the carousel
62
+ const [startIndex] = useState(index ?? defaultIndex);
63
+ const [emblaRef, api] = useEmblaCarousel({
64
+ axis: orientation === 'vertical' ? 'y' : 'x',
65
+ direction,
66
+ loop,
67
+ align,
68
+ slidesToScroll,
69
+ dragFree,
70
+ dragThreshold,
71
+ skipSnaps,
72
+ duration,
73
+ containScroll,
74
+ startIndex,
75
+ active: !disabled,
76
+ }, autoplay ? [Autoplay(autoplay === true ? {} : autoplay)] : []);
77
+ const [snapshot, setSnapshot] = useState({
78
+ selectedIndex: startIndex,
79
+ snapCount: 0,
80
+ canScrollPrevious: false,
81
+ canScrollNext: false,
82
+ playing: false,
83
+ });
84
+ //1. Mirroring Embla's state, so the parts re-render when it changes
85
+ useEffect(() => {
86
+ if (!api)
87
+ return;
88
+ function sync(carousel, event) {
89
+ setSnapshot({
90
+ selectedIndex: carousel.selectedScrollSnap(),
91
+ snapCount: carousel.scrollSnapList().length,
92
+ canScrollPrevious: carousel.canScrollPrev(),
93
+ canScrollNext: carousel.canScrollNext(),
94
+ playing: readPlaying(carousel, event),
95
+ });
96
+ }
97
+ // The plugin fires play/stop before updating isPlaying(), so those events decide it themselves
98
+ function readPlaying(carousel, event) {
99
+ if (event === 'autoplay:play')
100
+ return true;
101
+ if (event === 'autoplay:stop')
102
+ return false;
103
+ // Embla starts no plugins while disabled, so plugins() is undefined then
104
+ return carousel.plugins()?.autoplay?.isPlaying() ?? false;
105
+ }
106
+ sync(api);
107
+ api.on('select', sync).on('reInit', sync).on('autoplay:play', sync).on('autoplay:stop', sync);
108
+ return () => {
109
+ api.off('select', sync).off('reInit', sync).off('autoplay:play', sync).off('autoplay:stop', sync);
110
+ };
111
+ }, [api]);
112
+ //2. Reporting every newly selected slide
113
+ useEffect(() => {
114
+ if (!api || !onIndexChange)
115
+ return;
116
+ function handleSelect(carousel) {
117
+ onIndexChange?.(carousel.selectedScrollSnap());
118
+ }
119
+ api.on('select', handleSelect);
120
+ return () => {
121
+ api.off('select', handleSelect);
122
+ };
123
+ }, [api, onIndexChange]);
124
+ //3. Moving to a controlled index set from outside
125
+ useEffect(() => {
126
+ if (!api || index === undefined || index === api.selectedScrollSnap())
127
+ return;
128
+ api.scrollTo(index);
129
+ }, [api, index]);
130
+ const context = { emblaRef, api, orientation, disabled, gap, ...snapshot };
131
+ return _jsx(CarouselContext.Provider, { value: context, children: children });
132
+ }
133
+ /** The visible window the slides move inside, announced to screen readers as the carousel. Style it freely (background, border, radius); a vertical carousel needs a height here. Put only Slides inside. */
134
+ export function SlidesList({ disableStyle, style, children, ref, ...props }) {
135
+ const context = useCarouselContext('SlidesList');
136
+ const vertical = context.orientation === 'vertical';
137
+ // The track Embla moves; its negative margin cancels the first slide's gap
138
+ const track = (_jsx("div", { style: {
139
+ display: 'flex',
140
+ flexDirection: vertical ? 'column' : 'row',
141
+ height: vertical ? '100%' : undefined,
142
+ touchAction: vertical ? 'pan-x pinch-zoom' : 'pan-y pinch-zoom',
143
+ [vertical ? 'marginBlockStart' : 'marginInlineStart']: negativeLength(context.gap),
144
+ }, children: children }));
145
+ return useRender({
146
+ ref: [ref ?? null, context.emblaRef],
147
+ state: partState(context),
148
+ props: {
149
+ role: 'region',
150
+ 'aria-roledescription': 'carousel',
151
+ ...props,
152
+ style: { overflow: 'hidden', ...style },
153
+ 'data-component': 'carousel-slideslist',
154
+ 'data-style-disabled': disableStyle ? '' : undefined,
155
+ children: track,
156
+ },
157
+ });
158
+ }
159
+ /**
160
+ * One slide; each can hold different content. Size it with a className, like `basis-1/3`; it fills SlidesList by default.
161
+ * Its padding makes the gap between slides, so put backgrounds, borders and radius on its content, not on the Slide itself.
162
+ */
163
+ export function Slide({ disableStyle, style, ref, ...props }) {
164
+ const context = useCarouselContext('Slide');
165
+ const vertical = context.orientation === 'vertical';
166
+ return useRender({
167
+ ref,
168
+ state: partState(context),
169
+ props: {
170
+ role: 'group',
171
+ 'aria-roledescription': 'slide',
172
+ ...props,
173
+ style: {
174
+ flexShrink: 0,
175
+ minWidth: 0,
176
+ minHeight: 0,
177
+ boxSizing: 'border-box',
178
+ [vertical ? 'paddingBlockStart' : 'paddingInlineStart']: context.gap,
179
+ ...style,
180
+ },
181
+ 'data-component': 'carousel-slide',
182
+ 'data-style-disabled': disableStyle ? '' : undefined,
183
+ },
184
+ });
185
+ }
186
+ /** Moves to the previous slide; no click handler needed. Disabled when there's no slide before (never with loop). Supply its content, normally PreviousButtonIcon, and an accessible label. Pass asChild to give this behavior to your own button instead. */
187
+ export function PreviousButton({ ref, ...props }) {
188
+ const context = useCarouselContext('PreviousButton');
189
+ return useActionButton(props, ref, context, {
190
+ component: 'carousel-previousbutton',
191
+ disabled: context.disabled || !context.canScrollPrevious,
192
+ onClick: () => context.api?.scrollPrev(),
193
+ });
194
+ }
195
+ /** Moves to the next slide; no click handler needed. Disabled when there's no slide after (never with loop). Supply its content, normally NextButtonIcon, and an accessible label. Pass asChild to give this behavior to your own button instead. */
196
+ export function NextButton({ ref, ...props }) {
197
+ const context = useCarouselContext('NextButton');
198
+ return useActionButton(props, ref, context, {
199
+ component: 'carousel-nextbutton',
200
+ disabled: context.disabled || !context.canScrollNext,
201
+ onClick: () => context.api?.scrollNext(),
202
+ });
203
+ }
204
+ /**
205
+ * Renders one IndicatorButton per position through its children function, which receives that position's index.
206
+ * The count comes from the carousel itself: with several slides per view, it counts positions, not slides.
207
+ *
208
+ * @example Dots
209
+ * ;<Carousel.IndicatorList>
210
+ * {(index) => <Carousel.IndicatorButton index={index} />}
211
+ * </Carousel.IndicatorList>
212
+ */
213
+ export function IndicatorList({ disableStyle, children, ref, ...props }) {
214
+ const context = useCarouselContext('IndicatorList');
215
+ return useRender({
216
+ ref,
217
+ state: partState(context),
218
+ props: {
219
+ ...props,
220
+ 'data-component': 'carousel-indicatorlist',
221
+ 'data-style-disabled': disableStyle ? '' : undefined,
222
+ children: Array.from({ length: context.snapCount }, (_, index) => _jsx(Fragment, { children: children(index) }, index)),
223
+ },
224
+ });
225
+ }
226
+ /**
227
+ * Moves to its position when clicked, and carries data-selected while that position is shown. Shows as a dot when empty.
228
+ * Pass asChild to give this behavior to your own button instead.
229
+ *
230
+ * @example Thumbnails instead of dots
231
+ * ;<Carousel.IndicatorList>
232
+ * {(index) => (
233
+ * <Carousel.IndicatorButton index={index}>
234
+ * <img src={images[index].thumbnail} alt="" />
235
+ * </Carousel.IndicatorButton>
236
+ * )}
237
+ * </Carousel.IndicatorList>
238
+ */
239
+ export function IndicatorButton({ index, ref, ...props }) {
240
+ const context = useCarouselContext('IndicatorButton');
241
+ const selected = context.selectedIndex === index;
242
+ return useActionButton(props, ref, context, {
243
+ component: 'carousel-indicatorbutton',
244
+ disabled: context.disabled,
245
+ selected,
246
+ attributes: { 'aria-label': `Go to slide ${index + 1}`, 'aria-current': selected ? 'true' : undefined },
247
+ onClick: () => context.api?.scrollTo(index),
248
+ });
249
+ }
250
+ /** Built-in decorative previous icon. Place inside PreviousButton. Omit children for the supplied default icon; pass an icon only to replace it. Defaults to strokeWidth 1.5. */
251
+ export function PreviousButtonIcon({ ref, ...props }) {
252
+ return _jsx(BuiltInIcon, { ...props, ref: ref, icon: ChevronLeft, component: "carousel-previousbuttonicon" });
253
+ }
254
+ /** Built-in decorative next icon. Place inside NextButton. Omit children for the supplied default icon; pass an icon only to replace it. Defaults to strokeWidth 1.5. */
255
+ export function NextButtonIcon({ ref, ...props }) {
256
+ return _jsx(BuiltInIcon, { ...props, ref: ref, icon: ChevronRight, component: "carousel-nextbuttonicon" });
257
+ }
258
+ // The shared body of the built-in decorative icons above, which only differ by their icon and component name
259
+ function BuiltInIcon({ disableStyle, className, strokeWidth, icon: Icon, component, children, ref, ...props }) {
260
+ const attributes = partAttributes(useCarouselContext('Icon'));
261
+ return cloneElement(children ?? _jsx(Icon, {}), {
262
+ ...attributes,
263
+ 'aria-hidden': true,
264
+ ...props,
265
+ ref,
266
+ strokeWidth: strokeWidth ?? children?.props.strokeWidth ?? 1.5,
267
+ 'data-component': component,
268
+ 'data-style-disabled': disableStyle ? '' : undefined,
269
+ className: twMerge(children?.props.className, className),
270
+ });
271
+ }
272
+ // The shared body of the action buttons: the part's own styled button, or the consumer's child with asChild
273
+ function useActionButton({ asChild, disableStyle, children, ...props }, ref, context, internal) {
274
+ return useRender({
275
+ render: asChild ? children : undefined,
276
+ defaultTagName: 'button',
277
+ ref,
278
+ state: { ...partState(context), disabled: internal.disabled, selected: internal.selected ?? false },
279
+ props: mergeProps({ type: 'button', disabled: internal.disabled, onClick: internal.onClick, ...internal.attributes }, props, asChild ? {} : { children, ...asChildStyleAttributes({ asChild, component: internal.component, disableStyle }) }),
280
+ });
281
+ }
282
+ // The carousel's state every part exposes as data attributes
283
+ function partState(context) {
284
+ return { orientation: context.orientation, disabled: context.disabled, playing: context.playing };
285
+ }
286
+ // The same data attributes, for the icons, which render without useRender
287
+ function partAttributes(context) {
288
+ return {
289
+ 'data-orientation': context.orientation,
290
+ 'data-disabled': context.disabled ? '' : undefined,
291
+ 'data-playing': context.playing ? '' : undefined,
292
+ };
293
+ }
294
+ // Turning the gap into the track's offset, whether it's a number of pixels or a CSS length
295
+ function negativeLength(length) {
296
+ return typeof length === 'number' ? -length : `calc(${length} * -1)`;
297
+ }
298
+ function useCarouselContext(part) {
299
+ const context = useContext(CarouselContext);
300
+ if (!context)
301
+ throw new Error(`Carousel.${part} must be placed inside Carousel.Root.`);
302
+ return context;
303
+ }
@@ -1,5 +1,6 @@
1
1
  import type { StyleProps } from '../../types/Style.ts';
2
2
  import { Checkbox as BaseCheckbox } from '@base-ui/react/checkbox';
3
+ import { CheckboxGroup as BaseCheckboxGroup } from '@base-ui/react/checkbox-group';
3
4
  import type { ComponentProps, ReactNode } from 'react';
4
5
  import type { ValidationProps } from '../../types/Validation.ts';
5
6
  import type { ValidationMessageChildren } from '../../utils/Validation.ts';
@@ -44,25 +45,25 @@ export type RootProps = Pick<BaseCheckbox.Root.Props, CoreKeys> & ValidationProp
44
45
  */
45
46
  export declare function Root({ children, id, validation, ...coreProps }: RootProps): import("react").JSX.Element;
46
47
  /** Optional: controls multiple checkboxes as a native array of checked values. Put individual Roots inside and give each child a value. For a select-all checkbox, use parent on its Root and provide allValues on this controlled group; Base UI derives checked and indeterminate automatically. */
47
- export declare const CheckboxGroup: import("react").ForwardRefExoticComponent<Omit<Omit<import("@base-ui/react").CheckboxGroupProps, "className"> & {
48
+ export declare function CheckboxGroup({ disableStyle, className, ref, ...props }: (Omit<BaseCheckboxGroup.Props, 'className'> & {
48
49
  className?: string;
49
- } & StyleProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
50
- export declare const Container: import("react").ForwardRefExoticComponent<Omit<import("react").ClassAttributes<HTMLDivElement> & import("react").HTMLAttributes<HTMLDivElement> & StyleProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
51
- export declare const Button: import("react").ForwardRefExoticComponent<Omit<Omit<import("@base-ui/react").CheckboxRootProps, "className" | "render" | "nativeButton" | CoreKeys> & {
50
+ }) & StyleProps): import("react").JSX.Element;
51
+ export declare function Container({ disableStyle, className, ref, ...props }: ComponentProps<'div'> & StyleProps): import("react").JSX.Element;
52
+ export declare function Button({ disableStyle, className, ref, ...props }: (Omit<BaseCheckbox.Root.Props, CoreKeys | 'className' | 'render' | 'nativeButton'> & {
52
53
  className?: string;
53
- } & StyleProps, "ref"> & import("react").RefAttributes<HTMLElement>>;
54
+ }) & StyleProps): import("react").JSX.Element;
54
55
  /** Tracks the surrounding item state automatically. Keep it mounted; do not add your own visibility condition. Use the supplied default visual unless customization is needed. */
55
- export declare const Indicator: import("react").ForwardRefExoticComponent<Omit<Omit<import("@base-ui/react").CheckboxIndicatorProps, "className" | "children" | "render"> & {
56
+ export declare function Indicator({ disableStyle, className, ref, ...props }: (Omit<BaseCheckbox.Indicator.Props, 'className' | 'children' | 'render'> & {
56
57
  className?: string;
57
- } & StyleProps, "ref"> & import("react").RefAttributes<HTMLSpanElement>>;
58
+ }) & StyleProps): import("react").JSX.Element;
58
59
  /** Labels the associated control automatically. Supply the visible text; do not wire a second label association. */
59
- export declare const Label: import("react").ForwardRefExoticComponent<Omit<Omit<import("react").DetailedHTMLProps<import("react").LabelHTMLAttributes<HTMLLabelElement>, HTMLLabelElement>, "htmlFor"> & StyleProps, "ref"> & import("react").RefAttributes<HTMLLabelElement>>;
60
- export declare const Description: import("react").ForwardRefExoticComponent<Omit<import("react").ClassAttributes<HTMLSpanElement> & import("react").HTMLAttributes<HTMLSpanElement> & StyleProps, "ref"> & import("react").RefAttributes<HTMLSpanElement>>;
60
+ export declare function Label({ disableStyle, className, ref, ...props }: Omit<ComponentProps<'label'>, 'htmlFor'> & StyleProps): import("react").JSX.Element;
61
+ export declare function Description({ disableStyle, className, id, ref, ...props }: ComponentProps<'span'> & StyleProps): import("react").JSX.Element;
61
62
  type MessageProps = Omit<ComponentProps<'span'>, 'children'> & StyleProps & {
62
63
  children?: ValidationMessageChildren;
63
64
  };
64
65
  /** Shows itself only while Root's validation is `'error'`: with no children it shows the message from `['error', message]`, a function children formats that message, and other children replace it. */
65
- export declare const Error: import("react").ForwardRefExoticComponent<Omit<MessageProps, "ref"> & import("react").RefAttributes<HTMLSpanElement>>;
66
+ export declare function Error({ children, ref, ...props }: MessageProps): import("react").JSX.Element | null;
66
67
  /** Shows itself only while Root's validation is `'success'`: with no children it shows the message from `['success', message]`, a function children formats that message, and other children replace it. */
67
- export declare const Success: import("react").ForwardRefExoticComponent<Omit<MessageProps, "ref"> & import("react").RefAttributes<HTMLSpanElement>>;
68
+ export declare function Success({ children, ref, ...props }: MessageProps): import("react").JSX.Element | null;
68
69
  export {};