Skip to content

Commit

Permalink
refactor: prevent page scrolling when resizing text area (#2456)
Browse files Browse the repository at this point in the history
  • Loading branch information
sissbruecker authored Sep 6, 2021
1 parent 1ed5818 commit 3b56605
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 41 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
79 changes: 45 additions & 34 deletions packages/text-area/src/vaadin-text-area.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,31 +102,17 @@ export class TextArea extends CharLengthMixin(
-webkit-overflow-scrolling: touch;
}
.textarea-wrapper {
display: grid;
flex: 1 1 auto;
align-self: stretch;
padding: 0;
}
.textarea-wrapper::after {
content: attr(data-replicated-value) ' ';
white-space: pre-wrap;
visibility: hidden;
}
::slotted(textarea) {
-webkit-appearance: none;
-moz-appearance: none;
flex: auto;
white-space: nowrap;
overflow: hidden;
width: 100%;
height: 100%;
outline: none;
resize: none;
margin: 0;
padding: 0;
padding: 0 0.25em;
border: 0;
border-radius: 0;
min-width: 0;
Expand All @@ -139,16 +125,6 @@ export class TextArea extends CharLengthMixin(
box-shadow: none;
}
::slotted(textarea),
.textarea-wrapper::after {
grid-area: 1 / 1 / 2 / 2;
box-sizing: border-box;
padding: 0 0.25em;
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-word;
}
[part='input-field'] ::slotted(*) {
align-self: flex-start;
}
Expand All @@ -174,9 +150,7 @@ export class TextArea extends CharLengthMixin(
theme$="[[theme]]"
>
<slot name="prefix" slot="prefix"></slot>
<div class="textarea-wrapper">
<slot name="textarea"></slot>
</div>
<slot name="textarea"></slot>
<slot name="suffix" slot="suffix"></slot>
<div id="clearButton" part="clear-button" slot="suffix"></div>
</vaadin-input-container>
Expand Down Expand Up @@ -204,6 +178,7 @@ export class TextArea extends CharLengthMixin(
connectedCallback() {
super.connectedCallback();

this._inputField = this.shadowRoot.querySelector('[part=input-field]');
this._updateHeight();
}

Expand Down Expand Up @@ -234,13 +209,49 @@ export class TextArea extends CharLengthMixin(

/** @private */
_updateHeight() {
if (this.inputElement) {
/* https://css-tricks.com/the-cleanest-trick-for-autogrowing-textareas/ */
this.__textAreaWrapper = this.__textAreaWrapper || this.shadowRoot.querySelector('.textarea-wrapper');
this.__textAreaWrapper.dataset.replicatedValue = this.inputElement.value;
// getComputedStyle is expensive, maybe we can use ResizeObserver in the future
this._dispatchIronResizeEventIfNeeded('InputHeight', getComputedStyle(this.__textAreaWrapper).height);
const input = this.inputElement;
const inputField = this._inputField;

if (!input || !inputField) {
return;
}

const scrollTop = inputField.scrollTop;

// Only clear the height when the content shortens to minimize scrollbar flickering.
const valueLength = this.value ? this.value.length : 0;

if (this._oldValueLength >= valueLength) {
const inputFieldHeight = getComputedStyle(inputField).height;
const inputWidth = getComputedStyle(input).width;

// Temporarily fix the height of the wrapping input field container to prevent changing the browsers scroll
// position while resetting the textareas height. If the textarea had a large height, then removing its height
// will reset its height to the default of two rows. That might reduce the height of the page, and the
// browser might adjust the scroll position before we can restore the measured height of the textarea.
inputField.style.display = 'block';
inputField.style.height = inputFieldHeight;

// Fix the input element width so its scroll height isn't affected by host's disappearing scrollbars
input.style.maxWidth = inputWidth;

// Clear the height of the textarea to allow measuring a reduced scroll height
input.style.height = 'auto';
}
this._oldValueLength = valueLength;

const inputHeight = input.scrollHeight;
if (inputHeight > input.clientHeight) {
input.style.height = inputHeight + 'px';
}

// Restore
input.style.removeProperty('max-width');
inputField.style.removeProperty('display');
inputField.style.removeProperty('height');
inputField.scrollTop = scrollTop;

this._dispatchIronResizeEventIfNeeded('InputHeight', inputHeight);
}
}

Expand Down
9 changes: 2 additions & 7 deletions packages/text-area/theme/material/vaadin-text-area-styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,9 @@ const textArea = css`
box-sizing: border-box;
}
.textarea-wrapper {
[part='input-field'] ::slotted(textarea) {
padding-top: 0;
margin-top: 4px;
padding: 0;
}
[part='input-field'] ::slotted(textarea),
.textarea-wrapper::after {
padding: 0 0 8px;
}
[part='input-field'] ::slotted(textarea) {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class MessageInputTextArea extends TextArea {

// Set initial height to one row
input.setAttribute('rows', 1);
input.style.minHeight = '0';

this.__updateAriaLabel(this.ariaLabel);
}
Expand Down

0 comments on commit 3b56605

Please sign in to comment.