Files
windmill/frontend/src/lib/autosize.ts
T
centdix 070ef1a950 fix: fix input with wrong height on first render (#5935)
* fix input with wrong height on first render

* better fix
2025-06-13 09:29:57 +00:00

91 lines
2.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { tick } from 'svelte'
type TextArea = HTMLTextAreaElement
export const autosize = (node: TextArea) => {
/* ------------------------------------------------------------------
* Constants
* ---------------------------------------------------------------- */
const UPDATE_EVENT = new Event('update')
const MIN_HEIGHT = 30 // px
const EXTRA = 2 // px added to scrollHeight
let width = 0
/* ------------------------------------------------------------------
* Core resize routine
* ---------------------------------------------------------------- */
const resize = () => {
node.style.height = 'auto'
node.style.height = `${Math.max(node.scrollHeight, MIN_HEIGHT) + EXTRA}px`
}
/* ------------------------------------------------------------------
* Patch `value` so programmatic changes trigger resize
* ---------------------------------------------------------------- */
const proto = Object.getPrototypeOf(node)
const desc = Object.getOwnPropertyDescriptor(proto, 'value')
if (desc) {
Object.defineProperty(node, 'value', {
get() {
return desc.get?.call(this)
},
set(v: unknown) {
desc.set?.call(this, v)
node.dispatchEvent(UPDATE_EVENT)
}
})
}
/* ------------------------------------------------------------------
* Event listeners
* ---------------------------------------------------------------- */
const onInput = () => node.dispatchEvent(UPDATE_EVENT)
node.addEventListener('input', onInput)
node.addEventListener('update', resize)
/* ------------------------------------------------------------------
* Inline styling
* ---------------------------------------------------------------- */
node.style.boxSizing = 'border-box'
/* ------------------------------------------------------------------
* Wait for DOM mount, then do an initial measure.
* If the <textarea> is already visible (offsetWidth > 0) this covers it;
* otherwise the first ResizeObserver callback will.
* ---------------------------------------------------------------- */
;(async () => {
await tick()
resize()
})()
/* ------------------------------------------------------------------
* ResizeObserver handles:
* • first time the element gets a real width
* • container/window resizes afterwards
* ---------------------------------------------------------------- */
const ro = new ResizeObserver(([entry]) => {
const newWidth = entry.contentRect.width
if (newWidth !== width) {
width = newWidth
resize()
}
})
ro.observe(node)
/* ------------------------------------------------------------------
* Action lifecycle
* ---------------------------------------------------------------- */
return {
destroy() {
ro.disconnect()
node.removeEventListener('input', onInput)
node.removeEventListener('update', resize)
}
}
}
export default autosize