Data grid / foldkit-data-grid

Focus

Focus is the grid's first piece of state: which cell is current, held in the Model by identity. Make it once per grid, then place its Bundle like any other.

ts
import { Option } from 'effect'
import { GridFocus } from 'foldkit-data-grid'

const Focus = GridFocus.make(columns)
// Focus.bundle is placed with foldkit-bundle; Focus.Model holds
// { current: Option<CellAddress> }, stored as null when nothing is focused.

const next = GridFocus.target(grid, {
  current: Option.some({ row: 'p1', column: 'sku' }),
  key: 'ArrowDown',
  modifiers: { shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
  pageRows: 10,
})
// Option.some({ row: 'p2', column: 'sku' }): dispatch Focus.Message.Focused with it
  • GridFocus.make(columns) builds the Bundle, its Model and its one Message, Focused. The stored column is one of these columns' ids, so a saved focus naming a removed column fails to decode. Focusing the focused cell again returns the Model it was given.
  • GridFocus.target(projection, options) is the cell a key moves to: arrows, Home and End (with Ctrl, the grid's corners), and PageUp and PageDown by pageRows. Under direction: 'rtl' left and right swap. It returns none for a key the grid leaves to the page, such as Enter, Tab, or anything with Shift, Alt or Meta; a key it handles always lands somewhere, staying put at an edge or before a row that has not loaded.
  • GridFocus.tabStop(projection, current) is the cell holding the grid's one tab stop: the focused cell while it is shown, else the first cell. A focused cell in a hidden column stays in the Model, so focus comes back when the column does.
  • GridFocus.cellId(gridId, address) is a cell's DOM id, unique across cells whatever their keys hold. The grid keeps DOM focus on its container and points at the current cell with aria-activedescendant, so a cell can scroll out of a virtual window without losing focus.

foldkit-mixins-data-grid wires these keys onto the drawn grid.