UNPKG

@thi.ng/layout

Version:

Configurable nested 2D grid layout generators

102 lines (101 loc) 2.55 kB
import { isNumber } from "@thi.ng/checks/is-number"; import { layoutBox } from "./box.js"; const __DEFAULT_SPANS = [1, 1]; class GridLayout { parent; cols; width; x; y; cellW; cellH; cellWG; cellHG; gapX; gapY; currCol; currRow; rows; constructor(parent, x, y, width, cols, rowH, gapX, gapY = gapX) { this.parent = parent; this.cols = cols; this.x = x; this.y = y; this.width = width; this.cellW = (width - (cols - 1) * gapX) / cols; this.cellH = rowH; this.cellWG = this.cellW + gapX; this.cellHG = rowH + gapY; this.gapX = gapX; this.gapY = gapY; this.currCol = 0; this.currRow = 0; this.rows = 0; } get height() { return this.y + this.rows * this.cellHG; } colsForWidth(w) { return Math.ceil(w / this.cellWG); } rowsForHeight(h) { return Math.ceil(h / this.cellHG); } spanForSize(w, h) { const size = isNumber(w) ? [w, h ?? w] : w; return [this.colsForWidth(size[0]), this.rowsForHeight(size[1])]; } next(spans = __DEFAULT_SPANS) { const { cellWG, cellHG, gapX, gapY, cols } = this; const cspan = Math.min(spans[0], cols); const rspan = spans[1]; if (this.currCol > 0) { if (this.currCol + cspan > cols) { this.currCol = 0; this.currRow = this.rows; } } else { this.currRow = this.rows; } const h = rspan * cellHG - gapY; const cell = layoutBox( this.x + this.currCol * cellWG, this.y + this.currRow * cellHG, cspan * cellWG - gapX, h, this.cellW, this.cellH, gapX, gapY, [cspan, rspan] ); this.propagateSize(rspan); this.currCol = Math.min(this.currCol + cspan, cols) % cols; return cell; } // TODO add optional colspan arg, fix rounding nextSquare() { const box = this.next([1, Math.ceil(this.cellW / this.cellHG) + 1]); box.h = box.w; return box; } nest(cols, spans, gapX = this.gapX, gapY = this.gapY) { const { x, y, w } = this.next(spans); return new GridLayout(this, x, y, w, cols, this.cellH, gapX, gapY); } /** * Updates max rows used in this layout and all of its parents. * * @param rspan - */ propagateSize(rspan) { this.rows = Math.max(this.rows, this.currRow + rspan); this.parent?.propagateSize(this.rows); } } const gridLayout = (x, y, width, cols = 1, rowH = 16, gapX = 4, gapY = gapX) => new GridLayout(null, x, y, width, cols, rowH, gapX, gapY); export { GridLayout, __DEFAULT_SPANS, gridLayout };