UNPKG

@yandex/ui

Version:

Yandex UI components

90 lines (89 loc) 5.63 kB
import { __assign, __extends } from "tslib"; import React, { PureComponent, createRef } from 'react'; import { mergeAllRefs } from '../../lib/mergeRefs'; import { throttle } from '../../lib/throttle'; import { getDisplayName } from '../../lib/getDisplayName'; import { cnTextarea } from '../Textarea'; import './Textarea_autoResize.css'; /** * Модификатор который увеличивает размер контрола при наборе текста. */ export function withAutoResize(WrappedComponent) { var WithAutoResize = /** @class */ (function (_super) { __extends(WithAutoResize, _super); function WithAutoResize() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.wrapRef = createRef(); _this.controlRef = createRef(); _this.initialHeight = 0; _this.newHeight = 0; _this.onResize = throttle(_this.updateHeight).bind(_this); return _this; } WithAutoResize.prototype.componentDidMount = function () { this.saveInitialHeight(); this.updateHeight(); window.addEventListener('resize', this.onResize); }; WithAutoResize.prototype.componentDidUpdate = function (prevProps) { // Если компонент скрыт на момент componentDidMount, то минимальная высота будет вычислена неверно (= 0). // Используем активацию фокуса как признак того, что компонент был показан пользователю. if (this.props.focused && this.props.focused !== prevProps.focused) { this.recalcInitialHeight(); } this.updateHeight(); }; WithAutoResize.prototype.componentWillUnmount = function () { window.removeEventListener('resize', this.onResize); }; WithAutoResize.prototype.render = function () { var _a = this.props, className = _a.className, _b = _a.controlRef, controlRef = _b === void 0 ? null : _b, _c = _a.wrapRef, wrapRef = _c === void 0 ? null : _c; return (React.createElement(WrappedComponent, __assign({}, this.props, { className: cnTextarea({ autoResize: true }, [className]), controlRef: mergeAllRefs(this.controlRef, controlRef), wrapRef: mergeAllRefs(this.wrapRef, wrapRef) }))); }; /** * Сохраняем минимальную высоту которую нужно будет проставлять при удалении текста. */ WithAutoResize.prototype.saveInitialHeight = function () { if (this.wrapRef.current) { this.initialHeight = this.wrapRef.current.clientHeight; } }; /** * Пересчет минимальной высоты которую нужно будет проставлять при удалении текста. */ WithAutoResize.prototype.recalcInitialHeight = function () { if (this.wrapRef.current) { this.wrapRef.current.style.height = 'auto'; this.saveInitialHeight(); } }; /** * Обновление высоты корневому блоку компонента в зависимости от размера контрола. * Для уменьшении блока при удалении текста нужно вначале выставить его стиль высоты в initialHeight. * Иначе блок с удаленным текстом будет всегда оставаться с тем размером который ему выставили в стилях. * Получается, что scrollHeight вычисляется всегда относительно initialHeight размера контейнера. * Высота контейнера вычисляется относительно контрола учитывая его отступы. */ WithAutoResize.prototype.updateHeight = function () { if (this.wrapRef && this.wrapRef.current) { if (this.newHeight > this.initialHeight) { this.wrapRef.current.style.height = this.initialHeight + "px"; this.newHeight = this.initialHeight; } if (this.controlRef && this.controlRef.current) { // Цикл, чтобы перепроверить получившийся элемент, т.к. после его увеличения может появиться скролл // который выдавит текст внутри него и тогда часть текста может быть не видна var i = 2; // Подстраховка while (this.controlRef.current.scrollHeight > this.controlRef.current.offsetHeight && i--) { var padding = this.controlRef.current.offsetHeight - this.controlRef.current.clientHeight; this.newHeight = this.controlRef.current.scrollHeight; this.wrapRef.current.style.height = this.newHeight + padding + "px"; } } } }; WithAutoResize.displayName = "withAutoResize(" + getDisplayName(WrappedComponent) + ")"; return WithAutoResize; }(PureComponent)); return WithAutoResize; }