azamat-ui-kit-cli 0.2.2 → 0.3.4
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/README.md +11 -0
- package/dist/index.cjs +452 -0
- package/package.json +2 -2
- package/vendor/src/components/actions/action-menu.tsx +21 -18
- package/vendor/src/components/calendar/calendar.tsx +153 -102
- package/vendor/src/components/calendar/date-picker.tsx +24 -14
- package/vendor/src/components/calendar/date-range-picker.tsx +137 -58
- package/vendor/src/components/charts/charts.tsx +32 -21
- package/vendor/src/components/command/command-palette.tsx +68 -57
- package/vendor/src/components/data-table/data-table-bulk-actions.tsx +23 -20
- package/vendor/src/components/data-table/data-table-column-visibility-menu.tsx +21 -10
- package/vendor/src/components/data-table/data-table-pagination.tsx +6 -6
- package/vendor/src/components/data-table/data-table-toolbar.tsx +72 -44
- package/vendor/src/components/data-table/data-table.tsx +15 -11
- package/vendor/src/components/data-table/table-export-menu.tsx +1 -1
- package/vendor/src/components/data-table/table-import-button.tsx +1 -1
- package/vendor/src/components/display/data-state.tsx +20 -8
- package/vendor/src/components/display/index.ts +19 -15
- package/vendor/src/components/display/metric-card.tsx +35 -0
- package/vendor/src/components/display/progress-circle.tsx +24 -0
- package/vendor/src/components/display/smart-card.tsx +49 -27
- package/vendor/src/components/display/status-dot.tsx +45 -0
- package/vendor/src/components/display/user-card.tsx +30 -0
- package/vendor/src/components/feedback/alert.tsx +21 -11
- package/vendor/src/components/feedback/empty-state.tsx +2 -2
- package/vendor/src/components/feedback/loading-state.tsx +2 -2
- package/vendor/src/components/feedback/page-state.tsx +19 -15
- package/vendor/src/components/feedback/status-badge.tsx +43 -43
- package/vendor/src/components/form/form-app-input.tsx +147 -0
- package/vendor/src/components/form/form-date-input.tsx +16 -19
- package/vendor/src/components/form/form-field-shell.tsx +11 -8
- package/vendor/src/components/form/form-field-utils.ts +76 -0
- package/vendor/src/components/form/form-input.tsx +423 -44
- package/vendor/src/components/form/form-number-input.tsx +16 -15
- package/vendor/src/components/form/form-phone-input.tsx +15 -9
- package/vendor/src/components/form/form-search-input.tsx +16 -19
- package/vendor/src/components/form/form-select.tsx +4 -3
- package/vendor/src/components/form/public.ts +16 -14
- package/vendor/src/components/form/smart-form-shell.tsx +13 -12
- package/vendor/src/components/inputs/app-input.tsx +27 -0
- package/vendor/src/components/inputs/async-select.tsx +113 -84
- package/vendor/src/components/inputs/clearable-input.tsx +81 -61
- package/vendor/src/components/inputs/date-input.tsx +21 -17
- package/vendor/src/components/inputs/date-range-input.tsx +10 -10
- package/vendor/src/components/inputs/index.ts +1 -0
- package/vendor/src/components/inputs/input-decorator.tsx +101 -57
- package/vendor/src/components/inputs/masked-input.tsx +20 -20
- package/vendor/src/components/inputs/money-input.tsx +2 -2
- package/vendor/src/components/inputs/number-input.tsx +29 -19
- package/vendor/src/components/inputs/password-input.tsx +82 -45
- package/vendor/src/components/inputs/phone-input.tsx +24 -2
- package/vendor/src/components/inputs/quantity-input.tsx +2 -2
- package/vendor/src/components/inputs/search-input.tsx +54 -3
- package/vendor/src/components/inputs/simple-select.tsx +110 -22
- package/vendor/src/components/layout/app-shell.tsx +2 -2
- package/vendor/src/components/layout/index.ts +5 -4
- package/vendor/src/components/layout/page-header.tsx +79 -35
- package/vendor/src/components/layout/public.ts +12 -10
- package/vendor/src/components/layout/section-header.tsx +56 -0
- package/vendor/src/components/layout/stack.tsx +106 -0
- package/vendor/src/components/layout/stat-card.tsx +66 -29
- package/vendor/src/components/navigation/index.ts +1 -0
- package/vendor/src/components/navigation/nav-tabs.tsx +60 -0
- package/vendor/src/components/navigation/page-tabs.tsx +41 -26
- package/vendor/src/components/navigation/pagination.tsx +14 -10
- package/vendor/src/components/overlay/alert-dialog.tsx +65 -0
- package/vendor/src/components/overlay/drawer.tsx +71 -0
- package/vendor/src/components/overlay/index.ts +4 -2
- package/vendor/src/components/patterns/data-view.tsx +13 -8
- package/vendor/src/components/ui/badge.tsx +96 -52
- package/vendor/src/components/ui/button.tsx +99 -61
- package/vendor/src/components/ui/card.tsx +84 -25
- package/vendor/src/components/ui/checkbox.tsx +68 -68
- package/vendor/src/components/ui/command.tsx +32 -32
- package/vendor/src/components/ui/dialog.tsx +135 -138
- package/vendor/src/components/ui/dropdown-menu.tsx +21 -21
- package/vendor/src/components/ui/hover-card.tsx +49 -0
- package/vendor/src/components/ui/input-primitive.tsx +24 -0
- package/vendor/src/components/ui/input.tsx +191 -20
- package/vendor/src/components/ui/kbd.tsx +33 -0
- package/vendor/src/components/ui/popover.tsx +11 -11
- package/vendor/src/components/ui/radio-group.tsx +102 -0
- package/vendor/src/components/ui/right-click-menu.tsx +60 -0
- package/vendor/src/components/ui/scroll-box.tsx +27 -0
- package/vendor/src/components/ui/segmented-control.tsx +21 -17
- package/vendor/src/components/ui/select.tsx +187 -189
- package/vendor/src/components/ui/skeleton.tsx +2 -2
- package/vendor/src/components/ui/switch.tsx +60 -60
- package/vendor/src/components/ui/table.tsx +114 -114
- package/vendor/src/components/ui/tabs.tsx +2 -2
- package/vendor/src/components/ui/textarea.tsx +1 -1
- package/vendor/src/components/upload/file-dropzone.tsx +38 -0
- package/vendor/src/components/upload/file-upload.tsx +4 -4
- package/vendor/src/components/upload/image-upload.tsx +22 -19
- package/vendor/src/components/upload/index.ts +2 -0
- package/vendor/src/families/catalog.ts +1 -0
- package/vendor/src/families/docs-groups.ts +10 -1
- package/vendor/src/families/member-metadata.ts +24 -0
- package/vendor/src/families/member-snippets.ts +41 -2
- package/vendor/src/families/migration-map.ts +3 -0
- package/vendor/src/index.ts +23 -18
- package/vendor/templates/styles/globals.css +253 -0
- package/dist/index.js +0 -432
|
@@ -32,21 +32,25 @@ function defaultPageStateIcon(tone: PageStateTone) {
|
|
|
32
32
|
|
|
33
33
|
function PageState({ tone = "empty", title, description, icon, action, compact = false, className, ...props }: PageStateProps) {
|
|
34
34
|
return (
|
|
35
|
-
<div
|
|
36
|
-
data-slot="page-state"
|
|
37
|
-
role={tone === "error" ? "alert" : "status"}
|
|
38
|
-
className={cn(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
{
|
|
47
|
-
</div>
|
|
48
|
-
|
|
49
|
-
|
|
35
|
+
<div
|
|
36
|
+
data-slot="page-state"
|
|
37
|
+
role={tone === "error" ? "alert" : "status"}
|
|
38
|
+
className={cn(
|
|
39
|
+
"flex flex-col items-center justify-center rounded-[var(--radius-3xl)] border border-border/75 bg-card/96 text-center shadow-sm ring-1 ring-foreground/4",
|
|
40
|
+
compact ? "gap-2 p-6" : "min-h-72 gap-4 p-10",
|
|
41
|
+
className
|
|
42
|
+
)}
|
|
43
|
+
{...props}
|
|
44
|
+
>
|
|
45
|
+
<div className="flex size-14 items-center justify-center rounded-full border border-border/70 bg-muted/45 text-muted-foreground shadow-[0_1px_0_rgba(255,255,255,0.05)]">
|
|
46
|
+
{icon ?? defaultPageStateIcon(tone)}
|
|
47
|
+
</div>
|
|
48
|
+
<div className="grid gap-1">
|
|
49
|
+
{title && <div className="text-base font-semibold tracking-tight text-foreground">{title}</div>}
|
|
50
|
+
{description && <div className="max-w-md text-sm leading-6 text-muted-foreground">{description}</div>}
|
|
51
|
+
</div>
|
|
52
|
+
{action && <div>{action}</div>}
|
|
53
|
+
</div>
|
|
50
54
|
)
|
|
51
55
|
}
|
|
52
56
|
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
|
|
3
|
-
import { Badge, badgeVariants } from "@/components/ui/badge"
|
|
4
|
-
import { cn } from "@/lib/utils"
|
|
5
|
-
|
|
6
|
-
type BadgeVariant = NonNullable<Parameters<typeof badgeVariants>[0]>["variant"]
|
|
7
|
-
|
|
8
|
-
export type StatusBadgeTone =
|
|
9
|
-
| "default"
|
|
10
|
-
| "success"
|
|
11
|
-
| "warning"
|
|
12
|
-
| "danger"
|
|
13
|
-
| "info"
|
|
14
|
-
| "muted"
|
|
15
|
-
| "outline"
|
|
16
|
-
|
|
17
|
-
export type StatusBadgeProps = React.ComponentProps<typeof Badge> & {
|
|
18
|
-
tone?: StatusBadgeTone
|
|
19
|
-
dot?: boolean
|
|
20
|
-
}
|
|
21
|
-
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
|
|
3
|
+
import { Badge, badgeVariants } from "@/components/ui/badge"
|
|
4
|
+
import { cn } from "@/lib/utils"
|
|
5
|
+
|
|
6
|
+
type BadgeVariant = NonNullable<Parameters<typeof badgeVariants>[0]>["variant"]
|
|
7
|
+
|
|
8
|
+
export type StatusBadgeTone =
|
|
9
|
+
| "default"
|
|
10
|
+
| "success"
|
|
11
|
+
| "warning"
|
|
12
|
+
| "danger"
|
|
13
|
+
| "info"
|
|
14
|
+
| "muted"
|
|
15
|
+
| "outline"
|
|
16
|
+
|
|
17
|
+
export type StatusBadgeProps = Omit<React.ComponentProps<typeof Badge>, "tone" | "dot"> & {
|
|
18
|
+
tone?: StatusBadgeTone
|
|
19
|
+
dot?: boolean
|
|
20
|
+
}
|
|
21
|
+
|
|
22
22
|
const toneClassName: Record<StatusBadgeTone, string> = {
|
|
23
23
|
default: "border-primary/10 bg-primary/8 text-primary shadow-sm",
|
|
24
24
|
success: "border-emerald-500/20 bg-emerald-500/12 text-emerald-700 shadow-sm dark:text-emerald-300",
|
|
@@ -28,26 +28,26 @@ const toneClassName: Record<StatusBadgeTone, string> = {
|
|
|
28
28
|
muted: "border-border/70 bg-muted/80 text-muted-foreground",
|
|
29
29
|
outline: "border-border/80 bg-background/90 text-foreground shadow-sm",
|
|
30
30
|
}
|
|
31
|
-
|
|
32
|
-
const toneVariant: Record<StatusBadgeTone, BadgeVariant> = {
|
|
33
|
-
default: "default",
|
|
34
|
-
success: "secondary",
|
|
35
|
-
warning: "secondary",
|
|
36
|
-
danger: "destructive",
|
|
37
|
-
info: "secondary",
|
|
38
|
-
muted: "secondary",
|
|
39
|
-
outline: "outline",
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function StatusBadge({
|
|
43
|
-
className,
|
|
44
|
-
tone = "default",
|
|
45
|
-
dot = false,
|
|
46
|
-
children,
|
|
47
|
-
...props
|
|
48
|
-
}: StatusBadgeProps) {
|
|
49
|
-
return (
|
|
50
|
-
<Badge
|
|
31
|
+
|
|
32
|
+
const toneVariant: Record<StatusBadgeTone, BadgeVariant> = {
|
|
33
|
+
default: "default",
|
|
34
|
+
success: "secondary",
|
|
35
|
+
warning: "secondary",
|
|
36
|
+
danger: "destructive",
|
|
37
|
+
info: "secondary",
|
|
38
|
+
muted: "secondary",
|
|
39
|
+
outline: "outline",
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function StatusBadge({
|
|
43
|
+
className,
|
|
44
|
+
tone = "default",
|
|
45
|
+
dot = false,
|
|
46
|
+
children,
|
|
47
|
+
...props
|
|
48
|
+
}: StatusBadgeProps) {
|
|
49
|
+
return (
|
|
50
|
+
<Badge
|
|
51
51
|
data-slot="status-badge"
|
|
52
52
|
variant={toneVariant[tone]}
|
|
53
53
|
className={cn("rounded-full px-2.5 py-1 text-[11px] font-semibold tracking-[0.01em]", toneClassName[tone], className)}
|
|
@@ -58,5 +58,5 @@ function StatusBadge({
|
|
|
58
58
|
</Badge>
|
|
59
59
|
)
|
|
60
60
|
}
|
|
61
|
-
|
|
62
|
-
export { StatusBadge }
|
|
61
|
+
|
|
62
|
+
export { StatusBadge }
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { type FieldPath, type FieldValues } from "react-hook-form"
|
|
2
|
+
|
|
3
|
+
import { AppInputKind } from "@/components/inputs/app-input"
|
|
4
|
+
import {
|
|
5
|
+
FormInput,
|
|
6
|
+
type FormInputDateVariantProps,
|
|
7
|
+
type FormInputDateRangeVariantProps,
|
|
8
|
+
type FormInputClearableVariantProps,
|
|
9
|
+
type FormInputMaskedVariantProps,
|
|
10
|
+
type FormInputMoneyVariantProps,
|
|
11
|
+
type FormInputQuantityVariantProps,
|
|
12
|
+
type FormInputNumberVariantProps,
|
|
13
|
+
type FormInputPasswordVariantProps,
|
|
14
|
+
type FormInputPhoneVariantProps,
|
|
15
|
+
type FormInputSearchVariantProps,
|
|
16
|
+
type FormTextInputProps,
|
|
17
|
+
} from "@/components/form/form-input"
|
|
18
|
+
|
|
19
|
+
export type FormAppInputPhoneValueMode = "raw" | "masked"
|
|
20
|
+
type FormAppInputVariantProps<
|
|
21
|
+
TFieldValues extends FieldValues = FieldValues,
|
|
22
|
+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
|
23
|
+
> =
|
|
24
|
+
| Omit<FormTextInputProps<TFieldValues, TName>, "kind" | "onValueChange">
|
|
25
|
+
| Omit<FormInputSearchVariantProps<TFieldValues, TName>, "kind">
|
|
26
|
+
| Omit<FormInputPasswordVariantProps<TFieldValues, TName>, "kind">
|
|
27
|
+
| Omit<FormInputNumberVariantProps<TFieldValues, TName>, "kind">
|
|
28
|
+
| Omit<FormInputPhoneVariantProps<TFieldValues, TName>, "kind">
|
|
29
|
+
| Omit<FormInputDateVariantProps<TFieldValues, TName>, "kind">
|
|
30
|
+
| Omit<FormInputDateRangeVariantProps<TFieldValues, TName>, "kind">
|
|
31
|
+
| Omit<FormInputClearableVariantProps<TFieldValues, TName>, "kind">
|
|
32
|
+
| Omit<FormInputMaskedVariantProps<TFieldValues, TName>, "kind">
|
|
33
|
+
| Omit<FormInputMoneyVariantProps<TFieldValues, TName>, "kind">
|
|
34
|
+
| Omit<FormInputQuantityVariantProps<TFieldValues, TName>, "kind">
|
|
35
|
+
|
|
36
|
+
export type FormAppInputProps<
|
|
37
|
+
TFieldValues extends FieldValues = FieldValues,
|
|
38
|
+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
|
39
|
+
> = FormAppInputVariantProps<TFieldValues, TName> & {
|
|
40
|
+
kind?: AppInputKind
|
|
41
|
+
phoneValueMode?: FormAppInputPhoneValueMode
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @deprecated Use {@link FormInput} directly.
|
|
46
|
+
*/
|
|
47
|
+
function FormAppInput<TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>>(
|
|
48
|
+
props: FormAppInputProps<TFieldValues, TName>
|
|
49
|
+
) {
|
|
50
|
+
const { phoneValueMode = "raw", kind = "text", ...rest } = props
|
|
51
|
+
|
|
52
|
+
const safeRest = rest as unknown as Record<string, unknown>
|
|
53
|
+
|
|
54
|
+
if (kind === "text") {
|
|
55
|
+
return <FormInput {...(safeRest as Omit<FormTextInputProps<TFieldValues, TName>, "kind">)} kind="text" />
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (kind === "search") {
|
|
59
|
+
return (
|
|
60
|
+
<FormInput
|
|
61
|
+
{...(safeRest as Omit<FormInputSearchVariantProps<TFieldValues, TName>, "kind">)}
|
|
62
|
+
kind="search"
|
|
63
|
+
/>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (kind === "password") {
|
|
68
|
+
return (
|
|
69
|
+
<FormInput
|
|
70
|
+
{...(safeRest as Omit<FormInputPasswordVariantProps<TFieldValues, TName>, "kind">)}
|
|
71
|
+
kind="password"
|
|
72
|
+
/>
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (kind === "number") {
|
|
77
|
+
return (
|
|
78
|
+
<FormInput
|
|
79
|
+
{...(safeRest as Omit<FormInputNumberVariantProps<TFieldValues, TName>, "kind">)}
|
|
80
|
+
kind="number"
|
|
81
|
+
/>
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (kind === "phone") {
|
|
86
|
+
return (
|
|
87
|
+
<FormInput
|
|
88
|
+
{...(safeRest as Omit<FormInputPhoneVariantProps<TFieldValues, TName>, "kind">)}
|
|
89
|
+
kind="phone"
|
|
90
|
+
valueMode={phoneValueMode}
|
|
91
|
+
/>
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (kind === "date") {
|
|
96
|
+
return (
|
|
97
|
+
<FormInput
|
|
98
|
+
{...(safeRest as Omit<FormInputDateVariantProps<TFieldValues, TName>, "kind">)}
|
|
99
|
+
kind="date"
|
|
100
|
+
/>
|
|
101
|
+
)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (kind === "date-range") {
|
|
105
|
+
return (
|
|
106
|
+
<FormInput
|
|
107
|
+
{...(safeRest as Omit<FormInputDateRangeVariantProps<TFieldValues, TName>, "kind">)}
|
|
108
|
+
kind="date-range"
|
|
109
|
+
/>
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (kind === "clearable") {
|
|
114
|
+
return (
|
|
115
|
+
<FormInput
|
|
116
|
+
{...(safeRest as Omit<FormInputClearableVariantProps<TFieldValues, TName>, "kind">)}
|
|
117
|
+
kind="clearable"
|
|
118
|
+
/>
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (kind === "masked") {
|
|
123
|
+
return (
|
|
124
|
+
<FormInput
|
|
125
|
+
{...(safeRest as Omit<FormInputMaskedVariantProps<TFieldValues, TName>, "kind">)}
|
|
126
|
+
kind="masked"
|
|
127
|
+
/>
|
|
128
|
+
)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (kind === "money") {
|
|
132
|
+
return <FormInput {...(safeRest as Omit<FormInputMoneyVariantProps<TFieldValues, TName>, "kind">)} kind="money" />
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (kind === "quantity") {
|
|
136
|
+
return (
|
|
137
|
+
<FormInput
|
|
138
|
+
{...(safeRest as Omit<FormInputQuantityVariantProps<TFieldValues, TName>, "kind">)}
|
|
139
|
+
kind="quantity"
|
|
140
|
+
/>
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return <FormInput {...(safeRest as Omit<FormTextInputProps<TFieldValues, TName>, "kind">)} kind="text" />
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export { FormAppInput }
|
|
@@ -1,19 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export { FormDateInput }
|
|
1
|
+
import { FormAppInput, type FormAppInputProps } from "@/components/form/form-app-input"
|
|
2
|
+
import type { FieldPath, FieldValues } from "react-hook-form"
|
|
3
|
+
|
|
4
|
+
export type FormDateInputProps<
|
|
5
|
+
TFieldValues extends FieldValues = FieldValues,
|
|
6
|
+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
|
7
|
+
> = Omit<FormAppInputProps<TFieldValues, TName>, "kind">
|
|
8
|
+
|
|
9
|
+
function FormDateInput<
|
|
10
|
+
TFieldValues extends FieldValues = FieldValues,
|
|
11
|
+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
|
12
|
+
>(props: FormDateInputProps<TFieldValues, TName>) {
|
|
13
|
+
return <FormAppInput {...props} kind="date" />
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export { FormDateInput }
|
|
@@ -96,7 +96,7 @@ function FormFieldShell({
|
|
|
96
96
|
data-slot="form-field-label"
|
|
97
97
|
htmlFor={htmlFor}
|
|
98
98
|
className={cn(
|
|
99
|
-
"min-w-0 text-sm font-semibold leading-none text-foreground",
|
|
99
|
+
"min-w-0 text-sm font-semibold leading-none tracking-tight text-foreground",
|
|
100
100
|
disabled && "cursor-not-allowed opacity-60",
|
|
101
101
|
readOnly && "opacity-80",
|
|
102
102
|
labelClassName
|
|
@@ -132,10 +132,13 @@ function FormFieldShell({
|
|
|
132
132
|
</div>
|
|
133
133
|
)
|
|
134
134
|
|
|
135
|
-
const errorNode = error ? (
|
|
135
|
+
const errorNode = error ? (
|
|
136
136
|
<p
|
|
137
137
|
data-slot="form-field-error"
|
|
138
|
-
className={cn(
|
|
138
|
+
className={cn(
|
|
139
|
+
"flex items-start gap-2 rounded-[min(var(--radius-xl),16px)] border border-destructive/18 bg-destructive/8 px-3 py-2 text-sm font-medium leading-6 text-destructive",
|
|
140
|
+
errorClassName
|
|
141
|
+
)}
|
|
139
142
|
>
|
|
140
143
|
{showErrorIcon && <span className="mt-1 shrink-0">{resolvedErrorIcon}</span>}
|
|
141
144
|
<span className="min-w-0">{error}</span>
|
|
@@ -146,11 +149,11 @@ function FormFieldShell({
|
|
|
146
149
|
<div
|
|
147
150
|
data-slot="form-field-shell"
|
|
148
151
|
data-layout={layout}
|
|
149
|
-
data-invalid={Boolean(error) || undefined}
|
|
150
|
-
data-disabled={disabled || undefined}
|
|
151
|
-
data-readonly={readOnly || undefined}
|
|
152
|
-
aria-disabled={disabled || undefined}
|
|
153
|
-
className={cn(layoutClassName[layout], className)}
|
|
152
|
+
data-invalid={Boolean(error) || undefined}
|
|
153
|
+
data-disabled={disabled || undefined}
|
|
154
|
+
data-readonly={readOnly || undefined}
|
|
155
|
+
aria-disabled={disabled || undefined}
|
|
156
|
+
className={cn(layoutClassName[layout], "rounded-[var(--radius-2xl)]", className)}
|
|
154
157
|
{...props}
|
|
155
158
|
>
|
|
156
159
|
{layout === "horizontal" ? (
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { FieldPath, FieldValues, Control } from "react-hook-form"
|
|
2
|
+
|
|
3
|
+
import type { FormFieldShellControlProps } from "@/components/form/form-field-shell"
|
|
4
|
+
|
|
5
|
+
export type FormControlledFieldProps<
|
|
6
|
+
TFieldValues extends FieldValues,
|
|
7
|
+
TName extends FieldPath<TFieldValues>,
|
|
8
|
+
> = FormFieldShellControlProps & {
|
|
9
|
+
control: Control<TFieldValues>
|
|
10
|
+
name: TName
|
|
11
|
+
fieldClassName?: string
|
|
12
|
+
id?: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const formShellPropKeys = [
|
|
16
|
+
"label",
|
|
17
|
+
"description",
|
|
18
|
+
"required",
|
|
19
|
+
"className",
|
|
20
|
+
"layout",
|
|
21
|
+
"descriptionPosition",
|
|
22
|
+
"labelAction",
|
|
23
|
+
"requiredIndicator",
|
|
24
|
+
"errorIcon",
|
|
25
|
+
"showErrorIcon",
|
|
26
|
+
"disabled",
|
|
27
|
+
"readOnly",
|
|
28
|
+
"labelClassName",
|
|
29
|
+
"labelRowClassName",
|
|
30
|
+
"descriptionClassName",
|
|
31
|
+
"errorClassName",
|
|
32
|
+
"contentClassName",
|
|
33
|
+
] as const
|
|
34
|
+
|
|
35
|
+
const controlledFieldPropKeys = ["control", "name", "fieldClassName", "id", ...formShellPropKeys] as const
|
|
36
|
+
|
|
37
|
+
export function pickFormFieldShellProps(props: FormFieldShellControlProps): FormFieldShellControlProps {
|
|
38
|
+
return {
|
|
39
|
+
label: props.label,
|
|
40
|
+
description: props.description,
|
|
41
|
+
required: props.required,
|
|
42
|
+
className: props.className,
|
|
43
|
+
layout: props.layout,
|
|
44
|
+
descriptionPosition: props.descriptionPosition,
|
|
45
|
+
labelAction: props.labelAction,
|
|
46
|
+
requiredIndicator: props.requiredIndicator,
|
|
47
|
+
errorIcon: props.errorIcon,
|
|
48
|
+
showErrorIcon: props.showErrorIcon,
|
|
49
|
+
disabled: props.disabled,
|
|
50
|
+
readOnly: props.readOnly,
|
|
51
|
+
labelClassName: props.labelClassName,
|
|
52
|
+
labelRowClassName: props.labelRowClassName,
|
|
53
|
+
descriptionClassName: props.descriptionClassName,
|
|
54
|
+
errorClassName: props.errorClassName,
|
|
55
|
+
contentClassName: props.contentClassName,
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function splitFormControlledProps<
|
|
60
|
+
TProps extends FormControlledFieldProps<FieldValues, FieldPath<FieldValues>>,
|
|
61
|
+
>(props: TProps) {
|
|
62
|
+
const inputProps = { ...props } as Record<string, unknown>
|
|
63
|
+
|
|
64
|
+
controlledFieldPropKeys.forEach((key) => {
|
|
65
|
+
delete inputProps[key]
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
control: props.control,
|
|
70
|
+
name: props.name,
|
|
71
|
+
id: props.id ?? props.name,
|
|
72
|
+
fieldClassName: props.fieldClassName,
|
|
73
|
+
shellProps: pickFormFieldShellProps(props),
|
|
74
|
+
inputProps,
|
|
75
|
+
}
|
|
76
|
+
}
|