@axenstudio/axen-form 0.1.18 → 0.1.19
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/dist/core/formStore.cjs +121 -25
- package/dist/core/formStore.d.ts +4 -1
- package/dist/core/formStore.js +121 -25
- package/dist/core/semanticValidation.cjs +22 -9
- package/dist/core/semanticValidation.d.ts +1 -1
- package/dist/core/semanticValidation.js +22 -9
- package/dist/index.d.ts +1 -1
- package/dist/react/AxenArrayField.cjs +2 -0
- package/dist/react/AxenArrayField.js +2 -0
- package/dist/react/AxenField.cjs +2 -0
- package/dist/react/AxenField.js +2 -0
- package/dist/react/useAxenForm.cjs +6 -0
- package/dist/react/useAxenForm.js +6 -0
- package/dist/types.d.ts +10 -1
- package/package.json +1 -1
package/dist/core/formStore.cjs
CHANGED
|
@@ -23,6 +23,9 @@ function createFormStore(options) {
|
|
|
23
23
|
values: structuredClone(initialValues),
|
|
24
24
|
errors: {},
|
|
25
25
|
touched: {},
|
|
26
|
+
validationStatus: "unvalidated",
|
|
27
|
+
fieldValidationStatus: {},
|
|
28
|
+
isValidating: false,
|
|
26
29
|
dirty: false,
|
|
27
30
|
isSubmitting: false,
|
|
28
31
|
submitCount: 0,
|
|
@@ -73,19 +76,42 @@ function createFormStore(options) {
|
|
|
73
76
|
}
|
|
74
77
|
function setFieldValue(name, value) {
|
|
75
78
|
const nextValues = pathUtils.set(state.values, name, value);
|
|
79
|
+
const statuses = { ...state.fieldValidationStatus, [name]: "unvalidated" };
|
|
80
|
+
const dependents = (options.fields ?? []).filter((field) => {
|
|
81
|
+
var _a;
|
|
82
|
+
return field.name !== name && ((_a = field.rules) == null ? void 0 : _a.countryField) === name;
|
|
83
|
+
});
|
|
84
|
+
const errors = { ...state.errors };
|
|
85
|
+
for (const field of dependents) {
|
|
86
|
+
statuses[field.name] = "unvalidated";
|
|
87
|
+
delete errors[field.name];
|
|
88
|
+
}
|
|
76
89
|
valuesRevision += 1;
|
|
77
|
-
state = {
|
|
90
|
+
state = {
|
|
91
|
+
...state,
|
|
92
|
+
values: nextValues,
|
|
93
|
+
errors,
|
|
94
|
+
dirty: !valuesEqual(nextValues, state.initialValues),
|
|
95
|
+
validationStatus: "unvalidated",
|
|
96
|
+
isValidating: false,
|
|
97
|
+
fieldValidationStatus: statuses
|
|
98
|
+
};
|
|
78
99
|
for (const field of options.fields ?? []) {
|
|
79
100
|
if (field.hidden && state.errors[field.name] && (typeof field.hidden === "function" ? field.hidden(nextValues) : field.hidden)) {
|
|
80
101
|
setFieldError(field.name, void 0);
|
|
81
102
|
}
|
|
82
103
|
}
|
|
83
104
|
notifyValuePath(name);
|
|
105
|
+
for (const field of dependents) notifyField(field.name);
|
|
84
106
|
if (validateOnChange && validationAdapter && validationSchema) {
|
|
85
107
|
const revision = valuesRevision;
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
108
|
+
for (const fieldName of [name, ...dependents.map((field) => field.name)]) {
|
|
109
|
+
void runFieldValidation(fieldName).catch((error) => {
|
|
110
|
+
if (revision === valuesRevision) {
|
|
111
|
+
setFieldError(fieldName, error instanceof Error ? error.message : "Validation failed");
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
89
115
|
}
|
|
90
116
|
}
|
|
91
117
|
function setFieldTouched(name, touched = true) {
|
|
@@ -103,23 +129,39 @@ function createFormStore(options) {
|
|
|
103
129
|
}
|
|
104
130
|
}
|
|
105
131
|
function setFieldError(name, error) {
|
|
106
|
-
const
|
|
107
|
-
if (error)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
132
|
+
const errors = { ...state.errors };
|
|
133
|
+
if (error) errors[name] = error;
|
|
134
|
+
else delete errors[name];
|
|
135
|
+
state = {
|
|
136
|
+
...state,
|
|
137
|
+
errors,
|
|
138
|
+
validationStatus: Object.keys(errors).length ? "invalid" : "unvalidated",
|
|
139
|
+
isValidating: false,
|
|
140
|
+
fieldValidationStatus: { ...state.fieldValidationStatus, [name]: error ? "invalid" : "unvalidated" }
|
|
141
|
+
};
|
|
113
142
|
notifyField(name);
|
|
114
143
|
notify();
|
|
115
144
|
}
|
|
116
145
|
function setValues(values) {
|
|
117
146
|
valuesRevision += 1;
|
|
118
|
-
state = {
|
|
147
|
+
state = {
|
|
148
|
+
...state,
|
|
149
|
+
values,
|
|
150
|
+
dirty: !valuesEqual(values, state.initialValues),
|
|
151
|
+
validationStatus: "unvalidated",
|
|
152
|
+
isValidating: false,
|
|
153
|
+
fieldValidationStatus: {}
|
|
154
|
+
};
|
|
119
155
|
notifyAllFields();
|
|
120
156
|
}
|
|
121
157
|
function setErrors(errors) {
|
|
122
|
-
state = {
|
|
158
|
+
state = {
|
|
159
|
+
...state,
|
|
160
|
+
errors,
|
|
161
|
+
validationStatus: Object.keys(errors).length ? "invalid" : "unvalidated",
|
|
162
|
+
isValidating: false,
|
|
163
|
+
fieldValidationStatus: {}
|
|
164
|
+
};
|
|
123
165
|
notifyAllFields();
|
|
124
166
|
}
|
|
125
167
|
function setFieldArrayValue(name, items, origins) {
|
|
@@ -152,7 +194,10 @@ function createFormStore(options) {
|
|
|
152
194
|
values,
|
|
153
195
|
dirty: !valuesEqual(values, state.initialValues),
|
|
154
196
|
errors: remap(state.errors),
|
|
155
|
-
touched: remap(state.touched)
|
|
197
|
+
touched: remap(state.touched),
|
|
198
|
+
validationStatus: "unvalidated",
|
|
199
|
+
isValidating: false,
|
|
200
|
+
fieldValidationStatus: remap(state.fieldValidationStatus)
|
|
156
201
|
};
|
|
157
202
|
notifyValuePath(name);
|
|
158
203
|
}
|
|
@@ -162,6 +207,9 @@ function createFormStore(options) {
|
|
|
162
207
|
values: newValues,
|
|
163
208
|
errors: {},
|
|
164
209
|
touched: {},
|
|
210
|
+
validationStatus: "unvalidated",
|
|
211
|
+
fieldValidationStatus: {},
|
|
212
|
+
isValidating: false,
|
|
165
213
|
dirty: false,
|
|
166
214
|
isSubmitting: false,
|
|
167
215
|
submitCount: state.submitCount,
|
|
@@ -174,12 +222,30 @@ function createFormStore(options) {
|
|
|
174
222
|
if (!validationAdapter || !validationSchema) return {};
|
|
175
223
|
const revision = valuesRevision;
|
|
176
224
|
const request = ++formRequest;
|
|
177
|
-
|
|
178
|
-
if (revision !== valuesRevision || request !== formRequest) return state.errors;
|
|
179
|
-
state = { ...state, errors };
|
|
225
|
+
state = { ...state, validationStatus: "validating", isValidating: true };
|
|
180
226
|
notify();
|
|
181
|
-
|
|
182
|
-
|
|
227
|
+
try {
|
|
228
|
+
const errors = await validationAdapter.validate(validationSchema, state.values);
|
|
229
|
+
if (revision !== valuesRevision || request !== formRequest) return state.errors;
|
|
230
|
+
const statuses = { ...state.fieldValidationStatus };
|
|
231
|
+
for (const field of options.fields ?? []) statuses[field.name] = errors[field.name] ? "invalid" : "valid";
|
|
232
|
+
state = {
|
|
233
|
+
...state,
|
|
234
|
+
errors,
|
|
235
|
+
validationStatus: Object.keys(errors).length ? "invalid" : "valid",
|
|
236
|
+
isValidating: false,
|
|
237
|
+
fieldValidationStatus: statuses
|
|
238
|
+
};
|
|
239
|
+
notify();
|
|
240
|
+
for (const name of fieldSubscribers.keys()) notifyField(name);
|
|
241
|
+
return errors;
|
|
242
|
+
} catch (error) {
|
|
243
|
+
if (revision === valuesRevision && request === formRequest) {
|
|
244
|
+
state = { ...state, validationStatus: "unvalidated", isValidating: false };
|
|
245
|
+
notify();
|
|
246
|
+
}
|
|
247
|
+
throw error;
|
|
248
|
+
}
|
|
183
249
|
}
|
|
184
250
|
async function validateField(name) {
|
|
185
251
|
var _a;
|
|
@@ -188,17 +254,47 @@ function createFormStore(options) {
|
|
|
188
254
|
const request = (fieldRequests.get(name) ?? 0) + 1;
|
|
189
255
|
fieldRequests.set(name, request);
|
|
190
256
|
const config = (_a = options.fields) == null ? void 0 : _a.find((field) => field.name === name);
|
|
191
|
-
|
|
257
|
+
const hidden = (config == null ? void 0 : config.hidden) && (typeof config.hidden === "function" ? config.hidden(state.values) : config.hidden);
|
|
258
|
+
if (hidden) {
|
|
192
259
|
setFieldError(name, void 0);
|
|
260
|
+
state = { ...state, fieldValidationStatus: { ...state.fieldValidationStatus, [name]: "valid" } };
|
|
261
|
+
notifyField(name);
|
|
262
|
+
notify();
|
|
193
263
|
return void 0;
|
|
194
264
|
}
|
|
265
|
+
state = {
|
|
266
|
+
...state,
|
|
267
|
+
fieldValidationStatus: { ...state.fieldValidationStatus, [name]: "validating" },
|
|
268
|
+
validationStatus: state.validationStatus === "validating" ? "validating" : "unvalidated"
|
|
269
|
+
};
|
|
270
|
+
notifyField(name);
|
|
271
|
+
notify();
|
|
195
272
|
const fieldValue = pathUtils.get(state.values, name);
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
273
|
+
try {
|
|
274
|
+
const error = await validationAdapter.validateField(validationSchema, name, fieldValue, state.values);
|
|
275
|
+
if (revision === valuesRevision && request === fieldRequests.get(name)) {
|
|
276
|
+
const errors = { ...state.errors };
|
|
277
|
+
if (error) errors[name] = error;
|
|
278
|
+
else delete errors[name];
|
|
279
|
+
state = {
|
|
280
|
+
...state,
|
|
281
|
+
errors,
|
|
282
|
+
validationStatus: state.validationStatus === "validating" ? "validating" : Object.keys(errors).length ? "invalid" : "unvalidated",
|
|
283
|
+
fieldValidationStatus: { ...state.fieldValidationStatus, [name]: error ? "invalid" : "valid" }
|
|
284
|
+
};
|
|
285
|
+
notifyField(name);
|
|
286
|
+
notify();
|
|
287
|
+
return error;
|
|
288
|
+
}
|
|
289
|
+
return state.errors[name];
|
|
290
|
+
} catch (error) {
|
|
291
|
+
if (revision === valuesRevision && request === fieldRequests.get(name)) {
|
|
292
|
+
state = { ...state, fieldValidationStatus: { ...state.fieldValidationStatus, [name]: "unvalidated" } };
|
|
293
|
+
notifyField(name);
|
|
294
|
+
notify();
|
|
295
|
+
}
|
|
296
|
+
throw error;
|
|
200
297
|
}
|
|
201
|
-
return state.errors[name];
|
|
202
298
|
}
|
|
203
299
|
async function runFieldValidation(name) {
|
|
204
300
|
if (validationAdapter == null ? void 0 : validationAdapter.validateField) {
|
package/dist/core/formStore.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import { FieldConfig, ValidationAdapter } from '../types';
|
|
1
|
+
import { FieldConfig, ValidationAdapter, ValidationStatus } from '../types';
|
|
2
2
|
export interface FormState<TValues extends Record<string, unknown> = Record<string, unknown>> {
|
|
3
3
|
values: TValues;
|
|
4
4
|
errors: Record<string, string>;
|
|
5
5
|
touched: Record<string, boolean>;
|
|
6
|
+
validationStatus: ValidationStatus;
|
|
7
|
+
fieldValidationStatus: Record<string, ValidationStatus>;
|
|
8
|
+
isValidating: boolean;
|
|
6
9
|
dirty: boolean;
|
|
7
10
|
isSubmitting: boolean;
|
|
8
11
|
submitCount: number;
|
package/dist/core/formStore.js
CHANGED
|
@@ -21,6 +21,9 @@ function createFormStore(options) {
|
|
|
21
21
|
values: structuredClone(initialValues),
|
|
22
22
|
errors: {},
|
|
23
23
|
touched: {},
|
|
24
|
+
validationStatus: "unvalidated",
|
|
25
|
+
fieldValidationStatus: {},
|
|
26
|
+
isValidating: false,
|
|
24
27
|
dirty: false,
|
|
25
28
|
isSubmitting: false,
|
|
26
29
|
submitCount: 0,
|
|
@@ -71,19 +74,42 @@ function createFormStore(options) {
|
|
|
71
74
|
}
|
|
72
75
|
function setFieldValue(name, value) {
|
|
73
76
|
const nextValues = set(state.values, name, value);
|
|
77
|
+
const statuses = { ...state.fieldValidationStatus, [name]: "unvalidated" };
|
|
78
|
+
const dependents = (options.fields ?? []).filter((field) => {
|
|
79
|
+
var _a;
|
|
80
|
+
return field.name !== name && ((_a = field.rules) == null ? void 0 : _a.countryField) === name;
|
|
81
|
+
});
|
|
82
|
+
const errors = { ...state.errors };
|
|
83
|
+
for (const field of dependents) {
|
|
84
|
+
statuses[field.name] = "unvalidated";
|
|
85
|
+
delete errors[field.name];
|
|
86
|
+
}
|
|
74
87
|
valuesRevision += 1;
|
|
75
|
-
state = {
|
|
88
|
+
state = {
|
|
89
|
+
...state,
|
|
90
|
+
values: nextValues,
|
|
91
|
+
errors,
|
|
92
|
+
dirty: !valuesEqual(nextValues, state.initialValues),
|
|
93
|
+
validationStatus: "unvalidated",
|
|
94
|
+
isValidating: false,
|
|
95
|
+
fieldValidationStatus: statuses
|
|
96
|
+
};
|
|
76
97
|
for (const field of options.fields ?? []) {
|
|
77
98
|
if (field.hidden && state.errors[field.name] && (typeof field.hidden === "function" ? field.hidden(nextValues) : field.hidden)) {
|
|
78
99
|
setFieldError(field.name, void 0);
|
|
79
100
|
}
|
|
80
101
|
}
|
|
81
102
|
notifyValuePath(name);
|
|
103
|
+
for (const field of dependents) notifyField(field.name);
|
|
82
104
|
if (validateOnChange && validationAdapter && validationSchema) {
|
|
83
105
|
const revision = valuesRevision;
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
106
|
+
for (const fieldName of [name, ...dependents.map((field) => field.name)]) {
|
|
107
|
+
void runFieldValidation(fieldName).catch((error) => {
|
|
108
|
+
if (revision === valuesRevision) {
|
|
109
|
+
setFieldError(fieldName, error instanceof Error ? error.message : "Validation failed");
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
87
113
|
}
|
|
88
114
|
}
|
|
89
115
|
function setFieldTouched(name, touched = true) {
|
|
@@ -101,23 +127,39 @@ function createFormStore(options) {
|
|
|
101
127
|
}
|
|
102
128
|
}
|
|
103
129
|
function setFieldError(name, error) {
|
|
104
|
-
const
|
|
105
|
-
if (error)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
130
|
+
const errors = { ...state.errors };
|
|
131
|
+
if (error) errors[name] = error;
|
|
132
|
+
else delete errors[name];
|
|
133
|
+
state = {
|
|
134
|
+
...state,
|
|
135
|
+
errors,
|
|
136
|
+
validationStatus: Object.keys(errors).length ? "invalid" : "unvalidated",
|
|
137
|
+
isValidating: false,
|
|
138
|
+
fieldValidationStatus: { ...state.fieldValidationStatus, [name]: error ? "invalid" : "unvalidated" }
|
|
139
|
+
};
|
|
111
140
|
notifyField(name);
|
|
112
141
|
notify();
|
|
113
142
|
}
|
|
114
143
|
function setValues(values) {
|
|
115
144
|
valuesRevision += 1;
|
|
116
|
-
state = {
|
|
145
|
+
state = {
|
|
146
|
+
...state,
|
|
147
|
+
values,
|
|
148
|
+
dirty: !valuesEqual(values, state.initialValues),
|
|
149
|
+
validationStatus: "unvalidated",
|
|
150
|
+
isValidating: false,
|
|
151
|
+
fieldValidationStatus: {}
|
|
152
|
+
};
|
|
117
153
|
notifyAllFields();
|
|
118
154
|
}
|
|
119
155
|
function setErrors(errors) {
|
|
120
|
-
state = {
|
|
156
|
+
state = {
|
|
157
|
+
...state,
|
|
158
|
+
errors,
|
|
159
|
+
validationStatus: Object.keys(errors).length ? "invalid" : "unvalidated",
|
|
160
|
+
isValidating: false,
|
|
161
|
+
fieldValidationStatus: {}
|
|
162
|
+
};
|
|
121
163
|
notifyAllFields();
|
|
122
164
|
}
|
|
123
165
|
function setFieldArrayValue(name, items, origins) {
|
|
@@ -150,7 +192,10 @@ function createFormStore(options) {
|
|
|
150
192
|
values,
|
|
151
193
|
dirty: !valuesEqual(values, state.initialValues),
|
|
152
194
|
errors: remap(state.errors),
|
|
153
|
-
touched: remap(state.touched)
|
|
195
|
+
touched: remap(state.touched),
|
|
196
|
+
validationStatus: "unvalidated",
|
|
197
|
+
isValidating: false,
|
|
198
|
+
fieldValidationStatus: remap(state.fieldValidationStatus)
|
|
154
199
|
};
|
|
155
200
|
notifyValuePath(name);
|
|
156
201
|
}
|
|
@@ -160,6 +205,9 @@ function createFormStore(options) {
|
|
|
160
205
|
values: newValues,
|
|
161
206
|
errors: {},
|
|
162
207
|
touched: {},
|
|
208
|
+
validationStatus: "unvalidated",
|
|
209
|
+
fieldValidationStatus: {},
|
|
210
|
+
isValidating: false,
|
|
163
211
|
dirty: false,
|
|
164
212
|
isSubmitting: false,
|
|
165
213
|
submitCount: state.submitCount,
|
|
@@ -172,12 +220,30 @@ function createFormStore(options) {
|
|
|
172
220
|
if (!validationAdapter || !validationSchema) return {};
|
|
173
221
|
const revision = valuesRevision;
|
|
174
222
|
const request = ++formRequest;
|
|
175
|
-
|
|
176
|
-
if (revision !== valuesRevision || request !== formRequest) return state.errors;
|
|
177
|
-
state = { ...state, errors };
|
|
223
|
+
state = { ...state, validationStatus: "validating", isValidating: true };
|
|
178
224
|
notify();
|
|
179
|
-
|
|
180
|
-
|
|
225
|
+
try {
|
|
226
|
+
const errors = await validationAdapter.validate(validationSchema, state.values);
|
|
227
|
+
if (revision !== valuesRevision || request !== formRequest) return state.errors;
|
|
228
|
+
const statuses = { ...state.fieldValidationStatus };
|
|
229
|
+
for (const field of options.fields ?? []) statuses[field.name] = errors[field.name] ? "invalid" : "valid";
|
|
230
|
+
state = {
|
|
231
|
+
...state,
|
|
232
|
+
errors,
|
|
233
|
+
validationStatus: Object.keys(errors).length ? "invalid" : "valid",
|
|
234
|
+
isValidating: false,
|
|
235
|
+
fieldValidationStatus: statuses
|
|
236
|
+
};
|
|
237
|
+
notify();
|
|
238
|
+
for (const name of fieldSubscribers.keys()) notifyField(name);
|
|
239
|
+
return errors;
|
|
240
|
+
} catch (error) {
|
|
241
|
+
if (revision === valuesRevision && request === formRequest) {
|
|
242
|
+
state = { ...state, validationStatus: "unvalidated", isValidating: false };
|
|
243
|
+
notify();
|
|
244
|
+
}
|
|
245
|
+
throw error;
|
|
246
|
+
}
|
|
181
247
|
}
|
|
182
248
|
async function validateField(name) {
|
|
183
249
|
var _a;
|
|
@@ -186,17 +252,47 @@ function createFormStore(options) {
|
|
|
186
252
|
const request = (fieldRequests.get(name) ?? 0) + 1;
|
|
187
253
|
fieldRequests.set(name, request);
|
|
188
254
|
const config = (_a = options.fields) == null ? void 0 : _a.find((field) => field.name === name);
|
|
189
|
-
|
|
255
|
+
const hidden = (config == null ? void 0 : config.hidden) && (typeof config.hidden === "function" ? config.hidden(state.values) : config.hidden);
|
|
256
|
+
if (hidden) {
|
|
190
257
|
setFieldError(name, void 0);
|
|
258
|
+
state = { ...state, fieldValidationStatus: { ...state.fieldValidationStatus, [name]: "valid" } };
|
|
259
|
+
notifyField(name);
|
|
260
|
+
notify();
|
|
191
261
|
return void 0;
|
|
192
262
|
}
|
|
263
|
+
state = {
|
|
264
|
+
...state,
|
|
265
|
+
fieldValidationStatus: { ...state.fieldValidationStatus, [name]: "validating" },
|
|
266
|
+
validationStatus: state.validationStatus === "validating" ? "validating" : "unvalidated"
|
|
267
|
+
};
|
|
268
|
+
notifyField(name);
|
|
269
|
+
notify();
|
|
193
270
|
const fieldValue = get(state.values, name);
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
271
|
+
try {
|
|
272
|
+
const error = await validationAdapter.validateField(validationSchema, name, fieldValue, state.values);
|
|
273
|
+
if (revision === valuesRevision && request === fieldRequests.get(name)) {
|
|
274
|
+
const errors = { ...state.errors };
|
|
275
|
+
if (error) errors[name] = error;
|
|
276
|
+
else delete errors[name];
|
|
277
|
+
state = {
|
|
278
|
+
...state,
|
|
279
|
+
errors,
|
|
280
|
+
validationStatus: state.validationStatus === "validating" ? "validating" : Object.keys(errors).length ? "invalid" : "unvalidated",
|
|
281
|
+
fieldValidationStatus: { ...state.fieldValidationStatus, [name]: error ? "invalid" : "valid" }
|
|
282
|
+
};
|
|
283
|
+
notifyField(name);
|
|
284
|
+
notify();
|
|
285
|
+
return error;
|
|
286
|
+
}
|
|
287
|
+
return state.errors[name];
|
|
288
|
+
} catch (error) {
|
|
289
|
+
if (revision === valuesRevision && request === fieldRequests.get(name)) {
|
|
290
|
+
state = { ...state, fieldValidationStatus: { ...state.fieldValidationStatus, [name]: "unvalidated" } };
|
|
291
|
+
notifyField(name);
|
|
292
|
+
notify();
|
|
293
|
+
}
|
|
294
|
+
throw error;
|
|
198
295
|
}
|
|
199
|
-
return state.errors[name];
|
|
200
296
|
}
|
|
201
297
|
async function runFieldValidation(name) {
|
|
202
298
|
if (validationAdapter == null ? void 0 : validationAdapter.validateField) {
|
|
@@ -4,8 +4,8 @@ const pathUtils = require("./pathUtils.cjs");
|
|
|
4
4
|
function isHidden(field, values) {
|
|
5
5
|
return typeof field.hidden === "function" ? Boolean(field.hidden(values)) : Boolean(field.hidden);
|
|
6
6
|
}
|
|
7
|
-
function validateSemanticValue(field, value, policy) {
|
|
8
|
-
var _a;
|
|
7
|
+
function validateSemanticValue(field, value, policy, values = {}) {
|
|
8
|
+
var _a, _b;
|
|
9
9
|
if (field.validation) return void 0;
|
|
10
10
|
const rules = field.rules;
|
|
11
11
|
const inferred = field.type === "tel" ? "phone" : field.type;
|
|
@@ -29,13 +29,23 @@ function validateSemanticValue(field, value, policy) {
|
|
|
29
29
|
const region = ((rules == null ? void 0 : rules.region) ?? "").toUpperCase();
|
|
30
30
|
valid = region === "ID" ? /^(?:\+62|62|0)8\d{7,11}$/u.test(clean) : /^\+[1-9]\d{7,14}$/u.test(clean);
|
|
31
31
|
} else if (format === "postalCode") {
|
|
32
|
-
const
|
|
32
|
+
const resolved = ((_a = rules == null ? void 0 : rules.countryResolver) == null ? void 0 : _a.call(rules, values)) ?? ((rules == null ? void 0 : rules.countryField) ? pathUtils.get(values, rules.countryField) : void 0) ?? (rules == null ? void 0 : rules.country);
|
|
33
|
+
const country = typeof resolved === "string" ? resolved.trim().toUpperCase() : "";
|
|
34
|
+
if (!country && ((rules == null ? void 0 : rules.countryField) || (rules == null ? void 0 : rules.countryResolver))) return void 0;
|
|
33
35
|
const patterns = {
|
|
34
36
|
ID: /^\d{5}$/u,
|
|
35
37
|
US: /^\d{5}(?:-\d{4})?$/u,
|
|
36
|
-
SG: /^\d{6}$/u
|
|
38
|
+
SG: /^\d{6}$/u,
|
|
39
|
+
JP: /^\d{3}-?\d{4}$/u,
|
|
40
|
+
GB: /^(?:GIR\s?0AA|[A-Z]{1,2}\d[A-Z\d]?\s?\d[A-Z]{2})$/iu,
|
|
41
|
+
UK: /^(?:GIR\s?0AA|[A-Z]{1,2}\d[A-Z\d]?\s?\d[A-Z]{2})$/iu
|
|
37
42
|
};
|
|
38
|
-
|
|
43
|
+
const validator = ((_b = policy == null ? void 0 : policy.postalCodePatterns) == null ? void 0 : _b[country]) ?? patterns[country];
|
|
44
|
+
if (!validator) valid = (policy == null ? void 0 : policy.unknownCountry) === "ignore";
|
|
45
|
+
else if (validator instanceof RegExp) {
|
|
46
|
+
validator.lastIndex = 0;
|
|
47
|
+
valid = validator.test(input);
|
|
48
|
+
} else valid = Boolean(validator(input, values));
|
|
39
49
|
}
|
|
40
50
|
return valid ? void 0 : (rules == null ? void 0 : rules.message) ?? `Invalid ${format}`;
|
|
41
51
|
}
|
|
@@ -51,17 +61,20 @@ function withValidationPolicy(adapter, policy) {
|
|
|
51
61
|
if (isHidden(field, values)) {
|
|
52
62
|
delete errors[field.name];
|
|
53
63
|
} else if (!errors[field.name]) {
|
|
54
|
-
const error = validateSemanticValue(field, pathUtils.get(values, field.name), policy);
|
|
64
|
+
const error = validateSemanticValue(field, pathUtils.get(values, field.name), policy, values);
|
|
55
65
|
if (error) errors[field.name] = error;
|
|
56
66
|
}
|
|
57
67
|
}
|
|
58
68
|
return errors;
|
|
59
69
|
},
|
|
60
|
-
async validateField(schema, name, value) {
|
|
70
|
+
async validateField(schema, name, value, values) {
|
|
61
71
|
var _a;
|
|
62
72
|
const { base, fields } = schema;
|
|
63
|
-
const
|
|
64
|
-
|
|
73
|
+
const context = values ?? { [name]: value };
|
|
74
|
+
const field = fields.find((f) => f.name === name) ?? { name, type: "text" };
|
|
75
|
+
if (isHidden(field, context)) return void 0;
|
|
76
|
+
const error = await ((_a = adapter.validateField) == null ? void 0 : _a.call(adapter, base, name, value, context));
|
|
77
|
+
return error ?? validateSemanticValue(field, value, policy, context);
|
|
65
78
|
}
|
|
66
79
|
};
|
|
67
80
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FieldConfig, ValidationAdapter, ValidationPolicy } from '../types';
|
|
2
2
|
/** Optional semantics: custom Yup/Zod field schemas retain precedence. */
|
|
3
|
-
export declare function validateSemanticValue(field: FieldConfig, value: unknown, policy?: ValidationPolicy): string | undefined;
|
|
3
|
+
export declare function validateSemanticValue(field: FieldConfig, value: unknown, policy?: ValidationPolicy, values?: Record<string, unknown>): string | undefined;
|
|
4
4
|
/** Adapter-independent policy wrapper. No mandatory Yup, Zod, or UI dependencies. */
|
|
5
5
|
export declare function withValidationPolicy(adapter: ValidationAdapter, policy?: ValidationPolicy): ValidationAdapter;
|
|
@@ -2,8 +2,8 @@ import { get } from "./pathUtils.js";
|
|
|
2
2
|
function isHidden(field, values) {
|
|
3
3
|
return typeof field.hidden === "function" ? Boolean(field.hidden(values)) : Boolean(field.hidden);
|
|
4
4
|
}
|
|
5
|
-
function validateSemanticValue(field, value, policy) {
|
|
6
|
-
var _a;
|
|
5
|
+
function validateSemanticValue(field, value, policy, values = {}) {
|
|
6
|
+
var _a, _b;
|
|
7
7
|
if (field.validation) return void 0;
|
|
8
8
|
const rules = field.rules;
|
|
9
9
|
const inferred = field.type === "tel" ? "phone" : field.type;
|
|
@@ -27,13 +27,23 @@ function validateSemanticValue(field, value, policy) {
|
|
|
27
27
|
const region = ((rules == null ? void 0 : rules.region) ?? "").toUpperCase();
|
|
28
28
|
valid = region === "ID" ? /^(?:\+62|62|0)8\d{7,11}$/u.test(clean) : /^\+[1-9]\d{7,14}$/u.test(clean);
|
|
29
29
|
} else if (format === "postalCode") {
|
|
30
|
-
const
|
|
30
|
+
const resolved = ((_a = rules == null ? void 0 : rules.countryResolver) == null ? void 0 : _a.call(rules, values)) ?? ((rules == null ? void 0 : rules.countryField) ? get(values, rules.countryField) : void 0) ?? (rules == null ? void 0 : rules.country);
|
|
31
|
+
const country = typeof resolved === "string" ? resolved.trim().toUpperCase() : "";
|
|
32
|
+
if (!country && ((rules == null ? void 0 : rules.countryField) || (rules == null ? void 0 : rules.countryResolver))) return void 0;
|
|
31
33
|
const patterns = {
|
|
32
34
|
ID: /^\d{5}$/u,
|
|
33
35
|
US: /^\d{5}(?:-\d{4})?$/u,
|
|
34
|
-
SG: /^\d{6}$/u
|
|
36
|
+
SG: /^\d{6}$/u,
|
|
37
|
+
JP: /^\d{3}-?\d{4}$/u,
|
|
38
|
+
GB: /^(?:GIR\s?0AA|[A-Z]{1,2}\d[A-Z\d]?\s?\d[A-Z]{2})$/iu,
|
|
39
|
+
UK: /^(?:GIR\s?0AA|[A-Z]{1,2}\d[A-Z\d]?\s?\d[A-Z]{2})$/iu
|
|
35
40
|
};
|
|
36
|
-
|
|
41
|
+
const validator = ((_b = policy == null ? void 0 : policy.postalCodePatterns) == null ? void 0 : _b[country]) ?? patterns[country];
|
|
42
|
+
if (!validator) valid = (policy == null ? void 0 : policy.unknownCountry) === "ignore";
|
|
43
|
+
else if (validator instanceof RegExp) {
|
|
44
|
+
validator.lastIndex = 0;
|
|
45
|
+
valid = validator.test(input);
|
|
46
|
+
} else valid = Boolean(validator(input, values));
|
|
37
47
|
}
|
|
38
48
|
return valid ? void 0 : (rules == null ? void 0 : rules.message) ?? `Invalid ${format}`;
|
|
39
49
|
}
|
|
@@ -49,17 +59,20 @@ function withValidationPolicy(adapter, policy) {
|
|
|
49
59
|
if (isHidden(field, values)) {
|
|
50
60
|
delete errors[field.name];
|
|
51
61
|
} else if (!errors[field.name]) {
|
|
52
|
-
const error = validateSemanticValue(field, get(values, field.name), policy);
|
|
62
|
+
const error = validateSemanticValue(field, get(values, field.name), policy, values);
|
|
53
63
|
if (error) errors[field.name] = error;
|
|
54
64
|
}
|
|
55
65
|
}
|
|
56
66
|
return errors;
|
|
57
67
|
},
|
|
58
|
-
async validateField(schema, name, value) {
|
|
68
|
+
async validateField(schema, name, value, values) {
|
|
59
69
|
var _a;
|
|
60
70
|
const { base, fields } = schema;
|
|
61
|
-
const
|
|
62
|
-
|
|
71
|
+
const context = values ?? { [name]: value };
|
|
72
|
+
const field = fields.find((f) => f.name === name) ?? { name, type: "text" };
|
|
73
|
+
if (isHidden(field, context)) return void 0;
|
|
74
|
+
const error = await ((_a = adapter.validateField) == null ? void 0 : _a.call(adapter, base, name, value, context));
|
|
75
|
+
return error ?? validateSemanticValue(field, value, policy, context);
|
|
63
76
|
}
|
|
64
77
|
};
|
|
65
78
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { ArrayFieldConfig, ArrayHelpers, AutocompleteOption, AxenFormProps, AxenFormRef, BoxProps, Breakpoint, BuiltInFieldType, DividerProps, FieldComponentProps, FieldConfig, FieldGroupConfig, FieldOption, FieldTypeComponentMap, FormApi, FormConfig, GridProps, GridSize, PayloadConfig, SpacerProps, StackProps, ValidationAdapter, ValidationPolicy, ValidationRules, } from './types';
|
|
1
|
+
export type { ArrayFieldConfig, ArrayHelpers, AutocompleteOption, AxenFormProps, AxenFormRef, BoxProps, Breakpoint, BuiltInFieldType, DividerProps, FieldComponentProps, FieldConfig, FieldGroupConfig, FieldOption, FieldTypeComponentMap, FormApi, FormConfig, GridProps, GridSize, PayloadConfig, SpacerProps, StackProps, ValidationAdapter, ValidationPolicy, ValidationRules, ValidationStatus, } from './types';
|
|
2
2
|
export { isFieldGroup } from './types';
|
|
3
3
|
export { formatDateLocale, formatDateTimeLocale, formatTimeLocale, getDefaultLocale, toISODateString, toISODateTimeLocalString, toISOTimeString, } from './core/dateUtils';
|
|
4
4
|
export { createFormStore } from './core/formStore';
|
|
@@ -57,6 +57,8 @@ function ArrayItemField({
|
|
|
57
57
|
touched: store.getState().touched,
|
|
58
58
|
dirty: store.getState().dirty,
|
|
59
59
|
isSubmitting: store.getState().isSubmitting,
|
|
60
|
+
validationStatus: store.getState().validationStatus,
|
|
61
|
+
fieldValidationStatus: store.getState().fieldValidationStatus,
|
|
60
62
|
isValid: Object.keys(store.getState().errors).length === 0,
|
|
61
63
|
submitCount: store.getState().submitCount,
|
|
62
64
|
setFieldValue: store.setFieldValue,
|
|
@@ -55,6 +55,8 @@ function ArrayItemField({
|
|
|
55
55
|
touched: store.getState().touched,
|
|
56
56
|
dirty: store.getState().dirty,
|
|
57
57
|
isSubmitting: store.getState().isSubmitting,
|
|
58
|
+
validationStatus: store.getState().validationStatus,
|
|
59
|
+
fieldValidationStatus: store.getState().fieldValidationStatus,
|
|
58
60
|
isValid: Object.keys(store.getState().errors).length === 0,
|
|
59
61
|
submitCount: store.getState().submitCount,
|
|
60
62
|
setFieldValue: store.setFieldValue,
|
package/dist/react/AxenField.cjs
CHANGED
|
@@ -29,6 +29,8 @@ function AxenField({ name, config, component, children }) {
|
|
|
29
29
|
touched: store.getState().touched,
|
|
30
30
|
dirty: store.getState().dirty,
|
|
31
31
|
isSubmitting: store.getState().isSubmitting,
|
|
32
|
+
validationStatus: store.getState().validationStatus,
|
|
33
|
+
fieldValidationStatus: store.getState().fieldValidationStatus,
|
|
32
34
|
isValid: Object.keys(store.getState().errors).length === 0,
|
|
33
35
|
submitCount: store.getState().submitCount,
|
|
34
36
|
setFieldValue: store.setFieldValue,
|
package/dist/react/AxenField.js
CHANGED
|
@@ -27,6 +27,8 @@ function AxenField({ name, config, component, children }) {
|
|
|
27
27
|
touched: store.getState().touched,
|
|
28
28
|
dirty: store.getState().dirty,
|
|
29
29
|
isSubmitting: store.getState().isSubmitting,
|
|
30
|
+
validationStatus: store.getState().validationStatus,
|
|
31
|
+
fieldValidationStatus: store.getState().fieldValidationStatus,
|
|
30
32
|
isValid: Object.keys(store.getState().errors).length === 0,
|
|
31
33
|
submitCount: store.getState().submitCount,
|
|
32
34
|
setFieldValue: store.setFieldValue,
|
|
@@ -70,6 +70,12 @@ function useAxenForm(props) {
|
|
|
70
70
|
get isSubmitting() {
|
|
71
71
|
return store.getState().isSubmitting;
|
|
72
72
|
},
|
|
73
|
+
get validationStatus() {
|
|
74
|
+
return store.getState().validationStatus;
|
|
75
|
+
},
|
|
76
|
+
get fieldValidationStatus() {
|
|
77
|
+
return store.getState().fieldValidationStatus;
|
|
78
|
+
},
|
|
73
79
|
get isValid() {
|
|
74
80
|
return Object.keys(store.getState().errors).length === 0;
|
|
75
81
|
},
|
|
@@ -68,6 +68,12 @@ function useAxenForm(props) {
|
|
|
68
68
|
get isSubmitting() {
|
|
69
69
|
return store.getState().isSubmitting;
|
|
70
70
|
},
|
|
71
|
+
get validationStatus() {
|
|
72
|
+
return store.getState().validationStatus;
|
|
73
|
+
},
|
|
74
|
+
get fieldValidationStatus() {
|
|
75
|
+
return store.getState().fieldValidationStatus;
|
|
76
|
+
},
|
|
71
77
|
get isValid() {
|
|
72
78
|
return Object.keys(store.getState().errors).length === 0;
|
|
73
79
|
},
|
package/dist/types.d.ts
CHANGED
|
@@ -17,13 +17,20 @@ export interface ValidationRules {
|
|
|
17
17
|
format?: 'email' | 'url' | 'phone' | 'postalCode';
|
|
18
18
|
region?: string;
|
|
19
19
|
country?: string;
|
|
20
|
+
/** Resolve country from a sibling or nested form field. */
|
|
21
|
+
countryField?: string;
|
|
22
|
+
/** Optional custom country resolver, taking precedence over countryField. */
|
|
23
|
+
countryResolver?: (values: Record<string, unknown>) => string | undefined;
|
|
20
24
|
message?: string;
|
|
21
25
|
}
|
|
22
26
|
/** Legacy required-only mode remains the default. */
|
|
23
27
|
export interface ValidationPolicy {
|
|
24
28
|
mode?: 'legacy-required-only' | 'standard';
|
|
25
29
|
locale?: string;
|
|
30
|
+
postalCodePatterns?: Record<string, RegExp | ((code: string, values: Record<string, unknown>) => boolean)>;
|
|
31
|
+
unknownCountry?: 'reject' | 'ignore';
|
|
26
32
|
}
|
|
33
|
+
export type ValidationStatus = 'unvalidated' | 'validating' | 'valid' | 'invalid';
|
|
27
34
|
/** Base field configuration */
|
|
28
35
|
export interface FieldConfig<TValues extends Record<string, unknown> = Record<string, unknown>> {
|
|
29
36
|
name: string;
|
|
@@ -125,6 +132,8 @@ export interface FormApi<TValues extends Record<string, unknown> = Record<string
|
|
|
125
132
|
touched: Record<string, boolean>;
|
|
126
133
|
dirty: boolean;
|
|
127
134
|
isSubmitting: boolean;
|
|
135
|
+
validationStatus: ValidationStatus;
|
|
136
|
+
fieldValidationStatus: Record<string, ValidationStatus>;
|
|
128
137
|
isValid: boolean;
|
|
129
138
|
submitCount: number;
|
|
130
139
|
setFieldValue: (name: string, value: unknown) => void;
|
|
@@ -183,7 +192,7 @@ export interface AxenFormProps<TValues extends Record<string, unknown> = Record<
|
|
|
183
192
|
export interface ValidationAdapter {
|
|
184
193
|
buildSchema: (fields: FieldConfig[]) => unknown;
|
|
185
194
|
validate: (schema: unknown, values: Record<string, unknown>) => Promise<Record<string, string>>;
|
|
186
|
-
validateField?: (schema: unknown, name: string, value: unknown) => Promise<string | undefined>;
|
|
195
|
+
validateField?: (schema: unknown, name: string, value: unknown, values?: Record<string, unknown>) => Promise<string | undefined>;
|
|
187
196
|
}
|
|
188
197
|
export interface ArrayHelpers {
|
|
189
198
|
push: (value: unknown) => void;
|
package/package.json
CHANGED