react-expanding-textarea
Version:
React textarea component to automatically expand and contract your textareas
87 lines (86 loc) • 3.78 kB
JavaScript
import { __assign, __rest } from "tslib";
import React, { useCallback, useLayoutEffect, useMemo, useRef, } from 'react';
import withForwardedRef from 'react-with-forwarded-ref';
import { equal as isShallowEqual } from 'fast-shallow-equal';
export var getHeight = function (rows, el) {
var _a = window.getComputedStyle(el), borderBottomWidth = _a.borderBottomWidth, borderTopWidth = _a.borderTopWidth, fontSize = _a.fontSize, lineHeight = _a.lineHeight, paddingBottom = _a.paddingBottom, paddingTop = _a.paddingTop;
var lh = lineHeight === 'normal'
? parseFloat(fontSize) * 1.2
: parseFloat(lineHeight);
var rowHeight = rows === 0
? 0
: lh * rows +
parseFloat(borderBottomWidth) +
parseFloat(borderTopWidth) +
parseFloat(paddingBottom) +
parseFloat(paddingTop);
var scrollHeight = el.scrollHeight +
parseFloat(borderBottomWidth) +
parseFloat(borderTopWidth);
return Math.max(rowHeight, scrollHeight);
};
export var resize = function (rows, el) {
if (el) {
var overflowY = 'hidden';
var maxHeight = window.getComputedStyle(el).maxHeight;
if (maxHeight !== 'none') {
var maxHeightN = parseFloat(maxHeight);
if (maxHeightN < el.scrollHeight) {
overflowY = '';
}
}
el.style.height = '0';
el.style.overflowY = overflowY;
el.style.height = "".concat(getHeight(rows, el), "px");
}
};
// =============================================================================
var useShallowObjectMemo = function (obj) {
var refObject = useRef(obj);
var refCounter = useRef(0);
if (!isShallowEqual(obj, refObject.current)) {
refObject.current = obj;
refCounter.current += 1;
}
// eslint-disable-next-line react-hooks/exhaustive-deps
return useMemo(function () { return refObject.current; }, [refCounter.current]);
};
// =============================================================================
var useSSRLayoutEffect = typeof window === 'undefined' ? Function.prototype : useLayoutEffect;
var ExpandingTextarea = function (_a) {
var forwardedRef = _a.forwardedRef, props = __rest(_a, ["forwardedRef"]);
var isForwardedRefFn = typeof forwardedRef === 'function';
var style = useShallowObjectMemo(props.style);
var internalRef = useRef();
var ref = (isForwardedRefFn || !forwardedRef ? internalRef : forwardedRef);
var rows = props.rows ? parseInt('' + props.rows, 10) : 0;
var onChange = props.onChange, onInput = props.onInput, rest = __rest(props, ["onChange", "onInput"]);
useSSRLayoutEffect(function () {
resize(rows, ref.current);
}, [props.className, props.value, ref, rows, style]);
useSSRLayoutEffect(function () {
if (!window.ResizeObserver) {
return;
}
var observer = new ResizeObserver(function () {
resize(rows, ref.current);
});
observer.observe(ref.current);
return function () {
observer.disconnect();
};
}, [ref, rows]);
var handleInput = useCallback(function (e) {
onChange === null || onChange === void 0 ? void 0 : onChange(e);
onInput === null || onInput === void 0 ? void 0 : onInput(e);
resize(rows, ref.current);
}, [onChange, onInput, ref, rows]);
var handleRef = useCallback(function (node) {
ref.current = node;
if (isForwardedRefFn) {
forwardedRef(node);
}
}, [forwardedRef, isForwardedRefFn, ref]);
return (React.createElement("textarea", __assign({}, rest, { onInput: handleInput, ref: handleRef, rows: rows })));
};
export default withForwardedRef(ExpandingTextarea);