form-builder-pro 1.3.4 → 1.3.6
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/index.css +8 -0
- package/dist/index.d.mts +18 -3
- package/dist/index.d.ts +18 -3
- package/dist/index.js +408 -18
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +408 -18
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4117,6 +4117,7 @@ var FIELD_TYPES = [
|
|
|
4117
4117
|
{ type: "email", label: "Email", icon: "Mail" },
|
|
4118
4118
|
{ type: "phone", label: "Phone", icon: "Phone" },
|
|
4119
4119
|
{ type: "date", label: "Date Picker", icon: "Calendar" },
|
|
4120
|
+
{ type: "datetime", label: "Date & Time", icon: "Clock" },
|
|
4120
4121
|
{ type: "select", label: "Dropdown", icon: "ListBullet" },
|
|
4121
4122
|
{ type: "checkbox", label: "Checkbox", icon: "CheckSquare" },
|
|
4122
4123
|
{ type: "radio", label: "Radio Group", icon: "CircleDot" },
|
|
@@ -4146,6 +4147,7 @@ var DEFAULT_FIELD_CONFIG = {
|
|
|
4146
4147
|
}
|
|
4147
4148
|
},
|
|
4148
4149
|
date: { label: "Date", width: "50%", enabled: true, visible: true },
|
|
4150
|
+
datetime: { label: "Date & Time", placeholder: "Select date and time", width: "50%", enabled: true, visible: true },
|
|
4149
4151
|
select: { label: "Dropdown", options: [], width: "100%", enabled: true, visible: true, optionSource: "STATIC" },
|
|
4150
4152
|
checkbox: { label: "Checkbox", options: [], width: "100%", enabled: true, visible: true },
|
|
4151
4153
|
radio: { label: "Radio Group", options: [], width: "100%", enabled: true, visible: true },
|
|
@@ -4362,6 +4364,10 @@ function validationsToValidationObject(v) {
|
|
|
4362
4364
|
obj.minDate = v.minDate;
|
|
4363
4365
|
if (v.maxDate)
|
|
4364
4366
|
obj.maxDate = v.maxDate;
|
|
4367
|
+
if (v.minDateTime)
|
|
4368
|
+
obj.minDateTime = v.minDateTime;
|
|
4369
|
+
if (v.maxDateTime)
|
|
4370
|
+
obj.maxDateTime = v.maxDateTime;
|
|
4365
4371
|
return Object.keys(obj).length > 0 ? obj : void 0;
|
|
4366
4372
|
}
|
|
4367
4373
|
function validationObjectToValidations(v) {
|
|
@@ -4394,6 +4400,10 @@ function validationObjectToValidations(v) {
|
|
|
4394
4400
|
validations.minDate = v.minDate;
|
|
4395
4401
|
if (v.maxDate)
|
|
4396
4402
|
validations.maxDate = v.maxDate;
|
|
4403
|
+
if (vAny.minDateTime)
|
|
4404
|
+
validations.minDateTime = vAny.minDateTime;
|
|
4405
|
+
if (vAny.maxDateTime)
|
|
4406
|
+
validations.maxDateTime = vAny.maxDateTime;
|
|
4397
4407
|
return Object.keys(validations).length > 0 ? validations : void 0;
|
|
4398
4408
|
}
|
|
4399
4409
|
function convertValidationObjectToArray(validationObj) {
|
|
@@ -4404,7 +4414,7 @@ function convertValidationObjectToArray(validationObj) {
|
|
|
4404
4414
|
}
|
|
4405
4415
|
const rules = [];
|
|
4406
4416
|
const obj = validationObj;
|
|
4407
|
-
const hasValidationProperties = !!(obj.regex || obj.regexMessage || obj.minLength !== void 0 || obj.maxLength !== void 0 || obj.min !== void 0 || obj.max !== void 0 || obj.minSelected !== void 0 || obj.maxSelected !== void 0);
|
|
4417
|
+
const hasValidationProperties = !!(obj.regex || obj.regexMessage || obj.minLength !== void 0 || obj.maxLength !== void 0 || obj.min !== void 0 || obj.max !== void 0 || obj.minSelected !== void 0 || obj.maxSelected !== void 0 || obj.minDate || obj.maxDate || obj.minDateTime || obj.maxDateTime);
|
|
4408
4418
|
const isRequired = obj.required === true || hasValidationProperties && obj.required !== false;
|
|
4409
4419
|
if (isRequired) {
|
|
4410
4420
|
rules.push({ type: "required", value: true });
|
|
@@ -4434,6 +4444,18 @@ function convertValidationObjectToArray(validationObj) {
|
|
|
4434
4444
|
if (obj.maxSelected !== void 0) {
|
|
4435
4445
|
rules.push({ type: "maxSelected", value: obj.maxSelected });
|
|
4436
4446
|
}
|
|
4447
|
+
if (obj.minDate && typeof obj.minDate === "string") {
|
|
4448
|
+
rules.push({ type: "minDate", value: obj.minDate });
|
|
4449
|
+
}
|
|
4450
|
+
if (obj.maxDate && typeof obj.maxDate === "string") {
|
|
4451
|
+
rules.push({ type: "maxDate", value: obj.maxDate });
|
|
4452
|
+
}
|
|
4453
|
+
if (obj.minDateTime && typeof obj.minDateTime === "string") {
|
|
4454
|
+
rules.push({ type: "minDateTime", value: obj.minDateTime });
|
|
4455
|
+
}
|
|
4456
|
+
if (obj.maxDateTime && typeof obj.maxDateTime === "string") {
|
|
4457
|
+
rules.push({ type: "maxDateTime", value: obj.maxDateTime });
|
|
4458
|
+
}
|
|
4437
4459
|
return rules;
|
|
4438
4460
|
}
|
|
4439
4461
|
function convertSpanToWidth(span, totalColumns = 12) {
|
|
@@ -4442,6 +4464,14 @@ function convertSpanToWidth(span, totalColumns = 12) {
|
|
|
4442
4464
|
const percentage = span / totalColumns * 100;
|
|
4443
4465
|
return Math.max(10, Math.min(100, Math.round(percentage)));
|
|
4444
4466
|
}
|
|
4467
|
+
var EMAIL_REGEX = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$";
|
|
4468
|
+
var EMAIL_REGEX_MESSAGE = "Invalid email format";
|
|
4469
|
+
function isEmailLikeField(field) {
|
|
4470
|
+
const label = (field.label || "").toLowerCase();
|
|
4471
|
+
const fieldName = (field.fieldName || field.name || "").toLowerCase();
|
|
4472
|
+
const placeholder = (field.placeholder || "").toLowerCase();
|
|
4473
|
+
return /email/.test(label) || /email/.test(fieldName) || /@/.test(placeholder) || /email/.test(placeholder);
|
|
4474
|
+
}
|
|
4445
4475
|
function normalizeFieldType(type) {
|
|
4446
4476
|
if (!type)
|
|
4447
4477
|
return "text";
|
|
@@ -4455,12 +4485,17 @@ function normalizeFieldType(type) {
|
|
|
4455
4485
|
return "binary_choice";
|
|
4456
4486
|
if (str === "REPEATER" || normalized === "repeater")
|
|
4457
4487
|
return "repeater";
|
|
4488
|
+
if (str === "DATETIME" || normalized === "datetime")
|
|
4489
|
+
return "datetime";
|
|
4458
4490
|
return str.toLowerCase();
|
|
4459
4491
|
}
|
|
4460
4492
|
function transformField(field) {
|
|
4461
4493
|
const fieldId = field.id || field.fieldId;
|
|
4462
4494
|
const fieldType = field.type || field.fieldType;
|
|
4463
|
-
|
|
4495
|
+
let normalizedType = normalizeFieldType(fieldType);
|
|
4496
|
+
if (normalizedType === "text" && isEmailLikeField(field)) {
|
|
4497
|
+
normalizedType = "email";
|
|
4498
|
+
}
|
|
4464
4499
|
const transformed = {
|
|
4465
4500
|
id: fieldId,
|
|
4466
4501
|
type: normalizedType,
|
|
@@ -4539,6 +4574,10 @@ function transformField(field) {
|
|
|
4539
4574
|
validationObj.minDate = rule.value;
|
|
4540
4575
|
} else if (rule.type === "maxDate" && typeof rule.value === "string") {
|
|
4541
4576
|
validationObj.maxDate = rule.value;
|
|
4577
|
+
} else if (rule.type === "minDateTime" && typeof rule.value === "string") {
|
|
4578
|
+
validationObj.minDateTime = rule.value;
|
|
4579
|
+
} else if (rule.type === "maxDateTime" && typeof rule.value === "string") {
|
|
4580
|
+
validationObj.maxDateTime = rule.value;
|
|
4542
4581
|
}
|
|
4543
4582
|
});
|
|
4544
4583
|
transformed.validation = validationObj;
|
|
@@ -4667,6 +4706,8 @@ function transformField(field) {
|
|
|
4667
4706
|
transformed.repeatItemLabel = field.repeatItemLabel;
|
|
4668
4707
|
if (field.repeatIncrementEnabled !== void 0)
|
|
4669
4708
|
transformed.repeatIncrementEnabled = field.repeatIncrementEnabled;
|
|
4709
|
+
if (field.dateConstraints !== void 0)
|
|
4710
|
+
transformed.dateConstraints = field.dateConstraints;
|
|
4670
4711
|
if (field.css !== void 0)
|
|
4671
4712
|
transformed.css = field.css;
|
|
4672
4713
|
if (field.optionsSource !== void 0)
|
|
@@ -4772,6 +4813,10 @@ function convertValidationArrayToObject(validation) {
|
|
|
4772
4813
|
obj.minDate = rule.value;
|
|
4773
4814
|
} else if (rule.type === "maxDate" && typeof rule.value === "string") {
|
|
4774
4815
|
obj.maxDate = rule.value;
|
|
4816
|
+
} else if (rule.type === "minDateTime" && typeof rule.value === "string") {
|
|
4817
|
+
obj.minDateTime = rule.value;
|
|
4818
|
+
} else if (rule.type === "maxDateTime" && typeof rule.value === "string") {
|
|
4819
|
+
obj.maxDateTime = rule.value;
|
|
4775
4820
|
}
|
|
4776
4821
|
});
|
|
4777
4822
|
return Object.keys(obj).length > 0 ? obj : void 0;
|
|
@@ -4783,9 +4828,19 @@ function convertWidthToSpan(width, totalColumns = 12) {
|
|
|
4783
4828
|
return Math.max(1, Math.min(12, Math.round(widthNum / 100 * totalColumns)));
|
|
4784
4829
|
}
|
|
4785
4830
|
function fieldToPayload(field) {
|
|
4831
|
+
let outputType = field.type;
|
|
4832
|
+
let outputValidations = field.validations ? { ...field.validations } : void 0;
|
|
4833
|
+
if (field.type === "text" && isEmailLikeField(field)) {
|
|
4834
|
+
outputType = "email";
|
|
4835
|
+
outputValidations = field.validations ? { ...field.validations } : {};
|
|
4836
|
+
if (!outputValidations.pattern) {
|
|
4837
|
+
outputValidations.pattern = EMAIL_REGEX;
|
|
4838
|
+
outputValidations.customErrorMessages = { ...outputValidations.customErrorMessages, pattern: EMAIL_REGEX_MESSAGE };
|
|
4839
|
+
}
|
|
4840
|
+
}
|
|
4786
4841
|
const payload = {
|
|
4787
4842
|
id: field.id,
|
|
4788
|
-
type:
|
|
4843
|
+
type: outputType,
|
|
4789
4844
|
label: field.label,
|
|
4790
4845
|
name: field.fieldName || field.id,
|
|
4791
4846
|
// Model key for binding (API / host app)
|
|
@@ -4810,17 +4865,18 @@ function fieldToPayload(field) {
|
|
|
4810
4865
|
span: 12
|
|
4811
4866
|
};
|
|
4812
4867
|
}
|
|
4813
|
-
|
|
4814
|
-
|
|
4868
|
+
const validationsToUse = outputValidations ?? field.validations;
|
|
4869
|
+
if (validationsToUse) {
|
|
4870
|
+
let validations = { ...validationsToUse };
|
|
4815
4871
|
if (validations.validationType === "age") {
|
|
4816
4872
|
validations = { ...validations, validationType: "custom" };
|
|
4817
4873
|
}
|
|
4818
4874
|
payload.validations = validations;
|
|
4819
|
-
if (
|
|
4875
|
+
if (validationsToUse.required) {
|
|
4820
4876
|
payload.required = true;
|
|
4821
4877
|
}
|
|
4822
4878
|
}
|
|
4823
|
-
const baseValidation =
|
|
4879
|
+
const baseValidation = validationsToUse ? validationsToValidationObject(validationsToUse) : convertValidationArrayToObject(field.validation);
|
|
4824
4880
|
payload.validation = baseValidation;
|
|
4825
4881
|
const pattern = baseValidation?.regex ?? field.validations?.pattern;
|
|
4826
4882
|
const patternMsg = baseValidation?.regexMessage ?? field.validations?.customErrorMessages?.pattern;
|
|
@@ -4880,6 +4936,11 @@ function fieldToPayload(field) {
|
|
|
4880
4936
|
if ((field.type === "select" || field.type === "radio" || field.type === "checkbox") && field.options && Array.isArray(field.options)) {
|
|
4881
4937
|
payload.options = field.options.map((opt) => ({ label: opt.label, value: opt.value }));
|
|
4882
4938
|
}
|
|
4939
|
+
if (field.dateConstraints !== void 0)
|
|
4940
|
+
payload.dateConstraints = field.dateConstraints;
|
|
4941
|
+
if (field.type === "datetime") {
|
|
4942
|
+
payload.fieldType = "DATETIME";
|
|
4943
|
+
}
|
|
4883
4944
|
if (field.type === "binary_choice") {
|
|
4884
4945
|
payload.fieldType = "BINARY_CHOICE";
|
|
4885
4946
|
payload.type = "binary_choice";
|
|
@@ -5964,7 +6025,9 @@ function getValidationRules(field) {
|
|
|
5964
6025
|
min: v.min,
|
|
5965
6026
|
max: v.max,
|
|
5966
6027
|
minDate: v.minDate,
|
|
5967
|
-
maxDate: v.maxDate
|
|
6028
|
+
maxDate: v.maxDate,
|
|
6029
|
+
minDateTime: v.minDateTime,
|
|
6030
|
+
maxDateTime: v.maxDateTime
|
|
5968
6031
|
};
|
|
5969
6032
|
}
|
|
5970
6033
|
const val = field.validation;
|
|
@@ -5990,6 +6053,10 @@ function getValidationRules(field) {
|
|
|
5990
6053
|
rules.minDate = r.value;
|
|
5991
6054
|
else if (r.type === "maxDate")
|
|
5992
6055
|
rules.maxDate = r.value;
|
|
6056
|
+
else if (r.type === "minDateTime")
|
|
6057
|
+
rules.minDateTime = r.value;
|
|
6058
|
+
else if (r.type === "maxDateTime")
|
|
6059
|
+
rules.maxDateTime = r.value;
|
|
5993
6060
|
});
|
|
5994
6061
|
return rules;
|
|
5995
6062
|
}
|
|
@@ -6003,15 +6070,108 @@ function getValidationRules(field) {
|
|
|
6003
6070
|
min: o.min,
|
|
6004
6071
|
max: o.max,
|
|
6005
6072
|
minDate: o.minDate,
|
|
6006
|
-
maxDate: o.maxDate
|
|
6073
|
+
maxDate: o.maxDate,
|
|
6074
|
+
minDateTime: o.minDateTime,
|
|
6075
|
+
maxDateTime: o.maxDateTime
|
|
6007
6076
|
};
|
|
6008
6077
|
}
|
|
6009
6078
|
function isNumericTextField(field) {
|
|
6010
6079
|
const v = field.validations;
|
|
6011
6080
|
return !!(v?.validationType && ["postalCode", "phoneNumber", "otp"].includes(v.validationType));
|
|
6012
6081
|
}
|
|
6082
|
+
function toDateString(d) {
|
|
6083
|
+
return d.toISOString().split("T")[0];
|
|
6084
|
+
}
|
|
6085
|
+
function toDatetimeString(d) {
|
|
6086
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
6087
|
+
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
|
6088
|
+
}
|
|
6089
|
+
function resolveDateConstraintBounds(field, allFields, formData) {
|
|
6090
|
+
const constraint = field.dateConstraints;
|
|
6091
|
+
if (!constraint)
|
|
6092
|
+
return null;
|
|
6093
|
+
const isDatetime = field.type === "datetime";
|
|
6094
|
+
let constraintDate = null;
|
|
6095
|
+
if (constraint.compareWith === "CURRENT_DATE") {
|
|
6096
|
+
constraintDate = /* @__PURE__ */ new Date();
|
|
6097
|
+
if (!isDatetime)
|
|
6098
|
+
constraintDate.setHours(0, 0, 0, 0);
|
|
6099
|
+
} else if (constraint.compareWith === "FIELD" && constraint.fieldName) {
|
|
6100
|
+
const refField = allFields?.find(
|
|
6101
|
+
(f) => f.fieldName === constraint.fieldName || f.id === constraint.fieldName
|
|
6102
|
+
);
|
|
6103
|
+
if (refField && formData) {
|
|
6104
|
+
const key = refField.fieldName ?? refField.id;
|
|
6105
|
+
const raw = formData[key];
|
|
6106
|
+
if (raw)
|
|
6107
|
+
constraintDate = new Date(raw);
|
|
6108
|
+
}
|
|
6109
|
+
}
|
|
6110
|
+
if (!constraintDate || isNaN(constraintDate.getTime()))
|
|
6111
|
+
return null;
|
|
6112
|
+
const MS_MIN = 6e4;
|
|
6113
|
+
const MS_DAY = 864e5;
|
|
6114
|
+
const { operator } = constraint;
|
|
6115
|
+
if (isDatetime) {
|
|
6116
|
+
if (operator === "LESS_THAN")
|
|
6117
|
+
return { max: toDatetimeString(new Date(constraintDate.getTime() - MS_MIN)) };
|
|
6118
|
+
if (operator === "LESS_THAN_EQUAL")
|
|
6119
|
+
return { max: toDatetimeString(constraintDate) };
|
|
6120
|
+
if (operator === "GREATER_THAN")
|
|
6121
|
+
return { min: toDatetimeString(new Date(constraintDate.getTime() + MS_MIN)) };
|
|
6122
|
+
return { min: toDatetimeString(constraintDate) };
|
|
6123
|
+
} else {
|
|
6124
|
+
if (operator === "LESS_THAN")
|
|
6125
|
+
return { max: toDateString(new Date(constraintDate.getTime() - MS_DAY)) };
|
|
6126
|
+
if (operator === "LESS_THAN_EQUAL")
|
|
6127
|
+
return { max: toDateString(constraintDate) };
|
|
6128
|
+
if (operator === "GREATER_THAN")
|
|
6129
|
+
return { min: toDateString(new Date(constraintDate.getTime() + MS_DAY)) };
|
|
6130
|
+
return { min: toDateString(constraintDate) };
|
|
6131
|
+
}
|
|
6132
|
+
}
|
|
6133
|
+
function validateDateConstraint(field, value, allFields, formData) {
|
|
6134
|
+
const constraint = field.dateConstraints;
|
|
6135
|
+
if (!constraint || !value)
|
|
6136
|
+
return "";
|
|
6137
|
+
const inputDate = new Date(value);
|
|
6138
|
+
if (isNaN(inputDate.getTime()))
|
|
6139
|
+
return "";
|
|
6140
|
+
let constraintDate = null;
|
|
6141
|
+
let constraintLabel = "";
|
|
6142
|
+
if (constraint.compareWith === "CURRENT_DATE") {
|
|
6143
|
+
constraintDate = /* @__PURE__ */ new Date();
|
|
6144
|
+
if (field.type === "date")
|
|
6145
|
+
constraintDate.setHours(0, 0, 0, 0);
|
|
6146
|
+
constraintLabel = "today's date";
|
|
6147
|
+
} else if (constraint.compareWith === "FIELD" && constraint.fieldName) {
|
|
6148
|
+
const refField = allFields?.find(
|
|
6149
|
+
(f) => f.fieldName === constraint.fieldName || f.id === constraint.fieldName
|
|
6150
|
+
);
|
|
6151
|
+
if (refField && formData) {
|
|
6152
|
+
const key = refField.fieldName ?? refField.id;
|
|
6153
|
+
const raw = formData[key];
|
|
6154
|
+
if (raw) {
|
|
6155
|
+
constraintDate = new Date(raw);
|
|
6156
|
+
constraintLabel = `"${refField.label || constraint.fieldName}"`;
|
|
6157
|
+
}
|
|
6158
|
+
}
|
|
6159
|
+
}
|
|
6160
|
+
if (!constraintDate || isNaN(constraintDate.getTime()))
|
|
6161
|
+
return "";
|
|
6162
|
+
const { operator } = constraint;
|
|
6163
|
+
if (operator === "LESS_THAN" && !(inputDate < constraintDate))
|
|
6164
|
+
return `Date must be before ${constraintLabel}`;
|
|
6165
|
+
if (operator === "LESS_THAN_EQUAL" && !(inputDate <= constraintDate))
|
|
6166
|
+
return `Date must be on or before ${constraintLabel}`;
|
|
6167
|
+
if (operator === "GREATER_THAN" && !(inputDate > constraintDate))
|
|
6168
|
+
return `Date must be after ${constraintLabel}`;
|
|
6169
|
+
if (operator === "GREATER_THAN_EQUAL" && !(inputDate >= constraintDate))
|
|
6170
|
+
return `Date must be on or after ${constraintLabel}`;
|
|
6171
|
+
return "";
|
|
6172
|
+
}
|
|
6013
6173
|
var FieldRenderer = class {
|
|
6014
|
-
static render(field, value, onChange, readOnly = false) {
|
|
6174
|
+
static render(field, value, onChange, readOnly = false, allFields, formData) {
|
|
6015
6175
|
const wrapper = createElement("div", { className: "w-full form-row" });
|
|
6016
6176
|
const isEnabled = field.enabled !== false && !readOnly;
|
|
6017
6177
|
if (field.type !== "checkbox" && field.type !== "toggle" && field.type !== "binary_choice") {
|
|
@@ -6047,7 +6207,7 @@ var FieldRenderer = class {
|
|
|
6047
6207
|
const validationRules = getValidationRules(field);
|
|
6048
6208
|
const hasPatternValidation = !!validationRules.pattern;
|
|
6049
6209
|
const hasNumericTextValidation = isNumericTextField(field);
|
|
6050
|
-
if (field.type === "date" || field.type === "email" || field.type === "number" || field.type === "text" && (hasPatternValidation || hasNumericTextValidation || validationRules.minLength !== void 0 || validationRules.maxLength !== void 0)) {
|
|
6210
|
+
if (field.type === "date" || field.type === "datetime" || field.type === "email" || field.type === "number" || field.type === "text" && (hasPatternValidation || hasNumericTextValidation || validationRules.minLength !== void 0 || validationRules.maxLength !== void 0)) {
|
|
6051
6211
|
validationMsg = createElement("div", { className: "text-xs text-red-600 dark:text-red-400 mt-1 hidden", id: `validation-${field.id}` });
|
|
6052
6212
|
}
|
|
6053
6213
|
const validateField = (f, value2, inputElement, msgEl) => {
|
|
@@ -6072,6 +6232,26 @@ var FieldRenderer = class {
|
|
|
6072
6232
|
}
|
|
6073
6233
|
}
|
|
6074
6234
|
}
|
|
6235
|
+
if (!errorMessage && f.type === "datetime" && value2) {
|
|
6236
|
+
const inputDateTime = new Date(value2);
|
|
6237
|
+
const minDt = rules.minDateTime ?? rules.minDate;
|
|
6238
|
+
const maxDt = rules.maxDateTime ?? rules.maxDate;
|
|
6239
|
+
if (minDt) {
|
|
6240
|
+
const minDateTime = new Date(minDt);
|
|
6241
|
+
if (inputDateTime < minDateTime) {
|
|
6242
|
+
errorMessage = "Date & time must be after the minimum";
|
|
6243
|
+
}
|
|
6244
|
+
}
|
|
6245
|
+
if (!errorMessage && maxDt) {
|
|
6246
|
+
const maxDateTime = new Date(maxDt);
|
|
6247
|
+
if (inputDateTime > maxDateTime) {
|
|
6248
|
+
errorMessage = "Date & time must be before the maximum";
|
|
6249
|
+
}
|
|
6250
|
+
}
|
|
6251
|
+
}
|
|
6252
|
+
if (!errorMessage && (f.type === "date" || f.type === "datetime") && value2 && f.dateConstraints) {
|
|
6253
|
+
errorMessage = validateDateConstraint(f, value2, allFields, formData);
|
|
6254
|
+
}
|
|
6075
6255
|
if (!errorMessage && (f.type === "email" || f.type === "text" || f.type === "phone") && value2 && rules.pattern) {
|
|
6076
6256
|
try {
|
|
6077
6257
|
if (!new RegExp(rules.pattern).test(value2)) {
|
|
@@ -6323,12 +6503,23 @@ var FieldRenderer = class {
|
|
|
6323
6503
|
default:
|
|
6324
6504
|
const rules = getValidationRules(field);
|
|
6325
6505
|
const useNumericTextInput = field.type === "text" && isNumericTextField(field);
|
|
6326
|
-
const inputType = useNumericTextInput ? "text" : field.type === "number" ? "number" : field.type;
|
|
6506
|
+
const inputType = useNumericTextInput ? "text" : field.type === "number" ? "number" : field.type === "datetime" ? "datetime-local" : field.type;
|
|
6327
6507
|
const runValidation = () => {
|
|
6328
|
-
if (validationMsg && (field.type === "date" || field.type === "email" || field.type === "text" || field.type === "number")) {
|
|
6508
|
+
if (validationMsg && (field.type === "date" || field.type === "datetime" || field.type === "email" || field.type === "text" || field.type === "number")) {
|
|
6329
6509
|
validateField(field, input.value, input, validationMsg);
|
|
6330
6510
|
}
|
|
6331
6511
|
};
|
|
6512
|
+
let minVal = field.type === "datetime" ? rules.minDateTime ?? rules.minDate : field.type === "date" ? rules.minDate : field.type === "number" && rules.min !== void 0 ? String(rules.min) : void 0;
|
|
6513
|
+
let maxVal = field.type === "datetime" ? rules.maxDateTime ?? rules.maxDate : field.type === "date" ? rules.maxDate : field.type === "number" && rules.max !== void 0 ? String(rules.max) : void 0;
|
|
6514
|
+
if (field.type === "date" || field.type === "datetime") {
|
|
6515
|
+
const bounds = resolveDateConstraintBounds(field, allFields, formData);
|
|
6516
|
+
if (bounds) {
|
|
6517
|
+
if (bounds.min !== void 0)
|
|
6518
|
+
minVal = bounds.min;
|
|
6519
|
+
if (bounds.max !== void 0)
|
|
6520
|
+
maxVal = bounds.max;
|
|
6521
|
+
}
|
|
6522
|
+
}
|
|
6332
6523
|
input = createElement("input", {
|
|
6333
6524
|
type: inputType,
|
|
6334
6525
|
...useNumericTextInput && { inputmode: "numeric" },
|
|
@@ -6336,8 +6527,8 @@ var FieldRenderer = class {
|
|
|
6336
6527
|
placeholder: field.placeholder,
|
|
6337
6528
|
value: value || "",
|
|
6338
6529
|
disabled: !isEnabled,
|
|
6339
|
-
min:
|
|
6340
|
-
max:
|
|
6530
|
+
min: minVal,
|
|
6531
|
+
max: maxVal,
|
|
6341
6532
|
pattern: !useNumericTextInput ? rules.pattern || void 0 : void 0,
|
|
6342
6533
|
oninput: (e) => {
|
|
6343
6534
|
const inputEl = e.target;
|
|
@@ -6595,7 +6786,7 @@ function getValidationRulesForField(field) {
|
|
|
6595
6786
|
}
|
|
6596
6787
|
return convertValidationToArray(field.validation);
|
|
6597
6788
|
}
|
|
6598
|
-
function getFieldValidationError(field, fieldValue) {
|
|
6789
|
+
function getFieldValidationError(field, fieldValue, allFields, formData) {
|
|
6599
6790
|
const isRequired = field.validations?.required ?? field.required;
|
|
6600
6791
|
if (isRequired && (!fieldValue || fieldValue === "" || Array.isArray(fieldValue) && fieldValue.length === 0)) {
|
|
6601
6792
|
return field.validations?.customErrorMessages?.required || "This field is required";
|
|
@@ -6636,6 +6827,53 @@ function getFieldValidationError(field, fieldValue) {
|
|
|
6636
6827
|
}
|
|
6637
6828
|
}
|
|
6638
6829
|
}
|
|
6830
|
+
if (field.type === "datetime" && fieldValue) {
|
|
6831
|
+
const v = field.validations;
|
|
6832
|
+
const minDt = v?.minDateTime ?? v?.minDate;
|
|
6833
|
+
const maxDt = v?.maxDateTime ?? v?.maxDate;
|
|
6834
|
+
const inputDt = new Date(fieldValue);
|
|
6835
|
+
if (minDt && inputDt < new Date(minDt)) {
|
|
6836
|
+
return "Date & time must be after the minimum";
|
|
6837
|
+
}
|
|
6838
|
+
if (maxDt && inputDt > new Date(maxDt)) {
|
|
6839
|
+
return "Date & time must be before the maximum";
|
|
6840
|
+
}
|
|
6841
|
+
}
|
|
6842
|
+
if ((field.type === "date" || field.type === "datetime") && fieldValue && field.dateConstraints) {
|
|
6843
|
+
const constraint = field.dateConstraints;
|
|
6844
|
+
const inputDate = new Date(fieldValue);
|
|
6845
|
+
let constraintDate = null;
|
|
6846
|
+
let constraintLabel = "";
|
|
6847
|
+
if (constraint.compareWith === "CURRENT_DATE") {
|
|
6848
|
+
constraintDate = /* @__PURE__ */ new Date();
|
|
6849
|
+
if (field.type === "date")
|
|
6850
|
+
constraintDate.setHours(0, 0, 0, 0);
|
|
6851
|
+
constraintLabel = "today's date";
|
|
6852
|
+
} else if (constraint.compareWith === "FIELD" && constraint.fieldName && allFields && formData) {
|
|
6853
|
+
const refField = allFields.find(
|
|
6854
|
+
(f) => f.fieldName === constraint.fieldName || f.id === constraint.fieldName
|
|
6855
|
+
);
|
|
6856
|
+
if (refField) {
|
|
6857
|
+
const key = refField.fieldName ?? refField.id;
|
|
6858
|
+
const raw = formData[key];
|
|
6859
|
+
if (raw) {
|
|
6860
|
+
constraintDate = new Date(raw);
|
|
6861
|
+
constraintLabel = `"${refField.label || constraint.fieldName}"`;
|
|
6862
|
+
}
|
|
6863
|
+
}
|
|
6864
|
+
}
|
|
6865
|
+
if (constraintDate && !isNaN(constraintDate.getTime())) {
|
|
6866
|
+
const { operator } = constraint;
|
|
6867
|
+
if (operator === "LESS_THAN" && !(inputDate < constraintDate))
|
|
6868
|
+
return `Date must be before ${constraintLabel}`;
|
|
6869
|
+
if (operator === "LESS_THAN_EQUAL" && !(inputDate <= constraintDate))
|
|
6870
|
+
return `Date must be on or before ${constraintLabel}`;
|
|
6871
|
+
if (operator === "GREATER_THAN" && !(inputDate > constraintDate))
|
|
6872
|
+
return `Date must be after ${constraintLabel}`;
|
|
6873
|
+
if (operator === "GREATER_THAN_EQUAL" && !(inputDate >= constraintDate))
|
|
6874
|
+
return `Date must be on or after ${constraintLabel}`;
|
|
6875
|
+
}
|
|
6876
|
+
}
|
|
6639
6877
|
if (field.type === "checkbox" && Array.isArray(fieldValue)) {
|
|
6640
6878
|
const validationArray = getValidationRulesForField(field);
|
|
6641
6879
|
const minSelectedRule = validationArray.find((v) => v.type === "minSelected");
|
|
@@ -6705,6 +6943,20 @@ function isFormulaDependency(schema, modelKey, fieldId) {
|
|
|
6705
6943
|
}
|
|
6706
6944
|
return false;
|
|
6707
6945
|
}
|
|
6946
|
+
function isDateConstraintDependency(schema, modelKey, fieldId) {
|
|
6947
|
+
for (const section of schema.sections) {
|
|
6948
|
+
for (const field of section.fields) {
|
|
6949
|
+
const c = field.dateConstraints;
|
|
6950
|
+
if (c && c.compareWith === "FIELD" && c.fieldName) {
|
|
6951
|
+
if (c.fieldName === modelKey)
|
|
6952
|
+
return true;
|
|
6953
|
+
if (fieldId && c.fieldName === fieldId)
|
|
6954
|
+
return true;
|
|
6955
|
+
}
|
|
6956
|
+
}
|
|
6957
|
+
}
|
|
6958
|
+
return false;
|
|
6959
|
+
}
|
|
6708
6960
|
var FormRenderer = class {
|
|
6709
6961
|
constructor(container, schema, onSubmit, onDropdownValueChange, initialData) {
|
|
6710
6962
|
__publicField(this, "container");
|
|
@@ -6729,6 +6981,7 @@ var FormRenderer = class {
|
|
|
6729
6981
|
this.container.innerHTML = "";
|
|
6730
6982
|
const form = createElement("form", { className: "space-y-6 md:space-y-8" });
|
|
6731
6983
|
form.appendChild(createElement("h1", { className: "text-2xl font-semibold mb-2 text-[#3b497e] ", text: this.schema.title }));
|
|
6984
|
+
const allFields = this.schema.sections.flatMap((s) => s.fields);
|
|
6732
6985
|
this.schema.sections.forEach((section) => {
|
|
6733
6986
|
const sectionEl = createElement("div", { className: "space-y-3 md:space-y-4 !m-0" });
|
|
6734
6987
|
sectionEl.appendChild(createElement("h2", { className: "text-xl font-semibold text-[#3b497e] dark:text-gray-200 border-b pb-2", text: section.title }));
|
|
@@ -6773,10 +7026,16 @@ var FormRenderer = class {
|
|
|
6773
7026
|
}
|
|
6774
7027
|
if (isFormulaDependency(this.schema, modelKey, field.id)) {
|
|
6775
7028
|
this.render();
|
|
7029
|
+
return;
|
|
7030
|
+
}
|
|
7031
|
+
if (isDateConstraintDependency(this.schema, modelKey, field.id)) {
|
|
7032
|
+
this.render();
|
|
6776
7033
|
}
|
|
6777
7034
|
},
|
|
6778
|
-
isFormulaField
|
|
7035
|
+
isFormulaField,
|
|
6779
7036
|
// Formula fields are read-only
|
|
7037
|
+
allFields,
|
|
7038
|
+
this.data
|
|
6780
7039
|
);
|
|
6781
7040
|
fieldWrapper.appendChild(fieldEl);
|
|
6782
7041
|
grid.appendChild(fieldWrapper);
|
|
@@ -6800,7 +7059,7 @@ var FormRenderer = class {
|
|
|
6800
7059
|
const modelKey = getModelKey(field);
|
|
6801
7060
|
const fieldValue = this.data[modelKey];
|
|
6802
7061
|
const fieldElement = form.querySelector(`input[id*="${field.id}"], textarea[id*="${field.id}"], select[id*="${field.id}"]`);
|
|
6803
|
-
const fieldError = getFieldValidationError(field, fieldValue);
|
|
7062
|
+
const fieldError = getFieldValidationError(field, fieldValue, allFields, this.data);
|
|
6804
7063
|
const element = fieldElement ?? Array.from(form.querySelectorAll("input, textarea, select")).find((el) => {
|
|
6805
7064
|
const wrapper = el.closest("div");
|
|
6806
7065
|
return wrapper && wrapper.textContent?.includes(field.label);
|
|
@@ -10869,6 +11128,10 @@ var FormBuilder = class {
|
|
|
10869
11128
|
obj.minDate = rule.value;
|
|
10870
11129
|
else if (rule.type === "maxDate" && typeof rule.value === "string")
|
|
10871
11130
|
obj.maxDate = rule.value;
|
|
11131
|
+
else if (rule.type === "minDateTime" && typeof rule.value === "string")
|
|
11132
|
+
obj.minDateTime = rule.value;
|
|
11133
|
+
else if (rule.type === "maxDateTime" && typeof rule.value === "string")
|
|
11134
|
+
obj.maxDateTime = rule.value;
|
|
10872
11135
|
});
|
|
10873
11136
|
return obj;
|
|
10874
11137
|
}
|
|
@@ -10884,6 +11147,8 @@ var FormBuilder = class {
|
|
|
10884
11147
|
maxSelected: o.maxSelected,
|
|
10885
11148
|
minDate: o.minDate,
|
|
10886
11149
|
maxDate: o.maxDate,
|
|
11150
|
+
minDateTime: o.minDateTime,
|
|
11151
|
+
maxDateTime: o.maxDateTime,
|
|
10887
11152
|
customErrorMessages: o.regexMessage ? { pattern: o.regexMessage } : void 0
|
|
10888
11153
|
};
|
|
10889
11154
|
})();
|
|
@@ -11319,11 +11584,136 @@ var FormBuilder = class {
|
|
|
11319
11584
|
}));
|
|
11320
11585
|
validationElements.push(maxDateGroup);
|
|
11321
11586
|
}
|
|
11587
|
+
if (selectedField.type === "datetime") {
|
|
11588
|
+
const minMaxDt = validationsObj;
|
|
11589
|
+
const minDateTimeGroup = createElement("div", { className: "mb-3" });
|
|
11590
|
+
minDateTimeGroup.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "Minimum Date & Time" }));
|
|
11591
|
+
minDateTimeGroup.appendChild(createElement("input", {
|
|
11592
|
+
type: "datetime-local",
|
|
11593
|
+
className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent",
|
|
11594
|
+
value: minMaxDt.minDateTime || minMaxDt.minDate || "",
|
|
11595
|
+
onchange: (e) => {
|
|
11596
|
+
const val = e.target.value;
|
|
11597
|
+
updateValidations({ minDateTime: val || void 0 });
|
|
11598
|
+
}
|
|
11599
|
+
}));
|
|
11600
|
+
validationElements.push(minDateTimeGroup);
|
|
11601
|
+
const maxDateTimeGroup = createElement("div", { className: "mb-3" });
|
|
11602
|
+
maxDateTimeGroup.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "Maximum Date & Time" }));
|
|
11603
|
+
maxDateTimeGroup.appendChild(createElement("input", {
|
|
11604
|
+
type: "datetime-local",
|
|
11605
|
+
className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent",
|
|
11606
|
+
value: minMaxDt.maxDateTime || minMaxDt.maxDate || "",
|
|
11607
|
+
onchange: (e) => {
|
|
11608
|
+
const val = e.target.value;
|
|
11609
|
+
updateValidations({ maxDateTime: val || void 0 });
|
|
11610
|
+
}
|
|
11611
|
+
}));
|
|
11612
|
+
validationElements.push(maxDateTimeGroup);
|
|
11613
|
+
}
|
|
11322
11614
|
if (validationElements.length > 0) {
|
|
11323
11615
|
const validationHeader = createElement("h3", { className: "text-xs font-semibold text-gray-500 uppercase tracking-wider mb-3 mt-6", text: "Validation Rules" });
|
|
11324
11616
|
body.appendChild(validationHeader);
|
|
11325
11617
|
validationElements.forEach((el) => body.appendChild(el));
|
|
11326
11618
|
}
|
|
11619
|
+
if (selectedField.type === "date" || selectedField.type === "datetime") {
|
|
11620
|
+
const constraintHeader = createElement("h3", { className: "text-xs font-semibold text-gray-500 uppercase tracking-wider mb-3 mt-6", text: "Date Constraint" });
|
|
11621
|
+
body.appendChild(constraintHeader);
|
|
11622
|
+
const getConstraint = () => {
|
|
11623
|
+
const f = formStore.getState().schema.sections.flatMap((s) => s.fields).find((f2) => f2.id === selectedField.id);
|
|
11624
|
+
return f?.dateConstraints;
|
|
11625
|
+
};
|
|
11626
|
+
const updateConstraint = (patch) => {
|
|
11627
|
+
if (patch === null) {
|
|
11628
|
+
formStore.getState().updateField(selectedField.id, { dateConstraints: void 0 });
|
|
11629
|
+
return;
|
|
11630
|
+
}
|
|
11631
|
+
const current = getConstraint() || {};
|
|
11632
|
+
const next = { ...current, ...patch };
|
|
11633
|
+
formStore.getState().updateField(selectedField.id, { dateConstraints: next });
|
|
11634
|
+
};
|
|
11635
|
+
const currentConstraint = getConstraint();
|
|
11636
|
+
const operatorGroup = createElement("div", { className: "mb-3" });
|
|
11637
|
+
operatorGroup.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "Operator" }));
|
|
11638
|
+
const operatorSelect = createElement("select", {
|
|
11639
|
+
className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent text-sm",
|
|
11640
|
+
onchange: (e) => {
|
|
11641
|
+
const val = e.target.value;
|
|
11642
|
+
if (!val) {
|
|
11643
|
+
updateConstraint(null);
|
|
11644
|
+
} else {
|
|
11645
|
+
updateConstraint({ operator: val, compareWith: getConstraint()?.compareWith || "CURRENT_DATE" });
|
|
11646
|
+
}
|
|
11647
|
+
this.render();
|
|
11648
|
+
}
|
|
11649
|
+
});
|
|
11650
|
+
[
|
|
11651
|
+
{ value: "", label: "None (no constraint)" },
|
|
11652
|
+
{ value: "LESS_THAN", label: "Less than (<)" },
|
|
11653
|
+
{ value: "LESS_THAN_EQUAL", label: "Less than or equal (\u2264)" },
|
|
11654
|
+
{ value: "GREATER_THAN", label: "Greater than (>)" },
|
|
11655
|
+
{ value: "GREATER_THAN_EQUAL", label: "Greater than or equal (\u2265)" }
|
|
11656
|
+
].forEach((opt) => {
|
|
11657
|
+
const option2 = createElement("option", { value: opt.value, text: opt.label });
|
|
11658
|
+
if ((currentConstraint?.operator || "") === opt.value)
|
|
11659
|
+
option2.selected = true;
|
|
11660
|
+
operatorSelect.appendChild(option2);
|
|
11661
|
+
});
|
|
11662
|
+
operatorGroup.appendChild(operatorSelect);
|
|
11663
|
+
body.appendChild(operatorGroup);
|
|
11664
|
+
if (currentConstraint?.operator) {
|
|
11665
|
+
const compareGroup = createElement("div", { className: "mb-3" });
|
|
11666
|
+
compareGroup.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "Compare With" }));
|
|
11667
|
+
const compareSelect = createElement("select", {
|
|
11668
|
+
className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent text-sm",
|
|
11669
|
+
onchange: (e) => {
|
|
11670
|
+
const val = e.target.value;
|
|
11671
|
+
updateConstraint({ compareWith: val, fieldName: val === "CURRENT_DATE" ? void 0 : getConstraint()?.fieldName });
|
|
11672
|
+
this.render();
|
|
11673
|
+
}
|
|
11674
|
+
});
|
|
11675
|
+
[
|
|
11676
|
+
{ value: "CURRENT_DATE", label: "Current date (today)" },
|
|
11677
|
+
{ value: "FIELD", label: "Another date/datetime field" }
|
|
11678
|
+
].forEach((opt) => {
|
|
11679
|
+
const option2 = createElement("option", { value: opt.value, text: opt.label });
|
|
11680
|
+
if ((currentConstraint?.compareWith || "CURRENT_DATE") === opt.value)
|
|
11681
|
+
option2.selected = true;
|
|
11682
|
+
compareSelect.appendChild(option2);
|
|
11683
|
+
});
|
|
11684
|
+
compareGroup.appendChild(compareSelect);
|
|
11685
|
+
body.appendChild(compareGroup);
|
|
11686
|
+
if ((currentConstraint?.compareWith || "CURRENT_DATE") === "FIELD") {
|
|
11687
|
+
const otherDateFields = formStore.getState().schema.sections.flatMap((s) => s.fields).filter((f) => (f.type === "date" || f.type === "datetime") && f.id !== selectedField.id);
|
|
11688
|
+
const fieldGroup = createElement("div", { className: "mb-3" });
|
|
11689
|
+
fieldGroup.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "Reference Field" }));
|
|
11690
|
+
if (otherDateFields.length === 0) {
|
|
11691
|
+
fieldGroup.appendChild(createElement("p", {
|
|
11692
|
+
className: "text-xs text-gray-400 dark:text-gray-500 italic",
|
|
11693
|
+
text: "No other date or datetime fields in this form."
|
|
11694
|
+
}));
|
|
11695
|
+
} else {
|
|
11696
|
+
const fieldSelect = createElement("select", {
|
|
11697
|
+
className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent text-sm",
|
|
11698
|
+
onchange: (e) => {
|
|
11699
|
+
const val = e.target.value;
|
|
11700
|
+
updateConstraint({ fieldName: val || void 0 });
|
|
11701
|
+
}
|
|
11702
|
+
});
|
|
11703
|
+
fieldSelect.appendChild(createElement("option", { value: "", text: "\u2014 Select a field \u2014" }));
|
|
11704
|
+
otherDateFields.forEach((f) => {
|
|
11705
|
+
const refKey = f.fieldName || f.id;
|
|
11706
|
+
const option2 = createElement("option", { value: refKey, text: `${f.label} (${f.type})` });
|
|
11707
|
+
if (currentConstraint?.fieldName === refKey)
|
|
11708
|
+
option2.selected = true;
|
|
11709
|
+
fieldSelect.appendChild(option2);
|
|
11710
|
+
});
|
|
11711
|
+
fieldGroup.appendChild(fieldSelect);
|
|
11712
|
+
}
|
|
11713
|
+
body.appendChild(fieldGroup);
|
|
11714
|
+
}
|
|
11715
|
+
}
|
|
11716
|
+
}
|
|
11327
11717
|
const cssHeader = createElement("h3", { className: "text-xs font-semibold text-gray-500 uppercase tracking-wider mb-3 mt-6", text: "Styling" });
|
|
11328
11718
|
body.appendChild(cssHeader);
|
|
11329
11719
|
const getStyleValue = (prop) => selectedField.css?.style?.[prop] || "";
|