Compare commits

...

2 Commits

Author SHA1 Message Date
konnorrogers
e27d8429a3 prettier 2024-12-19 20:39:03 -05:00
konnorrogers
479a660dfe fix textarea jank 2024-12-19 20:38:26 -05:00
3 changed files with 71 additions and 16 deletions

View File

@@ -98,3 +98,19 @@ Textareas will automatically resize to expand to fit their content when `resize`
```html {.example}
<wa-textarea resize="auto"></wa-textarea>
```
### Resize horizontal
Textareas can be made to resize horizontally when `resize` is set to `"horizontal"`
```html {.example}
<wa-textarea resize="horizontal"></wa-textarea>
```
### Resize both
Textareas can be made to resize both vertically and horizontally when `resize` is set to `"both"`
```html {.example}
<wa-textarea resize="both"></wa-textarea>
```

View File

@@ -1,6 +1,5 @@
:host {
display: flex;
flex-flow: column;
display: block;
border-width: 0;
}
@@ -46,10 +45,19 @@ textarea::-webkit-search-results-decoration {
resize: none;
}
textarea,
:host([resize='vertical']) textarea {
resize: vertical;
}
:host([resize='horizontal']) textarea {
resize: horizontal;
}
:host([resize='both']) textarea {
resize: both;
}
:host([resize='auto']) textarea {
height: auto;
resize: none;

View File

@@ -1,4 +1,4 @@
import { html } from 'lit';
import { html, type PropertyValues } from 'lit';
import { customElement, property, query, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { ifDefined } from 'lit/directives/if-defined.js';
@@ -35,7 +35,8 @@ import styles from './textarea.css';
* @csspart label - The label
* @csspart form-control-input - The input's wrapper.
* @csspart hint - The hint's wrapper.
* @csspart base - The internal `<textarea>` control.
* @csspart textarea - The internal `<textarea>` control.
* @csspart base - The wrapper around the `<textarea>` control.
*
* @cssproperty --background-color - The textarea's background color.
* @cssproperty --border-color - The color of the textarea's borders.
@@ -57,6 +58,7 @@ export default class WaTextarea extends WebAwesomeFormAssociatedElement {
private resizeObserver: ResizeObserver;
@query('.control') input: HTMLTextAreaElement;
@query('[part~="base"]') base: HTMLDivElement;
@query('.size-adjuster') sizeAdjuster: HTMLTextAreaElement;
@property() title = ''; // make reactive to pass through
@@ -107,7 +109,7 @@ export default class WaTextarea extends WebAwesomeFormAssociatedElement {
@property({ type: Number }) rows = 4;
/** Controls how the textarea can be resized. */
@property() resize: 'none' | 'vertical' | 'auto' = 'vertical';
@property({ reflect: true }) resize: 'none' | 'vertical' | 'horizontal' | 'both' | 'auto' = 'vertical';
/** Disables the textarea. */
@property({ type: Boolean }) disabled = false;
@@ -179,10 +181,10 @@ export default class WaTextarea extends WebAwesomeFormAssociatedElement {
connectedCallback() {
super.connectedCallback();
this.resizeObserver = new ResizeObserver(() => this.setTextareaHeight());
this.resizeObserver = new ResizeObserver(() => this.setTextareaDimensions());
this.updateComplete.then(() => {
this.setTextareaHeight();
this.setTextareaDimensions();
this.resizeObserver.observe(this.input);
if (this.didSSR && this.input && this.value !== this.input.value) {
@@ -208,7 +210,7 @@ export default class WaTextarea extends WebAwesomeFormAssociatedElement {
private handleChange() {
this.valueHasChanged = true;
this.value = this.input.value;
this.setTextareaHeight();
this.setTextareaDimensions();
this.dispatchEvent(new WaChangeEvent());
this.checkValidity();
}
@@ -223,7 +225,14 @@ export default class WaTextarea extends WebAwesomeFormAssociatedElement {
this.dispatchEvent(new WaInputEvent());
}
private setTextareaHeight() {
private setTextareaDimensions() {
if (this.resize === 'none') {
// just in case this is called via a property changing.
this.base.style.width = ``;
this.base.style.height = ``;
return;
}
if (this.resize === 'auto') {
// This prevents layout shifts. We use `clientHeight` instead of `scrollHeight` to account for if the `<textarea>` has a max-height set on it.
// In my tests, this has worked fine. Im not aware of any edge cases. [Konnor]
@@ -231,21 +240,43 @@ export default class WaTextarea extends WebAwesomeFormAssociatedElement {
this.sizeAdjuster.style.height = `${this.input.clientHeight}px`;
this.input.style.height = 'auto';
this.input.style.height = `${this.input.scrollHeight}px`;
} else {
this.input.style.height = '';
this.base.style.width = ``;
this.base.style.height = ``;
return;
}
// handles vertical, horizontal, and both resizers:
// These should always be set by a manual resize operation , so its reasonable to expect px.
if (this.input.style.width) {
const width = Number(this.input.style.width.split(/px/)[0]) + 2;
this.base.style.width = `${width}px`;
}
if (this.input.style.height) {
const height = Number(this.input.style.height.split(/px/)[0]) + 2;
this.base.style.height = `${height}px`;
}
}
@watch('rows', { waitUntilFirstUpdate: true })
handleRowsChange() {
this.setTextareaHeight();
this.setTextareaDimensions();
}
@watch('value', { waitUntilFirstUpdate: true })
async handleValueChange() {
await this.updateComplete;
this.checkValidity();
this.setTextareaHeight();
this.setTextareaDimensions();
}
protected updated(changedProperties: PropertyValues<this>) {
if (changedProperties.has('resize')) {
this.setTextareaDimensions();
}
super.updated(changedProperties);
}
/** Sets focus on the textarea. */
@@ -300,7 +331,7 @@ export default class WaTextarea extends WebAwesomeFormAssociatedElement {
if (this.value !== this.input.value) {
this.value = this.input.value;
this.setTextareaHeight();
this.setTextareaDimensions();
}
}
@@ -321,9 +352,9 @@ export default class WaTextarea extends WebAwesomeFormAssociatedElement {
<slot name="label">${this.label}</slot>
</label>
<div part="textarea" class="textarea wa-text-field">
<div part="base" class="wa-text-field textarea">
<textarea
part="base"
part="textarea"
id="input"
class="control"
title=${this.title /* An empty title prevents browser validation tooltips from appearing on hover */}