@fusion.dev/helix-headless 0.198.0 → 0.199.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/api.json +1 -1
  2. package/custom-elements.json +8267 -2270
  3. package/dist/carousel/root/carousel-root.d.ts +14 -0
  4. package/dist/carousel/root/carousel-root.js +15 -1
  5. package/dist/element-events.generated.js +19 -0
  6. package/dist/graph/root/graph-root.d.ts +33 -0
  7. package/dist/graph/root/graph-root.js +57 -0
  8. package/dist/index.d.ts +4 -0
  9. package/dist/index.js +4 -0
  10. package/dist/map/raster/map-raster.d.ts +7 -3
  11. package/dist/map/raster/map-raster.js +5 -3
  12. package/dist/motion-toggle/index.d.ts +1 -0
  13. package/dist/motion-toggle/index.js +1 -0
  14. package/dist/motion-toggle/root/motion-toggle-root.d.ts +77 -0
  15. package/dist/motion-toggle/root/motion-toggle-root.js +56 -0
  16. package/dist/spatial-bar-chart/bars.d.ts +79 -0
  17. package/dist/spatial-bar-chart/bars.js +72 -0
  18. package/dist/spatial-bar-chart/index.d.ts +2 -0
  19. package/dist/spatial-bar-chart/index.js +2 -0
  20. package/dist/spatial-bar-chart/root/spatial-bar-chart-root.d.ts +245 -0
  21. package/dist/spatial-bar-chart/root/spatial-bar-chart-root.js +262 -0
  22. package/dist/spatial-ribbon-chart/index.d.ts +2 -0
  23. package/dist/spatial-ribbon-chart/index.js +2 -0
  24. package/dist/spatial-ribbon-chart/ribbons.d.ts +55 -0
  25. package/dist/spatial-ribbon-chart/ribbons.js +99 -0
  26. package/dist/spatial-ribbon-chart/root/spatial-ribbon-chart-root.d.ts +243 -0
  27. package/dist/spatial-ribbon-chart/root/spatial-ribbon-chart-root.js +259 -0
  28. package/dist/spatial-scatter-chart/field.d.ts +8 -0
  29. package/dist/spatial-scatter-chart/root/spatial-scatter-chart-root.d.ts +14 -0
  30. package/dist/spatial-scatter-chart/root/spatial-scatter-chart-root.js +20 -6
  31. package/dist/spatial-scatter-chart/view.d.ts +14 -1
  32. package/dist/spatial-scatter-chart/view.js +29 -9
  33. package/dist/spatial-surface-chart/grid.d.ts +22 -0
  34. package/dist/spatial-surface-chart/grid.js +67 -1
  35. package/dist/spatial-surface-chart/index.d.ts +1 -1
  36. package/dist/spatial-surface-chart/index.js +1 -1
  37. package/dist/spatial-surface-chart/root/spatial-surface-chart-root.d.ts +20 -0
  38. package/dist/spatial-surface-chart/root/spatial-surface-chart-root.js +38 -5
  39. package/dist/spatial-treemap/blocks.d.ts +106 -0
  40. package/dist/spatial-treemap/blocks.js +215 -0
  41. package/dist/spatial-treemap/index.d.ts +2 -0
  42. package/dist/spatial-treemap/index.js +2 -0
  43. package/dist/spatial-treemap/root/spatial-treemap-root.d.ts +236 -0
  44. package/dist/spatial-treemap/root/spatial-treemap-root.js +249 -0
  45. package/dist/video/root/video-root.d.ts +14 -0
  46. package/dist/video/root/video-root.js +30 -2
  47. package/examples.json +1 -1
  48. package/lint.json +1 -1
  49. package/package.json +5 -5
  50. package/types/jsx.d.ts +3037 -99
  51. package/types/svelte.d.ts +4 -0
  52. package/types/vue.d.ts +519 -3
  53. package/types/words.d.ts +149 -0
@@ -0,0 +1,99 @@
1
+ import { pathData } from '@fusion.dev/helix-core';
2
+ import { barsOf } from '../spatial-bar-chart/bars.js';
3
+ import { inBox } from '../spatial-scatter-chart/view.js';
4
+ const HEIGHT = 0.6;
5
+ const THICKNESS = 0.45;
6
+ export const ribbonsOf = (rows, columns) => {
7
+ const field = barsOf(rows, columns);
8
+ const series = Math.max(field.series.length, 1);
9
+ return {
10
+ ...field,
11
+ axes: {
12
+ ...field.axes,
13
+ extent: [1, HEIGHT, Math.min(1, Math.max(0.3, series * 0.22))],
14
+ x: {
15
+ ...field.axes.x,
16
+ range: [0, Math.max(field.categories.length - 1, 1)],
17
+ rules: undefined,
18
+ },
19
+ z: {
20
+ ...field.axes.z,
21
+ labels: field.series,
22
+ ticks: field.series.map((_, at) => at),
23
+ },
24
+ },
25
+ };
26
+ };
27
+ export const ribbonsDrawn = (field, projection) => {
28
+ const [ex, ey, ez] = field.axes.extent;
29
+ const on = (x, y, z) => projection.at(x * ex, -z * ez, y * ey);
30
+ const across = field.categories.length;
31
+ const count = Math.max(field.series.length, 1);
32
+ const half = (THICKNESS * 2) / count / 2;
33
+ const zero = inBox(field.axes.y, Math.max(field.axes.y.range[0], 0));
34
+ const [tx, ty, tz] = projection.toward;
35
+ const toward = { x: tx, y: tz, z: -ty };
36
+ return field.series.map((_, series) => {
37
+ const z = inBox(field.axes.z, series);
38
+ const [near, far] = [z + half, z - half];
39
+ const points = [];
40
+ for (let at = 0; at < across; at++) {
41
+ const index = at * field.series.length + series;
42
+ const value = field.cells[index]?.value;
43
+ if (value !== undefined) {
44
+ points.push({ index, value: inBox(field.axes.y, value), x: inBox(field.axes.x, at) });
45
+ }
46
+ }
47
+ const faces = [];
48
+ const front = toward.z >= 0 ? near : far;
49
+ const curtain = (depth) => pathData([
50
+ ...points.map((point) => on(point.x, point.value, depth)),
51
+ ...[...points].reverse().map((point) => on(point.x, zero, depth)),
52
+ ]);
53
+ const first = points[0];
54
+ const last = points.at(-1);
55
+ const ends = [];
56
+ if (first && last) {
57
+ const end = toward.x >= 0 ? last : first;
58
+ ends.push({
59
+ d: pathData([
60
+ on(end.x, zero, near),
61
+ on(end.x, end.value, near),
62
+ on(end.x, end.value, far),
63
+ on(end.x, zero, far),
64
+ ]),
65
+ shade: 0.22,
66
+ });
67
+ }
68
+ const tops = [];
69
+ for (let at = 0; at + 1 < points.length; at++) {
70
+ const [a, b] = [
71
+ points[at],
72
+ points[at + 1],
73
+ ];
74
+ const rise = Math.abs(b.value - a.value) * ey;
75
+ const run = Math.abs(b.x - a.x) * ex || 1e-6;
76
+ tops.push({
77
+ d: pathData([
78
+ on(a.x, a.value, near),
79
+ on(b.x, b.value, near),
80
+ on(b.x, b.value, far),
81
+ on(a.x, a.value, far),
82
+ ]),
83
+ shade: Math.min(0.3, (rise / Math.hypot(rise, run)) * 0.3),
84
+ });
85
+ }
86
+ if (toward.x < 0) {
87
+ tops.reverse();
88
+ }
89
+ faces.push(...ends, ...tops, { d: curtain(front), shade: 0.12 });
90
+ const outline = points.flatMap((point) => [near, far].flatMap((depth) => [on(point.x, point.value, depth), on(point.x, zero, depth)]));
91
+ return {
92
+ depth: projection.depth(0, -z * ez),
93
+ faces,
94
+ outline,
95
+ readings: points.map((point) => ({ index: point.index, ...on(point.x, point.value, front) })),
96
+ series,
97
+ };
98
+ });
99
+ };
@@ -0,0 +1,243 @@
1
+ import { AriaForwardController, HelixElement } from '@fusion.dev/helix-core';
2
+ import { type PropertyValues, type TemplateResult } from 'lit';
3
+ import '../../tooltip/index.js';
4
+ import { type RibbonField } from '../ribbons.js';
5
+ declare const HelixHeadlessSpatialRibbonChart_base: typeof HelixElement & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinChartSourceInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinChartStatusInterface);
6
+ /**
7
+ * @summary A chart that draws each series as a ribbon in three dimensions, its top following the values, for several series over one set of places.
8
+ *
9
+ * @description
10
+ * A 3D ribbon chart reads its table as `Line Chart` does: **each row is a
11
+ * place across, such as a day, named by its label, and each numeric column a
12
+ * series**, named by its heading. Each series is a ribbon in a depth of its
13
+ * own, the first in front, its top following the values and its face falling
14
+ * to zero, so several series that would tangle as lines stand apart.
15
+ *
16
+ * **The flat `Line Chart` is read more exactly.** Use this one where the
17
+ * shape of each series, and how the series differ, matters more than any one
18
+ * value, such as response times by region over a month. The tooltip and the
19
+ * table carry the exact values, and a drag turns the view to show a ribbon
20
+ * another hides.
21
+ *
22
+ * **Helix draws the frame, and the chart draws the ribbons in 3D itself, in
23
+ * SVG**: a picture a print and a page without WebGL show too, which a drag
24
+ * or Shift with an arrow turns. Put a box inside the chart, and
25
+ * `connectSpatialRibbonChart` from `@fusion.dev/helix-plot`, given
26
+ * `threeRibbonDriver` from `@fusion.dev/helix-plot/three`, draws the ribbons
27
+ * in it with three.js, lit and in perspective.
28
+ *
29
+ * **The table is the reading.** Every number is in it, and screen readers
30
+ * read it. The `summary` part names the number of readings, series, and
31
+ * places, and the range of values, and describes the plot.
32
+ *
33
+ * **The plot is read by key, across and in depth.** It takes focus, named by
34
+ * `label` or the caption. The left and right arrows move from place to
35
+ * place, the down and up arrows from series to series, and Home and End go
36
+ * to the first and the last reading. The `live` part says the reading's
37
+ * place, series, and value in words. Shift with an arrow turns the view, and
38
+ * Escape lets the reading go.
39
+ *
40
+ * **A reading shows its row in a tooltip**, as on every chart: the reading a
41
+ * mouse or a pen rests nearest, the one tapped, or the one the keys move to.
42
+ *
43
+ * **Readings are sent as every chart sends them.** `helix-reading-change`
44
+ * says each move of the reading, and `helix-reading-press` a reading clicked
45
+ * or pressed with Enter or Space, each with the row of the table in `index`.
46
+ *
47
+ * **Keyboard.** The plot is one tab stop once it has readings.
48
+ *
49
+ * | Key | What it does |
50
+ * |---|---|
51
+ * | Tab | Moves focus to the plot. |
52
+ * | Right, Left | Moves to the next or the previous place. |
53
+ * | Down, Up | Moves to the next or the previous series. |
54
+ * | Home, End | Moves to the first or the last reading. |
55
+ * | Shift with an arrow | Turns the view across, or tilts it up and down. |
56
+ * | Enter, Space | Presses the reading, which sends `helix-reading-press`. |
57
+ * | Escape | Lets the reading go. |
58
+ *
59
+ * @since 0.199.0
60
+ * @status experimental
61
+ * @category charts
62
+ * @aliases 3D ribbon chart, 3D line chart, Ribbon chart, Waterfall plot, Line chart in 3D
63
+ * @documentation https://fusion.dev/helix/components/spatial-ribbon-chart
64
+ *
65
+ * @slot - The renderer's box, which the 3D drawing is made in.
66
+ * @slot empty - Content shown when the table has no readings. Falls back to `empty-text`.
67
+ * @slot loading - Content shown while `loading` is set and there are no readings yet. Falls back to `loading-text`.
68
+ *
69
+ * @event {HelixPlotChangeEvent} helix-plot-change - The readings or the reading the keys rest on changed, which the renderer is told. It stays on the chart.
70
+ * @event {HelixPlotTurnEvent} helix-plot-turn - Shift with an arrow asked the view to turn, by a step across or up.
71
+ * @event {HelixReadingChangeEvent} helix-reading-change - The reading moved, or was put away: `index` is the row, or `-1` for none, with its `label` and the `pointerType` that moved it.
72
+ * @event {HelixReadingPressEvent} helix-reading-press - A reading was clicked or pressed with Enter or Space, with the same detail as `helix-reading-change`.
73
+ *
74
+ * @csspart axis-name - A measure's name, along its edge of the box, in the chart's own drawing.
75
+ * @csspart drawing - The chart's own drawing in SVG: the box, its grid and names, and the ribbons.
76
+ * @csspart empty - The words when the table has no readings: the `empty` slot, falling back to `empty-text`.
77
+ * @csspart face - One face of a ribbon the view shows: a piece of its top, an end, or its side, shaded by how far it turns from the light.
78
+ * @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.
79
+ * @csspart grid - The box's edges and its grid, in the chart's own drawing.
80
+ * @csspart live - The readout: a polite live region that says the reading the keys moved to. Hide it visually.
81
+ * @csspart loading - The words while `loading` is set and there are no readings.
82
+ * @csspart mark - The invisible point the tooltip is anchored to, where the renderer draws the reading.
83
+ * @csspart plot - The box the drawing fills, which takes focus to be read by key. It is named by the host's `aria-labelledby` or `aria-label`, then by `label`, then by the caption of the table.
84
+ * @csspart ribbon - One ribbon, its faces in its series' color.
85
+ * @csspart ring - The ring around the reading the pointer or the keys rest on, drawn over the ribbons.
86
+ * @csspart summary - The number of readings, series, and places, and the range of values, written as a sentence for screen readers, which describes the plot. Hide it visually.
87
+ * @csspart table - The readings as a table, for a screen reader, rendered only where they were drawn from `data`. Hide it visually.
88
+ * @csspart tick - A place's or a series' name, or a value at a tick, along its edge of the box, in the chart's own drawing.
89
+ * @csspart tooltip - The bubble, forwarded from `Tooltip`.
90
+ * @csspart tooltip-anchor - The `Tooltip` element itself.
91
+ * @csspart tooltip-arrow - The bubble's point, forwarded from `Tooltip`.
92
+ * @csspart tooltip-arrow-bottom - The point while the bubble is below the reading, on its top edge. Set alongside `tooltip-arrow`.
93
+ * @csspart tooltip-arrow-left - The point while the bubble is left of the reading, on its right edge. Set alongside `tooltip-arrow`.
94
+ * @csspart tooltip-arrow-right - The point while the bubble is right of the reading, on its left edge. Set alongside `tooltip-arrow`.
95
+ * @csspart tooltip-arrow-top - The point while the bubble is above the reading, on its bottom edge. Set alongside `tooltip-arrow`.
96
+ * @csspart tooltip-bottom - The bubble while it is below the reading. Set alongside `tooltip`.
97
+ * @csspart tooltip-cell - One column's name and value inside the tooltip.
98
+ * @csspart tooltip-cell-current - The cell a chart's series keys rest on, which every chart's tooltip can mark. This chart marks none, since its readout says the measure. Set alongside `tooltip-cell`.
99
+ * @csspart tooltip-label - The row's own name inside the tooltip.
100
+ * @csspart tooltip-left - The bubble while it is left of the reading. Set alongside `tooltip`.
101
+ * @csspart tooltip-name - A column's name inside the tooltip.
102
+ * @csspart tooltip-right - The bubble while it is right of the reading. Set alongside `tooltip`.
103
+ * @csspart tooltip-top - The bubble while it is above the reading. Set alongside `tooltip`.
104
+ * @csspart tooltip-value - A column's value inside the tooltip.
105
+ *
106
+ * @cssproperty --spatial-ribbon-chart-shade - How far a ribbon's face turns from the light, from 0 for a level top. The component writes it on each face, and the stylesheet that shades the face reads it.
107
+ *
108
+ * @cssstate active - While a reading is shown, under the pointer or the keys.
109
+ * @cssstate drawn - While a renderer has drawn the ribbons, and the chart's own drawing is hidden.
110
+ * @cssstate empty - While the `empty` part stands in place of the readings.
111
+ * @cssstate loading - While the `loading` part stands in place of the readings.
112
+ */
113
+ export declare class HelixHeadlessSpatialRibbonChart extends HelixHeadlessSpatialRibbonChart_base {
114
+ /**
115
+ * The key this family's words are registered under, its styled tag:
116
+ * `registerWords('de', { 'helix-spatial-ribbon-chart': { emptyText: 'Keine Werte' } })`.
117
+ */
118
+ static words: string;
119
+ /**
120
+ * Draws no box: no edges, grid, numbers, or names, so the readings stand
121
+ * alone and fill the plot, as a picture on a product's own page does. The
122
+ * table, the summary, the tooltip, and the keys still carry every value,
123
+ * and a renderer draws no box either.
124
+ */
125
+ noBox: boolean;
126
+ /**
127
+ * What the readout says for a reading the table has no value for.
128
+ * `{place}` and `{series}` name it.
129
+ */
130
+ openLabel: string;
131
+ /** The plot's name where the chart has none: no `aria-label`, `label`, or table caption. */
132
+ plotLabel: string;
133
+ /**
134
+ * The `summary` part's sentence. `{count}`, `{series}`, and `{places}` are
135
+ * the number of readings, series, and places, written in the locale,
136
+ * and `{low}` and `{high}` the lowest and the highest value.
137
+ */
138
+ summaryLabel: string;
139
+ /** Whether a renderer has drawn the ribbons, which hides the chart's own drawing. */
140
+ private rendered;
141
+ /** Where the chart's own drawing is seen from, which a drag or Shift with an arrow turns. */
142
+ private camera;
143
+ /** A drag across the chart's own drawing, which turns its view. */
144
+ private readonly drag;
145
+ /** Where the renderer draws the reading, which the tooltip points at. */
146
+ private place;
147
+ /** The field last told, kept so a change of it can be told. */
148
+ private last;
149
+ /** The reading the keys rested on last, so a change of it can be told. */
150
+ private told;
151
+ /** Carries a host `aria-labelledby` and `aria-describedby` to the plot. */
152
+ protected readonly aria: AriaForwardController;
153
+ /**
154
+ * The table read as ribbons. **Each row is a place across, and each
155
+ * numeric column a series.** A renderer reads it.
156
+ */
157
+ get field(): RibbonField;
158
+ /** The field last read, and the rows and columns it was read from. */
159
+ private memo;
160
+ /** The reading the keys or the pointer rest on, as its place in `field.cells`, or `-1`. */
161
+ get active(): number;
162
+ /** The number of places on the grid, which the keys walk. */
163
+ get readings(): number;
164
+ /**
165
+ * Says whether a renderer has drawn the ribbons, which hides the chart's own
166
+ * drawing or shows it again. Called by the connector.
167
+ */
168
+ rendererDrawn(on?: boolean): void;
169
+ /**
170
+ * Puts the reading on the one at `index`, or lets it go with `-1`, as a
171
+ * pointer resting on it in the renderer's drawing or a press there does.
172
+ * A touch keeps it until a tap lands elsewhere, and a mouse leaving a
173
+ * reading the keys moved to leaves it. Called by the connector.
174
+ */
175
+ requestActive(index: number, kind?: 'mouse' | 'pen' | 'touch'): void;
176
+ /**
177
+ * Says where the renderer draws the reading, as fractions of the plot from
178
+ * its top left, or `undefined` while it draws none. The tooltip points
179
+ * there. Called by the connector.
180
+ */
181
+ placeReading(at: {
182
+ x: number;
183
+ y: number;
184
+ } | undefined): void;
185
+ /**
186
+ * Tells the keys how many series stand in depth for each place, so the
187
+ * up and down arrows move from series to series and the left and right
188
+ * arrows from place to place. Here rather than in `updated`, since
189
+ * the server calls this one.
190
+ */
191
+ protected willUpdate(changed: PropertyValues<this>): void;
192
+ connectedCallback(): void;
193
+ protected updated(changed: PropertyValues): void;
194
+ /**
195
+ * Shift with an arrow turns the view, the chart's own drawing and the
196
+ * renderer's alike, and moves no reading.
197
+ */
198
+ private readonly onPlotKeydown;
199
+ /** Each reading where the chart's own drawing put it, which the pointer finds. */
200
+ private placed;
201
+ /** A mouse or a pen over the chart's own drawing puts the reading on the one nearest it. */
202
+ private readonly onDrawingMove;
203
+ private readonly onDrawingDown;
204
+ /** A press the browser took back, such as for a scroll, which ends a drag as a release does. */
205
+ private readonly onDrawingCancel;
206
+ private readonly onDrawingLeave;
207
+ /**
208
+ * A tap is a touch reader's hover, unless it was a drag, which turned the
209
+ * view: it shows the reading nearest it until a tap lands elsewhere.
210
+ */
211
+ private readonly onDrawingTap;
212
+ protected render(): TemplateResult;
213
+ /**
214
+ * The ribbons in 3D, in SVG: the box with its grid and names, and each
215
+ * series a ribbon, painted back to front, each in its series' color with
216
+ * its faces shaded by how far they turn from the light. The chart's own
217
+ * drawing, until a renderer has drawn and where none is connected.
218
+ */
219
+ private renderFallback;
220
+ /** One ribbon, its faces in its series' color, each shaded by how far it turns from the light. */
221
+ private renderRibbon;
222
+ /** The readout: the reading's place, series, and value in words, once the keys have moved to one. */
223
+ private renderLive;
224
+ /**
225
+ * The reading's row in the tooltip, pointed at it in the chart's own
226
+ * drawing, which holds it there.
227
+ */
228
+ private renderOwnTooltip;
229
+ /**
230
+ * The reading's row in the tooltip every chart shows, pointed at where the
231
+ * renderer draws it. Only once a renderer has drawn; until then the
232
+ * chart's own drawing holds the tooltip at the reading.
233
+ */
234
+ private renderTooltip;
235
+ /** What the plot shows, in a sentence, which describes the plot. */
236
+ private renderSummary;
237
+ }
238
+ declare global {
239
+ interface HTMLElementTagNameMap {
240
+ 'helix-headless-spatial-ribbon-chart': HelixHeadlessSpatialRibbonChart;
241
+ }
242
+ }
243
+ export {};
@@ -0,0 +1,259 @@
1
+ import { __decorate } from "tslib";
2
+ import { AriaForwardController, CHART_PLOT, customElement, drawTooltip, formatWords, HelixElement, HelixPlotChangeEvent, HelixPlotTurnEvent, HelixReadingChangeEvent, HelixReadingPressEvent, isRtl, MixinChartSource, MixinChartStatus, spokenReading, words, } from '@fusion.dev/helix-core';
3
+ import { html, nothing, svg, } from 'lit';
4
+ import { property, state } from 'lit/decorators.js';
5
+ import '../../tooltip/index.js';
6
+ import { boundsOf, boxView, DragTurn, drawBox, nearestTo, SPATIAL_VIEW, turned, } from '../../spatial-scatter-chart/view.js';
7
+ import { ribbonsDrawn, ribbonsOf } from '../ribbons.js';
8
+ const TURNS = {
9
+ ArrowDown: { across: 0, up: -1 },
10
+ ArrowLeft: { across: -1, up: 0 },
11
+ ArrowRight: { across: 1, up: 0 },
12
+ ArrowUp: { across: 0, up: 1 },
13
+ };
14
+ const SERIES = 24;
15
+ let HelixHeadlessSpatialRibbonChart = class HelixHeadlessSpatialRibbonChart extends MixinChartStatus(MixinChartSource(HelixElement)) {
16
+ constructor() {
17
+ super(...arguments);
18
+ this.noBox = false;
19
+ this.openLabel = '{place}, {series}: no value';
20
+ this.plotLabel = '3D ribbon chart';
21
+ this.summaryLabel = '{count, plural, one {# reading} other {# readings}} in {series, plural, one {# series} other {# series}} over {places, plural, one {# place} other {# places}}, from {low} to {high}.';
22
+ this.rendered = false;
23
+ this.camera = SPATIAL_VIEW;
24
+ this.drag = new DragTurn((next) => {
25
+ this.camera = next(this.camera);
26
+ }, () => this.requestUpdate());
27
+ this.told = -1;
28
+ this.aria = new AriaForwardController(this, {
29
+ target: () => this.partNamed('plot'),
30
+ });
31
+ this.onPlotKeydown = (event) => {
32
+ const turn = TURNS[event.key];
33
+ if (!event.shiftKey || !turn || event.altKey || event.ctrlKey || event.metaKey) {
34
+ return;
35
+ }
36
+ event.preventDefault();
37
+ event.stopPropagation();
38
+ this.camera = turned(this.camera, turn.across, turn.up);
39
+ this.dispatchEvent(new HelixPlotTurnEvent(turn));
40
+ };
41
+ this.placed = [];
42
+ this.onDrawingMove = (event) => {
43
+ if (this.drag.move(event)) {
44
+ return;
45
+ }
46
+ if (event.pointerType === 'touch') {
47
+ return;
48
+ }
49
+ this.requestActive(nearestTo(event.currentTarget, event, this.placed), event.pointerType === 'pen' ? 'pen' : 'mouse');
50
+ };
51
+ this.onDrawingDown = (event) => {
52
+ this.drag.down(event);
53
+ };
54
+ this.onDrawingCancel = (event) => {
55
+ this.drag.up(event);
56
+ };
57
+ this.onDrawingLeave = (event) => {
58
+ if (event.pointerType !== 'touch') {
59
+ this.requestActive(-1, 'mouse');
60
+ }
61
+ };
62
+ this.onDrawingTap = (event) => {
63
+ if (this.drag.up(event)) {
64
+ return;
65
+ }
66
+ if (event.pointerType === 'touch') {
67
+ this.requestActive(nearestTo(event.currentTarget, event, this.placed, 24), 'touch');
68
+ }
69
+ };
70
+ }
71
+ static { this.words = 'helix-spatial-ribbon-chart'; }
72
+ get field() {
73
+ const rows = this.sourceRows;
74
+ const columns = this.plottedColumns;
75
+ if (this.memo?.rows !== rows || this.memo.columns !== columns) {
76
+ this.memo = { columns, field: ribbonsOf(rows, columns), rows };
77
+ }
78
+ return this.memo.field;
79
+ }
80
+ get active() {
81
+ return this.pointer.at;
82
+ }
83
+ get readings() {
84
+ return this.field.cells.length;
85
+ }
86
+ rendererDrawn(on = true) {
87
+ this.rendered = on;
88
+ this.toggleState('drawn', on);
89
+ }
90
+ requestActive(index, kind = 'mouse') {
91
+ if (kind === 'touch') {
92
+ this.pointer.tapTo(index);
93
+ }
94
+ else if (index >= 0 || !this.pointer.keyed) {
95
+ this.pointer.set(index);
96
+ }
97
+ }
98
+ placeReading(at) {
99
+ this.place = at;
100
+ }
101
+ willUpdate(changed) {
102
+ super.willUpdate(changed);
103
+ this.pointer.grid = Math.max(1, this.field.series.length);
104
+ }
105
+ connectedCallback() {
106
+ super.connectedCallback();
107
+ this.pointer.axis = 'none';
108
+ this.pointer.onReading = (heard, detail) => {
109
+ const cell = this.field.cells[detail.index];
110
+ const told = {
111
+ ...detail,
112
+ index: cell?.value === undefined ? -1 : cell.row,
113
+ label: cell?.label ?? '',
114
+ };
115
+ this.dispatchEvent(heard === 'press' ? new HelixReadingPressEvent(told) : new HelixReadingChangeEvent(told));
116
+ };
117
+ }
118
+ updated(changed) {
119
+ super.updated(changed);
120
+ const field = this.field;
121
+ if (field !== this.last) {
122
+ this.last = field;
123
+ this.dispatchEvent(new HelixPlotChangeEvent({ kind: 'data' }));
124
+ }
125
+ if (this.pointer.at !== this.told) {
126
+ this.told = this.pointer.at;
127
+ this.dispatchEvent(new HelixPlotChangeEvent({ kind: 'focus' }));
128
+ }
129
+ if (changed.has('place')) {
130
+ ;
131
+ this.renderRoot.querySelector('[for="helix-chart-mark"]')?.reposition?.();
132
+ }
133
+ }
134
+ render() {
135
+ const field = this.field;
136
+ const readable = field.readings > 0;
137
+ const at = this.pointer.at;
138
+ const cell = field.cells[at];
139
+ return html `<div
140
+ aria-describedby=${readable ? 'helix-chart-summary' : nothing}
141
+ aria-label=${this.named || this.plotLabel}
142
+ id=${CHART_PLOT}
143
+ part="plot"
144
+ role=${readable ? 'application' : 'img'}
145
+ tabindex=${readable ? '0' : nothing}
146
+ @keydown=${this.onPlotKeydown}
147
+ >
148
+ <slot></slot>
149
+ ${readable ? this.renderFallback(field, at) : nothing}
150
+ ${readable ? this.renderLive(field, at) : nothing}
151
+ ${this.renderTooltip(cell?.value === undefined ? undefined : cell.row)}
152
+ </div>
153
+ ${readable ? this.renderSummary(field) : this.renderStatus()}${this.renderTable()}`;
154
+ }
155
+ renderFallback(field, at) {
156
+ const view = boxView(field.axes, this.camera, [], this.noBox);
157
+ const ribbons = ribbonsDrawn(field, view.projection).sort((one, other) => one.depth - other.depth);
158
+ const places = ribbons.flatMap((ribbon) => ribbon.readings);
159
+ this.placed = places;
160
+ const active = places.find((place) => place.index === at);
161
+ const cell = field.cells[at];
162
+ const bounds = this.drag.frame(this.noBox
163
+ ? boundsOf(ribbons.flatMap((ribbon) => ribbon.outline), 0.1)
164
+ : view.bounds, view.on([0, 0, 0]));
165
+ return html `<div aria-hidden="true" dir="ltr" ?hidden=${this.rendered} part="fallback">
166
+ <svg
167
+ part="drawing"
168
+ preserveAspectRatio="xMidYMid meet"
169
+ viewBox="${bounds.x} ${bounds.y} ${bounds.width} ${bounds.height}"
170
+ xmlns="http://www.w3.org/2000/svg"
171
+ @click=${this.drag.click}
172
+ @pointercancel=${this.onDrawingCancel}
173
+ @pointerdown=${this.onDrawingDown}
174
+ @pointerleave=${this.onDrawingLeave}
175
+ @pointermove=${this.onDrawingMove}
176
+ @pointerup=${this.onDrawingTap}
177
+ >${this.noBox ? nothing : drawBox(view)}${ribbons.map((ribbon) => this.renderRibbon(ribbon))}${active
178
+ ? svg `<circle cx=${active.x} cy=${active.y} part="ring" r="0.06"></circle><foreignObject height="0.001" overflow="visible" width="0.001" x=${active.x} y=${active.y}>${this.renderOwnTooltip(cell?.row)}</foreignObject>`
179
+ : nothing}</svg>
180
+ </div>`;
181
+ }
182
+ renderRibbon(ribbon) {
183
+ const short = (value) => Math.round(value * 1000) / 1000;
184
+ return svg `<g part="ribbon" style="--chart-series: var(--chart-series-${(ribbon.series % SERIES) + 1})">${ribbon.faces.map((face) => svg `<path d=${face.d} part="face" style="--spatial-ribbon-chart-shade: ${short(face.shade)}"></path>`)}</g>`;
185
+ }
186
+ renderLive(field, at) {
187
+ const cell = field.cells[at];
188
+ const row = cell === undefined ? undefined : this.sourceRows[cell.row];
189
+ let said = '';
190
+ if (this.pointer.keyed && cell) {
191
+ said =
192
+ row && cell.value !== undefined
193
+ ? spokenReading(row, cell.column, (one) => this.pointer.shows(one), this.pointer.position)
194
+ : formatWords(this, 'openLabel', {
195
+ place: cell.label,
196
+ series: field.series[cell.depth] ?? '',
197
+ });
198
+ }
199
+ return html `<span aria-live="polite" part="live" role="status">${said}</span>`;
200
+ }
201
+ renderOwnTooltip(row) {
202
+ if (this.rendered || row === undefined || !this.sourceRows[row]) {
203
+ return nothing;
204
+ }
205
+ return drawTooltip(this.sourceRows, row, this.tag('tooltip'), { dir: isRtl(this) ? 'rtl' : 'ltr', fixed: true, placement: 'top', x: '0%', y: '0%' }, () => undefined);
206
+ }
207
+ renderTooltip(row) {
208
+ const place = this.place;
209
+ if (!this.rendered || !place || row === undefined || !this.sourceRows[row]) {
210
+ return nothing;
211
+ }
212
+ return drawTooltip(this.sourceRows, row, this.tag('tooltip'), {
213
+ dir: isRtl(this) ? 'rtl' : 'ltr',
214
+ fixed: true,
215
+ placement: 'top',
216
+ x: `${place.x * 100}%`,
217
+ y: `${place.y * 100}%`,
218
+ }, () => undefined);
219
+ }
220
+ renderSummary(field) {
221
+ const values = field.cells.flatMap((cell) => (cell.value === undefined ? [] : [cell.value]));
222
+ const said = formatWords(this, 'summaryLabel', {
223
+ count: field.readings,
224
+ high: Math.max(...values),
225
+ low: Math.min(...values),
226
+ places: field.categories.length,
227
+ series: field.series.length,
228
+ });
229
+ return html `<p id="helix-chart-summary" part="summary">${said}</p>`;
230
+ }
231
+ };
232
+ __decorate([
233
+ property({ attribute: 'no-box', reflect: true, type: Boolean })
234
+ ], HelixHeadlessSpatialRibbonChart.prototype, "noBox", void 0);
235
+ __decorate([
236
+ words,
237
+ property({ attribute: 'open-label' })
238
+ ], HelixHeadlessSpatialRibbonChart.prototype, "openLabel", void 0);
239
+ __decorate([
240
+ words,
241
+ property({ attribute: 'plot-label' })
242
+ ], HelixHeadlessSpatialRibbonChart.prototype, "plotLabel", void 0);
243
+ __decorate([
244
+ words,
245
+ property({ attribute: 'summary-label' })
246
+ ], HelixHeadlessSpatialRibbonChart.prototype, "summaryLabel", void 0);
247
+ __decorate([
248
+ state()
249
+ ], HelixHeadlessSpatialRibbonChart.prototype, "rendered", void 0);
250
+ __decorate([
251
+ state()
252
+ ], HelixHeadlessSpatialRibbonChart.prototype, "camera", void 0);
253
+ __decorate([
254
+ state()
255
+ ], HelixHeadlessSpatialRibbonChart.prototype, "place", void 0);
256
+ HelixHeadlessSpatialRibbonChart = __decorate([
257
+ customElement('helix-headless-spatial-ribbon-chart')
258
+ ], HelixHeadlessSpatialRibbonChart);
259
+ export { HelixHeadlessSpatialRibbonChart };
@@ -31,8 +31,16 @@ export interface PlotPoint {
31
31
  }
32
32
  /** One axis: its column's name, its range widened to round numbers, and the ticks on it. */
33
33
  export interface PlotAxis {
34
+ /**
35
+ * The words written at the ticks, in their order, for an axis of names such
36
+ * as a bar chart's categories, which stand at 0, 1, 2 and on. Absent, each
37
+ * tick is written as its number.
38
+ */
39
+ labels?: readonly string[] | undefined;
34
40
  name: string;
35
41
  range: [number, number];
42
+ /** Where the grid's lines run along this axis, where not at the ticks: between the names of an axis of names. */
43
+ rules?: readonly number[] | undefined;
36
44
  ticks: number[];
37
45
  }
38
46
  /** The table as a 3D scatter reads it. */
@@ -70,6 +70,7 @@ declare const HelixHeadlessSpatialScatterChart_base: typeof HelixElement & (new
70
70
  *
71
71
  * @csspart axis-name - A measure's name, along its edge of the box, in the chart's own drawing.
72
72
  * @csspart drawing - The chart's own drawing in SVG: the box, its grid and numbers, and the points.
73
+ * @csspart drop-line - The line from a point straight down to the floor of the box, drawn under `drop-lines`, in its group's color.
73
74
  * @csspart empty - The words when the table has no points: the `empty` slot, falling back to `empty-text`.
74
75
  * @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.
75
76
  * @csspart grid - The box's edges and its grid at the ticks, in the chart's own drawing.
@@ -111,8 +112,21 @@ export declare class HelixHeadlessSpatialScatterChart extends HelixHeadlessSpati
111
112
  * `registerWords('de', { 'helix-spatial-scatter-chart': { emptyText: 'Keine Punkte' } })`.
112
113
  */
113
114
  static words: string;
115
+ /**
116
+ * Draws a line from each point straight down to the floor of the box, in
117
+ * its group's color, so where a point stands across and in depth reads off
118
+ * the floor's grid. A renderer draws them too.
119
+ */
120
+ dropLines: boolean;
114
121
  /** The column of text that puts each point in a group, by its heading. None by default. */
115
122
  groupBy: string;
123
+ /**
124
+ * Draws no box: no edges, grid, numbers, or names, so the readings stand
125
+ * alone and fill the plot, as a picture on a product's own page does. The
126
+ * table, the summary, the tooltip, and the keys still carry every value,
127
+ * and a renderer draws no box either.
128
+ */
129
+ noBox: boolean;
116
130
  /** The plot's name where the chart has none: no `aria-label`, `label`, or table caption. */
117
131
  plotLabel: string;
118
132
  /**