SelectField.options now supports more formats

Massimo Melina committed Mar 8, 2022 at 15:36 UTC 48794a36eeb9aebac18523dc7a22d9cfe7d50808
1 file changed +21 -17
admin/src/Form.ts
+21 -17
@@ -149,16 +149,15 @@ export function DisplayField({ value, empty='-', ...props }: any) {
149 return h(StringField, { ...props, value, disabled: true })
150 }
151
152 +type SelectOptions<T> = { [label:string]:T } | SelectOption<T>[]
153 +type SelectOption<T> = SelectPair<T> | (T extends string | number ? T : never)
154 interface SelectPair<T> { label: string, value:T }
153 -export function SelectField<T>(props: FieldProps<T> & { options:SelectPair<T>[] }) {
155 +
156 +export function SelectField<T>(props: FieldProps<T> & { options:SelectOptions<T> }) {
157 const { value, onChange, options, ...rest } = props
155 - const jsonValue = JSON.stringify(value)
156 - const currentOption = options.find(x => JSON.stringify(x.value) === jsonValue)
158 return h(TextField, { // using TextField because Select is not displaying label correctly
159 ...rest,
160 ...commonSelectProps(props),
160 - // 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.
161 - value: currentOption ? jsonValue : '',
161 onChange(event) {
162 try {
163 let newVal: any = event.target.value
@@ -170,36 +169,41 @@ export function SelectField<T>(props: FieldProps<T> & { options:SelectPair<T>[]
169 })
170 }
171
173 -export function MultiSelectField<T>(props: FieldProps<T[]> & { options:SelectPair<T>[] }) {
174 - const { value, onChange, options, ...rest } = props
172 +export function MultiSelectField<T>(props: FieldProps<T[]> & { options:SelectOptions<T> }) {
173 + const { value, options, ...rest } = props
174 return h(TextField, {
175 ...rest,
177 - ...commonSelectProps(props),
176 + ...commonSelectProps({ ...props, value:undefined }),
177 SelectProps: { multiple: true },
178 value: !Array.isArray(value) ? [] : value.map(x => JSON.stringify(x)),
179 onChange(event) {
180 try {
181 let v: any = event.target.value
182 v = Array.isArray(v) ? v.map(x => JSON.parse(x)) : []
184 - onChange(v as T[], { was: value, event })
183 + props.onChange(v as T[], { was: value, event })
184 }
185 catch {}
186 }
187 })
188 }
189
191 -function commonSelectProps<T>(props: { disabled?: boolean, options:SelectPair<T>[] }) {
190 +function commonSelectProps<T>(props: { value?: T, disabled?: boolean, options:SelectOptions<T> }) {
191 const { options, disabled } = props
192 + const normalizedOptions = !Array.isArray(options) ? Object.entries(options).map(([label,value]) => ({ value, label }))
193 + : options.map(o => typeof o === 'string' || typeof o === 'number' ? { value: o, label: String(o) } : o as SelectPair<T>)
194 + const jsonValue = JSON.stringify(props.value)
195 + const currentOption = normalizedOptions.find(x => JSON.stringify(x.value) === jsonValue)
196 return {
197 select: true,
198 fullWidth: true,
196 - disabled: !options?.length || disabled,
197 - children: options.map((o, i) => {
198 - const obj = o && typeof o === 'object'
199 - const value = obj && 'value' in o ? o.value : o
200 - const label = obj && 'label' in o ? o.label : o
201 - return h(MenuItem, { key: i, value: JSON.stringify(value), children: label })
202 - })
199 + // 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.
200 + value: currentOption ? jsonValue : '',
201 + disabled: !normalizedOptions?.length || disabled,
202 + children: normalizedOptions.map((o, i) => h(MenuItem, {
203 + key: i,
204 + value: JSON.stringify(o?.value),
205 + children: o?.label
206 + }))
207 }
208 }
209