hs-uix 1.6.5 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/form.js CHANGED
@@ -38,6 +38,9 @@ module.exports = __toCommonJS(form_exports);
38
38
  var import_react = __toESM(require("react"));
39
39
  var import_ui_extensions = require("@hubspot/ui-extensions");
40
40
  var import_crm = require("@hubspot/ui-extensions/crm");
41
+
42
+ // packages/form/src/formValues.js
43
+ var isPlainObject = (value) => Object.prototype.toString.call(value) === "[object Object]";
41
44
  var getEmptyValue = (field) => {
42
45
  switch (field.type) {
43
46
  case "toggle":
@@ -73,12 +76,6 @@ var isValueEmpty = (value, field) => {
73
76
  if ((field.type === "toggle" || field.type === "checkbox") && value === false) return true;
74
77
  return false;
75
78
  };
76
- var isPromise = (value) => value && typeof value.then === "function";
77
- var isAsyncFunction = (fn) => fn && fn.constructor && fn.constructor.name === "AsyncFunction";
78
- var normalizeValidatorResult = (result) => {
79
- if (result === true || result === void 0 || result === null || result === false) return null;
80
- return String(result);
81
- };
82
79
  var isDateValueObject = (value) => isPlainObject(value) && Number.isInteger(value.year) && Number.isInteger(value.month) && Number.isInteger(value.date);
83
80
  var isTimeValueObject = (value) => isPlainObject(value) && Number.isInteger(value.hours) && Number.isInteger(value.minutes);
84
81
  var compareDateValues = (a, b) => {
@@ -90,6 +87,64 @@ var compareTimeValues = (a, b) => {
90
87
  if (a.hours !== b.hours) return a.hours - b.hours;
91
88
  return a.minutes - b.minutes;
92
89
  };
90
+ var deepEqual = (a, b) => {
91
+ if (Object.is(a, b)) return true;
92
+ if (typeof a !== typeof b) return false;
93
+ if (a == null || b == null) return false;
94
+ if (Array.isArray(a)) {
95
+ if (!Array.isArray(b) || a.length !== b.length) return false;
96
+ for (let i = 0; i < a.length; i++) {
97
+ if (!deepEqual(a[i], b[i])) return false;
98
+ }
99
+ return true;
100
+ }
101
+ if (a instanceof Date && b instanceof Date) {
102
+ return a.getTime() === b.getTime();
103
+ }
104
+ if (isPlainObject(a) && isPlainObject(b)) {
105
+ const aKeys = Object.keys(a);
106
+ const bKeys = Object.keys(b);
107
+ if (aKeys.length !== bKeys.length) return false;
108
+ for (const key of aKeys) {
109
+ if (!Object.prototype.hasOwnProperty.call(b, key)) return false;
110
+ if (!deepEqual(a[key], b[key])) return false;
111
+ }
112
+ return true;
113
+ }
114
+ return false;
115
+ };
116
+ var deepClone = (value) => {
117
+ if (Array.isArray(value)) return value.map(deepClone);
118
+ if (value instanceof Date) return new Date(value.getTime());
119
+ if (isPlainObject(value)) {
120
+ const next = {};
121
+ for (const key of Object.keys(value)) {
122
+ next[key] = deepClone(value[key]);
123
+ }
124
+ return next;
125
+ }
126
+ return value;
127
+ };
128
+
129
+ // packages/form/src/formValidation.js
130
+ var isPromise = (value) => value && typeof value.then === "function";
131
+ var isAsyncFunction = (fn) => fn && fn.constructor && fn.constructor.name === "AsyncFunction";
132
+ var normalizeValidatorResult = (result) => {
133
+ if (result === true || result === void 0 || result === null || result === false) return null;
134
+ return String(result);
135
+ };
136
+ var resolveRequired = (field, allValues) => {
137
+ if (typeof field.required === "function") return field.required(allValues);
138
+ return !!field.required;
139
+ };
140
+ var resolveDisabled = (field, allValues) => {
141
+ if (typeof field.disabled === "function") return field.disabled(allValues);
142
+ return !!field.disabled;
143
+ };
144
+ var resolveOptions = (field, allValues) => {
145
+ if (typeof field.options === "function") return field.options(allValues);
146
+ return field.options || [];
147
+ };
93
148
  var runDefaultFieldValidator = (value, field, allValues) => {
94
149
  const errorPrefix = field.label || field.name;
95
150
  switch (field.type) {
@@ -259,50 +314,53 @@ var runValidators = (value, field, allValues, fieldTypes, options = {}) => {
259
314
  }
260
315
  return null;
261
316
  };
262
- var resolveRequired = (field, allValues) => {
263
- if (typeof field.required === "function") return field.required(allValues);
264
- return !!field.required;
265
- };
266
- var resolveDisabled = (field, allValues) => {
267
- if (typeof field.disabled === "function") return field.disabled(allValues);
268
- return !!field.disabled;
269
- };
270
- var resolveOptions = (field, allValues) => {
271
- if (typeof field.options === "function") return field.options(allValues);
272
- return field.options || [];
273
- };
317
+
318
+ // packages/form/src/formDependencies.js
274
319
  var getDependsOnName = (field) => field.dependsOnConfig && field.dependsOnConfig.field;
275
320
  var getDependsOnDisplay = (field) => field.dependsOnConfig && field.dependsOnConfig.display || "grouped";
276
321
  var getDependsOnLabel = (field) => field.dependsOnConfig && field.dependsOnConfig.label;
277
322
  var getDependsOnMessage = (field) => field.dependsOnConfig && field.dependsOnConfig.message;
278
- var getRepeaterErrorKey = (fieldName, rowIdx, subFieldName) => `${fieldName}[${rowIdx}].${subFieldName}`;
279
- var isPlainObject = (value) => Object.prototype.toString.call(value) === "[object Object]";
280
- var deepEqual = (a, b) => {
281
- if (Object.is(a, b)) return true;
282
- if (typeof a !== typeof b) return false;
283
- if (a == null || b == null) return false;
284
- if (Array.isArray(a)) {
285
- if (!Array.isArray(b) || a.length !== b.length) return false;
286
- for (let i = 0; i < a.length; i++) {
287
- if (!deepEqual(a[i], b[i])) return false;
288
- }
289
- return true;
290
- }
291
- if (a instanceof Date && b instanceof Date) {
292
- return a.getTime() === b.getTime();
293
- }
294
- if (isPlainObject(a) && isPlainObject(b)) {
295
- const aKeys = Object.keys(a);
296
- const bKeys = Object.keys(b);
297
- if (aKeys.length !== bKeys.length) return false;
298
- for (const key of aKeys) {
299
- if (!Object.prototype.hasOwnProperty.call(b, key)) return false;
300
- if (!deepEqual(a[key], b[key])) return false;
301
- }
302
- return true;
323
+ var resolveDependentCascade = ({ name, value, fields, values, getEmptyValueForField }) => {
324
+ const newValues = { ...values, [name]: value };
325
+ const queue = [name];
326
+ const visited = /* @__PURE__ */ new Set();
327
+ const changedDependents = [];
328
+ while (queue.length > 0) {
329
+ const current = queue.shift();
330
+ if (!current || visited.has(current)) continue;
331
+ visited.add(current);
332
+ fields.forEach((dep) => {
333
+ const parentName = getDependsOnName(dep);
334
+ if (parentName !== current || dep.name === current) return;
335
+ if (!dep.options) return;
336
+ const depOptions = resolveOptions(dep, newValues);
337
+ const depValue = newValues[dep.name];
338
+ if (depValue == null || depValue === "") return;
339
+ const validValues = new Set(depOptions.map((o) => o.value));
340
+ let nextDepValue = depValue;
341
+ let changed = false;
342
+ if (Array.isArray(depValue)) {
343
+ const filtered = depValue.filter((v) => validValues.has(v));
344
+ if (filtered.length !== depValue.length) {
345
+ nextDepValue = filtered;
346
+ changed = true;
347
+ }
348
+ } else if (!validValues.has(depValue)) {
349
+ nextDepValue = getEmptyValueForField(dep);
350
+ changed = true;
351
+ }
352
+ if (changed) {
353
+ newValues[dep.name] = nextDepValue;
354
+ queue.push(dep.name);
355
+ changedDependents.push(dep.name);
356
+ }
357
+ });
303
358
  }
304
- return false;
359
+ return { newValues, changedDependents };
305
360
  };
361
+
362
+ // packages/form/src/FormBuilder.jsx
363
+ var getRepeaterErrorKey = (fieldName, rowIdx, subFieldName) => `${fieldName}[${rowIdx}].${subFieldName}`;
306
364
  var fieldSetHasErrors = (errors, fields) => {
307
365
  if (!errors || !fields || fields.length === 0) return false;
308
366
  const names = new Set(fields.map((field) => field.name));
@@ -311,18 +369,6 @@ var fieldSetHasErrors = (errors, fields) => {
311
369
  return names.has(base);
312
370
  });
313
371
  };
314
- var deepClone = (value) => {
315
- if (Array.isArray(value)) return value.map(deepClone);
316
- if (value instanceof Date) return new Date(value.getTime());
317
- if (isPlainObject(value)) {
318
- const next = {};
319
- for (const key of Object.keys(value)) {
320
- next[key] = deepClone(value[key]);
321
- }
322
- return next;
323
- }
324
- return value;
325
- };
326
372
  var useFormPrefill = (properties, mapping) => {
327
373
  return (0, import_react.useMemo)(() => {
328
374
  if (!properties) return {};
@@ -1189,42 +1235,18 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
1189
1235
  const handleFieldChange = (0, import_react.useCallback)(
1190
1236
  (name, value, options = {}) => {
1191
1237
  const { clearNestedErrors = true } = options;
1192
- const newValues = { ...formValuesRef.current, [name]: value };
1193
- const queue = [name];
1194
- const visited = /* @__PURE__ */ new Set();
1238
+ const { newValues, changedDependents } = resolveDependentCascade({
1239
+ name,
1240
+ value,
1241
+ fields,
1242
+ values: formValuesRef.current,
1243
+ getEmptyValueForField: getFieldEmptyValue
1244
+ });
1195
1245
  const clearedErrors = {};
1196
- while (queue.length > 0) {
1197
- const current = queue.shift();
1198
- if (!current || visited.has(current)) continue;
1199
- visited.add(current);
1200
- fields.forEach((dep) => {
1201
- const parentName = getDependsOnName(dep);
1202
- if (parentName !== current || dep.name === current) return;
1203
- if (!dep.options) return;
1204
- const depOptions = resolveOptions(dep, newValues);
1205
- const depValue = newValues[dep.name];
1206
- if (depValue == null || depValue === "") return;
1207
- const validValues = new Set(depOptions.map((o) => o.value));
1208
- let nextDepValue = depValue;
1209
- let changed = false;
1210
- if (Array.isArray(depValue)) {
1211
- const filtered = depValue.filter((v) => validValues.has(v));
1212
- if (filtered.length !== depValue.length) {
1213
- nextDepValue = filtered;
1214
- changed = true;
1215
- }
1216
- } else if (!validValues.has(depValue)) {
1217
- nextDepValue = getFieldEmptyValue(dep);
1218
- changed = true;
1219
- }
1220
- if (changed) {
1221
- newValues[dep.name] = nextDepValue;
1222
- queue.push(dep.name);
1223
- if (formErrorsRef.current[dep.name] != null) {
1224
- clearedErrors[dep.name] = null;
1225
- }
1226
- }
1227
- });
1246
+ for (const depName of changedDependents) {
1247
+ if (formErrorsRef.current[depName] != null) {
1248
+ clearedErrors[depName] = null;
1249
+ }
1228
1250
  }
1229
1251
  if (formErrorsRef.current[name] != null) {
1230
1252
  clearedErrors[name] = null;
@@ -2042,7 +2064,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
2042
2064
  );
2043
2065
  }
2044
2066
  return /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Box, { key: sf.name, flex: 1 }, sfElement);
2045
- }), /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Inline, { gap: "xs" }, canReorder && rowIdx > 0 && (renderMoveUpControl ? renderMoveUpControl({ index: rowIdx, onClick: () => moveRow(rowIdx, rowIdx - 1) }) : /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Button, { variant: "secondary", size: "sm", onClick: () => moveRow(rowIdx, rowIdx - 1) }, moveUpLabel)), canReorder && rowIdx < rows.length - 1 && (renderMoveDownControl ? renderMoveDownControl({ index: rowIdx, onClick: () => moveRow(rowIdx, rowIdx + 1) }) : /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Button, { variant: "secondary", size: "sm", onClick: () => moveRow(rowIdx, rowIdx + 1) }, moveDownLabel)), canRemove && (renderRemoveControl ? renderRemoveControl({ index: rowIdx, onClick: () => removeRow(rowIdx) }) : /* @__PURE__ */ import_react.default.createElement(
2067
+ }), /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Inline, { gap: "xs" }, canReorder && (renderMoveUpControl ? renderMoveUpControl({ index: rowIdx, disabled: rowIdx === 0, onClick: () => moveRow(rowIdx, rowIdx - 1) }) : /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Button, { variant: "secondary", size: "sm", disabled: rowIdx === 0, onClick: () => moveRow(rowIdx, rowIdx - 1) }, moveUpLabel)), canReorder && (renderMoveDownControl ? renderMoveDownControl({ index: rowIdx, disabled: rowIdx === rows.length - 1, onClick: () => moveRow(rowIdx, rowIdx + 1) }) : /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Button, { variant: "secondary", size: "sm", disabled: rowIdx === rows.length - 1, onClick: () => moveRow(rowIdx, rowIdx + 1) }, moveDownLabel)), canRemove && (renderRemoveControl ? renderRemoveControl({ index: rowIdx, onClick: () => removeRow(rowIdx) }) : /* @__PURE__ */ import_react.default.createElement(
2046
2068
  import_ui_extensions.Button,
2047
2069
  {
2048
2070
  variant: "secondary",
@@ -2050,7 +2072,7 @@ var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref)
2050
2072
  onClick: () => removeRow(rowIdx)
2051
2073
  },
2052
2074
  removeLabel
2053
- ))))), canAdd && (renderAddControl ? renderAddControl({ onClick: addRow, count: rows.length }) : /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Link, { onClick: addRow }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", align: "center", gap: "flush" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Icon, { name: "add" }), /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { format: { fontWeight: "demibold" } }, addLabel)))), repeaterHasError && repeaterErrorMessage && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { variant: "microcopy" }, repeaterErrorMessage));
2075
+ ))))), canAdd && (renderAddControl ? renderAddControl({ onClick: addRow, count: rows.length }) : /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Link, { onClick: addRow }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", align: "center", gap: "xs" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Icon, { name: "add" }), /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { format: { fontWeight: "demibold" } }, addLabel)))), repeaterHasError && repeaterErrorMessage && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { variant: "microcopy" }, repeaterErrorMessage));
2054
2076
  }
2055
2077
  default:
2056
2078
  return /* @__PURE__ */ import_react.default.createElement(
package/dist/form.mjs CHANGED
@@ -42,6 +42,9 @@ import {
42
42
  CrmPropertyList,
43
43
  CrmAssociationPropertyList
44
44
  } from "@hubspot/ui-extensions/crm";
45
+
46
+ // packages/form/src/formValues.js
47
+ var isPlainObject = (value) => Object.prototype.toString.call(value) === "[object Object]";
45
48
  var getEmptyValue = (field) => {
46
49
  switch (field.type) {
47
50
  case "toggle":
@@ -77,12 +80,6 @@ var isValueEmpty = (value, field) => {
77
80
  if ((field.type === "toggle" || field.type === "checkbox") && value === false) return true;
78
81
  return false;
79
82
  };
80
- var isPromise = (value) => value && typeof value.then === "function";
81
- var isAsyncFunction = (fn) => fn && fn.constructor && fn.constructor.name === "AsyncFunction";
82
- var normalizeValidatorResult = (result) => {
83
- if (result === true || result === void 0 || result === null || result === false) return null;
84
- return String(result);
85
- };
86
83
  var isDateValueObject = (value) => isPlainObject(value) && Number.isInteger(value.year) && Number.isInteger(value.month) && Number.isInteger(value.date);
87
84
  var isTimeValueObject = (value) => isPlainObject(value) && Number.isInteger(value.hours) && Number.isInteger(value.minutes);
88
85
  var compareDateValues = (a, b) => {
@@ -94,6 +91,64 @@ var compareTimeValues = (a, b) => {
94
91
  if (a.hours !== b.hours) return a.hours - b.hours;
95
92
  return a.minutes - b.minutes;
96
93
  };
94
+ var deepEqual = (a, b) => {
95
+ if (Object.is(a, b)) return true;
96
+ if (typeof a !== typeof b) return false;
97
+ if (a == null || b == null) return false;
98
+ if (Array.isArray(a)) {
99
+ if (!Array.isArray(b) || a.length !== b.length) return false;
100
+ for (let i = 0; i < a.length; i++) {
101
+ if (!deepEqual(a[i], b[i])) return false;
102
+ }
103
+ return true;
104
+ }
105
+ if (a instanceof Date && b instanceof Date) {
106
+ return a.getTime() === b.getTime();
107
+ }
108
+ if (isPlainObject(a) && isPlainObject(b)) {
109
+ const aKeys = Object.keys(a);
110
+ const bKeys = Object.keys(b);
111
+ if (aKeys.length !== bKeys.length) return false;
112
+ for (const key of aKeys) {
113
+ if (!Object.prototype.hasOwnProperty.call(b, key)) return false;
114
+ if (!deepEqual(a[key], b[key])) return false;
115
+ }
116
+ return true;
117
+ }
118
+ return false;
119
+ };
120
+ var deepClone = (value) => {
121
+ if (Array.isArray(value)) return value.map(deepClone);
122
+ if (value instanceof Date) return new Date(value.getTime());
123
+ if (isPlainObject(value)) {
124
+ const next = {};
125
+ for (const key of Object.keys(value)) {
126
+ next[key] = deepClone(value[key]);
127
+ }
128
+ return next;
129
+ }
130
+ return value;
131
+ };
132
+
133
+ // packages/form/src/formValidation.js
134
+ var isPromise = (value) => value && typeof value.then === "function";
135
+ var isAsyncFunction = (fn) => fn && fn.constructor && fn.constructor.name === "AsyncFunction";
136
+ var normalizeValidatorResult = (result) => {
137
+ if (result === true || result === void 0 || result === null || result === false) return null;
138
+ return String(result);
139
+ };
140
+ var resolveRequired = (field, allValues) => {
141
+ if (typeof field.required === "function") return field.required(allValues);
142
+ return !!field.required;
143
+ };
144
+ var resolveDisabled = (field, allValues) => {
145
+ if (typeof field.disabled === "function") return field.disabled(allValues);
146
+ return !!field.disabled;
147
+ };
148
+ var resolveOptions = (field, allValues) => {
149
+ if (typeof field.options === "function") return field.options(allValues);
150
+ return field.options || [];
151
+ };
97
152
  var runDefaultFieldValidator = (value, field, allValues) => {
98
153
  const errorPrefix = field.label || field.name;
99
154
  switch (field.type) {
@@ -263,50 +318,53 @@ var runValidators = (value, field, allValues, fieldTypes, options = {}) => {
263
318
  }
264
319
  return null;
265
320
  };
266
- var resolveRequired = (field, allValues) => {
267
- if (typeof field.required === "function") return field.required(allValues);
268
- return !!field.required;
269
- };
270
- var resolveDisabled = (field, allValues) => {
271
- if (typeof field.disabled === "function") return field.disabled(allValues);
272
- return !!field.disabled;
273
- };
274
- var resolveOptions = (field, allValues) => {
275
- if (typeof field.options === "function") return field.options(allValues);
276
- return field.options || [];
277
- };
321
+
322
+ // packages/form/src/formDependencies.js
278
323
  var getDependsOnName = (field) => field.dependsOnConfig && field.dependsOnConfig.field;
279
324
  var getDependsOnDisplay = (field) => field.dependsOnConfig && field.dependsOnConfig.display || "grouped";
280
325
  var getDependsOnLabel = (field) => field.dependsOnConfig && field.dependsOnConfig.label;
281
326
  var getDependsOnMessage = (field) => field.dependsOnConfig && field.dependsOnConfig.message;
282
- var getRepeaterErrorKey = (fieldName, rowIdx, subFieldName) => `${fieldName}[${rowIdx}].${subFieldName}`;
283
- var isPlainObject = (value) => Object.prototype.toString.call(value) === "[object Object]";
284
- var deepEqual = (a, b) => {
285
- if (Object.is(a, b)) return true;
286
- if (typeof a !== typeof b) return false;
287
- if (a == null || b == null) return false;
288
- if (Array.isArray(a)) {
289
- if (!Array.isArray(b) || a.length !== b.length) return false;
290
- for (let i = 0; i < a.length; i++) {
291
- if (!deepEqual(a[i], b[i])) return false;
292
- }
293
- return true;
294
- }
295
- if (a instanceof Date && b instanceof Date) {
296
- return a.getTime() === b.getTime();
297
- }
298
- if (isPlainObject(a) && isPlainObject(b)) {
299
- const aKeys = Object.keys(a);
300
- const bKeys = Object.keys(b);
301
- if (aKeys.length !== bKeys.length) return false;
302
- for (const key of aKeys) {
303
- if (!Object.prototype.hasOwnProperty.call(b, key)) return false;
304
- if (!deepEqual(a[key], b[key])) return false;
305
- }
306
- return true;
327
+ var resolveDependentCascade = ({ name, value, fields, values, getEmptyValueForField }) => {
328
+ const newValues = { ...values, [name]: value };
329
+ const queue = [name];
330
+ const visited = /* @__PURE__ */ new Set();
331
+ const changedDependents = [];
332
+ while (queue.length > 0) {
333
+ const current = queue.shift();
334
+ if (!current || visited.has(current)) continue;
335
+ visited.add(current);
336
+ fields.forEach((dep) => {
337
+ const parentName = getDependsOnName(dep);
338
+ if (parentName !== current || dep.name === current) return;
339
+ if (!dep.options) return;
340
+ const depOptions = resolveOptions(dep, newValues);
341
+ const depValue = newValues[dep.name];
342
+ if (depValue == null || depValue === "") return;
343
+ const validValues = new Set(depOptions.map((o) => o.value));
344
+ let nextDepValue = depValue;
345
+ let changed = false;
346
+ if (Array.isArray(depValue)) {
347
+ const filtered = depValue.filter((v) => validValues.has(v));
348
+ if (filtered.length !== depValue.length) {
349
+ nextDepValue = filtered;
350
+ changed = true;
351
+ }
352
+ } else if (!validValues.has(depValue)) {
353
+ nextDepValue = getEmptyValueForField(dep);
354
+ changed = true;
355
+ }
356
+ if (changed) {
357
+ newValues[dep.name] = nextDepValue;
358
+ queue.push(dep.name);
359
+ changedDependents.push(dep.name);
360
+ }
361
+ });
307
362
  }
308
- return false;
363
+ return { newValues, changedDependents };
309
364
  };
365
+
366
+ // packages/form/src/FormBuilder.jsx
367
+ var getRepeaterErrorKey = (fieldName, rowIdx, subFieldName) => `${fieldName}[${rowIdx}].${subFieldName}`;
310
368
  var fieldSetHasErrors = (errors, fields) => {
311
369
  if (!errors || !fields || fields.length === 0) return false;
312
370
  const names = new Set(fields.map((field) => field.name));
@@ -315,18 +373,6 @@ var fieldSetHasErrors = (errors, fields) => {
315
373
  return names.has(base);
316
374
  });
317
375
  };
318
- var deepClone = (value) => {
319
- if (Array.isArray(value)) return value.map(deepClone);
320
- if (value instanceof Date) return new Date(value.getTime());
321
- if (isPlainObject(value)) {
322
- const next = {};
323
- for (const key of Object.keys(value)) {
324
- next[key] = deepClone(value[key]);
325
- }
326
- return next;
327
- }
328
- return value;
329
- };
330
376
  var useFormPrefill = (properties, mapping) => {
331
377
  return useMemo(() => {
332
378
  if (!properties) return {};
@@ -1193,42 +1239,18 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
1193
1239
  const handleFieldChange = useCallback(
1194
1240
  (name, value, options = {}) => {
1195
1241
  const { clearNestedErrors = true } = options;
1196
- const newValues = { ...formValuesRef.current, [name]: value };
1197
- const queue = [name];
1198
- const visited = /* @__PURE__ */ new Set();
1242
+ const { newValues, changedDependents } = resolveDependentCascade({
1243
+ name,
1244
+ value,
1245
+ fields,
1246
+ values: formValuesRef.current,
1247
+ getEmptyValueForField: getFieldEmptyValue
1248
+ });
1199
1249
  const clearedErrors = {};
1200
- while (queue.length > 0) {
1201
- const current = queue.shift();
1202
- if (!current || visited.has(current)) continue;
1203
- visited.add(current);
1204
- fields.forEach((dep) => {
1205
- const parentName = getDependsOnName(dep);
1206
- if (parentName !== current || dep.name === current) return;
1207
- if (!dep.options) return;
1208
- const depOptions = resolveOptions(dep, newValues);
1209
- const depValue = newValues[dep.name];
1210
- if (depValue == null || depValue === "") return;
1211
- const validValues = new Set(depOptions.map((o) => o.value));
1212
- let nextDepValue = depValue;
1213
- let changed = false;
1214
- if (Array.isArray(depValue)) {
1215
- const filtered = depValue.filter((v) => validValues.has(v));
1216
- if (filtered.length !== depValue.length) {
1217
- nextDepValue = filtered;
1218
- changed = true;
1219
- }
1220
- } else if (!validValues.has(depValue)) {
1221
- nextDepValue = getFieldEmptyValue(dep);
1222
- changed = true;
1223
- }
1224
- if (changed) {
1225
- newValues[dep.name] = nextDepValue;
1226
- queue.push(dep.name);
1227
- if (formErrorsRef.current[dep.name] != null) {
1228
- clearedErrors[dep.name] = null;
1229
- }
1230
- }
1231
- });
1250
+ for (const depName of changedDependents) {
1251
+ if (formErrorsRef.current[depName] != null) {
1252
+ clearedErrors[depName] = null;
1253
+ }
1232
1254
  }
1233
1255
  if (formErrorsRef.current[name] != null) {
1234
1256
  clearedErrors[name] = null;
@@ -2046,7 +2068,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
2046
2068
  );
2047
2069
  }
2048
2070
  return /* @__PURE__ */ React.createElement(Box, { key: sf.name, flex: 1 }, sfElement);
2049
- }), /* @__PURE__ */ React.createElement(Inline, { gap: "xs" }, canReorder && rowIdx > 0 && (renderMoveUpControl ? renderMoveUpControl({ index: rowIdx, onClick: () => moveRow(rowIdx, rowIdx - 1) }) : /* @__PURE__ */ React.createElement(Button, { variant: "secondary", size: "sm", onClick: () => moveRow(rowIdx, rowIdx - 1) }, moveUpLabel)), canReorder && rowIdx < rows.length - 1 && (renderMoveDownControl ? renderMoveDownControl({ index: rowIdx, onClick: () => moveRow(rowIdx, rowIdx + 1) }) : /* @__PURE__ */ React.createElement(Button, { variant: "secondary", size: "sm", onClick: () => moveRow(rowIdx, rowIdx + 1) }, moveDownLabel)), canRemove && (renderRemoveControl ? renderRemoveControl({ index: rowIdx, onClick: () => removeRow(rowIdx) }) : /* @__PURE__ */ React.createElement(
2071
+ }), /* @__PURE__ */ React.createElement(Inline, { gap: "xs" }, canReorder && (renderMoveUpControl ? renderMoveUpControl({ index: rowIdx, disabled: rowIdx === 0, onClick: () => moveRow(rowIdx, rowIdx - 1) }) : /* @__PURE__ */ React.createElement(Button, { variant: "secondary", size: "sm", disabled: rowIdx === 0, onClick: () => moveRow(rowIdx, rowIdx - 1) }, moveUpLabel)), canReorder && (renderMoveDownControl ? renderMoveDownControl({ index: rowIdx, disabled: rowIdx === rows.length - 1, onClick: () => moveRow(rowIdx, rowIdx + 1) }) : /* @__PURE__ */ React.createElement(Button, { variant: "secondary", size: "sm", disabled: rowIdx === rows.length - 1, onClick: () => moveRow(rowIdx, rowIdx + 1) }, moveDownLabel)), canRemove && (renderRemoveControl ? renderRemoveControl({ index: rowIdx, onClick: () => removeRow(rowIdx) }) : /* @__PURE__ */ React.createElement(
2050
2072
  Button,
2051
2073
  {
2052
2074
  variant: "secondary",
@@ -2054,7 +2076,7 @@ var FormBuilder = forwardRef(function FormBuilder2(props, ref) {
2054
2076
  onClick: () => removeRow(rowIdx)
2055
2077
  },
2056
2078
  removeLabel
2057
- ))))), canAdd && (renderAddControl ? renderAddControl({ onClick: addRow, count: rows.length }) : /* @__PURE__ */ React.createElement(Link, { onClick: addRow }, /* @__PURE__ */ React.createElement(Flex, { direction: "row", align: "center", gap: "flush" }, /* @__PURE__ */ React.createElement(Icon, { name: "add" }), /* @__PURE__ */ React.createElement(Text, { format: { fontWeight: "demibold" } }, addLabel)))), repeaterHasError && repeaterErrorMessage && /* @__PURE__ */ React.createElement(Text, { variant: "microcopy" }, repeaterErrorMessage));
2079
+ ))))), canAdd && (renderAddControl ? renderAddControl({ onClick: addRow, count: rows.length }) : /* @__PURE__ */ React.createElement(Link, { onClick: addRow }, /* @__PURE__ */ React.createElement(Flex, { direction: "row", align: "center", gap: "xs" }, /* @__PURE__ */ React.createElement(Icon, { name: "add" }), /* @__PURE__ */ React.createElement(Text, { format: { fontWeight: "demibold" } }, addLabel)))), repeaterHasError && repeaterErrorMessage && /* @__PURE__ */ React.createElement(Text, { variant: "microcopy" }, repeaterErrorMessage));
2058
2080
  }
2059
2081
  default:
2060
2082
  return /* @__PURE__ */ React.createElement(