@react-x11/components 0.14.1 → 0.16.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 (96) hide show
  1. package/dist/code-editor/node.d.ts +13 -0
  2. package/dist/code-editor/node.d.ts.map +1 -1
  3. package/dist/code-editor/node.js +23 -5
  4. package/dist/code-editor/node.js.map +1 -1
  5. package/dist/html/css/cascade.d.ts +171 -26
  6. package/dist/html/css/cascade.d.ts.map +1 -1
  7. package/dist/html/css/cascade.js +616 -216
  8. package/dist/html/css/cascade.js.map +1 -1
  9. package/dist/html/css/color.d.ts +25 -0
  10. package/dist/html/css/color.d.ts.map +1 -1
  11. package/dist/html/css/color.js +307 -19
  12. package/dist/html/css/color.js.map +1 -1
  13. package/dist/html/css/content.d.ts +11 -6
  14. package/dist/html/css/content.d.ts.map +1 -1
  15. package/dist/html/css/content.js +171 -12
  16. package/dist/html/css/content.js.map +1 -1
  17. package/dist/html/css/parse.d.ts.map +1 -1
  18. package/dist/html/css/parse.js +62 -9
  19. package/dist/html/css/parse.js.map +1 -1
  20. package/dist/html/css/shapes.d.ts +18 -0
  21. package/dist/html/css/shapes.d.ts.map +1 -1
  22. package/dist/html/css/shapes.js +21 -1
  23. package/dist/html/css/shapes.js.map +1 -1
  24. package/dist/html/css/style.d.ts +42 -5
  25. package/dist/html/css/style.d.ts.map +1 -1
  26. package/dist/html/css/style.js +74 -12
  27. package/dist/html/css/style.js.map +1 -1
  28. package/dist/html/css/ua.js +15 -0
  29. package/dist/html/css/ua.js.map +1 -1
  30. package/dist/html/css/values.d.ts +5 -0
  31. package/dist/html/css/values.d.ts.map +1 -1
  32. package/dist/html/css/values.js +18 -9
  33. package/dist/html/css/values.js.map +1 -1
  34. package/dist/html/index.d.ts.map +1 -1
  35. package/dist/html/index.js +7 -0
  36. package/dist/html/index.js.map +1 -1
  37. package/dist/html/layout/block.d.ts +29 -9
  38. package/dist/html/layout/block.d.ts.map +1 -1
  39. package/dist/html/layout/block.js +141 -52
  40. package/dist/html/layout/block.js.map +1 -1
  41. package/dist/html/layout/boxes.d.ts +16 -2
  42. package/dist/html/layout/boxes.d.ts.map +1 -1
  43. package/dist/html/layout/boxes.js +144 -23
  44. package/dist/html/layout/boxes.js.map +1 -1
  45. package/dist/html/layout/css-grid.d.ts.map +1 -1
  46. package/dist/html/layout/css-grid.js +11 -9
  47. package/dist/html/layout/css-grid.js.map +1 -1
  48. package/dist/html/layout/flex.d.ts.map +1 -1
  49. package/dist/html/layout/flex.js +1631 -221
  50. package/dist/html/layout/flex.js.map +1 -1
  51. package/dist/html/layout/inline.d.ts +15 -3
  52. package/dist/html/layout/inline.d.ts.map +1 -1
  53. package/dist/html/layout/inline.js +23 -8
  54. package/dist/html/layout/inline.js.map +1 -1
  55. package/dist/html/layout/table.d.ts +27 -1
  56. package/dist/html/layout/table.d.ts.map +1 -1
  57. package/dist/html/layout/table.js +886 -324
  58. package/dist/html/layout/table.js.map +1 -1
  59. package/dist/html/node.d.ts +35 -4
  60. package/dist/html/node.d.ts.map +1 -1
  61. package/dist/html/node.js +118 -26
  62. package/dist/html/node.js.map +1 -1
  63. package/dist/html/paint.d.ts.map +1 -1
  64. package/dist/html/paint.js +558 -178
  65. package/dist/html/paint.js.map +1 -1
  66. package/dist/html/resources.js +14 -1
  67. package/dist/html/resources.js.map +1 -1
  68. package/dist/html/svg.d.ts +23 -7
  69. package/dist/html/svg.d.ts.map +1 -1
  70. package/dist/html/svg.js +264 -59
  71. package/dist/html/svg.js.map +1 -1
  72. package/dist/html/widgets.d.ts.map +1 -1
  73. package/dist/html/widgets.js +57 -4
  74. package/dist/html/widgets.js.map +1 -1
  75. package/package.json +3 -3
  76. package/src/code-editor/node.ts +24 -5
  77. package/src/html/css/cascade.ts +697 -221
  78. package/src/html/css/color.ts +345 -18
  79. package/src/html/css/content.ts +173 -15
  80. package/src/html/css/parse.ts +60 -8
  81. package/src/html/css/shapes.ts +35 -1
  82. package/src/html/css/style.ts +122 -17
  83. package/src/html/css/ua.ts +15 -0
  84. package/src/html/css/values.ts +23 -9
  85. package/src/html/index.ts +7 -0
  86. package/src/html/layout/block.ts +149 -58
  87. package/src/html/layout/boxes.ts +171 -27
  88. package/src/html/layout/css-grid.ts +12 -9
  89. package/src/html/layout/flex.ts +1843 -212
  90. package/src/html/layout/inline.ts +27 -7
  91. package/src/html/layout/table.ts +961 -332
  92. package/src/html/node.ts +151 -27
  93. package/src/html/paint.ts +692 -234
  94. package/src/html/resources.ts +13 -1
  95. package/src/html/svg.ts +299 -56
  96. package/src/html/widgets.ts +61 -4
package/src/html/node.ts CHANGED
@@ -56,8 +56,15 @@ import {
56
56
  HOVER_PSEUDO_NONE,
57
57
  HOVER_PSEUDO_OTHER,
58
58
  HOVER_UNTOUCHED,
59
+ NO_FOCUS,
60
+ } from './css/cascade.js';
61
+ import type {
62
+ FocusState,
63
+ HoverTouch,
64
+ KeptStyles,
65
+ MetricFace,
66
+ PointerState,
59
67
  } from './css/cascade.js';
60
- import type { HoverTouch, KeptStyles, MetricFace } from './css/cascade.js';
61
68
  import { mediaMatches, parseStylesheet } from './css/parse.js';
62
69
  import type { MediaCondition, Stylesheet } from './css/parse.js';
63
70
  import type { ShapeStyles } from './css/shapes.js';
@@ -129,7 +136,7 @@ import {
129
136
  stacksLayers,
130
137
  } from './paint.js';
131
138
  import type { PaintContext } from './paint.js';
132
- import { controlRectsOf, measureControl } from './controls.js';
139
+ import { controlRectsOf, measureControl, styledField } from './controls.js';
133
140
  import type { BareField, ControlRect } from './controls.js';
134
141
  import { ResourceStore } from './resources.js';
135
142
  import type { ResourceRequest, ResourceResult } from './resources.js';
@@ -290,6 +297,8 @@ export class HtmlViewNode extends Node {
290
297
  * the text carries surrogate pairs. null until checked. */
291
298
  private _pointsAreUnits: boolean | null = null;
292
299
  private _hovered: Element[] = [];
300
+ /** Which element's widget holds the focus (`setFocus`). */
301
+ private _focus: FocusState = NO_FOCUS;
293
302
  /** Where the pointer last moved to, in logical window pixels, while it
294
303
  * is over the element (`defaultMouseMove`). */
295
304
  private _pointerAt: { x: number; y: number } | null = null;
@@ -402,6 +411,12 @@ export class HtmlViewNode extends Node {
402
411
  controlBorder: look.controlBorder * s,
403
412
  controlRadius: look.controlRadius * s,
404
413
  controlFontSize: (look.controlFontSize ?? look.fontSize) * s,
414
+ ...(look.focusRingWidth !== undefined && {
415
+ focusRingWidth: look.focusRingWidth * s,
416
+ }),
417
+ ...(look.focusRingOffset !== undefined && {
418
+ focusRingOffset: look.focusRingOffset * s,
419
+ }),
405
420
  };
406
421
  this._deviceLookFor = look;
407
422
  this._deviceLookAt = s;
@@ -711,6 +726,7 @@ export class HtmlViewNode extends Node {
711
726
  hovered: new Set(this._hovered),
712
727
  active: EMPTY_SET,
713
728
  });
729
+ this._cascade.setFocus(this._focus);
714
730
  this._mediaBand = this._cascade.mediaBand(width);
715
731
  }
716
732
 
@@ -1031,6 +1047,10 @@ export class HtmlViewNode extends Node {
1031
1047
  scale: this._scale,
1032
1048
  imageSize: (el) => this._resources.imageSize(imageUrlOf(el) ?? ''),
1033
1049
  urlSize: (url) => this._resources.imageSize(url),
1050
+ faceAscent: (style) => {
1051
+ const fonts = this._fonts();
1052
+ return fonts ? faceExtentOf(fonts, style).ascent : undefined;
1053
+ },
1034
1054
  controlSize: (el, kind, style) =>
1035
1055
  measureControl(el, kind, style, this._fonts(), look),
1036
1056
  });
@@ -1539,37 +1559,120 @@ export class HtmlViewNode extends Node {
1539
1559
  * with the styles of every element the move did not reach kept.
1540
1560
  */
1541
1561
  private _restyleHover(was: readonly Element[], now: readonly Element[]) {
1562
+ const cascade = this._cascade;
1542
1563
  const reach = this._hoverReach(was, now);
1543
- if (reach !== null) {
1564
+ if (reach !== null && cascade) {
1544
1565
  if (!reach.size) return;
1545
- if (this._hoverInPlace(reach, was)) return;
1566
+ const before = { hovered: new Set(was), active: EMPTY_SET };
1567
+ if (this._hoverInPlace(reach, before, this._focus, cascade.hoverPseudo)) {
1568
+ return;
1569
+ }
1570
+ }
1571
+ this._invalidate(Stale.Boxes, reach);
1572
+ }
1573
+
1574
+ /** The element whose widget holds the focus, or null (`setFocus`). */
1575
+ get focusedElement(): Element | null {
1576
+ return this._focus.element;
1577
+ }
1578
+
1579
+ /**
1580
+ * The focus moved: `element`'s widget took it, or, given null, no widget
1581
+ * of this document holds it now — it went to something else in the
1582
+ * window, or nowhere. `visible` is whether it shows, `:focus-visible`,
1583
+ * which it always does in a text field. Restyles what `:focus` and its
1584
+ * kin reach as a hover does (`_restyleHover`), and returns whether the
1585
+ * cascade's answer could have changed.
1586
+ *
1587
+ * No element of a document takes the focus: a control's widget, mounted
1588
+ * beside it, does, and says so here. Only a text field's does, so far —
1589
+ * the widgets core draws as components keep their own focus to
1590
+ * themselves.
1591
+ */
1592
+ setFocus(element: Element | null, visible = true): boolean {
1593
+ const was = this._focus;
1594
+ if (was.element === element && (!element || was.visible === visible)) {
1595
+ return false;
1596
+ }
1597
+ const within = new Set<Element>();
1598
+ for (
1599
+ let at: Element | null = element;
1600
+ at;
1601
+ at = isElement(at.parent) ? at.parent : null
1602
+ ) {
1603
+ within.add(at);
1604
+ }
1605
+ const now: FocusState = element ? { element, visible, within } : NO_FOCUS;
1606
+ this._focus = now;
1607
+ const cascade = this._cascade;
1608
+ cascade?.setFocus(now);
1609
+ if (!cascade || !cascade.focusSensitive) return false;
1610
+ const reach = cascade.focusLocal
1611
+ ? this._stateReach(
1612
+ [...was.within],
1613
+ [...now.within],
1614
+ // one element, whose ring came or went
1615
+ was.element === element ? element : null,
1616
+ (el) => cascade.focusTouches(el),
1617
+ (el, into) => cascade.focusAnchors(el, into),
1618
+ )
1619
+ : null;
1620
+ if (reach !== null) {
1621
+ if (!reach.size) return true;
1622
+ const pointer = { hovered: new Set(this._hovered), active: EMPTY_SET };
1623
+ if (this._hoverInPlace(reach, pointer, was, cascade.focusPseudo)) {
1624
+ return true;
1625
+ }
1546
1626
  }
1547
1627
  this._invalidate(Stale.Boxes, reach);
1628
+ return true;
1548
1629
  }
1549
1630
 
1550
1631
  /**
1551
1632
  * The elements whose styles a move of the hovered chain can change, or
1552
1633
  * null where that cannot be said (`Cascade.hoverLocal`).
1634
+ */
1635
+ private _hoverReach(
1636
+ was: readonly Element[],
1637
+ now: readonly Element[],
1638
+ ): Set<Element> | null {
1639
+ const cascade = this._cascade;
1640
+ if (!cascade || !cascade.hoverLocal) return null;
1641
+ return this._stateReach(
1642
+ was,
1643
+ now,
1644
+ null,
1645
+ (el) => cascade.hoverTouches(el),
1646
+ (el, into) => cascade.hoverAnchors(el, into),
1647
+ );
1648
+ }
1649
+
1650
+ /**
1651
+ * The elements whose styles a move of the hovered chain can change, or
1652
+ * of the focus's chain.
1553
1653
  *
1554
1654
  * Only an element whose hover state flipped and that a compound testing
1555
1655
  * the pointer could match can change (`Cascade.hoverTouches`), with its
1556
1656
  * subtree, and its later siblings' where a sibling combinator is in play;
1557
1657
  * and the elements a `:has()` testing the pointer may flip, which are
1558
1658
  * around the ones that changed. A move between two paragraphs under
1559
- * `a:hover` reaches nothing.
1659
+ * `a:hover` reaches nothing. The focus's the same way, its chain the
1660
+ * focused element and every element around it, and `also` one whose
1661
+ * state flipped though it is in both — the ring coming or going.
1560
1662
  */
1561
- private _hoverReach(
1663
+ private _stateReach(
1562
1664
  was: readonly Element[],
1563
1665
  now: readonly Element[],
1564
- ): Set<Element> | null {
1565
- const cascade = this._cascade;
1566
- if (!cascade || !cascade.hoverLocal) return null;
1666
+ also: Element | null,
1667
+ touches: (el: Element) => HoverTouch,
1668
+ anchors: (el: Element, into: Map<Element, HoverTouch>) => void,
1669
+ ): Set<Element> {
1567
1670
  const before = new Set(was);
1568
1671
  const after = new Set(now);
1569
1672
  const roots = new Map<Element, HoverTouch>();
1570
- const flipped = (el: Element, other: Set<Element>): void => {
1571
- if (other.has(el)) return;
1572
- const touch = cascade.hoverTouches(el);
1673
+ const flipped = (el: Element, other: Set<Element> | null): void => {
1674
+ if (other?.has(el)) return;
1675
+ const touch = touches(el);
1573
1676
  if (touch > (roots.get(el) ?? HOVER_UNTOUCHED)) roots.set(el, touch);
1574
1677
  };
1575
1678
  for (const el of was) flipped(el, after);
@@ -1578,10 +1681,14 @@ export class HtmlViewNode extends Node {
1578
1681
  // side is where a `:has()` is looked for from
1579
1682
  const deepest = (chain: readonly Element[], other: Set<Element>) => {
1580
1683
  const el = chain[0];
1581
- if (el && !other.has(el)) cascade.hoverAnchors(el, roots);
1684
+ if (el && !other.has(el)) anchors(el, roots);
1582
1685
  };
1583
1686
  deepest(was, after);
1584
1687
  deepest(now, before);
1688
+ if (also) {
1689
+ flipped(also, null);
1690
+ anchors(also, roots);
1691
+ }
1585
1692
 
1586
1693
  // the roots' subtrees, which inherit from them and a descendant
1587
1694
  // combinator reaches, and their later siblings' too where a sibling
@@ -1626,8 +1733,10 @@ export class HtmlViewNode extends Node {
1626
1733
  * - The boxes take their new styles: an element's own, the text in it,
1627
1734
  * the anonymous boxes the fix-up made around what is in it, and its
1628
1735
  * `::before` and `::after`, styled again by their own rules. A box
1629
- * whose style is derived some other way — a marker, a first letter, a
1630
- * control — is not one to restyle here.
1736
+ * whose style is derived some other way — a marker, a first letter —
1737
+ * is not one to restyle here. A control's is, and its widget is told
1738
+ * (`_reportControls`), unless the page took to drawing the field or
1739
+ * stopped.
1631
1740
  * - Each layout of their text is made again from the runs it was made
1632
1741
  * from (`TextLayoutCache.inputsOf`) with the new ink (`runFor`), where
1633
1742
  * its geometry comes out the same.
@@ -1637,10 +1746,16 @@ export class HtmlViewNode extends Node {
1637
1746
  * costs what it changed, in a document of any length. True where it was
1638
1747
  * restyled here, or had nothing to restyle; false where the boxes have to
1639
1748
  * be built again.
1749
+ *
1750
+ * A focus change is answered the same way (`setFocus`): `before` and
1751
+ * `beforeFocus` are the states the change was from, and `pseudoRules`
1752
+ * which pseudo-elements' rules test the state that changed.
1640
1753
  */
1641
1754
  private _hoverInPlace(
1642
1755
  reach: ReadonlySet<Element>,
1643
- was: readonly Element[],
1756
+ before: PointerState,
1757
+ beforeFocus: FocusState,
1758
+ pseudoRules: 0 | 1 | 2,
1644
1759
  ): boolean {
1645
1760
  const cascade = this._cascade;
1646
1761
  const tree = this._tree;
@@ -1649,17 +1764,15 @@ export class HtmlViewNode extends Node {
1649
1764
  if (this._stale !== Stale.Nothing || this._laidOutWidth < 0) return false;
1650
1765
  if (reach.size > HOVER_RESTYLE_LIMIT) return false;
1651
1766
  // a first line's, a first letter's and a marker's styles are layout's
1652
- const pseudoRules = cascade.hoverPseudo;
1653
1767
  if (pseudoRules === HOVER_PSEUDO_OTHER) return false;
1654
1768
 
1655
- // whether the rules that test the pointer answer differently for an
1656
- // element than they did before the move
1657
- const before = { hovered: new Set(was), active: EMPTY_SET };
1769
+ // whether the rules that test the pointer or the focus answer
1770
+ // differently for an element than they did before the change
1658
1771
  const flips = new Map<Element, boolean>();
1659
1772
  const flipped = (el: Element): boolean => {
1660
1773
  let flip = flips.get(el);
1661
1774
  if (flip === undefined) {
1662
- flip = cascade.pointerChanged(el, before);
1775
+ flip = cascade.pointerChanged(el, before, beforeFocus);
1663
1776
  flips.set(el, flip);
1664
1777
  }
1665
1778
  return flip;
@@ -1677,6 +1790,8 @@ export class HtmlViewNode extends Node {
1677
1790
  let reaches = false;
1678
1791
  let reorders = false;
1679
1792
  const moving = new Set<Element>();
1793
+ /** Whether a control's box was restyled: its widget is told. */
1794
+ let widgets = false;
1680
1795
  const styleOf = (el: Element): ComputedStyle | null => {
1681
1796
  const kept = tree.styles.get(el);
1682
1797
  if (!kept) return null;
@@ -1729,7 +1844,7 @@ export class HtmlViewNode extends Node {
1729
1844
  const box = this._firstBoxesOf(tree).get(el);
1730
1845
  const was = box && SHAPE_STYLES.get(box);
1731
1846
  if (!box || was === undefined) continue;
1732
- const shapes = cascade.shapeStyles(el, style);
1847
+ const shapes = cascade.shapeStyles(el, style, tree.shapeCopies);
1733
1848
  if (shapes?.key !== was?.key) redrawn.push([box, shapes]);
1734
1849
  }
1735
1850
  }
@@ -1820,11 +1935,20 @@ export class HtmlViewNode extends Node {
1820
1935
  if (!style) continue;
1821
1936
  const kept = tree.styles.get(el)!.style;
1822
1937
  // a style derived from the element's, or drawn from it somewhere
1823
- // else — a widget mounted beside the document takes its look from
1824
- // it. An image, a drawing or a rule is drawn here, from its box,
1825
- // like any.
1938
+ // else. A widget mounted beside the document takes its look from
1939
+ // it, and is told again once it is restyled — unless the page took
1940
+ // to drawing the field, or stopped, which is another size of box.
1941
+ // An image, a drawing or a rule is drawn here, from its box, like
1942
+ // any.
1826
1943
  if (box.style !== kept) return false;
1827
- if (box.marker || WIDGETS.has(box.replaced)) return false;
1944
+ if (box.marker) return false;
1945
+ if (WIDGETS.has(box.replaced)) {
1946
+ const kind = box.replaced!;
1947
+ if (styledField(kind, kept) !== styledField(kind, style)) {
1948
+ return false;
1949
+ }
1950
+ widgets = true;
1951
+ }
1828
1952
  if (moving.has(el) && !movable(box, kept, style)) return false;
1829
1953
  ink = !quiet.has(el);
1830
1954
  } else if (above) {
@@ -1944,7 +2068,7 @@ export class HtmlViewNode extends Node {
1944
2068
  relayout = true;
1945
2069
  }
1946
2070
  }
1947
- if (moved.length) this._reportControls();
2071
+ if (moved.length || widgets) this._reportControls();
1948
2072
  if (relayout) {
1949
2073
  this._invalidate(Stale.Layout);
1950
2074
  return true;