UNPKG

@yandex/ui

Version:

Yandex UI components

190 lines (189 loc) 10.7 kB
"use strict"; var _a; Object.defineProperty(exports, "__esModule", { value: true }); exports.Select = void 0; var tslib_1 = require("tslib"); var react_1 = tslib_1.__importStar(require("react")); var di_1 = require("@bem-react/di"); var keyboard_1 = require("../lib/keyboard"); var mergeRefs_1 = require("../lib/mergeRefs"); var render_override_1 = require("../lib/render-override"); var usePopper_1 = require("../usePopper"); var desktop_1 = require("./Select.registry/desktop"); var Select_1 = require("./Select"); var withNativeControl_1 = require("./Select.hocs/withNativeControl"); var POPUP_DIRECTIONS = ['bottom-start', 'bottom-end', 'top-start', 'top-end']; var defaultProps = { value: '', }; var SelectPresenter = (_a = /** @class */ (function (_super) { tslib_1.__extends(class_1, _super); function class_1() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.state = { activeDescendant: undefined }; /** * Контейнер с ссылкой на корневой DOM элемент селекта. */ _this.innerRef = react_1.createRef(); /** * Контейнер с ссылкой на корневой DOM элемент select. */ _this.controlRef = react_1.createRef(); /** * Контейнер с ссылкой на DOM элемент триггера. */ _this.triggerRef = react_1.createRef(); /** * Контейнер с ссылкой на DOM элемент попапа. */ _this.popupRef = react_1.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 (keyboard_1.isKeyCode(event.keyCode, [keyboard_1.Keys.ENTER])) { // Делаем поведение как у нативного селекта, // он открывается только по клавишам space, up, down. event.preventDefault(); } if (keyboard_1.isKeyCode(event.keyCode, [keyboard_1.Keys.UP, keyboard_1.Keys.DOWN])) { event.preventDefault(); if (_this.props.setOpened !== undefined) { _this.props.setOpened(true); } } if (keyboard_1.isKeyCode(event.keyCode, [keyboard_1.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 = tslib_1.__rest(_a, ["addonAfter", "maxHeight", "opened", "options", "popupRef", "size", "theme", "unsafe_scope", "value", "view", "renderControl", "renderMenu"]); return (react_1.default.createElement(di_1.ComponentRegistryConsumer, { id: Select_1.cnSelect() }, function (_a) { var Popup = _a.Popup, MenuOriginal = _a.Menu; return (react_1.default.createElement(render_override_1.RenderOverrideProvider, { component: MenuOriginal, render: renderMenu }, function (Menu) { return (react_1.default.createElement(Select_1.Select, tslib_1.__assign({}, props, { triggerRef: _this.triggerRef, activeDescendant: _this.state.activeDescendant, innerRef: mergeRefs_1.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_1.default.createElement(react_1.default.Fragment, null, renderControl && (react_1.default.createElement("select", tslib_1.__assign({}, props, { multiple: Array.isArray(value), tabIndex: -1, value: value, ref: _this.controlRef, style: { display: 'none' } }), options.map(withNativeControl_1.toGroupOptions))), react_1.default.createElement(Popup, { target: "anchor", anchor: _this.triggerRef, className: Select_1.cnSelect('Popup'), direction: POPUP_DIRECTIONS, view: view, theme: theme, visible: opened, innerRef: mergeRefs_1.mergeAllRefs(_this.popupRef, _this.props.popupRef), onClose: _this.onClosePopup, scope: unsafe_scope, modifiers: [ usePopper_1.applyMinWidth, tslib_1.__assign(tslib_1.__assign({}, usePopper_1.applyMaxHeight), { options: { maxHeight: _this.props.maxHeight, } }), ] }, react_1.default.createElement(Menu, { width: "max", className: Select_1.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 () { mergeRefs_1.mergeAllRefs(this.props.triggerRef)(this.triggerRef.current); }; return class_1; }(react_1.PureComponent)), _a.displayName = Select_1.cnSelect() + "@desktop", _a); tslib_1.__exportStar(require("./Select"), exports); exports.Select = di_1.withRegistry(desktop_1.selectRegistry)(SelectPresenter);