UNPKG

@yandex/ui

Version:

Yandex UI components

187 lines (186 loc) 10.3 kB
var _a; import { __assign, __extends, __rest } from "tslib"; import React, { PureComponent, createRef, } from 'react'; import { withRegistry, ComponentRegistryConsumer } from '@bem-react/di'; import { Keys, isKeyCode } from '../lib/keyboard'; import { mergeAllRefs } from '../lib/mergeRefs'; import { RenderOverrideProvider } from '../lib/render-override'; import { applyMaxHeight, applyMinWidth } from '../usePopper'; import { selectRegistry } from './Select.registry/desktop'; import { Select as SelectCommon, cnSelect } from './Select'; import { toGroupOptions } from './Select.hocs/withNativeControl'; var POPUP_DIRECTIONS = ['bottom-start', 'bottom-end', 'top-start', 'top-end']; var defaultProps = { value: '', }; var SelectPresenter = (_a = /** @class */ (function (_super) { __extends(class_1, _super); function class_1() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.state = { activeDescendant: undefined }; /** * Контейнер с ссылкой на корневой DOM элемент селекта. */ _this.innerRef = createRef(); /** * Контейнер с ссылкой на корневой DOM элемент select. */ _this.controlRef = createRef(); /** * Контейнер с ссылкой на DOM элемент триггера. */ _this.triggerRef = createRef(); /** * Контейнер с ссылкой на DOM элемент попапа. */ _this.popupRef = createRef(); /** * Флаг, предотвращающий закрытие меню после клика в скроллбар (нужен исключительно для ie11). * * @see https://st.yandex-team.ru/ISL-7610 */ _this.preventClosable = false; _this.subscribeToEvents = function () { document.addEventListener('mousedown', _this.onDocumentMouseDown); window.addEventListener('resize', _this.onWindowChange); window.addEventListener('orientationchange', _this.onWindowChange); }; _this.unsubscribeFromEvents = function () { document.removeEventListener('mousedown', _this.onDocumentMouseDown); window.removeEventListener('resize', _this.onWindowChange); window.removeEventListener('orientationchange', _this.onWindowChange); }; _this.onDocumentMouseDown = function (event) { var popupNode = _this.popupRef.current; if (popupNode !== null && popupNode.contains(event.target)) { _this.preventClosable = true; } }; _this.onClosePopup = function () { if (_this.props.setOpened !== undefined) { _this.props.setOpened(false); } }; _this.onWindowChange = function () { if (_this.props.setOpened !== undefined) { _this.props.setOpened(false); } }; _this.onBlur = function (event) { if (_this.preventClosable) { _this.preventClosable = false; return; } // Закрыть попап только, если внутри него нет элемента с фокусом. // (Внутри попапа может быть текстовое поле для поиска опций.) requestAnimationFrame(function () { var popupNode = _this.popupRef.current; if (_this.props.setOpened && popupNode && !popupNode.contains(document.activeElement)) { _this.props.setOpened(false); } }); if (_this.props.onBlur !== undefined) { _this.props.onBlur(event); } }; _this.onClick = function (event) { if (_this.props.setOpened !== undefined) { _this.props.setOpened(!_this.props.opened); } if (_this.props.onClick !== undefined) { _this.props.onClick(event); } }; _this.onKeyDown = function (event) { if (isKeyCode(event.keyCode, [Keys.ENTER])) { // Делаем поведение как у нативного селекта, // он открывается только по клавишам space, up, down. event.preventDefault(); } if (isKeyCode(event.keyCode, [Keys.UP, Keys.DOWN])) { event.preventDefault(); if (_this.props.setOpened !== undefined) { _this.props.setOpened(true); } } if (isKeyCode(event.keyCode, [Keys.SPACE])) { if (_this.props.setOpened !== undefined) { event.preventDefault(); if (!_this.props.opened) { _this.props.setOpened(true); } } } if (_this.props.opened && _this.props.onKeyDown !== undefined) { _this.props.onKeyDown(event); } }; _this.onActiveItemChange = function (id) { _this.setState({ activeDescendant: id }); }; _this.onMenuChange = function (event) { // Сюда приходит событие из Menu // Дополняем его знаниями о селекте if (_this.controlRef.current) { var value = event.target.value; event.target = _this.controlRef.current; event.target.value = value; } if (_this.props.onChange !== undefined) { _this.props.onChange(event); } if (_this.props.setOpened !== undefined) { if (!Array.isArray(_this.props.value)) { _this.props.setOpened(false); } } if (_this.preventClosable) { _this.preventClosable = false; } }; return _this; } class_1.prototype.componentDidMount = function () { this.forwardRefs(); }; class_1.prototype.componentWillUnmount = function () { this.unsubscribeFromEvents(); }; class_1.prototype.componentDidUpdate = function (prevProps) { this.forwardRefs(); if (!prevProps.opened && this.props.opened) { this.subscribeToEvents(); } else if (prevProps.opened && !this.props.opened) { this.unsubscribeFromEvents(); } }; class_1.prototype.render = function () { var _this = this; var _a = this.props, addonAfter = _a.addonAfter, maxHeight = _a.maxHeight, opened = _a.opened, options = _a.options, popupRef = _a.popupRef, size = _a.size, theme = _a.theme, _b = _a.unsafe_scope, unsafe_scope = _b === void 0 ? this.innerRef : _b, value = _a.value, view = _a.view, _c = _a.renderControl, renderControl = _c === void 0 ? false : _c, renderMenu = _a.renderMenu, props = __rest(_a, ["addonAfter", "maxHeight", "opened", "options", "popupRef", "size", "theme", "unsafe_scope", "value", "view", "renderControl", "renderMenu"]); return (React.createElement(ComponentRegistryConsumer, { id: cnSelect() }, function (_a) { var Popup = _a.Popup, MenuOriginal = _a.Menu; return (React.createElement(RenderOverrideProvider, { component: MenuOriginal, render: renderMenu }, function (Menu) { return (React.createElement(SelectCommon, __assign({}, props, { triggerRef: _this.triggerRef, activeDescendant: _this.state.activeDescendant, innerRef: mergeAllRefs(_this.innerRef, _this.props.innerRef), onBlur: _this.onBlur, onClick: _this.onClick, onKeyDown: _this.onKeyDown, options: options, size: size, theme: theme, value: value, view: view, opened: opened, addonAfter: React.createElement(React.Fragment, null, renderControl && (React.createElement("select", __assign({}, props, { multiple: Array.isArray(value), tabIndex: -1, value: value, ref: _this.controlRef, style: { display: 'none' } }), options.map(toGroupOptions))), React.createElement(Popup, { target: "anchor", anchor: _this.triggerRef, className: cnSelect('Popup'), direction: POPUP_DIRECTIONS, view: view, theme: theme, visible: opened, innerRef: mergeAllRefs(_this.popupRef, _this.props.popupRef), onClose: _this.onClosePopup, scope: unsafe_scope, modifiers: [ applyMinWidth, __assign(__assign({}, applyMaxHeight), { options: { maxHeight: _this.props.maxHeight, } }), ] }, React.createElement(Menu, { width: "max", className: cnSelect('Menu'), focused: opened, items: options, onChange: _this.onMenuChange, onActiveItemChange: _this.onActiveItemChange, size: size, theme: theme, value: value, view: view })), addonAfter) }))); })); })); }; /** * Копирует ссылки на DOM узлы для дальнейшего использования. */ class_1.prototype.forwardRefs = function () { mergeAllRefs(this.props.triggerRef)(this.triggerRef.current); }; return class_1; }(PureComponent)), _a.displayName = cnSelect() + "@desktop", _a); export * from './Select'; export var Select = withRegistry(selectRegistry)(SelectPresenter);