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
|
|
|
}
|
|
|
|
}
|