@arolariu/components 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +57 -0
- package/EXAMPLES.md +2510 -0
- package/dist/components/ui/alert-dialog.d.ts +4 -16
- package/dist/components/ui/alert-dialog.d.ts.map +1 -1
- package/dist/components/ui/alert-dialog.js +18 -14
- package/dist/components/ui/alert-dialog.js.map +1 -1
- package/dist/components/ui/avatar.d.ts +3 -12
- package/dist/components/ui/avatar.d.ts.map +1 -1
- package/dist/components/ui/avatar.js +18 -15
- package/dist/components/ui/avatar.js.map +1 -1
- package/dist/components/ui/button-group.d.ts +1 -1
- package/dist/components/ui/button-group.d.ts.map +1 -1
- package/dist/components/ui/calendar.d.ts +1 -4
- package/dist/components/ui/calendar.d.ts.map +1 -1
- package/dist/components/ui/calendar.js +7 -7
- package/dist/components/ui/calendar.js.map +1 -1
- package/dist/components/ui/carousel.d.ts.map +1 -1
- package/dist/components/ui/carousel.js.map +1 -1
- package/dist/components/ui/chart.d.ts.map +1 -1
- package/dist/components/ui/chart.js +125 -59
- package/dist/components/ui/chart.js.map +1 -1
- package/dist/components/ui/checkbox-group.d.ts +2 -6
- package/dist/components/ui/checkbox-group.d.ts.map +1 -1
- package/dist/components/ui/checkbox-group.js +8 -7
- package/dist/components/ui/checkbox-group.js.map +1 -1
- package/dist/components/ui/checkbox.d.ts +3 -1
- package/dist/components/ui/checkbox.d.ts.map +1 -1
- package/dist/components/ui/checkbox.js +4 -1
- package/dist/components/ui/checkbox.js.map +1 -1
- package/dist/components/ui/collapsible.d.ts.map +1 -1
- package/dist/components/ui/collapsible.js.map +1 -1
- package/dist/components/ui/combobox.d.ts +335 -0
- package/dist/components/ui/combobox.d.ts.map +1 -0
- package/dist/components/ui/combobox.js +206 -0
- package/dist/components/ui/combobox.js.map +1 -0
- package/dist/components/ui/combobox.module.js +23 -0
- package/dist/components/ui/combobox.module.js.map +1 -0
- package/dist/components/ui/combobox_module.css +142 -0
- package/dist/components/ui/combobox_module.css.map +1 -0
- package/dist/components/ui/command.d.ts.map +1 -1
- package/dist/components/ui/command.js +25 -16
- package/dist/components/ui/command.js.map +1 -1
- package/dist/components/ui/context-menu.d.ts.map +1 -1
- package/dist/components/ui/context-menu.js.map +1 -1
- package/dist/components/ui/drawer.d.ts.map +1 -1
- package/dist/components/ui/drawer.js.map +1 -1
- package/dist/components/ui/dropdown-menu.d.ts.map +1 -1
- package/dist/components/ui/dropdown-menu.js.map +1 -1
- package/dist/components/ui/dropdrawer.d.ts +10 -16
- package/dist/components/ui/dropdrawer.d.ts.map +1 -1
- package/dist/components/ui/dropdrawer.js +28 -20
- package/dist/components/ui/dropdrawer.js.map +1 -1
- package/dist/components/ui/item.d.ts +1 -1
- package/dist/components/ui/item.d.ts.map +1 -1
- package/dist/components/ui/menubar.d.ts +11 -13
- package/dist/components/ui/menubar.d.ts.map +1 -1
- package/dist/components/ui/menubar.js.map +1 -1
- package/dist/components/ui/meter.d.ts +8 -24
- package/dist/components/ui/meter.d.ts.map +1 -1
- package/dist/components/ui/meter.js +23 -19
- package/dist/components/ui/meter.js.map +1 -1
- package/dist/components/ui/navigation-menu.d.ts +3 -12
- package/dist/components/ui/navigation-menu.d.ts.map +1 -1
- package/dist/components/ui/navigation-menu.js +14 -11
- package/dist/components/ui/navigation-menu.js.map +1 -1
- package/dist/components/ui/number-field.d.ts +6 -12
- package/dist/components/ui/number-field.d.ts.map +1 -1
- package/dist/components/ui/number-field.js.map +1 -1
- package/dist/components/ui/progress.d.ts +1 -4
- package/dist/components/ui/progress.d.ts.map +1 -1
- package/dist/components/ui/progress.js +10 -9
- package/dist/components/ui/progress.js.map +1 -1
- package/dist/components/ui/radio-group.d.ts +2 -4
- package/dist/components/ui/radio-group.d.ts.map +1 -1
- package/dist/components/ui/radio-group.js.map +1 -1
- package/dist/components/ui/resizable.d.ts +3 -3
- package/dist/components/ui/resizable.d.ts.map +1 -1
- package/dist/components/ui/resizable.js.map +1 -1
- package/dist/components/ui/scratcher.d.ts +1 -1
- package/dist/components/ui/scratcher.d.ts.map +1 -1
- package/dist/components/ui/scratcher.js +5 -4
- package/dist/components/ui/scratcher.js.map +1 -1
- package/dist/components/ui/scroll-area.d.ts +2 -4
- package/dist/components/ui/scroll-area.d.ts.map +1 -1
- package/dist/components/ui/scroll-area.js.map +1 -1
- package/dist/components/ui/separator.d.ts +1 -4
- package/dist/components/ui/separator.d.ts.map +1 -1
- package/dist/components/ui/separator.js +9 -8
- package/dist/components/ui/separator.js.map +1 -1
- package/dist/components/ui/sheet.d.ts.map +1 -1
- package/dist/components/ui/sheet.js.map +1 -1
- package/dist/components/ui/sidebar.d.ts +1 -1
- package/dist/components/ui/sidebar.d.ts.map +1 -1
- package/dist/components/ui/sidebar.js.map +1 -1
- package/dist/components/ui/sonner.d.ts +5 -4
- package/dist/components/ui/sonner.d.ts.map +1 -1
- package/dist/components/ui/sonner.js +7 -6
- package/dist/components/ui/sonner.js.map +1 -1
- package/dist/components/ui/toggle-group.d.ts +2 -8
- package/dist/components/ui/toggle-group.d.ts.map +1 -1
- package/dist/components/ui/toggle-group.js +12 -10
- package/dist/components/ui/toggle-group.js.map +1 -1
- package/dist/components/ui/toolbar.d.ts +10 -30
- package/dist/components/ui/toolbar.d.ts.map +1 -1
- package/dist/components/ui/toolbar.js +28 -23
- package/dist/components/ui/toolbar.js.map +1 -1
- package/dist/hooks/useClipboard.d.ts +77 -0
- package/dist/hooks/useClipboard.d.ts.map +1 -0
- package/dist/hooks/useClipboard.js +42 -0
- package/dist/hooks/useClipboard.js.map +1 -0
- package/dist/hooks/useControllableState.d.ts +54 -0
- package/dist/hooks/useControllableState.d.ts.map +1 -0
- package/dist/hooks/useControllableState.js +29 -0
- package/dist/hooks/useControllableState.js.map +1 -0
- package/dist/hooks/useDebounce.d.ts +33 -0
- package/dist/hooks/useDebounce.d.ts.map +1 -0
- package/dist/hooks/useDebounce.js +20 -0
- package/dist/hooks/useDebounce.js.map +1 -0
- package/dist/hooks/useEventCallback.d.ts +34 -0
- package/dist/hooks/useEventCallback.d.ts.map +1 -0
- package/dist/hooks/useEventCallback.js +12 -0
- package/dist/hooks/useEventCallback.js.map +1 -0
- package/dist/hooks/useId.d.ts +30 -0
- package/dist/hooks/useId.d.ts.map +1 -0
- package/dist/hooks/useId.js +9 -0
- package/dist/hooks/useId.js.map +1 -0
- package/dist/hooks/useIntersectionObserver.d.ts +51 -0
- package/dist/hooks/useIntersectionObserver.d.ts.map +1 -0
- package/dist/hooks/useIntersectionObserver.js +25 -0
- package/dist/hooks/useIntersectionObserver.js.map +1 -0
- package/dist/hooks/useInterval.d.ts +55 -0
- package/dist/hooks/useInterval.d.ts.map +1 -0
- package/dist/hooks/useInterval.js +24 -0
- package/dist/hooks/useInterval.js.map +1 -0
- package/dist/hooks/useLocalStorage.d.ts +43 -0
- package/dist/hooks/useLocalStorage.d.ts.map +1 -0
- package/dist/hooks/useLocalStorage.js +53 -0
- package/dist/hooks/useLocalStorage.js.map +1 -0
- package/dist/hooks/useMergedRefs.d.ts +27 -0
- package/dist/hooks/useMergedRefs.d.ts.map +1 -0
- package/dist/hooks/useMergedRefs.js +11 -0
- package/dist/hooks/useMergedRefs.js.map +1 -0
- package/dist/hooks/useOnClickOutside.d.ts +32 -0
- package/dist/hooks/useOnClickOutside.d.ts.map +1 -0
- package/dist/hooks/useOnClickOutside.js +23 -0
- package/dist/hooks/useOnClickOutside.js.map +1 -0
- package/dist/hooks/usePrevious.d.ts +33 -0
- package/dist/hooks/usePrevious.d.ts.map +1 -0
- package/dist/hooks/usePrevious.js +14 -0
- package/dist/hooks/usePrevious.js.map +1 -0
- package/dist/hooks/useThrottle.d.ts +37 -0
- package/dist/hooks/useThrottle.d.ts.map +1 -0
- package/dist/hooks/useThrottle.js +34 -0
- package/dist/hooks/useThrottle.js.map +1 -0
- package/dist/hooks/useTimeout.d.ts +28 -0
- package/dist/hooks/useTimeout.d.ts.map +1 -0
- package/dist/hooks/useTimeout.js +24 -0
- package/dist/hooks/useTimeout.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -0
- package/dist/lib/utilities.d.ts +2 -3
- package/dist/lib/utilities.d.ts.map +1 -1
- package/dist/lib/utilities.js.map +1 -1
- package/dist/motion/tokens.js +5 -5
- package/dist/motion/tokens.js.map +1 -1
- package/dist/rslib-runtime.js +39 -0
- package/dist/rslib-runtime.js.map +1 -0
- package/package.json +82 -3
- package/src/components/ui/alert-dialog.tsx +15 -8
- package/src/components/ui/avatar.tsx +9 -6
- package/src/components/ui/calendar.tsx +7 -13
- package/src/components/ui/carousel.tsx +2 -0
- package/src/components/ui/chart.tsx +63 -60
- package/src/components/ui/checkbox-group.tsx +4 -5
- package/src/components/ui/checkbox.tsx +10 -2
- package/src/components/ui/collapsible.tsx +1 -0
- package/src/components/ui/combobox.module.css +158 -0
- package/src/components/ui/combobox.tsx +569 -0
- package/src/components/ui/command.tsx +31 -15
- package/src/components/ui/context-menu.tsx +3 -0
- package/src/components/ui/drawer.tsx +2 -0
- package/src/components/ui/dropdown-menu.tsx +3 -0
- package/src/components/ui/dropdrawer.tsx +80 -62
- package/src/components/ui/menubar.tsx +9 -10
- package/src/components/ui/meter.tsx +16 -17
- package/src/components/ui/navigation-menu.tsx +41 -33
- package/src/components/ui/number-field.tsx +6 -13
- package/src/components/ui/progress.tsx +3 -2
- package/src/components/ui/radio-group.tsx +2 -5
- package/src/components/ui/resizable.tsx +2 -2
- package/src/components/ui/scratcher.tsx +6 -10
- package/src/components/ui/scroll-area.tsx +2 -5
- package/src/components/ui/separator.tsx +4 -3
- package/src/components/ui/sheet.tsx +3 -0
- package/src/components/ui/sidebar.tsx +1 -0
- package/src/components/ui/sonner.tsx +20 -12
- package/src/components/ui/toggle-group.tsx +6 -4
- package/src/components/ui/toolbar.tsx +20 -21
- package/src/hooks/useClipboard.tsx +137 -0
- package/src/hooks/useControllableState.tsx +81 -0
- package/src/hooks/useDebounce.tsx +50 -0
- package/src/hooks/useEventCallback.tsx +47 -0
- package/src/hooks/useId.tsx +36 -0
- package/src/hooks/useIntersectionObserver.tsx +81 -0
- package/src/hooks/useInterval.tsx +80 -0
- package/src/hooks/useLocalStorage.tsx +111 -0
- package/src/hooks/useMergedRefs.tsx +48 -0
- package/src/hooks/useOnClickOutside.tsx +55 -0
- package/src/hooks/usePrevious.tsx +44 -0
- package/src/hooks/useThrottle.tsx +78 -0
- package/src/hooks/useTimeout.tsx +51 -0
- package/src/index.ts +23 -0
- package/src/lib/utilities.ts +4 -4
- package/src/motion/tokens.ts +4 -4
- package/src/stories/DesignPrinciples.mdx +48 -0
- package/src/stories/GettingStarted.mdx +92 -0
- package/src/stories/Welcome.mdx +44 -0
|
@@ -33,13 +33,14 @@ export interface ProgressProps extends Omit<React.ComponentPropsWithRef<typeof B
|
|
|
33
33
|
*
|
|
34
34
|
* @see {@link https://base-ui.com/react/components/progress | Base UI Documentation}
|
|
35
35
|
*/
|
|
36
|
-
|
|
36
|
+
const Progress = React.forwardRef<HTMLDivElement, Progress.Props>(function Progress(props, forwardedRef) {
|
|
37
37
|
const {className, render, value = 0, ...otherProps} = props;
|
|
38
38
|
|
|
39
39
|
return (
|
|
40
40
|
<BaseProgress.Root
|
|
41
41
|
value={value}
|
|
42
42
|
{...otherProps}
|
|
43
|
+
ref={forwardedRef}
|
|
43
44
|
render={useRender({
|
|
44
45
|
defaultTagName: "div",
|
|
45
46
|
render: render as never,
|
|
@@ -49,7 +50,7 @@ function Progress(props: Readonly<Progress.Props>): React.ReactElement {
|
|
|
49
50
|
</BaseProgress.Track>
|
|
50
51
|
</BaseProgress.Root>
|
|
51
52
|
);
|
|
52
|
-
}
|
|
53
|
+
});
|
|
53
54
|
Progress.displayName = "Progress";
|
|
54
55
|
|
|
55
56
|
// eslint-disable-next-line no-redeclare -- required for the canonical component namespace typing API
|
|
@@ -10,9 +10,6 @@ import * as React from "react";
|
|
|
10
10
|
import {cn} from "@/lib/utilities";
|
|
11
11
|
import styles from "./radio-group.module.css";
|
|
12
12
|
|
|
13
|
-
type RadioGroupProps = React.ComponentPropsWithRef<typeof BaseRadioGroup>;
|
|
14
|
-
type RadioGroupItemProps = React.ComponentPropsWithRef<typeof Radio.Root>;
|
|
15
|
-
|
|
16
13
|
/**
|
|
17
14
|
* Coordinates radio group state and selection behavior.
|
|
18
15
|
*
|
|
@@ -84,13 +81,13 @@ const RadioGroupItem = React.forwardRef<React.ComponentRef<typeof Radio.Root>, R
|
|
|
84
81
|
|
|
85
82
|
// eslint-disable-next-line no-redeclare -- required for the canonical component namespace typing API
|
|
86
83
|
namespace RadioGroup {
|
|
87
|
-
export type Props =
|
|
84
|
+
export type Props = React.ComponentPropsWithRef<typeof BaseRadioGroup>;
|
|
88
85
|
export type State = BaseRadioGroup.State;
|
|
89
86
|
}
|
|
90
87
|
|
|
91
88
|
// eslint-disable-next-line no-redeclare -- required for the canonical component namespace typing API
|
|
92
89
|
namespace RadioGroupItem {
|
|
93
|
-
export type Props =
|
|
90
|
+
export type Props = React.ComponentPropsWithRef<typeof Radio.Root>;
|
|
94
91
|
export type State = Radio.Root.State;
|
|
95
92
|
}
|
|
96
93
|
|
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
import {GripVertical} from "lucide-react";
|
|
6
6
|
import * as React from "react";
|
|
7
|
-
import type {ImperativePanelGroupHandle
|
|
7
|
+
import type {ImperativePanelGroupHandle} from "react-resizable-panels";
|
|
8
8
|
import * as ResizablePrimitive from "react-resizable-panels";
|
|
9
9
|
|
|
10
10
|
import {cn} from "@/lib/utilities";
|
|
11
11
|
|
|
12
12
|
import styles from "./resizable.module.css";
|
|
13
13
|
|
|
14
|
-
export type {ImperativePanelGroupHandle, ImperativePanelHandle};
|
|
14
|
+
export type {ImperativePanelGroupHandle, ImperativePanelHandle} from "react-resizable-panels";
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Props for the {@link ResizablePanelGroup} component.
|
|
@@ -43,15 +43,10 @@ const ignoreAnimationError = (): null => null;
|
|
|
43
43
|
*
|
|
44
44
|
* @see {@link ScratcherProps} for available props
|
|
45
45
|
*/
|
|
46
|
-
export const Scratcher
|
|
47
|
-
width,
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
onComplete,
|
|
51
|
-
children,
|
|
52
|
-
className,
|
|
53
|
-
gradientColors = defaultGradientColors,
|
|
54
|
-
}): React.JSX.Element => {
|
|
46
|
+
export const Scratcher = React.forwardRef<HTMLDivElement, ScratcherProps>(function Scratcher(
|
|
47
|
+
{width, height, minScratchPercentage = 50, onComplete, children, className, gradientColors = defaultGradientColors},
|
|
48
|
+
forwardedRef,
|
|
49
|
+
) {
|
|
55
50
|
const canvasRef = useRef<HTMLCanvasElement>(null);
|
|
56
51
|
const [isScratching, setIsScratching] = useState(false);
|
|
57
52
|
const [isComplete, setIsComplete] = useState(false);
|
|
@@ -173,6 +168,7 @@ export const Scratcher: React.FC<ScratcherProps> = ({
|
|
|
173
168
|
|
|
174
169
|
return (
|
|
175
170
|
<motion.div
|
|
171
|
+
ref={forwardedRef}
|
|
176
172
|
className={cn(styles.root, className)}
|
|
177
173
|
style={{
|
|
178
174
|
width,
|
|
@@ -192,6 +188,6 @@ export const Scratcher: React.FC<ScratcherProps> = ({
|
|
|
192
188
|
{children}
|
|
193
189
|
</motion.div>
|
|
194
190
|
);
|
|
195
|
-
};
|
|
191
|
+
});
|
|
196
192
|
|
|
197
193
|
Scratcher.displayName = "Scratcher";
|
|
@@ -8,9 +8,6 @@ import * as React from "react";
|
|
|
8
8
|
import {cn} from "@/lib/utilities";
|
|
9
9
|
import styles from "./scroll-area.module.css";
|
|
10
10
|
|
|
11
|
-
type ScrollAreaProps = React.ComponentPropsWithRef<typeof BaseScrollArea.Root>;
|
|
12
|
-
type ScrollBarProps = React.ComponentPropsWithRef<typeof BaseScrollArea.Scrollbar>;
|
|
13
|
-
|
|
14
11
|
/**
|
|
15
12
|
* Coordinates scroll area layout and scrolling behavior.
|
|
16
13
|
*
|
|
@@ -88,13 +85,13 @@ function ScrollBar(props: Readonly<ScrollBar.Props>): React.ReactElement {
|
|
|
88
85
|
|
|
89
86
|
// eslint-disable-next-line no-redeclare -- required for the canonical component namespace typing API
|
|
90
87
|
namespace ScrollArea {
|
|
91
|
-
export type Props =
|
|
88
|
+
export type Props = React.ComponentPropsWithRef<typeof BaseScrollArea.Root>;
|
|
92
89
|
export type State = BaseScrollArea.Root.State;
|
|
93
90
|
}
|
|
94
91
|
|
|
95
92
|
// eslint-disable-next-line no-redeclare -- required for the canonical component namespace typing API
|
|
96
93
|
namespace ScrollBar {
|
|
97
|
-
export type Props =
|
|
94
|
+
export type Props = React.ComponentPropsWithRef<typeof BaseScrollArea.Scrollbar>;
|
|
98
95
|
export type State = BaseScrollArea.Scrollbar.State;
|
|
99
96
|
}
|
|
100
97
|
|
|
@@ -36,13 +36,14 @@ export interface SeparatorProps extends Omit<React.ComponentPropsWithRef<typeof
|
|
|
36
36
|
*
|
|
37
37
|
* @see {@link https://base-ui.com/react/components/separator | Base UI Documentation}
|
|
38
38
|
*/
|
|
39
|
-
|
|
40
|
-
const {className,
|
|
39
|
+
const Separator = React.forwardRef<HTMLDivElement, Separator.Props>(function Separator(props, forwardedRef) {
|
|
40
|
+
const {className, orientation = "horizontal", render, ...otherProps} = props;
|
|
41
41
|
|
|
42
42
|
return (
|
|
43
43
|
<BaseSeparator
|
|
44
44
|
orientation={orientation}
|
|
45
45
|
{...otherProps}
|
|
46
|
+
ref={forwardedRef}
|
|
46
47
|
render={useRender({
|
|
47
48
|
defaultTagName: "div",
|
|
48
49
|
render: render as never,
|
|
@@ -55,7 +56,7 @@ function Separator(props: Readonly<Separator.Props>): React.ReactElement {
|
|
|
55
56
|
})}
|
|
56
57
|
/>
|
|
57
58
|
);
|
|
58
|
-
}
|
|
59
|
+
});
|
|
59
60
|
Separator.displayName = "Separator";
|
|
60
61
|
|
|
61
62
|
// eslint-disable-next-line no-redeclare -- required for the canonical component namespace typing API
|
|
@@ -171,6 +171,7 @@ const SheetClose = BaseDialog.Close;
|
|
|
171
171
|
*/
|
|
172
172
|
const SheetTrigger = React.forwardRef<HTMLButtonElement, SheetTrigger.Props>(
|
|
173
173
|
(props: Readonly<SheetTrigger.Props>, ref): React.ReactElement => {
|
|
174
|
+
// eslint-disable-next-line sonarjs/deprecation -- backward-compatible asChild API
|
|
174
175
|
const {asChild = false, children, className, render, ...otherProps} = props;
|
|
175
176
|
const renderProp = asChild && React.isValidElement(children) ? children : render;
|
|
176
177
|
|
|
@@ -277,6 +278,7 @@ const SheetContent = React.forwardRef<React.ComponentRef<typeof BaseDialog.Popup
|
|
|
277
278
|
* @see {@link https://base-ui.com/react/components/dialog | Base UI Documentation}
|
|
278
279
|
*/
|
|
279
280
|
function SheetHeader(props: Readonly<SheetHeader.Props>): React.ReactElement {
|
|
281
|
+
// eslint-disable-next-line sonarjs/deprecation -- backward-compatible asChild API
|
|
280
282
|
const {asChild = false, children, className, render, ...otherProps} = props;
|
|
281
283
|
const renderProp = asChild && React.isValidElement(children) ? children : render;
|
|
282
284
|
|
|
@@ -305,6 +307,7 @@ function SheetHeader(props: Readonly<SheetHeader.Props>): React.ReactElement {
|
|
|
305
307
|
* @see {@link https://base-ui.com/react/components/dialog | Base UI Documentation}
|
|
306
308
|
*/
|
|
307
309
|
function SheetFooter(props: Readonly<SheetFooter.Props>): React.ReactElement {
|
|
310
|
+
// eslint-disable-next-line sonarjs/deprecation -- backward-compatible asChild API
|
|
308
311
|
const {asChild = false, children, className, render, ...otherProps} = props;
|
|
309
312
|
const renderProp = asChild && React.isValidElement(children) ? children : render;
|
|
310
313
|
|
|
@@ -384,7 +384,7 @@ function updateToast(toastId: ToastIdentifier, options: ToastUpdateOptions): str
|
|
|
384
384
|
const normalizedToastId = String(toastId);
|
|
385
385
|
const existingRecord = toastRecords.get(normalizedToastId);
|
|
386
386
|
const mergedOptions: ToastOptions = {
|
|
387
|
-
...
|
|
387
|
+
...existingRecord?.options,
|
|
388
388
|
...options,
|
|
389
389
|
};
|
|
390
390
|
const variant = options.variant ?? existingRecord?.snapshot.variant ?? "default";
|
|
@@ -594,16 +594,23 @@ function ToastViewportContent(): React.JSX.Element {
|
|
|
594
594
|
*
|
|
595
595
|
* @see {@link https://base-ui.com/react/components/toast | Base UI Toast Docs}
|
|
596
596
|
*/
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
597
|
+
/**
|
|
598
|
+
* Toaster is the root viewport container for displaying toast notifications.
|
|
599
|
+
* It should be rendered once at the app root level.
|
|
600
|
+
*/
|
|
601
|
+
const Toaster = React.forwardRef<HTMLDivElement, ToasterProps>(function Toaster(
|
|
602
|
+
{
|
|
603
|
+
className,
|
|
604
|
+
closeButton = true,
|
|
605
|
+
containerAriaLabel = DEFAULT_VIEWPORT_ARIA_LABEL,
|
|
606
|
+
duration = DEFAULT_TOAST_DURATION,
|
|
607
|
+
position = "bottom-right",
|
|
608
|
+
style,
|
|
609
|
+
toastOptions,
|
|
610
|
+
visibleToasts = DEFAULT_TOAST_LIMIT,
|
|
611
|
+
},
|
|
612
|
+
forwardedRef,
|
|
613
|
+
) {
|
|
607
614
|
const toasterId = React.useId();
|
|
608
615
|
|
|
609
616
|
React.useEffect(() => {
|
|
@@ -624,6 +631,7 @@ function Toaster({
|
|
|
624
631
|
toastManager={toastManager}>
|
|
625
632
|
<Toast.Portal>
|
|
626
633
|
<Toast.Viewport
|
|
634
|
+
ref={forwardedRef}
|
|
627
635
|
aria-label={containerAriaLabel}
|
|
628
636
|
className={cn(styles.viewport, positionStyles[position], className)}
|
|
629
637
|
style={style}>
|
|
@@ -632,7 +640,7 @@ function Toaster({
|
|
|
632
640
|
</Toast.Portal>
|
|
633
641
|
</Toast.Provider>
|
|
634
642
|
);
|
|
635
|
-
}
|
|
643
|
+
});
|
|
636
644
|
|
|
637
645
|
Toaster.displayName = "Toaster";
|
|
638
646
|
|
|
@@ -63,12 +63,13 @@ export interface ToggleGroupItemProps extends Omit<ToggleProps, "pressed" | "def
|
|
|
63
63
|
*
|
|
64
64
|
* @see {@link https://base-ui.com/react/components/toggle-group | Base UI Toggle Group Docs}
|
|
65
65
|
*/
|
|
66
|
-
|
|
66
|
+
const ToggleGroup = React.forwardRef<HTMLDivElement, ToggleGroup.Props>(function ToggleGroup(props, forwardedRef) {
|
|
67
67
|
const {className, children, render, size, variant, ...otherProps} = props;
|
|
68
68
|
|
|
69
69
|
return (
|
|
70
70
|
<BaseToggleGroup
|
|
71
71
|
{...otherProps}
|
|
72
|
+
ref={forwardedRef}
|
|
72
73
|
render={useRender({
|
|
73
74
|
defaultTagName: "div",
|
|
74
75
|
render: render as never,
|
|
@@ -77,7 +78,7 @@ function ToggleGroup(props: Readonly<ToggleGroup.Props>): React.ReactElement {
|
|
|
77
78
|
<ToggleGroupContext.Provider value={{variant, size}}>{children}</ToggleGroupContext.Provider>
|
|
78
79
|
</BaseToggleGroup>
|
|
79
80
|
);
|
|
80
|
-
}
|
|
81
|
+
});
|
|
81
82
|
|
|
82
83
|
/**
|
|
83
84
|
* Renders an individual toggle item within a toggle group.
|
|
@@ -96,12 +97,13 @@ function ToggleGroup(props: Readonly<ToggleGroup.Props>): React.ReactElement {
|
|
|
96
97
|
*
|
|
97
98
|
* @see {@link https://base-ui.com/react/components/toggle-group | Base UI Toggle Group Docs}
|
|
98
99
|
*/
|
|
99
|
-
|
|
100
|
+
const ToggleGroupItem = React.forwardRef<HTMLButtonElement, ToggleGroupItem.Props>(function ToggleGroupItem(props, forwardedRef) {
|
|
100
101
|
const {className, size, variant, ...otherProps} = props;
|
|
101
102
|
const context = React.useContext(ToggleGroupContext);
|
|
102
103
|
|
|
103
104
|
return (
|
|
104
105
|
<Toggle
|
|
106
|
+
ref={forwardedRef}
|
|
105
107
|
className={cn(
|
|
106
108
|
toggleVariants({
|
|
107
109
|
variant: variant ?? context.variant,
|
|
@@ -114,7 +116,7 @@ function ToggleGroupItem(props: Readonly<ToggleGroupItem.Props>): React.ReactEle
|
|
|
114
116
|
{...otherProps}
|
|
115
117
|
/>
|
|
116
118
|
);
|
|
117
|
-
}
|
|
119
|
+
});
|
|
118
120
|
|
|
119
121
|
// eslint-disable-next-line no-redeclare -- required for the canonical component namespace typing API
|
|
120
122
|
namespace ToggleGroup {
|
|
@@ -8,12 +8,6 @@ import * as React from "react";
|
|
|
8
8
|
import {cn} from "@/lib/utilities";
|
|
9
9
|
import styles from "./toolbar.module.css";
|
|
10
10
|
|
|
11
|
-
type ToolbarProps = React.ComponentPropsWithRef<typeof BaseToolbar.Root>;
|
|
12
|
-
type ToolbarButtonProps = React.ComponentPropsWithRef<typeof BaseToolbar.Button>;
|
|
13
|
-
type ToolbarGroupProps = React.ComponentPropsWithRef<typeof BaseToolbar.Group>;
|
|
14
|
-
type ToolbarSeparatorProps = React.ComponentPropsWithRef<typeof BaseToolbar.Separator>;
|
|
15
|
-
type ToolbarLinkProps = React.ComponentPropsWithRef<typeof BaseToolbar.Link>;
|
|
16
|
-
|
|
17
11
|
/**
|
|
18
12
|
* Arranges related actions into a keyboard-accessible toolbar.
|
|
19
13
|
*
|
|
@@ -32,12 +26,13 @@ type ToolbarLinkProps = React.ComponentPropsWithRef<typeof BaseToolbar.Link>;
|
|
|
32
26
|
*
|
|
33
27
|
* @see {@link https://base-ui.com/react/components/toolbar | Base UI Toolbar Docs}
|
|
34
28
|
*/
|
|
35
|
-
|
|
29
|
+
const Toolbar = React.forwardRef<HTMLDivElement, Toolbar.Props>(function Toolbar(props, forwardedRef) {
|
|
36
30
|
const {className, children, render, ...otherProps} = props;
|
|
37
31
|
|
|
38
32
|
return (
|
|
39
33
|
<BaseToolbar.Root
|
|
40
34
|
{...otherProps}
|
|
35
|
+
ref={forwardedRef}
|
|
41
36
|
render={useRender({
|
|
42
37
|
defaultTagName: "div",
|
|
43
38
|
render: render as never,
|
|
@@ -46,7 +41,7 @@ function Toolbar(props: Readonly<Toolbar.Props>): React.ReactElement {
|
|
|
46
41
|
{children}
|
|
47
42
|
</BaseToolbar.Root>
|
|
48
43
|
);
|
|
49
|
-
}
|
|
44
|
+
});
|
|
50
45
|
|
|
51
46
|
/**
|
|
52
47
|
* Renders an interactive button within a toolbar.
|
|
@@ -63,12 +58,13 @@ function Toolbar(props: Readonly<Toolbar.Props>): React.ReactElement {
|
|
|
63
58
|
*
|
|
64
59
|
* @see {@link https://base-ui.com/react/components/toolbar | Base UI Toolbar Docs}
|
|
65
60
|
*/
|
|
66
|
-
|
|
61
|
+
const ToolbarButton = React.forwardRef<HTMLButtonElement, ToolbarButton.Props>(function ToolbarButton(props, forwardedRef) {
|
|
67
62
|
const {className, children, render, ...otherProps} = props;
|
|
68
63
|
|
|
69
64
|
return (
|
|
70
65
|
<BaseToolbar.Button
|
|
71
66
|
{...otherProps}
|
|
67
|
+
ref={forwardedRef}
|
|
72
68
|
render={useRender({
|
|
73
69
|
defaultTagName: "button",
|
|
74
70
|
render: render as never,
|
|
@@ -77,7 +73,7 @@ function ToolbarButton(props: Readonly<ToolbarButton.Props>): React.ReactElement
|
|
|
77
73
|
{children}
|
|
78
74
|
</BaseToolbar.Button>
|
|
79
75
|
);
|
|
80
|
-
}
|
|
76
|
+
});
|
|
81
77
|
|
|
82
78
|
/**
|
|
83
79
|
* Groups related toolbar controls into a single visual cluster.
|
|
@@ -96,12 +92,13 @@ function ToolbarButton(props: Readonly<ToolbarButton.Props>): React.ReactElement
|
|
|
96
92
|
*
|
|
97
93
|
* @see {@link https://base-ui.com/react/components/toolbar | Base UI Toolbar Docs}
|
|
98
94
|
*/
|
|
99
|
-
|
|
95
|
+
const ToolbarGroup = React.forwardRef<HTMLDivElement, ToolbarGroup.Props>(function ToolbarGroup(props, forwardedRef) {
|
|
100
96
|
const {className, children, render, ...otherProps} = props;
|
|
101
97
|
|
|
102
98
|
return (
|
|
103
99
|
<BaseToolbar.Group
|
|
104
100
|
{...otherProps}
|
|
101
|
+
ref={forwardedRef}
|
|
105
102
|
render={useRender({
|
|
106
103
|
defaultTagName: "div",
|
|
107
104
|
render: render as never,
|
|
@@ -110,7 +107,7 @@ function ToolbarGroup(props: Readonly<ToolbarGroup.Props>): React.ReactElement {
|
|
|
110
107
|
{children}
|
|
111
108
|
</BaseToolbar.Group>
|
|
112
109
|
);
|
|
113
|
-
}
|
|
110
|
+
});
|
|
114
111
|
|
|
115
112
|
/**
|
|
116
113
|
* Renders a visual separator between toolbar items or groups.
|
|
@@ -126,12 +123,13 @@ function ToolbarGroup(props: Readonly<ToolbarGroup.Props>): React.ReactElement {
|
|
|
126
123
|
*
|
|
127
124
|
* @see {@link https://base-ui.com/react/components/toolbar | Base UI Toolbar Docs}
|
|
128
125
|
*/
|
|
129
|
-
|
|
126
|
+
const ToolbarSeparator = React.forwardRef<HTMLDivElement, ToolbarSeparator.Props>(function ToolbarSeparator(props, forwardedRef) {
|
|
130
127
|
const {className, render, ...otherProps} = props;
|
|
131
128
|
|
|
132
129
|
return (
|
|
133
130
|
<BaseToolbar.Separator
|
|
134
131
|
{...otherProps}
|
|
132
|
+
ref={forwardedRef}
|
|
135
133
|
render={useRender({
|
|
136
134
|
defaultTagName: "div",
|
|
137
135
|
render: render as never,
|
|
@@ -139,7 +137,7 @@ function ToolbarSeparator(props: Readonly<ToolbarSeparator.Props>): React.ReactE
|
|
|
139
137
|
})}
|
|
140
138
|
/>
|
|
141
139
|
);
|
|
142
|
-
}
|
|
140
|
+
});
|
|
143
141
|
|
|
144
142
|
/**
|
|
145
143
|
* Renders a link that visually matches toolbar buttons.
|
|
@@ -155,12 +153,13 @@ function ToolbarSeparator(props: Readonly<ToolbarSeparator.Props>): React.ReactE
|
|
|
155
153
|
*
|
|
156
154
|
* @see {@link https://base-ui.com/react/components/toolbar | Base UI Toolbar Docs}
|
|
157
155
|
*/
|
|
158
|
-
|
|
156
|
+
const ToolbarLink = React.forwardRef<HTMLAnchorElement, ToolbarLink.Props>(function ToolbarLink(props, forwardedRef) {
|
|
159
157
|
const {className, children, render, ...otherProps} = props;
|
|
160
158
|
|
|
161
159
|
return (
|
|
162
160
|
<BaseToolbar.Link
|
|
163
161
|
{...otherProps}
|
|
162
|
+
ref={forwardedRef}
|
|
164
163
|
render={useRender({
|
|
165
164
|
defaultTagName: "a",
|
|
166
165
|
render: render as never,
|
|
@@ -169,35 +168,35 @@ function ToolbarLink(props: Readonly<ToolbarLink.Props>): React.ReactElement {
|
|
|
169
168
|
{children}
|
|
170
169
|
</BaseToolbar.Link>
|
|
171
170
|
);
|
|
172
|
-
}
|
|
171
|
+
});
|
|
173
172
|
|
|
174
173
|
// eslint-disable-next-line no-redeclare -- required for the canonical component namespace typing API
|
|
175
174
|
namespace Toolbar {
|
|
176
|
-
export type Props =
|
|
175
|
+
export type Props = React.ComponentPropsWithRef<typeof BaseToolbar.Root>;
|
|
177
176
|
export type State = BaseToolbar.Root.State;
|
|
178
177
|
}
|
|
179
178
|
|
|
180
179
|
// eslint-disable-next-line no-redeclare -- required for the canonical component namespace typing API
|
|
181
180
|
namespace ToolbarButton {
|
|
182
|
-
export type Props =
|
|
181
|
+
export type Props = React.ComponentPropsWithRef<typeof BaseToolbar.Button>;
|
|
183
182
|
export type State = BaseToolbar.Button.State;
|
|
184
183
|
}
|
|
185
184
|
|
|
186
185
|
// eslint-disable-next-line no-redeclare -- required for the canonical component namespace typing API
|
|
187
186
|
namespace ToolbarGroup {
|
|
188
|
-
export type Props =
|
|
187
|
+
export type Props = React.ComponentPropsWithRef<typeof BaseToolbar.Group>;
|
|
189
188
|
export type State = BaseToolbar.Group.State;
|
|
190
189
|
}
|
|
191
190
|
|
|
192
191
|
// eslint-disable-next-line no-redeclare -- required for the canonical component namespace typing API
|
|
193
192
|
namespace ToolbarSeparator {
|
|
194
|
-
export type Props =
|
|
193
|
+
export type Props = React.ComponentPropsWithRef<typeof BaseToolbar.Separator>;
|
|
195
194
|
export type State = BaseToolbar.Separator.State;
|
|
196
195
|
}
|
|
197
196
|
|
|
198
197
|
// eslint-disable-next-line no-redeclare -- required for the canonical component namespace typing API
|
|
199
198
|
namespace ToolbarLink {
|
|
200
|
-
export type Props =
|
|
199
|
+
export type Props = React.ComponentPropsWithRef<typeof BaseToolbar.Link>;
|
|
201
200
|
export type State = BaseToolbar.Link.State;
|
|
202
201
|
}
|
|
203
202
|
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Options for the `useClipboard` hook.
|
|
7
|
+
*/
|
|
8
|
+
export interface UseClipboardOptions {
|
|
9
|
+
/**
|
|
10
|
+
* The duration in milliseconds after which the `copied` state resets to `false`.
|
|
11
|
+
*
|
|
12
|
+
* @defaultValue 2000
|
|
13
|
+
*/
|
|
14
|
+
timeout?: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Return type for the `useClipboard` hook.
|
|
19
|
+
*/
|
|
20
|
+
export interface UseClipboardReturn {
|
|
21
|
+
/**
|
|
22
|
+
* Whether the text was successfully copied to the clipboard.
|
|
23
|
+
*/
|
|
24
|
+
copied: boolean;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Copies the provided text to the clipboard.
|
|
28
|
+
*
|
|
29
|
+
* @param text - The text to copy.
|
|
30
|
+
* @returns A promise that resolves when the copy operation completes.
|
|
31
|
+
*/
|
|
32
|
+
copy: (text: string) => Promise<void>;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The error that occurred during the copy operation, or `null` if no error occurred.
|
|
36
|
+
*/
|
|
37
|
+
error: Error | null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Copies text to the clipboard with success/error state management.
|
|
42
|
+
*
|
|
43
|
+
* @remarks
|
|
44
|
+
* This hook provides a simple interface for copying text to the clipboard using
|
|
45
|
+
* the Clipboard API. It manages the `copied` state that automatically resets
|
|
46
|
+
* after a configurable timeout, and handles errors gracefully when the Clipboard
|
|
47
|
+
* API is unavailable or the operation fails.
|
|
48
|
+
*
|
|
49
|
+
* The hook is SSR-safe and will handle the case where `navigator.clipboard` is
|
|
50
|
+
* not available (e.g., in non-secure contexts or older browsers).
|
|
51
|
+
*
|
|
52
|
+
* @param options - Configuration options for the hook.
|
|
53
|
+
* @returns An object containing the copied state, copy function, and any error.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```tsx
|
|
57
|
+
* function CopyButton({textToCopy}: {textToCopy: string}) {
|
|
58
|
+
* const {copied, copy, error} = useClipboard({timeout: 3000});
|
|
59
|
+
*
|
|
60
|
+
* return (
|
|
61
|
+
* <div>
|
|
62
|
+
* <button onClick={() => copy(textToCopy)}>
|
|
63
|
+
* {copied ? "Copied!" : "Copy to clipboard"}
|
|
64
|
+
* </button>
|
|
65
|
+
* {error && <span>Failed to copy: {error.message}</span>}
|
|
66
|
+
* </div>
|
|
67
|
+
* );
|
|
68
|
+
* }
|
|
69
|
+
* ```
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```tsx
|
|
73
|
+
* function ShareLink({url}: {url: string}) {
|
|
74
|
+
* const {copied, copy} = useClipboard();
|
|
75
|
+
*
|
|
76
|
+
* return (
|
|
77
|
+
* <button onClick={() => copy(url)} disabled={copied}>
|
|
78
|
+
* {copied ? "✓ Copied" : "Share link"}
|
|
79
|
+
* </button>
|
|
80
|
+
* );
|
|
81
|
+
* }
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export function useClipboard(options: UseClipboardOptions = {}): UseClipboardReturn {
|
|
85
|
+
const {timeout = 2000} = options;
|
|
86
|
+
|
|
87
|
+
const [copied, setCopied] = React.useState(false);
|
|
88
|
+
const [error, setError] = React.useState<Error | null>(null);
|
|
89
|
+
const timeoutRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
90
|
+
|
|
91
|
+
const copy = React.useCallback(
|
|
92
|
+
async (text: string): Promise<void> => {
|
|
93
|
+
// Clear any existing timeout
|
|
94
|
+
if (timeoutRef.current !== null) {
|
|
95
|
+
clearTimeout(timeoutRef.current);
|
|
96
|
+
timeoutRef.current = null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Reset error state
|
|
100
|
+
setError(null);
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
// Check if Clipboard API is available
|
|
104
|
+
if (typeof globalThis.navigator === "undefined" || !globalThis.navigator.clipboard) {
|
|
105
|
+
throw new Error("Clipboard API is not available");
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
await globalThis.navigator.clipboard.writeText(text);
|
|
109
|
+
setCopied(true);
|
|
110
|
+
|
|
111
|
+
// Reset copied state after timeout
|
|
112
|
+
timeoutRef.current = setTimeout(() => {
|
|
113
|
+
setCopied(false);
|
|
114
|
+
timeoutRef.current = null;
|
|
115
|
+
}, timeout);
|
|
116
|
+
} catch (err) {
|
|
117
|
+
const errorMessage = err instanceof Error ? err : new Error("Failed to copy to clipboard");
|
|
118
|
+
|
|
119
|
+
setError(errorMessage);
|
|
120
|
+
setCopied(false);
|
|
121
|
+
console.error("Copy to clipboard failed:", errorMessage);
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
[timeout],
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
// Cleanup timeout on unmount
|
|
128
|
+
React.useEffect(() => {
|
|
129
|
+
return () => {
|
|
130
|
+
if (timeoutRef.current !== null) {
|
|
131
|
+
clearTimeout(timeoutRef.current);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
}, []);
|
|
135
|
+
|
|
136
|
+
return {copied, copy, error};
|
|
137
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Options for configuring controllable state behavior.
|
|
7
|
+
*
|
|
8
|
+
* @typeParam T - The type of the state value.
|
|
9
|
+
*/
|
|
10
|
+
export interface UseControllableStateOptions<T> {
|
|
11
|
+
/**
|
|
12
|
+
* The controlled value from props. When provided, the component operates in controlled mode.
|
|
13
|
+
*/
|
|
14
|
+
controlled?: T;
|
|
15
|
+
/**
|
|
16
|
+
* The default value used when the component is uncontrolled.
|
|
17
|
+
*/
|
|
18
|
+
defaultValue: T;
|
|
19
|
+
/**
|
|
20
|
+
* Callback fired when the internal state changes in controlled mode.
|
|
21
|
+
*
|
|
22
|
+
* @param value - The new state value.
|
|
23
|
+
*/
|
|
24
|
+
onChange?: (value: T) => void;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Manages state that can be either controlled or uncontrolled.
|
|
29
|
+
*
|
|
30
|
+
* @remarks
|
|
31
|
+
* This hook enables components to support both controlled and uncontrolled patterns
|
|
32
|
+
* seamlessly. When a `controlled` value is provided, it takes precedence; otherwise,
|
|
33
|
+
* the hook manages internal state initialized with `defaultValue`.
|
|
34
|
+
*
|
|
35
|
+
* @typeParam T - The type of the state value.
|
|
36
|
+
* @param options - Configuration object for controllable state.
|
|
37
|
+
* @returns A tuple containing the current state value and a setter function.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```tsx
|
|
41
|
+
* function CustomInput({value, defaultValue = "", onChange}) {
|
|
42
|
+
* const [internalValue, setValue] = useControllableState({
|
|
43
|
+
* controlled: value,
|
|
44
|
+
* defaultValue,
|
|
45
|
+
* onChange,
|
|
46
|
+
* });
|
|
47
|
+
*
|
|
48
|
+
* return (
|
|
49
|
+
* <input
|
|
50
|
+
* type="text"
|
|
51
|
+
* value={internalValue}
|
|
52
|
+
* onChange={(e) => setValue(e.target.value)}
|
|
53
|
+
* />
|
|
54
|
+
* );
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export function useControllableState<T>(options: UseControllableStateOptions<T>): [T, (value: T | ((prev: T) => T)) => void] {
|
|
59
|
+
const {controlled, defaultValue, onChange} = options;
|
|
60
|
+
const [uncontrolledState, setUncontrolledState] = React.useState<T>(defaultValue);
|
|
61
|
+
const isControlled = controlled !== undefined;
|
|
62
|
+
const value = isControlled ? controlled : uncontrolledState;
|
|
63
|
+
|
|
64
|
+
const setValue = React.useCallback(
|
|
65
|
+
(nextValue: T | ((prev: T) => T)) => {
|
|
66
|
+
if (!isControlled) {
|
|
67
|
+
setUncontrolledState((currentValue) => {
|
|
68
|
+
const resolvedValue = typeof nextValue === "function" ? (nextValue as (prev: T) => T)(currentValue) : nextValue;
|
|
69
|
+
onChange?.(resolvedValue);
|
|
70
|
+
return resolvedValue;
|
|
71
|
+
});
|
|
72
|
+
} else {
|
|
73
|
+
const resolvedValue = typeof nextValue === "function" ? (nextValue as (prev: T) => T)(controlled as T) : nextValue;
|
|
74
|
+
onChange?.(resolvedValue);
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
[isControlled, onChange, controlled],
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
return [value, setValue];
|
|
81
|
+
}
|