@yandex/ui
Version:
Yandex UI components
106 lines (105 loc) • 5.49 kB
JavaScript
import { __assign, __extends, __rest } from "tslib";
import React, { PureComponent, createRef } from 'react';
import { getDisplayName } from '../lib/getDisplayName';
import { mergeAllRefs } from '../lib/mergeRefs';
var ZINDEX_FACTOR = 1000;
var ZINDEXES_STACK = {};
var defaultProps = {
zIndexGroupLevel: 0,
};
export var withZIndex = function (WrappedComponent) {
var _a;
return _a = /** @class */ (function (_super) {
__extends(WithZIndex, _super);
function WithZIndex() {
var _this = _super !== null && _super.apply(this, arguments) || this;
/**
* Текущий `z-index` компонента
*/
_this.currentZIndex = 0;
/**
* Контейнер с ссылкой на DOM элемент компонента.
*/
_this.innerRef = createRef();
return _this;
}
WithZIndex.prototype.componentDidMount = function () {
if (this.props.visible) {
this.updateZIndexPosition();
}
};
WithZIndex.prototype.componentDidUpdate = function (prevProps) {
if (prevProps.visible !== this.props.visible) {
this.updateZIndexPosition();
}
};
WithZIndex.prototype.componentWillUnmount = function () {
if (this.currentZIndex !== 0) {
this.releaseZIndex(this.props.zIndexGroupLevel, this.currentZIndex);
}
};
WithZIndex.prototype.render = function () {
var _a = this.props, zIndexGroupLevel = _a.zIndexGroupLevel, props = __rest(_a, ["zIndexGroupLevel"]);
return (React.createElement(WrappedComponent, __assign({}, props, { innerRef: mergeAllRefs(this.innerRef, this.props.innerRef) })));
};
/**
* Обновлять стили, отвечающие за позиционирование у DOM элемента компонента.
*/
WithZIndex.prototype.updateZIndexPosition = function () {
var _this = this;
requestAnimationFrame(function () {
var _a = _this.props, visible = _a.visible, zIndexGroupLevel = _a.zIndexGroupLevel;
if (visible && _this.currentZIndex === 0) {
_this.currentZIndex = _this.captureZIndex(zIndexGroupLevel);
}
else if (!visible) {
_this.currentZIndex = _this.releaseZIndex(zIndexGroupLevel, _this.currentZIndex);
}
// имеет смысл обновлять zIndex только у видимого элемента
if (visible) {
// Обновляем стили сразу у DOM узла, а не через setState,
// т.к. это не вызывает лишний раз re-render и повышает производительность.
if (_this.innerRef.current !== null) {
_this.innerRef.current.style.zIndex = String(_this.currentZIndex);
}
else {
// пробуем проставить zIndex в следующем кадре, если в текущем ref еще не существует
_this.updateZIndexPosition();
}
}
});
};
/**
* Занимает наименьший свободный z-index в стеке для своего уровня и возвращает его.
*
* @param zIndexGroupLevel Уровень в стеке для которого необходимо занять `z-index`
*/
WithZIndex.prototype.captureZIndex = function (zIndexGroupLevel) {
var zIndexes = ZINDEXES_STACK[zIndexGroupLevel];
if (zIndexes === undefined) {
zIndexes = [(zIndexGroupLevel + 1) * ZINDEX_FACTOR];
ZINDEXES_STACK[zIndexGroupLevel] = zIndexes;
}
var nextZIndex = zIndexes[zIndexes.length - 1] + 1;
zIndexes.push(nextZIndex);
return nextZIndex;
};
/**
* Освобождает `z-index` в стеке и возвращает нулевую позицию.
*
* @param zIndexGroupLevel Уровень в стеке для которого необходимо освободить `z-index`
* @param currentZIndex Текущий `z-index` компонента
*/
WithZIndex.prototype.releaseZIndex = function (zIndexGroupLevel, currentZIndex) {
var zIndexes = ZINDEXES_STACK[zIndexGroupLevel];
if (zIndexes !== undefined && zIndexes.length > 1) {
zIndexes.splice(zIndexes.indexOf(currentZIndex), 1);
}
return 0;
};
return WithZIndex;
}(PureComponent)),
_a.displayName = "withZIndex(" + getDisplayName(WrappedComponent) + ")",
_a.defaultProps = defaultProps,
_a;
};