@fusion.dev/helix-headless 0.145.0 → 0.146.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (278) hide show
  1. package/custom-elements.json +7373 -3849
  2. package/dist/accordion/item/accordion-item.d.ts +14 -0
  3. package/dist/accordion/item/accordion-item.js +16 -6
  4. package/dist/accordion/root/accordion-root.d.ts +8 -5
  5. package/dist/announcement/root/announcement-root.d.ts +3 -2
  6. package/dist/announcement/root/announcement-root.js +3 -1
  7. package/dist/app-header/root/app-header-root.d.ts +7 -2
  8. package/dist/app-header/root/app-header-root.js +9 -1
  9. package/dist/app-header/sectioning.d.ts +3 -1
  10. package/dist/app-header/sectioning.js +3 -2
  11. package/dist/avatar/group/avatar-group.d.ts +20 -14
  12. package/dist/avatar/group/avatar-group.js +17 -19
  13. package/dist/badge/root/badge-root.d.ts +3 -1
  14. package/dist/badge/root/badge-root.js +2 -2
  15. package/dist/bar-chart/root/bar-chart-root.d.ts +18 -1
  16. package/dist/bar-chart/root/bar-chart-root.js +52 -15
  17. package/dist/bar-chart/stack.d.ts +15 -0
  18. package/dist/bar-chart/stack.js +19 -0
  19. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +25 -0
  20. package/dist/breadcrumbs/root/breadcrumbs-root.js +50 -3
  21. package/dist/button/root/button-root.d.ts +26 -0
  22. package/dist/button/root/button-root.js +38 -18
  23. package/dist/button-group/root/button-group-root.d.ts +2 -1
  24. package/dist/button-group/root/button-group-root.js +5 -1
  25. package/dist/canvas/node/canvas-node.d.ts +4 -3
  26. package/dist/canvas/node/canvas-node.js +3 -2
  27. package/dist/canvas/root/canvas-root.js +2 -2
  28. package/dist/card/root/card-root.d.ts +24 -3
  29. package/dist/card/root/card-root.js +20 -8
  30. package/dist/carousel/root/carousel-root.d.ts +43 -4
  31. package/dist/carousel/root/carousel-root.js +40 -12
  32. package/dist/carousel/stops.d.ts +20 -0
  33. package/dist/carousel/stops.js +30 -0
  34. package/dist/chat/message/chat-message.js +5 -4
  35. package/dist/chat/naming.d.ts +5 -1
  36. package/dist/chat/naming.js +4 -2
  37. package/dist/chat/root/chat-root.d.ts +25 -3
  38. package/dist/chat/root/chat-root.js +22 -5
  39. package/dist/checkbox/root/checkbox-root.d.ts +20 -0
  40. package/dist/checkbox/root/checkbox-root.js +14 -1
  41. package/dist/checkbox-group/root/checkbox-group-root.d.ts +8 -2
  42. package/dist/checkbox-group/root/checkbox-group-root.js +12 -10
  43. package/dist/checkbox-group/select-all.d.ts +27 -0
  44. package/dist/checkbox-group/select-all.js +12 -0
  45. package/dist/code-block/highlight.d.ts +15 -0
  46. package/dist/code-block/highlight.js +4 -0
  47. package/dist/code-block/root/code-block-root.d.ts +49 -1
  48. package/dist/code-block/root/code-block-root.js +58 -3
  49. package/dist/color-input/root/color-input-root.d.ts +3 -0
  50. package/dist/color-input/root/color-input-root.js +6 -3
  51. package/dist/color-picker/root/color-picker-root.d.ts +33 -4
  52. package/dist/color-picker/root/color-picker-root.js +80 -21
  53. package/dist/color-picker/swatches.d.ts +12 -0
  54. package/dist/color-picker/swatches.js +26 -0
  55. package/dist/combobox/root/combobox-root.d.ts +43 -0
  56. package/dist/combobox/root/combobox-root.js +79 -15
  57. package/dist/command/root/command-root.d.ts +16 -1
  58. package/dist/command/root/command-root.js +22 -4
  59. package/dist/copy-button/root/copy-button-root.d.ts +5 -0
  60. package/dist/copy-button/root/copy-button-root.js +10 -3
  61. package/dist/data-table/index.d.ts +1 -1
  62. package/dist/data-table/index.js +1 -1
  63. package/dist/data-table/keys.d.ts +24 -0
  64. package/dist/data-table/keys.js +24 -0
  65. package/dist/data-table/root/data-table-root.d.ts +27 -18
  66. package/dist/data-table/root/data-table-root.js +20 -52
  67. package/dist/data-table/tree.d.ts +16 -1
  68. package/dist/data-table/tree.js +2 -3
  69. package/dist/date-input/root/date-input-root.d.ts +3 -1
  70. package/dist/date-input/root/date-input-root.js +18 -7
  71. package/dist/date-picker/root/date-picker-root.js +12 -6
  72. package/dist/dialog/description.js +15 -1
  73. package/dist/dialog/root/dialog-root.d.ts +8 -2
  74. package/dist/dialog/root/dialog-root.js +16 -1
  75. package/dist/divider/root/divider-root.d.ts +3 -0
  76. package/dist/divider/root/divider-root.js +2 -1
  77. package/dist/donut-chart/root/donut-chart-root.d.ts +31 -0
  78. package/dist/donut-chart/root/donut-chart-root.js +48 -1
  79. package/dist/drawer/reveal.d.ts +8 -0
  80. package/dist/drawer/reveal.js +1 -0
  81. package/dist/drawer/root/drawer-root.d.ts +59 -0
  82. package/dist/drawer/root/drawer-root.js +63 -4
  83. package/dist/drilldown/item/drilldown-item.js +1 -1
  84. package/dist/drilldown/root/drilldown-root.d.ts +21 -3
  85. package/dist/drilldown/root/drilldown-root.js +21 -3
  86. package/dist/element-events.generated.js +31 -5
  87. package/dist/empty/root/empty-root.d.ts +3 -1
  88. package/dist/empty/root/empty-root.js +3 -2
  89. package/dist/file-input/root/file-input-root.d.ts +69 -6
  90. package/dist/file-input/root/file-input-root.js +82 -19
  91. package/dist/file-input/screen.d.ts +2 -1
  92. package/dist/filter-input/menu/filter-menu.d.ts +2 -0
  93. package/dist/filter-input/menu/filter-menu.js +12 -4
  94. package/dist/filter-input/root/filter-input-root.d.ts +30 -6
  95. package/dist/filter-input/root/filter-input-root.js +1 -1
  96. package/dist/filter-input/suggest.d.ts +28 -3
  97. package/dist/filter-input/suggest.js +18 -8
  98. package/dist/format-date/root/format-date-root.d.ts +9 -16
  99. package/dist/format-date/root/format-date-root.js +10 -27
  100. package/dist/heatmap/root/heatmap-root.d.ts +24 -13
  101. package/dist/heatmap/root/heatmap-root.js +34 -12
  102. package/dist/icon/root/icon-root.d.ts +12 -1
  103. package/dist/icon/root/icon-root.js +21 -10
  104. package/dist/icon/settle.d.ts +7 -0
  105. package/dist/icon/settle.js +10 -0
  106. package/dist/icon-button/root/icon-button-root.d.ts +2 -0
  107. package/dist/image/root/image-root.d.ts +5 -1
  108. package/dist/image/root/image-root.js +1 -1
  109. package/dist/input/root/input-root.d.ts +12 -2
  110. package/dist/input/root/input-root.js +11 -2
  111. package/dist/kbd/root/kbd-root.d.ts +35 -1
  112. package/dist/kbd/root/kbd-root.js +23 -4
  113. package/dist/legend/root/legend-root.d.ts +55 -8
  114. package/dist/legend/root/legend-root.js +55 -13
  115. package/dist/lightbox/root/lightbox-root.d.ts +49 -6
  116. package/dist/lightbox/root/lightbox-root.js +86 -19
  117. package/dist/lightbox/zoom-controller.d.ts +59 -0
  118. package/dist/lightbox/zoom-controller.js +128 -0
  119. package/dist/lightbox/zoom.d.ts +22 -0
  120. package/dist/lightbox/zoom.js +16 -0
  121. package/dist/line-chart/root/line-chart-root.d.ts +22 -1
  122. package/dist/line-chart/root/line-chart-root.js +21 -1
  123. package/dist/list/item/list-item.d.ts +2 -0
  124. package/dist/list/item/list-item.js +11 -2
  125. package/dist/list/root/list-root.d.ts +62 -13
  126. package/dist/list/root/list-root.js +70 -13
  127. package/dist/list/selection.d.ts +37 -7
  128. package/dist/list/selection.js +16 -7
  129. package/dist/listbox/root/listbox-root.d.ts +37 -1
  130. package/dist/listbox/root/listbox-root.js +49 -3
  131. package/dist/loader/busy.d.ts +10 -0
  132. package/dist/loader/busy.js +11 -0
  133. package/dist/loader/root/loader-root.d.ts +9 -2
  134. package/dist/loader/root/loader-root.js +23 -3
  135. package/dist/megamenu/root/megamenu-root.d.ts +51 -10
  136. package/dist/megamenu/root/megamenu-root.js +76 -5
  137. package/dist/menu/item/menu-item.d.ts +51 -2
  138. package/dist/menu/item/menu-item.js +47 -23
  139. package/dist/menu/root/menu-root.d.ts +14 -0
  140. package/dist/menu/root/menu-root.js +2 -2
  141. package/dist/meter/root/meter-root.d.ts +20 -1
  142. package/dist/meter/root/meter-root.js +11 -15
  143. package/dist/navigation/root/navigation-root.d.ts +2 -1
  144. package/dist/navigation/root/navigation-root.js +2 -2
  145. package/dist/notifications/root/notifications-root.d.ts +10 -3
  146. package/dist/notifications/root/notifications-root.js +19 -7
  147. package/dist/number-input/root/number-input-root.d.ts +17 -8
  148. package/dist/number-input/root/number-input-root.js +2 -7
  149. package/dist/object-inspector/root/object-inspector-root.d.ts +75 -27
  150. package/dist/object-inspector/root/object-inspector-root.js +117 -76
  151. package/dist/object-inspector/rows.d.ts +18 -0
  152. package/dist/object-inspector/rows.js +33 -0
  153. package/dist/option/root/option-root.d.ts +5 -3
  154. package/dist/outline/root/outline-root.d.ts +18 -0
  155. package/dist/outline/root/outline-root.js +23 -5
  156. package/dist/pagination/size/pagination-size.d.ts +1 -0
  157. package/dist/pagination/size/pagination-size.js +1 -1
  158. package/dist/pin-input/code.d.ts +10 -0
  159. package/dist/pin-input/code.js +13 -0
  160. package/dist/pin-input/root/pin-input-root.d.ts +25 -1
  161. package/dist/pin-input/root/pin-input-root.js +10 -11
  162. package/dist/popover/hide-outside.d.ts +14 -0
  163. package/dist/popover/hide-outside.js +41 -0
  164. package/dist/popover/root/popover-root.d.ts +57 -7
  165. package/dist/popover/root/popover-root.js +58 -3
  166. package/dist/progress/root/progress-root.d.ts +20 -0
  167. package/dist/progress/root/progress-root.js +15 -15
  168. package/dist/qr-code/export.d.ts +26 -1
  169. package/dist/qr-code/export.js +14 -0
  170. package/dist/qr-code/root/qr-code-root.d.ts +19 -5
  171. package/dist/qr-code/root/qr-code-root.js +58 -9
  172. package/dist/quote/marks.d.ts +8 -0
  173. package/dist/quote/marks.js +5 -0
  174. package/dist/quote/root/quote-root.d.ts +14 -2
  175. package/dist/quote/root/quote-root.js +23 -1
  176. package/dist/radio/root/radio-root.d.ts +6 -2
  177. package/dist/radio/root/radio-root.js +6 -1
  178. package/dist/rating/root/rating-root.d.ts +9 -4
  179. package/dist/rating/root/rating-root.js +31 -7
  180. package/dist/rating/steps.d.ts +7 -0
  181. package/dist/rating/steps.js +9 -0
  182. package/dist/scatter-chart/fit.d.ts +15 -0
  183. package/dist/scatter-chart/fit.js +35 -0
  184. package/dist/scatter-chart/root/scatter-chart-root.d.ts +21 -1
  185. package/dist/scatter-chart/root/scatter-chart-root.js +30 -17
  186. package/dist/scroll-area/root/scroll-area-root.d.ts +22 -51
  187. package/dist/scroll-area/root/scroll-area-root.js +57 -173
  188. package/dist/scroll-area/scrollbar-controller.d.ts +111 -0
  189. package/dist/scroll-area/scrollbar-controller.js +172 -0
  190. package/dist/scroll-area/scrollbar.d.ts +27 -0
  191. package/dist/scroll-area/scrollbar.js +9 -0
  192. package/dist/select/root/select-root.d.ts +15 -0
  193. package/dist/select/root/select-root.js +18 -6
  194. package/dist/skeleton/root/skeleton-root.d.ts +3 -0
  195. package/dist/skeleton/root/skeleton-root.js +4 -2
  196. package/dist/skip-link/root/skip-link-root.js +2 -2
  197. package/dist/slider/root/slider-root.d.ts +41 -4
  198. package/dist/slider/root/slider-root.js +23 -3
  199. package/dist/sparkline/readings.d.ts +18 -0
  200. package/dist/sparkline/readings.js +19 -0
  201. package/dist/sparkline/root/sparkline-root.d.ts +78 -7
  202. package/dist/sparkline/root/sparkline-root.js +118 -23
  203. package/dist/speed-dial/root/speed-dial-root.js +2 -2
  204. package/dist/splitter/root/splitter-root.d.ts +7 -12
  205. package/dist/splitter/root/splitter-root.js +209 -204
  206. package/dist/stat/root/stat-root.d.ts +14 -5
  207. package/dist/stat/root/stat-root.js +21 -5
  208. package/dist/steps/reach.d.ts +5 -0
  209. package/dist/steps/reach.js +3 -0
  210. package/dist/steps/root/steps-root.d.ts +49 -3
  211. package/dist/steps/root/steps-root.js +44 -5
  212. package/dist/steps/step/step.d.ts +7 -0
  213. package/dist/steps/step/step.js +7 -2
  214. package/dist/switch/root/switch-root.d.ts +10 -0
  215. package/dist/table/column/table-column.d.ts +0 -10
  216. package/dist/table/column/table-column.js +3 -19
  217. package/dist/table/root/table-root.d.ts +31 -10
  218. package/dist/table/root/table-root.js +2 -13
  219. package/dist/tabs/panel/tab-panel.d.ts +16 -3
  220. package/dist/tabs/panel/tab-panel.js +34 -5
  221. package/dist/tabs/root/tabs-root.d.ts +12 -0
  222. package/dist/tabs/root/tabs-root.js +1 -1
  223. package/dist/tabs/tab/tab.d.ts +5 -0
  224. package/dist/tabs/tab/tab.js +5 -2
  225. package/dist/tag/group/tag-group.d.ts +31 -4
  226. package/dist/tag/group/tag-group.js +46 -7
  227. package/dist/tag/root/tag-root.js +9 -2
  228. package/dist/textarea/root/textarea-root.d.ts +28 -1
  229. package/dist/textarea/root/textarea-root.js +40 -1
  230. package/dist/time-input/root/time-input-root.d.ts +5 -1
  231. package/dist/time-input/root/time-input-root.js +16 -8
  232. package/dist/time-picker/root/time-picker-root.d.ts +18 -4
  233. package/dist/time-picker/root/time-picker-root.js +7 -10
  234. package/dist/time-picker/wheel-name.d.ts +9 -0
  235. package/dist/time-picker/wheel-name.js +15 -0
  236. package/dist/timeline/group/timeline-group.js +2 -2
  237. package/dist/toast/item/toast-item.d.ts +14 -1
  238. package/dist/toast/item/toast-item.js +22 -5
  239. package/dist/toast/root/toast-root.d.ts +6 -0
  240. package/dist/toast/root/toast-root.js +16 -1
  241. package/dist/toggle/group/toggle-group.d.ts +24 -3
  242. package/dist/toggle/group/toggle-group.js +34 -5
  243. package/dist/tooltip/root/tooltip-root.d.ts +13 -2
  244. package/dist/tooltip/root/tooltip-root.js +9 -1
  245. package/dist/tour/back.d.ts +10 -0
  246. package/dist/tour/back.js +8 -0
  247. package/dist/tour/root/tour-root.d.ts +30 -2
  248. package/dist/tour/root/tour-root.js +34 -8
  249. package/dist/tree/item/tree-item.d.ts +7 -3
  250. package/dist/tree/item/tree-item.js +3 -0
  251. package/dist/tree/root/tree-root.d.ts +24 -3
  252. package/dist/tree/root/tree-root.js +32 -13
  253. package/dist/tree/selection.js +2 -15
  254. package/dist/trend/root/trend-root.d.ts +43 -18
  255. package/dist/trend/root/trend-root.js +31 -6
  256. package/dist/video/media-events.d.ts +13 -18
  257. package/dist/video/media-events.js +1 -5
  258. package/dist/video/root/video-root.d.ts +18 -5
  259. package/dist/video/root/video-root.js +19 -10
  260. package/dist/video-playlist/menu/video-playlist-menu.d.ts +2 -0
  261. package/dist/video-playlist/menu/video-playlist-menu.js +2 -1
  262. package/dist/video-playlist/root/video-playlist-root.d.ts +7 -1
  263. package/dist/virtual-list/fill.d.ts +0 -8
  264. package/dist/virtual-list/fill.js +1 -17
  265. package/dist/virtual-list/plan.d.ts +49 -0
  266. package/dist/virtual-list/plan.js +16 -0
  267. package/dist/virtual-list/root/virtual-list-root.d.ts +114 -13
  268. package/dist/virtual-list/root/virtual-list-root.js +183 -40
  269. package/lint.json +1 -1
  270. package/package.json +8 -8
  271. package/types/jsx.d.ts +4577 -835
  272. package/types/vue.d.ts +926 -168
  273. package/dist/chat/relative-clock.d.ts +0 -39
  274. package/dist/chat/relative-clock.js +0 -31
  275. package/dist/data-table/root/order.d.ts +0 -141
  276. package/dist/data-table/root/order.js +0 -76
  277. package/dist/tag/selection.d.ts +0 -10
  278. package/dist/tag/selection.js +0 -12
@@ -9,6 +9,7 @@ import { caretLeft, caretRight, play, stop } 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
+ import { stepStop, stopFor, stopsOf } from '../stops.js';
12
13
  const COUNT_OR_AUTO = {
13
14
  fromAttribute: (value) => {
14
15
  if (value === null) {
@@ -59,7 +60,7 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
59
60
  ? -1
60
61
  : 0
61
62
  : inlineStep(this, event.key);
62
- let to = this.index + step;
63
+ let to;
63
64
  if (event.key === 'Home') {
64
65
  to = 0;
65
66
  }
@@ -69,6 +70,9 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
69
70
  else if (step === 0) {
70
71
  return;
71
72
  }
73
+ else {
74
+ to = this.move(step);
75
+ }
72
76
  event.preventDefault();
73
77
  this.goTo(to);
74
78
  this.updateComplete.then(() => {
@@ -213,6 +217,9 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
213
217
  get positions() {
214
218
  return Math.max(1, this.slides.length - (this.across ?? 1) + 1);
215
219
  }
220
+ get stops() {
221
+ return stopsOf(this.positions, this.slidesPerMove);
222
+ }
216
223
  measure() {
217
224
  const track = this.partNamed('track');
218
225
  const pane = this.panes[0]?.getBoundingClientRect().width;
@@ -281,6 +288,9 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
281
288
  this.requestUpdate('index', previous);
282
289
  this.dispatchEvent(new HelixActivateEvent({ value: String(value) }));
283
290
  }
291
+ move(step) {
292
+ return stepStop(this.stops, this.index, step < 0 ? -1 : 1, this.loop);
293
+ }
284
294
  goTo(index) {
285
295
  if (this.slides.length === 0) {
286
296
  return;
@@ -357,7 +367,7 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
357
367
  if (!this.rotating || this.slides.length < 2) {
358
368
  return;
359
369
  }
360
- this.timer = setInterval(() => this.goTo(this.index >= this.positions - 1 ? 0 : this.index + 1), this.interval);
370
+ this.timer = setInterval(() => this.goTo(this.index >= this.positions - 1 ? 0 : this.move(1)), this.interval);
361
371
  }
362
372
  nearest() {
363
373
  const track = this.partNamed('track');
@@ -366,8 +376,11 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
366
376
  }
367
377
  const bleed = this.bleed(track);
368
378
  const at = this.orientation === 'vertical' ? track.scrollTop : track.scrollLeft;
369
- return this.panes.slice(0, this.positions).reduce((best, slide, index) => {
370
- const distance = Math.abs(this.edgeOf(slide, track, bleed) - at);
379
+ return this.stops.reduce((best, index) => {
380
+ const slide = this.panes[index];
381
+ const distance = slide
382
+ ? Math.abs(this.edgeOf(slide, track, bleed) - at)
383
+ : Number.POSITIVE_INFINITY;
371
384
  return distance < best.distance ? { distance, index } : best;
372
385
  }, { distance: Number.POSITIVE_INFINITY, index: this.index }).index;
373
386
  }
@@ -484,9 +497,21 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
484
497
  </svg></slot
485
498
  >`;
486
499
  }
500
+ paneStyle(index, last, stops) {
501
+ const declared = [
502
+ index === last ? this.runEnd() : nothing,
503
+ (this.slidesPerMove ?? 1) > 1 && !stops.includes(index)
504
+ ? 'scroll-snap-align: none !important'
505
+ : nothing,
506
+ ].filter((one) => one !== nothing);
507
+ return declared.length > 0 ? declared.join('; ') : nothing;
508
+ }
487
509
  render() {
488
510
  const rotating = this.rotating;
489
511
  const last = this.slides.length - 1;
512
+ const stops = this.stops;
513
+ const place = stopFor(stops, this.index);
514
+ const showing = stopFor(stops, this.showing ?? this.index);
490
515
  return html `<div part="controls">
491
516
  ${this.autoplay
492
517
  ? html `<button
@@ -511,7 +536,7 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
511
536
  type="button"
512
537
  aria-label=${this.previousLabel}
513
538
  aria-disabled=${this.atEnd('start') ? 'true' : nothing}
514
- @click=${() => this.goTo(this.index - 1)}
539
+ @click=${() => this.goTo(this.move(-1))}
515
540
  >
516
541
  ${this.renderPreviousMark()}
517
542
  </button>
@@ -520,7 +545,7 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
520
545
  type="button"
521
546
  aria-label=${this.nextLabel}
522
547
  aria-disabled=${this.atEnd('end') ? 'true' : nothing}
523
- @click=${() => this.goTo(this.index + 1)}
548
+ @click=${() => this.goTo(this.move(1))}
524
549
  >
525
550
  ${this.renderNextMark()}
526
551
  </button>`}
@@ -545,7 +570,7 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
545
570
  role="group"
546
571
  aria-roledescription=${this.slideRoleLabel || nothing}
547
572
  aria-label=${fillTemplate(this.slideLabel, { count: this.slides.length, index: index + 1 })}
548
- style=${index === last ? this.runEnd() : nothing}
573
+ style=${this.paneStyle(index, last, stops)}
549
574
  >
550
575
  <slot
551
576
  name=${this.assignsByName ? `slide-${index + 1}` : nothing}
@@ -557,17 +582,17 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
557
582
  ${this.slides.length > 1 && this.across !== undefined
558
583
  ? html `<div part="indicators" role="group" aria-label=${fillTemplate(this.indicatorsLabel, { label: this.label })}>
559
584
  ${''}
560
- ${Array.from({ length: this.positions }).map((_place, at) => {
561
- const current = at === (this.showing ?? this.index);
585
+ ${stops.map((stop, at) => {
586
+ const current = at === showing;
562
587
  return html `<button
563
588
  part="indicator${current ? ' indicator-current' : ''}"
564
589
  type="button"
565
590
  aria-label=${fillTemplate(this.indicatorLabel, { index: at + 1 })}
566
- aria-current=${at === this.index ? 'true' : nothing}
567
- tabindex=${at === this.index ? '0' : '-1'}
591
+ aria-current=${at === place ? 'true' : nothing}
592
+ tabindex=${at === place ? '0' : '-1'}
568
593
  ?data-current=${current}
569
594
  @keydown=${this.handleIndicatorKeydown}
570
- @click=${() => this.goTo(at)}
595
+ @click=${() => this.goTo(stop)}
571
596
  ></button>`;
572
597
  })}
573
598
  </div>`
@@ -628,6 +653,9 @@ __decorate([
628
653
  __decorate([
629
654
  property({ attribute: 'slide-role-label' })
630
655
  ], HelixHeadlessCarousel.prototype, "slideRoleLabel", void 0);
656
+ __decorate([
657
+ property({ attribute: 'slides-per-move', type: Number })
658
+ ], HelixHeadlessCarousel.prototype, "slidesPerMove", void 0);
631
659
  __decorate([
632
660
  property({ attribute: 'start-label' })
633
661
  ], HelixHeadlessCarousel.prototype, "startLabel", void 0);
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Where a carousel's track stops when it moves several slides at a time, as
3
+ * arithmetic over its positions.
4
+ *
5
+ * A position is a slide the track can start at: six slides four across have
6
+ * three, 0, 1 and 2. Moving `perMove` slides at a time stops at every
7
+ * `perMove`th of them from the first, and always at the last, so the end of
8
+ * the run is reachable whatever the count.
9
+ */
10
+ /** The positions the track stops at, in order: every `perMove`th from 0, and the last. */
11
+ export declare const stopsOf: (positions: number, perMove?: number) => number[];
12
+ /** Which stop a position is at or past: the last stop at or before it, as an index into `stops`. */
13
+ export declare const stopFor: (stops: readonly number[], position: number) => number;
14
+ /**
15
+ * The position a move of one stop forward (`1`) or back (`-1`) lands on.
16
+ *
17
+ * From a position between two stops, back goes to the stop before it. Past
18
+ * either end it wraps where `loop` says so, and stays at the end otherwise.
19
+ */
20
+ export declare const stepStop: (stops: readonly number[], position: number, step: -1 | 1, loop: boolean) => number;
@@ -0,0 +1,30 @@
1
+ export const stopsOf = (positions, perMove = 1) => {
2
+ const step = Number.isFinite(perMove) ? Math.max(1, Math.floor(perMove)) : 1;
3
+ const last = Math.max(0, positions - 1);
4
+ const stops = [];
5
+ for (let at = 0; at < last; at += step) {
6
+ stops.push(at);
7
+ }
8
+ stops.push(last);
9
+ return stops;
10
+ };
11
+ export const stopFor = (stops, position) => {
12
+ let found = 0;
13
+ stops.forEach((stop, at) => {
14
+ if (stop <= position) {
15
+ found = at;
16
+ }
17
+ });
18
+ return found;
19
+ };
20
+ export const stepStop = (stops, position, step, loop) => {
21
+ const at = stopFor(stops, position);
22
+ let next = step < 0 && stops[at] !== position ? at : at + step;
23
+ if (loop) {
24
+ next = (next + stops.length) % stops.length;
25
+ }
26
+ else {
27
+ next = Math.min(Math.max(next, 0), stops.length - 1);
28
+ }
29
+ return stops[next] ?? 0;
30
+ };
@@ -4,12 +4,11 @@ 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, HelixSendEvent, hostAria, markRegion, relativeTime, requestChange, TONE_MARKS, } from '@fusion.dev/helix-core';
7
+ import { customElement, HelixElement, HelixSendEvent, hostAria, markRegion, RelativeClockController, relativeTime, requestChange, TONE_MARKS, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
11
11
  import { authorWords, TURN_WORDS, turnName } from '../naming.js';
12
- import { RelativeClockController } from '../relative-clock.js';
13
12
  export const FAILED_LABEL = 'Not sent';
14
13
  let HelixHeadlessChatMessage = class HelixHeadlessChatMessage extends HelixElement {
15
14
  constructor() {
@@ -23,8 +22,10 @@ let HelixHeadlessChatMessage = class HelixHeadlessChatMessage extends HelixEleme
23
22
  this.turnWords = TURN_WORDS;
24
23
  this.isChatMessage = true;
25
24
  this.clock = new RelativeClockController(this, {
26
- locale: () => this.locale,
27
- moment: () => this.moment,
25
+ expires: (now) => {
26
+ const moment = this.moment;
27
+ return moment ? relativeTime(this.locale)(moment, now).next : undefined;
28
+ },
28
29
  });
29
30
  this.handleFill = () => {
30
31
  this.markEmpty();
@@ -4,6 +4,8 @@ export interface TurnWords {
4
4
  me: string;
5
5
  /** The sentence for a turn with an author and a time, with `{author}` and `{time}`. */
6
6
  sentence: string;
7
+ /** The name of a turn with neither an author a reader can be told nor a time. */
8
+ turn: string;
7
9
  }
8
10
  /** The words a chat says its turns with, where nobody said otherwise. */
9
11
  export declare const TURN_WORDS: TurnWords;
@@ -17,6 +19,8 @@ export declare const TURN_WORDS: TurnWords;
17
19
  export declare const authorWords: (author: string | undefined, authorLabel: string, words: TurnWords) => string;
18
20
  /**
19
21
  * The turn's accessible name: the sentence where it has an author and a time,
20
- * and whichever of the two it has otherwise. Empty where it has neither.
22
+ * and whichever of the two it has otherwise. Where it has neither, a turn by
23
+ * `them` with no `author-label` and no time, the chat's word for a turn, so
24
+ * no turn is an `article` with no name.
21
25
  */
22
26
  export declare const turnName: (author: string, time: string, words: TurnWords) => string;
@@ -1,5 +1,5 @@
1
1
  import { fillTemplate } from '@fusion.dev/helix-core';
2
- export const TURN_WORDS = { me: 'You', sentence: '{author}, {time}' };
2
+ export const TURN_WORDS = { me: 'You', sentence: '{author}, {time}', turn: 'Message' };
3
3
  export const authorWords = (author, authorLabel, words) => {
4
4
  if (authorLabel !== '') {
5
5
  return authorLabel;
@@ -9,4 +9,6 @@ export const authorWords = (author, authorLabel, words) => {
9
9
  }
10
10
  return author === undefined || author === 'them' ? '' : author;
11
11
  };
12
- export const turnName = (author, time, words) => author !== '' && time !== '' ? fillTemplate(words.sentence, { author, time }) : author || time;
12
+ export const turnName = (author, time, words) => author !== '' && time !== ''
13
+ ? fillTemplate(words.sentence, { author, time })
14
+ : author || time || words.turn;
@@ -53,12 +53,15 @@ declare const HelixHeadlessChat_base: (new (...args: any[]) => import("@fusion.d
53
53
  * `sent` or `failed` when your server answers. Add the reply as a turn with
54
54
  * `state="streaming"`, write its text as it arrives, and set `state` to `sent`
55
55
  * when it is complete. While the user types, the composer sends
56
- * `helix-typing`, which you can pass on to the other side. Show the other
57
- * side's typing in the `status` slot.
56
+ * `helix-typing`, which you can pass on to the other side. When the other side
57
+ * types, set `typing` to their name: the status band draws three dots and
58
+ * "Ada is typing" (`typing-label`), which a screen reader hears, and clearing
59
+ * it takes them away. Anything slotted into `status` is drawn first.
58
60
  *
59
61
  * A screen reader names each turn by its author and its time, such as "You, 2
60
62
  * minutes ago". Set `me-label` and `message-label` to say it in your language,
61
- * and give each turn by `them` an `author-label`.
63
+ * and give each turn by `them` an `author-label`. A turn with neither an author
64
+ * label nor a time is named by `turn-label`, "Message".
62
65
  *
63
66
  * A turn from `turns` is drawn in the chat's shadow root, so style it through
64
67
  * the forwarded `message-*` parts. A turn by `me` also gets each part with
@@ -91,6 +94,7 @@ declare const HelixHeadlessChat_base: (new (...args: any[]) => import("@fusion.d
91
94
  * @csspart empty - Shown when the conversation has no turns.
92
95
  * @csspart header - The region above the transcript.
93
96
  * @csspart history - Shown at the top of the transcript while older turns load.
97
+ * @csspart indicator - The three dots before the words that say who is typing, which the styled tier animates as a message's are. Hidden from screen readers.
94
98
  * @csspart live - The region where screen readers announce each turn once it has arrived.
95
99
  * @csspart loading - Shown while the history loads.
96
100
  * @csspart log - The transcript, which scrolls.
@@ -117,6 +121,7 @@ declare const HelixHeadlessChat_base: (new (...args: any[]) => import("@fusion.d
117
121
  * @csspart resume - The control shown while the user is scrolled away from the bottom.
118
122
  * @csspart status - The status region, outside the log.
119
123
  * @csspart turn - The wrapper around the message drawn for one turn from `turns`.
124
+ * @csspart typing - The words that say who is typing, in the status band, while `typing` names someone.
120
125
  */
121
126
  export declare class HelixHeadlessChat extends HelixHeadlessChat_base {
122
127
  /**
@@ -197,6 +202,11 @@ export declare class HelixHeadlessChat extends HelixHeadlessChat_base {
197
202
  * history.
198
203
  */
199
204
  threshold: number;
205
+ /**
206
+ * The name a screen reader gives a turn with neither an author it can say
207
+ * nor a time: a turn by `them` with no `author-label`, say.
208
+ */
209
+ turnLabel: string;
200
210
  /**
201
211
  * The turns as data, for a transcript too long to hold as elements.
202
212
  *
@@ -208,6 +218,18 @@ export declare class HelixHeadlessChat extends HelixHeadlessChat_base {
208
218
  * element to look again.
209
219
  */
210
220
  turns: readonly ChatTurn[];
221
+ /**
222
+ * Who on the other side is typing, by name, or empty while nobody is.
223
+ *
224
+ * Set it from your connection, and clear it when they stop or send. The
225
+ * status band says it with `typing-label`.
226
+ */
227
+ typing: string;
228
+ /**
229
+ * What the status band says while someone is typing. `{author}` is the
230
+ * name in `typing`.
231
+ */
232
+ typingLabel: string;
211
233
  private pinnedState;
212
234
  /** The last thing said, and how many times something has been. */
213
235
  private announcement;
@@ -5,7 +5,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
7
  var HelixHeadlessChat_1;
8
- import { customElement, HelixBeforePinEvent, HelixElement, HelixHistoryEvent, HelixPinEvent, hostAria, MixinCollection, onUnlessFalse, requestChange, VirtualizeController, whenHydrated, } from '@fusion.dev/helix-core';
8
+ import { customElement, fillTemplate, HelixBeforePinEvent, HelixElement, HelixHistoryEvent, HelixPinEvent, hostAria, MixinCollection, onUnlessFalse, requestChange, VirtualizeController, whenHydrated, } from '@fusion.dev/helix-core';
9
9
  import { nothing } from 'lit';
10
10
  import { property, state } from 'lit/decorators.js';
11
11
  import { keyed } from 'lit/directives/keyed.js';
@@ -43,7 +43,10 @@ let HelixHeadlessChat = class HelixHeadlessChat extends MixinCollection(HelixEle
43
43
  this.meLabel = 'You';
44
44
  this.messageLabel = '{author}, {time}';
45
45
  this.threshold = 48;
46
+ this.turnLabel = 'Message';
46
47
  this.turns = [];
48
+ this.typing = '';
49
+ this.typingLabel = '{author} is typing';
47
50
  this.pinnedState = undefined;
48
51
  this.announcement = '';
49
52
  this.said = 0;
@@ -365,8 +368,8 @@ let HelixHeadlessChat = class HelixHeadlessChat extends MixinCollection(HelixEle
365
368
  willUpdate(changed) {
366
369
  super.willUpdate(changed);
367
370
  hostAria(this.internals, { label: this.label || 'Conversation', role: 'region' });
368
- if (changed.has('meLabel') || changed.has('messageLabel')) {
369
- this.words = { me: this.meLabel, sentence: this.messageLabel };
371
+ if (changed.has('meLabel') || changed.has('messageLabel') || changed.has('turnLabel')) {
372
+ this.words = { me: this.meLabel, sentence: this.messageLabel, turn: this.turnLabel };
370
373
  }
371
374
  this.countRegions();
372
375
  if (changed.has('history')) {
@@ -466,8 +469,13 @@ let HelixHeadlessChat = class HelixHeadlessChat extends MixinCollection(HelixEle
466
469
  <div part="resume" ?hidden=${!away || !this.has('resume')} @click=${this.handleResume}>
467
470
  <slot name="resume"></slot>
468
471
  </div>
469
- <div part="status" role="status" ?data-empty=${!this.has('status')}>
470
- <slot name="status"></slot>
472
+ <div part="status" role="status" ?data-empty=${!this.has('status') && this.typing === ''}>
473
+ <slot name="status"></slot>${this.typing === ''
474
+ ? nothing
475
+ : html `<span part="typing"
476
+ ><span part="indicator" aria-hidden="true"></span
477
+ >${fillTemplate(this.typingLabel, { author: this.typing })}</span
478
+ >`}
471
479
  </div>
472
480
  <div part="composer" ?hidden=${!this.has('composer')}>
473
481
  <slot name="composer"></slot>
@@ -498,9 +506,18 @@ __decorate([
498
506
  __decorate([
499
507
  property({ type: Number })
500
508
  ], HelixHeadlessChat.prototype, "threshold", void 0);
509
+ __decorate([
510
+ property({ attribute: 'turn-label' })
511
+ ], HelixHeadlessChat.prototype, "turnLabel", void 0);
501
512
  __decorate([
502
513
  property({ attribute: false })
503
514
  ], HelixHeadlessChat.prototype, "turns", void 0);
515
+ __decorate([
516
+ property()
517
+ ], HelixHeadlessChat.prototype, "typing", void 0);
518
+ __decorate([
519
+ property({ attribute: 'typing-label' })
520
+ ], HelixHeadlessChat.prototype, "typingLabel", void 0);
504
521
  __decorate([
505
522
  state()
506
523
  ], HelixHeadlessChat.prototype, "pinnedState", void 0);
@@ -21,6 +21,16 @@ declare const HelixHeadlessCheckbox_base: (new (...args: any[]) => import("@fusi
21
21
  * it as neither checked nor unchecked, it never submits, and the first click
22
22
  * resolves it.
23
23
  *
24
+ * **Keyboard.** The box is one tab stop.
25
+ *
26
+ * | Key | What it does |
27
+ * |---|---|
28
+ * | Space | Checks or unchecks the box, and resolves `indeterminate`. |
29
+ *
30
+ * The native input reports checked, unchecked, or mixed. `aria-readonly` is set
31
+ * under `readonly`, and `aria-invalid` and `aria-describedby` point at the
32
+ * message while one shows.
33
+ *
24
34
  * @since 0.1.0
25
35
  * @status stable
26
36
  * @category forms
@@ -28,6 +38,8 @@ declare const HelixHeadlessCheckbox_base: (new (...args: any[]) => import("@fusi
28
38
  * @documentation https://fusion.dev/helix/components/checkbox
29
39
  *
30
40
  * @slot - The label.
41
+ * @slot check - An icon drawn as the mark while the box is checked, in place of the tick.
42
+ * @slot dash - An icon drawn as the mark while the box is indeterminate, in place of the dash.
31
43
  * @slot feedback - The message that says what is wrong, for content richer than a string. It shows while there is a message.
32
44
  * @slot hint - Help under the control, for content richer than the `hint` text.
33
45
  *
@@ -47,6 +59,8 @@ declare const HelixHeadlessCheckbox_base: (new (...args: any[]) => import("@fusi
47
59
  * @csspart mark-indeterminate - Added to `mark` while the box is indeterminate.
48
60
  *
49
61
  * @cssstate checked - While the box is checked.
62
+ * @cssstate has-check - An icon is slotted into `check`.
63
+ * @cssstate has-dash - An icon is slotted into `dash`.
50
64
  * @cssstate has-label - The label slot has content, so the label is drawn.
51
65
  * @cssstate indeterminate - While the box shows the dash, neither checked nor unchecked.
52
66
  * @cssstate invalid - The control has an error to show, from the browser's validation or from your `feedback`.
@@ -107,6 +121,12 @@ export declare class HelixHeadlessCheckbox extends HelixHeadlessCheckbox_base {
107
121
  * worse problem than its spacing.
108
122
  */
109
123
  private readonly handleLabelChange;
124
+ /**
125
+ * Records which of the mark's slots hold an icon, as `:state(has-check)` and
126
+ * `:state(has-dash)`, so a stylesheet drops its own drawing of that shape.
127
+ * A state, for the reason `:state(has-label)` is one.
128
+ */
129
+ private readonly handleMarkChange;
110
130
  /**
111
131
  * Asks the slot, and records the answer as a state.
112
132
  *
@@ -32,6 +32,16 @@ let HelixHeadlessCheckbox = class HelixHeadlessCheckbox extends MixinControllerC
32
32
  this.handleLabelChange = () => {
33
33
  this.syncLabelPresence();
34
34
  };
35
+ this.handleMarkChange = (event) => {
36
+ const slot = event.target;
37
+ const filled = slot.assignedNodes().length > 0;
38
+ if (slot.name === 'dash') {
39
+ this.toggleState('has-dash', filled);
40
+ }
41
+ else {
42
+ this.toggleState('has-check', filled);
43
+ }
44
+ };
35
45
  }
36
46
  static { this.shadowRootOptions = {
37
47
  ...HelixFormElement.shadowRootOptions,
@@ -94,7 +104,10 @@ let HelixHeadlessCheckbox = class HelixHeadlessCheckbox extends MixinControllerC
94
104
  <span
95
105
  part="mark${this.checked ? ' mark-checked' : ''}${this.indeterminate ? ' mark-indeterminate' : ''}${this.dashed ? ' mark-dash' : ''}"
96
106
  aria-hidden="true"
97
- ></span>
107
+ >${this.dashed
108
+ ? html `<slot name="dash" @slotchange=${this.handleMarkChange}></slot>`
109
+ : html `<slot name="check" @slotchange=${this.handleMarkChange}></slot>`}</span
110
+ >
98
111
  </span>
99
112
  <span part="label" ?data-hidden=${named !== null}
100
113
  ><slot @slotchange=${this.handleLabelChange}>${this.label || named}</slot></span
@@ -34,6 +34,9 @@ declare const HelixHeadlessCheckboxGroup_base: (new (...args: any[]) => import("
34
34
  * only for validity and never calls `setFormValue`, so the browser does not
35
35
  * generate a message for it.
36
36
  *
37
+ * **Keyboard.** Each box is its own tab stop, as a set of native checkboxes
38
+ * is, and Space checks the focused one. The select-all box comes first.
39
+ *
37
40
  * @since 0.1.0
38
41
  * @status stable
39
42
  * @category forms
@@ -221,12 +224,15 @@ export declare class HelixHeadlessCheckboxGroup extends HelixHeadlessCheckboxGro
221
224
  * for the whole set, as a box moves it.
222
225
  */
223
226
  private readonly handleSelectAll;
227
+ /** The set as the select-all helper reads it. */
228
+ private setBoxes;
224
229
  /**
225
230
  * The box above the set that checks or unchecks every box, under
226
231
  * `select-all`, behind a seam a tier can move.
227
232
  *
228
- * Checked while every box is, and showing a dash while only some are. It is
229
- * in this shadow root, so it is no member of the set and submits nothing.
233
+ * Checked while every enabled box is, and showing a dash while only some
234
+ * are, so its state says what a press does. It is in this shadow root, so
235
+ * it is no member of the set and submits nothing.
230
236
  */
231
237
  protected renderSelectAll(): TemplateResult | typeof nothing;
232
238
  protected render(): TemplateResult;
@@ -11,6 +11,7 @@ import { property } from 'lit/decorators.js';
11
11
  import { html } from 'lit/static-html.js';
12
12
  import { HelixHeadlessCheckbox } from '../../checkbox/root/checkbox-root.js';
13
13
  import '../../checkbox/index.js';
14
+ import { selectAllPress, selectAllState } from '../select-all.js';
14
15
  let HelixHeadlessCheckboxGroup = HelixHeadlessCheckboxGroup_1 = class HelixHeadlessCheckboxGroup extends MixinField(HelixFormElement) {
15
16
  constructor() {
16
17
  super(...arguments);
@@ -59,11 +60,7 @@ let HelixHeadlessCheckboxGroup = HelixHeadlessCheckboxGroup_1 = class HelixHeadl
59
60
  this.handleSelectAll = (event) => {
60
61
  event.preventDefault();
61
62
  event.stopPropagation();
62
- const enabled = this.boxes.filter((box) => !box.isDisabled);
63
- const every = enabled.length > 0 && enabled.every((box) => box.checked);
64
- const next = this.boxes
65
- .filter((box) => (enabled.includes(box) ? !every : box.checked))
66
- .map((box) => box.value);
63
+ const next = selectAllPress(this.setBoxes());
67
64
  if (!this.dispatchEvent(new HelixBeforeChangeEvent({ value: next.join(',') }))) {
68
65
  return;
69
66
  }
@@ -177,13 +174,18 @@ let HelixHeadlessCheckboxGroup = HelixHeadlessCheckboxGroup_1 = class HelixHeadl
177
174
  this.validate();
178
175
  this.linkHost();
179
176
  }
177
+ setBoxes() {
178
+ return this.boxes.map((box) => ({
179
+ checked: box.checked,
180
+ disabled: box.isDisabled,
181
+ value: box.value,
182
+ }));
183
+ }
180
184
  renderSelectAll() {
181
185
  if (!this.selectAll) {
182
186
  return nothing;
183
187
  }
184
- const boxes = this.childrenKnown ? this.boxes : [];
185
- const ticked = boxes.filter((box) => box.checked).length;
186
- const every = boxes.length > 0 && ticked === boxes.length;
188
+ const { checked, indeterminate } = selectAllState(this.childrenKnown ? this.setBoxes() : []);
187
189
  const box = this.tag('checkbox');
188
190
  return html `<${box}
189
191
  part="select-all"
@@ -191,8 +193,8 @@ let HelixHeadlessCheckboxGroup = HelixHeadlessCheckboxGroup_1 = class HelixHeadl
191
193
  feedback: select-all-feedback, field: select-all-field, hint: select-all-hint,
192
194
  label: select-all-label, mark: select-all-mark, mark-checked: select-all-mark-checked,
193
195
  mark-dash: select-all-mark-dash, mark-indeterminate: select-all-mark-indeterminate"
194
- .checked=${every}
195
- .indeterminate=${ticked > 0 && !every}
196
+ .checked=${checked}
197
+ .indeterminate=${indeterminate}
196
198
  ?disabled=${this.isDisabled}
197
199
  label=${this.selectAllLabel}
198
200
  @helix-before-toggle=${this.handleSelectAll}
@@ -0,0 +1,27 @@
1
+ /** What the select-all helper reads from one box in the set. */
2
+ export interface SetBox {
3
+ /** Whether the box is checked. */
4
+ readonly checked: boolean;
5
+ /** Whether the box refuses a press, so select-all leaves it as it is. */
6
+ readonly disabled: boolean;
7
+ /** What the box submits when checked. */
8
+ readonly value: string;
9
+ }
10
+ /**
11
+ * What the select-all box shows: checked while every enabled box is checked,
12
+ * mixed while only some are, and clear while none is.
13
+ *
14
+ * A disabled box counts for nothing, because a press leaves it as it is. A
15
+ * set whose every box is disabled reads every box, so the select-all box still
16
+ * shows what the set holds.
17
+ */
18
+ export declare const selectAllState: (boxes: readonly SetBox[]) => {
19
+ checked: boolean;
20
+ indeterminate: boolean;
21
+ };
22
+ /**
23
+ * The values a press on the select-all box leaves, in the set's order: every
24
+ * enabled box checked, or every enabled box clear once all of them are
25
+ * checked. A disabled box keeps its state either way.
26
+ */
27
+ export declare const selectAllPress: (boxes: readonly SetBox[]) => string[];
@@ -0,0 +1,12 @@
1
+ export const selectAllState = (boxes) => {
2
+ const enabled = boxes.filter((box) => !box.disabled);
3
+ const counted = enabled.length > 0 ? enabled : boxes;
4
+ const ticked = counted.filter((box) => box.checked).length;
5
+ const checked = counted.length > 0 && ticked === counted.length;
6
+ return { checked, indeterminate: ticked > 0 && !checked };
7
+ };
8
+ export const selectAllPress = (boxes) => {
9
+ const enabled = boxes.filter((box) => !box.disabled);
10
+ const every = enabled.length > 0 && enabled.every((box) => box.checked);
11
+ return boxes.filter((box) => (box.disabled ? box.checked : !every)).map((box) => box.value);
12
+ };
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The highlighted lines out of what a highlighter returned, without the
3
+ * `<pre>` and `<code>` around them that some highlighters write.
4
+ *
5
+ * Shiki's `codeToHtml` returns a whole block, `<pre class="shiki" tabindex="0"
6
+ * style="…"><code>…</code></pre>`. Drawn as given inside the block's own
7
+ * `<pre>`, that second `<pre>` sat 13px below the gutter's first number, and
8
+ * its `tabindex` was a second tab stop inside the block's scroller. Prism's
9
+ * `highlight` and highlight.js return the lines alone, which pass through
10
+ * unchanged.
11
+ *
12
+ * Only a wrapper around the whole markup is taken off, and only one. The block draws its own
13
+ * box and colors, so the wrapper's `style` and `class` go with it.
14
+ */
15
+ export declare const highlightedLines: (markup: string) => string;
@@ -0,0 +1,4 @@
1
+ export const highlightedLines = (markup) => {
2
+ const lines = /^\s*<pre\b[^>]*>\s*<code\b[^>]*>([\s\S]*)<\/code>\s*<\/pre>\s*$/i.exec(markup)?.[1];
3
+ return lines === undefined || /<\/?pre\b/i.test(lines) ? markup : lines;
4
+ };