@typecad/ui 1.0.0-alpha.12 → 1.0.0-alpha.13
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/assets/fonts/dejavu/DejaVuSans-Bold.ttf +0 -0
- package/assets/fonts/dejavu/DejaVuSans-ExtraLight.ttf +0 -0
- package/assets/fonts/dejavu/DejaVuSans-Oblique.ttf +0 -0
- package/assets/fonts/dejavu/DejaVuSans.ttf +0 -0
- package/assets/fonts/dejavu/DejaVuSansMono-Bold.ttf +0 -0
- package/assets/fonts/dejavu/DejaVuSansMono.ttf +0 -0
- package/assets/fonts/dejavu/LICENSE +187 -0
- package/assets/fonts/dejavu/README.md +49 -0
- package/dist/engine-index.js +2 -0
- package/dist/preview/build-program.d.ts +7 -1
- package/dist/preview/build-program.js +94 -15
- package/dist/preview/host-gfx.d.ts +3 -0
- package/dist/preview/host-gfx.js +30 -3
- package/dist/preview/host-ui-runtime.d.ts +115 -0
- package/dist/preview/host-ui-runtime.js +928 -46
- package/dist/ui-engine/block-layout.js +25 -11
- package/dist/ui-engine/color.d.ts +6 -0
- package/dist/ui-engine/color.js +80 -2
- package/dist/ui-engine/compat-report.d.ts +8 -0
- package/dist/ui-engine/compat-report.js +124 -0
- package/dist/ui-engine/css-imports.d.ts +4 -0
- package/dist/ui-engine/css-imports.js +70 -0
- package/dist/ui-engine/css-parser.js +58 -14
- package/dist/ui-engine/default-font.d.ts +25 -0
- package/dist/ui-engine/default-font.js +80 -0
- package/dist/ui-engine/font-assets.d.ts +2 -2
- package/dist/ui-engine/font-assets.js +22 -5
- package/dist/ui-engine/html-parser.d.ts +4 -0
- package/dist/ui-engine/html-parser.js +241 -26
- package/dist/ui-engine/image-assets.d.ts +8 -1
- package/dist/ui-engine/image-assets.js +35 -2
- package/dist/ui-engine/image-decode.d.ts +22 -0
- package/dist/ui-engine/image-decode.js +194 -0
- package/dist/ui-engine/inline-parser.js +1 -1
- package/dist/ui-engine/layout-engine.d.ts +19 -0
- package/dist/ui-engine/layout-engine.js +59 -3
- package/dist/ui-engine/model.d.ts +18 -0
- package/dist/ui-engine/model.js +75 -7
- package/dist/ui-engine/runtime-header/blend-bodies.js +29 -9
- package/dist/ui-engine/runtime-header/canvas-helpers.js +3 -0
- package/dist/ui-engine/runtime-header/canvas-scrollbar.js +115 -111
- package/dist/ui-engine/runtime-header/cuttlefish-gfx.js +21 -0
- package/dist/ui-engine/runtime-header/dirty-scroll-mutators.js +182 -179
- package/dist/ui-engine/runtime-header/forward-decls.js +215 -167
- package/dist/ui-engine/runtime-header/init-press-input.js +138 -135
- package/dist/ui-engine/runtime-header/keyboard.js +682 -380
- package/dist/ui-engine/runtime-header/node-draw-body.js +1656 -1386
- package/dist/ui-engine/runtime-header/paint-order-coords.js +259 -214
- package/dist/ui-engine/runtime-header/paint-rects-repair.js +736 -568
- package/dist/ui-engine/runtime-header/scroll-physics.js +25 -1
- package/dist/ui-engine/runtime-header/state-bindings-nav.js +9 -0
- package/dist/ui-engine/runtime-header/structs.js +228 -216
- package/dist/ui-engine/runtime-header/text-rendering.js +888 -770
- package/dist/ui-engine/runtime-header/tick/bindings-phase.js +118 -116
- package/dist/ui-engine/runtime-header/tick/dirty-draw-phase.js +897 -633
- package/dist/ui-engine/runtime-header/tick/scroll-canvas-phase.js +24 -25
- package/dist/ui-engine/runtime-header/touch-keyboard-fwd.js +78 -23
- package/dist/ui-engine/runtime-header/types-defines.js +110 -93
- package/dist/ui-engine/shadcn-kit.d.ts +1 -0
- package/dist/ui-engine/shadcn-kit.js +502 -0
- package/dist/ui-engine/style-resolver.d.ts +4 -0
- package/dist/ui-engine/style-resolver.js +58 -6
- package/dist/ui-engine/transpile-ui.js +12 -5
- package/dist/ui-engine/ua-stylesheet.d.ts +22 -1
- package/dist/ui-engine/ua-stylesheet.js +119 -32
- package/dist/ui-engine/ui-lowering.js +37 -3
- package/dist/ui-engine/ui-registry.js +32 -4
- package/dist/ui-engine/yoga-layout.js +57 -52
- package/package.json +9 -3
- package/src/engine-index.ts +53 -51
- package/src/preview/build-program.ts +810 -734
- package/src/preview/host-gfx.ts +30 -3
- package/src/preview/host-ui-runtime.ts +4133 -3289
- package/src/ui-engine/block-layout.ts +24 -11
- package/src/ui-engine/color.ts +77 -2
- package/src/ui-engine/compat-report.ts +139 -0
- package/src/ui-engine/css-imports.ts +69 -0
- package/src/ui-engine/css-parser.ts +64 -15
- package/src/ui-engine/default-font.ts +95 -0
- package/src/ui-engine/font-assets.ts +545 -525
- package/src/ui-engine/html-parser.ts +615 -397
- package/src/ui-engine/image-assets.ts +37 -2
- package/src/ui-engine/image-decode.ts +244 -0
- package/src/ui-engine/inline-parser.ts +1 -1
- package/src/ui-engine/layout-engine.ts +61 -3
- package/src/ui-engine/model.ts +1317 -1230
- package/src/ui-engine/runtime-header/blend-bodies.ts +29 -9
- package/src/ui-engine/runtime-header/canvas-helpers.ts +3 -0
- package/src/ui-engine/runtime-header/canvas-scrollbar.ts +121 -117
- package/src/ui-engine/runtime-header/cuttlefish-gfx.ts +21 -0
- package/src/ui-engine/runtime-header/dirty-scroll-mutators.ts +188 -185
- package/src/ui-engine/runtime-header/forward-decls.ts +227 -179
- package/src/ui-engine/runtime-header/init-press-input.ts +144 -141
- package/src/ui-engine/runtime-header/keyboard.ts +689 -386
- package/src/ui-engine/runtime-header/node-draw-body.ts +1681 -1411
- package/src/ui-engine/runtime-header/paint-order-coords.ts +265 -220
- package/src/ui-engine/runtime-header/paint-rects-repair.ts +742 -574
- package/src/ui-engine/runtime-header/scroll-physics.ts +25 -1
- package/src/ui-engine/runtime-header/state-bindings-nav.ts +9 -0
- package/src/ui-engine/runtime-header/structs.ts +234 -222
- package/src/ui-engine/runtime-header/text-rendering.ts +894 -776
- package/src/ui-engine/runtime-header/tick/bindings-phase.ts +124 -122
- package/src/ui-engine/runtime-header/tick/dirty-draw-phase.ts +902 -638
- package/src/ui-engine/runtime-header/tick/scroll-canvas-phase.ts +30 -31
- package/src/ui-engine/runtime-header/touch-keyboard-fwd.ts +78 -23
- package/src/ui-engine/runtime-header/types-defines.ts +116 -99
- package/src/ui-engine/shadcn-kit.ts +503 -0
- package/src/ui-engine/style-resolver.ts +471 -416
- package/src/ui-engine/transpile-ui.ts +12 -5
- package/src/ui-engine/ua-stylesheet.ts +137 -31
- package/src/ui-engine/ui-lowering.ts +486 -452
- package/src/ui-engine/ui-registry.ts +556 -524
- package/src/ui-engine/yoga-layout.ts +307 -309
|
@@ -1,309 +1,307 @@
|
|
|
1
|
-
// ---------------------------------------------------------------------------
|
|
2
|
-
// YogaLayoutEngine — flexbox layout via Facebook's Yoga engine.
|
|
3
|
-
//
|
|
4
|
-
// Implements the LayoutEngine interface using yoga-layout (WASM, ~2MB).
|
|
5
|
-
// Handles display:flex, flex-direction, gap, align-self, padding (shorthand),
|
|
6
|
-
// border, and content-sized leaf nodes. Falls back gracefully when flex
|
|
7
|
-
// properties are absent.
|
|
8
|
-
//
|
|
9
|
-
// The critical contract: arrange() returns Box[] in pre-order DFS order
|
|
10
|
-
// matching the StyledNode tree, so lowerUIToCpp's flatten() can index
|
|
11
|
-
// boxes[] in lockstep with the styled nodes.
|
|
12
|
-
// ---------------------------------------------------------------------------
|
|
13
|
-
|
|
14
|
-
import { Box, IntrinsicSize, isDisplayNone, LayoutEngine, parseAspectRatio } from "./layout-engine.js";
|
|
15
|
-
import { StyledNode } from "./style-resolver.js";
|
|
16
|
-
import { CSSProperty } from "./css-parser.js";
|
|
17
|
-
import Yoga from "yoga-layout";
|
|
18
|
-
|
|
19
|
-
/** Parse the first numeric value from a CSS string ("8px", "8px 16px" → 8). */
|
|
20
|
-
/** Parse a single CSS length to device pixels.
|
|
21
|
-
* rem/em × 16 (root font size), px/bare as-is, decimals supported.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
if (
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
//
|
|
153
|
-
|
|
154
|
-
if (
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
else if (s.
|
|
170
|
-
else if (s.
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if (s.alignContent === "
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
else if (s.
|
|
179
|
-
else if (s.
|
|
180
|
-
else if (s.
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
if (s.
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
if (s.
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
if (s.
|
|
212
|
-
if (s.
|
|
213
|
-
|
|
214
|
-
//
|
|
215
|
-
if (s.
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
if (s.
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
if (s.
|
|
226
|
-
|
|
227
|
-
if (
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
//
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
//
|
|
239
|
-
//
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
const
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
const
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
}
|
|
309
|
-
}
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// YogaLayoutEngine — flexbox layout via Facebook's Yoga engine.
|
|
3
|
+
//
|
|
4
|
+
// Implements the LayoutEngine interface using yoga-layout (WASM, ~2MB).
|
|
5
|
+
// Handles display:flex, flex-direction, gap, align-self, padding (shorthand),
|
|
6
|
+
// border, and content-sized leaf nodes. Falls back gracefully when flex
|
|
7
|
+
// properties are absent.
|
|
8
|
+
//
|
|
9
|
+
// The critical contract: arrange() returns Box[] in pre-order DFS order
|
|
10
|
+
// matching the StyledNode tree, so lowerUIToCpp's flatten() can index
|
|
11
|
+
// boxes[] in lockstep with the styled nodes.
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
import { Box, IntrinsicSize, isDisplayNone, LayoutEngine, parseAspectRatio, cssDimValue, cssSizeValue, dimSet, expandBoxShorthand, CssDim } from "./layout-engine.js";
|
|
15
|
+
import { StyledNode } from "./style-resolver.js";
|
|
16
|
+
import { CSSProperty } from "./css-parser.js";
|
|
17
|
+
import Yoga from "yoga-layout";
|
|
18
|
+
|
|
19
|
+
/** Parse the first numeric value from a CSS string ("8px", "8px 16px" → 8). */
|
|
20
|
+
/** Parse a single CSS length to device pixels.
|
|
21
|
+
* rem/em × 16 (root font size), px/bare as-is, decimals supported.
|
|
22
|
+
* Percentages and "auto" fold to numbers here — use cssDimValue() for the
|
|
23
|
+
* properties (sizes, margins, paddings, insets) where they are meaningful. */
|
|
24
|
+
function cssLength(val: string): number {
|
|
25
|
+
const v = val.trim();
|
|
26
|
+
const remM = /^(-?[\d.]+)rem$/.exec(v);
|
|
27
|
+
if (remM) return Math.round(parseFloat(remM[1]) * 16);
|
|
28
|
+
const emM = /^(-?[\d.]+)em$/.exec(v);
|
|
29
|
+
if (emM) return Math.round(parseFloat(emM[1]) * 16);
|
|
30
|
+
const m = /^(-?[\d.]+)(?:px|%)?$/.exec(v);
|
|
31
|
+
return m ? parseFloat(m[1]) : 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function cssNum(val: string | undefined): number {
|
|
35
|
+
if (!val) return 0;
|
|
36
|
+
return cssLength(val);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Parse border width from "2px solid #808080" → 2. */
|
|
40
|
+
function cssBorderWidth(val: string | undefined): number {
|
|
41
|
+
if (!val) return 0;
|
|
42
|
+
// border shorthand: "<width> <style> <color>" — width is the first token.
|
|
43
|
+
const first = val.trim().split(/\s+/)[0];
|
|
44
|
+
return cssLength(first);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Metadata stored per Yoga node, indexed by traversal position. */
|
|
48
|
+
interface NodeMeta {
|
|
49
|
+
style: CSSProperty;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export class YogaLayoutEngine implements LayoutEngine {
|
|
53
|
+
readonly id = "flex" as const;
|
|
54
|
+
|
|
55
|
+
arrange(
|
|
56
|
+
root: StyledNode,
|
|
57
|
+
viewport: Box,
|
|
58
|
+
measureFn: (node: StyledNode, availableWidth?: number) => IntrinsicSize,
|
|
59
|
+
): Box[] {
|
|
60
|
+
const metaArray: NodeMeta[] = [];
|
|
61
|
+
|
|
62
|
+
// Build the Yoga tree from the StyledNode tree.
|
|
63
|
+
const yogaRoot = this.buildTree(root, metaArray, measureFn);
|
|
64
|
+
|
|
65
|
+
yogaRoot.setWidth(viewport.w);
|
|
66
|
+
yogaRoot.setHeight(viewport.h);
|
|
67
|
+
yogaRoot.calculateLayout(viewport.w, viewport.h, Yoga.DIRECTION_LTR);
|
|
68
|
+
|
|
69
|
+
// Extract boxes in pre-order DFS matching the StyledNode tree.
|
|
70
|
+
const boxes: Box[] = [];
|
|
71
|
+
let extractIdx = 0;
|
|
72
|
+
this.extractBoxes(yogaRoot, boxes, () => metaArray[extractIdx++]);
|
|
73
|
+
|
|
74
|
+
return boxes;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Recursively build a Yoga node tree from a StyledNode tree. */
|
|
78
|
+
private buildTree(
|
|
79
|
+
node: StyledNode,
|
|
80
|
+
metaArray: NodeMeta[],
|
|
81
|
+
measureFn: (node: StyledNode, availableWidth?: number) => IntrinsicSize,
|
|
82
|
+
myIndex: number = metaArray.length,
|
|
83
|
+
): any {
|
|
84
|
+
const yn = Yoga.Node.create();
|
|
85
|
+
const idx = metaArray.length;
|
|
86
|
+
metaArray.push({ style: node.style });
|
|
87
|
+
|
|
88
|
+
const s = node.style;
|
|
89
|
+
if (isDisplayNone(node)) {
|
|
90
|
+
yn.setDisplay?.(Yoga.DISPLAY_NONE);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Flex container
|
|
94
|
+
// flex-direction: row | row-reverse | column | column-reverse.
|
|
95
|
+
// Each value maps to a distinct Yoga enum (reverse was previously
|
|
96
|
+
// dropped — any non-"row" value collapsed to column).
|
|
97
|
+
const fd = s.display === "flex" ? s.flexDirection : "column";
|
|
98
|
+
if (fd === "row") yn.setFlexDirection(Yoga.FLEX_DIRECTION_ROW);
|
|
99
|
+
else if (fd === "row-reverse") yn.setFlexDirection(Yoga.FLEX_DIRECTION_ROW_REVERSE);
|
|
100
|
+
else if (fd === "column-reverse") yn.setFlexDirection(Yoga.FLEX_DIRECTION_COLUMN_REVERSE);
|
|
101
|
+
else yn.setFlexDirection(Yoga.FLEX_DIRECTION_COLUMN);
|
|
102
|
+
|
|
103
|
+
// Padding: full 1-4 value shorthand (TRBL rotation) with % support.
|
|
104
|
+
// "auto" is not valid padding — fold it to the default 0 via asSize.
|
|
105
|
+
const asSize = (d: CssDim): number | `${number}%` => (d === "auto" ? 0 : d);
|
|
106
|
+
const [padT, padR, padB, padL] = expandBoxShorthand(s.padding);
|
|
107
|
+
if (dimSet(padT)) yn.setPadding(Yoga.EDGE_TOP, asSize(padT));
|
|
108
|
+
if (dimSet(padR)) yn.setPadding(Yoga.EDGE_RIGHT, asSize(padR));
|
|
109
|
+
if (dimSet(padB)) yn.setPadding(Yoga.EDGE_BOTTOM, asSize(padB));
|
|
110
|
+
if (dimSet(padL)) yn.setPadding(Yoga.EDGE_LEFT, asSize(padL));
|
|
111
|
+
// Scroll containers draw an overlay scrollbar at the right edge (4px
|
|
112
|
+
// strip: box.x + box.w - 4). Reserve the strip plus a 4px breathing gap
|
|
113
|
+
// as right padding so stretched children (align-items: stretch selects,
|
|
114
|
+
// cards) never lay out or paint against/under the scrollbar — browser
|
|
115
|
+
// behavior for classic scrollbars, which take layout space.
|
|
116
|
+
if (s.overflow === "scroll") {
|
|
117
|
+
const basePad = typeof padR === "number" ? padR : 0;
|
|
118
|
+
yn.setPadding(Yoga.EDGE_RIGHT, basePad + 8);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Margin: per-side longhands win over the `margin` shorthand when set.
|
|
122
|
+
// Full 1-4 value shorthand expansion; percentages resolve against the
|
|
123
|
+
// parent, and "auto" margins absorb free space (margin: 0 auto centers).
|
|
124
|
+
const [mgT, mgR, mgB, mgL] = expandBoxShorthand(s.margin);
|
|
125
|
+
const mTop: CssDim = s.marginTop !== undefined ? cssDimValue(s.marginTop) : mgT;
|
|
126
|
+
const mBottom: CssDim = s.marginBottom !== undefined ? cssDimValue(s.marginBottom) : mgB;
|
|
127
|
+
const mLeft: CssDim = s.marginLeft !== undefined ? cssDimValue(s.marginLeft) : mgL;
|
|
128
|
+
const mRight: CssDim = s.marginRight !== undefined ? cssDimValue(s.marginRight) : mgR;
|
|
129
|
+
if (dimSet(mTop)) yn.setMargin(Yoga.EDGE_TOP, mTop);
|
|
130
|
+
if (dimSet(mBottom)) yn.setMargin(Yoga.EDGE_BOTTOM, mBottom);
|
|
131
|
+
if (dimSet(mLeft)) yn.setMargin(Yoga.EDGE_LEFT, mLeft);
|
|
132
|
+
if (dimSet(mRight)) yn.setMargin(Yoga.EDGE_RIGHT, mRight);
|
|
133
|
+
|
|
134
|
+
// Gap: row-gap / column-gap are applied per-axis. Uniform `gap`
|
|
135
|
+
// (both equal) uses GUTTER_ALL for efficiency; mismatched values
|
|
136
|
+
// set GUTTER_ROW and GUTTER_COLUMN separately.
|
|
137
|
+
const rowGap = cssNum(s.rowGap);
|
|
138
|
+
const colGap = cssNum(s.columnGap);
|
|
139
|
+
if (rowGap && colGap && rowGap === colGap) {
|
|
140
|
+
yn.setGap(Yoga.GUTTER_ALL, rowGap);
|
|
141
|
+
} else {
|
|
142
|
+
if (rowGap) yn.setGap(Yoga.GUTTER_ROW, rowGap);
|
|
143
|
+
if (colGap) yn.setGap(Yoga.GUTTER_COLUMN, colGap);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Border
|
|
147
|
+
const borderW = cssBorderWidth(s.border);
|
|
148
|
+
if (borderW) {
|
|
149
|
+
yn.setBorder(Yoga.EDGE_ALL, borderW);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Align-self
|
|
153
|
+
if (s.alignSelf === "flex-start") yn.setAlignSelf(Yoga.ALIGN_FLEX_START);
|
|
154
|
+
else if (s.alignSelf === "center") yn.setAlignSelf(Yoga.ALIGN_CENTER);
|
|
155
|
+
else if (s.alignSelf === "stretch") yn.setAlignSelf(Yoga.ALIGN_STRETCH);
|
|
156
|
+
else if (s.alignSelf === "flex-end") yn.setAlignSelf(Yoga.ALIGN_FLEX_END);
|
|
157
|
+
else if (s.alignSelf === "baseline") yn.setAlignSelf(Yoga.ALIGN_BASELINE);
|
|
158
|
+
|
|
159
|
+
// Align-items / justify-content (container properties)
|
|
160
|
+
if (s.alignItems) {
|
|
161
|
+
if (s.alignItems === "flex-start") yn.setAlignItems(Yoga.ALIGN_FLEX_START);
|
|
162
|
+
else if (s.alignItems === "center") yn.setAlignItems(Yoga.ALIGN_CENTER);
|
|
163
|
+
else if (s.alignItems === "stretch") yn.setAlignItems(Yoga.ALIGN_STRETCH);
|
|
164
|
+
else if (s.alignItems === "flex-end") yn.setAlignItems(Yoga.ALIGN_FLEX_END);
|
|
165
|
+
}
|
|
166
|
+
// Align-content (multi-line flex-wrap cross-axis alignment)
|
|
167
|
+
if (s.alignContent) {
|
|
168
|
+
if (s.alignContent === "flex-start") yn.setAlignContent(Yoga.ALIGN_FLEX_START);
|
|
169
|
+
else if (s.alignContent === "center") yn.setAlignContent(Yoga.ALIGN_CENTER);
|
|
170
|
+
else if (s.alignContent === "flex-end") yn.setAlignContent(Yoga.ALIGN_FLEX_END);
|
|
171
|
+
else if (s.alignContent === "stretch") yn.setAlignContent(Yoga.ALIGN_STRETCH);
|
|
172
|
+
else if (s.alignContent === "space-between") yn.setAlignContent(Yoga.ALIGN_SPACE_BETWEEN);
|
|
173
|
+
else if (s.alignContent === "space-around") yn.setAlignContent(Yoga.ALIGN_SPACE_AROUND);
|
|
174
|
+
else if (s.alignContent === "space-evenly") yn.setAlignContent(Yoga.ALIGN_SPACE_EVENLY);
|
|
175
|
+
}
|
|
176
|
+
if (s.justifyContent) {
|
|
177
|
+
if (s.justifyContent === "flex-start") yn.setJustifyContent(Yoga.JUSTIFY_FLEX_START);
|
|
178
|
+
else if (s.justifyContent === "center") yn.setJustifyContent(Yoga.JUSTIFY_CENTER);
|
|
179
|
+
else if (s.justifyContent === "flex-end") yn.setJustifyContent(Yoga.JUSTIFY_FLEX_END);
|
|
180
|
+
else if (s.justifyContent === "space-between") yn.setJustifyContent(Yoga.JUSTIFY_SPACE_BETWEEN);
|
|
181
|
+
else if (s.justifyContent === "space-around") yn.setJustifyContent(Yoga.JUSTIFY_SPACE_AROUND);
|
|
182
|
+
else if (s.justifyContent === "space-evenly") yn.setJustifyContent(Yoga.JUSTIFY_SPACE_EVENLY);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Flex wrap
|
|
186
|
+
if (s.flexWrap === "wrap") yn.setFlexWrap(Yoga.WRAP_WRAP);
|
|
187
|
+
else if (s.flexWrap === "wrap-reverse") yn.setFlexWrap(Yoga.WRAP_WRAP_REVERSE);
|
|
188
|
+
else if (s.flexWrap === "nowrap") yn.setFlexWrap(Yoga.WRAP_NO_WRAP);
|
|
189
|
+
|
|
190
|
+
// Flex grow/shrink/basis
|
|
191
|
+
if (s.flexGrow) yn.setFlexGrow(cssNum(s.flexGrow));
|
|
192
|
+
if (s.flexShrink) yn.setFlexShrink(cssNum(s.flexShrink));
|
|
193
|
+
if (s.flexBasis) {
|
|
194
|
+
if (s.flexBasis === "auto") yn.setFlexBasisAuto();
|
|
195
|
+
else yn.setFlexBasis(cssSizeValue(s.flexBasis));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Order (Yoga may not expose setOrder in its types, but the runtime has it)
|
|
199
|
+
if (s.order) (yn as any).setOrder?.(cssNum(s.order));
|
|
200
|
+
|
|
201
|
+
// Min/max dimensions (px or % of the parent)
|
|
202
|
+
if (s.minWidth) yn.setMinWidth(cssSizeValue(s.minWidth));
|
|
203
|
+
if (s.maxWidth) yn.setMaxWidth(cssSizeValue(s.maxWidth));
|
|
204
|
+
if (s.minHeight) yn.setMinHeight(cssSizeValue(s.minHeight));
|
|
205
|
+
if (s.maxHeight) yn.setMaxHeight(cssSizeValue(s.maxHeight));
|
|
206
|
+
|
|
207
|
+
// Box sizing
|
|
208
|
+
if (s.boxSizing === "border-box") yn.setBoxSizing?.(Yoga.BOX_SIZING_BORDER_BOX);
|
|
209
|
+
|
|
210
|
+
// Overflow
|
|
211
|
+
if (s.overflow === "hidden") yn.setOverflow?.(Yoga.OVERFLOW_HIDDEN);
|
|
212
|
+
else if (s.overflow === "scroll") yn.setOverflow?.(Yoga.OVERFLOW_SCROLL);
|
|
213
|
+
|
|
214
|
+
// Position
|
|
215
|
+
if (s.position === "relative") yn.setPositionType(Yoga.POSITION_TYPE_RELATIVE);
|
|
216
|
+
else if (s.position === "absolute") yn.setPositionType(Yoga.POSITION_TYPE_ABSOLUTE);
|
|
217
|
+
else yn.setPositionType(Yoga.POSITION_TYPE_STATIC);
|
|
218
|
+
if (s.top !== undefined) yn.setPosition(Yoga.EDGE_TOP, cssSizeValue(s.top));
|
|
219
|
+
if (s.right !== undefined) yn.setPosition(Yoga.EDGE_RIGHT, cssSizeValue(s.right));
|
|
220
|
+
if (s.bottom !== undefined) yn.setPosition(Yoga.EDGE_BOTTOM, cssSizeValue(s.bottom));
|
|
221
|
+
if (s.left !== undefined) yn.setPosition(Yoga.EDGE_LEFT, cssSizeValue(s.left));
|
|
222
|
+
|
|
223
|
+
// Width/height (explicit; px or % of the parent)
|
|
224
|
+
if (s.width) yn.setWidth(cssSizeValue(s.width));
|
|
225
|
+
if (s.height) yn.setHeight(cssSizeValue(s.height));
|
|
226
|
+
const aspectRatio = parseAspectRatio(s.aspectRatio);
|
|
227
|
+
if (aspectRatio !== undefined) yn.setAspectRatio(aspectRatio);
|
|
228
|
+
|
|
229
|
+
// Children. The yoga binding in use has no setOrder API, so CSS `order`
|
|
230
|
+
// can't be applied via yoga — sort children by their order value first
|
|
231
|
+
// (stable sort: source order preserved for equal order) so the flex engine
|
|
232
|
+
// lays items out in their order sequence.
|
|
233
|
+
const orderedChildren = [...node.children].sort((a, b) => {
|
|
234
|
+
const ao = cssNum(a.style.order);
|
|
235
|
+
const bo = cssNum(b.style.order);
|
|
236
|
+
return ao - bo;
|
|
237
|
+
});
|
|
238
|
+
// Build yoga nodes in the sorted (visual) order so yoga positions them by
|
|
239
|
+
// order, but record the SOURCE index of each so extractBoxes can emit boxes
|
|
240
|
+
// in source-tree order (the contract lowerUIToModel relies on).
|
|
241
|
+
for (let si = 0; si < orderedChildren.length; si++) {
|
|
242
|
+
const child = orderedChildren[si];
|
|
243
|
+
const sourceIndex = node.children.indexOf(child);
|
|
244
|
+
const childNode = this.buildTree(child, metaArray, measureFn);
|
|
245
|
+
// Tag the yoga node with its source index so extraction can reorder.
|
|
246
|
+
(childNode as any).__sourceIndex = sourceIndex;
|
|
247
|
+
yn.insertChild(childNode, yn.getChildCount());
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Leaf nodes with text: let Yoga pass available width into measurement so
|
|
251
|
+
// wrapped text can expand height under constraints.
|
|
252
|
+
if (node.children.length === 0) {
|
|
253
|
+
// select is text-like too: its label is the current option, and the
|
|
254
|
+
// generic else-branch sizes the width without the UA control padding —
|
|
255
|
+
// a wider option ("Gamma" vs "Alpha" in a proportional font) then
|
|
256
|
+
// overflows the box and wraps mid-word.
|
|
257
|
+
const textLike = node.tag === "text" || node.tag === "button" || node.tag === "check" || node.tag === "radio" || node.tag === "select";
|
|
258
|
+
if (textLike) {
|
|
259
|
+
yn.setMeasureFunc((width: number, widthMode: number) => {
|
|
260
|
+
const hasWidth = widthMode !== Yoga.MEASURE_MODE_UNDEFINED && Number.isFinite(width) && width > 0;
|
|
261
|
+
const intrinsic = measureFn(node, hasWidth ? width : undefined);
|
|
262
|
+
return {
|
|
263
|
+
width: Math.ceil(intrinsic.w),
|
|
264
|
+
height: Math.ceil(intrinsic.h),
|
|
265
|
+
};
|
|
266
|
+
});
|
|
267
|
+
} else {
|
|
268
|
+
const intrinsic = measureFn(node);
|
|
269
|
+
const [padTop, , , padLeft] = expandBoxShorthand(s.padding);
|
|
270
|
+
const childPadV = typeof padTop === "number" ? padTop : 0;
|
|
271
|
+
const childPadH = typeof padLeft === "number" ? padLeft : 0;
|
|
272
|
+
const childBorder = cssBorderWidth(s.border);
|
|
273
|
+
if (!s.width && intrinsic.w > 0) {
|
|
274
|
+
yn.setWidth(Math.ceil(intrinsic.w + childPadH * 2 + childBorder * 2));
|
|
275
|
+
}
|
|
276
|
+
if (!s.height && intrinsic.h > 0) {
|
|
277
|
+
yn.setHeight(intrinsic.h + childPadV * 2 + childBorder * 2);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return yn;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** Walk the Yoga tree in pre-order DFS, extracting absolute {x,y,w,h} per node.
|
|
286
|
+
* Yoga returns positions relative to the parent — we accumulate parentX/Y
|
|
287
|
+
* to produce absolute coordinates. Must match the StyledNode tree's DFS. */
|
|
288
|
+
private extractBoxes(
|
|
289
|
+
node: any,
|
|
290
|
+
out: Box[],
|
|
291
|
+
nextMeta: () => NodeMeta | undefined,
|
|
292
|
+
parentX: number = 0,
|
|
293
|
+
parentY: number = 0,
|
|
294
|
+
): void {
|
|
295
|
+
const x = parentX + Math.round(node.getComputedLeft());
|
|
296
|
+
const y = parentY + Math.round(node.getComputedTop());
|
|
297
|
+
const w = Math.round(node.getComputedWidth());
|
|
298
|
+
const h = Math.round(node.getComputedHeight());
|
|
299
|
+
out.push({ x, y, w, h });
|
|
300
|
+
|
|
301
|
+
nextMeta();
|
|
302
|
+
|
|
303
|
+
for (let i = 0; i < node.getChildCount(); i++) {
|
|
304
|
+
this.extractBoxes(node.getChild(i), out, nextMeta, x, y);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|