hs-uix 1.2.3 → 1.4.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/datatable.d.ts CHANGED
@@ -3,6 +3,7 @@ export {
3
3
  DataTableProps,
4
4
  DataTableColumn,
5
5
  DataTableFilterConfig,
6
+ DataTableFilterType,
6
7
  DataTableGroupBy,
7
8
  DataTableSortDirection,
8
9
  DataTableSortObject,
@@ -18,4 +19,9 @@ export {
18
19
  DataTableSelectionAction,
19
20
  DataTableRowAction,
20
21
  DataTableSelectAllRequestPayload,
22
+ DataTableLabels,
23
+ DataTableSelectionBarRenderContext,
24
+ DataTableEmptyStateRenderContext,
25
+ DataTableLoadingStateRenderContext,
26
+ DataTableErrorStateRenderContext,
21
27
  } from "./packages/datatable/index";
package/dist/form.js CHANGED
@@ -55,6 +55,7 @@ var getEmptyValue = (field) => {
55
55
  case "datetime":
56
56
  return void 0;
57
57
  case "display":
58
+ case "slot":
58
59
  case "crmPropertyList":
59
60
  case "crmAssociationPropertyList":
60
61
  return void 0;
@@ -198,7 +199,7 @@ var collectAsyncValidatorPromises = (value, field, allValues, context) => {
198
199
  var runValidators = (value, field, allValues, fieldTypes, options = {}) => {
199
200
  const includeCustomValidators = options.includeCustomValidators !== false;
200
201
  const msg = options.messages || {};
201
- if (field.type === "display" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList" || field.type === "fieldGroup") return null;
202
+ if (field.type === "display" || field.type === "slot" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList" || field.type === "fieldGroup") 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);
@@ -262,6 +263,10 @@ var resolveRequired = (field, allValues) => {
262
263
  if (typeof field.required === "function") return field.required(allValues);
263
264
  return !!field.required;
264
265
  };
266
+ var resolveDisabled = (field, allValues) => {
267
+ if (typeof field.disabled === "function") return field.disabled(allValues);
268
+ return !!field.disabled;
269
+ };
265
270
  var resolveOptions = (field, allValues) => {
266
271
  if (typeof field.options === "function") return field.options(allValues);
267
272
  return field.options || [];
@@ -417,6 +422,8 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
417
422
  // explicit row layout array (overrides columns + columnWidth)
418
423
  sections,
419
424
  // FormBuilderSection[] — accordion field grouping
425
+ groups,
426
+ // Record<string, FormBuilderGroupOptions> — per-group rendering options keyed by group name
420
427
  gap = "sm",
421
428
  // gap between fields
422
429
  showRequiredIndicator = true,
@@ -593,7 +600,8 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
593
600
  "defaultOpen",
594
601
  "info",
595
602
  "renderBefore",
596
- "renderAfter"
603
+ "renderAfter",
604
+ "columns"
597
605
  ]);
598
606
  const SECTION_SUGGESTIONS = {
599
607
  title: "Use label instead",
@@ -625,7 +633,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
625
633
  const resolved = transformInitialValues && initialValues ? transformInitialValues(initialValues) : initialValues;
626
634
  const vals = {};
627
635
  for (const field of fields) {
628
- if (field.type === "display" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList") continue;
636
+ if (field.type === "display" || field.type === "slot" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList") continue;
629
637
  if (field.type === "fieldGroup" && field.items && field.fields) {
630
638
  for (const item of field.items) {
631
639
  const subFields = field.fields(item);
@@ -1157,7 +1165,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
1157
1165
  const rawValues = {};
1158
1166
  for (const key of Object.keys(formValues)) {
1159
1167
  const f = fieldByName.get(key);
1160
- if (f && (f.type === "display" || f.type === "crmPropertyList" || f.type === "crmAssociationPropertyList" || f.type === "fieldGroup")) continue;
1168
+ if (f && (f.type === "display" || f.type === "slot" || f.type === "crmPropertyList" || f.type === "crmAssociationPropertyList" || f.type === "fieldGroup")) continue;
1161
1169
  rawValues[key] = f && f.transformOut ? f.transformOut(formValues[key]) : formValues[key];
1162
1170
  }
1163
1171
  for (const f of fields) {
@@ -1306,9 +1314,9 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
1306
1314
  const hasError = !!fieldError;
1307
1315
  const isRequired = showRequiredIndicator && resolveRequired(field, formValues);
1308
1316
  const isReadOnly = field.readOnly || formReadOnly;
1309
- const isDisabled = disabled || field.disabled || formReadOnly;
1317
+ const isDisabled = disabled || resolveDisabled(field, formValues) || formReadOnly;
1310
1318
  const fieldOnChange = field.debounce ? (v) => handleDebouncedFieldChange(field.name, v) : (v) => handleFieldChange(field.name, v);
1311
- if (field.type === "display") {
1319
+ if (field.type === "display" || field.type === "slot") {
1312
1320
  if (field.render) {
1313
1321
  return field.render({
1314
1322
  values: formValues,
@@ -1350,7 +1358,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
1350
1358
  const sfError = formErrors[sf.name] || null;
1351
1359
  const sfLabel = itemIdx === 0 ? sf.label : void 0;
1352
1360
  const sfReadOnly = sf.readOnly || formReadOnly;
1353
- const sfDisabled = disabled || sf.disabled || formReadOnly;
1361
+ const sfDisabled = disabled || resolveDisabled(sf, formValues) || formReadOnly;
1354
1362
  const sfOnChange = sf.debounce ? (v) => handleDebouncedFieldChange(sf.name, v) : (v) => handleFieldChange(sf.name, v);
1355
1363
  const sfProps = {
1356
1364
  name: sf.name,
@@ -1800,7 +1808,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
1800
1808
  label: sfLabel,
1801
1809
  placeholder: sf.placeholder,
1802
1810
  readOnly: sf.readOnly || isReadOnly,
1803
- disabled: sf.disabled || isDisabled,
1811
+ disabled: resolveDisabled(sf, formValues) || isDisabled,
1804
1812
  error: !!sfError,
1805
1813
  validationMessage: sfError || void 0,
1806
1814
  ...sf.fieldProps || {}
@@ -1880,6 +1888,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
1880
1888
  const getFieldColSpan = (field) => {
1881
1889
  if (field.colSpan != null) return Math.min(field.colSpan, columns);
1882
1890
  if (field.width === "full" && columns > 1) return columns;
1891
+ if (columns > 1 && (field.type === "display" || field.type === "slot" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList")) return columns;
1883
1892
  return 1;
1884
1893
  };
1885
1894
  const getDependents = (parentField) => visibleFields.filter(
@@ -1894,24 +1903,31 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
1894
1903
  const tooltipMessage = typeof rawMessage === "function" ? rawMessage(parentField.label) : rawMessage || "";
1895
1904
  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)));
1896
1905
  };
1897
- const renderGridLayout = (fieldSubset) => {
1906
+ const renderGridLayout = (fieldSubset, effectiveCols) => {
1907
+ const cols = effectiveCols || columns;
1898
1908
  const fieldList = fieldSubset || visibleFields;
1899
1909
  const elements = [];
1900
1910
  let currentRow = [];
1901
1911
  let currentRowSpan = 0;
1902
1912
  const gridColumnWidth = 200;
1913
+ const colSpan = (field) => {
1914
+ if (field.colSpan != null) return Math.min(field.colSpan, cols);
1915
+ if (field.width === "full" && cols > 1) return cols;
1916
+ if (cols > 1 && (field.type === "display" || field.type === "slot" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList")) return cols;
1917
+ return 1;
1918
+ };
1903
1919
  const flushRow = () => {
1904
1920
  if (currentRow.length === 0) return;
1905
- const allUniform = currentRow.every((f) => getFieldColSpan(f) === 1);
1906
- const totalSpan = currentRow.reduce((s, f) => s + getFieldColSpan(f), 0);
1907
- const remainder = columns - totalSpan;
1921
+ const allUniform = currentRow.every((f) => colSpan(f) === 1);
1922
+ const totalSpan = currentRow.reduce((s, f) => s + colSpan(f), 0);
1923
+ const remainder = cols - totalSpan;
1908
1924
  if (allUniform) {
1909
1925
  elements.push(
1910
1926
  /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.AutoGrid, { key: `row-${currentRow[0].name}`, columnWidth: gridColumnWidth, flexible: true, gap }, currentRow.map((f) => /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, { key: f.name }, renderField(f))), remainder > 0 && Array.from({ length: remainder }, (_, i) => /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Box, { key: `spacer-${i}` })))
1911
1927
  );
1912
1928
  } else {
1913
1929
  elements.push(
1914
- /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { key: `row-${currentRow[0].name}`, direction: "row", gap }, currentRow.map((f) => /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Box, { key: f.name, flex: getFieldColSpan(f) }, renderField(f))), remainder > 0 && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Box, { flex: remainder }))
1930
+ /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { key: `row-${currentRow[0].name}`, direction: "row", gap }, currentRow.map((f) => /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Box, { key: f.name, flex: colSpan(f) }, renderField(f))), remainder > 0 && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Box, { flex: remainder }))
1915
1931
  );
1916
1932
  }
1917
1933
  currentRow = [];
@@ -1919,17 +1935,17 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
1919
1935
  };
1920
1936
  for (const field of fieldList) {
1921
1937
  if (isDependent(field)) continue;
1922
- const span = getFieldColSpan(field);
1923
- if (span >= columns) {
1938
+ const span = colSpan(field);
1939
+ if (span >= cols) {
1924
1940
  flushRow();
1925
1941
  elements.push(
1926
1942
  /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, { key: field.name }, renderField(field))
1927
1943
  );
1928
1944
  } else {
1929
- if (currentRowSpan + span > columns) flushRow();
1945
+ if (currentRowSpan + span > cols) flushRow();
1930
1946
  currentRow.push(field);
1931
1947
  currentRowSpan += span;
1932
- if (currentRowSpan >= columns) flushRow();
1948
+ if (currentRowSpan >= cols) flushRow();
1933
1949
  }
1934
1950
  const dependents = getDependents(field);
1935
1951
  if (dependents.length > 0) {
@@ -2061,13 +2077,23 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
2061
2077
  const elements = [];
2062
2078
  for (let i = 0; i < chunks.length; i++) {
2063
2079
  const chunk = chunks[i];
2064
- if (i > 0) {
2080
+ const opts = chunk.group && groups && groups[chunk.group] || {};
2081
+ const showDivider = opts.showDivider !== false;
2082
+ const showLabel = opts.showLabel !== false;
2083
+ if (i > 0 && showDivider) {
2065
2084
  elements.push(/* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Divider, { key: `group-div-${i}` }));
2066
2085
  }
2067
- if (chunk.group) {
2068
- elements.push(
2069
- /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { key: `group-label-${i}`, format: { fontWeight: "demibold" } }, chunk.group)
2070
- );
2086
+ if (chunk.group && showLabel) {
2087
+ if (typeof opts.renderHeader === "function") {
2088
+ const header = opts.renderHeader(chunk.group, chunk.fields, formValues);
2089
+ if (header) elements.push(
2090
+ /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, { key: `group-header-${i}` }, header)
2091
+ );
2092
+ } else {
2093
+ elements.push(
2094
+ /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { key: `group-label-${i}`, format: { fontWeight: "demibold" } }, opts.label || chunk.group)
2095
+ );
2096
+ }
2071
2097
  }
2072
2098
  const chunkElements = renderFn(chunk.fields);
2073
2099
  if (Array.isArray(chunkElements)) {
@@ -2078,10 +2104,11 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
2078
2104
  }
2079
2105
  return elements;
2080
2106
  };
2081
- const renderFieldSubset = (fieldSubset) => {
2107
+ const renderFieldSubset = (fieldSubset, overrides) => {
2108
+ const effectiveColumns = overrides && overrides.columns || columns;
2082
2109
  if (layout && fieldSubset === visibleFields) return renderExplicitLayout();
2083
2110
  if (columnWidth) return renderAutoGridLayout(fieldSubset);
2084
- if (columns > 1) return renderGridLayout(fieldSubset);
2111
+ if (effectiveColumns > 1) return renderGridLayout(fieldSubset, effectiveColumns);
2085
2112
  return renderSingleColumnLayout(fieldSubset);
2086
2113
  };
2087
2114
  const renderSections = () => {
@@ -2096,7 +2123,8 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
2096
2123
  const sectionFields = sec.fields ? visibleFields.filter((f) => sec.fields.includes(f.name)) : [];
2097
2124
  if (sectionFields.length === 0) continue;
2098
2125
  const sectionContext = { values: formValues, errors: formErrors };
2099
- const accordionContent = /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "column", gap }, sec.renderBefore && sec.renderBefore(sectionContext), renderFieldSubset(sectionFields), sec.renderAfter && sec.renderAfter(sectionContext));
2126
+ const sectionOverrides = sec.columns ? { columns: sec.columns } : void 0;
2127
+ 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));
2100
2128
  const accordion = /* @__PURE__ */ import_react.default.createElement(
2101
2129
  import_ui_extensions.Accordion,
2102
2130
  {
package/dist/form.mjs CHANGED
@@ -59,6 +59,7 @@ var getEmptyValue = (field) => {
59
59
  case "datetime":
60
60
  return void 0;
61
61
  case "display":
62
+ case "slot":
62
63
  case "crmPropertyList":
63
64
  case "crmAssociationPropertyList":
64
65
  return void 0;
@@ -202,7 +203,7 @@ var collectAsyncValidatorPromises = (value, field, allValues, context) => {
202
203
  var runValidators = (value, field, allValues, fieldTypes, options = {}) => {
203
204
  const includeCustomValidators = options.includeCustomValidators !== false;
204
205
  const msg = options.messages || {};
205
- if (field.type === "display" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList" || field.type === "fieldGroup") return null;
206
+ if (field.type === "display" || field.type === "slot" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList" || field.type === "fieldGroup") 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);
@@ -266,6 +267,10 @@ var resolveRequired = (field, allValues) => {
266
267
  if (typeof field.required === "function") return field.required(allValues);
267
268
  return !!field.required;
268
269
  };
270
+ var resolveDisabled = (field, allValues) => {
271
+ if (typeof field.disabled === "function") return field.disabled(allValues);
272
+ return !!field.disabled;
273
+ };
269
274
  var resolveOptions = (field, allValues) => {
270
275
  if (typeof field.options === "function") return field.options(allValues);
271
276
  return field.options || [];
@@ -421,6 +426,8 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
421
426
  // explicit row layout array (overrides columns + columnWidth)
422
427
  sections,
423
428
  // FormBuilderSection[] — accordion field grouping
429
+ groups,
430
+ // Record<string, FormBuilderGroupOptions> — per-group rendering options keyed by group name
424
431
  gap = "sm",
425
432
  // gap between fields
426
433
  showRequiredIndicator = true,
@@ -597,7 +604,8 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
597
604
  "defaultOpen",
598
605
  "info",
599
606
  "renderBefore",
600
- "renderAfter"
607
+ "renderAfter",
608
+ "columns"
601
609
  ]);
602
610
  const SECTION_SUGGESTIONS = {
603
611
  title: "Use label instead",
@@ -629,7 +637,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
629
637
  const resolved = transformInitialValues && initialValues ? transformInitialValues(initialValues) : initialValues;
630
638
  const vals = {};
631
639
  for (const field of fields) {
632
- if (field.type === "display" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList") continue;
640
+ if (field.type === "display" || field.type === "slot" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList") continue;
633
641
  if (field.type === "fieldGroup" && field.items && field.fields) {
634
642
  for (const item of field.items) {
635
643
  const subFields = field.fields(item);
@@ -1161,7 +1169,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
1161
1169
  const rawValues = {};
1162
1170
  for (const key of Object.keys(formValues)) {
1163
1171
  const f = fieldByName.get(key);
1164
- if (f && (f.type === "display" || f.type === "crmPropertyList" || f.type === "crmAssociationPropertyList" || f.type === "fieldGroup")) continue;
1172
+ if (f && (f.type === "display" || f.type === "slot" || f.type === "crmPropertyList" || f.type === "crmAssociationPropertyList" || f.type === "fieldGroup")) continue;
1165
1173
  rawValues[key] = f && f.transformOut ? f.transformOut(formValues[key]) : formValues[key];
1166
1174
  }
1167
1175
  for (const f of fields) {
@@ -1310,9 +1318,9 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
1310
1318
  const hasError = !!fieldError;
1311
1319
  const isRequired = showRequiredIndicator && resolveRequired(field, formValues);
1312
1320
  const isReadOnly = field.readOnly || formReadOnly;
1313
- const isDisabled = disabled || field.disabled || formReadOnly;
1321
+ const isDisabled = disabled || resolveDisabled(field, formValues) || formReadOnly;
1314
1322
  const fieldOnChange = field.debounce ? (v) => handleDebouncedFieldChange(field.name, v) : (v) => handleFieldChange(field.name, v);
1315
- if (field.type === "display") {
1323
+ if (field.type === "display" || field.type === "slot") {
1316
1324
  if (field.render) {
1317
1325
  return field.render({
1318
1326
  values: formValues,
@@ -1354,7 +1362,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
1354
1362
  const sfError = formErrors[sf.name] || null;
1355
1363
  const sfLabel = itemIdx === 0 ? sf.label : void 0;
1356
1364
  const sfReadOnly = sf.readOnly || formReadOnly;
1357
- const sfDisabled = disabled || sf.disabled || formReadOnly;
1365
+ const sfDisabled = disabled || resolveDisabled(sf, formValues) || formReadOnly;
1358
1366
  const sfOnChange = sf.debounce ? (v) => handleDebouncedFieldChange(sf.name, v) : (v) => handleFieldChange(sf.name, v);
1359
1367
  const sfProps = {
1360
1368
  name: sf.name,
@@ -1804,7 +1812,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
1804
1812
  label: sfLabel,
1805
1813
  placeholder: sf.placeholder,
1806
1814
  readOnly: sf.readOnly || isReadOnly,
1807
- disabled: sf.disabled || isDisabled,
1815
+ disabled: resolveDisabled(sf, formValues) || isDisabled,
1808
1816
  error: !!sfError,
1809
1817
  validationMessage: sfError || void 0,
1810
1818
  ...sf.fieldProps || {}
@@ -1884,6 +1892,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
1884
1892
  const getFieldColSpan = (field) => {
1885
1893
  if (field.colSpan != null) return Math.min(field.colSpan, columns);
1886
1894
  if (field.width === "full" && columns > 1) return columns;
1895
+ if (columns > 1 && (field.type === "display" || field.type === "slot" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList")) return columns;
1887
1896
  return 1;
1888
1897
  };
1889
1898
  const getDependents = (parentField) => visibleFields.filter(
@@ -1898,24 +1907,31 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
1898
1907
  const tooltipMessage = typeof rawMessage === "function" ? rawMessage(parentField.label) : rawMessage || "";
1899
1908
  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)));
1900
1909
  };
1901
- const renderGridLayout = (fieldSubset) => {
1910
+ const renderGridLayout = (fieldSubset, effectiveCols) => {
1911
+ const cols = effectiveCols || columns;
1902
1912
  const fieldList = fieldSubset || visibleFields;
1903
1913
  const elements = [];
1904
1914
  let currentRow = [];
1905
1915
  let currentRowSpan = 0;
1906
1916
  const gridColumnWidth = 200;
1917
+ const colSpan = (field) => {
1918
+ if (field.colSpan != null) return Math.min(field.colSpan, cols);
1919
+ if (field.width === "full" && cols > 1) return cols;
1920
+ if (cols > 1 && (field.type === "display" || field.type === "slot" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList")) return cols;
1921
+ return 1;
1922
+ };
1907
1923
  const flushRow = () => {
1908
1924
  if (currentRow.length === 0) return;
1909
- const allUniform = currentRow.every((f) => getFieldColSpan(f) === 1);
1910
- const totalSpan = currentRow.reduce((s, f) => s + getFieldColSpan(f), 0);
1911
- const remainder = columns - totalSpan;
1925
+ const allUniform = currentRow.every((f) => colSpan(f) === 1);
1926
+ const totalSpan = currentRow.reduce((s, f) => s + colSpan(f), 0);
1927
+ const remainder = cols - totalSpan;
1912
1928
  if (allUniform) {
1913
1929
  elements.push(
1914
1930
  /* @__PURE__ */ React.createElement(AutoGrid, { key: `row-${currentRow[0].name}`, columnWidth: gridColumnWidth, flexible: true, gap }, currentRow.map((f) => /* @__PURE__ */ React.createElement(React.Fragment, { key: f.name }, renderField(f))), remainder > 0 && Array.from({ length: remainder }, (_, i) => /* @__PURE__ */ React.createElement(Box, { key: `spacer-${i}` })))
1915
1931
  );
1916
1932
  } else {
1917
1933
  elements.push(
1918
- /* @__PURE__ */ React.createElement(Flex, { key: `row-${currentRow[0].name}`, direction: "row", gap }, currentRow.map((f) => /* @__PURE__ */ React.createElement(Box, { key: f.name, flex: getFieldColSpan(f) }, renderField(f))), remainder > 0 && /* @__PURE__ */ React.createElement(Box, { flex: remainder }))
1934
+ /* @__PURE__ */ React.createElement(Flex, { key: `row-${currentRow[0].name}`, direction: "row", gap }, currentRow.map((f) => /* @__PURE__ */ React.createElement(Box, { key: f.name, flex: colSpan(f) }, renderField(f))), remainder > 0 && /* @__PURE__ */ React.createElement(Box, { flex: remainder }))
1919
1935
  );
1920
1936
  }
1921
1937
  currentRow = [];
@@ -1923,17 +1939,17 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
1923
1939
  };
1924
1940
  for (const field of fieldList) {
1925
1941
  if (isDependent(field)) continue;
1926
- const span = getFieldColSpan(field);
1927
- if (span >= columns) {
1942
+ const span = colSpan(field);
1943
+ if (span >= cols) {
1928
1944
  flushRow();
1929
1945
  elements.push(
1930
1946
  /* @__PURE__ */ React.createElement(React.Fragment, { key: field.name }, renderField(field))
1931
1947
  );
1932
1948
  } else {
1933
- if (currentRowSpan + span > columns) flushRow();
1949
+ if (currentRowSpan + span > cols) flushRow();
1934
1950
  currentRow.push(field);
1935
1951
  currentRowSpan += span;
1936
- if (currentRowSpan >= columns) flushRow();
1952
+ if (currentRowSpan >= cols) flushRow();
1937
1953
  }
1938
1954
  const dependents = getDependents(field);
1939
1955
  if (dependents.length > 0) {
@@ -2065,13 +2081,23 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
2065
2081
  const elements = [];
2066
2082
  for (let i = 0; i < chunks.length; i++) {
2067
2083
  const chunk = chunks[i];
2068
- if (i > 0) {
2084
+ const opts = chunk.group && groups && groups[chunk.group] || {};
2085
+ const showDivider = opts.showDivider !== false;
2086
+ const showLabel = opts.showLabel !== false;
2087
+ if (i > 0 && showDivider) {
2069
2088
  elements.push(/* @__PURE__ */ React.createElement(Divider, { key: `group-div-${i}` }));
2070
2089
  }
2071
- if (chunk.group) {
2072
- elements.push(
2073
- /* @__PURE__ */ React.createElement(Text, { key: `group-label-${i}`, format: { fontWeight: "demibold" } }, chunk.group)
2074
- );
2090
+ if (chunk.group && showLabel) {
2091
+ if (typeof opts.renderHeader === "function") {
2092
+ const header = opts.renderHeader(chunk.group, chunk.fields, formValues);
2093
+ if (header) elements.push(
2094
+ /* @__PURE__ */ React.createElement(React.Fragment, { key: `group-header-${i}` }, header)
2095
+ );
2096
+ } else {
2097
+ elements.push(
2098
+ /* @__PURE__ */ React.createElement(Text, { key: `group-label-${i}`, format: { fontWeight: "demibold" } }, opts.label || chunk.group)
2099
+ );
2100
+ }
2075
2101
  }
2076
2102
  const chunkElements = renderFn(chunk.fields);
2077
2103
  if (Array.isArray(chunkElements)) {
@@ -2082,10 +2108,11 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
2082
2108
  }
2083
2109
  return elements;
2084
2110
  };
2085
- const renderFieldSubset = (fieldSubset) => {
2111
+ const renderFieldSubset = (fieldSubset, overrides) => {
2112
+ const effectiveColumns = overrides && overrides.columns || columns;
2086
2113
  if (layout && fieldSubset === visibleFields) return renderExplicitLayout();
2087
2114
  if (columnWidth) return renderAutoGridLayout(fieldSubset);
2088
- if (columns > 1) return renderGridLayout(fieldSubset);
2115
+ if (effectiveColumns > 1) return renderGridLayout(fieldSubset, effectiveColumns);
2089
2116
  return renderSingleColumnLayout(fieldSubset);
2090
2117
  };
2091
2118
  const renderSections = () => {
@@ -2100,7 +2127,8 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
2100
2127
  const sectionFields = sec.fields ? visibleFields.filter((f) => sec.fields.includes(f.name)) : [];
2101
2128
  if (sectionFields.length === 0) continue;
2102
2129
  const sectionContext = { values: formValues, errors: formErrors };
2103
- const accordionContent = /* @__PURE__ */ React.createElement(Flex, { direction: "column", gap }, sec.renderBefore && sec.renderBefore(sectionContext), renderFieldSubset(sectionFields), sec.renderAfter && sec.renderAfter(sectionContext));
2130
+ const sectionOverrides = sec.columns ? { columns: sec.columns } : void 0;
2131
+ const accordionContent = /* @__PURE__ */ React.createElement(Flex, { direction: "column", gap }, sec.renderBefore && sec.renderBefore(sectionContext), renderFieldSubset(sectionFields, sectionOverrides), sec.renderAfter && sec.renderAfter(sectionContext));
2104
2132
  const accordion = /* @__PURE__ */ React.createElement(
2105
2133
  Accordion,
2106
2134
  {
package/dist/index.js CHANGED
@@ -1126,6 +1126,7 @@ var getEmptyValue = (field) => {
1126
1126
  case "datetime":
1127
1127
  return void 0;
1128
1128
  case "display":
1129
+ case "slot":
1129
1130
  case "crmPropertyList":
1130
1131
  case "crmAssociationPropertyList":
1131
1132
  return void 0;
@@ -1269,7 +1270,7 @@ var collectAsyncValidatorPromises = (value, field, allValues, context) => {
1269
1270
  var runValidators = (value, field, allValues, fieldTypes, options = {}) => {
1270
1271
  const includeCustomValidators = options.includeCustomValidators !== false;
1271
1272
  const msg = options.messages || {};
1272
- if (field.type === "display" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList" || field.type === "fieldGroup") return null;
1273
+ if (field.type === "display" || field.type === "slot" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList" || field.type === "fieldGroup") return null;
1273
1274
  const isRequired = resolveRequired(field, allValues);
1274
1275
  const plugin = fieldTypes && fieldTypes[field.type];
1275
1276
  const empty = plugin && plugin.isEmpty ? plugin.isEmpty(value) : isValueEmpty(value, field);
@@ -1333,6 +1334,10 @@ var resolveRequired = (field, allValues) => {
1333
1334
  if (typeof field.required === "function") return field.required(allValues);
1334
1335
  return !!field.required;
1335
1336
  };
1337
+ var resolveDisabled = (field, allValues) => {
1338
+ if (typeof field.disabled === "function") return field.disabled(allValues);
1339
+ return !!field.disabled;
1340
+ };
1336
1341
  var resolveOptions = (field, allValues) => {
1337
1342
  if (typeof field.options === "function") return field.options(allValues);
1338
1343
  return field.options || [];
@@ -1488,6 +1493,8 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
1488
1493
  // explicit row layout array (overrides columns + columnWidth)
1489
1494
  sections,
1490
1495
  // FormBuilderSection[] — accordion field grouping
1496
+ groups,
1497
+ // Record<string, FormBuilderGroupOptions> — per-group rendering options keyed by group name
1491
1498
  gap = "sm",
1492
1499
  // gap between fields
1493
1500
  showRequiredIndicator = true,
@@ -1664,7 +1671,8 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
1664
1671
  "defaultOpen",
1665
1672
  "info",
1666
1673
  "renderBefore",
1667
- "renderAfter"
1674
+ "renderAfter",
1675
+ "columns"
1668
1676
  ]);
1669
1677
  const SECTION_SUGGESTIONS = {
1670
1678
  title: "Use label instead",
@@ -1696,7 +1704,7 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
1696
1704
  const resolved = transformInitialValues && initialValues ? transformInitialValues(initialValues) : initialValues;
1697
1705
  const vals = {};
1698
1706
  for (const field of fields) {
1699
- if (field.type === "display" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList") continue;
1707
+ if (field.type === "display" || field.type === "slot" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList") continue;
1700
1708
  if (field.type === "fieldGroup" && field.items && field.fields) {
1701
1709
  for (const item of field.items) {
1702
1710
  const subFields = field.fields(item);
@@ -2228,7 +2236,7 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
2228
2236
  const rawValues = {};
2229
2237
  for (const key of Object.keys(formValues)) {
2230
2238
  const f = fieldByName.get(key);
2231
- if (f && (f.type === "display" || f.type === "crmPropertyList" || f.type === "crmAssociationPropertyList" || f.type === "fieldGroup")) continue;
2239
+ if (f && (f.type === "display" || f.type === "slot" || f.type === "crmPropertyList" || f.type === "crmAssociationPropertyList" || f.type === "fieldGroup")) continue;
2232
2240
  rawValues[key] = f && f.transformOut ? f.transformOut(formValues[key]) : formValues[key];
2233
2241
  }
2234
2242
  for (const f of fields) {
@@ -2377,9 +2385,9 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
2377
2385
  const hasError = !!fieldError;
2378
2386
  const isRequired = showRequiredIndicator && resolveRequired(field, formValues);
2379
2387
  const isReadOnly = field.readOnly || formReadOnly;
2380
- const isDisabled = disabled || field.disabled || formReadOnly;
2388
+ const isDisabled = disabled || resolveDisabled(field, formValues) || formReadOnly;
2381
2389
  const fieldOnChange = field.debounce ? (v) => handleDebouncedFieldChange(field.name, v) : (v) => handleFieldChange(field.name, v);
2382
- if (field.type === "display") {
2390
+ if (field.type === "display" || field.type === "slot") {
2383
2391
  if (field.render) {
2384
2392
  return field.render({
2385
2393
  values: formValues,
@@ -2421,7 +2429,7 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
2421
2429
  const sfError = formErrors[sf.name] || null;
2422
2430
  const sfLabel = itemIdx === 0 ? sf.label : void 0;
2423
2431
  const sfReadOnly = sf.readOnly || formReadOnly;
2424
- const sfDisabled = disabled || sf.disabled || formReadOnly;
2432
+ const sfDisabled = disabled || resolveDisabled(sf, formValues) || formReadOnly;
2425
2433
  const sfOnChange = sf.debounce ? (v) => handleDebouncedFieldChange(sf.name, v) : (v) => handleFieldChange(sf.name, v);
2426
2434
  const sfProps = {
2427
2435
  name: sf.name,
@@ -2871,7 +2879,7 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
2871
2879
  label: sfLabel,
2872
2880
  placeholder: sf.placeholder,
2873
2881
  readOnly: sf.readOnly || isReadOnly,
2874
- disabled: sf.disabled || isDisabled,
2882
+ disabled: resolveDisabled(sf, formValues) || isDisabled,
2875
2883
  error: !!sfError,
2876
2884
  validationMessage: sfError || void 0,
2877
2885
  ...sf.fieldProps || {}
@@ -2951,6 +2959,7 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
2951
2959
  const getFieldColSpan = (field) => {
2952
2960
  if (field.colSpan != null) return Math.min(field.colSpan, columns);
2953
2961
  if (field.width === "full" && columns > 1) return columns;
2962
+ if (columns > 1 && (field.type === "display" || field.type === "slot" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList")) return columns;
2954
2963
  return 1;
2955
2964
  };
2956
2965
  const getDependents = (parentField) => visibleFields.filter(
@@ -2965,24 +2974,31 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
2965
2974
  const tooltipMessage = typeof rawMessage === "function" ? rawMessage(parentField.label) : rawMessage || "";
2966
2975
  return /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Tile, { key: `dep-${parentField.name}`, compact: true }, /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Flex, { direction: "column", gap }, /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Flex, { direction: "row", align: "center", gap: "xs" }, /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Text, { format: { fontWeight: "demibold" } }, groupLabel, " ", tooltipMessage && /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Link, { inline: true, variant: "dark", overlay: /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Tooltip, null, tooltipMessage) }, /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Icon, { name: "info" })))), renderFieldSubset(dependents)));
2967
2976
  };
2968
- const renderGridLayout = (fieldSubset) => {
2977
+ const renderGridLayout = (fieldSubset, effectiveCols) => {
2978
+ const cols = effectiveCols || columns;
2969
2979
  const fieldList = fieldSubset || visibleFields;
2970
2980
  const elements = [];
2971
2981
  let currentRow = [];
2972
2982
  let currentRowSpan = 0;
2973
2983
  const gridColumnWidth = 200;
2984
+ const colSpan = (field) => {
2985
+ if (field.colSpan != null) return Math.min(field.colSpan, cols);
2986
+ if (field.width === "full" && cols > 1) return cols;
2987
+ if (cols > 1 && (field.type === "display" || field.type === "slot" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList")) return cols;
2988
+ return 1;
2989
+ };
2974
2990
  const flushRow = () => {
2975
2991
  if (currentRow.length === 0) return;
2976
- const allUniform = currentRow.every((f) => getFieldColSpan(f) === 1);
2977
- const totalSpan = currentRow.reduce((s, f) => s + getFieldColSpan(f), 0);
2978
- const remainder = columns - totalSpan;
2992
+ const allUniform = currentRow.every((f) => colSpan(f) === 1);
2993
+ const totalSpan = currentRow.reduce((s, f) => s + colSpan(f), 0);
2994
+ const remainder = cols - totalSpan;
2979
2995
  if (allUniform) {
2980
2996
  elements.push(
2981
2997
  /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.AutoGrid, { key: `row-${currentRow[0].name}`, columnWidth: gridColumnWidth, flexible: true, gap }, currentRow.map((f) => /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, { key: f.name }, renderField(f))), remainder > 0 && Array.from({ length: remainder }, (_, i) => /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Box, { key: `spacer-${i}` })))
2982
2998
  );
2983
2999
  } else {
2984
3000
  elements.push(
2985
- /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Flex, { key: `row-${currentRow[0].name}`, direction: "row", gap }, currentRow.map((f) => /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Box, { key: f.name, flex: getFieldColSpan(f) }, renderField(f))), remainder > 0 && /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Box, { flex: remainder }))
3001
+ /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Flex, { key: `row-${currentRow[0].name}`, direction: "row", gap }, currentRow.map((f) => /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Box, { key: f.name, flex: colSpan(f) }, renderField(f))), remainder > 0 && /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Box, { flex: remainder }))
2986
3002
  );
2987
3003
  }
2988
3004
  currentRow = [];
@@ -2990,17 +3006,17 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
2990
3006
  };
2991
3007
  for (const field of fieldList) {
2992
3008
  if (isDependent(field)) continue;
2993
- const span = getFieldColSpan(field);
2994
- if (span >= columns) {
3009
+ const span = colSpan(field);
3010
+ if (span >= cols) {
2995
3011
  flushRow();
2996
3012
  elements.push(
2997
3013
  /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, { key: field.name }, renderField(field))
2998
3014
  );
2999
3015
  } else {
3000
- if (currentRowSpan + span > columns) flushRow();
3016
+ if (currentRowSpan + span > cols) flushRow();
3001
3017
  currentRow.push(field);
3002
3018
  currentRowSpan += span;
3003
- if (currentRowSpan >= columns) flushRow();
3019
+ if (currentRowSpan >= cols) flushRow();
3004
3020
  }
3005
3021
  const dependents = getDependents(field);
3006
3022
  if (dependents.length > 0) {
@@ -3132,13 +3148,23 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
3132
3148
  const elements = [];
3133
3149
  for (let i = 0; i < chunks.length; i++) {
3134
3150
  const chunk = chunks[i];
3135
- if (i > 0) {
3151
+ const opts = chunk.group && groups && groups[chunk.group] || {};
3152
+ const showDivider = opts.showDivider !== false;
3153
+ const showLabel = opts.showLabel !== false;
3154
+ if (i > 0 && showDivider) {
3136
3155
  elements.push(/* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Divider, { key: `group-div-${i}` }));
3137
3156
  }
3138
- if (chunk.group) {
3139
- elements.push(
3140
- /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Text, { key: `group-label-${i}`, format: { fontWeight: "demibold" } }, chunk.group)
3141
- );
3157
+ if (chunk.group && showLabel) {
3158
+ if (typeof opts.renderHeader === "function") {
3159
+ const header = opts.renderHeader(chunk.group, chunk.fields, formValues);
3160
+ if (header) elements.push(
3161
+ /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, { key: `group-header-${i}` }, header)
3162
+ );
3163
+ } else {
3164
+ elements.push(
3165
+ /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Text, { key: `group-label-${i}`, format: { fontWeight: "demibold" } }, opts.label || chunk.group)
3166
+ );
3167
+ }
3142
3168
  }
3143
3169
  const chunkElements = renderFn(chunk.fields);
3144
3170
  if (Array.isArray(chunkElements)) {
@@ -3149,10 +3175,11 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
3149
3175
  }
3150
3176
  return elements;
3151
3177
  };
3152
- const renderFieldSubset = (fieldSubset) => {
3178
+ const renderFieldSubset = (fieldSubset, overrides) => {
3179
+ const effectiveColumns = overrides && overrides.columns || columns;
3153
3180
  if (layout && fieldSubset === visibleFields) return renderExplicitLayout();
3154
3181
  if (columnWidth) return renderAutoGridLayout(fieldSubset);
3155
- if (columns > 1) return renderGridLayout(fieldSubset);
3182
+ if (effectiveColumns > 1) return renderGridLayout(fieldSubset, effectiveColumns);
3156
3183
  return renderSingleColumnLayout(fieldSubset);
3157
3184
  };
3158
3185
  const renderSections = () => {
@@ -3167,7 +3194,8 @@ var FormBuilder = (0, import_react2.forwardRef)(function FormBuilder2(props, ref
3167
3194
  const sectionFields = sec.fields ? visibleFields.filter((f) => sec.fields.includes(f.name)) : [];
3168
3195
  if (sectionFields.length === 0) continue;
3169
3196
  const sectionContext = { values: formValues, errors: formErrors };
3170
- const accordionContent = /* @__PURE__ */ import_react2.default.createElement(import_ui_extensions2.Flex, { direction: "column", gap }, sec.renderBefore && sec.renderBefore(sectionContext), renderFieldSubset(sectionFields), sec.renderAfter && sec.renderAfter(sectionContext));
3197
+ const sectionOverrides = sec.columns ? { columns: sec.columns } : void 0;
3198
+ 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));
3171
3199
  const accordion = /* @__PURE__ */ import_react2.default.createElement(
3172
3200
  import_ui_extensions2.Accordion,
3173
3201
  {
package/dist/index.mjs CHANGED
@@ -1159,6 +1159,7 @@ var getEmptyValue = (field) => {
1159
1159
  case "datetime":
1160
1160
  return void 0;
1161
1161
  case "display":
1162
+ case "slot":
1162
1163
  case "crmPropertyList":
1163
1164
  case "crmAssociationPropertyList":
1164
1165
  return void 0;
@@ -1302,7 +1303,7 @@ var collectAsyncValidatorPromises = (value, field, allValues, context) => {
1302
1303
  var runValidators = (value, field, allValues, fieldTypes, options = {}) => {
1303
1304
  const includeCustomValidators = options.includeCustomValidators !== false;
1304
1305
  const msg = options.messages || {};
1305
- if (field.type === "display" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList" || field.type === "fieldGroup") return null;
1306
+ if (field.type === "display" || field.type === "slot" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList" || field.type === "fieldGroup") return null;
1306
1307
  const isRequired = resolveRequired(field, allValues);
1307
1308
  const plugin = fieldTypes && fieldTypes[field.type];
1308
1309
  const empty = plugin && plugin.isEmpty ? plugin.isEmpty(value) : isValueEmpty(value, field);
@@ -1366,6 +1367,10 @@ var resolveRequired = (field, allValues) => {
1366
1367
  if (typeof field.required === "function") return field.required(allValues);
1367
1368
  return !!field.required;
1368
1369
  };
1370
+ var resolveDisabled = (field, allValues) => {
1371
+ if (typeof field.disabled === "function") return field.disabled(allValues);
1372
+ return !!field.disabled;
1373
+ };
1369
1374
  var resolveOptions = (field, allValues) => {
1370
1375
  if (typeof field.options === "function") return field.options(allValues);
1371
1376
  return field.options || [];
@@ -1521,6 +1526,8 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
1521
1526
  // explicit row layout array (overrides columns + columnWidth)
1522
1527
  sections,
1523
1528
  // FormBuilderSection[] — accordion field grouping
1529
+ groups,
1530
+ // Record<string, FormBuilderGroupOptions> — per-group rendering options keyed by group name
1524
1531
  gap = "sm",
1525
1532
  // gap between fields
1526
1533
  showRequiredIndicator = true,
@@ -1697,7 +1704,8 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
1697
1704
  "defaultOpen",
1698
1705
  "info",
1699
1706
  "renderBefore",
1700
- "renderAfter"
1707
+ "renderAfter",
1708
+ "columns"
1701
1709
  ]);
1702
1710
  const SECTION_SUGGESTIONS = {
1703
1711
  title: "Use label instead",
@@ -1729,7 +1737,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
1729
1737
  const resolved = transformInitialValues && initialValues ? transformInitialValues(initialValues) : initialValues;
1730
1738
  const vals = {};
1731
1739
  for (const field of fields) {
1732
- if (field.type === "display" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList") continue;
1740
+ if (field.type === "display" || field.type === "slot" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList") continue;
1733
1741
  if (field.type === "fieldGroup" && field.items && field.fields) {
1734
1742
  for (const item of field.items) {
1735
1743
  const subFields = field.fields(item);
@@ -2261,7 +2269,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
2261
2269
  const rawValues = {};
2262
2270
  for (const key of Object.keys(formValues)) {
2263
2271
  const f = fieldByName.get(key);
2264
- if (f && (f.type === "display" || f.type === "crmPropertyList" || f.type === "crmAssociationPropertyList" || f.type === "fieldGroup")) continue;
2272
+ if (f && (f.type === "display" || f.type === "slot" || f.type === "crmPropertyList" || f.type === "crmAssociationPropertyList" || f.type === "fieldGroup")) continue;
2265
2273
  rawValues[key] = f && f.transformOut ? f.transformOut(formValues[key]) : formValues[key];
2266
2274
  }
2267
2275
  for (const f of fields) {
@@ -2410,9 +2418,9 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
2410
2418
  const hasError = !!fieldError;
2411
2419
  const isRequired = showRequiredIndicator && resolveRequired(field, formValues);
2412
2420
  const isReadOnly = field.readOnly || formReadOnly;
2413
- const isDisabled = disabled || field.disabled || formReadOnly;
2421
+ const isDisabled = disabled || resolveDisabled(field, formValues) || formReadOnly;
2414
2422
  const fieldOnChange = field.debounce ? (v) => handleDebouncedFieldChange(field.name, v) : (v) => handleFieldChange(field.name, v);
2415
- if (field.type === "display") {
2423
+ if (field.type === "display" || field.type === "slot") {
2416
2424
  if (field.render) {
2417
2425
  return field.render({
2418
2426
  values: formValues,
@@ -2454,7 +2462,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
2454
2462
  const sfError = formErrors[sf.name] || null;
2455
2463
  const sfLabel = itemIdx === 0 ? sf.label : void 0;
2456
2464
  const sfReadOnly = sf.readOnly || formReadOnly;
2457
- const sfDisabled = disabled || sf.disabled || formReadOnly;
2465
+ const sfDisabled = disabled || resolveDisabled(sf, formValues) || formReadOnly;
2458
2466
  const sfOnChange = sf.debounce ? (v) => handleDebouncedFieldChange(sf.name, v) : (v) => handleFieldChange(sf.name, v);
2459
2467
  const sfProps = {
2460
2468
  name: sf.name,
@@ -2904,7 +2912,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
2904
2912
  label: sfLabel,
2905
2913
  placeholder: sf.placeholder,
2906
2914
  readOnly: sf.readOnly || isReadOnly,
2907
- disabled: sf.disabled || isDisabled,
2915
+ disabled: resolveDisabled(sf, formValues) || isDisabled,
2908
2916
  error: !!sfError,
2909
2917
  validationMessage: sfError || void 0,
2910
2918
  ...sf.fieldProps || {}
@@ -2984,6 +2992,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
2984
2992
  const getFieldColSpan = (field) => {
2985
2993
  if (field.colSpan != null) return Math.min(field.colSpan, columns);
2986
2994
  if (field.width === "full" && columns > 1) return columns;
2995
+ if (columns > 1 && (field.type === "display" || field.type === "slot" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList")) return columns;
2987
2996
  return 1;
2988
2997
  };
2989
2998
  const getDependents = (parentField) => visibleFields.filter(
@@ -2998,24 +3007,31 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
2998
3007
  const tooltipMessage = typeof rawMessage === "function" ? rawMessage(parentField.label) : rawMessage || "";
2999
3008
  return /* @__PURE__ */ React2.createElement(Tile, { key: `dep-${parentField.name}`, compact: true }, /* @__PURE__ */ React2.createElement(Flex2, { direction: "column", gap }, /* @__PURE__ */ React2.createElement(Flex2, { direction: "row", align: "center", gap: "xs" }, /* @__PURE__ */ React2.createElement(Text2, { format: { fontWeight: "demibold" } }, groupLabel, " ", tooltipMessage && /* @__PURE__ */ React2.createElement(Link2, { inline: true, variant: "dark", overlay: /* @__PURE__ */ React2.createElement(Tooltip, null, tooltipMessage) }, /* @__PURE__ */ React2.createElement(Icon2, { name: "info" })))), renderFieldSubset(dependents)));
3000
3009
  };
3001
- const renderGridLayout = (fieldSubset) => {
3010
+ const renderGridLayout = (fieldSubset, effectiveCols) => {
3011
+ const cols = effectiveCols || columns;
3002
3012
  const fieldList = fieldSubset || visibleFields;
3003
3013
  const elements = [];
3004
3014
  let currentRow = [];
3005
3015
  let currentRowSpan = 0;
3006
3016
  const gridColumnWidth = 200;
3017
+ const colSpan = (field) => {
3018
+ if (field.colSpan != null) return Math.min(field.colSpan, cols);
3019
+ if (field.width === "full" && cols > 1) return cols;
3020
+ if (cols > 1 && (field.type === "display" || field.type === "slot" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList")) return cols;
3021
+ return 1;
3022
+ };
3007
3023
  const flushRow = () => {
3008
3024
  if (currentRow.length === 0) return;
3009
- const allUniform = currentRow.every((f) => getFieldColSpan(f) === 1);
3010
- const totalSpan = currentRow.reduce((s, f) => s + getFieldColSpan(f), 0);
3011
- const remainder = columns - totalSpan;
3025
+ const allUniform = currentRow.every((f) => colSpan(f) === 1);
3026
+ const totalSpan = currentRow.reduce((s, f) => s + colSpan(f), 0);
3027
+ const remainder = cols - totalSpan;
3012
3028
  if (allUniform) {
3013
3029
  elements.push(
3014
3030
  /* @__PURE__ */ React2.createElement(AutoGrid, { key: `row-${currentRow[0].name}`, columnWidth: gridColumnWidth, flexible: true, gap }, currentRow.map((f) => /* @__PURE__ */ React2.createElement(React2.Fragment, { key: f.name }, renderField(f))), remainder > 0 && Array.from({ length: remainder }, (_, i) => /* @__PURE__ */ React2.createElement(Box2, { key: `spacer-${i}` })))
3015
3031
  );
3016
3032
  } else {
3017
3033
  elements.push(
3018
- /* @__PURE__ */ React2.createElement(Flex2, { key: `row-${currentRow[0].name}`, direction: "row", gap }, currentRow.map((f) => /* @__PURE__ */ React2.createElement(Box2, { key: f.name, flex: getFieldColSpan(f) }, renderField(f))), remainder > 0 && /* @__PURE__ */ React2.createElement(Box2, { flex: remainder }))
3034
+ /* @__PURE__ */ React2.createElement(Flex2, { key: `row-${currentRow[0].name}`, direction: "row", gap }, currentRow.map((f) => /* @__PURE__ */ React2.createElement(Box2, { key: f.name, flex: colSpan(f) }, renderField(f))), remainder > 0 && /* @__PURE__ */ React2.createElement(Box2, { flex: remainder }))
3019
3035
  );
3020
3036
  }
3021
3037
  currentRow = [];
@@ -3023,17 +3039,17 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
3023
3039
  };
3024
3040
  for (const field of fieldList) {
3025
3041
  if (isDependent(field)) continue;
3026
- const span = getFieldColSpan(field);
3027
- if (span >= columns) {
3042
+ const span = colSpan(field);
3043
+ if (span >= cols) {
3028
3044
  flushRow();
3029
3045
  elements.push(
3030
3046
  /* @__PURE__ */ React2.createElement(React2.Fragment, { key: field.name }, renderField(field))
3031
3047
  );
3032
3048
  } else {
3033
- if (currentRowSpan + span > columns) flushRow();
3049
+ if (currentRowSpan + span > cols) flushRow();
3034
3050
  currentRow.push(field);
3035
3051
  currentRowSpan += span;
3036
- if (currentRowSpan >= columns) flushRow();
3052
+ if (currentRowSpan >= cols) flushRow();
3037
3053
  }
3038
3054
  const dependents = getDependents(field);
3039
3055
  if (dependents.length > 0) {
@@ -3165,13 +3181,23 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
3165
3181
  const elements = [];
3166
3182
  for (let i = 0; i < chunks.length; i++) {
3167
3183
  const chunk = chunks[i];
3168
- if (i > 0) {
3184
+ const opts = chunk.group && groups && groups[chunk.group] || {};
3185
+ const showDivider = opts.showDivider !== false;
3186
+ const showLabel = opts.showLabel !== false;
3187
+ if (i > 0 && showDivider) {
3169
3188
  elements.push(/* @__PURE__ */ React2.createElement(Divider, { key: `group-div-${i}` }));
3170
3189
  }
3171
- if (chunk.group) {
3172
- elements.push(
3173
- /* @__PURE__ */ React2.createElement(Text2, { key: `group-label-${i}`, format: { fontWeight: "demibold" } }, chunk.group)
3174
- );
3190
+ if (chunk.group && showLabel) {
3191
+ if (typeof opts.renderHeader === "function") {
3192
+ const header = opts.renderHeader(chunk.group, chunk.fields, formValues);
3193
+ if (header) elements.push(
3194
+ /* @__PURE__ */ React2.createElement(React2.Fragment, { key: `group-header-${i}` }, header)
3195
+ );
3196
+ } else {
3197
+ elements.push(
3198
+ /* @__PURE__ */ React2.createElement(Text2, { key: `group-label-${i}`, format: { fontWeight: "demibold" } }, opts.label || chunk.group)
3199
+ );
3200
+ }
3175
3201
  }
3176
3202
  const chunkElements = renderFn(chunk.fields);
3177
3203
  if (Array.isArray(chunkElements)) {
@@ -3182,10 +3208,11 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
3182
3208
  }
3183
3209
  return elements;
3184
3210
  };
3185
- const renderFieldSubset = (fieldSubset) => {
3211
+ const renderFieldSubset = (fieldSubset, overrides) => {
3212
+ const effectiveColumns = overrides && overrides.columns || columns;
3186
3213
  if (layout && fieldSubset === visibleFields) return renderExplicitLayout();
3187
3214
  if (columnWidth) return renderAutoGridLayout(fieldSubset);
3188
- if (columns > 1) return renderGridLayout(fieldSubset);
3215
+ if (effectiveColumns > 1) return renderGridLayout(fieldSubset, effectiveColumns);
3189
3216
  return renderSingleColumnLayout(fieldSubset);
3190
3217
  };
3191
3218
  const renderSections = () => {
@@ -3200,7 +3227,8 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
3200
3227
  const sectionFields = sec.fields ? visibleFields.filter((f) => sec.fields.includes(f.name)) : [];
3201
3228
  if (sectionFields.length === 0) continue;
3202
3229
  const sectionContext = { values: formValues, errors: formErrors };
3203
- const accordionContent = /* @__PURE__ */ React2.createElement(Flex2, { direction: "column", gap }, sec.renderBefore && sec.renderBefore(sectionContext), renderFieldSubset(sectionFields), sec.renderAfter && sec.renderAfter(sectionContext));
3230
+ const sectionOverrides = sec.columns ? { columns: sec.columns } : void 0;
3231
+ const accordionContent = /* @__PURE__ */ React2.createElement(Flex2, { direction: "column", gap }, sec.renderBefore && sec.renderBefore(sectionContext), renderFieldSubset(sectionFields, sectionOverrides), sec.renderAfter && sec.renderAfter(sectionContext));
3204
3232
  const accordion = /* @__PURE__ */ React2.createElement(
3205
3233
  Accordion,
3206
3234
  {
package/form.d.ts CHANGED
@@ -8,6 +8,14 @@ export {
8
8
  FormBuilderDateValue,
9
9
  FormBuilderTimeValue,
10
10
  FormBuilderDateTimeValue,
11
+ FormBuilderValidationContext,
12
+ FormBuilderValidatorResult,
13
+ FormBuilderValidator,
14
+ FormBuilderDependsOnConfig,
15
+ FormBuilderRepeaterProps,
16
+ FormBuilderLabels,
17
+ FormBuilderAlertConfig,
18
+ FormBuilderButtonsRenderContext,
11
19
  FormBuilderLayout,
12
20
  FormBuilderLayoutEntry,
13
21
  FormBuilderSection,
package/index.d.ts CHANGED
@@ -3,6 +3,7 @@ export type {
3
3
  DataTableProps,
4
4
  DataTableColumn,
5
5
  DataTableFilterConfig,
6
+ DataTableFilterType,
6
7
  DataTableGroupBy,
7
8
  DataTableSortDirection,
8
9
  DataTableSortObject,
@@ -18,6 +19,11 @@ export type {
18
19
  DataTableSelectionAction,
19
20
  DataTableRowAction,
20
21
  DataTableSelectAllRequestPayload,
22
+ DataTableLabels,
23
+ DataTableSelectionBarRenderContext,
24
+ DataTableEmptyStateRenderContext,
25
+ DataTableLoadingStateRenderContext,
26
+ DataTableErrorStateRenderContext,
21
27
  } from "./packages/datatable/index";
22
28
 
23
29
  export { FormBuilder, useFormPrefill } from "./packages/form/index";
@@ -29,6 +35,14 @@ export type {
29
35
  FormBuilderDateValue,
30
36
  FormBuilderTimeValue,
31
37
  FormBuilderDateTimeValue,
38
+ FormBuilderValidationContext,
39
+ FormBuilderValidatorResult,
40
+ FormBuilderValidator,
41
+ FormBuilderDependsOnConfig,
42
+ FormBuilderRepeaterProps,
43
+ FormBuilderLabels,
44
+ FormBuilderAlertConfig,
45
+ FormBuilderButtonsRenderContext,
32
46
  FormBuilderLayout,
33
47
  FormBuilderLayoutEntry,
34
48
  FormBuilderSection,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hs-uix",
3
- "version": "1.2.3",
3
+ "version": "1.4.0",
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",