UNPKG

@gravity-ui/uikit

Version:

Gravity UI base styling and components

176 lines (175 loc) 10.5 kB
'use client'; "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Select = exports.DEFAULT_RENDER_POPUP = void 0; const tslib_1 = require("tslib"); const jsx_runtime_1 = require("react/jsx-runtime"); const React = tslib_1.__importStar(require("react")); const constants_1 = require("../../constants.js"); const hooks_1 = require("../../hooks/index.js"); const OuterAdditionalContent_1 = require("../controls/common/OuterAdditionalContent/OuterAdditionalContent.js"); const utils_1 = require("../controls/utils.js"); const mobile_1 = require("../mobile/index.js"); const filterDOMProps_1 = require("../utils/filterDOMProps.js"); const components_1 = require("./components/index.js"); const constants_2 = require("./constants.js"); const hooks_2 = require("./hooks/index.js"); const hooks_public_1 = require("./hooks-public/index.js"); const tech_components_1 = require("./tech-components.js"); const utils_2 = require("./utils.js"); require("./Select.css"); const DEFAULT_RENDER_POPUP = ({ renderFilter, renderList }) => { return ((0, jsx_runtime_1.jsxs)(React.Fragment, { children: [renderFilter(), renderList()] })); }; exports.DEFAULT_RENDER_POPUP = DEFAULT_RENDER_POPUP; exports.Select = React.forwardRef(function Select(props, ref) { const { onUpdate, onOpenChange, onFilterChange, renderControl, renderFilter, renderOption, renderOptionGroup, renderSelectedOption, renderEmptyOptions, renderPopup = exports.DEFAULT_RENDER_POPUP, getOptionHeight, getOptionGroupHeight, filterOption, name, form, className, controlClassName, popupClassName, qa, value: propsValue, defaultValue, defaultOpen, open: propsOpen, label, placeholder, filterPlaceholder, width, popupWidth, popupPlacement, error, virtualizationThreshold = constants_2.DEFAULT_VIRTUALIZATION_THRESHOLD, view = 'normal', size = 'm', pin = 'round-round', multiple = false, disabled = false, filterable = false, filter: propsFilter, disablePortal, hasClear = false, onClose, id, hasCounter, renderCounter, title, } = props; const mobile = (0, mobile_1.useMobile)(); const [filter, setFilter] = (0, hooks_1.useControlledState)(propsFilter, '', onFilterChange); // to avoid problem with incorrect popper offset calculation // for example: https://github.com/radix-ui/primitives/issues/1567 const controlWrapRef = React.useRef(null); const controlRef = React.useRef(null); const filterRef = React.useRef(null); const listRef = React.useRef(null); const handleControlRef = (0, hooks_1.useForkRef)(ref, controlRef); const { value, open, toggleOpen, setValue, handleSelection, handleClearValue } = (0, hooks_1.useSelect)({ onUpdate, value: propsValue, defaultValue, defaultOpen, multiple, open: propsOpen, onClose, onOpenChange, disabled, }); React.useEffect(() => { if (!open && filterable && mobile) { // FIXME: add handlers to Sheet like in https://github.com/gravity-ui/uikit/issues/1354 setTimeout(() => { setFilter(''); }, 300); } }, [open, filterable, setFilter, mobile]); const propsOptions = props.options || (0, utils_2.getOptionsFromChildren)(props.children); const options = (0, hooks_public_1.useSelectOptions)({ options: propsOptions, filter, filterable, filterOption, }); const filteredOptions = (0, hooks_public_1.getSelectFilteredOptions)(options); const selectedOptionsContent = (0, utils_2.getSelectedOptionsContent)(options, value, renderSelectedOption); const virtualized = filteredOptions.length >= virtualizationThreshold; const { errorMessage, errorPlacement, validationState } = (0, utils_1.errorPropsMapper)({ error, errorMessage: props.errorMessage, errorPlacement: props.errorPlacement || 'outside', validationState: props.validationState, }); const errorMessageId = (0, hooks_1.useUniqId)(); const isErrorStateVisible = validationState === 'invalid'; const isErrorMsgVisible = isErrorStateVisible && Boolean(errorMessage) && errorPlacement === 'outside'; const isErrorIconVisible = isErrorStateVisible && Boolean(errorMessage) && errorPlacement === 'inside'; const handleOptionClick = React.useCallback((option) => { if (!option || option?.disabled || 'label' in option) { return; } if (multiple) { const activeItemIndex = listRef?.current?.getActiveItem(); if (!mobile) { filterRef.current?.focus(); } if (typeof activeItemIndex === 'number') { // prevent item deactivation in case of multiple selection // https://github.com/gravity-ui/uikit/blob/main/src/components/List/List.tsx#L369 // Will fixed after https://github.com/gravity-ui/uikit/issues/385 setTimeout(() => { listRef?.current?.activateItem(activeItemIndex, true); }, 50); } } handleSelection(option); }, [handleSelection, mobile, multiple]); const handleControlKeyDown = React.useCallback((e) => { // prevent dialog closing in case of item selection by Enter/Spacebar keydown if ([constants_1.KeyCode.ENTER, constants_1.KeyCode.SPACEBAR].includes(e.key) && open) { e.preventDefault(); if (e.key === constants_1.KeyCode.SPACEBAR) { handleOptionClick((0, utils_2.getActiveItem)(listRef)); } } if ([constants_1.KeyCode.ARROW_DOWN, constants_1.KeyCode.ARROW_UP].includes(e.key) && !open) { e.preventDefault(); toggleOpen(); } listRef?.current?.onKeyDown(e); }, [handleOptionClick, open, toggleOpen]); const handleFilterKeyDown = React.useCallback((e) => { listRef?.current?.onKeyDown(e); }, []); const handleQuickSearchChange = React.useCallback((search) => { if (search) { const itemIndex = (0, utils_2.findItemIndexByQuickSearch)(search, (0, utils_2.getListItems)(listRef)); if (typeof itemIndex === 'number' && itemIndex !== -1) { listRef?.current?.activateItem(itemIndex, true); } } }, []); (0, hooks_2.useQuickSearch)({ onChange: handleQuickSearchChange, open, disabled: filterable, }); const mods = { ...(width === 'max' && { width }), }; const inlineStyles = {}; if (typeof width === 'number') { inlineStyles.width = width; } const handleClose = React.useCallback(() => toggleOpen(false), [toggleOpen]); const { onFocus, onBlur } = props; const { focusWithinProps } = (0, hooks_1.useFocusWithin)({ onFocusWithin: onFocus, onBlurWithin: React.useCallback((e) => { onBlur?.(e); if (!mobile) { handleClose(); } }, [handleClose, mobile, onBlur]), }); const uniqId = (0, hooks_1.useUniqId)(); const selectId = id ?? uniqId; const popupId = `select-popup-${selectId}`; const [activeIndex, setActiveIndex] = (0, hooks_2.useActiveItemIndex)({ options: filteredOptions, open, value, }); const _renderFilter = () => { if (filterable) { return ((0, jsx_runtime_1.jsx)(components_1.SelectFilter, { ref: filterRef, size: size, value: filter, placeholder: filterPlaceholder, onChange: setFilter, onKeyDown: handleFilterKeyDown, renderFilter: renderFilter, popupId: popupId, activeIndex: activeIndex })); } return null; }; const _renderList = () => { if (filteredOptions.length || props.loading) { return ((0, jsx_runtime_1.jsx)(components_1.SelectList, { ref: listRef, size: size, value: value, mobile: mobile, flattenOptions: filteredOptions, multiple: multiple, virtualized: virtualized, onOptionClick: handleOptionClick, renderOption: renderOption, renderOptionGroup: renderOptionGroup, getOptionHeight: getOptionHeight, getOptionGroupHeight: getOptionGroupHeight, loading: props.loading, onLoadMore: props.onLoadMore, id: popupId, activeIndex: activeIndex, onChangeActive: setActiveIndex })); } return (0, jsx_runtime_1.jsx)(components_1.EmptyOptions, { filter: filter, renderEmptyOptions: renderEmptyOptions }); }; return ((0, jsx_runtime_1.jsxs)("div", { ref: controlWrapRef, className: (0, constants_2.selectBlock)(mods, className), ...focusWithinProps, style: inlineStyles, tabIndex: -1, children: [(0, jsx_runtime_1.jsx)(components_1.SelectControl, { ...(0, filterDOMProps_1.filterDOMProps)(props, { labelable: true }), toggleOpen: toggleOpen, hasClear: hasClear, clearValue: handleClearValue, ref: handleControlRef, className: controlClassName, qa: qa, view: view, size: size, pin: pin, label: label, placeholder: placeholder, selectedOptionsContent: selectedOptionsContent, isErrorVisible: isErrorStateVisible, errorMessage: isErrorIconVisible ? errorMessage : undefined, open: open, disabled: disabled, onKeyDown: handleControlKeyDown, renderControl: renderControl, value: value, popupId: popupId, selectId: selectId, activeIndex: activeIndex, hasCounter: multiple && hasCounter, renderCounter: renderCounter, title: title }), (0, jsx_runtime_1.jsx)(components_1.SelectPopup, { ref: controlWrapRef, className: popupClassName, controlRef: controlRef, width: popupWidth, open: open, handleClose: handleClose, disablePortal: disablePortal, virtualized: virtualized, mobile: mobile, placement: popupPlacement, onAfterOpen: filterable ? () => { filterRef.current?.focus(); } : undefined, onAfterClose: filterable ? () => { setFilter(''); } : undefined, children: renderPopup({ renderFilter: _renderFilter, renderList: _renderList }) }), (0, jsx_runtime_1.jsx)(OuterAdditionalContent_1.OuterAdditionalContent, { errorMessage: isErrorMsgVisible ? errorMessage : null, errorMessageId: errorMessageId }), (0, jsx_runtime_1.jsx)(components_1.HiddenSelect, { name: name, value: value, disabled: disabled, form: form, onReset: setValue })] })); }); exports.Select.Option = tech_components_1.Option; exports.Select.OptionGroup = tech_components_1.OptionGroup; //# sourceMappingURL=Select.js.map