import { DeesElement, css, cssManager, customElement, html, property, type TemplateResult, } from '@design.estate/dees-element'; import { mobileComponentStyles } from '../../00componentstyles.js'; import { demoFunc } from './dees-mobile-input.demo.js'; export type InputType = 'text' | 'email' | 'password' | 'number' | 'tel' | 'url' | 'search'; declare global { interface HTMLElementTagNameMap { 'dees-mobile-input': DeesMobileInput; } } @customElement('dees-mobile-input') export class DeesMobileInput extends DeesElement { public static demo = demoFunc; @property({ type: String }) accessor type: InputType = 'text'; @property({ type: String }) accessor placeholder: string = ''; @property({ type: String }) accessor value: string = ''; @property({ type: Boolean }) accessor disabled: boolean = false; @property({ type: String }) accessor id: string = ''; @property({ type: String }) accessor name: string = ''; @property({ type: String }) accessor label: string = ''; @property({ type: String }) accessor error: string = ''; @property({ type: Boolean }) accessor required: boolean = false; @property({ type: String }) accessor autocomplete: string = ''; public static styles = [ cssManager.defaultStyles, mobileComponentStyles, css` :host { display: block; } .input-wrapper { display: flex; flex-direction: column; gap: 0.375rem; } label { font-size: 0.875rem; font-weight: 500; color: var(--dees-foreground); } label .required { color: var(--dees-danger); margin-left: 0.25rem; } input { width: 100%; height: 2.5rem; padding: 0 0.75rem; /* 16px minimum to prevent iOS zoom */ font-size: 1rem; line-height: 1.25rem; color: var(--dees-foreground); background: var(--dees-background); border: 1px solid var(--dees-input); border-radius: calc(var(--dees-radius) - 2px); outline: none; transition: all var(--dees-transition-fast); box-sizing: border-box; -webkit-appearance: none; font-family: inherit; } input:focus { outline: 2px solid transparent; outline-offset: 2px; border-color: var(--dees-ring); box-shadow: 0 0 0 2px var(--dees-background), 0 0 0 4px var(--dees-ring); } input:disabled { opacity: 0.5; cursor: not-allowed; background: var(--dees-muted); } input::placeholder { color: var(--dees-muted-foreground); } /* Remove number input spinners */ input::-webkit-outer-spin-button, input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; } input[type=number] { -moz-appearance: textfield; } /* Error state */ :host([error]) input, input.error { border-color: var(--dees-danger); } :host([error]) input:focus, input.error:focus { box-shadow: 0 0 0 2px var(--dees-background), 0 0 0 4px var(--dees-danger); } .error-message { font-size: 0.75rem; color: var(--dees-danger); margin-top: 0.25rem; } `, ]; private handleInput(e: Event) { const input = e.target as HTMLInputElement; this.value = input.value; this.dispatchEvent(new CustomEvent('input', { detail: { value: input.value }, bubbles: true, composed: true })); } private handleChange(e: Event) { const input = e.target as HTMLInputElement; this.value = input.value; this.dispatchEvent(new CustomEvent('change', { detail: { value: input.value }, bubbles: true, composed: true })); } private handleFocus() { // Emit input-focus for keyboard detection this.dispatchEvent(new CustomEvent('input-focus', { bubbles: true, composed: true })); } private handleBlur() { // Emit input-blur for keyboard detection this.dispatchEvent(new CustomEvent('input-blur', { bubbles: true, composed: true })); } public render(): TemplateResult { return html`
${this.label ? html` ` : ''} ${this.error ? html`
${this.error}
` : ''}
`; } /** * Focus the input programmatically */ public focus() { const input = this.shadowRoot?.querySelector('input'); input?.focus(); } /** * Blur the input programmatically */ public blur() { const input = this.shadowRoot?.querySelector('input'); input?.blur(); } }