@fusion.dev/helix-headless 0.184.0 → 0.186.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 +3796 -1105
- package/dist/accordion/item/accordion-item.js +1 -1
- package/dist/button-group/root/button-group-root.js +1 -1
- package/dist/canvas/root/canvas-root.js +1 -1
- package/dist/data-table/root/data-table-root.d.ts +25 -6
- package/dist/data-table/root/data-table-root.js +21 -9
- package/dist/dialog/root/dialog-root.js +1 -1
- package/dist/drawer/root/drawer-root.js +1 -1
- package/dist/drilldown/item/drilldown-item.js +1 -1
- package/dist/element-events.generated.js +7 -0
- package/dist/graph/root/graph-root.d.ts +6 -4
- package/dist/graph/root/graph-root.js +3 -3
- package/dist/heatmap/root/heatmap-root.d.ts +15 -15
- package/dist/icon/all/index.js +0 -3
- package/dist/icon/root/icon-root.d.ts +11 -18
- package/dist/icon/root/icon-root.js +4 -16
- package/dist/icon/set.js +2 -16
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/isometric-heatmap/index.d.ts +1 -0
- package/dist/isometric-heatmap/index.js +1 -0
- package/dist/isometric-heatmap/projection.d.ts +70 -0
- package/dist/isometric-heatmap/projection.js +49 -0
- package/dist/isometric-heatmap/root/isometric-heatmap-root.d.ts +118 -0
- package/dist/isometric-heatmap/root/isometric-heatmap-root.js +127 -0
- package/dist/lightbox/root/lightbox-root.js +1 -1
- package/dist/list/root/list-root.js +1 -1
- package/dist/map/root/map-root.js +1 -1
- package/dist/menu/item/menu-item.js +1 -1
- package/dist/outline/root/outline-root.js +2 -2
- package/dist/popover/root/popover-root.js +1 -1
- package/dist/table/root/table-root.d.ts +4 -6
- package/dist/table/root/table-root.js +3 -29
- package/dist/table/tracks.d.ts +20 -0
- package/dist/table/tracks.js +28 -0
- package/dist/tabs/root/tabs-root.js +4 -5
- package/dist/tabs/tab/tab.js +2 -3
- package/dist/time-picker/root/time-picker-root.js +1 -1
- package/dist/toggle/root/toggle-root.js +1 -1
- package/dist/tour/root/tour-root.js +1 -1
- package/dist/tree/root/tree-root.js +1 -1
- package/dist/video/root/video-root.js +1 -1
- package/lint.json +1 -1
- package/package.json +6 -6
- package/types/jsx.d.ts +1340 -74
- package/types/svelte.d.ts +1 -0
- package/types/vue.d.ts +279 -13
- package/types/words.d.ts +69 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { nothing, type TemplateResult } from 'lit';
|
|
2
|
+
import '../../tooltip/index.js';
|
|
3
|
+
import { HelixHeadlessHeatmap } from '../../heatmap/root/heatmap-root.js';
|
|
4
|
+
/**
|
|
5
|
+
* @summary A heatmap drawn as a relief, where each reading stands as tall as its value.
|
|
6
|
+
*
|
|
7
|
+
* @description
|
|
8
|
+
* The same strip as `Heatmap`, seen from above at an angle. Each reading is a
|
|
9
|
+
* prism: its top face takes the cell's tone, and its height repeats the value,
|
|
10
|
+
* measured from zero against the highest reading in the table. `rows="7"`
|
|
11
|
+
* makes a contribution graph, with each week a column running down to the
|
|
12
|
+
* right.
|
|
13
|
+
*
|
|
14
|
+
* **The height says what the tone says.** Read the exact value from the tooltip
|
|
15
|
+
* or the table, and compare readings by tone. A tall reading at the front can
|
|
16
|
+
* hide a short one behind it, so the newest column stands at the front, where
|
|
17
|
+
* nothing covers it.
|
|
18
|
+
*
|
|
19
|
+
* **Everything else is `Heatmap`.** The table named by `for`, the tones and
|
|
20
|
+
* thresholds, the keys, the tooltip, the overview, `selection-mode`, and the
|
|
21
|
+
* events all work as they do there. The class extends the headless heatmap,
|
|
22
|
+
* and its own entry point carries the drawing, so a page using only `Heatmap`
|
|
23
|
+
* loads none of it.
|
|
24
|
+
*
|
|
25
|
+
* **`flat` lays every prism down** at no height, which leaves the tones on a
|
|
26
|
+
* plane at the same angle.
|
|
27
|
+
*
|
|
28
|
+
* **`gap` and `min-cell` set the smallest cell.** The drawing scales as a
|
|
29
|
+
* whole, so a wider box draws larger cells and the gap grows with them. A box
|
|
30
|
+
* too narrow for every column at `min-cell` draws the newest that fit.
|
|
31
|
+
*
|
|
32
|
+
* **The drawing is SVG and holds still.** It scales with its box through its
|
|
33
|
+
* `viewBox`, needs no WebGL, and has no motion to pause.
|
|
34
|
+
*
|
|
35
|
+
* **Keyboard.** The keys are `Heatmap`'s. Down moves to the next reading in a
|
|
36
|
+
* column, drawn down to the left, and Right to the same reading in the next
|
|
37
|
+
* column, drawn down to the right.
|
|
38
|
+
*
|
|
39
|
+
* | Key | What it does |
|
|
40
|
+
* |---|---|
|
|
41
|
+
* | Tab | Moves focus to the strip and shows the first cell. |
|
|
42
|
+
* | Down, Up | Moves to the next or the previous cell. |
|
|
43
|
+
* | Right, Left | Moves a column on or back, a week with `rows="7"`. On a right-to-left page, Left moves on. |
|
|
44
|
+
* | Home, End | Moves to the first or the last cell. |
|
|
45
|
+
* | Page Down, Page Up | Moves a month on or back where the rows carry dates, and four columns otherwise. |
|
|
46
|
+
* | Enter, Space | Presses the cell, which sends `helix-reading-press`, and selects it or lets it go under `selection-mode`. |
|
|
47
|
+
* | 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"`. |
|
|
48
|
+
* | Escape | Puts the cell's tooltip away. |
|
|
49
|
+
*
|
|
50
|
+
* @since 0.185.0
|
|
51
|
+
* @status experimental
|
|
52
|
+
* @category charts
|
|
53
|
+
* @aliases Isometric chart, 3D heatmap, Relief chart, Isometric contribution graph
|
|
54
|
+
* @documentation https://fusion.dev/helix/components/isometric-heatmap
|
|
55
|
+
*
|
|
56
|
+
* @csspart cell - One reading: a group holding its three faces.
|
|
57
|
+
* @csspart drawing - The SVG every prism is drawn in. It is mirrored on a right-to-left page.
|
|
58
|
+
* @csspart left-face - The face of a prism toward the next row, drawn at its lower left.
|
|
59
|
+
* @csspart right-face - The face of a prism toward the next column, drawn at its lower right.
|
|
60
|
+
* @csspart row - A column of prisms, which a grid reads as a row, under `selection-mode`.
|
|
61
|
+
* @csspart strip - 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.
|
|
62
|
+
* @csspart top-face - The top of a prism, which carries the reading's tone.
|
|
63
|
+
*
|
|
64
|
+
* @cssproperty --isometric-heatmap-span - How many cells the drawing spans across: its columns and its rows together. The component writes it on the strip, and a stylesheet that caps the cell size reads it.
|
|
65
|
+
*/
|
|
66
|
+
export declare class HelixHeadlessIsometricHeatmap extends HelixHeadlessHeatmap {
|
|
67
|
+
/**
|
|
68
|
+
* The key this family's words are registered under, its styled tag:
|
|
69
|
+
* `registerWords('de', { 'helix-isometric-heatmap': { emptyText: 'Keine Werte' } })`.
|
|
70
|
+
*/
|
|
71
|
+
static words: string;
|
|
72
|
+
protected static markRules: string[];
|
|
73
|
+
/**
|
|
74
|
+
* Draws every prism at no height, so the tones lie on a plane.
|
|
75
|
+
*/
|
|
76
|
+
flat: boolean;
|
|
77
|
+
/**
|
|
78
|
+
* How tall the highest reading stands, in cells. Every other reading stands
|
|
79
|
+
* at its share of it.
|
|
80
|
+
*/
|
|
81
|
+
relief: number;
|
|
82
|
+
/** The prisms drawn on the last render, which the tooltip is placed against. */
|
|
83
|
+
private shown;
|
|
84
|
+
/**
|
|
85
|
+
* How many readings fit, as whole columns, newest kept.
|
|
86
|
+
*
|
|
87
|
+
* The drawing runs at 30 degrees, so a cell takes `ACROSS` of its width, and
|
|
88
|
+
* the rows run across too: a strip `rows` deep starts that many cells to the
|
|
89
|
+
* left of its first column.
|
|
90
|
+
*/
|
|
91
|
+
protected get room(): number;
|
|
92
|
+
/** The drawn readings as prisms, standing against the highest reading in the table. */
|
|
93
|
+
private drawnRelief;
|
|
94
|
+
protected render(): TemplateResult;
|
|
95
|
+
/**
|
|
96
|
+
* One reading as a prism. The sides come first, so the top face is painted
|
|
97
|
+
* over the edge they share with it.
|
|
98
|
+
*
|
|
99
|
+
* Under `selection-mode` the prism is a `gridcell` named by its row in the
|
|
100
|
+
* table and saying whether it is selected, as the flat strip's cell is.
|
|
101
|
+
*/
|
|
102
|
+
private renderPrism;
|
|
103
|
+
/**
|
|
104
|
+
* The prisms as a grid's rows, one for each column of the strip, which is
|
|
105
|
+
* also the order they are painted in.
|
|
106
|
+
*/
|
|
107
|
+
private renderPrismRows;
|
|
108
|
+
/**
|
|
109
|
+
* The bubble over the top face of the reading the pointer or the keys rest
|
|
110
|
+
* on, placed as a share of the drawing, which the strip's box matches.
|
|
111
|
+
*/
|
|
112
|
+
protected renderTooltip(grid?: boolean): TemplateResult | typeof nothing;
|
|
113
|
+
}
|
|
114
|
+
declare global {
|
|
115
|
+
interface HTMLElementTagNameMap {
|
|
116
|
+
'helix-headless-isometric-heatmap': HelixHeadlessIsometricHeatmap;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { __decorate } from "tslib";
|
|
2
|
+
import { CHART_PLOT, commaList, customElement, drawTooltip, MIRROR_RULE, spokenReading, writtenList, writtenNumbers, } from '@fusion.dev/helix-core';
|
|
3
|
+
import { html, nothing, svg } from 'lit';
|
|
4
|
+
import { property } from 'lit/decorators.js';
|
|
5
|
+
import '../../tooltip/index.js';
|
|
6
|
+
import { fits, toneOf } from '../../heatmap/root/buckets.js';
|
|
7
|
+
import { HelixHeadlessHeatmap } from '../../heatmap/root/heatmap-root.js';
|
|
8
|
+
import { ACROSS, heightsOf, highestOf, reliefOf } from '../projection.js';
|
|
9
|
+
let HelixHeadlessIsometricHeatmap = class HelixHeadlessIsometricHeatmap extends HelixHeadlessHeatmap {
|
|
10
|
+
constructor() {
|
|
11
|
+
super(...arguments);
|
|
12
|
+
this.flat = false;
|
|
13
|
+
this.relief = 2;
|
|
14
|
+
this.shown = undefined;
|
|
15
|
+
}
|
|
16
|
+
static { this.words = 'helix-isometric-heatmap'; }
|
|
17
|
+
static { this.markRules = [MIRROR_RULE]; }
|
|
18
|
+
get room() {
|
|
19
|
+
const width = this.width;
|
|
20
|
+
if (width === undefined) {
|
|
21
|
+
return this.points.length;
|
|
22
|
+
}
|
|
23
|
+
const pitch = this.cellPx + this.gapPx;
|
|
24
|
+
const across = width / ACROSS - this.rowCount * pitch - this.gapPx;
|
|
25
|
+
return fits(this.points.length, Math.max(across, Number.MIN_VALUE), this.cellPx, this.gapPx, this.rowCount);
|
|
26
|
+
}
|
|
27
|
+
drawnRelief() {
|
|
28
|
+
const tallest = this.flat ? 0 : Math.max(0, this.relief || 0);
|
|
29
|
+
const highest = highestOf(this.points.map((point) => point.value));
|
|
30
|
+
const heights = heightsOf(this.drawn.map((point) => point.value), highest, tallest);
|
|
31
|
+
return reliefOf(heights, this.rowCount, this.cellPx / (this.cellPx + this.gapPx));
|
|
32
|
+
}
|
|
33
|
+
render() {
|
|
34
|
+
const bounds = writtenNumbers(this.thresholds);
|
|
35
|
+
const palette = writtenList(this.tones);
|
|
36
|
+
const cells = this.drawn;
|
|
37
|
+
const readable = cells.length > 0;
|
|
38
|
+
const grid = readable && this.selectionMode !== undefined;
|
|
39
|
+
const keys = this.selectionMode ? this.keysOf(cells) : [];
|
|
40
|
+
const chosen = new Set(this.selectionMode ? commaList(this.value) : []);
|
|
41
|
+
const relief = this.drawnRelief();
|
|
42
|
+
this.shown = relief;
|
|
43
|
+
const at = this.pointer.at;
|
|
44
|
+
const prisms = relief.prisms.map((prism) => this.renderPrism(prism.index, prism.top, prism.left, prism.right, at === prism.index, chosen.has(keys[prism.index] ?? ''), grid, toneOf(cells[prism.index] ?? { value: undefined }, bounds, palette)));
|
|
45
|
+
const { box } = relief;
|
|
46
|
+
const drawing = readable
|
|
47
|
+
? html `<svg
|
|
48
|
+
aria-hidden=${grid ? nothing : 'true'}
|
|
49
|
+
data-mirror
|
|
50
|
+
part="drawing"
|
|
51
|
+
role=${grid ? 'none' : nothing}
|
|
52
|
+
viewBox="${box.x} ${box.y} ${box.width} ${box.height}"
|
|
53
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
54
|
+
>${grid ? this.renderPrismRows(prisms) : prisms}</svg>`
|
|
55
|
+
: nothing;
|
|
56
|
+
return html `<div
|
|
57
|
+
aria-activedescendant=${grid && at >= 0 ? `cell-${at}` : nothing}
|
|
58
|
+
aria-describedby=${readable ? 'helix-chart-summary' : nothing}
|
|
59
|
+
aria-label=${this.named || this.stripLabel}
|
|
60
|
+
aria-multiselectable=${grid && this.selectionMode === 'multiple' ? 'true' : nothing}
|
|
61
|
+
part="strip plot"
|
|
62
|
+
id=${CHART_PLOT}
|
|
63
|
+
role=${grid ? 'grid' : readable ? 'application' : 'img'}
|
|
64
|
+
style="--isometric-heatmap-span: ${Math.ceil(cells.length / this.rowCount) + Math.min(this.rowCount, cells.length)}"
|
|
65
|
+
tabindex=${readable ? '0' : nothing}
|
|
66
|
+
@click=${this.onClick}
|
|
67
|
+
@keydown=${this.onStripKeydown}
|
|
68
|
+
@pointerover=${this.onPointerOver}
|
|
69
|
+
@pointerleave=${this.onPointerLeave}
|
|
70
|
+
@pointerup=${this.onPointerUp}
|
|
71
|
+
>${drawing}${this.renderTooltip(grid)}</div>
|
|
72
|
+
${cells.length === 0 ? this.renderStatus() : nothing}
|
|
73
|
+
<div part="footer">
|
|
74
|
+
<span part="start"><slot name="start"></slot></span>
|
|
75
|
+
<span part="summary"><slot name="summary"></slot></span>
|
|
76
|
+
<span part="end"><slot name="end"></slot></span>
|
|
77
|
+
</div>${readable ? this.renderOverview(cells) : nothing}${this.status.spoken
|
|
78
|
+
? html `<span part="selection" role="status">${this.status.said}</span>`
|
|
79
|
+
: nothing}${this.renderTable()}`;
|
|
80
|
+
}
|
|
81
|
+
renderPrism(index, top, left, right, active, picked, grid, tone) {
|
|
82
|
+
const row = grid ? this.sourceRows[this.points.length - this.drawn.length + index] : undefined;
|
|
83
|
+
const name = row ? spokenReading(row) : '';
|
|
84
|
+
return svg `<g
|
|
85
|
+
aria-label=${grid && name ? name : nothing}
|
|
86
|
+
aria-selected=${grid ? (picked ? 'true' : 'false') : nothing}
|
|
87
|
+
data-index=${index}
|
|
88
|
+
data-tone=${tone ?? nothing}
|
|
89
|
+
id=${grid ? `cell-${index}` : nothing}
|
|
90
|
+
part="cell${active ? ' cell-active' : ''}${picked ? ' cell-selected' : ''}"
|
|
91
|
+
role=${grid ? 'gridcell' : nothing}
|
|
92
|
+
style=${tone === undefined ? nothing : `--heatmap-tone: var(--heatmap-tone-${tone})`}
|
|
93
|
+
><path d=${left} part="left-face"></path><path d=${right} part="right-face"></path><path d=${top} part="top-face"></path></g>`;
|
|
94
|
+
}
|
|
95
|
+
renderPrismRows(prisms) {
|
|
96
|
+
const size = this.rowCount === 1 ? prisms.length : this.rowCount;
|
|
97
|
+
const rows = [];
|
|
98
|
+
for (let start = 0; start < prisms.length; start += size) {
|
|
99
|
+
rows.push(svg `<g part="row" role="row">${prisms.slice(start, start + size)}</g>`);
|
|
100
|
+
}
|
|
101
|
+
return rows;
|
|
102
|
+
}
|
|
103
|
+
renderTooltip(grid = false) {
|
|
104
|
+
const at = this.pointer.at;
|
|
105
|
+
const prism = this.shown?.prisms[at];
|
|
106
|
+
const box = this.shown?.box;
|
|
107
|
+
const reading = grid ? undefined : this.pointer;
|
|
108
|
+
if (at < 0 || !prism || !box || box.width <= 0 || box.height <= 0) {
|
|
109
|
+
return drawTooltip(this.sourceRows, -1, this.tag('tooltip'), undefined, undefined, reading);
|
|
110
|
+
}
|
|
111
|
+
return drawTooltip(this.sourceRows, this.points.length - this.drawn.length + at, this.tag('tooltip'), {
|
|
112
|
+
placement: 'top',
|
|
113
|
+
x: `${((prism.peak.x - box.x) / box.width) * 100}%`,
|
|
114
|
+
y: `${((prism.peak.y - box.y) / box.height) * 100}%`,
|
|
115
|
+
}, undefined, reading);
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
__decorate([
|
|
119
|
+
property({ type: Boolean })
|
|
120
|
+
], HelixHeadlessIsometricHeatmap.prototype, "flat", void 0);
|
|
121
|
+
__decorate([
|
|
122
|
+
property({ type: Number })
|
|
123
|
+
], HelixHeadlessIsometricHeatmap.prototype, "relief", void 0);
|
|
124
|
+
HelixHeadlessIsometricHeatmap = __decorate([
|
|
125
|
+
customElement('helix-headless-isometric-heatmap')
|
|
126
|
+
], HelixHeadlessIsometricHeatmap);
|
|
127
|
+
export { HelixHeadlessIsometricHeatmap };
|
|
@@ -356,7 +356,7 @@ let HelixHeadlessLightbox = class HelixHeadlessLightbox extends MixinControllerA
|
|
|
356
356
|
return;
|
|
357
357
|
}
|
|
358
358
|
if (this.open && !dialog.open) {
|
|
359
|
-
auditName(this, () => null, '
|
|
359
|
+
auditName(this, () => null, 'lightbox', () => this.label !== '');
|
|
360
360
|
dialog.showModal();
|
|
361
361
|
dialog.focus({ preventScroll: true });
|
|
362
362
|
this.idle.stir();
|
|
@@ -366,7 +366,7 @@ let HelixHeadlessList = class HelixHeadlessList extends HelixElement {
|
|
|
366
366
|
this.sync();
|
|
367
367
|
}
|
|
368
368
|
if (this.interactive) {
|
|
369
|
-
auditName(this, () => null, '
|
|
369
|
+
auditName(this, () => null, 'list', () => this.label !== '');
|
|
370
370
|
}
|
|
371
371
|
if (this.childrenKnown) {
|
|
372
372
|
this.toggleState('empty', this.rows.length === 0);
|
|
@@ -346,7 +346,7 @@ let HelixHeadlessMap = class HelixHeadlessMap extends MixinViewport(HelixElement
|
|
|
346
346
|
firstUpdated(changed) {
|
|
347
347
|
super.firstUpdated(changed);
|
|
348
348
|
this.finishHydration();
|
|
349
|
-
auditName(this, () => null, '
|
|
349
|
+
auditName(this, () => null, 'map', () => this.label !== '');
|
|
350
350
|
}
|
|
351
351
|
updated(changed) {
|
|
352
352
|
super.updated(changed);
|
|
@@ -116,7 +116,7 @@ let HelixHeadlessMenuItem = class HelixHeadlessMenuItem extends HelixElement {
|
|
|
116
116
|
: [],
|
|
117
117
|
});
|
|
118
118
|
if (this.partNamed('item')) {
|
|
119
|
-
auditName(this, () => this.partNamed('item') ?? undefined, '
|
|
119
|
+
auditName(this, () => this.partNamed('item') ?? undefined, 'row');
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
122
|
renderDescription() {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { __decorate } from "tslib";
|
|
2
|
-
import { customElement, elementById, HelixActivateEvent, HelixElement, IndicatorController, plainClick, reflectElements,
|
|
2
|
+
import { customElement, elementById, HelixActivateEvent, HelixElement, IndicatorController, plainClick, reflectElements, scrollingAncestor, scrollWithin, words, } from '@fusion.dev/helix-core';
|
|
3
3
|
import { html, nothing } from 'lit';
|
|
4
4
|
import { property } from 'lit/decorators.js';
|
|
5
5
|
import { levelOf, nest } from '../levels.js';
|
|
@@ -151,7 +151,7 @@ let HelixHeadlessOutline = class HelixHeadlessOutline extends HelixElement {
|
|
|
151
151
|
const sections = this.scroller;
|
|
152
152
|
const node = scrollingAncestor(link, { axis: 'y', overflowing: true, stop: this.page });
|
|
153
153
|
if (node && node !== sections && !node.contains(sections)) {
|
|
154
|
-
|
|
154
|
+
scrollWithin(link, node);
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
157
|
get atEnd() {
|
|
@@ -216,7 +216,7 @@ let HelixHeadlessPopover = class HelixHeadlessPopover extends MixinControllerOpe
|
|
|
216
216
|
this.tellTrigger();
|
|
217
217
|
this.nameByHeading();
|
|
218
218
|
if (changed.has('open') && this.open) {
|
|
219
|
-
auditName(this, () => null, '
|
|
219
|
+
auditName(this, () => null, 'popover', () => this.label !== '' || this.namedByHeading || headingIn(this.slotted) != null);
|
|
220
220
|
}
|
|
221
221
|
if (this.settled) {
|
|
222
222
|
this.hideAround();
|
|
@@ -243,13 +243,11 @@ export declare class HelixHeadlessTable extends HelixHeadlessTable_base {
|
|
|
243
243
|
* **It is also the only track list a server can send.** The shim has no tree
|
|
244
244
|
* to walk, so a render there knows of no columns and this attribute is the
|
|
245
245
|
* whole answer: a table given one lays its grid out in the first paint, and a
|
|
246
|
-
* table sized only by its columns
|
|
246
|
+
* table sized only by its columns takes, until it upgrades, a track for each
|
|
247
|
+
* column at the width a column with no `width` takes (`helix.css`).
|
|
247
248
|
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
* and a list here would be a second way to say what that says. This table's
|
|
251
|
-
* cells are markup, and a table written as rows of cells with no column
|
|
252
|
-
* declared in it has nowhere else to be sized from.
|
|
249
|
+
* Data Table takes the same list, for the same reason: a data table that
|
|
250
|
+
* serves its first page as rows is laid out by it before it upgrades.
|
|
253
251
|
*/
|
|
254
252
|
columns: string;
|
|
255
253
|
/**
|
|
@@ -1,38 +1,12 @@
|
|
|
1
1
|
import { __decorate } from "tslib";
|
|
2
|
-
import { AriaForwardController, anchorAt, assignSlots, auditName, commaList, customElement, cycle, everyState, followLink, format, formatWords, HelixBeforeToggleEvent, HelixElement, HelixLoadMoreEvent, HelixSelectEvent, HelixSelectedEvent, HelixSortEvent, HelixSortedEvent, HelixToggleEvent, inRowOrder, MixinCollection, onAndroid, parse, ReorderController, rangeAdd, reflectElements, requestChange, scrollingAncestor, sort,
|
|
2
|
+
import { AriaForwardController, anchorAt, assignSlots, auditName, commaList, customElement, cycle, everyState, followLink, format, formatWords, HelixBeforeToggleEvent, HelixElement, HelixLoadMoreEvent, HelixSelectEvent, HelixSelectedEvent, HelixSortEvent, HelixSortedEvent, HelixToggleEvent, inRowOrder, MixinCollection, onAndroid, parse, ReorderController, rangeAdd, reflectElements, requestChange, scrollingAncestor, sort, toggleEvery, treeArrow, VIRTUAL_RULE, VirtualizeController, whenHydrated, words, } from '@fusion.dev/helix-core';
|
|
3
3
|
import { nothing } from 'lit';
|
|
4
4
|
import { property, state } from 'lit/decorators.js';
|
|
5
5
|
import { html } from 'lit/static-html.js';
|
|
6
6
|
import '../../checkbox/index.js';
|
|
7
7
|
import { edgeOf } from '../column/table-column.js';
|
|
8
8
|
import { TableGridController } from '../grid-controller.js';
|
|
9
|
-
|
|
10
|
-
const words = splitValues(list, ' ');
|
|
11
|
-
const valid = (track) => typeof CSS === 'undefined' || CSS.supports('grid-template-columns', track);
|
|
12
|
-
const tracks = [];
|
|
13
|
-
let names = '';
|
|
14
|
-
for (const one of words) {
|
|
15
|
-
if (tracks.length >= wanted) {
|
|
16
|
-
break;
|
|
17
|
-
}
|
|
18
|
-
if (one.startsWith('[')) {
|
|
19
|
-
names += `${one} `;
|
|
20
|
-
continue;
|
|
21
|
-
}
|
|
22
|
-
const repeat = /^repeat\(\s*(\d+)\s*,(.*)\)$/is.exec(one);
|
|
23
|
-
if (repeat) {
|
|
24
|
-
const inner = tracksOf(repeat[2] ?? '', wanted);
|
|
25
|
-
for (let time = 0; time < Number(repeat[1]) && tracks.length < wanted; time += 1) {
|
|
26
|
-
tracks.push(...inner.map((track, index) => (time === 0 && index === 0 ? names + track : track)));
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
else if (!/^repeat\(/i.test(one)) {
|
|
30
|
-
tracks.push(valid(names + one) ? names + one : '');
|
|
31
|
-
}
|
|
32
|
-
names = '';
|
|
33
|
-
}
|
|
34
|
-
return tracks.slice(0, wanted);
|
|
35
|
-
};
|
|
9
|
+
import { tracksOf } from '../tracks.js';
|
|
36
10
|
const SHORT = 32;
|
|
37
11
|
const FLOOR = '14ch';
|
|
38
12
|
const SAMPLE = 200;
|
|
@@ -812,7 +786,7 @@ let HelixHeadlessTable = class HelixHeadlessTable extends MixinCollection(HelixE
|
|
|
812
786
|
this.markSorted();
|
|
813
787
|
this.markSelected();
|
|
814
788
|
this.nameByHeading();
|
|
815
|
-
auditName(this, () => null, '
|
|
789
|
+
auditName(this, () => null, 'table', () => this.label !== '' || this.headings.length > 0);
|
|
816
790
|
if (this.childrenKnown) {
|
|
817
791
|
this.gridKeys.mark();
|
|
818
792
|
this.gridKeys.place();
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The first `wanted` tracks of a track list, one entry each, so every declared
|
|
3
|
+
* column can take the one in its place.
|
|
4
|
+
*
|
|
5
|
+
* Split on the spaces between tracks and never inside a function, so
|
|
6
|
+
* `minmax(0, 1fr)` stays one track. A `repeat()` with a count is that many
|
|
7
|
+
* tracks, written out; an automatic one names no count and yields none. Line
|
|
8
|
+
* names go with the track after them.
|
|
9
|
+
*
|
|
10
|
+
* Everything above the split is a fact about a grid track list rather than
|
|
11
|
+
* about a value list, which is why only the split itself is shared.
|
|
12
|
+
*
|
|
13
|
+
* **A track the grid would refuse yields nothing either**, where the platform
|
|
14
|
+
* can say. One invalid track makes the whole `grid-template-columns` invalid,
|
|
15
|
+
* which a grid ignores in silence and draws as one column. That is the failure
|
|
16
|
+
* that once had `columns` deprecated, a list of headings handed over as widths,
|
|
17
|
+
* and dealt out a track at a time it costs the one column its width and no
|
|
18
|
+
* more.
|
|
19
|
+
*/
|
|
20
|
+
export declare const tracksOf: (list: string, wanted: number) => string[];
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { splitValues } from '@fusion.dev/helix-core';
|
|
2
|
+
export const tracksOf = (list, wanted) => {
|
|
3
|
+
const words = splitValues(list, ' ');
|
|
4
|
+
const valid = (track) => typeof CSS === 'undefined' || CSS.supports('grid-template-columns', track);
|
|
5
|
+
const tracks = [];
|
|
6
|
+
let names = '';
|
|
7
|
+
for (const one of words) {
|
|
8
|
+
if (tracks.length >= wanted) {
|
|
9
|
+
break;
|
|
10
|
+
}
|
|
11
|
+
if (one.startsWith('[')) {
|
|
12
|
+
names += `${one} `;
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
const repeat = /^repeat\(\s*(\d+)\s*,(.*)\)$/is.exec(one);
|
|
16
|
+
if (repeat) {
|
|
17
|
+
const inner = tracksOf(repeat[2] ?? '', wanted);
|
|
18
|
+
for (let time = 0; time < Number(repeat[1]) && tracks.length < wanted; time += 1) {
|
|
19
|
+
tracks.push(...inner.map((track, index) => (time === 0 && index === 0 ? names + track : track)));
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
else if (!/^repeat\(/i.test(one)) {
|
|
23
|
+
tracks.push(valid(names + one) ? names + one : '');
|
|
24
|
+
}
|
|
25
|
+
names = '';
|
|
26
|
+
}
|
|
27
|
+
return tracks.slice(0, wanted);
|
|
28
|
+
};
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { __decorate } from "tslib";
|
|
2
|
-
import { AriaForwardController, customElement, deepActiveElement, HelixActivateEvent, HelixElement, IndicatorController, isRtl, KeyboardNavigationController, MIRROR_RULE, MixinControllerActive,
|
|
2
|
+
import { AriaForwardController, customElement, deepActiveElement, glyphPath, HelixActivateEvent, HelixElement, IndicatorController, isRtl, KeyboardNavigationController, MIRROR_RULE, MixinControllerActive, scrollWithin, whenHydrated, words, } from '@fusion.dev/helix-core';
|
|
3
3
|
import { caretDown, caretLeft, caretRight, caretUp } from '@fusion.dev/helix-icons';
|
|
4
4
|
import { html, nothing } from 'lit';
|
|
5
5
|
import { property, state } from 'lit/decorators.js';
|
|
6
|
-
import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
|
|
7
6
|
import { HelixHeadlessTabPanel } from '../panel/tab-panel.js';
|
|
8
7
|
import { HelixHeadlessTab } from '../tab/tab.js';
|
|
9
8
|
let HelixHeadlessTabs = class HelixHeadlessTabs extends MixinControllerActive(HelixElement) {
|
|
@@ -192,7 +191,7 @@ let HelixHeadlessTabs = class HelixHeadlessTabs extends MixinControllerActive(He
|
|
|
192
191
|
const strip = this.partNamed('tabs');
|
|
193
192
|
const tab = this.tabs.find((one) => one.selected);
|
|
194
193
|
if (strip && tab) {
|
|
195
|
-
|
|
194
|
+
scrollWithin(tab, strip, { padding: this.scrollControls ? 40 : 0 });
|
|
196
195
|
}
|
|
197
196
|
}
|
|
198
197
|
updated(changed) {
|
|
@@ -223,8 +222,8 @@ let HelixHeadlessTabs = class HelixHeadlessTabs extends MixinControllerActive(He
|
|
|
223
222
|
const icon = down ? (toward > 0 ? caretDown : caretUp) : toward > 0 ? caretRight : caretLeft;
|
|
224
223
|
const mirror = down ? nothing : '';
|
|
225
224
|
return toward > 0
|
|
226
|
-
? html `<slot name="scroll-end-icon"><svg part="scroll-end-mark" data-mirror=${mirror} aria-hidden="true" fill="currentColor" viewBox=${icon.viewBox}
|
|
227
|
-
: html `<slot name="scroll-start-icon"><svg part="scroll-start-mark" data-mirror=${mirror} aria-hidden="true" fill="currentColor" viewBox=${icon.viewBox}
|
|
225
|
+
? html `<slot name="scroll-end-icon"><svg part="scroll-end-mark" data-mirror=${mirror} aria-hidden="true" fill="currentColor" viewBox=${icon.viewBox}><path d=${glyphPath(icon)}></path></svg></slot>`
|
|
226
|
+
: html `<slot name="scroll-start-icon"><svg part="scroll-start-mark" data-mirror=${mirror} aria-hidden="true" fill="currentColor" viewBox=${icon.viewBox}><path d=${glyphPath(icon)}></path></svg></slot>`;
|
|
228
227
|
}
|
|
229
228
|
render() {
|
|
230
229
|
return html `<div
|
package/dist/tabs/tab/tab.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { __decorate } from "tslib";
|
|
2
|
-
import { customElement, HelixElement, MixinDismissible, MixinSelectableItem, plainClick, reflectElements, words, } from '@fusion.dev/helix-core';
|
|
2
|
+
import { customElement, glyphPath, HelixElement, MixinDismissible, MixinSelectableItem, plainClick, reflectElements, words, } from '@fusion.dev/helix-core';
|
|
3
3
|
import { x } from '@fusion.dev/helix-icons';
|
|
4
4
|
import { html, nothing } from 'lit';
|
|
5
5
|
import { property } from 'lit/decorators.js';
|
|
6
|
-
import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
|
|
7
6
|
let HelixHeadlessTab = class HelixHeadlessTab extends MixinDismissible(MixinSelectableItem(HelixElement)) {
|
|
8
7
|
constructor() {
|
|
9
8
|
super(...arguments);
|
|
@@ -49,7 +48,7 @@ let HelixHeadlessTab = class HelixHeadlessTab extends MixinDismissible(MixinSele
|
|
|
49
48
|
}
|
|
50
49
|
return html `<span part="dismiss" aria-hidden="true" title=${this.dismissLabel} @click=${this.handleDismissPress}
|
|
51
50
|
><slot name="dismiss-icon"
|
|
52
|
-
><svg part="dismiss-mark" fill="currentColor" viewBox=${x.viewBox} aria-hidden="true"
|
|
51
|
+
><svg part="dismiss-mark" fill="currentColor" viewBox=${x.viewBox} aria-hidden="true"><path d=${glyphPath(x)}></path></svg></slot
|
|
53
52
|
></span
|
|
54
53
|
>`;
|
|
55
54
|
}
|
|
@@ -375,7 +375,7 @@ let HelixHeadlessTimePicker = class HelixHeadlessTimePicker extends MixinPickerF
|
|
|
375
375
|
}
|
|
376
376
|
firstUpdated(changed) {
|
|
377
377
|
super.firstUpdated(changed);
|
|
378
|
-
auditName(this, () => null, '
|
|
378
|
+
auditName(this, () => null, 'time-picker', () => this.listName !== '');
|
|
379
379
|
const list = this.partNamed('list');
|
|
380
380
|
if (list) {
|
|
381
381
|
auditName(list, () => null, '', () => true);
|
|
@@ -34,7 +34,7 @@ let HelixHeadlessToggle = class HelixHeadlessToggle extends MixinSelectableItem(
|
|
|
34
34
|
firstUpdated(changed) {
|
|
35
35
|
super.firstUpdated(changed);
|
|
36
36
|
this.finishHydration();
|
|
37
|
-
auditName(this, () => this.partNamed('item'), '
|
|
37
|
+
auditName(this, () => this.partNamed('item'), 'toggle');
|
|
38
38
|
}
|
|
39
39
|
click() {
|
|
40
40
|
if (!this.clicks.click()) {
|
|
@@ -233,7 +233,7 @@ let HelixHeadlessTour = class HelixHeadlessTour extends MixinControllerOpen(Heli
|
|
|
233
233
|
this.settle();
|
|
234
234
|
this.light();
|
|
235
235
|
if (changed.has('open') && this.open) {
|
|
236
|
-
auditName(this, () => null, '
|
|
236
|
+
auditName(this, () => null, 'tour', () => this.label !== '');
|
|
237
237
|
}
|
|
238
238
|
this.follow(changed);
|
|
239
239
|
this.wait();
|
|
@@ -468,7 +468,7 @@ let HelixHeadlessTree = class HelixHeadlessTree extends MixinCollection(MixinCon
|
|
|
468
468
|
firstUpdated(changed) {
|
|
469
469
|
super.firstUpdated(changed);
|
|
470
470
|
this.syncItems();
|
|
471
|
-
auditName(this, () => null, '
|
|
471
|
+
auditName(this, () => null, 'tree', () => this.hasAttribute('label') || this.label !== 'Tree');
|
|
472
472
|
}
|
|
473
473
|
updated(changed) {
|
|
474
474
|
super.updated(changed);
|
|
@@ -459,7 +459,7 @@ let HelixHeadlessVideo = class HelixHeadlessVideo extends HelixElement {
|
|
|
459
459
|
firstUpdated(changed) {
|
|
460
460
|
super.firstUpdated?.(changed);
|
|
461
461
|
this.finishHydration();
|
|
462
|
-
auditName(this, () => null, '
|
|
462
|
+
auditName(this, () => null, 'video', () => this.label !== '');
|
|
463
463
|
const video = this.video;
|
|
464
464
|
if (video) {
|
|
465
465
|
if (this.defaultMuted || this.silent) {
|