| 
									
										
										
										
											2023-10-23 16:13:02 +02:00
										 |  |  | import { | 
					
						
							|  |  |  |   customElement, | 
					
						
							|  |  |  |   type TemplateResult, | 
					
						
							|  |  |  |   property, | 
					
						
							| 
									
										
										
										
											2025-06-19 11:39:16 +00:00
										 |  |  |   state, | 
					
						
							| 
									
										
										
										
											2023-10-23 16:13:02 +02:00
										 |  |  |   html, | 
					
						
							|  |  |  |   css, | 
					
						
							|  |  |  |   cssManager, | 
					
						
							|  |  |  | } from '@design.estate/dees-element'; | 
					
						
							| 
									
										
										
										
											2025-06-19 11:39:16 +00:00
										 |  |  | import * as domtools from '@design.estate/dees-domtools'; | 
					
						
							|  |  |  | import { DeesInputBase } from './dees-input-base.js'; | 
					
						
							| 
									
										
										
										
											2023-10-23 16:13:02 +02:00
										 |  |  | import { demoFunc } from './dees-input-phone.demo.js'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | declare global { | 
					
						
							|  |  |  |   interface HTMLElementTagNameMap { | 
					
						
							|  |  |  |     'dees-input-phone': DeesInputPhone; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @customElement('dees-input-phone') | 
					
						
							| 
									
										
										
										
											2025-06-19 11:39:16 +00:00
										 |  |  | export class DeesInputPhone extends DeesInputBase<DeesInputPhone> { | 
					
						
							| 
									
										
										
										
											2023-10-23 16:13:02 +02:00
										 |  |  |   // STATIC
 | 
					
						
							|  |  |  |   public static demo = demoFunc; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // INSTANCE
 | 
					
						
							| 
									
										
										
										
											2025-06-19 11:39:16 +00:00
										 |  |  |   @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`
 | 
					
						
							|  |  |  |       <div class="input-wrapper"> | 
					
						
							|  |  |  |         <dees-label .label=${this.label} .description=${this.description}></dees-label> | 
					
						
							|  |  |  |         <dees-input-text | 
					
						
							|  |  |  |           .value=${this.formattedPhone} | 
					
						
							|  |  |  |           .disabled=${this.disabled} | 
					
						
							|  |  |  |           .required=${this.required} | 
					
						
							|  |  |  |           .placeholder=${this.placeholder} | 
					
						
							|  |  |  |           @input=${(event: InputEvent) => this.handlePhoneInput(event)} | 
					
						
							|  |  |  |         ></dees-input-text> | 
					
						
							|  |  |  |       </div> | 
					
						
							|  |  |  |     `;
 | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   public firstUpdated(_changedProperties: Map<string | number | symbol, unknown>) { | 
					
						
							|  |  |  |     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); | 
					
						
							| 
									
										
										
										
											2023-10-23 16:13:02 +02:00
										 |  |  |   } | 
					
						
							|  |  |  | } |