@meonode/canvas 1.2.0 → 1.3.1
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/README.md +320 -225
- package/dist/cjs/canvas/canvas.type.d.ts +38 -7
- package/dist/cjs/canvas/canvas.type.d.ts.map +1 -1
- package/dist/cjs/canvas/grid.canvas.util.d.ts +25 -18
- package/dist/cjs/canvas/grid.canvas.util.d.ts.map +1 -1
- package/dist/cjs/canvas/grid.canvas.util.js +304 -210
- package/dist/cjs/canvas/grid.canvas.util.js.map +1 -1
- package/dist/cjs/canvas/text.canvas.util.d.ts.map +1 -1
- package/dist/cjs/canvas/text.canvas.util.js +84 -51
- package/dist/cjs/canvas/text.canvas.util.js.map +1 -1
- package/dist/esm/canvas/canvas.type.d.ts +38 -7
- package/dist/esm/canvas/canvas.type.d.ts.map +1 -1
- package/dist/esm/canvas/grid.canvas.util.d.ts +25 -18
- package/dist/esm/canvas/grid.canvas.util.d.ts.map +1 -1
- package/dist/esm/canvas/grid.canvas.util.js +304 -210
- package/dist/esm/canvas/text.canvas.util.d.ts.map +1 -1
- package/dist/esm/canvas/text.canvas.util.js +84 -51
- package/package.json +22 -22
|
@@ -1,275 +1,369 @@
|
|
|
1
1
|
import { RowNode } from './layout.canvas.util.js';
|
|
2
2
|
import { Style } from '../constant/common.const.js';
|
|
3
|
+
import { parsePercentage } from './canvas.helper.js';
|
|
3
4
|
|
|
4
|
-
// TODO: Add comprehensive unit tests for this file.
|
|
5
5
|
/**
|
|
6
|
-
* Grid layout node that arranges children in a
|
|
7
|
-
*
|
|
8
|
-
* @extends RowNode
|
|
6
|
+
* Grid layout node that arranges children in a 2D grid.
|
|
7
|
+
* Implements a simplified version of the CSS Grid Layout algorithm.
|
|
9
8
|
*/
|
|
10
9
|
class GridNode extends RowNode {
|
|
11
|
-
columns;
|
|
12
|
-
columnGapValue;
|
|
13
|
-
rowGapValue;
|
|
14
|
-
isVertical; // True if the main axis is vertical (flexDirection: column or column-reverse)
|
|
15
10
|
/**
|
|
16
11
|
* Creates a new grid layout node
|
|
17
12
|
* @param props Grid configuration properties
|
|
18
13
|
*/
|
|
19
14
|
constructor(props) {
|
|
20
|
-
const columns = Math.max(1, props.columns || 1);
|
|
21
|
-
const direction = props.direction || 'row'; // Default to horizontal row
|
|
22
|
-
const isVertical = direction === 'column' || direction === 'column-reverse';
|
|
23
|
-
// Map direction string to Yoga FlexDirection
|
|
24
|
-
let flexDirection;
|
|
25
|
-
switch (direction) {
|
|
26
|
-
case 'row':
|
|
27
|
-
flexDirection = Style.FlexDirection.Row;
|
|
28
|
-
break;
|
|
29
|
-
case 'column':
|
|
30
|
-
flexDirection = Style.FlexDirection.Column;
|
|
31
|
-
break;
|
|
32
|
-
case 'row-reverse':
|
|
33
|
-
flexDirection = Style.FlexDirection.RowReverse;
|
|
34
|
-
break;
|
|
35
|
-
case 'column-reverse':
|
|
36
|
-
flexDirection = Style.FlexDirection.ColumnReverse;
|
|
37
|
-
break;
|
|
38
|
-
default:
|
|
39
|
-
console.warn(`[GridNode] Invalid direction "${direction}". Defaulting to "row".`);
|
|
40
|
-
flexDirection = Style.FlexDirection.Row;
|
|
41
|
-
}
|
|
42
|
-
// Determine the column and row gap values from props
|
|
43
|
-
let columnGap = 0;
|
|
44
|
-
let rowGap = 0;
|
|
45
|
-
if (typeof props.gap === 'number' || (typeof props.gap === 'string' && props.gap.trim() !== '')) {
|
|
46
|
-
// Single value applies to both row and column gaps
|
|
47
|
-
columnGap = props.gap;
|
|
48
|
-
rowGap = props.gap;
|
|
49
|
-
}
|
|
50
|
-
else if (props.gap && typeof props.gap === 'object') {
|
|
51
|
-
// Object format: prioritize a specific direction (Column/Row), then All
|
|
52
|
-
columnGap = props.gap.Column ?? props.gap.All ?? 0;
|
|
53
|
-
rowGap = props.gap.Row ?? props.gap.All ?? 0;
|
|
54
|
-
}
|
|
55
15
|
super({
|
|
56
|
-
name: 'Grid',
|
|
57
|
-
flexWrap: Style.Wrap.Wrap, // Essential for grid behavior
|
|
58
|
-
flexDirection,
|
|
59
16
|
...props,
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
// Pass undefined for gap to prevent BoxNode from trying to parse it
|
|
63
|
-
gap: undefined,
|
|
17
|
+
name: props.name || 'Grid',
|
|
18
|
+
flexWrap: Style.Wrap.Wrap,
|
|
64
19
|
});
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
this.node.setGap(Style.Gutter.Column, columnGap);
|
|
73
|
-
}
|
|
74
|
-
else if (typeof columnGap === 'string' && columnGap.endsWith('%')) {
|
|
75
|
-
this.node.setGapPercent(Style.Gutter.Column, parseFloat(columnGap));
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Helper to parse a track size definition.
|
|
23
|
+
*/
|
|
24
|
+
parseTrack(track, availableSpace) {
|
|
25
|
+
if (typeof track === 'number') {
|
|
26
|
+
return { type: 'px', value: track };
|
|
76
27
|
}
|
|
77
|
-
if (
|
|
78
|
-
|
|
28
|
+
if (track === 'auto') {
|
|
29
|
+
return { type: 'auto', value: 0 };
|
|
79
30
|
}
|
|
80
|
-
|
|
81
|
-
|
|
31
|
+
if (typeof track === 'string') {
|
|
32
|
+
if (track.endsWith('fr')) {
|
|
33
|
+
return { type: 'fr', value: parseFloat(track) };
|
|
34
|
+
}
|
|
35
|
+
if (track.endsWith('%')) {
|
|
36
|
+
return { type: '%', value: parsePercentage(track, availableSpace) };
|
|
37
|
+
}
|
|
38
|
+
// Try parsing as number (px) if just string "100"
|
|
39
|
+
const num = parseFloat(track);
|
|
40
|
+
if (!isNaN(num))
|
|
41
|
+
return { type: 'px', value: num };
|
|
82
42
|
}
|
|
43
|
+
return { type: 'auto', value: 0 };
|
|
83
44
|
}
|
|
84
45
|
/**
|
|
85
|
-
*
|
|
86
|
-
* Overridden primarily for documentation/clarity, functionality is inherited.
|
|
87
|
-
* @param child Child node to append
|
|
88
|
-
* @param index Index at which to insert the child
|
|
46
|
+
* Parses the gap property into pixels.
|
|
89
47
|
*/
|
|
90
|
-
|
|
91
|
-
|
|
48
|
+
getGapPixels(gap, width, height) {
|
|
49
|
+
let rowGap = 0;
|
|
50
|
+
let colGap = 0;
|
|
51
|
+
if (typeof gap === 'number') {
|
|
52
|
+
rowGap = colGap = gap;
|
|
53
|
+
}
|
|
54
|
+
else if (typeof gap === 'string') {
|
|
55
|
+
const val = parsePercentage(gap, width); // Use width as base for simplicity if %
|
|
56
|
+
rowGap = colGap = val;
|
|
57
|
+
}
|
|
58
|
+
else if (gap && typeof gap === 'object') {
|
|
59
|
+
const colVal = gap.Column ?? gap.All ?? 0;
|
|
60
|
+
const rowVal = gap.Row ?? gap.All ?? 0;
|
|
61
|
+
colGap = parsePercentage(colVal, width);
|
|
62
|
+
rowGap = parsePercentage(rowVal, height);
|
|
63
|
+
}
|
|
64
|
+
return { rowGap, colGap };
|
|
92
65
|
}
|
|
93
66
|
/**
|
|
94
67
|
* Update layout calculations after the initial layout is computed.
|
|
95
|
-
* This method calculates the appropriate flex-basis for children based on the
|
|
96
|
-
* number of columns and gaps, respecting the container's padding,
|
|
97
|
-
* and applies the gaps using Yoga's built-in properties.
|
|
98
68
|
*/
|
|
99
69
|
updateLayoutBasedOnComputedSize() {
|
|
100
|
-
//
|
|
101
|
-
if (this.columns <= 0 || this.children.length === 0) {
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
// Step 2: Get container dimensions and padding after the initial layout
|
|
70
|
+
// 1. Get Container Dimensions
|
|
105
71
|
const width = this.node.getComputedWidth();
|
|
106
|
-
const height = this.node.getComputedHeight();
|
|
107
72
|
const paddingLeft = this.node.getComputedPadding(Style.Edge.Left);
|
|
108
73
|
const paddingRight = this.node.getComputedPadding(Style.Edge.Right);
|
|
109
74
|
const paddingTop = this.node.getComputedPadding(Style.Edge.Top);
|
|
110
75
|
const paddingBottom = this.node.getComputedPadding(Style.Edge.Bottom);
|
|
111
|
-
// Calculate content box dimensions
|
|
112
76
|
const contentWidth = Math.max(0, width - paddingLeft - paddingRight);
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if (
|
|
121
|
-
|
|
122
|
-
if (this.columns > 1)
|
|
123
|
-
return;
|
|
77
|
+
const computedHeight = this.node.getComputedHeight();
|
|
78
|
+
const contentHeight = Math.max(0, computedHeight - paddingTop - paddingBottom);
|
|
79
|
+
const { templateColumns, templateRows, autoRows = 'auto', gap, columns } = this.props;
|
|
80
|
+
// 2. Resolve Gaps
|
|
81
|
+
const { rowGap, colGap } = this.getGapPixels(gap, contentWidth, contentHeight);
|
|
82
|
+
// 3. Resolve Columns (Tracks)
|
|
83
|
+
let explicitColTracks = templateColumns || [];
|
|
84
|
+
if (explicitColTracks.length === 0 && columns) {
|
|
85
|
+
explicitColTracks = Array(columns).fill('1fr');
|
|
124
86
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
87
|
+
if (explicitColTracks.length === 0)
|
|
88
|
+
explicitColTracks = ['1fr'];
|
|
89
|
+
const resolvedColTracks = this.resolveTracks(explicitColTracks, contentWidth, colGap);
|
|
90
|
+
// Pre-calculate Col Offsets needed for placement/width
|
|
91
|
+
const colOffsetsValues = [0];
|
|
92
|
+
for (let i = 0; i < resolvedColTracks.length; i++) {
|
|
93
|
+
colOffsetsValues.push(colOffsetsValues[i] + resolvedColTracks[i] + colGap);
|
|
129
94
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
95
|
+
// 4. Place Items & Resolve Explicit Row Tracks
|
|
96
|
+
const explicitRowTracks = templateRows || [];
|
|
97
|
+
const resolvedExplicitRowTracks = this.resolveTracks(explicitRowTracks, contentHeight, rowGap);
|
|
98
|
+
const cells = []; // true if occupied
|
|
99
|
+
const items = [];
|
|
100
|
+
const isOccupied = (r, c) => {
|
|
101
|
+
if (!cells[r])
|
|
102
|
+
return false;
|
|
103
|
+
return cells[r][c] === true;
|
|
104
|
+
};
|
|
105
|
+
const setOccupied = (r, c) => {
|
|
106
|
+
if (!cells[r])
|
|
107
|
+
cells[r] = [];
|
|
108
|
+
cells[r][c] = true;
|
|
109
|
+
};
|
|
110
|
+
let cursorRow = 0;
|
|
111
|
+
let cursorCol = 0;
|
|
112
|
+
for (const child of this.children) {
|
|
113
|
+
const childProps = child.props;
|
|
114
|
+
const { gridColumn, gridRow } = childProps;
|
|
115
|
+
let colStart;
|
|
116
|
+
let colEnd;
|
|
117
|
+
let colSpan = 1;
|
|
118
|
+
let rowStart;
|
|
119
|
+
let rowEnd;
|
|
120
|
+
let rowSpan = 1;
|
|
121
|
+
// ... Grid Placement Logic ...
|
|
122
|
+
if (gridColumn) {
|
|
123
|
+
const parts = gridColumn.split('/').map(s => s.trim());
|
|
124
|
+
if (parts[0]) {
|
|
125
|
+
if (parts[0].startsWith('span')) {
|
|
126
|
+
colSpan = parseInt(parts[0].replace('span', '')) || 1;
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
colStart = parseInt(parts[0]) - 1;
|
|
130
|
+
}
|
|
138
131
|
}
|
|
139
|
-
|
|
140
|
-
|
|
132
|
+
if (parts[1]) {
|
|
133
|
+
if (parts[1].startsWith('span')) {
|
|
134
|
+
const span = parseInt(parts[1].replace('span', '')) || 1;
|
|
135
|
+
if (colStart !== undefined) {
|
|
136
|
+
colEnd = colStart + span;
|
|
137
|
+
colSpan = span;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
// If start is undefined but end is span? Unusual. Treat as span.
|
|
141
|
+
colSpan = span;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
colEnd = parseInt(parts[1]) - 1;
|
|
146
|
+
if (colStart !== undefined) {
|
|
147
|
+
colSpan = colEnd - colStart;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
141
150
|
}
|
|
142
151
|
}
|
|
143
|
-
|
|
144
|
-
|
|
152
|
+
if (gridRow) {
|
|
153
|
+
const parts = gridRow.split('/').map(s => s.trim());
|
|
154
|
+
if (parts[0]) {
|
|
155
|
+
if (parts[0].startsWith('span')) {
|
|
156
|
+
rowSpan = parseInt(parts[0].replace('span', '')) || 1;
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
rowStart = parseInt(parts[0]) - 1;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (parts[1]) {
|
|
163
|
+
if (parts[1].startsWith('span')) {
|
|
164
|
+
const span = parseInt(parts[1].replace('span', '')) || 1;
|
|
165
|
+
if (rowStart !== undefined) {
|
|
166
|
+
rowEnd = rowStart + span;
|
|
167
|
+
rowSpan = span;
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
rowSpan = span;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
rowEnd = parseInt(parts[1]) - 1;
|
|
175
|
+
if (rowStart !== undefined) {
|
|
176
|
+
rowSpan = rowEnd - rowStart;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
145
180
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
181
|
+
if (colStart !== undefined && rowStart !== undefined) ;
|
|
182
|
+
else {
|
|
183
|
+
// Auto placement
|
|
184
|
+
let placed = false;
|
|
185
|
+
while (!placed) {
|
|
186
|
+
if (!cells[cursorRow])
|
|
187
|
+
cells[cursorRow] = [];
|
|
188
|
+
if (colStart !== undefined)
|
|
189
|
+
cursorCol = colStart;
|
|
190
|
+
let fits = true;
|
|
191
|
+
for (let r = 0; r < rowSpan; r++) {
|
|
192
|
+
for (let c = 0; c < colSpan; c++) {
|
|
193
|
+
if (isOccupied(cursorRow + r, cursorCol + c)) {
|
|
194
|
+
fits = false;
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (!fits)
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
if (fits) {
|
|
202
|
+
rowStart = cursorRow;
|
|
203
|
+
colStart = cursorCol;
|
|
204
|
+
placed = true;
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
cursorCol++;
|
|
208
|
+
if (cursorCol + colSpan > resolvedColTracks.length) {
|
|
209
|
+
cursorCol = 0;
|
|
210
|
+
cursorRow++;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
159
213
|
}
|
|
160
|
-
|
|
161
|
-
|
|
214
|
+
cursorCol += colSpan;
|
|
215
|
+
if (cursorCol >= resolvedColTracks.length) {
|
|
216
|
+
cursorCol = 0;
|
|
217
|
+
cursorRow++;
|
|
162
218
|
}
|
|
163
|
-
|
|
164
|
-
|
|
219
|
+
}
|
|
220
|
+
rowEnd = (rowStart ?? 0) + rowSpan;
|
|
221
|
+
colEnd = (colStart ?? 0) + colSpan;
|
|
222
|
+
for (let r = rowStart; r < rowEnd; r++) {
|
|
223
|
+
for (let c = colStart; c < colEnd; c++) {
|
|
224
|
+
setOccupied(r, c);
|
|
165
225
|
}
|
|
166
226
|
}
|
|
167
|
-
|
|
168
|
-
|
|
227
|
+
// CRITICAL FIX: Pre-set width on item to ensure height calculation is accurate later
|
|
228
|
+
const itemColStart = colStart;
|
|
229
|
+
const itemColEnd = colEnd;
|
|
230
|
+
// Extend local offsets if needed for spanned columns beyond track count (rare but safe)
|
|
231
|
+
while (colOffsetsValues.length <= itemColEnd) {
|
|
232
|
+
colOffsetsValues.push(colOffsetsValues[colOffsetsValues.length - 1] + 0 + colGap);
|
|
169
233
|
}
|
|
234
|
+
const cs = Math.min(itemColStart, colOffsetsValues.length - 1);
|
|
235
|
+
const ce = Math.min(itemColEnd, colOffsetsValues.length - 1);
|
|
236
|
+
const targetWidth = Math.max(0, colOffsetsValues[ce] - colOffsetsValues[cs] - colGap);
|
|
237
|
+
child.node.setWidth(targetWidth);
|
|
238
|
+
child.node.calculateLayout(targetWidth, Number.NaN, Style.Direction.LTR);
|
|
239
|
+
// Recursively finalize nested children (e.g. inner Grids) so their
|
|
240
|
+
// computed heights are accurate before we measure row sizes.
|
|
241
|
+
child.finalizeLayout();
|
|
242
|
+
if (child.node.isDirty()) {
|
|
243
|
+
child.node.calculateLayout(targetWidth, Number.NaN, Style.Direction.LTR);
|
|
244
|
+
}
|
|
245
|
+
items.push({ node: child, rowStart: rowStart, rowEnd: rowEnd, colStart: itemColStart, colEnd: itemColEnd });
|
|
170
246
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
//
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
// Total space taken up by gaps on the main axis
|
|
183
|
-
const totalGapSpaceOnMainAxis = this.columns > 1 ? mainAxisGapPixels * (this.columns - 1) : 0;
|
|
184
|
-
// Calculate the space available *only* for the items themselves
|
|
185
|
-
const availableSpaceOnMainAxis = Math.max(0, mainAxisContentSize - totalGapSpaceOnMainAxis);
|
|
186
|
-
// Calculate the exact pixel of the total content size that each item should occupy
|
|
187
|
-
const exactItemWidth = availableSpaceOnMainAxis / this.columns;
|
|
188
|
-
// Ensure it's not negative (shouldn't happen, but safety)
|
|
189
|
-
childWidth = Math.max(0, exactItemWidth - 0.5); // Slightly reduce to avoid rounding issues
|
|
190
|
-
}
|
|
191
|
-
else if (this.columns === 1) {
|
|
192
|
-
// If only one column, it takes up the full basis (gaps don't apply)
|
|
193
|
-
childWidth = mainAxisContentSize;
|
|
194
|
-
}
|
|
195
|
-
// Clamp basis percentage between 0 and 100 (mostly redundant after floor/max(0) but safe)
|
|
196
|
-
childWidth = Math.max(0, Math.min(mainAxisContentSize, childWidth));
|
|
197
|
-
// Step 6: Apply layout properties to children
|
|
198
|
-
let childrenNeedRecalculation = false;
|
|
199
|
-
for (const child of this.children) {
|
|
200
|
-
let childChanged = false;
|
|
201
|
-
const currentLayoutWidth = child.node.getWidth();
|
|
202
|
-
const currentWidthValue = currentLayoutWidth.value;
|
|
203
|
-
const currentWidthUnit = currentLayoutWidth.unit;
|
|
204
|
-
let widthNeedsUpdate = false;
|
|
205
|
-
if (currentWidthUnit === Style.Unit.Point) {
|
|
206
|
-
// If current width is in points, check if the value is significantly different
|
|
207
|
-
if (Math.abs(currentWidthValue - childWidth) > 0.01) {
|
|
208
|
-
widthNeedsUpdate = true;
|
|
247
|
+
// 6. Finalize Rows (Implicit)
|
|
248
|
+
const totalRowsNeeded = Math.max(resolvedExplicitRowTracks.length, ...items.map(i => i.rowEnd));
|
|
249
|
+
const resolvedRowTracks = [...resolvedExplicitRowTracks];
|
|
250
|
+
// Fill implicit rows
|
|
251
|
+
for (let r = resolvedExplicitRowTracks.length; r < totalRowsNeeded; r++) {
|
|
252
|
+
let rowSize = 0;
|
|
253
|
+
// Better 'auto' handling:
|
|
254
|
+
if (autoRows === 'auto') {
|
|
255
|
+
const rowItems = items.filter(i => i.rowStart === r && i.rowEnd - i.rowStart === 1);
|
|
256
|
+
for (const item of rowItems) {
|
|
257
|
+
rowSize = Math.max(rowSize, item.node.node.getComputedHeight());
|
|
209
258
|
}
|
|
210
259
|
}
|
|
211
260
|
else {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
}
|
|
215
|
-
if (widthNeedsUpdate) {
|
|
216
|
-
child.node.setWidth(childWidth);
|
|
217
|
-
childChanged = true;
|
|
261
|
+
const parsed = this.parseTrack(autoRows, contentHeight);
|
|
262
|
+
rowSize = parsed.value;
|
|
218
263
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
264
|
+
resolvedRowTracks.push(rowSize);
|
|
265
|
+
}
|
|
266
|
+
// 6. Calculate Offsets (Rows) & Final Layout Application
|
|
267
|
+
const colOffsets = colOffsetsValues; // Re-use
|
|
268
|
+
const rowOffsets = [0];
|
|
269
|
+
for (let i = 0; i < resolvedRowTracks.length; i++) {
|
|
270
|
+
let size = resolvedRowTracks[i];
|
|
271
|
+
// Re-check auto-sized explicit rows (value 0)
|
|
272
|
+
if (size === 0) {
|
|
273
|
+
const rowItems = items.filter(it => it.rowStart === i && it.rowEnd - it.rowStart === 1);
|
|
274
|
+
for (const item of rowItems) {
|
|
275
|
+
size = Math.max(size, item.node.node.getComputedHeight());
|
|
276
|
+
}
|
|
277
|
+
resolvedRowTracks[i] = size;
|
|
223
278
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
279
|
+
rowOffsets.push(rowOffsets[i] + size + rowGap);
|
|
280
|
+
}
|
|
281
|
+
// 7. Apply Positions
|
|
282
|
+
let childrenChanged = false;
|
|
283
|
+
for (const item of items) {
|
|
284
|
+
const x = colOffsets[item.colStart] + paddingLeft;
|
|
285
|
+
while (colOffsets.length <= item.colEnd) {
|
|
286
|
+
colOffsets.push(colOffsets[colOffsets.length - 1] + 0 + colGap);
|
|
227
287
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
288
|
+
const widthStart = colOffsets[item.colStart];
|
|
289
|
+
const widthEnd = colOffsets[item.colEnd];
|
|
290
|
+
const totalWidth = Math.max(0, widthEnd - widthStart - colGap);
|
|
291
|
+
const y = rowOffsets[item.rowStart] + paddingTop;
|
|
292
|
+
const heightStart = rowOffsets[item.rowStart];
|
|
293
|
+
const heightEnd = rowOffsets[item.rowEnd];
|
|
294
|
+
const totalHeight = Math.max(0, heightEnd - heightStart - rowGap);
|
|
295
|
+
const childNode = item.node.node;
|
|
296
|
+
if (childNode.getPositionType() !== Style.PositionType.Absolute) {
|
|
297
|
+
childNode.setPositionType(Style.PositionType.Absolute);
|
|
298
|
+
childrenChanged = true;
|
|
232
299
|
}
|
|
233
|
-
if (
|
|
234
|
-
|
|
235
|
-
|
|
300
|
+
if (childNode.getPosition(Style.Edge.Left).value !== x) {
|
|
301
|
+
childNode.setPosition(Style.Edge.Left, x);
|
|
302
|
+
childrenChanged = true;
|
|
236
303
|
}
|
|
237
|
-
if (
|
|
238
|
-
|
|
239
|
-
|
|
304
|
+
if (childNode.getPosition(Style.Edge.Top).value !== y) {
|
|
305
|
+
childNode.setPosition(Style.Edge.Top, y);
|
|
306
|
+
childrenChanged = true;
|
|
240
307
|
}
|
|
241
|
-
if (
|
|
242
|
-
|
|
243
|
-
|
|
308
|
+
if (childNode.getWidth().unit !== Style.Unit.Point || Math.abs(childNode.getWidth().value - totalWidth) > 0.1) {
|
|
309
|
+
childNode.setWidth(totalWidth);
|
|
310
|
+
childrenChanged = true;
|
|
244
311
|
}
|
|
245
|
-
if (
|
|
246
|
-
|
|
247
|
-
|
|
312
|
+
if (childNode.getHeight().unit !== Style.Unit.Point || Math.abs(childNode.getHeight().value - totalHeight) > 0.1) {
|
|
313
|
+
childNode.setHeight(totalHeight);
|
|
314
|
+
childrenChanged = true;
|
|
248
315
|
}
|
|
249
316
|
}
|
|
250
|
-
//
|
|
251
|
-
const
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
gapsChanged = true;
|
|
258
|
-
}
|
|
259
|
-
if (Math.abs(currentRowGap - rowGapPixels) > 0.001) {
|
|
260
|
-
this.node.setGap(Style.Gutter.Row, rowGapPixels);
|
|
261
|
-
gapsChanged = true;
|
|
317
|
+
// 9. Update Grid Height
|
|
318
|
+
const totalGridHeight = Math.max(0, rowOffsets[rowOffsets.length - 1] - rowGap);
|
|
319
|
+
const currentHeightStyle = this.node.getHeight();
|
|
320
|
+
if (currentHeightStyle.unit === Style.Unit.Auto || currentHeightStyle.unit === Style.Unit.Undefined) {
|
|
321
|
+
const targetTotalHeight = totalGridHeight + paddingTop + paddingBottom;
|
|
322
|
+
this.node.setHeight(targetTotalHeight);
|
|
323
|
+
childrenChanged = true;
|
|
262
324
|
}
|
|
263
|
-
|
|
264
|
-
if ((gapsChanged || childrenNeedRecalculation) && !this.node.isDirty()) {
|
|
325
|
+
if (childrenChanged && !this.node.isDirty()) {
|
|
265
326
|
this.node.markDirty();
|
|
266
327
|
}
|
|
267
328
|
}
|
|
329
|
+
/**
|
|
330
|
+
* Resolves track sizes to pixels.
|
|
331
|
+
*/
|
|
332
|
+
resolveTracks(tracks, availableSpace, gap) {
|
|
333
|
+
const resolved = [];
|
|
334
|
+
let usedSpace = 0;
|
|
335
|
+
let totalFr = 0;
|
|
336
|
+
const frIndices = [];
|
|
337
|
+
tracks.forEach((t, i) => {
|
|
338
|
+
const parsed = this.parseTrack(t, availableSpace);
|
|
339
|
+
if (parsed.type === 'px' || parsed.type === '%') {
|
|
340
|
+
resolved[i] = parsed.value;
|
|
341
|
+
usedSpace += parsed.value;
|
|
342
|
+
}
|
|
343
|
+
else if (parsed.type === 'fr') {
|
|
344
|
+
totalFr += parsed.value;
|
|
345
|
+
resolved[i] = 0;
|
|
346
|
+
frIndices.push(i);
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
resolved[i] = 0;
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
const totalGaps = Math.max(0, tracks.length - 1) * gap;
|
|
353
|
+
usedSpace += totalGaps;
|
|
354
|
+
const remainingSpace = Math.max(0, availableSpace - usedSpace);
|
|
355
|
+
if (totalFr > 0) {
|
|
356
|
+
frIndices.forEach(i => {
|
|
357
|
+
const parsed = this.parseTrack(tracks[i], availableSpace);
|
|
358
|
+
const share = (parsed.value / totalFr) * remainingSpace;
|
|
359
|
+
resolved[i] = share;
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
return resolved;
|
|
363
|
+
}
|
|
268
364
|
}
|
|
269
365
|
/**
|
|
270
366
|
* Factory function to create a new GridNode instance.
|
|
271
|
-
* @param props Grid configuration properties.
|
|
272
|
-
* @returns A new GridNode instance.
|
|
273
367
|
*/
|
|
274
368
|
const Grid = (props) => new GridNode(props);
|
|
275
369
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text.canvas.util.d.ts","sourceRoot":"","sources":["../../../src/canvas/text.canvas.util.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,yBAAyB,CAAA;AACrE,OAAO,EAAU,KAAK,wBAAwB,EAA2B,MAAM,aAAa,CAAA;AAC5F,OAAO,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAA;AAGxD;;;GAGG;AACH,qBAAa,QAAS,SAAQ,OAAO;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAC7C,OAAO,CAAC,KAAK,CAAsB;IACnC,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAwC;IACzE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAU;IACxC,OAAO,CAAC,WAAW,CAAe;IAClC,OAAO,CAAC,WAAW,CAAe;IAClC,OAAO,CAAC,kBAAkB,CAAe;IAEjC,KAAK,EAAE,SAAS,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;gBAElC,IAAI,GAAE,MAAM,GAAG,MAAW,EAAE,KAAK,GAAE,SAAc;IAuB7D;;;;;;;;OAQG;WACW,gBAAgB,CAC5B,GAAG,EAAE,wBAAwB,EAC7B,IAAI,EAAE,MAAM,EACZ,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,KAAK,GAAE;QACL,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,UAAU,CAAC,EAAE,SAAS,CAAC,YAAY,CAAC,CAAA;QACpC,SAAS,CAAC,EAAE,SAAS,CAAC,WAAW,CAAC,CAAA;QAClC,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,SAAS,CAAC,EAAE,wBAAwB,CAAC,WAAW,CAAC,CAAA;QACjD,YAAY,CAAC,EAAE,wBAAwB,CAAC,cAAc,CAAC,CAAA;KACnD;cAwBW,aAAa,IAAI,IAAI;IAoDxC;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,CAAC,sBAAsB;IA8B9B;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,CAAC,aAAa;IA+ErB,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,gBAAgB;IAyBxB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,aAAa;IAiCrB;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAQjC;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,WAAW;IA8NnB;;;;;;;;;OASG;IACH,OAAO,CAAC,YAAY;IAuKpB;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa;IAmErB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAQzB;;;;;;;;;;;;;;;;OAgBG;cACgB,cAAc,CAAC,GAAG,EAAE,wBAAwB,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;
|
|
1
|
+
{"version":3,"file":"text.canvas.util.d.ts","sourceRoot":"","sources":["../../../src/canvas/text.canvas.util.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,yBAAyB,CAAA;AACrE,OAAO,EAAU,KAAK,wBAAwB,EAA2B,MAAM,aAAa,CAAA;AAC5F,OAAO,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAA;AAGxD;;;GAGG;AACH,qBAAa,QAAS,SAAQ,OAAO;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAC7C,OAAO,CAAC,KAAK,CAAsB;IACnC,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAwC;IACzE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAU;IACxC,OAAO,CAAC,WAAW,CAAe;IAClC,OAAO,CAAC,WAAW,CAAe;IAClC,OAAO,CAAC,kBAAkB,CAAe;IAEjC,KAAK,EAAE,SAAS,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;gBAElC,IAAI,GAAE,MAAM,GAAG,MAAW,EAAE,KAAK,GAAE,SAAc;IAuB7D;;;;;;;;OAQG;WACW,gBAAgB,CAC5B,GAAG,EAAE,wBAAwB,EAC7B,IAAI,EAAE,MAAM,EACZ,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,KAAK,GAAE;QACL,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,UAAU,CAAC,EAAE,SAAS,CAAC,YAAY,CAAC,CAAA;QACpC,SAAS,CAAC,EAAE,SAAS,CAAC,WAAW,CAAC,CAAA;QAClC,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,SAAS,CAAC,EAAE,wBAAwB,CAAC,WAAW,CAAC,CAAA;QACjD,YAAY,CAAC,EAAE,wBAAwB,CAAC,cAAc,CAAC,CAAA;KACnD;cAwBW,aAAa,IAAI,IAAI;IAoDxC;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,CAAC,sBAAsB;IA8B9B;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,CAAC,aAAa;IA+ErB,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,gBAAgB;IAyBxB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,aAAa;IAiCrB;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAQjC;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,WAAW;IA8NnB;;;;;;;;;OASG;IACH,OAAO,CAAC,YAAY;IAuKpB;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa;IAmErB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAQzB;;;;;;;;;;;;;;;;OAgBG;cACgB,cAAc,CAAC,GAAG,EAAE,wBAAwB,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAiXrH;AAED;;GAEG;AACH,eAAO,MAAM,IAAI,GAAI,MAAM,MAAM,GAAG,MAAM,EAAE,QAAQ,SAAS,aAA8B,CAAA"}
|