import { Component, EventEmitter, HostBinding, Inject, Input, Output, } from '@angular/core'; import { CommonModule, DOCUMENT } from '@angular/common'; import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; import { faRectangleXmark } from '@fortawesome/free-regular-svg-icons'; import { WindowConfig } from './window-config'; export enum Resize { Top = 't', Right = 'r', Bottom = 'b', Left = 'l', } @Component({ selector: 'fbi-window', standalone: true, imports: [CommonModule, FontAwesomeModule], templateUrl: './window.component.html', styleUrls: ['./window.component.scss'], }) export class WindowComponent { @HostBinding('style.height.px') height: number = 200; @HostBinding('style.width.px') width: number = 400; @HostBinding('style.left.px') x: number = 200; @HostBinding('style.top.px') y: number = 200; @HostBinding('style.visibility') visibility: string = 'hidden'; private _display: boolean = false; private get visible() { return this._display; } private set visible(value: boolean) { this._display = value; this.visibility = value ? 'visible' : 'hidden'; } @Input() config!: WindowConfig; @Output() closing = new EventEmitter(); dragging: boolean = false; resizing: boolean = false; faRectangleXmark = faRectangleXmark; Resize = Resize; constructor(@Inject(DOCUMENT) private _document: Document) { this.visible = true; } toggle = (display?: boolean) => (this.visible = display ?? !this.visible); hide = () => this.toggle(false); show = () => this.toggle(true); startDrag(event: MouseEvent): void { event.preventDefault(); const { innerHeight, innerWidth } = window; const { clientX, clientY } = event; const x = this.x; const y = this.y; const h = this.height; const w = this.width; const doc = this._document; this.dragging = true; const dragging = (e: MouseEvent) => { this.x = Math.min(Math.max(x + e.clientX - clientX, 0), innerWidth - w); this.y = Math.min(Math.max(y + e.clientY - clientY, 0), innerHeight - h); }; const dragEnd = (e: MouseEvent) => { this.dragging = false; doc.removeEventListener('mousemove', dragging); doc.removeEventListener('mouseup', dragEnd); }; doc.addEventListener('mousemove', dragging); doc.addEventListener('mouseup', dragEnd); } startResize(event: MouseEvent, anchor: Resize): void { event.preventDefault(); const { innerHeight, innerWidth } = window; const { clientX, clientY } = event; const x = this.x; const y = this.y; const h = this.height; const w = this.width; const doc = this._document; const minH = 32; const minW = 100; this.resizing = true; const resizing = (e: MouseEvent) => { const dx = e.clientX - clientX; const dy = e.clientY - clientY; switch (anchor) { case Resize.Top: this.y = Math.min(Math.max(y + dy, 0), y + h - minH); if (this.y > 0) this.height = Math.min( Math.max(h - dy, minH), innerHeight - this.y ); break; case Resize.Bottom: this.height = Math.min(Math.max(h + dy, minH), innerHeight - this.y); break; case Resize.Left: this.x = Math.min(Math.max(x + dx, 0), x + w - minW); if (this.x > 0) this.width = Math.min(Math.max(w - dx, minW), innerWidth - this.x); break; case Resize.Right: this.width = Math.min(Math.max(w + dx, minW), innerWidth - this.x); break; } }; const resizeEnd = (e: MouseEvent) => { this.resizing = false; doc.removeEventListener('mousemove', resizing); doc.removeEventListener('mouseup', resizeEnd); }; doc.addEventListener('mousemove', resizing); doc.addEventListener('mouseup', resizeEnd); } onClose(event: MouseEvent): void { event.preventDefault(); this.hide(); this.closing.emit(); } }