bpist-squalk-forum/src/lib/actions/autogrow.ts
2026-06-23 17:50:30 +02:00

48 lines
1.5 KiB
TypeScript

import type { Action } from "svelte/action";
type AutogrowParams = {
// When false the action is inert, leaving the textarea at its native size.
enabled?: boolean;
// Max height in lines before the textarea starts scrolling.
maxRows?: number;
// A value to watch; resize is re-run whenever it changes (e.g. after a reset).
value?: unknown;
};
// Grow a textarea with its content up to maxRows lines, then scroll.
export const autogrow: Action<
HTMLTextAreaElement,
AutogrowParams | undefined
> = (node, params) => {
let enabled = params?.enabled ?? true;
let maxRows = params?.maxRows ?? 10;
function resize() {
if (!enabled) return;
const cs = getComputedStyle(node);
const lh = parseFloat(cs.lineHeight) || 20;
const vPad = parseFloat(cs.paddingTop) + parseFloat(cs.paddingBottom);
const vBorder =
parseFloat(cs.borderTopWidth) + parseFloat(cs.borderBottomWidth);
const max = lh * maxRows + vPad + vBorder;
node.style.height = "auto";
const next = Math.min(node.scrollHeight, max);
node.style.height = `${next}px`;
node.style.overflowY = node.scrollHeight > max ? "auto" : "hidden";
}
node.addEventListener("input", resize);
resize();
return {
update(next) {
enabled = next?.enabled ?? true;
maxRows = next?.maxRows ?? 10;
// Re-run on watched-value changes, including programmatic resets.
resize();
},
destroy() {
node.removeEventListener("input", resize);
},
};
};