UNPKG

@geneui/components

Version:

The Gene UI components library designed for BI tools

528 lines (521 loc) 22.7 kB
import { _ as _extends } from '../_rollupPluginBabelHelpers-e8fb2e5c.js'; import React__default, { forwardRef, useState, useRef, useMemo, useCallback, useEffect } from 'react'; import PropTypes from 'prop-types'; import { c as classnames } from '../index-031ff73c.js'; import { i as inputConfig } from '../configs-00612ce0.js'; import { s as stopEvent, n as noop } from '../index-a0e4e333.js'; import useClickOutside from '../hooks/useClickOutside.js'; import { k as keyDownKeys, a as actionTypes, S as SPACE_HEIGHT } from '../config-1053d64d.js'; import Icon from '../Icon/index.js'; import CustomScrollbar from '../Scrollbar/index.js'; import { T as Tooltip } from '../index-6d7e99cd.js'; import useKeyDown from '../hooks/useKeyDown.js'; import Tag from '../Tag/index.js'; import { s as styleInject } from '../style-inject.es-746bb8ed.js'; import { c as callAfterDelay } from '../callAfterDelay-7272faca.js'; import '../dateValidation-67caec66.js'; import '../_commonjsHelpers-24198af3.js'; import 'react-dom'; import '../tslib.es6-f211516f.js'; import '../hooks/useDeviceType.js'; import '../hooks/useWindowSize.js'; import '../hooks/useDebounce.js'; import '../GeneUIProvider/index.js'; var css_248z$1 = "[data-gene-ui-version=\"2.16.5\"] .tag-wrapper{align-items:center;display:flex;max-width:100%}[data-gene-ui-version=\"2.16.5\"] .tag-wrapper.flex-basis{align-self:center;flex-basis:20px}[data-gene-ui-version=\"2.16.5\"] .tag-wrapper .input{background:#0000;border:none;caret-color:var(--background-sc);color:var(--background-sc);flex:1 1;margin:1px 0;outline:none;padding:0;width:100%}[data-gene-ui-version=\"2.16.5\"] .tag-wrapper span{align-self:flex-start;background:#fff;border:none;color:#000;display:flex;flex:1 1;height:0;outline:none;padding:0;visibility:hidden;white-space:pre}"; styleInject(css_248z$1); /** * Tag Wrapper */ const TagWrapper = /*#__PURE__*/forwardRef((props, ref) => { const { createInputRef, activeRowRef } = ref; const { isValid, label, editMode, index, onChangeHandler, onDeleteHandler, placeholder, hasTags, hasIcon, onKeyPress, activeTagIndex, setEditModeIndex, setActiveTagIndex, withEdit } = props; const [hasTooltip, setHasTooltip] = useState(false); const [inputValue, setInputValue] = useState(''); const [isEditMode, setIsEditMode] = useState(editMode); const inputRef = useRef(null); const tagRef = useRef(null); const isActive = useMemo(() => index === activeTagIndex, [activeTagIndex, index]); const createMode = useMemo(() => index === undefined, [index]); const editModeChangeHandler = useCallback(() => { setIsEditMode(editMode); editMode && callAfterDelay(() => { var _inputRef$current; return inputRef === null || inputRef === void 0 ? void 0 : (_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 ? void 0 : _inputRef$current.focus(); }); }, [editMode, inputRef]); const stopEventHandler = e => stopEvent(e, true); const keyDownHandler = useCallback(e => { switch (e.key) { case keyDownKeys.tab: case keyDownKeys.enter: e.key === keyDownKeys.tab && (isEditMode && !createMode || createMode && inputValue && inputValue.trim().length) && stopEventHandler(e); setEditModeIndex && setEditModeIndex(null); if (inputValue && inputValue.trim().length && isEditMode) { setInputValue(createMode ? '' : inputValue); onChangeHandler({ label: inputValue, index }); !createMode && setIsEditMode(false); } else if (!inputValue && !createMode) { onDeleteHandler(index); setIsEditMode(false); } else if (activeTagIndex !== null && setEditModeIndex) { e.key === keyDownKeys.enter && setEditModeIndex(activeTagIndex); e.key === keyDownKeys.tab && setActiveTagIndex(null); } break; case keyDownKeys.escape: setEditModeIndex && setEditModeIndex(null); setActiveTagIndex && setActiveTagIndex(null); if (!createMode) { setIsEditMode(false); setInputValue(label || ''); } break; case keyDownKeys.arrowRight: case keyDownKeys.backspace: case keyDownKeys.arrowLeft: case keyDownKeys.delete: createMode && !inputValue && withEdit && onKeyPress(e.key); break; } }, [activeTagIndex, createMode, index, inputValue, isEditMode, label, onChangeHandler, onDeleteHandler, onKeyPress, setActiveTagIndex, setEditModeIndex, withEdit]); const dblclickHandler = useCallback(() => { const handleClick = () => { if (createMode || !withEdit || isEditMode) return; setIsEditMode(prev => !prev); setEditModeIndex(index); setActiveTagIndex(null); (createInputRef || inputRef).current.focus(); }; const element = tagRef === null || tagRef === void 0 ? void 0 : tagRef.current; element && element.addEventListener('dblclick', handleClick); return () => { element && element.removeEventListener('dblclick', handleClick); }; }, [createMode, isEditMode, createInputRef, withEdit, setEditModeIndex, index, setActiveTagIndex]); const handleOutsideClick = useClickOutside(event => { if (tagRef && !tagRef.current.contains(event.target)) { if (!createMode && isEditMode) { setIsEditMode(false); if (label !== inputValue) { if (inputValue && inputValue.trim().length) { onChangeHandler({ label: inputValue, index }); } else { onDeleteHandler(index); } } } else if (inputValue && createMode) { inputValue.trim().length && onChangeHandler({ label: inputValue, index }); setInputValue(''); } } }); const handleChangeLabel = useCallback(() => { var _tagRef$current, _tagRef$current2, _tagRef$current2$offs; setHasTooltip((tagRef === null || tagRef === void 0 ? void 0 : (_tagRef$current = tagRef.current) === null || _tagRef$current === void 0 ? void 0 : _tagRef$current.offsetWidth) + 130 > (tagRef === null || tagRef === void 0 ? void 0 : (_tagRef$current2 = tagRef.current) === null || _tagRef$current2 === void 0 ? void 0 : (_tagRef$current2$offs = _tagRef$current2.offsetParent) === null || _tagRef$current2$offs === void 0 ? void 0 : _tagRef$current2$offs.offsetWidth)); }, [setHasTooltip, tagRef]); const inputChangeHandler = useCallback(e => { setInputValue(e.target.value); createMode && setActiveTagIndex(null); }, [createMode, setActiveTagIndex]); useKeyDown(keyDownHandler, [keyDownHandler], createInputRef || inputRef, [keyDownKeys.enter, keyDownKeys.escape, keyDownKeys.arrowLeft, keyDownKeys.arrowRight, keyDownKeys.backspace, keyDownKeys.tab, keyDownKeys.delete]); useEffect(dblclickHandler, [createInputRef, tagRef, inputRef, withEdit, isEditMode]); useEffect(() => label && setInputValue(label), [label]); useEffect(editModeChangeHandler, [editMode, inputRef]); useEffect(handleChangeLabel, [tagRef, label]); return /*#__PURE__*/React__default.createElement("div", { className: classnames('tag-wrapper', { 'flex-basis': hasTags }), ref: tagRef }, /*#__PURE__*/React__default.createElement("div", { ref: handleOutsideClick }, isEditMode ? /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement("input", { ref: createInputRef || inputRef, onChange: inputChangeHandler, onClick: stopEventHandler, placeholder: placeholder, className: "input", value: inputValue }), /*#__PURE__*/React__default.createElement("span", null, inputValue.replaceAll(' ', '-'))) : /*#__PURE__*/React__default.createElement("div", { ref: isActive ? activeRowRef : null }, /*#__PURE__*/React__default.createElement(Tooltip, { position: "auto", title: hasTooltip ? label : '' }, /*#__PURE__*/React__default.createElement(Tag, _extends({}, hasIcon ? { icons: /*#__PURE__*/React__default.createElement(Icon, { type: "bc-icon-clear-small", onClick: () => onDeleteHandler(index) }) } : {}, { appearance: isActive ? 'colored' : 'light' }, !isValid && { color: 'red' }, { cornerRadius: "smooth-radius", onClick: stopEventHandler, name: label })))))); }); TagWrapper.propTypes = { isValid: PropTypes.bool, label: PropTypes.string, editMode: PropTypes.bool, index: PropTypes.number, onChangeHandler: PropTypes.func, onDeleteHandler: PropTypes.func, placeholder: PropTypes.string, hasTags: PropTypes.bool, hasIcon: PropTypes.bool, onKeyPress: PropTypes.func, activeTagIndex: PropTypes.number, setEditModeIndex: PropTypes.func, setActiveTagIndex: PropTypes.func, withEdit: PropTypes.bool }; TagWrapper.defaultProps = { isValid: false, isActive: false }; var css_248z = "[data-gene-ui-version=\"2.16.5\"] .combo-box{--element-height:4.6rem;--element-min-width:25rem;--message-padding-side:2rem;--input-message-padding-side:2rem;--element-border-color:rgba(var(--background-sc-rgb),0.2);height:-webkit-fit-content;height:-moz-fit-content;height:fit-content;max-width:100%;min-width:var(--element-min-width);position:relative;transition:opacity .3s;-webkit-user-select:none;user-select:none;width:-webkit-fit-content;width:-moz-fit-content;width:fit-content}[data-gene-ui-version=\"2.16.5\"] .combo-box.read-only{--element-border-color:rgba(var(--background-sc-rgb),0.05)}[data-gene-ui-version=\"2.16.5\"] .combo-box.filled:not(:focus-within){--element-border-color:rgba(var(--background-sc-rgb),0.3)}[data-gene-ui-version=\"2.16.5\"] .combo-box:not(.read-only):focus-within{--element-border-color:var(--hero)}[data-gene-ui-version=\"2.16.5\"] .combo-box:not(.f-content-size){width:100%}[data-gene-ui-version=\"2.16.5\"] .combo-box.disabled{opacity:.5;pointer-events:none}[data-gene-ui-version=\"2.16.5\"] .combo-box.disabled:not(.read-only):focus-within{--element-border-color:rgba(var(--background-sc-rgb),0.2)}[data-gene-ui-version=\"2.16.5\"] .combo-box.read-only{cursor:inherit}[data-gene-ui-version=\"2.16.5\"] .combo-box .combo-box-container{border:1px solid var(--element-border-color,#0000);border-radius:10px;overflow:hidden;width:100%}[data-gene-ui-version=\"2.16.5\"] .combo-box .combo-box-container .swap-label{display:flex;font:600 1rem/1.8rem var(--font-family);left:0;padding:0 var(--input-message-padding-side);position:absolute;top:-.9rem;width:100%;z-index:5}[data-gene-ui-version=\"2.16.5\"] .combo-box .combo-box-container .swap-label .icon{font-size:1.8rem;transition:none}[data-gene-ui-version=\"2.16.5\"] .combo-box .combo-box-container .swap{max-width:100%;overflow:hidden}[data-gene-ui-version=\"2.16.5\"] .combo-box .combo-box-container .swap-animate{align-items:center;background:var(--background);box-shadow:inset 0 -.8rem 0 var(--input-element-background);color:var(--swap-label-color);display:flex;max-width:100%;padding:0 .4rem;transition:transform .3s,opacity .3s .1s,color .3s}[data-gene-ui-version=\"2.16.5\"] .combo-box .combo-box-container .swap-animate>*+*{margin-inline-start:.5rem}[data-gene-ui-version=\"2.16.5\"] .combo-box .combo-box-container .swap-animate.hide{opacity:0;transform:translateY(100%)}[data-gene-ui-version=\"2.16.5\"] .combo-box .combo-box-container .boxes{display:flex;flex-wrap:wrap;gap:8px;min-height:4.2rem;padding:8px 16px}[data-gene-ui-version=\"2.16.5\"] .information-message{cursor:default;font:600 1.2rem/1.42 var(--font-family);margin:.6rem 0 0;padding:0 var(--input-message-padding-side);width:100%}[data-gene-ui-version=\"2.16.5\"] .information-message:empty{display:none}[data-gene-ui-version=\"2.16.5\"] .input-description{cursor:default;font:600 1.4rem/2rem var(--font-family);margin:1rem 0 0;max-width:100%;opacity:.7}[data-gene-ui-version=\"2.16.5\"] .input-structure~.input-description{padding:0 1rem}[data-gene-ui-version=\"2.16.5\"] .title{align-items:center;cursor:default;display:flex;font:600 1.4rem/2rem var(--font-family);margin:0 0 .4rem;max-width:100%;opacity:.5;padding:0 var(--message-padding-side)}[data-gene-ui-version=\"2.16.5\"] .title>*+*{margin-inline-start:.5rem}"; styleInject(css_248z); /** * Combo box */ function ComboBox(props) { const { infoIconTooltipProps, labelAppearance, withInfoIcon, flexibility, placeholder, withDelete, className, maxHeight, required, tabIndex, onChange, readOnly, withEdit, disabled, label, regEx } = props; const inputLabel = label || placeholder; const asterisk = required ? '*' : ''; const isControlled = 'value' in props && props.value; const [activeTagIndex, setActiveTagIndex] = useState(null); const [editModeIndex, setEditModeIndex] = useState(null); const [values, setValues] = useState([]); const scrollbarRef = useRef(); const activeRowRef = useRef(); const inputRef = useRef(); const checkIsValid = useCallback(value => regEx ? new RegExp(regEx).test(value) : true, [regEx]); const onValueChangeHandler = useCallback(_ref => { let { label, index } = _ref; const valuesClone = [...values]; const isValid = checkIsValid(label); const added = index === undefined; if (added) { valuesClone.push({ label, isValid }); } else { valuesClone[index].label = label; valuesClone[index].isValid = isValid; inputRef.current.focus(); setEditModeIndex(null); } !isControlled && setValues(valuesClone); onChange({ value: valuesClone, additional: { type: added ? actionTypes.add : actionTypes.edit, index: added ? valuesClone.length - 1 : index, isValid, label } }); }, [values, checkIsValid, isControlled, onChange, setEditModeIndex]); const onDeleteHandler = useCallback(index => { const valuesClone = [...values]; const item = valuesClone[index]; valuesClone.splice(index, 1); !isControlled && setValues(valuesClone); !valuesClone.length && setActiveTagIndex(null); onChange({ value: valuesClone, additional: { type: actionTypes.delete, index, ...item } }); inputRef.current && callAfterDelay(() => inputRef.current.focus()); }, [values, isControlled, onChange, inputRef]); const init = useCallback(() => { if (isControlled && Array.isArray(props.value)) { setValues(props.value.map(row => ({ ...row, isValid: checkIsValid(row === null || row === void 0 ? void 0 : row.label) }))); } else if (props.defaultValue && Array.isArray(props.defaultValue)) { setValues(props.defaultValue.map(row => ({ ...row, isValid: checkIsValid(row === null || row === void 0 ? void 0 : row.label) }))); } }, [checkIsValid, isControlled, props.defaultValue, props.value]); const handleOutsideClick = useClickOutside(() => { scrollbarRef.current.scrollTop(0, 0); setActiveTagIndex(null); }); const onClickHandler = useCallback(() => { if (disabled || readOnly) return; inputRef.current && callAfterDelay(() => inputRef.current.focus()); }, [disabled, readOnly, inputRef]); const onKeyPress = useCallback(type => { switch (type) { case keyDownKeys.arrowLeft: if (values.length && activeTagIndex !== 0) { setActiveTagIndex(activeTagIndex === null ? values.length - 1 : activeTagIndex - 1); } break; case keyDownKeys.arrowRight: if (values.length && activeTagIndex !== null && activeTagIndex < values.length - 1) { setActiveTagIndex(activeTagIndex + 1); } break; case keyDownKeys.delete: case keyDownKeys.backspace: if (withDelete) { if (activeTagIndex !== null) { activeTagIndex > 0 && setActiveTagIndex(activeTagIndex - 1); onDeleteHandler(activeTagIndex); } else if (values.length) { onDeleteHandler(values.length - 1); } } break; } }, [activeTagIndex, onDeleteHandler, values.length, withDelete]); const onFocusHandler = useCallback(() => { var _inputRef$current; return editModeIndex === null && !disabled && !readOnly && (inputRef === null || inputRef === void 0 ? void 0 : (_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 ? void 0 : _inputRef$current.focus()); }, [editModeIndex, disabled, readOnly, inputRef]); useEffect(init, [props.defaultValue, props.value]); useEffect(() => { if (activeTagIndex && activeRowRef.current) { const scrollbarRefBounding = scrollbarRef.current.container.getBoundingClientRect(); const activeRowRefBounding = activeRowRef.current.getBoundingClientRect(); if (scrollbarRefBounding.top > activeRowRefBounding.top - SPACE_HEIGHT) { scrollbarRef.current.scrollTop(scrollbarRef.current.viewScrollTop - activeRowRefBounding.height - SPACE_HEIGHT); } else if (activeRowRefBounding.top + activeRowRefBounding.height + SPACE_HEIGHT > scrollbarRefBounding.top + maxHeight) { scrollbarRef.current.scrollTop(scrollbarRef.current.viewScrollTop + activeRowRefBounding.height + SPACE_HEIGHT); } } }, [activeTagIndex, scrollbarRef]); return /*#__PURE__*/React__default.createElement("div", { onFocus: onFocusHandler, ref: handleOutsideClick, tabIndex: tabIndex, className: classnames('combo-box', "f-".concat(flexibility), className, { 'read-only': readOnly, disabled }) }, labelAppearance === inputConfig.labelAppearance[1] && /*#__PURE__*/React__default.createElement("div", { className: "title ellipsis-text" }, /*#__PURE__*/React__default.createElement(Tooltip, _extends({ position: "auto" }, infoIconTooltipProps), /*#__PURE__*/React__default.createElement("span", { className: "ellipsis-text" }, asterisk, " ", inputLabel)), withInfoIcon && /*#__PURE__*/React__default.createElement(Tooltip, _extends({ position: "auto" }, infoIconTooltipProps), /*#__PURE__*/React__default.createElement(Icon, { type: "bc-icon-info" }))), /*#__PURE__*/React__default.createElement("div", { className: "combo-box-container", onClick: onClickHandler }, /*#__PURE__*/React__default.createElement("div", { className: "element-back" }, labelAppearance === inputConfig.labelAppearance[2] && /*#__PURE__*/React__default.createElement("div", { className: "swap-label" }, /*#__PURE__*/React__default.createElement("div", { className: "swap" }, /*#__PURE__*/React__default.createElement("div", { className: classnames('input-swap-animate ellipsis-text', { hide: !values.length }) }, /*#__PURE__*/React__default.createElement(Tooltip, _extends({ position: "auto" }, infoIconTooltipProps), /*#__PURE__*/React__default.createElement("span", { className: "ellipsis-text" }, asterisk, " ", inputLabel)), withInfoIcon && /*#__PURE__*/React__default.createElement(Tooltip, _extends({ position: "auto" }, infoIconTooltipProps), /*#__PURE__*/React__default.createElement(Icon, { type: "bc-icon-info" })))))), /*#__PURE__*/React__default.createElement("div", { className: classnames({ 'pointer-events-none': disabled || readOnly }) }, /*#__PURE__*/React__default.createElement(CustomScrollbar, { autoHeight: true, autoHeightMax: maxHeight, ref: scrollbarRef }, /*#__PURE__*/React__default.createElement("div", { className: "boxes" }, values.map((value, index) => /*#__PURE__*/React__default.createElement(TagWrapper, _extends({ key: "".concat(JSON.stringify(value)).concat(index), onChangeHandler: onValueChangeHandler, setActiveTagIndex: setActiveTagIndex, setEditModeIndex: setEditModeIndex, editMode: editModeIndex === index, hasIcon: !readOnly && withDelete, onDeleteHandler: onDeleteHandler, activeTagIndex: activeTagIndex, activeRowRef: activeRowRef, onKeyPress: onKeyPress, ref: { activeRowRef }, withEdit: withEdit, hasTags: true, index: index }, value))), withEdit && !disabled && !readOnly && /*#__PURE__*/React__default.createElement(TagWrapper, { placeholder: !values.length ? placeholder : '', onChangeHandler: onValueChangeHandler, setActiveTagIndex: setActiveTagIndex, setEditModeIndex: setEditModeIndex, activeTagIndex: activeTagIndex, hasTags: !!values.length, onKeyPress: onKeyPress, withEdit: withEdit, editMode: true, ref: { createInputRef: inputRef } })))))); } ComboBox.propTypes = { /** * Additional className */ className: PropTypes.string, /** * Specify a "label" appearance */ labelAppearance: PropTypes.oneOf(inputConfig.labelAppearance), /** * How to display inscription in relation to it's parent in ExtendedInput */ flexibility: PropTypes.oneOf(inputConfig.flexibility), /** * Define is input required or no. */ required: PropTypes.bool, /** * Define is need input info icon required. */ withInfoIcon: PropTypes.bool, /** * Tooltip props */ infoIconTooltipProps: PropTypes.shape({ ...Tooltip.propTypes }), /** * validation regex */ regEx: PropTypes.string, /** * On change handler */ onChange: PropTypes.func, /** * Use this prop to get specified value when "onChange" is fired */ value: PropTypes.arrayOf(PropTypes.shape({ label: PropTypes.string })), /** * Initial Combobox value. */ defaultValue: PropTypes.arrayOf(PropTypes.shape({ label: PropTypes.string })), /** * Combobox disabled state */ disabled: PropTypes.bool, /** * Combobox readOnly state */ readOnly: PropTypes.bool, /** * Is Combobox in edit mode */ withEdit: PropTypes.bool, /** * Can Delete tags */ withDelete: PropTypes.bool, /** * Max height of Combobox */ maxHeight: PropTypes.number, /** * Since the editor has its own modal windows, there may be a problem with * the zIndex so you can change it using this prop */ tabIndex: PropTypes.number }; // toDo Add validation scheme fot form ComboBox.defaultProps = { labelAppearance: inputConfig.labelAppearance[0], flexibility: inputConfig.flexibility[0], label: '', placeholder: '', onChange: noop, regEx: '', withInfoIcon: false, required: false, disabled: false, readOnly: false, withEdit: true, withDelete: true, maxHeight: 510, tabIndex: 0, infoIconTooltipProps: {} }; ComboBox.displayName = 'ComboBox'; export { ComboBox as default };