fix: prevent page jumps due to textarea auto resizing (#7569)
The change prevents unnecessary resizing of the text field. ## Testing - Compose some text e.g. in an issue - Enter a lot of lines, until the compose field is as big as possible - Move the screen up, the top of the compose field should leave the screen, but the last e.g. 5 lines should still be visible on the screen - Append some text - Make sure the textfield just gets bigger, but doesn't jump to the bottom of the page Fixes #7522 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/7569 Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org> Co-authored-by: Beowulf <beowulf@beocode.eu> Co-committed-by: Beowulf <beowulf@beocode.eu>
This commit is contained in:
parent
dcd431b0d4
commit
1cd0c5e99b
2 changed files with 18 additions and 17 deletions
|
|
@ -84,7 +84,7 @@
|
|||
}
|
||||
|
||||
text-expander {
|
||||
display: block;
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -117,9 +117,16 @@ export function isDocumentFragmentOrElementNode(el) {
|
|||
// included in all copies or substantial portions of the Software.
|
||||
// ---------------------------------------------------------------------
|
||||
export function autosize(textarea, {viewportMarginBottom = 0} = {}) {
|
||||
const textareaParent = textarea.parentElement;
|
||||
function createJumpPreventer() {
|
||||
const el = document.createElement('div');
|
||||
textareaParent.prepend(el, textareaParent.firstChild);
|
||||
return el;
|
||||
}
|
||||
const jumpPreventer = textareaParent.querySelector('div') || createJumpPreventer();
|
||||
let isUserResized = false;
|
||||
// lastStyleHeight and initialStyleHeight are CSS values like '100px'
|
||||
let lastMouseX, lastMouseY, lastStyleHeight, initialStyleHeight;
|
||||
let lastMouseX, lastMouseY, lastStyleHeight, initialStyleHeight, lastLines;
|
||||
|
||||
function onUserResize(event) {
|
||||
if (isUserResized) return;
|
||||
|
|
@ -157,6 +164,12 @@ export function autosize(textarea, {viewportMarginBottom = 0} = {}) {
|
|||
const {top, bottom} = overflowOffset();
|
||||
const isOutOfViewport = top < 0 || bottom < 0;
|
||||
|
||||
const currLines = textarea.value.split('\n').length;
|
||||
const shouldResize = !isOutOfViewport || currLines < lastLines;
|
||||
if (currLines < lastLines) jumpPreventer.style.height = '0';
|
||||
lastLines = currLines;
|
||||
if (!shouldResize) return;
|
||||
|
||||
const computedStyle = getComputedStyle(textarea);
|
||||
const topBorderWidth = parseFloat(computedStyle.borderTopWidth);
|
||||
const bottomBorderWidth = parseFloat(computedStyle.borderBottomWidth);
|
||||
|
|
@ -168,23 +181,10 @@ export function autosize(textarea, {viewportMarginBottom = 0} = {}) {
|
|||
const maxHeight = curHeight + bottom - adjustedViewportMarginBottom;
|
||||
|
||||
textarea.style.height = 'auto';
|
||||
let newHeight = textarea.scrollHeight + borderAddOn;
|
||||
|
||||
if (isOutOfViewport) {
|
||||
// it is already out of the viewport:
|
||||
// * if the textarea is expanding: do not resize it
|
||||
if (newHeight > curHeight) {
|
||||
newHeight = curHeight;
|
||||
}
|
||||
// * if the textarea is shrinking, shrink line by line (just use the
|
||||
// scrollHeight). do not apply max-height limit, otherwise the page
|
||||
// flickers and the textarea jumps
|
||||
} else {
|
||||
// * if it is in the viewport, apply the max-height limit
|
||||
newHeight = Math.min(maxHeight, newHeight);
|
||||
}
|
||||
const newHeight = Math.min(maxHeight, textarea.scrollHeight + borderAddOn);
|
||||
|
||||
textarea.style.height = `${newHeight}px`;
|
||||
jumpPreventer.style.height = textarea.style.height;
|
||||
lastStyleHeight = textarea.style.height;
|
||||
} finally {
|
||||
// ensure that the textarea is fully scrolled to the end, when the cursor
|
||||
|
|
@ -209,6 +209,7 @@ export function autosize(textarea, {viewportMarginBottom = 0} = {}) {
|
|||
textarea.addEventListener('input', resizeToFit);
|
||||
textarea.form?.addEventListener('reset', onFormReset);
|
||||
initialStyleHeight = textarea.style.height ?? undefined;
|
||||
lastLines = textarea.value.split('\n').length;
|
||||
if (textarea.value) resizeToFit();
|
||||
|
||||
return {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue