@overmap-ai/forms 1.0.32-react-flow-david-fixes.11 → 1.0.32-react-flow-david-fixes.13

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.
Files changed (59) hide show
  1. package/dist/form/builder/components/FieldBuilder.d.ts +1 -2
  2. package/dist/form/builder/components/index.d.ts +1 -2
  3. package/dist/form/conditions/DateFieldCondition/DateFieldCondition.d.ts +6 -6
  4. package/dist/form/conditions/NumberFieldCondition/NumberFieldCondition.d.ts +16 -16
  5. package/dist/form/conditions/OTPFieldCondition/OTPFieldCondition.d.ts +1 -1
  6. package/dist/form/conditions/OTPFieldCondition/typings.d.ts +1 -2
  7. package/dist/form/conditions/RadioFieldCondition/RadioFieldCondition.d.ts +4 -4
  8. package/dist/form/conditions/SelectFieldCondition/SelectFieldCondition.d.ts +4 -4
  9. package/dist/form/conditions/components/RemoveConditionButton.d.ts +1 -1
  10. package/dist/form/fields/BaseField/BaseField.d.ts +7 -5
  11. package/dist/form/fields/BaseField/index.d.ts +0 -1
  12. package/dist/form/fields/BaseField/typings.d.ts +2 -1
  13. package/dist/form/fields/BaseFormElement/BaseFormElement.d.ts +1 -2
  14. package/dist/form/fields/BaseFormElement/typings.d.ts +8 -2
  15. package/dist/form/fields/BaseStringField/BaseStringField.d.ts +5 -2
  16. package/dist/form/fields/BooleanField/BooleanField.d.ts +6 -2
  17. package/dist/form/fields/BooleanField/BooleanInput.d.ts +1 -1
  18. package/dist/form/fields/CheckboxListField/CheckboxListField.d.ts +5 -1
  19. package/dist/form/fields/CheckboxListField/CheckboxListInput.d.ts +1 -1
  20. package/dist/form/fields/DateField/DateField.d.ts +9 -6
  21. package/dist/form/fields/DateField/DateInput.d.ts +1 -1
  22. package/dist/form/fields/FieldSection/FieldSection.d.ts +3 -4
  23. package/dist/form/fields/FieldSection/FieldSectionLayout.d.ts +1 -1
  24. package/dist/form/fields/FieldSection/typings.d.ts +1 -1
  25. package/dist/form/fields/MultiSelectField/MultiSelectField.d.ts +5 -3
  26. package/dist/form/fields/MultiSelectField/MultiSelectInput.d.ts +1 -1
  27. package/dist/form/fields/MultiStringField/MultiStringField.d.ts +6 -4
  28. package/dist/form/fields/MultiStringField/MultiStringInput.d.ts +1 -1
  29. package/dist/form/fields/NumberField/NumberField.d.ts +11 -7
  30. package/dist/form/fields/NumberField/NumberInput.d.ts +1 -1
  31. package/dist/form/fields/OneTimePasswordField/OTPField.d.ts +6 -2
  32. package/dist/form/fields/OneTimePasswordField/OTPInput.d.ts +1 -1
  33. package/dist/form/fields/RadioField/RadioField.d.ts +9 -5
  34. package/dist/form/fields/RadioField/RadioInput.d.ts +1 -1
  35. package/dist/form/fields/ScanField/ScanField.d.ts +5 -1
  36. package/dist/form/fields/ScanField/ScanInput.d.ts +1 -1
  37. package/dist/form/fields/SelectField/SelectField.d.ts +9 -5
  38. package/dist/form/fields/SelectField/SelectInput.d.ts +1 -1
  39. package/dist/form/fields/StringField/StringField.d.ts +1 -1
  40. package/dist/form/fields/StringField/StringInput.d.ts +1 -1
  41. package/dist/form/fields/TextField/TextField.d.ts +1 -1
  42. package/dist/form/fields/TextField/TextInput.d.ts +1 -1
  43. package/dist/form/fields/UploadField/UploadField.d.ts +6 -4
  44. package/dist/form/fields/UploadField/UploadInput.d.ts +1 -1
  45. package/dist/form/fields/UploadField/utils.d.ts +2 -0
  46. package/dist/form/fields/_utils.d.ts +2 -0
  47. package/dist/form/fields/hooks.d.ts +398 -2
  48. package/dist/form/fields/index.d.ts +2 -0
  49. package/dist/form/fields/typings.d.ts +2 -10
  50. package/dist/form/fields/utils.d.ts +3 -1
  51. package/dist/form/index.d.ts +1 -1
  52. package/dist/form/typings.d.ts +1 -1
  53. package/dist/form/utils.d.ts +6 -7
  54. package/dist/forms.js +698 -567
  55. package/dist/forms.umd.cjs +698 -567
  56. package/package.json +1 -1
  57. package/dist/form/fields/BaseField/hooks.d.ts +0 -397
  58. /package/dist/form/builder/{components → list}/FieldSectionBuilder.d.ts +0 -0
  59. /package/dist/form/builder/{components → list}/FieldSectionWithActions.d.ts +0 -0
package/dist/forms.js CHANGED
@@ -5,7 +5,7 @@ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
5
5
  import { Card, LuIcon, Text, Spinner, ButtonGroup, Tooltip, IconButton, Separator, Input, Badge, Checkbox, CheckboxGroup, Popover, Button, DayPicker, Menu, OneTimePasswordField, RadioGroup, TextArea, useToast, stopPropagation, Heading, HoverCard, ToggleGroup } from "@overmap-ai/blocks";
6
6
  import { cx } from "class-variance-authority";
7
7
  import * as React from "react";
8
- import { memo, forwardRef, useMemo, createContext, useContext, useState, useRef, useCallback, useEffect, Fragment as Fragment$1, use, useLayoutEffect, useId as useId$1 } from "react";
8
+ import { memo, forwardRef, createContext, useContext, useState, useRef, useCallback, useMemo, useEffect, Fragment as Fragment$1, use, useLayoutEffect, useId as useId$1 } from "react";
9
9
  import "@xyflow/react/dist/style.css";
10
10
  import { getBezierPath, BaseEdge, EdgeLabelRenderer, NodeToolbar, Position, Handle, MarkerType, useNodesState, useEdgesState, ReactFlow, Panel } from "@xyflow/react";
11
11
  import { useField, useFormikContext, useFormik, FormikProvider } from "formik";
@@ -19,8 +19,8 @@ import ReactDOM__default from "react-dom";
19
19
  import { saveAs } from "file-saver";
20
20
  import { DecodeHintType as DecodeHintType$2, useZxing } from "react-zxing";
21
21
  import get from "lodash.get";
22
- import set from "lodash.set";
23
22
  import Dagre from "@dagrejs/dagre";
23
+ import set from "lodash.set";
24
24
  const FileCard = memo(
25
25
  forwardRef((props, ref) => {
26
26
  const { file, className, error, rightSlot, ...rest } = props;
@@ -38,6 +38,14 @@ const SEVERITY_COLOR_MAPPING = {
38
38
  info: "base",
39
39
  success: "success"
40
40
  };
41
+ function isStringArray(value) {
42
+ if (!Array.isArray(value)) return false;
43
+ return value.every((item) => typeof item === "string");
44
+ }
45
+ function isFilePromiseArray(value) {
46
+ if (!Array.isArray(value)) return false;
47
+ return value.every((item) => item instanceof Promise || item instanceof File);
48
+ }
41
49
  class Observable {
42
50
  constructor() {
43
51
  __publicField(this, "observers", /* @__PURE__ */ new Set());
@@ -99,14 +107,8 @@ class BaseField extends BaseFormElement {
99
107
  this.image = image;
100
108
  this.fieldValidators = fieldValidators;
101
109
  }
102
- isBlank(value) {
103
- return value === void 0;
104
- }
105
- isEqual(value1, value2) {
106
- return value1 === value2;
107
- }
108
110
  getError(value) {
109
- if (this.required && this.isBlank(value)) {
111
+ if (this.required && this.isValueBlank(value)) {
110
112
  return "This field is required.";
111
113
  }
112
114
  for (const validator of this.getFieldValidators()) {
@@ -142,59 +144,15 @@ class BaseField extends BaseFormElement {
142
144
  this.fieldValidators = fieldValidators ?? this.fieldValidators;
143
145
  super.setOptions(base);
144
146
  }
147
+ isValueBlank(value) {
148
+ return this.areValuesEqual(value, this.blankValue());
149
+ }
145
150
  getFieldValidators() {
146
151
  return [...this.fieldValidators];
147
152
  }
148
153
  }
149
154
  __publicField(BaseField, "fieldTypeName");
150
155
  __publicField(BaseField, "fieldTypeDescription");
151
- const useFormikInput = (props) => {
152
- const { field, size, showInputOnly, onValuesChange, ...rest } = props;
153
- const [fieldProps, meta, helpers] = useField(field.identifier);
154
- const { touched } = meta;
155
- const helpText = meta.error ?? field.description;
156
- const severity = meta.error ? "danger" : void 0;
157
- const inputId = field.identifier;
158
- const labelId = `${inputId}-label`;
159
- const label = field.required ? `${field.label} *` : field.label;
160
- const fieldPropsWithValidation = useMemo(() => {
161
- const handleChange = (value) => {
162
- helpers.setValue(value, false).then();
163
- onValuesChange == null ? void 0 : onValuesChange(field.identifier, value);
164
- if (touched || !field.onlyValidateAfterTouched) {
165
- helpers.setError(field.getError(value));
166
- }
167
- };
168
- const handleBlur = (value) => {
169
- void helpers.setTouched(true, false).then();
170
- helpers.setError(field.getError(value));
171
- };
172
- return {
173
- ...fieldProps,
174
- // name and value not being overridden
175
- onChange: handleChange,
176
- onBlur: handleBlur
177
- };
178
- }, [field, fieldProps, helpers, onValuesChange, touched]);
179
- return [
180
- {
181
- helpText,
182
- size,
183
- severity,
184
- inputId,
185
- labelId,
186
- label,
187
- showInputOnly,
188
- fieldProps: fieldPropsWithValidation,
189
- helpers,
190
- field,
191
- touched
192
- },
193
- /* Props that should be spread on the html element representing the field */
194
- { ...rest, "aria-labelledby": labelId }
195
- ];
196
- };
197
- const EMPTY_ARRAY = [];
198
156
  const InputWithHelpText = (props) => {
199
157
  const { helpText, children, severity } = props;
200
158
  const color = severity ? SEVERITY_COLOR_MAPPING[severity] : "base";
@@ -2304,14 +2262,70 @@ const InputWithLabelAndHelpText = (props) => {
2304
2262
  const { children, ...restProps } = props;
2305
2263
  return /* @__PURE__ */ jsx(InputWithHelpText, { ...restProps, children });
2306
2264
  };
2265
+ const useFieldInput = (field, props) => {
2266
+ return useMemo(() => {
2267
+ if (!props || !field) return null;
2268
+ return field.render(props);
2269
+ }, [field, props]);
2270
+ };
2271
+ const useFieldInputs = (fields, props) => {
2272
+ return /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-4", children: fields.map((field) => {
2273
+ return /* @__PURE__ */ jsx(Fragment$1, { children: field.render(props) }, field.identifier);
2274
+ }) });
2275
+ };
2276
+ const useFormikInput = (props) => {
2277
+ const { field, size, showInputOnly, onValuesChange, ...rest } = props;
2278
+ const [fieldProps, meta, helpers] = useField(field.identifier);
2279
+ const { touched } = meta;
2280
+ const helpText = meta.error ?? field.description;
2281
+ const severity = meta.error ? "danger" : void 0;
2282
+ const inputId = field.identifier;
2283
+ const labelId = `${inputId}-label`;
2284
+ const label = field.required ? `${field.label} *` : field.label;
2285
+ const fieldPropsWithValidation = useMemo(() => {
2286
+ const handleChange = (value) => {
2287
+ helpers.setValue(value, false).then();
2288
+ onValuesChange == null ? void 0 : onValuesChange(field.identifier, value);
2289
+ if (touched || !field.onlyValidateAfterTouched) {
2290
+ helpers.setError(field.getError(value));
2291
+ }
2292
+ };
2293
+ const handleBlur = (value) => {
2294
+ void helpers.setTouched(true, false).then();
2295
+ helpers.setError(field.getError(value));
2296
+ };
2297
+ return {
2298
+ ...fieldProps,
2299
+ // name and value not being overridden
2300
+ onChange: handleChange,
2301
+ onBlur: handleBlur
2302
+ };
2303
+ }, [field, fieldProps, helpers, onValuesChange, touched]);
2304
+ return [
2305
+ {
2306
+ helpText,
2307
+ size,
2308
+ severity,
2309
+ inputId,
2310
+ labelId,
2311
+ label,
2312
+ showInputOnly,
2313
+ fieldProps: fieldPropsWithValidation,
2314
+ helpers,
2315
+ field,
2316
+ touched
2317
+ },
2318
+ /* Props that should be spread on the html element representing the field */
2319
+ { ...rest, "aria-labelledby": labelId }
2320
+ ];
2321
+ };
2307
2322
  const MultiStringInput = memo((props) => {
2308
2323
  const [{ inputId, labelId, size, severity, showInputOnly, field, helpText, label, fieldProps }, rest] = useFormikInput(props);
2309
2324
  const computedHelpText = showInputOnly ? null : helpText;
2310
2325
  const computedLabel = showInputOnly ? "" : label;
2311
- const { name, onChange, onBlur } = fieldProps;
2326
+ const { value, name, onChange, onBlur } = fieldProps;
2312
2327
  const droppableId = `${inputId}-droppable`;
2313
2328
  const { disabled } = rest;
2314
- const value = fieldProps.value ?? EMPTY_ARRAY;
2315
2329
  const [intermediateValue, setIntermediateValue] = useState("");
2316
2330
  const [internalError, setInternalError] = useState("");
2317
2331
  const updatedHelpText = internalError || computedHelpText;
@@ -2525,14 +2539,6 @@ const _MultiStringField = class _MultiStringField extends BaseField {
2525
2539
  deserializeValue(value) {
2526
2540
  return value;
2527
2541
  }
2528
- isBlank(value) {
2529
- return super.isBlank(value) || (value == null ? void 0 : value.length) === 0;
2530
- }
2531
- isEqual(value1, value2) {
2532
- if (value1 === void 0 && value2 === void 0) return true;
2533
- if (value1 === void 0 || value2 === void 0) return false;
2534
- return value1.every((v) => value2.includes(v)) && value2.every((v) => value1.includes(v));
2535
- }
2536
2542
  getFieldValidators() {
2537
2543
  const validators = super.getFieldValidators();
2538
2544
  validators.push((value) => {
@@ -2550,6 +2556,18 @@ const _MultiStringField = class _MultiStringField extends BaseField {
2550
2556
  static deserialize(data) {
2551
2557
  return new _MultiStringField(data);
2552
2558
  }
2559
+ isSerializedValueValid(value) {
2560
+ return isStringArray(value);
2561
+ }
2562
+ isValueValid(value) {
2563
+ return isStringArray(value);
2564
+ }
2565
+ blankValue() {
2566
+ return [];
2567
+ }
2568
+ areValuesEqual(value1, value2) {
2569
+ return value1.every((v) => value2.includes(v)) && value2.every((v) => value1.includes(v));
2570
+ }
2553
2571
  };
2554
2572
  __publicField(_MultiStringField, "fieldTypeName", "Multi-string");
2555
2573
  __publicField(_MultiStringField, "fieldTypeDescription", "Allows the user to provide multiple unique strings.");
@@ -2655,10 +2673,6 @@ const _BooleanField = class _BooleanField extends BaseField {
2655
2673
  __publicField(this, "type", "boolean");
2656
2674
  __publicField(this, "onlyValidateAfterTouched", false);
2657
2675
  }
2658
- // if a BooleanField is required, `false` is considered blank
2659
- isBlank(value) {
2660
- return this.required && !value;
2661
- }
2662
2676
  serialize() {
2663
2677
  return super.serialize();
2664
2678
  }
@@ -2687,6 +2701,18 @@ const _BooleanField = class _BooleanField extends BaseField {
2687
2701
  render(props) {
2688
2702
  return /* @__PURE__ */ jsx(BooleanInput, { ...props, field: this });
2689
2703
  }
2704
+ isSerializedValueValid(value) {
2705
+ return typeof value === "boolean";
2706
+ }
2707
+ isValueValid(value) {
2708
+ return typeof value === "boolean";
2709
+ }
2710
+ blankValue() {
2711
+ return false;
2712
+ }
2713
+ areValuesEqual(value1, value2) {
2714
+ return value1 === value2;
2715
+ }
2690
2716
  };
2691
2717
  __publicField(_BooleanField, "fieldTypeName", "Checkbox");
2692
2718
  __publicField(_BooleanField, "fieldTypeDescription", "Perfect for both optional and required yes/no questions.");
@@ -2699,14 +2725,14 @@ const NumberInput = memo((props) => {
2699
2725
  const { name, onBlur, onChange, value } = fieldProps;
2700
2726
  const computedHelpText = showInputOnly ? null : helpText;
2701
2727
  const computedLabel = showInputOnly ? "" : label;
2702
- const [internalValue, setInternalValue] = useState(void 0);
2728
+ const [internalValue, setInternalValue] = useState(null);
2703
2729
  useEffect(() => {
2704
2730
  setInternalValue(value);
2705
2731
  }, [value]);
2706
2732
  const handleChange = useCallback(
2707
2733
  (e) => {
2708
2734
  const number = e.target.valueAsNumber;
2709
- const value2 = Number.isNaN(number) ? void 0 : number;
2735
+ const value2 = !isNaN(number) ? number : null;
2710
2736
  setInternalValue(value2);
2711
2737
  if (touched || !field.onlyValidateAfterTouched) {
2712
2738
  helpers.setError(field.getError(value2));
@@ -2874,6 +2900,18 @@ const _NumberField = class _NumberField extends BaseField {
2874
2900
  render(props) {
2875
2901
  return /* @__PURE__ */ jsx(NumberInput, { field: this, ...props });
2876
2902
  }
2903
+ isSerializedValueValid(value) {
2904
+ return typeof value === "number" || value === null;
2905
+ }
2906
+ isValueValid(value) {
2907
+ return typeof value === "number" || value === null;
2908
+ }
2909
+ areValuesEqual(value1, value2) {
2910
+ return value1 === value2;
2911
+ }
2912
+ blankValue() {
2913
+ return null;
2914
+ }
2877
2915
  };
2878
2916
  __publicField(_NumberField, "fieldTypeName", "Number");
2879
2917
  __publicField(_NumberField, "fieldTypeDescription", "Allows specifying a number within a given range.");
@@ -2971,18 +3009,28 @@ class BaseStringField extends BaseField {
2971
3009
  deserializeValue(value) {
2972
3010
  return value;
2973
3011
  }
3012
+ isSerializedValueValid(value) {
3013
+ return typeof value === "string";
3014
+ }
3015
+ isValueValid(value) {
3016
+ return typeof value === "string";
3017
+ }
3018
+ blankValue() {
3019
+ return "";
3020
+ }
3021
+ areValuesEqual(value1, value2) {
3022
+ return value1 === value2;
3023
+ }
2974
3024
  }
2975
3025
  const CheckboxListInput = memo((props) => {
2976
3026
  const [{ inputId, labelId, size, severity, field, showInputOnly, helpText, label, fieldProps }, rest] = useFormikInput(props);
2977
- const { name, onChange, onBlur } = fieldProps;
3027
+ const { value, name, onChange, onBlur } = fieldProps;
2978
3028
  const computedHelpText = showInputOnly ? null : helpText;
2979
3029
  const computedLabel = showInputOnly ? "" : label;
2980
- const value = fieldProps.value ?? EMPTY_ARRAY;
2981
3030
  const handleChange = useCallback(
2982
3031
  (value2) => {
2983
- const newValue = value2.length > 0 ? value2 : void 0;
2984
- onChange(newValue);
2985
- onBlur(newValue);
3032
+ onChange(value2);
3033
+ onBlur(value2);
2986
3034
  },
2987
3035
  [onBlur, onChange]
2988
3036
  );
@@ -3047,20 +3095,31 @@ const _CheckboxListField = class _CheckboxListField extends BaseOptionsField {
3047
3095
  render(props) {
3048
3096
  return /* @__PURE__ */ jsx(CheckboxListInput, { field: this, ...props });
3049
3097
  }
3098
+ isSerializedValueValid(value) {
3099
+ return isStringArray(value);
3100
+ }
3101
+ isValueValid(value) {
3102
+ return isStringArray(value);
3103
+ }
3104
+ blankValue() {
3105
+ return [];
3106
+ }
3107
+ areValuesEqual(value1, value2) {
3108
+ return value1.every((v) => value2.includes(v)) && value2.every((v) => value1.includes(v));
3109
+ }
3050
3110
  };
3051
3111
  __publicField(_CheckboxListField, "fieldTypeName", "Checkbox list");
3052
3112
  __publicField(_CheckboxListField, "fieldTypeDescription", "Allows the user to select a multiple options from a list.");
3053
3113
  let CheckboxListField = _CheckboxListField;
3054
3114
  const DateInput = memo((props) => {
3055
3115
  const [{ inputId, labelId, size, severity, showInputOnly, field, helpText, label, fieldProps }, rest] = useFormikInput(props);
3056
- const { name, onChange, onBlur } = fieldProps;
3116
+ const { value, name, onChange, onBlur } = fieldProps;
3057
3117
  const [popoverOpen, setPopoverOpen] = useState(false);
3058
3118
  const computedHelpText = showInputOnly ? null : helpText;
3059
3119
  const computedLabel = showInputOnly ? "" : label;
3060
- const value = fieldProps.value ? new Date(fieldProps.value) : void 0;
3061
3120
  const handleValueChange = useCallback(
3062
3121
  (date) => {
3063
- onChange(date);
3122
+ onChange(date ?? null);
3064
3123
  setPopoverOpen(false);
3065
3124
  },
3066
3125
  [onChange]
@@ -3109,7 +3168,7 @@ const DateInput = memo((props) => {
3109
3168
  required: false,
3110
3169
  mode: "single",
3111
3170
  variant: "solid",
3112
- selected: value,
3171
+ selected: value ?? void 0,
3113
3172
  onSelect: handleValueChange
3114
3173
  }
3115
3174
  ) })
@@ -3143,34 +3202,41 @@ const _DateField = class _DateField extends BaseField {
3143
3202
  super.setOptions(options);
3144
3203
  }
3145
3204
  serializeValue(value) {
3146
- return value.toISOString();
3205
+ return value ? value.toISOString() : null;
3147
3206
  }
3148
3207
  deserializeValue(value) {
3149
- return new Date(value);
3150
- }
3151
- isEqual(value1, value2) {
3152
- if (value1 === void 0 && value2 === void 0) return true;
3153
- if (!value1 || !value2) return false;
3154
- return value1.getTime() === value2.getTime();
3208
+ return value ? new Date(value) : null;
3155
3209
  }
3156
3210
  render(props) {
3157
3211
  return /* @__PURE__ */ jsx(DateInput, { field: this, ...props });
3158
3212
  }
3213
+ isSerializedValueValid(value) {
3214
+ return typeof value === "string" || value === null;
3215
+ }
3216
+ isValueValid(value) {
3217
+ return value instanceof Date || value === null;
3218
+ }
3219
+ blankValue() {
3220
+ return null;
3221
+ }
3222
+ areValuesEqual(value1, value2) {
3223
+ if (!value1 && !value2) return true;
3224
+ if (!value1 || !value2) return false;
3225
+ return value1.getTime() === value2.getTime();
3226
+ }
3159
3227
  };
3160
3228
  __publicField(_DateField, "fieldTypeName", "Date");
3161
3229
  __publicField(_DateField, "fieldTypeDescription", "Allows specifying a date.");
3162
3230
  let DateField = _DateField;
3163
3231
  const MultiSelectInput = memo((props) => {
3164
3232
  const [{ inputId, labelId, size, severity, showInputOnly, field, helpText, label, fieldProps }, rest] = useFormikInput(props);
3165
- const { name, onChange, onBlur } = fieldProps;
3233
+ const { value, name, onChange, onBlur } = fieldProps;
3166
3234
  const computedHelpText = showInputOnly ? null : helpText;
3167
3235
  const computedLabel = showInputOnly ? "" : label;
3168
- const value = fieldProps.value ?? EMPTY_ARRAY;
3169
3236
  const handleChange = useCallback(
3170
3237
  (value2) => {
3171
- const newValue = value2.length > 0 ? value2 : void 0;
3172
- onChange(newValue);
3173
- onBlur(newValue);
3238
+ onChange(value2);
3239
+ onBlur(value2);
3174
3240
  },
3175
3241
  [onBlur, onChange]
3176
3242
  );
@@ -3237,14 +3303,6 @@ const _MultiSelectField = class _MultiSelectField extends BaseOptionsField {
3237
3303
  __publicField(this, "placeholder");
3238
3304
  this.placeholder = placeholder;
3239
3305
  }
3240
- isBlank(value) {
3241
- return super.isBlank(value) || (value == null ? void 0 : value.length) === 0;
3242
- }
3243
- isEqual(value1, value2) {
3244
- if (value1 === void 0 && value2 === void 0) return true;
3245
- if (value1 === void 0 || value2 === void 0) return false;
3246
- return value1.every((v) => value2.includes(v)) && value2.every((v) => value1.includes(v));
3247
- }
3248
3306
  serialize() {
3249
3307
  return super.serialize();
3250
3308
  }
@@ -3277,6 +3335,18 @@ const _MultiSelectField = class _MultiSelectField extends BaseOptionsField {
3277
3335
  render(props) {
3278
3336
  return /* @__PURE__ */ jsx(MultiSelectInput, { field: this, ...props });
3279
3337
  }
3338
+ isSerializedValueValid(value) {
3339
+ return isStringArray(value);
3340
+ }
3341
+ isValueValid(value) {
3342
+ return isStringArray(value);
3343
+ }
3344
+ blankValue() {
3345
+ return [];
3346
+ }
3347
+ areValuesEqual(value1, value2) {
3348
+ return value1.every((v) => value2.includes(v)) && value2.every((v) => value1.includes(v));
3349
+ }
3280
3350
  };
3281
3351
  __publicField(_MultiSelectField, "fieldTypeName", "Multi-select");
3282
3352
  __publicField(_MultiSelectField, "fieldTypeDescription", "Allows the user to select a multiple options from a list of options.");
@@ -3288,8 +3358,8 @@ const SelectInput = memo((props) => {
3288
3358
  const computedLabel = showInputOnly ? "" : label;
3289
3359
  const handleChange = useCallback(
3290
3360
  (value2) => {
3291
- onChange(value2);
3292
- onBlur(value2);
3361
+ onChange(value2 ?? null);
3362
+ onBlur(value2 ?? null);
3293
3363
  },
3294
3364
  [onBlur, onChange]
3295
3365
  );
@@ -3372,6 +3442,18 @@ const _SelectField = class _SelectField extends BaseOptionsField {
3372
3442
  render(props) {
3373
3443
  return /* @__PURE__ */ jsx(SelectInput, { field: this, ...props });
3374
3444
  }
3445
+ isSerializedValueValid(value) {
3446
+ return typeof value === "string" || value === null;
3447
+ }
3448
+ isValueValid(value) {
3449
+ return typeof value === "string" || value === null;
3450
+ }
3451
+ blankValue() {
3452
+ return null;
3453
+ }
3454
+ areValuesEqual(value1, value2) {
3455
+ return value1 === value2;
3456
+ }
3375
3457
  };
3376
3458
  __publicField(_SelectField, "fieldTypeName", "Dropdown");
3377
3459
  __publicField(_SelectField, "fieldTypeDescription", "Allows the user to select a single option from a list of options.");
@@ -3384,16 +3466,16 @@ const OTPInput = memo((props) => {
3384
3466
  const { name, onBlur, onChange, value } = fieldProps;
3385
3467
  const computedHelpText = showInputOnly ? null : helpText;
3386
3468
  const computedLabel = showInputOnly ? "" : label;
3387
- const [internalValue, setInternalValue] = useState();
3469
+ const [internalValue, setInternalValue] = useState("");
3388
3470
  const inputUuids = useMemo(() => Array.from({ length: field.length }, () => v4()), [field.length]);
3389
3471
  useEffect(() => {
3390
3472
  setInternalValue(value);
3391
3473
  }, [value]);
3392
3474
  const handleChange = useCallback(
3393
3475
  (value2) => {
3394
- setInternalValue(value2 || void 0);
3476
+ setInternalValue(value2);
3395
3477
  if (touched || !field.onlyValidateAfterTouched) {
3396
- helpers.setError(field.getError(value2 || void 0));
3478
+ helpers.setError(field.getError(value2));
3397
3479
  }
3398
3480
  },
3399
3481
  [field, helpers, touched]
@@ -3423,7 +3505,7 @@ const OTPInput = memo((props) => {
3423
3505
  {
3424
3506
  id: inputId,
3425
3507
  name,
3426
- value: internalValue ?? "",
3508
+ value: internalValue,
3427
3509
  onValueChange: handleChange,
3428
3510
  validationType: field.validationType,
3429
3511
  form: formId2,
@@ -3506,7 +3588,7 @@ const _OTPField = class _OTPField extends BaseField {
3506
3588
  const validators = super.getFieldValidators();
3507
3589
  const length = this.length;
3508
3590
  validators.push((value) => {
3509
- if (typeof value === "string" && (value.length < length || value.length > length)) {
3591
+ if (!this.isValueBlank(value) && (value.length < length || value.length > length)) {
3510
3592
  return `Must be ${length} characters.`;
3511
3593
  }
3512
3594
  });
@@ -3550,6 +3632,18 @@ const _OTPField = class _OTPField extends BaseField {
3550
3632
  render(props) {
3551
3633
  return /* @__PURE__ */ jsx(OTPInput, { field: this, ...props });
3552
3634
  }
3635
+ isSerializedValueValid(value) {
3636
+ return typeof value === "string";
3637
+ }
3638
+ isValueValid(value) {
3639
+ return typeof value === "string";
3640
+ }
3641
+ blankValue() {
3642
+ return "";
3643
+ }
3644
+ areValuesEqual(value1, value2) {
3645
+ return value1 === value2;
3646
+ }
3553
3647
  };
3554
3648
  __publicField(_OTPField, "fieldTypeName", "OTP");
3555
3649
  __publicField(_OTPField, "fieldTypeDescription", "Allows specifying a number within a given range.");
@@ -3561,14 +3655,14 @@ const RadioInput = memo((props) => {
3561
3655
  const computedLabel = showInputOnly ? "" : label;
3562
3656
  const handleChange = useCallback(
3563
3657
  (value2) => {
3564
- onChange(value2 ?? void 0);
3565
- onBlur(value2 ?? void 0);
3658
+ onChange(value2);
3659
+ onBlur(value2);
3566
3660
  },
3567
3661
  [onBlur, onChange]
3568
3662
  );
3569
3663
  const handleClear = useCallback(() => {
3570
- onChange(void 0);
3571
- onBlur(void 0);
3664
+ onChange(null);
3665
+ onBlur(null);
3572
3666
  }, [onBlur, onChange]);
3573
3667
  return /* @__PURE__ */ jsx(InputWithLabelAndHelpText, { helpText: computedHelpText, severity, children: /* @__PURE__ */ jsx(
3574
3668
  InputWithLabel,
@@ -3651,6 +3745,18 @@ const _RadioField = class _RadioField extends BaseOptionsField {
3651
3745
  render(props) {
3652
3746
  return /* @__PURE__ */ jsx(RadioInput, { field: this, ...props });
3653
3747
  }
3748
+ isSerializedValueValid(value) {
3749
+ return typeof value === "string" || value === "null";
3750
+ }
3751
+ isValueValid(value) {
3752
+ return typeof value === "string" || value === "null";
3753
+ }
3754
+ blankValue() {
3755
+ return null;
3756
+ }
3757
+ areValuesEqual(value1, value2) {
3758
+ return value1 === value2;
3759
+ }
3654
3760
  };
3655
3761
  __publicField(_RadioField, "fieldTypeName", "Option list");
3656
3762
  __publicField(_RadioField, "fieldTypeDescription", "Allows the user to select a single option from a list of options.");
@@ -31751,15 +31857,16 @@ const ScanInput = memo((props) => {
31751
31857
  const [showScanner, setShowScanner] = useState(false);
31752
31858
  const computedHelpText = showInputOnly ? null : helpText;
31753
31859
  const computedLabel = showInputOnly ? "" : label;
31754
- const [internalValue, setInternalValue] = useState(void 0);
31860
+ const [internalValue, setInternalValue] = useState("");
31755
31861
  useEffect(() => {
31756
31862
  setInternalValue(value);
31757
31863
  }, [value]);
31758
31864
  const handleChange = useCallback(
31759
31865
  (e) => {
31760
- setInternalValue(e.target.value || void 0);
31866
+ const value2 = e.target.value;
31867
+ setInternalValue(value2);
31761
31868
  if (touched || !field.onlyValidateAfterTouched) {
31762
- helpers.setError(field.getError(e.target.value));
31869
+ helpers.setError(field.getError(value2));
31763
31870
  }
31764
31871
  },
31765
31872
  [field, helpers, touched]
@@ -31804,7 +31911,7 @@ const ScanInput = memo((props) => {
31804
31911
  size: "sm",
31805
31912
  id: inputId,
31806
31913
  name,
31807
- value: internalValue ?? "",
31914
+ value: internalValue,
31808
31915
  placeholder: "Enter a qr or barcode",
31809
31916
  onChange: handleChange,
31810
31917
  onBlur: handleBlur,
@@ -31909,6 +32016,18 @@ const _ScanField = class _ScanField extends BaseField {
31909
32016
  render(props) {
31910
32017
  return /* @__PURE__ */ jsx(ScanInput, { ...props, field: this });
31911
32018
  }
32019
+ isSerializedValueValid(value) {
32020
+ return typeof value === "string";
32021
+ }
32022
+ isValueValid(value) {
32023
+ return typeof value === "string";
32024
+ }
32025
+ blankValue() {
32026
+ return "";
32027
+ }
32028
+ areValuesEqual(value1, value2) {
32029
+ return value1 === value2;
32030
+ }
31912
32031
  };
31913
32032
  __publicField(_ScanField, "fieldTypeName", "Scan");
31914
32033
  __publicField(_ScanField, "fieldTypeDescription", "Used for scanning/reading QR and barcodes.");
@@ -31922,15 +32041,16 @@ const StringInput = memo((props) => {
31922
32041
  const { name, onBlur, onChange, value } = fieldProps;
31923
32042
  const computedHelpText = showInputOnly ? null : helpText;
31924
32043
  const computedLabel = showInputOnly ? "" : label;
31925
- const [internalValue, setInternalValue] = useState(void 0);
32044
+ const [internalValue, setInternalValue] = useState("");
31926
32045
  useEffect(() => {
31927
32046
  setInternalValue(value);
31928
32047
  }, [value]);
31929
32048
  const handleChange = useCallback(
31930
32049
  (e) => {
31931
- setInternalValue(e.target.value || void 0);
32050
+ const value2 = e.target.value;
32051
+ setInternalValue(value2);
31932
32052
  if (touched || !field.onlyValidateAfterTouched) {
31933
- helpers.setError(field.getError(e.target.value));
32053
+ helpers.setError(field.getError(value2));
31934
32054
  }
31935
32055
  },
31936
32056
  [field, helpers, touched]
@@ -31954,7 +32074,7 @@ const StringInput = memo((props) => {
31954
32074
  id: inputId,
31955
32075
  className: "truncate",
31956
32076
  name,
31957
- value: internalValue ?? "",
32077
+ value: internalValue,
31958
32078
  type: "text",
31959
32079
  placeholder: field.placeholder,
31960
32080
  onChange: handleChange,
@@ -32014,15 +32134,16 @@ const TextInput = memo((props) => {
32014
32134
  const { name, onBlur, onChange, value } = fieldProps;
32015
32135
  const computedHelpText = showInputOnly ? null : helpText;
32016
32136
  const computedLabel = showInputOnly ? "" : label;
32017
- const [internalValue, setInternalValue] = useState(void 0);
32137
+ const [internalValue, setInternalValue] = useState("");
32018
32138
  useEffect(() => {
32019
32139
  setInternalValue(value);
32020
32140
  }, [value]);
32021
32141
  const handleChange = useCallback(
32022
32142
  (e) => {
32023
- setInternalValue(e.target.value || void 0);
32143
+ const value2 = e.target.value;
32144
+ setInternalValue(value2);
32024
32145
  if (touched || !field.onlyValidateAfterTouched) {
32025
- helpers.setError(field.getError(e.target.value));
32146
+ helpers.setError(field.getError(value2));
32026
32147
  }
32027
32148
  },
32028
32149
  [field, helpers, touched]
@@ -32044,7 +32165,7 @@ const TextInput = memo((props) => {
32044
32165
  TextArea,
32045
32166
  {
32046
32167
  id: inputId,
32047
- value: internalValue ?? "",
32168
+ value: internalValue,
32048
32169
  name,
32049
32170
  onChange: handleChange,
32050
32171
  onBlur: handleBlur,
@@ -32152,11 +32273,25 @@ const convertBytesToLargestUnit = (bytes) => {
32152
32273
  });
32153
32274
  return formatter.format(sizeInUnit);
32154
32275
  };
32276
+ function areFilesEqual(file1, file2) {
32277
+ return file1.name === file2.name && file1.size === file2.size && file1.type === file2.type;
32278
+ }
32279
+ function seprateFilesFromPromises(filesOrPromises) {
32280
+ const files = [];
32281
+ const promises = [];
32282
+ for (const fileOrPromise of filesOrPromises) {
32283
+ if (fileOrPromise instanceof Promise) {
32284
+ promises.push(fileOrPromise);
32285
+ } else {
32286
+ files.push(fileOrPromise);
32287
+ }
32288
+ }
32289
+ return [files, promises];
32290
+ }
32155
32291
  const UploadInput = memo((props) => {
32156
32292
  var _a2;
32157
32293
  const [{ inputId, labelId, label, size, severity, helpText, showInputOnly, field, fieldProps }, rest] = useFormikInput(props);
32158
- const { name, onChange, onBlur } = fieldProps;
32159
- const value = fieldProps.value ?? EMPTY_ARRAY;
32294
+ const { value, name, onChange, onBlur } = fieldProps;
32160
32295
  const input = useRef(null);
32161
32296
  const updatedHelpText = useMemo(() => {
32162
32297
  if (showInputOnly) return null;
@@ -32186,7 +32321,7 @@ const UploadInput = memo((props) => {
32186
32321
  (index) => {
32187
32322
  const files = [...value];
32188
32323
  files.splice(index, 1);
32189
- onChange(files.length > 0 ? files : void 0);
32324
+ onChange(files);
32190
32325
  },
32191
32326
  [value, onChange]
32192
32327
  );
@@ -32196,7 +32331,7 @@ const UploadInput = memo((props) => {
32196
32331
  input.current.addEventListener(
32197
32332
  "cancel",
32198
32333
  () => {
32199
- onBlur(void 0);
32334
+ onBlur([...value]);
32200
32335
  },
32201
32336
  {
32202
32337
  signal: abortController.signal
@@ -32355,21 +32490,6 @@ const DisplayFile = memo((props) => {
32355
32490
  ) : /* @__PURE__ */ jsx(FileCard, { file: resolvedFile, error: error ?? void 0, rightSlot: rightSlotContent });
32356
32491
  });
32357
32492
  DisplayFile.displayName = "DisplayFile";
32358
- function areFilesEqual(file1, file2) {
32359
- return file1.name === file2.name && file1.size === file2.size && file1.type === file2.type;
32360
- }
32361
- function seprateFilesFromPromises(filesOrPromises) {
32362
- const files = [];
32363
- const promises = [];
32364
- for (const fileOrPromise of filesOrPromises) {
32365
- if (fileOrPromise instanceof Promise) {
32366
- promises.push(fileOrPromise);
32367
- } else {
32368
- files.push(fileOrPromise);
32369
- }
32370
- }
32371
- return [files, promises];
32372
- }
32373
32493
  const _UploadField = class _UploadField extends BaseField {
32374
32494
  constructor(options) {
32375
32495
  const { extensions, maximum_files, maximum_size, ...base } = options;
@@ -32383,20 +32503,6 @@ const _UploadField = class _UploadField extends BaseField {
32383
32503
  this.maxFiles = Math.max(typeof maximum_files === "number" ? maximum_files : 1, 1);
32384
32504
  this.extensions = extensions;
32385
32505
  }
32386
- isBlank(value) {
32387
- return super.isBlank(value) || (value == null ? void 0 : value.length) === 0;
32388
- }
32389
- isEqual(value1, value2) {
32390
- if (value1 === void 0 && value2 === void 0) return true;
32391
- if (value1 === void 0 || value2 === void 0) return false;
32392
- const [files1, promises1] = seprateFilesFromPromises(value1);
32393
- const [files2, promises2] = seprateFilesFromPromises(value2);
32394
- if (!files1.every((file1) => files2.some((file2) => areFilesEqual(file1, file2)))) return false;
32395
- if (!files2.every((file2) => files1.some((file1) => areFilesEqual(file1, file2)))) return false;
32396
- if (!promises1.every((promise1) => promises2.some((promise2) => promise1 === promise2))) return false;
32397
- if (!promises2.every((promise2) => promises1.some((promise1) => promise1 === promise2))) return false;
32398
- return true;
32399
- }
32400
32506
  static getFieldCreationSchema(parentPath = "") {
32401
32507
  const path = parentPath && `${parentPath}.`;
32402
32508
  return [
@@ -32515,6 +32621,24 @@ const _UploadField = class _UploadField extends BaseField {
32515
32621
  render(props) {
32516
32622
  return /* @__PURE__ */ jsx(UploadInput, { field: this, ...props });
32517
32623
  }
32624
+ isSerializedValueValid(value) {
32625
+ return Array.isArray(value) && value.length === 0;
32626
+ }
32627
+ isValueValid(value) {
32628
+ return isFilePromiseArray(value);
32629
+ }
32630
+ areValuesEqual(value1, value2) {
32631
+ const [files1, promises1] = seprateFilesFromPromises(value1);
32632
+ const [files2, promises2] = seprateFilesFromPromises(value2);
32633
+ if (!files1.every((file1) => files2.some((file2) => areFilesEqual(file1, file2)))) return false;
32634
+ if (!files2.every((file2) => files1.some((file1) => areFilesEqual(file1, file2)))) return false;
32635
+ if (!promises1.every((promise1) => promises2.some((promise2) => promise1 === promise2))) return false;
32636
+ if (!promises2.every((promise2) => promises1.some((promise1) => promise1 === promise2))) return false;
32637
+ return true;
32638
+ }
32639
+ blankValue() {
32640
+ return [];
32641
+ }
32518
32642
  };
32519
32643
  __publicField(_UploadField, "fieldTypeName", "Upload");
32520
32644
  __publicField(_UploadField, "fieldTypeDescription", "Allows a file to be uploaded.");
@@ -32553,60 +32677,6 @@ const fieldIcons = {
32553
32677
  const maxFileSizeMB = 50;
32554
32678
  const maxFileSizeKB = maxFileSizeMB * 1e3;
32555
32679
  const maxFileSizeB = maxFileSizeKB * 1e3;
32556
- class BaseCondition extends Observable {
32557
- constructor(config) {
32558
- const { id, field, conditionValue, conditionModifier } = config;
32559
- super();
32560
- __publicField(this, "id");
32561
- __publicField(this, "conditionValue");
32562
- __publicField(this, "conditionModifier");
32563
- __publicField(this, "field");
32564
- __publicField(this, "getConditionValue", () => {
32565
- return this.conditionValue;
32566
- });
32567
- __publicField(this, "setConditionValue", (conditionValue) => {
32568
- const modifier = this.modifiers[this.conditionModifier];
32569
- if (conditionValue !== void 0 && !modifier.isConditionValueValid(conditionValue)) return;
32570
- this.conditionValue = conditionValue;
32571
- this.notify(this);
32572
- });
32573
- __publicField(this, "getConditionModifier", () => {
32574
- return this.conditionModifier;
32575
- });
32576
- __publicField(this, "setConditionModifier", (modifier) => {
32577
- const foundModifier = this.modifiers[modifier];
32578
- const filterValue = this.getConditionValue();
32579
- if (filterValue && !foundModifier.isConditionValueValid(filterValue)) {
32580
- this.conditionValue = void 0;
32581
- }
32582
- this.conditionModifier = modifier;
32583
- this.notify(this);
32584
- });
32585
- __publicField(this, "getConditionModifiers", () => {
32586
- return Object.entries(this.modifiers);
32587
- });
32588
- __publicField(this, "apply", (value) => {
32589
- const modifier = this.modifiers[this.conditionModifier];
32590
- const conditionValue = this.getConditionValue();
32591
- if (conditionValue === void 0 || !modifier.isConditionValueValid(conditionValue)) return true;
32592
- if (!modifier.isValueValid(value)) return false;
32593
- return modifier.modifier.modifierFn(value, conditionValue);
32594
- });
32595
- this.id = id;
32596
- this.field = field;
32597
- this.conditionValue = conditionValue;
32598
- this.conditionModifier = conditionModifier;
32599
- }
32600
- serialize() {
32601
- return {
32602
- id: this.id,
32603
- type: this.field.type,
32604
- fieldId: this.field.identifier,
32605
- conditionValue: this.modifiers[this.conditionModifier].modifier.serialize(this.conditionValue),
32606
- conditionModifier: this.conditionModifier
32607
- };
32608
- }
32609
- }
32610
32680
  class ConditionModifier {
32611
32681
  constructor(config) {
32612
32682
  __publicField(this, "id");
@@ -32913,6 +32983,60 @@ const StringArrayNotEqualsConditionModifier = ConditionModifier.create({
32913
32983
  deserialize: (filterValue) => filterValue
32914
32984
  });
32915
32985
  const createConditionModifierConfig = (conifg) => conifg;
32986
+ class BaseCondition extends Observable {
32987
+ constructor(config) {
32988
+ const { id, field, conditionValue, conditionModifier } = config;
32989
+ super();
32990
+ __publicField(this, "id");
32991
+ __publicField(this, "conditionValue");
32992
+ __publicField(this, "conditionModifier");
32993
+ __publicField(this, "field");
32994
+ __publicField(this, "getConditionValue", () => {
32995
+ return this.conditionValue;
32996
+ });
32997
+ __publicField(this, "setConditionValue", (conditionValue) => {
32998
+ const modifier = this.modifiers[this.conditionModifier];
32999
+ if (conditionValue !== void 0 && !modifier.isConditionValueValid(conditionValue)) return;
33000
+ this.conditionValue = conditionValue;
33001
+ this.notify(this);
33002
+ });
33003
+ __publicField(this, "getConditionModifier", () => {
33004
+ return this.conditionModifier;
33005
+ });
33006
+ __publicField(this, "setConditionModifier", (modifier) => {
33007
+ const foundModifier = this.modifiers[modifier];
33008
+ const filterValue = this.getConditionValue();
33009
+ if (filterValue && !foundModifier.isConditionValueValid(filterValue)) {
33010
+ this.conditionValue = void 0;
33011
+ }
33012
+ this.conditionModifier = modifier;
33013
+ this.notify(this);
33014
+ });
33015
+ __publicField(this, "getConditionModifiers", () => {
33016
+ return Object.entries(this.modifiers);
33017
+ });
33018
+ __publicField(this, "apply", (value) => {
33019
+ const modifier = this.modifiers[this.conditionModifier];
33020
+ const conditionValue = this.getConditionValue();
33021
+ if (conditionValue === void 0 || !modifier.isConditionValueValid(conditionValue)) return true;
33022
+ if (!modifier.isValueValid(value)) return false;
33023
+ return modifier.modifier.modifierFn(value, conditionValue);
33024
+ });
33025
+ this.id = id;
33026
+ this.field = field;
33027
+ this.conditionValue = conditionValue;
33028
+ this.conditionModifier = conditionModifier;
33029
+ }
33030
+ serialize() {
33031
+ return {
33032
+ id: this.id,
33033
+ type: this.field.type,
33034
+ fieldId: this.field.identifier,
33035
+ conditionValue: this.modifiers[this.conditionModifier].modifier.serialize(this.conditionValue),
33036
+ conditionModifier: this.conditionModifier
33037
+ };
33038
+ }
33039
+ }
32916
33040
  const formId = "form-builder";
32917
33041
  const UNLABELLED_FIELD_LABEL = "Unlabelled";
32918
33042
  const UNLABELLED_SECTION_LABEL = "Unlabelled";
@@ -33653,19 +33777,19 @@ const NumberFieldConditionCell = (props) => {
33653
33777
  const modifiers$7 = {
33654
33778
  equals: createConditionModifierConfig({
33655
33779
  modifier: NumberEqualsConditionModifier,
33656
- isValueValid: (_value) => true,
33780
+ isValueValid: (value) => typeof value === "number",
33657
33781
  isConditionValueValid: (conditionValue) => typeof conditionValue === "number",
33658
33782
  isSerializedValueValid: (conditionValue) => typeof conditionValue === "number"
33659
33783
  }),
33660
33784
  notEquals: createConditionModifierConfig({
33661
33785
  modifier: NumberNotEqualsConditionModifier,
33662
- isValueValid: (_value) => true,
33786
+ isValueValid: (value) => typeof value === "number",
33663
33787
  isConditionValueValid: (conditionValue) => typeof conditionValue === "number",
33664
33788
  isSerializedValueValid: (conditionValue) => typeof conditionValue === "number"
33665
33789
  }),
33666
33790
  lessThan: createConditionModifierConfig({
33667
33791
  modifier: NumberLessThanConditionModifier,
33668
- isValueValid: (_value) => true,
33792
+ isValueValid: (value) => typeof value === "number",
33669
33793
  isConditionValueValid: (conditionValue) => typeof conditionValue === "number",
33670
33794
  isSerializedValueValid: (conditionValue) => typeof conditionValue === "number"
33671
33795
  }),
@@ -33876,25 +34000,25 @@ const RadioFieldConditionCell = (props) => {
33876
34000
  const modifiers$5 = {
33877
34001
  equals: createConditionModifierConfig({
33878
34002
  modifier: StringEqualsConditionModifier,
33879
- isValueValid: (_value) => true,
34003
+ isValueValid: (value) => typeof value === "string",
33880
34004
  isConditionValueValid: (conditionValue) => !Array.isArray(conditionValue),
33881
34005
  isSerializedValueValid: (serializedConditionValue) => !Array.isArray(serializedConditionValue)
33882
34006
  }),
33883
34007
  notEquals: createConditionModifierConfig({
33884
34008
  modifier: StringNotEqualsConditionModifier,
33885
- isValueValid: (_value) => true,
34009
+ isValueValid: (value) => typeof value === "string",
33886
34010
  isConditionValueValid: (conditionValue) => !Array.isArray(conditionValue),
33887
34011
  isSerializedValueValid: (serializedConditionValue) => !Array.isArray(serializedConditionValue)
33888
34012
  }),
33889
34013
  includes: createConditionModifierConfig({
33890
34014
  modifier: StringArrayIncludesConditionModifier,
33891
- isValueValid: (_value) => true,
34015
+ isValueValid: (value) => typeof value === "string",
33892
34016
  isConditionValueValid: (conditionValue) => Array.isArray(conditionValue),
33893
34017
  isSerializedValueValid: (serializedConditionValue) => Array.isArray(serializedConditionValue)
33894
34018
  }),
33895
34019
  excludes: createConditionModifierConfig({
33896
34020
  modifier: StringArrayExcludesConditionModifier,
33897
- isValueValid: (_value) => true,
34021
+ isValueValid: (value) => typeof value === "string",
33898
34022
  isConditionValueValid: (conditionValue) => Array.isArray(conditionValue),
33899
34023
  isSerializedValueValid: (serializedConditionValue) => Array.isArray(serializedConditionValue)
33900
34024
  })
@@ -34075,25 +34199,25 @@ const SelectFieldConditionCell = (props) => {
34075
34199
  const modifiers$3 = {
34076
34200
  equals: createConditionModifierConfig({
34077
34201
  modifier: StringEqualsConditionModifier,
34078
- isValueValid: (_value) => true,
34202
+ isValueValid: (value) => typeof value === "string",
34079
34203
  isConditionValueValid: (conditionValue) => !Array.isArray(conditionValue),
34080
34204
  isSerializedValueValid: (serializedConditionValue) => !Array.isArray(serializedConditionValue)
34081
34205
  }),
34082
34206
  notEquals: createConditionModifierConfig({
34083
34207
  modifier: StringNotEqualsConditionModifier,
34084
- isValueValid: (_value) => true,
34208
+ isValueValid: (value) => typeof value === "string",
34085
34209
  isConditionValueValid: (conditionValue) => !Array.isArray(conditionValue),
34086
34210
  isSerializedValueValid: (serializedConditionValue) => !Array.isArray(serializedConditionValue)
34087
34211
  }),
34088
34212
  includes: createConditionModifierConfig({
34089
34213
  modifier: StringArrayIncludesConditionModifier,
34090
- isValueValid: (_value) => true,
34214
+ isValueValid: (value) => typeof value === "string",
34091
34215
  isConditionValueValid: (conditionValue) => Array.isArray(conditionValue),
34092
34216
  isSerializedValueValid: (serializedConditionValue) => Array.isArray(serializedConditionValue)
34093
34217
  }),
34094
34218
  excludes: createConditionModifierConfig({
34095
34219
  modifier: StringArrayExcludesConditionModifier,
34096
- isValueValid: (_value) => true,
34220
+ isValueValid: (value) => typeof value === "string",
34097
34221
  isConditionValueValid: (conditionValue) => Array.isArray(conditionValue),
34098
34222
  isSerializedValueValid: (serializedConditionValue) => Array.isArray(serializedConditionValue)
34099
34223
  })
@@ -34416,15 +34540,15 @@ const deserializeField = (serializedField) => {
34416
34540
  return OTPField.deserialize(serializedField);
34417
34541
  }
34418
34542
  };
34543
+ function deserializeFields(fields) {
34544
+ return fields.map(deserialize);
34545
+ }
34419
34546
  const deserialize = (serialized) => {
34420
34547
  if (serialized.type === "section") {
34421
34548
  return FieldSection.deserialize(serialized);
34422
34549
  }
34423
34550
  return deserializeField(serialized);
34424
34551
  };
34425
- function deserializeFields(fields) {
34426
- return fields.map(deserialize);
34427
- }
34428
34552
  function deserializeOnlyFields(fields) {
34429
34553
  return fields.map(deserializeField);
34430
34554
  }
@@ -34450,33 +34574,44 @@ function getFieldsMapping(fields) {
34450
34574
  return result;
34451
34575
  }
34452
34576
  function serializeFieldValues(fields, values) {
34577
+ const cleanValues = cleanFieldValues(fields, values);
34453
34578
  const ret = {};
34454
34579
  for (const field of fields) {
34455
- const value = values[field.identifier];
34580
+ const value = cleanValues[field.identifier];
34581
+ if (!field.isValueValid(value)) continue;
34456
34582
  ret[field.identifier] = field.serializeValue(value);
34457
34583
  }
34458
34584
  return ret;
34459
34585
  }
34460
34586
  function deserializeFieldValues(fields, values) {
34587
+ const cleanValues = cleanSerializedFieldValues(fields, values);
34461
34588
  const ret = {};
34462
34589
  for (const field of fields) {
34463
- const value = values[field.identifier];
34590
+ const value = cleanValues[field.identifier];
34591
+ if (!field.isSerializedValueValid(value)) continue;
34464
34592
  ret[field.identifier] = field.deserializeValue(value);
34465
34593
  }
34466
34594
  return ret;
34467
34595
  }
34596
+ function cleanFieldValues(fields, values) {
34597
+ const ret = {};
34598
+ for (const field of fields) {
34599
+ const value = values[field.identifier];
34600
+ if (!field.isSerializedValueValid(value)) continue;
34601
+ ret[field.identifier] = value;
34602
+ }
34603
+ return ret;
34604
+ }
34605
+ function cleanSerializedFieldValues(fields, values) {
34606
+ const ret = {};
34607
+ for (const field of fields) {
34608
+ const value = values[field.identifier];
34609
+ if (!field.isSerializedValueValid(value)) continue;
34610
+ ret[field.identifier] = value;
34611
+ }
34612
+ return ret;
34613
+ }
34468
34614
  const RendererContext = createContext({});
34469
- const useFieldInput = (field, props) => {
34470
- return useMemo(() => {
34471
- if (!props || !field) return null;
34472
- return field.render(props);
34473
- }, [field, props]);
34474
- };
34475
- const useFieldInputs = (fields, props) => {
34476
- return /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-4", children: fields.map((field) => {
34477
- return /* @__PURE__ */ jsx(Fragment$1, { children: field.render(props) }, field.identifier);
34478
- }) });
34479
- };
34480
34615
  const FieldSectionLayout = memo((props) => {
34481
34616
  const { fieldSection: section, ...rest } = props;
34482
34617
  const { label, description } = section;
@@ -34583,13 +34718,12 @@ const _FieldSection = class _FieldSection extends BaseFormElement {
34583
34718
  this.conditions = conditions || this.conditions;
34584
34719
  super.setOptions(base);
34585
34720
  }
34586
- getErrors(allValues) {
34721
+ getErrors(values) {
34587
34722
  const errors = {};
34588
34723
  for (const field of this.fields) {
34589
- const id = field.identifier;
34590
- const error = field.getError(get(allValues, id));
34724
+ const error = field.getError(values[field.identifier]);
34591
34725
  if (error) {
34592
- set(errors, field.identifier, error);
34726
+ errors[field.identifier] = error;
34593
34727
  }
34594
34728
  }
34595
34729
  return errors;
@@ -34744,124 +34878,6 @@ class FieldSchema extends Observable {
34744
34878
  }
34745
34879
  const FieldSchemaContext = createContext(new FieldSchema([]));
34746
34880
  const FormBuilderContext = createContext({});
34747
- const hasKeys = (errors) => {
34748
- return Object.keys(errors).length > 0;
34749
- };
34750
- const validateFields = (fields, values) => {
34751
- const errors = {};
34752
- const sectionElements = fields.filter((f) => f instanceof FieldSection);
34753
- for (const field of fields) {
34754
- if (field instanceof FieldSection) {
34755
- const conditionalSections = sectionElements.filter((section) => field.identifier in section.conditions);
34756
- const conditionMet = conditionalSections.length > 0 ? conditionalSections.some(
34757
- (conditionalSection) => applyConditions(conditionalSection.getConditions(field.identifier), values)
34758
- ) : true;
34759
- if (!conditionMet) continue;
34760
- Object.assign(errors, field.getErrors(values));
34761
- } else {
34762
- if (!(field instanceof BaseField)) {
34763
- throw new Error("Invalid field type");
34764
- }
34765
- const id = field.identifier;
34766
- const error = field.getError(get(values, id));
34767
- if (error) set(errors, id, error);
34768
- }
34769
- }
34770
- if (hasKeys(errors)) return errors;
34771
- };
34772
- const initializeFieldValues = (fields, values) => {
34773
- return fields.reduce((acc, field) => {
34774
- if (field instanceof FieldSection) {
34775
- return { ...acc, ...initializeFieldValues(field.fields, values) };
34776
- }
34777
- const identifier = field.identifier;
34778
- const value = acc[identifier];
34779
- switch (value) {
34780
- case "":
34781
- acc[identifier] = void 0;
34782
- break;
34783
- case []:
34784
- acc[identifier] = void 0;
34785
- break;
34786
- default:
34787
- acc[identifier] = value ?? void 0;
34788
- }
34789
- return acc;
34790
- }, values);
34791
- };
34792
- const changedFieldValues = (fields, values1, values2) => {
34793
- return fields.reduce((acc, field) => {
34794
- if (field instanceof FieldSection) {
34795
- return { ...acc, ...changedFieldValues(field.fields, values1, values2) };
34796
- }
34797
- if (field instanceof BaseField) {
34798
- const identifier = field.identifier;
34799
- const value1 = values1[identifier];
34800
- const value2 = values2[identifier];
34801
- if (!field.isEqual(value1, value2)) {
34802
- acc[identifier] = value2;
34803
- }
34804
- }
34805
- return acc;
34806
- }, {});
34807
- };
34808
- const isArrayOfFiles = (value) => {
34809
- return Array.isArray(value) && value[0] instanceof File;
34810
- };
34811
- const separateFilesFromFieldValues = (values) => {
34812
- const files = {};
34813
- const newValues = {};
34814
- for (const key in values) {
34815
- const value = values[key];
34816
- if (value instanceof File) {
34817
- files[key] = [value];
34818
- } else if (isArrayOfFiles(value)) {
34819
- files[key] = value;
34820
- } else if (value !== void 0) {
34821
- newValues[key] = value;
34822
- }
34823
- }
34824
- return { values: newValues, files };
34825
- };
34826
- const separateFilesFromFields = async (fields) => {
34827
- const images = {};
34828
- const newFields = [];
34829
- for (const section of fields) {
34830
- if (section.type !== "section") {
34831
- throw new Error(`Expected ISerializedField type to be a section. Got ${section.type} instead.`);
34832
- }
34833
- const { fields: sectionFields } = section;
34834
- const newSectionFields = [];
34835
- for (const field of sectionFields) {
34836
- if (field.image) {
34837
- if (field.image instanceof Promise) {
34838
- try {
34839
- images[field.identifier] = await field.image;
34840
- } catch (e) {
34841
- console.error("Failed to get image from promise", e);
34842
- }
34843
- } else {
34844
- images[field.identifier] = field.image;
34845
- }
34846
- delete field.image;
34847
- }
34848
- newSectionFields.push(field);
34849
- }
34850
- newFields.push({ ...section, fields: newSectionFields });
34851
- }
34852
- return { fields: newFields, images };
34853
- };
34854
- async function awaitPromisesFromFieldValues(values) {
34855
- const valuesWithoutFiles = {};
34856
- for (const [key, value] of Object.entries(values)) {
34857
- if (Array.isArray(value) && value.every((item) => item instanceof Promise)) {
34858
- valuesWithoutFiles[key] = await Promise.all(value);
34859
- } else {
34860
- valuesWithoutFiles[key] = value;
34861
- }
34862
- }
34863
- return valuesWithoutFiles;
34864
- }
34865
34881
  const createField = (type) => {
34866
34882
  switch (type) {
34867
34883
  case "text":
@@ -35134,7 +35150,7 @@ const FieldBuilder = memo((props) => {
35134
35150
  const showPopoverInputs = popoverFields.length > 0;
35135
35151
  const popoverHasErrors = popoverFields.some((field2) => {
35136
35152
  const error = get(errors, field2.identifier);
35137
- return error && (typeof error !== "object" || hasKeys(error));
35153
+ return error && (typeof error !== "object" || Object.values(error).length > 0);
35138
35154
  });
35139
35155
  const previewInput = useFieldInput(field, { formId, showInputOnly: false });
35140
35156
  const handleFieldImageClick = useCallback(() => {
@@ -35238,45 +35254,6 @@ const FieldDropdownMenu = memo((props) => {
35238
35254
  ] });
35239
35255
  });
35240
35256
  FieldDropdownMenu.displayName = "IssueDataFilterMenu";
35241
- const FieldSectionConditionalItem = memo((props) => {
35242
- const { sourceFieldSection, targetFieldSection } = props;
35243
- const conditions = useMemo(() => {
35244
- return sourceFieldSection.getConditions(targetFieldSection.identifier);
35245
- }, [sourceFieldSection, targetFieldSection.identifier]);
35246
- const handleDelete = useCallback(() => {
35247
- sourceFieldSection.removeConditional(targetFieldSection.identifier);
35248
- }, [sourceFieldSection, targetFieldSection.identifier]);
35249
- const handleSelectField = useCallback(
35250
- (field) => {
35251
- sourceFieldSection.addCondition(targetFieldSection.identifier, createCondition(field));
35252
- },
35253
- [sourceFieldSection, targetFieldSection.identifier]
35254
- );
35255
- const handleDeleteCondition = useCallback(
35256
- (condition) => {
35257
- sourceFieldSection.removeCondition(targetFieldSection.identifier, condition);
35258
- },
35259
- [sourceFieldSection, targetFieldSection.identifier]
35260
- );
35261
- return /* @__PURE__ */ jsxs(Card, { className: "flex flex-col gap-2", children: [
35262
- /* @__PURE__ */ jsxs(ButtonGroup, { className: "justify-between gap-2 flex", size: "sm", accentColor: "base", variant: "soft", children: [
35263
- /* @__PURE__ */ jsxs(Badge, { accentColor: "base", variant: "soft", size: "sm", children: [
35264
- /* @__PURE__ */ jsx(LuIcon, { icon: fieldIcons.section }),
35265
- sourceFieldSection.label ?? UNLABELLED_SECTION_LABEL
35266
- ] }),
35267
- /* @__PURE__ */ jsx(IconButton, { type: "button", onClick: handleDelete, variant: "ghost", children: /* @__PURE__ */ jsx(LuIcon, { icon: "trash" }) })
35268
- ] }),
35269
- /* @__PURE__ */ jsxs("div", { className: "flex h-max w-full flex-wrap gap-2", children: [
35270
- conditions.map((condition) => {
35271
- return condition.render({
35272
- onRemove: handleDeleteCondition
35273
- });
35274
- }),
35275
- /* @__PURE__ */ jsx(FieldDropdownMenu, { fields: sourceFieldSection.fields, onSelectField: handleSelectField, align: "start", children: /* @__PURE__ */ jsx(IconButton, { type: "button", size: "sm", variant: "soft", accentColor: "base", children: /* @__PURE__ */ jsx(LuIcon, { icon: "plus" }) }) })
35276
- ] })
35277
- ] });
35278
- });
35279
- FieldSectionConditionalItem.displayName = "FieldSectionConditionalItem";
35280
35257
  const FieldSectionDropdownMenu = memo((props) => {
35281
35258
  const { children, variant, size, accentColor, fieldSections, onSelectFieldCondition, ...rest } = props;
35282
35259
  return /* @__PURE__ */ jsxs(Menu.Root, { ...rest, children: [
@@ -35290,169 +35267,6 @@ const FieldSectionDropdownMenu = memo((props) => {
35290
35267
  ] });
35291
35268
  });
35292
35269
  FieldSectionDropdownMenu.displayName = "IssueDataFilterMenu";
35293
- const FieldSectionBuilder = memo((props) => {
35294
- const { fieldSection } = props;
35295
- const fieldSchema = use(FieldSchemaContext);
35296
- const conditionalSections = useMemo(() => {
35297
- return fieldSchema.fields.filter((section) => fieldSection.identifier in section.conditions);
35298
- }, [fieldSchema.fields, fieldSection.identifier]);
35299
- const handleAddConditional = useCallback(
35300
- (conditionalSection) => {
35301
- conditionalSection.addConditional(fieldSection.identifier);
35302
- },
35303
- [fieldSection.identifier]
35304
- );
35305
- const validFieldSections = useMemo(() => {
35306
- const graph = new DirectedGraph({});
35307
- for (const sourceSection of fieldSchema.fields) {
35308
- for (const targetSectionId of Object.keys(sourceSection.conditions)) {
35309
- graph.mergeEdge(sourceSection.identifier, targetSectionId);
35310
- }
35311
- }
35312
- return fieldSchema.fields.filter(
35313
- (sourceSection) => !willCreateCycle(graph, sourceSection.identifier, fieldSection.identifier)
35314
- );
35315
- }, [fieldSchema.fields, fieldSection.identifier]);
35316
- return /* @__PURE__ */ jsxs("div", { className: "flex grow w-full flex-col gap-4", children: [
35317
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 w-full overflow-hidden", children: [
35318
- /* @__PURE__ */ jsx(Input.Root, { variant: "outline", size: "md", children: /* @__PURE__ */ jsx(
35319
- Input.Field,
35320
- {
35321
- placeholder: "Enter a section label (optional)",
35322
- value: fieldSection.label ?? "",
35323
- onChange: (event) => fieldSection.setOptions({ label: event.target.value }),
35324
- maxLength: 200
35325
- }
35326
- ) }),
35327
- /* @__PURE__ */ jsx(
35328
- TextArea,
35329
- {
35330
- className: "field-sizing-content",
35331
- placeholder: "Enter a section description (optional)",
35332
- value: fieldSection.description ?? "",
35333
- onChange: (event) => fieldSection.setOptions({ description: event.target.value }),
35334
- maxLength: 1e3,
35335
- resize: "vertical",
35336
- size: "md"
35337
- }
35338
- )
35339
- ] }),
35340
- /* @__PURE__ */ jsx(Separator, { size: "full" }),
35341
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
35342
- /* @__PURE__ */ jsxs("div", { className: "flex gap-2 justify-between", children: [
35343
- /* @__PURE__ */ jsx(Text, { accentColor: "base", children: "Conditions" }),
35344
- /* @__PURE__ */ jsx(
35345
- FieldSectionDropdownMenu,
35346
- {
35347
- fieldSections: validFieldSections,
35348
- onSelectFieldCondition: handleAddConditional,
35349
- children: /* @__PURE__ */ jsxs(Button, { type: "button", variant: "soft", size: "sm", className: "w-max", children: [
35350
- /* @__PURE__ */ jsx(LuIcon, { icon: "plus" }),
35351
- "Add condition"
35352
- ] })
35353
- }
35354
- )
35355
- ] }),
35356
- conditionalSections.map((section) => /* @__PURE__ */ jsx(
35357
- FieldSectionConditionalItem,
35358
- {
35359
- sourceFieldSection: section,
35360
- targetFieldSection: fieldSection
35361
- },
35362
- section.identifier
35363
- ))
35364
- ] }),
35365
- /* @__PURE__ */ jsx(Separator, { size: "full" })
35366
- ] });
35367
- });
35368
- FieldSectionBuilder.displayName = "FieldSectionBuilder";
35369
- const useFieldTypeItems = (onSelect = () => null) => {
35370
- return useMemo(() => {
35371
- const entries = Object.entries(FieldTypeToClsMapping);
35372
- return entries.map(([type, fieldClass]) => ({
35373
- children: fieldClass.fieldTypeName,
35374
- icon: /* @__PURE__ */ jsx(LuIcon, { icon: fieldIcons[type] }),
35375
- value: type,
35376
- onSelect: () => {
35377
- onSelect(type);
35378
- }
35379
- }));
35380
- }, [onSelect]);
35381
- };
35382
- const FieldSectionWithActions = memo((props) => {
35383
- const { fieldSection, index: sectionIndex } = props;
35384
- const fieldSchema = use(FieldSchemaContext);
35385
- const removeField = useCallback(
35386
- (field) => {
35387
- fieldSection.removeField(field);
35388
- },
35389
- [fieldSection]
35390
- );
35391
- const removeSection = useCallback(() => {
35392
- fieldSchema.removeField(fieldSection);
35393
- }, [fieldSchema, fieldSection]);
35394
- const moveSection = useCallback(
35395
- (direction) => {
35396
- const targetIndex = direction === "up" ? sectionIndex - 1 : sectionIndex + 1;
35397
- fieldSchema.moveField(sectionIndex, targetIndex);
35398
- },
35399
- [fieldSchema, sectionIndex]
35400
- );
35401
- const duplicateSection = useCallback(() => {
35402
- fieldSchema.addField(fieldSection.duplicate(v4()));
35403
- }, [fieldSchema, fieldSection]);
35404
- const handleCreateField = useCallback(
35405
- (type) => {
35406
- fieldSection.addField(createField(type));
35407
- },
35408
- [fieldSection]
35409
- );
35410
- const handleMoveUp = useCallback(() => {
35411
- moveSection("up");
35412
- }, [moveSection]);
35413
- const handleMoveDown = useCallback(() => {
35414
- moveSection("down");
35415
- }, [moveSection]);
35416
- const fieldTypeItems = useFieldTypeItems(handleCreateField);
35417
- return /* @__PURE__ */ jsxs(Card, { variant: "outline", className: "flex items-center justify-between gap-4 w-full", children: [
35418
- /* @__PURE__ */ jsxs("div", { className: "flex grow flex-col gap-4 w-full", children: [
35419
- /* @__PURE__ */ jsx(FieldSectionBuilder, { fieldSection }),
35420
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 w-full", children: [
35421
- /* @__PURE__ */ jsxs("div", { className: "flex gap-2 justify-between", children: [
35422
- /* @__PURE__ */ jsx(Text, { accentColor: "base", size: "md", children: "Fields" }),
35423
- /* @__PURE__ */ jsxs(Menu.Root, { children: [
35424
- /* @__PURE__ */ jsx(Menu.ClickTrigger, { children: /* @__PURE__ */ jsxs(Button, { type: "button", variant: "soft", size: "sm", children: [
35425
- /* @__PURE__ */ jsx(LuIcon, { icon: "plus" }),
35426
- " Add field"
35427
- ] }) }),
35428
- /* @__PURE__ */ jsx(Menu.Content, { children: /* @__PURE__ */ jsx(Menu.Scroll, { children: fieldTypeItems.flat().map((item) => /* @__PURE__ */ jsxs(Menu.Item, { onSelect: item.onSelect, children: [
35429
- item.icon,
35430
- item.children
35431
- ] }, item.value)) }) })
35432
- ] })
35433
- ] }),
35434
- fieldSection.fields.map((child, index) => /* @__PURE__ */ jsx(
35435
- FieldWithActions,
35436
- {
35437
- field: child,
35438
- fieldSection,
35439
- index,
35440
- sectionIndex,
35441
- remove: removeField
35442
- },
35443
- child.identifier
35444
- ))
35445
- ] })
35446
- ] }),
35447
- /* @__PURE__ */ jsxs(ButtonGroup, { className: "flex-col gap-0.5 flex", variant: "ghost", accentColor: "base", size: "sm", children: [
35448
- /* @__PURE__ */ jsx(IconButton, { type: "button", onClick: handleMoveUp, children: /* @__PURE__ */ jsx(LuIcon, { icon: "move-up" }) }),
35449
- /* @__PURE__ */ jsx(IconButton, { type: "button", onClick: handleMoveDown, children: /* @__PURE__ */ jsx(LuIcon, { icon: "move-down" }) }),
35450
- /* @__PURE__ */ jsx(IconButton, { type: "button", onClick: duplicateSection, children: /* @__PURE__ */ jsx(LuIcon, { icon: "copy" }) }),
35451
- /* @__PURE__ */ jsx(IconButton, { type: "button", onClick: removeSection, children: /* @__PURE__ */ jsx(LuIcon, { icon: "trash" }) })
35452
- ] })
35453
- ] });
35454
- });
35455
- FieldSectionWithActions.displayName = "FieldSectionWithActions";
35456
35270
  const FieldWithActions = memo((props) => {
35457
35271
  const { field, fieldSection, index, sectionIndex, remove } = props;
35458
35272
  const { showError } = useToast();
@@ -35594,6 +35408,19 @@ const FieldSectionConditionEdgeComponent = memo((props) => {
35594
35408
  ] });
35595
35409
  });
35596
35410
  FieldSectionConditionEdgeComponent.displayName = "FieldSectionConditionEdgeComponent";
35411
+ const useFieldTypeItems = (onSelect = () => null) => {
35412
+ return useMemo(() => {
35413
+ const entries = Object.entries(FieldTypeToClsMapping);
35414
+ return entries.map(([type, fieldClass]) => ({
35415
+ children: fieldClass.fieldTypeName,
35416
+ icon: /* @__PURE__ */ jsx(LuIcon, { icon: fieldIcons[type] }),
35417
+ value: type,
35418
+ onSelect: () => {
35419
+ onSelect(type);
35420
+ }
35421
+ }));
35422
+ }, [onSelect]);
35423
+ };
35597
35424
  const FieldSectionNodeComponent = memo((props) => {
35598
35425
  const { data, selected } = props;
35599
35426
  const { fieldSection, index: sectionIndex, layoutDirection } = data;
@@ -35940,6 +35767,195 @@ const FormBuilderFlowBuilder = memo(() => {
35940
35767
  ] });
35941
35768
  });
35942
35769
  FormBuilderFlowBuilder.displayName = "FormBuilderFlowBuilder";
35770
+ const FieldSectionConditionalItem = memo((props) => {
35771
+ const { sourceFieldSection, targetFieldSection } = props;
35772
+ const conditions = useMemo(() => {
35773
+ return sourceFieldSection.getConditions(targetFieldSection.identifier);
35774
+ }, [sourceFieldSection, targetFieldSection.identifier]);
35775
+ const handleDelete = useCallback(() => {
35776
+ sourceFieldSection.removeConditional(targetFieldSection.identifier);
35777
+ }, [sourceFieldSection, targetFieldSection.identifier]);
35778
+ const handleSelectField = useCallback(
35779
+ (field) => {
35780
+ sourceFieldSection.addCondition(targetFieldSection.identifier, createCondition(field));
35781
+ },
35782
+ [sourceFieldSection, targetFieldSection.identifier]
35783
+ );
35784
+ const handleDeleteCondition = useCallback(
35785
+ (condition) => {
35786
+ sourceFieldSection.removeCondition(targetFieldSection.identifier, condition);
35787
+ },
35788
+ [sourceFieldSection, targetFieldSection.identifier]
35789
+ );
35790
+ return /* @__PURE__ */ jsxs(Card, { className: "flex flex-col gap-2", children: [
35791
+ /* @__PURE__ */ jsxs(ButtonGroup, { className: "justify-between gap-2 flex", size: "sm", accentColor: "base", variant: "soft", children: [
35792
+ /* @__PURE__ */ jsxs(Badge, { accentColor: "base", variant: "soft", size: "sm", children: [
35793
+ /* @__PURE__ */ jsx(LuIcon, { icon: fieldIcons.section }),
35794
+ sourceFieldSection.label ?? UNLABELLED_SECTION_LABEL
35795
+ ] }),
35796
+ /* @__PURE__ */ jsx(IconButton, { type: "button", onClick: handleDelete, variant: "ghost", children: /* @__PURE__ */ jsx(LuIcon, { icon: "trash" }) })
35797
+ ] }),
35798
+ /* @__PURE__ */ jsxs("div", { className: "flex h-max w-full flex-wrap gap-2", children: [
35799
+ conditions.map((condition) => {
35800
+ return condition.render({
35801
+ onRemove: handleDeleteCondition
35802
+ });
35803
+ }),
35804
+ /* @__PURE__ */ jsx(FieldDropdownMenu, { fields: sourceFieldSection.fields, onSelectField: handleSelectField, align: "start", children: /* @__PURE__ */ jsx(IconButton, { type: "button", size: "sm", variant: "soft", accentColor: "base", children: /* @__PURE__ */ jsx(LuIcon, { icon: "plus" }) }) })
35805
+ ] })
35806
+ ] });
35807
+ });
35808
+ FieldSectionConditionalItem.displayName = "FieldSectionConditionalItem";
35809
+ const FieldSectionBuilder = memo((props) => {
35810
+ const { fieldSection } = props;
35811
+ const fieldSchema = use(FieldSchemaContext);
35812
+ const conditionalSections = useMemo(() => {
35813
+ return fieldSchema.fields.filter((section) => fieldSection.identifier in section.conditions);
35814
+ }, [fieldSchema.fields, fieldSection.identifier]);
35815
+ const handleAddConditional = useCallback(
35816
+ (conditionalSection) => {
35817
+ conditionalSection.addConditional(fieldSection.identifier);
35818
+ },
35819
+ [fieldSection.identifier]
35820
+ );
35821
+ const validFieldSections = useMemo(() => {
35822
+ const graph = new DirectedGraph({});
35823
+ for (const sourceSection of fieldSchema.fields) {
35824
+ for (const targetSectionId of Object.keys(sourceSection.conditions)) {
35825
+ graph.mergeEdge(sourceSection.identifier, targetSectionId);
35826
+ }
35827
+ }
35828
+ return fieldSchema.fields.filter(
35829
+ (sourceSection) => !willCreateCycle(graph, sourceSection.identifier, fieldSection.identifier)
35830
+ );
35831
+ }, [fieldSchema.fields, fieldSection.identifier]);
35832
+ return /* @__PURE__ */ jsxs("div", { className: "flex grow w-full flex-col gap-4", children: [
35833
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 w-full overflow-hidden", children: [
35834
+ /* @__PURE__ */ jsx(Input.Root, { variant: "outline", size: "md", children: /* @__PURE__ */ jsx(
35835
+ Input.Field,
35836
+ {
35837
+ placeholder: "Enter a section label (optional)",
35838
+ value: fieldSection.label ?? "",
35839
+ onChange: (event) => fieldSection.setOptions({ label: event.target.value }),
35840
+ maxLength: 200
35841
+ }
35842
+ ) }),
35843
+ /* @__PURE__ */ jsx(
35844
+ TextArea,
35845
+ {
35846
+ className: "field-sizing-content",
35847
+ placeholder: "Enter a section description (optional)",
35848
+ value: fieldSection.description ?? "",
35849
+ onChange: (event) => fieldSection.setOptions({ description: event.target.value }),
35850
+ maxLength: 1e3,
35851
+ resize: "vertical",
35852
+ size: "md"
35853
+ }
35854
+ )
35855
+ ] }),
35856
+ /* @__PURE__ */ jsx(Separator, { size: "full" }),
35857
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
35858
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2 justify-between", children: [
35859
+ /* @__PURE__ */ jsx(Text, { accentColor: "base", children: "Conditions" }),
35860
+ /* @__PURE__ */ jsx(
35861
+ FieldSectionDropdownMenu,
35862
+ {
35863
+ fieldSections: validFieldSections,
35864
+ onSelectFieldCondition: handleAddConditional,
35865
+ children: /* @__PURE__ */ jsxs(Button, { type: "button", variant: "soft", size: "sm", className: "w-max", children: [
35866
+ /* @__PURE__ */ jsx(LuIcon, { icon: "plus" }),
35867
+ "Add condition"
35868
+ ] })
35869
+ }
35870
+ )
35871
+ ] }),
35872
+ conditionalSections.map((section) => /* @__PURE__ */ jsx(
35873
+ FieldSectionConditionalItem,
35874
+ {
35875
+ sourceFieldSection: section,
35876
+ targetFieldSection: fieldSection
35877
+ },
35878
+ section.identifier
35879
+ ))
35880
+ ] }),
35881
+ /* @__PURE__ */ jsx(Separator, { size: "full" })
35882
+ ] });
35883
+ });
35884
+ FieldSectionBuilder.displayName = "FieldSectionBuilder";
35885
+ const FieldSectionWithActions = memo((props) => {
35886
+ const { fieldSection, index: sectionIndex } = props;
35887
+ const fieldSchema = use(FieldSchemaContext);
35888
+ const removeField = useCallback(
35889
+ (field) => {
35890
+ fieldSection.removeField(field);
35891
+ },
35892
+ [fieldSection]
35893
+ );
35894
+ const removeSection = useCallback(() => {
35895
+ fieldSchema.removeField(fieldSection);
35896
+ }, [fieldSchema, fieldSection]);
35897
+ const moveSection = useCallback(
35898
+ (direction) => {
35899
+ const targetIndex = direction === "up" ? sectionIndex - 1 : sectionIndex + 1;
35900
+ fieldSchema.moveField(sectionIndex, targetIndex);
35901
+ },
35902
+ [fieldSchema, sectionIndex]
35903
+ );
35904
+ const duplicateSection = useCallback(() => {
35905
+ fieldSchema.addField(fieldSection.duplicate(v4()));
35906
+ }, [fieldSchema, fieldSection]);
35907
+ const handleCreateField = useCallback(
35908
+ (type) => {
35909
+ fieldSection.addField(createField(type));
35910
+ },
35911
+ [fieldSection]
35912
+ );
35913
+ const handleMoveUp = useCallback(() => {
35914
+ moveSection("up");
35915
+ }, [moveSection]);
35916
+ const handleMoveDown = useCallback(() => {
35917
+ moveSection("down");
35918
+ }, [moveSection]);
35919
+ const fieldTypeItems = useFieldTypeItems(handleCreateField);
35920
+ return /* @__PURE__ */ jsxs(Card, { variant: "outline", className: "flex items-center justify-between gap-4 w-full", children: [
35921
+ /* @__PURE__ */ jsxs("div", { className: "flex grow flex-col gap-4 w-full", children: [
35922
+ /* @__PURE__ */ jsx(FieldSectionBuilder, { fieldSection }),
35923
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 w-full", children: [
35924
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2 justify-between", children: [
35925
+ /* @__PURE__ */ jsx(Text, { accentColor: "base", size: "md", children: "Fields" }),
35926
+ /* @__PURE__ */ jsxs(Menu.Root, { children: [
35927
+ /* @__PURE__ */ jsx(Menu.ClickTrigger, { children: /* @__PURE__ */ jsxs(Button, { type: "button", variant: "soft", size: "sm", children: [
35928
+ /* @__PURE__ */ jsx(LuIcon, { icon: "plus" }),
35929
+ " Add field"
35930
+ ] }) }),
35931
+ /* @__PURE__ */ jsx(Menu.Content, { children: /* @__PURE__ */ jsx(Menu.Scroll, { children: fieldTypeItems.flat().map((item) => /* @__PURE__ */ jsxs(Menu.Item, { onSelect: item.onSelect, children: [
35932
+ item.icon,
35933
+ item.children
35934
+ ] }, item.value)) }) })
35935
+ ] })
35936
+ ] }),
35937
+ fieldSection.fields.map((child, index) => /* @__PURE__ */ jsx(
35938
+ FieldWithActions,
35939
+ {
35940
+ field: child,
35941
+ fieldSection,
35942
+ index,
35943
+ sectionIndex,
35944
+ remove: removeField
35945
+ },
35946
+ child.identifier
35947
+ ))
35948
+ ] })
35949
+ ] }),
35950
+ /* @__PURE__ */ jsxs(ButtonGroup, { className: "flex-col gap-0.5 flex", variant: "ghost", accentColor: "base", size: "sm", children: [
35951
+ /* @__PURE__ */ jsx(IconButton, { type: "button", onClick: handleMoveUp, children: /* @__PURE__ */ jsx(LuIcon, { icon: "move-up" }) }),
35952
+ /* @__PURE__ */ jsx(IconButton, { type: "button", onClick: handleMoveDown, children: /* @__PURE__ */ jsx(LuIcon, { icon: "move-down" }) }),
35953
+ /* @__PURE__ */ jsx(IconButton, { type: "button", onClick: duplicateSection, children: /* @__PURE__ */ jsx(LuIcon, { icon: "copy" }) }),
35954
+ /* @__PURE__ */ jsx(IconButton, { type: "button", onClick: removeSection, children: /* @__PURE__ */ jsx(LuIcon, { icon: "trash" }) })
35955
+ ] })
35956
+ ] });
35957
+ });
35958
+ FieldSectionWithActions.displayName = "FieldSectionWithActions";
35943
35959
  const FormBuilderListBuilder = memo(() => {
35944
35960
  const { handleSubmit, errors } = useFormikContext();
35945
35961
  const { hideTitle, hideDescription, onCancel } = use(FormBuilderContext);
@@ -36009,6 +36025,113 @@ const FormBuilderListBuilder = memo(() => {
36009
36025
  ] });
36010
36026
  });
36011
36027
  FormBuilderListBuilder.displayName = "FormBuilderListBuilder";
36028
+ const validateFields = (fields, values) => {
36029
+ const errors = {};
36030
+ const sectionElements = fields.filter((f) => f instanceof FieldSection);
36031
+ for (const field of fields) {
36032
+ if (field instanceof FieldSection) {
36033
+ const conditionalSections = sectionElements.filter((section) => field.identifier in section.conditions);
36034
+ const conditionMet = conditionalSections.length > 0 ? conditionalSections.some(
36035
+ (conditionalSection) => applyConditions(conditionalSection.getConditions(field.identifier), values)
36036
+ ) : true;
36037
+ if (!conditionMet) continue;
36038
+ Object.assign(errors, field.getErrors(values));
36039
+ } else {
36040
+ if (!(field instanceof BaseField)) {
36041
+ throw new Error("Invalid field type");
36042
+ }
36043
+ const id = field.identifier;
36044
+ const error = field.getError(get(values, id));
36045
+ if (error) set(errors, id, error);
36046
+ }
36047
+ }
36048
+ if (Object.keys(errors).length > 0) return errors;
36049
+ };
36050
+ const initializeFieldValues = (fields, values) => {
36051
+ const ret = {};
36052
+ for (const field of fields) {
36053
+ const value = values[field.identifier];
36054
+ ret[field.identifier] = value !== void 0 ? value : field.blankValue();
36055
+ }
36056
+ return ret;
36057
+ };
36058
+ const changedFieldValues = (fields, values1, values2) => {
36059
+ const ret = {};
36060
+ for (const field of fields) {
36061
+ const value1 = values1[field.identifier];
36062
+ const value2 = values2[field.identifier];
36063
+ if (field.areValuesEqual(value1, value2)) continue;
36064
+ ret[field.identifier] = value2;
36065
+ }
36066
+ return ret;
36067
+ };
36068
+ const unchangedFieldValues = (fields, values1, values2) => {
36069
+ const ret = {};
36070
+ for (const field of fields) {
36071
+ const value1 = values1[field.identifier];
36072
+ const value2 = values2[field.identifier];
36073
+ if (!field.areValuesEqual(value1, value2)) continue;
36074
+ ret[field.identifier] = value2;
36075
+ }
36076
+ return ret;
36077
+ };
36078
+ const isArrayOfFiles = (value) => {
36079
+ return Array.isArray(value) && value[0] instanceof File;
36080
+ };
36081
+ const separateFilesFromFieldValues = (values) => {
36082
+ const files = {};
36083
+ const newValues = {};
36084
+ for (const key in values) {
36085
+ const value = values[key];
36086
+ if (value instanceof File) {
36087
+ files[key] = [value];
36088
+ } else if (isArrayOfFiles(value)) {
36089
+ files[key] = value;
36090
+ } else if (value !== void 0) {
36091
+ newValues[key] = value;
36092
+ }
36093
+ }
36094
+ return { values: newValues, files };
36095
+ };
36096
+ const separateFilesFromFields = async (fields) => {
36097
+ const images = {};
36098
+ const newFields = [];
36099
+ for (const section of fields) {
36100
+ if (section.type !== "section") {
36101
+ throw new Error(`Expected ISerializedField type to be a section. Got ${section.type} instead.`);
36102
+ }
36103
+ const { fields: sectionFields } = section;
36104
+ const newSectionFields = [];
36105
+ for (const field of sectionFields) {
36106
+ if (field.image) {
36107
+ if (field.image instanceof Promise) {
36108
+ try {
36109
+ images[field.identifier] = await field.image;
36110
+ } catch (e) {
36111
+ console.error("Failed to get image from promise", e);
36112
+ }
36113
+ } else {
36114
+ images[field.identifier] = field.image;
36115
+ }
36116
+ delete field.image;
36117
+ }
36118
+ newSectionFields.push(field);
36119
+ }
36120
+ newFields.push({ ...section, fields: newSectionFields });
36121
+ }
36122
+ return { fields: newFields, images };
36123
+ };
36124
+ async function awaitPromisesFromFieldValues(values) {
36125
+ const valuesWithoutFiles = {};
36126
+ for (const [key, value] of Object.entries(values)) {
36127
+ if (Array.isArray(value) && value.every((item) => item instanceof Promise)) {
36128
+ valuesWithoutFiles[key] = await Promise.all(value);
36129
+ } else {
36130
+ valuesWithoutFiles[key] = value;
36131
+ }
36132
+ }
36133
+ return valuesWithoutFiles;
36134
+ }
36012
36135
  const FormRenderer = memo(
36013
36136
  forwardRef((props, ref) => {
36014
36137
  const {
@@ -36032,11 +36155,13 @@ const FormRenderer = memo(
36032
36155
  const { readonly } = schema.meta;
36033
36156
  const formId2 = useId$1();
36034
36157
  const initialValues = useMemo(() => {
36035
- return initializeFieldValues(schema.fields, values);
36158
+ return initializeFieldValues(flattenFields(schema.fields), values);
36036
36159
  }, [schema.fields, values]);
36037
36160
  const handleSubmit = useCallback(
36038
36161
  (values2) => {
36039
- onSubmit == null ? void 0 : onSubmit(excludeUnchangedFields ? changedFieldValues(schema.fields, initialValues, values2) : values2);
36162
+ onSubmit == null ? void 0 : onSubmit(
36163
+ excludeUnchangedFields ? changedFieldValues(flattenFields(schema.fields), initialValues, values2) : values2
36164
+ );
36040
36165
  },
36041
36166
  [excludeUnchangedFields, initialValues, onSubmit, schema.fields]
36042
36167
  );
@@ -36060,7 +36185,7 @@ const FormRenderer = memo(
36060
36185
  const handleValuesChange = useCallback(
36061
36186
  (identifier, value) => {
36062
36187
  const field = getFieldsMapping(schema.fields)[identifier];
36063
- if (field.isEqual(initialValues[identifier], value)) return;
36188
+ if (field.areValuesEqual(initialValues[identifier], value)) return;
36064
36189
  onValuesChange == null ? void 0 : onValuesChange({ ...formik.values, [identifier]: value }, { [identifier]: value });
36065
36190
  },
36066
36191
  [formik.values, initialValues, onValuesChange, schema.fields]
@@ -36173,7 +36298,7 @@ const FormBuilderRoot = memo((props) => {
36173
36298
  if (fieldErrors) {
36174
36299
  errors.fields = fieldErrors.fields;
36175
36300
  }
36176
- if (hasKeys(errors)) {
36301
+ if (Object.keys(errors).length > 0) {
36177
36302
  showError({
36178
36303
  title: "Some form settings are invalid",
36179
36304
  description: "Please check settings highlighted in red."
@@ -36333,6 +36458,9 @@ export {
36333
36458
  UploadInput,
36334
36459
  applyConditions,
36335
36460
  awaitPromisesFromFieldValues,
36461
+ changedFieldValues,
36462
+ cleanFieldValues,
36463
+ cleanSerializedFieldValues,
36336
36464
  createCondition,
36337
36465
  createConditionModifierConfig,
36338
36466
  createField,
@@ -36347,12 +36475,15 @@ export {
36347
36475
  flattenFields,
36348
36476
  getFieldsMapping,
36349
36477
  initializeFieldValues,
36478
+ isFilePromiseArray,
36479
+ isStringArray,
36350
36480
  maxFileSizeB,
36351
36481
  maxFileSizeKB,
36352
36482
  maxFileSizeMB,
36353
36483
  separateFilesFromFieldValues,
36354
36484
  separateFilesFromFields,
36355
36485
  serializeFieldValues,
36486
+ unchangedFieldValues,
36356
36487
  useFieldInput,
36357
36488
  useFieldInputs,
36358
36489
  useFormikInput,