@thi.ng/layout 3.0.5 → 3.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1 -1
- package/api.js +0 -1
- package/box.js +12 -9
- package/checks.js +4 -1
- package/grid-layout.js +95 -103
- package/package.json +8 -6
- package/stacked-layout.js +86 -106
package/CHANGELOG.md
CHANGED
package/api.js
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/box.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
const layoutBox = (x, y, w, h, cw, ch, gap, span) => ({
|
|
2
|
+
x,
|
|
3
|
+
y,
|
|
4
|
+
w,
|
|
5
|
+
h,
|
|
6
|
+
cw,
|
|
7
|
+
ch,
|
|
8
|
+
gap,
|
|
9
|
+
span: span || [~~(w / cw), ~~(h / ch)]
|
|
10
10
|
});
|
|
11
|
+
export {
|
|
12
|
+
layoutBox
|
|
13
|
+
};
|
package/checks.js
CHANGED
package/grid-layout.js
CHANGED
|
@@ -1,107 +1,99 @@
|
|
|
1
1
|
import { isNumber } from "@thi.ng/checks/is-number";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
2
|
+
const __DEFAULT_SPANS = [1, 1];
|
|
3
|
+
class GridLayout {
|
|
4
|
+
parent;
|
|
5
|
+
cols;
|
|
6
|
+
width;
|
|
7
|
+
x;
|
|
8
|
+
y;
|
|
9
|
+
cellW;
|
|
10
|
+
cellH;
|
|
11
|
+
cellWG;
|
|
12
|
+
cellHG;
|
|
13
|
+
gap;
|
|
14
|
+
currCol;
|
|
15
|
+
currRow;
|
|
16
|
+
rows;
|
|
17
|
+
constructor(parent, x, y, width, cols, rowH, gap) {
|
|
18
|
+
this.parent = parent;
|
|
19
|
+
this.cols = cols;
|
|
20
|
+
this.x = x;
|
|
21
|
+
this.y = y;
|
|
22
|
+
this.width = width;
|
|
23
|
+
this.cellW = (width - (cols - 1) * gap) / cols;
|
|
24
|
+
this.cellH = rowH;
|
|
25
|
+
this.cellWG = this.cellW + gap;
|
|
26
|
+
this.cellHG = rowH + gap;
|
|
27
|
+
this.gap = gap;
|
|
28
|
+
this.currCol = 0;
|
|
29
|
+
this.currRow = 0;
|
|
30
|
+
this.rows = 0;
|
|
31
|
+
}
|
|
32
|
+
colsForWidth(w) {
|
|
33
|
+
return Math.ceil(w / this.cellWG);
|
|
34
|
+
}
|
|
35
|
+
rowsForHeight(h) {
|
|
36
|
+
return Math.ceil(h / this.cellHG);
|
|
37
|
+
}
|
|
38
|
+
spanForSize(w, h) {
|
|
39
|
+
const size = isNumber(w) ? [w, h] : w;
|
|
40
|
+
return [this.colsForWidth(size[0]), this.rowsForHeight(size[1])];
|
|
41
|
+
}
|
|
42
|
+
next(spans = __DEFAULT_SPANS) {
|
|
43
|
+
const { cellWG, cellHG, gap, cols } = this;
|
|
44
|
+
const cspan = Math.min(spans[0], cols);
|
|
45
|
+
const rspan = spans[1];
|
|
46
|
+
if (this.currCol > 0) {
|
|
47
|
+
if (this.currCol + cspan > cols) {
|
|
29
48
|
this.currCol = 0;
|
|
30
|
-
this.currRow =
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return Math.ceil(w / this.cellWG);
|
|
35
|
-
}
|
|
36
|
-
rowsForHeight(h) {
|
|
37
|
-
return Math.ceil(h / this.cellHG);
|
|
38
|
-
}
|
|
39
|
-
spanForSize(w, h) {
|
|
40
|
-
const size = isNumber(w) ? [w, h] : w;
|
|
41
|
-
return [this.colsForWidth(size[0]), this.rowsForHeight(size[1])];
|
|
42
|
-
}
|
|
43
|
-
next(spans = __DEFAULT_SPANS) {
|
|
44
|
-
const { cellWG, cellHG, gap, cols } = this;
|
|
45
|
-
const cspan = Math.min(spans[0], cols);
|
|
46
|
-
const rspan = spans[1];
|
|
47
|
-
if (this.currCol > 0) {
|
|
48
|
-
if (this.currCol + cspan > cols) {
|
|
49
|
-
this.currCol = 0;
|
|
50
|
-
this.currRow = this.rows;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
54
|
-
this.currRow = this.rows;
|
|
55
|
-
}
|
|
56
|
-
const h = rspan * cellHG - gap;
|
|
57
|
-
const cell = {
|
|
58
|
-
x: this.x + this.currCol * cellWG,
|
|
59
|
-
y: this.y + this.currRow * cellHG,
|
|
60
|
-
w: cspan * cellWG - gap,
|
|
61
|
-
h,
|
|
62
|
-
cw: this.cellW,
|
|
63
|
-
ch: this.cellH,
|
|
64
|
-
gap,
|
|
65
|
-
span: [cspan, rspan],
|
|
66
|
-
};
|
|
67
|
-
this.propagateSize(rspan);
|
|
68
|
-
this.currCol = Math.min(this.currCol + cspan, cols) % cols;
|
|
69
|
-
return cell;
|
|
70
|
-
}
|
|
71
|
-
// TODO add optional colspan arg, fix rounding
|
|
72
|
-
nextSquare() {
|
|
73
|
-
const box = this.next([
|
|
74
|
-
1,
|
|
75
|
-
Math.ceil(this.cellW / (this.cellH + this.gap)) + 1,
|
|
76
|
-
]);
|
|
77
|
-
box.h = box.w;
|
|
78
|
-
return box;
|
|
79
|
-
}
|
|
80
|
-
nest(cols, spans, gap = this.gap) {
|
|
81
|
-
const { x, y, w } = this.next(spans);
|
|
82
|
-
return new GridLayout(this, x, y, w, cols, this.cellH, gap);
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Updates max rows used in this layout and all of its parents.
|
|
86
|
-
*
|
|
87
|
-
* @param rspan -
|
|
88
|
-
*/
|
|
89
|
-
propagateSize(rspan) {
|
|
90
|
-
let rows = this.rows;
|
|
91
|
-
this.rows = rows = Math.max(rows, this.currRow + rspan);
|
|
92
|
-
const parent = this.parent;
|
|
93
|
-
parent && parent.propagateSize(rows);
|
|
49
|
+
this.currRow = this.rows;
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
52
|
+
this.currRow = this.rows;
|
|
94
53
|
}
|
|
54
|
+
const h = rspan * cellHG - gap;
|
|
55
|
+
const cell = {
|
|
56
|
+
x: this.x + this.currCol * cellWG,
|
|
57
|
+
y: this.y + this.currRow * cellHG,
|
|
58
|
+
w: cspan * cellWG - gap,
|
|
59
|
+
h,
|
|
60
|
+
cw: this.cellW,
|
|
61
|
+
ch: this.cellH,
|
|
62
|
+
gap,
|
|
63
|
+
span: [cspan, rspan]
|
|
64
|
+
};
|
|
65
|
+
this.propagateSize(rspan);
|
|
66
|
+
this.currCol = Math.min(this.currCol + cspan, cols) % cols;
|
|
67
|
+
return cell;
|
|
68
|
+
}
|
|
69
|
+
// TODO add optional colspan arg, fix rounding
|
|
70
|
+
nextSquare() {
|
|
71
|
+
const box = this.next([
|
|
72
|
+
1,
|
|
73
|
+
Math.ceil(this.cellW / (this.cellH + this.gap)) + 1
|
|
74
|
+
]);
|
|
75
|
+
box.h = box.w;
|
|
76
|
+
return box;
|
|
77
|
+
}
|
|
78
|
+
nest(cols, spans, gap = this.gap) {
|
|
79
|
+
const { x, y, w } = this.next(spans);
|
|
80
|
+
return new GridLayout(this, x, y, w, cols, this.cellH, gap);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Updates max rows used in this layout and all of its parents.
|
|
84
|
+
*
|
|
85
|
+
* @param rspan -
|
|
86
|
+
*/
|
|
87
|
+
propagateSize(rspan) {
|
|
88
|
+
let rows = this.rows;
|
|
89
|
+
this.rows = rows = Math.max(rows, this.currRow + rspan);
|
|
90
|
+
const parent = this.parent;
|
|
91
|
+
parent && parent.propagateSize(rows);
|
|
92
|
+
}
|
|
95
93
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
* @param width -
|
|
103
|
-
* @param cols -
|
|
104
|
-
* @param rowH -
|
|
105
|
-
* @param gap -
|
|
106
|
-
*/
|
|
107
|
-
export const gridLayout = (x, y, width, cols = 1, rowH = 16, gap = 4) => new GridLayout(null, x, y, width, cols, rowH, gap);
|
|
94
|
+
const gridLayout = (x, y, width, cols = 1, rowH = 16, gap = 4) => new GridLayout(null, x, y, width, cols, rowH, gap);
|
|
95
|
+
export {
|
|
96
|
+
GridLayout,
|
|
97
|
+
__DEFAULT_SPANS,
|
|
98
|
+
gridLayout
|
|
99
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/layout",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.7",
|
|
4
4
|
"description": "Configurable nested 2D grid layout generators",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -24,7 +24,9 @@
|
|
|
24
24
|
"author": "Karsten Schmidt (https://thi.ng)",
|
|
25
25
|
"license": "Apache-2.0",
|
|
26
26
|
"scripts": {
|
|
27
|
-
"build": "yarn
|
|
27
|
+
"build": "yarn build:esbuild && yarn build:decl",
|
|
28
|
+
"build:decl": "tsc --declaration --emitDeclarationOnly",
|
|
29
|
+
"build:esbuild": "esbuild --format=esm --platform=neutral --target=es2022 --tsconfig=tsconfig.json --outdir=. src/**/*.ts",
|
|
28
30
|
"clean": "rimraf --glob '*.js' '*.d.ts' '*.map' doc",
|
|
29
31
|
"doc": "typedoc --excludePrivate --excludeInternal --out doc src/index.ts",
|
|
30
32
|
"doc:ae": "mkdir -p .ae/doc .ae/temp && api-extractor run --local --verbose",
|
|
@@ -33,12 +35,12 @@
|
|
|
33
35
|
"test": "bun test"
|
|
34
36
|
},
|
|
35
37
|
"dependencies": {
|
|
36
|
-
"@thi.ng/arrays": "^2.7.
|
|
37
|
-
"@thi.ng/checks": "^3.4.
|
|
38
|
+
"@thi.ng/arrays": "^2.7.8",
|
|
39
|
+
"@thi.ng/checks": "^3.4.12"
|
|
38
40
|
},
|
|
39
41
|
"devDependencies": {
|
|
40
42
|
"@microsoft/api-extractor": "^7.38.3",
|
|
41
|
-
"
|
|
43
|
+
"esbuild": "^0.19.8",
|
|
42
44
|
"rimraf": "^5.0.5",
|
|
43
45
|
"tools": "^0.0.1",
|
|
44
46
|
"typedoc": "^0.25.4",
|
|
@@ -92,5 +94,5 @@
|
|
|
92
94
|
],
|
|
93
95
|
"year": 2019
|
|
94
96
|
},
|
|
95
|
-
"gitHead": "
|
|
97
|
+
"gitHead": "5e7bafedfc3d53bc131469a28de31dd8e5b4a3ff\n"
|
|
96
98
|
}
|
package/stacked-layout.js
CHANGED
|
@@ -1,111 +1,91 @@
|
|
|
1
1
|
import { argMax, argMin } from "@thi.ng/arrays/argmin";
|
|
2
2
|
import { GridLayout, __DEFAULT_SPANS } from "./grid-layout.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
3
|
+
class StackedLayout extends GridLayout {
|
|
4
|
+
offsets;
|
|
5
|
+
currSpan = 1;
|
|
6
|
+
constructor(parent, x, y, width, cols, rowH, gap) {
|
|
7
|
+
super(parent, x, y, width, cols, rowH, gap);
|
|
8
|
+
this.offsets = new Uint32Array(cols);
|
|
9
|
+
}
|
|
10
|
+
nest(cols, spans, gap = this.gap) {
|
|
11
|
+
const { x, y, w } = this.next(spans);
|
|
12
|
+
return new StackedLayout(this, x, y, w, cols, this.cellH, gap);
|
|
13
|
+
}
|
|
14
|
+
next(spans = __DEFAULT_SPANS) {
|
|
15
|
+
const { cellWG, cellHG, gap, cols, offsets } = this;
|
|
16
|
+
const cspan = Math.min(spans[0], cols);
|
|
17
|
+
const rspan = spans[1];
|
|
18
|
+
let minY = Infinity;
|
|
19
|
+
let maxY = 0;
|
|
20
|
+
let column = 0;
|
|
21
|
+
for (let i = 0; i <= cols - cspan; i++) {
|
|
22
|
+
const chunk = offsets.subarray(i, i + cspan);
|
|
23
|
+
const maxID = argMax(chunk);
|
|
24
|
+
const y = chunk[maxID];
|
|
25
|
+
if (y < minY) {
|
|
26
|
+
minY = y;
|
|
27
|
+
maxY = chunk[maxID];
|
|
28
|
+
column = i;
|
|
29
|
+
}
|
|
22
30
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
* match that of the next column after (if any). Repeated use of this method
|
|
67
|
-
* can be used to fill up (aka equalize) any bottom gaps of a layout
|
|
68
|
-
* container until all columns are equal. If the function returns a vertical
|
|
69
|
-
* span of 0, all columns are equalized already.
|
|
70
|
-
*
|
|
71
|
-
* @remarks
|
|
72
|
-
* An optional `maxSpan` can be provided to constrain the returned span (by
|
|
73
|
-
* default unconstrained).
|
|
74
|
-
*
|
|
75
|
-
* @param maxSpan
|
|
76
|
-
*/
|
|
77
|
-
availableSpan(maxSpan = [Infinity, Infinity]) {
|
|
78
|
-
const { offsets, cols } = this;
|
|
79
|
-
const minID = argMin(offsets);
|
|
80
|
-
const y = offsets[minID];
|
|
81
|
-
let result;
|
|
82
|
-
for (let i = minID + 1; i < cols; i++) {
|
|
83
|
-
if (offsets[i] > y) {
|
|
84
|
-
result = [i - minID, offsets[i] - y];
|
|
85
|
-
break;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
if (!result)
|
|
89
|
-
result = [cols - minID, offsets[argMax(offsets)] - y];
|
|
90
|
-
result[0] = Math.min(result[0], maxSpan[0]);
|
|
91
|
-
result[1] = Math.min(result[1], maxSpan[1]);
|
|
92
|
-
return result;
|
|
93
|
-
}
|
|
94
|
-
propagateSize(rspan) {
|
|
95
|
-
const newY = Math.max(this.currRow + rspan, this.offsets[this.currCol]);
|
|
96
|
-
this.offsets.fill(newY, this.currCol, this.currCol + this.currSpan);
|
|
97
|
-
this.parent && this.parent.propagateSize(newY);
|
|
31
|
+
const h = rspan * cellHG - gap;
|
|
32
|
+
const cell = {
|
|
33
|
+
x: this.x + column * cellWG,
|
|
34
|
+
y: this.y + maxY * cellHG,
|
|
35
|
+
w: cspan * cellWG - gap,
|
|
36
|
+
h,
|
|
37
|
+
cw: this.cellW,
|
|
38
|
+
ch: this.cellH,
|
|
39
|
+
gap,
|
|
40
|
+
span: [cspan, rspan]
|
|
41
|
+
};
|
|
42
|
+
this.currRow = maxY;
|
|
43
|
+
this.currCol = column;
|
|
44
|
+
offsets.fill(maxY + rspan, column, column + cspan);
|
|
45
|
+
this.currSpan = cspan;
|
|
46
|
+
this.parent && this.parent.propagateSize(Math.max(...this.offsets));
|
|
47
|
+
return cell;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Finds the largest available span of free area, such that if it'll be
|
|
51
|
+
* allocated via {@link StackedLayout.next} or {@link StackedLayout.nest},
|
|
52
|
+
* the impacted columns will all have the same height, and that height will
|
|
53
|
+
* match that of the next column after (if any). Repeated use of this method
|
|
54
|
+
* can be used to fill up (aka equalize) any bottom gaps of a layout
|
|
55
|
+
* container until all columns are equal. If the function returns a vertical
|
|
56
|
+
* span of 0, all columns are equalized already.
|
|
57
|
+
*
|
|
58
|
+
* @remarks
|
|
59
|
+
* An optional `maxSpan` can be provided to constrain the returned span (by
|
|
60
|
+
* default unconstrained).
|
|
61
|
+
*
|
|
62
|
+
* @param maxSpan
|
|
63
|
+
*/
|
|
64
|
+
availableSpan(maxSpan = [Infinity, Infinity]) {
|
|
65
|
+
const { offsets, cols } = this;
|
|
66
|
+
const minID = argMin(offsets);
|
|
67
|
+
const y = offsets[minID];
|
|
68
|
+
let result;
|
|
69
|
+
for (let i = minID + 1; i < cols; i++) {
|
|
70
|
+
if (offsets[i] > y) {
|
|
71
|
+
result = [i - minID, offsets[i] - y];
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
98
74
|
}
|
|
75
|
+
if (!result)
|
|
76
|
+
result = [cols - minID, offsets[argMax(offsets)] - y];
|
|
77
|
+
result[0] = Math.min(result[0], maxSpan[0]);
|
|
78
|
+
result[1] = Math.min(result[1], maxSpan[1]);
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
propagateSize(rspan) {
|
|
82
|
+
const newY = Math.max(this.currRow + rspan, this.offsets[this.currCol]);
|
|
83
|
+
this.offsets.fill(newY, this.currCol, this.currCol + this.currSpan);
|
|
84
|
+
this.parent && this.parent.propagateSize(newY);
|
|
85
|
+
}
|
|
99
86
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
* @param y -
|
|
106
|
-
* @param width -
|
|
107
|
-
* @param cols -
|
|
108
|
-
* @param rowH -
|
|
109
|
-
* @param gap -
|
|
110
|
-
*/
|
|
111
|
-
export const stackedLayout = (x, y, width, cols = 4, rowH = 16, gap = 4) => new StackedLayout(null, x, y, width, cols, rowH, gap);
|
|
87
|
+
const stackedLayout = (x, y, width, cols = 4, rowH = 16, gap = 4) => new StackedLayout(null, x, y, width, cols, rowH, gap);
|
|
88
|
+
export {
|
|
89
|
+
StackedLayout,
|
|
90
|
+
stackedLayout
|
|
91
|
+
};
|