@fusion.dev/helix-core 0.145.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 (254) hide show
  1. package/custom-elements.json +5203 -1777
  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 +3 -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/keyboard-navigation-controller/keyboard-navigation-controller.d.ts +9 -55
  9. package/dist/controllers/keyboard-navigation-controller/keyboard-navigation-controller.js +7 -39
  10. package/dist/controllers/relative-clock-controller/relative-clock-controller.d.ts +44 -0
  11. package/dist/controllers/relative-clock-controller/relative-clock-controller.js +31 -0
  12. package/dist/controllers/source-controller/source-controller.d.ts +10 -3
  13. package/dist/controllers/source-controller/source-controller.js +7 -4
  14. package/dist/controllers/virtualize-controller/virtualize-controller.d.ts +48 -1
  15. package/dist/controllers/virtualize-controller/virtualize-controller.js +50 -7
  16. package/dist/events/action.d.ts +12 -0
  17. package/dist/events/action.js +6 -0
  18. package/dist/events/activate.d.ts +22 -0
  19. package/dist/events/activate.js +11 -0
  20. package/dist/events/aura-fallback.d.ts +12 -0
  21. package/dist/events/aura-fallback.js +11 -0
  22. package/dist/events/before-activate.d.ts +24 -0
  23. package/dist/events/before-activate.js +11 -0
  24. package/dist/events/before-change.d.ts +26 -0
  25. package/dist/events/before-change.js +11 -0
  26. package/dist/events/before-close.d.ts +6 -0
  27. package/dist/events/before-close.js +11 -0
  28. package/dist/events/before-collapse.d.ts +6 -0
  29. package/dist/events/before-collapse.js +11 -0
  30. package/dist/events/before-edit.d.ts +6 -0
  31. package/dist/events/before-edit.js +11 -0
  32. package/dist/events/before-move.d.ts +17 -0
  33. package/dist/events/before-move.js +11 -0
  34. package/dist/events/before-open.d.ts +17 -0
  35. package/dist/events/before-open.js +11 -0
  36. package/dist/events/before-panel-resize.d.ts +13 -0
  37. package/dist/events/before-panel-resize.js +11 -0
  38. package/dist/events/before-pin.d.ts +6 -0
  39. package/dist/events/before-pin.js +11 -0
  40. package/dist/events/before-reconnect.d.ts +14 -0
  41. package/dist/events/before-reconnect.js +11 -0
  42. package/dist/events/before-relabel.d.ts +15 -0
  43. package/dist/events/before-relabel.js +11 -0
  44. package/dist/events/before-resize.d.ts +6 -0
  45. package/dist/events/before-resize.js +11 -0
  46. package/dist/events/before-split.d.ts +12 -0
  47. package/dist/events/before-split.js +11 -0
  48. package/dist/events/before-toggle.d.ts +23 -0
  49. package/dist/events/before-toggle.js +11 -0
  50. package/dist/events/canvas-edit.d.ts +16 -0
  51. package/dist/events/canvas-edit.js +11 -0
  52. package/dist/events/canvas-pan.d.ts +13 -0
  53. package/dist/events/canvas-pan.js +11 -0
  54. package/dist/events/canvas-view.d.ts +14 -0
  55. package/dist/events/canvas-view.js +11 -0
  56. package/dist/events/canvas-zoom.d.ts +6 -0
  57. package/dist/events/canvas-zoom.js +11 -0
  58. package/dist/events/caret.d.ts +17 -0
  59. package/dist/events/caret.js +11 -0
  60. package/dist/events/clear.d.ts +11 -0
  61. package/dist/events/clear.js +6 -0
  62. package/dist/events/collapse.d.ts +12 -0
  63. package/dist/events/collapse.js +11 -0
  64. package/dist/events/complete.d.ts +26 -0
  65. package/dist/events/complete.js +11 -0
  66. package/dist/events/connect.d.ts +14 -0
  67. package/dist/events/connect.js +11 -0
  68. package/dist/events/console.d.ts +13 -0
  69. package/dist/events/console.js +11 -0
  70. package/dist/events/copy-error.d.ts +9 -0
  71. package/dist/events/copy-error.js +6 -0
  72. package/dist/events/copy.d.ts +12 -0
  73. package/dist/events/copy.js +10 -0
  74. package/dist/events/create.d.ts +18 -0
  75. package/dist/events/create.js +11 -0
  76. package/dist/events/details.d.ts +454 -0
  77. package/dist/events/details.js +1 -0
  78. package/dist/events/dismiss.d.ts +16 -0
  79. package/dist/events/dismiss.js +11 -0
  80. package/dist/events/edit.d.ts +12 -0
  81. package/dist/events/edit.js +11 -0
  82. package/dist/events/end.d.ts +9 -0
  83. package/dist/events/end.js +6 -0
  84. package/dist/events/events.d.ts +111 -1478
  85. package/dist/events/events.js +91 -863
  86. package/dist/events/expand.d.ts +10 -0
  87. package/dist/events/expand.js +11 -0
  88. package/dist/events/facet.d.ts +17 -0
  89. package/dist/events/facet.js +11 -0
  90. package/dist/events/filter.d.ts +12 -0
  91. package/dist/events/filter.js +11 -0
  92. package/dist/events/filtered.d.ts +12 -0
  93. package/dist/events/filtered.js +11 -0
  94. package/dist/events/graph-filter.d.ts +10 -0
  95. package/dist/events/graph-filter.js +11 -0
  96. package/dist/events/graph-layout.d.ts +10 -0
  97. package/dist/events/graph-layout.js +11 -0
  98. package/dist/events/graph-pan.d.ts +9 -0
  99. package/dist/events/graph-pan.js +11 -0
  100. package/dist/events/graph-part-change.d.ts +10 -0
  101. package/dist/events/graph-part-change.js +11 -0
  102. package/dist/events/graph-ready.d.ts +8 -0
  103. package/dist/events/graph-ready.js +6 -0
  104. package/dist/events/graph-signal.d.ts +11 -0
  105. package/dist/events/graph-signal.js +11 -0
  106. package/dist/events/graph-zoom.d.ts +6 -0
  107. package/dist/events/graph-zoom.js +11 -0
  108. package/dist/events/history.d.ts +16 -0
  109. package/dist/events/history.js +11 -0
  110. package/dist/events/load-more.d.ts +12 -0
  111. package/dist/events/load-more.js +6 -0
  112. package/dist/events/load.d.ts +12 -0
  113. package/dist/events/load.js +6 -0
  114. package/dist/events/map-cluster.d.ts +15 -0
  115. package/dist/events/map-cluster.js +11 -0
  116. package/dist/events/map-pan.d.ts +18 -0
  117. package/dist/events/map-pan.js +11 -0
  118. package/dist/events/map-part-change.d.ts +16 -0
  119. package/dist/events/map-part-change.js +11 -0
  120. package/dist/events/map-ready.d.ts +15 -0
  121. package/dist/events/map-ready.js +6 -0
  122. package/dist/events/map-rotate.d.ts +9 -0
  123. package/dist/events/map-rotate.js +11 -0
  124. package/dist/events/map-tilt.d.ts +9 -0
  125. package/dist/events/map-tilt.js +11 -0
  126. package/dist/events/map-zoom.d.ts +13 -0
  127. package/dist/events/map-zoom.js +11 -0
  128. package/dist/events/move.d.ts +11 -0
  129. package/dist/events/move.js +11 -0
  130. package/dist/events/names.d.ts +15 -0
  131. package/dist/events/names.js +88 -0
  132. package/dist/events/navigate.d.ts +13 -0
  133. package/dist/events/navigate.js +11 -0
  134. package/dist/events/next.d.ts +23 -0
  135. package/dist/events/next.js +11 -0
  136. package/dist/events/panel-resize.d.ts +12 -0
  137. package/dist/events/panel-resize.js +11 -0
  138. package/dist/events/pin.d.ts +12 -0
  139. package/dist/events/pin.js +11 -0
  140. package/dist/events/press.d.ts +19 -0
  141. package/dist/events/press.js +11 -0
  142. package/dist/events/preview.d.ts +14 -0
  143. package/dist/events/preview.js +11 -0
  144. package/dist/events/previous.d.ts +6 -0
  145. package/dist/events/previous.js +11 -0
  146. package/dist/events/range-change.d.ts +10 -0
  147. package/dist/events/range-change.js +11 -0
  148. package/dist/events/read.d.ts +19 -0
  149. package/dist/events/read.js +11 -0
  150. package/dist/events/reconnect.d.ts +6 -0
  151. package/dist/events/reconnect.js +11 -0
  152. package/dist/events/reject.d.ts +13 -0
  153. package/dist/events/reject.js +11 -0
  154. package/dist/events/relabel.d.ts +6 -0
  155. package/dist/events/relabel.js +11 -0
  156. package/dist/events/remove.d.ts +12 -0
  157. package/dist/events/remove.js +11 -0
  158. package/dist/events/reorder.d.ts +18 -0
  159. package/dist/events/reorder.js +11 -0
  160. package/dist/events/reordered.d.ts +12 -0
  161. package/dist/events/reordered.js +11 -0
  162. package/dist/events/resize.d.ts +19 -0
  163. package/dist/events/resize.js +11 -0
  164. package/dist/events/resized.d.ts +16 -0
  165. package/dist/events/resized.js +11 -0
  166. package/dist/events/sample-end.d.ts +5 -0
  167. package/dist/events/sample-end.js +6 -0
  168. package/dist/events/sample-start.d.ts +18 -0
  169. package/dist/events/sample-start.js +6 -0
  170. package/dist/events/scrolled.d.ts +32 -0
  171. package/dist/events/scrolled.js +11 -0
  172. package/dist/events/select.d.ts +12 -0
  173. package/dist/events/select.js +11 -0
  174. package/dist/events/selected.d.ts +12 -0
  175. package/dist/events/selected.js +11 -0
  176. package/dist/events/send.d.ts +18 -0
  177. package/dist/events/send.js +11 -0
  178. package/dist/events/shortcut.d.ts +13 -0
  179. package/dist/events/shortcut.js +11 -0
  180. package/dist/events/sort.d.ts +17 -0
  181. package/dist/events/sort.js +11 -0
  182. package/dist/events/sorted.d.ts +15 -0
  183. package/dist/events/sorted.js +11 -0
  184. package/dist/events/split.d.ts +12 -0
  185. package/dist/events/split.js +11 -0
  186. package/dist/events/toggle.d.ts +15 -0
  187. package/dist/events/toggle.js +11 -0
  188. package/dist/events/tour-ask.d.ts +16 -0
  189. package/dist/events/tour-ask.js +11 -0
  190. package/dist/events/tour-status.d.ts +14 -0
  191. package/dist/events/tour-status.js +11 -0
  192. package/dist/events/typing.d.ts +10 -0
  193. package/dist/events/typing.js +11 -0
  194. package/dist/helix-element/react-bridge.d.ts +10 -0
  195. package/dist/helix-element/react-bridge.js +1 -1
  196. package/dist/helix-form-element/helix-form-element.d.ts +5 -20
  197. package/dist/helix-form-element/helix-form-element.js +4 -56
  198. package/dist/index.d.ts +15 -3
  199. package/dist/index.js +15 -3
  200. package/dist/mixins/mixin-chart-source/mixin-chart-source.d.ts +17 -2
  201. package/dist/mixins/mixin-chart-source/mixin-chart-source.js +22 -1
  202. package/dist/mixins/mixin-controller-active/mixin-controller-active.js +4 -3
  203. package/dist/mixins/mixin-controller-checked/mixin-controller-checked.d.ts +5 -2
  204. package/dist/mixins/mixin-controller-checked/mixin-controller-checked.js +2 -1
  205. package/dist/mixins/mixin-controller-value/mixin-controller-value.d.ts +4 -2
  206. package/dist/mixins/mixin-controller-value/mixin-controller-value.js +7 -2
  207. package/dist/mixins/mixin-dismissible/mixin-dismissible.js +20 -4
  208. package/dist/mixins/mixin-field/mixin-field.js +2 -2
  209. package/dist/mixins/mixin-picker-field/mixin-picker-field.d.ts +15 -3
  210. package/dist/mixins/mixin-picker-field/mixin-picker-field.js +11 -4
  211. package/dist/mixins/mixin-segmented-field/mixin-segmented-field.d.ts +1 -1
  212. package/dist/mixins/mixin-segmented-field/mixin-segmented-field.js +10 -4
  213. package/dist/utils/audit-name/audit-name-off.d.ts +10 -0
  214. package/dist/utils/audit-name/audit-name-off.js +1 -0
  215. package/dist/utils/audit-name/audit-name.d.ts +19 -6
  216. package/dist/utils/audit-name/audit-name.js +10 -27
  217. package/dist/utils/calendar/calendar.js +7 -7
  218. package/dist/utils/draw-tooltip/draw-tooltip.d.ts +1 -1
  219. package/dist/utils/draw-tooltip/draw-tooltip.js +3 -1
  220. package/dist/utils/draw-tooltip/spoken-reading.d.ts +6 -2
  221. package/dist/utils/draw-tooltip/spoken-reading.js +5 -4
  222. package/dist/utils/heading-level/heading-level.d.ts +8 -0
  223. package/dist/utils/heading-level/heading-level.js +4 -0
  224. package/dist/utils/implicit-submit/implicit-submit.d.ts +25 -0
  225. package/dist/utils/implicit-submit/implicit-submit.js +52 -0
  226. package/dist/utils/name-text/name-text.d.ts +12 -0
  227. package/dist/utils/name-text/name-text.js +25 -0
  228. package/dist/utils/number-format/number-format.d.ts +2 -1
  229. package/dist/utils/number-format/number-format.js +13 -5
  230. package/dist/utils/plain-click/plain-click.d.ts +10 -0
  231. package/dist/utils/plain-click/plain-click.js +1 -0
  232. package/dist/utils/press-type/press-type.d.ts +20 -0
  233. package/dist/utils/press-type/press-type.js +15 -0
  234. package/dist/utils/report-runtime/report-runtime.d.ts +6 -3
  235. package/dist/utils/row-data/row-data.d.ts +15 -0
  236. package/dist/utils/row-data/row-data.js +17 -0
  237. package/dist/utils/row-order/row-order.d.ts +141 -0
  238. package/dist/utils/row-order/row-order.js +76 -0
  239. package/dist/utils/tell-disclosure/tell-disclosure.d.ts +4 -0
  240. package/dist/utils/tree-arrow/tree-arrow.d.ts +23 -0
  241. package/dist/utils/tree-arrow/tree-arrow.js +9 -0
  242. package/dist/utils/typeahead/typeahead.d.ts +67 -0
  243. package/dist/utils/typeahead/typeahead.js +36 -0
  244. package/dist/utils/value-as/value-as.d.ts +9 -0
  245. package/dist/utils/value-as/value-as.js +9 -1
  246. package/dist/utils/watch-labels/watch-labels.d.ts +16 -0
  247. package/dist/utils/watch-labels/watch-labels.js +34 -0
  248. package/dist/utils/words-converter/words-converter.d.ts +14 -0
  249. package/dist/utils/words-converter/words-converter.js +15 -0
  250. package/dist/utils/written-list/written-list.d.ts +29 -0
  251. package/dist/utils/written-list/written-list.js +30 -0
  252. package/dist/version.d.ts +1 -1
  253. package/dist/version.js +1 -1
  254. package/package.json +18 -5
@@ -84,7 +84,8 @@ export interface AriaForwardOptions {
84
84
  * and the host is a generic the reader never lands on, so a `<label for>` or a
85
85
  * wrapping `<label>` left the textbox unnamed. The labels travel the same way a
86
86
  * forwarded `aria-labelledby` does, as element references, and are read again
87
- * when focus arrives, since a label can be added without the host hearing.
87
+ * on every update, which a form control asks for whenever a label in its tree
88
+ * changes (`watchLabels`), and as focus arrives.
88
89
  */
89
90
  export declare class AriaForwardController implements ReactiveController {
90
91
  private readonly host;
@@ -128,7 +129,7 @@ export declare class AriaForwardController implements ReactiveController {
128
129
  forward(): void;
129
130
  hostConnected(): void;
130
131
  hostDisconnected(): void;
131
- /** Forwards again as focus arrives, for a label added since the last update. */
132
+ /** Forwards again as focus arrives. */
132
133
  private readonly refresh;
133
134
  hostUpdated(): void;
134
135
  }
@@ -1,17 +1,6 @@
1
- import { auditName } from '../../utils/audit-name/audit-name.js';
1
+ import { auditName } from '#audit-name';
2
2
  import { elementById } from '../../utils/element-by-id/element-by-id.js';
3
- const shownText = (node) => {
4
- if (node instanceof HTMLSlotElement) {
5
- return node.assignedNodes({ flatten: true }).map(shownText).join('');
6
- }
7
- if (node.nodeType === Node.TEXT_NODE) {
8
- return node.textContent ?? '';
9
- }
10
- if (node instanceof HTMLElement && node.hidden) {
11
- return '';
12
- }
13
- return [...node.childNodes].map(shownText).join('');
14
- };
3
+ import { nameText } from '../../utils/name-text/name-text.js';
15
4
  const RELATIONSHIPS = [
16
5
  ['aria-describedby', 'ariaDescribedByElements'],
17
6
  ['aria-labelledby', 'ariaLabelledByElements'],
@@ -49,7 +38,7 @@ export class AriaForwardController {
49
38
  return true;
50
39
  }
51
40
  const own = target.labels;
52
- return [...(own ?? [])].some((label) => shownText(label).trim() !== '');
41
+ return [...(own ?? [])].some((label) => nameText(label).trim() !== '');
53
42
  }
54
43
  hostLabels(target) {
55
44
  const labels = this.host.labels;
@@ -34,6 +34,31 @@ export interface ChartPointerHost extends ReactiveControllerHost, HTMLElement {
34
34
  * drawn from, such as a scatter's measure across.
35
35
  */
36
36
  seriesOf?(cell: number): number | undefined;
37
+ /**
38
+ * Whether the series drawn from the cell at this place in a row is shown,
39
+ * which it is unless a legend hid it. Absent, every series is shown.
40
+ *
41
+ * The series keys pass over a hidden one, and the readout leaves it out.
42
+ */
43
+ showsColumn?(cell: number): boolean;
44
+ /**
45
+ * Whether the reading at this place is shown, for a chart whose readings
46
+ * a legend can hide: a ring's slices are its rows. Absent, every reading is
47
+ * shown.
48
+ *
49
+ * The keys pass over a hidden reading, as they pass over a hidden series.
50
+ */
51
+ showsReading?(at: number): boolean;
52
+ /**
53
+ * The words for where a reading stands among them, with `{position}` and
54
+ * `{count}` to fill. Absent or empty, no position is said.
55
+ */
56
+ readonly positionLabel?: string;
57
+ /**
58
+ * The locale the position's numbers are written in. Absent or empty is the
59
+ * runtime's own.
60
+ */
61
+ readonly locale?: string;
37
62
  }
38
63
  /**
39
64
  * Which reading a pointer is nearest, for a chart that draws no markers.
@@ -62,8 +87,9 @@ export interface ChartPointerHost extends ReactiveControllerHost, HTMLElement {
62
87
  * **The keyboard moves the same reading, which is what makes it the reading
63
88
  * and not the pointer's.** A chart's plot takes focus (`tabindex="0"` on the
64
89
  * element carrying `CHART_PLOT`), and while it has it the arrow keys walk the
65
- * readings, Home and End go to the first and the last, and the other pair of
66
- * arrows walks the series within a reading. Escape puts the bubble away, as it
90
+ * readings, Home and End go to the first and the last, PageUp and PageDown
91
+ * move a page of readings, and the other pair of arrows walks the series
92
+ * within a reading. Escape puts the bubble away, as it
67
93
  * does a tooltip. The tooltip follows because it already follows `at`, and the
68
94
  * chart's readout, a polite live region `drawTooltip` renders, says the
69
95
  * reading in words. It speaks only for a key: a pointer crossing a chart is a
@@ -125,8 +151,41 @@ export declare class ChartPointerController implements ReactiveController {
125
151
  * from, and the bubble lists every one of them.
126
152
  */
127
153
  get column(): number | undefined;
128
- /** The cells a series key can rest on: every column a series was drawn from. */
154
+ /**
155
+ * Whether the series drawn from the cell at this place in a row is shown.
156
+ *
157
+ * The readout asks, so a reading said whole leaves out what a legend hid,
158
+ * as the drawing does.
159
+ */
160
+ shows(cell: number): boolean;
161
+ /**
162
+ * How far PageUp and PageDown move: four columns of a grid, which is four
163
+ * weeks of a calendar heatmap, a month near enough; and a tenth of the
164
+ * readings otherwise, at least one.
165
+ */
166
+ private page;
167
+ /**
168
+ * Where the reading stands among the shown readings, in the host's
169
+ * `positionLabel`, such as "2 of 12". Empty with no reading, and where the
170
+ * host gives no words.
171
+ *
172
+ * Counted among the shown readings, so a slice a legend hid is not one of
173
+ * the count.
174
+ */
175
+ get position(): string;
176
+ /**
177
+ * The cells a series key can rest on: every column a series was drawn
178
+ * from, and is still drawn from.
179
+ */
129
180
  private columns;
181
+ /**
182
+ * The readings the keys walk, by place: every one, less those a legend hid.
183
+ *
184
+ * The keys step through this list, so a hidden
185
+ * slice is passed over as if it were not in the table, which is how the
186
+ * ring draws it.
187
+ */
188
+ private shown;
130
189
  /**
131
190
  * Tells the tooltip its anchor moved, which nothing else would.
132
191
  *
@@ -145,6 +204,9 @@ export declare class ChartPointerController implements ReactiveController {
145
204
  /**
146
205
  * The box the readings are laid out in, which is the plot and not the host.
147
206
  *
207
+ * Found by its part, or by the id every chart gives its plot, which is how
208
+ * a sparkline's drawing, whose part is `svg`, is found.
209
+ *
148
210
  * The host also holds the row of labels, so measuring against it would map
149
211
  * the pointer over a box taller than the drawing. That mattered the moment a
150
212
  * chart started measuring down the box rather than across it, which is what
@@ -1,3 +1,5 @@
1
+ import { fillTemplate } from '../../utils/fill-template/fill-template.js';
2
+ import { formatNumbers } from '../../utils/format-numbers/format-numbers.js';
1
3
  import { isRtl } from '../../utils/is-rtl/is-rtl.js';
2
4
  import { stepReading } from './reading-step.js';
3
5
  export const CHART_PLOT = 'helix-chart-plot';
@@ -13,7 +15,8 @@ export class ChartPointerController {
13
15
  if (!this.fromPlot(event) || event.altKey || event.ctrlKey || event.metaKey) {
14
16
  return;
15
17
  }
16
- const count = this.host.readings;
18
+ const shown = this.shown();
19
+ const count = shown.length;
17
20
  if (count === 0) {
18
21
  return;
19
22
  }
@@ -24,17 +27,19 @@ export class ChartPointerController {
24
27
  }
25
28
  return;
26
29
  }
27
- const next = stepReading(event.key, { at: this.at, series: this.series }, {
30
+ const next = stepReading(event.key, { at: shown.indexOf(this.at), series: this.series }, {
28
31
  axis: this.axis,
29
32
  count,
30
33
  grid: this.grid,
34
+ page: this.page(count),
31
35
  rtl: isRtl(this.host),
32
36
  series: this.columns().length,
33
37
  });
34
38
  if (next === undefined) {
35
39
  return;
36
40
  }
37
- const { at, series } = next;
41
+ const at = shown[next.at] ?? -1;
42
+ const { series } = next;
38
43
  event.preventDefault();
39
44
  if (at === this.at && series === this.series && this.keyed) {
40
45
  return;
@@ -56,10 +61,11 @@ export class ChartPointerController {
56
61
  }
57
62
  catch {
58
63
  }
59
- if (!visible || this.host.readings === 0) {
64
+ const first = this.shown()[0];
65
+ if (!visible || first === undefined) {
60
66
  return;
61
67
  }
62
- this.at = 0;
68
+ this.at = first;
63
69
  this.keyed = true;
64
70
  this.moved = true;
65
71
  this.host.requestUpdate();
@@ -123,12 +129,32 @@ export class ChartPointerController {
123
129
  get column() {
124
130
  return this.series < 0 ? undefined : this.columns()[this.series];
125
131
  }
132
+ shows(cell) {
133
+ return this.host.showsColumn?.(cell) !== false;
134
+ }
135
+ page(count) {
136
+ return this.grid > 1 ? this.grid * 4 : Math.max(1, Math.round(count / 10));
137
+ }
138
+ get position() {
139
+ const words = this.host.positionLabel ?? '';
140
+ const shown = this.shown();
141
+ const place = shown.indexOf(this.at);
142
+ if (words === '' || place < 0) {
143
+ return '';
144
+ }
145
+ const format = formatNumbers(this.host.locale ?? '');
146
+ return fillTemplate(words, { count: format(shown.length), position: format(place + 1) });
147
+ }
126
148
  columns() {
127
149
  const host = this.host;
128
- const all = host.plottedColumns ?? [];
129
- return host.seriesOf === undefined
150
+ return (host.plottedColumns ?? []).filter((one) => (host.seriesOf === undefined || host.seriesOf(one) !== undefined) && this.shows(one));
151
+ }
152
+ shown() {
153
+ const host = this.host;
154
+ const all = Array.from({ length: host.readings }, (_, at) => at);
155
+ return host.showsReading === undefined
130
156
  ? all
131
- : all.filter((one) => host.seriesOf?.(one) !== undefined);
157
+ : all.filter((at) => host.showsReading?.(at) !== false);
132
158
  }
133
159
  hostUpdated() {
134
160
  if (!this.moved) {
@@ -141,7 +167,9 @@ export class ChartPointerController {
141
167
  }
142
168
  box() {
143
169
  if (!this.plot?.isConnected) {
144
- this.plot = this.host.renderRoot.querySelector('[part="plot"]') ?? undefined;
170
+ this.plot =
171
+ this.host.renderRoot.querySelector(`[part="plot"], #${CHART_PLOT}`) ??
172
+ undefined;
145
173
  }
146
174
  return this.plot?.getBoundingClientRect();
147
175
  }
@@ -18,6 +18,11 @@ export interface ReadingLayout {
18
18
  count: number;
19
19
  /** How many readings stand in one column, for a grid. `1` is a row of them. */
20
20
  grid: number;
21
+ /**
22
+ * How far PageUp and PageDown move, in readings. Absent, they move nothing
23
+ * and the page keeps them.
24
+ */
25
+ page?: number;
21
26
  /** Whether the line of text runs right to left, which turns the inline arrows. */
22
27
  rtl: boolean;
23
28
  /** How many series a reading holds, which the cross pair of arrows walks. */
@@ -49,7 +54,8 @@ export declare const amongSeries: (key: string, layout: ReadingLayout) => number
49
54
  * Where a key moves the reading, or `undefined` for a key that moves nothing,
50
55
  * which the page keeps.
51
56
  *
52
- * Home and End go to the first and the last. From no reading, an arrow moving
57
+ * Home and End go to the first and the last, and PageUp and PageDown move a
58
+ * `page` of readings where the layout gives one. From no reading, an arrow moving
53
59
  * on starts at the first and one moving back at the last, so either arrow
54
60
  * starts somewhere a reader expects. A series key on no reading starts on the
55
61
  * first. Nothing walks off either end.
@@ -29,6 +29,11 @@ export const stepReading = (key, from, layout) => {
29
29
  if (key === 'End') {
30
30
  return { at: last, series: from.series };
31
31
  }
32
+ if ((key === 'PageDown' || key === 'PageUp') && layout.page !== undefined) {
33
+ const step = Math.max(1, layout.page) * (key === 'PageDown' ? 1 : -1);
34
+ const at = from.at < 0 ? (step > 0 ? 0 : last) : Math.min(Math.max(from.at + step, 0), last);
35
+ return { at, series: from.series };
36
+ }
32
37
  const along = alongReadings(key, layout);
33
38
  if (along !== 0) {
34
39
  const at = from.at < 0 ? (along > 0 ? 0 : last) : Math.min(Math.max(from.at + along, 0), last);
@@ -1,4 +1,5 @@
1
1
  import type { ReactiveController, ReactiveControllerHost } from 'lit';
2
+ import { type TypeaheadBy } from '../../utils/typeahead/typeahead.js';
2
3
  /** What a controller needs of an item to move to it and choose it. */
3
4
  export interface NavigableItem {
4
5
  /**
@@ -70,14 +71,15 @@ export interface KeyboardNavigationOptions {
70
71
  */
71
72
  reserved?: () => string[];
72
73
  /**
73
- * The words of an item, which turns on typeahead: a printable key moves to
74
- * the next item whose words start with what was typed.
74
+ * The search that turns on typeahead, built with `typeaheadBy` from the
75
+ * words of an item: a printable key moves to the next item whose words
76
+ * start with what was typed.
75
77
  *
76
78
  * Off unless given. A set whose items have no words a reader would type, or
77
79
  * that holds a text field of its own, leaves it out. See `Typeahead` for how
78
80
  * the letters are matched.
79
81
  */
80
- typeahead?: (item: NavigableItem) => string;
82
+ typeahead?: TypeaheadBy<NavigableItem>;
81
83
  /**
82
84
  * Whether the ends join up, which they do unless a set says otherwise.
83
85
  *
@@ -98,54 +100,6 @@ export interface KeyboardNavigationOptions {
98
100
  */
99
101
  wrap?: boolean;
100
102
  }
101
- /**
102
- * Whether a press types a character, which is what typeahead reads.
103
- *
104
- * One character with no Control, Command or Alt held, so a shortcut such as
105
- * Control+A is never taken for the letter A. Shift is allowed, because a
106
- * capital is still a letter.
107
- */
108
- export declare const typesCharacter: (event: KeyboardEvent) => boolean;
109
- /**
110
- * Finds an item by the letters a reader types at a set, the way a native
111
- * `<select>` does.
112
- *
113
- * Letters typed within `timeout` milliseconds of each other build one search,
114
- * so typing "ca" moves to "Canada" past "Cambodia". The same letter typed again
115
- * moves to the next item that starts with it, so pressing "c" three times visits
116
- * the first three. A pause starts a new search.
117
- *
118
- * Matching ignores case and leading spaces, and searches from the reader's
119
- * position, wrapping at the end. A search that matches nothing moves nothing.
120
- *
121
- * Holds only the letters, so one instance serves any set: the caller passes the
122
- * words of the items and where the reader is on every key.
123
- */
124
- export declare class Typeahead {
125
- private letters;
126
- private last;
127
- private readonly timeout;
128
- constructor({ timeout }?: {
129
- timeout?: number;
130
- });
131
- /**
132
- * Whether a search is under way.
133
- *
134
- * A set that chooses on Space asks this first, because a Space typed in the
135
- * middle of a search is part of the words, as in "New York".
136
- */
137
- typing(now?: number): boolean;
138
- /**
139
- * Adds a character to the search, and returns the index of the item it
140
- * matches, or `-1` where none does.
141
- *
142
- * `texts` are the words of the items a reader can reach, in order, and
143
- * `from` is the index of the item the reader is on, or `-1`.
144
- */
145
- match(key: string, texts: readonly string[], from: number, now?: number): number;
146
- /** Ends the search, so the next letter starts a new one. */
147
- reset(): void;
148
- }
149
103
  /**
150
104
  * Moves through a set of items with the arrow keys, and chooses as it goes.
151
105
  *
@@ -171,9 +125,10 @@ export declare class Typeahead {
171
125
  * Disabled items are skipped rather than stopped at, so a set with a gap in it
172
126
  * still moves in one press.
173
127
  *
174
- * **Typeahead is opt-in.** Pass `typeahead` with a function that returns an
175
- * item's words, and a printable key moves to the next item whose words start
176
- * with the letters typed, choosing it where arriving chooses. A key that a text
128
+ * **Typeahead is opt-in.** Pass `typeahead`, built by `typeaheadBy` from a
129
+ * function that returns an item's words, and a printable key moves to the next
130
+ * item whose words start with the letters typed, choosing it where arriving
131
+ * chooses. A key that a text
177
132
  * field inside the set is typing into is left to the field.
178
133
  *
179
134
  * A set that starts from focus rather than from the selection, or that moves
@@ -187,7 +142,6 @@ export declare class KeyboardNavigationController implements ReactiveController
187
142
  private readonly selects;
188
143
  private readonly reserved;
189
144
  private readonly wrap;
190
- private readonly words;
191
145
  private readonly typeahead;
192
146
  constructor(host: ReactiveControllerHost & HTMLElement, { items, origin, reserved, selects, typeahead, wrap, }: KeyboardNavigationOptions);
193
147
  /** Where this press starts from, read again on every press. */
@@ -1,41 +1,9 @@
1
1
  import { inlineStep } from '../../utils/inline-step/inline-step.js';
2
2
  import { keyClaimed } from '../../utils/key-claimed/key-claimed.js';
3
+ import { typesCharacter } from '../../utils/typeahead/typeahead.js';
3
4
  const KEYS = new Set(['ArrowDown', 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'End', 'Home']);
4
- export const typesCharacter = (event) => event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey;
5
- export class Typeahead {
6
- constructor({ timeout = 500 } = {}) {
7
- this.letters = '';
8
- this.last = 0;
9
- this.timeout = timeout;
10
- }
11
- typing(now = Date.now()) {
12
- return this.letters !== '' && now - this.last <= this.timeout;
13
- }
14
- match(key, texts, from, now = Date.now()) {
15
- if (!this.typing(now)) {
16
- this.letters = '';
17
- }
18
- this.last = now;
19
- this.letters += key.toLowerCase();
20
- const words = texts.map((text) => text.trim().toLowerCase());
21
- const repeated = [...this.letters].every((letter) => letter === this.letters[0]);
22
- const needle = repeated ? (this.letters[0] ?? '') : this.letters;
23
- const start = repeated ? from + 1 : Math.max(from, 0);
24
- for (let step = 0; step < words.length; step += 1) {
25
- const at = (start + step) % words.length;
26
- if (words[at]?.startsWith(needle)) {
27
- return at;
28
- }
29
- }
30
- return -1;
31
- }
32
- reset() {
33
- this.letters = '';
34
- }
35
- }
36
5
  export class KeyboardNavigationController {
37
6
  constructor(host, { items, origin = 'selection', reserved = () => [], selects = () => true, typeahead, wrap = true, }) {
38
- this.typeahead = new Typeahead();
39
7
  this.handleKeydown = (event) => {
40
8
  if (!KEYS.has(event.key)) {
41
9
  this.handleTypeahead(event);
@@ -76,7 +44,7 @@ export class KeyboardNavigationController {
76
44
  next = last;
77
45
  }
78
46
  event.preventDefault();
79
- this.typeahead.reset();
47
+ this.typeahead?.search.reset();
80
48
  this.arrive(items[next]);
81
49
  };
82
50
  this.host = host;
@@ -84,7 +52,7 @@ export class KeyboardNavigationController {
84
52
  this.originOf = typeof origin === 'function' ? origin : () => origin;
85
53
  this.reserved = reserved;
86
54
  this.selects = selects;
87
- this.words = typeahead;
55
+ this.typeahead = typeahead;
88
56
  this.wrap = wrap;
89
57
  host.addController(this);
90
58
  }
@@ -107,11 +75,11 @@ export class KeyboardNavigationController {
107
75
  return selected < 0 ? focused : selected;
108
76
  }
109
77
  handleTypeahead(event) {
110
- const words = this.words;
111
- if (!words || !typesCharacter(event) || keyClaimed(event)) {
78
+ const typeahead = this.typeahead;
79
+ if (!typeahead || !typesCharacter(event) || keyClaimed(event)) {
112
80
  return;
113
81
  }
114
- if (event.key === ' ' && !this.typeahead.typing()) {
82
+ if (event.key === ' ' && !typeahead.search.typing()) {
115
83
  return;
116
84
  }
117
85
  const items = this.items().filter((item) => !item.disabled);
@@ -122,7 +90,7 @@ export class KeyboardNavigationController {
122
90
  if (this.origin === 'focus' && from === -1) {
123
91
  return;
124
92
  }
125
- const at = this.typeahead.match(event.key, items.map(words), from);
93
+ const at = typeahead.search.match(event.key, items.map(typeahead.words), from);
126
94
  event.preventDefault();
127
95
  if (at >= 0 && at !== from) {
128
96
  this.arrive(items[at]);
@@ -0,0 +1,44 @@
1
+ import type { ReactiveController, ReactiveControllerHost } from 'lit';
2
+ /** What the clock reads from its host. */
3
+ export interface RelativeClockOptions {
4
+ /**
5
+ * When the reading drawn at `now` stops being true, as `relativeTime` and
6
+ * `elapsedTime` say in `next`, in milliseconds from `now`; or `undefined`
7
+ * where the host shows no reading, which reads nothing and schedules
8
+ * nothing.
9
+ */
10
+ expires: (now: Date) => number | undefined;
11
+ }
12
+ /**
13
+ * The time a relative reading is drawn against, moved on when the reading
14
+ * would change: "3 minutes ago" becoming "4 minutes ago".
15
+ *
16
+ * **One wakeup when the words go stale, and none in between.** The reading
17
+ * says how long it holds, so the clock asks core's shared ticker for exactly
18
+ * that moment: a reading a minute old wakes once a minute, an hour old once an
19
+ * hour, and a date never. Every reading on the page shares the ticker's one
20
+ * timer. Chat Message and Format Date draw against it.
21
+ *
22
+ * **Only on a page.** A server has nobody to update a reading for, and a
23
+ * subscription there holds the element after its markup is sent, so off the
24
+ * page `tick()` reads the time and schedules nothing.
25
+ */
26
+ export declare class RelativeClockController implements ReactiveController {
27
+ private readonly host;
28
+ private readonly options;
29
+ /** The time the host last drew against, in milliseconds. */
30
+ now: number;
31
+ private stop;
32
+ constructor(host: ReactiveControllerHost & HTMLElement, options: RelativeClockOptions);
33
+ hostConnected(): void;
34
+ hostDisconnected(): void;
35
+ /**
36
+ * Reads the clock again, asks the host to draw against it, and schedules
37
+ * the next reading.
38
+ *
39
+ * The host calls it when what it reads changes, the instant or the locale,
40
+ * and the ticker calls it when the words go stale. A host with no reading
41
+ * is left as it is.
42
+ */
43
+ tick(): void;
44
+ }
@@ -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) {