hs-uix 1.6.4 → 1.6.5

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/datatable.js CHANGED
@@ -187,7 +187,7 @@ var DataTable = ({
187
187
  // show First/Last page buttons (default: auto when pageCount > 5)
188
188
  // Row count
189
189
  title,
190
- // optional title shown above the table toolbar
190
+ // optional title shown as demibold text above the table toolbar
191
191
  showRowCount = true,
192
192
  // show "X records" / "X of Y records" text
193
193
  rowCountBold = false,
@@ -657,8 +657,7 @@ var DataTable = ({
657
657
  selectionResetRef.current = combinedSelectionResetKey;
658
658
  }, [combinedSelectionResetKey, selectable, externalSelectedIds]);
659
659
  const selectedIds = externalSelectedIds != null ? new Set(externalSelectedIds) : internalSelectedIds;
660
- const showToolbarCount = showRowCount && !title && displayCount > 0 && !(showSelectionBar && selectable && selectedIds.size > 0);
661
- const showTitleCount = showRowCount && !!title && displayCount > 0 && !(showSelectionBar && selectable && selectedIds.size > 0);
660
+ const showToolbarCount = showRowCount && displayCount > 0 && !(showSelectionBar && selectable && selectedIds.size > 0);
662
661
  const hasToolbarContent = showSearch && searchFields.length > 0 || filters.length > 0 || activeChips.length > 0 && (showFilterBadges || showClearFiltersButton) || showToolbarCount;
663
662
  const showRowActionsColumn = !!rowActions && !(hideRowActionsWhenSelectionActive && selectable && selectedIds.size > 0);
664
663
  const applySelection = (0, import_react.useCallback)((nextSet) => {
@@ -1009,7 +1008,7 @@ var DataTable = ({
1009
1008
  }
1010
1009
  );
1011
1010
  };
1012
- return /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "column", gap: "xs" }, title && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", align: "center", justify: "between", gap: "sm" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Heading, null, title), showTitleCount && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { variant: "microcopy", format: rowCountBold ? { fontWeight: "bold" } : void 0 }, recordCountLabel)), hasToolbarContent && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", gap: "sm" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Box, { flex: 3 }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "column", gap: "sm" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", align: "center", gap: "sm", wrap: "wrap" }, showSearch && searchFields.length > 0 && /* @__PURE__ */ import_react.default.createElement(
1011
+ return /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "column", gap: "xs" }, title && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", align: "center", justify: "between", gap: "sm" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { format: { fontWeight: "demibold" } }, title)), hasToolbarContent && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", gap: "sm" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Box, { flex: 3 }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "column", gap: "sm" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", align: "center", gap: "sm", wrap: "wrap" }, showSearch && searchFields.length > 0 && /* @__PURE__ */ import_react.default.createElement(
1013
1012
  import_ui_extensions.SearchInput,
1014
1013
  {
1015
1014
  name: "datatable-search",
@@ -10,7 +10,6 @@ import {
10
10
  EmptyState,
11
11
  ErrorState,
12
12
  Flex,
13
- Heading,
14
13
  Icon,
15
14
  Input,
16
15
  Link,
@@ -184,7 +183,7 @@ var DataTable = ({
184
183
  // show First/Last page buttons (default: auto when pageCount > 5)
185
184
  // Row count
186
185
  title,
187
- // optional title shown above the table toolbar
186
+ // optional title shown as demibold text above the table toolbar
188
187
  showRowCount = true,
189
188
  // show "X records" / "X of Y records" text
190
189
  rowCountBold = false,
@@ -654,8 +653,7 @@ var DataTable = ({
654
653
  selectionResetRef.current = combinedSelectionResetKey;
655
654
  }, [combinedSelectionResetKey, selectable, externalSelectedIds]);
656
655
  const selectedIds = externalSelectedIds != null ? new Set(externalSelectedIds) : internalSelectedIds;
657
- const showToolbarCount = showRowCount && !title && displayCount > 0 && !(showSelectionBar && selectable && selectedIds.size > 0);
658
- const showTitleCount = showRowCount && !!title && displayCount > 0 && !(showSelectionBar && selectable && selectedIds.size > 0);
656
+ const showToolbarCount = showRowCount && displayCount > 0 && !(showSelectionBar && selectable && selectedIds.size > 0);
659
657
  const hasToolbarContent = showSearch && searchFields.length > 0 || filters.length > 0 || activeChips.length > 0 && (showFilterBadges || showClearFiltersButton) || showToolbarCount;
660
658
  const showRowActionsColumn = !!rowActions && !(hideRowActionsWhenSelectionActive && selectable && selectedIds.size > 0);
661
659
  const applySelection = useCallback((nextSet) => {
@@ -1006,7 +1004,7 @@ var DataTable = ({
1006
1004
  }
1007
1005
  );
1008
1006
  };
1009
- return /* @__PURE__ */ React.createElement(Flex, { direction: "column", gap: "xs" }, title && /* @__PURE__ */ React.createElement(Flex, { direction: "row", align: "center", justify: "between", gap: "sm" }, /* @__PURE__ */ React.createElement(Heading, null, title), showTitleCount && /* @__PURE__ */ React.createElement(Text, { variant: "microcopy", format: rowCountBold ? { fontWeight: "bold" } : void 0 }, recordCountLabel)), hasToolbarContent && /* @__PURE__ */ React.createElement(Flex, { direction: "row", gap: "sm" }, /* @__PURE__ */ React.createElement(Box, { flex: 3 }, /* @__PURE__ */ React.createElement(Flex, { direction: "column", gap: "sm" }, /* @__PURE__ */ React.createElement(Flex, { direction: "row", align: "center", gap: "sm", wrap: "wrap" }, showSearch && searchFields.length > 0 && /* @__PURE__ */ React.createElement(
1007
+ return /* @__PURE__ */ React.createElement(Flex, { direction: "column", gap: "xs" }, title && /* @__PURE__ */ React.createElement(Flex, { direction: "row", align: "center", justify: "between", gap: "sm" }, /* @__PURE__ */ React.createElement(Text, { format: { fontWeight: "demibold" } }, title)), hasToolbarContent && /* @__PURE__ */ React.createElement(Flex, { direction: "row", gap: "sm" }, /* @__PURE__ */ React.createElement(Box, { flex: 3 }, /* @__PURE__ */ React.createElement(Flex, { direction: "column", gap: "sm" }, /* @__PURE__ */ React.createElement(Flex, { direction: "row", align: "center", gap: "sm", wrap: "wrap" }, showSearch && searchFields.length > 0 && /* @__PURE__ */ React.createElement(
1010
1008
  SearchInput,
1011
1009
  {
1012
1010
  name: "datatable-search",
package/dist/form.js CHANGED
@@ -378,8 +378,12 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
378
378
  // validate on blur
379
379
  validateOnSubmit = true,
380
380
  // validate all before onSubmit
381
- onValidationChange
381
+ onValidationChange,
382
382
  // (errors) => void
383
+ onValidationFail,
384
+ // ({ errors, fields, firstInvalidField }) => void — called when submit-time validation blocks submission
385
+ openSectionOnValidationFail = false
386
+ // auto-open accordion section containing first invalid field on submit failure
383
387
  } = props;
384
388
  const {
385
389
  steps,
@@ -528,6 +532,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
528
532
  "tooltip",
529
533
  "required",
530
534
  "readOnly",
535
+ "alwaysEditable",
531
536
  "disabled",
532
537
  "defaultValue",
533
538
  "fieldProps",
@@ -700,6 +705,17 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
700
705
  }
701
706
  return map;
702
707
  }, [fields]);
708
+ const sectionIdByFieldName = (0, import_react.useMemo)(() => {
709
+ const map = /* @__PURE__ */ new Map();
710
+ if (Array.isArray(sections)) {
711
+ for (const sec of sections) {
712
+ if (!sec || !Array.isArray(sec.fields)) continue;
713
+ for (const name of sec.fields) map.set(name, sec.id);
714
+ }
715
+ }
716
+ return map;
717
+ }, [sections]);
718
+ const [validationOpenSection, setValidationOpenSection] = (0, import_react.useState)(null);
703
719
  const isDev = typeof process === "undefined" || !process.env || process.env.NODE_ENV !== "production";
704
720
  const configWarningsRef = (0, import_react.useRef)(/* @__PURE__ */ new Set());
705
721
  const warnConfig = (0, import_react.useCallback)((message) => {
@@ -1280,10 +1296,35 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
1280
1296
  const handleSubmit = (0, import_react.useCallback)(
1281
1297
  async (e) => {
1282
1298
  if (e && e.preventDefault) e.preventDefault();
1299
+ const reportValidationFailure = (errors) => {
1300
+ const errorNames = Object.keys(errors).filter((n) => !!errors[n]);
1301
+ if (errorNames.length === 0) return;
1302
+ const orderedNames = allVisibleFields.map((f) => f.name).filter((n) => errorNames.includes(n));
1303
+ for (const n of errorNames) if (!orderedNames.includes(n)) orderedNames.push(n);
1304
+ const fieldInfos = orderedNames.map((name) => {
1305
+ const f = fieldByName.get(name);
1306
+ return {
1307
+ name,
1308
+ label: f == null ? void 0 : f.label,
1309
+ sectionId: sectionIdByFieldName.get(name)
1310
+ };
1311
+ });
1312
+ const firstInvalidField = fieldInfos[0];
1313
+ if (openSectionOnValidationFail && (firstInvalidField == null ? void 0 : firstInvalidField.sectionId)) {
1314
+ setValidationOpenSection({
1315
+ id: firstInvalidField.sectionId,
1316
+ nonce: ((validationOpenSection == null ? void 0 : validationOpenSection.nonce) || 0) + 1
1317
+ });
1318
+ }
1319
+ if (onValidationFail) {
1320
+ onValidationFail({ errors, fields: fieldInfos, firstInvalidField });
1321
+ }
1322
+ };
1283
1323
  if (validateOnSubmit) {
1284
1324
  const { errors, hasErrors } = validateVisibleFields(allVisibleFields);
1285
1325
  if (hasErrors) {
1286
1326
  replaceErrors(errors);
1327
+ reportValidationFailure(errors);
1287
1328
  return;
1288
1329
  }
1289
1330
  const asyncSubmitValidations = getAsyncValidationTargets(allVisibleFields).map((target) => runAsyncValidationTarget(target)).filter(Boolean);
@@ -1295,7 +1336,10 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
1295
1336
  ])
1296
1337
  ];
1297
1338
  await Promise.all(pendingValidations);
1298
- if (fieldSetHasErrors(formErrorsRef.current, allVisibleFields)) return;
1339
+ if (fieldSetHasErrors(formErrorsRef.current, allVisibleFields)) {
1340
+ reportValidationFailure(formErrorsRef.current);
1341
+ return;
1342
+ }
1299
1343
  }
1300
1344
  }
1301
1345
  const reset = () => {
@@ -1341,7 +1385,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
1341
1385
  if (controlledLoading == null) setInternalLoading(false);
1342
1386
  }
1343
1387
  },
1344
- [validateOnSubmit, allVisibleFields, validateVisibleFields, replaceErrors, onSubmit, values, controlledLoading, transformValues, onBeforeSubmit, onSubmitSuccess, onSubmitError, resetOnSuccess, formValues, fieldByName, getAsyncValidationTargets, runAsyncValidationTarget]
1388
+ [validateOnSubmit, allVisibleFields, validateVisibleFields, replaceErrors, onSubmit, values, controlledLoading, transformValues, onBeforeSubmit, onSubmitSuccess, onSubmitError, resetOnSuccess, formValues, fieldByName, getAsyncValidationTargets, runAsyncValidationTarget, onValidationFail, openSectionOnValidationFail, sectionIdByFieldName, validationOpenSection]
1345
1389
  );
1346
1390
  const handleNext = (0, import_react.useCallback)(async () => {
1347
1391
  if (!isMultiStep) return;
@@ -1434,8 +1478,9 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
1434
1478
  const fieldError = formErrors[field.name] || null;
1435
1479
  const hasError = !!fieldError;
1436
1480
  const isRequired = showRequiredIndicator && resolveRequired(field, formValues);
1437
- const isReadOnly = field.readOnly || formReadOnly;
1438
- const isDisabled = disabled || resolveDisabled(field, formValues) || formReadOnly;
1481
+ const fieldFormReadOnly = field.alwaysEditable ? false : formReadOnly;
1482
+ const isReadOnly = field.readOnly || fieldFormReadOnly;
1483
+ const isDisabled = disabled || resolveDisabled(field, formValues) || fieldFormReadOnly;
1439
1484
  const fieldOnChange = field.debounce ? (v) => handleDebouncedFieldChange(field.name, v) : (v) => handleFieldChange(field.name, v);
1440
1485
  if (field.type === "display" || field.type === "slot") {
1441
1486
  if (field.render) {
@@ -1478,8 +1523,9 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
1478
1523
  const sfValue = formValues[sf.name];
1479
1524
  const sfError = formErrors[sf.name] || null;
1480
1525
  const sfLabel = itemIdx === 0 ? sf.label : void 0;
1481
- const sfReadOnly = sf.readOnly || formReadOnly;
1482
- const sfDisabled = disabled || resolveDisabled(sf, formValues) || formReadOnly;
1526
+ const sfFormReadOnly = sf.alwaysEditable ? false : formReadOnly;
1527
+ const sfReadOnly = sf.readOnly || sfFormReadOnly;
1528
+ const sfDisabled = disabled || resolveDisabled(sf, formValues) || sfFormReadOnly;
1483
1529
  const sfOnChange = sf.debounce ? (v) => handleDebouncedFieldChange(sf.name, v) : (v) => handleFieldChange(sf.name, v);
1484
1530
  const sfProps = {
1485
1531
  name: sf.name,
@@ -2276,13 +2322,16 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
2276
2322
  const sectionContext = { values: formValues, errors: formErrors };
2277
2323
  const sectionOverrides = sec.columns ? { columns: sec.columns } : void 0;
2278
2324
  const accordionContent = /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "column", gap }, sec.renderBefore && sec.renderBefore(sectionContext), renderFieldSubset(sectionFields, sectionOverrides), sec.renderAfter && sec.renderAfter(sectionContext));
2325
+ const isValidationOverrideTarget = validationOpenSection && validationOpenSection.id === sec.id;
2326
+ const accordionKey = isValidationOverrideTarget ? `${sec.id}::open::${validationOpenSection.nonce}` : sec.id;
2327
+ const accordionDefaultOpen = isValidationOverrideTarget ? true : sec.defaultOpen !== false;
2279
2328
  const accordion = /* @__PURE__ */ import_react.default.createElement(
2280
2329
  import_ui_extensions.Accordion,
2281
2330
  {
2282
- key: sec.id,
2331
+ key: accordionKey,
2283
2332
  title: sec.label,
2284
2333
  size: "sm",
2285
- defaultOpen: sec.defaultOpen !== false
2334
+ defaultOpen: accordionDefaultOpen
2286
2335
  },
2287
2336
  accordionContent
2288
2337
  );
package/dist/form.mjs CHANGED
@@ -382,8 +382,12 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
382
382
  // validate on blur
383
383
  validateOnSubmit = true,
384
384
  // validate all before onSubmit
385
- onValidationChange
385
+ onValidationChange,
386
386
  // (errors) => void
387
+ onValidationFail,
388
+ // ({ errors, fields, firstInvalidField }) => void — called when submit-time validation blocks submission
389
+ openSectionOnValidationFail = false
390
+ // auto-open accordion section containing first invalid field on submit failure
387
391
  } = props;
388
392
  const {
389
393
  steps,
@@ -532,6 +536,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
532
536
  "tooltip",
533
537
  "required",
534
538
  "readOnly",
539
+ "alwaysEditable",
535
540
  "disabled",
536
541
  "defaultValue",
537
542
  "fieldProps",
@@ -704,6 +709,17 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
704
709
  }
705
710
  return map;
706
711
  }, [fields]);
712
+ const sectionIdByFieldName = useMemo(() => {
713
+ const map = /* @__PURE__ */ new Map();
714
+ if (Array.isArray(sections)) {
715
+ for (const sec of sections) {
716
+ if (!sec || !Array.isArray(sec.fields)) continue;
717
+ for (const name of sec.fields) map.set(name, sec.id);
718
+ }
719
+ }
720
+ return map;
721
+ }, [sections]);
722
+ const [validationOpenSection, setValidationOpenSection] = useState(null);
707
723
  const isDev = typeof process === "undefined" || !process.env || process.env.NODE_ENV !== "production";
708
724
  const configWarningsRef = useRef(/* @__PURE__ */ new Set());
709
725
  const warnConfig = useCallback((message) => {
@@ -1284,10 +1300,35 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
1284
1300
  const handleSubmit = useCallback(
1285
1301
  async (e) => {
1286
1302
  if (e && e.preventDefault) e.preventDefault();
1303
+ const reportValidationFailure = (errors) => {
1304
+ const errorNames = Object.keys(errors).filter((n) => !!errors[n]);
1305
+ if (errorNames.length === 0) return;
1306
+ const orderedNames = allVisibleFields.map((f) => f.name).filter((n) => errorNames.includes(n));
1307
+ for (const n of errorNames) if (!orderedNames.includes(n)) orderedNames.push(n);
1308
+ const fieldInfos = orderedNames.map((name) => {
1309
+ const f = fieldByName.get(name);
1310
+ return {
1311
+ name,
1312
+ label: f == null ? void 0 : f.label,
1313
+ sectionId: sectionIdByFieldName.get(name)
1314
+ };
1315
+ });
1316
+ const firstInvalidField = fieldInfos[0];
1317
+ if (openSectionOnValidationFail && (firstInvalidField == null ? void 0 : firstInvalidField.sectionId)) {
1318
+ setValidationOpenSection({
1319
+ id: firstInvalidField.sectionId,
1320
+ nonce: ((validationOpenSection == null ? void 0 : validationOpenSection.nonce) || 0) + 1
1321
+ });
1322
+ }
1323
+ if (onValidationFail) {
1324
+ onValidationFail({ errors, fields: fieldInfos, firstInvalidField });
1325
+ }
1326
+ };
1287
1327
  if (validateOnSubmit) {
1288
1328
  const { errors, hasErrors } = validateVisibleFields(allVisibleFields);
1289
1329
  if (hasErrors) {
1290
1330
  replaceErrors(errors);
1331
+ reportValidationFailure(errors);
1291
1332
  return;
1292
1333
  }
1293
1334
  const asyncSubmitValidations = getAsyncValidationTargets(allVisibleFields).map((target) => runAsyncValidationTarget(target)).filter(Boolean);
@@ -1299,7 +1340,10 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
1299
1340
  ])
1300
1341
  ];
1301
1342
  await Promise.all(pendingValidations);
1302
- if (fieldSetHasErrors(formErrorsRef.current, allVisibleFields)) return;
1343
+ if (fieldSetHasErrors(formErrorsRef.current, allVisibleFields)) {
1344
+ reportValidationFailure(formErrorsRef.current);
1345
+ return;
1346
+ }
1303
1347
  }
1304
1348
  }
1305
1349
  const reset = () => {
@@ -1345,7 +1389,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
1345
1389
  if (controlledLoading == null) setInternalLoading(false);
1346
1390
  }
1347
1391
  },
1348
- [validateOnSubmit, allVisibleFields, validateVisibleFields, replaceErrors, onSubmit, values, controlledLoading, transformValues, onBeforeSubmit, onSubmitSuccess, onSubmitError, resetOnSuccess, formValues, fieldByName, getAsyncValidationTargets, runAsyncValidationTarget]
1392
+ [validateOnSubmit, allVisibleFields, validateVisibleFields, replaceErrors, onSubmit, values, controlledLoading, transformValues, onBeforeSubmit, onSubmitSuccess, onSubmitError, resetOnSuccess, formValues, fieldByName, getAsyncValidationTargets, runAsyncValidationTarget, onValidationFail, openSectionOnValidationFail, sectionIdByFieldName, validationOpenSection]
1349
1393
  );
1350
1394
  const handleNext = useCallback(async () => {
1351
1395
  if (!isMultiStep) return;
@@ -1438,8 +1482,9 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
1438
1482
  const fieldError = formErrors[field.name] || null;
1439
1483
  const hasError = !!fieldError;
1440
1484
  const isRequired = showRequiredIndicator && resolveRequired(field, formValues);
1441
- const isReadOnly = field.readOnly || formReadOnly;
1442
- const isDisabled = disabled || resolveDisabled(field, formValues) || formReadOnly;
1485
+ const fieldFormReadOnly = field.alwaysEditable ? false : formReadOnly;
1486
+ const isReadOnly = field.readOnly || fieldFormReadOnly;
1487
+ const isDisabled = disabled || resolveDisabled(field, formValues) || fieldFormReadOnly;
1443
1488
  const fieldOnChange = field.debounce ? (v) => handleDebouncedFieldChange(field.name, v) : (v) => handleFieldChange(field.name, v);
1444
1489
  if (field.type === "display" || field.type === "slot") {
1445
1490
  if (field.render) {
@@ -1482,8 +1527,9 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
1482
1527
  const sfValue = formValues[sf.name];
1483
1528
  const sfError = formErrors[sf.name] || null;
1484
1529
  const sfLabel = itemIdx === 0 ? sf.label : void 0;
1485
- const sfReadOnly = sf.readOnly || formReadOnly;
1486
- const sfDisabled = disabled || resolveDisabled(sf, formValues) || formReadOnly;
1530
+ const sfFormReadOnly = sf.alwaysEditable ? false : formReadOnly;
1531
+ const sfReadOnly = sf.readOnly || sfFormReadOnly;
1532
+ const sfDisabled = disabled || resolveDisabled(sf, formValues) || sfFormReadOnly;
1487
1533
  const sfOnChange = sf.debounce ? (v) => handleDebouncedFieldChange(sf.name, v) : (v) => handleFieldChange(sf.name, v);
1488
1534
  const sfProps = {
1489
1535
  name: sf.name,
@@ -2280,13 +2326,16 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
2280
2326
  const sectionContext = { values: formValues, errors: formErrors };
2281
2327
  const sectionOverrides = sec.columns ? { columns: sec.columns } : void 0;
2282
2328
  const accordionContent = /* @__PURE__ */ React.createElement(Flex, { direction: "column", gap }, sec.renderBefore && sec.renderBefore(sectionContext), renderFieldSubset(sectionFields, sectionOverrides), sec.renderAfter && sec.renderAfter(sectionContext));
2329
+ const isValidationOverrideTarget = validationOpenSection && validationOpenSection.id === sec.id;
2330
+ const accordionKey = isValidationOverrideTarget ? `${sec.id}::open::${validationOpenSection.nonce}` : sec.id;
2331
+ const accordionDefaultOpen = isValidationOverrideTarget ? true : sec.defaultOpen !== false;
2283
2332
  const accordion = /* @__PURE__ */ React.createElement(
2284
2333
  Accordion,
2285
2334
  {
2286
- key: sec.id,
2335
+ key: accordionKey,
2287
2336
  title: sec.label,
2288
2337
  size: "sm",
2289
- defaultOpen: sec.defaultOpen !== false
2338
+ defaultOpen: accordionDefaultOpen
2290
2339
  },
2291
2340
  accordionContent
2292
2341
  );
package/dist/index.js CHANGED
@@ -218,7 +218,7 @@ var DataTable = ({
218
218
  // show First/Last page buttons (default: auto when pageCount > 5)
219
219
  // Row count
220
220
  title,
221
- // optional title shown above the table toolbar
221
+ // optional title shown as demibold text above the table toolbar
222
222
  showRowCount = true,
223
223
  // show "X records" / "X of Y records" text
224
224
  rowCountBold = false,
@@ -688,8 +688,7 @@ var DataTable = ({
688
688
  selectionResetRef.current = combinedSelectionResetKey;
689
689
  }, [combinedSelectionResetKey, selectable, externalSelectedIds]);
690
690
  const selectedIds = externalSelectedIds != null ? new Set(externalSelectedIds) : internalSelectedIds;
691
- const showToolbarCount = showRowCount && !title && displayCount > 0 && !(showSelectionBar && selectable && selectedIds.size > 0);
692
- const showTitleCount = showRowCount && !!title && displayCount > 0 && !(showSelectionBar && selectable && selectedIds.size > 0);
691
+ const showToolbarCount = showRowCount && displayCount > 0 && !(showSelectionBar && selectable && selectedIds.size > 0);
693
692
  const hasToolbarContent = showSearch && searchFields.length > 0 || filters.length > 0 || activeChips.length > 0 && (showFilterBadges || showClearFiltersButton) || showToolbarCount;
694
693
  const showRowActionsColumn = !!rowActions && !(hideRowActionsWhenSelectionActive && selectable && selectedIds.size > 0);
695
694
  const applySelection = (0, import_react.useCallback)((nextSet) => {
@@ -1040,7 +1039,7 @@ var DataTable = ({
1040
1039
  }
1041
1040
  );
1042
1041
  };
1043
- return /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "column", gap: "xs" }, title && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", align: "center", justify: "between", gap: "sm" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Heading, null, title), showTitleCount && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { variant: "microcopy", format: rowCountBold ? { fontWeight: "bold" } : void 0 }, recordCountLabel)), hasToolbarContent && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", gap: "sm" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Box, { flex: 3 }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "column", gap: "sm" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", align: "center", gap: "sm", wrap: "wrap" }, showSearch && searchFields.length > 0 && /* @__PURE__ */ import_react.default.createElement(
1042
+ return /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "column", gap: "xs" }, title && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", align: "center", justify: "between", gap: "sm" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { format: { fontWeight: "demibold" } }, title)), hasToolbarContent && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", gap: "sm" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Box, { flex: 3 }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "column", gap: "sm" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", align: "center", gap: "sm", wrap: "wrap" }, showSearch && searchFields.length > 0 && /* @__PURE__ */ import_react.default.createElement(
1044
1043
  import_ui_extensions.SearchInput,
1045
1044
  {
1046
1045
  name: "datatable-search",
@@ -1519,8 +1518,12 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
1519
1518
  // validate on blur
1520
1519
  validateOnSubmit = true,
1521
1520
  // validate all before onSubmit
1522
- onValidationChange
1521
+ onValidationChange,
1523
1522
  // (errors) => void
1523
+ onValidationFail,
1524
+ // ({ errors, fields, firstInvalidField }) => void — called when submit-time validation blocks submission
1525
+ openSectionOnValidationFail = false
1526
+ // auto-open accordion section containing first invalid field on submit failure
1524
1527
  } = props;
1525
1528
  const {
1526
1529
  steps,
@@ -1669,6 +1672,7 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
1669
1672
  "tooltip",
1670
1673
  "required",
1671
1674
  "readOnly",
1675
+ "alwaysEditable",
1672
1676
  "disabled",
1673
1677
  "defaultValue",
1674
1678
  "fieldProps",
@@ -1841,6 +1845,17 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
1841
1845
  }
1842
1846
  return map;
1843
1847
  }, [fields]);
1848
+ const sectionIdByFieldName = (0, import_react2.useMemo)(() => {
1849
+ const map = /* @__PURE__ */ new Map();
1850
+ if (Array.isArray(sections)) {
1851
+ for (const sec of sections) {
1852
+ if (!sec || !Array.isArray(sec.fields)) continue;
1853
+ for (const name of sec.fields) map.set(name, sec.id);
1854
+ }
1855
+ }
1856
+ return map;
1857
+ }, [sections]);
1858
+ const [validationOpenSection, setValidationOpenSection] = (0, import_react2.useState)(null);
1844
1859
  const isDev = typeof process === "undefined" || !process.env || process.env.NODE_ENV !== "production";
1845
1860
  const configWarningsRef = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
1846
1861
  const warnConfig = (0, import_react2.useCallback)((message) => {
@@ -2421,10 +2436,35 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
2421
2436
  const handleSubmit = (0, import_react2.useCallback)(
2422
2437
  async (e) => {
2423
2438
  if (e && e.preventDefault) e.preventDefault();
2439
+ const reportValidationFailure = (errors) => {
2440
+ const errorNames = Object.keys(errors).filter((n) => !!errors[n]);
2441
+ if (errorNames.length === 0) return;
2442
+ const orderedNames = allVisibleFields.map((f) => f.name).filter((n) => errorNames.includes(n));
2443
+ for (const n of errorNames) if (!orderedNames.includes(n)) orderedNames.push(n);
2444
+ const fieldInfos = orderedNames.map((name) => {
2445
+ const f = fieldByName.get(name);
2446
+ return {
2447
+ name,
2448
+ label: f == null ? void 0 : f.label,
2449
+ sectionId: sectionIdByFieldName.get(name)
2450
+ };
2451
+ });
2452
+ const firstInvalidField = fieldInfos[0];
2453
+ if (openSectionOnValidationFail && (firstInvalidField == null ? void 0 : firstInvalidField.sectionId)) {
2454
+ setValidationOpenSection({
2455
+ id: firstInvalidField.sectionId,
2456
+ nonce: ((validationOpenSection == null ? void 0 : validationOpenSection.nonce) || 0) + 1
2457
+ });
2458
+ }
2459
+ if (onValidationFail) {
2460
+ onValidationFail({ errors, fields: fieldInfos, firstInvalidField });
2461
+ }
2462
+ };
2424
2463
  if (validateOnSubmit) {
2425
2464
  const { errors, hasErrors } = validateVisibleFields(allVisibleFields);
2426
2465
  if (hasErrors) {
2427
2466
  replaceErrors(errors);
2467
+ reportValidationFailure(errors);
2428
2468
  return;
2429
2469
  }
2430
2470
  const asyncSubmitValidations = getAsyncValidationTargets(allVisibleFields).map((target) => runAsyncValidationTarget(target)).filter(Boolean);
@@ -2436,7 +2476,10 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
2436
2476
  ])
2437
2477
  ];
2438
2478
  await Promise.all(pendingValidations);
2439
- if (fieldSetHasErrors(formErrorsRef.current, allVisibleFields)) return;
2479
+ if (fieldSetHasErrors(formErrorsRef.current, allVisibleFields)) {
2480
+ reportValidationFailure(formErrorsRef.current);
2481
+ return;
2482
+ }
2440
2483
  }
2441
2484
  }
2442
2485
  const reset = () => {
@@ -2482,7 +2525,7 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
2482
2525
  if (controlledLoading == null) setInternalLoading(false);
2483
2526
  }
2484
2527
  },
2485
- [validateOnSubmit, allVisibleFields, validateVisibleFields, replaceErrors, onSubmit, values, controlledLoading, transformValues, onBeforeSubmit, onSubmitSuccess, onSubmitError, resetOnSuccess, formValues, fieldByName, getAsyncValidationTargets, runAsyncValidationTarget]
2528
+ [validateOnSubmit, allVisibleFields, validateVisibleFields, replaceErrors, onSubmit, values, controlledLoading, transformValues, onBeforeSubmit, onSubmitSuccess, onSubmitError, resetOnSuccess, formValues, fieldByName, getAsyncValidationTargets, runAsyncValidationTarget, onValidationFail, openSectionOnValidationFail, sectionIdByFieldName, validationOpenSection]
2486
2529
  );
2487
2530
  const handleNext = (0, import_react2.useCallback)(async () => {
2488
2531
  if (!isMultiStep) return;
@@ -2575,8 +2618,9 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
2575
2618
  const fieldError = formErrors[field.name] || null;
2576
2619
  const hasError = !!fieldError;
2577
2620
  const isRequired = showRequiredIndicator && resolveRequired(field, formValues);
2578
- const isReadOnly = field.readOnly || formReadOnly;
2579
- const isDisabled = disabled || resolveDisabled(field, formValues) || formReadOnly;
2621
+ const fieldFormReadOnly = field.alwaysEditable ? false : formReadOnly;
2622
+ const isReadOnly = field.readOnly || fieldFormReadOnly;
2623
+ const isDisabled = disabled || resolveDisabled(field, formValues) || fieldFormReadOnly;
2580
2624
  const fieldOnChange = field.debounce ? (v) => handleDebouncedFieldChange(field.name, v) : (v) => handleFieldChange(field.name, v);
2581
2625
  if (field.type === "display" || field.type === "slot") {
2582
2626
  if (field.render) {
@@ -2619,8 +2663,9 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
2619
2663
  const sfValue = formValues[sf.name];
2620
2664
  const sfError = formErrors[sf.name] || null;
2621
2665
  const sfLabel = itemIdx === 0 ? sf.label : void 0;
2622
- const sfReadOnly = sf.readOnly || formReadOnly;
2623
- const sfDisabled = disabled || resolveDisabled(sf, formValues) || formReadOnly;
2666
+ const sfFormReadOnly = sf.alwaysEditable ? false : formReadOnly;
2667
+ const sfReadOnly = sf.readOnly || sfFormReadOnly;
2668
+ const sfDisabled = disabled || resolveDisabled(sf, formValues) || sfFormReadOnly;
2624
2669
  const sfOnChange = sf.debounce ? (v) => handleDebouncedFieldChange(sf.name, v) : (v) => handleFieldChange(sf.name, v);
2625
2670
  const sfProps = {
2626
2671
  name: sf.name,
@@ -3417,13 +3462,16 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
3417
3462
  const sectionContext = { values: formValues, errors: formErrors };
3418
3463
  const sectionOverrides = sec.columns ? { columns: sec.columns } : void 0;
3419
3464
  const accordionContent = /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Flex, { direction: "column", gap }, sec.renderBefore && sec.renderBefore(sectionContext), renderFieldSubset(sectionFields, sectionOverrides), sec.renderAfter && sec.renderAfter(sectionContext));
3465
+ const isValidationOverrideTarget = validationOpenSection && validationOpenSection.id === sec.id;
3466
+ const accordionKey = isValidationOverrideTarget ? `${sec.id}::open::${validationOpenSection.nonce}` : sec.id;
3467
+ const accordionDefaultOpen = isValidationOverrideTarget ? true : sec.defaultOpen !== false;
3420
3468
  const accordion = /* @__PURE__ */ import_react2.default.createElement(
3421
3469
  import_ui_extensions2.Accordion,
3422
3470
  {
3423
- key: sec.id,
3471
+ key: accordionKey,
3424
3472
  title: sec.label,
3425
3473
  size: "sm",
3426
- defaultOpen: sec.defaultOpen !== false
3474
+ defaultOpen: accordionDefaultOpen
3427
3475
  },
3428
3476
  accordionContent
3429
3477
  );
package/dist/index.mjs CHANGED
@@ -10,7 +10,6 @@ import {
10
10
  EmptyState,
11
11
  ErrorState,
12
12
  Flex,
13
- Heading,
14
13
  Icon,
15
14
  Input,
16
15
  Link,
@@ -184,7 +183,7 @@ var DataTable = ({
184
183
  // show First/Last page buttons (default: auto when pageCount > 5)
185
184
  // Row count
186
185
  title,
187
- // optional title shown above the table toolbar
186
+ // optional title shown as demibold text above the table toolbar
188
187
  showRowCount = true,
189
188
  // show "X records" / "X of Y records" text
190
189
  rowCountBold = false,
@@ -654,8 +653,7 @@ var DataTable = ({
654
653
  selectionResetRef.current = combinedSelectionResetKey;
655
654
  }, [combinedSelectionResetKey, selectable, externalSelectedIds]);
656
655
  const selectedIds = externalSelectedIds != null ? new Set(externalSelectedIds) : internalSelectedIds;
657
- const showToolbarCount = showRowCount && !title && displayCount > 0 && !(showSelectionBar && selectable && selectedIds.size > 0);
658
- const showTitleCount = showRowCount && !!title && displayCount > 0 && !(showSelectionBar && selectable && selectedIds.size > 0);
656
+ const showToolbarCount = showRowCount && displayCount > 0 && !(showSelectionBar && selectable && selectedIds.size > 0);
659
657
  const hasToolbarContent = showSearch && searchFields.length > 0 || filters.length > 0 || activeChips.length > 0 && (showFilterBadges || showClearFiltersButton) || showToolbarCount;
660
658
  const showRowActionsColumn = !!rowActions && !(hideRowActionsWhenSelectionActive && selectable && selectedIds.size > 0);
661
659
  const applySelection = useCallback((nextSet) => {
@@ -1006,7 +1004,7 @@ var DataTable = ({
1006
1004
  }
1007
1005
  );
1008
1006
  };
1009
- return /* @__PURE__ */ React.createElement(Flex, { direction: "column", gap: "xs" }, title && /* @__PURE__ */ React.createElement(Flex, { direction: "row", align: "center", justify: "between", gap: "sm" }, /* @__PURE__ */ React.createElement(Heading, null, title), showTitleCount && /* @__PURE__ */ React.createElement(Text, { variant: "microcopy", format: rowCountBold ? { fontWeight: "bold" } : void 0 }, recordCountLabel)), hasToolbarContent && /* @__PURE__ */ React.createElement(Flex, { direction: "row", gap: "sm" }, /* @__PURE__ */ React.createElement(Box, { flex: 3 }, /* @__PURE__ */ React.createElement(Flex, { direction: "column", gap: "sm" }, /* @__PURE__ */ React.createElement(Flex, { direction: "row", align: "center", gap: "sm", wrap: "wrap" }, showSearch && searchFields.length > 0 && /* @__PURE__ */ React.createElement(
1007
+ return /* @__PURE__ */ React.createElement(Flex, { direction: "column", gap: "xs" }, title && /* @__PURE__ */ React.createElement(Flex, { direction: "row", align: "center", justify: "between", gap: "sm" }, /* @__PURE__ */ React.createElement(Text, { format: { fontWeight: "demibold" } }, title)), hasToolbarContent && /* @__PURE__ */ React.createElement(Flex, { direction: "row", gap: "sm" }, /* @__PURE__ */ React.createElement(Box, { flex: 3 }, /* @__PURE__ */ React.createElement(Flex, { direction: "column", gap: "sm" }, /* @__PURE__ */ React.createElement(Flex, { direction: "row", align: "center", gap: "sm", wrap: "wrap" }, showSearch && searchFields.length > 0 && /* @__PURE__ */ React.createElement(
1010
1008
  SearchInput,
1011
1009
  {
1012
1010
  name: "datatable-search",
@@ -1525,8 +1523,12 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
1525
1523
  // validate on blur
1526
1524
  validateOnSubmit = true,
1527
1525
  // validate all before onSubmit
1528
- onValidationChange
1526
+ onValidationChange,
1529
1527
  // (errors) => void
1528
+ onValidationFail,
1529
+ // ({ errors, fields, firstInvalidField }) => void — called when submit-time validation blocks submission
1530
+ openSectionOnValidationFail = false
1531
+ // auto-open accordion section containing first invalid field on submit failure
1530
1532
  } = props;
1531
1533
  const {
1532
1534
  steps,
@@ -1675,6 +1677,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
1675
1677
  "tooltip",
1676
1678
  "required",
1677
1679
  "readOnly",
1680
+ "alwaysEditable",
1678
1681
  "disabled",
1679
1682
  "defaultValue",
1680
1683
  "fieldProps",
@@ -1847,6 +1850,17 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
1847
1850
  }
1848
1851
  return map;
1849
1852
  }, [fields]);
1853
+ const sectionIdByFieldName = useMemo2(() => {
1854
+ const map = /* @__PURE__ */ new Map();
1855
+ if (Array.isArray(sections)) {
1856
+ for (const sec of sections) {
1857
+ if (!sec || !Array.isArray(sec.fields)) continue;
1858
+ for (const name of sec.fields) map.set(name, sec.id);
1859
+ }
1860
+ }
1861
+ return map;
1862
+ }, [sections]);
1863
+ const [validationOpenSection, setValidationOpenSection] = useState2(null);
1850
1864
  const isDev = typeof process === "undefined" || !process.env || process.env.NODE_ENV !== "production";
1851
1865
  const configWarningsRef = useRef2(/* @__PURE__ */ new Set());
1852
1866
  const warnConfig = useCallback2((message) => {
@@ -2427,10 +2441,35 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
2427
2441
  const handleSubmit = useCallback2(
2428
2442
  async (e) => {
2429
2443
  if (e && e.preventDefault) e.preventDefault();
2444
+ const reportValidationFailure = (errors) => {
2445
+ const errorNames = Object.keys(errors).filter((n) => !!errors[n]);
2446
+ if (errorNames.length === 0) return;
2447
+ const orderedNames = allVisibleFields.map((f) => f.name).filter((n) => errorNames.includes(n));
2448
+ for (const n of errorNames) if (!orderedNames.includes(n)) orderedNames.push(n);
2449
+ const fieldInfos = orderedNames.map((name) => {
2450
+ const f = fieldByName.get(name);
2451
+ return {
2452
+ name,
2453
+ label: f == null ? void 0 : f.label,
2454
+ sectionId: sectionIdByFieldName.get(name)
2455
+ };
2456
+ });
2457
+ const firstInvalidField = fieldInfos[0];
2458
+ if (openSectionOnValidationFail && (firstInvalidField == null ? void 0 : firstInvalidField.sectionId)) {
2459
+ setValidationOpenSection({
2460
+ id: firstInvalidField.sectionId,
2461
+ nonce: ((validationOpenSection == null ? void 0 : validationOpenSection.nonce) || 0) + 1
2462
+ });
2463
+ }
2464
+ if (onValidationFail) {
2465
+ onValidationFail({ errors, fields: fieldInfos, firstInvalidField });
2466
+ }
2467
+ };
2430
2468
  if (validateOnSubmit) {
2431
2469
  const { errors, hasErrors } = validateVisibleFields(allVisibleFields);
2432
2470
  if (hasErrors) {
2433
2471
  replaceErrors(errors);
2472
+ reportValidationFailure(errors);
2434
2473
  return;
2435
2474
  }
2436
2475
  const asyncSubmitValidations = getAsyncValidationTargets(allVisibleFields).map((target) => runAsyncValidationTarget(target)).filter(Boolean);
@@ -2442,7 +2481,10 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
2442
2481
  ])
2443
2482
  ];
2444
2483
  await Promise.all(pendingValidations);
2445
- if (fieldSetHasErrors(formErrorsRef.current, allVisibleFields)) return;
2484
+ if (fieldSetHasErrors(formErrorsRef.current, allVisibleFields)) {
2485
+ reportValidationFailure(formErrorsRef.current);
2486
+ return;
2487
+ }
2446
2488
  }
2447
2489
  }
2448
2490
  const reset = () => {
@@ -2488,7 +2530,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
2488
2530
  if (controlledLoading == null) setInternalLoading(false);
2489
2531
  }
2490
2532
  },
2491
- [validateOnSubmit, allVisibleFields, validateVisibleFields, replaceErrors, onSubmit, values, controlledLoading, transformValues, onBeforeSubmit, onSubmitSuccess, onSubmitError, resetOnSuccess, formValues, fieldByName, getAsyncValidationTargets, runAsyncValidationTarget]
2533
+ [validateOnSubmit, allVisibleFields, validateVisibleFields, replaceErrors, onSubmit, values, controlledLoading, transformValues, onBeforeSubmit, onSubmitSuccess, onSubmitError, resetOnSuccess, formValues, fieldByName, getAsyncValidationTargets, runAsyncValidationTarget, onValidationFail, openSectionOnValidationFail, sectionIdByFieldName, validationOpenSection]
2492
2534
  );
2493
2535
  const handleNext = useCallback2(async () => {
2494
2536
  if (!isMultiStep) return;
@@ -2581,8 +2623,9 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
2581
2623
  const fieldError = formErrors[field.name] || null;
2582
2624
  const hasError = !!fieldError;
2583
2625
  const isRequired = showRequiredIndicator && resolveRequired(field, formValues);
2584
- const isReadOnly = field.readOnly || formReadOnly;
2585
- const isDisabled = disabled || resolveDisabled(field, formValues) || formReadOnly;
2626
+ const fieldFormReadOnly = field.alwaysEditable ? false : formReadOnly;
2627
+ const isReadOnly = field.readOnly || fieldFormReadOnly;
2628
+ const isDisabled = disabled || resolveDisabled(field, formValues) || fieldFormReadOnly;
2586
2629
  const fieldOnChange = field.debounce ? (v) => handleDebouncedFieldChange(field.name, v) : (v) => handleFieldChange(field.name, v);
2587
2630
  if (field.type === "display" || field.type === "slot") {
2588
2631
  if (field.render) {
@@ -2625,8 +2668,9 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
2625
2668
  const sfValue = formValues[sf.name];
2626
2669
  const sfError = formErrors[sf.name] || null;
2627
2670
  const sfLabel = itemIdx === 0 ? sf.label : void 0;
2628
- const sfReadOnly = sf.readOnly || formReadOnly;
2629
- const sfDisabled = disabled || resolveDisabled(sf, formValues) || formReadOnly;
2671
+ const sfFormReadOnly = sf.alwaysEditable ? false : formReadOnly;
2672
+ const sfReadOnly = sf.readOnly || sfFormReadOnly;
2673
+ const sfDisabled = disabled || resolveDisabled(sf, formValues) || sfFormReadOnly;
2630
2674
  const sfOnChange = sf.debounce ? (v) => handleDebouncedFieldChange(sf.name, v) : (v) => handleFieldChange(sf.name, v);
2631
2675
  const sfProps = {
2632
2676
  name: sf.name,
@@ -3423,13 +3467,16 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
3423
3467
  const sectionContext = { values: formValues, errors: formErrors };
3424
3468
  const sectionOverrides = sec.columns ? { columns: sec.columns } : void 0;
3425
3469
  const accordionContent = /* @__PURE__ */ React2.createElement(Flex2, { direction: "column", gap }, sec.renderBefore && sec.renderBefore(sectionContext), renderFieldSubset(sectionFields, sectionOverrides), sec.renderAfter && sec.renderAfter(sectionContext));
3470
+ const isValidationOverrideTarget = validationOpenSection && validationOpenSection.id === sec.id;
3471
+ const accordionKey = isValidationOverrideTarget ? `${sec.id}::open::${validationOpenSection.nonce}` : sec.id;
3472
+ const accordionDefaultOpen = isValidationOverrideTarget ? true : sec.defaultOpen !== false;
3426
3473
  const accordion = /* @__PURE__ */ React2.createElement(
3427
3474
  Accordion,
3428
3475
  {
3429
- key: sec.id,
3476
+ key: accordionKey,
3430
3477
  title: sec.label,
3431
3478
  size: "sm",
3432
- defaultOpen: sec.defaultOpen !== false
3479
+ defaultOpen: accordionDefaultOpen
3433
3480
  },
3434
3481
  accordionContent
3435
3482
  );
@@ -5442,7 +5489,7 @@ var KeyValueList = ({ items = [], direction = "row", gap = "sm" }) => {
5442
5489
 
5443
5490
  // src/common-components/SectionHeader.js
5444
5491
  import React10 from "react";
5445
- import { Flex as Flex6, Heading as Heading2, Text as Text5 } from "@hubspot/ui-extensions";
5492
+ import { Flex as Flex6, Heading, Text as Text5 } from "@hubspot/ui-extensions";
5446
5493
  var SectionHeader = ({
5447
5494
  title,
5448
5495
  description,
@@ -5453,7 +5500,7 @@ var SectionHeader = ({
5453
5500
  }) => {
5454
5501
  const body = [];
5455
5502
  if (title != null) {
5456
- body.push(React10.createElement(Heading2, { key: "title", as: titleAs }, title));
5503
+ body.push(React10.createElement(Heading, { key: "title", as: titleAs }, title));
5457
5504
  }
5458
5505
  if (description != null) {
5459
5506
  body.push(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hs-uix",
3
- "version": "1.6.4",
3
+ "version": "1.6.5",
4
4
  "description": "Production-ready UI components for HubSpot UI Extensions — DataTable, FormBuilder, and more",
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.js",