UNPKG

@zohodesk/dot

Version:

In this Library, we Provide Some Basic Components to Build Your Application

903 lines (850 loc) 28.6 kB
import React, { Component, Fragment } from 'react'; import { defaultProps } from "./props/defaultProps"; import { propTypes } from "./props/propTypes"; import { Icon } from '@zohodesk/icons'; import Popup from '@zohodesk/components/es/Popup/Popup'; import { Box, Container } from '@zohodesk/components/es/Layout'; import ListItem from '@zohodesk/components/es/ListItem/ListItem'; import ListItemWithIcon from '@zohodesk/components/es/ListItem/ListItemWithIcon'; import DropDownHeading from '@zohodesk/components/es/DropDown/DropDownHeading'; import TextBoxIcon from '@zohodesk/components/es/TextBoxIcon/TextBoxIcon'; import Loader from '@zohodesk/svg/es/Loader/Loader'; import CommonEmptyState from "../../emptystate/CommonEmptyState/CommonEmptyState"; import EmptySearch from '@zohodesk/svg/es/emptystate/version3/EmptySearch'; import CssProvider from '@zohodesk/components/es/Provider/CssProvider'; import { findScrollEnd } from '@zohodesk/components/es/utils/Common'; import { getUniqueId } from '@zohodesk/components/es/Provider/IdProvider'; import btnStyle from '@zohodesk/components/es/semantic/Button/semanticButton.module.css'; import RippleEffect from '@zohodesk/components/es/RippleEffect/RippleEffect'; import ResponsiveDropBox from '@zohodesk/components/es/ResponsiveDropBox/ResponsiveDropBox'; import { ResponsiveReceiver } from '@zohodesk/components/es/Responsive/CustomResponsive'; import style from "./ToggleDropDown.module.css"; export class ToggleDropDown extends Component { constructor(props) { super(props); this.state = { searchValue: '', // selectedIndex: -1, options: props.options, isFetchingOptions: false }; this._isMounted = false; this.handleChange = this.handleChange.bind(this); // this.handleKeyDown = this.handleKeyDown.bind(this); this.onSelect = this.onSelect.bind(this); this.handleTogglePopup = this.handleTogglePopup.bind(this); this.showTogglePopup = this.showTogglePopup.bind(this); this.hideTogglePopup = this.hideTogglePopup.bind(this); this.onSearchAPI = this.onSearchAPI.bind(this); this.onSearchClear = this.onSearchClear.bind(this); this.handleFilterSuggestions = this.handleFilterSuggestions.bind(this); this.scrollContentRef = this.scrollContentRef.bind(this); // this.handleMouseEnter = this.handleMouseEnter.bind(this); this.searchInputRef = this.searchInputRef.bind(this); this.itemRef = this.itemRef.bind(this); // this.inputRef = this.inputRef.bind(this); this.handleScroll = this.handleScroll.bind(this); // this.getOptionsArray = this.getOptionsArray.bind(this); this.emptySearchSVG = this.emptySearchSVG.bind(this); this.getAriaId = getUniqueId(this); // this.getSelectedIndex = this.getSelectedIndex.bind(this); this.handleGetNextOptions = this.handleGetNextOptions.bind(this); this.handleFetchOptions = this.handleFetchOptions.bind(this); } componentDidMount() { this._isMounted = true; } componentWillUnmount() { this._isMounted = false; } emptySearchSVG() { return /*#__PURE__*/React.createElement(EmptySearch, { size: "small" }); } // inputRef(el) { // this.hiddenInput = el; // } itemRef(ele, index, id) { this[`suggestion_${id}`] = ele; } searchInputRef(el) { this.searchInput = el; } handleTogglePopup(e) { const { togglePopup, boxPosition, onSelectLabel, isPopupOpen, removeClose } = this.props; removeClose && removeClose(e); !isPopupOpen && onSelectLabel && onSelectLabel(e); togglePopup(e, boxPosition); } showTogglePopup(e) { const { togglePopup, boxPosition, onSelectLabel, isPopupOpen } = this.props; !isPopupOpen && onSelectLabel && onSelectLabel(e); !isPopupOpen && togglePopup(e, boxPosition); } hideTogglePopup(e) { const { togglePopup, boxPosition, onSelectLabel, isPopupOpen } = this.props; !isPopupOpen && onSelectLabel && onSelectLabel(e); isPopupOpen && togglePopup(e, boxPosition); } scrollContentRef(el) { const { isPopupOpen } = this.props; if (isPopupOpen) { this.optionsContainer = el; } } UNSAFE_componentWillReceiveProps(nextProps) { this.setState({ options: nextProps.options }); } onSelect(element, e) { const { onClick, togglePopup, preventPopupClose } = this.props; onClick && onClick(e, element); !preventPopupClose && togglePopup(e); } componentDidUpdate(prevProps) { const { isPopupOpen, isPopupReady, isSearch, idName, options, onDropDownOpen, onDropDownClose } = this.props; const { selectedIndex, searchValue } = this.state; if (prevProps.isPopupReady !== isPopupReady) {// setTimeout(() => { // isPopupReady // ? isSearch // ? this.searchInput.focus({ preventScroll: true }) // : this.hiddenInput.focus({ preventScroll: true }) // : this.hiddenInput.focus({ preventScroll: true }); // }, 10); } // const optionsArr = this.getOptionsArray(); // const option = optionsArr[selectedIndex]; // const id = (option && option[idName]) || {}; // const selSuggestion = this[`suggestion_${id}`]; // if (isPopupOpen) { // this.optionsContainer && scrollTo(this.optionsContainer, selSuggestion); // } if (!prevProps.isPopupOpen && isPopupOpen && searchValue.length) { this.onSearchClear(); } if (this.props.from == 'activityFilter') { //Temproary const results = this.props.options.filter(_ref => { let { isDisabled: id1 } = _ref; return !prevProps.options.some(_ref2 => { let { isDisabled: id2 } = _ref2; return id2 === id1; }); }); if (results.length > 0) { this.setState({ options: options }); } } if (prevProps.isPopupOpen !== isPopupOpen) { if (isPopupOpen) { onDropDownOpen && onDropDownOpen(); } else { onDropDownClose && onDropDownClose(); } // this.getSelectedIndex(optionsArr); } } handleFilterSuggestions(searchValue) { const { options, keyName, isGroupDropDown, groupOptionsKey, groupNameKey, searchFields } = this.props; searchValue = searchValue.trim().toLowerCase(); searchFields.push(keyName !== 'value' ? keyName : 'value'); let result = []; if (isGroupDropDown) { const filteredGroups = []; options.map(group => { const { needDivider } = group; const name = group[groupNameKey]; const groupOptions = group[groupOptionsKey]; const datas = groupOptions.filter(list => { return searchFields.some(key => { let val = list[key]; if (val === null || val === undefined) { return false; } else { return val.toLowerCase().includes(searchValue); } }); }); if (datas.length) { filteredGroups.push({ [groupNameKey]: name, [groupOptionsKey]: datas, needDivider }); } }); result = filteredGroups; } else { let filteredOptions = options.filter((item, i) => { if (item.needDivider) { return true; } return searchFields.some(key => { let val = item[key]; if (val === null || val === undefined) { return false; } else { return val.toLowerCase().includes(searchValue); } }); }); if (filteredOptions.length) { // to avoid more than one dividers consecutively comes as one by one let needDividerCount = 0; const orderedOptions = filteredOptions.filter(item => { if (item.needDivider && needDividerCount === 0) { needDividerCount++; return true; } else if (!item.needDivider) { needDividerCount = 0; return true; } }); // remove divider if it placed in first or last index filteredOptions = []; filteredOptions = orderedOptions.filter((item, i) => { if (i == 0 || i == orderedOptions.length - 1) { if (!item.needDivider) { return true; } } else { return true; } }); result = filteredOptions; } } return result; } // getOptionsArray() { // const { searchValue } = this.state; // const { isGroupDropDown, groupOptionsKey } = this.props; // const options = searchValue.length ? this.handleFilterSuggestions(searchValue) : this.props.options; // let optionsArr = []; // if (isGroupDropDown) { // for (let i = 0; i < options.length; i++) { // const groupOptions = options[i][groupOptionsKey]; // Array.prototype.push.apply(optionsArr, groupOptions); // } // } else { // optionsArr = options.filter((item) => !item.needDivider); // } // return optionsArr; // } // handleKeyDown(e) { // const { keyCode } = e; // const { selectedIndex, searchValue } = this.state; // const optionsArr = this.getOptionsArray(); // const totalIndex = optionsArr.length; // const { togglePopup, onClick, boxPosition, isPopupReady, value, keyName, idName, preventPopupClose, isSearch } = // this.props; // if (isPopupReady && (keyCode === 38 || keyCode === 40) && e.preventDefault) { // e.preventDefault(); //prevent body scroll // } // if (isPopupReady) { // switch (keyCode) { // case 40: // if (selectedIndex === totalIndex - 1) { // this.setState({ selectedIndex: 0 }); // } else { // if (selectedIndex === totalIndex - 3) { // this.handleGetNextOptions(); // } // this.setState({ // selectedIndex: selectedIndex + 1 // }); // } // break; // case 38: // if (selectedIndex === 0) { // this.setState({ selectedIndex: totalIndex - 1 }); // } else { // this.setState({ // selectedIndex: selectedIndex - 1 // }); // } // break; // case 13: { // const selectedId = optionsArr[selectedIndex][idName] || ''; // onClick && onClick(selectedId, optionsArr[selectedIndex]); // if (!preventPopupClose) { // togglePopup(e, boxPosition); // } else if (isSearch) { // this.searchInput.focus({ preventScroll: true }); // } else { // this.hiddenInput.focus({ preventScroll: true }); // } // break; // } // } // } else { // if (keyCode === 13 || keyCode === 40) { // togglePopup(e, boxPosition); // } // } // } // getSelectedIndex(optionsArr) { // const { selectedId, idName } = this.props; // if (selectedId) { // for (let i = 0; i < optionsArr.length; i++) { // const indexId = optionsArr[i][idName]; // if (selectedId === indexId) { // this.setState({ // selectedIndex: i // }); // break; // } // } // } else { // this.setState({ // selectedIndex: -1 // }); // } // } onSearchAPI() { const { searchValue } = this.state; const { needSearchFetching, onSearch } = this.props; if (needSearchFetching && onSearch) { onSearch(searchValue); } } handleChange(value, e) { const filteredOptions = this.handleFilterSuggestions(value); this.setState({ searchValue: value, // selectedIndex: -1, options: filteredOptions }, () => { this.onSearchAPI(); }); } onSearchClear() { const filteredOptions = this.handleFilterSuggestions(''); this.setState({ searchValue: '', options: filteredOptions }, () => { this.onSearchAPI(); }); } // handleMouseEnter(id, value, index, e) { // this.setState({ // selectedIndex: index // }); // } handleScroll(e) { let ele = e.target; let isScrollReachedBottom = findScrollEnd(ele); isScrollReachedBottom && this.handleGetNextOptions(); } handleGetNextOptions() { let { isNextOptions, getNextOptions } = this.props; let { searchValue } = this.state; isNextOptions && getNextOptions && this.handleFetchOptions(getNextOptions, searchValue); } handleFetchOptions(APICall) { let searchValue = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; let { isFetchingOptions = false } = this.state; let { _isMounted } = this; if (!isFetchingOptions && APICall) { this.setState({ isFetchingOptions: true }); try { return APICall(searchValue).then(() => { _isMounted && this.setState({ isFetchingOptions: false }); }, () => { _isMounted && this.setState({ isFetchingOptions: false }); }); } catch (e) { _isMounted && this.setState({ isFetchingOptions: false }); } } } responsiveFunc(_ref3) { let { mediaQueryOR } = _ref3; return { tabletMode: mediaQueryOR([{ maxWidth: 700 }]) }; } render() { const { options, selectedIndex } = this.state; const { value, removeClose, boxSize, keyName, idName, title, isSearch, isArrow, placeHolderText, className, right, left, top, bottom, isPopupOpen: isOpen, isPopupActive, needExternalPopupState, isPopupReady, position, getTargetRef, getContainerRef, dataId, dataSelectorId, searchBoxSize, searchEmptyHint, searchErrorText, activeStyle, showOnHover, isDisabled, showIconOnHover, isReadOnly, hoverStyle, isEditable, iconName, iconSize, needTick, dataTitle, isDataLoaded, children, needResponsive, arrowIconPosition, isGroupDropDown, groupOptionsKey, groupNameKey, isToggleStateNeeded, selectedId, isPadding, isNeedEffect, hoverType, palette, getFooter, customProps, needMultiLineText, isAbsolutePositioningNeeded, positionsOffset, targetOffset, isRestrictScroll, customClass } = this.props; let { ToggleDropDownProps = {}, DropBoxProps = {}, TextBoxIconProps = {}, ListItemWithIconProps = {}, ListItemProps = {} } = customProps; let { customDropBox = '', customListBox = '' } = customClass; const isPopupOpen = needExternalPopupState ? isPopupActive && isOpen : isOpen; const Component = isToggleStateNeeded ? children.type : null, componentProps = isToggleStateNeeded ? children.props : null; const { searchValue, isFetchingOptions } = this.state; const commonClass = `${className ? className : ''} ${isPopupReady ? activeStyle ? activeStyle : '' : ''} ${isDisabled ? CssProvider('isDisable') : isReadOnly || !isEditable ? style.cursorDefault : !showOnHover ? `${style.cursor} ${hoverStyle ? hoverStyle : ''}` : `${hoverStyle ? hoverStyle : ''} ${style.cursorDefault}`}`; let listIndex = -1; const ariaTitleId = this.getAriaId(); // const allyOptionsArr = this.getOptionsArray(); return /*#__PURE__*/React.createElement("div", { className: style.wrapper, onMouseEnter: showOnHover && !isDisabled && !isReadOnly && isEditable ? this.showTogglePopup : undefined, onMouseLeave: showOnHover && !isDisabled && !isReadOnly && isEditable ? this.hideTogglePopup : undefined, "data-selector-id": dataSelectorId, ...ToggleDropDownProps }, /*#__PURE__*/React.createElement(Container, { alignBox: "row", onClick: !showOnHover && !isDisabled && !isReadOnly && isEditable && this.handleTogglePopup, eleRef: getTargetRef, align: "vertical", isCover: false, dataId: dataId }, children ? isToggleStateNeeded ? /*#__PURE__*/React.createElement(Component, { ...componentProps, isActive: isPopupOpen }) : children : /*#__PURE__*/React.createElement(RippleEffect, { hoverType: hoverType, isActive: isPopupOpen, isNeedEffect: isEditable && isNeedEffect }, /*#__PURE__*/React.createElement(Container, { className: `${btnStyle.buttonReset} ${commonClass} ${!isPopupOpen && showIconOnHover ? style.hoverIcon : ''}`, isCover: false, alignBox: "row", align: "vertical", tagName: "button", "aria-labelledby": ariaTitleId, "aria-haspopup": true, "aria-expanded": isPopupOpen ? true : false }, iconName ? /*#__PURE__*/React.createElement(Box, { className: value ? style.iconBox : '' }, /*#__PURE__*/React.createElement(Icon, { name: iconName, size: iconSize, dataId: `${dataId}_icon` })) : null, value && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Box, { id: ariaTitleId, "aria-hidden": true, className: `${style.value} toggleDropText`, shrink: true, tagName: "span", "data-title": dataTitle, dataId: `${dataId}_value` }, value), isEditable ? /*#__PURE__*/React.createElement(Icon, { "aria-hidden": true, size: "6", name: "ZD-down", iconClass: `${'toggleDropIcon'} ${style.arrow} ${style[`${arrowIconPosition}_arrow`]}`, dataId: "statusdownarrow" }) : null)))), isPopupOpen ? /*#__PURE__*/React.createElement(ResponsiveReceiver, { query: this.responsiveFunc, responsiveId: "Helmet" }, _ref4 => { let { tabletMode } = _ref4; return /*#__PURE__*/React.createElement(ResponsiveDropBox, { boxPosition: position, isActive: isPopupReady, onClick: removeClose, size: boxSize, right: right, left: left, top: top, bottom: bottom, isArrow: isArrow, isAnimate: true, getRef: getContainerRef, customClass: { customDropBoxWrap: style.dropBoxContainer, customDropBox: customDropBox }, needResponsive: needResponsive, isPadding: isPadding, tabindex: "0", a11y: { role: !isSearch ? 'menu' : undefined, ariaLabelledby: !isSearch ? ariaTitleId : undefined }, palette: palette, isResponsivePadding: true, needFocusScope: true, isAbsolutePositioningNeeded: isAbsolutePositioningNeeded, positionsOffset: positionsOffset, targetOffset: targetOffset, isRestrictScroll: isRestrictScroll, customProps: { focusScopeProps: { loadNextOptions: this.handleGetNextOptions, searchValue: searchValue, isFetchingOptions: isFetchingOptions } }, dataId: `${dataId}_dropbox`, ...DropBoxProps, onClose: this.handleTogglePopup }, /*#__PURE__*/React.createElement(React.Fragment, null, isSearch ? /*#__PURE__*/React.createElement(Box, { className: style.search }, /*#__PURE__*/React.createElement(TextBoxIcon, { placeHolder: placeHolderText, onChange: this.handleChange, value: searchValue, onClear: this.onSearchClear, size: searchBoxSize // inputRef={this.searchInputRef} , customProps: { TextBoxProps: { 'data-a11y-autofocus': true } } //search // onKeyDown={this.handleKeyDown} , dataId: `${dataId}_search`, a11y: { role: 'combobox', ariaOwns: ariaTitleId, // ariaActivedescendant: allyOptionsArr[selectedIndex] && allyOptionsArr[selectedIndex][keyName], ariaAutocomplete: 'list', ariaHaspopup: true, ariaExpanded: true }, ...TextBoxIconProps })) : null, title && options.length != 0 && /*#__PURE__*/React.createElement(Box, { className: style.title }, /*#__PURE__*/React.createElement(DropDownHeading, { text: title, htmlId: ariaTitleId, palette: palette, a11y: { role: 'heading' }, customClass: style.dropdown })), /*#__PURE__*/React.createElement(Box, { id: ariaTitleId, flexible: true, shrink: true, scroll: "vertical", preventParentScroll: "vertical", dataId: `${dataId}_list`, className: `${tabletMode ? style.responsivemaxHgt : style.maxHgt} ${customListBox}`, eleRef: this.scrollContentRef, onScroll: this.handleScroll, role: isSearch ? 'listbox' : 'menu', "aria-labelledby": isSearch ? ariaTitleId : undefined, "data-scroll": "true" }, isDataLoaded ? options && options.length != 0 ? isGroupDropDown ? /*#__PURE__*/React.createElement(React.Fragment, null, options.map(group => { const groupName = group[groupNameKey]; const groupOptions = group[groupOptionsKey]; const { needDivider } = group; return /*#__PURE__*/React.createElement(Fragment, { key: `index${groupName}` }, needDivider && /*#__PURE__*/React.createElement("div", { className: style.seperatedLine }), groupName && /*#__PURE__*/React.createElement("div", { className: style.groupName }, /*#__PURE__*/React.createElement(DropDownHeading, { text: groupName, palette: palette, a11y: { role: 'heading' } })), groupOptions && groupOptions.map(item => { const { iconName, iconSize, iconClass, title, disableTitle = '', isDisabled = false, secondaryValue } = item; listIndex += 1; return iconName ? /*#__PURE__*/React.createElement(ListItemWithIcon, { key: listIndex, dataId: item[keyName], value: item[keyName], id: item[idName], active: selectedId === item[idName], onClick: this.onSelect.bind(this, item), index: listIndex // highlight={selectedIndex === listIndex} , disableTitle: disableTitle, isDisabled: isDisabled, iconName: iconName, iconClass: iconClass, iconSize: iconSize, needTick: needTick, needBorder: false // onMouseEnter={this.handleMouseEnter} , getRef: this.itemRef, title: title ? title : item[keyName], palette: palette, needMultiLineText: needMultiLineText, autoHover: true, a11y: { role: isSearch ? 'option' : 'menuitem', ariaSelected: selectedId === item[idName], ariaLabel: item[keyName] }, secondaryValue: secondaryValue, ...ListItemWithIconProps }) : /*#__PURE__*/React.createElement(ListItem, { key: listIndex, dataId: item[keyName], value: item[keyName], id: item[idName], active: selectedId === item[idName], onClick: this.onSelect.bind(this, item), isDisabled: isDisabled, disableTitle: disableTitle, index: listIndex // highlight={selectedIndex === listIndex} , needTick: needTick, needBorder: false // onMouseEnter={this.handleMouseEnter} , getRef: this.itemRef, title: title ? title : item[keyName], palette: palette, needMultiLineText: needMultiLineText, autoHover: true, a11y: { role: isSearch ? 'option' : 'menuitem', ariaSelected: selectedId === item[idName], ariaLabel: item[keyName] }, secondaryValue: secondaryValue, ...ListItemProps }); })); }), isFetchingOptions && /*#__PURE__*/React.createElement(Container, { isCover: false, align: "both" }, /*#__PURE__*/React.createElement(Loader, null))) : /*#__PURE__*/React.createElement(React.Fragment, null, options.map((item, i) => { const { iconName, iconSize, iconClass, title, needDivider, isDisabled = false, disableTitle = '', secondaryValue } = item; if (!needDivider) { listIndex += 1; } return /*#__PURE__*/React.createElement(Fragment, { key: i }, needDivider ? /*#__PURE__*/React.createElement("div", { className: style.seperatedLine }) : iconName ? /*#__PURE__*/React.createElement(ListItemWithIcon, { dataId: item[keyName], value: item[keyName], id: item[idName], active: selectedId === item[idName], onClick: this.onSelect.bind(this, item), index: listIndex, disableTitle: disableTitle, isDisabled: isDisabled // highlight={selectedIndex === listIndex} , iconName: iconName, iconClass: iconClass, iconSize: iconSize, needTick: needTick, needBorder: false // onMouseEnter={this.handleMouseEnter} , getRef: this.itemRef, title: title ? title : item[keyName], key: listIndex, palette: palette, needMultiLineText: needMultiLineText, autoHover: true, a11y: { role: isSearch ? 'option' : 'menuitem', ariaSelected: selectedId === item[idName], ariaLabel: item[keyName] }, secondaryValue: secondaryValue, ...ListItemWithIconProps }) : /*#__PURE__*/React.createElement(ListItem, { key: listIndex, dataId: item[keyName], value: item[keyName], id: item[idName], disableTitle: disableTitle, isDisabled: isDisabled, active: selectedId === item[idName], onClick: this.onSelect.bind(this, item), index: listIndex // highlight={selectedIndex === listIndex} , needTick: needTick, needBorder: false // onMouseEnter={this.handleMouseEnter} , getRef: this.itemRef, title: title ? title : item[keyName], palette: palette, needMultiLineText: needMultiLineText, autoHover: true, a11y: { role: isSearch ? 'option' : 'menuitem', ariaSelected: selectedId === item[idName], ariaLabel: item[keyName] }, secondaryValue: secondaryValue, ...ListItemProps })); }), isFetchingOptions && /*#__PURE__*/React.createElement(Container, { isCover: false, align: "both" }, /*#__PURE__*/React.createElement(Loader, null))) : /*#__PURE__*/React.createElement(CommonEmptyState, { className: style.svgWrapper, description: searchEmptyHint, title: searchErrorText || 'No results', size: "small", getEmptyState: this.emptySearchSVG }) : /*#__PURE__*/React.createElement("div", { className: style.loader }, /*#__PURE__*/React.createElement(Loader, null))), getFooter ? /*#__PURE__*/React.createElement(Box, null, getFooter()) : null)); }) : null); } } ToggleDropDown.defaultProps = defaultProps; ToggleDropDown.propTypes = propTypes; const ToggleDropDown_Component = Popup(ToggleDropDown); ToggleDropDown_Component.defaultProps = ToggleDropDown.defaultProps; ToggleDropDown_Component.propTypes = ToggleDropDown.propTypes; // if (__DOCS__) { // ToggleDropDown.docs = { // componentGroup: 'Molecule' // }; // } export default ToggleDropDown_Component;