@nwon/react-mui-hook-form 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.
Files changed (34) hide show
  1. package/README.md +106 -0
  2. package/dist/LoadingButton/LoadingButton.d.ts +6 -0
  3. package/dist/LoadingButton/LoadingButtonInterfaces.d.ts +33 -0
  4. package/dist/LoadingButton/getLoadingButtonState.d.ts +20 -0
  5. package/dist/LoadingButton/index.d.ts +4 -0
  6. package/dist/formFields/HookFormProvider.d.ts +7 -0
  7. package/dist/formFields/InputEmail/InputEmail.d.ts +11 -0
  8. package/dist/formFields/InputPasswordSet/InputPasswordSet.d.ts +8 -0
  9. package/dist/formFields/InputPasswordSet/InputPasswordSetInterfaces.d.ts +15 -0
  10. package/dist/formFields/TranslatedString.d.ts +13 -0
  11. package/dist/formFields/baseTypes/InputAutocomplete/InputAutocomplete.d.ts +8 -0
  12. package/dist/formFields/baseTypes/InputAutocomplete/InputAutocompleteInterfaces.d.ts +22 -0
  13. package/dist/formFields/baseTypes/InputCheckbox/InputCheckbox.d.ts +16 -0
  14. package/dist/formFields/baseTypes/InputDateTimePicker/InputDateTimePicker.d.ts +12 -0
  15. package/dist/formFields/baseTypes/InputRadio/InputRadio.d.ts +10 -0
  16. package/dist/formFields/baseTypes/InputSelect/InputSelect.d.ts +16 -0
  17. package/dist/formFields/baseTypes/InputSelectFromEnum/InputSelectFromEnum.d.ts +6 -0
  18. package/dist/formFields/baseTypes/InputSelectFromEnum/InputSelectFromEnumInterfaces.d.ts +10 -0
  19. package/dist/formFields/baseTypes/InputText/InputText.d.ts +14 -0
  20. package/dist/formFields/errorMessages.d.ts +1 -0
  21. package/dist/formFields/formHelper/getFormError.d.ts +11 -0
  22. package/dist/formFields/formHelper/syncSubmit.d.ts +15 -0
  23. package/dist/formFields/formHelper/useHookForm.d.ts +50 -0
  24. package/dist/formFields/formHelper/useHookFormContext.d.ts +9 -0
  25. package/dist/formFields/formHelper/useMergedFormField.d.ts +6 -0
  26. package/dist/formFields/formHelper/useSortedOptionsWithGroup.d.ts +5 -0
  27. package/dist/formFields/formHelper/useTranslateString.d.ts +8 -0
  28. package/dist/formFields/index.d.ts +28 -0
  29. package/dist/formFields/inputTypes.d.ts +76 -0
  30. package/dist/packageExports.d.ts +7 -0
  31. package/dist/packageExports.js +727 -0
  32. package/dist/packageExports.js.map +1 -0
  33. package/license +9 -0
  34. package/package.json +1 -0
@@ -0,0 +1,727 @@
1
+ function ___$insertStyle(css) {
2
+ if (!css || typeof window === 'undefined') {
3
+ return;
4
+ }
5
+ const style = document.createElement('style');
6
+ style.setAttribute('type', 'text/css');
7
+ style.innerHTML = css;
8
+ document.head.appendChild(style);
9
+ return css;
10
+ }
11
+
12
+ import { LoadingButton as LoadingButton$1 } from '@mui/lab';
13
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
14
+ import { useFormContext, FormProvider, useFormState, Controller, useForm } from 'react-hook-form';
15
+ import { useFormControl, FormControl, InputLabel, OutlinedInput, FormHelperText, Autocomplete, TextField, FormControlLabel, Checkbox, Box, FormLabel, RadioGroup, Radio, Select, MenuItem, Stack, Tooltip, ListSubheader } from '@mui/material';
16
+ import { uppercaseFirstLetter, assertTruthy, objectHasKey, rethrow } from '@nvon/baseline';
17
+ import { useCallback, useMemo, useState, createElement } from 'react';
18
+ import { t } from 'i18next';
19
+ import { DateTimePicker } from '@mui/x-date-pickers';
20
+ import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline';
21
+ import { identity } from 'lodash-es';
22
+
23
+ var LoadingButtonState;
24
+ (function (LoadingButtonState) {
25
+ LoadingButtonState["Idle"] = "idle";
26
+ LoadingButtonState["Loading"] = "loading";
27
+ LoadingButtonState["Success"] = "success";
28
+ })(LoadingButtonState || (LoadingButtonState = {}));
29
+
30
+ const LoadingButton = ({
31
+ buttonText,
32
+ buttonTextSuccess,
33
+ loadingButtonProps,
34
+ loadingButtonPropsPropsSuccess,
35
+ buttonState,
36
+ successIcon
37
+ }) => {
38
+ const isSuccess = buttonState === LoadingButtonState.Success;
39
+ const propsToSpread = isSuccess ? loadingButtonPropsPropsSuccess : loadingButtonProps;
40
+ return /*#__PURE__*/jsx(LoadingButton$1, {
41
+ loading: buttonState === LoadingButtonState.Loading,
42
+ disabled: buttonState !== LoadingButtonState.Idle,
43
+ ...propsToSpread,
44
+ children: buttonState === LoadingButtonState.Success ? /*#__PURE__*/jsxs(Fragment, {
45
+ children: [successIcon ? successIcon : null, buttonTextSuccess ? buttonTextSuccess : buttonText]
46
+ }) : buttonText
47
+ });
48
+ };
49
+
50
+ /**
51
+ * Get loading button state from react-query mutation status.
52
+ */
53
+ const loadingButtonStateFromReactQueryMutationStatus = mutationStatus => {
54
+ const mapStatus = {
55
+ idle: LoadingButtonState.Idle,
56
+ error: LoadingButtonState.Idle,
57
+ success: LoadingButtonState.Success,
58
+ loading: LoadingButtonState.Loading
59
+ };
60
+ return mapStatus[mutationStatus];
61
+ };
62
+ /**
63
+ * Get loading button state from a react-hook-form context.
64
+ */
65
+ const useLoadingButtonStateFromFormContext = () => {
66
+ const formContext = useFormContext();
67
+ return loadingButtonStateFromFormState(formContext.formState);
68
+ };
69
+ /**
70
+ * Get loading button state from a react-hook-form state.
71
+ */
72
+ const loadingButtonStateFromFormState = (
73
+ // we are not interested in the value types FormState holds
74
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
75
+ formState) => {
76
+ if (formState.isSubmitting) {
77
+ return LoadingButtonState.Loading;
78
+ }
79
+ if (formState.isDirty) {
80
+ return LoadingButtonState.Idle;
81
+ }
82
+ if (formState.isSubmitSuccessful) {
83
+ return LoadingButtonState.Success;
84
+ }
85
+ return LoadingButtonState.Idle;
86
+ };
87
+
88
+ /**
89
+ * Reexport of FormProvider.
90
+ *
91
+ * 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.
92
+ */
93
+ const HookFormProvider = FormProvider;
94
+
95
+ 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";
96
+
97
+ /**
98
+ * Get the field errors for a (possibly nested) field name.
99
+ *
100
+ * Checking `formState.errors[fieldName]` only works if fieldName is not a path
101
+ * (e.g. Does not include dots).
102
+ *
103
+ * Did not find a built-in way to do this … there might be one though.
104
+ */
105
+ const getFormError = (errors, fieldName) => {
106
+ const nestedKeys = fieldName.split(".");
107
+ //
108
+ // To my best knowledge, this works as expected.
109
+ //
110
+ // It recursively walks the error object by the given keys
111
+ // until the lowest level is reached, or undefined returned.
112
+ //
113
+ // Typescript is just not very happy with the dynamic nature of this,
114
+ // hence the errors.
115
+ //
116
+ /* eslint-disable @typescript-eslint/ban-ts-comment,@typescript-eslint/no-unsafe-return */
117
+ // @ts-expect-error
118
+ return nestedKeys.reduce(
119
+ // @ts-expect-error
120
+ (errorObj, pathKey) => errorObj?.[pathKey], errors);
121
+ /* eslint-enable */
122
+ };
123
+
124
+ /**
125
+ * Return a function which turns our TranslatedString into a plain string.
126
+ *
127
+ * @throws Error if no t was provided but a translated string.
128
+ */
129
+ const useTranslateString = () => {
130
+ if (!t) {
131
+ throw Error("If you pass in a translated validate function you need to provide a t");
132
+ }
133
+ return useCallback(input => {
134
+ return typeof input === "string" ? input : input(t);
135
+ }, []);
136
+ };
137
+
138
+ /**
139
+ * Merge a form field with defaults.
140
+ */
141
+ const useMergedFormField = (
142
+ /**
143
+ * Form fields, typically those passed into a component.
144
+ *
145
+ */
146
+ formField, t,
147
+ /**
148
+ * Additional defaults. Duplicate values will be overridden by the formField.
149
+ */
150
+ formFieldDefaults) => {
151
+ const translate = useTranslateString();
152
+ const merged = useMemo(() => {
153
+ const builtInDefaults = {
154
+ label: uppercaseFirstLetter(formField.name),
155
+ required: false,
156
+ requiredErrorMessage: "Please fill in required information",
157
+ validate: undefined
158
+ };
159
+ return {
160
+ ...builtInDefaults,
161
+ ...formFieldDefaults,
162
+ ...formField
163
+ };
164
+ }, [formField, formFieldDefaults]);
165
+ const withPlainStrings = useMemo(() => {
166
+ // Translating the validate function is a bit tricky,
167
+ // since it can be an object in both cases.
168
+ const validate = (() => {
169
+ if (typeof merged.validate === "object" && typeof merged.validate.translated === "function") {
170
+ if (!t) {
171
+ throw Error("If you pass in a translated validate function you need to provide a t");
172
+ }
173
+ return merged.validate.translated(t);
174
+ }
175
+ return merged.validate;
176
+ })(); // This should be fine. Not sure why TS is complaining.
177
+ return {
178
+ ...merged,
179
+ label: translate(merged.label, t),
180
+ requiredErrorMessage: translate(merged.requiredErrorMessage, t),
181
+ validate
182
+ };
183
+ }, [merged, t, translate]);
184
+ return withPlainStrings;
185
+ };
186
+
187
+ const InputText = ({
188
+ formField,
189
+ textFieldProps
190
+ }) => {
191
+ const form = useFormContext();
192
+ const muiFormControl = useFormControl();
193
+ const {
194
+ label,
195
+ name,
196
+ required,
197
+ requiredErrorMessage,
198
+ validate
199
+ } = useMergedFormField(formField, formField.t);
200
+ if (!form) {
201
+ throw Error(formContextNull);
202
+ }
203
+ const {
204
+ register,
205
+ formState
206
+ } = form;
207
+ const error = getFormError(formState.errors, formField.name);
208
+ return /*#__PURE__*/jsxs(FormControl, {
209
+ error: !!error,
210
+ required: required,
211
+ disabled: muiFormControl?.disabled,
212
+ children: [/*#__PURE__*/jsx(InputLabel, {
213
+ required: required,
214
+ children: label
215
+ }), /*#__PURE__*/jsx(OutlinedInput, {
216
+ ...register(name, {
217
+ required: required ? requiredErrorMessage : false,
218
+ validate,
219
+ disabled: muiFormControl?.disabled
220
+ }),
221
+ required: required,
222
+ label: label,
223
+ ...textFieldProps
224
+ }), error?.message && /*#__PURE__*/jsx(FormHelperText, {
225
+ children: error?.message
226
+ })]
227
+ });
228
+ };
229
+
230
+ const InputEmail = props => {
231
+ const merged = useMergedFormField(props.formField, props.formField.t, formDefaults);
232
+ return /*#__PURE__*/jsx(InputText, {
233
+ textFieldProps: {
234
+ type: "email",
235
+ ...props.textFieldProps
236
+ },
237
+ formField: merged
238
+ });
239
+ };
240
+ const formDefaults = {
241
+ required: true,
242
+ validate: emailInput => {
243
+ if (emailInput.match(/.@./)) {
244
+ return true;
245
+ }
246
+ return "E-Mail address invalid";
247
+ }
248
+ };
249
+
250
+ const InputPasswordSet = ({
251
+ passwordField,
252
+ passwordValidationField,
253
+ doNotMatchError
254
+ }) => {
255
+ const {
256
+ watch
257
+ } = useFormContext();
258
+ const passwordFieldMerged = useMergedFormField(passwordField, passwordField.t, {
259
+ required: true
260
+ });
261
+ const validationFieldMerged = useMergedFormField(passwordValidationField, passwordValidationField.t, {
262
+ required: true
263
+ });
264
+ const validatePasswordMatch = useCallback(value => {
265
+ const passwordValue = watch(passwordFieldMerged.name);
266
+ return value === passwordValue || doNotMatchError;
267
+ }, [doNotMatchError, passwordFieldMerged.name, watch]);
268
+ return /*#__PURE__*/jsxs(Fragment, {
269
+ children: [/*#__PURE__*/jsx(InputText, {
270
+ formField: passwordFieldMerged,
271
+ textFieldProps: {
272
+ type: "password"
273
+ }
274
+ }), /*#__PURE__*/jsx(InputText, {
275
+ formField: {
276
+ ...validationFieldMerged,
277
+ validate: validatePasswordMatch,
278
+ t: passwordValidationField.t
279
+ },
280
+ textFieldProps: {
281
+ type: "password"
282
+ }
283
+ })]
284
+ });
285
+ };
286
+
287
+ const InputAutocomplete = ({
288
+ formField,
289
+ selectableOptions,
290
+ multiSelect,
291
+ noOptions,
292
+ autoCompleteProps
293
+ }) => {
294
+ const form = useFormContext();
295
+ if (!form) {
296
+ throw Error(formContextNull);
297
+ }
298
+ const {
299
+ register,
300
+ watch,
301
+ formState,
302
+ setValue
303
+ } = form;
304
+ const error = getFormError(formState.errors, formField.name);
305
+ const muiFormControl = useFormControl();
306
+ const {
307
+ label,
308
+ name,
309
+ required,
310
+ requiredErrorMessage,
311
+ validate
312
+ } = useMergedFormField(formField, formField.t);
313
+ const value = watch(name);
314
+ let valueToUse = value;
315
+ if (!value) {
316
+ valueToUse = multiSelect ? [] : "";
317
+ }
318
+ assertTruthy(["string", "number", "object"].includes(typeof valueToUse), "Value for select input has unexpected type");
319
+ const props = register(name, {
320
+ required: required ? requiredErrorMessage : false,
321
+ validate,
322
+ disabled: muiFormControl?.disabled
323
+ });
324
+ const currentValue = useMemo(() => multiSelect ? selectableOptions.filter(o => {
325
+ if (Array.isArray(valueToUse)) {
326
+ return valueToUse.includes(o.value);
327
+ }
328
+ return valueToUse;
329
+ }) : valueToUse, [multiSelect, selectableOptions, valueToUse]);
330
+ return /*#__PURE__*/jsxs(FormControl, {
331
+ error: !!error,
332
+ required: required,
333
+ disabled: muiFormControl?.disabled,
334
+ children: [/*#__PURE__*/jsx(Autocomplete, {
335
+ options: selectableOptions,
336
+ multiple: multiSelect,
337
+ value: currentValue,
338
+ label: label,
339
+ ...props,
340
+ getOptionLabel: o => {
341
+ if (objectHasKey(o, "label")) {
342
+ return o.label;
343
+ }
344
+ return "";
345
+ },
346
+ onChange: (e, selectedOption) => {
347
+ if (Array.isArray(selectedOption)) {
348
+ setValue(name, selectedOption.map(o => o.value));
349
+ } else {
350
+ setValue(name, selectedOption.value);
351
+ }
352
+ },
353
+ noOptionsText: noOptions,
354
+ renderInput: params => /*#__PURE__*/jsx(TextField, {
355
+ ...params,
356
+ label: label,
357
+ variant: "outlined"
358
+ })
359
+ // need to parse this as any in order to get no typing errors
360
+ // for multiselect
361
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
362
+ ,
363
+ ...autoCompleteProps
364
+ }), error?.message && /*#__PURE__*/jsx(FormHelperText, {
365
+ children: error?.message
366
+ })]
367
+ });
368
+ };
369
+
370
+ const InputCheckbox = ({
371
+ formField,
372
+ jsxLabel
373
+ }) => {
374
+ const {
375
+ register,
376
+ watch,
377
+ setValue
378
+ } = useFormContext();
379
+ const {
380
+ errors
381
+ } = useFormState();
382
+ const error = getFormError(errors, formField.name);
383
+ const muiFormControl = useFormControl();
384
+ const {
385
+ label,
386
+ name,
387
+ required,
388
+ requiredErrorMessage,
389
+ validate
390
+ } = useMergedFormField(formField);
391
+ const changeEv = useCallback(e => {
392
+ setValue(name, e.target.checked, {
393
+ shouldValidate: true,
394
+ shouldDirty: true,
395
+ shouldTouch: true
396
+ });
397
+ }, [name, setValue]);
398
+ const id = `rvo-input-checkbox-${name}`;
399
+ const registered = register(name, {
400
+ required: required ? requiredErrorMessage : false,
401
+ onChange: changeEv,
402
+ validate,
403
+ disabled: muiFormControl?.disabled
404
+ });
405
+ const value = watch(name, false) ?? false;
406
+ return /*#__PURE__*/jsxs(FormControl, {
407
+ error: !!error,
408
+ disabled: muiFormControl?.disabled,
409
+ children: [/*#__PURE__*/jsx(FormControlLabel, {
410
+ required: jsxLabel ? false : required // If jsxLabel is set, we don't want to show the asterisk.
411
+ ,
412
+ control: /*#__PURE__*/jsx(Checkbox, {
413
+ checked: value,
414
+ "aria-labelledby": id,
415
+ ...registered
416
+ }),
417
+ label: jsxLabel ?? label
418
+ }), error?.message && /*#__PURE__*/jsx(FormHelperText, {
419
+ children: error?.message
420
+ })]
421
+ });
422
+ };
423
+
424
+ const InputDateTimePicker = ({
425
+ formField,
426
+ dateTimePickerProps
427
+ }) => {
428
+ const {
429
+ formState,
430
+ control
431
+ } = useFormContext();
432
+ const error = getFormError(formState.errors, formField.name);
433
+ const muiFormControl = useFormControl();
434
+ const {
435
+ label,
436
+ name,
437
+ required
438
+ } = useMergedFormField(formField);
439
+ const [date, setDate] = useState(new Date());
440
+ return /*#__PURE__*/jsx(Box, {
441
+ children: /*#__PURE__*/jsxs(FormControl, {
442
+ error: !!error,
443
+ required: required,
444
+ disabled: muiFormControl?.disabled,
445
+ children: [/*#__PURE__*/jsx(Controller, {
446
+ name: name,
447
+ defaultValue: date,
448
+ control: control,
449
+ render: ({
450
+ field: {
451
+ onChange,
452
+ ...restField
453
+ }
454
+ }) => /*#__PURE__*/jsx(DateTimePicker, {
455
+ label: label,
456
+ ampm: dateTimePickerProps?.ampm ? dateTimePickerProps.ampm : false,
457
+ onChange: event => {
458
+ onChange(event);
459
+ if (event) {
460
+ setDate(event);
461
+ }
462
+ },
463
+ disabled: muiFormControl?.disabled,
464
+ renderInput: params => /*#__PURE__*/jsx(TextField, {
465
+ required: required,
466
+ ...params
467
+ }),
468
+ ...restField
469
+ })
470
+ }), error?.message && /*#__PURE__*/jsx(FormHelperText, {
471
+ children: error?.message
472
+ })]
473
+ })
474
+ });
475
+ };
476
+
477
+ const InputRadio = ({
478
+ formField,
479
+ selectableOptions
480
+ }) => {
481
+ const {
482
+ register,
483
+ watch,
484
+ setValue
485
+ } = useFormContext();
486
+ const {
487
+ errors
488
+ } = useFormState();
489
+ const error = getFormError(errors, formField.name);
490
+ const muiFormControl = useFormControl();
491
+ const {
492
+ label,
493
+ name,
494
+ required,
495
+ requiredErrorMessage,
496
+ validate
497
+ } = useMergedFormField(formField);
498
+ const changeEv = useCallback(e => {
499
+ setValue(name, e.target.value, {
500
+ shouldValidate: true,
501
+ shouldDirty: true,
502
+ shouldTouch: true
503
+ });
504
+ }, [name, setValue]);
505
+ const value = watch(name) ?? "";
506
+ assertTruthy(["string", "number"].includes(typeof value), "Value for select input has unexpected type");
507
+ const id = `rvo-input-radio-${name}`;
508
+ const registered = register(name, {
509
+ required: required ? requiredErrorMessage : false,
510
+ onChange: changeEv,
511
+ validate,
512
+ disabled: muiFormControl?.disabled
513
+ });
514
+ return /*#__PURE__*/jsxs(FormControl, {
515
+ error: !!error,
516
+ disabled: muiFormControl?.disabled,
517
+ children: [/*#__PURE__*/jsx(FormLabel, {
518
+ id: id,
519
+ required: required,
520
+ children: label
521
+ }), /*#__PURE__*/jsx(RadioGroup, {
522
+ value: value,
523
+ "aria-labelledby": id,
524
+ children: selectableOptions?.map(option => {
525
+ return /*#__PURE__*/createElement(FormControlLabel, {
526
+ ...registered,
527
+ key: option.value,
528
+ value: option.value,
529
+ control: /*#__PURE__*/jsx(Radio, {}),
530
+ label: option.label
531
+ });
532
+ })
533
+ }), error?.message && /*#__PURE__*/jsx(FormHelperText, {
534
+ children: error?.message
535
+ })]
536
+ });
537
+ };
538
+
539
+ /**
540
+ * Sorts the options by group and value.
541
+ */
542
+ const useSortedOptionsWithGroup = selectableOptions => {
543
+ return useMemo(() => selectableOptions.sort((a, b) => {
544
+ if (a.group == undefined && !!b.group) {
545
+ return -1;
546
+ }
547
+ if (a.group && b.group && a.group < b.group) {
548
+ return -1;
549
+ }
550
+ if (a.group == b.group && b.value > a.value) {
551
+ return 1;
552
+ }
553
+ return 0;
554
+ }), [selectableOptions]);
555
+ };
556
+
557
+ const InputSelect = ({
558
+ formField,
559
+ selectableOptions,
560
+ placeholderText,
561
+ selectComponentProps
562
+ }) => {
563
+ const form = useFormContext();
564
+ if (!form) {
565
+ throw Error(formContextNull);
566
+ }
567
+ const {
568
+ register,
569
+ watch,
570
+ formState
571
+ } = form;
572
+ const error = getFormError(formState.errors, formField.name);
573
+ const muiFormControl = useFormControl();
574
+ const {
575
+ label,
576
+ name,
577
+ required,
578
+ requiredErrorMessage,
579
+ validate
580
+ } = useMergedFormField(formField, formField.t);
581
+ const value = watch(name) ?? "";
582
+ assertTruthy(["string", "number"].includes(typeof value), "Value for select input has unexpected type");
583
+ const id = `input-select-${name}`;
584
+ const sortedOptions = useSortedOptionsWithGroup(selectableOptions);
585
+ return /*#__PURE__*/jsxs(FormControl, {
586
+ error: !!error,
587
+ required: required,
588
+ disabled: muiFormControl?.disabled,
589
+ children: [/*#__PURE__*/jsx(InputLabel, {
590
+ id: id,
591
+ required: required,
592
+ children: label
593
+ }), /*#__PURE__*/jsxs(Select, {
594
+ labelId: id,
595
+ ...selectComponentProps,
596
+ error: !!formState.errors[name],
597
+ label: label,
598
+ value: value,
599
+ ...register(name, {
600
+ required: required ? requiredErrorMessage : false,
601
+ validate,
602
+ disabled: muiFormControl?.disabled
603
+ }),
604
+ children: [/*#__PURE__*/jsx(MenuItem, {
605
+ value: "",
606
+ children: /*#__PURE__*/jsx("em", {
607
+ children: placeholderText
608
+ })
609
+ }), sortedOptions?.map((option, index) => {
610
+ const optionBefore = sortedOptions[index - 1];
611
+ const showGroup = (optionBefore || index == 0) && option.group && optionBefore?.group !== option.group;
612
+ const disabledText = typeof option.disabled === "string" ? option.disabled : undefined;
613
+ const isDisabled = !!disabledText || !!option.disabled;
614
+ const menuItem = /*#__PURE__*/jsx(MenuItem, {
615
+ value: option.value,
616
+ disabled: isDisabled,
617
+ title: typeof option.disabled === "string" ? option.disabled : undefined,
618
+ selected: value === option.value,
619
+ children: option.label
620
+ }, option.label);
621
+ const wrappedMenuItem = disabledText ? /*#__PURE__*/jsxs(Box, {
622
+ sx: {
623
+ position: "relative"
624
+ },
625
+ children: [menuItem, /*#__PURE__*/jsx(Stack, {
626
+ direction: "column",
627
+ justifyContent: "center",
628
+ spacing: 1,
629
+ sx: {
630
+ position: "absolute",
631
+ right: th => th.spacing(0.5),
632
+ top: 0,
633
+ bottom: 0
634
+ },
635
+ children: /*#__PURE__*/jsx(Tooltip, {
636
+ title: disabledText,
637
+ children: /*#__PURE__*/jsx(ErrorOutlineIcon, {})
638
+ })
639
+ })]
640
+ }) : menuItem;
641
+ return showGroup ? [/*#__PURE__*/jsx(ListSubheader, {
642
+ children: option.group
643
+ }, option.group), wrappedMenuItem] : wrappedMenuItem;
644
+ })]
645
+ }), error?.message && /*#__PURE__*/jsx(FormHelperText, {
646
+ children: error?.message
647
+ })]
648
+ });
649
+ };
650
+
651
+ const InputSelectFromEnum = ({
652
+ enumData,
653
+ formField,
654
+ placeholderText
655
+ }) => {
656
+ const translateFunction = formField.t;
657
+ const options = useMemo(() => {
658
+ return Object.entries(enumData.enumToUse).map(([, value]) => {
659
+ const label = enumData.labelsForEnum[value];
660
+ assertTruthy(label, "Could not find a label for value" + value);
661
+ if (typeof label === "string") {
662
+ return {
663
+ label,
664
+ value
665
+ };
666
+ }
667
+ assertTruthy(translateFunction, "If you want to use translated labels you need to provide a translate function in the HookFormProvider.");
668
+ return {
669
+ label: label(translateFunction),
670
+ value
671
+ };
672
+ });
673
+ }, [enumData.enumToUse, enumData.labelsForEnum, translateFunction]);
674
+ return /*#__PURE__*/jsx(InputSelect, {
675
+ selectableOptions: options,
676
+ formField: formField,
677
+ placeholderText: placeholderText
678
+ });
679
+ };
680
+
681
+ /**
682
+ * Turn an async submitHandler into a synchronous one.
683
+ *
684
+ * Reasoning:
685
+ * - we're often talking to the API during form submission. The onSubmit handler will be returning a Promise in this case.
686
+ * - the `form` element, per definition, always expects a synchronous function.
687
+ * - creating on inline function in every form is a bit clumsy, and does not look clean.
688
+ *
689
+ * Then/catch cases should be properly handled by the submit handler itself,
690
+ * the catch supplied here is only a last safety net against an "unhandled promise rejection" error.
691
+ */
692
+ const syncSubmit = asyncSubmitHandler => {
693
+ return e => {
694
+ asyncSubmitHandler(e).catch(rethrow);
695
+ };
696
+ };
697
+
698
+ /**
699
+ * Wrapper around react-hook-form's useForm.
700
+ *
701
+ * Should behave like the original function, only exposing a "fieldName"
702
+ * function on top which is used to validate field names.
703
+ */
704
+ const useHookForm = props => {
705
+ const form = useForm(props);
706
+ return {
707
+ ...form,
708
+ fieldName: identity
709
+ };
710
+ };
711
+
712
+ /**
713
+ * Wrapper around react-hook-form's useFormContext.
714
+ *
715
+ * Should behave like the original function, only exposing a "fieldName"
716
+ * function on top which is used to validate field names.
717
+ */
718
+ const useHookFormContext = () => {
719
+ const form = useFormContext();
720
+ return {
721
+ ...form,
722
+ fieldName: identity
723
+ };
724
+ };
725
+
726
+ export { HookFormProvider, InputAutocomplete, InputCheckbox, InputDateTimePicker, InputEmail, InputPasswordSet, InputRadio, InputSelect, InputSelectFromEnum, InputText, LoadingButton, getFormError, loadingButtonStateFromFormState, loadingButtonStateFromReactQueryMutationStatus, syncSubmit, useHookForm, useHookFormContext, useLoadingButtonStateFromFormContext, useMergedFormField, useSortedOptionsWithGroup, useTranslateString };
727
+ //# sourceMappingURL=packageExports.js.map