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() {