@thi.ng/layout
Version:
Configurable nested 2D grid layout generators
102 lines (101 loc) • 2.55 kB
JavaScript
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
};