@tioelvis/next-template 2.1.8 → 2.2.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tioelvis/next-template",
3
- "version": "2.1.8",
3
+ "version": "2.2.0",
4
4
  "description": "CLI to scaffold a Next.js + Tailwind project using shadcn/ui components",
5
5
  "type": "module",
6
6
  "bin": {
@@ -30,6 +30,7 @@
30
30
  },
31
31
  "devDependencies": {
32
32
  "@eslint/eslintrc": "^3.3.1",
33
+ "@hookform/resolvers": "^5.2.0",
33
34
  "@radix-ui/react-accordion": "^1.2.11",
34
35
  "@radix-ui/react-alert-dialog": "^1.1.14",
35
36
  "@radix-ui/react-aspect-ratio": "^1.1.7",
@@ -39,6 +40,9 @@
39
40
  "@radix-ui/react-context-menu": "^2.2.15",
40
41
  "@radix-ui/react-dialog": "^1.1.14",
41
42
  "@radix-ui/react-dropdown-menu": "^2.1.15",
43
+ "@radix-ui/react-hover-card": "^1.1.14",
44
+ "@radix-ui/react-label": "^2.1.7",
45
+ "@radix-ui/react-slot": "^1.2.3",
42
46
  "@tailwindcss/postcss": "^4.1.11",
43
47
  "@tanstack/react-query": "^5.83.0",
44
48
  "@types/node": "^24.1.0",
@@ -60,11 +64,13 @@
60
64
  "react": "^19.1.0",
61
65
  "react-day-picker": "^9.8.1",
62
66
  "react-dom": "^19.1.0",
67
+ "react-hook-form": "^7.61.1",
63
68
  "recharts": "^2.15.4",
64
69
  "tailwind-merge": "^3.3.1",
65
70
  "tailwindcss": "^4.1.11",
66
71
  "tw-animate-css": "^1.3.6",
67
72
  "typescript": "^5.8.3",
68
- "vaul": "^1.1.2"
73
+ "vaul": "^1.1.2",
74
+ "zod": "^4.0.13"
69
75
  }
70
76
  }
@@ -0,0 +1,12 @@
1
+ {
2
+ "dependencies": [
3
+ "@radix-ui/react-label",
4
+ "@radix-ui/react-slot",
5
+ "react-hook-form",
6
+ "zod",
7
+ "@hookform/resolvers"
8
+ ],
9
+ "dev_dependence": [],
10
+ "hooks": [],
11
+ "supports": ["label"]
12
+ }
@@ -0,0 +1,167 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as LabelPrimitive from "@radix-ui/react-label";
5
+ import { Slot } from "@radix-ui/react-slot";
6
+ import {
7
+ Controller,
8
+ FormProvider,
9
+ useFormContext,
10
+ useFormState,
11
+ type ControllerProps,
12
+ type FieldPath,
13
+ type FieldValues,
14
+ } from "react-hook-form";
15
+
16
+ import { cn } from "@/lib/utils";
17
+ import { Label } from "@/components/ui/label";
18
+
19
+ const Form = FormProvider;
20
+
21
+ type FormFieldContextValue<
22
+ TFieldValues extends FieldValues = FieldValues,
23
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
24
+ > = {
25
+ name: TName;
26
+ };
27
+
28
+ const FormFieldContext = React.createContext<FormFieldContextValue>(
29
+ {} as FormFieldContextValue
30
+ );
31
+
32
+ const FormField = <
33
+ TFieldValues extends FieldValues = FieldValues,
34
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
35
+ >({
36
+ ...props
37
+ }: ControllerProps<TFieldValues, TName>) => {
38
+ return (
39
+ <FormFieldContext.Provider value={{ name: props.name }}>
40
+ <Controller {...props} />
41
+ </FormFieldContext.Provider>
42
+ );
43
+ };
44
+
45
+ const useFormField = () => {
46
+ const fieldContext = React.useContext(FormFieldContext);
47
+ const itemContext = React.useContext(FormItemContext);
48
+ const { getFieldState } = useFormContext();
49
+ const formState = useFormState({ name: fieldContext.name });
50
+ const fieldState = getFieldState(fieldContext.name, formState);
51
+
52
+ if (!fieldContext) {
53
+ throw new Error("useFormField should be used within <FormField>");
54
+ }
55
+
56
+ const { id } = itemContext;
57
+
58
+ return {
59
+ id,
60
+ name: fieldContext.name,
61
+ formItemId: `${id}-form-item`,
62
+ formDescriptionId: `${id}-form-item-description`,
63
+ formMessageId: `${id}-form-item-message`,
64
+ ...fieldState,
65
+ };
66
+ };
67
+
68
+ type FormItemContextValue = {
69
+ id: string;
70
+ };
71
+
72
+ const FormItemContext = React.createContext<FormItemContextValue>(
73
+ {} as FormItemContextValue
74
+ );
75
+
76
+ function FormItem({ className, ...props }: React.ComponentProps<"div">) {
77
+ const id = React.useId();
78
+
79
+ return (
80
+ <FormItemContext.Provider value={{ id }}>
81
+ <div
82
+ data-slot="form-item"
83
+ className={cn("grid gap-2", className)}
84
+ {...props}
85
+ />
86
+ </FormItemContext.Provider>
87
+ );
88
+ }
89
+
90
+ function FormLabel({
91
+ className,
92
+ ...props
93
+ }: React.ComponentProps<typeof LabelPrimitive.Root>) {
94
+ const { error, formItemId } = useFormField();
95
+
96
+ return (
97
+ <Label
98
+ data-slot="form-label"
99
+ data-error={!!error}
100
+ className={cn("data-[error=true]:text-destructive", className)}
101
+ htmlFor={formItemId}
102
+ {...props}
103
+ />
104
+ );
105
+ }
106
+
107
+ function FormControl({ ...props }: React.ComponentProps<typeof Slot>) {
108
+ const { error, formItemId, formDescriptionId, formMessageId } =
109
+ useFormField();
110
+
111
+ return (
112
+ <Slot
113
+ data-slot="form-control"
114
+ id={formItemId}
115
+ aria-describedby={
116
+ !error
117
+ ? `${formDescriptionId}`
118
+ : `${formDescriptionId} ${formMessageId}`
119
+ }
120
+ aria-invalid={!!error}
121
+ {...props}
122
+ />
123
+ );
124
+ }
125
+
126
+ function FormDescription({ className, ...props }: React.ComponentProps<"p">) {
127
+ const { formDescriptionId } = useFormField();
128
+
129
+ return (
130
+ <p
131
+ data-slot="form-description"
132
+ id={formDescriptionId}
133
+ className={cn("text-muted-foreground text-sm", className)}
134
+ {...props}
135
+ />
136
+ );
137
+ }
138
+
139
+ function FormMessage({ className, ...props }: React.ComponentProps<"p">) {
140
+ const { error, formMessageId } = useFormField();
141
+ const body = error ? String(error?.message ?? "") : props.children;
142
+
143
+ if (!body) {
144
+ return null;
145
+ }
146
+
147
+ return (
148
+ <p
149
+ data-slot="form-message"
150
+ id={formMessageId}
151
+ className={cn("text-destructive text-sm", className)}
152
+ {...props}>
153
+ {body}
154
+ </p>
155
+ );
156
+ }
157
+
158
+ export {
159
+ useFormField,
160
+ Form,
161
+ FormItem,
162
+ FormLabel,
163
+ FormControl,
164
+ FormDescription,
165
+ FormMessage,
166
+ FormField,
167
+ };
@@ -0,0 +1,6 @@
1
+ {
2
+ "dependencies": ["@radix-ui/react-hover-card"],
3
+ "dev_dependence": [],
4
+ "hooks": [],
5
+ "supports": []
6
+ }
@@ -0,0 +1,49 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as HoverCardPrimitive from "@radix-ui/react-hover-card";
5
+
6
+ import { cn } from "@/lib/utils";
7
+
8
+ function HoverCard({
9
+ ...props
10
+ }: React.ComponentProps<typeof HoverCardPrimitive.Root>) {
11
+ return <HoverCardPrimitive.Root data-slot="hover-card" {...props} />;
12
+ }
13
+
14
+ function HoverCardTrigger({
15
+ className,
16
+ ...props
17
+ }: React.ComponentProps<typeof HoverCardPrimitive.Trigger>) {
18
+ return (
19
+ <HoverCardPrimitive.Trigger
20
+ data-slot="hover-card-trigger"
21
+ className={cn("cursor-pointer", className)}
22
+ {...props}
23
+ />
24
+ );
25
+ }
26
+
27
+ function HoverCardContent({
28
+ className,
29
+ align = "center",
30
+ sideOffset = 4,
31
+ ...props
32
+ }: React.ComponentProps<typeof HoverCardPrimitive.Content>) {
33
+ return (
34
+ <HoverCardPrimitive.Portal data-slot="hover-card-portal">
35
+ <HoverCardPrimitive.Content
36
+ data-slot="hover-card-content"
37
+ align={align}
38
+ sideOffset={sideOffset}
39
+ className={cn(
40
+ "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-64 origin-(--radix-hover-card-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
41
+ className
42
+ )}
43
+ {...props}
44
+ />
45
+ </HoverCardPrimitive.Portal>
46
+ );
47
+ }
48
+
49
+ export { HoverCard, HoverCardTrigger, HoverCardContent };
@@ -0,0 +1,6 @@
1
+ {
2
+ "dependencies": ["@radix-ui/react-label"],
3
+ "dev_dependence": [],
4
+ "hooks": [],
5
+ "supports": []
6
+ }
@@ -0,0 +1,24 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as LabelPrimitive from "@radix-ui/react-label";
5
+
6
+ import { cn } from "@/lib/utils";
7
+
8
+ function Label({
9
+ className,
10
+ ...props
11
+ }: React.ComponentProps<typeof LabelPrimitive.Root>) {
12
+ return (
13
+ <LabelPrimitive.Root
14
+ data-slot="label"
15
+ className={cn(
16
+ "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:cursor-not-allowed group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
17
+ className
18
+ )}
19
+ {...props}
20
+ />
21
+ );
22
+ }
23
+
24
+ export { Label };
package/src/constants.js CHANGED
@@ -57,6 +57,8 @@ const COMPONENTS = [
57
57
  "dialog",
58
58
  "drawer",
59
59
  "dropdown-menu",
60
+ "form",
61
+ "hover-card",
60
62
  ];
61
63
 
62
64
  export {