@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.
- package/dist/html/controls.d.ts +29 -2
- package/dist/html/controls.d.ts.map +1 -1
- package/dist/html/controls.js +70 -2
- package/dist/html/controls.js.map +1 -1
- package/dist/html/css/cascade.d.ts +42 -1
- package/dist/html/css/cascade.d.ts.map +1 -1
- package/dist/html/css/cascade.js +336 -16
- package/dist/html/css/cascade.js.map +1 -1
- package/dist/html/css/color.d.ts +18 -0
- package/dist/html/css/color.d.ts.map +1 -1
- package/dist/html/css/color.js +27 -0
- package/dist/html/css/color.js.map +1 -1
- package/dist/html/css/parse.d.ts +18 -4
- package/dist/html/css/parse.d.ts.map +1 -1
- package/dist/html/css/parse.js +66 -28
- package/dist/html/css/parse.js.map +1 -1
- package/dist/html/css/shapes.d.ts +44 -0
- package/dist/html/css/shapes.d.ts.map +1 -0
- package/dist/html/css/shapes.js +194 -0
- package/dist/html/css/shapes.js.map +1 -0
- package/dist/html/css/style.d.ts +128 -15
- package/dist/html/css/style.d.ts.map +1 -1
- package/dist/html/css/style.js +547 -144
- package/dist/html/css/style.js.map +1 -1
- package/dist/html/css/transform.d.ts +85 -0
- package/dist/html/css/transform.d.ts.map +1 -0
- package/dist/html/css/transform.js +425 -0
- package/dist/html/css/transform.js.map +1 -0
- package/dist/html/css/ua.d.ts.map +1 -1
- package/dist/html/css/ua.js +14 -2
- package/dist/html/css/ua.js.map +1 -1
- package/dist/html/css/values.d.ts +4 -0
- package/dist/html/css/values.d.ts.map +1 -1
- package/dist/html/css/values.js.map +1 -1
- package/dist/html/fonts.d.ts +55 -5
- package/dist/html/fonts.d.ts.map +1 -1
- package/dist/html/fonts.js +284 -23
- package/dist/html/fonts.js.map +1 -1
- package/dist/html/layout/axes.d.ts +14 -0
- package/dist/html/layout/axes.d.ts.map +1 -0
- package/dist/html/layout/axes.js +28 -0
- package/dist/html/layout/axes.js.map +1 -0
- package/dist/html/layout/block.d.ts +104 -12
- package/dist/html/layout/block.d.ts.map +1 -1
- package/dist/html/layout/block.js +410 -39
- package/dist/html/layout/block.js.map +1 -1
- package/dist/html/layout/boxes.d.ts +72 -1
- package/dist/html/layout/boxes.d.ts.map +1 -1
- package/dist/html/layout/boxes.js +40 -2
- package/dist/html/layout/boxes.js.map +1 -1
- package/dist/html/layout/cache.d.ts +1 -1
- package/dist/html/layout/cache.d.ts.map +1 -1
- package/dist/html/layout/cache.js +6 -0
- package/dist/html/layout/cache.js.map +1 -1
- package/dist/html/layout/css-grid.d.ts.map +1 -1
- package/dist/html/layout/css-grid.js +144 -24
- package/dist/html/layout/css-grid.js.map +1 -1
- package/dist/html/layout/flex.d.ts.map +1 -1
- package/dist/html/layout/flex.js +585 -85
- package/dist/html/layout/flex.js.map +1 -1
- package/dist/html/layout/inline.d.ts +3 -0
- package/dist/html/layout/inline.d.ts.map +1 -1
- package/dist/html/layout/inline.js +144 -54
- package/dist/html/layout/inline.js.map +1 -1
- package/dist/html/layout/multicol.d.ts +42 -0
- package/dist/html/layout/multicol.d.ts.map +1 -0
- package/dist/html/layout/multicol.js +897 -0
- package/dist/html/layout/multicol.js.map +1 -0
- package/dist/html/layout/shaping.d.ts +7 -0
- package/dist/html/layout/shaping.d.ts.map +1 -1
- package/dist/html/layout/shaping.js +25 -20
- package/dist/html/layout/shaping.js.map +1 -1
- package/dist/html/layout/table.d.ts.map +1 -1
- package/dist/html/layout/table.js +97 -31
- package/dist/html/layout/table.js.map +1 -1
- package/dist/html/node.d.ts +4 -0
- package/dist/html/node.d.ts.map +1 -1
- package/dist/html/node.js +319 -47
- package/dist/html/node.js.map +1 -1
- package/dist/html/paint.d.ts +83 -5
- package/dist/html/paint.d.ts.map +1 -1
- package/dist/html/paint.js +1474 -217
- package/dist/html/paint.js.map +1 -1
- package/dist/html/svg.d.ts +39 -9
- package/dist/html/svg.d.ts.map +1 -1
- package/dist/html/svg.js +482 -37
- package/dist/html/svg.js.map +1 -1
- package/dist/html/widgets.js +87 -7
- package/dist/html/widgets.js.map +1 -1
- package/dist/html/woff2.d.ts +14 -0
- package/dist/html/woff2.d.ts.map +1 -0
- package/dist/html/woff2.js +608 -0
- package/dist/html/woff2.js.map +1 -0
- package/dist/maps/controller.d.ts +17 -1
- package/dist/maps/controller.d.ts.map +1 -1
- package/dist/maps/controller.js +27 -7
- package/dist/maps/controller.js.map +1 -1
- package/dist/richtext/node.d.ts +8 -1
- package/dist/richtext/node.d.ts.map +1 -1
- package/dist/richtext/node.js +5 -1
- package/dist/richtext/node.js.map +1 -1
- package/dist/richtext/runs.d.ts +25 -6
- package/dist/richtext/runs.d.ts.map +1 -1
- package/dist/richtext/runs.js +240 -24
- package/dist/richtext/runs.js.map +1 -1
- package/package.json +1 -1
- package/src/html/controls.ts +85 -2
- package/src/html/css/cascade.ts +366 -8
- package/src/html/css/color.ts +29 -0
- package/src/html/css/parse.ts +71 -31
- package/src/html/css/shapes.ts +239 -0
- package/src/html/css/style.ts +652 -147
- package/src/html/css/transform.ts +459 -0
- package/src/html/css/ua.ts +15 -2
- package/src/html/css/values.ts +4 -0
- package/src/html/fonts.ts +312 -29
- package/src/html/layout/axes.ts +62 -0
- package/src/html/layout/block.ts +466 -37
- package/src/html/layout/boxes.ts +95 -3
- package/src/html/layout/cache.ts +6 -0
- package/src/html/layout/css-grid.ts +169 -32
- package/src/html/layout/flex.ts +686 -79
- package/src/html/layout/inline.ts +149 -55
- package/src/html/layout/multicol.ts +977 -0
- package/src/html/layout/shaping.ts +27 -18
- package/src/html/layout/table.ts +105 -35
- package/src/html/node.ts +354 -48
- package/src/html/paint.ts +1804 -315
- package/src/html/svg.ts +573 -36
- package/src/html/widgets.ts +95 -9
- package/src/html/woff2.ts +612 -0
- package/src/maps/controller.ts +28 -7
- package/src/richtext/node.ts +13 -2
- package/src/richtext/runs.ts +313 -35
package/src/html/layout/flex.ts
CHANGED
|
@@ -20,8 +20,10 @@
|
|
|
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
|
|
24
|
-
//
|
|
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 type { Config as YogaConfig, Node as YogaNode } from 'react-x11/yoga';
|
|
27
29
|
|
|
@@ -32,6 +34,8 @@ import { Box, PAINT_ORDER, isBlank } from './boxes.js';
|
|
|
32
34
|
import {
|
|
33
35
|
FLEXED_HEIGHT,
|
|
34
36
|
MIN_CONTENT_PROBE,
|
|
37
|
+
USED_HEIGHT,
|
|
38
|
+
centreButton,
|
|
35
39
|
clampHeight,
|
|
36
40
|
clampWidth,
|
|
37
41
|
contentSizedWidth,
|
|
@@ -122,9 +126,14 @@ export function layoutFlex(
|
|
|
122
126
|
// percentage that resolves, or what `aspect-ratio` makes of the width —
|
|
123
127
|
// less the padding and borders a `border-box` height holds, which put
|
|
124
128
|
// `h-16 py-2 items-center` eight pixels low. Where it is not, the limits
|
|
125
|
-
// on it, so a column `min-h-screen` gives its `flex-1` the rest
|
|
129
|
+
// on it, so a column `min-h-screen` gives its `flex-1` the rest — or the
|
|
130
|
+
// height the column it is an item of flexed it to (`USED_HEIGHT`), which
|
|
131
|
+
// its items are laid out in and take no percentage of.
|
|
126
132
|
const definite = percentBaseInside(box);
|
|
127
|
-
const
|
|
133
|
+
const own = Number.isFinite(definite);
|
|
134
|
+
const height = USED_HEIGHT.get(box) ?? (own ? definite : null);
|
|
135
|
+
// whether its content may come to more than it is tall
|
|
136
|
+
let capped = height !== null;
|
|
128
137
|
if (height !== null) root.setHeight(height);
|
|
129
138
|
else {
|
|
130
139
|
root.setHeightAuto();
|
|
@@ -132,7 +141,10 @@ export function layoutFlex(
|
|
|
132
141
|
const min = clampHeight(box, extra) - extra;
|
|
133
142
|
if (min > 0) root.setMinHeight(min);
|
|
134
143
|
const max = clampHeight(box, Infinity) - extra;
|
|
135
|
-
if (Number.isFinite(max))
|
|
144
|
+
if (Number.isFinite(max)) {
|
|
145
|
+
root.setMaxHeight(Math.max(0, max));
|
|
146
|
+
capped = true;
|
|
147
|
+
}
|
|
136
148
|
}
|
|
137
149
|
|
|
138
150
|
const flowing: Box[] = [];
|
|
@@ -161,8 +173,18 @@ export function layoutFlex(
|
|
|
161
173
|
for (const child of flowing) {
|
|
162
174
|
const node = Y.Node.create(flexConfig());
|
|
163
175
|
resolveEdges(child, contentWidth);
|
|
164
|
-
const laid: Laid = {
|
|
165
|
-
|
|
176
|
+
const laid: Laid = {
|
|
177
|
+
width: NaN,
|
|
178
|
+
height: NaN,
|
|
179
|
+
tall: NaN,
|
|
180
|
+
set: NaN,
|
|
181
|
+
stretch: NaN,
|
|
182
|
+
main: NaN,
|
|
183
|
+
least: NaN,
|
|
184
|
+
held: NaN,
|
|
185
|
+
auto: false,
|
|
186
|
+
};
|
|
187
|
+
applyItem(node, child, ctx, contentWidth, laid, height !== null, own);
|
|
166
188
|
root.insertChild(node, items.length);
|
|
167
189
|
items.push({ box: child, node, laid });
|
|
168
190
|
}
|
|
@@ -170,12 +192,38 @@ export function layoutFlex(
|
|
|
170
192
|
const direction =
|
|
171
193
|
box.style.direction === 'rtl' ? Y.DIRECTION_RTL : Y.DIRECTION_LTR;
|
|
172
194
|
const row = box.style.flexDirection.startsWith('row');
|
|
173
|
-
|
|
195
|
+
// frozen at its least size on the one line of a box that does not
|
|
196
|
+
// wrap, by a basis where Yoga takes one along the main axis
|
|
197
|
+
// (`applyItem`'s `setBasis`)
|
|
198
|
+
const hold: Hold = {
|
|
199
|
+
frozen: box.style.flexWrap === 'nowrap',
|
|
200
|
+
basis: row ? bounded : height !== null,
|
|
201
|
+
};
|
|
202
|
+
const ask = (): void =>
|
|
174
203
|
root.calculateLayout(
|
|
175
204
|
bounded ? contentWidth : Number.NaN,
|
|
176
205
|
height ?? Number.NaN,
|
|
177
206
|
direction,
|
|
178
207
|
);
|
|
208
|
+
// the one line of a box that does not wrap may be short of room, where
|
|
209
|
+
// the box has a size along it that its items do not make: and Yoga's
|
|
210
|
+
// answer for a line every item of which its minimum stops is put right
|
|
211
|
+
// (`stoppedLine`)
|
|
212
|
+
const short = hold.frozen && (row ? bounded : capped);
|
|
213
|
+
// each item's size along the main axis, read the once a layout (`Laid`)
|
|
214
|
+
const read = (): void => {
|
|
215
|
+
for (const { node, laid } of items) {
|
|
216
|
+
laid.main = row ? node.getComputedWidth() : node.getComputedHeight();
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
const calculate = (): void => {
|
|
220
|
+
ask();
|
|
221
|
+
read();
|
|
222
|
+
if (!short) return;
|
|
223
|
+
const room = row ? contentWidth : (height ?? root.getComputedHeight());
|
|
224
|
+
const gap = row ? columnGap : rowGap;
|
|
225
|
+
if (stoppedLine(items, row, room, gap, hold, ask)) read();
|
|
226
|
+
};
|
|
179
227
|
ctx.flexDepth = depth + 1;
|
|
180
228
|
try {
|
|
181
229
|
calculate();
|
|
@@ -184,7 +232,7 @@ export function layoutFlex(
|
|
|
184
232
|
// another under its own, a few times over at most
|
|
185
233
|
for (
|
|
186
234
|
let pass = 0;
|
|
187
|
-
pass < 4 && autoMinimums(items, row, ctx, contentWidth);
|
|
235
|
+
pass < 4 && autoMinimums(items, row, ctx, contentWidth, hold);
|
|
188
236
|
pass += 1
|
|
189
237
|
) {
|
|
190
238
|
calculate();
|
|
@@ -203,8 +251,9 @@ export function layoutFlex(
|
|
|
203
251
|
ctx,
|
|
204
252
|
items,
|
|
205
253
|
row,
|
|
206
|
-
|
|
254
|
+
own,
|
|
207
255
|
box.style.flexWrap === 'wrap' || baselines,
|
|
256
|
+
contentWidth,
|
|
208
257
|
);
|
|
209
258
|
// Items aligned by their baselines, which Yoga cannot see, are aligned
|
|
210
259
|
// with their baselines known (`baselineLines`)
|
|
@@ -237,8 +286,9 @@ export function layoutFlex(
|
|
|
237
286
|
ctx,
|
|
238
287
|
items,
|
|
239
288
|
row,
|
|
240
|
-
|
|
289
|
+
own,
|
|
241
290
|
box.style.flexWrap === 'wrap',
|
|
291
|
+
contentWidth,
|
|
242
292
|
started,
|
|
243
293
|
);
|
|
244
294
|
}
|
|
@@ -264,8 +314,9 @@ export function layoutFlex(
|
|
|
264
314
|
ctx,
|
|
265
315
|
items,
|
|
266
316
|
row,
|
|
267
|
-
|
|
317
|
+
own,
|
|
268
318
|
box.style.flexWrap === 'wrap' || baselines,
|
|
319
|
+
contentWidth,
|
|
269
320
|
);
|
|
270
321
|
}
|
|
271
322
|
|
|
@@ -278,11 +329,11 @@ export function layoutFlex(
|
|
|
278
329
|
* base size the ratio makes of the stretched size; whether any changed. */
|
|
279
330
|
function keepRatios(
|
|
280
331
|
container: Box,
|
|
281
|
-
items: readonly { box: Box; node: YogaNode }[],
|
|
332
|
+
items: readonly { box: Box; node: YogaNode; laid: Laid }[],
|
|
282
333
|
row: boolean,
|
|
283
334
|
): boolean {
|
|
284
335
|
let changed = false;
|
|
285
|
-
for (const { box, node } of items) {
|
|
336
|
+
for (const { box, node, laid } of items) {
|
|
286
337
|
if (box.kind !== 'replaced' || !stretches(box, container.style, row)) {
|
|
287
338
|
continue;
|
|
288
339
|
}
|
|
@@ -295,9 +346,16 @@ function keepRatios(
|
|
|
295
346
|
box.horizontalExtra
|
|
296
347
|
: Math.max(0, node.getComputedWidth() - box.horizontalExtra) / ratio +
|
|
297
348
|
box.verticalExtra;
|
|
298
|
-
|
|
299
|
-
if (Math.abs(basis - main) < 0.5) continue;
|
|
349
|
+
if (Math.abs(basis - laid.main) < 0.5) continue;
|
|
300
350
|
node.setFlexBasis(basis);
|
|
351
|
+
// one held at its least size (`holdAt`) flexes from the new basis, no
|
|
352
|
+
// further down than that
|
|
353
|
+
if (!Number.isNaN(laid.held)) {
|
|
354
|
+
laid.held = NaN;
|
|
355
|
+
laid.auto = false;
|
|
356
|
+
node.setFlexGrow(style.flexGrow);
|
|
357
|
+
node.setFlexShrink(style.flexShrink);
|
|
358
|
+
}
|
|
301
359
|
changed = true;
|
|
302
360
|
}
|
|
303
361
|
return changed;
|
|
@@ -431,9 +489,12 @@ function placeItems(
|
|
|
431
489
|
ctx: LayoutContext,
|
|
432
490
|
items: readonly { box: Box; node: YogaNode; laid: Laid }[],
|
|
433
491
|
row: boolean,
|
|
434
|
-
|
|
492
|
+
/** Whether the flex box has a definite height. */
|
|
493
|
+
own: boolean,
|
|
435
494
|
/** Whether Yoga aligned the lines in its pass that drops a margin. */
|
|
436
495
|
lined: boolean,
|
|
496
|
+
/** The flex box's content width: its items' containing block's. */
|
|
497
|
+
contentWidth: number,
|
|
437
498
|
started?: ReadonlySet<Box>,
|
|
438
499
|
): number {
|
|
439
500
|
let bottom = 0;
|
|
@@ -444,12 +505,13 @@ function placeItems(
|
|
|
444
505
|
const left = box.contentX + node.getComputedLeft() + (row ? 0 : across);
|
|
445
506
|
const top = box.contentY + node.getComputedTop() + (row ? across : 0);
|
|
446
507
|
const width = meant(
|
|
447
|
-
node.getComputedWidth(),
|
|
508
|
+
row ? laid.main : node.getComputedWidth(),
|
|
448
509
|
laid.set,
|
|
449
510
|
laid.stretch,
|
|
450
511
|
laid.width,
|
|
512
|
+
row ? laid.held : NaN,
|
|
451
513
|
);
|
|
452
|
-
const itemHeight = node.getComputedHeight();
|
|
514
|
+
const itemHeight = row ? node.getComputedHeight() : laid.main;
|
|
453
515
|
// The item is laid out again at the width the flex pass settled on: the
|
|
454
516
|
// measure function answered a question, and the answer is not a layout —
|
|
455
517
|
// its line breaks were computed against a width that may have changed
|
|
@@ -459,12 +521,15 @@ function placeItems(
|
|
|
459
521
|
// 9.8): stretched across a row's line, or flexed in a column of a
|
|
460
522
|
// height of its own. What is in it takes its percentages of that —
|
|
461
523
|
// the `h-full` in a stretched sidebar — where they had nothing to be
|
|
462
|
-
// of, and the item is laid out again for them
|
|
524
|
+
// of, and the item is laid out again for them; and so is one that is a
|
|
525
|
+
// flex or grid container itself, whose own items are laid out in that
|
|
526
|
+
// height (9.4, step 11: "redo layout for its contents, treating this
|
|
527
|
+
// used size as its definite cross size").
|
|
463
528
|
const definite = row
|
|
464
529
|
? stretches(child, box.style)
|
|
465
530
|
? itemHeight
|
|
466
531
|
: null
|
|
467
|
-
:
|
|
532
|
+
: own
|
|
468
533
|
? itemHeight
|
|
469
534
|
: null;
|
|
470
535
|
layoutItemAt(
|
|
@@ -476,6 +541,7 @@ function placeItems(
|
|
|
476
541
|
itemHeight,
|
|
477
542
|
laid,
|
|
478
543
|
definite,
|
|
544
|
+
contentWidth,
|
|
479
545
|
!row,
|
|
480
546
|
);
|
|
481
547
|
// to the end of its margin box, as Yoga measured the box: a negative
|
|
@@ -496,6 +562,8 @@ function layoutItemAt(
|
|
|
496
562
|
laid: Laid,
|
|
497
563
|
/** Its border box's height where the flex layout made it definite. */
|
|
498
564
|
definite: number | null,
|
|
565
|
+
/** The flex box's content width, the item's containing block's. */
|
|
566
|
+
containing: number,
|
|
499
567
|
/** Whether its height is the one the flex layout gave it, in a column,
|
|
500
568
|
* whatever its own says: flexed, it is shrunk as well as grown. */
|
|
501
569
|
column = false,
|
|
@@ -509,29 +577,76 @@ function layoutItemAt(
|
|
|
509
577
|
box.height = height;
|
|
510
578
|
return;
|
|
511
579
|
}
|
|
512
|
-
const
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
580
|
+
const sameWidth = Math.abs(laid.width - width) <= 0.01;
|
|
581
|
+
// The content height the item is laid out at, where the flex layout gave
|
|
582
|
+
// it one that is not its own already: NaN where its content says how
|
|
583
|
+
// tall it is.
|
|
584
|
+
let tall = NaN;
|
|
585
|
+
// A flex or grid container shares its height out among its items: an
|
|
586
|
+
// `auto` margin takes what is free of it (8.1), a `flex: 1` grows into it
|
|
587
|
+
// (9.7), its lines are as tall as it (9.4), its `fr` rows fill it. So it
|
|
588
|
+
// is laid out in the height the flex layout gave it, definite or not —
|
|
589
|
+
// the one a column with no height of its own flexed it to is its used
|
|
590
|
+
// size all the same (9.7), and `min-h-screen flex flex-col` around a
|
|
591
|
+
// `flex-1 flex items-center` centres what is in that. At its content's
|
|
592
|
+
// own height there is nothing to share, and the layout it has is that
|
|
593
|
+
// one: the tallest card of a row is laid out the once. A table shares
|
|
594
|
+
// what it is given past its rows among them (`layoutTable`), and a
|
|
595
|
+
// height short of them is none it can have: it is laid out again where
|
|
596
|
+
// it was given more.
|
|
597
|
+
const given = definite ?? (column ? height : null);
|
|
598
|
+
let shares = false;
|
|
599
|
+
if (given !== null && (box.kind === 'flex' || box.kind === 'table')) {
|
|
600
|
+
const natural = naturalHeight(box, ctx, width, containing, laid);
|
|
601
|
+
shares =
|
|
602
|
+
box.kind === 'flex'
|
|
603
|
+
? !(Math.abs(natural - given) <= 0.01)
|
|
604
|
+
: !(natural >= given - 0.01);
|
|
605
|
+
}
|
|
606
|
+
if (given !== null) {
|
|
607
|
+
const inner = Math.max(0, given - box.verticalExtra);
|
|
608
|
+
if (Object.is(inner, percentBaseInside(box))) {
|
|
609
|
+
// the height it has of its own
|
|
610
|
+
} else if (shares || (definite !== null && percentHeightsIn(box))) {
|
|
611
|
+
tall = inner;
|
|
522
612
|
}
|
|
523
613
|
}
|
|
524
614
|
// `measureBox` lays the box out at (0, 0); re-running it at the final width
|
|
525
615
|
// and then moving it is one pass, not two, because the second call is the
|
|
526
|
-
// one whose result is kept — or no pass, where the last
|
|
527
|
-
// this width. An item Yoga never measured has none, NaN,
|
|
528
|
-
|
|
529
|
-
|
|
616
|
+
// one whose result is kept — or no pass, where the last layout was at
|
|
617
|
+
// this width and this height. An item Yoga never measured has none, NaN,
|
|
618
|
+
// and is laid out.
|
|
619
|
+
if (sameWidth && Object.is(laid.tall, tall)) box.height = laid.height;
|
|
620
|
+
else if (Number.isNaN(tall)) {
|
|
621
|
+
layoutNaturally(box, ctx, width, containing, laid);
|
|
622
|
+
} else {
|
|
623
|
+
// what is in it takes its percentages of a definite height alone (9.8)
|
|
624
|
+
const heights = definite !== null ? FLEXED_HEIGHT : USED_HEIGHT;
|
|
625
|
+
heights.set(box, tall);
|
|
626
|
+
try {
|
|
627
|
+
ctx.layoutSubtree(box, width, containing);
|
|
628
|
+
} finally {
|
|
629
|
+
heights.delete(box);
|
|
630
|
+
}
|
|
631
|
+
laid.width = box.width;
|
|
632
|
+
laid.height = box.height;
|
|
633
|
+
laid.tall = tall;
|
|
634
|
+
}
|
|
530
635
|
// A stretched item is taller than its content, and the box has to say so
|
|
531
636
|
// or its background stops short of the row — and it is no taller than its
|
|
532
637
|
// line where its content is, or than what its ratio makes of its width:
|
|
533
|
-
// its height is the line's (CSS Flexbox 9.4, step 11).
|
|
534
|
-
|
|
638
|
+
// its height is the line's (CSS Flexbox 9.4, step 11). But for a table,
|
|
639
|
+
// whose rows are the least it can be, however short its line (CSS 2.1
|
|
640
|
+
// 17.5.3, as a height of its own is; Blink: "Tables can't shrink below
|
|
641
|
+
// their min-intrinsic size").
|
|
642
|
+
if (
|
|
643
|
+
height > box.height ||
|
|
644
|
+
((column || definite !== null) && box.kind !== 'table')
|
|
645
|
+
) {
|
|
646
|
+
box.height = height;
|
|
647
|
+
}
|
|
648
|
+
// and a button's content is centred in the height it came to
|
|
649
|
+
centreButton(box);
|
|
535
650
|
moveTo(box, x, y);
|
|
536
651
|
}
|
|
537
652
|
|
|
@@ -561,8 +676,11 @@ function applyItem(
|
|
|
561
676
|
ctx: LayoutContext,
|
|
562
677
|
containingWidth: number,
|
|
563
678
|
laid: Laid,
|
|
564
|
-
/** Whether the flex box has a height
|
|
679
|
+
/** Whether the flex box has a height its items are laid out in. */
|
|
565
680
|
tall: boolean,
|
|
681
|
+
/** Whether that height is definite, and a percentage may be of it: one a
|
|
682
|
+
* column flexed the box to is not (`USED_HEIGHT`). */
|
|
683
|
+
own: boolean,
|
|
566
684
|
): void {
|
|
567
685
|
const style = box.style;
|
|
568
686
|
laid.stretch = containingWidth - box.marginLeft - box.marginRight;
|
|
@@ -590,9 +708,20 @@ function applyItem(
|
|
|
590
708
|
if (style.width !== AUTO) {
|
|
591
709
|
laid.set = setLength(node, true, style.width, containingWidth, across);
|
|
592
710
|
}
|
|
593
|
-
|
|
711
|
+
// a percentage of a height that is not definite is `auto` (CSS 2.1 10.5),
|
|
712
|
+
// which Yoga makes of one where the flex box has no height at all
|
|
713
|
+
if (style.height !== AUTO && (own || !tall || !isPct(style.height))) {
|
|
714
|
+
setLength(node, false, style.height, NaN, down);
|
|
715
|
+
}
|
|
716
|
+
// the least it may be along each axis, its border box's, as Yoga is
|
|
717
|
+
// told it
|
|
718
|
+
let leastWidth = NaN;
|
|
719
|
+
let leastHeight = NaN;
|
|
594
720
|
const minWidth = resolveOrNull(style.minWidth, containingWidth);
|
|
595
|
-
if (minWidth !== null)
|
|
721
|
+
if (minWidth !== null) {
|
|
722
|
+
leastWidth = minWidth + across;
|
|
723
|
+
node.setMinWidth(leastWidth);
|
|
724
|
+
}
|
|
596
725
|
if (style.maxWidth !== 'none') {
|
|
597
726
|
const maxWidth = resolveOrNull(style.maxWidth, containingWidth);
|
|
598
727
|
if (maxWidth !== null) node.setMaxWidth(maxWidth + across);
|
|
@@ -611,17 +740,24 @@ function applyItem(
|
|
|
611
740
|
laid.set = size(style.widthKeyword);
|
|
612
741
|
node.setWidth(laid.set);
|
|
613
742
|
}
|
|
614
|
-
if (style.minWidthKeyword)
|
|
743
|
+
if (style.minWidthKeyword) {
|
|
744
|
+
leastWidth = size(style.minWidthKeyword);
|
|
745
|
+
node.setMinWidth(leastWidth);
|
|
746
|
+
}
|
|
615
747
|
if (style.maxWidthKeyword) node.setMaxWidth(size(style.maxWidthKeyword));
|
|
616
748
|
}
|
|
617
749
|
const minHeight = resolveOrNull(style.minHeight, NaN);
|
|
618
|
-
if (minHeight !== null)
|
|
750
|
+
if (minHeight !== null) {
|
|
751
|
+
leastHeight = minHeight + down;
|
|
752
|
+
node.setMinHeight(leastHeight);
|
|
753
|
+
}
|
|
619
754
|
if (style.maxHeight !== 'none') {
|
|
620
755
|
const maxHeight = resolveOrNull(style.maxHeight, NaN);
|
|
621
756
|
if (maxHeight !== null) node.setMaxHeight(maxHeight + down);
|
|
622
757
|
}
|
|
623
758
|
|
|
624
759
|
const row = box.parent?.style.flexDirection.startsWith('row') ?? true;
|
|
760
|
+
laid.least = row ? leastWidth : leastHeight;
|
|
625
761
|
// Yoga takes a length for a basis only where the flex box's main size is
|
|
626
762
|
// definite, and else the item's own size along it: down a column of no
|
|
627
763
|
// height of its own it read `flex: 0 0 3rem` as the item's height, or
|
|
@@ -653,6 +789,9 @@ function applyItem(
|
|
|
653
789
|
setBasis(columnBasis);
|
|
654
790
|
} else if (style.flexBasis === 'content' || style.flexBasis === AUTO) {
|
|
655
791
|
// Yoga's own
|
|
792
|
+
} else if (isPct(style.flexBasis) && !row && tall && !own) {
|
|
793
|
+
// a percentage of a main size that is not definite is `content` (CSS
|
|
794
|
+
// Flexbox 7.2.3): Yoga's own, down a column
|
|
656
795
|
} else if (isPct(style.flexBasis)) {
|
|
657
796
|
// a percentage of the main size, which is known across a row
|
|
658
797
|
const basis = style.flexBasis;
|
|
@@ -738,7 +877,13 @@ function applyItem(
|
|
|
738
877
|
const width = meant(w, laid.set - extra, laid.stretch - extra);
|
|
739
878
|
const given = hm === Y.MEASURE_MODE_EXACTLY && Number.isFinite(h);
|
|
740
879
|
const exact = wm === Y.MEASURE_MODE_EXACTLY && Number.isFinite(width);
|
|
741
|
-
|
|
880
|
+
// the height it was flexed to, that is: one its automatic minimum
|
|
881
|
+
// holds it at (`holdAt`) is its content's, which a replaced element's
|
|
882
|
+
// width follows through its ratio and no other's does (CSS Sizing 4,
|
|
883
|
+
// 5.1)
|
|
884
|
+
const flexed = box.kind === 'replaced' || !laid.auto;
|
|
885
|
+
const through =
|
|
886
|
+
widthThrough && given && !exact && flexed ? widthThrough(h) : null;
|
|
742
887
|
// along a row, a replaced element is its own width wherever there is
|
|
743
888
|
// room for less — its flex base size is not fitted to the room (9.2)
|
|
744
889
|
// — and across a column it is fitted, as any item is
|
|
@@ -758,10 +903,8 @@ function applyItem(
|
|
|
758
903
|
}
|
|
759
904
|
let answer = answers.get(inner);
|
|
760
905
|
if (answer === undefined) {
|
|
761
|
-
answer = measureBox(box, ctx, inner);
|
|
906
|
+
answer = measureBox(box, ctx, inner, containingWidth, laid);
|
|
762
907
|
answers.set(inner, answer);
|
|
763
|
-
laid.width = box.width;
|
|
764
|
-
laid.height = box.height;
|
|
765
908
|
}
|
|
766
909
|
return answer;
|
|
767
910
|
});
|
|
@@ -777,12 +920,28 @@ const MAX_CONTENT = new WeakMap<Box, number>();
|
|
|
777
920
|
interface Laid {
|
|
778
921
|
width: number;
|
|
779
922
|
height: number;
|
|
923
|
+
/** The content height that layout was made at, where the flex layout gave
|
|
924
|
+
* the item one (`FLEXED_HEIGHT`, `USED_HEIGHT`), and NaN where its
|
|
925
|
+
* content's own. */
|
|
926
|
+
tall: number;
|
|
780
927
|
/** The border-box width this engine set on the item's node — a length,
|
|
781
928
|
* or the content's for `fit-content` and its kin — or NaN. */
|
|
782
929
|
set: number;
|
|
783
930
|
/** Its border-box width stretched across its container: the container's
|
|
784
931
|
* content width, less its margins. */
|
|
785
932
|
stretch: number;
|
|
933
|
+
/** Its border box's size along the main axis in the layout Yoga last
|
|
934
|
+
* made of the flex box, as Yoga has it. */
|
|
935
|
+
main: number;
|
|
936
|
+
/** The least it may be along the main axis, where its own style says:
|
|
937
|
+
* the minimum this engine set on its node, or NaN. */
|
|
938
|
+
least: number;
|
|
939
|
+
/** The size along the main axis it is frozen at, inflexible (`freezeAt`),
|
|
940
|
+
* or NaN. */
|
|
941
|
+
held: number;
|
|
942
|
+
/** Whether that is its automatic minimum (`holdAt`), its content's size,
|
|
943
|
+
* and not a minimum the line stopped at (`stoppedLine`). */
|
|
944
|
+
auto: boolean;
|
|
786
945
|
}
|
|
787
946
|
|
|
788
947
|
/**
|
|
@@ -855,14 +1014,236 @@ function measureBox(
|
|
|
855
1014
|
box: Box,
|
|
856
1015
|
ctx: LayoutContext,
|
|
857
1016
|
inner: number,
|
|
1017
|
+
/** The flex box's content width, the item's containing block's. */
|
|
1018
|
+
containing: number,
|
|
1019
|
+
laid: Laid,
|
|
858
1020
|
): { width: number; height: number } {
|
|
859
|
-
|
|
1021
|
+
// its padding as Yoga has it, a percentage of the flex box's width: a
|
|
1022
|
+
// probe of its content's width leaves a percentage at none
|
|
1023
|
+
resolveEdges(box, containing);
|
|
1024
|
+
const width = inner + box.horizontalExtra;
|
|
1025
|
+
// its height at this width is known, where a layout since has left it
|
|
1026
|
+
// another: the answer is the height, and the layout `layoutItemAt`'s
|
|
1027
|
+
const kept = keptNatural(box, ctx, width, containing);
|
|
1028
|
+
if (kept !== null && kept.serial !== box.layoutSerial) {
|
|
1029
|
+
return {
|
|
1030
|
+
width: inner,
|
|
1031
|
+
height: Math.max(0, kept.height - box.verticalExtra),
|
|
1032
|
+
};
|
|
1033
|
+
}
|
|
1034
|
+
layoutNaturally(box, ctx, width, containing, laid);
|
|
860
1035
|
return {
|
|
861
1036
|
width: inner,
|
|
862
1037
|
height: Math.max(0, box.height - box.verticalExtra),
|
|
863
1038
|
};
|
|
864
1039
|
}
|
|
865
1040
|
|
|
1041
|
+
/** An item's height as its content's own makes it at a border-box width,
|
|
1042
|
+
* where this pass has laid it out so (`layoutNaturally`), and else NaN. */
|
|
1043
|
+
function naturalHeight(
|
|
1044
|
+
box: Box,
|
|
1045
|
+
ctx: LayoutContext,
|
|
1046
|
+
width: number,
|
|
1047
|
+
containing: number,
|
|
1048
|
+
laid: Laid,
|
|
1049
|
+
): number {
|
|
1050
|
+
if (Math.abs(laid.width - width) <= 0.01 && Number.isNaN(laid.tall)) {
|
|
1051
|
+
return laid.height;
|
|
1052
|
+
}
|
|
1053
|
+
return keptNatural(box, ctx, width, containing)?.height ?? NaN;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
/**
|
|
1057
|
+
* Give an item the layout its content's own height makes of it at a
|
|
1058
|
+
* border-box width, and note it (`Laid`): the one this pass has made of it
|
|
1059
|
+
* there already, where it still has it.
|
|
1060
|
+
*
|
|
1061
|
+
* A flex box laid out a second time — at the height a stretch gave it, its
|
|
1062
|
+
* own flex layout made anew (`layoutItemAt`) — asks of each item what it
|
|
1063
|
+
* asked the first time, and the item's layout at a width is what it was.
|
|
1064
|
+
* Kept for the pass, a card laid out again for its height lays nothing in
|
|
1065
|
+
* it out that is where it was; made again at every layout of the box
|
|
1066
|
+
* around it, a card in a card in a card laid its innermost out twice a
|
|
1067
|
+
* level.
|
|
1068
|
+
*/
|
|
1069
|
+
function layoutNaturally(
|
|
1070
|
+
box: Box,
|
|
1071
|
+
ctx: LayoutContext,
|
|
1072
|
+
width: number,
|
|
1073
|
+
/** The flex box's content width, the item's containing block's. */
|
|
1074
|
+
containing: number,
|
|
1075
|
+
laid: Laid,
|
|
1076
|
+
): void {
|
|
1077
|
+
const kept = keptNatural(box, ctx, width, containing);
|
|
1078
|
+
if (kept !== null && kept.serial === box.layoutSerial) {
|
|
1079
|
+
// put back, where the flex layout stretched it
|
|
1080
|
+
box.height = kept.height;
|
|
1081
|
+
} else {
|
|
1082
|
+
const at = ctx.positioned.length;
|
|
1083
|
+
ctx.layoutSubtree(box, width, containing);
|
|
1084
|
+
// a replaced box is sized, not laid out, and has no layout to count
|
|
1085
|
+
if (box.kind !== 'replaced') {
|
|
1086
|
+
NATURAL.set(box, {
|
|
1087
|
+
ctx,
|
|
1088
|
+
serial: box.layoutSerial,
|
|
1089
|
+
width,
|
|
1090
|
+
containing: readsContaining(box) ? containing : NaN,
|
|
1091
|
+
base: box.percentHeightBase,
|
|
1092
|
+
edges: edgesOf(box),
|
|
1093
|
+
height: box.height,
|
|
1094
|
+
queued:
|
|
1095
|
+
ctx.positioned.length > at ? { at, first: ctx.positioned[at] } : null,
|
|
1096
|
+
drawn: NaN,
|
|
1097
|
+
content: NaN,
|
|
1098
|
+
});
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
laid.width = box.width;
|
|
1102
|
+
laid.height = box.height;
|
|
1103
|
+
laid.tall = NaN;
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
/**
|
|
1107
|
+
* The layout this pass made of an item at its content's own height
|
|
1108
|
+
* (`layoutNaturally`), where one at `width` now would be the same: the same
|
|
1109
|
+
* width, the same edges — a percentage among them resolved, as the layout
|
|
1110
|
+
* leaves them — the same containing block's width where a limit on its own
|
|
1111
|
+
* is a percentage of it, and the same height for its percentages, where its
|
|
1112
|
+
* own style reads that at all, which the flex box around it coming to a
|
|
1113
|
+
* definite height changes for every item in it. Its `height` is the item's
|
|
1114
|
+
* there whatever has laid it out since, and the layout is the one the box
|
|
1115
|
+
* has where nothing has (`Box.layoutSerial`).
|
|
1116
|
+
*/
|
|
1117
|
+
function keptNatural(
|
|
1118
|
+
box: Box,
|
|
1119
|
+
ctx: LayoutContext,
|
|
1120
|
+
width: number,
|
|
1121
|
+
containing: number,
|
|
1122
|
+
): Natural | null {
|
|
1123
|
+
const kept = NATURAL.get(box);
|
|
1124
|
+
if (kept === undefined || kept.ctx !== ctx || kept.width !== width) {
|
|
1125
|
+
return null;
|
|
1126
|
+
}
|
|
1127
|
+
if (!Number.isNaN(kept.containing) && kept.containing !== containing) {
|
|
1128
|
+
return null;
|
|
1129
|
+
}
|
|
1130
|
+
if (!Object.is(kept.base, box.percentHeightBase) && readsPercentBase(box)) {
|
|
1131
|
+
return null;
|
|
1132
|
+
}
|
|
1133
|
+
// the boxes in it that are positioned last are still waiting to be: a
|
|
1134
|
+
// `line-clamp: auto` box laid out a second time forgets the ones its
|
|
1135
|
+
// first layout found
|
|
1136
|
+
if (kept.queued && ctx.positioned[kept.queued.at] !== kept.queued.first) {
|
|
1137
|
+
return null;
|
|
1138
|
+
}
|
|
1139
|
+
const edges = kept.edges;
|
|
1140
|
+
return edges[0] === box.marginTop &&
|
|
1141
|
+
edges[1] === box.marginRight &&
|
|
1142
|
+
edges[2] === box.marginBottom &&
|
|
1143
|
+
edges[3] === box.marginLeft &&
|
|
1144
|
+
edges[4] === box.borderTop &&
|
|
1145
|
+
edges[5] === box.borderRight &&
|
|
1146
|
+
edges[6] === box.borderBottom &&
|
|
1147
|
+
edges[7] === box.borderLeft &&
|
|
1148
|
+
edges[8] === box.padTop &&
|
|
1149
|
+
edges[9] === box.padRight &&
|
|
1150
|
+
edges[10] === box.padBottom &&
|
|
1151
|
+
edges[11] === box.padLeft
|
|
1152
|
+
? kept
|
|
1153
|
+
: null;
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
/** A layout of an item at its content's own height (`layoutNaturally`): in
|
|
1157
|
+
* which pass, which of the box's layouts it was, and what it was made
|
|
1158
|
+
* from. */
|
|
1159
|
+
interface Natural {
|
|
1160
|
+
ctx: LayoutContext;
|
|
1161
|
+
serial: number;
|
|
1162
|
+
/** The border-box width it was asked for at. */
|
|
1163
|
+
width: number;
|
|
1164
|
+
/** Its containing block's width, where a limit on its own width is a
|
|
1165
|
+
* percentage of that (`readsContaining`), and else NaN: a nest of flex
|
|
1166
|
+
* boxes is laid out in rooms of many widths, and an item with no such
|
|
1167
|
+
* limit is laid out the same in each. */
|
|
1168
|
+
containing: number;
|
|
1169
|
+
/** The height its percentages were of. */
|
|
1170
|
+
base: number;
|
|
1171
|
+
/** Its margins, borders and padding once laid out (`edgesOf`). */
|
|
1172
|
+
edges: number[];
|
|
1173
|
+
height: number;
|
|
1174
|
+
/** The first of the out-of-flow boxes that layout queued to be
|
|
1175
|
+
* positioned (`LayoutContext.positioned`) and where; null for none. */
|
|
1176
|
+
queued: { at: number; first: LayoutContext['positioned'][number] } | null;
|
|
1177
|
+
/** What `autoMinimums` read from that layout, NaN until it has: how wide
|
|
1178
|
+
* it drew, and how far down its content came, each its border box's. */
|
|
1179
|
+
drawn: number;
|
|
1180
|
+
content: number;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
const NATURAL = new WeakMap<Box, Natural>();
|
|
1184
|
+
|
|
1185
|
+
/** Note what was read from the layout an item has, where that is the one
|
|
1186
|
+
* its content's own height makes of it at `width`. */
|
|
1187
|
+
function remember(
|
|
1188
|
+
box: Box,
|
|
1189
|
+
ctx: LayoutContext,
|
|
1190
|
+
width: number,
|
|
1191
|
+
containing: number,
|
|
1192
|
+
field: 'drawn' | 'content',
|
|
1193
|
+
value: number,
|
|
1194
|
+
): void {
|
|
1195
|
+
const made = keptNatural(box, ctx, width, containing);
|
|
1196
|
+
if (made !== null && made.serial === box.layoutSerial) made[field] = value;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
/** A box's edges, which a layout resolves again against the width it is
|
|
1200
|
+
* given: margins, borders, padding, each from the top round. */
|
|
1201
|
+
function edgesOf(box: Box): number[] {
|
|
1202
|
+
return [
|
|
1203
|
+
box.marginTop,
|
|
1204
|
+
box.marginRight,
|
|
1205
|
+
box.marginBottom,
|
|
1206
|
+
box.marginLeft,
|
|
1207
|
+
box.borderTop,
|
|
1208
|
+
box.borderRight,
|
|
1209
|
+
box.borderBottom,
|
|
1210
|
+
box.borderLeft,
|
|
1211
|
+
box.padTop,
|
|
1212
|
+
box.padRight,
|
|
1213
|
+
box.padBottom,
|
|
1214
|
+
box.padLeft,
|
|
1215
|
+
];
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
/** Whether the width a box is laid out at is held to a percentage of its
|
|
1219
|
+
* containing block's: a least or greatest width that is one. */
|
|
1220
|
+
function readsContaining(box: Box): boolean {
|
|
1221
|
+
const style = box.style;
|
|
1222
|
+
return (
|
|
1223
|
+
isPct(style.minWidth) ||
|
|
1224
|
+
(style.maxWidth !== 'none' && isPct(style.maxWidth))
|
|
1225
|
+
);
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
/** Whether a box's own layout reads the height its percentages are of: a
|
|
1229
|
+
* height, a least or greatest one, or an offset that is a percentage, a
|
|
1230
|
+
* height that is a keyword — `stretch` fills it — or an anonymous box,
|
|
1231
|
+
* which hands it on to what is in it (`percentBaseInside`). */
|
|
1232
|
+
function readsPercentBase(box: Box): boolean {
|
|
1233
|
+
if (!box.el && !box.pseudo) return true;
|
|
1234
|
+
const style = box.style;
|
|
1235
|
+
return (
|
|
1236
|
+
isPct(style.height) ||
|
|
1237
|
+
isPct(style.minHeight) ||
|
|
1238
|
+
(style.maxHeight !== 'none' && isPct(style.maxHeight)) ||
|
|
1239
|
+
isPct(style.top) ||
|
|
1240
|
+
isPct(style.bottom) ||
|
|
1241
|
+
style.heightKeyword !== null ||
|
|
1242
|
+
style.minHeightKeyword !== null ||
|
|
1243
|
+
style.maxHeightKeyword !== null
|
|
1244
|
+
);
|
|
1245
|
+
}
|
|
1246
|
+
|
|
866
1247
|
/** The width an item's content is measured at: the width Yoga gives it
|
|
867
1248
|
* exactly, or its max-content width, no wider than any it may take up to
|
|
868
1249
|
* (CSS Flexbox 9.2). */
|
|
@@ -903,6 +1284,7 @@ function autoMinimums(
|
|
|
903
1284
|
row: boolean,
|
|
904
1285
|
ctx: LayoutContext,
|
|
905
1286
|
containingWidth: number,
|
|
1287
|
+
hold: Hold,
|
|
906
1288
|
): boolean {
|
|
907
1289
|
let changed = false;
|
|
908
1290
|
for (const { box, node, laid } of items) {
|
|
@@ -915,17 +1297,18 @@ function autoMinimums(
|
|
|
915
1297
|
continue;
|
|
916
1298
|
}
|
|
917
1299
|
const width = meant(
|
|
918
|
-
node.getComputedWidth(),
|
|
1300
|
+
row ? laid.main : node.getComputedWidth(),
|
|
919
1301
|
laid.set,
|
|
920
1302
|
laid.stretch,
|
|
921
1303
|
laid.width,
|
|
1304
|
+
row ? laid.held : NaN,
|
|
922
1305
|
);
|
|
923
1306
|
// a replaced item's content along a row is its natural width
|
|
924
1307
|
if (row && box.kind === 'replaced') {
|
|
925
1308
|
if (style.minWidth !== AUTO || style.minWidthKeyword) continue;
|
|
926
1309
|
const least = replacedMinimum(box, containingWidth);
|
|
927
1310
|
if (least === null || width >= least - 0.01) continue;
|
|
928
|
-
node
|
|
1311
|
+
holdAt(node, laid, row, least, hold);
|
|
929
1312
|
changed = true;
|
|
930
1313
|
continue;
|
|
931
1314
|
}
|
|
@@ -952,7 +1335,7 @@ function autoMinimums(
|
|
|
952
1335
|
if (most !== null) least = Math.min(least, most + extra);
|
|
953
1336
|
}
|
|
954
1337
|
if (width >= least - 0.01) continue;
|
|
955
|
-
node
|
|
1338
|
+
holdAt(node, laid, row, least, hold);
|
|
956
1339
|
changed = true;
|
|
957
1340
|
continue;
|
|
958
1341
|
}
|
|
@@ -980,8 +1363,8 @@ function autoMinimums(
|
|
|
980
1363
|
}
|
|
981
1364
|
if (width >= least - 0.01) continue;
|
|
982
1365
|
least = Math.min(least, minContentOf(box, ctx, laid));
|
|
983
|
-
if (
|
|
984
|
-
node
|
|
1366
|
+
if (laid.main >= least - 0.01) continue;
|
|
1367
|
+
holdAt(node, laid, row, least, hold);
|
|
985
1368
|
changed = true;
|
|
986
1369
|
continue;
|
|
987
1370
|
}
|
|
@@ -995,16 +1378,38 @@ function autoMinimums(
|
|
|
995
1378
|
) {
|
|
996
1379
|
continue;
|
|
997
1380
|
}
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1381
|
+
// What its content's own height makes of it at this width — how wide
|
|
1382
|
+
// that drew, along a row, and how far down, along a column — is read
|
|
1383
|
+
// from that layout, which the item is given where it has another: or
|
|
1384
|
+
// is what this pass read from it before, where the flex box is laid
|
|
1385
|
+
// out a second time (`layoutNaturally`)
|
|
1386
|
+
const kept = keptNatural(box, ctx, width, containingWidth);
|
|
1387
|
+
const read = kept === null ? NaN : row ? kept.drawn : kept.content;
|
|
1388
|
+
if (Number.isNaN(read) && !(Math.abs(laid.width - width) <= 0.01)) {
|
|
1389
|
+
layoutNaturally(box, ctx, width, containingWidth, laid);
|
|
1002
1390
|
}
|
|
1391
|
+
const height = Number.isNaN(read) ? laid.height : kept!.height;
|
|
1003
1392
|
let least: number;
|
|
1004
1393
|
if (row) {
|
|
1005
|
-
// or than what it drew at the width it has: nothing overflows it
|
|
1006
|
-
|
|
1007
|
-
|
|
1394
|
+
// or than what it drew at the width it has: nothing overflows it —
|
|
1395
|
+
// unless a line in it fits for being cut short by `text-overflow`,
|
|
1396
|
+
// which is how the line is drawn and no part of how wide it is: a
|
|
1397
|
+
// button whose label is a `truncate` span is no narrower than the
|
|
1398
|
+
// label, where it was shrunk and the label cut. No width, then, that
|
|
1399
|
+
// it drew at, which is what is kept of it
|
|
1400
|
+
let drawn = read;
|
|
1401
|
+
if (Number.isNaN(drawn)) {
|
|
1402
|
+
const seen = { cut: false };
|
|
1403
|
+
drawn = intrinsicWidth(box, seen) + box.horizontalExtra;
|
|
1404
|
+
if (seen.cut) drawn = Infinity;
|
|
1405
|
+
remember(box, ctx, width, containingWidth, 'drawn', drawn);
|
|
1406
|
+
}
|
|
1407
|
+
if (
|
|
1408
|
+
Number.isFinite(drawn) &&
|
|
1409
|
+
transferredWidth(box, drawn) <= width + 0.5
|
|
1410
|
+
) {
|
|
1411
|
+
continue;
|
|
1412
|
+
}
|
|
1008
1413
|
// within what a ratio makes of its least and greatest heights (4.5's
|
|
1009
1414
|
// content size suggestion)
|
|
1010
1415
|
least = transferredWidth(box, minContentOf(box, ctx, laid));
|
|
@@ -1013,55 +1418,257 @@ function autoMinimums(
|
|
|
1013
1418
|
const most = resolveOrNull(style.maxWidth, containingWidth);
|
|
1014
1419
|
if (most !== null) least = Math.min(least, most + extra);
|
|
1015
1420
|
}
|
|
1016
|
-
if (
|
|
1017
|
-
node
|
|
1421
|
+
if (laid.main >= least - 0.01) continue;
|
|
1422
|
+
holdAt(node, laid, row, least, hold);
|
|
1018
1423
|
} else {
|
|
1019
1424
|
// its height, or where it has one of its own and its content comes to
|
|
1020
1425
|
// less, its content's: the lesser of the two (4.5)
|
|
1021
|
-
let content
|
|
1022
|
-
if (
|
|
1426
|
+
let content = read;
|
|
1427
|
+
if (!Number.isNaN(content)) {
|
|
1428
|
+
// read before
|
|
1429
|
+
} else if (box.kind === 'replaced') content = laid.height;
|
|
1023
1430
|
else if (style.height !== AUTO && percentHeightsIn(box)) {
|
|
1024
1431
|
// what its content comes to where it has no height to take
|
|
1025
1432
|
// percentages of, as an intrinsic size is measured: laid out so
|
|
1026
1433
|
// apart, and the final pass lays it out again
|
|
1027
1434
|
FLEXED_HEIGHT.set(box, NaN);
|
|
1028
1435
|
try {
|
|
1029
|
-
ctx.layoutSubtree(box, width);
|
|
1436
|
+
ctx.layoutSubtree(box, width, containingWidth);
|
|
1030
1437
|
} finally {
|
|
1031
1438
|
FLEXED_HEIGHT.delete(box);
|
|
1032
1439
|
}
|
|
1033
|
-
content = contentBottom(box) + box.verticalExtra;
|
|
1440
|
+
content = ratioContent(box, contentBottom(box) + box.verticalExtra);
|
|
1034
1441
|
laid.width = NaN;
|
|
1035
|
-
} else
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
const aspect = style.aspectRatio;
|
|
1039
|
-
if (aspect && box.kind !== 'replaced') {
|
|
1040
|
-
content = Math.max(
|
|
1041
|
-
content,
|
|
1042
|
-
style.boxSizing === 'border-box'
|
|
1043
|
-
? box.width / aspect.ratio
|
|
1044
|
-
: box.contentWidth / aspect.ratio + box.verticalExtra,
|
|
1045
|
-
);
|
|
1442
|
+
} else {
|
|
1443
|
+
content = ratioContent(box, contentBottom(box) + box.verticalExtra);
|
|
1444
|
+
remember(box, ctx, width, containingWidth, 'content', content);
|
|
1046
1445
|
}
|
|
1047
1446
|
// within what the ratio makes of its least and greatest widths
|
|
1048
1447
|
if (box.kind !== 'replaced') {
|
|
1049
1448
|
content = transferredHeight(box, content, containingWidth);
|
|
1050
1449
|
}
|
|
1051
|
-
least = Math.min(
|
|
1450
|
+
least = Math.min(height, content);
|
|
1052
1451
|
const extra = style.boxSizing === 'border-box' ? 0 : box.verticalExtra;
|
|
1053
1452
|
if (style.maxHeight !== 'none') {
|
|
1054
1453
|
const most = resolveOrNull(style.maxHeight, NaN);
|
|
1055
1454
|
if (most !== null) least = Math.min(least, most + extra);
|
|
1056
1455
|
}
|
|
1057
|
-
if (
|
|
1058
|
-
node
|
|
1456
|
+
if (laid.main >= least - 0.01) continue;
|
|
1457
|
+
holdAt(node, laid, row, least, hold);
|
|
1059
1458
|
}
|
|
1060
1459
|
changed = true;
|
|
1061
1460
|
}
|
|
1062
1461
|
return changed;
|
|
1063
1462
|
}
|
|
1064
1463
|
|
|
1464
|
+
/** A laid-out item's content height, its border box's: no less than its
|
|
1465
|
+
* width through its ratio, height of its own or not (CSS Sizing 4, 5.1). */
|
|
1466
|
+
function ratioContent(box: Box, content: number): number {
|
|
1467
|
+
const aspect = box.style.aspectRatio;
|
|
1468
|
+
if (!aspect || box.kind === 'replaced') return content;
|
|
1469
|
+
return Math.max(
|
|
1470
|
+
content,
|
|
1471
|
+
box.style.boxSizing === 'border-box'
|
|
1472
|
+
? box.width / aspect.ratio
|
|
1473
|
+
: box.contentWidth / aspect.ratio + box.verticalExtra,
|
|
1474
|
+
);
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
/** How a flex box holds its items to their least sizes (`holdAt`). */
|
|
1478
|
+
interface Hold {
|
|
1479
|
+
/** Whether an item is frozen at its least size: on the one line of a
|
|
1480
|
+
* box that does not wrap. */
|
|
1481
|
+
frozen: boolean;
|
|
1482
|
+
/** Whether Yoga takes a length for a basis along the main axis
|
|
1483
|
+
* (`applyItem`'s `setBasis`), and else the item's own size. */
|
|
1484
|
+
basis: boolean;
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
/**
|
|
1488
|
+
* Hold an item to its least size along the main axis, the size Yoga gave
|
|
1489
|
+
* it being under that. It is frozen there, as CSS Flexbox freezes an item
|
|
1490
|
+
* its minimum stops (9.7, step 4): inflexible, at that size, the rest of
|
|
1491
|
+
* the line sharing what is left. Handed the minimum as a `min-width`
|
|
1492
|
+
* alone, Yoga makes it the item's flex base size, which is two things the
|
|
1493
|
+
* specification does not do:
|
|
1494
|
+
*
|
|
1495
|
+
* - where the line has room to share out, the item takes its share on top
|
|
1496
|
+
* of the minimum: of three `flex: 1` items in 300 pixels, the one with a
|
|
1497
|
+
* word 200 wide was 233, where it is 200 and the others 50 each;
|
|
1498
|
+
* - where every item of a line is held, Yoga divides what the line is
|
|
1499
|
+
* short of by what is left of the shrink factors once it has taken each
|
|
1500
|
+
* item's away — nothing, or a float's rounding of it. Two items 110.992
|
|
1501
|
+
* and 62.706 pixels wide in a row too narrow for them came out billions
|
|
1502
|
+
* of pixels wide, and 31.3 and 17.7 came out as they are: by the digits
|
|
1503
|
+
* of their sizes, which are a text's widths in whatever font it is set.
|
|
1504
|
+
*
|
|
1505
|
+
* On the one line of a box that does not wrap, an item Yoga had under its
|
|
1506
|
+
* minimum has no more room when another is held too, so frozen is where it
|
|
1507
|
+
* ends. In a box that wraps it may have: the minimums move the line breaks
|
|
1508
|
+
* (9.3), and an item held on a crowded line may be on one with room to
|
|
1509
|
+
* grow in once they have. There the minimum is left to Yoga, as a minimum;
|
|
1510
|
+
* only a line of one item is short of room there, and what Yoga takes away
|
|
1511
|
+
* from its shrink factors is exactly what it added.
|
|
1512
|
+
*/
|
|
1513
|
+
function holdAt(
|
|
1514
|
+
node: YogaNode,
|
|
1515
|
+
laid: Laid,
|
|
1516
|
+
row: boolean,
|
|
1517
|
+
least: number,
|
|
1518
|
+
hold: Hold,
|
|
1519
|
+
): void {
|
|
1520
|
+
if (row) node.setMinWidth(least);
|
|
1521
|
+
else node.setMinHeight(least);
|
|
1522
|
+
if (!hold.frozen) return;
|
|
1523
|
+
laid.auto = true;
|
|
1524
|
+
freezeAt(node, laid, row, least, hold);
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
/** Freeze an item at a size along the main axis: inflexible, and that size
|
|
1528
|
+
* its flex base size. */
|
|
1529
|
+
function freezeAt(
|
|
1530
|
+
node: YogaNode,
|
|
1531
|
+
laid: Laid,
|
|
1532
|
+
row: boolean,
|
|
1533
|
+
size: number,
|
|
1534
|
+
hold: Hold,
|
|
1535
|
+
): void {
|
|
1536
|
+
laid.held = size;
|
|
1537
|
+
node.setFlexGrow(0);
|
|
1538
|
+
node.setFlexShrink(0);
|
|
1539
|
+
if (hold.basis) node.setFlexBasis(size);
|
|
1540
|
+
else if (row) node.setWidth(size);
|
|
1541
|
+
else node.setHeight(size);
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
/**
|
|
1545
|
+
* Put right the one line of a box that does not wrap, where every item of
|
|
1546
|
+
* it that may shrink is stopped by its minimum and Yoga made them wider for
|
|
1547
|
+
* it; whether Yoga was asked again. The line is laid out with each of them
|
|
1548
|
+
* frozen at its minimum, which is where CSS Flexbox leaves them (9.7, step
|
|
1549
|
+
* 4): the line is short of room with every one of them as small as it
|
|
1550
|
+
* goes.
|
|
1551
|
+
*
|
|
1552
|
+
* Yoga shares what a line is short of in two passes. The first takes each
|
|
1553
|
+
* item a minimum stops out of the sum of the scaled shrink factors, one
|
|
1554
|
+
* subtraction an item, and the second divides what the line is still short
|
|
1555
|
+
* of by what is left of the sum. With every item stopped that is nothing:
|
|
1556
|
+
* `(a + b) - a - b`, in float32s, which is 0 for some sizes and a rounding
|
|
1557
|
+
* either side of it for others, 2^-18 for sizes about a hundred. Yoga
|
|
1558
|
+
* 3.2.1 tests for 0, and the guard it has had since, a sum under 1e-6, is
|
|
1559
|
+
* under that rounding (react/yoga#1665, #1974). A negative rounding sends
|
|
1560
|
+
* every item under its minimum, which stops it, and a positive one makes
|
|
1561
|
+
* each of them wider by billions of pixels: `width: 110.992px; min-width:
|
|
1562
|
+
* 110.992px` beside `62.705625px` of the same, in a row 50 wide, were
|
|
1563
|
+
* 3599091712 and 2033329408 wide, where 100 and 60 were as they are. So
|
|
1564
|
+
* were two items with no minimum but their padding, and two with
|
|
1565
|
+
* `min-width: 0` beside one that does not shrink and is wider than the row
|
|
1566
|
+
* alone.
|
|
1567
|
+
*
|
|
1568
|
+
* A minimum of the item's own is Yoga's to hold it to, so this looks at
|
|
1569
|
+
* its answer, for a line that is two things at once. It is short of room
|
|
1570
|
+
* with every item that may shrink at its minimum — where 9.7 leaves each
|
|
1571
|
+
* of them at that, whatever their sizes. And one of them is wider than its
|
|
1572
|
+
* hypothetical size (9.7, step 2), which no sharing out of what a line is
|
|
1573
|
+
* short of makes an item: Yoga is asked for the line with nothing
|
|
1574
|
+
* shrinking, where each item is that size. Any other line is put back as
|
|
1575
|
+
* it was, the two layouts that took being the cost of one that is short of
|
|
1576
|
+
* room at its minimums and was not shared out to them; a line with room
|
|
1577
|
+
* for its minimums, or one whose every item sits at its own, has cost the
|
|
1578
|
+
* sum of its sizes.
|
|
1579
|
+
*
|
|
1580
|
+
* An automatic minimum is no part of it: an item held at one is frozen
|
|
1581
|
+
* already (`holdAt`), and one not yet held has no minimum to Yoga but its
|
|
1582
|
+
* padding and border, where this leaves it for `autoMinimums` to find
|
|
1583
|
+
* short of its content.
|
|
1584
|
+
*/
|
|
1585
|
+
function stoppedLine(
|
|
1586
|
+
items: readonly { box: Box; node: YogaNode; laid: Laid }[],
|
|
1587
|
+
row: boolean,
|
|
1588
|
+
/** The flex box's size along the main axis. */
|
|
1589
|
+
room: number,
|
|
1590
|
+
/** The gap between two items. */
|
|
1591
|
+
gap: number,
|
|
1592
|
+
hold: Hold,
|
|
1593
|
+
/** Lay the flex box out, as its nodes now are. */
|
|
1594
|
+
ask: () => void,
|
|
1595
|
+
): boolean {
|
|
1596
|
+
// what is between the items' border boxes: the gaps, and their margins
|
|
1597
|
+
let between = gap * (items.length - 1);
|
|
1598
|
+
// the line's size with each item that may shrink at its minimum
|
|
1599
|
+
let least = 0;
|
|
1600
|
+
let shrinking = 0;
|
|
1601
|
+
let over = false;
|
|
1602
|
+
for (const { box, laid } of items) {
|
|
1603
|
+
const style = box.style;
|
|
1604
|
+
// an `auto` margin is none on a line with no room (8.1)
|
|
1605
|
+
if (row) {
|
|
1606
|
+
if (style.marginLeft !== AUTO) between += box.marginLeft;
|
|
1607
|
+
if (style.marginRight !== AUTO) between += box.marginRight;
|
|
1608
|
+
} else {
|
|
1609
|
+
if (style.marginTop !== AUTO) between += box.marginTop;
|
|
1610
|
+
if (style.marginBottom !== AUTO) between += box.marginBottom;
|
|
1611
|
+
}
|
|
1612
|
+
if (!shrinks(box, laid)) least += laid.main;
|
|
1613
|
+
else {
|
|
1614
|
+
const min = leastOf(box, laid, row);
|
|
1615
|
+
shrinking += 1;
|
|
1616
|
+
least += min;
|
|
1617
|
+
if (laid.main > min + 0.01) over = true;
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
// two at the least: what is left of a sum of one, less it, is 0 exactly.
|
|
1621
|
+
// A line within a thousandth of a pixel of room is short of it: what
|
|
1622
|
+
// Yoga makes of that is as far out, and the minimums are that near
|
|
1623
|
+
if (shrinking < 2 || !over || !(between + least > room - SHORT)) {
|
|
1624
|
+
return false;
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
const stopped = items.filter(({ box, laid }) => shrinks(box, laid));
|
|
1628
|
+
for (const { node } of stopped) node.setFlexShrink(0);
|
|
1629
|
+
ask();
|
|
1630
|
+
// each item its hypothetical size, which one that does not shrink is on
|
|
1631
|
+
// a line short of room — where Yoga may have grown it, on a line it took
|
|
1632
|
+
// for having some
|
|
1633
|
+
let wider = false;
|
|
1634
|
+
least = 0;
|
|
1635
|
+
for (const { box, node, laid } of items) {
|
|
1636
|
+
const hypothetical = row
|
|
1637
|
+
? node.getComputedWidth()
|
|
1638
|
+
: node.getComputedHeight();
|
|
1639
|
+
if (!shrinks(box, laid)) least += hypothetical;
|
|
1640
|
+
else {
|
|
1641
|
+
least += leastOf(box, laid, row);
|
|
1642
|
+
if (laid.main > hypothetical && !nearly(laid.main, hypothetical)) {
|
|
1643
|
+
wider = true;
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
const freeze = wider && between + least > room - SHORT;
|
|
1648
|
+
for (const { box, node, laid } of stopped) {
|
|
1649
|
+
if (freeze) freezeAt(node, laid, row, leastOf(box, laid, row), hold);
|
|
1650
|
+
else node.setFlexShrink(box.style.flexShrink);
|
|
1651
|
+
}
|
|
1652
|
+
ask();
|
|
1653
|
+
return true;
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
/** Whether Yoga may shrink an item: it has a shrink factor, and is not
|
|
1657
|
+
* frozen (`freezeAt`). */
|
|
1658
|
+
function shrinks(box: Box, laid: Laid): boolean {
|
|
1659
|
+
return box.style.flexShrink > 0 && Number.isNaN(laid.held);
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
/** How near to its room a line may be and be short of it (`stoppedLine`). */
|
|
1663
|
+
const SHORT = 0.001;
|
|
1664
|
+
|
|
1665
|
+
/** The least an item may be along the main axis, its border box: the
|
|
1666
|
+
* minimum of its own, and no less than its padding and borders. */
|
|
1667
|
+
function leastOf(box: Box, laid: Laid, row: boolean): number {
|
|
1668
|
+
const extra = row ? box.horizontalExtra : box.verticalExtra;
|
|
1669
|
+
return Number.isNaN(laid.least) ? extra : Math.max(laid.least, extra);
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1065
1672
|
/** An item's min-content width, its border box's, taken the once: its
|
|
1066
1673
|
* content's, whatever width it has of its own (`keywordWidth`'s too). */
|
|
1067
1674
|
function minContentOf(box: Box, ctx: LayoutContext, laid: Laid): number {
|