1+ import {
2+ CircularProgress ,
3+ FormControl ,
4+ FormHelperText ,
5+ InputLabel ,
6+ ListItemText ,
7+ MenuItem ,
8+ Select ,
9+ Stack ,
10+ Typography ,
11+ } from '@mui/material' ;
112import { Controller } from 'react-hook-form' ;
2- import { FormControl , FormHelperText , InputLabel , ListItemText , MenuItem , Select } from '@mui/material' ;
3- import { useFieldLabel } from '../index' ;
4- import type { FieldProps } from '../index' ;
5- import type { FieldValues } from 'react-hook-form' ;
13+ import { useAsyncFieldLabels , useFieldLabels , useFieldWithOptionsLabels } from '../index' ;
14+ import { useMemo } from 'react' ;
15+ import type { AsyncFieldProps , FieldProps , FieldWithOptionsProps , ObjectLike } from '../index' ;
616import type {
17+ CircularProgressProps ,
718 FormControlProps ,
819 FormHelperTextProps ,
920 InputLabelProps ,
1021 ListItemTextProps ,
1122 MenuItemProps ,
1223 SelectProps ,
24+ StackProps ,
25+ TypographyProps ,
1326} from '@mui/material' ;
27+ import type { FieldValues } from 'react-hook-form' ;
1428
1529interface MuiProps {
1630 formControlProps ?: FormControlProps ;
1731 selectProps ?: SelectProps ;
1832 inputLabelProps ?: InputLabelProps ;
1933 menuItemProps ?: MenuItemProps ;
34+ loadingMenuItemProps ?: MenuItemProps ;
35+ errorMenuItemProps ?: MenuItemProps ;
36+ noOptionsMenuItemProps ?: MenuItemProps ;
2037 listItemTextProps ?: ListItemTextProps ;
2138 formHelperTextProps ?: FormHelperTextProps ;
39+ loadingTypographyProps ?: TypographyProps ;
40+ errorTypographyProps ?: TypographyProps ;
41+ noOptionsTypographyProps ?: TypographyProps ;
42+ loadingStackProps ?: StackProps ;
43+ loadingCircularProgressProps ?: CircularProgressProps ;
2244}
2345
24- export interface SelectFieldProps < T extends FieldValues , V extends Record < string , unknown > > extends FieldProps < T > {
46+ export interface SelectFieldProps < T extends FieldValues , V extends ObjectLike >
47+ extends FieldProps < T > ,
48+ AsyncFieldProps ,
49+ FieldWithOptionsProps < V > {
2550 muiProps ?: MuiProps ;
26- options : V [ ] ;
2751 optionValueAccessor : ( value : V ) => string | number ;
2852 optionLabelAccessor : ( value : V ) => string ;
2953 optionExtraLabelAccessor ?: ( value : V ) => string ;
3054}
3155
32- export const SelectField = < T extends FieldValues , V extends Record < string , unknown > > ( {
56+ export const SelectField = < T extends FieldValues , V extends ObjectLike > ( {
3357 control,
3458 label,
3559 name,
@@ -41,8 +65,17 @@ export const SelectField = <T extends FieldValues, V extends Record<string, unkn
4165 optionLabelAccessor,
4266 optionValueAccessor,
4367 optionExtraLabelAccessor,
68+ loadingErrorLabel,
69+ isLoading = false ,
70+ loadingLabel,
71+ isError = false ,
72+ noOptionsLabel,
4473} : SelectFieldProps < T , V > ) => {
45- const { fieldLabel } = useFieldLabel ( { isOptional, label, requiredLabel } ) ;
74+ const { fieldLabel } = useFieldLabels ( { isOptional, label, requiredLabel } ) ;
75+ const { fieldLoadingErrorLabel, fieldLoadingLabel } = useAsyncFieldLabels ( { loadingErrorLabel, loadingLabel } ) ;
76+ const { fieldNoOptionsLabel } = useFieldWithOptionsLabels ( { noOptionsLabel } ) ;
77+
78+ const displayOptions = useMemo ( ( ) => ! isLoading && ! isError , [ isError , isLoading ] ) ;
4679
4780 return (
4881 < Controller
@@ -61,7 +94,7 @@ export const SelectField = <T extends FieldValues, V extends Record<string, unkn
6194 aria-required = { isOptional ? 'false' : 'true' }
6295 label = { fieldLabel }
6396 renderValue = { ( selected ) => {
64- const found = options . find ( ( option ) => optionValueAccessor ( option ) === selected ) ;
97+ const found = options ? .find ( ( option ) => optionValueAccessor ( option ) === selected ) ;
6598
6699 if ( found ) {
67100 return optionLabelAccessor ( found ) ;
@@ -70,19 +103,56 @@ export const SelectField = <T extends FieldValues, V extends Record<string, unkn
70103 return '' ;
71104 } }
72105 >
73- { options . map ( ( option ) => (
106+ { isLoading && (
107+ < MenuItem
108+ { ...muiProps ?. loadingMenuItemProps }
109+ disabled
110+ >
111+ < Stack
112+ alignItems = 'center'
113+ flexDirection = 'row'
114+ justifyContent = 'space-between'
115+ width = '100%'
116+ { ...muiProps ?. loadingStackProps }
117+ >
118+ < Typography { ...muiProps ?. loadingTypographyProps } > { fieldLoadingLabel } </ Typography >
119+ < CircularProgress
120+ size = { 30 }
121+ { ...muiProps ?. loadingCircularProgressProps }
122+ />
123+ </ Stack >
124+ </ MenuItem >
125+ ) }
126+ { isError && (
127+ < MenuItem
128+ { ...muiProps ?. errorMenuItemProps }
129+ disabled
130+ >
131+ < Typography { ...muiProps ?. errorTypographyProps } > { fieldLoadingErrorLabel } </ Typography >
132+ </ MenuItem >
133+ ) }
134+ { displayOptions && options ?. length === 0 && (
74135 < MenuItem
75- { ...muiProps ?. menuItemProps }
76- key = { optionValueAccessor ( option ) }
77- value = { optionValueAccessor ( option ) }
136+ disabled
137+ { ...muiProps ?. noOptionsMenuItemProps }
78138 >
79- < ListItemText
80- { ...muiProps ?. listItemTextProps }
81- primary = { optionLabelAccessor ( option ) }
82- secondary = { optionExtraLabelAccessor ?.( option ) }
83- />
139+ < Typography { ...muiProps ?. noOptionsTypographyProps } > { fieldNoOptionsLabel } </ Typography >
84140 </ MenuItem >
85- ) ) }
141+ ) }
142+ { displayOptions &&
143+ options ?. map ( ( option ) => (
144+ < MenuItem
145+ { ...muiProps ?. menuItemProps }
146+ key = { optionValueAccessor ( option ) }
147+ value = { optionValueAccessor ( option ) }
148+ >
149+ < ListItemText
150+ { ...muiProps ?. listItemTextProps }
151+ primary = { optionLabelAccessor ( option ) }
152+ secondary = { optionExtraLabelAccessor ?.( option ) }
153+ />
154+ </ MenuItem >
155+ ) ) }
86156 </ Select >
87157 { invalid && (
88158 < FormHelperText { ...muiProps ?. formHelperTextProps } >
0 commit comments