pdyform 2.1.0 → 2.2.1
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 +98 -74
- package/package.json +1 -1
- package/packages/core/dist/chunk-6F4PWJZI.js +0 -0
- package/packages/core/dist/chunk-B7OMM2UC.js +160 -0
- package/packages/core/dist/chunk-J6ESJZ4U.js +82 -0
- package/packages/core/dist/formState.cjs +140 -78
- package/packages/core/dist/formState.d.cts +14 -10
- package/packages/core/dist/formState.d.ts +14 -10
- package/packages/core/dist/formState.js +4 -12
- package/packages/core/dist/index.cjs +146 -78
- package/packages/core/dist/index.d.cts +4 -3
- package/packages/core/dist/index.d.ts +4 -3
- package/packages/core/dist/index.js +11 -12
- package/packages/core/dist/types.d.cts +17 -5
- package/packages/core/dist/types.d.ts +17 -5
- package/packages/core/dist/types.js +1 -0
- package/packages/core/dist/utils.cjs +80 -19
- package/packages/core/dist/utils.d.cts +17 -5
- package/packages/core/dist/utils.d.ts +17 -5
- package/packages/core/dist/utils.js +7 -1
- package/packages/react/dist/index.cjs +1 -461
- package/packages/react/dist/index.d.cts +33 -2
- package/packages/react/dist/index.d.ts +33 -2
- package/packages/react/dist/index.js +1 -411
- package/packages/vue/dist/index.d.ts +55 -9
- package/packages/vue/dist/index.js +1 -1
- package/packages/vue/dist/index.mjs +600 -247
- package/packages/core/dist/chunk-TP3IHKWV.js +0 -69
- package/packages/core/dist/chunk-WEDHXOHH.js +0 -102
|
@@ -20,13 +20,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
createFormStore: () => createFormStore,
|
|
24
|
+
defaultErrorMessages: () => defaultErrorMessages,
|
|
25
|
+
get: () => get,
|
|
26
26
|
getDefaultValues: () => getDefaultValues,
|
|
27
27
|
normalizeFieldValue: () => normalizeFieldValue,
|
|
28
|
-
|
|
29
|
-
setSubmitting: () => setSubmitting,
|
|
28
|
+
set: () => set,
|
|
30
29
|
validateField: () => validateField,
|
|
31
30
|
validateFieldByName: () => validateFieldByName,
|
|
32
31
|
validateForm: () => validateForm
|
|
@@ -40,85 +39,140 @@ function parseNumberish(value) {
|
|
|
40
39
|
const parsed = Number(value);
|
|
41
40
|
return Number.isNaN(parsed) ? null : parsed;
|
|
42
41
|
}
|
|
42
|
+
var defaultErrorMessages = {
|
|
43
|
+
required: "{label} is required",
|
|
44
|
+
min: "{label} must be at least {value}",
|
|
45
|
+
max: "{label} must be at most {value}",
|
|
46
|
+
email: "Invalid email address",
|
|
47
|
+
pattern: "Invalid format",
|
|
48
|
+
custom: "Invalid value"
|
|
49
|
+
};
|
|
50
|
+
function formatMessage(template, field, rule) {
|
|
51
|
+
return template.replace("{label}", field.label).replace("{value}", String(rule.value || ""));
|
|
52
|
+
}
|
|
53
|
+
function get(obj, path, defaultValue) {
|
|
54
|
+
if (!path) return defaultValue;
|
|
55
|
+
const keys = path.split(/[.[\]]/).filter(Boolean);
|
|
56
|
+
let result = obj;
|
|
57
|
+
for (const key of keys) {
|
|
58
|
+
if (result === null || result === void 0) return defaultValue;
|
|
59
|
+
result = result[key];
|
|
60
|
+
}
|
|
61
|
+
return result === void 0 ? defaultValue : result;
|
|
62
|
+
}
|
|
63
|
+
function set(obj, path, value) {
|
|
64
|
+
if (Object(obj) !== obj) return obj;
|
|
65
|
+
const keys = path.split(/[.[\]]/).filter(Boolean);
|
|
66
|
+
const newObj = { ...obj };
|
|
67
|
+
let current = newObj;
|
|
68
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
69
|
+
const key = keys[i];
|
|
70
|
+
const nextKey = keys[i + 1];
|
|
71
|
+
const isNextKeyIndex = /^\d+$/.test(nextKey);
|
|
72
|
+
if (!(key in current) || current[key] === null || typeof current[key] !== "object") {
|
|
73
|
+
current[key] = isNextKeyIndex ? [] : {};
|
|
74
|
+
} else {
|
|
75
|
+
current[key] = Array.isArray(current[key]) ? [...current[key]] : { ...current[key] };
|
|
76
|
+
}
|
|
77
|
+
current = current[key];
|
|
78
|
+
}
|
|
79
|
+
current[keys[keys.length - 1]] = value;
|
|
80
|
+
return newObj;
|
|
81
|
+
}
|
|
43
82
|
function normalizeFieldValue(field, value) {
|
|
44
83
|
if (field.type !== "number") return value;
|
|
45
84
|
if (value === "" || value === void 0 || value === null) return "";
|
|
46
85
|
const numericValue = parseNumberish(value);
|
|
47
86
|
return numericValue === null ? value : numericValue;
|
|
48
87
|
}
|
|
49
|
-
function validateField(value, field) {
|
|
88
|
+
async function validateField(value, field, customMessages) {
|
|
50
89
|
if (!field.validations) return null;
|
|
90
|
+
const messages = { ...defaultErrorMessages, ...customMessages };
|
|
51
91
|
for (const rule of field.validations) {
|
|
52
92
|
switch (rule.type) {
|
|
53
93
|
case "required":
|
|
54
94
|
if (value === void 0 || value === null || value === "" || Array.isArray(value) && value.length === 0) {
|
|
55
|
-
return rule.message ||
|
|
95
|
+
return rule.message || formatMessage(messages.required, field, rule);
|
|
56
96
|
}
|
|
57
97
|
break;
|
|
58
98
|
case "min":
|
|
59
99
|
if (field.type === "number") {
|
|
60
100
|
const numericValue = parseNumberish(value);
|
|
61
101
|
if (numericValue !== null && numericValue < rule.value) {
|
|
62
|
-
|
|
102
|
+
const template = field.type === "number" ? messages.min : typeof value === "string" ? "{label} must be at least {value} characters" : messages.min;
|
|
103
|
+
return rule.message || formatMessage(template, field, rule);
|
|
63
104
|
}
|
|
64
105
|
break;
|
|
65
106
|
}
|
|
66
107
|
if (typeof value === "number" && value < rule.value) {
|
|
67
|
-
return rule.message ||
|
|
108
|
+
return rule.message || formatMessage(messages.min, field, rule);
|
|
68
109
|
}
|
|
69
110
|
if (typeof value === "string" && value.length < rule.value) {
|
|
70
|
-
|
|
111
|
+
const template = "{label} must be at least {value} characters";
|
|
112
|
+
return rule.message || formatMessage(template, field, rule);
|
|
71
113
|
}
|
|
72
114
|
break;
|
|
73
115
|
case "max":
|
|
74
116
|
if (field.type === "number") {
|
|
75
117
|
const numericValue = parseNumberish(value);
|
|
76
118
|
if (numericValue !== null && numericValue > rule.value) {
|
|
77
|
-
return rule.message ||
|
|
119
|
+
return rule.message || formatMessage(messages.max, field, rule);
|
|
78
120
|
}
|
|
79
121
|
break;
|
|
80
122
|
}
|
|
81
123
|
if (typeof value === "number" && value > rule.value) {
|
|
82
|
-
return rule.message ||
|
|
124
|
+
return rule.message || formatMessage(messages.max, field, rule);
|
|
83
125
|
}
|
|
84
126
|
if (typeof value === "string" && value.length > rule.value) {
|
|
85
|
-
|
|
127
|
+
const template = "{label} must be at most {value} characters";
|
|
128
|
+
return rule.message || formatMessage(template, field, rule);
|
|
86
129
|
}
|
|
87
130
|
break;
|
|
88
131
|
case "email": {
|
|
89
132
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
90
133
|
if (value && !emailRegex.test(value)) {
|
|
91
|
-
return rule.message ||
|
|
134
|
+
return rule.message || formatMessage(messages.email, field, rule);
|
|
92
135
|
}
|
|
93
136
|
break;
|
|
94
137
|
}
|
|
95
138
|
case "pattern":
|
|
96
139
|
if (value && rule.value && !new RegExp(rule.value).test(value)) {
|
|
97
|
-
return rule.message ||
|
|
140
|
+
return rule.message || formatMessage(messages.pattern, field, rule);
|
|
98
141
|
}
|
|
99
142
|
break;
|
|
100
143
|
case "custom":
|
|
101
144
|
if (rule.validator) {
|
|
102
|
-
const result = rule.validator(value);
|
|
145
|
+
const result = await rule.validator(value);
|
|
103
146
|
if (typeof result === "string") return result;
|
|
104
|
-
if (
|
|
147
|
+
if (result === false) return rule.message || formatMessage(messages.custom, field, rule);
|
|
105
148
|
}
|
|
106
149
|
break;
|
|
107
150
|
}
|
|
108
151
|
}
|
|
109
152
|
return null;
|
|
110
153
|
}
|
|
111
|
-
function validateFieldByName(fields, name, value) {
|
|
154
|
+
async function validateFieldByName(fields, name, value, resolver, allValues, customMessages) {
|
|
155
|
+
if (resolver && allValues) {
|
|
156
|
+
const resolverErrors = await resolver(allValues);
|
|
157
|
+
if (resolverErrors[name]) return resolverErrors[name];
|
|
158
|
+
}
|
|
112
159
|
const field = fields.find((f) => f.name === name);
|
|
113
160
|
if (!field) return null;
|
|
114
|
-
return validateField(value, field);
|
|
161
|
+
return await validateField(value, field, customMessages);
|
|
115
162
|
}
|
|
116
|
-
function validateForm(fields, values) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (error) errors[field.name] = error;
|
|
163
|
+
async function validateForm(fields, values, resolver, customMessages) {
|
|
164
|
+
let errors = {};
|
|
165
|
+
if (resolver) {
|
|
166
|
+
errors = await resolver(values);
|
|
121
167
|
}
|
|
168
|
+
const validationPromises = fields.map(async (field) => {
|
|
169
|
+
if (errors[field.name]) return;
|
|
170
|
+
const isHidden = typeof field.hidden === "function" ? field.hidden(values) : field.hidden;
|
|
171
|
+
if (isHidden) return;
|
|
172
|
+
const error = await validateField(get(values, field.name), field, customMessages);
|
|
173
|
+
if (error) errors[field.name] = error;
|
|
174
|
+
});
|
|
175
|
+
await Promise.all(validationPromises);
|
|
122
176
|
return errors;
|
|
123
177
|
}
|
|
124
178
|
function getDefaultValues(fields) {
|
|
@@ -129,68 +183,82 @@ function getDefaultValues(fields) {
|
|
|
129
183
|
}
|
|
130
184
|
|
|
131
185
|
// src/formState.ts
|
|
132
|
-
|
|
133
|
-
|
|
186
|
+
var import_vanilla = require("zustand/vanilla");
|
|
187
|
+
function createFormStore(fields, resolver, errorMessages) {
|
|
188
|
+
return (0, import_vanilla.createStore)()((set2, getStore) => ({
|
|
134
189
|
values: getDefaultValues(fields),
|
|
135
190
|
errors: {},
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
}
|
|
163
|
-
function applyFieldBlur(fields, state, name) {
|
|
164
|
-
const error = validateFieldByName(fields, name, state.values[name]);
|
|
165
|
-
return {
|
|
166
|
-
...state,
|
|
167
|
-
errors: {
|
|
168
|
-
...state.errors,
|
|
169
|
-
[name]: error || ""
|
|
170
|
-
}
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
function runSubmitValidation(fields, state) {
|
|
174
|
-
const errors = validateForm(fields, state.values);
|
|
175
|
-
const hasError = Object.keys(errors).length > 0;
|
|
176
|
-
return {
|
|
177
|
-
state: {
|
|
178
|
-
...state,
|
|
179
|
-
errors,
|
|
180
|
-
isSubmitting: false
|
|
191
|
+
validatingFields: [],
|
|
192
|
+
isSubmitting: false,
|
|
193
|
+
setFieldValue: async (name, rawValue) => {
|
|
194
|
+
const field = fields.find((f) => f.name === name);
|
|
195
|
+
const normalizedValue = field ? normalizeFieldValue(field, rawValue) : rawValue;
|
|
196
|
+
set2((state) => ({
|
|
197
|
+
values: set(state.values, name, normalizedValue)
|
|
198
|
+
}));
|
|
199
|
+
const hasExistingError = !!getStore().errors[name];
|
|
200
|
+
const shouldValidateImmediately = field && ["select", "checkbox", "radio", "switch", "date"].includes(field.type);
|
|
201
|
+
if (shouldValidateImmediately || hasExistingError) {
|
|
202
|
+
set2((state) => ({
|
|
203
|
+
validatingFields: [...state.validatingFields, name]
|
|
204
|
+
}));
|
|
205
|
+
try {
|
|
206
|
+
const currentValues = getStore().values;
|
|
207
|
+
const error = await validateFieldByName(fields, name, normalizedValue, resolver, currentValues, errorMessages);
|
|
208
|
+
set2((state) => ({
|
|
209
|
+
errors: { ...state.errors, [name]: error || "" },
|
|
210
|
+
validatingFields: state.validatingFields.filter((f) => f !== name)
|
|
211
|
+
}));
|
|
212
|
+
} catch (err) {
|
|
213
|
+
set2((state) => ({
|
|
214
|
+
validatingFields: state.validatingFields.filter((f) => f !== name)
|
|
215
|
+
}));
|
|
216
|
+
}
|
|
217
|
+
}
|
|
181
218
|
},
|
|
182
|
-
|
|
183
|
-
|
|
219
|
+
setFieldBlur: async (name) => {
|
|
220
|
+
set2((state) => ({
|
|
221
|
+
validatingFields: [...state.validatingFields, name]
|
|
222
|
+
}));
|
|
223
|
+
try {
|
|
224
|
+
const currentValues = getStore().values;
|
|
225
|
+
const value = get(currentValues, name);
|
|
226
|
+
const error = await validateFieldByName(fields, name, value, resolver, currentValues, errorMessages);
|
|
227
|
+
set2((state) => ({
|
|
228
|
+
errors: { ...state.errors, [name]: error || "" },
|
|
229
|
+
validatingFields: state.validatingFields.filter((f) => f !== name)
|
|
230
|
+
}));
|
|
231
|
+
} catch (err) {
|
|
232
|
+
set2((state) => ({
|
|
233
|
+
validatingFields: state.validatingFields.filter((f) => f !== name)
|
|
234
|
+
}));
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
setSubmitting: (isSubmitting) => set2({ isSubmitting }),
|
|
238
|
+
runSubmitValidation: async () => {
|
|
239
|
+
set2({ isSubmitting: true });
|
|
240
|
+
const state = getStore();
|
|
241
|
+
const errors = await validateForm(fields, state.values, resolver, errorMessages);
|
|
242
|
+
const hasError = Object.keys(errors).length > 0;
|
|
243
|
+
set2({
|
|
244
|
+
errors,
|
|
245
|
+
isSubmitting: false
|
|
246
|
+
});
|
|
247
|
+
return {
|
|
248
|
+
state: getStore(),
|
|
249
|
+
hasError
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
}));
|
|
184
253
|
}
|
|
185
254
|
// Annotate the CommonJS export names for ESM import in node:
|
|
186
255
|
0 && (module.exports = {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
256
|
+
createFormStore,
|
|
257
|
+
defaultErrorMessages,
|
|
258
|
+
get,
|
|
190
259
|
getDefaultValues,
|
|
191
260
|
normalizeFieldValue,
|
|
192
|
-
|
|
193
|
-
setSubmitting,
|
|
261
|
+
set,
|
|
194
262
|
validateField,
|
|
195
263
|
validateFieldByName,
|
|
196
264
|
validateForm
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
export { FieldType, FormField, FormSchema, FormState, Option, ValidationRule } from './types.cjs';
|
|
2
|
-
export { getDefaultValues, normalizeFieldValue, validateField, validateFieldByName, validateForm } from './utils.cjs';
|
|
3
|
-
export { FormRuntimeState,
|
|
1
|
+
export { ErrorMessageTemplates, FieldType, FormField, FormResolver, FormSchema, FormState, Option, ValidationRule } from './types.cjs';
|
|
2
|
+
export { defaultErrorMessages, get, getDefaultValues, normalizeFieldValue, set, validateField, validateFieldByName, validateForm } from './utils.cjs';
|
|
3
|
+
export { FormRuntimeState, FormStore, createFormStore } from './formState.cjs';
|
|
4
|
+
import 'zustand/vanilla';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
export { FieldType, FormField, FormSchema, FormState, Option, ValidationRule } from './types.js';
|
|
2
|
-
export { getDefaultValues, normalizeFieldValue, validateField, validateFieldByName, validateForm } from './utils.js';
|
|
3
|
-
export { FormRuntimeState,
|
|
1
|
+
export { ErrorMessageTemplates, FieldType, FormField, FormResolver, FormSchema, FormState, Option, ValidationRule } from './types.js';
|
|
2
|
+
export { defaultErrorMessages, get, getDefaultValues, normalizeFieldValue, set, validateField, validateFieldByName, validateForm } from './utils.js';
|
|
3
|
+
export { FormRuntimeState, FormStore, createFormStore } from './formState.js';
|
|
4
|
+
import 'zustand/vanilla';
|
|
@@ -1,25 +1,24 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
runSubmitValidation,
|
|
6
|
-
setSubmitting
|
|
7
|
-
} from "./chunk-TP3IHKWV.js";
|
|
2
|
+
createFormStore
|
|
3
|
+
} from "./chunk-J6ESJZ4U.js";
|
|
4
|
+
import "./chunk-6F4PWJZI.js";
|
|
8
5
|
import {
|
|
6
|
+
defaultErrorMessages,
|
|
7
|
+
get,
|
|
9
8
|
getDefaultValues,
|
|
10
9
|
normalizeFieldValue,
|
|
10
|
+
set,
|
|
11
11
|
validateField,
|
|
12
12
|
validateFieldByName,
|
|
13
13
|
validateForm
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-B7OMM2UC.js";
|
|
15
15
|
export {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
createFormStore,
|
|
17
|
+
defaultErrorMessages,
|
|
18
|
+
get,
|
|
19
19
|
getDefaultValues,
|
|
20
20
|
normalizeFieldValue,
|
|
21
|
-
|
|
22
|
-
setSubmitting,
|
|
21
|
+
set,
|
|
23
22
|
validateField,
|
|
24
23
|
validateFieldByName,
|
|
25
24
|
validateForm
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
type FieldType = 'text' | 'number' | 'email' | 'password' | 'select' | 'checkbox' | 'radio' | 'textarea' | 'date';
|
|
1
|
+
type FieldType = 'text' | 'number' | 'email' | 'password' | 'select' | 'checkbox' | 'radio' | 'textarea' | 'date' | 'switch';
|
|
2
2
|
interface ValidationRule {
|
|
3
3
|
type: 'required' | 'min' | 'max' | 'pattern' | 'email' | 'custom';
|
|
4
4
|
value?: any;
|
|
5
5
|
message?: string;
|
|
6
|
-
validator?: (value: any) => boolean | string
|
|
6
|
+
validator?: (value: any) => boolean | string | Promise<boolean | string>;
|
|
7
7
|
}
|
|
8
8
|
interface Option {
|
|
9
9
|
label: string;
|
|
@@ -19,21 +19,33 @@ interface FormField {
|
|
|
19
19
|
defaultValue?: any;
|
|
20
20
|
options?: Option[];
|
|
21
21
|
validations?: ValidationRule[];
|
|
22
|
-
hidden?: boolean;
|
|
23
|
-
disabled?: boolean;
|
|
22
|
+
hidden?: boolean | ((values: any) => boolean);
|
|
23
|
+
disabled?: boolean | ((values: any) => boolean);
|
|
24
24
|
className?: string;
|
|
25
25
|
}
|
|
26
|
+
type FormResolver = (values: any) => Record<string, string> | Promise<Record<string, string>>;
|
|
27
|
+
type ErrorMessageTemplates = {
|
|
28
|
+
required?: string;
|
|
29
|
+
min?: string;
|
|
30
|
+
max?: string;
|
|
31
|
+
email?: string;
|
|
32
|
+
pattern?: string;
|
|
33
|
+
custom?: string;
|
|
34
|
+
};
|
|
26
35
|
interface FormSchema {
|
|
27
36
|
title?: string;
|
|
28
37
|
description?: string;
|
|
29
38
|
fields: FormField[];
|
|
30
39
|
submitButtonText?: string;
|
|
40
|
+
resolver?: FormResolver;
|
|
41
|
+
errorMessages?: ErrorMessageTemplates;
|
|
31
42
|
}
|
|
32
43
|
interface FormState {
|
|
33
44
|
values: Record<string, any>;
|
|
34
45
|
errors: Record<string, string>;
|
|
46
|
+
validatingFields: string[];
|
|
35
47
|
isSubmitting: boolean;
|
|
36
48
|
isValid: boolean;
|
|
37
49
|
}
|
|
38
50
|
|
|
39
|
-
export type { FieldType, FormField, FormSchema, FormState, Option, ValidationRule };
|
|
51
|
+
export type { ErrorMessageTemplates, FieldType, FormField, FormResolver, FormSchema, FormState, Option, ValidationRule };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
type FieldType = 'text' | 'number' | 'email' | 'password' | 'select' | 'checkbox' | 'radio' | 'textarea' | 'date';
|
|
1
|
+
type FieldType = 'text' | 'number' | 'email' | 'password' | 'select' | 'checkbox' | 'radio' | 'textarea' | 'date' | 'switch';
|
|
2
2
|
interface ValidationRule {
|
|
3
3
|
type: 'required' | 'min' | 'max' | 'pattern' | 'email' | 'custom';
|
|
4
4
|
value?: any;
|
|
5
5
|
message?: string;
|
|
6
|
-
validator?: (value: any) => boolean | string
|
|
6
|
+
validator?: (value: any) => boolean | string | Promise<boolean | string>;
|
|
7
7
|
}
|
|
8
8
|
interface Option {
|
|
9
9
|
label: string;
|
|
@@ -19,21 +19,33 @@ interface FormField {
|
|
|
19
19
|
defaultValue?: any;
|
|
20
20
|
options?: Option[];
|
|
21
21
|
validations?: ValidationRule[];
|
|
22
|
-
hidden?: boolean;
|
|
23
|
-
disabled?: boolean;
|
|
22
|
+
hidden?: boolean | ((values: any) => boolean);
|
|
23
|
+
disabled?: boolean | ((values: any) => boolean);
|
|
24
24
|
className?: string;
|
|
25
25
|
}
|
|
26
|
+
type FormResolver = (values: any) => Record<string, string> | Promise<Record<string, string>>;
|
|
27
|
+
type ErrorMessageTemplates = {
|
|
28
|
+
required?: string;
|
|
29
|
+
min?: string;
|
|
30
|
+
max?: string;
|
|
31
|
+
email?: string;
|
|
32
|
+
pattern?: string;
|
|
33
|
+
custom?: string;
|
|
34
|
+
};
|
|
26
35
|
interface FormSchema {
|
|
27
36
|
title?: string;
|
|
28
37
|
description?: string;
|
|
29
38
|
fields: FormField[];
|
|
30
39
|
submitButtonText?: string;
|
|
40
|
+
resolver?: FormResolver;
|
|
41
|
+
errorMessages?: ErrorMessageTemplates;
|
|
31
42
|
}
|
|
32
43
|
interface FormState {
|
|
33
44
|
values: Record<string, any>;
|
|
34
45
|
errors: Record<string, string>;
|
|
46
|
+
validatingFields: string[];
|
|
35
47
|
isSubmitting: boolean;
|
|
36
48
|
isValid: boolean;
|
|
37
49
|
}
|
|
38
50
|
|
|
39
|
-
export type { FieldType, FormField, FormSchema, FormState, Option, ValidationRule };
|
|
51
|
+
export type { ErrorMessageTemplates, FieldType, FormField, FormResolver, FormSchema, FormState, Option, ValidationRule };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "./chunk-6F4PWJZI.js";
|
|
@@ -20,8 +20,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/utils.ts
|
|
21
21
|
var utils_exports = {};
|
|
22
22
|
__export(utils_exports, {
|
|
23
|
+
defaultErrorMessages: () => defaultErrorMessages,
|
|
24
|
+
get: () => get,
|
|
23
25
|
getDefaultValues: () => getDefaultValues,
|
|
24
26
|
normalizeFieldValue: () => normalizeFieldValue,
|
|
27
|
+
set: () => set,
|
|
25
28
|
validateField: () => validateField,
|
|
26
29
|
validateFieldByName: () => validateFieldByName,
|
|
27
30
|
validateForm: () => validateForm
|
|
@@ -33,85 +36,140 @@ function parseNumberish(value) {
|
|
|
33
36
|
const parsed = Number(value);
|
|
34
37
|
return Number.isNaN(parsed) ? null : parsed;
|
|
35
38
|
}
|
|
39
|
+
var defaultErrorMessages = {
|
|
40
|
+
required: "{label} is required",
|
|
41
|
+
min: "{label} must be at least {value}",
|
|
42
|
+
max: "{label} must be at most {value}",
|
|
43
|
+
email: "Invalid email address",
|
|
44
|
+
pattern: "Invalid format",
|
|
45
|
+
custom: "Invalid value"
|
|
46
|
+
};
|
|
47
|
+
function formatMessage(template, field, rule) {
|
|
48
|
+
return template.replace("{label}", field.label).replace("{value}", String(rule.value || ""));
|
|
49
|
+
}
|
|
50
|
+
function get(obj, path, defaultValue) {
|
|
51
|
+
if (!path) return defaultValue;
|
|
52
|
+
const keys = path.split(/[.[\]]/).filter(Boolean);
|
|
53
|
+
let result = obj;
|
|
54
|
+
for (const key of keys) {
|
|
55
|
+
if (result === null || result === void 0) return defaultValue;
|
|
56
|
+
result = result[key];
|
|
57
|
+
}
|
|
58
|
+
return result === void 0 ? defaultValue : result;
|
|
59
|
+
}
|
|
60
|
+
function set(obj, path, value) {
|
|
61
|
+
if (Object(obj) !== obj) return obj;
|
|
62
|
+
const keys = path.split(/[.[\]]/).filter(Boolean);
|
|
63
|
+
const newObj = { ...obj };
|
|
64
|
+
let current = newObj;
|
|
65
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
66
|
+
const key = keys[i];
|
|
67
|
+
const nextKey = keys[i + 1];
|
|
68
|
+
const isNextKeyIndex = /^\d+$/.test(nextKey);
|
|
69
|
+
if (!(key in current) || current[key] === null || typeof current[key] !== "object") {
|
|
70
|
+
current[key] = isNextKeyIndex ? [] : {};
|
|
71
|
+
} else {
|
|
72
|
+
current[key] = Array.isArray(current[key]) ? [...current[key]] : { ...current[key] };
|
|
73
|
+
}
|
|
74
|
+
current = current[key];
|
|
75
|
+
}
|
|
76
|
+
current[keys[keys.length - 1]] = value;
|
|
77
|
+
return newObj;
|
|
78
|
+
}
|
|
36
79
|
function normalizeFieldValue(field, value) {
|
|
37
80
|
if (field.type !== "number") return value;
|
|
38
81
|
if (value === "" || value === void 0 || value === null) return "";
|
|
39
82
|
const numericValue = parseNumberish(value);
|
|
40
83
|
return numericValue === null ? value : numericValue;
|
|
41
84
|
}
|
|
42
|
-
function validateField(value, field) {
|
|
85
|
+
async function validateField(value, field, customMessages) {
|
|
43
86
|
if (!field.validations) return null;
|
|
87
|
+
const messages = { ...defaultErrorMessages, ...customMessages };
|
|
44
88
|
for (const rule of field.validations) {
|
|
45
89
|
switch (rule.type) {
|
|
46
90
|
case "required":
|
|
47
91
|
if (value === void 0 || value === null || value === "" || Array.isArray(value) && value.length === 0) {
|
|
48
|
-
return rule.message ||
|
|
92
|
+
return rule.message || formatMessage(messages.required, field, rule);
|
|
49
93
|
}
|
|
50
94
|
break;
|
|
51
95
|
case "min":
|
|
52
96
|
if (field.type === "number") {
|
|
53
97
|
const numericValue = parseNumberish(value);
|
|
54
98
|
if (numericValue !== null && numericValue < rule.value) {
|
|
55
|
-
|
|
99
|
+
const template = field.type === "number" ? messages.min : typeof value === "string" ? "{label} must be at least {value} characters" : messages.min;
|
|
100
|
+
return rule.message || formatMessage(template, field, rule);
|
|
56
101
|
}
|
|
57
102
|
break;
|
|
58
103
|
}
|
|
59
104
|
if (typeof value === "number" && value < rule.value) {
|
|
60
|
-
return rule.message ||
|
|
105
|
+
return rule.message || formatMessage(messages.min, field, rule);
|
|
61
106
|
}
|
|
62
107
|
if (typeof value === "string" && value.length < rule.value) {
|
|
63
|
-
|
|
108
|
+
const template = "{label} must be at least {value} characters";
|
|
109
|
+
return rule.message || formatMessage(template, field, rule);
|
|
64
110
|
}
|
|
65
111
|
break;
|
|
66
112
|
case "max":
|
|
67
113
|
if (field.type === "number") {
|
|
68
114
|
const numericValue = parseNumberish(value);
|
|
69
115
|
if (numericValue !== null && numericValue > rule.value) {
|
|
70
|
-
return rule.message ||
|
|
116
|
+
return rule.message || formatMessage(messages.max, field, rule);
|
|
71
117
|
}
|
|
72
118
|
break;
|
|
73
119
|
}
|
|
74
120
|
if (typeof value === "number" && value > rule.value) {
|
|
75
|
-
return rule.message ||
|
|
121
|
+
return rule.message || formatMessage(messages.max, field, rule);
|
|
76
122
|
}
|
|
77
123
|
if (typeof value === "string" && value.length > rule.value) {
|
|
78
|
-
|
|
124
|
+
const template = "{label} must be at most {value} characters";
|
|
125
|
+
return rule.message || formatMessage(template, field, rule);
|
|
79
126
|
}
|
|
80
127
|
break;
|
|
81
128
|
case "email": {
|
|
82
129
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
83
130
|
if (value && !emailRegex.test(value)) {
|
|
84
|
-
return rule.message ||
|
|
131
|
+
return rule.message || formatMessage(messages.email, field, rule);
|
|
85
132
|
}
|
|
86
133
|
break;
|
|
87
134
|
}
|
|
88
135
|
case "pattern":
|
|
89
136
|
if (value && rule.value && !new RegExp(rule.value).test(value)) {
|
|
90
|
-
return rule.message ||
|
|
137
|
+
return rule.message || formatMessage(messages.pattern, field, rule);
|
|
91
138
|
}
|
|
92
139
|
break;
|
|
93
140
|
case "custom":
|
|
94
141
|
if (rule.validator) {
|
|
95
|
-
const result = rule.validator(value);
|
|
142
|
+
const result = await rule.validator(value);
|
|
96
143
|
if (typeof result === "string") return result;
|
|
97
|
-
if (
|
|
144
|
+
if (result === false) return rule.message || formatMessage(messages.custom, field, rule);
|
|
98
145
|
}
|
|
99
146
|
break;
|
|
100
147
|
}
|
|
101
148
|
}
|
|
102
149
|
return null;
|
|
103
150
|
}
|
|
104
|
-
function validateFieldByName(fields, name, value) {
|
|
151
|
+
async function validateFieldByName(fields, name, value, resolver, allValues, customMessages) {
|
|
152
|
+
if (resolver && allValues) {
|
|
153
|
+
const resolverErrors = await resolver(allValues);
|
|
154
|
+
if (resolverErrors[name]) return resolverErrors[name];
|
|
155
|
+
}
|
|
105
156
|
const field = fields.find((f) => f.name === name);
|
|
106
157
|
if (!field) return null;
|
|
107
|
-
return validateField(value, field);
|
|
158
|
+
return await validateField(value, field, customMessages);
|
|
108
159
|
}
|
|
109
|
-
function validateForm(fields, values) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if (error) errors[field.name] = error;
|
|
160
|
+
async function validateForm(fields, values, resolver, customMessages) {
|
|
161
|
+
let errors = {};
|
|
162
|
+
if (resolver) {
|
|
163
|
+
errors = await resolver(values);
|
|
114
164
|
}
|
|
165
|
+
const validationPromises = fields.map(async (field) => {
|
|
166
|
+
if (errors[field.name]) return;
|
|
167
|
+
const isHidden = typeof field.hidden === "function" ? field.hidden(values) : field.hidden;
|
|
168
|
+
if (isHidden) return;
|
|
169
|
+
const error = await validateField(get(values, field.name), field, customMessages);
|
|
170
|
+
if (error) errors[field.name] = error;
|
|
171
|
+
});
|
|
172
|
+
await Promise.all(validationPromises);
|
|
115
173
|
return errors;
|
|
116
174
|
}
|
|
117
175
|
function getDefaultValues(fields) {
|
|
@@ -122,8 +180,11 @@ function getDefaultValues(fields) {
|
|
|
122
180
|
}
|
|
123
181
|
// Annotate the CommonJS export names for ESM import in node:
|
|
124
182
|
0 && (module.exports = {
|
|
183
|
+
defaultErrorMessages,
|
|
184
|
+
get,
|
|
125
185
|
getDefaultValues,
|
|
126
186
|
normalizeFieldValue,
|
|
187
|
+
set,
|
|
127
188
|
validateField,
|
|
128
189
|
validateFieldByName,
|
|
129
190
|
validateForm
|