/ ink / events / terminal-event.ts
terminal-event.ts
  1  import { Event } from './event.js'
  2  
  3  type EventPhase = 'none' | 'capturing' | 'at_target' | 'bubbling'
  4  
  5  type TerminalEventInit = {
  6    bubbles?: boolean
  7    cancelable?: boolean
  8  }
  9  
 10  /**
 11   * Base class for all terminal events with DOM-style propagation.
 12   *
 13   * Extends Event so existing event types (ClickEvent, InputEvent,
 14   * TerminalFocusEvent) share a common ancestor and can migrate later.
 15   *
 16   * Mirrors the browser's Event API: target, currentTarget, eventPhase,
 17   * stopPropagation(), preventDefault(), timeStamp.
 18   */
 19  export class TerminalEvent extends Event {
 20    readonly type: string
 21    readonly timeStamp: number
 22    readonly bubbles: boolean
 23    readonly cancelable: boolean
 24  
 25    private _target: EventTarget | null = null
 26    private _currentTarget: EventTarget | null = null
 27    private _eventPhase: EventPhase = 'none'
 28    private _propagationStopped = false
 29    private _defaultPrevented = false
 30  
 31    constructor(type: string, init?: TerminalEventInit) {
 32      super()
 33      this.type = type
 34      this.timeStamp = performance.now()
 35      this.bubbles = init?.bubbles ?? true
 36      this.cancelable = init?.cancelable ?? true
 37    }
 38  
 39    get target(): EventTarget | null {
 40      return this._target
 41    }
 42  
 43    get currentTarget(): EventTarget | null {
 44      return this._currentTarget
 45    }
 46  
 47    get eventPhase(): EventPhase {
 48      return this._eventPhase
 49    }
 50  
 51    get defaultPrevented(): boolean {
 52      return this._defaultPrevented
 53    }
 54  
 55    stopPropagation(): void {
 56      this._propagationStopped = true
 57    }
 58  
 59    override stopImmediatePropagation(): void {
 60      super.stopImmediatePropagation()
 61      this._propagationStopped = true
 62    }
 63  
 64    preventDefault(): void {
 65      if (this.cancelable) {
 66        this._defaultPrevented = true
 67      }
 68    }
 69  
 70    // -- Internal setters used by the Dispatcher
 71  
 72    /** @internal */
 73    _setTarget(target: EventTarget): void {
 74      this._target = target
 75    }
 76  
 77    /** @internal */
 78    _setCurrentTarget(target: EventTarget | null): void {
 79      this._currentTarget = target
 80    }
 81  
 82    /** @internal */
 83    _setEventPhase(phase: EventPhase): void {
 84      this._eventPhase = phase
 85    }
 86  
 87    /** @internal */
 88    _isPropagationStopped(): boolean {
 89      return this._propagationStopped
 90    }
 91  
 92    /** @internal */
 93    _isImmediatePropagationStopped(): boolean {
 94      return this.didStopImmediatePropagation()
 95    }
 96  
 97    /**
 98     * Hook for subclasses to do per-node setup before each handler fires.
 99     * Default is a no-op.
100     */
101    _prepareForTarget(_target: EventTarget): void {}
102  }
103  
104  export type EventTarget = {
105    parentNode: EventTarget | undefined
106    _eventHandlers?: Record<string, unknown>
107  }