@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
JavaScript
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) ;
}
: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 };