@fusion.dev/helix-headless 0.142.1 → 0.144.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 (285) hide show
  1. package/custom-elements.json +25381 -1202
  2. package/dist/accordion/item/accordion-item.d.ts +38 -2
  3. package/dist/accordion/item/accordion-item.js +38 -6
  4. package/dist/accordion/root/accordion-root.d.ts +23 -1
  5. package/dist/accordion/root/accordion-root.js +39 -3
  6. package/dist/announcement/root/announcement-root.d.ts +11 -7
  7. package/dist/announcement/root/announcement-root.js +1 -1
  8. package/dist/app-header/root/app-header-root.d.ts +21 -0
  9. package/dist/app-header/root/app-header-root.js +31 -5
  10. package/dist/area-chart/root/area-chart-root.d.ts +12 -4
  11. package/dist/area-chart/root/area-chart-root.js +39 -11
  12. package/dist/avatar/group/avatar-group.d.ts +24 -3
  13. package/dist/avatar/group/avatar-group.js +34 -2
  14. package/dist/avatar/root/avatar-root.d.ts +17 -1
  15. package/dist/avatar/root/avatar-root.js +31 -10
  16. package/dist/badge/root/badge-root.d.ts +12 -0
  17. package/dist/badge/root/badge-root.js +1 -1
  18. package/dist/bar-chart/root/bar-chart-root.d.ts +30 -5
  19. package/dist/bar-chart/root/bar-chart-root.js +58 -21
  20. package/dist/blockquote/root/blockquote-root.d.ts +12 -7
  21. package/dist/blockquote/root/blockquote-root.js +6 -4
  22. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +9 -3
  23. package/dist/breadcrumbs/root/breadcrumbs-root.js +9 -1
  24. package/dist/button/root/button-root.d.ts +84 -1
  25. package/dist/button/root/button-root.js +81 -3
  26. package/dist/callout/root/callout-root.d.ts +32 -6
  27. package/dist/callout/root/callout-root.js +26 -7
  28. package/dist/canvas/root/canvas-root.d.ts +1 -0
  29. package/dist/canvas/root/canvas-root.js +10 -1
  30. package/dist/card/root/card-root.d.ts +30 -1
  31. package/dist/card/root/card-root.js +38 -5
  32. package/dist/carousel/root/carousel-root.d.ts +47 -5
  33. package/dist/carousel/root/carousel-root.js +53 -21
  34. package/dist/chat/composer/chat-composer.d.ts +13 -11
  35. package/dist/chat/composer/chat-composer.js +6 -12
  36. package/dist/chat/message/chat-message.js +8 -1
  37. package/dist/chat/root/chat-root.d.ts +1 -0
  38. package/dist/chat/root/chat-root.js +2 -1
  39. package/dist/checkbox/root/checkbox-root.d.ts +7 -0
  40. package/dist/checkbox/root/checkbox-root.js +9 -3
  41. package/dist/checkbox-group/root/checkbox-group-root.d.ts +116 -33
  42. package/dist/checkbox-group/root/checkbox-group-root.js +141 -44
  43. package/dist/code/root/code-root.d.ts +1 -1
  44. package/dist/code-block/lines.d.ts +13 -0
  45. package/dist/code-block/lines.js +2 -0
  46. package/dist/code-block/root/code-block-root.d.ts +49 -0
  47. package/dist/code-block/root/code-block-root.js +44 -3
  48. package/dist/color-input/root/color-input-root.d.ts +60 -2
  49. package/dist/color-input/root/color-input-root.js +55 -7
  50. package/dist/color-picker/channels.d.ts +26 -0
  51. package/dist/color-picker/channels.js +84 -0
  52. package/dist/color-picker/root/color-picker-root.d.ts +118 -12
  53. package/dist/color-picker/root/color-picker-root.js +183 -13
  54. package/dist/combobox/root/combobox-root.d.ts +126 -3
  55. package/dist/combobox/root/combobox-root.js +224 -21
  56. package/dist/command/root/command-root.d.ts +47 -10
  57. package/dist/command/root/command-root.js +45 -4
  58. package/dist/copy-button/from.d.ts +22 -0
  59. package/dist/copy-button/from.js +28 -0
  60. package/dist/copy-button/root/copy-button-root.d.ts +48 -7
  61. package/dist/copy-button/root/copy-button-root.js +54 -8
  62. package/dist/data-table/root/data-table-root.d.ts +164 -10
  63. package/dist/data-table/root/data-table-root.js +488 -57
  64. package/dist/date-input/root/date-input-root.d.ts +123 -9
  65. package/dist/date-input/root/date-input-root.js +147 -45
  66. package/dist/date-picker/root/date-picker-root.d.ts +229 -18
  67. package/dist/date-picker/root/date-picker-root.js +342 -46
  68. package/dist/dialog/description.d.ts +9 -0
  69. package/dist/dialog/description.js +14 -0
  70. package/dist/dialog/root/dialog-root.d.ts +101 -10
  71. package/dist/dialog/root/dialog-root.js +83 -4
  72. package/dist/donut-chart/root/donut-chart-root.d.ts +61 -16
  73. package/dist/donut-chart/root/donut-chart-root.js +48 -13
  74. package/dist/drawer/root/drawer-root.d.ts +93 -7
  75. package/dist/drawer/root/drawer-root.js +126 -5
  76. package/dist/drawer/swipe-start.d.ts +9 -0
  77. package/dist/drawer/swipe-start.js +12 -0
  78. package/dist/drilldown/item/drilldown-item.d.ts +16 -13
  79. package/dist/drilldown/item/drilldown-item.js +6 -2
  80. package/dist/drilldown/root/drilldown-root.d.ts +21 -5
  81. package/dist/drilldown/root/drilldown-root.js +26 -10
  82. package/dist/element-events.generated.js +42 -13
  83. package/dist/empty/root/empty-root.d.ts +1 -1
  84. package/dist/file-input/root/file-input-root.d.ts +65 -1
  85. package/dist/file-input/root/file-input-root.js +76 -12
  86. package/dist/file-input/screen.d.ts +35 -0
  87. package/dist/file-input/screen.js +49 -0
  88. package/dist/filter-input/menu/filter-menu.d.ts +9 -0
  89. package/dist/filter-input/menu/filter-menu.js +8 -0
  90. package/dist/filter-input/root/filter-input-root.d.ts +43 -6
  91. package/dist/filter-input/root/filter-input-root.js +89 -10
  92. package/dist/filter-input/suggest.d.ts +17 -0
  93. package/dist/filter-input/suggest.js +23 -0
  94. package/dist/format-date/options.d.ts +33 -0
  95. package/dist/format-date/options.js +42 -0
  96. package/dist/format-date/root/format-date-root.d.ts +62 -3
  97. package/dist/format-date/root/format-date-root.js +78 -37
  98. package/dist/graph/root/graph-root.d.ts +24 -3
  99. package/dist/graph/root/graph-root.js +66 -12
  100. package/dist/heatmap/root/buckets.d.ts +2 -1
  101. package/dist/heatmap/root/buckets.js +5 -1
  102. package/dist/heatmap/root/heatmap-root.d.ts +47 -9
  103. package/dist/heatmap/root/heatmap-root.js +46 -18
  104. package/dist/icon/index.d.ts +2 -0
  105. package/dist/icon/index.js +2 -0
  106. package/dist/icon/registry.d.ts +37 -0
  107. package/dist/icon/registry.js +83 -0
  108. package/dist/icon/root/icon-root.d.ts +53 -6
  109. package/dist/icon/root/icon-root.js +72 -4
  110. package/dist/icon/root/index.d.ts +2 -0
  111. package/dist/icon/root/index.js +2 -0
  112. package/dist/icon/set.d.ts +1 -0
  113. package/dist/icon/set.js +3 -0
  114. package/dist/icon-button/root/icon-button-root.d.ts +1 -1
  115. package/dist/image/root/image-root.d.ts +23 -2
  116. package/dist/image/root/image-root.js +23 -4
  117. package/dist/input/root/input-root.d.ts +62 -9
  118. package/dist/input/root/input-root.js +61 -5
  119. package/dist/kbd/root/kbd-root.d.ts +26 -11
  120. package/dist/kbd/root/kbd-root.js +35 -38
  121. package/dist/kbd/shortcut.d.ts +18 -0
  122. package/dist/kbd/shortcut.js +35 -0
  123. package/dist/legend/hidden-series.d.ts +8 -0
  124. package/dist/legend/hidden-series.js +13 -0
  125. package/dist/legend/root/legend-root.d.ts +63 -0
  126. package/dist/legend/root/legend-root.js +61 -7
  127. package/dist/lightbox/item/lightbox-item.d.ts +17 -1
  128. package/dist/lightbox/item/lightbox-item.js +10 -1
  129. package/dist/lightbox/root/lightbox-root.d.ts +77 -6
  130. package/dist/lightbox/root/lightbox-root.js +116 -5
  131. package/dist/line-chart/root/line-chart-root.d.ts +29 -4
  132. package/dist/line-chart/root/line-chart-root.js +58 -17
  133. package/dist/list/item/list-item.d.ts +11 -5
  134. package/dist/list/item/list-item.js +5 -6
  135. package/dist/listbox/range.d.ts +17 -0
  136. package/dist/listbox/range.js +5 -0
  137. package/dist/listbox/root/listbox-root.d.ts +184 -4
  138. package/dist/listbox/root/listbox-root.js +229 -23
  139. package/dist/loader/busy.d.ts +17 -0
  140. package/dist/loader/busy.js +24 -0
  141. package/dist/loader/root/loader-root.d.ts +37 -0
  142. package/dist/loader/root/loader-root.js +52 -3
  143. package/dist/map/root/map-root.d.ts +19 -4
  144. package/dist/map/root/map-root.js +36 -7
  145. package/dist/megamenu/root/megamenu-root.d.ts +10 -1
  146. package/dist/megamenu/root/megamenu-root.js +10 -1
  147. package/dist/menu/item/menu-item.d.ts +41 -12
  148. package/dist/menu/item/menu-item.js +46 -3
  149. package/dist/menu/root/menu-root.d.ts +32 -2
  150. package/dist/menu/root/menu-root.js +48 -8
  151. package/dist/meter/root/meter-root.d.ts +13 -0
  152. package/dist/meter/root/meter-root.js +24 -5
  153. package/dist/navigation/root/navigation-root.d.ts +10 -2
  154. package/dist/navigation/root/navigation-root.js +13 -2
  155. package/dist/notifications/feed-keys.d.ts +10 -0
  156. package/dist/notifications/feed-keys.js +14 -0
  157. package/dist/notifications/group/notification-group.d.ts +1 -0
  158. package/dist/notifications/notification/notification.d.ts +29 -0
  159. package/dist/notifications/notification/notification.js +51 -3
  160. package/dist/notifications/root/notifications-root.d.ts +49 -0
  161. package/dist/notifications/root/notifications-root.js +76 -2
  162. package/dist/number-input/root/number-input-root.d.ts +109 -7
  163. package/dist/number-input/root/number-input-root.js +109 -21
  164. package/dist/object-inspector/root/object-inspector-root.d.ts +49 -5
  165. package/dist/object-inspector/root/object-inspector-root.js +92 -12
  166. package/dist/option/root/option-root.d.ts +58 -6
  167. package/dist/option/root/option-root.js +35 -5
  168. package/dist/outline/root/outline-root.d.ts +13 -2
  169. package/dist/outline/root/outline-root.js +4 -2
  170. package/dist/pagination/root/pagination-root.d.ts +19 -1
  171. package/dist/pagination/root/pagination-root.js +49 -9
  172. package/dist/pagination/size/pagination-size.d.ts +13 -3
  173. package/dist/pagination/size/pagination-size.js +7 -0
  174. package/dist/pin-input/format.d.ts +7 -0
  175. package/dist/pin-input/format.js +16 -0
  176. package/dist/pin-input/root/pin-input-root.d.ts +37 -0
  177. package/dist/pin-input/root/pin-input-root.js +48 -7
  178. package/dist/popover/root/popover-root.d.ts +78 -3
  179. package/dist/popover/root/popover-root.js +74 -5
  180. package/dist/progress/root/progress-root.d.ts +23 -1
  181. package/dist/progress/root/progress-root.js +40 -10
  182. package/dist/qr-code/export.d.ts +31 -0
  183. package/dist/qr-code/export.js +10 -0
  184. package/dist/qr-code/root/qr-code-root.d.ts +45 -0
  185. package/dist/qr-code/root/qr-code-root.js +78 -3
  186. package/dist/radio/group/radio-group.d.ts +64 -0
  187. package/dist/radio/group/radio-group.js +66 -12
  188. package/dist/radio/root/radio-root.d.ts +8 -5
  189. package/dist/radio/root/radio-root.js +1 -2
  190. package/dist/rating/root/rating-root.d.ts +49 -1
  191. package/dist/rating/root/rating-root.js +69 -10
  192. package/dist/scatter-chart/root/scatter-chart-root.d.ts +17 -3
  193. package/dist/scatter-chart/root/scatter-chart-root.js +40 -14
  194. package/dist/select/root/select-root.d.ts +93 -3
  195. package/dist/select/root/select-root.js +111 -13
  196. package/dist/skeleton/root/skeleton-root.d.ts +39 -3
  197. package/dist/skeleton/root/skeleton-root.js +33 -4
  198. package/dist/skip-link/root/skip-link-root.d.ts +10 -0
  199. package/dist/skip-link/root/skip-link-root.js +15 -1
  200. package/dist/slider/root/slider-root.d.ts +59 -2
  201. package/dist/slider/root/slider-root.js +82 -8
  202. package/dist/sparkline/root/sparkline-root.d.ts +9 -10
  203. package/dist/speed-dial/item/speed-dial-item.d.ts +11 -5
  204. package/dist/speed-dial/item/speed-dial-item.js +7 -2
  205. package/dist/speed-dial/root/speed-dial-root.d.ts +15 -4
  206. package/dist/speed-dial/root/speed-dial-root.js +19 -5
  207. package/dist/splitter/root/splitter-root.d.ts +28 -6
  208. package/dist/splitter/root/splitter-root.js +35 -1
  209. package/dist/stat/root/stat-root.d.ts +9 -1
  210. package/dist/stat/root/stat-root.js +15 -2
  211. package/dist/steps/root/steps-root.js +1 -1
  212. package/dist/steps/step/step.d.ts +1 -0
  213. package/dist/steps/step/step.js +1 -1
  214. package/dist/switch/root/switch-root.d.ts +14 -2
  215. package/dist/switch/root/switch-root.js +7 -1
  216. package/dist/table/cell/table-cell.d.ts +24 -4
  217. package/dist/table/cell/table-cell.js +21 -1
  218. package/dist/table/column/table-column.d.ts +32 -6
  219. package/dist/table/column/table-column.js +36 -6
  220. package/dist/table/root/table-root.d.ts +94 -3
  221. package/dist/table/root/table-root.js +128 -5
  222. package/dist/table/row/table-row.d.ts +7 -0
  223. package/dist/table/row/table-row.js +9 -1
  224. package/dist/tabs/root/tabs-root.d.ts +6 -1
  225. package/dist/tabs/root/tabs-root.js +6 -2
  226. package/dist/tag/group/tag-group.d.ts +107 -0
  227. package/dist/tag/group/tag-group.js +157 -0
  228. package/dist/tag/index.d.ts +1 -0
  229. package/dist/tag/index.js +1 -0
  230. package/dist/tag/root/tag-root.d.ts +83 -1
  231. package/dist/tag/root/tag-root.js +87 -17
  232. package/dist/tag/selection.d.ts +10 -0
  233. package/dist/tag/selection.js +12 -0
  234. package/dist/textarea/grow.d.ts +22 -0
  235. package/dist/textarea/grow.js +20 -0
  236. package/dist/textarea/root/textarea-root.d.ts +13 -0
  237. package/dist/textarea/root/textarea-root.js +10 -2
  238. package/dist/time-input/root/time-input-root.d.ts +71 -6
  239. package/dist/time-input/root/time-input-root.js +110 -44
  240. package/dist/time-picker/root/time-picker-root.d.ts +155 -26
  241. package/dist/time-picker/root/time-picker-root.js +192 -63
  242. package/dist/toast/hotkey.d.ts +22 -0
  243. package/dist/toast/hotkey.js +23 -0
  244. package/dist/toast/item/toast-item.d.ts +16 -1
  245. package/dist/toast/item/toast-item.js +41 -13
  246. package/dist/toast/root/toast-root.d.ts +105 -8
  247. package/dist/toast/root/toast-root.js +209 -4
  248. package/dist/toggle/group/toggle-group.d.ts +95 -16
  249. package/dist/toggle/group/toggle-group.js +99 -13
  250. package/dist/toggle/root/toggle-root.d.ts +26 -11
  251. package/dist/toggle/root/toggle-root.js +14 -6
  252. package/dist/toolbar/root/toolbar-root.d.ts +55 -15
  253. package/dist/toolbar/root/toolbar-root.js +99 -16
  254. package/dist/tooltip/focus-target.d.ts +6 -0
  255. package/dist/tooltip/focus-target.js +9 -0
  256. package/dist/tooltip/root/tooltip-root.d.ts +73 -15
  257. package/dist/tooltip/root/tooltip-root.js +96 -7
  258. package/dist/tour/naming.d.ts +30 -0
  259. package/dist/tour/naming.js +10 -0
  260. package/dist/tour/root/tour-root.d.ts +67 -3
  261. package/dist/tour/root/tour-root.js +105 -17
  262. package/dist/tour/stop/tour-stop.d.ts +65 -3
  263. package/dist/tour/stop/tour-stop.js +61 -11
  264. package/dist/tour/wait.d.ts +11 -0
  265. package/dist/tour/wait.js +19 -0
  266. package/dist/tree/item/tree-item.d.ts +21 -5
  267. package/dist/tree/item/tree-item.js +14 -4
  268. package/dist/tree/root/tree-root.d.ts +29 -5
  269. package/dist/tree/root/tree-root.js +64 -8
  270. package/dist/trend/root/trend-root.d.ts +36 -1
  271. package/dist/trend/root/trend-root.js +41 -15
  272. package/dist/video/root/keys.d.ts +5 -0
  273. package/dist/video/root/keys.js +13 -1
  274. package/dist/video/root/video-root.d.ts +19 -0
  275. package/dist/video/root/video-root.js +48 -4
  276. package/dist/video-playlist/menu/video-playlist-menu.d.ts +20 -2
  277. package/dist/video-playlist/menu/video-playlist-menu.js +27 -5
  278. package/dist/video-playlist/root/video-playlist-root.js +6 -3
  279. package/dist/virtual-list/root/virtual-list-root.d.ts +52 -4
  280. package/dist/virtual-list/root/virtual-list-root.js +103 -21
  281. package/lint.json +1 -1
  282. package/package.json +5 -5
  283. package/types/jsx.d.ts +9140 -1149
  284. package/types/svelte.d.ts +1 -0
  285. package/types/vue.d.ts +2089 -267
@@ -4,14 +4,17 @@ 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, customElement, DisabledCascadeController, HelixFilterEvent, HelixFormElement, hostAria, MixinCollection, MixinControllerValue, MixinField, reportValues, requestChange, scrollItemIntoView, } from '@fusion.dev/helix-core';
7
+ import { AriaForwardController, countLabel, customElement, DelayController, DisabledCascadeController, HelixFilterEvent, HelixFormElement, hostAria, MixinCollection, MixinControllerValue, MixinField, reportValues, requestChange, scrollItemIntoView, Typeahead, typesCharacter, } from '@fusion.dev/helix-core';
8
8
  import { magnifyingGlass } from '@fusion.dev/helix-icons';
9
9
  import { html, nothing } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
11
11
  import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
12
12
  import { HelixHeadlessOptionGroup } from '../../option/group/option-group.js';
13
13
  import { HelixHeadlessOption } from '../../option/root/option-root.js';
14
- const MOVES = new Set(['ArrowDown', 'ArrowUp', 'End', 'Home']);
14
+ import { rangeAdd } from '../range.js';
15
+ const MOVES = new Set(['ArrowDown', 'ArrowUp', 'End', 'Home', 'PageDown', 'PageUp']);
16
+ const FIELD_MOVES = new Set(['ArrowDown', 'ArrowUp', 'PageDown', 'PageUp']);
17
+ const PAGE = 10;
15
18
  const matched = new Map();
16
19
  const HIGHLIGHT = 'helix-filter-match';
17
20
  const paintable = () => typeof CSS !== 'undefined' && 'highlights' in CSS && typeof Highlight !== 'undefined';
@@ -66,9 +69,16 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
66
69
  this.emptyText = 'No matches';
67
70
  this.filterState = undefined;
68
71
  this.filterable = false;
72
+ this.filterDelay = 0;
69
73
  this.filterLabel = 'Filter';
74
+ this.manual = false;
70
75
  this.multiple = false;
76
+ this.resultLabel = '{count} result';
77
+ this.resultsLabel = '{count} results';
71
78
  this.activeValue = '';
79
+ this.parked = false;
80
+ this.typeahead = new Typeahead();
81
+ this.seen = new Set();
72
82
  this.forwarded = false;
73
83
  this.hasMatches = true;
74
84
  this.counted = false;
@@ -76,6 +86,9 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
76
86
  items: () => this.options,
77
87
  });
78
88
  this.handleSelection = (event) => {
89
+ if (event.detail.href !== undefined) {
90
+ return;
91
+ }
79
92
  event.stopPropagation();
80
93
  if (event.defaultPrevented) {
81
94
  return;
@@ -88,57 +101,114 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
88
101
  if (shown.length === 0) {
89
102
  return;
90
103
  }
91
- if (event.key === ' ' || event.key === 'Enter') {
92
- event.preventDefault();
93
- if (this.activeValue !== '') {
104
+ const typing = this.forwarded || event.composedPath()[0] === this.partNamed('search-field');
105
+ if (event.key === 'Enter' || (event.key === ' ' && !typing && !this.typeahead.typing())) {
106
+ const link = shown.find((option) => option.value === this.activeValue && Boolean(option.href));
107
+ if (link !== undefined) {
108
+ event.preventDefault();
109
+ if (event.key === 'Enter') {
110
+ link.select(event);
111
+ }
112
+ }
113
+ else if (this.activeValue !== '') {
114
+ event.preventDefault();
94
115
  this.choose(this.activeValue);
95
116
  }
117
+ else if (event.key === ' ') {
118
+ event.preventDefault();
119
+ }
96
120
  return;
97
121
  }
98
- if (!MOVES.has(event.key) && event.key !== 'Enter') {
122
+ if (this.multiple &&
123
+ !typing &&
124
+ event.key.toLowerCase() === 'a' &&
125
+ (event.ctrlKey || event.metaKey) &&
126
+ !event.altKey) {
127
+ event.preventDefault();
128
+ const reachable = shown.map((option) => option.value);
129
+ const all = reachable.every((value) => this.values.includes(value));
130
+ const kept = this.values.filter((value) => !reachable.includes(value));
131
+ this.requestValue((all ? kept : [...kept, ...reachable]).join(','));
132
+ return;
133
+ }
134
+ if (!typing && typesCharacter(event)) {
135
+ event.preventDefault();
136
+ const value = this.typeaheadTo(event.key);
137
+ if (value !== undefined) {
138
+ this.arrive(value);
139
+ }
140
+ return;
141
+ }
142
+ if (!(typing ? FIELD_MOVES : MOVES).has(event.key)) {
99
143
  return;
100
144
  }
101
145
  event.preventDefault();
146
+ this.typeahead.reset();
102
147
  const from = this.currentValue;
103
148
  const at = shown.findIndex((option) => option.value === from);
149
+ const last = shown.length - 1;
104
150
  let next = at;
105
151
  if (event.key === 'ArrowDown') {
106
152
  next = at < 0 ? 0 : (at + 1) % shown.length;
107
153
  }
108
154
  else if (event.key === 'ArrowUp') {
109
- next = at < 0 ? shown.length - 1 : (at - 1 + shown.length) % shown.length;
155
+ next = at < 0 ? last : (at - 1 + shown.length) % shown.length;
110
156
  }
111
157
  else if (event.key === 'Home') {
112
158
  next = 0;
113
159
  }
160
+ else if (event.key === 'End') {
161
+ next = last;
162
+ }
163
+ else if (event.key === 'PageDown') {
164
+ next = at < 0 ? 0 : Math.min(at + this.pageSize(), last);
165
+ }
114
166
  else {
115
- next = shown.length - 1;
167
+ next = at < 0 ? last : Math.max(at - this.pageSize(), 0);
116
168
  }
117
169
  const target = shown[next];
118
- if (!target) {
119
- return;
120
- }
121
- this.activeValue = target.value;
122
- if (!this.multiple && !this.forwarded) {
123
- this.choose(target.value);
170
+ if (target) {
171
+ this.arrive(target.value);
172
+ const ranged = event.key === 'Home' || event.key === 'End';
173
+ if (this.multiple && !typing && event.shiftKey && ranged) {
174
+ const taken = rangeAdd(this.values, shown.map((option) => option.value), at, next);
175
+ if (taken) {
176
+ this.requestValue(taken.join(','));
177
+ }
178
+ return;
179
+ }
180
+ if (this.multiple && !typing && event.shiftKey && !this.values.includes(target.value)) {
181
+ this.choose(target.value);
182
+ }
124
183
  }
125
184
  };
126
185
  this.handleFilter = (event) => {
127
186
  event.stopPropagation();
128
187
  const control = event.target;
129
- this.requestFilter(control.value);
130
- if (control.value !== this.filter) {
131
- control.value = this.filter;
188
+ this.filtering.cancel();
189
+ if (this.filterDelay > 0) {
190
+ this.filtering.start(this.filterDelay, () => this.takeFilter(control));
191
+ return;
132
192
  }
193
+ this.takeFilter(control);
133
194
  };
195
+ this.filtering = new DelayController(this, { renders: false });
134
196
  this.handleFilterKeydown = (event) => {
135
- if (event.key === 'Escape' && this.filter !== '') {
197
+ if (event.key === 'Enter' || FIELD_MOVES.has(event.key)) {
198
+ this.flushFilter();
199
+ }
200
+ if (event.key === 'Escape' && (this.filter !== '' || this.filtering.pending)) {
136
201
  event.stopPropagation();
137
202
  event.preventDefault();
203
+ this.filtering.cancel();
138
204
  this.requestFilter('');
205
+ const control = this.partNamed('search-field');
206
+ if (control && control.value !== this.filter) {
207
+ control.value = this.filter;
208
+ }
139
209
  return;
140
210
  }
141
- if (!MOVES.has(event.key)) {
211
+ if (!FIELD_MOVES.has(event.key)) {
142
212
  return;
143
213
  }
144
214
  event.stopPropagation();
@@ -160,6 +230,53 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
160
230
  const option = this.options[at];
161
231
  this.value = option?.value ?? '';
162
232
  }
233
+ highlight(where) {
234
+ const shown = this.shown.filter((option) => !option.disabled);
235
+ this.typeahead.reset();
236
+ this.parked = where === 'none';
237
+ if (where === 'none') {
238
+ this.activeValue = '';
239
+ }
240
+ else if (where === 'first') {
241
+ this.activeValue = shown[0]?.value ?? '';
242
+ }
243
+ else if (where === 'last') {
244
+ this.activeValue = shown.at(-1)?.value ?? '';
245
+ }
246
+ else {
247
+ const chosen = shown.find((option) => this.values.includes(option.value));
248
+ this.activeValue = (chosen ?? shown[0])?.value ?? '';
249
+ }
250
+ if (this.childrenKnown) {
251
+ this.place();
252
+ }
253
+ }
254
+ tell(changed) {
255
+ if (!changed.has('filter') || !this.hasUpdated) {
256
+ return;
257
+ }
258
+ const count = this.shown.length;
259
+ if (this.filter.trim() === '' || count === 0) {
260
+ this.announced = '';
261
+ return;
262
+ }
263
+ this.announced = countLabel(count, this.resultLabel, this.resultsLabel, this.lang);
264
+ }
265
+ typeaheadTo(key) {
266
+ const shown = this.shown.filter((option) => !option.disabled);
267
+ const from = shown.findIndex((option) => option.value === this.currentValue);
268
+ const at = this.typeahead.match(key, shown.map((option) => option.text), from);
269
+ const target = shown[at];
270
+ if (!target) {
271
+ return undefined;
272
+ }
273
+ this.parked = false;
274
+ this.activeValue = target.value;
275
+ return target.value;
276
+ }
277
+ get typing() {
278
+ return this.typeahead.typing();
279
+ }
163
280
  get type() {
164
281
  return this.multiple ? 'select-multiple' : 'select-one';
165
282
  }
@@ -218,6 +335,20 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
218
335
  }
219
336
  this.requestValue(joined);
220
337
  }
338
+ arrive(value) {
339
+ this.parked = false;
340
+ this.activeValue = value;
341
+ const link = this.options.some((option) => option.value === value && Boolean(option.href));
342
+ if (!this.multiple && !this.manual && !this.forwarded && !link) {
343
+ this.choose(value);
344
+ }
345
+ }
346
+ pageSize() {
347
+ const items = this.partNamed('items');
348
+ const row = this.shown[0]?.getBoundingClientRect().height ?? 0;
349
+ const height = items?.clientHeight ?? 0;
350
+ return row > 0 && height > 0 ? Math.max(1, Math.floor(height / row) - 1) : PAGE;
351
+ }
221
352
  navigate(event) {
222
353
  this.forwarded = true;
223
354
  try {
@@ -247,6 +378,7 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
247
378
  }
248
379
  }
249
380
  place() {
381
+ this.forgetRemoved();
250
382
  const needle = this.filter.trim().toLowerCase();
251
383
  const chosen = new Set(this.values);
252
384
  for (const option of this.options) {
@@ -258,7 +390,7 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
258
390
  group.hidden = inside.length > 0 && inside.every((option) => option.hidden);
259
391
  }
260
392
  const shown = this.shown;
261
- const rescue = needle !== '' || this.activeValue !== '';
393
+ const rescue = !this.parked && (needle !== '' || this.activeValue !== '');
262
394
  if (rescue && !shown.some((option) => option.value === this.activeValue)) {
263
395
  this.activeValue = shown.find((option) => !option.disabled)?.value ?? '';
264
396
  }
@@ -278,6 +410,21 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
278
410
  }
279
411
  this.counted = true;
280
412
  }
413
+ get owned() {
414
+ const root = this.getRootNode();
415
+ return root instanceof ShadowRoot && root.host.localName.startsWith('helix-');
416
+ }
417
+ forgetRemoved() {
418
+ if (this.owned) {
419
+ return;
420
+ }
421
+ const present = new Set(this.options.map((option) => option.value));
422
+ const gone = this.values.filter((value) => this.seen.has(value) && !present.has(value));
423
+ this.seen = present;
424
+ if (gone.length > 0) {
425
+ this.value = this.values.filter((value) => !gone.includes(value)).join(',');
426
+ }
427
+ }
281
428
  followActive() {
282
429
  const active = this.shown.find((option) => option.value === this.activeValue);
283
430
  if (active) {
@@ -309,8 +456,32 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
309
456
  willUpdate(changed) {
310
457
  super.willUpdate(changed);
311
458
  hostAria(this.internals, { role: '' });
459
+ if (changed.has('value') &&
460
+ !this.multiple &&
461
+ this.activeValue !== '' &&
462
+ !this.values.includes(this.activeValue)) {
463
+ this.activeValue = '';
464
+ this.typeahead.reset();
465
+ }
466
+ if (this.childrenKnown) {
467
+ this.seedDeclared();
468
+ }
312
469
  if (this.childrenKnown) {
313
470
  this.place();
471
+ this.tell(changed);
472
+ }
473
+ this.toggleState('empty', !this.showsOptions);
474
+ this.toggleState('loading', this.loading);
475
+ }
476
+ seedDeclared() {
477
+ if (this.owned || this.defaultValue !== '' || this.hasAttribute('default-value')) {
478
+ return;
479
+ }
480
+ const declared = this.options
481
+ .filter((option) => option.defaultSelected)
482
+ .map((option) => option.value);
483
+ if (declared.length > 0) {
484
+ this.defaultValue = this.multiple ? declared.join(',') : (declared.at(-1) ?? '');
314
485
  }
315
486
  }
316
487
  get currentValue() {
@@ -330,6 +501,16 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
330
501
  this.sync();
331
502
  this.linkActive();
332
503
  }
504
+ takeFilter(control) {
505
+ this.filtering.cancel();
506
+ this.requestFilter(control.value);
507
+ if (control.value !== this.filter) {
508
+ control.value = this.filter;
509
+ }
510
+ }
511
+ flushFilter() {
512
+ this.filtering.flush();
513
+ }
333
514
  requestFilter(next) {
334
515
  if (next === this.filter) {
335
516
  return true;
@@ -338,6 +519,7 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
338
519
  apply: (value) => {
339
520
  const previous = this.filter;
340
521
  this.filterState = value;
522
+ this.parked = false;
341
523
  this.requestUpdate('filter', previous);
342
524
  },
343
525
  event: (value) => new HelixFilterEvent({ value }),
@@ -356,12 +538,23 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
356
538
  </svg>
357
539
  </slot>`;
358
540
  }
359
- render() {
360
- const shown = this.counted && !this.loading
541
+ get showsOptions() {
542
+ return this.counted && !this.loading
361
543
  ? this.filter === ''
362
544
  ? this.options.length > 0
363
545
  : this.hasMatches
364
546
  : true;
547
+ }
548
+ renderStatus(hasContent) {
549
+ if (!this.loading && hasContent) {
550
+ return nothing;
551
+ }
552
+ return this.loading
553
+ ? html `<p part="loading"><slot name="loading">${this.loadingText}</slot></p>`
554
+ : html `<p part="empty"><slot name="empty">${this.emptyText}</slot></p>`;
555
+ }
556
+ render() {
557
+ const shown = this.showsOptions;
365
558
  return html `${this.renderGroupLabel()}
366
559
  ${this.filterable
367
560
  ? html `<div part="search">
@@ -376,6 +569,7 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
376
569
  .value=${this.filter}
377
570
  placeholder=${this.filterLabel}
378
571
  aria-label=${this.filterLabel}
572
+ aria-autocomplete="list"
379
573
  aria-controls="items"
380
574
  ?disabled=${this.isDisabled}
381
575
  @input=${this.handleFilter}
@@ -394,7 +588,7 @@ let HelixHeadlessListbox = class HelixHeadlessListbox extends MixinCollection(Mi
394
588
  aria-multiselectable=${this.multiple ? 'true' : nothing}
395
589
  aria-required=${this.required ? 'true' : nothing}
396
590
  ${''}
397
- tabindex=${this.isDisabled ? -1 : 0}
591
+ tabindex=${this.filterable ? nothing : this.isDisabled ? -1 : 0}
398
592
  >
399
593
  <slot @slotchange=${() => this.place()}></slot>
400
594
  ${this.renderStatus(shown)}
@@ -417,12 +611,24 @@ __decorate([
417
611
  __decorate([
418
612
  property({ attribute: 'filterable', reflect: true, type: Boolean })
419
613
  ], HelixHeadlessListbox.prototype, "filterable", void 0);
614
+ __decorate([
615
+ property({ attribute: 'filter-delay', type: Number })
616
+ ], HelixHeadlessListbox.prototype, "filterDelay", void 0);
420
617
  __decorate([
421
618
  property({ attribute: 'filter-label' })
422
619
  ], HelixHeadlessListbox.prototype, "filterLabel", void 0);
620
+ __decorate([
621
+ property({ type: Boolean })
622
+ ], HelixHeadlessListbox.prototype, "manual", void 0);
423
623
  __decorate([
424
624
  property({ reflect: true, type: Boolean })
425
625
  ], HelixHeadlessListbox.prototype, "multiple", void 0);
626
+ __decorate([
627
+ property({ attribute: 'result-label' })
628
+ ], HelixHeadlessListbox.prototype, "resultLabel", void 0);
629
+ __decorate([
630
+ property({ attribute: 'results-label' })
631
+ ], HelixHeadlessListbox.prototype, "resultsLabel", void 0);
426
632
  __decorate([
427
633
  state()
428
634
  ], HelixHeadlessListbox.prototype, "activeValue", void 0);
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Marks a region busy for as long as something is loading into it, and
3
+ * counts, so two loaders in one region do not clear each other.
4
+ *
5
+ * Beside the family's `index.ts`, because which element is busy and how many
6
+ * claims it holds is bookkeeping about elements, not about a loader's
7
+ * template.
8
+ */
9
+ /**
10
+ * Adds a claim on the region, setting `aria-busy="true"` on the first.
11
+ *
12
+ * Only an `aria-busy` this file wrote is ever taken off again: a region the
13
+ * page marked busy itself keeps its own attribute.
14
+ */
15
+ export declare const claimBusy: (region: Element) => void;
16
+ /** Takes a claim back, and the attribute with the last one. */
17
+ export declare const releaseBusy: (region: Element) => void;
@@ -0,0 +1,24 @@
1
+ const CLAIMS = new WeakMap();
2
+ export const claimBusy = (region) => {
3
+ const held = CLAIMS.get(region) ?? 0;
4
+ if (held === 0 && region.getAttribute('aria-busy') === 'true') {
5
+ return;
6
+ }
7
+ CLAIMS.set(region, held + 1);
8
+ if (held === 0) {
9
+ region.setAttribute('aria-busy', 'true');
10
+ }
11
+ };
12
+ export const releaseBusy = (region) => {
13
+ const held = CLAIMS.get(region);
14
+ if (held === undefined) {
15
+ return;
16
+ }
17
+ if (held <= 1) {
18
+ CLAIMS.delete(region);
19
+ region.removeAttribute('aria-busy');
20
+ }
21
+ else {
22
+ CLAIMS.set(region, held - 1);
23
+ }
24
+ };
@@ -19,6 +19,15 @@ import { type PropertyValues, type TemplateResult } from 'lit';
19
19
  * a `role` attribute on the host would be missing from React server-rendered
20
20
  * markup.
21
21
  *
22
+ * **`for` names the region being loaded**, and the loader sets
23
+ * `aria-busy="true"` on it for as long as the loader is on the page, so a
24
+ * screen reader waits for the region rather than reading it half filled. Two
25
+ * loaders in one region hold it busy until the last one leaves.
26
+ *
27
+ * **`delay` keeps a quick load from flashing a loader.** The loader stays
28
+ * hidden, and silent, for that many milliseconds, and appears only if the work
29
+ * is still going.
30
+ *
22
31
  * @since 0.1.0
23
32
  * @status stable
24
33
  * @category feedback
@@ -33,11 +42,39 @@ import { type PropertyValues, type TemplateResult } from 'lit';
33
42
  * @csspart track - The full circle behind the indicator.
34
43
  */
35
44
  export declare class HelixHeadlessLoader extends HelixElement {
45
+ /**
46
+ * How long the loader waits before it shows, in milliseconds, so work that
47
+ * finishes quickly never flashes one.
48
+ *
49
+ * Absent shows it at once. Hidden in the served markup too, so a page that
50
+ * loads quickly never draws it.
51
+ */
52
+ delay: number;
53
+ /**
54
+ * The id of the region being loaded, which is marked `aria-busy` while the
55
+ * loader is on the page.
56
+ *
57
+ * Names an element in the same tree. In React it arrives as a property, the
58
+ * way `Tooltip.for` does.
59
+ */
60
+ for: string;
36
61
  /**
37
62
  * The loader's name, announced by screen readers while the work is in
38
63
  * progress.
39
64
  */
40
65
  label: string;
66
+ /** The wait `delay` asks for, and whether it has run out. */
67
+ private readonly due;
68
+ /** The region this loader marked busy, so it is the one released. */
69
+ private region;
70
+ /** Whether the loader is still waiting out its `delay`. */
71
+ private get waiting();
72
+ connectedCallback(): void;
73
+ disconnectedCallback(): void;
74
+ /** Marks the region `for` names busy, releasing one it named before. */
75
+ private mark;
76
+ private release;
77
+ protected updated(changed: PropertyValues<this>): void;
41
78
  protected willUpdate(changed: PropertyValues): void;
42
79
  protected render(): TemplateResult;
43
80
  }
@@ -4,27 +4,76 @@ 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, hostAria } from '@fusion.dev/helix-core';
7
+ import { customElement, DelayController, elementById, HelixElement, hostAria, } from '@fusion.dev/helix-core';
8
8
  import { html } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
+ import { claimBusy, releaseBusy } from '../busy.js';
10
11
  let HelixHeadlessLoader = class HelixHeadlessLoader extends HelixElement {
11
12
  constructor() {
12
13
  super(...arguments);
14
+ this.delay = 0;
15
+ this.for = '';
13
16
  this.label = 'Loading';
17
+ this.due = new DelayController(this);
18
+ this.region = null;
19
+ }
20
+ get waiting() {
21
+ return this.delay > 0 && !this.due.elapsed;
22
+ }
23
+ connectedCallback() {
24
+ super.connectedCallback();
25
+ if (this.delay > 0 && !this.due.elapsed) {
26
+ this.due.start(this.delay);
27
+ }
28
+ requestAnimationFrame(() => this.mark());
29
+ }
30
+ disconnectedCallback() {
31
+ super.disconnectedCallback();
32
+ this.release();
33
+ }
34
+ mark() {
35
+ const region = this.isConnected && this.for !== '' ? elementById(this, this.for) : null;
36
+ if (region === this.region) {
37
+ return;
38
+ }
39
+ this.release();
40
+ if (region) {
41
+ claimBusy(region);
42
+ this.region = region;
43
+ }
44
+ }
45
+ release() {
46
+ if (this.region) {
47
+ releaseBusy(this.region);
48
+ this.region = null;
49
+ }
50
+ }
51
+ updated(changed) {
52
+ super.updated(changed);
53
+ if (changed.has('for') && changed.get('for') !== undefined) {
54
+ this.mark();
55
+ }
14
56
  }
15
57
  willUpdate(changed) {
16
58
  super.willUpdate(changed);
17
59
  hostAria(this.internals, { label: this.label, role: 'progressbar' });
18
60
  this.internals.ariaBusy = 'true';
61
+ this.internals.ariaHidden = this.waiting ? 'true' : null;
19
62
  }
20
63
  render() {
21
- return html `<svg part="svg" viewBox="0 0 24 24" aria-hidden="true">
64
+ return html `<svg part="svg" viewBox="0 0 24 24" aria-hidden="true" ?hidden=${this.waiting}>
22
65
  <circle part="track" cx="12" cy="12" r="10" fill="none" />
23
66
  <circle part="indicator" cx="12" cy="12" r="10" fill="none" />
24
67
  </svg>
25
- <span part="mark" aria-hidden="true"><slot></slot></span>`;
68
+ <span part="mark" aria-hidden="true" ?hidden=${this.waiting}><slot></slot></span>`;
26
69
  }
27
70
  };
71
+ __decorate([
72
+ property({ type: Number })
73
+ ], HelixHeadlessLoader.prototype, "delay", void 0);
74
+ __decorate([
75
+ property()
76
+ ], HelixHeadlessLoader.prototype, "for", void 0);
28
77
  __decorate([
29
78
  property()
30
79
  ], HelixHeadlessLoader.prototype, "label", void 0);
@@ -43,14 +43,17 @@ declare const HelixHeadlessMap_base: (new (...args: any[]) => import("@fusion.de
43
43
  *
44
44
  * Add `layers` to `controls` for a layers button. It opens a menu with a tile
45
45
  * for each layer, which sets the layer's `hidden` attribute. Layers with the
46
- * same `group`, such as a route and its dotted legs, share one tile.
46
+ * same `group`, such as a route and its dotted legs, share one tile. A tile
47
+ * sends `helix-before-toggle` from the layer first, so you can store the
48
+ * choice or refuse it.
47
49
  *
48
50
  * Imagery, a `helix-map-raster` with `ground`, is a map type and not a tile.
49
51
  * The menu opens with a card for each type, Standard, Hybrid, and Satellite,
50
52
  * with a picture of the map in it, and `map-type` says which is chosen.
51
53
  * Standard hides the imagery, Hybrid draws it under the roads and names, and
52
- * Satellite draws it alone. `connectMap` does not draw a hidden layer, so a layer you write with
53
- * `hidden` starts turned off.
54
+ * Satellite draws it alone. Choosing one sends `helix-before-change` with its
55
+ * name, and `change` once `map-type` holds it. `connectMap` does not draw a
56
+ * hidden layer, so a layer you write with `hidden` starts turned off.
54
57
  *
55
58
  * Markers, routes, areas, live positions, and points layers go inside the
56
59
  * frame as child elements.
@@ -66,7 +69,10 @@ declare const HelixHeadlessMap_base: (new (...args: any[]) => import("@fusion.de
66
69
  * @slot footer - Content below the map, such as a legend or a scale.
67
70
  * @slot header - Content above the map, such as a title or a search field.
68
71
  *
72
+ * @event {Event} change - The layers menu changed the map type, which `map-type` now holds.
69
73
  * @event {HelixActivateEvent} helix-activate - A marker was pressed. The value is the marker's `name`, or its `label` if it has no name. A cluster sends `helix-map-cluster` instead.
74
+ * @event {HelixBeforeChangeEvent} helix-before-change - A map type was chosen in the layers menu, with its name: `standard`, `hybrid`, or `satellite`. Cancelable: cancel it to keep the current type.
75
+ * @event {HelixBeforeToggleEvent} helix-before-toggle - A layer's tile in the layers menu was pressed. Sent from the layer, with `checked` true when it is to be shown. Cancelable: cancel it to keep the layer as it is.
70
76
  * @event {HelixMapClusterEvent} helix-map-cluster - A cluster was pressed. Sent from the marker that shows the cluster, with the names of its places.
71
77
  * @event {HelixMapPanEvent} helix-map-pan - A pan button or arrow key was pressed. Move your map in response.
72
78
  * @event {HelixMapPartChangeEvent} helix-map-part-change - Clustering was turned on or off. Used by `connectMap`; pages do not need to listen for it.
@@ -279,8 +285,17 @@ export declare class HelixHeadlessMap extends HelixHeadlessMap_base {
279
285
  private shown;
280
286
  /** A map type's name on its card. */
281
287
  private nameOf;
282
- /** Shows or hides layers, which `connectMap` draws or takes off the map. */
288
+ /**
289
+ * Shows or hides layers, which `connectMap` draws or takes off the map.
290
+ *
291
+ * **Asked first, from the layer.** The tile wrote `hidden` onto the
292
+ * consumer's element and said nothing, so a page could neither keep the
293
+ * choice nor keep its own state in step. The request comes from the first
294
+ * layer of the tile, which is the element the page wrote.
295
+ */
283
296
  private showLayers;
297
+ /** Asks for a map type, and sends `change` once it holds. */
298
+ private chooseMapType;
284
299
  /**
285
300
  * The layers menu's entries, in the order the layers are written: a layer
286
301
  * of its own, or every layer in a group, at the place of the first.