# presentation/renderers/walk.renderer.ts

> 120 lines of code and 15 definitions.

Tree: Site tree
Language: typescript
Layer: product
Canonical: https://banes-lab.com/anatomy/tree#file-presentation-renderers-walk-renderer-ts
Source text: https://banes-lab.com/assets/sources/source.39f4c4084ddf535526f28a639d7d516099b71c0911bba6d16aec70c8e84ac659.generated.txt

## Definitions

- `createCanvas` (lexical_declaration, line 83)
- `enableInspector` (lexical_declaration, line 72, exported)
- `renderWalk` (lexical_declaration, line 108, exported)
- `bindPointer` (lexical_declaration, line 17)
- `bindHover` (lexical_declaration, line 35)
- `pressed` (lexical_declaration, line 18)
- `start` (lexical_declaration, line 23)
- `index` (lexical_declaration, line 37)
- `leaving` (lexical_declaration, line 58)
- `inspector` (lexical_declaration, line 77, exported)
- `holder` (lexical_declaration, line 88)
- `panel` (lexical_declaration, line 89)
- `status` (lexical_declaration, line 109, exported)
- `expand` (lexical_declaration, line 110, exported)
- `header` (lexical_declaration, line 113, exported)

## Uses

- [core/factories/element.factory.ts](https://banes-lab.com/source/tree/core/factories/element.factory.ts.md)
- [core/loaders/walk.loader.ts](https://banes-lab.com/source/tree/core/loaders/walk.loader.ts.md)
- [core/predicates/document.predicate.ts](https://banes-lab.com/source/tree/core/predicates/document.predicate.ts.md)
- [presentation/components/walk.component.ts](https://banes-lab.com/source/tree/presentation/components/walk.component.ts.md)

## Used by

- [presentation/renderers/block.renderer.ts](https://banes-lab.com/source/tree/presentation/renderers/block.renderer.ts.md)

## Source

```typescript
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",
    });
};
```
