Files
dees-catalog/ts_web/elements/dees-input-phone.ts

133 lines
4.1 KiB
TypeScript
Raw Normal View History

2023-10-23 16:13:02 +02:00
import {
customElement,
type TemplateResult,
property,
state,
2023-10-23 16:13:02 +02:00
html,
css,
cssManager,
} from '@design.estate/dees-element';
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')
export class DeesInputPhone extends DeesInputBase<DeesInputPhone> {
2023-10-23 16:13:02 +02:00
// 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`
<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
}
}