@paperbits/common
Version:
Paperbits common components.
107 lines (87 loc) • 2.67 kB
text/typescript
import * as Html from "../html";
import { EventManager } from "../events";
import { View } from "./view";
export class ViewStack {
private stack: View[];
constructor(private readonly eventManager: EventManager) {
this.stack = [];
this.eventManager.addEventListener("onPointerDown", this.onPointerDown.bind(this));
this.eventManager.addEventListener("onEscape", this.popView.bind(this));
}
private onPointerDown(event: MouseEvent): void {
const targetElement = <HTMLElement>event.target;
this.runHitTest(targetElement);
}
public runHitTest(targetElement: HTMLElement): void {
const views = [...this.stack]; // clone array
for (const view of views.reverse()) {
let hit: boolean;
if (view.hitTest) {
hit = view.hitTest(targetElement);
}
else {
hit = !!Html.closest(targetElement, (node: HTMLElement) => node === view.element);
}
if (hit) {
break;
}
this.stack.pop();
view.close();
}
}
/**
* Pushes the view to the top of the stack.
*/
public pushView(view: View): void {
this.stack.push(view);
}
/**
* Removes the top view from the stack.
*/
public popView(): void {
const view = this.stack.pop();
if (!view) {
this.eventManager.dispatchEvent("onTopLevelEscape");
return;
}
view.close();
if (view.returnFocusTo) {
view.returnFocusTo.focus();
}
}
/**
* Removes specified view and all its children from the stack.
* @param view - A view to be removed.
*/
public removeView(view: View): void {
if (!this.stack.includes(view)) {
return;
}
let topView: View;
do {
topView = this.stack.pop();
topView.close();
}
while (!topView || topView !== view);
if (!topView) {
this.eventManager.dispatchEvent("onTopLevelEscape");
return;
}
if (topView.returnFocusTo) {
view.returnFocusTo.focus();
}
}
/**
* Returns all views in the stack.
*/
public getViews(): View[] {
return [...this.stack]; // clone array
}
/**
* Clears the view stack.
*/
public clear(): void {
this.stack.forEach(view => view.close());
this.stack = [];
}
}