@thi.ng/layout 3.2.0 → 4.0.0

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**: 2025-07-22T10:52:52Z
3
+ - **Last updated**: 2025-07-26T12:33:06Z
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.
@@ -11,6 +11,17 @@ See [Conventional Commits](https://conventionalcommits.org/) for commit guidelin
11
11
  **Note:** Unlisted _patch_ versions only involve non-code or otherwise excluded changes
12
12
  and/or version bumps of transitive dependencies.
13
13
 
14
+ # [4.0.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/layout@4.0.0) (2025-07-26)
15
+
16
+ #### 🛑 Breaking changes
17
+
18
+ - add support for separate X/Y gaps/gutters ([cdc3385](https://github.com/thi-ng/umbrella/commit/cdc3385))
19
+ - BREAKING CHANGE: update `LayoutBox` and `IGridLayout`, split `gap` into `gapX` and `gapY`
20
+ - update `LayoutBox`, `IGridLayout`
21
+ - update `IGridLayout.nest()` to support differing gaps/gutters
22
+ - update `GridLayout` and `StackedLayout` impls
23
+ - add/update docs
24
+
14
25
  ## [3.2.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/layout@3.2.0) (2025-07-22)
15
26
 
16
27
  #### 🚀 Features
package/README.md CHANGED
@@ -74,7 +74,7 @@ For Node.js REPL:
74
74
  const l = await import("@thi.ng/layout");
75
75
  ```
76
76
 
77
- Package sizes (brotli'd, pre-treeshake): ESM: 1.21 KB
77
+ Package sizes (brotli'd, pre-treeshake): ESM: 1.22 KB
78
78
 
79
79
  ## Dependencies
80
80
 
@@ -116,9 +116,10 @@ import * as g from "@thi.ng/geom";
116
116
  import { gridLayout, type LayoutBox } from "@thi.ng/layout";
117
117
  import { writeFileSync } from "node:fs";
118
118
 
119
- // collection of generated layout cells
119
+ // collection of generated layout boxes/cells
120
120
  const cells: g.Group[] = [];
121
121
 
122
+ // helper function to collect & visualize layout boxes
122
123
  const addRect = (id: number, box: LayoutBox, fill: string) => {
123
124
  console.log(box);
124
125
  const shape = g.rect([box.x, box.y], [box.w, box.h], { fill });
@@ -139,20 +140,20 @@ const layout = gridLayout(10, 10, 1000, 1, 60, 4);
139
140
 
140
141
  // get next layout box (1st row, by default the column/row span is [1,1])
141
142
  addRect(1, layout.next(), "#fec");
142
- // { x: 10, y: 10, w: 1000, h: 60, cw: 1000, ch: 60, gap: 4, span: [ 1, 1 ] }
143
+ // { x: 10, y: 10, w: 1000, h: 60, cw: 1000, ch: 60, gapX: 4, gapY: 4, span: [ 1, 1 ] }
143
144
 
144
145
  // 2nd row
145
146
  addRect(2, layout.next(), "#fec");
146
- // { x: 10, y: 74, w: 1000, h: 60, cw: 1000, ch: 60, gap: 4, span: [ 1, 1 ] }
147
+ // { x: 10, y: 74, w: 1000, h: 60, cw: 1000, ch: 60, gapX: 4, gapY: 4, span: [ 1, 1 ] }
147
148
 
148
149
  // create nested 2-column layout (3rd row)
149
150
  const twoCols = layout.nest(2);
150
151
 
151
152
  addRect(3, twoCols.next(), "#cfc");
152
- // { x: 10, y: 138, w: 498, h: 60, cw: 498, ch: 60, gap: 4, span: [ 1, 1 ] }
153
+ // { x: 10, y: 138, w: 498, h: 60, cw: 498, ch: 60, gapX: 4, gapY: 4, span: [ 1, 1 ] }
153
154
 
154
155
  addRect(4, twoCols.next(), "#cfc");
155
- // { x: 512, y: 138, w: 498, h: 60, cw: 498, ch: 60, gap: 4, span: [ 1, 1 ] }
156
+ // { x: 512, y: 138, w: 498, h: 60, cw: 498, ch: 60, gapX: 4, gapY: 4, span: [ 1, 1 ] }
156
157
 
157
158
  // now nest 3-columns in the 1st column of twoCols
158
159
  // (i.e. now each column is 1/6th of the main layout's width)
@@ -160,15 +161,15 @@ const inner = twoCols.nest(3);
160
161
 
161
162
  // allocate with col/rowspan, here 1 column x 4 rows
162
163
  addRect(5, inner.next([1, 4]), "#9ff");
163
- // { x: 10, y: 202, w: 163.33, h: 252, cw: 163.33, ch: 60, gap: 4, span: [ 1, 4 ] }
164
+ // { x: 10, y: 202, w: 163.33, h: 252, cw: 163.33, ch: 60, gapX: 4, gapY: 4, span: [ 1, 4 ] }
164
165
  addRect(6, inner.next([1, 4]), "#9ff");
165
- // { x: 177.33, y: 202, w: 163.33, h: 252, cw: 163.33, ch: 60, gap: 4, span: [ 1, 4 ] }
166
+ // { x: 177.33, y: 202, w: 163.33, h: 252, cw: 163.33, ch: 60, gapX: 4, gapY: 4, span: [ 1, 4 ] }
166
167
  addRect(7, inner.next([1, 4]), "#9ff");
167
- // { x: 344.66, y: 202, w: 163.33, h: 252, cw: 163.33, ch: 60, gap: 4, span: [ 1, 4 ] }
168
+ // { x: 344.66, y: 202, w: 163.33, h: 252, cw: 163.33, ch: 60, gapX: 4, gapY: 4, span: [ 1, 4 ] }
168
169
 
169
170
  // back to twoCols (2nd column)
170
171
  addRect(8, twoCols.next([1, 2]), "#cfc");
171
- // { x: 512, y: 202, w: 498, h: 124, cw: 498, ch: 60, gap: 4, span: [ 1, 2 ] }
172
+ // { x: 512, y: 202, w: 498, h: 124, cw: 498, ch: 60, gapX: 4, gapY: 4, span: [ 1, 2 ] }
172
173
 
173
174
  // export as SVG
174
175
  writeFileSync(
@@ -212,9 +213,10 @@ import * as g from "@thi.ng/geom";
212
213
  import { stackedLayout, type LayoutBox } from "@thi.ng/layout";
213
214
  import { writeFileSync } from "node:fs";
214
215
 
215
- // collection of generated layout cells
216
+ // collection of generated layout boxes/cells
216
217
  const cells: g.Group[] = [];
217
218
 
219
+ // helper function to collect & visualize layout boxes
218
220
  const addRect = (id: number, box: LayoutBox, fill: string) => {
219
221
  console.log(box);
220
222
  const shape = g.rect([box.x, box.y], [box.w, box.h], { fill });
@@ -235,37 +237,37 @@ const layout = stackedLayout(0, 0, 1000, 4, 60, 4);
235
237
 
236
238
  // get next layout box (1st column)
237
239
  addRect(1, layout.next([1, 2]), "#fec");
238
- // { x: 0, y: 0, w: 247, h: 124, cw: 247, ch: 60, gap: 4, span: [ 1, 2 ] }
240
+ // { x: 0, y: 0, w: 247, h: 124, cw: 247, ch: 60, gapX: 4, gapY: 4, span: [ 1, 2 ] }
239
241
 
240
242
  // 2nd column
241
243
  addRect(2, layout.next(), "#fec");
242
- // { x: 251, y: 0, w: 247, h: 60, cw: 247, ch: 60, gap: 4, span: [ 1, 1 ] }
244
+ // { x: 251, y: 0, w: 247, h: 60, cw: 247, ch: 60, gapX: 4, gapY: 4, span: [ 1, 1 ] }
243
245
 
244
246
  // 3rd column
245
247
  addRect(3, layout.next([1, 4]), "#fec");
246
- // { x: 502, y: 0, w: 247, h: 252, cw: 247, ch: 60, gap: 4, span: [ 1, 4 ] }
248
+ // { x: 502, y: 0, w: 247, h: 252, cw: 247, ch: 60, gapX: 4, gapY: 4, span: [ 1, 4 ] }
247
249
 
248
250
  // 4th column
249
251
  addRect(4, layout.next([1, 1]), "#fec");
250
- // { x: 753, y: 0, w: 247, h: 60, cw: 247, ch: 60, gap: 4, span: [ 1, 1 ] }
252
+ // { x: 753, y: 0, w: 247, h: 60, cw: 247, ch: 60, gapX: 4, gapY: 4, span: [ 1, 1 ] }
251
253
 
252
254
  // 2x2 span
253
255
  // (note that this will create a gap in the 2nd column)
254
256
  addRect(5, layout.next([2, 2]), "#fec");
255
- // { x: 0, y: 128, w: 498, h: 124, cw: 247, ch: 60, gap: 4, span: [ 2, 2 ] }
257
+ // { x: 0, y: 128, w: 498, h: 124, cw: 247, ch: 60, gapX: 4, gapY: 4, span: [ 2, 2 ] }
256
258
 
257
259
  const inner = layout.nest(2);
258
260
 
259
261
  addRect(6, inner.next([1, 5]), "#cfc");
260
- // { x: 753, y: 64, w: 121.5, h: 316, cw: 121.5, ch: 60, gap: 4, span: [ 1, 5 ] }
262
+ // { x: 753, y: 64, w: 121.5, h: 316, cw: 121.5, ch: 60, gapX: 4, gapY: 4, span: [ 1, 5 ] }
261
263
  addRect(7, inner.next([1, 5]), "#cfc");
262
- // { x: 878.5, y: 64, w: 121.5, h: 316, cw: 121.5, ch: 60, gap: 4, span: [ 1, 5 ] }
264
+ // { x: 878.5, y: 64, w: 121.5, h: 316, cw: 121.5, ch: 60, gapX: 4, gapY: 4, span: [ 1, 5 ] }
263
265
 
264
266
  // fill available space in the other columns
265
267
  // (depending on situation, this might have to be done multiple times
266
268
  // to fill all available space, please consult documentation)
267
269
  addRect(8, layout.next(layout.availableSpan()), "#9ff");
268
- // { x: 0, y: 256, w: 749, h: 124, cw: 247, ch: 60, gap: 4, span: [ 3, 2 ] }
270
+ // { x: 0, y: 256, w: 749, h: 124, cw: 247, ch: 60, gapX: 4, gapY: 4, span: [ 3, 2 ] }
269
271
 
270
272
  // export as SVG
271
273
  writeFileSync(
package/api.d.ts CHANGED
@@ -27,9 +27,13 @@ export interface LayoutBox {
27
27
  */
28
28
  ch: number;
29
29
  /**
30
- * Gutter size.
30
+ * Horizontal gutter size.
31
31
  */
32
- gap: number;
32
+ gapX: number;
33
+ /**
34
+ * Vertical gutter size.
35
+ */
36
+ gapY: number;
33
37
  /**
34
38
  *
35
39
  */
@@ -39,13 +43,29 @@ export interface ILayout<O, T> {
39
43
  next(opts?: O): T;
40
44
  }
41
45
  export interface IGridLayout<T extends IGridLayout<T>> extends ILayout<CellSpan, LayoutBox> {
46
+ readonly parent: IGridLayout<T> | null;
47
+ /** Left coordinate of entire layout */
42
48
  readonly x: number;
49
+ /** Top coordinate of entire layout */
43
50
  readonly y: number;
51
+ /** Configured total width */
44
52
  readonly width: number;
53
+ /** The current total height of the layout */
54
+ readonly height: number;
55
+ /** Configured number of columns */
45
56
  readonly cols: number;
57
+ /** Width of single grid cell (without gap) */
46
58
  readonly cellW: number;
59
+ /** Height of single grid cell (without gap) */
47
60
  readonly cellH: number;
48
- readonly gap: number;
61
+ /** Width of single grid cell (incl. gap) */
62
+ readonly cellWG: number;
63
+ /** Height of single grid cell (incl. gap) */
64
+ readonly cellHG: number;
65
+ /** Horizontal gap/gutter between grid cells */
66
+ readonly gapX: number;
67
+ /** Vertical gap/gutter between grid cells */
68
+ readonly gapY: number;
49
69
  /**
50
70
  * Returns the number of columns for given width.
51
71
  *
@@ -74,43 +94,47 @@ export interface IGridLayout<T extends IGridLayout<T>> extends ILayout<CellSpan,
74
94
  /**
75
95
  * Requests a `spans` sized cell from this layout (via `.next()`) and
76
96
  * creates and returns a new child {@link GridLayout} for the returned box /
77
- * grid cell. This child layout is configured to use `cols` columns and
78
- * shares same `gap` as this (parent) layout. The configured row span only
79
- * acts as initial minimum vertical space reseervation, but is allowed to
80
- * grow and if needed will propagate the new space requirements to parent
81
- * layouts.
97
+ * grid cell. This child layout is configured to use `cols` columns and (by
98
+ * default) shares same `gapX` and `gapY` as this (parent) layout. The
99
+ * configured row span only acts as initial minimum vertical space
100
+ * reservation, but is allowed to grow and if needed will propagate the new
101
+ * space requirements to parent layouts.
82
102
  *
83
- * Note: this size child-parent size propagation ONLY works until the next
84
- * cell is requested from any parent. IOW, child layouts MUST be
103
+ * IMPORTANT: This child-parent size propagation ONLY works until the next
104
+ * cell is requested from any parent. Ion other words, child layouts MUST be
85
105
  * completed/populated first before continuing with siblings/ancestors of
86
106
  * this current layout.
87
107
  *
88
- * ```
108
+ * ```ts tangle:../export/nest.ts
89
109
  * import { gridLayout } from "@thi.ng/layout";
90
110
  *
91
111
  * // single column layout (default config)
92
- * const outer = gridLayout(null, 0, 0, 200, 1, 16, 4);
112
+ * const outer = gridLayout(0, 0, 200, 1, 16, 4);
93
113
  *
94
- * // add button (full 1st row)
95
- * button(gui, outer, "foo",...);
114
+ * // obtain a box for entire 1st row
115
+ * console.log(outer.next());
116
+ * // { x: 0, y: 0, w: 200, h: 16, cw: 200, ch: 16, gapX: 4, gapY: 4, span: [ 1, 1 ] }
96
117
  *
97
118
  * // 2-column nested layout (2nd row)
98
- * const inner = outer.nest(2)
99
- * // these buttons are on same row
100
- * button(gui, inner, "bar",...);
101
- * button(gui, inner, "baz",...);
119
+ * const inner = outer.nest(2);
120
+ *
121
+ * // these smaller boxes are on same row
122
+ * console.log(inner.next());
123
+ * // { x: 0, y: 20, w: 98, h: 16, cw: 98, ch: 16, gapX: 4, gapY: 4, span: [ 1, 1 ] }
102
124
  *
103
- * // continue with outer, create empty row
104
- * outer.next();
125
+ * console.log(inner.next());
126
+ * // { x: 102, y: 20, w: 98, h: 16, cw: 98, ch: 16, gapX: 4, gapY: 4, span: [ 1, 1 ] }
105
127
  *
106
- * // continue with outer (4th row)
107
- * button(gui, outer, "bye",...);
128
+ * // continue with outer (again full row)
129
+ * console.log(outer.next());
130
+ * // { x: 0, y: 40, w: 200, h: 16, cw: 200, ch: 16, gapX: 4, gapY: 4, span: [ 1, 1 ] }
108
131
  * ```
109
132
  *
110
133
  * @param cols - columns in nested layout
111
134
  * @param spans - default `[1,1]` (i.e. size of single cell)
112
- * @param gap - gap for child layout
135
+ * @param gapX - horizontal gap for child layout
136
+ * @param gapY - vertical gap for child layout
113
137
  */
114
- nest(cols: number, spans?: CellSpan, gap?: number): T;
138
+ nest(cols: number, spans?: CellSpan, gapX?: number, gapY?: number): T;
115
139
  }
116
140
  //# sourceMappingURL=api.d.ts.map
package/box.d.ts CHANGED
@@ -1,3 +1,16 @@
1
1
  import type { CellSpan, LayoutBox } from "./api.js";
2
- export declare const layoutBox: (x: number, y: number, w: number, h: number, cw: number, ch: number, gap: number, span?: CellSpan) => LayoutBox;
2
+ /**
3
+ * Creates a {@link LayoutBox} object with given details.
4
+ *
5
+ * @param x
6
+ * @param y
7
+ * @param w
8
+ * @param h
9
+ * @param cw
10
+ * @param ch
11
+ * @param gapX
12
+ * @param gapY
13
+ * @param span
14
+ */
15
+ export declare const layoutBox: (x: number, y: number, w: number, h: number, cw: number, ch: number, gapX: number, gapY?: number, span?: CellSpan) => LayoutBox;
3
16
  //# sourceMappingURL=box.d.ts.map
package/box.js CHANGED
@@ -1,11 +1,12 @@
1
- const layoutBox = (x, y, w, h, cw, ch, gap, span) => ({
1
+ const layoutBox = (x, y, w, h, cw, ch, gapX, gapY = gapX, span) => ({
2
2
  x,
3
3
  y,
4
4
  w,
5
5
  h,
6
6
  cw,
7
7
  ch,
8
- gap,
8
+ gapX,
9
+ gapY,
9
10
  span: span || [~~(w / cw), ~~(h / ch)]
10
11
  });
11
12
  export {
package/grid-layout.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { CellSpan, IGridLayout, LayoutBox } from "./api.js";
1
+ import type { CellSpan, IGridLayout } from "./api.js";
2
2
  /** @internal */
3
3
  export declare const __DEFAULT_SPANS: CellSpan;
4
4
  export declare class GridLayout implements IGridLayout<GridLayout> {
@@ -11,19 +11,20 @@ export declare class GridLayout implements IGridLayout<GridLayout> {
11
11
  readonly cellH: number;
12
12
  readonly cellWG: number;
13
13
  readonly cellHG: number;
14
- readonly gap: number;
14
+ readonly gapX: number;
15
+ readonly gapY: number;
15
16
  protected currCol: number;
16
17
  protected currRow: number;
17
18
  protected rows: number;
18
- constructor(parent: GridLayout | null, x: number, y: number, width: number, cols: number, rowH: number, gap: number);
19
+ constructor(parent: GridLayout | null, x: number, y: number, width: number, cols: number, rowH: number, gapX: number, gapY?: number);
19
20
  get height(): number;
20
21
  colsForWidth(w: number): number;
21
22
  rowsForHeight(h: number): number;
22
23
  spanForSize(size: ArrayLike<number>): CellSpan;
23
24
  spanForSize(w: number, h: number): CellSpan;
24
- next(spans?: CellSpan): LayoutBox;
25
- nextSquare(): LayoutBox;
26
- nest(cols: number, spans?: CellSpan, gap?: number): GridLayout;
25
+ next(spans?: CellSpan): import("./api.js").LayoutBox;
26
+ nextSquare(): import("./api.js").LayoutBox;
27
+ nest(cols: number, spans?: CellSpan, gapX?: number, gapY?: number): GridLayout;
27
28
  /**
28
29
  * Updates max rows used in this layout and all of its parents.
29
30
  *
@@ -40,7 +41,8 @@ export declare class GridLayout implements IGridLayout<GridLayout> {
40
41
  * @param width -
41
42
  * @param cols -
42
43
  * @param rowH -
43
- * @param gap -
44
+ * @param gapX -
45
+ * @param gapY -
44
46
  */
45
- export declare const gridLayout: (x: number, y: number, width: number, cols?: number, rowH?: number, gap?: number) => GridLayout;
47
+ export declare const gridLayout: (x: number, y: number, width: number, cols?: number, rowH?: number, gapX?: number, gapY?: number) => GridLayout;
46
48
  //# sourceMappingURL=grid-layout.d.ts.map
package/grid-layout.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { isNumber } from "@thi.ng/checks/is-number";
2
+ import { layoutBox } from "./box.js";
2
3
  const __DEFAULT_SPANS = [1, 1];
3
4
  class GridLayout {
4
5
  parent;
@@ -10,21 +11,23 @@ class GridLayout {
10
11
  cellH;
11
12
  cellWG;
12
13
  cellHG;
13
- gap;
14
+ gapX;
15
+ gapY;
14
16
  currCol;
15
17
  currRow;
16
18
  rows;
17
- constructor(parent, x, y, width, cols, rowH, gap) {
19
+ constructor(parent, x, y, width, cols, rowH, gapX, gapY = gapX) {
18
20
  this.parent = parent;
19
21
  this.cols = cols;
20
22
  this.x = x;
21
23
  this.y = y;
22
24
  this.width = width;
23
- this.cellW = (width - (cols - 1) * gap) / cols;
25
+ this.cellW = (width - (cols - 1) * gapX) / cols;
24
26
  this.cellH = rowH;
25
- this.cellWG = this.cellW + gap;
26
- this.cellHG = rowH + gap;
27
- this.gap = gap;
27
+ this.cellWG = this.cellW + gapX;
28
+ this.cellHG = rowH + gapY;
29
+ this.gapX = gapX;
30
+ this.gapY = gapY;
28
31
  this.currCol = 0;
29
32
  this.currRow = 0;
30
33
  this.rows = 0;
@@ -39,11 +42,11 @@ class GridLayout {
39
42
  return Math.ceil(h / this.cellHG);
40
43
  }
41
44
  spanForSize(w, h) {
42
- const size = isNumber(w) ? [w, h] : w;
45
+ const size = isNumber(w) ? [w, h ?? w] : w;
43
46
  return [this.colsForWidth(size[0]), this.rowsForHeight(size[1])];
44
47
  }
45
48
  next(spans = __DEFAULT_SPANS) {
46
- const { cellWG, cellHG, gap, cols } = this;
49
+ const { cellWG, cellHG, gapX, gapY, cols } = this;
47
50
  const cspan = Math.min(spans[0], cols);
48
51
  const rspan = spans[1];
49
52
  if (this.currCol > 0) {
@@ -54,33 +57,31 @@ class GridLayout {
54
57
  } else {
55
58
  this.currRow = this.rows;
56
59
  }
57
- const h = rspan * cellHG - gap;
58
- const cell = {
59
- x: this.x + this.currCol * cellWG,
60
- y: this.y + this.currRow * cellHG,
61
- w: cspan * cellWG - gap,
60
+ const h = rspan * cellHG - gapY;
61
+ const cell = layoutBox(
62
+ this.x + this.currCol * cellWG,
63
+ this.y + this.currRow * cellHG,
64
+ cspan * cellWG - gapX,
62
65
  h,
63
- cw: this.cellW,
64
- ch: this.cellH,
65
- gap,
66
- span: [cspan, rspan]
67
- };
66
+ this.cellW,
67
+ this.cellH,
68
+ gapX,
69
+ gapY,
70
+ [cspan, rspan]
71
+ );
68
72
  this.propagateSize(rspan);
69
73
  this.currCol = Math.min(this.currCol + cspan, cols) % cols;
70
74
  return cell;
71
75
  }
72
76
  // TODO add optional colspan arg, fix rounding
73
77
  nextSquare() {
74
- const box = this.next([
75
- 1,
76
- Math.ceil(this.cellW / (this.cellH + this.gap)) + 1
77
- ]);
78
+ const box = this.next([1, Math.ceil(this.cellW / this.cellHG) + 1]);
78
79
  box.h = box.w;
79
80
  return box;
80
81
  }
81
- nest(cols, spans, gap = this.gap) {
82
+ nest(cols, spans, gapX = this.gapX, gapY = this.gapY) {
82
83
  const { x, y, w } = this.next(spans);
83
- return new GridLayout(this, x, y, w, cols, this.cellH, gap);
84
+ return new GridLayout(this, x, y, w, cols, this.cellH, gapX, gapY);
84
85
  }
85
86
  /**
86
87
  * Updates max rows used in this layout and all of its parents.
@@ -88,12 +89,11 @@ class GridLayout {
88
89
  * @param rspan -
89
90
  */
90
91
  propagateSize(rspan) {
91
- let rows = this.rows;
92
- this.rows = rows = Math.max(rows, this.currRow + rspan);
93
- this.parent?.propagateSize(rows);
92
+ this.rows = Math.max(this.rows, this.currRow + rspan);
93
+ this.parent?.propagateSize(this.rows);
94
94
  }
95
95
  }
96
- const gridLayout = (x, y, width, cols = 1, rowH = 16, gap = 4) => new GridLayout(null, x, y, width, cols, rowH, gap);
96
+ const gridLayout = (x, y, width, cols = 1, rowH = 16, gapX = 4, gapY = gapX) => new GridLayout(null, x, y, width, cols, rowH, gapX, gapY);
97
97
  export {
98
98
  GridLayout,
99
99
  __DEFAULT_SPANS,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thi.ng/layout",
3
- "version": "3.2.0",
3
+ "version": "4.0.0",
4
4
  "description": "Configurable nested 2D grid layout generators",
5
5
  "type": "module",
6
6
  "module": "./index.js",
@@ -98,5 +98,5 @@
98
98
  ],
99
99
  "year": 2019
100
100
  },
101
- "gitHead": "802e34c5e0786adcae538011713171232693b031\n"
101
+ "gitHead": "b138ae2fb8567acfe724cc675654878f60d26631\n"
102
102
  }
@@ -3,21 +3,23 @@ import { GridLayout } from "./grid-layout.js";
3
3
  /**
4
4
  * An extension of {@link GridLayout} which tracks individual column-based
5
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.
6
+ * layout arrangements. This layout algorithm prioritizes allocating the
7
+ * column(s) with the currently lowest height.
8
8
  *
9
9
  * The class also provides a {@link StackedLayout.availableSpan} method to find
10
10
  * available space and help equalize columns and fill/allocate any bottom gaps.
11
11
  *
12
12
  * **IMPORTANT:** As with {@link GridLayout}, nested layouts MUST be completed
13
13
  * first before requesting new cells (aka {@link LayoutBox}es) from a parent,
14
- * otherwise unintended overlaps will occur.
14
+ * otherwise unintended overlaps will occur. Also see {@link IGridLayout.nest}
15
+ * for more details.
15
16
  */
16
17
  export declare class StackedLayout extends GridLayout {
18
+ /** Number of rows per column */
17
19
  offsets: Uint32Array;
18
20
  currSpan: number;
19
- constructor(parent: GridLayout | null, x: number, y: number, width: number, cols: number, rowH: number, gap: number);
20
- nest(cols: number, spans?: CellSpan, gap?: number): StackedLayout;
21
+ constructor(parent: GridLayout | null, x: number, y: number, width: number, cols: number, rowH: number, gapX: number, gapY?: number);
22
+ nest(cols: number, spans?: CellSpan, gapX?: number, gapY?: number): StackedLayout;
21
23
  next(spans?: CellSpan): LayoutBox;
22
24
  /**
23
25
  * Finds the largest available span of free area, such that if it'll be
@@ -50,7 +52,8 @@ export declare class StackedLayout extends GridLayout {
50
52
  * @param width -
51
53
  * @param cols -
52
54
  * @param rowH -
53
- * @param gap -
55
+ * @param gapX -
56
+ * @param gapY -
54
57
  */
55
- export declare const stackedLayout: (x: number, y: number, width: number, cols?: number, rowH?: number, gap?: number) => StackedLayout;
58
+ export declare const stackedLayout: (x: number, y: number, width: number, cols?: number, rowH?: number, gapX?: number, gapY?: number) => StackedLayout;
56
59
  //# sourceMappingURL=stacked-layout.d.ts.map
package/stacked-layout.js CHANGED
@@ -1,18 +1,20 @@
1
1
  import { argMax, argMin } from "@thi.ng/arrays/argmin";
2
+ import { layoutBox } from "./box.js";
2
3
  import { GridLayout, __DEFAULT_SPANS } from "./grid-layout.js";
3
4
  class StackedLayout extends GridLayout {
5
+ /** Number of rows per column */
4
6
  offsets;
5
7
  currSpan = 1;
6
- constructor(parent, x, y, width, cols, rowH, gap) {
7
- super(parent, x, y, width, cols, rowH, gap);
8
+ constructor(parent, x, y, width, cols, rowH, gapX, gapY = gapX) {
9
+ super(parent, x, y, width, cols, rowH, gapX, gapY);
8
10
  this.offsets = new Uint32Array(cols);
9
11
  }
10
- nest(cols, spans, gap = this.gap) {
12
+ nest(cols, spans, gapX = this.gapX, gapY = this.gapY) {
11
13
  const { x, y, w } = this.next(spans);
12
- return new StackedLayout(this, x, y, w, cols, this.cellH, gap);
14
+ return new StackedLayout(this, x, y, w, cols, this.cellH, gapX, gapY);
13
15
  }
14
16
  next(spans = __DEFAULT_SPANS) {
15
- const { cellWG, cellHG, gap, cols, offsets } = this;
17
+ const { cellWG, cellHG, gapX, gapY, cols, offsets } = this;
16
18
  const cspan = Math.min(spans[0], cols);
17
19
  const rspan = spans[1];
18
20
  let minY = Infinity;
@@ -28,22 +30,23 @@ class StackedLayout extends GridLayout {
28
30
  column = i;
29
31
  }
30
32
  }
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,
33
+ const h = rspan * cellHG - gapY;
34
+ const cell = layoutBox(
35
+ this.x + column * cellWG,
36
+ this.y + maxY * cellHG,
37
+ cspan * cellWG - gapX,
36
38
  h,
37
- cw: this.cellW,
38
- ch: this.cellH,
39
- gap,
40
- span: [cspan, rspan]
41
- };
39
+ this.cellW,
40
+ this.cellH,
41
+ gapX,
42
+ gapY,
43
+ [cspan, rspan]
44
+ );
42
45
  this.currRow = maxY;
43
46
  this.currCol = column;
44
47
  offsets.fill(maxY + rspan, column, column + cspan);
45
48
  this.currSpan = cspan;
46
- this.parent?.propagateSize(Math.max(...this.offsets));
49
+ this.parent?.propagateSize(Math.max(...offsets));
47
50
  return cell;
48
51
  }
49
52
  /**
@@ -89,7 +92,7 @@ class StackedLayout extends GridLayout {
89
92
  return Math.min(...this.offsets) === Math.max(...this.offsets);
90
93
  }
91
94
  }
92
- const stackedLayout = (x, y, width, cols = 4, rowH = 16, gap = 4) => new StackedLayout(null, x, y, width, cols, rowH, gap);
95
+ const stackedLayout = (x, y, width, cols = 4, rowH = 16, gapX = 4, gapY = gapX) => new StackedLayout(null, x, y, width, cols, rowH, gapX, gapY);
93
96
  export {
94
97
  StackedLayout,
95
98
  stackedLayout