@fusion.dev/helix-headless 0.218.0 → 0.219.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 (74) 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/copy-button/root/copy-button-root.js +3 -3
  18. package/dist/device-screen/root/device-screen-root.d.ts +11 -0
  19. package/dist/device-screen/root/device-screen-root.js +22 -10
  20. package/dist/dialog/root/dialog-root.js +6 -1
  21. package/dist/drilldown/item/drilldown-item.js +1 -1
  22. package/dist/element-events.generated.js +1 -0
  23. package/dist/filter-input/root/filter-input-root.d.ts +4 -3
  24. package/dist/filter-input/root/filter-input-root.js +1 -20
  25. package/dist/graph/root/graph-root.d.ts +11 -0
  26. package/dist/graph/root/graph-root.js +4 -1
  27. package/dist/input/root/input-root.d.ts +6 -0
  28. package/dist/input/root/input-root.js +7 -0
  29. package/dist/lightbox/root/lightbox-root.d.ts +4 -1
  30. package/dist/lightbox/root/lightbox-root.js +10 -8
  31. package/dist/listbox/root/listbox-root.d.ts +21 -0
  32. package/dist/listbox/root/listbox-root.js +37 -2
  33. package/dist/log/group/log-group.d.ts +7 -1
  34. package/dist/log/group/log-group.js +14 -1
  35. package/dist/log/root/log-root.d.ts +13 -3
  36. package/dist/log/root/log-root.js +5 -2
  37. package/dist/map/root/map-root.d.ts +11 -0
  38. package/dist/map/root/map-root.js +28 -12
  39. package/dist/menu/root/menu-root.d.ts +8 -0
  40. package/dist/menu/root/menu-root.js +22 -1
  41. package/dist/menu/section/menu-section.d.ts +10 -0
  42. package/dist/menu/section/menu-section.js +10 -0
  43. package/dist/meter/index.d.ts +1 -0
  44. package/dist/meter/index.js +1 -0
  45. package/dist/meter/root/meter-root.d.ts +16 -1
  46. package/dist/meter/root/meter-root.js +27 -11
  47. package/dist/meter/segment/meter-segment.d.ts +74 -0
  48. package/dist/meter/segment/meter-segment.js +47 -0
  49. package/dist/scroll-area/root/scroll-area-root.d.ts +20 -3
  50. package/dist/scroll-area/root/scroll-area-root.js +23 -4
  51. package/dist/spatial-bar-chart/bars.d.ts +125 -5
  52. package/dist/spatial-bar-chart/bars.js +194 -17
  53. package/dist/spatial-bar-chart/root/spatial-bar-chart-root.d.ts +148 -6
  54. package/dist/spatial-bar-chart/root/spatial-bar-chart-root.js +285 -16
  55. package/dist/splitter/root/splitter-root.js +1 -0
  56. package/dist/tabs/root/tabs-root.d.ts +4 -0
  57. package/dist/tabs/tab/tab.d.ts +1 -0
  58. package/dist/tabs/tab/tab.js +2 -2
  59. package/dist/tag/root/tag-root.d.ts +1 -1
  60. package/dist/terminal/root/terminal-root.d.ts +11 -0
  61. package/dist/terminal/root/terminal-root.js +23 -10
  62. package/dist/toolbar/root/toolbar-root.d.ts +4 -1
  63. package/dist/tooltip/root/tooltip-root.d.ts +23 -1
  64. package/dist/tooltip/root/tooltip-root.js +28 -5
  65. package/dist/tour/root/tour-root.d.ts +1 -0
  66. package/dist/tour/root/tour-root.js +4 -1
  67. package/dist/tour/stop/tour-stop.js +1 -0
  68. package/examples.json +1 -1
  69. package/lint.json +1 -1
  70. package/package.json +5 -5
  71. package/types/jsx.d.ts +1373 -52
  72. package/types/svelte.d.ts +1 -0
  73. package/types/vue.d.ts +312 -17
  74. package/types/words.d.ts +9 -1
@@ -13,6 +13,11 @@ declare const HelixHeadlessSpatialBarChart_base: typeof HelixElement & (new (...
13
13
  * depth, each as tall as its value from zero and colored by its series, so
14
14
  * one series is a single row of bars and several make a block of them.
15
15
  *
16
+ * **A cell's `data-tone` colors its own bar and writes its value above it**,
17
+ * such as `data-tone="primary"` on the one reading a chart is about, as on
18
+ * `Bar Chart`. The value is the cell's own text. Without a tone, a bar takes
19
+ * its series' color.
20
+ *
16
21
  * **The flat `Bar Chart` is read more exactly.** Measured, a reader judges
17
22
  * a 3D bar's value less closely than a flat one's, and a bar can stand
18
23
  * behind another. Use this one where the shape across two sets of names
@@ -79,6 +84,8 @@ declare const HelixHeadlessSpatialBarChart_base: typeof HelixElement & (new (...
79
84
  * @csspart empty - The words when the table has no readings: the `empty` slot, falling back to `empty-text`.
80
85
  * @csspart face - One face of a bar the view shows, shaded by which way it faces.
81
86
  * @csspart fallback - The box the chart's own drawing fills, shown until a renderer has drawn and where none is connected. Hidden from screen readers.
87
+ * @csspart floor - One place on the floor of a `row`, under a bar or where one would stand, drawn as its outline.
88
+ * @csspart glow - The outline of a bar whose cell names a tone, blurred by 8 CSS pixels and painted under the bar, with no fill until a stylesheet gives it one: a soft glow in the bar's tone.
82
89
  * @csspart grid - The box's edges and its grid, in the chart's own drawing.
83
90
  * @csspart live - The readout: a polite live region that says the bar the keys moved to. Hide it visually.
84
91
  * @csspart loading - The words while `loading` is set and there are no readings.
@@ -90,7 +97,7 @@ declare const HelixHeadlessSpatialBarChart_base: typeof HelixElement & (new (...
90
97
  * @csspart summary - The number of bars, series, and categories, and the range of values, written as a sentence for screen readers, which describes the plot. Hide it visually.
91
98
  * @csspart table - The readings as a table, for a screen reader, rendered only where they were drawn from `data`, a page of them at a time past a few hundred. Hide it visually.
92
99
  * @csspart table-pages - Which readings a page of the `table` holds, and the buttons that show the previous and the next page, rendered only while it holds a page. Hide it visually.
93
- * @csspart tick - A category's or a series' name, or a value at a tick, along its edge of the box, in the chart's own drawing.
100
+ * @csspart tick - A category's or a series' name, or a value at a tick, along its edge of the box, or a category's name under its place in a `row`, in the chart's own drawing.
94
101
  * @csspart tooltip-anchor - The `Tooltip` element itself.
95
102
  * @csspart tooltip-arrow - The bubble's point, forwarded from `Tooltip`.
96
103
  * @csspart tooltip-arrow-bottom - The point while the bubble is below the reading, on its top edge. Set alongside `tooltip-arrow`.
@@ -107,8 +114,16 @@ declare const HelixHeadlessSpatialBarChart_base: typeof HelixElement & (new (...
107
114
  * @csspart tooltip-surface-right - The bubble while it is right of the reading. Set alongside `tooltip-surface`.
108
115
  * @csspart tooltip-surface-top - The bubble while it is above the reading. Set alongside `tooltip-surface`.
109
116
  * @csspart tooltip-value - A column's value inside the tooltip.
117
+ * @csspart value - The value of a bar whose cell names a tone, written above it in the chart's own drawing.
110
118
  *
111
- * @cssproperty --spatial-bar-chart-shade - How far a bar's face turns from the light, from 0 for its top. The component writes it on each face, and the stylesheet that shades the face reads it.
119
+ * @cssproperty --spatial-bar-chart-shade -How far a bar's face turns from the light, from 0 for its top. The component writes it on each face, and the stylesheet that shades the face reads it.
120
+ * @cssproperty --spatial-bar-chart-tone-accent - The color of a bar whose cell says `data-tone="accent"`.
121
+ * @cssproperty --spatial-bar-chart-tone-danger - The color of a bar whose cell says `data-tone="danger"`.
122
+ * @cssproperty --spatial-bar-chart-tone-info - The color of a bar whose cell says `data-tone="info"`.
123
+ * @cssproperty --spatial-bar-chart-tone-muted - The color of a bar whose cell says `data-tone="muted"`.
124
+ * @cssproperty --spatial-bar-chart-tone-primary - The color of a bar whose cell says `data-tone="primary"`. A tone with no property of its own falls back to the bar's series color, and the same holds for a name you define.
125
+ * @cssproperty --spatial-bar-chart-tone-success - The color of a bar whose cell says `data-tone="success"`.
126
+ * @cssproperty --spatial-bar-chart-tone-warning - The color of a bar whose cell says `data-tone="warning"`.
112
127
  *
113
128
  * @cssstate active - While a bar is shown, under the pointer or the keys.
114
129
  * @cssstate drawn - While a renderer has drawn the bars, and the chart's own drawing is hidden.
@@ -122,8 +137,32 @@ export declare class HelixHeadlessSpatialBarChart extends HelixHeadlessSpatialBa
122
137
  */
123
138
  static words: string;
124
139
  /**
125
- * Draws no box: no edges, grid, numbers, or names, so the readings stand
126
- * alone and fill the plot, as a picture on a product's own page does. The
140
+ * Grows the bars up from the floor the first time the chart comes into
141
+ * view with readings, each category a little after the one before it, so
142
+ * the drawing arrives the way a page's own picture does. The growth is
143
+ * fast at first, slows to rest, and never passes a bar's value.
144
+ *
145
+ * Under `prefers-reduced-motion`, or where the page has paused its motion
146
+ * through `pauseMotion` or `Motion Toggle`, the bars stand at their height
147
+ * from the start. A later table draws the bars it already had at their
148
+ * height, and grows the bars it adds, each from the floor, so a reading that
149
+ * arrives while the chart is in view arrives the same way.
150
+ */
151
+ grow: boolean;
152
+ /**
153
+ * Grows the bars again each time the chart comes back into view after it
154
+ * left it, as a pane does when it is opened again, where `grow` alone grows
155
+ * them the first time only. Only a chart that grows takes it.
156
+ *
157
+ * While it is out of view the bars stand at the floor, so the first frame
158
+ * after it returns is the start of the growth and never a flash at full
159
+ * height.
160
+ */
161
+ growAgain: boolean;
162
+ /**
163
+ * Draws no box: no edges, grid, numbers, or names, and in a `row` no
164
+ * floor, so the readings stand alone and fill the plot, as a picture on a
165
+ * product's own page does. The
127
166
  * table, the summary, the tooltip, and the keys still carry every value,
128
167
  * and a renderer draws no box either.
129
168
  */
@@ -135,12 +174,57 @@ export declare class HelixHeadlessSpatialBarChart extends HelixHeadlessSpatialBa
135
174
  openLabel: string;
136
175
  /** The plot's name where the chart has none: no `aria-label`, `label`, or table caption. */
137
176
  plotLabel: string;
177
+ /**
178
+ * Stacks a category's series into one column, each block standing on the
179
+ * ones before it, so the column is as tall as the category's total, as a
180
+ * flat `Bar Chart` stacks them.
181
+ *
182
+ * Absent stands the series one behind another in depth. Stacked, every
183
+ * series stands in the front row, so none hides another, and the parts of
184
+ * one total read together. A negative reading hangs below zero on the
185
+ * negatives before it. The scale fits the stacks.
186
+ */
187
+ stacked: boolean;
138
188
  /**
139
189
  * The `summary` part's sentence. `{count}`, `{series}`, and `{categories}`
140
190
  * are the number of bars, series, and categories, written in the locale,
141
191
  * and `{low}` and `{high}` the lowest and the highest value.
142
192
  */
143
193
  summaryLabel: string;
194
+ /**
195
+ * How the bars stand.
196
+ *
197
+ * Unset, they stand on a grid of categories across and series in depth,
198
+ * in a box the camera looks at from a corner, so the grid recedes at an
199
+ * angle. `row` lays the categories in a straight line across the page and
200
+ * turns each bar to the camera, showing its top and two sides, for one row
201
+ * of readings in a small space such as a pane: turning the view turns each
202
+ * bar where it stands, and the row stays level. It is the chart's own
203
+ * drawing that lays the row: a renderer connected through
204
+ * `@fusion.dev/helix-plot` stands the bars on the grid.
205
+ *
206
+ * `diagonal` is the same row climbing to the right as it recedes, each
207
+ * place a step further back than the one before, as a row of days on a
208
+ * product's page is drawn: the row rises one part in five across the page
209
+ * from any turn, and its floor and names climb with it. A row whose bars
210
+ * have a size climbs at most a quarter of the plot's height, so the bars
211
+ * of a long row keep theirs.
212
+ *
213
+ * **A row draws its floor in place of a box**: each place as an outline a
214
+ * little wider than its bar, and each category's name under it, as many
215
+ * names as fit without touching. A name whose bar names a tone is written
216
+ * first, in that tone. `no-box` leaves the floor and the names out.
217
+ *
218
+ * **A `column-gap` on the `fallback` part gives a row's bars one width**,
219
+ * which the styled tier sets from `--spatial-bar-chart-bar-size`: one bar
220
+ * stands as wide as each of twenty, the row is as long as its bars and
221
+ * centered, the tallest bar stands as high as the plot has room for and
222
+ * no taller than the row is long and a quarter again, so one bar stands
223
+ * as a block, and `fits` says how many bars the plot holds, so a page
224
+ * can give it the latest of a longer table. It is read when the plot's
225
+ * size changes.
226
+ */
227
+ variant?: 'diagonal' | 'row';
144
228
  /** Whether a renderer has drawn the bars, which hides the chart's own drawing. */
145
229
  private rendered;
146
230
  /** Where the chart's own drawing is seen from, which a drag or Shift with an arrow turns. */
@@ -153,6 +237,34 @@ export declare class HelixHeadlessSpatialBarChart extends HelixHeadlessSpatialBa
153
237
  private last;
154
238
  /** The bar the keys rested on last, so a change of it can be told. */
155
239
  private told;
240
+ /**
241
+ * The size of the box the chart's own drawing fills, its font size, and
242
+ * the bar size the stylesheet asks for, in CSS pixels, once measured: the
243
+ * names and values are written at that size whatever the drawing's scale.
244
+ */
245
+ private room;
246
+ /** Measures the box the chart's own drawing fills. */
247
+ private sizes;
248
+ /**
249
+ * How far the bars have grown in: waiting to be seen, growing since a
250
+ * moment, or `undefined` once they stand at their height. `grew` says the
251
+ * first table has been drawn, so a later one is not grown again.
252
+ */
253
+ private growth;
254
+ private grew;
255
+ /**
256
+ * The bars a later table added, by category label, each growing from the
257
+ * floor: when it started, and its place among the bars that came with it, so
258
+ * several arriving together follow one another as the first table's do. A
259
+ * bar leaves the map once it stands.
260
+ */
261
+ private readonly fresh;
262
+ /** The categories of the table drawn last, so a later table's new ones are known. */
263
+ private drawnLabels;
264
+ /** Watches for the chart to leave the view, after which `grow-again` has it grow in once more. */
265
+ private gone;
266
+ /** Watches for the chart to come into view, which starts the bars growing. */
267
+ private seen;
156
268
  /** Carries a host `aria-labelledby` and `aria-describedby` to the plot. */
157
269
  protected readonly aria: AriaForwardController;
158
270
  /**
@@ -160,7 +272,7 @@ export declare class HelixHeadlessSpatialBarChart extends HelixHeadlessSpatialBa
160
272
  * column a series.** A renderer reads it.
161
273
  */
162
274
  get field(): BarField;
163
- /** The field last read, and the rows and columns it was read from. */
275
+ /** The field last read, and the rows, columns, and series it was read from. */
164
276
  private memo;
165
277
  /** The bar the keys or the pointer rest on, as its place in `field.cells`, or `-1`. */
166
278
  get active(): number;
@@ -194,6 +306,36 @@ export declare class HelixHeadlessSpatialBarChart extends HelixHeadlessSpatialBa
194
306
  * the server calls this one.
195
307
  */
196
308
  protected willUpdate(changed: PropertyValues<this>): void;
309
+ /** Draws each frame until the bars a later table added stand. */
310
+ private followFresh;
311
+ disconnectedCallback(): void;
312
+ /** The element the size observer watches, so a drawing drawn again is watched again. */
313
+ private measured;
314
+ /** Watches the box the chart's own drawing fills, and keeps its size and font size. */
315
+ private measure;
316
+ /**
317
+ * How many categories a `row` or a `diagonal` has room for across the
318
+ * plot at its bar size, measured as it is read, or
319
+ * `undefined` where there is no row, no bar size, or nothing drawn to
320
+ * measure. A page with a longer table gives the chart its latest that
321
+ * many rows, and says so.
322
+ */
323
+ get fits(): number | undefined;
324
+ /**
325
+ * A row with a bar size of its own: how many of the drawing's units make a
326
+ * pixel, so each bar is as wide as asked, and the field with its value axis
327
+ * as tall as fills the plot's height with the names and the value. Where
328
+ * the row is too long for the plot, its bars narrow until it fits.
329
+ */
330
+ private sized;
331
+ /** Starts the bars growing once the chart comes into view, and draws each frame until they stand. */
332
+ private watchGrowth;
333
+ /**
334
+ * Under `grow-again`, waits for the chart to leave the view, then puts the
335
+ * bars back at the floor and waits to be seen, so growing again starts from
336
+ * the same place the first time did.
337
+ */
338
+ private watchExit;
197
339
  connectedCallback(): void;
198
340
  protected updated(changed: PropertyValues): void;
199
341
  /**
@@ -222,7 +364,7 @@ export declare class HelixHeadlessSpatialBarChart extends HelixHeadlessSpatialBa
222
364
  * drawing, until a renderer has drawn and where none is connected.
223
365
  */
224
366
  private renderFallback;
225
- /** One bar, its faces in its series' color, each shaded by which way it faces. */
367
+ /** One bar, its faces in its tone's color or its series', each shaded by which way it faces. */
226
368
  private renderBar;
227
369
  /** The readout: the bar's category, series, and value in words, once the keys have moved to one. */
228
370
  private renderLive;
@@ -1,10 +1,16 @@
1
1
  import { __decorate } from "tslib";
2
- import { AriaForwardController, CHART_PLOT, customElement, depthOrder, drawTooltip, formatWords, HelixElement, HelixPlotChangeEvent, HelixPlotTurnEvent, HelixReadingChangeEvent, HelixReadingPressEvent, isRtl, MixinChartSource, MixinChartStatus, spokenReading, words, } from '@fusion.dev/helix-core';
2
+ import { AriaForwardController, CHART_PLOT, customElement, drawTooltip, formatWords, HelixElement, HelixPlotChangeEvent, HelixPlotTurnEvent, HelixReadingChangeEvent, HelixReadingPressEvent, isRtl, MixinChartSource, MixinChartStatus, motionPaused, reducedMotion, shortNumber, spokenReading, words, } from '@fusion.dev/helix-core';
3
3
  import { html, nothing, svg, } from 'lit';
4
4
  import { property, state } from 'lit/decorators.js';
5
5
  import '../../tooltip/index.js';
6
- import { boundsOf, boxView, DragTurn, drawBox, SPATIAL_VIEW, turned, } from '../../spatial-scatter-chart/view.js';
7
- import { barsDrawn, barsOf } from '../bars.js';
6
+ import { boundsOf, boxView, DragTurn, drawBox, SPATIAL_VIEW, TICK_SIZE, turned, } from '../../spatial-scatter-chart/view.js';
7
+ import { barsDrawn, barsOf, climbing, floorOf, grownAt, growTime, heightened, namesOf, paintOrder, rowFits, tallestTimes, widthOf, } from '../bars.js';
8
+ const barSizeOf = (style) => {
9
+ const size = Number.parseFloat(style.columnGap);
10
+ return Number.isFinite(size) && size > 0 ? size : undefined;
11
+ };
12
+ const WORD_LINES = 3.6;
13
+ const CLIMB_SHARE = 0.25;
8
14
  const TURNS = {
9
15
  ArrowDown: { across: 0, up: -1 },
10
16
  ArrowLeft: { across: -1, up: 0 },
@@ -12,12 +18,22 @@ const TURNS = {
12
18
  ArrowUp: { across: 0, up: 1 },
13
19
  };
14
20
  const SERIES = 24;
21
+ const toned = (tone) => tone && /^[a-z][a-z0-9-]*$/.test(tone) ? tone : undefined;
22
+ const around = (frame, points) => {
23
+ const xs = [frame.x, frame.x + frame.width, ...points.map((point) => point.x)];
24
+ const ys = [frame.y, frame.y + frame.height, ...points.map((point) => point.y)];
25
+ const [left, top] = [Math.min(...xs), Math.min(...ys)];
26
+ return { height: Math.max(...ys) - top, width: Math.max(...xs) - left, x: left, y: top };
27
+ };
15
28
  let HelixHeadlessSpatialBarChart = class HelixHeadlessSpatialBarChart extends MixinChartStatus(MixinChartSource(HelixElement)) {
16
29
  constructor() {
17
30
  super(...arguments);
31
+ this.grow = false;
32
+ this.growAgain = false;
18
33
  this.noBox = false;
19
34
  this.openLabel = '{category}, {series}: no value';
20
35
  this.plotLabel = '3D bar chart';
36
+ this.stacked = false;
21
37
  this.summaryLabel = '{count, plural, one {# bar} other {# bars}} in {series, plural, one {# series} other {# series}} over {categories, plural, one {# category} other {# categories}}, from {low} to {high}.';
22
38
  this.rendered = false;
23
39
  this.camera = SPATIAL_VIEW;
@@ -25,6 +41,8 @@ let HelixHeadlessSpatialBarChart = class HelixHeadlessSpatialBarChart extends Mi
25
41
  this.camera = next(this.camera);
26
42
  }, () => this.requestUpdate());
27
43
  this.told = -1;
44
+ this.grew = false;
45
+ this.fresh = new Map();
28
46
  this.aria = new AriaForwardController(this, {
29
47
  target: () => this.partNamed('plot'),
30
48
  });
@@ -71,8 +89,14 @@ let HelixHeadlessSpatialBarChart = class HelixHeadlessSpatialBarChart extends Mi
71
89
  get field() {
72
90
  const rows = this.sourceRows;
73
91
  const columns = this.plottedColumns;
74
- if (this.memo?.rows !== rows || this.memo.columns !== columns) {
75
- this.memo = { columns, field: barsOf(rows, columns), rows };
92
+ const series = this.series;
93
+ if (this.memo?.rows !== rows ||
94
+ this.memo.columns !== columns ||
95
+ this.memo.series !== series ||
96
+ this.memo.stacked !== this.stacked) {
97
+ const tones = series.map((one) => new Map(one.points.map((point) => [point.at, toned(point.tone)])));
98
+ const field = barsOf(rows, columns, this.stacked, (row, depth) => tones[depth]?.get(row));
99
+ this.memo = { columns, field, rows, series, stacked: this.stacked };
76
100
  }
77
101
  return this.memo.field;
78
102
  }
@@ -99,10 +123,158 @@ let HelixHeadlessSpatialBarChart = class HelixHeadlessSpatialBarChart extends Mi
99
123
  }
100
124
  willUpdate(changed) {
101
125
  super.willUpdate(changed);
102
- this.pointer.grid = Math.max(1, this.field.series.length);
126
+ const field = this.field;
127
+ this.pointer.grid = Math.max(1, field.series.length);
128
+ const labels = new Set(field.categories);
129
+ const moves = typeof IntersectionObserver === 'function' &&
130
+ typeof requestAnimationFrame === 'function' &&
131
+ !reducedMotion() &&
132
+ !motionPaused();
133
+ if (this.grow && !this.grew && field.readings > 0) {
134
+ this.grew = true;
135
+ this.growth = moves ? {} : undefined;
136
+ }
137
+ else if (this.grow && this.grew && field.readings > 0 && this.drawnLabels && moves) {
138
+ const added = [...labels].filter((label) => !this.drawnLabels?.has(label));
139
+ if (added.length > 0 && !this.growth) {
140
+ const since = performance.now();
141
+ added.forEach((label, index) => {
142
+ this.fresh.set(label, { count: added.length, index, since });
143
+ });
144
+ this.followFresh();
145
+ }
146
+ }
147
+ if (field.readings > 0) {
148
+ this.drawnLabels = labels;
149
+ }
150
+ }
151
+ followFresh() {
152
+ const frame = () => {
153
+ const now = performance.now();
154
+ for (const [label, one] of this.fresh) {
155
+ if (now - one.since >= growTime(one.count)) {
156
+ this.fresh.delete(label);
157
+ }
158
+ }
159
+ this.requestUpdate();
160
+ if (this.fresh.size > 0) {
161
+ requestAnimationFrame(frame);
162
+ }
163
+ };
164
+ requestAnimationFrame(frame);
165
+ }
166
+ disconnectedCallback() {
167
+ super.disconnectedCallback();
168
+ this.sizes?.disconnect();
169
+ this.sizes = undefined;
170
+ this.measured = undefined;
171
+ this.seen?.disconnect();
172
+ this.seen = undefined;
173
+ this.gone?.disconnect();
174
+ this.gone = undefined;
175
+ }
176
+ measure() {
177
+ const box = this.partNamed('fallback');
178
+ if (!box || box === this.measured || typeof ResizeObserver !== 'function') {
179
+ return;
180
+ }
181
+ this.measured = box;
182
+ this.sizes?.disconnect();
183
+ this.sizes = new ResizeObserver(([entry]) => {
184
+ const { height, width } = entry?.contentRect ?? { height: 0, width: 0 };
185
+ const style = getComputedStyle(box);
186
+ const font = Number.parseFloat(style.fontSize) || 16;
187
+ const bar = barSizeOf(style);
188
+ const was = this.room;
189
+ if (was?.width !== width || was.height !== height || was.font !== font || was.bar !== bar) {
190
+ this.room =
191
+ height > 0 && width > 0
192
+ ? { font, height, width, ...(bar && bar > 0 ? { bar } : {}) }
193
+ : undefined;
194
+ }
195
+ });
196
+ this.sizes.observe(box);
197
+ }
198
+ get fits() {
199
+ const box = this.partNamed('fallback');
200
+ if (!box || !this.variant || typeof getComputedStyle !== 'function') {
201
+ return undefined;
202
+ }
203
+ const style = getComputedStyle(box);
204
+ const bar = barSizeOf(style);
205
+ const width = box.clientWidth;
206
+ return bar && width > 0 ? rowFits(width, bar) : undefined;
207
+ }
208
+ sized(read, projection, layout, room) {
209
+ const first = barsDrawn(read, projection, layout)[0];
210
+ if (!first || !room.bar) {
211
+ return undefined;
212
+ }
213
+ const frameOf = (field, grown) => {
214
+ const floor = this.noBox ? [] : floorOf(field, projection, layout);
215
+ const drawn = barsDrawn(field, projection, layout, grown);
216
+ return boundsOf([...drawn.flatMap((bar) => bar.corners), ...floor.flatMap((one) => one.corners)], 0);
217
+ };
218
+ const unit = Math.max(widthOf(first) / room.bar, frameOf(read, () => 0).width / Math.max(1, room.width - room.font * 2));
219
+ const field = climbing(read, room.height * CLIMB_SHARE * unit, frameOf(read, () => 0).width);
220
+ const low = frameOf(field, () => 0);
221
+ const high = frameOf(field);
222
+ const tall = (room.height - room.font * WORD_LINES) * unit;
223
+ const rises = high.height - low.height;
224
+ const fills = rises > 0 ? Math.max(0.05, (tall - low.height) / rises) : 1;
225
+ const times = Math.min(fills, tallestTimes(field, projection, barsDrawn(field, projection, layout)));
226
+ return { field: heightened(field, times), unit };
227
+ }
228
+ watchGrowth() {
229
+ if (!this.growth || this.growth.since !== undefined || this.seen) {
230
+ return;
231
+ }
232
+ this.seen = new IntersectionObserver(([entry]) => {
233
+ if (!entry?.isIntersecting || !this.growth) {
234
+ return;
235
+ }
236
+ this.seen?.disconnect();
237
+ this.seen = undefined;
238
+ const since = performance.now();
239
+ this.growth = { since };
240
+ const length = growTime(this.field.categories.length);
241
+ const frame = () => {
242
+ if (this.growth?.since !== since) {
243
+ return;
244
+ }
245
+ if (performance.now() - since >= length) {
246
+ this.growth = undefined;
247
+ this.watchExit();
248
+ return;
249
+ }
250
+ this.requestUpdate();
251
+ requestAnimationFrame(frame);
252
+ };
253
+ requestAnimationFrame(frame);
254
+ }, { threshold: 0.4 });
255
+ this.seen.observe(this);
256
+ }
257
+ watchExit() {
258
+ if (!this.growAgain || this.gone) {
259
+ return;
260
+ }
261
+ this.gone = new IntersectionObserver(([entry]) => {
262
+ if (!entry || entry.isIntersecting) {
263
+ return;
264
+ }
265
+ this.gone?.disconnect();
266
+ this.gone = undefined;
267
+ this.fresh.clear();
268
+ this.growth = {};
269
+ });
270
+ this.gone.observe(this);
103
271
  }
104
272
  connectedCallback() {
105
273
  super.connectedCallback();
274
+ if (this.hasUpdated) {
275
+ this.measure();
276
+ this.watchGrowth();
277
+ }
106
278
  this.pointer.axis = 'none';
107
279
  this.pointer.onReading = (heard, detail) => {
108
280
  const cell = this.field.cells[detail.index];
@@ -116,6 +288,8 @@ let HelixHeadlessSpatialBarChart = class HelixHeadlessSpatialBarChart extends Mi
116
288
  }
117
289
  updated(changed) {
118
290
  super.updated(changed);
291
+ this.measure();
292
+ this.watchGrowth();
119
293
  const field = this.field;
120
294
  if (field !== this.last) {
121
295
  this.last = field;
@@ -156,14 +330,85 @@ let HelixHeadlessSpatialBarChart = class HelixHeadlessSpatialBarChart extends Mi
156
330
  </div>
157
331
  ${readable ? this.renderSummary(field) : this.renderStatus()}${this.renderSonify()}${this.renderTable()}`;
158
332
  }
159
- renderFallback(field, at) {
160
- const view = boxView(field.axes, this.camera, [], this.noBox);
161
- const bars = barsDrawn(field, view.projection);
333
+ renderFallback(read, at) {
334
+ const layout = this.variant ?? 'grid';
335
+ const lined = layout === 'grid' ? undefined : layout;
336
+ const bare = this.noBox || lined !== undefined;
337
+ const view = boxView(read.axes, this.camera, [], bare);
338
+ const room = this.room;
339
+ const sized = lined && room?.bar ? this.sized(read, view.projection, lined, room) : undefined;
340
+ const field = sized?.field ?? read;
341
+ const full = barsDrawn(field, view.projection, layout);
342
+ const growth = this.growth;
343
+ const since = growth?.since;
344
+ const elapsed = since === undefined ? 0 : performance.now() - since;
345
+ const categories = field.categories.length;
346
+ const now = performance.now();
347
+ const bars = growth
348
+ ? barsDrawn(field, view.projection, layout, (one) => grownAt(one.across, categories, elapsed))
349
+ : this.fresh.size > 0
350
+ ? barsDrawn(field, view.projection, layout, (one) => {
351
+ const added = this.fresh.get(one.label);
352
+ return added ? grownAt(added.index, added.count, now - added.since) : 1;
353
+ })
354
+ : full;
355
+ const floor = lined && !this.noBox ? floorOf(field, view.projection, lined) : [];
162
356
  const active = bars.find((bar) => bar.index === at);
163
357
  const cell = field.cells[at];
164
- const bounds = this.drag.frame(this.noBox
165
- ? boundsOf(bars.flatMap((bar) => bar.corners), 0.1)
166
- : view.bounds, view.on([0, 0, 0]));
358
+ const base = bare
359
+ ? boundsOf([...full.flatMap((bar) => bar.corners), ...floor.flatMap((one) => one.corners)], 0.1)
360
+ : view.bounds;
361
+ const pixelOf = (frame) => sized
362
+ ? sized.unit
363
+ : room
364
+ ? 1 / Math.min(room.width / frame.width, room.height / frame.height)
365
+ : undefined;
366
+ const sizeOf = (frame) => {
367
+ const pixel = pixelOf(frame);
368
+ return room && pixel !== undefined ? room.font * pixel : TICK_SIZE;
369
+ };
370
+ const wordsAt = (size) => {
371
+ const names = floor.length > 0 ? namesOf(field, floor, size) : [];
372
+ const values = full.flatMap((bar) => {
373
+ const one = field.cells[bar.index];
374
+ const text = one?.tone ? this.sourceRows[one.row]?.cells[one.column]?.text : undefined;
375
+ return one && text
376
+ ? [
377
+ {
378
+ index: bar.index,
379
+ text,
380
+ tone: one.tone,
381
+ x: bar.drawn.peak.x,
382
+ y: bar.drawn.crown - size,
383
+ },
384
+ ]
385
+ : [];
386
+ });
387
+ const extents = [...names, ...values].flatMap((word) => {
388
+ const half = (word.text.length * size * 0.62) / 2;
389
+ return [
390
+ { x: word.x - half, y: word.y - size * 0.7 },
391
+ { x: word.x + half, y: word.y + size * 0.7 },
392
+ ];
393
+ });
394
+ return { frame: around(base, extents), names, values };
395
+ };
396
+ const first = wordsAt(sizeOf(base));
397
+ const size = sizeOf(first.frame);
398
+ const { frame: fits, names, values } = wordsAt(size);
399
+ const wide = sized && room ? room.width * sized.unit : 0;
400
+ const high = sized && room ? room.height * sized.unit : 0;
401
+ const placed = sized && fits.width <= wide + 1e-9 && fits.height <= high + 1e-9
402
+ ? {
403
+ height: high,
404
+ width: wide,
405
+ x: fits.x + (fits.width - wide) / 2,
406
+ y: fits.y + (fits.height - high) / 2,
407
+ }
408
+ : fits;
409
+ const bounds = this.drag.frame(placed, view.on([0, 0, 0]));
410
+ const pixel = pixelOf(bounds);
411
+ const font = Math.round(size * 10_000) / 10_000;
167
412
  return html `<div aria-hidden="true" dir="ltr" ?hidden=${this.rendered} part="fallback">
168
413
  <svg
169
414
  part="drawing"
@@ -176,15 +421,21 @@ let HelixHeadlessSpatialBarChart = class HelixHeadlessSpatialBarChart extends Mi
176
421
  @pointerleave=${this.onDrawingLeave}
177
422
  @pointermove=${this.onDrawingMove}
178
423
  @pointerup=${this.onDrawingTap}
179
- >${this.noBox ? nothing : drawBox(view)}${depthOrder(view.projection, bars).map((bar) => svg `${this.renderBar(field, bar, at)}${bar === active ? svg `<path d=${active.drawn.outline} part="ring"></path>` : nothing}`)}${active
180
- ? svg `<path d=${active.drawn.outline} part="ring-behind"></path><foreignObject height="0.001" overflow="visible" width="0.001" x=${active.drawn.peak.x} y=${active.drawn.peak.y}>${this.renderOwnTooltip(cell?.row)}</foreignObject>`
424
+ >${bars.some((bar) => field.cells[bar.index]?.tone)
425
+ ? svg `<defs><filter height="160%" id="helix-spatial-bar-chart-glow" width="500%" x="-200%" y="-30%"><feGaussianBlur stdDeviation=${(pixel ?? 0.006) * 8}></feGaussianBlur></filter></defs>`
426
+ : nothing}${bare ? nothing : drawBox(view)}${floor.map((plate) => svg `<path d=${plate.d} part="floor"></path>`)}${names.map((name) => svg `<text data-tone=${name.tone ?? nothing} dominant-baseline="central" font-size=${font} part="tick" style="${name.tone ? `--chart-series: var(--spatial-bar-chart-tone-${name.tone})` : ''}" text-anchor="middle" x=${shortNumber(name.x)} y=${shortNumber(name.y)}>${name.text}</text>`)}${paintOrder(view.projection, bars).map((bar) => svg `${field.cells[bar.index]?.tone ? svg `<path d=${bar.drawn.outline} fill="none" filter="url(#helix-spatial-bar-chart-glow)" part="glow" style="--chart-series: var(--spatial-bar-chart-tone-${field.cells[bar.index]?.tone})"></path>` : nothing}${this.renderBar(field, bar, at)}${bar === active ? svg `<path d=${active.drawn.outline} part="ring"></path>` : nothing}`)}${values.map((value) => {
427
+ const crown = bars.find((bar) => bar.index === value.index)?.drawn.crown ?? value.y + size;
428
+ return svg `<text data-tone=${value.tone ?? nothing} dominant-baseline="central" font-size=${font} part="value" text-anchor="middle" x=${shortNumber(value.x)} y=${shortNumber(crown - size)}>${value.text}</text>`;
429
+ })}${active
430
+ ? svg `<path d=${active.drawn.outline} part="ring-behind"></path><foreignObject height="0.001" overflow="visible" width="0.001" x=${active.drawn.peak.x} y=${active.drawn.crown}>${this.renderOwnTooltip(cell?.row)}</foreignObject>`
181
431
  : nothing}</svg>
182
432
  </div>`;
183
433
  }
184
434
  renderBar(field, bar, at) {
185
- const depth = field.cells[bar.index]?.depth ?? 0;
435
+ const one = field.cells[bar.index];
436
+ const series = ((one?.depth ?? 0) % SERIES) + 1;
186
437
  const short = (value) => Math.round(value * 1000) / 1000;
187
- return svg `<g data-index=${bar.index} part="bar${bar.index === at ? ' bar-active' : ''}" style="--chart-series: var(--chart-series-${(depth % SERIES) + 1})">${bar.drawn.faces.map((face) => svg `<path d=${face.d} part="face" style="--spatial-bar-chart-shade: ${short(face.darken)}"></path>`)}</g>`;
438
+ return svg `<g data-index=${bar.index} data-tone=${one?.tone ?? nothing} part="bar${bar.index === at ? ' bar-active' : ''}" style="--chart-series: ${one?.tone ? `var(--spatial-bar-chart-tone-${one.tone}, var(--chart-series-${series}))` : `var(--chart-series-${series})`}">${bar.drawn.faces.map((face) => svg `<path d=${face.d} part="face" style="--spatial-bar-chart-shade: ${short(face.darken)}"></path>`)}</g>`;
188
439
  }
189
440
  renderLive(field, at) {
190
441
  const cell = field.cells[at];
@@ -232,6 +483,12 @@ let HelixHeadlessSpatialBarChart = class HelixHeadlessSpatialBarChart extends Mi
232
483
  return html `<p id="helix-chart-summary" part="summary">${said}</p>`;
233
484
  }
234
485
  };
486
+ __decorate([
487
+ property({ reflect: true, type: Boolean })
488
+ ], HelixHeadlessSpatialBarChart.prototype, "grow", void 0);
489
+ __decorate([
490
+ property({ attribute: 'grow-again', reflect: true, type: Boolean })
491
+ ], HelixHeadlessSpatialBarChart.prototype, "growAgain", void 0);
235
492
  __decorate([
236
493
  property({ attribute: 'no-box', reflect: true, type: Boolean })
237
494
  ], HelixHeadlessSpatialBarChart.prototype, "noBox", void 0);
@@ -243,10 +500,16 @@ __decorate([
243
500
  words,
244
501
  property({ attribute: 'plot-label' })
245
502
  ], HelixHeadlessSpatialBarChart.prototype, "plotLabel", void 0);
503
+ __decorate([
504
+ property({ reflect: true, type: Boolean })
505
+ ], HelixHeadlessSpatialBarChart.prototype, "stacked", void 0);
246
506
  __decorate([
247
507
  words,
248
508
  property({ attribute: 'summary-label' })
249
509
  ], HelixHeadlessSpatialBarChart.prototype, "summaryLabel", void 0);
510
+ __decorate([
511
+ property({ reflect: true })
512
+ ], HelixHeadlessSpatialBarChart.prototype, "variant", void 0);
250
513
  __decorate([
251
514
  state()
252
515
  ], HelixHeadlessSpatialBarChart.prototype, "rendered", void 0);
@@ -256,6 +519,12 @@ __decorate([
256
519
  __decorate([
257
520
  state()
258
521
  ], HelixHeadlessSpatialBarChart.prototype, "place", void 0);
522
+ __decorate([
523
+ state()
524
+ ], HelixHeadlessSpatialBarChart.prototype, "room", void 0);
525
+ __decorate([
526
+ state()
527
+ ], HelixHeadlessSpatialBarChart.prototype, "growth", void 0);
259
528
  HelixHeadlessSpatialBarChart = __decorate([
260
529
  customElement('helix-headless-spatial-bar-chart')
261
530
  ], HelixHeadlessSpatialBarChart);
@@ -20,6 +20,7 @@ let HelixHeadlessSplitter = class HelixHeadlessSplitter extends HelixElement {
20
20
  set collapsed(next) {
21
21
  const previous = this.collapsed;
22
22
  this.collapsedState = next ?? 'none';
23
+ this.reached = undefined;
23
24
  this.requestUpdate('collapsed', previous);
24
25
  }
25
26
  toPercent(value, fallback) {
@@ -60,6 +60,10 @@ declare const HelixHeadlessTabs_base: (new (...args: any[]) => import("@fusion.d
60
60
  *
61
61
  * A tab with `dismissible` can be closed: see `Tabs.Tab`.
62
62
  *
63
+ * **Panels are optional.** A set with none is a strip whose tabs stand for
64
+ * something the page shows elsewhere: the keys, the selection, `active` and
65
+ * `helix-activate` work the same, and no tab names a panel it controls.
66
+ *
63
67
  * The `indicator` part moves to the selected tab and carries its place and
64
68
  * size, so a mark of your own can be drawn there: style the part, or slot an
65
69
  * element into `indicator`, which sits inside the part and reads the same
@@ -34,6 +34,7 @@ declare const HelixHeadlessTab_base: (new (...args: any[]) => import("@fusion.de
34
34
  *
35
35
  * @csspart dismiss - The mark a pointer presses to close a `dismissible` tab.
36
36
  * @csspart dismiss-mark - The glyph on that mark, where none is slotted.
37
+ * @csspart label - The slot the tab's label is in.
37
38
  * @csspart tab - The tab itself.
38
39
  *
39
40
  * @cssstate selected - While it is the tab selected.