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