import { customElement, type TemplateResult, property, state, html, css, cssManager, } from '@design.estate/dees-element'; import * as domtools from '@design.estate/dees-domtools'; import { DeesInputBase } from './dees-input-base.js'; import { demoFunc } from './dees-input-phone.demo.js'; declare global { interface HTMLElementTagNameMap { 'dees-input-phone': DeesInputPhone; } } @customElement('dees-input-phone') export class DeesInputPhone extends DeesInputBase { // STATIC public static demo = demoFunc; // INSTANCE @state() protected formattedPhone: string = ''; @property({ type: String }) public value: string = ''; @property({ type: String }) public placeholder: string = '+1 (555) 123-4567'; public static styles = [ ...DeesInputBase.baseStyles, cssManager.defaultStyles, css` /* Phone input specific styles can go here */ `, ]; public render(): TemplateResult { return html`
this.handlePhoneInput(event)} >
`; } public firstUpdated(_changedProperties: Map) { super.firstUpdated(_changedProperties); // Initialize formatted phone from value if (this.value) { this.formattedPhone = this.formatPhoneNumber(this.value); } // Subscribe to the inner input's changes const innerInput = this.shadowRoot.querySelector('dees-input-text') as any; if (innerInput && innerInput.changeSubject) { innerInput.changeSubject.subscribe(() => { this.changeSubject.next(this); }); } } private handlePhoneInput(event: InputEvent) { const input = event.target as HTMLInputElement; const cleanedValue = this.cleanPhoneNumber(input.value); const formatted = this.formatPhoneNumber(cleanedValue); // Update the input with formatted value if (input.value !== formatted) { const cursorPosition = input.selectionStart || 0; input.value = formatted; // Try to maintain cursor position intelligently const newCursorPos = this.calculateCursorPosition(cleanedValue, formatted, cursorPosition); input.setSelectionRange(newCursorPos, newCursorPos); } this.formattedPhone = formatted; this.value = cleanedValue; this.changeSubject.next(this); } private cleanPhoneNumber(value: string): string { // Remove all non-numeric characters return value.replace(/\D/g, ''); } private formatPhoneNumber(value: string): string { // Basic US phone number formatting // This can be enhanced to support international formats const cleaned = this.cleanPhoneNumber(value); if (cleaned.length === 0) return ''; if (cleaned.length <= 3) return cleaned; if (cleaned.length <= 6) return `(${cleaned.slice(0, 3)}) ${cleaned.slice(3)}`; if (cleaned.length <= 10) return `(${cleaned.slice(0, 3)}) ${cleaned.slice(3, 6)}-${cleaned.slice(6)}`; // For numbers longer than 10 digits, format as international return `+${cleaned.slice(0, cleaned.length - 10)} (${cleaned.slice(-10, -7)}) ${cleaned.slice(-7, -4)}-${cleaned.slice(-4)}`; } private calculateCursorPosition(cleaned: string, formatted: string, oldPos: number): number { // Simple cursor position calculation // Count how many formatting characters are before the cursor let formattingChars = 0; for (let i = 0; i < oldPos && i < formatted.length; i++) { if (!/\d/.test(formatted[i])) { formattingChars++; } } return Math.min(oldPos + formattingChars, formatted.length); } public getValue(): string { return this.value; } public setValue(value: string): void { this.value = value; this.formattedPhone = this.formatPhoneNumber(value); } }