comment, cleanup and improve autoresize/autoscroll
This commit is contained in:
parent
0f55359b49
commit
0d6a9f5a62
2 changed files with 50 additions and 36 deletions
|
@ -277,6 +277,8 @@ const PostStatusForm = {
|
||||||
resize (e) {
|
resize (e) {
|
||||||
const target = e.target || e
|
const target = e.target || e
|
||||||
if (!(target instanceof window.Element)) { return }
|
if (!(target instanceof window.Element)) { return }
|
||||||
|
|
||||||
|
// Reset to default height for empty form, nothing else to do here.
|
||||||
if (target.value === '') {
|
if (target.value === '') {
|
||||||
target.style.height = null
|
target.style.height = null
|
||||||
this.$refs['emoji-input'].resize()
|
this.$refs['emoji-input'].resize()
|
||||||
|
@ -284,61 +286,74 @@ const PostStatusForm = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const rootRef = this.$refs['root']
|
const rootRef = this.$refs['root']
|
||||||
const scroller = this.$el.closest('.sidebar-scroller') ||
|
/* Scroller is either `window` (replies in TL), sidebar (main post form,
|
||||||
|
* replies in notifs) or mobile post form. Note that getting and setting
|
||||||
|
* scroll is different for `Window` and `Element`s
|
||||||
|
*/
|
||||||
|
const scrollerRef = this.$el.closest('.sidebar-scroller') ||
|
||||||
this.$el.closest('.post-form-modal-view') ||
|
this.$el.closest('.post-form-modal-view') ||
|
||||||
window
|
window
|
||||||
|
|
||||||
|
// Getting info about padding we have to account for, removing 'px' part
|
||||||
const topPaddingStr = window.getComputedStyle(target)['padding-top']
|
const topPaddingStr = window.getComputedStyle(target)['padding-top']
|
||||||
const bottomPaddingStr = window.getComputedStyle(target)['padding-bottom']
|
const bottomPaddingStr = window.getComputedStyle(target)['padding-bottom']
|
||||||
// Remove "px" at the end of the values
|
|
||||||
const topPadding = Number(topPaddingStr.substring(0, topPaddingStr.length - 2))
|
const topPadding = Number(topPaddingStr.substring(0, topPaddingStr.length - 2))
|
||||||
const bottomPadding = Number(bottomPaddingStr.substring(0, bottomPaddingStr.length - 2))
|
const bottomPadding = Number(bottomPaddingStr.substring(0, bottomPaddingStr.length - 2))
|
||||||
const vertPadding = topPadding + bottomPadding
|
const vertPadding = topPadding + bottomPadding
|
||||||
|
|
||||||
const oldHeightStr = target.style.height || ''
|
const oldHeightStr = target.style.height || ''
|
||||||
const oldHeight = Number(oldHeightStr.substring(0, oldHeightStr.length - 2))
|
const oldHeight = Number(oldHeightStr.substring(0, oldHeightStr.length - 2))
|
||||||
|
|
||||||
const tempScroll = scroller === window ? scroller.scrollY : scroller.scrollTop
|
/* Explanation:
|
||||||
|
*
|
||||||
|
* https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollHeight
|
||||||
|
* scrollHeight returns element's scrollable content height, i.e. visible
|
||||||
|
* element + overscrolled parts of it. We use it to determine when text
|
||||||
|
* inside the textarea exceeded its height, so we can set height to prevent
|
||||||
|
* overscroll, i.e. make textarea grow with the text. HOWEVER, since we
|
||||||
|
* explicitly set new height, scrollHeight won't go below that, so we can't
|
||||||
|
* SHRINK the textarea when there's extra space. To workaround that we set
|
||||||
|
* height to 'auto' which makes textarea tiny again, so that scrollHeight
|
||||||
|
* will match text height again. HOWEVER, shrinking textarea can screw with
|
||||||
|
* the scroll since there might be not enough padding around root to even
|
||||||
|
* varrant a scroll, so it will jump to 0 and refuse to move anywhere,
|
||||||
|
* so we check current scroll position before shrinking and then restore it
|
||||||
|
* with needed delta.
|
||||||
|
*/
|
||||||
|
|
||||||
// Auto is needed to make textbox shrink when removing lines
|
// this part has to be BEFORE the content size update
|
||||||
target.style.height = 'auto'
|
const currentScroll = scrollerRef === window
|
||||||
const newHeight = target.scrollHeight - vertPadding
|
? scrollerRef.scrollY
|
||||||
target.style.height = `${oldHeight}px`
|
: scrollerRef.scrollTop
|
||||||
|
const scrollerHeight = scrollerRef === window
|
||||||
if (scroller === window) {
|
? scrollerRef.innerHeight
|
||||||
scroller.scroll(0, tempScroll)
|
: scrollerRef.offsetHeight
|
||||||
} else {
|
|
||||||
scroller.scrollTop = tempScroll
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentScroll = scroller === window ? scroller.scrollY : scroller.scrollTop
|
|
||||||
const scrollerHeight = scroller === window ? scroller.innerHeight : scroller.offsetHeight
|
|
||||||
const scrollerBottomBorder = currentScroll + scrollerHeight
|
const scrollerBottomBorder = currentScroll + scrollerHeight
|
||||||
|
|
||||||
const rootBottomBorder = rootRef.offsetHeight +
|
// BEGIN content size update
|
||||||
findOffset(rootRef, scroller).top
|
target.style.height = 'auto'
|
||||||
|
const newHeight = target.scrollHeight - vertPadding
|
||||||
|
target.style.height = `${newHeight}px`
|
||||||
|
// END content size update
|
||||||
|
|
||||||
|
// We check where the bottom border of root element is, this uses findOffset
|
||||||
|
// to find offset relative to scrollable container (scroller)
|
||||||
|
const rootBottomBorder = rootRef.offsetHeight + findOffset(rootRef, scrollerRef).top
|
||||||
|
|
||||||
const textareaSizeChangeDelta = newHeight - oldHeight || 0
|
const textareaSizeChangeDelta = newHeight - oldHeight || 0
|
||||||
const rootChangeDelta = rootBottomBorder - scrollerBottomBorder + textareaSizeChangeDelta
|
const isBottomObstructed = scrollerBottomBorder < rootBottomBorder
|
||||||
|
const rootChangeDelta = rootBottomBorder - scrollerBottomBorder
|
||||||
|
const totalDelta = textareaSizeChangeDelta +
|
||||||
|
(isBottomObstructed ? rootChangeDelta : 0)
|
||||||
|
|
||||||
// console.log('CURRENT SCROLL', currentScroll)
|
const targetScroll = currentScroll + totalDelta
|
||||||
console.log('BOTTOM BORDERS', rootBottomBorder, scrollerBottomBorder)
|
|
||||||
console.log('BOTTOM DELTA', rootBottomBorder - scrollerBottomBorder)
|
if (scrollerRef === window) {
|
||||||
const targetScroll = scrollerBottomBorder < rootBottomBorder
|
scrollerRef.scroll(0, targetScroll)
|
||||||
? currentScroll + rootChangeDelta
|
|
||||||
: currentScroll + textareaSizeChangeDelta
|
|
||||||
if (scroller === window) {
|
|
||||||
scroller.scroll(0, targetScroll)
|
|
||||||
} else {
|
} else {
|
||||||
scroller.scrollTop = targetScroll
|
scrollerRef.scrollTop = targetScroll
|
||||||
}
|
}
|
||||||
target.style.height = `${newHeight}px`
|
|
||||||
|
|
||||||
console.log(scroller, rootRef)
|
|
||||||
// console.log('SCROLL TO BUTTON', scrollerBottomBorder < rootBottomBorder)
|
|
||||||
// console.log('DELTA B', rootChangeDelta)
|
|
||||||
// console.log('DELTA D', textareaSizeChangeDelta)
|
|
||||||
// console.log('TARGET', targetScroll)
|
|
||||||
// console.log('ACTUAL', scroller.scrollTop || scroller.scrollY || 0)
|
|
||||||
this.$refs['emoji-input'].resize()
|
this.$refs['emoji-input'].resize()
|
||||||
},
|
},
|
||||||
showEmojiPicker () {
|
showEmojiPicker () {
|
||||||
|
|
|
@ -9,7 +9,6 @@ export const findOffset = (child, parent, { top = 0, left = 0 } = {}, ignorePadd
|
||||||
result.left += ignorePadding ? 0 : leftPadding
|
result.left += ignorePadding ? 0 : leftPadding
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('eee', parent, child.offsetParent)
|
|
||||||
if (child.offsetParent && (parent === window || parent.contains(child.offsetParent) || parent === child.offsetParent)) {
|
if (child.offsetParent && (parent === window || parent.contains(child.offsetParent) || parent === child.offsetParent)) {
|
||||||
return findOffset(child.offsetParent, parent, result, false)
|
return findOffset(child.offsetParent, parent, result, false)
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in a new issue