@inui/design-system 0.0.0-stage → 1.22.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/README.md +360 -2
- package/dist/components/ui/accordion.d.ts +44 -0
- package/dist/components/ui/accordion.d.ts.map +1 -0
- package/dist/components/ui/accordion.js +86 -0
- package/dist/components/ui/accordion.js.map +1 -0
- package/dist/components/ui/ai-avatar.d.ts +46 -0
- package/dist/components/ui/ai-avatar.d.ts.map +1 -0
- package/dist/components/ui/ai-avatar.js +43 -0
- package/dist/components/ui/ai-avatar.js.map +1 -0
- package/dist/components/ui/ai-label.d.ts +118 -0
- package/dist/components/ui/ai-label.d.ts.map +1 -0
- package/dist/components/ui/ai-label.js +184 -0
- package/dist/components/ui/ai-label.js.map +1 -0
- package/dist/components/ui/ai-sparkle.d.ts +91 -0
- package/dist/components/ui/ai-sparkle.d.ts.map +1 -0
- package/dist/components/ui/ai-sparkle.js +200 -0
- package/dist/components/ui/ai-sparkle.js.map +1 -0
- package/dist/components/ui/alert-dialog.d.ts +79 -0
- package/dist/components/ui/alert-dialog.d.ts.map +1 -0
- package/dist/components/ui/alert-dialog.js +147 -0
- package/dist/components/ui/alert-dialog.js.map +1 -0
- package/dist/components/ui/alert.d.ts +71 -0
- package/dist/components/ui/alert.d.ts.map +1 -0
- package/dist/components/ui/alert.js +108 -0
- package/dist/components/ui/alert.js.map +1 -0
- package/dist/components/ui/aspect-ratio.d.ts +30 -0
- package/dist/components/ui/aspect-ratio.d.ts.map +1 -0
- package/dist/components/ui/aspect-ratio.js +40 -0
- package/dist/components/ui/aspect-ratio.js.map +1 -0
- package/dist/components/ui/attachment.d.ts +74 -0
- package/dist/components/ui/attachment.d.ts.map +1 -0
- package/dist/components/ui/attachment.js +142 -0
- package/dist/components/ui/attachment.js.map +1 -0
- package/dist/components/ui/avatar.d.ts +82 -0
- package/dist/components/ui/avatar.d.ts.map +1 -0
- package/dist/components/ui/avatar.js +123 -0
- package/dist/components/ui/avatar.js.map +1 -0
- package/dist/components/ui/badge.d.ts +130 -0
- package/dist/components/ui/badge.d.ts.map +1 -0
- package/dist/components/ui/badge.js +226 -0
- package/dist/components/ui/badge.js.map +1 -0
- package/dist/components/ui/breadcrumb.d.ts +43 -0
- package/dist/components/ui/breadcrumb.d.ts.map +1 -0
- package/dist/components/ui/breadcrumb.js +98 -0
- package/dist/components/ui/breadcrumb.js.map +1 -0
- package/dist/components/ui/browser-tabs.d.ts +218 -0
- package/dist/components/ui/browser-tabs.d.ts.map +1 -0
- package/dist/components/ui/browser-tabs.js +528 -0
- package/dist/components/ui/browser-tabs.js.map +1 -0
- package/dist/components/ui/bubble.d.ts +80 -0
- package/dist/components/ui/bubble.d.ts.map +1 -0
- package/dist/components/ui/bubble.js +235 -0
- package/dist/components/ui/bubble.js.map +1 -0
- package/dist/components/ui/button-group.d.ts +60 -0
- package/dist/components/ui/button-group.d.ts.map +1 -0
- package/dist/components/ui/button-group.js +89 -0
- package/dist/components/ui/button-group.js.map +1 -0
- package/dist/components/ui/button.d.ts +157 -0
- package/dist/components/ui/button.d.ts.map +1 -0
- package/dist/components/ui/button.js +144 -0
- package/dist/components/ui/button.js.map +1 -0
- package/dist/components/ui/calendar.d.ts +57 -0
- package/dist/components/ui/calendar.d.ts.map +1 -0
- package/dist/components/ui/calendar.js +163 -0
- package/dist/components/ui/calendar.js.map +1 -0
- package/dist/components/ui/card.d.ts +96 -0
- package/dist/components/ui/card.d.ts.map +1 -0
- package/dist/components/ui/card.js +131 -0
- package/dist/components/ui/card.js.map +1 -0
- package/dist/components/ui/carousel.d.ts +86 -0
- package/dist/components/ui/carousel.d.ts.map +1 -0
- package/dist/components/ui/carousel.js +207 -0
- package/dist/components/ui/carousel.js.map +1 -0
- package/dist/components/ui/checkbox.d.ts +54 -0
- package/dist/components/ui/checkbox.d.ts.map +1 -0
- package/dist/components/ui/checkbox.js +78 -0
- package/dist/components/ui/checkbox.js.map +1 -0
- package/dist/components/ui/collapsible.d.ts +45 -0
- package/dist/components/ui/collapsible.d.ts.map +1 -0
- package/dist/components/ui/collapsible.js +65 -0
- package/dist/components/ui/collapsible.js.map +1 -0
- package/dist/components/ui/combobox.d.ts +111 -0
- package/dist/components/ui/combobox.d.ts.map +1 -0
- package/dist/components/ui/combobox.js +264 -0
- package/dist/components/ui/combobox.js.map +1 -0
- package/dist/components/ui/command.d.ts +71 -0
- package/dist/components/ui/command.d.ts.map +1 -0
- package/dist/components/ui/command.js +133 -0
- package/dist/components/ui/command.js.map +1 -0
- package/dist/components/ui/context-menu.d.ts +70 -0
- package/dist/components/ui/context-menu.d.ts.map +1 -0
- package/dist/components/ui/context-menu.js +163 -0
- package/dist/components/ui/context-menu.js.map +1 -0
- package/dist/components/ui/count-badge.d.ts +40 -0
- package/dist/components/ui/count-badge.d.ts.map +1 -0
- package/dist/components/ui/count-badge.js +43 -0
- package/dist/components/ui/count-badge.js.map +1 -0
- package/dist/components/ui/data-table.d.ts +1026 -0
- package/dist/components/ui/data-table.d.ts.map +1 -0
- package/dist/components/ui/data-table.js +3423 -0
- package/dist/components/ui/data-table.js.map +1 -0
- package/dist/components/ui/dialog.d.ts +73 -0
- package/dist/components/ui/dialog.d.ts.map +1 -0
- package/dist/components/ui/dialog.js +145 -0
- package/dist/components/ui/dialog.js.map +1 -0
- package/dist/components/ui/direction.d.ts +35 -0
- package/dist/components/ui/direction.d.ts.map +1 -0
- package/dist/components/ui/direction.js +35 -0
- package/dist/components/ui/direction.js.map +1 -0
- package/dist/components/ui/drawer.d.ts +53 -0
- package/dist/components/ui/drawer.d.ts.map +1 -0
- package/dist/components/ui/drawer.js +115 -0
- package/dist/components/ui/drawer.js.map +1 -0
- package/dist/components/ui/dropdown-menu.d.ts +75 -0
- package/dist/components/ui/dropdown-menu.d.ts.map +1 -0
- package/dist/components/ui/dropdown-menu.js +173 -0
- package/dist/components/ui/dropdown-menu.js.map +1 -0
- package/dist/components/ui/dropzone.d.ts +141 -0
- package/dist/components/ui/dropzone.d.ts.map +1 -0
- package/dist/components/ui/dropzone.js +257 -0
- package/dist/components/ui/dropzone.js.map +1 -0
- package/dist/components/ui/empty.d.ts +94 -0
- package/dist/components/ui/empty.d.ts.map +1 -0
- package/dist/components/ui/empty.js +149 -0
- package/dist/components/ui/empty.js.map +1 -0
- package/dist/components/ui/field.d.ts +122 -0
- package/dist/components/ui/field.d.ts.map +1 -0
- package/dist/components/ui/field.js +254 -0
- package/dist/components/ui/field.js.map +1 -0
- package/dist/components/ui/hover-card.d.ts +51 -0
- package/dist/components/ui/hover-card.d.ts.map +1 -0
- package/dist/components/ui/hover-card.js +101 -0
- package/dist/components/ui/hover-card.js.map +1 -0
- package/dist/components/ui/input-group.d.ts +62 -0
- package/dist/components/ui/input-group.d.ts.map +1 -0
- package/dist/components/ui/input-group.js +115 -0
- package/dist/components/ui/input-group.js.map +1 -0
- package/dist/components/ui/input-otp.d.ts +52 -0
- package/dist/components/ui/input-otp.d.ts.map +1 -0
- package/dist/components/ui/input-otp.js +89 -0
- package/dist/components/ui/input-otp.js.map +1 -0
- package/dist/components/ui/input.d.ts +47 -0
- package/dist/components/ui/input.d.ts.map +1 -0
- package/dist/components/ui/input.js +47 -0
- package/dist/components/ui/input.js.map +1 -0
- package/dist/components/ui/item.d.ts +74 -0
- package/dist/components/ui/item.d.ts.map +1 -0
- package/dist/components/ui/item.js +176 -0
- package/dist/components/ui/item.js.map +1 -0
- package/dist/components/ui/kbd.d.ts +41 -0
- package/dist/components/ui/kbd.d.ts.map +1 -0
- package/dist/components/ui/kbd.js +58 -0
- package/dist/components/ui/kbd.js.map +1 -0
- package/dist/components/ui/key-value-pair.d.ts +56 -0
- package/dist/components/ui/key-value-pair.d.ts.map +1 -0
- package/dist/components/ui/key-value-pair.js +110 -0
- package/dist/components/ui/key-value-pair.js.map +1 -0
- package/dist/components/ui/label.d.ts +83 -0
- package/dist/components/ui/label.d.ts.map +1 -0
- package/dist/components/ui/label.js +85 -0
- package/dist/components/ui/label.js.map +1 -0
- package/dist/components/ui/marker.d.ts +54 -0
- package/dist/components/ui/marker.d.ts.map +1 -0
- package/dist/components/ui/marker.js +79 -0
- package/dist/components/ui/marker.js.map +1 -0
- package/dist/components/ui/menubar.d.ts +72 -0
- package/dist/components/ui/menubar.d.ts.map +1 -0
- package/dist/components/ui/menubar.js +174 -0
- package/dist/components/ui/menubar.js.map +1 -0
- package/dist/components/ui/message-scroller.d.ts +53 -0
- package/dist/components/ui/message-scroller.d.ts.map +1 -0
- package/dist/components/ui/message-scroller.js +95 -0
- package/dist/components/ui/message-scroller.js.map +1 -0
- package/dist/components/ui/message.d.ts +63 -0
- package/dist/components/ui/message.d.ts.map +1 -0
- package/dist/components/ui/message.js +92 -0
- package/dist/components/ui/message.js.map +1 -0
- package/dist/components/ui/meta-list.d.ts +55 -0
- package/dist/components/ui/meta-list.d.ts.map +1 -0
- package/dist/components/ui/meta-list.js +75 -0
- package/dist/components/ui/meta-list.js.map +1 -0
- package/dist/components/ui/native-select.d.ts +51 -0
- package/dist/components/ui/native-select.d.ts.map +1 -0
- package/dist/components/ui/native-select.js +75 -0
- package/dist/components/ui/native-select.js.map +1 -0
- package/dist/components/ui/navigation-menu.d.ts +56 -0
- package/dist/components/ui/navigation-menu.d.ts.map +1 -0
- package/dist/components/ui/navigation-menu.js +116 -0
- package/dist/components/ui/navigation-menu.js.map +1 -0
- package/dist/components/ui/page-header.d.ts +223 -0
- package/dist/components/ui/page-header.d.ts.map +1 -0
- package/dist/components/ui/page-header.js +233 -0
- package/dist/components/ui/page-header.js.map +1 -0
- package/dist/components/ui/pagination.d.ts +68 -0
- package/dist/components/ui/pagination.d.ts.map +1 -0
- package/dist/components/ui/pagination.js +145 -0
- package/dist/components/ui/pagination.js.map +1 -0
- package/dist/components/ui/popover.d.ts +61 -0
- package/dist/components/ui/popover.d.ts.map +1 -0
- package/dist/components/ui/popover.js +144 -0
- package/dist/components/ui/popover.js.map +1 -0
- package/dist/components/ui/progress.d.ts +35 -0
- package/dist/components/ui/progress.d.ts.map +1 -0
- package/dist/components/ui/progress.js +46 -0
- package/dist/components/ui/progress.js.map +1 -0
- package/dist/components/ui/questionnaire.d.ts +92 -0
- package/dist/components/ui/questionnaire.d.ts.map +1 -0
- package/dist/components/ui/questionnaire.js +277 -0
- package/dist/components/ui/questionnaire.js.map +1 -0
- package/dist/components/ui/radio-group.d.ts +50 -0
- package/dist/components/ui/radio-group.d.ts.map +1 -0
- package/dist/components/ui/radio-group.js +65 -0
- package/dist/components/ui/radio-group.js.map +1 -0
- package/dist/components/ui/resizable.d.ts +44 -0
- package/dist/components/ui/resizable.d.ts.map +1 -0
- package/dist/components/ui/resizable.js +73 -0
- package/dist/components/ui/resizable.js.map +1 -0
- package/dist/components/ui/scroll-area.d.ts +55 -0
- package/dist/components/ui/scroll-area.d.ts.map +1 -0
- package/dist/components/ui/scroll-area.js +72 -0
- package/dist/components/ui/scroll-area.js.map +1 -0
- package/dist/components/ui/select.d.ts +274 -0
- package/dist/components/ui/select.d.ts.map +1 -0
- package/dist/components/ui/select.js +609 -0
- package/dist/components/ui/select.js.map +1 -0
- package/dist/components/ui/separator.d.ts +31 -0
- package/dist/components/ui/separator.d.ts.map +1 -0
- package/dist/components/ui/separator.js +38 -0
- package/dist/components/ui/separator.js.map +1 -0
- package/dist/components/ui/sheet.d.ts +59 -0
- package/dist/components/ui/sheet.d.ts.map +1 -0
- package/dist/components/ui/sheet.js +128 -0
- package/dist/components/ui/sheet.js.map +1 -0
- package/dist/components/ui/sidebar.d.ts +166 -0
- package/dist/components/ui/sidebar.d.ts.map +1 -0
- package/dist/components/ui/sidebar.js +512 -0
- package/dist/components/ui/sidebar.js.map +1 -0
- package/dist/components/ui/skeleton.d.ts +35 -0
- package/dist/components/ui/skeleton.d.ts.map +1 -0
- package/dist/components/ui/skeleton.js +46 -0
- package/dist/components/ui/skeleton.js.map +1 -0
- package/dist/components/ui/slider.d.ts +39 -0
- package/dist/components/ui/slider.d.ts.map +1 -0
- package/dist/components/ui/slider.js +75 -0
- package/dist/components/ui/slider.js.map +1 -0
- package/dist/components/ui/sonner.d.ts +56 -0
- package/dist/components/ui/sonner.d.ts.map +1 -0
- package/dist/components/ui/sonner.js +96 -0
- package/dist/components/ui/sonner.js.map +1 -0
- package/dist/components/ui/spinner.d.ts +37 -0
- package/dist/components/ui/spinner.d.ts.map +1 -0
- package/dist/components/ui/spinner.js +51 -0
- package/dist/components/ui/spinner.js.map +1 -0
- package/dist/components/ui/stepper.d.ts +84 -0
- package/dist/components/ui/stepper.d.ts.map +1 -0
- package/dist/components/ui/stepper.js +145 -0
- package/dist/components/ui/stepper.js.map +1 -0
- package/dist/components/ui/switch.d.ts +36 -0
- package/dist/components/ui/switch.d.ts.map +1 -0
- package/dist/components/ui/switch.js +44 -0
- package/dist/components/ui/switch.js.map +1 -0
- package/dist/components/ui/table.d.ts +80 -0
- package/dist/components/ui/table.d.ts.map +1 -0
- package/dist/components/ui/table.js +130 -0
- package/dist/components/ui/table.js.map +1 -0
- package/dist/components/ui/tabs.d.ts +87 -0
- package/dist/components/ui/tabs.d.ts.map +1 -0
- package/dist/components/ui/tabs.js +128 -0
- package/dist/components/ui/tabs.js.map +1 -0
- package/dist/components/ui/textarea.d.ts +35 -0
- package/dist/components/ui/textarea.d.ts.map +1 -0
- package/dist/components/ui/textarea.js +38 -0
- package/dist/components/ui/textarea.js.map +1 -0
- package/dist/components/ui/time-picker.d.ts +58 -0
- package/dist/components/ui/time-picker.d.ts.map +1 -0
- package/dist/components/ui/time-picker.js +188 -0
- package/dist/components/ui/time-picker.js.map +1 -0
- package/dist/components/ui/toggle-group.d.ts +52 -0
- package/dist/components/ui/toggle-group.d.ts.map +1 -0
- package/dist/components/ui/toggle-group.js +94 -0
- package/dist/components/ui/toggle-group.js.map +1 -0
- package/dist/components/ui/toggle.d.ts +50 -0
- package/dist/components/ui/toggle.d.ts.map +1 -0
- package/dist/components/ui/toggle.js +70 -0
- package/dist/components/ui/toggle.js.map +1 -0
- package/dist/components/ui/tooltip.d.ts +51 -0
- package/dist/components/ui/tooltip.d.ts.map +1 -0
- package/dist/components/ui/tooltip.js +78 -0
- package/dist/components/ui/tooltip.js.map +1 -0
- package/dist/hooks/use-dark-mode.d.ts +2 -0
- package/dist/hooks/use-dark-mode.d.ts.map +1 -0
- package/dist/hooks/use-dark-mode.js +36 -0
- package/dist/hooks/use-dark-mode.js.map +1 -0
- package/dist/hooks/use-mobile.d.ts +2 -0
- package/dist/hooks/use-mobile.d.ts.map +1 -0
- package/dist/hooks/use-mobile.js +20 -0
- package/dist/hooks/use-mobile.js.map +1 -0
- package/dist/index.d.ts +88 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +77 -0
- package/dist/lib/scope.d.ts +64 -0
- package/dist/lib/scope.d.ts.map +1 -0
- package/dist/lib/scope.js +74 -0
- package/dist/lib/scope.js.map +1 -0
- package/dist/lib/utils.d.ts +3 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +11 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/styles.css +2 -0
- package/dist/theme-tokens.css +537 -0
- package/dist/theme.css +417 -0
- package/package.json +103 -3
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { cn } from "../../lib/utils.js";
|
|
2
|
+
import { Button } from "./button.js";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import useEmblaCarousel from "embla-carousel-react";
|
|
7
|
+
//#region src/components/ui/carousel.tsx
|
|
8
|
+
/**
|
|
9
|
+
* Carousel — slide container with prev/next controls.
|
|
10
|
+
*
|
|
11
|
+
* Built on embla-carousel; opts/plugins/setApi pass straight through to the
|
|
12
|
+
* underlying embla instance.
|
|
13
|
+
* Arrow-key navigation is wired in via `onKeyDownCapture` on the root, on
|
|
14
|
+
* top of embla's own drag/swipe handling.
|
|
15
|
+
*/
|
|
16
|
+
var CarouselContext = React.createContext(null);
|
|
17
|
+
function useCarousel() {
|
|
18
|
+
const context = React.useContext(CarouselContext);
|
|
19
|
+
if (!context) throw new Error("useCarousel must be used within a <Carousel />");
|
|
20
|
+
return context;
|
|
21
|
+
}
|
|
22
|
+
function Carousel({ orientation = "horizontal", opts, setApi, plugins, className, children, ...props }) {
|
|
23
|
+
const [carouselRef, api] = useEmblaCarousel({
|
|
24
|
+
...opts,
|
|
25
|
+
axis: orientation === "horizontal" ? "x" : "y"
|
|
26
|
+
}, plugins);
|
|
27
|
+
const [canScrollPrev, setCanScrollPrev] = React.useState(false);
|
|
28
|
+
const [canScrollNext, setCanScrollNext] = React.useState(false);
|
|
29
|
+
const [slidesInView, setSlidesInView] = React.useState([]);
|
|
30
|
+
const [announcement, setAnnouncement] = React.useState("");
|
|
31
|
+
const onSelect = React.useCallback((api) => {
|
|
32
|
+
if (!api) return;
|
|
33
|
+
setCanScrollPrev(api.canScrollPrev());
|
|
34
|
+
setCanScrollNext(api.canScrollNext());
|
|
35
|
+
setAnnouncement(`Slide ${api.selectedScrollSnap() + 1} of ${api.scrollSnapList().length}`);
|
|
36
|
+
}, []);
|
|
37
|
+
const onSlidesInView = React.useCallback((api) => {
|
|
38
|
+
if (!api) return;
|
|
39
|
+
setSlidesInView(api.slidesInView());
|
|
40
|
+
}, []);
|
|
41
|
+
const scrollPrev = React.useCallback(() => {
|
|
42
|
+
api?.scrollPrev();
|
|
43
|
+
}, [api]);
|
|
44
|
+
const scrollNext = React.useCallback(() => {
|
|
45
|
+
api?.scrollNext();
|
|
46
|
+
}, [api]);
|
|
47
|
+
const handleKeyDown = React.useCallback((event) => {
|
|
48
|
+
if (event.key === "ArrowLeft") {
|
|
49
|
+
event.preventDefault();
|
|
50
|
+
scrollPrev();
|
|
51
|
+
} else if (event.key === "ArrowRight") {
|
|
52
|
+
event.preventDefault();
|
|
53
|
+
scrollNext();
|
|
54
|
+
}
|
|
55
|
+
}, [scrollPrev, scrollNext]);
|
|
56
|
+
React.useEffect(() => {
|
|
57
|
+
if (!api || !setApi) return;
|
|
58
|
+
setApi(api);
|
|
59
|
+
}, [api, setApi]);
|
|
60
|
+
React.useEffect(() => {
|
|
61
|
+
if (!api) return;
|
|
62
|
+
onSelect(api);
|
|
63
|
+
onSlidesInView(api);
|
|
64
|
+
api.on("reInit", onSelect);
|
|
65
|
+
api.on("select", onSelect);
|
|
66
|
+
api.on("reInit", onSlidesInView);
|
|
67
|
+
api.on("slidesInView", onSlidesInView);
|
|
68
|
+
return () => {
|
|
69
|
+
api?.off("select", onSelect);
|
|
70
|
+
api?.off("reInit", onSelect);
|
|
71
|
+
api?.off("reInit", onSlidesInView);
|
|
72
|
+
api?.off("slidesInView", onSlidesInView);
|
|
73
|
+
};
|
|
74
|
+
}, [
|
|
75
|
+
api,
|
|
76
|
+
onSelect,
|
|
77
|
+
onSlidesInView
|
|
78
|
+
]);
|
|
79
|
+
return /* @__PURE__ */ jsx(CarouselContext.Provider, {
|
|
80
|
+
value: {
|
|
81
|
+
carouselRef,
|
|
82
|
+
api,
|
|
83
|
+
opts,
|
|
84
|
+
orientation: orientation || (opts?.axis === "y" ? "vertical" : "horizontal"),
|
|
85
|
+
scrollPrev,
|
|
86
|
+
scrollNext,
|
|
87
|
+
canScrollPrev,
|
|
88
|
+
canScrollNext,
|
|
89
|
+
slidesInView
|
|
90
|
+
},
|
|
91
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
92
|
+
onKeyDownCapture: handleKeyDown,
|
|
93
|
+
className: cn("relative", className),
|
|
94
|
+
role: "region",
|
|
95
|
+
"aria-roledescription": "carousel",
|
|
96
|
+
"data-slot": "carousel",
|
|
97
|
+
...props,
|
|
98
|
+
children: [children, /* @__PURE__ */ jsx("span", {
|
|
99
|
+
role: "status",
|
|
100
|
+
className: "sr-only",
|
|
101
|
+
children: announcement
|
|
102
|
+
})]
|
|
103
|
+
})
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
function CarouselContent({ className, children, ...props }) {
|
|
107
|
+
const { carouselRef, orientation } = useCarousel();
|
|
108
|
+
return /* @__PURE__ */ jsx("div", {
|
|
109
|
+
ref: carouselRef,
|
|
110
|
+
className: "overflow-hidden",
|
|
111
|
+
"data-slot": "carousel-content",
|
|
112
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
113
|
+
className: cn("flex", orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col", className),
|
|
114
|
+
...props,
|
|
115
|
+
children: React.Children.map(children, (child, index) => React.isValidElement(child) ? React.cloneElement(child, { "data-slide-index": index }) : child)
|
|
116
|
+
})
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
function CarouselItem({ className, "data-slide-index": slideIndex, ...props }) {
|
|
120
|
+
const { orientation, slidesInView } = useCarousel();
|
|
121
|
+
const isOffscreen = slidesInView.length > 0 && slideIndex !== void 0 && !slidesInView.includes(slideIndex);
|
|
122
|
+
return /* @__PURE__ */ jsx("div", {
|
|
123
|
+
role: "group",
|
|
124
|
+
"aria-roledescription": "slide",
|
|
125
|
+
"data-slot": "carousel-item",
|
|
126
|
+
inert: isOffscreen,
|
|
127
|
+
className: cn("min-w-0 shrink-0 grow-0 basis-full", orientation === "horizontal" ? "pl-4" : "pt-4", className),
|
|
128
|
+
...props
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
function CarouselPrevious({ className, variant = "outline", size = "sm", ...props }) {
|
|
132
|
+
const { orientation, scrollPrev, canScrollPrev } = useCarousel();
|
|
133
|
+
return /* @__PURE__ */ jsxs(Button, {
|
|
134
|
+
"data-slot": "carousel-previous",
|
|
135
|
+
variant,
|
|
136
|
+
size,
|
|
137
|
+
iconOnly: true,
|
|
138
|
+
className: cn("absolute touch-manipulation rounded-full", orientation === "horizontal" ? "inset-y-0 -left-12 my-auto" : "-top-12 left-1/2 -translate-x-1/2 rotate-90", className),
|
|
139
|
+
disabled: !canScrollPrev,
|
|
140
|
+
onClick: scrollPrev,
|
|
141
|
+
"aria-label": "Previous slide",
|
|
142
|
+
...props,
|
|
143
|
+
children: [/* @__PURE__ */ jsx(ChevronLeftIcon, {}), /* @__PURE__ */ jsx("span", {
|
|
144
|
+
className: "sr-only",
|
|
145
|
+
children: "Previous slide"
|
|
146
|
+
})]
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
function CarouselNext({ className, variant = "outline", size = "sm", ...props }) {
|
|
150
|
+
const { orientation, scrollNext, canScrollNext } = useCarousel();
|
|
151
|
+
return /* @__PURE__ */ jsxs(Button, {
|
|
152
|
+
"data-slot": "carousel-next",
|
|
153
|
+
variant,
|
|
154
|
+
size,
|
|
155
|
+
iconOnly: true,
|
|
156
|
+
className: cn("absolute touch-manipulation rounded-full", orientation === "horizontal" ? "inset-y-0 -right-12 my-auto" : "-bottom-12 left-1/2 -translate-x-1/2 rotate-90", className),
|
|
157
|
+
disabled: !canScrollNext,
|
|
158
|
+
onClick: scrollNext,
|
|
159
|
+
"aria-label": "Next slide",
|
|
160
|
+
...props,
|
|
161
|
+
children: [/* @__PURE__ */ jsx(ChevronRightIcon, {}), /* @__PURE__ */ jsx("span", {
|
|
162
|
+
className: "sr-only",
|
|
163
|
+
children: "Next slide"
|
|
164
|
+
})]
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* WHEN TO USE
|
|
169
|
+
* - Paging horizontally (or vertically) through cards or media where
|
|
170
|
+
* showing everything at once is impractical — featured items, testimonials,
|
|
171
|
+
* image galleries.
|
|
172
|
+
*
|
|
173
|
+
* NOT WHEN
|
|
174
|
+
* - Content users need to scan or compare side by side — use a grid; a
|
|
175
|
+
* carousel hides everything but the current slide(s).
|
|
176
|
+
* - Essential content that must never be missed — carousels are easy to
|
|
177
|
+
* stop paging through.
|
|
178
|
+
* - Paged tabular/list data — use Pagination, not slide-based paging.
|
|
179
|
+
*
|
|
180
|
+
* KEY RULES
|
|
181
|
+
* - `orientation="vertical"` flips both the embla axis and the previous/next
|
|
182
|
+
* button placement (rotated 90°, stacked above/below instead of beside).
|
|
183
|
+
* - Number of visible slides is controlled entirely by `basis-*` on
|
|
184
|
+
* CarouselItem (e.g. `basis-1/3` shows three) — there's no `slidesToShow` prop.
|
|
185
|
+
* - `useCarousel()` throws outside a `<Carousel>` — only call it from a
|
|
186
|
+
* descendant of Carousel (as CarouselContent/Item/Previous/Next do).
|
|
187
|
+
* - `setApi` is how you get imperative control (`api.scrollTo(i)`, listen to
|
|
188
|
+
* `"select"`) — pass a state setter and read the api once it's set.
|
|
189
|
+
*
|
|
190
|
+
* USAGE
|
|
191
|
+
* <Carousel className="w-64">
|
|
192
|
+
* <CarouselContent>
|
|
193
|
+
* {slides.map((s) => (
|
|
194
|
+
* <CarouselItem key={s}><Card>...</Card></CarouselItem>
|
|
195
|
+
* ))}
|
|
196
|
+
* </CarouselContent>
|
|
197
|
+
* <CarouselPrevious />
|
|
198
|
+
* <CarouselNext />
|
|
199
|
+
* </Carousel>
|
|
200
|
+
*
|
|
201
|
+
* // Three slides visible at once
|
|
202
|
+
* <CarouselItem className="basis-1/3">...</CarouselItem>
|
|
203
|
+
*/
|
|
204
|
+
//#endregion
|
|
205
|
+
export { Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, useCarousel };
|
|
206
|
+
|
|
207
|
+
//# sourceMappingURL=carousel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"carousel.js","names":[],"sources":["../../../src/components/ui/carousel.tsx"],"sourcesContent":["/**\n * Carousel — slide container with prev/next controls.\n *\n * Built on embla-carousel; opts/plugins/setApi pass straight through to the\n * underlying embla instance.\n * Arrow-key navigation is wired in via `onKeyDownCapture` on the root, on\n * top of embla's own drag/swipe handling.\n */\n\nimport * as React from \"react\"\nimport useEmblaCarousel, {\n type UseEmblaCarouselType,\n} from \"embla-carousel-react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button, type TextButtonSize } from \"@/components/ui/button\"\nimport { ChevronLeftIcon, ChevronRightIcon } from \"lucide-react\"\n\ntype CarouselApi = UseEmblaCarouselType[1]\ntype UseCarouselParameters = Parameters<typeof useEmblaCarousel>\ntype CarouselOptions = UseCarouselParameters[0]\ntype CarouselPlugin = UseCarouselParameters[1]\n\ntype CarouselProps = {\n opts?: CarouselOptions\n plugins?: CarouselPlugin\n orientation?: \"horizontal\" | \"vertical\"\n setApi?: (api: CarouselApi) => void\n}\n\ntype CarouselContextProps = {\n carouselRef: ReturnType<typeof useEmblaCarousel>[0]\n api: ReturnType<typeof useEmblaCarousel>[1]\n scrollPrev: () => void\n scrollNext: () => void\n canScrollPrev: boolean\n canScrollNext: boolean\n /** Indices embla currently considers in view — CarouselItem uses this to `inert` itself while scrolled off-screen, since embla only ever transforms slide position, never removes/hides one from the tab order or accessibility tree on its own. */\n slidesInView: number[]\n} & CarouselProps\n\nconst CarouselContext = React.createContext<CarouselContextProps | null>(null)\n\nfunction useCarousel() {\n const context = React.useContext(CarouselContext)\n\n if (!context) {\n throw new Error(\"useCarousel must be used within a <Carousel />\")\n }\n\n return context\n}\n\nfunction Carousel({\n orientation = \"horizontal\",\n opts,\n setApi,\n plugins,\n className,\n children,\n ...props\n}: React.ComponentProps<\"div\"> & CarouselProps) {\n const [carouselRef, api] = useEmblaCarousel(\n {\n ...opts,\n axis: orientation === \"horizontal\" ? \"x\" : \"y\",\n },\n plugins\n )\n const [canScrollPrev, setCanScrollPrev] = React.useState(false)\n const [canScrollNext, setCanScrollNext] = React.useState(false)\n const [slidesInView, setSlidesInView] = React.useState<number[]>([])\n // \"Slide X of N\" for screen reader users — sighted users get this from\n // seeing which slide is visible; nothing else here announces a change\n // triggered by Previous/Next, drag, keyboard, or an autoplay plugin.\n const [announcement, setAnnouncement] = React.useState(\"\")\n\n const onSelect = React.useCallback((api: CarouselApi) => {\n if (!api) return\n setCanScrollPrev(api.canScrollPrev())\n setCanScrollNext(api.canScrollNext())\n setAnnouncement(\n `Slide ${api.selectedScrollSnap() + 1} of ${api.scrollSnapList().length}`\n )\n }, [])\n\n const onSlidesInView = React.useCallback((api: CarouselApi) => {\n if (!api) return\n setSlidesInView(api.slidesInView())\n }, [])\n\n const scrollPrev = React.useCallback(() => {\n api?.scrollPrev()\n }, [api])\n\n const scrollNext = React.useCallback(() => {\n api?.scrollNext()\n }, [api])\n\n const handleKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (event.key === \"ArrowLeft\") {\n event.preventDefault()\n scrollPrev()\n } else if (event.key === \"ArrowRight\") {\n event.preventDefault()\n scrollNext()\n }\n },\n [scrollPrev, scrollNext]\n )\n\n React.useEffect(() => {\n if (!api || !setApi) return\n setApi(api)\n }, [api, setApi])\n\n React.useEffect(() => {\n if (!api) return\n onSelect(api)\n onSlidesInView(api)\n api.on(\"reInit\", onSelect)\n api.on(\"select\", onSelect)\n api.on(\"reInit\", onSlidesInView)\n api.on(\"slidesInView\", onSlidesInView)\n\n return () => {\n api?.off(\"select\", onSelect)\n api?.off(\"reInit\", onSelect)\n api?.off(\"reInit\", onSlidesInView)\n api?.off(\"slidesInView\", onSlidesInView)\n }\n }, [api, onSelect, onSlidesInView])\n\n return (\n <CarouselContext.Provider\n value={{\n carouselRef,\n api: api,\n opts,\n orientation:\n orientation || (opts?.axis === \"y\" ? \"vertical\" : \"horizontal\"),\n scrollPrev,\n scrollNext,\n canScrollPrev,\n canScrollNext,\n slidesInView,\n }}\n >\n <div\n onKeyDownCapture={handleKeyDown}\n className={cn(\"relative\", className)}\n role=\"region\"\n aria-roledescription=\"carousel\"\n data-slot=\"carousel\"\n {...props}\n >\n {children}\n <span role=\"status\" className=\"sr-only\">\n {announcement}\n </span>\n </div>\n </CarouselContext.Provider>\n )\n}\n\nfunction CarouselContent({\n className,\n children,\n ...props\n}: React.ComponentProps<\"div\">) {\n const { carouselRef, orientation } = useCarousel()\n\n return (\n <div\n ref={carouselRef}\n className=\"overflow-hidden\"\n data-slot=\"carousel-content\"\n >\n <div\n className={cn(\n \"flex\",\n orientation === \"horizontal\" ? \"-ml-4\" : \"-mt-4 flex-col\",\n className\n )}\n {...props}\n >\n {/* Index by position, not by cloning a prop the consumer set —\n embla's own slidesInView()/slideNodes() are index-based against\n this exact DOM order, so this has to match it exactly regardless\n of whatever key/props each CarouselItem already carries. */}\n {React.Children.map(children, (child, index) =>\n React.isValidElement(child)\n ? React.cloneElement(\n child as React.ReactElement<{ \"data-slide-index\"?: number }>,\n { \"data-slide-index\": index }\n )\n : child\n )}\n </div>\n </div>\n )\n}\n\nfunction CarouselItem({\n className,\n \"data-slide-index\": slideIndex,\n ...props\n}: React.ComponentProps<\"div\"> & { \"data-slide-index\"?: number }) {\n const { orientation, slidesInView } = useCarousel()\n // Only actually withhold once embla has reported an in-view set at all —\n // before the first \"slidesInView\" event (or if this item's index is\n // somehow missing) every slide would otherwise start `inert`, which is\n // worse than never applying it.\n const isOffscreen =\n slidesInView.length > 0 &&\n slideIndex !== undefined &&\n !slidesInView.includes(slideIndex)\n\n return (\n <div\n role=\"group\"\n aria-roledescription=\"slide\"\n data-slot=\"carousel-item\"\n // embla only ever transforms slide position — scrolled-away slides\n // stay in the tab order and accessibility tree unless something else\n // removes them. `inert` does both at once (and is a no-op, harmless,\n // for the slide(s) actually in view).\n inert={isOffscreen}\n className={cn(\n \"min-w-0 shrink-0 grow-0 basis-full\",\n orientation === \"horizontal\" ? \"pl-4\" : \"pt-4\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction CarouselPrevious({\n className,\n variant = \"outline\",\n size = \"sm\",\n ...props\n}: Omit<\n React.ComponentProps<typeof Button>,\n \"size\" | \"iconOnly\" | \"asChild\" | \"aria-label\"\n> & {\n /** Height step of the round icon-only control; it is always square (`iconOnly`). */\n size?: TextButtonSize\n \"aria-label\"?: string\n}) {\n const { orientation, scrollPrev, canScrollPrev } = useCarousel()\n\n return (\n <Button\n data-slot=\"carousel-previous\"\n variant={variant}\n size={size}\n iconOnly\n className={cn(\n \"absolute touch-manipulation rounded-full\",\n orientation === \"horizontal\"\n ? \"inset-y-0 -left-12 my-auto\"\n : \"-top-12 left-1/2 -translate-x-1/2 rotate-90\",\n className\n )}\n disabled={!canScrollPrev}\n onClick={scrollPrev}\n aria-label=\"Previous slide\"\n {...props}\n >\n <ChevronLeftIcon />\n <span className=\"sr-only\">Previous slide</span>\n </Button>\n )\n}\n\nfunction CarouselNext({\n className,\n variant = \"outline\",\n size = \"sm\",\n ...props\n}: Omit<\n React.ComponentProps<typeof Button>,\n \"size\" | \"iconOnly\" | \"asChild\" | \"aria-label\"\n> & {\n /** Height step of the round icon-only control; it is always square (`iconOnly`). */\n size?: TextButtonSize\n \"aria-label\"?: string\n}) {\n const { orientation, scrollNext, canScrollNext } = useCarousel()\n\n return (\n <Button\n data-slot=\"carousel-next\"\n variant={variant}\n size={size}\n iconOnly\n className={cn(\n \"absolute touch-manipulation rounded-full\",\n orientation === \"horizontal\"\n ? \"inset-y-0 -right-12 my-auto\"\n : \"-bottom-12 left-1/2 -translate-x-1/2 rotate-90\",\n className\n )}\n disabled={!canScrollNext}\n onClick={scrollNext}\n aria-label=\"Next slide\"\n {...props}\n >\n <ChevronRightIcon />\n <span className=\"sr-only\">Next slide</span>\n </Button>\n )\n}\n\nexport {\n type CarouselApi,\n Carousel,\n CarouselContent,\n CarouselItem,\n CarouselPrevious,\n CarouselNext,\n useCarousel,\n}\n\n/**\n * WHEN TO USE\n * - Paging horizontally (or vertically) through cards or media where\n * showing everything at once is impractical — featured items, testimonials,\n * image galleries.\n *\n * NOT WHEN\n * - Content users need to scan or compare side by side — use a grid; a\n * carousel hides everything but the current slide(s).\n * - Essential content that must never be missed — carousels are easy to\n * stop paging through.\n * - Paged tabular/list data — use Pagination, not slide-based paging.\n *\n * KEY RULES\n * - `orientation=\"vertical\"` flips both the embla axis and the previous/next\n * button placement (rotated 90°, stacked above/below instead of beside).\n * - Number of visible slides is controlled entirely by `basis-*` on\n * CarouselItem (e.g. `basis-1/3` shows three) — there's no `slidesToShow` prop.\n * - `useCarousel()` throws outside a `<Carousel>` — only call it from a\n * descendant of Carousel (as CarouselContent/Item/Previous/Next do).\n * - `setApi` is how you get imperative control (`api.scrollTo(i)`, listen to\n * `\"select\"`) — pass a state setter and read the api once it's set.\n *\n * USAGE\n * <Carousel className=\"w-64\">\n * <CarouselContent>\n * {slides.map((s) => (\n * <CarouselItem key={s}><Card>...</Card></CarouselItem>\n * ))}\n * </CarouselContent>\n * <CarouselPrevious />\n * <CarouselNext />\n * </Carousel>\n *\n * // Three slides visible at once\n * <CarouselItem className=\"basis-1/3\">...</CarouselItem>\n */\n"],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checkbox — boolean toggle control.
|
|
3
|
+
*
|
|
4
|
+
* Built on the Radix Checkbox primitive. Error styling is driven off
|
|
5
|
+
* `aria-invalid` (no `error` prop), and a checked state of "indeterminate"
|
|
6
|
+
* is supported for parent-of-many selection patterns.
|
|
7
|
+
*/
|
|
8
|
+
import * as React from "react";
|
|
9
|
+
import { Checkbox as CheckboxPrimitive } from "radix-ui";
|
|
10
|
+
declare function Checkbox({ className, checked, "aria-labelledby": ariaLabelledby, "aria-describedby": ariaDescribedby, ...props }: React.ComponentProps<typeof CheckboxPrimitive.Root>): React.JSX.Element;
|
|
11
|
+
export { Checkbox };
|
|
12
|
+
/**
|
|
13
|
+
* WHEN TO USE
|
|
14
|
+
* - Toggling one independent boolean value: accepting terms, a single yes/no setting
|
|
15
|
+
* - Selecting rows in a list (multi-select), including a "select all" checkbox whose
|
|
16
|
+
* own `checked` is set to "indeterminate" when some but not all children are checked
|
|
17
|
+
*
|
|
18
|
+
* NOT WHEN
|
|
19
|
+
* - Exclusive selection from a small set — use RadioGroup
|
|
20
|
+
* - A setting that takes effect immediately with no form submission — use Switch
|
|
21
|
+
*
|
|
22
|
+
* KEY RULES
|
|
23
|
+
* - `checked` accepts true, false, or "indeterminate" — indeterminate is visual only,
|
|
24
|
+
* the app owns the tri-state logic for parent/child checkbox groups. It renders with
|
|
25
|
+
* the same filled/primary treatment as `true`, but with a dash instead of a check —
|
|
26
|
+
* distinct from both `true` and `false`, not just a `true` alias.
|
|
27
|
+
* - Error state is driven entirely by `aria-invalid` — there is no `error` prop
|
|
28
|
+
* - Not a standalone label — wrap in a Label (or pair via htmlFor/id) so the text is
|
|
29
|
+
* clickable and screen readers announce it. Inside Field's horizontal FieldContent +
|
|
30
|
+
* FieldTitle pattern (a standalone control beside a heading `<div>`, not a `<label>`),
|
|
31
|
+
* Checkbox instead picks up FieldTitle as `aria-labelledby` and FieldDescription/
|
|
32
|
+
* FieldError as `aria-describedby` automatically — pass your own aria-label/
|
|
33
|
+
* aria-labelledby/aria-describedby only to override that default.
|
|
34
|
+
* - An AI-suggested (pre-checked) option is a plain composition, not a dedicated prop: put
|
|
35
|
+
* `AILabel` (size="sm" — never the opt-in `iconOnly` by default) beside that option as a sibling of the Label,
|
|
36
|
+
* never inside it — AILabelTrigger is a button, and a button nested in a label is invalid
|
|
37
|
+
* HTML that would also toggle the checkbox on every click. Use the htmlFor/id pairing for
|
|
38
|
+
* those rows instead of the wrapping Label. Pass AILabel `edited={!checked}` and an
|
|
39
|
+
* `onRevert` that re-checks it, so flipping an AI-suggested option turns its marker into an
|
|
40
|
+
* undo button (AILabel's own override/undo morph, same as Input/Select/Textarea). When most
|
|
41
|
+
* options are AI-selected, put one AILabel beside the FieldLegend instead, with `edited`
|
|
42
|
+
* true once the checked set differs from AI's and `onRevert` restoring the whole set. See
|
|
43
|
+
* AI/Checkbox.
|
|
44
|
+
*
|
|
45
|
+
* USAGE
|
|
46
|
+
* <Label className="flex items-center gap-2">
|
|
47
|
+
* <Checkbox />
|
|
48
|
+
* Accept terms and conditions
|
|
49
|
+
* </Label>
|
|
50
|
+
*
|
|
51
|
+
* <Checkbox checked="indeterminate" />
|
|
52
|
+
* <Checkbox aria-invalid />
|
|
53
|
+
*/
|
|
54
|
+
//# sourceMappingURL=checkbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkbox.d.ts","sourceRoot":"","sources":["../../../src/components/ui/checkbox.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,QAAQ,IAAI,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAMxD,iBAAS,QAAQ,CAAC,EAChB,SAAS,EACT,OAAO,EACP,iBAAiB,EAAE,cAAc,EACjC,kBAAkB,EAAE,eAAe,EACnC,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,iBAAiB,CAAC,IAAI,CAAC,qBAuCrD;AAED,OAAO,EAAE,QAAQ,EAAE,CAAA;AAEnB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { cn } from "../../lib/utils.js";
|
|
2
|
+
import { FieldIdContext } from "./field.js";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { Checkbox as Checkbox$1 } from "radix-ui";
|
|
5
|
+
import { CheckIcon, MinusIcon } from "lucide-react";
|
|
6
|
+
import { jsx } from "react/jsx-runtime";
|
|
7
|
+
//#region src/components/ui/checkbox.tsx
|
|
8
|
+
/**
|
|
9
|
+
* Checkbox — boolean toggle control.
|
|
10
|
+
*
|
|
11
|
+
* Built on the Radix Checkbox primitive. Error styling is driven off
|
|
12
|
+
* `aria-invalid` (no `error` prop), and a checked state of "indeterminate"
|
|
13
|
+
* is supported for parent-of-many selection patterns.
|
|
14
|
+
*/
|
|
15
|
+
function Checkbox({ className, checked, "aria-labelledby": ariaLabelledby, "aria-describedby": ariaDescribedby, ...props }) {
|
|
16
|
+
const idContext = React.useContext(FieldIdContext);
|
|
17
|
+
const resolvedLabelledby = ariaLabelledby ?? (!props["aria-label"] ? idContext?.titleId ?? void 0 : void 0);
|
|
18
|
+
const resolvedDescribedby = ariaDescribedby ?? ([idContext?.descriptionId, idContext?.errorId].filter(Boolean).join(" ") || void 0);
|
|
19
|
+
return /* @__PURE__ */ jsx(Checkbox$1.Root, {
|
|
20
|
+
"data-slot": "checkbox",
|
|
21
|
+
checked,
|
|
22
|
+
"aria-labelledby": resolvedLabelledby,
|
|
23
|
+
"aria-describedby": resolvedDescribedby,
|
|
24
|
+
className: cn("peer relative flex size-4 shrink-0 items-center justify-center rounded-[4px] border border-input transition-colors outline-none group-has-disabled/field:opacity-50 after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=indeterminate]:border-primary data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground dark:data-[state=checked]:bg-primary dark:data-[state=indeterminate]:bg-primary", className),
|
|
25
|
+
...props,
|
|
26
|
+
children: /* @__PURE__ */ jsx(Checkbox$1.Indicator, {
|
|
27
|
+
"data-slot": "checkbox-indicator",
|
|
28
|
+
className: "grid place-content-center text-current transition-none [&>svg]:size-3.5",
|
|
29
|
+
children: checked === "indeterminate" ? /* @__PURE__ */ jsx(MinusIcon, {}) : /* @__PURE__ */ jsx(CheckIcon, {})
|
|
30
|
+
})
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* WHEN TO USE
|
|
35
|
+
* - Toggling one independent boolean value: accepting terms, a single yes/no setting
|
|
36
|
+
* - Selecting rows in a list (multi-select), including a "select all" checkbox whose
|
|
37
|
+
* own `checked` is set to "indeterminate" when some but not all children are checked
|
|
38
|
+
*
|
|
39
|
+
* NOT WHEN
|
|
40
|
+
* - Exclusive selection from a small set — use RadioGroup
|
|
41
|
+
* - A setting that takes effect immediately with no form submission — use Switch
|
|
42
|
+
*
|
|
43
|
+
* KEY RULES
|
|
44
|
+
* - `checked` accepts true, false, or "indeterminate" — indeterminate is visual only,
|
|
45
|
+
* the app owns the tri-state logic for parent/child checkbox groups. It renders with
|
|
46
|
+
* the same filled/primary treatment as `true`, but with a dash instead of a check —
|
|
47
|
+
* distinct from both `true` and `false`, not just a `true` alias.
|
|
48
|
+
* - Error state is driven entirely by `aria-invalid` — there is no `error` prop
|
|
49
|
+
* - Not a standalone label — wrap in a Label (or pair via htmlFor/id) so the text is
|
|
50
|
+
* clickable and screen readers announce it. Inside Field's horizontal FieldContent +
|
|
51
|
+
* FieldTitle pattern (a standalone control beside a heading `<div>`, not a `<label>`),
|
|
52
|
+
* Checkbox instead picks up FieldTitle as `aria-labelledby` and FieldDescription/
|
|
53
|
+
* FieldError as `aria-describedby` automatically — pass your own aria-label/
|
|
54
|
+
* aria-labelledby/aria-describedby only to override that default.
|
|
55
|
+
* - An AI-suggested (pre-checked) option is a plain composition, not a dedicated prop: put
|
|
56
|
+
* `AILabel` (size="sm" — never the opt-in `iconOnly` by default) beside that option as a sibling of the Label,
|
|
57
|
+
* never inside it — AILabelTrigger is a button, and a button nested in a label is invalid
|
|
58
|
+
* HTML that would also toggle the checkbox on every click. Use the htmlFor/id pairing for
|
|
59
|
+
* those rows instead of the wrapping Label. Pass AILabel `edited={!checked}` and an
|
|
60
|
+
* `onRevert` that re-checks it, so flipping an AI-suggested option turns its marker into an
|
|
61
|
+
* undo button (AILabel's own override/undo morph, same as Input/Select/Textarea). When most
|
|
62
|
+
* options are AI-selected, put one AILabel beside the FieldLegend instead, with `edited`
|
|
63
|
+
* true once the checked set differs from AI's and `onRevert` restoring the whole set. See
|
|
64
|
+
* AI/Checkbox.
|
|
65
|
+
*
|
|
66
|
+
* USAGE
|
|
67
|
+
* <Label className="flex items-center gap-2">
|
|
68
|
+
* <Checkbox />
|
|
69
|
+
* Accept terms and conditions
|
|
70
|
+
* </Label>
|
|
71
|
+
*
|
|
72
|
+
* <Checkbox checked="indeterminate" />
|
|
73
|
+
* <Checkbox aria-invalid />
|
|
74
|
+
*/
|
|
75
|
+
//#endregion
|
|
76
|
+
export { Checkbox };
|
|
77
|
+
|
|
78
|
+
//# sourceMappingURL=checkbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkbox.js","names":[],"sources":["../../../src/components/ui/checkbox.tsx"],"sourcesContent":["/**\n * Checkbox — boolean toggle control.\n *\n * Built on the Radix Checkbox primitive. Error styling is driven off\n * `aria-invalid` (no `error` prop), and a checked state of \"indeterminate\"\n * is supported for parent-of-many selection patterns.\n */\n\nimport * as React from \"react\"\nimport { Checkbox as CheckboxPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@/lib/utils\"\nimport { CheckIcon, MinusIcon } from \"lucide-react\"\nimport { FieldIdContext } from \"@/components/ui/field\"\n\nfunction Checkbox({\n className,\n checked,\n \"aria-labelledby\": ariaLabelledby,\n \"aria-describedby\": ariaDescribedby,\n ...props\n}: React.ComponentProps<typeof CheckboxPrimitive.Root>) {\n // Inside Field's horizontal FieldContent+FieldTitle pattern, FieldTitle is\n // a plain heading `<div>` with no `htmlFor`/id wiring of its own — without\n // this, that documented pattern shipped an accessible-name-less checkbox.\n // Falls back to FieldTitle/FieldDescription/FieldError ids only when the\n // consumer hasn't already supplied their own.\n const idContext = React.useContext(FieldIdContext)\n const resolvedLabelledby =\n ariaLabelledby ??\n (!props[\"aria-label\"] ? idContext?.titleId ?? undefined : undefined)\n const resolvedDescribedby =\n ariaDescribedby ??\n ([idContext?.descriptionId, idContext?.errorId].filter(Boolean).join(\" \") ||\n undefined)\n\n return (\n <CheckboxPrimitive.Root\n data-slot=\"checkbox\"\n checked={checked}\n aria-labelledby={resolvedLabelledby}\n aria-describedby={resolvedDescribedby}\n className={cn(\n // Explicit data-[state=x]: for both truthy states — \"checked\" alone\n // (or the data-checked: shorthand) doesn't also match\n // data-state=\"indeterminate\", so indeterminate silently rendered as\n // a plain unchecked box with a stray dark checkmark floating in it.\n \"peer relative flex size-4 shrink-0 items-center justify-center rounded-[4px] border border-input transition-colors outline-none group-has-disabled/field:opacity-50 after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=indeterminate]:border-primary data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground dark:data-[state=checked]:bg-primary dark:data-[state=indeterminate]:bg-primary\",\n className\n )}\n {...props}\n >\n <CheckboxPrimitive.Indicator\n data-slot=\"checkbox-indicator\"\n className=\"grid place-content-center text-current transition-none [&>svg]:size-3.5\"\n >\n {checked === \"indeterminate\" ? <MinusIcon /> : <CheckIcon />}\n </CheckboxPrimitive.Indicator>\n </CheckboxPrimitive.Root>\n )\n}\n\nexport { Checkbox }\n\n/**\n * WHEN TO USE\n * - Toggling one independent boolean value: accepting terms, a single yes/no setting\n * - Selecting rows in a list (multi-select), including a \"select all\" checkbox whose\n * own `checked` is set to \"indeterminate\" when some but not all children are checked\n *\n * NOT WHEN\n * - Exclusive selection from a small set — use RadioGroup\n * - A setting that takes effect immediately with no form submission — use Switch\n *\n * KEY RULES\n * - `checked` accepts true, false, or \"indeterminate\" — indeterminate is visual only,\n * the app owns the tri-state logic for parent/child checkbox groups. It renders with\n * the same filled/primary treatment as `true`, but with a dash instead of a check —\n * distinct from both `true` and `false`, not just a `true` alias.\n * - Error state is driven entirely by `aria-invalid` — there is no `error` prop\n * - Not a standalone label — wrap in a Label (or pair via htmlFor/id) so the text is\n * clickable and screen readers announce it. Inside Field's horizontal FieldContent +\n * FieldTitle pattern (a standalone control beside a heading `<div>`, not a `<label>`),\n * Checkbox instead picks up FieldTitle as `aria-labelledby` and FieldDescription/\n * FieldError as `aria-describedby` automatically — pass your own aria-label/\n * aria-labelledby/aria-describedby only to override that default.\n * - An AI-suggested (pre-checked) option is a plain composition, not a dedicated prop: put\n * `AILabel` (size=\"sm\" — never the opt-in `iconOnly` by default) beside that option as a sibling of the Label,\n * never inside it — AILabelTrigger is a button, and a button nested in a label is invalid\n * HTML that would also toggle the checkbox on every click. Use the htmlFor/id pairing for\n * those rows instead of the wrapping Label. Pass AILabel `edited={!checked}` and an\n * `onRevert` that re-checks it, so flipping an AI-suggested option turns its marker into an\n * undo button (AILabel's own override/undo morph, same as Input/Select/Textarea). When most\n * options are AI-selected, put one AILabel beside the FieldLegend instead, with `edited`\n * true once the checked set differs from AI's and `onRevert` restoring the whole set. See\n * AI/Checkbox.\n *\n * USAGE\n * <Label className=\"flex items-center gap-2\">\n * <Checkbox />\n * Accept terms and conditions\n * </Label>\n *\n * <Checkbox checked=\"indeterminate\" />\n * <Checkbox aria-invalid />\n */\n"],"mappings":";;;;;;;;;;;;;;AAeA,SAAS,SAAS,EAChB,WACA,SACA,mBAAmB,gBACnB,oBAAoB,iBACpB,GAAG,SACmD;CAMtD,MAAM,YAAY,MAAM,WAAW,cAAc;CACjD,MAAM,qBACJ,mBACC,CAAC,MAAM,gBAAgB,WAAW,WAAW,KAAA,IAAY,KAAA;CAC5D,MAAM,sBACJ,oBACC,CAAC,WAAW,eAAe,WAAW,OAAO,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG,KACtE,KAAA;CAEJ,OACE,oBAAC,WAAkB,MAAnB;EACE,aAAU;EACD;EACT,mBAAiB;EACjB,oBAAkB;EAClB,WAAW,GAKT,m3BACA,SACF;EACA,GAAI;EAEJ,UAAA,oBAAC,WAAkB,WAAnB;GACE,aAAU;GACV,WAAU;GAET,UAAA,YAAY,kBAAkB,oBAAC,WAAD,CAAY,CAAA,IAAI,oBAAC,WAAD,CAAY,CAAA;EAChC,CAAA;CACP,CAAA;AAE5B"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collapsible — single unstyled show/hide region.
|
|
3
|
+
*
|
|
4
|
+
* A thin wrapper over the Radix Collapsible primitive with no default
|
|
5
|
+
* classes of its own — it's the unopinionated building block Accordion is
|
|
6
|
+
* built on top of.
|
|
7
|
+
*/
|
|
8
|
+
import { Collapsible as CollapsiblePrimitive } from "radix-ui";
|
|
9
|
+
declare function Collapsible({ ...props }: React.ComponentProps<typeof CollapsiblePrimitive.Root>): import("react").JSX.Element;
|
|
10
|
+
declare function CollapsibleTrigger({ ...props }: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>): import("react").JSX.Element;
|
|
11
|
+
declare function CollapsibleContent({ ...props }: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>): import("react").JSX.Element;
|
|
12
|
+
export { Collapsible, CollapsibleTrigger, CollapsibleContent };
|
|
13
|
+
/**
|
|
14
|
+
* WHEN TO USE
|
|
15
|
+
* - One independent expandable region — an advanced-options block, a
|
|
16
|
+
* "toggle details" row, a single FAQ-style disclosure with no siblings.
|
|
17
|
+
*
|
|
18
|
+
* NOT WHEN
|
|
19
|
+
* - Several sections that should coordinate (only one open at a time, or a
|
|
20
|
+
* shared visual list of items) — use Accordion, which composes this
|
|
21
|
+
* primitive with that coordination.
|
|
22
|
+
* - Content that should float above the page instead of pushing it down —
|
|
23
|
+
* use Popover.
|
|
24
|
+
*
|
|
25
|
+
* KEY RULES
|
|
26
|
+
* - Uncontrolled by default (`defaultOpen`); pass `open`/`onOpenChange` to
|
|
27
|
+
* control it externally.
|
|
28
|
+
* - Ships with no styling — width, spacing, and the open/close affordance
|
|
29
|
+
* (chevron, etc.) are entirely the consumer's responsibility.
|
|
30
|
+
* - `CollapsibleTrigger asChild` lets any element (e.g. a Button) drive it.
|
|
31
|
+
*
|
|
32
|
+
* USAGE
|
|
33
|
+
* <Collapsible className="w-80">
|
|
34
|
+
* <div className="flex items-center justify-between">
|
|
35
|
+
* <h4>Installed dependencies</h4>
|
|
36
|
+
* <CollapsibleTrigger asChild>
|
|
37
|
+
* <Button variant="ghost" size="sm" iconOnly aria-label="Toggle">
|
|
38
|
+
* <ChevronsUpDownIcon />
|
|
39
|
+
* </Button>
|
|
40
|
+
* </CollapsibleTrigger>
|
|
41
|
+
* </div>
|
|
42
|
+
* <CollapsibleContent>...</CollapsibleContent>
|
|
43
|
+
* </Collapsible>
|
|
44
|
+
*/
|
|
45
|
+
//# sourceMappingURL=collapsible.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collapsible.d.ts","sourceRoot":"","sources":["../../../src/components/ui/collapsible.tsx"],"names":[],"mappings":"AAEA;;;;;;GAMG;AAEH,OAAO,EAAE,WAAW,IAAI,oBAAoB,EAAE,MAAM,UAAU,CAAA;AAE9D,iBAAS,WAAW,CAAC,EACnB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,IAAI,CAAC,+BAExD;AAED,iBAAS,kBAAkB,CAAC,EAC1B,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,kBAAkB,CAAC,+BAOtE;AAED,iBAAS,kBAAkB,CAAC,EAC1B,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,oBAAoB,CAAC,kBAAkB,CAAC,+BAOtE;AAED,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,CAAA;AAE9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Collapsible as Collapsible$1 } from "radix-ui";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
//#region src/components/ui/collapsible.tsx
|
|
5
|
+
/**
|
|
6
|
+
* Collapsible — single unstyled show/hide region.
|
|
7
|
+
*
|
|
8
|
+
* A thin wrapper over the Radix Collapsible primitive with no default
|
|
9
|
+
* classes of its own — it's the unopinionated building block Accordion is
|
|
10
|
+
* built on top of.
|
|
11
|
+
*/
|
|
12
|
+
function Collapsible({ ...props }) {
|
|
13
|
+
return /* @__PURE__ */ jsx(Collapsible$1.Root, {
|
|
14
|
+
"data-slot": "collapsible",
|
|
15
|
+
...props
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
function CollapsibleTrigger({ ...props }) {
|
|
19
|
+
return /* @__PURE__ */ jsx(Collapsible$1.CollapsibleTrigger, {
|
|
20
|
+
"data-slot": "collapsible-trigger",
|
|
21
|
+
...props
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function CollapsibleContent({ ...props }) {
|
|
25
|
+
return /* @__PURE__ */ jsx(Collapsible$1.CollapsibleContent, {
|
|
26
|
+
"data-slot": "collapsible-content",
|
|
27
|
+
...props
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* WHEN TO USE
|
|
32
|
+
* - One independent expandable region — an advanced-options block, a
|
|
33
|
+
* "toggle details" row, a single FAQ-style disclosure with no siblings.
|
|
34
|
+
*
|
|
35
|
+
* NOT WHEN
|
|
36
|
+
* - Several sections that should coordinate (only one open at a time, or a
|
|
37
|
+
* shared visual list of items) — use Accordion, which composes this
|
|
38
|
+
* primitive with that coordination.
|
|
39
|
+
* - Content that should float above the page instead of pushing it down —
|
|
40
|
+
* use Popover.
|
|
41
|
+
*
|
|
42
|
+
* KEY RULES
|
|
43
|
+
* - Uncontrolled by default (`defaultOpen`); pass `open`/`onOpenChange` to
|
|
44
|
+
* control it externally.
|
|
45
|
+
* - Ships with no styling — width, spacing, and the open/close affordance
|
|
46
|
+
* (chevron, etc.) are entirely the consumer's responsibility.
|
|
47
|
+
* - `CollapsibleTrigger asChild` lets any element (e.g. a Button) drive it.
|
|
48
|
+
*
|
|
49
|
+
* USAGE
|
|
50
|
+
* <Collapsible className="w-80">
|
|
51
|
+
* <div className="flex items-center justify-between">
|
|
52
|
+
* <h4>Installed dependencies</h4>
|
|
53
|
+
* <CollapsibleTrigger asChild>
|
|
54
|
+
* <Button variant="ghost" size="sm" iconOnly aria-label="Toggle">
|
|
55
|
+
* <ChevronsUpDownIcon />
|
|
56
|
+
* </Button>
|
|
57
|
+
* </CollapsibleTrigger>
|
|
58
|
+
* </div>
|
|
59
|
+
* <CollapsibleContent>...</CollapsibleContent>
|
|
60
|
+
* </Collapsible>
|
|
61
|
+
*/
|
|
62
|
+
//#endregion
|
|
63
|
+
export { Collapsible, CollapsibleContent, CollapsibleTrigger };
|
|
64
|
+
|
|
65
|
+
//# sourceMappingURL=collapsible.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collapsible.js","names":[],"sources":["../../../src/components/ui/collapsible.tsx"],"sourcesContent":["\"use client\"\n\n/**\n * Collapsible — single unstyled show/hide region.\n *\n * A thin wrapper over the Radix Collapsible primitive with no default\n * classes of its own — it's the unopinionated building block Accordion is\n * built on top of.\n */\n\nimport { Collapsible as CollapsiblePrimitive } from \"radix-ui\"\n\nfunction Collapsible({\n ...props\n}: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {\n return <CollapsiblePrimitive.Root data-slot=\"collapsible\" {...props} />\n}\n\nfunction CollapsibleTrigger({\n ...props\n}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {\n return (\n <CollapsiblePrimitive.CollapsibleTrigger\n data-slot=\"collapsible-trigger\"\n {...props}\n />\n )\n}\n\nfunction CollapsibleContent({\n ...props\n}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {\n return (\n <CollapsiblePrimitive.CollapsibleContent\n data-slot=\"collapsible-content\"\n {...props}\n />\n )\n}\n\nexport { Collapsible, CollapsibleTrigger, CollapsibleContent }\n\n/**\n * WHEN TO USE\n * - One independent expandable region — an advanced-options block, a\n * \"toggle details\" row, a single FAQ-style disclosure with no siblings.\n *\n * NOT WHEN\n * - Several sections that should coordinate (only one open at a time, or a\n * shared visual list of items) — use Accordion, which composes this\n * primitive with that coordination.\n * - Content that should float above the page instead of pushing it down —\n * use Popover.\n *\n * KEY RULES\n * - Uncontrolled by default (`defaultOpen`); pass `open`/`onOpenChange` to\n * control it externally.\n * - Ships with no styling — width, spacing, and the open/close affordance\n * (chevron, etc.) are entirely the consumer's responsibility.\n * - `CollapsibleTrigger asChild` lets any element (e.g. a Button) drive it.\n *\n * USAGE\n * <Collapsible className=\"w-80\">\n * <div className=\"flex items-center justify-between\">\n * <h4>Installed dependencies</h4>\n * <CollapsibleTrigger asChild>\n * <Button variant=\"ghost\" size=\"sm\" iconOnly aria-label=\"Toggle\">\n * <ChevronsUpDownIcon />\n * </Button>\n * </CollapsibleTrigger>\n * </div>\n * <CollapsibleContent>...</CollapsibleContent>\n * </Collapsible>\n */\n"],"mappings":";;;;;;;;;;;AAYA,SAAS,YAAY,EACnB,GAAG,SACsD;CACzD,OAAO,oBAAC,cAAqB,MAAtB;EAA2B,aAAU;EAAc,GAAI;CAAQ,CAAA;AACxE;AAEA,SAAS,mBAAmB,EAC1B,GAAG,SACoE;CACvE,OACE,oBAAC,cAAqB,oBAAtB;EACE,aAAU;EACV,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,mBAAmB,EAC1B,GAAG,SACoE;CACvE,OACE,oBAAC,cAAqB,oBAAtB;EACE,aAAU;EACV,GAAI;CACL,CAAA;AAEL"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Combobox — searchable select that filters options as you type.
|
|
3
|
+
*
|
|
4
|
+
* Built on the Base UI Combobox primitive (Radix has no combobox primitive
|
|
5
|
+
* of its own). Composed of Combobox, ComboboxInput, ComboboxChips, etc.; the
|
|
6
|
+
* input/trigger/clear row renders through MMDS's own InputGroup component,
|
|
7
|
+
* and a useComboboxAnchor helper wires the popup to the chips container.
|
|
8
|
+
*/
|
|
9
|
+
import * as React from "react";
|
|
10
|
+
import { Combobox as ComboboxPrimitive } from "@base-ui/react/combobox";
|
|
11
|
+
declare const Combobox: typeof ComboboxPrimitive.Root;
|
|
12
|
+
declare function ComboboxValue({ ...props }: ComboboxPrimitive.Value.Props): React.JSX.Element;
|
|
13
|
+
declare function ComboboxTrigger({ className, children, "aria-label": ariaLabel, ...props }: ComboboxPrimitive.Trigger.Props): React.JSX.Element;
|
|
14
|
+
declare function ComboboxInput({ className, children, disabled, showTrigger, showClear, size, ...props }: Omit<ComboboxPrimitive.Input.Props, "size"> & {
|
|
15
|
+
showTrigger?: boolean;
|
|
16
|
+
showClear?: boolean;
|
|
17
|
+
size?: "default" | "sm" | "xs";
|
|
18
|
+
}): React.JSX.Element;
|
|
19
|
+
declare function ComboboxContent({ className, side, sideOffset, align, alignOffset, anchor, ...props }: ComboboxPrimitive.Popup.Props & Pick<ComboboxPrimitive.Positioner.Props, "side" | "align" | "sideOffset" | "alignOffset" | "anchor">): React.JSX.Element;
|
|
20
|
+
declare function ComboboxList({ className, ...props }: ComboboxPrimitive.List.Props): React.JSX.Element;
|
|
21
|
+
declare function ComboboxItem({ className, children, ...props }: ComboboxPrimitive.Item.Props): React.JSX.Element;
|
|
22
|
+
declare function ComboboxGroup({ className, ...props }: ComboboxPrimitive.Group.Props): React.JSX.Element;
|
|
23
|
+
declare function ComboboxLabel({ className, ...props }: ComboboxPrimitive.GroupLabel.Props): React.JSX.Element;
|
|
24
|
+
declare function ComboboxCollection({ ...props }: ComboboxPrimitive.Collection.Props): React.JSX.Element;
|
|
25
|
+
declare function ComboboxEmpty({ className, ...props }: ComboboxPrimitive.Empty.Props): React.JSX.Element;
|
|
26
|
+
declare function ComboboxSeparator({ className, ...props }: ComboboxPrimitive.Separator.Props): React.JSX.Element;
|
|
27
|
+
declare function ComboboxChips({ className, ...props }: React.ComponentPropsWithRef<typeof ComboboxPrimitive.Chips> & ComboboxPrimitive.Chips.Props): React.JSX.Element;
|
|
28
|
+
declare function ComboboxChip({ className, children, showRemove, ...props }: ComboboxPrimitive.Chip.Props & {
|
|
29
|
+
showRemove?: boolean;
|
|
30
|
+
}): React.JSX.Element;
|
|
31
|
+
declare function ComboboxChipsInput({ className, ...props }: ComboboxPrimitive.Input.Props): React.JSX.Element;
|
|
32
|
+
declare function useComboboxAnchor(): React.RefObject<HTMLDivElement | null>;
|
|
33
|
+
export { Combobox, ComboboxInput, ComboboxContent, ComboboxList, ComboboxItem, ComboboxGroup, ComboboxLabel, ComboboxCollection, ComboboxEmpty, ComboboxSeparator, ComboboxChips, ComboboxChip, ComboboxChipsInput, ComboboxTrigger, ComboboxValue, useComboboxAnchor, };
|
|
34
|
+
/**
|
|
35
|
+
* WHEN TO USE
|
|
36
|
+
* - Large or dynamic option lists where typing to filter matters.
|
|
37
|
+
* - Grouped options with headings (ComboboxGroup + ComboboxLabel).
|
|
38
|
+
* - Multi-value entry with removable chips — pass `multiple` to the root Combobox, and render ComboboxChips / ComboboxChip / ComboboxChipsInput instead of ComboboxInput; wire the popover's `anchor` to the chips container via useComboboxAnchor. See the "MultiSelect" story.
|
|
39
|
+
*
|
|
40
|
+
* NOT WHEN
|
|
41
|
+
* - The list is short and fixed — use Select, it's simpler with no search overhead.
|
|
42
|
+
* - It's free text with no option list at all — use Input.
|
|
43
|
+
* - It's a global command palette / action launcher rather than a form value — use Command.
|
|
44
|
+
* - It's a free-text search with no value to commit — results to act on, not a field to fill in
|
|
45
|
+
* (a page-level "Search patients" box, an embedded search panel) — use Command directly (not
|
|
46
|
+
* CommandDialog, which is the ⌘K-palette wrapper) instead of reaching for Combobox just
|
|
47
|
+
* because it also filters as you type; Combobox always resolves to one selected item sitting
|
|
48
|
+
* in the trigger, which a search box doesn't have.
|
|
49
|
+
*
|
|
50
|
+
* KEY RULES
|
|
51
|
+
* - Pass options through the `items` prop on the root Combobox; ComboboxList takes a render-function child, called once per item.
|
|
52
|
+
* - ComboboxContent is portaled and width-locked to the anchor via --anchor-width — don't give it a fixed width.
|
|
53
|
+
* - ComboboxInput has `showTrigger` (default true, shows the dropdown chevron button) and `showClear` (default false, shows a clear button — only useful once a value is set).
|
|
54
|
+
* - ComboboxInput's `size` is "default" | "sm" | "xs" (default "default") —
|
|
55
|
+
* same default/sm/xs vocabulary as Select/Button/Tabs, not Combobox-specific
|
|
56
|
+
* naming. Only affects the input row itself; ComboboxContent/ComboboxItem
|
|
57
|
+
* stay fixed size, matching how Select's own `size` only sizes its trigger.
|
|
58
|
+
* Omits the native `size` (character-width) attribute `<input>` normally
|
|
59
|
+
* has — there's no way to set that one through ComboboxInput.
|
|
60
|
+
* - ComboboxItem renders its own CheckIcon selected-state indicator — don't add a second one.
|
|
61
|
+
*
|
|
62
|
+
* USAGE
|
|
63
|
+
* <Combobox items={spices}>
|
|
64
|
+
* <ComboboxInput placeholder="Select a spice" />
|
|
65
|
+
* <ComboboxContent>
|
|
66
|
+
* <ComboboxEmpty>No spice found.</ComboboxEmpty>
|
|
67
|
+
* <ComboboxList>
|
|
68
|
+
* {(item: string) => <ComboboxItem key={item} value={item}>{item}</ComboboxItem>}
|
|
69
|
+
* </ComboboxList>
|
|
70
|
+
* </ComboboxContent>
|
|
71
|
+
* </Combobox>
|
|
72
|
+
*
|
|
73
|
+
* // Grouped options
|
|
74
|
+
* <Combobox items={spices}>
|
|
75
|
+
* <ComboboxInput placeholder="Select a spice" />
|
|
76
|
+
* <ComboboxContent>
|
|
77
|
+
* <ComboboxList>
|
|
78
|
+
* <ComboboxGroup>
|
|
79
|
+
* <ComboboxLabel>Warm</ComboboxLabel>
|
|
80
|
+
* <ComboboxItem value="Cinnamon">Cinnamon</ComboboxItem>
|
|
81
|
+
* </ComboboxGroup>
|
|
82
|
+
* </ComboboxList>
|
|
83
|
+
* </ComboboxContent>
|
|
84
|
+
* </Combobox>
|
|
85
|
+
*
|
|
86
|
+
* // Default value with a clear button
|
|
87
|
+
* <Combobox items={spices} defaultValue="Cardamom">
|
|
88
|
+
* <ComboboxInput placeholder="Select a spice" showClear />
|
|
89
|
+
* <ComboboxContent>
|
|
90
|
+
* <ComboboxList>
|
|
91
|
+
* {(item: string) => <ComboboxItem key={item} value={item}>{item}</ComboboxItem>}
|
|
92
|
+
* </ComboboxList>
|
|
93
|
+
* </ComboboxContent>
|
|
94
|
+
* </Combobox>
|
|
95
|
+
*
|
|
96
|
+
* // Multi-select with removable chips
|
|
97
|
+
* const [value, setValue] = React.useState<string[]>([])
|
|
98
|
+
* const anchorRef = useComboboxAnchor()
|
|
99
|
+
* <Combobox items={spices} multiple value={value} onValueChange={setValue}>
|
|
100
|
+
* <ComboboxChips ref={anchorRef}>
|
|
101
|
+
* {value.map((item) => <ComboboxChip key={item}>{item}</ComboboxChip>)}
|
|
102
|
+
* <ComboboxChipsInput placeholder="Select spices" />
|
|
103
|
+
* </ComboboxChips>
|
|
104
|
+
* <ComboboxContent anchor={anchorRef}>
|
|
105
|
+
* <ComboboxList>
|
|
106
|
+
* {(item: string) => <ComboboxItem key={item} value={item}>{item}</ComboboxItem>}
|
|
107
|
+
* </ComboboxList>
|
|
108
|
+
* </ComboboxContent>
|
|
109
|
+
* </Combobox>
|
|
110
|
+
*/
|
|
111
|
+
//# sourceMappingURL=combobox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"combobox.d.ts","sourceRoot":"","sources":["../../../src/components/ui/combobox.tsx"],"names":[],"mappings":"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"}
|