@thi.ng/layout 3.0.6 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Change Log
2
2
 
3
- - **Last updated**: 2023-12-09T19:12:03Z
3
+ - **Last updated**: 2023-12-11T10:07:09Z
4
4
  - **Generator**: [thi.ng/monopub](https://thi.ng/monopub)
5
5
 
6
6
  All notable changes to this project will be documented in this file.
package/api.js CHANGED
@@ -1 +0,0 @@
1
- export {};
package/box.js CHANGED
@@ -1,10 +1,13 @@
1
- export 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)],
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
@@ -1,2 +1,5 @@
1
1
  import { implementsFunction } from "@thi.ng/checks/implements-function";
2
- export const isLayout = (x) => implementsFunction(x, "next");
2
+ const isLayout = (x) => implementsFunction(x, "next");
3
+ export {
4
+ isLayout
5
+ };
package/grid-layout.js CHANGED
@@ -1,107 +1,99 @@
1
1
  import { isNumber } from "@thi.ng/checks/is-number";
2
- /** @internal */
3
- export const __DEFAULT_SPANS = [1, 1];
4
- export class GridLayout {
5
- parent;
6
- cols;
7
- width;
8
- x;
9
- y;
10
- cellW;
11
- cellH;
12
- cellWG;
13
- cellHG;
14
- gap;
15
- currCol;
16
- currRow;
17
- rows;
18
- constructor(parent, x, y, width, cols, rowH, gap) {
19
- this.parent = parent;
20
- this.cols = cols;
21
- this.x = x;
22
- this.y = y;
23
- this.width = width;
24
- this.cellW = (width - (cols - 1) * gap) / cols;
25
- this.cellH = rowH;
26
- this.cellWG = this.cellW + gap;
27
- this.cellHG = rowH + gap;
28
- this.gap = gap;
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 = 0;
31
- this.rows = 0;
32
- }
33
- colsForWidth(w) {
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
- * Syntax sugar for {@link GridLayout} ctor. By default creates a
98
- * single-column layout at given position and width.
99
- *
100
- * @param x -
101
- * @param y -
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.6",
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 clean && tsc --declaration",
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,11 +35,12 @@
33
35
  "test": "bun test"
34
36
  },
35
37
  "dependencies": {
36
- "@thi.ng/arrays": "^2.7.7",
37
- "@thi.ng/checks": "^3.4.11"
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",
43
+ "esbuild": "^0.19.8",
41
44
  "rimraf": "^5.0.5",
42
45
  "tools": "^0.0.1",
43
46
  "typedoc": "^0.25.4",
@@ -91,5 +94,5 @@
91
94
  ],
92
95
  "year": 2019
93
96
  },
94
- "gitHead": "25f2ac8ff795a432a930119661b364d4d93b59a0\n"
97
+ "gitHead": "5e7bafedfc3d53bc131469a28de31dd8e5b4a3ff\n"
95
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
- * An extension of {@link GridLayout} which tracks individual column-based
5
- * heights and so can create more complex, irregular, packed, space-filling
6
- * layout arrangements. This layout algorithm prioritizes the column(s) with the
7
- * lowest height.
8
- *
9
- * The class also provides a {@link StackedLayout.availableSpan} method to find
10
- * available space and help equalize columns and fill/allocate any bottom gaps.
11
- *
12
- * **IMPORTANT:** As with {@link GridLayout}, nested layouts MUST be completed
13
- * first before requesting new cells (aka {@link LayoutBox}es) from a parent,
14
- * otherwise unintended overlaps will occur.
15
- */
16
- export class StackedLayout extends GridLayout {
17
- offsets;
18
- currSpan = 1;
19
- constructor(parent, x, y, width, cols, rowH, gap) {
20
- super(parent, x, y, width, cols, rowH, gap);
21
- this.offsets = new Uint32Array(cols);
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
- nest(cols, spans, gap = this.gap) {
24
- const { x, y, w } = this.next(spans);
25
- return new StackedLayout(this, x, y, w, cols, this.cellH, gap);
26
- }
27
- next(spans = __DEFAULT_SPANS) {
28
- const { cellWG, cellHG, gap, cols, offsets } = this;
29
- const cspan = Math.min(spans[0], cols);
30
- const rspan = spans[1];
31
- let minY = Infinity;
32
- let maxY = 0;
33
- let column = 0;
34
- for (let i = 0; i <= cols - cspan; i++) {
35
- const chunk = offsets.subarray(i, i + cspan);
36
- const maxID = argMax(chunk);
37
- const y = chunk[maxID];
38
- if (y < minY) {
39
- minY = y;
40
- maxY = chunk[maxID];
41
- column = i;
42
- }
43
- }
44
- const h = rspan * cellHG - gap;
45
- const cell = {
46
- x: this.x + column * cellWG,
47
- y: this.y + maxY * cellHG,
48
- w: cspan * cellWG - gap,
49
- h,
50
- cw: this.cellW,
51
- ch: this.cellH,
52
- gap,
53
- span: [cspan, rspan],
54
- };
55
- this.currRow = maxY;
56
- this.currCol = column;
57
- offsets.fill(maxY + rspan, column, column + cspan);
58
- this.currSpan = cspan;
59
- this.parent && this.parent.propagateSize(Math.max(...this.offsets));
60
- return cell;
61
- }
62
- /**
63
- * Finds the largest available span of free area, such that if it'll be
64
- * allocated via {@link StackedLayout.next} or {@link StackedLayout.nest},
65
- * the impacted columns will all have the same height, and that height will
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
- * Syntax sugar for {@link StackedLayout} ctor. By default creates a 4-column
102
- * layout at given position and total width.
103
- *
104
- * @param x -
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
+ };