@react-x11/components 0.16.0 → 0.17.0
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/css/animation.d.ts +91 -0
- package/dist/html/css/animation.d.ts.map +1 -0
- package/dist/html/css/animation.js +430 -0
- package/dist/html/css/animation.js.map +1 -0
- package/dist/html/css/cascade.d.ts +27 -4
- package/dist/html/css/cascade.d.ts.map +1 -1
- package/dist/html/css/cascade.js +94 -6
- package/dist/html/css/cascade.js.map +1 -1
- package/dist/html/css/parse.d.ts +35 -0
- package/dist/html/css/parse.d.ts.map +1 -1
- package/dist/html/css/parse.js +115 -2
- package/dist/html/css/parse.js.map +1 -1
- package/dist/html/css/style.d.ts +5 -0
- package/dist/html/css/style.d.ts.map +1 -1
- package/dist/html/css/style.js +57 -7
- package/dist/html/css/style.js.map +1 -1
- package/dist/html/css/ua.js +12 -8
- package/dist/html/css/ua.js.map +1 -1
- package/dist/html/layout/boxes.d.ts +13 -0
- package/dist/html/layout/boxes.d.ts.map +1 -1
- package/dist/html/layout/boxes.js +28 -0
- package/dist/html/layout/boxes.js.map +1 -1
- package/dist/html/layout/flex.d.ts.map +1 -1
- package/dist/html/layout/flex.js +237 -67
- package/dist/html/layout/flex.js.map +1 -1
- package/dist/html/layout/inline.d.ts.map +1 -1
- package/dist/html/layout/inline.js +41 -11
- package/dist/html/layout/inline.js.map +1 -1
- package/dist/html/node.d.ts.map +1 -1
- package/dist/html/node.js +12 -0
- package/dist/html/node.js.map +1 -1
- package/package.json +1 -1
- package/src/html/css/animation.ts +466 -0
- package/src/html/css/cascade.ts +130 -7
- package/src/html/css/parse.ts +151 -2
- package/src/html/css/style.ts +69 -7
- package/src/html/css/ua.ts +12 -8
- package/src/html/layout/boxes.ts +40 -0
- package/src/html/layout/flex.ts +251 -75
- package/src/html/layout/inline.ts +43 -9
- package/src/html/node.ts +12 -0
package/src/html/layout/flex.ts
CHANGED
|
@@ -252,6 +252,10 @@ export function layoutFlex(
|
|
|
252
252
|
const direction =
|
|
253
253
|
box.style.direction === 'rtl' ? Y.DIRECTION_RTL : Y.DIRECTION_LTR;
|
|
254
254
|
const row = box.style.flexDirection.startsWith('row');
|
|
255
|
+
const baselines =
|
|
256
|
+
row &&
|
|
257
|
+
(box.style.alignItems === 'baseline' ||
|
|
258
|
+
items.some(({ box: child }) => child.style.alignSelf === 'baseline'));
|
|
255
259
|
// An `auto` margin across a row that wraps takes the room its line has
|
|
256
260
|
// past the item (CSS Flexbox 8.1, 9.6), where Yoga's pass that sets the
|
|
257
261
|
// items across their lines reads an item's alignment and not its
|
|
@@ -264,6 +268,43 @@ export function layoutFlex(
|
|
|
264
268
|
const aligned = autoAcross(child.style, reverse);
|
|
265
269
|
if (aligned) node.setAlignSelf(ALIGN[aligned]);
|
|
266
270
|
}
|
|
271
|
+
} else if (baselines) {
|
|
272
|
+
// Yoga takes a row aligned by baselines through that pass whether it
|
|
273
|
+
// wraps or not, and sets an item with an `auto` margin there by its
|
|
274
|
+
// baseline: at the top, where the margin puts it at the bottom, and
|
|
275
|
+
// every item beside it as far down as its height reached. That item
|
|
276
|
+
// takes no part in aligning the line by baselines (9.4 step 8), and
|
|
277
|
+
// Yoga is told the alignment its margins come to.
|
|
278
|
+
for (const { box: child, node } of items) {
|
|
279
|
+
const aligned = autoAcross(child.style, false);
|
|
280
|
+
if (aligned) node.setAlignSelf(ALIGN[aligned]);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
// Items aligned by their baselines, which Yoga cannot see, are told they
|
|
284
|
+
// are at their lines' starts, which is where one is that shares its line
|
|
285
|
+
// with no other (8.3); those that do share one are lined up by their
|
|
286
|
+
// baselines once those are known (`baselineLines`), and the row laid out
|
|
287
|
+
// again. Told an item is aligned by its baseline, Yoga sizes each line
|
|
288
|
+
// of the row as the most any item on it reaches down to its bottom edge
|
|
289
|
+
// — a baseline, to Yoga — and the largest bottom margin besides,
|
|
290
|
+
// whichever items those are, and stretches the items on the line to
|
|
291
|
+
// that: a row of no height of its own was as tall as both, and
|
|
292
|
+
// `align-content` had that much less room to share out. And it takes a
|
|
293
|
+
// row that does not wrap through its pass for lines, which sets the one
|
|
294
|
+
// line there as tall as its items and not the row: an item centred or at
|
|
295
|
+
// the end of a row of a height was centred or ended in the items'
|
|
296
|
+
// height. Told none is, it takes neither. A row whose lines wrap in
|
|
297
|
+
// reverse is left to Yoga.
|
|
298
|
+
const started = new Set<Box>();
|
|
299
|
+
if (baselines && box.style.flexWrap !== 'wrap-reverse') {
|
|
300
|
+
for (const { box: child, node } of items) {
|
|
301
|
+
if (!byBaseline(box.style, child.style)) continue;
|
|
302
|
+
node.setAlignSelf(Y.ALIGN_FLEX_START);
|
|
303
|
+
started.add(child);
|
|
304
|
+
}
|
|
305
|
+
if (box.style.alignItems === 'baseline') {
|
|
306
|
+
root.setAlignItems(Y.ALIGN_FLEX_START);
|
|
307
|
+
}
|
|
267
308
|
}
|
|
268
309
|
// frozen at its least size on the one line of a box that does not
|
|
269
310
|
// wrap, by a basis where Yoga takes one along the main axis
|
|
@@ -288,12 +329,13 @@ export function layoutFlex(
|
|
|
288
329
|
// `align-content` spaces out come out of its layout as tall as their
|
|
289
330
|
// line and the room after it as well (`unspace`): laid out so that each
|
|
290
331
|
// is as tall as its line, where the box has room to space them out in —
|
|
291
|
-
// a height, or a minimum one
|
|
332
|
+
// a height, or a minimum one — and with the lines at its start where it
|
|
333
|
+
// may be too short for them, a height or a maximum one
|
|
292
334
|
const spaced =
|
|
293
335
|
row &&
|
|
294
336
|
bounded &&
|
|
295
337
|
!hold.frozen &&
|
|
296
|
-
(height !== null || floor > 0) &&
|
|
338
|
+
(height !== null || floor > 0 || ceiling < Infinity) &&
|
|
297
339
|
spacesLines(box.style);
|
|
298
340
|
const ask =
|
|
299
341
|
restretched.length > 0
|
|
@@ -409,42 +451,33 @@ export function layoutFlex(
|
|
|
409
451
|
}
|
|
410
452
|
}
|
|
411
453
|
|
|
412
|
-
// Yoga aligns the lines of a box that wraps
|
|
413
|
-
//
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
454
|
+
// Yoga aligns the lines of a box that wraps in one pass of its own, which
|
|
455
|
+
// drops a margin (`lineMarginFix`), and keeps the margins of one that
|
|
456
|
+
// does not, which it takes through that pass only where it is told an
|
|
457
|
+
// item is aligned by its baseline: here, never
|
|
458
|
+
const lined = box.style.flexWrap !== 'nowrap';
|
|
417
459
|
let bottom = placeItems(
|
|
418
460
|
box,
|
|
419
461
|
ctx,
|
|
420
462
|
items,
|
|
421
463
|
row,
|
|
422
464
|
own,
|
|
423
|
-
|
|
465
|
+
lined,
|
|
424
466
|
contentWidth,
|
|
425
|
-
|
|
467
|
+
started,
|
|
426
468
|
crossed ? crossLines(box.style, items, contentWidth, columnGap) : undefined,
|
|
427
469
|
);
|
|
428
|
-
// Items aligned by their baselines
|
|
429
|
-
//
|
|
430
|
-
//
|
|
431
|
-
//
|
|
432
|
-
|
|
433
|
-
const lines = row && baselines ? baselineLines(box.style, items) : null;
|
|
470
|
+
// Items that share a line and are aligned by their baselines are laid
|
|
471
|
+
// out by the flex layout again, set at their lines' starts with the
|
|
472
|
+
// margins that put their baselines together (`baselineLines`), which
|
|
473
|
+
// makes each line as tall as that does
|
|
474
|
+
const lines = started.size > 1 ? baselineLines(box.style, items) : null;
|
|
434
475
|
if (lines) {
|
|
435
|
-
// every one of them, and the box's own `baseline` too, which is what
|
|
436
|
-
// takes Yoga down that pass
|
|
437
|
-
const started = new Set<Box>();
|
|
438
476
|
for (const line of lines) {
|
|
439
|
-
line.forEach(({
|
|
440
|
-
node.setAlignSelf(Y.ALIGN_FLEX_START);
|
|
477
|
+
line.forEach(({ node }, i) => {
|
|
441
478
|
node.setMargin(Y.EDGE_TOP, line.margins[i]);
|
|
442
|
-
started.add(child);
|
|
443
479
|
});
|
|
444
480
|
}
|
|
445
|
-
if (box.style.alignItems === 'baseline') {
|
|
446
|
-
root.setAlignItems(Y.ALIGN_FLEX_START);
|
|
447
|
-
}
|
|
448
481
|
ctx.flexDepth = depth + 1;
|
|
449
482
|
try {
|
|
450
483
|
calculate();
|
|
@@ -457,7 +490,7 @@ export function layoutFlex(
|
|
|
457
490
|
items,
|
|
458
491
|
row,
|
|
459
492
|
own,
|
|
460
|
-
|
|
493
|
+
lined,
|
|
461
494
|
contentWidth,
|
|
462
495
|
started,
|
|
463
496
|
);
|
|
@@ -485,8 +518,9 @@ export function layoutFlex(
|
|
|
485
518
|
items,
|
|
486
519
|
row,
|
|
487
520
|
own,
|
|
488
|
-
|
|
521
|
+
lined,
|
|
489
522
|
contentWidth,
|
|
523
|
+
started,
|
|
490
524
|
);
|
|
491
525
|
}
|
|
492
526
|
|
|
@@ -676,16 +710,26 @@ function placeItems(
|
|
|
676
710
|
let bottom = 0;
|
|
677
711
|
// the lines of a row that wraps in reverse are turned round to run up it
|
|
678
712
|
const reverse = row && box.style.flexWrap === 'wrap-reverse';
|
|
713
|
+
// the left edge a column's items aligned by baselines share, where it
|
|
714
|
+
// does not wrap (`baselineEdge`); a column that wraps is `crossed`
|
|
715
|
+
const edge =
|
|
716
|
+
row || box.style.flexWrap !== 'nowrap'
|
|
717
|
+
? NaN
|
|
718
|
+
: baselineEdge(box.style, items);
|
|
679
719
|
for (const { box: child, node, laid } of items) {
|
|
680
720
|
const placed = crossed?.get(child);
|
|
681
721
|
const across =
|
|
682
722
|
lined && !placed
|
|
683
|
-
? lineMarginFix(box.style, child, node, row, started?.has(child))
|
|
723
|
+
? lineMarginFix(box.style, child, node, row, own, started?.has(child))
|
|
684
724
|
: 0;
|
|
685
725
|
const itemHeight = row ? heightOf(node, laid) : laid.main;
|
|
686
726
|
const left =
|
|
687
727
|
box.contentX +
|
|
688
|
-
(placed
|
|
728
|
+
(placed
|
|
729
|
+
? placed.x
|
|
730
|
+
: !Number.isNaN(edge) && byBaseline(box.style, child.style, false)
|
|
731
|
+
? edge
|
|
732
|
+
: node.getComputedLeft() + (row ? 0 : across));
|
|
689
733
|
// which Yoga turns an item round in by the height it laid it out at, and
|
|
690
734
|
// not the one it is (`unspace`)
|
|
691
735
|
const top =
|
|
@@ -896,6 +940,14 @@ function applyItem(
|
|
|
896
940
|
if (style.flexShrink) node.setFlexShrink(style.flexShrink);
|
|
897
941
|
if (style.alignSelf !== AUTO)
|
|
898
942
|
node.setAlignSelf(ALIGN[style.alignSelf] ?? Y.ALIGN_AUTO);
|
|
943
|
+
// `stretch` sets an item whose height is a percentage of none at its
|
|
944
|
+
// line's start, as tall as its content (`heightOfNone`), where Yoga,
|
|
945
|
+
// which is handed no height for it, stretched it across the line
|
|
946
|
+
if (row && box.parent && heightOfNone(style, own)) {
|
|
947
|
+
const align =
|
|
948
|
+
style.alignSelf === AUTO ? box.parent.style.alignItems : style.alignSelf;
|
|
949
|
+
if (align === 'stretch') node.setAlignSelf(Y.ALIGN_FLEX_START);
|
|
950
|
+
}
|
|
899
951
|
|
|
900
952
|
// Yoga's sizes are border boxes, as `box-sizing: border-box` has them;
|
|
901
953
|
// a `content-box` length is the content's, and the item's padding and
|
|
@@ -2820,17 +2872,18 @@ function contentBottom(box: Box): number {
|
|
|
2820
2872
|
* its bottom edge, so it lined them up by their bottoms. With the items laid
|
|
2821
2873
|
* out, each line's, and for each the top margin that sets it at the line's
|
|
2822
2874
|
* start with its first baseline where the lowest-reaching one's is — or
|
|
2823
|
-
* null where no line has two to align.
|
|
2875
|
+
* null where no line has two to align. Not for a row whose lines wrap in
|
|
2876
|
+
* reverse, which grow from their ends and are left to Yoga (`layoutFlex`).
|
|
2824
2877
|
*/
|
|
2825
|
-
function baselineLines<T extends { box: Box }>(
|
|
2878
|
+
function baselineLines<T extends { box: Box; node: YogaNode }>(
|
|
2826
2879
|
style: ComputedStyle,
|
|
2827
2880
|
items: readonly T[],
|
|
2828
2881
|
): (T[] & { margins: number[] })[] | null {
|
|
2829
|
-
// a line that grows from its end is aligned from there, and left to Yoga
|
|
2830
|
-
if (style.flexWrap === 'wrap-reverse') return null;
|
|
2831
2882
|
const wraps = style.flexWrap !== 'nowrap';
|
|
2832
|
-
// along a line the items go the way the main axis does,
|
|
2833
|
-
// line starts
|
|
2883
|
+
// along a line the items go the way the main axis does, each margin box
|
|
2884
|
+
// after the last one's end, and the next line starts before it (9.3): at
|
|
2885
|
+
// the line's start, where an item with a margin before it was further
|
|
2886
|
+
// along than one alone on the line before
|
|
2834
2887
|
const back =
|
|
2835
2888
|
(style.flexDirection === 'row-reverse') !== (style.direction === 'rtl');
|
|
2836
2889
|
const out: (T[] & { margins: number[] })[] = [];
|
|
@@ -2852,28 +2905,76 @@ function baselineLines<T extends { box: Box }>(
|
|
|
2852
2905
|
out.push(Object.assign(line, { margins }));
|
|
2853
2906
|
};
|
|
2854
2907
|
for (const item of items) {
|
|
2855
|
-
const box = item
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
const own = box.style;
|
|
2862
|
-
const aligned = own.alignSelf === AUTO ? style.alignItems : own.alignSelf;
|
|
2863
|
-
// an `auto` margin across the line takes it over alignment
|
|
2908
|
+
const { box, node } = item;
|
|
2909
|
+
const left = node.getComputedLeft() - node.getComputedMargin(Y.EDGE_LEFT);
|
|
2910
|
+
const right =
|
|
2911
|
+
node.getComputedLeft() +
|
|
2912
|
+
node.getComputedWidth() +
|
|
2913
|
+
node.getComputedMargin(Y.EDGE_RIGHT);
|
|
2864
2914
|
if (
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2915
|
+
wraps &&
|
|
2916
|
+
line.length &&
|
|
2917
|
+
(back ? right > last + 0.01 : left < last - 0.01)
|
|
2868
2918
|
) {
|
|
2869
|
-
|
|
2919
|
+
close();
|
|
2920
|
+
line = [];
|
|
2870
2921
|
}
|
|
2922
|
+
last = back ? left : right;
|
|
2923
|
+
if (byBaseline(style, box.style)) line.push(item);
|
|
2871
2924
|
}
|
|
2872
2925
|
close();
|
|
2873
2926
|
// one alone on its line is at its start already
|
|
2874
2927
|
return several ? out : null;
|
|
2875
2928
|
}
|
|
2876
2929
|
|
|
2930
|
+
/** Whether an item takes part in aligning its line by baselines (CSS
|
|
2931
|
+
* Flexbox 8.3, 9.4 step 8): aligned by its baseline, with no `auto`
|
|
2932
|
+
* margin across the line, which takes it over its alignment. */
|
|
2933
|
+
function byBaseline(
|
|
2934
|
+
container: ComputedStyle,
|
|
2935
|
+
item: ComputedStyle,
|
|
2936
|
+
/** Whether the line is a row's, across which its margins are its top
|
|
2937
|
+
* and bottom ones; a column's are its left and right ones. */
|
|
2938
|
+
row = true,
|
|
2939
|
+
): boolean {
|
|
2940
|
+
const aligned =
|
|
2941
|
+
item.alignSelf === AUTO ? container.alignItems : item.alignSelf;
|
|
2942
|
+
return (
|
|
2943
|
+
aligned === 'baseline' &&
|
|
2944
|
+
(row ? item.marginTop : item.marginLeft) !== AUTO &&
|
|
2945
|
+
(row ? item.marginBottom : item.marginRight) !== AUTO
|
|
2946
|
+
);
|
|
2947
|
+
}
|
|
2948
|
+
|
|
2949
|
+
/**
|
|
2950
|
+
* Where the items a column that does not wrap aligns by baselines have
|
|
2951
|
+
* their left edges, from its content box's: NaN where it has none.
|
|
2952
|
+
*
|
|
2953
|
+
* A horizontal item has no baseline across a column, and one is made from
|
|
2954
|
+
* its border box — the line-under edge of the vertical writing mode it is
|
|
2955
|
+
* taken to have, its left, whichever way its text runs (CSS Box Alignment
|
|
2956
|
+
* 3, 9.1). So the items share their left edges, and are set together at
|
|
2957
|
+
* the line's cross start, the column's left or, where it runs right to
|
|
2958
|
+
* left, its right (9.3; CSS Flexbox 8.3), as Chrome sets them: as far from
|
|
2959
|
+
* there as the most any of them reaches past its edge on that side. Yoga
|
|
2960
|
+
* never aligns a column by baselines, and sets each at its line's start
|
|
2961
|
+
* with its margin there, so that is the furthest any of them is from the
|
|
2962
|
+
* start: the rightmost left edge, or from the right the leftmost.
|
|
2963
|
+
*/
|
|
2964
|
+
function baselineEdge(
|
|
2965
|
+
container: ComputedStyle,
|
|
2966
|
+
items: readonly { box: Box; node: YogaNode }[],
|
|
2967
|
+
): number {
|
|
2968
|
+
const rtl = container.direction === 'rtl';
|
|
2969
|
+
let edge = NaN;
|
|
2970
|
+
for (const { box, node } of items) {
|
|
2971
|
+
if (!byBaseline(container, box.style, false)) continue;
|
|
2972
|
+
const left = node.getComputedLeft();
|
|
2973
|
+
if (!(rtl ? left >= edge : left <= edge)) edge = left;
|
|
2974
|
+
}
|
|
2975
|
+
return edge;
|
|
2976
|
+
}
|
|
2977
|
+
|
|
2877
2978
|
/**
|
|
2878
2979
|
* Whether Yoga made a line of a row that wraps the row's height, where its
|
|
2879
2980
|
* items make it another (CSS Flexbox 9.4, steps 7 and 8; `layoutFlex`).
|
|
@@ -2983,13 +3084,13 @@ function autoAcross(
|
|
|
2983
3084
|
|
|
2984
3085
|
/**
|
|
2985
3086
|
* How far an item is from where it belongs across its line, where Yoga
|
|
2986
|
-
* (3.2.1) aligned the lines in the pass it takes for a box that wraps
|
|
2987
|
-
*
|
|
2988
|
-
*
|
|
2989
|
-
*
|
|
2990
|
-
*
|
|
2991
|
-
*
|
|
2992
|
-
*
|
|
3087
|
+
* (3.2.1) aligned the lines in the pass it takes for a box that wraps: it
|
|
3088
|
+
* sets an item aligned to its line's start there as though it had no
|
|
3089
|
+
* margin at that side, and one centred as though it had none at either —
|
|
3090
|
+
* `items-start` in a wrapping row put every card's top margin under it. By
|
|
3091
|
+
* the margin at the start of the line's cross axis, and by half what it
|
|
3092
|
+
* exceeds the one at the end by, as Yoga holds them: for an item set at
|
|
3093
|
+
* its line's start (`started`), its baseline's.
|
|
2993
3094
|
*
|
|
2994
3095
|
* A row whose lines wrap in reverse Yoga lays out as though they wrapped
|
|
2995
3096
|
* forward, and then turns each item over across the box, its margins with
|
|
@@ -2998,14 +3099,15 @@ function autoAcross(
|
|
|
2998
3099
|
* stretched, is its bottom margin down from its line's top where it is its
|
|
2999
3100
|
* top one; and one centred, half the difference. A column's, whose cross
|
|
3000
3101
|
* axis runs right to left, or that wraps in reverse, is left as Yoga has
|
|
3001
|
-
* it, and so is an item an `auto` margin aligns
|
|
3002
|
-
* wrap.
|
|
3102
|
+
* it, and so is an item an `auto` margin aligns across a column.
|
|
3003
3103
|
*/
|
|
3004
3104
|
function lineMarginFix(
|
|
3005
3105
|
container: ComputedStyle,
|
|
3006
3106
|
item: Box,
|
|
3007
3107
|
node: YogaNode,
|
|
3008
3108
|
row: boolean,
|
|
3109
|
+
/** Whether the flex box has a definite height. */
|
|
3110
|
+
own: boolean,
|
|
3009
3111
|
started = false,
|
|
3010
3112
|
): number {
|
|
3011
3113
|
const reverse = container.flexWrap === 'wrap-reverse';
|
|
@@ -3018,12 +3120,14 @@ function lineMarginFix(
|
|
|
3018
3120
|
let align: string | null;
|
|
3019
3121
|
if (started) align = 'flex-start';
|
|
3020
3122
|
else if (lead === AUTO || trail === AUTO) {
|
|
3021
|
-
align =
|
|
3022
|
-
row && container.flexWrap !== 'nowrap'
|
|
3023
|
-
? autoAcross(style, reverse)
|
|
3024
|
-
: null;
|
|
3123
|
+
align = row ? autoAcross(style, reverse) : null;
|
|
3025
3124
|
} else {
|
|
3026
3125
|
align = style.alignSelf === AUTO ? container.alignItems : style.alignSelf;
|
|
3126
|
+
// which sets one whose height is a percentage of none at its line's
|
|
3127
|
+
// start, as Yoga is told it does (`applyItem`)
|
|
3128
|
+
if (align === 'stretch' && row && heightOfNone(style, own)) {
|
|
3129
|
+
align = 'flex-start';
|
|
3130
|
+
}
|
|
3027
3131
|
}
|
|
3028
3132
|
const leading = node.getComputedMargin(row ? Y.EDGE_TOP : Y.EDGE_LEFT);
|
|
3029
3133
|
const trailing = node.getComputedMargin(row ? Y.EDGE_BOTTOM : Y.EDGE_RIGHT);
|
|
@@ -3041,11 +3145,14 @@ function lineMarginFix(
|
|
|
3041
3145
|
|
|
3042
3146
|
/** An item's first baseline, down from its border edge: its content's
|
|
3043
3147
|
* first line's, or where it has none, or is replaced, one synthesized
|
|
3044
|
-
* from its border box — its bottom edge.
|
|
3148
|
+
* from its border box — its bottom edge. Past that edge where the line
|
|
3149
|
+
* is: only a scroll container's is held to its box (`firstBaselineIn`),
|
|
3150
|
+
* and an item shorter than its text, or whose padding puts the text
|
|
3151
|
+
* under it, sits on its text where it had sat on its bottom. */
|
|
3045
3152
|
function itemBaseline(item: Box): number {
|
|
3046
3153
|
if (item.kind !== 'replaced') {
|
|
3047
3154
|
const found = firstBaselineIn(item);
|
|
3048
|
-
if (found !== null) return
|
|
3155
|
+
if (found !== null) return found - item.y;
|
|
3049
3156
|
}
|
|
3050
3157
|
return item.height;
|
|
3051
3158
|
}
|
|
@@ -3197,6 +3304,16 @@ function spacesLines(style: ComputedStyle): boolean {
|
|
|
3197
3304
|
* makes too, and is kept. Where they do, Yoga is asked again as the row
|
|
3198
3305
|
* is, for where the lines and the items are, and the heights are kept from
|
|
3199
3306
|
* the first layout.
|
|
3307
|
+
*
|
|
3308
|
+
* And where the lines are taller than the row, `space-around` and
|
|
3309
|
+
* `space-evenly` fall back to `safe center`, which sets them at the row's
|
|
3310
|
+
* own start, its top (CSS Box Alignment 3, 4.3 and 5.1; CSS Flexbox 8.4).
|
|
3311
|
+
* Yoga falls back to `flex-start` for both, which is the bottom where the
|
|
3312
|
+
* lines run bottom to top, and ran them out past the row's top. It sets
|
|
3313
|
+
* the lines of a row that wraps in reverse top to bottom and turns them
|
|
3314
|
+
* over once they are set, so it is asked for them at `flex-end` there,
|
|
3315
|
+
* which comes out at the top — where the lines it set at their start, or
|
|
3316
|
+
* spaced, reach past the row's bottom.
|
|
3200
3317
|
*/
|
|
3201
3318
|
function unspace(
|
|
3202
3319
|
root: YogaNode,
|
|
@@ -3206,6 +3323,16 @@ function unspace(
|
|
|
3206
3323
|
layout: () => void,
|
|
3207
3324
|
): void {
|
|
3208
3325
|
for (const { laid } of items) laid.unstretched = NaN;
|
|
3326
|
+
const lines = root.getAlignContent();
|
|
3327
|
+
// where the lines start at the row's bottom, its start is their end
|
|
3328
|
+
const safe =
|
|
3329
|
+
reverse &&
|
|
3330
|
+
(lines === Y.ALIGN_SPACE_AROUND || lines === Y.ALIGN_SPACE_EVENLY);
|
|
3331
|
+
const atEnd = (): void => {
|
|
3332
|
+
root.setAlignContent(Y.ALIGN_FLEX_END);
|
|
3333
|
+
layout();
|
|
3334
|
+
root.setAlignContent(lines);
|
|
3335
|
+
};
|
|
3209
3336
|
// each item Yoga stretches: `stretch`, with no height of its own
|
|
3210
3337
|
const align = root.getAlignItems();
|
|
3211
3338
|
const stretched = items.filter(({ node }) => {
|
|
@@ -3216,15 +3343,24 @@ function unspace(
|
|
|
3216
3343
|
});
|
|
3217
3344
|
if (stretched.length === 0) {
|
|
3218
3345
|
layout();
|
|
3346
|
+
// spaced where there is room, and else at their start, past the bottom
|
|
3347
|
+
if (
|
|
3348
|
+
safe &&
|
|
3349
|
+
linesReach(root, items, reverse) - root.getComputedHeight() > NEAR
|
|
3350
|
+
) {
|
|
3351
|
+
atEnd();
|
|
3352
|
+
}
|
|
3219
3353
|
return;
|
|
3220
3354
|
}
|
|
3221
|
-
const lines = root.getAlignContent();
|
|
3222
3355
|
root.setAlignContent(Y.ALIGN_FLEX_START);
|
|
3223
3356
|
layout();
|
|
3224
3357
|
root.setAlignContent(lines);
|
|
3225
|
-
|
|
3358
|
+
const room = root.getComputedHeight() - linesReach(root, items, reverse);
|
|
3359
|
+
if (safe && room < -NEAR) {
|
|
3360
|
+
atEnd();
|
|
3226
3361
|
return;
|
|
3227
3362
|
}
|
|
3363
|
+
if (!(room > NEAR)) return;
|
|
3228
3364
|
const heights = stretched.map(({ node }) => node.getComputedHeight());
|
|
3229
3365
|
layout();
|
|
3230
3366
|
stretched.forEach(({ node, laid }, i) => {
|
|
@@ -3276,6 +3412,22 @@ function linesReach(
|
|
|
3276
3412
|
return reach;
|
|
3277
3413
|
}
|
|
3278
3414
|
|
|
3415
|
+
/**
|
|
3416
|
+
* Whether a row item's height is a percentage of a height its flex box has
|
|
3417
|
+
* none of. That is `auto` to the item's size (CSS 2.1 10.5), and is not
|
|
3418
|
+
* handed to Yoga (`applyItem`), but it is not `auto` to its alignment:
|
|
3419
|
+
* `stretch` stretches an item whose height computes to `auto` (CSS Flexbox
|
|
3420
|
+
* 8.3), and this one's computes to the percentage. Such an item is as tall
|
|
3421
|
+
* as its content, set at its line's start, which is what Yoga is told.
|
|
3422
|
+
*/
|
|
3423
|
+
function heightOfNone(
|
|
3424
|
+
style: ComputedStyle,
|
|
3425
|
+
/** Whether the flex box has a definite height. */
|
|
3426
|
+
own: boolean,
|
|
3427
|
+
): boolean {
|
|
3428
|
+
return !own && isPct(style.height);
|
|
3429
|
+
}
|
|
3430
|
+
|
|
3279
3431
|
/** Whether an item is stretched across its line: `stretch`, its own or its
|
|
3280
3432
|
* container's, with no height of its own and no `auto` margin across. */
|
|
3281
3433
|
function stretches(
|
|
@@ -3331,15 +3483,17 @@ interface Across {
|
|
|
3331
3483
|
* where they are short of the box; `center` and `flex-end` whether they are
|
|
3332
3484
|
* or not, so that lines wider than the box run past both its sides, or its
|
|
3333
3485
|
* start; and the spacing values only where there is room, setting the lines
|
|
3334
|
-
* at
|
|
3335
|
-
* `space-between` falls back to `flex-start`, `space-around` and
|
|
3336
|
-
* `space-evenly` to `safe center`, the start where the lines
|
|
3486
|
+
* at their start where there is none, or at the box's (CSS Box Alignment 3,
|
|
3487
|
+
* 4.3: `space-between` falls back to `flex-start`, `space-around` and
|
|
3488
|
+
* `space-evenly` to `safe center`, the box's own start where the lines
|
|
3489
|
+
* overflow, and not theirs where they wrap in reverse). An
|
|
3337
3490
|
* item with an `auto` margin across its line takes the room the line has
|
|
3338
3491
|
* past it on that side (8.1), and none where there is none; a stretched
|
|
3339
3492
|
* item is as wide as its line less its margins, within its limits (9.4,
|
|
3340
|
-
* step 11), and as tall as the column made it;
|
|
3341
|
-
*
|
|
3342
|
-
*
|
|
3493
|
+
* step 11), and as tall as the column made it; those aligned by baselines
|
|
3494
|
+
* share their left edges, set together at the line's start, where a column
|
|
3495
|
+
* that does not wrap sets them (`baselineEdge`); and the rest are where
|
|
3496
|
+
* `align-self` says.
|
|
3343
3497
|
*/
|
|
3344
3498
|
function crossLines(
|
|
3345
3499
|
style: ComputedStyle,
|
|
@@ -3373,14 +3527,34 @@ function crossLines(
|
|
|
3373
3527
|
lead.push(fromRight ? r : l);
|
|
3374
3528
|
trail.push(fromRight ? l : r);
|
|
3375
3529
|
});
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3530
|
+
// A line is as wide as its widest item, margins and all, and as the
|
|
3531
|
+
// items it aligns by baselines come to, sharing their left edges: the
|
|
3532
|
+
// most any reaches from the line's start to that edge, and the most any
|
|
3533
|
+
// reaches past it (`baselineEdge`; 9.4 step 8). `reach` is each line's
|
|
3534
|
+
// first, where those items' left edges are from its start.
|
|
3535
|
+
const reach: number[] = [];
|
|
3536
|
+
const across = lines.map((line) => {
|
|
3537
|
+
let most = 0;
|
|
3538
|
+
let before = -Infinity;
|
|
3539
|
+
let after = -Infinity;
|
|
3540
|
+
for (const i of line) {
|
|
3541
|
+
most = Math.max(most, sizes[i] + lead[i] + trail[i]);
|
|
3542
|
+
if (!byBaseline(style, items[i].box.style, false)) continue;
|
|
3543
|
+
before = Math.max(before, lead[i] + (fromRight ? sizes[i] : 0));
|
|
3544
|
+
after = Math.max(after, trail[i] + (fromRight ? 0 : sizes[i]));
|
|
3545
|
+
}
|
|
3546
|
+
reach.push(before);
|
|
3547
|
+
return Math.max(most, before + after);
|
|
3548
|
+
});
|
|
3379
3549
|
const count = lines.length;
|
|
3380
3550
|
let room = width - gap * (count - 1);
|
|
3381
3551
|
for (const size of across) room -= size;
|
|
3382
3552
|
let at = 0;
|
|
3383
3553
|
let between = gap;
|
|
3554
|
+
// Where they are wider than the box, `space-around` and `space-evenly`
|
|
3555
|
+
// set the lines at the box's own start (`safe center`), which is where
|
|
3556
|
+
// their last one ends where they wrap in reverse: they run past its end
|
|
3557
|
+
const reverse = style.flexWrap === 'wrap-reverse';
|
|
3384
3558
|
switch (crossContent(style)) {
|
|
3385
3559
|
case 'flex-end':
|
|
3386
3560
|
at = room;
|
|
@@ -3398,13 +3572,13 @@ function crossLines(
|
|
|
3398
3572
|
if (room > 0) {
|
|
3399
3573
|
at = room / count / 2;
|
|
3400
3574
|
between += room / count;
|
|
3401
|
-
}
|
|
3575
|
+
} else if (reverse) at = room;
|
|
3402
3576
|
break;
|
|
3403
3577
|
case 'space-evenly':
|
|
3404
3578
|
if (room > 0) {
|
|
3405
3579
|
at = room / (count + 1);
|
|
3406
3580
|
between += room / (count + 1);
|
|
3407
|
-
}
|
|
3581
|
+
} else if (reverse) at = room;
|
|
3408
3582
|
break;
|
|
3409
3583
|
}
|
|
3410
3584
|
const placed = new Map<Box, Across>();
|
|
@@ -3429,6 +3603,8 @@ function crossLines(
|
|
|
3429
3603
|
if (itemWidth > most) itemWidth = most;
|
|
3430
3604
|
if (itemWidth < least) itemWidth = least;
|
|
3431
3605
|
itemWidth = Math.max(itemWidth, box.horizontalExtra);
|
|
3606
|
+
} else if (byBaseline(style, own, false)) {
|
|
3607
|
+
offset = reach[k] - (fromRight ? itemWidth : 0);
|
|
3432
3608
|
} else {
|
|
3433
3609
|
const align = own.alignSelf === AUTO ? style.alignItems : own.alignSelf;
|
|
3434
3610
|
if (align === 'center') offset += free / 2;
|
|
@@ -45,6 +45,7 @@ import { inkColor, isTransparent, resolve } from '../css/values.js';
|
|
|
45
45
|
import { tagOf } from '../dom.js';
|
|
46
46
|
import {
|
|
47
47
|
BOX_RAISES,
|
|
48
|
+
CONTROL_BASELINES,
|
|
48
49
|
GRID_TRACKS,
|
|
49
50
|
LINE_BOX_RAISES,
|
|
50
51
|
PADDED_FACES,
|
|
@@ -3329,6 +3330,22 @@ function atomicRaise(
|
|
|
3329
3330
|
*/
|
|
3330
3331
|
function atomicBaseline(box: Box): number {
|
|
3331
3332
|
const margin = box.height + box.marginTop + box.marginBottom;
|
|
3333
|
+
// a control on its text's baseline, or on its border box's bottom,
|
|
3334
|
+
// where layout containment does not keep its baseline in
|
|
3335
|
+
const control =
|
|
3336
|
+
box.style.contain & CONTAIN_LAYOUT ? undefined : CONTROL_BASELINES.get(box);
|
|
3337
|
+
if (control === 'border') return box.marginTop + box.height;
|
|
3338
|
+
if (control) {
|
|
3339
|
+
const content = box.height - box.verticalExtra;
|
|
3340
|
+
const text = control.ascent + control.descent;
|
|
3341
|
+
return (
|
|
3342
|
+
box.marginTop +
|
|
3343
|
+
box.borderTop +
|
|
3344
|
+
box.padTop +
|
|
3345
|
+
(content - text) / 2 +
|
|
3346
|
+
control.ascent
|
|
3347
|
+
);
|
|
3348
|
+
}
|
|
3332
3349
|
const button = isButton(box);
|
|
3333
3350
|
// A `<button>` with no line in it sits on the bottom of its content box,
|
|
3334
3351
|
// not of its margin box: where its label would have sat. No specification
|
|
@@ -3468,14 +3485,14 @@ function tableBaseline(table: Box): number | null {
|
|
|
3468
3485
|
* chevron, and the line it was on came out shorter than a browser's.
|
|
3469
3486
|
*/
|
|
3470
3487
|
function lastBaselineIn(box: Box): number | null {
|
|
3471
|
-
if (box.kind === 'flex') return flexBaseline(box);
|
|
3488
|
+
if (box.kind === 'flex') return heldIn(box, flexBaseline(box));
|
|
3472
3489
|
if (box.lines?.length) {
|
|
3473
3490
|
const line = box.lines[box.lines.length - 1];
|
|
3474
|
-
return line.y + line.baseline;
|
|
3491
|
+
return heldIn(box, line.y + line.baseline);
|
|
3475
3492
|
}
|
|
3476
3493
|
for (let i = box.children.length - 1; i >= 0; i -= 1) {
|
|
3477
3494
|
const found = childBaseline(box.children[i], lastBaselineIn);
|
|
3478
|
-
if (found !== null) return found;
|
|
3495
|
+
if (found !== null) return heldIn(box, found);
|
|
3479
3496
|
}
|
|
3480
3497
|
return null;
|
|
3481
3498
|
}
|
|
@@ -3483,15 +3500,32 @@ function lastBaselineIn(box: Box): number | null {
|
|
|
3483
3500
|
/** The same, from the first line box or child: a table's baseline is its
|
|
3484
3501
|
* first row's, and so is a table cell's. */
|
|
3485
3502
|
export function firstBaselineIn(box: Box): number | null {
|
|
3486
|
-
if (box.kind === 'flex') return flexBaseline(box);
|
|
3487
|
-
if (box.lines?.length)
|
|
3503
|
+
if (box.kind === 'flex') return heldIn(box, flexBaseline(box));
|
|
3504
|
+
if (box.lines?.length) {
|
|
3505
|
+
return heldIn(box, box.lines[0].y + box.lines[0].baseline);
|
|
3506
|
+
}
|
|
3488
3507
|
for (const child of box.children) {
|
|
3489
3508
|
const found = childBaseline(child, firstBaselineIn);
|
|
3490
|
-
if (found !== null) return found;
|
|
3509
|
+
if (found !== null) return heldIn(box, found);
|
|
3491
3510
|
}
|
|
3492
3511
|
return null;
|
|
3493
3512
|
}
|
|
3494
3513
|
|
|
3514
|
+
/**
|
|
3515
|
+
* A box's baseline as its content gives it, held to its border box where
|
|
3516
|
+
* the box is a scroll container (CSS Box Alignment 3, 9.1), at either edge
|
|
3517
|
+
* — and only there: a box whose text overflows it otherwise, or clips it
|
|
3518
|
+
* (`overflow: clip` is no scroll container), has the baseline its text
|
|
3519
|
+
* has, as Chrome has it. Held at every box, so a scroll container deep in
|
|
3520
|
+
* an item, or in an inline block, holds the baseline it gives to its box.
|
|
3521
|
+
* The flex layout held every item to its height, and an item shorter
|
|
3522
|
+
* than its text sat its text's descent below the items beside it.
|
|
3523
|
+
*/
|
|
3524
|
+
function heldIn(box: Box, found: number | null): number | null {
|
|
3525
|
+
if (found === null || !scrolls(box.style)) return found;
|
|
3526
|
+
return Math.min(Math.max(found, box.y), box.y + box.height);
|
|
3527
|
+
}
|
|
3528
|
+
|
|
3495
3529
|
function childBaseline(
|
|
3496
3530
|
child: Box,
|
|
3497
3531
|
inside: (box: Box) => number | null,
|
|
@@ -3502,9 +3536,9 @@ function childBaseline(
|
|
|
3502
3536
|
// a table is passed over for a block's last baseline (`lastBaselineIn`)
|
|
3503
3537
|
if (inside === lastBaselineIn && child.kind === 'table') return null;
|
|
3504
3538
|
// the legacy rule is a block container's last baseline alone (CSS Box
|
|
3505
|
-
// Alignment 3, 9.2): its first is its first line's, clipped or not
|
|
3506
|
-
// and a button that clips keeps its
|
|
3507
|
-
// own (`atomicBaseline`)
|
|
3539
|
+
// Alignment 3, 9.2): its first is its first line's, clipped or not, held
|
|
3540
|
+
// to its border box (`heldIn`) — and a button that clips keeps its
|
|
3541
|
+
// label's, here as on a line of its own (`atomicBaseline`)
|
|
3508
3542
|
if (
|
|
3509
3543
|
inside === lastBaselineIn &&
|
|
3510
3544
|
child.kind !== 'flex' &&
|
package/src/html/node.ts
CHANGED
|
@@ -1051,6 +1051,10 @@ export class HtmlViewNode extends Node {
|
|
|
1051
1051
|
const fonts = this._fonts();
|
|
1052
1052
|
return fonts ? faceExtentOf(fonts, style).ascent : undefined;
|
|
1053
1053
|
},
|
|
1054
|
+
faceExtent: (style) => {
|
|
1055
|
+
const fonts = this._fonts();
|
|
1056
|
+
return fonts ? faceExtentOf(fonts, style) : undefined;
|
|
1057
|
+
},
|
|
1054
1058
|
controlSize: (el, kind, style) =>
|
|
1055
1059
|
measureControl(el, kind, style, this._fonts(), look),
|
|
1056
1060
|
});
|
|
@@ -3514,6 +3518,14 @@ function hoverChange(
|
|
|
3514
3518
|
}
|
|
3515
3519
|
continue;
|
|
3516
3520
|
}
|
|
3521
|
+
// what an element's animations leave is in its other fields already
|
|
3522
|
+
// (`Cascade._computeStyle`); the lists themselves draw nothing
|
|
3523
|
+
if (key === 'animations') {
|
|
3524
|
+
if (!sameValue(a[key], b[key])) {
|
|
3525
|
+
change ??= { ink: false, reach: false, order: false, move: false };
|
|
3526
|
+
}
|
|
3527
|
+
continue;
|
|
3528
|
+
}
|
|
3517
3529
|
if (sameValue(a[key], b[key])) continue;
|
|
3518
3530
|
change ??= { ink: true, reach: false, order: false, move: false };
|
|
3519
3531
|
change.ink = true;
|