@react-md/form
Version:
This package is for creating all the different form input types.
178 lines • 11 kB
JavaScript
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TextArea = void 0;
var jsx_runtime_1 = require("react/jsx-runtime");
var react_1 = require("react");
var classnames_1 = __importDefault(require("classnames"));
var utils_1 = require("@react-md/utils");
var FormThemeProvider_1 = require("../FormThemeProvider");
var FloatingLabel_1 = require("../label/FloatingLabel");
var useFieldStates_1 = require("../useFieldStates");
var TextFieldContainer_1 = require("./TextFieldContainer");
var block = (0, utils_1.bem)("rmd-textarea");
var container = (0, utils_1.bem)("rmd-textarea-container");
var PADDING_VARIABLES = "var(--rmd-form-text-padding-top, 0px) + var(--rmd-form-textarea-padding, 0px)";
// this is the default of 1.5rem line-height in the styles
var DEFAULT_LINE_HEIGHT = "24";
exports.TextArea = (0, react_1.forwardRef)(function TextArea(_a, forwardedRef) {
var style = _a.style, className = _a.className, areaStyle = _a.areaStyle, areaClassName = _a.areaClassName, containerRef = _a.containerRef, containerProps = _a.containerProps, label = _a.label, labelStyle = _a.labelStyle, labelClassName = _a.labelClassName, _b = _a.rows, rows = _b === void 0 ? 2 : _b, _c = _a.maxRows, maxRows = _c === void 0 ? -1 : _c, _d = _a.resize, resize = _d === void 0 ? "auto" : _d, propTheme = _a.theme, _e = _a.dense, dense = _e === void 0 ? false : _e, _f = _a.inline, propInline = _f === void 0 ? false : _f, _g = _a.error, error = _g === void 0 ? false : _g, _h = _a.stretch, stretch = _h === void 0 ? false : _h, _j = _a.disabled, disabled = _j === void 0 ? false : _j, _k = _a.animate, animate = _k === void 0 ? true : _k, _l = _a.isLeftAddon, isLeftAddon = _l === void 0 ? true : _l, _m = _a.isRightAddon, isRightAddon = _m === void 0 ? true : _m, propUnderlineDirection = _a.underlineDirection, propOnBlur = _a.onBlur, propOnFocus = _a.onFocus, propOnChange = _a.onChange, leftChildren = _a.leftChildren, rightChildren = _a.rightChildren, props = __rest(_a, ["style", "className", "areaStyle", "areaClassName", "containerRef", "containerProps", "label", "labelStyle", "labelClassName", "rows", "maxRows", "resize", "theme", "dense", "inline", "error", "stretch", "disabled", "animate", "isLeftAddon", "isRightAddon", "underlineDirection", "onBlur", "onFocus", "onChange", "leftChildren", "rightChildren"]);
var id = props.id, value = props.value, defaultValue = props.defaultValue;
var _o = (0, FormThemeProvider_1.useFormTheme)({
theme: propTheme,
underlineDirection: propUnderlineDirection,
}), theme = _o.theme, underlineDirection = _o.underlineDirection;
var _p = __read((0, react_1.useState)(), 2), height = _p[0], setHeight = _p[1];
(0, react_1.useEffect)(function () {
if (resize !== "auto" && typeof height === "number") {
setHeight(undefined);
}
}, [resize, height]);
var maskRef = (0, react_1.useRef)(null);
var _q = __read((0, react_1.useState)(false), 2), scrollable = _q[0], setScrollable = _q[1];
var updateHeight = (0, react_1.useCallback)(function () {
var mask = maskRef.current;
/* istanbul ignore if */
if (!mask) {
return;
}
var nextHeight = mask.scrollHeight;
/* istanbul ignore if */
if (maxRows > 0) {
var lineHeight = parseFloat(window.getComputedStyle(mask).lineHeight || DEFAULT_LINE_HEIGHT);
var maxHeight = maxRows * lineHeight;
nextHeight = Math.min(maxHeight, nextHeight);
// only want the textarea to be scrollable if there's a limit on the rows
// since it'll flash the scrollbar on most OS during the height transition
if (nextHeight === maxHeight && !scrollable) {
setScrollable(true);
}
else if (nextHeight !== maxHeight && scrollable) {
setScrollable(false);
}
}
if (height !== nextHeight) {
setHeight(nextHeight);
}
}, [height, maxRows, scrollable]);
// the window can be resized while there is text inside the textarea so need to
// recalculate the height when the width changes as well.
var _r = __read((0, utils_1.useResizeObserver)(updateHeight, {
ref: maskRef,
disableHeight: true,
}), 2), maskRefHandler = _r[1];
var _s = (0, useFieldStates_1.useFieldStates)({
onBlur: propOnBlur,
onFocus: propOnFocus,
onChange: function (event) {
var mask = maskRef.current;
if (propOnChange) {
propOnChange(event);
}
/* istanbul ignore if */
if (!mask || resize !== "auto") {
return;
}
// to get the height transition to work, you have to set the height on:
// - the main container element (including padding) that has the height
// transition enabled
// - a child div wrapper (without padding) that has the height transition
// enabled
// - the textarea element (without padding) and without a height transition
//
// if it isn't done this way, the height transition will look weird since
// the text will be fixed to the bottom of the area and more text at the top
// will become visible as the height transition completes. applying the
// transition on the two parent elements work because:
// - the height is set immediately on the text field so it expands to show all
// the text
// - the height is correctly applied to both parent elements, but their height
// haven't fully been adjusted due to the animation
// - the parent divs have overflow visible by default, so the textarea's text
// will expand past the boundaries of the divs and not cause the upwards
// animation weirdness.
mask.value = event.currentTarget.value;
updateHeight();
},
value: value,
defaultValue: defaultValue,
}), valued = _s.valued, focused = _s.focused, onBlur = _s.onBlur, onFocus = _s.onFocus, onChange = _s.onChange;
var _t = __read((0, utils_1.useEnsuredRef)(forwardedRef), 2), ref = _t[0], refHandler = _t[1];
// the container element adds some padding so that the content can scroll and
// not be covered by the floating label. unfortunately, this means that the entire
// container is no longer clickable to focus the input. This is used to add that
// functionality back.
var handleClick = (0, react_1.useCallback)(function (event) {
if (ref.current && event.target === event.currentTarget) {
ref.current.focus();
}
}, [ref]);
var area = ((0, jsx_runtime_1.jsx)("textarea", __assign({}, props, { ref: refHandler, rows: rows, disabled: disabled, onFocus: onFocus, onBlur: onBlur, onChange: onChange, style: __assign(__assign({}, areaStyle), { height: height !== null && height !== void 0 ? height : areaStyle === null || areaStyle === void 0 ? void 0 : areaStyle.height }), className: (0, classnames_1.default)(block({
scrollable: scrollable || resize === "none",
floating: label && theme !== "none",
rh: resize === "horizontal",
rv: resize === "vertical",
rn: resize === "auto" || resize === "none",
}), areaClassName) })));
var children = area;
if (resize === "auto") {
children = ((0, jsx_runtime_1.jsxs)("div", __assign({ style: { height: height }, className: container("inner", { animate: animate }) }, { children: [area, (0, jsx_runtime_1.jsx)("textarea", { "aria-hidden": true, defaultValue: value || defaultValue, id: "".concat(id, "-mask"), ref: maskRefHandler, readOnly: true, rows: rows, tabIndex: -1, style: areaStyle, className: (0, classnames_1.default)(block({
rn: true,
mask: true,
floating: label && theme !== "none",
}), areaClassName) })] })));
}
var inline = propInline;
if (resize === "horizontal" || resize === "both") {
// have to force it inline or else you won't be able to resize
// it horizontally.
inline = true;
}
return ((0, jsx_runtime_1.jsxs)(TextFieldContainer_1.TextFieldContainer, __assign({}, containerProps, { style: __assign(__assign({}, style), { height: height
? "calc(".concat(PADDING_VARIABLES, " + ").concat(height, "px)")
: style === null || style === void 0 ? void 0 : style.height }), className: (0, classnames_1.default)(container({
animate: animate && resize === "auto",
cursor: !disabled,
}), className), ref: containerRef, theme: theme, error: error, active: focused, label: !!label, dense: dense, inline: inline, stretch: stretch, disabled: disabled, isLeftAddon: isLeftAddon, isRightAddon: isRightAddon, leftChildren: leftChildren, rightChildren: rightChildren, underlineDirection: underlineDirection, onClick: !disabled ? handleClick : undefined }, { children: [(0, jsx_runtime_1.jsx)(FloatingLabel_1.FloatingLabel, __assign({ style: labelStyle, className: labelClassName, htmlFor: id, error: error, active: focused, floating: focused || valued, valued: valued, dense: dense, disabled: disabled }, { children: label })), children] })));
});
//# sourceMappingURL=TextArea.js.map