@fusion.dev/helix-headless 0.177.2 → 0.179.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.
- package/custom-elements.json +826 -104
- package/dist/button/root/button-root.d.ts +12 -0
- package/dist/button/root/button-root.js +9 -0
- package/dist/drilldown/item/drilldown-item.d.ts +6 -0
- package/dist/drilldown/item/drilldown-item.js +9 -0
- package/dist/element-events.generated.js +7 -1
- package/dist/filter-input/index.d.ts +1 -0
- package/dist/filter-input/root/filter-input-root.d.ts +92 -6
- package/dist/filter-input/root/filter-input-root.js +182 -10
- package/dist/filter-input/suggest.d.ts +47 -0
- package/dist/filter-input/suggest.js +17 -0
- package/dist/graph/root/graph-root.d.ts +101 -3
- package/dist/graph/root/graph-root.js +188 -9
- package/dist/heatmap/root/heatmap-root.d.ts +54 -42
- package/dist/heatmap/root/heatmap-root.js +72 -54
- package/dist/menu/item/menu-item.d.ts +6 -0
- package/dist/menu/item/menu-item.js +9 -0
- package/dist/notifications/notification/notification.d.ts +37 -9
- package/dist/notifications/notification/notification.js +21 -4
- package/dist/speed-dial/item/speed-dial-item.d.ts +6 -0
- package/dist/speed-dial/item/speed-dial-item.js +9 -0
- package/dist/tabs/tab/tab.d.ts +6 -0
- package/dist/tabs/tab/tab.js +9 -0
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +460 -132
- package/types/vue.d.ts +83 -43
- package/types/words.d.ts +30 -6
|
@@ -2,7 +2,7 @@ import { AriaForwardController, HelixElement } from '@fusion.dev/helix-core';
|
|
|
2
2
|
import { nothing, type PropertyValues, type TemplateResult } from 'lit';
|
|
3
3
|
import '../../tooltip/index.js';
|
|
4
4
|
import { type Reading } from './buckets.js';
|
|
5
|
-
declare const HelixHeadlessHeatmap_base: typeof HelixElement & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinChartSourceInterface);
|
|
5
|
+
declare const HelixHeadlessHeatmap_base: typeof HelixElement & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinChartSourceInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinChartStatusInterface);
|
|
6
6
|
/**
|
|
7
7
|
* @summary A strip of cells, each colored by its reading, for uptime, contributions, or activity.
|
|
8
8
|
*
|
|
@@ -50,6 +50,25 @@ declare const HelixHeadlessHeatmap_base: typeof HelixElement & (new (...args: an
|
|
|
50
50
|
* `helix-before-change` and tells with `change`, and the `selection` part says
|
|
51
51
|
* how many are selected, in `selection-label`.
|
|
52
52
|
*
|
|
53
|
+
* **Under `selection-mode` the strip is a grid**, so the selection is state a
|
|
54
|
+
* reader is told rather than words alone: each column of the strip is a `row`,
|
|
55
|
+
* a week with `rows="7"`, and each cell a `gridcell` named by its row in the
|
|
56
|
+
* table and carrying `aria-selected`. The strip says
|
|
57
|
+
* `aria-multiselectable` under `multiple` and points at the cell the keys rest
|
|
58
|
+
* on with `aria-activedescendant`, so a screen reader reads that cell, and the
|
|
59
|
+
* `live` part stays quiet. Without `selection-mode` nothing is selectable, and
|
|
60
|
+
* the strip is an application with the readout, as every chart's plot is.
|
|
61
|
+
*
|
|
62
|
+
* **Readings are sent as every chart sends them.** `helix-reading-change`
|
|
63
|
+
* says each move of the reading, by pointer, tap or key, and
|
|
64
|
+
* `helix-reading-press` a reading clicked, tapped, or pressed with Enter or
|
|
65
|
+
* Space, each with the row of the table in `index`.
|
|
66
|
+
*
|
|
67
|
+
* **Nothing to draw.** A table with no readings shows `empty-text`, "No
|
|
68
|
+
* readings", under the strip, and `loading` shows `loading-text` there until
|
|
69
|
+
* readings arrive. The `empty` and `loading` slots take markup in place of
|
|
70
|
+
* either, as on every chart.
|
|
71
|
+
*
|
|
53
72
|
* **Keyboard.** The strip is one tab stop once it has cells.
|
|
54
73
|
*
|
|
55
74
|
* | Key | What it does |
|
|
@@ -59,7 +78,7 @@ declare const HelixHeadlessHeatmap_base: typeof HelixElement & (new (...args: an
|
|
|
59
78
|
* | Right, Left | Moves a column on or back, a week with `rows="7"`. On a right-to-left page, Left moves on. |
|
|
60
79
|
* | Home, End | Moves to the first or the last cell. |
|
|
61
80
|
* | Page Down, Page Up | Moves a month on or back where the rows carry dates, and four columns otherwise. |
|
|
62
|
-
* | Enter, Space |
|
|
81
|
+
* | Enter, Space | Presses the cell, which sends `helix-reading-press`, and selects it or lets it go under `selection-mode`. |
|
|
63
82
|
* | Shift with an arrow, Home, End, Page Up or Page Down | Selects every cell from the last one pressed to where the key went, under `selection-mode="multiple"`. |
|
|
64
83
|
* | Escape | Puts the cell's tooltip away. |
|
|
65
84
|
*
|
|
@@ -69,23 +88,30 @@ declare const HelixHeadlessHeatmap_base: typeof HelixElement & (new (...args: an
|
|
|
69
88
|
* @aliases Contribution graph, Uptime strip, Calendar heatmap, Activity graph
|
|
70
89
|
* @documentation https://fusion.dev/helix/components/heatmap
|
|
71
90
|
*
|
|
91
|
+
* @slot empty - Content shown under the strip when the table has no readings, such as a link to add the first. Falls back to `empty-text`.
|
|
72
92
|
* @slot end - Below the strip, at the trailing edge: usually the newest date.
|
|
93
|
+
* @slot loading - Content shown under the strip while `loading` is set and there are no readings yet. Falls back to `loading-text`.
|
|
73
94
|
* @slot start - Below the strip, at the leading edge: usually the oldest date.
|
|
74
95
|
* @slot summary - Below the strip, in the middle: a summary of the range shown.
|
|
75
96
|
*
|
|
76
97
|
* @event {Event} change - Fired once a press or a key has changed which cells are selected.
|
|
77
98
|
* @event {HelixBeforeChangeEvent} helix-before-change - Fired before a press or a key changes which cells are selected, with the `value` it would leave. Cancelable.
|
|
99
|
+
* @event {HelixReadingChangeEvent} helix-reading-change - The reading shown moved, or was put away: `index` is the row, or `-1` for none, with its `label`, the `series` the keys rest on, and the `pointerType` that moved it.
|
|
100
|
+
* @event {HelixReadingPressEvent} helix-reading-press - A reading was clicked, tapped, or pressed with Enter or Space, with the same detail as `helix-reading-change`.
|
|
78
101
|
* @event {HelixResizeEvent} helix-resize - Fired when the number of cells drawn changes, with the new number.
|
|
79
102
|
*
|
|
80
103
|
* @csspart cell - One reading.
|
|
81
104
|
* @csspart cell-active - The cell the pointer or the keys rest on. Set alongside `cell`.
|
|
82
105
|
* @csspart cell-selected - A cell that is selected, under `selection-mode`. Set alongside `cell`.
|
|
83
|
-
* @csspart empty -
|
|
106
|
+
* @csspart empty - The words under the strip when the table has no readings: the `empty` slot, falling back to `empty-text`. Not drawn while the table cannot be read yet, on the server and during hydration, there being no document to resolve `for` in.
|
|
84
107
|
* @csspart end - The trailing slot's box.
|
|
85
108
|
* @csspart footer - The three-slot row under the strip.
|
|
86
109
|
* @csspart live - The readout: a polite live region that says the cell the keys moved to. Hide it visually.
|
|
110
|
+
* @csspart loading - The words under the strip while `loading` is set and there are no readings: the `loading` slot, falling back to `loading-text`.
|
|
87
111
|
* @csspart mark - The invisible point the tooltip is anchored to.
|
|
88
112
|
* @csspart overview - The busiest reading and the total of the readings drawn, written as a sentence for screen readers, which describes the strip. Rendered once there are cells. Hide it visually.
|
|
113
|
+
* @csspart plot - The same element as `strip`, under the name every chart gives the drawing that takes focus.
|
|
114
|
+
* @csspart row - A column of cells, which a grid reads as a row, under `selection-mode`. It draws no box.
|
|
89
115
|
* @csspart selection - A polite region that says how many cells are selected after a change, rendered from the first change. Hide it visually.
|
|
90
116
|
* @csspart start - The leading slot's box.
|
|
91
117
|
* @csspart strip - The grid of cells, 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.
|
|
@@ -125,6 +151,7 @@ declare const HelixHeadlessHeatmap_base: typeof HelixElement & (new (...args: an
|
|
|
125
151
|
*
|
|
126
152
|
* @cssstate active - While a cell is shown, under the pointer or the keys, with its tooltip open.
|
|
127
153
|
* @cssstate empty - While the `empty` part stands in place of the cells.
|
|
154
|
+
* @cssstate loading - While the `loading` part stands in place of the cells.
|
|
128
155
|
*/
|
|
129
156
|
export declare class HelixHeadlessHeatmap extends HelixHeadlessHeatmap_base {
|
|
130
157
|
/**
|
|
@@ -137,13 +164,6 @@ export declare class HelixHeadlessHeatmap extends HelixHeadlessHeatmap_base {
|
|
|
137
164
|
* `overview-label`.
|
|
138
165
|
*/
|
|
139
166
|
busiestLabel: string;
|
|
140
|
-
/**
|
|
141
|
-
* Shown when the table has no readings in it.
|
|
142
|
-
*
|
|
143
|
-
* Never shown while the table cannot be read at all, which is a server render
|
|
144
|
-
* and the hydrating one after it: see `sourceKnown`.
|
|
145
|
-
*/
|
|
146
|
-
emptyText: string;
|
|
147
167
|
/**
|
|
148
168
|
* Space between cells, in CSS pixels. It is a number for the same reason
|
|
149
169
|
* `min-cell` is.
|
|
@@ -187,9 +207,11 @@ export declare class HelixHeadlessHeatmap extends HelixHeadlessHeatmap_base {
|
|
|
187
207
|
*/
|
|
188
208
|
rows: number;
|
|
189
209
|
/**
|
|
190
|
-
* What the readout
|
|
191
|
-
*
|
|
192
|
-
*
|
|
210
|
+
* What the readout added for a selected cell, under `selection-mode`.
|
|
211
|
+
*
|
|
212
|
+
* @deprecated The strip is a grid under `selection-mode`, and each cell says
|
|
213
|
+
* whether it is selected with `aria-selected`, in the reader's own words, so
|
|
214
|
+
* the readout says nothing there and this is not read.
|
|
193
215
|
*/
|
|
194
216
|
selectedLabel: string;
|
|
195
217
|
/**
|
|
@@ -334,28 +356,6 @@ export declare class HelixHeadlessHeatmap extends HelixHeadlessHeatmap_base {
|
|
|
334
356
|
* the one under the pointer.
|
|
335
357
|
*/
|
|
336
358
|
private get points();
|
|
337
|
-
/**
|
|
338
|
-
* Whether this render can tell a table with no readings from one it has not
|
|
339
|
-
* resolved yet.
|
|
340
|
-
*
|
|
341
|
-
* **Unknown is not empty, which is what an empty state must never say.**
|
|
342
|
-
* `for` names an id and resolving an id wants a document, so a server has
|
|
343
|
-
* none and a hydrating render must not look either. Without this the server
|
|
344
|
-
* drew "No readings" over a table holding ninety days of them, a crawler read
|
|
345
|
-
* that sentence, and the strip flipped to a full one on hydration: the fault
|
|
346
|
-
* `MixinCollection` names for a slotted collection, arriving through the one
|
|
347
|
-
* channel that mixin cannot see.
|
|
348
|
-
*
|
|
349
|
-
* The same shape as `childrenKnown`, asked of a tree that can answer about
|
|
350
|
-
* ids rather than of one that holds children. An id that resolves to nothing
|
|
351
|
-
* in a browser is still known, and still empty: a `for` naming no table
|
|
352
|
-
* should say so rather than sit blank, which is why this asks
|
|
353
|
-
* `searchableById` and not `elementById`.
|
|
354
|
-
*
|
|
355
|
-
* `readingsKnown` asks it for every chart, and for `data` too, which a
|
|
356
|
-
* server does not draw.
|
|
357
|
-
*/
|
|
358
|
-
private get sourceKnown();
|
|
359
359
|
/**
|
|
360
360
|
* `rows` as a usable count, which is not always what the attribute says.
|
|
361
361
|
*
|
|
@@ -432,6 +432,23 @@ export declare class HelixHeadlessHeatmap extends HelixHeadlessHeatmap_base {
|
|
|
432
432
|
private markEmpty;
|
|
433
433
|
protected updated(changed: PropertyValues): void;
|
|
434
434
|
protected render(): TemplateResult;
|
|
435
|
+
/**
|
|
436
|
+
* The cells as a grid's rows, one for each column of the strip, so a week
|
|
437
|
+
* is a row with `rows="7"` and a status strip is one row.
|
|
438
|
+
*
|
|
439
|
+
* **A column, because that is the order the cells are in.** The strip fills
|
|
440
|
+
* down the column and then across, so a row wrapping a column keeps every
|
|
441
|
+
* cell where it was, and `display: contents` keeps the wrapper out of the
|
|
442
|
+
* strip's grid, drawing no box, which is a mechanism and not a look. A row
|
|
443
|
+
* across the strip would have taken cells out of order and moved them.
|
|
444
|
+
*/
|
|
445
|
+
private renderRows;
|
|
446
|
+
/**
|
|
447
|
+
* One cell under `selection-mode`: a `gridcell` named by its row in the
|
|
448
|
+
* table, its reading and its place, and saying whether it is selected, which
|
|
449
|
+
* a reader hears on reaching it as the strip's active descendant.
|
|
450
|
+
*/
|
|
451
|
+
private renderGridCell;
|
|
435
452
|
/**
|
|
436
453
|
* What the strip shows, in a sentence: the busiest reading drawn, as the
|
|
437
454
|
* table writes it, and the total of them all, in `locale`. The strip is
|
|
@@ -461,13 +478,7 @@ export declare class HelixHeadlessHeatmap extends HelixHeadlessHeatmap_base {
|
|
|
461
478
|
* that is usually wider, so a percentage of the box lands between cells. The
|
|
462
479
|
* pitch is `cell + gap` and both are measured.
|
|
463
480
|
*/
|
|
464
|
-
protected renderTooltip(): TemplateResult | typeof nothing;
|
|
465
|
-
/**
|
|
466
|
-
* The readout's cursor: the pointer, with the selected word after where a
|
|
467
|
-
* selected cell stands, so a reader moving through the strip hears which
|
|
468
|
-
* cells are in the selection and not only how many.
|
|
469
|
-
*/
|
|
470
|
-
private reading;
|
|
481
|
+
protected renderTooltip(grid?: boolean): TemplateResult | typeof nothing;
|
|
471
482
|
/**
|
|
472
483
|
* Which drawn cell the pointer is over, or -1.
|
|
473
484
|
*
|
|
@@ -476,6 +487,7 @@ export declare class HelixHeadlessHeatmap extends HelixHeadlessHeatmap_base {
|
|
|
476
487
|
* `mouseover` it is delivered for a pen.
|
|
477
488
|
*/
|
|
478
489
|
private readonly onPointerOver;
|
|
490
|
+
private readonly onPointerUp;
|
|
479
491
|
private readonly onPointerLeave;
|
|
480
492
|
}
|
|
481
493
|
declare global {
|
|
@@ -4,16 +4,15 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
import { AriaForwardController, byRow, CHART_PLOT, commaList, customElement, drawTooltip, formatWords, HelixElement, HelixResizeEvent, MixinChartSource, markRegion, pressedValue, rangeAdd, requestValue, StatusController, words, writtenList, writtenNumbers, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { AriaForwardController, byRow, CHART_PLOT, commaList, customElement, drawTooltip, formatWords, HelixElement, HelixReadingChangeEvent, HelixReadingPressEvent, HelixResizeEvent, MixinChartSource, MixinChartStatus, markRegion, pressedValue, rangeAdd, requestValue, StatusController, spokenReading, words, writtenList, writtenNumbers, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html, nothing } from 'lit';
|
|
9
9
|
import { property, state } from 'lit/decorators.js';
|
|
10
10
|
import '../../tooltip/index.js';
|
|
11
11
|
import { fits, monthPage, overviewOf, toneOf } from './buckets.js';
|
|
12
|
-
let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(HelixElement) {
|
|
12
|
+
let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartStatus(MixinChartSource(HelixElement)) {
|
|
13
13
|
constructor() {
|
|
14
14
|
super(...arguments);
|
|
15
15
|
this.busiestLabel = 'Busiest';
|
|
16
|
-
this.emptyText = 'No readings';
|
|
17
16
|
this.gap = 2;
|
|
18
17
|
this.minCell = 8;
|
|
19
18
|
this.overviewLabel = '{busiestLabel}: {busiest}; {totalLabel} {total}.';
|
|
@@ -69,6 +68,16 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
|
|
|
69
68
|
const index = Number(cell?.getAttribute('data-index') ?? -1);
|
|
70
69
|
this.pointer.set(Number.isInteger(index) ? index : -1);
|
|
71
70
|
};
|
|
71
|
+
this.onPointerUp = (event) => {
|
|
72
|
+
if (event.pointerType !== 'touch') {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const cell = event.target?.closest?.('[part~="cell"]');
|
|
76
|
+
const index = Number(cell?.getAttribute('data-index') ?? -1);
|
|
77
|
+
if (cell && Number.isInteger(index) && index >= 0) {
|
|
78
|
+
this.pointer.tapTo(index);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
72
81
|
this.onPointerLeave = () => {
|
|
73
82
|
if (!this.pointer.keyed) {
|
|
74
83
|
this.pointer.set(-1);
|
|
@@ -112,9 +121,6 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
|
|
|
112
121
|
}
|
|
113
122
|
return byRow(series, this.sourceRows.length).map((point) => point ?? { value: undefined });
|
|
114
123
|
}
|
|
115
|
-
get sourceKnown() {
|
|
116
|
-
return this.readingsKnown;
|
|
117
|
-
}
|
|
118
124
|
get rowCount() {
|
|
119
125
|
return Math.max(1, Math.floor(this.rows) || 1);
|
|
120
126
|
}
|
|
@@ -160,6 +166,12 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
|
|
|
160
166
|
}
|
|
161
167
|
connectedCallback() {
|
|
162
168
|
super.connectedCallback();
|
|
169
|
+
this.pointer.axis = 'none';
|
|
170
|
+
this.pointer.onReading = (heard, detail) => {
|
|
171
|
+
const row = detail.index < 0 ? -1 : this.points.length - this.drawn.length + detail.index;
|
|
172
|
+
const told = { ...detail, index: row, label: this.sourceRows[row]?.label ?? '' };
|
|
173
|
+
this.dispatchEvent(heard === 'press' ? new HelixReadingPressEvent(told) : new HelixReadingChangeEvent(told));
|
|
174
|
+
};
|
|
163
175
|
this.addEventListener('keydown', this.onKeydown);
|
|
164
176
|
if (typeof ResizeObserver !== 'undefined') {
|
|
165
177
|
this.observer = new ResizeObserver((entries) => {
|
|
@@ -184,8 +196,6 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
|
|
|
184
196
|
super.updated(changed);
|
|
185
197
|
this.markEmpty();
|
|
186
198
|
this.measure();
|
|
187
|
-
this.toggleState('active', this.pointer.at >= 0);
|
|
188
|
-
this.toggleState('empty', this.drawn.length === 0 && this.sourceKnown);
|
|
189
199
|
const showing = this.visible;
|
|
190
200
|
const written = showing > 0 ? String(showing) : null;
|
|
191
201
|
if (this.getAttribute('visible') !== written) {
|
|
@@ -206,41 +216,48 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
|
|
|
206
216
|
const palette = writtenList(this.tones);
|
|
207
217
|
const cells = this.drawn;
|
|
208
218
|
const readable = cells.length > 0;
|
|
219
|
+
const grid = readable && this.selectionMode !== undefined;
|
|
209
220
|
const keys = this.selectionMode ? this.keysOf(cells) : [];
|
|
210
221
|
const chosen = new Set(this.selectionMode ? commaList(this.value) : []);
|
|
222
|
+
const drawn = cells.map((reading, index) => {
|
|
223
|
+
const tone = toneOf(reading, bounds, palette);
|
|
224
|
+
const active = this.pointer.at === index;
|
|
225
|
+
const picked = chosen.has(keys[index] ?? '');
|
|
226
|
+
if (grid) {
|
|
227
|
+
return this.renderGridCell(index, active, picked, tone);
|
|
228
|
+
}
|
|
229
|
+
return tone === undefined
|
|
230
|
+
? html `<span
|
|
231
|
+
part="cell${active ? ' cell-active' : ''}${picked ? ' cell-selected' : ''}"
|
|
232
|
+
data-index=${index}
|
|
233
|
+
></span>`
|
|
234
|
+
: html `<span
|
|
235
|
+
part="cell${active ? ' cell-active' : ''}${picked ? ' cell-selected' : ''}"
|
|
236
|
+
data-index=${index}
|
|
237
|
+
data-tone=${tone}
|
|
238
|
+
style="--heatmap-tone: var(--heatmap-tone-${tone})"
|
|
239
|
+
></span>`;
|
|
240
|
+
});
|
|
241
|
+
const at = this.pointer.at;
|
|
211
242
|
return html `<div
|
|
243
|
+
aria-activedescendant=${grid && at >= 0 ? `cell-${at}` : nothing}
|
|
212
244
|
aria-describedby=${readable ? 'helix-chart-summary' : nothing}
|
|
213
245
|
aria-label=${this.named || this.stripLabel}
|
|
214
|
-
|
|
246
|
+
aria-multiselectable=${grid && this.selectionMode === 'multiple' ? 'true' : nothing}
|
|
247
|
+
part="strip plot"
|
|
215
248
|
id=${CHART_PLOT}
|
|
216
|
-
role=${readable ? 'application' : 'img'}
|
|
249
|
+
role=${grid ? 'grid' : readable ? 'application' : 'img'}
|
|
217
250
|
style="--heatmap-rows: ${this.rowCount}"
|
|
218
251
|
tabindex=${readable ? '0' : nothing}
|
|
219
252
|
@click=${this.onClick}
|
|
220
253
|
@pointerover=${this.onPointerOver}
|
|
221
254
|
@pointerleave=${this.onPointerLeave}
|
|
255
|
+
@pointerup=${this.onPointerUp}
|
|
222
256
|
>
|
|
223
|
-
${
|
|
224
|
-
|
|
225
|
-
: cells.map((reading, index) => {
|
|
226
|
-
const tone = toneOf(reading, bounds, palette);
|
|
227
|
-
const active = this.pointer.at === index;
|
|
228
|
-
const picked = chosen.has(keys[index] ?? '');
|
|
229
|
-
return tone === undefined
|
|
230
|
-
? html `<span
|
|
231
|
-
part="cell${active ? ' cell-active' : ''}${picked ? ' cell-selected' : ''}"
|
|
232
|
-
data-index=${index}
|
|
233
|
-
></span>`
|
|
234
|
-
: html `<span
|
|
235
|
-
part="cell${active ? ' cell-active' : ''}${picked ? ' cell-selected' : ''}"
|
|
236
|
-
data-index=${index}
|
|
237
|
-
data-tone=${tone}
|
|
238
|
-
style="--heatmap-tone: var(--heatmap-tone-${tone})"
|
|
239
|
-
></span>`;
|
|
240
|
-
})}
|
|
241
|
-
${this.renderTooltip()}
|
|
257
|
+
${grid ? this.renderRows(drawn) : drawn}
|
|
258
|
+
${this.renderTooltip(grid)}
|
|
242
259
|
</div>
|
|
243
|
-
${cells.length === 0
|
|
260
|
+
${cells.length === 0 ? this.renderStatus() : nothing}
|
|
244
261
|
<div part="footer">
|
|
245
262
|
<span part="start"><slot name="start"></slot></span>
|
|
246
263
|
<span part="summary"><slot name="summary"></slot></span>
|
|
@@ -249,6 +266,28 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
|
|
|
249
266
|
? html `<span part="selection" role="status">${this.status.said}</span>`
|
|
250
267
|
: nothing}${this.renderTable()}`;
|
|
251
268
|
}
|
|
269
|
+
renderRows(cells) {
|
|
270
|
+
const size = this.rowCount === 1 ? cells.length : this.rowCount;
|
|
271
|
+
const rows = [];
|
|
272
|
+
for (let start = 0; start < cells.length; start += size) {
|
|
273
|
+
rows.push(html `<div part="row" role="row" style="display: contents">${cells.slice(start, start + size)}</div>`);
|
|
274
|
+
}
|
|
275
|
+
return rows;
|
|
276
|
+
}
|
|
277
|
+
renderGridCell(index, active, picked, tone) {
|
|
278
|
+
const row = this.sourceRows[this.points.length - this.drawn.length + index];
|
|
279
|
+
const name = row ? spokenReading(row) : '';
|
|
280
|
+
return html `<span
|
|
281
|
+
aria-label=${name || nothing}
|
|
282
|
+
aria-selected=${picked ? 'true' : 'false'}
|
|
283
|
+
data-index=${index}
|
|
284
|
+
data-tone=${tone ?? nothing}
|
|
285
|
+
id="cell-${index}"
|
|
286
|
+
part="cell${active ? ' cell-active' : ''}${picked ? ' cell-selected' : ''}"
|
|
287
|
+
role="gridcell"
|
|
288
|
+
style=${tone === undefined ? nothing : `--heatmap-tone: var(--heatmap-tone-${tone})`}
|
|
289
|
+
></span>`;
|
|
290
|
+
}
|
|
252
291
|
renderOverview(cells) {
|
|
253
292
|
const rows = this.sourceRows;
|
|
254
293
|
const offset = this.points.length - cells.length;
|
|
@@ -264,10 +303,10 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
|
|
|
264
303
|
});
|
|
265
304
|
return html `<p id="helix-chart-summary" part="overview">${said}</p>`;
|
|
266
305
|
}
|
|
267
|
-
renderTooltip() {
|
|
306
|
+
renderTooltip(grid = false) {
|
|
268
307
|
const at = this.pointer.at;
|
|
269
308
|
if (at < 0) {
|
|
270
|
-
return drawTooltip(this.sourceRows, -1, this.tag('tooltip'), undefined, undefined, this.pointer);
|
|
309
|
+
return drawTooltip(this.sourceRows, -1, this.tag('tooltip'), undefined, undefined, grid ? undefined : this.pointer);
|
|
271
310
|
}
|
|
272
311
|
const offset = this.points.length - this.drawn.length;
|
|
273
312
|
const cell = this.cellPx;
|
|
@@ -278,34 +317,13 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
|
|
|
278
317
|
placement: 'top',
|
|
279
318
|
x: `${column * (cell + gap) + cell / 2}px`,
|
|
280
319
|
y: `${row * (cell + gap)}px`,
|
|
281
|
-
}, undefined, this.
|
|
282
|
-
}
|
|
283
|
-
reading(at) {
|
|
284
|
-
const pointer = this.pointer;
|
|
285
|
-
const key = this.keysOf(this.drawn)[at] ?? '';
|
|
286
|
-
if (!this.selectionMode || key === '' || this.selectedLabel === '') {
|
|
287
|
-
return pointer;
|
|
288
|
-
}
|
|
289
|
-
if (!commaList(this.value).includes(key)) {
|
|
290
|
-
return pointer;
|
|
291
|
-
}
|
|
292
|
-
return {
|
|
293
|
-
column: pointer.column,
|
|
294
|
-
keyed: pointer.keyed,
|
|
295
|
-
position: pointer.position,
|
|
296
|
-
shows: (cell) => pointer.shows(cell),
|
|
297
|
-
state: this.selectedLabel,
|
|
298
|
-
};
|
|
320
|
+
}, undefined, grid ? undefined : this.pointer);
|
|
299
321
|
}
|
|
300
322
|
};
|
|
301
323
|
__decorate([
|
|
302
324
|
words,
|
|
303
325
|
property({ attribute: 'busiest-label' })
|
|
304
326
|
], HelixHeadlessHeatmap.prototype, "busiestLabel", void 0);
|
|
305
|
-
__decorate([
|
|
306
|
-
words,
|
|
307
|
-
property({ attribute: 'empty-text' })
|
|
308
|
-
], HelixHeadlessHeatmap.prototype, "emptyText", void 0);
|
|
309
327
|
__decorate([
|
|
310
328
|
property({ type: Number })
|
|
311
329
|
], HelixHeadlessHeatmap.prototype, "gap", void 0);
|
|
@@ -436,6 +436,12 @@ export declare class HelixHeadlessMenuItem extends HelixElement {
|
|
|
436
436
|
protected renderMark(submenu: boolean): TemplateResult | typeof nothing;
|
|
437
437
|
/** Whether the row says what it is from its host, for a field's `aria-activedescendant`. */
|
|
438
438
|
private get onHost();
|
|
439
|
+
/**
|
|
440
|
+
* Presses the row, as `click()` presses a native button: passed to the
|
|
441
|
+
* control inside, which is what answers a click, so a script's `click()`
|
|
442
|
+
* chooses the row, or follows its link, once.
|
|
443
|
+
*/
|
|
444
|
+
click(): void;
|
|
439
445
|
protected render(): TemplateResult | typeof nothing;
|
|
440
446
|
}
|
|
441
447
|
declare global {
|
|
@@ -240,6 +240,15 @@ let HelixHeadlessMenuItem = class HelixHeadlessMenuItem extends HelixElement {
|
|
|
240
240
|
get onHost() {
|
|
241
241
|
return this.virtual && this.onPage && !this.showingSubmenu;
|
|
242
242
|
}
|
|
243
|
+
click() {
|
|
244
|
+
const control = this.partNamed('item');
|
|
245
|
+
if (control) {
|
|
246
|
+
control.click();
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
super.click();
|
|
250
|
+
}
|
|
251
|
+
}
|
|
243
252
|
render() {
|
|
244
253
|
const submenu = this.hasSubmenu;
|
|
245
254
|
const ticks = this.choice;
|
|
@@ -89,6 +89,15 @@ export declare class HelixHeadlessNotification extends HelixHeadlessNotification
|
|
|
89
89
|
* Whether the notification starts as read, used until `read` is set.
|
|
90
90
|
*/
|
|
91
91
|
defaultRead: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* The dismiss control's name, read by screen readers, with `{label}` for the
|
|
94
|
+
* row's own words.
|
|
95
|
+
*
|
|
96
|
+
* One sentence with the words in it, so a translation puts them where its
|
|
97
|
+
* language does: `registerWords('de', { 'helix-notification': { dismissLabel:
|
|
98
|
+
* '{label} schließen' } })`.
|
|
99
|
+
*/
|
|
100
|
+
dismissLabel: string;
|
|
92
101
|
/**
|
|
93
102
|
* Locale for the timestamp.
|
|
94
103
|
*
|
|
@@ -129,13 +138,6 @@ export declare class HelixHeadlessNotification extends HelixHeadlessNotification
|
|
|
129
138
|
* the first render on the client is the server's.
|
|
130
139
|
*/
|
|
131
140
|
stop: boolean;
|
|
132
|
-
/**
|
|
133
|
-
* What a screen reader hears before an unread notification's text.
|
|
134
|
-
*
|
|
135
|
-
* The unread mark is drawn, so a reader who cannot see it hears this word in
|
|
136
|
-
* its place. Translate it with the rest of your interface.
|
|
137
|
-
*/
|
|
138
|
-
unreadLabel: string;
|
|
139
141
|
/**
|
|
140
142
|
* @internal Read by the list to identify its children across the light DOM,
|
|
141
143
|
* which is cheaper than `instanceof` and is why this is public rather than
|
|
@@ -231,11 +233,37 @@ export declare class HelixHeadlessNotification extends HelixHeadlessNotification
|
|
|
231
233
|
*/
|
|
232
234
|
private renderTime;
|
|
233
235
|
protected render(): TemplateResult;
|
|
236
|
+
/**
|
|
237
|
+
* What a screen reader hears before an unread notification's text.
|
|
238
|
+
*
|
|
239
|
+
* The unread mark is drawn, so a reader who cannot see it hears this word in
|
|
240
|
+
* its place. Translate it with the rest of your interface.
|
|
241
|
+
*/
|
|
242
|
+
unreadLabel: string;
|
|
243
|
+
/**
|
|
244
|
+
* The dismiss control's name: `dismissLabel` with the row's words.
|
|
245
|
+
*
|
|
246
|
+
* **Named for its row**, because a feed of rows each with a control named
|
|
247
|
+
* Dismiss is a list of buttons that says nothing about which notice each one
|
|
248
|
+
* ends. The words are the default slot's, the ones `helix-dismiss` reports,
|
|
249
|
+
* so a row's actions are not read into it. A page's own `dismiss-label` with
|
|
250
|
+
* no `{label}` is followed by them, unless it holds them already, as `Tag`
|
|
251
|
+
* names its remove button. Read from the children, so the verb alone on a
|
|
252
|
+
* server, until the update after.
|
|
253
|
+
*/
|
|
254
|
+
private get dismissName();
|
|
255
|
+
protected firstUpdated(changed: PropertyValues): void;
|
|
234
256
|
private readonly handleActions;
|
|
235
257
|
private readonly handleDismiss;
|
|
236
258
|
/**
|
|
237
|
-
* Where focus goes when this row leaves: the next row
|
|
238
|
-
*
|
|
259
|
+
* Where focus goes when this row leaves: the next row, the one before, then
|
|
260
|
+
* the first control left in the feed.
|
|
261
|
+
*
|
|
262
|
+
* **The row itself, and not a control in it**, because the row is what a
|
|
263
|
+
* reader moves through the feed by, and every row takes focus. Sent to the
|
|
264
|
+
* next dismiss control, focus passed over each row with no controls between,
|
|
265
|
+
* and to Load more where no row after had one. Focused, the row becomes the
|
|
266
|
+
* feed's stop, as one reached with the arrows does.
|
|
239
267
|
*/
|
|
240
268
|
private neighbor;
|
|
241
269
|
}
|
|
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
import { customElement, deepActiveElement, deepContains, focusableElements, HelixElement, HelixReadEvent, MixinDismissible, requestChange, slotFilled, words, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { customElement, deepActiveElement, deepContains, focusableElements, formatWords, HelixElement, HelixReadEvent, MixinDismissible, requestChange, slotFilled, slottedWords, words, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { x } from '@fusion.dev/helix-icons';
|
|
9
9
|
import { nothing } from 'lit';
|
|
10
10
|
import { property } from 'lit/decorators.js';
|
|
@@ -16,13 +16,14 @@ let HelixHeadlessNotification = class HelixHeadlessNotification extends MixinDis
|
|
|
16
16
|
super(...arguments);
|
|
17
17
|
this.at = '';
|
|
18
18
|
this.defaultRead = false;
|
|
19
|
+
this.dismissLabel = 'Dismiss {label}';
|
|
19
20
|
this.locale = '';
|
|
20
21
|
this.position = 0;
|
|
21
22
|
this.setSize = 0;
|
|
22
23
|
this.stop = true;
|
|
23
|
-
this.unreadLabel = 'Unread';
|
|
24
24
|
this.isNotification = true;
|
|
25
25
|
this.readState = undefined;
|
|
26
|
+
this.unreadLabel = 'Unread';
|
|
26
27
|
this.handleActions = () => {
|
|
27
28
|
this.markActions();
|
|
28
29
|
};
|
|
@@ -82,7 +83,7 @@ let HelixHeadlessNotification = class HelixHeadlessNotification extends MixinDis
|
|
|
82
83
|
return html `<button
|
|
83
84
|
part="dismiss"
|
|
84
85
|
type="button"
|
|
85
|
-
aria-label=${this.
|
|
86
|
+
aria-label=${this.dismissName}
|
|
86
87
|
@click=${this.handleDismiss}
|
|
87
88
|
>
|
|
88
89
|
<slot name="dismiss">
|
|
@@ -132,6 +133,18 @@ let HelixHeadlessNotification = class HelixHeadlessNotification extends MixinDis
|
|
|
132
133
|
</div>
|
|
133
134
|
</div>`;
|
|
134
135
|
}
|
|
136
|
+
get dismissName() {
|
|
137
|
+
const { dismissLabel } = this;
|
|
138
|
+
const label = this.childrenKnown ? slottedWords(this, ['']) : '';
|
|
139
|
+
if (dismissLabel.includes('{label}')) {
|
|
140
|
+
return formatWords(this, 'dismissLabel', { label }).trim();
|
|
141
|
+
}
|
|
142
|
+
return label === '' || dismissLabel.includes(label) ? dismissLabel : `${dismissLabel} ${label}`;
|
|
143
|
+
}
|
|
144
|
+
firstUpdated(changed) {
|
|
145
|
+
super.firstUpdated(changed);
|
|
146
|
+
this.renderRoot.addEventListener('slotchange', () => this.requestUpdate());
|
|
147
|
+
}
|
|
135
148
|
neighbor() {
|
|
136
149
|
let feed = this.parentElement;
|
|
137
150
|
while (feed && !('markEveryoneRead' in feed)) {
|
|
@@ -143,7 +156,7 @@ let HelixHeadlessNotification = class HelixHeadlessNotification extends MixinDis
|
|
|
143
156
|
const at = rows.indexOf(this);
|
|
144
157
|
const order = [...rows.slice(at + 1), ...rows.slice(0, Math.max(at, 0)).reverse()];
|
|
145
158
|
for (const row of order) {
|
|
146
|
-
const target = row.shadowRoot?.querySelector('[part="
|
|
159
|
+
const target = row.shadowRoot?.querySelector('[part="item"]');
|
|
147
160
|
if (target) {
|
|
148
161
|
return target;
|
|
149
162
|
}
|
|
@@ -162,6 +175,10 @@ __decorate([
|
|
|
162
175
|
__decorate([
|
|
163
176
|
property({ attribute: 'default-read', type: Boolean })
|
|
164
177
|
], HelixHeadlessNotification.prototype, "defaultRead", void 0);
|
|
178
|
+
__decorate([
|
|
179
|
+
words,
|
|
180
|
+
property({ attribute: 'dismiss-label' })
|
|
181
|
+
], HelixHeadlessNotification.prototype, "dismissLabel", void 0);
|
|
165
182
|
__decorate([
|
|
166
183
|
property()
|
|
167
184
|
], HelixHeadlessNotification.prototype, "locale", void 0);
|
|
@@ -87,6 +87,12 @@ export declare class HelixHeadlessSpeedDialItem extends HelixElement {
|
|
|
87
87
|
* keyboard controller uses for the arrow keys.
|
|
88
88
|
*/
|
|
89
89
|
focusItem(): void;
|
|
90
|
+
/**
|
|
91
|
+
* Presses the action, as `click()` presses a native button: passed to the
|
|
92
|
+
* control inside, which is what answers a click, so a script's `click()`
|
|
93
|
+
* chooses the action and follows its link, once.
|
|
94
|
+
*/
|
|
95
|
+
click(): void;
|
|
90
96
|
/** Chooses the action, the same as pressing it. */
|
|
91
97
|
select(): void;
|
|
92
98
|
/**
|
|
@@ -43,6 +43,15 @@ let HelixHeadlessSpeedDialItem = class HelixHeadlessSpeedDialItem extends HelixE
|
|
|
43
43
|
focusItem() {
|
|
44
44
|
this.focus();
|
|
45
45
|
}
|
|
46
|
+
click() {
|
|
47
|
+
const control = this.partNamed('item');
|
|
48
|
+
if (control) {
|
|
49
|
+
control.click();
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
super.click();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
46
55
|
select() {
|
|
47
56
|
this.choose();
|
|
48
57
|
}
|
package/dist/tabs/tab/tab.d.ts
CHANGED
|
@@ -112,6 +112,12 @@ export declare class HelixHeadlessTab extends HelixHeadlessTab_base {
|
|
|
112
112
|
* which changes nothing and so re-renders nothing.
|
|
113
113
|
*/
|
|
114
114
|
protected firstUpdated(changed: PropertyValues): void;
|
|
115
|
+
/**
|
|
116
|
+
* Presses the tab, as `click()` presses a native button: passed to the
|
|
117
|
+
* control inside, which is what answers a click, so a script's `click()`
|
|
118
|
+
* selects the tab, or follows its link, once.
|
|
119
|
+
*/
|
|
120
|
+
click(): void;
|
|
115
121
|
protected render(): TemplateResult;
|
|
116
122
|
}
|
|
117
123
|
declare global {
|
package/dist/tabs/tab/tab.js
CHANGED
|
@@ -67,6 +67,15 @@ let HelixHeadlessTab = class HelixHeadlessTab extends MixinDismissible(MixinSele
|
|
|
67
67
|
super.firstUpdated(changed);
|
|
68
68
|
this.finishHydration();
|
|
69
69
|
}
|
|
70
|
+
click() {
|
|
71
|
+
const control = this.partNamed('tab');
|
|
72
|
+
if (control) {
|
|
73
|
+
control.click();
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
super.click();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
70
79
|
render() {
|
|
71
80
|
const tabindex = this.selected || !this.childrenKnown ? '0' : '-1';
|
|
72
81
|
const shortcut = this.dismissible && !this.disabled ? 'Delete' : nothing;
|