@react-x11/components 0.13.0 → 0.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/dist/html/controls.d.ts +29 -2
  2. package/dist/html/controls.d.ts.map +1 -1
  3. package/dist/html/controls.js +70 -2
  4. package/dist/html/controls.js.map +1 -1
  5. package/dist/html/css/cascade.d.ts +42 -1
  6. package/dist/html/css/cascade.d.ts.map +1 -1
  7. package/dist/html/css/cascade.js +336 -16
  8. package/dist/html/css/cascade.js.map +1 -1
  9. package/dist/html/css/color.d.ts +18 -0
  10. package/dist/html/css/color.d.ts.map +1 -1
  11. package/dist/html/css/color.js +27 -0
  12. package/dist/html/css/color.js.map +1 -1
  13. package/dist/html/css/parse.d.ts +18 -4
  14. package/dist/html/css/parse.d.ts.map +1 -1
  15. package/dist/html/css/parse.js +66 -28
  16. package/dist/html/css/parse.js.map +1 -1
  17. package/dist/html/css/shapes.d.ts +44 -0
  18. package/dist/html/css/shapes.d.ts.map +1 -0
  19. package/dist/html/css/shapes.js +194 -0
  20. package/dist/html/css/shapes.js.map +1 -0
  21. package/dist/html/css/style.d.ts +128 -15
  22. package/dist/html/css/style.d.ts.map +1 -1
  23. package/dist/html/css/style.js +547 -144
  24. package/dist/html/css/style.js.map +1 -1
  25. package/dist/html/css/transform.d.ts +85 -0
  26. package/dist/html/css/transform.d.ts.map +1 -0
  27. package/dist/html/css/transform.js +425 -0
  28. package/dist/html/css/transform.js.map +1 -0
  29. package/dist/html/css/ua.d.ts.map +1 -1
  30. package/dist/html/css/ua.js +14 -2
  31. package/dist/html/css/ua.js.map +1 -1
  32. package/dist/html/css/values.d.ts +4 -0
  33. package/dist/html/css/values.d.ts.map +1 -1
  34. package/dist/html/css/values.js.map +1 -1
  35. package/dist/html/fonts.d.ts +55 -5
  36. package/dist/html/fonts.d.ts.map +1 -1
  37. package/dist/html/fonts.js +284 -23
  38. package/dist/html/fonts.js.map +1 -1
  39. package/dist/html/layout/axes.d.ts +14 -0
  40. package/dist/html/layout/axes.d.ts.map +1 -0
  41. package/dist/html/layout/axes.js +28 -0
  42. package/dist/html/layout/axes.js.map +1 -0
  43. package/dist/html/layout/block.d.ts +104 -12
  44. package/dist/html/layout/block.d.ts.map +1 -1
  45. package/dist/html/layout/block.js +410 -39
  46. package/dist/html/layout/block.js.map +1 -1
  47. package/dist/html/layout/boxes.d.ts +72 -1
  48. package/dist/html/layout/boxes.d.ts.map +1 -1
  49. package/dist/html/layout/boxes.js +40 -2
  50. package/dist/html/layout/boxes.js.map +1 -1
  51. package/dist/html/layout/cache.d.ts +1 -1
  52. package/dist/html/layout/cache.d.ts.map +1 -1
  53. package/dist/html/layout/cache.js +6 -0
  54. package/dist/html/layout/cache.js.map +1 -1
  55. package/dist/html/layout/css-grid.d.ts.map +1 -1
  56. package/dist/html/layout/css-grid.js +144 -24
  57. package/dist/html/layout/css-grid.js.map +1 -1
  58. package/dist/html/layout/flex.d.ts.map +1 -1
  59. package/dist/html/layout/flex.js +585 -85
  60. package/dist/html/layout/flex.js.map +1 -1
  61. package/dist/html/layout/inline.d.ts +3 -0
  62. package/dist/html/layout/inline.d.ts.map +1 -1
  63. package/dist/html/layout/inline.js +144 -54
  64. package/dist/html/layout/inline.js.map +1 -1
  65. package/dist/html/layout/multicol.d.ts +42 -0
  66. package/dist/html/layout/multicol.d.ts.map +1 -0
  67. package/dist/html/layout/multicol.js +897 -0
  68. package/dist/html/layout/multicol.js.map +1 -0
  69. package/dist/html/layout/shaping.d.ts +7 -0
  70. package/dist/html/layout/shaping.d.ts.map +1 -1
  71. package/dist/html/layout/shaping.js +25 -20
  72. package/dist/html/layout/shaping.js.map +1 -1
  73. package/dist/html/layout/table.d.ts.map +1 -1
  74. package/dist/html/layout/table.js +97 -31
  75. package/dist/html/layout/table.js.map +1 -1
  76. package/dist/html/node.d.ts +4 -0
  77. package/dist/html/node.d.ts.map +1 -1
  78. package/dist/html/node.js +319 -47
  79. package/dist/html/node.js.map +1 -1
  80. package/dist/html/paint.d.ts +83 -5
  81. package/dist/html/paint.d.ts.map +1 -1
  82. package/dist/html/paint.js +1474 -217
  83. package/dist/html/paint.js.map +1 -1
  84. package/dist/html/svg.d.ts +39 -9
  85. package/dist/html/svg.d.ts.map +1 -1
  86. package/dist/html/svg.js +482 -37
  87. package/dist/html/svg.js.map +1 -1
  88. package/dist/html/widgets.js +87 -7
  89. package/dist/html/widgets.js.map +1 -1
  90. package/dist/html/woff2.d.ts +14 -0
  91. package/dist/html/woff2.d.ts.map +1 -0
  92. package/dist/html/woff2.js +608 -0
  93. package/dist/html/woff2.js.map +1 -0
  94. package/dist/maps/controller.d.ts +17 -1
  95. package/dist/maps/controller.d.ts.map +1 -1
  96. package/dist/maps/controller.js +27 -7
  97. package/dist/maps/controller.js.map +1 -1
  98. package/dist/richtext/node.d.ts +8 -1
  99. package/dist/richtext/node.d.ts.map +1 -1
  100. package/dist/richtext/node.js +5 -1
  101. package/dist/richtext/node.js.map +1 -1
  102. package/dist/richtext/runs.d.ts +25 -6
  103. package/dist/richtext/runs.d.ts.map +1 -1
  104. package/dist/richtext/runs.js +240 -24
  105. package/dist/richtext/runs.js.map +1 -1
  106. package/package.json +1 -1
  107. package/src/html/controls.ts +85 -2
  108. package/src/html/css/cascade.ts +366 -8
  109. package/src/html/css/color.ts +29 -0
  110. package/src/html/css/parse.ts +71 -31
  111. package/src/html/css/shapes.ts +239 -0
  112. package/src/html/css/style.ts +652 -147
  113. package/src/html/css/transform.ts +459 -0
  114. package/src/html/css/ua.ts +15 -2
  115. package/src/html/css/values.ts +4 -0
  116. package/src/html/fonts.ts +312 -29
  117. package/src/html/layout/axes.ts +62 -0
  118. package/src/html/layout/block.ts +466 -37
  119. package/src/html/layout/boxes.ts +95 -3
  120. package/src/html/layout/cache.ts +6 -0
  121. package/src/html/layout/css-grid.ts +169 -32
  122. package/src/html/layout/flex.ts +686 -79
  123. package/src/html/layout/inline.ts +149 -55
  124. package/src/html/layout/multicol.ts +977 -0
  125. package/src/html/layout/shaping.ts +27 -18
  126. package/src/html/layout/table.ts +105 -35
  127. package/src/html/node.ts +354 -48
  128. package/src/html/paint.ts +1804 -315
  129. package/src/html/svg.ts +573 -36
  130. package/src/html/widgets.ts +95 -9
  131. package/src/html/woff2.ts +612 -0
  132. package/src/maps/controller.ts +28 -7
  133. package/src/richtext/node.ts +13 -2
  134. package/src/richtext/runs.ts +313 -35
@@ -20,13 +20,15 @@
20
20
  // independent Yoga pass inside the measure call. Not a nested Yoga node:
21
21
  // that would need the whole child subtree mirrored into Yoga's tree, and the
22
22
  // measure seam already answers the only question the outer pass asks. What
23
- // the leaf shape costs is stretch — a stretched item's box grows but its
24
- // contents are not re-laid at the stretched height.
23
+ // the leaf shape costs is a layout: an item the flex pass made taller or
24
+ // shorter than its content, which lays what is in it out by its height — a
25
+ // flex or grid container, or a box holding a percentage of a height — is
26
+ // laid out again at that height (`layoutItemAt`).
25
27
  import { Yoga, layoutLoaded } from 'react-x11/yoga';
26
28
  import { AUTO, gapOf, isPct, resolve, resolveOrNull } from '../css/values.js';
27
29
  import { scrolls } from '../css/style.js';
28
30
  import { Box, PAINT_ORDER, isBlank } from './boxes.js';
29
- import { FLEXED_HEIGHT, MIN_CONTENT_PROBE, clampHeight, clampWidth, contentSizedWidth, exactMinContent, intrinsicWidth, measureIntrinsicWidth, moveTo, percentBaseInside, percentHeightsIn, positionOutOfFlow, replacedRatio, transferredHeight, transferredWidth, heightFromWidth, widthFromHeight, resolveEdges, } from './block.js';
31
+ import { FLEXED_HEIGHT, MIN_CONTENT_PROBE, USED_HEIGHT, centreButton, clampHeight, clampWidth, contentSizedWidth, exactMinContent, intrinsicWidth, measureIntrinsicWidth, moveTo, percentBaseInside, percentHeightsIn, positionOutOfFlow, replacedRatio, transferredHeight, transferredWidth, heightFromWidth, widthFromHeight, resolveEdges, } from './block.js';
30
32
  import { layoutGrid } from './css-grid.js';
31
33
  import { firstBaselineIn } from './inline.js';
32
34
  // `react-x11/yoga` re-exports yoga's own declarations, so the node shape and
@@ -97,9 +99,14 @@ export function layoutFlex(box, ctx, contentWidth) {
97
99
  // percentage that resolves, or what `aspect-ratio` makes of the width —
98
100
  // less the padding and borders a `border-box` height holds, which put
99
101
  // `h-16 py-2 items-center` eight pixels low. Where it is not, the limits
100
- // on it, so a column `min-h-screen` gives its `flex-1` the rest.
102
+ // on it, so a column `min-h-screen` gives its `flex-1` the rest — or the
103
+ // height the column it is an item of flexed it to (`USED_HEIGHT`), which
104
+ // its items are laid out in and take no percentage of.
101
105
  const definite = percentBaseInside(box);
102
- const height = Number.isFinite(definite) ? definite : null;
106
+ const own = Number.isFinite(definite);
107
+ const height = USED_HEIGHT.get(box) ?? (own ? definite : null);
108
+ // whether its content may come to more than it is tall
109
+ let capped = height !== null;
103
110
  if (height !== null)
104
111
  root.setHeight(height);
105
112
  else {
@@ -109,8 +116,10 @@ export function layoutFlex(box, ctx, contentWidth) {
109
116
  if (min > 0)
110
117
  root.setMinHeight(min);
111
118
  const max = clampHeight(box, Infinity) - extra;
112
- if (Number.isFinite(max))
119
+ if (Number.isFinite(max)) {
113
120
  root.setMaxHeight(Math.max(0, max));
121
+ capped = true;
122
+ }
114
123
  }
115
124
  const flowing = [];
116
125
  let reordered = false;
@@ -138,21 +147,59 @@ export function layoutFlex(box, ctx, contentWidth) {
138
147
  for (const child of flowing) {
139
148
  const node = Y.Node.create(flexConfig());
140
149
  resolveEdges(child, contentWidth);
141
- const laid = { width: NaN, height: NaN, set: NaN, stretch: NaN };
142
- applyItem(node, child, ctx, contentWidth, laid, height !== null);
150
+ const laid = {
151
+ width: NaN,
152
+ height: NaN,
153
+ tall: NaN,
154
+ set: NaN,
155
+ stretch: NaN,
156
+ main: NaN,
157
+ least: NaN,
158
+ held: NaN,
159
+ auto: false,
160
+ };
161
+ applyItem(node, child, ctx, contentWidth, laid, height !== null, own);
143
162
  root.insertChild(node, items.length);
144
163
  items.push({ box: child, node, laid });
145
164
  }
146
165
  const direction = box.style.direction === 'rtl' ? Y.DIRECTION_RTL : Y.DIRECTION_LTR;
147
166
  const row = box.style.flexDirection.startsWith('row');
148
- const calculate = () => root.calculateLayout(bounded ? contentWidth : Number.NaN, height ?? Number.NaN, direction);
167
+ // frozen at its least size on the one line of a box that does not
168
+ // wrap, by a basis where Yoga takes one along the main axis
169
+ // (`applyItem`'s `setBasis`)
170
+ const hold = {
171
+ frozen: box.style.flexWrap === 'nowrap',
172
+ basis: row ? bounded : height !== null,
173
+ };
174
+ const ask = () => root.calculateLayout(bounded ? contentWidth : Number.NaN, height ?? Number.NaN, direction);
175
+ // the one line of a box that does not wrap may be short of room, where
176
+ // the box has a size along it that its items do not make: and Yoga's
177
+ // answer for a line every item of which its minimum stops is put right
178
+ // (`stoppedLine`)
179
+ const short = hold.frozen && (row ? bounded : capped);
180
+ // each item's size along the main axis, read the once a layout (`Laid`)
181
+ const read = () => {
182
+ for (const { node, laid } of items) {
183
+ laid.main = row ? node.getComputedWidth() : node.getComputedHeight();
184
+ }
185
+ };
186
+ const calculate = () => {
187
+ ask();
188
+ read();
189
+ if (!short)
190
+ return;
191
+ const room = row ? contentWidth : (height ?? root.getComputedHeight());
192
+ const gap = row ? columnGap : rowGap;
193
+ if (stoppedLine(items, row, room, gap, hold, ask))
194
+ read();
195
+ };
149
196
  ctx.flexDepth = depth + 1;
150
197
  try {
151
198
  calculate();
152
199
  // an item Yoga shrank under what its content comes to is kept to it,
153
200
  // and the row is laid out again (`autoMinimums`) — which may shrink
154
201
  // another under its own, a few times over at most
155
- for (let pass = 0; pass < 4 && autoMinimums(items, row, ctx, contentWidth); pass += 1) {
202
+ for (let pass = 0; pass < 4 && autoMinimums(items, row, ctx, contentWidth, hold); pass += 1) {
156
203
  calculate();
157
204
  }
158
205
  }
@@ -163,7 +210,7 @@ export function layoutFlex(box, ctx, contentWidth) {
163
210
  // one pass of its own, which drops a margin (`lineMarginFix`)
164
211
  const baselines = box.style.alignItems === 'baseline' ||
165
212
  items.some(({ box: child }) => child.style.alignSelf === 'baseline');
166
- let bottom = placeItems(box, ctx, items, row, height, box.style.flexWrap === 'wrap' || baselines);
213
+ let bottom = placeItems(box, ctx, items, row, own, box.style.flexWrap === 'wrap' || baselines, contentWidth);
167
214
  // Items aligned by their baselines, which Yoga cannot see, are aligned
168
215
  // with their baselines known (`baselineLines`)
169
216
  // by the flex layout again, set at their lines' starts with the margins
@@ -191,7 +238,7 @@ export function layoutFlex(box, ctx, contentWidth) {
191
238
  finally {
192
239
  ctx.flexDepth = depth;
193
240
  }
194
- bottom = placeItems(box, ctx, items, row, height, box.style.flexWrap === 'wrap', started);
241
+ bottom = placeItems(box, ctx, items, row, own, box.style.flexWrap === 'wrap', contentWidth, started);
195
242
  }
196
243
  // A replaced item with a ratio that is stretched across a line of a
197
244
  // definite size takes its flex base size from the stretched one through
@@ -209,7 +256,7 @@ export function layoutFlex(box, ctx, contentWidth) {
209
256
  finally {
210
257
  ctx.flexDepth = depth;
211
258
  }
212
- bottom = placeItems(box, ctx, items, row, height, box.style.flexWrap === 'wrap' || baselines);
259
+ bottom = placeItems(box, ctx, items, row, own, box.style.flexWrap === 'wrap' || baselines, contentWidth);
213
260
  }
214
261
  const contentHeight = root.getComputedHeight();
215
262
  root.freeRecursive();
@@ -219,7 +266,7 @@ export function layoutFlex(box, ctx, contentWidth) {
219
266
  * base size the ratio makes of the stretched size; whether any changed. */
220
267
  function keepRatios(container, items, row) {
221
268
  let changed = false;
222
- for (const { box, node } of items) {
269
+ for (const { box, node, laid } of items) {
223
270
  if (box.kind !== 'replaced' || !stretches(box, container.style, row)) {
224
271
  continue;
225
272
  }
@@ -234,10 +281,17 @@ function keepRatios(container, items, row) {
234
281
  box.horizontalExtra
235
282
  : Math.max(0, node.getComputedWidth() - box.horizontalExtra) / ratio +
236
283
  box.verticalExtra;
237
- const main = row ? node.getComputedWidth() : node.getComputedHeight();
238
- if (Math.abs(basis - main) < 0.5)
284
+ if (Math.abs(basis - laid.main) < 0.5)
239
285
  continue;
240
286
  node.setFlexBasis(basis);
287
+ // one held at its least size (`holdAt`) flexes from the new basis, no
288
+ // further down than that
289
+ if (!Number.isNaN(laid.held)) {
290
+ laid.held = NaN;
291
+ laid.auto = false;
292
+ node.setFlexGrow(style.flexGrow);
293
+ node.setFlexShrink(style.flexShrink);
294
+ }
241
295
  changed = true;
242
296
  }
243
297
  return changed;
@@ -357,9 +411,13 @@ function orderOf(box) {
357
411
  /** Lay each item out where the flex layout put it, at the size it gave it,
358
412
  * and answer where the lowest ends. `started` are items set at their
359
413
  * lines' starts whatever their own alignment says (`baselineLines`). */
360
- function placeItems(box, ctx, items, row, height,
414
+ function placeItems(box, ctx, items, row,
415
+ /** Whether the flex box has a definite height. */
416
+ own,
361
417
  /** Whether Yoga aligned the lines in its pass that drops a margin. */
362
- lined, started) {
418
+ lined,
419
+ /** The flex box's content width: its items' containing block's. */
420
+ contentWidth, started) {
363
421
  let bottom = 0;
364
422
  for (const { box: child, node, laid } of items) {
365
423
  const across = lined
@@ -367,8 +425,8 @@ lined, started) {
367
425
  : 0;
368
426
  const left = box.contentX + node.getComputedLeft() + (row ? 0 : across);
369
427
  const top = box.contentY + node.getComputedTop() + (row ? across : 0);
370
- const width = meant(node.getComputedWidth(), laid.set, laid.stretch, laid.width);
371
- const itemHeight = node.getComputedHeight();
428
+ const width = meant(row ? laid.main : node.getComputedWidth(), laid.set, laid.stretch, laid.width, row ? laid.held : NaN);
429
+ const itemHeight = row ? node.getComputedHeight() : laid.main;
372
430
  // The item is laid out again at the width the flex pass settled on: the
373
431
  // measure function answered a question, and the answer is not a layout —
374
432
  // its line breaks were computed against a width that may have changed
@@ -378,15 +436,18 @@ lined, started) {
378
436
  // 9.8): stretched across a row's line, or flexed in a column of a
379
437
  // height of its own. What is in it takes its percentages of that —
380
438
  // the `h-full` in a stretched sidebar — where they had nothing to be
381
- // of, and the item is laid out again for them.
439
+ // of, and the item is laid out again for them; and so is one that is a
440
+ // flex or grid container itself, whose own items are laid out in that
441
+ // height (9.4, step 11: "redo layout for its contents, treating this
442
+ // used size as its definite cross size").
382
443
  const definite = row
383
444
  ? stretches(child, box.style)
384
445
  ? itemHeight
385
446
  : null
386
- : height !== null
447
+ : own
387
448
  ? itemHeight
388
449
  : null;
389
- layoutItemAt(child, ctx, left, top, width, itemHeight, laid, definite, !row);
450
+ layoutItemAt(child, ctx, left, top, width, itemHeight, laid, definite, contentWidth, !row);
390
451
  // to the end of its margin box, as Yoga measured the box: a negative
391
452
  // margin takes the item's end back in, which Codex's search field
392
453
  // hangs a pixel over its form's border with, top and bottom
@@ -397,6 +458,8 @@ lined, started) {
397
458
  function layoutItemAt(box, ctx, x, y, width, height, laid,
398
459
  /** Its border box's height where the flex layout made it definite. */
399
460
  definite,
461
+ /** The flex box's content width, the item's containing block's. */
462
+ containing,
400
463
  /** Whether its height is the one the flex layout gave it, in a column,
401
464
  * whatever its own says: flexed, it is shrunk as well as grown. */
402
465
  column = false) {
@@ -409,32 +472,78 @@ column = false) {
409
472
  box.height = height;
410
473
  return;
411
474
  }
412
- const inner = definite === null || !percentHeightsIn(box)
413
- ? null
414
- : Math.max(0, definite - box.verticalExtra);
415
- if (inner !== null && !Object.is(inner, percentBaseInside(box))) {
416
- FLEXED_HEIGHT.set(box, inner);
417
- try {
418
- ctx.layoutSubtree(box, width);
475
+ const sameWidth = Math.abs(laid.width - width) <= 0.01;
476
+ // The content height the item is laid out at, where the flex layout gave
477
+ // it one that is not its own already: NaN where its content says how
478
+ // tall it is.
479
+ let tall = NaN;
480
+ // A flex or grid container shares its height out among its items: an
481
+ // `auto` margin takes what is free of it (8.1), a `flex: 1` grows into it
482
+ // (9.7), its lines are as tall as it (9.4), its `fr` rows fill it. So it
483
+ // is laid out in the height the flex layout gave it, definite or not —
484
+ // the one a column with no height of its own flexed it to is its used
485
+ // size all the same (9.7), and `min-h-screen flex flex-col` around a
486
+ // `flex-1 flex items-center` centres what is in that. At its content's
487
+ // own height there is nothing to share, and the layout it has is that
488
+ // one: the tallest card of a row is laid out the once. A table shares
489
+ // what it is given past its rows among them (`layoutTable`), and a
490
+ // height short of them is none it can have: it is laid out again where
491
+ // it was given more.
492
+ const given = definite ?? (column ? height : null);
493
+ let shares = false;
494
+ if (given !== null && (box.kind === 'flex' || box.kind === 'table')) {
495
+ const natural = naturalHeight(box, ctx, width, containing, laid);
496
+ shares =
497
+ box.kind === 'flex'
498
+ ? !(Math.abs(natural - given) <= 0.01)
499
+ : !(natural >= given - 0.01);
500
+ }
501
+ if (given !== null) {
502
+ const inner = Math.max(0, given - box.verticalExtra);
503
+ if (Object.is(inner, percentBaseInside(box))) {
504
+ // the height it has of its own
419
505
  }
420
- finally {
421
- FLEXED_HEIGHT.delete(box);
506
+ else if (shares || (definite !== null && percentHeightsIn(box))) {
507
+ tall = inner;
422
508
  }
423
509
  }
424
510
  // `measureBox` lays the box out at (0, 0); re-running it at the final width
425
511
  // and then moving it is one pass, not two, because the second call is the
426
- // one whose result is kept — or no pass, where the last measure was at
427
- // this width. An item Yoga never measured has none, NaN, and is laid out.
428
- else if (Math.abs(laid.width - width) <= 0.01)
512
+ // one whose result is kept — or no pass, where the last layout was at
513
+ // this width and this height. An item Yoga never measured has none, NaN,
514
+ // and is laid out.
515
+ if (sameWidth && Object.is(laid.tall, tall))
429
516
  box.height = laid.height;
430
- else
431
- ctx.layoutSubtree(box, width);
517
+ else if (Number.isNaN(tall)) {
518
+ layoutNaturally(box, ctx, width, containing, laid);
519
+ }
520
+ else {
521
+ // what is in it takes its percentages of a definite height alone (9.8)
522
+ const heights = definite !== null ? FLEXED_HEIGHT : USED_HEIGHT;
523
+ heights.set(box, tall);
524
+ try {
525
+ ctx.layoutSubtree(box, width, containing);
526
+ }
527
+ finally {
528
+ heights.delete(box);
529
+ }
530
+ laid.width = box.width;
531
+ laid.height = box.height;
532
+ laid.tall = tall;
533
+ }
432
534
  // A stretched item is taller than its content, and the box has to say so
433
535
  // or its background stops short of the row — and it is no taller than its
434
536
  // line where its content is, or than what its ratio makes of its width:
435
- // its height is the line's (CSS Flexbox 9.4, step 11).
436
- if (height > box.height || column || definite !== null)
537
+ // its height is the line's (CSS Flexbox 9.4, step 11). But for a table,
538
+ // whose rows are the least it can be, however short its line (CSS 2.1
539
+ // 17.5.3, as a height of its own is; Blink: "Tables can't shrink below
540
+ // their min-intrinsic size").
541
+ if (height > box.height ||
542
+ ((column || definite !== null) && box.kind !== 'table')) {
437
543
  box.height = height;
544
+ }
545
+ // and a button's content is centred in the height it came to
546
+ centreButton(box);
438
547
  moveTo(box, x, y);
439
548
  }
440
549
  // What a new Yoga node already is is not said again. A call into the
@@ -461,8 +570,11 @@ function applyContainer(node, style) {
461
570
  node.setAlignContent(lines);
462
571
  }
463
572
  function applyItem(node, box, ctx, containingWidth, laid,
464
- /** Whether the flex box has a height of its own. */
465
- tall) {
573
+ /** Whether the flex box has a height its items are laid out in. */
574
+ tall,
575
+ /** Whether that height is definite, and a percentage may be of it: one a
576
+ * column flexed the box to is not (`USED_HEIGHT`). */
577
+ own) {
466
578
  const style = box.style;
467
579
  laid.stretch = containingWidth - box.marginLeft - box.marginRight;
468
580
  // an `auto` margin takes the free space on its side, which is how
@@ -492,11 +604,20 @@ tall) {
492
604
  if (style.width !== AUTO) {
493
605
  laid.set = setLength(node, true, style.width, containingWidth, across);
494
606
  }
495
- if (style.height !== AUTO)
607
+ // a percentage of a height that is not definite is `auto` (CSS 2.1 10.5),
608
+ // which Yoga makes of one where the flex box has no height at all
609
+ if (style.height !== AUTO && (own || !tall || !isPct(style.height))) {
496
610
  setLength(node, false, style.height, NaN, down);
611
+ }
612
+ // the least it may be along each axis, its border box's, as Yoga is
613
+ // told it
614
+ let leastWidth = NaN;
615
+ let leastHeight = NaN;
497
616
  const minWidth = resolveOrNull(style.minWidth, containingWidth);
498
- if (minWidth !== null)
499
- node.setMinWidth(minWidth + across);
617
+ if (minWidth !== null) {
618
+ leastWidth = minWidth + across;
619
+ node.setMinWidth(leastWidth);
620
+ }
500
621
  if (style.maxWidth !== 'none') {
501
622
  const maxWidth = resolveOrNull(style.maxWidth, containingWidth);
502
623
  if (maxWidth !== null)
@@ -512,20 +633,25 @@ tall) {
512
633
  laid.set = size(style.widthKeyword);
513
634
  node.setWidth(laid.set);
514
635
  }
515
- if (style.minWidthKeyword)
516
- node.setMinWidth(size(style.minWidthKeyword));
636
+ if (style.minWidthKeyword) {
637
+ leastWidth = size(style.minWidthKeyword);
638
+ node.setMinWidth(leastWidth);
639
+ }
517
640
  if (style.maxWidthKeyword)
518
641
  node.setMaxWidth(size(style.maxWidthKeyword));
519
642
  }
520
643
  const minHeight = resolveOrNull(style.minHeight, NaN);
521
- if (minHeight !== null)
522
- node.setMinHeight(minHeight + down);
644
+ if (minHeight !== null) {
645
+ leastHeight = minHeight + down;
646
+ node.setMinHeight(leastHeight);
647
+ }
523
648
  if (style.maxHeight !== 'none') {
524
649
  const maxHeight = resolveOrNull(style.maxHeight, NaN);
525
650
  if (maxHeight !== null)
526
651
  node.setMaxHeight(maxHeight + down);
527
652
  }
528
653
  const row = box.parent?.style.flexDirection.startsWith('row') ?? true;
654
+ laid.least = row ? leastWidth : leastHeight;
529
655
  // Yoga takes a length for a basis only where the flex box's main size is
530
656
  // definite, and else the item's own size along it: down a column of no
531
657
  // height of its own it read `flex: 0 0 3rem` as the item's height, or
@@ -561,6 +687,10 @@ tall) {
561
687
  else if (style.flexBasis === 'content' || style.flexBasis === AUTO) {
562
688
  // Yoga's own
563
689
  }
690
+ else if (isPct(style.flexBasis) && !row && tall && !own) {
691
+ // a percentage of a main size that is not definite is `content` (CSS
692
+ // Flexbox 7.2.3): Yoga's own, down a column
693
+ }
564
694
  else if (isPct(style.flexBasis)) {
565
695
  // a percentage of the main size, which is known across a row
566
696
  const basis = style.flexBasis;
@@ -656,7 +786,12 @@ tall) {
656
786
  const width = meant(w, laid.set - extra, laid.stretch - extra);
657
787
  const given = hm === Y.MEASURE_MODE_EXACTLY && Number.isFinite(h);
658
788
  const exact = wm === Y.MEASURE_MODE_EXACTLY && Number.isFinite(width);
659
- const through = widthThrough && given && !exact ? widthThrough(h) : null;
789
+ // the height it was flexed to, that is: one its automatic minimum
790
+ // holds it at (`holdAt`) is its content's, which a replaced element's
791
+ // width follows through its ratio and no other's does (CSS Sizing 4,
792
+ // 5.1)
793
+ const flexed = box.kind === 'replaced' || !laid.auto;
794
+ const through = widthThrough && given && !exact && flexed ? widthThrough(h) : null;
660
795
  // along a row, a replaced element is its own width wherever there is
661
796
  // room for less — its flex base size is not fitted to the room (9.2)
662
797
  // — and across a column it is fitted, as any item is
@@ -673,10 +808,8 @@ tall) {
673
808
  }
674
809
  let answer = answers.get(inner);
675
810
  if (answer === undefined) {
676
- answer = measureBox(box, ctx, inner);
811
+ answer = measureBox(box, ctx, inner, containingWidth, laid);
677
812
  answers.set(inner, answer);
678
- laid.width = box.width;
679
- laid.height = box.height;
680
813
  }
681
814
  return answer;
682
815
  });
@@ -752,13 +885,174 @@ extra) {
752
885
  * 9.2, `flex-basis: auto`), rather than as wide as the row; asked for one
753
886
  * exactly, it is that wide. Its height is its content's at that width.
754
887
  */
755
- function measureBox(box, ctx, inner) {
756
- ctx.layoutSubtree(box, inner + box.horizontalExtra);
888
+ function measureBox(box, ctx, inner,
889
+ /** The flex box's content width, the item's containing block's. */
890
+ containing, laid) {
891
+ // its padding as Yoga has it, a percentage of the flex box's width: a
892
+ // probe of its content's width leaves a percentage at none
893
+ resolveEdges(box, containing);
894
+ const width = inner + box.horizontalExtra;
895
+ // its height at this width is known, where a layout since has left it
896
+ // another: the answer is the height, and the layout `layoutItemAt`'s
897
+ const kept = keptNatural(box, ctx, width, containing);
898
+ if (kept !== null && kept.serial !== box.layoutSerial) {
899
+ return {
900
+ width: inner,
901
+ height: Math.max(0, kept.height - box.verticalExtra),
902
+ };
903
+ }
904
+ layoutNaturally(box, ctx, width, containing, laid);
757
905
  return {
758
906
  width: inner,
759
907
  height: Math.max(0, box.height - box.verticalExtra),
760
908
  };
761
909
  }
910
+ /** An item's height as its content's own makes it at a border-box width,
911
+ * where this pass has laid it out so (`layoutNaturally`), and else NaN. */
912
+ function naturalHeight(box, ctx, width, containing, laid) {
913
+ if (Math.abs(laid.width - width) <= 0.01 && Number.isNaN(laid.tall)) {
914
+ return laid.height;
915
+ }
916
+ return keptNatural(box, ctx, width, containing)?.height ?? NaN;
917
+ }
918
+ /**
919
+ * Give an item the layout its content's own height makes of it at a
920
+ * border-box width, and note it (`Laid`): the one this pass has made of it
921
+ * there already, where it still has it.
922
+ *
923
+ * A flex box laid out a second time — at the height a stretch gave it, its
924
+ * own flex layout made anew (`layoutItemAt`) — asks of each item what it
925
+ * asked the first time, and the item's layout at a width is what it was.
926
+ * Kept for the pass, a card laid out again for its height lays nothing in
927
+ * it out that is where it was; made again at every layout of the box
928
+ * around it, a card in a card in a card laid its innermost out twice a
929
+ * level.
930
+ */
931
+ function layoutNaturally(box, ctx, width,
932
+ /** The flex box's content width, the item's containing block's. */
933
+ containing, laid) {
934
+ const kept = keptNatural(box, ctx, width, containing);
935
+ if (kept !== null && kept.serial === box.layoutSerial) {
936
+ // put back, where the flex layout stretched it
937
+ box.height = kept.height;
938
+ }
939
+ else {
940
+ const at = ctx.positioned.length;
941
+ ctx.layoutSubtree(box, width, containing);
942
+ // a replaced box is sized, not laid out, and has no layout to count
943
+ if (box.kind !== 'replaced') {
944
+ NATURAL.set(box, {
945
+ ctx,
946
+ serial: box.layoutSerial,
947
+ width,
948
+ containing: readsContaining(box) ? containing : NaN,
949
+ base: box.percentHeightBase,
950
+ edges: edgesOf(box),
951
+ height: box.height,
952
+ queued: ctx.positioned.length > at ? { at, first: ctx.positioned[at] } : null,
953
+ drawn: NaN,
954
+ content: NaN,
955
+ });
956
+ }
957
+ }
958
+ laid.width = box.width;
959
+ laid.height = box.height;
960
+ laid.tall = NaN;
961
+ }
962
+ /**
963
+ * The layout this pass made of an item at its content's own height
964
+ * (`layoutNaturally`), where one at `width` now would be the same: the same
965
+ * width, the same edges — a percentage among them resolved, as the layout
966
+ * leaves them — the same containing block's width where a limit on its own
967
+ * is a percentage of it, and the same height for its percentages, where its
968
+ * own style reads that at all, which the flex box around it coming to a
969
+ * definite height changes for every item in it. Its `height` is the item's
970
+ * there whatever has laid it out since, and the layout is the one the box
971
+ * has where nothing has (`Box.layoutSerial`).
972
+ */
973
+ function keptNatural(box, ctx, width, containing) {
974
+ const kept = NATURAL.get(box);
975
+ if (kept === undefined || kept.ctx !== ctx || kept.width !== width) {
976
+ return null;
977
+ }
978
+ if (!Number.isNaN(kept.containing) && kept.containing !== containing) {
979
+ return null;
980
+ }
981
+ if (!Object.is(kept.base, box.percentHeightBase) && readsPercentBase(box)) {
982
+ return null;
983
+ }
984
+ // the boxes in it that are positioned last are still waiting to be: a
985
+ // `line-clamp: auto` box laid out a second time forgets the ones its
986
+ // first layout found
987
+ if (kept.queued && ctx.positioned[kept.queued.at] !== kept.queued.first) {
988
+ return null;
989
+ }
990
+ const edges = kept.edges;
991
+ return edges[0] === box.marginTop &&
992
+ edges[1] === box.marginRight &&
993
+ edges[2] === box.marginBottom &&
994
+ edges[3] === box.marginLeft &&
995
+ edges[4] === box.borderTop &&
996
+ edges[5] === box.borderRight &&
997
+ edges[6] === box.borderBottom &&
998
+ edges[7] === box.borderLeft &&
999
+ edges[8] === box.padTop &&
1000
+ edges[9] === box.padRight &&
1001
+ edges[10] === box.padBottom &&
1002
+ edges[11] === box.padLeft
1003
+ ? kept
1004
+ : null;
1005
+ }
1006
+ const NATURAL = new WeakMap();
1007
+ /** Note what was read from the layout an item has, where that is the one
1008
+ * its content's own height makes of it at `width`. */
1009
+ function remember(box, ctx, width, containing, field, value) {
1010
+ const made = keptNatural(box, ctx, width, containing);
1011
+ if (made !== null && made.serial === box.layoutSerial)
1012
+ made[field] = value;
1013
+ }
1014
+ /** A box's edges, which a layout resolves again against the width it is
1015
+ * given: margins, borders, padding, each from the top round. */
1016
+ function edgesOf(box) {
1017
+ return [
1018
+ box.marginTop,
1019
+ box.marginRight,
1020
+ box.marginBottom,
1021
+ box.marginLeft,
1022
+ box.borderTop,
1023
+ box.borderRight,
1024
+ box.borderBottom,
1025
+ box.borderLeft,
1026
+ box.padTop,
1027
+ box.padRight,
1028
+ box.padBottom,
1029
+ box.padLeft,
1030
+ ];
1031
+ }
1032
+ /** Whether the width a box is laid out at is held to a percentage of its
1033
+ * containing block's: a least or greatest width that is one. */
1034
+ function readsContaining(box) {
1035
+ const style = box.style;
1036
+ return (isPct(style.minWidth) ||
1037
+ (style.maxWidth !== 'none' && isPct(style.maxWidth)));
1038
+ }
1039
+ /** Whether a box's own layout reads the height its percentages are of: a
1040
+ * height, a least or greatest one, or an offset that is a percentage, a
1041
+ * height that is a keyword — `stretch` fills it — or an anonymous box,
1042
+ * which hands it on to what is in it (`percentBaseInside`). */
1043
+ function readsPercentBase(box) {
1044
+ if (!box.el && !box.pseudo)
1045
+ return true;
1046
+ const style = box.style;
1047
+ return (isPct(style.height) ||
1048
+ isPct(style.minHeight) ||
1049
+ (style.maxHeight !== 'none' && isPct(style.maxHeight)) ||
1050
+ isPct(style.top) ||
1051
+ isPct(style.bottom) ||
1052
+ style.heightKeyword !== null ||
1053
+ style.minHeightKeyword !== null ||
1054
+ style.maxHeightKeyword !== null);
1055
+ }
762
1056
  /** The width an item's content is measured at: the width Yoga gives it
763
1057
  * exactly, or its max-content width, no wider than any it may take up to
764
1058
  * (CSS Flexbox 9.2). */
@@ -789,7 +1083,7 @@ function innerWidth(width, mode, maxContent) {
789
1083
  * (`Laid`), so a row with room pays for nothing. True where some item
790
1084
  * was given a minimum and the row has to be laid out again.
791
1085
  */
792
- function autoMinimums(items, row, ctx, containingWidth) {
1086
+ function autoMinimums(items, row, ctx, containingWidth, hold) {
793
1087
  let changed = false;
794
1088
  for (const { box, node, laid } of items) {
795
1089
  if (box.kind === 'text' || box.kind === 'break')
@@ -801,7 +1095,7 @@ function autoMinimums(items, row, ctx, containingWidth) {
801
1095
  if (!asked && scrolls(style)) {
802
1096
  continue;
803
1097
  }
804
- const width = meant(node.getComputedWidth(), laid.set, laid.stretch, laid.width);
1098
+ const width = meant(row ? laid.main : node.getComputedWidth(), laid.set, laid.stretch, laid.width, row ? laid.held : NaN);
805
1099
  // a replaced item's content along a row is its natural width
806
1100
  if (row && box.kind === 'replaced') {
807
1101
  if (style.minWidth !== AUTO || style.minWidthKeyword)
@@ -809,7 +1103,7 @@ function autoMinimums(items, row, ctx, containingWidth) {
809
1103
  const least = replacedMinimum(box, containingWidth);
810
1104
  if (least === null || width >= least - 0.01)
811
1105
  continue;
812
- node.setMinWidth(least);
1106
+ holdAt(node, laid, row, least, hold);
813
1107
  changed = true;
814
1108
  continue;
815
1109
  }
@@ -836,7 +1130,7 @@ function autoMinimums(items, row, ctx, containingWidth) {
836
1130
  }
837
1131
  if (width >= least - 0.01)
838
1132
  continue;
839
- node.setMinWidth(least);
1133
+ holdAt(node, laid, row, least, hold);
840
1134
  changed = true;
841
1135
  continue;
842
1136
  }
@@ -865,9 +1159,9 @@ function autoMinimums(items, row, ctx, containingWidth) {
865
1159
  if (width >= least - 0.01)
866
1160
  continue;
867
1161
  least = Math.min(least, minContentOf(box, ctx, laid));
868
- if (node.getComputedWidth() >= least - 0.01)
1162
+ if (laid.main >= least - 0.01)
869
1163
  continue;
870
- node.setMinWidth(least);
1164
+ holdAt(node, laid, row, least, hold);
871
1165
  changed = true;
872
1166
  continue;
873
1167
  }
@@ -879,17 +1173,37 @@ function autoMinimums(items, row, ctx, containingWidth) {
879
1173
  transferredWidth(box, widest + box.horizontalExtra) <= width + 0.5) {
880
1174
  continue;
881
1175
  }
882
- if (!(Math.abs(laid.width - width) <= 0.01)) {
883
- ctx.layoutSubtree(box, width);
884
- laid.width = box.width;
885
- laid.height = box.height;
1176
+ // What its content's own height makes of it at this width — how wide
1177
+ // that drew, along a row, and how far down, along a column — is read
1178
+ // from that layout, which the item is given where it has another: or
1179
+ // is what this pass read from it before, where the flex box is laid
1180
+ // out a second time (`layoutNaturally`)
1181
+ const kept = keptNatural(box, ctx, width, containingWidth);
1182
+ const read = kept === null ? NaN : row ? kept.drawn : kept.content;
1183
+ if (Number.isNaN(read) && !(Math.abs(laid.width - width) <= 0.01)) {
1184
+ layoutNaturally(box, ctx, width, containingWidth, laid);
886
1185
  }
1186
+ const height = Number.isNaN(read) ? laid.height : kept.height;
887
1187
  let least;
888
1188
  if (row) {
889
- // or than what it drew at the width it has: nothing overflows it
890
- const drawn = intrinsicWidth(box) + box.horizontalExtra;
891
- if (transferredWidth(box, drawn) <= width + 0.5)
1189
+ // or than what it drew at the width it has: nothing overflows it —
1190
+ // unless a line in it fits for being cut short by `text-overflow`,
1191
+ // which is how the line is drawn and no part of how wide it is: a
1192
+ // button whose label is a `truncate` span is no narrower than the
1193
+ // label, where it was shrunk and the label cut. No width, then, that
1194
+ // it drew at, which is what is kept of it
1195
+ let drawn = read;
1196
+ if (Number.isNaN(drawn)) {
1197
+ const seen = { cut: false };
1198
+ drawn = intrinsicWidth(box, seen) + box.horizontalExtra;
1199
+ if (seen.cut)
1200
+ drawn = Infinity;
1201
+ remember(box, ctx, width, containingWidth, 'drawn', drawn);
1202
+ }
1203
+ if (Number.isFinite(drawn) &&
1204
+ transferredWidth(box, drawn) <= width + 0.5) {
892
1205
  continue;
1206
+ }
893
1207
  // within what a ratio makes of its least and greatest heights (4.5's
894
1208
  // content size suggestion)
895
1209
  least = transferredWidth(box, minContentOf(box, ctx, laid));
@@ -899,15 +1213,18 @@ function autoMinimums(items, row, ctx, containingWidth) {
899
1213
  if (most !== null)
900
1214
  least = Math.min(least, most + extra);
901
1215
  }
902
- if (node.getComputedWidth() >= least - 0.01)
1216
+ if (laid.main >= least - 0.01)
903
1217
  continue;
904
- node.setMinWidth(least);
1218
+ holdAt(node, laid, row, least, hold);
905
1219
  }
906
1220
  else {
907
1221
  // its height, or where it has one of its own and its content comes to
908
1222
  // less, its content's: the lesser of the two (4.5)
909
- let content;
910
- if (box.kind === 'replaced')
1223
+ let content = read;
1224
+ if (!Number.isNaN(content)) {
1225
+ // read before
1226
+ }
1227
+ else if (box.kind === 'replaced')
911
1228
  content = laid.height;
912
1229
  else if (style.height !== AUTO && percentHeightsIn(box)) {
913
1230
  // what its content comes to where it has no height to take
@@ -915,43 +1232,226 @@ function autoMinimums(items, row, ctx, containingWidth) {
915
1232
  // apart, and the final pass lays it out again
916
1233
  FLEXED_HEIGHT.set(box, NaN);
917
1234
  try {
918
- ctx.layoutSubtree(box, width);
1235
+ ctx.layoutSubtree(box, width, containingWidth);
919
1236
  }
920
1237
  finally {
921
1238
  FLEXED_HEIGHT.delete(box);
922
1239
  }
923
- content = contentBottom(box) + box.verticalExtra;
1240
+ content = ratioContent(box, contentBottom(box) + box.verticalExtra);
924
1241
  laid.width = NaN;
925
1242
  }
926
- else
927
- content = contentBottom(box) + box.verticalExtra;
928
- // and a ratio's content is at least its width through the ratio,
929
- // height of its own or not (CSS Sizing 4, 5.1)
930
- const aspect = style.aspectRatio;
931
- if (aspect && box.kind !== 'replaced') {
932
- content = Math.max(content, style.boxSizing === 'border-box'
933
- ? box.width / aspect.ratio
934
- : box.contentWidth / aspect.ratio + box.verticalExtra);
1243
+ else {
1244
+ content = ratioContent(box, contentBottom(box) + box.verticalExtra);
1245
+ remember(box, ctx, width, containingWidth, 'content', content);
935
1246
  }
936
1247
  // within what the ratio makes of its least and greatest widths
937
1248
  if (box.kind !== 'replaced') {
938
1249
  content = transferredHeight(box, content, containingWidth);
939
1250
  }
940
- least = Math.min(laid.height, content);
1251
+ least = Math.min(height, content);
941
1252
  const extra = style.boxSizing === 'border-box' ? 0 : box.verticalExtra;
942
1253
  if (style.maxHeight !== 'none') {
943
1254
  const most = resolveOrNull(style.maxHeight, NaN);
944
1255
  if (most !== null)
945
1256
  least = Math.min(least, most + extra);
946
1257
  }
947
- if (node.getComputedHeight() >= least - 0.01)
1258
+ if (laid.main >= least - 0.01)
948
1259
  continue;
949
- node.setMinHeight(least);
1260
+ holdAt(node, laid, row, least, hold);
950
1261
  }
951
1262
  changed = true;
952
1263
  }
953
1264
  return changed;
954
1265
  }
1266
+ /** A laid-out item's content height, its border box's: no less than its
1267
+ * width through its ratio, height of its own or not (CSS Sizing 4, 5.1). */
1268
+ function ratioContent(box, content) {
1269
+ const aspect = box.style.aspectRatio;
1270
+ if (!aspect || box.kind === 'replaced')
1271
+ return content;
1272
+ return Math.max(content, box.style.boxSizing === 'border-box'
1273
+ ? box.width / aspect.ratio
1274
+ : box.contentWidth / aspect.ratio + box.verticalExtra);
1275
+ }
1276
+ /**
1277
+ * Hold an item to its least size along the main axis, the size Yoga gave
1278
+ * it being under that. It is frozen there, as CSS Flexbox freezes an item
1279
+ * its minimum stops (9.7, step 4): inflexible, at that size, the rest of
1280
+ * the line sharing what is left. Handed the minimum as a `min-width`
1281
+ * alone, Yoga makes it the item's flex base size, which is two things the
1282
+ * specification does not do:
1283
+ *
1284
+ * - where the line has room to share out, the item takes its share on top
1285
+ * of the minimum: of three `flex: 1` items in 300 pixels, the one with a
1286
+ * word 200 wide was 233, where it is 200 and the others 50 each;
1287
+ * - where every item of a line is held, Yoga divides what the line is
1288
+ * short of by what is left of the shrink factors once it has taken each
1289
+ * item's away — nothing, or a float's rounding of it. Two items 110.992
1290
+ * and 62.706 pixels wide in a row too narrow for them came out billions
1291
+ * of pixels wide, and 31.3 and 17.7 came out as they are: by the digits
1292
+ * of their sizes, which are a text's widths in whatever font it is set.
1293
+ *
1294
+ * On the one line of a box that does not wrap, an item Yoga had under its
1295
+ * minimum has no more room when another is held too, so frozen is where it
1296
+ * ends. In a box that wraps it may have: the minimums move the line breaks
1297
+ * (9.3), and an item held on a crowded line may be on one with room to
1298
+ * grow in once they have. There the minimum is left to Yoga, as a minimum;
1299
+ * only a line of one item is short of room there, and what Yoga takes away
1300
+ * from its shrink factors is exactly what it added.
1301
+ */
1302
+ function holdAt(node, laid, row, least, hold) {
1303
+ if (row)
1304
+ node.setMinWidth(least);
1305
+ else
1306
+ node.setMinHeight(least);
1307
+ if (!hold.frozen)
1308
+ return;
1309
+ laid.auto = true;
1310
+ freezeAt(node, laid, row, least, hold);
1311
+ }
1312
+ /** Freeze an item at a size along the main axis: inflexible, and that size
1313
+ * its flex base size. */
1314
+ function freezeAt(node, laid, row, size, hold) {
1315
+ laid.held = size;
1316
+ node.setFlexGrow(0);
1317
+ node.setFlexShrink(0);
1318
+ if (hold.basis)
1319
+ node.setFlexBasis(size);
1320
+ else if (row)
1321
+ node.setWidth(size);
1322
+ else
1323
+ node.setHeight(size);
1324
+ }
1325
+ /**
1326
+ * Put right the one line of a box that does not wrap, where every item of
1327
+ * it that may shrink is stopped by its minimum and Yoga made them wider for
1328
+ * it; whether Yoga was asked again. The line is laid out with each of them
1329
+ * frozen at its minimum, which is where CSS Flexbox leaves them (9.7, step
1330
+ * 4): the line is short of room with every one of them as small as it
1331
+ * goes.
1332
+ *
1333
+ * Yoga shares what a line is short of in two passes. The first takes each
1334
+ * item a minimum stops out of the sum of the scaled shrink factors, one
1335
+ * subtraction an item, and the second divides what the line is still short
1336
+ * of by what is left of the sum. With every item stopped that is nothing:
1337
+ * `(a + b) - a - b`, in float32s, which is 0 for some sizes and a rounding
1338
+ * either side of it for others, 2^-18 for sizes about a hundred. Yoga
1339
+ * 3.2.1 tests for 0, and the guard it has had since, a sum under 1e-6, is
1340
+ * under that rounding (react/yoga#1665, #1974). A negative rounding sends
1341
+ * every item under its minimum, which stops it, and a positive one makes
1342
+ * each of them wider by billions of pixels: `width: 110.992px; min-width:
1343
+ * 110.992px` beside `62.705625px` of the same, in a row 50 wide, were
1344
+ * 3599091712 and 2033329408 wide, where 100 and 60 were as they are. So
1345
+ * were two items with no minimum but their padding, and two with
1346
+ * `min-width: 0` beside one that does not shrink and is wider than the row
1347
+ * alone.
1348
+ *
1349
+ * A minimum of the item's own is Yoga's to hold it to, so this looks at
1350
+ * its answer, for a line that is two things at once. It is short of room
1351
+ * with every item that may shrink at its minimum — where 9.7 leaves each
1352
+ * of them at that, whatever their sizes. And one of them is wider than its
1353
+ * hypothetical size (9.7, step 2), which no sharing out of what a line is
1354
+ * short of makes an item: Yoga is asked for the line with nothing
1355
+ * shrinking, where each item is that size. Any other line is put back as
1356
+ * it was, the two layouts that took being the cost of one that is short of
1357
+ * room at its minimums and was not shared out to them; a line with room
1358
+ * for its minimums, or one whose every item sits at its own, has cost the
1359
+ * sum of its sizes.
1360
+ *
1361
+ * An automatic minimum is no part of it: an item held at one is frozen
1362
+ * already (`holdAt`), and one not yet held has no minimum to Yoga but its
1363
+ * padding and border, where this leaves it for `autoMinimums` to find
1364
+ * short of its content.
1365
+ */
1366
+ function stoppedLine(items, row,
1367
+ /** The flex box's size along the main axis. */
1368
+ room,
1369
+ /** The gap between two items. */
1370
+ gap, hold,
1371
+ /** Lay the flex box out, as its nodes now are. */
1372
+ ask) {
1373
+ // what is between the items' border boxes: the gaps, and their margins
1374
+ let between = gap * (items.length - 1);
1375
+ // the line's size with each item that may shrink at its minimum
1376
+ let least = 0;
1377
+ let shrinking = 0;
1378
+ let over = false;
1379
+ for (const { box, laid } of items) {
1380
+ const style = box.style;
1381
+ // an `auto` margin is none on a line with no room (8.1)
1382
+ if (row) {
1383
+ if (style.marginLeft !== AUTO)
1384
+ between += box.marginLeft;
1385
+ if (style.marginRight !== AUTO)
1386
+ between += box.marginRight;
1387
+ }
1388
+ else {
1389
+ if (style.marginTop !== AUTO)
1390
+ between += box.marginTop;
1391
+ if (style.marginBottom !== AUTO)
1392
+ between += box.marginBottom;
1393
+ }
1394
+ if (!shrinks(box, laid))
1395
+ least += laid.main;
1396
+ else {
1397
+ const min = leastOf(box, laid, row);
1398
+ shrinking += 1;
1399
+ least += min;
1400
+ if (laid.main > min + 0.01)
1401
+ over = true;
1402
+ }
1403
+ }
1404
+ // two at the least: what is left of a sum of one, less it, is 0 exactly.
1405
+ // A line within a thousandth of a pixel of room is short of it: what
1406
+ // Yoga makes of that is as far out, and the minimums are that near
1407
+ if (shrinking < 2 || !over || !(between + least > room - SHORT)) {
1408
+ return false;
1409
+ }
1410
+ const stopped = items.filter(({ box, laid }) => shrinks(box, laid));
1411
+ for (const { node } of stopped)
1412
+ node.setFlexShrink(0);
1413
+ ask();
1414
+ // each item its hypothetical size, which one that does not shrink is on
1415
+ // a line short of room — where Yoga may have grown it, on a line it took
1416
+ // for having some
1417
+ let wider = false;
1418
+ least = 0;
1419
+ for (const { box, node, laid } of items) {
1420
+ const hypothetical = row
1421
+ ? node.getComputedWidth()
1422
+ : node.getComputedHeight();
1423
+ if (!shrinks(box, laid))
1424
+ least += hypothetical;
1425
+ else {
1426
+ least += leastOf(box, laid, row);
1427
+ if (laid.main > hypothetical && !nearly(laid.main, hypothetical)) {
1428
+ wider = true;
1429
+ }
1430
+ }
1431
+ }
1432
+ const freeze = wider && between + least > room - SHORT;
1433
+ for (const { box, node, laid } of stopped) {
1434
+ if (freeze)
1435
+ freezeAt(node, laid, row, leastOf(box, laid, row), hold);
1436
+ else
1437
+ node.setFlexShrink(box.style.flexShrink);
1438
+ }
1439
+ ask();
1440
+ return true;
1441
+ }
1442
+ /** Whether Yoga may shrink an item: it has a shrink factor, and is not
1443
+ * frozen (`freezeAt`). */
1444
+ function shrinks(box, laid) {
1445
+ return box.style.flexShrink > 0 && Number.isNaN(laid.held);
1446
+ }
1447
+ /** How near to its room a line may be and be short of it (`stoppedLine`). */
1448
+ const SHORT = 0.001;
1449
+ /** The least an item may be along the main axis, its border box: the
1450
+ * minimum of its own, and no less than its padding and borders. */
1451
+ function leastOf(box, laid, row) {
1452
+ const extra = row ? box.horizontalExtra : box.verticalExtra;
1453
+ return Number.isNaN(laid.least) ? extra : Math.max(laid.least, extra);
1454
+ }
955
1455
  /** An item's min-content width, its border box's, taken the once: its
956
1456
  * content's, whatever width it has of its own (`keywordWidth`'s too). */
957
1457
  function minContentOf(box, ctx, laid) {