UNPKG

braid-design-system

Version:
136 lines (135 loc) 4.47 kB
import { jsx, jsxs } from "react/jsx-runtime"; import { forwardRef, useState, useRef, useCallback } from "react"; import { Box } from "../Box/Box.mjs"; import { Field } from "../private/Field/Field.mjs"; import { getCharacterLimitStatus } from "../private/Field/getCharacterLimitStatus.mjs"; import { formatRanges } from "./formatRanges.mjs"; import { highlights, field } from "./Textarea.css.mjs"; const pxToInt = (str) => typeof str === "string" ? parseInt(str.replace("px", ""), 10) : 0; const calculateLines = (target, lines, lineLimit) => { const { paddingBottom, paddingTop, lineHeight } = window.getComputedStyle(target); if (!lineHeight.endsWith("px")) { return lines; } const padding = pxToInt(paddingTop) + pxToInt(paddingBottom); const currentRows = Math.floor( (target.scrollHeight - padding) / pxToInt(lineHeight) ); if (target && target.value === "") { return lines; } return typeof lineLimit === "number" && currentRows > lineLimit ? lineLimit : currentRows; }; const Textarea = forwardRef( ({ value, onChange, onBlur, onFocus, onPaste, placeholder, characterLimit, highlightRanges: highlightRangesProp = [], lines = 3, lineLimit, grow = true, tone, spellCheck, ...restProps }, ref) => { const [rows, setRows] = useState(lines); const highlightsRef = useRef(null); const updateScroll = useCallback( (scrollTop) => { if (highlightsRef.current) { highlightsRef.current.scrollTop = scrollTop; } }, [highlightsRef] ); const inputLength = String(value).length; const hasExceededCharacterLimit = characterLimit && inputLength > characterLimit; const highlightTone = !hasExceededCharacterLimit && (tone === "critical" || tone === "caution") ? tone : "critical"; const highlightRanges = hasExceededCharacterLimit ? [{ start: characterLimit }] : highlightRangesProp; const hasHighlights = highlightRanges.length > 0; return /* @__PURE__ */ jsx( Field, { ...restProps, componentName: "Textarea", tone, value, icon: void 0, prefix: void 0, secondaryMessage: characterLimit ? getCharacterLimitStatus({ value, characterLimit }) : null, children: (overlays, { className, borderRadius, background, ...fieldProps }) => /* @__PURE__ */ jsxs( Box, { position: "relative", width: "full", zIndex: 0, background, borderRadius, children: [ hasHighlights ? /* @__PURE__ */ jsx( Box, { ref: highlightsRef, position: "absolute", overflow: "hidden", pointerEvents: "none", height: "full", "aria-hidden": "true", top: 0, left: 0, className: [highlights, className], ...fieldProps, children: formatRanges(String(value), highlightRanges, highlightTone) } ) : null, /* @__PURE__ */ jsx( Box, { component: "textarea", position: "relative", zIndex: 1, rows, value, onChange: (e) => { if (grow) { setRows(calculateLines(e.currentTarget, lines, lineLimit)); } if (typeof onChange === "function") { onChange(e); } if (hasHighlights) { updateScroll(e.currentTarget.scrollTop); } }, onBlur, onFocus, onPaste, onScroll: hasHighlights ? (event) => updateScroll(event.currentTarget.scrollTop) : void 0, placeholder: !restProps.disabled ? placeholder : void 0, spellCheck, className: [field, className], borderRadius, ...fieldProps, ref } ), overlays ] } ) } ); } ); Textarea.displayName = "Textarea"; export { Textarea };