@openconsole/shadcn 0.0.0 → 0.0.1
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/accordion.tsx +66 -66
- package/alert-dialog.tsx +196 -196
- package/alert.tsx +66 -66
- package/aspect-ratio.tsx +11 -11
- package/avatar.tsx +53 -53
- package/badge.tsx +46 -46
- package/breadcrumb.tsx +109 -109
- package/button-group.tsx +83 -83
- package/button.tsx +60 -60
- package/calendar.tsx +219 -219
- package/card.tsx +92 -92
- package/carousel.tsx +241 -241
- package/chart.tsx +374 -374
- package/checkbox.tsx +32 -32
- package/collapsible.tsx +33 -33
- package/command.tsx +184 -184
- package/context-menu.tsx +252 -252
- package/dialog.tsx +143 -143
- package/direction.tsx +22 -22
- package/drawer.tsx +135 -135
- package/dropdown-menu.tsx +257 -257
- package/empty.tsx +104 -104
- package/field.tsx +248 -248
- package/form.tsx +167 -167
- package/hooks/index.ts +1 -1
- package/hooks/use-mobile.ts +19 -19
- package/hover-card.tsx +44 -44
- package/icon.tsx +21 -21
- package/index.ts +59 -59
- package/input-group.tsx +170 -170
- package/input-otp.tsx +77 -77
- package/input.tsx +21 -21
- package/item.tsx +193 -193
- package/kbd.tsx +28 -28
- package/label.tsx +24 -24
- package/lib/index.ts +1 -1
- package/lib/utils.ts +6 -6
- package/menubar.tsx +276 -276
- package/native-select.tsx +62 -62
- package/navigation-menu.tsx +168 -168
- package/package.json +10 -2
- package/pagination.tsx +127 -127
- package/popover.tsx +89 -89
- package/progress.tsx +31 -31
- package/radio-group.tsx +45 -45
- package/resizable.tsx +53 -53
- package/scroll-area.tsx +58 -58
- package/select.tsx +187 -187
- package/separator.tsx +28 -28
- package/sheet.tsx +139 -139
- package/sidebar.tsx +724 -724
- package/skeleton.tsx +13 -13
- package/skill/SKILL.md +620 -599
- package/skill/customization.md +301 -263
- package/skill/rules/base-vs-radix.md +167 -167
- package/skill/rules/composition.md +240 -240
- package/skill/rules/forms.md +271 -271
- package/skill/rules/icons.md +136 -136
- package/skill/rules/styling.md +180 -180
- package/slider.tsx +63 -63
- package/sonner.tsx +40 -40
- package/spinner.tsx +16 -16
- package/styles.css +122 -0
- package/switch.tsx +35 -35
- package/table.tsx +116 -116
- package/tabs.tsx +66 -66
- package/textarea.tsx +18 -18
- package/toggle-group.tsx +83 -83
- package/toggle.tsx +47 -47
- package/tooltip.tsx +61 -61
- package/tsconfig.json +12 -12
package/input-group.tsx
CHANGED
|
@@ -1,170 +1,170 @@
|
|
|
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 "./button"
|
|
8
|
-
import { Input } from "./input"
|
|
9
|
-
import { Textarea } from "./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 relative flex w-full items-center rounded-md border border-input shadow-xs transition-[color,box-shadow] outline-none dark:bg-input/30",
|
|
18
|
-
"h-9 min-w-0 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]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-destructive/20 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
|
-
"flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium text-muted-foreground select-none 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.45rem] has-[>kbd]:mr-[-0.35rem]",
|
|
48
|
-
"block-start":
|
|
49
|
-
"order-first w-full justify-start px-3 pt-3 group-has-[>input]/input-group:pt-2.5 [.border-b]:pb-3",
|
|
50
|
-
"block-end":
|
|
51
|
-
"order-last w-full justify-start px-3 pb-3 group-has-[>input]/input-group:pb-2.5 [.border-t]:pt-3",
|
|
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
|
-
"flex items-center gap-2 text-sm text-muted-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
|
|
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
|
-
}
|
|
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 "./button"
|
|
8
|
+
import { Input } from "./input"
|
|
9
|
+
import { Textarea } from "./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 relative flex w-full items-center rounded-md border border-input shadow-xs transition-[color,box-shadow] outline-none dark:bg-input/30",
|
|
18
|
+
"h-9 min-w-0 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]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-destructive/20 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
|
+
"flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium text-muted-foreground select-none 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.45rem] has-[>kbd]:mr-[-0.35rem]",
|
|
48
|
+
"block-start":
|
|
49
|
+
"order-first w-full justify-start px-3 pt-3 group-has-[>input]/input-group:pt-2.5 [.border-b]:pb-3",
|
|
50
|
+
"block-end":
|
|
51
|
+
"order-last w-full justify-start px-3 pb-3 group-has-[>input]/input-group:pb-2.5 [.border-t]:pt-3",
|
|
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
|
+
"flex items-center gap-2 text-sm text-muted-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
|
|
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
|
+
}
|
package/input-otp.tsx
CHANGED
|
@@ -1,77 +1,77 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import * as React from "react"
|
|
4
|
-
import { OTPInput, OTPInputContext } from "input-otp"
|
|
5
|
-
import { MinusIcon } from "lucide-react"
|
|
6
|
-
|
|
7
|
-
import { cn } from "./lib/utils"
|
|
8
|
-
|
|
9
|
-
function InputOTP({
|
|
10
|
-
className,
|
|
11
|
-
containerClassName,
|
|
12
|
-
...props
|
|
13
|
-
}: React.ComponentProps<typeof OTPInput> & {
|
|
14
|
-
containerClassName?: string
|
|
15
|
-
}) {
|
|
16
|
-
return (
|
|
17
|
-
<OTPInput
|
|
18
|
-
data-slot="input-otp"
|
|
19
|
-
containerClassName={cn(
|
|
20
|
-
"flex items-center gap-2 has-disabled:opacity-50",
|
|
21
|
-
containerClassName
|
|
22
|
-
)}
|
|
23
|
-
className={cn("disabled:cursor-not-allowed", className)}
|
|
24
|
-
{...props}
|
|
25
|
-
/>
|
|
26
|
-
)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function InputOTPGroup({ className, ...props }: React.ComponentProps<"div">) {
|
|
30
|
-
return (
|
|
31
|
-
<div
|
|
32
|
-
data-slot="input-otp-group"
|
|
33
|
-
className={cn("flex items-center", className)}
|
|
34
|
-
{...props}
|
|
35
|
-
/>
|
|
36
|
-
)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function InputOTPSlot({
|
|
40
|
-
index,
|
|
41
|
-
className,
|
|
42
|
-
...props
|
|
43
|
-
}: React.ComponentProps<"div"> & {
|
|
44
|
-
index: number
|
|
45
|
-
}) {
|
|
46
|
-
const inputOTPContext = React.useContext(OTPInputContext)
|
|
47
|
-
const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {}
|
|
48
|
-
|
|
49
|
-
return (
|
|
50
|
-
<div
|
|
51
|
-
data-slot="input-otp-slot"
|
|
52
|
-
data-active={isActive}
|
|
53
|
-
className={cn(
|
|
54
|
-
"relative flex h-9 w-9 items-center justify-center border-y border-r border-input text-sm shadow-xs transition-all outline-none first:rounded-l-md first:border-l last:rounded-r-md aria-invalid:border-destructive data-[active=true]:z-10 data-[active=true]:border-ring data-[active=true]:ring-[3px] data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:border-destructive data-[active=true]:aria-invalid:ring-destructive/20 dark:bg-input/30 dark:data-[active=true]:aria-invalid:ring-destructive/40",
|
|
55
|
-
className
|
|
56
|
-
)}
|
|
57
|
-
{...props}
|
|
58
|
-
>
|
|
59
|
-
{char}
|
|
60
|
-
{hasFakeCaret && (
|
|
61
|
-
<div className="pointer-events-none absolute inset-0 flex items-center justify-center">
|
|
62
|
-
<div className="h-4 w-px animate-caret-blink bg-foreground duration-1000" />
|
|
63
|
-
</div>
|
|
64
|
-
)}
|
|
65
|
-
</div>
|
|
66
|
-
)
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function InputOTPSeparator({ ...props }: React.ComponentProps<"div">) {
|
|
70
|
-
return (
|
|
71
|
-
<div data-slot="input-otp-separator" role="separator" {...props}>
|
|
72
|
-
<MinusIcon />
|
|
73
|
-
</div>
|
|
74
|
-
)
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator }
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { OTPInput, OTPInputContext } from "input-otp"
|
|
5
|
+
import { MinusIcon } from "lucide-react"
|
|
6
|
+
|
|
7
|
+
import { cn } from "./lib/utils"
|
|
8
|
+
|
|
9
|
+
function InputOTP({
|
|
10
|
+
className,
|
|
11
|
+
containerClassName,
|
|
12
|
+
...props
|
|
13
|
+
}: React.ComponentProps<typeof OTPInput> & {
|
|
14
|
+
containerClassName?: string
|
|
15
|
+
}) {
|
|
16
|
+
return (
|
|
17
|
+
<OTPInput
|
|
18
|
+
data-slot="input-otp"
|
|
19
|
+
containerClassName={cn(
|
|
20
|
+
"flex items-center gap-2 has-disabled:opacity-50",
|
|
21
|
+
containerClassName
|
|
22
|
+
)}
|
|
23
|
+
className={cn("disabled:cursor-not-allowed", className)}
|
|
24
|
+
{...props}
|
|
25
|
+
/>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function InputOTPGroup({ className, ...props }: React.ComponentProps<"div">) {
|
|
30
|
+
return (
|
|
31
|
+
<div
|
|
32
|
+
data-slot="input-otp-group"
|
|
33
|
+
className={cn("flex items-center", className)}
|
|
34
|
+
{...props}
|
|
35
|
+
/>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function InputOTPSlot({
|
|
40
|
+
index,
|
|
41
|
+
className,
|
|
42
|
+
...props
|
|
43
|
+
}: React.ComponentProps<"div"> & {
|
|
44
|
+
index: number
|
|
45
|
+
}) {
|
|
46
|
+
const inputOTPContext = React.useContext(OTPInputContext)
|
|
47
|
+
const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {}
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<div
|
|
51
|
+
data-slot="input-otp-slot"
|
|
52
|
+
data-active={isActive}
|
|
53
|
+
className={cn(
|
|
54
|
+
"relative flex h-9 w-9 items-center justify-center border-y border-r border-input text-sm shadow-xs transition-all outline-none first:rounded-l-md first:border-l last:rounded-r-md aria-invalid:border-destructive data-[active=true]:z-10 data-[active=true]:border-ring data-[active=true]:ring-[3px] data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:border-destructive data-[active=true]:aria-invalid:ring-destructive/20 dark:bg-input/30 dark:data-[active=true]:aria-invalid:ring-destructive/40",
|
|
55
|
+
className
|
|
56
|
+
)}
|
|
57
|
+
{...props}
|
|
58
|
+
>
|
|
59
|
+
{char}
|
|
60
|
+
{hasFakeCaret && (
|
|
61
|
+
<div className="pointer-events-none absolute inset-0 flex items-center justify-center">
|
|
62
|
+
<div className="h-4 w-px animate-caret-blink bg-foreground duration-1000" />
|
|
63
|
+
</div>
|
|
64
|
+
)}
|
|
65
|
+
</div>
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function InputOTPSeparator({ ...props }: React.ComponentProps<"div">) {
|
|
70
|
+
return (
|
|
71
|
+
<div data-slot="input-otp-separator" role="separator" {...props}>
|
|
72
|
+
<MinusIcon />
|
|
73
|
+
</div>
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator }
|
package/input.tsx
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
|
|
3
|
-
import { 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
|
-
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
12
|
-
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
|
13
|
-
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
14
|
-
className,
|
|
15
|
-
)}
|
|
16
|
-
{...props}
|
|
17
|
-
/>
|
|
18
|
-
);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export { Input };
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import { 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
|
+
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
|
12
|
+
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
|
13
|
+
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
14
|
+
className,
|
|
15
|
+
)}
|
|
16
|
+
{...props}
|
|
17
|
+
/>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { Input };
|