@react-x11/components 0.14.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 (51) hide show
  1. package/dist/html/css/cascade.d.ts +42 -1
  2. package/dist/html/css/cascade.d.ts.map +1 -1
  3. package/dist/html/css/cascade.js +310 -12
  4. package/dist/html/css/cascade.js.map +1 -1
  5. package/dist/html/css/shapes.d.ts +44 -0
  6. package/dist/html/css/shapes.d.ts.map +1 -0
  7. package/dist/html/css/shapes.js +194 -0
  8. package/dist/html/css/shapes.js.map +1 -0
  9. package/dist/html/css/style.d.ts.map +1 -1
  10. package/dist/html/css/style.js +1 -15
  11. package/dist/html/css/style.js.map +1 -1
  12. package/dist/html/layout/block.d.ts +16 -5
  13. package/dist/html/layout/block.d.ts.map +1 -1
  14. package/dist/html/layout/block.js +26 -5
  15. package/dist/html/layout/block.js.map +1 -1
  16. package/dist/html/layout/boxes.d.ts +13 -0
  17. package/dist/html/layout/boxes.d.ts.map +1 -1
  18. package/dist/html/layout/boxes.js +18 -0
  19. package/dist/html/layout/boxes.js.map +1 -1
  20. package/dist/html/layout/css-grid.d.ts.map +1 -1
  21. package/dist/html/layout/css-grid.js +58 -10
  22. package/dist/html/layout/css-grid.js.map +1 -1
  23. package/dist/html/layout/flex.js +19 -6
  24. package/dist/html/layout/flex.js.map +1 -1
  25. package/dist/html/layout/table.d.ts.map +1 -1
  26. package/dist/html/layout/table.js +38 -16
  27. package/dist/html/layout/table.js.map +1 -1
  28. package/dist/html/node.d.ts +1 -0
  29. package/dist/html/node.d.ts.map +1 -1
  30. package/dist/html/node.js +57 -12
  31. package/dist/html/node.js.map +1 -1
  32. package/dist/html/paint.d.ts +4 -1
  33. package/dist/html/paint.d.ts.map +1 -1
  34. package/dist/html/paint.js +269 -84
  35. package/dist/html/paint.js.map +1 -1
  36. package/dist/html/svg.d.ts +27 -10
  37. package/dist/html/svg.d.ts.map +1 -1
  38. package/dist/html/svg.js +259 -46
  39. package/dist/html/svg.js.map +1 -1
  40. package/package.json +1 -1
  41. package/src/html/css/cascade.ts +343 -8
  42. package/src/html/css/shapes.ts +239 -0
  43. package/src/html/css/style.ts +1 -13
  44. package/src/html/layout/block.ts +27 -5
  45. package/src/html/layout/boxes.ts +30 -0
  46. package/src/html/layout/css-grid.ts +77 -13
  47. package/src/html/layout/flex.ts +22 -9
  48. package/src/html/layout/table.ts +45 -18
  49. package/src/html/node.ts +54 -11
  50. package/src/html/paint.ts +379 -90
  51. package/src/html/svg.ts +318 -39
@@ -17,7 +17,10 @@ import type { ComputedStyle } from '../css/style.js';
17
17
  import { Box, CLIPPED_CELLS, COLLAPSED_CELLS, isBlank } from './boxes.js';
18
18
  import {
19
19
  CELL_CONTENT,
20
+ FLEXED_HEIGHT,
20
21
  MIN_CONTENT_PROBE,
22
+ STRETCHED_ACROSS,
23
+ USED_HEIGHT,
21
24
  clampHeight,
22
25
  cyclicWidth,
23
26
  exactMinContent,
@@ -44,8 +47,11 @@ export function layoutTable(
44
47
  table.captionBottom = 0;
45
48
  // a percentage of a width that is not known is `auto`: the table is as
46
49
  // wide as its columns where its content is measured (`cyclicWidth`)
47
- const auto =
50
+ const own =
48
51
  table.style.width === AUTO || cyclicWidth(table.style, contentWidth);
52
+ // — and one a grid stretches across its area is as wide as that, as a
53
+ // table with a width of its own is (`STRETCHED_ACROSS`)
54
+ const auto = own && !STRETCHED_ACROSS.has(table);
49
55
  if (!columnCount) {
50
56
  // With no columns, an auto table is as wide as its widest caption can
51
57
  // be and its `min-width` asks, as one with columns is at the least
@@ -88,8 +94,8 @@ export function layoutTable(
88
94
  for (const cell of cells) resolveEdges(cell.box, 0);
89
95
 
90
96
  // a fixed layout needs a width to be fixed to; with `auto` a table is laid
91
- // out by its contents after all (CSS 2.1 17.5.2.1)
92
- const fixed = style.tableLayout === 'fixed' && !auto;
97
+ // out by its contents after all (CSS 2.1 17.5.2.1), stretched or not
98
+ const fixed = style.tableLayout === 'fixed' && !own;
93
99
  // a percentage of the table's width less its spacing (CSS 2.1 17.5.2.1)
94
100
  const set = columnWidths(columnBoxes, columnGroups, available);
95
101
  const widths = fixed
@@ -210,15 +216,21 @@ export function layoutTable(
210
216
  // A cell aligned on the baseline — every value but `top`, `middle` and
211
217
  // `bottom` — hangs its first line on its first row's baseline, the lowest
212
218
  // of theirs from the row's top (CSS 2.1 17.5.3), and the row is as tall as
213
- // the cells hung from it need.
214
- const lift = baselineLifts(cells, rows.length);
219
+ // the cells hung from it need. What moves down is the content, so a cell
220
+ // needs the lift and its content, and a height it sets is a least one
221
+ // beside that rather than more room under the lift: two cells set
222
+ // `height: 80px` with text of two sizes in them are a row 80px tall, where
223
+ // the lift on top of the height made it 104px. A cell spanning rows needs
224
+ // the same of them, which is where Chrome differs: it sizes a spanning
225
+ // cell by its content and height alone and lets the lifted content hang
226
+ // out of it, and the rows here "encompass the cell" (17.5.3).
227
+ const lift = baselineLifts(cells, rows.length, natural);
228
+ const needs = (i: number): number =>
229
+ Math.max(cells[i].box.height, lift[i] + natural[i]);
215
230
  for (let i = 0; i < cells.length; i += 1) {
216
231
  const cell = cells[i];
217
232
  if (cell.rowSpan > 1) continue;
218
- rowHeight[cell.row] = Math.max(
219
- rowHeight[cell.row],
220
- lift[i] + cell.box.height,
221
- );
233
+ rowHeight[cell.row] = Math.max(rowHeight[cell.row], needs(i));
222
234
  }
223
235
  for (let r = 0; r < rows.length; r += 1) {
224
236
  const specified = resolveOrNull(rows[r].style.height, NaN);
@@ -234,14 +246,20 @@ export function layoutTable(
234
246
  let covered = 0;
235
247
  for (let r = cell.row; r <= last; r += 1) covered += rowHeight[r];
236
248
  covered += rowSpacing * (last - cell.row);
237
- const missing = lift[i] + cell.box.height - covered;
249
+ const missing = needs(i) - covered;
238
250
  if (missing > 0) rowHeight[last] += missing;
239
251
  }
240
252
 
241
253
  // A table's own height, within its least and greatest, is a least
242
254
  // height: what its rows come short of it goes to them (CSS 2.1 17.5.3,
243
- // which leaves how to its user agents: `growRows`)
255
+ // which leaves how to its user agents: `growRows`). So is the height a
256
+ // flex or grid layout gave it, stretched or flexed (`FLEXED_HEIGHT`,
257
+ // `USED_HEIGHT`), which is its wrapper box's, captions and all (CSS
258
+ // Flexbox 1, 4): taken as the table's box alone, a table stretched down
259
+ // its line kept its rows at their content's height, and a cell had
260
+ // nothing for `vertical-align` to centre its content in.
244
261
  const wanted = resolveOrNull(style.height, table.percentHeightBase);
262
+ const given = FLEXED_HEIGHT.get(table) ?? USED_HEIGHT.get(table) ?? NaN;
245
263
  if (rows.length) {
246
264
  const extraBox = table.verticalExtra;
247
265
  const set =
@@ -250,7 +268,10 @@ export function layoutTable(
250
268
  : style.boxSizing === 'border-box'
251
269
  ? wanted
252
270
  : wanted + extraBox;
253
- const inner = clampHeight(table, set) - extraBox;
271
+ let inner = clampHeight(table, set) - extraBox;
272
+ if (Number.isFinite(given)) {
273
+ inner = Math.max(inner, given - table.captionTop - table.captionBottom);
274
+ }
254
275
  let total = 0;
255
276
  for (const h of rowHeight) total += h;
256
277
  const extra = inner - total - rowSpacing * (rows.length + 1);
@@ -805,12 +826,18 @@ function lengthAgainst(len: Len, base: number): number | null {
805
826
  * row's: 0 for a cell aligned `top`, `middle` or `bottom`, which is placed
806
827
  * once the row's height is known. A cell's baseline is its first line box's,
807
828
  * at any depth, or the bottom of its content where it has none (CSS 2.1
808
- * 17.5.3); it is taken as laid out, from the cell's top. An empty cell has
809
- * nothing to align and says nothing about the row's, as in a browser: its
810
- * content's bottom is its height, which would hang every other cell in the
811
- * row from the bottom of a cell given a height and nothing in it.
829
+ * 17.5.3); it is taken as laid out, from the cell's top. That bottom is of
830
+ * the content (`natural`) and not of a height the cell sets, which "does not
831
+ * increase the height of the cell box": taken from the set height, a cell
832
+ * holding one block of 30px in 80px hung the text beside it from 80px. An
833
+ * empty cell has nothing to align and says nothing about the row's, as in a
834
+ * browser.
812
835
  */
813
- function baselineLifts(cells: Cell[], rowCount: number): number[] {
836
+ function baselineLifts(
837
+ cells: Cell[],
838
+ rowCount: number,
839
+ natural: number[],
840
+ ): number[] {
814
841
  const lift = new Array<number>(cells.length).fill(0);
815
842
  let any = false;
816
843
  for (const cell of cells) {
@@ -829,7 +856,7 @@ function baselineLifts(cells: Cell[], rowCount: number): number[] {
829
856
  own[i] =
830
857
  first !== null
831
858
  ? first - box.y
832
- : box.height - box.padBottom - box.borderBottom;
859
+ : natural[i] - box.padBottom - box.borderBottom;
833
860
  row[cells[i].row] = Math.max(row[cells[i].row], own[i]);
834
861
  }
835
862
  for (let i = 0; i < cells.length; i += 1) {
package/src/html/node.ts CHANGED
@@ -60,6 +60,7 @@ import {
60
60
  import type { HoverTouch, KeptStyles, MetricFace } from './css/cascade.js';
61
61
  import { mediaMatches, parseStylesheet } from './css/parse.js';
62
62
  import type { MediaCondition, Stylesheet } from './css/parse.js';
63
+ import type { ShapeStyles } from './css/shapes.js';
63
64
  import { uaStylesheet } from './css/ua.js';
64
65
  import type { ComputedStyle, RootLook } from './css/style.js';
65
66
  import {
@@ -74,6 +75,7 @@ import {
74
75
  GENERATED_FROM,
75
76
  INLINE_OFFSETS,
76
77
  LINE_BOX_RAISES,
78
+ SHAPE_STYLES,
77
79
  SHIFTED_LINES,
78
80
  TEXT_SHIFTS,
79
81
  columned,
@@ -1711,7 +1713,39 @@ export class HtmlViewNode extends Node {
1711
1713
  styleOf(el);
1712
1714
  if (refused) return false;
1713
1715
  }
1714
- if (!changed.size && pseudoRules === HOVER_PSEUDO_NONE) return true;
1716
+ // The drawings the move reached. What the rules give the shapes in one
1717
+ // is no box's style (`BoxTree.shapeStyler`), so it is asked for again:
1718
+ // of a drawing whose own style changed — its colour, a custom property
1719
+ // — and of each where a rule that reaches into a drawing tests the
1720
+ // pointer, `a:hover svg path`. One with another answer is painted
1721
+ // again; one never painted has none yet, and asks when it is.
1722
+ const redrawn: [Box, ShapeStyles | null][] = [];
1723
+ if (tree.shapeStyler) {
1724
+ for (const el of reach) {
1725
+ if (tagOf(el) !== 'svg') continue;
1726
+ const style = fresh.get(el);
1727
+ if (!style) continue;
1728
+ if (!changed.has(el) && !cascade.shapesFollowPointer) continue;
1729
+ const box = this._firstBoxesOf(tree).get(el);
1730
+ const was = box && SHAPE_STYLES.get(box);
1731
+ if (!box || was === undefined) continue;
1732
+ const shapes = cascade.shapeStyles(el, style);
1733
+ if (shapes?.key !== was?.key) redrawn.push([box, shapes]);
1734
+ }
1735
+ }
1736
+ const redraw = (): Rect[] => {
1737
+ const inks: Rect[] = [];
1738
+ for (const [box, shapes] of redrawn) {
1739
+ SHAPE_STYLES.set(box, shapes);
1740
+ const ink = inkOf(box);
1741
+ if (ink) inks.push(ink);
1742
+ }
1743
+ return inks;
1744
+ };
1745
+ if (!changed.size && pseudoRules === HOVER_PSEUDO_NONE) {
1746
+ this._repaintInk(redraw());
1747
+ return true;
1748
+ }
1715
1749
  for (const el of changed.keys()) {
1716
1750
  const tag = tagOf(el);
1717
1751
  // their backgrounds are the canvas's
@@ -1830,7 +1864,10 @@ export class HtmlViewNode extends Node {
1830
1864
  }
1831
1865
  }
1832
1866
  }
1833
- if (!restyled.length) return true;
1867
+ if (!restyled.length) {
1868
+ this._repaintInk(redraw());
1869
+ return true;
1870
+ }
1834
1871
 
1835
1872
  // the text: each layout holding a run of a restyled box's, made again
1836
1873
  // with its new ink, where it comes out the same shape
@@ -1883,6 +1920,7 @@ export class HtmlViewNode extends Node {
1883
1920
  for (const [el, style] of changed) {
1884
1921
  tree.styles.set(el, { style, inFlex: tree.styles.get(el)!.inFlex });
1885
1922
  }
1923
+ for (const ink of redraw()) inks.push(ink);
1886
1924
  // A box that moved moves what is in it, as layout moved it the first
1887
1925
  // time (`applyRelativeOffsets`), and a later layout moves it the same
1888
1926
  // way; where it reached the document's end, before or after, the
@@ -1923,14 +1961,7 @@ export class HtmlViewNode extends Node {
1923
1961
  * of: made on the first move over a tree, which is one walk of it. */
1924
1962
  private _firstBoxes: { tree: BoxTree; of: Map<Element, Box> } | null = null;
1925
1963
 
1926
- /**
1927
- * The blocks a restyle of `changed` has to look in: for each element, the
1928
- * box its text is set in the lines of — the nearest box around its first
1929
- * that is not an inline one — which holds every box that takes its style
1930
- * from it, the pieces a block in it broke it into among them
1931
- * (`breakAround`). One that is inside another is left to it.
1932
- */
1933
- private _blocksOf(tree: BoxTree, changed: Iterable<Element>): Box[] {
1964
+ private _firstBoxesOf(tree: BoxTree): Map<Element, Box> {
1934
1965
  let index = this._firstBoxes;
1935
1966
  if (index?.tree !== tree) {
1936
1967
  const of = new Map<Element, Box>();
@@ -1944,9 +1975,21 @@ export class HtmlViewNode extends Node {
1944
1975
  }
1945
1976
  index = this._firstBoxes = { tree, of };
1946
1977
  }
1978
+ return index.of;
1979
+ }
1980
+
1981
+ /**
1982
+ * The blocks a restyle of `changed` has to look in: for each element, the
1983
+ * box its text is set in the lines of — the nearest box around its first
1984
+ * that is not an inline one — which holds every box that takes its style
1985
+ * from it, the pieces a block in it broke it into among them
1986
+ * (`breakAround`). One that is inside another is left to it.
1987
+ */
1988
+ private _blocksOf(tree: BoxTree, changed: Iterable<Element>): Box[] {
1989
+ const first = this._firstBoxesOf(tree);
1947
1990
  const blocks = new Set<Box>();
1948
1991
  for (const el of changed) {
1949
- let box: Box | null = index.of.get(el) ?? null;
1992
+ let box: Box | null = first.get(el) ?? null;
1950
1993
  while (
1951
1994
  box?.parent &&
1952
1995
  (box.kind === 'inline' || box.kind === 'text' || box.kind === 'break')