generator-kodly-react-app 1.0.14 → 1.0.16
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/generators/app/index.js +4 -0
- package/generators/app/templates/.env.dev +1 -1
- package/generators/app/templates/.env.sandbox +2 -2
- package/generators/app/templates/I18N_MIGRATION.md +28 -35
- package/generators/app/templates/STRUCTURE.md +26 -26
- package/generators/app/templates/index.html +6 -14
- package/generators/app/templates/openapi-ts.config.ts +12 -12
- package/generators/app/templates/postcss.config.js +0 -1
- package/generators/app/templates/src/app.tsx +20 -20
- package/generators/app/templates/src/components/layout/language-switcher.tsx +20 -16
- package/generators/app/templates/src/components/layout/page-layout.tsx +31 -0
- package/generators/app/templates/src/components/layout/theme-switcher.tsx +15 -14
- package/generators/app/templates/src/components/theme/theme-provider.tsx +20 -13
- package/generators/app/templates/src/components/ui/button.tsx +15 -14
- package/generators/app/templates/src/components/ui/card.tsx +23 -16
- package/generators/app/templates/src/components/ui/field.tsx +35 -35
- package/generators/app/templates/src/components/ui/form.tsx +66 -30
- package/generators/app/templates/src/components/ui/input-group.tsx +24 -24
- package/generators/app/templates/src/components/ui/input.tsx +8 -8
- package/generators/app/templates/src/components/ui/label.tsx +9 -9
- package/generators/app/templates/src/components/ui/select.tsx +0 -1
- package/generators/app/templates/src/components/ui/separator.tsx +10 -10
- package/generators/app/templates/src/components/ui/textarea.tsx +7 -7
- package/generators/app/templates/src/index.css +12 -2
- package/generators/app/templates/src/lib/i18n.ts +16 -14
- package/generators/app/templates/src/lib/routes.ts +2 -3
- package/generators/app/templates/src/lib/utils/error-handler.ts +21 -14
- package/generators/app/templates/src/lib/utils.ts +3 -3
- package/generators/app/templates/src/locales/index.ts +12 -12
- package/generators/app/templates/src/locales/ja/theme.json +0 -1
- package/generators/app/templates/src/main.tsx +10 -12
- package/generators/app/templates/src/modules/app/layouts/app-layout.tsx +14 -25
- package/generators/app/templates/src/modules/app/locales/app-en.json +0 -1
- package/generators/app/templates/src/modules/app/locales/app-ja.json +0 -1
- package/generators/app/templates/src/modules/auth/components/auth-form.tsx +83 -0
- package/generators/app/templates/src/modules/auth/components/forgot-password-form.tsx +27 -52
- package/generators/app/templates/src/modules/auth/components/login-form.tsx +42 -72
- package/generators/app/templates/src/modules/auth/components/login-link.tsx +12 -0
- package/generators/app/templates/src/modules/auth/components/reset-password-form.tsx +46 -92
- package/generators/app/templates/src/modules/auth/components/signup-form.tsx +35 -71
- package/generators/app/templates/src/modules/auth/contexts/auth-context.tsx +10 -6
- package/generators/app/templates/src/modules/auth/hooks/use-auth-hook.ts +51 -36
- package/generators/app/templates/src/modules/auth/layouts/auth-layout.tsx +12 -15
- package/generators/app/templates/src/modules/auth/locales/auth-en.json +0 -1
- package/generators/app/templates/src/modules/auth/locales/auth-ja.json +0 -1
- package/generators/app/templates/src/modules/auth/schemas/form-schemas.ts +14 -10
- package/generators/app/templates/src/modules/landing/components/auth-hero.tsx +20 -18
- package/generators/app/templates/src/modules/landing/components/welcome-hero.tsx +13 -11
- package/generators/app/templates/src/modules/landing/landing-page-layout.tsx +1 -24
- package/generators/app/templates/src/modules/landing/landing-page.tsx +11 -6
- package/generators/app/templates/src/modules/landing/layouts/landing-page-layout.tsx +2 -21
- package/generators/app/templates/src/modules/landing/locales/landing-en.json +0 -1
- package/generators/app/templates/src/openapi-client-config.ts +1 -1
- package/generators/app/templates/src/routeTree.gen.ts +188 -188
- package/generators/app/templates/src/router.tsx +2 -2
- package/generators/app/templates/src/routes/$.tsx +0 -1
- package/generators/app/templates/src/routes/__root.tsx +2 -2
- package/generators/app/templates/src/routes/_landing/index.tsx +3 -3
- package/generators/app/templates/src/routes/_landing/route.tsx +3 -3
- package/generators/app/templates/src/routes/app/index.tsx +6 -6
- package/generators/app/templates/src/routes/app/route.tsx +4 -4
- package/generators/app/templates/src/routes/auth/forgot-password.tsx +1 -1
- package/generators/app/templates/src/routes/auth/login.tsx +3 -3
- package/generators/app/templates/src/routes/auth/reset-password.tsx +3 -3
- package/generators/app/templates/src/routes/auth/route.tsx +12 -7
- package/generators/app/templates/src/routes/auth/signup.tsx +1 -2
- package/generators/app/templates/src/sdk/@tanstack/react-query.gen.ts +157 -76
- package/generators/app/templates/src/sdk/client/client.gen.ts +35 -37
- package/generators/app/templates/src/sdk/client/index.ts +8 -8
- package/generators/app/templates/src/sdk/client/types.gen.ts +22 -19
- package/generators/app/templates/src/sdk/client/utils.gen.ts +53 -45
- package/generators/app/templates/src/sdk/client.gen.ts +18 -5
- package/generators/app/templates/src/sdk/core/auth.gen.ts +6 -6
- package/generators/app/templates/src/sdk/core/bodySerializer.gen.ts +25 -27
- package/generators/app/templates/src/sdk/core/params.gen.ts +12 -12
- package/generators/app/templates/src/sdk/core/pathSerializer.gen.ts +40 -41
- package/generators/app/templates/src/sdk/core/queryKeySerializer.gen.ts +12 -29
- package/generators/app/templates/src/sdk/core/serverSentEvents.gen.ts +26 -24
- package/generators/app/templates/src/sdk/core/types.gen.ts +17 -15
- package/generators/app/templates/src/sdk/core/utils.gen.ts +18 -18
- package/generators/app/templates/src/sdk/index.ts +40 -2
- package/generators/app/templates/src/sdk/schemas.gen.ts +108 -166
- package/generators/app/templates/src/sdk/sdk.gen.ts +145 -63
- package/generators/app/templates/src/sdk/types.gen.ts +90 -84
- package/generators/app/templates/src/sdk/zod.gen.ts +57 -74
- package/generators/app/templates/src/types/i18n.d.ts +0 -1
- package/generators/app/templates/tailwind.config.js +0 -1
- package/generators/app/templates/tsconfig.app.json +0 -1
- package/generators/app/templates/tsconfig.json +0 -1
- package/generators/app/templates/tsr.config.json +4 -0
- package/generators/app/templates/vite.config.js +13 -12
- package/package.json +2 -4
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { createContext, useContext, useEffect, useState } from
|
|
1
|
+
import { createContext, useContext, useEffect, useState } from "react";
|
|
2
2
|
|
|
3
|
-
export type ThemeTypes =
|
|
3
|
+
export type ThemeTypes = "dark" | "light" | "system";
|
|
4
4
|
|
|
5
5
|
type ThemeProviderProps = {
|
|
6
6
|
children: React.ReactNode;
|
|
@@ -14,28 +14,36 @@ type ThemeProviderState = {
|
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
const initialState: ThemeProviderState = {
|
|
17
|
-
theme:
|
|
17
|
+
theme: "system" as ThemeTypes,
|
|
18
18
|
setTheme: () => null,
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
const ThemeProviderContext = createContext<ThemeProviderState>(initialState);
|
|
22
22
|
|
|
23
|
-
export function ThemeProvider({
|
|
23
|
+
export function ThemeProvider({
|
|
24
|
+
children,
|
|
25
|
+
defaultTheme = "system",
|
|
26
|
+
storageKey = "vite-ui-theme",
|
|
27
|
+
...props
|
|
28
|
+
}: ThemeProviderProps) {
|
|
24
29
|
const [theme, setTheme] = useState<ThemeTypes>(() => {
|
|
25
30
|
const stored = localStorage.getItem(storageKey);
|
|
26
|
-
if (stored && [
|
|
31
|
+
if (stored && ["dark", "light", "system"].includes(stored)) {
|
|
27
32
|
return stored as ThemeTypes;
|
|
28
33
|
}
|
|
29
|
-
return
|
|
34
|
+
return defaultTheme;
|
|
30
35
|
});
|
|
31
36
|
|
|
32
37
|
useEffect(() => {
|
|
33
38
|
const root = window.document.documentElement;
|
|
34
39
|
|
|
35
|
-
root.classList.remove(
|
|
40
|
+
root.classList.remove("light", "dark");
|
|
36
41
|
|
|
37
|
-
if (theme ===
|
|
38
|
-
const systemTheme = window.matchMedia(
|
|
42
|
+
if (theme === "system") {
|
|
43
|
+
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)")
|
|
44
|
+
.matches
|
|
45
|
+
? "dark"
|
|
46
|
+
: "light";
|
|
39
47
|
root.classList.add(systemTheme);
|
|
40
48
|
return;
|
|
41
49
|
}
|
|
@@ -52,9 +60,7 @@ export function ThemeProvider({ children, defaultTheme = 'system', storageKey =
|
|
|
52
60
|
};
|
|
53
61
|
|
|
54
62
|
return (
|
|
55
|
-
<ThemeProviderContext.Provider
|
|
56
|
-
{...props}
|
|
57
|
-
value={value}>
|
|
63
|
+
<ThemeProviderContext.Provider {...props} value={value}>
|
|
58
64
|
{children}
|
|
59
65
|
</ThemeProviderContext.Provider>
|
|
60
66
|
);
|
|
@@ -62,6 +68,7 @@ export function ThemeProvider({ children, defaultTheme = 'system', storageKey =
|
|
|
62
68
|
|
|
63
69
|
export const useTheme = () => {
|
|
64
70
|
const context = useContext(ThemeProviderContext);
|
|
65
|
-
if (context === undefined)
|
|
71
|
+
if (context === undefined)
|
|
72
|
+
throw new Error("useTheme must be used within a ThemeProvider");
|
|
66
73
|
return context;
|
|
67
74
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
import { Slot } from "@radix-ui/react-slot"
|
|
3
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
4
4
|
|
|
5
|
-
import { cn } from "@/lib/utils"
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
6
|
|
|
7
7
|
const buttonVariants = cva(
|
|
8
8
|
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
@@ -31,27 +31,28 @@ const buttonVariants = cva(
|
|
|
31
31
|
variant: "default",
|
|
32
32
|
size: "default",
|
|
33
33
|
},
|
|
34
|
-
}
|
|
35
|
-
)
|
|
34
|
+
},
|
|
35
|
+
);
|
|
36
36
|
|
|
37
37
|
export interface ButtonProps
|
|
38
|
-
extends
|
|
38
|
+
extends
|
|
39
|
+
React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
39
40
|
VariantProps<typeof buttonVariants> {
|
|
40
|
-
asChild?: boolean
|
|
41
|
+
asChild?: boolean;
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
44
45
|
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
45
|
-
const Comp = asChild ? Slot : "button"
|
|
46
|
+
const Comp = asChild ? Slot : "button";
|
|
46
47
|
return (
|
|
47
48
|
<Comp
|
|
48
49
|
className={cn(buttonVariants({ variant, size, className }))}
|
|
49
50
|
ref={ref}
|
|
50
51
|
{...props}
|
|
51
52
|
/>
|
|
52
|
-
)
|
|
53
|
-
}
|
|
54
|
-
)
|
|
55
|
-
Button.displayName = "Button"
|
|
53
|
+
);
|
|
54
|
+
},
|
|
55
|
+
);
|
|
56
|
+
Button.displayName = "Button";
|
|
56
57
|
|
|
57
|
-
export { Button, buttonVariants }
|
|
58
|
+
export { Button, buttonVariants };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import * as React from "react"
|
|
1
|
+
import * as React from "react";
|
|
2
2
|
|
|
3
|
-
import { cn } from "@/lib/utils"
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
4
|
|
|
5
5
|
const Card = React.forwardRef<
|
|
6
6
|
HTMLDivElement,
|
|
@@ -10,12 +10,12 @@ const Card = React.forwardRef<
|
|
|
10
10
|
ref={ref}
|
|
11
11
|
className={cn(
|
|
12
12
|
"rounded-xl border bg-card text-card-foreground shadow",
|
|
13
|
-
className
|
|
13
|
+
className,
|
|
14
14
|
)}
|
|
15
15
|
{...props}
|
|
16
16
|
/>
|
|
17
|
-
))
|
|
18
|
-
Card.displayName = "Card"
|
|
17
|
+
));
|
|
18
|
+
Card.displayName = "Card";
|
|
19
19
|
|
|
20
20
|
const CardHeader = React.forwardRef<
|
|
21
21
|
HTMLDivElement,
|
|
@@ -26,8 +26,8 @@ const CardHeader = React.forwardRef<
|
|
|
26
26
|
className={cn("flex flex-col space-y-1.5 p-6", className)}
|
|
27
27
|
{...props}
|
|
28
28
|
/>
|
|
29
|
-
))
|
|
30
|
-
CardHeader.displayName = "CardHeader"
|
|
29
|
+
));
|
|
30
|
+
CardHeader.displayName = "CardHeader";
|
|
31
31
|
|
|
32
32
|
const CardTitle = React.forwardRef<
|
|
33
33
|
HTMLDivElement,
|
|
@@ -38,8 +38,8 @@ const CardTitle = React.forwardRef<
|
|
|
38
38
|
className={cn("font-semibold leading-none tracking-tight", className)}
|
|
39
39
|
{...props}
|
|
40
40
|
/>
|
|
41
|
-
))
|
|
42
|
-
CardTitle.displayName = "CardTitle"
|
|
41
|
+
));
|
|
42
|
+
CardTitle.displayName = "CardTitle";
|
|
43
43
|
|
|
44
44
|
const CardDescription = React.forwardRef<
|
|
45
45
|
HTMLDivElement,
|
|
@@ -50,16 +50,16 @@ const CardDescription = React.forwardRef<
|
|
|
50
50
|
className={cn("text-sm text-muted-foreground", className)}
|
|
51
51
|
{...props}
|
|
52
52
|
/>
|
|
53
|
-
))
|
|
54
|
-
CardDescription.displayName = "CardDescription"
|
|
53
|
+
));
|
|
54
|
+
CardDescription.displayName = "CardDescription";
|
|
55
55
|
|
|
56
56
|
const CardContent = React.forwardRef<
|
|
57
57
|
HTMLDivElement,
|
|
58
58
|
React.HTMLAttributes<HTMLDivElement>
|
|
59
59
|
>(({ className, ...props }, ref) => (
|
|
60
60
|
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
|
61
|
-
))
|
|
62
|
-
CardContent.displayName = "CardContent"
|
|
61
|
+
));
|
|
62
|
+
CardContent.displayName = "CardContent";
|
|
63
63
|
|
|
64
64
|
const CardFooter = React.forwardRef<
|
|
65
65
|
HTMLDivElement,
|
|
@@ -70,7 +70,14 @@ const CardFooter = React.forwardRef<
|
|
|
70
70
|
className={cn("flex items-center p-6 pt-0", className)}
|
|
71
71
|
{...props}
|
|
72
72
|
/>
|
|
73
|
-
))
|
|
74
|
-
CardFooter.displayName = "CardFooter"
|
|
73
|
+
));
|
|
74
|
+
CardFooter.displayName = "CardFooter";
|
|
75
75
|
|
|
76
|
-
export {
|
|
76
|
+
export {
|
|
77
|
+
Card,
|
|
78
|
+
CardHeader,
|
|
79
|
+
CardFooter,
|
|
80
|
+
CardTitle,
|
|
81
|
+
CardDescription,
|
|
82
|
+
CardContent,
|
|
83
|
+
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { useMemo } from "react"
|
|
2
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
3
|
|
|
4
|
-
import { cn } from "@/lib/utils"
|
|
5
|
-
import { Label } from "@/components/ui/label"
|
|
6
|
-
import { Separator } from "@/components/ui/separator"
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
import { Label } from "@/components/ui/label";
|
|
6
|
+
import { Separator } from "@/components/ui/separator";
|
|
7
7
|
|
|
8
8
|
function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) {
|
|
9
9
|
return (
|
|
@@ -12,11 +12,11 @@ function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) {
|
|
|
12
12
|
className={cn(
|
|
13
13
|
"flex flex-col gap-6",
|
|
14
14
|
"has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3",
|
|
15
|
-
className
|
|
15
|
+
className,
|
|
16
16
|
)}
|
|
17
17
|
{...props}
|
|
18
18
|
/>
|
|
19
|
-
)
|
|
19
|
+
);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
function FieldLegend({
|
|
@@ -32,11 +32,11 @@ function FieldLegend({
|
|
|
32
32
|
"mb-3 font-medium",
|
|
33
33
|
"data-[variant=legend]:text-base",
|
|
34
34
|
"data-[variant=label]:text-sm",
|
|
35
|
-
className
|
|
35
|
+
className,
|
|
36
36
|
)}
|
|
37
37
|
{...props}
|
|
38
38
|
/>
|
|
39
|
-
)
|
|
39
|
+
);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
|
|
@@ -45,11 +45,11 @@ function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
|
|
|
45
45
|
data-slot="field-group"
|
|
46
46
|
className={cn(
|
|
47
47
|
"group/field-group @container/field-group flex w-full flex-col gap-7 data-[slot=checkbox-group]:gap-3 [&>[data-slot=field-group]]:gap-4",
|
|
48
|
-
className
|
|
48
|
+
className,
|
|
49
49
|
)}
|
|
50
50
|
{...props}
|
|
51
51
|
/>
|
|
52
|
-
)
|
|
52
|
+
);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
const fieldVariants = cva(
|
|
@@ -73,8 +73,8 @@ const fieldVariants = cva(
|
|
|
73
73
|
defaultVariants: {
|
|
74
74
|
orientation: "vertical",
|
|
75
75
|
},
|
|
76
|
-
}
|
|
77
|
-
)
|
|
76
|
+
},
|
|
77
|
+
);
|
|
78
78
|
|
|
79
79
|
function Field({
|
|
80
80
|
className,
|
|
@@ -89,7 +89,7 @@ function Field({
|
|
|
89
89
|
className={cn(fieldVariants({ orientation }), className)}
|
|
90
90
|
{...props}
|
|
91
91
|
/>
|
|
92
|
-
)
|
|
92
|
+
);
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
function FieldContent({ className, ...props }: React.ComponentProps<"div">) {
|
|
@@ -98,11 +98,11 @@ function FieldContent({ className, ...props }: React.ComponentProps<"div">) {
|
|
|
98
98
|
data-slot="field-content"
|
|
99
99
|
className={cn(
|
|
100
100
|
"group/field-content flex flex-1 flex-col gap-1.5 leading-snug",
|
|
101
|
-
className
|
|
101
|
+
className,
|
|
102
102
|
)}
|
|
103
103
|
{...props}
|
|
104
104
|
/>
|
|
105
|
-
)
|
|
105
|
+
);
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
function FieldLabel({
|
|
@@ -116,11 +116,11 @@ function FieldLabel({
|
|
|
116
116
|
"group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50",
|
|
117
117
|
"has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border [&>[data-slot=field]]:p-4",
|
|
118
118
|
"has-data-[state=checked]:bg-primary/5 has-data-[state=checked]:border-primary dark:has-data-[state=checked]:bg-primary/10",
|
|
119
|
-
className
|
|
119
|
+
className,
|
|
120
120
|
)}
|
|
121
121
|
{...props}
|
|
122
122
|
/>
|
|
123
|
-
)
|
|
123
|
+
);
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
function FieldTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|
@@ -129,11 +129,11 @@ function FieldTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|
|
129
129
|
data-slot="field-label"
|
|
130
130
|
className={cn(
|
|
131
131
|
"flex w-fit items-center gap-2 text-sm font-medium leading-snug group-data-[disabled=true]/field:opacity-50",
|
|
132
|
-
className
|
|
132
|
+
className,
|
|
133
133
|
)}
|
|
134
134
|
{...props}
|
|
135
135
|
/>
|
|
136
|
-
)
|
|
136
|
+
);
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
function FieldDescription({ className, ...props }: React.ComponentProps<"p">) {
|
|
@@ -144,11 +144,11 @@ function FieldDescription({ className, ...props }: React.ComponentProps<"p">) {
|
|
|
144
144
|
"text-muted-foreground text-sm font-normal leading-normal group-has-[[data-orientation=horizontal]]/field:text-balance",
|
|
145
145
|
"nth-last-2:-mt-1 last:mt-0 [[data-variant=legend]+&]:-mt-1.5",
|
|
146
146
|
"[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4",
|
|
147
|
-
className
|
|
147
|
+
className,
|
|
148
148
|
)}
|
|
149
149
|
{...props}
|
|
150
150
|
/>
|
|
151
|
-
)
|
|
151
|
+
);
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
function FieldSeparator({
|
|
@@ -156,7 +156,7 @@ function FieldSeparator({
|
|
|
156
156
|
className,
|
|
157
157
|
...props
|
|
158
158
|
}: React.ComponentProps<"div"> & {
|
|
159
|
-
children?: React.ReactNode
|
|
159
|
+
children?: React.ReactNode;
|
|
160
160
|
}) {
|
|
161
161
|
return (
|
|
162
162
|
<div
|
|
@@ -164,7 +164,7 @@ function FieldSeparator({
|
|
|
164
164
|
data-content={!!children}
|
|
165
165
|
className={cn(
|
|
166
166
|
"relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2",
|
|
167
|
-
className
|
|
167
|
+
className,
|
|
168
168
|
)}
|
|
169
169
|
{...props}
|
|
170
170
|
>
|
|
@@ -178,7 +178,7 @@ function FieldSeparator({
|
|
|
178
178
|
</span>
|
|
179
179
|
)}
|
|
180
180
|
</div>
|
|
181
|
-
)
|
|
181
|
+
);
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
function FieldError({
|
|
@@ -187,33 +187,33 @@ function FieldError({
|
|
|
187
187
|
errors,
|
|
188
188
|
...props
|
|
189
189
|
}: React.ComponentProps<"div"> & {
|
|
190
|
-
errors?: Array<{ message?: string } | undefined
|
|
190
|
+
errors?: Array<{ message?: string } | undefined>;
|
|
191
191
|
}) {
|
|
192
192
|
const content = useMemo(() => {
|
|
193
193
|
if (children) {
|
|
194
|
-
return children
|
|
194
|
+
return children;
|
|
195
195
|
}
|
|
196
196
|
|
|
197
197
|
if (!errors) {
|
|
198
|
-
return null
|
|
198
|
+
return null;
|
|
199
199
|
}
|
|
200
200
|
|
|
201
201
|
if (errors?.length === 1 && errors[0]?.message) {
|
|
202
|
-
return errors[0].message
|
|
202
|
+
return errors[0].message;
|
|
203
203
|
}
|
|
204
204
|
|
|
205
205
|
return (
|
|
206
206
|
<ul className="ml-4 flex list-disc flex-col gap-1">
|
|
207
207
|
{errors.map(
|
|
208
208
|
(error, index) =>
|
|
209
|
-
error?.message && <li key={index}>{error.message}</li
|
|
209
|
+
error?.message && <li key={index}>{error.message}</li>,
|
|
210
210
|
)}
|
|
211
211
|
</ul>
|
|
212
|
-
)
|
|
213
|
-
}, [children, errors])
|
|
212
|
+
);
|
|
213
|
+
}, [children, errors]);
|
|
214
214
|
|
|
215
215
|
if (!content) {
|
|
216
|
-
return null
|
|
216
|
+
return null;
|
|
217
217
|
}
|
|
218
218
|
|
|
219
219
|
return (
|
|
@@ -225,7 +225,7 @@ function FieldError({
|
|
|
225
225
|
>
|
|
226
226
|
{content}
|
|
227
227
|
</div>
|
|
228
|
-
)
|
|
228
|
+
);
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
export {
|
|
@@ -239,4 +239,4 @@ export {
|
|
|
239
239
|
FieldSet,
|
|
240
240
|
FieldContent,
|
|
241
241
|
FieldTitle,
|
|
242
|
-
}
|
|
242
|
+
};
|
|
@@ -1,20 +1,36 @@
|
|
|
1
|
-
import * as React from
|
|
2
|
-
import * as LabelPrimitive from
|
|
3
|
-
import { Slot } from
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as LabelPrimitive from "@radix-ui/react-label";
|
|
3
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
4
|
+
import {
|
|
5
|
+
Controller,
|
|
6
|
+
FormProvider,
|
|
7
|
+
useFormContext,
|
|
8
|
+
useFormState,
|
|
9
|
+
type ControllerProps,
|
|
10
|
+
type FieldPath,
|
|
11
|
+
type FieldValues,
|
|
12
|
+
} from "react-hook-form";
|
|
13
|
+
|
|
14
|
+
import { cn } from "@/lib/utils";
|
|
15
|
+
import { Label } from "@/components/ui/label";
|
|
8
16
|
|
|
9
17
|
const Form = FormProvider;
|
|
10
18
|
|
|
11
|
-
type FormFieldContextValue<
|
|
19
|
+
type FormFieldContextValue<
|
|
20
|
+
TFieldValues extends FieldValues = FieldValues,
|
|
21
|
+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
|
22
|
+
> = {
|
|
12
23
|
name: TName;
|
|
13
24
|
};
|
|
14
25
|
|
|
15
|
-
const FormFieldContext = React.createContext<FormFieldContextValue>(
|
|
26
|
+
const FormFieldContext = React.createContext<FormFieldContextValue>(
|
|
27
|
+
{} as FormFieldContextValue,
|
|
28
|
+
);
|
|
16
29
|
|
|
17
|
-
const FormField = <
|
|
30
|
+
const FormField = <
|
|
31
|
+
TFieldValues extends FieldValues = FieldValues,
|
|
32
|
+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
|
33
|
+
>({
|
|
18
34
|
...props
|
|
19
35
|
}: ControllerProps<TFieldValues, TName>) => {
|
|
20
36
|
return (
|
|
@@ -28,7 +44,9 @@ type FormItemContextValue = {
|
|
|
28
44
|
id: string;
|
|
29
45
|
};
|
|
30
46
|
|
|
31
|
-
const FormItemContext = React.createContext<FormItemContextValue>(
|
|
47
|
+
const FormItemContext = React.createContext<FormItemContextValue>(
|
|
48
|
+
{} as FormItemContextValue,
|
|
49
|
+
);
|
|
32
50
|
|
|
33
51
|
const useFormField = () => {
|
|
34
52
|
const fieldContext = React.useContext(FormFieldContext);
|
|
@@ -38,7 +56,7 @@ const useFormField = () => {
|
|
|
38
56
|
const fieldState = getFieldState(fieldContext.name, formState);
|
|
39
57
|
|
|
40
58
|
if (!fieldContext) {
|
|
41
|
-
throw new Error(
|
|
59
|
+
throw new Error("useFormField should be used within <FormField>");
|
|
42
60
|
}
|
|
43
61
|
|
|
44
62
|
const { id } = itemContext;
|
|
@@ -53,27 +71,30 @@ const useFormField = () => {
|
|
|
53
71
|
};
|
|
54
72
|
};
|
|
55
73
|
|
|
56
|
-
function FormItem({ className, ...props }: React.ComponentProps<
|
|
74
|
+
function FormItem({ className, ...props }: React.ComponentProps<"div">) {
|
|
57
75
|
const id = React.useId();
|
|
58
76
|
|
|
59
77
|
return (
|
|
60
78
|
<FormItemContext.Provider value={{ id }}>
|
|
61
79
|
<div
|
|
62
|
-
data-slot=
|
|
63
|
-
className={cn(
|
|
80
|
+
data-slot="form-item"
|
|
81
|
+
className={cn("flex flex-col gap-2", className)}
|
|
64
82
|
{...props}
|
|
65
83
|
/>
|
|
66
84
|
</FormItemContext.Provider>
|
|
67
85
|
);
|
|
68
86
|
}
|
|
69
87
|
|
|
70
|
-
function FormLabel({
|
|
88
|
+
function FormLabel({
|
|
89
|
+
className,
|
|
90
|
+
...props
|
|
91
|
+
}: React.ComponentProps<typeof LabelPrimitive.Root>) {
|
|
71
92
|
const { error, formItemId } = useFormField();
|
|
72
93
|
|
|
73
94
|
return (
|
|
74
95
|
<Label
|
|
75
|
-
data-slot=
|
|
76
|
-
className={cn(error &&
|
|
96
|
+
data-slot="form-label"
|
|
97
|
+
className={cn(error && "text-destructive", className)}
|
|
77
98
|
htmlFor={formItemId}
|
|
78
99
|
{...props}
|
|
79
100
|
/>
|
|
@@ -81,35 +102,40 @@ function FormLabel({ className, ...props }: React.ComponentProps<typeof LabelPri
|
|
|
81
102
|
}
|
|
82
103
|
|
|
83
104
|
function FormControl({ ...props }: React.ComponentProps<typeof Slot>) {
|
|
84
|
-
const { error, formItemId, formDescriptionId, formMessageId } =
|
|
105
|
+
const { error, formItemId, formDescriptionId, formMessageId } =
|
|
106
|
+
useFormField();
|
|
85
107
|
|
|
86
108
|
return (
|
|
87
109
|
<Slot
|
|
88
|
-
data-slot=
|
|
110
|
+
data-slot="form-control"
|
|
89
111
|
id={formItemId}
|
|
90
|
-
aria-describedby={
|
|
112
|
+
aria-describedby={
|
|
113
|
+
!error
|
|
114
|
+
? `${formDescriptionId}`
|
|
115
|
+
: `${formDescriptionId} ${formMessageId}`
|
|
116
|
+
}
|
|
91
117
|
aria-invalid={!!error}
|
|
92
118
|
{...props}
|
|
93
119
|
/>
|
|
94
120
|
);
|
|
95
121
|
}
|
|
96
122
|
|
|
97
|
-
function FormDescription({ className, ...props }: React.ComponentProps<
|
|
123
|
+
function FormDescription({ className, ...props }: React.ComponentProps<"p">) {
|
|
98
124
|
const { formDescriptionId } = useFormField();
|
|
99
125
|
|
|
100
126
|
return (
|
|
101
127
|
<p
|
|
102
|
-
data-slot=
|
|
128
|
+
data-slot="form-description"
|
|
103
129
|
id={formDescriptionId}
|
|
104
|
-
className={cn(
|
|
130
|
+
className={cn("text-sm text-muted-foreground", className)}
|
|
105
131
|
{...props}
|
|
106
132
|
/>
|
|
107
133
|
);
|
|
108
134
|
}
|
|
109
135
|
|
|
110
|
-
function FormMessage({ className, ...props }: React.ComponentProps<
|
|
136
|
+
function FormMessage({ className, ...props }: React.ComponentProps<"p">) {
|
|
111
137
|
const { error, formMessageId } = useFormField();
|
|
112
|
-
const body = error ? String(error?.message ??
|
|
138
|
+
const body = error ? String(error?.message ?? "") : props.children;
|
|
113
139
|
|
|
114
140
|
if (!body) {
|
|
115
141
|
return null;
|
|
@@ -117,13 +143,23 @@ function FormMessage({ className, ...props }: React.ComponentProps<'p'>) {
|
|
|
117
143
|
|
|
118
144
|
return (
|
|
119
145
|
<p
|
|
120
|
-
data-slot=
|
|
146
|
+
data-slot="form-message"
|
|
121
147
|
id={formMessageId}
|
|
122
|
-
className={cn(
|
|
123
|
-
{...props}
|
|
148
|
+
className={cn("text-sm text-destructive", className)}
|
|
149
|
+
{...props}
|
|
150
|
+
>
|
|
124
151
|
{body}
|
|
125
152
|
</p>
|
|
126
153
|
);
|
|
127
154
|
}
|
|
128
155
|
|
|
129
|
-
export {
|
|
156
|
+
export {
|
|
157
|
+
useFormField,
|
|
158
|
+
Form,
|
|
159
|
+
FormItem,
|
|
160
|
+
FormLabel,
|
|
161
|
+
FormControl,
|
|
162
|
+
FormDescription,
|
|
163
|
+
FormMessage,
|
|
164
|
+
FormField,
|
|
165
|
+
};
|