UNPKG

rmwc

Version:

A thin React wrapper for Material Design (Web) Components

186 lines (166 loc) 4.9 kB
// @flow import type { SimpleTagPropsT } from '../Base'; import * as React from 'react'; import { MDCSelect } from '@material/select/dist/mdc.select'; import { simpleTag, withFoundation, syncFoundationProp } from '../Base'; import { FloatingLabel } from '../FloatingLabel'; import { LineRipple } from '../LineRipple'; type FormattedOption = { label: string, value: string, options?: FormattedOption[] }; export type SelectPropsT = { /** The value for a controlled select. */ value?: mixed, /** Options accepts flat arrays, value => label maps, and more. See examples for details. */ options?: string[] | { [value: string]: string } | any[], /** A label for the form control. */ label?: string, /** Placeholder text for the form control. Set to a blank string to create a non-floating placeholder label. */ placeholder?: string, /** Disables the form control. */ disabled?: boolean, /** Makes the Select have a visual box. */ box?: boolean, /** Props for the root element. By default, additional props spread to the native select element. */ rootProps?: Object, /** A className for the root element. */ className?: string } & SimpleTagPropsT; export const SelectRoot = simpleTag({ displayName: 'SelectRoot', classNames: (props: SelectPropsT) => [ 'mdc-select', { 'mdc-select--box': props.box } ], consumeProps: ['box'], defaultProps: { role: 'listbox' } }); export const SelectSurface = simpleTag({ displayName: 'SelectSurface', classNames: 'mdc-select__surface' }); export const SelectSelectedText = simpleTag({ displayName: 'SelectSelectedText', classNames: 'mdc-select__selected-text' }); export const SelectBottomLine = simpleTag({ displayName: 'SelectBottomLine', classNames: 'mdc-select__bottom-line' }); export const SelectNativeControl = simpleTag({ displayName: 'SelectNativeControl', tag: 'select', classNames: 'mdc-select__native-control' }); /** * Takes multiple structures for options and returns [{label: 'label', value: 'value', ...rest}] */ const createSelectOptions = (options): FormattedOption[] => { // preformatted array if (Array.isArray(options) && options[0] && typeof options[0] === 'object') { return options.map(opt => { if (typeof opt !== 'object') { throw new Error(`Encountered non object for Select ${opt}`); } return { ...opt, options: createSelectOptions(opt.options) }; }); } // simple array if (Array.isArray(options)) { return options.map(value => ({ value, label: value })); } // value => label objects if (typeof options === 'object') { return Object.keys(options).map(value => ({ value, label: options[value] })); } // default, just return return options; }; export class Select extends withFoundation({ constructor: MDCSelect, adapter: { getValue: function() { const value = this.nativeControl_.value; return value === '' && this.props.placeholder ? ' ' : value; } } })<SelectPropsT> { static displayName = 'Select'; disabled: boolean; syncWithProps(nextProps: SelectPropsT) { //disabled syncFoundationProp( nextProps.disabled, this.disabled, () => (this.disabled = !!nextProps.disabled) ); } render() { const { placeholder, children, value, label = '', options = [], box, className, rootProps = {}, apiRef, ...rest } = this.props; const { root_ } = this.foundationRefs; const selectOptions = createSelectOptions(options); return ( <SelectRoot {...rootProps} box={box} elementRef={root_} className={className} > <SelectNativeControl {...rest} value={value} defaultValue={value ? undefined : ''} > {(!!placeholder || placeholder === '') && ( <option value="" disabled={placeholder === ''}> {placeholder} </option> )} {selectOptions && selectOptions.map(({ label, options, ...option }, i) => { if (options) { return ( <optgroup label={label} key={label}> {options.map(({ label, ...option }, i) => ( <option key={label} {...option} value={option.value}> {label} </option> ))} </optgroup> ); } return ( <option key={label} {...option} value={option.value}> {label} </option> ); })} {children} </SelectNativeControl> <FloatingLabel>{label}</FloatingLabel> <LineRipple /> </SelectRoot> ); } } export default Select;