NavCube — a small 3D cube widget that reflects the View | View's camera orientation and lets the user fly the camera to any canonical axis-aligned, corner, or edge view by clicking the corresponding region of the cube.

Wrapped in the same floating-panel chrome as the other demo panels: drag-by-header, close button, reopen pill, layout persistence to localStorage.

Mirrors the shape of V2's NavCubePlugin but rendered with CSS 3D transforms — no second renderer or off-screen canvas is required, and the widget composes naturally with whatever overlay chrome an example already has.

The cube's local axes are fixed: +X is RIGHT, +Y is FRONT, +Z is TOP (CAD / BIM convention). For Y-up scenes a +90° rotation about X is folded into the view matrix so the +Y face still appears at the top.

Region of cube View flown to
Face Axis-aligned (front / right / …)
Edge between two faces Diagonal between those two axes
Corner Three-axis isometric corner view

Each region highlights on hover.

Subscribes to view.viewer.events.onCameraViewMatrixUpdated and recomputes the cube's CSS rotation each tick. Camera changes driven by anything (orbit, drag, flyTo, programmatic) keep the cube in sync.

destroy() removes the DOM, unsubscribes, and is idempotent.

Hierarchy (View Summary)

Constructors

Properties

_classPrefix: string
_closeBtn: HTMLButtonElement

Close button (clicked → hide(), surfaces the pill).

_container: HTMLElement
_destroyed: boolean = false
_header: HTMLElement

Drag handle inside the panel — usually the title row.

_minHeight: number
_minWidth: number
_modal: boolean
_panel: HTMLElement

Root panel element. Populated by subclass in _buildDom().

_pill: HTMLElement

Floating "reopen" pill shown while the panel is hidden.

_resizable: boolean
_storageKey: string
_tier: "default" | "view"
onLayoutChanged: EventEmitter<FloatingPanelBase, void> = ...

Fires while the user drags the panel (one notification per pointermove during a drag) and once when the drag ends.

Drag changes the panel's CSS left / top but never its size, so a ResizeObserver on any descendant element never sees it. Subclasses or hosts that need to track the panel's viewport position — for example a View embedded in the body whose shared WebGL canvas must follow the panel — should subscribe to this and re-read the panel's bounding rect.

onVisibilityChanged: EventEmitter<FloatingPanelBase, boolean> = ...

Fires when show() / hide() toggles the panel's visibility.

view: View

Accessors

Methods

  • Inject eight invisible drag handles (4 edges + 4 corners) into _panel and wire pointer events. Each handle pins the panel to absolute left / top on pointerdown (so subsequent size + position writes take effect over any CSS that uses right / bottom), then writes new width / height — and, for north / west edges, new left / top — on pointermove. onLayoutChanged fires per move and at drag-end; the saved layout is updated on drag-end.

    min-width / min-height enforced from _minWidth / _minHeight; max-width / max-height are released by setting them to "none" inline so user-driven size is not clipped by CSS bounds like max-height: calc(100vh - 32px).

    Returns void

  • Engage the header drag from an external pointerdown / move event so the panel begins following ev immediately. Used by the drag-to-undock gesture on docked View cells — the cell's own pointer listener detects motion-past-threshold, replaces the cell with this freshly-created floating panel, and hands the live pointer over via this method.

    The panel is reflowed so the pointer sits inside its header at (panelOffsetX, panelOffsetY) from the panel's top-left. Then setPointerCapture routes all subsequent pointermove / pointerup events for this pointerId to the panel's own header, where _bindChrome's existing listeners drive the drag — no parallel drag loop required.

    Parameters

    • ev: PointerEvent
    • OptionalpanelOffsetX: number
    • OptionalpanelOffsetY: number

    Returns void