1+ "use client" ;
2+
3+ import * as React from "react" ;
4+ import { cva , type VariantProps } from "class-variance-authority" ;
5+
6+ import { cn } from "@/lib/utils" ;
7+ import { Button } from "@/components/ui/button" ;
8+ import { Input } from "@/components/ui/input" ;
9+ import { Textarea } from "@/components/ui/textarea" ;
10+
11+ function InputGroup ( { className, ...props } : React . ComponentProps < "div" > ) {
12+ return (
13+ < div
14+ data-slot = "input-group"
15+ role = "group"
16+ className = { cn (
17+ "group/input-group border-input dark:bg-input/30 shadow-xs relative flex w-full items-center rounded-md border outline-none transition-[color,box-shadow]" ,
18+ "h-9 has-[>textarea]:h-auto" ,
19+
20+ // Variants based on alignment.
21+ "has-[>[data-align=inline-start]]:[&>input]:pl-2" ,
22+ "has-[>[data-align=inline-end]]:[&>input]:pr-2" ,
23+ "has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3" ,
24+ "has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3" ,
25+
26+ // Focus state.
27+ "has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-[3px] has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50" ,
28+
29+ // Error state.
30+ "has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40" ,
31+
32+ className
33+ ) }
34+ { ...props }
35+ />
36+ ) ;
37+ }
38+
39+ const inputGroupAddonVariants = cva (
40+ "text-muted-foreground flex h-auto cursor-text select-none items-center justify-center gap-2 py-1.5 text-sm font-medium group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-4" ,
41+ {
42+ variants : {
43+ align : {
44+ "inline-start" :
45+ "order-first pl-3 has-[>button]:ml-[-0.45rem] has-[>kbd]:ml-[-0.35rem]" ,
46+ "inline-end" :
47+ "order-last pr-3 has-[>button]:mr-[-0.4rem] has-[>kbd]:mr-[-0.35rem]" ,
48+ "block-start" :
49+ "[.border-b]:pb-3 order-first w-full justify-start px-3 pt-3 group-has-[>input]/input-group:pt-2.5" ,
50+ "block-end" :
51+ "[.border-t]:pt-3 order-last w-full justify-start px-3 pb-3 group-has-[>input]/input-group:pb-2.5" ,
52+ } ,
53+ } ,
54+ defaultVariants : {
55+ align : "inline-start" ,
56+ } ,
57+ }
58+ ) ;
59+
60+ function InputGroupAddon ( {
61+ className,
62+ align = "inline-start" ,
63+ ...props
64+ } : React . ComponentProps < "div" > & VariantProps < typeof inputGroupAddonVariants > ) {
65+ return (
66+ < div
67+ role = "group"
68+ data-slot = "input-group-addon"
69+ data-align = { align }
70+ className = { cn ( inputGroupAddonVariants ( { align } ) , className ) }
71+ onClick = { ( e ) => {
72+ if ( ( e . target as HTMLElement ) . closest ( "button" ) ) {
73+ return ;
74+ }
75+ e . currentTarget . parentElement ?. querySelector ( "input" ) ?. focus ( ) ;
76+ } }
77+ { ...props }
78+ />
79+ ) ;
80+ }
81+
82+ const inputGroupButtonVariants = cva (
83+ "flex items-center gap-2 text-sm shadow-none" ,
84+ {
85+ variants : {
86+ size : {
87+ xs : "h-6 gap-1 rounded-[calc(var(--radius)-5px)] px-2 has-[>svg]:px-2 [&>svg:not([class*='size-'])]:size-3.5" ,
88+ sm : "h-8 gap-1.5 rounded-md px-2.5 has-[>svg]:px-2.5" ,
89+ "icon-xs" :
90+ "size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0" ,
91+ "icon-sm" : "size-8 p-0 has-[>svg]:p-0" ,
92+ } ,
93+ } ,
94+ defaultVariants : {
95+ size : "xs" ,
96+ } ,
97+ }
98+ ) ;
99+
100+ function InputGroupButton ( {
101+ className,
102+ type = "button" ,
103+ variant = "ghost" ,
104+ size = "xs" ,
105+ ...props
106+ } : Omit < React . ComponentProps < typeof Button > , "size" > &
107+ VariantProps < typeof inputGroupButtonVariants > ) {
108+ return (
109+ < Button
110+ type = { type }
111+ data-size = { size }
112+ variant = { variant }
113+ className = { cn ( inputGroupButtonVariants ( { size } ) , className ) }
114+ { ...props }
115+ />
116+ ) ;
117+ }
118+
119+ function InputGroupText ( { className, ...props } : React . ComponentProps < "span" > ) {
120+ return (
121+ < span
122+ className = { cn (
123+ "text-muted-foreground flex items-center gap-2 text-sm [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none" ,
124+ className
125+ ) }
126+ { ...props }
127+ />
128+ ) ;
129+ }
130+
131+ function InputGroupInput ( {
132+ className,
133+ ...props
134+ } : React . ComponentProps < "input" > ) {
135+ return (
136+ < Input
137+ data-slot = "input-group-control"
138+ className = { cn (
139+ "flex-1 rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 dark:bg-transparent" ,
140+ className
141+ ) }
142+ { ...props }
143+ />
144+ ) ;
145+ }
146+
147+ function InputGroupTextarea ( {
148+ className,
149+ ...props
150+ } : React . ComponentProps < "textarea" > ) {
151+ return (
152+ < Textarea
153+ data-slot = "input-group-control"
154+ className = { cn (
155+ "flex-1 resize-none rounded-none border-0 bg-transparent py-3 shadow-none focus-visible:ring-0 dark:bg-transparent" ,
156+ className
157+ ) }
158+ { ...props }
159+ />
160+ ) ;
161+ }
162+
163+ export {
164+ InputGroup ,
165+ InputGroupAddon ,
166+ InputGroupButton ,
167+ InputGroupText ,
168+ InputGroupInput ,
169+ InputGroupTextarea ,
170+ } ;
0 commit comments