export interface ConfirmDialogOptions { icon?: string; title: string; message: string; confirmLabel?: string; cancelLabel?: string; danger?: boolean; } class ConfirmDialog extends HTMLElement { private resolver: ((value: boolean) => void) | null = null; connectedCallback(): void { this.style.display = 'none'; this.innerHTML = `
`; this.querySelector('.cd-backdrop')!.addEventListener('click', () => this.close(false)); this.querySelector('.cd-close')!.addEventListener('click', () => this.close(false)); document.addEventListener('keydown', (e: KeyboardEvent) => { if (e.key === 'Escape' && this.style.display !== 'none') this.close(false); }); } show(options: ConfirmDialogOptions): Promise { console.log( "Show dialog" ); this.querySelector('.cd-icon')!.textContent = options.icon ?? ''; this.querySelector('.cd-title')!.textContent = options.title; this.querySelector('.cd-message')!.textContent = options.message; const footer = this.querySelector('.cd-footer')!; footer.innerHTML = ''; if (options.cancelLabel) { const cancel = document.createElement('button'); cancel.className = 'cd-btn cd-btn-cancel'; cancel.textContent = options.cancelLabel; cancel.addEventListener('click', () => this.close(false)); footer.appendChild(cancel); } const confirm = document.createElement('button'); confirm.className = `cd-btn cd-btn-confirm${options.danger ? ' cd-btn-danger' : ''}`; confirm.textContent = options.confirmLabel ?? 'OK'; confirm.addEventListener('click', () => this.close(true)); footer.appendChild(confirm); this.style.display = ''; confirm.focus(); return new Promise(resolve => { this.resolver = resolve; }); } private close(value: boolean): void { this.style.display = 'none'; console.log( "Closing:", value ); if (this.resolver) { this.resolver(value); this.resolver = null; } } } customElements.define('confirm-dialog', ConfirmDialog); export function showConfirmDialog(options: ConfirmDialogOptions): Promise { let dialog = document.querySelector('confirm-dialog') as ConfirmDialog | null; if (!dialog) { dialog = document.createElement('confirm-dialog') as ConfirmDialog; document.body.appendChild(dialog); } return dialog.show(options); } // ── Input dialog ────────────────────────────────────────────────────────────── export interface InputDialogOptions { icon?: string; title: string; label: string; defaultValue?: string; confirmLabel?: string; cancelLabel?: string; } class InputDialog extends HTMLElement { private resolver: (( value: string | null ) => void) | null = null; connectedCallback(): void { this.style.display = 'none'; this.innerHTML = `
`; this.querySelector( '.cd-backdrop' )!.addEventListener( 'click', () => this._close( null ) ); this.querySelector( '.cd-close' )!.addEventListener( 'click', () => this._close( null ) ); document.addEventListener( 'keydown', ( e: KeyboardEvent ) => { if ( e.key === 'Escape' && this.style.display !== 'none' ) this._close( null ); if ( e.key === 'Enter' && this.style.display !== 'none' ) this._submit(); } ); } show( options: InputDialogOptions ): Promise { this.querySelector( '.cd-icon' )!.textContent = options.icon ?? ''; this.querySelector( '.cd-title' )!.textContent = options.title; ( this.querySelector( '.cd-input-label' ) as HTMLElement ).textContent = options.label; const input = this.querySelector( '.cd-input-field' ) as HTMLInputElement; input.value = options.defaultValue ?? ''; const footer = this.querySelector( '.cd-footer' )!; footer.innerHTML = ''; const cancel = document.createElement( 'button' ); cancel.className = 'cd-btn cd-btn-cancel'; cancel.textContent = options.cancelLabel ?? 'Cancel'; cancel.addEventListener( 'click', () => this._close( null ) ); footer.appendChild( cancel ); const confirm = document.createElement( 'button' ); confirm.className = 'cd-btn cd-btn-confirm'; confirm.textContent = options.confirmLabel ?? 'OK'; confirm.addEventListener( 'click', () => this._submit() ); footer.appendChild( confirm ); this.style.display = ''; input.focus(); input.select(); return new Promise( resolve => { this.resolver = resolve; } ); } private _submit(): void { const value = ( this.querySelector( '.cd-input-field' ) as HTMLInputElement ).value.trim(); this._close( value || null ); } private _close( value: string | null ): void { this.style.display = 'none'; if ( this.resolver ) { this.resolver( value ); this.resolver = null; } } } customElements.define( 'input-dialog', InputDialog ); export function showInputDialog( options: InputDialogOptions ): Promise { let dialog = document.querySelector( 'input-dialog' ) as InputDialog | null; if ( !dialog ) { dialog = document.createElement( 'input-dialog' ) as InputDialog; document.body.appendChild( dialog ); } return dialog.show( options ); }