@fusion.dev/helix-headless 0.150.0 → 0.152.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 (75) hide show
  1. package/custom-elements.json +1388 -93
  2. package/dist/app-header/root/app-header-root.d.ts +75 -0
  3. package/dist/app-header/root/app-header-root.js +140 -5
  4. package/dist/area-chart/root/area-chart-root.d.ts +36 -0
  5. package/dist/area-chart/root/area-chart-root.js +17 -2
  6. package/dist/bar-chart/root/bar-chart-root.d.ts +28 -4
  7. package/dist/bar-chart/root/bar-chart-root.js +17 -4
  8. package/dist/breadcrumbs/breadcrumb/breadcrumb.d.ts +11 -2
  9. package/dist/breadcrumbs/breadcrumb/breadcrumb.js +11 -2
  10. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +14 -2
  11. package/dist/breadcrumbs/root/breadcrumbs-root.js +16 -3
  12. package/dist/callout/root/callout-root.d.ts +20 -0
  13. package/dist/callout/root/callout-root.js +19 -4
  14. package/dist/chat/composer/chat-composer.d.ts +30 -0
  15. package/dist/chat/composer/chat-composer.js +47 -2
  16. package/dist/chat/root/chat-root.d.ts +10 -0
  17. package/dist/chat/root/chat-root.js +14 -0
  18. package/dist/data-table/root/data-table-root.d.ts +34 -2
  19. package/dist/data-table/root/data-table-root.js +63 -2
  20. package/dist/dialog/description.d.ts +10 -0
  21. package/dist/dialog/description.js +22 -8
  22. package/dist/dialog/root/dialog-root.d.ts +17 -1
  23. package/dist/dialog/root/dialog-root.js +2 -2
  24. package/dist/divider/root/divider-root.d.ts +8 -6
  25. package/dist/divider/root/divider-root.js +10 -2
  26. package/dist/drawer/root/drawer-root.d.ts +38 -2
  27. package/dist/drawer/root/drawer-root.js +33 -6
  28. package/dist/element-events.generated.js +4 -0
  29. package/dist/kbd/root/kbd-root.d.ts +7 -1
  30. package/dist/kbd/root/kbd-root.js +8 -4
  31. package/dist/lightbox/root/lightbox-root.d.ts +44 -6
  32. package/dist/lightbox/root/lightbox-root.js +59 -11
  33. package/dist/lightbox/zoom-controller.d.ts +5 -0
  34. package/dist/lightbox/zoom-controller.js +8 -0
  35. package/dist/line-chart/root/line-chart-root.d.ts +36 -0
  36. package/dist/line-chart/root/line-chart-root.js +15 -2
  37. package/dist/menu/root/menu-root.js +2 -0
  38. package/dist/navigation/root/navigation-root.d.ts +11 -1
  39. package/dist/navigation/root/navigation-root.js +10 -5
  40. package/dist/notifications/root/notifications-root.d.ts +17 -0
  41. package/dist/notifications/root/notifications-root.js +21 -1
  42. package/dist/popover/root/popover-root.d.ts +18 -7
  43. package/dist/popover/root/popover-root.js +8 -1
  44. package/dist/scatter-chart/root/scatter-chart-root.d.ts +18 -0
  45. package/dist/scatter-chart/root/scatter-chart-root.js +38 -15
  46. package/dist/splitter/root/splitter-root.d.ts +41 -0
  47. package/dist/splitter/root/splitter-root.js +77 -0
  48. package/dist/table/column/table-column.js +3 -2
  49. package/dist/table/grid-controller.d.ts +14 -2
  50. package/dist/table/grid-controller.js +27 -1
  51. package/dist/table/root/table-root.d.ts +30 -4
  52. package/dist/table/root/table-root.js +45 -7
  53. package/dist/table/row/table-row.d.ts +12 -2
  54. package/dist/table/row/table-row.js +8 -2
  55. package/dist/tabs/root/tabs-root.d.ts +25 -0
  56. package/dist/tabs/root/tabs-root.js +28 -1
  57. package/dist/tour/naming.d.ts +9 -3
  58. package/dist/tour/naming.js +9 -3
  59. package/dist/tour/root/tour-root.d.ts +29 -5
  60. package/dist/tour/root/tour-root.js +13 -1
  61. package/dist/tour/stop/tour-stop.d.ts +2 -1
  62. package/dist/tour/stop/tour-stop.js +4 -1
  63. package/dist/tree/root/tree-root.d.ts +13 -0
  64. package/dist/tree/root/tree-root.js +42 -2
  65. package/dist/video/root/video-root.d.ts +6 -0
  66. package/dist/video/root/video-root.js +5 -1
  67. package/dist/video-playlist/root/video-playlist-root.d.ts +4 -0
  68. package/dist/video-playlist/root/video-playlist-root.js +4 -1
  69. package/dist/virtual-list/root/virtual-list-root.d.ts +23 -1
  70. package/dist/virtual-list/root/virtual-list-root.js +21 -2
  71. package/lint.json +1 -1
  72. package/package.json +5 -5
  73. package/types/jsx.d.ts +1098 -128
  74. package/types/vue.d.ts +248 -59
  75. package/types/words.d.ts +12 -2
@@ -19,6 +19,20 @@ import { type PropertyValues, type TemplateResult } from 'lit';
19
19
  * The page is the window unless `scroller` names the element that scrolls, for
20
20
  * an app shell whose content scrolls inside a container.
21
21
  *
22
+ * **Set `hide-on-scroll` for a bar that moves out of the way.** Scrolling down
23
+ * past the bar's own height hides it, and scrolling up shows it again, as does
24
+ * focus moving into it. The custom state `hidden` is on the host while it is
25
+ * hidden, and the styled tier slides a `sticky` bar up out of view.
26
+ *
27
+ * **Set `fold-below` to fold the middle region on a narrow screen.** Below that
28
+ * many pixels of the bar's own width, the middle region is hidden behind a
29
+ * toggle at the end of the bar, named by `menu-label`. The toggle says whether
30
+ * the region is open with `aria-expanded`, and the region opens below the row.
31
+ * Escape closes it and puts focus back on the toggle. The custom state
32
+ * `folded` is on the host while the bar is that narrow, and `open` while the
33
+ * folded region is open. A server serves the bar unfolded, and the browser
34
+ * folds it once it has measured it.
35
+ *
22
36
  * The host is the page's `banner` landmark. Placed inside `main`, `article`,
23
37
  * `aside`, `nav` or `section`, it is a plain box instead, as a native
24
38
  * `<header>` is there, so a header inside a card or a pane does not add a
@@ -36,6 +50,7 @@ import { type PropertyValues, type TemplateResult } from 'lit';
36
50
  *
37
51
  * @slot - The middle region, usually a `Navigation`.
38
52
  * @slot end - Trailing content.
53
+ * @slot menu-icon - Replaces the glyph inside the toggle that opens a folded middle region.
39
54
  * @slot start - Leading content, usually a wordmark.
40
55
  *
41
56
  * @event {HelixScrolledEvent} helix-scrolled - Fired when the page crosses the
@@ -48,10 +63,20 @@ import { type PropertyValues, type TemplateResult } from 'lit';
48
63
  * @csspart end - The trailing region.
49
64
  * @csspart row - The centered row inside the banner.
50
65
  * @csspart start - The leading region.
66
+ * @csspart toggle - The control that opens and closes the folded middle region, drawn while the bar is narrower than `fold-below`.
67
+ * @csspart toggle-mark - The glyph on the toggle, where none is slotted.
51
68
  *
69
+ * @cssstate folded - While the bar is narrower than `fold-below`, and the middle region is behind the toggle.
70
+ * @cssstate hidden - While `hide-on-scroll` has the bar out of the way, after a scroll down.
71
+ * @cssstate open - While the folded middle region is open.
52
72
  * @cssstate scrolled - While the page is scrolled down from the top, which is when the bar lifts off it.
53
73
  */
54
74
  export declare class HelixHeadlessAppHeader extends HelixElement {
75
+ /**
76
+ * The key this family's words are registered under, its styled tag:
77
+ * `registerWords('de', { 'helix-app-header': { menuLabel: 'Menü' } })`.
78
+ */
79
+ static words: string;
55
80
  /**
56
81
  * Where the middle region sits in the bar.
57
82
  *
@@ -68,6 +93,47 @@ export declare class HelixHeadlessAppHeader extends HelixElement {
68
93
  * the property carries the part's name, `content`.
69
94
  */
70
95
  contentAlign?: 'start';
96
+ /**
97
+ * The width of the bar, in pixels, below which the middle region folds behind
98
+ * a toggle. Unset, it never folds.
99
+ */
100
+ foldBelow?: number;
101
+ /**
102
+ * Hides the bar as the page scrolls down past it, and shows it again as the
103
+ * page scrolls up.
104
+ */
105
+ hideOnScroll: boolean;
106
+ /** Whether the bar is narrower than `fold-below`, which only the observer sets. */
107
+ private folded;
108
+ /** Whether the folded middle region is open. */
109
+ private unfolded;
110
+ /** Watches the bar's own width while `fold-below` is set. */
111
+ private widths;
112
+ /** Starts or stops watching the width, according to `fold-below`. */
113
+ private watchWidth;
114
+ /** Folds the middle region where the bar is narrower than `fold-below`. */
115
+ private fold;
116
+ /** Opens or closes the folded middle region. */
117
+ private readonly handleToggle;
118
+ /** Escape in the open region closes it and puts focus back on the toggle. */
119
+ private readonly handleKeydown;
120
+ /** Whether the bar is out of the way, under `hide-on-scroll`. */
121
+ private away;
122
+ /** Where the page was last scrolled to, to tell down from up. */
123
+ private lastY;
124
+ /** Shows a hidden bar again when focus moves into it. */
125
+ private readonly handleFocusIn;
126
+ /** Hides the bar or shows it again, under `hide-on-scroll`. */
127
+ private hide;
128
+ /**
129
+ * Hides the bar on a scroll down past its own height, and shows it on a
130
+ * scroll up. A few pixels either way are not a direction, so a bar does not
131
+ * flicker under a hand resting on a trackpad. Not while focus is in the bar
132
+ * or its folded region is open, which a reader is using.
133
+ */
134
+ private follow;
135
+ /** The name of the toggle that opens a folded middle region. */
136
+ menuLabel: string;
71
137
  /**
72
138
  * The id of the element whose scrolling lifts the bar, for an app shell that
73
139
  * scrolls a container instead of the window. Unset is the window.
@@ -111,7 +177,16 @@ export declare class HelixHeadlessAppHeader extends HelixElement {
111
177
  /** Whether this header is the page's banner, read from where it sits. */
112
178
  private readLandmark;
113
179
  disconnectedCallback(): void;
180
+ /** The `fold-below` the width watch was started for, so a change is measured at once. */
181
+ private watchedFor;
182
+ protected firstUpdated(changed: PropertyValues): void;
114
183
  protected updated(changed: PropertyValues): void;
184
+ /**
185
+ * The glyph on the toggle that opens a folded middle region, behind a seam:
186
+ * slot fallback, so a consumer replaces it by slotting, and a method, so a
187
+ * tier above replaces it by overriding.
188
+ */
189
+ protected renderMenuIcon(): TemplateResult;
115
190
  protected render(): TemplateResult;
116
191
  }
117
192
  declare global {
@@ -4,18 +4,41 @@ 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, elementById, HelixElement, HelixScrolledEvent, hostAria, } from '@fusion.dev/helix-core';
8
- import { html } from 'lit';
9
- import { property } from 'lit/decorators.js';
7
+ import { customElement, deepActiveElement, deepContains, elementById, HelixElement, HelixScrolledEvent, hostAria, words, } from '@fusion.dev/helix-core';
8
+ import { list } from '@fusion.dev/helix-icons';
9
+ import { html, nothing } from 'lit';
10
+ import { property, state } from 'lit/decorators.js';
11
+ import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
10
12
  import { inSectioningContent } from '../sectioning.js';
11
13
  let HelixHeadlessAppHeader = class HelixHeadlessAppHeader extends HelixElement {
12
14
  constructor() {
13
15
  super(...arguments);
16
+ this.hideOnScroll = false;
17
+ this.folded = false;
18
+ this.unfolded = false;
19
+ this.handleToggle = () => {
20
+ this.unfolded = !this.unfolded;
21
+ };
22
+ this.handleKeydown = (event) => {
23
+ if (event.key !== 'Escape' || !this.unfolded || event.defaultPrevented) {
24
+ return;
25
+ }
26
+ event.preventDefault();
27
+ this.unfolded = false;
28
+ void this.updateComplete.then(() => this.partNamed('toggle')?.focus());
29
+ };
30
+ this.away = false;
31
+ this.lastY = 0;
32
+ this.handleFocusIn = () => {
33
+ this.hide(false);
34
+ };
35
+ this.menuLabel = 'Menu';
14
36
  this.landmark = 'banner';
15
37
  this.scrolled = false;
16
38
  this.handleScroll = () => {
17
39
  const source = this.source ?? window;
18
40
  const y = source instanceof Element ? source.scrollTop : window.scrollY;
41
+ this.follow(y);
19
42
  const next = this.scrolled ? y > 2 : y > 8;
20
43
  if (next === this.scrolled) {
21
44
  return;
@@ -25,6 +48,57 @@ let HelixHeadlessAppHeader = class HelixHeadlessAppHeader extends HelixElement {
25
48
  this.dispatchEvent(new HelixScrolledEvent({ scrolled: next }));
26
49
  };
27
50
  }
51
+ static { this.words = 'helix-app-header'; }
52
+ watchWidth() {
53
+ if (!this.foldBelow || typeof ResizeObserver === 'undefined') {
54
+ this.widths?.disconnect();
55
+ this.widths = undefined;
56
+ this.folded = false;
57
+ return;
58
+ }
59
+ if (this.widths) {
60
+ return;
61
+ }
62
+ this.widths = new ResizeObserver(([entry]) => {
63
+ const width = entry?.borderBoxSize?.[0]?.inlineSize ?? entry?.contentRect.width ?? 0;
64
+ if (width > 0) {
65
+ this.fold(width);
66
+ }
67
+ });
68
+ this.widths.observe(this);
69
+ }
70
+ fold(width) {
71
+ const folded = this.foldBelow !== undefined && width < this.foldBelow;
72
+ if (folded !== this.folded) {
73
+ this.folded = folded;
74
+ this.unfolded = false;
75
+ }
76
+ }
77
+ hide(away) {
78
+ if (away === this.away) {
79
+ return;
80
+ }
81
+ this.away = away;
82
+ this.toggleState('hidden', away);
83
+ }
84
+ follow(y) {
85
+ const moved = y - this.lastY;
86
+ this.lastY = y;
87
+ if (!this.hideOnScroll) {
88
+ this.hide(false);
89
+ return;
90
+ }
91
+ const height = this.getBoundingClientRect().height;
92
+ if (y <= height) {
93
+ this.hide(false);
94
+ }
95
+ else if (moved > 4 && !this.unfolded && !deepContains(this, deepActiveElement())) {
96
+ this.hide(true);
97
+ }
98
+ else if (moved < -4) {
99
+ this.hide(false);
100
+ }
101
+ }
28
102
  findSource() {
29
103
  return (this.scroller ? elementById(this, this.scroller) : null) ?? window;
30
104
  }
@@ -44,6 +118,8 @@ let HelixHeadlessAppHeader = class HelixHeadlessAppHeader extends HelixElement {
44
118
  willUpdate(changed) {
45
119
  super.willUpdate(changed);
46
120
  hostAria(this.internals, { role: this.landmark });
121
+ this.toggleState('folded', this.folded);
122
+ this.toggleState('open', this.folded && this.unfolded);
47
123
  }
48
124
  connectedCallback() {
49
125
  super.connectedCallback();
@@ -54,6 +130,11 @@ let HelixHeadlessAppHeader = class HelixHeadlessAppHeader extends HelixElement {
54
130
  }
55
131
  });
56
132
  this.listen();
133
+ this.addEventListener('focusin', this.handleFocusIn);
134
+ this.addEventListener('keydown', this.handleKeydown);
135
+ if (this.hasUpdated) {
136
+ this.watchWidth();
137
+ }
57
138
  }
58
139
  readLandmark() {
59
140
  this.landmark = inSectioningContent(this) ? '' : 'banner';
@@ -63,19 +144,57 @@ let HelixHeadlessAppHeader = class HelixHeadlessAppHeader extends HelixElement {
63
144
  super.disconnectedCallback();
64
145
  this.source?.removeEventListener('scroll', this.handleScroll);
65
146
  this.source = undefined;
147
+ this.removeEventListener('focusin', this.handleFocusIn);
148
+ this.removeEventListener('keydown', this.handleKeydown);
149
+ this.widths?.disconnect();
150
+ this.widths = undefined;
151
+ }
152
+ firstUpdated(changed) {
153
+ super.firstUpdated(changed);
154
+ this.watchedFor = this.foldBelow;
155
+ this.watchWidth();
66
156
  }
67
157
  updated(changed) {
68
158
  super.updated(changed);
69
159
  if (changed.has('scroller') && this.isConnected) {
70
160
  this.listen();
71
161
  }
162
+ if (this.foldBelow !== this.watchedFor) {
163
+ this.watchedFor = this.foldBelow;
164
+ this.watchWidth();
165
+ const width = this.getBoundingClientRect().width;
166
+ if (this.widths && width > 0) {
167
+ this.fold(width);
168
+ }
169
+ }
170
+ if (changed.has('hideOnScroll') && !this.hideOnScroll) {
171
+ this.hide(false);
172
+ }
173
+ }
174
+ renderMenuIcon() {
175
+ return html `<slot name="menu-icon">
176
+ <svg part="toggle-mark" fill="currentColor" viewBox=${list.viewBox} aria-hidden="true">
177
+ ${unsafeSVG(list.content)}
178
+ </svg>
179
+ </slot>`;
72
180
  }
73
181
  render() {
74
182
  return html `
75
183
  <div part="row">
76
184
  <div part="start"><slot name="start"></slot></div>
77
- <div part="content"><slot></slot></div>
78
- <div part="end"><slot name="end"></slot></div>
185
+ <div part="content" id="content" ?hidden=${this.folded && !this.unfolded}><slot></slot></div>
186
+ <div part="end"><slot name="end"></slot>${this.folded
187
+ ? html `<button
188
+ part="toggle"
189
+ type="button"
190
+ aria-controls="content"
191
+ aria-expanded=${this.unfolded ? 'true' : 'false'}
192
+ aria-label=${this.menuLabel}
193
+ @click=${this.handleToggle}
194
+ >
195
+ ${this.renderMenuIcon()}
196
+ </button>`
197
+ : nothing}</div>
79
198
  </div>
80
199
  `;
81
200
  }
@@ -83,6 +202,22 @@ let HelixHeadlessAppHeader = class HelixHeadlessAppHeader extends HelixElement {
83
202
  __decorate([
84
203
  property({ attribute: 'content-align', reflect: true })
85
204
  ], HelixHeadlessAppHeader.prototype, "contentAlign", void 0);
205
+ __decorate([
206
+ property({ attribute: 'fold-below', type: Number })
207
+ ], HelixHeadlessAppHeader.prototype, "foldBelow", void 0);
208
+ __decorate([
209
+ property({ attribute: 'hide-on-scroll', type: Boolean })
210
+ ], HelixHeadlessAppHeader.prototype, "hideOnScroll", void 0);
211
+ __decorate([
212
+ state()
213
+ ], HelixHeadlessAppHeader.prototype, "folded", void 0);
214
+ __decorate([
215
+ state()
216
+ ], HelixHeadlessAppHeader.prototype, "unfolded", void 0);
217
+ __decorate([
218
+ words,
219
+ property({ attribute: 'menu-label' })
220
+ ], HelixHeadlessAppHeader.prototype, "menuLabel", void 0);
86
221
  __decorate([
87
222
  property()
88
223
  ], HelixHeadlessAppHeader.prototype, "scroller", void 0);
@@ -52,6 +52,9 @@ declare const HelixHeadlessAreaChart_base: typeof HelixElement & (new (...args:
52
52
  * compare the parts with each other; the scale then fits the largest part
53
53
  * rather than the total.
54
54
  *
55
+ * `value-axis` draws the scale's numbers beside the plot, at the levels
56
+ * `gridlines` rules: amounts in `locale`, or percentages under `share`.
57
+ *
55
58
  * The drawing is hidden from screen readers. The text summary gives the latest
56
59
  * reading's total and each part's share of it, and the table beside the chart
57
60
  * has every number.
@@ -65,13 +68,22 @@ declare const HelixHeadlessAreaChart_base: typeof HelixElement & (new (...args:
65
68
  * Every numeric column is a part, so leave a total column out of the table the
66
69
  * chart reads, or it is counted twice. A negative reading is drawn as zero.
67
70
  *
71
+ * **A tap shows the reading under it**, and keeps it until a tap lands
72
+ * elsewhere. `helix-reading-change` says each move of the reading, by pointer,
73
+ * tap or key, and `helix-reading-press` says a reading was clicked, tapped, or
74
+ * pressed with Enter or Space, for a page that opens what a reading stands for.
75
+ *
68
76
  * @since 0.1.0
69
77
  * @status stable
70
78
  * @category charts
71
79
  * @aliases Stacked Area Chart, Composition Chart, Banded Chart
72
80
  * @documentation https://fusion.dev/helix/components/area-chart
73
81
  *
82
+ * @event {HelixReadingChangeEvent} helix-reading-change - The reading shown moved, or was put away: `index` is the row, or `-1` for none, with its `label`, the `series` the keys rest on, and the `pointerType` that moved it.
83
+ * @event {HelixReadingPressEvent} helix-reading-press - A reading was clicked, tapped, or pressed with Enter or Space, with the same detail as `helix-reading-change`.
84
+ *
74
85
  * @csspart area - One part's band.
86
+ * @csspart axis - The numbers of the scale beside the plot, under `value-axis`.
75
87
  * @csspart baseline - The rule at zero, which is the floor of the drawing.
76
88
  * @csspart gridline - One rule across the plot, under `gridlines`.
77
89
  * @csspart guide - A rule down the reading a pointer is resting on.
@@ -83,6 +95,7 @@ declare const HelixHeadlessAreaChart_base: typeof HelixElement & (new (...args:
83
95
  * @csspart plot - The drawing, which takes focus to be read by key.
84
96
  * @csspart summary - The total and each part's share at the latest reading, written as a sentence for screen readers.
85
97
  * @csspart svg - The drawing surface the bands sit on.
98
+ * @csspart tick - One number on the axis, placed with `inset-block-start` at its level.
86
99
  * @csspart tooltip - The bubble, forwarded from `Tooltip`.
87
100
  * @csspart tooltip-anchor - The `Tooltip` element itself.
88
101
  * @csspart tooltip-arrow - The bubble's point, forwarded from `Tooltip`.
@@ -99,6 +112,8 @@ declare const HelixHeadlessAreaChart_base: typeof HelixElement & (new (...args:
99
112
  * @csspart tooltip-right - The bubble while it is right of the reading, beside `tooltip`.
100
113
  * @csspart tooltip-top - The bubble while it is above the reading, beside `tooltip`.
101
114
  * @csspart tooltip-value - A column's value inside the tooltip.
115
+ *
116
+ * @cssproperty --area-chart-axis - The width of the axis, in `ch`, set from the widest number on it. Written by the component on the axis.
102
117
  */
103
118
  export declare class HelixHeadlessAreaChart extends HelixHeadlessAreaChart_base {
104
119
  /**
@@ -106,6 +121,13 @@ export declare class HelixHeadlessAreaChart extends HelixHeadlessAreaChart_base
106
121
  * `registerWords('de', { 'helix-area-chart': { totalLabel: 'Summe' } })`.
107
122
  */
108
123
  static words: string;
124
+ /**
125
+ * Sends the reading's moves and presses as `helix-reading-change` and
126
+ * `helix-reading-press`, which the pointer hears. Here and not in a
127
+ * constructor, where the manifest's analyzer reads the assignment as a
128
+ * member of this class.
129
+ */
130
+ connectedCallback(): void;
109
131
  /**
110
132
  * How a band's edge gets from one reading to the next.
111
133
  *
@@ -161,6 +183,14 @@ export declare class HelixHeadlessAreaChart extends HelixHeadlessAreaChart_base
161
183
  * The name for the sum of the parts, shown in the tooltip and the summary.
162
184
  */
163
185
  totalLabel: string;
186
+ /**
187
+ * Draws the scale's numbers beside the plot, the `axis` part, at the levels
188
+ * `gridlines` rules: amounts, or percentages under `share`.
189
+ *
190
+ * Absent draws none: bands read as a composition need no numbers beside
191
+ * them, and the tooltip and the table hold every reading.
192
+ */
193
+ valueAxis: boolean;
164
194
  private warnedAboutNegatives;
165
195
  /** Whether the levels are ruled, which is every value but `category`. */
166
196
  protected get rulesValue(): boolean;
@@ -184,6 +214,12 @@ export declare class HelixHeadlessAreaChart extends HelixHeadlessAreaChart_base
184
214
  */
185
215
  private warnNegative;
186
216
  protected render(): TemplateResult;
217
+ /**
218
+ * The scale's numbers beside the plot, under `value-axis`: amounts written
219
+ * in the chart's locale, or percentages under `share`. A seam, so a
220
+ * subclass can write the numbers its own way, such as a currency.
221
+ */
222
+ protected renderAxis(y: (value: number) => number, domain: [number, number]): TemplateResult;
187
223
  /**
188
224
  * The bands, behind a seam the styled tier can move.
189
225
  *
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { CHART_PLOT, customElement, drawTooltip, formatNumbers, formatWords, HelixElement, linear, linePath, MixinChartSource, PLOT_HEIGHT, PLOT_WIDTH, reportRuntime, seriesIndex, ticks, words, wordsLocale, } from '@fusion.dev/helix-core';
7
+ import { CHART_PLOT, customElement, drawTooltip, drawValueAxis, formatNumbers, formatWords, HelixElement, HelixReadingChangeEvent, HelixReadingPressEvent, linear, linePath, MixinChartSource, PLOT_HEIGHT, PLOT_WIDTH, reportRuntime, seriesIndex, ticks, words, wordsLocale, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing, svg } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import '../../tooltip/index.js';
@@ -27,9 +27,16 @@ let HelixHeadlessAreaChart = class HelixHeadlessAreaChart extends MixinChartSour
27
27
  this.share = false;
28
28
  this.summaryLabel = '{reading}: {totalLabel} {total}; {shares}.';
29
29
  this.totalLabel = 'Total';
30
+ this.valueAxis = false;
30
31
  this.warnedAboutNegatives = false;
31
32
  }
32
33
  static { this.words = 'helix-area-chart'; }
34
+ connectedCallback() {
35
+ super.connectedCallback();
36
+ this.pointer.onReading = (heard, detail) => this.dispatchEvent(heard === 'press'
37
+ ? new HelixReadingPressEvent(detail)
38
+ : new HelixReadingChangeEvent(detail));
39
+ }
33
40
  get rulesValue() {
34
41
  return this.gridlines !== undefined && this.gridlines !== 'category';
35
42
  }
@@ -100,9 +107,14 @@ let HelixHeadlessAreaChart = class HelixHeadlessAreaChart extends MixinChartSour
100
107
  const y = linear(domain, [PLOT_HEIGHT, 0]);
101
108
  const slot = PLOT_WIDTH / rows.length;
102
109
  const x = linear([0, Math.max(rows.length - 1, 1)], [slot / 2, PLOT_WIDTH - slot / 2]);
103
- return html `${this.renderPlot(stack, rows.length, x, y, domain)}
110
+ return html `${this.valueAxis ? this.renderAxis(y, domain) : nothing}${this.renderPlot(stack, rows.length, x, y, domain)}
104
111
  ${this.renderLabels(rows.map((row) => row.label))} ${this.renderSummary(stack, rows)}`;
105
112
  }
113
+ renderAxis(y, domain) {
114
+ const locale = wordsLocale(this);
115
+ const percent = percentIn(locale);
116
+ return drawValueAxis(domain, (value) => (y(value) / PLOT_HEIGHT) * 100, this.share ? (value) => percent.format(value) : formatNumbers(locale), (widest) => `--area-chart-axis: ${widest}ch`);
117
+ }
106
118
  renderPlot(stack, count, x, y, domain) {
107
119
  const order = stack.layers.map((_, depth) => depth).reverse();
108
120
  return html `<div
@@ -249,6 +261,9 @@ __decorate([
249
261
  words,
250
262
  property({ attribute: 'total-label' })
251
263
  ], HelixHeadlessAreaChart.prototype, "totalLabel", void 0);
264
+ __decorate([
265
+ property({ attribute: 'value-axis', reflect: true, type: Boolean })
266
+ ], HelixHeadlessAreaChart.prototype, "valueAxis", void 0);
252
267
  HelixHeadlessAreaChart = __decorate([
253
268
  customElement('helix-headless-area-chart')
254
269
  ], HelixHeadlessAreaChart);
@@ -30,13 +30,21 @@ declare const HelixHeadlessBarChart_base: typeof HelixElement & (new (...args: a
30
30
  * The component writes the custom properties below with numbers it computes,
31
31
  * and the stylesheet that draws the chart reads them. Do not set them yourself.
32
32
  *
33
+ * **A tap shows the reading under it**, and keeps it until a tap lands
34
+ * elsewhere. `helix-reading-change` says each move of the reading, by pointer,
35
+ * tap or key, and `helix-reading-press` says a reading was clicked, tapped, or
36
+ * pressed with Enter or Space, for a page that opens what a reading stands for.
37
+ *
33
38
  * @since 0.1.0
34
39
  * @status stable
35
40
  * @category charts
36
41
  * @aliases Column Chart, Categorical Chart
37
42
  * @documentation https://fusion.dev/helix/components/bar-chart
38
43
  *
39
- * @csspart axis - The numbers of one scale, drawn only when `secondary` splits the chart into two scales.
44
+ * @event {HelixReadingChangeEvent} helix-reading-change - The reading shown moved, or was put away: `index` is the row, or `-1` for none, with its `label`, the `series` the keys rest on, and the `pointerType` that moved it.
45
+ * @event {HelixReadingPressEvent} helix-reading-press - A reading was clicked, tapped, or pressed with Enter or Space, with the same detail as `helix-reading-change`.
46
+ *
47
+ * @csspart axis - The numbers of one scale: the primary under `value-axis`, and both when `secondary` splits the chart into two scales.
40
48
  * @csspart band - The category band under the pointer.
41
49
  * @csspart bar - One column.
42
50
  * @csspart bar-wash-end - The far stop of a bar's gradient, where one is used.
@@ -83,6 +91,13 @@ export declare class HelixHeadlessBarChart extends HelixHeadlessBarChart_base {
83
91
  * `registerWords('de', { 'helix-bar-chart': { highestLabel: 'Höchster Wert' } })`.
84
92
  */
85
93
  static words: string;
94
+ /**
95
+ * Sends the reading's moves and presses as `helix-reading-change` and
96
+ * `helix-reading-press`, which the pointer hears. Here and not in a
97
+ * constructor, where the manifest's analyzer reads the assignment as a
98
+ * member of this class.
99
+ */
100
+ connectedCallback(): void;
86
101
  /**
87
102
  * Which gridlines are drawn across the plot, if any.
88
103
  *
@@ -199,6 +214,15 @@ export declare class HelixHeadlessBarChart extends HelixHeadlessBarChart_base {
199
214
  * the reading as the table writes it.
200
215
  */
201
216
  summaryLabel: string;
217
+ /**
218
+ * Draws the numbers of the scale the bars are measured on, the `axis` part,
219
+ * at the levels `gridlines` rules: down the side of columns, and along the
220
+ * bottom of bars under `orientation="horizontal"`.
221
+ *
222
+ * Absent draws none with one scale, since bars compared with each other need
223
+ * no numbers. With `secondary` both scales are drawn whatever this says.
224
+ */
225
+ valueAxis: boolean;
202
226
  /** The series names on the second scale, lowercased and trimmed. */
203
227
  private get scaled();
204
228
  /** The series names drawn as a line, lowercased and trimmed for matching. */
@@ -251,8 +275,8 @@ export declare class HelixHeadlessBarChart extends HelixHeadlessBarChart_base {
251
275
  * a small chart is furniture that makes the drawing smaller for no reading a
252
276
  * reader was going to do. With two scales that argument inverts: a reader
253
277
  * cannot tell there are two, cannot tell which series is on which, and cannot
254
- * recover either from the picture. So this appears with `secondary` and never
255
- * without it.
278
+ * recover either from the picture. So this appears with `secondary`, and
279
+ * with one scale only where `value-axis` asks for its numbers.
256
280
  *
257
281
  * **Beside the plot and not inside it.** Inside, the numbers are drawn over
258
282
  * the drawing, and a horizontal chart shows it immediately: its value axis
@@ -263,7 +287,7 @@ export declare class HelixHeadlessBarChart extends HelixHeadlessBarChart_base {
263
287
  * to the right.
264
288
  *
265
289
  * A column of the host grid instead, which the stylesheet grows only when
266
- * `secondary` is set. That is what the property reflects for.
290
+ * `secondary` or `value-axis` is set. That is what the two reflect for.
267
291
  *
268
292
  * The primary hugs the edge the drawing starts at and the secondary the edge
269
293
  * it ends at: down the two sides for columns, along the bottom and top for
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { byRow, CHART_PLOT, customElement, drawTooltip, extent, formatNumbers, formatWords, HelixElement, linear, linePath, MixinChartSource, PLOT_HEIGHT, PLOT_WIDTH, seriesIndex, seriesKey, seriesNames, ticks, unbroken, words, wordsLocale, } from '@fusion.dev/helix-core';
7
+ import { byRow, CHART_PLOT, customElement, drawTooltip, extent, formatNumbers, formatWords, HelixElement, HelixReadingChangeEvent, HelixReadingPressEvent, linear, linePath, MixinChartSource, PLOT_HEIGHT, PLOT_WIDTH, seriesIndex, seriesKey, seriesNames, ticks, unbroken, words, wordsLocale, } from '@fusion.dev/helix-core';
8
8
  import { html, nothing, svg } from 'lit';
9
9
  import { property } from 'lit/decorators.js';
10
10
  import { stackEnds, stackedReadings, stackSpans } from '../stack.js';
@@ -15,8 +15,15 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource
15
15
  this.highestLabel = 'Highest';
16
16
  this.stacked = false;
17
17
  this.summaryLabel = '{highestLabel}: {readings}.';
18
+ this.valueAxis = false;
18
19
  }
19
20
  static { this.words = 'helix-bar-chart'; }
21
+ connectedCallback() {
22
+ super.connectedCallback();
23
+ this.pointer.onReading = (heard, detail) => this.dispatchEvent(heard === 'press'
24
+ ? new HelixReadingPressEvent(detail)
25
+ : new HelixReadingChangeEvent(detail));
26
+ }
20
27
  get scaled() {
21
28
  return new Set(seriesNames(this.secondary));
22
29
  }
@@ -61,14 +68,17 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource
61
68
  const other = [Math.min(0, second), Math.max(0, top)];
62
69
  const z = scaled.size > 0 ? linear(other, range) : undefined;
63
70
  const format = formatNumbers(wordsLocale(this));
64
- const widest = z === undefined
71
+ const levels = [...ticks(domain), ...(z === undefined ? [] : ticks(other))];
72
+ const widest = z === undefined && !this.valueAxis
65
73
  ? 0
66
- : Math.max(...[...ticks(domain), ...ticks(other)].map((level) => format(level).length));
74
+ : Math.max(1, ...levels.map((level) => format(level).length));
67
75
  const lone = series.filter(own);
68
76
  const tint = lone.length === 1 && lone[0] !== undefined
69
77
  ? `--bar-chart-secondary: var(--chart-series-${seriesIndex(series.indexOf(lone[0]))})`
70
78
  : undefined;
71
- return html `${z === undefined ? nothing : this.renderAxis(y, domain, 'primary', sideways, widest)}${this.renderPlot(series, categories, y, domain, floor, z)}${z === undefined ? nothing : this.renderAxis(z, other, 'secondary', sideways, widest, tint)}${this.renderLabels(categories)}${this.renderSummary(series)}`;
79
+ return html `${z === undefined && !this.valueAxis
80
+ ? nothing
81
+ : this.renderAxis(y, domain, 'primary', sideways, widest)}${this.renderPlot(series, categories, y, domain, floor, z)}${z === undefined ? nothing : this.renderAxis(z, other, 'secondary', sideways, widest, tint)}${this.renderLabels(categories)}${this.renderSummary(series)}`;
72
82
  }
73
83
  renderSummary(series) {
74
84
  const rows = this.sourceRows;
@@ -309,6 +319,9 @@ __decorate([
309
319
  words,
310
320
  property({ attribute: 'summary-label' })
311
321
  ], HelixHeadlessBarChart.prototype, "summaryLabel", void 0);
322
+ __decorate([
323
+ property({ attribute: 'value-axis', reflect: true, type: Boolean })
324
+ ], HelixHeadlessBarChart.prototype, "valueAxis", void 0);
312
325
  HelixHeadlessBarChart = __decorate([
313
326
  customElement('helix-headless-bar-chart')
314
327
  ], HelixHeadlessBarChart);
@@ -1,5 +1,5 @@
1
1
  import { HelixElement } from '@fusion.dev/helix-core';
2
- import { type PropertyValues, type TemplateResult } from 'lit';
2
+ import { type CSSResultGroup, type PropertyValues, type TemplateResult } from 'lit';
3
3
  /**
4
4
  * @summary One step in a breadcrumb trail.
5
5
  *
@@ -18,7 +18,10 @@ import { type PropertyValues, type TemplateResult } from 'lit';
18
18
  * including your own anchor.
19
19
  *
20
20
  * The current page is plain text, and `aria-current="page"` on the host conveys
21
- * the position.
21
+ * the position. In a trail where no crumb is `current`, the last one is said
22
+ * to be the current page, and keeps its link. An `aria-current` you write on
23
+ * the host, such as `aria-current="location"`, replaces the one the crumb
24
+ * gives itself.
22
25
  *
23
26
  * @since 0.1.0
24
27
  * @status stable
@@ -42,6 +45,7 @@ import { type PropertyValues, type TemplateResult } from 'lit';
42
45
  * the label and what follows it into one control.
43
46
  */
44
47
  export declare class HelixHeadlessBreadcrumb extends HelixElement {
48
+ static styles: CSSResultGroup;
45
49
  /**
46
50
  * Marks this item as the page you are on.
47
51
  */
@@ -65,6 +69,11 @@ export declare class HelixHeadlessBreadcrumb extends HelixElement {
65
69
  * of `breadcrumbs/links` lost its padding at once.
66
70
  */
67
71
  href?: string;
72
+ /**
73
+ * @internal Set by the trail: this is its last crumb and no crumb is
74
+ * `current`, so this one is said to be the current page.
75
+ */
76
+ implied: boolean;
68
77
  /**
69
78
  * The link's `rel` value, which describes how the linked page relates to this
70
79
  * one.
@@ -6,23 +6,29 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
6
6
  };
7
7
  import { customElement, HelixElement, hostAria, slotFilled, slotNamed, } from '@fusion.dev/helix-core';
8
8
  import { caretRight } from '@fusion.dev/helix-icons';
9
- import { html, nothing } from 'lit';
9
+ import { css, html, nothing, } from 'lit';
10
10
  import { property } from 'lit/decorators.js';
11
11
  import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
12
12
  let HelixHeadlessBreadcrumb = class HelixHeadlessBreadcrumb extends HelixElement {
13
13
  constructor() {
14
14
  super(...arguments);
15
15
  this.current = false;
16
+ this.implied = false;
16
17
  this.rel = '';
17
18
  this.target = '';
18
19
  this.handleEnd = () => {
19
20
  this.markEnd();
20
21
  };
21
22
  }
23
+ static { this.styles = css `
24
+ :host(:first-child) [part='separator'] {
25
+ display: none;
26
+ }
27
+ `; }
22
28
  willUpdate(changed) {
23
29
  super.willUpdate(changed);
24
30
  hostAria(this.internals, { role: 'listitem' });
25
- this.internals.ariaCurrent = this.current ? 'page' : null;
31
+ this.internals.ariaCurrent = this.current || this.implied ? 'page' : null;
26
32
  }
27
33
  updated(changed) {
28
34
  super.updated(changed);
@@ -60,6 +66,9 @@ __decorate([
60
66
  __decorate([
61
67
  property({ reflect: true })
62
68
  ], HelixHeadlessBreadcrumb.prototype, "href", void 0);
69
+ __decorate([
70
+ property({ attribute: false })
71
+ ], HelixHeadlessBreadcrumb.prototype, "implied", void 0);
63
72
  __decorate([
64
73
  property()
65
74
  ], HelixHeadlessBreadcrumb.prototype, "rel", void 0);