@fusion.dev/helix-headless 0.198.0 → 0.199.1

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 (53) hide show
  1. package/api.json +1 -1
  2. package/custom-elements.json +8267 -2270
  3. package/dist/carousel/root/carousel-root.d.ts +14 -0
  4. package/dist/carousel/root/carousel-root.js +15 -1
  5. package/dist/element-events.generated.js +19 -0
  6. package/dist/graph/root/graph-root.d.ts +33 -0
  7. package/dist/graph/root/graph-root.js +57 -0
  8. package/dist/index.d.ts +4 -0
  9. package/dist/index.js +4 -0
  10. package/dist/map/raster/map-raster.d.ts +7 -3
  11. package/dist/map/raster/map-raster.js +5 -3
  12. package/dist/motion-toggle/index.d.ts +1 -0
  13. package/dist/motion-toggle/index.js +1 -0
  14. package/dist/motion-toggle/root/motion-toggle-root.d.ts +77 -0
  15. package/dist/motion-toggle/root/motion-toggle-root.js +56 -0
  16. package/dist/spatial-bar-chart/bars.d.ts +79 -0
  17. package/dist/spatial-bar-chart/bars.js +72 -0
  18. package/dist/spatial-bar-chart/index.d.ts +2 -0
  19. package/dist/spatial-bar-chart/index.js +2 -0
  20. package/dist/spatial-bar-chart/root/spatial-bar-chart-root.d.ts +245 -0
  21. package/dist/spatial-bar-chart/root/spatial-bar-chart-root.js +262 -0
  22. package/dist/spatial-ribbon-chart/index.d.ts +2 -0
  23. package/dist/spatial-ribbon-chart/index.js +2 -0
  24. package/dist/spatial-ribbon-chart/ribbons.d.ts +55 -0
  25. package/dist/spatial-ribbon-chart/ribbons.js +99 -0
  26. package/dist/spatial-ribbon-chart/root/spatial-ribbon-chart-root.d.ts +243 -0
  27. package/dist/spatial-ribbon-chart/root/spatial-ribbon-chart-root.js +259 -0
  28. package/dist/spatial-scatter-chart/field.d.ts +8 -0
  29. package/dist/spatial-scatter-chart/root/spatial-scatter-chart-root.d.ts +14 -0
  30. package/dist/spatial-scatter-chart/root/spatial-scatter-chart-root.js +20 -6
  31. package/dist/spatial-scatter-chart/view.d.ts +14 -1
  32. package/dist/spatial-scatter-chart/view.js +29 -9
  33. package/dist/spatial-surface-chart/grid.d.ts +22 -0
  34. package/dist/spatial-surface-chart/grid.js +67 -1
  35. package/dist/spatial-surface-chart/index.d.ts +1 -1
  36. package/dist/spatial-surface-chart/index.js +1 -1
  37. package/dist/spatial-surface-chart/root/spatial-surface-chart-root.d.ts +20 -0
  38. package/dist/spatial-surface-chart/root/spatial-surface-chart-root.js +38 -5
  39. package/dist/spatial-treemap/blocks.d.ts +106 -0
  40. package/dist/spatial-treemap/blocks.js +215 -0
  41. package/dist/spatial-treemap/index.d.ts +2 -0
  42. package/dist/spatial-treemap/index.js +2 -0
  43. package/dist/spatial-treemap/root/spatial-treemap-root.d.ts +236 -0
  44. package/dist/spatial-treemap/root/spatial-treemap-root.js +249 -0
  45. package/dist/video/root/video-root.d.ts +14 -0
  46. package/dist/video/root/video-root.js +30 -2
  47. package/examples.json +1 -1
  48. package/lint.json +1 -1
  49. package/package.json +5 -5
  50. package/types/jsx.d.ts +3037 -99
  51. package/types/svelte.d.ts +4 -0
  52. package/types/vue.d.ts +519 -3
  53. package/types/words.d.ts +149 -0
@@ -115,6 +115,12 @@ export declare class HelixHeadlessCarousel extends HelixElement {
115
115
  static shadowRootOptions: ShadowRootInit;
116
116
  /**
117
117
  * Whether the carousel advances on its own.
118
+ *
119
+ * **Not where the reader asked for less motion**, or where the page has
120
+ * paused its motion, through `pauseMotion` or `Motion Toggle`: the carousel
121
+ * starts still, and its rotation control starts it. A page that pauses its
122
+ * motion later stops the rotation, and one that lets it run again starts it
123
+ * again, unless the reader stopped it.
118
124
  */
119
125
  autoplay: boolean;
120
126
  /**
@@ -741,6 +747,14 @@ export declare class HelixHeadlessCarousel extends HelixElement {
741
747
  disconnectedCallback(): void;
742
748
  /** Pauses without forgetting that the reader wanted it running. */
743
749
  private readonly pause;
750
+ /** What stops hearing the page's pause. */
751
+ private unpaused;
752
+ /**
753
+ * The page paused its motion, which pauses the rotation, or let it run
754
+ * again, which starts it again where it was asked for and the reader has
755
+ * not stopped it.
756
+ */
757
+ private readonly handlePageMotion;
744
758
  protected willUpdate(changed: PropertyValues): void;
745
759
  protected firstUpdated(changed: PropertyValues): void;
746
760
  protected updated(changed: PropertyValues): void;
@@ -1,5 +1,5 @@
1
1
  import { __decorate } from "tslib";
2
- import { ChildrenController, capturePointer, customElement, formatWords, HelixActivateEvent, HelixElement, HelixSelectEvent, hostAria, inlineStep, isRtl, MIRROR_RULE, requestChange, SCROLLER_RULE, words, } from '@fusion.dev/helix-core';
2
+ import { ChildrenController, capturePointer, customElement, formatWords, HelixActivateEvent, HelixElement, HelixSelectEvent, hostAria, inlineStep, isRtl, MIRROR_RULE, motionPaused, onMotionChange, reducedMotion, requestChange, SCROLLER_RULE, words, } from '@fusion.dev/helix-core';
3
3
  import { caretLeft, caretRight, play, stop } from '@fusion.dev/helix-icons';
4
4
  import { html, nothing } from 'lit';
5
5
  import { property, state } from 'lit/decorators.js';
@@ -186,6 +186,14 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
186
186
  this.rotating = false;
187
187
  }
188
188
  };
189
+ this.handlePageMotion = (paused) => {
190
+ if (paused) {
191
+ this.pause();
192
+ }
193
+ else if (this.autoplay && !this.stopped) {
194
+ this.rotating = true;
195
+ }
196
+ };
189
197
  }
190
198
  static { this.markRules = [SCROLLER_RULE, MIRROR_RULE]; }
191
199
  static { this.words = 'helix-carousel'; }
@@ -385,6 +393,7 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
385
393
  super.connectedCallback();
386
394
  this.addEventListener('focusin', this.pause);
387
395
  this.addEventListener('pointerenter', this.pause);
396
+ this.unpaused ??= onMotionChange(this.handlePageMotion);
388
397
  this.watchPane();
389
398
  if (this.hasUpdated) {
390
399
  this.runTimer();
@@ -398,6 +407,8 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
398
407
  }
399
408
  this.removeEventListener('focusin', this.pause);
400
409
  this.removeEventListener('pointerenter', this.pause);
410
+ this.unpaused?.();
411
+ this.unpaused = undefined;
401
412
  this.sizes?.disconnect();
402
413
  this.sizes = undefined;
403
414
  this.watched = undefined;
@@ -428,6 +439,9 @@ let HelixHeadlessCarousel = class HelixHeadlessCarousel extends HelixElement {
428
439
  super.firstUpdated(changed);
429
440
  this.watchPane();
430
441
  this.finishHydration();
442
+ if (this.rotating && (reducedMotion() || motionPaused())) {
443
+ this.rotating = false;
444
+ }
431
445
  }
432
446
  updated(changed) {
433
447
  super.updated(changed);
@@ -212,6 +212,7 @@ registerElementEvents({
212
212
  ],
213
213
  'helix-headless-menu-item': ['helix-before-toggle', 'helix-select'],
214
214
  'helix-headless-meter': ['helix-band'],
215
+ 'helix-headless-motion-toggle': ['change'],
215
216
  'helix-headless-notification': ['helix-dismiss', 'helix-read'],
216
217
  'helix-headless-notification-group': ['helix-before-close', 'helix-before-open', 'toggle'],
217
218
  'helix-headless-notifications': ['helix-dismiss', 'helix-load-more', 'helix-read'],
@@ -257,6 +258,12 @@ registerElementEvents({
257
258
  ],
258
259
  'helix-headless-slider': ['change', 'helix-before-change', 'input'],
259
260
  'helix-headless-sparkline': ['helix-reading-change', 'helix-reading-press'],
261
+ 'helix-headless-spatial-bar-chart': [
262
+ 'helix-plot-change',
263
+ 'helix-plot-turn',
264
+ 'helix-reading-change',
265
+ 'helix-reading-press',
266
+ ],
260
267
  'helix-headless-spatial-heatmap': [
261
268
  'change',
262
269
  'helix-before-change',
@@ -266,6 +273,12 @@ registerElementEvents({
266
273
  'helix-reading-press',
267
274
  'helix-resize',
268
275
  ],
276
+ 'helix-headless-spatial-ribbon-chart': [
277
+ 'helix-plot-change',
278
+ 'helix-plot-turn',
279
+ 'helix-reading-change',
280
+ 'helix-reading-press',
281
+ ],
269
282
  'helix-headless-spatial-scatter-chart': [
270
283
  'helix-plot-change',
271
284
  'helix-plot-turn',
@@ -278,6 +291,12 @@ registerElementEvents({
278
291
  'helix-reading-change',
279
292
  'helix-reading-press',
280
293
  ],
294
+ 'helix-headless-spatial-treemap': [
295
+ 'helix-plot-change',
296
+ 'helix-plot-turn',
297
+ 'helix-reading-change',
298
+ 'helix-reading-press',
299
+ ],
281
300
  'helix-headless-speed-dial': [
282
301
  'helix-before-close',
283
302
  'helix-before-open',
@@ -118,7 +118,9 @@ declare const HelixHeadlessGraph_base: (new (...args: any[]) => import("@fusion.
118
118
  * @csspart region - The frame: the drawing, its controls and the list.
119
119
  * @csspart search - The bar holding the icon, the search field and the count.
120
120
  * @csspart search-field - The field that finds a note by its name.
121
+ * @csspart settling - What is said while a layout runs unseen, where the reader asked for less motion or the page paused its motion, over the hidden drawing.
121
122
  * @csspart stage - The drawing and its controls.
123
+ * @csspart stop-layout - The button that stops a layout that has run past five seconds, where the notes stand.
122
124
  * @csspart viewport - The box the renderer draws in.
123
125
  * @csspart zoom-in - The zoom in button.
124
126
  * @csspart zoom-out - The zoom out button.
@@ -132,6 +134,8 @@ declare const HelixHeadlessGraph_base: (new (...args: any[]) => import("@fusion.
132
134
  * @cssstate ready - The graph has been drawn whole, after `helix-graph-ready`.
133
135
  * @cssstate searching - The search field holds words, and the list holds only the notes they find.
134
136
  * @cssstate selected - A note is selected, in the list or in the drawing.
137
+ * @cssstate settling - A layout is running.
138
+ * @cssstate settling-unseen - A layout is running with the drawing hidden, where the reader asked for less motion or the page paused its motion.
135
139
  * @cssstate sharing - The page keeps the plain wheel and one finger, with `gestures="cooperative"` or while a graph with `gestures="activate"` waits to be pressed.
136
140
  * @cssstate using - A graph with `gestures="activate"` has been pressed and has the gestures.
137
141
  */
@@ -253,6 +257,13 @@ export declare class HelixHeadlessGraph extends HelixHeadlessGraph_base {
253
257
  * the list then holds, written with `count-label`.
254
258
  */
255
259
  selectionLabel: string;
260
+ /**
261
+ * What the `settling` part says while a layout runs unseen, where the reader
262
+ * asked for less motion or the page paused its motion.
263
+ */
264
+ settlingLabel: string;
265
+ /** What the button that stops a long layout says. */
266
+ stopLayoutLabel: string;
256
267
  /** The hint shown when one finger swipes over the graph with `gestures="cooperative"`. */
257
268
  twoFingerHintLabel: string;
258
269
  /** The line shown while the graph, with `gestures="activate"`, has the gestures. */
@@ -287,6 +298,12 @@ export declare class HelixHeadlessGraph extends HelixHeadlessGraph_base {
287
298
  private fieldKeys;
288
299
  /** Whether the list has the focus, so the drawing marks the note its keys are on. */
289
300
  private listKeys;
301
+ /** The layout running, what stops it, and whether it is shown, while one runs. */
302
+ private layout;
303
+ /** Whether the layout has run long enough to offer the button that stops it. */
304
+ private stoppable;
305
+ /** What offers the stop once the layout has run five seconds. */
306
+ private stopping;
290
307
  /**
291
308
  * The notes a walk along the links stepped from, by `id`, the last the one
292
309
  * the note the keys are on was reached from. Empty when the keys moved some
@@ -374,6 +391,22 @@ export declare class HelixHeadlessGraph extends HelixHeadlessGraph_base {
374
391
  y: number;
375
392
  z?: number | undefined;
376
393
  }>): void;
394
+ /**
395
+ * Says a layout is running, with what ends it where the notes stand, or
396
+ * with nothing, that it has ended. Called by `connectGraph`.
397
+ *
398
+ * **A layout that runs past five seconds offers a button that stops it**,
399
+ * the `stop-layout` part, as WCAG 2.2.2 asks of motion that starts by itself
400
+ * and runs that long. **`unseen` hides the drawing while the notes move**,
401
+ * which the connector asks for where the reader asked for less motion or
402
+ * the page paused its motion: the `settling` part says the notes are being
403
+ * placed, and the drawing shows them where they settle.
404
+ */
405
+ settling(stop?: () => void, unseen?: boolean): void;
406
+ /** The stop button ends the layout where the notes stand. */
407
+ private readonly handleStopLayout;
408
+ connectedCallback(): void;
409
+ disconnectedCallback(): void;
377
410
  /**
378
411
  * Shows something passing from one note to another, such as a message, a
379
412
  * request or a payment, drawn traveling along the drawing and arriving with
@@ -6,6 +6,7 @@ import { property, state } from 'lit/decorators.js';
6
6
  import { repeat } from 'lit/directives/repeat.js';
7
7
  import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
8
8
  import { adjacencyOf, cursorAfter, entriesOf, groupsOf, nameOf, sortedOf, within, withoutGroups, } from './graph-model.js';
9
+ const LONG_LAYOUT = 5000;
9
10
  let HelixHeadlessGraph = class HelixHeadlessGraph extends MixinViewport(MixinControllerActive(HelixElement)) {
10
11
  constructor() {
11
12
  super(...arguments);
@@ -36,6 +37,8 @@ let HelixHeadlessGraph = class HelixHeadlessGraph extends MixinViewport(MixinCon
36
37
  this.searchLabel = 'Find a note';
37
38
  this.selectedLabel = 'Selected';
38
39
  this.selectionLabel = '{note}, selected. {count}';
40
+ this.settlingLabel = 'Placing the notes';
41
+ this.stopLayoutLabel = 'Stop the layout';
39
42
  this.twoFingerHintLabel = 'Use two fingers to move the graph';
40
43
  this.usingLabel = 'Using the graph. Press Escape to scroll the page again.';
41
44
  this.walkLabel = '{note}. {relation}. {index} of {count}';
@@ -47,6 +50,7 @@ let HelixHeadlessGraph = class HelixHeadlessGraph extends MixinViewport(MixinCon
47
50
  this.panelOpen = false;
48
51
  this.fieldKeys = false;
49
52
  this.listKeys = false;
53
+ this.stoppable = false;
50
54
  this.trail = [];
51
55
  this.adjacency = new Map();
52
56
  this.sorted = [];
@@ -73,6 +77,11 @@ let HelixHeadlessGraph = class HelixHeadlessGraph extends MixinViewport(MixinCon
73
77
  index: Number(element.dataset.index),
74
78
  })),
75
79
  });
80
+ this.handleStopLayout = () => {
81
+ const stop = this.layout?.stop;
82
+ this.settling();
83
+ stop?.();
84
+ };
76
85
  this.handleListKeydown = (event) => {
77
86
  if (this.walk(event)) {
78
87
  return;
@@ -190,6 +199,32 @@ let HelixHeadlessGraph = class HelixHeadlessGraph extends MixinViewport(MixinCon
190
199
  laidOut(positions) {
191
200
  this.dispatchEvent(new HelixGraphLayoutEvent({ positions }));
192
201
  }
202
+ settling(stop, unseen = false) {
203
+ clearTimeout(this.stopping);
204
+ this.stopping = undefined;
205
+ this.stoppable = false;
206
+ this.layout = stop ? { stop, unseen } : undefined;
207
+ this.toggleState('settling', stop !== undefined);
208
+ this.toggleState('settling-unseen', stop !== undefined && unseen);
209
+ if (stop) {
210
+ this.stopping = setTimeout(() => {
211
+ this.stoppable = true;
212
+ }, LONG_LAYOUT);
213
+ }
214
+ }
215
+ connectedCallback() {
216
+ super.connectedCallback();
217
+ if (this.layout && !this.stoppable && this.stopping === undefined) {
218
+ this.stopping = setTimeout(() => {
219
+ this.stoppable = true;
220
+ }, LONG_LAYOUT);
221
+ }
222
+ }
223
+ disconnectedCallback() {
224
+ super.disconnectedCallback();
225
+ clearTimeout(this.stopping);
226
+ this.stopping = undefined;
227
+ }
193
228
  signal(from, to, options = {}) {
194
229
  this.dispatchEvent(new HelixGraphSignalEvent({ from, to, tone: options.tone }));
195
230
  }
@@ -606,6 +641,14 @@ let HelixHeadlessGraph = class HelixHeadlessGraph extends MixinViewport(MixinCon
606
641
  <div part="viewport">
607
642
  <slot></slot>
608
643
  ${this.renderGestures()}
644
+ ${this.layout?.unseen
645
+ ? html `<div part="settling" role="status">${this.settlingLabel}</div>`
646
+ : nothing}
647
+ ${this.stoppable
648
+ ? html `<button part="stop-layout" type="button" @click=${this.handleStopLayout}>
649
+ ${this.stopLayoutLabel}
650
+ </button>`
651
+ : nothing}
609
652
  </div>
610
653
  ${this.renderControls()} ${this.renderLegend()}
611
654
  </div>
@@ -780,6 +823,14 @@ __decorate([
780
823
  words,
781
824
  property({ attribute: 'selection-label' })
782
825
  ], HelixHeadlessGraph.prototype, "selectionLabel", void 0);
826
+ __decorate([
827
+ words,
828
+ property({ attribute: 'settling-label' })
829
+ ], HelixHeadlessGraph.prototype, "settlingLabel", void 0);
830
+ __decorate([
831
+ words,
832
+ property({ attribute: 'stop-layout-label' })
833
+ ], HelixHeadlessGraph.prototype, "stopLayoutLabel", void 0);
783
834
  __decorate([
784
835
  words,
785
836
  property({ attribute: 'two-finger-hint-label' })
@@ -819,6 +870,12 @@ __decorate([
819
870
  __decorate([
820
871
  state()
821
872
  ], HelixHeadlessGraph.prototype, "listKeys", void 0);
873
+ __decorate([
874
+ state()
875
+ ], HelixHeadlessGraph.prototype, "layout", void 0);
876
+ __decorate([
877
+ state()
878
+ ], HelixHeadlessGraph.prototype, "stoppable", void 0);
822
879
  __decorate([
823
880
  state()
824
881
  ], HelixHeadlessGraph.prototype, "said", void 0);
package/dist/index.d.ts CHANGED
@@ -66,6 +66,7 @@ export * from './megamenu/index.js';
66
66
  export * from './menu/filter/index.js';
67
67
  export * from './menu/index.js';
68
68
  export * from './meter/index.js';
69
+ export * from './motion-toggle/index.js';
69
70
  export * from './navigation/index.js';
70
71
  export * from './notifications/index.js';
71
72
  export * from './number-input/index.js';
@@ -87,9 +88,12 @@ export * from './skeleton/index.js';
87
88
  export * from './skip-link/index.js';
88
89
  export * from './slider/index.js';
89
90
  export * from './sparkline/index.js';
91
+ export * from './spatial-bar-chart/index.js';
90
92
  export * from './spatial-heatmap/index.js';
93
+ export * from './spatial-ribbon-chart/index.js';
91
94
  export * from './spatial-scatter-chart/index.js';
92
95
  export * from './spatial-surface-chart/index.js';
96
+ export * from './spatial-treemap/index.js';
93
97
  export * from './speed-dial/index.js';
94
98
  export * from './splitter/index.js';
95
99
  export * from './stat/index.js';
package/dist/index.js CHANGED
@@ -57,6 +57,7 @@ export * from './megamenu/index.js';
57
57
  export * from './menu/filter/index.js';
58
58
  export * from './menu/index.js';
59
59
  export * from './meter/index.js';
60
+ export * from './motion-toggle/index.js';
60
61
  export * from './navigation/index.js';
61
62
  export * from './notifications/index.js';
62
63
  export * from './number-input/index.js';
@@ -78,9 +79,12 @@ export * from './skeleton/index.js';
78
79
  export * from './skip-link/index.js';
79
80
  export * from './slider/index.js';
80
81
  export * from './sparkline/index.js';
82
+ export * from './spatial-bar-chart/index.js';
81
83
  export * from './spatial-heatmap/index.js';
84
+ export * from './spatial-ribbon-chart/index.js';
82
85
  export * from './spatial-scatter-chart/index.js';
83
86
  export * from './spatial-surface-chart/index.js';
87
+ export * from './spatial-treemap/index.js';
84
88
  export * from './speed-dial/index.js';
85
89
  export * from './splitter/index.js';
86
90
  export * from './stat/index.js';
@@ -13,9 +13,11 @@ import { nothing, type PropertyValues } from 'lit';
13
13
  * While `playing` is set, the layer shows the frames in order, `interval`
14
14
  * milliseconds each, and starts again from the first.
15
15
  *
16
- * The frames do not play while the user has asked for reduced motion, and the
17
- * layer shows its last frame. Content that moves for more than five seconds
18
- * needs a way to pause it, so give `playing` a control on your page.
16
+ * The frames do not play while the user has asked for reduced motion, or
17
+ * while the page has paused its motion, through `pauseMotion` or `Motion
18
+ * Toggle`, and the layer holds the frame it shows. Content that moves for more
19
+ * than five seconds needs a way to pause it, which `Motion Toggle` gives the
20
+ * whole page, or give `playing` a control of its own.
19
21
  *
20
22
  * Put the tile provider's credit in `attribution`. The frame shows it beside
21
23
  * the map's own credit.
@@ -79,6 +81,8 @@ export declare class HelixHeadlessMapRaster extends HelixElement {
79
81
  private timer;
80
82
  connectedCallback(): void;
81
83
  disconnectedCallback(): void;
84
+ /** What stops hearing the page's pause, which stops the frames and starts them again. */
85
+ private unpaused;
82
86
  private stopTimer;
83
87
  /** Starts or stops the timer when the frames, the interval, or `playing` change. */
84
88
  private schedule;
@@ -1,5 +1,5 @@
1
1
  import { __decorate } from "tslib";
2
- import { customElement, HelixElement, HelixMapPartChangeEvent, words } from '@fusion.dev/helix-core';
2
+ import { customElement, HelixElement, HelixMapPartChangeEvent, motionPaused, onMotionChange, reducedMotion, words, } from '@fusion.dev/helix-core';
3
3
  import { nothing } from 'lit';
4
4
  import { property } from 'lit/decorators.js';
5
5
  const framesConverter = {
@@ -20,12 +20,15 @@ let HelixHeadlessMapRaster = class HelixHeadlessMapRaster extends HelixElement {
20
20
  static { this.words = 'helix-map-raster'; }
21
21
  connectedCallback() {
22
22
  super.connectedCallback();
23
+ this.unpaused ??= onMotionChange(() => this.schedule());
23
24
  if (this.hasUpdated) {
24
25
  this.schedule();
25
26
  }
26
27
  }
27
28
  disconnectedCallback() {
28
29
  super.disconnectedCallback();
30
+ this.unpaused?.();
31
+ this.unpaused = undefined;
29
32
  this.stopTimer();
30
33
  }
31
34
  stopTimer() {
@@ -37,8 +40,7 @@ let HelixHeadlessMapRaster = class HelixHeadlessMapRaster extends HelixElement {
37
40
  schedule() {
38
41
  this.stopTimer();
39
42
  const count = this.frames?.length ?? 0;
40
- const reduced = globalThis.matchMedia?.('(prefers-reduced-motion: reduce)').matches ?? false;
41
- if (!this.playing || count < 2 || reduced) {
43
+ if (!this.playing || count < 2 || reducedMotion() || motionPaused()) {
42
44
  return;
43
45
  }
44
46
  this.timer = setInterval(() => {
@@ -0,0 +1 @@
1
+ export { HelixHeadlessMotionToggle } from './root/motion-toggle-root.js';
@@ -0,0 +1 @@
1
+ export { HelixHeadlessMotionToggle } from './root/motion-toggle-root.js';
@@ -0,0 +1,77 @@
1
+ import { HelixElement } from '@fusion.dev/helix-core';
2
+ import { type TemplateResult } from 'lit';
3
+ /**
4
+ * @summary A button that pauses every motion on the page that starts by itself, and lets it run again.
5
+ *
6
+ * @description
7
+ * WCAG 2.2.2 asks that motion which starts by itself and runs longer than
8
+ * five seconds can be paused. **This is one control for the whole page**: a
9
+ * press pauses every Helix component that moves by itself, and another lets
10
+ * them run again.
11
+ *
12
+ * - `Carousel` stops rotating under `autoplay`.
13
+ * - `Video` pauses a video that started itself under `autoplay`.
14
+ * - `Aura`, `Orb`, and `Strands` hold the frame they show.
15
+ * - A graph that `connectGraph` lays out stops where its notes stand, and its
16
+ * three.js view stops turning.
17
+ * - A map layer's `frames` stop on the frame they show.
18
+ *
19
+ * **The choice is the page's**, held on the document element as
20
+ * `data-helix-motion="paused"`, so two of these on one page agree, and a page
21
+ * can set it in its markup or with `pauseMotion` from
22
+ * `@fusion.dev/helix-core`. A stylesheet can select on the attribute to
23
+ * pause motion of its own.
24
+ *
25
+ * It is a toggle button: a `<button>` that says whether motion is paused with
26
+ * `aria-pressed`. It keeps one name, `label`, whichever way it stands, as a
27
+ * toggle button does, and its icon shows what a press does next.
28
+ *
29
+ * Put it where a reader looks first, such as the page's header. Where the
30
+ * reader asked for reduced motion, nothing moves by itself to begin with.
31
+ *
32
+ * @since 0.199.0
33
+ * @status experimental
34
+ * @category actions
35
+ * @aliases Pause button, Pause animations, Stop motion, Motion control, Animation toggle
36
+ * @documentation https://fusion.dev/helix/components/motion-toggle
37
+ *
38
+ * @slot - The button's text, where it shows more than its icon. Defaults to nothing, the icon alone, named by `label`.
39
+ * @slot icon - Replaces the icon, which is pause while motion runs and play while it is paused.
40
+ *
41
+ * @event {Event} change - Fired after a press pauses the page's motion or lets it run.
42
+ *
43
+ * @csspart button - The toggle button, with `aria-pressed` while the page's motion is paused.
44
+ * @csspart icon - The icon inside the button.
45
+ *
46
+ * @cssstate paused - The page's motion is paused.
47
+ */
48
+ export declare class HelixHeadlessMotionToggle extends HelixElement {
49
+ /**
50
+ * The key this family's words are registered under, its styled tag:
51
+ * `registerWords('de', { 'helix-motion-toggle': { label: 'Bewegung anhalten' } })`.
52
+ */
53
+ static words: string;
54
+ /** The button's name, which stays the same whichever way it stands. */
55
+ label: string;
56
+ /** Whether the page's motion is paused, kept with the document element's attribute. */
57
+ private paused;
58
+ /** What stops hearing the page's choice. */
59
+ private unpaused;
60
+ connectedCallback(): void;
61
+ disconnectedCallback(): void;
62
+ protected updated(): void;
63
+ /** Pauses the page's motion, or lets it run again. */
64
+ private readonly toggle;
65
+ protected render(): TemplateResult;
66
+ /**
67
+ * The icon the `icon` slot falls back to: pause while motion runs, and play
68
+ * while it is paused, which is what a press does next. A tier above draws
69
+ * its own here.
70
+ */
71
+ protected renderIcon(): TemplateResult;
72
+ }
73
+ declare global {
74
+ interface HTMLElementTagNameMap {
75
+ 'helix-headless-motion-toggle': HelixHeadlessMotionToggle;
76
+ }
77
+ }
@@ -0,0 +1,56 @@
1
+ import { __decorate } from "tslib";
2
+ import { customElement, drawIcon, HelixElement, motionPaused, onMotionChange, pauseMotion, words, } from '@fusion.dev/helix-core';
3
+ import { pause, play } from '@fusion.dev/helix-icons';
4
+ import { html } from 'lit';
5
+ import { property, state } from 'lit/decorators.js';
6
+ let HelixHeadlessMotionToggle = class HelixHeadlessMotionToggle extends HelixElement {
7
+ constructor() {
8
+ super(...arguments);
9
+ this.label = 'Pause motion';
10
+ this.paused = false;
11
+ this.toggle = () => {
12
+ pauseMotion(!motionPaused());
13
+ this.paused = motionPaused();
14
+ this.dispatchEvent(new Event('change', { bubbles: true }));
15
+ };
16
+ }
17
+ static { this.words = 'helix-motion-toggle'; }
18
+ connectedCallback() {
19
+ super.connectedCallback();
20
+ this.paused = motionPaused();
21
+ this.unpaused ??= onMotionChange((paused) => {
22
+ this.paused = paused;
23
+ });
24
+ }
25
+ disconnectedCallback() {
26
+ super.disconnectedCallback();
27
+ this.unpaused?.();
28
+ this.unpaused = undefined;
29
+ }
30
+ updated() {
31
+ this.toggleState('paused', this.paused);
32
+ }
33
+ render() {
34
+ return html `<button
35
+ aria-label=${this.label}
36
+ aria-pressed=${this.paused ? 'true' : 'false'}
37
+ part="button"
38
+ type="button"
39
+ @click=${this.toggle}
40
+ ><span part="icon">${this.renderIcon()}</span><slot></slot></button>`;
41
+ }
42
+ renderIcon() {
43
+ return html `<slot name="icon">${drawIcon(this.paused ? play : pause)}</slot>`;
44
+ }
45
+ };
46
+ __decorate([
47
+ words,
48
+ property()
49
+ ], HelixHeadlessMotionToggle.prototype, "label", void 0);
50
+ __decorate([
51
+ state()
52
+ ], HelixHeadlessMotionToggle.prototype, "paused", void 0);
53
+ HelixHeadlessMotionToggle = __decorate([
54
+ customElement('helix-headless-motion-toggle')
55
+ ], HelixHeadlessMotionToggle);
56
+ export { HelixHeadlessMotionToggle };
@@ -0,0 +1,79 @@
1
+ /**
2
+ * A 3D bar chart's table read as bars on a grid of names.
3
+ *
4
+ * Kept apart from the element because it is arithmetic a picture hides: a
5
+ * value put under the wrong name stands as tall as a real one, and a bar
6
+ * measured from anywhere but zero says one value is a multiple of another
7
+ * when it is not.
8
+ */
9
+ import { type Drawn, type PagePoint, type Projection } from '@fusion.dev/helix-core';
10
+ import { type FieldRow, type PlotAxis } from '../spatial-scatter-chart/field.js';
11
+ import { type BoxExtent } from '../spatial-scatter-chart/view.js';
12
+ /** One place on the grid: a category across, a series in depth, and its value, where the table has one. */
13
+ export interface BarCell {
14
+ /** Which category it stands under, counting from zero: the row's place among the rows read. */
15
+ across: number;
16
+ /** The column of the row the value was read from, which names its series. */
17
+ column: number;
18
+ /** Which series it stands in, counting from zero. */
19
+ depth: number;
20
+ /** The row's own label, which names its category. */
21
+ label: string;
22
+ /** The row it was read from, counting from zero. */
23
+ row: number;
24
+ /** The value, where the cell holds a number. */
25
+ value?: number | undefined;
26
+ }
27
+ /** The table as a 3D bar chart reads it. */
28
+ export interface BarField {
29
+ /**
30
+ * Across, the categories; up, the values; and in depth, the series. The
31
+ * box is as long along the floor as the grid, so every place on it is
32
+ * square, and one series is a long, low row of bars.
33
+ */
34
+ axes: {
35
+ extent: BoxExtent;
36
+ x: PlotAxis;
37
+ y: PlotAxis;
38
+ z: PlotAxis;
39
+ };
40
+ /** Every category, in the table's order: each row's label. */
41
+ categories: string[];
42
+ /**
43
+ * Every place on the grid, across first: the bar for category `across` in
44
+ * series `depth` is at `across * series.length + depth`.
45
+ */
46
+ cells: BarCell[];
47
+ /** How many cells hold a value. */
48
+ readings: number;
49
+ /** Every series, in the table's order: each numeric column's heading. */
50
+ series: string[];
51
+ }
52
+ /**
53
+ * Reads the table as a bar chart does: **each row is a category, named by
54
+ * its label, and each numeric column a series**, named by its heading. The
55
+ * bars stand on a grid of categories across and series in depth, so one
56
+ * series is a single row of bars. A cell with no number leaves its place on
57
+ * the grid empty.
58
+ *
59
+ * **The value axis runs from zero**, whatever the values, so a bar's height
60
+ * is its value: one twice as tall is twice as much. It widens to round
61
+ * numbers past the highest value, and below zero where a value is negative.
62
+ */
63
+ export declare const barsOf: (rows: readonly FieldRow[], columns: readonly number[]) => BarField;
64
+ /** A bar as the chart's own drawing paints it. */
65
+ export interface DrawnBar {
66
+ /** The block's eight corners on the page, which a drawing with no box is framed around. */
67
+ corners: PagePoint[];
68
+ drawn: Drawn;
69
+ height: number;
70
+ index: number;
71
+ x: number;
72
+ y: number;
73
+ }
74
+ /**
75
+ * Each bar with a value as a block, seen through `projection`: standing on
76
+ * its place on the grid, from zero to its value, a little narrower than its
77
+ * place so neighbors stand apart. A value below zero hangs from zero.
78
+ */
79
+ export declare const barsDrawn: (field: BarField, projection: Projection) => DrawnBar[];