@fusion.dev/helix-headless 0.218.0 → 0.220.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 (84) hide show
  1. package/api.json +1 -1
  2. package/custom-elements.json +1 -1
  3. package/dist/breadcrumbs/breadcrumb/breadcrumb.d.ts +13 -2
  4. package/dist/breadcrumbs/breadcrumb/breadcrumb.js +19 -9
  5. package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +16 -1
  6. package/dist/breadcrumbs/root/breadcrumbs-root.js +6 -0
  7. package/dist/button/root/button-root.d.ts +33 -0
  8. package/dist/button/root/button-root.js +13 -3
  9. package/dist/canvas/root/canvas-root.d.ts +11 -0
  10. package/dist/canvas/root/canvas-root.js +1 -0
  11. package/dist/chat/composer/chat-composer.d.ts +221 -6
  12. package/dist/chat/composer/chat-composer.js +249 -19
  13. package/dist/chat/message/chat-message.d.ts +7 -0
  14. package/dist/chat/message/chat-message.js +2 -1
  15. package/dist/chat/root/chat-root.d.ts +1 -0
  16. package/dist/chat/root/chat-root.js +1 -1
  17. package/dist/code-editor/root/code-editor-root.d.ts +48 -51
  18. package/dist/code-editor/root/code-editor-root.js +67 -249
  19. package/dist/copy-button/root/copy-button-root.js +3 -3
  20. package/dist/device-screen/root/device-screen-root.d.ts +11 -0
  21. package/dist/device-screen/root/device-screen-root.js +22 -10
  22. package/dist/dialog/root/dialog-root.js +6 -1
  23. package/dist/drilldown/item/drilldown-item.js +1 -1
  24. package/dist/element-events.generated.js +4 -1
  25. package/dist/filter-input/root/filter-input-root.d.ts +4 -3
  26. package/dist/filter-input/root/filter-input-root.js +1 -20
  27. package/dist/find/index.d.ts +1 -0
  28. package/dist/find/index.js +1 -0
  29. package/dist/find/root/find-root.d.ts +314 -0
  30. package/dist/find/root/find-root.js +577 -0
  31. package/dist/graph/root/graph-root.d.ts +11 -0
  32. package/dist/graph/root/graph-root.js +4 -1
  33. package/dist/index.d.ts +1 -0
  34. package/dist/index.js +1 -0
  35. package/dist/input/root/input-root.d.ts +6 -0
  36. package/dist/input/root/input-root.js +7 -0
  37. package/dist/lightbox/root/lightbox-root.d.ts +4 -1
  38. package/dist/lightbox/root/lightbox-root.js +10 -8
  39. package/dist/listbox/root/listbox-root.d.ts +21 -0
  40. package/dist/listbox/root/listbox-root.js +37 -2
  41. package/dist/log/group/log-group.d.ts +18 -3
  42. package/dist/log/group/log-group.js +31 -4
  43. package/dist/log/root/log-root.d.ts +13 -3
  44. package/dist/log/root/log-root.js +5 -2
  45. package/dist/map/root/map-root.d.ts +11 -0
  46. package/dist/map/root/map-root.js +28 -12
  47. package/dist/menu/root/menu-root.d.ts +8 -0
  48. package/dist/menu/root/menu-root.js +22 -1
  49. package/dist/menu/section/menu-section.d.ts +10 -0
  50. package/dist/menu/section/menu-section.js +10 -0
  51. package/dist/meter/index.d.ts +1 -0
  52. package/dist/meter/index.js +1 -0
  53. package/dist/meter/root/meter-root.d.ts +16 -1
  54. package/dist/meter/root/meter-root.js +27 -11
  55. package/dist/meter/segment/meter-segment.d.ts +74 -0
  56. package/dist/meter/segment/meter-segment.js +47 -0
  57. package/dist/scroll-area/root/scroll-area-root.d.ts +20 -3
  58. package/dist/scroll-area/root/scroll-area-root.js +23 -4
  59. package/dist/spatial-bar-chart/bars.d.ts +125 -5
  60. package/dist/spatial-bar-chart/bars.js +194 -17
  61. package/dist/spatial-bar-chart/root/spatial-bar-chart-root.d.ts +148 -6
  62. package/dist/spatial-bar-chart/root/spatial-bar-chart-root.js +285 -16
  63. package/dist/splitter/root/splitter-root.js +1 -0
  64. package/dist/tabs/root/tabs-root.d.ts +4 -0
  65. package/dist/tabs/tab/tab.d.ts +1 -0
  66. package/dist/tabs/tab/tab.js +2 -2
  67. package/dist/tag/root/tag-root.d.ts +1 -1
  68. package/dist/terminal/root/terminal-root.d.ts +60 -32
  69. package/dist/terminal/root/terminal-root.js +82 -178
  70. package/dist/toolbar/root/toolbar-root.d.ts +4 -1
  71. package/dist/tooltip/root/tooltip-root.d.ts +23 -1
  72. package/dist/tooltip/root/tooltip-root.js +28 -5
  73. package/dist/tour/root/tour-root.d.ts +1 -0
  74. package/dist/tour/root/tour-root.js +4 -1
  75. package/dist/tour/stop/tour-stop.js +1 -0
  76. package/dist/tree/item/tree-item.d.ts +11 -0
  77. package/dist/tree/item/tree-item.js +19 -1
  78. package/examples.json +1 -1
  79. package/lint.json +1 -1
  80. package/package.json +5 -5
  81. package/types/jsx.d.ts +2222 -118
  82. package/types/svelte.d.ts +2 -0
  83. package/types/vue.d.ts +468 -19
  84. package/types/words.d.ts +96 -1
@@ -23,6 +23,10 @@ import { nothing, type PropertyValues, type TemplateResult } from 'lit';
23
23
  *
24
24
  * @csspart heading - The words that name the section.
25
25
  * @csspart section - The group that holds the heading and the rows.
26
+ *
27
+ * @cssstate start - A row in the section has content before its label, so
28
+ * every row in it keeps the leading column. The menu decides it for each
29
+ * section, apart from the rows outside one.
26
30
  */
27
31
  export declare class HelixHeadlessMenuSection extends HelixElement {
28
32
  /**
@@ -35,6 +39,11 @@ export declare class HelixHeadlessMenuSection extends HelixElement {
35
39
  * property, because the menu writes it after the section upgrades.
36
40
  */
37
41
  onPage: boolean;
42
+ /**
43
+ * @internal Set by the menu: whether a row in the section has content
44
+ * before its label, so the section's rows keep the leading column.
45
+ */
46
+ startColumn: boolean;
38
47
  /** @internal What a menu reads to tell a section from a row. */
39
48
  readonly isMenuSection = true;
40
49
  /**
@@ -44,6 +53,7 @@ export declare class HelixHeadlessMenuSection extends HelixElement {
44
53
  */
45
54
  private heading;
46
55
  protected willUpdate(changed: PropertyValues): void;
56
+ protected updated(changed: PropertyValues): void;
47
57
  protected firstUpdated(changed: PropertyValues): void;
48
58
  private readonly readHeading;
49
59
  protected render(): TemplateResult | typeof nothing;
@@ -7,6 +7,7 @@ let HelixHeadlessMenuSection = class HelixHeadlessMenuSection extends HelixEleme
7
7
  super(...arguments);
8
8
  this.label = '';
9
9
  this.onPage = true;
10
+ this.startColumn = false;
10
11
  this.isMenuSection = true;
11
12
  this.heading = false;
12
13
  this.readHeading = (event) => {
@@ -19,6 +20,12 @@ let HelixHeadlessMenuSection = class HelixHeadlessMenuSection extends HelixEleme
19
20
  this.heading = this.knownChildren.some((child) => child.slot === 'heading');
20
21
  }
21
22
  }
23
+ updated(changed) {
24
+ super.updated(changed);
25
+ if (changed.has('startColumn')) {
26
+ this.toggleState('start', this.startColumn);
27
+ }
28
+ }
22
29
  firstUpdated(changed) {
23
30
  super.firstUpdated(changed);
24
31
  this.finishHydration();
@@ -46,6 +53,9 @@ __decorate([
46
53
  __decorate([
47
54
  property({ attribute: false })
48
55
  ], HelixHeadlessMenuSection.prototype, "onPage", void 0);
56
+ __decorate([
57
+ property({ attribute: false })
58
+ ], HelixHeadlessMenuSection.prototype, "startColumn", void 0);
49
59
  __decorate([
50
60
  state()
51
61
  ], HelixHeadlessMenuSection.prototype, "heading", void 0);
@@ -1 +1,2 @@
1
1
  export * from './root/meter-root.js';
2
+ export * from './segment/meter-segment.js';
@@ -1 +1,2 @@
1
1
  export * from './root/meter-root.js';
2
+ export * from './segment/meter-segment.js';
@@ -6,7 +6,9 @@ export type MeterBand = 'fair' | 'good' | 'poor';
6
6
  * @summary A measurement in a known range, with optional bands that mark it good, fair, or poor.
7
7
  *
8
8
  * @description
9
- * A meter shows a level within a range. Use `Progress` to show how far along a
9
+ * A meter shows a level within a range, or how the level divides: write
10
+ * `Meter.Segment` inside it for each part of the whole, and the meter measures
11
+ * their sum. Use `Progress` to show how far along a
10
12
  * task is. A progress bar starts empty and ends full, and full is always the
11
13
  * good end. For a meter, which end is good depends on what is measured. There
12
14
  * is no `indeterminate`, because a meter always has a value.
@@ -31,6 +33,7 @@ export type MeterBand = 'fair' | 'good' | 'poor';
31
33
  * @aliases Gauge, Level, Capacity
32
34
  * @documentation https://fusion.dev/helix/components/meter
33
35
  *
36
+ * @slot - `Meter.Segment` elements, one for each share of the whole, drawn in the bar.
34
37
  * @slot label - A visible label. Its words name the meter in place of `label`.
35
38
  *
36
39
  * @event {HelixBandEvent} helix-band - Fired when the reading moves into another band, with the band it is in now, or `''` where it is in none. Not sent on the first render, since a meter that arrives in a band has not moved.
@@ -50,6 +53,7 @@ export type MeterBand = 'fair' | 'good' | 'poor';
50
53
  * @cssproperty --meter-along - Where a bound sits on the arc, as a length along it.
51
54
  * @cssproperty --meter-at - Where a bound sits on a linear track, as a percentage.
52
55
  * @cssproperty --meter-fraction - The value as a fraction of the range, 0 to 1.
56
+ * @cssproperty --meter-span - How much the range holds, `max` less `min`, which a segment divides its own value by to find its width.
53
57
  * @cssproperty --meter-sweep - How much of the circle the arc covers, as a path length.
54
58
  *
55
59
  * @cssstate fair - The reading is in a band next to the good band.
@@ -225,6 +229,17 @@ export declare class HelixHeadlessMeter extends HelixElement {
225
229
  * an empty string for none, and undefined before the first update.
226
230
  */
227
231
  private lastBand;
232
+ /**
233
+ * The segments written inside, where they can be known: none on a server or
234
+ * while hydrating what one sent, so the first render is the server's, and
235
+ * every one once the page has them.
236
+ */
237
+ private get segments();
238
+ /**
239
+ * What the meter measures: the sum of its segments where it has any, and its
240
+ * own `value` where it has none, so the two can never disagree.
241
+ */
242
+ private get amount();
228
243
  /** The range, clamped so `max` is never at or below `min`. */
229
244
  private get span();
230
245
  /** The reading, pulled into the range. */
@@ -21,6 +21,19 @@ let HelixHeadlessMeter = class HelixHeadlessMeter extends HelixElement {
21
21
  };
22
22
  }
23
23
  static { this.words = 'helix-meter'; }
24
+ get segments() {
25
+ return this.knownChildren.filter((child) => child.localName.endsWith('meter-segment'));
26
+ }
27
+ get amount() {
28
+ const segments = this.segments;
29
+ if (segments.length === 0) {
30
+ return this.value;
31
+ }
32
+ return segments.reduce((sum, segment) => {
33
+ const share = Number(segment.value ?? segment.getAttribute('value'));
34
+ return Number.isFinite(share) && share > 0 ? sum + share : sum;
35
+ }, 0);
36
+ }
24
37
  get span() {
25
38
  const min = Number.isFinite(this.min) ? this.min : 0;
26
39
  const max = Number.isFinite(this.max) && this.max > min ? this.max : min + 1;
@@ -28,7 +41,7 @@ let HelixHeadlessMeter = class HelixHeadlessMeter extends HelixElement {
28
41
  }
29
42
  get reading() {
30
43
  const { max, min } = this.span;
31
- return Math.min(max, Math.max(min, Number.isFinite(this.value) ? this.value : min));
44
+ return Math.min(max, Math.max(min, Number.isFinite(this.amount) ? this.amount : min));
32
45
  }
33
46
  get fraction() {
34
47
  const { max, min } = this.span;
@@ -36,12 +49,12 @@ let HelixHeadlessMeter = class HelixHeadlessMeter extends HelixElement {
36
49
  }
37
50
  get ratio() {
38
51
  const { max, min } = this.span;
39
- const raw = Number.isFinite(this.value) ? this.value : min;
52
+ const raw = Number.isFinite(this.amount) ? this.amount : min;
40
53
  return (raw - min) / (max - min);
41
54
  }
42
55
  get beyond() {
43
56
  const { max, min } = this.span;
44
- const raw = Number.isFinite(this.value) ? this.value : min;
57
+ const raw = Number.isFinite(this.amount) ? this.amount : min;
45
58
  return raw > max ? 'over' : raw < min ? 'under' : undefined;
46
59
  }
47
60
  get thresholds() {
@@ -69,7 +82,7 @@ let HelixHeadlessMeter = class HelixHeadlessMeter extends HelixElement {
69
82
  }
70
83
  get text() {
71
84
  return (this.valueText ||
72
- readingText(Number.isFinite(this.value) ? this.value : this.span.min, this.ratio, wordsLocale(this), this.formatOptions));
85
+ readingText(Number.isFinite(this.amount) ? this.amount : this.span.min, this.ratio, wordsLocale(this), this.formatOptions));
73
86
  }
74
87
  willUpdate(changed) {
75
88
  super.willUpdate(changed);
@@ -134,17 +147,20 @@ let HelixHeadlessMeter = class HelixHeadlessMeter extends HelixElement {
134
147
  const at = (value) => `${((value - min) / (max - min)) * 100}%`;
135
148
  return html `<slot name="label" @slotchange=${this.renamed}></slot>${this.shape
136
149
  ? this.renderRound()
137
- : html `<div part="track">
150
+ : html `<div part="track" style="--meter-span: ${max - min}">
151
+ <slot @slotchange=${this.renamed}></slot>
138
152
  ${bounds
139
153
  ? html `<span part="tick" style="--meter-at: ${at(bounds.low)}"></span>
140
154
  <span part="tick" style="--meter-at: ${at(bounds.high)}"></span>`
141
155
  : nothing}
142
- <div
143
- part="indicator${band ? { fair: ' indicator-fair', good: ' indicator-good', poor: ' indicator-poor' }[band] : ''}${beyond ? { over: ' indicator-over', under: ' indicator-under' }[beyond] : ''}"
144
- data-band=${band ?? nothing}
145
- data-beyond=${beyond ?? nothing}
146
- style="--meter-fraction: ${this.fraction}"
147
- ></div>
156
+ ${this.segments.length > 0
157
+ ? nothing
158
+ : html `<div
159
+ part="indicator${band ? { fair: ' indicator-fair', good: ' indicator-good', poor: ' indicator-poor' }[band] : ''}${beyond ? { over: ' indicator-over', under: ' indicator-under' }[beyond] : ''}"
160
+ data-band=${band ?? nothing}
161
+ data-beyond=${beyond ?? nothing}
162
+ style="--meter-fraction: ${this.fraction}"
163
+ ></div>`}
148
164
  </div>`}
149
165
  ${this.showValue ? html `<span part="value">${this.text}</span>` : nothing}${drawStatus(this.status)}`;
150
166
  }
@@ -0,0 +1,74 @@
1
+ import { HelixElement } from '@fusion.dev/helix-core';
2
+ import { type PropertyValues, type TemplateResult } from 'lit';
3
+ /**
4
+ * @summary One share of a meter's whole, drawn as a slice of its bar.
5
+ *
6
+ * @description
7
+ * A segment says how much of the range one part of the whole takes, such as the
8
+ * tokens a model's context holds for its tools, against the tokens it holds for
9
+ * the conversation. Write one for each part inside a `Meter`.
10
+ *
11
+ * **The meter's reading is the sum of its segments.** `value` on the meter is
12
+ * ignored while it has any, so the two cannot disagree, and the bands, the ticks
13
+ * and the reading answer to the total. A segment is drawn as wide as its `value`
14
+ * is of the meter's range, so segments that add up to less than the range leave
15
+ * the rest of the track empty, and ones that add up to more are cut at the end.
16
+ *
17
+ * **A meter's children are presentational to a screen reader**, so a segment
18
+ * reaches no one that cannot see the bar. Say the breakdown in text beside it,
19
+ * such as a list with the name and the amount of each part, which is also where a
20
+ * reader finds the exact figures.
21
+ *
22
+ * A segment draws only in the bar. A meter in `ring` or `arc` draws its total.
23
+ *
24
+ * @since 0.1.0
25
+ * @status experimental
26
+ * @category feedback
27
+ * @documentation https://fusion.dev/helix/components/meter
28
+ *
29
+ * @slot - Nothing. A segment draws its share and has no content.
30
+ *
31
+ * @csspart fill - The slice of the bar the segment takes.
32
+ *
33
+ * @cssproperty --meter-segment-value - How much of the range the segment takes, in the meter's own units.
34
+ */
35
+ export declare class HelixHeadlessMeterSegment extends HelixElement {
36
+ /**
37
+ * What the segment is, such as "Tools", which names the slice when a pointer
38
+ * rests on it.
39
+ *
40
+ * Not announced: see the note on a meter's children above.
41
+ */
42
+ label: string;
43
+ /**
44
+ * What the segment means, as it does on a callout. Takes the tone's color in
45
+ * place of the next color in the series.
46
+ */
47
+ tone?: 'accent' | 'danger' | 'info' | 'neutral' | 'primary' | 'success' | 'warning';
48
+ /**
49
+ * How much of the meter's range the segment takes, in the units of the meter's
50
+ * `min` and `max`.
51
+ *
52
+ * A value that is not a number, or is below zero, draws nothing.
53
+ */
54
+ value: number;
55
+ /** The meter this is a segment of, which reads its sum. */
56
+ private get meter();
57
+ /** The amount drawn: never below zero, and never a number that is not one. */
58
+ private get amount();
59
+ /**
60
+ * The meter sums its segments, so it hears when one arrives or changes. The
61
+ * segment is the one that knows, so it reaches up by identity and the meter
62
+ * does not have to ask. A meter's own update never updates a segment, so this
63
+ * cannot loop.
64
+ */
65
+ protected updated(changed: PropertyValues): void;
66
+ /** A segment taken out leaves the meter with a smaller sum, which it reads again. */
67
+ disconnectedCallback(): void;
68
+ protected render(): TemplateResult;
69
+ }
70
+ declare global {
71
+ interface HTMLElementTagNameMap {
72
+ 'helix-headless-meter-segment': HelixHeadlessMeterSegment;
73
+ }
74
+ }
@@ -0,0 +1,47 @@
1
+ import { __decorate } from "tslib";
2
+ import { closestInstance, customElement, HelixElement, numberConverter, } from '@fusion.dev/helix-core';
3
+ import { html, nothing } from 'lit';
4
+ import { property } from 'lit/decorators.js';
5
+ import { HelixHeadlessMeter } from '../root/meter-root.js';
6
+ let HelixHeadlessMeterSegment = class HelixHeadlessMeterSegment extends HelixElement {
7
+ constructor() {
8
+ super(...arguments);
9
+ this.label = '';
10
+ this.value = 0;
11
+ }
12
+ get meter() {
13
+ return closestInstance(this, HelixHeadlessMeter);
14
+ }
15
+ get amount() {
16
+ return Number.isFinite(this.value) && this.value > 0 ? this.value : 0;
17
+ }
18
+ updated(changed) {
19
+ super.updated(changed);
20
+ this.meter?.requestUpdate();
21
+ }
22
+ disconnectedCallback() {
23
+ const meter = this.meter;
24
+ super.disconnectedCallback();
25
+ queueMicrotask(() => meter?.requestUpdate());
26
+ }
27
+ render() {
28
+ return html `<span
29
+ part="fill"
30
+ title=${this.label || nothing}
31
+ style="--meter-segment-value: ${this.amount}"
32
+ ></span>`;
33
+ }
34
+ };
35
+ __decorate([
36
+ property()
37
+ ], HelixHeadlessMeterSegment.prototype, "label", void 0);
38
+ __decorate([
39
+ property({ reflect: true })
40
+ ], HelixHeadlessMeterSegment.prototype, "tone", void 0);
41
+ __decorate([
42
+ property({ converter: numberConverter('value', 0) })
43
+ ], HelixHeadlessMeterSegment.prototype, "value", void 0);
44
+ HelixHeadlessMeterSegment = __decorate([
45
+ customElement('helix-headless-meter-segment')
46
+ ], HelixHeadlessMeterSegment);
47
+ export { HelixHeadlessMeterSegment };
@@ -35,6 +35,11 @@ declare const EDGES: readonly ['block-start', 'block-end', 'inline-start', 'inli
35
35
  * scrollbar, the browser's or the component's, and clips what is past it.
36
36
  * Leave it unset to scroll both ways.
37
37
  *
38
+ * **A mask on the viewport also masks the browser's scrollbar.** While a
39
+ * scrollbar takes room beside the content, the viewport carries its size in
40
+ * `--scroll-area-gutter-inline-end` or `--scroll-area-gutter-block-end`, so a
41
+ * mask can leave that room opaque. The styled tier's fades use them.
42
+ *
38
43
  * The viewport's `scroll` and `scrollend` are sent again from the host, which
39
44
  * is the element you hold. A thumb dragged across the content sends one
40
45
  * `scrollend`, once it is let go. In React, bind them with `onScroll` and
@@ -56,7 +61,7 @@ declare const EDGES: readonly ['block-start', 'block-end', 'inline-start', 'inli
56
61
  * @event {Event} scrollend - The content stopped scrolling. Sent from the host, as `scroll` is, and once for a thumb dragged across the content. Does not bubble.
57
62
  *
58
63
  * @csspart corner - The square where the two scrollbars meet, under `scrollbars`. Hidden unless both are drawn.
59
- * @csspart edges - The layer that draws the cues on the inline start and end edges.
64
+ * @csspart edges - The layer over the viewport. The styled tier draws the focus ring on it, and the painted fades at the inline start and end.
60
65
  * @csspart scrollbar - Each scrollbar, under `scrollbars`. Hidden on an axis that has none.
61
66
  * @csspart scrollbar-horizontal - The scrollbar along the inline axis, alongside `scrollbar`.
62
67
  * @csspart scrollbar-vertical - The scrollbar along the block axis, alongside `scrollbar`.
@@ -66,6 +71,8 @@ declare const EDGES: readonly ['block-start', 'block-end', 'inline-start', 'inli
66
71
  * @csspart thumb-vertical - The thumb of the vertical scrollbar, alongside `thumb`.
67
72
  * @csspart viewport - The scrolling box.
68
73
  *
74
+ * @cssproperty --scroll-area-gutter-block-end - The room the browser's horizontal scrollbar takes at the block end of the viewport, in pixels. Written on the `viewport` part while that scrollbar takes room, and absent while it draws over the content.
75
+ * @cssproperty --scroll-area-gutter-inline-end - The room the browser's vertical scrollbar takes at the inline end of the viewport, in pixels. Written on the `viewport` part while that scrollbar takes room, and absent while it draws over the content.
69
76
  * @cssproperty --scroll-area-scroll-fraction - How far along its axis the content is scrolled, from 0 at the start to 1 at the end. Written on each `scrollbar` part, so its thumb inherits it. Counted from the right on a right-to-left page.
70
77
  * @cssproperty --scroll-area-thumb-fraction - The share of the track the thumb covers, from 0 to 1, which is how much of the content is in view. Written on each `scrollbar` part.
71
78
  *
@@ -119,6 +126,12 @@ export declare class HelixHeadlessScrollArea extends HelixElement {
119
126
  * Whether there is anything to scroll at all, which decides the tab stop.
120
127
  */
121
128
  private scrolls;
129
+ /**
130
+ * The room the browser's own scrollbars take, in pixels: the vertical one at
131
+ * the inline end, and the horizontal one at the block end. Zero where they are
132
+ * drawn over the content.
133
+ */
134
+ private gutter;
122
135
  private watcher;
123
136
  /**
124
137
  * The element that scrolls. Use it to scroll the content from your code.
@@ -154,8 +167,12 @@ export declare class HelixHeadlessScrollArea extends HelixElement {
154
167
  private watchContent;
155
168
  protected updated(changed: PropertyValues): void;
156
169
  private readonly handleSlotChange;
157
- /** The axis `orientation` takes away, clipped where the content would overflow it. */
158
- private get clipped();
170
+ /**
171
+ * The viewport's inline style: the axis `orientation` takes away, clipped
172
+ * where the content would overflow it, and the room each of the browser's
173
+ * scrollbars takes.
174
+ */
175
+ private get viewportStyle();
159
176
  protected render(): TemplateResult;
160
177
  /**
161
178
  * The scrollbars, the thumbs in them and the corner.
@@ -17,6 +17,7 @@ let HelixHeadlessScrollArea = class HelixHeadlessScrollArea extends HelixElement
17
17
  });
18
18
  this.more = [];
19
19
  this.scrolls = false;
20
+ this.gutter = { blockEnd: 0, inlineEnd: 0 };
20
21
  this.measure = () => {
21
22
  const box = this.scroller;
22
23
  if (!box) {
@@ -43,6 +44,11 @@ let HelixHeadlessScrollArea = class HelixHeadlessScrollArea extends HelixElement
43
44
  this.scrolls =
44
45
  (block && box.scrollHeight > box.clientHeight) ||
45
46
  (inline && box.scrollWidth > box.clientWidth);
47
+ const blockEnd = Math.max(0, box.offsetHeight - box.clientHeight);
48
+ const inlineEnd = Math.max(0, box.offsetWidth - box.clientWidth);
49
+ if (blockEnd !== this.gutter.blockEnd || inlineEnd !== this.gutter.inlineEnd) {
50
+ this.gutter = { blockEnd, inlineEnd };
51
+ }
46
52
  if (this.scrollbars) {
47
53
  this.bars.read(box);
48
54
  }
@@ -118,17 +124,27 @@ let HelixHeadlessScrollArea = class HelixHeadlessScrollArea extends HelixElement
118
124
  this.toggleState('scrollbar-horizontal', drawn?.horizontal.drawn === true);
119
125
  this.toggleState('scrollbar-vertical', drawn?.vertical.drawn === true);
120
126
  }
121
- get clipped() {
127
+ get viewportStyle() {
128
+ const rules = [];
122
129
  if (this.orientation === 'vertical') {
123
- return 'overflow-x: hidden';
130
+ rules.push('overflow-x: hidden');
131
+ }
132
+ else if (this.orientation === 'horizontal') {
133
+ rules.push('overflow-y: hidden');
134
+ }
135
+ if (this.gutter.blockEnd > 0) {
136
+ rules.push(`--scroll-area-gutter-block-end: ${this.gutter.blockEnd}px`);
124
137
  }
125
- return this.orientation === 'horizontal' ? 'overflow-y: hidden' : nothing;
138
+ if (this.gutter.inlineEnd > 0) {
139
+ rules.push(`--scroll-area-gutter-inline-end: ${this.gutter.inlineEnd}px`);
140
+ }
141
+ return rules.length > 0 ? rules.join('; ') : nothing;
126
142
  }
127
143
  render() {
128
144
  return html `<div
129
145
  data-scroller
130
146
  part="viewport"
131
- style=${this.clipped}
147
+ style=${this.viewportStyle}
132
148
  role=${this.label ? 'region' : nothing}
133
149
  aria-label=${this.label || nothing}
134
150
  tabindex=${this.scrolls ? '0' : nothing}
@@ -211,6 +227,9 @@ __decorate([
211
227
  __decorate([
212
228
  state()
213
229
  ], HelixHeadlessScrollArea.prototype, "scrolls", void 0);
230
+ __decorate([
231
+ state()
232
+ ], HelixHeadlessScrollArea.prototype, "gutter", void 0);
214
233
  HelixHeadlessScrollArea = __decorate([
215
234
  customElement('helix-headless-scroll-area')
216
235
  ], HelixHeadlessScrollArea);
@@ -15,12 +15,21 @@ export interface BarCell {
15
15
  across: number;
16
16
  /** The column of the row the value was read from, which names its series. */
17
17
  column: number;
18
- /** Which series it stands in, counting from zero. */
18
+ /** Which series it stands in, counting from zero, which colors it. */
19
19
  depth: number;
20
+ /**
21
+ * Where its block starts on the value axis: zero, or under `stacked` the
22
+ * readings of the series before it in its category, on its side of zero.
23
+ */
24
+ from: number;
20
25
  /** The row's own label, which names its category. */
21
26
  label: string;
27
+ /** Which row of the grid in depth it stands in: its series, or the one row of a stacked chart. */
28
+ lane: number;
22
29
  /** The row it was read from, counting from zero. */
23
30
  row: number;
31
+ /** The tone the cell names with `data-tone`, which colors its own bar, where it names one. */
32
+ tone?: string | undefined;
24
33
  /** The value, where the cell holds a number. */
25
34
  value?: number | undefined;
26
35
  }
@@ -44,10 +53,20 @@ export interface BarField {
44
53
  * series `depth` is at `across * series.length + depth`.
45
54
  */
46
55
  cells: BarCell[];
56
+ /** How many rows of bars stand in depth: one a series, or one in all when stacked. */
57
+ lanes: number;
47
58
  /** How many cells hold a value. */
48
59
  readings: number;
60
+ /**
61
+ * How far a `diagonal` row climbs on the page for each step across, where
62
+ * it climbs less than Switchyard's 15 for every 72: a long row in a short
63
+ * plot, whose climb would leave its bars no height.
64
+ */
65
+ climb?: number;
49
66
  /** Every series, in the table's order: each numeric column's heading. */
50
67
  series: string[];
68
+ /** Whether a category's series stand in one column, each on the ones before it. */
69
+ stacked: boolean;
51
70
  }
52
71
  /**
53
72
  * Reads the table as a bar chart does: **each row is a category, named by
@@ -59,10 +78,46 @@ export interface BarField {
59
78
  * **The value axis runs from zero**, whatever the values, so a bar's height
60
79
  * is its value: one twice as tall is twice as much. It widens to round
61
80
  * numbers past the highest value, and below zero where a value is negative.
81
+ *
82
+ * **`stacked` stands a category's series in one column**, each block on the
83
+ * ones before it, so the column is as tall as the category's total and the
84
+ * axis runs to the tallest total. A negative reading hangs below zero on the
85
+ * negatives before it, as a flat chart's stack does.
86
+ *
87
+ * `toneOf` gives the tone a cell names, by its row and its series.
88
+ */
89
+ export declare const barsOf: (rows: readonly FieldRow[], columns: readonly number[], stacked?: boolean, toneOf?: (row: number, depth: number) => string | undefined) => BarField;
90
+ /**
91
+ * How the bars stand: on the box's grid, in a row level across the page, or
92
+ * in a row that climbs to the right as it recedes.
62
93
  */
63
- export declare const barsOf: (rows: readonly FieldRow[], columns: readonly number[]) => BarField;
94
+ export type BarLayout = 'diagonal' | 'grid' | 'row';
95
+ /**
96
+ * How many categories a row has room for across `width` pixels with each bar
97
+ * `size` pixels wide: a bar covers `FOOTPRINT` of its place, so a place is
98
+ * that much wider than its bar. At least one.
99
+ */
100
+ export declare const rowFits: (width: number, size: number) => number;
101
+ /** How wide a bar is on the page, in the drawing's units. */
102
+ export declare const widthOf: (bar: DrawnBar) => number;
103
+ /**
104
+ * The field with its `diagonal` row climbing at most `rise` on the page over
105
+ * `length` across, so a long row keeps the height its bars need.
106
+ */
107
+ export declare const climbing: (field: BarField, rise: number, length: number) => BarField;
108
+ /**
109
+ * The most times taller a row's value axis may stand than it is drawn in
110
+ * `bars`, so the tallest bar is no taller than `ROW_TALL` times the row's
111
+ * length: each of its places is a bar's width over `FOOTPRINT`, the width
112
+ * `widthOf` gives on the page.
113
+ */
114
+ export declare const tallestTimes: (field: BarField, projection: Projection, bars: readonly DrawnBar[]) => number;
115
+ /** The field with its value axis `times` as tall, which stands every bar that much taller. */
116
+ export declare const heightened: (field: BarField, times: number) => BarField;
64
117
  /** A bar as the chart's own drawing paints it. */
65
118
  export interface DrawnBar {
119
+ /** Where the block starts on the floor's way up, in the box's units. */
120
+ base: number;
66
121
  /** The block's eight corners on the page, which a drawing with no box is framed around. */
67
122
  corners: PagePoint[];
68
123
  drawn: Drawn;
@@ -73,7 +128,72 @@ export interface DrawnBar {
73
128
  }
74
129
  /**
75
130
  * 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.
131
+ * its place on the grid, from where it starts to its value, a little
132
+ * narrower than its place so neighbors stand apart. A value below zero
133
+ * hangs from where it starts.
134
+ *
135
+ * **`row` lays the categories in a straight line across the page**, along
136
+ * the way the camera reads as level, and the series straight back from it,
137
+ * so the row stays level whichever way the camera turns, and each block,
138
+ * which keeps to the floor's grid, shows the turn: its top and two sides.
139
+ * Turned, a block is wider on the page than its footprint, so the footprint
140
+ * narrows by as much, and neighbors still stand apart. **`diagonal` lays the
141
+ * same row climbing to the right**, each place a step further back than the
142
+ * one before, at the same climb on the page from any turn.
143
+ *
144
+ * `grown` says how much of its height each bar has reached, from 0 to 1, as
145
+ * the chart grows its bars in.
146
+ */
147
+ export declare const barsDrawn: (field: BarField, projection: Projection, layout?: BarLayout, grown?: (cell: BarCell) => number) => DrawnBar[];
148
+ /**
149
+ * The bars in the order they are painted, back to front, and **a stacked
150
+ * column from the floor up**: its blocks share a place on the floor, which
151
+ * the order by depth cannot tell apart, and one painted under the block below
152
+ * it would show the lower block's top through the upper one.
153
+ */
154
+ export declare const paintOrder: (projection: Projection, bars: readonly DrawnBar[]) => DrawnBar[];
155
+ /** One place on the floor of a row, under a bar or where a bar would stand. */
156
+ export interface FloorPlate {
157
+ /** Which category it stands under, counting from zero. */
158
+ across: number;
159
+ /** Its four corners on the page. */
160
+ corners: PagePoint[];
161
+ /** Its outline, as path data. */
162
+ d: string;
163
+ /** Its corner lowest on the page, nearest the reader, which its name is written under. */
164
+ front: PagePoint;
165
+ }
166
+ /**
167
+ * The places on the floor of a row, one for each category in each lane,
168
+ * whether a bar stands there or not: a square a little wider than a bar's
169
+ * footprint, turned as the bars are, where zero stands on the value axis,
170
+ * along the row `layout` lays.
171
+ */
172
+ export declare const floorOf: (field: BarField, projection: Projection, layout?: Exclude<BarLayout, 'grid'>) => FloorPlate[];
173
+ /** A category's name written under its place in a row. */
174
+ export interface RowName {
175
+ across: number;
176
+ text: string;
177
+ /** Whether a bar of its category names a tone, which colors the name. */
178
+ tone?: string | undefined;
179
+ x: number;
180
+ /** The middle of the line of text. */
181
+ y: number;
182
+ }
183
+ /**
184
+ * The names written under a row's plates, `size` tall: each category's name
185
+ * under its plate nearest the reader, **and only the names that fit**, so
186
+ * none overlaps another. A name whose bar names a tone is written first,
187
+ * then every other name, or every second, third, and so on, at the smallest
188
+ * even step that fits, leaving out a name the toned one covers.
189
+ */
190
+ export declare const namesOf: (field: BarField, plates: readonly FloorPlate[], size: number) => RowName[];
191
+ /**
192
+ * How much of its height a bar of category `across` has reached `elapsed`
193
+ * milliseconds after the bars began to grow in, from 0 to 1. **In reading
194
+ * order**, each category a little after the one before it, and fast at first
195
+ * and slow to rest, never past its value.
78
196
  */
79
- export declare const barsDrawn: (field: BarField, projection: Projection) => DrawnBar[];
197
+ export declare const grownAt: (across: number, categories: number, elapsed: number) => number;
198
+ /** How long the bars of `categories` take to grow in, the last one included, in milliseconds. */
199
+ export declare const growTime: (categories: number) => number;