UNPKG

@umbraco-ui/uui-textarea

Version:

Custom element wrapping the native textarea element. This is a formAssociated element, meaning it can participate in a native HTMLForm. In browsers other then Chrome it may require a polyfill.

293 lines (285 loc) 9.2 kB
import { LitElement, html, css } from 'lit'; import { defineElement } from '@umbraco-ui/uui-base/lib/registration'; import { property, query } from 'lit/decorators.js'; import { UUIEvent } from '@umbraco-ui/uui-base/lib/events'; import { UUIFormControlMixin } from '@umbraco-ui/uui-base/lib/mixins'; import { ifDefined } from 'lit/directives/if-defined.js'; class UUITextareaEvent extends UUIEvent { constructor(evName, eventInit = {}) { super(evName, { ...{ bubbles: true }, ...eventInit }); } } UUITextareaEvent.CHANGE = "change"; UUITextareaEvent.INPUT = "input"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __decorateClass = (decorators, target, key, kind) => { var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target; for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result; if (kind && result) __defProp(target, key, result); return result; }; let UUITextareaElement = class extends UUIFormControlMixin(LitElement, "") { constructor() { super(); this.placeholder = ""; this.disabled = false; this.readonly = false; this.name = ""; this.error = false; this.minlengthMessage = (charsLeft) => `${charsLeft} characters left`; this.maxlengthMessage = (max, current) => `Maximum ${max} characters, ${current} too many.`; this.autoHeight = false; this.addEventListener("mousedown", () => { this.style.setProperty("--uui-show-focus-outline", "0"); }); this.addEventListener("blur", () => { this.style.setProperty("--uui-show-focus-outline", ""); }); this.addValidator( "tooShort", () => { const label = this.minlengthMessage; if (typeof label === "function") { return label( this.minlength ? this.minlength - String(this.value).length : 0 ); } return label; }, () => !!this.minlength && this.value.length < this.minlength ); this.addValidator( "tooLong", () => { const label = this.maxlengthMessage; if (typeof label === "function") { return label(this.maxlength ?? 0, String(this.value).length); } return label; }, () => !!this.maxlength && this.value.length > this.maxlength ); } connectedCallback() { super.connectedCallback(); if (!this.label) { console.warn(this.tagName + " needs a `label`", this); } if (this.autoHeight) { requestAnimationFrame(() => { this.autoUpdateHeight(); }); } } /** * This method enables <label for="..."> to focus the select */ async focus() { await this.updateComplete; this._textarea.focus(); } async blur() { await this.updateComplete; this._textarea.blur(); } /** * This method enables <label for="..."> to open the select */ async click() { await this.updateComplete; this._textarea.click(); } getFormElement() { return this._textarea; } onInput(e) { this.value = e.target.value; if (this.autoHeight) { this.autoUpdateHeight(); } } onChange(e) { e.stopPropagation(); this.pristine = false; this.dispatchEvent(new UUITextareaEvent(UUITextareaEvent.CHANGE)); } autoUpdateHeight() { const host = this.shadowRoot.host; const input = this._textarea; const scrollTop = host.scrollTop; const hostHeight = getComputedStyle(host).height; host.style.display = "block"; host.style.height = hostHeight; input.style.height = "auto"; if (input.scrollHeight + 2 > input.clientHeight) { input.style.height = input.scrollHeight + 2 + "px"; } else if (input.scrollHeight + 2 < input.clientHeight) { input.style.removeProperty("height"); } host.style.removeProperty("display"); host.style.removeProperty("height"); host.scrollTop = scrollTop; } render() { return html` <textarea id="textarea" rows=${ifDefined(this.rows)} cols=${ifDefined(this.cols)} .value=${this.value} .name=${this.name} wrap=${ifDefined(this.wrap)} placeholder=${this.placeholder} aria-label=${this.label} .disabled=${this.disabled} ?readonly=${this.readonly} @input=${this.onInput} @change=${this.onChange}> </textarea> `; } }; /** * This is a static class field indicating that the element is can be used inside a native form and participate in its events. It may require a polyfill, check support here https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/attachInternals. Read more about form controls here https://web.dev/more-capable-form-controls/ * @type {boolean} */ UUITextareaElement.formAssociated = true; UUITextareaElement.styles = [ css` :host { position: relative; } :host([error]) textarea, :host([error]) textarea[disabled] { border: 1px solid var(--uui-color-invalid,#d42054) !important; } :host(:not([pristine]):invalid) textarea, /* polyfill support */ :host(:not([pristine])[internals-invalid]) textarea { border-color: var(--uui-color-invalid,#d42054); } :host([auto-height]) textarea { resize: none; } .label { display: inline-block; margin-bottom: var(--uui-size-1,3px); font-weight: bold; } textarea[readonly] { border-color: var( --uui-textarea-border-color-readonly, var(--uui-color-disabled-standalone,rgb( 226, 226, 226 )) ); background-color: var( --uui-textarea-background-color-readonly, var(--uui-color-disabled,#f3f3f5) ); } textarea[disabled] { cursor: not-allowed; background-color: var( --uui-textarea-background-color-disabled, var(--uui-color-disabled,#f3f3f5) ); border-color: var( --uui-textarea-border-color-disabled, var(--uui-color-disabled,#f3f3f5) ); color: var(--uui-color-disabled-contrast,#c4c4c4); } textarea { color: var(--uui-color-text,#060606); font-family: inherit; line-height: var(--uui-size-6,18px); box-sizing: border-box; min-width: 100%; max-width: 100%; font-size: inherit; padding: var(--uui-size-2,6px); border: 1px solid var(--uui-textarea-border-color, var(--uui-color-border,#d8d7d9)); /** Note: Specified border size is needed and hardcoded in autoUpdateHeight() */ border-radius: var(--uui-border-radius,3px); outline: none; min-height: var(--uui-textarea-min-height); max-height: var(--uui-textarea-max-height); background-color: var( --uui-textarea-background-color, var(--uui-color-surface,#fff) ); } :host(:hover) textarea:not([readonly]):not([disabled]) :host(:focus-within) textarea, :host(:focus) textarea { border-color: var( --uui-textarea-border-color, var(--uui-color-border-emphasis,#a1a1a1) ); } textarea:focus { outline: calc(2px * var(--uui-show-focus-outline, 1)) solid var(--uui-color-focus,#3879ff); } ` ]; __decorateClass([ property() ], UUITextareaElement.prototype, "placeholder", 2); __decorateClass([ property({ type: Boolean, reflect: true }) ], UUITextareaElement.prototype, "disabled", 2); __decorateClass([ property({ type: Boolean, reflect: true }) ], UUITextareaElement.prototype, "readonly", 2); __decorateClass([ property({ type: String }) ], UUITextareaElement.prototype, "name", 2); __decorateClass([ property({ type: Boolean, reflect: true }) ], UUITextareaElement.prototype, "error", 2); __decorateClass([ property({ type: Number }) ], UUITextareaElement.prototype, "minlength", 2); __decorateClass([ property({ attribute: "minlength-message" }) ], UUITextareaElement.prototype, "minlengthMessage", 2); __decorateClass([ property({ type: Number }) ], UUITextareaElement.prototype, "maxlength", 2); __decorateClass([ property({ attribute: "maxlength-message" }) ], UUITextareaElement.prototype, "maxlengthMessage", 2); __decorateClass([ query("#textarea") ], UUITextareaElement.prototype, "_textarea", 2); __decorateClass([ property({ type: Boolean, attribute: "auto-height", reflect: true }) ], UUITextareaElement.prototype, "autoHeight", 2); __decorateClass([ property({ type: String }) ], UUITextareaElement.prototype, "label", 2); __decorateClass([ property({ type: Number }) ], UUITextareaElement.prototype, "rows", 2); __decorateClass([ property({ type: Number }) ], UUITextareaElement.prototype, "cols", 2); __decorateClass([ property({ type: String }) ], UUITextareaElement.prototype, "wrap", 2); UUITextareaElement = __decorateClass([ defineElement("uui-textarea") ], UUITextareaElement); export { UUITextareaElement, UUITextareaEvent };