Spaces:
Running on Zero
Running on Zero
| /** | |
| * ============================================================================ | |
| * MODAL OVERLAY - Base class for modal dialogs | |
| * ============================================================================ | |
| * | |
| * Creates a semi-transparent overlay that blocks background interaction, | |
| * providing a content container for modal windows like quiz panels, | |
| * confirmation dialogs, or information popups. | |
| * | |
| * USAGE: | |
| * class MyModal extends ModalOverlay { | |
| * protected createContent(): void { | |
| * // Add your content to this.content container | |
| * const text = this.scene.add.text(0, 0, 'Hello!'); | |
| * this.content.add(text); | |
| * } | |
| * } | |
| * const modal = new MyModal(scene, { width: 600, height: 400 }); | |
| * modal.show(); | |
| * modal.hide(); | |
| */ | |
| import Phaser from 'phaser'; | |
| export interface ModalConfig { | |
| /** Modal content area width */ | |
| width?: number; | |
| /** Modal content area height */ | |
| height?: number; | |
| /** Overlay background color (default: 0x000000) */ | |
| overlayColor?: number; | |
| /** Overlay opacity (default: 0.6) */ | |
| overlayAlpha?: number; | |
| /** Close on clicking overlay (default: false) */ | |
| closeOnOverlayClick?: boolean; | |
| /** Panel background color (default: 0x1a1a2e) */ | |
| panelColor?: number; | |
| /** Panel background opacity (default: 0.95) */ | |
| panelAlpha?: number; | |
| /** Panel border color (default: 0x6666aa) */ | |
| panelStrokeColor?: number; | |
| /** Panel border width (default: 2) */ | |
| panelStrokeWidth?: number; | |
| } | |
| export class ModalOverlay extends Phaser.GameObjects.Container { | |
| protected modalConfig: ModalConfig; | |
| protected overlay!: Phaser.GameObjects.Rectangle; | |
| protected panel!: Phaser.GameObjects.Rectangle; | |
| protected content!: Phaser.GameObjects.Container; | |
| protected isShowing: boolean = false; | |
| constructor(scene: Phaser.Scene, config?: ModalConfig) { | |
| super(scene, 0, 0); | |
| this.modalConfig = config ?? {}; | |
| scene.add.existing(this); | |
| this.setDepth(300); | |
| this.createOverlay(); | |
| this.setVisible(false); | |
| } | |
| // -- Public API -- | |
| /** Show the modal with fade-in. */ | |
| show(): void { | |
| // Cancel any pending hide tween to prevent it from overriding this show | |
| this.scene.tweens.killTweensOf(this); | |
| this.isShowing = true; | |
| this.setVisible(true); | |
| this.setAlpha(0); | |
| this.scene.tweens.add({ | |
| targets: this, | |
| alpha: 1, | |
| duration: 200, | |
| }); | |
| } | |
| /** Hide the modal with fade-out. */ | |
| hide(): void { | |
| this.scene.tweens.killTweensOf(this); | |
| this.scene.tweens.add({ | |
| targets: this, | |
| alpha: 0, | |
| duration: 200, | |
| onComplete: () => { | |
| this.isShowing = false; | |
| this.setVisible(false); | |
| this.emit('hidden'); | |
| }, | |
| }); | |
| } | |
| /** Check if modal is currently visible. */ | |
| getIsShowing(): boolean { | |
| return this.isShowing; | |
| } | |
| // -- Protected (override in subclasses) -- | |
| /** HOOK: Override to populate the content container with custom UI. */ | |
| protected createContent(): void { | |
| // Override in subclass | |
| } | |
| // -- Internal -- | |
| private createOverlay(): void { | |
| const cam = this.scene.cameras.main; | |
| const w = cam.width; | |
| const h = cam.height; | |
| const cfg = this.modalConfig; | |
| // Dark overlay | |
| this.overlay = this.scene.add.rectangle( | |
| w / 2, | |
| h / 2, | |
| w, | |
| h, | |
| cfg.overlayColor ?? 0x000000, | |
| cfg.overlayAlpha ?? 0.6, | |
| ); | |
| this.overlay.setOrigin(0.5); | |
| this.overlay.setInteractive(); // block clicks to background | |
| this.add(this.overlay); | |
| if (cfg.closeOnOverlayClick) { | |
| this.overlay.on('pointerdown', () => this.hide()); | |
| } | |
| // Panel background (colors and stroke are configurable) | |
| const panelW = cfg.width ?? 600; | |
| const panelH = cfg.height ?? 400; | |
| this.panel = this.scene.add.rectangle( | |
| w / 2, | |
| h / 2, | |
| panelW, | |
| panelH, | |
| cfg.panelColor ?? 0x1a1a2e, | |
| cfg.panelAlpha ?? 0.95, | |
| ); | |
| this.panel.setOrigin(0.5); | |
| this.panel.setStrokeStyle( | |
| cfg.panelStrokeWidth ?? 2, | |
| cfg.panelStrokeColor ?? 0x6666aa, | |
| ); | |
| this.add(this.panel); | |
| // Content container (positioned at panel center) | |
| this.content = this.scene.add.container(w / 2, h / 2); | |
| this.add(this.content); | |
| // Let subclasses populate content | |
| this.createContent(); | |
| } | |
| } | |