@fusion.dev/helix-core 0.144.0 → 0.146.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 (262) hide show
  1. package/custom-elements.json +5724 -1404
  2. package/dist/controllers/aria-forward-controller/aria-forward-controller.d.ts +3 -2
  3. package/dist/controllers/aria-forward-controller/aria-forward-controller.js +4 -14
  4. package/dist/controllers/chart-pointer-controller/chart-pointer-controller.d.ts +65 -3
  5. package/dist/controllers/chart-pointer-controller/chart-pointer-controller.js +37 -9
  6. package/dist/controllers/chart-pointer-controller/reading-step.d.ts +7 -1
  7. package/dist/controllers/chart-pointer-controller/reading-step.js +5 -0
  8. package/dist/controllers/countdown-controller/countdown-controller.d.ts +55 -0
  9. package/dist/controllers/countdown-controller/countdown-controller.js +57 -0
  10. package/dist/controllers/keyboard-navigation-controller/keyboard-navigation-controller.d.ts +20 -57
  11. package/dist/controllers/keyboard-navigation-controller/keyboard-navigation-controller.js +11 -40
  12. package/dist/controllers/relative-clock-controller/relative-clock-controller.d.ts +44 -0
  13. package/dist/controllers/relative-clock-controller/relative-clock-controller.js +31 -0
  14. package/dist/controllers/source-controller/source-controller.d.ts +10 -3
  15. package/dist/controllers/source-controller/source-controller.js +7 -4
  16. package/dist/controllers/virtualize-controller/virtualize-controller.d.ts +116 -36
  17. package/dist/controllers/virtualize-controller/virtualize-controller.js +122 -12
  18. package/dist/events/action.d.ts +12 -0
  19. package/dist/events/action.js +6 -0
  20. package/dist/events/activate.d.ts +22 -0
  21. package/dist/events/activate.js +11 -0
  22. package/dist/events/aura-fallback.d.ts +12 -0
  23. package/dist/events/aura-fallback.js +11 -0
  24. package/dist/events/before-activate.d.ts +24 -0
  25. package/dist/events/before-activate.js +11 -0
  26. package/dist/events/before-change.d.ts +26 -0
  27. package/dist/events/before-change.js +11 -0
  28. package/dist/events/before-close.d.ts +6 -0
  29. package/dist/events/before-close.js +11 -0
  30. package/dist/events/before-collapse.d.ts +6 -0
  31. package/dist/events/before-collapse.js +11 -0
  32. package/dist/events/before-edit.d.ts +6 -0
  33. package/dist/events/before-edit.js +11 -0
  34. package/dist/events/before-move.d.ts +17 -0
  35. package/dist/events/before-move.js +11 -0
  36. package/dist/events/before-open.d.ts +17 -0
  37. package/dist/events/before-open.js +11 -0
  38. package/dist/events/before-panel-resize.d.ts +13 -0
  39. package/dist/events/before-panel-resize.js +11 -0
  40. package/dist/events/before-pin.d.ts +6 -0
  41. package/dist/events/before-pin.js +11 -0
  42. package/dist/events/before-reconnect.d.ts +14 -0
  43. package/dist/events/before-reconnect.js +11 -0
  44. package/dist/events/before-relabel.d.ts +15 -0
  45. package/dist/events/before-relabel.js +11 -0
  46. package/dist/events/before-resize.d.ts +6 -0
  47. package/dist/events/before-resize.js +11 -0
  48. package/dist/events/before-split.d.ts +12 -0
  49. package/dist/events/before-split.js +11 -0
  50. package/dist/events/before-toggle.d.ts +23 -0
  51. package/dist/events/before-toggle.js +11 -0
  52. package/dist/events/canvas-edit.d.ts +16 -0
  53. package/dist/events/canvas-edit.js +11 -0
  54. package/dist/events/canvas-pan.d.ts +13 -0
  55. package/dist/events/canvas-pan.js +11 -0
  56. package/dist/events/canvas-view.d.ts +14 -0
  57. package/dist/events/canvas-view.js +11 -0
  58. package/dist/events/canvas-zoom.d.ts +6 -0
  59. package/dist/events/canvas-zoom.js +11 -0
  60. package/dist/events/caret.d.ts +17 -0
  61. package/dist/events/caret.js +11 -0
  62. package/dist/events/clear.d.ts +11 -0
  63. package/dist/events/clear.js +6 -0
  64. package/dist/events/collapse.d.ts +12 -0
  65. package/dist/events/collapse.js +11 -0
  66. package/dist/events/complete.d.ts +26 -0
  67. package/dist/events/complete.js +11 -0
  68. package/dist/events/connect.d.ts +14 -0
  69. package/dist/events/connect.js +11 -0
  70. package/dist/events/console.d.ts +13 -0
  71. package/dist/events/console.js +11 -0
  72. package/dist/events/copy-error.d.ts +9 -0
  73. package/dist/events/copy-error.js +6 -0
  74. package/dist/events/copy.d.ts +12 -0
  75. package/dist/events/copy.js +10 -0
  76. package/dist/events/create.d.ts +18 -0
  77. package/dist/events/create.js +11 -0
  78. package/dist/events/details.d.ts +454 -0
  79. package/dist/events/details.js +1 -0
  80. package/dist/events/dismiss.d.ts +16 -0
  81. package/dist/events/dismiss.js +11 -0
  82. package/dist/events/edit.d.ts +12 -0
  83. package/dist/events/edit.js +11 -0
  84. package/dist/events/end.d.ts +9 -0
  85. package/dist/events/end.js +6 -0
  86. package/dist/events/events.d.ts +125 -1306
  87. package/dist/events/events.js +105 -739
  88. package/dist/events/expand.d.ts +10 -0
  89. package/dist/events/expand.js +11 -0
  90. package/dist/events/facet.d.ts +17 -0
  91. package/dist/events/facet.js +11 -0
  92. package/dist/events/filter.d.ts +12 -0
  93. package/dist/events/filter.js +11 -0
  94. package/dist/events/filtered.d.ts +12 -0
  95. package/dist/events/filtered.js +11 -0
  96. package/dist/events/graph-filter.d.ts +10 -0
  97. package/dist/events/graph-filter.js +11 -0
  98. package/dist/events/graph-layout.d.ts +10 -0
  99. package/dist/events/graph-layout.js +11 -0
  100. package/dist/events/graph-pan.d.ts +9 -0
  101. package/dist/events/graph-pan.js +11 -0
  102. package/dist/events/graph-part-change.d.ts +10 -0
  103. package/dist/events/graph-part-change.js +11 -0
  104. package/dist/events/graph-ready.d.ts +8 -0
  105. package/dist/events/graph-ready.js +6 -0
  106. package/dist/events/graph-signal.d.ts +11 -0
  107. package/dist/events/graph-signal.js +11 -0
  108. package/dist/events/graph-zoom.d.ts +6 -0
  109. package/dist/events/graph-zoom.js +11 -0
  110. package/dist/events/history.d.ts +16 -0
  111. package/dist/events/history.js +11 -0
  112. package/dist/events/load-more.d.ts +12 -0
  113. package/dist/events/load-more.js +6 -0
  114. package/dist/events/load.d.ts +12 -0
  115. package/dist/events/load.js +6 -0
  116. package/dist/events/map-cluster.d.ts +15 -0
  117. package/dist/events/map-cluster.js +11 -0
  118. package/dist/events/map-pan.d.ts +18 -0
  119. package/dist/events/map-pan.js +11 -0
  120. package/dist/events/map-part-change.d.ts +16 -0
  121. package/dist/events/map-part-change.js +11 -0
  122. package/dist/events/map-ready.d.ts +15 -0
  123. package/dist/events/map-ready.js +6 -0
  124. package/dist/events/map-rotate.d.ts +9 -0
  125. package/dist/events/map-rotate.js +11 -0
  126. package/dist/events/map-tilt.d.ts +9 -0
  127. package/dist/events/map-tilt.js +11 -0
  128. package/dist/events/map-zoom.d.ts +13 -0
  129. package/dist/events/map-zoom.js +11 -0
  130. package/dist/events/move.d.ts +11 -0
  131. package/dist/events/move.js +11 -0
  132. package/dist/events/names.d.ts +15 -0
  133. package/dist/events/names.js +88 -0
  134. package/dist/events/navigate.d.ts +13 -0
  135. package/dist/events/navigate.js +11 -0
  136. package/dist/events/next.d.ts +23 -0
  137. package/dist/events/next.js +11 -0
  138. package/dist/events/panel-resize.d.ts +12 -0
  139. package/dist/events/panel-resize.js +11 -0
  140. package/dist/events/pin.d.ts +12 -0
  141. package/dist/events/pin.js +11 -0
  142. package/dist/events/press.d.ts +19 -0
  143. package/dist/events/press.js +11 -0
  144. package/dist/events/preview.d.ts +14 -0
  145. package/dist/events/preview.js +11 -0
  146. package/dist/events/previous.d.ts +6 -0
  147. package/dist/events/previous.js +11 -0
  148. package/dist/events/range-change.d.ts +10 -0
  149. package/dist/events/range-change.js +11 -0
  150. package/dist/events/read.d.ts +19 -0
  151. package/dist/events/read.js +11 -0
  152. package/dist/events/reconnect.d.ts +6 -0
  153. package/dist/events/reconnect.js +11 -0
  154. package/dist/events/reject.d.ts +13 -0
  155. package/dist/events/reject.js +11 -0
  156. package/dist/events/relabel.d.ts +6 -0
  157. package/dist/events/relabel.js +11 -0
  158. package/dist/events/remove.d.ts +12 -0
  159. package/dist/events/remove.js +11 -0
  160. package/dist/events/reorder.d.ts +18 -0
  161. package/dist/events/reorder.js +11 -0
  162. package/dist/events/reordered.d.ts +12 -0
  163. package/dist/events/reordered.js +11 -0
  164. package/dist/events/resize.d.ts +19 -0
  165. package/dist/events/resize.js +11 -0
  166. package/dist/events/resized.d.ts +16 -0
  167. package/dist/events/resized.js +11 -0
  168. package/dist/events/sample-end.d.ts +5 -0
  169. package/dist/events/sample-end.js +6 -0
  170. package/dist/events/sample-start.d.ts +18 -0
  171. package/dist/events/sample-start.js +6 -0
  172. package/dist/events/scrolled.d.ts +32 -0
  173. package/dist/events/scrolled.js +11 -0
  174. package/dist/events/select.d.ts +12 -0
  175. package/dist/events/select.js +11 -0
  176. package/dist/events/selected.d.ts +12 -0
  177. package/dist/events/selected.js +11 -0
  178. package/dist/events/send.d.ts +18 -0
  179. package/dist/events/send.js +11 -0
  180. package/dist/events/shortcut.d.ts +13 -0
  181. package/dist/events/shortcut.js +11 -0
  182. package/dist/events/sort.d.ts +17 -0
  183. package/dist/events/sort.js +11 -0
  184. package/dist/events/sorted.d.ts +15 -0
  185. package/dist/events/sorted.js +11 -0
  186. package/dist/events/split.d.ts +12 -0
  187. package/dist/events/split.js +11 -0
  188. package/dist/events/toggle.d.ts +15 -0
  189. package/dist/events/toggle.js +11 -0
  190. package/dist/events/tour-ask.d.ts +16 -0
  191. package/dist/events/tour-ask.js +11 -0
  192. package/dist/events/tour-status.d.ts +14 -0
  193. package/dist/events/tour-status.js +11 -0
  194. package/dist/events/typing.d.ts +10 -0
  195. package/dist/events/typing.js +11 -0
  196. package/dist/helix-element/helix-element.js +14 -1
  197. package/dist/helix-element/react-bridge.d.ts +19 -4
  198. package/dist/helix-element/react-bridge.js +15 -2
  199. package/dist/helix-form-element/helix-form-element.d.ts +5 -20
  200. package/dist/helix-form-element/helix-form-element.js +4 -56
  201. package/dist/index.d.ts +17 -3
  202. package/dist/index.js +17 -3
  203. package/dist/mixins/mixin-chart-source/mixin-chart-source.d.ts +17 -2
  204. package/dist/mixins/mixin-chart-source/mixin-chart-source.js +22 -1
  205. package/dist/mixins/mixin-controller-active/mixin-controller-active.js +4 -3
  206. package/dist/mixins/mixin-controller-checked/mixin-controller-checked.d.ts +5 -2
  207. package/dist/mixins/mixin-controller-checked/mixin-controller-checked.js +2 -1
  208. package/dist/mixins/mixin-controller-value/mixin-controller-value.d.ts +4 -2
  209. package/dist/mixins/mixin-controller-value/mixin-controller-value.js +7 -2
  210. package/dist/mixins/mixin-dismissible/mixin-dismissible.js +20 -4
  211. package/dist/mixins/mixin-field/mixin-field.js +2 -2
  212. package/dist/mixins/mixin-picker-field/mixin-picker-field.d.ts +15 -3
  213. package/dist/mixins/mixin-picker-field/mixin-picker-field.js +11 -4
  214. package/dist/mixins/mixin-segmented-field/mixin-segmented-field.d.ts +1 -1
  215. package/dist/mixins/mixin-segmented-field/mixin-segmented-field.js +10 -4
  216. package/dist/utils/audit-name/audit-name-off.d.ts +10 -0
  217. package/dist/utils/audit-name/audit-name-off.js +1 -0
  218. package/dist/utils/audit-name/audit-name.d.ts +19 -6
  219. package/dist/utils/audit-name/audit-name.js +10 -27
  220. package/dist/utils/calendar/calendar.js +7 -7
  221. package/dist/utils/draw-icon/draw-icon.d.ts +7 -6
  222. package/dist/utils/draw-tooltip/draw-tooltip.d.ts +1 -1
  223. package/dist/utils/draw-tooltip/draw-tooltip.js +3 -1
  224. package/dist/utils/draw-tooltip/spoken-reading.d.ts +6 -2
  225. package/dist/utils/draw-tooltip/spoken-reading.js +5 -4
  226. package/dist/utils/heading-level/heading-level.d.ts +8 -0
  227. package/dist/utils/heading-level/heading-level.js +4 -0
  228. package/dist/utils/implicit-submit/implicit-submit.d.ts +25 -0
  229. package/dist/utils/implicit-submit/implicit-submit.js +52 -0
  230. package/dist/utils/name-text/name-text.d.ts +12 -0
  231. package/dist/utils/name-text/name-text.js +25 -0
  232. package/dist/utils/number-format/number-format.d.ts +2 -1
  233. package/dist/utils/number-format/number-format.js +13 -5
  234. package/dist/utils/plain-click/plain-click.d.ts +10 -0
  235. package/dist/utils/plain-click/plain-click.js +1 -0
  236. package/dist/utils/press-type/press-type.d.ts +20 -0
  237. package/dist/utils/press-type/press-type.js +15 -0
  238. package/dist/utils/range-add/range-add.d.ts +19 -0
  239. package/dist/utils/range-add/range-add.js +5 -0
  240. package/dist/utils/reflect-elements/reflect-elements.d.ts +3 -3
  241. package/dist/utils/report-runtime/report-runtime.d.ts +9 -4
  242. package/dist/utils/row-data/row-data.d.ts +15 -0
  243. package/dist/utils/row-data/row-data.js +17 -0
  244. package/dist/utils/row-order/row-order.d.ts +141 -0
  245. package/dist/utils/row-order/row-order.js +76 -0
  246. package/dist/utils/tell-disclosure/tell-disclosure.d.ts +4 -0
  247. package/dist/utils/tree-arrow/tree-arrow.d.ts +23 -0
  248. package/dist/utils/tree-arrow/tree-arrow.js +9 -0
  249. package/dist/utils/typeahead/typeahead.d.ts +67 -0
  250. package/dist/utils/typeahead/typeahead.js +36 -0
  251. package/dist/utils/value-as/value-as.d.ts +9 -0
  252. package/dist/utils/value-as/value-as.js +9 -1
  253. package/dist/utils/watch-labels/watch-labels.d.ts +16 -0
  254. package/dist/utils/watch-labels/watch-labels.js +34 -0
  255. package/dist/utils/words-converter/words-converter.d.ts +14 -0
  256. package/dist/utils/words-converter/words-converter.js +15 -0
  257. package/dist/utils/write-time/write-time.d.ts +4 -4
  258. package/dist/utils/written-list/written-list.d.ts +29 -0
  259. package/dist/utils/written-list/written-list.js +30 -0
  260. package/dist/version.d.ts +1 -1
  261. package/dist/version.js +1 -1
  262. package/package.json +18 -5
@@ -0,0 +1,31 @@
1
+ import { subscribe } from '../../utils/ticker/ticker.js';
2
+ export class RelativeClockController {
3
+ constructor(host, options) {
4
+ this.host = host;
5
+ this.options = options;
6
+ this.now = Date.now();
7
+ this.stop = undefined;
8
+ host.addController(this);
9
+ }
10
+ hostConnected() {
11
+ this.tick();
12
+ }
13
+ hostDisconnected() {
14
+ this.stop?.();
15
+ this.stop = undefined;
16
+ }
17
+ tick() {
18
+ this.stop?.();
19
+ this.stop = undefined;
20
+ const now = Date.now();
21
+ const next = this.options.expires(new Date(now));
22
+ if (next === undefined) {
23
+ return;
24
+ }
25
+ this.now = now;
26
+ this.host.requestUpdate();
27
+ if (Number.isFinite(next) && this.host.isConnected) {
28
+ this.stop = subscribe(next, () => this.tick());
29
+ }
30
+ }
31
+ }
@@ -107,13 +107,20 @@ export declare class SourceController implements ReactiveController {
107
107
  get hidden(): ReadonlySet<string>;
108
108
  /**
109
109
  * Hides these series on the table and shows every other, for every chart
110
- * and legend reading it, which then redraw.
110
+ * and legend reading it, which then redraw. Answers the table it wrote to,
111
+ * or null where `for` names none.
112
+ *
113
+ * Given `among`, only those names are shown or hidden, and a series hidden
114
+ * outside them stays hidden. A legend passes its own entries, so a legend
115
+ * of the rows and a legend of the columns on one table each keep what the
116
+ * other hid.
111
117
  *
112
118
  * Names are matched without regard to case or surrounding space, as a
113
119
  * column's heading is everywhere else. Nothing happens where `for` names no
114
- * table, or the set is already this one.
120
+ * table, or the set is already this one, so two writers that agree stop
121
+ * asking each other to redraw.
115
122
  */
116
- hide(names: Iterable<string>): void;
123
+ hide(names: Iterable<string>, among?: Iterable<string>): Element | null;
117
124
  /** Counts this controller among the readers of the table it watches, or of none. */
118
125
  private enlist;
119
126
  hostConnected(): void;
@@ -54,15 +54,17 @@ export class SourceController {
54
54
  const element = this.element;
55
55
  return element ? (HIDDEN.get(element) ?? NONE) : NONE;
56
56
  }
57
- hide(names) {
57
+ hide(names, among) {
58
58
  const element = this.resolve();
59
59
  if (!element) {
60
- return;
60
+ return null;
61
61
  }
62
- const next = new Set([...names].map(seriesKey).filter(Boolean));
63
62
  const now = HIDDEN.get(element) ?? NONE;
63
+ const mine = among === undefined ? undefined : new Set([...among].map(seriesKey));
64
+ const kept = mine === undefined ? [] : [...now].filter((one) => !mine.has(one));
65
+ const next = new Set([...kept, ...[...names].map(seriesKey)].filter(Boolean));
64
66
  if (next.size === now.size && [...next].every((one) => now.has(one))) {
65
- return;
67
+ return element;
66
68
  }
67
69
  if (next.size === 0) {
68
70
  HIDDEN.delete(element);
@@ -73,6 +75,7 @@ export class SourceController {
73
75
  for (const reader of READERS.get(element) ?? []) {
74
76
  reader.host.requestUpdate();
75
77
  }
78
+ return element;
76
79
  }
77
80
  enlist(element) {
78
81
  if (this.watched) {
@@ -20,7 +20,9 @@ export interface VirtualizeOptions {
20
20
  * the straight line the note below defends. A host that returns its row
21
21
  * elements gets each one measured after it renders and placed by what it
22
22
  * came to; a host that returns nothing gets every item at `estimate`, which
23
- * is right whenever the rows really are uniform and is cheaper.
23
+ * is right whenever the rows really are uniform and is cheaper. A host that
24
+ * returns its rows and turns `measured` off keeps the straight line, with its
25
+ * slope read once from the rows it drew.
24
26
  *
25
27
  * **Each row carries its own index rather than being counted off from
26
28
  * `window.first`.** A host is free to place its rows whenever it likes, and
@@ -63,6 +65,16 @@ export interface Window {
63
65
  /** Index one past the last item rendered. */
64
66
  last: number;
65
67
  }
68
+ /** Where `reveal` puts an item, and how it gets there. */
69
+ export interface RevealOptions {
70
+ /**
71
+ * Where the item lands: the least distance that shows it whole (`nearest`,
72
+ * the default), or at the box's `start`, `center` or `end`.
73
+ */
74
+ align?: 'center' | 'end' | 'nearest' | 'start';
75
+ /** The platform's scroll behavior. Instant when left out. */
76
+ behavior?: ScrollBehavior;
77
+ }
66
78
  /**
67
79
  * Works out which slice of a long set is worth rendering.
68
80
  *
@@ -86,35 +98,14 @@ export interface Window {
86
98
  * track, so the room the absent rows would have taken is left as padding before
87
99
  * and after. A reader dragging that thumb is dragging against the real length.
88
100
  *
89
- * **What it does not do, and the reason is narrower than it first looks.** True
90
- * virtualization means never creating the rows that are not on screen, and that
91
- * needs the set as *data*. Two channels are shut: a consumer's items arrive as
92
- * light-DOM children, which exist by the time this could decide not to create
93
- * them, and an `items` property cannot survive React hydration, which skips
94
- * every property it would have to apply imperatively (see `bindReactHandler`,
95
- * and the note in the rules that objects and functions have that bug and are
96
- * not fixed).
97
- *
98
- * **A third channel is open.** Data as *text*
99
- * in the markup, a `<script type="application/json">` child or an attribute, is
100
- * neither a property nor a node per row: React's hydration bug cannot touch text
101
- * it renders as a child, a server emits it, and the node count is one. The
102
- * charts already read data out of the document this way, `for="<id>"` at a real
103
- * `<table>`, so the pattern is in the library rather than being invented for
104
- * this.
105
- *
106
- * So the thing actually blocked is the *row template*. Turning an item into
107
- * markup wants either a render function, which is a function property and dies
108
- * on the same bug, or a `<template>` with named holes, which survives because it
109
- * is markup and which no component here asks anyone to write. That is a decision
110
- * about what this library is willing to look like, not a platform limit, and it
111
- * is open at #71.
112
- *
113
- * So what this buys is the expensive half rather than all of it: the rows
114
- * outside the window are not laid out and not painted, which is where the time
115
- * goes in a long list, and the node count stays. That is worth having and it is
116
- * not worth calling something it is not. An element that windows children it
117
- * was handed is not a virtual list, whatever it is named.
101
+ * **The set arrives as data**, which is what lets a host create only the rows
102
+ * in the window. A host takes it through a property (an `items` array, which
103
+ * `applyReactProperties` carries across React hydration with every other object
104
+ * property) or as text in the markup (a `<script type="application/json">`
105
+ * named by `for`, which a server emits and which is one node whatever the
106
+ * row count). A host that is handed its rows as light-DOM children still gets
107
+ * the expensive half from this: the rows outside the window are not laid out
108
+ * and not painted, and the node count stays.
118
109
  *
119
110
  * **One estimate, not per-row measurement.** Measuring every row to place it is
120
111
  * how a virtual list ends up reading the layout it is trying to avoid, and it
@@ -140,6 +131,15 @@ export declare class VirtualizeController implements ReactiveController {
140
131
  private readonly heights;
141
132
  /** Room `shift` added above the window, owed to the scroll offset once drawn. */
142
133
  private owed;
134
+ /**
135
+ * The drawn rows' height on the straight line, read once per estimate.
136
+ *
137
+ * Undefined until a host that offers `rows` has drawn some on the uniform
138
+ * path, and again after `forget`.
139
+ */
140
+ private sample;
141
+ /** The length of the set when `endInSight` last answered true. */
142
+ private askedAt;
143
143
  constructor(host: ReactiveControllerHost, options: VirtualizeOptions);
144
144
  hostConnected(): void;
145
145
  hostDisconnected(): void;
@@ -156,6 +156,28 @@ export declare class VirtualizeController implements ReactiveController {
156
156
  measure(): void;
157
157
  /** The straight line: one estimate, so offset and index convert by division. */
158
158
  private uniform;
159
+ /**
160
+ * How tall one item is on the straight line: the drawn rows' height once
161
+ * read, and `estimate` until then.
162
+ *
163
+ * **An estimate that is wrong on the straight line leaves the end of the
164
+ * set out of reach.** Rows of 21 pixels placed at 32 fill two thirds of the
165
+ * room they are counted for, so the window stops short of the bottom of the
166
+ * box and the spacer after it holds the rest: in Chromium, `scrollToIndex`
167
+ * on the last of 5,000 such rows scrolled to the end and drew nothing there.
168
+ * Reading the drawn rows once corrects the slope, and it is still a line.
169
+ */
170
+ private line;
171
+ /**
172
+ * Reads the drawn rows' height once for the straight line, for a host that
173
+ * offers `rows` and does not measure.
174
+ *
175
+ * The mean of the rows drawn, each item's elements counted together. A row
176
+ * with no height, such as one that is not laid out, says nothing. When the
177
+ * line moves, the scroll offset moves with it, so the item at the top of the
178
+ * box stays there.
179
+ */
180
+ private sampleRows;
159
181
  /** Whether this pass places by measurement: offered, and wanted. */
160
182
  private measuring;
161
183
  /**
@@ -166,6 +188,53 @@ export declare class VirtualizeController implements ReactiveController {
166
188
  * on screen plus the overscan, whatever the set's length.
167
189
  */
168
190
  private placed;
191
+ /**
192
+ * The items on screen, with no overscan: `first` is the item at the top of
193
+ * the box and `last` is one past the item at its bottom, so
194
+ * `items.slice(first, last)` is what a reader sees.
195
+ *
196
+ * For a host that reports what is visible, where `window` is what is drawn.
197
+ * An empty span (`first === last`) while the box has no height or the set
198
+ * no items.
199
+ */
200
+ visible(): {
201
+ first: number;
202
+ last: number;
203
+ };
204
+ /**
205
+ * How much of the set lies below the bottom of the box, in pixels.
206
+ *
207
+ * For a host that asks for more items as a reader nears the end. Read from
208
+ * the arithmetic and not from `scrollHeight`, which also counts whatever the
209
+ * host draws around the rows.
210
+ */
211
+ remaining(): number;
212
+ /**
213
+ * Whether a reader has come within one screen of the end of the set, true
214
+ * once for each length of it.
215
+ *
216
+ * For a host that asks for more items as a reader nears the end: it sends
217
+ * its request when this answers true, and the set growing lets it answer
218
+ * true again. False while the box has no height or the set no items, so a
219
+ * list that has not been laid out asks for nothing.
220
+ *
221
+ * `length` is what the answer is kept for, the count by default. A host
222
+ * whose count is a filtered view of what it holds passes what it holds, so
223
+ * new items that the filter hides still let it ask again.
224
+ */
225
+ endInSight(length?: number): boolean;
226
+ /**
227
+ * The two spacers' heights for the window, with item `kept` drawn too.
228
+ *
229
+ * For a host that keeps an item outside the window drawn, the row holding
230
+ * focus say. The item takes its own room out of the spacer on its side, so
231
+ * the scrollbar still measures the set. Every host that keeps a focused row
232
+ * asks this, so the arithmetic is written once.
233
+ */
234
+ spacers(kept?: number): {
235
+ after: number;
236
+ before: number;
237
+ };
169
238
  /**
170
239
  * Measures what was just drawn, and holds the reader still if it moved.
171
240
  *
@@ -206,14 +275,25 @@ export declare class VirtualizeController implements ReactiveController {
206
275
  */
207
276
  sizeOf(index: number): number;
208
277
  /**
209
- * Scrolls the box the least distance that shows item `index` whole.
278
+ * Scrolls the box to show item `index`.
210
279
  *
211
- * For a host moving focus to an item outside the window, whose own element
212
- * cannot be scrolled to because it is not drawn where it belongs. `inset` is
213
- * how far into the box the items start, for a heading that sticks at its
214
- * top and covers that much of every item scrolled under it.
280
+ * By default the least distance that shows it whole, for a host moving focus
281
+ * to an item outside the window, whose own element cannot be scrolled to
282
+ * because it is not drawn where it belongs. `inset` is how far into the box
283
+ * the items start, for a heading that sticks at its top and covers that much
284
+ * of every item scrolled under it.
285
+ *
286
+ * `align` puts the item at the box's `start`, `center` or `end` instead, and
287
+ * `behavior` is the platform's, for a host whose consumer asked to be taken
288
+ * to an item. A smooth scroll moves the window through the box's own
289
+ * `scroll` events, the same as a reader scrolling.
290
+ */
291
+ reveal(index: number, inset?: number, { align, behavior }?: RevealOptions): void;
292
+ /**
293
+ * Where item `index` starts along the scroll axis, in pixels from the top of
294
+ * the set: what was measured above it, or the estimate.
215
295
  */
216
- reveal(index: number, inset?: number): void;
296
+ offsetOf(index: number): number;
217
297
  /**
218
298
  * Drops every measurement, for a host that swapped its data.
219
299
  *
@@ -8,6 +8,7 @@ export class VirtualizeController {
8
8
  this.warned = false;
9
9
  this.window = { after: 0, before: 0, first: 0, last: 0 };
10
10
  this.owed = 0;
11
+ this.askedAt = -1;
11
12
  this.handleScroll = () => {
12
13
  this.settling = 0;
13
14
  this.measure();
@@ -39,13 +40,14 @@ export class VirtualizeController {
39
40
  }
40
41
  this.observer.observe(box);
41
42
  box.addEventListener('scroll', this.handleScroll, { passive: true });
43
+ box.setAttribute('data-virtual', '');
42
44
  this.watching = true;
43
45
  }
44
46
  measure() {
45
47
  this.watch();
46
48
  const box = this.options.container();
47
49
  const total = Math.max(0, this.options.count());
48
- const size = Math.max(1, this.options.estimate());
50
+ const size = this.line();
49
51
  if (!box) {
50
52
  return;
51
53
  }
@@ -79,6 +81,36 @@ export class VirtualizeController {
79
81
  const last = Math.min(total, first + fits);
80
82
  return { after: Math.max(0, (total - last) * size), before: first * size, first, last };
81
83
  }
84
+ line() {
85
+ const estimate = Math.max(1, this.options.estimate());
86
+ return this.sample?.estimate === estimate ? this.sample.size : estimate;
87
+ }
88
+ sampleRows() {
89
+ const estimate = Math.max(1, this.options.estimate());
90
+ if (this.sample?.estimate === estimate) {
91
+ return;
92
+ }
93
+ const heights = new Map();
94
+ for (const { element, index } of this.options.rows?.() ?? []) {
95
+ heights.set(index, (heights.get(index) ?? 0) + element.getBoundingClientRect().height);
96
+ }
97
+ const seen = [...heights.values()].filter((height) => height > 0);
98
+ if (seen.length === 0) {
99
+ return;
100
+ }
101
+ const was = this.line();
102
+ const size = Math.max(1, seen.reduce((sum, height) => sum + height, 0) / seen.length);
103
+ this.sample = { estimate, size };
104
+ if (Math.abs(size - was) < 0.5) {
105
+ return;
106
+ }
107
+ const box = this.options.container();
108
+ if (box && box.scrollTop > 0) {
109
+ box.scrollTop = (box.scrollTop / was) * size;
110
+ }
111
+ this.settling = 0;
112
+ this.measure();
113
+ }
82
114
  measuring() {
83
115
  return this.options.rows !== undefined && (this.options.measured?.() ?? true);
84
116
  }
@@ -97,6 +129,58 @@ export class VirtualizeController {
97
129
  last,
98
130
  };
99
131
  }
132
+ visible() {
133
+ const box = this.options.container();
134
+ const total = Math.max(0, this.options.count());
135
+ if (!box || total === 0) {
136
+ return { first: 0, last: 0 };
137
+ }
138
+ const top = Math.max(0, box.scrollTop);
139
+ const bottom = top + box.clientHeight;
140
+ if (!this.measuring()) {
141
+ const size = this.line();
142
+ const first = Math.min(Math.floor(top / size), total);
143
+ return { first, last: Math.max(first, Math.min(total, Math.ceil(bottom / size))) };
144
+ }
145
+ const first = this.heights.indexAt(top, total);
146
+ let last = first;
147
+ while (last < total && this.heights.before(last, total) < bottom) {
148
+ last += 1;
149
+ }
150
+ return { first, last };
151
+ }
152
+ remaining() {
153
+ const box = this.options.container();
154
+ const total = Math.max(0, this.options.count());
155
+ if (!box) {
156
+ return 0;
157
+ }
158
+ const length = this.measuring() ? this.heights.total(total) : total * this.line();
159
+ return Math.max(0, length - Math.max(0, box.scrollTop) - box.clientHeight);
160
+ }
161
+ endInSight(length = Math.max(0, this.options.count())) {
162
+ const screen = this.options.container()?.clientHeight ?? 0;
163
+ if (length === 0 ||
164
+ screen <= 0 ||
165
+ this.askedAt === length ||
166
+ this.options.count() <= 0 ||
167
+ this.remaining() > screen) {
168
+ return false;
169
+ }
170
+ this.askedAt = length;
171
+ return true;
172
+ }
173
+ spacers(kept) {
174
+ const { after, before, first, last } = this.window;
175
+ if (kept === undefined || (kept >= first && kept < last)) {
176
+ return { after, before };
177
+ }
178
+ const room = this.sizeOf(kept);
179
+ return {
180
+ after: kept >= last ? Math.max(0, after - room) : after,
181
+ before: kept < first ? Math.max(0, before - room) : before,
182
+ };
183
+ }
100
184
  hostUpdated() {
101
185
  if (this.owed !== 0) {
102
186
  const box = this.options.container();
@@ -106,6 +190,7 @@ export class VirtualizeController {
106
190
  this.owed = 0;
107
191
  }
108
192
  if (!this.measuring()) {
193
+ this.sampleRows();
109
194
  return;
110
195
  }
111
196
  const rows = this.options.rows?.();
@@ -147,27 +232,52 @@ export class VirtualizeController {
147
232
  this.owed += room;
148
233
  }
149
234
  sizeOf(index) {
150
- return this.measuring() ? this.heights.at(index) : Math.max(1, this.options.estimate());
235
+ return this.measuring() ? this.heights.at(index) : this.line();
151
236
  }
152
- reveal(index, inset = 0) {
237
+ reveal(index, inset = 0, { align = 'nearest', behavior } = {}) {
153
238
  const box = this.options.container();
154
239
  if (!box) {
155
240
  return;
156
241
  }
157
- const total = Math.max(0, this.options.count());
158
- const top = this.measuring()
159
- ? this.heights.before(index, total)
160
- : index * Math.max(1, this.options.estimate());
161
- const bottom = top + this.sizeOf(index) + inset;
162
- if (top < box.scrollTop) {
163
- box.scrollTop = top;
242
+ const top = this.offsetOf(index);
243
+ const size = this.sizeOf(index);
244
+ const room = box.clientHeight - inset;
245
+ let to;
246
+ if (align === 'start') {
247
+ to = top;
248
+ }
249
+ else if (align === 'center') {
250
+ to = top - inset - (room - size) / 2;
251
+ }
252
+ else if (align === 'end') {
253
+ to = top + size + inset - box.clientHeight;
164
254
  }
165
- else if (bottom > box.scrollTop + box.clientHeight) {
166
- box.scrollTop = bottom - box.clientHeight;
255
+ else if (top < box.scrollTop) {
256
+ to = top;
257
+ }
258
+ else if (top + size + inset > box.scrollTop + box.clientHeight) {
259
+ to = top + size + inset - box.clientHeight;
260
+ }
261
+ else {
262
+ return;
167
263
  }
264
+ const next = Math.max(0, to);
265
+ if (behavior && typeof box.scrollTo === 'function') {
266
+ box.scrollTo({ behavior, top: next });
267
+ }
268
+ else {
269
+ box.scrollTop = next;
270
+ }
271
+ }
272
+ offsetOf(index) {
273
+ const total = Math.max(0, this.options.count());
274
+ return this.measuring()
275
+ ? this.heights.before(index, total)
276
+ : Math.max(0, Math.min(index, total)) * this.line();
168
277
  }
169
278
  forget() {
170
279
  this.heights.forget();
280
+ this.sample = undefined;
171
281
  }
172
282
  report() {
173
283
  if (this.warned) {
@@ -0,0 +1,12 @@
1
+ /**
2
+ * A reader acted on a row: Enter on a list row, or a press in a list that
3
+ * selects nothing. Sent from the row, so `target` is the row acted on.
4
+ *
5
+ * Carries nothing, because the row is the whole of the answer. Not cancelable:
6
+ * a row with a link follows it and sends nothing, so there is no default left
7
+ * to refuse.
8
+ */
9
+ export declare class HelixActionEvent extends Event {
10
+ static readonly eventName = "helix-action";
11
+ constructor();
12
+ }
@@ -0,0 +1,6 @@
1
+ export class HelixActionEvent extends Event {
2
+ static { this.eventName = 'helix-action'; }
3
+ constructor() {
4
+ super(HelixActionEvent.eventName, { bubbles: true, cancelable: false, composed: true });
5
+ }
6
+ }
@@ -0,0 +1,22 @@
1
+ import type { ActivateDetail } from './details.js';
2
+ /**
3
+ * A set has made a different child current.
4
+ *
5
+ * The notification half of `helix-before-activate`, dispatched after the state
6
+ * has moved, so `currentTarget.active` is the new value and the detail is a fact
7
+ * rather than a proposal. `previous` is the value that was current before, where
8
+ * the set knows it, as Web Awesome's `wa-video-change` carries both.
9
+ *
10
+ * **Not cancelable**, for the reason every notification here is not: the
11
+ * decision was taken by the event before it, and a second refusable event for
12
+ * one transition is two answers to one question.
13
+ *
14
+ * **Prefixed, and it has to be.** `activate` unprefixed is not in any
15
+ * `*EventMap` in the DOM library, checked rather than assumed, but the prefix is
16
+ * what stops an event dispatched inside a shadow root and retargeted to the host
17
+ * being mistaken for something the host's own contents sent.
18
+ */
19
+ export declare class HelixActivateEvent extends CustomEvent<ActivateDetail> {
20
+ static readonly eventName = "helix-activate";
21
+ constructor(detail: ActivateDetail);
22
+ }
@@ -0,0 +1,11 @@
1
+ export class HelixActivateEvent extends CustomEvent {
2
+ static { this.eventName = 'helix-activate'; }
3
+ constructor(detail) {
4
+ super(HelixActivateEvent.eventName, {
5
+ bubbles: true,
6
+ cancelable: false,
7
+ composed: true,
8
+ detail,
9
+ });
10
+ }
11
+ }
@@ -0,0 +1,12 @@
1
+ import type { AuraFallbackDetail } from './details.js';
2
+ /**
3
+ * An aura stopped drawing its shader and shows its gradient, and says why.
4
+ *
5
+ * Sent each time it falls back, from the aura, which then matches
6
+ * `:state(fallback)` until it draws again. Not cancelable: there is nothing
7
+ * left to refuse.
8
+ */
9
+ export declare class HelixAuraFallbackEvent extends CustomEvent<AuraFallbackDetail> {
10
+ static readonly eventName = "helix-aura-fallback";
11
+ constructor(detail: AuraFallbackDetail);
12
+ }
@@ -0,0 +1,11 @@
1
+ export class HelixAuraFallbackEvent extends CustomEvent {
2
+ static { this.eventName = 'helix-aura-fallback'; }
3
+ constructor(detail) {
4
+ super(HelixAuraFallbackEvent.eventName, {
5
+ bubbles: true,
6
+ cancelable: false,
7
+ composed: true,
8
+ detail,
9
+ });
10
+ }
11
+ }
@@ -0,0 +1,24 @@
1
+ import type { ChangeDetail } from './details.js';
2
+ /**
3
+ * A set is about to make a different child current, and can be refused.
4
+ *
5
+ * **Not a generic `helix-change`.** `Tabs` moving to another tab, `Tree` marking
6
+ * another row and `Steps` advancing are not a value changing, they are a set
7
+ * choosing, and the platform's word for a committed value would say the wrong
8
+ * thing. `active` names which child is current, so `activate` is the verb, and
9
+ * unlike `change` the platform has no claim on it.
10
+ *
11
+ * **This fires before the state moves.** Inside a handler `currentTarget.active`
12
+ * still holds the previous value, and `detail.value` is a proposal another
13
+ * listener may refuse, so a consumer who only wants to know which child is
14
+ * current listens for `helix-activate` instead. `helix-select` is not that
15
+ * notification either: it fires when an item is *chosen*, not when `active` is
16
+ * assigned.
17
+ *
18
+ * The shape is the one `MixinControllerOpen` already has, a cancelable request
19
+ * and a plain notification after the state has moved.
20
+ */
21
+ export declare class HelixBeforeActivateEvent extends CustomEvent<ChangeDetail> {
22
+ static readonly eventName = "helix-before-activate";
23
+ constructor(detail: ChangeDetail);
24
+ }
@@ -0,0 +1,11 @@
1
+ export class HelixBeforeActivateEvent extends CustomEvent {
2
+ static { this.eventName = 'helix-before-activate'; }
3
+ constructor(detail) {
4
+ super(HelixBeforeActivateEvent.eventName, {
5
+ bubbles: true,
6
+ cancelable: true,
7
+ composed: true,
8
+ detail,
9
+ });
10
+ }
11
+ }
@@ -0,0 +1,26 @@
1
+ import type { ChangeDetail } from './details.js';
2
+ /**
3
+ * A value is about to change, and the change can be refused.
4
+ *
5
+ * **The request half of every value control.** It announces the value being
6
+ * *proposed*, before anything moves, so a listener calling `preventDefault()`
7
+ * refuses it and the control stays where it was. That is how a consumer takes
8
+ * one decision without declaring a mode, which is what 294 settled: the element
9
+ * always owns and applies its state, and refusing is the way to take it over.
10
+ *
11
+ * **The notification is native and separate.** Once this has gone out and the
12
+ * value has moved, the control announces it again under the platform's own
13
+ * name, `input` while a reader is editing and `change` when they are done, so
14
+ * `onChange` in React and `onchange=` in HTML both reach it and
15
+ * `currentTarget.value` is correct by the time a handler reads it. This event
16
+ * carries the detail because at the moment it fires the value is not on the
17
+ * element yet.
18
+ *
19
+ * **`announcesInput` picks which native notification fires, not which name this
20
+ * carries.** Granularity is the platform's own distinction, `input` against
21
+ * `change`; the request is always this one event.
22
+ */
23
+ export declare class HelixBeforeChangeEvent extends CustomEvent<ChangeDetail> {
24
+ static readonly eventName = "helix-before-change";
25
+ constructor(detail: ChangeDetail);
26
+ }
@@ -0,0 +1,11 @@
1
+ export class HelixBeforeChangeEvent extends CustomEvent {
2
+ static { this.eventName = 'helix-before-change'; }
3
+ constructor(detail) {
4
+ super(HelixBeforeChangeEvent.eventName, {
5
+ bubbles: true,
6
+ cancelable: true,
7
+ composed: true,
8
+ detail,
9
+ });
10
+ }
11
+ }
@@ -0,0 +1,6 @@
1
+ import type { OpenDetail } from './details.js';
2
+ /** Fired when an element is about to close. See `HelixBeforeOpenEvent`. */
3
+ export declare class HelixBeforeCloseEvent extends CustomEvent<OpenDetail> {
4
+ static readonly eventName = "helix-before-close";
5
+ constructor();
6
+ }
@@ -0,0 +1,11 @@
1
+ export class HelixBeforeCloseEvent extends CustomEvent {
2
+ static { this.eventName = 'helix-before-close'; }
3
+ constructor() {
4
+ super(HelixBeforeCloseEvent.eventName, {
5
+ bubbles: false,
6
+ cancelable: true,
7
+ composed: false,
8
+ detail: { open: false },
9
+ });
10
+ }
11
+ }