schedgrid/dom

Bind a scroll container to the engine and hit-test pointer events.

import { bindScrollElement, viewportPoint } from 'schedgrid/dom';

bindScrollElement(grid, element, options?)

Wires a scroll container to the engine in both directions and returns the unbind function.

  • element → grid. scroll events become setScroll and size changes (through ResizeObserver) become setViewport, both read once up front.
  • grid → element. When something else moves the grid's scroll (a canvas twin handling wheel events, a "scroll to today" button), the element follows. The resulting scroll event reports the same offset, which setScroll ignores by value, so the loop closes without a guard flag.

On bind the grid wins: the element is moved to the grid's current scroll offset rather than asked where it is. That is what makes createGrid's scroll option survive being bound to an element, and what carries the offset across a rebind onto a fresh element. If the element clamps the write to its own maximum, the grid adopts the clamped value.

const unbind = bindScrollElement(grid, scroller);
// later
unbind();

Prop

Type

The binder only needs a slice of HTMLElement, so it can drive any scrollable and be stubbed in tests without a DOM:

Prop

Type

viewportPoint(element, event)

Pointer position relative to the scroll container's padding box, which is the viewport space grid.indexAt and grid.cellAt expect.

scroller.addEventListener('pointermove', (e) => {
  const cell = grid.cellAt(viewportPoint(scroller, e));
  // { row, column } or null outside the content
});

Pinned bands are resolved before scroll is applied, so a pointer over the header row reports the header row wherever the user has scrolled.