@classytic/formkit 1.0.3 → 1.2.2
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/CHANGELOG.md +88 -56
- package/README.md +465 -113
- package/dist/index.d.mts +914 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +1044 -0
- package/dist/index.mjs.map +1 -0
- package/dist/server.d.mts +625 -0
- package/dist/server.d.mts.map +1 -0
- package/dist/server.mjs +418 -0
- package/dist/server.mjs.map +1 -0
- package/package.json +24 -31
- package/dist/index.cjs +0 -233
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -511
- package/dist/index.d.ts +0 -511
- package/dist/index.js +0 -223
- package/dist/index.js.map +0 -1
package/dist/server.mjs
ADDED
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
import { clsx } from "clsx";
|
|
2
|
+
import { twMerge } from "tailwind-merge";
|
|
3
|
+
|
|
4
|
+
//#region src/utils.ts
|
|
5
|
+
/**
|
|
6
|
+
* Utility function to merge CSS classes with Tailwind CSS conflict resolution.
|
|
7
|
+
*
|
|
8
|
+
* Combines `clsx` for conditional class handling with `tailwind-merge`
|
|
9
|
+
* for proper Tailwind CSS class conflict resolution.
|
|
10
|
+
*
|
|
11
|
+
* @param inputs - Class values to merge (strings, arrays, objects, or conditionals)
|
|
12
|
+
* @returns Merged and deduplicated class string
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```tsx
|
|
16
|
+
* // Basic usage
|
|
17
|
+
* cn("px-2 py-1", "px-4") // "py-1 px-4"
|
|
18
|
+
*
|
|
19
|
+
* // Conditional classes
|
|
20
|
+
* cn("base", isActive && "active", { "disabled": isDisabled })
|
|
21
|
+
*
|
|
22
|
+
* // Arrays
|
|
23
|
+
* cn(["flex", "items-center"], "gap-2")
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
function cn(...inputs) {
|
|
27
|
+
return twMerge(clsx(inputs));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region src/schema.ts
|
|
32
|
+
/**
|
|
33
|
+
* Resolves a dot-notation path against an object.
|
|
34
|
+
* Handles array indices: "items.0.name" resolves through arrays correctly.
|
|
35
|
+
*/
|
|
36
|
+
function getNestedValue(obj, path) {
|
|
37
|
+
const parts = path.split(".");
|
|
38
|
+
let current = obj;
|
|
39
|
+
for (const part of parts) {
|
|
40
|
+
if (current == null || typeof current !== "object") return void 0;
|
|
41
|
+
if (Array.isArray(current)) {
|
|
42
|
+
const index = Number(part);
|
|
43
|
+
if (Number.isNaN(index)) return void 0;
|
|
44
|
+
current = current[index];
|
|
45
|
+
} else current = current[part];
|
|
46
|
+
}
|
|
47
|
+
return current;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Evaluates a single condition rule against form values.
|
|
51
|
+
* Supports both flat dotted keys ("address.city" as literal key) and
|
|
52
|
+
* nested object resolution (values.address.city). Flat key takes priority
|
|
53
|
+
* because DynamicFieldWrapper reconstructs watched values as flat keys.
|
|
54
|
+
*/
|
|
55
|
+
function evaluateRule(rule, formValues) {
|
|
56
|
+
const watchPath = rule.watch;
|
|
57
|
+
const obj = formValues;
|
|
58
|
+
const value = watchPath in obj ? obj[watchPath] : getNestedValue(obj, watchPath);
|
|
59
|
+
switch (rule.operator) {
|
|
60
|
+
case "===": return value === rule.value;
|
|
61
|
+
case "!==": return value !== rule.value;
|
|
62
|
+
case "in": return Array.isArray(rule.value) && rule.value.includes(value);
|
|
63
|
+
case "not-in": return Array.isArray(rule.value) && !rule.value.includes(value);
|
|
64
|
+
case "truthy": return Boolean(value);
|
|
65
|
+
case "falsy": return !value;
|
|
66
|
+
default: return false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Type guard: checks if a condition is a ConditionConfig (has `rules` array).
|
|
71
|
+
*/
|
|
72
|
+
function isConditionConfig(condition) {
|
|
73
|
+
return typeof condition === "object" && !Array.isArray(condition) && "rules" in condition;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Extracts the rules array from any non-function condition shape.
|
|
77
|
+
*/
|
|
78
|
+
function toRules(condition) {
|
|
79
|
+
if (isConditionConfig(condition)) return {
|
|
80
|
+
rules: condition.rules,
|
|
81
|
+
logic: condition.logic ?? "and"
|
|
82
|
+
};
|
|
83
|
+
return {
|
|
84
|
+
rules: Array.isArray(condition) ? condition : [condition],
|
|
85
|
+
logic: "and"
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Evaluates a conditional rule, array of rules, or a ConditionConfig against form values.
|
|
90
|
+
* Supports AND (default) and OR logic via ConditionConfig.
|
|
91
|
+
*
|
|
92
|
+
* @param condition - The condition function, rule(s), or config
|
|
93
|
+
* @param formValues - The form values to evaluate against
|
|
94
|
+
* @returns boolean indicating if condition matches
|
|
95
|
+
*/
|
|
96
|
+
function evaluateCondition(condition, formValues) {
|
|
97
|
+
if (!condition) return true;
|
|
98
|
+
if (typeof condition === "function") return condition(formValues);
|
|
99
|
+
const { rules, logic } = toRules(condition);
|
|
100
|
+
const evalFn = (rule) => evaluateRule(rule, formValues);
|
|
101
|
+
return logic === "or" ? rules.some(evalFn) : rules.every(evalFn);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Extracts all watch names from a condition to optimize `useWatch`.
|
|
105
|
+
* Handles single rules, arrays, and ConditionConfig objects.
|
|
106
|
+
*/
|
|
107
|
+
function extractWatchNames(condition) {
|
|
108
|
+
if (!condition || typeof condition === "function") return [];
|
|
109
|
+
const { rules } = toRules(condition);
|
|
110
|
+
return rules.map((r) => r.watch);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Strictly types a comprehensive form schema, granting exact intellisense bounds across conditions and nested watches.
|
|
114
|
+
*/
|
|
115
|
+
function defineSchema(schema) {
|
|
116
|
+
return schema;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Standard utility to strictly type a standalone field out-of-bounds, useful for externalizing massive schema structures.
|
|
120
|
+
*/
|
|
121
|
+
function defineField(field) {
|
|
122
|
+
return field;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Standard utility to strictly type a standalone logic section layout block.
|
|
126
|
+
*/
|
|
127
|
+
function defineSection(section) {
|
|
128
|
+
return section;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Extracts default values from a form schema.
|
|
132
|
+
* Walks all sections and fields, respecting nameSpace prefixes and group nesting.
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```ts
|
|
136
|
+
* const defaults = extractDefaultValues(schema);
|
|
137
|
+
* const form = useForm({ defaultValues: defaults });
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
function extractDefaultValues(schema) {
|
|
141
|
+
const defaults = {};
|
|
142
|
+
for (const section of schema.sections) {
|
|
143
|
+
const prefix = section.nameSpace ? `${section.nameSpace}.` : "";
|
|
144
|
+
if (!section.fields) continue;
|
|
145
|
+
for (const field of section.fields) {
|
|
146
|
+
if (field.defaultValue !== void 0) defaults[`${prefix}${field.name}`] = field.defaultValue;
|
|
147
|
+
if (field.itemFields && field.type !== "array") {
|
|
148
|
+
for (const sub of field.itemFields) if (sub.defaultValue !== void 0) defaults[`${prefix}${field.name}.${sub.name}`] = sub.defaultValue;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return defaults;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Generates react-hook-form `RegisterOptions`-compatible validation rules
|
|
156
|
+
* from a field's schema props. Maps `required`, `min`, `max`, `minLength`,
|
|
157
|
+
* `maxLength`, `pattern`, and `validate` to RHF rules.
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* ```tsx
|
|
161
|
+
* import { buildValidationRules } from '@classytic/formkit';
|
|
162
|
+
*
|
|
163
|
+
* function FormInput({ field, control }: FieldComponentProps) {
|
|
164
|
+
* const rules = buildValidationRules(field);
|
|
165
|
+
* return <Controller name={field.name} control={control} rules={rules} render={...} />;
|
|
166
|
+
* }
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
function buildValidationRules(field) {
|
|
170
|
+
const rules = {};
|
|
171
|
+
if (field.required) rules.required = `${field.label || field.name} is required`;
|
|
172
|
+
if (field.minLength !== void 0) rules.minLength = {
|
|
173
|
+
value: field.minLength,
|
|
174
|
+
message: `At least ${field.minLength} characters`
|
|
175
|
+
};
|
|
176
|
+
if (field.maxLength !== void 0) rules.maxLength = {
|
|
177
|
+
value: field.maxLength,
|
|
178
|
+
message: `At most ${field.maxLength} characters`
|
|
179
|
+
};
|
|
180
|
+
if (field.min !== void 0) rules.min = {
|
|
181
|
+
value: field.min,
|
|
182
|
+
message: `Must be at least ${field.min}`
|
|
183
|
+
};
|
|
184
|
+
if (field.max !== void 0) rules.max = {
|
|
185
|
+
value: field.max,
|
|
186
|
+
message: `Must be at most ${field.max}`
|
|
187
|
+
};
|
|
188
|
+
if (field.pattern) rules.pattern = {
|
|
189
|
+
value: new RegExp(field.pattern),
|
|
190
|
+
message: "Invalid format"
|
|
191
|
+
};
|
|
192
|
+
if (field.validate) rules.validate = field.validate;
|
|
193
|
+
return rules;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
//#endregion
|
|
197
|
+
//#region src/builders.ts
|
|
198
|
+
/**
|
|
199
|
+
* Type-safe field builder helpers for schema-driven forms.
|
|
200
|
+
*
|
|
201
|
+
* Provides shorthand methods for common field types with sensible defaults,
|
|
202
|
+
* reducing boilerplate while maintaining full type safety.
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* ```ts
|
|
206
|
+
* import { field, section } from '@classytic/formkit';
|
|
207
|
+
*
|
|
208
|
+
* const schema = {
|
|
209
|
+
* sections: [
|
|
210
|
+
* section("personal", "Personal Info", [
|
|
211
|
+
* field.text("firstName", "First Name", { required: true }),
|
|
212
|
+
* field.email("email", "Email"),
|
|
213
|
+
* field.select("role", "Role", [
|
|
214
|
+
* { label: "Admin", value: "admin" },
|
|
215
|
+
* { label: "User", value: "user" },
|
|
216
|
+
* ]),
|
|
217
|
+
* ], { cols: 2 }),
|
|
218
|
+
* ],
|
|
219
|
+
* };
|
|
220
|
+
* ```
|
|
221
|
+
*/
|
|
222
|
+
const field = {
|
|
223
|
+
text: (name, label, props = {}) => ({
|
|
224
|
+
type: "text",
|
|
225
|
+
name,
|
|
226
|
+
label,
|
|
227
|
+
...props
|
|
228
|
+
}),
|
|
229
|
+
email: (name, label, props = {}) => ({
|
|
230
|
+
type: "email",
|
|
231
|
+
name,
|
|
232
|
+
label,
|
|
233
|
+
placeholder: "example@email.com",
|
|
234
|
+
...props
|
|
235
|
+
}),
|
|
236
|
+
url: (name, label, props = {}) => ({
|
|
237
|
+
type: "url",
|
|
238
|
+
name,
|
|
239
|
+
label,
|
|
240
|
+
placeholder: "https://example.com",
|
|
241
|
+
...props
|
|
242
|
+
}),
|
|
243
|
+
tel: (name, label, props = {}) => ({
|
|
244
|
+
type: "tel",
|
|
245
|
+
name,
|
|
246
|
+
label,
|
|
247
|
+
placeholder: "+1 (555) 000-0000",
|
|
248
|
+
...props
|
|
249
|
+
}),
|
|
250
|
+
password: (name, label, props = {}) => ({
|
|
251
|
+
type: "password",
|
|
252
|
+
name,
|
|
253
|
+
label,
|
|
254
|
+
...props
|
|
255
|
+
}),
|
|
256
|
+
number: (name, label, props = {}) => ({
|
|
257
|
+
type: "number",
|
|
258
|
+
name,
|
|
259
|
+
label,
|
|
260
|
+
min: 0,
|
|
261
|
+
...props
|
|
262
|
+
}),
|
|
263
|
+
textarea: (name, label, props = {}) => ({
|
|
264
|
+
type: "textarea",
|
|
265
|
+
name,
|
|
266
|
+
label,
|
|
267
|
+
rows: 3,
|
|
268
|
+
...props
|
|
269
|
+
}),
|
|
270
|
+
select: (name, label, options, props = {}) => ({
|
|
271
|
+
type: "select",
|
|
272
|
+
name,
|
|
273
|
+
label,
|
|
274
|
+
options,
|
|
275
|
+
...props
|
|
276
|
+
}),
|
|
277
|
+
combobox: (name, label, options, props = {}) => ({
|
|
278
|
+
type: "combobox",
|
|
279
|
+
name,
|
|
280
|
+
label,
|
|
281
|
+
options,
|
|
282
|
+
...props
|
|
283
|
+
}),
|
|
284
|
+
multiselect: (name, label, options, props = {}) => ({
|
|
285
|
+
type: "multiselect",
|
|
286
|
+
name,
|
|
287
|
+
label,
|
|
288
|
+
options,
|
|
289
|
+
placeholder: "Select options...",
|
|
290
|
+
...props
|
|
291
|
+
}),
|
|
292
|
+
dependentSelect: (name, label, props = {}) => ({
|
|
293
|
+
type: "dependentSelect",
|
|
294
|
+
name,
|
|
295
|
+
label,
|
|
296
|
+
...props
|
|
297
|
+
}),
|
|
298
|
+
switch: (name, label, props = {}) => ({
|
|
299
|
+
type: "switch",
|
|
300
|
+
name,
|
|
301
|
+
label,
|
|
302
|
+
...props
|
|
303
|
+
}),
|
|
304
|
+
boolean: (name, label, props = {}) => ({
|
|
305
|
+
type: "switch",
|
|
306
|
+
name,
|
|
307
|
+
label,
|
|
308
|
+
...props
|
|
309
|
+
}),
|
|
310
|
+
checkbox: (name, label, props = {}) => ({
|
|
311
|
+
type: "checkbox",
|
|
312
|
+
name,
|
|
313
|
+
label,
|
|
314
|
+
...props
|
|
315
|
+
}),
|
|
316
|
+
radio: (name, label, options, props = {}) => ({
|
|
317
|
+
type: "radio",
|
|
318
|
+
name,
|
|
319
|
+
label,
|
|
320
|
+
options,
|
|
321
|
+
...props
|
|
322
|
+
}),
|
|
323
|
+
date: (name, label, props = {}) => ({
|
|
324
|
+
type: "date",
|
|
325
|
+
name,
|
|
326
|
+
label,
|
|
327
|
+
...props
|
|
328
|
+
}),
|
|
329
|
+
tags: (name, label, props = {}) => ({
|
|
330
|
+
type: "tags",
|
|
331
|
+
name,
|
|
332
|
+
label,
|
|
333
|
+
placeholder: "Add tags...",
|
|
334
|
+
...props
|
|
335
|
+
}),
|
|
336
|
+
slug: (name, label, props = {}) => ({
|
|
337
|
+
type: "slug",
|
|
338
|
+
name,
|
|
339
|
+
label,
|
|
340
|
+
placeholder: "my-page-slug",
|
|
341
|
+
...props
|
|
342
|
+
}),
|
|
343
|
+
file: (name, label, props = {}) => ({
|
|
344
|
+
type: "file",
|
|
345
|
+
name,
|
|
346
|
+
label,
|
|
347
|
+
...props
|
|
348
|
+
}),
|
|
349
|
+
hidden: (name, props = {}) => ({
|
|
350
|
+
type: "hidden",
|
|
351
|
+
name,
|
|
352
|
+
...props
|
|
353
|
+
}),
|
|
354
|
+
group: (name, label, itemFields, props = {}) => ({
|
|
355
|
+
type: "group",
|
|
356
|
+
name,
|
|
357
|
+
label,
|
|
358
|
+
itemFields,
|
|
359
|
+
...props
|
|
360
|
+
}),
|
|
361
|
+
array: (name, label, itemFields, props = {}) => ({
|
|
362
|
+
type: "array",
|
|
363
|
+
name,
|
|
364
|
+
label,
|
|
365
|
+
itemFields,
|
|
366
|
+
...props
|
|
367
|
+
}),
|
|
368
|
+
custom: (name, label, render, props = {}) => ({
|
|
369
|
+
type: "custom",
|
|
370
|
+
name,
|
|
371
|
+
label,
|
|
372
|
+
render,
|
|
373
|
+
...props
|
|
374
|
+
})
|
|
375
|
+
};
|
|
376
|
+
/**
|
|
377
|
+
* Create a section definition with sensible defaults.
|
|
378
|
+
*
|
|
379
|
+
* @param id - Unique section identifier
|
|
380
|
+
* @param title - Section title
|
|
381
|
+
* @param fields - Array of field definitions
|
|
382
|
+
* @param props - Additional section configuration
|
|
383
|
+
*
|
|
384
|
+
* @example
|
|
385
|
+
* ```ts
|
|
386
|
+
* section("personal", "Personal Info", [
|
|
387
|
+
* field.text("name", "Name", { required: true }),
|
|
388
|
+
* field.email("email", "Email"),
|
|
389
|
+
* ], { cols: 2, variant: "card" })
|
|
390
|
+
* ```
|
|
391
|
+
*/
|
|
392
|
+
function section(id, title, fields, props = {}) {
|
|
393
|
+
const { cols = 2, ...rest } = props;
|
|
394
|
+
return {
|
|
395
|
+
id,
|
|
396
|
+
title,
|
|
397
|
+
fields,
|
|
398
|
+
cols,
|
|
399
|
+
...rest
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Create a section without a title (transparent section).
|
|
404
|
+
* Useful for grouping fields without visual separation.
|
|
405
|
+
*/
|
|
406
|
+
function sectionUntitled(fields, props = {}) {
|
|
407
|
+
const { cols = 1, ...rest } = props;
|
|
408
|
+
return {
|
|
409
|
+
fields,
|
|
410
|
+
cols,
|
|
411
|
+
variant: "transparent",
|
|
412
|
+
...rest
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
//#endregion
|
|
417
|
+
export { buildValidationRules, cn, defineField, defineSchema, defineSection, evaluateCondition, extractDefaultValues, extractWatchNames, field, section, sectionUntitled };
|
|
418
|
+
//# sourceMappingURL=server.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.mjs","names":[],"sources":["../src/utils.ts","../src/schema.ts","../src/builders.ts"],"sourcesContent":["import { clsx } from \"clsx\";\r\nimport { twMerge } from \"tailwind-merge\";\r\nimport type { ClassValue } from \"clsx\";\r\n\r\n/**\r\n * Utility function to merge CSS classes with Tailwind CSS conflict resolution.\r\n *\r\n * Combines `clsx` for conditional class handling with `tailwind-merge`\r\n * for proper Tailwind CSS class conflict resolution.\r\n *\r\n * @param inputs - Class values to merge (strings, arrays, objects, or conditionals)\r\n * @returns Merged and deduplicated class string\r\n *\r\n * @example\r\n * ```tsx\r\n * // Basic usage\r\n * cn(\"px-2 py-1\", \"px-4\") // \"py-1 px-4\"\r\n *\r\n * // Conditional classes\r\n * cn(\"base\", isActive && \"active\", { \"disabled\": isDisabled })\r\n *\r\n * // Arrays\r\n * cn([\"flex\", \"items-center\"], \"gap-2\")\r\n * ```\r\n */\r\nexport function cn(...inputs: ClassValue[]): string {\r\n return twMerge(clsx(inputs));\r\n}\r\n\r\n/**\r\n * Shallow equality check for arrays and primitives.\r\n * Used to stabilize useWatch output without JSON.stringify overhead.\r\n */\r\nexport function shallowEqual(a: unknown, b: unknown): boolean {\r\n if (Object.is(a, b)) return true;\r\n if (\r\n typeof a !== \"object\" ||\r\n typeof b !== \"object\" ||\r\n a === null ||\r\n b === null\r\n ) {\r\n return false;\r\n }\r\n\r\n if (Array.isArray(a) && Array.isArray(b)) {\r\n if (a.length !== b.length) return false;\r\n for (let i = 0; i < a.length; i++) {\r\n if (!Object.is(a[i], b[i])) return false;\r\n }\r\n return true;\r\n }\r\n\r\n const keysA = Object.keys(a);\r\n const keysB = Object.keys(b);\r\n if (keysA.length !== keysB.length) return false;\r\n\r\n for (const key of keysA) {\r\n if (\r\n !Object.prototype.hasOwnProperty.call(b, key) ||\r\n !Object.is(\r\n (a as Record<string, unknown>)[key],\r\n (b as Record<string, unknown>)[key],\r\n )\r\n ) {\r\n return false;\r\n }\r\n }\r\n return true;\r\n}\r\n\r\n/**\r\n * Re-export ClassValue type for consumers who need it.\r\n */\r\nexport type { ClassValue };\r\n","import type { FieldValues } from \"react-hook-form\";\r\nimport type {\r\n FormSchema,\r\n Section,\r\n BaseField,\r\n ConditionRule,\r\n ConditionConfig,\r\n ValidationRules,\r\n} from \"./types\";\r\n\r\n// ============================================================================\r\n// Condition Types (union used by both evaluateCondition and extractWatchNames)\r\n// ============================================================================\r\n\r\n/**\r\n * All supported condition shapes.\r\n */\r\nexport type Condition<TFieldValues extends FieldValues = FieldValues> =\r\n | ((formValues: Partial<TFieldValues>) => boolean)\r\n | ConditionRule<TFieldValues>\r\n | ConditionRule<TFieldValues>[]\r\n | ConditionConfig<TFieldValues>\r\n | undefined;\r\n\r\n// ============================================================================\r\n// Internal Helpers\r\n// ============================================================================\r\n\r\n/**\r\n * Resolves a dot-notation path against an object.\r\n * Handles array indices: \"items.0.name\" resolves through arrays correctly.\r\n */\r\nfunction getNestedValue(obj: Record<string, unknown>, path: string): unknown {\r\n const parts = path.split(\".\");\r\n let current: unknown = obj;\r\n for (const part of parts) {\r\n if (current == null || typeof current !== \"object\") return undefined;\r\n if (Array.isArray(current)) {\r\n const index = Number(part);\r\n if (Number.isNaN(index)) return undefined;\r\n current = current[index];\r\n } else {\r\n current = (current as Record<string, unknown>)[part];\r\n }\r\n }\r\n return current;\r\n}\r\n\r\n/**\r\n * Evaluates a single condition rule against form values.\r\n * Supports both flat dotted keys (\"address.city\" as literal key) and\r\n * nested object resolution (values.address.city). Flat key takes priority\r\n * because DynamicFieldWrapper reconstructs watched values as flat keys.\r\n */\r\nfunction evaluateRule<TFieldValues extends FieldValues>(\r\n rule: ConditionRule<TFieldValues>,\r\n formValues: Partial<TFieldValues>,\r\n): boolean {\r\n const watchPath = rule.watch as string;\r\n const obj = formValues as Record<string, unknown>;\r\n // Flat key first (handles DynamicFieldWrapper's { \"address.city\": val }),\r\n // then nested resolution (handles full form values { address: { city: val } })\r\n const value = watchPath in obj\r\n ? obj[watchPath]\r\n : getNestedValue(obj, watchPath);\r\n\r\n switch (rule.operator) {\r\n case \"===\":\r\n return value === rule.value;\r\n case \"!==\":\r\n return value !== rule.value;\r\n case \"in\":\r\n return Array.isArray(rule.value) && rule.value.includes(value);\r\n case \"not-in\":\r\n return Array.isArray(rule.value) && !rule.value.includes(value);\r\n case \"truthy\":\r\n return Boolean(value);\r\n case \"falsy\":\r\n return !value;\r\n default:\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Type guard: checks if a condition is a ConditionConfig (has `rules` array).\r\n */\r\nfunction isConditionConfig<TFieldValues extends FieldValues>(\r\n condition: NonNullable<Condition<TFieldValues>>,\r\n): condition is ConditionConfig<TFieldValues> {\r\n return (\r\n typeof condition === \"object\" &&\r\n !Array.isArray(condition) &&\r\n \"rules\" in condition\r\n );\r\n}\r\n\r\n/**\r\n * Extracts the rules array from any non-function condition shape.\r\n */\r\nfunction toRules<TFieldValues extends FieldValues>(\r\n condition: Exclude<NonNullable<Condition<TFieldValues>>, Function>,\r\n): { rules: ConditionRule<TFieldValues>[]; logic: \"and\" | \"or\" } {\r\n if (isConditionConfig(condition)) {\r\n return { rules: condition.rules, logic: condition.logic ?? \"and\" };\r\n }\r\n const rules = Array.isArray(condition) ? condition : [condition];\r\n return { rules, logic: \"and\" };\r\n}\r\n\r\n// ============================================================================\r\n// Public API\r\n// ============================================================================\r\n\r\n/**\r\n * Evaluates a conditional rule, array of rules, or a ConditionConfig against form values.\r\n * Supports AND (default) and OR logic via ConditionConfig.\r\n *\r\n * @param condition - The condition function, rule(s), or config\r\n * @param formValues - The form values to evaluate against\r\n * @returns boolean indicating if condition matches\r\n */\r\nexport function evaluateCondition<\r\n TFieldValues extends FieldValues = FieldValues,\r\n>(\r\n condition: Condition<TFieldValues>,\r\n formValues: Partial<TFieldValues>,\r\n): boolean {\r\n if (!condition) return true;\r\n\r\n if (typeof condition === \"function\") {\r\n return condition(formValues);\r\n }\r\n\r\n const { rules, logic } = toRules(condition);\r\n const evalFn = (rule: ConditionRule<TFieldValues>) =>\r\n evaluateRule(rule, formValues);\r\n\r\n return logic === \"or\" ? rules.some(evalFn) : rules.every(evalFn);\r\n}\r\n\r\n/**\r\n * Extracts all watch names from a condition to optimize `useWatch`.\r\n * Handles single rules, arrays, and ConditionConfig objects.\r\n */\r\nexport function extractWatchNames<\r\n TFieldValues extends FieldValues = FieldValues,\r\n>(condition: Condition<TFieldValues>): string[] {\r\n if (!condition || typeof condition === \"function\") return [];\r\n\r\n const { rules } = toRules(condition);\r\n return rules.map((r) => r.watch as string);\r\n}\r\n\r\n/**\r\n * Strictly types a comprehensive form schema, granting exact intellisense bounds across conditions and nested watches.\r\n */\r\nexport function defineSchema<TFieldValues extends FieldValues = FieldValues>(\r\n schema: FormSchema<TFieldValues>,\r\n): FormSchema<TFieldValues> {\r\n return schema;\r\n}\r\n\r\n/**\r\n * Standard utility to strictly type a standalone field out-of-bounds, useful for externalizing massive schema structures.\r\n */\r\nexport function defineField<TFieldValues extends FieldValues = FieldValues>(\r\n field: BaseField<TFieldValues>,\r\n): BaseField<TFieldValues> {\r\n return field;\r\n}\r\n\r\n/**\r\n * Standard utility to strictly type a standalone logic section layout block.\r\n */\r\nexport function defineSection<TFieldValues extends FieldValues = FieldValues>(\r\n section: Section<TFieldValues>,\r\n): Section<TFieldValues> {\r\n return section;\r\n}\r\n\r\n/**\r\n * Extracts default values from a form schema.\r\n * Walks all sections and fields, respecting nameSpace prefixes and group nesting.\r\n *\r\n * @example\r\n * ```ts\r\n * const defaults = extractDefaultValues(schema);\r\n * const form = useForm({ defaultValues: defaults });\r\n * ```\r\n */\r\nexport function extractDefaultValues<\r\n TFieldValues extends FieldValues = FieldValues,\r\n>(schema: FormSchema<TFieldValues>): Partial<TFieldValues> {\r\n const defaults: Record<string, unknown> = {};\r\n\r\n for (const section of schema.sections) {\r\n const prefix = section.nameSpace ? `${section.nameSpace}.` : \"\";\r\n if (!section.fields) continue;\r\n\r\n for (const field of section.fields) {\r\n if (field.defaultValue !== undefined) {\r\n defaults[`${prefix}${field.name as string}`] = field.defaultValue;\r\n }\r\n // Handle group itemFields defaults (skip array types — items are dynamic)\r\n if (field.itemFields && field.type !== \"array\") {\r\n for (const sub of field.itemFields) {\r\n if (sub.defaultValue !== undefined) {\r\n defaults[\r\n `${prefix}${field.name as string}.${sub.name as string}`\r\n ] = sub.defaultValue;\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n return defaults as Partial<TFieldValues>;\r\n}\r\n\r\n/**\r\n * Generates react-hook-form `RegisterOptions`-compatible validation rules\r\n * from a field's schema props. Maps `required`, `min`, `max`, `minLength`,\r\n * `maxLength`, `pattern`, and `validate` to RHF rules.\r\n *\r\n * @example\r\n * ```tsx\r\n * import { buildValidationRules } from '@classytic/formkit';\r\n *\r\n * function FormInput({ field, control }: FieldComponentProps) {\r\n * const rules = buildValidationRules(field);\r\n * return <Controller name={field.name} control={control} rules={rules} render={...} />;\r\n * }\r\n * ```\r\n */\r\nexport function buildValidationRules<\r\n TFieldValues extends FieldValues = FieldValues,\r\n>(field: BaseField<TFieldValues>): ValidationRules {\r\n const rules: ValidationRules = {};\r\n\r\n if (field.required) {\r\n rules.required = `${field.label || (field.name as string)} is required`;\r\n }\r\n if (field.minLength !== undefined) {\r\n rules.minLength = {\r\n value: field.minLength,\r\n message: `At least ${field.minLength} characters`,\r\n };\r\n }\r\n if (field.maxLength !== undefined) {\r\n rules.maxLength = {\r\n value: field.maxLength,\r\n message: `At most ${field.maxLength} characters`,\r\n };\r\n }\r\n if (field.min !== undefined) {\r\n rules.min = {\r\n value: field.min,\r\n message: `Must be at least ${field.min}`,\r\n };\r\n }\r\n if (field.max !== undefined) {\r\n rules.max = {\r\n value: field.max,\r\n message: `Must be at most ${field.max}`,\r\n };\r\n }\r\n if (field.pattern) {\r\n rules.pattern = {\r\n value: new RegExp(field.pattern),\r\n message: \"Invalid format\",\r\n };\r\n }\r\n if (field.validate) {\r\n rules.validate = field.validate as ValidationRules[\"validate\"];\r\n }\r\n\r\n return rules;\r\n}\r\n","import type { ReactNode } from \"react\";\r\nimport type { FieldValues, Control, FieldError } from \"react-hook-form\";\r\nimport type {\r\n BaseField,\r\n FieldOption,\r\n FieldOptionGroup,\r\n Section,\r\n} from \"./types\";\r\n\r\n// ============================================================================\r\n// Types\r\n// ============================================================================\r\n\r\n/**\r\n * Additional field props for builder helpers.\r\n * Accepts all BaseField properties except `name`, `type`, and `label`\r\n * which are set by the builder method.\r\n */\r\ntype FieldProps<TFieldValues extends FieldValues = FieldValues> = Omit<\r\n BaseField<TFieldValues>,\r\n \"name\" | \"type\" | \"label\"\r\n> & {\r\n /** Grid column class (e.g., \"col-span-2\") */\r\n gridColumn?: string;\r\n /** Icon for the left side of input */\r\n iconLeft?: ReactNode;\r\n /** Icon for the right side of input */\r\n iconRight?: ReactNode;\r\n /** Additional custom props */\r\n [key: string]: unknown;\r\n};\r\n\r\n/**\r\n * Section configuration props.\r\n */\r\ninterface SectionProps<TFieldValues extends FieldValues = FieldValues>\r\n extends Omit<Section<TFieldValues>, \"id\" | \"title\" | \"fields\" | \"cols\"> {\r\n cols?: number;\r\n}\r\n\r\n/**\r\n * Render function for custom field types.\r\n */\r\ntype CustomRenderFn = (props: {\r\n control: Control<FieldValues>;\r\n disabled?: boolean;\r\n error?: FieldError;\r\n}) => ReactNode;\r\n\r\n// ============================================================================\r\n// Field Builder\r\n// ============================================================================\r\n\r\n/**\r\n * Type-safe field builder helpers for schema-driven forms.\r\n *\r\n * Provides shorthand methods for common field types with sensible defaults,\r\n * reducing boilerplate while maintaining full type safety.\r\n *\r\n * @example\r\n * ```ts\r\n * import { field, section } from '@classytic/formkit';\r\n *\r\n * const schema = {\r\n * sections: [\r\n * section(\"personal\", \"Personal Info\", [\r\n * field.text(\"firstName\", \"First Name\", { required: true }),\r\n * field.email(\"email\", \"Email\"),\r\n * field.select(\"role\", \"Role\", [\r\n * { label: \"Admin\", value: \"admin\" },\r\n * { label: \"User\", value: \"user\" },\r\n * ]),\r\n * ], { cols: 2 }),\r\n * ],\r\n * };\r\n * ```\r\n */\r\nexport const field = {\r\n /**\r\n * Text input field.\r\n */\r\n text: (\r\n name: string,\r\n label: string,\r\n props: FieldProps = {},\r\n ): BaseField => ({\r\n type: \"text\",\r\n name,\r\n label,\r\n ...props,\r\n }),\r\n\r\n /**\r\n * Email input field with default placeholder.\r\n */\r\n email: (\r\n name: string,\r\n label: string,\r\n props: FieldProps = {},\r\n ): BaseField => ({\r\n type: \"email\",\r\n name,\r\n label,\r\n placeholder: \"example@email.com\",\r\n ...props,\r\n }),\r\n\r\n /**\r\n * URL input field with default placeholder.\r\n */\r\n url: (\r\n name: string,\r\n label: string,\r\n props: FieldProps = {},\r\n ): BaseField => ({\r\n type: \"url\",\r\n name,\r\n label,\r\n placeholder: \"https://example.com\",\r\n ...props,\r\n }),\r\n\r\n /**\r\n * Phone/tel input field with default placeholder.\r\n */\r\n tel: (\r\n name: string,\r\n label: string,\r\n props: FieldProps = {},\r\n ): BaseField => ({\r\n type: \"tel\",\r\n name,\r\n label,\r\n placeholder: \"+1 (555) 000-0000\",\r\n ...props,\r\n }),\r\n\r\n /**\r\n * Password input field.\r\n */\r\n password: (\r\n name: string,\r\n label: string,\r\n props: FieldProps = {},\r\n ): BaseField => ({\r\n type: \"password\",\r\n name,\r\n label,\r\n ...props,\r\n }),\r\n\r\n /**\r\n * Number input field with min: 0 default.\r\n */\r\n number: (\r\n name: string,\r\n label: string,\r\n props: FieldProps = {},\r\n ): BaseField => ({\r\n type: \"number\",\r\n name,\r\n label,\r\n min: 0,\r\n ...props,\r\n }),\r\n\r\n /**\r\n * Textarea field with default 3 rows.\r\n */\r\n textarea: (\r\n name: string,\r\n label: string,\r\n props: FieldProps = {},\r\n ): BaseField => ({\r\n type: \"textarea\",\r\n name,\r\n label,\r\n rows: 3,\r\n ...props,\r\n }),\r\n\r\n /**\r\n * Select dropdown field.\r\n */\r\n select: (\r\n name: string,\r\n label: string,\r\n options: (FieldOption | FieldOptionGroup)[],\r\n props: FieldProps = {},\r\n ): BaseField => ({\r\n type: \"select\",\r\n name,\r\n label,\r\n options,\r\n ...props,\r\n }),\r\n\r\n /**\r\n * Searchable combobox field.\r\n */\r\n combobox: (\r\n name: string,\r\n label: string,\r\n options: (FieldOption | FieldOptionGroup)[],\r\n props: FieldProps = {},\r\n ): BaseField => ({\r\n type: \"combobox\",\r\n name,\r\n label,\r\n options,\r\n ...props,\r\n }),\r\n\r\n /**\r\n * Multi-select field (tag choice).\r\n */\r\n multiselect: (\r\n name: string,\r\n label: string,\r\n options: (FieldOption | FieldOptionGroup)[],\r\n props: FieldProps = {},\r\n ): BaseField => ({\r\n type: \"multiselect\",\r\n name,\r\n label,\r\n options,\r\n placeholder: \"Select options...\",\r\n ...props,\r\n }),\r\n\r\n /**\r\n * Dependent select field that reacts to parent field changes.\r\n */\r\n dependentSelect: (\r\n name: string,\r\n label: string,\r\n props: FieldProps = {},\r\n ): BaseField => ({\r\n type: \"dependentSelect\",\r\n name,\r\n label,\r\n ...props,\r\n }),\r\n\r\n /**\r\n * Switch/toggle field.\r\n */\r\n switch: (\r\n name: string,\r\n label: string,\r\n props: FieldProps = {},\r\n ): BaseField => ({\r\n type: \"switch\",\r\n name,\r\n label,\r\n ...props,\r\n }),\r\n\r\n /**\r\n * Boolean field (alias for switch).\r\n */\r\n boolean: (\r\n name: string,\r\n label: string,\r\n props: FieldProps = {},\r\n ): BaseField => ({\r\n type: \"switch\",\r\n name,\r\n label,\r\n ...props,\r\n }),\r\n\r\n /**\r\n * Checkbox field.\r\n */\r\n checkbox: (\r\n name: string,\r\n label: string,\r\n props: FieldProps = {},\r\n ): BaseField => ({\r\n type: \"checkbox\",\r\n name,\r\n label,\r\n ...props,\r\n }),\r\n\r\n /**\r\n * Radio button group field.\r\n */\r\n radio: (\r\n name: string,\r\n label: string,\r\n options: FieldOption[],\r\n props: FieldProps = {},\r\n ): BaseField => ({\r\n type: \"radio\",\r\n name,\r\n label,\r\n options,\r\n ...props,\r\n }),\r\n\r\n /**\r\n * Date picker field.\r\n */\r\n date: (\r\n name: string,\r\n label: string,\r\n props: FieldProps = {},\r\n ): BaseField => ({\r\n type: \"date\",\r\n name,\r\n label,\r\n ...props,\r\n }),\r\n\r\n /**\r\n * Tag input field with default placeholder.\r\n */\r\n tags: (\r\n name: string,\r\n label: string,\r\n props: FieldProps = {},\r\n ): BaseField => ({\r\n type: \"tags\",\r\n name,\r\n label,\r\n placeholder: \"Add tags...\",\r\n ...props,\r\n }),\r\n\r\n /**\r\n * Slug field with auto-generation from source value.\r\n */\r\n slug: (\r\n name: string,\r\n label: string,\r\n props: FieldProps = {},\r\n ): BaseField => ({\r\n type: \"slug\",\r\n name,\r\n label,\r\n placeholder: \"my-page-slug\",\r\n ...props,\r\n }),\r\n\r\n /**\r\n * File upload field.\r\n */\r\n file: (\r\n name: string,\r\n label: string,\r\n props: FieldProps = {},\r\n ): BaseField => ({\r\n type: \"file\",\r\n name,\r\n label,\r\n ...props,\r\n }),\r\n\r\n /**\r\n * Hidden field (no UI).\r\n */\r\n hidden: (\r\n name: string,\r\n props: FieldProps = {},\r\n ): BaseField => ({\r\n type: \"hidden\",\r\n name,\r\n ...props,\r\n }),\r\n\r\n /**\r\n * Group field for nested objects.\r\n * Renders itemFields as a sub-grid within the form.\r\n *\r\n * @example\r\n * ```ts\r\n * field.group(\"address\", \"Address\", [\r\n * field.text(\"street\", \"Street\"),\r\n * field.text(\"city\", \"City\"),\r\n * field.text(\"zip\", \"ZIP Code\"),\r\n * ], { cols: 3 })\r\n * ```\r\n */\r\n group: (\r\n name: string,\r\n label: string,\r\n itemFields: BaseField[],\r\n props: FieldProps = {},\r\n ): BaseField => ({\r\n type: \"group\",\r\n name,\r\n label,\r\n itemFields,\r\n ...props,\r\n }),\r\n\r\n /**\r\n * Array/repeatable field.\r\n * Renders a dynamic list of sub-forms using react-hook-form's useFieldArray.\r\n *\r\n * @example\r\n * ```ts\r\n * field.array(\"contacts\", \"Contacts\", [\r\n * field.text(\"name\", \"Name\"),\r\n * field.email(\"email\", \"Email\"),\r\n * ])\r\n * ```\r\n */\r\n array: (\r\n name: string,\r\n label: string,\r\n itemFields: BaseField[],\r\n props: FieldProps = {},\r\n ): BaseField => ({\r\n type: \"array\",\r\n name,\r\n label,\r\n itemFields,\r\n ...props,\r\n }),\r\n\r\n /**\r\n * Custom field with a render function.\r\n * Bypasses the component registry entirely.\r\n *\r\n * @example\r\n * ```ts\r\n * field.custom(\"skills\", \"Skills\", ({ control, disabled }) => (\r\n * <SkillSelector control={control} disabled={disabled} />\r\n * ))\r\n * ```\r\n */\r\n custom: (\r\n name: string,\r\n label: string,\r\n render: CustomRenderFn,\r\n props: FieldProps = {},\r\n ): BaseField => ({\r\n type: \"custom\",\r\n name,\r\n label,\r\n render: render as BaseField[\"render\"],\r\n ...props,\r\n }),\r\n};\r\n\r\n// ============================================================================\r\n// Section Builder\r\n// ============================================================================\r\n\r\n/**\r\n * Create a section definition with sensible defaults.\r\n *\r\n * @param id - Unique section identifier\r\n * @param title - Section title\r\n * @param fields - Array of field definitions\r\n * @param props - Additional section configuration\r\n *\r\n * @example\r\n * ```ts\r\n * section(\"personal\", \"Personal Info\", [\r\n * field.text(\"name\", \"Name\", { required: true }),\r\n * field.email(\"email\", \"Email\"),\r\n * ], { cols: 2, variant: \"card\" })\r\n * ```\r\n */\r\nexport function section<TFieldValues extends FieldValues = FieldValues>(\r\n id: string,\r\n title: string,\r\n fields: BaseField<TFieldValues>[],\r\n props: SectionProps<TFieldValues> = {},\r\n): Section<TFieldValues> {\r\n const { cols = 2, ...rest } = props;\r\n return {\r\n id,\r\n title,\r\n fields,\r\n cols,\r\n ...rest,\r\n };\r\n}\r\n\r\n/**\r\n * Create a section without a title (transparent section).\r\n * Useful for grouping fields without visual separation.\r\n */\r\nexport function sectionUntitled<TFieldValues extends FieldValues = FieldValues>(\r\n fields: BaseField<TFieldValues>[],\r\n props: Omit<SectionProps<TFieldValues>, \"variant\"> = {},\r\n): Section<TFieldValues> {\r\n const { cols = 1, ...rest } = props;\r\n return {\r\n fields,\r\n cols,\r\n variant: \"transparent\",\r\n ...rest,\r\n };\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,SAAgB,GAAG,GAAG,QAA8B;AAClD,QAAO,QAAQ,KAAK,OAAO,CAAC;;;;;;;;;ACM9B,SAAS,eAAe,KAA8B,MAAuB;CAC3E,MAAM,QAAQ,KAAK,MAAM,IAAI;CAC7B,IAAI,UAAmB;AACvB,MAAK,MAAM,QAAQ,OAAO;AACxB,MAAI,WAAW,QAAQ,OAAO,YAAY,SAAU,QAAO;AAC3D,MAAI,MAAM,QAAQ,QAAQ,EAAE;GAC1B,MAAM,QAAQ,OAAO,KAAK;AAC1B,OAAI,OAAO,MAAM,MAAM,CAAE,QAAO;AAChC,aAAU,QAAQ;QAElB,WAAW,QAAoC;;AAGnD,QAAO;;;;;;;;AAST,SAAS,aACP,MACA,YACS;CACT,MAAM,YAAY,KAAK;CACvB,MAAM,MAAM;CAGZ,MAAM,QAAQ,aAAa,MACvB,IAAI,aACJ,eAAe,KAAK,UAAU;AAElC,SAAQ,KAAK,UAAb;EACE,KAAK,MACH,QAAO,UAAU,KAAK;EACxB,KAAK,MACH,QAAO,UAAU,KAAK;EACxB,KAAK,KACH,QAAO,MAAM,QAAQ,KAAK,MAAM,IAAI,KAAK,MAAM,SAAS,MAAM;EAChE,KAAK,SACH,QAAO,MAAM,QAAQ,KAAK,MAAM,IAAI,CAAC,KAAK,MAAM,SAAS,MAAM;EACjE,KAAK,SACH,QAAO,QAAQ,MAAM;EACvB,KAAK,QACH,QAAO,CAAC;EACV,QACE,QAAO;;;;;;AAOb,SAAS,kBACP,WAC4C;AAC5C,QACE,OAAO,cAAc,YACrB,CAAC,MAAM,QAAQ,UAAU,IACzB,WAAW;;;;;AAOf,SAAS,QACP,WAC+D;AAC/D,KAAI,kBAAkB,UAAU,CAC9B,QAAO;EAAE,OAAO,UAAU;EAAO,OAAO,UAAU,SAAS;EAAO;AAGpE,QAAO;EAAE,OADK,MAAM,QAAQ,UAAU,GAAG,YAAY,CAAC,UAAU;EAChD,OAAO;EAAO;;;;;;;;;;AAehC,SAAgB,kBAGd,WACA,YACS;AACT,KAAI,CAAC,UAAW,QAAO;AAEvB,KAAI,OAAO,cAAc,WACvB,QAAO,UAAU,WAAW;CAG9B,MAAM,EAAE,OAAO,UAAU,QAAQ,UAAU;CAC3C,MAAM,UAAU,SACd,aAAa,MAAM,WAAW;AAEhC,QAAO,UAAU,OAAO,MAAM,KAAK,OAAO,GAAG,MAAM,MAAM,OAAO;;;;;;AAOlE,SAAgB,kBAEd,WAA8C;AAC9C,KAAI,CAAC,aAAa,OAAO,cAAc,WAAY,QAAO,EAAE;CAE5D,MAAM,EAAE,UAAU,QAAQ,UAAU;AACpC,QAAO,MAAM,KAAK,MAAM,EAAE,MAAgB;;;;;AAM5C,SAAgB,aACd,QAC0B;AAC1B,QAAO;;;;;AAMT,SAAgB,YACd,OACyB;AACzB,QAAO;;;;;AAMT,SAAgB,cACd,SACuB;AACvB,QAAO;;;;;;;;;;;;AAaT,SAAgB,qBAEd,QAAyD;CACzD,MAAM,WAAoC,EAAE;AAE5C,MAAK,MAAM,WAAW,OAAO,UAAU;EACrC,MAAM,SAAS,QAAQ,YAAY,GAAG,QAAQ,UAAU,KAAK;AAC7D,MAAI,CAAC,QAAQ,OAAQ;AAErB,OAAK,MAAM,SAAS,QAAQ,QAAQ;AAClC,OAAI,MAAM,iBAAiB,OACzB,UAAS,GAAG,SAAS,MAAM,UAAoB,MAAM;AAGvD,OAAI,MAAM,cAAc,MAAM,SAAS,SACrC;SAAK,MAAM,OAAO,MAAM,WACtB,KAAI,IAAI,iBAAiB,OACvB,UACE,GAAG,SAAS,MAAM,KAAe,GAAG,IAAI,UACtC,IAAI;;;;AAOlB,QAAO;;;;;;;;;;;;;;;;;AAkBT,SAAgB,qBAEd,OAAiD;CACjD,MAAM,QAAyB,EAAE;AAEjC,KAAI,MAAM,SACR,OAAM,WAAW,GAAG,MAAM,SAAU,MAAM,KAAgB;AAE5D,KAAI,MAAM,cAAc,OACtB,OAAM,YAAY;EAChB,OAAO,MAAM;EACb,SAAS,YAAY,MAAM,UAAU;EACtC;AAEH,KAAI,MAAM,cAAc,OACtB,OAAM,YAAY;EAChB,OAAO,MAAM;EACb,SAAS,WAAW,MAAM,UAAU;EACrC;AAEH,KAAI,MAAM,QAAQ,OAChB,OAAM,MAAM;EACV,OAAO,MAAM;EACb,SAAS,oBAAoB,MAAM;EACpC;AAEH,KAAI,MAAM,QAAQ,OAChB,OAAM,MAAM;EACV,OAAO,MAAM;EACb,SAAS,mBAAmB,MAAM;EACnC;AAEH,KAAI,MAAM,QACR,OAAM,UAAU;EACd,OAAO,IAAI,OAAO,MAAM,QAAQ;EAChC,SAAS;EACV;AAEH,KAAI,MAAM,SACR,OAAM,WAAW,MAAM;AAGzB,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACxMT,MAAa,QAAQ;CAInB,OACE,MACA,OACA,QAAoB,EAAE,MACP;EACf,MAAM;EACN;EACA;EACA,GAAG;EACJ;CAKD,QACE,MACA,OACA,QAAoB,EAAE,MACP;EACf,MAAM;EACN;EACA;EACA,aAAa;EACb,GAAG;EACJ;CAKD,MACE,MACA,OACA,QAAoB,EAAE,MACP;EACf,MAAM;EACN;EACA;EACA,aAAa;EACb,GAAG;EACJ;CAKD,MACE,MACA,OACA,QAAoB,EAAE,MACP;EACf,MAAM;EACN;EACA;EACA,aAAa;EACb,GAAG;EACJ;CAKD,WACE,MACA,OACA,QAAoB,EAAE,MACP;EACf,MAAM;EACN;EACA;EACA,GAAG;EACJ;CAKD,SACE,MACA,OACA,QAAoB,EAAE,MACP;EACf,MAAM;EACN;EACA;EACA,KAAK;EACL,GAAG;EACJ;CAKD,WACE,MACA,OACA,QAAoB,EAAE,MACP;EACf,MAAM;EACN;EACA;EACA,MAAM;EACN,GAAG;EACJ;CAKD,SACE,MACA,OACA,SACA,QAAoB,EAAE,MACP;EACf,MAAM;EACN;EACA;EACA;EACA,GAAG;EACJ;CAKD,WACE,MACA,OACA,SACA,QAAoB,EAAE,MACP;EACf,MAAM;EACN;EACA;EACA;EACA,GAAG;EACJ;CAKD,cACE,MACA,OACA,SACA,QAAoB,EAAE,MACP;EACf,MAAM;EACN;EACA;EACA;EACA,aAAa;EACb,GAAG;EACJ;CAKD,kBACE,MACA,OACA,QAAoB,EAAE,MACP;EACf,MAAM;EACN;EACA;EACA,GAAG;EACJ;CAKD,SACE,MACA,OACA,QAAoB,EAAE,MACP;EACf,MAAM;EACN;EACA;EACA,GAAG;EACJ;CAKD,UACE,MACA,OACA,QAAoB,EAAE,MACP;EACf,MAAM;EACN;EACA;EACA,GAAG;EACJ;CAKD,WACE,MACA,OACA,QAAoB,EAAE,MACP;EACf,MAAM;EACN;EACA;EACA,GAAG;EACJ;CAKD,QACE,MACA,OACA,SACA,QAAoB,EAAE,MACP;EACf,MAAM;EACN;EACA;EACA;EACA,GAAG;EACJ;CAKD,OACE,MACA,OACA,QAAoB,EAAE,MACP;EACf,MAAM;EACN;EACA;EACA,GAAG;EACJ;CAKD,OACE,MACA,OACA,QAAoB,EAAE,MACP;EACf,MAAM;EACN;EACA;EACA,aAAa;EACb,GAAG;EACJ;CAKD,OACE,MACA,OACA,QAAoB,EAAE,MACP;EACf,MAAM;EACN;EACA;EACA,aAAa;EACb,GAAG;EACJ;CAKD,OACE,MACA,OACA,QAAoB,EAAE,MACP;EACf,MAAM;EACN;EACA;EACA,GAAG;EACJ;CAKD,SACE,MACA,QAAoB,EAAE,MACP;EACf,MAAM;EACN;EACA,GAAG;EACJ;CAeD,QACE,MACA,OACA,YACA,QAAoB,EAAE,MACP;EACf,MAAM;EACN;EACA;EACA;EACA,GAAG;EACJ;CAcD,QACE,MACA,OACA,YACA,QAAoB,EAAE,MACP;EACf,MAAM;EACN;EACA;EACA;EACA,GAAG;EACJ;CAaD,SACE,MACA,OACA,QACA,QAAoB,EAAE,MACP;EACf,MAAM;EACN;EACA;EACQ;EACR,GAAG;EACJ;CACF;;;;;;;;;;;;;;;;;AAsBD,SAAgB,QACd,IACA,OACA,QACA,QAAoC,EAAE,EACf;CACvB,MAAM,EAAE,OAAO,GAAG,GAAG,SAAS;AAC9B,QAAO;EACL;EACA;EACA;EACA;EACA,GAAG;EACJ;;;;;;AAOH,SAAgB,gBACd,QACA,QAAqD,EAAE,EAChC;CACvB,MAAM,EAAE,OAAO,GAAG,GAAG,SAAS;AAC9B,QAAO;EACL;EACA;EACA,SAAS;EACT,GAAG;EACJ"}
|
package/package.json
CHANGED
|
@@ -1,31 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@classytic/formkit",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Headless, type-safe form generation engine for React. Schema-driven with full TypeScript support.",
|
|
3
|
+
"version": "1.2.2",
|
|
4
|
+
"description": "Headless, type-safe form generation engine for React 19. Schema-driven with full TypeScript support.",
|
|
5
5
|
"author": "Classytic",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"sideEffects": false,
|
|
9
|
-
"main": "./dist/index.
|
|
10
|
-
"module": "./dist/index.
|
|
11
|
-
"types": "./dist/index.d.
|
|
9
|
+
"main": "./dist/index.mjs",
|
|
10
|
+
"module": "./dist/index.mjs",
|
|
11
|
+
"types": "./dist/index.d.mts",
|
|
12
12
|
"exports": {
|
|
13
13
|
".": {
|
|
14
|
-
"types": "./dist/index.d.
|
|
15
|
-
"import": "./dist/index.
|
|
16
|
-
"
|
|
17
|
-
|
|
14
|
+
"types": "./dist/index.d.mts",
|
|
15
|
+
"import": "./dist/index.mjs",
|
|
16
|
+
"default": "./dist/index.mjs"
|
|
17
|
+
},
|
|
18
|
+
"./server": {
|
|
19
|
+
"types": "./dist/server.d.mts",
|
|
20
|
+
"import": "./dist/server.mjs",
|
|
21
|
+
"default": "./dist/server.mjs"
|
|
18
22
|
},
|
|
19
23
|
"./package.json": "./package.json"
|
|
20
24
|
},
|
|
21
|
-
"typesVersions": {
|
|
22
|
-
"*": {
|
|
23
|
-
"*": [
|
|
24
|
-
"./dist/*",
|
|
25
|
-
"./dist/index.d.ts"
|
|
26
|
-
]
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
25
|
"files": [
|
|
30
26
|
"dist",
|
|
31
27
|
"README.md",
|
|
@@ -33,15 +29,14 @@
|
|
|
33
29
|
"CHANGELOG.md"
|
|
34
30
|
],
|
|
35
31
|
"scripts": {
|
|
36
|
-
"build": "
|
|
37
|
-
"dev": "
|
|
32
|
+
"build": "tsdown",
|
|
33
|
+
"dev": "tsdown --watch",
|
|
38
34
|
"typecheck": "tsc --noEmit",
|
|
39
35
|
"lint": "eslint src --ext .ts,.tsx",
|
|
40
36
|
"test": "vitest run",
|
|
41
37
|
"test:watch": "vitest",
|
|
42
38
|
"test:coverage": "vitest run --coverage",
|
|
43
|
-
"
|
|
44
|
-
"prepublishOnly": "npm run clean && npm run typecheck && npm run build && npm run test",
|
|
39
|
+
"prepublishOnly": "npm run typecheck && npm run build && npm run test",
|
|
45
40
|
"version:patch": "npm version patch",
|
|
46
41
|
"version:minor": "npm version minor",
|
|
47
42
|
"version:major": "npm version major",
|
|
@@ -53,7 +48,6 @@
|
|
|
53
48
|
"keywords": [
|
|
54
49
|
"react",
|
|
55
50
|
"react-19",
|
|
56
|
-
"react-18",
|
|
57
51
|
"forms",
|
|
58
52
|
"form-generator",
|
|
59
53
|
"form-builder",
|
|
@@ -78,9 +72,9 @@
|
|
|
78
72
|
},
|
|
79
73
|
"homepage": "https://github.com/classytic/formkit#readme",
|
|
80
74
|
"peerDependencies": {
|
|
81
|
-
"react": "^
|
|
82
|
-
"react-dom": "^
|
|
83
|
-
"react-hook-form": "^7.
|
|
75
|
+
"react": "^19.0.0",
|
|
76
|
+
"react-dom": "^19.0.0",
|
|
77
|
+
"react-hook-form": "^7.55.0"
|
|
84
78
|
},
|
|
85
79
|
"peerDependenciesMeta": {
|
|
86
80
|
"react-dom": {
|
|
@@ -95,8 +89,8 @@
|
|
|
95
89
|
"@testing-library/jest-dom": "^6.9.1",
|
|
96
90
|
"@testing-library/react": "^16.0.0",
|
|
97
91
|
"@types/node": "^22.8.7",
|
|
98
|
-
"@types/react": "^
|
|
99
|
-
"@types/react-dom": "^
|
|
92
|
+
"@types/react": "^19.0.0",
|
|
93
|
+
"@types/react-dom": "^19.0.0",
|
|
100
94
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
101
95
|
"@typescript-eslint/parser": "^8.0.0",
|
|
102
96
|
"@vitejs/plugin-react": "^4.3.0",
|
|
@@ -104,9 +98,8 @@
|
|
|
104
98
|
"happy-dom": "^15.0.0",
|
|
105
99
|
"react": "^19.0.0",
|
|
106
100
|
"react-dom": "^19.0.0",
|
|
107
|
-
"react-hook-form": "^7.
|
|
108
|
-
"
|
|
109
|
-
"tsup": "^8.3.5",
|
|
101
|
+
"react-hook-form": "^7.55.0",
|
|
102
|
+
"tsdown": "^0.20.3",
|
|
110
103
|
"typescript": "^5.6.3",
|
|
111
104
|
"vitest": "^2.0.0"
|
|
112
105
|
},
|
|
@@ -117,4 +110,4 @@
|
|
|
117
110
|
"access": "public",
|
|
118
111
|
"registry": "https://registry.npmjs.org/"
|
|
119
112
|
}
|
|
120
|
-
}
|
|
113
|
+
}
|