@camox/ui 0.1.1-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/LICENSE.md +110 -0
  2. package/package.json +185 -0
  3. package/src/components/accordion.tsx +58 -0
  4. package/src/components/alert-dialog.tsx +133 -0
  5. package/src/components/alert.tsx +60 -0
  6. package/src/components/avatar.tsx +94 -0
  7. package/src/components/badge.tsx +39 -0
  8. package/src/components/breadcrumb.tsx +102 -0
  9. package/src/components/button-group.tsx +78 -0
  10. package/src/components/button.tsx +58 -0
  11. package/src/components/checkbox.tsx +27 -0
  12. package/src/components/command.tsx +168 -0
  13. package/src/components/control-group.tsx +58 -0
  14. package/src/components/dialog.tsx +127 -0
  15. package/src/components/dropdown-menu.tsx +226 -0
  16. package/src/components/floating-toolbar.tsx +17 -0
  17. package/src/components/frame.tsx +146 -0
  18. package/src/components/input-base.tsx +189 -0
  19. package/src/components/input.tsx +21 -0
  20. package/src/components/kbd.tsx +28 -0
  21. package/src/components/label.tsx +21 -0
  22. package/src/components/panel.tsx +78 -0
  23. package/src/components/popover.tsx +40 -0
  24. package/src/components/resizable.tsx +46 -0
  25. package/src/components/select.tsx +169 -0
  26. package/src/components/separator.tsx +26 -0
  27. package/src/components/sheet.tsx +130 -0
  28. package/src/components/skeleton.tsx +13 -0
  29. package/src/components/spinner.tsx +16 -0
  30. package/src/components/switch.tsx +26 -0
  31. package/src/components/tabs.tsx +52 -0
  32. package/src/components/textarea.tsx +20 -0
  33. package/src/components/toaster.tsx +22 -0
  34. package/src/components/toggle.tsx +45 -0
  35. package/src/components/tooltip.tsx +55 -0
  36. package/src/hooks/use-mobile.ts +19 -0
  37. package/src/lib/utils.ts +15 -0
  38. package/src/styles/globals.css +120 -0
@@ -0,0 +1,226 @@
1
+ import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
2
+ import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
3
+ import * as React from "react";
4
+
5
+ import { cn } from "../lib/utils";
6
+
7
+ function DropdownMenu({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
8
+ return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
9
+ }
10
+
11
+ function DropdownMenuPortal({
12
+ ...props
13
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
14
+ return <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />;
15
+ }
16
+
17
+ function DropdownMenuTrigger({
18
+ ...props
19
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
20
+ return <DropdownMenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />;
21
+ }
22
+
23
+ function DropdownMenuContent({
24
+ className,
25
+ sideOffset = 4,
26
+ ...props
27
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
28
+ return (
29
+ <DropdownMenuPrimitive.Portal>
30
+ <DropdownMenuPrimitive.Content
31
+ data-slot="dropdown-menu-content"
32
+ sideOffset={sideOffset}
33
+ className={cn(
34
+ "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
35
+ className,
36
+ )}
37
+ {...props}
38
+ />
39
+ </DropdownMenuPrimitive.Portal>
40
+ );
41
+ }
42
+
43
+ function DropdownMenuGroup({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
44
+ return <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />;
45
+ }
46
+
47
+ function DropdownMenuItem({
48
+ className,
49
+ inset,
50
+ variant = "default",
51
+ ...props
52
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
53
+ inset?: boolean;
54
+ variant?: "default" | "destructive";
55
+ }) {
56
+ return (
57
+ <DropdownMenuPrimitive.Item
58
+ data-slot="dropdown-menu-item"
59
+ data-inset={inset}
60
+ data-variant={variant}
61
+ className={cn(
62
+ "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
63
+ className,
64
+ )}
65
+ {...props}
66
+ />
67
+ );
68
+ }
69
+
70
+ function DropdownMenuCheckboxItem({
71
+ className,
72
+ children,
73
+ checked,
74
+ ...props
75
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
76
+ return (
77
+ <DropdownMenuPrimitive.CheckboxItem
78
+ data-slot="dropdown-menu-checkbox-item"
79
+ className={cn(
80
+ "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
81
+ className,
82
+ )}
83
+ checked={checked}
84
+ {...props}
85
+ >
86
+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
87
+ <DropdownMenuPrimitive.ItemIndicator>
88
+ <CheckIcon className="size-4" />
89
+ </DropdownMenuPrimitive.ItemIndicator>
90
+ </span>
91
+ {children}
92
+ </DropdownMenuPrimitive.CheckboxItem>
93
+ );
94
+ }
95
+
96
+ function DropdownMenuRadioGroup({
97
+ ...props
98
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
99
+ return <DropdownMenuPrimitive.RadioGroup data-slot="dropdown-menu-radio-group" {...props} />;
100
+ }
101
+
102
+ function DropdownMenuRadioItem({
103
+ className,
104
+ children,
105
+ ...props
106
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
107
+ return (
108
+ <DropdownMenuPrimitive.RadioItem
109
+ data-slot="dropdown-menu-radio-item"
110
+ className={cn(
111
+ "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
112
+ className,
113
+ )}
114
+ {...props}
115
+ >
116
+ <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
117
+ <DropdownMenuPrimitive.ItemIndicator>
118
+ <CircleIcon className="size-2 fill-current" />
119
+ </DropdownMenuPrimitive.ItemIndicator>
120
+ </span>
121
+ {children}
122
+ </DropdownMenuPrimitive.RadioItem>
123
+ );
124
+ }
125
+
126
+ function DropdownMenuLabel({
127
+ className,
128
+ inset,
129
+ ...props
130
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
131
+ inset?: boolean;
132
+ }) {
133
+ return (
134
+ <DropdownMenuPrimitive.Label
135
+ data-slot="dropdown-menu-label"
136
+ data-inset={inset}
137
+ className={cn("px-2 py-1.5 text-sm font-medium data-[inset]:pl-8", className)}
138
+ {...props}
139
+ />
140
+ );
141
+ }
142
+
143
+ function DropdownMenuSeparator({
144
+ className,
145
+ ...props
146
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
147
+ return (
148
+ <DropdownMenuPrimitive.Separator
149
+ data-slot="dropdown-menu-separator"
150
+ className={cn("bg-border -mx-1 my-1 h-px", className)}
151
+ {...props}
152
+ />
153
+ );
154
+ }
155
+
156
+ function DropdownMenuShortcut({ className, ...props }: React.ComponentProps<"span">) {
157
+ return (
158
+ <span
159
+ data-slot="dropdown-menu-shortcut"
160
+ className={cn("text-muted-foreground ml-auto text-xs tracking-widest", className)}
161
+ {...props}
162
+ />
163
+ );
164
+ }
165
+
166
+ function DropdownMenuSub({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
167
+ return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />;
168
+ }
169
+
170
+ function DropdownMenuSubTrigger({
171
+ className,
172
+ inset,
173
+ children,
174
+ ...props
175
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
176
+ inset?: boolean;
177
+ }) {
178
+ return (
179
+ <DropdownMenuPrimitive.SubTrigger
180
+ data-slot="dropdown-menu-sub-trigger"
181
+ data-inset={inset}
182
+ className={cn(
183
+ "focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8",
184
+ className,
185
+ )}
186
+ {...props}
187
+ >
188
+ {children}
189
+ <ChevronRightIcon className="text-muted-foreground ml-auto size-4" />
190
+ </DropdownMenuPrimitive.SubTrigger>
191
+ );
192
+ }
193
+
194
+ function DropdownMenuSubContent({
195
+ className,
196
+ ...props
197
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
198
+ return (
199
+ <DropdownMenuPrimitive.SubContent
200
+ data-slot="dropdown-menu-sub-content"
201
+ className={cn(
202
+ "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
203
+ className,
204
+ )}
205
+ {...props}
206
+ />
207
+ );
208
+ }
209
+
210
+ export {
211
+ DropdownMenu,
212
+ DropdownMenuPortal,
213
+ DropdownMenuTrigger,
214
+ DropdownMenuContent,
215
+ DropdownMenuGroup,
216
+ DropdownMenuLabel,
217
+ DropdownMenuItem,
218
+ DropdownMenuCheckboxItem,
219
+ DropdownMenuRadioGroup,
220
+ DropdownMenuRadioItem,
221
+ DropdownMenuSeparator,
222
+ DropdownMenuShortcut,
223
+ DropdownMenuSub,
224
+ DropdownMenuSubTrigger,
225
+ DropdownMenuSubContent,
226
+ };
@@ -0,0 +1,17 @@
1
+ import { cn } from "../lib/utils";
2
+
3
+ function FloatingToolbar({ className, ...props }: React.ComponentProps<"menu">) {
4
+ return (
5
+ <menu
6
+ role="toolbar"
7
+ data-slot="floating-toolbar"
8
+ className={cn(
9
+ "absolute left-[50%] z-30 flex translate-x-[-50%] items-center rounded-xl border-1 bg-background/95 p-2 shadow-2xl backdrop-blur-lg transition-all duration-200",
10
+ className,
11
+ )}
12
+ {...props}
13
+ />
14
+ );
15
+ }
16
+
17
+ export { FloatingToolbar };
@@ -0,0 +1,146 @@
1
+ import * as React from "react";
2
+ import { createPortal } from "react-dom";
3
+
4
+ import { cn } from "../lib/utils";
5
+
6
+ interface FrameContextValue {
7
+ window: Window | null;
8
+ iframeElement: HTMLIFrameElement | null;
9
+ }
10
+
11
+ const FrameContext = React.createContext<FrameContextValue>({
12
+ window: null,
13
+ iframeElement: null,
14
+ });
15
+
16
+ export function useFrame() {
17
+ const context = React.use(FrameContext);
18
+ if (!context) {
19
+ throw new Error("useFrame must be used within a Frame");
20
+ }
21
+ return context;
22
+ }
23
+
24
+ interface FrameProps {
25
+ children: React.ReactNode;
26
+ /** Optional className for the iframe element */
27
+ className?: string;
28
+ /** Optional inline styles for the iframe element */
29
+ style?: React.CSSProperties;
30
+ /** Whether to copy parent document styles into the iframe (default: true) */
31
+ copyStyles?: boolean;
32
+ /** Callback when iframe is ready, receives the iframe element */
33
+ onIframeReady?: (iframe: HTMLIFrameElement) => void;
34
+ }
35
+
36
+ export const Frame = ({
37
+ children,
38
+ className,
39
+ style,
40
+ copyStyles = true,
41
+ onIframeReady,
42
+ }: FrameProps) => {
43
+ const iframeRef = React.useRef<HTMLIFrameElement>(null);
44
+ const [iframeWindow, setIframeWindow] = React.useState<Window | null>(null);
45
+ const [iframeElement, setIframeElement] = React.useState<HTMLIFrameElement | null>(null);
46
+ const [mountNode, setMountNode] = React.useState<HTMLElement | null>(null);
47
+ const [hasRadixPopper, setHasRadixPopper] = React.useState(false);
48
+
49
+ React.useEffect(() => {
50
+ const iframe = iframeRef.current;
51
+ if (!iframe) return;
52
+
53
+ const handleLoad = () => {
54
+ const iframeDoc = iframe.contentDocument;
55
+ const iframeWin = iframe.contentWindow;
56
+
57
+ if (!iframeDoc || !iframeWin) return;
58
+
59
+ // Set up basic document structure
60
+ iframeDoc.open();
61
+ iframeDoc.write(
62
+ "<!DOCTYPE html><html><head></head><body style='background: transparent;'></body></html>",
63
+ );
64
+ iframeDoc.close();
65
+
66
+ // Navigate the top-level window when a native <a> is clicked inside the
67
+ // iframe. Links managed by a client-side router (e.g. TanStack Router's
68
+ // <Link>) call e.preventDefault() themselves, so we skip those.
69
+ // We listen on `iframeWin` (not `iframeDoc`) so that this handler fires
70
+ // AFTER React's event delegation (which is on the document/body), giving
71
+ // React a chance to call preventDefault() first.
72
+ iframeWin.addEventListener("click", (e) => {
73
+ if (e.defaultPrevented) return;
74
+ const anchor = (e.target as Element).closest("a");
75
+ if (!anchor?.href) return;
76
+ if (anchor.target === "_blank") return;
77
+ e.preventDefault();
78
+ window.top?.location.assign(anchor.href);
79
+ });
80
+
81
+ // Copy styles from parent document if requested
82
+ if (copyStyles) {
83
+ const headStyles = Array.from(
84
+ document.head.querySelectorAll('style, link[rel="stylesheet"]'),
85
+ );
86
+ headStyles.forEach((style) => {
87
+ const clonedStyle = style.cloneNode(true);
88
+ iframeDoc.head.appendChild(clonedStyle);
89
+ });
90
+ }
91
+
92
+ // Set the mount node to the iframe's body
93
+ setMountNode(iframeDoc.body);
94
+ setIframeWindow(iframeWin);
95
+ setIframeElement(iframe);
96
+ onIframeReady?.(iframe);
97
+ };
98
+
99
+ // Add load event listener
100
+ iframe.addEventListener("load", handleLoad);
101
+
102
+ // Trigger load if iframe is already loaded
103
+ if (iframe.contentDocument?.readyState === "complete") {
104
+ handleLoad();
105
+ }
106
+
107
+ return () => {
108
+ iframe.removeEventListener("load", handleLoad);
109
+ };
110
+ }, [copyStyles, onIframeReady]);
111
+
112
+ // Monitor for Radix popper content wrapper in body
113
+ React.useEffect(() => {
114
+ const checkForRadixPopper = () => {
115
+ const hasPopper =
116
+ document.body.querySelector(":scope > [data-radix-popper-content-wrapper]") !== null;
117
+ setHasRadixPopper(hasPopper);
118
+ };
119
+
120
+ // Initial check
121
+ checkForRadixPopper();
122
+
123
+ // Set up MutationObserver to watch for changes
124
+ const observer = new MutationObserver(checkForRadixPopper);
125
+ observer.observe(document.body, {
126
+ childList: true,
127
+ subtree: false, // Only watch direct children of body
128
+ });
129
+
130
+ return () => {
131
+ observer.disconnect();
132
+ };
133
+ }, []);
134
+
135
+ return (
136
+ <div className={cn("relative w-full h-full", className)} style={style}>
137
+ {/* Display an overlay to properly close close Radix portals (modals, popovers...) */}
138
+ {/* because otherwise Radix wouldn't detect pointer events that would happen on the iframe */}
139
+ {hasRadixPopper && <div className="absolute top-0 left-0 h-full w-full" />}
140
+ <FrameContext.Provider value={{ window: iframeWindow, iframeElement }}>
141
+ <iframe ref={iframeRef} className={cn("w-full h-full")} />
142
+ {mountNode && createPortal(children, mountNode)}
143
+ </FrameContext.Provider>
144
+ </div>
145
+ );
146
+ };
@@ -0,0 +1,189 @@
1
+ "use client";
2
+
3
+ import { composeEventHandlers } from "@radix-ui/primitive";
4
+ import { useComposedRefs } from "@radix-ui/react-compose-refs";
5
+ import { Primitive } from "@radix-ui/react-primitive";
6
+ import { Slot } from "@radix-ui/react-slot";
7
+ import * as React from "react";
8
+
9
+ import { cn } from "../lib/utils";
10
+ import { Button } from "./button";
11
+
12
+ export type InputBaseContextProps = Pick<InputBaseProps, "autoFocus" | "disabled"> & {
13
+ controlRef: React.RefObject<HTMLElement | null>;
14
+ onFocusedChange: (focused: boolean) => void;
15
+ };
16
+
17
+ const InputBaseContext = React.createContext<InputBaseContextProps>({
18
+ autoFocus: false,
19
+ controlRef: { current: null },
20
+ disabled: false,
21
+ onFocusedChange: () => {},
22
+ });
23
+
24
+ function useInputBase() {
25
+ const context = React.useContext(InputBaseContext);
26
+ if (!context) {
27
+ throw new Error("useInputBase must be used within a <InputBase />.");
28
+ }
29
+
30
+ return context;
31
+ }
32
+
33
+ export interface InputBaseProps extends React.ComponentProps<typeof Primitive.div> {
34
+ autoFocus?: boolean;
35
+ disabled?: boolean;
36
+ error?: boolean;
37
+ }
38
+
39
+ function InputBase({ autoFocus, disabled, className, onClick, error, ...props }: InputBaseProps) {
40
+ const [focused, setFocused] = React.useState(false);
41
+ const controlRef = React.useRef<HTMLElement>(null);
42
+
43
+ return (
44
+ <InputBaseContext.Provider
45
+ value={{
46
+ autoFocus,
47
+ controlRef,
48
+ disabled,
49
+ onFocusedChange: setFocused,
50
+ }}
51
+ >
52
+ <Primitive.div
53
+ data-slot="input-base"
54
+ // Based on MUI's <InputBase /> implementation.
55
+ // https://github.com/mui/material-ui/blob/master/packages/mui-material/src/InputBase/InputBase.js#L458~L460
56
+ onClick={composeEventHandlers(onClick, (event) => {
57
+ if (controlRef.current && event.currentTarget === event.target) {
58
+ controlRef.current.focus();
59
+ }
60
+ })}
61
+ className={cn(
62
+ "border-input selection:bg-primary selection:text-primary-foreground dark:bg-input/30 flex min-h-9 cursor-text items-center gap-2 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none md:text-sm",
63
+ disabled && "pointer-events-none cursor-not-allowed opacity-50",
64
+ focused && "border-ring ring-ring/50 ring-[3px]",
65
+ error && "ring-destructive/20 dark:ring-destructive/40 border-destructive",
66
+ className,
67
+ )}
68
+ {...props}
69
+ />
70
+ </InputBaseContext.Provider>
71
+ );
72
+ }
73
+
74
+ function InputBaseFlexWrapper({ className, ...props }: React.ComponentProps<typeof Primitive.div>) {
75
+ return (
76
+ <Primitive.div
77
+ data-slot="input-base-flex-wrapper"
78
+ className={cn("flex flex-1 flex-wrap", className)}
79
+ {...props}
80
+ />
81
+ );
82
+ }
83
+
84
+ function InputBaseControl({ ref, onFocus, onBlur, ...props }: React.ComponentProps<typeof Slot>) {
85
+ const { controlRef, autoFocus, disabled, onFocusedChange } = useInputBase();
86
+
87
+ const composedRefs = useComposedRefs(controlRef, ref);
88
+
89
+ return (
90
+ <Slot
91
+ data-slot="input-base-control"
92
+ ref={composedRefs}
93
+ autoFocus={autoFocus}
94
+ onFocus={composeEventHandlers(onFocus, () => onFocusedChange(true))}
95
+ onBlur={composeEventHandlers(onBlur, () => onFocusedChange(false))}
96
+ {...{ disabled }}
97
+ {...props}
98
+ />
99
+ );
100
+ }
101
+
102
+ export interface InputBaseAdornmentProps extends React.ComponentProps<"div"> {
103
+ asChild?: boolean;
104
+ }
105
+
106
+ function InputBaseAdornment({ className, asChild, children, ...props }: InputBaseAdornmentProps) {
107
+ let Comp: typeof Slot | "p" | "div";
108
+ if (asChild) {
109
+ Comp = Slot;
110
+ } else if (typeof children === "string") {
111
+ Comp = "p";
112
+ } else {
113
+ Comp = "div";
114
+ }
115
+
116
+ return (
117
+ <Comp
118
+ data-slot="input-base-adornment"
119
+ className={cn(
120
+ "text-muted-foreground flex items-center [&_svg:not([class*='size-'])]:size-4",
121
+ "[&:not(:has(button))]:pointer-events-none",
122
+ className,
123
+ )}
124
+ {...props}
125
+ >
126
+ {children}
127
+ </Comp>
128
+ );
129
+ }
130
+
131
+ function InputBaseAdornmentButton({
132
+ type = "button",
133
+ variant = "ghost",
134
+ size = "icon",
135
+ disabled: disabledProp,
136
+ className,
137
+ ...props
138
+ }: React.ComponentProps<typeof Button>) {
139
+ const { disabled } = useInputBase();
140
+
141
+ return (
142
+ <Button
143
+ data-slot="input-base-adornment-button"
144
+ type={type}
145
+ variant={variant}
146
+ size={size}
147
+ disabled={disabled || disabledProp}
148
+ className={cn("size-6", className)}
149
+ {...props}
150
+ />
151
+ );
152
+ }
153
+
154
+ function InputBaseInput({ className, ...props }: React.ComponentProps<typeof Primitive.input>) {
155
+ return (
156
+ <Primitive.input
157
+ data-slot="input-base-input"
158
+ className={cn(
159
+ "placeholder:text-muted-foreground file:text-foreground w-full flex-1 bg-transparent file:border-0 file:bg-transparent file:text-sm file:font-medium focus:outline-none disabled:pointer-events-none",
160
+ className,
161
+ )}
162
+ {...props}
163
+ />
164
+ );
165
+ }
166
+
167
+ function InputBaseTextarea({ className, ...props }: React.ComponentProps<"textarea">) {
168
+ return (
169
+ <textarea
170
+ data-slot="input-base-textarea"
171
+ className={cn(
172
+ "placeholder:text-muted-foreground min-h-16 flex-1 bg-transparent focus:outline-none disabled:pointer-events-none",
173
+ className,
174
+ )}
175
+ {...props}
176
+ />
177
+ );
178
+ }
179
+
180
+ export {
181
+ InputBase,
182
+ InputBaseFlexWrapper,
183
+ InputBaseControl,
184
+ InputBaseAdornment,
185
+ InputBaseAdornmentButton,
186
+ InputBaseInput,
187
+ InputBaseTextarea,
188
+ useInputBase,
189
+ };
@@ -0,0 +1,21 @@
1
+ import * as React from "react";
2
+
3
+ import { INPUT_BASE_STYLES, INPUT_FOCUS_STYLES, cn } from "../lib/utils";
4
+
5
+ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
6
+ return (
7
+ <input
8
+ type={type}
9
+ data-slot="input"
10
+ className={cn(
11
+ INPUT_BASE_STYLES,
12
+ INPUT_FOCUS_STYLES,
13
+ "file:text-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 h-9 w-full min-w-0 px-3 py-1 file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none",
14
+ className,
15
+ )}
16
+ {...props}
17
+ />
18
+ );
19
+ }
20
+
21
+ export { Input };
@@ -0,0 +1,28 @@
1
+ import { cn } from "../lib/utils";
2
+
3
+ function Kbd({ className, ...props }: React.ComponentProps<"kbd">) {
4
+ return (
5
+ <kbd
6
+ data-slot="kbd"
7
+ className={cn(
8
+ "bg-muted text-muted-foreground pointer-events-none inline-flex h-5 w-fit min-w-5 items-center justify-center gap-1 rounded-sm px-1 font-sans text-xs font-medium select-none",
9
+ "[&_svg:not([class*='size-'])]:size-3",
10
+ "[[data-slot=tooltip-content]_&]:bg-background/20 [[data-slot=tooltip-content]_&]:text-background dark:[[data-slot=tooltip-content]_&]:bg-background/10",
11
+ className,
12
+ )}
13
+ {...props}
14
+ />
15
+ );
16
+ }
17
+
18
+ function KbdGroup({ className, ...props }: React.ComponentProps<"div">) {
19
+ return (
20
+ <kbd
21
+ data-slot="kbd-group"
22
+ className={cn("inline-flex items-center gap-1", className)}
23
+ {...props}
24
+ />
25
+ );
26
+ }
27
+
28
+ export { Kbd, KbdGroup };
@@ -0,0 +1,21 @@
1
+ "use client";
2
+
3
+ import * as LabelPrimitive from "@radix-ui/react-label";
4
+ import * as React from "react";
5
+
6
+ import { cn } from "../lib/utils";
7
+
8
+ function Label({ className, ...props }: React.ComponentProps<typeof LabelPrimitive.Root>) {
9
+ return (
10
+ <LabelPrimitive.Root
11
+ data-slot="label"
12
+ className={cn(
13
+ "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
14
+ className,
15
+ )}
16
+ {...props}
17
+ />
18
+ );
19
+ }
20
+
21
+ export { Label };