175 lines
5.4 KiB
TypeScript
175 lines
5.4 KiB
TypeScript
import * as THREE from "three";
|
|
import {
|
|
beginHistoryWindowDrag,
|
|
bringHistoryWindowToFront,
|
|
copyTextToClipboard,
|
|
destroyHistoryWindow,
|
|
endHistoryWindowDrag,
|
|
openHistoryWindow,
|
|
refreshHistoryWindows,
|
|
updateHistoryWindowDrag,
|
|
} from "./viewerHistoryManager";
|
|
import type { HistoryWindowState } from "./viewerHudState";
|
|
import type { Selectable, WorldState } from "./viewerTypes";
|
|
|
|
export interface ViewerHistoryWindowContext {
|
|
historyWindows: HistoryWindowState[];
|
|
getWorld: () => WorldState | undefined;
|
|
getHistoryWindowCounter: () => number;
|
|
setHistoryWindowCounter: (value: number) => void;
|
|
getHistoryWindowZCounter: () => number;
|
|
setHistoryWindowZCounter: (value: number) => void;
|
|
getHistoryWindowDragId: () => string | undefined;
|
|
setHistoryWindowDragId: (value: string | undefined) => void;
|
|
getHistoryWindowDragPointerId: () => number | undefined;
|
|
setHistoryWindowDragPointerId: (value: number | undefined) => void;
|
|
historyWindowDragOffset: THREE.Vector2;
|
|
renderRecentEvents: (entityKind: string, entityId: string) => string;
|
|
}
|
|
|
|
export class ViewerHistoryWindowController {
|
|
constructor(private readonly context: ViewerHistoryWindowContext) {}
|
|
|
|
openHistoryWindow(target: Selectable) {
|
|
const nextCounter = openHistoryWindow(
|
|
this.context.historyWindows,
|
|
target,
|
|
this.context.getHistoryWindowCounter() + 1,
|
|
(windowState) => this.bringHistoryWindowToFront(windowState),
|
|
() => this.refreshHistoryWindows(),
|
|
);
|
|
this.context.setHistoryWindowCounter(nextCounter);
|
|
}
|
|
|
|
refreshHistoryWindows() {
|
|
refreshHistoryWindows(
|
|
this.context.getWorld(),
|
|
this.context.historyWindows,
|
|
this.context.renderRecentEvents,
|
|
(id) => this.destroyHistoryWindow(id),
|
|
);
|
|
}
|
|
|
|
readonly onHistoryLayerClick = (event: MouseEvent) => {
|
|
const target = event.target;
|
|
if (!(target instanceof HTMLElement)) {
|
|
return;
|
|
}
|
|
|
|
const windowEl = target.closest<HTMLElement>("[data-history-window-id]");
|
|
const windowId = windowEl?.dataset.historyWindowId;
|
|
if (!windowId) {
|
|
return;
|
|
}
|
|
|
|
if (target.closest(".history-window-copy")) {
|
|
void this.copyHistoryWindowContent(windowId);
|
|
return;
|
|
}
|
|
|
|
if (target.closest(".history-window-close")) {
|
|
this.destroyHistoryWindow(windowId);
|
|
return;
|
|
}
|
|
|
|
const windowState = this.context.historyWindows.find((candidate) => candidate.id === windowId);
|
|
if (windowState) {
|
|
this.bringHistoryWindowToFront(windowState);
|
|
}
|
|
};
|
|
|
|
readonly onHistoryLayerPointerDown = (event: PointerEvent) => {
|
|
const target = event.target;
|
|
if (!(target instanceof HTMLElement)) {
|
|
return;
|
|
}
|
|
|
|
const windowEl = target.closest<HTMLElement>("[data-history-window-id]");
|
|
const windowId = windowEl?.dataset.historyWindowId;
|
|
if (!windowEl || !windowId) {
|
|
return;
|
|
}
|
|
|
|
const windowState = this.context.historyWindows.find((candidate) => candidate.id === windowId);
|
|
if (!windowState) {
|
|
return;
|
|
}
|
|
|
|
this.bringHistoryWindowToFront(windowState);
|
|
if (!target.closest(".history-window-header") || target.closest("button")) {
|
|
return;
|
|
}
|
|
|
|
const nextState = beginHistoryWindowDrag(
|
|
this.context.historyWindows,
|
|
this.context.historyWindowDragOffset,
|
|
event.pointerId,
|
|
windowId,
|
|
event.clientX,
|
|
event.clientY,
|
|
);
|
|
this.context.setHistoryWindowDragId(nextState.historyWindowDragId);
|
|
this.context.setHistoryWindowDragPointerId(nextState.historyWindowDragPointerId);
|
|
};
|
|
|
|
readonly onHistoryWindowPointerMove = (event: PointerEvent) => {
|
|
updateHistoryWindowDrag(
|
|
this.context.historyWindows,
|
|
this.context.getHistoryWindowDragId(),
|
|
this.context.getHistoryWindowDragPointerId(),
|
|
this.context.historyWindowDragOffset,
|
|
event.pointerId,
|
|
event.clientX,
|
|
event.clientY,
|
|
);
|
|
};
|
|
|
|
readonly onHistoryWindowPointerUp = (event: PointerEvent) => {
|
|
const nextState = endHistoryWindowDrag(
|
|
this.context.historyWindows,
|
|
this.context.getHistoryWindowDragId(),
|
|
this.context.getHistoryWindowDragPointerId(),
|
|
event.pointerId,
|
|
);
|
|
this.context.setHistoryWindowDragId(nextState.historyWindowDragId);
|
|
this.context.setHistoryWindowDragPointerId(nextState.historyWindowDragPointerId);
|
|
};
|
|
|
|
private destroyHistoryWindow(id: string) {
|
|
const nextState = destroyHistoryWindow(
|
|
this.context.historyWindows,
|
|
this.context.getHistoryWindowDragId(),
|
|
this.context.getHistoryWindowDragPointerId(),
|
|
id,
|
|
);
|
|
this.context.setHistoryWindowDragId(nextState.historyWindowDragId);
|
|
this.context.setHistoryWindowDragPointerId(nextState.historyWindowDragPointerId);
|
|
}
|
|
|
|
private async copyHistoryWindowContent(windowId: string) {
|
|
const windowState = this.context.historyWindows.find((candidate) => candidate.id === windowId);
|
|
if (!windowState?.text) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await copyTextToClipboard(windowState.text);
|
|
windowState.copyLabel = "Copied";
|
|
window.setTimeout(() => {
|
|
windowState.copyLabel = "Copy";
|
|
}, 1200);
|
|
} catch {
|
|
windowState.copyLabel = "Failed";
|
|
window.setTimeout(() => {
|
|
windowState.copyLabel = "Copy";
|
|
}, 1200);
|
|
}
|
|
}
|
|
|
|
private bringHistoryWindowToFront(windowState: HistoryWindowState) {
|
|
const nextZIndex = this.context.getHistoryWindowZCounter() + 1;
|
|
this.context.setHistoryWindowZCounter(nextZIndex);
|
|
bringHistoryWindowToFront(windowState, nextZIndex);
|
|
}
|
|
}
|