Skip to content

Commit

Permalink
refactor: use overlapping grid elements to grow/shrink textarea's hei…
Browse files Browse the repository at this point in the history
…ght (#2143)

* refactor: use overlapping grid elements to grow/shrink textarea's height

fixes: #291

* fix visual material's tests

* update form-layout-item-text-area screenshot
  • Loading branch information
Farhad authored Jun 30, 2021
1 parent ab1c379 commit 4ca8293
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 38 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
70 changes: 36 additions & 34 deletions packages/vaadin-text-field/src/vaadin-text-area.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,33 @@ class TextAreaElement extends ElementMixin(TextFieldMixin(ControlStateMixin(Them
-webkit-overflow-scrolling: touch;
}
[part='value'] {
.textarea-wrapper {
display: grid;
flex-grow: 1;
align-self: stretch;
}
.textarea-wrapper::after {
content: attr(data-replicated-value) ' ';
white-space: pre-wrap;
visibility: hidden;
}
[part='value'],
[part='input-field'] ::slotted(textarea) {
resize: none;
overflow: hidden;
}
[part='value'],
[part='input-field'] ::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='value'],
Expand All @@ -115,9 +140,11 @@ class TextAreaElement extends ElementMixin(TextFieldMixin(ControlStateMixin(Them
<div part="input-field" id="[[_inputId]]">
<slot name="prefix"></slot>
<slot name="textarea">
<textarea part="value"></textarea>
</slot>
<div class="textarea-wrapper">
<slot name="textarea">
<textarea part="value"></textarea>
</slot>
</div>
<div part="clear-button" id="clearButton" role="button" aria-label$="[[i18n.clear]]"></div>
<slot name="suffix"></slot>
Expand Down Expand Up @@ -182,36 +209,11 @@ class TextAreaElement extends ElementMixin(TextFieldMixin(ControlStateMixin(Them

/** @private */
_updateHeight() {
const inputField = this.root.querySelector('[part=input-field]');
const scrollTop = inputField.scrollTop;
const input = this.inputElement;

const inputWidth = getComputedStyle(input).width;

const valueLength = this.value ? this.value.length : 0;
// Only clear the height when the content shortens to minimize scrollbar flickering.
if (this._oldValueLength >= valueLength) {
// Fix the input element width so its scroll height isn't affected by host's disappearing scrollbars
input.style.maxWidth = inputWidth;
input.style.height = 'auto';
// Avoid a jumpy Safari rendering issue
if (this.__safari) {
inputField.style.display = 'block';
}
}
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.scrollTop = scrollTop;

this._dispatchIronResizeEventIfNeeded('InputHeight', inputHeight);
/* 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);
}

/**
Expand Down
4 changes: 2 additions & 2 deletions packages/vaadin-text-field/test/text-area.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -279,13 +279,13 @@ import '../vaadin-text-area.js';
);
});

it('should have matching scrollHeight', () => {
it('should have matching height', () => {
inputField.style.padding = '0';
textArea.style.maxHeight = '100px';

textArea.value = Array(400).join('400');
textArea.value = textArea.value.slice(0, -1);
expect(input.scrollHeight).to.equal(inputField.scrollHeight);
expect(input.clientHeight).to.equal(inputField.scrollHeight);
});

it('should cover input field', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,16 @@ registerStyles(
box-sizing: border-box;
}
[part='input-field'] [part='value'] {
padding-top: 0;
.textarea-wrapper {
margin-top: 4px;
}
[part='value'],
[part='input-field'] ::slotted(textarea),
.textarea-wrapper::after {
padding: 0 0 8px;
}
[part='input-field'] [part='value'],
[part='input-field'] ::slotted(textarea) {
white-space: pre-wrap; /* override "nowrap" from <vaadin-text-field> */
Expand Down

0 comments on commit 4ca8293

Please sign in to comment.