@axenstudio/axen-form 0.1.17 → 0.1.18
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 +34 -3
- package/dist/adapters/yupAdapter.cjs +29 -10
- package/dist/adapters/yupAdapter.d.ts +1 -2
- package/dist/adapters/yupAdapter.js +29 -10
- package/dist/adapters/zodAdapter.cjs +29 -10
- package/dist/adapters/zodAdapter.d.ts +1 -2
- package/dist/adapters/zodAdapter.js +29 -10
- package/dist/core/dateUtils.d.ts +0 -1
- package/dist/core/formStore.cjs +110 -39
- package/dist/core/formStore.d.ts +1 -1
- package/dist/core/formStore.js +110 -39
- package/dist/core/numberUtils.d.ts +0 -1
- package/dist/core/pathUtils.d.ts +0 -1
- package/dist/core/payloadUtils.d.ts +0 -1
- package/dist/core/semanticValidation.cjs +69 -0
- package/dist/core/semanticValidation.d.ts +5 -0
- package/dist/core/semanticValidation.js +69 -0
- package/dist/core/validation.cjs +4 -3
- package/dist/core/validation.d.ts +0 -1
- package/dist/core/validation.js +4 -3
- package/dist/fields/AutocompleteField/AutocompleteField.d.ts +1 -2
- package/dist/fields/AutocompleteMultiField/AutocompleteMultiField.d.ts +1 -2
- package/dist/fields/AutocompletePredictField/AutocompletePredictField.d.ts +1 -2
- package/dist/fields/CheckboxField/CheckboxField.d.ts +1 -2
- package/dist/fields/ColorField/ColorField.d.ts +1 -2
- package/dist/fields/CurrencyField/CurrencyField.d.ts +1 -2
- package/dist/fields/DateField/DateField.d.ts +1 -2
- package/dist/fields/DateField/DatePicker.d.ts +1 -2
- package/dist/fields/DateTimeField/DateTimeField.d.ts +1 -2
- package/dist/fields/EmailField/EmailField.d.ts +1 -2
- package/dist/fields/NumberField/NumberField.d.ts +1 -2
- package/dist/fields/PasswordField/PasswordField.d.ts +1 -2
- package/dist/fields/PhoneField/PhoneField.d.ts +1 -2
- package/dist/fields/RadioField/RadioField.d.ts +1 -2
- package/dist/fields/SelectField/SelectField.d.ts +1 -2
- package/dist/fields/SliderField/SliderField.d.ts +1 -2
- package/dist/fields/SwitchField/SwitchField.d.ts +1 -2
- package/dist/fields/TextField/TextField.d.ts +1 -2
- package/dist/fields/TextareaField/TextareaField.d.ts +1 -2
- package/dist/fields/TimeField/TimeField.d.ts +1 -2
- package/dist/fields/TimeField/TimePicker.d.ts +1 -2
- package/dist/fields/defaultComponentMap.cjs +3 -0
- package/dist/fields/defaultComponentMap.d.ts +0 -1
- package/dist/fields/defaultComponentMap.js +3 -0
- package/dist/fields/defaultPlaceholders.d.ts +0 -1
- package/dist/fields/fieldBase.module.css.cjs +92 -70
- package/dist/fields/fieldBase.module.css.js +92 -70
- package/dist/fields/fieldUtils.d.ts +0 -1
- package/dist/fields/iconUtils.d.ts +0 -1
- package/dist/fields/icons.d.ts +3 -4
- package/dist/index.cjs +31 -22
- package/dist/index.d.ts +3 -2
- package/dist/index.js +32 -23
- package/dist/layout/Box/Box.d.ts +1 -2
- package/dist/layout/Divider/Divider.d.ts +1 -2
- package/dist/layout/Grid/Grid.d.ts +1 -2
- package/dist/layout/Spacer/Spacer.d.ts +1 -2
- package/dist/layout/Stack/Stack.d.ts +1 -2
- package/dist/layout/index.d.ts +0 -1
- package/dist/react/AxenArrayField.d.ts +1 -2
- package/dist/react/AxenField.d.ts +1 -2
- package/dist/react/AxenForm.cjs +29 -23
- package/dist/react/AxenForm.d.ts +0 -1
- package/dist/react/AxenForm.js +30 -24
- package/dist/react/FormContext.d.ts +0 -1
- package/dist/react/useAxenForm.cjs +11 -4
- package/dist/react/useAxenForm.d.ts +1 -2
- package/dist/react/useAxenForm.js +11 -4
- package/dist/react/useField.cjs +3 -0
- package/dist/react/useField.d.ts +0 -1
- package/dist/react/useField.js +3 -0
- package/dist/react/useFieldArray.cjs +19 -13
- package/dist/react/useFieldArray.d.ts +0 -1
- package/dist/react/useFieldArray.js +19 -13
- package/dist/react/useFormState.cjs +18 -0
- package/dist/react/useFormState.d.ts +5 -0
- package/dist/react/useFormState.js +18 -0
- package/dist/types.d.ts +18 -2
- package/package.json +5 -2
- package/dist/adapters/yupAdapter.d.ts.map +0 -1
- package/dist/adapters/zodAdapter.d.ts.map +0 -1
- package/dist/core/dateUtils.d.ts.map +0 -1
- package/dist/core/formStore.d.ts.map +0 -1
- package/dist/core/numberUtils.d.ts.map +0 -1
- package/dist/core/pathUtils.d.ts.map +0 -1
- package/dist/core/payloadUtils.d.ts.map +0 -1
- package/dist/core/validation.d.ts.map +0 -1
- package/dist/fields/AutocompleteField/AutocompleteField.d.ts.map +0 -1
- package/dist/fields/AutocompleteMultiField/AutocompleteMultiField.d.ts.map +0 -1
- package/dist/fields/AutocompletePredictField/AutocompletePredictField.d.ts.map +0 -1
- package/dist/fields/CheckboxField/CheckboxField.d.ts.map +0 -1
- package/dist/fields/ColorField/ColorField.d.ts.map +0 -1
- package/dist/fields/CurrencyField/CurrencyField.d.ts.map +0 -1
- package/dist/fields/DateField/DateField.d.ts.map +0 -1
- package/dist/fields/DateField/DatePicker.d.ts.map +0 -1
- package/dist/fields/DateTimeField/DateTimeField.d.ts.map +0 -1
- package/dist/fields/EmailField/EmailField.d.ts.map +0 -1
- package/dist/fields/NumberField/NumberField.d.ts.map +0 -1
- package/dist/fields/PasswordField/PasswordField.d.ts.map +0 -1
- package/dist/fields/PhoneField/PhoneField.d.ts.map +0 -1
- package/dist/fields/RadioField/RadioField.d.ts.map +0 -1
- package/dist/fields/SelectField/SelectField.d.ts.map +0 -1
- package/dist/fields/SliderField/SliderField.d.ts.map +0 -1
- package/dist/fields/SwitchField/SwitchField.d.ts.map +0 -1
- package/dist/fields/TextField/TextField.d.ts.map +0 -1
- package/dist/fields/TextareaField/TextareaField.d.ts.map +0 -1
- package/dist/fields/TimeField/TimeField.d.ts.map +0 -1
- package/dist/fields/TimeField/TimePicker.d.ts.map +0 -1
- package/dist/fields/defaultComponentMap.d.ts.map +0 -1
- package/dist/fields/defaultPlaceholders.d.ts.map +0 -1
- package/dist/fields/fieldUtils.d.ts.map +0 -1
- package/dist/fields/iconUtils.d.ts.map +0 -1
- package/dist/fields/icons.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/layout/Box/Box.d.ts.map +0 -1
- package/dist/layout/Divider/Divider.d.ts.map +0 -1
- package/dist/layout/Grid/Grid.d.ts.map +0 -1
- package/dist/layout/Spacer/Spacer.d.ts.map +0 -1
- package/dist/layout/Stack/Stack.d.ts.map +0 -1
- package/dist/layout/index.d.ts.map +0 -1
- package/dist/react/AxenArrayField.d.ts.map +0 -1
- package/dist/react/AxenField.d.ts.map +0 -1
- package/dist/react/AxenForm.d.ts.map +0 -1
- package/dist/react/FormContext.d.ts.map +0 -1
- package/dist/react/useAxenForm.d.ts.map +0 -1
- package/dist/react/useField.d.ts.map +0 -1
- package/dist/react/useFieldArray.d.ts.map +0 -1
- package/dist/types.d.ts.map +0 -1
package/dist/core/formStore.js
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
import { get, set } from "./pathUtils.js";
|
|
2
|
+
function valuesEqual(a, b) {
|
|
3
|
+
if (Object.is(a, b)) return true;
|
|
4
|
+
if (a instanceof Date || b instanceof Date) return a instanceof Date && b instanceof Date && a.getTime() === b.getTime();
|
|
5
|
+
if (a === null || b === null || typeof a !== "object" || typeof b !== "object") return false;
|
|
6
|
+
if (Array.isArray(a) !== Array.isArray(b)) return false;
|
|
7
|
+
const left = a;
|
|
8
|
+
const right = b;
|
|
9
|
+
const keys = Object.keys(left);
|
|
10
|
+
if (keys.length !== Object.keys(right).length) return false;
|
|
11
|
+
return keys.every((key) => Object.prototype.hasOwnProperty.call(right, key) && valuesEqual(left[key], right[key]));
|
|
12
|
+
}
|
|
2
13
|
function createFormStore(options) {
|
|
3
14
|
const {
|
|
4
15
|
initialValues,
|
|
@@ -18,6 +29,9 @@ function createFormStore(options) {
|
|
|
18
29
|
const subscribers = /* @__PURE__ */ new Set();
|
|
19
30
|
const fieldSubscribers = /* @__PURE__ */ new Map();
|
|
20
31
|
let validationSchema = options.validationSchema;
|
|
32
|
+
let valuesRevision = 0;
|
|
33
|
+
let formRequest = 0;
|
|
34
|
+
const fieldRequests = /* @__PURE__ */ new Map();
|
|
21
35
|
if (validationAdapter && options.fields && !validationSchema) {
|
|
22
36
|
validationSchema = validationAdapter.buildSchema(options.fields);
|
|
23
37
|
}
|
|
@@ -46,16 +60,30 @@ function createFormStore(options) {
|
|
|
46
60
|
var _a;
|
|
47
61
|
(_a = fieldSubscribers.get(name)) == null ? void 0 : _a.forEach((fn) => fn());
|
|
48
62
|
}
|
|
49
|
-
function
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
};
|
|
55
|
-
notifyField(
|
|
63
|
+
function notifyAllFields() {
|
|
64
|
+
for (const name of fieldSubscribers.keys()) notifyField(name);
|
|
65
|
+
notify();
|
|
66
|
+
}
|
|
67
|
+
function notifyValuePath(path) {
|
|
68
|
+
const related = (key) => key === path || key.startsWith(`${path}.`) || key.startsWith(`${path}[`) || path.startsWith(`${key}.`) || path.startsWith(`${key}[`);
|
|
69
|
+
for (const key of fieldSubscribers.keys()) if (related(key)) notifyField(key);
|
|
56
70
|
notify();
|
|
71
|
+
}
|
|
72
|
+
function setFieldValue(name, value) {
|
|
73
|
+
const nextValues = set(state.values, name, value);
|
|
74
|
+
valuesRevision += 1;
|
|
75
|
+
state = { ...state, values: nextValues, dirty: !valuesEqual(nextValues, state.initialValues) };
|
|
76
|
+
for (const field of options.fields ?? []) {
|
|
77
|
+
if (field.hidden && state.errors[field.name] && (typeof field.hidden === "function" ? field.hidden(nextValues) : field.hidden)) {
|
|
78
|
+
setFieldError(field.name, void 0);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
notifyValuePath(name);
|
|
57
82
|
if (validateOnChange && validationAdapter && validationSchema) {
|
|
58
|
-
|
|
83
|
+
const revision = valuesRevision;
|
|
84
|
+
void runFieldValidation(name).catch((error) => {
|
|
85
|
+
if (revision === valuesRevision) setFieldError(name, error instanceof Error ? error.message : "Validation failed");
|
|
86
|
+
});
|
|
59
87
|
}
|
|
60
88
|
}
|
|
61
89
|
function setFieldTouched(name, touched = true) {
|
|
@@ -66,7 +94,10 @@ function createFormStore(options) {
|
|
|
66
94
|
notifyField(name);
|
|
67
95
|
notify();
|
|
68
96
|
if (validateOnBlur && touched && validationAdapter && validationSchema) {
|
|
69
|
-
|
|
97
|
+
const revision = valuesRevision;
|
|
98
|
+
void runFieldValidation(name).catch((error) => {
|
|
99
|
+
if (revision === valuesRevision) setFieldError(name, error instanceof Error ? error.message : "Validation failed");
|
|
100
|
+
});
|
|
70
101
|
}
|
|
71
102
|
}
|
|
72
103
|
function setFieldError(name, error) {
|
|
@@ -81,13 +112,47 @@ function createFormStore(options) {
|
|
|
81
112
|
notify();
|
|
82
113
|
}
|
|
83
114
|
function setValues(values) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
115
|
+
valuesRevision += 1;
|
|
116
|
+
state = { ...state, values, dirty: !valuesEqual(values, state.initialValues) };
|
|
117
|
+
notifyAllFields();
|
|
87
118
|
}
|
|
88
119
|
function setErrors(errors) {
|
|
89
120
|
state = { ...state, errors };
|
|
90
|
-
|
|
121
|
+
notifyAllFields();
|
|
122
|
+
}
|
|
123
|
+
function setFieldArrayValue(name, items, origins) {
|
|
124
|
+
const remap = (entries) => {
|
|
125
|
+
const result = {};
|
|
126
|
+
for (const [path, value] of Object.entries(entries)) {
|
|
127
|
+
if (!path.startsWith(name)) {
|
|
128
|
+
result[path] = value;
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const suffix = path.slice(name.length);
|
|
132
|
+
const match = /^(?:\.(\d+)|\[(\d+)\])(?=\.|\[|$)/.exec(suffix);
|
|
133
|
+
if (!match) {
|
|
134
|
+
result[path] = value;
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
const oldIndex = Number(match[1] ?? match[2]);
|
|
138
|
+
const tail = suffix.slice(match[0].length);
|
|
139
|
+
for (let i = 0; i < origins.length; i++) {
|
|
140
|
+
if (origins[i] !== oldIndex) continue;
|
|
141
|
+
result[`${name}${match[1] !== void 0 ? `.${i}` : `[${i}]`}${tail}`] = value;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return result;
|
|
145
|
+
};
|
|
146
|
+
const values = set(state.values, name, items);
|
|
147
|
+
valuesRevision += 1;
|
|
148
|
+
state = {
|
|
149
|
+
...state,
|
|
150
|
+
values,
|
|
151
|
+
dirty: !valuesEqual(values, state.initialValues),
|
|
152
|
+
errors: remap(state.errors),
|
|
153
|
+
touched: remap(state.touched)
|
|
154
|
+
};
|
|
155
|
+
notifyValuePath(name);
|
|
91
156
|
}
|
|
92
157
|
function resetForm(nextValues) {
|
|
93
158
|
const newValues = nextValues ? { ...state.initialValues, ...nextValues } : structuredClone(state.initialValues);
|
|
@@ -98,26 +163,40 @@ function createFormStore(options) {
|
|
|
98
163
|
dirty: false,
|
|
99
164
|
isSubmitting: false,
|
|
100
165
|
submitCount: state.submitCount,
|
|
101
|
-
initialValues: state.initialValues
|
|
166
|
+
initialValues: nextValues ? structuredClone(newValues) : state.initialValues
|
|
102
167
|
};
|
|
103
|
-
|
|
168
|
+
valuesRevision += 1;
|
|
169
|
+
notifyAllFields();
|
|
104
170
|
}
|
|
105
171
|
async function validateForm() {
|
|
106
172
|
if (!validationAdapter || !validationSchema) return {};
|
|
173
|
+
const revision = valuesRevision;
|
|
174
|
+
const request = ++formRequest;
|
|
107
175
|
const errors = await validationAdapter.validate(validationSchema, state.values);
|
|
176
|
+
if (revision !== valuesRevision || request !== formRequest) return state.errors;
|
|
108
177
|
state = { ...state, errors };
|
|
109
178
|
notify();
|
|
110
|
-
for (const name of fieldSubscribers.keys())
|
|
111
|
-
notifyField(name);
|
|
112
|
-
}
|
|
179
|
+
for (const name of fieldSubscribers.keys()) notifyField(name);
|
|
113
180
|
return errors;
|
|
114
181
|
}
|
|
115
182
|
async function validateField(name) {
|
|
183
|
+
var _a;
|
|
116
184
|
if (!(validationAdapter == null ? void 0 : validationAdapter.validateField) || !validationSchema) return void 0;
|
|
185
|
+
const revision = valuesRevision;
|
|
186
|
+
const request = (fieldRequests.get(name) ?? 0) + 1;
|
|
187
|
+
fieldRequests.set(name, request);
|
|
188
|
+
const config = (_a = options.fields) == null ? void 0 : _a.find((field) => field.name === name);
|
|
189
|
+
if ((config == null ? void 0 : config.hidden) && (typeof config.hidden === "function" ? config.hidden(state.values) : config.hidden)) {
|
|
190
|
+
setFieldError(name, void 0);
|
|
191
|
+
return void 0;
|
|
192
|
+
}
|
|
117
193
|
const fieldValue = get(state.values, name);
|
|
118
194
|
const error = await validationAdapter.validateField(validationSchema, name, fieldValue);
|
|
119
|
-
|
|
120
|
-
|
|
195
|
+
if (revision === valuesRevision && request === fieldRequests.get(name)) {
|
|
196
|
+
setFieldError(name, error);
|
|
197
|
+
return error;
|
|
198
|
+
}
|
|
199
|
+
return state.errors[name];
|
|
121
200
|
}
|
|
122
201
|
async function runFieldValidation(name) {
|
|
123
202
|
if (validationAdapter == null ? void 0 : validationAdapter.validateField) {
|
|
@@ -127,27 +206,18 @@ function createFormStore(options) {
|
|
|
127
206
|
}
|
|
128
207
|
}
|
|
129
208
|
async function submitForm(onSubmit) {
|
|
209
|
+
if (state.isSubmitting) return;
|
|
130
210
|
state = { ...state, isSubmitting: true, submitCount: state.submitCount + 1 };
|
|
131
211
|
notify();
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
const handler = onSubmit ?? options.onSubmit;
|
|
143
|
-
if (handler) {
|
|
144
|
-
try {
|
|
145
|
-
await handler(state.values);
|
|
146
|
-
} finally {
|
|
147
|
-
state = { ...state, isSubmitting: false };
|
|
148
|
-
notify();
|
|
149
|
-
}
|
|
150
|
-
} else {
|
|
212
|
+
try {
|
|
213
|
+
touchAllFields();
|
|
214
|
+
const revision = valuesRevision;
|
|
215
|
+
const errors = await validateForm();
|
|
216
|
+
for (const name of Object.keys(state.touched)) notifyField(name);
|
|
217
|
+
if (revision !== valuesRevision || Object.keys(errors).length > 0) return;
|
|
218
|
+
const handler = onSubmit ?? options.onSubmit;
|
|
219
|
+
if (handler) await handler(state.values);
|
|
220
|
+
} finally {
|
|
151
221
|
state = { ...state, isSubmitting: false };
|
|
152
222
|
notify();
|
|
153
223
|
}
|
|
@@ -186,6 +256,7 @@ function createFormStore(options) {
|
|
|
186
256
|
setFieldError,
|
|
187
257
|
setValues,
|
|
188
258
|
setErrors,
|
|
259
|
+
setFieldArrayValue,
|
|
189
260
|
resetForm,
|
|
190
261
|
validateForm,
|
|
191
262
|
validateField,
|
|
@@ -23,4 +23,3 @@ export declare function getGroupSeparator(locale?: string): string;
|
|
|
23
23
|
* then finds the same position in the formatted string.
|
|
24
24
|
*/
|
|
25
25
|
export declare function calculateCursorPosition(rawValue: string, formattedValue: string, cursorPos: number, locale?: string): number;
|
|
26
|
-
//# sourceMappingURL=numberUtils.d.ts.map
|
package/dist/core/pathUtils.d.ts
CHANGED
|
@@ -12,4 +12,3 @@ export declare function set<T extends Record<string, unknown>>(obj: T, path: str
|
|
|
12
12
|
export declare function toPathArray(path: string): string[];
|
|
13
13
|
/** Convert path segments back to dot-path string */
|
|
14
14
|
export declare function pathToString(segments: string[]): string;
|
|
15
|
-
//# sourceMappingURL=pathUtils.d.ts.map
|
|
@@ -19,4 +19,3 @@ export declare function pickFields<T extends Record<string, unknown>>(values: T,
|
|
|
19
19
|
* Useful for debugging / UI display.
|
|
20
20
|
*/
|
|
21
21
|
export declare function getStrippedFields<T extends Record<string, unknown>>(values: T, fieldMap: string[] | Record<string, string[]>, discriminatorKey?: string): string[];
|
|
22
|
-
//# sourceMappingURL=payloadUtils.d.ts.map
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const pathUtils = require("./pathUtils.cjs");
|
|
4
|
+
function isHidden(field, values) {
|
|
5
|
+
return typeof field.hidden === "function" ? Boolean(field.hidden(values)) : Boolean(field.hidden);
|
|
6
|
+
}
|
|
7
|
+
function validateSemanticValue(field, value, policy) {
|
|
8
|
+
var _a;
|
|
9
|
+
if (field.validation) return void 0;
|
|
10
|
+
const rules = field.rules;
|
|
11
|
+
const inferred = field.type === "tel" ? "phone" : field.type;
|
|
12
|
+
const format = (rules == null ? void 0 : rules.format) ?? ((policy == null ? void 0 : policy.mode) === "standard" && (inferred === "email" || inferred === "phone" || inferred === "url") ? inferred : void 0);
|
|
13
|
+
if (!format || value === void 0 || value === null || value === "") return void 0;
|
|
14
|
+
if (typeof value !== "string") return (rules == null ? void 0 : rules.message) ?? "Invalid value";
|
|
15
|
+
const input = value.trim();
|
|
16
|
+
if (!input) return void 0;
|
|
17
|
+
let valid = true;
|
|
18
|
+
if (format === "email") {
|
|
19
|
+
valid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/u.test(input) && input.length <= 254;
|
|
20
|
+
} else if (format === "url") {
|
|
21
|
+
try {
|
|
22
|
+
const parsed = new URL(input);
|
|
23
|
+
valid = (parsed.protocol === "http:" || parsed.protocol === "https:") && Boolean(parsed.hostname);
|
|
24
|
+
} catch {
|
|
25
|
+
valid = false;
|
|
26
|
+
}
|
|
27
|
+
} else if (format === "phone") {
|
|
28
|
+
const clean = input.replace(/[().\s-]/g, "");
|
|
29
|
+
const region = ((rules == null ? void 0 : rules.region) ?? "").toUpperCase();
|
|
30
|
+
valid = region === "ID" ? /^(?:\+62|62|0)8\d{7,11}$/u.test(clean) : /^\+[1-9]\d{7,14}$/u.test(clean);
|
|
31
|
+
} else if (format === "postalCode") {
|
|
32
|
+
const country = ((rules == null ? void 0 : rules.country) ?? "").toUpperCase();
|
|
33
|
+
const patterns = {
|
|
34
|
+
ID: /^\d{5}$/u,
|
|
35
|
+
US: /^\d{5}(?:-\d{4})?$/u,
|
|
36
|
+
SG: /^\d{6}$/u
|
|
37
|
+
};
|
|
38
|
+
valid = Boolean((_a = patterns[country]) == null ? void 0 : _a.test(input));
|
|
39
|
+
}
|
|
40
|
+
return valid ? void 0 : (rules == null ? void 0 : rules.message) ?? `Invalid ${format}`;
|
|
41
|
+
}
|
|
42
|
+
function withValidationPolicy(adapter, policy) {
|
|
43
|
+
return {
|
|
44
|
+
buildSchema(fields) {
|
|
45
|
+
return { base: adapter.buildSchema(fields), fields };
|
|
46
|
+
},
|
|
47
|
+
async validate(schema, values) {
|
|
48
|
+
const { base, fields } = schema;
|
|
49
|
+
const errors = { ...await adapter.validate(base, values) };
|
|
50
|
+
for (const field of fields) {
|
|
51
|
+
if (isHidden(field, values)) {
|
|
52
|
+
delete errors[field.name];
|
|
53
|
+
} else if (!errors[field.name]) {
|
|
54
|
+
const error = validateSemanticValue(field, pathUtils.get(values, field.name), policy);
|
|
55
|
+
if (error) errors[field.name] = error;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return errors;
|
|
59
|
+
},
|
|
60
|
+
async validateField(schema, name, value) {
|
|
61
|
+
var _a;
|
|
62
|
+
const { base, fields } = schema;
|
|
63
|
+
const error = await ((_a = adapter.validateField) == null ? void 0 : _a.call(adapter, base, name, value));
|
|
64
|
+
return error ?? validateSemanticValue(fields.find((f) => f.name === name) ?? { type: "text" }, value, policy);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
exports.validateSemanticValue = validateSemanticValue;
|
|
69
|
+
exports.withValidationPolicy = withValidationPolicy;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { FieldConfig, ValidationAdapter, ValidationPolicy } from '../types';
|
|
2
|
+
/** Optional semantics: custom Yup/Zod field schemas retain precedence. */
|
|
3
|
+
export declare function validateSemanticValue(field: FieldConfig, value: unknown, policy?: ValidationPolicy): string | undefined;
|
|
4
|
+
/** Adapter-independent policy wrapper. No mandatory Yup, Zod, or UI dependencies. */
|
|
5
|
+
export declare function withValidationPolicy(adapter: ValidationAdapter, policy?: ValidationPolicy): ValidationAdapter;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { get } from "./pathUtils.js";
|
|
2
|
+
function isHidden(field, values) {
|
|
3
|
+
return typeof field.hidden === "function" ? Boolean(field.hidden(values)) : Boolean(field.hidden);
|
|
4
|
+
}
|
|
5
|
+
function validateSemanticValue(field, value, policy) {
|
|
6
|
+
var _a;
|
|
7
|
+
if (field.validation) return void 0;
|
|
8
|
+
const rules = field.rules;
|
|
9
|
+
const inferred = field.type === "tel" ? "phone" : field.type;
|
|
10
|
+
const format = (rules == null ? void 0 : rules.format) ?? ((policy == null ? void 0 : policy.mode) === "standard" && (inferred === "email" || inferred === "phone" || inferred === "url") ? inferred : void 0);
|
|
11
|
+
if (!format || value === void 0 || value === null || value === "") return void 0;
|
|
12
|
+
if (typeof value !== "string") return (rules == null ? void 0 : rules.message) ?? "Invalid value";
|
|
13
|
+
const input = value.trim();
|
|
14
|
+
if (!input) return void 0;
|
|
15
|
+
let valid = true;
|
|
16
|
+
if (format === "email") {
|
|
17
|
+
valid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/u.test(input) && input.length <= 254;
|
|
18
|
+
} else if (format === "url") {
|
|
19
|
+
try {
|
|
20
|
+
const parsed = new URL(input);
|
|
21
|
+
valid = (parsed.protocol === "http:" || parsed.protocol === "https:") && Boolean(parsed.hostname);
|
|
22
|
+
} catch {
|
|
23
|
+
valid = false;
|
|
24
|
+
}
|
|
25
|
+
} else if (format === "phone") {
|
|
26
|
+
const clean = input.replace(/[().\s-]/g, "");
|
|
27
|
+
const region = ((rules == null ? void 0 : rules.region) ?? "").toUpperCase();
|
|
28
|
+
valid = region === "ID" ? /^(?:\+62|62|0)8\d{7,11}$/u.test(clean) : /^\+[1-9]\d{7,14}$/u.test(clean);
|
|
29
|
+
} else if (format === "postalCode") {
|
|
30
|
+
const country = ((rules == null ? void 0 : rules.country) ?? "").toUpperCase();
|
|
31
|
+
const patterns = {
|
|
32
|
+
ID: /^\d{5}$/u,
|
|
33
|
+
US: /^\d{5}(?:-\d{4})?$/u,
|
|
34
|
+
SG: /^\d{6}$/u
|
|
35
|
+
};
|
|
36
|
+
valid = Boolean((_a = patterns[country]) == null ? void 0 : _a.test(input));
|
|
37
|
+
}
|
|
38
|
+
return valid ? void 0 : (rules == null ? void 0 : rules.message) ?? `Invalid ${format}`;
|
|
39
|
+
}
|
|
40
|
+
function withValidationPolicy(adapter, policy) {
|
|
41
|
+
return {
|
|
42
|
+
buildSchema(fields) {
|
|
43
|
+
return { base: adapter.buildSchema(fields), fields };
|
|
44
|
+
},
|
|
45
|
+
async validate(schema, values) {
|
|
46
|
+
const { base, fields } = schema;
|
|
47
|
+
const errors = { ...await adapter.validate(base, values) };
|
|
48
|
+
for (const field of fields) {
|
|
49
|
+
if (isHidden(field, values)) {
|
|
50
|
+
delete errors[field.name];
|
|
51
|
+
} else if (!errors[field.name]) {
|
|
52
|
+
const error = validateSemanticValue(field, get(values, field.name), policy);
|
|
53
|
+
if (error) errors[field.name] = error;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return errors;
|
|
57
|
+
},
|
|
58
|
+
async validateField(schema, name, value) {
|
|
59
|
+
var _a;
|
|
60
|
+
const { base, fields } = schema;
|
|
61
|
+
const error = await ((_a = adapter.validateField) == null ? void 0 : _a.call(adapter, base, name, value));
|
|
62
|
+
return error ?? validateSemanticValue(fields.find((f) => f.name === name) ?? { type: "text" }, value, policy);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export {
|
|
67
|
+
validateSemanticValue,
|
|
68
|
+
withValidationPolicy
|
|
69
|
+
};
|
package/dist/core/validation.cjs
CHANGED
|
@@ -16,7 +16,7 @@ const simpleValidator = {
|
|
|
16
16
|
const isHidden = typeof rule.hidden === "function" ? rule.hidden(values) : rule.hidden;
|
|
17
17
|
if (isHidden) continue;
|
|
18
18
|
const value = getNestedValue(values, rule.name);
|
|
19
|
-
if (isEmpty(value)) {
|
|
19
|
+
if (isEmpty(value, rule.type)) {
|
|
20
20
|
errors[rule.name] = rule.message;
|
|
21
21
|
}
|
|
22
22
|
}
|
|
@@ -26,10 +26,11 @@ const simpleValidator = {
|
|
|
26
26
|
const rules = schema;
|
|
27
27
|
const rule = rules.find((r) => r.name === name);
|
|
28
28
|
if (!rule) return void 0;
|
|
29
|
-
return isEmpty(value) ? rule.message : void 0;
|
|
29
|
+
return isEmpty(value, rule.type) ? rule.message : void 0;
|
|
30
30
|
}
|
|
31
31
|
};
|
|
32
|
-
function isEmpty(value) {
|
|
32
|
+
function isEmpty(value, type) {
|
|
33
|
+
if (type === "checkbox" || type === "switch") return value !== true;
|
|
33
34
|
if (value == null) return true;
|
|
34
35
|
if (typeof value === "string") return value.trim() === "";
|
|
35
36
|
if (Array.isArray(value)) return value.length === 0;
|
package/dist/core/validation.js
CHANGED
|
@@ -14,7 +14,7 @@ const simpleValidator = {
|
|
|
14
14
|
const isHidden = typeof rule.hidden === "function" ? rule.hidden(values) : rule.hidden;
|
|
15
15
|
if (isHidden) continue;
|
|
16
16
|
const value = getNestedValue(values, rule.name);
|
|
17
|
-
if (isEmpty(value)) {
|
|
17
|
+
if (isEmpty(value, rule.type)) {
|
|
18
18
|
errors[rule.name] = rule.message;
|
|
19
19
|
}
|
|
20
20
|
}
|
|
@@ -24,10 +24,11 @@ const simpleValidator = {
|
|
|
24
24
|
const rules = schema;
|
|
25
25
|
const rule = rules.find((r) => r.name === name);
|
|
26
26
|
if (!rule) return void 0;
|
|
27
|
-
return isEmpty(value) ? rule.message : void 0;
|
|
27
|
+
return isEmpty(value, rule.type) ? rule.message : void 0;
|
|
28
28
|
}
|
|
29
29
|
};
|
|
30
|
-
function isEmpty(value) {
|
|
30
|
+
function isEmpty(value, type) {
|
|
31
|
+
if (type === "checkbox" || type === "switch") return value !== true;
|
|
31
32
|
if (value == null) return true;
|
|
32
33
|
if (typeof value === "string") return value.trim() === "";
|
|
33
34
|
if (Array.isArray(value)) return value.length === 0;
|
|
@@ -2,5 +2,4 @@ import { AutocompleteOption, FieldComponentProps } from '../../types';
|
|
|
2
2
|
export declare function AutocompleteField({ name, value, onChange: _onChange, onBlur, error, helperText, label, placeholder, disabled, readOnly, required, options: staticOptions, fetchOptions, optionLabel, icon, form, }: FieldComponentProps & {
|
|
3
3
|
fetchOptions?: (query: string, signal?: AbortSignal) => Promise<AutocompleteOption[]>;
|
|
4
4
|
optionLabel?: string;
|
|
5
|
-
}): import("react
|
|
6
|
-
//# sourceMappingURL=AutocompleteField.d.ts.map
|
|
5
|
+
}): import("react").JSX.Element;
|
|
@@ -2,5 +2,4 @@ import { AutocompleteOption, FieldComponentProps } from '../../types';
|
|
|
2
2
|
export declare function AutocompleteMultiField({ name, value, onChange: _onChange, onBlur, error, helperText, label, placeholder, disabled, readOnly, required, options: staticOptions, fetchOptions, optionLabel, icon, form, }: FieldComponentProps & {
|
|
3
3
|
fetchOptions?: (query: string, signal?: AbortSignal) => Promise<AutocompleteOption[]>;
|
|
4
4
|
optionLabel?: string;
|
|
5
|
-
}): import("react
|
|
6
|
-
//# sourceMappingURL=AutocompleteMultiField.d.ts.map
|
|
5
|
+
}): import("react").JSX.Element;
|
|
@@ -6,5 +6,4 @@ import { AutocompleteOption, FieldComponentProps } from '../../types';
|
|
|
6
6
|
export declare function AutocompletePredictField({ name, value, onChange: _onChange, onBlur, error, helperText, label, placeholder, disabled, readOnly, required, options: staticOptions, fetchOptions, optionLabel, icon, form, }: FieldComponentProps & {
|
|
7
7
|
fetchOptions?: (query: string, signal?: AbortSignal) => Promise<AutocompleteOption[]>;
|
|
8
8
|
optionLabel?: string;
|
|
9
|
-
}): import("react
|
|
10
|
-
//# sourceMappingURL=AutocompletePredictField.d.ts.map
|
|
9
|
+
}): import("react").JSX.Element;
|
|
@@ -1,3 +1,2 @@
|
|
|
1
1
|
import { FieldComponentProps } from '../../types';
|
|
2
|
-
export declare function CheckboxField({ name, value, onChange: _onChange, onBlur, error, helperText, label, disabled, readOnly, required, form, }: Readonly<FieldComponentProps>): import("react
|
|
3
|
-
//# sourceMappingURL=CheckboxField.d.ts.map
|
|
2
|
+
export declare function CheckboxField({ name, value, onChange: _onChange, onBlur, error, helperText, label, disabled, readOnly, required, form, }: Readonly<FieldComponentProps>): import("react").JSX.Element;
|
|
@@ -1,3 +1,2 @@
|
|
|
1
1
|
import { FieldComponentProps } from '../../types';
|
|
2
|
-
export declare function ColorField({ name, value, onChange: _onChange, onBlur, error, helperText, label, disabled, readOnly, required, form, }: Readonly<FieldComponentProps>): import("react
|
|
3
|
-
//# sourceMappingURL=ColorField.d.ts.map
|
|
2
|
+
export declare function ColorField({ name, value, onChange: _onChange, onBlur, error, helperText, label, disabled, readOnly, required, form, }: Readonly<FieldComponentProps>): import("react").JSX.Element;
|
|
@@ -6,6 +6,5 @@ interface DatePickerProps {
|
|
|
6
6
|
onApply: (isoDate: string) => void;
|
|
7
7
|
onClose: () => void;
|
|
8
8
|
}
|
|
9
|
-
export declare function DatePicker({ value, min, max, pickerId, onApply, onClose, }: Readonly<DatePickerProps>): import("react
|
|
9
|
+
export declare function DatePicker({ value, min, max, pickerId, onApply, onClose, }: Readonly<DatePickerProps>): import("react").JSX.Element;
|
|
10
10
|
export {};
|
|
11
|
-
//# sourceMappingURL=DatePicker.d.ts.map
|
|
@@ -5,5 +5,4 @@ export declare function DateTimeField({ name, value, onChange, onBlur, error, he
|
|
|
5
5
|
locale?: string;
|
|
6
6
|
ampm?: boolean;
|
|
7
7
|
showSeconds?: boolean;
|
|
8
|
-
}>): import("react
|
|
9
|
-
//# sourceMappingURL=DateTimeField.d.ts.map
|
|
8
|
+
}>): import("react").JSX.Element;
|
|
@@ -1,3 +1,2 @@
|
|
|
1
1
|
import { FieldComponentProps } from '../../types';
|
|
2
|
-
export declare function EmailField({ name, value, onChange, onBlur, error, helperText, label, placeholder, disabled, readOnly, required, icon, }: Readonly<FieldComponentProps>): import("react
|
|
3
|
-
//# sourceMappingURL=EmailField.d.ts.map
|
|
2
|
+
export declare function EmailField({ name, value, onChange, onBlur, error, helperText, label, placeholder, disabled, readOnly, required, icon, }: Readonly<FieldComponentProps>): import("react").JSX.Element;
|
|
@@ -1,3 +1,2 @@
|
|
|
1
1
|
import { FieldComponentProps } from '../../types';
|
|
2
|
-
export declare function PasswordField({ name, value, onChange, onBlur, error, helperText, label, placeholder, disabled, readOnly, required, icon, }: Readonly<FieldComponentProps>): import("react
|
|
3
|
-
//# sourceMappingURL=PasswordField.d.ts.map
|
|
2
|
+
export declare function PasswordField({ name, value, onChange, onBlur, error, helperText, label, placeholder, disabled, readOnly, required, icon, }: Readonly<FieldComponentProps>): import("react").JSX.Element;
|
|
@@ -1,3 +1,2 @@
|
|
|
1
1
|
import { FieldComponentProps } from '../../types';
|
|
2
|
-
export declare function PhoneField({ name, value, onChange, onBlur, error, helperText, label, placeholder, disabled, readOnly, required, icon, }: Readonly<FieldComponentProps>): import("react
|
|
3
|
-
//# sourceMappingURL=PhoneField.d.ts.map
|
|
2
|
+
export declare function PhoneField({ name, value, onChange, onBlur, error, helperText, label, placeholder, disabled, readOnly, required, icon, }: Readonly<FieldComponentProps>): import("react").JSX.Element;
|
|
@@ -1,3 +1,2 @@
|
|
|
1
1
|
import { FieldComponentProps } from '../../types';
|
|
2
|
-
export declare function RadioField({ name, value, onChange: _onChange, onBlur, error, helperText, label, disabled, readOnly, required, options, form, }: Readonly<FieldComponentProps>): import("react
|
|
3
|
-
//# sourceMappingURL=RadioField.d.ts.map
|
|
2
|
+
export declare function RadioField({ name, value, onChange: _onChange, onBlur, error, helperText, label, disabled, readOnly, required, options, form, }: Readonly<FieldComponentProps>): import("react").JSX.Element;
|
|
@@ -1,3 +1,2 @@
|
|
|
1
1
|
import { FieldComponentProps } from '../../types';
|
|
2
|
-
export declare function SelectField({ name, value, onChange, onBlur, error, helperText, label, placeholder, disabled, readOnly, required, options, }: Readonly<FieldComponentProps>): import("react
|
|
3
|
-
//# sourceMappingURL=SelectField.d.ts.map
|
|
2
|
+
export declare function SelectField({ name, value, onChange, onBlur, error, helperText, label, placeholder, disabled, readOnly, required, options, }: Readonly<FieldComponentProps>): import("react").JSX.Element;
|
|
@@ -1,3 +1,2 @@
|
|
|
1
1
|
import { FieldComponentProps } from '../../types';
|
|
2
|
-
export declare function SwitchField({ name, value, onChange: _onChange, onBlur, error, helperText, label, disabled, readOnly, required, form, }: Readonly<FieldComponentProps>): import("react
|
|
3
|
-
//# sourceMappingURL=SwitchField.d.ts.map
|
|
2
|
+
export declare function SwitchField({ name, value, onChange: _onChange, onBlur, error, helperText, label, disabled, readOnly, required, form, }: Readonly<FieldComponentProps>): import("react").JSX.Element;
|
|
@@ -1,3 +1,2 @@
|
|
|
1
1
|
import { FieldComponentProps } from '../../types';
|
|
2
|
-
export declare function TextField({ name, value, onChange, onBlur, error, helperText, label, placeholder, disabled, readOnly, required, icon, }: Readonly<FieldComponentProps>): import("react
|
|
3
|
-
//# sourceMappingURL=TextField.d.ts.map
|
|
2
|
+
export declare function TextField({ name, value, onChange, onBlur, error, helperText, label, placeholder, disabled, readOnly, required, icon, }: Readonly<FieldComponentProps>): import("react").JSX.Element;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { FieldComponentProps } from '../../types';
|
|
2
2
|
export declare function TextareaField({ name, value, onChange, onBlur, error, helperText, label, placeholder, disabled, readOnly, required, rows, }: FieldComponentProps & {
|
|
3
3
|
rows?: number;
|
|
4
|
-
}): import("react
|
|
5
|
-
//# sourceMappingURL=TextareaField.d.ts.map
|
|
4
|
+
}): import("react").JSX.Element;
|
|
@@ -6,6 +6,5 @@ interface TimePickerProps {
|
|
|
6
6
|
onApply: (isoTime: string) => void;
|
|
7
7
|
onClose: () => void;
|
|
8
8
|
}
|
|
9
|
-
export declare function TimePicker({ value, showSeconds, ampm, pickerId, onApply, onClose, }: Readonly<TimePickerProps>): import("react
|
|
9
|
+
export declare function TimePicker({ value, showSeconds, ampm, pickerId, onApply, onClose, }: Readonly<TimePickerProps>): import("react").JSX.Element;
|
|
10
10
|
export {};
|
|
11
|
-
//# sourceMappingURL=TimePicker.d.ts.map
|
|
@@ -24,6 +24,9 @@ const defaultComponentMap = {
|
|
|
24
24
|
email: EmailField.EmailField,
|
|
25
25
|
password: PasswordField.PasswordField,
|
|
26
26
|
phone: PhoneField.PhoneField,
|
|
27
|
+
// HTML type aliases retained for existing consumer configurations.
|
|
28
|
+
tel: PhoneField.PhoneField,
|
|
29
|
+
url: TextField.TextField,
|
|
27
30
|
textarea: TextareaField.TextareaField,
|
|
28
31
|
number: NumberField.NumberField,
|
|
29
32
|
currency: CurrencyField.CurrencyField,
|
|
@@ -22,6 +22,9 @@ const defaultComponentMap = {
|
|
|
22
22
|
email: EmailField,
|
|
23
23
|
password: PasswordField,
|
|
24
24
|
phone: PhoneField,
|
|
25
|
+
// HTML type aliases retained for existing consumer configurations.
|
|
26
|
+
tel: PhoneField,
|
|
27
|
+
url: TextField,
|
|
25
28
|
textarea: TextareaField,
|
|
26
29
|
number: NumberField,
|
|
27
30
|
currency: CurrencyField,
|