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

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