import * as React from 'react'; import { InputOption } from './InputOption'; import { levenshteinDistance } from '../../mmui-util'; export type OnSelected = (item: any) => void; export interface Props { reset?: boolean; header?: string; options: Array; defaults?: Array; onSelected: OnSelected; } export interface State { id: string; open: boolean; query: string; selected: Array; } /** * Dropdown Multi-select React Component * @deprecated Use MmuiDropChoiceComponent */ export class MmuiDropSelectComponent extends React.Component { dropDownBodyRef: React.RefObject; protected id; static id_counter = 0; constructor(props) { super(props); this.dropDownBodyRef = React.createRef(); this.state = { id: 'mmui_drop_select_component_' + this.getID(), selected: 'defaults' in props ? props.defaults : [], open: false, query: '', }; } protected getID(): string { if (this.id === undefined) { this.id = MmuiDropSelectComponent.id_counter; MmuiDropSelectComponent.id_counter++; } return this.id; } componentDidUpdate(prevProps) { if ('reset' in this.props && this.props.reset && !prevProps.reset) { if ('defaults' in this.props && this.props.defaults) { this.setState({ selected: this.props.defaults }); } else { if (this.state.selected.length > 0) { this.setState({ selected: [] }); } } } } onBlur = (e) => { if (this.state.open) { const mX = e.clientX; const mY = e.clientY; const rect = this.dropDownBodyRef.current.getBoundingClientRect(); const inRect = rect.left <= mX && mX <= rect.right && rect.top <= mY && mY <= rect.bottom; if (!inRect) { this.setState({ open: false }); window.removeEventListener('click', this.onBlur); } } }; onHeaderClick = () => { const isOpen = !this.state.open; this.setState({ open: isOpen }); if (isOpen) { setTimeout(() => { window.addEventListener('click', this.onBlur); }, 100); } else { window.removeEventListener('click', this.onBlur); } }; onSearchChange = (e: React.SyntheticEvent) => { this.setState({ query: (e.target as HTMLInputElement).value }); }; onItemSelect = (e: React.SyntheticEvent) => { const target: HTMLInputElement = e.target as HTMLInputElement; const selectedValue: string = target.value; const isChecked: boolean = target.checked; const n: number = this.state.selected.indexOf(selectedValue); let selectedValues: Array; if (isChecked) { if (n == -1) { selectedValues = this.state.selected.concat(selectedValue); } } else { if (n > -1) { this.state.selected.splice(n, 1); selectedValues = this.state.selected; } } this.setState({ selected: selectedValues }); if ('onSelected' in this.props) { this.props.onSelected(selectedValues.slice()); } }; isSearchable() { return 'searchable' in this.props; } render() { const isSearchable: boolean = this.isSearchable(); const selected: Array = this.state.selected; let header = 'Select'; if (selected.length == 0) { if ('header' in this.props) { header = this.props.header; } } else { header = selected[0]; if (selected.length > 1) { header += '+' + (selected.length - 1).toString(); } } let searchField = null; if (isSearchable) { // todo: make the id configurable searchField = ( ); } let optionElements = null; if ('options' in this.props) { let options: Array = this.props.options; if (isSearchable) { if (this.state.query.length > 0) { if ('fuzzy' in this.props) { const scored: Array = options.map( (option: InputOption): any => { return { w: option.label, d: levenshteinDistance( option.label, this.state.query ), }; } ); scored.sort(function (a, b) { return a['d'] - b['d']; }); options = scored.map(function (d) { return d['w']; }); } else { options = options.filter((option: InputOption) => { return option.label.indexOf(this.state.query) > -1; }); } } } optionElements = options.map((option: InputOption) => { const label: string = option.label; const value: string = option.value; const checked: boolean = this.state.selected.indexOf(value) > -1; const id: string = 'input_checkbox_' + this.getID(); return (
); }); } const id: string = 'drop_select_' + this.getID(); return (
{searchField}
{optionElements}
); } }