@nwon/react-mui-hook-form 3.1.1 → 3.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/LoadingButton/LoadingButton.d.ts +6 -0
- package/dist/LoadingButton/LoadingButtonInterfaces.d.ts +33 -0
- package/dist/LoadingButton/getLoadingButtonState.d.ts +20 -0
- package/dist/LoadingButton/index.d.ts +4 -0
- package/dist/formFields/HookFormProvider.d.ts +7 -0
- package/dist/formFields/InputEmail/InputEmail.d.ts +10 -0
- package/dist/formFields/InputPasswordSet/InputPasswordSet.d.ts +8 -0
- package/dist/formFields/InputPasswordSet/InputPasswordSetInterfaces.d.ts +18 -0
- package/dist/formFields/TranslatedString.d.ts +13 -0
- package/dist/formFields/baseTypes/BaseInputProps.d.ts +33 -0
- package/dist/formFields/baseTypes/InputAutocomplete/InputAutocomplete.d.ts +9 -0
- package/dist/formFields/baseTypes/InputAutocomplete/InputAutocompleteInterfaces.d.ts +24 -0
- package/dist/formFields/baseTypes/InputAutocomplete/useSortedOptionsWithGroup.d.ts +9 -0
- package/dist/formFields/baseTypes/InputCheckbox/InputCheckbox.d.ts +18 -0
- package/dist/formFields/baseTypes/InputDatePicker/InputDatePicker.d.ts +16 -0
- package/dist/formFields/baseTypes/InputDateTimePicker/InputDateTimePicker.d.ts +16 -0
- package/dist/formFields/baseTypes/InputNumber/InputNumber.d.ts +15 -0
- package/dist/formFields/baseTypes/InputOutlinedInput/InputOutlinedInput.d.ts +13 -0
- package/dist/formFields/baseTypes/InputRadio/InputRadio.d.ts +13 -0
- package/dist/formFields/baseTypes/InputSelect/InputSelect.d.ts +26 -0
- package/dist/formFields/baseTypes/InputSelectFromEnum/InputSelectFromEnum.d.ts +15 -0
- package/dist/formFields/baseTypes/InputSelectFromEnum/InputSelectFromEnumInterfaces.d.ts +10 -0
- package/dist/formFields/baseTypes/InputSlider/InputSlider.d.ts +13 -0
- package/dist/formFields/baseTypes/InputSwitch/InputSwitch.d.ts +11 -0
- package/dist/formFields/baseTypes/InputTextField/InputTextField.d.ts +13 -0
- package/dist/formFields/baseTypes/InputTimePicker/InputTimePicker.d.ts +16 -0
- package/dist/formFields/baseTypes/InputToggleButton/InputToggleButton.d.ts +12 -0
- package/dist/formFields/baseTypes/InputToggleButtonGroup/InputToggleButtonGroup.d.ts +34 -0
- package/dist/formFields/baseTypes/useEffectFieldUnregisterOnUnmount.d.ts +12 -0
- package/dist/formFields/errorMessages.d.ts +1 -0
- package/dist/formFields/errors/FormFieldError.d.ts +11 -0
- package/dist/formFields/errors/FormFieldErrorDisplay.d.ts +11 -0
- package/dist/formFields/errors/getFormErrorForNestedKeys.d.ts +14 -0
- package/dist/formFields/errors/useFormFieldError.d.ts +7 -0
- package/dist/formFields/errors/useFormFieldHasError.d.ts +6 -0
- package/dist/formFields/formHelper/syncSubmit.d.ts +15 -0
- package/dist/formFields/formHelper/useHookForm.d.ts +58 -0
- package/dist/formFields/formHelper/useHookFormContext.d.ts +9 -0
- package/dist/formFields/formHelper/useMergedFormField.d.ts +6 -0
- package/dist/formFields/formHelper/useSortedOptionsWithGroup.d.ts +5 -0
- package/dist/formFields/formHelper/useTranslateString.d.ts +8 -0
- package/dist/formFields/index.d.ts +49 -0
- package/dist/formFields/inputTypes.d.ts +76 -0
- package/dist/packageExports.d.ts +9 -0
- package/dist/packageExports.js +1766 -0
- package/dist/packageExports.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,1766 @@
|
|
|
1
|
+
import { CheckBoxOutlineBlank, CheckBox } from '@mui/icons-material';
|
|
2
|
+
import { FormHelperText, Checkbox, FormControl, Autocomplete, TextField, FormControlLabel, Box, InputLabel, OutlinedInput, FormLabel, RadioGroup, Radio, Select, MenuItem, Typography, Stack, Tooltip, ListSubheader, Slider, Switch, ToggleButton, ToggleButtonGroup } from '@mui/material';
|
|
3
|
+
import { useFormControl } from '@mui/material/FormControl';
|
|
4
|
+
import { filterNoUndefinedNoNull, uppercaseFirstLetter, assertTruthy, objectHasKey, rethrow } from '@nvon/baseline';
|
|
5
|
+
import { useCallback, useMemo, useState, useEffect } from 'react';
|
|
6
|
+
import { useFormContext, useWatch, Controller, useForm, useFieldArray, FormProvider } from 'react-hook-form';
|
|
7
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
8
|
+
import { identity } from 'lodash-es';
|
|
9
|
+
import { t } from 'i18next';
|
|
10
|
+
import { DatePicker, DateTimePicker, TimePicker } from '@mui/x-date-pickers';
|
|
11
|
+
import dayjs from 'dayjs';
|
|
12
|
+
import { NumericFormat } from 'react-number-format';
|
|
13
|
+
import { LoadingButton as LoadingButton$1 } from '@mui/lab';
|
|
14
|
+
|
|
15
|
+
const formContextNull = "FormContext is null. This should not happen. You may need to use the HookFormProvider instead of the FormProvider exported by react-hook-form";
|
|
16
|
+
|
|
17
|
+
const FormFieldErrorDisplay = ({
|
|
18
|
+
error,
|
|
19
|
+
formHelperTextProps
|
|
20
|
+
}) => {
|
|
21
|
+
if (Array.isArray(error)) {
|
|
22
|
+
return /*#__PURE__*/jsx(FormHelperText, {
|
|
23
|
+
...formHelperTextProps,
|
|
24
|
+
error: true,
|
|
25
|
+
children: error.map(e => e.message).filter(filterNoUndefinedNoNull).join(", ")
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
if (error?.message) {
|
|
29
|
+
return /*#__PURE__*/jsx(FormHelperText, {
|
|
30
|
+
...formHelperTextProps,
|
|
31
|
+
error: true,
|
|
32
|
+
children: error?.message
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Wrapper around react-hook-form's useFormContext.
|
|
40
|
+
*
|
|
41
|
+
* Should behave like the original function, only exposing a "fieldName"
|
|
42
|
+
* function on top which is used to validate field names.
|
|
43
|
+
*/
|
|
44
|
+
const useHookFormContext = () => {
|
|
45
|
+
const form = useFormContext();
|
|
46
|
+
return {
|
|
47
|
+
...form,
|
|
48
|
+
fieldName: identity
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Get the field errors for a (possibly nested) field name.
|
|
54
|
+
*
|
|
55
|
+
* Checking `formState.errors[fieldName]` only works if fieldName is not a path
|
|
56
|
+
* (e.g. Does not include dots).
|
|
57
|
+
*
|
|
58
|
+
* Did not find a built-in way to do this … there might be one though.
|
|
59
|
+
*
|
|
60
|
+
* Supports both dot notation (e.g., "response.receipts.0.field") and
|
|
61
|
+
* bracket notation (e.g., "response.receipts[0].field") for array fields.
|
|
62
|
+
*/
|
|
63
|
+
const getFormErrorForNestedKeys = (errors, fieldName) => {
|
|
64
|
+
// Parse the field name to handle both dot notation and bracket notation
|
|
65
|
+
// e.g., "response.receipts.0.field" or "response.receipts[0].field"
|
|
66
|
+
const keys = [];
|
|
67
|
+
let currentKey = "";
|
|
68
|
+
let i = 0;
|
|
69
|
+
while (i < fieldName.length) {
|
|
70
|
+
const char = fieldName[i];
|
|
71
|
+
if (char === "[") {
|
|
72
|
+
// Save the current key if any
|
|
73
|
+
if (currentKey) {
|
|
74
|
+
keys.push(currentKey);
|
|
75
|
+
currentKey = "";
|
|
76
|
+
}
|
|
77
|
+
// Find the closing bracket
|
|
78
|
+
i++; // Skip the opening bracket
|
|
79
|
+
let bracketContent = "";
|
|
80
|
+
while (i < fieldName.length && fieldName[i] !== "]") {
|
|
81
|
+
bracketContent += fieldName[i];
|
|
82
|
+
i++;
|
|
83
|
+
}
|
|
84
|
+
// Parse the bracket content as a number if possible, otherwise as string
|
|
85
|
+
const numIndex = Number(bracketContent);
|
|
86
|
+
keys.push(isNaN(numIndex) ? bracketContent : numIndex);
|
|
87
|
+
i++; // Skip the closing bracket
|
|
88
|
+
} else if (char === ".") {
|
|
89
|
+
// Save the current key if any
|
|
90
|
+
if (currentKey) {
|
|
91
|
+
keys.push(currentKey);
|
|
92
|
+
currentKey = "";
|
|
93
|
+
}
|
|
94
|
+
i++; // Skip the dot
|
|
95
|
+
} else {
|
|
96
|
+
currentKey += char;
|
|
97
|
+
i++;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// Don't forget the last key
|
|
101
|
+
if (currentKey) {
|
|
102
|
+
keys.push(currentKey);
|
|
103
|
+
}
|
|
104
|
+
//
|
|
105
|
+
// To my best knowledge, this works as expected.
|
|
106
|
+
//
|
|
107
|
+
// It recursively walks the error object by the given keys
|
|
108
|
+
// until the lowest level is reached, or undefined returned.
|
|
109
|
+
//
|
|
110
|
+
// Handles both object properties and array indices.
|
|
111
|
+
//
|
|
112
|
+
// Typescript is just not very happy with the dynamic nature of this,
|
|
113
|
+
// hence the errors.
|
|
114
|
+
//
|
|
115
|
+
/* eslint-disable @typescript-eslint/ban-ts-comment,@typescript-eslint/no-unsafe-return */
|
|
116
|
+
// @ts-expect-error
|
|
117
|
+
return keys.reduce((errorObj, pathKey) => {
|
|
118
|
+
if (errorObj === undefined || errorObj === null) {
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
// Handle array access
|
|
122
|
+
if (typeof pathKey === "number") {
|
|
123
|
+
return Array.isArray(errorObj) ? errorObj[pathKey] :
|
|
124
|
+
// @ts-expect-error
|
|
125
|
+
errorObj?.[pathKey];
|
|
126
|
+
}
|
|
127
|
+
// Handle object property access
|
|
128
|
+
// @ts-expect-error
|
|
129
|
+
return errorObj?.[pathKey];
|
|
130
|
+
}, errors);
|
|
131
|
+
/* eslint-enable */
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Returns the error for a given field name.
|
|
136
|
+
*
|
|
137
|
+
* @throws Error when FormContext is null.
|
|
138
|
+
*/
|
|
139
|
+
const useFormFieldError = fieldName => {
|
|
140
|
+
const form = useHookFormContext();
|
|
141
|
+
if (!form) {
|
|
142
|
+
throw Error(formContextNull);
|
|
143
|
+
}
|
|
144
|
+
const {
|
|
145
|
+
formState
|
|
146
|
+
} = form;
|
|
147
|
+
const errors = getFormErrorForNestedKeys(formState.errors, fieldName);
|
|
148
|
+
return errors;
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const FormFieldError = ({
|
|
152
|
+
fieldName,
|
|
153
|
+
formHelperTextProps
|
|
154
|
+
}) => {
|
|
155
|
+
const error = useFormFieldError(fieldName);
|
|
156
|
+
return /*#__PURE__*/jsx(FormFieldErrorDisplay, {
|
|
157
|
+
error: error,
|
|
158
|
+
formHelperTextProps: formHelperTextProps
|
|
159
|
+
});
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Returns whether or not a field has an error.
|
|
164
|
+
*
|
|
165
|
+
* @throws Error when FormContext is null.
|
|
166
|
+
*/
|
|
167
|
+
const useFormFieldHasError = fieldName => !!useFormFieldError(fieldName);
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Return a function which turns our TranslatedString into a plain string.
|
|
171
|
+
*
|
|
172
|
+
* @throws Error if no t was provided but a translated string.
|
|
173
|
+
*/
|
|
174
|
+
const useTranslateString = () => {
|
|
175
|
+
if (!t) {
|
|
176
|
+
throw Error("If you pass in a translated validate function you need to provide a t");
|
|
177
|
+
}
|
|
178
|
+
return useCallback(input => {
|
|
179
|
+
return typeof input === "string" ? input : input(t);
|
|
180
|
+
}, []);
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Merge a form field with defaults.
|
|
185
|
+
*/
|
|
186
|
+
const useMergedFormField = (
|
|
187
|
+
/**
|
|
188
|
+
* Form fields, typically those passed into a component.
|
|
189
|
+
*
|
|
190
|
+
*/
|
|
191
|
+
formField, t,
|
|
192
|
+
/**
|
|
193
|
+
* Additional defaults. Duplicate values will be overridden by the formField.
|
|
194
|
+
*/
|
|
195
|
+
formFieldDefaults) => {
|
|
196
|
+
const translate = useTranslateString();
|
|
197
|
+
const merged = useMemo(() => {
|
|
198
|
+
const builtInDefaults = {
|
|
199
|
+
label: uppercaseFirstLetter(formField.name),
|
|
200
|
+
required: false,
|
|
201
|
+
requiredErrorMessage: "Please fill in required information",
|
|
202
|
+
validate: undefined
|
|
203
|
+
};
|
|
204
|
+
return {
|
|
205
|
+
...builtInDefaults,
|
|
206
|
+
...formFieldDefaults,
|
|
207
|
+
...formField
|
|
208
|
+
};
|
|
209
|
+
}, [formField, formFieldDefaults]);
|
|
210
|
+
const withPlainStrings = useMemo(() => {
|
|
211
|
+
// Translating the validate function is a bit tricky,
|
|
212
|
+
// since it can be an object in both cases.
|
|
213
|
+
const validate = (() => {
|
|
214
|
+
if (typeof merged.validate === "object" && typeof merged.validate.translated === "function") {
|
|
215
|
+
if (!t) {
|
|
216
|
+
throw Error("If you pass in a translated validate function you need to provide a t");
|
|
217
|
+
}
|
|
218
|
+
return merged.validate.translated(t);
|
|
219
|
+
}
|
|
220
|
+
return merged.validate;
|
|
221
|
+
})(); // This should be fine. Not sure why TS is complaining.
|
|
222
|
+
return {
|
|
223
|
+
...merged,
|
|
224
|
+
label: translate(merged.label, t),
|
|
225
|
+
requiredErrorMessage: translate(merged.requiredErrorMessage, t),
|
|
226
|
+
validate
|
|
227
|
+
};
|
|
228
|
+
}, [merged, t, translate]);
|
|
229
|
+
return withPlainStrings;
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* An effect that unregisters a field from the form when the component unmounts.
|
|
234
|
+
*
|
|
235
|
+
* @throws Error when FormContext is null.
|
|
236
|
+
*/
|
|
237
|
+
const useEffectFieldUnregisterOnUnmount = ({
|
|
238
|
+
name,
|
|
239
|
+
unregisterOptions,
|
|
240
|
+
isUnregisteringOnUnmount
|
|
241
|
+
}) => {
|
|
242
|
+
const form = useHookFormContext();
|
|
243
|
+
/**
|
|
244
|
+
* Default options for unregistering the field.
|
|
245
|
+
*
|
|
246
|
+
* Needed to wrap this in a state in order to prevent infinite rerenders.
|
|
247
|
+
*/
|
|
248
|
+
const [options] = useState(unregisterOptions);
|
|
249
|
+
if (!form) {
|
|
250
|
+
throw Error(formContextNull);
|
|
251
|
+
}
|
|
252
|
+
const {
|
|
253
|
+
unregister
|
|
254
|
+
} = form;
|
|
255
|
+
useEffect(() => {
|
|
256
|
+
return () => {
|
|
257
|
+
if (isUnregisteringOnUnmount) {
|
|
258
|
+
unregister(name, options);
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
}, [isUnregisteringOnUnmount, name, unregister, options]);
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
var GroupLabelSorting;
|
|
265
|
+
(function (GroupLabelSorting) {
|
|
266
|
+
GroupLabelSorting["Alphabetical"] = "alphabetical";
|
|
267
|
+
GroupLabelSorting["GroupMemberCountAsc"] = "groupMemberCountAsc";
|
|
268
|
+
})(GroupLabelSorting || (GroupLabelSorting = {}));
|
|
269
|
+
/**
|
|
270
|
+
* Sorts option in the desired order.
|
|
271
|
+
*/
|
|
272
|
+
const useSortedOptionsWithGroup$1 = (selectableOptions, sorting = GroupLabelSorting.Alphabetical) => {
|
|
273
|
+
return useMemo(() => {
|
|
274
|
+
if (sorting === GroupLabelSorting.GroupMemberCountAsc) {
|
|
275
|
+
const groupCounts = selectableOptions.reduce((acc, option) => {
|
|
276
|
+
if (option.group) {
|
|
277
|
+
acc[option.group] = (acc[option.group] || 0) + 1;
|
|
278
|
+
}
|
|
279
|
+
return acc;
|
|
280
|
+
}, {});
|
|
281
|
+
return selectableOptions.sort((a, b) => {
|
|
282
|
+
if (a.group == undefined && !!b.group) {
|
|
283
|
+
return -1;
|
|
284
|
+
}
|
|
285
|
+
if (a.group && b.group) {
|
|
286
|
+
const groupA = groupCounts[a.group];
|
|
287
|
+
const groupB = groupCounts[b.group];
|
|
288
|
+
if (groupA && groupB && groupA < groupB) {
|
|
289
|
+
return -1;
|
|
290
|
+
} else if (groupCounts[a.group] === groupCounts[b.group]) {
|
|
291
|
+
if (b.label > a.label) {
|
|
292
|
+
return -1;
|
|
293
|
+
}
|
|
294
|
+
return 1;
|
|
295
|
+
}
|
|
296
|
+
return 1;
|
|
297
|
+
}
|
|
298
|
+
if (b.label > a.label) {
|
|
299
|
+
return -1;
|
|
300
|
+
}
|
|
301
|
+
return 1;
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
return selectableOptions.sort((a, b) => {
|
|
305
|
+
if (a.group == undefined && !!b.group) {
|
|
306
|
+
return -1;
|
|
307
|
+
}
|
|
308
|
+
if (a.group && b.group && a.group < b.group) {
|
|
309
|
+
return -1;
|
|
310
|
+
}
|
|
311
|
+
if (a.group == b.group && b.label > a.label) {
|
|
312
|
+
return 1;
|
|
313
|
+
}
|
|
314
|
+
return 0;
|
|
315
|
+
});
|
|
316
|
+
}, [selectableOptions, sorting]);
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
const icon = /*#__PURE__*/jsx(CheckBoxOutlineBlank, {
|
|
320
|
+
fontSize: "small"
|
|
321
|
+
});
|
|
322
|
+
const checkedIcon = /*#__PURE__*/jsx(CheckBox, {
|
|
323
|
+
fontSize: "small"
|
|
324
|
+
});
|
|
325
|
+
const defaultNonMultiselectValue = "";
|
|
326
|
+
/**
|
|
327
|
+
* Renders a MUI autocomplete input that is embedded in a react hook form context.
|
|
328
|
+
*
|
|
329
|
+
* @throws Error when FormContext is null.
|
|
330
|
+
*/
|
|
331
|
+
const InputAutocomplete = ({
|
|
332
|
+
formField,
|
|
333
|
+
selectableOptions,
|
|
334
|
+
noOptions,
|
|
335
|
+
autoCompleteProps,
|
|
336
|
+
checkboxesForMultiselect: renderCheckboxes,
|
|
337
|
+
groupSorting,
|
|
338
|
+
inputTextFieldProps,
|
|
339
|
+
unregisterOptions,
|
|
340
|
+
isUnregisteringOnUnmount
|
|
341
|
+
}) => {
|
|
342
|
+
const form = useHookFormContext();
|
|
343
|
+
if (!form) {
|
|
344
|
+
throw Error(formContextNull);
|
|
345
|
+
}
|
|
346
|
+
const {
|
|
347
|
+
register,
|
|
348
|
+
control,
|
|
349
|
+
setValue
|
|
350
|
+
} = form;
|
|
351
|
+
const hasError = useFormFieldHasError(formField.name);
|
|
352
|
+
const muiFormControl = useFormControl();
|
|
353
|
+
const {
|
|
354
|
+
label,
|
|
355
|
+
name,
|
|
356
|
+
required,
|
|
357
|
+
requiredErrorMessage,
|
|
358
|
+
validate
|
|
359
|
+
} = useMergedFormField(formField, formField.t);
|
|
360
|
+
useEffectFieldUnregisterOnUnmount({
|
|
361
|
+
name,
|
|
362
|
+
unregisterOptions,
|
|
363
|
+
isUnregisteringOnUnmount
|
|
364
|
+
});
|
|
365
|
+
const multiSelect = !!autoCompleteProps?.multiple;
|
|
366
|
+
const value = useWatch({
|
|
367
|
+
name,
|
|
368
|
+
control,
|
|
369
|
+
exact: true
|
|
370
|
+
});
|
|
371
|
+
const {
|
|
372
|
+
value: valueToUse,
|
|
373
|
+
hasValue
|
|
374
|
+
} = useMemo(() => {
|
|
375
|
+
if (value) {
|
|
376
|
+
return {
|
|
377
|
+
value,
|
|
378
|
+
hasValue: true
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
return {
|
|
382
|
+
value: multiSelect ? [] : defaultNonMultiselectValue,
|
|
383
|
+
hasValue: false
|
|
384
|
+
};
|
|
385
|
+
}, [multiSelect, value]);
|
|
386
|
+
const adjustedSelectableOptions = useMemo(() => {
|
|
387
|
+
if (hasValue || multiSelect) {
|
|
388
|
+
return selectableOptions;
|
|
389
|
+
}
|
|
390
|
+
return [...selectableOptions, {
|
|
391
|
+
value: defaultNonMultiselectValue,
|
|
392
|
+
label: ""
|
|
393
|
+
}];
|
|
394
|
+
}, [hasValue, multiSelect, selectableOptions]);
|
|
395
|
+
const sortedOptions = useSortedOptionsWithGroup$1(adjustedSelectableOptions, groupSorting);
|
|
396
|
+
assertTruthy(["string", "number", "object"].includes(typeof valueToUse), `Value (Field: ${name}, value: ${valueToUse?.toString() || "undefined"}) for select input has unexpected type`);
|
|
397
|
+
const isRequired = required || muiFormControl?.required;
|
|
398
|
+
const isDisabled = muiFormControl?.disabled || autoCompleteProps?.disabled;
|
|
399
|
+
const props = register(name, {
|
|
400
|
+
required: isRequired ? requiredErrorMessage : false,
|
|
401
|
+
validate,
|
|
402
|
+
disabled: isDisabled
|
|
403
|
+
});
|
|
404
|
+
const currentValue = useMemo(() => {
|
|
405
|
+
return multiSelect ? adjustedSelectableOptions.filter(o => {
|
|
406
|
+
if (Array.isArray(valueToUse)) {
|
|
407
|
+
return valueToUse.includes(o.value);
|
|
408
|
+
}
|
|
409
|
+
return adjustedSelectableOptions.find(option => option.value === valueToUse);
|
|
410
|
+
}) : adjustedSelectableOptions.find(option => option.value === valueToUse);
|
|
411
|
+
}, [multiSelect, adjustedSelectableOptions, valueToUse]);
|
|
412
|
+
const renderOption = useMemo(() => {
|
|
413
|
+
if (autoCompleteProps?.renderOption) {
|
|
414
|
+
return autoCompleteProps.renderOption;
|
|
415
|
+
}
|
|
416
|
+
return multiSelect && renderCheckboxes ? (liProps, option) => {
|
|
417
|
+
return /*#__PURE__*/jsxs("li", {
|
|
418
|
+
...liProps,
|
|
419
|
+
children: [/*#__PURE__*/jsx(Checkbox, {
|
|
420
|
+
icon: icon,
|
|
421
|
+
checkedIcon: checkedIcon,
|
|
422
|
+
sx: {
|
|
423
|
+
mr: 2
|
|
424
|
+
},
|
|
425
|
+
checked: Array.isArray(currentValue) ? !!currentValue.find(item => item.value === option.value) : false
|
|
426
|
+
}), option.label]
|
|
427
|
+
});
|
|
428
|
+
} : undefined;
|
|
429
|
+
}, [autoCompleteProps?.renderOption, currentValue, multiSelect, renderCheckboxes]);
|
|
430
|
+
return /*#__PURE__*/jsxs(FormControl, {
|
|
431
|
+
error: hasError,
|
|
432
|
+
required: isRequired,
|
|
433
|
+
disabled: isDisabled,
|
|
434
|
+
children: [/*#__PURE__*/jsx(Autocomplete, {
|
|
435
|
+
options: sortedOptions,
|
|
436
|
+
value: currentValue,
|
|
437
|
+
...props,
|
|
438
|
+
required: isRequired,
|
|
439
|
+
label: label,
|
|
440
|
+
renderOption: renderOption,
|
|
441
|
+
multiple: multiSelect,
|
|
442
|
+
getOptionLabel: o => {
|
|
443
|
+
if (objectHasKey(o, "label")) {
|
|
444
|
+
return o.label;
|
|
445
|
+
}
|
|
446
|
+
return "";
|
|
447
|
+
},
|
|
448
|
+
onChange: (e, selectedOption) => {
|
|
449
|
+
if (Array.isArray(selectedOption)) {
|
|
450
|
+
setValue(name, selectedOption.map(o => o.value), {
|
|
451
|
+
shouldDirty: true,
|
|
452
|
+
shouldValidate: true,
|
|
453
|
+
shouldTouch: true
|
|
454
|
+
});
|
|
455
|
+
} else if (selectedOption) {
|
|
456
|
+
setValue(name, selectedOption.value, {
|
|
457
|
+
shouldDirty: true,
|
|
458
|
+
shouldValidate: true,
|
|
459
|
+
shouldTouch: true
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
},
|
|
463
|
+
noOptionsText: noOptions,
|
|
464
|
+
filterOptions: (options, {
|
|
465
|
+
inputValue
|
|
466
|
+
}) => {
|
|
467
|
+
return options.filter(o => {
|
|
468
|
+
// Filter out the default empty option
|
|
469
|
+
if (o.value === defaultNonMultiselectValue) {
|
|
470
|
+
return false;
|
|
471
|
+
}
|
|
472
|
+
// If no input, show all non-default options
|
|
473
|
+
if (!inputValue) {
|
|
474
|
+
return true;
|
|
475
|
+
}
|
|
476
|
+
// Filter based on input text (case-insensitive)
|
|
477
|
+
return o.label.toLowerCase().includes(inputValue.toLowerCase());
|
|
478
|
+
});
|
|
479
|
+
},
|
|
480
|
+
renderInput: params => {
|
|
481
|
+
return /*#__PURE__*/jsx(TextField, {
|
|
482
|
+
...params,
|
|
483
|
+
label: label,
|
|
484
|
+
variant: "outlined",
|
|
485
|
+
...inputTextFieldProps,
|
|
486
|
+
required: isRequired,
|
|
487
|
+
disabled: isDisabled,
|
|
488
|
+
error: hasError
|
|
489
|
+
});
|
|
490
|
+
},
|
|
491
|
+
groupBy: option => option.group || "Choose"
|
|
492
|
+
// need to parse this as any in order to get no typing errors
|
|
493
|
+
// for multiselect
|
|
494
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
495
|
+
,
|
|
496
|
+
...autoCompleteProps
|
|
497
|
+
}), /*#__PURE__*/jsx(FormFieldError, {
|
|
498
|
+
fieldName: formField.name
|
|
499
|
+
})]
|
|
500
|
+
});
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
const InputCheckbox = ({
|
|
504
|
+
formField,
|
|
505
|
+
jsxLabel,
|
|
506
|
+
checkboxProps,
|
|
507
|
+
formControlLabelProps,
|
|
508
|
+
unregisterOptions,
|
|
509
|
+
isUnregisteringOnUnmount
|
|
510
|
+
}) => {
|
|
511
|
+
const {
|
|
512
|
+
control,
|
|
513
|
+
setValue
|
|
514
|
+
} = useHookFormContext();
|
|
515
|
+
const hasError = useFormFieldHasError(formField.name);
|
|
516
|
+
const muiFormControl = useFormControl();
|
|
517
|
+
const {
|
|
518
|
+
label,
|
|
519
|
+
name,
|
|
520
|
+
required,
|
|
521
|
+
requiredErrorMessage,
|
|
522
|
+
validate
|
|
523
|
+
} = useMergedFormField(formField);
|
|
524
|
+
useEffectFieldUnregisterOnUnmount({
|
|
525
|
+
name,
|
|
526
|
+
unregisterOptions,
|
|
527
|
+
isUnregisteringOnUnmount
|
|
528
|
+
});
|
|
529
|
+
const id = `hook-form-input-checkbox-${name}`;
|
|
530
|
+
const disabled = muiFormControl?.disabled || checkboxProps?.disabled;
|
|
531
|
+
const isRequired = required || muiFormControl?.required;
|
|
532
|
+
return /*#__PURE__*/jsxs(FormControl, {
|
|
533
|
+
error: hasError,
|
|
534
|
+
disabled: disabled,
|
|
535
|
+
required: required,
|
|
536
|
+
children: [/*#__PURE__*/jsx(FormControlLabel, {
|
|
537
|
+
required: jsxLabel ? false : isRequired // If jsxLabel is set, we don't want to show the asterisk.
|
|
538
|
+
,
|
|
539
|
+
disabled: disabled,
|
|
540
|
+
label: jsxLabel ?? label,
|
|
541
|
+
sx: {
|
|
542
|
+
color: t => hasError ? t.palette.error.main : undefined
|
|
543
|
+
},
|
|
544
|
+
control: /*#__PURE__*/jsx(Controller, {
|
|
545
|
+
name: name,
|
|
546
|
+
control: control,
|
|
547
|
+
rules: {
|
|
548
|
+
required: isRequired ? requiredErrorMessage : false,
|
|
549
|
+
validate
|
|
550
|
+
},
|
|
551
|
+
render: ({
|
|
552
|
+
field: {
|
|
553
|
+
value
|
|
554
|
+
}
|
|
555
|
+
}) => {
|
|
556
|
+
return /*#__PURE__*/jsx(Checkbox, {
|
|
557
|
+
checked: !!value,
|
|
558
|
+
"aria-labelledby": id,
|
|
559
|
+
onChange: (e, val) => {
|
|
560
|
+
setValue(name, val, {
|
|
561
|
+
shouldValidate: true,
|
|
562
|
+
shouldDirty: true,
|
|
563
|
+
shouldTouch: true
|
|
564
|
+
});
|
|
565
|
+
},
|
|
566
|
+
...checkboxProps,
|
|
567
|
+
disabled: disabled
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
}),
|
|
571
|
+
...formControlLabelProps
|
|
572
|
+
}), /*#__PURE__*/jsx(FormFieldError, {
|
|
573
|
+
fieldName: formField.name
|
|
574
|
+
})]
|
|
575
|
+
});
|
|
576
|
+
};
|
|
577
|
+
|
|
578
|
+
const InputDatePicker = ({
|
|
579
|
+
formField,
|
|
580
|
+
datePickerProps,
|
|
581
|
+
unregisterOptions,
|
|
582
|
+
isUnregisteringOnUnmount
|
|
583
|
+
}) => {
|
|
584
|
+
const {
|
|
585
|
+
control
|
|
586
|
+
} = useHookFormContext();
|
|
587
|
+
const hasError = useFormFieldHasError(formField.name);
|
|
588
|
+
const muiFormControl = useFormControl();
|
|
589
|
+
const {
|
|
590
|
+
label,
|
|
591
|
+
name,
|
|
592
|
+
required,
|
|
593
|
+
requiredErrorMessage
|
|
594
|
+
} = useMergedFormField(formField);
|
|
595
|
+
useEffectFieldUnregisterOnUnmount({
|
|
596
|
+
name,
|
|
597
|
+
unregisterOptions,
|
|
598
|
+
isUnregisteringOnUnmount
|
|
599
|
+
});
|
|
600
|
+
const [date, setDate] = useState(dayjs());
|
|
601
|
+
const isRequired = required || muiFormControl?.required;
|
|
602
|
+
const isDisabled = muiFormControl?.disabled || datePickerProps?.disabled;
|
|
603
|
+
return /*#__PURE__*/jsx(Box, {
|
|
604
|
+
children: /*#__PURE__*/jsxs(FormControl, {
|
|
605
|
+
error: hasError,
|
|
606
|
+
required: isRequired,
|
|
607
|
+
disabled: isDisabled,
|
|
608
|
+
children: [/*#__PURE__*/jsx(Controller, {
|
|
609
|
+
name: name,
|
|
610
|
+
defaultValue: date,
|
|
611
|
+
control: control,
|
|
612
|
+
rules: {
|
|
613
|
+
required: isRequired ? requiredErrorMessage : false
|
|
614
|
+
},
|
|
615
|
+
render: ({
|
|
616
|
+
field: {
|
|
617
|
+
onChange,
|
|
618
|
+
...restField
|
|
619
|
+
}
|
|
620
|
+
}) => /*#__PURE__*/jsx(DatePicker, {
|
|
621
|
+
label: label,
|
|
622
|
+
onChange: event => {
|
|
623
|
+
onChange(event);
|
|
624
|
+
if (event) {
|
|
625
|
+
setDate(event);
|
|
626
|
+
}
|
|
627
|
+
},
|
|
628
|
+
disabled: isDisabled,
|
|
629
|
+
slotProps: {
|
|
630
|
+
textField: {
|
|
631
|
+
required: isRequired,
|
|
632
|
+
disabled: isDisabled,
|
|
633
|
+
error: hasError
|
|
634
|
+
}
|
|
635
|
+
},
|
|
636
|
+
...restField,
|
|
637
|
+
...datePickerProps
|
|
638
|
+
})
|
|
639
|
+
}), /*#__PURE__*/jsx(FormFieldError, {
|
|
640
|
+
fieldName: formField.name
|
|
641
|
+
})]
|
|
642
|
+
})
|
|
643
|
+
});
|
|
644
|
+
};
|
|
645
|
+
|
|
646
|
+
const InputDateTimePicker = ({
|
|
647
|
+
formField,
|
|
648
|
+
dateTimePickerProps,
|
|
649
|
+
unregisterOptions,
|
|
650
|
+
isUnregisteringOnUnmount
|
|
651
|
+
}) => {
|
|
652
|
+
const {
|
|
653
|
+
control
|
|
654
|
+
} = useHookFormContext();
|
|
655
|
+
const hasError = useFormFieldHasError(formField.name);
|
|
656
|
+
const muiFormControl = useFormControl();
|
|
657
|
+
const {
|
|
658
|
+
label,
|
|
659
|
+
name,
|
|
660
|
+
required,
|
|
661
|
+
requiredErrorMessage
|
|
662
|
+
} = useMergedFormField(formField);
|
|
663
|
+
useEffectFieldUnregisterOnUnmount({
|
|
664
|
+
name,
|
|
665
|
+
unregisterOptions,
|
|
666
|
+
isUnregisteringOnUnmount
|
|
667
|
+
});
|
|
668
|
+
const [date, setDate] = useState(dayjs());
|
|
669
|
+
const isRequired = required || muiFormControl?.required;
|
|
670
|
+
const isDisabled = muiFormControl?.disabled || dateTimePickerProps?.disabled;
|
|
671
|
+
return /*#__PURE__*/jsx(Box, {
|
|
672
|
+
children: /*#__PURE__*/jsxs(FormControl, {
|
|
673
|
+
error: hasError,
|
|
674
|
+
required: isRequired,
|
|
675
|
+
disabled: isDisabled,
|
|
676
|
+
children: [/*#__PURE__*/jsx(Controller, {
|
|
677
|
+
name: name,
|
|
678
|
+
defaultValue: date,
|
|
679
|
+
control: control,
|
|
680
|
+
rules: {
|
|
681
|
+
required: isRequired ? requiredErrorMessage : false
|
|
682
|
+
},
|
|
683
|
+
render: ({
|
|
684
|
+
field: {
|
|
685
|
+
onChange,
|
|
686
|
+
...restField
|
|
687
|
+
}
|
|
688
|
+
}) => /*#__PURE__*/jsx(DateTimePicker, {
|
|
689
|
+
label: label,
|
|
690
|
+
ampm: dateTimePickerProps?.ampm ? dateTimePickerProps.ampm : false,
|
|
691
|
+
onChange: event => {
|
|
692
|
+
onChange(event);
|
|
693
|
+
if (event) {
|
|
694
|
+
setDate(event);
|
|
695
|
+
}
|
|
696
|
+
},
|
|
697
|
+
disabled: isDisabled,
|
|
698
|
+
required: isRequired,
|
|
699
|
+
slotProps: {
|
|
700
|
+
textField: {
|
|
701
|
+
required: isRequired,
|
|
702
|
+
disabled: isDisabled,
|
|
703
|
+
error: hasError
|
|
704
|
+
}
|
|
705
|
+
},
|
|
706
|
+
...restField,
|
|
707
|
+
...dateTimePickerProps
|
|
708
|
+
})
|
|
709
|
+
}), /*#__PURE__*/jsx(FormFieldError, {
|
|
710
|
+
fieldName: formField.name
|
|
711
|
+
})]
|
|
712
|
+
})
|
|
713
|
+
});
|
|
714
|
+
};
|
|
715
|
+
|
|
716
|
+
const InputNumber = ({
|
|
717
|
+
formField,
|
|
718
|
+
textFieldProps,
|
|
719
|
+
numericFormatProps,
|
|
720
|
+
unregisterOptions,
|
|
721
|
+
isUnregisteringOnUnmount
|
|
722
|
+
}) => {
|
|
723
|
+
const form = useHookFormContext();
|
|
724
|
+
const {
|
|
725
|
+
control
|
|
726
|
+
} = form;
|
|
727
|
+
const muiFormControl = useFormControl();
|
|
728
|
+
const {
|
|
729
|
+
label,
|
|
730
|
+
name,
|
|
731
|
+
required,
|
|
732
|
+
requiredErrorMessage,
|
|
733
|
+
validate
|
|
734
|
+
} = useMergedFormField(formField, formField.t);
|
|
735
|
+
useEffectFieldUnregisterOnUnmount({
|
|
736
|
+
name,
|
|
737
|
+
unregisterOptions,
|
|
738
|
+
isUnregisteringOnUnmount
|
|
739
|
+
});
|
|
740
|
+
if (!form) {
|
|
741
|
+
throw Error(formContextNull);
|
|
742
|
+
}
|
|
743
|
+
const hasError = useFormFieldHasError(formField.name);
|
|
744
|
+
const isRequired = required || muiFormControl?.required;
|
|
745
|
+
const isDisabled = muiFormControl?.disabled || textFieldProps?.disabled;
|
|
746
|
+
return /*#__PURE__*/jsxs(FormControl, {
|
|
747
|
+
error: hasError,
|
|
748
|
+
required: required,
|
|
749
|
+
disabled: isDisabled,
|
|
750
|
+
children: [/*#__PURE__*/jsx(Controller, {
|
|
751
|
+
name: name,
|
|
752
|
+
control: control,
|
|
753
|
+
rules: {
|
|
754
|
+
required: isRequired ? requiredErrorMessage : false,
|
|
755
|
+
validate
|
|
756
|
+
},
|
|
757
|
+
render: props => {
|
|
758
|
+
const {
|
|
759
|
+
ref,
|
|
760
|
+
...rest
|
|
761
|
+
} = props.field;
|
|
762
|
+
return (
|
|
763
|
+
/*#__PURE__*/
|
|
764
|
+
// @ts-expect-error TextFieldProps is not compatible with NumberFormatBaseProps
|
|
765
|
+
jsx(NumericFormat, {
|
|
766
|
+
...rest,
|
|
767
|
+
customInput: TextField,
|
|
768
|
+
onValueChange: target => {
|
|
769
|
+
rest.onChange(target.value);
|
|
770
|
+
},
|
|
771
|
+
label: label,
|
|
772
|
+
onChange: undefined,
|
|
773
|
+
onBlur: props.field.onBlur,
|
|
774
|
+
error: !!props.fieldState.error,
|
|
775
|
+
...textFieldProps,
|
|
776
|
+
...numericFormatProps,
|
|
777
|
+
disabled: isDisabled,
|
|
778
|
+
required: isRequired
|
|
779
|
+
})
|
|
780
|
+
);
|
|
781
|
+
}
|
|
782
|
+
}), /*#__PURE__*/jsx(FormFieldError, {
|
|
783
|
+
fieldName: formField.name
|
|
784
|
+
})]
|
|
785
|
+
});
|
|
786
|
+
};
|
|
787
|
+
|
|
788
|
+
const InputOutlinedInput = ({
|
|
789
|
+
formField,
|
|
790
|
+
outlinedInputProps,
|
|
791
|
+
unregisterOptions,
|
|
792
|
+
isUnregisteringOnUnmount
|
|
793
|
+
}) => {
|
|
794
|
+
const form = useHookFormContext();
|
|
795
|
+
const muiFormControl = useFormControl();
|
|
796
|
+
const {
|
|
797
|
+
label,
|
|
798
|
+
name,
|
|
799
|
+
required,
|
|
800
|
+
requiredErrorMessage,
|
|
801
|
+
validate
|
|
802
|
+
} = useMergedFormField(formField, formField.t);
|
|
803
|
+
useEffectFieldUnregisterOnUnmount({
|
|
804
|
+
name,
|
|
805
|
+
unregisterOptions,
|
|
806
|
+
isUnregisteringOnUnmount
|
|
807
|
+
});
|
|
808
|
+
if (!form) {
|
|
809
|
+
throw Error(formContextNull);
|
|
810
|
+
}
|
|
811
|
+
const {
|
|
812
|
+
control
|
|
813
|
+
} = form;
|
|
814
|
+
const hasError = useFormFieldHasError(formField.name);
|
|
815
|
+
const isRequired = required || muiFormControl?.required;
|
|
816
|
+
const isDisabled = muiFormControl?.disabled || outlinedInputProps?.disabled;
|
|
817
|
+
return /*#__PURE__*/jsxs(FormControl, {
|
|
818
|
+
error: hasError,
|
|
819
|
+
required: required,
|
|
820
|
+
disabled: muiFormControl?.disabled,
|
|
821
|
+
children: [/*#__PURE__*/jsx(InputLabel, {
|
|
822
|
+
required: isRequired,
|
|
823
|
+
disabled: isDisabled,
|
|
824
|
+
children: label
|
|
825
|
+
}), /*#__PURE__*/jsx(Controller, {
|
|
826
|
+
name: name,
|
|
827
|
+
control: control,
|
|
828
|
+
rules: {
|
|
829
|
+
required: isRequired ? requiredErrorMessage : false,
|
|
830
|
+
validate
|
|
831
|
+
},
|
|
832
|
+
render: ({
|
|
833
|
+
field: {
|
|
834
|
+
value,
|
|
835
|
+
onBlur,
|
|
836
|
+
onChange
|
|
837
|
+
}
|
|
838
|
+
}) => {
|
|
839
|
+
return /*#__PURE__*/jsx(OutlinedInput, {
|
|
840
|
+
value: value,
|
|
841
|
+
onChange: onChange,
|
|
842
|
+
onBlur: onBlur,
|
|
843
|
+
label: label,
|
|
844
|
+
...outlinedInputProps,
|
|
845
|
+
required: isRequired,
|
|
846
|
+
disabled: isDisabled
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
}), /*#__PURE__*/jsx(FormFieldError, {
|
|
850
|
+
fieldName: formField.name
|
|
851
|
+
})]
|
|
852
|
+
});
|
|
853
|
+
};
|
|
854
|
+
|
|
855
|
+
const InputRadio = ({
|
|
856
|
+
formField,
|
|
857
|
+
selectableOptions,
|
|
858
|
+
formControlLabelProps,
|
|
859
|
+
radioGroupProps,
|
|
860
|
+
unregisterOptions,
|
|
861
|
+
isUnregisteringOnUnmount
|
|
862
|
+
}) => {
|
|
863
|
+
const {
|
|
864
|
+
control
|
|
865
|
+
} = useHookFormContext();
|
|
866
|
+
const hasError = useFormFieldHasError(formField.name);
|
|
867
|
+
const muiFormControl = useFormControl();
|
|
868
|
+
const {
|
|
869
|
+
label,
|
|
870
|
+
name,
|
|
871
|
+
required,
|
|
872
|
+
requiredErrorMessage,
|
|
873
|
+
validate
|
|
874
|
+
} = useMergedFormField(formField);
|
|
875
|
+
useEffectFieldUnregisterOnUnmount({
|
|
876
|
+
name,
|
|
877
|
+
unregisterOptions,
|
|
878
|
+
isUnregisteringOnUnmount
|
|
879
|
+
});
|
|
880
|
+
const id = `hook-form-input-radio-${name}`;
|
|
881
|
+
const isRequired = required || muiFormControl?.required;
|
|
882
|
+
const isDisabled = muiFormControl?.disabled || formControlLabelProps?.disabled;
|
|
883
|
+
return /*#__PURE__*/jsxs(FormControl, {
|
|
884
|
+
error: hasError,
|
|
885
|
+
disabled: muiFormControl?.disabled,
|
|
886
|
+
children: [/*#__PURE__*/jsx(FormLabel, {
|
|
887
|
+
id: id,
|
|
888
|
+
required: isRequired,
|
|
889
|
+
disabled: isDisabled,
|
|
890
|
+
error: hasError,
|
|
891
|
+
sx: {
|
|
892
|
+
color: t => hasError ? t.palette.error.main : undefined
|
|
893
|
+
},
|
|
894
|
+
children: label
|
|
895
|
+
}), /*#__PURE__*/jsx(Controller, {
|
|
896
|
+
name: name,
|
|
897
|
+
control: control,
|
|
898
|
+
rules: {
|
|
899
|
+
required: isRequired ? requiredErrorMessage : false,
|
|
900
|
+
validate
|
|
901
|
+
},
|
|
902
|
+
render: ({
|
|
903
|
+
field: {
|
|
904
|
+
onChange,
|
|
905
|
+
value,
|
|
906
|
+
onBlur
|
|
907
|
+
}
|
|
908
|
+
}) => {
|
|
909
|
+
return /*#__PURE__*/jsx(RadioGroup, {
|
|
910
|
+
value: value,
|
|
911
|
+
"aria-labelledby": id,
|
|
912
|
+
onChange: onChange,
|
|
913
|
+
onBlur: onBlur,
|
|
914
|
+
...radioGroupProps,
|
|
915
|
+
children: selectableOptions?.map(option => {
|
|
916
|
+
return /*#__PURE__*/jsx(FormControlLabel, {
|
|
917
|
+
value: option.value,
|
|
918
|
+
control: /*#__PURE__*/jsx(Radio, {}),
|
|
919
|
+
label: option.label,
|
|
920
|
+
...formControlLabelProps,
|
|
921
|
+
disabled: isDisabled
|
|
922
|
+
}, typeof option.value === "boolean" ? option.value.toString() : option.value);
|
|
923
|
+
})
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
}), /*#__PURE__*/jsx(FormFieldError, {
|
|
927
|
+
fieldName: formField.name
|
|
928
|
+
})]
|
|
929
|
+
});
|
|
930
|
+
};
|
|
931
|
+
|
|
932
|
+
/**
|
|
933
|
+
* Sorts the options by group and value.
|
|
934
|
+
*/
|
|
935
|
+
const useSortedOptionsWithGroup = selectableOptions => {
|
|
936
|
+
return useMemo(() => selectableOptions.sort((a, b) => {
|
|
937
|
+
if (a.group == undefined && !!b.group) {
|
|
938
|
+
return -1;
|
|
939
|
+
}
|
|
940
|
+
if (a.group && b.group && a.group < b.group) {
|
|
941
|
+
return -1;
|
|
942
|
+
}
|
|
943
|
+
if (a.group == b.group && b.value > a.value) {
|
|
944
|
+
return 1;
|
|
945
|
+
}
|
|
946
|
+
return 0;
|
|
947
|
+
}), [selectableOptions]);
|
|
948
|
+
};
|
|
949
|
+
|
|
950
|
+
const InputSelect = ({
|
|
951
|
+
formField,
|
|
952
|
+
selectableOptions,
|
|
953
|
+
optionsTitle,
|
|
954
|
+
selectComponentProps,
|
|
955
|
+
tooltipHolder,
|
|
956
|
+
unregisterOptions,
|
|
957
|
+
isUnregisteringOnUnmount
|
|
958
|
+
}) => {
|
|
959
|
+
const form = useHookFormContext();
|
|
960
|
+
if (!form) {
|
|
961
|
+
throw Error(formContextNull);
|
|
962
|
+
}
|
|
963
|
+
const {
|
|
964
|
+
control
|
|
965
|
+
} = form;
|
|
966
|
+
const muiFormControl = useFormControl();
|
|
967
|
+
const {
|
|
968
|
+
label,
|
|
969
|
+
name,
|
|
970
|
+
required,
|
|
971
|
+
requiredErrorMessage,
|
|
972
|
+
validate
|
|
973
|
+
} = useMergedFormField(formField, formField.t);
|
|
974
|
+
useEffectFieldUnregisterOnUnmount({
|
|
975
|
+
name,
|
|
976
|
+
unregisterOptions,
|
|
977
|
+
isUnregisteringOnUnmount
|
|
978
|
+
});
|
|
979
|
+
const defaultValue = useMemo(() => selectComponentProps?.multiple ? [] : "", [selectComponentProps?.multiple]);
|
|
980
|
+
const id = `input-select-${name}`;
|
|
981
|
+
const adjustedOptions = useMemo(() => selectComponentProps?.multiple || Array.isArray(defaultValue) ? selectableOptions : [...selectableOptions, {
|
|
982
|
+
value: defaultValue,
|
|
983
|
+
label: ""
|
|
984
|
+
}], [defaultValue, selectComponentProps?.multiple, selectableOptions]);
|
|
985
|
+
const sortedOptions = useSortedOptionsWithGroup(adjustedOptions);
|
|
986
|
+
const hasErrors = useFormFieldHasError(formField.name);
|
|
987
|
+
const isRequired = required || muiFormControl?.required;
|
|
988
|
+
const isDisabled = muiFormControl?.disabled || selectComponentProps?.disabled;
|
|
989
|
+
return /*#__PURE__*/jsxs(FormControl, {
|
|
990
|
+
error: hasErrors,
|
|
991
|
+
required: isRequired,
|
|
992
|
+
disabled: isDisabled,
|
|
993
|
+
children: [/*#__PURE__*/jsx(InputLabel, {
|
|
994
|
+
id: id,
|
|
995
|
+
required: isRequired,
|
|
996
|
+
disabled: isDisabled,
|
|
997
|
+
error: hasErrors,
|
|
998
|
+
children: label
|
|
999
|
+
}), /*#__PURE__*/jsx(Controller, {
|
|
1000
|
+
name: name,
|
|
1001
|
+
control: control,
|
|
1002
|
+
rules: {
|
|
1003
|
+
required: isRequired ? requiredErrorMessage : false,
|
|
1004
|
+
validate
|
|
1005
|
+
},
|
|
1006
|
+
render: ({
|
|
1007
|
+
field: {
|
|
1008
|
+
onChange,
|
|
1009
|
+
onBlur,
|
|
1010
|
+
value
|
|
1011
|
+
},
|
|
1012
|
+
fieldState: {
|
|
1013
|
+
error
|
|
1014
|
+
}
|
|
1015
|
+
}) => {
|
|
1016
|
+
const valueToUse = value || defaultValue;
|
|
1017
|
+
return /*#__PURE__*/jsxs(Select, {
|
|
1018
|
+
labelId: id,
|
|
1019
|
+
...selectComponentProps,
|
|
1020
|
+
label: label,
|
|
1021
|
+
value: valueToUse,
|
|
1022
|
+
error: !!error,
|
|
1023
|
+
onChange: onChange,
|
|
1024
|
+
onBlur: onBlur,
|
|
1025
|
+
disabled: isDisabled,
|
|
1026
|
+
required: isRequired,
|
|
1027
|
+
children: [optionsTitle && /*#__PURE__*/jsx(MenuItem, {
|
|
1028
|
+
value: defaultValue,
|
|
1029
|
+
disabled: true,
|
|
1030
|
+
children: /*#__PURE__*/jsx("em", {
|
|
1031
|
+
children: optionsTitle
|
|
1032
|
+
})
|
|
1033
|
+
}), sortedOptions?.filter(o => o.value !== "").map((option, index) => {
|
|
1034
|
+
const optionBefore = sortedOptions[index - 1];
|
|
1035
|
+
const showGroup = (optionBefore || index == 0) && option.group && optionBefore?.group !== option.group;
|
|
1036
|
+
const disabledText = typeof option.disabled === "string" ? option.disabled : undefined;
|
|
1037
|
+
const isDisabledToUse = !!disabledText || !!option.disabled || isDisabled;
|
|
1038
|
+
const menuItem = /*#__PURE__*/jsx(MenuItem, {
|
|
1039
|
+
value: typeof option.value === "boolean" ? option.value.toString() : option.value,
|
|
1040
|
+
disabled: isDisabledToUse,
|
|
1041
|
+
title: typeof option.disabled === "string" ? option.disabled : undefined,
|
|
1042
|
+
selected: typeof option.value === "boolean" ? option.value.toString() === value : value === option.value,
|
|
1043
|
+
children: /*#__PURE__*/jsx(Typography, {
|
|
1044
|
+
dangerouslySetInnerHTML: {
|
|
1045
|
+
__html: option.label
|
|
1046
|
+
}
|
|
1047
|
+
})
|
|
1048
|
+
}, option.label);
|
|
1049
|
+
const wrappedMenuItem = disabledText ? /*#__PURE__*/jsxs(Box, {
|
|
1050
|
+
sx: {
|
|
1051
|
+
position: "relative"
|
|
1052
|
+
},
|
|
1053
|
+
children: [menuItem, /*#__PURE__*/jsx(Stack, {
|
|
1054
|
+
direction: "column",
|
|
1055
|
+
justifyContent: "center",
|
|
1056
|
+
spacing: 1,
|
|
1057
|
+
sx: {
|
|
1058
|
+
position: "absolute",
|
|
1059
|
+
right: th => th.spacing(0.5),
|
|
1060
|
+
top: 0,
|
|
1061
|
+
bottom: 0
|
|
1062
|
+
},
|
|
1063
|
+
children: tooltipHolder ? /*#__PURE__*/jsx(Tooltip, {
|
|
1064
|
+
title: disabledText,
|
|
1065
|
+
children: tooltipHolder
|
|
1066
|
+
}) : null
|
|
1067
|
+
})]
|
|
1068
|
+
}) : menuItem;
|
|
1069
|
+
return showGroup ? [/*#__PURE__*/jsx(ListSubheader, {
|
|
1070
|
+
children: option.group
|
|
1071
|
+
}, option.group), wrappedMenuItem] : wrappedMenuItem;
|
|
1072
|
+
})]
|
|
1073
|
+
});
|
|
1074
|
+
}
|
|
1075
|
+
}), /*#__PURE__*/jsx(FormFieldError, {
|
|
1076
|
+
fieldName: formField.name
|
|
1077
|
+
})]
|
|
1078
|
+
});
|
|
1079
|
+
};
|
|
1080
|
+
|
|
1081
|
+
const InputSelectFromEnum = props => {
|
|
1082
|
+
const {
|
|
1083
|
+
enumData,
|
|
1084
|
+
formField
|
|
1085
|
+
} = props;
|
|
1086
|
+
const translateFunction = formField.t;
|
|
1087
|
+
const options = useMemo(() => {
|
|
1088
|
+
return Object.entries(enumData.enumToUse).map(([key, value]) => {
|
|
1089
|
+
const label = enumData.labelsForEnum[key];
|
|
1090
|
+
assertTruthy(label, "Could not find a label for value: " + value + " in Field " + formField.name);
|
|
1091
|
+
if (typeof label === "string") {
|
|
1092
|
+
return {
|
|
1093
|
+
label,
|
|
1094
|
+
value
|
|
1095
|
+
};
|
|
1096
|
+
}
|
|
1097
|
+
assertTruthy(translateFunction, `If you want to use translated labels for field (name: ${formField.name}) you need to provide a translate function in the HookFormProvider.`);
|
|
1098
|
+
return {
|
|
1099
|
+
label: label(translateFunction),
|
|
1100
|
+
value
|
|
1101
|
+
};
|
|
1102
|
+
}).filter(option => {
|
|
1103
|
+
if (props.limitOptions) {
|
|
1104
|
+
return props.limitOptions.includes(option.value);
|
|
1105
|
+
}
|
|
1106
|
+
return true;
|
|
1107
|
+
});
|
|
1108
|
+
}, [enumData.enumToUse, enumData.labelsForEnum, formField.name, translateFunction, props.limitOptions]);
|
|
1109
|
+
return props.autocomplete ? /*#__PURE__*/jsx(InputAutocomplete, {
|
|
1110
|
+
noOptions: props.autocomplete.noOptionText,
|
|
1111
|
+
...props,
|
|
1112
|
+
selectableOptions: options,
|
|
1113
|
+
formField: formField
|
|
1114
|
+
}) : /*#__PURE__*/jsx(InputSelect, {
|
|
1115
|
+
...props,
|
|
1116
|
+
selectableOptions: options,
|
|
1117
|
+
formField: formField
|
|
1118
|
+
});
|
|
1119
|
+
};
|
|
1120
|
+
|
|
1121
|
+
const InputSlider = ({
|
|
1122
|
+
formField,
|
|
1123
|
+
sliderProps,
|
|
1124
|
+
unregisterOptions,
|
|
1125
|
+
isUnregisteringOnUnmount
|
|
1126
|
+
}) => {
|
|
1127
|
+
const form = useHookFormContext();
|
|
1128
|
+
const muiFormControl = useFormControl();
|
|
1129
|
+
const {
|
|
1130
|
+
label,
|
|
1131
|
+
name,
|
|
1132
|
+
required,
|
|
1133
|
+
requiredErrorMessage,
|
|
1134
|
+
validate
|
|
1135
|
+
} = useMergedFormField(formField, formField.t);
|
|
1136
|
+
useEffectFieldUnregisterOnUnmount({
|
|
1137
|
+
name,
|
|
1138
|
+
unregisterOptions,
|
|
1139
|
+
isUnregisteringOnUnmount
|
|
1140
|
+
});
|
|
1141
|
+
if (!form) {
|
|
1142
|
+
throw Error(formContextNull);
|
|
1143
|
+
}
|
|
1144
|
+
const {
|
|
1145
|
+
control
|
|
1146
|
+
} = form;
|
|
1147
|
+
const hasError = useFormFieldHasError(formField.name);
|
|
1148
|
+
const isRequired = required || muiFormControl?.required;
|
|
1149
|
+
const isDisabled = muiFormControl?.disabled || sliderProps?.disabled;
|
|
1150
|
+
return /*#__PURE__*/jsxs(FormControl, {
|
|
1151
|
+
error: hasError,
|
|
1152
|
+
required: isRequired,
|
|
1153
|
+
disabled: isDisabled,
|
|
1154
|
+
children: [/*#__PURE__*/jsx(Controller, {
|
|
1155
|
+
name: name,
|
|
1156
|
+
control: control,
|
|
1157
|
+
defaultValue: 0,
|
|
1158
|
+
rules: {
|
|
1159
|
+
required: isRequired ? requiredErrorMessage : false,
|
|
1160
|
+
validate
|
|
1161
|
+
},
|
|
1162
|
+
render: ({
|
|
1163
|
+
field: {
|
|
1164
|
+
onChange,
|
|
1165
|
+
value,
|
|
1166
|
+
onBlur
|
|
1167
|
+
}
|
|
1168
|
+
}) => {
|
|
1169
|
+
return /*#__PURE__*/jsx(Slider, {
|
|
1170
|
+
value: value || 0,
|
|
1171
|
+
onChange: onChange,
|
|
1172
|
+
onBlur: onBlur,
|
|
1173
|
+
required: isRequired,
|
|
1174
|
+
label: label,
|
|
1175
|
+
component: sliderProps?.component || "span",
|
|
1176
|
+
max: typeof sliderProps?.max === "number" ? sliderProps.max : undefined,
|
|
1177
|
+
min: typeof sliderProps?.min === "number" ? sliderProps.min : undefined,
|
|
1178
|
+
...sliderProps,
|
|
1179
|
+
disabled: isDisabled
|
|
1180
|
+
});
|
|
1181
|
+
}
|
|
1182
|
+
}), /*#__PURE__*/jsx(FormFieldError, {
|
|
1183
|
+
fieldName: name
|
|
1184
|
+
})]
|
|
1185
|
+
});
|
|
1186
|
+
};
|
|
1187
|
+
|
|
1188
|
+
const InputSwitch = ({
|
|
1189
|
+
formField,
|
|
1190
|
+
switchProps,
|
|
1191
|
+
formControlLabelProps,
|
|
1192
|
+
unregisterOptions,
|
|
1193
|
+
isUnregisteringOnUnmount
|
|
1194
|
+
}) => {
|
|
1195
|
+
const {
|
|
1196
|
+
control,
|
|
1197
|
+
setValue
|
|
1198
|
+
} = useHookFormContext();
|
|
1199
|
+
const hasError = useFormFieldHasError(formField.name);
|
|
1200
|
+
const muiFormControl = useFormControl();
|
|
1201
|
+
const {
|
|
1202
|
+
label,
|
|
1203
|
+
name,
|
|
1204
|
+
required,
|
|
1205
|
+
requiredErrorMessage,
|
|
1206
|
+
validate
|
|
1207
|
+
} = useMergedFormField(formField);
|
|
1208
|
+
useEffectFieldUnregisterOnUnmount({
|
|
1209
|
+
name,
|
|
1210
|
+
unregisterOptions,
|
|
1211
|
+
isUnregisteringOnUnmount
|
|
1212
|
+
});
|
|
1213
|
+
const isRequired = required || muiFormControl?.required;
|
|
1214
|
+
const isDisabled = muiFormControl?.disabled || switchProps?.disabled;
|
|
1215
|
+
return /*#__PURE__*/jsxs(FormControl, {
|
|
1216
|
+
error: hasError,
|
|
1217
|
+
disabled: isDisabled,
|
|
1218
|
+
required: isRequired,
|
|
1219
|
+
children: [/*#__PURE__*/jsx(FormControlLabel, {
|
|
1220
|
+
label: label,
|
|
1221
|
+
disabled: isDisabled,
|
|
1222
|
+
required: isRequired,
|
|
1223
|
+
sx: {
|
|
1224
|
+
color: t => hasError ? t.palette.error.main : undefined
|
|
1225
|
+
},
|
|
1226
|
+
control: /*#__PURE__*/jsx(Controller, {
|
|
1227
|
+
name: name,
|
|
1228
|
+
control: control,
|
|
1229
|
+
rules: {
|
|
1230
|
+
required: isRequired ? requiredErrorMessage : false,
|
|
1231
|
+
validate
|
|
1232
|
+
},
|
|
1233
|
+
render: ({
|
|
1234
|
+
field: {
|
|
1235
|
+
value
|
|
1236
|
+
}
|
|
1237
|
+
}) => {
|
|
1238
|
+
return /*#__PURE__*/jsx(Switch, {
|
|
1239
|
+
checked: !!value,
|
|
1240
|
+
onChange: (e, val) => {
|
|
1241
|
+
setValue(name, val, {
|
|
1242
|
+
shouldValidate: true,
|
|
1243
|
+
shouldDirty: true,
|
|
1244
|
+
shouldTouch: true
|
|
1245
|
+
});
|
|
1246
|
+
},
|
|
1247
|
+
...switchProps
|
|
1248
|
+
});
|
|
1249
|
+
}
|
|
1250
|
+
}),
|
|
1251
|
+
...formControlLabelProps
|
|
1252
|
+
}), /*#__PURE__*/jsx(FormFieldError, {
|
|
1253
|
+
fieldName: formField.name
|
|
1254
|
+
})]
|
|
1255
|
+
});
|
|
1256
|
+
};
|
|
1257
|
+
|
|
1258
|
+
const InputTextField = ({
|
|
1259
|
+
formField,
|
|
1260
|
+
textFieldProps,
|
|
1261
|
+
unregisterOptions,
|
|
1262
|
+
isUnregisteringOnUnmount
|
|
1263
|
+
}) => {
|
|
1264
|
+
const form = useHookFormContext();
|
|
1265
|
+
const {
|
|
1266
|
+
control
|
|
1267
|
+
} = form;
|
|
1268
|
+
const muiFormControl = useFormControl();
|
|
1269
|
+
const {
|
|
1270
|
+
label,
|
|
1271
|
+
name,
|
|
1272
|
+
required,
|
|
1273
|
+
requiredErrorMessage,
|
|
1274
|
+
validate
|
|
1275
|
+
} = useMergedFormField(formField, formField.t);
|
|
1276
|
+
useEffectFieldUnregisterOnUnmount({
|
|
1277
|
+
name,
|
|
1278
|
+
unregisterOptions,
|
|
1279
|
+
isUnregisteringOnUnmount
|
|
1280
|
+
});
|
|
1281
|
+
if (!form) {
|
|
1282
|
+
throw Error(formContextNull);
|
|
1283
|
+
}
|
|
1284
|
+
const hasError = useFormFieldHasError(formField.name);
|
|
1285
|
+
const isRequired = required || muiFormControl?.required;
|
|
1286
|
+
const isDisabled = muiFormControl?.disabled || textFieldProps?.disabled;
|
|
1287
|
+
return /*#__PURE__*/jsxs(FormControl, {
|
|
1288
|
+
error: hasError,
|
|
1289
|
+
required: isRequired,
|
|
1290
|
+
disabled: isDisabled,
|
|
1291
|
+
children: [/*#__PURE__*/jsx(Controller, {
|
|
1292
|
+
name: name,
|
|
1293
|
+
control: control,
|
|
1294
|
+
rules: {
|
|
1295
|
+
required: isRequired ? requiredErrorMessage : false,
|
|
1296
|
+
validate
|
|
1297
|
+
},
|
|
1298
|
+
render: ({
|
|
1299
|
+
field: {
|
|
1300
|
+
onChange,
|
|
1301
|
+
value
|
|
1302
|
+
},
|
|
1303
|
+
fieldState: {
|
|
1304
|
+
error
|
|
1305
|
+
}
|
|
1306
|
+
}) => {
|
|
1307
|
+
return /*#__PURE__*/jsx(TextField, {
|
|
1308
|
+
error: !!error,
|
|
1309
|
+
onChange: onChange,
|
|
1310
|
+
value: value,
|
|
1311
|
+
label: label,
|
|
1312
|
+
...textFieldProps,
|
|
1313
|
+
required: isRequired,
|
|
1314
|
+
disabled: isDisabled
|
|
1315
|
+
});
|
|
1316
|
+
}
|
|
1317
|
+
}), /*#__PURE__*/jsx(FormFieldError, {
|
|
1318
|
+
fieldName: formField.name
|
|
1319
|
+
})]
|
|
1320
|
+
});
|
|
1321
|
+
};
|
|
1322
|
+
|
|
1323
|
+
const InputTimePicker = ({
|
|
1324
|
+
formField,
|
|
1325
|
+
timePickerProps,
|
|
1326
|
+
unregisterOptions,
|
|
1327
|
+
isUnregisteringOnUnmount
|
|
1328
|
+
}) => {
|
|
1329
|
+
const {
|
|
1330
|
+
control
|
|
1331
|
+
} = useHookFormContext();
|
|
1332
|
+
const hasError = useFormFieldHasError(formField.name);
|
|
1333
|
+
const muiFormControl = useFormControl();
|
|
1334
|
+
const {
|
|
1335
|
+
label,
|
|
1336
|
+
name,
|
|
1337
|
+
required,
|
|
1338
|
+
requiredErrorMessage
|
|
1339
|
+
} = useMergedFormField(formField);
|
|
1340
|
+
useEffectFieldUnregisterOnUnmount({
|
|
1341
|
+
name,
|
|
1342
|
+
unregisterOptions,
|
|
1343
|
+
isUnregisteringOnUnmount
|
|
1344
|
+
});
|
|
1345
|
+
const [date, setDate] = useState(dayjs());
|
|
1346
|
+
const isRequired = required || muiFormControl?.required;
|
|
1347
|
+
const isDisabled = muiFormControl?.disabled || timePickerProps?.disabled;
|
|
1348
|
+
return /*#__PURE__*/jsx(Box, {
|
|
1349
|
+
children: /*#__PURE__*/jsxs(FormControl, {
|
|
1350
|
+
error: hasError,
|
|
1351
|
+
required: isRequired,
|
|
1352
|
+
disabled: isDisabled,
|
|
1353
|
+
children: [/*#__PURE__*/jsx(Controller, {
|
|
1354
|
+
name: name,
|
|
1355
|
+
defaultValue: date,
|
|
1356
|
+
control: control,
|
|
1357
|
+
rules: {
|
|
1358
|
+
required: isRequired ? requiredErrorMessage : false
|
|
1359
|
+
},
|
|
1360
|
+
render: ({
|
|
1361
|
+
field: {
|
|
1362
|
+
onChange,
|
|
1363
|
+
...restField
|
|
1364
|
+
}
|
|
1365
|
+
}) => /*#__PURE__*/jsx(TimePicker, {
|
|
1366
|
+
label: label,
|
|
1367
|
+
onChange: event => {
|
|
1368
|
+
onChange(event);
|
|
1369
|
+
if (event) {
|
|
1370
|
+
setDate(event);
|
|
1371
|
+
}
|
|
1372
|
+
},
|
|
1373
|
+
disabled: isDisabled,
|
|
1374
|
+
required: isRequired,
|
|
1375
|
+
slotProps: {
|
|
1376
|
+
textField: {
|
|
1377
|
+
required: isRequired,
|
|
1378
|
+
disabled: isDisabled,
|
|
1379
|
+
error: hasError
|
|
1380
|
+
}
|
|
1381
|
+
},
|
|
1382
|
+
...restField,
|
|
1383
|
+
...timePickerProps
|
|
1384
|
+
})
|
|
1385
|
+
}), /*#__PURE__*/jsx(FormFieldError, {
|
|
1386
|
+
fieldName: formField.name
|
|
1387
|
+
})]
|
|
1388
|
+
})
|
|
1389
|
+
});
|
|
1390
|
+
};
|
|
1391
|
+
|
|
1392
|
+
const InputToggleButton = ({
|
|
1393
|
+
formField,
|
|
1394
|
+
toggleButtonProps,
|
|
1395
|
+
formControlLabelProps,
|
|
1396
|
+
children,
|
|
1397
|
+
unregisterOptions,
|
|
1398
|
+
isUnregisteringOnUnmount
|
|
1399
|
+
}) => {
|
|
1400
|
+
const {
|
|
1401
|
+
register,
|
|
1402
|
+
control,
|
|
1403
|
+
setValue
|
|
1404
|
+
} = useHookFormContext();
|
|
1405
|
+
const hasError = useFormFieldHasError(formField.name);
|
|
1406
|
+
const muiFormControl = useFormControl();
|
|
1407
|
+
const {
|
|
1408
|
+
label,
|
|
1409
|
+
name,
|
|
1410
|
+
required,
|
|
1411
|
+
requiredErrorMessage,
|
|
1412
|
+
validate
|
|
1413
|
+
} = useMergedFormField(formField);
|
|
1414
|
+
useEffectFieldUnregisterOnUnmount({
|
|
1415
|
+
name,
|
|
1416
|
+
unregisterOptions,
|
|
1417
|
+
isUnregisteringOnUnmount
|
|
1418
|
+
});
|
|
1419
|
+
const id = `hook-form-input-toggle-button-${name}`;
|
|
1420
|
+
const valueToUse = !!useWatch({
|
|
1421
|
+
name: name,
|
|
1422
|
+
control: control,
|
|
1423
|
+
exact: true
|
|
1424
|
+
});
|
|
1425
|
+
assertTruthy(typeof valueToUse === "boolean", `Value (Field: ${name}, value: ${valueToUse?.toString() || "undefined"}) for a InputToggleButton should be undefined or boolean`);
|
|
1426
|
+
const onClick = useCallback(
|
|
1427
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1428
|
+
e => {
|
|
1429
|
+
setValue(name, !valueToUse, {
|
|
1430
|
+
shouldValidate: true,
|
|
1431
|
+
shouldDirty: true,
|
|
1432
|
+
shouldTouch: true
|
|
1433
|
+
});
|
|
1434
|
+
}, [name, setValue, valueToUse]);
|
|
1435
|
+
const isRequired = required || muiFormControl?.required;
|
|
1436
|
+
const isDisabled = muiFormControl?.disabled || toggleButtonProps?.disabled;
|
|
1437
|
+
const registered = register(name, {
|
|
1438
|
+
required: isRequired ? requiredErrorMessage : false,
|
|
1439
|
+
validate,
|
|
1440
|
+
disabled: muiFormControl?.disabled
|
|
1441
|
+
});
|
|
1442
|
+
return /*#__PURE__*/jsxs(FormControl, {
|
|
1443
|
+
error: hasError,
|
|
1444
|
+
disabled: isDisabled,
|
|
1445
|
+
required: isRequired,
|
|
1446
|
+
children: [/*#__PURE__*/jsx(FormControlLabel, {
|
|
1447
|
+
required: isRequired,
|
|
1448
|
+
label: label,
|
|
1449
|
+
labelPlacement: "top",
|
|
1450
|
+
disabled: isDisabled,
|
|
1451
|
+
control: /*#__PURE__*/jsx(ToggleButton, {
|
|
1452
|
+
value: valueToUse,
|
|
1453
|
+
selected: valueToUse,
|
|
1454
|
+
"aria-labelledby": id,
|
|
1455
|
+
onClick: onClick,
|
|
1456
|
+
...registered,
|
|
1457
|
+
...toggleButtonProps,
|
|
1458
|
+
children: children
|
|
1459
|
+
}),
|
|
1460
|
+
sx: {
|
|
1461
|
+
alignItems: "flex-start",
|
|
1462
|
+
ml: 0,
|
|
1463
|
+
color: t => hasError ? t.palette.error.main : undefined,
|
|
1464
|
+
...formControlLabelProps?.sx
|
|
1465
|
+
},
|
|
1466
|
+
...formControlLabelProps
|
|
1467
|
+
}), /*#__PURE__*/jsx(FormFieldError, {
|
|
1468
|
+
fieldName: formField.name
|
|
1469
|
+
})]
|
|
1470
|
+
});
|
|
1471
|
+
};
|
|
1472
|
+
|
|
1473
|
+
const InputToggleButtonGroup = ({
|
|
1474
|
+
formField,
|
|
1475
|
+
toggleButtonGroupProps,
|
|
1476
|
+
stackProps,
|
|
1477
|
+
typographyProps,
|
|
1478
|
+
buttons,
|
|
1479
|
+
enforceValueSelected = false,
|
|
1480
|
+
unregisterOptions,
|
|
1481
|
+
isUnregisteringOnUnmount
|
|
1482
|
+
}) => {
|
|
1483
|
+
const {
|
|
1484
|
+
register,
|
|
1485
|
+
setValue,
|
|
1486
|
+
control
|
|
1487
|
+
} = useHookFormContext();
|
|
1488
|
+
const hasError = useFormFieldHasError(formField.name);
|
|
1489
|
+
const muiFormControl = useFormControl();
|
|
1490
|
+
const {
|
|
1491
|
+
label: labelText,
|
|
1492
|
+
name,
|
|
1493
|
+
required,
|
|
1494
|
+
requiredErrorMessage,
|
|
1495
|
+
validate
|
|
1496
|
+
} = useMergedFormField(formField);
|
|
1497
|
+
useEffectFieldUnregisterOnUnmount({
|
|
1498
|
+
name,
|
|
1499
|
+
unregisterOptions,
|
|
1500
|
+
isUnregisteringOnUnmount
|
|
1501
|
+
});
|
|
1502
|
+
const changeEv = useCallback((_, value) => {
|
|
1503
|
+
const onSetValue = () => setValue(name, value, {
|
|
1504
|
+
shouldValidate: true,
|
|
1505
|
+
shouldDirty: true,
|
|
1506
|
+
shouldTouch: true
|
|
1507
|
+
});
|
|
1508
|
+
if (enforceValueSelected) {
|
|
1509
|
+
if (toggleButtonGroupProps?.exclusive === true && value !== null) {
|
|
1510
|
+
onSetValue();
|
|
1511
|
+
} else if (toggleButtonGroupProps?.exclusive === false || toggleButtonGroupProps?.exclusive === undefined && value?.length) {
|
|
1512
|
+
onSetValue();
|
|
1513
|
+
}
|
|
1514
|
+
} else {
|
|
1515
|
+
onSetValue();
|
|
1516
|
+
}
|
|
1517
|
+
}, [enforceValueSelected, name, setValue, toggleButtonGroupProps?.exclusive]);
|
|
1518
|
+
const id = `hook-form-input-toggle-button-group-${name}`;
|
|
1519
|
+
const registered = register(name, {
|
|
1520
|
+
required: required ? requiredErrorMessage : false,
|
|
1521
|
+
validate,
|
|
1522
|
+
disabled: muiFormControl?.disabled
|
|
1523
|
+
});
|
|
1524
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
1525
|
+
const valueFromForm = useWatch({
|
|
1526
|
+
name: name,
|
|
1527
|
+
control: control,
|
|
1528
|
+
exact: true
|
|
1529
|
+
});
|
|
1530
|
+
const isRequired = required || muiFormControl?.required;
|
|
1531
|
+
const isDisabled = muiFormControl?.disabled || toggleButtonGroupProps?.disabled;
|
|
1532
|
+
const toggleButtonGroup = /*#__PURE__*/jsx(ToggleButtonGroup, {
|
|
1533
|
+
...registered,
|
|
1534
|
+
...toggleButtonGroupProps,
|
|
1535
|
+
onChange: changeEv
|
|
1536
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
1537
|
+
,
|
|
1538
|
+
value: valueFromForm,
|
|
1539
|
+
children: buttons.map((button, i) => /*#__PURE__*/jsx(ToggleButton
|
|
1540
|
+
// eslint-disable-next-line react/no-array-index-key
|
|
1541
|
+
, {
|
|
1542
|
+
value: button.value,
|
|
1543
|
+
"aria-labelledby": id,
|
|
1544
|
+
...button.toggleButtonProps,
|
|
1545
|
+
disabled: isDisabled,
|
|
1546
|
+
children: button.icon
|
|
1547
|
+
}, `${button.value.toString()}-${i}`))
|
|
1548
|
+
});
|
|
1549
|
+
const label = /*#__PURE__*/jsx(Typography, {
|
|
1550
|
+
component: "span",
|
|
1551
|
+
...typographyProps,
|
|
1552
|
+
sx: {
|
|
1553
|
+
color: t => {
|
|
1554
|
+
if (hasError) {
|
|
1555
|
+
return t.palette.error.main;
|
|
1556
|
+
}
|
|
1557
|
+
return isDisabled ? t.palette.text.disabled : undefined;
|
|
1558
|
+
}
|
|
1559
|
+
},
|
|
1560
|
+
children: labelText
|
|
1561
|
+
});
|
|
1562
|
+
return /*#__PURE__*/jsxs(FormControl, {
|
|
1563
|
+
error: hasError,
|
|
1564
|
+
disabled: isDisabled,
|
|
1565
|
+
required: isRequired,
|
|
1566
|
+
children: [formField.label ? /*#__PURE__*/jsxs(Stack, {
|
|
1567
|
+
...stackProps,
|
|
1568
|
+
children: [isRequired ? /*#__PURE__*/jsxs(Stack, {
|
|
1569
|
+
id: id,
|
|
1570
|
+
component: "label",
|
|
1571
|
+
sx: {
|
|
1572
|
+
display: "block"
|
|
1573
|
+
},
|
|
1574
|
+
children: [label, /*#__PURE__*/jsxs(Box, {
|
|
1575
|
+
component: "span",
|
|
1576
|
+
"aria-hidden": true,
|
|
1577
|
+
sx: {
|
|
1578
|
+
color: hasError ? t => t.palette.error.main : undefined
|
|
1579
|
+
},
|
|
1580
|
+
children: ["\u2009", "*"]
|
|
1581
|
+
})]
|
|
1582
|
+
}) : label, toggleButtonGroup]
|
|
1583
|
+
}) : toggleButtonGroup, /*#__PURE__*/jsx(FormFieldError, {
|
|
1584
|
+
fieldName: formField.name
|
|
1585
|
+
})]
|
|
1586
|
+
});
|
|
1587
|
+
};
|
|
1588
|
+
|
|
1589
|
+
/**
|
|
1590
|
+
* Turn an async submitHandler into a synchronous one.
|
|
1591
|
+
*
|
|
1592
|
+
* Reasoning:
|
|
1593
|
+
* - we're often talking to the API during form submission. The onSubmit handler will be returning a Promise in this case.
|
|
1594
|
+
* - the `form` element, per definition, always expects a synchronous function.
|
|
1595
|
+
* - creating on inline function in every form is a bit clumsy, and does not look clean.
|
|
1596
|
+
*
|
|
1597
|
+
* Then/catch cases should be properly handled by the submit handler itself,
|
|
1598
|
+
* the catch supplied here is only a last safety net against an "unhandled promise rejection" error.
|
|
1599
|
+
*/
|
|
1600
|
+
const syncSubmit = asyncSubmitHandler => {
|
|
1601
|
+
return e => {
|
|
1602
|
+
asyncSubmitHandler(e).catch(rethrow);
|
|
1603
|
+
};
|
|
1604
|
+
};
|
|
1605
|
+
|
|
1606
|
+
/**
|
|
1607
|
+
* Wrapper around react-hook-form's useForm.
|
|
1608
|
+
*
|
|
1609
|
+
* Should behave like the original function, only exposing a "fieldName"
|
|
1610
|
+
* function on top which is used to validate field names.
|
|
1611
|
+
*/
|
|
1612
|
+
const useHookForm = props => {
|
|
1613
|
+
const form = useForm(props);
|
|
1614
|
+
return {
|
|
1615
|
+
...form,
|
|
1616
|
+
fieldName: identity
|
|
1617
|
+
};
|
|
1618
|
+
};
|
|
1619
|
+
const useHookFormFieldArray = useFieldArray;
|
|
1620
|
+
|
|
1621
|
+
/**
|
|
1622
|
+
* Reexport of FormProvider.
|
|
1623
|
+
*
|
|
1624
|
+
* This became necessary as context is not available when the FormProvider is initialized outside of the package. Most likely this is some kind of instance mismatch.
|
|
1625
|
+
*/
|
|
1626
|
+
const HookFormProvider = FormProvider;
|
|
1627
|
+
|
|
1628
|
+
const InputEmail = props => {
|
|
1629
|
+
const merged = useMergedFormField(props.formField, props.formField.t, formDefaults);
|
|
1630
|
+
const commonProps = useMemo(() => ({
|
|
1631
|
+
textFieldProps: {
|
|
1632
|
+
type: "email",
|
|
1633
|
+
autoComplete: "email",
|
|
1634
|
+
...props.textFieldProps
|
|
1635
|
+
},
|
|
1636
|
+
formField: merged
|
|
1637
|
+
}), [merged, props.textFieldProps]);
|
|
1638
|
+
return props.isUnregisteringOnUnmount ? /*#__PURE__*/jsx(InputTextField, {
|
|
1639
|
+
...commonProps,
|
|
1640
|
+
isUnregisteringOnUnmount: props.isUnregisteringOnUnmount,
|
|
1641
|
+
unregisterOptions: props.unregisterOptions
|
|
1642
|
+
}) : /*#__PURE__*/jsx(InputTextField, {
|
|
1643
|
+
...commonProps,
|
|
1644
|
+
isUnregisteringOnUnmount: props.isUnregisteringOnUnmount
|
|
1645
|
+
});
|
|
1646
|
+
};
|
|
1647
|
+
const formDefaults = {
|
|
1648
|
+
required: true,
|
|
1649
|
+
validate: emailInput => {
|
|
1650
|
+
if (emailInput && emailInput.match(/.@./)) {
|
|
1651
|
+
return true;
|
|
1652
|
+
}
|
|
1653
|
+
return "E-Mail address invalid";
|
|
1654
|
+
}
|
|
1655
|
+
};
|
|
1656
|
+
|
|
1657
|
+
const InputPasswordSet = ({
|
|
1658
|
+
passwordField,
|
|
1659
|
+
passwordValidationField,
|
|
1660
|
+
doNotMatchError,
|
|
1661
|
+
passwordValidationTextFieldProps,
|
|
1662
|
+
passwordTextFieldProps
|
|
1663
|
+
}) => {
|
|
1664
|
+
const {
|
|
1665
|
+
watch
|
|
1666
|
+
} = useHookFormContext();
|
|
1667
|
+
const passwordFieldMerged = useMergedFormField(passwordField, passwordField.t, {
|
|
1668
|
+
required: true
|
|
1669
|
+
});
|
|
1670
|
+
const validationFieldMerged = useMergedFormField(passwordValidationField, passwordValidationField.t, {
|
|
1671
|
+
required: true
|
|
1672
|
+
});
|
|
1673
|
+
const validatePasswordMatch = useCallback(value => {
|
|
1674
|
+
const passwordValue = watch(passwordFieldMerged.name);
|
|
1675
|
+
return value === passwordValue || doNotMatchError;
|
|
1676
|
+
}, [doNotMatchError, passwordFieldMerged.name, watch]);
|
|
1677
|
+
return /*#__PURE__*/jsxs(Fragment, {
|
|
1678
|
+
children: [/*#__PURE__*/jsx(InputTextField, {
|
|
1679
|
+
formField: passwordFieldMerged,
|
|
1680
|
+
textFieldProps: {
|
|
1681
|
+
type: "password",
|
|
1682
|
+
autoComplete: "new-password",
|
|
1683
|
+
...passwordTextFieldProps
|
|
1684
|
+
}
|
|
1685
|
+
}), /*#__PURE__*/jsx(InputTextField, {
|
|
1686
|
+
formField: {
|
|
1687
|
+
...validationFieldMerged,
|
|
1688
|
+
validate: validatePasswordMatch,
|
|
1689
|
+
t: passwordValidationField.t
|
|
1690
|
+
},
|
|
1691
|
+
textFieldProps: {
|
|
1692
|
+
type: "password",
|
|
1693
|
+
autoComplete: "new-password",
|
|
1694
|
+
...passwordValidationTextFieldProps
|
|
1695
|
+
}
|
|
1696
|
+
})]
|
|
1697
|
+
});
|
|
1698
|
+
};
|
|
1699
|
+
|
|
1700
|
+
var LoadingButtonState;
|
|
1701
|
+
(function (LoadingButtonState) {
|
|
1702
|
+
LoadingButtonState["Idle"] = "idle";
|
|
1703
|
+
LoadingButtonState["Loading"] = "loading";
|
|
1704
|
+
LoadingButtonState["Success"] = "success";
|
|
1705
|
+
})(LoadingButtonState || (LoadingButtonState = {}));
|
|
1706
|
+
|
|
1707
|
+
const LoadingButton = ({
|
|
1708
|
+
buttonText,
|
|
1709
|
+
buttonTextSuccess,
|
|
1710
|
+
loadingButtonProps,
|
|
1711
|
+
loadingButtonPropsPropsSuccess,
|
|
1712
|
+
buttonState,
|
|
1713
|
+
successIcon
|
|
1714
|
+
}) => {
|
|
1715
|
+
const isSuccess = buttonState === LoadingButtonState.Success;
|
|
1716
|
+
const propsToSpread = isSuccess ? loadingButtonPropsPropsSuccess : loadingButtonProps;
|
|
1717
|
+
return /*#__PURE__*/jsx(LoadingButton$1, {
|
|
1718
|
+
loading: buttonState === LoadingButtonState.Loading,
|
|
1719
|
+
disabled: buttonState !== LoadingButtonState.Idle,
|
|
1720
|
+
...propsToSpread,
|
|
1721
|
+
children: buttonState === LoadingButtonState.Success ? /*#__PURE__*/jsxs(Fragment, {
|
|
1722
|
+
children: [successIcon ? successIcon : null, buttonTextSuccess ? buttonTextSuccess : buttonText]
|
|
1723
|
+
}) : buttonText
|
|
1724
|
+
});
|
|
1725
|
+
};
|
|
1726
|
+
|
|
1727
|
+
/**
|
|
1728
|
+
* Get loading button state from react-query mutation status.
|
|
1729
|
+
*/
|
|
1730
|
+
const loadingButtonStateFromReactQueryMutationStatus = mutationStatus => {
|
|
1731
|
+
const mapStatus = {
|
|
1732
|
+
idle: LoadingButtonState.Idle,
|
|
1733
|
+
error: LoadingButtonState.Idle,
|
|
1734
|
+
success: LoadingButtonState.Success,
|
|
1735
|
+
loading: LoadingButtonState.Loading
|
|
1736
|
+
};
|
|
1737
|
+
return mapStatus[mutationStatus];
|
|
1738
|
+
};
|
|
1739
|
+
/**
|
|
1740
|
+
* Get loading button state from a react-hook-form context.
|
|
1741
|
+
*/
|
|
1742
|
+
const useLoadingButtonStateFromFormContext = () => {
|
|
1743
|
+
const formContext = useHookFormContext();
|
|
1744
|
+
return loadingButtonStateFromFormState(formContext.formState);
|
|
1745
|
+
};
|
|
1746
|
+
/**
|
|
1747
|
+
* Get loading button state from a react-hook-form state.
|
|
1748
|
+
*/
|
|
1749
|
+
const loadingButtonStateFromFormState = (
|
|
1750
|
+
// we are not interested in the value types FormState holds
|
|
1751
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1752
|
+
formState) => {
|
|
1753
|
+
if (formState.isSubmitting) {
|
|
1754
|
+
return LoadingButtonState.Loading;
|
|
1755
|
+
}
|
|
1756
|
+
if (formState.isDirty) {
|
|
1757
|
+
return LoadingButtonState.Idle;
|
|
1758
|
+
}
|
|
1759
|
+
if (formState.isSubmitSuccessful) {
|
|
1760
|
+
return LoadingButtonState.Success;
|
|
1761
|
+
}
|
|
1762
|
+
return LoadingButtonState.Idle;
|
|
1763
|
+
};
|
|
1764
|
+
|
|
1765
|
+
export { FormFieldError, FormFieldErrorDisplay, HookFormProvider, InputAutocomplete, InputCheckbox, InputDatePicker, InputDateTimePicker, InputEmail, InputNumber, InputOutlinedInput, InputPasswordSet, InputRadio, InputSelect, InputSelectFromEnum, InputSlider, InputSwitch, InputOutlinedInput as InputText, InputTextField, InputTimePicker, InputToggleButton, InputToggleButtonGroup, LoadingButton, LoadingButtonState, getFormErrorForNestedKeys, loadingButtonStateFromFormState, loadingButtonStateFromReactQueryMutationStatus, syncSubmit, useFormFieldError, useFormFieldHasError, useHookForm, useHookFormContext, useHookFormFieldArray, useLoadingButtonStateFromFormContext, useMergedFormField, useSortedOptionsWithGroup, useTranslateString };
|
|
1766
|
+
//# sourceMappingURL=packageExports.js.map
|