UNPKG

@yandex/ui

Version:

Yandex UI components

106 lines (105 loc) 5.49 kB
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; };