hs-uix 1.0.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/README.md +80 -0
- package/datatable.d.ts +21 -0
- package/dist/datatable.js +1040 -0
- package/dist/datatable.mjs +1034 -0
- package/dist/form.js +1268 -0
- package/dist/form.mjs +1271 -0
- package/dist/index.js +2272 -0
- package/dist/index.mjs +2304 -0
- package/form.d.ts +17 -0
- package/index.d.ts +38 -0
- package/package.json +69 -0
package/dist/form.js
ADDED
|
@@ -0,0 +1,1268 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
|
|
29
|
+
// src/form.js
|
|
30
|
+
var form_exports = {};
|
|
31
|
+
__export(form_exports, {
|
|
32
|
+
FormBuilder: () => FormBuilder,
|
|
33
|
+
useFormPrefill: () => useFormPrefill
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(form_exports);
|
|
36
|
+
|
|
37
|
+
// packages/form/src/FormBuilder.jsx
|
|
38
|
+
var import_react = __toESM(require("react"));
|
|
39
|
+
var import_ui_extensions = require("@hubspot/ui-extensions");
|
|
40
|
+
var import_crm = require("@hubspot/ui-extensions/crm");
|
|
41
|
+
var getEmptyValue = (field) => {
|
|
42
|
+
switch (field.type) {
|
|
43
|
+
case "toggle":
|
|
44
|
+
case "checkbox":
|
|
45
|
+
return false;
|
|
46
|
+
case "multiselect":
|
|
47
|
+
case "checkboxGroup":
|
|
48
|
+
return [];
|
|
49
|
+
case "number":
|
|
50
|
+
case "stepper":
|
|
51
|
+
case "currency":
|
|
52
|
+
return void 0;
|
|
53
|
+
case "date":
|
|
54
|
+
case "time":
|
|
55
|
+
case "datetime":
|
|
56
|
+
return void 0;
|
|
57
|
+
case "display":
|
|
58
|
+
case "crmPropertyList":
|
|
59
|
+
case "crmAssociationPropertyList":
|
|
60
|
+
return void 0;
|
|
61
|
+
// these field types have no form value
|
|
62
|
+
case "repeater":
|
|
63
|
+
return [];
|
|
64
|
+
default:
|
|
65
|
+
return "";
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
var isValueEmpty = (value, field) => {
|
|
69
|
+
if (value === void 0 || value === null) return true;
|
|
70
|
+
if (typeof value === "string" && value.trim() === "") return true;
|
|
71
|
+
if (Array.isArray(value) && value.length === 0) return true;
|
|
72
|
+
if ((field.type === "toggle" || field.type === "checkbox") && value === false) return true;
|
|
73
|
+
return false;
|
|
74
|
+
};
|
|
75
|
+
var runValidators = (value, field, allValues, fieldTypes) => {
|
|
76
|
+
if (field.type === "display" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList") return null;
|
|
77
|
+
const isRequired = resolveRequired(field, allValues);
|
|
78
|
+
const plugin = fieldTypes && fieldTypes[field.type];
|
|
79
|
+
const empty = plugin && plugin.isEmpty ? plugin.isEmpty(value) : isValueEmpty(value, field);
|
|
80
|
+
if (isRequired && empty) {
|
|
81
|
+
return `${field.label} is required`;
|
|
82
|
+
}
|
|
83
|
+
if (empty) return null;
|
|
84
|
+
if (field.pattern && typeof value === "string") {
|
|
85
|
+
if (!field.pattern.test(value)) {
|
|
86
|
+
return field.patternMessage || "Invalid format";
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (typeof value === "string") {
|
|
90
|
+
if (field.minLength != null && value.length < field.minLength) {
|
|
91
|
+
return `Must be at least ${field.minLength} characters`;
|
|
92
|
+
}
|
|
93
|
+
if (field.maxLength != null && value.length > field.maxLength) {
|
|
94
|
+
return `Must be no more than ${field.maxLength} characters`;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (typeof value === "number") {
|
|
98
|
+
if (field.min != null && value < field.min) {
|
|
99
|
+
return `Must be at least ${field.min}`;
|
|
100
|
+
}
|
|
101
|
+
if (field.max != null && value > field.max) {
|
|
102
|
+
return `Must be no more than ${field.max}`;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (field.validate) {
|
|
106
|
+
const result = field.validate(value, allValues);
|
|
107
|
+
if (result && typeof result.then === "function") return null;
|
|
108
|
+
if (result !== true && result) return result;
|
|
109
|
+
}
|
|
110
|
+
return null;
|
|
111
|
+
};
|
|
112
|
+
var resolveRequired = (field, allValues) => {
|
|
113
|
+
if (typeof field.required === "function") return field.required(allValues);
|
|
114
|
+
return !!field.required;
|
|
115
|
+
};
|
|
116
|
+
var resolveOptions = (field, allValues) => {
|
|
117
|
+
if (typeof field.options === "function") return field.options(allValues);
|
|
118
|
+
return field.options || [];
|
|
119
|
+
};
|
|
120
|
+
var useFormPrefill = (properties, mapping) => {
|
|
121
|
+
return (0, import_react.useMemo)(() => {
|
|
122
|
+
if (!properties) return {};
|
|
123
|
+
if (!mapping) {
|
|
124
|
+
const result2 = {};
|
|
125
|
+
for (const [key, value] of Object.entries(properties)) {
|
|
126
|
+
if (value !== void 0) result2[key] = value;
|
|
127
|
+
}
|
|
128
|
+
return result2;
|
|
129
|
+
}
|
|
130
|
+
const result = {};
|
|
131
|
+
for (const [formField, crmProp] of Object.entries(mapping)) {
|
|
132
|
+
if (properties[crmProp] !== void 0) {
|
|
133
|
+
result[formField] = properties[crmProp];
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return result;
|
|
137
|
+
}, [properties, mapping]);
|
|
138
|
+
};
|
|
139
|
+
var FormBuilder = (0, import_react.forwardRef)(function FormBuilder2(props, ref) {
|
|
140
|
+
const {
|
|
141
|
+
fields,
|
|
142
|
+
// FormBuilderField[] — field definitions
|
|
143
|
+
onSubmit,
|
|
144
|
+
// (values, { reset, rawValues }) => void | Promise
|
|
145
|
+
transformValues,
|
|
146
|
+
// (values) => values — reshape before submit
|
|
147
|
+
onBeforeSubmit,
|
|
148
|
+
// (values) => boolean | Promise<boolean> — intercept submit
|
|
149
|
+
onSubmitSuccess,
|
|
150
|
+
// (result, { reset, values }) => void
|
|
151
|
+
onSubmitError,
|
|
152
|
+
// (error, { values }) => void
|
|
153
|
+
resetOnSuccess = false
|
|
154
|
+
// auto-reset after successful submit
|
|
155
|
+
} = props;
|
|
156
|
+
const {
|
|
157
|
+
initialValues,
|
|
158
|
+
// Record<string, unknown> — starting values (uncontrolled)
|
|
159
|
+
values,
|
|
160
|
+
// Record<string, unknown> — controlled values
|
|
161
|
+
onChange,
|
|
162
|
+
// (values) => void — called on any field change (controlled)
|
|
163
|
+
onFieldChange
|
|
164
|
+
// (name, value, allValues) => void — per-field change callback
|
|
165
|
+
} = props;
|
|
166
|
+
const {
|
|
167
|
+
validateOnChange = false,
|
|
168
|
+
// validate on keystroke (onInput)
|
|
169
|
+
validateOnBlur = true,
|
|
170
|
+
// validate on blur
|
|
171
|
+
validateOnSubmit = true,
|
|
172
|
+
// validate all before onSubmit
|
|
173
|
+
onValidationChange
|
|
174
|
+
// (errors) => void
|
|
175
|
+
} = props;
|
|
176
|
+
const {
|
|
177
|
+
steps,
|
|
178
|
+
// FormBuilderStep[] — enables multi-step mode
|
|
179
|
+
step: controlledStep,
|
|
180
|
+
// number — controlled current step (0-based)
|
|
181
|
+
onStepChange,
|
|
182
|
+
// (step) => void
|
|
183
|
+
showStepIndicator = true,
|
|
184
|
+
// show StepIndicator component
|
|
185
|
+
validateStepOnNext = true
|
|
186
|
+
// validate current step fields before Next
|
|
187
|
+
} = props;
|
|
188
|
+
const {
|
|
189
|
+
submitLabel = "Submit",
|
|
190
|
+
// submit button text
|
|
191
|
+
submitVariant = "primary",
|
|
192
|
+
// submit button variant
|
|
193
|
+
showCancel = false,
|
|
194
|
+
// show cancel button
|
|
195
|
+
cancelLabel = "Cancel",
|
|
196
|
+
// cancel button text
|
|
197
|
+
onCancel,
|
|
198
|
+
// () => void
|
|
199
|
+
submitPosition = "bottom",
|
|
200
|
+
// "bottom" | "none"
|
|
201
|
+
loading: controlledLoading,
|
|
202
|
+
// controlled loading state
|
|
203
|
+
disabled = false
|
|
204
|
+
// disable entire form
|
|
205
|
+
} = props;
|
|
206
|
+
const {
|
|
207
|
+
columns = 1,
|
|
208
|
+
// number of grid columns (1 = full-width stack)
|
|
209
|
+
columnWidth,
|
|
210
|
+
// AutoGrid columnWidth — responsive layout (overrides columns)
|
|
211
|
+
layout,
|
|
212
|
+
// explicit row layout array (overrides columns + columnWidth)
|
|
213
|
+
sections,
|
|
214
|
+
// FormBuilderSection[] — accordion field grouping
|
|
215
|
+
gap = "sm",
|
|
216
|
+
// gap between fields
|
|
217
|
+
showRequiredIndicator = true,
|
|
218
|
+
// show * on required fields
|
|
219
|
+
noFormWrapper = false,
|
|
220
|
+
// skip HubSpot <Form> wrapper
|
|
221
|
+
fieldTypes
|
|
222
|
+
// Record<string, FieldTypePlugin> — custom field type registry
|
|
223
|
+
} = props;
|
|
224
|
+
const {
|
|
225
|
+
error: formError,
|
|
226
|
+
// string | boolean — form-level error alert
|
|
227
|
+
success: formSuccess,
|
|
228
|
+
// string — form-level success alert
|
|
229
|
+
readOnly: formReadOnly = false,
|
|
230
|
+
// boolean — lock all fields
|
|
231
|
+
readOnlyMessage
|
|
232
|
+
// string — warning alert when readOnly
|
|
233
|
+
} = props;
|
|
234
|
+
const {
|
|
235
|
+
onDirtyChange,
|
|
236
|
+
// (isDirty: boolean) => void
|
|
237
|
+
autoSave
|
|
238
|
+
// { debounce: number, onAutoSave: (values) => void }
|
|
239
|
+
} = props;
|
|
240
|
+
const computeInitialValues = () => {
|
|
241
|
+
const vals = {};
|
|
242
|
+
for (const field of fields) {
|
|
243
|
+
if (field.type === "display" || field.type === "crmPropertyList" || field.type === "crmAssociationPropertyList") continue;
|
|
244
|
+
const plugin = fieldTypes && fieldTypes[field.type];
|
|
245
|
+
const emptyValue = plugin && plugin.getEmptyValue ? plugin.getEmptyValue() : getEmptyValue(field);
|
|
246
|
+
const init = initialValues && initialValues[field.name] !== void 0 ? initialValues[field.name] : field.defaultValue !== void 0 ? field.defaultValue : emptyValue;
|
|
247
|
+
vals[field.name] = init;
|
|
248
|
+
}
|
|
249
|
+
return vals;
|
|
250
|
+
};
|
|
251
|
+
const [internalValues, setInternalValues] = (0, import_react.useState)(computeInitialValues);
|
|
252
|
+
const [internalErrors, setInternalErrors] = (0, import_react.useState)({});
|
|
253
|
+
const [internalStep, setInternalStep] = (0, import_react.useState)(0);
|
|
254
|
+
const [internalLoading, setInternalLoading] = (0, import_react.useState)(false);
|
|
255
|
+
const [touchedFields, setTouchedFields] = (0, import_react.useState)({});
|
|
256
|
+
const [validatingFields, setValidatingFields] = (0, import_react.useState)({});
|
|
257
|
+
const asyncValidationRef = (0, import_react.useRef)(/* @__PURE__ */ new Map());
|
|
258
|
+
const debounceTimersRef = (0, import_react.useRef)(/* @__PURE__ */ new Map());
|
|
259
|
+
const initialSnapshot = (0, import_react.useRef)(null);
|
|
260
|
+
if (initialSnapshot.current === null) {
|
|
261
|
+
initialSnapshot.current = JSON.stringify(computeInitialValues());
|
|
262
|
+
}
|
|
263
|
+
const formValues = values != null ? values : internalValues;
|
|
264
|
+
const currentStep = controlledStep != null ? controlledStep : internalStep;
|
|
265
|
+
const isLoading = controlledLoading != null ? controlledLoading : internalLoading;
|
|
266
|
+
const isMultiStep = Array.isArray(steps) && steps.length > 0;
|
|
267
|
+
(0, import_react.useEffect)(() => {
|
|
268
|
+
return () => {
|
|
269
|
+
for (const timer of debounceTimersRef.current.values()) clearTimeout(timer);
|
|
270
|
+
};
|
|
271
|
+
}, []);
|
|
272
|
+
const isDirty = (0, import_react.useMemo)(() => {
|
|
273
|
+
return JSON.stringify(formValues) !== initialSnapshot.current;
|
|
274
|
+
}, [formValues]);
|
|
275
|
+
const prevDirtyRef = (0, import_react.useRef)(false);
|
|
276
|
+
(0, import_react.useEffect)(() => {
|
|
277
|
+
if (isDirty !== prevDirtyRef.current) {
|
|
278
|
+
prevDirtyRef.current = isDirty;
|
|
279
|
+
if (onDirtyChange) onDirtyChange(isDirty);
|
|
280
|
+
}
|
|
281
|
+
}, [isDirty, onDirtyChange]);
|
|
282
|
+
const autoSaveTimerRef = (0, import_react.useRef)(null);
|
|
283
|
+
(0, import_react.useEffect)(() => {
|
|
284
|
+
if (!autoSave || !autoSave.onAutoSave || !isDirty) return;
|
|
285
|
+
if (autoSaveTimerRef.current) clearTimeout(autoSaveTimerRef.current);
|
|
286
|
+
autoSaveTimerRef.current = setTimeout(() => {
|
|
287
|
+
autoSaveTimerRef.current = null;
|
|
288
|
+
autoSave.onAutoSave(formValues);
|
|
289
|
+
}, autoSave.debounce || 1e3);
|
|
290
|
+
return () => {
|
|
291
|
+
if (autoSaveTimerRef.current) clearTimeout(autoSaveTimerRef.current);
|
|
292
|
+
};
|
|
293
|
+
}, [formValues, isDirty, autoSave]);
|
|
294
|
+
const visibleFields = (0, import_react.useMemo)(() => {
|
|
295
|
+
let filtered = fields.filter((f) => {
|
|
296
|
+
if (f.visible && !f.visible(formValues)) return false;
|
|
297
|
+
return true;
|
|
298
|
+
});
|
|
299
|
+
if (isMultiStep && steps[currentStep] && steps[currentStep].fields) {
|
|
300
|
+
const stepFieldNames = new Set(steps[currentStep].fields);
|
|
301
|
+
filtered = filtered.filter((f) => stepFieldNames.has(f.name));
|
|
302
|
+
}
|
|
303
|
+
return filtered;
|
|
304
|
+
}, [fields, formValues, isMultiStep, steps, currentStep]);
|
|
305
|
+
const validateField = (0, import_react.useCallback)(
|
|
306
|
+
(name, value) => {
|
|
307
|
+
const field = fields.find((f) => f.name === name);
|
|
308
|
+
if (!field) return null;
|
|
309
|
+
if (field.visible && !field.visible(formValues)) return null;
|
|
310
|
+
return runValidators(value != null ? value : formValues[name], field, formValues, fieldTypes);
|
|
311
|
+
},
|
|
312
|
+
[fields, formValues, fieldTypes]
|
|
313
|
+
);
|
|
314
|
+
const validateVisibleFields = (0, import_react.useCallback)(
|
|
315
|
+
(fieldSubset) => {
|
|
316
|
+
const toValidate = fieldSubset || visibleFields;
|
|
317
|
+
const errors = {};
|
|
318
|
+
let hasErrors = false;
|
|
319
|
+
for (const field of toValidate) {
|
|
320
|
+
const err = runValidators(formValues[field.name], field, formValues, fieldTypes);
|
|
321
|
+
if (err) {
|
|
322
|
+
errors[field.name] = err;
|
|
323
|
+
hasErrors = true;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return { errors, hasErrors };
|
|
327
|
+
},
|
|
328
|
+
[visibleFields, formValues]
|
|
329
|
+
);
|
|
330
|
+
const updateErrors = (0, import_react.useCallback)(
|
|
331
|
+
(newErrors) => {
|
|
332
|
+
setInternalErrors((prev) => {
|
|
333
|
+
const merged = { ...prev, ...newErrors };
|
|
334
|
+
for (const key of Object.keys(merged)) {
|
|
335
|
+
if (newErrors[key] === null || newErrors[key] === void 0) {
|
|
336
|
+
delete merged[key];
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
if (onValidationChange) onValidationChange(merged);
|
|
340
|
+
return merged;
|
|
341
|
+
});
|
|
342
|
+
},
|
|
343
|
+
[onValidationChange]
|
|
344
|
+
);
|
|
345
|
+
const runAsyncValidation = (0, import_react.useCallback)(
|
|
346
|
+
(name, value) => {
|
|
347
|
+
const field = fields.find((f) => f.name === name);
|
|
348
|
+
if (!field || !field.validate) return;
|
|
349
|
+
const val = value != null ? value : formValues[name];
|
|
350
|
+
const syncError = runValidators(val, field, formValues, fieldTypes);
|
|
351
|
+
if (syncError) return;
|
|
352
|
+
const result = field.validate(val, formValues);
|
|
353
|
+
if (!result || typeof result.then !== "function") return;
|
|
354
|
+
const validationPromise = result.then(
|
|
355
|
+
(asyncResult) => {
|
|
356
|
+
if (asyncValidationRef.current.get(name) !== validationPromise) return;
|
|
357
|
+
asyncValidationRef.current.delete(name);
|
|
358
|
+
setValidatingFields((prev) => {
|
|
359
|
+
const next = { ...prev };
|
|
360
|
+
delete next[name];
|
|
361
|
+
return next;
|
|
362
|
+
});
|
|
363
|
+
const err = asyncResult !== true && asyncResult ? asyncResult : null;
|
|
364
|
+
updateErrors({ [name]: err });
|
|
365
|
+
},
|
|
366
|
+
(rejection) => {
|
|
367
|
+
if (asyncValidationRef.current.get(name) !== validationPromise) return;
|
|
368
|
+
asyncValidationRef.current.delete(name);
|
|
369
|
+
setValidatingFields((prev) => {
|
|
370
|
+
const next = { ...prev };
|
|
371
|
+
delete next[name];
|
|
372
|
+
return next;
|
|
373
|
+
});
|
|
374
|
+
updateErrors({ [name]: (rejection == null ? void 0 : rejection.message) || "Validation failed" });
|
|
375
|
+
}
|
|
376
|
+
);
|
|
377
|
+
asyncValidationRef.current.set(name, validationPromise);
|
|
378
|
+
setValidatingFields((prev) => ({ ...prev, [name]: true }));
|
|
379
|
+
},
|
|
380
|
+
[fields, formValues, updateErrors]
|
|
381
|
+
);
|
|
382
|
+
const triggerAsyncValidation = (0, import_react.useCallback)(
|
|
383
|
+
(name, value) => {
|
|
384
|
+
const field = fields.find((f) => f.name === name);
|
|
385
|
+
if (!field || !field.validate) return;
|
|
386
|
+
const debounceMs = field.validateDebounce;
|
|
387
|
+
if (debounceMs && debounceMs > 0) {
|
|
388
|
+
const existing = debounceTimersRef.current.get(name);
|
|
389
|
+
if (existing) clearTimeout(existing);
|
|
390
|
+
const timer = setTimeout(() => {
|
|
391
|
+
debounceTimersRef.current.delete(name);
|
|
392
|
+
runAsyncValidation(name, value);
|
|
393
|
+
}, debounceMs);
|
|
394
|
+
debounceTimersRef.current.set(name, timer);
|
|
395
|
+
} else {
|
|
396
|
+
runAsyncValidation(name, value);
|
|
397
|
+
}
|
|
398
|
+
},
|
|
399
|
+
[fields, runAsyncValidation]
|
|
400
|
+
);
|
|
401
|
+
const setFieldValueSilent = (0, import_react.useCallback)(
|
|
402
|
+
(name, value) => {
|
|
403
|
+
if (values != null) {
|
|
404
|
+
if (onChange) onChange({ ...formValues, [name]: value });
|
|
405
|
+
} else {
|
|
406
|
+
setInternalValues((prev) => ({ ...prev, [name]: value }));
|
|
407
|
+
}
|
|
408
|
+
},
|
|
409
|
+
[values, onChange, formValues]
|
|
410
|
+
);
|
|
411
|
+
const handleFieldChange = (0, import_react.useCallback)(
|
|
412
|
+
(name, value) => {
|
|
413
|
+
const newValues = { ...formValues, [name]: value };
|
|
414
|
+
if (values != null) {
|
|
415
|
+
if (onChange) onChange(newValues);
|
|
416
|
+
} else {
|
|
417
|
+
setInternalValues(newValues);
|
|
418
|
+
}
|
|
419
|
+
if (onFieldChange) onFieldChange(name, value, newValues);
|
|
420
|
+
if (internalErrors[name]) {
|
|
421
|
+
updateErrors({ [name]: null });
|
|
422
|
+
}
|
|
423
|
+
const field = fields.find((f) => f.name === name);
|
|
424
|
+
if (field && field.onFieldChange) {
|
|
425
|
+
field.onFieldChange(value, newValues, {
|
|
426
|
+
setFieldValue: setFieldValueSilent,
|
|
427
|
+
setFieldError: (fieldName, message) => updateErrors({ [fieldName]: message })
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
},
|
|
431
|
+
[formValues, values, onChange, onFieldChange, internalErrors, updateErrors, fields, setFieldValueSilent]
|
|
432
|
+
);
|
|
433
|
+
const inputDebounceRef = (0, import_react.useRef)(/* @__PURE__ */ new Map());
|
|
434
|
+
const handleDebouncedFieldChange = (0, import_react.useCallback)(
|
|
435
|
+
(name, value) => {
|
|
436
|
+
const field = fields.find((f) => f.name === name);
|
|
437
|
+
const debounceMs = field && field.debounce;
|
|
438
|
+
if (debounceMs && debounceMs > 0) {
|
|
439
|
+
const existing = inputDebounceRef.current.get(name);
|
|
440
|
+
if (existing) clearTimeout(existing);
|
|
441
|
+
const timer = setTimeout(() => {
|
|
442
|
+
inputDebounceRef.current.delete(name);
|
|
443
|
+
handleFieldChange(name, value);
|
|
444
|
+
}, debounceMs);
|
|
445
|
+
inputDebounceRef.current.set(name, timer);
|
|
446
|
+
} else {
|
|
447
|
+
handleFieldChange(name, value);
|
|
448
|
+
}
|
|
449
|
+
},
|
|
450
|
+
[fields, handleFieldChange]
|
|
451
|
+
);
|
|
452
|
+
const handleFieldInput = (0, import_react.useCallback)(
|
|
453
|
+
(name, value) => {
|
|
454
|
+
if (validateOnChange) {
|
|
455
|
+
const err = validateField(name, value);
|
|
456
|
+
updateErrors({ [name]: err });
|
|
457
|
+
}
|
|
458
|
+
},
|
|
459
|
+
[validateOnChange, validateField, updateErrors]
|
|
460
|
+
);
|
|
461
|
+
const handleFieldBlur = (0, import_react.useCallback)(
|
|
462
|
+
(name, value) => {
|
|
463
|
+
setTouchedFields((prev) => ({ ...prev, [name]: true }));
|
|
464
|
+
if (validateOnBlur) {
|
|
465
|
+
const err = validateField(name, value != null ? value : formValues[name]);
|
|
466
|
+
updateErrors({ [name]: err });
|
|
467
|
+
if (!err) {
|
|
468
|
+
triggerAsyncValidation(name, value != null ? value : formValues[name]);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
},
|
|
472
|
+
[validateOnBlur, validateField, updateErrors, formValues, triggerAsyncValidation]
|
|
473
|
+
);
|
|
474
|
+
const handleSubmit = (0, import_react.useCallback)(
|
|
475
|
+
async (e) => {
|
|
476
|
+
if (e && e.preventDefault) e.preventDefault();
|
|
477
|
+
if (validateOnSubmit) {
|
|
478
|
+
const allVisible = fields.filter((f) => !f.visible || f.visible(formValues));
|
|
479
|
+
const { errors, hasErrors } = validateVisibleFields(allVisible);
|
|
480
|
+
if (hasErrors) {
|
|
481
|
+
setInternalErrors(errors);
|
|
482
|
+
if (onValidationChange) onValidationChange(errors);
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
if (asyncValidationRef.current.size > 0) {
|
|
486
|
+
await Promise.all(asyncValidationRef.current.values());
|
|
487
|
+
const currentErrors = { ...internalErrors };
|
|
488
|
+
const hasAsyncErrors = Object.keys(currentErrors).length > 0;
|
|
489
|
+
if (hasAsyncErrors) return;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
const reset = () => {
|
|
493
|
+
const fresh = computeInitialValues();
|
|
494
|
+
if (values == null) setInternalValues(fresh);
|
|
495
|
+
setInternalErrors({});
|
|
496
|
+
setTouchedFields({});
|
|
497
|
+
initialSnapshot.current = JSON.stringify(fresh);
|
|
498
|
+
};
|
|
499
|
+
const rawValues = {};
|
|
500
|
+
for (const key of Object.keys(formValues)) {
|
|
501
|
+
const f = fields.find((fd) => fd.name === key);
|
|
502
|
+
if (f && (f.type === "display" || f.type === "crmPropertyList" || f.type === "crmAssociationPropertyList")) continue;
|
|
503
|
+
rawValues[key] = formValues[key];
|
|
504
|
+
}
|
|
505
|
+
const submitValues = transformValues ? transformValues(rawValues) : rawValues;
|
|
506
|
+
if (onBeforeSubmit) {
|
|
507
|
+
const proceed = await onBeforeSubmit(submitValues);
|
|
508
|
+
if (proceed === false) return;
|
|
509
|
+
}
|
|
510
|
+
if (controlledLoading == null) setInternalLoading(true);
|
|
511
|
+
try {
|
|
512
|
+
const result = await onSubmit(submitValues, { reset, rawValues });
|
|
513
|
+
if (resetOnSuccess) reset();
|
|
514
|
+
if (onSubmitSuccess) onSubmitSuccess(result, { reset, values: submitValues });
|
|
515
|
+
} catch (err) {
|
|
516
|
+
if (onSubmitError) {
|
|
517
|
+
onSubmitError(err, { values: submitValues });
|
|
518
|
+
} else {
|
|
519
|
+
throw err;
|
|
520
|
+
}
|
|
521
|
+
} finally {
|
|
522
|
+
if (controlledLoading == null) setInternalLoading(false);
|
|
523
|
+
}
|
|
524
|
+
},
|
|
525
|
+
[validateOnSubmit, fields, formValues, validateVisibleFields, onValidationChange, onSubmit, values, controlledLoading, internalErrors, transformValues, onBeforeSubmit, onSubmitSuccess, onSubmitError, resetOnSuccess]
|
|
526
|
+
);
|
|
527
|
+
const handleNext = (0, import_react.useCallback)(() => {
|
|
528
|
+
if (!isMultiStep) return;
|
|
529
|
+
if (validateStepOnNext && steps[currentStep] && steps[currentStep].fields) {
|
|
530
|
+
const stepFields = fields.filter(
|
|
531
|
+
(f) => steps[currentStep].fields.includes(f.name) && (!f.visible || f.visible(formValues))
|
|
532
|
+
);
|
|
533
|
+
const { errors, hasErrors } = validateVisibleFields(stepFields);
|
|
534
|
+
if (hasErrors) {
|
|
535
|
+
setInternalErrors((prev) => ({ ...prev, ...errors }));
|
|
536
|
+
if (onValidationChange) onValidationChange({ ...internalErrors, ...errors });
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
if (steps[currentStep] && steps[currentStep].validate) {
|
|
541
|
+
const result = steps[currentStep].validate(formValues);
|
|
542
|
+
if (result !== true && result) {
|
|
543
|
+
setInternalErrors((prev) => ({ ...prev, ...result }));
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
const nextStep = Math.min(currentStep + 1, steps.length - 1);
|
|
548
|
+
if (controlledStep != null) {
|
|
549
|
+
if (onStepChange) onStepChange(nextStep);
|
|
550
|
+
} else {
|
|
551
|
+
setInternalStep(nextStep);
|
|
552
|
+
}
|
|
553
|
+
}, [isMultiStep, validateStepOnNext, steps, currentStep, fields, formValues, validateVisibleFields, onValidationChange, internalErrors, controlledStep, onStepChange]);
|
|
554
|
+
const handleBack = (0, import_react.useCallback)(() => {
|
|
555
|
+
if (!isMultiStep) return;
|
|
556
|
+
const prevStep = Math.max(currentStep - 1, 0);
|
|
557
|
+
if (controlledStep != null) {
|
|
558
|
+
if (onStepChange) onStepChange(prevStep);
|
|
559
|
+
} else {
|
|
560
|
+
setInternalStep(prevStep);
|
|
561
|
+
}
|
|
562
|
+
}, [isMultiStep, currentStep, controlledStep, onStepChange]);
|
|
563
|
+
const handleGoTo = (0, import_react.useCallback)(
|
|
564
|
+
(stepIndex) => {
|
|
565
|
+
if (!isMultiStep) return;
|
|
566
|
+
const clamped = Math.max(0, Math.min(stepIndex, steps.length - 1));
|
|
567
|
+
if (controlledStep != null) {
|
|
568
|
+
if (onStepChange) onStepChange(clamped);
|
|
569
|
+
} else {
|
|
570
|
+
setInternalStep(clamped);
|
|
571
|
+
}
|
|
572
|
+
},
|
|
573
|
+
[isMultiStep, steps, controlledStep, onStepChange]
|
|
574
|
+
);
|
|
575
|
+
(0, import_react.useImperativeHandle)(ref, () => ({
|
|
576
|
+
submit: handleSubmit,
|
|
577
|
+
validate: () => {
|
|
578
|
+
const allVisible = fields.filter((f) => !f.visible || f.visible(formValues));
|
|
579
|
+
const { errors, hasErrors } = validateVisibleFields(allVisible);
|
|
580
|
+
setInternalErrors(errors);
|
|
581
|
+
return { valid: !hasErrors, errors };
|
|
582
|
+
},
|
|
583
|
+
reset: () => {
|
|
584
|
+
const fresh = computeInitialValues();
|
|
585
|
+
if (values == null) setInternalValues(fresh);
|
|
586
|
+
setInternalErrors({});
|
|
587
|
+
setTouchedFields({});
|
|
588
|
+
initialSnapshot.current = JSON.stringify(fresh);
|
|
589
|
+
},
|
|
590
|
+
getValues: () => formValues,
|
|
591
|
+
isDirty: () => isDirty,
|
|
592
|
+
setFieldValue: (name, value) => handleFieldChange(name, value),
|
|
593
|
+
setFieldError: (name, message) => updateErrors({ [name]: message }),
|
|
594
|
+
setErrors: (errors) => {
|
|
595
|
+
setInternalErrors(errors);
|
|
596
|
+
if (onValidationChange) onValidationChange(errors);
|
|
597
|
+
}
|
|
598
|
+
}));
|
|
599
|
+
const renderField = (field) => {
|
|
600
|
+
const fieldValue = formValues[field.name];
|
|
601
|
+
const fieldError = internalErrors[field.name] || null;
|
|
602
|
+
const hasError = !!fieldError;
|
|
603
|
+
const isRequired = showRequiredIndicator && resolveRequired(field, formValues);
|
|
604
|
+
const isReadOnly = field.readOnly || disabled || formReadOnly;
|
|
605
|
+
const fieldOnChange = field.debounce ? (v) => handleDebouncedFieldChange(field.name, v) : (v) => handleFieldChange(field.name, v);
|
|
606
|
+
if (field.type === "display") {
|
|
607
|
+
if (field.render) {
|
|
608
|
+
return field.render({ allValues: formValues });
|
|
609
|
+
}
|
|
610
|
+
return null;
|
|
611
|
+
}
|
|
612
|
+
if (field.type === "crmPropertyList") {
|
|
613
|
+
return /* @__PURE__ */ import_react.default.createElement(
|
|
614
|
+
import_crm.CrmPropertyList,
|
|
615
|
+
{
|
|
616
|
+
properties: field.properties,
|
|
617
|
+
direction: field.direction,
|
|
618
|
+
...field.objectId ? { objectId: field.objectId } : {},
|
|
619
|
+
...field.objectTypeId ? { objectTypeId: field.objectTypeId } : {},
|
|
620
|
+
...field.fieldProps || {}
|
|
621
|
+
}
|
|
622
|
+
);
|
|
623
|
+
}
|
|
624
|
+
if (field.type === "crmAssociationPropertyList") {
|
|
625
|
+
return /* @__PURE__ */ import_react.default.createElement(
|
|
626
|
+
import_crm.CrmAssociationPropertyList,
|
|
627
|
+
{
|
|
628
|
+
objectTypeId: field.objectTypeId,
|
|
629
|
+
properties: field.properties,
|
|
630
|
+
...field.associationLabels ? { associationLabels: field.associationLabels } : {},
|
|
631
|
+
...field.filters ? { filters: field.filters } : {},
|
|
632
|
+
...field.sort ? { sort: field.sort } : {},
|
|
633
|
+
...field.fieldProps || {}
|
|
634
|
+
}
|
|
635
|
+
);
|
|
636
|
+
}
|
|
637
|
+
if (field.render) {
|
|
638
|
+
return field.render({
|
|
639
|
+
value: fieldValue,
|
|
640
|
+
onChange: fieldOnChange,
|
|
641
|
+
error: hasError,
|
|
642
|
+
allValues: formValues
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
const plugin = fieldTypes && fieldTypes[field.type];
|
|
646
|
+
if (plugin && plugin.render) {
|
|
647
|
+
return plugin.render({
|
|
648
|
+
value: fieldValue,
|
|
649
|
+
onChange: fieldOnChange,
|
|
650
|
+
error: hasError,
|
|
651
|
+
field,
|
|
652
|
+
allValues: formValues
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
const commonProps = {
|
|
656
|
+
name: field.name,
|
|
657
|
+
label: field.label,
|
|
658
|
+
description: field.description,
|
|
659
|
+
placeholder: field.placeholder,
|
|
660
|
+
tooltip: field.tooltip,
|
|
661
|
+
required: isRequired,
|
|
662
|
+
readOnly: isReadOnly,
|
|
663
|
+
error: hasError,
|
|
664
|
+
validationMessage: fieldError || void 0,
|
|
665
|
+
...field.loading || validatingFields[field.name] ? { loading: true } : {},
|
|
666
|
+
...field.fieldProps || {}
|
|
667
|
+
};
|
|
668
|
+
const options = resolveOptions(field, formValues);
|
|
669
|
+
switch (field.type) {
|
|
670
|
+
case "text":
|
|
671
|
+
case "password":
|
|
672
|
+
return /* @__PURE__ */ import_react.default.createElement(
|
|
673
|
+
import_ui_extensions.Input,
|
|
674
|
+
{
|
|
675
|
+
...commonProps,
|
|
676
|
+
type: field.type === "password" ? "password" : "text",
|
|
677
|
+
value: fieldValue || "",
|
|
678
|
+
onChange: fieldOnChange,
|
|
679
|
+
onInput: (v) => handleFieldInput(field.name, v),
|
|
680
|
+
onBlur: (v) => handleFieldBlur(field.name, v)
|
|
681
|
+
}
|
|
682
|
+
);
|
|
683
|
+
case "textarea":
|
|
684
|
+
return /* @__PURE__ */ import_react.default.createElement(
|
|
685
|
+
import_ui_extensions.TextArea,
|
|
686
|
+
{
|
|
687
|
+
...commonProps,
|
|
688
|
+
value: fieldValue || "",
|
|
689
|
+
rows: field.rows,
|
|
690
|
+
cols: field.cols,
|
|
691
|
+
resize: field.resize,
|
|
692
|
+
maxLength: field.maxLength,
|
|
693
|
+
onChange: fieldOnChange,
|
|
694
|
+
onInput: (v) => handleFieldInput(field.name, v),
|
|
695
|
+
onBlur: (v) => handleFieldBlur(field.name, v)
|
|
696
|
+
}
|
|
697
|
+
);
|
|
698
|
+
case "number":
|
|
699
|
+
return /* @__PURE__ */ import_react.default.createElement(
|
|
700
|
+
import_ui_extensions.NumberInput,
|
|
701
|
+
{
|
|
702
|
+
...commonProps,
|
|
703
|
+
value: fieldValue,
|
|
704
|
+
min: field.min,
|
|
705
|
+
max: field.max,
|
|
706
|
+
precision: field.precision,
|
|
707
|
+
formatStyle: field.formatStyle,
|
|
708
|
+
onChange: fieldOnChange,
|
|
709
|
+
onBlur: (v) => handleFieldBlur(field.name, v)
|
|
710
|
+
}
|
|
711
|
+
);
|
|
712
|
+
case "stepper":
|
|
713
|
+
return /* @__PURE__ */ import_react.default.createElement(
|
|
714
|
+
import_ui_extensions.StepperInput,
|
|
715
|
+
{
|
|
716
|
+
...commonProps,
|
|
717
|
+
value: fieldValue,
|
|
718
|
+
min: field.min,
|
|
719
|
+
max: field.max,
|
|
720
|
+
precision: field.precision,
|
|
721
|
+
formatStyle: field.formatStyle,
|
|
722
|
+
stepSize: field.stepSize,
|
|
723
|
+
minValueReachedTooltip: field.minValueReachedTooltip,
|
|
724
|
+
maxValueReachedTooltip: field.maxValueReachedTooltip,
|
|
725
|
+
onChange: fieldOnChange,
|
|
726
|
+
onBlur: (v) => handleFieldBlur(field.name, v)
|
|
727
|
+
}
|
|
728
|
+
);
|
|
729
|
+
case "currency":
|
|
730
|
+
return /* @__PURE__ */ import_react.default.createElement(
|
|
731
|
+
import_ui_extensions.CurrencyInput,
|
|
732
|
+
{
|
|
733
|
+
...commonProps,
|
|
734
|
+
currency: field.currency || "USD",
|
|
735
|
+
value: fieldValue,
|
|
736
|
+
min: field.min,
|
|
737
|
+
max: field.max,
|
|
738
|
+
precision: field.precision,
|
|
739
|
+
onChange: fieldOnChange,
|
|
740
|
+
onBlur: (v) => handleFieldBlur(field.name, v)
|
|
741
|
+
}
|
|
742
|
+
);
|
|
743
|
+
case "date":
|
|
744
|
+
return /* @__PURE__ */ import_react.default.createElement(
|
|
745
|
+
import_ui_extensions.DateInput,
|
|
746
|
+
{
|
|
747
|
+
...commonProps,
|
|
748
|
+
value: fieldValue,
|
|
749
|
+
format: field.format,
|
|
750
|
+
min: field.min,
|
|
751
|
+
max: field.max,
|
|
752
|
+
timezone: field.timezone,
|
|
753
|
+
clearButtonLabel: field.clearButtonLabel,
|
|
754
|
+
todayButtonLabel: field.todayButtonLabel,
|
|
755
|
+
minValidationMessage: field.minValidationMessage,
|
|
756
|
+
maxValidationMessage: field.maxValidationMessage,
|
|
757
|
+
onChange: fieldOnChange,
|
|
758
|
+
onBlur: (v) => handleFieldBlur(field.name, v)
|
|
759
|
+
}
|
|
760
|
+
);
|
|
761
|
+
case "time":
|
|
762
|
+
return /* @__PURE__ */ import_react.default.createElement(
|
|
763
|
+
import_ui_extensions.TimeInput,
|
|
764
|
+
{
|
|
765
|
+
...commonProps,
|
|
766
|
+
value: fieldValue,
|
|
767
|
+
interval: field.interval,
|
|
768
|
+
min: field.min,
|
|
769
|
+
max: field.max,
|
|
770
|
+
timezone: field.timezone,
|
|
771
|
+
onChange: fieldOnChange,
|
|
772
|
+
onBlur: (v) => handleFieldBlur(field.name, v)
|
|
773
|
+
}
|
|
774
|
+
);
|
|
775
|
+
case "datetime": {
|
|
776
|
+
const dateVal = fieldValue ? fieldValue.date || fieldValue : void 0;
|
|
777
|
+
const timeVal = fieldValue ? fieldValue.time || void 0 : void 0;
|
|
778
|
+
return /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", gap: "sm" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Box, { flex: 1 }, /* @__PURE__ */ import_react.default.createElement(
|
|
779
|
+
import_ui_extensions.DateInput,
|
|
780
|
+
{
|
|
781
|
+
...commonProps,
|
|
782
|
+
name: `${field.name}-date`,
|
|
783
|
+
label: field.label,
|
|
784
|
+
format: field.format,
|
|
785
|
+
value: dateVal,
|
|
786
|
+
min: field.min,
|
|
787
|
+
max: field.max,
|
|
788
|
+
timezone: field.timezone,
|
|
789
|
+
clearButtonLabel: field.clearButtonLabel,
|
|
790
|
+
todayButtonLabel: field.todayButtonLabel,
|
|
791
|
+
minValidationMessage: field.minValidationMessage,
|
|
792
|
+
maxValidationMessage: field.maxValidationMessage,
|
|
793
|
+
onChange: (v) => {
|
|
794
|
+
handleFieldChange(field.name, { ...fieldValue, date: v, time: timeVal });
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
)), /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Box, { flex: 1 }, /* @__PURE__ */ import_react.default.createElement(
|
|
798
|
+
import_ui_extensions.TimeInput,
|
|
799
|
+
{
|
|
800
|
+
name: `${field.name}-time`,
|
|
801
|
+
label: "Time",
|
|
802
|
+
description: field.description,
|
|
803
|
+
tooltip: field.tooltip,
|
|
804
|
+
readOnly: isReadOnly,
|
|
805
|
+
error: hasError,
|
|
806
|
+
value: timeVal,
|
|
807
|
+
interval: field.interval,
|
|
808
|
+
timezone: field.timezone,
|
|
809
|
+
onChange: (v) => {
|
|
810
|
+
handleFieldChange(field.name, { ...fieldValue, date: dateVal, time: v });
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
)));
|
|
814
|
+
}
|
|
815
|
+
case "select":
|
|
816
|
+
return /* @__PURE__ */ import_react.default.createElement(
|
|
817
|
+
import_ui_extensions.Select,
|
|
818
|
+
{
|
|
819
|
+
...commonProps,
|
|
820
|
+
value: fieldValue,
|
|
821
|
+
options,
|
|
822
|
+
variant: field.variant,
|
|
823
|
+
onChange: fieldOnChange
|
|
824
|
+
}
|
|
825
|
+
);
|
|
826
|
+
case "multiselect":
|
|
827
|
+
return /* @__PURE__ */ import_react.default.createElement(
|
|
828
|
+
import_ui_extensions.MultiSelect,
|
|
829
|
+
{
|
|
830
|
+
...commonProps,
|
|
831
|
+
value: fieldValue || [],
|
|
832
|
+
options,
|
|
833
|
+
onChange: fieldOnChange
|
|
834
|
+
}
|
|
835
|
+
);
|
|
836
|
+
case "toggle":
|
|
837
|
+
return /* @__PURE__ */ import_react.default.createElement(
|
|
838
|
+
import_ui_extensions.Toggle,
|
|
839
|
+
{
|
|
840
|
+
name: field.name,
|
|
841
|
+
label: field.label,
|
|
842
|
+
checked: !!fieldValue,
|
|
843
|
+
size: field.size || "md",
|
|
844
|
+
labelDisplay: field.labelDisplay || "top",
|
|
845
|
+
textChecked: field.textChecked,
|
|
846
|
+
textUnchecked: field.textUnchecked,
|
|
847
|
+
readonly: isReadOnly,
|
|
848
|
+
onChange: fieldOnChange,
|
|
849
|
+
...field.fieldProps || {}
|
|
850
|
+
}
|
|
851
|
+
);
|
|
852
|
+
case "checkbox":
|
|
853
|
+
return /* @__PURE__ */ import_react.default.createElement(
|
|
854
|
+
import_ui_extensions.Checkbox,
|
|
855
|
+
{
|
|
856
|
+
name: field.name,
|
|
857
|
+
checked: !!fieldValue,
|
|
858
|
+
description: field.description,
|
|
859
|
+
readOnly: isReadOnly,
|
|
860
|
+
inline: field.inline,
|
|
861
|
+
variant: field.variant,
|
|
862
|
+
onChange: fieldOnChange,
|
|
863
|
+
...field.fieldProps || {}
|
|
864
|
+
},
|
|
865
|
+
field.label
|
|
866
|
+
);
|
|
867
|
+
case "checkboxGroup":
|
|
868
|
+
return /* @__PURE__ */ import_react.default.createElement(
|
|
869
|
+
import_ui_extensions.ToggleGroup,
|
|
870
|
+
{
|
|
871
|
+
...commonProps,
|
|
872
|
+
toggleType: "checkboxList",
|
|
873
|
+
value: fieldValue || [],
|
|
874
|
+
options,
|
|
875
|
+
inline: field.inline,
|
|
876
|
+
variant: field.variant,
|
|
877
|
+
onChange: fieldOnChange
|
|
878
|
+
}
|
|
879
|
+
);
|
|
880
|
+
case "radioGroup":
|
|
881
|
+
return /* @__PURE__ */ import_react.default.createElement(
|
|
882
|
+
import_ui_extensions.ToggleGroup,
|
|
883
|
+
{
|
|
884
|
+
...commonProps,
|
|
885
|
+
toggleType: "radioButtonList",
|
|
886
|
+
value: fieldValue,
|
|
887
|
+
options,
|
|
888
|
+
inline: field.inline,
|
|
889
|
+
variant: field.variant,
|
|
890
|
+
onChange: fieldOnChange
|
|
891
|
+
}
|
|
892
|
+
);
|
|
893
|
+
case "repeater": {
|
|
894
|
+
const rows = Array.isArray(fieldValue) ? fieldValue : [];
|
|
895
|
+
const subFields = field.fields || [];
|
|
896
|
+
const minRows = field.min || 0;
|
|
897
|
+
const maxRows = field.max || Infinity;
|
|
898
|
+
const canAdd = rows.length < maxRows && !isReadOnly;
|
|
899
|
+
const canRemove = rows.length > minRows && !isReadOnly;
|
|
900
|
+
const addRow = () => {
|
|
901
|
+
const emptyRow = {};
|
|
902
|
+
for (const sf of subFields) {
|
|
903
|
+
emptyRow[sf.name] = sf.defaultValue !== void 0 ? sf.defaultValue : getEmptyValue(sf);
|
|
904
|
+
}
|
|
905
|
+
handleFieldChange(field.name, [...rows, emptyRow]);
|
|
906
|
+
};
|
|
907
|
+
const removeRow = (idx) => {
|
|
908
|
+
handleFieldChange(field.name, rows.filter((_, i) => i !== idx));
|
|
909
|
+
};
|
|
910
|
+
const updateRow = (idx, subName, subValue) => {
|
|
911
|
+
const updated = rows.map(
|
|
912
|
+
(row, i) => i === idx ? { ...row, [subName]: subValue } : row
|
|
913
|
+
);
|
|
914
|
+
handleFieldChange(field.name, updated);
|
|
915
|
+
};
|
|
916
|
+
return /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "column", gap: "xs" }, field.label && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { format: { fontWeight: "demibold" } }, field.label, isRequired ? " *" : ""), field.description && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { variant: "microcopy" }, field.description), rows.map((row, rowIdx) => /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { key: rowIdx, direction: "row", gap: "xs", align: "end" }, subFields.map((sf) => {
|
|
917
|
+
const sfValue = row[sf.name];
|
|
918
|
+
const sfLabel = rowIdx === 0 ? sf.label : void 0;
|
|
919
|
+
const sfOptions = resolveOptions(sf, formValues);
|
|
920
|
+
const sfProps = {
|
|
921
|
+
name: `${field.name}-${rowIdx}-${sf.name}`,
|
|
922
|
+
label: sfLabel,
|
|
923
|
+
placeholder: sf.placeholder,
|
|
924
|
+
readOnly: isReadOnly,
|
|
925
|
+
...sf.fieldProps || {}
|
|
926
|
+
};
|
|
927
|
+
let sfElement;
|
|
928
|
+
switch (sf.type) {
|
|
929
|
+
case "select":
|
|
930
|
+
sfElement = /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Select, { ...sfProps, value: sfValue, options: sfOptions, onChange: (v) => updateRow(rowIdx, sf.name, v) });
|
|
931
|
+
break;
|
|
932
|
+
case "number":
|
|
933
|
+
sfElement = /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.NumberInput, { ...sfProps, value: sfValue, onChange: (v) => updateRow(rowIdx, sf.name, v) });
|
|
934
|
+
break;
|
|
935
|
+
case "checkbox":
|
|
936
|
+
sfElement = /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Checkbox, { ...sfProps, checked: !!sfValue, onChange: (v) => updateRow(rowIdx, sf.name, v) }, sf.label);
|
|
937
|
+
break;
|
|
938
|
+
default:
|
|
939
|
+
sfElement = /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Input, { ...sfProps, value: sfValue || "", onChange: (v) => updateRow(rowIdx, sf.name, v) });
|
|
940
|
+
}
|
|
941
|
+
return /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Box, { key: sf.name, flex: 1 }, sfElement);
|
|
942
|
+
}), canRemove && /* @__PURE__ */ import_react.default.createElement(
|
|
943
|
+
import_ui_extensions.Button,
|
|
944
|
+
{
|
|
945
|
+
variant: "secondary",
|
|
946
|
+
size: "xs",
|
|
947
|
+
onClick: () => removeRow(rowIdx)
|
|
948
|
+
},
|
|
949
|
+
"Remove"
|
|
950
|
+
))), canAdd && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Button, { variant: "secondary", size: "sm", onClick: addRow }, "+ Add"), hasError && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { variant: "microcopy" }, fieldError));
|
|
951
|
+
}
|
|
952
|
+
default:
|
|
953
|
+
return /* @__PURE__ */ import_react.default.createElement(
|
|
954
|
+
import_ui_extensions.Input,
|
|
955
|
+
{
|
|
956
|
+
...commonProps,
|
|
957
|
+
value: fieldValue || "",
|
|
958
|
+
onChange: fieldOnChange,
|
|
959
|
+
onInput: (v) => handleFieldInput(field.name, v),
|
|
960
|
+
onBlur: (v) => handleFieldBlur(field.name, v)
|
|
961
|
+
}
|
|
962
|
+
);
|
|
963
|
+
}
|
|
964
|
+
};
|
|
965
|
+
const getFieldColSpan = (field) => {
|
|
966
|
+
if (field.colSpan != null) return Math.min(field.colSpan, columns);
|
|
967
|
+
if (field.width === "full" && columns > 1) return columns;
|
|
968
|
+
return 1;
|
|
969
|
+
};
|
|
970
|
+
const getDependents = (parentField) => visibleFields.filter((f) => f.dependsOn === parentField.name && f.name !== parentField.name);
|
|
971
|
+
const isDependent = (field) => field.dependsOn && visibleFields.some((f) => f.name === field.dependsOn && f.name !== field.name);
|
|
972
|
+
const renderDependentGroup = (parentField, dependents) => {
|
|
973
|
+
const firstWithLabel = dependents.find((f) => f.dependsOnLabel) || dependents[0];
|
|
974
|
+
const firstWithMessage = dependents.find((f) => f.dependsOnMessage) || dependents[0];
|
|
975
|
+
const groupLabel = firstWithLabel.dependsOnLabel || "Dependent properties";
|
|
976
|
+
const rawMessage = firstWithMessage.dependsOnMessage;
|
|
977
|
+
const tooltipMessage = typeof rawMessage === "function" ? rawMessage(parentField.label) : rawMessage || "";
|
|
978
|
+
return /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Tile, { key: `dep-${parentField.name}`, compact: true }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "column", gap }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", align: "center", gap: "xs" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { format: { fontWeight: "demibold" } }, groupLabel, " ", tooltipMessage && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Link, { inline: true, variant: "dark", overlay: /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Tooltip, null, tooltipMessage) }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Icon, { name: "info" })))), dependents.map((dep) => /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, { key: dep.name }, renderField(dep)))));
|
|
979
|
+
};
|
|
980
|
+
const renderGridLayout = (fieldSubset) => {
|
|
981
|
+
const fieldList = fieldSubset || visibleFields;
|
|
982
|
+
const elements = [];
|
|
983
|
+
let currentRow = [];
|
|
984
|
+
let currentRowSpan = 0;
|
|
985
|
+
const flushRow = () => {
|
|
986
|
+
if (currentRow.length === 0) return;
|
|
987
|
+
const totalSpan = currentRow.reduce((s, f) => s + getFieldColSpan(f), 0);
|
|
988
|
+
const remainder = columns - totalSpan;
|
|
989
|
+
elements.push(
|
|
990
|
+
/* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { key: `row-${currentRow[0].name}`, direction: "row", gap }, currentRow.map((f) => /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Box, { key: f.name, flex: getFieldColSpan(f) }, renderField(f))), remainder > 0 && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Box, { flex: remainder }))
|
|
991
|
+
);
|
|
992
|
+
currentRow = [];
|
|
993
|
+
currentRowSpan = 0;
|
|
994
|
+
};
|
|
995
|
+
for (const field of fieldList) {
|
|
996
|
+
if (isDependent(field)) continue;
|
|
997
|
+
const span = getFieldColSpan(field);
|
|
998
|
+
if (span >= columns) {
|
|
999
|
+
flushRow();
|
|
1000
|
+
elements.push(
|
|
1001
|
+
/* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, { key: field.name }, renderField(field))
|
|
1002
|
+
);
|
|
1003
|
+
} else {
|
|
1004
|
+
if (currentRowSpan + span > columns) flushRow();
|
|
1005
|
+
currentRow.push(field);
|
|
1006
|
+
currentRowSpan += span;
|
|
1007
|
+
if (currentRowSpan >= columns) flushRow();
|
|
1008
|
+
}
|
|
1009
|
+
const dependents = getDependents(field);
|
|
1010
|
+
if (dependents.length > 0) {
|
|
1011
|
+
flushRow();
|
|
1012
|
+
elements.push(renderDependentGroup(field, dependents));
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
flushRow();
|
|
1016
|
+
return elements;
|
|
1017
|
+
};
|
|
1018
|
+
const renderExplicitLayout = () => {
|
|
1019
|
+
const elements = [];
|
|
1020
|
+
const renderedNames = /* @__PURE__ */ new Set();
|
|
1021
|
+
for (let rowIdx = 0; rowIdx < layout.length; rowIdx++) {
|
|
1022
|
+
const row = layout[rowIdx];
|
|
1023
|
+
const rowFields = [];
|
|
1024
|
+
for (const entry of row) {
|
|
1025
|
+
const fieldName = typeof entry === "string" ? entry : entry.field;
|
|
1026
|
+
const flexValue = typeof entry === "string" ? 1 : entry.flex || 1;
|
|
1027
|
+
const field = visibleFields.find((f) => f.name === fieldName);
|
|
1028
|
+
if (!field) continue;
|
|
1029
|
+
rowFields.push({ field, flex: flexValue });
|
|
1030
|
+
renderedNames.add(fieldName);
|
|
1031
|
+
}
|
|
1032
|
+
if (rowFields.length === 0) continue;
|
|
1033
|
+
if (rowFields.length === 1) {
|
|
1034
|
+
elements.push(
|
|
1035
|
+
/* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, { key: rowFields[0].field.name }, renderField(rowFields[0].field))
|
|
1036
|
+
);
|
|
1037
|
+
} else {
|
|
1038
|
+
elements.push(
|
|
1039
|
+
/* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { key: `layout-row-${rowIdx}`, direction: "row", gap }, rowFields.map(({ field, flex }) => /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Box, { key: field.name, flex }, renderField(field))))
|
|
1040
|
+
);
|
|
1041
|
+
}
|
|
1042
|
+
for (const { field } of rowFields) {
|
|
1043
|
+
const dependents = getDependents(field).filter((d) => !renderedNames.has(d.name));
|
|
1044
|
+
if (dependents.length > 0) {
|
|
1045
|
+
elements.push(renderDependentGroup(field, dependents));
|
|
1046
|
+
for (const dep of dependents) renderedNames.add(dep.name);
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
for (const field of visibleFields) {
|
|
1051
|
+
if (renderedNames.has(field.name)) continue;
|
|
1052
|
+
if (isDependent(field)) continue;
|
|
1053
|
+
elements.push(
|
|
1054
|
+
/* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, { key: field.name }, renderField(field))
|
|
1055
|
+
);
|
|
1056
|
+
renderedNames.add(field.name);
|
|
1057
|
+
const dependents = getDependents(field).filter((d) => !renderedNames.has(d.name));
|
|
1058
|
+
if (dependents.length > 0) {
|
|
1059
|
+
elements.push(renderDependentGroup(field, dependents));
|
|
1060
|
+
for (const dep of dependents) renderedNames.add(dep.name);
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
return elements;
|
|
1064
|
+
};
|
|
1065
|
+
const renderLegacyLayout = (fieldSubset) => {
|
|
1066
|
+
const fieldList = fieldSubset || visibleFields;
|
|
1067
|
+
const rows = [];
|
|
1068
|
+
let i = 0;
|
|
1069
|
+
while (i < fieldList.length) {
|
|
1070
|
+
const field = fieldList[i];
|
|
1071
|
+
if (field.width === "half" && i + 1 < fieldList.length && fieldList[i + 1].width === "half" && !field.dependsOn) {
|
|
1072
|
+
rows.push({ type: "pair", fields: [fieldList[i], fieldList[i + 1]] });
|
|
1073
|
+
i += 2;
|
|
1074
|
+
} else {
|
|
1075
|
+
rows.push({ type: "single", field });
|
|
1076
|
+
i++;
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
const elements = [];
|
|
1080
|
+
const processedDeps = /* @__PURE__ */ new Set();
|
|
1081
|
+
for (const row of rows) {
|
|
1082
|
+
if (row.type === "pair") {
|
|
1083
|
+
elements.push(
|
|
1084
|
+
/* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { key: `pair-${row.fields[0].name}`, direction: "row", gap: "sm" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Box, { flex: 1 }, renderField(row.fields[0])), /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Box, { flex: 1 }, renderField(row.fields[1])))
|
|
1085
|
+
);
|
|
1086
|
+
} else {
|
|
1087
|
+
const field = row.field;
|
|
1088
|
+
if (processedDeps.has(field.name)) continue;
|
|
1089
|
+
elements.push(
|
|
1090
|
+
/* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, { key: field.name }, renderField(field))
|
|
1091
|
+
);
|
|
1092
|
+
const dependents = getDependents(field);
|
|
1093
|
+
if (dependents.length > 0) {
|
|
1094
|
+
for (const dep of dependents) processedDeps.add(dep.name);
|
|
1095
|
+
elements.push(renderDependentGroup(field, dependents));
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
return elements;
|
|
1100
|
+
};
|
|
1101
|
+
const renderAutoGridLayout = (fieldSubset) => {
|
|
1102
|
+
const fieldList = fieldSubset || visibleFields;
|
|
1103
|
+
const elements = [];
|
|
1104
|
+
let batch = [];
|
|
1105
|
+
const flushBatch = () => {
|
|
1106
|
+
if (batch.length === 0) return;
|
|
1107
|
+
elements.push(
|
|
1108
|
+
/* @__PURE__ */ import_react.default.createElement(import_ui_extensions.AutoGrid, { key: `ag-${batch[0].name}`, columnWidth, flexible: true, gap }, batch.map((f) => /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, { key: f.name }, renderField(f))))
|
|
1109
|
+
);
|
|
1110
|
+
batch = [];
|
|
1111
|
+
};
|
|
1112
|
+
for (const field of fieldList) {
|
|
1113
|
+
if (isDependent(field)) continue;
|
|
1114
|
+
batch.push(field);
|
|
1115
|
+
const dependents = getDependents(field);
|
|
1116
|
+
if (dependents.length > 0) {
|
|
1117
|
+
flushBatch();
|
|
1118
|
+
elements.push(renderDependentGroup(field, dependents));
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
flushBatch();
|
|
1122
|
+
return elements;
|
|
1123
|
+
};
|
|
1124
|
+
const wrapWithGroups = (fieldList, renderFn) => {
|
|
1125
|
+
const hasGroups = fieldList.some((f) => f.group);
|
|
1126
|
+
if (!hasGroups) return renderFn(fieldList);
|
|
1127
|
+
const chunks = [];
|
|
1128
|
+
let currentGroup = void 0;
|
|
1129
|
+
let currentChunk = [];
|
|
1130
|
+
for (const field of fieldList) {
|
|
1131
|
+
const fieldGroup = field.group || void 0;
|
|
1132
|
+
if (fieldGroup !== currentGroup && currentChunk.length > 0) {
|
|
1133
|
+
chunks.push({ group: currentGroup, fields: [...currentChunk] });
|
|
1134
|
+
currentChunk = [];
|
|
1135
|
+
}
|
|
1136
|
+
currentGroup = fieldGroup;
|
|
1137
|
+
currentChunk.push(field);
|
|
1138
|
+
}
|
|
1139
|
+
if (currentChunk.length > 0) {
|
|
1140
|
+
chunks.push({ group: currentGroup, fields: currentChunk });
|
|
1141
|
+
}
|
|
1142
|
+
const elements = [];
|
|
1143
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
1144
|
+
const chunk = chunks[i];
|
|
1145
|
+
if (i > 0) {
|
|
1146
|
+
elements.push(/* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Divider, { key: `group-div-${i}` }));
|
|
1147
|
+
}
|
|
1148
|
+
if (chunk.group) {
|
|
1149
|
+
elements.push(
|
|
1150
|
+
/* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { key: `group-label-${i}`, format: { fontWeight: "demibold" } }, chunk.group)
|
|
1151
|
+
);
|
|
1152
|
+
}
|
|
1153
|
+
const chunkElements = renderFn(chunk.fields);
|
|
1154
|
+
if (Array.isArray(chunkElements)) {
|
|
1155
|
+
elements.push(...chunkElements);
|
|
1156
|
+
} else {
|
|
1157
|
+
elements.push(chunkElements);
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
return elements;
|
|
1161
|
+
};
|
|
1162
|
+
const renderFieldSubset = (fieldSubset) => {
|
|
1163
|
+
if (layout && fieldSubset === visibleFields) return renderExplicitLayout();
|
|
1164
|
+
if (columnWidth) return renderAutoGridLayout(fieldSubset);
|
|
1165
|
+
if (columns > 1) return renderGridLayout(fieldSubset);
|
|
1166
|
+
return renderLegacyLayout(fieldSubset);
|
|
1167
|
+
};
|
|
1168
|
+
const renderSections = () => {
|
|
1169
|
+
const hasSections = Array.isArray(sections) && sections.length > 0;
|
|
1170
|
+
if (!hasSections) return null;
|
|
1171
|
+
const sectionFieldNames = /* @__PURE__ */ new Set();
|
|
1172
|
+
for (const sec of sections) {
|
|
1173
|
+
if (sec.fields) for (const name of sec.fields) sectionFieldNames.add(name);
|
|
1174
|
+
}
|
|
1175
|
+
const elements = [];
|
|
1176
|
+
for (const sec of sections) {
|
|
1177
|
+
const sectionFields = sec.fields ? visibleFields.filter((f) => sec.fields.includes(f.name)) : [];
|
|
1178
|
+
if (sectionFields.length === 0) continue;
|
|
1179
|
+
const accordionContent = /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "column", gap }, renderFieldSubset(sectionFields));
|
|
1180
|
+
const accordion = /* @__PURE__ */ import_react.default.createElement(
|
|
1181
|
+
import_ui_extensions.Accordion,
|
|
1182
|
+
{
|
|
1183
|
+
key: sec.id,
|
|
1184
|
+
title: sec.label,
|
|
1185
|
+
size: "sm",
|
|
1186
|
+
defaultOpen: sec.defaultOpen !== false
|
|
1187
|
+
},
|
|
1188
|
+
accordionContent
|
|
1189
|
+
);
|
|
1190
|
+
if (sec.info) {
|
|
1191
|
+
elements.push(
|
|
1192
|
+
/* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { key: sec.id, direction: "row", align: "start", gap: "flush" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Box, { flex: 1 }, accordion), /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Link, { overlay: /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Tooltip, null, sec.info) }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Icon, { name: "info", size: "sm", screenReaderText: sec.info })))
|
|
1193
|
+
);
|
|
1194
|
+
} else {
|
|
1195
|
+
elements.push(accordion);
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
const unsectionedFields = visibleFields.filter(
|
|
1199
|
+
(f) => !sectionFieldNames.has(f.name)
|
|
1200
|
+
);
|
|
1201
|
+
if (unsectionedFields.length > 0) {
|
|
1202
|
+
elements.push(...renderFieldSubset(unsectionedFields));
|
|
1203
|
+
}
|
|
1204
|
+
return elements;
|
|
1205
|
+
};
|
|
1206
|
+
const renderFieldLayout = () => {
|
|
1207
|
+
const hasSections = Array.isArray(sections) && sections.length > 0;
|
|
1208
|
+
if (hasSections) return renderSections();
|
|
1209
|
+
const hasGroups = visibleFields.some((f) => f.group);
|
|
1210
|
+
if (hasGroups && !layout) {
|
|
1211
|
+
return wrapWithGroups(visibleFields, renderFieldSubset);
|
|
1212
|
+
}
|
|
1213
|
+
if (layout) return renderExplicitLayout();
|
|
1214
|
+
return renderFieldSubset(visibleFields);
|
|
1215
|
+
};
|
|
1216
|
+
const renderButtons = () => {
|
|
1217
|
+
if (submitPosition === "none" || formReadOnly) return null;
|
|
1218
|
+
const isLastStep = !isMultiStep || currentStep === steps.length - 1;
|
|
1219
|
+
const isFirstStep = !isMultiStep || currentStep === 0;
|
|
1220
|
+
if (isMultiStep) {
|
|
1221
|
+
return /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", justify: "between", align: "center" }, !isFirstStep ? /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Button, { variant: "secondary", onClick: handleBack, disabled }, "Back") : showCancel ? /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Button, { variant: "secondary", onClick: onCancel, disabled }, cancelLabel) : /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, null, " "), /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Inline, { gap: "small" }, /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Text, { variant: "microcopy" }, "Step ", currentStep + 1, " of ", steps.length), isLastStep ? /* @__PURE__ */ import_react.default.createElement(
|
|
1222
|
+
import_ui_extensions.LoadingButton,
|
|
1223
|
+
{
|
|
1224
|
+
variant: submitVariant,
|
|
1225
|
+
loading: isLoading,
|
|
1226
|
+
onClick: handleSubmit,
|
|
1227
|
+
disabled
|
|
1228
|
+
},
|
|
1229
|
+
submitLabel
|
|
1230
|
+
) : /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Button, { variant: "primary", onClick: handleNext, disabled }, "Next")));
|
|
1231
|
+
}
|
|
1232
|
+
return /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "row", justify: showCancel ? "between" : "start", gap: "sm" }, showCancel && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Button, { variant: "secondary", onClick: onCancel, disabled }, cancelLabel), /* @__PURE__ */ import_react.default.createElement(
|
|
1233
|
+
import_ui_extensions.LoadingButton,
|
|
1234
|
+
{
|
|
1235
|
+
variant: submitVariant,
|
|
1236
|
+
type: noFormWrapper ? "button" : "submit",
|
|
1237
|
+
loading: isLoading,
|
|
1238
|
+
onClick: noFormWrapper ? handleSubmit : void 0,
|
|
1239
|
+
disabled
|
|
1240
|
+
},
|
|
1241
|
+
submitLabel
|
|
1242
|
+
));
|
|
1243
|
+
};
|
|
1244
|
+
const formContent = /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Flex, { direction: "column", gap }, isMultiStep && showStepIndicator && /* @__PURE__ */ import_react.default.createElement(
|
|
1245
|
+
import_ui_extensions.StepIndicator,
|
|
1246
|
+
{
|
|
1247
|
+
currentStep,
|
|
1248
|
+
stepNames: steps.map((s) => s.title)
|
|
1249
|
+
}
|
|
1250
|
+
), formReadOnly && readOnlyMessage && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Alert, { title: "Read Only", variant: "warning" }, readOnlyMessage), formError && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Alert, { title: "Error", variant: "danger" }, typeof formError === "string" ? formError : void 0), formSuccess && /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Alert, { title: "Success", variant: "success" }, formSuccess), isMultiStep && steps[currentStep] && steps[currentStep].render ? steps[currentStep].render({
|
|
1251
|
+
values: formValues,
|
|
1252
|
+
goNext: handleNext,
|
|
1253
|
+
goBack: handleBack,
|
|
1254
|
+
goTo: handleGoTo
|
|
1255
|
+
}) : (
|
|
1256
|
+
/* Field layout */
|
|
1257
|
+
renderFieldLayout()
|
|
1258
|
+
), renderButtons());
|
|
1259
|
+
if (noFormWrapper) {
|
|
1260
|
+
return formContent;
|
|
1261
|
+
}
|
|
1262
|
+
return /* @__PURE__ */ import_react.default.createElement(import_ui_extensions.Form, { onSubmit: handleSubmit, autoComplete: props.autoComplete }, formContent);
|
|
1263
|
+
});
|
|
1264
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1265
|
+
0 && (module.exports = {
|
|
1266
|
+
FormBuilder,
|
|
1267
|
+
useFormPrefill
|
|
1268
|
+
});
|