UNPKG

react-mapfilter

Version:

These components are designed for viewing data in Mapeo. They share a common interface:

176 lines (162 loc) 3.96 kB
// @flow import * as React from 'react' import TextField from './TextField' import Autocomplete from '@material-ui/lab/Autocomplete' import { makeStyles } from '@material-ui/core/styles' import { primitiveToString } from '../utils/strings' import type { SelectableFieldValue, LabeledSelectOption, SelectOptions } from '../types' const useStyles = makeStyles(theme => ({ root: { flexGrow: 1, height: 250 }, container: { width: '100%' }, paper: { position: 'absolute', zIndex: 1, marginTop: theme.spacing(1), left: 0, right: 0 }, chip: { margin: theme.spacing(0.5, 0.25) }, inputRoot: { flexWrap: 'wrap' }, inputInput: { width: 'auto', flexGrow: 1 }, divider: { height: theme.spacing(2) } })) function renderInput(inputProps) { const { InputProps, classes, ref, ...other } = inputProps return ( <TextField InputProps={{ inputRef: ref, classes: { root: classes.inputRoot, input: classes.inputInput }, ...InputProps }} {...other} /> ) } type SelectOneProps = { id?: string, label: string | React.Element<any>, value: SelectableFieldValue, placeholder?: string, onChange: (value: SelectableFieldValue | void) => any, options: SelectOptions } type SelectMultipleProps = { ...$Exact<SelectOneProps>, value: Array<SelectableFieldValue> } function Encoder(options: Array<LabeledSelectOption>) { return { toValue: (label: string | null): SelectableFieldValue | void => { if (label === null) return var opts = options.find(ops => ops.label === label) return opts && typeof opts.value !== 'undefined' ? opts.value : label }, toLabel: (value: SelectableFieldValue): string => { var opts = options.find(ops => ops.value === value) return (opts && opts.label) || primitiveToString(value) } } } function isSelectableFieldValue( v: SelectableFieldValue | LabeledSelectOption ): boolean { return ( typeof v === 'string' || typeof v === 'boolean' || typeof v === 'number' || v === null ) } function getLabeledSelectOptions( options: SelectOptions ): Array<LabeledSelectOption> { return options.map(opt => isSelectableFieldValue(opt) ? // $FlowFixMe { label: primitiveToString(opt), value: opt } : // $FlowFixMe opt ) } /** * A multi-select field that allows the user to enter a value that is not on the * list. Allows the selection of non-string values from a list, but the labels * for each item must be unique for this to work reliably */ export const SelectOne = ({ id, value, label, options, placeholder, onChange, ...props }: SelectOneProps) => { const classes = useStyles() const labeledOptions = getLabeledSelectOptions(options) const encoder = Encoder(labeledOptions) return ( <Autocomplete id={id} value={encoder.toLabel(value)} onChange={(e, v: string) => onChange(encoder.toValue(v))} options={labeledOptions.map(opt => opt.label)} renderInput={params => renderInput({ ...params, classes, label, placeholder }) } {...props} /> ) } export const SelectMultiple = ({ id, value, label, options, placeholder, onChange, ...props }: SelectMultipleProps) => { const classes = useStyles() const labeledOptions = getLabeledSelectOptions(options) const encoder = Encoder(labeledOptions) let arrayValue = [] if (!Array.isArray(value)) arrayValue = [value] else if (value) arrayValue = value return ( <Autocomplete id={id} multiple freeSolo value={arrayValue.map(encoder.toLabel)} onChange={(e, v) => onChange(v.map(encoder.toValue))} options={labeledOptions.map(opt => opt.label)} renderInput={params => renderInput({ ...params, classes, label, placeholder }) } {...props} /> ) }