@i-novus/ui-core
Version:
Базовые UI компоненты
81 lines (80 loc) • 5.46 kB
JavaScript
import { jsx as _jsx } from "react/jsx-runtime";
import { forwardRef, useCallback, useEffect, useMemo, useRef, useState, } from 'react';
import classNames from 'classnames';
import { Popover } from '../../data-display/Popover';
import { Input } from '../Input';
import { FocusTrap, useForwardedRef } from '../../core';
import { InputSuffixIcon } from './components/InputSuffixIcon';
const REACT_TOOLTIP_PLACEMENT_CLASS_PREFIX = 'react-tooltip__place-';
const INITIAL_POPOVER_PLACEMENT = 'bottom';
// TODO: Вероятно стоит вынести часть логики в хуки. Сейчас все в кучу свалено
export const DropdownInput = forwardRef((props, ref) => {
const { className, prefixCls, dropdownInnerComponentProps, dropdownInnerComponent: DropdownInnerComponent, onBlur, onFocus, onOpen, onClose, style, readOnly = false, disabled = false, loading = false, error = false, clearIcon = true, type, value, disableInput = false, getContainer, onChange, onKeyDown, ...inputProps } = props;
const [isDropdownOpened, setDropdownOpened] = useState(false);
const [observedPopoverPlacement, setObservedPopoverPlacement] = useState(INITIAL_POPOVER_PLACEMENT);
const InputRef = useForwardedRef(ref);
const returnFocusElementRef = useRef(null);
const dropdownComponentRef = useRef(null);
const popoverRef = useRef(null);
const focusEventRef = useRef();
const dropdownPrefix = `${prefixCls}-dropdown`;
const dropdownClasses = useMemo(() => classNames(className, dropdownPrefix, `${dropdownPrefix}_place_${observedPopoverPlacement}`, {
[`${dropdownPrefix}_opened`]: isDropdownOpened,
}), [className, dropdownPrefix, isDropdownOpened, observedPopoverPlacement]);
const popoverClasses = useMemo(() => classNames(`${dropdownPrefix}-popover`, `${dropdownPrefix}-popover_place_${observedPopoverPlacement}`, {
[`${dropdownPrefix}-popover_opened`]: isDropdownOpened,
}), [dropdownPrefix, isDropdownOpened, observedPopoverPlacement]);
const saveEvent = useCallback((event) => {
focusEventRef.current = event;
}, []);
const handleReset = useCallback(() => {
onChange('');
}, [onChange]);
const close = useCallback(() => {
setDropdownOpened(false);
}, []);
const onOpenDropdown = useCallback(() => {
onFocus?.(focusEventRef.current);
onOpen?.(focusEventRef.current);
}, [onFocus, onOpen]);
const onCloseDropdown = useCallback(() => {
onClose?.();
onBlur?.(focusEventRef.current);
}, [onBlur, onClose]);
const handleInput = useCallback(() => {
setDropdownOpened(true);
}, []);
useEffect(() => {
const element = popoverRef.current;
if (!element) {
return undefined;
}
const onPopoverReplace = (mutationRecords) => {
const record = mutationRecords[0];
const classes = Array.from((record?.target).classList);
const popoverPlacement = classes
.find(className => className.startsWith(REACT_TOOLTIP_PLACEMENT_CLASS_PREFIX))?.replace(REACT_TOOLTIP_PLACEMENT_CLASS_PREFIX, '');
if (popoverPlacement) {
setObservedPopoverPlacement(popoverPlacement);
}
};
const popoverObserver = new MutationObserver(onPopoverReplace);
popoverObserver.observe(element, {
attributes: true,
attributeFilter: ['class'],
});
return () => {
popoverObserver.disconnect();
};
/** Необходимо вызвать хук,как только меняется реф, что создать подписку на MutationObserver */
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [popoverRef.current]);
return (_jsx("div", { className: dropdownClasses, ref: dropdownComponentRef, style: style, children: _jsx(Popover, { closeOnEsc: true, getContainer: getContainer, returnFocusRef: returnFocusElementRef, ref: popoverRef, afterShow: onOpenDropdown, afterHide: onCloseDropdown, style: {
width: dropdownComponentRef.current?.offsetWidth,
minWidth: dropdownComponentRef.current?.offsetWidth,
maxWidth: dropdownComponentRef.current?.offsetWidth,
}, disabled: disabled, readOnly: readOnly, prefix: prefixCls, open: isDropdownOpened, onOpenChange: setDropdownOpened, placement: INITIAL_POPOVER_PLACEMENT, className: popoverClasses, offset: 2, components: {
Content: (_jsx(FocusTrap, { children: _jsx(DropdownInnerComponent, { ...dropdownInnerComponentProps, close: close, prefix: dropdownPrefix, value: value }) })),
}, children: _jsx("div", { className: `${dropdownPrefix}__input`, children: _jsx(Input, { ...inputProps, onReset: handleReset, onChange: disableInput ? undefined : onChange, onKeyDown: disableInput ? undefined : onKeyDown, onInput: disableInput ? undefined : handleInput, value: value, ref: InputRef, prefix: prefixCls, disabled: disabled || readOnly, readOnly: readOnly, error: error, onClick: saveEvent, type: type, suffixIcon: (_jsx(InputSuffixIcon, { ref: returnFocusElementRef, disabled: disabled || readOnly, error: error, loading: loading, className: `${prefixCls}-dropdown__suffix-icon-button`, onClick: saveEvent })), tabIndex: disableInput ? -1 : 0, clearIcon: clearIcon }) }) }) }));
});
DropdownInput.displayName = 'DropdownInput';