|
|
|
|
@ -25,10 +25,9 @@ THE SOFTWARE.
|
|
|
|
|
|
|
|
|
|
/* eslint-disable jsdoc/require-jsdoc */
|
|
|
|
|
// @ts-nocheck
|
|
|
|
|
import clsx from 'clsx';
|
|
|
|
|
import React, { useRef, useEffect } from 'react';
|
|
|
|
|
|
|
|
|
|
// import './styles.css';
|
|
|
|
|
|
|
|
|
|
import Icon from './icon';
|
|
|
|
|
|
|
|
|
|
function useOutsideAlerter(ref, clickEvent) {
|
|
|
|
|
@ -56,38 +55,20 @@ const OutsideAlerter = (props) => {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
interface IMultiselectProps {
|
|
|
|
|
options: any;
|
|
|
|
|
disablePreSelectedValues?: boolean;
|
|
|
|
|
selectedValues?: any;
|
|
|
|
|
isObject?: boolean;
|
|
|
|
|
displayValue?: string;
|
|
|
|
|
showCheckbox?: boolean;
|
|
|
|
|
selectionLimit?: any;
|
|
|
|
|
placeholder?: string;
|
|
|
|
|
groupBy?: string;
|
|
|
|
|
loading?: boolean;
|
|
|
|
|
style?: object;
|
|
|
|
|
emptyRecordMsg?: string;
|
|
|
|
|
onSelect?: (selectedList:any, selectedItem: any) => void;
|
|
|
|
|
onRemove?: (selectedList:any, selectedItem: any) => void;
|
|
|
|
|
onSearch?: (value:string) => void;
|
|
|
|
|
onKeyPressFn?: (event:any, value:string) => void;
|
|
|
|
|
closeIcon?: string;
|
|
|
|
|
singleSelect?: boolean;
|
|
|
|
|
caseSensitiveSearch?: boolean;
|
|
|
|
|
id?: string;
|
|
|
|
|
closeOnSelect?: boolean;
|
|
|
|
|
avoidHighlightFirstOption?: boolean;
|
|
|
|
|
hidePlaceholder?: boolean;
|
|
|
|
|
showArrow?: boolean;
|
|
|
|
|
keepSearchTerm?: boolean;
|
|
|
|
|
customCloseIcon?: React.ReactNode | string;
|
|
|
|
|
customArrow?: any;
|
|
|
|
|
disable?: boolean;
|
|
|
|
|
className?: string;
|
|
|
|
|
selectedValueDecorator?: (v:string, option: any) => React.ReactNode | string;
|
|
|
|
|
optionValueDecorator?: (v:string, option: any) => React.ReactNode | string;
|
|
|
|
|
hideSelectedList?: boolean;
|
|
|
|
|
options: any;
|
|
|
|
|
selectedValues?: any;
|
|
|
|
|
displayValue?: string;
|
|
|
|
|
placeholder?: string;
|
|
|
|
|
loading?: boolean;
|
|
|
|
|
emptyRecordMsg?: string;
|
|
|
|
|
onSelect?: (selectedList:any, selectedItem: any) => void;
|
|
|
|
|
onRemove?: (selectedList:any, selectedItem: any) => void;
|
|
|
|
|
onSearch?: (value:string) => void;
|
|
|
|
|
onKeyPressFn?: (event:any, value:string) => void;
|
|
|
|
|
id?: string;
|
|
|
|
|
name?: string;
|
|
|
|
|
disabled?: boolean;
|
|
|
|
|
className?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class Multiselect extends React.Component<IMultiselectProps, any> {
|
|
|
|
|
@ -102,12 +83,8 @@ export class Multiselect extends React.Component<IMultiselectProps, any> {
|
|
|
|
|
filteredOptions: props.options,
|
|
|
|
|
unfilteredOptions: props.options,
|
|
|
|
|
selectedValues: Object.assign([], props.selectedValues),
|
|
|
|
|
preSelectedValues: Object.assign([], props.selectedValues),
|
|
|
|
|
toggleOptionsList: false,
|
|
|
|
|
highlightOption: props.avoidHighlightFirstOption ? -1 : 0,
|
|
|
|
|
showCheckbox: props.showCheckbox,
|
|
|
|
|
keepSearchTerm: props.keepSearchTerm,
|
|
|
|
|
groupedObject: [],
|
|
|
|
|
highlightOption: 0,
|
|
|
|
|
};
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
this.optionTimeout = null;
|
|
|
|
|
@ -122,61 +99,19 @@ export class Multiselect extends React.Component<IMultiselectProps, any> {
|
|
|
|
|
this.renderMultiselectContainer = this.renderMultiselectContainer.bind(this);
|
|
|
|
|
this.renderSelectedList = this.renderSelectedList.bind(this);
|
|
|
|
|
this.onRemoveSelectedItem = this.onRemoveSelectedItem.bind(this);
|
|
|
|
|
this.toggelOptionList = this.toggelOptionList.bind(this);
|
|
|
|
|
this.toggleOptionList = this.toggleOptionList.bind(this);
|
|
|
|
|
this.onArrowKeyNavigation = this.onArrowKeyNavigation.bind(this);
|
|
|
|
|
this.onSelectItem = this.onSelectItem.bind(this);
|
|
|
|
|
this.filterOptionsByInput = this.filterOptionsByInput.bind(this);
|
|
|
|
|
this.removeSelectedValuesFromOptions = this.removeSelectedValuesFromOptions.bind(this);
|
|
|
|
|
this.isSelectedValue = this.isSelectedValue.bind(this);
|
|
|
|
|
this.fadeOutSelection = this.fadeOutSelection.bind(this);
|
|
|
|
|
this.isDisablePreSelectedValues = this.isDisablePreSelectedValues.bind(this);
|
|
|
|
|
this.renderGroupByOptions = this.renderGroupByOptions.bind(this);
|
|
|
|
|
this.renderNormalOption = this.renderNormalOption.bind(this);
|
|
|
|
|
this.renderOption = this.renderOption.bind(this);
|
|
|
|
|
this.listenerCallback = this.listenerCallback.bind(this);
|
|
|
|
|
this.resetSelectedValues = this.resetSelectedValues.bind(this);
|
|
|
|
|
this.getSelectedItems = this.getSelectedItems.bind(this);
|
|
|
|
|
this.getSelectedItemsCount = this.getSelectedItemsCount.bind(this);
|
|
|
|
|
this.hideOnClickOutside = this.hideOnClickOutside.bind(this);
|
|
|
|
|
this.onCloseOptionList = this.onCloseOptionList.bind(this);
|
|
|
|
|
this.isVisible = this.isVisible.bind(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
initialSetValue() {
|
|
|
|
|
const { showCheckbox, groupBy, singleSelect } = this.props;
|
|
|
|
|
const { options } = this.state;
|
|
|
|
|
if (!showCheckbox && !singleSelect) {
|
|
|
|
|
this.removeSelectedValuesFromOptions(false);
|
|
|
|
|
}
|
|
|
|
|
// if (singleSelect) {
|
|
|
|
|
// this.hideOnClickOutside();
|
|
|
|
|
// }
|
|
|
|
|
if (groupBy) {
|
|
|
|
|
this.groupByOptions(options);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
resetSelectedValues() {
|
|
|
|
|
const { unfilteredOptions } = this.state;
|
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
|
this.setState({
|
|
|
|
|
selectedValues: [],
|
|
|
|
|
preSelectedValues: [],
|
|
|
|
|
options: unfilteredOptions,
|
|
|
|
|
filteredOptions: unfilteredOptions,
|
|
|
|
|
}, () => {
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
resolve();
|
|
|
|
|
this.initialSetValue();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getSelectedItems() {
|
|
|
|
|
return this.state.selectedValues;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getSelectedItemsCount() {
|
|
|
|
|
return this.state.selectedValues.length;
|
|
|
|
|
this.removeSelectedValuesFromOptions(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
|
|
|
@ -192,7 +127,7 @@ export class Multiselect extends React.Component<IMultiselectProps, any> {
|
|
|
|
|
this.setState({ options, filteredOptions: options, unfilteredOptions: options }, this.initialSetValue);
|
|
|
|
|
}
|
|
|
|
|
if (JSON.stringify(prevSelectedvalues) !== JSON.stringify(selectedValues)) {
|
|
|
|
|
this.setState({ selectedValues: Object.assign([], selectedValues), preSelectedValues: Object.assign([], selectedValues) }, this.initialSetValue);
|
|
|
|
|
this.setState({ selectedValues: Object.assign([], selectedValues) }, this.initialSetValue);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -213,51 +148,23 @@ export class Multiselect extends React.Component<IMultiselectProps, any> {
|
|
|
|
|
|
|
|
|
|
// Skipcheck flag - value will be true when the func called from on deselect anything.
|
|
|
|
|
removeSelectedValuesFromOptions(skipCheck) {
|
|
|
|
|
const { isObject, displayValue, groupBy } = this.props;
|
|
|
|
|
const { selectedValues = [], unfilteredOptions, options } = this.state;
|
|
|
|
|
if (!skipCheck && groupBy) {
|
|
|
|
|
this.groupByOptions(options);
|
|
|
|
|
}
|
|
|
|
|
const { displayValue } = this.props;
|
|
|
|
|
const { selectedValues = [], unfilteredOptions } = this.state;
|
|
|
|
|
if (!selectedValues.length && !skipCheck) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (isObject) {
|
|
|
|
|
const optionList = unfilteredOptions.filter(item => {
|
|
|
|
|
return selectedValues.findIndex(
|
|
|
|
|
v => v[displayValue] === item[displayValue],
|
|
|
|
|
) === -1
|
|
|
|
|
? true
|
|
|
|
|
: false;
|
|
|
|
|
});
|
|
|
|
|
if (groupBy) {
|
|
|
|
|
this.groupByOptions(optionList);
|
|
|
|
|
}
|
|
|
|
|
this.setState(
|
|
|
|
|
{ options: optionList, filteredOptions: optionList },
|
|
|
|
|
this.filterOptionsByInput,
|
|
|
|
|
);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const optionList = unfilteredOptions.filter(
|
|
|
|
|
item => selectedValues.indexOf(item) === -1,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const optionList = unfilteredOptions.filter(item => {
|
|
|
|
|
return selectedValues.findIndex(
|
|
|
|
|
v => v[displayValue] === item[displayValue],
|
|
|
|
|
) === -1
|
|
|
|
|
? true
|
|
|
|
|
: false;
|
|
|
|
|
});
|
|
|
|
|
this.setState(
|
|
|
|
|
{ options: optionList, filteredOptions: optionList },
|
|
|
|
|
this.filterOptionsByInput,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
groupByOptions(options) {
|
|
|
|
|
const { groupBy } = this.props;
|
|
|
|
|
const groupedObject = options.reduce(function(r, a) {
|
|
|
|
|
const key = a[groupBy] || 'Others';
|
|
|
|
|
r[key] = r[key] || [];
|
|
|
|
|
r[key].push(a);
|
|
|
|
|
return r;
|
|
|
|
|
}, Object.create({}));
|
|
|
|
|
|
|
|
|
|
this.setState({ groupedObject });
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onChange(event) {
|
|
|
|
|
@ -281,20 +188,12 @@ export class Multiselect extends React.Component<IMultiselectProps, any> {
|
|
|
|
|
filterOptionsByInput() {
|
|
|
|
|
let { options } = this.state;
|
|
|
|
|
const { filteredOptions, inputValue } = this.state;
|
|
|
|
|
const { isObject, displayValue } = this.props;
|
|
|
|
|
if (isObject) {
|
|
|
|
|
options = filteredOptions.filter(i => this.matchValues(i[displayValue], inputValue));
|
|
|
|
|
} else {
|
|
|
|
|
options = filteredOptions.filter(i => this.matchValues(i, inputValue));
|
|
|
|
|
}
|
|
|
|
|
this.groupByOptions(options);
|
|
|
|
|
const { displayValue } = this.props;
|
|
|
|
|
options = filteredOptions.filter(i => this.matchValues(i[displayValue], inputValue));
|
|
|
|
|
this.setState({ options });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
matchValues(value, search) {
|
|
|
|
|
if (this.props.caseSensitiveSearch) {
|
|
|
|
|
return value.indexOf(search) > -1;
|
|
|
|
|
}
|
|
|
|
|
if (value.toLowerCase) {
|
|
|
|
|
return value.toLowerCase().indexOf(search.toLowerCase()) > -1;
|
|
|
|
|
}
|
|
|
|
|
@ -309,8 +208,7 @@ export class Multiselect extends React.Component<IMultiselectProps, any> {
|
|
|
|
|
inputValue,
|
|
|
|
|
selectedValues,
|
|
|
|
|
} = this.state;
|
|
|
|
|
const { disablePreSelectedValues } = this.props;
|
|
|
|
|
if (e.keyCode === 8 && !inputValue && !disablePreSelectedValues && selectedValues.length) {
|
|
|
|
|
if (e.keyCode === 8 && !inputValue && selectedValues.length) {
|
|
|
|
|
this.onRemoveSelectedItem(selectedValues.length - 1);
|
|
|
|
|
}
|
|
|
|
|
if (!options.length) {
|
|
|
|
|
@ -350,220 +248,103 @@ export class Multiselect extends React.Component<IMultiselectProps, any> {
|
|
|
|
|
onRemoveSelectedItem(item) {
|
|
|
|
|
const { selectedValues } = this.state;
|
|
|
|
|
let { index = 0 } = this.state;
|
|
|
|
|
const { onRemove, showCheckbox, displayValue, isObject } = this.props;
|
|
|
|
|
if (isObject) {
|
|
|
|
|
index = selectedValues.findIndex(
|
|
|
|
|
i => i[displayValue] === item[displayValue],
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
index = selectedValues.indexOf(item);
|
|
|
|
|
}
|
|
|
|
|
const { onRemove, displayValue } = this.props;
|
|
|
|
|
index = selectedValues.findIndex(
|
|
|
|
|
i => i[displayValue] === item[displayValue],
|
|
|
|
|
);
|
|
|
|
|
selectedValues.splice(index, 1);
|
|
|
|
|
onRemove(selectedValues, item);
|
|
|
|
|
this.setState({ selectedValues }, () => {
|
|
|
|
|
if (!showCheckbox) {
|
|
|
|
|
this.removeSelectedValuesFromOptions(true);
|
|
|
|
|
}
|
|
|
|
|
this.removeSelectedValuesFromOptions(true);
|
|
|
|
|
});
|
|
|
|
|
if (!this.props.closeOnSelect) {
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
this.searchBox.current.focus();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onSelectItem(item) {
|
|
|
|
|
const { selectedValues } = this.state;
|
|
|
|
|
const { selectionLimit, onSelect, singleSelect, showCheckbox } = this.props;
|
|
|
|
|
if (!this.state.keepSearchTerm){
|
|
|
|
|
this.setState({
|
|
|
|
|
inputValue: '',
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
if (singleSelect) {
|
|
|
|
|
this.onSingleSelect(item);
|
|
|
|
|
onSelect([item], item);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const { onSelect } = this.props;
|
|
|
|
|
this.setState({
|
|
|
|
|
inputValue: '',
|
|
|
|
|
});
|
|
|
|
|
if (this.isSelectedValue(item)) {
|
|
|
|
|
this.onRemoveSelectedItem(item);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (selectionLimit === selectedValues.length) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
selectedValues.push(item);
|
|
|
|
|
onSelect(selectedValues, item);
|
|
|
|
|
this.setState({ selectedValues }, () => {
|
|
|
|
|
if (!showCheckbox) {
|
|
|
|
|
this.removeSelectedValuesFromOptions(true);
|
|
|
|
|
} else {
|
|
|
|
|
this.filterOptionsByInput();
|
|
|
|
|
}
|
|
|
|
|
this.removeSelectedValuesFromOptions(true);
|
|
|
|
|
});
|
|
|
|
|
if (!this.props.closeOnSelect) {
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
this.searchBox.current.focus();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onSingleSelect(item) {
|
|
|
|
|
this.setState({ selectedValues: [item], toggleOptionsList: false });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
isSelectedValue(item) {
|
|
|
|
|
const { isObject, displayValue } = this.props;
|
|
|
|
|
const { displayValue } = this.props;
|
|
|
|
|
const { selectedValues } = this.state;
|
|
|
|
|
if (isObject) {
|
|
|
|
|
return (
|
|
|
|
|
selectedValues.filter(i => i[displayValue] === item[displayValue])
|
|
|
|
|
.length > 0
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return selectedValues.filter(i => i === item).length > 0;
|
|
|
|
|
return (
|
|
|
|
|
selectedValues.filter(i => i[displayValue] === item[displayValue])
|
|
|
|
|
.length > 0
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
renderOptionList() {
|
|
|
|
|
const { groupBy, style, emptyRecordMsg, loading, loadingMessage = 'loading...' } = this.props;
|
|
|
|
|
const { emptyRecordMsg, loading, loadingMessage = 'loading...' } = this.props;
|
|
|
|
|
const { options } = this.state;
|
|
|
|
|
if (loading) {
|
|
|
|
|
return (
|
|
|
|
|
<ul className={'optionContainer'} style={style.optionContainer}>
|
|
|
|
|
{typeof loadingMessage === 'string' && <span style={style.loadingMessage} className={'notFound'}>{loadingMessage}</span>}
|
|
|
|
|
<ul className='optionContainer'>
|
|
|
|
|
{typeof loadingMessage === 'string' && <span className='notFound'>{loadingMessage}</span>}
|
|
|
|
|
{typeof loadingMessage !== 'string' && loadingMessage}
|
|
|
|
|
</ul>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return (
|
|
|
|
|
<ul className={'optionContainer'} style={style.optionContainer}>
|
|
|
|
|
{options.length === 0 && <span style={style.notFound} className={'notFound'}>{emptyRecordMsg}</span>}
|
|
|
|
|
{!groupBy ? this.renderNormalOption() : this.renderGroupByOptions()}
|
|
|
|
|
<ul className='optionContainer'>
|
|
|
|
|
{options.length === 0 && <span className='notFound'>{emptyRecordMsg}</span>}
|
|
|
|
|
{this.renderOption()}
|
|
|
|
|
</ul>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
renderGroupByOptions() {
|
|
|
|
|
const { isObject = false, displayValue, showCheckbox, style, singleSelect } = this.props;
|
|
|
|
|
const { groupedObject } = this.state;
|
|
|
|
|
return Object.keys(groupedObject).map(obj => {
|
|
|
|
|
return (
|
|
|
|
|
<React.Fragment key={obj}>
|
|
|
|
|
<li className='groupHeading' style={style.groupHeading}>{obj}</li>
|
|
|
|
|
{groupedObject[obj].map((option, i) => {
|
|
|
|
|
const isSelected = this.isSelectedValue(option);
|
|
|
|
|
return (
|
|
|
|
|
<li
|
|
|
|
|
key={`option${i}`}
|
|
|
|
|
style={style.option}
|
|
|
|
|
className={`groupChildEle option ${isSelected ? 'selected' : ''} ${this.fadeOutSelection(option) ? 'disableSelection' : ''} ${this.isDisablePreSelectedValues(option) ? 'disableSelection' : ''}`}
|
|
|
|
|
onClick={() => this.onSelectItem(option)}
|
|
|
|
|
>
|
|
|
|
|
{showCheckbox && !singleSelect && (
|
|
|
|
|
<input
|
|
|
|
|
type='checkbox'
|
|
|
|
|
className={'checkbox'}
|
|
|
|
|
readOnly
|
|
|
|
|
checked={isSelected}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
{this.props.optionValueDecorator(isObject ? option[displayValue] : (option || '').toString(), option)}
|
|
|
|
|
</li>
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
)}
|
|
|
|
|
</React.Fragment>
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
renderNormalOption() {
|
|
|
|
|
const { isObject = false, displayValue, showCheckbox, style, singleSelect } = this.props;
|
|
|
|
|
renderOption() {
|
|
|
|
|
const { displayValue } = this.props;
|
|
|
|
|
const { highlightOption } = this.state;
|
|
|
|
|
return this.state.options.map((option, i) => {
|
|
|
|
|
const isSelected = this.isSelectedValue(option);
|
|
|
|
|
return (
|
|
|
|
|
<li
|
|
|
|
|
key={`option${i}`}
|
|
|
|
|
style={style.option}
|
|
|
|
|
className={`option ${isSelected ? 'selected' : ''} ${highlightOption === i ? 'highlightOption highlight' : ''} ${this.fadeOutSelection(option) ? 'disableSelection' : ''} ${this.isDisablePreSelectedValues(option) ? 'disableSelection' : ''}`}
|
|
|
|
|
className={`option ${isSelected ? 'selected' : ''} ${highlightOption === i ? 'highlightOption highlight' : ''}`}
|
|
|
|
|
onClick={() => this.onSelectItem(option)}
|
|
|
|
|
>
|
|
|
|
|
{showCheckbox && !singleSelect && (
|
|
|
|
|
<input
|
|
|
|
|
type='checkbox'
|
|
|
|
|
readOnly
|
|
|
|
|
className={'checkbox'}
|
|
|
|
|
checked={isSelected}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
{this.props.optionValueDecorator(isObject ? option[displayValue] : (option || '').toString(), option)}
|
|
|
|
|
{option[displayValue]}
|
|
|
|
|
</li>
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
renderSelectedList() {
|
|
|
|
|
const { isObject = false, displayValue, style, singleSelect } = this.props;
|
|
|
|
|
const { displayValue } = this.props;
|
|
|
|
|
const { selectedValues } = this.state;
|
|
|
|
|
return selectedValues.map((value, index) => (
|
|
|
|
|
<span className={`chip ${singleSelect && 'singleChip'} ${this.isDisablePreSelectedValues(value) && 'disableSelection'}`} key={index} style={style.chips}>
|
|
|
|
|
{this.props.selectedValueDecorator(!isObject ? (value || '').toString() : value[displayValue], value)}
|
|
|
|
|
{!this.isDisablePreSelectedValues(value) && (<i className='custom-close' onClick={() => this.onRemoveSelectedItem(value)}>
|
|
|
|
|
<span className='chip' key={index}>
|
|
|
|
|
{value[displayValue]}
|
|
|
|
|
<button onClick={() => this.onRemoveSelectedItem(value)}>
|
|
|
|
|
<Icon className='ml-1 size-4 hover:cursor-pointer' src={require('@phosphor-icons/core/regular/x-circle.svg')} />
|
|
|
|
|
</i>)}
|
|
|
|
|
</button>
|
|
|
|
|
</span>
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
isDisablePreSelectedValues(value) {
|
|
|
|
|
const { isObject, disablePreSelectedValues, displayValue } = this.props;
|
|
|
|
|
const { preSelectedValues } = this.state;
|
|
|
|
|
if (!disablePreSelectedValues || !preSelectedValues.length) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (isObject) {
|
|
|
|
|
return (
|
|
|
|
|
preSelectedValues.filter(i => i[displayValue] === value[displayValue])
|
|
|
|
|
.length > 0
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return preSelectedValues.filter(i => i === value).length > 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fadeOutSelection(item) {
|
|
|
|
|
const { selectionLimit, showCheckbox, singleSelect } = this.props;
|
|
|
|
|
if (singleSelect) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const { selectedValues } = this.state;
|
|
|
|
|
if (selectionLimit === -1) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (selectionLimit !== selectedValues.length) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (selectionLimit === selectedValues.length) {
|
|
|
|
|
if (!showCheckbox) {
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
if (this.isSelectedValue(item)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
toggelOptionList() {
|
|
|
|
|
toggleOptionList() {
|
|
|
|
|
this.setState({
|
|
|
|
|
toggleOptionsList: !this.state.toggleOptionsList,
|
|
|
|
|
highlightOption: this.props.avoidHighlightFirstOption ? -1 : 0,
|
|
|
|
|
highlightOption: 0,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onCloseOptionList() {
|
|
|
|
|
this.setState({
|
|
|
|
|
toggleOptionsList: false,
|
|
|
|
|
highlightOption: this.props.avoidHighlightFirstOption ? -1 : 0,
|
|
|
|
|
highlightOption: 0,
|
|
|
|
|
inputValue: '',
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
@ -573,7 +354,7 @@ export class Multiselect extends React.Component<IMultiselectProps, any> {
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
clearTimeout(this.optionTimeout);
|
|
|
|
|
} else {
|
|
|
|
|
this.toggelOptionList();
|
|
|
|
|
this.toggleOptionList();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -583,53 +364,31 @@ export class Multiselect extends React.Component<IMultiselectProps, any> {
|
|
|
|
|
this.optionTimeout = setTimeout(this.onCloseOptionList, 250);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
isVisible(elem) {
|
|
|
|
|
return !!elem && !!(elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hideOnClickOutside() {
|
|
|
|
|
const element = document.getElementsByClassName('multiselect-container')[0];
|
|
|
|
|
const outsideClickListener = event => {
|
|
|
|
|
if (element && !element.contains(event.target) && this.isVisible(element)) {
|
|
|
|
|
this.toggelOptionList();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
document.addEventListener('click', outsideClickListener);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
renderMultiselectContainer() {
|
|
|
|
|
const { inputValue, toggleOptionsList, selectedValues } = this.state;
|
|
|
|
|
const { placeholder, style, singleSelect, id, name, hidePlaceholder, disable, showArrow, className, customArrow, hideSelectedList } = this.props;
|
|
|
|
|
const { inputValue, toggleOptionsList } = this.state;
|
|
|
|
|
const { placeholder, id, name, disabled, className } = this.props;
|
|
|
|
|
return (
|
|
|
|
|
<div className={`multiselect-container multiSelectContainer ${disable ? 'disable_ms' : ''} ${className || ''}`} id={id || 'multiselectContainerReact'} style={style.multiselectContainer}>
|
|
|
|
|
<div className={clsx('multiselect-container', { 'multiselect-container--disabled': disabled }, className)} id={id}>
|
|
|
|
|
<div
|
|
|
|
|
className={`search-wrapper searchWrapper ${singleSelect ? 'singleSelect' : ''}`}
|
|
|
|
|
ref={this.searchWrapper} style={style.searchBox}
|
|
|
|
|
onClick={singleSelect ? this.toggelOptionList : () => {}}
|
|
|
|
|
className='searchWrapper'
|
|
|
|
|
ref={this.searchWrapper}
|
|
|
|
|
>
|
|
|
|
|
{!hideSelectedList && this.renderSelectedList()}
|
|
|
|
|
{this.renderSelectedList()}
|
|
|
|
|
<input
|
|
|
|
|
type='text'
|
|
|
|
|
ref={this.searchBox}
|
|
|
|
|
className={`searchBox ${singleSelect && selectedValues.length ? 'display-none' : ''}`}
|
|
|
|
|
id={`${id || 'search'}_input`}
|
|
|
|
|
name={`${name || 'search_name'}_input`}
|
|
|
|
|
className='searchBox'
|
|
|
|
|
name={`${name || 'search-name'}-input`}
|
|
|
|
|
onChange={this.onChange}
|
|
|
|
|
onKeyPress={this.onKeyPress}
|
|
|
|
|
value={inputValue}
|
|
|
|
|
onFocus={this.onFocus}
|
|
|
|
|
onBlur={this.onBlur}
|
|
|
|
|
placeholder={((singleSelect && selectedValues.length) || (hidePlaceholder && selectedValues.length)) ? '' : placeholder}
|
|
|
|
|
placeholder={placeholder}
|
|
|
|
|
onKeyDown={this.onArrowKeyNavigation}
|
|
|
|
|
style={style.inputField}
|
|
|
|
|
autoComplete='off'
|
|
|
|
|
disabled={singleSelect || disable}
|
|
|
|
|
disabled={disabled}
|
|
|
|
|
/>
|
|
|
|
|
{(singleSelect || showArrow) && (
|
|
|
|
|
<>
|
|
|
|
|
{customArrow ? <span className='icon_down_dir'>{customArrow}</span> : <img src='' className={'icon_cancel icon_down_dir'} />}
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
<div
|
|
|
|
|
className={`optionListContainer ${
|
|
|
|
|
@ -657,32 +416,15 @@ export class Multiselect extends React.Component<IMultiselectProps, any> {
|
|
|
|
|
|
|
|
|
|
Multiselect.defaultProps = {
|
|
|
|
|
options: [],
|
|
|
|
|
disablePreSelectedValues: false,
|
|
|
|
|
selectedValues: [],
|
|
|
|
|
isObject: true,
|
|
|
|
|
displayValue: 'model',
|
|
|
|
|
showCheckbox: false,
|
|
|
|
|
selectionLimit: -1,
|
|
|
|
|
placeholder: 'Select',
|
|
|
|
|
groupBy: '',
|
|
|
|
|
style: {},
|
|
|
|
|
emptyRecordMsg: 'No Options Available',
|
|
|
|
|
onSelect: () => {},
|
|
|
|
|
onRemove: () => {},
|
|
|
|
|
onKeyPressFn: () => {},
|
|
|
|
|
closeIcon: 'circle2',
|
|
|
|
|
singleSelect: false,
|
|
|
|
|
caseSensitiveSearch: false,
|
|
|
|
|
id: '',
|
|
|
|
|
name: '',
|
|
|
|
|
closeOnSelect: true,
|
|
|
|
|
avoidHighlightFirstOption: false,
|
|
|
|
|
hidePlaceholder: false,
|
|
|
|
|
showArrow: false,
|
|
|
|
|
keepSearchTerm: false,
|
|
|
|
|
customCloseIcon: '',
|
|
|
|
|
disabled: false,
|
|
|
|
|
className: '',
|
|
|
|
|
customArrow: undefined,
|
|
|
|
|
selectedValueDecorator: v => v,
|
|
|
|
|
optionValueDecorator: v => v,
|
|
|
|
|
} as IMultiselectProps;
|
|
|
|
|
|