@a2v2ai/uikit 0.0.36 → 0.0.38
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/Alert/Alert.stories.tsx +121 -0
- package/Alert/Alert.tsx +71 -0
- package/AlertDialog/AlertDialog.stories.tsx +665 -0
- package/AlertDialog/AlertDialog.tsx +241 -0
- package/Avatar/Avatar.stories.tsx +128 -0
- package/Avatar/Avatar.tsx +71 -0
- package/Badge/Badge.stories.tsx +76 -0
- package/Badge/Badge.tsx +39 -0
- package/Breadcrumb/Breadcrumb.stories.tsx +231 -0
- package/Breadcrumb/Breadcrumb.tsx +114 -0
- package/Button/Button.stories.tsx +684 -0
- package/Button/Button.tsx +107 -0
- package/Calendar/Calendar.stories.tsx +291 -0
- package/Calendar/Calendar.tsx +246 -0
- package/Card/Card.stories.tsx +136 -0
- package/Card/Card.tsx +96 -0
- package/Carousel/Carousel.stories.tsx +256 -0
- package/Carousel/Carousel.tsx +301 -0
- package/ChatBubble/ChatBubble.stories.tsx +339 -0
- package/ChatBubble/ChatBubble.tsx +179 -0
- package/Checkbox/Checkbox.stories.tsx +137 -0
- package/Checkbox/Checkbox.tsx +53 -0
- package/DataTable/DataTable.stories.tsx +400 -0
- package/DataTable/DataTable.tsx +207 -0
- package/Drawer/Drawer.stories.tsx +721 -0
- package/Drawer/Drawer.tsx +201 -0
- package/DropdownMenu/DropdownMenu.stories.tsx +251 -0
- package/DropdownMenu/DropdownMenu.tsx +199 -0
- package/ErrorMessage/ErrorMessage.stories.tsx +159 -0
- package/ErrorMessage/ErrorMessage.tsx +55 -0
- package/Flex/Flex.stories.tsx +390 -0
- package/Flex/Flex.tsx +102 -0
- package/IconButton/IconButton.stories.tsx +566 -0
- package/IconButton/IconButton.tsx +95 -0
- package/Input/Input.stories.tsx +566 -0
- package/Input/Input.tsx +168 -0
- package/InputOTP/InputOTP.stories.tsx +246 -0
- package/InputOTP/InputOTP.tsx +127 -0
- package/Label/Label.stories.tsx +110 -0
- package/Label/Label.tsx +44 -0
- package/Loader/Loader.stories.tsx +170 -0
- package/Loader/Loader.tsx +62 -0
- package/Menubar/Menubar.stories.tsx +382 -0
- package/Menubar/Menubar.tsx +274 -0
- package/Menubar/index.ts +18 -0
- package/Pagination/Pagination.stories.tsx +196 -0
- package/Pagination/Pagination.tsx +122 -0
- package/Popover/Popover.stories.tsx +133 -0
- package/Popover/Popover.tsx +31 -0
- package/Progress/Progress.stories.tsx +146 -0
- package/Progress/Progress.tsx +67 -0
- package/RadioGroup/RadioGroup.stories.tsx +159 -0
- package/RadioGroup/RadioGroup.tsx +68 -0
- package/ScrollArea/ScrollArea.stories.tsx +136 -0
- package/ScrollArea/ScrollArea.tsx +46 -0
- package/Select/Select.stories.tsx +378 -0
- package/Select/Select.tsx +230 -0
- package/Separator/Separator.stories.tsx +110 -0
- package/Separator/Separator.tsx +29 -0
- package/Sidebar/Sidebar.stories.tsx +340 -0
- package/Sidebar/Sidebar.tsx +414 -0
- package/Sidebar/index.ts +28 -0
- package/Skeleton/Skeleton.stories.tsx +117 -0
- package/Skeleton/Skeleton.tsx +16 -0
- package/Slider/Slider.stories.tsx +216 -0
- package/Slider/Slider.tsx +29 -0
- package/Spinner/Spinner.stories.tsx +210 -0
- package/Spinner/Spinner.tsx +78 -0
- package/Switch/Switch.stories.tsx +146 -0
- package/Switch/Switch.tsx +59 -0
- package/Table/Table.stories.tsx +510 -0
- package/Table/Table.tsx +114 -0
- package/Tabs/Tabs.stories.tsx +197 -0
- package/Tabs/Tabs.tsx +74 -0
- package/Textarea/Textarea.stories.tsx +187 -0
- package/Textarea/Textarea.tsx +73 -0
- package/Toast/Toast.stories.tsx +285 -0
- package/Toast/Toast.tsx +59 -0
- package/Tooltip/Tooltip.stories.tsx +463 -0
- package/Tooltip/Tooltip.tsx +96 -0
- package/Typography/Typography.stories.tsx +425 -0
- package/Typography/Typography.tsx +106 -0
- package/helpers.ts +5 -0
- package/{icons.js → icons.ts} +1 -1
- package/index.ts +217 -0
- package/lib/typography-types.ts +223 -0
- package/lib/utils.ts +15 -0
- package/package.json +36 -33
- package/tsconfig.json +22 -0
- package/Alert/Alert.d.ts +0 -13
- package/Alert/Alert.js +0 -25
- package/AlertDialog/AlertDialog.d.ts +0 -43
- package/AlertDialog/AlertDialog.js +0 -71
- package/Avatar/Avatar.d.ts +0 -14
- package/Avatar/Avatar.js +0 -25
- package/Badge/Badge.d.ts +0 -11
- package/Badge/Badge.js +0 -23
- package/Breadcrumb/Breadcrumb.d.ts +0 -19
- package/Breadcrumb/Breadcrumb.js +0 -23
- package/Button/Button.d.ts +0 -23
- package/Button/Button.js +0 -52
- package/Calendar/Calendar.d.ts +0 -20
- package/Calendar/Calendar.js +0 -78
- package/Card/Card.d.ts +0 -16
- package/Card/Card.js +0 -28
- package/Carousel/Carousel.d.ts +0 -37
- package/Carousel/Carousel.js +0 -132
- package/ChatBubble/ChatBubble.d.ts +0 -33
- package/ChatBubble/ChatBubble.js +0 -107
- package/Checkbox/Checkbox.d.ts +0 -12
- package/Checkbox/Checkbox.js +0 -20
- package/DataTable/DataTable.d.ts +0 -35
- package/DataTable/DataTable.js +0 -51
- package/Dialog/Dialog.d.ts +0 -35
- package/Dialog/Dialog.js +0 -130
- package/Drawer/Drawer.d.ts +0 -33
- package/Drawer/Drawer.js +0 -55
- package/DropdownMenu/DropdownMenu.d.ts +0 -27
- package/DropdownMenu/DropdownMenu.js +0 -35
- package/ErrorMessage/ErrorMessage.d.ts +0 -27
- package/ErrorMessage/ErrorMessage.js +0 -14
- package/Flex/Flex.d.ts +0 -31
- package/Flex/Flex.js +0 -64
- package/IconButton/IconButton.d.ts +0 -23
- package/IconButton/IconButton.js +0 -48
- package/Input/Input.d.ts +0 -27
- package/Input/Input.js +0 -42
- package/InputOTP/InputOTP.d.ts +0 -20
- package/InputOTP/InputOTP.js +0 -44
- package/Label/Label.d.ts +0 -13
- package/Label/Label.js +0 -19
- package/Loader/Loader.d.ts +0 -21
- package/Loader/Loader.js +0 -30
- package/Menubar/Menubar.d.ts +0 -26
- package/Menubar/Menubar.js +0 -54
- package/Menubar/index.d.ts +0 -1
- package/Menubar/index.js +0 -1
- package/Pagination/Pagination.d.ts +0 -35
- package/Pagination/Pagination.js +0 -37
- package/Popover/Popover.d.ts +0 -7
- package/Popover/Popover.js +0 -10
- package/Progress/Progress.d.ts +0 -17
- package/Progress/Progress.js +0 -33
- package/RadioGroup/RadioGroup.d.ts +0 -13
- package/RadioGroup/RadioGroup.js +0 -26
- package/ScrollArea/ScrollArea.d.ts +0 -5
- package/ScrollArea/ScrollArea.js +0 -11
- package/Select/Select.d.ts +0 -29
- package/Select/Select.js +0 -50
- package/Separator/Separator.d.ts +0 -4
- package/Separator/Separator.js +0 -7
- package/Sidebar/Sidebar.d.ts +0 -48
- package/Sidebar/Sidebar.js +0 -116
- package/Sidebar/index.d.ts +0 -2
- package/Sidebar/index.js +0 -1
- package/Skeleton/Skeleton.d.ts +0 -4
- package/Skeleton/Skeleton.js +0 -7
- package/Slider/Slider.d.ts +0 -6
- package/Slider/Slider.js +0 -7
- package/Spinner/Spinner.d.ts +0 -19
- package/Spinner/Spinner.js +0 -31
- package/Switch/Switch.d.ts +0 -12
- package/Switch/Switch.js +0 -30
- package/Table/Table.d.ts +0 -10
- package/Table/Table.js +0 -20
- package/Tabs/Tabs.d.ts +0 -15
- package/Tabs/Tabs.js +0 -24
- package/Textarea/Textarea.d.ts +0 -19
- package/Textarea/Textarea.js +0 -31
- package/Toast/Toast.d.ts +0 -12
- package/Toast/Toast.js +0 -25
- package/Tooltip/Tooltip.d.ts +0 -17
- package/Tooltip/Tooltip.js +0 -29
- package/Typography/Typography.d.ts +0 -20
- package/Typography/Typography.js +0 -43
- package/helpers.d.ts +0 -4
- package/helpers.js +0 -5
- package/icons.d.ts +0 -1
- package/index.d.ts +0 -42
- package/index.js +0 -45
- package/lib/typography-types.d.ts +0 -4
- package/lib/typography-types.js +0 -90
- package/lib/utils.d.ts +0 -3
- package/lib/utils.js +0 -14
- package/tmpclaude-2407-cwd +0 -1
package/Input/Input.tsx
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cva } from "class-variance-authority"
|
|
3
|
+
|
|
4
|
+
import { cn } from "../lib/utils"
|
|
5
|
+
import { Typography } from "../Typography/Typography"
|
|
6
|
+
import { Label } from "../Label/Label"
|
|
7
|
+
import { type TextVariant, type TextColor } from "../lib/typography-types"
|
|
8
|
+
|
|
9
|
+
type InputSize = "mini" | "small" | "regular" | "large"
|
|
10
|
+
type InputRoundness = "default" | "round"
|
|
11
|
+
type InputVariant = "default" | "error"
|
|
12
|
+
|
|
13
|
+
const inputVariants = cva(
|
|
14
|
+
"flex w-full bg-white border font-normal font-sans transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-grey-400 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 disabled:bg-grey-50",
|
|
15
|
+
{
|
|
16
|
+
variants: {
|
|
17
|
+
size: {
|
|
18
|
+
mini: "h-6 px-3 text-xs rounded-md",
|
|
19
|
+
small: "h-8 px-3 text-sm rounded-md",
|
|
20
|
+
regular: "h-9 px-3 text-sm rounded-lg",
|
|
21
|
+
large: "h-10 px-3 text-base rounded-lg",
|
|
22
|
+
},
|
|
23
|
+
roundness: {
|
|
24
|
+
default: "",
|
|
25
|
+
round: "!rounded-full",
|
|
26
|
+
},
|
|
27
|
+
variant: {
|
|
28
|
+
default: "border-grey-300 focus-visible:border-grey-300 focus-visible:ring-[3px] focus-visible:ring-grey-300/50",
|
|
29
|
+
error: "border-error-500 focus-visible:border-error-500 focus-visible:ring-[3px] focus-visible:ring-error-200",
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
defaultVariants: {
|
|
33
|
+
size: "regular",
|
|
34
|
+
roundness: "default",
|
|
35
|
+
variant: "default",
|
|
36
|
+
},
|
|
37
|
+
}
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
export interface InputProps
|
|
41
|
+
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "size"> {
|
|
42
|
+
size?: InputSize
|
|
43
|
+
roundness?: InputRoundness
|
|
44
|
+
variant?: InputVariant
|
|
45
|
+
label?: string
|
|
46
|
+
labelVariant?: TextVariant
|
|
47
|
+
labelColor?: TextColor
|
|
48
|
+
leftIcon?: React.ReactNode
|
|
49
|
+
rightIcon?: React.ReactNode
|
|
50
|
+
leftAddon?: React.ReactNode
|
|
51
|
+
rightAddon?: React.ReactNode
|
|
52
|
+
rightElement?: React.ReactNode
|
|
53
|
+
error?: string
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
57
|
+
(
|
|
58
|
+
{
|
|
59
|
+
className,
|
|
60
|
+
type = "text",
|
|
61
|
+
size,
|
|
62
|
+
roundness,
|
|
63
|
+
variant,
|
|
64
|
+
label,
|
|
65
|
+
labelVariant="body2",
|
|
66
|
+
labelColor="main-800",
|
|
67
|
+
leftIcon,
|
|
68
|
+
rightIcon,
|
|
69
|
+
leftAddon,
|
|
70
|
+
rightAddon,
|
|
71
|
+
rightElement,
|
|
72
|
+
error,
|
|
73
|
+
id,
|
|
74
|
+
...props
|
|
75
|
+
},
|
|
76
|
+
ref
|
|
77
|
+
) => {
|
|
78
|
+
const hasAddons = leftIcon || rightIcon || leftAddon || rightAddon || rightElement
|
|
79
|
+
const computedVariant = error ? "error" : variant
|
|
80
|
+
|
|
81
|
+
if (hasAddons) {
|
|
82
|
+
return (
|
|
83
|
+
<div className="flex flex-col gap-2 w-full">
|
|
84
|
+
{label && (
|
|
85
|
+
<Label htmlFor={id} variant={labelVariant} color={labelColor}>
|
|
86
|
+
{label}
|
|
87
|
+
</Label>
|
|
88
|
+
)}
|
|
89
|
+
<div
|
|
90
|
+
className={cn(
|
|
91
|
+
"flex items-center gap-2 bg-white border font-sans transition-colors w-full",
|
|
92
|
+
"focus-within:ring-[3px]",
|
|
93
|
+
computedVariant === "error"
|
|
94
|
+
? "border-error-500 focus-within:border-error-500 focus-within:ring-error-200"
|
|
95
|
+
: "border-grey-300 focus-within:border-grey-300 focus-within:ring-grey-300/50",
|
|
96
|
+
size === "mini" && "h-6 pl-3 pr-0.5 text-xs rounded-md",
|
|
97
|
+
size === "small" && "h-8 pl-3 pr-0.5 text-sm rounded-md",
|
|
98
|
+
size === "regular" && "h-9 pl-3 pr-0.5 text-sm rounded-lg",
|
|
99
|
+
size === "large" && "h-10 pl-3 pr-0.5 text-base rounded-lg",
|
|
100
|
+
!size && "h-9 pl-3 pr-0.5 text-sm rounded-lg",
|
|
101
|
+
!rightElement && "pr-3",
|
|
102
|
+
roundness === "round" && "!rounded-full",
|
|
103
|
+
props.disabled && "opacity-50 cursor-not-allowed bg-grey-50",
|
|
104
|
+
className
|
|
105
|
+
)}
|
|
106
|
+
>
|
|
107
|
+
{leftIcon && (
|
|
108
|
+
<span className="shrink-0 text-grey-500 [&_svg]:size-4">{leftIcon}</span>
|
|
109
|
+
)}
|
|
110
|
+
{leftAddon && (
|
|
111
|
+
<span className="shrink-0 text-grey-500">{leftAddon}</span>
|
|
112
|
+
)}
|
|
113
|
+
<input
|
|
114
|
+
type={type}
|
|
115
|
+
id={id}
|
|
116
|
+
className={cn(
|
|
117
|
+
"flex-1 min-w-0 bg-transparent border-0 outline-none placeholder:text-grey-400",
|
|
118
|
+
"disabled:cursor-not-allowed"
|
|
119
|
+
)}
|
|
120
|
+
ref={ref}
|
|
121
|
+
{...props}
|
|
122
|
+
/>
|
|
123
|
+
{rightAddon && (
|
|
124
|
+
<span className="shrink-0 text-grey-500">{rightAddon}</span>
|
|
125
|
+
)}
|
|
126
|
+
{rightIcon && (
|
|
127
|
+
<span className="shrink-0 text-grey-500 [&_svg]:size-4">{rightIcon}</span>
|
|
128
|
+
)}
|
|
129
|
+
{rightElement && (
|
|
130
|
+
<span className="shrink-0">{rightElement}</span>
|
|
131
|
+
)}
|
|
132
|
+
</div>
|
|
133
|
+
{error && (
|
|
134
|
+
<Typography variant="caption" color="error-600" className="mt-1">
|
|
135
|
+
{error}
|
|
136
|
+
</Typography>
|
|
137
|
+
)}
|
|
138
|
+
</div>
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return (
|
|
143
|
+
<div className="flex flex-col gap-2 w-full">
|
|
144
|
+
{label && (
|
|
145
|
+
<Label htmlFor={id} variant={labelVariant} color={labelColor}>
|
|
146
|
+
{label}
|
|
147
|
+
</Label>
|
|
148
|
+
)}
|
|
149
|
+
<input
|
|
150
|
+
type={type}
|
|
151
|
+
id={id}
|
|
152
|
+
className={cn(inputVariants({ size, roundness, variant: computedVariant, className }))}
|
|
153
|
+
ref={ref}
|
|
154
|
+
{...props}
|
|
155
|
+
/>
|
|
156
|
+
{error && (
|
|
157
|
+
<Typography variant="caption" color="error-600" className="mt-1">
|
|
158
|
+
{error}
|
|
159
|
+
</Typography>
|
|
160
|
+
)}
|
|
161
|
+
</div>
|
|
162
|
+
)
|
|
163
|
+
}
|
|
164
|
+
)
|
|
165
|
+
Input.displayName = "Input"
|
|
166
|
+
|
|
167
|
+
export { Input, inputVariants }
|
|
168
|
+
export type { InputSize, InputRoundness, InputVariant }
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react"
|
|
2
|
+
import * as React from "react"
|
|
3
|
+
import {
|
|
4
|
+
InputOTP,
|
|
5
|
+
InputOTPGroup,
|
|
6
|
+
InputOTPSlot,
|
|
7
|
+
InputOTPSeparator,
|
|
8
|
+
} from "./InputOTP"
|
|
9
|
+
import { Typography } from "../Typography/Typography"
|
|
10
|
+
import { Flex } from "../Flex/Flex"
|
|
11
|
+
|
|
12
|
+
const meta: Meta<typeof InputOTP> = {
|
|
13
|
+
title: "Components/InputOTP",
|
|
14
|
+
component: InputOTP,
|
|
15
|
+
parameters: {
|
|
16
|
+
layout: "centered",
|
|
17
|
+
},
|
|
18
|
+
tags: ["autodocs"],
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default meta
|
|
22
|
+
type Story = StoryObj<typeof InputOTP>
|
|
23
|
+
|
|
24
|
+
export const Default: Story = {
|
|
25
|
+
render: () => (
|
|
26
|
+
<InputOTP maxLength={6}>
|
|
27
|
+
<InputOTPGroup>
|
|
28
|
+
<InputOTPSlot index={0} />
|
|
29
|
+
<InputOTPSlot index={1} />
|
|
30
|
+
<InputOTPSlot index={2} />
|
|
31
|
+
<InputOTPSlot index={3} />
|
|
32
|
+
<InputOTPSlot index={4} />
|
|
33
|
+
<InputOTPSlot index={5} />
|
|
34
|
+
</InputOTPGroup>
|
|
35
|
+
</InputOTP>
|
|
36
|
+
),
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const WithSeparator: Story = {
|
|
40
|
+
render: () => (
|
|
41
|
+
<InputOTP maxLength={6}>
|
|
42
|
+
<InputOTPGroup>
|
|
43
|
+
<InputOTPSlot index={0} />
|
|
44
|
+
<InputOTPSlot index={1} />
|
|
45
|
+
<InputOTPSlot index={2} />
|
|
46
|
+
</InputOTPGroup>
|
|
47
|
+
<InputOTPSeparator />
|
|
48
|
+
<InputOTPGroup>
|
|
49
|
+
<InputOTPSlot index={3} />
|
|
50
|
+
<InputOTPSlot index={4} />
|
|
51
|
+
<InputOTPSlot index={5} />
|
|
52
|
+
</InputOTPGroup>
|
|
53
|
+
</InputOTP>
|
|
54
|
+
),
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const FourDigits: Story = {
|
|
58
|
+
render: () => (
|
|
59
|
+
<InputOTP maxLength={4}>
|
|
60
|
+
<InputOTPGroup>
|
|
61
|
+
<InputOTPSlot index={0} />
|
|
62
|
+
<InputOTPSlot index={1} />
|
|
63
|
+
<InputOTPSlot index={2} />
|
|
64
|
+
<InputOTPSlot index={3} />
|
|
65
|
+
</InputOTPGroup>
|
|
66
|
+
</InputOTP>
|
|
67
|
+
),
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export const Disabled: Story = {
|
|
71
|
+
render: () => (
|
|
72
|
+
<InputOTP maxLength={6} disabled>
|
|
73
|
+
<InputOTPGroup>
|
|
74
|
+
<InputOTPSlot index={0} />
|
|
75
|
+
<InputOTPSlot index={1} />
|
|
76
|
+
<InputOTPSlot index={2} />
|
|
77
|
+
<InputOTPSlot index={3} />
|
|
78
|
+
<InputOTPSlot index={4} />
|
|
79
|
+
<InputOTPSlot index={5} />
|
|
80
|
+
</InputOTPGroup>
|
|
81
|
+
</InputOTP>
|
|
82
|
+
),
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export const Controlled: Story = {
|
|
86
|
+
render: () => {
|
|
87
|
+
const [value, setValue] = React.useState("")
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<Flex gap={4} align="center">
|
|
91
|
+
<InputOTP
|
|
92
|
+
maxLength={6}
|
|
93
|
+
value={value}
|
|
94
|
+
onChange={(value: string) => setValue(value)}
|
|
95
|
+
>
|
|
96
|
+
<InputOTPGroup>
|
|
97
|
+
<InputOTPSlot index={0} />
|
|
98
|
+
<InputOTPSlot index={1} />
|
|
99
|
+
<InputOTPSlot index={2} />
|
|
100
|
+
<InputOTPSlot index={3} />
|
|
101
|
+
<InputOTPSlot index={4} />
|
|
102
|
+
<InputOTPSlot index={5} />
|
|
103
|
+
</InputOTPGroup>
|
|
104
|
+
</InputOTP>
|
|
105
|
+
<Typography variant="body2" color="grey-500">
|
|
106
|
+
Value: {value || "(empty)"}
|
|
107
|
+
</Typography>
|
|
108
|
+
</Flex>
|
|
109
|
+
)
|
|
110
|
+
},
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export const WithPattern: Story = {
|
|
114
|
+
render: () => (
|
|
115
|
+
<Flex gap={4} align="center">
|
|
116
|
+
<Typography variant="body2" color="grey-500">
|
|
117
|
+
Only digits allowed
|
|
118
|
+
</Typography>
|
|
119
|
+
<InputOTP maxLength={6} pattern="^[0-9]+$">
|
|
120
|
+
<InputOTPGroup>
|
|
121
|
+
<InputOTPSlot index={0} />
|
|
122
|
+
<InputOTPSlot index={1} />
|
|
123
|
+
<InputOTPSlot index={2} />
|
|
124
|
+
<InputOTPSlot index={3} />
|
|
125
|
+
<InputOTPSlot index={4} />
|
|
126
|
+
<InputOTPSlot index={5} />
|
|
127
|
+
</InputOTPGroup>
|
|
128
|
+
</InputOTP>
|
|
129
|
+
</Flex>
|
|
130
|
+
),
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export const Sizes: Story = {
|
|
134
|
+
render: () => (
|
|
135
|
+
<Flex gap={6} align="center">
|
|
136
|
+
<div>
|
|
137
|
+
<Typography variant="body2" color="grey-500" className="mb-2 text-center">
|
|
138
|
+
Small
|
|
139
|
+
</Typography>
|
|
140
|
+
<InputOTP maxLength={4}>
|
|
141
|
+
<InputOTPGroup>
|
|
142
|
+
<InputOTPSlot index={0} slotSize="sm" />
|
|
143
|
+
<InputOTPSlot index={1} slotSize="sm" />
|
|
144
|
+
<InputOTPSlot index={2} slotSize="sm" />
|
|
145
|
+
<InputOTPSlot index={3} slotSize="sm" />
|
|
146
|
+
</InputOTPGroup>
|
|
147
|
+
</InputOTP>
|
|
148
|
+
</div>
|
|
149
|
+
<div>
|
|
150
|
+
<Typography variant="body2" color="grey-500" className="mb-2 text-center">
|
|
151
|
+
Default
|
|
152
|
+
</Typography>
|
|
153
|
+
<InputOTP maxLength={4}>
|
|
154
|
+
<InputOTPGroup>
|
|
155
|
+
<InputOTPSlot index={0} />
|
|
156
|
+
<InputOTPSlot index={1} />
|
|
157
|
+
<InputOTPSlot index={2} />
|
|
158
|
+
<InputOTPSlot index={3} />
|
|
159
|
+
</InputOTPGroup>
|
|
160
|
+
</InputOTP>
|
|
161
|
+
</div>
|
|
162
|
+
<div>
|
|
163
|
+
<Typography variant="body2" color="grey-500" className="mb-2 text-center">
|
|
164
|
+
Large
|
|
165
|
+
</Typography>
|
|
166
|
+
<InputOTP maxLength={4}>
|
|
167
|
+
<InputOTPGroup>
|
|
168
|
+
<InputOTPSlot index={0} slotSize="lg" />
|
|
169
|
+
<InputOTPSlot index={1} slotSize="lg" />
|
|
170
|
+
<InputOTPSlot index={2} slotSize="lg" />
|
|
171
|
+
<InputOTPSlot index={3} slotSize="lg" />
|
|
172
|
+
</InputOTPGroup>
|
|
173
|
+
</InputOTP>
|
|
174
|
+
</div>
|
|
175
|
+
</Flex>
|
|
176
|
+
),
|
|
177
|
+
parameters: {
|
|
178
|
+
layout: "padded",
|
|
179
|
+
},
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export const AllVariants: Story = {
|
|
183
|
+
render: () => (
|
|
184
|
+
<Flex gap={8}>
|
|
185
|
+
<div>
|
|
186
|
+
<Typography variant="h4" className="mb-4">6-Digit Code</Typography>
|
|
187
|
+
<InputOTP maxLength={6}>
|
|
188
|
+
<InputOTPGroup>
|
|
189
|
+
<InputOTPSlot index={0} />
|
|
190
|
+
<InputOTPSlot index={1} />
|
|
191
|
+
<InputOTPSlot index={2} />
|
|
192
|
+
<InputOTPSlot index={3} />
|
|
193
|
+
<InputOTPSlot index={4} />
|
|
194
|
+
<InputOTPSlot index={5} />
|
|
195
|
+
</InputOTPGroup>
|
|
196
|
+
</InputOTP>
|
|
197
|
+
</div>
|
|
198
|
+
|
|
199
|
+
<div>
|
|
200
|
+
<Typography variant="h4" className="mb-4">With Separator (3-3)</Typography>
|
|
201
|
+
<InputOTP maxLength={6}>
|
|
202
|
+
<InputOTPGroup>
|
|
203
|
+
<InputOTPSlot index={0} />
|
|
204
|
+
<InputOTPSlot index={1} />
|
|
205
|
+
<InputOTPSlot index={2} />
|
|
206
|
+
</InputOTPGroup>
|
|
207
|
+
<InputOTPSeparator />
|
|
208
|
+
<InputOTPGroup>
|
|
209
|
+
<InputOTPSlot index={3} />
|
|
210
|
+
<InputOTPSlot index={4} />
|
|
211
|
+
<InputOTPSlot index={5} />
|
|
212
|
+
</InputOTPGroup>
|
|
213
|
+
</InputOTP>
|
|
214
|
+
</div>
|
|
215
|
+
|
|
216
|
+
<div>
|
|
217
|
+
<Typography variant="h4" className="mb-4">4-Digit PIN</Typography>
|
|
218
|
+
<InputOTP maxLength={4}>
|
|
219
|
+
<InputOTPGroup>
|
|
220
|
+
<InputOTPSlot index={0} />
|
|
221
|
+
<InputOTPSlot index={1} />
|
|
222
|
+
<InputOTPSlot index={2} />
|
|
223
|
+
<InputOTPSlot index={3} />
|
|
224
|
+
</InputOTPGroup>
|
|
225
|
+
</InputOTP>
|
|
226
|
+
</div>
|
|
227
|
+
|
|
228
|
+
<div>
|
|
229
|
+
<Typography variant="h4" className="mb-4">Disabled State</Typography>
|
|
230
|
+
<InputOTP maxLength={6} disabled>
|
|
231
|
+
<InputOTPGroup>
|
|
232
|
+
<InputOTPSlot index={0} />
|
|
233
|
+
<InputOTPSlot index={1} />
|
|
234
|
+
<InputOTPSlot index={2} />
|
|
235
|
+
<InputOTPSlot index={3} />
|
|
236
|
+
<InputOTPSlot index={4} />
|
|
237
|
+
<InputOTPSlot index={5} />
|
|
238
|
+
</InputOTPGroup>
|
|
239
|
+
</InputOTP>
|
|
240
|
+
</div>
|
|
241
|
+
</Flex>
|
|
242
|
+
),
|
|
243
|
+
parameters: {
|
|
244
|
+
layout: "padded",
|
|
245
|
+
},
|
|
246
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import { OTPInput, OTPInputContext } from "input-otp"
|
|
5
|
+
import { Minus } from "lucide-react"
|
|
6
|
+
import { cva } from "class-variance-authority"
|
|
7
|
+
import { cn } from "../lib/utils"
|
|
8
|
+
|
|
9
|
+
type InputOTPSlotSize = "sm" | "default" | "lg"
|
|
10
|
+
|
|
11
|
+
const inputOTPVariants = cva(
|
|
12
|
+
"flex items-center gap-2 has-[:disabled]:opacity-50",
|
|
13
|
+
{
|
|
14
|
+
variants: {},
|
|
15
|
+
defaultVariants: {},
|
|
16
|
+
}
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
export interface InputOTPProps
|
|
20
|
+
extends Omit<React.ComponentPropsWithoutRef<typeof OTPInput>, "render"> {
|
|
21
|
+
children?: React.ReactNode
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const InputOTP = React.forwardRef<HTMLInputElement, InputOTPProps>(
|
|
25
|
+
({ className, containerClassName, children, ...props }, ref) => (
|
|
26
|
+
<OTPInput
|
|
27
|
+
ref={ref}
|
|
28
|
+
containerClassName={cn(
|
|
29
|
+
"flex items-center gap-2 has-[:disabled]:opacity-50",
|
|
30
|
+
containerClassName
|
|
31
|
+
)}
|
|
32
|
+
className={cn("disabled:cursor-not-allowed", className)}
|
|
33
|
+
{...props}
|
|
34
|
+
render={({ slots, isFocused, isHovering }) => (
|
|
35
|
+
<OTPInputContext.Provider value={{ slots, isFocused, isHovering }}>
|
|
36
|
+
{children}
|
|
37
|
+
</OTPInputContext.Provider>
|
|
38
|
+
)}
|
|
39
|
+
/>
|
|
40
|
+
)
|
|
41
|
+
)
|
|
42
|
+
InputOTP.displayName = "InputOTP"
|
|
43
|
+
|
|
44
|
+
const InputOTPGroup = React.forwardRef<
|
|
45
|
+
HTMLDivElement,
|
|
46
|
+
React.ComponentPropsWithoutRef<"div">
|
|
47
|
+
>(({ className, ...props }, ref) => (
|
|
48
|
+
<div ref={ref} className={cn("flex items-center", className)} {...props} />
|
|
49
|
+
))
|
|
50
|
+
InputOTPGroup.displayName = "InputOTPGroup"
|
|
51
|
+
|
|
52
|
+
const inputOTPSlotVariants = cva(
|
|
53
|
+
// Match Input component styles: border-grey-300, rounded-lg, focus ring
|
|
54
|
+
"relative flex items-center justify-center bg-white border border-grey-300 text-main-950 font-sans transition-all first:rounded-l-lg last:rounded-r-lg first:border-l border-l-0",
|
|
55
|
+
{
|
|
56
|
+
variants: {
|
|
57
|
+
slotSize: {
|
|
58
|
+
sm: "h-8 w-8 text-sm rounded-md first:rounded-l-md last:rounded-r-md",
|
|
59
|
+
default: "h-9 w-9 text-sm",
|
|
60
|
+
lg: "h-10 w-10 text-base",
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
defaultVariants: {
|
|
64
|
+
slotSize: "default",
|
|
65
|
+
},
|
|
66
|
+
}
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
export interface InputOTPSlotProps
|
|
70
|
+
extends React.ComponentPropsWithoutRef<"div"> {
|
|
71
|
+
index: number
|
|
72
|
+
slotSize?: InputOTPSlotSize
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const InputOTPSlot = React.forwardRef<HTMLDivElement, InputOTPSlotProps>(
|
|
76
|
+
({ index, className, slotSize, ...props }, ref) => {
|
|
77
|
+
const inputOTPContext = React.useContext(OTPInputContext)
|
|
78
|
+
const slot = inputOTPContext.slots[index]
|
|
79
|
+
|
|
80
|
+
if (!slot) {
|
|
81
|
+
return null
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const { char, hasFakeCaret, isActive } = slot
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<div
|
|
88
|
+
ref={ref}
|
|
89
|
+
className={cn(
|
|
90
|
+
inputOTPSlotVariants({ slotSize }),
|
|
91
|
+
// Match Input focus style: ring-[3px] ring-grey-300/50
|
|
92
|
+
isActive && "z-10 border-grey-300 ring-[3px] ring-grey-300/50",
|
|
93
|
+
className
|
|
94
|
+
)}
|
|
95
|
+
{...props}
|
|
96
|
+
>
|
|
97
|
+
{char}
|
|
98
|
+
{hasFakeCaret && (
|
|
99
|
+
<div className="pointer-events-none absolute inset-0 flex items-center justify-center">
|
|
100
|
+
<div className="h-4 w-px animate-caret-blink bg-main-950 duration-1000" />
|
|
101
|
+
</div>
|
|
102
|
+
)}
|
|
103
|
+
</div>
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
)
|
|
107
|
+
InputOTPSlot.displayName = "InputOTPSlot"
|
|
108
|
+
|
|
109
|
+
const InputOTPSeparator = React.forwardRef<
|
|
110
|
+
HTMLDivElement,
|
|
111
|
+
React.ComponentPropsWithoutRef<"div">
|
|
112
|
+
>(({ ...props }, ref) => (
|
|
113
|
+
<div ref={ref} role="separator" {...props}>
|
|
114
|
+
<Minus className="text-grey-400" />
|
|
115
|
+
</div>
|
|
116
|
+
))
|
|
117
|
+
InputOTPSeparator.displayName = "InputOTPSeparator"
|
|
118
|
+
|
|
119
|
+
export {
|
|
120
|
+
InputOTP,
|
|
121
|
+
InputOTPGroup,
|
|
122
|
+
InputOTPSlot,
|
|
123
|
+
InputOTPSeparator,
|
|
124
|
+
inputOTPVariants,
|
|
125
|
+
inputOTPSlotVariants,
|
|
126
|
+
}
|
|
127
|
+
export type { InputOTPSlotSize }
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react"
|
|
2
|
+
|
|
3
|
+
import { Input } from "../Input/Input"
|
|
4
|
+
import { Checkbox } from "../Checkbox/Checkbox"
|
|
5
|
+
import { Flex } from "../Flex/Flex"
|
|
6
|
+
import { Typography } from "../Typography/Typography"
|
|
7
|
+
import { Label } from "./Label"
|
|
8
|
+
|
|
9
|
+
const meta: Meta<typeof Label> = {
|
|
10
|
+
title: "Components/Label",
|
|
11
|
+
component: Label,
|
|
12
|
+
parameters: {
|
|
13
|
+
layout: "centered",
|
|
14
|
+
},
|
|
15
|
+
argTypes: {
|
|
16
|
+
textVariant: {
|
|
17
|
+
control: "select",
|
|
18
|
+
options: ["h1", "h2", "h3", "h4", "h5", "h6", "subtitle1", "subtitle2", "subtitle3", "body1", "body2", "body3", "caption"],
|
|
19
|
+
description: "The text variant of the label",
|
|
20
|
+
},
|
|
21
|
+
color: {
|
|
22
|
+
control: "select",
|
|
23
|
+
options: ["default", "main-950", "main-500", "grey-500", "error-500"],
|
|
24
|
+
description: "The color of the label",
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
tags: ["autodocs"],
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export default meta
|
|
31
|
+
type Story = StoryObj<typeof meta>
|
|
32
|
+
|
|
33
|
+
export const Default: Story = {
|
|
34
|
+
render: () => <Label>Email address</Label>,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const WithInput: Story = {
|
|
38
|
+
render: () => (
|
|
39
|
+
<Flex gap={2} className="w-[300px]">
|
|
40
|
+
<Label htmlFor="email">Email</Label>
|
|
41
|
+
<Input type="email" id="email" placeholder="Enter your email" />
|
|
42
|
+
</Flex>
|
|
43
|
+
),
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const WithCheckbox: Story = {
|
|
47
|
+
render: () => (
|
|
48
|
+
<Flex direction="row" align="center" gap={2}>
|
|
49
|
+
<Checkbox id="terms" />
|
|
50
|
+
<Label htmlFor="terms">Accept terms and conditions</Label>
|
|
51
|
+
</Flex>
|
|
52
|
+
),
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const Muted: Story = {
|
|
56
|
+
render: () => <Label color="grey-500">Optional field</Label>,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const Error: Story = {
|
|
60
|
+
render: () => (
|
|
61
|
+
<Flex gap={2} className="w-[300px]">
|
|
62
|
+
<Label htmlFor="email-error" color="error-500">Email</Label>
|
|
63
|
+
<Input type="email" id="email-error" variant="error" placeholder="Enter your email" />
|
|
64
|
+
<Typography variant="caption" color="error-500">Please enter a valid email address</Typography>
|
|
65
|
+
</Flex>
|
|
66
|
+
),
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const AllVariants: Story = {
|
|
70
|
+
render: () => (
|
|
71
|
+
<Flex gap={6}>
|
|
72
|
+
<Typography variant="h4" className="text-white">Label Variants</Typography>
|
|
73
|
+
|
|
74
|
+
<Flex gap={4}>
|
|
75
|
+
<Flex gap={2} className="w-[300px]">
|
|
76
|
+
<Label htmlFor="default">Default Label</Label>
|
|
77
|
+
<Input id="default" placeholder="Default input" />
|
|
78
|
+
</Flex>
|
|
79
|
+
|
|
80
|
+
<Flex gap={2} className="w-[300px]">
|
|
81
|
+
<Flex direction="row" gap={2} align="center">
|
|
82
|
+
<Label htmlFor="with-optional">Name</Label>
|
|
83
|
+
<Label color="grey-500">(optional)</Label>
|
|
84
|
+
</Flex>
|
|
85
|
+
<Input id="with-optional" placeholder="Enter your name" />
|
|
86
|
+
</Flex>
|
|
87
|
+
|
|
88
|
+
<Flex gap={2} className="w-[300px]">
|
|
89
|
+
<Label htmlFor="error-field" color="error-500">Password</Label>
|
|
90
|
+
<Input id="error-field" type="password" variant="error" placeholder="Enter password" />
|
|
91
|
+
<Typography variant="caption" color="error-500">Password must be at least 8 characters</Typography>
|
|
92
|
+
</Flex>
|
|
93
|
+
</Flex>
|
|
94
|
+
|
|
95
|
+
<Typography variant="h4" className="text-white mt-4">With Form Controls</Typography>
|
|
96
|
+
|
|
97
|
+
<Flex gap={3}>
|
|
98
|
+
<Flex direction="row" align="center" gap={2}>
|
|
99
|
+
<Checkbox id="newsletter" />
|
|
100
|
+
<Label htmlFor="newsletter">Subscribe to newsletter</Label>
|
|
101
|
+
</Flex>
|
|
102
|
+
|
|
103
|
+
<Flex direction="row" align="center" gap={2}>
|
|
104
|
+
<Checkbox id="updates" defaultChecked />
|
|
105
|
+
<Label htmlFor="updates">Receive product updates</Label>
|
|
106
|
+
</Flex>
|
|
107
|
+
</Flex>
|
|
108
|
+
</Flex>
|
|
109
|
+
),
|
|
110
|
+
}
|
package/Label/Label.tsx
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as LabelPrimitive from "@radix-ui/react-label";
|
|
3
|
+
import { cva } from "class-variance-authority";
|
|
4
|
+
import { cn } from "../lib/utils";
|
|
5
|
+
import {
|
|
6
|
+
type TextVariant,
|
|
7
|
+
type TextColor,
|
|
8
|
+
textVariantClasses,
|
|
9
|
+
textColorClasses,
|
|
10
|
+
} from "../lib/typography-types";
|
|
11
|
+
|
|
12
|
+
const labelVariants = cva(
|
|
13
|
+
"font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
|
|
14
|
+
{
|
|
15
|
+
variants: {
|
|
16
|
+
textVariant: textVariantClasses,
|
|
17
|
+
color: textColorClasses,
|
|
18
|
+
},
|
|
19
|
+
defaultVariants: {
|
|
20
|
+
textVariant: "body2",
|
|
21
|
+
color: "main-950",
|
|
22
|
+
},
|
|
23
|
+
}
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
export interface LabelProps
|
|
27
|
+
extends React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> {
|
|
28
|
+
variant?: TextVariant;
|
|
29
|
+
color?: TextColor;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const Label = React.forwardRef<
|
|
33
|
+
React.ElementRef<typeof LabelPrimitive.Root>,
|
|
34
|
+
LabelProps
|
|
35
|
+
>(({ className, variant, color, ...props }, ref) => (
|
|
36
|
+
<LabelPrimitive.Root
|
|
37
|
+
ref={ref}
|
|
38
|
+
className={cn(labelVariants({ textVariant: variant, color }), className)}
|
|
39
|
+
{...props}
|
|
40
|
+
/>
|
|
41
|
+
));
|
|
42
|
+
Label.displayName = LabelPrimitive.Root.displayName;
|
|
43
|
+
|
|
44
|
+
export { Label, labelVariants };
|