form-builder-pro 1.2.8 → 1.2.9

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.mjs CHANGED
@@ -4119,7 +4119,8 @@ var FIELD_TYPES = [
4119
4119
  { type: "checkbox", label: "Checkbox", icon: "CheckSquare" },
4120
4120
  { type: "radio", label: "Radio Group", icon: "CircleDot" },
4121
4121
  { type: "toggle", label: "Toggle", icon: "ToggleSwitch" },
4122
- { type: "file", label: "File Upload", icon: "Upload" }
4122
+ { type: "file", label: "File Upload", icon: "Upload" },
4123
+ { type: "image", label: "Image", icon: "Image" }
4123
4124
  ];
4124
4125
  var DEFAULT_FIELD_CONFIG = {
4125
4126
  text: { label: "Text Input", placeholder: "Enter text...", width: "100%", enabled: true, visible: true },
@@ -4145,7 +4146,39 @@ var DEFAULT_FIELD_CONFIG = {
4145
4146
  checkbox: { label: "Checkbox", options: [], width: "100%", enabled: true, visible: true },
4146
4147
  radio: { label: "Radio Group", options: [], width: "100%", enabled: true, visible: true },
4147
4148
  toggle: { label: "Toggle", width: "50%", enabled: true, visible: true },
4148
- file: { label: "File Upload", width: "100%", enabled: true, visible: true }
4149
+ file: { label: "File Upload", width: "100%", enabled: true, visible: true },
4150
+ image: { label: "Image", width: "50%", enabled: true, visible: true }
4151
+ };
4152
+ var VALIDATION_TYPE_PRESETS = {
4153
+ postalCode: {
4154
+ pattern: "^[0-9]{6}$",
4155
+ minLength: 6,
4156
+ maxLength: 6,
4157
+ validationType: "postalCode",
4158
+ customErrorMessages: { pattern: "Please enter a valid 6-digit postal code" }
4159
+ },
4160
+ phoneNumber: {
4161
+ pattern: "^[6-9][0-9]{9}$",
4162
+ minLength: 10,
4163
+ maxLength: 10,
4164
+ validationType: "phoneNumber",
4165
+ customErrorMessages: { pattern: "Please enter a valid 10-digit mobile number starting with 6-9" }
4166
+ },
4167
+ otp: {
4168
+ pattern: "^[0-9]{4,6}$",
4169
+ minLength: 4,
4170
+ maxLength: 6,
4171
+ validationType: "otp",
4172
+ customErrorMessages: { pattern: "Please enter a valid OTP (4-6 digits)" }
4173
+ },
4174
+ amount: {
4175
+ min: 0,
4176
+ allowDecimal: true,
4177
+ decimalPlaces: 2,
4178
+ allowNegative: false,
4179
+ validationType: "amount",
4180
+ customErrorMessages: { min: "Amount must be at least 0" }
4181
+ }
4149
4182
  };
4150
4183
  var REGEX_PRESETS = [
4151
4184
  {
@@ -4271,11 +4304,72 @@ var cloneField = (field) => {
4271
4304
  id: generateId(),
4272
4305
  // Ensure options are also cloned if present
4273
4306
  options: field.options ? field.options.map((opt) => ({ ...opt })) : void 0,
4274
- validation: field.validation ? field.validation.map((v) => ({ ...v })) : void 0
4307
+ validation: field.validation ? field.validation.map((v) => ({ ...v })) : void 0,
4308
+ // Preserve formula config for number fields (dependencies stay as-is - they reference other field ids/names)
4309
+ valueSource: field.valueSource,
4310
+ formula: field.formula,
4311
+ dependencies: field.dependencies ? [...field.dependencies] : void 0
4275
4312
  };
4276
4313
  };
4277
4314
 
4278
4315
  // src/utils/mapper.ts
4316
+ function validationsToValidationObject(v) {
4317
+ if (!v)
4318
+ return void 0;
4319
+ const obj = {};
4320
+ if (v.required !== void 0)
4321
+ obj.required = v.required;
4322
+ if (v.pattern)
4323
+ obj.regex = v.pattern;
4324
+ if (v.minLength !== void 0)
4325
+ obj.minLength = v.minLength;
4326
+ if (v.maxLength !== void 0)
4327
+ obj.maxLength = v.maxLength;
4328
+ if (v.min !== void 0)
4329
+ obj.min = v.min;
4330
+ if (v.max !== void 0)
4331
+ obj.max = v.max;
4332
+ if (v.customErrorMessages?.pattern)
4333
+ obj.regexMessage = v.customErrorMessages.pattern;
4334
+ if (v.minSelected !== void 0)
4335
+ obj.minSelected = v.minSelected;
4336
+ if (v.maxSelected !== void 0)
4337
+ obj.maxSelected = v.maxSelected;
4338
+ if (v.minDate)
4339
+ obj.minDate = v.minDate;
4340
+ if (v.maxDate)
4341
+ obj.maxDate = v.maxDate;
4342
+ return Object.keys(obj).length > 0 ? obj : void 0;
4343
+ }
4344
+ function validationObjectToValidations(v) {
4345
+ if (!v)
4346
+ return void 0;
4347
+ const validations = {};
4348
+ if (v.required !== void 0)
4349
+ validations.required = v.required;
4350
+ if (v.regex)
4351
+ validations.pattern = v.regex;
4352
+ if (v.minLength !== void 0)
4353
+ validations.minLength = v.minLength;
4354
+ if (v.maxLength !== void 0)
4355
+ validations.maxLength = v.maxLength;
4356
+ if (v.min !== void 0)
4357
+ validations.min = v.min;
4358
+ if (v.max !== void 0)
4359
+ validations.max = v.max;
4360
+ if (v.regexMessage) {
4361
+ validations.customErrorMessages = { pattern: v.regexMessage };
4362
+ }
4363
+ if (v.minSelected !== void 0)
4364
+ validations.minSelected = v.minSelected;
4365
+ if (v.maxSelected !== void 0)
4366
+ validations.maxSelected = v.maxSelected;
4367
+ if (v.minDate)
4368
+ validations.minDate = v.minDate;
4369
+ if (v.maxDate)
4370
+ validations.maxDate = v.maxDate;
4371
+ return Object.keys(validations).length > 0 ? validations : void 0;
4372
+ }
4279
4373
  function convertValidationObjectToArray(validationObj) {
4280
4374
  if (!validationObj)
4281
4375
  return [];
@@ -4284,7 +4378,7 @@ function convertValidationObjectToArray(validationObj) {
4284
4378
  }
4285
4379
  const rules = [];
4286
4380
  const obj = validationObj;
4287
- const hasValidationProperties = !!(obj.regex || obj.regexMessage || obj.minLength !== void 0 || obj.maxLength !== void 0 || obj.minSelected !== void 0 || obj.maxSelected !== void 0);
4381
+ 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);
4288
4382
  const isRequired = obj.required === true || hasValidationProperties && obj.required !== false;
4289
4383
  if (isRequired) {
4290
4384
  rules.push({ type: "required", value: true });
@@ -4302,6 +4396,12 @@ function convertValidationObjectToArray(validationObj) {
4302
4396
  if (obj.maxLength !== void 0) {
4303
4397
  rules.push({ type: "maxLength", value: obj.maxLength });
4304
4398
  }
4399
+ if (obj.min !== void 0) {
4400
+ rules.push({ type: "min", value: obj.min });
4401
+ }
4402
+ if (obj.max !== void 0) {
4403
+ rules.push({ type: "max", value: obj.max });
4404
+ }
4305
4405
  if (obj.minSelected !== void 0) {
4306
4406
  rules.push({ type: "minSelected", value: obj.minSelected });
4307
4407
  }
@@ -4375,7 +4475,15 @@ function transformField(field) {
4375
4475
  if (transformed.order === void 0) {
4376
4476
  transformed.order = field.order !== void 0 ? field.order : 0;
4377
4477
  }
4378
- if (field.validation) {
4478
+ if (field.validations) {
4479
+ let validations = field.validations;
4480
+ if (validations.validationType === "age") {
4481
+ validations = { ...validations, validationType: "custom" };
4482
+ }
4483
+ transformed.validations = validations;
4484
+ transformed.required = validations.required ?? false;
4485
+ transformed.validation = validationsToValidationObject(validations);
4486
+ } else if (field.validation) {
4379
4487
  if (Array.isArray(field.validation)) {
4380
4488
  const validationObj = {};
4381
4489
  field.validation.forEach((rule) => {
@@ -4388,6 +4496,10 @@ function transformField(field) {
4388
4496
  validationObj.minLength = rule.value;
4389
4497
  } else if (rule.type === "maxLength" && typeof rule.value === "number") {
4390
4498
  validationObj.maxLength = rule.value;
4499
+ } else if (rule.type === "min" && typeof rule.value === "number") {
4500
+ validationObj.min = rule.value;
4501
+ } else if (rule.type === "max" && typeof rule.value === "number") {
4502
+ validationObj.max = rule.value;
4391
4503
  } else if (rule.type === "minSelected" && typeof rule.value === "number") {
4392
4504
  validationObj.minSelected = rule.value;
4393
4505
  } else if (rule.type === "maxSelected" && typeof rule.value === "number") {
@@ -4400,13 +4512,20 @@ function transformField(field) {
4400
4512
  });
4401
4513
  transformed.validation = validationObj;
4402
4514
  transformed.required = validationObj.required || false;
4515
+ transformed.validations = validationObjectToValidations(validationObj);
4403
4516
  } else {
4404
4517
  transformed.validation = field.validation;
4405
4518
  transformed.required = field.validation.required || false;
4519
+ let validations = validationObjectToValidations(field.validation);
4520
+ if (validations?.validationType === "age") {
4521
+ validations = { ...validations, validationType: "custom" };
4522
+ }
4523
+ transformed.validations = validations;
4406
4524
  }
4407
4525
  } else if (field.required !== void 0) {
4408
4526
  transformed.required = field.required;
4409
4527
  transformed.validation = { required: field.required };
4528
+ transformed.validations = { required: field.required };
4410
4529
  }
4411
4530
  if (normalizedType === "select") {
4412
4531
  if (field.multiSelect !== void 0) {
@@ -4455,6 +4574,10 @@ function transformField(field) {
4455
4574
  transformed.lookupValueField = lookupValueField;
4456
4575
  if (lookupLabelField !== void 0)
4457
4576
  transformed.lookupLabelField = lookupLabelField;
4577
+ if (field.fieldName !== void 0)
4578
+ transformed.fieldName = field.fieldName;
4579
+ else if (field.name !== void 0)
4580
+ transformed.fieldName = field.name;
4458
4581
  if (field.placeholder !== void 0)
4459
4582
  transformed.placeholder = field.placeholder;
4460
4583
  if (field.description !== void 0)
@@ -4471,6 +4594,14 @@ function transformField(field) {
4471
4594
  transformed.visible = field.visible;
4472
4595
  if (field.isd !== void 0)
4473
4596
  transformed.isd = field.isd;
4597
+ if (field.imageUrl !== void 0)
4598
+ transformed.imageUrl = field.imageUrl;
4599
+ if (field.valueSource !== void 0)
4600
+ transformed.valueSource = field.valueSource;
4601
+ if (field.formula !== void 0)
4602
+ transformed.formula = field.formula;
4603
+ if (field.dependencies !== void 0 && Array.isArray(field.dependencies))
4604
+ transformed.dependencies = field.dependencies;
4474
4605
  if (field.css !== void 0)
4475
4606
  transformed.css = field.css;
4476
4607
  if (field.optionsSource !== void 0)
@@ -4481,8 +4612,18 @@ function transformField(field) {
4481
4612
  transformed.groupName = field.groupName;
4482
4613
  if (field.masterTypeName !== void 0)
4483
4614
  transformed.masterTypeName = field.masterTypeName;
4484
- if ((normalizedType === "select" || normalizedType === "radio" || normalizedType === "checkbox") && field.options) {
4485
- transformed.options = field.options;
4615
+ if ((normalizedType === "select" || normalizedType === "radio" || normalizedType === "checkbox") && field.options && Array.isArray(field.options)) {
4616
+ transformed.options = field.options.map((opt, idx) => {
4617
+ if (opt && typeof opt === "object" && "label" in opt && "value" in opt) {
4618
+ return { label: String(opt.label), value: String(opt.value) };
4619
+ }
4620
+ const label = opt?.label ?? opt?.name ?? opt?.displayName ?? opt?.text ?? `Option ${idx + 1}`;
4621
+ const value = opt?.value ?? opt?.id ?? opt?.name ?? String(idx);
4622
+ return { label: String(label), value: String(value) };
4623
+ });
4624
+ if (normalizedType === "select" && transformed.options.length > 0 && (transformed.optionSource === "STATIC" || !transformed.optionSource) && transformed.customOptionsEnabled === void 0) {
4625
+ transformed.customOptionsEnabled = true;
4626
+ }
4486
4627
  }
4487
4628
  return transformed;
4488
4629
  }
@@ -4552,6 +4693,10 @@ function convertValidationArrayToObject(validation) {
4552
4693
  obj.minLength = rule.value;
4553
4694
  } else if (rule.type === "maxLength" && typeof rule.value === "number") {
4554
4695
  obj.maxLength = rule.value;
4696
+ } else if (rule.type === "min" && typeof rule.value === "number") {
4697
+ obj.min = rule.value;
4698
+ } else if (rule.type === "max" && typeof rule.value === "number") {
4699
+ obj.max = rule.value;
4555
4700
  } else if (rule.type === "minSelected" && typeof rule.value === "number") {
4556
4701
  obj.minSelected = rule.value;
4557
4702
  } else if (rule.type === "maxSelected" && typeof rule.value === "number") {
@@ -4575,6 +4720,8 @@ function fieldToPayload(field) {
4575
4720
  id: field.id,
4576
4721
  type: field.type,
4577
4722
  label: field.label,
4723
+ name: field.fieldName || field.id,
4724
+ // Model key for binding (API / host app)
4578
4725
  order: field.order !== void 0 ? field.order : 0
4579
4726
  };
4580
4727
  if (field.layout?.span !== void 0) {
@@ -4596,8 +4743,18 @@ function fieldToPayload(field) {
4596
4743
  span: 12
4597
4744
  };
4598
4745
  }
4599
- payload.validation = convertValidationArrayToObject(field.validation);
4600
- if (payload.validation?.required) {
4746
+ if (field.validations) {
4747
+ let validations = { ...field.validations };
4748
+ if (validations.validationType === "age") {
4749
+ validations = { ...validations, validationType: "custom" };
4750
+ }
4751
+ payload.validations = validations;
4752
+ if (field.validations.required) {
4753
+ payload.required = true;
4754
+ }
4755
+ }
4756
+ payload.validation = field.validations ? validationsToValidationObject(field.validations) : convertValidationArrayToObject(field.validation);
4757
+ if (payload.validation?.required || payload.validations?.required) {
4601
4758
  payload.required = true;
4602
4759
  }
4603
4760
  if (field.type === "select") {
@@ -4633,6 +4790,14 @@ function fieldToPayload(field) {
4633
4790
  payload.lookupLabelField = field.lookupLabelField;
4634
4791
  if (field.isd !== void 0)
4635
4792
  payload.isd = field.isd;
4793
+ if (field.imageUrl !== void 0)
4794
+ payload.imageUrl = field.imageUrl;
4795
+ if (field.valueSource !== void 0)
4796
+ payload.valueSource = field.valueSource;
4797
+ if (field.formula !== void 0)
4798
+ payload.formula = field.formula;
4799
+ if (field.dependencies !== void 0 && Array.isArray(field.dependencies))
4800
+ payload.dependencies = field.dependencies;
4636
4801
  if ((field.type === "select" || field.type === "radio" || field.type === "checkbox") && field.options && Array.isArray(field.options)) {
4637
4802
  payload.options = field.options.map((opt) => ({ label: opt.label, value: opt.value }));
4638
4803
  }
@@ -4657,6 +4822,20 @@ var builderToPlatform = (builderSchema) => {
4657
4822
  var platformToBuilder = (platformSchema) => {
4658
4823
  return cleanFormSchema(platformSchema);
4659
4824
  };
4825
+ function getValidationConfigForAngular(validations) {
4826
+ if (!validations) {
4827
+ return { required: false, customErrorMessages: {} };
4828
+ }
4829
+ return {
4830
+ required: validations.required ?? false,
4831
+ minLength: validations.minLength,
4832
+ maxLength: validations.maxLength,
4833
+ min: validations.min,
4834
+ max: validations.max,
4835
+ pattern: validations.pattern,
4836
+ customErrorMessages: validations.customErrorMessages || {}
4837
+ };
4838
+ }
4660
4839
 
4661
4840
  // src/core/useFormStore.ts
4662
4841
  var INITIAL_SCHEMA = {
@@ -4961,9 +5140,10 @@ var formStore = createStore((set, get) => ({
4961
5140
  const { existingForms, history, historyIndex } = get();
4962
5141
  const found = existingForms.find((f) => f.id === formId);
4963
5142
  if (found) {
5143
+ const cleanedSchema = cleanFormSchema(found);
4964
5144
  set({
4965
- schema: found,
4966
- history: [...history.slice(0, historyIndex + 1), found],
5145
+ schema: cleanedSchema,
5146
+ history: [...history.slice(0, historyIndex + 1), cleanedSchema],
4967
5147
  historyIndex: historyIndex + 1
4968
5148
  });
4969
5149
  }
@@ -4973,9 +5153,10 @@ var formStore = createStore((set, get) => ({
4973
5153
  const found = existingForms.find((f) => f.id === formId);
4974
5154
  if (found) {
4975
5155
  const cloned = cloneForm(found);
5156
+ const cleanedSchema = cleanFormSchema(cloned);
4976
5157
  set({
4977
- schema: cloned,
4978
- history: [...history.slice(0, historyIndex + 1), cloned],
5158
+ schema: cleanedSchema,
5159
+ history: [...history.slice(0, historyIndex + 1), cleanedSchema],
4979
5160
  historyIndex: historyIndex + 1
4980
5161
  });
4981
5162
  }
@@ -5358,6 +5539,7 @@ var ICONS = {
5358
5539
  "ToggleSwitch": '<rect x="2" y="6" width="20" height="12" rx="6" ry="6" stroke-linecap="round" stroke-linejoin="round" /><circle cx="8" cy="12" r="4" stroke-linecap="round" stroke-linejoin="round" />',
5359
5540
  // Custom SVG for toggle
5360
5541
  "Upload": '<path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5m-13.5-9L12 3m0 0l4.5 4.5M12 3v13.5" />',
5542
+ "Image": '<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 001.5-1.5V6a1.5 1.5 0 00-1.5-1.5H3.75A1.5 1.5 0 002.25 6v12a1.5 1.5 0 001.5 1.5zm10.5-11.25h.008v.008h-.008V8.25zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z" />',
5361
5543
  // UI Icons
5362
5544
  "GripVertical": '<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 15L12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9" />',
5363
5545
  "GripDots": '<path stroke-linecap="round" stroke-linejoin="round" d="M12 6.75a.75.75 0 110-1.5.75.75 0 010 1.5zM12 12.75a.75.75 0 110-1.5.75.75 0 010 1.5zM12 18.75a.75.75 0 110-1.5.75.75 0 010 1.5z" />',
@@ -5390,6 +5572,224 @@ function getIcon(name, size = 20) {
5390
5572
  return svg;
5391
5573
  }
5392
5574
 
5575
+ // src/utils/formula.ts
5576
+ var FORMULA_OPERATOR_REGEX = /[\+\-\*\/\(\)]/g;
5577
+ function parseFormulaDependencies(formula) {
5578
+ if (!formula || typeof formula !== "string")
5579
+ return [];
5580
+ const tokens = formula.replace(FORMULA_OPERATOR_REGEX, " ").split(/\s+/).filter(Boolean);
5581
+ const seen = /* @__PURE__ */ new Set();
5582
+ const deps = [];
5583
+ for (const t of tokens) {
5584
+ const trimmed = t.trim();
5585
+ if (trimmed && !seen.has(trimmed)) {
5586
+ seen.add(trimmed);
5587
+ deps.push(trimmed);
5588
+ }
5589
+ }
5590
+ return deps;
5591
+ }
5592
+ function validateFormula(formula, availableFieldIds, availableFieldNames, _currentFieldId) {
5593
+ if (!formula || typeof formula !== "string") {
5594
+ return { valid: false, error: "Formula cannot be empty" };
5595
+ }
5596
+ const trimmed = formula.trim();
5597
+ if (!trimmed) {
5598
+ return { valid: false, error: "Formula cannot be empty" };
5599
+ }
5600
+ const deps = parseFormulaDependencies(trimmed);
5601
+ const validRefs = /* @__PURE__ */ new Set([...availableFieldIds, ...availableFieldNames]);
5602
+ for (const dep of deps) {
5603
+ if (!validRefs.has(dep)) {
5604
+ return { valid: false, error: `Unknown field reference: "${dep}"` };
5605
+ }
5606
+ }
5607
+ let open = 0;
5608
+ for (const c of trimmed) {
5609
+ if (c === "(")
5610
+ open++;
5611
+ else if (c === ")") {
5612
+ open--;
5613
+ if (open < 0)
5614
+ return { valid: false, error: "Unbalanced parentheses" };
5615
+ }
5616
+ }
5617
+ if (open !== 0)
5618
+ return { valid: false, error: "Unbalanced parentheses" };
5619
+ return { valid: true };
5620
+ }
5621
+ function detectCircularDependency(schema, formulaFieldId, _formula, dependencies) {
5622
+ const visited = /* @__PURE__ */ new Set();
5623
+ const resolveFieldByRef = (ref) => {
5624
+ for (const s of schema.sections) {
5625
+ for (const f of s.fields) {
5626
+ if (f.id === ref || f.fieldName === ref)
5627
+ return f;
5628
+ }
5629
+ }
5630
+ return void 0;
5631
+ };
5632
+ const hasCycle = (fieldId) => {
5633
+ if (visited.has(fieldId))
5634
+ return true;
5635
+ visited.add(fieldId);
5636
+ const field = resolveFieldByRef(fieldId);
5637
+ if (!field || field.type !== "number" || field.valueSource !== "formula" || !field.formula) {
5638
+ visited.delete(fieldId);
5639
+ return false;
5640
+ }
5641
+ const deps = field.dependencies ?? parseFormulaDependencies(field.formula);
5642
+ for (const dep of deps) {
5643
+ const depField = resolveFieldByRef(dep);
5644
+ if (!depField)
5645
+ continue;
5646
+ const depId = depField.id;
5647
+ if (depId === formulaFieldId) {
5648
+ visited.delete(fieldId);
5649
+ return true;
5650
+ }
5651
+ if (hasCycle(depId)) {
5652
+ visited.delete(fieldId);
5653
+ return true;
5654
+ }
5655
+ }
5656
+ visited.delete(fieldId);
5657
+ return false;
5658
+ };
5659
+ for (const dep of dependencies) {
5660
+ const depField = resolveFieldByRef(dep);
5661
+ if (!depField)
5662
+ continue;
5663
+ if (depField.id === formulaFieldId)
5664
+ return true;
5665
+ if (hasCycle(depField.id))
5666
+ return true;
5667
+ }
5668
+ return false;
5669
+ }
5670
+ function evaluateFormula(formula, values) {
5671
+ if (!formula || typeof formula !== "string")
5672
+ return NaN;
5673
+ const trimmed = formula.trim();
5674
+ if (!trimmed)
5675
+ return NaN;
5676
+ const getValue = (ref) => {
5677
+ const v = values[ref];
5678
+ if (v === void 0 || v === null || v === "")
5679
+ return 0;
5680
+ const n = typeof v === "number" ? v : parseFloat(String(v));
5681
+ return isNaN(n) ? 0 : n;
5682
+ };
5683
+ const tokens = [];
5684
+ let i = 0;
5685
+ while (i < trimmed.length) {
5686
+ const c = trimmed[i];
5687
+ if (/\s/.test(c)) {
5688
+ i++;
5689
+ continue;
5690
+ }
5691
+ if (/[\+\-\*\/\(\)]/.test(c)) {
5692
+ tokens.push(c);
5693
+ i++;
5694
+ continue;
5695
+ }
5696
+ if (/[a-zA-Z_0-9]/.test(c)) {
5697
+ let ident = "";
5698
+ while (i < trimmed.length && /[a-zA-Z0-9_]/.test(trimmed[i])) {
5699
+ ident += trimmed[i++];
5700
+ }
5701
+ tokens.push(ident);
5702
+ continue;
5703
+ }
5704
+ i++;
5705
+ }
5706
+ let pos = 0;
5707
+ const parseExpr = () => {
5708
+ let left = parseTerm();
5709
+ while (pos < tokens.length) {
5710
+ const op = tokens[pos];
5711
+ if (op === "+") {
5712
+ pos++;
5713
+ left += parseTerm();
5714
+ } else if (op === "-") {
5715
+ pos++;
5716
+ left -= parseTerm();
5717
+ } else
5718
+ break;
5719
+ }
5720
+ return left;
5721
+ };
5722
+ const parseTerm = () => {
5723
+ let left = parseFactor();
5724
+ while (pos < tokens.length) {
5725
+ const op = tokens[pos];
5726
+ if (op === "*") {
5727
+ pos++;
5728
+ left *= parseFactor();
5729
+ } else if (op === "/") {
5730
+ pos++;
5731
+ const right = parseFactor();
5732
+ if (right === 0)
5733
+ return NaN;
5734
+ left /= right;
5735
+ } else
5736
+ break;
5737
+ }
5738
+ return left;
5739
+ };
5740
+ const parseFactor = () => {
5741
+ if (pos >= tokens.length)
5742
+ return NaN;
5743
+ const t = tokens[pos];
5744
+ if (t === "(") {
5745
+ pos++;
5746
+ const v = parseExpr();
5747
+ if (pos < tokens.length && tokens[pos] === ")")
5748
+ pos++;
5749
+ return v;
5750
+ }
5751
+ if (t === "-") {
5752
+ pos++;
5753
+ return -parseFactor();
5754
+ }
5755
+ if (t === "+") {
5756
+ pos++;
5757
+ return parseFactor();
5758
+ }
5759
+ const n = parseFloat(t);
5760
+ if (!isNaN(n)) {
5761
+ pos++;
5762
+ return n;
5763
+ }
5764
+ pos++;
5765
+ return getValue(t);
5766
+ };
5767
+ try {
5768
+ const result = parseExpr();
5769
+ return isNaN(result) ? NaN : result;
5770
+ } catch {
5771
+ return NaN;
5772
+ }
5773
+ }
5774
+ function getNumericFieldsForFormula(schema, excludeFieldId) {
5775
+ const result = [];
5776
+ for (const section of schema.sections) {
5777
+ for (const field of section.fields) {
5778
+ if (field.type !== "number")
5779
+ continue;
5780
+ if (excludeFieldId && field.id === excludeFieldId)
5781
+ continue;
5782
+ const fieldName = field.fieldName ?? field.id;
5783
+ result.push({
5784
+ id: field.id,
5785
+ fieldName,
5786
+ label: field.label || fieldName
5787
+ });
5788
+ }
5789
+ }
5790
+ return result;
5791
+ }
5792
+
5393
5793
  // src/core/countryData.ts
5394
5794
  var COUNTRY_DATA = [
5395
5795
  { code: "US", name: "United States", dialCode: "+1", flag: "\u{1F1FA}\u{1F1F8}" },
@@ -5445,6 +5845,64 @@ function getDefaultCountry() {
5445
5845
  }
5446
5846
 
5447
5847
  // src/renderer/FieldRenderer.ts
5848
+ function getValidationRules(field) {
5849
+ const v = field.validations;
5850
+ if (v) {
5851
+ return {
5852
+ required: v.required,
5853
+ pattern: v.pattern,
5854
+ patternMessage: v.customErrorMessages?.pattern,
5855
+ minLength: v.minLength,
5856
+ maxLength: v.maxLength,
5857
+ min: v.min,
5858
+ max: v.max,
5859
+ minDate: v.minDate,
5860
+ maxDate: v.maxDate
5861
+ };
5862
+ }
5863
+ const val = field.validation;
5864
+ if (!val)
5865
+ return {};
5866
+ if (Array.isArray(val)) {
5867
+ const rules = {};
5868
+ val.forEach((r) => {
5869
+ if (r.type === "required")
5870
+ rules.required = true;
5871
+ else if (r.type === "pattern") {
5872
+ rules.pattern = r.regex;
5873
+ rules.patternMessage = r.message;
5874
+ } else if (r.type === "minLength")
5875
+ rules.minLength = r.value;
5876
+ else if (r.type === "maxLength")
5877
+ rules.maxLength = r.value;
5878
+ else if (r.type === "min")
5879
+ rules.min = r.value;
5880
+ else if (r.type === "max")
5881
+ rules.max = r.value;
5882
+ else if (r.type === "minDate")
5883
+ rules.minDate = r.value;
5884
+ else if (r.type === "maxDate")
5885
+ rules.maxDate = r.value;
5886
+ });
5887
+ return rules;
5888
+ }
5889
+ const o = val;
5890
+ return {
5891
+ required: o.required,
5892
+ pattern: o.regex,
5893
+ patternMessage: o.regexMessage,
5894
+ minLength: o.minLength,
5895
+ maxLength: o.maxLength,
5896
+ min: o.min,
5897
+ max: o.max,
5898
+ minDate: o.minDate,
5899
+ maxDate: o.maxDate
5900
+ };
5901
+ }
5902
+ function isNumericTextField(field) {
5903
+ const v = field.validations;
5904
+ return !!(v?.validationType && ["postalCode", "phoneNumber", "otp"].includes(v.validationType));
5905
+ }
5448
5906
  var FieldRenderer = class {
5449
5907
  static render(field, value, onChange, readOnly = false) {
5450
5908
  const wrapper = createElement("div", { className: "w-full form-row" });
@@ -5479,89 +5937,71 @@ var FieldRenderer = class {
5479
5937
  }
5480
5938
  let input;
5481
5939
  let validationMsg = null;
5482
- const validationArray = Array.isArray(field.validation) ? field.validation : field.validation ? (() => {
5483
- const obj = field.validation;
5484
- const rules = [];
5485
- if (obj.required)
5486
- rules.push({ type: "required", value: true });
5487
- if (obj.regex)
5488
- rules.push({ type: "pattern", regex: obj.regex, message: obj.regexMessage });
5489
- if (obj.minLength !== void 0)
5490
- rules.push({ type: "minLength", value: obj.minLength });
5491
- if (obj.maxLength !== void 0)
5492
- rules.push({ type: "maxLength", value: obj.maxLength });
5493
- if (obj.minSelected !== void 0)
5494
- rules.push({ type: "minSelected", value: obj.minSelected });
5495
- if (obj.maxSelected !== void 0)
5496
- rules.push({ type: "maxSelected", value: obj.maxSelected });
5497
- if (obj.minDate)
5498
- rules.push({ type: "minDate", value: obj.minDate });
5499
- if (obj.maxDate)
5500
- rules.push({ type: "maxDate", value: obj.maxDate });
5501
- return rules;
5502
- })() : [];
5503
- const hasPatternValidation = validationArray.some((v) => v.type === "pattern");
5504
- if (field.type === "date" || field.type === "email" || field.type === "text" && hasPatternValidation) {
5940
+ const validationRules = getValidationRules(field);
5941
+ const hasPatternValidation = !!validationRules.pattern;
5942
+ const hasNumericTextValidation = isNumericTextField(field);
5943
+ if (field.type === "date" || field.type === "email" || field.type === "number" || field.type === "text" && (hasPatternValidation || hasNumericTextValidation || validationRules.minLength !== void 0 || validationRules.maxLength !== void 0)) {
5505
5944
  validationMsg = createElement("div", { className: "text-xs text-red-600 dark:text-red-400 mt-1 hidden", id: `validation-${field.id}` });
5506
5945
  }
5507
- const validateField = (field2, value2, inputElement, validationMsg2) => {
5946
+ const validateField = (f, value2, inputElement, msgEl) => {
5947
+ const rules = getValidationRules(f);
5948
+ const custom2 = f.validations?.customErrorMessages;
5508
5949
  let errorMessage = "";
5509
- const validationArray2 = Array.isArray(field2.validation) ? field2.validation : field2.validation ? (() => {
5510
- const obj = field2.validation;
5511
- const rules = [];
5512
- if (obj.required)
5513
- rules.push({ type: "required", value: true });
5514
- if (obj.regex)
5515
- rules.push({ type: "pattern", regex: obj.regex, message: obj.regexMessage });
5516
- if (obj.minLength !== void 0)
5517
- rules.push({ type: "minLength", value: obj.minLength });
5518
- if (obj.maxLength !== void 0)
5519
- rules.push({ type: "maxLength", value: obj.maxLength });
5520
- if (obj.minSelected !== void 0)
5521
- rules.push({ type: "minSelected", value: obj.minSelected });
5522
- if (obj.maxSelected !== void 0)
5523
- rules.push({ type: "maxSelected", value: obj.maxSelected });
5524
- if (obj.minDate)
5525
- rules.push({ type: "minDate", value: obj.minDate });
5526
- if (obj.maxDate)
5527
- rules.push({ type: "maxDate", value: obj.maxDate });
5528
- return rules;
5529
- })() : [];
5530
- if (field2.type === "date" && value2) {
5531
- const minDateRule = validationArray2.find((v) => v.type === "minDate");
5532
- const maxDateRule = validationArray2.find((v) => v.type === "maxDate");
5950
+ if (rules.required && (!value2 || String(value2).trim() === "")) {
5951
+ errorMessage = custom2?.required || "This field is required";
5952
+ }
5953
+ if (!errorMessage && f.type === "date" && value2) {
5533
5954
  const inputDate = new Date(value2);
5534
- if (minDateRule?.value) {
5535
- const minDate = new Date(minDateRule.value);
5955
+ if (rules.minDate) {
5956
+ const minDate = new Date(rules.minDate);
5536
5957
  if (inputDate < minDate) {
5537
- errorMessage = minDateRule.message || "Date must be after the minimum date";
5958
+ errorMessage = "Date must be after the minimum date";
5538
5959
  }
5539
5960
  }
5540
- if (maxDateRule?.value && !errorMessage) {
5541
- const maxDate = new Date(maxDateRule.value);
5961
+ if (!errorMessage && rules.maxDate) {
5962
+ const maxDate = new Date(rules.maxDate);
5542
5963
  if (inputDate > maxDate) {
5543
- errorMessage = maxDateRule.message || "Date must be before the maximum date";
5964
+ errorMessage = "Date must be before the maximum date";
5544
5965
  }
5545
5966
  }
5546
5967
  }
5547
- if ((field2.type === "email" || field2.type === "text") && value2) {
5548
- const patternRule = validationArray2.find((v) => v.type === "pattern");
5549
- if (patternRule?.regex) {
5550
- try {
5551
- const regex = new RegExp(patternRule.regex);
5552
- if (!regex.test(value2)) {
5553
- errorMessage = patternRule.message || "Invalid format";
5554
- }
5555
- } catch (e) {
5968
+ if (!errorMessage && (f.type === "email" || f.type === "text" || f.type === "phone") && value2 && rules.pattern) {
5969
+ try {
5970
+ if (!new RegExp(rules.pattern).test(value2)) {
5971
+ errorMessage = rules.patternMessage || custom2?.pattern || "Invalid format";
5972
+ }
5973
+ } catch (_e) {
5974
+ }
5975
+ }
5976
+ if (!errorMessage && value2 && (f.type === "text" || f.type === "phone")) {
5977
+ const len = String(value2).length;
5978
+ if (rules.minLength !== void 0 && len < rules.minLength) {
5979
+ errorMessage = custom2?.minLength || `Minimum length is ${rules.minLength}`;
5980
+ }
5981
+ if (!errorMessage && rules.maxLength !== void 0 && len > rules.maxLength) {
5982
+ errorMessage = custom2?.maxLength || `Maximum length is ${rules.maxLength}`;
5983
+ }
5984
+ }
5985
+ if (!errorMessage && f.type === "number" && value2 !== "" && value2 !== void 0) {
5986
+ const num = parseFloat(String(value2));
5987
+ if (!isNaN(num)) {
5988
+ if (f.validations?.allowNegative === false && num < 0) {
5989
+ errorMessage = custom2?.min || "Negative values are not allowed";
5990
+ }
5991
+ if (!errorMessage && rules.min !== void 0 && num < rules.min) {
5992
+ errorMessage = custom2?.min || `Value must be at least ${rules.min}`;
5993
+ }
5994
+ if (!errorMessage && rules.max !== void 0 && num > rules.max) {
5995
+ errorMessage = custom2?.max || `Value must be at most ${rules.max}`;
5556
5996
  }
5557
5997
  }
5558
5998
  }
5559
5999
  if (errorMessage) {
5560
- validationMsg2.textContent = errorMessage;
5561
- validationMsg2.classList.remove("hidden");
6000
+ msgEl.textContent = errorMessage;
6001
+ msgEl.classList.remove("hidden");
5562
6002
  inputElement.classList.add("border-red-500");
5563
6003
  } else {
5564
- validationMsg2.classList.add("hidden");
6004
+ msgEl.classList.add("hidden");
5565
6005
  inputElement.classList.remove("border-red-500");
5566
6006
  }
5567
6007
  };
@@ -5695,59 +6135,40 @@ var FieldRenderer = class {
5695
6135
  case "phone":
5696
6136
  input = this.renderPhoneField(field, value, onChange, isEnabled);
5697
6137
  break;
6138
+ case "image":
6139
+ input = this.renderImageField(field, value ?? field.imageUrl ?? field.defaultValue, onChange, isEnabled);
6140
+ break;
5698
6141
  default:
5699
- const fieldValidationArray = Array.isArray(field.validation) ? field.validation : field.validation ? (() => {
5700
- const obj = field.validation;
5701
- const rules = [];
5702
- if (obj.required)
5703
- rules.push({ type: "required", value: true });
5704
- if (obj.regex)
5705
- rules.push({ type: "pattern", regex: obj.regex, message: obj.regexMessage });
5706
- if (obj.minLength !== void 0)
5707
- rules.push({ type: "minLength", value: obj.minLength });
5708
- if (obj.maxLength !== void 0)
5709
- rules.push({ type: "maxLength", value: obj.maxLength });
5710
- if (obj.minSelected !== void 0)
5711
- rules.push({ type: "minSelected", value: obj.minSelected });
5712
- if (obj.maxSelected !== void 0)
5713
- rules.push({ type: "maxSelected", value: obj.maxSelected });
5714
- if (obj.minDate)
5715
- rules.push({ type: "minDate", value: obj.minDate });
5716
- if (obj.maxDate)
5717
- rules.push({ type: "maxDate", value: obj.maxDate });
5718
- return rules;
5719
- })() : [];
5720
- const patternRule = fieldValidationArray.find((v) => v.type === "pattern");
5721
- const patternRegex = patternRule?.regex;
6142
+ const rules = getValidationRules(field);
6143
+ const useNumericTextInput = field.type === "text" && isNumericTextField(field);
6144
+ const inputType = useNumericTextInput ? "text" : field.type === "number" ? "number" : field.type;
6145
+ const runValidation = () => {
6146
+ if (validationMsg && (field.type === "date" || field.type === "email" || field.type === "text" || field.type === "number")) {
6147
+ validateField(field, input.value, input, validationMsg);
6148
+ }
6149
+ };
5722
6150
  input = createElement("input", {
5723
- type: field.type,
6151
+ type: inputType,
6152
+ ...useNumericTextInput && { inputmode: "numeric" },
5724
6153
  className: "flex min-h-touch w-full rounded-md border border-input bg-background px-3 py-2 text-sm sm:text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
5725
- // type: field.type === 'phone' ? 'tel' : field.type,
5726
- // className: 'flex min-h-touch w-full rounded-md border border-gray-300 bg-background px-3 py-2 text-sm sm:text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50',
5727
6154
  placeholder: field.placeholder,
5728
6155
  value: value || "",
5729
6156
  disabled: !isEnabled,
5730
- min: field.type === "date" ? fieldValidationArray.find((v) => v.type === "minDate")?.value : void 0,
5731
- max: field.type === "date" ? fieldValidationArray.find((v) => v.type === "maxDate")?.value : void 0,
5732
- pattern: patternRegex || void 0,
5733
- // Apply pattern for both email and text fields
6157
+ min: field.type === "date" ? rules.minDate : field.type === "number" ? rules.min !== void 0 ? String(rules.min) : void 0 : void 0,
6158
+ max: field.type === "date" ? rules.maxDate : field.type === "number" ? rules.max !== void 0 ? String(rules.max) : void 0 : void 0,
6159
+ pattern: !useNumericTextInput ? rules.pattern || void 0 : void 0,
5734
6160
  oninput: (e) => {
5735
- const inputValue = e.target.value;
5736
- onChange?.(inputValue);
5737
- if (validationMsg && (field.type === "date" || field.type === "email" || field.type === "text" && hasPatternValidation)) {
5738
- validateField(field, inputValue, input, validationMsg);
5739
- }
5740
- },
5741
- onchange: (e) => {
5742
- if (validationMsg && (field.type === "date" || field.type === "email" || field.type === "text" && hasPatternValidation)) {
5743
- validateField(field, e.target.value, input, validationMsg);
6161
+ const inputEl = e.target;
6162
+ let inputValue = inputEl.value;
6163
+ if (useNumericTextInput) {
6164
+ inputValue = inputValue.replace(/[eE+-]/g, "");
6165
+ inputEl.value = inputValue;
5744
6166
  }
6167
+ onChange?.(inputValue);
6168
+ runValidation();
5745
6169
  },
5746
- onblur: (e) => {
5747
- if (validationMsg && (field.type === "date" || field.type === "email" || field.type === "text" && hasPatternValidation)) {
5748
- validateField(field, e.target.value, input, validationMsg);
5749
- }
5750
- }
6170
+ onchange: () => runValidation(),
6171
+ onblur: () => runValidation()
5751
6172
  });
5752
6173
  }
5753
6174
  wrapper.appendChild(input);
@@ -5843,6 +6264,93 @@ var FieldRenderer = class {
5843
6264
  });
5844
6265
  return container;
5845
6266
  }
6267
+ /**
6268
+ * Render image field with upload, preview, and remove/replace
6269
+ */
6270
+ static renderImageField(field, imageValue, onChange, isEnabled = true) {
6271
+ const ACCEPT = "image/jpeg,image/png,image/gif,image/webp";
6272
+ const MAX_SIZE_MB = 5;
6273
+ const MAX_SIZE_BYTES = MAX_SIZE_MB * 1024 * 1024;
6274
+ const container = createElement("div", { className: "image-field-wrapper flex flex-col gap-3 w-full" });
6275
+ const previewWrap = createElement("div", {
6276
+ className: "relative rounded-md border border-input bg-gray-50 dark:bg-gray-800 overflow-hidden min-h-[120px] flex items-center justify-center"
6277
+ });
6278
+ if (imageValue) {
6279
+ const img = createElement("img", {
6280
+ src: imageValue,
6281
+ alt: field.label || "Uploaded image",
6282
+ className: "max-h-48 max-w-full object-contain"
6283
+ });
6284
+ img.onerror = () => {
6285
+ previewWrap.innerHTML = "";
6286
+ previewWrap.appendChild(createElement("p", {
6287
+ className: "text-xs text-red-500 p-4",
6288
+ text: "Failed to load image"
6289
+ }));
6290
+ };
6291
+ previewWrap.appendChild(img);
6292
+ if (isEnabled) {
6293
+ const removeBtn = createElement("button", {
6294
+ type: "button",
6295
+ className: "absolute top-2 right-2 p-1.5 bg-red-500 text-white rounded-md hover:bg-red-600 text-xs",
6296
+ title: "Remove image",
6297
+ onclick: () => onChange?.(void 0)
6298
+ });
6299
+ removeBtn.innerHTML = "\xD7";
6300
+ previewWrap.appendChild(removeBtn);
6301
+ }
6302
+ } else {
6303
+ previewWrap.appendChild(createElement("p", {
6304
+ className: "text-xs text-muted-foreground p-4",
6305
+ text: "No image uploaded"
6306
+ }));
6307
+ }
6308
+ container.appendChild(previewWrap);
6309
+ if (isEnabled) {
6310
+ const fileInput = createElement("input", {
6311
+ type: "file",
6312
+ accept: ACCEPT,
6313
+ className: "hidden",
6314
+ id: `image-upload-${field.id}`
6315
+ });
6316
+ const uploadBtn = createElement("button", {
6317
+ type: "button",
6318
+ className: "px-3 py-2 text-sm border border-gray-300 dark:border-gray-700 rounded-md hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors disabled:opacity-50 disabled:cursor-not-allowed",
6319
+ text: imageValue ? "Replace" : "Upload Image",
6320
+ onclick: () => fileInput.click()
6321
+ });
6322
+ fileInput.onchange = (e) => {
6323
+ const target = e.target;
6324
+ const file = target.files?.[0];
6325
+ if (!file)
6326
+ return;
6327
+ if (!file.type.match(/^image\/(jpeg|png|gif|webp)$/)) {
6328
+ alert(`Please select a valid image (JPG, PNG, GIF, WebP)`);
6329
+ target.value = "";
6330
+ return;
6331
+ }
6332
+ if (file.size > MAX_SIZE_BYTES) {
6333
+ alert(`Image must be under ${MAX_SIZE_MB}MB`);
6334
+ target.value = "";
6335
+ return;
6336
+ }
6337
+ const reader = new FileReader();
6338
+ reader.onload = () => {
6339
+ const result = reader.result;
6340
+ if (result)
6341
+ onChange?.(result);
6342
+ };
6343
+ reader.onerror = () => {
6344
+ alert("Failed to read image");
6345
+ };
6346
+ reader.readAsDataURL(file);
6347
+ target.value = "";
6348
+ };
6349
+ container.appendChild(fileInput);
6350
+ container.appendChild(uploadBtn);
6351
+ }
6352
+ return container;
6353
+ }
5846
6354
  };
5847
6355
 
5848
6356
  // src/renderer/FormRenderer.ts
@@ -5861,6 +6369,10 @@ function convertValidationToArray(validation) {
5861
6369
  rules.push({ type: "minLength", value: obj.minLength });
5862
6370
  if (obj.maxLength !== void 0)
5863
6371
  rules.push({ type: "maxLength", value: obj.maxLength });
6372
+ if (obj.min !== void 0)
6373
+ rules.push({ type: "min", value: obj.min });
6374
+ if (obj.max !== void 0)
6375
+ rules.push({ type: "max", value: obj.max });
5864
6376
  if (obj.minSelected !== void 0)
5865
6377
  rules.push({ type: "minSelected", value: obj.minSelected });
5866
6378
  if (obj.maxSelected !== void 0)
@@ -5871,9 +6383,146 @@ function convertValidationToArray(validation) {
5871
6383
  rules.push({ type: "maxDate", value: obj.maxDate });
5872
6384
  return rules;
5873
6385
  }
6386
+ function getValidationRulesForField(field) {
6387
+ const v = field.validations;
6388
+ if (v) {
6389
+ const obj = {};
6390
+ if (v.required)
6391
+ obj.required = true;
6392
+ if (v.pattern)
6393
+ obj.regex = v.pattern;
6394
+ if (v.customErrorMessages?.pattern)
6395
+ obj.regexMessage = v.customErrorMessages.pattern;
6396
+ if (v.minLength !== void 0)
6397
+ obj.minLength = v.minLength;
6398
+ if (v.maxLength !== void 0)
6399
+ obj.maxLength = v.maxLength;
6400
+ if (v.min !== void 0)
6401
+ obj.min = v.min;
6402
+ if (v.max !== void 0)
6403
+ obj.max = v.max;
6404
+ if (v.minSelected !== void 0)
6405
+ obj.minSelected = v.minSelected;
6406
+ if (v.maxSelected !== void 0)
6407
+ obj.maxSelected = v.maxSelected;
6408
+ if (v.minDate)
6409
+ obj.minDate = v.minDate;
6410
+ if (v.maxDate)
6411
+ obj.maxDate = v.maxDate;
6412
+ return convertValidationToArray(obj);
6413
+ }
6414
+ return convertValidationToArray(field.validation);
6415
+ }
6416
+ function getFieldValidationError(field, fieldValue) {
6417
+ const isRequired = field.validations?.required ?? field.required;
6418
+ if (isRequired && (!fieldValue || fieldValue === "" || Array.isArray(fieldValue) && fieldValue.length === 0)) {
6419
+ return field.validations?.customErrorMessages?.required || "This field is required";
6420
+ }
6421
+ if ((field.type === "text" || field.type === "email" || field.type === "phone") && fieldValue) {
6422
+ const validationArray = getValidationRulesForField(field);
6423
+ const patternRule = validationArray.find((v) => v.type === "pattern");
6424
+ if (patternRule?.regex) {
6425
+ try {
6426
+ if (!new RegExp(patternRule.regex).test(String(fieldValue))) {
6427
+ return field.validations?.customErrorMessages?.pattern || patternRule.message || "Invalid format";
6428
+ }
6429
+ } catch (_e) {
6430
+ }
6431
+ }
6432
+ const minLenRule = validationArray.find((v) => v.type === "minLength");
6433
+ const maxLenRule = validationArray.find((v) => v.type === "maxLength");
6434
+ const len = String(fieldValue).length;
6435
+ if (minLenRule && typeof minLenRule.value === "number" && len < minLenRule.value) {
6436
+ return field.validations?.customErrorMessages?.minLength || `Minimum length is ${minLenRule.value}`;
6437
+ }
6438
+ if (maxLenRule && typeof maxLenRule.value === "number" && len > maxLenRule.value) {
6439
+ return field.validations?.customErrorMessages?.maxLength || `Maximum length is ${maxLenRule.value}`;
6440
+ }
6441
+ }
6442
+ if (field.type === "number" && fieldValue !== "" && fieldValue !== void 0) {
6443
+ const v = field.validations;
6444
+ const num = parseFloat(String(fieldValue));
6445
+ if (!isNaN(num)) {
6446
+ if (v?.min !== void 0 && num < v.min) {
6447
+ return v.customErrorMessages?.min || `Value must be at least ${v.min}`;
6448
+ }
6449
+ if (v?.max !== void 0 && num > v.max) {
6450
+ return v.customErrorMessages?.max || `Value must be at most ${v.max}`;
6451
+ }
6452
+ if (v?.allowNegative === false && num < 0) {
6453
+ return v.customErrorMessages?.min || "Negative values are not allowed";
6454
+ }
6455
+ }
6456
+ }
6457
+ if (field.type === "checkbox" && Array.isArray(fieldValue)) {
6458
+ const validationArray = getValidationRulesForField(field);
6459
+ const minSelectedRule = validationArray.find((v) => v.type === "minSelected");
6460
+ const maxSelectedRule = validationArray.find((v) => v.type === "maxSelected");
6461
+ const selectedCount = fieldValue.length;
6462
+ const minSelected = typeof minSelectedRule?.value === "number" ? minSelectedRule.value : void 0;
6463
+ const maxSelected = typeof maxSelectedRule?.value === "number" ? maxSelectedRule.value : void 0;
6464
+ if (minSelected !== void 0 && selectedCount < minSelected) {
6465
+ return `Please select at least ${minSelected} option(s)`;
6466
+ }
6467
+ if (maxSelected !== void 0 && selectedCount > maxSelected) {
6468
+ return `Please select at most ${maxSelected} option(s)`;
6469
+ }
6470
+ }
6471
+ return "";
6472
+ }
5874
6473
  function getModelKey(field) {
5875
6474
  return field.fieldName ?? field.id;
5876
6475
  }
6476
+ function buildFormulaValuesMap(schema, data) {
6477
+ const values = {};
6478
+ const allFields = schema.sections.flatMap((s) => s.fields);
6479
+ for (const field of allFields) {
6480
+ const modelKey = getModelKey(field);
6481
+ const val = data[modelKey];
6482
+ values[modelKey] = val;
6483
+ values[field.id] = val;
6484
+ if (field.fieldName)
6485
+ values[field.fieldName] = val;
6486
+ }
6487
+ const formulaFields = allFields.filter((f) => f.type === "number" && f.valueSource === "formula" && f.formula);
6488
+ for (let pass = 0; pass < Math.max(1, formulaFields.length); pass++) {
6489
+ for (const field of formulaFields) {
6490
+ const modelKey = getModelKey(field);
6491
+ const result = evaluateFormula(field.formula, values);
6492
+ const newVal = isNaN(result) ? void 0 : result;
6493
+ values[modelKey] = newVal;
6494
+ values[field.id] = newVal;
6495
+ if (field.fieldName)
6496
+ values[field.fieldName] = newVal;
6497
+ }
6498
+ }
6499
+ return values;
6500
+ }
6501
+ function computeFormulaValue(field, schema, data) {
6502
+ if (field.type !== "number" || field.valueSource !== "formula" || !field.formula)
6503
+ return void 0;
6504
+ const values = buildFormulaValuesMap(schema, data);
6505
+ const modelKey = getModelKey(field);
6506
+ const result = values[modelKey];
6507
+ if (typeof result !== "number")
6508
+ return void 0;
6509
+ const decimalPlaces = field.validations?.decimalPlaces ?? (field.validations?.allowDecimal ? 2 : 0);
6510
+ const rounded = decimalPlaces > 0 ? Math.round(result * Math.pow(10, decimalPlaces)) / Math.pow(10, decimalPlaces) : Math.round(result);
6511
+ return rounded;
6512
+ }
6513
+ function isFormulaDependency(schema, modelKey, fieldId) {
6514
+ for (const section of schema.sections) {
6515
+ for (const field of section.fields) {
6516
+ if (field.type === "number" && field.valueSource === "formula" && field.dependencies) {
6517
+ if (field.dependencies.includes(modelKey))
6518
+ return true;
6519
+ if (fieldId && field.dependencies.includes(fieldId))
6520
+ return true;
6521
+ }
6522
+ }
6523
+ }
6524
+ return false;
6525
+ }
5877
6526
  var FormRenderer = class {
5878
6527
  constructor(container, schema, onSubmit, onDropdownValueChange, initialData) {
5879
6528
  __publicField(this, "container");
@@ -5918,15 +6567,35 @@ var FormRenderer = class {
5918
6567
  }
5919
6568
  fieldWrapper.className = spanClass;
5920
6569
  const modelKey = getModelKey(field);
5921
- const fieldEl = FieldRenderer.render(field, this.data[modelKey], (val) => {
5922
- this.data[modelKey] = val;
5923
- if (field.type === "select" && this.onDropdownValueChange) {
5924
- this.onDropdownValueChange({
5925
- fieldId: field.id,
5926
- value: val || ""
5927
- });
5928
- }
5929
- });
6570
+ let fieldValue;
6571
+ if (field.type === "number" && field.valueSource === "formula" && field.formula) {
6572
+ const computed = computeFormulaValue(field, this.schema, this.data);
6573
+ fieldValue = computed;
6574
+ this.data[modelKey] = computed;
6575
+ } else if (field.type === "image") {
6576
+ fieldValue = this.data[modelKey] ?? field.imageUrl ?? field.defaultValue;
6577
+ } else {
6578
+ fieldValue = this.data[modelKey];
6579
+ }
6580
+ const isFormulaField = field.type === "number" && field.valueSource === "formula";
6581
+ const fieldEl = FieldRenderer.render(
6582
+ field,
6583
+ fieldValue,
6584
+ (val) => {
6585
+ this.data[modelKey] = val;
6586
+ if (field.type === "select" && this.onDropdownValueChange) {
6587
+ this.onDropdownValueChange({
6588
+ fieldId: field.id,
6589
+ value: val || ""
6590
+ });
6591
+ }
6592
+ if (isFormulaDependency(this.schema, modelKey, field.id)) {
6593
+ this.render();
6594
+ }
6595
+ },
6596
+ isFormulaField
6597
+ // Formula fields are read-only
6598
+ );
5930
6599
  fieldWrapper.appendChild(fieldEl);
5931
6600
  grid.appendChild(fieldWrapper);
5932
6601
  });
@@ -5949,107 +6618,19 @@ var FormRenderer = class {
5949
6618
  const modelKey = getModelKey(field);
5950
6619
  const fieldValue = this.data[modelKey];
5951
6620
  const fieldElement = form.querySelector(`input[id*="${field.id}"], textarea[id*="${field.id}"], select[id*="${field.id}"]`);
5952
- if (!fieldElement) {
5953
- const altElement = Array.from(form.querySelectorAll("input, textarea, select")).find((el) => {
5954
- const wrapper = el.closest("div");
5955
- return wrapper && wrapper.textContent?.includes(field.label);
5956
- });
5957
- if (altElement) {
5958
- if (field.required && (!fieldValue || fieldValue === "" || Array.isArray(fieldValue) && fieldValue.length === 0)) {
5959
- isValid2 = false;
5960
- altElement.setCustomValidity("This field is required");
5961
- altElement.reportValidity();
5962
- invalidFields.push(altElement);
5963
- } else {
5964
- altElement.setCustomValidity("");
5965
- }
5966
- if ((field.type === "text" || field.type === "email") && fieldValue) {
5967
- const validationArray = convertValidationToArray(field.validation);
5968
- const patternRule = validationArray.find((v) => v.type === "pattern");
5969
- if (patternRule?.regex) {
5970
- try {
5971
- const regex = new RegExp(patternRule.regex);
5972
- if (!regex.test(String(fieldValue))) {
5973
- isValid2 = false;
5974
- altElement.setCustomValidity(patternRule.message || "Invalid format");
5975
- altElement.reportValidity();
5976
- invalidFields.push(altElement);
5977
- } else {
5978
- altElement.setCustomValidity("");
5979
- }
5980
- } catch (e2) {
5981
- }
5982
- }
5983
- }
5984
- if (field.type === "checkbox" && Array.isArray(fieldValue)) {
5985
- const validationArray = convertValidationToArray(field.validation);
5986
- const minSelectedRule = validationArray.find((v) => v.type === "minSelected");
5987
- const maxSelectedRule = validationArray.find((v) => v.type === "maxSelected");
5988
- const selectedCount = fieldValue.length;
5989
- const minSelected = typeof minSelectedRule?.value === "number" ? minSelectedRule.value : void 0;
5990
- const maxSelected = typeof maxSelectedRule?.value === "number" ? maxSelectedRule.value : void 0;
5991
- if (minSelected !== void 0 && selectedCount < minSelected) {
5992
- isValid2 = false;
5993
- altElement.setCustomValidity(`Please select at least ${minSelected} option(s)`);
5994
- altElement.reportValidity();
5995
- invalidFields.push(altElement);
5996
- } else if (maxSelected !== void 0 && selectedCount > maxSelected) {
5997
- isValid2 = false;
5998
- altElement.setCustomValidity(`Please select at most ${maxSelected} option(s)`);
5999
- altElement.reportValidity();
6000
- invalidFields.push(altElement);
6001
- } else {
6002
- altElement.setCustomValidity("");
6003
- }
6004
- }
6005
- }
6006
- return;
6007
- }
6008
- if (field.required && (!fieldValue || fieldValue === "" || Array.isArray(fieldValue) && fieldValue.length === 0)) {
6009
- isValid2 = false;
6010
- fieldElement.setCustomValidity("This field is required");
6011
- fieldElement.reportValidity();
6012
- invalidFields.push(fieldElement);
6013
- } else {
6014
- fieldElement.setCustomValidity("");
6015
- }
6016
- if ((field.type === "text" || field.type === "email") && fieldValue) {
6017
- const validationArray = convertValidationToArray(field.validation);
6018
- const patternRule = validationArray.find((v) => v.type === "pattern");
6019
- if (patternRule?.regex) {
6020
- try {
6021
- const regex = new RegExp(patternRule.regex);
6022
- if (!regex.test(String(fieldValue))) {
6023
- isValid2 = false;
6024
- fieldElement.setCustomValidity(patternRule.message || "Invalid format");
6025
- fieldElement.reportValidity();
6026
- invalidFields.push(fieldElement);
6027
- } else {
6028
- fieldElement.setCustomValidity("");
6029
- }
6030
- } catch (e2) {
6031
- }
6032
- }
6033
- }
6034
- if (field.type === "checkbox" && Array.isArray(fieldValue)) {
6035
- const validationArray = convertValidationToArray(field.validation);
6036
- const minSelectedRule = validationArray.find((v) => v.type === "minSelected");
6037
- const maxSelectedRule = validationArray.find((v) => v.type === "maxSelected");
6038
- const selectedCount = fieldValue.length;
6039
- const minSelected = typeof minSelectedRule?.value === "number" ? minSelectedRule.value : void 0;
6040
- const maxSelected = typeof maxSelectedRule?.value === "number" ? maxSelectedRule.value : void 0;
6041
- if (minSelected !== void 0 && selectedCount < minSelected) {
6042
- isValid2 = false;
6043
- fieldElement.setCustomValidity(`Please select at least ${minSelected} option(s)`);
6044
- fieldElement.reportValidity();
6045
- invalidFields.push(fieldElement);
6046
- } else if (maxSelected !== void 0 && selectedCount > maxSelected) {
6621
+ const fieldError = getFieldValidationError(field, fieldValue);
6622
+ const element = fieldElement ?? Array.from(form.querySelectorAll("input, textarea, select")).find((el) => {
6623
+ const wrapper = el.closest("div");
6624
+ return wrapper && wrapper.textContent?.includes(field.label);
6625
+ });
6626
+ if (element) {
6627
+ if (fieldError) {
6047
6628
  isValid2 = false;
6048
- fieldElement.setCustomValidity(`Please select at most ${maxSelected} option(s)`);
6049
- fieldElement.reportValidity();
6050
- invalidFields.push(fieldElement);
6629
+ element.setCustomValidity(fieldError);
6630
+ element.reportValidity();
6631
+ invalidFields.push(element);
6051
6632
  } else {
6052
- fieldElement.setCustomValidity("");
6633
+ element.setCustomValidity("");
6053
6634
  }
6054
6635
  }
6055
6636
  });
@@ -9069,6 +9650,36 @@ var FormBuilder = class {
9069
9650
  className: "flex items-center px-3 py-2 text-sm font-medium text-[#635bff] bg-[#e7e7ff] rounded-md shadow-sm transition-colors",
9070
9651
  onclick: () => {
9071
9652
  const schema = formStore.getState().schema;
9653
+ const numericFields = schema.sections.flatMap((s) => s.fields).filter((f) => f.type === "number");
9654
+ const allIds = numericFields.map((f) => f.id);
9655
+ const allNames = numericFields.map((f) => f.fieldName ?? f.id);
9656
+ for (const field of schema.sections.flatMap((s) => s.fields)) {
9657
+ if (field.type === "number" && field.valueSource === "formula" && field.formula) {
9658
+ const validation = validateFormula(field.formula, allIds, allNames, field.id);
9659
+ if (!validation.valid) {
9660
+ alert(`Formula error in "${field.label}": ${validation.error}`);
9661
+ return;
9662
+ }
9663
+ const deps = field.dependencies ?? parseFormulaDependencies(field.formula);
9664
+ if (detectCircularDependency(schema, field.id, field.formula, deps)) {
9665
+ alert(`Circular dependency in formula for "${field.label}"`);
9666
+ return;
9667
+ }
9668
+ }
9669
+ }
9670
+ schema.sections.forEach((section) => {
9671
+ section.fields?.forEach((field) => {
9672
+ if (field.type === "number" && field.validations) {
9673
+ console.log("[Form Builder] Number field validations before save:", {
9674
+ fieldId: field.id,
9675
+ label: field.label,
9676
+ validations: field.validations,
9677
+ hasMin: "min" in field.validations,
9678
+ hasMax: "max" in field.validations
9679
+ });
9680
+ }
9681
+ });
9682
+ });
9072
9683
  console.log("[Form Builder] Schema being sent to app:", JSON.stringify(schema, null, 2));
9073
9684
  if (this.options.onSave) {
9074
9685
  this.options.onSave(schema);
@@ -9275,17 +9886,178 @@ var FormBuilder = class {
9275
9886
  }
9276
9887
  }));
9277
9888
  body.appendChild(labelGroup);
9278
- const placeholderGroup = createElement("div");
9279
- placeholderGroup.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "Placeholder" }));
9280
- placeholderGroup.appendChild(createElement("input", {
9281
- className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent",
9282
- value: selectedField.placeholder || "",
9283
- "data-focus-id": `field-placeholder-${selectedField.id}`,
9284
- oninput: (e) => {
9285
- formStore.getState().updateField(selectedField.id, { placeholder: e.target.value });
9889
+ if (selectedField.type === "number") {
9890
+ const valueSourceHeader = createElement("h3", { className: "text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2 mt-4", text: "Value Source" });
9891
+ body.appendChild(valueSourceHeader);
9892
+ const valueSourceGroup = createElement("div", { className: "mb-3" });
9893
+ valueSourceGroup.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "Source" }));
9894
+ const valueSourceSelect = createElement("select", {
9895
+ className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent",
9896
+ value: selectedField.valueSource || "manual",
9897
+ onchange: (e) => {
9898
+ const source = e.target.value;
9899
+ const updates = { valueSource: source };
9900
+ if (source === "manual") {
9901
+ updates.formula = void 0;
9902
+ updates.dependencies = void 0;
9903
+ } else if (source === "formula") {
9904
+ updates.formula = selectedField.formula || "";
9905
+ updates.dependencies = selectedField.dependencies || [];
9906
+ }
9907
+ formStore.getState().updateField(selectedField.id, updates);
9908
+ this.render();
9909
+ }
9910
+ });
9911
+ valueSourceSelect.appendChild(createElement("option", { value: "manual", text: "Manual", selected: (selectedField.valueSource || "manual") === "manual" }));
9912
+ valueSourceSelect.appendChild(createElement("option", { value: "formula", text: "Formula", selected: selectedField.valueSource === "formula" }));
9913
+ valueSourceGroup.appendChild(valueSourceSelect);
9914
+ body.appendChild(valueSourceGroup);
9915
+ if (selectedField.valueSource === "formula") {
9916
+ const schema = formStore.getState().schema;
9917
+ const numericFields = getNumericFieldsForFormula(schema, selectedField.id);
9918
+ const availableIds = numericFields.map((f) => f.id);
9919
+ const availableNames = numericFields.map((f) => f.fieldName);
9920
+ const formulaGroup = createElement("div", { className: "mb-3" });
9921
+ formulaGroup.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "Formula" }));
9922
+ const formulaInput = createElement("input", {
9923
+ type: "text",
9924
+ className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent font-mono text-sm",
9925
+ value: selectedField.formula || "",
9926
+ placeholder: "e.g. quantity * price",
9927
+ "data-focus-id": `field-formula-${selectedField.id}`,
9928
+ oninput: (e) => {
9929
+ const formula = e.target.value.trim();
9930
+ const deps = parseFormulaDependencies(formula);
9931
+ const validation = validateFormula(formula, availableIds, availableNames, selectedField.id);
9932
+ const hasCircular = deps.length > 0 && detectCircularDependency(schema, selectedField.id, formula, deps);
9933
+ const errEl = formulaGroup.querySelector(".formula-error");
9934
+ if (errEl) {
9935
+ if (validation.valid && !hasCircular) {
9936
+ errEl.textContent = "";
9937
+ errEl.classList.add("hidden");
9938
+ } else {
9939
+ errEl.textContent = !validation.valid ? validation.error : "Circular dependency detected";
9940
+ errEl.classList.remove("hidden");
9941
+ }
9942
+ }
9943
+ formStore.getState().updateField(selectedField.id, { formula, dependencies: deps });
9944
+ }
9945
+ });
9946
+ formulaGroup.appendChild(formulaInput);
9947
+ const formulaError = createElement("div", { className: "text-xs text-red-600 dark:text-red-400 mt-1 formula-error hidden" });
9948
+ formulaGroup.appendChild(formulaError);
9949
+ body.appendChild(formulaGroup);
9950
+ const insertGroup = createElement("div", { className: "mb-3" });
9951
+ insertGroup.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "Insert Field" }));
9952
+ const insertSelect = createElement("select", {
9953
+ className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent",
9954
+ onchange: (e) => {
9955
+ const sel = e.target;
9956
+ const ref = sel.value;
9957
+ if (!ref)
9958
+ return;
9959
+ const current = selectedField.formula || "";
9960
+ const insert = current ? ` ${ref} ` : ref;
9961
+ const newFormula = current + insert;
9962
+ formStore.getState().updateField(selectedField.id, {
9963
+ formula: newFormula,
9964
+ dependencies: parseFormulaDependencies(newFormula)
9965
+ });
9966
+ formulaInput.value = newFormula;
9967
+ sel.value = "";
9968
+ this.render();
9969
+ }
9970
+ });
9971
+ insertSelect.appendChild(createElement("option", { value: "", text: "Select field to insert...", selected: true }));
9972
+ numericFields.forEach((f) => {
9973
+ const ref = f.fieldName !== f.id ? f.fieldName : f.id;
9974
+ insertSelect.appendChild(createElement("option", { value: ref, text: `${f.label} (${ref})` }));
9975
+ });
9976
+ insertGroup.appendChild(insertSelect);
9977
+ body.appendChild(insertGroup);
9978
+ const hintEl = createElement("p", {
9979
+ className: "text-xs text-gray-500 dark:text-gray-400 mb-2",
9980
+ text: "Use +, -, *, / and parentheses. Reference fields by their name or ID."
9981
+ });
9982
+ body.appendChild(hintEl);
9286
9983
  }
9287
- }));
9288
- body.appendChild(placeholderGroup);
9984
+ }
9985
+ if (selectedField.type !== "image") {
9986
+ const placeholderGroup = createElement("div");
9987
+ placeholderGroup.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "Placeholder" }));
9988
+ placeholderGroup.appendChild(createElement("input", {
9989
+ className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent",
9990
+ value: selectedField.placeholder || "",
9991
+ "data-focus-id": `field-placeholder-${selectedField.id}`,
9992
+ oninput: (e) => {
9993
+ formStore.getState().updateField(selectedField.id, { placeholder: e.target.value });
9994
+ }
9995
+ }));
9996
+ body.appendChild(placeholderGroup);
9997
+ }
9998
+ if (selectedField.type === "image") {
9999
+ const imageUrl = selectedField.imageUrl ?? selectedField.defaultValue;
10000
+ const imageGroup = createElement("div", { className: "mb-4" });
10001
+ imageGroup.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-2", text: "Image" }));
10002
+ const previewWrap = createElement("div", {
10003
+ className: "rounded-md border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 overflow-hidden min-h-[80px] flex items-center justify-center mb-2"
10004
+ });
10005
+ if (imageUrl) {
10006
+ const img = createElement("img", {
10007
+ src: imageUrl,
10008
+ alt: selectedField.label || "Image",
10009
+ className: "max-h-32 max-w-full object-contain"
10010
+ });
10011
+ img.onerror = () => {
10012
+ previewWrap.innerHTML = "";
10013
+ previewWrap.appendChild(createElement("p", { className: "text-xs text-red-500 p-2", text: "Failed to load" }));
10014
+ };
10015
+ previewWrap.appendChild(img);
10016
+ } else {
10017
+ previewWrap.appendChild(createElement("p", { className: "text-xs text-muted-foreground p-2", text: "No image" }));
10018
+ }
10019
+ imageGroup.appendChild(previewWrap);
10020
+ const btnRow = createElement("div", { className: "flex gap-2" });
10021
+ const fileInput = createElement("input", {
10022
+ type: "file",
10023
+ accept: "image/jpeg,image/png,image/gif,image/webp",
10024
+ className: "hidden",
10025
+ id: `config-image-${selectedField.id}`
10026
+ });
10027
+ fileInput.onchange = (e) => {
10028
+ const file = e.target.files?.[0];
10029
+ if (!file || !file.type.match(/^image\/(jpeg|png|gif|webp)$/))
10030
+ return;
10031
+ if (file.size > 5 * 1024 * 1024) {
10032
+ alert("Image must be under 5MB");
10033
+ return;
10034
+ }
10035
+ const reader = new FileReader();
10036
+ reader.onload = () => {
10037
+ const base64 = reader.result;
10038
+ if (base64)
10039
+ formStore.getState().updateField(selectedField.id, { imageUrl: base64, defaultValue: base64 });
10040
+ };
10041
+ reader.readAsDataURL(file);
10042
+ e.target.value = "";
10043
+ };
10044
+ btnRow.appendChild(fileInput);
10045
+ btnRow.appendChild(createElement("button", {
10046
+ type: "button",
10047
+ className: "px-3 py-2 text-sm border border-gray-300 dark:border-gray-700 rounded-md hover:bg-gray-50 dark:hover:bg-gray-800",
10048
+ text: imageUrl ? "Replace" : "Upload",
10049
+ onclick: () => fileInput.click()
10050
+ }));
10051
+ btnRow.appendChild(createElement("button", {
10052
+ type: "button",
10053
+ className: "px-3 py-2 text-sm border border-red-200 text-red-600 rounded-md hover:bg-red-50 dark:hover:bg-red-900/20",
10054
+ text: "Remove",
10055
+ disabled: !imageUrl,
10056
+ onclick: () => formStore.getState().updateField(selectedField.id, { imageUrl: void 0, defaultValue: void 0 })
10057
+ }));
10058
+ imageGroup.appendChild(btnRow);
10059
+ body.appendChild(imageGroup);
10060
+ }
9289
10061
  const layoutGroup = createElement("div", { className: "layout-span-group" });
9290
10062
  const layoutLabelRow = createElement("div", { className: "flex items-center justify-between mb-2" });
9291
10063
  layoutLabelRow.appendChild(createElement("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", text: "Grid Span" }));
@@ -9330,8 +10102,14 @@ var FormBuilder = class {
9330
10102
  body.appendChild(layoutGroup);
9331
10103
  body.appendChild(this.createCheckboxField(
9332
10104
  "Required",
9333
- !!selectedField.required,
9334
- (checked) => formStore.getState().updateField(selectedField.id, { required: checked }),
10105
+ !!selectedField.required || !!selectedField.validations?.required,
10106
+ (checked) => {
10107
+ const currentValidations = selectedField.validations || {};
10108
+ formStore.getState().updateField(selectedField.id, {
10109
+ required: checked,
10110
+ validations: { ...currentValidations, required: checked }
10111
+ });
10112
+ },
9335
10113
  `required-${selectedField.id}`
9336
10114
  ));
9337
10115
  body.appendChild(this.createCheckboxField(
@@ -9701,7 +10479,9 @@ var FormBuilder = class {
9701
10479
  `custom-options-${selectedField.id}`
9702
10480
  ));
9703
10481
  }
9704
- const shouldShowOptions = selectedField.type === "select" ? selectedField.customOptionsEnabled && (selectedField.optionSource === "STATIC" || !selectedField.optionSource) : true;
10482
+ const isStaticSelect = selectedField.type === "select" && (selectedField.optionSource === "STATIC" || !selectedField.optionSource);
10483
+ const hasOptions = selectedField.options && selectedField.options.length > 0;
10484
+ const shouldShowOptions = selectedField.type === "select" ? isStaticSelect && (!!selectedField.customOptionsEnabled || !!hasOptions) : true;
9705
10485
  if (shouldShowOptions) {
9706
10486
  const options = selectedField.options || [];
9707
10487
  const fieldId2 = selectedField.id;
@@ -9743,12 +10523,16 @@ var FormBuilder = class {
9743
10523
  }
9744
10524
  });
9745
10525
  const deleteBtn = createElement("button", {
10526
+ type: "button",
9746
10527
  className: "p-1.5 text-red-600 hover:bg-red-50 rounded transition-colors",
9747
10528
  title: "Delete option",
9748
- onclick: () => {
10529
+ onclick: (e) => {
10530
+ e.preventDefault();
10531
+ e.stopPropagation();
9749
10532
  const currentOptions = getCurrentOptions();
9750
- const newOptions = currentOptions.filter((_, i) => i !== index2);
10533
+ const newOptions = currentOptions.filter((o) => o.value !== opt.value);
9751
10534
  formStore.getState().updateField(fieldId2, { options: newOptions });
10535
+ this.render();
9752
10536
  }
9753
10537
  }, [getIcon("Trash2", 14)]);
9754
10538
  optionRow.appendChild(labelInput);
@@ -9772,40 +10556,67 @@ var FormBuilder = class {
9772
10556
  body.appendChild(addOptionBtn);
9773
10557
  }
9774
10558
  }
9775
- const validationObj = Array.isArray(selectedField.validation) ? (() => {
9776
- const obj = {};
9777
- selectedField.validation.forEach((rule) => {
9778
- if (rule.type === "required")
9779
- obj.required = true;
9780
- else if (rule.type === "pattern" && rule.regex) {
9781
- obj.regex = rule.regex;
9782
- obj.regexMessage = rule.message;
9783
- } else if (rule.type === "minLength" && typeof rule.value === "number")
9784
- obj.minLength = rule.value;
9785
- else if (rule.type === "maxLength" && typeof rule.value === "number")
9786
- obj.maxLength = rule.value;
9787
- else if (rule.type === "minSelected" && typeof rule.value === "number")
9788
- obj.minSelected = rule.value;
9789
- else if (rule.type === "maxSelected" && typeof rule.value === "number")
9790
- obj.maxSelected = rule.value;
9791
- else if (rule.type === "minDate" && typeof rule.value === "string")
9792
- obj.minDate = rule.value;
9793
- else if (rule.type === "maxDate" && typeof rule.value === "string")
9794
- obj.maxDate = rule.value;
9795
- });
9796
- return obj;
9797
- })() : selectedField.validation || {};
9798
- const updateValidation = (updates) => {
9799
- const newValidation = { ...validationObj, ...updates };
9800
- Object.keys(newValidation).forEach((key) => {
9801
- if (newValidation[key] === void 0) {
9802
- delete newValidation[key];
10559
+ const validationsObj = selectedField.validations || (() => {
10560
+ const v = selectedField.validation;
10561
+ if (!v)
10562
+ return {};
10563
+ if (Array.isArray(v)) {
10564
+ const obj = {};
10565
+ v.forEach((rule) => {
10566
+ if (rule.type === "required")
10567
+ obj.required = true;
10568
+ else if (rule.type === "pattern" && rule.regex) {
10569
+ obj.pattern = rule.regex;
10570
+ if (rule.message)
10571
+ obj.customErrorMessages = { ...obj.customErrorMessages, pattern: rule.message };
10572
+ } else if (rule.type === "minLength" && typeof rule.value === "number")
10573
+ obj.minLength = rule.value;
10574
+ else if (rule.type === "maxLength" && typeof rule.value === "number")
10575
+ obj.maxLength = rule.value;
10576
+ else if (rule.type === "min" && typeof rule.value === "number")
10577
+ obj.min = rule.value;
10578
+ else if (rule.type === "max" && typeof rule.value === "number")
10579
+ obj.max = rule.value;
10580
+ else if (rule.type === "minSelected" && typeof rule.value === "number")
10581
+ obj.minSelected = rule.value;
10582
+ else if (rule.type === "maxSelected" && typeof rule.value === "number")
10583
+ obj.maxSelected = rule.value;
10584
+ else if (rule.type === "minDate" && typeof rule.value === "string")
10585
+ obj.minDate = rule.value;
10586
+ else if (rule.type === "maxDate" && typeof rule.value === "string")
10587
+ obj.maxDate = rule.value;
10588
+ });
10589
+ return obj;
10590
+ }
10591
+ const o = v;
10592
+ return {
10593
+ required: o.required,
10594
+ pattern: o.regex,
10595
+ minLength: o.minLength,
10596
+ maxLength: o.maxLength,
10597
+ min: o.min,
10598
+ max: o.max,
10599
+ minSelected: o.minSelected,
10600
+ maxSelected: o.maxSelected,
10601
+ minDate: o.minDate,
10602
+ maxDate: o.maxDate,
10603
+ customErrorMessages: o.regexMessage ? { pattern: o.regexMessage } : void 0
10604
+ };
10605
+ })();
10606
+ const updateValidations = (updates) => {
10607
+ const currentField = formStore.getState().schema.sections.flatMap((s) => s.fields).find((f) => f.id === selectedField.id);
10608
+ const currentValidations = currentField?.validations ? { ...currentField.validations } : { ...validationsObj };
10609
+ const newValidations = { ...currentValidations, ...updates };
10610
+ Object.keys(newValidations).forEach((key) => {
10611
+ const v = newValidations[key];
10612
+ if (v === void 0 || v === null) {
10613
+ delete newValidations[key];
9803
10614
  }
9804
10615
  });
9805
- formStore.getState().updateField(selectedField.id, { validation: newValidation });
10616
+ formStore.getState().updateField(selectedField.id, { validations: newValidations });
9806
10617
  };
9807
- const getRuleValue = (key) => {
9808
- const value = validationObj[key];
10618
+ const getValidationsValue = (key) => {
10619
+ const value = validationsObj[key];
9809
10620
  if (value === void 0 || value === null)
9810
10621
  return "";
9811
10622
  if (typeof value === "number")
@@ -9815,17 +10626,139 @@ var FormBuilder = class {
9815
10626
  return String(value);
9816
10627
  };
9817
10628
  const validationElements = [];
9818
- if (["text", "textarea", "email", "password"].includes(selectedField.type)) {
10629
+ if (selectedField.type === "number") {
10630
+ const numValidationTypeGroup = createElement("div", { className: "mb-3" });
10631
+ numValidationTypeGroup.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "Validation Type" }));
10632
+ const numValidationTypeSelect = createElement("select", {
10633
+ className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent",
10634
+ onchange: (e) => {
10635
+ const presetId = e.target.value;
10636
+ if (presetId) {
10637
+ const preset = VALIDATION_TYPE_PRESETS[presetId];
10638
+ if (preset && presetId === "amount") {
10639
+ updateValidations({ ...preset, validationType: presetId });
10640
+ }
10641
+ } else {
10642
+ updateValidations({ validationType: "custom" });
10643
+ }
10644
+ }
10645
+ });
10646
+ [
10647
+ { value: "", text: "Custom" },
10648
+ { value: "amount", text: "Amount (min 0, 2 decimals)" }
10649
+ ].forEach((opt) => {
10650
+ numValidationTypeSelect.appendChild(createElement("option", {
10651
+ value: opt.value,
10652
+ text: opt.text,
10653
+ selected: validationsObj.validationType === opt.value || !validationsObj.validationType && opt.value === ""
10654
+ }));
10655
+ });
10656
+ numValidationTypeGroup.appendChild(numValidationTypeSelect);
10657
+ validationElements.push(numValidationTypeGroup);
10658
+ const minValGroup = createElement("div", { className: "mb-3" });
10659
+ minValGroup.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "Min Value" }));
10660
+ minValGroup.appendChild(createElement("input", {
10661
+ type: "number",
10662
+ className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent",
10663
+ value: getValidationsValue("min") || "",
10664
+ placeholder: "e.g. 0",
10665
+ oninput: (e) => {
10666
+ const val = e.target.value;
10667
+ updateValidations({ min: val !== "" ? parseFloat(val) : void 0 });
10668
+ }
10669
+ }));
10670
+ validationElements.push(minValGroup);
10671
+ const maxValGroup = createElement("div", { className: "mb-3" });
10672
+ maxValGroup.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "Max Value" }));
10673
+ maxValGroup.appendChild(createElement("input", {
10674
+ type: "number",
10675
+ className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent",
10676
+ value: getValidationsValue("max") || "",
10677
+ placeholder: "e.g. 100",
10678
+ oninput: (e) => {
10679
+ const val = e.target.value;
10680
+ updateValidations({ max: val !== "" ? parseFloat(val) : void 0 });
10681
+ }
10682
+ }));
10683
+ validationElements.push(maxValGroup);
10684
+ validationElements.push(this.createCheckboxField(
10685
+ "Allow Decimal",
10686
+ validationsObj.allowDecimal === true,
10687
+ (checked) => updateValidations({
10688
+ allowDecimal: checked,
10689
+ // When unchecked, remove decimalPlaces so it's not in the payload
10690
+ decimalPlaces: checked ? validationsObj.decimalPlaces ?? 2 : void 0
10691
+ }),
10692
+ `allow-decimal-${selectedField.id}`
10693
+ ));
10694
+ const decimalPlacesGroup = createElement("div", { className: "mb-3" });
10695
+ decimalPlacesGroup.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "Decimal Places" }));
10696
+ const decimalPlacesInput = createElement("input", {
10697
+ type: "number",
10698
+ className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent disabled:opacity-50 disabled:cursor-not-allowed",
10699
+ value: validationsObj.allowDecimal === true ? String(validationsObj.decimalPlaces ?? 2) : "",
10700
+ placeholder: "e.g. 2",
10701
+ min: "0",
10702
+ max: "10",
10703
+ disabled: validationsObj.allowDecimal !== true,
10704
+ oninput: (e) => {
10705
+ const val = e.target.value;
10706
+ updateValidations({ decimalPlaces: val !== "" ? parseInt(val) : void 0 });
10707
+ }
10708
+ });
10709
+ decimalPlacesGroup.appendChild(decimalPlacesInput);
10710
+ validationElements.push(decimalPlacesGroup);
10711
+ validationElements.push(this.createCheckboxField(
10712
+ "Allow Negative",
10713
+ validationsObj.allowNegative === true,
10714
+ (checked) => updateValidations({ allowNegative: checked }),
10715
+ `allow-negative-${selectedField.id}`
10716
+ ));
10717
+ }
10718
+ if (["text", "textarea", "email", "phone"].includes(selectedField.type)) {
10719
+ if (selectedField.type === "text" || selectedField.type === "phone") {
10720
+ const validationTypeGroup = createElement("div", { className: "mb-3" });
10721
+ validationTypeGroup.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "Validation Type" }));
10722
+ const validationTypeSelect = createElement("select", {
10723
+ className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent",
10724
+ onchange: (e) => {
10725
+ const presetId = e.target.value;
10726
+ if (presetId) {
10727
+ const preset = VALIDATION_TYPE_PRESETS[presetId];
10728
+ if (preset) {
10729
+ updateValidations({ ...preset, validationType: presetId });
10730
+ }
10731
+ } else {
10732
+ updateValidations({ validationType: "custom" });
10733
+ }
10734
+ }
10735
+ });
10736
+ const options = [
10737
+ { value: "", text: "Custom" },
10738
+ { value: "postalCode", text: "Postal Code (6 digit)" },
10739
+ { value: "phoneNumber", text: "Phone Number (10 digit)" },
10740
+ { value: "otp", text: "OTP (4/6 digit)" }
10741
+ ];
10742
+ options.forEach((opt) => {
10743
+ validationTypeSelect.appendChild(createElement("option", {
10744
+ value: opt.value,
10745
+ text: opt.text,
10746
+ selected: validationsObj.validationType === opt.value || !validationsObj.validationType && opt.value === ""
10747
+ }));
10748
+ });
10749
+ validationTypeGroup.appendChild(validationTypeSelect);
10750
+ validationElements.push(validationTypeGroup);
10751
+ }
9819
10752
  const minLenGroup = createElement("div", { className: "mb-3" });
9820
10753
  minLenGroup.appendChild(createElement("label", { className: "block text-sm font-normal text-gray-700 dark:text-gray-300 mb-1", text: "Min Length" }));
9821
10754
  minLenGroup.appendChild(createElement("input", {
9822
10755
  type: "number",
9823
10756
  className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent",
9824
- value: getRuleValue("minLength") || "",
10757
+ value: getValidationsValue("minLength") || "",
9825
10758
  placeholder: "e.g. 3",
9826
- onchange: (e) => {
10759
+ oninput: (e) => {
9827
10760
  const value = e.target.value;
9828
- updateValidation({ minLength: value ? parseInt(value) : void 0 });
10761
+ updateValidations({ minLength: value !== "" ? parseInt(value) : void 0 });
9829
10762
  }
9830
10763
  }));
9831
10764
  validationElements.push(minLenGroup);
@@ -9834,11 +10767,11 @@ var FormBuilder = class {
9834
10767
  maxLenGroup.appendChild(createElement("input", {
9835
10768
  type: "number",
9836
10769
  className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent",
9837
- value: getRuleValue("maxLength") || "",
10770
+ value: getValidationsValue("maxLength") || "",
9838
10771
  placeholder: "e.g. 100",
9839
- onchange: (e) => {
10772
+ oninput: (e) => {
9840
10773
  const value = e.target.value;
9841
- updateValidation({ maxLength: value ? parseInt(value) : void 0 });
10774
+ updateValidations({ maxLength: value !== "" ? parseInt(value) : void 0 });
9842
10775
  }
9843
10776
  }));
9844
10777
  validationElements.push(maxLenGroup);
@@ -9898,10 +10831,11 @@ var FormBuilder = class {
9898
10831
  examplesContainer.appendChild(examplesList);
9899
10832
  regexGroup.appendChild(examplesContainer);
9900
10833
  }
9901
- const currentRegex = validationObj.regex || "";
10834
+ const currentRegex = validationsObj.pattern || selectedField.validation?.regex || "";
9902
10835
  const findPresetByRegex = (regex) => {
9903
10836
  return REGEX_PRESETS.find((preset) => preset.pattern === regex);
9904
10837
  };
10838
+ const regexMessage = validationsObj.customErrorMessages?.pattern || selectedField.validation?.regexMessage || "Invalid format";
9905
10839
  let currentPreset = currentRegex ? findPresetByRegex(currentRegex) : void 0;
9906
10840
  let selectedPresetId = currentPreset?.id || "";
9907
10841
  let regexInput;
@@ -9916,9 +10850,9 @@ var FormBuilder = class {
9916
10850
  selectedPresetId = presetId;
9917
10851
  const preset = REGEX_PRESETS.find((p) => p.id === presetId);
9918
10852
  if (preset) {
9919
- updateValidation({
9920
- regex: preset.pattern,
9921
- regexMessage: preset.errorMessage
10853
+ updateValidations({
10854
+ pattern: preset.pattern,
10855
+ customErrorMessages: { ...validationsObj.customErrorMessages, pattern: preset.errorMessage }
9922
10856
  });
9923
10857
  regexInput.value = preset.pattern;
9924
10858
  currentPreset = preset;
@@ -9973,9 +10907,9 @@ var FormBuilder = class {
9973
10907
  }
9974
10908
  }
9975
10909
  }
9976
- updateValidation({
9977
- regex: val || void 0,
9978
- regexMessage: currentPreset?.errorMessage || validationObj.regexMessage || "Invalid format"
10910
+ updateValidations({
10911
+ pattern: val || void 0,
10912
+ customErrorMessages: { ...validationsObj.customErrorMessages, pattern: currentPreset?.errorMessage || regexMessage || "Invalid format" }
9979
10913
  });
9980
10914
  if (examplesList) {
9981
10915
  if (currentPreset) {
@@ -9998,6 +10932,21 @@ var FormBuilder = class {
9998
10932
  updateExamples(examplesList, currentRegex);
9999
10933
  }
10000
10934
  }
10935
+ const patternMsgGroup = createElement("div", { className: "mb-3 mt-2" });
10936
+ patternMsgGroup.appendChild(createElement("label", { className: "block text-xs font-medium text-gray-600 dark:text-gray-400 mb-1", text: "Pattern Error Message" }));
10937
+ patternMsgGroup.appendChild(createElement("input", {
10938
+ type: "text",
10939
+ className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent text-sm",
10940
+ value: validationsObj.customErrorMessages?.pattern || "",
10941
+ placeholder: "e.g. Invalid format",
10942
+ oninput: (e) => {
10943
+ const val = e.target.value;
10944
+ updateValidations({
10945
+ customErrorMessages: { ...validationsObj.customErrorMessages, pattern: val || void 0 }
10946
+ });
10947
+ }
10948
+ }));
10949
+ regexGroup.appendChild(patternMsgGroup);
10001
10950
  validationElements.push(regexGroup);
10002
10951
  }
10003
10952
  if (selectedField.type === "checkbox") {
@@ -10006,12 +10955,12 @@ var FormBuilder = class {
10006
10955
  minSelectedGroup.appendChild(createElement("input", {
10007
10956
  type: "number",
10008
10957
  className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent",
10009
- value: getRuleValue("minSelected"),
10958
+ value: getValidationsValue("minSelected"),
10010
10959
  placeholder: "e.g. 1",
10011
10960
  min: "0",
10012
10961
  onchange: (e) => {
10013
10962
  const val = e.target.value;
10014
- updateValidation({ minSelected: val ? parseInt(val) : void 0 });
10963
+ updateValidations({ minSelected: val ? parseInt(val) : void 0 });
10015
10964
  }
10016
10965
  }));
10017
10966
  validationElements.push(minSelectedGroup);
@@ -10020,12 +10969,12 @@ var FormBuilder = class {
10020
10969
  maxSelectedGroup.appendChild(createElement("input", {
10021
10970
  type: "number",
10022
10971
  className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent",
10023
- value: getRuleValue("maxSelected"),
10972
+ value: getValidationsValue("maxSelected"),
10024
10973
  placeholder: "e.g. 2",
10025
10974
  min: "1",
10026
10975
  onchange: (e) => {
10027
10976
  const val = e.target.value;
10028
- updateValidation({ maxSelected: val ? parseInt(val) : void 0 });
10977
+ updateValidations({ maxSelected: val ? parseInt(val) : void 0 });
10029
10978
  }
10030
10979
  }));
10031
10980
  validationElements.push(maxSelectedGroup);
@@ -10036,10 +10985,10 @@ var FormBuilder = class {
10036
10985
  minDateGroup.appendChild(createElement("input", {
10037
10986
  type: "date",
10038
10987
  className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent",
10039
- value: validationObj.minDate || "",
10988
+ value: validationsObj.minDate || "",
10040
10989
  onchange: (e) => {
10041
10990
  const val = e.target.value;
10042
- updateValidation({ minDate: val || void 0 });
10991
+ updateValidations({ minDate: val || void 0 });
10043
10992
  }
10044
10993
  }));
10045
10994
  validationElements.push(minDateGroup);
@@ -10048,10 +10997,10 @@ var FormBuilder = class {
10048
10997
  maxDateGroup.appendChild(createElement("input", {
10049
10998
  type: "date",
10050
10999
  className: "w-full px-3 py-2 border border-gray-200 dark:border-gray-700 rounded-md bg-transparent",
10051
- value: validationObj.maxDate || "",
11000
+ value: validationsObj.maxDate || "",
10052
11001
  onchange: (e) => {
10053
11002
  const val = e.target.value;
10054
- updateValidation({ maxDate: val || void 0 });
11003
+ updateValidations({ maxDate: val || void 0 });
10055
11004
  }
10056
11005
  }));
10057
11006
  validationElements.push(maxDateGroup);
@@ -10342,6 +11291,6 @@ sortablejs/modular/sortable.esm.js:
10342
11291
  *)
10343
11292
  */
10344
11293
 
10345
- export { FormBuilder, FormRenderer, FormSchemaValidation, builderToPlatform, cleanFormSchema, convertValidationObjectToArray, formStore, getColSpanFromWidth, initFormBuilder, parseWidth, platformToBuilder };
11294
+ export { FormBuilder, FormRenderer, FormSchemaValidation, builderToPlatform, cleanFormSchema, convertValidationObjectToArray, detectCircularDependency, evaluateFormula, formStore, getColSpanFromWidth, getNumericFieldsForFormula, getValidationConfigForAngular, initFormBuilder, parseFormulaDependencies, parseWidth, platformToBuilder, validateFormula };
10346
11295
  //# sourceMappingURL=out.js.map
10347
11296
  //# sourceMappingURL=index.mjs.map