UNPKG

chayns-components

Version:

A set of beautiful React components for developing chayns® applications.

197 lines (196 loc) 7.22 kB
/* eslint-disable jsx-a11y/click-events-have-key-events,react/forbid-prop-types */ import classNames from 'clsx'; import PropTypes from 'prop-types'; import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react'; import { renderToStaticMarkup } from 'react-dom/server'; import { useTransitionState } from 'react-transition-state'; import Button from '../../react-chayns-button/component/Button'; import Icon from '../../react-chayns-icon/component/Icon'; import TappPortal from '../../react-chayns-tapp_portal/component/TappPortal'; import isDescendant from '../../utils/isDescendant'; import { mapStatusToClass } from '../../utils/mapStatusToClass'; import ComboBoxItem from './ComboBoxItem'; const DialogSelectComboBox = _ref => { var _getItem; let { className, label, list, disabled = false, listValue, listKey, stopPropagation = false, defaultValue, parent, onSelect, style, value = null } = _ref; const [position, setPosition] = useState({ bottom: 0, left: 0, width: 0 }); const [showOverlay, setShowOverlay] = useState(false); const [selected, setSelected] = useState(defaultValue); const [width, setWidth] = useState(null); const mounted = useRef(false); const overlayRef = useRef(null); const buttonRef = useRef(null); const [{ status, isMounted }, toggle] = useTransitionState({ timeout: 200, mountOnEnter: true, unmountOnExit: true, preEnter: true }); useEffect(() => { toggle(!!(position && showOverlay)); }, [position, showOverlay, toggle]); const onHide = useCallback(e => { if (!(isDescendant(overlayRef.current, e.target) || buttonRef.current === e.target)) { setShowOverlay(false); } }, []); const getItem = useCallback(key => { if (key === null || key === undefined) { return list[0]; } return list.find(item => String(item[listKey]) === String(key)); }, [list, listKey]); useLayoutEffect(() => { const resize = () => { let max = 0; const clonedButton = buttonRef.current.cloneNode(true); clonedButton.style.position = 'absolute'; clonedButton.style.visibility = 'hidden'; clonedButton.style.width = ''; clonedButton.style.maxWidth = ''; buttonRef.current.parentElement.insertBefore(clonedButton, buttonRef.current); max = Math.max(max, clonedButton.getBoundingClientRect().width); list.forEach(item => { clonedButton.children[0].innerHTML = /*#__PURE__*/React.isValidElement(item[listValue]) ? renderToStaticMarkup(item[listValue]) : item[listValue]; max = Math.max(max, clonedButton.getBoundingClientRect().width); }); buttonRef.current.parentElement.removeChild(clonedButton); setWidth(max); }; // check if font awesome is loaded and resize again // required cause calculated width might be wrong before font is loaded if (!document.fonts.check('15px "Font Awesome 7 Pro"')) { document.fonts.addEventListener('loadingdone', () => { resize(); }); } resize(); }, [list, listValue, selected, label]); useEffect(() => { if (showOverlay) { window.addEventListener('click', onHide); window.addEventListener('blur', onHide); } return () => { window.removeEventListener('click', onHide); window.removeEventListener('blur', onHide); }; }, [showOverlay, onHide]); useEffect(() => { if (!mounted.current) { mounted.current = true; } else { setSelected(value); } }, [value]); const select = useCallback(selection => { setSelected(selection); if (onSelect && list && list.length > 0 && listKey && selection !== null && selection !== undefined) { onSelect(getItem(selection)); } }, [getItem, list, listKey, onSelect]); const onButtonClick = useCallback(e => { if (stopPropagation) { e.stopPropagation(); } if (chayns.env.isMobile) { const items = list.map(item => ({ name: /*#__PURE__*/React.isValidElement(item[listValue]) ? renderToStaticMarkup(item[listValue]) : item[listValue], value: item[listKey], isSelected: item[listKey] === (value !== null ? value : selected) })); chayns.dialog.select({ list: items, buttons: [] }).then(result => { if (result.buttonType === 1 && result.selection && result.selection[0]) { select(result.selection[0].value); } }); } else { const parentElement = parent || document.querySelector('.tapp'); if (parentElement && ['absolute', 'relative'].includes(window.getComputedStyle(parentElement).position)) { const rect = e.target.getBoundingClientRect(); const parentRect = parentElement.getBoundingClientRect(); setPosition({ bottom: rect.bottom - parentRect.top, left: rect.left - parentRect.left, width: rect.width }); } else { setPosition(e.target.getBoundingClientRect()); } setShowOverlay(!showOverlay); } }, [setPosition, setShowOverlay, showOverlay, selected, stopPropagation, list, listKey, listValue, select, value, parent]); return [/*#__PURE__*/React.createElement(Button, { key: "combobox-button", secondary: true, ref: buttonRef, onClick: onButtonClick, className: classNames('cc__combo-box', className, disabled && 'cc__combo-box--disabled'), style: { width, ...style } }, /*#__PURE__*/React.createElement("div", { className: "cc__combo-box__label ellipsis" }, (selected === null || selected === undefined || !getItem(selected)) && value === null && label ? label : ((_getItem = getItem(value !== null ? value : selected)) !== null && _getItem !== void 0 ? _getItem : getItem(selected))[listValue]), /*#__PURE__*/React.createElement(Icon, { className: "cc__combo-box__icon", icon: "fa fa-chevron-down" })), /*#__PURE__*/React.createElement(TappPortal, { parent: parent, key: "combobox-portal" }, isMounted && /*#__PURE__*/React.createElement("div", { ref: overlayRef, className: classNames('cc__combo-box__overlay scrollbar chayns__background-color--101 chayns__border-color--104', className, mapStatusToClass(status, 'fade')), style: { top: position.bottom, left: position.left, width: position.width, ...style } }, list.map(item => /*#__PURE__*/React.createElement(ComboBoxItem, { key: item[listKey], id: item[listKey], value: item[listValue], setShowOverlay: setShowOverlay, onSelect: select }))))]; }; export default DialogSelectComboBox; DialogSelectComboBox.propTypes = { onSelect: PropTypes.func, disabled: PropTypes.bool, label: PropTypes.string, list: PropTypes.array.isRequired, listKey: PropTypes.string.isRequired, listValue: PropTypes.string.isRequired, className: PropTypes.string, defaultValue: PropTypes.string, stopPropagation: PropTypes.bool, parent: PropTypes.oneOfType([PropTypes.func, PropTypes.node]), style: PropTypes.object, value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]) }; //# sourceMappingURL=DialogSelectComboBox.js.map