UNPKG

@zohodesk/components

Version:

In this Package, we Provide Some Basic Components to Build Web App

583 lines (552 loc) • 19.2 kB
/**** Libraries ****/ import React from 'react'; import { AdvancedMultiSelect_propTypes } from "./props/propTypes"; import { AdvancedMultiSelect_defaultProps } from "./props/defaultProps"; /**** Components ****/ import { MultiSelectComponent } from "./MultiSelect"; import SelectedOptions from "./SelectedOptions"; import Suggestions from "./Suggestions"; import EmptyState from "./EmptyState"; import MultiSelectHeader from "./MultiSelectHeader"; import Popup from "../Popup/Popup"; import TextBoxIcon from "../TextBoxIcon/TextBoxIcon"; import { Container, Box } from "../Layout"; import Card, { CardContent, CardHeader, CardFooter } from "../Card/Card"; import { Icon } from '@zohodesk/icons'; import { getUniqueId } from "../../Provider/IdProvider"; import ResponsiveDropBox from "../ResponsiveDropBox/ResponsiveDropBox"; import { ResponsiveReceiver } from "../../Responsive/CustomResponsive"; import Loader from '@zohodesk/svg/lib/Loader/Loader'; /**** CSS ****/ import style from "../../MultiSelect/MultiSelect.module.css"; /**** Methods ****/ import { getIsEmptyValue, getSearchString } from "../../utils/Common.js"; import { filterSelectedOptions, makeGetIsShowClearIcon } from "../../utils/dropDownUtils"; /* eslint-disable react/sort-prop-types */ /* eslint-disable react/no-unused-prop-types */ /* eslint-disable react/forbid-component-props */ const dummyArray = []; export class AdvancedMultiSelectComponent extends MultiSelectComponent { constructor(props) { super(props); const { state: oldState } = this; const { selectedOptionsLimit, selectedOptions } = this.props; const selectedOptionsLen = selectedOptions.length; this.state = Object.assign({}, oldState, { showedSelectedOptionsCount: !getIsEmptyValue(selectedOptionsLimit) && selectedOptionsLen > selectedOptionsLimit ? selectedOptionsLimit : selectedOptionsLen }); this.getIsShowClearIcon = makeGetIsShowClearIcon(); this.handleFilterSuggestions = this.handleFilterSuggestions.bind(this); this.handleChange = this.handleChange.bind(this); this.handleSelectOption = this.handleSelectOption.bind(this); this.handleGetSelectedOptions = this.handleGetSelectedOptions.bind(this); this.handleFormatSelectedOptions = this.handleFormatSelectedOptions.bind(this); this.handleComponentDidUpdate = this.handleComponentDidUpdate.bind(this); this.handleMoreClick = this.handleMoreClick.bind(this); this.getNextAriaId = getUniqueId(this); } handleFilterSuggestions() { const { options = dummyArray, searchStr } = this.state; const { needLocalSearch } = this.props; const { suggestions, suggestionIds } = this.getFilterSuggestions({ options, selectedOptions: dummyArray, searchStr: getSearchString(searchStr), needSearch: needLocalSearch }); this.suggestionsOrder = suggestionIds; return suggestions; } handleChange() { let selectedOptions = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; let e = arguments.length > 1 ? arguments[1] : undefined; const { optionsNormalize, searchStr } = this.state; const { onChange, needToCloseOnSelect = false, togglePopup, isSearchClearOnSelect, selectedOptions: propSelectedOptions, disabledOptions } = this.props; const { newSelectedOptions } = filterSelectedOptions({ selectedOptions, propSelectedOptions, disabledOptions }); 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); if (searchStr.trim() !== '' && isSearchClearOnSelect) { this.handleSearch(''); } this.searchInput && this.searchInput.focus({ preventScroll: true }); if (needToCloseOnSelect) { e && togglePopup(e); } } handleSelectOption(option, value, index, e) { const { selectedOptions } = this.props; if (selectedOptions.indexOf(option) >= 0) { //toggle select option this.handleRemoveOption(option); } else { this.handleChange([...selectedOptions, option], e); } } handleFormatOptions(props) { const { options, valueField, textField, imageField, iconName, iconSize, prefixText, optionType, disabledOptions } = props; return this.formatOptions({ options, valueField, textField, imageField, iconName, prefixText, optionType, iconSize, disabledOptions }); } handleGetSelectedOptions(selectedOptions, normalizedFormatOptions, props) { //handle selectedoptions length let { showedSelectedOptionsCount } = this.state || {}; const { selectedOptionsLimit, selectedOptionDetails } = props; showedSelectedOptionsCount = getIsEmptyValue(showedSelectedOptionsCount) ? selectedOptionsLimit : showedSelectedOptionsCount; const { normalizedFormatOptions: normalizedSelectedOptions } = this.handleFormatSelectedOptions(selectedOptionDetails, props); const formatOptions = this.objectConcat({ obj1: normalizedFormatOptions, obj2: normalizedSelectedOptions }); return this.getSelectedOptions({ selectedOptions, normalizedFormatOptions: formatOptions, selectedOptionsLength: showedSelectedOptionsCount }); } handleFormatSelectedOptions(selectedOptionDetails, props) { const { valueField, textField, imageField, optionType, iconName, iconSize, disabledOptions } = props; return this.formatSelectedOptions({ options: selectedOptionDetails, valueField, textField, imageField, optionType, iconName, iconSize, disabledOptions }); } handleComponentDidUpdate(prevProps, prevState) { const { allSelectedOptionsDetails: oldAllSelectedOptionsDetails } = this; const { selectedOptions, selectedOptionsLimit, id, selectedOptionDetails } = this.props; const { selectedOptions: oldSelectedOptions, selectedOptionsLimit: oldSelectedOptionsLimit } = prevProps; const { showedSelectedOptionsCount: oldCount } = this.state; const selectedOptionsLen = selectedOptions.length; const oldSelectedOptionsLen = oldSelectedOptions.length; if (id !== prevProps.id || selectedOptionsLimit !== oldSelectedOptionsLimit) { //handle component update case (New set of options or new selectedOptionsLimit update) this.setState({ showedSelectedOptionsCount: !getIsEmptyValue(selectedOptionsLimit) && selectedOptionsLen > selectedOptionsLimit ? selectedOptionsLimit : selectedOptionsLen }); return; } if (oldCount !== prevState.showedSelectedOptionsCount || selectedOptionDetails !== prevProps.selectedOptionDetails) { //When showedSelectedOptionsCount update, setState the updated selected option details const { normalizedFormatOptions } = this.handleFormatOptions(this.props); const optionsDetails = Object.assign({}, oldAllSelectedOptionsDetails, normalizedFormatOptions); const { formatSelectedOptions, normalizedSelectedOptions, selectedOptionIds } = this.handleGetSelectedOptions(selectedOptions, optionsDetails, this.props); this.allSelectedOptionsDetails = Object.assign(oldAllSelectedOptionsDetails, normalizedSelectedOptions); this.setState({ selectedOptions: formatSelectedOptions, selectedOptionIds }); } if (getIsEmptyValue(oldCount) || oldSelectedOptionsLen <= oldCount) { //handle not more options case if (selectedOptionsLen > oldCount) { this.setState({ showedSelectedOptionsCount: selectedOptionsLen }); } } else if (oldSelectedOptionsLen > selectedOptionsLen) { //handle more case and option remove case const showedSelectedOptions = oldSelectedOptions.slice(0, oldCount); let removeSelectedOption = oldCount; selectedOptions.forEach(option => { if (showedSelectedOptions.indexOf(option) >= 0) { removeSelectedOption -= 1; } }); const newCount = oldCount - removeSelectedOption; this.setState({ showedSelectedOptionsCount: newCount }, () => { const { showedSelectedOptionsCount } = this.state; if (showedSelectedOptionsCount === 0) { this.handleMoreClick(); } }); } } handleMoreClick(e) { const { showedSelectedOptionsCount } = this.state; const { selectedOptionsLimit, selectedOptions, getSelectedOptionDetails, removeClose } = this.props; removeClose(e); const selectedOptionsLen = selectedOptions.length; if (showedSelectedOptionsCount < selectedOptionsLen) { const newCount = showedSelectedOptionsCount + selectedOptionsLimit; const newSelectedOptionIds = selectedOptions.slice(showedSelectedOptionsCount, newCount); try { getSelectedOptionDetails(newSelectedOptionIds).then(() => { this.setState({ showedSelectedOptionsCount: showedSelectedOptionsCount + newSelectedOptionIds.length }); }); } catch (e) {//console.error('getSelectedOptionDetails is not a promise function.'); } } this.searchInput && this.searchInput.focus({ preventScroll: true }); } responsiveFunc(_ref) { let { mediaQueryOR } = _ref; return { tabletMode: mediaQueryOR([{ maxWidth: 700 }]) }; } render() { let { isReadOnly, needSelectAll, searchEmptyMessage, emptyMessage, noMoreOptionsMessage, dropBoxSize, placeHolder, isPopupOpen, isPopupReady, position, defaultDropBoxPosition, selectAllText, getContainerRef, removeClose, isAnimate, animationStyle, textBoxSize, variant, size, isDisabled, title, dataIdMultiSelectComp, dataIdClearIcon, dataIdSelectAllEle, dataIdLoading, selectedOptions: allselectedOptionIds = [], listItemSize, needBorder, i18nKeys, htmlId, a11y, borderColor, isBoxPaddingNeed, getFooter, customProps, needEffect, disabledOptions, isLoading, dataSelectorId, customClass, isAbsolutePositioningNeeded, positionsOffset, targetOffset, isRestrictScroll } = this.props; let { SuggestionsProps = {}, DropBoxProps = {} } = customProps; let { containerClass = '' } = customClass; const { clearText = 'Clear all' } = i18nKeys; const { clearLabel = 'Clear all' } = a11y; i18nKeys = Object.assign({}, i18nKeys, { emptyText: i18nKeys.emptyText || emptyMessage, noMoreText: i18nKeys.noMoreText || noMoreOptionsMessage, searchEmptyText: i18nKeys.searchEmptyText || searchEmptyMessage }); const { selectedOptions, searchStr, hoverOption, highLightedSelectOptions, isFetchingOptions, isActive, showedSelectedOptionsCount, options } = this.state; const suggestions = this.handleFilterSuggestions(); const allselectedOptionIdsLen = allselectedOptionIds.length; const moreCount = allselectedOptionIdsLen - showedSelectedOptionsCount; const setAriaId = this.getNextAriaId(); const ariaErrorId = this.getNextAriaId(); let { isShowClearIcon: isShowClear } = this.getIsShowClearIcon({ selectedOptions, disabledOptions }); let isShowClearIcon = !isReadOnly && !isDisabled && isShowClear; return /*#__PURE__*/React.createElement("div", { className: `${style.wrapper} ${isDisabled ? style.disabled : ''} ${borderColor === 'transparent' ? style.transparentContainer : ''} ${needEffect && !(isDisabled || isReadOnly) ? style.effect : ''} ${containerClass}`, "data-id": dataIdMultiSelectComp, "data-test-id": dataIdMultiSelectComp, "data-title": isDisabled ? title : null, onClick: this.handleInputFocus, "data-selector-id": dataSelectorId }, /*#__PURE__*/React.createElement(Container, { align: "vertical", alignBox: "row", className: `${style.container} ${style[size]} ${isActive && needBorder ? style.active : ''} ${needBorder ? style.hasBorder : ''} ${needBorder ? !isDisabled ? style[`borderColor_${borderColor}`] : style.borderColor_transparent : ''}`, eleRef: this.selectedOptionContainerRef, wrap: "wrap" }, /*#__PURE__*/React.createElement(SelectedOptions, { selectedOptions: selectedOptions, highLightedSelectOptions: highLightedSelectOptions, isReadOnly: isReadOnly, getRef: this.selectedOptionRef, onRemove: this.handleRemoveOption, onSelect: this.handleClickSelectedOption, dataId: `${dataIdMultiSelectComp}_selectedOptions` }), moreCount > 0 ? /*#__PURE__*/React.createElement(Box, { className: style.more, onClick: this.handleMoreClick, tagName: "button" }, `+ ${moreCount} More`) : null, /*#__PURE__*/React.createElement(Box, { flexible: true, className: style.wrapper, adjust: true, shrink: true, dataId: `${dataIdMultiSelectComp}_textBox` }, /*#__PURE__*/React.createElement("span", { className: ` ${style.custmSpan} ${textBoxSize === 'xmedium' ? style.custmSpanXmedium : style.custmSpanMedium} ${isShowClearIcon ? style.clearIconSpace : ''} ` }, searchStr), /*#__PURE__*/React.createElement(TextBoxIcon, { isDisabled: isDisabled, inputRef: this.searchInputRef, needBorder: false, onBlur: this.handleInactive, onChange: this.handleSearch, onClick: this.handleInputCick, onFocus: this.handleActive, onKeyDown: this.handleKeyDown, placeHolder: selectedOptions.length >= 1 ? '' : placeHolder, size: textBoxSize, value: searchStr, variant: variant, isReadOnly: isReadOnly, tabindex: isDisabled && '-1', customClass: { customTBoxWrap: style.custmInputWrapper }, htmlId: htmlId, a11y: { role: 'combobox', ariaOwns: setAriaId, ariaControls: setAriaId, ariaExpanded: !isReadOnly && !isDisabled && isPopupOpen ? true : false, ariaHaspopup: true, ariaDescribedby: ariaErrorId }, autoComplete: false }, isShowClearIcon ? /*#__PURE__*/React.createElement(Container, { alignBox: "row", align: "vertical" }, /*#__PURE__*/React.createElement(Box, { className: `${style.delete}`, dataId: dataIdClearIcon, "data-title": clearText, onClick: this.handleDeselectAll, tagName: "button", "aria-label": clearLabel }, /*#__PURE__*/React.createElement(Icon, { name: "ZD-delete", size: "15" }))) : null))), !isReadOnly && !isDisabled && isPopupOpen ? /*#__PURE__*/React.createElement(ResponsiveReceiver, { query: this.responsiveFunc, responsiveId: "Helmet" }, _ref2 => { let { tabletMode } = _ref2; return /*#__PURE__*/React.createElement(ResponsiveDropBox, { animationStyle: animationStyle, boxPosition: position || `${defaultDropBoxPosition}Center`, getRef: getContainerRef, isActive: isPopupReady, isAnimate: isAnimate, isArrow: false, onClick: removeClose, isPadding: false, isBoxPaddingNeed: isBoxPaddingNeed, htmlId: setAriaId, a11y: { role: 'listbox', ariaMultiselectable: true }, dataId: `${dataIdMultiSelectComp}_dropbox`, isAbsolutePositioningNeeded: isAbsolutePositioningNeeded, positionsOffset: positionsOffset, targetOffset: targetOffset, isRestrictScroll: isRestrictScroll, ...DropBoxProps, alignBox: "row", isResponsivePadding: getFooter ? false : true }, isLoading ? /*#__PURE__*/React.createElement(Container, { align: "both", className: style.loader }, /*#__PURE__*/React.createElement(Loader, null)) : /*#__PURE__*/React.createElement(Box, { flexible: true }, /*#__PURE__*/React.createElement(Card, { customClass: style.box, onScroll: this.handleScroll }, needSelectAll ? /*#__PURE__*/React.createElement(CardHeader, null, /*#__PURE__*/React.createElement(MultiSelectHeader, { onSelect: this.handleSelectAll, selectAllText: selectAllText, suggestions: suggestions, dataId: dataIdSelectAllEle })) : null, /*#__PURE__*/React.createElement(CardContent, { shrink: true, customClass: !tabletMode && dropBoxSize ? style[dropBoxSize] : '', eleRef: this.suggestionContainerRef }, suggestions.length ? /*#__PURE__*/React.createElement(Suggestions, { suggestions: suggestions, selectedOptions: allselectedOptionIds, getRef: this.suggestionItemRef, hoverOption: hoverOption, onClick: this.handleSelectOption, onMouseEnter: this.handleMouseEnter, needTick: true, listItemSize: listItemSize, a11y: { role: 'option' }, dataId: `${dataIdMultiSelectComp}_Options`, ...SuggestionsProps }) : /*#__PURE__*/React.createElement(EmptyState, { isLoading: isFetchingOptions, options: options, searchString: searchStr, suggestions: suggestions, dataId: dataIdLoading, 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); } } AdvancedMultiSelectComponent.propTypes = AdvancedMultiSelect_propTypes; AdvancedMultiSelectComponent.defaultProps = AdvancedMultiSelect_defaultProps; AdvancedMultiSelectComponent.displayName = 'AdvancedMultiSelect'; const AdvancedMultiSelect = Popup(AdvancedMultiSelectComponent); AdvancedMultiSelect.defaultProps = AdvancedMultiSelectComponent.defaultProps; AdvancedMultiSelect.propTypes = AdvancedMultiSelectComponent.propTypes; // if (__DOCS__) { // AdvancedMultiSelectComponent.docs = { // componentGroup: 'Form Elements', // folderName: 'Style Guide' // }; // // eslint-disable-next-line react/forbid-foreign-prop-types // AdvancedMultiSelect.propTypes = AdvancedMultiSelectComponent.propTypes; // } export default AdvancedMultiSelect;