@fusion.dev/helix-headless 0.146.0 → 0.148.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 (288) hide show
  1. package/custom-elements.json +9287 -1084
  2. package/dist/accordion/item/accordion-item.d.ts +43 -21
  3. package/dist/accordion/item/accordion-item.js +37 -11
  4. package/dist/accordion/root/accordion-root.d.ts +8 -2
  5. package/dist/accordion/root/accordion-root.js +6 -8
  6. package/dist/announcement/root/announcement-root.d.ts +66 -6
  7. package/dist/announcement/root/announcement-root.js +144 -13
  8. package/dist/area-chart/root/area-chart-root.d.ts +18 -1
  9. package/dist/area-chart/root/area-chart-root.js +14 -7
  10. package/dist/avatar/root/avatar-root.d.ts +31 -1
  11. package/dist/avatar/root/avatar-root.js +11 -1
  12. package/dist/badge/count.d.ts +14 -0
  13. package/dist/badge/count.js +20 -0
  14. package/dist/badge/root/badge-root.d.ts +57 -6
  15. package/dist/badge/root/badge-root.js +77 -8
  16. package/dist/bar-chart/root/bar-chart-root.js +11 -20
  17. package/dist/bar-chart/stack.d.ts +10 -0
  18. package/dist/bar-chart/stack.js +8 -0
  19. package/dist/blockquote/root/blockquote-root.d.ts +66 -8
  20. package/dist/blockquote/root/blockquote-root.js +85 -8
  21. package/dist/breadcrumbs/breadcrumb/breadcrumb.d.ts +14 -0
  22. package/dist/breadcrumbs/breadcrumb/breadcrumb.js +12 -2
  23. package/dist/breadcrumbs/root/breadcrumbs-root.js +2 -2
  24. package/dist/button/root/button-root.d.ts +24 -38
  25. package/dist/button/root/button-root.js +6 -37
  26. package/dist/button/submitter.d.ts +27 -0
  27. package/dist/button/submitter.js +25 -0
  28. package/dist/button-group/arrows.d.ts +14 -0
  29. package/dist/button-group/arrows.js +10 -0
  30. package/dist/button-group/root/button-group-root.d.ts +141 -3
  31. package/dist/button-group/root/button-group-root.js +142 -2
  32. package/dist/canvas/edge-pan.d.ts +61 -0
  33. package/dist/canvas/edge-pan.js +63 -0
  34. package/dist/canvas/node/canvas-node.d.ts +12 -2
  35. package/dist/canvas/node/canvas-node.js +6 -2
  36. package/dist/canvas/root/canvas-document.d.ts +10 -0
  37. package/dist/canvas/root/canvas-document.js +1 -0
  38. package/dist/canvas/root/canvas-root.d.ts +107 -5
  39. package/dist/canvas/root/canvas-root.js +140 -36
  40. package/dist/canvas/snap.d.ts +42 -0
  41. package/dist/canvas/snap.js +32 -0
  42. package/dist/card/root/card-root.d.ts +12 -1
  43. package/dist/card/root/card-root.js +11 -4
  44. package/dist/carousel/root/carousel-root.d.ts +4 -3
  45. package/dist/carousel/root/carousel-root.js +2 -6
  46. package/dist/chat/root/chat-root.d.ts +28 -4
  47. package/dist/chat/root/chat-root.js +12 -7
  48. package/dist/checkbox/root/checkbox-root.js +5 -7
  49. package/dist/checkbox-group/root/checkbox-group-root.d.ts +13 -0
  50. package/dist/checkbox-group/root/checkbox-group-root.js +16 -7
  51. package/dist/checkbox-group/select-all.d.ts +9 -4
  52. package/dist/checkbox-group/select-all.js +1 -1
  53. package/dist/code-block/root/code-block-root.d.ts +41 -18
  54. package/dist/code-block/root/code-block-root.js +21 -6
  55. package/dist/color-input/root/color-input-root.d.ts +11 -0
  56. package/dist/color-input/root/color-input-root.js +12 -5
  57. package/dist/color-picker/channels.js +1 -2
  58. package/dist/color-picker/root/color-picker-root.d.ts +32 -24
  59. package/dist/color-picker/root/color-picker-root.js +99 -55
  60. package/dist/color-picker/wheel.d.ts +28 -0
  61. package/dist/color-picker/wheel.js +15 -0
  62. package/dist/combobox/root/combobox-root.d.ts +83 -11
  63. package/dist/combobox/root/combobox-root.js +81 -18
  64. package/dist/command/root/command-root.d.ts +7 -4
  65. package/dist/command/root/command-root.js +2 -19
  66. package/dist/copy-button/root/copy-button-root.d.ts +16 -1
  67. package/dist/copy-button/root/copy-button-root.js +7 -2
  68. package/dist/data-table/keys.d.ts +0 -18
  69. package/dist/data-table/keys.js +1 -11
  70. package/dist/data-table/root/data-table-root.js +29 -50
  71. package/dist/date-input/root/date-input-root.d.ts +8 -1
  72. package/dist/date-input/root/date-input-root.js +8 -5
  73. package/dist/date-picker/root/date-picker-root.d.ts +14 -9
  74. package/dist/date-picker/root/date-picker-root.js +21 -16
  75. package/dist/description-list/item/description-list-item.d.ts +22 -4
  76. package/dist/description-list/item/description-list-item.js +14 -4
  77. package/dist/divider/root/divider-root.d.ts +45 -16
  78. package/dist/divider/root/divider-root.js +28 -4
  79. package/dist/donut-chart/root/donut-chart-root.js +13 -6
  80. package/dist/donut-chart/slice-label.d.ts +35 -0
  81. package/dist/donut-chart/slice-label.js +11 -0
  82. package/dist/drawer/root/drawer-root.d.ts +4 -2
  83. package/dist/drawer/root/drawer-root.js +15 -3
  84. package/dist/drilldown/item/drilldown-item.d.ts +21 -1
  85. package/dist/drilldown/item/drilldown-item.js +38 -4
  86. package/dist/drilldown/root/drilldown-root.js +3 -2
  87. package/dist/element-events.generated.js +5 -1
  88. package/dist/empty/root/empty-root.d.ts +46 -2
  89. package/dist/empty/root/empty-root.js +50 -5
  90. package/dist/file-input/root/file-input-root.d.ts +34 -6
  91. package/dist/file-input/root/file-input-root.js +55 -21
  92. package/dist/file-input/status.d.ts +24 -0
  93. package/dist/file-input/status.js +11 -0
  94. package/dist/filter-input/chip/filter-chip.d.ts +23 -0
  95. package/dist/filter-input/chip/filter-chip.js +10 -2
  96. package/dist/filter-input/root/filter-input-root.d.ts +8 -0
  97. package/dist/filter-input/root/filter-input-root.js +17 -27
  98. package/dist/format-date/fields.d.ts +20 -0
  99. package/dist/format-date/fields.js +14 -0
  100. package/dist/format-date/options.d.ts +5 -5
  101. package/dist/format-date/options.js +10 -39
  102. package/dist/format-date/root/format-date-root.d.ts +53 -4
  103. package/dist/format-date/root/format-date-root.js +38 -50
  104. package/dist/graph/root/graph-root.d.ts +23 -5
  105. package/dist/graph/root/graph-root.js +17 -20
  106. package/dist/heatmap/root/buckets.d.ts +21 -0
  107. package/dist/heatmap/root/buckets.js +34 -0
  108. package/dist/heatmap/root/heatmap-root.d.ts +76 -3
  109. package/dist/heatmap/root/heatmap-root.js +105 -20
  110. package/dist/icon/index.d.ts +1 -1
  111. package/dist/icon/index.js +1 -1
  112. package/dist/icon/registry.d.ts +26 -1
  113. package/dist/icon/registry.js +12 -1
  114. package/dist/icon/root/icon-root.d.ts +13 -0
  115. package/dist/icon/root/icon-root.js +23 -2
  116. package/dist/icon/root/index.d.ts +1 -1
  117. package/dist/icon/root/index.js +1 -1
  118. package/dist/image/root/image-root.d.ts +20 -0
  119. package/dist/image/root/image-root.js +6 -1
  120. package/dist/input/root/input-root.d.ts +23 -2
  121. package/dist/input/root/input-root.js +12 -8
  122. package/dist/kbd/root/kbd-root.d.ts +20 -3
  123. package/dist/kbd/root/kbd-root.js +23 -50
  124. package/dist/legend/hidden-series.d.ts +10 -2
  125. package/dist/legend/hidden-series.js +5 -2
  126. package/dist/legend/root/legend-root.d.ts +25 -7
  127. package/dist/legend/root/legend-root.js +25 -26
  128. package/dist/lightbox/root/lightbox-root.d.ts +50 -5
  129. package/dist/lightbox/root/lightbox-root.js +76 -8
  130. package/dist/lightbox/zoom.d.ts +2 -0
  131. package/dist/lightbox/zoom.js +5 -3
  132. package/dist/line-chart/root/line-chart-root.d.ts +16 -0
  133. package/dist/line-chart/root/line-chart-root.js +10 -2
  134. package/dist/list/root/list-root.d.ts +10 -2
  135. package/dist/list/root/list-root.js +31 -24
  136. package/dist/list/selection.d.ts +17 -0
  137. package/dist/list/selection.js +16 -0
  138. package/dist/listbox/root/listbox-root.d.ts +125 -26
  139. package/dist/listbox/root/listbox-root.js +156 -166
  140. package/dist/map/marker/map-marker.d.ts +2 -1
  141. package/dist/map/root/map-root.d.ts +40 -2
  142. package/dist/map/root/map-root.js +64 -23
  143. package/dist/megamenu/hand-over.d.ts +75 -0
  144. package/dist/megamenu/hand-over.js +67 -0
  145. package/dist/megamenu/root/megamenu-root.d.ts +38 -14
  146. package/dist/megamenu/root/megamenu-root.js +29 -50
  147. package/dist/menu/item/menu-item.js +4 -4
  148. package/dist/menu/root/menu-root.d.ts +5 -2
  149. package/dist/meter/root/meter-root.d.ts +42 -0
  150. package/dist/meter/root/meter-root.js +33 -7
  151. package/dist/navigation/root/navigation-root.js +5 -8
  152. package/dist/notifications/group/notification-group.d.ts +8 -1
  153. package/dist/notifications/group/notification-group.js +12 -6
  154. package/dist/notifications/notification/notification.d.ts +12 -1
  155. package/dist/notifications/notification/notification.js +3 -1
  156. package/dist/notifications/root/notifications-root.d.ts +48 -3
  157. package/dist/notifications/root/notifications-root.js +36 -6
  158. package/dist/number-input/grain.d.ts +28 -0
  159. package/dist/number-input/grain.js +14 -0
  160. package/dist/number-input/root/number-input-root.d.ts +17 -1
  161. package/dist/number-input/root/number-input-root.js +29 -18
  162. package/dist/object-inspector/root/object-inspector-root.d.ts +17 -6
  163. package/dist/object-inspector/root/object-inspector-root.js +31 -20
  164. package/dist/option/index.d.ts +1 -0
  165. package/dist/option/index.js +1 -0
  166. package/dist/option/separator/option-separator.d.ts +30 -0
  167. package/dist/option/separator/option-separator.js +21 -0
  168. package/dist/outline/root/outline-root.d.ts +12 -0
  169. package/dist/outline/root/outline-root.js +5 -19
  170. package/dist/pagination/size/pagination-size.d.ts +6 -0
  171. package/dist/pagination/size/pagination-size.js +5 -3
  172. package/dist/pin-input/code.d.ts +51 -1
  173. package/dist/pin-input/code.js +37 -0
  174. package/dist/pin-input/root/pin-input-root.d.ts +98 -121
  175. package/dist/pin-input/root/pin-input-root.js +151 -156
  176. package/dist/popover/hide-outside.js +2 -9
  177. package/dist/popover/root/popover-root.d.ts +7 -1
  178. package/dist/popover/root/popover-root.js +9 -1
  179. package/dist/progress/root/progress-root.d.ts +27 -0
  180. package/dist/progress/root/progress-root.js +25 -7
  181. package/dist/qr-code/export.d.ts +19 -0
  182. package/dist/qr-code/export.js +14 -0
  183. package/dist/qr-code/name.d.ts +11 -0
  184. package/dist/qr-code/name.js +7 -0
  185. package/dist/qr-code/root/qr-code-root.d.ts +31 -9
  186. package/dist/qr-code/root/qr-code-root.js +29 -17
  187. package/dist/radio/group/radio-group.d.ts +15 -0
  188. package/dist/radio/root/radio-root.js +2 -2
  189. package/dist/rating/root/rating-root.d.ts +22 -9
  190. package/dist/rating/root/rating-root.js +15 -39
  191. package/dist/scatter-chart/measures.d.ts +22 -0
  192. package/dist/scatter-chart/measures.js +11 -0
  193. package/dist/scatter-chart/root/scatter-chart-root.d.ts +26 -0
  194. package/dist/scatter-chart/root/scatter-chart-root.js +21 -7
  195. package/dist/scroll-area/scrollbar.d.ts +0 -8
  196. package/dist/scroll-area/scrollbar.js +1 -1
  197. package/dist/select/align.d.ts +47 -0
  198. package/dist/select/align.js +7 -0
  199. package/dist/select/root/select-root.d.ts +115 -11
  200. package/dist/select/root/select-root.js +104 -20
  201. package/dist/slider/root/slider-root.d.ts +18 -1
  202. package/dist/slider/root/slider-root.js +22 -0
  203. package/dist/sparkline/readings.d.ts +30 -12
  204. package/dist/sparkline/readings.js +32 -15
  205. package/dist/sparkline/root/sparkline-root.d.ts +46 -3
  206. package/dist/sparkline/root/sparkline-root.js +60 -21
  207. package/dist/speed-dial/group/speed-dial-group.d.ts +48 -0
  208. package/dist/speed-dial/group/speed-dial-group.js +43 -0
  209. package/dist/speed-dial/index.d.ts +1 -0
  210. package/dist/speed-dial/index.js +1 -0
  211. package/dist/speed-dial/item/speed-dial-item.d.ts +27 -2
  212. package/dist/speed-dial/item/speed-dial-item.js +43 -5
  213. package/dist/speed-dial/long-press.d.ts +50 -0
  214. package/dist/speed-dial/long-press.js +48 -0
  215. package/dist/speed-dial/root/speed-dial-root.d.ts +86 -1
  216. package/dist/speed-dial/root/speed-dial-root.js +123 -6
  217. package/dist/splitter/root/splitter-root.d.ts +9 -2
  218. package/dist/splitter/root/splitter-root.js +18 -3
  219. package/dist/steps/root/steps-root.d.ts +8 -1
  220. package/dist/steps/root/steps-root.js +6 -1
  221. package/dist/steps/step/step.d.ts +1 -0
  222. package/dist/steps/step/step.js +1 -1
  223. package/dist/table/cell/table-cell.d.ts +8 -1
  224. package/dist/table/cell/table-cell.js +6 -2
  225. package/dist/table/column/table-column.d.ts +6 -0
  226. package/dist/table/column/table-column.js +5 -1
  227. package/dist/table/grid-controller.d.ts +117 -0
  228. package/dist/table/grid-controller.js +293 -0
  229. package/dist/table/root/table-root.d.ts +47 -3
  230. package/dist/table/root/table-root.js +93 -29
  231. package/dist/table/row/table-row.d.ts +36 -0
  232. package/dist/table/row/table-row.js +52 -2
  233. package/dist/tabs/root/tabs-root.d.ts +5 -3
  234. package/dist/tag/group/tag-group.d.ts +16 -4
  235. package/dist/tag/group/tag-group.js +23 -20
  236. package/dist/textarea/root/textarea-root.d.ts +14 -2
  237. package/dist/textarea/root/textarea-root.js +6 -7
  238. package/dist/time-input/root/time-input-root.d.ts +5 -1
  239. package/dist/time-input/root/time-input-root.js +10 -7
  240. package/dist/time-picker/root/time-picker-root.d.ts +12 -14
  241. package/dist/time-picker/root/time-picker-root.js +18 -23
  242. package/dist/toast/item/toast-item.d.ts +9 -6
  243. package/dist/toast/item/toast-item.js +1 -1
  244. package/dist/toast/root/toast-root.d.ts +40 -4
  245. package/dist/toast/root/toast-root.js +68 -16
  246. package/dist/toast/words.d.ts +12 -0
  247. package/dist/toast/words.js +25 -0
  248. package/dist/toggle/group/toggle-group.d.ts +29 -5
  249. package/dist/toggle/group/toggle-group.js +26 -43
  250. package/dist/toggle/root/toggle-root.js +2 -4
  251. package/dist/toolbar/root/toolbar-root.d.ts +52 -5
  252. package/dist/toolbar/root/toolbar-root.js +88 -13
  253. package/dist/tooltip/root/tooltip-root.d.ts +7 -1
  254. package/dist/tooltip/root/tooltip-root.js +10 -2
  255. package/dist/tour/root/tour-root.d.ts +15 -0
  256. package/dist/tour/root/tour-root.js +20 -1
  257. package/dist/tour/stop/tour-stop.d.ts +3 -0
  258. package/dist/tour/stop/tour-stop.js +3 -3
  259. package/dist/tree/root/tree-root.js +2 -2
  260. package/dist/trend/figure.d.ts +16 -0
  261. package/dist/trend/figure.js +8 -0
  262. package/dist/trend/root/trend-root.d.ts +41 -2
  263. package/dist/trend/root/trend-root.js +29 -3
  264. package/dist/video/root/extra-controls.d.ts +12 -0
  265. package/dist/video/root/extra-controls.js +6 -2
  266. package/dist/video/root/video-root.d.ts +16 -1
  267. package/dist/video/root/video-root.js +15 -1
  268. package/dist/video-playlist/root/video-playlist-root.d.ts +11 -1
  269. package/dist/video-playlist/root/video-playlist-root.js +3 -0
  270. package/dist/virtual-list/fill.d.ts +11 -0
  271. package/dist/virtual-list/fill.js +9 -0
  272. package/dist/virtual-list/keys.d.ts +16 -7
  273. package/dist/virtual-list/keys.js +17 -6
  274. package/dist/virtual-list/plan.d.ts +17 -1
  275. package/dist/virtual-list/plan.js +13 -2
  276. package/dist/virtual-list/root/virtual-list-root.d.ts +25 -3
  277. package/dist/virtual-list/root/virtual-list-root.js +53 -41
  278. package/lint.json +1 -1
  279. package/package.json +5 -5
  280. package/types/jsx.d.ts +4934 -954
  281. package/types/svelte.d.ts +2 -0
  282. package/types/vue.d.ts +946 -253
  283. package/dist/color-picker/swatches.d.ts +0 -12
  284. package/dist/color-picker/swatches.js +0 -26
  285. package/dist/rating/steps.d.ts +0 -7
  286. package/dist/rating/steps.js +0 -9
  287. package/dist/tooltip/focus-target.d.ts +0 -6
  288. package/dist/tooltip/focus-target.js +0 -9
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { controlsIn, customElement, deepActiveElement, deepContains, fillTemplate, HelixActivateEvent, HelixBeforeActivateEvent, HelixCompleteEvent, HelixElement, HelixTourStatusEvent, hostAria, MixinControllerOpen, requestChange, whenHydrated, } from '@fusion.dev/helix-core';
7
+ import { controlsIn, customElement, deepActiveElement, deepContains, fillTemplate, HelixActivateEvent, HelixBeforeActivateEvent, HelixCompleteEvent, HelixElement, HelixTourStatusEvent, hostAria, inlineStep, keyClaimed, MixinControllerOpen, requestChange, whenHydrated, } from '@fusion.dev/helix-core';
8
8
  import { html } from 'lit';
9
9
  import { property, queryAssignedElements, state } from 'lit/decorators.js';
10
10
  import { stopBefore } from '../back.js';
@@ -36,6 +36,20 @@ let HelixHeadlessTour = class HelixHeadlessTour extends MixinControllerOpen(Heli
36
36
  this.skip();
37
37
  }
38
38
  };
39
+ this.handleArrows = (event) => {
40
+ if (!this.open || keyClaimed(event) || event.altKey || event.ctrlKey || event.metaKey) {
41
+ return;
42
+ }
43
+ const along = inlineStep(this, event.key);
44
+ if (along === 1 && this.index < this.length - 1) {
45
+ event.preventDefault();
46
+ this.next();
47
+ }
48
+ else if (along === -1 && this.canGoBack) {
49
+ event.preventDefault();
50
+ this.back();
51
+ }
52
+ };
39
53
  this.handleKeydown = (event) => {
40
54
  if (this.open && event.key === 'Escape' && !event.defaultPrevented) {
41
55
  event.preventDefault();
@@ -99,9 +113,13 @@ let HelixHeadlessTour = class HelixHeadlessTour extends MixinControllerOpen(Heli
99
113
  this.dispatchEvent(new HelixActivateEvent({ value: String(this.index) }));
100
114
  }
101
115
  }
116
+ get canGoBack() {
117
+ return (stopBefore(this.stops.map((stop) => stop.type), this.index) !== undefined);
118
+ }
102
119
  connectedCallback() {
103
120
  super.connectedCallback();
104
121
  this.addEventListener('helix-tour-ask', this.handleAsk);
122
+ this.addEventListener('keydown', this.handleArrows);
105
123
  document.addEventListener('keydown', this.handleKeydown);
106
124
  window.addEventListener('resize', this.handleViewport, { passive: true });
107
125
  window.addEventListener('scroll', this.handleViewport, { capture: true, passive: true });
@@ -112,6 +130,7 @@ let HelixHeadlessTour = class HelixHeadlessTour extends MixinControllerOpen(Heli
112
130
  disconnectedCallback() {
113
131
  super.disconnectedCallback();
114
132
  this.removeEventListener('helix-tour-ask', this.handleAsk);
133
+ this.removeEventListener('keydown', this.handleArrows);
115
134
  document.removeEventListener('keydown', this.handleKeydown);
116
135
  window.removeEventListener('resize', this.handleViewport);
117
136
  window.removeEventListener('scroll', this.handleViewport, { capture: true });
@@ -62,6 +62,7 @@ import '../../button/index.js';
62
62
  * @csspart back - The control that goes to the previous stop.
63
63
  * @csspart back-button - The button inside `back`.
64
64
  * @csspart back-live - The live region inside `back`.
65
+ * @csspart back-spinner - The ring `back` turns while it is pending.
65
66
  * @csspart back-text - The words on that button, trimmed to their capitals and baseline.
66
67
  * @csspart bubble - The panel that points at the target.
67
68
  * @csspart content - The region holding the slotted content.
@@ -70,11 +71,13 @@ import '../../button/index.js';
70
71
  * @csspart next - The control that goes to the next stop, and finishes the tour on the last stop.
71
72
  * @csspart next-button - The button inside `next`.
72
73
  * @csspart next-live - The live region inside `next`.
74
+ * @csspart next-spinner - The ring `next` turns while it is pending.
73
75
  * @csspart next-text - The words on that button, trimmed to their capitals and baseline.
74
76
  * @csspart progress - The stop's place in the tour, such as "2 of 5". It describes the panel, and is shown when the tour has `progress`.
75
77
  * @csspart skip - The control that ends the tour early.
76
78
  * @csspart skip-button - The button inside `skip`.
77
79
  * @csspart skip-live - The live region inside `skip`.
80
+ * @csspart skip-spinner - The ring `skip` turns while it is pending.
78
81
  * @csspart skip-text - The words on that button, trimmed to their capitals and baseline.
79
82
  *
80
83
  * @cssstate placed-bottom - The panel sits below its target.
@@ -86,14 +86,14 @@ let HelixHeadlessTourStop = class HelixHeadlessTourStop extends HelixElement {
86
86
  return html `<slot name="actions">
87
87
  <${this.tag('button')}
88
88
  part="skip"
89
- exportparts="button: skip-button, live: skip-live, text: skip-text" emphasis="ghost" @click=${() => this.ask('skip')}>
89
+ exportparts="button: skip-button, live: skip-live, spinner: skip-spinner, text: skip-text" emphasis="ghost" @click=${() => this.ask('skip')}>
90
90
  ${this.skipLabel}
91
91
  </${this.tag('button')}>
92
92
  ${this.first
93
93
  ? ''
94
94
  : html `<${this.tag('button')}
95
95
  part="back"
96
- exportparts="button: back-button, live: back-live, text: back-text"
96
+ exportparts="button: back-button, live: back-live, spinner: back-spinner, text: back-text"
97
97
  emphasis="outlined"
98
98
  @click=${() => this.ask('back')}
99
99
  >
@@ -101,7 +101,7 @@ let HelixHeadlessTourStop = class HelixHeadlessTourStop extends HelixElement {
101
101
  </${this.tag('button')}>`}
102
102
  <${this.tag('button')}
103
103
  part="next"
104
- exportparts="button: next-button, live: next-live, text: next-text"
104
+ exportparts="button: next-button, live: next-live, spinner: next-spinner, text: next-text"
105
105
  emphasis="solid"
106
106
  tone="primary"
107
107
  @click=${() => this.ask('next')}
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { AriaForwardController, closestInstance, commaList, customElement, HelixElement, inlineStep, keyClaimed, MixinCollection, MixinControllerActive, scrollItemIntoView, slottedWords, Typeahead, treeArrow, typesCharacter, whenHydrated, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, closestInstance, commaList, customElement, HelixElement, inlineStep, keyClaimed, MixinCollection, MixinControllerActive, scrollItemIntoView, selectsAll, slottedWords, Typeahead, treeArrow, typesCharacter, whenHydrated, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { HelixHeadlessTreeItem } from '../item/tree-item.js';
@@ -217,7 +217,7 @@ let HelixHeadlessTree = class HelixHeadlessTree extends MixinCollection(MixinCon
217
217
  return true;
218
218
  }
219
219
  handleMultiple(current, event) {
220
- if ((event.ctrlKey || event.metaKey) && !event.altKey && event.key.toLowerCase() === 'a') {
220
+ if (selectsAll(event)) {
221
221
  const leaves = this.items
222
222
  .filter((item) => !this.holds(item) && !item.disabled && item.value !== '')
223
223
  .map((item) => item.value);
@@ -0,0 +1,16 @@
1
+ /**
2
+ * The movement a signed change makes: `up` above zero, `down` below it and
3
+ * `neutral` at zero. Nothing for a value that is no number, so a trend with
4
+ * none falls back to what its attributes say.
5
+ */
6
+ export declare const movementOf: (value: number | undefined) => 'down' | 'neutral' | 'up' | undefined;
7
+ /**
8
+ * A change written in a locale with `Intl.NumberFormat` options, such as
9
+ * `12%` for 0.12 with `{ style: 'percent' }`.
10
+ *
11
+ * **The size of the change, with no sign**, because the movement is already
12
+ * said by the arrow and the word: "Down, worse 8%" rather than "Down, worse
13
+ * -8%". Options that set `signDisplay` ask for the sign, and get the value as
14
+ * it is.
15
+ */
16
+ export declare const figureOf: (value: number, locale?: string, options?: Intl.NumberFormatOptions) => string;
@@ -0,0 +1,8 @@
1
+ import { formatNumber } from '@fusion.dev/helix-core';
2
+ export const movementOf = (value) => {
3
+ if (value === undefined || !Number.isFinite(value)) {
4
+ return undefined;
5
+ }
6
+ return value > 0 ? 'up' : value < 0 ? 'down' : 'neutral';
7
+ };
8
+ export const figureOf = (value, locale = '', options) => formatNumber(options?.signDisplay === undefined ? Math.abs(value) : value, locale, options);
@@ -21,6 +21,12 @@ import { type PropertyValues, type TemplateResult } from 'lit';
21
21
  * `worse-label`, so a page in another language can set its own. The arrow and
22
22
  * any glyph slotted in its place are hidden from screen readers.
23
23
  *
24
+ * **Or give the change as a number.** `value` is the signed change, such as
25
+ * `0.12` or `-8`: its sign is the movement, and its size is written as the
26
+ * metric with `Intl.NumberFormat`, in `locale` and with `format-options`,
27
+ * such as `{"style":"percent"}` for `12%`. `movement` still wins when it is
28
+ * written. Without `value` the metric is what the page slots.
29
+ *
24
30
  * Slot one glyph per movement into `up-icon`, `down-icon` and `neutral-icon`,
25
31
  * and the trend shows the one that matches; a glyph in `icon` stands in for
26
32
  * all three. Inside a `Stat`, put the trend in the stat's `trend` slot so it is
@@ -36,7 +42,7 @@ import { type PropertyValues, type TemplateResult } from 'lit';
36
42
  * @aliases Delta, Change, Movement
37
43
  * @documentation https://fusion.dev/helix/components/trend
38
44
  *
39
- * @slot - The metric that moved.
45
+ * @slot - The metric that moved. Not needed with `value`, which writes it.
40
46
  * @slot down-icon - Replaces the arrow while `movement` is `down`. Hidden from screen readers.
41
47
  * @slot icon - Replaces the arrow for every movement, such as with a glyph of your own set. Hidden from screen readers, since the movement is also a word.
42
48
  * @slot label - The period the metric moved over.
@@ -45,6 +51,7 @@ import { type PropertyValues, type TemplateResult } from 'lit';
45
51
  *
46
52
  * @csspart direction - The movement as a word, for people who cannot see the arrow.
47
53
  * @csspart icon - The arrow.
54
+ * @csspart value - The change written from `value`, hidden while `value` is no number.
48
55
  *
49
56
  * @cssstate better - The trend moved the way `good` names.
50
57
  * @cssstate worse - The trend moved against the way `good` names.
@@ -66,6 +73,20 @@ export declare class HelixHeadlessTrend extends HelixElement {
66
73
  * The word read out for a move down.
67
74
  */
68
75
  downLabel: string;
76
+ /**
77
+ * `Intl.NumberFormat` options `value` is written with, as React Aria's
78
+ * NumberField takes them: a percent, a currency, a unit or a number of
79
+ * decimals. Written as JSON in the attribute.
80
+ *
81
+ * ```html
82
+ * <helix-trend value="-0.084" format-options='{"style":"percent","maximumFractionDigits":1}'></helix-trend>
83
+ * ```
84
+ *
85
+ * The example says "Down 8.4%". The size is written without a sign, since
86
+ * the arrow and the word say which way; set `signDisplay` for one. Absent
87
+ * writes the number as the locale writes any number.
88
+ */
89
+ formatOptions?: Intl.NumberFormatOptions;
69
90
  /**
70
91
  * Which movement counts as an improvement for this metric.
71
92
  *
@@ -75,6 +96,13 @@ export declare class HelixHeadlessTrend extends HelixElement {
75
96
  * movement changes underneath.
76
97
  */
77
98
  good?: 'down' | 'up';
99
+ /**
100
+ * The locale `value` is written in, such as `de` for `1.234,5`.
101
+ *
102
+ * Absent uses the runtime's own. Set it on a page rendered on a server, so
103
+ * the server and the browser write the figure alike.
104
+ */
105
+ locale: string;
78
106
  /**
79
107
  * Which way the measurement moved.
80
108
  *
@@ -94,6 +122,16 @@ export declare class HelixHeadlessTrend extends HelixElement {
94
122
  * The word read out for a move up.
95
123
  */
96
124
  upLabel: string;
125
+ /**
126
+ * The change as a signed number, such as `0.12` or `-8`, which sets the
127
+ * movement by its sign and is written as the metric.
128
+ *
129
+ * Above zero is up, below is down, and zero is neutral; `movement` wins when
130
+ * both are written. The size is written in the `value` part with
131
+ * `format-options` and `locale`, in place of a slotted metric. Absent leaves
132
+ * the movement to `movement` and the metric to the slot.
133
+ */
134
+ value?: number;
97
135
  /**
98
136
  * The word read out after the direction when the move is a setback.
99
137
  */
@@ -110,10 +148,11 @@ export declare class HelixHeadlessTrend extends HelixElement {
110
148
  content: string;
111
149
  viewBox: string;
112
150
  }): TemplateResult;
113
- /** The movement, read from `movement` and then from the old `direction`. */
151
+ /** The movement, read from `movement`, then from the old `direction`, then from the sign of `value`. */
114
152
  private get moved();
115
153
  /** Better or worse, or neither when `good` is unset or nothing moved. */
116
154
  private get judged();
155
+ protected updated(changed: PropertyValues): void;
117
156
  protected willUpdate(changed: PropertyValues): void;
118
157
  protected render(): TemplateResult;
119
158
  }
@@ -4,11 +4,12 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { customElement, HelixElement } from '@fusion.dev/helix-core';
7
+ import { customElement, HelixElement, reportRuntime } from '@fusion.dev/helix-core';
8
8
  import { arrowDown, arrowUp, minus } from '@fusion.dev/helix-icons';
9
9
  import { html } from 'lit';
10
10
  import { property } from 'lit/decorators.js';
11
11
  import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
12
+ import { figureOf, movementOf } from '../figure.js';
12
13
  const DIRECTIONS = {
13
14
  down: { icon: arrowDown, word: 'downLabel' },
14
15
  neutral: { icon: minus, word: 'unchangedLabel' },
@@ -20,6 +21,7 @@ let HelixHeadlessTrend = class HelixHeadlessTrend extends HelixElement {
20
21
  super(...arguments);
21
22
  this.betterLabel = 'better';
22
23
  this.downLabel = 'Down';
24
+ this.locale = '';
23
25
  this.unchangedLabel = 'Unchanged';
24
26
  this.upLabel = 'Up';
25
27
  this.worseLabel = 'worse';
@@ -38,7 +40,7 @@ let HelixHeadlessTrend = class HelixHeadlessTrend extends HelixElement {
38
40
  >`;
39
41
  }
40
42
  get moved() {
41
- return this.movement ?? this.direction;
43
+ return this.movement ?? this.direction ?? movementOf(this.value);
42
44
  }
43
45
  get judged() {
44
46
  const moved = this.moved;
@@ -47,6 +49,17 @@ let HelixHeadlessTrend = class HelixHeadlessTrend extends HelixElement {
47
49
  }
48
50
  return this.good === moved ? 'better' : 'worse';
49
51
  }
52
+ updated(changed) {
53
+ super.updated(changed);
54
+ if (changed.has('direction') && this.direction && changed.get('direction') === undefined) {
55
+ reportRuntime({
56
+ kind: 'deprecated',
57
+ message: `<${this.localName} direction="${this.direction}"> is deprecated: write ` +
58
+ `movement="${this.direction}", which says the same. \`direction\` is a layout on Stat and Flex.`,
59
+ tag: this.localName,
60
+ });
61
+ }
62
+ }
50
63
  willUpdate(changed) {
51
64
  super.willUpdate(changed);
52
65
  const judged = this.judged;
@@ -64,9 +77,13 @@ let HelixHeadlessTrend = class HelixHeadlessTrend extends HelixElement {
64
77
  : moved === 'down'
65
78
  ? html `<slot aria-hidden="true" name="down-icon">${icon}</slot>`
66
79
  : html `<slot aria-hidden="true" name="neutral-icon">${icon}</slot>`;
80
+ const value = this.value;
81
+ const figure = value === undefined || !Number.isFinite(value)
82
+ ? ''
83
+ : figureOf(value, this.locale, this.formatOptions);
67
84
  return html `${glyph}
68
85
  <span part="direction">${`${this[direction.word]}${judgment}`}</span>
69
- <slot></slot>
86
+ <span part="value" ?hidden=${figure === ''}>${figure}</span><slot></slot>
70
87
  <slot name="label"></slot>`;
71
88
  }
72
89
  };
@@ -79,9 +96,15 @@ __decorate([
79
96
  __decorate([
80
97
  property({ attribute: 'down-label' })
81
98
  ], HelixHeadlessTrend.prototype, "downLabel", void 0);
99
+ __decorate([
100
+ property({ attribute: 'format-options', type: Object })
101
+ ], HelixHeadlessTrend.prototype, "formatOptions", void 0);
82
102
  __decorate([
83
103
  property({ reflect: true })
84
104
  ], HelixHeadlessTrend.prototype, "good", void 0);
105
+ __decorate([
106
+ property()
107
+ ], HelixHeadlessTrend.prototype, "locale", void 0);
85
108
  __decorate([
86
109
  property({ reflect: true })
87
110
  ], HelixHeadlessTrend.prototype, "movement", void 0);
@@ -91,6 +114,9 @@ __decorate([
91
114
  __decorate([
92
115
  property({ attribute: 'up-label' })
93
116
  ], HelixHeadlessTrend.prototype, "upLabel", void 0);
117
+ __decorate([
118
+ property({ type: Number })
119
+ ], HelixHeadlessTrend.prototype, "value", void 0);
94
120
  __decorate([
95
121
  property({ attribute: 'worse-label' })
96
122
  ], HelixHeadlessTrend.prototype, "worseLabel", void 0);
@@ -20,6 +20,18 @@ export interface Conditions {
20
20
  }
21
21
  /** Whether this platform can do a named control at all. */
22
22
  export declare const offers: (one: string, player: Conditions) => boolean;
23
+ /**
24
+ * What `full` stands for in a list of controls: every control that does
25
+ * something to the video by itself, as Web Awesome's `full` preset adds speed
26
+ * and picture in picture to its standard bar.
27
+ *
28
+ * `download`, `previous` and `next` are left out. Offering the file is a
29
+ * decision about the content, and the other two move a set the player cannot
30
+ * see, so each is named where it is wanted.
31
+ */
32
+ export declare const FULL_CONTROLS: readonly ['rewind', 'forward', 'speed', 'pip', 'remote'];
33
+ /** The names in a written list, with `full` read as the controls it stands for, each once. */
34
+ export declare const controlNames: (written: string) => string[];
23
35
  /** The extras asked for, in order, and only the ones this platform can do. */
24
36
  export declare const barControls: (written: string, player: Conditions) => string[];
25
37
  /**
@@ -20,10 +20,14 @@ export const offers = (one, player) => {
20
20
  }
21
21
  return one === 'speed';
22
22
  };
23
- export const barControls = (written, player) => writtenList(written).filter((one) => offers(one, player));
23
+ export const FULL_CONTROLS = ['rewind', 'forward', 'speed', 'pip', 'remote'];
24
+ export const controlNames = (written) => [
25
+ ...new Set(writtenList(written).flatMap((one) => one === 'full' ? FULL_CONTROLS : [one])),
26
+ ];
27
+ export const barControls = (written, player) => controlNames(written).filter((one) => offers(one, player));
24
28
  export const menuControls = (written, onBar, player) => {
25
29
  const bar = new Set(onBar);
26
- return writtenList(written).filter((one) => !bar.has(one) && offers(one, player));
30
+ return controlNames(written).filter((one) => !bar.has(one) && offers(one, player));
27
31
  };
28
32
  export const ownFile = (src) => {
29
33
  if (src === '') {
@@ -161,6 +161,15 @@ import '../../menu/index.js';
161
161
  * @cssproperty --video-intrinsic-ratio - The media's own aspect ratio, once it is known. `--video-aspect-ratio` overrides it.
162
162
  * @cssproperty --video-level - The current volume, 0 to 1.
163
163
  * @cssproperty --video-progress - How far playback has reached, as a fraction of the duration.
164
+ *
165
+ * @cssstate ended - Playback reached the end, until it starts again or the source changes.
166
+ * @cssstate error - The source could not be played, and `failure` shows.
167
+ * @cssstate fullscreen - The player fills the screen.
168
+ * @cssstate idle - The video plays and the user has been still for `idle-delay`, so the bar hides.
169
+ * @cssstate muted - The sound is off.
170
+ * @cssstate paused - Playback is paused, which is where every player starts.
171
+ * @cssstate playing - Playback runs, waiting for data included.
172
+ * @cssstate waiting - Playback stopped to wait for more data, and `loading` shows.
164
173
  */
165
174
  export declare class HelixHeadlessVideo extends HelixElement {
166
175
  /**
@@ -242,7 +251,11 @@ export declare class HelixHeadlessVideo extends HelixElement {
242
251
  * that cannot work is worse than one that is not there.
243
252
  *
244
253
  * The names are `speed`, `pip`, `remote`, `download`, `rewind`, `forward`,
245
- * `previous` and `next`.
254
+ * `previous` and `next`. `full` stands for `rewind`, `forward`, `speed`,
255
+ * `pip` and `remote` in that order, the controls that act on the video by
256
+ * themselves, as Web Awesome's `full` preset does, and a name it already
257
+ * holds is drawn once. `extra-controls="full"` is the whole bar, and
258
+ * `overflow-controls="full"` puts all of it behind one button.
246
259
  *
247
260
  * **`previous` and `next` do nothing to the video, and are offered anyway.**
248
261
  * They announce `helix-previous` and `helix-next` and stop there, so what
@@ -448,6 +461,8 @@ export declare class HelixHeadlessVideo extends HelixElement {
448
461
  */
449
462
  private failed;
450
463
  private playing;
464
+ /** Whether playback reached the end, for `:state(ended)`. */
465
+ private finished;
451
466
  private position;
452
467
  private knownDuration;
453
468
  private loudness;
@@ -72,6 +72,7 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
72
72
  this.buffering = false;
73
73
  this.failed = false;
74
74
  this.playing = false;
75
+ this.finished = false;
75
76
  this.position = 0;
76
77
  this.knownDuration = 0;
77
78
  this.loudness = 1;
@@ -127,6 +128,7 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
127
128
  };
128
129
  this.handleTime = () => {
129
130
  this.position = this.video?.currentTime ?? 0;
131
+ this.finished = this.video?.ended ?? false;
130
132
  };
131
133
  this.handleWaiting = () => {
132
134
  this.buffering = true;
@@ -144,7 +146,8 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
144
146
  this.handleState = (event) => {
145
147
  const video = this.video;
146
148
  this.playing = video ? !video.paused : false;
147
- if (event.type === 'emptied' || event.type === 'pause') {
149
+ this.finished = video?.ended ?? false;
150
+ if (event.type === 'emptied' || event.type === 'ended' || event.type === 'pause') {
148
151
  this.buffering = false;
149
152
  }
150
153
  if (event.type === 'play') {
@@ -438,6 +441,14 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
438
441
  super.updated?.(changed);
439
442
  this.toggleAttribute('data-playing', this.playing);
440
443
  this.toggleAttribute('data-idle', this.idle && this.playing);
444
+ this.toggleState('ended', this.finished);
445
+ this.toggleState('error', this.failed);
446
+ this.toggleState('fullscreen', this.full);
447
+ this.toggleState('idle', this.idle && this.playing);
448
+ this.toggleState('muted', this.silent);
449
+ this.toggleState('paused', !this.playing);
450
+ this.toggleState('playing', this.playing);
451
+ this.toggleState('waiting', this.buffering);
441
452
  if (changed.has('media')) {
442
453
  this.video?.load();
443
454
  }
@@ -1010,6 +1021,9 @@ __decorate([
1010
1021
  __decorate([
1011
1022
  state()
1012
1023
  ], HelixHeadlessVideo.prototype, "playing", void 0);
1024
+ __decorate([
1025
+ state()
1026
+ ], HelixHeadlessVideo.prototype, "finished", void 0);
1013
1027
  __decorate([
1014
1028
  state()
1015
1029
  ], HelixHeadlessVideo.prototype, "position", void 0);
@@ -1,5 +1,6 @@
1
1
  import { HelixElement } from '@fusion.dev/helix-core';
2
2
  import { type PropertyValues, type TemplateResult } from 'lit';
3
+ import { HelixHeadlessVideoPlaylistItem } from '../item/video-playlist-item.js';
3
4
  import { type Repeat } from '../order.js';
4
5
  declare const HelixHeadlessVideoPlaylist_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerActiveInterface) & typeof HelixElement;
5
6
  /**
@@ -44,7 +45,7 @@ declare const HelixHeadlessVideoPlaylist_base: (new (...args: any[]) => import("
44
45
  * @slot - The entries.
45
46
  * @slot player - The player that plays the entries.
46
47
  *
47
- * @event {HelixActivateEvent} helix-activate - The current entry changed. The playlist already holds the new entry when this is sent, and `detail.previous` is the entry it left.
48
+ * @event {HelixActivateEvent} helix-activate - The current entry changed. The playlist already holds the new entry when this is sent, and `detail.previous` is the entry it left. `currentEntry` is the entry itself, with its `src`, `poster` and name.
48
49
  * @event {HelixBeforeActivateEvent} helix-before-activate - The current entry is about to change. Cancelable: cancel it to keep the current entry.
49
50
  * @event {HelixEndEvent} helix-end - The last entry finished playing, and the playlist stopped there. Not sent while `repeat` is set.
50
51
  *
@@ -137,6 +138,15 @@ export declare class HelixHeadlessVideoPlaylist extends HelixHeadlessVideoPlayli
137
138
  * under `repeat="one"` asks for, and a list of one under `repeat="all"`.
138
139
  */
139
140
  private replay;
141
+ /**
142
+ * The entry that is current, with the `src`, `poster`, `value` and name the
143
+ * player was given, or `undefined` in an empty playlist.
144
+ *
145
+ * Read it in a `helix-activate` listener for the clip the playlist moved to,
146
+ * which Web Awesome's change event carries: the event says which entry by
147
+ * value, and this is the entry.
148
+ */
149
+ get currentEntry(): HelixHeadlessVideoPlaylistItem | undefined;
140
150
  /**
141
151
  * Moves to the next entry and plays it, as the player's next control does.
142
152
  * Follows `shuffle`, goes round under `repeat="all"`, and stays on the last
@@ -81,6 +81,9 @@ let HelixHeadlessVideoPlaylist = class HelixHeadlessVideoPlaylist extends MixinC
81
81
  player.currentTime = 0;
82
82
  void Promise.resolve(player.play()).catch(() => { });
83
83
  }
84
+ get currentEntry() {
85
+ return this.resolveActive(this.items);
86
+ }
84
87
  next() {
85
88
  this.step(1);
86
89
  }
@@ -20,3 +20,14 @@
20
20
  export declare const textOf: (value: unknown) => string;
21
21
  /** Fills one element's holes, its own and those below it, from an item. */
22
22
  export declare const fillHoles: (root: Element, item: unknown) => void;
23
+ /**
24
+ * The key of item `at` in a selecting list's `value`: its `keyField` where it
25
+ * is an object and one is named, a string or a number item itself, and its
26
+ * place in the set otherwise.
27
+ */
28
+ export declare const keyOf: (item: unknown, at: number, keyField: string) => string;
29
+ /**
30
+ * The text typing matches for each item: its `field`, or a string or number
31
+ * item itself, and nothing where an object has no `field` to read.
32
+ */
33
+ export declare const textsOf: (data: readonly unknown[], field: string) => string[];
@@ -21,3 +21,12 @@ export const fillHoles = (root, item) => {
21
21
  }
22
22
  }
23
23
  };
24
+ export const keyOf = (item, at, keyField) => {
25
+ if (keyField !== '' && item !== null && typeof item === 'object') {
26
+ return textOf(valueAt(item, keyField));
27
+ }
28
+ return typeof item === 'string' || typeof item === 'number' ? String(item) : String(at);
29
+ };
30
+ export const textsOf = (data, field) => data.map((item) => field === '' || item === null || typeof item !== 'object'
31
+ ? textOf(item)
32
+ : textOf(valueAt(item, field)));
@@ -1,15 +1,24 @@
1
1
  /**
2
- * Which item a key moves to in a vertical list of `count` items, from `at`.
2
+ * Which item a key moves to in a list of `count` items laid out `across`
3
+ * items to a line, from `at`.
3
4
  *
4
5
  * Index arithmetic, because most of the items are not drawn: the list moves to
5
6
  * an index, scrolls it into view, and focuses the row once it is there. The
6
- * keys are the ones a listbox answers:
7
+ * keys are the ones a listbox answers, and in a grid the ones a grid of
8
+ * options answers:
7
9
  *
8
- * - Up and Down move one item.
9
- * - Home and End move to the first and the last.
10
- * - Page Up and Page Down move by `page`, the number of items on screen less
11
- * one, so the item at one edge of the box lands at the other.
10
+ * - Up and Down move one line, to the item in the same column. Down from the
11
+ * line above a shorter last line goes to the last item.
12
+ * - Left and Right move one item, in a grid only, with `forward` naming the
13
+ * one that moves ahead along the line: Right, or Left on a right-to-left
14
+ * page. In a list they move nothing, and are left to the row.
15
+ * - Home and End move to the first and the last item.
16
+ * - Page Up and Page Down move by `page` lines, the number of lines on screen
17
+ * less one, so the line at one edge of the box lands at the other.
12
18
  *
13
19
  * `undefined` for a key the list does not take, and for an empty list.
14
20
  */
15
- export declare const stepIndex: (key: string, at: number, count: number, page: number) => number | undefined;
21
+ export declare const stepIndex: (key: string, at: number, count: number, page: number, { across, forward }?: {
22
+ across?: number;
23
+ forward?: string;
24
+ }) => number | undefined;
@@ -1,23 +1,34 @@
1
- export const stepIndex = (key, at, count, page) => {
1
+ export const stepIndex = (key, at, count, page, { across = 1, forward = 'ArrowRight' } = {}) => {
2
2
  if (count <= 0) {
3
3
  return undefined;
4
4
  }
5
+ const width = Math.max(1, Math.trunc(across));
5
6
  const last = count - 1;
6
7
  const from = Math.max(0, Math.min(at, last));
7
- const jump = Math.max(1, page);
8
+ const line = Math.floor(from / width);
9
+ const along = (lines) => {
10
+ const to = Math.max(0, Math.min(line + lines, Math.floor(last / width)));
11
+ return Math.min(last, to * width + (from % width));
12
+ };
8
13
  switch (key) {
9
14
  case 'ArrowDown':
10
- return Math.min(last, from + 1);
15
+ return along(1);
11
16
  case 'ArrowUp':
12
- return Math.max(0, from - 1);
17
+ return along(-1);
18
+ case 'ArrowLeft':
19
+ case 'ArrowRight':
20
+ if (width === 1) {
21
+ return undefined;
22
+ }
23
+ return Math.max(0, Math.min(last, from + (key === forward ? 1 : -1)));
13
24
  case 'End':
14
25
  return last;
15
26
  case 'Home':
16
27
  return 0;
17
28
  case 'PageDown':
18
- return Math.min(last, from + jump);
29
+ return along(Math.max(1, page));
19
30
  case 'PageUp':
20
- return Math.max(0, from - jump);
31
+ return along(-Math.max(1, page));
21
32
  default:
22
33
  return undefined;
23
34
  }
@@ -27,6 +27,11 @@ export interface DrawInput {
27
27
  drawn: readonly number[];
28
28
  /** Whether every drawn row is drawn again from nothing, as for a new way of drawing. */
29
29
  redraw?: boolean;
30
+ /**
31
+ * How many items a line holds, in a grid. The kept item brings its whole
32
+ * line, so the columns of the lines around it stay where they are.
33
+ */
34
+ across?: number;
30
35
  }
31
36
  /**
32
37
  * Which items a pass draws, and which drawn rows leave.
@@ -34,7 +39,7 @@ export interface DrawInput {
34
39
  * A drawn row leaves when its item is not wanted, when it holds no item, or
35
40
  * when everything is drawn again. A wanted item with no row left is drawn new.
36
41
  */
37
- export declare const drawPlan: ({ count, drawn, first, kept, last, redraw, }: DrawInput) => DrawPlan;
42
+ export declare const drawPlan: ({ across, count, drawn, first, kept, last, redraw, }: DrawInput) => DrawPlan;
38
43
  /**
39
44
  * Which item holds the list's tab stop after a pass.
40
45
  *
@@ -47,3 +52,14 @@ export declare const stopAfter: (active: number, wanted: readonly number[], { co
47
52
  last: number;
48
53
  onScreen: number;
49
54
  }) => number;
55
+ /**
56
+ * The items a span of lines holds, `across` items to a line: what a window of
57
+ * lines draws, or which items a reader sees.
58
+ */
59
+ export declare const itemsOf: (lines: {
60
+ first: number;
61
+ last: number;
62
+ }, across: number, count: number) => {
63
+ first: number;
64
+ last: number;
65
+ };