@yandex/ui
Version:
Yandex UI components
190 lines (189 loc) • 10.7 kB
JavaScript
"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);