forked from cybrespace/pinafore
138 lines
3.2 KiB
JavaScript
138 lines
3.2 KiB
JavaScript
// Modified from https://github.com/jackmoore/autosize/blob/113f1b3/src/autosize.js
|
|
// The only change is to remove IE-specific hacks,
|
|
// remove parent overflow checks, make page resizes more performant,
|
|
// add deferredUpdate, and add perf marks.
|
|
|
|
import { mark, stop } from './marks'
|
|
import debounce from 'lodash-es/debounce'
|
|
import throttle from 'lodash-es/throttle'
|
|
|
|
const map = new Map()
|
|
let createEvent = (name) => new Event(name, {bubbles: true})
|
|
|
|
function assign (ta) {
|
|
if (!ta || !ta.nodeName || ta.nodeName !== 'TEXTAREA' || map.has(ta)) {
|
|
return
|
|
}
|
|
|
|
let heightOffset = null
|
|
let cachedHeight = null
|
|
|
|
function init () {
|
|
const style = window.getComputedStyle(ta, null)
|
|
|
|
if (style.boxSizing === 'content-box') {
|
|
heightOffset = -(parseFloat(style.paddingTop) + parseFloat(style.paddingBottom))
|
|
} else {
|
|
heightOffset = parseFloat(style.borderTopWidth) + parseFloat(style.borderBottomWidth)
|
|
}
|
|
|
|
update()
|
|
}
|
|
|
|
function resize () {
|
|
mark('autosize:resize()')
|
|
let res = _resize()
|
|
stop('autosize:resize()')
|
|
return res
|
|
}
|
|
|
|
function _resize () {
|
|
const originalHeight = ta.style.height
|
|
|
|
ta.style.height = ''
|
|
|
|
let endHeight = ta.scrollHeight + heightOffset
|
|
|
|
if (ta.scrollHeight === 0) {
|
|
// If the scrollHeight is 0, then the element probably has display:none or is detached from the DOM.
|
|
ta.style.height = originalHeight
|
|
return
|
|
}
|
|
|
|
ta.style.height = endHeight + 'px'
|
|
return endHeight
|
|
}
|
|
|
|
const deferredUpdate = throttle(() => requestAnimationFrame(update), 100)
|
|
|
|
function update () {
|
|
mark('autosize:update()')
|
|
_update()
|
|
stop('autosize:update()')
|
|
}
|
|
|
|
function _update () {
|
|
let newHeight = resize()
|
|
if (cachedHeight !== newHeight) {
|
|
cachedHeight = newHeight
|
|
const evt = createEvent('autosize:resized')
|
|
try {
|
|
ta.dispatchEvent(evt)
|
|
} catch (err) {
|
|
// Firefox will throw an error on dispatchEvent for a detached element
|
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=889376
|
|
}
|
|
}
|
|
}
|
|
|
|
const pageResize = debounce(update, 1000)
|
|
|
|
const destroy = () => {
|
|
window.removeEventListener('resize', pageResize, false)
|
|
ta.removeEventListener('input', deferredUpdate, false)
|
|
ta.removeEventListener('autosize:destroy', destroy, false)
|
|
ta.removeEventListener('autosize:update', update, false)
|
|
|
|
map.delete(ta)
|
|
}
|
|
|
|
ta.addEventListener('autosize:destroy', destroy, false)
|
|
|
|
window.addEventListener('resize', pageResize, false)
|
|
ta.addEventListener('input', deferredUpdate, false)
|
|
ta.addEventListener('autosize:update', update, false)
|
|
|
|
map.set(ta, {
|
|
destroy,
|
|
update
|
|
})
|
|
|
|
init()
|
|
}
|
|
|
|
function destroy (ta) {
|
|
const methods = map.get(ta)
|
|
if (methods) {
|
|
methods.destroy()
|
|
}
|
|
}
|
|
|
|
function update (ta) {
|
|
const methods = map.get(ta)
|
|
if (methods) {
|
|
methods.update()
|
|
}
|
|
}
|
|
|
|
let autosize = (el, options) => {
|
|
if (el) {
|
|
Array.prototype.forEach.call(el.length ? el : [el], x => assign(x, options))
|
|
}
|
|
return el
|
|
}
|
|
autosize.destroy = el => {
|
|
if (el) {
|
|
Array.prototype.forEach.call(el.length ? el : [el], destroy)
|
|
}
|
|
return el
|
|
}
|
|
autosize.update = el => {
|
|
if (el) {
|
|
Array.prototype.forEach.call(el.length ? el : [el], update)
|
|
}
|
|
return el
|
|
}
|
|
|
|
export { autosize }
|