import * as React from 'react'; import { Fields, Field } from 'redux-form'; import { translate } from 'react-i18next'; import * as _ from 'lodash'; import Number from 'ui/common/Number'; import { Properties, PropertiesItem } from 'ui/common/Properties'; import LabelValue from 'model/LabelValue'; import AccessionFilter from 'model/accession/AccessionFilter'; import MaterialAutosuggest from 'ui/common/material-autosuggest'; import { debounce } from 'debounce'; import { AddCircle as Add, RemoveCircle as Remove, HighlightOff as Clear} from '@material-ui/icons'; interface IStringListProps extends React.ClassAttributes { input: any; notInput: any; removeByValue: (index: number, isNot: boolean) => void; } class StringList extends React.Component { public state = { renderList: [], }; private addToNotList = (item, index) => { const { input, notInput } = this.props; const {renderList} = this.state; renderList[index] = {value: item.value, state: false}; this.setState({renderList}); input.onChange(renderList.filter((renderItem) => renderItem.state).map((renderItem) => renderItem.value)); notInput.onChange(renderList.filter((renderItem) => !renderItem.state).map((renderItem) => renderItem.value)); } private removeFromNotList = (item, index) => { const { input, notInput } = this.props; const {renderList} = this.state; renderList[index] = {value: item.value, state: true}; this.setState({renderList}); input.onChange(renderList.filter((renderItem) => renderItem.state).map((renderItem) => renderItem.value)); notInput.onChange(renderList.filter((renderItem) => !renderItem.state).map((renderItem) => renderItem.value)); } public componentWillMount() { let {input: {value: doList}, notInput: {value: notList}} = this.props; doList = doList || []; notList = notList || []; const {renderList} = this.state; doList .filter((doItem) => renderList.findIndex((renderItem) => renderItem.value === doItem) === -1) .map((doItem) => renderList.push({value: doItem, state: true})); notList .filter((notItem) => renderList.findIndex((renderItem) => renderItem.value === notItem) === -1) .map((notItem) => renderList.push({value: notItem, state: false})); this.setState({renderList}); } public componentWillReceiveProps(nextProps) { let {input: {value: doList}, notInput: {value: notList}} = nextProps; doList = doList || []; notList = notList || []; const {renderList} = this.state; const newRenderList = []; renderList.map((renderItem) => { if (doList.indexOf(renderItem.value) !== -1 || notList.indexOf(renderItem.value) !== -1) { newRenderList.push(renderItem); } }); doList .filter((doItem) => newRenderList.findIndex((renderItem) => renderItem.value === doItem) === -1) .map((doItem) => newRenderList.push({value: doItem, state: true})); notList .filter((notItem) => newRenderList.findIndex((renderItem) => renderItem.value === notItem) === -1) .map((notItem) => newRenderList.push({value: notItem, state: false})); this.setState({renderList: newRenderList}); } public render() { const {removeByValue} = this.props; const {renderList} = this.state; return (
{ renderList.map((renderItem, index) => (
{ renderItem.state ? this.addToNotList(renderItem, index) }> : this.removeFromNotList(renderItem, index) }> } { renderItem.value }
removeByValue(renderItem.value, !renderItem.state) }>
)) }
); } } interface IAutocompleteFilterInternal extends React.ClassAttributes { field: string; filterCode: string; input: any; placeholder?: string; label?: string; options?: { [key: string]: any; }; t: any; addToNotList: (item: any) => void; notList: any[]; autocomplete: (field: string, term: string, filter: string | AccessionFilter) => Promise>>; } class AutocompleteFilterInternal extends React.Component { private constructor(props, context) { super(props, context); const notValues = _.get(props, `${props.names[0]}.input.value`); const values = _.get(props, `${props.names[1]}.input.value`); this.state = { excludedValues: [], values, notValues, text: '', autocompleteObj: [], }; } public componentWillMount() { const input = _.get(this.props, `${this.props.names[0]}.input`); const value = typeof input.value[0] === 'number' ? input.value.map((key) => `${key}`) : input.value; const notValue = _.get(this.props, `${this.props.names[1]}.input.value`); this.setState({ excludedValues: [], values: [ ...value ], notValues: [ ...notValue ], text: '', }); } public componentWillReceiveProps(nextProps) { const input = _.get(nextProps, `${nextProps.names[0]}.input`); const value = typeof input.value[0] === 'number' ? input.value.map((key) => `${key}`) : input.value; const notValue = _.get(nextProps, `${nextProps.names[1]}.input.value`); this.setState({ excludedValues: [...value, ...notValue], values: [ ...value ], notValues: [...notValue], text: '', }); } private maybeAdd = (...newValues: string[]) => { const values = [ ...this.state.values ]; const excludedValues = [ ]; if (this.state.excludedValues) { excludedValues.push(...this.state.excludedValues); } newValues.forEach((text) => { if (text && text.length > 0) { if (values.indexOf(text) < 0) { values.push(text); } if (excludedValues.indexOf(text) < 0) { excludedValues.push(text); } } }); if (!_.isEqual(values, this.state.values)) { this.setState({ text: '', values, excludedValues, }); } return values; } private maybeRemove = (isNot?: boolean, ...newValues: string[]) => { const values = isNot ? [ ...this.state.notValues ] : [ ...this.state.values ]; const excludedValues = this.state.excludedValues; newValues.forEach((text) => { if (text && text.length > 0) { const index: number = values.indexOf(text); if (index >= 0) { values.splice(index, 1); } const indexExcluded: number = excludedValues.indexOf(text); if (indexExcluded >= 0) { excludedValues.splice(indexExcluded, 1); } } }); if (!_.isEqual(values, isNot ? this.state.notValues : this.state.values)) { this.setState({ text: '', values: isNot ? this.state.values : values, notValues: !isNot ? this.state.notValues : values, excludedValues, }); } return values; } private removeByValue = (value, isNot) => { const { input } = _.get(this.props, this.props.names[isNot ? 1 : 0]); const newValues = this.maybeRemove(isNot, value); input.onChange(newValues); } private onInputChange = (e) => { const { autocomplete, field, filterCode } = this.props; if (e.target && typeof e.target.value === 'string') { if (e.target && e.target.value) { if (e.target.value !== this.state.text) { if (e.target.value.length >= 3) { this.setState({text: e.target.value}); autocomplete(field, e.target.value, filterCode) .then((autocompleteObj) => { this.setState({...this.state, autocompleteObj}); }); } } } } } private onSuggestionSelected = (e, data, autocompleteInput, that) => { autocompleteInput.onChange.call(that, ''); const { input } = _.get(this.props, this.props.names[0]); input.onChange(this.maybeAdd(data.suggestion.value)); } public render() { const { placeholder, label, t, names, terms, classes } = this.props; const { input } = _.get(this.props, names[0]); const { input: notInput } = _.get(this.props, names[1]); const { values, notValues, excludedValues } = this.state; return (
{ ((values && values.length > 0) || (notValues && notValues.length > 0)) && } { terms &&
{ t('common:f.suggestedFilters') }
{ terms && Array.from(terms).slice(0, 10).filter(([key, value]) => excludedValues.length === 0 || !excludedValues.includes(key)).map(([key, value]) => ( input.onChange(this.maybeAdd(key)) } classes={ {...classes, propertiesRow: 'cursor-pointer'} }> )) }
}
); } } interface IAutocompleteFilter extends React.ClassAttributes { name: string; placeholder?: string; label?: string; options?: { [key: string]: any; }; terms?: { [key: string]: any; }; byKey?: boolean; autocomplete: (field: string, term: string, filter: string | AccessionFilter) => Promise>>; filterCode: string; classes: any; t: any; } class AutocompleteFilter extends React.Component { public render() { const { name, label, placeholder, options, terms, byKey, autocomplete, filterCode, classes, t } = this.props; return (
); } } export default translate()(AutocompleteFilter);