Skip to content

Commit 4bc5f78

Browse files
committed
Generate Shadcn input group components
1 parent 06a75b1 commit 4bc5f78

4 files changed

Lines changed: 158 additions & 2 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@
7373
"@radix-ui/react-select": "^2.2.2",
7474
"@radix-ui/react-separator": "^1.1.4",
7575
"@radix-ui/react-slider": "^1.3.2",
76-
"@radix-ui/react-slot": "^1.2.0",
76+
"@radix-ui/react-slot": "^1.2.4",
7777
"@radix-ui/react-switch": "^1.2.2",
7878
"@radix-ui/react-tabs": "^1.1.9",
7979
"@radix-ui/react-toggle": "^1.1.6",
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from '../ui/input-otp'

src/components/ui/input-group.tsx

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

yarn.lock

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1952,13 +1952,20 @@
19521952
"@radix-ui/react-use-previous" "1.1.1"
19531953
"@radix-ui/react-use-size" "1.1.1"
19541954

1955-
"@radix-ui/react-slot@1.2.0", "@radix-ui/react-slot@^1.2.0":
1955+
"@radix-ui/react-slot@1.2.0":
19561956
version "1.2.0"
19571957
resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.2.0.tgz#57727fc186ddb40724ccfbe294e1a351d92462ba"
19581958
integrity sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==
19591959
dependencies:
19601960
"@radix-ui/react-compose-refs" "1.1.2"
19611961

1962+
"@radix-ui/react-slot@^1.2.4":
1963+
version "1.2.4"
1964+
resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.2.4.tgz#63c0ba05fdf90cc49076b94029c852d7bac1fb83"
1965+
integrity sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==
1966+
dependencies:
1967+
"@radix-ui/react-compose-refs" "1.1.2"
1968+
19621969
"@radix-ui/react-switch@^1.2.2":
19631970
version "1.2.2"
19641971
resolved "https://registry.yarnpkg.com/@radix-ui/react-switch/-/react-switch-1.2.2.tgz#aee51a72b93b49d625e201e32c43deb7957e4641"

0 commit comments

Comments
 (0)