@coherent.js/forms 1.0.0-beta.8 → 1.0.0-rc.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/dist/form-builder.js +0 -5
- package/dist/form-builder.js.map +2 -2
- package/dist/index.js +14 -617
- package/dist/index.js.map +4 -4
- package/package.json +5 -7
- package/types/index.d.ts +3 -50
- package/dist/advanced-validation.js +0 -523
- package/dist/advanced-validation.js.map +0 -7
- package/dist/forms.js +0 -432
- package/dist/forms.js.map +0 -7
package/dist/index.js
CHANGED
|
@@ -458,7 +458,13 @@ var FormBuilder = class {
|
|
|
458
458
|
}
|
|
459
459
|
};
|
|
460
460
|
function createFormBuilder(options = {}) {
|
|
461
|
-
|
|
461
|
+
const form = new FormBuilder(options);
|
|
462
|
+
if (options.fields) {
|
|
463
|
+
options.fields.forEach((fieldConfig) => {
|
|
464
|
+
form.addField(fieldConfig.name, fieldConfig);
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
return form;
|
|
462
468
|
}
|
|
463
469
|
function buildForm(fields, options = {}) {
|
|
464
470
|
const builder = new FormBuilder(options);
|
|
@@ -941,8 +947,8 @@ function hydrateForm(formSelector, options = {}) {
|
|
|
941
947
|
}
|
|
942
948
|
return isValid;
|
|
943
949
|
}
|
|
944
|
-
function handleChange(
|
|
945
|
-
const input =
|
|
950
|
+
function handleChange(event) {
|
|
951
|
+
const input = event.target;
|
|
946
952
|
const name = input.getAttribute("name");
|
|
947
953
|
if (!state.fields.has(name)) return;
|
|
948
954
|
state.values[name] = getFieldValue(input);
|
|
@@ -957,8 +963,8 @@ function hydrateForm(formSelector, options = {}) {
|
|
|
957
963
|
debounceTimers.set(name, timer);
|
|
958
964
|
}
|
|
959
965
|
}
|
|
960
|
-
function handleBlur(
|
|
961
|
-
const input =
|
|
966
|
+
function handleBlur(event) {
|
|
967
|
+
const input = event.target;
|
|
962
968
|
const name = input.getAttribute("name");
|
|
963
969
|
if (!state.fields.has(name)) return;
|
|
964
970
|
state.touched[name] = true;
|
|
@@ -966,8 +972,8 @@ function hydrateForm(formSelector, options = {}) {
|
|
|
966
972
|
validateField2(name);
|
|
967
973
|
}
|
|
968
974
|
}
|
|
969
|
-
function handleSubmit(
|
|
970
|
-
|
|
975
|
+
function handleSubmit(event) {
|
|
976
|
+
event.preventDefault();
|
|
971
977
|
for (const name of state.fields.keys()) {
|
|
972
978
|
state.touched[name] = true;
|
|
973
979
|
}
|
|
@@ -985,7 +991,7 @@ function hydrateForm(formSelector, options = {}) {
|
|
|
985
991
|
state.isSubmitting = true;
|
|
986
992
|
const submitData = { ...state.values };
|
|
987
993
|
if (options.onSubmit) {
|
|
988
|
-
const result = options.onSubmit(submitData,
|
|
994
|
+
const result = options.onSubmit(submitData, event);
|
|
989
995
|
if (result === false) {
|
|
990
996
|
state.isSubmitting = false;
|
|
991
997
|
return;
|
|
@@ -1234,627 +1240,18 @@ function validate(formData, schema) {
|
|
|
1234
1240
|
const validator = new FormValidator(schema);
|
|
1235
1241
|
return validator.validate(formData);
|
|
1236
1242
|
}
|
|
1237
|
-
|
|
1238
|
-
// src/forms.js
|
|
1239
|
-
function createForm(options = {}) {
|
|
1240
|
-
const opts = {
|
|
1241
|
-
fields: {},
|
|
1242
|
-
validation: {
|
|
1243
|
-
strategy: "blur",
|
|
1244
|
-
debounce: 300,
|
|
1245
|
-
async: true,
|
|
1246
|
-
stopOnFirstError: false,
|
|
1247
|
-
revalidateOn: ["change", "blur"],
|
|
1248
|
-
...options.validation
|
|
1249
|
-
},
|
|
1250
|
-
errors: {
|
|
1251
|
-
format: "detailed",
|
|
1252
|
-
display: "inline",
|
|
1253
|
-
customFormatter: null,
|
|
1254
|
-
...options.errors
|
|
1255
|
-
},
|
|
1256
|
-
submission: {
|
|
1257
|
-
preventDefault: true,
|
|
1258
|
-
validateBeforeSubmit: true,
|
|
1259
|
-
disableOnSubmit: true,
|
|
1260
|
-
resetOnSuccess: false,
|
|
1261
|
-
onSuccess: null,
|
|
1262
|
-
onError: null,
|
|
1263
|
-
...options.submission
|
|
1264
|
-
},
|
|
1265
|
-
state: {
|
|
1266
|
-
trackDirty: true,
|
|
1267
|
-
trackTouched: true,
|
|
1268
|
-
trackVisited: true,
|
|
1269
|
-
initialValues: {},
|
|
1270
|
-
resetValues: null,
|
|
1271
|
-
...options.state
|
|
1272
|
-
},
|
|
1273
|
-
middleware: options.middleware || [],
|
|
1274
|
-
...options
|
|
1275
|
-
};
|
|
1276
|
-
const state = {
|
|
1277
|
-
values: { ...opts.state.initialValues },
|
|
1278
|
-
errors: {},
|
|
1279
|
-
touched: {},
|
|
1280
|
-
dirty: {},
|
|
1281
|
-
visited: {},
|
|
1282
|
-
isSubmitting: false,
|
|
1283
|
-
isValidating: false,
|
|
1284
|
-
submitCount: 0,
|
|
1285
|
-
asyncValidations: /* @__PURE__ */ new Map()
|
|
1286
|
-
};
|
|
1287
|
-
const fields = /* @__PURE__ */ new Map();
|
|
1288
|
-
const stats = {
|
|
1289
|
-
validations: 0,
|
|
1290
|
-
asyncValidations: 0,
|
|
1291
|
-
submissions: 0,
|
|
1292
|
-
successfulSubmissions: 0,
|
|
1293
|
-
failedSubmissions: 0,
|
|
1294
|
-
middlewareExecutions: 0
|
|
1295
|
-
};
|
|
1296
|
-
function registerField(name, config) {
|
|
1297
|
-
fields.set(name, {
|
|
1298
|
-
name,
|
|
1299
|
-
type: config.type || "text",
|
|
1300
|
-
validators: config.validators || [],
|
|
1301
|
-
transform: config.transform || {},
|
|
1302
|
-
validateWhen: config.validateWhen,
|
|
1303
|
-
required: config.required || false,
|
|
1304
|
-
defaultValue: config.defaultValue,
|
|
1305
|
-
...config
|
|
1306
|
-
});
|
|
1307
|
-
if (!(name in state.values)) {
|
|
1308
|
-
state.values[name] = config.defaultValue !== void 0 ? config.defaultValue : "";
|
|
1309
|
-
}
|
|
1310
|
-
state.errors[name] = [];
|
|
1311
|
-
state.touched[name] = false;
|
|
1312
|
-
state.dirty[name] = false;
|
|
1313
|
-
state.visited[name] = false;
|
|
1314
|
-
}
|
|
1315
|
-
function getField(name) {
|
|
1316
|
-
return fields.get(name);
|
|
1317
|
-
}
|
|
1318
|
-
function setFieldValue(name, value, shouldValidate = true) {
|
|
1319
|
-
const field = fields.get(name);
|
|
1320
|
-
if (!field) {
|
|
1321
|
-
console.warn(`Field ${name} not registered`);
|
|
1322
|
-
return;
|
|
1323
|
-
}
|
|
1324
|
-
if (field.transform?.input) {
|
|
1325
|
-
value = field.transform.input(value);
|
|
1326
|
-
}
|
|
1327
|
-
state.values[name] = value;
|
|
1328
|
-
if (opts.state.trackDirty) {
|
|
1329
|
-
const initialValue = opts.state.initialValues[name];
|
|
1330
|
-
state.dirty[name] = value !== initialValue;
|
|
1331
|
-
}
|
|
1332
|
-
if (shouldValidate && opts.validation.revalidateOn.includes("change")) {
|
|
1333
|
-
validateField2(name);
|
|
1334
|
-
}
|
|
1335
|
-
}
|
|
1336
|
-
function getFieldValue(name) {
|
|
1337
|
-
return state.values[name];
|
|
1338
|
-
}
|
|
1339
|
-
function setFieldTouched(name, touched = true) {
|
|
1340
|
-
if (!opts.state.trackTouched) return;
|
|
1341
|
-
state.touched[name] = touched;
|
|
1342
|
-
if (touched && opts.validation.revalidateOn.includes("blur")) {
|
|
1343
|
-
validateField2(name);
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
function setFieldVisited(name, visited = true) {
|
|
1347
|
-
if (!opts.state.trackVisited) return;
|
|
1348
|
-
state.visited[name] = visited;
|
|
1349
|
-
}
|
|
1350
|
-
async function validateField2(name) {
|
|
1351
|
-
const field = fields.get(name);
|
|
1352
|
-
if (!field) return { valid: true, errors: [] };
|
|
1353
|
-
stats.validations++;
|
|
1354
|
-
if (field.validateWhen && !field.validateWhen(state.values)) {
|
|
1355
|
-
state.errors[name] = [];
|
|
1356
|
-
return { valid: true, errors: [] };
|
|
1357
|
-
}
|
|
1358
|
-
const value = state.values[name];
|
|
1359
|
-
const errors = [];
|
|
1360
|
-
if (state.asyncValidations.has(name)) {
|
|
1361
|
-
clearTimeout(state.asyncValidations.get(name));
|
|
1362
|
-
}
|
|
1363
|
-
if (field.required && (value === "" || value === null || value === void 0)) {
|
|
1364
|
-
errors.push({
|
|
1365
|
-
field: name,
|
|
1366
|
-
type: "required",
|
|
1367
|
-
message: `${name} is required`
|
|
1368
|
-
});
|
|
1369
|
-
state.errors[name] = errors;
|
|
1370
|
-
return { valid: false, errors };
|
|
1371
|
-
}
|
|
1372
|
-
for (const validator of field.validators) {
|
|
1373
|
-
try {
|
|
1374
|
-
let result;
|
|
1375
|
-
if (validator.validate.constructor.name === "AsyncFunction" || opts.validation.async) {
|
|
1376
|
-
stats.asyncValidations++;
|
|
1377
|
-
if (validator.debounce || opts.validation.debounce) {
|
|
1378
|
-
await new Promise((resolve) => {
|
|
1379
|
-
const timeoutId = setTimeout(resolve, validator.debounce || opts.validation.debounce);
|
|
1380
|
-
state.asyncValidations.set(name, timeoutId);
|
|
1381
|
-
});
|
|
1382
|
-
}
|
|
1383
|
-
result = await validator.validate(value, state.values);
|
|
1384
|
-
} else {
|
|
1385
|
-
result = validator.validate(value, state.values);
|
|
1386
|
-
}
|
|
1387
|
-
if (result !== true && result !== void 0 && result !== null) {
|
|
1388
|
-
errors.push({
|
|
1389
|
-
field: name,
|
|
1390
|
-
type: validator.name || "custom",
|
|
1391
|
-
message: validator.message || result || "Validation failed"
|
|
1392
|
-
});
|
|
1393
|
-
if (opts.validation.stopOnFirstError) {
|
|
1394
|
-
break;
|
|
1395
|
-
}
|
|
1396
|
-
}
|
|
1397
|
-
} catch (error) {
|
|
1398
|
-
errors.push({
|
|
1399
|
-
field: name,
|
|
1400
|
-
type: "error",
|
|
1401
|
-
message: error.message || "Validation error"
|
|
1402
|
-
});
|
|
1403
|
-
if (opts.validation.stopOnFirstError) {
|
|
1404
|
-
break;
|
|
1405
|
-
}
|
|
1406
|
-
}
|
|
1407
|
-
}
|
|
1408
|
-
state.errors[name] = errors;
|
|
1409
|
-
return { valid: errors.length === 0, errors };
|
|
1410
|
-
}
|
|
1411
|
-
async function validateForm2() {
|
|
1412
|
-
state.isValidating = true;
|
|
1413
|
-
const validationPromises = Array.from(fields.keys()).map(
|
|
1414
|
-
(name) => validateField2(name)
|
|
1415
|
-
);
|
|
1416
|
-
const results = await Promise.all(validationPromises);
|
|
1417
|
-
state.isValidating = false;
|
|
1418
|
-
const allErrors = results.reduce((acc, result, index) => {
|
|
1419
|
-
const fieldName = Array.from(fields.keys())[index];
|
|
1420
|
-
if (result.errors.length > 0) {
|
|
1421
|
-
acc[fieldName] = result.errors;
|
|
1422
|
-
}
|
|
1423
|
-
return acc;
|
|
1424
|
-
}, {});
|
|
1425
|
-
const isValid2 = Object.keys(allErrors).length === 0;
|
|
1426
|
-
return {
|
|
1427
|
-
valid: isValid2,
|
|
1428
|
-
errors: allErrors
|
|
1429
|
-
};
|
|
1430
|
-
}
|
|
1431
|
-
async function executeMiddleware(action, data) {
|
|
1432
|
-
if (opts.middleware.length === 0) return data;
|
|
1433
|
-
stats.middlewareExecutions++;
|
|
1434
|
-
let result = data;
|
|
1435
|
-
for (const middleware of opts.middleware) {
|
|
1436
|
-
try {
|
|
1437
|
-
const next = () => result;
|
|
1438
|
-
result = await middleware(action, result, next, state);
|
|
1439
|
-
} catch (error) {
|
|
1440
|
-
console.error("Middleware error:", error);
|
|
1441
|
-
throw error;
|
|
1442
|
-
}
|
|
1443
|
-
}
|
|
1444
|
-
return result;
|
|
1445
|
-
}
|
|
1446
|
-
function applyTransformations(values) {
|
|
1447
|
-
const transformed = { ...values };
|
|
1448
|
-
fields.forEach((field, name) => {
|
|
1449
|
-
if (field.transform?.output && name in transformed) {
|
|
1450
|
-
transformed[name] = field.transform.output(transformed[name]);
|
|
1451
|
-
}
|
|
1452
|
-
});
|
|
1453
|
-
return transformed;
|
|
1454
|
-
}
|
|
1455
|
-
async function handleSubmit(onSubmit) {
|
|
1456
|
-
stats.submissions++;
|
|
1457
|
-
try {
|
|
1458
|
-
if (opts.submission.preventDefault && typeof event !== "undefined") {
|
|
1459
|
-
event.preventDefault();
|
|
1460
|
-
}
|
|
1461
|
-
if (opts.submission.disableOnSubmit) {
|
|
1462
|
-
state.isSubmitting = true;
|
|
1463
|
-
}
|
|
1464
|
-
let values = { ...state.values };
|
|
1465
|
-
values = await executeMiddleware("beforeSubmit", values);
|
|
1466
|
-
if (opts.submission.validateBeforeSubmit) {
|
|
1467
|
-
const validation = await validateForm2();
|
|
1468
|
-
if (!validation.valid) {
|
|
1469
|
-
if (opts.submission.onError) {
|
|
1470
|
-
opts.submission.onError(validation.errors);
|
|
1471
|
-
}
|
|
1472
|
-
stats.failedSubmissions++;
|
|
1473
|
-
return { success: false, errors: validation.errors };
|
|
1474
|
-
}
|
|
1475
|
-
}
|
|
1476
|
-
values = applyTransformations(values);
|
|
1477
|
-
values = await executeMiddleware("afterValidation", values);
|
|
1478
|
-
const result = await onSubmit(values);
|
|
1479
|
-
await executeMiddleware("afterSubmit", result);
|
|
1480
|
-
state.submitCount++;
|
|
1481
|
-
stats.successfulSubmissions++;
|
|
1482
|
-
if (opts.submission.resetOnSuccess) {
|
|
1483
|
-
reset();
|
|
1484
|
-
}
|
|
1485
|
-
if (opts.submission.onSuccess) {
|
|
1486
|
-
opts.submission.onSuccess(result);
|
|
1487
|
-
}
|
|
1488
|
-
return { success: true, data: result };
|
|
1489
|
-
} catch (error) {
|
|
1490
|
-
stats.failedSubmissions++;
|
|
1491
|
-
if (opts.submission.onError) {
|
|
1492
|
-
opts.submission.onError(error);
|
|
1493
|
-
}
|
|
1494
|
-
await executeMiddleware("onError", error);
|
|
1495
|
-
return { success: false, error };
|
|
1496
|
-
} finally {
|
|
1497
|
-
state.isSubmitting = false;
|
|
1498
|
-
}
|
|
1499
|
-
}
|
|
1500
|
-
function reset(values) {
|
|
1501
|
-
const resetValues = values || opts.state.resetValues || opts.state.initialValues;
|
|
1502
|
-
state.values = { ...resetValues };
|
|
1503
|
-
state.errors = {};
|
|
1504
|
-
state.touched = {};
|
|
1505
|
-
state.dirty = {};
|
|
1506
|
-
state.visited = {};
|
|
1507
|
-
state.submitCount = 0;
|
|
1508
|
-
fields.forEach((field, name) => {
|
|
1509
|
-
if (!(name in state.values)) {
|
|
1510
|
-
state.values[name] = field.defaultValue !== void 0 ? field.defaultValue : "";
|
|
1511
|
-
}
|
|
1512
|
-
state.errors[name] = [];
|
|
1513
|
-
state.touched[name] = false;
|
|
1514
|
-
state.dirty[name] = false;
|
|
1515
|
-
state.visited[name] = false;
|
|
1516
|
-
});
|
|
1517
|
-
}
|
|
1518
|
-
function getErrors(fieldName) {
|
|
1519
|
-
if (fieldName) {
|
|
1520
|
-
return state.errors[fieldName] || [];
|
|
1521
|
-
}
|
|
1522
|
-
if (opts.errors.customFormatter) {
|
|
1523
|
-
return opts.errors.customFormatter(state.errors);
|
|
1524
|
-
}
|
|
1525
|
-
if (opts.errors.format === "simple") {
|
|
1526
|
-
return Object.values(state.errors).flat().map((e) => e.message);
|
|
1527
|
-
}
|
|
1528
|
-
return state.errors;
|
|
1529
|
-
}
|
|
1530
|
-
function isValid() {
|
|
1531
|
-
return Object.values(state.errors).every((errors) => errors.length === 0);
|
|
1532
|
-
}
|
|
1533
|
-
function isDirty(fieldName) {
|
|
1534
|
-
if (fieldName) {
|
|
1535
|
-
return state.dirty[fieldName] || false;
|
|
1536
|
-
}
|
|
1537
|
-
return Object.values(state.dirty).some((dirty) => dirty);
|
|
1538
|
-
}
|
|
1539
|
-
function isTouched(fieldName) {
|
|
1540
|
-
if (fieldName) {
|
|
1541
|
-
return state.touched[fieldName] || false;
|
|
1542
|
-
}
|
|
1543
|
-
return Object.values(state.touched).some((touched) => touched);
|
|
1544
|
-
}
|
|
1545
|
-
function getValues() {
|
|
1546
|
-
return { ...state.values };
|
|
1547
|
-
}
|
|
1548
|
-
function setValues(values, shouldValidate = false) {
|
|
1549
|
-
Object.entries(values).forEach(([name, value]) => {
|
|
1550
|
-
setFieldValue(name, value, shouldValidate);
|
|
1551
|
-
});
|
|
1552
|
-
}
|
|
1553
|
-
function getState() {
|
|
1554
|
-
return {
|
|
1555
|
-
values: { ...state.values },
|
|
1556
|
-
errors: { ...state.errors },
|
|
1557
|
-
touched: { ...state.touched },
|
|
1558
|
-
dirty: { ...state.dirty },
|
|
1559
|
-
visited: { ...state.visited },
|
|
1560
|
-
isSubmitting: state.isSubmitting,
|
|
1561
|
-
isValidating: state.isValidating,
|
|
1562
|
-
submitCount: state.submitCount,
|
|
1563
|
-
isValid: isValid(),
|
|
1564
|
-
isDirty: isDirty(),
|
|
1565
|
-
isTouched: isTouched()
|
|
1566
|
-
};
|
|
1567
|
-
}
|
|
1568
|
-
function getStats() {
|
|
1569
|
-
return {
|
|
1570
|
-
...stats,
|
|
1571
|
-
fieldsRegistered: fields.size,
|
|
1572
|
-
activeAsyncValidations: state.asyncValidations.size
|
|
1573
|
-
};
|
|
1574
|
-
}
|
|
1575
|
-
Object.entries(opts.fields).forEach(([name, config]) => {
|
|
1576
|
-
registerField(name, config);
|
|
1577
|
-
});
|
|
1578
|
-
return {
|
|
1579
|
-
registerField,
|
|
1580
|
-
getField,
|
|
1581
|
-
setFieldValue,
|
|
1582
|
-
getFieldValue,
|
|
1583
|
-
setFieldTouched,
|
|
1584
|
-
setFieldVisited,
|
|
1585
|
-
validateField: validateField2,
|
|
1586
|
-
validateForm: validateForm2,
|
|
1587
|
-
handleSubmit,
|
|
1588
|
-
reset,
|
|
1589
|
-
getErrors,
|
|
1590
|
-
isValid,
|
|
1591
|
-
isDirty,
|
|
1592
|
-
isTouched,
|
|
1593
|
-
getValues,
|
|
1594
|
-
setValues,
|
|
1595
|
-
getState,
|
|
1596
|
-
getStats,
|
|
1597
|
-
// Expose state for testing
|
|
1598
|
-
_state: state
|
|
1599
|
-
};
|
|
1600
|
-
}
|
|
1601
|
-
var formValidators = {
|
|
1602
|
-
required: {
|
|
1603
|
-
name: "required",
|
|
1604
|
-
validate: (value) => {
|
|
1605
|
-
return value !== "" && value !== null && value !== void 0;
|
|
1606
|
-
},
|
|
1607
|
-
message: "This field is required"
|
|
1608
|
-
},
|
|
1609
|
-
email: {
|
|
1610
|
-
name: "email",
|
|
1611
|
-
validate: (value) => {
|
|
1612
|
-
if (!value) return true;
|
|
1613
|
-
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
1614
|
-
return regex.test(value);
|
|
1615
|
-
},
|
|
1616
|
-
message: "Please enter a valid email address"
|
|
1617
|
-
},
|
|
1618
|
-
minLength: (min) => ({
|
|
1619
|
-
name: "minLength",
|
|
1620
|
-
validate: (value) => {
|
|
1621
|
-
if (!value) return true;
|
|
1622
|
-
return String(value).length >= min;
|
|
1623
|
-
},
|
|
1624
|
-
message: `Must be at least ${min} characters`
|
|
1625
|
-
}),
|
|
1626
|
-
maxLength: (max) => ({
|
|
1627
|
-
name: "maxLength",
|
|
1628
|
-
validate: (value) => {
|
|
1629
|
-
if (!value) return true;
|
|
1630
|
-
return String(value).length <= max;
|
|
1631
|
-
},
|
|
1632
|
-
message: `Must be no more than ${max} characters`
|
|
1633
|
-
}),
|
|
1634
|
-
pattern: (regex, message) => ({
|
|
1635
|
-
name: "pattern",
|
|
1636
|
-
validate: (value) => {
|
|
1637
|
-
if (!value) return true;
|
|
1638
|
-
return regex.test(value);
|
|
1639
|
-
},
|
|
1640
|
-
message: message || "Invalid format"
|
|
1641
|
-
}),
|
|
1642
|
-
asyncEmail: {
|
|
1643
|
-
name: "asyncEmail",
|
|
1644
|
-
validate: async (value) => {
|
|
1645
|
-
if (!value) return true;
|
|
1646
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
1647
|
-
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
1648
|
-
return regex.test(value);
|
|
1649
|
-
},
|
|
1650
|
-
message: "Please enter a valid email address",
|
|
1651
|
-
debounce: 500
|
|
1652
|
-
},
|
|
1653
|
-
asyncUnique: (checkFn) => ({
|
|
1654
|
-
name: "asyncUnique",
|
|
1655
|
-
validate: async (value) => {
|
|
1656
|
-
if (!value) return true;
|
|
1657
|
-
return await checkFn(value);
|
|
1658
|
-
},
|
|
1659
|
-
message: "This value is already taken",
|
|
1660
|
-
debounce: 500
|
|
1661
|
-
})
|
|
1662
|
-
};
|
|
1663
|
-
var enhancedForm = createForm();
|
|
1664
|
-
|
|
1665
|
-
// src/advanced-validation.js
|
|
1666
|
-
import { ReactiveState, observable, computed } from "@coherent.js/state/src/reactive-state.js";
|
|
1667
|
-
import { globalErrorHandler } from "@coherent.js/core/src/utils/error-handler.js";
|
|
1668
|
-
var validationRules = {
|
|
1669
|
-
required: (value, params = true) => {
|
|
1670
|
-
if (!params) return true;
|
|
1671
|
-
const isEmpty = value === null || value === void 0 || value === "" || Array.isArray(value) && value.length === 0;
|
|
1672
|
-
return !isEmpty || "This field is required";
|
|
1673
|
-
},
|
|
1674
|
-
min: (value, minValue) => {
|
|
1675
|
-
if (value === null || value === void 0 || value === "") return true;
|
|
1676
|
-
const num = Number(value);
|
|
1677
|
-
return isNaN(num) || num >= minValue || `Value must be at least ${minValue}`;
|
|
1678
|
-
},
|
|
1679
|
-
max: (value, maxValue) => {
|
|
1680
|
-
if (value === null || value === void 0 || value === "") return true;
|
|
1681
|
-
const num = Number(value);
|
|
1682
|
-
return isNaN(num) || num <= maxValue || `Value must be no more than ${maxValue}`;
|
|
1683
|
-
},
|
|
1684
|
-
minLength: (value, minLen) => {
|
|
1685
|
-
if (value === null || value === void 0) return true;
|
|
1686
|
-
const str = String(value);
|
|
1687
|
-
return str.length >= minLen || `Must be at least ${minLen} characters`;
|
|
1688
|
-
},
|
|
1689
|
-
maxLength: (value, maxLen) => {
|
|
1690
|
-
if (value === null || value === void 0) return true;
|
|
1691
|
-
const str = String(value);
|
|
1692
|
-
return str.length <= maxLen || `Must be no more than ${maxLen} characters`;
|
|
1693
|
-
},
|
|
1694
|
-
pattern: (value, regex, message = "Invalid format") => {
|
|
1695
|
-
if (value === null || value === void 0 || value === "") return true;
|
|
1696
|
-
const pattern = typeof regex === "string" ? new RegExp(regex) : regex;
|
|
1697
|
-
return pattern.test(String(value)) || message;
|
|
1698
|
-
},
|
|
1699
|
-
email: (value) => {
|
|
1700
|
-
if (value === null || value === void 0 || value === "") return true;
|
|
1701
|
-
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
1702
|
-
return emailRegex.test(String(value)) || "Please enter a valid email address";
|
|
1703
|
-
},
|
|
1704
|
-
url: (value) => {
|
|
1705
|
-
if (value === null || value === void 0 || value === "") return true;
|
|
1706
|
-
try {
|
|
1707
|
-
new URL(String(value));
|
|
1708
|
-
return true;
|
|
1709
|
-
} catch {
|
|
1710
|
-
return "Please enter a valid URL";
|
|
1711
|
-
}
|
|
1712
|
-
},
|
|
1713
|
-
numeric: (value) => {
|
|
1714
|
-
if (value === null || value === void 0 || value === "") return true;
|
|
1715
|
-
return !isNaN(Number(value)) || "Must be a valid number";
|
|
1716
|
-
},
|
|
1717
|
-
integer: (value) => {
|
|
1718
|
-
if (value === null || value === void 0 || value === "") return true;
|
|
1719
|
-
const num = Number(value);
|
|
1720
|
-
return Number.isInteger(num) || "Must be a whole number";
|
|
1721
|
-
},
|
|
1722
|
-
alpha: (value) => {
|
|
1723
|
-
if (value === null || value === void 0 || value === "") return true;
|
|
1724
|
-
return /^[a-zA-Z]+$/.test(String(value)) || "Must contain only letters";
|
|
1725
|
-
},
|
|
1726
|
-
alphanumeric: (value) => {
|
|
1727
|
-
if (value === null || value === void 0 || value === "") return true;
|
|
1728
|
-
return /^[a-zA-Z0-9]+$/.test(String(value)) || "Must contain only letters and numbers";
|
|
1729
|
-
},
|
|
1730
|
-
equals: (value, otherValue, fieldName = "other field") => {
|
|
1731
|
-
return value === otherValue || `Must match ${fieldName}`;
|
|
1732
|
-
},
|
|
1733
|
-
oneOf: (value, options, message = "Invalid selection") => {
|
|
1734
|
-
if (value === null || value === void 0 || value === "") return true;
|
|
1735
|
-
return options.includes(value) || message;
|
|
1736
|
-
},
|
|
1737
|
-
custom: (value, validator, ...args) => {
|
|
1738
|
-
if (typeof validator !== "function") {
|
|
1739
|
-
throw new Error("Custom validator must be a function");
|
|
1740
|
-
}
|
|
1741
|
-
return validator(value, ...args);
|
|
1742
|
-
}
|
|
1743
|
-
};
|
|
1744
|
-
var binding = {
|
|
1745
|
-
/**
|
|
1746
|
-
* Two-way data binding for input elements
|
|
1747
|
-
*/
|
|
1748
|
-
model(form, fieldName, options = {}) {
|
|
1749
|
-
return {
|
|
1750
|
-
value: form.getField(fieldName),
|
|
1751
|
-
oninput: (event2) => {
|
|
1752
|
-
const value = options.number ? Number(event2.target.value) : event2.target.value;
|
|
1753
|
-
form.setField(fieldName, value);
|
|
1754
|
-
},
|
|
1755
|
-
onblur: () => {
|
|
1756
|
-
form.handleBlur(fieldName);
|
|
1757
|
-
}
|
|
1758
|
-
};
|
|
1759
|
-
},
|
|
1760
|
-
/**
|
|
1761
|
-
* Checkbox binding
|
|
1762
|
-
*/
|
|
1763
|
-
checkbox(form, fieldName) {
|
|
1764
|
-
return {
|
|
1765
|
-
checked: Boolean(form.getField(fieldName)),
|
|
1766
|
-
onchange: (event2) => {
|
|
1767
|
-
form.setField(fieldName, event2.target.checked);
|
|
1768
|
-
},
|
|
1769
|
-
onblur: () => {
|
|
1770
|
-
form.handleBlur(fieldName);
|
|
1771
|
-
}
|
|
1772
|
-
};
|
|
1773
|
-
},
|
|
1774
|
-
/**
|
|
1775
|
-
* Select dropdown binding
|
|
1776
|
-
*/
|
|
1777
|
-
select(form, fieldName, options = {}) {
|
|
1778
|
-
return {
|
|
1779
|
-
value: form.getField(fieldName),
|
|
1780
|
-
onchange: (event2) => {
|
|
1781
|
-
const value = options.multiple ? Array.from(event2.target.selectedOptions, (opt) => opt.value) : event2.target.value;
|
|
1782
|
-
form.setField(fieldName, value);
|
|
1783
|
-
},
|
|
1784
|
-
onblur: () => {
|
|
1785
|
-
form.handleBlur(fieldName);
|
|
1786
|
-
}
|
|
1787
|
-
};
|
|
1788
|
-
},
|
|
1789
|
-
/**
|
|
1790
|
-
* Radio button binding
|
|
1791
|
-
*/
|
|
1792
|
-
radio(form, fieldName, value) {
|
|
1793
|
-
return {
|
|
1794
|
-
checked: form.getField(fieldName) === value,
|
|
1795
|
-
value,
|
|
1796
|
-
onchange: (event2) => {
|
|
1797
|
-
if (event2.target.checked) {
|
|
1798
|
-
form.setField(fieldName, value);
|
|
1799
|
-
}
|
|
1800
|
-
}
|
|
1801
|
-
};
|
|
1802
|
-
}
|
|
1803
|
-
};
|
|
1804
|
-
var formComponents = {
|
|
1805
|
-
/**
|
|
1806
|
-
* Validation _error display
|
|
1807
|
-
*/
|
|
1808
|
-
ValidationError({ form, field, className = "validation-_error" }) {
|
|
1809
|
-
const validator = form.getValidator(field);
|
|
1810
|
-
if (!validator || validator.errors.value.length === 0) {
|
|
1811
|
-
return null;
|
|
1812
|
-
}
|
|
1813
|
-
return {
|
|
1814
|
-
div: {
|
|
1815
|
-
className,
|
|
1816
|
-
children: validator.errors.value.map((_error) => ({
|
|
1817
|
-
span: { text: _error, className: "_error-message" }
|
|
1818
|
-
}))
|
|
1819
|
-
}
|
|
1820
|
-
};
|
|
1821
|
-
},
|
|
1822
|
-
/**
|
|
1823
|
-
* Form field wrapper with validation
|
|
1824
|
-
*/
|
|
1825
|
-
FormField({ form, field, label, children, showErrors = true }) {
|
|
1826
|
-
const validator = form.getValidator(field);
|
|
1827
|
-
const state = validator ? validator.getState() : {};
|
|
1828
|
-
return {
|
|
1829
|
-
div: {
|
|
1830
|
-
className: `form-field ${state.hasError ? "has-_error" : ""} ${state.isTouched ? "touched" : ""}`,
|
|
1831
|
-
children: [
|
|
1832
|
-
label ? { label: { text: label, htmlFor: field } } : null,
|
|
1833
|
-
children,
|
|
1834
|
-
showErrors && state.hasError ? formComponents.ValidationError({ form, field }) : null
|
|
1835
|
-
].filter(Boolean)
|
|
1836
|
-
}
|
|
1837
|
-
};
|
|
1838
|
-
}
|
|
1839
|
-
};
|
|
1840
1243
|
export {
|
|
1841
1244
|
FormBuilder,
|
|
1842
1245
|
FormValidator,
|
|
1843
|
-
binding,
|
|
1844
1246
|
buildForm,
|
|
1845
1247
|
composeValidators,
|
|
1846
|
-
createForm,
|
|
1847
1248
|
createFormBuilder,
|
|
1848
1249
|
createValidator,
|
|
1849
|
-
enhancedForm,
|
|
1850
|
-
formComponents,
|
|
1851
|
-
formValidators,
|
|
1852
1250
|
hydrateForm,
|
|
1853
1251
|
registerValidator,
|
|
1854
1252
|
validate,
|
|
1855
1253
|
validateField,
|
|
1856
1254
|
validateForm,
|
|
1857
|
-
validationRules,
|
|
1858
1255
|
validators2 as validators
|
|
1859
1256
|
};
|
|
1860
1257
|
//# sourceMappingURL=index.js.map
|