UNPKG

@umbraco-ui/uui-input

Version:

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

358 lines (343 loc) 11.1 kB
import { UUIFormControlMixin, LabelMixin } from '@umbraco-ui/uui-base/lib/mixins'; import { defineElement } from '@umbraco-ui/uui-base/lib/registration'; import { LitElement, html, css } from 'lit'; import { property, query } from 'lit/decorators.js'; import { ifDefined } from 'lit/directives/if-defined.js'; import { UUIEvent } from '@umbraco-ui/uui-base/lib/events'; class UUIInputEvent extends UUIEvent { constructor(evName, eventInit = {}) { super(evName, { ...{ bubbles: true }, ...eventInit }); } } UUIInputEvent.CHANGE = "change"; UUIInputEvent.INPUT = "input"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __typeError = (msg) => { throw TypeError(msg); }; 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; }; var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg); var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method); var _UUIInputElement_instances, onKeyDown_fn; let UUIInputElement = class extends UUIFormControlMixin( LabelMixin("", LitElement), "" ) { constructor() { super(); __privateAdd(this, _UUIInputElement_instances); this.minlengthMessage = "This field need more characters"; this.maxlengthMessage = "This field exceeds the allowed amount of characters"; this.disabled = false; this.readonly = false; this.placeholder = ""; this.autoWidth = false; this.inputMode = ""; this._type = "text"; this.addEventListener("mousedown", () => { this.style.setProperty("--uui-show-focus-outline", "0"); }); this.addEventListener("blur", () => { this.style.setProperty("--uui-show-focus-outline", ""); }); this.addEventListener("keydown", __privateMethod(this, _UUIInputElement_instances, onKeyDown_fn)); this.addValidator( "tooShort", () => this.minlengthMessage, () => !!this.minlength && String(this.value).length < this.minlength ); this.addValidator( "tooLong", () => this.maxlengthMessage, () => !!this.maxlength && String(this.value).length > this.maxlength ); this.updateComplete.then(() => { this.addFormControlElement(this._input); }); } get type() { return this._type; } set type(value) { this._type = value; } /** * Removes focus from the input. */ async blur() { await this.updateComplete; this._input.blur(); } /** * This method enables <label for="..."> to focus the input */ async focus() { await this.updateComplete; this._input.focus(); } /** * Selects all the text in the input. */ async select() { await this.updateComplete; this._input.select(); } getFormElement() { return this.shadowRoot?.querySelector("input"); } onInput(e) { e.stopPropagation(); this.value = e.target.value; this.dispatchEvent(new UUIInputEvent(UUIInputEvent.INPUT)); } onChange(e) { e.stopPropagation(); this.pristine = false; this.dispatchEvent(new UUIInputEvent(UUIInputEvent.CHANGE)); } renderPrepend() { return html`<slot name="prepend"></slot>`; } renderAppend() { return html`<slot name="append"></slot>`; } render() { return html` ${this.renderPrepend()} ${this.autoWidth ? this.renderInputWithAutoWidth() : this.renderInput()} ${this.renderAppend()} `; } renderInputWithAutoWidth() { return html`<div id="control"> ${this.renderInput()}${this.renderAutoWidthBackground()} </div>`; } renderInput() { return html`<input id="input" .type=${this.type} .value=${this.value} .name=${this.name} pattern=${ifDefined(this.pattern)} min=${ifDefined(this.min)} max=${ifDefined(this.max)} step=${ifDefined(this.step)} spellcheck=${this.spellcheck} autocomplete=${ifDefined(this.autocomplete)} placeholder=${ifDefined(this.placeholder)} aria-label=${ifDefined(this.label)} inputmode=${ifDefined(this.inputMode)} ?disabled=${this.disabled} ?autofocus=${this.autofocus} ?required=${this.required} ?readonly=${this.readonly} @input=${this.onInput} @change=${this.onChange} />`; } renderAutoWidthBackground() { return html` <div id="auto" aria-hidden="true">${this.renderText()}</div>`; } renderText() { return html`${this.value.length > 0 ? this.value : this.placeholder}`; } }; _UUIInputElement_instances = new WeakSet(); onKeyDown_fn = function(e) { if (this.type !== "color" && e.key == "Enter") { this.submit(); } }; /** * 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} */ UUIInputElement.formAssociated = true; UUIInputElement.styles = [ css` :host { position: relative; display: inline-flex; align-items: stretch; height: var(--uui-input-height, var(--uui-size-11,33px)); text-align: left; box-sizing: border-box; background-color: var( --uui-input-background-color, var(--uui-color-surface,#fff) ); border: var(--uui-input-border-width, 1px) solid var(--uui-input-border-color, var(--uui-color-border,#d8d7d9)); border-radius: var(--uui-border-radius,3px); --uui-button-height: 100%; --auto-width-text-margin-right: 0; --auto-width-text-margin-left: 0; } #control { position: relative; display: flex; flex-direction: column; align-items: stretch; justify-content: center; flex-grow: 1; } #auto { border: 0 1px solid transparent; visibility: hidden; white-space: pre; z-index: -1; height: 0px; padding: 0 var(--uui-size-space-3,9px); margin: 0 var(--auto-width-text-margin-right) 0 var(--auto-width-text-margin-left); } :host([auto-width]) #input { width: 10px; min-width: 100%; } :host(:hover) { border-color: var( --uui-input-border-color-hover, var(--uui-color-border-standalone,#c2c2c2) ); } /* TODO: Fix so we dont get double outline when there is focus on things in the slot. */ :host(:focus-within) { border-color: var( --uui-input-border-color-focus, var(--uui-color-border-emphasis,#a1a1a1) ); outline: calc(2px * var(--uui-show-focus-outline, 1)) solid var(--uui-color-focus,#3879ff); } :host(:focus) { border-color: var( --uui-input-border-color-focus, var(--uui-color-border-emphasis,#a1a1a1) ); } :host([disabled]) { background-color: var( --uui-input-background-color-disabled, var(--uui-color-disabled,#f3f3f5) ); border-color: var( --uui-input-border-color-disabled, var(--uui-color-disabled,#f3f3f5) ); color: var(--uui-color-disabled-contrast,#c4c4c4); } :host([disabled]) input { -webkit-text-fill-color: var( --uui-color-disabled-contrast,#c4c4c4 ); /* required on Safari and IOS */ } :host([readonly]) { background-color: var( --uui-input-background-color-readonly, var(--uui-color-disabled,#f3f3f5) ); border-color: var( --uui-input-border-color-readonly, var(--uui-color-disabled-standalone,rgb(226, 226, 226)) ); } :host(:not([pristine]):invalid), /* polyfill support */ :host(:not([pristine])[internals-invalid]) { border-color: var(--uui-color-invalid,#d42054); } input { font-family: inherit; padding: var(--uui-size-1,3px) var(--uui-size-space-3,9px); font-size: inherit; color: inherit; border-radius: var(--uui-border-radius,3px); box-sizing: border-box; border: none; background: none; width: 100%; height: inherit; text-align: inherit; outline: none; } input[type='password']::-ms-reveal { display: none; } /* TODO: make sure color looks good, or remove it as an option as we want to provide color-picker component */ input[type='color'] { width: 30px; padding: 0; border: none; } ::slotted(uui-input), ::slotted(uui-input-lock) { height: 100%; --uui-input-border-width: 0; } ` ]; __decorateClass([ property() ], UUIInputElement.prototype, "min", 2); __decorateClass([ property({ type: Number }) ], UUIInputElement.prototype, "minlength", 2); __decorateClass([ property({ type: String, attribute: "minlength-message" }) ], UUIInputElement.prototype, "minlengthMessage", 2); __decorateClass([ property() ], UUIInputElement.prototype, "max", 2); __decorateClass([ property({ type: Number }) ], UUIInputElement.prototype, "maxlength", 2); __decorateClass([ property({ type: String, attribute: "maxlength-message" }) ], UUIInputElement.prototype, "maxlengthMessage", 2); __decorateClass([ property({ type: Number }) ], UUIInputElement.prototype, "step", 2); __decorateClass([ property({ type: Boolean, reflect: true }) ], UUIInputElement.prototype, "disabled", 2); __decorateClass([ property({ type: Boolean, reflect: true }) ], UUIInputElement.prototype, "readonly", 2); __decorateClass([ property() ], UUIInputElement.prototype, "placeholder", 2); __decorateClass([ property() ], UUIInputElement.prototype, "autocomplete", 2); __decorateClass([ property({ type: Boolean, reflect: true, attribute: "auto-width" }) ], UUIInputElement.prototype, "autoWidth", 2); __decorateClass([ property({ type: String }) ], UUIInputElement.prototype, "type", 1); __decorateClass([ property({ type: String }) ], UUIInputElement.prototype, "pattern", 2); __decorateClass([ property({ type: String }) ], UUIInputElement.prototype, "inputMode", 2); __decorateClass([ query("#input") ], UUIInputElement.prototype, "_input", 2); UUIInputElement = __decorateClass([ defineElement("uui-input") ], UUIInputElement); export { UUIInputElement, UUIInputEvent };