@fusion.dev/helix-headless 0.152.0 → 0.154.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 +1143 -62
- package/dist/area-chart/root/area-chart-root.d.ts +16 -1
- package/dist/area-chart/root/area-chart-root.js +3 -3
- package/dist/badge/root/badge-root.d.ts +15 -2
- package/dist/badge/root/badge-root.js +20 -3
- package/dist/bar-chart/root/bar-chart-root.d.ts +16 -1
- package/dist/bar-chart/root/bar-chart-root.js +3 -3
- package/dist/card/root/card-root.d.ts +31 -11
- package/dist/card/root/card-root.js +25 -11
- package/dist/code/root/code-root.d.ts +7 -0
- package/dist/code/root/code-root.js +1 -1
- package/dist/code-block/root/code-block-root.d.ts +37 -1
- package/dist/code-block/root/code-block-root.js +38 -5
- package/dist/empty/root/empty-root.d.ts +19 -1
- package/dist/empty/root/empty-root.js +13 -4
- package/dist/heatmap/root/heatmap-root.d.ts +3 -0
- package/dist/heatmap/root/heatmap-root.js +2 -8
- package/dist/icon/fetch.d.ts +20 -0
- package/dist/icon/fetch.js +70 -0
- package/dist/icon/root/icon-root.d.ts +27 -5
- package/dist/icon/root/icon-root.js +41 -17
- package/dist/line-chart/root/line-chart-root.d.ts +16 -1
- package/dist/line-chart/root/line-chart-root.js +3 -3
- package/dist/loader/root/loader-root.d.ts +5 -0
- package/dist/progress/root/progress-root.d.ts +16 -1
- package/dist/progress/root/progress-root.js +12 -4
- package/dist/quote/root/quote-root.d.ts +17 -1
- package/dist/quote/root/quote-root.js +15 -2
- package/dist/scatter-chart/root/scatter-chart-root.d.ts +16 -1
- package/dist/scatter-chart/root/scatter-chart-root.js +3 -3
- package/dist/skeleton/root/skeleton-root.d.ts +7 -0
- package/dist/skeleton/root/skeleton-root.js +4 -1
- package/dist/stat/group/stat-group.d.ts +23 -4
- package/dist/stat/group/stat-group.js +13 -1
- package/dist/stat/root/stat-root.d.ts +46 -1
- package/dist/stat/root/stat-root.js +29 -5
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +1434 -62
- package/types/vue.d.ts +297 -24
- package/types/words.d.ts +69 -0
|
@@ -17,7 +17,7 @@ export interface AreaStack {
|
|
|
17
17
|
/** Each column's own reading, not yet summed. */
|
|
18
18
|
values: number[][];
|
|
19
19
|
}
|
|
20
|
-
declare const HelixHeadlessAreaChart_base: typeof HelixElement & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinChartSourceInterface);
|
|
20
|
+
declare const HelixHeadlessAreaChart_base: typeof HelixElement & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinChartSourceInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinChartStatusInterface);
|
|
21
21
|
/**
|
|
22
22
|
* @summary A chart that shows how a total is made up over time, as bands stacked on one another.
|
|
23
23
|
*
|
|
@@ -73,24 +73,39 @@ declare const HelixHeadlessAreaChart_base: typeof HelixElement & (new (...args:
|
|
|
73
73
|
* tap or key, and `helix-reading-press` says a reading was clicked, tapped, or
|
|
74
74
|
* pressed with Enter or Space, for a page that opens what a reading stands for.
|
|
75
75
|
*
|
|
76
|
+
* **`data` takes the readings as data** where the page has no table to point
|
|
77
|
+
* `for` at, in a table's shape: the column headings, then one row per reading
|
|
78
|
+
* with its label first. A table is still the better source where the page can
|
|
79
|
+
* show one, since it reads before any script runs.
|
|
80
|
+
*
|
|
81
|
+
* **A chart with nothing to draw says so.** A table with no readings shows
|
|
82
|
+
* `empty-text`, "No readings", in the plot, and `loading` shows `loading-text`
|
|
83
|
+
* there until readings arrive. The `empty` and `loading` slots take markup in
|
|
84
|
+
* their place, such as a link to add the first reading.
|
|
85
|
+
*
|
|
76
86
|
* @since 0.1.0
|
|
77
87
|
* @status stable
|
|
78
88
|
* @category charts
|
|
79
89
|
* @aliases Stacked Area Chart, Composition Chart, Banded Chart
|
|
80
90
|
* @documentation https://fusion.dev/helix/components/area-chart
|
|
81
91
|
*
|
|
92
|
+
* @slot empty - Content shown in place of the drawing when the table has no readings, such as a link to add the first. Falls back to `empty-text`.
|
|
93
|
+
* @slot loading - Content shown in place of the drawing while `loading` is set and there are no readings yet. Falls back to `loading-text`.
|
|
94
|
+
*
|
|
82
95
|
* @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.
|
|
83
96
|
* @event {HelixReadingPressEvent} helix-reading-press - A reading was clicked, tapped, or pressed with Enter or Space, with the same detail as `helix-reading-change`.
|
|
84
97
|
*
|
|
85
98
|
* @csspart area - One part's band.
|
|
86
99
|
* @csspart axis - The numbers of the scale beside the plot, under `value-axis`.
|
|
87
100
|
* @csspart baseline - The rule at zero, which is the floor of the drawing.
|
|
101
|
+
* @csspart empty - The words in place of the drawing 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.
|
|
88
102
|
* @csspart gridline - One rule across the plot, under `gridlines`.
|
|
89
103
|
* @csspart guide - A rule down the reading a pointer is resting on.
|
|
90
104
|
* @csspart label - One reading's name.
|
|
91
105
|
* @csspart labels - The row of reading names.
|
|
92
106
|
* @csspart line - The top edge of one part's band.
|
|
93
107
|
* @csspart live - The readout: a polite live region that says the reading the keys moved to. Hide it visually.
|
|
108
|
+
* @csspart loading - The words in place of the drawing while `loading` is set and there are no readings: the `loading` slot, falling back to `loading-text`.
|
|
94
109
|
* @csspart mark - The invisible point the tooltip is anchored to.
|
|
95
110
|
* @csspart plot - The drawing, which takes focus to be read by key.
|
|
96
111
|
* @csspart summary - The total and each part's share at the latest reading, written as a sentence for screen readers.
|
|
@@ -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 { CHART_PLOT, customElement, drawTooltip, drawValueAxis, formatNumbers, formatWords, HelixElement, HelixReadingChangeEvent, HelixReadingPressEvent, linear, linePath, MixinChartSource, PLOT_HEIGHT, PLOT_WIDTH, reportRuntime, seriesIndex, ticks, words, wordsLocale, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { CHART_PLOT, customElement, drawTooltip, drawValueAxis, formatNumbers, formatWords, HelixElement, HelixReadingChangeEvent, HelixReadingPressEvent, linear, linePath, MixinChartSource, MixinChartStatus, PLOT_HEIGHT, PLOT_WIDTH, reportRuntime, seriesIndex, ticks, words, wordsLocale, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html, nothing, svg } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
10
|
import '../../tooltip/index.js';
|
|
@@ -20,7 +20,7 @@ const percentIn = (locale) => {
|
|
|
20
20
|
}
|
|
21
21
|
return found;
|
|
22
22
|
};
|
|
23
|
-
let HelixHeadlessAreaChart = class HelixHeadlessAreaChart extends MixinChartSource(HelixElement) {
|
|
23
|
+
let HelixHeadlessAreaChart = class HelixHeadlessAreaChart extends MixinChartStatus(MixinChartSource(HelixElement)) {
|
|
24
24
|
constructor() {
|
|
25
25
|
super(...arguments);
|
|
26
26
|
this.overlap = false;
|
|
@@ -96,7 +96,7 @@ let HelixHeadlessAreaChart = class HelixHeadlessAreaChart extends MixinChartSour
|
|
|
96
96
|
const rows = this.sourceRows;
|
|
97
97
|
const stack = this.stack;
|
|
98
98
|
if (rows.length === 0 || stack.columns.length === 0) {
|
|
99
|
-
return html `<div id=${CHART_PLOT} part="plot"
|
|
99
|
+
return html `<div id=${CHART_PLOT} part="plot">${this.renderStatus()}</div>`;
|
|
100
100
|
}
|
|
101
101
|
const top = this.overlap
|
|
102
102
|
? Math.max(0, ...stack.layers.flat())
|
|
@@ -35,6 +35,12 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
35
35
|
* button "Inbox 8 unread", and stays current as the count changes. The styled
|
|
36
36
|
* tier's `placement="inline"` draws the mark in the line for that case.
|
|
37
37
|
*
|
|
38
|
+
* **An icon goes inside the mark** through the `icon` slot, before the count,
|
|
39
|
+
* as Web Awesome's badge takes one at its start. With no `value` the icon is
|
|
40
|
+
* the whole mark, such as a check on an avatar, drawn where the dot would be.
|
|
41
|
+
* The icon is hidden from screen readers: the count and `label` are what the
|
|
42
|
+
* mark says.
|
|
43
|
+
*
|
|
38
44
|
* @since 0.1.0
|
|
39
45
|
* @status stable
|
|
40
46
|
* @category markers
|
|
@@ -42,12 +48,15 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
42
48
|
* @documentation https://fusion.dev/helix/components/badge
|
|
43
49
|
*
|
|
44
50
|
* @slot - The element being decorated.
|
|
51
|
+
* @slot icon - An icon inside the mark, before the count, or the whole mark when there is no value. Hidden from screen readers.
|
|
45
52
|
*
|
|
46
|
-
* @csspart
|
|
53
|
+
* @csspart icon - The slot the icon arrives through, inside the indicator. A slot draws no box, so it passes inherited properties, such as color, to the icon.
|
|
54
|
+
* @csspart indicator - The count, or the dot when there is no value. Carries `data-dot` when there is no value, `data-single` when the count is one character, and `data-blank` when the value is zero.
|
|
47
55
|
*
|
|
48
56
|
* @cssstate blank - The value is zero, so there is no mark.
|
|
49
57
|
* @cssstate capped - The count is past `max`, so the mark shows `{max}+`.
|
|
50
|
-
* @cssstate dot - There is no value, so the mark is a dot.
|
|
58
|
+
* @cssstate dot - There is no value and no icon, so the mark is a dot.
|
|
59
|
+
* @cssstate has-icon - Something is slotted into `icon`, so it is drawn inside the mark.
|
|
51
60
|
*/
|
|
52
61
|
export declare class HelixHeadlessBadge extends HelixElement {
|
|
53
62
|
/**
|
|
@@ -98,6 +107,10 @@ export declare class HelixHeadlessBadge extends HelixElement {
|
|
|
98
107
|
* is showing a count must not lose the count for being handed one.
|
|
99
108
|
*/
|
|
100
109
|
value: number | string;
|
|
110
|
+
/** Whether something is slotted into `icon`, read from the children. */
|
|
111
|
+
private hasIcon;
|
|
112
|
+
/** Reads whether an icon is slotted, from the children where they can be known. */
|
|
113
|
+
private readonly readIcon;
|
|
101
114
|
/** The value as text, whatever it was set to. */
|
|
102
115
|
private get given();
|
|
103
116
|
/** Whether the value counts nothing, in which case there is no mark. */
|
|
@@ -6,7 +6,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
6
6
|
};
|
|
7
7
|
import { customElement, focusTarget, HelixElement, wordsLocale } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html, nothing } from 'lit';
|
|
9
|
-
import { property } from 'lit/decorators.js';
|
|
9
|
+
import { property, state } from 'lit/decorators.js';
|
|
10
10
|
import { badgeBlank, badgeCapped, badgeText, badgeWords } from '../count.js';
|
|
11
11
|
let HelixHeadlessBadge = class HelixHeadlessBadge extends HelixElement {
|
|
12
12
|
constructor() {
|
|
@@ -14,6 +14,10 @@ let HelixHeadlessBadge = class HelixHeadlessBadge extends HelixElement {
|
|
|
14
14
|
this.label = '';
|
|
15
15
|
this.locale = '';
|
|
16
16
|
this.value = '';
|
|
17
|
+
this.hasIcon = false;
|
|
18
|
+
this.readIcon = () => {
|
|
19
|
+
this.hasIcon = this.knownChildren.some((child) => child.slot === 'icon');
|
|
20
|
+
};
|
|
17
21
|
this.handleSlotchange = () => {
|
|
18
22
|
this.describe();
|
|
19
23
|
};
|
|
@@ -81,9 +85,13 @@ let HelixHeadlessBadge = class HelixHeadlessBadge extends HelixElement {
|
|
|
81
85
|
}
|
|
82
86
|
willUpdate(changed) {
|
|
83
87
|
super.willUpdate(changed);
|
|
88
|
+
if (!this.hasUpdated) {
|
|
89
|
+
this.readIcon();
|
|
90
|
+
}
|
|
84
91
|
this.toggleState('capped', this.capped);
|
|
85
|
-
this.toggleState('dot', this.given.trim() === '');
|
|
92
|
+
this.toggleState('dot', this.given.trim() === '' && !this.hasIcon);
|
|
86
93
|
this.toggleState('blank', this.blank);
|
|
94
|
+
this.toggleState('has-icon', this.hasIcon);
|
|
87
95
|
}
|
|
88
96
|
updated(changed) {
|
|
89
97
|
super.updated(changed);
|
|
@@ -97,10 +105,16 @@ let HelixHeadlessBadge = class HelixHeadlessBadge extends HelixElement {
|
|
|
97
105
|
<span
|
|
98
106
|
part="indicator"
|
|
99
107
|
data-blank=${blank ? '' : nothing}
|
|
108
|
+
data-dot=${this.given.trim() === '' ? '' : nothing}
|
|
100
109
|
data-single=${text.length <= 1 ? '' : nothing}
|
|
101
110
|
role="status"
|
|
102
111
|
aria-label=${announced ? this.words : nothing}
|
|
103
|
-
|
|
112
|
+
><slot
|
|
113
|
+
aria-hidden="true"
|
|
114
|
+
name="icon"
|
|
115
|
+
part="icon"
|
|
116
|
+
@slotchange=${this.readIcon}
|
|
117
|
+
></slot>${blank ? html `<span aria-hidden="true">${text}</span>` : text}</span
|
|
104
118
|
>`;
|
|
105
119
|
}
|
|
106
120
|
};
|
|
@@ -119,6 +133,9 @@ __decorate([
|
|
|
119
133
|
__decorate([
|
|
120
134
|
property()
|
|
121
135
|
], HelixHeadlessBadge.prototype, "value", void 0);
|
|
136
|
+
__decorate([
|
|
137
|
+
state()
|
|
138
|
+
], HelixHeadlessBadge.prototype, "hasIcon", void 0);
|
|
122
139
|
HelixHeadlessBadge = __decorate([
|
|
123
140
|
customElement('helix-headless-badge')
|
|
124
141
|
], HelixHeadlessBadge);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { HelixElement, type Series } from '@fusion.dev/helix-core';
|
|
2
2
|
import { nothing, type PropertyValues, type TemplateResult } from 'lit';
|
|
3
3
|
import '../../tooltip/index.js';
|
|
4
|
-
declare const HelixHeadlessBarChart_base: typeof HelixElement & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinChartSourceInterface);
|
|
4
|
+
declare const HelixHeadlessBarChart_base: typeof HelixElement & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinChartSourceInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinChartStatusInterface);
|
|
5
5
|
/**
|
|
6
6
|
* @summary A chart that draws a table's categories as columns.
|
|
7
7
|
*
|
|
@@ -35,12 +35,25 @@ declare const HelixHeadlessBarChart_base: typeof HelixElement & (new (...args: a
|
|
|
35
35
|
* tap or key, and `helix-reading-press` says a reading was clicked, tapped, or
|
|
36
36
|
* pressed with Enter or Space, for a page that opens what a reading stands for.
|
|
37
37
|
*
|
|
38
|
+
* **`data` takes the readings as data** where the page has no table to point
|
|
39
|
+
* `for` at, in a table's shape: the column headings, then one row per reading
|
|
40
|
+
* with its label first. A table is still the better source where the page can
|
|
41
|
+
* show one, since it reads before any script runs.
|
|
42
|
+
*
|
|
43
|
+
* **A chart with nothing to draw says so.** A table with no readings shows
|
|
44
|
+
* `empty-text`, "No readings", in the plot, and `loading` shows `loading-text`
|
|
45
|
+
* there until readings arrive. The `empty` and `loading` slots take markup in
|
|
46
|
+
* their place, such as a link to add the first reading.
|
|
47
|
+
*
|
|
38
48
|
* @since 0.1.0
|
|
39
49
|
* @status stable
|
|
40
50
|
* @category charts
|
|
41
51
|
* @aliases Column Chart, Categorical Chart
|
|
42
52
|
* @documentation https://fusion.dev/helix/components/bar-chart
|
|
43
53
|
*
|
|
54
|
+
* @slot empty - Content shown in place of the drawing when the table has no readings, such as a link to add the first. Falls back to `empty-text`.
|
|
55
|
+
* @slot loading - Content shown in place of the drawing while `loading` is set and there are no readings yet. Falls back to `loading-text`.
|
|
56
|
+
*
|
|
44
57
|
* @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.
|
|
45
58
|
* @event {HelixReadingPressEvent} helix-reading-press - A reading was clicked, tapped, or pressed with Enter or Space, with the same detail as `helix-reading-change`.
|
|
46
59
|
*
|
|
@@ -50,6 +63,7 @@ declare const HelixHeadlessBarChart_base: typeof HelixElement & (new (...args: a
|
|
|
50
63
|
* @csspart bar-wash-end - The far stop of a bar's gradient, where one is used.
|
|
51
64
|
* @csspart bar-wash-start - The stop of a bar's gradient at its base.
|
|
52
65
|
* @csspart baseline - The line the columns stand on.
|
|
66
|
+
* @csspart empty - The words in place of the drawing 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.
|
|
53
67
|
* @csspart gridline - One gridline across the plot, drawn when `gridlines` is set.
|
|
54
68
|
* @csspart group - One category's columns, together.
|
|
55
69
|
* @csspart group-active - The group the tooltip describes. Set together with `group`.
|
|
@@ -57,6 +71,7 @@ declare const HelixHeadlessBarChart_base: typeof HelixElement & (new (...args: a
|
|
|
57
71
|
* @csspart labels - The row of category names.
|
|
58
72
|
* @csspart line - A series drawn as a line, named in `lines`.
|
|
59
73
|
* @csspart live - The readout: a polite live region that says the reading the keys moved to. Hide it visually.
|
|
74
|
+
* @csspart loading - The words in place of the drawing while `loading` is set and there are no readings: the `loading` slot, falling back to `loading-text`.
|
|
60
75
|
* @csspart mark - The invisible point the tooltip is anchored to.
|
|
61
76
|
* @csspart plot - The drawing, which takes focus to be read by key.
|
|
62
77
|
* @csspart summary - The chart's name and each series' highest reading, written as a sentence for screen readers. Hide it visually.
|
|
@@ -4,12 +4,12 @@ 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 { byRow, CHART_PLOT, customElement, drawTooltip, extent, formatNumbers, formatWords, HelixElement, HelixReadingChangeEvent, HelixReadingPressEvent, linear, linePath, MixinChartSource, PLOT_HEIGHT, PLOT_WIDTH, seriesIndex, seriesKey, seriesNames, ticks, unbroken, words, wordsLocale, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { byRow, CHART_PLOT, customElement, drawTooltip, extent, formatNumbers, formatWords, HelixElement, HelixReadingChangeEvent, HelixReadingPressEvent, linear, linePath, MixinChartSource, MixinChartStatus, PLOT_HEIGHT, PLOT_WIDTH, seriesIndex, seriesKey, seriesNames, ticks, unbroken, words, wordsLocale, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html, nothing, svg } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
10
|
import { stackEnds, stackedReadings, stackSpans } from '../stack.js';
|
|
11
11
|
import '../../tooltip/index.js';
|
|
12
|
-
let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource(HelixElement) {
|
|
12
|
+
let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartStatus(MixinChartSource(HelixElement)) {
|
|
13
13
|
constructor() {
|
|
14
14
|
super(...arguments);
|
|
15
15
|
this.highestLabel = 'Highest';
|
|
@@ -44,7 +44,7 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource
|
|
|
44
44
|
const series = this.series;
|
|
45
45
|
const categories = series.length === 0 ? [] : this.sourceRows.map((row) => row.label);
|
|
46
46
|
if (categories.length === 0) {
|
|
47
|
-
return html `<div id=${CHART_PLOT} part="plot"
|
|
47
|
+
return html `<div id=${CHART_PLOT} part="plot">${this.renderStatus()}</div>`;
|
|
48
48
|
}
|
|
49
49
|
const scaled = this.scaled;
|
|
50
50
|
const own = (one) => scaled.has(seriesKey(one.name));
|
|
@@ -13,13 +13,17 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
13
13
|
* through with `::part`.
|
|
14
14
|
*
|
|
15
15
|
* Name the card with `label`, as a Stat Group is named, or with `aria-label`
|
|
16
|
-
* or `aria-labelledby`.
|
|
17
|
-
*
|
|
18
|
-
* server has none. `label` is set through `ElementInternals`, so it writes no
|
|
19
|
-
* attribute onto the card. **A named card is a group**, so a
|
|
16
|
+
* or `aria-labelledby`. `label` is set through `ElementInternals`, so it
|
|
17
|
+
* writes no attribute onto the card. **A named card is a group**, so a
|
|
20
18
|
* reader entering it hears its name; an unnamed one has no role, since a group
|
|
21
19
|
* with no name only adds "group" to what a reader says.
|
|
22
20
|
*
|
|
21
|
+
* **With none of those, a heading in `header` names the card**, such as the
|
|
22
|
+
* `<h2>` of its title, by reference, so the name follows the heading's words.
|
|
23
|
+
* Reading a heading needs a DOM, so a served card is named by it once the
|
|
24
|
+
* script runs; `label`, `aria-label` and `aria-labelledby` are the names a
|
|
25
|
+
* server writes.
|
|
26
|
+
*
|
|
23
27
|
* Each region's slot is a part of the same name, `body` for the main content,
|
|
24
28
|
* as Web Awesome's card names its regions. A slot draws no box, so a part
|
|
25
29
|
* passes inherited properties to what is slotted, and a page wanting a box
|
|
@@ -29,7 +33,9 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
29
33
|
* header's and the footer's line, as Web Awesome's card does. While one holds
|
|
30
34
|
* something, its line is a `header-row` or `footer-row` part; otherwise there
|
|
31
35
|
* is no wrapper and every region is a bare slot. A server cannot see slotted
|
|
32
|
-
* content, so served
|
|
36
|
+
* content, so a served card has no row; the styled tier sets served actions at
|
|
37
|
+
* the end of their line from the stylesheet alone, so they do not move when
|
|
38
|
+
* the script runs.
|
|
33
39
|
*
|
|
34
40
|
* **The card says which regions it holds**, as `:state(has-media)`,
|
|
35
41
|
* `:state(has-header)` and `:state(has-footer)`, so a sheet can draw a card
|
|
@@ -87,20 +93,34 @@ export declare class HelixHeadlessCard extends HelixElement {
|
|
|
87
93
|
*/
|
|
88
94
|
mediaPlacement?: 'end';
|
|
89
95
|
/**
|
|
90
|
-
*
|
|
91
|
-
*
|
|
96
|
+
* Hears a name written on the card, which no property mirrors.
|
|
97
|
+
*
|
|
98
|
+
* Lit observes the attributes of its own properties only, and `ariaLabel`
|
|
99
|
+
* and `ariaLabelledByElements` belong to the platform. Observed here rather
|
|
100
|
+
* than through a property, so the manifest lists no attribute of the card's
|
|
101
|
+
* own for them.
|
|
92
102
|
*/
|
|
93
|
-
|
|
103
|
+
static get observedAttributes(): string[];
|
|
94
104
|
/**
|
|
95
|
-
*
|
|
96
|
-
*
|
|
105
|
+
* Called by the browser when an observed attribute changes. A name written
|
|
106
|
+
* or taken off asks for an update, because it decides whether the card is a
|
|
107
|
+
* group and whether its heading names it.
|
|
97
108
|
*/
|
|
98
|
-
|
|
109
|
+
attributeChangedCallback(name: string, old: string | null, value: string | null): void;
|
|
99
110
|
/** Whether `footer-actions` holds something, read from the slot. */
|
|
100
111
|
private footerActions;
|
|
101
112
|
/** Whether `header-actions` holds something, read from the slot. */
|
|
102
113
|
private headerActions;
|
|
103
114
|
protected willUpdate(changed: PropertyValues): void;
|
|
115
|
+
/**
|
|
116
|
+
* Says what the card is and what names it. On the host, since the host is
|
|
117
|
+
* the box, and from `willUpdate`, which the server calls too.
|
|
118
|
+
*
|
|
119
|
+
* **A heading in `header` is the name only where nothing else is**, and it
|
|
120
|
+
* names the card by reference, so a renamed heading renames the card. An
|
|
121
|
+
* engine without element references hears its words.
|
|
122
|
+
*/
|
|
123
|
+
private writeName;
|
|
104
124
|
protected updated(changed: PropertyValues): void;
|
|
105
125
|
protected firstUpdated(changed: PropertyValues): void;
|
|
106
126
|
private readonly readRegions;
|
|
@@ -4,15 +4,13 @@ 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, HelixElement, hostAria, slotFilled } from '@fusion.dev/helix-core';
|
|
7
|
+
import { customElement, HelixElement, headingIn, hostAria, slotFilled, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html } from 'lit';
|
|
9
9
|
import { property, state } from 'lit/decorators.js';
|
|
10
10
|
let HelixHeadlessCard = class HelixHeadlessCard extends HelixElement {
|
|
11
11
|
constructor() {
|
|
12
12
|
super(...arguments);
|
|
13
13
|
this.label = '';
|
|
14
|
-
this.labelledBy = null;
|
|
15
|
-
this.namedBy = null;
|
|
16
14
|
this.footerActions = false;
|
|
17
15
|
this.headerActions = false;
|
|
18
16
|
this.readRegions = () => {
|
|
@@ -22,12 +20,34 @@ let HelixHeadlessCard = class HelixHeadlessCard extends HelixElement {
|
|
|
22
20
|
this.toggleState('has-media', filled('media'));
|
|
23
21
|
this.toggleState('has-header', this.headerActions || filled('header'));
|
|
24
22
|
this.toggleState('has-footer', this.footerActions || filled('footer'));
|
|
23
|
+
this.writeName();
|
|
25
24
|
};
|
|
26
25
|
}
|
|
26
|
+
static get observedAttributes() {
|
|
27
|
+
return [...super.observedAttributes, 'aria-label', 'aria-labelledby'];
|
|
28
|
+
}
|
|
29
|
+
attributeChangedCallback(name, old, value) {
|
|
30
|
+
super.attributeChangedCallback(name, old, value);
|
|
31
|
+
if (name === 'aria-label' || name === 'aria-labelledby') {
|
|
32
|
+
this.requestUpdate();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
27
35
|
willUpdate(changed) {
|
|
28
36
|
super.willUpdate(changed);
|
|
29
|
-
|
|
30
|
-
|
|
37
|
+
this.writeName();
|
|
38
|
+
}
|
|
39
|
+
writeName() {
|
|
40
|
+
const attribute = (name) => Boolean(this.getAttribute?.(name)?.trim());
|
|
41
|
+
const named = this.label.trim() !== '' || attribute('aria-label') || attribute('aria-labelledby');
|
|
42
|
+
const heading = named
|
|
43
|
+
? null
|
|
44
|
+
: headingIn(this.knownChildren.filter((child) => child.slot === 'header'));
|
|
45
|
+
const references = 'ariaLabelledByElements' in this.internals;
|
|
46
|
+
hostAria(this.internals, {
|
|
47
|
+
label: heading && !references ? (heading.textContent ?? '').trim() : this.label,
|
|
48
|
+
labelledBy: heading ? [heading] : [],
|
|
49
|
+
role: named || heading ? 'group' : '',
|
|
50
|
+
});
|
|
31
51
|
}
|
|
32
52
|
updated(changed) {
|
|
33
53
|
super.updated(changed);
|
|
@@ -64,12 +84,6 @@ __decorate([
|
|
|
64
84
|
__decorate([
|
|
65
85
|
property({ attribute: 'media-placement', reflect: true })
|
|
66
86
|
], HelixHeadlessCard.prototype, "mediaPlacement", void 0);
|
|
67
|
-
__decorate([
|
|
68
|
-
property({ attribute: 'aria-labelledby' })
|
|
69
|
-
], HelixHeadlessCard.prototype, "labelledBy", void 0);
|
|
70
|
-
__decorate([
|
|
71
|
-
property({ attribute: 'aria-label' })
|
|
72
|
-
], HelixHeadlessCard.prototype, "namedBy", void 0);
|
|
73
87
|
__decorate([
|
|
74
88
|
state()
|
|
75
89
|
], HelixHeadlessCard.prototype, "footerActions", void 0);
|
|
@@ -8,6 +8,13 @@ import { type TemplateResult } from 'lit';
|
|
|
8
8
|
* tools and document search. A styled span carries no such meaning, and a
|
|
9
9
|
* screen reader announces nothing about it.
|
|
10
10
|
*
|
|
11
|
+
* **Set `translate="no"` on the element to keep a page translator off the
|
|
12
|
+
* code.** A translator that rewrites prose would also rewrite an identifier.
|
|
13
|
+
* The slotted text takes its translation mode from the element, its parent in
|
|
14
|
+
* the page, and the element writes nothing onto itself, so the attribute is
|
|
15
|
+
* yours to set. The `<code>` inside carries `translate="no"` for anything the
|
|
16
|
+
* element draws itself.
|
|
17
|
+
*
|
|
11
18
|
* `Code` is inline only and never scrolls. For a block of code with its own
|
|
12
19
|
* box, its own overflow, and copying, use `CodeBlock`, which is a separate
|
|
13
20
|
* family.
|
|
@@ -8,7 +8,7 @@ import { customElement, HelixElement } from '@fusion.dev/helix-core';
|
|
|
8
8
|
import { html } from 'lit';
|
|
9
9
|
let HelixHeadlessCode = class HelixHeadlessCode extends HelixElement {
|
|
10
10
|
render() {
|
|
11
|
-
return html `<code part="code"><slot></slot></code>`;
|
|
11
|
+
return html `<code part="code" translate="no"><slot></slot></code>`;
|
|
12
12
|
}
|
|
13
13
|
};
|
|
14
14
|
HelixHeadlessCode = __decorate([
|
|
@@ -38,6 +38,18 @@ import '../../copy-button/index.js';
|
|
|
38
38
|
* and its copy button copies the new version of the file. `variant="split"`
|
|
39
39
|
* shows the old version beside the new, each removal opposite its replacement.
|
|
40
40
|
*
|
|
41
|
+
* **A block that scrolls is a tab stop.** While its lines run past its width or
|
|
42
|
+
* its cap, the scroller is a region named for the block, such as "Code,
|
|
43
|
+
* app.ts", that Tab reaches, so a keyboard scrolls it with the arrow keys in
|
|
44
|
+
* every browser. Chromium and Firefox make a scrolling box focusable on their
|
|
45
|
+
* own, and Safari does not. A block that fits is no stop at all.
|
|
46
|
+
*
|
|
47
|
+
* **The code the block draws is marked untranslated**, `translate="no"` on
|
|
48
|
+
* its `<pre>`, so a page translator leaves highlighted lines and a diff as
|
|
49
|
+
* written. Code in the slot takes its translation mode from the element, which
|
|
50
|
+
* writes nothing onto itself, so set `translate="no"` on the element to keep a
|
|
51
|
+
* translator off plain code too.
|
|
52
|
+
*
|
|
41
53
|
* On the server, the slot renders as ordinary code, so a diff first appears as
|
|
42
54
|
* the patch text in a `<pre>`. The client makes the same first render, which
|
|
43
55
|
* keeps hydration clean, and draws the diff once it can read the content. Diff
|
|
@@ -95,6 +107,18 @@ export declare class HelixHeadlessCodeBlock extends HelixElement {
|
|
|
95
107
|
* `registerWords('de', { 'helix-code-block': { copyLabel: 'Code kopieren' } })`.
|
|
96
108
|
*/
|
|
97
109
|
static words: string;
|
|
110
|
+
/**
|
|
111
|
+
* The name of a block that scrolls, which is a tab stop while it does.
|
|
112
|
+
*
|
|
113
|
+
* Where the block has a name, the scroller says it too, such as "Code,
|
|
114
|
+
* app.ts", as `code-name-label` puts it.
|
|
115
|
+
*/
|
|
116
|
+
codeLabel: string;
|
|
117
|
+
/**
|
|
118
|
+
* The name of a block that scrolls, where the block has a name. `{code}` is
|
|
119
|
+
* `code-label` and `{name}` the block's name.
|
|
120
|
+
*/
|
|
121
|
+
codeNameLabel: string;
|
|
98
122
|
/**
|
|
99
123
|
* The expand button's words while the block shows every line.
|
|
100
124
|
*/
|
|
@@ -249,7 +273,7 @@ export declare class HelixHeadlessCodeBlock extends HelixElement {
|
|
|
249
273
|
private capped;
|
|
250
274
|
/** Whether the expand button has shown every line. */
|
|
251
275
|
private expanded;
|
|
252
|
-
/** Watches the block's size
|
|
276
|
+
/** Watches the block's size. */
|
|
253
277
|
private sizes;
|
|
254
278
|
/**
|
|
255
279
|
* Reads whether the cap hides lines: the scroller is taller inside than it
|
|
@@ -258,6 +282,18 @@ export declare class HelixHeadlessCodeBlock extends HelixElement {
|
|
|
258
282
|
* everything.
|
|
259
283
|
*/
|
|
260
284
|
private readonly measure;
|
|
285
|
+
/**
|
|
286
|
+
* Makes each box that scrolls a tab stop and a region named for its block,
|
|
287
|
+
* and takes both away from a box that fits.
|
|
288
|
+
*
|
|
289
|
+
* **A reader on a keyboard scrolls the code from the box itself.** Chromium
|
|
290
|
+
* and Firefox make a scrolling box focusable on their own, and Safari does
|
|
291
|
+
* not, so there the arrow keys could not reach the end of a long line. A
|
|
292
|
+
* box counts while its sheet lets it scroll and its content is wider or
|
|
293
|
+
* taller than it is drawn: an unstyled `<pre>` overflows without scrolling,
|
|
294
|
+
* and a stop on it would scroll nothing.
|
|
295
|
+
*/
|
|
296
|
+
private markScrollers;
|
|
261
297
|
protected updated(changed: PropertyValues): void;
|
|
262
298
|
connectedCallback(): void;
|
|
263
299
|
disconnectedCallback(): void;
|
|
@@ -16,6 +16,8 @@ import { pairLines, parsePatch } from '../patch.js';
|
|
|
16
16
|
let HelixHeadlessCodeBlock = class HelixHeadlessCodeBlock extends HelixElement {
|
|
17
17
|
constructor() {
|
|
18
18
|
super(...arguments);
|
|
19
|
+
this.codeLabel = 'Code';
|
|
20
|
+
this.codeNameLabel = '{code}, {name}';
|
|
19
21
|
this.collapseLabel = 'Show fewer lines';
|
|
20
22
|
this.copiedLabel = 'Copied';
|
|
21
23
|
this.copyLabel = 'Copy code';
|
|
@@ -31,6 +33,7 @@ let HelixHeadlessCodeBlock = class HelixHeadlessCodeBlock extends HelixElement {
|
|
|
31
33
|
this.capped = false;
|
|
32
34
|
this.expanded = false;
|
|
33
35
|
this.measure = () => {
|
|
36
|
+
this.markScrollers();
|
|
34
37
|
if (!this.expanded) {
|
|
35
38
|
this.capped =
|
|
36
39
|
this.expandable &&
|
|
@@ -60,9 +63,31 @@ let HelixHeadlessCodeBlock = class HelixHeadlessCodeBlock extends HelixElement {
|
|
|
60
63
|
.map((node) => node.textContent ?? '')
|
|
61
64
|
.join('');
|
|
62
65
|
}
|
|
66
|
+
markScrollers() {
|
|
67
|
+
for (const box of this.renderRoot.querySelectorAll('[part="block"], [part="code"], [part="old"], [part="new"]')) {
|
|
68
|
+
const style = getComputedStyle(box);
|
|
69
|
+
const scrolls = (value) => value === 'auto' || value === 'scroll';
|
|
70
|
+
const overflows = (scrolls(style.overflowX) && box.scrollWidth > box.clientWidth + 1) ||
|
|
71
|
+
(scrolls(style.overflowY) && box.scrollHeight > box.clientHeight + 1);
|
|
72
|
+
if (overflows) {
|
|
73
|
+
const name = box.closest('[part="file"]')?.querySelector('[part="label"]')?.textContent;
|
|
74
|
+
const named = name ?? (this.label || this.language);
|
|
75
|
+
box.tabIndex = 0;
|
|
76
|
+
box.setAttribute('role', 'region');
|
|
77
|
+
box.setAttribute('aria-label', named
|
|
78
|
+
? formatWords(this, 'codeNameLabel', { code: this.codeLabel, name: named })
|
|
79
|
+
: this.codeLabel);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
box.removeAttribute('tabindex');
|
|
83
|
+
box.removeAttribute('role');
|
|
84
|
+
box.removeAttribute('aria-label');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
63
88
|
updated(changed) {
|
|
64
89
|
super.updated(changed);
|
|
65
|
-
if (
|
|
90
|
+
if (typeof ResizeObserver !== 'undefined') {
|
|
66
91
|
this.sizes ??= new ResizeObserver(this.measure);
|
|
67
92
|
this.sizes.observe(this);
|
|
68
93
|
}
|
|
@@ -114,7 +139,7 @@ let HelixHeadlessCodeBlock = class HelixHeadlessCodeBlock extends HelixElement {
|
|
|
114
139
|
const name = this.label || this.language;
|
|
115
140
|
return html `${this.renderHeader(name)}
|
|
116
141
|
<div part="frame">
|
|
117
|
-
<pre id="block" part="block">${gutter}${code}</pre>
|
|
142
|
+
<pre id="block" part="block" translate="no">${gutter}${code}</pre>
|
|
118
143
|
${name ? nothing : this.renderCopy()}
|
|
119
144
|
</div>${this.renderExpand()}`;
|
|
120
145
|
}
|
|
@@ -179,8 +204,8 @@ let HelixHeadlessCodeBlock = class HelixHeadlessCodeBlock extends HelixElement {
|
|
|
179
204
|
const hunks = file.hunks.map((hunk) => ({ hunk, rows: pairLines(hunk.lines) }));
|
|
180
205
|
const filler = html `<span part="filler" aria-hidden="true">\n</span>`;
|
|
181
206
|
return html `<div part="split">
|
|
182
|
-
<pre part="old"><code part="code">${hunks.map(({ hunk, rows }) => html `${this.renderHunk(hunk)}${rows.map(({ left }) => left ? this.renderLine(left, width, 'old') : filler)}`)}</code></pre>
|
|
183
|
-
<pre part="new"><code part="code">${hunks.map(({ hunk, rows }) => html `${this.renderHunk(hunk, true)}${rows.map(({ right }) => right ? this.renderLine(right, width, 'new') : filler)}`)}</code></pre>
|
|
207
|
+
<pre part="old" translate="no"><code part="code">${hunks.map(({ hunk, rows }) => html `${this.renderHunk(hunk)}${rows.map(({ left }) => left ? this.renderLine(left, width, 'old') : filler)}`)}</code></pre>
|
|
208
|
+
<pre part="new" translate="no"><code part="code">${hunks.map(({ hunk, rows }) => html `${this.renderHunk(hunk, true)}${rows.map(({ right }) => right ? this.renderLine(right, width, 'new') : filler)}`)}</code></pre>
|
|
184
209
|
</div>`;
|
|
185
210
|
}
|
|
186
211
|
renderDiff(files) {
|
|
@@ -197,13 +222,21 @@ let HelixHeadlessCodeBlock = class HelixHeadlessCodeBlock extends HelixElement {
|
|
|
197
222
|
<div part="frame">
|
|
198
223
|
${this.variant === 'split'
|
|
199
224
|
? this.renderSplit(file, width)
|
|
200
|
-
: html `<pre part="block"><code part="code">${file.hunks.map((hunk) => html `${this.renderHunk(hunk)}${hunk.lines.map((line) => this.renderLine(line, width))}`)}</code></pre>`}
|
|
225
|
+
: html `<pre part="block" translate="no"><code part="code">${file.hunks.map((hunk) => html `${this.renderHunk(hunk)}${hunk.lines.map((line) => this.renderLine(line, width))}`)}</code></pre>`}
|
|
201
226
|
${name ? nothing : this.renderCopy(now)}
|
|
202
227
|
</div>
|
|
203
228
|
</div>`;
|
|
204
229
|
})}<slot hidden @slotchange=${() => this.requestUpdate()}></slot>`;
|
|
205
230
|
}
|
|
206
231
|
};
|
|
232
|
+
__decorate([
|
|
233
|
+
words,
|
|
234
|
+
property({ attribute: 'code-label' })
|
|
235
|
+
], HelixHeadlessCodeBlock.prototype, "codeLabel", void 0);
|
|
236
|
+
__decorate([
|
|
237
|
+
words,
|
|
238
|
+
property({ attribute: 'code-name-label' })
|
|
239
|
+
], HelixHeadlessCodeBlock.prototype, "codeNameLabel", void 0);
|
|
207
240
|
__decorate([
|
|
208
241
|
words,
|
|
209
242
|
property({ attribute: 'collapse-label' })
|
|
@@ -17,7 +17,16 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
17
17
|
*
|
|
18
18
|
* Every region is a bare slot. There is no `empty-state-heading` or
|
|
19
19
|
* `empty-state-actions` element: assign your own heading and actions to the
|
|
20
|
-
* slots.
|
|
20
|
+
* slots. The icon, the message and the actions slots are parts of the same
|
|
21
|
+
* names, as Card's regions are. A slot draws no box, so a part passes
|
|
22
|
+
* inherited properties, such as color and font, to what is slotted, and a
|
|
23
|
+
* page wanting a box there gives it one with `display`, such as a row of
|
|
24
|
+
* actions with `display: flex`.
|
|
25
|
+
*
|
|
26
|
+
* **The view says which regions it holds**, as `:state(has-icon)` and
|
|
27
|
+
* `:state(has-actions)`, so a sheet can space a view with no icon or no
|
|
28
|
+
* actions differently. Read in the browser, so a served view carries neither
|
|
29
|
+
* until the script runs.
|
|
21
30
|
*
|
|
22
31
|
* **Or give the heading as words.** `heading` draws them in a `heading` part
|
|
23
32
|
* that is a heading at `heading-level`, 3 where it is not set, so a page
|
|
@@ -42,8 +51,14 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
42
51
|
* @slot heading - The heading, which names what is missing. A heading element, such as an `<h3>`, keeps its level; any other element is read as a heading at `heading-level`. Replaces the one `heading` draws.
|
|
43
52
|
* @slot icon - An icon above the heading.
|
|
44
53
|
*
|
|
54
|
+
* @csspart actions - The slot the actions arrive through. A slot draws no box, so it passes inherited properties to the actions; `display: flex` lays them out in a row.
|
|
45
55
|
* @csspart heading - The heading at `heading-level`: the one `heading` draws while nothing is slotted into `heading`, or the one around a slotted element that is no heading.
|
|
56
|
+
* @csspart icon - The slot the icon arrives through, drawing no box, as `actions` does.
|
|
46
57
|
* @csspart live - A copy of the heading and the message, in a live region that screen readers announce when the view appears. Visually hidden in the styled tier.
|
|
58
|
+
* @csspart message - The slot the message arrives through, drawing no box, as `actions` does.
|
|
59
|
+
*
|
|
60
|
+
* @cssstate has-actions - Something is slotted into `actions`.
|
|
61
|
+
* @cssstate has-icon - Something is slotted into `icon`.
|
|
47
62
|
*/
|
|
48
63
|
export declare class HelixHeadlessEmpty extends HelixElement {
|
|
49
64
|
/**
|
|
@@ -75,6 +90,9 @@ export declare class HelixHeadlessEmpty extends HelixElement {
|
|
|
75
90
|
private looseHeading;
|
|
76
91
|
protected willUpdate(changed: PropertyValues): void;
|
|
77
92
|
protected firstUpdated(changed: PropertyValues): void;
|
|
93
|
+
protected updated(changed: PropertyValues): void;
|
|
94
|
+
/** Says which regions hold something, one call apiece so each pairs with its `@cssstate`. */
|
|
95
|
+
private readonly readRegions;
|
|
78
96
|
protected readonly arrival: ArrivalController;
|
|
79
97
|
/**
|
|
80
98
|
* The `heading` slot, with the heading `heading` draws as its fallback, so
|