hs-uix 1.0.3 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/form.js CHANGED
@@ -198,12 +198,14 @@ var collectAsyncValidatorPromises = (value, field, allValues, context) => {
198
198
  };
199
199
  var runValidators = (value, field, allValues, fieldTypes, options = {}) => {
200
200
  const includeCustomValidators = options.includeCustomValidators !== false;
201
+ const msg = options.messages || {};
201
202
  if (field.type === "display" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList") return null;
202
203
  const isRequired = resolveRequired(field, allValues);
203
204
  const plugin = fieldTypes && fieldTypes[field.type];
204
205
  const empty = plugin && plugin.isEmpty ? plugin.isEmpty(value) : isValueEmpty(value, field);
205
206
  if (isRequired && empty) {
206
- return `${field.label} is required`;
207
+ const fn = msg.required || ((label) => `${label} is required`);
208
+ return typeof fn === "function" ? fn(field.label) : fn;
207
209
  }
208
210
  if (empty) return null;
209
211
  if (field.useDefaultValidators !== false) {
@@ -212,23 +214,27 @@ var runValidators = (value, field, allValues, fieldTypes, options = {}) => {
212
214
  }
213
215
  if (field.pattern && typeof value === "string") {
214
216
  if (!field.pattern.test(value)) {
215
- return field.patternMessage || "Invalid format";
217
+ return field.patternMessage || msg.invalidFormat || "Invalid format";
216
218
  }
217
219
  }
218
220
  if (typeof value === "string") {
219
221
  if (field.minLength != null && value.length < field.minLength) {
220
- return `Must be at least ${field.minLength} characters`;
222
+ const fn = msg.minLength || ((min) => `Must be at least ${min} characters`);
223
+ return typeof fn === "function" ? fn(field.minLength) : fn;
221
224
  }
222
225
  if (field.maxLength != null && value.length > field.maxLength) {
223
- return `Must be no more than ${field.maxLength} characters`;
226
+ const fn = msg.maxLength || ((max) => `Must be no more than ${max} characters`);
227
+ return typeof fn === "function" ? fn(field.maxLength) : fn;
224
228
  }
225
229
  }
226
230
  if (typeof value === "number") {
227
231
  if (field.min != null && value < field.min) {
228
- return `Must be at least ${field.min}`;
232
+ const fn = msg.minValue || ((min) => `Must be at least ${min}`);
233
+ return typeof fn === "function" ? fn(field.min) : fn;
229
234
  }
230
235
  if (field.max != null && value > field.max) {
231
- return `Must be no more than ${field.max}`;
236
+ const fn = msg.maxValue || ((max) => `Must be no more than ${max}`);
237
+ return typeof fn === "function" ? fn(field.max) : fn;
232
238
  }
233
239
  }
234
240
  if (field.type === "date" && isDateValueObject(value)) {
@@ -434,8 +440,18 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
434
440
  // string — warning alert when readOnly
435
441
  alerts,
436
442
  // { addAlert, readOnlyTitle, errorTitle, successTitle }
437
- errors: controlledErrors
443
+ errors: controlledErrors,
438
444
  // controlled validation errors
445
+ showReadOnlyAlert = true,
446
+ // show warning Alert when readOnly is true
447
+ showInlineAlerts = true,
448
+ // show inline form-level error/success Alerts
449
+ renderReadOnlyAlert,
450
+ // (context: { title, message }) => ReactNode — custom readOnly alert renderer
451
+ renderFieldError,
452
+ // (error: string, field: object) => ReactNode — custom field error renderer
453
+ defaultCurrency = "USD"
454
+ // form-level default ISO 4217 currency code for currency fields
439
455
  } = props;
440
456
  const {
441
457
  onDirtyChange,
@@ -447,6 +463,23 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
447
463
  const cancelButtonLabel = (labels == null ? void 0 : labels.cancel) || "Cancel";
448
464
  const backButtonLabel = (labels == null ? void 0 : labels.back) || "Back";
449
465
  const nextButtonLabel = (labels == null ? void 0 : labels.next) || "Next";
466
+ const requiredMessage = (labels == null ? void 0 : labels.required) || ((label) => `${label} is required`);
467
+ const invalidFormatMessage = (labels == null ? void 0 : labels.invalidFormat) || "Invalid format";
468
+ const minLengthMessage = (labels == null ? void 0 : labels.minLength) || ((min) => `Must be at least ${min} characters`);
469
+ const maxLengthMessage = (labels == null ? void 0 : labels.maxLength) || ((max) => `Must be no more than ${max} characters`);
470
+ const minValueMessage = (labels == null ? void 0 : labels.minValue) || ((min) => `Must be at least ${min}`);
471
+ const maxValueMessage = (labels == null ? void 0 : labels.maxValue) || ((max) => `Must be no more than ${max}`);
472
+ const dependentPropertiesLabel = (labels == null ? void 0 : labels.dependentProperties) || "Dependent properties";
473
+ const repeaterAddLabel = (labels == null ? void 0 : labels.repeaterAdd) || "Add";
474
+ const repeaterRemoveLabel = (labels == null ? void 0 : labels.repeaterRemove) || "Remove";
475
+ const validationMessages = labels ? {
476
+ required: requiredMessage,
477
+ invalidFormat: invalidFormatMessage,
478
+ minLength: minLengthMessage,
479
+ maxLength: maxLengthMessage,
480
+ minValue: minValueMessage,
481
+ maxValue: maxValueMessage
482
+ } : void 0;
450
483
  const addAlert = alerts == null ? void 0 : alerts.addAlert;
451
484
  const readOnlyTitle = (alerts == null ? void 0 : alerts.readOnlyTitle) || "Read Only";
452
485
  const errorTitle = (alerts == null ? void 0 : alerts.errorTitle) || "Error";
@@ -685,7 +718,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
685
718
  const rowValues = { ...allValues, [field.name]: rows };
686
719
  subFields.forEach((subField) => {
687
720
  if (subField.visible && !subField.visible(rowValues)) return;
688
- const err = runValidators(row == null ? void 0 : row[subField.name], subField, rowValues, fieldTypes);
721
+ const err = runValidators(row == null ? void 0 : row[subField.name], subField, rowValues, fieldTypes, { messages: validationMessages });
689
722
  if (!err) return;
690
723
  const key = getRepeaterErrorKey(field.name, rowIdx, subField.name);
691
724
  errors[key] = err;
@@ -712,9 +745,9 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
712
745
  );
713
746
  return repeaterResult.errors[name] || null;
714
747
  }
715
- return runValidators(value != null ? value : formValues[name], field, formValues, fieldTypes);
748
+ return runValidators(value != null ? value : formValues[name], field, formValues, fieldTypes, { messages: validationMessages });
716
749
  },
717
- [fieldByName, formValues, validateRepeaterField, fieldTypes]
750
+ [fieldByName, formValues, validateRepeaterField, fieldTypes, validationMessages]
718
751
  );
719
752
  const validateVisibleFields = (0, import_react.useCallback)(
720
753
  (fieldSubset) => {
@@ -730,7 +763,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
730
763
  }
731
764
  continue;
732
765
  }
733
- const err = runValidators(formValues[field.name], field, formValues, fieldTypes);
766
+ const err = runValidators(formValues[field.name], field, formValues, fieldTypes, { messages: validationMessages });
734
767
  if (err) {
735
768
  errors[field.name] = err;
736
769
  hasErrors = true;
@@ -738,14 +771,14 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
738
771
  }
739
772
  return { errors, hasErrors };
740
773
  },
741
- [visibleFields, formValues, validateRepeaterField, fieldTypes]
774
+ [visibleFields, formValues, validateRepeaterField, fieldTypes, validationMessages]
742
775
  );
743
776
  const runAsyncValidation = (0, import_react.useCallback)(
744
777
  (name, value) => {
745
778
  const field = fieldByName.get(name);
746
779
  if (!field || field.type === "repeater") return null;
747
780
  const val = value != null ? value : formValues[name];
748
- const syncError = runValidators(val, field, formValues, fieldTypes, { includeCustomValidators: false });
781
+ const syncError = runValidators(val, field, formValues, fieldTypes, { includeCustomValidators: false, messages: validationMessages });
749
782
  const prevController = asyncAbortRef.current.get(name);
750
783
  if (prevController) prevController.abort();
751
784
  asyncAbortRef.current.delete(name);
@@ -1114,6 +1147,12 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
1114
1147
  [replaceErrors]
1115
1148
  );
1116
1149
  const renderField = (field) => {
1150
+ const fieldError = formErrors[field.name] || null;
1151
+ const rendered = renderFieldInner(field);
1152
+ if (!renderFieldError || !fieldError) return rendered;
1153
+ return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, rendered, renderFieldError(fieldError, field));
1154
+ };
1155
+ const renderFieldInner = (field) => {
1117
1156
  const fieldValue = formValues[field.name];
1118
1157
  const fieldError = formErrors[field.name] || null;
1119
1158
  const hasError = !!fieldError;
@@ -1180,7 +1219,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
1180
1219
  readOnly: isReadOnly,
1181
1220
  disabled: isDisabled,
1182
1221
  error: hasError,
1183
- validationMessage: fieldError || void 0,
1222
+ validationMessage: renderFieldError ? void 0 : fieldError || void 0,
1184
1223
  ...field.loading || validatingFields[field.name] ? { loading: true } : {},
1185
1224
  ...field.fieldProps || {}
1186
1225
  };
@@ -1250,7 +1289,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
1250
1289
  import_ui_extensions.CurrencyInput,
1251
1290
  {
1252
1291
  ...commonProps,
1253
- currency: field.currency || "USD",
1292
+ currency: field.currency || defaultCurrency,
1254
1293
  value: fieldValue,
1255
1294
  min: field.min,
1256
1295
  max: field.max,
@@ -1428,8 +1467,8 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
1428
1467
  const renderRemoveControl = repeaterProps.renderRemove;
1429
1468
  const renderMoveUpControl = repeaterProps.renderMoveUp;
1430
1469
  const renderMoveDownControl = repeaterProps.renderMoveDown;
1431
- const addLabel = repeaterProps.addLabel || "Add";
1432
- const removeLabel = repeaterProps.removeLabel || "Remove";
1470
+ const addLabel = repeaterProps.addLabel || repeaterAddLabel;
1471
+ const removeLabel = repeaterProps.removeLabel || repeaterRemoveLabel;
1433
1472
  const moveUpLabel = repeaterProps.moveUpLabel || "Up";
1434
1473
  const moveDownLabel = repeaterProps.moveDownLabel || "Down";
1435
1474
  const canEditRows = !isReadOnly && !isDisabled;
@@ -1463,7 +1502,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
1463
1502
  };
1464
1503
  const validateSubField = (rowIdx, subField, subValue, nextRows) => {
1465
1504
  const rowValues = { ...formValues, [field.name]: nextRows };
1466
- const err = runValidators(subValue, subField, rowValues, fieldTypes);
1505
+ const err = runValidators(subValue, subField, rowValues, fieldTypes, { messages: validationMessages });
1467
1506
  setRepeaterSubFieldError(field.name, rowIdx, subField.name, err);
1468
1507
  };
1469
1508
  const handleSubFieldChange = (rowIdx, subField, subValue) => {
@@ -1581,7 +1620,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
1581
1620
  const renderDependentGroup = (parentField, dependents) => {
1582
1621
  const firstWithLabel = dependents.find((f) => getDependsOnLabel(f)) || dependents[0];
1583
1622
  const firstWithMessage = dependents.find((f) => getDependsOnMessage(f)) || dependents[0];
1584
- const groupLabel = getDependsOnLabel(firstWithLabel) || "Dependent properties";
1623
+ const groupLabel = getDependsOnLabel(firstWithLabel) || dependentPropertiesLabel;
1585
1624
  const rawMessage = getDependsOnMessage(firstWithMessage);
1586
1625
  const tooltipMessage = typeof rawMessage === "function" ? rawMessage(parentField.label) : rawMessage || "";
1587
1626
  return /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Tile, { key: `dep-${parentField.name}`, compact: true }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "column", gap }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", align: "center", gap: "xs" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { format: { fontWeight: "demibold" } }, groupLabel, " ", tooltipMessage && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Link, { inline: true, variant: "dark", overlay: /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Tooltip, null, tooltipMessage) }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Icon, { name: "info" })))), renderFieldSubset(dependents)));
@@ -1881,7 +1920,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
1881
1920
  currentStep,
1882
1921
  stepNames: steps.map((s) => s.title)
1883
1922
  }
1884
- ), formReadOnly && readOnlyMessage && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Alert, { title: readOnlyTitle, variant: "warning" }, readOnlyMessage), !addAlert && formError && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Alert, { title: errorTitle, variant: "danger" }, typeof formError === "string" ? formError : void 0), !addAlert && formSuccess && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Alert, { title: successTitle, variant: "success" }, formSuccess), isMultiStep && steps[currentStep] && steps[currentStep].render ? steps[currentStep].render({
1923
+ ), showReadOnlyAlert && formReadOnly && readOnlyMessage && (renderReadOnlyAlert ? renderReadOnlyAlert({ title: readOnlyTitle, message: readOnlyMessage }) : /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Alert, { title: readOnlyTitle, variant: "warning" }, readOnlyMessage)), showInlineAlerts && !addAlert && formError && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Alert, { title: errorTitle, variant: "danger" }, typeof formError === "string" ? formError : void 0), showInlineAlerts && !addAlert && formSuccess && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Alert, { title: successTitle, variant: "success" }, formSuccess), isMultiStep && steps[currentStep] && steps[currentStep].render ? steps[currentStep].render({
1885
1924
  values: formValues,
1886
1925
  goNext: handleNext,
1887
1926
  goBack: handleBack,
package/dist/form.mjs CHANGED
@@ -202,12 +202,14 @@ var collectAsyncValidatorPromises = (value, field, allValues, context) => {
202
202
  };
203
203
  var runValidators = (value, field, allValues, fieldTypes, options = {}) => {
204
204
  const includeCustomValidators = options.includeCustomValidators !== false;
205
+ const msg = options.messages || {};
205
206
  if (field.type === "display" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList") return null;
206
207
  const isRequired = resolveRequired(field, allValues);
207
208
  const plugin = fieldTypes && fieldTypes[field.type];
208
209
  const empty = plugin && plugin.isEmpty ? plugin.isEmpty(value) : isValueEmpty(value, field);
209
210
  if (isRequired && empty) {
210
- return `${field.label} is required`;
211
+ const fn = msg.required || ((label) => `${label} is required`);
212
+ return typeof fn === "function" ? fn(field.label) : fn;
211
213
  }
212
214
  if (empty) return null;
213
215
  if (field.useDefaultValidators !== false) {
@@ -216,23 +218,27 @@ var runValidators = (value, field, allValues, fieldTypes, options = {}) => {
216
218
  }
217
219
  if (field.pattern && typeof value === "string") {
218
220
  if (!field.pattern.test(value)) {
219
- return field.patternMessage || "Invalid format";
221
+ return field.patternMessage || msg.invalidFormat || "Invalid format";
220
222
  }
221
223
  }
222
224
  if (typeof value === "string") {
223
225
  if (field.minLength != null && value.length < field.minLength) {
224
- return `Must be at least ${field.minLength} characters`;
226
+ const fn = msg.minLength || ((min) => `Must be at least ${min} characters`);
227
+ return typeof fn === "function" ? fn(field.minLength) : fn;
225
228
  }
226
229
  if (field.maxLength != null && value.length > field.maxLength) {
227
- return `Must be no more than ${field.maxLength} characters`;
230
+ const fn = msg.maxLength || ((max) => `Must be no more than ${max} characters`);
231
+ return typeof fn === "function" ? fn(field.maxLength) : fn;
228
232
  }
229
233
  }
230
234
  if (typeof value === "number") {
231
235
  if (field.min != null && value < field.min) {
232
- return `Must be at least ${field.min}`;
236
+ const fn = msg.minValue || ((min) => `Must be at least ${min}`);
237
+ return typeof fn === "function" ? fn(field.min) : fn;
233
238
  }
234
239
  if (field.max != null && value > field.max) {
235
- return `Must be no more than ${field.max}`;
240
+ const fn = msg.maxValue || ((max) => `Must be no more than ${max}`);
241
+ return typeof fn === "function" ? fn(field.max) : fn;
236
242
  }
237
243
  }
238
244
  if (field.type === "date" && isDateValueObject(value)) {
@@ -438,8 +444,18 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
438
444
  // string — warning alert when readOnly
439
445
  alerts,
440
446
  // { addAlert, readOnlyTitle, errorTitle, successTitle }
441
- errors: controlledErrors
447
+ errors: controlledErrors,
442
448
  // controlled validation errors
449
+ showReadOnlyAlert = true,
450
+ // show warning Alert when readOnly is true
451
+ showInlineAlerts = true,
452
+ // show inline form-level error/success Alerts
453
+ renderReadOnlyAlert,
454
+ // (context: { title, message }) => ReactNode — custom readOnly alert renderer
455
+ renderFieldError,
456
+ // (error: string, field: object) => ReactNode — custom field error renderer
457
+ defaultCurrency = "USD"
458
+ // form-level default ISO 4217 currency code for currency fields
443
459
  } = props;
444
460
  const {
445
461
  onDirtyChange,
@@ -451,6 +467,23 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
451
467
  const cancelButtonLabel = (labels == null ? void 0 : labels.cancel) || "Cancel";
452
468
  const backButtonLabel = (labels == null ? void 0 : labels.back) || "Back";
453
469
  const nextButtonLabel = (labels == null ? void 0 : labels.next) || "Next";
470
+ const requiredMessage = (labels == null ? void 0 : labels.required) || ((label) => `${label} is required`);
471
+ const invalidFormatMessage = (labels == null ? void 0 : labels.invalidFormat) || "Invalid format";
472
+ const minLengthMessage = (labels == null ? void 0 : labels.minLength) || ((min) => `Must be at least ${min} characters`);
473
+ const maxLengthMessage = (labels == null ? void 0 : labels.maxLength) || ((max) => `Must be no more than ${max} characters`);
474
+ const minValueMessage = (labels == null ? void 0 : labels.minValue) || ((min) => `Must be at least ${min}`);
475
+ const maxValueMessage = (labels == null ? void 0 : labels.maxValue) || ((max) => `Must be no more than ${max}`);
476
+ const dependentPropertiesLabel = (labels == null ? void 0 : labels.dependentProperties) || "Dependent properties";
477
+ const repeaterAddLabel = (labels == null ? void 0 : labels.repeaterAdd) || "Add";
478
+ const repeaterRemoveLabel = (labels == null ? void 0 : labels.repeaterRemove) || "Remove";
479
+ const validationMessages = labels ? {
480
+ required: requiredMessage,
481
+ invalidFormat: invalidFormatMessage,
482
+ minLength: minLengthMessage,
483
+ maxLength: maxLengthMessage,
484
+ minValue: minValueMessage,
485
+ maxValue: maxValueMessage
486
+ } : void 0;
454
487
  const addAlert = alerts == null ? void 0 : alerts.addAlert;
455
488
  const readOnlyTitle = (alerts == null ? void 0 : alerts.readOnlyTitle) || "Read Only";
456
489
  const errorTitle = (alerts == null ? void 0 : alerts.errorTitle) || "Error";
@@ -689,7 +722,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
689
722
  const rowValues = { ...allValues, [field.name]: rows };
690
723
  subFields.forEach((subField) => {
691
724
  if (subField.visible && !subField.visible(rowValues)) return;
692
- const err = runValidators(row == null ? void 0 : row[subField.name], subField, rowValues, fieldTypes);
725
+ const err = runValidators(row == null ? void 0 : row[subField.name], subField, rowValues, fieldTypes, { messages: validationMessages });
693
726
  if (!err) return;
694
727
  const key = getRepeaterErrorKey(field.name, rowIdx, subField.name);
695
728
  errors[key] = err;
@@ -716,9 +749,9 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
716
749
  );
717
750
  return repeaterResult.errors[name] || null;
718
751
  }
719
- return runValidators(value != null ? value : formValues[name], field, formValues, fieldTypes);
752
+ return runValidators(value != null ? value : formValues[name], field, formValues, fieldTypes, { messages: validationMessages });
720
753
  },
721
- [fieldByName, formValues, validateRepeaterField, fieldTypes]
754
+ [fieldByName, formValues, validateRepeaterField, fieldTypes, validationMessages]
722
755
  );
723
756
  const validateVisibleFields = useCallback(
724
757
  (fieldSubset) => {
@@ -734,7 +767,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
734
767
  }
735
768
  continue;
736
769
  }
737
- const err = runValidators(formValues[field.name], field, formValues, fieldTypes);
770
+ const err = runValidators(formValues[field.name], field, formValues, fieldTypes, { messages: validationMessages });
738
771
  if (err) {
739
772
  errors[field.name] = err;
740
773
  hasErrors = true;
@@ -742,14 +775,14 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
742
775
  }
743
776
  return { errors, hasErrors };
744
777
  },
745
- [visibleFields, formValues, validateRepeaterField, fieldTypes]
778
+ [visibleFields, formValues, validateRepeaterField, fieldTypes, validationMessages]
746
779
  );
747
780
  const runAsyncValidation = useCallback(
748
781
  (name, value) => {
749
782
  const field = fieldByName.get(name);
750
783
  if (!field || field.type === "repeater") return null;
751
784
  const val = value != null ? value : formValues[name];
752
- const syncError = runValidators(val, field, formValues, fieldTypes, { includeCustomValidators: false });
785
+ const syncError = runValidators(val, field, formValues, fieldTypes, { includeCustomValidators: false, messages: validationMessages });
753
786
  const prevController = asyncAbortRef.current.get(name);
754
787
  if (prevController) prevController.abort();
755
788
  asyncAbortRef.current.delete(name);
@@ -1118,6 +1151,12 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
1118
1151
  [replaceErrors]
1119
1152
  );
1120
1153
  const renderField = (field) => {
1154
+ const fieldError = formErrors[field.name] || null;
1155
+ const rendered = renderFieldInner(field);
1156
+ if (!renderFieldError || !fieldError) return rendered;
1157
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, rendered, renderFieldError(fieldError, field));
1158
+ };
1159
+ const renderFieldInner = (field) => {
1121
1160
  const fieldValue = formValues[field.name];
1122
1161
  const fieldError = formErrors[field.name] || null;
1123
1162
  const hasError = !!fieldError;
@@ -1184,7 +1223,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
1184
1223
  readOnly: isReadOnly,
1185
1224
  disabled: isDisabled,
1186
1225
  error: hasError,
1187
- validationMessage: fieldError || void 0,
1226
+ validationMessage: renderFieldError ? void 0 : fieldError || void 0,
1188
1227
  ...field.loading || validatingFields[field.name] ? { loading: true } : {},
1189
1228
  ...field.fieldProps || {}
1190
1229
  };
@@ -1254,7 +1293,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
1254
1293
  CurrencyInput,
1255
1294
  {
1256
1295
  ...commonProps,
1257
- currency: field.currency || "USD",
1296
+ currency: field.currency || defaultCurrency,
1258
1297
  value: fieldValue,
1259
1298
  min: field.min,
1260
1299
  max: field.max,
@@ -1432,8 +1471,8 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
1432
1471
  const renderRemoveControl = repeaterProps.renderRemove;
1433
1472
  const renderMoveUpControl = repeaterProps.renderMoveUp;
1434
1473
  const renderMoveDownControl = repeaterProps.renderMoveDown;
1435
- const addLabel = repeaterProps.addLabel || "Add";
1436
- const removeLabel = repeaterProps.removeLabel || "Remove";
1474
+ const addLabel = repeaterProps.addLabel || repeaterAddLabel;
1475
+ const removeLabel = repeaterProps.removeLabel || repeaterRemoveLabel;
1437
1476
  const moveUpLabel = repeaterProps.moveUpLabel || "Up";
1438
1477
  const moveDownLabel = repeaterProps.moveDownLabel || "Down";
1439
1478
  const canEditRows = !isReadOnly && !isDisabled;
@@ -1467,7 +1506,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
1467
1506
  };
1468
1507
  const validateSubField = (rowIdx, subField, subValue, nextRows) => {
1469
1508
  const rowValues = { ...formValues, [field.name]: nextRows };
1470
- const err = runValidators(subValue, subField, rowValues, fieldTypes);
1509
+ const err = runValidators(subValue, subField, rowValues, fieldTypes, { messages: validationMessages });
1471
1510
  setRepeaterSubFieldError(field.name, rowIdx, subField.name, err);
1472
1511
  };
1473
1512
  const handleSubFieldChange = (rowIdx, subField, subValue) => {
@@ -1585,7 +1624,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
1585
1624
  const renderDependentGroup = (parentField, dependents) => {
1586
1625
  const firstWithLabel = dependents.find((f) => getDependsOnLabel(f)) || dependents[0];
1587
1626
  const firstWithMessage = dependents.find((f) => getDependsOnMessage(f)) || dependents[0];
1588
- const groupLabel = getDependsOnLabel(firstWithLabel) || "Dependent properties";
1627
+ const groupLabel = getDependsOnLabel(firstWithLabel) || dependentPropertiesLabel;
1589
1628
  const rawMessage = getDependsOnMessage(firstWithMessage);
1590
1629
  const tooltipMessage = typeof rawMessage === "function" ? rawMessage(parentField.label) : rawMessage || "";
1591
1630
  return /* @__PURE__ */ React.createElement(Tile, { key: `dep-${parentField.name}`, compact: true }, /* @__PURE__ */ React.createElement(Flex, { direction: "column", gap }, /* @__PURE__ */ React.createElement(Flex, { direction: "row", align: "center", gap: "xs" }, /* @__PURE__ */ React.createElement(Text, { format: { fontWeight: "demibold" } }, groupLabel, " ", tooltipMessage && /* @__PURE__ */ React.createElement(Link, { inline: true, variant: "dark", overlay: /* @__PURE__ */ React.createElement(Tooltip, null, tooltipMessage) }, /* @__PURE__ */ React.createElement(Icon, { name: "info" })))), renderFieldSubset(dependents)));
@@ -1885,7 +1924,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
1885
1924
  currentStep,
1886
1925
  stepNames: steps.map((s) => s.title)
1887
1926
  }
1888
- ), formReadOnly && readOnlyMessage && /* @__PURE__ */ React.createElement(Alert, { title: readOnlyTitle, variant: "warning" }, readOnlyMessage), !addAlert && formError && /* @__PURE__ */ React.createElement(Alert, { title: errorTitle, variant: "danger" }, typeof formError === "string" ? formError : void 0), !addAlert && formSuccess && /* @__PURE__ */ React.createElement(Alert, { title: successTitle, variant: "success" }, formSuccess), isMultiStep && steps[currentStep] && steps[currentStep].render ? steps[currentStep].render({
1927
+ ), showReadOnlyAlert && formReadOnly && readOnlyMessage && (renderReadOnlyAlert ? renderReadOnlyAlert({ title: readOnlyTitle, message: readOnlyMessage }) : /* @__PURE__ */ React.createElement(Alert, { title: readOnlyTitle, variant: "warning" }, readOnlyMessage)), showInlineAlerts && !addAlert && formError && /* @__PURE__ */ React.createElement(Alert, { title: errorTitle, variant: "danger" }, typeof formError === "string" ? formError : void 0), showInlineAlerts && !addAlert && formSuccess && /* @__PURE__ */ React.createElement(Alert, { title: successTitle, variant: "success" }, formSuccess), isMultiStep && steps[currentStep] && steps[currentStep].render ? steps[currentStep].render({
1889
1928
  values: formValues,
1890
1929
  goNext: handleNext,
1891
1930
  goBack: handleBack,