@yandex/ui
Version:
Yandex UI components
187 lines (186 loc) • 10.3 kB
JavaScript
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);