@silvery/examples 0.17.3 → 0.17.5
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/dist/UPNG-ShUlaTDh.mjs +5074 -0
- package/dist/__vite-browser-external-2447137e-Bopa5BFR.mjs +4 -0
- package/dist/_banner-A70_y2Vi.mjs +43 -0
- package/dist/ansi-0VXlUmNn.mjs +16397 -0
- package/dist/apng-B0gRaDVT.mjs +3 -0
- package/dist/apng-BTRDTfDW.mjs +68 -0
- package/dist/apps/aichat/index.mjs +1298 -0
- package/dist/apps/app-todo.mjs +138 -0
- package/dist/apps/async-data.mjs +203 -0
- package/dist/apps/cli-wizard.mjs +338 -0
- package/dist/apps/clipboard.mjs +197 -0
- package/dist/apps/components.mjs +863 -0
- package/dist/apps/data-explorer.mjs +482 -0
- package/dist/apps/dev-tools.mjs +396 -0
- package/dist/apps/explorer.mjs +697 -0
- package/dist/apps/gallery.mjs +765 -0
- package/dist/apps/inline-bench.mjs +115 -0
- package/dist/apps/kanban.mjs +279 -0
- package/dist/apps/layout-ref.mjs +186 -0
- package/dist/apps/outline.mjs +202 -0
- package/dist/apps/paste-demo.mjs +188 -0
- package/dist/apps/scroll.mjs +85 -0
- package/dist/apps/search-filter.mjs +286 -0
- package/dist/apps/selection.mjs +354 -0
- package/dist/apps/spatial-focus-demo.mjs +387 -0
- package/dist/apps/task-list.mjs +257 -0
- package/dist/apps/terminal-caps-demo.mjs +314 -0
- package/dist/apps/terminal.mjs +871 -0
- package/dist/apps/text-selection-demo.mjs +253 -0
- package/dist/apps/textarea.mjs +177 -0
- package/dist/apps/theme.mjs +660 -0
- package/dist/apps/transform.mjs +214 -0
- package/dist/apps/virtual-10k.mjs +421 -0
- package/dist/assets/resvgjs.darwin-arm64-BtufyGW1.node +0 -0
- package/dist/backends-Dj-11kZF.mjs +1179 -0
- package/dist/backends-U3QwStfO.mjs +3 -0
- package/dist/{cli.mjs → bin/cli.mjs} +15 -19
- package/dist/chunk-BSw8zbkd.mjs +37 -0
- package/dist/components/counter.mjs +47 -0
- package/dist/components/hello.mjs +30 -0
- package/dist/components/progress-bar.mjs +58 -0
- package/dist/components/select-list.mjs +84 -0
- package/dist/components/spinner.mjs +56 -0
- package/dist/components/text-input.mjs +61 -0
- package/dist/components/virtual-list.mjs +50 -0
- package/dist/flexily-zero-adapter-ByVzLTFP.mjs +3374 -0
- package/dist/gif-B6NGH5gs.mjs +3 -0
- package/dist/gif-CfkOF-iG.mjs +71 -0
- package/dist/gifenc-BI4ihP_T.mjs +728 -0
- package/dist/key-mapping-5oYQdAQE.mjs +3 -0
- package/dist/key-mapping-D4LR1go6.mjs +130 -0
- package/dist/layout/dashboard.mjs +1203 -0
- package/dist/layout/live-resize.mjs +302 -0
- package/dist/layout/overflow.mjs +69 -0
- package/dist/layout/text-layout.mjs +334 -0
- package/dist/node-nsrAOjH4.mjs +1083 -0
- package/dist/plugins-CT0DdV_E.mjs +3056 -0
- package/dist/resvg-js-Cnk2o49d.mjs +201 -0
- package/dist/src-9ZhfQyzD.mjs +814 -0
- package/dist/src-CUUOuRH6.mjs +5322 -0
- package/dist/src-jO3Zuzjj.mjs +23538 -0
- package/dist/usingCtx-CsEf0xO3.mjs +57 -0
- package/dist/yoga-adapter-BSQHuMV9.mjs +237 -0
- package/package.json +21 -14
- package/_banner.tsx +0 -60
- package/apps/aichat/components.tsx +0 -469
- package/apps/aichat/index.tsx +0 -220
- package/apps/aichat/script.ts +0 -460
- package/apps/aichat/state.ts +0 -325
- package/apps/aichat/types.ts +0 -19
- package/apps/app-todo.tsx +0 -201
- package/apps/async-data.tsx +0 -196
- package/apps/cli-wizard.tsx +0 -332
- package/apps/clipboard.tsx +0 -183
- package/apps/components.tsx +0 -658
- package/apps/data-explorer.tsx +0 -490
- package/apps/dev-tools.tsx +0 -395
- package/apps/explorer.tsx +0 -731
- package/apps/gallery.tsx +0 -653
- package/apps/inline-bench.tsx +0 -138
- package/apps/kanban.tsx +0 -265
- package/apps/layout-ref.tsx +0 -173
- package/apps/outline.tsx +0 -160
- package/apps/panes/index.tsx +0 -203
- package/apps/paste-demo.tsx +0 -185
- package/apps/scroll.tsx +0 -80
- package/apps/search-filter.tsx +0 -240
- package/apps/selection.tsx +0 -346
- package/apps/spatial-focus-demo.tsx +0 -372
- package/apps/task-list.tsx +0 -271
- package/apps/terminal-caps-demo.tsx +0 -317
- package/apps/terminal.tsx +0 -784
- package/apps/text-selection-demo.tsx +0 -193
- package/apps/textarea.tsx +0 -155
- package/apps/theme.tsx +0 -515
- package/apps/transform.tsx +0 -229
- package/apps/virtual-10k.tsx +0 -405
- package/apps/vterm-demo/index.tsx +0 -216
- package/components/counter.tsx +0 -49
- package/components/hello.tsx +0 -38
- package/components/progress-bar.tsx +0 -52
- package/components/select-list.tsx +0 -54
- package/components/spinner.tsx +0 -44
- package/components/text-input.tsx +0 -61
- package/components/virtual-list.tsx +0 -56
- package/dist/cli.d.mts +0 -1
- package/dist/cli.mjs.map +0 -1
- package/layout/dashboard.tsx +0 -953
- package/layout/live-resize.tsx +0 -282
- package/layout/overflow.tsx +0 -51
- package/layout/text-layout.tsx +0 -283
|
@@ -0,0 +1,3374 @@
|
|
|
1
|
+
import { o as __toESM } from "./chunk-BSw8zbkd.mjs";
|
|
2
|
+
//#region ../../flexily/src/utils.ts
|
|
3
|
+
/**
|
|
4
|
+
* Flexily Utility Functions
|
|
5
|
+
*
|
|
6
|
+
* Helper functions for edge value manipulation and value resolution.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Shared traversal stack for iterative tree operations.
|
|
10
|
+
* Avoids recursion (prevents stack overflow on deep trees) and avoids
|
|
11
|
+
* allocation during layout passes.
|
|
12
|
+
*/
|
|
13
|
+
const traversalStack = [];
|
|
14
|
+
/**
|
|
15
|
+
* Set a value on an edge array (supports all edge types including logical START/END).
|
|
16
|
+
*/
|
|
17
|
+
function setEdgeValue(arr, edge, value, unit) {
|
|
18
|
+
const v = {
|
|
19
|
+
value,
|
|
20
|
+
unit
|
|
21
|
+
};
|
|
22
|
+
switch (edge) {
|
|
23
|
+
case 0:
|
|
24
|
+
arr[0] = v;
|
|
25
|
+
break;
|
|
26
|
+
case 1:
|
|
27
|
+
arr[1] = v;
|
|
28
|
+
break;
|
|
29
|
+
case 2:
|
|
30
|
+
arr[2] = v;
|
|
31
|
+
break;
|
|
32
|
+
case 3:
|
|
33
|
+
arr[3] = v;
|
|
34
|
+
break;
|
|
35
|
+
case 6:
|
|
36
|
+
arr[0] = v;
|
|
37
|
+
arr[2] = v;
|
|
38
|
+
break;
|
|
39
|
+
case 7:
|
|
40
|
+
arr[1] = v;
|
|
41
|
+
arr[3] = v;
|
|
42
|
+
break;
|
|
43
|
+
case 8:
|
|
44
|
+
arr[0] = v;
|
|
45
|
+
arr[1] = v;
|
|
46
|
+
arr[2] = v;
|
|
47
|
+
arr[3] = v;
|
|
48
|
+
break;
|
|
49
|
+
case 4:
|
|
50
|
+
arr[4] = v;
|
|
51
|
+
break;
|
|
52
|
+
case 5:
|
|
53
|
+
arr[5] = v;
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Set a border value on an edge array.
|
|
59
|
+
*/
|
|
60
|
+
function setEdgeBorder(arr, edge, value) {
|
|
61
|
+
switch (edge) {
|
|
62
|
+
case 0:
|
|
63
|
+
arr[0] = value;
|
|
64
|
+
break;
|
|
65
|
+
case 1:
|
|
66
|
+
arr[1] = value;
|
|
67
|
+
break;
|
|
68
|
+
case 2:
|
|
69
|
+
arr[2] = value;
|
|
70
|
+
break;
|
|
71
|
+
case 3:
|
|
72
|
+
arr[3] = value;
|
|
73
|
+
break;
|
|
74
|
+
case 6:
|
|
75
|
+
arr[0] = value;
|
|
76
|
+
arr[2] = value;
|
|
77
|
+
break;
|
|
78
|
+
case 7:
|
|
79
|
+
arr[1] = value;
|
|
80
|
+
arr[3] = value;
|
|
81
|
+
break;
|
|
82
|
+
case 8:
|
|
83
|
+
arr[0] = value;
|
|
84
|
+
arr[1] = value;
|
|
85
|
+
arr[2] = value;
|
|
86
|
+
arr[3] = value;
|
|
87
|
+
break;
|
|
88
|
+
case 4:
|
|
89
|
+
arr[4] = value;
|
|
90
|
+
break;
|
|
91
|
+
case 5:
|
|
92
|
+
arr[5] = value;
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Get a value from an edge array.
|
|
98
|
+
*/
|
|
99
|
+
function getEdgeValue(arr, edge) {
|
|
100
|
+
switch (edge) {
|
|
101
|
+
case 0: return arr[0];
|
|
102
|
+
case 1: return arr[1];
|
|
103
|
+
case 2: return arr[2];
|
|
104
|
+
case 3: return arr[3];
|
|
105
|
+
case 4: return arr[4];
|
|
106
|
+
case 5: return arr[5];
|
|
107
|
+
default: return arr[0];
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Get a border value from an edge array.
|
|
112
|
+
*/
|
|
113
|
+
function getEdgeBorderValue(arr, edge) {
|
|
114
|
+
switch (edge) {
|
|
115
|
+
case 0: return arr[0];
|
|
116
|
+
case 1: return arr[1];
|
|
117
|
+
case 2: return arr[2];
|
|
118
|
+
case 3: return arr[3];
|
|
119
|
+
case 4: return arr[4];
|
|
120
|
+
case 5: return arr[5];
|
|
121
|
+
default: return arr[0];
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Resolve a value (point or percent) to an absolute number.
|
|
126
|
+
*/
|
|
127
|
+
function resolveValue(value, availableSize) {
|
|
128
|
+
switch (value.unit) {
|
|
129
|
+
case 1: return value.value;
|
|
130
|
+
case 2:
|
|
131
|
+
if (Number.isNaN(availableSize)) return 0;
|
|
132
|
+
return availableSize * (value.value / 100);
|
|
133
|
+
default: return 0;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Apply min/max constraints to a size.
|
|
138
|
+
*
|
|
139
|
+
* CSS behavior:
|
|
140
|
+
* - min: Floor constraint. Does NOT affect children's layout — the container expands
|
|
141
|
+
* after shrink-wrap. When size is NaN (auto-sized), min is NOT applied here;
|
|
142
|
+
* the post-shrink-wrap applyMinMax call (Phase 9) handles it.
|
|
143
|
+
* - max: Ceiling constraint. DOES affect children's layout — content wraps/clips
|
|
144
|
+
* within the max. When size is NaN (auto-sized), max constrains the container
|
|
145
|
+
* so children are laid out within the max bound.
|
|
146
|
+
*
|
|
147
|
+
* Percent constraints that can't resolve (available is NaN) are skipped entirely,
|
|
148
|
+
* since resolveValue returns 0 for percent-against-NaN, which would incorrectly
|
|
149
|
+
* clamp sizes to 0.
|
|
150
|
+
*/
|
|
151
|
+
function applyMinMax(size, min, max, available) {
|
|
152
|
+
let result = size;
|
|
153
|
+
if (max.unit !== 0) if (max.unit === 2 && Number.isNaN(available)) {} else {
|
|
154
|
+
const maxValue = resolveValue(max, available);
|
|
155
|
+
if (!Number.isNaN(maxValue)) if (Number.isNaN(result)) {
|
|
156
|
+
if (maxValue !== Infinity) result = maxValue;
|
|
157
|
+
} else result = Math.min(result, maxValue);
|
|
158
|
+
}
|
|
159
|
+
if (min.unit !== 0) if (min.unit === 2 && Number.isNaN(available)) {} else {
|
|
160
|
+
const minValue = resolveValue(min, available);
|
|
161
|
+
if (!Number.isNaN(minValue)) {
|
|
162
|
+
if (!Number.isNaN(result)) result = Math.max(result, minValue);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return result;
|
|
166
|
+
}
|
|
167
|
+
//#endregion
|
|
168
|
+
//#region ../../flexily/src/logger.ts
|
|
169
|
+
let _logger = null;
|
|
170
|
+
async function createFallbackLogger(namespace) {
|
|
171
|
+
try {
|
|
172
|
+
const { default: createDebug } = await import("./src-9ZhfQyzD.mjs").then((m) => /* @__PURE__ */ __toESM(m.default, 1));
|
|
173
|
+
const debug = createDebug(namespace);
|
|
174
|
+
return { debug: debug.enabled ? debug : void 0 };
|
|
175
|
+
} catch {
|
|
176
|
+
return { debug: void 0 };
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
async function detectLogger(namespace) {
|
|
180
|
+
try {
|
|
181
|
+
const { createLogger } = await import("loggily");
|
|
182
|
+
const logger = createLogger(namespace);
|
|
183
|
+
if (logger.debug) {
|
|
184
|
+
const originalDebug = logger.debug;
|
|
185
|
+
return { debug: (msg, ...args) => {
|
|
186
|
+
let i = 0;
|
|
187
|
+
originalDebug(msg.replace(/%[sdOo]/g, () => {
|
|
188
|
+
const arg = args[i++];
|
|
189
|
+
if (arg === void 0) return "";
|
|
190
|
+
if (arg === null) return "null";
|
|
191
|
+
if (typeof arg === "object") return JSON.stringify(arg);
|
|
192
|
+
return String(arg);
|
|
193
|
+
}));
|
|
194
|
+
} };
|
|
195
|
+
}
|
|
196
|
+
return { debug: void 0 };
|
|
197
|
+
} catch {
|
|
198
|
+
return createFallbackLogger(namespace);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
_logger = await detectLogger("flexily:layout");
|
|
202
|
+
/** Logger instance - use with optional chaining: `log.debug?.('message')` */
|
|
203
|
+
const log = { get debug() {
|
|
204
|
+
return _logger?.debug;
|
|
205
|
+
} };
|
|
206
|
+
//#endregion
|
|
207
|
+
//#region ../../flexily/src/trace.ts
|
|
208
|
+
let _trace = null;
|
|
209
|
+
/** Get the current trace recorder (null when tracing is disabled). */
|
|
210
|
+
function getTrace() {
|
|
211
|
+
return _trace;
|
|
212
|
+
}
|
|
213
|
+
//#endregion
|
|
214
|
+
//#region ../../flexily/src/layout-helpers.ts
|
|
215
|
+
/**
|
|
216
|
+
* Layout Helper Functions
|
|
217
|
+
*
|
|
218
|
+
* Edge resolution helpers for margins, padding, borders.
|
|
219
|
+
* These are pure functions with no state — safe to extract.
|
|
220
|
+
*/
|
|
221
|
+
/**
|
|
222
|
+
* Check if flex direction is row-oriented (horizontal main axis).
|
|
223
|
+
*/
|
|
224
|
+
function isRowDirection(flexDirection) {
|
|
225
|
+
return flexDirection === 2 || flexDirection === 3;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Check if flex direction is reversed.
|
|
229
|
+
*/
|
|
230
|
+
function isReverseDirection(flexDirection) {
|
|
231
|
+
return flexDirection === 3 || flexDirection === 1;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Get the logical edge value (START/END) for a given physical index.
|
|
235
|
+
* Returns undefined if no logical value applies to this physical edge.
|
|
236
|
+
*
|
|
237
|
+
* EDGE_START/EDGE_END always resolve along the inline (horizontal) axis,
|
|
238
|
+
* regardless of flex direction. Direction (LTR/RTL) determines the mapping:
|
|
239
|
+
* - LTR: START->left, END->right
|
|
240
|
+
* - RTL: START->right, END->left
|
|
241
|
+
*/
|
|
242
|
+
function getLogicalEdgeValue(arr, physicalIndex, _flexDirection, direction = 1) {
|
|
243
|
+
const isRTL = direction === 2;
|
|
244
|
+
if (physicalIndex === 0) return isRTL ? arr[5] : arr[4];
|
|
245
|
+
else if (physicalIndex === 2) return isRTL ? arr[4] : arr[5];
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Resolve logical (START/END) margins/padding to physical values.
|
|
249
|
+
* EDGE_START/EDGE_END always resolve along the inline (horizontal) axis:
|
|
250
|
+
* - LTR: START->left, END->right
|
|
251
|
+
* - RTL: START->right, END->left
|
|
252
|
+
*
|
|
253
|
+
* Physical edges (LEFT/RIGHT/TOP/BOTTOM) are used directly.
|
|
254
|
+
* When both physical and logical are set, logical takes precedence.
|
|
255
|
+
*/
|
|
256
|
+
function resolveEdgeValue(arr, physicalIndex, flexDirection, availableSize, direction = 1) {
|
|
257
|
+
const logicalValue = getLogicalEdgeValue(arr, physicalIndex, flexDirection, direction);
|
|
258
|
+
if (logicalValue && logicalValue.unit !== 0) return resolveValue(logicalValue, availableSize);
|
|
259
|
+
return resolveValue(arr[physicalIndex], availableSize);
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Check if a logical edge margin is set to auto.
|
|
263
|
+
*/
|
|
264
|
+
function isEdgeAuto(arr, physicalIndex, flexDirection, direction = 1) {
|
|
265
|
+
const logicalValue = getLogicalEdgeValue(arr, physicalIndex, flexDirection, direction);
|
|
266
|
+
if (logicalValue && logicalValue.unit !== 0) return logicalValue.unit === 3;
|
|
267
|
+
return arr[physicalIndex].unit === 3;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Resolve logical (START/END) border widths to physical values.
|
|
271
|
+
* Border values are plain numbers (always points), so resolution is simpler
|
|
272
|
+
* than for margin/padding. Uses NaN as the "not set" sentinel for logical slots.
|
|
273
|
+
* When both physical and logical are set, logical takes precedence.
|
|
274
|
+
*
|
|
275
|
+
* EDGE_START/EDGE_END always resolve along the inline (horizontal) axis,
|
|
276
|
+
* regardless of flex direction. Direction (LTR/RTL) determines the mapping:
|
|
277
|
+
* - LTR: START->left, END->right
|
|
278
|
+
* - RTL: START->right, END->left
|
|
279
|
+
*/
|
|
280
|
+
/**
|
|
281
|
+
* Resolve logical (START/END) position edges to physical values.
|
|
282
|
+
* Returns the resolved Value for a physical position index, considering
|
|
283
|
+
* logical EDGE_START/EDGE_END. Logical takes precedence over physical.
|
|
284
|
+
*
|
|
285
|
+
* EDGE_START/EDGE_END always resolve along the inline (horizontal) axis:
|
|
286
|
+
* - LTR: START->left, END->right
|
|
287
|
+
* - RTL: START->right, END->left
|
|
288
|
+
*/
|
|
289
|
+
function resolvePositionEdge(arr, physicalIndex, direction = 1) {
|
|
290
|
+
const logicalValue = getLogicalEdgeValue(arr, physicalIndex, 0, direction);
|
|
291
|
+
if (logicalValue && logicalValue.unit !== 0) return logicalValue;
|
|
292
|
+
return arr[physicalIndex];
|
|
293
|
+
}
|
|
294
|
+
function resolveEdgeBorderValue(arr, physicalIndex, _flexDirection, direction = 1) {
|
|
295
|
+
const isRTL = direction === 2;
|
|
296
|
+
let logicalSlot;
|
|
297
|
+
if (physicalIndex === 0) logicalSlot = isRTL ? 5 : 4;
|
|
298
|
+
else if (physicalIndex === 2) logicalSlot = isRTL ? 4 : 5;
|
|
299
|
+
if (logicalSlot !== void 0 && !Number.isNaN(arr[logicalSlot])) return arr[logicalSlot];
|
|
300
|
+
return arr[physicalIndex];
|
|
301
|
+
}
|
|
302
|
+
//#endregion
|
|
303
|
+
//#region ../../flexily/src/layout-traversal.ts
|
|
304
|
+
/**
|
|
305
|
+
* Mark subtree as having new layout and clear dirty flags (iterative to avoid stack overflow).
|
|
306
|
+
* This is called after layout completes to reset dirty tracking for all nodes.
|
|
307
|
+
*/
|
|
308
|
+
function markSubtreeLayoutSeen(node) {
|
|
309
|
+
traversalStack.length = 0;
|
|
310
|
+
traversalStack.push(node);
|
|
311
|
+
while (traversalStack.length > 0) {
|
|
312
|
+
const current = traversalStack.pop();
|
|
313
|
+
current["_isDirty"] = false;
|
|
314
|
+
current["_hasNewLayout"] = true;
|
|
315
|
+
for (const child of current.children) traversalStack.push(child);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Count total nodes in tree (iterative to avoid stack overflow).
|
|
320
|
+
*/
|
|
321
|
+
function countNodes(node) {
|
|
322
|
+
let count = 0;
|
|
323
|
+
traversalStack.length = 0;
|
|
324
|
+
traversalStack.push(node);
|
|
325
|
+
while (traversalStack.length > 0) {
|
|
326
|
+
const current = traversalStack.pop();
|
|
327
|
+
count++;
|
|
328
|
+
for (const child of current.children) traversalStack.push(child);
|
|
329
|
+
}
|
|
330
|
+
return count;
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Propagate position delta to all descendants (iterative to avoid stack overflow).
|
|
334
|
+
* Used when parent position changes but layout is cached.
|
|
335
|
+
*
|
|
336
|
+
* Only updates the constraint fingerprint's lastOffset values, NOT layout.top/left.
|
|
337
|
+
* layout.top/left store RELATIVE positions (relative to parent's border box),
|
|
338
|
+
* so they don't change when an ancestor moves — only the absolute offset
|
|
339
|
+
* (tracked via lastOffsetX/Y) changes.
|
|
340
|
+
*/
|
|
341
|
+
function propagatePositionDelta(node, deltaX, deltaY) {
|
|
342
|
+
traversalStack.length = 0;
|
|
343
|
+
for (const child of node.children) traversalStack.push(child);
|
|
344
|
+
while (traversalStack.length > 0) {
|
|
345
|
+
const current = traversalStack.pop();
|
|
346
|
+
current.flex.lastOffsetX += deltaX;
|
|
347
|
+
current.flex.lastOffsetY += deltaY;
|
|
348
|
+
for (const child of current.children) traversalStack.push(child);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
//#endregion
|
|
352
|
+
//#region ../../flexily/src/layout-stats.ts
|
|
353
|
+
/**
|
|
354
|
+
* Layout Statistics Counters
|
|
355
|
+
*
|
|
356
|
+
* Mutable counters for debugging and benchmarking.
|
|
357
|
+
* Separated to avoid circular dependencies between layout modules.
|
|
358
|
+
*/
|
|
359
|
+
let layoutNodeCalls = 0;
|
|
360
|
+
let measureNodeCalls = 0;
|
|
361
|
+
let layoutSizingCalls = 0;
|
|
362
|
+
let layoutPositioningCalls = 0;
|
|
363
|
+
let layoutCacheHits = 0;
|
|
364
|
+
function resetLayoutStats() {
|
|
365
|
+
layoutNodeCalls = 0;
|
|
366
|
+
measureNodeCalls = 0;
|
|
367
|
+
layoutSizingCalls = 0;
|
|
368
|
+
layoutPositioningCalls = 0;
|
|
369
|
+
layoutCacheHits = 0;
|
|
370
|
+
}
|
|
371
|
+
function incLayoutNodeCalls() {
|
|
372
|
+
layoutNodeCalls++;
|
|
373
|
+
}
|
|
374
|
+
function incMeasureNodeCalls() {
|
|
375
|
+
measureNodeCalls++;
|
|
376
|
+
}
|
|
377
|
+
function incLayoutSizingCalls() {
|
|
378
|
+
layoutSizingCalls++;
|
|
379
|
+
}
|
|
380
|
+
function incLayoutPositioningCalls() {
|
|
381
|
+
layoutPositioningCalls++;
|
|
382
|
+
}
|
|
383
|
+
function incLayoutCacheHits() {
|
|
384
|
+
layoutCacheHits++;
|
|
385
|
+
}
|
|
386
|
+
//#endregion
|
|
387
|
+
//#region ../../flexily/src/layout-measure.ts
|
|
388
|
+
/**
|
|
389
|
+
* Node Measurement (Intrinsic Sizing)
|
|
390
|
+
*
|
|
391
|
+
* measureNode() computes a node's width and height without calculating positions.
|
|
392
|
+
* It's a lightweight alternative to layoutNode() used during Phase 5 for
|
|
393
|
+
* intrinsic sizing of auto-sized container children.
|
|
394
|
+
*
|
|
395
|
+
* IMPORTANT: measureNode() overwrites layout.width/layout.height as a side effect.
|
|
396
|
+
* Callers MUST save/restore these fields around calls to avoid corrupting
|
|
397
|
+
* the fingerprint cache (see "Bug 1: measureNode corruption" in CLAUDE.md).
|
|
398
|
+
*/
|
|
399
|
+
/**
|
|
400
|
+
* Measure a node to get its intrinsic size without computing positions.
|
|
401
|
+
* This is a lightweight alternative to layoutNode for sizing-only passes.
|
|
402
|
+
*
|
|
403
|
+
* Sets layout.width and layout.height but NOT layout.left/top.
|
|
404
|
+
*
|
|
405
|
+
* @param node - The node to measure
|
|
406
|
+
* @param availableWidth - Available width (NaN for unconstrained)
|
|
407
|
+
* @param availableHeight - Available height (NaN for unconstrained)
|
|
408
|
+
* @param direction - Layout direction (LTR or RTL)
|
|
409
|
+
*/
|
|
410
|
+
function measureNode(node, availableWidth, availableHeight, direction = 1) {
|
|
411
|
+
incMeasureNodeCalls();
|
|
412
|
+
const style = node.style;
|
|
413
|
+
const layout = node.layout;
|
|
414
|
+
if (style.display === 1) {
|
|
415
|
+
layout.width = 0;
|
|
416
|
+
layout.height = 0;
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
const marginLeft = resolveEdgeValue(style.margin, 0, style.flexDirection, availableWidth, direction);
|
|
420
|
+
const marginTop = resolveEdgeValue(style.margin, 1, style.flexDirection, availableWidth, direction);
|
|
421
|
+
const marginRight = resolveEdgeValue(style.margin, 2, style.flexDirection, availableWidth, direction);
|
|
422
|
+
const marginBottom = resolveEdgeValue(style.margin, 3, style.flexDirection, availableWidth, direction);
|
|
423
|
+
const paddingLeft = resolveEdgeValue(style.padding, 0, style.flexDirection, availableWidth, direction);
|
|
424
|
+
const paddingTop = resolveEdgeValue(style.padding, 1, style.flexDirection, availableWidth, direction);
|
|
425
|
+
const paddingRight = resolveEdgeValue(style.padding, 2, style.flexDirection, availableWidth, direction);
|
|
426
|
+
const paddingBottom = resolveEdgeValue(style.padding, 3, style.flexDirection, availableWidth, direction);
|
|
427
|
+
const borderLeft = resolveEdgeBorderValue(style.border, 0, style.flexDirection, direction);
|
|
428
|
+
const borderTop = resolveEdgeBorderValue(style.border, 1, style.flexDirection, direction);
|
|
429
|
+
const borderRight = resolveEdgeBorderValue(style.border, 2, style.flexDirection, direction);
|
|
430
|
+
const borderBottom = resolveEdgeBorderValue(style.border, 3, style.flexDirection, direction);
|
|
431
|
+
let nodeWidth;
|
|
432
|
+
if (style.width.unit === 1) nodeWidth = style.width.value;
|
|
433
|
+
else if (style.width.unit === 2) nodeWidth = resolveValue(style.width, availableWidth);
|
|
434
|
+
else if (Number.isNaN(availableWidth)) nodeWidth = NaN;
|
|
435
|
+
else nodeWidth = availableWidth - marginLeft - marginRight;
|
|
436
|
+
nodeWidth = applyMinMax(nodeWidth, style.minWidth, style.maxWidth, availableWidth);
|
|
437
|
+
let nodeHeight;
|
|
438
|
+
if (style.height.unit === 1) nodeHeight = style.height.value;
|
|
439
|
+
else if (style.height.unit === 2) nodeHeight = resolveValue(style.height, availableHeight);
|
|
440
|
+
else if (Number.isNaN(availableHeight)) nodeHeight = NaN;
|
|
441
|
+
else nodeHeight = availableHeight - marginTop - marginBottom;
|
|
442
|
+
const aspectRatio = style.aspectRatio;
|
|
443
|
+
if (!Number.isNaN(aspectRatio) && aspectRatio > 0) {
|
|
444
|
+
const widthIsAuto = Number.isNaN(nodeWidth) || style.width.unit === 3;
|
|
445
|
+
const heightIsAuto = Number.isNaN(nodeHeight) || style.height.unit === 3;
|
|
446
|
+
if (widthIsAuto && !heightIsAuto && !Number.isNaN(nodeHeight)) {
|
|
447
|
+
nodeWidth = nodeHeight * aspectRatio;
|
|
448
|
+
nodeWidth = applyMinMax(nodeWidth, style.minWidth, style.maxWidth, availableWidth);
|
|
449
|
+
} else if (heightIsAuto && !widthIsAuto && !Number.isNaN(nodeWidth)) nodeHeight = nodeWidth / aspectRatio;
|
|
450
|
+
}
|
|
451
|
+
nodeHeight = applyMinMax(nodeHeight, style.minHeight, style.maxHeight, availableHeight);
|
|
452
|
+
const innerLeft = borderLeft + paddingLeft;
|
|
453
|
+
const innerTop = borderTop + paddingTop;
|
|
454
|
+
const innerRight = borderRight + paddingRight;
|
|
455
|
+
const innerBottom = borderBottom + paddingBottom;
|
|
456
|
+
const minInnerWidth = innerLeft + innerRight;
|
|
457
|
+
const minInnerHeight = innerTop + innerBottom;
|
|
458
|
+
if (!Number.isNaN(nodeWidth) && nodeWidth < minInnerWidth) nodeWidth = minInnerWidth;
|
|
459
|
+
if (!Number.isNaN(nodeHeight) && nodeHeight < minInnerHeight) nodeHeight = minInnerHeight;
|
|
460
|
+
const contentWidth = Number.isNaN(nodeWidth) ? NaN : Math.max(0, nodeWidth - innerLeft - innerRight);
|
|
461
|
+
const contentHeight = Number.isNaN(nodeHeight) ? NaN : Math.max(0, nodeHeight - innerTop - innerBottom);
|
|
462
|
+
if (node.hasMeasureFunc() && node.children.length === 0) {
|
|
463
|
+
const widthIsAuto = style.width.unit === 3 || style.width.unit === 0 || Number.isNaN(nodeWidth);
|
|
464
|
+
const heightIsAuto = style.height.unit === 3 || style.height.unit === 0 || Number.isNaN(nodeHeight);
|
|
465
|
+
const widthMode = widthIsAuto ? 2 : 1;
|
|
466
|
+
const heightMode = heightIsAuto ? 0 : 1;
|
|
467
|
+
const measureWidth = Number.isNaN(contentWidth) ? Infinity : contentWidth;
|
|
468
|
+
const measureHeight = Number.isNaN(contentHeight) ? Infinity : contentHeight;
|
|
469
|
+
const measured = node.cachedMeasure(measureWidth, widthMode, measureHeight, heightMode);
|
|
470
|
+
if (widthIsAuto) nodeWidth = measured.width + innerLeft + innerRight;
|
|
471
|
+
if (heightIsAuto) nodeHeight = measured.height + innerTop + innerBottom;
|
|
472
|
+
layout.width = Math.round(nodeWidth);
|
|
473
|
+
layout.height = Math.round(nodeHeight);
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
if (node.children.length === 0) {
|
|
477
|
+
if (Number.isNaN(nodeWidth)) nodeWidth = innerLeft + innerRight;
|
|
478
|
+
if (Number.isNaN(nodeHeight)) nodeHeight = innerTop + innerBottom;
|
|
479
|
+
layout.width = Math.round(nodeWidth);
|
|
480
|
+
layout.height = Math.round(nodeHeight);
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
let relativeChildCount = 0;
|
|
484
|
+
for (const c of node.children) {
|
|
485
|
+
if (c.style.display === 1) continue;
|
|
486
|
+
if (c.style.positionType !== 2) relativeChildCount++;
|
|
487
|
+
}
|
|
488
|
+
if (relativeChildCount === 0) {
|
|
489
|
+
if (Number.isNaN(nodeWidth)) nodeWidth = minInnerWidth;
|
|
490
|
+
if (Number.isNaN(nodeHeight)) nodeHeight = minInnerHeight;
|
|
491
|
+
layout.width = Math.round(nodeWidth);
|
|
492
|
+
layout.height = Math.round(nodeHeight);
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
const isRow = isRowDirection(style.flexDirection);
|
|
496
|
+
const crossAxisSize = isRow ? contentHeight : contentWidth;
|
|
497
|
+
const mainGap = isRow ? style.gap[0] : style.gap[1];
|
|
498
|
+
let totalMainSize = 0;
|
|
499
|
+
let maxCrossSize = 0;
|
|
500
|
+
let itemCount = 0;
|
|
501
|
+
for (const child of node.children) {
|
|
502
|
+
if (child.style.display === 1) continue;
|
|
503
|
+
if (child.style.positionType === 2) continue;
|
|
504
|
+
const childStyle = child.style;
|
|
505
|
+
const childMarginMain = isRow ? resolveEdgeValue(childStyle.margin, 0, style.flexDirection, contentWidth, direction) + resolveEdgeValue(childStyle.margin, 2, style.flexDirection, contentWidth, direction) : resolveEdgeValue(childStyle.margin, 1, style.flexDirection, contentWidth, direction) + resolveEdgeValue(childStyle.margin, 3, style.flexDirection, contentWidth, direction);
|
|
506
|
+
const childMarginCross = isRow ? resolveEdgeValue(childStyle.margin, 1, style.flexDirection, contentWidth, direction) + resolveEdgeValue(childStyle.margin, 3, style.flexDirection, contentWidth, direction) : resolveEdgeValue(childStyle.margin, 0, style.flexDirection, contentWidth, direction) + resolveEdgeValue(childStyle.margin, 2, style.flexDirection, contentWidth, direction);
|
|
507
|
+
const childAvailW = isRow ? NaN : crossAxisSize;
|
|
508
|
+
const childAvailH = isRow ? crossAxisSize : NaN;
|
|
509
|
+
let measuredW = 0;
|
|
510
|
+
let measuredH = 0;
|
|
511
|
+
const cached = child.getCachedLayout(childAvailW, childAvailH);
|
|
512
|
+
if (cached) incLayoutCacheHits();
|
|
513
|
+
else {
|
|
514
|
+
const savedW = child.layout.width;
|
|
515
|
+
const savedH = child.layout.height;
|
|
516
|
+
measureNode(child, childAvailW, childAvailH, direction);
|
|
517
|
+
measuredW = child.layout.width;
|
|
518
|
+
measuredH = child.layout.height;
|
|
519
|
+
child.layout.width = savedW;
|
|
520
|
+
child.layout.height = savedH;
|
|
521
|
+
child.setCachedLayout(childAvailW, childAvailH, measuredW, measuredH);
|
|
522
|
+
}
|
|
523
|
+
const childMainSize = cached ? isRow ? cached.width : cached.height : isRow ? measuredW : measuredH;
|
|
524
|
+
const childCrossSize = cached ? isRow ? cached.height : cached.width : isRow ? measuredH : measuredW;
|
|
525
|
+
totalMainSize += childMainSize + childMarginMain;
|
|
526
|
+
maxCrossSize = Math.max(maxCrossSize, childCrossSize + childMarginCross);
|
|
527
|
+
itemCount++;
|
|
528
|
+
}
|
|
529
|
+
if (itemCount > 1) totalMainSize += mainGap * (itemCount - 1);
|
|
530
|
+
if (Number.isNaN(nodeWidth)) nodeWidth = (isRow ? totalMainSize : maxCrossSize) + innerLeft + innerRight;
|
|
531
|
+
if (Number.isNaN(nodeHeight)) nodeHeight = (isRow ? maxCrossSize : totalMainSize) + innerTop + innerBottom;
|
|
532
|
+
nodeWidth = applyMinMax(nodeWidth, style.minWidth, style.maxWidth, availableWidth);
|
|
533
|
+
nodeHeight = applyMinMax(nodeHeight, style.minHeight, style.maxHeight, availableHeight);
|
|
534
|
+
layout.width = Math.round(nodeWidth);
|
|
535
|
+
layout.height = Math.round(nodeHeight);
|
|
536
|
+
}
|
|
537
|
+
//#endregion
|
|
538
|
+
//#region ../../flexily/src/layout-flex-lines.ts
|
|
539
|
+
/**
|
|
540
|
+
* Flex Line Breaking and Space Distribution
|
|
541
|
+
*
|
|
542
|
+
* Pre-allocated arrays for zero-allocation flex-wrap layout,
|
|
543
|
+
* plus the line-breaking and flex-distribution algorithms.
|
|
544
|
+
*
|
|
545
|
+
* Re-entrancy: A user measureFunc or baselineFunc may synchronously call
|
|
546
|
+
* calculateLayout() on a separate tree. saveLineState/restoreLineState
|
|
547
|
+
* bracket such nested calls to protect the outer pass's scratch arrays.
|
|
548
|
+
*/
|
|
549
|
+
/**
|
|
550
|
+
* Maximum number of flex lines supported without dynamic allocation.
|
|
551
|
+
* If a layout exceeds this, arrays grow automatically (rare edge case).
|
|
552
|
+
* 32 lines covers virtually all real-world layouts while using minimal memory.
|
|
553
|
+
*/
|
|
554
|
+
let MAX_FLEX_LINES = 32;
|
|
555
|
+
/**
|
|
556
|
+
* Pre-allocated array for line cross sizes (reused across layout passes).
|
|
557
|
+
* Stores the computed cross-axis size of each flex line.
|
|
558
|
+
*/
|
|
559
|
+
let _lineCrossSizes = new Float64Array(MAX_FLEX_LINES);
|
|
560
|
+
/**
|
|
561
|
+
* Pre-allocated array for line cross offsets (reused across layout passes).
|
|
562
|
+
* Stores the cross-axis position offset for each flex line.
|
|
563
|
+
*/
|
|
564
|
+
let _lineCrossOffsets = new Float64Array(MAX_FLEX_LINES);
|
|
565
|
+
/**
|
|
566
|
+
* Pre-allocated array for line lengths (number of children per line).
|
|
567
|
+
* Uint16 supports up to 65535 children per line (more than sufficient).
|
|
568
|
+
*/
|
|
569
|
+
let _lineLengths = new Uint16Array(MAX_FLEX_LINES);
|
|
570
|
+
/**
|
|
571
|
+
* Pre-allocated 2D array for children per line.
|
|
572
|
+
* Avoids O(n*m) iteration when processing multi-line flex layouts.
|
|
573
|
+
* Each slot holds array of Node references for that line.
|
|
574
|
+
*/
|
|
575
|
+
let _lineChildren = Array.from({ length: MAX_FLEX_LINES }, () => []);
|
|
576
|
+
/**
|
|
577
|
+
* Pre-allocated array for per-line justify-content start offsets.
|
|
578
|
+
* Stores the main-axis starting position for each flex line.
|
|
579
|
+
*/
|
|
580
|
+
let _lineJustifyStarts = new Float64Array(MAX_FLEX_LINES);
|
|
581
|
+
/**
|
|
582
|
+
* Pre-allocated array for per-line item spacing (justify-content gaps).
|
|
583
|
+
* Stores the spacing between items for each flex line.
|
|
584
|
+
*/
|
|
585
|
+
let _lineItemSpacings = new Float64Array(MAX_FLEX_LINES);
|
|
586
|
+
/**
|
|
587
|
+
* Grow pre-allocated line arrays if needed.
|
|
588
|
+
* Called when a layout has more lines than current capacity.
|
|
589
|
+
* This is rare (>32 lines) and acceptable as a one-time allocation.
|
|
590
|
+
*/
|
|
591
|
+
function growLineArrays(needed) {
|
|
592
|
+
const newSize = Math.max(needed, MAX_FLEX_LINES * 2);
|
|
593
|
+
MAX_FLEX_LINES = newSize;
|
|
594
|
+
_lineCrossSizes = new Float64Array(newSize);
|
|
595
|
+
_lineCrossOffsets = new Float64Array(newSize);
|
|
596
|
+
_lineLengths = new Uint16Array(newSize);
|
|
597
|
+
_lineJustifyStarts = new Float64Array(newSize);
|
|
598
|
+
_lineItemSpacings = new Float64Array(newSize);
|
|
599
|
+
while (_lineChildren.length < newSize) _lineChildren.push([]);
|
|
600
|
+
}
|
|
601
|
+
/** Current re-entrancy depth. 0 = outermost (no save needed). */
|
|
602
|
+
let _layoutDepth = 0;
|
|
603
|
+
/**
|
|
604
|
+
* Enter a layout pass. If re-entrant (depth > 0), saves current line state.
|
|
605
|
+
* @returns The saved state (to pass to restoreLineState), or null at depth 0.
|
|
606
|
+
*/
|
|
607
|
+
function enterLayout() {
|
|
608
|
+
if (_layoutDepth++ === 0) return null;
|
|
609
|
+
return {
|
|
610
|
+
crossSizes: _lineCrossSizes.slice(),
|
|
611
|
+
crossOffsets: _lineCrossOffsets.slice(),
|
|
612
|
+
lengths: _lineLengths.slice(),
|
|
613
|
+
justifyStarts: _lineJustifyStarts.slice(),
|
|
614
|
+
itemSpacings: _lineItemSpacings.slice(),
|
|
615
|
+
children: _lineChildren.map((arr) => arr.slice()),
|
|
616
|
+
maxLines: MAX_FLEX_LINES
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* Exit a layout pass. If re-entrant, restores saved line state.
|
|
621
|
+
* @param saved - The state returned by enterLayout (null at depth 0).
|
|
622
|
+
*/
|
|
623
|
+
function exitLayout(saved) {
|
|
624
|
+
_layoutDepth--;
|
|
625
|
+
if (!saved) return;
|
|
626
|
+
MAX_FLEX_LINES = saved.maxLines;
|
|
627
|
+
_lineCrossSizes = saved.crossSizes;
|
|
628
|
+
_lineCrossOffsets = saved.crossOffsets;
|
|
629
|
+
_lineLengths = saved.lengths;
|
|
630
|
+
_lineJustifyStarts = saved.justifyStarts;
|
|
631
|
+
_lineItemSpacings = saved.itemSpacings;
|
|
632
|
+
_lineChildren = saved.children;
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Epsilon value for floating point comparisons in flex distribution.
|
|
636
|
+
* Used to determine when remaining space is negligible and iteration should stop.
|
|
637
|
+
*/
|
|
638
|
+
const EPSILON_FLOAT = .001;
|
|
639
|
+
/**
|
|
640
|
+
* Break children into flex lines based on available main-axis space.
|
|
641
|
+
* Zero-allocation: Sets child.flex.lineIndex directly, uses pre-allocated _lineStarts/_lineLengths.
|
|
642
|
+
*
|
|
643
|
+
* @param parent - Parent node whose children to wrap
|
|
644
|
+
* @param relativeCount - Number of relative children (those with flex.relativeIndex >= 0)
|
|
645
|
+
* @param mainAxisSize - Available main-axis space (NaN for unconstrained)
|
|
646
|
+
* @param mainGap - Gap between items on main axis
|
|
647
|
+
* @param wrap - Wrap mode (WRAP_NO_WRAP, WRAP_WRAP, WRAP_WRAP_REVERSE)
|
|
648
|
+
* @returns Number of lines created
|
|
649
|
+
*/
|
|
650
|
+
function breakIntoLines(parent, relativeCount, mainAxisSize, mainGap, wrap) {
|
|
651
|
+
if (wrap === 0 || Number.isNaN(mainAxisSize) || relativeCount === 0) {
|
|
652
|
+
const lineArr = _lineChildren[0];
|
|
653
|
+
let idx = 0;
|
|
654
|
+
for (const child of parent.children) if (child.flex.relativeIndex >= 0) {
|
|
655
|
+
child.flex.lineIndex = 0;
|
|
656
|
+
lineArr[idx++] = child;
|
|
657
|
+
}
|
|
658
|
+
lineArr.length = idx;
|
|
659
|
+
_lineLengths[0] = relativeCount;
|
|
660
|
+
_lineCrossSizes[0] = 0;
|
|
661
|
+
_lineCrossOffsets[0] = 0;
|
|
662
|
+
return 1;
|
|
663
|
+
}
|
|
664
|
+
let lineIndex = 0;
|
|
665
|
+
let lineMainSize = 0;
|
|
666
|
+
let lineChildCount = 0;
|
|
667
|
+
let lineChildIdx = 0;
|
|
668
|
+
for (const child of parent.children) {
|
|
669
|
+
if (child.flex.relativeIndex < 0) continue;
|
|
670
|
+
const flex = child.flex;
|
|
671
|
+
const childMainSize = Math.max(flex.minMain, Math.min(flex.maxMain, flex.baseSize)) + flex.mainMargin;
|
|
672
|
+
const gapIfNotFirst = lineChildCount > 0 ? mainGap : 0;
|
|
673
|
+
if (lineChildCount > 0 && lineMainSize + gapIfNotFirst + childMainSize > mainAxisSize) {
|
|
674
|
+
_lineChildren[lineIndex].length = lineChildIdx;
|
|
675
|
+
_lineLengths[lineIndex] = lineChildCount;
|
|
676
|
+
lineIndex++;
|
|
677
|
+
if (lineIndex >= MAX_FLEX_LINES) growLineArrays(lineIndex + 16);
|
|
678
|
+
lineChildIdx = 0;
|
|
679
|
+
lineMainSize = childMainSize;
|
|
680
|
+
lineChildCount = 1;
|
|
681
|
+
} else {
|
|
682
|
+
lineMainSize += gapIfNotFirst + childMainSize;
|
|
683
|
+
lineChildCount++;
|
|
684
|
+
}
|
|
685
|
+
flex.lineIndex = lineIndex;
|
|
686
|
+
_lineChildren[lineIndex][lineChildIdx++] = child;
|
|
687
|
+
}
|
|
688
|
+
if (lineChildCount > 0) {
|
|
689
|
+
_lineChildren[lineIndex].length = lineChildIdx;
|
|
690
|
+
_lineLengths[lineIndex] = lineChildCount;
|
|
691
|
+
lineIndex++;
|
|
692
|
+
}
|
|
693
|
+
const numLines = lineIndex;
|
|
694
|
+
for (let i = 0; i < numLines; i++) {
|
|
695
|
+
_lineCrossSizes[i] = 0;
|
|
696
|
+
_lineCrossOffsets[i] = 0;
|
|
697
|
+
}
|
|
698
|
+
if (wrap === 2 && numLines > 1) {
|
|
699
|
+
for (let i = 0; i < Math.floor(numLines / 2); i++) {
|
|
700
|
+
const j = numLines - 1 - i;
|
|
701
|
+
const lineI = _lineChildren[i];
|
|
702
|
+
const lineJ = _lineChildren[j];
|
|
703
|
+
const lenI = lineI.length;
|
|
704
|
+
const lenJ = lineJ.length;
|
|
705
|
+
const maxLen = Math.max(lenI, lenJ);
|
|
706
|
+
for (let k = 0; k < maxLen; k++) {
|
|
707
|
+
const hasI = k < lenI;
|
|
708
|
+
const hasJ = k < lenJ;
|
|
709
|
+
const tmpI = hasI ? lineI[k] : null;
|
|
710
|
+
const tmpJ = hasJ ? lineJ[k] : null;
|
|
711
|
+
if (hasJ) lineI[k] = tmpJ;
|
|
712
|
+
if (hasI) lineJ[k] = tmpI;
|
|
713
|
+
}
|
|
714
|
+
lineI.length = lenJ;
|
|
715
|
+
lineJ.length = lenI;
|
|
716
|
+
}
|
|
717
|
+
for (let i = 0; i < numLines; i++) {
|
|
718
|
+
const lc = _lineChildren[i];
|
|
719
|
+
for (let c = 0; c < lc.length; c++) lc[c].flex.lineIndex = i;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
return numLines;
|
|
723
|
+
}
|
|
724
|
+
/**
|
|
725
|
+
* Distribute flex space for a single line of children.
|
|
726
|
+
* Implements CSS Flexbox section 9.7: Resolving Flexible Lengths.
|
|
727
|
+
*
|
|
728
|
+
* Takes pre-collected children array to avoid O(n*m) iteration pattern.
|
|
729
|
+
* Previously iterated through ALL parent.children 8 times per line.
|
|
730
|
+
*
|
|
731
|
+
* @param lineChildren - Pre-collected children for this line (from _lineChildren)
|
|
732
|
+
* @param initialFreeSpace - Free space to distribute (positive=grow, negative=shrink)
|
|
733
|
+
*/
|
|
734
|
+
function distributeFlexSpaceForLine(lineChildren, initialFreeSpace) {
|
|
735
|
+
const isGrowing = initialFreeSpace > 0;
|
|
736
|
+
if (initialFreeSpace === 0) return;
|
|
737
|
+
const childCount = lineChildren.length;
|
|
738
|
+
if (childCount === 0) return;
|
|
739
|
+
if (childCount === 1) {
|
|
740
|
+
const flex = lineChildren[0].flex;
|
|
741
|
+
if (isGrowing ? flex.flexGrow > 0 : flex.flexShrink > 0) {
|
|
742
|
+
const target = flex.baseSize + initialFreeSpace;
|
|
743
|
+
flex.mainSize = Math.max(flex.minMain, Math.min(flex.maxMain, target));
|
|
744
|
+
}
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
let totalBase = 0;
|
|
748
|
+
for (let i = 0; i < childCount; i++) totalBase += lineChildren[i].flex.baseSize;
|
|
749
|
+
const containerInner = initialFreeSpace + totalBase;
|
|
750
|
+
for (let i = 0; i < childCount; i++) lineChildren[i].flex.frozen = false;
|
|
751
|
+
let freeSpace = initialFreeSpace;
|
|
752
|
+
let iterations = 0;
|
|
753
|
+
const maxIterations = childCount + 1;
|
|
754
|
+
while (iterations++ < maxIterations) {
|
|
755
|
+
let totalFlex = 0;
|
|
756
|
+
for (let i = 0; i < childCount; i++) {
|
|
757
|
+
const flex = lineChildren[i].flex;
|
|
758
|
+
if (flex.frozen) continue;
|
|
759
|
+
if (isGrowing) totalFlex += flex.flexGrow;
|
|
760
|
+
else totalFlex += flex.flexShrink * flex.baseSize;
|
|
761
|
+
}
|
|
762
|
+
if (totalFlex === 0) break;
|
|
763
|
+
let effectiveFreeSpace = freeSpace;
|
|
764
|
+
if (isGrowing && totalFlex < 1) effectiveFreeSpace = freeSpace * totalFlex;
|
|
765
|
+
let totalViolation = 0;
|
|
766
|
+
for (let i = 0; i < childCount; i++) {
|
|
767
|
+
const flex = lineChildren[i].flex;
|
|
768
|
+
if (flex.frozen) continue;
|
|
769
|
+
const flexFactor = isGrowing ? flex.flexGrow : flex.flexShrink * flex.baseSize;
|
|
770
|
+
const ratio = totalFlex > 0 ? flexFactor / totalFlex : 0;
|
|
771
|
+
const target = flex.baseSize + effectiveFreeSpace * ratio;
|
|
772
|
+
const clamped = Math.max(flex.minMain, Math.min(flex.maxMain, target));
|
|
773
|
+
totalViolation += clamped - target;
|
|
774
|
+
flex.mainSize = clamped;
|
|
775
|
+
}
|
|
776
|
+
let anyFrozen = false;
|
|
777
|
+
if (Math.abs(totalViolation) < EPSILON_FLOAT) {
|
|
778
|
+
for (let i = 0; i < childCount; i++) lineChildren[i].flex.frozen = true;
|
|
779
|
+
break;
|
|
780
|
+
} else if (totalViolation > 0) for (let i = 0; i < childCount; i++) {
|
|
781
|
+
const flex = lineChildren[i].flex;
|
|
782
|
+
if (flex.frozen) continue;
|
|
783
|
+
const target = flex.baseSize + (isGrowing ? flex.flexGrow : flex.flexShrink * flex.baseSize) / totalFlex * effectiveFreeSpace;
|
|
784
|
+
if (flex.mainSize > target + EPSILON_FLOAT) {
|
|
785
|
+
flex.frozen = true;
|
|
786
|
+
anyFrozen = true;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
else for (let i = 0; i < childCount; i++) {
|
|
790
|
+
const flex = lineChildren[i].flex;
|
|
791
|
+
if (flex.frozen) continue;
|
|
792
|
+
const flexFactor = isGrowing ? flex.flexGrow : flex.flexShrink * flex.baseSize;
|
|
793
|
+
const target = flex.baseSize + flexFactor / totalFlex * effectiveFreeSpace;
|
|
794
|
+
if (flex.mainSize < target - EPSILON_FLOAT) {
|
|
795
|
+
flex.frozen = true;
|
|
796
|
+
anyFrozen = true;
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
if (!anyFrozen) break;
|
|
800
|
+
let frozenSpace = 0;
|
|
801
|
+
let unfrozenBase = 0;
|
|
802
|
+
for (let i = 0; i < childCount; i++) {
|
|
803
|
+
const flex = lineChildren[i].flex;
|
|
804
|
+
if (flex.frozen) frozenSpace += flex.mainSize;
|
|
805
|
+
else unfrozenBase += flex.baseSize;
|
|
806
|
+
}
|
|
807
|
+
freeSpace = containerInner - frozenSpace - unfrozenBase;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
//#endregion
|
|
811
|
+
//#region ../../flexily/src/layout-zero.ts
|
|
812
|
+
/**
|
|
813
|
+
* Flexily Layout Algorithm — Main Entry Point
|
|
814
|
+
*
|
|
815
|
+
* Core flexbox layout computation. This file contains:
|
|
816
|
+
* - computeLayout(): top-level entry point
|
|
817
|
+
* - layoutNode(): recursive layout algorithm (11 phases)
|
|
818
|
+
*
|
|
819
|
+
* Helper modules (split for maintainability, zero-allocation preserved):
|
|
820
|
+
* - layout-helpers.ts: Edge resolution (margins, padding, borders)
|
|
821
|
+
* - layout-traversal.ts: Tree traversal (markSubtreeLayoutSeen, countNodes)
|
|
822
|
+
* - layout-flex-lines.ts: Pre-allocated arrays, line breaking, flex distribution
|
|
823
|
+
* - layout-measure.ts: Intrinsic sizing (measureNode)
|
|
824
|
+
* - layout-stats.ts: Debug/benchmark counters
|
|
825
|
+
*
|
|
826
|
+
* Based on Planning-nl/flexbox.js reference implementation.
|
|
827
|
+
*/
|
|
828
|
+
/**
|
|
829
|
+
* Compute layout for a node tree.
|
|
830
|
+
*/
|
|
831
|
+
function computeLayout(root, availableWidth, availableHeight, direction = 1) {
|
|
832
|
+
const saved = enterLayout();
|
|
833
|
+
try {
|
|
834
|
+
resetLayoutStats();
|
|
835
|
+
getTrace()?.resetCounter();
|
|
836
|
+
root.resetLayoutCache();
|
|
837
|
+
layoutNode(root, availableWidth, availableHeight, 0, 0, 0, 0, direction);
|
|
838
|
+
} finally {
|
|
839
|
+
exitLayout(saved);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
/**
|
|
843
|
+
* Layout a node and its children.
|
|
844
|
+
*
|
|
845
|
+
* @param absX - Absolute X position from document root (for Yoga-compatible edge rounding)
|
|
846
|
+
* @param absY - Absolute Y position from document root (for Yoga-compatible edge rounding)
|
|
847
|
+
*/
|
|
848
|
+
function layoutNode(node, availableWidth, availableHeight, offsetX, offsetY, absX, absY, direction = 1) {
|
|
849
|
+
incLayoutNodeCalls();
|
|
850
|
+
if (offsetX === 0 && offsetY === 0 && absX === 0 && absY === 0 && node.children.length > 0) incLayoutSizingCalls();
|
|
851
|
+
else incLayoutPositioningCalls();
|
|
852
|
+
log.debug?.("layoutNode called: availW=%d, availH=%d, offsetX=%d, offsetY=%d, absX=%d, absY=%d, children=%d", availableWidth, availableHeight, offsetX, offsetY, absX, absY, node.children.length);
|
|
853
|
+
const _t = getTrace();
|
|
854
|
+
const _tn = _t?.nextNode() ?? 0;
|
|
855
|
+
_t?.layoutEnter(_tn, availableWidth, availableHeight, node.isDirty(), node.children.length);
|
|
856
|
+
const style = node.style;
|
|
857
|
+
const layout = node.layout;
|
|
858
|
+
if (style.display === 1) {
|
|
859
|
+
layout.left = 0;
|
|
860
|
+
layout.top = 0;
|
|
861
|
+
layout.width = 0;
|
|
862
|
+
layout.height = 0;
|
|
863
|
+
return;
|
|
864
|
+
}
|
|
865
|
+
const flex = node.flex;
|
|
866
|
+
if (flex.layoutValid && !node.isDirty() && Object.is(flex.lastAvailW, availableWidth) && Object.is(flex.lastAvailH, availableHeight) && flex.lastDir === direction && flex.lastAbsX === absX && flex.lastAbsY === absY) {
|
|
867
|
+
_t?.fingerprintHit(_tn, availableWidth, availableHeight);
|
|
868
|
+
const deltaX = offsetX - flex.lastOffsetX;
|
|
869
|
+
const deltaY = offsetY - flex.lastOffsetY;
|
|
870
|
+
if (deltaX !== 0 || deltaY !== 0) {
|
|
871
|
+
layout.left += deltaX;
|
|
872
|
+
layout.top += deltaY;
|
|
873
|
+
flex.lastOffsetX = offsetX;
|
|
874
|
+
flex.lastOffsetY = offsetY;
|
|
875
|
+
propagatePositionDelta(node, deltaX, deltaY);
|
|
876
|
+
}
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
_t?.fingerprintMiss(_tn, availableWidth, availableHeight, {
|
|
880
|
+
layoutValid: flex.layoutValid,
|
|
881
|
+
isDirty: node.isDirty(),
|
|
882
|
+
sameW: Object.is(flex.lastAvailW, availableWidth),
|
|
883
|
+
sameH: Object.is(flex.lastAvailH, availableHeight),
|
|
884
|
+
sameDir: flex.lastDir === direction,
|
|
885
|
+
sameAbsX: flex.lastAbsX === absX,
|
|
886
|
+
sameAbsY: flex.lastAbsY === absY
|
|
887
|
+
});
|
|
888
|
+
const marginLeft = resolveEdgeValue(style.margin, 0, style.flexDirection, availableWidth, direction);
|
|
889
|
+
const marginTop = resolveEdgeValue(style.margin, 1, style.flexDirection, availableWidth, direction);
|
|
890
|
+
const marginRight = resolveEdgeValue(style.margin, 2, style.flexDirection, availableWidth, direction);
|
|
891
|
+
const marginBottom = resolveEdgeValue(style.margin, 3, style.flexDirection, availableWidth, direction);
|
|
892
|
+
const paddingLeft = resolveEdgeValue(style.padding, 0, style.flexDirection, availableWidth, direction);
|
|
893
|
+
const paddingTop = resolveEdgeValue(style.padding, 1, style.flexDirection, availableWidth, direction);
|
|
894
|
+
const paddingRight = resolveEdgeValue(style.padding, 2, style.flexDirection, availableWidth, direction);
|
|
895
|
+
const paddingBottom = resolveEdgeValue(style.padding, 3, style.flexDirection, availableWidth, direction);
|
|
896
|
+
const borderLeft = resolveEdgeBorderValue(style.border, 0, style.flexDirection, direction);
|
|
897
|
+
const borderTop = resolveEdgeBorderValue(style.border, 1, style.flexDirection, direction);
|
|
898
|
+
const borderRight = resolveEdgeBorderValue(style.border, 2, style.flexDirection, direction);
|
|
899
|
+
const borderBottom = resolveEdgeBorderValue(style.border, 3, style.flexDirection, direction);
|
|
900
|
+
let nodeWidth;
|
|
901
|
+
if (style.width.unit === 1) nodeWidth = style.width.value;
|
|
902
|
+
else if (style.width.unit === 2) nodeWidth = resolveValue(style.width, availableWidth);
|
|
903
|
+
else if (Number.isNaN(availableWidth)) nodeWidth = NaN;
|
|
904
|
+
else nodeWidth = availableWidth - marginLeft - marginRight;
|
|
905
|
+
nodeWidth = applyMinMax(nodeWidth, style.minWidth, style.maxWidth, availableWidth);
|
|
906
|
+
let nodeHeight;
|
|
907
|
+
if (style.height.unit === 1) nodeHeight = style.height.value;
|
|
908
|
+
else if (style.height.unit === 2) nodeHeight = resolveValue(style.height, availableHeight);
|
|
909
|
+
else if (Number.isNaN(availableHeight)) nodeHeight = NaN;
|
|
910
|
+
else nodeHeight = availableHeight - marginTop - marginBottom;
|
|
911
|
+
const aspectRatio = style.aspectRatio;
|
|
912
|
+
if (!Number.isNaN(aspectRatio) && aspectRatio > 0) {
|
|
913
|
+
const widthIsAuto = Number.isNaN(nodeWidth) || style.width.unit === 3;
|
|
914
|
+
const heightIsAuto = Number.isNaN(nodeHeight) || style.height.unit === 3;
|
|
915
|
+
if (widthIsAuto && !heightIsAuto && !Number.isNaN(nodeHeight)) {
|
|
916
|
+
nodeWidth = nodeHeight * aspectRatio;
|
|
917
|
+
nodeWidth = applyMinMax(nodeWidth, style.minWidth, style.maxWidth, availableWidth);
|
|
918
|
+
} else if (heightIsAuto && !widthIsAuto && !Number.isNaN(nodeWidth)) nodeHeight = nodeWidth / aspectRatio;
|
|
919
|
+
}
|
|
920
|
+
nodeHeight = applyMinMax(nodeHeight, style.minHeight, style.maxHeight, availableHeight);
|
|
921
|
+
const innerLeft = borderLeft + paddingLeft;
|
|
922
|
+
const innerTop = borderTop + paddingTop;
|
|
923
|
+
const innerRight = borderRight + paddingRight;
|
|
924
|
+
const innerBottom = borderBottom + paddingBottom;
|
|
925
|
+
const minInnerWidth = innerLeft + innerRight;
|
|
926
|
+
const minInnerHeight = innerTop + innerBottom;
|
|
927
|
+
if (!Number.isNaN(nodeWidth) && nodeWidth < minInnerWidth) nodeWidth = minInnerWidth;
|
|
928
|
+
if (!Number.isNaN(nodeHeight) && nodeHeight < minInnerHeight) nodeHeight = minInnerHeight;
|
|
929
|
+
const contentWidth = Number.isNaN(nodeWidth) ? NaN : Math.max(0, nodeWidth - innerLeft - innerRight);
|
|
930
|
+
const contentHeight = Number.isNaN(nodeHeight) ? NaN : Math.max(0, nodeHeight - innerTop - innerBottom);
|
|
931
|
+
let parentPosOffsetX = 0;
|
|
932
|
+
let parentPosOffsetY = 0;
|
|
933
|
+
if (style.positionType === 1) {
|
|
934
|
+
const leftPos = resolvePositionEdge(style.position, 0, direction);
|
|
935
|
+
const topPos = style.position[1];
|
|
936
|
+
const rightPos = resolvePositionEdge(style.position, 2, direction);
|
|
937
|
+
const bottomPos = style.position[3];
|
|
938
|
+
if (leftPos.unit !== 0) parentPosOffsetX = resolveValue(leftPos, availableWidth);
|
|
939
|
+
else if (rightPos.unit !== 0) parentPosOffsetX = -resolveValue(rightPos, availableWidth);
|
|
940
|
+
if (topPos.unit !== 0) parentPosOffsetY = resolveValue(topPos, availableHeight);
|
|
941
|
+
else if (bottomPos.unit !== 0) parentPosOffsetY = -resolveValue(bottomPos, availableHeight);
|
|
942
|
+
}
|
|
943
|
+
if (node.hasMeasureFunc() && node.children.length === 0) {
|
|
944
|
+
const widthIsAuto = style.width.unit === 3 || style.width.unit === 0 || Number.isNaN(nodeWidth);
|
|
945
|
+
const heightIsAuto = style.height.unit === 3 || style.height.unit === 0 || Number.isNaN(nodeHeight);
|
|
946
|
+
const widthMode = widthIsAuto ? 2 : 1;
|
|
947
|
+
const heightMode = heightIsAuto ? 0 : 1;
|
|
948
|
+
const measureWidth = Number.isNaN(contentWidth) ? Infinity : contentWidth;
|
|
949
|
+
const measureHeight = Number.isNaN(contentHeight) ? Infinity : contentHeight;
|
|
950
|
+
const measured = node.cachedMeasure(measureWidth, widthMode, measureHeight, heightMode);
|
|
951
|
+
if (widthIsAuto) nodeWidth = measured.width + innerLeft + innerRight;
|
|
952
|
+
if (heightIsAuto) nodeHeight = measured.height + innerTop + innerBottom;
|
|
953
|
+
layout.width = Math.round(nodeWidth);
|
|
954
|
+
layout.height = Math.round(nodeHeight);
|
|
955
|
+
layout.left = Math.round(offsetX + marginLeft);
|
|
956
|
+
layout.top = Math.round(offsetY + marginTop);
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
if (node.children.length === 0) {
|
|
960
|
+
if (Number.isNaN(nodeWidth)) nodeWidth = innerLeft + innerRight;
|
|
961
|
+
if (Number.isNaN(nodeHeight)) nodeHeight = innerTop + innerBottom;
|
|
962
|
+
layout.width = Math.round(nodeWidth);
|
|
963
|
+
layout.height = Math.round(nodeHeight);
|
|
964
|
+
layout.left = Math.round(offsetX + marginLeft);
|
|
965
|
+
layout.top = Math.round(offsetY + marginTop);
|
|
966
|
+
return;
|
|
967
|
+
}
|
|
968
|
+
const isRow = isRowDirection(style.flexDirection);
|
|
969
|
+
const isReverse = isReverseDirection(style.flexDirection);
|
|
970
|
+
const effectiveReverse = isRow ? direction === 2 !== isReverse : isReverse;
|
|
971
|
+
const mainAxisSize = isRow ? contentWidth : contentHeight;
|
|
972
|
+
const crossAxisSize = isRow ? contentHeight : contentWidth;
|
|
973
|
+
const mainGap = isRow ? style.gap[0] : style.gap[1];
|
|
974
|
+
let totalBaseMain = 0;
|
|
975
|
+
let relativeCount = 0;
|
|
976
|
+
let totalAutoMargins = 0;
|
|
977
|
+
let hasBaselineAlignment = style.alignItems === 5;
|
|
978
|
+
for (const child of node.children) {
|
|
979
|
+
if (child.style.display === 1 || child.style.positionType === 2) {
|
|
980
|
+
child.flex.relativeIndex = -1;
|
|
981
|
+
continue;
|
|
982
|
+
}
|
|
983
|
+
child.flex.relativeIndex = relativeCount++;
|
|
984
|
+
const childStyle = child.style;
|
|
985
|
+
const cflex = child.flex;
|
|
986
|
+
const mainStartIndex = isRow ? effectiveReverse ? 2 : 0 : isReverse ? 3 : 1;
|
|
987
|
+
const mainEndIndex = isRow ? effectiveReverse ? 0 : 2 : isReverse ? 1 : 3;
|
|
988
|
+
cflex.mainStartMarginAuto = isEdgeAuto(childStyle.margin, mainStartIndex, style.flexDirection, direction);
|
|
989
|
+
cflex.mainEndMarginAuto = isEdgeAuto(childStyle.margin, mainEndIndex, style.flexDirection, direction);
|
|
990
|
+
cflex.marginL = resolveEdgeValue(childStyle.margin, 0, style.flexDirection, contentWidth, direction);
|
|
991
|
+
cflex.marginT = resolveEdgeValue(childStyle.margin, 1, style.flexDirection, contentWidth, direction);
|
|
992
|
+
cflex.marginR = resolveEdgeValue(childStyle.margin, 2, style.flexDirection, contentWidth, direction);
|
|
993
|
+
cflex.marginB = resolveEdgeValue(childStyle.margin, 3, style.flexDirection, contentWidth, direction);
|
|
994
|
+
cflex.mainStartMarginValue = cflex.mainStartMarginAuto ? 0 : isRow ? effectiveReverse ? cflex.marginR : cflex.marginL : isReverse ? cflex.marginB : cflex.marginT;
|
|
995
|
+
cflex.mainEndMarginValue = cflex.mainEndMarginAuto ? 0 : isRow ? effectiveReverse ? cflex.marginL : cflex.marginR : isReverse ? cflex.marginT : cflex.marginB;
|
|
996
|
+
cflex.mainMargin = cflex.mainStartMarginValue + cflex.mainEndMarginValue;
|
|
997
|
+
let baseSize = 0;
|
|
998
|
+
if (childStyle.flexBasis.unit === 1) baseSize = childStyle.flexBasis.value;
|
|
999
|
+
else if (childStyle.flexBasis.unit === 2) baseSize = Number.isNaN(mainAxisSize) ? 0 : mainAxisSize * (childStyle.flexBasis.value / 100);
|
|
1000
|
+
else {
|
|
1001
|
+
const sizeVal = isRow ? childStyle.width : childStyle.height;
|
|
1002
|
+
if (sizeVal.unit === 1) baseSize = sizeVal.value;
|
|
1003
|
+
else if (sizeVal.unit === 2) baseSize = Number.isNaN(mainAxisSize) ? 0 : mainAxisSize * (sizeVal.value / 100);
|
|
1004
|
+
else if (child.hasMeasureFunc()) {
|
|
1005
|
+
const availCross = crossAxisSize - (isRow ? cflex.marginT + cflex.marginB : cflex.marginL + cflex.marginR);
|
|
1006
|
+
const wantMaxContent = childStyle.flexGrow > 0;
|
|
1007
|
+
const mW = isRow ? wantMaxContent ? Infinity : Number.isNaN(mainAxisSize) ? Infinity : mainAxisSize : Number.isNaN(availCross) ? Infinity : availCross;
|
|
1008
|
+
const mH = isRow ? Number.isNaN(availCross) ? Infinity : availCross : wantMaxContent ? Infinity : Number.isNaN(mainAxisSize) ? Infinity : mainAxisSize;
|
|
1009
|
+
const mWMode = isRow ? wantMaxContent ? 0 : 2 : Number.isNaN(availCross) ? 0 : 2;
|
|
1010
|
+
const mHMode = isRow ? 0 : wantMaxContent ? 0 : 2;
|
|
1011
|
+
const measured = child.cachedMeasure(mW, mWMode, mH, mHMode);
|
|
1012
|
+
baseSize = isRow ? measured.width : measured.height;
|
|
1013
|
+
} else if (child.children.length > 0) {
|
|
1014
|
+
const sizingW = isRow ? NaN : crossAxisSize;
|
|
1015
|
+
const sizingH = isRow ? crossAxisSize : NaN;
|
|
1016
|
+
const cached = child.getCachedLayout(sizingW, sizingH);
|
|
1017
|
+
if (cached) {
|
|
1018
|
+
incLayoutCacheHits();
|
|
1019
|
+
_t?.cacheHit(_tn, sizingW, sizingH, cached.width, cached.height);
|
|
1020
|
+
baseSize = isRow ? cached.width : cached.height;
|
|
1021
|
+
} else {
|
|
1022
|
+
_t?.cacheMiss(_tn, sizingW, sizingH);
|
|
1023
|
+
const savedW = child.layout.width;
|
|
1024
|
+
const savedH = child.layout.height;
|
|
1025
|
+
measureNode(child, sizingW, sizingH, direction);
|
|
1026
|
+
const measuredW = child.layout.width;
|
|
1027
|
+
const measuredH = child.layout.height;
|
|
1028
|
+
child.layout.width = savedW;
|
|
1029
|
+
child.layout.height = savedH;
|
|
1030
|
+
_t?.measureSaveRestore(_tn, savedW, savedH, measuredW, measuredH);
|
|
1031
|
+
baseSize = isRow ? measuredW : measuredH;
|
|
1032
|
+
child.setCachedLayout(sizingW, sizingH, measuredW, measuredH);
|
|
1033
|
+
}
|
|
1034
|
+
} else {
|
|
1035
|
+
const parentWidth = isRow ? mainAxisSize : crossAxisSize;
|
|
1036
|
+
baseSize = (isRow ? resolveEdgeValue(childStyle.padding, 0, childStyle.flexDirection, parentWidth, direction) + resolveEdgeValue(childStyle.padding, 2, childStyle.flexDirection, parentWidth, direction) : resolveEdgeValue(childStyle.padding, 1, childStyle.flexDirection, parentWidth, direction) + resolveEdgeValue(childStyle.padding, 3, childStyle.flexDirection, parentWidth, direction)) + (isRow ? resolveEdgeBorderValue(childStyle.border, 0, childStyle.flexDirection, direction) + resolveEdgeBorderValue(childStyle.border, 2, childStyle.flexDirection, direction) : resolveEdgeBorderValue(childStyle.border, 1, childStyle.flexDirection, direction) + resolveEdgeBorderValue(childStyle.border, 3, childStyle.flexDirection, direction));
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
const minVal = isRow ? childStyle.minWidth : childStyle.minHeight;
|
|
1040
|
+
const maxVal = isRow ? childStyle.maxWidth : childStyle.maxHeight;
|
|
1041
|
+
cflex.minMain = minVal.unit !== 0 ? resolveValue(minVal, mainAxisSize) : 0;
|
|
1042
|
+
cflex.maxMain = maxVal.unit !== 0 ? resolveValue(maxVal, mainAxisSize) : Infinity;
|
|
1043
|
+
cflex.flexGrow = childStyle.flexGrow;
|
|
1044
|
+
let shrink = childStyle.flexShrink;
|
|
1045
|
+
if (childStyle.overflow !== 0) shrink = Math.max(shrink, 1);
|
|
1046
|
+
if (child.hasMeasureFunc() && childStyle.flexGrow > 0) shrink = Math.max(shrink, 1);
|
|
1047
|
+
cflex.flexShrink = shrink;
|
|
1048
|
+
cflex.baseSize = baseSize;
|
|
1049
|
+
cflex.mainSize = baseSize;
|
|
1050
|
+
cflex.frozen = false;
|
|
1051
|
+
totalBaseMain += baseSize + cflex.mainMargin;
|
|
1052
|
+
if (cflex.mainStartMarginAuto) totalAutoMargins++;
|
|
1053
|
+
if (cflex.mainEndMarginAuto) totalAutoMargins++;
|
|
1054
|
+
if (!hasBaselineAlignment && childStyle.alignSelf === 5) hasBaselineAlignment = true;
|
|
1055
|
+
}
|
|
1056
|
+
log.debug?.("layoutNode: node.children=%d, relativeCount=%d", node.children.length, relativeCount);
|
|
1057
|
+
if (relativeCount > 0) {
|
|
1058
|
+
const numLines = breakIntoLines(node, relativeCount, mainAxisSize, mainGap, style.flexWrap);
|
|
1059
|
+
const crossGap = isRow ? style.gap[1] : style.gap[0];
|
|
1060
|
+
for (let lineIdx = 0; lineIdx < numLines; lineIdx++) {
|
|
1061
|
+
const lineChildren = _lineChildren[lineIdx];
|
|
1062
|
+
const lineLength = lineChildren.length;
|
|
1063
|
+
if (lineLength === 0) continue;
|
|
1064
|
+
let lineTotalBaseMain = 0;
|
|
1065
|
+
for (let i = 0; i < lineLength; i++) {
|
|
1066
|
+
const c = lineChildren[i];
|
|
1067
|
+
lineTotalBaseMain += c.flex.baseSize + c.flex.mainMargin;
|
|
1068
|
+
}
|
|
1069
|
+
const lineTotalGaps = lineLength > 1 ? mainGap * (lineLength - 1) : 0;
|
|
1070
|
+
let effectiveMainSize = mainAxisSize;
|
|
1071
|
+
if (Number.isNaN(mainAxisSize)) {
|
|
1072
|
+
const maxMainVal = isRow ? style.maxWidth : style.maxHeight;
|
|
1073
|
+
if (maxMainVal.unit !== 0) {
|
|
1074
|
+
const maxMain = resolveValue(maxMainVal, isRow ? availableWidth : availableHeight);
|
|
1075
|
+
if (!Number.isNaN(maxMain) && lineTotalBaseMain + lineTotalGaps > maxMain) effectiveMainSize = maxMain - (isRow ? innerLeft + innerRight : innerTop + innerBottom);
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
if (!Number.isNaN(effectiveMainSize)) distributeFlexSpaceForLine(lineChildren, effectiveMainSize - lineTotalBaseMain - lineTotalGaps);
|
|
1079
|
+
for (let i = 0; i < lineLength; i++) {
|
|
1080
|
+
const f = lineChildren[i].flex;
|
|
1081
|
+
f.mainSize = Math.max(f.minMain, Math.min(f.maxMain, f.mainSize));
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
for (let lineIdx = 0; lineIdx < numLines; lineIdx++) {
|
|
1085
|
+
const lineChildren = _lineChildren[lineIdx];
|
|
1086
|
+
const lineLength = lineChildren.length;
|
|
1087
|
+
if (lineLength === 0) {
|
|
1088
|
+
_lineJustifyStarts[lineIdx] = 0;
|
|
1089
|
+
_lineItemSpacings[lineIdx] = mainGap;
|
|
1090
|
+
continue;
|
|
1091
|
+
}
|
|
1092
|
+
let lineUsedMain = 0;
|
|
1093
|
+
let lineAutoMargins = 0;
|
|
1094
|
+
for (let i = 0; i < lineLength; i++) {
|
|
1095
|
+
const c = lineChildren[i];
|
|
1096
|
+
lineUsedMain += c.flex.mainSize + c.flex.mainMargin;
|
|
1097
|
+
if (c.flex.mainStartMarginAuto) lineAutoMargins++;
|
|
1098
|
+
if (c.flex.mainEndMarginAuto) lineAutoMargins++;
|
|
1099
|
+
}
|
|
1100
|
+
const lineGaps = lineLength > 1 ? mainGap * (lineLength - 1) : 0;
|
|
1101
|
+
lineUsedMain += lineGaps;
|
|
1102
|
+
const lineRemainingSpace = Number.isNaN(mainAxisSize) ? 0 : mainAxisSize - lineUsedMain;
|
|
1103
|
+
const lineHasAutoMargins = lineAutoMargins > 0;
|
|
1104
|
+
if (lineHasAutoMargins) {
|
|
1105
|
+
const autoMarginValue = Math.max(0, lineRemainingSpace) / lineAutoMargins;
|
|
1106
|
+
for (let i = 0; i < lineLength; i++) {
|
|
1107
|
+
const child = lineChildren[i];
|
|
1108
|
+
if (child.flex.mainStartMarginAuto) child.flex.mainStartMarginValue = autoMarginValue;
|
|
1109
|
+
if (child.flex.mainEndMarginAuto) child.flex.mainEndMarginValue = autoMarginValue;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
let lineStartOffset = 0;
|
|
1113
|
+
let lineItemSpacing = mainGap;
|
|
1114
|
+
if (!lineHasAutoMargins) switch (style.justifyContent) {
|
|
1115
|
+
case 2:
|
|
1116
|
+
lineStartOffset = lineRemainingSpace;
|
|
1117
|
+
break;
|
|
1118
|
+
case 1:
|
|
1119
|
+
lineStartOffset = lineRemainingSpace / 2;
|
|
1120
|
+
break;
|
|
1121
|
+
case 3:
|
|
1122
|
+
if (lineLength > 1 && lineRemainingSpace > 0) lineItemSpacing = mainGap + lineRemainingSpace / (lineLength - 1);
|
|
1123
|
+
break;
|
|
1124
|
+
case 4:
|
|
1125
|
+
if (lineLength > 0 && lineRemainingSpace > 0) {
|
|
1126
|
+
const extraSpace = lineRemainingSpace / lineLength;
|
|
1127
|
+
lineStartOffset = extraSpace / 2;
|
|
1128
|
+
lineItemSpacing = mainGap + extraSpace;
|
|
1129
|
+
}
|
|
1130
|
+
break;
|
|
1131
|
+
case 5:
|
|
1132
|
+
if (lineLength > 0 && lineRemainingSpace > 0) {
|
|
1133
|
+
const extraSpace = lineRemainingSpace / (lineLength + 1);
|
|
1134
|
+
lineStartOffset = extraSpace;
|
|
1135
|
+
lineItemSpacing = mainGap + extraSpace;
|
|
1136
|
+
}
|
|
1137
|
+
break;
|
|
1138
|
+
}
|
|
1139
|
+
_lineJustifyStarts[lineIdx] = lineStartOffset;
|
|
1140
|
+
_lineItemSpacings[lineIdx] = lineItemSpacing;
|
|
1141
|
+
}
|
|
1142
|
+
const startOffset = _lineJustifyStarts[0];
|
|
1143
|
+
const itemSpacing = _lineItemSpacings[0];
|
|
1144
|
+
let maxBaseline = 0;
|
|
1145
|
+
let baselineZoneHeight = 0;
|
|
1146
|
+
const alignItemsIsBaseline = style.alignItems === 5;
|
|
1147
|
+
if (hasBaselineAlignment && isRow) {
|
|
1148
|
+
let maxChildHeight = 0;
|
|
1149
|
+
for (const child of node.children) {
|
|
1150
|
+
if (child.flex.relativeIndex < 0) continue;
|
|
1151
|
+
const childStyle = child.style;
|
|
1152
|
+
const topMargin = child.flex.marginT;
|
|
1153
|
+
let childWidth;
|
|
1154
|
+
let childHeight;
|
|
1155
|
+
const widthDim = childStyle.width;
|
|
1156
|
+
const heightDim = childStyle.height;
|
|
1157
|
+
if (widthDim.unit === 1) childWidth = widthDim.value;
|
|
1158
|
+
else if (widthDim.unit === 2 && !Number.isNaN(mainAxisSize)) childWidth = mainAxisSize * (widthDim.value / 100);
|
|
1159
|
+
else childWidth = child.flex.mainSize;
|
|
1160
|
+
if (heightDim.unit === 1) childHeight = heightDim.value;
|
|
1161
|
+
else if (heightDim.unit === 2 && !Number.isNaN(crossAxisSize)) childHeight = crossAxisSize * (heightDim.value / 100);
|
|
1162
|
+
else {
|
|
1163
|
+
const cached = child.getCachedLayout(child.flex.mainSize, NaN);
|
|
1164
|
+
if (cached) {
|
|
1165
|
+
incLayoutCacheHits();
|
|
1166
|
+
_t?.cacheHit(_tn, child.flex.mainSize, NaN, cached.width, cached.height);
|
|
1167
|
+
childWidth = cached.width;
|
|
1168
|
+
childHeight = cached.height;
|
|
1169
|
+
} else {
|
|
1170
|
+
_t?.cacheMiss(_tn, child.flex.mainSize, NaN);
|
|
1171
|
+
const savedW = child.layout.width;
|
|
1172
|
+
const savedH = child.layout.height;
|
|
1173
|
+
measureNode(child, child.flex.mainSize, NaN, direction);
|
|
1174
|
+
childWidth = child.layout.width;
|
|
1175
|
+
childHeight = child.layout.height;
|
|
1176
|
+
child.layout.width = savedW;
|
|
1177
|
+
child.layout.height = savedH;
|
|
1178
|
+
_t?.measureSaveRestore(_tn, savedW, savedH, childWidth, childHeight);
|
|
1179
|
+
child.setCachedLayout(child.flex.mainSize, NaN, childWidth, childHeight);
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
if (child.baselineFunc !== null) child.flex.baseline = topMargin + child.baselineFunc(childWidth, childHeight);
|
|
1183
|
+
else child.flex.baseline = topMargin + childHeight;
|
|
1184
|
+
maxChildHeight = Math.max(maxChildHeight, topMargin + childHeight + child.flex.marginB);
|
|
1185
|
+
if (alignItemsIsBaseline || childStyle.alignSelf === 5) maxBaseline = Math.max(maxBaseline, child.flex.baseline);
|
|
1186
|
+
}
|
|
1187
|
+
baselineZoneHeight = Math.max(maxBaseline, maxChildHeight);
|
|
1188
|
+
}
|
|
1189
|
+
let cumulativeCrossOffset = 0;
|
|
1190
|
+
const isWrapReverse = style.flexWrap === 2;
|
|
1191
|
+
for (let lineIdx = 0; lineIdx < numLines; lineIdx++) {
|
|
1192
|
+
_lineCrossOffsets[lineIdx] = cumulativeCrossOffset;
|
|
1193
|
+
const lineChildren = _lineChildren[lineIdx];
|
|
1194
|
+
const lineLength = lineChildren.length;
|
|
1195
|
+
let maxLineCross = 0;
|
|
1196
|
+
for (let i = 0; i < lineLength; i++) {
|
|
1197
|
+
const child = lineChildren[i];
|
|
1198
|
+
const childStyle = child.style;
|
|
1199
|
+
const crossDim = isRow ? childStyle.height : childStyle.width;
|
|
1200
|
+
const crossMarginStart = isRow ? child.flex.marginT : child.flex.marginL;
|
|
1201
|
+
const crossMarginEnd = isRow ? child.flex.marginB : child.flex.marginR;
|
|
1202
|
+
let childCross = 0;
|
|
1203
|
+
if (crossDim.unit === 1) childCross = crossDim.value;
|
|
1204
|
+
else if (crossDim.unit === 2 && !Number.isNaN(crossAxisSize)) childCross = crossAxisSize * (crossDim.value / 100);
|
|
1205
|
+
else if (child.hasMeasureFunc()) {
|
|
1206
|
+
const crossMargin = crossMarginStart + crossMarginEnd;
|
|
1207
|
+
const availCross = Number.isNaN(crossAxisSize) ? Infinity : crossAxisSize - crossMargin;
|
|
1208
|
+
const childMainSize = child.flex.mainSize;
|
|
1209
|
+
const mW = isRow ? childMainSize : availCross;
|
|
1210
|
+
const mH = isRow ? availCross : childMainSize;
|
|
1211
|
+
const mWMode = Number.isNaN(mW) ? 0 : 2;
|
|
1212
|
+
const mHMode = Number.isNaN(mH) ? 0 : 2;
|
|
1213
|
+
const measured = child.cachedMeasure(Number.isNaN(mW) ? Infinity : mW, mWMode, Number.isNaN(mH) ? Infinity : mH, mHMode);
|
|
1214
|
+
if (measured) childCross = isRow ? measured.height : measured.width;
|
|
1215
|
+
} else if (child.children.length > 0) {
|
|
1216
|
+
const savedW = child.layout.width;
|
|
1217
|
+
const savedH = child.layout.height;
|
|
1218
|
+
measureNode(child, NaN, NaN, direction);
|
|
1219
|
+
childCross = isRow ? child.layout.height : child.layout.width;
|
|
1220
|
+
child.layout.width = savedW;
|
|
1221
|
+
child.layout.height = savedH;
|
|
1222
|
+
}
|
|
1223
|
+
maxLineCross = Math.max(maxLineCross, childCross + crossMarginStart + crossMarginEnd);
|
|
1224
|
+
}
|
|
1225
|
+
const lineCrossSize = maxLineCross;
|
|
1226
|
+
_lineCrossSizes[lineIdx] = lineCrossSize;
|
|
1227
|
+
cumulativeCrossOffset += lineCrossSize + crossGap;
|
|
1228
|
+
}
|
|
1229
|
+
if (!Number.isNaN(crossAxisSize) && numLines > 0) {
|
|
1230
|
+
const freeSpace = crossAxisSize - (cumulativeCrossOffset - crossGap);
|
|
1231
|
+
switch (style.alignContent) {
|
|
1232
|
+
case 3:
|
|
1233
|
+
for (let i = 0; i < numLines; i++) _lineCrossOffsets[i] += freeSpace;
|
|
1234
|
+
break;
|
|
1235
|
+
case 2:
|
|
1236
|
+
{
|
|
1237
|
+
const centerOffset = freeSpace / 2;
|
|
1238
|
+
for (let i = 0; i < numLines; i++) _lineCrossOffsets[i] += centerOffset;
|
|
1239
|
+
}
|
|
1240
|
+
break;
|
|
1241
|
+
case 6:
|
|
1242
|
+
if (freeSpace > 0 && numLines > 1) {
|
|
1243
|
+
const gap = freeSpace / (numLines - 1);
|
|
1244
|
+
for (let i = 1; i < numLines; i++) _lineCrossOffsets[i] += gap * i;
|
|
1245
|
+
}
|
|
1246
|
+
break;
|
|
1247
|
+
case 7:
|
|
1248
|
+
if (freeSpace > 0) {
|
|
1249
|
+
const halfGap = freeSpace / (numLines * 2);
|
|
1250
|
+
for (let i = 0; i < numLines; i++) _lineCrossOffsets[i] += halfGap + halfGap * 2 * i;
|
|
1251
|
+
} else {
|
|
1252
|
+
const centerOffset = freeSpace / 2;
|
|
1253
|
+
for (let i = 0; i < numLines; i++) _lineCrossOffsets[i] += centerOffset;
|
|
1254
|
+
}
|
|
1255
|
+
break;
|
|
1256
|
+
case 8:
|
|
1257
|
+
if (freeSpace > 0 && numLines > 0) {
|
|
1258
|
+
const gap = freeSpace / (numLines + 1);
|
|
1259
|
+
for (let i = 0; i < numLines; i++) _lineCrossOffsets[i] += gap * (i + 1);
|
|
1260
|
+
} else if (freeSpace < 0) {
|
|
1261
|
+
const centerOffset = freeSpace / 2;
|
|
1262
|
+
for (let i = 0; i < numLines; i++) _lineCrossOffsets[i] += centerOffset;
|
|
1263
|
+
}
|
|
1264
|
+
break;
|
|
1265
|
+
case 4:
|
|
1266
|
+
if (freeSpace > 0 && numLines > 0) {
|
|
1267
|
+
const extraPerLine = freeSpace / numLines;
|
|
1268
|
+
for (let i = 0; i < numLines; i++) {
|
|
1269
|
+
_lineCrossSizes[i] += extraPerLine;
|
|
1270
|
+
if (i > 0) _lineCrossOffsets[i] = _lineCrossOffsets[i - 1] + _lineCrossSizes[i - 1] + crossGap;
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
break;
|
|
1274
|
+
}
|
|
1275
|
+
if (isWrapReverse) {
|
|
1276
|
+
let totalLineCrossSize = 0;
|
|
1277
|
+
for (let i = 0; i < numLines; i++) totalLineCrossSize += _lineCrossSizes[i];
|
|
1278
|
+
totalLineCrossSize += crossGap * (numLines - 1);
|
|
1279
|
+
const crossStartOffset = crossAxisSize - totalLineCrossSize;
|
|
1280
|
+
for (let i = 0; i < numLines; i++) _lineCrossOffsets[i] += crossStartOffset;
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
let savedLineCrossSizes = null;
|
|
1284
|
+
let savedLineCrossOffsets = null;
|
|
1285
|
+
let savedLineJustifyStarts = null;
|
|
1286
|
+
let savedLineItemSpacings = null;
|
|
1287
|
+
if (numLines > 1) {
|
|
1288
|
+
savedLineCrossSizes = new Float64Array(numLines);
|
|
1289
|
+
savedLineCrossOffsets = new Float64Array(numLines);
|
|
1290
|
+
savedLineJustifyStarts = new Float64Array(numLines);
|
|
1291
|
+
savedLineItemSpacings = new Float64Array(numLines);
|
|
1292
|
+
for (let i = 0; i < numLines; i++) {
|
|
1293
|
+
savedLineCrossSizes[i] = _lineCrossSizes[i];
|
|
1294
|
+
savedLineCrossOffsets[i] = _lineCrossOffsets[i];
|
|
1295
|
+
savedLineJustifyStarts[i] = _lineJustifyStarts[i];
|
|
1296
|
+
savedLineItemSpacings[i] = _lineItemSpacings[i];
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
let effectiveMainAxisSize = mainAxisSize;
|
|
1300
|
+
const mainIsAuto = isRow ? style.width.unit !== 1 && style.width.unit !== 2 : style.height.unit !== 1 && style.height.unit !== 2;
|
|
1301
|
+
const totalGaps = relativeCount > 1 ? mainGap * (relativeCount - 1) : 0;
|
|
1302
|
+
if (effectiveReverse && mainIsAuto) {
|
|
1303
|
+
let totalContent = 0;
|
|
1304
|
+
for (const child of node.children) {
|
|
1305
|
+
if (child.flex.relativeIndex < 0) continue;
|
|
1306
|
+
totalContent += child.flex.mainSize + child.flex.mainStartMarginValue + child.flex.mainEndMarginValue;
|
|
1307
|
+
}
|
|
1308
|
+
totalContent += totalGaps;
|
|
1309
|
+
effectiveMainAxisSize = totalContent;
|
|
1310
|
+
}
|
|
1311
|
+
let mainPos = effectiveReverse ? effectiveMainAxisSize - startOffset : startOffset;
|
|
1312
|
+
let currentLineIdx = -1;
|
|
1313
|
+
let relIdx = 0;
|
|
1314
|
+
let lineChildIdx = 0;
|
|
1315
|
+
let currentLineLength = 0;
|
|
1316
|
+
let currentItemSpacing = itemSpacing;
|
|
1317
|
+
log.debug?.("positioning children: isRow=%s, startOffset=%d, relativeCount=%d, effectiveReverse=%s, numLines=%d", isRow, startOffset, relativeCount, effectiveReverse, numLines);
|
|
1318
|
+
for (const child of node.children) {
|
|
1319
|
+
if (child.flex.relativeIndex < 0) continue;
|
|
1320
|
+
const cflex = child.flex;
|
|
1321
|
+
const childStyle = child.style;
|
|
1322
|
+
const childLineIdx = cflex.lineIndex;
|
|
1323
|
+
if (childLineIdx !== currentLineIdx) {
|
|
1324
|
+
currentLineIdx = childLineIdx;
|
|
1325
|
+
lineChildIdx = 0;
|
|
1326
|
+
currentLineLength = _lineChildren[childLineIdx].length;
|
|
1327
|
+
const lineOffset = savedLineJustifyStarts ? savedLineJustifyStarts[childLineIdx] : _lineJustifyStarts[childLineIdx];
|
|
1328
|
+
currentItemSpacing = savedLineItemSpacings ? savedLineItemSpacings[childLineIdx] : _lineItemSpacings[childLineIdx];
|
|
1329
|
+
mainPos = effectiveReverse ? effectiveMainAxisSize - lineOffset : lineOffset;
|
|
1330
|
+
}
|
|
1331
|
+
const lineCrossOffset = savedLineCrossOffsets ? savedLineCrossOffsets[childLineIdx] : childLineIdx < MAX_FLEX_LINES ? _lineCrossOffsets[childLineIdx] : 0;
|
|
1332
|
+
let childMarginLeft;
|
|
1333
|
+
let childMarginTop;
|
|
1334
|
+
let childMarginRight;
|
|
1335
|
+
let childMarginBottom;
|
|
1336
|
+
if (isRow) {
|
|
1337
|
+
childMarginLeft = cflex.mainStartMarginAuto && !effectiveReverse ? cflex.mainStartMarginValue : cflex.mainEndMarginAuto && effectiveReverse ? cflex.mainEndMarginValue : cflex.marginL;
|
|
1338
|
+
childMarginRight = cflex.mainEndMarginAuto && !effectiveReverse ? cflex.mainEndMarginValue : cflex.mainStartMarginAuto && effectiveReverse ? cflex.mainStartMarginValue : cflex.marginR;
|
|
1339
|
+
childMarginTop = cflex.marginT;
|
|
1340
|
+
childMarginBottom = cflex.marginB;
|
|
1341
|
+
} else {
|
|
1342
|
+
childMarginTop = cflex.mainStartMarginAuto && !isReverse ? cflex.mainStartMarginValue : cflex.mainEndMarginAuto && isReverse ? cflex.mainEndMarginValue : cflex.marginT;
|
|
1343
|
+
childMarginBottom = cflex.mainEndMarginAuto && !isReverse ? cflex.mainEndMarginValue : cflex.mainStartMarginAuto && isReverse ? cflex.mainStartMarginValue : cflex.marginB;
|
|
1344
|
+
childMarginLeft = cflex.marginL;
|
|
1345
|
+
childMarginRight = cflex.marginR;
|
|
1346
|
+
}
|
|
1347
|
+
const childMainSize = cflex.mainSize;
|
|
1348
|
+
let alignment = style.alignItems;
|
|
1349
|
+
if (childStyle.alignSelf !== 0) alignment = childStyle.alignSelf;
|
|
1350
|
+
const childCrossDimForAR = isRow ? childStyle.height : childStyle.width;
|
|
1351
|
+
const childCrossIsAutoForAR = childCrossDimForAR.unit === 3 || childCrossDimForAR.unit === 0;
|
|
1352
|
+
if (alignment === 4 && childStyle.alignSelf === 0 && !Number.isNaN(childStyle.aspectRatio) && childStyle.aspectRatio > 0 && childCrossIsAutoForAR) alignment = 1;
|
|
1353
|
+
let childCrossSize;
|
|
1354
|
+
const crossDim = isRow ? childStyle.height : childStyle.width;
|
|
1355
|
+
const crossMargin = isRow ? childMarginTop + childMarginBottom : childMarginLeft + childMarginRight;
|
|
1356
|
+
const parentCrossDim = isRow ? style.height : style.width;
|
|
1357
|
+
const parentHasDefiniteCross = parentCrossDim.unit === 1 || parentCrossDim.unit === 2 || !Number.isNaN(crossAxisSize);
|
|
1358
|
+
if (crossDim.unit === 1) childCrossSize = crossDim.value;
|
|
1359
|
+
else if (crossDim.unit === 2) childCrossSize = resolveValue(crossDim, crossAxisSize);
|
|
1360
|
+
else if (parentHasDefiniteCross && alignment === 4) childCrossSize = (numLines > 1 ? savedLineCrossSizes ? savedLineCrossSizes[childLineIdx] : _lineCrossSizes[childLineIdx] : crossAxisSize) - crossMargin;
|
|
1361
|
+
else childCrossSize = NaN;
|
|
1362
|
+
const crossMinVal = isRow ? childStyle.minHeight : childStyle.minWidth;
|
|
1363
|
+
const crossMaxVal = isRow ? childStyle.maxHeight : childStyle.maxWidth;
|
|
1364
|
+
const crossMin = crossMinVal.unit !== 0 ? resolveValue(crossMinVal, crossAxisSize) : 0;
|
|
1365
|
+
const crossMax = crossMaxVal.unit !== 0 ? resolveValue(crossMaxVal, crossAxisSize) : Infinity;
|
|
1366
|
+
if (Number.isNaN(childCrossSize)) {
|
|
1367
|
+
if (crossMin > 0) childCrossSize = crossMin;
|
|
1368
|
+
} else childCrossSize = Math.max(crossMin, Math.min(crossMax, childCrossSize));
|
|
1369
|
+
const mainDim = isRow ? childStyle.width : childStyle.height;
|
|
1370
|
+
const hasDefiniteFlexBasis = childStyle.flexBasis.unit === 1 || childStyle.flexBasis.unit === 2;
|
|
1371
|
+
const mainIsAutoChild = (mainDim.unit === 3 || mainDim.unit === 0) && !hasDefiniteFlexBasis;
|
|
1372
|
+
const hasFlexGrow = cflex.flexGrow > 0;
|
|
1373
|
+
const effectiveMainSize = childMainSize;
|
|
1374
|
+
let childWidth = isRow ? effectiveMainSize : childCrossSize;
|
|
1375
|
+
let childHeight = isRow ? childCrossSize : effectiveMainSize;
|
|
1376
|
+
const shouldMeasure = child.hasMeasureFunc() && child.children.length === 0 && !hasFlexGrow;
|
|
1377
|
+
if (shouldMeasure) {
|
|
1378
|
+
const widthAuto = childStyle.width.unit === 3 || childStyle.width.unit === 0;
|
|
1379
|
+
const heightAuto = childStyle.height.unit === 3 || childStyle.height.unit === 0;
|
|
1380
|
+
if (widthAuto || heightAuto) {
|
|
1381
|
+
const widthMode = widthAuto ? 2 : 1;
|
|
1382
|
+
const heightMode = heightAuto ? 0 : 1;
|
|
1383
|
+
const rawAvailW = widthAuto ? isRow ? mainAxisSize - mainPos : crossAxisSize - crossMargin : childStyle.width.value;
|
|
1384
|
+
const rawAvailH = heightAuto ? isRow ? crossAxisSize - crossMargin : mainAxisSize - mainPos : childStyle.height.value;
|
|
1385
|
+
const availW = Number.isNaN(rawAvailW) ? Infinity : rawAvailW;
|
|
1386
|
+
const availH = Number.isNaN(rawAvailH) ? Infinity : rawAvailH;
|
|
1387
|
+
const measured = child.cachedMeasure(availW, widthMode, availH, heightMode);
|
|
1388
|
+
if (widthAuto) childWidth = measured.width;
|
|
1389
|
+
if (heightAuto) childHeight = measured.height;
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
let childX;
|
|
1393
|
+
let childY;
|
|
1394
|
+
if (effectiveReverse) if (isRow) {
|
|
1395
|
+
childX = mainPos - childMainSize - childMarginRight;
|
|
1396
|
+
childY = lineCrossOffset + childMarginTop;
|
|
1397
|
+
} else {
|
|
1398
|
+
childX = lineCrossOffset + childMarginLeft;
|
|
1399
|
+
childY = mainPos - childMainSize - childMarginTop;
|
|
1400
|
+
}
|
|
1401
|
+
else {
|
|
1402
|
+
childX = isRow ? mainPos + childMarginLeft : lineCrossOffset + childMarginLeft;
|
|
1403
|
+
childY = isRow ? lineCrossOffset + childMarginTop : mainPos + childMarginTop;
|
|
1404
|
+
}
|
|
1405
|
+
const fractionalLeft = innerLeft + childX;
|
|
1406
|
+
const fractionalTop = innerTop + childY;
|
|
1407
|
+
let posOffsetX = 0;
|
|
1408
|
+
let posOffsetY = 0;
|
|
1409
|
+
if (childStyle.positionType === 1) {
|
|
1410
|
+
const relLeftPos = resolvePositionEdge(childStyle.position, 0, direction);
|
|
1411
|
+
const relTopPos = childStyle.position[1];
|
|
1412
|
+
const relRightPos = resolvePositionEdge(childStyle.position, 2, direction);
|
|
1413
|
+
const relBottomPos = childStyle.position[3];
|
|
1414
|
+
if (relLeftPos.unit !== 0) posOffsetX = resolveValue(relLeftPos, contentWidth);
|
|
1415
|
+
else if (relRightPos.unit !== 0) posOffsetX = -resolveValue(relRightPos, contentWidth);
|
|
1416
|
+
if (relTopPos.unit !== 0) posOffsetY = resolveValue(relTopPos, contentHeight);
|
|
1417
|
+
else if (relBottomPos.unit !== 0) posOffsetY = -resolveValue(relBottomPos, contentHeight);
|
|
1418
|
+
}
|
|
1419
|
+
const absChildLeft = absX + marginLeft + parentPosOffsetX + fractionalLeft + posOffsetX;
|
|
1420
|
+
const absChildTop = absY + marginTop + parentPosOffsetY + fractionalTop + posOffsetY;
|
|
1421
|
+
let roundedAbsMainStart;
|
|
1422
|
+
let roundedAbsMainEnd;
|
|
1423
|
+
let edgeBasedMainSize;
|
|
1424
|
+
const useEdgeBasedRounding = childMainSize > 0;
|
|
1425
|
+
const childPaddingL = resolveEdgeValue(childStyle.padding, 0, childStyle.flexDirection, contentWidth, direction);
|
|
1426
|
+
const childPaddingT = resolveEdgeValue(childStyle.padding, 1, childStyle.flexDirection, contentWidth, direction);
|
|
1427
|
+
const childPaddingR = resolveEdgeValue(childStyle.padding, 2, childStyle.flexDirection, contentWidth, direction);
|
|
1428
|
+
const childPaddingB = resolveEdgeValue(childStyle.padding, 3, childStyle.flexDirection, contentWidth, direction);
|
|
1429
|
+
const childBorderL = resolveEdgeBorderValue(childStyle.border, 0, childStyle.flexDirection, direction);
|
|
1430
|
+
const childBorderT = resolveEdgeBorderValue(childStyle.border, 1, childStyle.flexDirection, direction);
|
|
1431
|
+
const childBorderR = resolveEdgeBorderValue(childStyle.border, 2, childStyle.flexDirection, direction);
|
|
1432
|
+
const childBorderB = resolveEdgeBorderValue(childStyle.border, 3, childStyle.flexDirection, direction);
|
|
1433
|
+
const childMinW = childPaddingL + childPaddingR + childBorderL + childBorderR;
|
|
1434
|
+
const childMinH = childPaddingT + childPaddingB + childBorderT + childBorderB;
|
|
1435
|
+
const childMinMain = isRow ? childMinW : childMinH;
|
|
1436
|
+
const constrainedMainSize = Math.max(childMainSize, childMinMain);
|
|
1437
|
+
if (useEdgeBasedRounding) if (isRow) {
|
|
1438
|
+
roundedAbsMainStart = Math.round(absChildLeft);
|
|
1439
|
+
roundedAbsMainEnd = Math.round(absChildLeft + constrainedMainSize);
|
|
1440
|
+
edgeBasedMainSize = roundedAbsMainEnd - roundedAbsMainStart;
|
|
1441
|
+
} else {
|
|
1442
|
+
roundedAbsMainStart = Math.round(absChildTop);
|
|
1443
|
+
roundedAbsMainEnd = Math.round(absChildTop + constrainedMainSize);
|
|
1444
|
+
edgeBasedMainSize = roundedAbsMainEnd - roundedAbsMainStart;
|
|
1445
|
+
}
|
|
1446
|
+
else {
|
|
1447
|
+
roundedAbsMainStart = isRow ? Math.round(absChildLeft) : Math.round(absChildTop);
|
|
1448
|
+
edgeBasedMainSize = childMinMain;
|
|
1449
|
+
}
|
|
1450
|
+
const posRound = shouldMeasure ? Math.floor : Math.round;
|
|
1451
|
+
const childLeft = posRound(fractionalLeft + posOffsetX);
|
|
1452
|
+
const childTop = posRound(fractionalTop + posOffsetY);
|
|
1453
|
+
const crossDimForLayoutCall = isRow ? childStyle.height : childStyle.width;
|
|
1454
|
+
const crossIsAutoForLayoutCall = crossDimForLayoutCall.unit === 3 || crossDimForLayoutCall.unit === 0;
|
|
1455
|
+
const mainIsPercentForLayoutCall = (isRow ? childStyle.width : childStyle.height).unit === 2;
|
|
1456
|
+
const crossIsPercentForLayoutCall = crossDimForLayoutCall.unit === 2;
|
|
1457
|
+
const flexDistChanged = child.flex.mainSize !== child.flex.baseSize;
|
|
1458
|
+
const hasMeasureLeaf = child.hasMeasureFunc() && child.children.length === 0;
|
|
1459
|
+
layoutNode(child, isRow && mainIsAutoChild && !hasFlexGrow && !flexDistChanged && !hasMeasureLeaf ? NaN : !isRow && crossIsAutoForLayoutCall && !parentHasDefiniteCross ? NaN : isRow && mainIsPercentForLayoutCall ? mainAxisSize : !isRow && crossIsPercentForLayoutCall ? crossAxisSize : childWidth, !isRow && mainIsAutoChild && !hasFlexGrow && !flexDistChanged && !hasMeasureLeaf ? NaN : isRow && crossIsAutoForLayoutCall && !parentHasDefiniteCross ? NaN : !isRow && mainIsPercentForLayoutCall ? mainAxisSize : isRow && crossIsPercentForLayoutCall ? crossAxisSize : childHeight, childLeft, childTop, absChildLeft - childMarginLeft, absChildTop - childMarginTop, direction);
|
|
1460
|
+
if (childWidth < childMinW) childWidth = childMinW;
|
|
1461
|
+
if (childHeight < childMinH) childHeight = childMinH;
|
|
1462
|
+
const hasMeasure = child.hasMeasureFunc() && child.children.length === 0;
|
|
1463
|
+
const flexDistributionChangedSize = child.flex.mainSize !== child.flex.baseSize;
|
|
1464
|
+
if (!mainIsAuto && !mainIsAutoChild || hasFlexGrow || hasMeasure || flexDistributionChangedSize) if (isRow) {
|
|
1465
|
+
_t?.parentOverride(_tn, "main", child.layout.width, edgeBasedMainSize);
|
|
1466
|
+
child.layout.width = edgeBasedMainSize;
|
|
1467
|
+
} else {
|
|
1468
|
+
_t?.parentOverride(_tn, "main", child.layout.height, edgeBasedMainSize);
|
|
1469
|
+
child.layout.height = edgeBasedMainSize;
|
|
1470
|
+
}
|
|
1471
|
+
const crossDimForCheck = isRow ? childStyle.height : childStyle.width;
|
|
1472
|
+
const crossIsAuto = crossDimForCheck.unit === 3 || crossDimForCheck.unit === 0;
|
|
1473
|
+
const parentCrossIsAuto = !parentHasDefiniteCross;
|
|
1474
|
+
const hasCrossMinMax = crossMinVal.unit !== 0 || crossMaxVal.unit !== 0;
|
|
1475
|
+
if (!crossIsAuto || !parentCrossIsAuto && alignment === 4 || hasCrossMinMax && !Number.isNaN(childCrossSize)) if (isRow) child.layout.height = Math.round(childHeight);
|
|
1476
|
+
else child.layout.width = Math.round(childWidth);
|
|
1477
|
+
child.layout.left = childLeft;
|
|
1478
|
+
child.layout.top = childTop;
|
|
1479
|
+
childWidth = child.layout.width;
|
|
1480
|
+
childHeight = child.layout.height;
|
|
1481
|
+
const finalCrossSize = isRow ? child.layout.height : child.layout.width;
|
|
1482
|
+
let crossOffset = 0;
|
|
1483
|
+
const crossStartIndex = isRow ? 1 : 0;
|
|
1484
|
+
const crossEndIndex = isRow ? 3 : 2;
|
|
1485
|
+
const hasAutoStartMargin = isEdgeAuto(childStyle.margin, crossStartIndex, style.flexDirection, direction);
|
|
1486
|
+
const hasAutoEndMargin = isEdgeAuto(childStyle.margin, crossEndIndex, style.flexDirection, direction);
|
|
1487
|
+
const availableCrossSpace = (hasBaselineAlignment && isRow && !alignItemsIsBaseline && alignment !== 5 && baselineZoneHeight > 0 ? baselineZoneHeight : crossAxisSize) - finalCrossSize - crossMargin;
|
|
1488
|
+
if (hasAutoStartMargin && hasAutoEndMargin) crossOffset = Math.max(0, availableCrossSpace) / 2;
|
|
1489
|
+
else if (hasAutoStartMargin) crossOffset = Math.max(0, availableCrossSpace);
|
|
1490
|
+
else if (hasAutoEndMargin) crossOffset = 0;
|
|
1491
|
+
else switch (alignment) {
|
|
1492
|
+
case 3:
|
|
1493
|
+
crossOffset = availableCrossSpace;
|
|
1494
|
+
break;
|
|
1495
|
+
case 2:
|
|
1496
|
+
crossOffset = availableCrossSpace / 2;
|
|
1497
|
+
break;
|
|
1498
|
+
case 5:
|
|
1499
|
+
if (isRow && hasBaselineAlignment) crossOffset = maxBaseline - child.flex.baseline;
|
|
1500
|
+
break;
|
|
1501
|
+
}
|
|
1502
|
+
if (crossOffset !== 0) {
|
|
1503
|
+
const crossRound = shouldMeasure ? Math.floor : Math.round;
|
|
1504
|
+
if (isRow) child.layout.top += crossRound(crossOffset);
|
|
1505
|
+
else child.layout.left += crossRound(crossOffset);
|
|
1506
|
+
}
|
|
1507
|
+
const fractionalMainSize = !mainIsAuto && !mainIsAutoChild || hasFlexGrow || hasMeasure || flexDistributionChangedSize ? constrainedMainSize : isRow ? child.layout.width : child.layout.height;
|
|
1508
|
+
const totalMainMargin = cflex.mainStartMarginValue + cflex.mainEndMarginValue;
|
|
1509
|
+
log.debug?.(" child %d: mainPos=%d -> top=%d (fractionalMainSize=%d, totalMainMargin=%d)", relIdx, mainPos, child.layout.top, fractionalMainSize, totalMainMargin);
|
|
1510
|
+
if (effectiveReverse) {
|
|
1511
|
+
mainPos -= fractionalMainSize + totalMainMargin;
|
|
1512
|
+
if (lineChildIdx < currentLineLength - 1) mainPos -= currentItemSpacing;
|
|
1513
|
+
} else {
|
|
1514
|
+
mainPos += fractionalMainSize + totalMainMargin;
|
|
1515
|
+
if (lineChildIdx < currentLineLength - 1) mainPos += currentItemSpacing;
|
|
1516
|
+
}
|
|
1517
|
+
relIdx++;
|
|
1518
|
+
lineChildIdx++;
|
|
1519
|
+
}
|
|
1520
|
+
let actualUsedMain = 0;
|
|
1521
|
+
for (const child of node.children) {
|
|
1522
|
+
if (child.flex.relativeIndex < 0) continue;
|
|
1523
|
+
const childMainSize = isRow ? child.layout.width : child.layout.height;
|
|
1524
|
+
const totalMainMargin = child.flex.mainStartMarginValue + child.flex.mainEndMarginValue;
|
|
1525
|
+
actualUsedMain += childMainSize + totalMainMargin;
|
|
1526
|
+
}
|
|
1527
|
+
actualUsedMain += totalGaps;
|
|
1528
|
+
const hasAR = !Number.isNaN(aspectRatio) && aspectRatio > 0;
|
|
1529
|
+
if (isRow && style.width.unit !== 1 && style.width.unit !== 2 && !hasAR) nodeWidth = actualUsedMain + innerLeft + innerRight;
|
|
1530
|
+
if (!isRow && style.height.unit !== 1 && style.height.unit !== 2 && !hasAR) nodeHeight = actualUsedMain + innerTop + innerBottom;
|
|
1531
|
+
let totalCrossSize = 0;
|
|
1532
|
+
if (numLines > 1) {
|
|
1533
|
+
for (let i = 0; i < numLines; i++) totalCrossSize += savedLineCrossSizes ? savedLineCrossSizes[i] : _lineCrossSizes[i];
|
|
1534
|
+
totalCrossSize += crossGap * (numLines - 1);
|
|
1535
|
+
} else for (const child of node.children) {
|
|
1536
|
+
if (child.flex.relativeIndex < 0) continue;
|
|
1537
|
+
const childCross = isRow ? child.layout.height : child.layout.width;
|
|
1538
|
+
const childMargin = isRow ? resolveEdgeValue(child.style.margin, 1, style.flexDirection, contentWidth, direction) + resolveEdgeValue(child.style.margin, 3, style.flexDirection, contentWidth, direction) : resolveEdgeValue(child.style.margin, 0, style.flexDirection, contentWidth, direction) + resolveEdgeValue(child.style.margin, 2, style.flexDirection, contentWidth, direction);
|
|
1539
|
+
totalCrossSize = Math.max(totalCrossSize, childCross + childMargin);
|
|
1540
|
+
}
|
|
1541
|
+
if (isRow && style.height.unit !== 1 && style.height.unit !== 2 && Number.isNaN(availableHeight) && !hasAR) nodeHeight = totalCrossSize + innerTop + innerBottom;
|
|
1542
|
+
if (!isRow && style.width.unit !== 1 && style.width.unit !== 2 && Number.isNaN(availableWidth) && !hasAR) nodeWidth = totalCrossSize + innerLeft + innerRight;
|
|
1543
|
+
}
|
|
1544
|
+
nodeWidth = applyMinMax(nodeWidth, style.minWidth, style.maxWidth, availableWidth);
|
|
1545
|
+
nodeHeight = applyMinMax(nodeHeight, style.minHeight, style.maxHeight, availableHeight);
|
|
1546
|
+
if (!Number.isNaN(nodeWidth) && nodeWidth < minInnerWidth) nodeWidth = minInnerWidth;
|
|
1547
|
+
if (!Number.isNaN(nodeHeight) && nodeHeight < minInnerHeight) nodeHeight = minInnerHeight;
|
|
1548
|
+
if (Number.isNaN(crossAxisSize) && relativeCount > 0) {
|
|
1549
|
+
const finalCross = isRow ? nodeHeight - innerTop - innerBottom : nodeWidth - innerLeft - innerRight;
|
|
1550
|
+
if (!Number.isNaN(finalCross) && finalCross > 0) {
|
|
1551
|
+
for (const child of node.children) {
|
|
1552
|
+
if (child.flex.relativeIndex < 0) continue;
|
|
1553
|
+
const cstyle = child.style;
|
|
1554
|
+
let childAlign = style.alignItems;
|
|
1555
|
+
if (cstyle.alignSelf !== 0) childAlign = cstyle.alignSelf;
|
|
1556
|
+
const cCrossDim = isRow ? cstyle.height : cstyle.width;
|
|
1557
|
+
const cCrossIsAuto = cCrossDim.unit === 3 || cCrossDim.unit === 0;
|
|
1558
|
+
if (childAlign === 4 && cstyle.alignSelf === 0 && !Number.isNaN(cstyle.aspectRatio) && cstyle.aspectRatio > 0 && cCrossIsAuto) childAlign = 1;
|
|
1559
|
+
if (childAlign !== 4) continue;
|
|
1560
|
+
if (!cCrossIsAuto) continue;
|
|
1561
|
+
const stretchedCross = finalCross - (isRow ? resolveEdgeValue(cstyle.margin, 1, style.flexDirection, contentWidth, direction) + resolveEdgeValue(cstyle.margin, 3, style.flexDirection, contentWidth, direction) : resolveEdgeValue(cstyle.margin, 0, style.flexDirection, contentWidth, direction) + resolveEdgeValue(cstyle.margin, 2, style.flexDirection, contentWidth, direction));
|
|
1562
|
+
const currentCross = isRow ? child.layout.height : child.layout.width;
|
|
1563
|
+
if (Math.round(stretchedCross) <= currentCross) continue;
|
|
1564
|
+
const savedLeft = child.layout.left;
|
|
1565
|
+
const savedTop = child.layout.top;
|
|
1566
|
+
const cMarginL = resolveEdgeValue(cstyle.margin, 0, style.flexDirection, contentWidth, direction);
|
|
1567
|
+
const cMarginT = resolveEdgeValue(cstyle.margin, 1, style.flexDirection, contentWidth, direction);
|
|
1568
|
+
const cAbsX = absX + innerLeft + savedLeft - cMarginL;
|
|
1569
|
+
const cAbsY = absY + innerTop + savedTop - cMarginT;
|
|
1570
|
+
layoutNode(child, isRow ? child.layout.width : stretchedCross, isRow ? stretchedCross : child.layout.height, savedLeft, savedTop, cAbsX, cAbsY, direction);
|
|
1571
|
+
child.layout.left = savedLeft;
|
|
1572
|
+
child.layout.top = savedTop;
|
|
1573
|
+
if (isRow) child.layout.height = Math.round(stretchedCross);
|
|
1574
|
+
else child.layout.width = Math.round(stretchedCross);
|
|
1575
|
+
}
|
|
1576
|
+
if (Number.isNaN(crossAxisSize) && relativeCount > 0) {
|
|
1577
|
+
const finalCross9c = isRow ? nodeHeight - innerTop - innerBottom : nodeWidth - innerLeft - innerRight;
|
|
1578
|
+
if (!Number.isNaN(finalCross9c) && finalCross9c > 0) for (const child of node.children) {
|
|
1579
|
+
if (child.flex.relativeIndex < 0) continue;
|
|
1580
|
+
const cstyle = child.style;
|
|
1581
|
+
let childAlign = style.alignItems;
|
|
1582
|
+
if (cstyle.alignSelf !== 0) childAlign = cstyle.alignSelf;
|
|
1583
|
+
const cCrossDim = isRow ? cstyle.height : cstyle.width;
|
|
1584
|
+
const cCrossIsAuto = cCrossDim.unit === 3 || cCrossDim.unit === 0;
|
|
1585
|
+
if (childAlign === 4 && cstyle.alignSelf === 0 && !Number.isNaN(cstyle.aspectRatio) && cstyle.aspectRatio > 0 && cCrossIsAuto) childAlign = 1;
|
|
1586
|
+
const crossStartIdx = isRow ? 1 : 0;
|
|
1587
|
+
const crossEndIdx = isRow ? 3 : 2;
|
|
1588
|
+
const hasAutoStart = isEdgeAuto(cstyle.margin, crossStartIdx, style.flexDirection, direction);
|
|
1589
|
+
const hasAutoEnd = isEdgeAuto(cstyle.margin, crossEndIdx, style.flexDirection, direction);
|
|
1590
|
+
if (!(hasAutoStart || hasAutoEnd || childAlign === 2 || childAlign === 3)) continue;
|
|
1591
|
+
const childCrossSize = isRow ? child.layout.height : child.layout.width;
|
|
1592
|
+
const cCrossMargin = isRow ? resolveEdgeValue(cstyle.margin, 1, style.flexDirection, contentWidth, direction) + resolveEdgeValue(cstyle.margin, 3, style.flexDirection, contentWidth, direction) : resolveEdgeValue(cstyle.margin, 0, style.flexDirection, contentWidth, direction) + resolveEdgeValue(cstyle.margin, 2, style.flexDirection, contentWidth, direction);
|
|
1593
|
+
const availSpace = finalCross9c - childCrossSize - cCrossMargin;
|
|
1594
|
+
let crossOffset = 0;
|
|
1595
|
+
if (hasAutoStart && hasAutoEnd) crossOffset = Math.max(0, availSpace) / 2;
|
|
1596
|
+
else if (hasAutoStart) crossOffset = Math.max(0, availSpace);
|
|
1597
|
+
else if (hasAutoEnd) crossOffset = 0;
|
|
1598
|
+
else switch (childAlign) {
|
|
1599
|
+
case 3:
|
|
1600
|
+
crossOffset = availSpace;
|
|
1601
|
+
break;
|
|
1602
|
+
case 2:
|
|
1603
|
+
crossOffset = availSpace / 2;
|
|
1604
|
+
break;
|
|
1605
|
+
}
|
|
1606
|
+
if (isRow) {
|
|
1607
|
+
if (Number.isNaN(child.layout.top)) {
|
|
1608
|
+
const cMarginT = resolveEdgeValue(cstyle.margin, 1, style.flexDirection, contentWidth, direction);
|
|
1609
|
+
child.layout.top = Math.round(cMarginT + crossOffset);
|
|
1610
|
+
} else if (crossOffset !== 0) child.layout.top += Math.round(crossOffset);
|
|
1611
|
+
} else if (Number.isNaN(child.layout.left)) {
|
|
1612
|
+
const cMarginL = resolveEdgeValue(cstyle.margin, 0, style.flexDirection, contentWidth, direction);
|
|
1613
|
+
child.layout.left = Math.round(cMarginL + crossOffset);
|
|
1614
|
+
} else if (crossOffset !== 0) child.layout.left += Math.round(crossOffset);
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
const absNodeLeft = absX + marginLeft + parentPosOffsetX;
|
|
1620
|
+
const absNodeTop = absY + marginTop + parentPosOffsetY;
|
|
1621
|
+
const absNodeRight = absNodeLeft + nodeWidth;
|
|
1622
|
+
const absNodeBottom = absNodeTop + nodeHeight;
|
|
1623
|
+
const roundedAbsLeft = Math.round(absNodeLeft);
|
|
1624
|
+
const roundedAbsTop = Math.round(absNodeTop);
|
|
1625
|
+
const roundedAbsRight = Math.round(absNodeRight);
|
|
1626
|
+
const roundedAbsBottom = Math.round(absNodeBottom);
|
|
1627
|
+
layout.width = roundedAbsRight - roundedAbsLeft;
|
|
1628
|
+
layout.height = roundedAbsBottom - roundedAbsTop;
|
|
1629
|
+
const roundedAbsParentLeft = Math.round(absX);
|
|
1630
|
+
const roundedAbsParentTop = Math.round(absY);
|
|
1631
|
+
layout.left = roundedAbsLeft - roundedAbsParentLeft;
|
|
1632
|
+
layout.top = roundedAbsTop - roundedAbsParentTop;
|
|
1633
|
+
const absInnerLeft = borderLeft;
|
|
1634
|
+
const absInnerTop = borderTop;
|
|
1635
|
+
const absInnerRight = borderRight;
|
|
1636
|
+
const absInnerBottom = borderBottom;
|
|
1637
|
+
const absPaddingBoxW = nodeWidth - absInnerLeft - absInnerRight;
|
|
1638
|
+
const absPaddingBoxH = nodeHeight - absInnerTop - absInnerBottom;
|
|
1639
|
+
const absContentBoxW = absPaddingBoxW - paddingLeft - paddingRight;
|
|
1640
|
+
const absContentBoxH = absPaddingBoxH - paddingTop - paddingBottom;
|
|
1641
|
+
for (const child of node.children) {
|
|
1642
|
+
if (child.style.display === 1) continue;
|
|
1643
|
+
if (child.style.positionType !== 2) continue;
|
|
1644
|
+
const childStyle = child.style;
|
|
1645
|
+
const childMarginLeft = resolveEdgeValue(childStyle.margin, 0, style.flexDirection, nodeWidth, direction);
|
|
1646
|
+
const childMarginTop = resolveEdgeValue(childStyle.margin, 1, style.flexDirection, nodeWidth, direction);
|
|
1647
|
+
const childMarginRight = resolveEdgeValue(childStyle.margin, 2, style.flexDirection, nodeWidth, direction);
|
|
1648
|
+
const childMarginBottom = resolveEdgeValue(childStyle.margin, 3, style.flexDirection, nodeWidth, direction);
|
|
1649
|
+
const hasAutoMarginLeft = isEdgeAuto(childStyle.margin, 0, style.flexDirection, direction);
|
|
1650
|
+
const hasAutoMarginRight = isEdgeAuto(childStyle.margin, 2, style.flexDirection, direction);
|
|
1651
|
+
const hasAutoMarginTop = isEdgeAuto(childStyle.margin, 1, style.flexDirection, direction);
|
|
1652
|
+
const hasAutoMarginBottom = isEdgeAuto(childStyle.margin, 3, style.flexDirection, direction);
|
|
1653
|
+
const leftPos = resolvePositionEdge(childStyle.position, 0, direction);
|
|
1654
|
+
const topPos = childStyle.position[1];
|
|
1655
|
+
const rightPos = resolvePositionEdge(childStyle.position, 2, direction);
|
|
1656
|
+
const bottomPos = childStyle.position[3];
|
|
1657
|
+
const hasLeft = leftPos.unit !== 0;
|
|
1658
|
+
const hasRight = rightPos.unit !== 0;
|
|
1659
|
+
const hasTop = topPos.unit !== 0;
|
|
1660
|
+
const hasBottom = bottomPos.unit !== 0;
|
|
1661
|
+
const leftOffset = resolveValue(leftPos, absContentBoxW);
|
|
1662
|
+
const topOffset = resolveValue(topPos, absContentBoxH);
|
|
1663
|
+
const rightOffset = resolveValue(rightPos, absContentBoxW);
|
|
1664
|
+
const bottomOffset = resolveValue(bottomPos, absContentBoxH);
|
|
1665
|
+
const contentW = absPaddingBoxW;
|
|
1666
|
+
const contentH = absPaddingBoxH;
|
|
1667
|
+
let childAvailWidth;
|
|
1668
|
+
const widthIsAuto = childStyle.width.unit === 3 || childStyle.width.unit === 0;
|
|
1669
|
+
const widthIsPercent = childStyle.width.unit === 2;
|
|
1670
|
+
if (widthIsAuto && hasLeft && hasRight) childAvailWidth = contentW - leftOffset - rightOffset - childMarginLeft - childMarginRight;
|
|
1671
|
+
else if (widthIsAuto) childAvailWidth = NaN;
|
|
1672
|
+
else if (widthIsPercent) childAvailWidth = absContentBoxW;
|
|
1673
|
+
else childAvailWidth = contentW;
|
|
1674
|
+
let childAvailHeight;
|
|
1675
|
+
const heightIsAuto = childStyle.height.unit === 3 || childStyle.height.unit === 0;
|
|
1676
|
+
const heightIsPercent = childStyle.height.unit === 2;
|
|
1677
|
+
if (heightIsAuto && hasTop && hasBottom) childAvailHeight = contentH - topOffset - bottomOffset - childMarginTop - childMarginBottom;
|
|
1678
|
+
else if (heightIsAuto) childAvailHeight = NaN;
|
|
1679
|
+
else if (heightIsPercent) childAvailHeight = absContentBoxH;
|
|
1680
|
+
else childAvailHeight = contentH;
|
|
1681
|
+
let childX = childMarginLeft + leftOffset;
|
|
1682
|
+
let childY = childMarginTop + topOffset;
|
|
1683
|
+
const childAbsX = absX + marginLeft + absInnerLeft + leftOffset;
|
|
1684
|
+
const childAbsY = absY + marginTop + absInnerTop + topOffset;
|
|
1685
|
+
const clampIfNumber = (v) => Number.isNaN(v) ? NaN : Math.max(0, v);
|
|
1686
|
+
layoutNode(child, clampIfNumber(childAvailWidth), clampIfNumber(childAvailHeight), layout.left + absInnerLeft + childX, layout.top + absInnerTop + childY, childAbsX, childAbsY, direction);
|
|
1687
|
+
const childWidth = child.layout.width;
|
|
1688
|
+
const childHeight = child.layout.height;
|
|
1689
|
+
if (!hasLeft && !hasRight) if (isRow) {
|
|
1690
|
+
const freeSpaceX = contentW - childWidth - childMarginLeft - childMarginRight;
|
|
1691
|
+
switch (style.justifyContent) {
|
|
1692
|
+
case 1:
|
|
1693
|
+
childX = childMarginLeft + freeSpaceX / 2;
|
|
1694
|
+
break;
|
|
1695
|
+
case 2:
|
|
1696
|
+
childX = childMarginLeft + freeSpaceX;
|
|
1697
|
+
break;
|
|
1698
|
+
default:
|
|
1699
|
+
childX = childMarginLeft;
|
|
1700
|
+
break;
|
|
1701
|
+
}
|
|
1702
|
+
} else {
|
|
1703
|
+
let alignment = style.alignItems;
|
|
1704
|
+
if (childStyle.alignSelf !== 0) alignment = childStyle.alignSelf;
|
|
1705
|
+
const freeSpaceX = contentW - childWidth - childMarginLeft - childMarginRight;
|
|
1706
|
+
switch (alignment) {
|
|
1707
|
+
case 2:
|
|
1708
|
+
childX = childMarginLeft + freeSpaceX / 2;
|
|
1709
|
+
break;
|
|
1710
|
+
case 3:
|
|
1711
|
+
childX = childMarginLeft + freeSpaceX;
|
|
1712
|
+
break;
|
|
1713
|
+
case 4: break;
|
|
1714
|
+
default:
|
|
1715
|
+
childX = childMarginLeft;
|
|
1716
|
+
break;
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
else if (!hasLeft && hasRight) childX = contentW - rightOffset - childMarginRight - childWidth;
|
|
1720
|
+
else if (hasLeft && hasRight) {
|
|
1721
|
+
if (widthIsAuto) child.layout.width = Math.round(childAvailWidth);
|
|
1722
|
+
else if (hasAutoMarginLeft || hasAutoMarginRight) {
|
|
1723
|
+
const freeSpace = Math.max(0, contentW - leftOffset - rightOffset - childWidth);
|
|
1724
|
+
if (hasAutoMarginLeft && hasAutoMarginRight) childX = leftOffset + freeSpace / 2;
|
|
1725
|
+
else if (hasAutoMarginLeft) childX = leftOffset + freeSpace;
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
if (!hasTop && !hasBottom) if (isRow) {
|
|
1729
|
+
let alignment = style.alignItems;
|
|
1730
|
+
if (childStyle.alignSelf !== 0) alignment = childStyle.alignSelf;
|
|
1731
|
+
const freeSpaceY = contentH - childHeight - childMarginTop - childMarginBottom;
|
|
1732
|
+
switch (alignment) {
|
|
1733
|
+
case 2:
|
|
1734
|
+
childY = childMarginTop + freeSpaceY / 2;
|
|
1735
|
+
break;
|
|
1736
|
+
case 3:
|
|
1737
|
+
childY = childMarginTop + freeSpaceY;
|
|
1738
|
+
break;
|
|
1739
|
+
case 4: break;
|
|
1740
|
+
default:
|
|
1741
|
+
childY = childMarginTop;
|
|
1742
|
+
break;
|
|
1743
|
+
}
|
|
1744
|
+
} else {
|
|
1745
|
+
const freeSpaceY = contentH - childHeight - childMarginTop - childMarginBottom;
|
|
1746
|
+
switch (style.justifyContent) {
|
|
1747
|
+
case 1:
|
|
1748
|
+
childY = childMarginTop + freeSpaceY / 2;
|
|
1749
|
+
break;
|
|
1750
|
+
case 2:
|
|
1751
|
+
childY = childMarginTop + freeSpaceY;
|
|
1752
|
+
break;
|
|
1753
|
+
default:
|
|
1754
|
+
childY = childMarginTop;
|
|
1755
|
+
break;
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
else if (!hasTop && hasBottom) childY = contentH - bottomOffset - childMarginBottom - childHeight;
|
|
1759
|
+
else if (hasTop && hasBottom) {
|
|
1760
|
+
if (heightIsAuto) child.layout.height = Math.round(childAvailHeight);
|
|
1761
|
+
else if (hasAutoMarginTop || hasAutoMarginBottom) {
|
|
1762
|
+
const freeSpace = Math.max(0, contentH - topOffset - bottomOffset - childHeight);
|
|
1763
|
+
if (hasAutoMarginTop && hasAutoMarginBottom) childY = topOffset + freeSpace / 2;
|
|
1764
|
+
else if (hasAutoMarginTop) childY = topOffset + freeSpace;
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
child.layout.left = Math.round(absInnerLeft + childX);
|
|
1768
|
+
child.layout.top = Math.round(absInnerTop + childY);
|
|
1769
|
+
}
|
|
1770
|
+
flex.lastAvailW = availableWidth;
|
|
1771
|
+
flex.lastAvailH = availableHeight;
|
|
1772
|
+
flex.lastOffsetX = offsetX;
|
|
1773
|
+
flex.lastOffsetY = offsetY;
|
|
1774
|
+
flex.lastAbsX = absX;
|
|
1775
|
+
flex.lastAbsY = absY;
|
|
1776
|
+
flex.lastDir = direction;
|
|
1777
|
+
flex.layoutValid = true;
|
|
1778
|
+
_t?.layoutExit(_tn, layout.width, layout.height);
|
|
1779
|
+
}
|
|
1780
|
+
//#endregion
|
|
1781
|
+
//#region ../../flexily/src/types.ts
|
|
1782
|
+
/**
|
|
1783
|
+
* Create a default Value (undefined).
|
|
1784
|
+
*/
|
|
1785
|
+
function createValue(value = 0, unit = 0) {
|
|
1786
|
+
return {
|
|
1787
|
+
value,
|
|
1788
|
+
unit
|
|
1789
|
+
};
|
|
1790
|
+
}
|
|
1791
|
+
/**
|
|
1792
|
+
* Create default style.
|
|
1793
|
+
*
|
|
1794
|
+
* Comments indicate where Yoga and CSS defaults differ.
|
|
1795
|
+
* Flexily follows Yoga defaults for API compatibility.
|
|
1796
|
+
*/
|
|
1797
|
+
function createDefaultStyle() {
|
|
1798
|
+
return {
|
|
1799
|
+
display: 0,
|
|
1800
|
+
positionType: 1,
|
|
1801
|
+
position: [
|
|
1802
|
+
createValue(),
|
|
1803
|
+
createValue(),
|
|
1804
|
+
createValue(),
|
|
1805
|
+
createValue(),
|
|
1806
|
+
createValue(),
|
|
1807
|
+
createValue()
|
|
1808
|
+
],
|
|
1809
|
+
flexDirection: 2,
|
|
1810
|
+
flexWrap: 0,
|
|
1811
|
+
flexGrow: 0,
|
|
1812
|
+
flexShrink: 0,
|
|
1813
|
+
flexBasis: createValue(0, 3),
|
|
1814
|
+
alignItems: 4,
|
|
1815
|
+
alignSelf: 0,
|
|
1816
|
+
alignContent: 1,
|
|
1817
|
+
justifyContent: 0,
|
|
1818
|
+
width: createValue(0, 3),
|
|
1819
|
+
height: createValue(0, 3),
|
|
1820
|
+
minWidth: createValue(),
|
|
1821
|
+
minHeight: createValue(),
|
|
1822
|
+
maxWidth: createValue(),
|
|
1823
|
+
maxHeight: createValue(),
|
|
1824
|
+
aspectRatio: NaN,
|
|
1825
|
+
margin: [
|
|
1826
|
+
createValue(),
|
|
1827
|
+
createValue(),
|
|
1828
|
+
createValue(),
|
|
1829
|
+
createValue(),
|
|
1830
|
+
createValue(),
|
|
1831
|
+
createValue()
|
|
1832
|
+
],
|
|
1833
|
+
padding: [
|
|
1834
|
+
createValue(),
|
|
1835
|
+
createValue(),
|
|
1836
|
+
createValue(),
|
|
1837
|
+
createValue(),
|
|
1838
|
+
createValue(),
|
|
1839
|
+
createValue()
|
|
1840
|
+
],
|
|
1841
|
+
border: [
|
|
1842
|
+
0,
|
|
1843
|
+
0,
|
|
1844
|
+
0,
|
|
1845
|
+
0,
|
|
1846
|
+
NaN,
|
|
1847
|
+
NaN
|
|
1848
|
+
],
|
|
1849
|
+
gap: [0, 0],
|
|
1850
|
+
overflow: 0
|
|
1851
|
+
};
|
|
1852
|
+
}
|
|
1853
|
+
//#endregion
|
|
1854
|
+
//#region ../../flexily/src/node-zero.ts
|
|
1855
|
+
/**
|
|
1856
|
+
* Flexily Node
|
|
1857
|
+
*
|
|
1858
|
+
* Yoga-compatible Node class for flexbox layout.
|
|
1859
|
+
*/
|
|
1860
|
+
/**
|
|
1861
|
+
* A layout node in the flexbox tree.
|
|
1862
|
+
*/
|
|
1863
|
+
var Node = class Node {
|
|
1864
|
+
_parent = null;
|
|
1865
|
+
_children = [];
|
|
1866
|
+
_style = createDefaultStyle();
|
|
1867
|
+
_measureFunc = null;
|
|
1868
|
+
_baselineFunc = null;
|
|
1869
|
+
_m0;
|
|
1870
|
+
_m1;
|
|
1871
|
+
_m2;
|
|
1872
|
+
_m3;
|
|
1873
|
+
_lc0;
|
|
1874
|
+
_lc1;
|
|
1875
|
+
_measureResult = {
|
|
1876
|
+
width: 0,
|
|
1877
|
+
height: 0
|
|
1878
|
+
};
|
|
1879
|
+
_layoutResult = {
|
|
1880
|
+
width: 0,
|
|
1881
|
+
height: 0
|
|
1882
|
+
};
|
|
1883
|
+
static measureCalls = 0;
|
|
1884
|
+
static measureCacheHits = 0;
|
|
1885
|
+
/**
|
|
1886
|
+
* Reset measure statistics (call before calculateLayout).
|
|
1887
|
+
*/
|
|
1888
|
+
static resetMeasureStats() {
|
|
1889
|
+
Node.measureCalls = 0;
|
|
1890
|
+
Node.measureCacheHits = 0;
|
|
1891
|
+
}
|
|
1892
|
+
_layout = {
|
|
1893
|
+
left: 0,
|
|
1894
|
+
top: 0,
|
|
1895
|
+
width: 0,
|
|
1896
|
+
height: 0
|
|
1897
|
+
};
|
|
1898
|
+
_flex = {
|
|
1899
|
+
mainSize: 0,
|
|
1900
|
+
baseSize: 0,
|
|
1901
|
+
mainMargin: 0,
|
|
1902
|
+
flexGrow: 0,
|
|
1903
|
+
flexShrink: 0,
|
|
1904
|
+
minMain: 0,
|
|
1905
|
+
maxMain: Infinity,
|
|
1906
|
+
mainStartMarginAuto: false,
|
|
1907
|
+
mainEndMarginAuto: false,
|
|
1908
|
+
mainStartMarginValue: 0,
|
|
1909
|
+
mainEndMarginValue: 0,
|
|
1910
|
+
marginL: 0,
|
|
1911
|
+
marginT: 0,
|
|
1912
|
+
marginR: 0,
|
|
1913
|
+
marginB: 0,
|
|
1914
|
+
frozen: false,
|
|
1915
|
+
lineIndex: 0,
|
|
1916
|
+
relativeIndex: -1,
|
|
1917
|
+
baseline: 0,
|
|
1918
|
+
lastAvailW: NaN,
|
|
1919
|
+
lastAvailH: NaN,
|
|
1920
|
+
lastOffsetX: NaN,
|
|
1921
|
+
lastOffsetY: NaN,
|
|
1922
|
+
lastAbsX: NaN,
|
|
1923
|
+
lastAbsY: NaN,
|
|
1924
|
+
layoutValid: false,
|
|
1925
|
+
lastDir: 0
|
|
1926
|
+
};
|
|
1927
|
+
_isDirty = true;
|
|
1928
|
+
_hasNewLayout = false;
|
|
1929
|
+
_lastCalcW = NaN;
|
|
1930
|
+
_lastCalcH = NaN;
|
|
1931
|
+
_lastCalcDir = 0;
|
|
1932
|
+
/**
|
|
1933
|
+
* Create a new layout node.
|
|
1934
|
+
*
|
|
1935
|
+
* @returns A new Node instance
|
|
1936
|
+
* @example
|
|
1937
|
+
* ```typescript
|
|
1938
|
+
* const root = Node.create();
|
|
1939
|
+
* root.setWidth(100);
|
|
1940
|
+
* root.setHeight(200);
|
|
1941
|
+
* ```
|
|
1942
|
+
*/
|
|
1943
|
+
static create() {
|
|
1944
|
+
return new Node();
|
|
1945
|
+
}
|
|
1946
|
+
/**
|
|
1947
|
+
* Get the number of child nodes.
|
|
1948
|
+
*
|
|
1949
|
+
* @returns The number of children
|
|
1950
|
+
*/
|
|
1951
|
+
getChildCount() {
|
|
1952
|
+
return this._children.length;
|
|
1953
|
+
}
|
|
1954
|
+
/**
|
|
1955
|
+
* Get a child node by index.
|
|
1956
|
+
*
|
|
1957
|
+
* @param index - Zero-based child index
|
|
1958
|
+
* @returns The child node at the given index, or undefined if index is out of bounds
|
|
1959
|
+
*/
|
|
1960
|
+
getChild(index) {
|
|
1961
|
+
return this._children[index];
|
|
1962
|
+
}
|
|
1963
|
+
/**
|
|
1964
|
+
* Get the parent node.
|
|
1965
|
+
*
|
|
1966
|
+
* @returns The parent node, or null if this is a root node
|
|
1967
|
+
*/
|
|
1968
|
+
getParent() {
|
|
1969
|
+
return this._parent;
|
|
1970
|
+
}
|
|
1971
|
+
/**
|
|
1972
|
+
* Insert a child node at the specified index.
|
|
1973
|
+
* If the child already has a parent, it will be removed from that parent first.
|
|
1974
|
+
* Marks the node as dirty to trigger layout recalculation.
|
|
1975
|
+
*
|
|
1976
|
+
* @param child - The child node to insert
|
|
1977
|
+
* @param index - The index at which to insert the child
|
|
1978
|
+
* @example
|
|
1979
|
+
* ```typescript
|
|
1980
|
+
* const parent = Node.create();
|
|
1981
|
+
* const child1 = Node.create();
|
|
1982
|
+
* const child2 = Node.create();
|
|
1983
|
+
* parent.insertChild(child1, 0);
|
|
1984
|
+
* parent.insertChild(child2, 1);
|
|
1985
|
+
* ```
|
|
1986
|
+
*/
|
|
1987
|
+
insertChild(child, index) {
|
|
1988
|
+
if (child === this) throw new Error("Cannot insert a node as a child of itself");
|
|
1989
|
+
let ancestor = this._parent;
|
|
1990
|
+
while (ancestor !== null) {
|
|
1991
|
+
if (ancestor === child) throw new Error("Cannot insert an ancestor as a child (would create a cycle)");
|
|
1992
|
+
ancestor = ancestor._parent;
|
|
1993
|
+
}
|
|
1994
|
+
if (child._parent !== null) child._parent.removeChild(child);
|
|
1995
|
+
child._parent = this;
|
|
1996
|
+
const clampedIndex = Math.max(0, Math.min(index, this._children.length));
|
|
1997
|
+
this._children.splice(clampedIndex, 0, child);
|
|
1998
|
+
for (let i = clampedIndex + 1; i < this._children.length; i++) this._children[i]._flex.layoutValid = false;
|
|
1999
|
+
this.markDirty();
|
|
2000
|
+
}
|
|
2001
|
+
/**
|
|
2002
|
+
* Remove a child node from this node.
|
|
2003
|
+
* The child's parent reference will be cleared.
|
|
2004
|
+
* Marks the node as dirty to trigger layout recalculation.
|
|
2005
|
+
* Invalidates layout validity of remaining siblings whose positions may change.
|
|
2006
|
+
*
|
|
2007
|
+
* @param child - The child node to remove
|
|
2008
|
+
*/
|
|
2009
|
+
removeChild(child) {
|
|
2010
|
+
const index = this._children.indexOf(child);
|
|
2011
|
+
if (index !== -1) {
|
|
2012
|
+
this._children.splice(index, 1);
|
|
2013
|
+
child._parent = null;
|
|
2014
|
+
for (let i = index; i < this._children.length; i++) this._children[i]._flex.layoutValid = false;
|
|
2015
|
+
this.markDirty();
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
/**
|
|
2019
|
+
* Free this node and clean up all references.
|
|
2020
|
+
* Removes the node from its parent, clears all children, and removes the measure function.
|
|
2021
|
+
* This does not recursively free child nodes.
|
|
2022
|
+
*/
|
|
2023
|
+
free() {
|
|
2024
|
+
if (this._parent !== null) this._parent.removeChild(this);
|
|
2025
|
+
for (const child of this._children) child._parent = null;
|
|
2026
|
+
this._children = [];
|
|
2027
|
+
this._measureFunc = null;
|
|
2028
|
+
this._baselineFunc = null;
|
|
2029
|
+
}
|
|
2030
|
+
/**
|
|
2031
|
+
* Free this node and all descendants recursively.
|
|
2032
|
+
* Each node is detached from its parent and cleaned up.
|
|
2033
|
+
* Uses iterative traversal to avoid stack overflow on deep trees.
|
|
2034
|
+
*/
|
|
2035
|
+
freeRecursive() {
|
|
2036
|
+
const nodes = [];
|
|
2037
|
+
traversalStack.length = 0;
|
|
2038
|
+
traversalStack.push(this);
|
|
2039
|
+
while (traversalStack.length > 0) {
|
|
2040
|
+
const current = traversalStack.pop();
|
|
2041
|
+
nodes.push(current);
|
|
2042
|
+
for (const child of current._children) traversalStack.push(child);
|
|
2043
|
+
}
|
|
2044
|
+
for (let i = nodes.length - 1; i >= 0; i--) nodes[i].free();
|
|
2045
|
+
}
|
|
2046
|
+
/**
|
|
2047
|
+
* Dispose the node (calls free)
|
|
2048
|
+
*/
|
|
2049
|
+
[Symbol.dispose]() {
|
|
2050
|
+
this.free();
|
|
2051
|
+
}
|
|
2052
|
+
/**
|
|
2053
|
+
* Set a measure function for intrinsic sizing.
|
|
2054
|
+
* The measure function is called during layout to determine the node's natural size.
|
|
2055
|
+
* Typically used for text nodes or other content that has an intrinsic size.
|
|
2056
|
+
* Marks the node as dirty to trigger layout recalculation.
|
|
2057
|
+
*
|
|
2058
|
+
* @param measureFunc - Function that returns width and height given available space and constraints
|
|
2059
|
+
* @example
|
|
2060
|
+
* ```typescript
|
|
2061
|
+
* const textNode = Node.create();
|
|
2062
|
+
* textNode.setMeasureFunc((width, widthMode, height, heightMode) => {
|
|
2063
|
+
* // Measure text and return dimensions
|
|
2064
|
+
* return { width: 50, height: 20 };
|
|
2065
|
+
* });
|
|
2066
|
+
* ```
|
|
2067
|
+
*/
|
|
2068
|
+
setMeasureFunc(measureFunc) {
|
|
2069
|
+
this._measureFunc = measureFunc;
|
|
2070
|
+
this.markDirty();
|
|
2071
|
+
}
|
|
2072
|
+
/**
|
|
2073
|
+
* Remove the measure function from this node.
|
|
2074
|
+
* Marks the node as dirty to trigger layout recalculation.
|
|
2075
|
+
*/
|
|
2076
|
+
unsetMeasureFunc() {
|
|
2077
|
+
this._measureFunc = null;
|
|
2078
|
+
this.markDirty();
|
|
2079
|
+
}
|
|
2080
|
+
/**
|
|
2081
|
+
* Check if this node has a measure function.
|
|
2082
|
+
*
|
|
2083
|
+
* @returns True if a measure function is set
|
|
2084
|
+
*/
|
|
2085
|
+
hasMeasureFunc() {
|
|
2086
|
+
return this._measureFunc !== null;
|
|
2087
|
+
}
|
|
2088
|
+
/**
|
|
2089
|
+
* Set a baseline function to determine where this node's text baseline is.
|
|
2090
|
+
* Used for ALIGN_BASELINE to align text across siblings with different heights.
|
|
2091
|
+
*
|
|
2092
|
+
* @param baselineFunc - Function that returns baseline offset from top given width and height
|
|
2093
|
+
* @example
|
|
2094
|
+
* ```typescript
|
|
2095
|
+
* textNode.setBaselineFunc((width, height) => {
|
|
2096
|
+
* // For a text node, baseline might be at 80% of height
|
|
2097
|
+
* return height * 0.8;
|
|
2098
|
+
* });
|
|
2099
|
+
* ```
|
|
2100
|
+
*/
|
|
2101
|
+
setBaselineFunc(baselineFunc) {
|
|
2102
|
+
this._baselineFunc = baselineFunc;
|
|
2103
|
+
this.markDirty();
|
|
2104
|
+
}
|
|
2105
|
+
/**
|
|
2106
|
+
* Remove the baseline function from this node.
|
|
2107
|
+
* Marks the node as dirty to trigger layout recalculation.
|
|
2108
|
+
*/
|
|
2109
|
+
unsetBaselineFunc() {
|
|
2110
|
+
this._baselineFunc = null;
|
|
2111
|
+
this.markDirty();
|
|
2112
|
+
}
|
|
2113
|
+
/**
|
|
2114
|
+
* Check if this node has a baseline function.
|
|
2115
|
+
*
|
|
2116
|
+
* @returns True if a baseline function is set
|
|
2117
|
+
*/
|
|
2118
|
+
hasBaselineFunc() {
|
|
2119
|
+
return this._baselineFunc !== null;
|
|
2120
|
+
}
|
|
2121
|
+
/**
|
|
2122
|
+
* Call the measure function with caching.
|
|
2123
|
+
* Uses a 4-entry numeric cache for fast lookup without allocations.
|
|
2124
|
+
* Cache is cleared when markDirty() is called.
|
|
2125
|
+
*
|
|
2126
|
+
* @returns Measured dimensions or null if no measure function
|
|
2127
|
+
*/
|
|
2128
|
+
cachedMeasure(w, wm, h, hm) {
|
|
2129
|
+
if (!this._measureFunc) return null;
|
|
2130
|
+
Node.measureCalls++;
|
|
2131
|
+
const m0 = this._m0;
|
|
2132
|
+
if (m0 && m0.w === w && m0.wm === wm && m0.h === h && m0.hm === hm) {
|
|
2133
|
+
Node.measureCacheHits++;
|
|
2134
|
+
this._measureResult.width = m0.rw;
|
|
2135
|
+
this._measureResult.height = m0.rh;
|
|
2136
|
+
getTrace()?.measureCacheHit(0, w, h, m0.rw, m0.rh);
|
|
2137
|
+
return this._measureResult;
|
|
2138
|
+
}
|
|
2139
|
+
const m1 = this._m1;
|
|
2140
|
+
if (m1 && m1.w === w && m1.wm === wm && m1.h === h && m1.hm === hm) {
|
|
2141
|
+
Node.measureCacheHits++;
|
|
2142
|
+
this._measureResult.width = m1.rw;
|
|
2143
|
+
this._measureResult.height = m1.rh;
|
|
2144
|
+
getTrace()?.measureCacheHit(0, w, h, m1.rw, m1.rh);
|
|
2145
|
+
return this._measureResult;
|
|
2146
|
+
}
|
|
2147
|
+
const m2 = this._m2;
|
|
2148
|
+
if (m2 && m2.w === w && m2.wm === wm && m2.h === h && m2.hm === hm) {
|
|
2149
|
+
Node.measureCacheHits++;
|
|
2150
|
+
this._measureResult.width = m2.rw;
|
|
2151
|
+
this._measureResult.height = m2.rh;
|
|
2152
|
+
getTrace()?.measureCacheHit(0, w, h, m2.rw, m2.rh);
|
|
2153
|
+
return this._measureResult;
|
|
2154
|
+
}
|
|
2155
|
+
const m3 = this._m3;
|
|
2156
|
+
if (m3 && m3.w === w && m3.wm === wm && m3.h === h && m3.hm === hm) {
|
|
2157
|
+
Node.measureCacheHits++;
|
|
2158
|
+
this._measureResult.width = m3.rw;
|
|
2159
|
+
this._measureResult.height = m3.rh;
|
|
2160
|
+
getTrace()?.measureCacheHit(0, w, h, m3.rw, m3.rh);
|
|
2161
|
+
return this._measureResult;
|
|
2162
|
+
}
|
|
2163
|
+
getTrace()?.measureCacheMiss(0, w, h);
|
|
2164
|
+
const result = this._measureFunc(w, wm, h, hm);
|
|
2165
|
+
if (this._m2) {
|
|
2166
|
+
if (!this._m3) this._m3 = {
|
|
2167
|
+
w: 0,
|
|
2168
|
+
wm: 0,
|
|
2169
|
+
h: 0,
|
|
2170
|
+
hm: 0,
|
|
2171
|
+
rw: 0,
|
|
2172
|
+
rh: 0
|
|
2173
|
+
};
|
|
2174
|
+
this._m3.w = this._m2.w;
|
|
2175
|
+
this._m3.wm = this._m2.wm;
|
|
2176
|
+
this._m3.h = this._m2.h;
|
|
2177
|
+
this._m3.hm = this._m2.hm;
|
|
2178
|
+
this._m3.rw = this._m2.rw;
|
|
2179
|
+
this._m3.rh = this._m2.rh;
|
|
2180
|
+
}
|
|
2181
|
+
if (this._m1) {
|
|
2182
|
+
if (!this._m2) this._m2 = {
|
|
2183
|
+
w: 0,
|
|
2184
|
+
wm: 0,
|
|
2185
|
+
h: 0,
|
|
2186
|
+
hm: 0,
|
|
2187
|
+
rw: 0,
|
|
2188
|
+
rh: 0
|
|
2189
|
+
};
|
|
2190
|
+
this._m2.w = this._m1.w;
|
|
2191
|
+
this._m2.wm = this._m1.wm;
|
|
2192
|
+
this._m2.h = this._m1.h;
|
|
2193
|
+
this._m2.hm = this._m1.hm;
|
|
2194
|
+
this._m2.rw = this._m1.rw;
|
|
2195
|
+
this._m2.rh = this._m1.rh;
|
|
2196
|
+
}
|
|
2197
|
+
if (this._m0) {
|
|
2198
|
+
if (!this._m1) this._m1 = {
|
|
2199
|
+
w: 0,
|
|
2200
|
+
wm: 0,
|
|
2201
|
+
h: 0,
|
|
2202
|
+
hm: 0,
|
|
2203
|
+
rw: 0,
|
|
2204
|
+
rh: 0
|
|
2205
|
+
};
|
|
2206
|
+
this._m1.w = this._m0.w;
|
|
2207
|
+
this._m1.wm = this._m0.wm;
|
|
2208
|
+
this._m1.h = this._m0.h;
|
|
2209
|
+
this._m1.hm = this._m0.hm;
|
|
2210
|
+
this._m1.rw = this._m0.rw;
|
|
2211
|
+
this._m1.rh = this._m0.rh;
|
|
2212
|
+
}
|
|
2213
|
+
if (!this._m0) this._m0 = {
|
|
2214
|
+
w: 0,
|
|
2215
|
+
wm: 0,
|
|
2216
|
+
h: 0,
|
|
2217
|
+
hm: 0,
|
|
2218
|
+
rw: 0,
|
|
2219
|
+
rh: 0
|
|
2220
|
+
};
|
|
2221
|
+
this._m0.w = w;
|
|
2222
|
+
this._m0.wm = wm;
|
|
2223
|
+
this._m0.h = h;
|
|
2224
|
+
this._m0.hm = hm;
|
|
2225
|
+
this._m0.rw = result.width;
|
|
2226
|
+
this._m0.rh = result.height;
|
|
2227
|
+
this._measureResult.width = result.width;
|
|
2228
|
+
this._measureResult.height = result.height;
|
|
2229
|
+
return this._measureResult;
|
|
2230
|
+
}
|
|
2231
|
+
/**
|
|
2232
|
+
* Check layout cache for a previously computed size with same available dimensions.
|
|
2233
|
+
* Returns cached (width, height) or null if not found.
|
|
2234
|
+
*
|
|
2235
|
+
* NaN dimensions are handled specially via Object.is (NaN === NaN is false, but Object.is(NaN, NaN) is true).
|
|
2236
|
+
*/
|
|
2237
|
+
getCachedLayout(availW, availH) {
|
|
2238
|
+
if (this._isDirty) return null;
|
|
2239
|
+
const lc0 = this._lc0;
|
|
2240
|
+
if (lc0 && Object.is(lc0.availW, availW) && Object.is(lc0.availH, availH)) {
|
|
2241
|
+
this._layoutResult.width = lc0.computedW;
|
|
2242
|
+
this._layoutResult.height = lc0.computedH;
|
|
2243
|
+
return this._layoutResult;
|
|
2244
|
+
}
|
|
2245
|
+
const lc1 = this._lc1;
|
|
2246
|
+
if (lc1 && Object.is(lc1.availW, availW) && Object.is(lc1.availH, availH)) {
|
|
2247
|
+
this._layoutResult.width = lc1.computedW;
|
|
2248
|
+
this._layoutResult.height = lc1.computedH;
|
|
2249
|
+
return this._layoutResult;
|
|
2250
|
+
}
|
|
2251
|
+
return null;
|
|
2252
|
+
}
|
|
2253
|
+
/**
|
|
2254
|
+
* Cache a computed layout result for the given available dimensions.
|
|
2255
|
+
* Zero-allocation: lazily allocates cache entries once, then reuses.
|
|
2256
|
+
*/
|
|
2257
|
+
setCachedLayout(availW, availH, computedW, computedH) {
|
|
2258
|
+
if (this._lc0) {
|
|
2259
|
+
if (!this._lc1) this._lc1 = {
|
|
2260
|
+
availW: NaN,
|
|
2261
|
+
availH: NaN,
|
|
2262
|
+
computedW: 0,
|
|
2263
|
+
computedH: 0
|
|
2264
|
+
};
|
|
2265
|
+
this._lc1.availW = this._lc0.availW;
|
|
2266
|
+
this._lc1.availH = this._lc0.availH;
|
|
2267
|
+
this._lc1.computedW = this._lc0.computedW;
|
|
2268
|
+
this._lc1.computedH = this._lc0.computedH;
|
|
2269
|
+
}
|
|
2270
|
+
if (!this._lc0) this._lc0 = {
|
|
2271
|
+
availW: 0,
|
|
2272
|
+
availH: 0,
|
|
2273
|
+
computedW: 0,
|
|
2274
|
+
computedH: 0
|
|
2275
|
+
};
|
|
2276
|
+
this._lc0.availW = availW;
|
|
2277
|
+
this._lc0.availH = availH;
|
|
2278
|
+
this._lc0.computedW = computedW;
|
|
2279
|
+
this._lc0.computedH = computedH;
|
|
2280
|
+
}
|
|
2281
|
+
/**
|
|
2282
|
+
* Clear layout cache for this node and all descendants.
|
|
2283
|
+
* Called at the start of each calculateLayout pass.
|
|
2284
|
+
* Zero-allocation: invalidates entries (availW = NaN) rather than deallocating.
|
|
2285
|
+
* Uses iterative traversal to avoid stack overflow on deep trees.
|
|
2286
|
+
*/
|
|
2287
|
+
resetLayoutCache() {
|
|
2288
|
+
traversalStack.length = 0;
|
|
2289
|
+
traversalStack.push(this);
|
|
2290
|
+
while (traversalStack.length > 0) {
|
|
2291
|
+
const node = traversalStack.pop();
|
|
2292
|
+
if (node._lc0) node._lc0.availW = -1;
|
|
2293
|
+
if (node._lc1) node._lc1.availW = -1;
|
|
2294
|
+
for (const child of node._children) traversalStack.push(child);
|
|
2295
|
+
}
|
|
2296
|
+
}
|
|
2297
|
+
/**
|
|
2298
|
+
* Check if this node needs layout recalculation.
|
|
2299
|
+
*
|
|
2300
|
+
* @returns True if the node is dirty and needs layout
|
|
2301
|
+
*/
|
|
2302
|
+
isDirty() {
|
|
2303
|
+
return this._isDirty;
|
|
2304
|
+
}
|
|
2305
|
+
/**
|
|
2306
|
+
* Mark this node and all ancestors as dirty.
|
|
2307
|
+
* A dirty node needs layout recalculation.
|
|
2308
|
+
* This is automatically called by all style setters and tree operations.
|
|
2309
|
+
* Uses iterative approach to avoid stack overflow on deep trees.
|
|
2310
|
+
*/
|
|
2311
|
+
markDirty() {
|
|
2312
|
+
let current = this;
|
|
2313
|
+
while (current !== null) {
|
|
2314
|
+
current._m0 = current._m1 = current._m2 = current._m3 = void 0;
|
|
2315
|
+
current._lc0 = current._lc1 = void 0;
|
|
2316
|
+
if (current._isDirty) break;
|
|
2317
|
+
current._isDirty = true;
|
|
2318
|
+
current._flex.layoutValid = false;
|
|
2319
|
+
current = current._parent;
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
/**
|
|
2323
|
+
* Check if this node has new layout results since the last check.
|
|
2324
|
+
*
|
|
2325
|
+
* @returns True if layout was recalculated since the last call to markLayoutSeen
|
|
2326
|
+
*/
|
|
2327
|
+
hasNewLayout() {
|
|
2328
|
+
return this._hasNewLayout;
|
|
2329
|
+
}
|
|
2330
|
+
/**
|
|
2331
|
+
* Mark that the current layout has been seen/processed.
|
|
2332
|
+
* Clears the hasNewLayout flag.
|
|
2333
|
+
*/
|
|
2334
|
+
markLayoutSeen() {
|
|
2335
|
+
this._hasNewLayout = false;
|
|
2336
|
+
}
|
|
2337
|
+
/**
|
|
2338
|
+
* Calculate layout for this node and all descendants.
|
|
2339
|
+
* This runs the flexbox layout algorithm to compute positions and sizes.
|
|
2340
|
+
* Only recalculates if the node is marked as dirty.
|
|
2341
|
+
*
|
|
2342
|
+
* @param width - Available width for layout
|
|
2343
|
+
* @param height - Available height for layout
|
|
2344
|
+
* @param _direction - Text direction (LTR or RTL), defaults to LTR
|
|
2345
|
+
* @example
|
|
2346
|
+
* ```typescript
|
|
2347
|
+
* const root = Node.create();
|
|
2348
|
+
* root.setFlexDirection(FLEX_DIRECTION_ROW);
|
|
2349
|
+
* root.setWidth(100);
|
|
2350
|
+
* root.setHeight(50);
|
|
2351
|
+
*
|
|
2352
|
+
* const child = Node.create();
|
|
2353
|
+
* child.setFlexGrow(1);
|
|
2354
|
+
* root.insertChild(child, 0);
|
|
2355
|
+
*
|
|
2356
|
+
* root.calculateLayout(100, 50, DIRECTION_LTR);
|
|
2357
|
+
*
|
|
2358
|
+
* // Now you can read computed layout
|
|
2359
|
+
* console.log(child.getComputedWidth());
|
|
2360
|
+
* ```
|
|
2361
|
+
*/
|
|
2362
|
+
calculateLayout(width, height, direction = 1) {
|
|
2363
|
+
const availableWidth = width ?? NaN;
|
|
2364
|
+
const availableHeight = height ?? NaN;
|
|
2365
|
+
if (!this._isDirty && Object.is(this._lastCalcW, availableWidth) && Object.is(this._lastCalcH, availableHeight) && this._lastCalcDir === direction) {
|
|
2366
|
+
log.debug?.("layout skip (not dirty, constraints unchanged)");
|
|
2367
|
+
return;
|
|
2368
|
+
}
|
|
2369
|
+
this._lastCalcW = availableWidth;
|
|
2370
|
+
this._lastCalcH = availableHeight;
|
|
2371
|
+
this._lastCalcDir = direction;
|
|
2372
|
+
const start = log.debug ? Date.now() : 0;
|
|
2373
|
+
const nodeCount = log.debug ? countNodes(this) : 0;
|
|
2374
|
+
Node.resetMeasureStats();
|
|
2375
|
+
computeLayout(this, availableWidth, availableHeight, direction);
|
|
2376
|
+
this._isDirty = false;
|
|
2377
|
+
this._hasNewLayout = true;
|
|
2378
|
+
markSubtreeLayoutSeen(this);
|
|
2379
|
+
log.debug?.("layout: %dx%d, %d nodes in %dms (measure: calls=%d hits=%d)", width, height, nodeCount, Date.now() - start, Node.measureCalls, Node.measureCacheHits);
|
|
2380
|
+
}
|
|
2381
|
+
/**
|
|
2382
|
+
* Get the computed left position after layout.
|
|
2383
|
+
*
|
|
2384
|
+
* @returns The left position in points
|
|
2385
|
+
*/
|
|
2386
|
+
getComputedLeft() {
|
|
2387
|
+
return this._layout.left;
|
|
2388
|
+
}
|
|
2389
|
+
/**
|
|
2390
|
+
* Get the computed top position after layout.
|
|
2391
|
+
*
|
|
2392
|
+
* @returns The top position in points
|
|
2393
|
+
*/
|
|
2394
|
+
getComputedTop() {
|
|
2395
|
+
return this._layout.top;
|
|
2396
|
+
}
|
|
2397
|
+
/**
|
|
2398
|
+
* Get the computed width after layout.
|
|
2399
|
+
*
|
|
2400
|
+
* @returns The width in points
|
|
2401
|
+
*/
|
|
2402
|
+
getComputedWidth() {
|
|
2403
|
+
return this._layout.width;
|
|
2404
|
+
}
|
|
2405
|
+
/**
|
|
2406
|
+
* Get the computed height after layout.
|
|
2407
|
+
*
|
|
2408
|
+
* @returns The height in points
|
|
2409
|
+
*/
|
|
2410
|
+
getComputedHeight() {
|
|
2411
|
+
return this._layout.height;
|
|
2412
|
+
}
|
|
2413
|
+
/**
|
|
2414
|
+
* Get the computed right edge position after layout (left + width).
|
|
2415
|
+
*
|
|
2416
|
+
* @returns The right edge position in points
|
|
2417
|
+
*/
|
|
2418
|
+
getComputedRight() {
|
|
2419
|
+
return this._layout.left + this._layout.width;
|
|
2420
|
+
}
|
|
2421
|
+
/**
|
|
2422
|
+
* Get the computed bottom edge position after layout (top + height).
|
|
2423
|
+
*
|
|
2424
|
+
* @returns The bottom edge position in points
|
|
2425
|
+
*/
|
|
2426
|
+
getComputedBottom() {
|
|
2427
|
+
return this._layout.top + this._layout.height;
|
|
2428
|
+
}
|
|
2429
|
+
/**
|
|
2430
|
+
* Get the computed padding for a specific edge after layout.
|
|
2431
|
+
* Returns the resolved padding value (percentage and logical edges resolved).
|
|
2432
|
+
*
|
|
2433
|
+
* @param edge - EDGE_LEFT, EDGE_TOP, EDGE_RIGHT, or EDGE_BOTTOM
|
|
2434
|
+
* @returns Padding value in points
|
|
2435
|
+
*/
|
|
2436
|
+
getComputedPadding(edge) {
|
|
2437
|
+
return getEdgeValue(this._style.padding, edge).value;
|
|
2438
|
+
}
|
|
2439
|
+
/**
|
|
2440
|
+
* Get the computed margin for a specific edge after layout.
|
|
2441
|
+
* Returns the resolved margin value (percentage and logical edges resolved).
|
|
2442
|
+
*
|
|
2443
|
+
* @param edge - EDGE_LEFT, EDGE_TOP, EDGE_RIGHT, or EDGE_BOTTOM
|
|
2444
|
+
* @returns Margin value in points
|
|
2445
|
+
*/
|
|
2446
|
+
getComputedMargin(edge) {
|
|
2447
|
+
return getEdgeValue(this._style.margin, edge).value;
|
|
2448
|
+
}
|
|
2449
|
+
/**
|
|
2450
|
+
* Get the computed border width for a specific edge after layout.
|
|
2451
|
+
*
|
|
2452
|
+
* @param edge - EDGE_LEFT, EDGE_TOP, EDGE_RIGHT, or EDGE_BOTTOM
|
|
2453
|
+
* @returns Border width in points
|
|
2454
|
+
*/
|
|
2455
|
+
getComputedBorder(edge) {
|
|
2456
|
+
return getEdgeBorderValue(this._style.border, edge);
|
|
2457
|
+
}
|
|
2458
|
+
get children() {
|
|
2459
|
+
return this._children;
|
|
2460
|
+
}
|
|
2461
|
+
get style() {
|
|
2462
|
+
return this._style;
|
|
2463
|
+
}
|
|
2464
|
+
get layout() {
|
|
2465
|
+
return this._layout;
|
|
2466
|
+
}
|
|
2467
|
+
get measureFunc() {
|
|
2468
|
+
return this._measureFunc;
|
|
2469
|
+
}
|
|
2470
|
+
get baselineFunc() {
|
|
2471
|
+
return this._baselineFunc;
|
|
2472
|
+
}
|
|
2473
|
+
get flex() {
|
|
2474
|
+
return this._flex;
|
|
2475
|
+
}
|
|
2476
|
+
/**
|
|
2477
|
+
* Set the width to a fixed value in points.
|
|
2478
|
+
*
|
|
2479
|
+
* @param value - Width in points
|
|
2480
|
+
*/
|
|
2481
|
+
setWidth(value) {
|
|
2482
|
+
if (Number.isNaN(value)) this._style.width = {
|
|
2483
|
+
value: 0,
|
|
2484
|
+
unit: 3
|
|
2485
|
+
};
|
|
2486
|
+
else this._style.width = {
|
|
2487
|
+
value,
|
|
2488
|
+
unit: 1
|
|
2489
|
+
};
|
|
2490
|
+
this.markDirty();
|
|
2491
|
+
}
|
|
2492
|
+
/**
|
|
2493
|
+
* Set the width as a percentage of the parent's width.
|
|
2494
|
+
*
|
|
2495
|
+
* @param value - Width as a percentage (0-100)
|
|
2496
|
+
*/
|
|
2497
|
+
setWidthPercent(value) {
|
|
2498
|
+
this._style.width = {
|
|
2499
|
+
value,
|
|
2500
|
+
unit: 2
|
|
2501
|
+
};
|
|
2502
|
+
this.markDirty();
|
|
2503
|
+
}
|
|
2504
|
+
/**
|
|
2505
|
+
* Set the width to auto (determined by layout algorithm).
|
|
2506
|
+
*/
|
|
2507
|
+
setWidthAuto() {
|
|
2508
|
+
this._style.width = {
|
|
2509
|
+
value: 0,
|
|
2510
|
+
unit: 3
|
|
2511
|
+
};
|
|
2512
|
+
this.markDirty();
|
|
2513
|
+
}
|
|
2514
|
+
/**
|
|
2515
|
+
* Set the height to a fixed value in points.
|
|
2516
|
+
*
|
|
2517
|
+
* @param value - Height in points
|
|
2518
|
+
*/
|
|
2519
|
+
setHeight(value) {
|
|
2520
|
+
if (Number.isNaN(value)) this._style.height = {
|
|
2521
|
+
value: 0,
|
|
2522
|
+
unit: 3
|
|
2523
|
+
};
|
|
2524
|
+
else this._style.height = {
|
|
2525
|
+
value,
|
|
2526
|
+
unit: 1
|
|
2527
|
+
};
|
|
2528
|
+
this.markDirty();
|
|
2529
|
+
}
|
|
2530
|
+
/**
|
|
2531
|
+
* Set the height as a percentage of the parent's height.
|
|
2532
|
+
*
|
|
2533
|
+
* @param value - Height as a percentage (0-100)
|
|
2534
|
+
*/
|
|
2535
|
+
setHeightPercent(value) {
|
|
2536
|
+
this._style.height = {
|
|
2537
|
+
value,
|
|
2538
|
+
unit: 2
|
|
2539
|
+
};
|
|
2540
|
+
this.markDirty();
|
|
2541
|
+
}
|
|
2542
|
+
/**
|
|
2543
|
+
* Set the height to auto (determined by layout algorithm).
|
|
2544
|
+
*/
|
|
2545
|
+
setHeightAuto() {
|
|
2546
|
+
this._style.height = {
|
|
2547
|
+
value: 0,
|
|
2548
|
+
unit: 3
|
|
2549
|
+
};
|
|
2550
|
+
this.markDirty();
|
|
2551
|
+
}
|
|
2552
|
+
/**
|
|
2553
|
+
* Set the minimum width in points.
|
|
2554
|
+
*
|
|
2555
|
+
* @param value - Minimum width in points
|
|
2556
|
+
*/
|
|
2557
|
+
setMinWidth(value) {
|
|
2558
|
+
this._style.minWidth = {
|
|
2559
|
+
value,
|
|
2560
|
+
unit: 1
|
|
2561
|
+
};
|
|
2562
|
+
this.markDirty();
|
|
2563
|
+
}
|
|
2564
|
+
/**
|
|
2565
|
+
* Set the minimum width as a percentage of the parent's width.
|
|
2566
|
+
*
|
|
2567
|
+
* @param value - Minimum width as a percentage (0-100)
|
|
2568
|
+
*/
|
|
2569
|
+
setMinWidthPercent(value) {
|
|
2570
|
+
this._style.minWidth = {
|
|
2571
|
+
value,
|
|
2572
|
+
unit: 2
|
|
2573
|
+
};
|
|
2574
|
+
this.markDirty();
|
|
2575
|
+
}
|
|
2576
|
+
/**
|
|
2577
|
+
* Set the minimum height in points.
|
|
2578
|
+
*
|
|
2579
|
+
* @param value - Minimum height in points
|
|
2580
|
+
*/
|
|
2581
|
+
setMinHeight(value) {
|
|
2582
|
+
this._style.minHeight = {
|
|
2583
|
+
value,
|
|
2584
|
+
unit: 1
|
|
2585
|
+
};
|
|
2586
|
+
this.markDirty();
|
|
2587
|
+
}
|
|
2588
|
+
/**
|
|
2589
|
+
* Set the minimum height as a percentage of the parent's height.
|
|
2590
|
+
*
|
|
2591
|
+
* @param value - Minimum height as a percentage (0-100)
|
|
2592
|
+
*/
|
|
2593
|
+
setMinHeightPercent(value) {
|
|
2594
|
+
this._style.minHeight = {
|
|
2595
|
+
value,
|
|
2596
|
+
unit: 2
|
|
2597
|
+
};
|
|
2598
|
+
this.markDirty();
|
|
2599
|
+
}
|
|
2600
|
+
/**
|
|
2601
|
+
* Set the maximum width in points.
|
|
2602
|
+
*
|
|
2603
|
+
* @param value - Maximum width in points
|
|
2604
|
+
*/
|
|
2605
|
+
setMaxWidth(value) {
|
|
2606
|
+
this._style.maxWidth = {
|
|
2607
|
+
value,
|
|
2608
|
+
unit: 1
|
|
2609
|
+
};
|
|
2610
|
+
this.markDirty();
|
|
2611
|
+
}
|
|
2612
|
+
/**
|
|
2613
|
+
* Set the maximum width as a percentage of the parent's width.
|
|
2614
|
+
*
|
|
2615
|
+
* @param value - Maximum width as a percentage (0-100)
|
|
2616
|
+
*/
|
|
2617
|
+
setMaxWidthPercent(value) {
|
|
2618
|
+
this._style.maxWidth = {
|
|
2619
|
+
value,
|
|
2620
|
+
unit: 2
|
|
2621
|
+
};
|
|
2622
|
+
this.markDirty();
|
|
2623
|
+
}
|
|
2624
|
+
/**
|
|
2625
|
+
* Set the maximum height in points.
|
|
2626
|
+
*
|
|
2627
|
+
* @param value - Maximum height in points
|
|
2628
|
+
*/
|
|
2629
|
+
setMaxHeight(value) {
|
|
2630
|
+
this._style.maxHeight = {
|
|
2631
|
+
value,
|
|
2632
|
+
unit: 1
|
|
2633
|
+
};
|
|
2634
|
+
this.markDirty();
|
|
2635
|
+
}
|
|
2636
|
+
/**
|
|
2637
|
+
* Set the maximum height as a percentage of the parent's height.
|
|
2638
|
+
*
|
|
2639
|
+
* @param value - Maximum height as a percentage (0-100)
|
|
2640
|
+
*/
|
|
2641
|
+
setMaxHeightPercent(value) {
|
|
2642
|
+
this._style.maxHeight = {
|
|
2643
|
+
value,
|
|
2644
|
+
unit: 2
|
|
2645
|
+
};
|
|
2646
|
+
this.markDirty();
|
|
2647
|
+
}
|
|
2648
|
+
/**
|
|
2649
|
+
* Set the aspect ratio of the node.
|
|
2650
|
+
* When set, the node's width/height relationship is constrained.
|
|
2651
|
+
* If width is defined, height = width / aspectRatio.
|
|
2652
|
+
* If height is defined, width = height * aspectRatio.
|
|
2653
|
+
*
|
|
2654
|
+
* @param value - Aspect ratio (width/height). Use NaN to unset.
|
|
2655
|
+
*/
|
|
2656
|
+
setAspectRatio(value) {
|
|
2657
|
+
this._style.aspectRatio = value;
|
|
2658
|
+
this.markDirty();
|
|
2659
|
+
}
|
|
2660
|
+
/**
|
|
2661
|
+
* Set the flex grow factor.
|
|
2662
|
+
* Determines how much the node will grow relative to siblings when there is extra space.
|
|
2663
|
+
*
|
|
2664
|
+
* @param value - Flex grow factor (typically 0 or 1+)
|
|
2665
|
+
* @example
|
|
2666
|
+
* ```typescript
|
|
2667
|
+
* const child = Node.create();
|
|
2668
|
+
* child.setFlexGrow(1); // Will grow to fill available space
|
|
2669
|
+
* ```
|
|
2670
|
+
*/
|
|
2671
|
+
setFlexGrow(value) {
|
|
2672
|
+
this._style.flexGrow = value;
|
|
2673
|
+
this.markDirty();
|
|
2674
|
+
}
|
|
2675
|
+
/**
|
|
2676
|
+
* Set the flex shrink factor.
|
|
2677
|
+
* Determines how much the node will shrink relative to siblings when there is insufficient space.
|
|
2678
|
+
*
|
|
2679
|
+
* @param value - Flex shrink factor (default is 1)
|
|
2680
|
+
*/
|
|
2681
|
+
setFlexShrink(value) {
|
|
2682
|
+
this._style.flexShrink = value;
|
|
2683
|
+
this.markDirty();
|
|
2684
|
+
}
|
|
2685
|
+
/**
|
|
2686
|
+
* Set the flex basis to a fixed value in points.
|
|
2687
|
+
* The initial size of the node before flex grow/shrink is applied.
|
|
2688
|
+
*
|
|
2689
|
+
* @param value - Flex basis in points
|
|
2690
|
+
*/
|
|
2691
|
+
setFlexBasis(value) {
|
|
2692
|
+
this._style.flexBasis = {
|
|
2693
|
+
value,
|
|
2694
|
+
unit: 1
|
|
2695
|
+
};
|
|
2696
|
+
this.markDirty();
|
|
2697
|
+
}
|
|
2698
|
+
/**
|
|
2699
|
+
* Set the flex basis as a percentage of the parent's size.
|
|
2700
|
+
*
|
|
2701
|
+
* @param value - Flex basis as a percentage (0-100)
|
|
2702
|
+
*/
|
|
2703
|
+
setFlexBasisPercent(value) {
|
|
2704
|
+
this._style.flexBasis = {
|
|
2705
|
+
value,
|
|
2706
|
+
unit: 2
|
|
2707
|
+
};
|
|
2708
|
+
this.markDirty();
|
|
2709
|
+
}
|
|
2710
|
+
/**
|
|
2711
|
+
* Set the flex basis to auto (based on the node's width/height).
|
|
2712
|
+
*/
|
|
2713
|
+
setFlexBasisAuto() {
|
|
2714
|
+
this._style.flexBasis = {
|
|
2715
|
+
value: 0,
|
|
2716
|
+
unit: 3
|
|
2717
|
+
};
|
|
2718
|
+
this.markDirty();
|
|
2719
|
+
}
|
|
2720
|
+
/**
|
|
2721
|
+
* Set the flex direction (main axis direction).
|
|
2722
|
+
*
|
|
2723
|
+
* @param direction - FLEX_DIRECTION_ROW, FLEX_DIRECTION_COLUMN, FLEX_DIRECTION_ROW_REVERSE, or FLEX_DIRECTION_COLUMN_REVERSE
|
|
2724
|
+
* @example
|
|
2725
|
+
* ```typescript
|
|
2726
|
+
* const container = Node.create();
|
|
2727
|
+
* container.setFlexDirection(FLEX_DIRECTION_ROW); // Lay out children horizontally
|
|
2728
|
+
* ```
|
|
2729
|
+
*/
|
|
2730
|
+
setFlexDirection(direction) {
|
|
2731
|
+
this._style.flexDirection = direction;
|
|
2732
|
+
this.markDirty();
|
|
2733
|
+
}
|
|
2734
|
+
/**
|
|
2735
|
+
* Set the flex wrap behavior.
|
|
2736
|
+
*
|
|
2737
|
+
* @param wrap - WRAP_NO_WRAP, WRAP_WRAP, or WRAP_WRAP_REVERSE
|
|
2738
|
+
*/
|
|
2739
|
+
setFlexWrap(wrap) {
|
|
2740
|
+
this._style.flexWrap = wrap;
|
|
2741
|
+
this.markDirty();
|
|
2742
|
+
}
|
|
2743
|
+
/**
|
|
2744
|
+
* Set how children are aligned along the cross axis.
|
|
2745
|
+
*
|
|
2746
|
+
* @param align - ALIGN_FLEX_START, ALIGN_CENTER, ALIGN_FLEX_END, ALIGN_STRETCH, or ALIGN_BASELINE
|
|
2747
|
+
* @example
|
|
2748
|
+
* ```typescript
|
|
2749
|
+
* const container = Node.create();
|
|
2750
|
+
* container.setFlexDirection(FLEX_DIRECTION_ROW);
|
|
2751
|
+
* container.setAlignItems(ALIGN_CENTER); // Center children vertically
|
|
2752
|
+
* ```
|
|
2753
|
+
*/
|
|
2754
|
+
setAlignItems(align) {
|
|
2755
|
+
this._style.alignItems = align;
|
|
2756
|
+
this.markDirty();
|
|
2757
|
+
}
|
|
2758
|
+
/**
|
|
2759
|
+
* Set how this node is aligned along the parent's cross axis.
|
|
2760
|
+
* Overrides the parent's alignItems for this specific child.
|
|
2761
|
+
*
|
|
2762
|
+
* @param align - ALIGN_AUTO, ALIGN_FLEX_START, ALIGN_CENTER, ALIGN_FLEX_END, ALIGN_STRETCH, or ALIGN_BASELINE
|
|
2763
|
+
*/
|
|
2764
|
+
setAlignSelf(align) {
|
|
2765
|
+
this._style.alignSelf = align;
|
|
2766
|
+
this.markDirty();
|
|
2767
|
+
}
|
|
2768
|
+
/**
|
|
2769
|
+
* Set how lines are aligned in a multi-line flex container.
|
|
2770
|
+
* Only affects containers with wrap enabled and multiple lines.
|
|
2771
|
+
*
|
|
2772
|
+
* @param align - ALIGN_FLEX_START, ALIGN_CENTER, ALIGN_FLEX_END, ALIGN_STRETCH, ALIGN_SPACE_BETWEEN, or ALIGN_SPACE_AROUND
|
|
2773
|
+
*/
|
|
2774
|
+
setAlignContent(align) {
|
|
2775
|
+
this._style.alignContent = align;
|
|
2776
|
+
this.markDirty();
|
|
2777
|
+
}
|
|
2778
|
+
/**
|
|
2779
|
+
* Set how children are distributed along the main axis.
|
|
2780
|
+
*
|
|
2781
|
+
* @param justify - JUSTIFY_FLEX_START, JUSTIFY_CENTER, JUSTIFY_FLEX_END, JUSTIFY_SPACE_BETWEEN, JUSTIFY_SPACE_AROUND, or JUSTIFY_SPACE_EVENLY
|
|
2782
|
+
* @example
|
|
2783
|
+
* ```typescript
|
|
2784
|
+
* const container = Node.create();
|
|
2785
|
+
* container.setJustifyContent(JUSTIFY_SPACE_BETWEEN); // Space children evenly with edges at start/end
|
|
2786
|
+
* ```
|
|
2787
|
+
*/
|
|
2788
|
+
setJustifyContent(justify) {
|
|
2789
|
+
this._style.justifyContent = justify;
|
|
2790
|
+
this.markDirty();
|
|
2791
|
+
}
|
|
2792
|
+
/**
|
|
2793
|
+
* Set padding for one or more edges.
|
|
2794
|
+
*
|
|
2795
|
+
* @param edge - EDGE_LEFT, EDGE_TOP, EDGE_RIGHT, EDGE_BOTTOM, EDGE_HORIZONTAL, EDGE_VERTICAL, or EDGE_ALL
|
|
2796
|
+
* @param value - Padding in points
|
|
2797
|
+
* @example
|
|
2798
|
+
* ```typescript
|
|
2799
|
+
* node.setPadding(EDGE_ALL, 10); // Set 10pt padding on all edges
|
|
2800
|
+
* node.setPadding(EDGE_HORIZONTAL, 5); // Set 5pt padding on left and right
|
|
2801
|
+
* ```
|
|
2802
|
+
*/
|
|
2803
|
+
setPadding(edge, value) {
|
|
2804
|
+
setEdgeValue(this._style.padding, edge, value, 1);
|
|
2805
|
+
this.markDirty();
|
|
2806
|
+
}
|
|
2807
|
+
/**
|
|
2808
|
+
* Set padding as a percentage of the parent's width.
|
|
2809
|
+
* Per CSS spec, percentage padding always resolves against the containing block's width.
|
|
2810
|
+
*
|
|
2811
|
+
* @param edge - EDGE_LEFT, EDGE_TOP, EDGE_RIGHT, EDGE_BOTTOM, EDGE_HORIZONTAL, EDGE_VERTICAL, or EDGE_ALL
|
|
2812
|
+
* @param value - Padding as a percentage (0-100)
|
|
2813
|
+
*/
|
|
2814
|
+
setPaddingPercent(edge, value) {
|
|
2815
|
+
setEdgeValue(this._style.padding, edge, value, 2);
|
|
2816
|
+
this.markDirty();
|
|
2817
|
+
}
|
|
2818
|
+
/**
|
|
2819
|
+
* Set margin for one or more edges.
|
|
2820
|
+
*
|
|
2821
|
+
* @param edge - EDGE_LEFT, EDGE_TOP, EDGE_RIGHT, EDGE_BOTTOM, EDGE_HORIZONTAL, EDGE_VERTICAL, or EDGE_ALL
|
|
2822
|
+
* @param value - Margin in points
|
|
2823
|
+
* @example
|
|
2824
|
+
* ```typescript
|
|
2825
|
+
* node.setMargin(EDGE_ALL, 5); // Set 5pt margin on all edges
|
|
2826
|
+
* node.setMargin(EDGE_TOP, 10); // Set 10pt margin on top only
|
|
2827
|
+
* ```
|
|
2828
|
+
*/
|
|
2829
|
+
setMargin(edge, value) {
|
|
2830
|
+
setEdgeValue(this._style.margin, edge, value, 1);
|
|
2831
|
+
this.markDirty();
|
|
2832
|
+
}
|
|
2833
|
+
/**
|
|
2834
|
+
* Set margin as a percentage of the parent's size.
|
|
2835
|
+
*
|
|
2836
|
+
* @param edge - EDGE_LEFT, EDGE_TOP, EDGE_RIGHT, EDGE_BOTTOM, EDGE_HORIZONTAL, EDGE_VERTICAL, or EDGE_ALL
|
|
2837
|
+
* @param value - Margin as a percentage (0-100)
|
|
2838
|
+
*/
|
|
2839
|
+
setMarginPercent(edge, value) {
|
|
2840
|
+
setEdgeValue(this._style.margin, edge, value, 2);
|
|
2841
|
+
this.markDirty();
|
|
2842
|
+
}
|
|
2843
|
+
/**
|
|
2844
|
+
* Set margin to auto (for centering items with margin: auto).
|
|
2845
|
+
*
|
|
2846
|
+
* @param edge - EDGE_LEFT, EDGE_TOP, EDGE_RIGHT, EDGE_BOTTOM, EDGE_HORIZONTAL, EDGE_VERTICAL, or EDGE_ALL
|
|
2847
|
+
*/
|
|
2848
|
+
setMarginAuto(edge) {
|
|
2849
|
+
setEdgeValue(this._style.margin, edge, 0, 3);
|
|
2850
|
+
this.markDirty();
|
|
2851
|
+
}
|
|
2852
|
+
/**
|
|
2853
|
+
* Set border width for one or more edges.
|
|
2854
|
+
*
|
|
2855
|
+
* @param edge - EDGE_LEFT, EDGE_TOP, EDGE_RIGHT, EDGE_BOTTOM, EDGE_HORIZONTAL, EDGE_VERTICAL, or EDGE_ALL
|
|
2856
|
+
* @param value - Border width in points
|
|
2857
|
+
*/
|
|
2858
|
+
setBorder(edge, value) {
|
|
2859
|
+
setEdgeBorder(this._style.border, edge, value);
|
|
2860
|
+
this.markDirty();
|
|
2861
|
+
}
|
|
2862
|
+
/**
|
|
2863
|
+
* Set gap between flex items.
|
|
2864
|
+
*
|
|
2865
|
+
* @param gutter - GUTTER_COLUMN (horizontal gap), GUTTER_ROW (vertical gap), or GUTTER_ALL (both)
|
|
2866
|
+
* @param value - Gap size in points
|
|
2867
|
+
* @example
|
|
2868
|
+
* ```typescript
|
|
2869
|
+
* container.setGap(GUTTER_ALL, 8); // Set 8pt gap between all items
|
|
2870
|
+
* container.setGap(GUTTER_COLUMN, 10); // Set 10pt horizontal gap only
|
|
2871
|
+
* ```
|
|
2872
|
+
*/
|
|
2873
|
+
setGap(gutter, value) {
|
|
2874
|
+
if (gutter === 0) this._style.gap[0] = value;
|
|
2875
|
+
else if (gutter === 1) this._style.gap[1] = value;
|
|
2876
|
+
else if (gutter === 2) {
|
|
2877
|
+
this._style.gap[0] = value;
|
|
2878
|
+
this._style.gap[1] = value;
|
|
2879
|
+
}
|
|
2880
|
+
this.markDirty();
|
|
2881
|
+
}
|
|
2882
|
+
/**
|
|
2883
|
+
* Set the position type.
|
|
2884
|
+
*
|
|
2885
|
+
* @param positionType - POSITION_TYPE_STATIC, POSITION_TYPE_RELATIVE, or POSITION_TYPE_ABSOLUTE
|
|
2886
|
+
* @example
|
|
2887
|
+
* ```typescript
|
|
2888
|
+
* node.setPositionType(POSITION_TYPE_ABSOLUTE);
|
|
2889
|
+
* node.setPosition(EDGE_LEFT, 10);
|
|
2890
|
+
* node.setPosition(EDGE_TOP, 20);
|
|
2891
|
+
* ```
|
|
2892
|
+
*/
|
|
2893
|
+
setPositionType(positionType) {
|
|
2894
|
+
this._style.positionType = positionType;
|
|
2895
|
+
this.markDirty();
|
|
2896
|
+
}
|
|
2897
|
+
/**
|
|
2898
|
+
* Set position offset for one or more edges.
|
|
2899
|
+
* Only applies when position type is ABSOLUTE or RELATIVE.
|
|
2900
|
+
*
|
|
2901
|
+
* @param edge - EDGE_LEFT, EDGE_TOP, EDGE_RIGHT, EDGE_BOTTOM, EDGE_HORIZONTAL, EDGE_VERTICAL, or EDGE_ALL
|
|
2902
|
+
* @param value - Position offset in points
|
|
2903
|
+
*/
|
|
2904
|
+
setPosition(edge, value) {
|
|
2905
|
+
if (Number.isNaN(value)) setEdgeValue(this._style.position, edge, 0, 0);
|
|
2906
|
+
else setEdgeValue(this._style.position, edge, value, 1);
|
|
2907
|
+
this.markDirty();
|
|
2908
|
+
}
|
|
2909
|
+
/**
|
|
2910
|
+
* Set position offset as a percentage.
|
|
2911
|
+
*
|
|
2912
|
+
* @param edge - EDGE_LEFT, EDGE_TOP, EDGE_RIGHT, EDGE_BOTTOM, EDGE_HORIZONTAL, EDGE_VERTICAL, or EDGE_ALL
|
|
2913
|
+
* @param value - Position offset as a percentage of parent's corresponding dimension
|
|
2914
|
+
*/
|
|
2915
|
+
setPositionPercent(edge, value) {
|
|
2916
|
+
setEdgeValue(this._style.position, edge, value, 2);
|
|
2917
|
+
this.markDirty();
|
|
2918
|
+
}
|
|
2919
|
+
/**
|
|
2920
|
+
* Set the display type.
|
|
2921
|
+
*
|
|
2922
|
+
* @param display - DISPLAY_FLEX or DISPLAY_NONE
|
|
2923
|
+
*/
|
|
2924
|
+
setDisplay(display) {
|
|
2925
|
+
this._style.display = display;
|
|
2926
|
+
this.markDirty();
|
|
2927
|
+
}
|
|
2928
|
+
/**
|
|
2929
|
+
* Set the overflow behavior.
|
|
2930
|
+
*
|
|
2931
|
+
* @param overflow - OVERFLOW_VISIBLE, OVERFLOW_HIDDEN, or OVERFLOW_SCROLL
|
|
2932
|
+
*/
|
|
2933
|
+
setOverflow(overflow) {
|
|
2934
|
+
this._style.overflow = overflow;
|
|
2935
|
+
this.markDirty();
|
|
2936
|
+
}
|
|
2937
|
+
/**
|
|
2938
|
+
* Get the width style value.
|
|
2939
|
+
*
|
|
2940
|
+
* @returns Width value with unit (points, percent, or auto)
|
|
2941
|
+
*/
|
|
2942
|
+
getWidth() {
|
|
2943
|
+
return this._style.width;
|
|
2944
|
+
}
|
|
2945
|
+
/**
|
|
2946
|
+
* Get the height style value.
|
|
2947
|
+
*
|
|
2948
|
+
* @returns Height value with unit (points, percent, or auto)
|
|
2949
|
+
*/
|
|
2950
|
+
getHeight() {
|
|
2951
|
+
return this._style.height;
|
|
2952
|
+
}
|
|
2953
|
+
/**
|
|
2954
|
+
* Get the minimum width style value.
|
|
2955
|
+
*
|
|
2956
|
+
* @returns Minimum width value with unit
|
|
2957
|
+
*/
|
|
2958
|
+
getMinWidth() {
|
|
2959
|
+
return this._style.minWidth;
|
|
2960
|
+
}
|
|
2961
|
+
/**
|
|
2962
|
+
* Get the minimum height style value.
|
|
2963
|
+
*
|
|
2964
|
+
* @returns Minimum height value with unit
|
|
2965
|
+
*/
|
|
2966
|
+
getMinHeight() {
|
|
2967
|
+
return this._style.minHeight;
|
|
2968
|
+
}
|
|
2969
|
+
/**
|
|
2970
|
+
* Get the maximum width style value.
|
|
2971
|
+
*
|
|
2972
|
+
* @returns Maximum width value with unit
|
|
2973
|
+
*/
|
|
2974
|
+
getMaxWidth() {
|
|
2975
|
+
return this._style.maxWidth;
|
|
2976
|
+
}
|
|
2977
|
+
/**
|
|
2978
|
+
* Get the maximum height style value.
|
|
2979
|
+
*
|
|
2980
|
+
* @returns Maximum height value with unit
|
|
2981
|
+
*/
|
|
2982
|
+
getMaxHeight() {
|
|
2983
|
+
return this._style.maxHeight;
|
|
2984
|
+
}
|
|
2985
|
+
/**
|
|
2986
|
+
* Get the aspect ratio.
|
|
2987
|
+
*
|
|
2988
|
+
* @returns Aspect ratio value (NaN if not set)
|
|
2989
|
+
*/
|
|
2990
|
+
getAspectRatio() {
|
|
2991
|
+
return this._style.aspectRatio;
|
|
2992
|
+
}
|
|
2993
|
+
/**
|
|
2994
|
+
* Get the flex grow factor.
|
|
2995
|
+
*
|
|
2996
|
+
* @returns Flex grow value
|
|
2997
|
+
*/
|
|
2998
|
+
getFlexGrow() {
|
|
2999
|
+
return this._style.flexGrow;
|
|
3000
|
+
}
|
|
3001
|
+
/**
|
|
3002
|
+
* Get the flex shrink factor.
|
|
3003
|
+
*
|
|
3004
|
+
* @returns Flex shrink value
|
|
3005
|
+
*/
|
|
3006
|
+
getFlexShrink() {
|
|
3007
|
+
return this._style.flexShrink;
|
|
3008
|
+
}
|
|
3009
|
+
/**
|
|
3010
|
+
* Get the flex basis style value.
|
|
3011
|
+
*
|
|
3012
|
+
* @returns Flex basis value with unit
|
|
3013
|
+
*/
|
|
3014
|
+
getFlexBasis() {
|
|
3015
|
+
return this._style.flexBasis;
|
|
3016
|
+
}
|
|
3017
|
+
/**
|
|
3018
|
+
* Get the flex direction.
|
|
3019
|
+
*
|
|
3020
|
+
* @returns Flex direction constant
|
|
3021
|
+
*/
|
|
3022
|
+
getFlexDirection() {
|
|
3023
|
+
return this._style.flexDirection;
|
|
3024
|
+
}
|
|
3025
|
+
/**
|
|
3026
|
+
* Get the flex wrap setting.
|
|
3027
|
+
*
|
|
3028
|
+
* @returns Flex wrap constant
|
|
3029
|
+
*/
|
|
3030
|
+
getFlexWrap() {
|
|
3031
|
+
return this._style.flexWrap;
|
|
3032
|
+
}
|
|
3033
|
+
/**
|
|
3034
|
+
* Get the align items setting.
|
|
3035
|
+
*
|
|
3036
|
+
* @returns Align items constant
|
|
3037
|
+
*/
|
|
3038
|
+
getAlignItems() {
|
|
3039
|
+
return this._style.alignItems;
|
|
3040
|
+
}
|
|
3041
|
+
/**
|
|
3042
|
+
* Get the align self setting.
|
|
3043
|
+
*
|
|
3044
|
+
* @returns Align self constant
|
|
3045
|
+
*/
|
|
3046
|
+
getAlignSelf() {
|
|
3047
|
+
return this._style.alignSelf;
|
|
3048
|
+
}
|
|
3049
|
+
/**
|
|
3050
|
+
* Get the align content setting.
|
|
3051
|
+
*
|
|
3052
|
+
* @returns Align content constant
|
|
3053
|
+
*/
|
|
3054
|
+
getAlignContent() {
|
|
3055
|
+
return this._style.alignContent;
|
|
3056
|
+
}
|
|
3057
|
+
/**
|
|
3058
|
+
* Get the justify content setting.
|
|
3059
|
+
*
|
|
3060
|
+
* @returns Justify content constant
|
|
3061
|
+
*/
|
|
3062
|
+
getJustifyContent() {
|
|
3063
|
+
return this._style.justifyContent;
|
|
3064
|
+
}
|
|
3065
|
+
/**
|
|
3066
|
+
* Get the padding for a specific edge.
|
|
3067
|
+
*
|
|
3068
|
+
* @param edge - EDGE_LEFT, EDGE_TOP, EDGE_RIGHT, or EDGE_BOTTOM
|
|
3069
|
+
* @returns Padding value with unit
|
|
3070
|
+
*/
|
|
3071
|
+
getPadding(edge) {
|
|
3072
|
+
return getEdgeValue(this._style.padding, edge);
|
|
3073
|
+
}
|
|
3074
|
+
/**
|
|
3075
|
+
* Get the margin for a specific edge.
|
|
3076
|
+
*
|
|
3077
|
+
* @param edge - EDGE_LEFT, EDGE_TOP, EDGE_RIGHT, or EDGE_BOTTOM
|
|
3078
|
+
* @returns Margin value with unit
|
|
3079
|
+
*/
|
|
3080
|
+
getMargin(edge) {
|
|
3081
|
+
return getEdgeValue(this._style.margin, edge);
|
|
3082
|
+
}
|
|
3083
|
+
/**
|
|
3084
|
+
* Get the border width for a specific edge.
|
|
3085
|
+
*
|
|
3086
|
+
* @param edge - EDGE_LEFT, EDGE_TOP, EDGE_RIGHT, or EDGE_BOTTOM
|
|
3087
|
+
* @returns Border width in points
|
|
3088
|
+
*/
|
|
3089
|
+
getBorder(edge) {
|
|
3090
|
+
return getEdgeBorderValue(this._style.border, edge);
|
|
3091
|
+
}
|
|
3092
|
+
/**
|
|
3093
|
+
* Get the position offset for a specific edge.
|
|
3094
|
+
*
|
|
3095
|
+
* @param edge - EDGE_LEFT, EDGE_TOP, EDGE_RIGHT, or EDGE_BOTTOM
|
|
3096
|
+
* @returns Position value with unit
|
|
3097
|
+
*/
|
|
3098
|
+
getPosition(edge) {
|
|
3099
|
+
return getEdgeValue(this._style.position, edge);
|
|
3100
|
+
}
|
|
3101
|
+
/**
|
|
3102
|
+
* Get the position type.
|
|
3103
|
+
*
|
|
3104
|
+
* @returns Position type constant
|
|
3105
|
+
*/
|
|
3106
|
+
getPositionType() {
|
|
3107
|
+
return this._style.positionType;
|
|
3108
|
+
}
|
|
3109
|
+
/**
|
|
3110
|
+
* Get the display type.
|
|
3111
|
+
*
|
|
3112
|
+
* @returns Display constant
|
|
3113
|
+
*/
|
|
3114
|
+
getDisplay() {
|
|
3115
|
+
return this._style.display;
|
|
3116
|
+
}
|
|
3117
|
+
/**
|
|
3118
|
+
* Get the overflow setting.
|
|
3119
|
+
*
|
|
3120
|
+
* @returns Overflow constant
|
|
3121
|
+
*/
|
|
3122
|
+
getOverflow() {
|
|
3123
|
+
return this._style.overflow;
|
|
3124
|
+
}
|
|
3125
|
+
/**
|
|
3126
|
+
* Get the gap for column or row.
|
|
3127
|
+
*
|
|
3128
|
+
* @param gutter - GUTTER_COLUMN or GUTTER_ROW
|
|
3129
|
+
* @returns Gap size in points
|
|
3130
|
+
*/
|
|
3131
|
+
getGap(gutter) {
|
|
3132
|
+
if (gutter === 0) return this._style.gap[0];
|
|
3133
|
+
else if (gutter === 1) return this._style.gap[1];
|
|
3134
|
+
return this._style.gap[0];
|
|
3135
|
+
}
|
|
3136
|
+
};
|
|
3137
|
+
new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
3138
|
+
new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
3139
|
+
//#endregion
|
|
3140
|
+
//#region ../packages/ag-term/src/adapters/flexily-zero-adapter.ts
|
|
3141
|
+
/**
|
|
3142
|
+
* Flexily Layout Engine Adapter
|
|
3143
|
+
*
|
|
3144
|
+
* Wraps Flexily to implement the LayoutEngine interface.
|
|
3145
|
+
* Uses the default zero-allocation algorithm from flexily.
|
|
3146
|
+
*/
|
|
3147
|
+
/**
|
|
3148
|
+
* Wraps a Flexily zero-alloc node to implement LayoutNode interface.
|
|
3149
|
+
* Since Flexily already has a Yoga-compatible API, this is mostly delegation.
|
|
3150
|
+
*/
|
|
3151
|
+
var FlexilyZeroNodeAdapter = class {
|
|
3152
|
+
node;
|
|
3153
|
+
constructor(node) {
|
|
3154
|
+
this.node = node;
|
|
3155
|
+
}
|
|
3156
|
+
/** Get the underlying Flexily node (for tree operations) */
|
|
3157
|
+
getFlexilyNode() {
|
|
3158
|
+
return this.node;
|
|
3159
|
+
}
|
|
3160
|
+
insertChild(child, index) {
|
|
3161
|
+
const flexilyChild = child.getFlexilyNode();
|
|
3162
|
+
this.node.insertChild(flexilyChild, index);
|
|
3163
|
+
}
|
|
3164
|
+
removeChild(child) {
|
|
3165
|
+
const flexilyChild = child.getFlexilyNode();
|
|
3166
|
+
this.node.removeChild(flexilyChild);
|
|
3167
|
+
}
|
|
3168
|
+
free() {
|
|
3169
|
+
this.node.free();
|
|
3170
|
+
}
|
|
3171
|
+
setMeasureFunc(measureFunc) {
|
|
3172
|
+
this.node.setMeasureFunc((width, widthMode, height, heightMode) => {
|
|
3173
|
+
return measureFunc(width, this.measureModeToString(widthMode), height, this.measureModeToString(heightMode));
|
|
3174
|
+
});
|
|
3175
|
+
}
|
|
3176
|
+
markDirty() {
|
|
3177
|
+
this.node.markDirty();
|
|
3178
|
+
}
|
|
3179
|
+
measureModeToString(mode) {
|
|
3180
|
+
if (mode === 1) return "exactly";
|
|
3181
|
+
if (mode === 2) return "at-most";
|
|
3182
|
+
return "undefined";
|
|
3183
|
+
}
|
|
3184
|
+
setWidth(value) {
|
|
3185
|
+
this.node.setWidth(value);
|
|
3186
|
+
}
|
|
3187
|
+
setWidthPercent(value) {
|
|
3188
|
+
this.node.setWidthPercent(value);
|
|
3189
|
+
}
|
|
3190
|
+
setWidthAuto() {
|
|
3191
|
+
this.node.setWidthAuto();
|
|
3192
|
+
}
|
|
3193
|
+
setHeight(value) {
|
|
3194
|
+
this.node.setHeight(value);
|
|
3195
|
+
}
|
|
3196
|
+
setHeightPercent(value) {
|
|
3197
|
+
this.node.setHeightPercent(value);
|
|
3198
|
+
}
|
|
3199
|
+
setHeightAuto() {
|
|
3200
|
+
this.node.setHeightAuto();
|
|
3201
|
+
}
|
|
3202
|
+
setMinWidth(value) {
|
|
3203
|
+
this.node.setMinWidth(value);
|
|
3204
|
+
}
|
|
3205
|
+
setMinWidthPercent(value) {
|
|
3206
|
+
this.node.setMinWidthPercent(value);
|
|
3207
|
+
}
|
|
3208
|
+
setMinHeight(value) {
|
|
3209
|
+
this.node.setMinHeight(value);
|
|
3210
|
+
}
|
|
3211
|
+
setMinHeightPercent(value) {
|
|
3212
|
+
this.node.setMinHeightPercent(value);
|
|
3213
|
+
}
|
|
3214
|
+
setMaxWidth(value) {
|
|
3215
|
+
this.node.setMaxWidth(value);
|
|
3216
|
+
}
|
|
3217
|
+
setMaxWidthPercent(value) {
|
|
3218
|
+
this.node.setMaxWidthPercent(value);
|
|
3219
|
+
}
|
|
3220
|
+
setMaxHeight(value) {
|
|
3221
|
+
this.node.setMaxHeight(value);
|
|
3222
|
+
}
|
|
3223
|
+
setMaxHeightPercent(value) {
|
|
3224
|
+
this.node.setMaxHeightPercent(value);
|
|
3225
|
+
}
|
|
3226
|
+
setFlexGrow(value) {
|
|
3227
|
+
this.node.setFlexGrow(value);
|
|
3228
|
+
}
|
|
3229
|
+
setFlexShrink(value) {
|
|
3230
|
+
this.node.setFlexShrink(value);
|
|
3231
|
+
}
|
|
3232
|
+
setFlexBasis(value) {
|
|
3233
|
+
this.node.setFlexBasis(value);
|
|
3234
|
+
}
|
|
3235
|
+
setFlexBasisPercent(value) {
|
|
3236
|
+
this.node.setFlexBasisPercent(value);
|
|
3237
|
+
}
|
|
3238
|
+
setFlexBasisAuto() {
|
|
3239
|
+
this.node.setFlexBasisAuto();
|
|
3240
|
+
}
|
|
3241
|
+
setFlexDirection(direction) {
|
|
3242
|
+
this.node.setFlexDirection(direction);
|
|
3243
|
+
}
|
|
3244
|
+
setFlexWrap(wrap) {
|
|
3245
|
+
this.node.setFlexWrap(wrap);
|
|
3246
|
+
}
|
|
3247
|
+
setAlignItems(align) {
|
|
3248
|
+
this.node.setAlignItems(align);
|
|
3249
|
+
}
|
|
3250
|
+
setAlignSelf(align) {
|
|
3251
|
+
this.node.setAlignSelf(align);
|
|
3252
|
+
}
|
|
3253
|
+
setAlignContent(align) {
|
|
3254
|
+
this.node.setAlignContent(align);
|
|
3255
|
+
}
|
|
3256
|
+
setJustifyContent(justify) {
|
|
3257
|
+
this.node.setJustifyContent(justify);
|
|
3258
|
+
}
|
|
3259
|
+
setPadding(edge, value) {
|
|
3260
|
+
this.node.setPadding(edge, value);
|
|
3261
|
+
}
|
|
3262
|
+
setMargin(edge, value) {
|
|
3263
|
+
this.node.setMargin(edge, value);
|
|
3264
|
+
}
|
|
3265
|
+
setBorder(edge, value) {
|
|
3266
|
+
this.node.setBorder(edge, value);
|
|
3267
|
+
}
|
|
3268
|
+
setGap(gutter, value) {
|
|
3269
|
+
this.node.setGap(gutter, value);
|
|
3270
|
+
}
|
|
3271
|
+
setDisplay(display) {
|
|
3272
|
+
this.node.setDisplay(display);
|
|
3273
|
+
}
|
|
3274
|
+
setPositionType(positionType) {
|
|
3275
|
+
this.node.setPositionType(positionType);
|
|
3276
|
+
}
|
|
3277
|
+
setPosition(edge, value) {
|
|
3278
|
+
this.node.setPosition(edge, value);
|
|
3279
|
+
}
|
|
3280
|
+
setPositionPercent(edge, value) {
|
|
3281
|
+
this.node.setPositionPercent(edge, value);
|
|
3282
|
+
}
|
|
3283
|
+
setOverflow(overflow) {
|
|
3284
|
+
this.node.setOverflow(overflow);
|
|
3285
|
+
}
|
|
3286
|
+
setAspectRatio(value) {
|
|
3287
|
+
this.node.setAspectRatio(value);
|
|
3288
|
+
}
|
|
3289
|
+
calculateLayout(width, height, direction) {
|
|
3290
|
+
this.node.calculateLayout(width, height, direction ?? 1);
|
|
3291
|
+
}
|
|
3292
|
+
getComputedLeft() {
|
|
3293
|
+
return this.node.getComputedLeft();
|
|
3294
|
+
}
|
|
3295
|
+
getComputedTop() {
|
|
3296
|
+
return this.node.getComputedTop();
|
|
3297
|
+
}
|
|
3298
|
+
getComputedWidth() {
|
|
3299
|
+
return this.node.getComputedWidth();
|
|
3300
|
+
}
|
|
3301
|
+
getComputedHeight() {
|
|
3302
|
+
return this.node.getComputedHeight();
|
|
3303
|
+
}
|
|
3304
|
+
};
|
|
3305
|
+
/**
|
|
3306
|
+
* Layout engine implementation using Flexily zero-allocation variant.
|
|
3307
|
+
* Optimized for high-frequency layout with reduced GC pressure.
|
|
3308
|
+
*/
|
|
3309
|
+
var FlexilyZeroLayoutEngine = class {
|
|
3310
|
+
_constants = {
|
|
3311
|
+
FLEX_DIRECTION_COLUMN: 0,
|
|
3312
|
+
FLEX_DIRECTION_COLUMN_REVERSE: 1,
|
|
3313
|
+
FLEX_DIRECTION_ROW: 2,
|
|
3314
|
+
FLEX_DIRECTION_ROW_REVERSE: 3,
|
|
3315
|
+
WRAP_NO_WRAP: 0,
|
|
3316
|
+
WRAP_WRAP: 1,
|
|
3317
|
+
WRAP_WRAP_REVERSE: 2,
|
|
3318
|
+
ALIGN_AUTO: 0,
|
|
3319
|
+
ALIGN_FLEX_START: 1,
|
|
3320
|
+
ALIGN_CENTER: 2,
|
|
3321
|
+
ALIGN_FLEX_END: 3,
|
|
3322
|
+
ALIGN_STRETCH: 4,
|
|
3323
|
+
ALIGN_BASELINE: 5,
|
|
3324
|
+
ALIGN_SPACE_BETWEEN: 6,
|
|
3325
|
+
ALIGN_SPACE_AROUND: 7,
|
|
3326
|
+
ALIGN_SPACE_EVENLY: 8,
|
|
3327
|
+
JUSTIFY_FLEX_START: 0,
|
|
3328
|
+
JUSTIFY_CENTER: 1,
|
|
3329
|
+
JUSTIFY_FLEX_END: 2,
|
|
3330
|
+
JUSTIFY_SPACE_BETWEEN: 3,
|
|
3331
|
+
JUSTIFY_SPACE_AROUND: 4,
|
|
3332
|
+
JUSTIFY_SPACE_EVENLY: 5,
|
|
3333
|
+
EDGE_LEFT: 0,
|
|
3334
|
+
EDGE_TOP: 1,
|
|
3335
|
+
EDGE_RIGHT: 2,
|
|
3336
|
+
EDGE_BOTTOM: 3,
|
|
3337
|
+
EDGE_HORIZONTAL: 6,
|
|
3338
|
+
EDGE_VERTICAL: 7,
|
|
3339
|
+
EDGE_ALL: 8,
|
|
3340
|
+
GUTTER_COLUMN: 0,
|
|
3341
|
+
GUTTER_ROW: 1,
|
|
3342
|
+
GUTTER_ALL: 2,
|
|
3343
|
+
DISPLAY_FLEX: 0,
|
|
3344
|
+
DISPLAY_NONE: 1,
|
|
3345
|
+
POSITION_TYPE_STATIC: 0,
|
|
3346
|
+
POSITION_TYPE_RELATIVE: 1,
|
|
3347
|
+
POSITION_TYPE_ABSOLUTE: 2,
|
|
3348
|
+
OVERFLOW_VISIBLE: 0,
|
|
3349
|
+
OVERFLOW_HIDDEN: 1,
|
|
3350
|
+
OVERFLOW_SCROLL: 2,
|
|
3351
|
+
DIRECTION_LTR: 1,
|
|
3352
|
+
MEASURE_MODE_UNDEFINED: 0,
|
|
3353
|
+
MEASURE_MODE_EXACTLY: 1,
|
|
3354
|
+
MEASURE_MODE_AT_MOST: 2
|
|
3355
|
+
};
|
|
3356
|
+
createNode() {
|
|
3357
|
+
return new FlexilyZeroNodeAdapter(Node.create());
|
|
3358
|
+
}
|
|
3359
|
+
get constants() {
|
|
3360
|
+
return this._constants;
|
|
3361
|
+
}
|
|
3362
|
+
get name() {
|
|
3363
|
+
return "flexily-zero";
|
|
3364
|
+
}
|
|
3365
|
+
};
|
|
3366
|
+
/**
|
|
3367
|
+
* Create a Flexily zero-allocation layout engine.
|
|
3368
|
+
* Unlike Yoga, Flexily doesn't require async initialization.
|
|
3369
|
+
*/
|
|
3370
|
+
function createFlexilyZeroEngine() {
|
|
3371
|
+
return new FlexilyZeroLayoutEngine();
|
|
3372
|
+
}
|
|
3373
|
+
//#endregion
|
|
3374
|
+
export { createFlexilyZeroEngine };
|