@zohodesk/components
Version:
Dot UI is a customizable React component library built to deliver a clean, accessible, and developer-friendly UI experience. It offers a growing set of reusable components designed to align with modern design systems and streamline application development
1,172 lines (1,076 loc) • 39.4 kB
JavaScript
/**** Libraries ****/
import React from 'react';
import { MultiSelect_propTypes } from "./props/propTypes";
import { MultiSelect_defaultProps } from "./props/defaultProps";
import { defaultProps as MobileHeader_defaultProps } from "./MobileHeader/props/defaultProps";
import { MULTISELECT_I18N_KEYS } from "./constants";
/**** Components ****/
import Popup from "../Popup/Popup";
import TextBoxIcon from "../TextBoxIcon/TextBoxIcon";
import { Container, Box } from "../Layout";
import Card, { CardContent, CardFooter, CardHeader } from "../Card/Card";
import SelectedOptions from "./SelectedOptions";
import Suggestions from "./Suggestions";
import EmptyState from "./EmptyState";
import CssProvider from "../Provider/CssProvider";
import { getUniqueId } from "../Provider/IdProvider";
import ResponsiveDropBox from "../ResponsiveDropBox/ResponsiveDropBox"; // import { ResponsiveReceiver } from '../Responsive/CustomResponsive';
import MultiSelectHeader from "./MultiSelectHeader";
import Loader from '@zohodesk/svg/lib/Loader/Loader';
import isMobilePopover from "../DropBox/utils/isMobilePopover";
/**** Icons ****/
import { Icon } from '@zohodesk/icons';
/**** CSS ****/
import style from "./MultiSelect.module.css";
/**** Methods ****/
import { debounce, scrollTo, bind, getIsEmptyValue, getSearchString, findScrollEnd } from "../utils/Common.js";
import { makeFormatOptions, makeGetMultiSelectFilterSuggestions as makeGetFilterSuggestions, makeGetMultiSelectSelectedOptions as makeGetSelectedOptions, makeObjectConcat, filterSelectedOptions, makeGetIsShowClearIcon } from "../utils/dropDownUtils";
import MobileHeader from "./MobileHeader/MobileHeader";
/* eslint-disable react/forbid-component-props */
/* eslint-disable react/no-unused-prop-types */
const dummyArray = [];
export class MultiSelectComponent extends React.Component {
constructor(props) {
super(props);
this.getNextAriaId = getUniqueId(this);
this.getFilterSuggestions = makeGetFilterSuggestions();
this.formatOptions = makeFormatOptions();
this.getSelectedOptions = makeGetSelectedOptions();
this.getIsShowClearIcon = makeGetIsShowClearIcon(); //Use in AdvancedMultiSelect component
this.objectConcat = makeObjectConcat();
this.formatSelectedOptions = makeFormatOptions();
const {
selectedOptions,
searchDebounceTime = 500
} = this.props;
const {
allOptions,
normalizedAllOptions,
normalizedFormatOptions
} = this.handleFormatOptions(this.props);
const {
formatSelectedOptions,
normalizedSelectedOptions,
selectedOptionIds
} = this.handleGetSelectedOptions(selectedOptions, normalizedFormatOptions, this.props);
this.state = {
options: allOptions,
optionsNormalize: normalizedAllOptions,
selectedOptions: formatSelectedOptions,
selectedOptionIds,
searchStr: '',
hoverOption: 0,
isFetchingOptions: false,
highLightedSelectOptions: [],
lastHighLightedSelectOption: '',
shiftKeyPressHighLighted: 0,
isActive: false
};
this.allSelectedOptionsDetails = normalizedSelectedOptions;
this.suggestionsOrder = [];
this._isMounted = false;
bind.apply(this, ['handleInputCick', 'handleFilterSuggestions', 'handleKeyDown', 'handleSelectAll', 'handleDeselectAll', 'handleSelectOption', 'handleRemoveOption', 'handleSearch', 'handleClickSelectedOption', 'handleChange', 'handlePopupClose', 'togglePopup', 'selectedOptionRef', 'searchInputRef', 'suggestionContainerRef', 'suggestionItemRef', 'selectedOptionContainerRef', 'handleActive', 'handleInactive', 'handleMouseEnter', 'handleFetchOptions', 'handleFormatOptions', 'handleGetSelectedOptions', 'handleComponentDidUpdate', 'handleInputFocus', 'handleExposedPublicMethods', 'getSelectionUI', 'moveFocusToTextbox']);
this.handleSearchOptions = debounce(this.handleSearchOptions.bind(this), searchDebounceTime);
this.handleScroll = this.handleScroll.bind(this);
this.handleScrollFuncCall = debounce(this.handleScrollFuncCall.bind(this), 500);
this.setSuggestionsVirtualizerContainerRefFunction = this.setSuggestionsVirtualizerContainerRefFunction.bind(this);
}
componentDidMount() {
// let { suggestionContainer } = this;
this._isMounted = true;
this.handleExposedPublicMethods(); // suggestionContainer &&
// suggestionContainer.addEventListener('scroll', this.handleScroll);
}
UNSAFE_componentWillReceiveProps(nextProps) {
const {
selectedOptions,
options,
valueField,
textField,
prefixText
} = nextProps;
const oldProps = this.props;
if (selectedOptions !== oldProps.selectedOptions || options !== oldProps.options || valueField !== oldProps.valueField || textField !== oldProps.textField || prefixText !== oldProps.prefixText //For GroupMultiSelect component
) {
const {
allOptions,
normalizedAllOptions,
normalizedFormatOptions
} = this.handleFormatOptions(nextProps);
const {
allSelectedOptionsDetails: oldAllSelectedOptionsDetails
} = this;
const optionsDetails = Object.assign({}, oldAllSelectedOptionsDetails, normalizedFormatOptions);
const {
formatSelectedOptions,
normalizedSelectedOptions,
selectedOptionIds
} = this.handleGetSelectedOptions(selectedOptions, optionsDetails, nextProps);
this.allSelectedOptionsDetails = Object.assign(oldAllSelectedOptionsDetails, normalizedSelectedOptions);
this.setState({
options: allOptions,
optionsNormalize: normalizedAllOptions,
selectedOptions: formatSelectedOptions,
selectedOptionIds
}, () => {
const {
hoverOption
} = this.state;
const suggestions = this.handleFilterSuggestions();
const suggestionsLen = suggestions.length;
const {
id
} = suggestions[hoverOption] || {};
const newHoverOption = !getIsEmptyValue(id) ? hoverOption : suggestionsLen ? suggestionsLen - 1 : 0;
this.setState({
hoverOption: newHoverOption
});
});
}
}
componentDidUpdate(prevProps, prevState) {
const {
suggestionContainer,
selectedOptionContainer,
suggestionsOrder
} = this;
const {
hoverOption,
highLightedSelectOptions,
selectedOptions,
searchStr
} = this.state;
const {
needLocalSearch,
isPopupOpen,
onDropBoxClose,
onDropBoxOpen,
isSearchClearOnClose
} = this.props; //handle dropbox open & close
if (prevProps.isPopupOpen !== isPopupOpen) {
isPopupOpen && onDropBoxOpen && this.handleFetchOptions(onDropBoxOpen, searchStr);
if (!isPopupOpen) {
this.setState({
hoverOption: 0
});
isSearchClearOnClose && searchStr && this.handleSearch('');
onDropBoxClose && onDropBoxClose();
}
} //scrollTo handling
const hoverId = suggestionsOrder[hoverOption] || '';
const selectedSuggestion = this[`suggestion_${hoverId}`];
const lastHighLightedSelectOption = highLightedSelectOptions.slice(-1).length ? highLightedSelectOptions.slice(-1)[0] : null;
const selectedOption = this[`selectedOption_${lastHighLightedSelectOption}`];
isPopupOpen && scrollTo(suggestionContainer, selectedSuggestion);
selectedOptions.length && selectedOption && scrollTo(selectedOptionContainer, selectedOption); //When suggestions length less than 5, getNextOptions function call
const {
isNextOptions,
getNextOptions
} = this.props; // let { searchStr } = this.state;
const suggestions = this.handleFilterSuggestions();
const suggestionsLen = suggestions.length;
if (isPopupOpen && suggestionsLen <= 5 && isNextOptions && getNextOptions && !needLocalSearch) {
this.handleFetchOptions(getNextOptions, searchStr);
} //Need To MultiselectNew Component
this.handleComponentDidUpdate(prevProps, prevState); // if (isPopupOpen && isNextOptions && prevProps.selectedOptions.length !== selectedOptions.length) {
// let { scrollHeight, clientHeight } = this.suggestionContainer || {};
// let isElementScrollable = scrollHeight > clientHeight;
// if (!isElementScrollable) {
// this.handleScrollFuncCall();
// }
// }
}
componentWillUnmount() {
// let { suggestionContainer } = this;
this._isMounted = false; // suggestionContainer &&
// suggestionContainer.removeEventListener('scroll', this.handleScroll);
}
handleComponentDidUpdate() {
return;
}
handleFormatOptions(props) {
const {
options,
valueField,
textField,
prefixText,
disabledOptions,
allowValueFallback,
searchFields
} = props;
return this.formatOptions({
options,
valueField,
textField,
prefixText,
optionType: 'default',
disabledOptions,
allowValueFallback,
searchFields
});
}
handleGetSelectedOptions(selectedOptions, normalizedFormatOptions, props) {
const {
allowValueFallback,
limit
} = props;
return this.getSelectedOptions({
selectedOptions,
normalizedFormatOptions,
allowValueFallback,
limit
});
}
handleInputCick(e) {
const {
removeClose
} = this.props;
const {
highLightedSelectOptions,
searchStr = ''
} = this.state;
if (highLightedSelectOptions.length) {
this.setState({
highLightedSelectOptions: [],
shiftKeyPressHighLighted: 0,
lastHighLightedSelectOption: ''
});
removeClose(e);
} else if (searchStr.length) {
removeClose(e);
} else {
this.togglePopup(e);
}
}
handleFilterSuggestions() {
const {
options = dummyArray,
searchStr = ''
} = this.state;
const {
selectedOptions,
needLocalSearch,
keepSelectedOptions,
searchFields
} = this.props;
const {
suggestions,
suggestionIds
} = this.getFilterSuggestions({
options,
selectedOptions,
searchStr: getSearchString(searchStr),
needSearch: needLocalSearch,
keepSelectedOptions,
searchFields
});
this.suggestionsOrder = suggestionIds;
return suggestions;
}
handleKeyDown(e) {
const {
keyCode,
ctrlKey,
metaKey,
shiftKey
} = e;
let suggestions = [];
let {
hoverOption,
searchStr,
highLightedSelectOptions,
lastHighLightedSelectOption,
shiftKeyPressHighLighted,
selectedOptionIds: selectedOptions
} = this.state;
const {
isNextOptions,
getNextOptions,
isPopupOpen,
isPopupOpenOnEnter,
onKeyDown,
limit
} = this.props;
const allowKeyboardActions = !limit || limit && selectedOptions.length < limit;
const highLightedSelectOptionsLen = highLightedSelectOptions.length;
if (isPopupOpen && (keyCode === 38 || keyCode === 40 || keyCode === 13 || keyCode === 27 || keyCode === 9)) {
suggestions = this.handleFilterSuggestions();
}
if (!isPopupOpen && !isPopupOpenOnEnter) {
onKeyDown && onKeyDown(e);
}
if (!isPopupOpen && keyCode === 40) {
//down arrow press popup open
e.preventDefault(); //prevent body scroll
this.togglePopup(e);
}
const suggestionsLen = suggestions.length;
if (suggestionsLen && isPopupOpen && keyCode === 38 && allowKeyboardActions) {
//up arrow
/*if (hoverOption === 0) { //disable first to last option higlight
!isNextOptions && this.setState({ hoverOption: suggestionsLen - 1 });
}*/
if (hoverOption) {
this.setState({
hoverOption: hoverOption - 1
});
}
} else if (suggestionsLen && isPopupOpen && keyCode === 40 && allowKeyboardActions) {
//down arrow
/*else if (hoverOption === suggestionsLen - 1 || hoverOption === null) {
//disable last to first option higlight
!isNextOptions && this.setState({ hoverOption: 0 });
}*/
if (isNextOptions && suggestionsLen >= 5 && hoverOption === suggestionsLen - 3) {
getNextOptions && this.handleFetchOptions(getNextOptions, searchStr);
this.setState({
hoverOption: hoverOption + 1
});
} else if (suggestionsLen - 1 > hoverOption) {
this.setState({
hoverOption: hoverOption + 1
});
}
} else if (keyCode === 13 && allowKeyboardActions) {
//enter ke
const selectedOption = suggestions[hoverOption] || {};
const {
id
} = selectedOption;
isPopupOpen && !getIsEmptyValue(id) && this.handleSelectOption(id, e);
!isPopupOpen && isPopupOpenOnEnter && this.togglePopup(e);
} else if (selectedOptions.length && keyCode === 8 && !searchStr.length) {
//backspace key
if (highLightedSelectOptionsLen) {
this.handleRemoveOption(highLightedSelectOptions);
} else {
this.handleRemoveOption(selectedOptions.slice(-1)); // this.setState({
// highLightedSelectOptions: selectedOptions.slice(-1)
// });
}
} else if (selectedOptions && keyCode === 65 && (ctrlKey || metaKey) && !searchStr.length) {
//ctrl+a key
this.setState({
highLightedSelectOptions: selectedOptions,
shiftKeyPressHighLighted: 0
});
} else if (keyCode === 39 && shiftKey && selectedOptions.length && !searchStr.length) {
//shift+right arrow=39
const lastHighLightedSelectOptionIndex = lastHighLightedSelectOption && selectedOptions.indexOf(lastHighLightedSelectOption) >= 0 ? selectedOptions.indexOf(lastHighLightedSelectOption) : 0;
const newShiftKeyPressHighLighted = shiftKeyPressHighLighted ? shiftKeyPressHighLighted : shiftKeyPressHighLighted + 1;
const newHighLightedSelectOption = lastHighLightedSelectOptionIndex !== null ? selectedOptions[lastHighLightedSelectOptionIndex + newShiftKeyPressHighLighted] : selectedOptions[0];
if (!getIsEmptyValue(newHighLightedSelectOption)) {
const newLastHighLightedSelectOption = lastHighLightedSelectOption ? lastHighLightedSelectOption : selectedOptions[0];
highLightedSelectOptions = !shiftKeyPressHighLighted ? [newLastHighLightedSelectOption] : highLightedSelectOptions;
const isRemove = highLightedSelectOptions.indexOf(newHighLightedSelectOption) >= 0 && newHighLightedSelectOption !== lastHighLightedSelectOption ? true : false;
const newHighLightedSelectOptions = isRemove ? highLightedSelectOptions.filter(option => option !== newHighLightedSelectOption) : [...highLightedSelectOptions, newHighLightedSelectOption];
this.setState({
highLightedSelectOptions: newHighLightedSelectOptions,
shiftKeyPressHighLighted: newShiftKeyPressHighLighted + 1,
lastHighLightedSelectOption: newLastHighLightedSelectOption
});
}
} else if (keyCode === 37 && shiftKey && selectedOptions.length && !searchStr.length) {
// shift+left arrow=37
const lastHighLightedSelectOptionIndex = lastHighLightedSelectOption ? selectedOptions.indexOf(lastHighLightedSelectOption) : selectedOptions.length - 1;
const newShiftKeyPressHighLighted = shiftKeyPressHighLighted !== 1 ? shiftKeyPressHighLighted : shiftKeyPressHighLighted - 1;
const newHighLightedSelectOption = selectedOptions[lastHighLightedSelectOptionIndex + newShiftKeyPressHighLighted - 1];
if (!getIsEmptyValue(newHighLightedSelectOption)) {
const newLastHighLightedSelectOption = lastHighLightedSelectOption ? lastHighLightedSelectOption : selectedOptions.slice(-1)[0];
highLightedSelectOptions = !shiftKeyPressHighLighted ? [newLastHighLightedSelectOption] : highLightedSelectOptions;
const isRemove = highLightedSelectOptions.indexOf(newHighLightedSelectOption) >= 0 && newHighLightedSelectOption !== lastHighLightedSelectOption ? true : false;
const newHighLightedSelectOptions = isRemove ? highLightedSelectOptions.filter(option => option !== newHighLightedSelectOption) : [...highLightedSelectOptions, newHighLightedSelectOption];
this.setState({
highLightedSelectOptions: newHighLightedSelectOptions,
shiftKeyPressHighLighted: newShiftKeyPressHighLighted - 1,
lastHighLightedSelectOption: newLastHighLightedSelectOption
});
}
} else if ((keyCode === 39 || keyCode === 37) && selectedOptions.length && !searchStr.length) {
const isRightArrow = keyCode === 39 ? true : false; // let isLefttArrow = keyCode === 37 ? true : false;
if (highLightedSelectOptions.length) {
const [lastHighLightedSelectOption] = highLightedSelectOptions.slice(-1);
const lastHighLightedSelectOptionIndex = selectedOptions.indexOf(lastHighLightedSelectOption);
const newLastHighLightedSelectOptionIndex = isRightArrow ? lastHighLightedSelectOptionIndex === selectedOptions.length - 1 ? lastHighLightedSelectOptionIndex : lastHighLightedSelectOptionIndex + 1 : lastHighLightedSelectOptionIndex - 1;
const newLastHighLightedSelectOption = selectedOptions[newLastHighLightedSelectOptionIndex];
const isEmptyHighlighted = isRightArrow && highLightedSelectOptions.length === 1 && selectedOptions.slice(-1)[0] === lastHighLightedSelectOption ? true : false;
if (!getIsEmptyValue(newLastHighLightedSelectOption)) {
this.setState({
lastHighLightedSelectOption: isEmptyHighlighted ? '' : newLastHighLightedSelectOption,
highLightedSelectOptions: isEmptyHighlighted ? [] : [newLastHighLightedSelectOption],
shiftKeyPressHighLighted: 0
});
}
} else {
const [newLastHighLightedSelectOption] = isRightArrow ? selectedOptions : selectedOptions.slice(-1);
this.setState({
lastHighLightedSelectOption: newLastHighLightedSelectOption,
highLightedSelectOptions: [newLastHighLightedSelectOption],
shiftKeyPressHighLighted: 0
});
}
} else if (keyCode === 27) {// this.handlePopupClose(e);
} else if (keyCode === 9) {
this.handlePopupClose(e);
}
}
handleSelectAll(e) {
e && e.preventDefault();
const suggestions = this.handleFilterSuggestions();
const {
selectedOptions
} = this.props;
const newSelectedOptions = [];
suggestions.forEach(option => {
const {
id
} = option;
if (selectedOptions.indexOf(id) === -1) {
newSelectedOptions.push(id);
}
});
this.handleChange([...selectedOptions, ...newSelectedOptions]); // this.handlePopupClose(e);
}
handleDeselectAll(e) {
e && e.preventDefault();
const {
removeClose
} = this.props;
const {
highLightedSelectOptions
} = this.state;
if (highLightedSelectOptions.length) {
this.setState({
highLightedSelectOptions: [],
lastHighLightedSelectOption: ''
});
}
removeClose(e);
this.handleChange([]);
}
handleSelectOption(option, value, index, e) {
const {
selectedOptions,
isSearchClearOnSelect,
keepSelectedOptions
} = this.props;
const {
searchStr
} = this.state;
if (searchStr.trim() != '' && isSearchClearOnSelect) {
this.handleSearch('');
}
if (keepSelectedOptions && selectedOptions.indexOf(option) != -1) {
let newSelectedOptions = selectedOptions.filter(id => {
return id != option;
});
this.handleChange(newSelectedOptions, e);
} else {
this.handleChange([...selectedOptions, option], e);
}
}
handleRemoveOption(options) {
const newOptions = !getIsEmptyValue(options) && !Array.isArray(options) ? [options] : options;
const {
selectedOptions,
isReadOnly
} = this.props;
const {
highLightedSelectOptions,
lastHighLightedSelectOption,
shiftKeyPressHighLighted
} = this.state;
if (newOptions.length && !isReadOnly) {
const newSelectedOptions = selectedOptions.filter(option => newOptions.indexOf(option) === -1);
const newHighLightedSelectOptions = highLightedSelectOptions.filter(option => newSelectedOptions.indexOf(option) >= 0);
let isHighlightedRemoved = false;
const newOptionsLen = newOptions.length;
for (let i = 0; i < newOptionsLen; i++) {
const removedOption = newOptions[i];
if (highLightedSelectOptions.indexOf(removedOption) >= 0) {
isHighlightedRemoved = true;
break;
}
}
this.setState({
lastHighLightedSelectOption: newSelectedOptions.indexOf(lastHighLightedSelectOption) >= 0 && !isHighlightedRemoved ? lastHighLightedSelectOption : '',
highLightedSelectOptions: isHighlightedRemoved ? [] : newHighLightedSelectOptions,
shiftKeyPressHighLighted: isHighlightedRemoved ? 0 : shiftKeyPressHighLighted
});
this.handleChange(newSelectedOptions);
}
this.moveFocusToTextbox();
}
handleMouseEnter(id, val, hoverOptionIndex, e) {
e && e.preventDefault();
const {
hoverOption
} = this.state;
const {
suggestionsOrder
} = this;
const newHoverIndex = suggestionsOrder.indexOf(id);
if (newHoverIndex !== hoverOption) {
this.setState({
hoverOption: newHoverIndex
});
}
}
handleFetchOptions() {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
const [APICall, searchStr] = args;
const {
isFetchingOptions
} = this.state;
const {
_isMounted
} = this;
const isForce = isFetchingOptions && searchStr ? true : false;
if (!isFetchingOptions && APICall || isForce) {
this.setState({
isFetchingOptions: true
});
try {
return APICall(searchStr).then(() => {
_isMounted && this.setState({
isFetchingOptions: false
});
}, () => {
_isMounted && this.setState({
isFetchingOptions: false
});
});
} catch (e) {
_isMounted && this.setState({
isFetchingOptions: false
});
}
}
}
handleSearchOptions() {
const {
onSearch
} = this.props;
const {
searchStr
} = this.state;
searchStr && this.handleFetchOptions(onSearch, searchStr);
}
handleSearch(value, e) {
const {
onSearch,
isPopupOpen
} = this.props;
!isPopupOpen && e && this.togglePopup(e);
const {
searchStr = ''
} = this.state;
const searchStrRegex = getSearchString(searchStr);
const valueStrRegex = getSearchString(value);
const isSearch = searchStrRegex !== valueStrRegex ? true : false;
this.setState({
searchStr: value
}, () => {
if (!value) {
onSearch && onSearch('');
} else if (isSearch && onSearch) {
this.handleSearchOptions();
}
});
}
handleClickSelectedOption() {
let id = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
let e = arguments.length > 1 ? arguments[1] : undefined;
const {
selectedOptions
} = this.props;
let {
highLightedSelectOptions,
lastHighLightedSelectOption
} = this.state;
const {
metaKey,
ctrlKey,
shiftKey
} = e;
if (e && shiftKey) {
//shift+click
let from = selectedOptions.indexOf(lastHighLightedSelectOption) >= 0 ? selectedOptions.indexOf(lastHighLightedSelectOption) : 0;
let to = id && selectedOptions.indexOf(id) >= 0 ? selectedOptions.indexOf(id) : null;
if (to >= 0 && to < from) {
[to] = [from, from = to];
}
to += 1;
const newSelectedHighlights = to ? selectedOptions.slice(from, to) : [];
to && this.setState({
highLightedSelectOptions: newSelectedHighlights,
lastHighLightedSelectOption: id
});
} else if (e && (ctrlKey || metaKey)) {
//ctrl+click
const isRemove = highLightedSelectOptions.indexOf(id) >= 0;
let newSelectedHighlights = [];
if (isRemove) {
lastHighLightedSelectOption = id === lastHighLightedSelectOption ? '' : lastHighLightedSelectOption;
newSelectedHighlights = highLightedSelectOptions.filter(option => option !== id);
} else {
lastHighLightedSelectOption = id;
newSelectedHighlights = [...highLightedSelectOptions, id];
}
this.setState({
highLightedSelectOptions: newSelectedHighlights,
lastHighLightedSelectOption
});
} else {
this.setState({
highLightedSelectOptions: [id],
lastHighLightedSelectOption: id
});
}
this.setState({
shiftKeyPressHighLighted: 0
});
this.moveFocusToTextbox();
}
handleScroll(e) {
let ele = e.target;
let isScrollReachedBottom = findScrollEnd(ele);
isScrollReachedBottom && this.handleScrollFuncCall();
}
handleScrollFuncCall() {
const {
getNextOptions,
isNextOptions
} = this.props;
const {
searchStr
} = this.state;
isNextOptions && getNextOptions && this.handleFetchOptions(getNextOptions, searchStr);
}
handleChange() {
let selectedOptions = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
let e = arguments.length > 1 ? arguments[1] : undefined;
const {
optionsNormalize
} = this.state;
const {
onChange,
needToCloseOnSelect,
togglePopup,
selectedOptions: propSelectedOptions,
disabledOptions = dummyArray,
limit
} = this.props;
const {
newSelectedOptions
} = filterSelectedOptions({
selectedOptions,
propSelectedOptions,
disabledOptions,
limit
});
const selectedOptionsLen = newSelectedOptions.length;
const allSelectedOptionsDetails = [];
for (let i = 0; i < selectedOptionsLen; i++) {
const id = newSelectedOptions[i];
allSelectedOptionsDetails.push(optionsNormalize[id]);
}
onChange && onChange(newSelectedOptions, allSelectedOptionsDetails); // this.setState({ searchStr: '' });
this.moveFocusToTextbox();
if (needToCloseOnSelect) {
togglePopup(e);
}
}
togglePopup(e) {
const {
togglePopup,
defaultDropBoxPosition,
isReadOnly
} = this.props;
!isReadOnly && togglePopup(e, defaultDropBoxPosition ? `${defaultDropBoxPosition}` : null);
}
handlePopupClose(e) {
const {
closePopupOnly
} = this.props;
closePopupOnly(e);
}
searchInputRef(el) {
const {
getRef
} = this.props;
this.searchInput = el;
getRef && getRef(el);
}
selectedOptionContainerRef(el) {
const {
getTargetRef
} = this.props;
this.selectedOptionContainer = el;
getTargetRef(el);
}
selectedOptionRef(el, id) {
this[`selectedOption_${id}`] = el;
}
suggestionContainerRef(el) {
this.suggestionContainer = el;
typeof this.setSuggestionsVirtualizerRef === 'function' && this.setSuggestionsVirtualizerRef(el);
}
suggestionItemRef(el, index, id) {
this[`suggestion_${id}`] = el;
}
handleActive(e) {
const {
searchStr,
isActive
} = this.state;
if (!isActive) {
this.setState({
isActive: true
});
}
const {
target
} = e || {};
target && target.setSelectionRange(target, 0);
const {
onFocus
} = this.props;
onFocus && onFocus(searchStr);
}
handleInactive() {
const {
isActive
} = this.state;
if (isActive) {
this.setState({
isActive: false
});
}
}
handleInputFocus() {
const {
isDisabled,
isReadOnly
} = this.props;
!isDisabled && !isReadOnly && this.moveFocusToTextbox();
}
moveFocusToTextbox() {
this.searchInput && this.searchInput.focus({
preventScroll: true
});
}
handleExposedPublicMethods() {
const {
getPublicMethods,
openPopupOnly
} = this.props;
getPublicMethods && getPublicMethods({
openPopupOnly
});
}
setSuggestionsVirtualizerContainerRefFunction(refFunc) {
this.setSuggestionsVirtualizerRef = refFunc;
this.suggestionContainer && refFunc(this.suggestionContainer);
}
getSelectionUI() {
let isResponsive = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
let {
size,
needBorder,
disableAction,
borderColor,
placeHolder,
textBoxSize,
variant,
textBoxClass,
needAutoFocus,
htmlId,
children,
customChildrenClass,
autoComplete,
a11y,
disabledOptions,
i18nKeys,
isReadOnly,
isDisabled,
palette,
dataId,
setAriaId,
isPopupOpen,
ariaErrorId,
customProps,
isFocus,
isPopupReady,
renderCustomClearComponent,
renderCustomToggleIndicator
} = this.props;
let {
isActive,
selectedOptions,
highLightedSelectOptions,
searchStr
} = this.state;
const {
clearText = MULTISELECT_I18N_KEYS.clearText
} = i18nKeys;
const {
clearLabel = MULTISELECT_I18N_KEYS.clearLabel,
ariaLabelledby
} = a11y;
let {
isShowClearIcon: isShowClear
} = this.getIsShowClearIcon({
selectedOptions,
disabledOptions
});
const isShowClearIcon = !isReadOnly && !isDisabled && !disableAction && isShowClear;
const isEditable = !(isReadOnly || isDisabled || disableAction);
let {
TextBoxIconProps = {}
} = customProps;
return /*#__PURE__*/React.createElement(Container, {
align: "vertical",
alignBox: "row",
className: `${style.container} ${style[size]} ${needBorder ? !disableAction ? style[`borderColor_${borderColor}`] : style.borderColor_transparent : ''} ${isActive && needBorder || isResponsive || isEditable && isFocus && needBorder ? style.active : ''} ${textBoxClass} ${needBorder ? style.hasBorder : ''}`,
eleRef: this.selectedOptionContainerRef,
wrap: "wrap"
}, /*#__PURE__*/React.createElement(SelectedOptions, {
selectedOptions: selectedOptions,
highLightedSelectOptions: highLightedSelectOptions,
isReadOnly: isReadOnly,
getRef: this.selectedOptionRef,
onRemove: this.handleRemoveOption,
onSelect: this.handleClickSelectedOption,
size: size,
palette: palette,
dataId: `${dataId}_selectedOptions`
}), /*#__PURE__*/React.createElement(Box, {
flexible: true,
className: style.wrapper,
adjust: true,
shrink: true
}, /*#__PURE__*/React.createElement("span", {
className: ` ${style.custmSpan} ${textBoxSize === 'xmedium' ? style.custmSpanXmedium : style.custmSpanMedium} ${isShowClearIcon ? style.clearIconSpace : ''}
${customChildrenClass ? customChildrenClass : ''}`
}, searchStr), /*#__PURE__*/React.createElement(TextBoxIcon, {
isDisabled: isDisabled,
inputRef: this.searchInputRef,
needBorder: false,
onBlur: this.handleInactive,
onChange: this.handleSearch,
onClick: !isResponsive ? this.handleInputCick : undefined,
onFocus: this.handleActive,
onKeyDown: this.handleKeyDown,
autofocus: needAutoFocus,
placeHolder: selectedOptions.length >= 1 ? '' : placeHolder,
size: textBoxSize,
value: searchStr,
variant: variant,
dataId: `${dataId}_textBox`,
isReadOnly: isReadOnly,
tabindex: isDisabled && '-1',
customClass: {
customTBoxWrap: style.custmInputWrapper
},
htmlId: htmlId,
a11y: {
role: 'combobox',
ariaOwns: setAriaId,
ariaControls: setAriaId,
ariaExpanded: !isReadOnly && !isDisabled && !disableAction && isPopupOpen ? true : false,
ariaHaspopup: true,
ariaRequired: true,
ariaDescribedby: ariaErrorId,
ariaLabelledby: ariaLabelledby
},
autoComplete: autoComplete,
renderRightPlaceholderNode: typeof renderCustomToggleIndicator == 'function' ? renderCustomToggleIndicator({
togglePopup: this.togglePopup,
isPopupOpened: isPopupReady,
isReadOnly: isReadOnly,
isDisabled: isDisabled
}) : renderCustomToggleIndicator,
...TextBoxIconProps
}, /*#__PURE__*/React.createElement(Container, {
isInline: true,
isCover: false,
alignBox: "row",
align: "vertical",
className: style.rightPlaceholder
}, isShowClearIcon ? typeof renderCustomClearComponent === 'function' ? renderCustomClearComponent({
clearText,
isPopupOpened: isPopupReady,
handleClearAll: this.handleDeselectAll
}) : /*#__PURE__*/React.createElement(Box, {
className: `${style.delete} ${style[`${palette}Delete`]}`,
dataId: `${dataId}_clearIcon`,
"data-title": clearText,
onClick: this.handleDeselectAll,
tagName: "button",
"aria-label": clearLabel
}, /*#__PURE__*/React.createElement(Icon, {
name: "ZD-delete",
size: "15"
})) : null, children ? /*#__PURE__*/React.createElement(Box, {
dataId: `${dataId}_children`
}, children) : null))));
}
render() {
let {
isReadOnly,
searchEmptyMessage,
emptyMessage,
noMoreOptionsMessage,
dropBoxSize,
isPopupOpen,
isPopupReady,
position,
defaultDropBoxPosition,
getContainerRef,
removeClose,
isAnimate,
animationStyle,
isDisabled,
title,
needResponsive,
dataId,
dataSelectorId,
isSearching,
borderColor,
disableAction,
isBoxPaddingNeed,
isAbsolutePositioningNeeded,
positionsOffset,
targetOffset,
isRestrictScroll,
palette,
i18nKeys,
getFooter,
needEffect,
boxSize,
isLoading,
selectAllText,
needSelectAll,
isVirtualizerEnabled,
limit
} = this.props;
const {
selectedOptions,
searchStr,
hoverOption,
options,
isFetchingOptions,
selectedOptionIds
} = this.state;
const {
searchText = MULTISELECT_I18N_KEYS.searchText,
limitReachedMessage = MULTISELECT_I18N_KEYS.limitReachedMessage
} = i18nKeys;
const suggestions = this.handleFilterSuggestions();
const setAriaId = this.getNextAriaId();
const ariaErrorId = this.getNextAriaId();
i18nKeys = Object.assign({}, MobileHeader_defaultProps.i18nKeys, i18nKeys, {
emptyText: i18nKeys.emptyText || emptyMessage,
searchEmptyText: i18nKeys.searchEmptyText || searchEmptyMessage,
noMoreText: i18nKeys.noMoreText || noMoreOptionsMessage
});
let isModel = isMobilePopover(needResponsive);
return /*#__PURE__*/React.createElement("div", {
className: `${style.wrapper} ${isDisabled ? style.disabled : ''} ${isReadOnly ? style.readOnly : ''} ${disableAction ? CssProvider('isBlock') : ''} ${borderColor === 'transparent' ? style.transparentContainer : ''} ${needEffect && !(isDisabled || isReadOnly) ? style.effect : ''}`,
"data-id": `${isDisabled ? `${dataId}_disabled` : isReadOnly ? `${dataId}_readOnly` : dataId}`,
"data-test-id": `${isDisabled ? `${dataId}_disabled` : isReadOnly ? `${dataId}_readOnly` : dataId}`,
"data-title": isDisabled ? title : null,
onClick: this.handleInputFocus,
"data-selector-id": dataSelectorId
}, this.getSelectionUI(), !isReadOnly && !isDisabled && !disableAction && isPopupOpen ? /*#__PURE__*/React.createElement(ResponsiveDropBox, {
animationStyle: animationStyle,
boxPosition: position || `${defaultDropBoxPosition}`,
getRef: getContainerRef,
isActive: isPopupReady,
isAnimate: isAnimate,
isArrow: false,
onClick: removeClose,
needResponsive: needResponsive,
isPadding: false,
isBoxPaddingNeed: isBoxPaddingNeed,
isAbsolutePositioningNeeded: isAbsolutePositioningNeeded,
positionsOffset: positionsOffset,
targetOffset: targetOffset,
isRestrictScroll: isRestrictScroll,
palette: palette,
htmlId: setAriaId,
a11y: {
role: 'listbox',
ariaMultiselectable: true
},
size: boxSize,
alignBox: "row",
isResponsivePadding: getFooter ? false : true,
dataId: `${dataId}_dropbox`
}, /*#__PURE__*/React.createElement(Box, {
flexible: true
}, /*#__PURE__*/React.createElement(Card, {
customClass: `${style.box} ${style[`${palette}Box`]}`,
onScroll: this.handleScroll
}, isModel ? /*#__PURE__*/React.createElement(MobileHeader, {
selectedOptions: selectedOptions,
i18nKeys: i18nKeys,
onClick: this.handlePopupClose
}, /*#__PURE__*/React.createElement("div", {
className: style.effect
}, this.getSelectionUI(true))) : null, needSelectAll && !(limit >= 0) ? /*#__PURE__*/React.createElement(CardHeader, null, /*#__PURE__*/React.createElement(MultiSelectHeader, {
onSelect: this.handleSelectAll,
selectAllText: selectAllText,
suggestions: suggestions,
dataId: dataId
})) : null, isLoading ? /*#__PURE__*/React.createElement(Container, {
align: "both",
className: style.loader
}, /*#__PURE__*/React.createElement(Loader, null)) : /*#__PURE__*/React.createElement(CardContent, {
shrink: true,
customClass: !isModel && dropBoxSize ? style[dropBoxSize] : '',
eleRef: this.suggestionContainerRef
}, isSearching ? /*#__PURE__*/React.createElement("div", {
className: style[palette]
}, searchText) : suggestions.length ? /*#__PURE__*/React.createElement(Suggestions, {
suggestions: suggestions,
isVirtualizerEnabled: isVirtualizerEnabled,
setVirtualizerContainerRefFunction: this.setSuggestionsVirtualizerContainerRefFunction,
getRef: this.suggestionItemRef,
hoverOption: hoverOption,
onClick: this.handleSelectOption,
onMouseEnter: this.handleMouseEnter,
needBorder: false,
dataId: `${dataId}_Options`,
palette: palette,
selectedOptions: selectedOptionIds,
a11y: {
role: 'option'
},
limit: limit,
limitReachedMessage: limitReachedMessage
}) : /*#__PURE__*/React.createElement(EmptyState, {
isLoading: isFetchingOptions,
options: options,
searchString: searchStr,
suggestions: suggestions,
dataId: dataId,
palette: palette,
i18nKeys: i18nKeys,
htmlId: ariaErrorId
}), isFetchingOptions && /*#__PURE__*/React.createElement(Container, {
isCover: false,
align: "both"
}, /*#__PURE__*/React.createElement(Loader, null))), getFooter ? /*#__PURE__*/React.createElement(CardFooter, null, getFooter()) : null))) : null);
}
}
MultiSelectComponent.propTypes = MultiSelect_propTypes;
MultiSelectComponent.defaultProps = MultiSelect_defaultProps;
MultiSelectComponent.displayName = 'MultiSelect';
const MultiSelect = Popup(MultiSelectComponent);
MultiSelect.propTypes = MultiSelectComponent.propTypes;
MultiSelect.defaultProps = MultiSelectComponent.defaultProps;
export default MultiSelect;