dev: better SelectField
Massimo Melina committed
Jul 23, 2023 at 10:49 UTC
d5a18f866f345673907add2e62f0259eff234425
2 files changed
+32
-8
mui-grid-form/SelectField.ts
+30
-6
@@ -1,15 +1,25 @@
1
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
3
-import { createElement as h } from 'react'
3
+import { createElement as h, ReactNode } from 'react'
4
import { FieldProps } from '.'
5
-import { FormControl, FormControlLabel, FormLabel, MenuItem, Radio, RadioGroup, TextField } from '@mui/material'
5
+import {
6
+ FormControl,
7
+ FormControlLabel,
8
+ FormLabel,
9
+ InputAdornment,
10
+ MenuItem,
11
+ Radio,
12
+ RadioGroup,
13
+ StandardTextFieldProps,
14
+ TextField
15
+} from '@mui/material'
16
import { SxProps } from '@mui/system'
17
18
type SelectOptions<T> = { [label:string]:T } | SelectOption<T>[]
19
type SelectOption<T> = SelectPair<T> | (T extends string | number ? T : never)
20
interface SelectPair<T> { label: string, value:T }
21
12
-export function SelectField<T>(props: FieldProps<T> & { options:SelectOptions<T> }) {
22
+export function SelectField<T>(props: FieldProps<T> & CommonSelectProps<T>) {
23
const { value, onChange, getApi, options, sx, ...rest } = props
24
return h(TextField, { // using TextField because Select is not displaying label correctly
25
...commonSelectProps(props),
@@ -25,7 +35,7 @@ export function SelectField<T>(props: FieldProps<T> & { options:SelectOptions<T>
35
})
36
}
37
28
-export function MultiSelectField<T>(props: FieldProps<T[]> & { options:SelectOptions<T> }) {
38
+export function MultiSelectField<T>(props: FieldProps<T[]> & CommonSelectProps<T>) {
39
const { value, onChange, getApi, options, sx, ...rest } = props
40
return h(TextField, {
41
...commonSelectProps({ ...props, value: undefined }),
@@ -43,8 +53,17 @@ export function MultiSelectField<T>(props: FieldProps<T[]> & { options:SelectOpt
53
})
54
}
55
46
-function commonSelectProps<T>(props: { sx?:SxProps, label?: FieldProps<T>['label'], value?: T, disabled?: boolean, options:SelectOptions<T> }) {
47
- const { options, disabled } = props
56
+interface CommonSelectProps<T> extends Partial<Omit<StandardTextFieldProps, 'label' | 'onChange' | 'value'>> {
57
+ sx?: SxProps
58
+ label?: FieldProps<T>['label']
59
+ value?: T
60
+ disabled?: boolean
61
+ options: SelectOptions<T>
62
+ start?: ReactNode
63
+ end?: ReactNode
64
+}
65
+function commonSelectProps<T>(props: CommonSelectProps<T>) {
66
+ const { options, disabled, start, end } = props
67
const normalizedOptions = !Array.isArray(options) ? Object.entries(options).map(([label,value]) => ({ value, label }))
68
: options.map(o => typeof o === 'string' || typeof o === 'number' ? { value: o, label: String(o) } : o as SelectPair<T>)
69
const jsonValue = JSON.stringify(props.value)
@@ -56,6 +75,11 @@ function commonSelectProps<T>(props: { sx?:SxProps, label?: FieldProps<T>['label
75
// avoid warning for invalid option. This can easily happen for a split-second when you keep value in a useState (or other async way) and calculate options with a useMemo (or other sync way) causing a temporary misalignment.
76
value: currentOption ? jsonValue : '',
77
disabled: !normalizedOptions?.length || disabled,
78
+ InputProps: {
79
+ startAdornment: start && h(InputAdornment, { position: 'start' }, start),
80
+ endAdornment: end && h(InputAdornment, { position: 'end' }, end),
81
+ ...props.InputProps,
82
+ },
83
children: normalizedOptions.map((o, i) => h(MenuItem, {
84
key: i,
85
value: JSON.stringify(o?.value),
mui-grid-form/StringField.ts
+2
-2
@@ -5,7 +5,7 @@ import { FieldProps } from '.'
5
import { Autocomplete, InputAdornment, TextField } from '@mui/material'
6
import { StandardTextFieldProps } from '@mui/material/TextField/TextField'
7
8
-interface StringProps extends FieldProps<string>, Partial<Omit<StandardTextFieldProps, 'label' | 'onChange' | 'value'>> {
8
+interface StringFieldProps extends FieldProps<string>, Partial<Omit<StandardTextFieldProps, 'label' | 'onChange' | 'value'>> {
9
typing?: boolean
10
onTyping?: (v: string) => boolean
11
min?: number
@@ -14,7 +14,7 @@ interface StringProps extends FieldProps<string>, Partial<Omit<StandardTextField
14
start?: ReactNode
15
end?: ReactNode
16
}
17
-export function StringField({ value, onChange, min, max, required, getApi, typing, start, end, onTyping, suggestions, ...props }: StringProps) {
17
+export function StringField({ value, onChange, min, max, required, getApi, typing, start, end, onTyping, suggestions, ...props }: StringFieldProps) {
18
const normalized = value ?? ''
19
getApi?.({
20
getError() {