@react-x11/components 0.12.0 → 0.14.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.
Files changed (150) hide show
  1. package/dist/html/controls.d.ts +41 -5
  2. package/dist/html/controls.d.ts.map +1 -1
  3. package/dist/html/controls.js +85 -57
  4. package/dist/html/controls.js.map +1 -1
  5. package/dist/html/css/cascade.d.ts +134 -20
  6. package/dist/html/css/cascade.d.ts.map +1 -1
  7. package/dist/html/css/cascade.js +545 -126
  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 +47 -6
  14. package/dist/html/css/parse.d.ts.map +1 -1
  15. package/dist/html/css/parse.js +336 -30
  16. package/dist/html/css/parse.js.map +1 -1
  17. package/dist/html/css/style.d.ts +142 -15
  18. package/dist/html/css/style.d.ts.map +1 -1
  19. package/dist/html/css/style.js +563 -147
  20. package/dist/html/css/style.js.map +1 -1
  21. package/dist/html/css/transform.d.ts +85 -0
  22. package/dist/html/css/transform.d.ts.map +1 -0
  23. package/dist/html/css/transform.js +425 -0
  24. package/dist/html/css/transform.js.map +1 -0
  25. package/dist/html/css/ua.d.ts +11 -1
  26. package/dist/html/css/ua.d.ts.map +1 -1
  27. package/dist/html/css/ua.js +73 -9
  28. package/dist/html/css/ua.js.map +1 -1
  29. package/dist/html/css/values.d.ts +4 -0
  30. package/dist/html/css/values.d.ts.map +1 -1
  31. package/dist/html/css/values.js.map +1 -1
  32. package/dist/html/dom.d.ts.map +1 -1
  33. package/dist/html/dom.js +3 -1
  34. package/dist/html/dom.js.map +1 -1
  35. package/dist/html/fonts.d.ts +78 -4
  36. package/dist/html/fonts.d.ts.map +1 -1
  37. package/dist/html/fonts.js +373 -19
  38. package/dist/html/fonts.js.map +1 -1
  39. package/dist/html/form.d.ts +200 -0
  40. package/dist/html/form.d.ts.map +1 -0
  41. package/dist/html/form.js +801 -0
  42. package/dist/html/form.js.map +1 -0
  43. package/dist/html/index.d.ts +12 -0
  44. package/dist/html/index.d.ts.map +1 -1
  45. package/dist/html/index.js +30 -301
  46. package/dist/html/index.js.map +1 -1
  47. package/dist/html/layout/axes.d.ts +14 -0
  48. package/dist/html/layout/axes.d.ts.map +1 -0
  49. package/dist/html/layout/axes.js +28 -0
  50. package/dist/html/layout/axes.js.map +1 -0
  51. package/dist/html/layout/block.d.ts +93 -12
  52. package/dist/html/layout/block.d.ts.map +1 -1
  53. package/dist/html/layout/block.js +393 -39
  54. package/dist/html/layout/block.js.map +1 -1
  55. package/dist/html/layout/boxes.d.ts +90 -2
  56. package/dist/html/layout/boxes.d.ts.map +1 -1
  57. package/dist/html/layout/boxes.js +74 -10
  58. package/dist/html/layout/boxes.js.map +1 -1
  59. package/dist/html/layout/cache.d.ts +1 -1
  60. package/dist/html/layout/cache.d.ts.map +1 -1
  61. package/dist/html/layout/cache.js +6 -0
  62. package/dist/html/layout/cache.js.map +1 -1
  63. package/dist/html/layout/css-grid.d.ts.map +1 -1
  64. package/dist/html/layout/css-grid.js +90 -18
  65. package/dist/html/layout/css-grid.js.map +1 -1
  66. package/dist/html/layout/flex.d.ts.map +1 -1
  67. package/dist/html/layout/flex.js +574 -84
  68. package/dist/html/layout/flex.js.map +1 -1
  69. package/dist/html/layout/inline.d.ts +3 -0
  70. package/dist/html/layout/inline.d.ts.map +1 -1
  71. package/dist/html/layout/inline.js +154 -54
  72. package/dist/html/layout/inline.js.map +1 -1
  73. package/dist/html/layout/multicol.d.ts +42 -0
  74. package/dist/html/layout/multicol.d.ts.map +1 -0
  75. package/dist/html/layout/multicol.js +897 -0
  76. package/dist/html/layout/multicol.js.map +1 -0
  77. package/dist/html/layout/shaping.d.ts +7 -0
  78. package/dist/html/layout/shaping.d.ts.map +1 -1
  79. package/dist/html/layout/shaping.js +25 -20
  80. package/dist/html/layout/shaping.js.map +1 -1
  81. package/dist/html/layout/table.d.ts.map +1 -1
  82. package/dist/html/layout/table.js +62 -18
  83. package/dist/html/layout/table.js.map +1 -1
  84. package/dist/html/node.d.ts +90 -15
  85. package/dist/html/node.d.ts.map +1 -1
  86. package/dist/html/node.js +771 -186
  87. package/dist/html/node.js.map +1 -1
  88. package/dist/html/paint.d.ts +101 -5
  89. package/dist/html/paint.d.ts.map +1 -1
  90. package/dist/html/paint.js +1502 -160
  91. package/dist/html/paint.js.map +1 -1
  92. package/dist/html/svg.d.ts +15 -2
  93. package/dist/html/svg.d.ts.map +1 -1
  94. package/dist/html/svg.js +249 -17
  95. package/dist/html/svg.js.map +1 -1
  96. package/dist/html/widgets.d.ts +29 -0
  97. package/dist/html/widgets.d.ts.map +1 -0
  98. package/dist/html/widgets.js +700 -0
  99. package/dist/html/widgets.js.map +1 -0
  100. package/dist/html/woff2.d.ts +14 -0
  101. package/dist/html/woff2.d.ts.map +1 -0
  102. package/dist/html/woff2.js +608 -0
  103. package/dist/html/woff2.js.map +1 -0
  104. package/dist/index.d.ts +1 -1
  105. package/dist/index.d.ts.map +1 -1
  106. package/dist/index.js.map +1 -1
  107. package/dist/maps/controller.d.ts +17 -1
  108. package/dist/maps/controller.d.ts.map +1 -1
  109. package/dist/maps/controller.js +27 -7
  110. package/dist/maps/controller.js.map +1 -1
  111. package/dist/richtext/node.d.ts +8 -1
  112. package/dist/richtext/node.d.ts.map +1 -1
  113. package/dist/richtext/node.js +5 -1
  114. package/dist/richtext/node.js.map +1 -1
  115. package/dist/richtext/runs.d.ts +25 -6
  116. package/dist/richtext/runs.d.ts.map +1 -1
  117. package/dist/richtext/runs.js +240 -24
  118. package/dist/richtext/runs.js.map +1 -1
  119. package/package.json +2 -2
  120. package/src/html/controls.ts +114 -51
  121. package/src/html/css/cascade.ts +614 -140
  122. package/src/html/css/color.ts +29 -0
  123. package/src/html/css/parse.ts +388 -33
  124. package/src/html/css/style.ts +688 -150
  125. package/src/html/css/transform.ts +459 -0
  126. package/src/html/css/ua.ts +77 -10
  127. package/src/html/css/values.ts +4 -0
  128. package/src/html/dom.ts +3 -1
  129. package/src/html/fonts.ts +417 -24
  130. package/src/html/form.ts +962 -0
  131. package/src/html/index.ts +49 -338
  132. package/src/html/layout/axes.ts +62 -0
  133. package/src/html/layout/block.ts +447 -37
  134. package/src/html/layout/boxes.ts +143 -14
  135. package/src/html/layout/cache.ts +6 -0
  136. package/src/html/layout/css-grid.ts +95 -22
  137. package/src/html/layout/flex.ts +675 -78
  138. package/src/html/layout/inline.ts +159 -55
  139. package/src/html/layout/multicol.ts +977 -0
  140. package/src/html/layout/shaping.ts +27 -18
  141. package/src/html/layout/table.ts +62 -19
  142. package/src/html/node.ts +798 -172
  143. package/src/html/paint.ts +1742 -241
  144. package/src/html/svg.ts +277 -19
  145. package/src/html/widgets.ts +821 -0
  146. package/src/html/woff2.ts +612 -0
  147. package/src/index.ts +1 -0
  148. package/src/maps/controller.ts +28 -7
  149. package/src/richtext/node.ts +13 -2
  150. package/src/richtext/runs.ts +313 -35
@@ -28,7 +28,12 @@ import {
28
28
  NON_RENDERED,
29
29
  tagOf,
30
30
  } from '../dom.js';
31
- import type { Cascade, FirstLetterRules } from '../css/cascade.js';
31
+ import type {
32
+ Cascade,
33
+ FirstLetterRules,
34
+ KeptStyles,
35
+ SelectionStyle,
36
+ } from '../css/cascade.js';
32
37
  import type { CollapsedTable } from './collapse.js';
33
38
  import { quoteAt } from '../css/content.js';
34
39
  import type { CounterStyles } from '../css/counter-styles.js';
@@ -197,6 +202,17 @@ export interface LineText {
197
202
  * the ones before a forced break that fit take room on the line.
198
203
  */
199
204
  hung?: { at: number; x: number; width: number }[];
205
+ /**
206
+ * How far the line goes on past this text for the collapsible spaces it
207
+ * ends on, which its engine, laying out a piece of a line as a line,
208
+ * stripped: the room they take where more follows them on the line, and
209
+ * none where the line ends on them, since CSS removes them there (CSS
210
+ * Text 3, 4.1.2). They are the text's, in the elements they are in, and
211
+ * `trailRun` is the run they are the end of: whose underline, line
212
+ * through and highlight go on under them.
213
+ */
214
+ trail?: number;
215
+ trailRun?: TextRun;
200
216
  /** Any offset in the layout's own text as a document index — how a run
201
217
  * under the pointer finds the element whose text it is. Per pass: the
202
218
  * layout may be one an earlier pass made (`TextLayoutCache`), and the
@@ -556,6 +572,10 @@ export type FirstLineStyler = (
556
572
  parent: ComputedStyle,
557
573
  ) => ComputedStyle;
558
574
 
575
+ /** The `::selection` of the element a box's text is in, or null for the
576
+ * palette's highlight (`Cascade.selectionStyle`). */
577
+ export type SelectionStyler = (box: Box | null) => SelectionStyle | null;
578
+
559
579
  /** The url of an image generated content put in a pseudo-element: it has
560
580
  * no element of its own to name it (`imageUrlOf`). */
561
581
  export const CONTENT_IMAGES = new WeakMap<Box, string>();
@@ -621,12 +641,16 @@ export const CLAMPED = new WeakSet<Box>();
621
641
  /** A grid's tracks as its layout left them, each column's and each row's
622
642
  * start and end from the content box's corner, with the lines of each
623
643
  * axis by number and by name: what an absolutely positioned box's grid
624
- * area is found in (CSS Grid 1, 9.1). */
644
+ * area is found in (CSS Grid 1, 9.1). And the first of its items in
645
+ * row-major order, where they were placed and not where they were
646
+ * written, which the grid's own baseline is taken from (10.6): null for a
647
+ * grid with no item. */
625
648
  export interface GridTracks {
626
649
  cols: [number, number][];
627
650
  rows: [number, number][];
628
651
  colLines: GridLines;
629
652
  rowLines: GridLines;
653
+ first: Box | null;
630
654
  }
631
655
  export const GRID_TRACKS = new WeakMap<Box, GridTracks>();
632
656
 
@@ -641,6 +665,54 @@ export const PAINT_ORDER = new WeakMap<Box, Box[]>();
641
665
  * a browser's does (`elementRect`). */
642
666
  export const CUT_BLOCKS = new WeakMap<Box, Box[]>();
643
667
 
668
+ /** Where a block that clearance moved below the floats would have stood
669
+ * without it, its border edge's top (CSS 2.1 9.5.2): a browser's
670
+ * fragment of an inline box around such a block starts there, and takes
671
+ * the clearance in (`elementRect`). Only the blocks that had clearance. */
672
+ export const CLEARED_FROM = new WeakMap<Box, number>();
673
+
674
+ /**
675
+ * One piece of a block that a column break falls inside (CSS Multi-column
676
+ * 1): its border box in one column, as far as the box goes in it, and where
677
+ * the whole box would stand there — the box its background and its borders
678
+ * are drawn as, cut to the piece (`box-decoration-break: slice`).
679
+ */
680
+ export interface ColumnPiece {
681
+ x: number;
682
+ y: number;
683
+ width: number;
684
+ height: number;
685
+ /** The top of the whole box, in this column, and its height. */
686
+ wholeY: number;
687
+ wholeHeight: number;
688
+ }
689
+
690
+ /** The pieces of each block a column break falls inside, in the order of
691
+ * the columns. The block's own rect is what takes them all in, as a
692
+ * browser's bounding rect of it is. */
693
+ export const COLUMN_PIECES = new WeakMap<Box, ColumnPiece[]>();
694
+
695
+ /** The lines of a box that columns took apart: they are not one under
696
+ * another, and a search that counts on their tops rising reads them all
697
+ * instead. */
698
+ export const COLUMN_LINES = new WeakSet<LineBox[]>();
699
+
700
+ /**
701
+ * The rows of a text's layout that are in the text's own column, from the
702
+ * layout's origin, where the layout's lines went to more than one: a layout
703
+ * is drawn whole, so it is drawn once for each column, there, and clipped
704
+ * to the rows that are that column's. One object for all the texts of a
705
+ * layout in a column, which is what tells a draw of it from another.
706
+ */
707
+ export const COLUMN_ROWS = new WeakMap<
708
+ LineText,
709
+ { top: number; bottom: number }
710
+ >();
711
+
712
+ /** Whether any layout has broken a box between columns: what the walks
713
+ * that would ask the three maps above of every box ask first. */
714
+ export const columned = { any: false };
715
+
644
716
  /** The element a pseudo-element's box is generated from, which has no
645
717
  * element of its own (`Box.pseudo`): whose the images its styles name are,
646
718
  * when the host is asked for them. */
@@ -673,6 +745,10 @@ export interface BoxTree {
673
745
  controls: Box[];
674
746
  /** Every box carrying an `href`, for click and hover. */
675
747
  links: Box[];
748
+ /** The style of an anonymous box under a parent's (`anonymousStyles`):
749
+ * the one the fix-up gave each it made, and what such a box takes when
750
+ * its parent's style changes where it is (`HtmlViewNode._hoverInPlace`). */
751
+ anonymous: AnonymousStyle;
676
752
  /** Every box with a `background-image`, a border image or a mask image,
677
753
  * an element's or a pseudo-element's, for the host to be asked for: a
678
754
  * document has a handful, and finding them was a walk over every box
@@ -694,6 +770,10 @@ export interface BoxTree {
694
770
  /** Each box's style on a first line (`FirstLineStyler`), where any
695
771
  * block has a `::first-line`. */
696
772
  firstLineStyler: FirstLineStyler | null;
773
+ /** Each box's `::selection` (`SelectionStyler`), where any rule styles
774
+ * one: worked out as a selection is first painted over it, not as the
775
+ * tree is built, since most builds are never selected in. */
776
+ selectionStyler: SelectionStyler | null;
697
777
  /** Whether a float or an out-of-flow box sits in an inline box: where
698
778
  * none does, layout looks for them among a block's own children and
699
779
  * goes through no inline box to find them. */
@@ -710,10 +790,17 @@ export interface BoxTree {
710
790
  * (`Cascade.rootStyle`), whose background is the canvas's; null where
711
791
  * the document has either. */
712
792
  impliedHtml: ComputedStyle | null;
793
+ /** The scheme the palette is, which the element the document is drawn
794
+ * in is of: where the root element's is the other, the canvas is that
795
+ * scheme's and opaque (`paintDocument`). */
796
+ paletteScheme: 'light' | 'dark';
713
797
  }
714
798
 
715
799
  export interface BuildOptions {
716
800
  cascade: Cascade;
801
+ /** The styles of the tree this one replaces, where only some elements'
802
+ * can have changed since (`Cascade.beginSharing`). */
803
+ kept?: KeptStyles | null;
717
804
  /** Device pixels per CSS pixel. An image's pixels and a `width="600"`
718
805
  * attribute are CSS pixels; every box is device, so both are multiplied
719
806
  * on the way in. Default 1. */
@@ -813,7 +900,7 @@ class Builder {
813
900
 
814
901
  run(root: Element): BoxTree {
815
902
  const cascade = this._options.cascade;
816
- cascade.beginSharing();
903
+ cascade.beginSharing(this._options.kept ?? null);
817
904
  const { style: rootStyle, html: impliedHtml } = cascade.rootStyle(
818
905
  hasBody(root),
819
906
  hasHtml(root),
@@ -832,10 +919,13 @@ class Builder {
832
919
  // it carries no margins of its own and cannot collapse with anything.
833
920
  this._children(root, rootBox, rootStyle, false, null, ROOT_SHARE_KEY);
834
921
  this._endLine();
835
- fixUp(rootBox, anonymousStyles(cascade.initial));
922
+ cascade.endSharing();
923
+ const anonymous = anonymousStyles(cascade.initial);
924
+ fixUp(rootBox, anonymous);
836
925
  assignSubtreeRanges(rootBox);
837
926
  return {
838
927
  root: rootBox,
928
+ anonymous,
839
929
  text: this._chunks.join(''),
840
930
  textBoxes: this._textBoxes,
841
931
  textStyles: this._textStyles,
@@ -848,10 +938,36 @@ class Builder {
848
938
  negative: this._negative,
849
939
  firstLine: this._firstLine,
850
940
  firstLineStyler: this._firstLine ? this._firstLineStyler() : null,
941
+ selectionStyler: cascade.hasSelection ? this._selectionStyler() : null,
851
942
  nestedOutOfLine: this._nestedOutOfLine,
852
943
  movedInline: this._movedInline,
853
944
  clipText: this._clipText,
854
945
  impliedHtml,
946
+ paletteScheme: cascade.initial.colorScheme,
947
+ };
948
+ }
949
+
950
+ /** `BoxTree.selectionStyler`, over this build's cascade: an element's
951
+ * over its parent's, each once. A box with no element of its own — text,
952
+ * an anonymous box — is its nearest element's. */
953
+ private _selectionStyler(): SelectionStyler {
954
+ const cascade = this._options.cascade;
955
+ const styles = this._styles;
956
+ const known = new Map<Element, SelectionStyle | null>();
957
+ const of = (el: Element): SelectionStyle | null => {
958
+ let found = known.get(el);
959
+ if (found !== undefined) return found;
960
+ // the parser keeps a document to 256 elements deep (`dom.ts`)
961
+ const parent = isElement(el.parent) ? of(el.parent) : null;
962
+ const own = styles.get(el)?.style;
963
+ found = own ? cascade.selectionStyle(el, own, parent) : parent;
964
+ known.set(el, found);
965
+ return found;
966
+ };
967
+ return (box) => {
968
+ let at = box;
969
+ while (at && !at.el) at = at.parent;
970
+ return at?.el ? of(at.el) : null;
855
971
  };
856
972
  }
857
973
 
@@ -989,7 +1105,7 @@ class Builder {
989
1105
  }
990
1106
  }
991
1107
  // a translation is moved by the same pass
992
- if (style.translate || style.transformTranslate) this._relative = true;
1108
+ if (style.translate || style.transform) this._relative = true;
993
1109
  if (style.backgroundClipText) this._clipText = true;
994
1110
  if (style.verticalAlign !== 'baseline' && style.display === 'inline') {
995
1111
  this._movedInline = true;
@@ -1024,7 +1140,9 @@ class Builder {
1024
1140
  }
1025
1141
 
1026
1142
  // an `<object>` is its image once it has one, and its content until;
1027
- // an `<embed>` its image, and a `<video>` its poster, or a frame
1143
+ // an `<embed>` its image, and a `<video>` its poster, or a frame; an
1144
+ // image button its image, and a button saying what it is for until it
1145
+ // has one, so it can be pressed either way
1028
1146
  const replaced =
1029
1147
  tag === 'object'
1030
1148
  ? this._options.imageSize(el)
@@ -1032,7 +1150,11 @@ class Builder {
1032
1150
  : 'none'
1033
1151
  : (tag === 'embed' || tag === 'video') && this._options.imageSize(el)
1034
1152
  ? 'image'
1035
- : replacedKind(el, tag);
1153
+ : isImageButton(el, tag)
1154
+ ? this._options.imageSize(el)
1155
+ ? 'image'
1156
+ : 'button'
1157
+ : replacedKind(el, tag);
1036
1158
  if (replaced !== 'none') {
1037
1159
  this._replaced(el, tag, replaced, style, into);
1038
1160
  return;
@@ -1288,7 +1410,7 @@ class Builder {
1288
1410
  }
1289
1411
  }
1290
1412
  // a translation is moved by the same pass
1291
- if (style.translate || style.transformTranslate) this._relative = true;
1413
+ if (style.translate || style.transform) this._relative = true;
1292
1414
  if (style.backgroundClipText) this._clipText = true;
1293
1415
  if (style.verticalAlign !== 'baseline' && style.display === 'inline') {
1294
1416
  this._movedInline = true;
@@ -2359,6 +2481,13 @@ function setIntrinsics(box: Box, size: IntrinsicSize, scale: number): void {
2359
2481
  };
2360
2482
  }
2361
2483
 
2484
+ /** An `<input type=image>`: a picture that submits its form. */
2485
+ function isImageButton(el: Element, tag: string): boolean {
2486
+ return (
2487
+ tag === 'input' && (attr(el, 'type') ?? '').trim().toLowerCase() === 'image'
2488
+ );
2489
+ }
2490
+
2362
2491
  function replacedKind(el: Element, tag: string): ReplacedKind {
2363
2492
  switch (tag) {
2364
2493
  case 'img':
@@ -2662,24 +2791,24 @@ function namesImages(style: ComputedStyle): boolean {
2662
2791
  * paragraph after it the height of the div further down. One per parent
2663
2792
  * style, which the cascade already shares between elements.
2664
2793
  */
2665
- type AnonymousStyle = (
2666
- parent: Box,
2794
+ export type AnonymousStyle = (
2795
+ parent: ComputedStyle,
2667
2796
  display: ComputedStyle['display'],
2668
2797
  ) => ComputedStyle;
2669
2798
 
2670
2799
  function anonymousStyles(initial: ComputedStyle): AnonymousStyle {
2671
2800
  const made = new WeakMap<ComputedStyle, Map<string, ComputedStyle>>();
2672
2801
  return (parent, display) => {
2673
- let byDisplay = made.get(parent.style);
2802
+ let byDisplay = made.get(parent);
2674
2803
  if (!byDisplay) {
2675
2804
  byDisplay = new Map();
2676
- made.set(parent.style, byDisplay);
2805
+ made.set(parent, byDisplay);
2677
2806
  }
2678
2807
  let style = byDisplay.get(display);
2679
2808
  if (!style) {
2680
2809
  // `display` is the one property it does not start from: it is the
2681
2810
  // box the fix-up made, and layout asks the style what a box is
2682
- style = inherit(parent.style, initial);
2811
+ style = inherit(parent, initial);
2683
2812
  style.display = display;
2684
2813
  byDisplay.set(display, style);
2685
2814
  }
@@ -2787,7 +2916,7 @@ function anonymousOf(
2787
2916
  anonymous: AnonymousStyle,
2788
2917
  display: ComputedStyle['display'] = kind as ComputedStyle['display'],
2789
2918
  ): Box {
2790
- const box = new Box(kind, null, anonymous(parent, display));
2919
+ const box = new Box(kind, null, anonymous(parent.style, display));
2791
2920
  box.parent = parent;
2792
2921
  for (const child of run) {
2793
2922
  child.parent = box;
@@ -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,8 @@ import { Box, GRID_TRACKS, isBlank } from './boxes.js';
31
31
  import {
32
32
  FLEXED_HEIGHT,
33
33
  MIN_CONTENT_PROBE,
34
+ USED_HEIGHT,
35
+ centreButton,
34
36
  clampHeight,
35
37
  clampWidth,
36
38
  exactMinContent,
@@ -60,6 +62,25 @@ interface Item {
60
62
  max: number;
61
63
  }
62
64
 
65
+ /**
66
+ * The first of a grid's items in row-major order: the one furthest to the
67
+ * start of the first row that holds an item, which the grid's baseline is
68
+ * taken from (CSS Grid 1, 10.6).
69
+ */
70
+ function firstItem(items: Item[]): Box | null {
71
+ let first: Item | null = null;
72
+ for (const item of items) {
73
+ if (
74
+ first === null ||
75
+ item.row < first.row ||
76
+ (item.row === first.row && item.col < first.col)
77
+ ) {
78
+ first = item;
79
+ }
80
+ }
81
+ return first?.box ?? null;
82
+ }
83
+
63
84
  /**
64
85
  * Lay out a grid container's children. Returns the content height.
65
86
  */
@@ -78,7 +99,13 @@ export function layoutGrid(
78
99
  positionOutOfFlow(child, box, ctx, false);
79
100
  continue;
80
101
  }
81
- resolveEdges(child, Number.isFinite(contentWidth) ? contentWidth : 0);
102
+ // While the columns are sized, the area an item is in has no width for
103
+ // the percentages in its margins and its padding to be of, and they
104
+ // are of nothing (CSS Sizing 3, 5.2.1): a column is as wide as what
105
+ // is in the item. They are of the area once it has a width
106
+ // (`layoutItem`). Taken of the grid's width, an item `margin: 0 25%`
107
+ // made its `min-content` column half the grid wide.
108
+ resolveEdges(child, 0);
82
109
  boxes.push(child);
83
110
  }
84
111
  // the gaps: a percentage of the grid's content size along them, and of
@@ -240,14 +267,15 @@ export function layoutGrid(
240
267
  item,
241
268
  extent(widths, lefts, item.col, item.cols),
242
269
  ctx,
243
- heightBefore(item),
270
+ spanBefore(item),
244
271
  );
245
272
  }
246
273
 
247
274
  // a height of the grid's own, or else the least it may be, which the
248
- // `fr` rows and the `auto` ones fill
275
+ // `fr` rows and the `auto` ones fill: its `min-height`, or the height a
276
+ // column it is an item of flexed it to (`USED_HEIGHT`)
249
277
  const own = Number.isFinite(definite);
250
- const least = own ? 0 : leastHeight(box);
278
+ const least = own ? 0 : Math.max(leastHeight(box), USED_HEIGHT.get(box) ?? 0);
251
279
  // the rows against a height: percentages of it, or `auto` without one
252
280
  const sizeRows = (base: number, available: number, atLeast: number) => {
253
281
  const rowTracks: SizingTrack[] = [];
@@ -301,18 +329,14 @@ export function layoutGrid(
301
329
  if (Object.is(child.percentHeightBase, tall) || !percentOwn(child))
302
330
  continue;
303
331
  child.percentHeightBase = tall;
304
- layoutItem(
305
- item,
306
- extent(widths, lefts, item.col, item.cols),
307
- ctx,
308
- heightBeforeRows(child, tall),
309
- );
332
+ layoutItem(item, extent(widths, lefts, item.col, item.cols), ctx, tall);
310
333
  }
311
334
  GRID_TRACKS.set(box, {
312
335
  cols: tracksOf(widths, lefts, keepCols),
313
336
  rows: tracksOf(heights, tops, keepRows),
314
337
  colLines,
315
338
  rowLines,
339
+ first: firstItem(items),
316
340
  });
317
341
 
318
342
  for (const item of items) {
@@ -371,20 +395,28 @@ export function layoutGrid(
371
395
  const width =
372
396
  wide === null ? child.width : clampWidth(child, wide, area, ctx);
373
397
  // what is in it takes its percentages of that height, which a
374
- // stretch makes definite, as it does a flex item's
398
+ // stretch makes definite, as it does a flex item's — and an item that
399
+ // is a flex or grid container lays its own items out in it (11.1:
400
+ // "the grid area's width and height are considered definite for this
401
+ // purpose"): a card's `margin-top: auto` takes what its row is taller
402
+ // than the card by, its `flex: 1` grows into it (CSS Flexbox 8.1,
403
+ // 9.7). Its content's own height is the same layout, and kept.
375
404
  if (
376
405
  child.kind !== 'replaced' &&
377
- (wide !== null || percentHeightsIn(child))
406
+ (wide !== null ||
407
+ percentHeightsIn(child) ||
408
+ (child.kind === 'flex' && height !== child.height))
378
409
  ) {
379
410
  FLEXED_HEIGHT.set(child, Math.max(0, height - child.verticalExtra));
380
411
  try {
381
- ctx.layoutSubtree(child, width);
412
+ ctx.layoutSubtree(child, width, area);
382
413
  } finally {
383
414
  FLEXED_HEIGHT.delete(child);
384
415
  }
385
416
  }
386
417
  child.width = width;
387
418
  child.height = height;
419
+ centreButton(child);
388
420
  }
389
421
  moveTo(
390
422
  child,
@@ -617,15 +649,25 @@ function maxContentOf(box: Box, ctx: LayoutContext): number {
617
649
  return box.intrinsicMaxContent;
618
650
  }
619
651
 
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. */
652
+ /**
653
+ * Lay an item out in its area: stretched across it where its width is
654
+ * `auto` and it is stretched, or at its content's width, no wider than
655
+ * the area, where it is aligned instead.
656
+ *
657
+ * The area is the item's containing block (CSS Grid 1, 3.3), so it is its
658
+ * width that a percentage in the item's margins, its padding, its
659
+ * `min-width` and its `max-width` is of (6.4, and CSS 2.1 8.3, 8.4 and
660
+ * 10.4), whatever width the item comes to in it. With its own width
661
+ * standing for its area's, an item `width: 50%; max-width: 80%` was four
662
+ * tenths of its column, and one `min-width: 50%` aligned to the start was
663
+ * as wide as its text.
664
+ */
623
665
  function layoutItem(
624
666
  item: Item,
625
667
  area: number,
626
668
  ctx: LayoutContext,
627
- /** Its border box's height before the rows are sized, where it has one. */
628
- tall: number | null,
669
+ /** The height of the rows it spans, where they have one yet. */
670
+ rows: number,
629
671
  ): void {
630
672
  const child = item.box;
631
673
  const style = child.style;
@@ -634,19 +676,35 @@ function layoutItem(
634
676
  style.justifySelf === 'auto'
635
677
  ? (parent?.justifyItems ?? 'normal')
636
678
  : style.justifySelf;
679
+ resolveEdges(child, area);
680
+ // its border box's height before the rows are sized, where it has one
681
+ const tall = heightBeforeRows(child, rows);
637
682
  const margins = child.marginLeft + child.marginRight;
638
683
  const room = Math.max(0, area - margins);
639
- // its content's widths, border box: measured once in its life
684
+ // Its content's widths, border box: measured once in its life, with the
685
+ // percentages in its padding of nothing, as they are while the columns
686
+ // are sized, and here with what those come to of its area. Measuring
687
+ // resolves its edges against nothing, and they are put back.
688
+ const padded = percentPadding(child);
640
689
  const content = (): [number, number] => {
641
- if (child.intrinsicMaxContent < 0) maxContentOf(child, ctx);
690
+ let probed = false;
691
+ if (child.intrinsicMaxContent < 0) {
692
+ maxContentOf(child, ctx);
693
+ probed = true;
694
+ }
642
695
  if (child.intrinsicMinContent < 0) {
643
696
  child.intrinsicMinContent = measureIntrinsicWidth(
644
697
  child,
645
698
  ctx,
646
699
  MIN_CONTENT_PROBE,
647
700
  );
701
+ probed = true;
648
702
  }
649
- return [child.intrinsicMinContent, child.intrinsicMaxContent];
703
+ if (probed) resolveEdges(child, area);
704
+ return [
705
+ child.intrinsicMinContent + padded,
706
+ child.intrinsicMaxContent + padded,
707
+ ];
650
708
  };
651
709
  const keyword = style.widthKeyword;
652
710
  // `stretch` fills its area, and so does `normal` but for a replaced
@@ -699,7 +757,7 @@ function layoutItem(
699
757
  width = Math.min(widest, Math.max(narrowest, room));
700
758
  }
701
759
  if (child.kind !== 'replaced') {
702
- ctx.layoutSubtree(child, width);
760
+ ctx.layoutSubtree(child, width, area);
703
761
  return;
704
762
  }
705
763
  // a replaced element sizes itself from its style: then it is given the
@@ -718,6 +776,21 @@ function layoutItem(
718
776
  }
719
777
  }
720
778
 
779
+ /** What the percentages in a box's padding come to across it, its edges
780
+ * resolved: what a width of its content's, measured with them of nothing,
781
+ * is short of its border box by. */
782
+ function percentPadding(box: Box): number {
783
+ const { paddingLeft, paddingRight } = box.style;
784
+ let out = 0;
785
+ if (isPct(paddingLeft)) {
786
+ out += box.padLeft - Math.max(0, resolve(paddingLeft, 0));
787
+ }
788
+ if (isPct(paddingRight)) {
789
+ out += box.padRight - Math.max(0, resolve(paddingRight, 0));
790
+ }
791
+ return out;
792
+ }
793
+
721
794
  /** Whether a box's content is its least size through a ratio, which it is
722
795
  * where the box shows what overflows it and its least width is `auto`
723
796
  * (CSS Sizing 4, 5.2). */