@react-x11/components 0.13.0 → 0.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/dist/html/controls.d.ts +29 -2
  2. package/dist/html/controls.d.ts.map +1 -1
  3. package/dist/html/controls.js +70 -2
  4. package/dist/html/controls.js.map +1 -1
  5. package/dist/html/css/cascade.d.ts +42 -1
  6. package/dist/html/css/cascade.d.ts.map +1 -1
  7. package/dist/html/css/cascade.js +336 -16
  8. package/dist/html/css/cascade.js.map +1 -1
  9. package/dist/html/css/color.d.ts +18 -0
  10. package/dist/html/css/color.d.ts.map +1 -1
  11. package/dist/html/css/color.js +27 -0
  12. package/dist/html/css/color.js.map +1 -1
  13. package/dist/html/css/parse.d.ts +18 -4
  14. package/dist/html/css/parse.d.ts.map +1 -1
  15. package/dist/html/css/parse.js +66 -28
  16. package/dist/html/css/parse.js.map +1 -1
  17. package/dist/html/css/shapes.d.ts +44 -0
  18. package/dist/html/css/shapes.d.ts.map +1 -0
  19. package/dist/html/css/shapes.js +194 -0
  20. package/dist/html/css/shapes.js.map +1 -0
  21. package/dist/html/css/style.d.ts +128 -15
  22. package/dist/html/css/style.d.ts.map +1 -1
  23. package/dist/html/css/style.js +547 -144
  24. package/dist/html/css/style.js.map +1 -1
  25. package/dist/html/css/transform.d.ts +85 -0
  26. package/dist/html/css/transform.d.ts.map +1 -0
  27. package/dist/html/css/transform.js +425 -0
  28. package/dist/html/css/transform.js.map +1 -0
  29. package/dist/html/css/ua.d.ts.map +1 -1
  30. package/dist/html/css/ua.js +14 -2
  31. package/dist/html/css/ua.js.map +1 -1
  32. package/dist/html/css/values.d.ts +4 -0
  33. package/dist/html/css/values.d.ts.map +1 -1
  34. package/dist/html/css/values.js.map +1 -1
  35. package/dist/html/fonts.d.ts +55 -5
  36. package/dist/html/fonts.d.ts.map +1 -1
  37. package/dist/html/fonts.js +284 -23
  38. package/dist/html/fonts.js.map +1 -1
  39. package/dist/html/layout/axes.d.ts +14 -0
  40. package/dist/html/layout/axes.d.ts.map +1 -0
  41. package/dist/html/layout/axes.js +28 -0
  42. package/dist/html/layout/axes.js.map +1 -0
  43. package/dist/html/layout/block.d.ts +104 -12
  44. package/dist/html/layout/block.d.ts.map +1 -1
  45. package/dist/html/layout/block.js +410 -39
  46. package/dist/html/layout/block.js.map +1 -1
  47. package/dist/html/layout/boxes.d.ts +72 -1
  48. package/dist/html/layout/boxes.d.ts.map +1 -1
  49. package/dist/html/layout/boxes.js +40 -2
  50. package/dist/html/layout/boxes.js.map +1 -1
  51. package/dist/html/layout/cache.d.ts +1 -1
  52. package/dist/html/layout/cache.d.ts.map +1 -1
  53. package/dist/html/layout/cache.js +6 -0
  54. package/dist/html/layout/cache.js.map +1 -1
  55. package/dist/html/layout/css-grid.d.ts.map +1 -1
  56. package/dist/html/layout/css-grid.js +144 -24
  57. package/dist/html/layout/css-grid.js.map +1 -1
  58. package/dist/html/layout/flex.d.ts.map +1 -1
  59. package/dist/html/layout/flex.js +585 -85
  60. package/dist/html/layout/flex.js.map +1 -1
  61. package/dist/html/layout/inline.d.ts +3 -0
  62. package/dist/html/layout/inline.d.ts.map +1 -1
  63. package/dist/html/layout/inline.js +144 -54
  64. package/dist/html/layout/inline.js.map +1 -1
  65. package/dist/html/layout/multicol.d.ts +42 -0
  66. package/dist/html/layout/multicol.d.ts.map +1 -0
  67. package/dist/html/layout/multicol.js +897 -0
  68. package/dist/html/layout/multicol.js.map +1 -0
  69. package/dist/html/layout/shaping.d.ts +7 -0
  70. package/dist/html/layout/shaping.d.ts.map +1 -1
  71. package/dist/html/layout/shaping.js +25 -20
  72. package/dist/html/layout/shaping.js.map +1 -1
  73. package/dist/html/layout/table.d.ts.map +1 -1
  74. package/dist/html/layout/table.js +97 -31
  75. package/dist/html/layout/table.js.map +1 -1
  76. package/dist/html/node.d.ts +4 -0
  77. package/dist/html/node.d.ts.map +1 -1
  78. package/dist/html/node.js +319 -47
  79. package/dist/html/node.js.map +1 -1
  80. package/dist/html/paint.d.ts +83 -5
  81. package/dist/html/paint.d.ts.map +1 -1
  82. package/dist/html/paint.js +1474 -217
  83. package/dist/html/paint.js.map +1 -1
  84. package/dist/html/svg.d.ts +39 -9
  85. package/dist/html/svg.d.ts.map +1 -1
  86. package/dist/html/svg.js +482 -37
  87. package/dist/html/svg.js.map +1 -1
  88. package/dist/html/widgets.js +87 -7
  89. package/dist/html/widgets.js.map +1 -1
  90. package/dist/html/woff2.d.ts +14 -0
  91. package/dist/html/woff2.d.ts.map +1 -0
  92. package/dist/html/woff2.js +608 -0
  93. package/dist/html/woff2.js.map +1 -0
  94. package/dist/maps/controller.d.ts +17 -1
  95. package/dist/maps/controller.d.ts.map +1 -1
  96. package/dist/maps/controller.js +27 -7
  97. package/dist/maps/controller.js.map +1 -1
  98. package/dist/richtext/node.d.ts +8 -1
  99. package/dist/richtext/node.d.ts.map +1 -1
  100. package/dist/richtext/node.js +5 -1
  101. package/dist/richtext/node.js.map +1 -1
  102. package/dist/richtext/runs.d.ts +25 -6
  103. package/dist/richtext/runs.d.ts.map +1 -1
  104. package/dist/richtext/runs.js +240 -24
  105. package/dist/richtext/runs.js.map +1 -1
  106. package/package.json +1 -1
  107. package/src/html/controls.ts +85 -2
  108. package/src/html/css/cascade.ts +366 -8
  109. package/src/html/css/color.ts +29 -0
  110. package/src/html/css/parse.ts +71 -31
  111. package/src/html/css/shapes.ts +239 -0
  112. package/src/html/css/style.ts +652 -147
  113. package/src/html/css/transform.ts +459 -0
  114. package/src/html/css/ua.ts +15 -2
  115. package/src/html/css/values.ts +4 -0
  116. package/src/html/fonts.ts +312 -29
  117. package/src/html/layout/axes.ts +62 -0
  118. package/src/html/layout/block.ts +466 -37
  119. package/src/html/layout/boxes.ts +95 -3
  120. package/src/html/layout/cache.ts +6 -0
  121. package/src/html/layout/css-grid.ts +169 -32
  122. package/src/html/layout/flex.ts +686 -79
  123. package/src/html/layout/inline.ts +149 -55
  124. package/src/html/layout/multicol.ts +977 -0
  125. package/src/html/layout/shaping.ts +27 -18
  126. package/src/html/layout/table.ts +105 -35
  127. package/src/html/node.ts +354 -48
  128. package/src/html/paint.ts +1804 -315
  129. package/src/html/svg.ts +573 -36
  130. package/src/html/widgets.ts +95 -9
  131. package/src/html/woff2.ts +612 -0
  132. package/src/maps/controller.ts +28 -7
  133. package/src/richtext/node.ts +13 -2
  134. package/src/richtext/runs.ts +313 -35
@@ -38,6 +38,7 @@ import type { CollapsedTable } from './collapse.js';
38
38
  import { quoteAt } from '../css/content.js';
39
39
  import type { CounterStyles } from '../css/counter-styles.js';
40
40
  import type { ContentItem } from '../css/content.js';
41
+ import type { ShapeStyles } from '../css/shapes.js';
41
42
  import {
42
43
  CONTAIN_STYLE,
43
44
  FIRST_LINE_INHERITED,
@@ -202,6 +203,17 @@ export interface LineText {
202
203
  * the ones before a forced break that fit take room on the line.
203
204
  */
204
205
  hung?: { at: number; x: number; width: number }[];
206
+ /**
207
+ * How far the line goes on past this text for the collapsible spaces it
208
+ * ends on, which its engine, laying out a piece of a line as a line,
209
+ * stripped: the room they take where more follows them on the line, and
210
+ * none where the line ends on them, since CSS removes them there (CSS
211
+ * Text 3, 4.1.2). They are the text's, in the elements they are in, and
212
+ * `trailRun` is the run they are the end of: whose underline, line
213
+ * through and highlight go on under them.
214
+ */
215
+ trail?: number;
216
+ trailRun?: TextRun;
205
217
  /** Any offset in the layout's own text as a document index — how a run
206
218
  * under the pointer finds the element whose text it is. Per pass: the
207
219
  * layout may be one an earlier pass made (`TextLayoutCache`), and the
@@ -630,12 +642,16 @@ export const CLAMPED = new WeakSet<Box>();
630
642
  /** A grid's tracks as its layout left them, each column's and each row's
631
643
  * start and end from the content box's corner, with the lines of each
632
644
  * axis by number and by name: what an absolutely positioned box's grid
633
- * area is found in (CSS Grid 1, 9.1). */
645
+ * area is found in (CSS Grid 1, 9.1). And the first of its items in
646
+ * row-major order, where they were placed and not where they were
647
+ * written, which the grid's own baseline is taken from (10.6): null for a
648
+ * grid with no item. */
634
649
  export interface GridTracks {
635
650
  cols: [number, number][];
636
651
  rows: [number, number][];
637
652
  colLines: GridLines;
638
653
  rowLines: GridLines;
654
+ first: Box | null;
639
655
  }
640
656
  export const GRID_TRACKS = new WeakMap<Box, GridTracks>();
641
657
 
@@ -656,6 +672,57 @@ export const CUT_BLOCKS = new WeakMap<Box, Box[]>();
656
672
  * the clearance in (`elementRect`). Only the blocks that had clearance. */
657
673
  export const CLEARED_FROM = new WeakMap<Box, number>();
658
674
 
675
+ /**
676
+ * One piece of a block that a column break falls inside (CSS Multi-column
677
+ * 1): its border box in one column, as far as the box goes in it, and where
678
+ * the whole box would stand there — the box its background and its borders
679
+ * are drawn as, cut to the piece (`box-decoration-break: slice`).
680
+ */
681
+ export interface ColumnPiece {
682
+ x: number;
683
+ y: number;
684
+ width: number;
685
+ height: number;
686
+ /** The top of the whole box, in this column, and its height. */
687
+ wholeY: number;
688
+ wholeHeight: number;
689
+ }
690
+
691
+ /** The pieces of each block a column break falls inside, in the order of
692
+ * the columns. The block's own rect is what takes them all in, as a
693
+ * browser's bounding rect of it is. */
694
+ export const COLUMN_PIECES = new WeakMap<Box, ColumnPiece[]>();
695
+
696
+ /** The lines of a box that columns took apart: they are not one under
697
+ * another, and a search that counts on their tops rising reads them all
698
+ * instead. */
699
+ export const COLUMN_LINES = new WeakSet<LineBox[]>();
700
+
701
+ /**
702
+ * The rows of a text's layout that are in the text's own column, from the
703
+ * layout's origin, where the layout's lines went to more than one: a layout
704
+ * is drawn whole, so it is drawn once for each column, there, and clipped
705
+ * to the rows that are that column's. One object for all the texts of a
706
+ * layout in a column, which is what tells a draw of it from another.
707
+ */
708
+ export const COLUMN_ROWS = new WeakMap<
709
+ LineText,
710
+ { top: number; bottom: number }
711
+ >();
712
+
713
+ /** Whether any layout has broken a box between columns: what the walks
714
+ * that would ask the three maps above of every box ask first. */
715
+ export const columned = { any: false };
716
+
717
+ /** What the document's rules give the elements inside an inline `<svg>`
718
+ * (`Cascade.shapeStyles`), null where they give none. */
719
+ export type ShapeStyler = (box: Box) => ShapeStyles | null;
720
+
721
+ /** What `BoxTree.shapeStyler` answered for each drawing's box it has been
722
+ * asked of, which are the ones painted: a box restyled where it is has
723
+ * its answer put here again (`HtmlViewNode._hoverInPlace`). */
724
+ export const SHAPE_STYLES = new WeakMap<Box, ShapeStyles | null>();
725
+
659
726
  /** The element a pseudo-element's box is generated from, which has no
660
727
  * element of its own (`Box.pseudo`): whose the images its styles name are,
661
728
  * when the host is asked for them. */
@@ -717,6 +784,11 @@ export interface BoxTree {
717
784
  * one: worked out as a selection is first painted over it, not as the
718
785
  * tree is built, since most builds are never selected in. */
719
786
  selectionStyler: SelectionStyler | null;
787
+ /** What the rules give the elements in each inline `<svg>`
788
+ * (`ShapeStyler`), which have no boxes and no styles, where any rule
789
+ * could reach one: worked out as a drawing is first painted, since a
790
+ * page of icons shows few of them at once. */
791
+ shapeStyler: ShapeStyler | null;
720
792
  /** Whether a float or an out-of-flow box sits in an inline box: where
721
793
  * none does, layout looks for them among a block's own children and
722
794
  * goes through no inline box to find them. */
@@ -733,6 +805,10 @@ export interface BoxTree {
733
805
  * (`Cascade.rootStyle`), whose background is the canvas's; null where
734
806
  * the document has either. */
735
807
  impliedHtml: ComputedStyle | null;
808
+ /** The scheme the palette is, which the element the document is drawn
809
+ * in is of: where the root element's is the other, the canvas is that
810
+ * scheme's and opaque (`paintDocument`). */
811
+ paletteScheme: 'light' | 'dark';
736
812
  }
737
813
 
738
814
  export interface BuildOptions {
@@ -878,10 +954,12 @@ class Builder {
878
954
  firstLine: this._firstLine,
879
955
  firstLineStyler: this._firstLine ? this._firstLineStyler() : null,
880
956
  selectionStyler: cascade.hasSelection ? this._selectionStyler() : null,
957
+ shapeStyler: cascade.stylesShapes ? this._shapeStyler() : null,
881
958
  nestedOutOfLine: this._nestedOutOfLine,
882
959
  movedInline: this._movedInline,
883
960
  clipText: this._clipText,
884
961
  impliedHtml,
962
+ paletteScheme: cascade.initial.colorScheme,
885
963
  };
886
964
  }
887
965
 
@@ -909,6 +987,20 @@ class Builder {
909
987
  };
910
988
  }
911
989
 
990
+ /** `BoxTree.shapeStyler`, over this build's cascade: each drawing's
991
+ * once, from its box's style as it is when first asked. */
992
+ private _shapeStyler(): ShapeStyler {
993
+ const cascade = this._options.cascade;
994
+ return (box) => {
995
+ let shapes = SHAPE_STYLES.get(box);
996
+ if (shapes === undefined) {
997
+ shapes = box.el ? cascade.shapeStyles(box.el, box.style) : null;
998
+ SHAPE_STYLES.set(box, shapes);
999
+ }
1000
+ return shapes;
1001
+ };
1002
+ }
1003
+
912
1004
  /** `BoxTree.firstLineStyler`, over this build's cascade. */
913
1005
  private _firstLineStyler(): FirstLineStyler {
914
1006
  const cascade = this._options.cascade;
@@ -1043,7 +1135,7 @@ class Builder {
1043
1135
  }
1044
1136
  }
1045
1137
  // a translation is moved by the same pass
1046
- if (style.translate || style.transformTranslate) this._relative = true;
1138
+ if (style.translate || style.transform) this._relative = true;
1047
1139
  if (style.backgroundClipText) this._clipText = true;
1048
1140
  if (style.verticalAlign !== 'baseline' && style.display === 'inline') {
1049
1141
  this._movedInline = true;
@@ -1348,7 +1440,7 @@ class Builder {
1348
1440
  }
1349
1441
  }
1350
1442
  // a translation is moved by the same pass
1351
- if (style.translate || style.transformTranslate) this._relative = true;
1443
+ if (style.translate || style.transform) this._relative = true;
1352
1444
  if (style.backgroundClipText) this._clipText = true;
1353
1445
  if (style.verticalAlign !== 'baseline' && style.display === 'inline') {
1354
1446
  this._movedInline = true;
@@ -186,6 +186,8 @@ export const RUN_FIELDS = [
186
186
  'underlineOffset',
187
187
  'underlineThickness',
188
188
  'strike',
189
+ 'strikeStyle',
190
+ 'strikeThickness',
189
191
  'href',
190
192
  'features',
191
193
  'nowrap',
@@ -221,6 +223,8 @@ function sameRun(a: TextRun, b: TextRun): boolean {
221
223
  a.underlineOffset === b.underlineOffset &&
222
224
  a.underlineThickness === b.underlineThickness &&
223
225
  a.strike === b.strike &&
226
+ a.strikeStyle === b.strikeStyle &&
227
+ a.strikeThickness === b.strikeThickness &&
224
228
  a.href === b.href &&
225
229
  a.features === b.features &&
226
230
  a.nowrap === b.nowrap &&
@@ -249,6 +253,8 @@ function sameStyle(
249
253
  a.underlineOffset === b.underlineOffset &&
250
254
  a.underlineThickness === b.underlineThickness &&
251
255
  a.strike === b.strike &&
256
+ a.strikeStyle === b.strikeStyle &&
257
+ a.strikeThickness === b.strikeThickness &&
252
258
  a.href === b.href &&
253
259
  a.features === b.features &&
254
260
  a.nowrap === b.nowrap &&
@@ -31,6 +31,9 @@ import { Box, GRID_TRACKS, isBlank } from './boxes.js';
31
31
  import {
32
32
  FLEXED_HEIGHT,
33
33
  MIN_CONTENT_PROBE,
34
+ STRETCHED_ACROSS,
35
+ USED_HEIGHT,
36
+ centreButton,
34
37
  clampHeight,
35
38
  clampWidth,
36
39
  exactMinContent,
@@ -58,6 +61,28 @@ interface Item {
58
61
  /** Min-content and max-content widths, margin box, taken when asked. */
59
62
  min: number;
60
63
  max: number;
64
+ /** Whether it is a table stretched across its area (`STRETCHED_ACROSS`),
65
+ * which `layoutItem` decides. */
66
+ across: boolean;
67
+ }
68
+
69
+ /**
70
+ * The first of a grid's items in row-major order: the one furthest to the
71
+ * start of the first row that holds an item, which the grid's baseline is
72
+ * taken from (CSS Grid 1, 10.6).
73
+ */
74
+ function firstItem(items: Item[]): Box | null {
75
+ let first: Item | null = null;
76
+ for (const item of items) {
77
+ if (
78
+ first === null ||
79
+ item.row < first.row ||
80
+ (item.row === first.row && item.col < first.col)
81
+ ) {
82
+ first = item;
83
+ }
84
+ }
85
+ return first?.box ?? null;
61
86
  }
62
87
 
63
88
  /**
@@ -78,7 +103,13 @@ export function layoutGrid(
78
103
  positionOutOfFlow(child, box, ctx, false);
79
104
  continue;
80
105
  }
81
- resolveEdges(child, Number.isFinite(contentWidth) ? contentWidth : 0);
106
+ // While the columns are sized, the area an item is in has no width for
107
+ // the percentages in its margins and its padding to be of, and they
108
+ // are of nothing (CSS Sizing 3, 5.2.1): a column is as wide as what
109
+ // is in the item. They are of the area once it has a width
110
+ // (`layoutItem`). Taken of the grid's width, an item `margin: 0 25%`
111
+ // made its `min-content` column half the grid wide.
112
+ resolveEdges(child, 0);
82
113
  boxes.push(child);
83
114
  }
84
115
  // the gaps: a percentage of the grid's content size along them, and of
@@ -176,6 +207,11 @@ export function layoutGrid(
176
207
  const extra = style.boxSizing === 'border-box' ? 0 : box.horizontalExtra;
177
208
  widest = narrowest = Math.max(style.width + extra, box.horizontalExtra);
178
209
  } else {
210
+ // A percentage is of an area the columns are still being sized for,
211
+ // and what it comes to depends on what the item makes them: it is
212
+ // `auto` for that (CSS Sizing 3, 5.2.1), and the item's content says
213
+ // how wide it is. Measured at the width a percentage made of the
214
+ // probe's, an item `width: 100%` made its `auto` column nothing.
179
215
  const fresh = box.intrinsicMaxContent < 0;
180
216
  widest = maxContentOf(box, ctx);
181
217
  if (box.intrinsicMinContent < 0) {
@@ -183,8 +219,7 @@ export function layoutGrid(
183
219
  // which is the max-content one only if it was made just now
184
220
  const exact =
185
221
  fresh && ctx.fonts ? exactMinContent(box, ctx.fonts) : null;
186
- box.intrinsicMinContent =
187
- exact ?? measureIntrinsicWidth(box, ctx, MIN_CONTENT_PROBE);
222
+ box.intrinsicMinContent = exact ?? minContentOf(box, ctx);
188
223
  }
189
224
  narrowest = box.intrinsicMinContent;
190
225
  }
@@ -240,14 +275,15 @@ export function layoutGrid(
240
275
  item,
241
276
  extent(widths, lefts, item.col, item.cols),
242
277
  ctx,
243
- heightBefore(item),
278
+ spanBefore(item),
244
279
  );
245
280
  }
246
281
 
247
282
  // a height of the grid's own, or else the least it may be, which the
248
- // `fr` rows and the `auto` ones fill
283
+ // `fr` rows and the `auto` ones fill: its `min-height`, or the height a
284
+ // column it is an item of flexed it to (`USED_HEIGHT`)
249
285
  const own = Number.isFinite(definite);
250
- const least = own ? 0 : leastHeight(box);
286
+ const least = own ? 0 : Math.max(leastHeight(box), USED_HEIGHT.get(box) ?? 0);
251
287
  // the rows against a height: percentages of it, or `auto` without one
252
288
  const sizeRows = (base: number, available: number, atLeast: number) => {
253
289
  const rowTracks: SizingTrack[] = [];
@@ -301,18 +337,14 @@ export function layoutGrid(
301
337
  if (Object.is(child.percentHeightBase, tall) || !percentOwn(child))
302
338
  continue;
303
339
  child.percentHeightBase = tall;
304
- layoutItem(
305
- item,
306
- extent(widths, lefts, item.col, item.cols),
307
- ctx,
308
- heightBeforeRows(child, tall),
309
- );
340
+ layoutItem(item, extent(widths, lefts, item.col, item.cols), ctx, tall);
310
341
  }
311
342
  GRID_TRACKS.set(box, {
312
343
  cols: tracksOf(widths, lefts, keepCols),
313
344
  rows: tracksOf(heights, tops, keepRows),
314
345
  colLines,
315
346
  rowLines,
347
+ first: firstItem(items),
316
348
  });
317
349
 
318
350
  for (const item of items) {
@@ -371,20 +403,34 @@ export function layoutGrid(
371
403
  const width =
372
404
  wide === null ? child.width : clampWidth(child, wide, area, ctx);
373
405
  // what is in it takes its percentages of that height, which a
374
- // stretch makes definite, as it does a flex item's
406
+ // stretch makes definite, as it does a flex item's — and an item that
407
+ // is a flex or grid container lays its own items out in it (11.1:
408
+ // "the grid area's width and height are considered definite for this
409
+ // purpose"): a card's `margin-top: auto` takes what its row is taller
410
+ // than the card by, its `flex: 1` grows into it (CSS Flexbox 8.1,
411
+ // 9.7). Its content's own height is the same layout, and kept.
412
+ // A table shares out among its rows what the area is taller than
413
+ // they are (`layoutTable`), and is no shorter than they are however
414
+ // short its area: its rows are the least it can be, as they are
415
+ // where it has a height of its own (CSS 2.1 17.5.3).
375
416
  if (
376
417
  child.kind !== 'replaced' &&
377
- (wide !== null || percentHeightsIn(child))
418
+ (wide !== null ||
419
+ percentHeightsIn(child) ||
420
+ (child.kind === 'flex' && height !== child.height) ||
421
+ (child.kind === 'table' && height > child.height + 0.01))
378
422
  ) {
379
423
  FLEXED_HEIGHT.set(child, Math.max(0, height - child.verticalExtra));
380
424
  try {
381
- ctx.layoutSubtree(child, width);
425
+ layoutAcross(item, width, area, ctx);
382
426
  } finally {
383
427
  FLEXED_HEIGHT.delete(child);
384
428
  }
385
429
  }
386
430
  child.width = width;
387
- child.height = height;
431
+ child.height =
432
+ child.kind === 'table' ? Math.max(height, child.height) : height;
433
+ centreButton(child);
388
434
  }
389
435
  moveTo(
390
436
  child,
@@ -564,6 +610,7 @@ function place(
564
610
  cols: p.bs,
565
611
  min: -1,
566
612
  max: -1,
613
+ across: false,
567
614
  }
568
615
  : {
569
616
  box: p.box,
@@ -573,6 +620,7 @@ function place(
573
620
  cols: p.as,
574
621
  min: -1,
575
622
  max: -1,
623
+ across: false,
576
624
  },
577
625
  );
578
626
  }
@@ -609,23 +657,56 @@ function extent(
609
657
  }
610
658
 
611
659
  /** An item's max-content width, its border box's: measured once in the
612
- * box's life, as a table cell's is (`Box.intrinsicMaxContent`). */
660
+ * box's life, as a table cell's is (`Box.intrinsicMaxContent`). Its
661
+ * content's, whatever width it has of its own (`minContentOf`). */
613
662
  function maxContentOf(box: Box, ctx: LayoutContext): number {
614
663
  if (box.intrinsicMaxContent < 0) {
615
- box.intrinsicMaxContent = measureIntrinsicWidth(box, ctx, Infinity);
664
+ box.intrinsicMaxContent = measureIntrinsicWidth(
665
+ box,
666
+ ctx,
667
+ Infinity,
668
+ box.kind !== 'replaced',
669
+ );
616
670
  }
617
671
  return box.intrinsicMaxContent;
618
672
  }
619
673
 
620
- /** Lay an item out in its area: stretched across it where its width is
621
- * `auto` and it is stretched, or at its content's width, no wider than
622
- * the area, where it is aligned instead. */
674
+ /**
675
+ * An item's min-content width, its border box's. What it is measured for is
676
+ * what it contributes to its columns, and the width a percentage makes of
677
+ * them is not known then: a box that is not replaced is as wide as its
678
+ * content for that, whatever its own `width` says, and a replaced element
679
+ * takes its percentage of nothing (CSS Sizing 3, 5.2.1). A width that is a
680
+ * length is weighed apart (`measure` in `layoutGrid`).
681
+ */
682
+ function minContentOf(box: Box, ctx: LayoutContext): number {
683
+ return measureIntrinsicWidth(
684
+ box,
685
+ ctx,
686
+ MIN_CONTENT_PROBE,
687
+ box.kind !== 'replaced',
688
+ );
689
+ }
690
+
691
+ /**
692
+ * Lay an item out in its area: stretched across it where its width is
693
+ * `auto` and it is stretched, or at its content's width, no wider than
694
+ * the area, where it is aligned instead.
695
+ *
696
+ * The area is the item's containing block (CSS Grid 1, 3.3), so it is its
697
+ * width that a percentage in the item's margins, its padding, its
698
+ * `min-width` and its `max-width` is of (6.4, and CSS 2.1 8.3, 8.4 and
699
+ * 10.4), whatever width the item comes to in it. With its own width
700
+ * standing for its area's, an item `width: 50%; max-width: 80%` was four
701
+ * tenths of its column, and one `min-width: 50%` aligned to the start was
702
+ * as wide as its text.
703
+ */
623
704
  function layoutItem(
624
705
  item: Item,
625
706
  area: number,
626
707
  ctx: LayoutContext,
627
- /** Its border box's height before the rows are sized, where it has one. */
628
- tall: number | null,
708
+ /** The height of the rows it spans, where they have one yet. */
709
+ rows: number,
629
710
  ): void {
630
711
  const child = item.box;
631
712
  const style = child.style;
@@ -634,19 +715,31 @@ function layoutItem(
634
715
  style.justifySelf === 'auto'
635
716
  ? (parent?.justifyItems ?? 'normal')
636
717
  : style.justifySelf;
718
+ resolveEdges(child, area);
719
+ // its border box's height before the rows are sized, where it has one
720
+ const tall = heightBeforeRows(child, rows);
637
721
  const margins = child.marginLeft + child.marginRight;
638
722
  const room = Math.max(0, area - margins);
639
- // its content's widths, border box: measured once in its life
723
+ // Its content's widths, border box: measured once in its life, with the
724
+ // percentages in its padding of nothing, as they are while the columns
725
+ // are sized, and here with what those come to of its area. Measuring
726
+ // resolves its edges against nothing, and they are put back.
727
+ const padded = percentPadding(child);
640
728
  const content = (): [number, number] => {
641
- if (child.intrinsicMaxContent < 0) maxContentOf(child, ctx);
729
+ let probed = false;
730
+ if (child.intrinsicMaxContent < 0) {
731
+ maxContentOf(child, ctx);
732
+ probed = true;
733
+ }
642
734
  if (child.intrinsicMinContent < 0) {
643
- child.intrinsicMinContent = measureIntrinsicWidth(
644
- child,
645
- ctx,
646
- MIN_CONTENT_PROBE,
647
- );
735
+ child.intrinsicMinContent = minContentOf(child, ctx);
736
+ probed = true;
648
737
  }
649
- return [child.intrinsicMinContent, child.intrinsicMaxContent];
738
+ if (probed) resolveEdges(child, area);
739
+ return [
740
+ child.intrinsicMinContent + padded,
741
+ child.intrinsicMaxContent + padded,
742
+ ];
650
743
  };
651
744
  const keyword = style.widthKeyword;
652
745
  // `stretch` fills its area, and so does `normal` but for a replaced
@@ -699,7 +792,15 @@ function layoutItem(
699
792
  width = Math.min(widest, Math.max(narrowest, room));
700
793
  }
701
794
  if (child.kind !== 'replaced') {
702
- ctx.layoutSubtree(child, width);
795
+ // a table stretched across is as wide as its area, which in a block's
796
+ // flow it would shrink from to its columns (`STRETCHED_ACROSS`)
797
+ item.across =
798
+ across &&
799
+ child.kind === 'table' &&
800
+ style.width === AUTO &&
801
+ !keyword &&
802
+ through === null;
803
+ layoutAcross(item, width, area, ctx);
703
804
  return;
704
805
  }
705
806
  // a replaced element sizes itself from its style: then it is given the
@@ -718,6 +819,42 @@ function layoutItem(
718
819
  }
719
820
  }
720
821
 
822
+ /** Lay an item out at a width: a table stretched across its area at that
823
+ * width as its own (`STRETCHED_ACROSS`), for this layout alone. */
824
+ function layoutAcross(
825
+ item: Item,
826
+ width: number,
827
+ area: number,
828
+ ctx: LayoutContext,
829
+ ): void {
830
+ const child = item.box;
831
+ if (!item.across) {
832
+ ctx.layoutSubtree(child, width, area);
833
+ return;
834
+ }
835
+ STRETCHED_ACROSS.add(child);
836
+ try {
837
+ ctx.layoutSubtree(child, width, area);
838
+ } finally {
839
+ STRETCHED_ACROSS.delete(child);
840
+ }
841
+ }
842
+
843
+ /** What the percentages in a box's padding come to across it, its edges
844
+ * resolved: what a width of its content's, measured with them of nothing,
845
+ * is short of its border box by. */
846
+ function percentPadding(box: Box): number {
847
+ const { paddingLeft, paddingRight } = box.style;
848
+ let out = 0;
849
+ if (isPct(paddingLeft)) {
850
+ out += box.padLeft - Math.max(0, resolve(paddingLeft, 0));
851
+ }
852
+ if (isPct(paddingRight)) {
853
+ out += box.padRight - Math.max(0, resolve(paddingRight, 0));
854
+ }
855
+ return out;
856
+ }
857
+
721
858
  /** Whether a box's content is its least size through a ratio, which it is
722
859
  * where the box shows what overflows it and its least width is `auto`
723
860
  * (CSS Sizing 4, 5.2). */