@hypergrid/core
v0.0.0
Published
Framework-agnostic layout math for hypergrid: types, compaction, collision, coordinate transforms.
Downloads
148
Maintainers
Readme
@hypergrid/core
Framework-agnostic layout math for hypergrid: types, collision detection, compaction, and coordinate transforms. Zero runtime dependencies, zero DOM access.
If you're building a React app, you almost certainly want @hypergrid/react — it re-exports the core types and wires the math up to a hook. This package is the layer underneath, useful if you're writing bindings for another framework, doing server-side layout validation, or running compaction in a worker.
Install
pnpm add @hypergrid/coreLayout shape
A layout is just an array of integer-positioned items:
import type { GridLayout } from "@hypergrid/core";
const layout: GridLayout = [
{ id: "a", x: 0, y: 0, w: 4, h: 2 },
{ id: "b", x: 4, y: 0, w: 8, h: 3 },
{ id: "c", x: 0, y: 2, w: 4, h: 2, static: true },
];x and y are cell coordinates (origin top-left). w and h are spans. Optional minW / maxW / minH / maxH clamp resizes; static: true pins an item in place.
Movement and resize
moveItem and resizeItem produce a new layout with collisions resolved according to your config. Neither mutates the input.
import { moveItem, resizeItem } from "@hypergrid/core";
const next = moveItem(layout, "a", { x: 2, y: 1 }, { cols: 12 });
const grown = resizeItem(layout, "b", { w: 10, h: 4 }, { cols: 12 });Pass preventCollision: true in the options arg to reject moves that would overlap a static item instead of pushing other items out of the way.
Compaction
Four built-in compactors plus a registry:
import {
compact,
getCompactor,
verticalCompactor,
horizontalCompactor,
tightCompactor,
noCompactor,
} from "@hypergrid/core";
const packed = compact(layout, { cols: 12 }, "tight");
// "vertical" — pull every item up until it hits another item or y=0
// "horizontal" — pull every item left until it hits another item or x=0
// "tight" — top-left scan, fills holes across both axes
// "none" — no-opcompactExcept(layout, config, mode, exceptId) runs compaction on every item except one — used during an active drag so the item under your cursor doesn't snap underneath itself.
Collision primitives
import {
collides,
getFirstCollision,
getAllCollisions,
sortByPosition,
pushItemDown,
resolveCollisions,
withReplaced,
} from "@hypergrid/core";collides(a, b)— boolean rect overlap.getFirstCollision(layout, item)/getAllCollisions(layout, item)— query against the rest of the layout.sortByPosition(layout)— y-then-x ordering used by the compactors.pushItemDown(layout, item, config)— recursively push an item and everything it would hit further down.resolveCollisions(layout, moved, config, opts)— the shared engine behindmoveItemandresizeItem.withReplaced(layout, item)— immutable replace by id.
Validation
import { validateLayout, type ValidationResult } from "@hypergrid/core";
const result: ValidationResult = validateLayout(layout, { cols: 12 });
if (!result.ok) {
for (const err of result.errors) {
// err.kind is one of: "duplicate-id" | "out-of-bounds" | "negative-size" | "min-max-conflict"
}
}Useful for catching bad data at the boundary of your app (e.g. layouts loaded from a database).
License
MIT
