import * as React from "react"; import * as cx from "classnames"; import { DropdownMenuItem } from "./menu.pc"; import { EMPTY_ARRAY, memoize } from "tandem-common"; import { BaseDropdownProps, ElementProps } from "./view.pc"; import { PCVariable } from "paperclip"; export type DropdownMenuOption = { label: string; value: any; special?: boolean; }; export const NO_OPTION: DropdownMenuOption = { label: "--", value: undefined }; export const dropdownMenuOptionFromValue = ( value: string ): DropdownMenuOption => ({ label: value || "--", value }); export const mapVariablesToDropdownOptions = memoize( (variables: PCVariable[]): DropdownMenuOption[] => { return variables.map(variable => ({ value: variable, label: variable.label, special: true })); } ); export type Props = { value?: any; filterable?: boolean; options: DropdownMenuOption[]; onChange?: (value: any) => any; onChangeComplete?: (value: any) => any; } & ElementProps; type DropdownState = { open: boolean; filter: string; }; export default (Base: React.ComponentClass) => { return class DropdownController extends React.PureComponent< Props, DropdownState > { constructor(props) { super(props); this.state = { open: false, filter: null }; } onMouseDown = event => { // only open if _not_ opened yet. The popover will call onShouldClose if already open // since the button is technically out of the popover scope. if (!this.state.open) { this.setState({ ...this.state, open: true }); } if (this.props.onMouseDown) { this.props.onMouseDown(event); } }; onFilterChange = value => { this.setState({ ...this.state, filter: value ? String(value).toLowerCase() : null }); }; componentWillUpdate(props) { if (this.props.value !== props.value && this.state.filter) { this.setState({ ...this.state, filter: null, open: false }); } } onItemClick = (item, event) => { const { onChange, onChangeComplete } = this.props; this.setState({ ...this.state, open: false }); if (onChange) { onChange(item.value); } if (onChangeComplete) { onChangeComplete(item.value); } }; onKeyDown = event => { if (event.key === "Enter") { this.setState({ ...this.state, open: true }); } }; onShouldClose = () => { this.setState({ ...this.state, open: false }); }; render() { const { value, options = EMPTY_ARRAY, filterable, onMouseDown, onChange, onChangeComplete, ...rest } = this.props; const { open, filter } = this.state; const menuItems = open ? options .filter( ({ label }) => !filter || String(label) .toLowerCase() .indexOf(filter) !== -1 ) .map((item, i) => { return ( this.onItemClick(item, event)} > {item.label} ); }) : EMPTY_ARRAY; const selectedItem: DropdownMenuOption = options.find( item => item.value === value ); const showFilter = open && filterable !== false; return ( ); } }; };