Layout
fitIconSize, centerBoard, createGrid, distribute: sizing and point math for boards made of icons.
Desktop icons are placed at absolute x/y points, so layout in filedom means
computing points: pick an icon size, center a board, then map cells to points.
These helpers do that math.
fitIconSize
Returns the largest icon size that fits cols × rows icon units inside a
fraction of the desktop, clamped to [min, max]. It takes the smaller of the
width-limited and height-limited sizes, so the board fits on both axes.
import { fitIconSize, MAX_DESKTOP_ICON_SIZE } from 'filedom';
const iconSize = fitIconSize({
metrics, // SystemMetrics (width/height used)
widthFraction: 0.6, // board may occupy 60% of screen width…
heightFraction: 0.8, // …and 80% of its height
cols: 10,
rows: 20,
min: 24,
// max defaults to MAX_DESKTOP_ICON_SIZE (128)
// gap: px shaved off so the *icon* (not the cell) is the fitted size
});
cols and rows are icon units and may be fractional. Solitaire passes
cols * cardAspect * 1.18 to allow for non-square cards and spacing.
centerBoard
Returns the top-left offset that centers a boardW × boardH box on the desktop.
top reserves that many pixels at the top (for the menu bar or a HUD row) and
excludes them from centering. Results are floored to whole pixels.
const { offsetX, offsetY } = centerBoard({ metrics, boardW, boardH, top: 64 });
createGrid
A uniform grid addressed by cell. cellCenter(col, row) gives the point to move
a sprite to, and pointToCell(x, y) reverses it for click hit-testing. Both are
pure functions. The grid has no knowledge of icons, stages or parking, and
computing a point never mounts or unmounts anything.
import { createGrid } from 'filedom';
const grid = createGrid({
cols: 8, rows: 8,
cell: iconSize + gap, // per-cell pitch, not the icon size
origin: { x: offsetX, y: offsetY }, // top-left of cell (0,0)'s box
});
grid.cellCenter(3, 4); // -> { x, y } for a sprite's `at`
grid.indexCenter(12); // row-major flat index variant
grid.pointToCell(px, py); // -> { col, row } | null (outside the board)
grid.boardRect; // { x, y, width, height } for frames/backgrounds
boardRect spans cols × rows whole cells. If cell includes a gap, the
rectangle includes the gap after the last cell too.
distribute
Returns count evenly spaced points centered on center along one axis, each
at the fixed cross coordinate on the other axis. Spacing is center to center,
so a step smaller than the item width gives an overlapping fan, such as a hand
of cards. Flexbox can’t express this because its gap is positive only.
import { distribute } from 'filedom';
const handSlots = distribute({ count: 5, step: iconSize * 0.55, center: cx, cross: handY });
// axis: 'x' (default) or 'y'