@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,95 @@
|
|
|
1
|
+
import { cn } from "../../lib/utils.js";
|
|
2
|
+
import { Button } from "./button.js";
|
|
3
|
+
import "react";
|
|
4
|
+
import { ArrowDownIcon } from "lucide-react";
|
|
5
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import { MessageScroller as MessageScroller$1, useMessageScroller, useMessageScrollerScrollable, useMessageScrollerVisibility } from "@shadcn/react/message-scroller";
|
|
7
|
+
//#region src/components/ui/message-scroller.tsx
|
|
8
|
+
function MessageScrollerProvider(props) {
|
|
9
|
+
return /* @__PURE__ */ jsx(MessageScroller$1.Provider, { ...props });
|
|
10
|
+
}
|
|
11
|
+
function MessageScroller({ className, ...props }) {
|
|
12
|
+
return /* @__PURE__ */ jsx(MessageScroller$1.Root, {
|
|
13
|
+
"data-slot": "message-scroller",
|
|
14
|
+
className: cn("group/message-scroller relative flex size-full min-h-0 flex-col overflow-hidden", className),
|
|
15
|
+
...props
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
function MessageScrollerViewport({ className, ...props }) {
|
|
19
|
+
return /* @__PURE__ */ jsx(MessageScroller$1.Viewport, {
|
|
20
|
+
"data-slot": "message-scroller-viewport",
|
|
21
|
+
className: cn("size-full min-h-0 min-w-0 scroll-fade-b scrollbar-thin scrollbar-gutter-stable overflow-y-auto overscroll-contain contain-content data-autoscrolling:scrollbar-thumb-transparent data-autoscrolling:scrollbar-track-transparent", className),
|
|
22
|
+
...props
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function MessageScrollerContent({ className, ...props }) {
|
|
26
|
+
return /* @__PURE__ */ jsx(MessageScroller$1.Content, {
|
|
27
|
+
"data-slot": "message-scroller-content",
|
|
28
|
+
className: cn("flex h-max min-h-full flex-col gap-6", className),
|
|
29
|
+
...props
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
function MessageScrollerItem({ className, scrollAnchor = false, ...props }) {
|
|
33
|
+
return /* @__PURE__ */ jsx(MessageScroller$1.Item, {
|
|
34
|
+
"data-slot": "message-scroller-item",
|
|
35
|
+
scrollAnchor,
|
|
36
|
+
className: cn("min-w-0 shrink-0 [contain-intrinsic-size:auto_10rem] [content-visibility:auto]", className),
|
|
37
|
+
...props
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function MessageScrollerButton({ direction = "end", className, children, render, variant = "secondary", size = "sm", ...props }) {
|
|
41
|
+
return /* @__PURE__ */ jsx(MessageScroller$1.Button, {
|
|
42
|
+
"data-slot": "message-scroller-button",
|
|
43
|
+
"data-direction": direction,
|
|
44
|
+
"data-variant": variant,
|
|
45
|
+
"data-size": size,
|
|
46
|
+
direction,
|
|
47
|
+
className: cn("absolute inset-s-1/2 -translate-x-1/2 border-border bg-background text-foreground transition-[translate,scale,opacity] duration-200 hover:bg-muted hover:text-foreground in-data-[slot=card-content]:bg-card in-data-[slot=dialog-content]:bg-popover in-data-[slot=popover-content]:bg-popover in-data-[slot=sheet-content]:bg-popover data-[active=false]:pointer-events-none data-[active=false]:scale-95 data-[active=false]:opacity-0 data-[active=false]:duration-400 data-[active=false]:ease-[cubic-bezier(0.7,0,0.84,0)] data-[active=true]:translate-y-0 data-[active=true]:scale-100 data-[active=true]:opacity-100 data-[active=true]:ease-[cubic-bezier(0.23,1,0.32,1)] data-[direction=end]:bottom-4 data-[direction=end]:data-[active=false]:translate-y-full data-[direction=start]:top-4 data-[direction=start]:data-[active=false]:-translate-y-full rtl:translate-x-1/2 data-[direction=start]:[&_svg]:rotate-180", className),
|
|
48
|
+
render: render ?? /* @__PURE__ */ jsx(Button, {
|
|
49
|
+
variant,
|
|
50
|
+
size,
|
|
51
|
+
iconOnly: true,
|
|
52
|
+
"aria-label": direction === "end" ? "Scroll to end" : "Scroll to start"
|
|
53
|
+
}),
|
|
54
|
+
...props,
|
|
55
|
+
children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(ArrowDownIcon, {}), /* @__PURE__ */ jsx("span", {
|
|
56
|
+
className: "sr-only",
|
|
57
|
+
children: direction === "end" ? "Scroll to end" : "Scroll to start"
|
|
58
|
+
})] })
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* WHEN TO USE
|
|
63
|
+
* - Chat transcripts, live logs, or any list where new content should keep the newest item in view
|
|
64
|
+
* - Streaming or incrementally-appended content
|
|
65
|
+
*
|
|
66
|
+
* NOT WHEN
|
|
67
|
+
* - Static, non-growing lists — use ScrollArea
|
|
68
|
+
* - Feeds anchored to the top (e.g. paginated results) — this only pins to one end anchor
|
|
69
|
+
*
|
|
70
|
+
* KEY RULES
|
|
71
|
+
* - Requires MessageScrollerProvider as an ancestor — MessageScroller does nothing useful without it
|
|
72
|
+
* - Exactly one MessageScrollerItem should carry `scrollAnchor` (usually the last rendered item) — that's what the viewport pins to and what the jump button targets
|
|
73
|
+
* - MessageScrollerButton `direction`: "end" (default, jump to newest) or "start"
|
|
74
|
+
* - useMessageScroller / useMessageScrollerScrollable / useMessageScrollerVisibility expose scroll state for custom UI — reach for these before wiring a manual scroll listener
|
|
75
|
+
*
|
|
76
|
+
* USAGE
|
|
77
|
+
* <MessageScrollerProvider>
|
|
78
|
+
* <MessageScroller className="h-80 rounded-xl border">
|
|
79
|
+
* <MessageScrollerViewport>
|
|
80
|
+
* <MessageScrollerContent>
|
|
81
|
+
* {messages.map((m, i) => (
|
|
82
|
+
* <MessageScrollerItem key={m.id} scrollAnchor={i === messages.length - 1}>
|
|
83
|
+
* <Message align={m.align}>{m.body}</Message>
|
|
84
|
+
* </MessageScrollerItem>
|
|
85
|
+
* ))}
|
|
86
|
+
* </MessageScrollerContent>
|
|
87
|
+
* </MessageScrollerViewport>
|
|
88
|
+
* <MessageScrollerButton direction="end" />
|
|
89
|
+
* </MessageScroller>
|
|
90
|
+
* </MessageScrollerProvider>
|
|
91
|
+
*/
|
|
92
|
+
//#endregion
|
|
93
|
+
export { MessageScroller, MessageScrollerButton, MessageScrollerContent, MessageScrollerItem, MessageScrollerProvider, MessageScrollerViewport, useMessageScroller, useMessageScrollerScrollable, useMessageScrollerVisibility };
|
|
94
|
+
|
|
95
|
+
//# sourceMappingURL=message-scroller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-scroller.js","names":[],"sources":["../../../src/components/ui/message-scroller.tsx"],"sourcesContent":["/**\n * MessageScroller — auto-sticking scroll container for chat transcripts and log streams.\n *\n * Pins to the newest item by default and reveals a jump-to-latest\n * MessageScrollerButton once the user scrolls away from it. Wraps the\n * `@shadcn/react` message-scroller primitive. Render Message or Bubble\n * items inside MessageScrollerItem; mark the item the viewport should pin\n * to with `scrollAnchor` (typically the last item).\n */\n\nimport * as React from \"react\"\nimport {\n MessageScroller as MessageScrollerPrimitive,\n useMessageScroller,\n useMessageScrollerScrollable,\n useMessageScrollerVisibility,\n} from \"@shadcn/react/message-scroller\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Button, type TextButtonSize } from \"@/components/ui/button\"\nimport { ArrowDownIcon } from \"lucide-react\"\n\nfunction MessageScrollerProvider(\n props: React.ComponentProps<typeof MessageScrollerPrimitive.Provider>\n) {\n return <MessageScrollerPrimitive.Provider {...props} />\n}\n\nfunction MessageScroller({\n className,\n ...props\n}: React.ComponentProps<typeof MessageScrollerPrimitive.Root>) {\n return (\n <MessageScrollerPrimitive.Root\n data-slot=\"message-scroller\"\n className={cn(\n \"group/message-scroller relative flex size-full min-h-0 flex-col overflow-hidden\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction MessageScrollerViewport({\n className,\n ...props\n}: React.ComponentProps<typeof MessageScrollerPrimitive.Viewport>) {\n return (\n <MessageScrollerPrimitive.Viewport\n data-slot=\"message-scroller-viewport\"\n className={cn(\n \"size-full min-h-0 min-w-0 scroll-fade-b scrollbar-thin scrollbar-gutter-stable overflow-y-auto overscroll-contain contain-content data-autoscrolling:scrollbar-thumb-transparent data-autoscrolling:scrollbar-track-transparent\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction MessageScrollerContent({\n className,\n ...props\n}: React.ComponentProps<typeof MessageScrollerPrimitive.Content>) {\n return (\n <MessageScrollerPrimitive.Content\n data-slot=\"message-scroller-content\"\n className={cn(\"flex h-max min-h-full flex-col gap-6\", className)}\n {...props}\n />\n )\n}\n\nfunction MessageScrollerItem({\n className,\n scrollAnchor = false,\n ...props\n}: React.ComponentProps<typeof MessageScrollerPrimitive.Item>) {\n return (\n <MessageScrollerPrimitive.Item\n data-slot=\"message-scroller-item\"\n scrollAnchor={scrollAnchor}\n className={cn(\n \"min-w-0 shrink-0 [contain-intrinsic-size:auto_10rem] [content-visibility:auto]\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction MessageScrollerButton({\n direction = \"end\",\n className,\n children,\n render,\n variant = \"secondary\",\n size = \"sm\",\n ...props\n}: React.ComponentProps<typeof MessageScrollerPrimitive.Button> &\n Pick<React.ComponentProps<typeof Button>, \"variant\"> & {\n size?: TextButtonSize\n }) {\n return (\n <MessageScrollerPrimitive.Button\n data-slot=\"message-scroller-button\"\n data-direction={direction}\n data-variant={variant}\n data-size={size}\n direction={direction}\n className={cn(\n \"absolute inset-s-1/2 -translate-x-1/2 border-border bg-background text-foreground transition-[translate,scale,opacity] duration-200 hover:bg-muted hover:text-foreground in-data-[slot=card-content]:bg-card in-data-[slot=dialog-content]:bg-popover in-data-[slot=popover-content]:bg-popover in-data-[slot=sheet-content]:bg-popover data-[active=false]:pointer-events-none data-[active=false]:scale-95 data-[active=false]:opacity-0 data-[active=false]:duration-400 data-[active=false]:ease-[cubic-bezier(0.7,0,0.84,0)] data-[active=true]:translate-y-0 data-[active=true]:scale-100 data-[active=true]:opacity-100 data-[active=true]:ease-[cubic-bezier(0.23,1,0.32,1)] data-[direction=end]:bottom-4 data-[direction=end]:data-[active=false]:translate-y-full data-[direction=start]:top-4 data-[direction=start]:data-[active=false]:-translate-y-full rtl:translate-x-1/2 data-[direction=start]:[&_svg]:rotate-180\",\n className\n )}\n render={\n render ?? (\n <Button\n variant={variant}\n size={size}\n iconOnly\n aria-label={\n direction === \"end\" ? \"Scroll to end\" : \"Scroll to start\"\n }\n />\n )\n }\n {...props}\n >\n {children ?? (\n <>\n <ArrowDownIcon />\n <span className=\"sr-only\">\n {direction === \"end\" ? \"Scroll to end\" : \"Scroll to start\"}\n </span>\n </>\n )}\n </MessageScrollerPrimitive.Button>\n )\n}\n\nexport {\n MessageScrollerProvider,\n MessageScroller,\n MessageScrollerViewport,\n MessageScrollerContent,\n MessageScrollerItem,\n MessageScrollerButton,\n useMessageScroller,\n useMessageScrollerScrollable,\n useMessageScrollerVisibility,\n}\n\n/**\n * WHEN TO USE\n * - Chat transcripts, live logs, or any list where new content should keep the newest item in view\n * - Streaming or incrementally-appended content\n *\n * NOT WHEN\n * - Static, non-growing lists — use ScrollArea\n * - Feeds anchored to the top (e.g. paginated results) — this only pins to one end anchor\n *\n * KEY RULES\n * - Requires MessageScrollerProvider as an ancestor — MessageScroller does nothing useful without it\n * - Exactly one MessageScrollerItem should carry `scrollAnchor` (usually the last rendered item) — that's what the viewport pins to and what the jump button targets\n * - MessageScrollerButton `direction`: \"end\" (default, jump to newest) or \"start\"\n * - useMessageScroller / useMessageScrollerScrollable / useMessageScrollerVisibility expose scroll state for custom UI — reach for these before wiring a manual scroll listener\n *\n * USAGE\n * <MessageScrollerProvider>\n * <MessageScroller className=\"h-80 rounded-xl border\">\n * <MessageScrollerViewport>\n * <MessageScrollerContent>\n * {messages.map((m, i) => (\n * <MessageScrollerItem key={m.id} scrollAnchor={i === messages.length - 1}>\n * <Message align={m.align}>{m.body}</Message>\n * </MessageScrollerItem>\n * ))}\n * </MessageScrollerContent>\n * </MessageScrollerViewport>\n * <MessageScrollerButton direction=\"end\" />\n * </MessageScroller>\n * </MessageScrollerProvider>\n */\n"],"mappings":";;;;;;;AAsBA,SAAS,wBACP,OACA;CACA,OAAO,oBAAC,kBAAyB,UAA1B,EAAmC,GAAI,MAAQ,CAAA;AACxD;AAEA,SAAS,gBAAgB,EACvB,WACA,GAAG,SAC0D;CAC7D,OACE,oBAAC,kBAAyB,MAA1B;EACE,aAAU;EACV,WAAW,GACT,mFACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,wBAAwB,EAC/B,WACA,GAAG,SAC8D;CACjE,OACE,oBAAC,kBAAyB,UAA1B;EACE,aAAU;EACV,WAAW,GACT,mOACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,uBAAuB,EAC9B,WACA,GAAG,SAC6D;CAChE,OACE,oBAAC,kBAAyB,SAA1B;EACE,aAAU;EACV,WAAW,GAAG,wCAAwC,SAAS;EAC/D,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,oBAAoB,EAC3B,WACA,eAAe,OACf,GAAG,SAC0D;CAC7D,OACE,oBAAC,kBAAyB,MAA1B;EACE,aAAU;EACI;EACd,WAAW,GACT,kFACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,sBAAsB,EAC7B,YAAY,OACZ,WACA,UACA,QACA,UAAU,aACV,OAAO,MACP,GAAG,SAIA;CACH,OACE,oBAAC,kBAAyB,QAA1B;EACE,aAAU;EACV,kBAAgB;EAChB,gBAAc;EACd,aAAW;EACA;EACX,WAAW,GACT,w4BACA,SACF;EACA,QACE,UACE,oBAAC,QAAD;GACW;GACH;GACN,UAAA;GACA,cACE,cAAc,QAAQ,kBAAkB;EAE3C,CAAA;EAGL,GAAI;EAEH,UAAA,YACC,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,eAAD,CAAgB,CAAA,GAChB,oBAAC,QAAD;GAAM,WAAU;GACb,UAAA,cAAc,QAAQ,kBAAkB;EACrC,CAAA,CACN,EAAA,CAAA;CAE2B,CAAA;AAErC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message — structured chat message with avatar, author, and timestamp slots.
|
|
3
|
+
*
|
|
4
|
+
* MessageAvatar, MessageHeader (author), MessageContent (body), and
|
|
5
|
+
* MessageFooter (timestamp) compose around a shared `align` value.
|
|
6
|
+
* MessageGroup stacks a thread. MessageContent also self-aligns any direct
|
|
7
|
+
* data-slot child when `align="end"`, so a Bubble can be dropped inside it to
|
|
8
|
+
* get avatar/header/footer chrome around a colored bubble surface. For long
|
|
9
|
+
* or streaming threads that must stay pinned to the newest message, wrap the
|
|
10
|
+
* list in MessageScroller instead of a plain scrolling div.
|
|
11
|
+
*/
|
|
12
|
+
import * as React from "react";
|
|
13
|
+
declare function MessageGroup({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
14
|
+
declare function Message({ className, align, ...props }: React.ComponentProps<"div"> & {
|
|
15
|
+
align?: "start" | "end";
|
|
16
|
+
}): React.JSX.Element;
|
|
17
|
+
declare function MessageAvatar({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
18
|
+
declare function MessageContent({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
19
|
+
declare function MessageHeader({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
20
|
+
declare function MessageFooter({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
21
|
+
export { MessageGroup, Message, MessageAvatar, MessageContent, MessageFooter, MessageHeader, };
|
|
22
|
+
/**
|
|
23
|
+
* WHEN TO USE
|
|
24
|
+
* - Chat threads where each turn needs an avatar, author name, and/or timestamp
|
|
25
|
+
* - Multi-participant conversations where "who said this" must be visible
|
|
26
|
+
*
|
|
27
|
+
* NOT WHEN
|
|
28
|
+
* - Bare speech bubbles with no metadata — use Bubble
|
|
29
|
+
* - Long scrolling threads that must auto-stick to the newest message — wrap in MessageScroller rather than a plain overflow-y-auto container
|
|
30
|
+
*
|
|
31
|
+
* KEY RULES
|
|
32
|
+
* - align: "start" (default) or "end" — reverses the avatar/content order via flex-row-reverse
|
|
33
|
+
* - Every slot is optional: omit MessageAvatar/MessageHeader/MessageFooter freely per message
|
|
34
|
+
* - MessageAvatar is `self-start` — it aligns with the first content line (the bubble/text), not the last (a MessageFooter timestamp), regardless of gap or footer text length
|
|
35
|
+
* - MessageGroup only provides gap-2 stacking — no scrolling or auto-anchoring of its own
|
|
36
|
+
* - MessageAvatar is for group threads specifically — once there are 3+ possible senders,
|
|
37
|
+
* it's what says who's speaking. A 1:1 conversation doesn't need it: the container
|
|
38
|
+
* already tells the user who the other party is, and `align` alone already distinguishes
|
|
39
|
+
* your own messages from theirs — omit MessageAvatar (and MessageHeader) entirely there
|
|
40
|
+
* - Per-message hover actions (e.g. a "Copy"/feedback row under an AI reply) are a plain
|
|
41
|
+
* composition, not a dedicated slot: give the action row `opacity-0
|
|
42
|
+
* group-hover/message:opacity-100 group-focus-within/message:opacity-100` — Message's own
|
|
43
|
+
* root already carries `group/message`, so no extra wrapper is needed
|
|
44
|
+
*
|
|
45
|
+
* USAGE
|
|
46
|
+
* <MessageGroup>
|
|
47
|
+
* <Message align="start">
|
|
48
|
+
* <MessageAvatar><Avatar><AvatarFallback>AL</AvatarFallback></Avatar></MessageAvatar>
|
|
49
|
+
* <MessageContent>
|
|
50
|
+
* <MessageHeader>Ada Lovelace</MessageHeader>
|
|
51
|
+
* Where should brand overrides live?
|
|
52
|
+
* <MessageFooter>9:41 AM</MessageFooter>
|
|
53
|
+
* </MessageContent>
|
|
54
|
+
* </Message>
|
|
55
|
+
* <Message align="end">
|
|
56
|
+
* <MessageContent>
|
|
57
|
+
* <MessageHeader>You</MessageHeader>
|
|
58
|
+
* In theme.css — it is the single source of truth for the theme.
|
|
59
|
+
* </MessageContent>
|
|
60
|
+
* </Message>
|
|
61
|
+
* </MessageGroup>
|
|
62
|
+
*/
|
|
63
|
+
//# sourceMappingURL=message.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message.d.ts","sourceRoot":"","sources":["../../../src/components/ui/message.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,iBAAS,YAAY,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAQzE;AAED,iBAAS,OAAO,CAAC,EACf,SAAS,EACT,KAAe,EACf,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG;IAAE,KAAK,CAAC,EAAE,OAAO,GAAG,KAAK,CAAA;CAAE,qBAY3D;AAED,iBAAS,aAAa,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAwB1E;AAED,iBAAS,cAAc,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAW3E;AAED,iBAAS,aAAa,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAW1E;AAED,iBAAS,aAAa,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAW1E;AAED,OAAO,EACL,YAAY,EACZ,OAAO,EACP,aAAa,EACb,cAAc,EACd,aAAa,EACb,aAAa,GACd,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { cn } from "../../lib/utils.js";
|
|
2
|
+
import "react";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
//#region src/components/ui/message.tsx
|
|
5
|
+
function MessageGroup({ className, ...props }) {
|
|
6
|
+
return /* @__PURE__ */ jsx("div", {
|
|
7
|
+
"data-slot": "message-group",
|
|
8
|
+
className: cn("flex min-w-0 flex-col gap-2", className),
|
|
9
|
+
...props
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
function Message({ className, align = "start", ...props }) {
|
|
13
|
+
return /* @__PURE__ */ jsx("div", {
|
|
14
|
+
"data-slot": "message",
|
|
15
|
+
"data-align": align,
|
|
16
|
+
className: cn("group/message relative flex w-full min-w-0 gap-2 text-sm data-[align=end]:flex-row-reverse", className),
|
|
17
|
+
...props
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
function MessageAvatar({ className, ...props }) {
|
|
21
|
+
return /* @__PURE__ */ jsx("div", {
|
|
22
|
+
"data-slot": "message-avatar",
|
|
23
|
+
className: cn("flex w-fit shrink-0 items-center justify-center self-start overflow-hidden rounded-full", className),
|
|
24
|
+
...props
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function MessageContent({ className, ...props }) {
|
|
28
|
+
return /* @__PURE__ */ jsx("div", {
|
|
29
|
+
"data-slot": "message-content",
|
|
30
|
+
className: cn("flex w-full min-w-0 flex-col gap-2.5 wrap-break-word group-data-[align=end]/message:*:data-slot:self-end", className),
|
|
31
|
+
...props
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
function MessageHeader({ className, ...props }) {
|
|
35
|
+
return /* @__PURE__ */ jsx("div", {
|
|
36
|
+
"data-slot": "message-header",
|
|
37
|
+
className: cn("flex max-w-full min-w-0 items-center px-3 text-xs font-medium text-muted-foreground group-has-data-[emphasis=ghost]/message:px-0", className),
|
|
38
|
+
...props
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
function MessageFooter({ className, ...props }) {
|
|
42
|
+
return /* @__PURE__ */ jsx("div", {
|
|
43
|
+
"data-slot": "message-footer",
|
|
44
|
+
className: cn("flex max-w-full min-w-0 items-center px-3 text-xs font-medium text-muted-foreground group-has-data-[emphasis=ghost]/message:px-0 group-data-[align=end]/message:justify-end", className),
|
|
45
|
+
...props
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* WHEN TO USE
|
|
50
|
+
* - Chat threads where each turn needs an avatar, author name, and/or timestamp
|
|
51
|
+
* - Multi-participant conversations where "who said this" must be visible
|
|
52
|
+
*
|
|
53
|
+
* NOT WHEN
|
|
54
|
+
* - Bare speech bubbles with no metadata — use Bubble
|
|
55
|
+
* - Long scrolling threads that must auto-stick to the newest message — wrap in MessageScroller rather than a plain overflow-y-auto container
|
|
56
|
+
*
|
|
57
|
+
* KEY RULES
|
|
58
|
+
* - align: "start" (default) or "end" — reverses the avatar/content order via flex-row-reverse
|
|
59
|
+
* - Every slot is optional: omit MessageAvatar/MessageHeader/MessageFooter freely per message
|
|
60
|
+
* - MessageAvatar is `self-start` — it aligns with the first content line (the bubble/text), not the last (a MessageFooter timestamp), regardless of gap or footer text length
|
|
61
|
+
* - MessageGroup only provides gap-2 stacking — no scrolling or auto-anchoring of its own
|
|
62
|
+
* - MessageAvatar is for group threads specifically — once there are 3+ possible senders,
|
|
63
|
+
* it's what says who's speaking. A 1:1 conversation doesn't need it: the container
|
|
64
|
+
* already tells the user who the other party is, and `align` alone already distinguishes
|
|
65
|
+
* your own messages from theirs — omit MessageAvatar (and MessageHeader) entirely there
|
|
66
|
+
* - Per-message hover actions (e.g. a "Copy"/feedback row under an AI reply) are a plain
|
|
67
|
+
* composition, not a dedicated slot: give the action row `opacity-0
|
|
68
|
+
* group-hover/message:opacity-100 group-focus-within/message:opacity-100` — Message's own
|
|
69
|
+
* root already carries `group/message`, so no extra wrapper is needed
|
|
70
|
+
*
|
|
71
|
+
* USAGE
|
|
72
|
+
* <MessageGroup>
|
|
73
|
+
* <Message align="start">
|
|
74
|
+
* <MessageAvatar><Avatar><AvatarFallback>AL</AvatarFallback></Avatar></MessageAvatar>
|
|
75
|
+
* <MessageContent>
|
|
76
|
+
* <MessageHeader>Ada Lovelace</MessageHeader>
|
|
77
|
+
* Where should brand overrides live?
|
|
78
|
+
* <MessageFooter>9:41 AM</MessageFooter>
|
|
79
|
+
* </MessageContent>
|
|
80
|
+
* </Message>
|
|
81
|
+
* <Message align="end">
|
|
82
|
+
* <MessageContent>
|
|
83
|
+
* <MessageHeader>You</MessageHeader>
|
|
84
|
+
* In theme.css — it is the single source of truth for the theme.
|
|
85
|
+
* </MessageContent>
|
|
86
|
+
* </Message>
|
|
87
|
+
* </MessageGroup>
|
|
88
|
+
*/
|
|
89
|
+
//#endregion
|
|
90
|
+
export { Message, MessageAvatar, MessageContent, MessageFooter, MessageGroup, MessageHeader };
|
|
91
|
+
|
|
92
|
+
//# sourceMappingURL=message.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message.js","names":[],"sources":["../../../src/components/ui/message.tsx"],"sourcesContent":["/**\n * Message — structured chat message with avatar, author, and timestamp slots.\n *\n * MessageAvatar, MessageHeader (author), MessageContent (body), and\n * MessageFooter (timestamp) compose around a shared `align` value.\n * MessageGroup stacks a thread. MessageContent also self-aligns any direct\n * data-slot child when `align=\"end\"`, so a Bubble can be dropped inside it to\n * get avatar/header/footer chrome around a colored bubble surface. For long\n * or streaming threads that must stay pinned to the newest message, wrap the\n * list in MessageScroller instead of a plain scrolling div.\n */\n\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nfunction MessageGroup({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"message-group\"\n className={cn(\"flex min-w-0 flex-col gap-2\", className)}\n {...props}\n />\n )\n}\n\nfunction Message({\n className,\n align = \"start\",\n ...props\n}: React.ComponentProps<\"div\"> & { align?: \"start\" | \"end\" }) {\n return (\n <div\n data-slot=\"message\"\n data-align={align}\n className={cn(\n \"group/message relative flex w-full min-w-0 gap-2 text-sm data-[align=end]:flex-row-reverse\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction MessageAvatar({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"message-avatar\"\n // self-start, not self-end + a fixed -translate-y-8 correction — that\n // bottom-align-then-shift-up-by-a-magic-number approach only lined up\n // with the default MessageContent gap it was tuned against; tighten\n // that gap (e.g. a P2P chat's bubble-to-timestamp spacing) and the\n // fixed offset overshoots, pushing the avatar above the bubble's own\n // top. True top-alignment needs no calibration at all — it's correct\n // for any gap, any footer text length, any bubble height.\n // No min-w-8/bg-muted of its own — those were sized for the default\n // 32px Avatar specifically; a smaller Avatar (size=\"sm\", 24px) left\n // a visible gray halo, the wrapper's own fill and forced minimum\n // width showing past the smaller circle inside it. The Avatar\n // already carries its own size and fallback background — this is\n // purely a layout/alignment container, not a second surface.\n className={cn(\n \"flex w-fit shrink-0 items-center justify-center self-start overflow-hidden rounded-full\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction MessageContent({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"message-content\"\n className={cn(\n \"flex w-full min-w-0 flex-col gap-2.5 wrap-break-word group-data-[align=end]/message:*:data-slot:self-end\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction MessageHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"message-header\"\n className={cn(\n \"flex max-w-full min-w-0 items-center px-3 text-xs font-medium text-muted-foreground group-has-data-[emphasis=ghost]/message:px-0\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction MessageFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"message-footer\"\n className={cn(\n \"flex max-w-full min-w-0 items-center px-3 text-xs font-medium text-muted-foreground group-has-data-[emphasis=ghost]/message:px-0 group-data-[align=end]/message:justify-end\",\n className\n )}\n {...props}\n />\n )\n}\n\nexport {\n MessageGroup,\n Message,\n MessageAvatar,\n MessageContent,\n MessageFooter,\n MessageHeader,\n}\n\n/**\n * WHEN TO USE\n * - Chat threads where each turn needs an avatar, author name, and/or timestamp\n * - Multi-participant conversations where \"who said this\" must be visible\n *\n * NOT WHEN\n * - Bare speech bubbles with no metadata — use Bubble\n * - Long scrolling threads that must auto-stick to the newest message — wrap in MessageScroller rather than a plain overflow-y-auto container\n *\n * KEY RULES\n * - align: \"start\" (default) or \"end\" — reverses the avatar/content order via flex-row-reverse\n * - Every slot is optional: omit MessageAvatar/MessageHeader/MessageFooter freely per message\n * - MessageAvatar is `self-start` — it aligns with the first content line (the bubble/text), not the last (a MessageFooter timestamp), regardless of gap or footer text length\n * - MessageGroup only provides gap-2 stacking — no scrolling or auto-anchoring of its own\n * - MessageAvatar is for group threads specifically — once there are 3+ possible senders,\n * it's what says who's speaking. A 1:1 conversation doesn't need it: the container\n * already tells the user who the other party is, and `align` alone already distinguishes\n * your own messages from theirs — omit MessageAvatar (and MessageHeader) entirely there\n * - Per-message hover actions (e.g. a \"Copy\"/feedback row under an AI reply) are a plain\n * composition, not a dedicated slot: give the action row `opacity-0\n * group-hover/message:opacity-100 group-focus-within/message:opacity-100` — Message's own\n * root already carries `group/message`, so no extra wrapper is needed\n *\n * USAGE\n * <MessageGroup>\n * <Message align=\"start\">\n * <MessageAvatar><Avatar><AvatarFallback>AL</AvatarFallback></Avatar></MessageAvatar>\n * <MessageContent>\n * <MessageHeader>Ada Lovelace</MessageHeader>\n * Where should brand overrides live?\n * <MessageFooter>9:41 AM</MessageFooter>\n * </MessageContent>\n * </Message>\n * <Message align=\"end\">\n * <MessageContent>\n * <MessageHeader>You</MessageHeader>\n * In theme.css — it is the single source of truth for the theme.\n * </MessageContent>\n * </Message>\n * </MessageGroup>\n */\n"],"mappings":";;;;AAgBA,SAAS,aAAa,EAAE,WAAW,GAAG,SAAsC;CAC1E,OACE,oBAAC,OAAD;EACE,aAAU;EACV,WAAW,GAAG,+BAA+B,SAAS;EACtD,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,QAAQ,EACf,WACA,QAAQ,SACR,GAAG,SACyD;CAC5D,OACE,oBAAC,OAAD;EACE,aAAU;EACV,cAAY;EACZ,WAAW,GACT,8FACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,cAAc,EAAE,WAAW,GAAG,SAAsC;CAC3E,OACE,oBAAC,OAAD;EACE,aAAU;EAcV,WAAW,GACT,2FACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,eAAe,EAAE,WAAW,GAAG,SAAsC;CAC5E,OACE,oBAAC,OAAD;EACE,aAAU;EACV,WAAW,GACT,4GACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,cAAc,EAAE,WAAW,GAAG,SAAsC;CAC3E,OACE,oBAAC,OAAD;EACE,aAAU;EACV,WAAW,GACT,oIACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,cAAc,EAAE,WAAW,GAAG,SAAsC;CAC3E,OACE,oBAAC,OAAD;EACE,aAAU;EACV,WAAW,GACT,+KACA,SACF;EACA,GAAI;CACL,CAAA;AAEL"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MetaList — a short horizontal list of secondary/meta information, items
|
|
3
|
+
* separated by a dot.
|
|
4
|
+
*
|
|
5
|
+
* `MetaList` is the flex row;
|
|
6
|
+
* `MetaListItem` is one entry, auto-separated from the previous item by a
|
|
7
|
+
* `·` unless it's first. Plain text/nodes instead of `MetaListItem` render
|
|
8
|
+
* with no separator, which is how a consumer opts out of the dot.
|
|
9
|
+
*/
|
|
10
|
+
import * as React from "react";
|
|
11
|
+
import { type VariantProps } from "class-variance-authority";
|
|
12
|
+
declare const metaListVariants: (props?: ({
|
|
13
|
+
size?: "sm" | "default" | null | undefined;
|
|
14
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
15
|
+
declare function MetaList({ className, size, ...props }: React.ComponentProps<"div"> & VariantProps<typeof metaListVariants>): React.JSX.Element;
|
|
16
|
+
declare function MetaListItem({ className, ...props }: React.ComponentProps<"span">): React.JSX.Element;
|
|
17
|
+
declare function MetaListIcon({ className, ...props }: React.ComponentProps<"span">): React.JSX.Element;
|
|
18
|
+
export { MetaList, MetaListItem, MetaListIcon, metaListVariants };
|
|
19
|
+
/**
|
|
20
|
+
* WHEN TO USE
|
|
21
|
+
* - Secondary information below a PageHeaderTitle or inside a table cell —
|
|
22
|
+
* e.g. "3 min read · 5 comments · Draft".
|
|
23
|
+
*
|
|
24
|
+
* NOT WHEN
|
|
25
|
+
* - Primary content or anything interactive — MetaList is a plain read-only
|
|
26
|
+
* row, not a set of links or actions.
|
|
27
|
+
* - A single piece of metadata with no list — just render the text.
|
|
28
|
+
*
|
|
29
|
+
* KEY RULES
|
|
30
|
+
* - size: default | sm (default "default") — regular is text-sm, sm is text-xs; both inherit
|
|
31
|
+
* text-muted-foreground.
|
|
32
|
+
* - Every MetaListItem after the first renders a `·` separator automatically — there's no
|
|
33
|
+
* separator prop. Mixing in plain children (not wrapped in MetaListItem) skips the dot for
|
|
34
|
+
* that entry.
|
|
35
|
+
* - MetaListIcon is an optional leading glyph inside an item, aria-hidden — pair it with real
|
|
36
|
+
* text in the item, not on its own.
|
|
37
|
+
* - No padding is baked in — this is a plain inline row; add spacing via the parent (e.g.
|
|
38
|
+
* PageHeaderContent's own gap) rather than on MetaList itself.
|
|
39
|
+
*
|
|
40
|
+
* USAGE
|
|
41
|
+
* <MetaList>
|
|
42
|
+
* <MetaListItem>3 min read</MetaListItem>
|
|
43
|
+
* <MetaListItem>5 comments</MetaListItem>
|
|
44
|
+
* <MetaListItem>Draft</MetaListItem>
|
|
45
|
+
* </MetaList>
|
|
46
|
+
*
|
|
47
|
+
* <MetaList size="sm">
|
|
48
|
+
* <MetaListItem>
|
|
49
|
+
* <MetaListIcon><ClockIcon /></MetaListIcon>
|
|
50
|
+
* Updated 2h ago
|
|
51
|
+
* </MetaListItem>
|
|
52
|
+
* <MetaListItem>James Wilson</MetaListItem>
|
|
53
|
+
* </MetaList>
|
|
54
|
+
*/
|
|
55
|
+
//# sourceMappingURL=meta-list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meta-list.d.ts","sourceRoot":"","sources":["../../../src/components/ui/meta-list.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAIjE,QAAA,MAAM,gBAAgB;;8EAarB,CAAA;AAED,iBAAS,QAAQ,CAAC,EAChB,SAAS,EACT,IAAgB,EAChB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG,YAAY,CAAC,OAAO,gBAAgB,CAAC,qBASrE;AAED,iBAAS,YAAY,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,qBAW1E;AAED,iBAAS,YAAY,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,qBAY1E;AAED,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,CAAA;AAEjE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { cn } from "../../lib/utils.js";
|
|
2
|
+
import "react";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
import { cva } from "class-variance-authority";
|
|
5
|
+
//#region src/components/ui/meta-list.tsx
|
|
6
|
+
var metaListVariants = cva("flex flex-wrap items-center text-muted-foreground", {
|
|
7
|
+
variants: { size: {
|
|
8
|
+
default: "text-sm",
|
|
9
|
+
sm: "text-xs"
|
|
10
|
+
} },
|
|
11
|
+
defaultVariants: { size: "default" }
|
|
12
|
+
});
|
|
13
|
+
function MetaList({ className, size = "default", ...props }) {
|
|
14
|
+
return /* @__PURE__ */ jsx("div", {
|
|
15
|
+
"data-slot": "meta-list",
|
|
16
|
+
"data-size": size,
|
|
17
|
+
className: cn(metaListVariants({ size }), className),
|
|
18
|
+
...props
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function MetaListItem({ className, ...props }) {
|
|
22
|
+
return /* @__PURE__ */ jsx("span", {
|
|
23
|
+
"data-slot": "meta-list-item",
|
|
24
|
+
className: cn("inline-flex items-center gap-1 [&:not(:first-child)]:before:mx-1.5 [&:not(:first-child)]:before:content-['•']", className),
|
|
25
|
+
...props
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function MetaListIcon({ className, ...props }) {
|
|
29
|
+
return /* @__PURE__ */ jsx("span", {
|
|
30
|
+
"data-slot": "meta-list-icon",
|
|
31
|
+
"aria-hidden": "true",
|
|
32
|
+
className: cn("size-3.5 shrink-0 [&_svg:not([class*='size-'])]:size-3.5", className),
|
|
33
|
+
...props
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* WHEN TO USE
|
|
38
|
+
* - Secondary information below a PageHeaderTitle or inside a table cell —
|
|
39
|
+
* e.g. "3 min read · 5 comments · Draft".
|
|
40
|
+
*
|
|
41
|
+
* NOT WHEN
|
|
42
|
+
* - Primary content or anything interactive — MetaList is a plain read-only
|
|
43
|
+
* row, not a set of links or actions.
|
|
44
|
+
* - A single piece of metadata with no list — just render the text.
|
|
45
|
+
*
|
|
46
|
+
* KEY RULES
|
|
47
|
+
* - size: default | sm (default "default") — regular is text-sm, sm is text-xs; both inherit
|
|
48
|
+
* text-muted-foreground.
|
|
49
|
+
* - Every MetaListItem after the first renders a `·` separator automatically — there's no
|
|
50
|
+
* separator prop. Mixing in plain children (not wrapped in MetaListItem) skips the dot for
|
|
51
|
+
* that entry.
|
|
52
|
+
* - MetaListIcon is an optional leading glyph inside an item, aria-hidden — pair it with real
|
|
53
|
+
* text in the item, not on its own.
|
|
54
|
+
* - No padding is baked in — this is a plain inline row; add spacing via the parent (e.g.
|
|
55
|
+
* PageHeaderContent's own gap) rather than on MetaList itself.
|
|
56
|
+
*
|
|
57
|
+
* USAGE
|
|
58
|
+
* <MetaList>
|
|
59
|
+
* <MetaListItem>3 min read</MetaListItem>
|
|
60
|
+
* <MetaListItem>5 comments</MetaListItem>
|
|
61
|
+
* <MetaListItem>Draft</MetaListItem>
|
|
62
|
+
* </MetaList>
|
|
63
|
+
*
|
|
64
|
+
* <MetaList size="sm">
|
|
65
|
+
* <MetaListItem>
|
|
66
|
+
* <MetaListIcon><ClockIcon /></MetaListIcon>
|
|
67
|
+
* Updated 2h ago
|
|
68
|
+
* </MetaListItem>
|
|
69
|
+
* <MetaListItem>James Wilson</MetaListItem>
|
|
70
|
+
* </MetaList>
|
|
71
|
+
*/
|
|
72
|
+
//#endregion
|
|
73
|
+
export { MetaList, MetaListIcon, MetaListItem, metaListVariants };
|
|
74
|
+
|
|
75
|
+
//# sourceMappingURL=meta-list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meta-list.js","names":[],"sources":["../../../src/components/ui/meta-list.tsx"],"sourcesContent":["/**\n * MetaList — a short horizontal list of secondary/meta information, items\n * separated by a dot.\n *\n * `MetaList` is the flex row;\n * `MetaListItem` is one entry, auto-separated from the previous item by a\n * `·` unless it's first. Plain text/nodes instead of `MetaListItem` render\n * with no separator, which is how a consumer opts out of the dot.\n */\n\nimport * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst metaListVariants = cva(\n \"flex flex-wrap items-center text-muted-foreground\",\n {\n variants: {\n size: {\n default: \"text-sm\",\n sm: \"text-xs\",\n },\n },\n defaultVariants: {\n size: \"default\",\n },\n }\n)\n\nfunction MetaList({\n className,\n size = \"default\",\n ...props\n}: React.ComponentProps<\"div\"> & VariantProps<typeof metaListVariants>) {\n return (\n <div\n data-slot=\"meta-list\"\n data-size={size}\n className={cn(metaListVariants({ size }), className)}\n {...props}\n />\n )\n}\n\nfunction MetaListItem({ className, ...props }: React.ComponentProps<\"span\">) {\n return (\n <span\n data-slot=\"meta-list-item\"\n className={cn(\n \"inline-flex items-center gap-1 [&:not(:first-child)]:before:mx-1.5 [&:not(:first-child)]:before:content-['•']\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction MetaListIcon({ className, ...props }: React.ComponentProps<\"span\">) {\n return (\n <span\n data-slot=\"meta-list-icon\"\n aria-hidden=\"true\"\n className={cn(\n \"size-3.5 shrink-0 [&_svg:not([class*='size-'])]:size-3.5\",\n className\n )}\n {...props}\n />\n )\n}\n\nexport { MetaList, MetaListItem, MetaListIcon, metaListVariants }\n\n/**\n * WHEN TO USE\n * - Secondary information below a PageHeaderTitle or inside a table cell —\n * e.g. \"3 min read · 5 comments · Draft\".\n *\n * NOT WHEN\n * - Primary content or anything interactive — MetaList is a plain read-only\n * row, not a set of links or actions.\n * - A single piece of metadata with no list — just render the text.\n *\n * KEY RULES\n * - size: default | sm (default \"default\") — regular is text-sm, sm is text-xs; both inherit\n * text-muted-foreground.\n * - Every MetaListItem after the first renders a `·` separator automatically — there's no\n * separator prop. Mixing in plain children (not wrapped in MetaListItem) skips the dot for\n * that entry.\n * - MetaListIcon is an optional leading glyph inside an item, aria-hidden — pair it with real\n * text in the item, not on its own.\n * - No padding is baked in — this is a plain inline row; add spacing via the parent (e.g.\n * PageHeaderContent's own gap) rather than on MetaList itself.\n *\n * USAGE\n * <MetaList>\n * <MetaListItem>3 min read</MetaListItem>\n * <MetaListItem>5 comments</MetaListItem>\n * <MetaListItem>Draft</MetaListItem>\n * </MetaList>\n *\n * <MetaList size=\"sm\">\n * <MetaListItem>\n * <MetaListIcon><ClockIcon /></MetaListIcon>\n * Updated 2h ago\n * </MetaListItem>\n * <MetaListItem>James Wilson</MetaListItem>\n * </MetaList>\n */\n"],"mappings":";;;;;AAeA,IAAM,mBAAmB,IACvB,qDACA;CACE,UAAU,EACR,MAAM;EACJ,SAAS;EACT,IAAI;CACN,EACF;CACA,iBAAiB,EACf,MAAM,UACR;AACF,CACF;AAEA,SAAS,SAAS,EAChB,WACA,OAAO,WACP,GAAG,SACmE;CACtE,OACE,oBAAC,OAAD;EACE,aAAU;EACV,aAAW;EACX,WAAW,GAAG,iBAAiB,EAAE,KAAK,CAAC,GAAG,SAAS;EACnD,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,aAAa,EAAE,WAAW,GAAG,SAAuC;CAC3E,OACE,oBAAC,QAAD;EACE,aAAU;EACV,WAAW,GACT,iHACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,aAAa,EAAE,WAAW,GAAG,SAAuC;CAC3E,OACE,oBAAC,QAAD;EACE,aAAU;EACV,eAAY;EACZ,WAAW,GACT,4DACA,SACF;EACA,GAAI;CACL,CAAA;AAEL"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NativeSelect — styled wrapper around the browser's native <select>.
|
|
3
|
+
*
|
|
4
|
+
* A plain <select> with a chevron icon overlay, not a Radix primitive.
|
|
5
|
+
* Sizes ("sm" | "default") are applied via a `data-size`
|
|
6
|
+
* attribute on both the wrapper and the <select>.
|
|
7
|
+
*/
|
|
8
|
+
import * as React from "react";
|
|
9
|
+
type NativeSelectProps = Omit<React.ComponentProps<"select">, "size"> & {
|
|
10
|
+
size?: "sm" | "default";
|
|
11
|
+
};
|
|
12
|
+
declare function NativeSelect({ className, size, ...props }: NativeSelectProps): React.JSX.Element;
|
|
13
|
+
declare function NativeSelectOption({ className, ...props }: React.ComponentProps<"option">): React.JSX.Element;
|
|
14
|
+
declare function NativeSelectOptGroup({ className, ...props }: React.ComponentProps<"optgroup">): React.JSX.Element;
|
|
15
|
+
export { NativeSelect, NativeSelectOptGroup, NativeSelectOption };
|
|
16
|
+
/**
|
|
17
|
+
* WHEN TO USE
|
|
18
|
+
* - Simple option lists in dense forms where native OS picker behavior and zero extra JS are preferable.
|
|
19
|
+
* - Mobile-heavy flows — the OS supplies its own picker UI, which tends to be more reliable than a custom popover.
|
|
20
|
+
*
|
|
21
|
+
* NOT WHEN
|
|
22
|
+
* - Custom item rendering, icons, or grouped visual styling is required — use Select instead.
|
|
23
|
+
* - The list is long enough to need type-to-filter — use Combobox instead.
|
|
24
|
+
*
|
|
25
|
+
* KEY RULES
|
|
26
|
+
* - `size` is "sm" | "default" (default "default") — no other sizes exist.
|
|
27
|
+
* - Compose options with NativeSelectOption / NativeSelectOptGroup only — content is plain text, no custom item rendering.
|
|
28
|
+
* - Disabled state is native (`disabled` on the underlying <select>); the wrapper dims via has-[select:disabled]:opacity-50.
|
|
29
|
+
*
|
|
30
|
+
* USAGE
|
|
31
|
+
* <NativeSelect defaultValue="cardamom">
|
|
32
|
+
* <NativeSelectOption value="turmeric">Turmeric</NativeSelectOption>
|
|
33
|
+
* <NativeSelectOption value="cardamom">Cardamom</NativeSelectOption>
|
|
34
|
+
* </NativeSelect>
|
|
35
|
+
*
|
|
36
|
+
* // Grouped
|
|
37
|
+
* <NativeSelect defaultValue="cumin">
|
|
38
|
+
* <NativeSelectOptGroup label="Whole">
|
|
39
|
+
* <NativeSelectOption value="cardamom">Cardamom pod</NativeSelectOption>
|
|
40
|
+
* </NativeSelectOptGroup>
|
|
41
|
+
* <NativeSelectOptGroup label="Ground">
|
|
42
|
+
* <NativeSelectOption value="cumin">Cumin</NativeSelectOption>
|
|
43
|
+
* </NativeSelectOptGroup>
|
|
44
|
+
* </NativeSelect>
|
|
45
|
+
*
|
|
46
|
+
* // Small size
|
|
47
|
+
* <NativeSelect size="sm" defaultValue="turmeric">
|
|
48
|
+
* <NativeSelectOption value="turmeric">Turmeric</NativeSelectOption>
|
|
49
|
+
* </NativeSelect>
|
|
50
|
+
*/
|
|
51
|
+
//# sourceMappingURL=native-select.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native-select.d.ts","sourceRoot":"","sources":["../../../src/components/ui/native-select.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B,KAAK,iBAAiB,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,GAAG;IACtE,IAAI,CAAC,EAAE,IAAI,GAAG,SAAS,CAAA;CACxB,CAAA;AAED,iBAAS,YAAY,CAAC,EACpB,SAAS,EACT,IAAgB,EAChB,GAAG,KAAK,EACT,EAAE,iBAAiB,qBAuBnB;AAED,iBAAS,kBAAkB,CAAC,EAC1B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,qBAQhC;AAED,iBAAS,oBAAoB,CAAC,EAC5B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,UAAU,CAAC,qBAQlC;AAED,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,CAAA;AAEjE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { cn } from "../../lib/utils.js";
|
|
2
|
+
import "react";
|
|
3
|
+
import { ChevronDownIcon } from "lucide-react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/ui/native-select.tsx
|
|
6
|
+
function NativeSelect({ className, size = "default", ...props }) {
|
|
7
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
8
|
+
className: cn("group/native-select relative w-fit has-[select:disabled]:opacity-50", className),
|
|
9
|
+
"data-slot": "native-select-wrapper",
|
|
10
|
+
"data-size": size,
|
|
11
|
+
children: [/* @__PURE__ */ jsx("select", {
|
|
12
|
+
"data-slot": "native-select",
|
|
13
|
+
"data-size": size,
|
|
14
|
+
className: "h-8 w-full min-w-0 appearance-none rounded-lg border border-input bg-transparent py-1 pr-8 pl-2.5 text-sm transition-colors outline-none select-none selection:bg-primary selection:text-primary-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=sm]:h-7 data-[size=sm]:rounded-[min(var(--radius-md,0.375rem),10px)] data-[size=sm]:py-0.5 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
|
|
15
|
+
...props
|
|
16
|
+
}), /* @__PURE__ */ jsx(ChevronDownIcon, {
|
|
17
|
+
className: "pointer-events-none absolute top-1/2 right-2.5 size-4 -translate-y-1/2 text-muted-foreground select-none",
|
|
18
|
+
"aria-hidden": "true",
|
|
19
|
+
"data-slot": "native-select-icon"
|
|
20
|
+
})]
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
function NativeSelectOption({ className, ...props }) {
|
|
24
|
+
return /* @__PURE__ */ jsx("option", {
|
|
25
|
+
"data-slot": "native-select-option",
|
|
26
|
+
className: cn("bg-[Canvas] text-[CanvasText]", className),
|
|
27
|
+
...props
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function NativeSelectOptGroup({ className, ...props }) {
|
|
31
|
+
return /* @__PURE__ */ jsx("optgroup", {
|
|
32
|
+
"data-slot": "native-select-optgroup",
|
|
33
|
+
className: cn("bg-[Canvas] text-[CanvasText]", className),
|
|
34
|
+
...props
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* WHEN TO USE
|
|
39
|
+
* - Simple option lists in dense forms where native OS picker behavior and zero extra JS are preferable.
|
|
40
|
+
* - Mobile-heavy flows — the OS supplies its own picker UI, which tends to be more reliable than a custom popover.
|
|
41
|
+
*
|
|
42
|
+
* NOT WHEN
|
|
43
|
+
* - Custom item rendering, icons, or grouped visual styling is required — use Select instead.
|
|
44
|
+
* - The list is long enough to need type-to-filter — use Combobox instead.
|
|
45
|
+
*
|
|
46
|
+
* KEY RULES
|
|
47
|
+
* - `size` is "sm" | "default" (default "default") — no other sizes exist.
|
|
48
|
+
* - Compose options with NativeSelectOption / NativeSelectOptGroup only — content is plain text, no custom item rendering.
|
|
49
|
+
* - Disabled state is native (`disabled` on the underlying <select>); the wrapper dims via has-[select:disabled]:opacity-50.
|
|
50
|
+
*
|
|
51
|
+
* USAGE
|
|
52
|
+
* <NativeSelect defaultValue="cardamom">
|
|
53
|
+
* <NativeSelectOption value="turmeric">Turmeric</NativeSelectOption>
|
|
54
|
+
* <NativeSelectOption value="cardamom">Cardamom</NativeSelectOption>
|
|
55
|
+
* </NativeSelect>
|
|
56
|
+
*
|
|
57
|
+
* // Grouped
|
|
58
|
+
* <NativeSelect defaultValue="cumin">
|
|
59
|
+
* <NativeSelectOptGroup label="Whole">
|
|
60
|
+
* <NativeSelectOption value="cardamom">Cardamom pod</NativeSelectOption>
|
|
61
|
+
* </NativeSelectOptGroup>
|
|
62
|
+
* <NativeSelectOptGroup label="Ground">
|
|
63
|
+
* <NativeSelectOption value="cumin">Cumin</NativeSelectOption>
|
|
64
|
+
* </NativeSelectOptGroup>
|
|
65
|
+
* </NativeSelect>
|
|
66
|
+
*
|
|
67
|
+
* // Small size
|
|
68
|
+
* <NativeSelect size="sm" defaultValue="turmeric">
|
|
69
|
+
* <NativeSelectOption value="turmeric">Turmeric</NativeSelectOption>
|
|
70
|
+
* </NativeSelect>
|
|
71
|
+
*/
|
|
72
|
+
//#endregion
|
|
73
|
+
export { NativeSelect, NativeSelectOptGroup, NativeSelectOption };
|
|
74
|
+
|
|
75
|
+
//# sourceMappingURL=native-select.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native-select.js","names":[],"sources":["../../../src/components/ui/native-select.tsx"],"sourcesContent":["/**\n * NativeSelect — styled wrapper around the browser's native <select>.\n *\n * A plain <select> with a chevron icon overlay, not a Radix primitive.\n * Sizes (\"sm\" | \"default\") are applied via a `data-size`\n * attribute on both the wrapper and the <select>.\n */\n\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { ChevronDownIcon } from \"lucide-react\"\n\ntype NativeSelectProps = Omit<React.ComponentProps<\"select\">, \"size\"> & {\n size?: \"sm\" | \"default\"\n}\n\nfunction NativeSelect({\n className,\n size = \"default\",\n ...props\n}: NativeSelectProps) {\n return (\n <div\n className={cn(\n \"group/native-select relative w-fit has-[select:disabled]:opacity-50\",\n className\n )}\n data-slot=\"native-select-wrapper\"\n data-size={size}\n >\n <select\n data-slot=\"native-select\"\n data-size={size}\n className=\"h-8 w-full min-w-0 appearance-none rounded-lg border border-input bg-transparent py-1 pr-8 pl-2.5 text-sm transition-colors outline-none select-none selection:bg-primary selection:text-primary-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=sm]:h-7 data-[size=sm]:rounded-[min(var(--radius-md,0.375rem),10px)] data-[size=sm]:py-0.5 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40\"\n {...props}\n />\n <ChevronDownIcon\n className=\"pointer-events-none absolute top-1/2 right-2.5 size-4 -translate-y-1/2 text-muted-foreground select-none\"\n aria-hidden=\"true\"\n data-slot=\"native-select-icon\"\n />\n </div>\n )\n}\n\nfunction NativeSelectOption({\n className,\n ...props\n}: React.ComponentProps<\"option\">) {\n return (\n <option\n data-slot=\"native-select-option\"\n className={cn(\"bg-[Canvas] text-[CanvasText]\", className)}\n {...props}\n />\n )\n}\n\nfunction NativeSelectOptGroup({\n className,\n ...props\n}: React.ComponentProps<\"optgroup\">) {\n return (\n <optgroup\n data-slot=\"native-select-optgroup\"\n className={cn(\"bg-[Canvas] text-[CanvasText]\", className)}\n {...props}\n />\n )\n}\n\nexport { NativeSelect, NativeSelectOptGroup, NativeSelectOption }\n\n/**\n * WHEN TO USE\n * - Simple option lists in dense forms where native OS picker behavior and zero extra JS are preferable.\n * - Mobile-heavy flows — the OS supplies its own picker UI, which tends to be more reliable than a custom popover.\n *\n * NOT WHEN\n * - Custom item rendering, icons, or grouped visual styling is required — use Select instead.\n * - The list is long enough to need type-to-filter — use Combobox instead.\n *\n * KEY RULES\n * - `size` is \"sm\" | \"default\" (default \"default\") — no other sizes exist.\n * - Compose options with NativeSelectOption / NativeSelectOptGroup only — content is plain text, no custom item rendering.\n * - Disabled state is native (`disabled` on the underlying <select>); the wrapper dims via has-[select:disabled]:opacity-50.\n *\n * USAGE\n * <NativeSelect defaultValue=\"cardamom\">\n * <NativeSelectOption value=\"turmeric\">Turmeric</NativeSelectOption>\n * <NativeSelectOption value=\"cardamom\">Cardamom</NativeSelectOption>\n * </NativeSelect>\n *\n * // Grouped\n * <NativeSelect defaultValue=\"cumin\">\n * <NativeSelectOptGroup label=\"Whole\">\n * <NativeSelectOption value=\"cardamom\">Cardamom pod</NativeSelectOption>\n * </NativeSelectOptGroup>\n * <NativeSelectOptGroup label=\"Ground\">\n * <NativeSelectOption value=\"cumin\">Cumin</NativeSelectOption>\n * </NativeSelectOptGroup>\n * </NativeSelect>\n *\n * // Small size\n * <NativeSelect size=\"sm\" defaultValue=\"turmeric\">\n * <NativeSelectOption value=\"turmeric\">Turmeric</NativeSelectOption>\n * </NativeSelect>\n */\n"],"mappings":";;;;;AAiBA,SAAS,aAAa,EACpB,WACA,OAAO,WACP,GAAG,SACiB;CACpB,OACE,qBAAC,OAAD;EACE,WAAW,GACT,uEACA,SACF;EACA,aAAU;EACV,aAAW;EANb,UAAA,CAQE,oBAAC,UAAD;GACE,aAAU;GACV,aAAW;GACX,WAAU;GACV,GAAI;EACL,CAAA,GACD,oBAAC,iBAAD;GACE,WAAU;GACV,eAAY;GACZ,aAAU;EACX,CAAA,CACE;;AAET;AAEA,SAAS,mBAAmB,EAC1B,WACA,GAAG,SAC8B;CACjC,OACE,oBAAC,UAAD;EACE,aAAU;EACV,WAAW,GAAG,iCAAiC,SAAS;EACxD,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,qBAAqB,EAC5B,WACA,GAAG,SACgC;CACnC,OACE,oBAAC,YAAD;EACE,aAAU;EACV,WAAW,GAAG,iCAAiC,SAAS;EACxD,GAAI;CACL,CAAA;AAEL"}
|