@thi.ng/layout 3.1.35 → 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 +18 -1
- package/README.md +23 -21
- package/api.d.ts +48 -24
- package/box.d.ts +14 -1
- package/box.js +3 -2
- package/grid-layout.d.ts +11 -8
- package/grid-layout.js +31 -28
- package/package.json +2 -2
- package/stacked-layout.d.ts +10 -7
- package/stacked-layout.js +20 -17
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
-
- **Last updated**: 2025-07-
|
|
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,23 @@ 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
|
+
|
|
25
|
+
## [3.2.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/layout@3.2.0) (2025-07-22)
|
|
26
|
+
|
|
27
|
+
#### 🚀 Features
|
|
28
|
+
|
|
29
|
+
- add `.height` getter ([a0188a6](https://github.com/thi-ng/umbrella/commit/a0188a6))
|
|
30
|
+
|
|
14
31
|
### [3.1.11](https://github.com/thi-ng/umbrella/tree/@thi.ng/layout@3.1.11) (2025-01-14)
|
|
15
32
|
|
|
16
33
|
#### ♻️ Refactoring
|
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.
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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(
|
|
@@ -176,7 +177,7 @@ writeFileSync(
|
|
|
176
177
|
g.asSvg(
|
|
177
178
|
g.svgDoc(
|
|
178
179
|
{
|
|
179
|
-
|
|
180
|
+
__margin: 10,
|
|
180
181
|
font: "12px Menlo, monospace",
|
|
181
182
|
align: "center",
|
|
182
183
|
baseline: "middle",
|
|
@@ -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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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(
|
|
@@ -273,7 +275,7 @@ writeFileSync(
|
|
|
273
275
|
g.asSvg(
|
|
274
276
|
g.svgDoc(
|
|
275
277
|
{
|
|
276
|
-
|
|
278
|
+
__margin: 10,
|
|
277
279
|
font: "12px Menlo, monospace",
|
|
278
280
|
align: "center",
|
|
279
281
|
baseline: "middle",
|
package/api.d.ts
CHANGED
|
@@ -27,9 +27,13 @@ export interface LayoutBox {
|
|
|
27
27
|
*/
|
|
28
28
|
ch: number;
|
|
29
29
|
/**
|
|
30
|
-
*
|
|
30
|
+
* Horizontal gutter size.
|
|
31
31
|
*/
|
|
32
|
-
|
|
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
|
-
|
|
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 `
|
|
79
|
-
* acts as initial minimum vertical space
|
|
80
|
-
* grow and if needed will propagate the new
|
|
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
|
-
*
|
|
84
|
-
* cell is requested from any parent.
|
|
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(
|
|
112
|
+
* const outer = gridLayout(0, 0, 200, 1, 16, 4);
|
|
93
113
|
*
|
|
94
|
-
* //
|
|
95
|
-
*
|
|
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
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
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
|
-
*
|
|
104
|
-
*
|
|
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 (
|
|
107
|
-
*
|
|
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
|
|
135
|
+
* @param gapX - horizontal gap for child layout
|
|
136
|
+
* @param gapY - vertical gap for child layout
|
|
113
137
|
*/
|
|
114
|
-
nest(cols: number, spans?: CellSpan,
|
|
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
|
-
|
|
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
package/grid-layout.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CellSpan, IGridLayout
|
|
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,18 +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
|
|
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,
|
|
19
|
+
constructor(parent: GridLayout | null, x: number, y: number, width: number, cols: number, rowH: number, gapX: number, gapY?: number);
|
|
20
|
+
get height(): number;
|
|
19
21
|
colsForWidth(w: number): number;
|
|
20
22
|
rowsForHeight(h: number): number;
|
|
21
23
|
spanForSize(size: ArrayLike<number>): CellSpan;
|
|
22
24
|
spanForSize(w: number, h: number): CellSpan;
|
|
23
|
-
next(spans?: CellSpan): LayoutBox;
|
|
24
|
-
nextSquare(): LayoutBox;
|
|
25
|
-
nest(cols: number, spans?: CellSpan,
|
|
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;
|
|
26
28
|
/**
|
|
27
29
|
* Updates max rows used in this layout and all of its parents.
|
|
28
30
|
*
|
|
@@ -39,7 +41,8 @@ export declare class GridLayout implements IGridLayout<GridLayout> {
|
|
|
39
41
|
* @param width -
|
|
40
42
|
* @param cols -
|
|
41
43
|
* @param rowH -
|
|
42
|
-
* @param
|
|
44
|
+
* @param gapX -
|
|
45
|
+
* @param gapY -
|
|
43
46
|
*/
|
|
44
|
-
export declare const gridLayout: (x: number, y: number, width: number, cols?: number, rowH?: number,
|
|
47
|
+
export declare const gridLayout: (x: number, y: number, width: number, cols?: number, rowH?: number, gapX?: number, gapY?: number) => GridLayout;
|
|
45
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,25 +11,30 @@ class GridLayout {
|
|
|
10
11
|
cellH;
|
|
11
12
|
cellWG;
|
|
12
13
|
cellHG;
|
|
13
|
-
|
|
14
|
+
gapX;
|
|
15
|
+
gapY;
|
|
14
16
|
currCol;
|
|
15
17
|
currRow;
|
|
16
18
|
rows;
|
|
17
|
-
constructor(parent, x, y, width, cols, rowH,
|
|
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) *
|
|
25
|
+
this.cellW = (width - (cols - 1) * gapX) / cols;
|
|
24
26
|
this.cellH = rowH;
|
|
25
|
-
this.cellWG = this.cellW +
|
|
26
|
-
this.cellHG = rowH +
|
|
27
|
-
this.
|
|
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;
|
|
31
34
|
}
|
|
35
|
+
get height() {
|
|
36
|
+
return this.y + this.rows * this.cellHG;
|
|
37
|
+
}
|
|
32
38
|
colsForWidth(w) {
|
|
33
39
|
return Math.ceil(w / this.cellWG);
|
|
34
40
|
}
|
|
@@ -36,11 +42,11 @@ class GridLayout {
|
|
|
36
42
|
return Math.ceil(h / this.cellHG);
|
|
37
43
|
}
|
|
38
44
|
spanForSize(w, h) {
|
|
39
|
-
const size = isNumber(w) ? [w, h] : w;
|
|
45
|
+
const size = isNumber(w) ? [w, h ?? w] : w;
|
|
40
46
|
return [this.colsForWidth(size[0]), this.rowsForHeight(size[1])];
|
|
41
47
|
}
|
|
42
48
|
next(spans = __DEFAULT_SPANS) {
|
|
43
|
-
const { cellWG, cellHG,
|
|
49
|
+
const { cellWG, cellHG, gapX, gapY, cols } = this;
|
|
44
50
|
const cspan = Math.min(spans[0], cols);
|
|
45
51
|
const rspan = spans[1];
|
|
46
52
|
if (this.currCol > 0) {
|
|
@@ -51,33 +57,31 @@ class GridLayout {
|
|
|
51
57
|
} else {
|
|
52
58
|
this.currRow = this.rows;
|
|
53
59
|
}
|
|
54
|
-
const h = rspan * cellHG -
|
|
55
|
-
const cell =
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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,
|
|
59
65
|
h,
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
66
|
+
this.cellW,
|
|
67
|
+
this.cellH,
|
|
68
|
+
gapX,
|
|
69
|
+
gapY,
|
|
70
|
+
[cspan, rspan]
|
|
71
|
+
);
|
|
65
72
|
this.propagateSize(rspan);
|
|
66
73
|
this.currCol = Math.min(this.currCol + cspan, cols) % cols;
|
|
67
74
|
return cell;
|
|
68
75
|
}
|
|
69
76
|
// TODO add optional colspan arg, fix rounding
|
|
70
77
|
nextSquare() {
|
|
71
|
-
const box = this.next([
|
|
72
|
-
1,
|
|
73
|
-
Math.ceil(this.cellW / (this.cellH + this.gap)) + 1
|
|
74
|
-
]);
|
|
78
|
+
const box = this.next([1, Math.ceil(this.cellW / this.cellHG) + 1]);
|
|
75
79
|
box.h = box.w;
|
|
76
80
|
return box;
|
|
77
81
|
}
|
|
78
|
-
nest(cols, spans,
|
|
82
|
+
nest(cols, spans, gapX = this.gapX, gapY = this.gapY) {
|
|
79
83
|
const { x, y, w } = this.next(spans);
|
|
80
|
-
return new GridLayout(this, x, y, w, cols, this.cellH,
|
|
84
|
+
return new GridLayout(this, x, y, w, cols, this.cellH, gapX, gapY);
|
|
81
85
|
}
|
|
82
86
|
/**
|
|
83
87
|
* Updates max rows used in this layout and all of its parents.
|
|
@@ -85,12 +89,11 @@ class GridLayout {
|
|
|
85
89
|
* @param rspan -
|
|
86
90
|
*/
|
|
87
91
|
propagateSize(rspan) {
|
|
88
|
-
|
|
89
|
-
this.
|
|
90
|
-
this.parent?.propagateSize(rows);
|
|
92
|
+
this.rows = Math.max(this.rows, this.currRow + rspan);
|
|
93
|
+
this.parent?.propagateSize(this.rows);
|
|
91
94
|
}
|
|
92
95
|
}
|
|
93
|
-
const gridLayout = (x, y, width, cols = 1, rowH = 16,
|
|
96
|
+
const gridLayout = (x, y, width, cols = 1, rowH = 16, gapX = 4, gapY = gapX) => new GridLayout(null, x, y, width, cols, rowH, gapX, gapY);
|
|
94
97
|
export {
|
|
95
98
|
GridLayout,
|
|
96
99
|
__DEFAULT_SPANS,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/layout",
|
|
3
|
-
"version": "
|
|
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": "
|
|
101
|
+
"gitHead": "b138ae2fb8567acfe724cc675654878f60d26631\n"
|
|
102
102
|
}
|
package/stacked-layout.d.ts
CHANGED
|
@@ -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
|
|
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,
|
|
20
|
-
nest(cols: number, spans?: CellSpan,
|
|
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
|
|
55
|
+
* @param gapX -
|
|
56
|
+
* @param gapY -
|
|
54
57
|
*/
|
|
55
|
-
export declare const stackedLayout: (x: number, y: number, width: number, cols?: number, rowH?: number,
|
|
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,
|
|
7
|
-
super(parent, x, y, width, cols, rowH,
|
|
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,
|
|
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,
|
|
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,
|
|
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 -
|
|
32
|
-
const cell =
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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(...
|
|
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,
|
|
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
|