presentation/renderers/walk.renderer.ts
presentation/renderers/walk.renderer.ts is a file in Bane's Lab Site. 120 lines of code and 15 definitions.
import { REFERENCE_HIDE_MS, REFERENCE_SHOW_MS } from "#configuration/constants/reference.constants";
import { STEPS_LABEL, VIEW_FULL_WALK_TITLE, WALK_MISSING } from "#configuration/strings/walk.strings";
import { createElement, createVectorElement } from "#core/factories/element.factory";
import { createStatus, runGhost } from "#presentation/components/walk.component";
import { DIAGRAM_FILLED } from "#core/ids/diagram.ids";
import { DRAG_TOLERANCE_PX } from "#configuration/constants/anatomy.constants";
import { ESCAPE_KEY } from "#configuration/constants/overlay.constants";
import type { PanelHandle } from "#types/panel.types";
import type { WalkBlock } from "#types/block.types";
import type { WalkCellView } from "#types/anatomy.types";
import { WalkInspector } from "#presentation/components/step.component";
import { createExpandButton } from "#presentation/components/overlay.component";
import { createPanel } from "#presentation/components/panel.component";
import { isStaticRender } from "#core/predicates/document.predicate";
import { loadWalk } from "#core/loaders/walk.loader";
const bindPointer = function bindPointer(element: HTMLElement, inspector: WalkInspector): void {
let pressed: { readonly x: number; readonly y: number } | null = null;
element.addEventListener("pointerdown", (event) => {
pressed = { x: event.clientX, y: event.clientY };
});
element.addEventListener("pointerup", (event) => {
const start = pressed;
pressed = null;
if (start === null || Math.hypot(event.clientX - start.x, event.clientY - start.y) > DRAG_TOLERANCE_PX) {
return;
}
const index = inspector.indexAt(document.elementFromPoint(event.clientX, event.clientY));
if (index !== -1) {
inspector.show(index, true);
}
});
};
const bindHover = function bindHover(element: HTMLElement, inspector: WalkInspector): void {
element.addEventListener("mouseover", (event) => {
const index = inspector.indexAt(event.target);
if (index !== -1) {
inspector.schedule(() => {
inspector.show(index, true);
}, REFERENCE_SHOW_MS);
}
});
element.addEventListener("mouseout", (event) => {
if (inspector.indexAt(event.target) === -1 || inspector.holds(event.relatedTarget)) {
return;
}
inspector.schedule(() => {
inspector.hide();
}, REFERENCE_HIDE_MS);
});
document.addEventListener("mouseover", (event) => {
if (inspector.isOpen() && inspector.holds(event.target)) {
inspector.cancel();
}
});
document.addEventListener("mouseout", (event) => {
const leaving = inspector.isOpen() && inspector.holds(event.target) && !inspector.holds(event.relatedTarget);
if (leaving && inspector.indexAt(event.relatedTarget) === -1) {
inspector.schedule(() => {
inspector.hide();
}, REFERENCE_HIDE_MS);
}
});
document.addEventListener("keydown", (event) => {
if (event.key === ESCAPE_KEY) {
inspector.hide();
}
});
};
export const enableInspector = function enableInspector(
element: HTMLElement,
status: HTMLElement,
cells: readonly WalkCellView[],
): void {
const inspector = new WalkInspector(element, status, cells);
void runGhost(element);
bindHover(element, inspector);
bindPointer(element, inspector);
};
const createCanvas = function createCanvas(
block: WalkBlock,
className: string,
status: HTMLElement | null,
): HTMLElement {
const holder = createElement("div");
const panel: PanelHandle = createPanel(holder, className, "fill");
if (isStaticRender()) {
return panel.element;
}
void loadWalk(block.walk).then((walk) => {
if (walk === null) {
holder.replaceChildren(createElement("p", { className: "diagram-missing", text: WALK_MISSING }));
} else {
holder.replaceChildren(createVectorElement(walk.markup));
if (status !== null) {
enableInspector(panel.element, status, walk.cells);
}
}
panel.reset();
panel.element.dispatchEvent(new Event(DIAGRAM_FILLED));
});
return panel.element;
};
export const renderWalk = function renderWalk(block: WalkBlock): Element {
const status = createStatus();
const expand = createExpandButton("diagram-expand", VIEW_FULL_WALK_TITLE, block.caption, () =>
createCanvas(block, "diagram-full", null),
);
const header = createElement("figcaption", {
children: [
createElement("span", { className: "diagram-caption", text: block.caption }),
createElement("span", { className: "walk-steps", text: String(block.walk.steps) + STEPS_LABEL }),
expand,
],
className: "diagram-header",
});
return createElement("figure", {
children: [header, createCanvas(block, "diagram-canvas", status), status],
className: "diagram-figure walk-figure",
});
};