@page-speed/forms 0.5.1 → 0.5.3
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 +29 -0
- package/dist/chunk-232KNGJI.js +207 -0
- package/dist/chunk-232KNGJI.js.map +1 -0
- package/dist/chunk-24RPM43T.js +373 -0
- package/dist/chunk-24RPM43T.js.map +1 -0
- package/dist/chunk-27JUYRDE.cjs +173 -0
- package/dist/chunk-27JUYRDE.cjs.map +1 -0
- package/dist/chunk-5NT5T5XY.js +4136 -0
- package/dist/chunk-5NT5T5XY.js.map +1 -0
- package/dist/chunk-AVAKC6R7.cjs +236 -0
- package/dist/chunk-AVAKC6R7.cjs.map +1 -0
- package/dist/chunk-DKLLPKZN.cjs +238 -0
- package/dist/chunk-DKLLPKZN.cjs.map +1 -0
- package/dist/chunk-EX6CRLKG.cjs +397 -0
- package/dist/chunk-EX6CRLKG.cjs.map +1 -0
- package/dist/chunk-H6NNFV64.js +127 -0
- package/dist/chunk-H6NNFV64.js.map +1 -0
- package/dist/chunk-JBEWTBFH.js +217 -0
- package/dist/chunk-JBEWTBFH.js.map +1 -0
- package/dist/chunk-JBEZLX3H.cjs +138 -0
- package/dist/chunk-JBEZLX3H.cjs.map +1 -0
- package/dist/chunk-VLGZG2VP.js +150 -0
- package/dist/chunk-VLGZG2VP.js.map +1 -0
- package/dist/chunk-ZYFTT6DB.cjs +4169 -0
- package/dist/chunk-ZYFTT6DB.cjs.map +1 -0
- package/dist/core.cjs +23 -722
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +3 -3
- package/dist/core.d.ts +3 -3
- package/dist/core.js +3 -705
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +43 -727
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -705
- package/dist/index.js.map +1 -1
- package/dist/inputs.cjs +44 -4359
- package/dist/inputs.cjs.map +1 -1
- package/dist/inputs.d.cts +1 -1
- package/dist/inputs.d.ts +1 -1
- package/dist/inputs.js +2 -4337
- package/dist/inputs.js.map +1 -1
- package/dist/integration.cjs +574 -9
- package/dist/integration.cjs.map +1 -1
- package/dist/integration.d.cts +298 -1
- package/dist/integration.d.ts +298 -1
- package/dist/integration.js +566 -9
- package/dist/integration.js.map +1 -1
- package/dist/{types-DuX3q6A4.d.cts → types-CnOCn7b3.d.cts} +51 -1
- package/dist/{types-DuX3q6A4.d.ts → types-CnOCn7b3.d.ts} +51 -1
- package/dist/validation-rules.cjs +75 -231
- package/dist/validation-rules.cjs.map +1 -1
- package/dist/validation-rules.d.cts +1 -1
- package/dist/validation-rules.d.ts +1 -1
- package/dist/validation-rules.js +1 -215
- package/dist/validation-rules.js.map +1 -1
- package/dist/validation-utils.cjs +43 -133
- package/dist/validation-utils.cjs.map +1 -1
- package/dist/validation-utils.d.cts +1 -1
- package/dist/validation-utils.d.ts +1 -1
- package/dist/validation-utils.js +1 -125
- package/dist/validation-utils.js.map +1 -1
- package/dist/validation-valibot.d.cts +1 -1
- package/dist/validation-valibot.d.ts +1 -1
- package/dist/validation.cjs +115 -364
- package/dist/validation.cjs.map +1 -1
- package/dist/validation.d.cts +1 -1
- package/dist/validation.d.ts +1 -1
- package/dist/validation.js +2 -339
- package/dist/validation.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -100,6 +100,35 @@ export function ContactForm() {
|
|
|
100
100
|
}
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
+
### Grouped Form Configuration
|
|
104
|
+
|
|
105
|
+
`Form` supports both classic props and grouped config objects for cleaner callsites.
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
import * as React from "react";
|
|
109
|
+
import { Form } from "@page-speed/forms";
|
|
110
|
+
|
|
111
|
+
<Form
|
|
112
|
+
form={form}
|
|
113
|
+
notificationConfig={{
|
|
114
|
+
successMessage: "Thanks, submission received.",
|
|
115
|
+
submissionError,
|
|
116
|
+
}}
|
|
117
|
+
styleConfig={{
|
|
118
|
+
formClassName: "space-y-6",
|
|
119
|
+
successMessageClassName: "bg-emerald-600 text-white",
|
|
120
|
+
errorMessageClassName: "bg-red-600 text-white",
|
|
121
|
+
}}
|
|
122
|
+
formConfig={{
|
|
123
|
+
endpoint: "/api/contact",
|
|
124
|
+
method: "post",
|
|
125
|
+
submissionConfig: {
|
|
126
|
+
behavior: "showConfirmation",
|
|
127
|
+
},
|
|
128
|
+
}}
|
|
129
|
+
/>;
|
|
130
|
+
```
|
|
131
|
+
|
|
103
132
|
## Package Entry Points
|
|
104
133
|
|
|
105
134
|
### Main
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import * as React2 from 'react';
|
|
2
|
+
import { Label as Label$1, Slot } from 'radix-ui';
|
|
3
|
+
import { clsx } from 'clsx';
|
|
4
|
+
import { twMerge } from 'tailwind-merge';
|
|
5
|
+
import { cva } from 'class-variance-authority';
|
|
6
|
+
|
|
7
|
+
// src/components/ui/field.tsx
|
|
8
|
+
function cn(...inputs) {
|
|
9
|
+
return twMerge(clsx(inputs));
|
|
10
|
+
}
|
|
11
|
+
var INPUT_AUTOFILL_RESET_CLASSES = "autofill:bg-transparent autofill:text-foreground [&:-webkit-autofill]:[-webkit-text-fill-color:hsl(var(--foreground))] [&:-webkit-autofill]:[caret-color:hsl(var(--foreground))] [&:-webkit-autofill]:[box-shadow:0_0_0px_1000px_hsl(var(--background))_inset] [&:-webkit-autofill:hover]:[box-shadow:0_0_0px_1000px_hsl(var(--background))_inset] [&:-webkit-autofill:focus]:[box-shadow:0_0_0px_1000px_hsl(var(--background))_inset] [&:-webkit-autofill]:[transition:background-color_9999s_ease-out,color_9999s_ease-out]";
|
|
12
|
+
|
|
13
|
+
// src/components/ui/label.tsx
|
|
14
|
+
function Label({
|
|
15
|
+
className,
|
|
16
|
+
...props
|
|
17
|
+
}) {
|
|
18
|
+
return /* @__PURE__ */ React2.createElement(
|
|
19
|
+
Label$1.Root,
|
|
20
|
+
{
|
|
21
|
+
"data-slot": "label",
|
|
22
|
+
className: cn(
|
|
23
|
+
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
|
|
24
|
+
className
|
|
25
|
+
),
|
|
26
|
+
...props
|
|
27
|
+
}
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// src/components/ui/field.tsx
|
|
32
|
+
var Field = React2.forwardRef(
|
|
33
|
+
({ className, orientation = "vertical", invalid = false, ...props }, ref) => {
|
|
34
|
+
return /* @__PURE__ */ React2.createElement(
|
|
35
|
+
"div",
|
|
36
|
+
{
|
|
37
|
+
ref,
|
|
38
|
+
"data-slot": "field",
|
|
39
|
+
"data-orientation": orientation,
|
|
40
|
+
"data-invalid": invalid || void 0,
|
|
41
|
+
className: cn(
|
|
42
|
+
"flex",
|
|
43
|
+
orientation === "horizontal" ? "items-center gap-2" : "flex-col gap-1.5",
|
|
44
|
+
className
|
|
45
|
+
),
|
|
46
|
+
...props
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
);
|
|
51
|
+
Field.displayName = "Field";
|
|
52
|
+
var FieldGroup = React2.forwardRef(({ className, ...props }, ref) => {
|
|
53
|
+
return /* @__PURE__ */ React2.createElement(
|
|
54
|
+
"div",
|
|
55
|
+
{
|
|
56
|
+
ref,
|
|
57
|
+
"data-slot": "field-group",
|
|
58
|
+
className: cn("flex flex-col gap-4", className),
|
|
59
|
+
...props
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
});
|
|
63
|
+
FieldGroup.displayName = "FieldGroup";
|
|
64
|
+
var FieldLabel = React2.forwardRef(({ className, required, children, ...props }, ref) => {
|
|
65
|
+
return /* @__PURE__ */ React2.createElement(
|
|
66
|
+
Label,
|
|
67
|
+
{
|
|
68
|
+
ref,
|
|
69
|
+
"data-slot": "field-label",
|
|
70
|
+
className: cn(
|
|
71
|
+
"text-sm font-medium leading-none select-none",
|
|
72
|
+
"peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
|
|
73
|
+
className
|
|
74
|
+
),
|
|
75
|
+
...props
|
|
76
|
+
},
|
|
77
|
+
children,
|
|
78
|
+
required && /* @__PURE__ */ React2.createElement("span", { className: "text-destructive ml-1" }, "*")
|
|
79
|
+
);
|
|
80
|
+
});
|
|
81
|
+
FieldLabel.displayName = "FieldLabel";
|
|
82
|
+
var FieldDescription = React2.forwardRef(({ className, ...props }, ref) => {
|
|
83
|
+
return /* @__PURE__ */ React2.createElement(
|
|
84
|
+
"p",
|
|
85
|
+
{
|
|
86
|
+
ref,
|
|
87
|
+
"data-slot": "field-description",
|
|
88
|
+
className: cn("text-sm opacity-70", className),
|
|
89
|
+
...props
|
|
90
|
+
}
|
|
91
|
+
);
|
|
92
|
+
});
|
|
93
|
+
FieldDescription.displayName = "FieldDescription";
|
|
94
|
+
var FieldError = React2.forwardRef(({ className, ...props }, ref) => {
|
|
95
|
+
return /* @__PURE__ */ React2.createElement(
|
|
96
|
+
"p",
|
|
97
|
+
{
|
|
98
|
+
ref,
|
|
99
|
+
"data-slot": "field-error",
|
|
100
|
+
role: "alert",
|
|
101
|
+
"aria-live": "polite",
|
|
102
|
+
className: cn("text-sm text-destructive", className),
|
|
103
|
+
...props
|
|
104
|
+
}
|
|
105
|
+
);
|
|
106
|
+
});
|
|
107
|
+
FieldError.displayName = "FieldError";
|
|
108
|
+
var buttonVariants = cva(
|
|
109
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 aria-invalid:border-destructive",
|
|
110
|
+
{
|
|
111
|
+
variants: {
|
|
112
|
+
variant: {
|
|
113
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
114
|
+
destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20",
|
|
115
|
+
outline: "border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground",
|
|
116
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
117
|
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
118
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
119
|
+
},
|
|
120
|
+
size: {
|
|
121
|
+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
122
|
+
xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
|
|
123
|
+
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
|
124
|
+
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
|
125
|
+
icon: "size-9",
|
|
126
|
+
"icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
|
|
127
|
+
"icon-sm": "size-8",
|
|
128
|
+
"icon-lg": "size-10"
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
defaultVariants: {
|
|
132
|
+
variant: "default",
|
|
133
|
+
size: "default"
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
);
|
|
137
|
+
function Button({
|
|
138
|
+
className,
|
|
139
|
+
variant = "default",
|
|
140
|
+
size = "default",
|
|
141
|
+
asChild = false,
|
|
142
|
+
...props
|
|
143
|
+
}) {
|
|
144
|
+
const Comp = asChild ? Slot.Root : "button";
|
|
145
|
+
return /* @__PURE__ */ React2.createElement(
|
|
146
|
+
Comp,
|
|
147
|
+
{
|
|
148
|
+
"data-slot": "button",
|
|
149
|
+
"data-variant": variant,
|
|
150
|
+
"data-size": size,
|
|
151
|
+
className: cn(buttonVariants({ variant, size, className })),
|
|
152
|
+
...props
|
|
153
|
+
}
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
var LabelGroup = ({
|
|
157
|
+
labelHtmlFor,
|
|
158
|
+
required = false,
|
|
159
|
+
variant = "label",
|
|
160
|
+
secondaryId,
|
|
161
|
+
secondary,
|
|
162
|
+
primary,
|
|
163
|
+
primaryClassName,
|
|
164
|
+
secondaryClassName
|
|
165
|
+
}) => {
|
|
166
|
+
const primaryClasses = cn(
|
|
167
|
+
"text-sm font-medium leading-snug",
|
|
168
|
+
variant === "legend" ? "mb-1.5" : "mb-1 block",
|
|
169
|
+
primaryClassName
|
|
170
|
+
);
|
|
171
|
+
const requiredIndicator = required && variant !== "label" ? /* @__PURE__ */ React2.createElement("span", { className: "text-destructive pl-0.5", "aria-label": "required" }, "*") : null;
|
|
172
|
+
let primaryElement = null;
|
|
173
|
+
if (primary) {
|
|
174
|
+
if (variant === "label") {
|
|
175
|
+
primaryElement = /* @__PURE__ */ React2.createElement(
|
|
176
|
+
FieldLabel,
|
|
177
|
+
{
|
|
178
|
+
htmlFor: labelHtmlFor,
|
|
179
|
+
required,
|
|
180
|
+
className: primaryClasses
|
|
181
|
+
},
|
|
182
|
+
primary
|
|
183
|
+
);
|
|
184
|
+
} else if (variant === "legend") {
|
|
185
|
+
primaryElement = /* @__PURE__ */ React2.createElement("legend", { "data-slot": "field-legend", className: primaryClasses }, primary, requiredIndicator);
|
|
186
|
+
} else {
|
|
187
|
+
primaryElement = /* @__PURE__ */ React2.createElement("div", { "data-slot": "field-label", className: primaryClasses }, primary, requiredIndicator);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
const secondaryElement = secondary ? /* @__PURE__ */ React2.createElement(
|
|
191
|
+
FieldDescription,
|
|
192
|
+
{
|
|
193
|
+
id: secondaryId,
|
|
194
|
+
className: cn("leading-normal font-normal", secondaryClassName)
|
|
195
|
+
},
|
|
196
|
+
secondary
|
|
197
|
+
) : null;
|
|
198
|
+
if (!primaryElement && !secondaryElement) return null;
|
|
199
|
+
if (variant === "legend") {
|
|
200
|
+
return /* @__PURE__ */ React2.createElement(React2.Fragment, null, primaryElement, secondaryElement);
|
|
201
|
+
}
|
|
202
|
+
return /* @__PURE__ */ React2.createElement("div", { className: "flex flex-1 flex-col gap-0.5" }, primaryElement, secondaryElement);
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
export { Button, Field, FieldDescription, FieldError, FieldGroup, FieldLabel, INPUT_AUTOFILL_RESET_CLASSES, LabelGroup, buttonVariants, cn };
|
|
206
|
+
//# sourceMappingURL=chunk-232KNGJI.js.map
|
|
207
|
+
//# sourceMappingURL=chunk-232KNGJI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/utils.ts","../src/components/ui/label.tsx","../src/components/ui/field.tsx","../src/components/ui/button.tsx","../src/core/label-group.tsx"],"names":["React","LabelPrimitive","React3","React4"],"mappings":"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","file":"chunk-232KNGJI.js","sourcesContent":["import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\n/**\n * Normalizes browser autofill colors so inputs keep theme colors.\n */\nexport const INPUT_AUTOFILL_RESET_CLASSES =\n \"autofill:bg-transparent autofill:text-foreground \" +\n \"[&:-webkit-autofill]:[-webkit-text-fill-color:hsl(var(--foreground))] \" +\n \"[&:-webkit-autofill]:[caret-color:hsl(var(--foreground))] \" +\n \"[&:-webkit-autofill]:[box-shadow:0_0_0px_1000px_hsl(var(--background))_inset] \" +\n \"[&:-webkit-autofill:hover]:[box-shadow:0_0_0px_1000px_hsl(var(--background))_inset] \" +\n \"[&:-webkit-autofill:focus]:[box-shadow:0_0_0px_1000px_hsl(var(--background))_inset] \" +\n \"[&:-webkit-autofill]:[transition:background-color_9999s_ease-out,color_9999s_ease-out]\";\n","import * as React from \"react\"\nimport { Label as LabelPrimitive } from \"radix-ui\"\n\nimport { cn } from \"../../lib/utils\"\n\nfunction Label({\n className,\n ...props\n}: React.ComponentProps<typeof LabelPrimitive.Root>) {\n return (\n <LabelPrimitive.Root\n data-slot=\"label\"\n className={cn(\n \"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50\",\n className\n )}\n {...props}\n />\n )\n}\n\nexport { Label }\n","import * as React from \"react\";\nimport { Label } from \"./label\";\nimport { cn } from \"../../lib/utils\";\n\ntype FieldOrientation = \"vertical\" | \"horizontal\";\n\ninterface FieldProps extends React.HTMLAttributes<HTMLDivElement> {\n orientation?: FieldOrientation;\n invalid?: boolean;\n}\n\n/**\n * Field - Container component for form inputs with validation display\n *\n * Provides consistent layout and spacing for form fields with labels,\n * inputs, descriptions, and error messages.\n */\nconst Field = React.forwardRef<HTMLDivElement, FieldProps>(\n({ className, orientation = \"vertical\", invalid = false, ...props }, ref) => {\n return (\n <div\n ref={ref}\n data-slot=\"field\"\n data-orientation={orientation}\n data-invalid={invalid || undefined}\n className={cn(\n \"flex\",\n orientation === \"horizontal\"\n ? \"items-center gap-2\"\n : \"flex-col gap-1.5\",\n className,\n )}\n {...props}\n />\n );\n});\nField.displayName = \"Field\";\n\n/**\n * FieldGroup - Container for multiple related fields\n *\n * Used to group fields together (e.g., first name + last name in a row)\n */\nconst FieldGroup = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => {\n return (\n <div\n ref={ref}\n data-slot=\"field-group\"\n className={cn(\"flex flex-col gap-4\", className)}\n {...props}\n />\n );\n});\nFieldGroup.displayName = \"FieldGroup\";\n\n/**\n * FieldLabel - Label component for form fields\n *\n * Wrapper around ShadCN Label with consistent styling\n */\nconst FieldLabel = React.forwardRef<\n HTMLLabelElement,\n React.LabelHTMLAttributes<HTMLLabelElement> & {\n required?: boolean;\n }\n>(({ className, required, children, ...props }, ref) => {\n return (\n <Label\n ref={ref}\n data-slot=\"field-label\"\n className={cn(\n \"text-sm font-medium leading-none select-none\",\n \"peer-disabled:cursor-not-allowed peer-disabled:opacity-50\",\n className,\n )}\n {...props}\n >\n {children}\n {required && <span className=\"text-destructive ml-1\">*</span>}\n </Label>\n );\n});\nFieldLabel.displayName = \"FieldLabel\";\n\n/**\n * FieldDescription - Helper text for form fields\n *\n * Displays additional information or instructions for the field\n */\nconst FieldDescription = React.forwardRef<\n HTMLParagraphElement,\n React.HTMLAttributes<HTMLParagraphElement>\n>(({ className, ...props }, ref) => {\n return (\n <p\n ref={ref}\n data-slot=\"field-description\"\n className={cn(\"text-sm opacity-70\", className)}\n {...props}\n />\n );\n});\nFieldDescription.displayName = \"FieldDescription\";\n\n/**\n * FieldError - Error message display for form fields\n *\n * Shows validation errors with proper styling and accessibility\n */\nconst FieldError = React.forwardRef<\n HTMLParagraphElement,\n React.HTMLAttributes<HTMLParagraphElement>\n>(({ className, ...props }, ref) => {\n return (\n <p\n ref={ref}\n data-slot=\"field-error\"\n role=\"alert\"\n aria-live=\"polite\"\n className={cn(\"text-sm text-destructive\", className)}\n {...props}\n />\n );\n});\nFieldError.displayName = \"FieldError\";\n\nexport { Field, FieldGroup, FieldLabel, FieldDescription, FieldError };\n","import * as React from \"react\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\nimport { Slot } from \"radix-ui\"\n\nimport { cn } from \"../../lib/utils\"\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 aria-invalid:border-destructive\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n destructive:\n \"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20\",\n outline:\n \"border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground\",\n secondary:\n \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n ghost:\n \"hover:bg-accent hover:text-accent-foreground\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-9 px-4 py-2 has-[>svg]:px-3\",\n xs: \"h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3\",\n sm: \"h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5\",\n lg: \"h-10 rounded-md px-6 has-[>svg]:px-4\",\n icon: \"size-9\",\n \"icon-xs\": \"size-6 rounded-md [&_svg:not([class*='size-'])]:size-3\",\n \"icon-sm\": \"size-8\",\n \"icon-lg\": \"size-10\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n)\n\nfunction Button({\n className,\n variant = \"default\",\n size = \"default\",\n asChild = false,\n ...props\n}: React.ComponentProps<\"button\"> &\n VariantProps<typeof buttonVariants> & {\n asChild?: boolean\n }) {\n const Comp = asChild ? Slot.Root : \"button\"\n\n return (\n <Comp\n data-slot=\"button\"\n data-variant={variant}\n data-size={size}\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n />\n )\n}\n\nexport { Button, buttonVariants }\n","\"use client\";\n\nimport * as React from \"react\";\nimport type { ReactNode } from \"react\";\nimport {\n FieldDescription,\n FieldLabel,\n} from \"../components/ui/field\";\nimport { cn } from \"../lib/utils\";\n\nexport type LabelGroupProps = {\n variant?: \"legend\" | \"label\" | \"text\";\n secondary?: ReactNode;\n secondaryId?: string;\n primary?: ReactNode;\n labelHtmlFor?: string;\n required?: boolean;\n primaryClassName?: string;\n secondaryClassName?: string;\n};\n\nconst LabelGroup = ({\n labelHtmlFor,\n required = false,\n variant = \"label\",\n secondaryId,\n secondary,\n primary,\n primaryClassName,\n secondaryClassName,\n}: LabelGroupProps) => {\n const primaryClasses = cn(\n \"text-sm font-medium leading-snug\",\n variant === \"legend\" ? \"mb-1.5\" : \"mb-1 block\",\n primaryClassName,\n );\n\n const requiredIndicator =\n required && variant !== \"label\" ? (\n <span className=\"text-destructive pl-0.5\" aria-label=\"required\">\n *\n </span>\n ) : null;\n\n let primaryElement: ReactNode = null;\n if (primary) {\n if (variant === \"label\") {\n primaryElement = (\n <FieldLabel\n htmlFor={labelHtmlFor}\n required={required}\n className={primaryClasses}\n >\n {primary}\n </FieldLabel>\n );\n } else if (variant === \"legend\") {\n primaryElement = (\n <legend data-slot=\"field-legend\" className={primaryClasses}>\n {primary}\n {requiredIndicator}\n </legend>\n );\n } else {\n primaryElement = (\n <div data-slot=\"field-label\" className={primaryClasses}>\n {primary}\n {requiredIndicator}\n </div>\n );\n }\n }\n\n const secondaryElement = secondary ? (\n <FieldDescription\n id={secondaryId}\n className={cn(\"leading-normal font-normal\", secondaryClassName)}\n >\n {secondary}\n </FieldDescription>\n ) : null;\n\n if (!primaryElement && !secondaryElement) return null;\n\n // Legend should remain a direct child of fieldset for proper semantics.\n if (variant === \"legend\") {\n return (\n <>\n {primaryElement}\n {secondaryElement}\n </>\n );\n }\n\n return (\n <div className=\"flex flex-1 flex-col gap-0.5\">\n {primaryElement}\n {secondaryElement}\n </div>\n );\n};\n\nexport { LabelGroup };\n"]}
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
import { Field, LabelGroup, FieldError } from './chunk-232KNGJI.js';
|
|
2
|
+
import * as React3 from 'react';
|
|
3
|
+
import { useRef, useCallback, useContext } from 'react';
|
|
4
|
+
import { useObservable, useSelector } from '@legendapp/state/react';
|
|
5
|
+
import { useMap } from '@opensite/hooks/useMap';
|
|
6
|
+
|
|
7
|
+
function useForm(options) {
|
|
8
|
+
const {
|
|
9
|
+
initialValues,
|
|
10
|
+
validationSchema,
|
|
11
|
+
validateOn = "onBlur",
|
|
12
|
+
revalidateOn = "onChange",
|
|
13
|
+
onSubmit,
|
|
14
|
+
onError,
|
|
15
|
+
debug = false
|
|
16
|
+
} = options;
|
|
17
|
+
const state$ = useObservable({
|
|
18
|
+
values: initialValues,
|
|
19
|
+
errors: {},
|
|
20
|
+
touched: {},
|
|
21
|
+
isSubmitting: false,
|
|
22
|
+
status: "idle",
|
|
23
|
+
initialValues: { ...initialValues },
|
|
24
|
+
// Create a copy to prevent reference sharing
|
|
25
|
+
hasValidated: {}
|
|
26
|
+
});
|
|
27
|
+
const validationInProgress = useRef(/* @__PURE__ */ new Set());
|
|
28
|
+
const [, fieldMetadataActions] = useMap();
|
|
29
|
+
const validateField = useCallback(
|
|
30
|
+
async (field) => {
|
|
31
|
+
const validators = validationSchema?.[field];
|
|
32
|
+
if (!validators) return void 0;
|
|
33
|
+
const fieldKey = String(field);
|
|
34
|
+
validationInProgress.current.add(fieldKey);
|
|
35
|
+
const currentMeta = fieldMetadataActions.get(fieldKey) || {
|
|
36
|
+
validationCount: 0
|
|
37
|
+
};
|
|
38
|
+
fieldMetadataActions.set(fieldKey, {
|
|
39
|
+
lastValidated: Date.now(),
|
|
40
|
+
validationCount: currentMeta.validationCount + 1
|
|
41
|
+
});
|
|
42
|
+
try {
|
|
43
|
+
const value = state$.values[field].get();
|
|
44
|
+
const allValues = state$.values.get();
|
|
45
|
+
const validatorArray = Array.isArray(validators) ? validators : [validators];
|
|
46
|
+
for (const validator of validatorArray) {
|
|
47
|
+
const error = await validator(value, allValues);
|
|
48
|
+
if (error) {
|
|
49
|
+
state$.errors[field].set(error);
|
|
50
|
+
validationInProgress.current.delete(fieldKey);
|
|
51
|
+
return error;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
state$.errors[field].set(void 0);
|
|
55
|
+
validationInProgress.current.delete(fieldKey);
|
|
56
|
+
return void 0;
|
|
57
|
+
} catch (error) {
|
|
58
|
+
validationInProgress.current.delete(fieldKey);
|
|
59
|
+
const errorMessage = error instanceof Error ? error.message : "Validation error";
|
|
60
|
+
state$.errors[field].set(errorMessage);
|
|
61
|
+
return errorMessage;
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
[validationSchema, state$, fieldMetadataActions]
|
|
65
|
+
);
|
|
66
|
+
const validateForm = useCallback(async () => {
|
|
67
|
+
if (!validationSchema) return {};
|
|
68
|
+
const fields = Object.keys(validationSchema);
|
|
69
|
+
const errors2 = {};
|
|
70
|
+
await Promise.all(
|
|
71
|
+
fields.map(async (field) => {
|
|
72
|
+
const error = await validateField(field);
|
|
73
|
+
if (error) {
|
|
74
|
+
errors2[field] = error;
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
);
|
|
78
|
+
state$.errors.set(errors2);
|
|
79
|
+
return errors2;
|
|
80
|
+
}, [validationSchema, validateField, state$]);
|
|
81
|
+
const setFieldValue = useCallback(
|
|
82
|
+
(field, value) => {
|
|
83
|
+
state$.values[field].set(value);
|
|
84
|
+
const shouldRevalidate = revalidateOn === "onChange" && state$.hasValidated[String(field)].get();
|
|
85
|
+
if (shouldRevalidate && validationSchema?.[field]) {
|
|
86
|
+
validateField(field);
|
|
87
|
+
}
|
|
88
|
+
if (debug) {
|
|
89
|
+
console.log("[useForm] setFieldValue:", { field, value });
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
[state$, revalidateOn, validationSchema, validateField, debug]
|
|
93
|
+
);
|
|
94
|
+
const setFieldTouched = useCallback(
|
|
95
|
+
(field, touched2) => {
|
|
96
|
+
state$.touched[field].set(touched2);
|
|
97
|
+
if (touched2 && validateOn === "onBlur" && validationSchema?.[field]) {
|
|
98
|
+
state$.hasValidated[String(field)].set(true);
|
|
99
|
+
validateField(field);
|
|
100
|
+
}
|
|
101
|
+
if (debug) {
|
|
102
|
+
console.log("[useForm] setFieldTouched:", { field, touched: touched2 });
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
[state$, validateOn, validationSchema, validateField, debug]
|
|
106
|
+
);
|
|
107
|
+
const resetForm = useCallback(() => {
|
|
108
|
+
state$.values.set(state$.initialValues.get());
|
|
109
|
+
state$.errors.set({});
|
|
110
|
+
state$.touched.set({});
|
|
111
|
+
state$.isSubmitting.set(false);
|
|
112
|
+
state$.status.set("idle");
|
|
113
|
+
state$.hasValidated.set({});
|
|
114
|
+
fieldMetadataActions.clear();
|
|
115
|
+
if (debug) {
|
|
116
|
+
console.log("[useForm] Form reset");
|
|
117
|
+
}
|
|
118
|
+
}, [state$, fieldMetadataActions, debug]);
|
|
119
|
+
const handleSubmit = useCallback(
|
|
120
|
+
async (e) => {
|
|
121
|
+
e?.preventDefault();
|
|
122
|
+
if (debug) {
|
|
123
|
+
console.log("[useForm] handleSubmit started");
|
|
124
|
+
}
|
|
125
|
+
state$.isSubmitting.set(true);
|
|
126
|
+
state$.status.set("submitting");
|
|
127
|
+
try {
|
|
128
|
+
const errors2 = await validateForm();
|
|
129
|
+
const hasErrors = Object.keys(errors2).length > 0;
|
|
130
|
+
if (hasErrors) {
|
|
131
|
+
state$.status.set("error");
|
|
132
|
+
onError?.(errors2);
|
|
133
|
+
if (debug) {
|
|
134
|
+
console.log("[useForm] Validation errors:", errors2);
|
|
135
|
+
}
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const helpers = {
|
|
139
|
+
setValues: (values2) => {
|
|
140
|
+
if (typeof values2 === "function") {
|
|
141
|
+
state$.values.set(values2(state$.values.get()));
|
|
142
|
+
} else {
|
|
143
|
+
state$.values.set(values2);
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
setFieldValue,
|
|
147
|
+
setErrors: (errors3) => state$.errors.set(errors3),
|
|
148
|
+
setFieldError: (field, error) => state$.errors[field].set(error),
|
|
149
|
+
setTouched: (touched2) => state$.touched.set(touched2),
|
|
150
|
+
setFieldTouched,
|
|
151
|
+
setSubmitting: (submitting) => state$.isSubmitting.set(submitting),
|
|
152
|
+
resetForm
|
|
153
|
+
};
|
|
154
|
+
await onSubmit(state$.values.get(), helpers);
|
|
155
|
+
state$.status.set("success");
|
|
156
|
+
if (debug) {
|
|
157
|
+
console.log("[useForm] Submit successful");
|
|
158
|
+
}
|
|
159
|
+
} catch (error) {
|
|
160
|
+
state$.status.set("error");
|
|
161
|
+
if (debug) {
|
|
162
|
+
console.error("[useForm] Submit error:", error);
|
|
163
|
+
}
|
|
164
|
+
throw error;
|
|
165
|
+
} finally {
|
|
166
|
+
state$.isSubmitting.set(false);
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
[
|
|
170
|
+
state$,
|
|
171
|
+
validateForm,
|
|
172
|
+
onSubmit,
|
|
173
|
+
onError,
|
|
174
|
+
setFieldValue,
|
|
175
|
+
setFieldTouched,
|
|
176
|
+
resetForm,
|
|
177
|
+
debug
|
|
178
|
+
]
|
|
179
|
+
);
|
|
180
|
+
const getFieldProps = useCallback(
|
|
181
|
+
(field) => {
|
|
182
|
+
return {
|
|
183
|
+
name: String(field),
|
|
184
|
+
value: state$.values[field].get(),
|
|
185
|
+
onChange: (value) => setFieldValue(field, value),
|
|
186
|
+
onBlur: () => setFieldTouched(field, true)
|
|
187
|
+
};
|
|
188
|
+
},
|
|
189
|
+
[state$, setFieldValue, setFieldTouched]
|
|
190
|
+
);
|
|
191
|
+
const getFieldMeta = useCallback(
|
|
192
|
+
(field) => {
|
|
193
|
+
const fieldKey = String(field);
|
|
194
|
+
const metadata = fieldMetadataActions.get(fieldKey);
|
|
195
|
+
return {
|
|
196
|
+
error: state$.errors[field].get(),
|
|
197
|
+
touched: state$.touched[field].get() ?? false,
|
|
198
|
+
isDirty: state$.values[field].get() !== state$.initialValues[field].get(),
|
|
199
|
+
isValidating: validationInProgress.current.has(fieldKey),
|
|
200
|
+
// Additional metadata from @opensite/hooks
|
|
201
|
+
validationCount: metadata?.validationCount,
|
|
202
|
+
lastValidated: metadata?.lastValidated
|
|
203
|
+
};
|
|
204
|
+
},
|
|
205
|
+
[state$, fieldMetadataActions]
|
|
206
|
+
);
|
|
207
|
+
const values = useSelector(() => state$.values.get());
|
|
208
|
+
const errors = useSelector(() => state$.errors.get());
|
|
209
|
+
const touched = useSelector(() => state$.touched.get());
|
|
210
|
+
const isSubmitting = useSelector(() => state$.isSubmitting.get());
|
|
211
|
+
const status = useSelector(() => state$.status.get());
|
|
212
|
+
const isValid = useSelector(() => Object.keys(state$.errors.get()).length === 0);
|
|
213
|
+
const isDirty = useSelector(() => {
|
|
214
|
+
const currentValues = state$.values.get();
|
|
215
|
+
const initialValues2 = state$.initialValues.get();
|
|
216
|
+
return Object.keys(currentValues).some(
|
|
217
|
+
(key) => currentValues[key] !== initialValues2[key]
|
|
218
|
+
);
|
|
219
|
+
});
|
|
220
|
+
return {
|
|
221
|
+
// State
|
|
222
|
+
values,
|
|
223
|
+
errors,
|
|
224
|
+
touched,
|
|
225
|
+
isSubmitting,
|
|
226
|
+
isValid,
|
|
227
|
+
isDirty,
|
|
228
|
+
status,
|
|
229
|
+
// Actions
|
|
230
|
+
handleSubmit,
|
|
231
|
+
setValues: (values2) => {
|
|
232
|
+
if (typeof values2 === "function") {
|
|
233
|
+
state$.values.set(values2(state$.values.get()));
|
|
234
|
+
} else {
|
|
235
|
+
state$.values.set(values2);
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
setFieldValue,
|
|
239
|
+
setErrors: (errors2) => state$.errors.set(errors2),
|
|
240
|
+
setFieldError: (field, error) => state$.errors[field].set(error),
|
|
241
|
+
setTouched: (touched2) => state$.touched.set(touched2),
|
|
242
|
+
setFieldTouched,
|
|
243
|
+
validateForm,
|
|
244
|
+
validateField,
|
|
245
|
+
resetForm,
|
|
246
|
+
getFieldProps,
|
|
247
|
+
getFieldMeta
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
var FormContext = React3.createContext(null);
|
|
251
|
+
FormContext.displayName = "FormContext";
|
|
252
|
+
function useField(options) {
|
|
253
|
+
const { name, validate, transform } = options;
|
|
254
|
+
const form = useContext(FormContext);
|
|
255
|
+
if (!form) {
|
|
256
|
+
throw new Error(
|
|
257
|
+
"useField must be used within a FormContext. Wrap your component with <Form> or use useForm's getFieldProps instead."
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
const baseFieldProps = form.getFieldProps(name);
|
|
261
|
+
const field = {
|
|
262
|
+
...baseFieldProps,
|
|
263
|
+
value: baseFieldProps.value,
|
|
264
|
+
onChange: (value) => {
|
|
265
|
+
const transformedValue = transform ? transform(value) : value;
|
|
266
|
+
baseFieldProps.onChange(transformedValue);
|
|
267
|
+
if (validate) {
|
|
268
|
+
const result = validate(transformedValue, form.values);
|
|
269
|
+
if (result instanceof Promise) {
|
|
270
|
+
result.then((error) => {
|
|
271
|
+
if (error !== void 0) {
|
|
272
|
+
form.setFieldError(name, error);
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
} else if (result !== void 0) {
|
|
276
|
+
form.setFieldError(name, result);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
const meta = form.getFieldMeta(name);
|
|
282
|
+
const helpers = {
|
|
283
|
+
setValue: useCallback(
|
|
284
|
+
(value) => {
|
|
285
|
+
const transformedValue = transform ? transform(value) : value;
|
|
286
|
+
form.setFieldValue(name, transformedValue);
|
|
287
|
+
},
|
|
288
|
+
[name, transform, form]
|
|
289
|
+
),
|
|
290
|
+
setTouched: useCallback(
|
|
291
|
+
(touched) => {
|
|
292
|
+
form.setFieldTouched(name, touched);
|
|
293
|
+
},
|
|
294
|
+
[name, form]
|
|
295
|
+
),
|
|
296
|
+
setError: useCallback(
|
|
297
|
+
(error) => {
|
|
298
|
+
form.setFieldError(name, error);
|
|
299
|
+
},
|
|
300
|
+
[name, form]
|
|
301
|
+
)
|
|
302
|
+
};
|
|
303
|
+
return {
|
|
304
|
+
field,
|
|
305
|
+
meta,
|
|
306
|
+
helpers
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
var FieldFeedback = ({
|
|
310
|
+
errorId,
|
|
311
|
+
errorClassName,
|
|
312
|
+
error,
|
|
313
|
+
shouldRenderError
|
|
314
|
+
}) => {
|
|
315
|
+
const errorText = Array.isArray(error) ? error.join(", ") : error;
|
|
316
|
+
if (!errorText || !shouldRenderError) return null;
|
|
317
|
+
return /* @__PURE__ */ React3.createElement(FieldError, { id: errorId, className: errorClassName }, errorText);
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
// src/core/Field.tsx
|
|
321
|
+
function Field2({
|
|
322
|
+
name,
|
|
323
|
+
label,
|
|
324
|
+
description,
|
|
325
|
+
children,
|
|
326
|
+
showError = true,
|
|
327
|
+
className,
|
|
328
|
+
errorClassName,
|
|
329
|
+
required = false,
|
|
330
|
+
validate
|
|
331
|
+
}) {
|
|
332
|
+
const fieldState = useField({ name, validate });
|
|
333
|
+
const { meta } = fieldState;
|
|
334
|
+
const hasError = React3.useMemo(() => {
|
|
335
|
+
return showError && meta.touched && meta.error ? true : false;
|
|
336
|
+
}, [meta?.touched, meta?.error, showError]);
|
|
337
|
+
const errorId = `${name}-error`;
|
|
338
|
+
const descriptionId = `${name}-description`;
|
|
339
|
+
return /* @__PURE__ */ React3.createElement(
|
|
340
|
+
Field,
|
|
341
|
+
{
|
|
342
|
+
className,
|
|
343
|
+
"data-field": name,
|
|
344
|
+
invalid: hasError
|
|
345
|
+
},
|
|
346
|
+
/* @__PURE__ */ React3.createElement(
|
|
347
|
+
LabelGroup,
|
|
348
|
+
{
|
|
349
|
+
labelHtmlFor: name,
|
|
350
|
+
required,
|
|
351
|
+
variant: "label",
|
|
352
|
+
secondaryId: descriptionId,
|
|
353
|
+
secondary: description,
|
|
354
|
+
primary: label
|
|
355
|
+
}
|
|
356
|
+
),
|
|
357
|
+
/* @__PURE__ */ React3.createElement("div", { "data-slot": "field-control" }, typeof children === "function" ? children(fieldState) : children),
|
|
358
|
+
/* @__PURE__ */ React3.createElement(
|
|
359
|
+
FieldFeedback,
|
|
360
|
+
{
|
|
361
|
+
errorId,
|
|
362
|
+
errorClassName,
|
|
363
|
+
shouldRenderError: hasError,
|
|
364
|
+
error: meta.error
|
|
365
|
+
}
|
|
366
|
+
)
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
Field2.displayName = "Field";
|
|
370
|
+
|
|
371
|
+
export { Field2 as Field, FormContext, useField, useForm };
|
|
372
|
+
//# sourceMappingURL=chunk-24RPM43T.js.map
|
|
373
|
+
//# sourceMappingURL=chunk-24RPM43T.js.map
|