@alchemy.run/sigil 0.0.0-alpha.1 → 0.0.0-alpha.2

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.
Files changed (121) hide show
  1. package/README.md +299 -299
  2. package/dist/ansi.d.ts +223 -0
  3. package/dist/ansi.js +2 -0
  4. package/dist/{devtools-QpCMm9JH.mjs → devtools-BhYGjb7h.js} +1 -1
  5. package/dist/index-DDVME65c.d.ts +919 -0
  6. package/dist/index.d.ts +1657 -0
  7. package/dist/index.js +4643 -0
  8. package/dist/sgr-CMfEpjSk.d.ts +91 -0
  9. package/dist/truncate-CBiyyZzw.js +2156 -0
  10. package/dist/yoga-5jKhYCJC.js +3465 -0
  11. package/dist/yoga.d.ts +2 -0
  12. package/dist/yoga.js +2 -0
  13. package/package.json +37 -17
  14. package/src/ansi/chalk.ts +179 -0
  15. package/src/ansi/cursor.ts +48 -0
  16. package/src/ansi/east-asian-width.ts +215 -0
  17. package/src/ansi/escapes.ts +128 -0
  18. package/src/ansi/index.ts +27 -0
  19. package/src/ansi/sgr.ts +237 -0
  20. package/src/ansi/slice.ts +43 -0
  21. package/src/ansi/string-width.ts +236 -0
  22. package/src/ansi/strip.ts +33 -0
  23. package/src/ansi/supports-color.ts +213 -0
  24. package/src/ansi/tokenize.ts +453 -0
  25. package/src/ansi/truncate.ts +194 -0
  26. package/src/ansi/widest-line.ts +12 -0
  27. package/src/ansi/wrap.ts +766 -0
  28. package/src/ansi-tokenizer.ts +510 -0
  29. package/src/auto-bind.ts +41 -0
  30. package/src/boxes.ts +100 -0
  31. package/src/code-excerpt.ts +39 -0
  32. package/src/colorize.ts +60 -0
  33. package/src/components/AccessibilityContext.ts +5 -0
  34. package/src/components/AnimationContext.ts +24 -0
  35. package/src/components/App.tsx +781 -0
  36. package/src/components/AppContext.ts +111 -0
  37. package/src/components/BackgroundContext.ts +8 -0
  38. package/src/components/Box.tsx +116 -0
  39. package/src/components/CursorContext.ts +19 -0
  40. package/src/components/ErrorBoundary.tsx +38 -0
  41. package/src/components/ErrorOverview.tsx +133 -0
  42. package/src/components/FocusContext.ts +30 -0
  43. package/src/components/Newline.tsx +15 -0
  44. package/src/components/Spacer.tsx +10 -0
  45. package/src/components/Static.tsx +59 -0
  46. package/src/components/StderrContext.ts +26 -0
  47. package/src/components/StdinContext.ts +49 -0
  48. package/src/components/StdoutContext.ts +28 -0
  49. package/src/components/Text.tsx +144 -0
  50. package/src/components/Transform.tsx +37 -0
  51. package/src/cursor-position.ts +103 -0
  52. package/src/devtools-window-polyfill.ts +73 -0
  53. package/src/devtools.ts +43 -0
  54. package/src/dom.ts +292 -0
  55. package/src/get-max-width.ts +11 -0
  56. package/src/global.d.ts +36 -0
  57. package/src/hooks/use-animation.ts +142 -0
  58. package/src/hooks/use-app.ts +8 -0
  59. package/src/hooks/use-box-metrics.ts +134 -0
  60. package/src/hooks/use-cursor.ts +33 -0
  61. package/src/hooks/use-focus-manager.ts +62 -0
  62. package/src/hooks/use-focus.ts +83 -0
  63. package/src/hooks/use-input.ts +267 -0
  64. package/src/hooks/use-is-screen-reader-enabled.ts +12 -0
  65. package/src/hooks/use-paste.ts +78 -0
  66. package/src/hooks/use-stderr.ts +8 -0
  67. package/src/hooks/use-stdin.ts +10 -0
  68. package/src/hooks/use-stdout.ts +8 -0
  69. package/src/hooks/use-window-size.ts +41 -0
  70. package/src/indent-string.ts +16 -0
  71. package/src/index.ts +44 -0
  72. package/src/ink.tsx +1506 -0
  73. package/src/input-parser.ts +283 -0
  74. package/src/instances.ts +9 -0
  75. package/src/is-in-ci.ts +7 -0
  76. package/src/kitty-keyboard.ts +57 -0
  77. package/src/log-update.ts +370 -0
  78. package/src/measure-element.ts +62 -0
  79. package/src/measure-text.ts +31 -0
  80. package/src/output.ts +308 -0
  81. package/src/parse-keypress.ts +516 -0
  82. package/src/parse-stack-line.ts +139 -0
  83. package/src/patch-console.ts +62 -0
  84. package/src/quick-lru.ts +85 -0
  85. package/src/reconciler.ts +451 -0
  86. package/src/render-background.ts +38 -0
  87. package/src/render-border.ts +134 -0
  88. package/src/render-node-to-output.ts +191 -0
  89. package/src/render-to-string.ts +131 -0
  90. package/src/render.ts +276 -0
  91. package/src/renderer.ts +73 -0
  92. package/src/sanitize-ansi.ts +33 -0
  93. package/src/signal-exit.ts +107 -0
  94. package/src/squash-text-nodes.ts +40 -0
  95. package/src/stream.ts +30 -0
  96. package/src/styles.ts +748 -0
  97. package/src/terminal-size.ts +57 -0
  98. package/src/throttle.ts +73 -0
  99. package/src/types.ts +15 -0
  100. package/src/utils.ts +40 -0
  101. package/src/wrap-text.ts +50 -0
  102. package/src/write-synchronized.ts +9 -0
  103. package/src/yoga/config.ts +57 -0
  104. package/src/yoga/core/absoluteLayout.ts +626 -0
  105. package/src/yoga/core/baseline.ts +66 -0
  106. package/src/yoga/core/cache.ts +136 -0
  107. package/src/yoga/core/calculateLayout.ts +2920 -0
  108. package/src/yoga/core/config.ts +104 -0
  109. package/src/yoga/core/flexLine.ts +177 -0
  110. package/src/yoga/core/helpers.ts +293 -0
  111. package/src/yoga/core/layoutResults.ts +167 -0
  112. package/src/yoga/core/node.ts +611 -0
  113. package/src/yoga/core/numeric.ts +44 -0
  114. package/src/yoga/core/pixelGrid.ts +151 -0
  115. package/src/yoga/core/style.ts +887 -0
  116. package/src/yoga/core/types.ts +224 -0
  117. package/src/yoga/generated/YGEnums.ts +263 -0
  118. package/src/yoga/index.ts +19 -0
  119. package/src/yoga/node.ts +1140 -0
  120. package/dist/index.d.mts +0 -2379
  121. package/dist/index.mjs +0 -10072
@@ -0,0 +1,2920 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Derived from Yoga. See THIRD_PARTY_NOTICES.md.
3
+
4
+ // Port of yoga/algorithm/CalculateLayout.cpp. The event system and
5
+ // LayoutPassReason-keyed instrumentation are reduced to plain counters.
6
+
7
+ import {
8
+ Align,
9
+ Dimension,
10
+ Direction,
11
+ Display,
12
+ Edge,
13
+ Errata,
14
+ ExperimentalFeature,
15
+ FlexDirection,
16
+ Gutter,
17
+ Justify,
18
+ MeasureMode,
19
+ Overflow,
20
+ PositionType,
21
+ Wrap,
22
+ } from "../generated/YGEnums.ts";
23
+ import { layoutAbsoluteDescendants } from "./absoluteLayout.ts";
24
+ import { calculateBaseline, isBaselineLayout } from "./baseline.ts";
25
+ import { canUseCachedMeasurement } from "./cache.ts";
26
+ import { type FlexLine, calculateFlexLine } from "./flexLine.ts";
27
+ import {
28
+ PhysicalEdge,
29
+ SizingMode,
30
+ boundAxis,
31
+ boundAxisWithinMinAndMax,
32
+ dimension,
33
+ fallbackAlignment,
34
+ fallbackJustification,
35
+ flexStartEdge,
36
+ isColumn,
37
+ isRow,
38
+ measureMode,
39
+ needsTrailingPosition,
40
+ paddingAndBorderForAxis,
41
+ resolveChildAlignment,
42
+ resolveCrossDirection,
43
+ resolveDirection,
44
+ setChildTrailingPosition,
45
+ } from "./helpers.ts";
46
+ import { CachedMeasurement, LayoutResults } from "./layoutResults.ts";
47
+ import type { Node } from "./node.ts";
48
+ import { isDefined, isUndefined, inexactEquals, maxOrDefined, minOrDefined } from "./numeric.ts";
49
+ import { roundLayoutResultsToPixelGrid } from "./pixelGrid.ts";
50
+ import type { Style } from "./style.ts";
51
+ import type { StyleLength } from "./types.ts";
52
+
53
+ export const LayoutPassReason = {
54
+ Initial: 0,
55
+ AbsLayout: 1,
56
+ Stretch: 2,
57
+ MultilineStretch: 3,
58
+ FlexLayout: 4,
59
+ MeasureChild: 5,
60
+ AbsMeasureChild: 6,
61
+ FlexMeasure: 7,
62
+ } as const;
63
+ export type LayoutPassReason = (typeof LayoutPassReason)[keyof typeof LayoutPassReason];
64
+
65
+ export interface LayoutData {
66
+ layouts: number;
67
+ measures: number;
68
+ maxMeasureCache: number;
69
+ cachedLayouts: number;
70
+ cachedMeasures: number;
71
+ measureCallbacks: number;
72
+ }
73
+
74
+ export function newLayoutData(): LayoutData {
75
+ return {
76
+ layouts: 0,
77
+ measures: 0,
78
+ maxMeasureCache: 0,
79
+ cachedLayouts: 0,
80
+ cachedMeasures: 0,
81
+ measureCallbacks: 0,
82
+ };
83
+ }
84
+
85
+ let gCurrentGenerationCount = 0;
86
+
87
+ function hasAutoHorizontalMargin(style: Style): boolean {
88
+ for (const direction of [Direction.LTR, Direction.RTL]) {
89
+ if (
90
+ style.flexStartMarginIsAuto(FlexDirection.Row, direction) ||
91
+ style.flexEndMarginIsAuto(FlexDirection.Row, direction)
92
+ ) {
93
+ return true;
94
+ }
95
+ }
96
+ return false;
97
+ }
98
+
99
+ function isColumnStretchEdge(owner: Node | null, child: Node | null): boolean {
100
+ if (owner === null || child === null) {
101
+ return false;
102
+ }
103
+ const ownerStyle = owner.style;
104
+ const childStyle = child.style;
105
+ const childWidth = child.getProcessedDimension(Dimension.Width);
106
+ return (
107
+ ownerStyle.display() === Display.Flex &&
108
+ isColumn(ownerStyle.flexDirection()) &&
109
+ ownerStyle.flexWrap() === Wrap.NoWrap &&
110
+ childStyle.positionType() !== PositionType.Absolute &&
111
+ !isDefined(childStyle.aspectRatio()) &&
112
+ (childWidth.isAuto() || childWidth.isUndefined()) &&
113
+ !hasAutoHorizontalMargin(childStyle) &&
114
+ resolveChildAlignment(owner, child) === Align.Stretch
115
+ );
116
+ }
117
+
118
+ function isInColumnStretchScrollSubtree(node: Node): boolean {
119
+ let current: Node | null = node;
120
+ while (current !== null) {
121
+ let owner: Node | null = current.owner;
122
+ while (owner !== null && owner.style.display() === Display.Contents) {
123
+ owner = owner.owner;
124
+ }
125
+ if (owner === null || !isColumnStretchEdge(owner, current)) {
126
+ return false;
127
+ }
128
+ if (owner.style.overflow() === Overflow.Scroll) {
129
+ return true;
130
+ }
131
+ current = owner;
132
+ }
133
+ return false;
134
+ }
135
+
136
+ function isNonZeroLength(length: StyleLength): boolean {
137
+ return length.isAuto() || (isDefined(length.value) && length.value !== 0);
138
+ }
139
+
140
+ function hasNonZeroVerticalSpacing(style: Style): boolean {
141
+ const verticalEdges = [Edge.Top, Edge.Bottom, Edge.Vertical, Edge.All];
142
+ for (const edge of verticalEdges) {
143
+ if (
144
+ isNonZeroLength(style.margin(edge)) ||
145
+ isNonZeroLength(style.padding(edge)) ||
146
+ isNonZeroLength(style.border(edge))
147
+ ) {
148
+ return true;
149
+ }
150
+ }
151
+ return false;
152
+ }
153
+
154
+ function hasPercentageLength(style: Style): boolean {
155
+ const edges = [
156
+ Edge.Left,
157
+ Edge.Top,
158
+ Edge.Right,
159
+ Edge.Bottom,
160
+ Edge.Start,
161
+ Edge.End,
162
+ Edge.Horizontal,
163
+ Edge.Vertical,
164
+ Edge.All,
165
+ ];
166
+ for (const edge of edges) {
167
+ if (
168
+ style.margin(edge).isPercent() ||
169
+ style.position(edge).isPercent() ||
170
+ style.padding(edge).isPercent() ||
171
+ style.border(edge).isPercent()
172
+ ) {
173
+ return true;
174
+ }
175
+ }
176
+
177
+ for (const dim of [Dimension.Width, Dimension.Height]) {
178
+ if (
179
+ style.dimension(dim).isPercent() ||
180
+ style.minDimension(dim).isPercent() ||
181
+ style.maxDimension(dim).isPercent()
182
+ ) {
183
+ return true;
184
+ }
185
+ }
186
+
187
+ return (
188
+ style.flexBasis().isPercent() ||
189
+ style.gap(Gutter.Column).isPercent() ||
190
+ style.gap(Gutter.Row).isPercent() ||
191
+ style.gap(Gutter.All).isPercent()
192
+ );
193
+ }
194
+
195
+ function hasNonZeroFlex(node: Node): boolean {
196
+ const style = node.style;
197
+ const flex = style.flex();
198
+ const flexGrow = style.flexGrow();
199
+ const flexShrink = style.flexShrink();
200
+ const config = node.config;
201
+ const canGrow = isDefined(flexGrow) ? flexGrow !== 0 : isDefined(flex) && flex > 0;
202
+ const canShrink = isDefined(flexShrink)
203
+ ? flexShrink !== 0
204
+ : config.useWebDefaults() || (isDefined(flex) && flex < 0);
205
+ return canGrow || canShrink;
206
+ }
207
+
208
+ function isHeightFitContentIndependent(node: Node): boolean {
209
+ const style = node.style;
210
+ const height = style.dimension(Dimension.Height);
211
+ const flexBasis = style.flexBasis();
212
+ const hasRelativePercentPosition =
213
+ style.position(Edge.Top).isPercent() ||
214
+ style.position(Edge.Bottom).isPercent() ||
215
+ style.position(Edge.Vertical).isPercent() ||
216
+ style.position(Edge.All).isPercent();
217
+ return (
218
+ !node.hasMeasureFunc() &&
219
+ !node.hasBaselineFunc() &&
220
+ !node.isReferenceBaseline_ &&
221
+ (height.isAuto() || height.isUndefined()) &&
222
+ style.minDimension(Dimension.Height).isUndefined() &&
223
+ style.maxDimension(Dimension.Height).isUndefined() &&
224
+ (flexBasis.isAuto() || flexBasis.isUndefined()) &&
225
+ !hasNonZeroFlex(node) &&
226
+ style.boxSizing() === 0 /* BoxSizing.BorderBox */ &&
227
+ !isDefined(style.aspectRatio()) &&
228
+ style.positionType() !== PositionType.Absolute &&
229
+ style.overflow() !== Overflow.Scroll &&
230
+ style.display() === Display.Flex &&
231
+ isColumn(style.flexDirection()) &&
232
+ style.alignItems() === Align.Stretch &&
233
+ (style.alignSelf() === Align.Auto || style.alignSelf() === Align.Stretch) &&
234
+ style.justifyContent() === Justify.FlexStart &&
235
+ style.flexWrap() === Wrap.NoWrap &&
236
+ !style.gap(Gutter.All).isDefined() &&
237
+ !style.gap(Gutter.Row).isDefined() &&
238
+ !hasRelativePercentPosition &&
239
+ !hasNonZeroVerticalSpacing(style) &&
240
+ !hasPercentageLength(style)
241
+ );
242
+ }
243
+
244
+ function canSkipHeightFitContent(root: Node | null): boolean {
245
+ if (root === null) {
246
+ return false;
247
+ }
248
+
249
+ const maxPendingNodes = 64;
250
+ const stack: Node[] = [root];
251
+ while (stack.length > 0) {
252
+ const node = stack.pop()!;
253
+ if (!isHeightFitContentIndependent(node)) {
254
+ return false;
255
+ }
256
+ for (const child of node.getLayoutChildren()) {
257
+ if (stack.length === maxPendingNodes) {
258
+ return false;
259
+ }
260
+ stack.push(child);
261
+ }
262
+ }
263
+ return true;
264
+ }
265
+
266
+ interface ModeAndSize {
267
+ mode: SizingMode;
268
+ size: number;
269
+ }
270
+
271
+ // Reusable in/out pairs for constrainMaxSizeForMode. Their values are always
272
+ // copied out before any recursion into calculateLayoutInternal, so a single
273
+ // pair per (width, height) role is safe despite the recursive algorithm.
274
+ const scratchModeA: ModeAndSize = { mode: SizingMode.StretchFit, size: 0 };
275
+ const scratchModeB: ModeAndSize = { mode: SizingMode.StretchFit, size: 0 };
276
+
277
+ export function constrainMaxSizeForMode(
278
+ node: Node,
279
+ direction: Direction,
280
+ axis: FlexDirection,
281
+ ownerAxisSize: number,
282
+ ownerWidth: number,
283
+ modeAndSize: ModeAndSize,
284
+ ): void {
285
+ const maxSize =
286
+ node.style.resolvedMaxDimension(direction, dimension(axis), ownerAxisSize, ownerWidth) +
287
+ node.style.computeMarginForAxis(axis, ownerWidth);
288
+ switch (modeAndSize.mode) {
289
+ case SizingMode.StretchFit:
290
+ case SizingMode.FitContent:
291
+ modeAndSize.size =
292
+ isUndefined(maxSize) || modeAndSize.size < maxSize ? modeAndSize.size : maxSize;
293
+ break;
294
+ case SizingMode.MaxContent:
295
+ if (isDefined(maxSize)) {
296
+ modeAndSize.mode = SizingMode.FitContent;
297
+ modeAndSize.size = maxSize;
298
+ }
299
+ break;
300
+ }
301
+ }
302
+
303
+ function computeFlexBasisForChild(
304
+ node: Node,
305
+ child: Node,
306
+ width: number,
307
+ widthMode: SizingMode,
308
+ height: number,
309
+ ownerWidth: number,
310
+ ownerHeight: number,
311
+ heightMode: SizingMode,
312
+ direction: Direction,
313
+ layoutMarkerData: LayoutData,
314
+ depth: number,
315
+ generationCount: number,
316
+ ): void {
317
+ const mainAxis = resolveDirection(node.style.flexDirection(), direction);
318
+ const isMainAxisRow = isRow(mainAxis);
319
+ const mainAxisSize = isMainAxisRow ? width : height;
320
+ const mainAxisOwnerSize = isMainAxisRow ? ownerWidth : ownerHeight;
321
+
322
+ let childWidth = NaN;
323
+ let childHeight = NaN;
324
+ let childWidthSizingMode: SizingMode;
325
+ let childHeightSizingMode: SizingMode;
326
+
327
+ const resolvedFlexBasis = child.resolveFlexBasis(
328
+ direction,
329
+ mainAxis,
330
+ mainAxisOwnerSize,
331
+ ownerWidth,
332
+ );
333
+ const isRowStyleDimDefined = child.hasDefiniteLength(Dimension.Width, ownerWidth);
334
+ const isColumnStyleDimDefined = child.hasDefiniteLength(Dimension.Height, ownerHeight);
335
+
336
+ const fixFlexBasisFitContent = node.config.isExperimentalFeatureEnabled(
337
+ ExperimentalFeature.FixFlexBasisFitContent,
338
+ );
339
+
340
+ const useResolvedFlexBasis = isDefined(resolvedFlexBasis) && isDefined(mainAxisSize);
341
+
342
+ if (useResolvedFlexBasis) {
343
+ if (
344
+ isUndefined(child.layout.computedFlexBasis) ||
345
+ (child.config.isExperimentalFeatureEnabled(ExperimentalFeature.WebFlexBasis) &&
346
+ child.layout.computedFlexBasisGeneration !== generationCount)
347
+ ) {
348
+ const paddingAndBorder = paddingAndBorderForAxis(child, mainAxis, direction, ownerWidth);
349
+ child.setLayoutComputedFlexBasis(maxOrDefined(resolvedFlexBasis, paddingAndBorder));
350
+ }
351
+ } else if (isMainAxisRow && isRowStyleDimDefined) {
352
+ // The width is definite, so use that as the flex basis.
353
+ const paddingAndBorder = paddingAndBorderForAxis(
354
+ child,
355
+ FlexDirection.Row,
356
+ direction,
357
+ ownerWidth,
358
+ );
359
+
360
+ child.setLayoutComputedFlexBasis(
361
+ maxOrDefined(
362
+ child.getResolvedDimension(direction, Dimension.Width, ownerWidth, ownerWidth),
363
+ paddingAndBorder,
364
+ ),
365
+ );
366
+ } else if (!isMainAxisRow && isColumnStyleDimDefined) {
367
+ // The height is definite, so use that as the flex basis.
368
+ const paddingAndBorder = paddingAndBorderForAxis(
369
+ child,
370
+ FlexDirection.Column,
371
+ direction,
372
+ ownerWidth,
373
+ );
374
+ child.setLayoutComputedFlexBasis(
375
+ maxOrDefined(
376
+ child.getResolvedDimension(direction, Dimension.Height, ownerHeight, ownerWidth),
377
+ paddingAndBorder,
378
+ ),
379
+ );
380
+ } else {
381
+ // Compute the flex basis and hypothetical main size (i.e. the clamped flex
382
+ // basis).
383
+ childWidthSizingMode = SizingMode.MaxContent;
384
+ childHeightSizingMode = SizingMode.MaxContent;
385
+
386
+ const marginRow = child.style.computeMarginForAxis(FlexDirection.Row, ownerWidth);
387
+ const marginColumn = child.style.computeMarginForAxis(FlexDirection.Column, ownerWidth);
388
+
389
+ if (isRowStyleDimDefined) {
390
+ childWidth =
391
+ child.getResolvedDimension(direction, Dimension.Width, ownerWidth, ownerWidth) + marginRow;
392
+ childWidthSizingMode = SizingMode.StretchFit;
393
+ }
394
+ if (isColumnStyleDimDefined) {
395
+ childHeight =
396
+ child.getResolvedDimension(direction, Dimension.Height, ownerHeight, ownerWidth) +
397
+ marginColumn;
398
+ childHeightSizingMode = SizingMode.StretchFit;
399
+ }
400
+
401
+ // The W3C spec doesn't say anything about the 'overflow' property, but all
402
+ // major browsers appear to implement the following logic.
403
+ if (
404
+ (!isMainAxisRow && node.style.overflow() === Overflow.Scroll) ||
405
+ node.style.overflow() !== Overflow.Scroll
406
+ ) {
407
+ if (isUndefined(childWidth) && isDefined(width)) {
408
+ childWidth = width;
409
+ childWidthSizingMode = SizingMode.FitContent;
410
+ }
411
+ }
412
+
413
+ // A zero-intrinsic-height column subtree has the same layout with an
414
+ // unbounded height, allowing its measurement cache to survive unrelated
415
+ // size changes elsewhere in a vertical scroll subtree.
416
+ const parentDoesNotScroll = node.style.overflow() !== Overflow.Scroll;
417
+ let applyHeightFitContent = isMainAxisRow || parentDoesNotScroll;
418
+ if (fixFlexBasisFitContent) {
419
+ const childHadOverflow = child.isDirty() && child.layout.hadOverflow();
420
+ const hasHeightIndependentSubtree =
421
+ !isMainAxisRow &&
422
+ parentDoesNotScroll &&
423
+ isUndefined(childHeight) &&
424
+ isDefined(height) &&
425
+ isColumnStretchEdge(node, child) &&
426
+ isInColumnStretchScrollSubtree(node) &&
427
+ canSkipHeightFitContent(child);
428
+ if (hasHeightIndependentSubtree && childHadOverflow) {
429
+ child.setLayoutHadOverflow(false);
430
+ }
431
+ if (hasHeightIndependentSubtree) {
432
+ applyHeightFitContent = false;
433
+ }
434
+ }
435
+ if (applyHeightFitContent && isUndefined(childHeight) && isDefined(height)) {
436
+ childHeight = height;
437
+ childHeightSizingMode = SizingMode.FitContent;
438
+ }
439
+
440
+ const childStyle = child.style;
441
+ if (isDefined(childStyle.aspectRatio())) {
442
+ if (!isMainAxisRow && childWidthSizingMode === SizingMode.StretchFit) {
443
+ childHeight = marginColumn + (childWidth - marginRow) / childStyle.aspectRatio();
444
+ childHeightSizingMode = SizingMode.StretchFit;
445
+ } else if (isMainAxisRow && childHeightSizingMode === SizingMode.StretchFit) {
446
+ childWidth = marginRow + (childHeight - marginColumn) * childStyle.aspectRatio();
447
+ childWidthSizingMode = SizingMode.StretchFit;
448
+ }
449
+ }
450
+
451
+ // If child has no defined size in the cross axis and is set to stretch,
452
+ // set the cross axis to be measured exactly with the available inner width
453
+
454
+ const hasExactWidth = isDefined(width) && widthMode === SizingMode.StretchFit;
455
+ const childWidthStretch =
456
+ resolveChildAlignment(node, child) === Align.Stretch &&
457
+ childWidthSizingMode !== SizingMode.StretchFit;
458
+ if (!isMainAxisRow && !isRowStyleDimDefined && hasExactWidth && childWidthStretch) {
459
+ childWidth = width;
460
+ childWidthSizingMode = SizingMode.StretchFit;
461
+ if (isDefined(childStyle.aspectRatio())) {
462
+ childHeight = (childWidth - marginRow) / childStyle.aspectRatio();
463
+ childHeightSizingMode = SizingMode.StretchFit;
464
+ }
465
+ }
466
+
467
+ const hasExactHeight = isDefined(height) && heightMode === SizingMode.StretchFit;
468
+ const childHeightStretch =
469
+ resolveChildAlignment(node, child) === Align.Stretch &&
470
+ childHeightSizingMode !== SizingMode.StretchFit;
471
+ if (isMainAxisRow && !isColumnStyleDimDefined && hasExactHeight && childHeightStretch) {
472
+ childHeight = height;
473
+ childHeightSizingMode = SizingMode.StretchFit;
474
+
475
+ if (isDefined(childStyle.aspectRatio())) {
476
+ childWidth = (childHeight - marginColumn) * childStyle.aspectRatio();
477
+ childWidthSizingMode = SizingMode.StretchFit;
478
+ }
479
+ }
480
+
481
+ const widthModeAndSize = scratchModeA;
482
+ widthModeAndSize.mode = childWidthSizingMode;
483
+ widthModeAndSize.size = childWidth;
484
+ constrainMaxSizeForMode(
485
+ child,
486
+ direction,
487
+ FlexDirection.Row,
488
+ ownerWidth,
489
+ ownerWidth,
490
+ widthModeAndSize,
491
+ );
492
+ const heightModeAndSize = scratchModeB;
493
+ heightModeAndSize.mode = childHeightSizingMode;
494
+ heightModeAndSize.size = childHeight;
495
+ constrainMaxSizeForMode(
496
+ child,
497
+ direction,
498
+ FlexDirection.Column,
499
+ ownerHeight,
500
+ ownerWidth,
501
+ heightModeAndSize,
502
+ );
503
+
504
+ // Measure the child
505
+ calculateLayoutInternal(
506
+ child,
507
+ widthModeAndSize.size,
508
+ heightModeAndSize.size,
509
+ direction,
510
+ widthModeAndSize.mode,
511
+ heightModeAndSize.mode,
512
+ ownerWidth,
513
+ ownerHeight,
514
+ false,
515
+ LayoutPassReason.MeasureChild,
516
+ layoutMarkerData,
517
+ depth,
518
+ generationCount,
519
+ );
520
+
521
+ child.setLayoutComputedFlexBasis(
522
+ maxOrDefined(
523
+ child.layout.measuredDimension(dimension(mainAxis)),
524
+ paddingAndBorderForAxis(child, mainAxis, direction, ownerWidth),
525
+ ),
526
+ );
527
+ }
528
+ child.setLayoutComputedFlexBasisGeneration(generationCount);
529
+ }
530
+
531
+ function measureNodeWithMeasureFunc(
532
+ node: Node,
533
+ direction: Direction,
534
+ availableWidth: number,
535
+ availableHeight: number,
536
+ widthSizingMode: SizingMode,
537
+ heightSizingMode: SizingMode,
538
+ ownerWidth: number,
539
+ ownerHeight: number,
540
+ layoutMarkerData: LayoutData,
541
+ ): void {
542
+ if (widthSizingMode === SizingMode.MaxContent) {
543
+ availableWidth = NaN;
544
+ }
545
+ if (heightSizingMode === SizingMode.MaxContent) {
546
+ availableHeight = NaN;
547
+ }
548
+
549
+ const layout = node.layout;
550
+ const paddingAndBorderAxisRow =
551
+ layout.padding(PhysicalEdge.Left) +
552
+ layout.padding(PhysicalEdge.Right) +
553
+ layout.border(PhysicalEdge.Left) +
554
+ layout.border(PhysicalEdge.Right);
555
+ const paddingAndBorderAxisColumn =
556
+ layout.padding(PhysicalEdge.Top) +
557
+ layout.padding(PhysicalEdge.Bottom) +
558
+ layout.border(PhysicalEdge.Top) +
559
+ layout.border(PhysicalEdge.Bottom);
560
+
561
+ // We want to make sure we don't call measure with negative size
562
+ const innerWidth = isUndefined(availableWidth)
563
+ ? availableWidth
564
+ : maxOrDefined(0, availableWidth - paddingAndBorderAxisRow);
565
+ const innerHeight = isUndefined(availableHeight)
566
+ ? availableHeight
567
+ : maxOrDefined(0, availableHeight - paddingAndBorderAxisColumn);
568
+
569
+ if (widthSizingMode === SizingMode.StretchFit && heightSizingMode === SizingMode.StretchFit) {
570
+ // Don't bother sizing the text if both dimensions are already defined.
571
+ node.setLayoutMeasuredDimension(
572
+ boundAxis(node, FlexDirection.Row, direction, availableWidth, ownerWidth, ownerWidth),
573
+ Dimension.Width,
574
+ );
575
+ node.setLayoutMeasuredDimension(
576
+ boundAxis(node, FlexDirection.Column, direction, availableHeight, ownerHeight, ownerWidth),
577
+ Dimension.Height,
578
+ );
579
+ } else {
580
+ // Measure the text under the current constraints.
581
+ const measuredSize = node.measure(
582
+ innerWidth,
583
+ measureMode(widthSizingMode),
584
+ innerHeight,
585
+ measureMode(heightSizingMode),
586
+ );
587
+
588
+ layoutMarkerData.measureCallbacks += 1;
589
+
590
+ node.setLayoutMeasuredDimension(
591
+ boundAxis(
592
+ node,
593
+ FlexDirection.Row,
594
+ direction,
595
+ widthSizingMode === SizingMode.MaxContent || widthSizingMode === SizingMode.FitContent
596
+ ? measuredSize.width + paddingAndBorderAxisRow
597
+ : availableWidth,
598
+ ownerWidth,
599
+ ownerWidth,
600
+ ),
601
+ Dimension.Width,
602
+ );
603
+
604
+ node.setLayoutMeasuredDimension(
605
+ boundAxis(
606
+ node,
607
+ FlexDirection.Column,
608
+ direction,
609
+ heightSizingMode === SizingMode.MaxContent || heightSizingMode === SizingMode.FitContent
610
+ ? measuredSize.height + paddingAndBorderAxisColumn
611
+ : availableHeight,
612
+ ownerHeight,
613
+ ownerWidth,
614
+ ),
615
+ Dimension.Height,
616
+ );
617
+ }
618
+ }
619
+
620
+ // For nodes with no children, use the available values if they were provided,
621
+ // or the minimum size as indicated by the padding and border sizes.
622
+ function measureNodeWithoutChildren(
623
+ node: Node,
624
+ direction: Direction,
625
+ availableWidth: number,
626
+ availableHeight: number,
627
+ widthSizingMode: SizingMode,
628
+ heightSizingMode: SizingMode,
629
+ ownerWidth: number,
630
+ ownerHeight: number,
631
+ ): void {
632
+ const layout = node.layout;
633
+
634
+ let width = availableWidth;
635
+ if (widthSizingMode === SizingMode.MaxContent || widthSizingMode === SizingMode.FitContent) {
636
+ width =
637
+ layout.padding(PhysicalEdge.Left) +
638
+ layout.padding(PhysicalEdge.Right) +
639
+ layout.border(PhysicalEdge.Left) +
640
+ layout.border(PhysicalEdge.Right);
641
+ }
642
+ node.setLayoutMeasuredDimension(
643
+ boundAxis(node, FlexDirection.Row, direction, width, ownerWidth, ownerWidth),
644
+ Dimension.Width,
645
+ );
646
+
647
+ let height = availableHeight;
648
+ if (heightSizingMode === SizingMode.MaxContent || heightSizingMode === SizingMode.FitContent) {
649
+ height =
650
+ layout.padding(PhysicalEdge.Top) +
651
+ layout.padding(PhysicalEdge.Bottom) +
652
+ layout.border(PhysicalEdge.Top) +
653
+ layout.border(PhysicalEdge.Bottom);
654
+ }
655
+ node.setLayoutMeasuredDimension(
656
+ boundAxis(node, FlexDirection.Column, direction, height, ownerHeight, ownerWidth),
657
+ Dimension.Height,
658
+ );
659
+ }
660
+
661
+ function isFixedSize(dim: number, sizingMode: SizingMode): boolean {
662
+ return (
663
+ sizingMode === SizingMode.StretchFit ||
664
+ (isDefined(dim) && sizingMode === SizingMode.FitContent && dim <= 0)
665
+ );
666
+ }
667
+
668
+ function measureNodeWithFixedSize(
669
+ node: Node,
670
+ direction: Direction,
671
+ availableWidth: number,
672
+ availableHeight: number,
673
+ widthSizingMode: SizingMode,
674
+ heightSizingMode: SizingMode,
675
+ ownerWidth: number,
676
+ ownerHeight: number,
677
+ ): boolean {
678
+ if (
679
+ isFixedSize(availableWidth, widthSizingMode) &&
680
+ isFixedSize(availableHeight, heightSizingMode)
681
+ ) {
682
+ node.setLayoutMeasuredDimension(
683
+ boundAxis(
684
+ node,
685
+ FlexDirection.Row,
686
+ direction,
687
+ isUndefined(availableWidth) ||
688
+ (widthSizingMode === SizingMode.FitContent && availableWidth < 0)
689
+ ? 0
690
+ : availableWidth,
691
+ ownerWidth,
692
+ ownerWidth,
693
+ ),
694
+ Dimension.Width,
695
+ );
696
+
697
+ node.setLayoutMeasuredDimension(
698
+ boundAxis(
699
+ node,
700
+ FlexDirection.Column,
701
+ direction,
702
+ isUndefined(availableHeight) ||
703
+ (heightSizingMode === SizingMode.FitContent && availableHeight < 0)
704
+ ? 0
705
+ : availableHeight,
706
+ ownerHeight,
707
+ ownerWidth,
708
+ ),
709
+ Dimension.Height,
710
+ );
711
+ return true;
712
+ }
713
+
714
+ return false;
715
+ }
716
+
717
+ export function zeroOutLayoutRecursively(node: Node): void {
718
+ node.layout = new LayoutResults();
719
+ node.setLayoutDimension(0, Dimension.Width);
720
+ node.setLayoutDimension(0, Dimension.Height);
721
+ node.hasNewLayout_ = true;
722
+
723
+ node.cloneChildrenIfNeeded();
724
+ for (const child of node.children) {
725
+ zeroOutLayoutRecursively(child);
726
+ }
727
+ }
728
+
729
+ export function cleanupContentsNodesRecursively(node: Node, didPerformLayout: boolean): void {
730
+ if (node.hasContentsChildren()) {
731
+ node.cloneContentsChildrenIfNeeded();
732
+ for (const child of node.children) {
733
+ if (child.style.display() === Display.Contents) {
734
+ child.layout = new LayoutResults();
735
+ child.setLayoutDimension(0, Dimension.Width);
736
+ child.setLayoutDimension(0, Dimension.Height);
737
+ if (didPerformLayout) {
738
+ child.hasNewLayout_ = true;
739
+ }
740
+ child.setDirty(false);
741
+ child.cloneChildrenIfNeeded();
742
+
743
+ cleanupContentsNodesRecursively(child, didPerformLayout);
744
+ }
745
+ }
746
+ }
747
+ }
748
+
749
+ export function calculateAvailableInnerDimension(
750
+ node: Node,
751
+ direction: Direction,
752
+ dim: Dimension,
753
+ availableDim: number,
754
+ paddingAndBorder: number,
755
+ ownerDim: number,
756
+ ownerWidth: number,
757
+ ): number {
758
+ let availableInnerDim = availableDim - paddingAndBorder;
759
+ // Max dimension overrides predefined dimension value; Min dimension in turn
760
+ // overrides both of the above
761
+ if (isDefined(availableInnerDim)) {
762
+ // We want to make sure our available height does not violate min and max
763
+ // constraints
764
+ const minDimensionOptional = node.style.resolvedMinDimension(
765
+ direction,
766
+ dim,
767
+ ownerDim,
768
+ ownerWidth,
769
+ );
770
+ const minInnerDim = isUndefined(minDimensionOptional)
771
+ ? 0
772
+ : minDimensionOptional - paddingAndBorder;
773
+
774
+ const maxDimensionOptional = node.style.resolvedMaxDimension(
775
+ direction,
776
+ dim,
777
+ ownerDim,
778
+ ownerWidth,
779
+ );
780
+
781
+ const maxInnerDim = isUndefined(maxDimensionOptional)
782
+ ? Number.MAX_VALUE
783
+ : maxDimensionOptional - paddingAndBorder;
784
+ availableInnerDim = maxOrDefined(minOrDefined(availableInnerDim, maxInnerDim), minInnerDim);
785
+ }
786
+
787
+ return availableInnerDim;
788
+ }
789
+
790
+ function computeFlexBasisForChildren(
791
+ node: Node,
792
+ layoutChildren: readonly Node[],
793
+ availableInnerWidth: number,
794
+ availableInnerHeight: number,
795
+ ownerWidth: number,
796
+ ownerHeight: number,
797
+ widthSizingMode: SizingMode,
798
+ heightSizingMode: SizingMode,
799
+ direction: Direction,
800
+ mainAxis: FlexDirection,
801
+ performLayout: boolean,
802
+ layoutMarkerData: LayoutData,
803
+ depth: number,
804
+ generationCount: number,
805
+ ): number {
806
+ let totalOuterFlexBasis = 0;
807
+ let singleFlexChild: Node | null = null;
808
+ const sizingModeMainDim = isRow(mainAxis) ? widthSizingMode : heightSizingMode;
809
+ // If there is only one child with flexGrow + flexShrink it means we can set
810
+ // the computedFlexBasis to 0 instead of measuring and shrinking / flexing
811
+ // the child to exactly match the remaining space
812
+ if (sizingModeMainDim === SizingMode.StretchFit) {
813
+ for (const child of layoutChildren) {
814
+ if (child.isNodeFlexible()) {
815
+ if (
816
+ singleFlexChild !== null ||
817
+ inexactEquals(child.resolveFlexGrow(), 0) ||
818
+ inexactEquals(child.resolveFlexShrink(), 0)
819
+ ) {
820
+ // There is already a flexible child, or this flexible child doesn't
821
+ // have flexGrow and flexShrink, abort
822
+ singleFlexChild = null;
823
+ break;
824
+ } else {
825
+ singleFlexChild = child;
826
+ }
827
+ }
828
+ }
829
+ }
830
+
831
+ for (const child of layoutChildren) {
832
+ child.processDimensions();
833
+ if (child.style.display() === Display.None) {
834
+ // Only mutate display: none children during layout passes. Zeroing them
835
+ // out during measure-only passes contributes nothing to the
836
+ // measurement, but sets `hasNewLayout` on nodes the parent's layout
837
+ // pass may never visit.
838
+ if (performLayout) {
839
+ zeroOutLayoutRecursively(child);
840
+ child.hasNewLayout_ = true;
841
+ child.setDirty(false);
842
+ }
843
+ continue;
844
+ }
845
+ if (performLayout) {
846
+ // Set the initial position (relative to the owner).
847
+ const childDirection = child.resolveDirection(direction);
848
+ child.setPositionFromStyle(childDirection, availableInnerWidth, availableInnerHeight);
849
+ }
850
+
851
+ if (child.style.positionType() === PositionType.Absolute) {
852
+ continue;
853
+ }
854
+ if (child === singleFlexChild) {
855
+ child.setLayoutComputedFlexBasisGeneration(generationCount);
856
+ child.setLayoutComputedFlexBasis(0);
857
+ } else {
858
+ computeFlexBasisForChild(
859
+ node,
860
+ child,
861
+ availableInnerWidth,
862
+ widthSizingMode,
863
+ availableInnerHeight,
864
+ ownerWidth,
865
+ ownerHeight,
866
+ heightSizingMode,
867
+ direction,
868
+ layoutMarkerData,
869
+ depth,
870
+ generationCount,
871
+ );
872
+ }
873
+
874
+ totalOuterFlexBasis +=
875
+ child.layout.computedFlexBasis +
876
+ child.style.computeMarginForAxis(mainAxis, availableInnerWidth);
877
+ }
878
+
879
+ return totalOuterFlexBasis;
880
+ }
881
+
882
+ // Returns the min-content size of `node` along `requestedAxis`, used by CSS
883
+ // Flexbox §4.5 automatic minimum sizing. See CalculateLayout.cpp for the full
884
+ // description of the algorithm.
885
+ function computeMinContentMainSize(
886
+ node: Node,
887
+ requestedAxis: FlexDirection,
888
+ ownerDirection: Direction,
889
+ ownerWidth: number,
890
+ // oxlint-disable-next-line oxc/only-used-in-recursion -- kept for call-site parity with upstream Yoga
891
+ ownerHeight: number,
892
+ ): number {
893
+ const wantRow = isRow(requestedAxis);
894
+
895
+ if (node.hasMeasureFunc()) {
896
+ // Fall back to the regular measure function with `AtMost 0`, which text
897
+ // measurers naturally answer with longest-word width.
898
+ const size = node.measure(
899
+ wantRow ? 0 : NaN,
900
+ wantRow ? MeasureMode.AtMost : MeasureMode.Undefined,
901
+ wantRow ? NaN : 0,
902
+ wantRow ? MeasureMode.Undefined : MeasureMode.AtMost,
903
+ );
904
+ // Add the leaf's own padding and border, like the container branch below.
905
+ const leafDirection = node.resolveDirection(ownerDirection);
906
+ const paddingAndBorder =
907
+ node.style.computeFlexStartPaddingAndBorder(requestedAxis, leafDirection, ownerWidth) +
908
+ node.style.computeFlexEndPaddingAndBorder(requestedAxis, leafDirection, ownerWidth);
909
+ return (wantRow ? size.width : size.height) + paddingAndBorder;
910
+ }
911
+
912
+ if (node.getChildCount() === 0) {
913
+ return 0;
914
+ }
915
+
916
+ const direction = node.resolveDirection(ownerDirection);
917
+ const nodeMainAxis = resolveDirection(node.style.flexDirection(), direction);
918
+ const nodeCrossAxis = resolveCrossDirection(nodeMainAxis, direction);
919
+
920
+ let mainTotal = 0;
921
+ let crossMax = 0;
922
+
923
+ for (let i = 0; i < node.getChildCount(); i++) {
924
+ const child = node.getChild(i);
925
+ if (
926
+ child.style.display() === Display.None ||
927
+ child.style.positionType() === PositionType.Absolute
928
+ ) {
929
+ continue;
930
+ }
931
+
932
+ let childMain = computeMinContentMainSize(
933
+ child,
934
+ nodeMainAxis,
935
+ direction,
936
+ ownerWidth,
937
+ ownerHeight,
938
+ );
939
+ childMain += child.style.computeMarginForAxis(nodeMainAxis, ownerWidth);
940
+
941
+ let childCross = computeMinContentMainSize(
942
+ child,
943
+ nodeCrossAxis,
944
+ direction,
945
+ ownerWidth,
946
+ ownerHeight,
947
+ );
948
+ childCross += child.style.computeMarginForAxis(nodeCrossAxis, ownerWidth);
949
+
950
+ mainTotal += childMain;
951
+ crossMax = Math.max(crossMax, childCross);
952
+ }
953
+
954
+ mainTotal +=
955
+ node.style.computeFlexStartPaddingAndBorder(nodeMainAxis, direction, ownerWidth) +
956
+ node.style.computeFlexEndPaddingAndBorder(nodeMainAxis, direction, ownerWidth);
957
+ crossMax +=
958
+ node.style.computeFlexStartPaddingAndBorder(nodeCrossAxis, direction, ownerWidth) +
959
+ node.style.computeFlexEndPaddingAndBorder(nodeCrossAxis, direction, ownerWidth);
960
+
961
+ const nodeMainIsRow = isRow(nodeMainAxis);
962
+ const widthMin = nodeMainIsRow ? mainTotal : crossMax;
963
+ const heightMin = nodeMainIsRow ? crossMax : mainTotal;
964
+ return wantRow ? widthMin : heightMin;
965
+ }
966
+
967
+ // Computes the CSS Flexbox §4.5 automatic minimum main-axis size for `child`.
968
+ // Returns NaN when no auto-min applies; 0 when the item's own
969
+ // `overflow != visible` (the spec's per-item escape hatch); or a concrete
970
+ // floor otherwise. See https://www.w3.org/TR/css-flexbox-1/#min-size-auto.
971
+ function computeAutoMinMainSize(
972
+ child: Node,
973
+ mainAxis: FlexDirection,
974
+ direction: Direction,
975
+ ownerMainAxisSize: number,
976
+ ownerWidth: number,
977
+ ownerHeight: number,
978
+ ): number {
979
+ if (child.hasErrata(Errata.MinSizeUndefinedInsteadOfAuto)) {
980
+ return NaN;
981
+ }
982
+ if (child.style.display() === Display.None) {
983
+ return NaN;
984
+ }
985
+ // Explicit `min-{w,h}` (including `0`) wins over auto. This is the
986
+ // CSS-spec opt-out (§4.5).
987
+ if (child.style.minDimension(dimension(mainAxis)).isDefined()) {
988
+ return NaN;
989
+ }
990
+ // Per CSS §4.5: a flex item whose own `overflow` is not `visible` gets
991
+ // auto-min = 0 (let scroll/clip handle overflow rather than enforce a
992
+ // content-based minimum).
993
+ if (child.style.overflow() !== Overflow.Visible) {
994
+ return 0;
995
+ }
996
+
997
+ const mainDim = dimension(mainAxis);
998
+ const crossDim = isRow(mainAxis) ? Dimension.Height : Dimension.Width;
999
+ const isMainAxisRow = isRow(mainAxis);
1000
+
1001
+ // Specified size suggestion: the resolved main-axis style dimension.
1002
+ const specifiedMain = child.getResolvedDimension(
1003
+ direction,
1004
+ mainDim,
1005
+ ownerMainAxisSize,
1006
+ ownerWidth,
1007
+ );
1008
+
1009
+ // Transferred size suggestion: cross × aspect-ratio, if both are definite.
1010
+ let transferredMain = NaN;
1011
+ const aspectRatio = child.style.aspectRatio();
1012
+ if (isDefined(aspectRatio)) {
1013
+ const crossOwner = isMainAxisRow ? ownerHeight : ownerWidth;
1014
+ const crossResolved = child.getResolvedDimension(direction, crossDim, crossOwner, ownerWidth);
1015
+ if (isDefined(crossResolved)) {
1016
+ transferredMain = isMainAxisRow ? crossResolved * aspectRatio : crossResolved / aspectRatio;
1017
+ }
1018
+ }
1019
+
1020
+ // Content size suggestion: probe via min-content recursion.
1021
+ const contentMain = computeMinContentMainSize(
1022
+ child,
1023
+ mainAxis,
1024
+ direction,
1025
+ ownerWidth,
1026
+ ownerHeight,
1027
+ );
1028
+
1029
+ // Combine per §4.5: floor = min(content, specified) when specified is
1030
+ // definite; otherwise floor = min(content, transferred) when transferred
1031
+ // applies; else floor = content.
1032
+ let floor = contentMain;
1033
+ if (isDefined(specifiedMain)) {
1034
+ if (isUndefined(floor) || specifiedMain < floor) {
1035
+ floor = specifiedMain;
1036
+ }
1037
+ } else if (isDefined(transferredMain)) {
1038
+ if (isUndefined(floor) || transferredMain < floor) {
1039
+ floor = transferredMain;
1040
+ }
1041
+ }
1042
+
1043
+ // §4.5: cap by the max main size.
1044
+ const maxMain = child.style.resolvedMaxDimension(
1045
+ direction,
1046
+ mainDim,
1047
+ ownerMainAxisSize,
1048
+ ownerWidth,
1049
+ );
1050
+ if (isDefined(maxMain) && floor > maxMain) {
1051
+ floor = maxMain;
1052
+ }
1053
+
1054
+ if (isUndefined(floor) || floor < 0) {
1055
+ floor = 0;
1056
+ }
1057
+ return floor;
1058
+ }
1059
+
1060
+ // boundAxis with an additional lower bound from `child`'s cached
1061
+ // `computedAutoMinMainSize`, applied on the main axis only.
1062
+ function boundAxisWithAutoMin(
1063
+ child: Node,
1064
+ axis: FlexDirection,
1065
+ direction: Direction,
1066
+ value: number,
1067
+ axisSize: number,
1068
+ widthSize: number,
1069
+ ): number {
1070
+ let bounded = boundAxis(child, axis, direction, value, axisSize, widthSize);
1071
+ const autoMin = child.layout.computedAutoMinMainSize;
1072
+ if (isDefined(autoMin) && bounded < autoMin) {
1073
+ bounded = autoMin;
1074
+ }
1075
+ return bounded;
1076
+ }
1077
+
1078
+ // It distributes the free space to the flexible items and ensures that the
1079
+ // size of the flex items abide the min and max constraints. At the end of this
1080
+ // function the child nodes would have proper size. Prior using this function
1081
+ // please ensure that distributeFreeSpaceFirstPass is called.
1082
+ function distributeFreeSpaceSecondPass(
1083
+ flexLine: FlexLine,
1084
+ node: Node,
1085
+ mainAxis: FlexDirection,
1086
+ crossAxis: FlexDirection,
1087
+ direction: Direction,
1088
+ ownerWidth: number,
1089
+ mainAxisOwnerSize: number,
1090
+ availableInnerMainDim: number,
1091
+ availableInnerCrossDim: number,
1092
+ availableInnerWidth: number,
1093
+ availableInnerHeight: number,
1094
+ mainAxisOverflows: boolean,
1095
+ sizingModeCrossDim: SizingMode,
1096
+ performLayout: boolean,
1097
+ layoutMarkerData: LayoutData,
1098
+ depth: number,
1099
+ generationCount: number,
1100
+ ): number {
1101
+ let childFlexBasis = 0;
1102
+ let flexShrinkScaledFactor = 0;
1103
+ let flexGrowFactor = 0;
1104
+ let deltaFreeSpace = 0;
1105
+ const isMainAxisRow = isRow(mainAxis);
1106
+ const isNodeFlexWrap = node.style.flexWrap() !== Wrap.NoWrap;
1107
+
1108
+ for (const currentLineChild of flexLine.itemsInFlow) {
1109
+ childFlexBasis = boundAxisWithinMinAndMax(
1110
+ currentLineChild,
1111
+ direction,
1112
+ mainAxis,
1113
+ currentLineChild.layout.computedFlexBasis,
1114
+ mainAxisOwnerSize,
1115
+ ownerWidth,
1116
+ );
1117
+ let updatedMainSize = childFlexBasis;
1118
+
1119
+ if (isDefined(flexLine.layout.remainingFreeSpace) && flexLine.layout.remainingFreeSpace < 0) {
1120
+ flexShrinkScaledFactor = -currentLineChild.resolveFlexShrink() * childFlexBasis;
1121
+ // Is this child able to shrink?
1122
+ if (flexShrinkScaledFactor !== 0) {
1123
+ let childSize = NaN;
1124
+
1125
+ if (
1126
+ isDefined(flexLine.layout.totalFlexShrinkScaledFactors) &&
1127
+ flexLine.layout.totalFlexShrinkScaledFactors === 0
1128
+ ) {
1129
+ childSize = childFlexBasis + flexShrinkScaledFactor;
1130
+ } else {
1131
+ childSize =
1132
+ childFlexBasis +
1133
+ (flexLine.layout.remainingFreeSpace / flexLine.layout.totalFlexShrinkScaledFactors) *
1134
+ flexShrinkScaledFactor;
1135
+ }
1136
+
1137
+ updatedMainSize = boundAxisWithAutoMin(
1138
+ currentLineChild,
1139
+ mainAxis,
1140
+ direction,
1141
+ childSize,
1142
+ availableInnerMainDim,
1143
+ availableInnerWidth,
1144
+ );
1145
+ }
1146
+ } else if (
1147
+ isDefined(flexLine.layout.remainingFreeSpace) &&
1148
+ flexLine.layout.remainingFreeSpace > 0
1149
+ ) {
1150
+ flexGrowFactor = currentLineChild.resolveFlexGrow();
1151
+
1152
+ // Is this child able to grow?
1153
+ if (!Number.isNaN(flexGrowFactor) && flexGrowFactor !== 0) {
1154
+ updatedMainSize = boundAxisWithAutoMin(
1155
+ currentLineChild,
1156
+ mainAxis,
1157
+ direction,
1158
+ childFlexBasis +
1159
+ (flexLine.layout.remainingFreeSpace / flexLine.layout.totalFlexGrowFactors) *
1160
+ flexGrowFactor,
1161
+ availableInnerMainDim,
1162
+ availableInnerWidth,
1163
+ );
1164
+ }
1165
+ }
1166
+
1167
+ deltaFreeSpace += updatedMainSize - childFlexBasis;
1168
+
1169
+ const marginMain = currentLineChild.style.computeMarginForAxis(mainAxis, availableInnerWidth);
1170
+ const marginCross = currentLineChild.style.computeMarginForAxis(crossAxis, availableInnerWidth);
1171
+
1172
+ let childCrossSize = NaN;
1173
+ const childMainSize = updatedMainSize + marginMain;
1174
+ let childCrossSizingMode: SizingMode;
1175
+ const childMainSizingMode = SizingMode.StretchFit;
1176
+
1177
+ // These are pure reads repeated below; evaluate once per child.
1178
+ const crossDim = dimension(crossAxis);
1179
+ const hasDefiniteCrossLength = currentLineChild.hasDefiniteLength(
1180
+ crossDim,
1181
+ availableInnerCrossDim,
1182
+ );
1183
+ const childAlignment = resolveChildAlignment(node, currentLineChild);
1184
+ const crossStartMarginIsAuto = currentLineChild.style.flexStartMarginIsAuto(
1185
+ crossAxis,
1186
+ direction,
1187
+ );
1188
+ const crossEndMarginIsAuto = currentLineChild.style.flexEndMarginIsAuto(crossAxis, direction);
1189
+
1190
+ const childStyle = currentLineChild.style;
1191
+ if (isDefined(childStyle.aspectRatio())) {
1192
+ childCrossSize = isMainAxisRow
1193
+ ? (childMainSize - marginMain) / childStyle.aspectRatio()
1194
+ : (childMainSize - marginMain) * childStyle.aspectRatio();
1195
+ childCrossSizingMode = SizingMode.StretchFit;
1196
+
1197
+ childCrossSize += marginCross;
1198
+ } else if (
1199
+ !Number.isNaN(availableInnerCrossDim) &&
1200
+ !hasDefiniteCrossLength &&
1201
+ sizingModeCrossDim === SizingMode.StretchFit &&
1202
+ !(isNodeFlexWrap && mainAxisOverflows) &&
1203
+ childAlignment === Align.Stretch &&
1204
+ !crossStartMarginIsAuto &&
1205
+ !crossEndMarginIsAuto
1206
+ ) {
1207
+ childCrossSize = availableInnerCrossDim;
1208
+ childCrossSizingMode = SizingMode.StretchFit;
1209
+ } else if (!hasDefiniteCrossLength) {
1210
+ childCrossSize = availableInnerCrossDim;
1211
+ childCrossSizingMode = isUndefined(childCrossSize)
1212
+ ? SizingMode.MaxContent
1213
+ : SizingMode.FitContent;
1214
+ } else {
1215
+ childCrossSize =
1216
+ currentLineChild.getResolvedDimension(
1217
+ direction,
1218
+ crossDim,
1219
+ availableInnerCrossDim,
1220
+ availableInnerWidth,
1221
+ ) + marginCross;
1222
+ const isLoosePercentageMeasurement =
1223
+ currentLineChild.getProcessedDimension(crossDim).isPercent() &&
1224
+ sizingModeCrossDim !== SizingMode.StretchFit;
1225
+ childCrossSizingMode =
1226
+ isUndefined(childCrossSize) || isLoosePercentageMeasurement
1227
+ ? SizingMode.MaxContent
1228
+ : SizingMode.StretchFit;
1229
+ }
1230
+
1231
+ const mainModeAndSize = scratchModeA;
1232
+ mainModeAndSize.mode = childMainSizingMode;
1233
+ mainModeAndSize.size = childMainSize;
1234
+ constrainMaxSizeForMode(
1235
+ currentLineChild,
1236
+ direction,
1237
+ mainAxis,
1238
+ availableInnerMainDim,
1239
+ availableInnerWidth,
1240
+ mainModeAndSize,
1241
+ );
1242
+ const crossModeAndSize = scratchModeB;
1243
+ crossModeAndSize.mode = childCrossSizingMode;
1244
+ crossModeAndSize.size = childCrossSize;
1245
+ constrainMaxSizeForMode(
1246
+ currentLineChild,
1247
+ direction,
1248
+ crossAxis,
1249
+ availableInnerCrossDim,
1250
+ availableInnerWidth,
1251
+ crossModeAndSize,
1252
+ );
1253
+
1254
+ const requiresStretchLayout =
1255
+ !hasDefiniteCrossLength &&
1256
+ childAlignment === Align.Stretch &&
1257
+ !crossStartMarginIsAuto &&
1258
+ !crossEndMarginIsAuto;
1259
+
1260
+ const childWidth = isMainAxisRow ? mainModeAndSize.size : crossModeAndSize.size;
1261
+ const childHeight = !isMainAxisRow ? mainModeAndSize.size : crossModeAndSize.size;
1262
+
1263
+ const childWidthSizingMode = isMainAxisRow ? mainModeAndSize.mode : crossModeAndSize.mode;
1264
+ const childHeightSizingMode = !isMainAxisRow ? mainModeAndSize.mode : crossModeAndSize.mode;
1265
+
1266
+ const isLayoutPass = performLayout && !requiresStretchLayout;
1267
+ // Recursively call the layout algorithm for this child with the updated
1268
+ // main size.
1269
+ calculateLayoutInternal(
1270
+ currentLineChild,
1271
+ childWidth,
1272
+ childHeight,
1273
+ node.layout.direction(),
1274
+ childWidthSizingMode,
1275
+ childHeightSizingMode,
1276
+ availableInnerWidth,
1277
+ availableInnerHeight,
1278
+ isLayoutPass,
1279
+ isLayoutPass ? LayoutPassReason.FlexLayout : LayoutPassReason.FlexMeasure,
1280
+ layoutMarkerData,
1281
+ depth,
1282
+ generationCount,
1283
+ );
1284
+ node.setLayoutHadOverflow(node.layout.hadOverflow() || currentLineChild.layout.hadOverflow());
1285
+ }
1286
+ return deltaFreeSpace;
1287
+ }
1288
+
1289
+ // It distributes the free space to the flexible items. For those flexible
1290
+ // items whose min and max constraints are triggered, those flex item's clamped
1291
+ // size is removed from the remaining free space.
1292
+ function distributeFreeSpaceFirstPass(
1293
+ flexLine: FlexLine,
1294
+ direction: Direction,
1295
+ mainAxis: FlexDirection,
1296
+ ownerWidth: number,
1297
+ mainAxisOwnerSize: number,
1298
+ availableInnerMainDim: number,
1299
+ availableInnerWidth: number,
1300
+ ): void {
1301
+ let flexShrinkScaledFactor = 0;
1302
+ let flexGrowFactor = 0;
1303
+ let baseMainSize = 0;
1304
+ let boundMainSize = 0;
1305
+ let deltaFreeSpace = 0;
1306
+
1307
+ for (const currentLineChild of flexLine.itemsInFlow) {
1308
+ const childFlexBasis = boundAxisWithinMinAndMax(
1309
+ currentLineChild,
1310
+ direction,
1311
+ mainAxis,
1312
+ currentLineChild.layout.computedFlexBasis,
1313
+ mainAxisOwnerSize,
1314
+ ownerWidth,
1315
+ );
1316
+
1317
+ if (flexLine.layout.remainingFreeSpace < 0) {
1318
+ flexShrinkScaledFactor = -currentLineChild.resolveFlexShrink() * childFlexBasis;
1319
+
1320
+ // Is this child able to shrink?
1321
+ if (isDefined(flexShrinkScaledFactor) && flexShrinkScaledFactor !== 0) {
1322
+ baseMainSize =
1323
+ childFlexBasis +
1324
+ (flexLine.layout.remainingFreeSpace / flexLine.layout.totalFlexShrinkScaledFactors) *
1325
+ flexShrinkScaledFactor;
1326
+ boundMainSize = boundAxisWithAutoMin(
1327
+ currentLineChild,
1328
+ mainAxis,
1329
+ direction,
1330
+ baseMainSize,
1331
+ availableInnerMainDim,
1332
+ availableInnerWidth,
1333
+ );
1334
+ if (isDefined(baseMainSize) && isDefined(boundMainSize) && baseMainSize !== boundMainSize) {
1335
+ // By excluding this item's size and flex factor from remaining,
1336
+ // this item's min/max constraints should also trigger in the second
1337
+ // pass resulting in the item's size calculation being identical in
1338
+ // the first and second passes.
1339
+ deltaFreeSpace += boundMainSize - childFlexBasis;
1340
+ flexLine.layout.totalFlexShrinkScaledFactors -=
1341
+ -currentLineChild.resolveFlexShrink() * currentLineChild.layout.computedFlexBasis;
1342
+ }
1343
+ }
1344
+ } else if (
1345
+ isDefined(flexLine.layout.remainingFreeSpace) &&
1346
+ flexLine.layout.remainingFreeSpace > 0
1347
+ ) {
1348
+ flexGrowFactor = currentLineChild.resolveFlexGrow();
1349
+
1350
+ // Is this child able to grow?
1351
+ if (isDefined(flexGrowFactor) && flexGrowFactor !== 0) {
1352
+ baseMainSize =
1353
+ childFlexBasis +
1354
+ (flexLine.layout.remainingFreeSpace / flexLine.layout.totalFlexGrowFactors) *
1355
+ flexGrowFactor;
1356
+ boundMainSize = boundAxis(
1357
+ currentLineChild,
1358
+ mainAxis,
1359
+ direction,
1360
+ baseMainSize,
1361
+ availableInnerMainDim,
1362
+ availableInnerWidth,
1363
+ );
1364
+
1365
+ if (isDefined(baseMainSize) && isDefined(boundMainSize) && baseMainSize !== boundMainSize) {
1366
+ // By excluding this item's size and flex factor from remaining,
1367
+ // this item's min/max constraints should also trigger in the second
1368
+ // pass resulting in the item's size calculation being identical in
1369
+ // the first and second passes.
1370
+ deltaFreeSpace += boundMainSize - childFlexBasis;
1371
+ flexLine.layout.totalFlexGrowFactors -= flexGrowFactor;
1372
+ }
1373
+ }
1374
+ }
1375
+ }
1376
+ flexLine.layout.remainingFreeSpace -= deltaFreeSpace;
1377
+ }
1378
+
1379
+ // Do two passes over the flex items to figure out how to distribute the
1380
+ // remaining space.
1381
+ //
1382
+ // The first pass finds the items whose min/max constraints trigger, freezes
1383
+ // them at those sizes, and excludes those sizes from the remaining space.
1384
+ //
1385
+ // The second pass sets the size of each flexible item. It distributes the
1386
+ // remaining space amongst the items whose min/max constraints didn't trigger
1387
+ // in the first pass. For the other items, it sets their sizes by forcing
1388
+ // their min/max constraints to trigger again.
1389
+ //
1390
+ // This two pass approach for resolving min/max constraints deviates from the
1391
+ // spec. The spec (https://www.w3.org/TR/CSS-flexbox-1/#resolve-flexible-lengths)
1392
+ // describes a process that needs to be repeated a variable number of times.
1393
+ // The algorithm implemented here won't handle all cases but it was simpler to
1394
+ // implement and it mitigates performance concerns because we know exactly how
1395
+ // many passes it'll do.
1396
+ //
1397
+ // At the end of this function the child nodes would have the proper size
1398
+ // assigned to them.
1399
+ //
1400
+ function resolveFlexibleLength(
1401
+ node: Node,
1402
+ flexLine: FlexLine,
1403
+ mainAxis: FlexDirection,
1404
+ crossAxis: FlexDirection,
1405
+ direction: Direction,
1406
+ ownerWidth: number,
1407
+ mainAxisOwnerSize: number,
1408
+ availableInnerMainDim: number,
1409
+ availableInnerCrossDim: number,
1410
+ availableInnerWidth: number,
1411
+ availableInnerHeight: number,
1412
+ mainAxisOverflows: boolean,
1413
+ sizingModeCrossDim: SizingMode,
1414
+ performLayout: boolean,
1415
+ layoutMarkerData: LayoutData,
1416
+ depth: number,
1417
+ generationCount: number,
1418
+ ): void {
1419
+ const originalFreeSpace = flexLine.layout.remainingFreeSpace;
1420
+
1421
+ // CSS Flexbox §4.5: compute each item's automatic minimum main-axis size
1422
+ // up front so the bounding helpers below can floor shrunk values.
1423
+ if (!node.hasErrata(Errata.MinSizeUndefinedInsteadOfAuto)) {
1424
+ for (const currentLineChild of flexLine.itemsInFlow) {
1425
+ currentLineChild.layout.computedAutoMinMainSize = computeAutoMinMainSize(
1426
+ currentLineChild,
1427
+ mainAxis,
1428
+ direction,
1429
+ mainAxisOwnerSize,
1430
+ availableInnerWidth,
1431
+ availableInnerHeight,
1432
+ );
1433
+ }
1434
+ } else {
1435
+ for (const currentLineChild of flexLine.itemsInFlow) {
1436
+ currentLineChild.layout.computedAutoMinMainSize = NaN;
1437
+ }
1438
+ }
1439
+
1440
+ // First pass: detect the flex items whose min/max constraints trigger
1441
+ distributeFreeSpaceFirstPass(
1442
+ flexLine,
1443
+ direction,
1444
+ mainAxis,
1445
+ ownerWidth,
1446
+ mainAxisOwnerSize,
1447
+ availableInnerMainDim,
1448
+ availableInnerWidth,
1449
+ );
1450
+
1451
+ // Second pass: resolve the sizes of the flexible items
1452
+ const distributedFreeSpace = distributeFreeSpaceSecondPass(
1453
+ flexLine,
1454
+ node,
1455
+ mainAxis,
1456
+ crossAxis,
1457
+ direction,
1458
+ ownerWidth,
1459
+ mainAxisOwnerSize,
1460
+ availableInnerMainDim,
1461
+ availableInnerCrossDim,
1462
+ availableInnerWidth,
1463
+ availableInnerHeight,
1464
+ mainAxisOverflows,
1465
+ sizingModeCrossDim,
1466
+ performLayout,
1467
+ layoutMarkerData,
1468
+ depth,
1469
+ generationCount,
1470
+ );
1471
+
1472
+ flexLine.layout.remainingFreeSpace = originalFreeSpace - distributedFreeSpace;
1473
+ }
1474
+
1475
+ function justifyMainAxis(
1476
+ node: Node,
1477
+ flexLine: FlexLine,
1478
+ mainAxis: FlexDirection,
1479
+ crossAxis: FlexDirection,
1480
+ direction: Direction,
1481
+ sizingModeMainDim: SizingMode,
1482
+ sizingModeCrossDim: SizingMode,
1483
+ mainAxisOwnerSize: number,
1484
+ ownerWidth: number,
1485
+ availableInnerMainDim: number,
1486
+ availableInnerCrossDim: number,
1487
+ availableInnerWidth: number,
1488
+ performLayout: boolean,
1489
+ ): void {
1490
+ const style = node.style;
1491
+
1492
+ const leadingPaddingAndBorderMain = node.style.computeFlexStartPaddingAndBorder(
1493
+ mainAxis,
1494
+ direction,
1495
+ ownerWidth,
1496
+ );
1497
+ const trailingPaddingAndBorderMain = node.style.computeFlexEndPaddingAndBorder(
1498
+ mainAxis,
1499
+ direction,
1500
+ ownerWidth,
1501
+ );
1502
+
1503
+ const gap = node.style.computeGapForAxis(mainAxis, availableInnerMainDim);
1504
+ // If we are using "at most" rules in the main axis, make sure that
1505
+ // remainingFreeSpace is 0 when min main dimension is not given
1506
+ if (sizingModeMainDim === SizingMode.FitContent && flexLine.layout.remainingFreeSpace > 0) {
1507
+ if (
1508
+ style.minDimension(dimension(mainAxis)).isDefined() &&
1509
+ isDefined(
1510
+ style.resolvedMinDimension(direction, dimension(mainAxis), mainAxisOwnerSize, ownerWidth),
1511
+ )
1512
+ ) {
1513
+ // This condition makes sure that if the size of main dimension(after
1514
+ // considering child nodes main dim, leading and trailing padding etc)
1515
+ // falls below min dimension, then the remainingFreeSpace is reassigned
1516
+ // considering the min dimension
1517
+
1518
+ // `minAvailableMainDim` denotes minimum available space in which child
1519
+ // can be laid out, it will exclude space consumed by padding and border.
1520
+ const minAvailableMainDim =
1521
+ style.resolvedMinDimension(direction, dimension(mainAxis), mainAxisOwnerSize, ownerWidth) -
1522
+ leadingPaddingAndBorderMain -
1523
+ trailingPaddingAndBorderMain;
1524
+ const occupiedSpaceByChildNodes = availableInnerMainDim - flexLine.layout.remainingFreeSpace;
1525
+ flexLine.layout.remainingFreeSpace = maxOrDefined(
1526
+ 0,
1527
+ minAvailableMainDim - occupiedSpaceByChildNodes,
1528
+ );
1529
+ } else {
1530
+ flexLine.layout.remainingFreeSpace = 0;
1531
+ }
1532
+ }
1533
+
1534
+ // In order to position the elements in the main axis, we have two controls.
1535
+ // The space between the beginning and the first element and the space
1536
+ // between each two elements.
1537
+ let leadingMainDim = 0;
1538
+ let betweenMainDim = gap;
1539
+ const justifyContent =
1540
+ flexLine.layout.remainingFreeSpace >= 0
1541
+ ? node.style.justifyContent()
1542
+ : fallbackJustification(node.style.justifyContent());
1543
+
1544
+ if (flexLine.numberOfAutoMargins === 0) {
1545
+ switch (justifyContent) {
1546
+ case Justify.Start:
1547
+ case Justify.End:
1548
+ case Justify.Auto:
1549
+ // No-Op
1550
+ break;
1551
+ case Justify.Stretch:
1552
+ // No-Op
1553
+ break;
1554
+ case Justify.Center:
1555
+ leadingMainDim = flexLine.layout.remainingFreeSpace / 2;
1556
+ break;
1557
+ case Justify.FlexEnd:
1558
+ leadingMainDim = flexLine.layout.remainingFreeSpace;
1559
+ break;
1560
+ case Justify.SpaceBetween:
1561
+ if (flexLine.itemsInFlow.length > 1) {
1562
+ betweenMainDim += flexLine.layout.remainingFreeSpace / (flexLine.itemsInFlow.length - 1);
1563
+ }
1564
+ break;
1565
+ case Justify.SpaceEvenly:
1566
+ // Space is distributed evenly across all elements
1567
+ leadingMainDim = flexLine.layout.remainingFreeSpace / (flexLine.itemsInFlow.length + 1);
1568
+ betweenMainDim += leadingMainDim;
1569
+ break;
1570
+ case Justify.SpaceAround:
1571
+ // Space on the edges is half of the space between elements
1572
+ leadingMainDim = (0.5 * flexLine.layout.remainingFreeSpace) / flexLine.itemsInFlow.length;
1573
+ betweenMainDim += leadingMainDim * 2;
1574
+ break;
1575
+ case Justify.FlexStart:
1576
+ break;
1577
+ }
1578
+ }
1579
+
1580
+ flexLine.layout.mainDim = leadingPaddingAndBorderMain + leadingMainDim;
1581
+ flexLine.layout.crossDim = 0;
1582
+
1583
+ let maxAscentForCurrentLine = 0;
1584
+ let maxDescentForCurrentLine = 0;
1585
+ const isNodeBaselineLayout = isBaselineLayout(node);
1586
+ for (const child of flexLine.itemsInFlow) {
1587
+ const childLayout = child.layout;
1588
+ if (
1589
+ child.style.flexStartMarginIsAuto(mainAxis, direction) &&
1590
+ flexLine.layout.remainingFreeSpace > 0
1591
+ ) {
1592
+ flexLine.layout.mainDim += flexLine.layout.remainingFreeSpace / flexLine.numberOfAutoMargins;
1593
+ }
1594
+
1595
+ if (performLayout) {
1596
+ child.setLayoutPosition(
1597
+ childLayout.position(flexStartEdge(mainAxis)) + flexLine.layout.mainDim,
1598
+ flexStartEdge(mainAxis),
1599
+ );
1600
+ }
1601
+
1602
+ if (child !== flexLine.itemsInFlow[flexLine.itemsInFlow.length - 1]) {
1603
+ flexLine.layout.mainDim += betweenMainDim;
1604
+ }
1605
+
1606
+ if (
1607
+ child.style.flexEndMarginIsAuto(mainAxis, direction) &&
1608
+ flexLine.layout.remainingFreeSpace > 0
1609
+ ) {
1610
+ flexLine.layout.mainDim += flexLine.layout.remainingFreeSpace / flexLine.numberOfAutoMargins;
1611
+ }
1612
+ const canSkipFlex = !performLayout && sizingModeCrossDim === SizingMode.StretchFit;
1613
+ if (canSkipFlex) {
1614
+ // If we skipped the flex step, then we can't rely on the measuredDims
1615
+ // because they weren't computed. This means we can't call
1616
+ // dimensionWithMargin.
1617
+ flexLine.layout.mainDim +=
1618
+ child.style.computeMarginForAxis(mainAxis, availableInnerWidth) +
1619
+ boundAxisWithinMinAndMax(
1620
+ child,
1621
+ direction,
1622
+ mainAxis,
1623
+ childLayout.computedFlexBasis,
1624
+ mainAxisOwnerSize,
1625
+ ownerWidth,
1626
+ );
1627
+ flexLine.layout.crossDim = availableInnerCrossDim;
1628
+ } else {
1629
+ // The main dimension is the sum of all the elements dimension plus
1630
+ // the spacing.
1631
+ flexLine.layout.mainDim += child.dimensionWithMargin(mainAxis, availableInnerWidth);
1632
+
1633
+ if (isNodeBaselineLayout) {
1634
+ // If the child is baseline aligned then the cross dimension is
1635
+ // calculated by adding maxAscent and maxDescent from the baseline.
1636
+ const ascent =
1637
+ calculateBaseline(child) +
1638
+ child.style.computeFlexStartMargin(FlexDirection.Column, direction, availableInnerWidth);
1639
+ const descent =
1640
+ child.layout.measuredDimension(Dimension.Height) +
1641
+ child.style.computeMarginForAxis(FlexDirection.Column, availableInnerWidth) -
1642
+ ascent;
1643
+
1644
+ maxAscentForCurrentLine = maxOrDefined(maxAscentForCurrentLine, ascent);
1645
+ maxDescentForCurrentLine = maxOrDefined(maxDescentForCurrentLine, descent);
1646
+ } else {
1647
+ // The cross dimension is the max of the elements dimension since
1648
+ // there can only be one element in that cross dimension in the case
1649
+ // when the items are not baseline aligned
1650
+ flexLine.layout.crossDim = maxOrDefined(
1651
+ flexLine.layout.crossDim,
1652
+ child.dimensionWithMargin(crossAxis, availableInnerWidth),
1653
+ );
1654
+ }
1655
+ }
1656
+ }
1657
+ flexLine.layout.mainDim += trailingPaddingAndBorderMain;
1658
+
1659
+ if (isNodeBaselineLayout) {
1660
+ flexLine.layout.crossDim = maxAscentForCurrentLine + maxDescentForCurrentLine;
1661
+ }
1662
+ }
1663
+
1664
+ //
1665
+ // This is the main routine that implements a subset of the flexbox layout
1666
+ // algorithm described in the W3C CSS documentation:
1667
+ // https://www.w3.org/TR/CSS3-flexbox/. See CalculateLayout.cpp for the list of
1668
+ // limitations and deviations from the standard.
1669
+ //
1670
+ function calculateLayoutImpl(
1671
+ node: Node,
1672
+ availableWidth: number,
1673
+ availableHeight: number,
1674
+ ownerDirection: Direction,
1675
+ widthSizingMode: SizingMode,
1676
+ heightSizingMode: SizingMode,
1677
+ ownerWidth: number,
1678
+ ownerHeight: number,
1679
+ performLayout: boolean,
1680
+ _reason: LayoutPassReason,
1681
+ layoutMarkerData: LayoutData,
1682
+ depth: number,
1683
+ generationCount: number,
1684
+ ): void {
1685
+ if (isUndefined(availableWidth) && widthSizingMode !== SizingMode.MaxContent) {
1686
+ throw new Error(
1687
+ "availableWidth is indefinite so widthSizingMode must be SizingMode::MaxContent",
1688
+ );
1689
+ }
1690
+ if (isUndefined(availableHeight) && heightSizingMode !== SizingMode.MaxContent) {
1691
+ throw new Error(
1692
+ "availableHeight is indefinite so heightSizingMode must be SizingMode::MaxContent",
1693
+ );
1694
+ }
1695
+
1696
+ if (performLayout) {
1697
+ layoutMarkerData.layouts += 1;
1698
+ } else {
1699
+ layoutMarkerData.measures += 1;
1700
+ }
1701
+
1702
+ // Set the resolved resolution in the node's layout.
1703
+ const direction = node.resolveDirection(ownerDirection);
1704
+ node.setLayoutDirection(direction);
1705
+ const fixFlexBasisFitContent = node.config.isExperimentalFeatureEnabled(
1706
+ ExperimentalFeature.FixFlexBasisFitContent,
1707
+ );
1708
+ if (fixFlexBasisFitContent && performLayout) {
1709
+ node.setLayoutHadOverflow(false);
1710
+ }
1711
+
1712
+ const flexRowDirection = resolveDirection(FlexDirection.Row, direction);
1713
+ const flexColumnDirection = resolveDirection(FlexDirection.Column, direction);
1714
+
1715
+ const startEdge = direction === Direction.LTR ? PhysicalEdge.Left : PhysicalEdge.Right;
1716
+ const endEdge = direction === Direction.LTR ? PhysicalEdge.Right : PhysicalEdge.Left;
1717
+
1718
+ const marginRowLeading = node.style.computeInlineStartMargin(
1719
+ flexRowDirection,
1720
+ direction,
1721
+ ownerWidth,
1722
+ );
1723
+ node.setLayoutMargin(marginRowLeading, startEdge);
1724
+ const marginRowTrailing = node.style.computeInlineEndMargin(
1725
+ flexRowDirection,
1726
+ direction,
1727
+ ownerWidth,
1728
+ );
1729
+ node.setLayoutMargin(marginRowTrailing, endEdge);
1730
+ const marginColumnLeading = node.style.computeInlineStartMargin(
1731
+ flexColumnDirection,
1732
+ direction,
1733
+ ownerWidth,
1734
+ );
1735
+ node.setLayoutMargin(marginColumnLeading, PhysicalEdge.Top);
1736
+ const marginColumnTrailing = node.style.computeInlineEndMargin(
1737
+ flexColumnDirection,
1738
+ direction,
1739
+ ownerWidth,
1740
+ );
1741
+ node.setLayoutMargin(marginColumnTrailing, PhysicalEdge.Bottom);
1742
+
1743
+ const marginAxisRow = marginRowLeading + marginRowTrailing;
1744
+ const marginAxisColumn = marginColumnLeading + marginColumnTrailing;
1745
+
1746
+ node.setLayoutBorder(node.style.computeInlineStartBorder(flexRowDirection, direction), startEdge);
1747
+ node.setLayoutBorder(node.style.computeInlineEndBorder(flexRowDirection, direction), endEdge);
1748
+ node.setLayoutBorder(
1749
+ node.style.computeInlineStartBorder(flexColumnDirection, direction),
1750
+ PhysicalEdge.Top,
1751
+ );
1752
+ node.setLayoutBorder(
1753
+ node.style.computeInlineEndBorder(flexColumnDirection, direction),
1754
+ PhysicalEdge.Bottom,
1755
+ );
1756
+
1757
+ node.setLayoutPadding(
1758
+ node.style.computeInlineStartPadding(flexRowDirection, direction, ownerWidth),
1759
+ startEdge,
1760
+ );
1761
+ node.setLayoutPadding(
1762
+ node.style.computeInlineEndPadding(flexRowDirection, direction, ownerWidth),
1763
+ endEdge,
1764
+ );
1765
+ node.setLayoutPadding(
1766
+ node.style.computeInlineStartPadding(flexColumnDirection, direction, ownerWidth),
1767
+ PhysicalEdge.Top,
1768
+ );
1769
+ node.setLayoutPadding(
1770
+ node.style.computeInlineEndPadding(flexColumnDirection, direction, ownerWidth),
1771
+ PhysicalEdge.Bottom,
1772
+ );
1773
+
1774
+ if (node.hasMeasureFunc()) {
1775
+ measureNodeWithMeasureFunc(
1776
+ node,
1777
+ direction,
1778
+ availableWidth - marginAxisRow,
1779
+ availableHeight - marginAxisColumn,
1780
+ widthSizingMode,
1781
+ heightSizingMode,
1782
+ ownerWidth,
1783
+ ownerHeight,
1784
+ layoutMarkerData,
1785
+ );
1786
+
1787
+ // Clean and update all display: contents nodes with a direct path to the
1788
+ // current node as they will not be traversed
1789
+ cleanupContentsNodesRecursively(node, performLayout);
1790
+ return;
1791
+ }
1792
+
1793
+ const layoutChildren = node.getLayoutChildren();
1794
+ const childCount = layoutChildren.length;
1795
+ if (childCount === 0) {
1796
+ measureNodeWithoutChildren(
1797
+ node,
1798
+ direction,
1799
+ availableWidth - marginAxisRow,
1800
+ availableHeight - marginAxisColumn,
1801
+ widthSizingMode,
1802
+ heightSizingMode,
1803
+ ownerWidth,
1804
+ ownerHeight,
1805
+ );
1806
+
1807
+ // Clean and update all display: contents nodes with a direct path to the
1808
+ // current node as they will not be traversed
1809
+ cleanupContentsNodesRecursively(node, performLayout);
1810
+ return;
1811
+ }
1812
+
1813
+ // If we're not being asked to perform a full layout we can skip the
1814
+ // algorithm if we already know the size
1815
+ if (
1816
+ !performLayout &&
1817
+ measureNodeWithFixedSize(
1818
+ node,
1819
+ direction,
1820
+ availableWidth - marginAxisRow,
1821
+ availableHeight - marginAxisColumn,
1822
+ widthSizingMode,
1823
+ heightSizingMode,
1824
+ ownerWidth,
1825
+ ownerHeight,
1826
+ )
1827
+ ) {
1828
+ // Clean and update all display: contents nodes with a direct path to the
1829
+ // current node as they will not be traversed
1830
+ cleanupContentsNodesRecursively(node, /* didPerformLayout */ false);
1831
+ return;
1832
+ }
1833
+
1834
+ // At this point we know we're going to perform work. Ensure that each child
1835
+ // has a mutable copy.
1836
+ node.cloneChildrenIfNeeded();
1837
+ if (!fixFlexBasisFitContent || !performLayout) {
1838
+ node.setLayoutHadOverflow(false);
1839
+ }
1840
+ // Clean and update all display: contents nodes with a direct path to the
1841
+ // current node as they will not be traversed
1842
+ cleanupContentsNodesRecursively(node, performLayout);
1843
+
1844
+ // STEP 1: CALCULATE VALUES FOR REMAINDER OF ALGORITHM
1845
+ const mainAxis = resolveDirection(node.style.flexDirection(), direction);
1846
+ const crossAxis = resolveCrossDirection(mainAxis, direction);
1847
+ const isMainAxisRow = isRow(mainAxis);
1848
+ const isNodeFlexWrap = node.style.flexWrap() !== Wrap.NoWrap;
1849
+
1850
+ const mainAxisOwnerSize = isMainAxisRow ? ownerWidth : ownerHeight;
1851
+ const crossAxisOwnerSize = isMainAxisRow ? ownerHeight : ownerWidth;
1852
+
1853
+ const paddingAndBorderAxisMain = paddingAndBorderForAxis(node, mainAxis, direction, ownerWidth);
1854
+ const paddingAndBorderAxisCross = paddingAndBorderForAxis(node, crossAxis, direction, ownerWidth);
1855
+ const leadingPaddingAndBorderCross = node.style.computeFlexStartPaddingAndBorder(
1856
+ crossAxis,
1857
+ direction,
1858
+ ownerWidth,
1859
+ );
1860
+
1861
+ let sizingModeMainDim = isMainAxisRow ? widthSizingMode : heightSizingMode;
1862
+ const sizingModeCrossDim = isMainAxisRow ? heightSizingMode : widthSizingMode;
1863
+
1864
+ const paddingAndBorderAxisRow = isMainAxisRow
1865
+ ? paddingAndBorderAxisMain
1866
+ : paddingAndBorderAxisCross;
1867
+ const paddingAndBorderAxisColumn = isMainAxisRow
1868
+ ? paddingAndBorderAxisCross
1869
+ : paddingAndBorderAxisMain;
1870
+
1871
+ // STEP 2: DETERMINE AVAILABLE SIZE IN MAIN AND CROSS DIRECTIONS
1872
+
1873
+ const availableInnerWidth = calculateAvailableInnerDimension(
1874
+ node,
1875
+ direction,
1876
+ Dimension.Width,
1877
+ availableWidth - marginAxisRow,
1878
+ paddingAndBorderAxisRow,
1879
+ ownerWidth,
1880
+ ownerWidth,
1881
+ );
1882
+ const availableInnerHeight = calculateAvailableInnerDimension(
1883
+ node,
1884
+ direction,
1885
+ Dimension.Height,
1886
+ availableHeight - marginAxisColumn,
1887
+ paddingAndBorderAxisColumn,
1888
+ ownerHeight,
1889
+ ownerWidth,
1890
+ );
1891
+
1892
+ let availableInnerMainDim = isMainAxisRow ? availableInnerWidth : availableInnerHeight;
1893
+ const availableInnerCrossDim = isMainAxisRow ? availableInnerHeight : availableInnerWidth;
1894
+
1895
+ // STEP 3: DETERMINE FLEX BASIS FOR EACH ITEM
1896
+
1897
+ // Computed basis + margins + gap
1898
+ let totalMainDim = 0;
1899
+ totalMainDim += computeFlexBasisForChildren(
1900
+ node,
1901
+ layoutChildren,
1902
+ availableInnerWidth,
1903
+ availableInnerHeight,
1904
+ availableInnerWidth,
1905
+ availableInnerHeight,
1906
+ widthSizingMode,
1907
+ heightSizingMode,
1908
+ direction,
1909
+ mainAxis,
1910
+ performLayout,
1911
+ layoutMarkerData,
1912
+ depth,
1913
+ generationCount,
1914
+ );
1915
+
1916
+ if (childCount > 1) {
1917
+ totalMainDim +=
1918
+ node.style.computeGapForAxis(mainAxis, availableInnerMainDim) * (childCount - 1);
1919
+ }
1920
+
1921
+ const mainAxisOverflows =
1922
+ sizingModeMainDim !== SizingMode.MaxContent && totalMainDim > availableInnerMainDim;
1923
+
1924
+ if (isNodeFlexWrap && mainAxisOverflows && sizingModeMainDim === SizingMode.FitContent) {
1925
+ sizingModeMainDim = SizingMode.StretchFit;
1926
+ }
1927
+ // STEP 4: COLLECT FLEX ITEMS INTO FLEX LINES
1928
+
1929
+ // Index of the first child of the current line.
1930
+ let startOfLineIndex = 0;
1931
+
1932
+ // Number of lines.
1933
+ let lineCount = 0;
1934
+
1935
+ // Accumulated cross dimensions of all lines so far.
1936
+ let totalLineCrossDim = 0;
1937
+
1938
+ const crossAxisGap = node.style.computeGapForAxis(crossAxis, availableInnerCrossDim);
1939
+
1940
+ // Max main dimension of all the lines.
1941
+ let maxLineMainDim = 0;
1942
+ const flexLine = (node.layout.flexLine ??= {
1943
+ itemsInFlow: [],
1944
+ sizeConsumed: 0,
1945
+ numberOfAutoMargins: 0,
1946
+ endIndex: 0,
1947
+ layout: {
1948
+ totalFlexGrowFactors: 0,
1949
+ totalFlexShrinkScaledFactors: 0,
1950
+ remainingFreeSpace: 0,
1951
+ mainDim: 0,
1952
+ crossDim: 0,
1953
+ },
1954
+ });
1955
+ const lineStarts = (node.layout.flexLineStarts ??= []);
1956
+ for (; startOfLineIndex < layoutChildren.length; lineCount++) {
1957
+ lineStarts[lineCount] = startOfLineIndex;
1958
+ calculateFlexLine(
1959
+ node,
1960
+ ownerDirection,
1961
+ ownerWidth,
1962
+ mainAxisOwnerSize,
1963
+ availableInnerWidth,
1964
+ availableInnerMainDim,
1965
+ layoutChildren,
1966
+ startOfLineIndex,
1967
+ lineCount,
1968
+ flexLine,
1969
+ );
1970
+ startOfLineIndex = flexLine.endIndex;
1971
+
1972
+ // If we don't need to measure the cross axis, we can skip the entire flex
1973
+ // step.
1974
+ const canSkipFlex = !performLayout && sizingModeCrossDim === SizingMode.StretchFit;
1975
+
1976
+ // STEP 5: RESOLVING FLEXIBLE LENGTHS ON MAIN AXIS
1977
+ // Calculate the remaining available space that needs to be allocated. If
1978
+ // the main dimension size isn't known, it is computed based on the line
1979
+ // length, so there's no more space left to distribute.
1980
+
1981
+ let sizeBasedOnContent = false;
1982
+ // If we don't measure with exact main dimension we want to ensure we don't
1983
+ // violate min and max
1984
+ if (sizingModeMainDim !== SizingMode.StretchFit) {
1985
+ const style = node.style;
1986
+ const minInnerWidth =
1987
+ style.resolvedMinDimension(direction, Dimension.Width, ownerWidth, ownerWidth) -
1988
+ paddingAndBorderAxisRow;
1989
+ const maxInnerWidth =
1990
+ style.resolvedMaxDimension(direction, Dimension.Width, ownerWidth, ownerWidth) -
1991
+ paddingAndBorderAxisRow;
1992
+ const minInnerHeight =
1993
+ style.resolvedMinDimension(direction, Dimension.Height, ownerHeight, ownerWidth) -
1994
+ paddingAndBorderAxisColumn;
1995
+ const maxInnerHeight =
1996
+ style.resolvedMaxDimension(direction, Dimension.Height, ownerHeight, ownerWidth) -
1997
+ paddingAndBorderAxisColumn;
1998
+
1999
+ const minInnerMainDim = isMainAxisRow ? minInnerWidth : minInnerHeight;
2000
+ const maxInnerMainDim = isMainAxisRow ? maxInnerWidth : maxInnerHeight;
2001
+
2002
+ if (isDefined(minInnerMainDim) && flexLine.sizeConsumed < minInnerMainDim) {
2003
+ availableInnerMainDim = minInnerMainDim;
2004
+ } else if (isDefined(maxInnerMainDim) && flexLine.sizeConsumed > maxInnerMainDim) {
2005
+ availableInnerMainDim = maxInnerMainDim;
2006
+ } else {
2007
+ const useLegacyStretchBehaviour = node.hasErrata(Errata.StretchFlexBasis);
2008
+
2009
+ if (
2010
+ !useLegacyStretchBehaviour &&
2011
+ ((isDefined(flexLine.layout.totalFlexGrowFactors) &&
2012
+ flexLine.layout.totalFlexGrowFactors === 0) ||
2013
+ (isDefined(node.resolveFlexGrow()) && node.resolveFlexGrow() === 0))
2014
+ ) {
2015
+ // If we don't have any children to flex or we can't flex the node
2016
+ // itself, space we've used is all space we need. Root node also
2017
+ // should be shrunk to minimum
2018
+ availableInnerMainDim = flexLine.sizeConsumed;
2019
+ }
2020
+
2021
+ sizeBasedOnContent = !useLegacyStretchBehaviour;
2022
+ }
2023
+ }
2024
+
2025
+ if (!sizeBasedOnContent && isDefined(availableInnerMainDim)) {
2026
+ flexLine.layout.remainingFreeSpace = availableInnerMainDim - flexLine.sizeConsumed;
2027
+ } else if (flexLine.sizeConsumed < 0) {
2028
+ // availableInnerMainDim is indefinite which means the node is being
2029
+ // sized based on its content. sizeConsumed is negative which means
2030
+ // the node will allocate 0 points for its content. Consequently,
2031
+ // remainingFreeSpace is 0 - sizeConsumed.
2032
+ flexLine.layout.remainingFreeSpace = -flexLine.sizeConsumed;
2033
+ }
2034
+
2035
+ if (!canSkipFlex) {
2036
+ resolveFlexibleLength(
2037
+ node,
2038
+ flexLine,
2039
+ mainAxis,
2040
+ crossAxis,
2041
+ direction,
2042
+ ownerWidth,
2043
+ mainAxisOwnerSize,
2044
+ availableInnerMainDim,
2045
+ availableInnerCrossDim,
2046
+ availableInnerWidth,
2047
+ availableInnerHeight,
2048
+ mainAxisOverflows,
2049
+ sizingModeCrossDim,
2050
+ performLayout,
2051
+ layoutMarkerData,
2052
+ depth,
2053
+ generationCount,
2054
+ );
2055
+ }
2056
+
2057
+ node.setLayoutHadOverflow(node.layout.hadOverflow() || flexLine.layout.remainingFreeSpace < 0);
2058
+
2059
+ // STEP 6: MAIN-AXIS JUSTIFICATION & CROSS-AXIS SIZE DETERMINATION
2060
+
2061
+ // At this point, all the children have their dimensions set in the main
2062
+ // axis. Their dimensions are also set in the cross axis with the exception
2063
+ // of items that are aligned "stretch". We need to compute these stretch
2064
+ // values and set the final positions.
2065
+
2066
+ justifyMainAxis(
2067
+ node,
2068
+ flexLine,
2069
+ mainAxis,
2070
+ crossAxis,
2071
+ direction,
2072
+ sizingModeMainDim,
2073
+ sizingModeCrossDim,
2074
+ mainAxisOwnerSize,
2075
+ ownerWidth,
2076
+ availableInnerMainDim,
2077
+ availableInnerCrossDim,
2078
+ availableInnerWidth,
2079
+ performLayout,
2080
+ );
2081
+
2082
+ let containerCrossAxis = availableInnerCrossDim;
2083
+ if (
2084
+ sizingModeCrossDim === SizingMode.MaxContent ||
2085
+ sizingModeCrossDim === SizingMode.FitContent
2086
+ ) {
2087
+ // Compute the cross axis from the max cross dimension of the children.
2088
+ containerCrossAxis =
2089
+ boundAxis(
2090
+ node,
2091
+ crossAxis,
2092
+ direction,
2093
+ flexLine.layout.crossDim + paddingAndBorderAxisCross,
2094
+ crossAxisOwnerSize,
2095
+ ownerWidth,
2096
+ ) - paddingAndBorderAxisCross;
2097
+ }
2098
+
2099
+ // If there's no flex wrap, the cross dimension is defined by the
2100
+ // container.
2101
+ if (!isNodeFlexWrap && sizingModeCrossDim === SizingMode.StretchFit) {
2102
+ flexLine.layout.crossDim = availableInnerCrossDim;
2103
+ }
2104
+
2105
+ // As-per https://www.w3.org/TR/css-flexbox-1/#cross-sizing, the
2106
+ // cross-size of the line within a single-line container should be bound
2107
+ // to min/max constraints before alignment within the line. In a
2108
+ // multi-line container, affecting alignment between the lines.
2109
+ if (!isNodeFlexWrap) {
2110
+ flexLine.layout.crossDim =
2111
+ boundAxis(
2112
+ node,
2113
+ crossAxis,
2114
+ direction,
2115
+ flexLine.layout.crossDim + paddingAndBorderAxisCross,
2116
+ crossAxisOwnerSize,
2117
+ ownerWidth,
2118
+ ) - paddingAndBorderAxisCross;
2119
+ }
2120
+
2121
+ // STEP 7: CROSS-AXIS ALIGNMENT
2122
+ // We can skip child alignment if we're just measuring the container.
2123
+ if (performLayout) {
2124
+ for (const child of flexLine.itemsInFlow) {
2125
+ let leadingCrossDim = leadingPaddingAndBorderCross;
2126
+
2127
+ // For a relative children, we're either using alignItems (owner) or
2128
+ // alignSelf (child) in order to determine the position in the cross
2129
+ // axis
2130
+ const alignItem = resolveChildAlignment(node, child);
2131
+
2132
+ // If the child uses align stretch, we need to lay it out one more
2133
+ // time, this time forcing the cross-axis size to be the computed
2134
+ // cross size for the current line.
2135
+ if (
2136
+ alignItem === Align.Stretch &&
2137
+ !child.style.flexStartMarginIsAuto(crossAxis, direction) &&
2138
+ !child.style.flexEndMarginIsAuto(crossAxis, direction)
2139
+ ) {
2140
+ // If the child defines a definite size for its cross axis, there's
2141
+ // no need to stretch.
2142
+ if (!child.hasDefiniteLength(dimension(crossAxis), availableInnerCrossDim)) {
2143
+ let childMainSize = child.layout.measuredDimension(dimension(mainAxis));
2144
+ const childStyle = child.style;
2145
+ const childCrossSize = isDefined(childStyle.aspectRatio())
2146
+ ? child.style.computeMarginForAxis(crossAxis, availableInnerWidth) +
2147
+ (isMainAxisRow
2148
+ ? childMainSize / childStyle.aspectRatio()
2149
+ : childMainSize * childStyle.aspectRatio())
2150
+ : flexLine.layout.crossDim;
2151
+
2152
+ childMainSize += child.style.computeMarginForAxis(mainAxis, availableInnerWidth);
2153
+
2154
+ const childMainModeAndSize = scratchModeA;
2155
+ childMainModeAndSize.mode = SizingMode.StretchFit;
2156
+ childMainModeAndSize.size = childMainSize;
2157
+ constrainMaxSizeForMode(
2158
+ child,
2159
+ direction,
2160
+ mainAxis,
2161
+ availableInnerMainDim,
2162
+ availableInnerWidth,
2163
+ childMainModeAndSize,
2164
+ );
2165
+ const childCrossModeAndSize = scratchModeB;
2166
+ childCrossModeAndSize.mode = SizingMode.StretchFit;
2167
+ childCrossModeAndSize.size = childCrossSize;
2168
+ constrainMaxSizeForMode(
2169
+ child,
2170
+ direction,
2171
+ crossAxis,
2172
+ availableInnerCrossDim,
2173
+ availableInnerWidth,
2174
+ childCrossModeAndSize,
2175
+ );
2176
+
2177
+ const childWidth = isMainAxisRow
2178
+ ? childMainModeAndSize.size
2179
+ : childCrossModeAndSize.size;
2180
+ const childHeight = !isMainAxisRow
2181
+ ? childMainModeAndSize.size
2182
+ : childCrossModeAndSize.size;
2183
+
2184
+ const alignContent = node.style.alignContent();
2185
+ const crossAxisDoesNotGrow = alignContent !== Align.Stretch && isNodeFlexWrap;
2186
+ const childWidthSizingMode =
2187
+ isUndefined(childWidth) || (!isMainAxisRow && crossAxisDoesNotGrow)
2188
+ ? SizingMode.MaxContent
2189
+ : SizingMode.StretchFit;
2190
+ const childHeightSizingMode =
2191
+ isUndefined(childHeight) || (isMainAxisRow && crossAxisDoesNotGrow)
2192
+ ? SizingMode.MaxContent
2193
+ : SizingMode.StretchFit;
2194
+
2195
+ calculateLayoutInternal(
2196
+ child,
2197
+ childWidth,
2198
+ childHeight,
2199
+ direction,
2200
+ childWidthSizingMode,
2201
+ childHeightSizingMode,
2202
+ availableInnerWidth,
2203
+ availableInnerHeight,
2204
+ true,
2205
+ LayoutPassReason.Stretch,
2206
+ layoutMarkerData,
2207
+ depth,
2208
+ generationCount,
2209
+ );
2210
+ }
2211
+ } else {
2212
+ const remainingCrossDim =
2213
+ containerCrossAxis - child.dimensionWithMargin(crossAxis, availableInnerWidth);
2214
+
2215
+ if (
2216
+ child.style.flexStartMarginIsAuto(crossAxis, direction) &&
2217
+ child.style.flexEndMarginIsAuto(crossAxis, direction)
2218
+ ) {
2219
+ leadingCrossDim += maxOrDefined(0, remainingCrossDim / 2);
2220
+ } else if (child.style.flexEndMarginIsAuto(crossAxis, direction)) {
2221
+ // No-Op
2222
+ } else if (child.style.flexStartMarginIsAuto(crossAxis, direction)) {
2223
+ leadingCrossDim += maxOrDefined(0, remainingCrossDim);
2224
+ } else if (alignItem === Align.FlexStart) {
2225
+ // No-Op
2226
+ } else if (alignItem === Align.Center) {
2227
+ leadingCrossDim += remainingCrossDim / 2;
2228
+ } else {
2229
+ leadingCrossDim += remainingCrossDim;
2230
+ }
2231
+ }
2232
+ // And we apply the position
2233
+ child.setLayoutPosition(
2234
+ child.layout.position(flexStartEdge(crossAxis)) + totalLineCrossDim + leadingCrossDim,
2235
+ flexStartEdge(crossAxis),
2236
+ );
2237
+ }
2238
+ }
2239
+
2240
+ const appliedCrossGap = lineCount !== 0 ? crossAxisGap : 0;
2241
+ totalLineCrossDim += flexLine.layout.crossDim + appliedCrossGap;
2242
+ maxLineMainDim = maxOrDefined(maxLineMainDim, flexLine.layout.mainDim);
2243
+ }
2244
+
2245
+ // STEP 8: MULTI-LINE CONTENT ALIGNMENT
2246
+ // currentLead stores the size of the cross dim
2247
+ if (performLayout && (isNodeFlexWrap || isBaselineLayout(node))) {
2248
+ let leadPerLine = 0;
2249
+ let currentLead = leadingPaddingAndBorderCross;
2250
+ let extraSpacePerLine = 0;
2251
+
2252
+ const unclampedCrossDim =
2253
+ sizingModeCrossDim === SizingMode.StretchFit
2254
+ ? availableInnerCrossDim + paddingAndBorderAxisCross
2255
+ : node.hasDefiniteLength(dimension(crossAxis), crossAxisOwnerSize)
2256
+ ? node.getResolvedDimension(
2257
+ direction,
2258
+ dimension(crossAxis),
2259
+ crossAxisOwnerSize,
2260
+ ownerWidth,
2261
+ )
2262
+ : totalLineCrossDim + paddingAndBorderAxisCross;
2263
+
2264
+ const innerCrossDim =
2265
+ boundAxis(node, crossAxis, direction, unclampedCrossDim, crossAxisOwnerSize, ownerWidth) -
2266
+ paddingAndBorderAxisCross;
2267
+
2268
+ const remainingAlignContentDim = innerCrossDim - totalLineCrossDim;
2269
+
2270
+ const alignContent =
2271
+ remainingAlignContentDim >= 0
2272
+ ? node.style.alignContent()
2273
+ : fallbackAlignment(node.style.alignContent());
2274
+
2275
+ switch (alignContent) {
2276
+ case Align.Start:
2277
+ case Align.End:
2278
+ // No-Op
2279
+ break;
2280
+ case Align.FlexEnd:
2281
+ currentLead += remainingAlignContentDim;
2282
+ break;
2283
+ case Align.Center:
2284
+ currentLead += remainingAlignContentDim / 2;
2285
+ break;
2286
+ case Align.Stretch:
2287
+ extraSpacePerLine = remainingAlignContentDim / lineCount;
2288
+ break;
2289
+ case Align.SpaceAround:
2290
+ currentLead += remainingAlignContentDim / (2 * lineCount);
2291
+ leadPerLine = remainingAlignContentDim / lineCount;
2292
+ break;
2293
+ case Align.SpaceEvenly:
2294
+ currentLead += remainingAlignContentDim / (lineCount + 1);
2295
+ leadPerLine = remainingAlignContentDim / (lineCount + 1);
2296
+ break;
2297
+ case Align.SpaceBetween:
2298
+ if (lineCount > 1) {
2299
+ leadPerLine = remainingAlignContentDim / (lineCount - 1);
2300
+ }
2301
+ break;
2302
+ case Align.Auto:
2303
+ case Align.FlexStart:
2304
+ case Align.Baseline:
2305
+ break;
2306
+ }
2307
+
2308
+ for (let i = 0; i < lineCount; i++) {
2309
+ const lineStart = lineStarts[i]!;
2310
+
2311
+ // compute the line's height and find the endIndex
2312
+ let lineHeight = 0;
2313
+ let maxAscentForCurrentLine = 0;
2314
+ let maxDescentForCurrentLine = 0;
2315
+ let endIndex = lineStart;
2316
+ for (let ii = lineStart; ii < layoutChildren.length; ii++) {
2317
+ const child = layoutChildren[ii]!;
2318
+ if (child.style.display() === Display.None) {
2319
+ endIndex = ii + 1;
2320
+ continue;
2321
+ }
2322
+ if (child.style.positionType() !== PositionType.Absolute) {
2323
+ if (child.lineIndex !== i) {
2324
+ break;
2325
+ }
2326
+ if (child.isLayoutDimensionDefined(crossAxis)) {
2327
+ lineHeight = maxOrDefined(
2328
+ lineHeight,
2329
+ child.layout.measuredDimension(dimension(crossAxis)) +
2330
+ child.style.computeMarginForAxis(crossAxis, availableInnerWidth),
2331
+ );
2332
+ }
2333
+ if (resolveChildAlignment(node, child) === Align.Baseline) {
2334
+ const ascent =
2335
+ calculateBaseline(child) +
2336
+ child.style.computeFlexStartMargin(
2337
+ FlexDirection.Column,
2338
+ direction,
2339
+ availableInnerWidth,
2340
+ );
2341
+ const descent =
2342
+ child.layout.measuredDimension(Dimension.Height) +
2343
+ child.style.computeMarginForAxis(FlexDirection.Column, availableInnerWidth) -
2344
+ ascent;
2345
+ maxAscentForCurrentLine = maxOrDefined(maxAscentForCurrentLine, ascent);
2346
+ maxDescentForCurrentLine = maxOrDefined(maxDescentForCurrentLine, descent);
2347
+ lineHeight = maxOrDefined(
2348
+ lineHeight,
2349
+ maxAscentForCurrentLine + maxDescentForCurrentLine,
2350
+ );
2351
+ }
2352
+ }
2353
+ endIndex = ii + 1;
2354
+ }
2355
+ currentLead += i !== 0 ? crossAxisGap : 0;
2356
+ lineHeight += extraSpacePerLine;
2357
+
2358
+ for (let ii = lineStart; ii < endIndex; ii++) {
2359
+ const child = layoutChildren[ii]!;
2360
+ if (child.style.display() === Display.None) {
2361
+ continue;
2362
+ }
2363
+ if (child.style.positionType() !== PositionType.Absolute) {
2364
+ switch (resolveChildAlignment(node, child)) {
2365
+ case Align.Start:
2366
+ case Align.End:
2367
+ // Not yet implemented
2368
+ break;
2369
+ case Align.FlexStart: {
2370
+ child.setLayoutPosition(
2371
+ currentLead +
2372
+ child.style.computeFlexStartPosition(crossAxis, direction, availableInnerWidth),
2373
+ flexStartEdge(crossAxis),
2374
+ );
2375
+ break;
2376
+ }
2377
+ case Align.FlexEnd: {
2378
+ child.setLayoutPosition(
2379
+ currentLead +
2380
+ lineHeight -
2381
+ child.style.computeFlexEndMargin(crossAxis, direction, availableInnerWidth) -
2382
+ child.layout.measuredDimension(dimension(crossAxis)),
2383
+ flexStartEdge(crossAxis),
2384
+ );
2385
+ break;
2386
+ }
2387
+ case Align.Center: {
2388
+ const childHeight = child.layout.measuredDimension(dimension(crossAxis));
2389
+
2390
+ child.setLayoutPosition(
2391
+ currentLead + (lineHeight - childHeight) / 2,
2392
+ flexStartEdge(crossAxis),
2393
+ );
2394
+ break;
2395
+ }
2396
+ case Align.Stretch: {
2397
+ child.setLayoutPosition(
2398
+ currentLead +
2399
+ child.style.computeFlexStartMargin(crossAxis, direction, availableInnerWidth),
2400
+ flexStartEdge(crossAxis),
2401
+ );
2402
+
2403
+ // Remeasure child with the line height as it as been only
2404
+ // measured with the owners height yet.
2405
+ if (!child.hasDefiniteLength(dimension(crossAxis), availableInnerCrossDim)) {
2406
+ const childWidth = isMainAxisRow
2407
+ ? child.layout.measuredDimension(Dimension.Width) +
2408
+ child.style.computeMarginForAxis(mainAxis, availableInnerWidth)
2409
+ : leadPerLine + lineHeight;
2410
+
2411
+ const childHeight = !isMainAxisRow
2412
+ ? child.layout.measuredDimension(Dimension.Height) +
2413
+ child.style.computeMarginForAxis(crossAxis, availableInnerWidth)
2414
+ : leadPerLine + lineHeight;
2415
+
2416
+ if (
2417
+ !(
2418
+ inexactEquals(childWidth, child.layout.measuredDimension(Dimension.Width)) &&
2419
+ inexactEquals(childHeight, child.layout.measuredDimension(Dimension.Height))
2420
+ )
2421
+ ) {
2422
+ calculateLayoutInternal(
2423
+ child,
2424
+ childWidth,
2425
+ childHeight,
2426
+ direction,
2427
+ SizingMode.StretchFit,
2428
+ SizingMode.StretchFit,
2429
+ availableInnerWidth,
2430
+ availableInnerHeight,
2431
+ true,
2432
+ LayoutPassReason.MultilineStretch,
2433
+ layoutMarkerData,
2434
+ depth,
2435
+ generationCount,
2436
+ );
2437
+ }
2438
+ }
2439
+ break;
2440
+ }
2441
+ case Align.Baseline: {
2442
+ child.setLayoutPosition(
2443
+ currentLead +
2444
+ maxAscentForCurrentLine -
2445
+ calculateBaseline(child) +
2446
+ child.style.computeFlexStartPosition(
2447
+ FlexDirection.Column,
2448
+ direction,
2449
+ availableInnerCrossDim,
2450
+ ),
2451
+ PhysicalEdge.Top,
2452
+ );
2453
+
2454
+ break;
2455
+ }
2456
+ case Align.Auto:
2457
+ case Align.SpaceBetween:
2458
+ case Align.SpaceAround:
2459
+ case Align.SpaceEvenly:
2460
+ break;
2461
+ }
2462
+ }
2463
+ }
2464
+
2465
+ currentLead = currentLead + leadPerLine + lineHeight;
2466
+ }
2467
+ }
2468
+
2469
+ // STEP 9: COMPUTING FINAL DIMENSIONS
2470
+
2471
+ node.setLayoutMeasuredDimension(
2472
+ boundAxis(
2473
+ node,
2474
+ FlexDirection.Row,
2475
+ direction,
2476
+ availableWidth - marginAxisRow,
2477
+ ownerWidth,
2478
+ ownerWidth,
2479
+ ),
2480
+ Dimension.Width,
2481
+ );
2482
+
2483
+ node.setLayoutMeasuredDimension(
2484
+ boundAxis(
2485
+ node,
2486
+ FlexDirection.Column,
2487
+ direction,
2488
+ availableHeight - marginAxisColumn,
2489
+ ownerHeight,
2490
+ ownerWidth,
2491
+ ),
2492
+ Dimension.Height,
2493
+ );
2494
+
2495
+ // If the user didn't specify a width or height for the node, set the
2496
+ // dimensions based on the children.
2497
+ if (
2498
+ sizingModeMainDim === SizingMode.MaxContent ||
2499
+ (node.style.overflow() !== Overflow.Scroll && sizingModeMainDim === SizingMode.FitContent)
2500
+ ) {
2501
+ // Clamp the size to the min/max size, if specified, and make sure it
2502
+ // doesn't go below the padding and border amount.
2503
+ node.setLayoutMeasuredDimension(
2504
+ boundAxis(node, mainAxis, direction, maxLineMainDim, mainAxisOwnerSize, ownerWidth),
2505
+ dimension(mainAxis),
2506
+ );
2507
+ } else if (
2508
+ sizingModeMainDim === SizingMode.FitContent &&
2509
+ node.style.overflow() === Overflow.Scroll
2510
+ ) {
2511
+ node.setLayoutMeasuredDimension(
2512
+ maxOrDefined(
2513
+ minOrDefined(
2514
+ availableInnerMainDim + paddingAndBorderAxisMain,
2515
+ boundAxisWithinMinAndMax(
2516
+ node,
2517
+ direction,
2518
+ mainAxis,
2519
+ maxLineMainDim,
2520
+ mainAxisOwnerSize,
2521
+ ownerWidth,
2522
+ ),
2523
+ ),
2524
+ paddingAndBorderAxisMain,
2525
+ ),
2526
+ dimension(mainAxis),
2527
+ );
2528
+ }
2529
+
2530
+ if (
2531
+ sizingModeCrossDim === SizingMode.MaxContent ||
2532
+ (node.style.overflow() !== Overflow.Scroll && sizingModeCrossDim === SizingMode.FitContent)
2533
+ ) {
2534
+ // Clamp the size to the min/max size, if specified, and make sure it
2535
+ // doesn't go below the padding and border amount.
2536
+ node.setLayoutMeasuredDimension(
2537
+ boundAxis(
2538
+ node,
2539
+ crossAxis,
2540
+ direction,
2541
+ totalLineCrossDim + paddingAndBorderAxisCross,
2542
+ crossAxisOwnerSize,
2543
+ ownerWidth,
2544
+ ),
2545
+ dimension(crossAxis),
2546
+ );
2547
+ } else if (
2548
+ sizingModeCrossDim === SizingMode.FitContent &&
2549
+ node.style.overflow() === Overflow.Scroll
2550
+ ) {
2551
+ node.setLayoutMeasuredDimension(
2552
+ maxOrDefined(
2553
+ minOrDefined(
2554
+ availableInnerCrossDim + paddingAndBorderAxisCross,
2555
+ boundAxisWithinMinAndMax(
2556
+ node,
2557
+ direction,
2558
+ crossAxis,
2559
+ totalLineCrossDim + paddingAndBorderAxisCross,
2560
+ crossAxisOwnerSize,
2561
+ ownerWidth,
2562
+ ),
2563
+ ),
2564
+ paddingAndBorderAxisCross,
2565
+ ),
2566
+ dimension(crossAxis),
2567
+ );
2568
+ }
2569
+
2570
+ // As we only wrapped in normal direction yet, we need to reverse the
2571
+ // positions on wrap-reverse.
2572
+ if (performLayout && node.style.flexWrap() === Wrap.WrapReverse) {
2573
+ for (const child of node.getLayoutChildren()) {
2574
+ if (child.style.positionType() !== PositionType.Absolute) {
2575
+ child.setLayoutPosition(
2576
+ node.layout.measuredDimension(dimension(crossAxis)) -
2577
+ child.layout.position(flexStartEdge(crossAxis)) -
2578
+ child.layout.measuredDimension(dimension(crossAxis)),
2579
+ flexStartEdge(crossAxis),
2580
+ );
2581
+ }
2582
+ }
2583
+ }
2584
+
2585
+ if (performLayout) {
2586
+ // STEP 10: SETTING TRAILING POSITIONS FOR CHILDREN
2587
+ const needsMainTrailingPos = needsTrailingPosition(mainAxis);
2588
+ const needsCrossTrailingPos = needsTrailingPosition(crossAxis);
2589
+
2590
+ if (needsMainTrailingPos || needsCrossTrailingPos) {
2591
+ for (const child of node.getLayoutChildren()) {
2592
+ // Absolute children will be handled by their containing block since we
2593
+ // cannot guarantee that their positions are set when their parents are
2594
+ // done with layout.
2595
+ if (
2596
+ child.style.display() === Display.None ||
2597
+ child.style.positionType() === PositionType.Absolute
2598
+ ) {
2599
+ continue;
2600
+ }
2601
+ if (needsMainTrailingPos) {
2602
+ setChildTrailingPosition(node, child, mainAxis);
2603
+ }
2604
+
2605
+ if (needsCrossTrailingPos) {
2606
+ setChildTrailingPosition(node, child, crossAxis);
2607
+ }
2608
+ }
2609
+ }
2610
+
2611
+ // STEP 11: SIZING AND POSITIONING ABSOLUTE CHILDREN
2612
+ // Let the containing block layout its absolute descendants.
2613
+ if (
2614
+ node.style.positionType() !== PositionType.Static ||
2615
+ node.alwaysFormsContainingBlock ||
2616
+ depth === 1
2617
+ ) {
2618
+ layoutAbsoluteDescendants(
2619
+ node,
2620
+ node,
2621
+ isMainAxisRow ? sizingModeMainDim : sizingModeCrossDim,
2622
+ direction,
2623
+ layoutMarkerData,
2624
+ depth,
2625
+ generationCount,
2626
+ 0,
2627
+ 0,
2628
+ availableInnerWidth,
2629
+ availableInnerHeight,
2630
+ );
2631
+ }
2632
+ }
2633
+ }
2634
+
2635
+ //
2636
+ // This is a wrapper around the calculateLayoutImpl function. It determines
2637
+ // whether the layout request is redundant and can be skipped.
2638
+ //
2639
+ // Parameters:
2640
+ // Input parameters are the same as calculateLayoutImpl (see above)
2641
+ // Return parameter is true if layout was performed, false if skipped
2642
+ //
2643
+ export function calculateLayoutInternal(
2644
+ node: Node,
2645
+ availableWidth: number,
2646
+ availableHeight: number,
2647
+ ownerDirection: Direction,
2648
+ widthSizingMode: SizingMode,
2649
+ heightSizingMode: SizingMode,
2650
+ ownerWidth: number,
2651
+ ownerHeight: number,
2652
+ performLayout: boolean,
2653
+ reason: LayoutPassReason,
2654
+ layoutMarkerData: LayoutData,
2655
+ depth: number,
2656
+ generationCount: number,
2657
+ ): boolean {
2658
+ // An indefinite available size can only be measured as max-content. Yoga's
2659
+ // callers normally preserve this invariant, but combinations of nested
2660
+ // intrinsic sizing, aspect ratios, and flex constraints can lose the mode
2661
+ // while propagating an indefinite size. Normalize it here so the public API
2662
+ // remains total for otherwise valid style combinations.
2663
+ if (isUndefined(availableWidth)) {
2664
+ widthSizingMode = SizingMode.MaxContent;
2665
+ }
2666
+ if (isUndefined(availableHeight)) {
2667
+ heightSizingMode = SizingMode.MaxContent;
2668
+ }
2669
+
2670
+ const layout = node.layout;
2671
+
2672
+ depth++;
2673
+
2674
+ const needToVisitNode =
2675
+ (node.isDirty() && layout.generationCount !== generationCount) ||
2676
+ layout.configVersion !== node.config.getVersion() ||
2677
+ layout.lastOwnerDirection !== ownerDirection;
2678
+
2679
+ if (needToVisitNode) {
2680
+ // Invalidate the cached results.
2681
+ layout.nextCachedMeasurementsIndex = 0;
2682
+ layout.cachedLayout.availableWidth = -1;
2683
+ layout.cachedLayout.availableHeight = -1;
2684
+ layout.cachedLayout.widthSizingMode = SizingMode.MaxContent;
2685
+ layout.cachedLayout.heightSizingMode = SizingMode.MaxContent;
2686
+ layout.cachedLayout.computedWidth = -1;
2687
+ layout.cachedLayout.computedHeight = -1;
2688
+ }
2689
+
2690
+ let cachedResults: CachedMeasurement | null = null;
2691
+
2692
+ // Determine whether the results are already cached. We maintain a separate
2693
+ // cache for layouts and measurements. A layout operation modifies the
2694
+ // positions and dimensions for nodes in the subtree. The algorithm assumes
2695
+ // that each node gets laid out a maximum of one time per tree layout, but
2696
+ // multiple measurements may be required to resolve all of the flex
2697
+ // dimensions. We handle nodes with measure functions specially here because
2698
+ // they are the most expensive to measure, so it's worth avoiding redundant
2699
+ // measurements if at all possible.
2700
+ if (node.hasMeasureFunc()) {
2701
+ const marginAxisRow = node.style.computeMarginForAxis(FlexDirection.Row, ownerWidth);
2702
+ const marginAxisColumn = node.style.computeMarginForAxis(FlexDirection.Column, ownerWidth);
2703
+
2704
+ // First, try to use the layout cache.
2705
+ if (
2706
+ canUseCachedMeasurement(
2707
+ widthSizingMode,
2708
+ availableWidth,
2709
+ heightSizingMode,
2710
+ availableHeight,
2711
+ layout.cachedLayout.widthSizingMode,
2712
+ layout.cachedLayout.availableWidth,
2713
+ layout.cachedLayout.heightSizingMode,
2714
+ layout.cachedLayout.availableHeight,
2715
+ layout.cachedLayout.computedWidth,
2716
+ layout.cachedLayout.computedHeight,
2717
+ marginAxisRow,
2718
+ marginAxisColumn,
2719
+ node.config,
2720
+ )
2721
+ ) {
2722
+ cachedResults = layout.cachedLayout;
2723
+ } else {
2724
+ // Try to use the measurement cache.
2725
+ for (let i = 0; i < layout.nextCachedMeasurementsIndex; i++) {
2726
+ if (
2727
+ canUseCachedMeasurement(
2728
+ widthSizingMode,
2729
+ availableWidth,
2730
+ heightSizingMode,
2731
+ availableHeight,
2732
+ layout.cachedMeasurements[i]!.widthSizingMode,
2733
+ layout.cachedMeasurements[i]!.availableWidth,
2734
+ layout.cachedMeasurements[i]!.heightSizingMode,
2735
+ layout.cachedMeasurements[i]!.availableHeight,
2736
+ layout.cachedMeasurements[i]!.computedWidth,
2737
+ layout.cachedMeasurements[i]!.computedHeight,
2738
+ marginAxisRow,
2739
+ marginAxisColumn,
2740
+ node.config,
2741
+ )
2742
+ ) {
2743
+ cachedResults = layout.cachedMeasurements[i]!;
2744
+ break;
2745
+ }
2746
+ }
2747
+ }
2748
+ } else if (performLayout) {
2749
+ if (
2750
+ inexactEquals(layout.cachedLayout.availableWidth, availableWidth) &&
2751
+ inexactEquals(layout.cachedLayout.availableHeight, availableHeight) &&
2752
+ layout.cachedLayout.widthSizingMode === widthSizingMode &&
2753
+ layout.cachedLayout.heightSizingMode === heightSizingMode
2754
+ ) {
2755
+ cachedResults = layout.cachedLayout;
2756
+ }
2757
+ } else {
2758
+ for (let i = 0; i < layout.nextCachedMeasurementsIndex; i++) {
2759
+ if (
2760
+ inexactEquals(layout.cachedMeasurements[i]!.availableWidth, availableWidth) &&
2761
+ inexactEquals(layout.cachedMeasurements[i]!.availableHeight, availableHeight) &&
2762
+ layout.cachedMeasurements[i]!.widthSizingMode === widthSizingMode &&
2763
+ layout.cachedMeasurements[i]!.heightSizingMode === heightSizingMode
2764
+ ) {
2765
+ cachedResults = layout.cachedMeasurements[i]!;
2766
+ break;
2767
+ }
2768
+ }
2769
+ }
2770
+
2771
+ if (!needToVisitNode && cachedResults !== null) {
2772
+ layout.setMeasuredDimension(Dimension.Width, cachedResults.computedWidth);
2773
+ layout.setMeasuredDimension(Dimension.Height, cachedResults.computedHeight);
2774
+
2775
+ if (performLayout) {
2776
+ layoutMarkerData.cachedLayouts += 1;
2777
+ } else {
2778
+ layoutMarkerData.cachedMeasures += 1;
2779
+ }
2780
+ } else {
2781
+ calculateLayoutImpl(
2782
+ node,
2783
+ availableWidth,
2784
+ availableHeight,
2785
+ ownerDirection,
2786
+ widthSizingMode,
2787
+ heightSizingMode,
2788
+ ownerWidth,
2789
+ ownerHeight,
2790
+ performLayout,
2791
+ reason,
2792
+ layoutMarkerData,
2793
+ depth,
2794
+ generationCount,
2795
+ );
2796
+
2797
+ layout.lastOwnerDirection = ownerDirection;
2798
+ layout.configVersion = node.config.getVersion();
2799
+
2800
+ if (cachedResults === null) {
2801
+ layoutMarkerData.maxMeasureCache = Math.max(
2802
+ layoutMarkerData.maxMeasureCache,
2803
+ layout.nextCachedMeasurementsIndex + 1,
2804
+ );
2805
+
2806
+ if (layout.nextCachedMeasurementsIndex === LayoutResults.MaxCachedMeasurements) {
2807
+ layout.nextCachedMeasurementsIndex = 0;
2808
+ }
2809
+
2810
+ let newCacheEntry;
2811
+ if (performLayout) {
2812
+ // Use the single layout cache entry.
2813
+ newCacheEntry = layout.cachedLayout;
2814
+ } else {
2815
+ // Allocate a new measurement cache entry.
2816
+ newCacheEntry = layout.cachedMeasurements[layout.nextCachedMeasurementsIndex] ??=
2817
+ new CachedMeasurement();
2818
+ layout.nextCachedMeasurementsIndex++;
2819
+ }
2820
+
2821
+ newCacheEntry.availableWidth = availableWidth;
2822
+ newCacheEntry.availableHeight = availableHeight;
2823
+ newCacheEntry.widthSizingMode = widthSizingMode;
2824
+ newCacheEntry.heightSizingMode = heightSizingMode;
2825
+ newCacheEntry.computedWidth = layout.measuredDimension(Dimension.Width);
2826
+ newCacheEntry.computedHeight = layout.measuredDimension(Dimension.Height);
2827
+ }
2828
+ }
2829
+
2830
+ if (performLayout) {
2831
+ node.setLayoutDimension(node.layout.measuredDimension(Dimension.Width), Dimension.Width);
2832
+ node.setLayoutDimension(node.layout.measuredDimension(Dimension.Height), Dimension.Height);
2833
+
2834
+ node.hasNewLayout_ = true;
2835
+ node.setDirty(false);
2836
+ }
2837
+
2838
+ layout.generationCount = generationCount;
2839
+
2840
+ return needToVisitNode || cachedResults === null;
2841
+ }
2842
+
2843
+ export function calculateLayout(
2844
+ node: Node,
2845
+ ownerWidth: number,
2846
+ ownerHeight: number,
2847
+ ownerDirection: Direction,
2848
+ ): void {
2849
+ const markerData = newLayoutData();
2850
+
2851
+ // Increment the generation count. This will force the recursive routine to
2852
+ // visit all dirty nodes at least once. Subsequent visits will be skipped if
2853
+ // the input parameters don't change.
2854
+ const currentGenerationCount = ++gCurrentGenerationCount;
2855
+ node.processDimensions();
2856
+ const direction = node.resolveDirection(ownerDirection);
2857
+ let width = NaN;
2858
+ let widthSizingMode: SizingMode = SizingMode.MaxContent;
2859
+ const style = node.style;
2860
+ if (node.hasDefiniteLength(Dimension.Width, ownerWidth)) {
2861
+ width =
2862
+ node.getResolvedDimension(direction, dimension(FlexDirection.Row), ownerWidth, ownerWidth) +
2863
+ node.style.computeMarginForAxis(FlexDirection.Row, ownerWidth);
2864
+ widthSizingMode = SizingMode.StretchFit;
2865
+ } else if (
2866
+ isDefined(style.resolvedMaxDimension(direction, Dimension.Width, ownerWidth, ownerWidth))
2867
+ ) {
2868
+ width = style.resolvedMaxDimension(direction, Dimension.Width, ownerWidth, ownerWidth);
2869
+ widthSizingMode = SizingMode.FitContent;
2870
+ } else {
2871
+ width = ownerWidth;
2872
+ widthSizingMode = isUndefined(width) ? SizingMode.MaxContent : SizingMode.StretchFit;
2873
+ }
2874
+
2875
+ let height = NaN;
2876
+ let heightSizingMode: SizingMode = SizingMode.MaxContent;
2877
+ if (node.hasDefiniteLength(Dimension.Height, ownerHeight)) {
2878
+ height =
2879
+ node.getResolvedDimension(
2880
+ direction,
2881
+ dimension(FlexDirection.Column),
2882
+ ownerHeight,
2883
+ ownerWidth,
2884
+ ) + node.style.computeMarginForAxis(FlexDirection.Column, ownerWidth);
2885
+ heightSizingMode = SizingMode.StretchFit;
2886
+ } else if (
2887
+ isDefined(style.resolvedMaxDimension(direction, Dimension.Height, ownerHeight, ownerWidth))
2888
+ ) {
2889
+ height = style.resolvedMaxDimension(direction, Dimension.Height, ownerHeight, ownerWidth);
2890
+ heightSizingMode = SizingMode.FitContent;
2891
+ } else {
2892
+ height = ownerHeight;
2893
+ heightSizingMode = isUndefined(height) ? SizingMode.MaxContent : SizingMode.StretchFit;
2894
+ }
2895
+ const generationCount = node.config.isExperimentalFeatureEnabled(
2896
+ ExperimentalFeature.FixFlexBasisFitContent,
2897
+ )
2898
+ ? currentGenerationCount
2899
+ : gCurrentGenerationCount;
2900
+ if (
2901
+ calculateLayoutInternal(
2902
+ node,
2903
+ width,
2904
+ height,
2905
+ ownerDirection,
2906
+ widthSizingMode,
2907
+ heightSizingMode,
2908
+ ownerWidth,
2909
+ ownerHeight,
2910
+ true,
2911
+ LayoutPassReason.Initial,
2912
+ markerData,
2913
+ 0, // tree root
2914
+ generationCount,
2915
+ )
2916
+ ) {
2917
+ node.setPositionFromStyle(node.layout.direction(), ownerWidth, ownerHeight);
2918
+ roundLayoutResultsToPixelGrid(node, 0, 0);
2919
+ }
2920
+ }