From 3b56605bb6e15151e696242b7bc3cd51c7ed6992 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sascha=20I=C3=9Fbr=C3=BCcker?= Date: Mon, 6 Sep 2021 18:09:06 +0200 Subject: [PATCH] refactor: prevent page scrolling when resizing text area (#2456) --- .../baseline/form-layout-item-text-area.png | Bin 13954 -> 14021 bytes packages/text-area/src/vaadin-text-area.js | 79 ++++++++++-------- .../theme/material/vaadin-text-area-styles.js | 9 +- .../baseline/form-layout-item-text-area.png | Bin 13954 -> 14021 bytes .../src/vaadin-message-input-text-area.js | 1 + 5 files changed, 48 insertions(+), 41 deletions(-) diff --git a/packages/custom-field/test/visual/material/screenshots/custom-field/baseline/form-layout-item-text-area.png b/packages/custom-field/test/visual/material/screenshots/custom-field/baseline/form-layout-item-text-area.png index 187292356e6bfa4695252e9e74a3fd25011ab99d..4b265e7e797593fabb6efb0144630862e915c5f4 100644 GIT binary patch delta 57 zcmZq5J(@c~oa+w<8v_Hwj<&NGHY&;}FcxpNQ*aiYtYGFmxzY5k2+yU<5BJZxSq@af N;OXk;vd$@?2>@ot6-WR8 delta 53 zcmX?_+mt&&oa+k*8v_GF$W{Nn8x>^~7!5YtDL9KxK4SV-Bxm2{iyOZ^JPMR%@O1Ta JS?83{1OO3>6ifgB diff --git a/packages/text-area/src/vaadin-text-area.js b/packages/text-area/src/vaadin-text-area.js index 8734e97248..4918ccee09 100644 --- a/packages/text-area/src/vaadin-text-area.js +++ b/packages/text-area/src/vaadin-text-area.js @@ -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; @@ -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; } @@ -174,9 +150,7 @@ export class TextArea extends CharLengthMixin( theme$="[[theme]]" > -
- -
+
@@ -204,6 +178,7 @@ export class TextArea extends CharLengthMixin( connectedCallback() { super.connectedCallback(); + this._inputField = this.shadowRoot.querySelector('[part=input-field]'); this._updateHeight(); } @@ -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); } } diff --git a/packages/text-area/theme/material/vaadin-text-area-styles.js b/packages/text-area/theme/material/vaadin-text-area-styles.js index 31c0a2bf14..5317bd72c2 100644 --- a/packages/text-area/theme/material/vaadin-text-area-styles.js +++ b/packages/text-area/theme/material/vaadin-text-area-styles.js @@ -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) { diff --git a/packages/vaadin-custom-field/test/visual/material/screenshots/custom-field/baseline/form-layout-item-text-area.png b/packages/vaadin-custom-field/test/visual/material/screenshots/custom-field/baseline/form-layout-item-text-area.png index 187292356e6bfa4695252e9e74a3fd25011ab99d..4b265e7e797593fabb6efb0144630862e915c5f4 100644 GIT binary patch delta 57 zcmZq5J(@c~oa+w<8v_Hwj<&NGHY&;}FcxpNQ*aiYtYGFmxzY5k2+yU<5BJZxSq@af N;OXk;vd$@?2>@ot6-WR8 delta 53 zcmX?_+mt&&oa+k*8v_GF$W{Nn8x>^~7!5YtDL9KxK4SV-Bxm2{iyOZ^JPMR%@O1Ta JS?83{1OO3>6ifgB diff --git a/packages/vaadin-messages/src/vaadin-message-input-text-area.js b/packages/vaadin-messages/src/vaadin-message-input-text-area.js index 7d07219488..e424b9cca2 100644 --- a/packages/vaadin-messages/src/vaadin-message-input-text-area.js +++ b/packages/vaadin-messages/src/vaadin-message-input-text-area.js @@ -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); }