@fusion.dev/helix-headless 0.2.0 → 0.3.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 +234 -234
- package/dist/accordion/item/accordion-item.js +0 -108
- package/dist/accordion/root/accordion-root.js +0 -33
- package/dist/announcement/root/announcement-root.js +1 -76
- package/dist/app-header/root/app-header-root.js +0 -84
- package/dist/avatar/group/avatar-group.js +0 -77
- package/dist/avatar/root/avatar-root.js +0 -72
- package/dist/badge/root/badge-root.js +0 -67
- package/dist/bar-chart/root/bar-chart-root.js +3 -263
- package/dist/blockquote/root/blockquote-root.js +0 -46
- package/dist/breadcrumbs/breadcrumb/breadcrumb.js +0 -72
- package/dist/breadcrumbs/root/breadcrumbs-root.js +0 -52
- package/dist/button/root/button-root.js +0 -215
- package/dist/button-group/root/button-group-root.js +0 -30
- package/dist/callout/root/callout-root.js +0 -52
- package/dist/card/root/card-root.js +0 -41
- package/dist/carousel/root/carousel-root.d.ts +2 -2
- package/dist/carousel/root/carousel-root.js +2 -362
- package/dist/chat/composer/chat-composer.js +0 -131
- package/dist/chat/message/chat-message.js +0 -137
- package/dist/chat/root/chat-root.js +0 -193
- package/dist/checkbox/root/checkbox-root.js +0 -113
- package/dist/checkbox-group/root/checkbox-group-root.js +1 -157
- package/dist/code/root/code-root.js +0 -26
- package/dist/code-block/root/code-block-root.js +0 -100
- package/dist/color-input/root/color-input-root.js +0 -217
- package/dist/color-picker/color.js +0 -69
- package/dist/color-picker/root/color-picker-root.js +0 -272
- package/dist/combobox/root/combobox-root.js +0 -428
- package/dist/command/root/command-root.js +0 -177
- package/dist/copy-button/root/copy-button-root.js +0 -134
- package/dist/data-table/root/data-table-root.js +0 -336
- package/dist/data-table/root/order.js +0 -100
- package/dist/date-input/root/date-input-root.js +1 -398
- package/dist/date-picker/root/date-picker-root.js +0 -358
- package/dist/dialog/root/dialog-root.js +0 -189
- package/dist/divider/root/divider-root.js +0 -71
- package/dist/donut-chart/root/donut-chart-root.js +0 -214
- package/dist/drawer/root/drawer-root.js +0 -116
- package/dist/drilldown/item/drilldown-item.js +0 -82
- package/dist/drilldown/root/drilldown-root.js +0 -91
- package/dist/element-events.generated.js +0 -5
- package/dist/empty/root/empty-root.js +0 -35
- package/dist/file-input/root/file-input-root.js +0 -232
- package/dist/format-date/root/format-date-root.js +0 -95
- package/dist/heatmap/root/buckets.js +0 -57
- package/dist/heatmap/root/heatmap-root.js +0 -313
- package/dist/icon/root/icon-root.js +0 -48
- package/dist/icon-button/root/icon-button-root.js +0 -30
- package/dist/image/root/image-root.js +0 -174
- package/dist/index.js +0 -9
- package/dist/input/root/input-root.js +0 -228
- package/dist/kbd/root/kbd-root.js +0 -117
- package/dist/legend/root/legend-root.js +0 -73
- package/dist/line-chart/root/line-chart-root.js +0 -172
- package/dist/list/item/list-item.js +0 -92
- package/dist/list/root/list-root.js +0 -26
- package/dist/listbox/root/listbox-root.js +2 -429
- package/dist/loader/root/loader-root.js +0 -40
- package/dist/megamenu/root/megamenu-root.js +0 -284
- package/dist/menu/item/menu-item.js +1 -221
- package/dist/menu/root/menu-root.js +1 -171
- package/dist/meter/root/meter-root.js +1 -152
- package/dist/navigation/root/navigation-root.js +0 -56
- package/dist/notifications/notification/notification.js +0 -159
- package/dist/notifications/root/notifications-root.js +0 -148
- package/dist/number-input/root/number-input-root.js +1 -263
- package/dist/option/group/option-group.js +0 -26
- package/dist/option/root/option-root.js +0 -157
- package/dist/outline/root/outline-root.js +0 -292
- package/dist/pagination/root/pagination-root.js +0 -361
- package/dist/pagination/size/pagination-size.js +0 -104
- package/dist/pagination/summary/pagination-summary.js +0 -90
- package/dist/pin-input/root/pin-input-root.js +0 -276
- package/dist/popover/root/popover-root.js +0 -103
- package/dist/progress/root/progress-root.js +1 -133
- package/dist/qr-code/root/qr-code-root.js +0 -96
- package/dist/quote/root/quote-root.js +0 -25
- package/dist/radio/group/radio-group.js +0 -81
- package/dist/radio/root/radio-root.js +0 -85
- package/dist/rating/root/rating-root.js +0 -220
- package/dist/scroll-area/root/scroll-area-root.js +0 -100
- package/dist/select/root/select-root.js +2 -218
- package/dist/skeleton/root/skeleton-root.js +0 -20
- package/dist/slider/root/slider-root.js +1 -267
- package/dist/sparkline/root/sparkline-root.js +3 -164
- package/dist/speed-dial/item/speed-dial-item.js +0 -52
- package/dist/speed-dial/root/speed-dial-root.js +1 -116
- package/dist/splitter/root/splitter-root.js +0 -489
- package/dist/stat/group/stat-group.js +0 -18
- package/dist/stat/root/stat-root.js +0 -23
- package/dist/steps/root/steps-root.js +0 -62
- package/dist/steps/step/step.js +0 -131
- package/dist/switch/root/switch-root.js +0 -80
- package/dist/table/cell/table-cell.js +0 -23
- package/dist/table/column/table-column.js +0 -158
- package/dist/table/root/table-root.js +0 -82
- package/dist/table/row/table-row.js +0 -21
- package/dist/tabs/panel/tab-panel.js +0 -49
- package/dist/tabs/root/tabs-root.js +0 -98
- package/dist/tabs/tab/tab.js +0 -59
- package/dist/tag/root/tag-root.js +0 -31
- package/dist/textarea/root/textarea-root.js +0 -162
- package/dist/time-input/root/time-input-root.js +0 -283
- package/dist/time-picker/root/time-picker-root.js +0 -326
- package/dist/timeline/item/timeline-item.js +0 -66
- package/dist/timeline/root/timeline-root.js +0 -41
- package/dist/toast/item/toast-item.js +0 -165
- package/dist/toast/root/toast-root.js +0 -51
- package/dist/toggle/group/toggle-group.js +0 -150
- package/dist/toggle/root/toggle-root.js +0 -78
- package/dist/toolbar/root/toolbar-root.js +0 -116
- package/dist/tooltip/root/tooltip-root.js +0 -427
- package/dist/tour/root/tour-root.js +0 -130
- package/dist/tour/stop/tour-stop.js +1 -127
- package/dist/tree/item/tree-item.js +0 -198
- package/dist/tree/root/tree-root.js +0 -97
- package/dist/trend/root/trend-root.js +0 -53
- package/dist/video/root/video-root.d.ts +8 -12
- package/dist/video/root/video-root.js +5 -756
- package/dist/video-playlist/item/video-playlist-item.js +1 -90
- package/dist/video-playlist/menu/video-playlist-menu.js +1 -132
- package/dist/video-playlist/root/video-playlist-root.js +0 -113
- package/dist/virtual-list/root/virtual-list-root.js +0 -105
- package/jsx.d.ts +1408 -2576
- package/package.json +6 -6
- package/vue.d.ts +1 -1
|
@@ -7,56 +7,15 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
import { customElement, HelixElement } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html, nothing } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
|
-
/**
|
|
11
|
-
* @summary Attaches a count or a presence mark to another element.
|
|
12
|
-
*
|
|
13
|
-
* @description
|
|
14
|
-
* Wraps whatever it decorates and pins the indicator to its corner, so the
|
|
15
|
-
* thing being counted stays the thing in the layout: a badge is an annotation,
|
|
16
|
-
* not a container that happens to hold an icon.
|
|
17
|
-
*
|
|
18
|
-
* The indicator says how many, and none is none. A `value` that reads as zero
|
|
19
|
-
* shows no mark at all, an absent one shows a bare dot, and any other value
|
|
20
|
-
* shows the value. `max` caps the top: past it the indicator reads `{max}+`
|
|
21
|
-
* rather than growing to fit, since a corner mark that widens with its value
|
|
22
|
-
* pushes the layout around.
|
|
23
|
-
*
|
|
24
|
-
* **A dot needs a label.** Without one it is a colored circle that announces
|
|
25
|
-
* nothing, which tells a sighted reader there is something new and tells
|
|
26
|
-
* everyone else there is not. The label is required in that form, and the
|
|
27
|
-
* indicator is hidden from assistive technology when a number already says it.
|
|
28
|
-
*
|
|
29
|
-
* @since 0.1.0
|
|
30
|
-
* @status stable
|
|
31
|
-
* @category markers
|
|
32
|
-
* @aliases Count, Indicator, Pip
|
|
33
|
-
* @documentation https://helix.fusion.dev/components/badge
|
|
34
|
-
*
|
|
35
|
-
* @slot - The element being decorated.
|
|
36
|
-
*
|
|
37
|
-
* @csspart indicator - The count, or the dot when there is no value.
|
|
38
|
-
*/
|
|
39
10
|
let HelixHeadlessBadge = class HelixHeadlessBadge extends HelixElement {
|
|
40
11
|
constructor() {
|
|
41
12
|
super(...arguments);
|
|
42
|
-
/**
|
|
43
|
-
* Accessible name.
|
|
44
|
-
*
|
|
45
|
-
* Required for a dot, which has no text to read.
|
|
46
|
-
*/
|
|
47
13
|
this.label = '';
|
|
48
|
-
/**
|
|
49
|
-
* The count or short text to show.
|
|
50
|
-
*
|
|
51
|
-
* Absent shows a dot, zero shows nothing.
|
|
52
|
-
*/
|
|
53
14
|
this.value = '';
|
|
54
15
|
}
|
|
55
|
-
/** Whether the value counts nothing, in which case there is no mark. */
|
|
56
16
|
get blank() {
|
|
57
17
|
return this.value.trim() !== '' && Number(this.value) === 0;
|
|
58
18
|
}
|
|
59
|
-
/** What the indicator reads, once the cap has been applied. */
|
|
60
19
|
get text() {
|
|
61
20
|
const count = Number(this.value);
|
|
62
21
|
if (this.max !== undefined && Number.isFinite(count) && count > this.max) {
|
|
@@ -67,33 +26,7 @@ let HelixHeadlessBadge = class HelixHeadlessBadge extends HelixElement {
|
|
|
67
26
|
render() {
|
|
68
27
|
const blank = this.blank;
|
|
69
28
|
const text = this.text;
|
|
70
|
-
/* A mark with text is content and is announced; a dot is decoration and is
|
|
71
|
-
not.
|
|
72
|
-
*
|
|
73
|
-
* It must not ask for a `label` as well, or a badge showing a count and no
|
|
74
|
-
* label is `aria-hidden` and the number it exists to show reaches nobody
|
|
75
|
-
* using a screen reader. The number is its own name where none is given,
|
|
76
|
-
* which is why `aria-label` is conditional on the label rather than on this:
|
|
77
|
-
* a `4` announces as "4" from the text, and `label="4 unread"` says the
|
|
78
|
-
* better thing.
|
|
79
|
-
*
|
|
80
|
-
* A dot with no label still says nothing, which is the deliberate half: a
|
|
81
|
-
* dot means "something here" and cannot say what, so naming it is the
|
|
82
|
-
* consumer's to do and silence is better than "badge". */
|
|
83
29
|
const announced = !blank && (text !== '' || this.label !== '');
|
|
84
|
-
// The indicator holds the text and nothing else, so an absent value leaves
|
|
85
|
-
// it empty and `[part="indicator"]:empty` is what draws the dot. Lit's
|
|
86
|
-
// marker comments do not affect `:empty`, but a whitespace text node does,
|
|
87
|
-
// which is why the tags close the way they do.
|
|
88
|
-
//
|
|
89
|
-
// It stays mounted when there is nothing to show, so the styled tier can
|
|
90
|
-
// transition it out rather than having it vanish between frames. Both
|
|
91
|
-
// markers are on the shadow content, not the host, so they cost no
|
|
92
|
-
// attribute a consumer's server markup would have to match.
|
|
93
|
-
//
|
|
94
|
-
// `single` is what lets a one-character mark be a circle. Whether the text
|
|
95
|
-
// fits inside the square is a fact about the string, and CSS cannot count
|
|
96
|
-
// characters.
|
|
97
30
|
return html `<slot></slot>
|
|
98
31
|
<span
|
|
99
32
|
part="indicator"
|
|
@@ -8,102 +8,26 @@ import { customElement, drawTooltip, extent, HelixElement, linear, linePath, Mix
|
|
|
8
8
|
import { html, nothing, svg } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
10
|
import '../../tooltip/index.js';
|
|
11
|
-
/** The drawing's own coordinates. Height only: the width is the element's. */
|
|
12
11
|
const HEIGHT = 100;
|
|
13
|
-
/**
|
|
14
|
-
* @summary Categories as columns, read from a table.
|
|
15
|
-
*
|
|
16
|
-
* @description
|
|
17
|
-
* Two decisions are made here rather than left to a consumer.
|
|
18
|
-
*
|
|
19
|
-
* **It measures from zero and there is no property to change that.** A bar's
|
|
20
|
-
* length *is* its value, so a baseline anywhere else makes the lengths
|
|
21
|
-
* proportional to nothing. A reader who wants the differences between values
|
|
22
|
-
* rather than the values should be drawing a line, which claims only a shape.
|
|
23
|
-
*
|
|
24
|
-
* **The labels are HTML beside the drawing rather than `<text>` inside it.** An
|
|
25
|
-
* `<svg>` that fills its box scales everything in it, including type: the same
|
|
26
|
-
* `font-size` comes out at 10px in a narrow chart and 43px in a wide one, and
|
|
27
|
-
* there is no non-scaling-font to answer it the way `vector-effect` answers
|
|
28
|
-
* strokes. So the columns are drawn and the words are written around them.
|
|
29
|
-
*
|
|
30
|
-
* The table is the accessible representation and this drawing is `aria-hidden`.
|
|
31
|
-
* A reader who wants the numbers gets the numbers, not a described picture of
|
|
32
|
-
* them.
|
|
33
|
-
*
|
|
34
|
-
* The custom properties below are written by this component and read by whoever
|
|
35
|
-
* draws it. A consumer sets none of them: they carry a number this element
|
|
36
|
-
* computed and CSS cannot.
|
|
37
|
-
*
|
|
38
|
-
* @since 0.1.0
|
|
39
|
-
* @status experimental
|
|
40
|
-
* @category charts
|
|
41
|
-
* @aliases Column Chart, Categorical Chart
|
|
42
|
-
* @documentation https://helix.fusion.dev/components/bar-chart
|
|
43
|
-
*
|
|
44
|
-
* @csspart axis - One scale's numbers, drawn only when `secondary` splits them.
|
|
45
|
-
* @csspart band - The slot the pointer is resting in.
|
|
46
|
-
* @csspart bar - One column.
|
|
47
|
-
* @csspart bar-wash-end - The far stop of a bar's gradient, where one is used.
|
|
48
|
-
* @csspart bar-wash-start - The stop of a bar's gradient at its base.
|
|
49
|
-
* @csspart baseline - The rule the columns stand on.
|
|
50
|
-
* @csspart gridline - One rule across the plot, under `gridlines`.
|
|
51
|
-
* @csspart group - One category's columns, together.
|
|
52
|
-
* @csspart label - One category's name.
|
|
53
|
-
* @csspart labels - The row of category names.
|
|
54
|
-
* @csspart line - A series drawn as a line rather than as columns, under `lines`.
|
|
55
|
-
* @csspart mark - The invisible point the tooltip is anchored to.
|
|
56
|
-
* @csspart plot - The drawing.
|
|
57
|
-
* @csspart svg - The drawing surface the bars and lines sit on.
|
|
58
|
-
* @csspart tick - One number on an axis.
|
|
59
|
-
* @csspart tooltip - The bubble, forwarded from `Tooltip`.
|
|
60
|
-
* @csspart tooltip-anchor - The `Tooltip` element itself.
|
|
61
|
-
* @csspart tooltip-cell - One column's name and value inside the tooltip.
|
|
62
|
-
* @csspart tooltip-label - The reading's own name inside the tooltip.
|
|
63
|
-
* @csspart tooltip-name - A column's name inside the tooltip.
|
|
64
|
-
* @csspart tooltip-value - A column's value inside the tooltip.
|
|
65
|
-
*
|
|
66
|
-
* @cssproperty --bar-chart-axis - The color of a single-series axis, taken from that series.
|
|
67
|
-
* @cssproperty --bar-chart-secondary - The color of the secondary axis when one series owns it. It resolves a `--helix-color-series-*` token, so on the headless tier alone it names something that is not there. #138.
|
|
68
|
-
* @cssproperty --chart-bar-wash - The gradient a bar would take, named per bar so the
|
|
69
|
-
* styled tier can point `fill` at it. Drawn whatever the treatment and used only where
|
|
70
|
-
* `wash="gradient"` asks for it.
|
|
71
|
-
*/
|
|
72
12
|
let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource(HelixElement) {
|
|
73
|
-
/** The series names on the second scale, lowercased and trimmed. */
|
|
74
13
|
get scaled() {
|
|
75
14
|
return new Set((this.secondary ?? '')
|
|
76
15
|
.split(/\s+/)
|
|
77
16
|
.map((name) => name.trim().toLowerCase())
|
|
78
17
|
.filter(Boolean));
|
|
79
18
|
}
|
|
80
|
-
/** The series names drawn as a line, lowercased and trimmed for matching. */
|
|
81
19
|
get lined() {
|
|
82
20
|
return new Set((this.lines ?? '')
|
|
83
21
|
.split(/\s+/)
|
|
84
22
|
.map((name) => name.trim().toLowerCase())
|
|
85
23
|
.filter(Boolean));
|
|
86
24
|
}
|
|
87
|
-
/** Whether the levels are ruled, which is every value but `category`. */
|
|
88
25
|
get rulesValue() {
|
|
89
26
|
return this.gridlines !== undefined && this.gridlines !== 'category';
|
|
90
27
|
}
|
|
91
|
-
/** Whether each category is ruled, which only two values ask for. */
|
|
92
28
|
get rulesCategory() {
|
|
93
29
|
return this.gridlines === 'both' || this.gridlines === 'category';
|
|
94
30
|
}
|
|
95
|
-
/**
|
|
96
|
-
* Tells the pointer which way the categories run.
|
|
97
|
-
*
|
|
98
|
-
* In `willUpdate` because `direction` is a property a consumer can change at
|
|
99
|
-
* any time, and the controller resolves a reading from whichever axis it was
|
|
100
|
-
* last told about: set once at connect, a chart turned sideways after that
|
|
101
|
-
* goes on reading x and answers with the same row wherever the pointer goes.
|
|
102
|
-
*
|
|
103
|
-
* `willUpdate` and not `updated`, because SSR calls the first and not the
|
|
104
|
-
* second, and a value the server and the client disagree about is worse than
|
|
105
|
-
* one neither has.
|
|
106
|
-
*/
|
|
107
31
|
willUpdate(changed) {
|
|
108
32
|
super.willUpdate(changed);
|
|
109
33
|
this.pointer.axis = this.direction === 'horizontal' ? 'block' : 'inline';
|
|
@@ -114,33 +38,14 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource
|
|
|
114
38
|
if (categories.length === 0) {
|
|
115
39
|
return html `<div id="helix-chart-plot" part="plot"></div>`;
|
|
116
40
|
}
|
|
117
|
-
/* Zero at both ends, so a series that goes negative gets a baseline inside
|
|
118
|
-
the box and its columns hang below it. `extent` over every series at once
|
|
119
|
-
rather than per series, because two series drawn to different scales are
|
|
120
|
-
two charts sharing a picture. */
|
|
121
41
|
const scaled = this.scaled;
|
|
122
42
|
const own = (one) => scaled.has(one.name.trim().toLowerCase());
|
|
123
|
-
/* Two extents where a series asked for its own, and the primary is taken
|
|
124
|
-
over the series that did *not*: measuring the primary over everything
|
|
125
|
-
would stretch it to hold numbers no column is drawn against, which is the
|
|
126
|
-
one thing splitting the axes was meant to stop. Empty on either side
|
|
127
|
-
falls back to the whole set, so `secondary` naming a column that is not
|
|
128
|
-
there leaves a chart rather than a blank. */
|
|
129
43
|
const values = series.flatMap((one) => one.points.map((point) => point.value));
|
|
130
44
|
const on = (which) => {
|
|
131
45
|
const found = series.filter(which).flatMap((one) => one.points.map((point) => point.value));
|
|
132
46
|
return found.length > 0 ? found : values.length > 0 ? values : [0];
|
|
133
47
|
};
|
|
134
48
|
const [low, high] = extent(scaled.size === 0 ? on(() => true) : on((one) => !own(one)));
|
|
135
|
-
/* **The range is inverted for columns and not for bars**, which is the
|
|
136
|
-
whole of what makes the two directions different arithmetic rather than
|
|
137
|
-
one drawing turned on its side. SVG counts down from the top, so a column
|
|
138
|
-
growing upward needs `[HEIGHT, 0]`; a bar growing rightward is already
|
|
139
|
-
going the way x counts, so it takes `[0, HEIGHT]`.
|
|
140
|
-
*
|
|
141
|
-
* Reusing the inverted scale for both puts the longest bar at x=0 and every
|
|
142
|
-
shorter one hard against the right edge, growing away from the baseline
|
|
143
|
-
instead of out of it. */
|
|
144
49
|
const domain = [Math.min(0, low), Math.max(0, high)];
|
|
145
50
|
const range = this.direction === 'horizontal' ? [0, HEIGHT] : [HEIGHT, 0];
|
|
146
51
|
const y = linear(domain, range);
|
|
@@ -149,88 +54,28 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource
|
|
|
149
54
|
const [second, top] = extent(scaled.size > 0 ? on(own) : [0]);
|
|
150
55
|
const other = [Math.min(0, second), Math.max(0, top)];
|
|
151
56
|
const z = scaled.size > 0 ? linear(other, range) : undefined;
|
|
152
|
-
/* Widest number on either scale, in characters, so the axis column is the
|
|
153
|
-
size of its own contents.
|
|
154
|
-
*
|
|
155
|
-
* The ticks are absolutely positioned, which is what puts each one on the
|
|
156
|
-
* level it names and also what stops any of them contributing a width: an
|
|
157
|
-
* axis of six numbers laid out that way is an empty box. The count is known
|
|
158
|
-
* here and unknowable to a selector, so it is written as a length and the
|
|
159
|
-
stylesheet reads it. */
|
|
160
57
|
const widest = z === undefined
|
|
161
58
|
? 0
|
|
162
59
|
: Math.max(...[...ticks(domain), ...ticks(other)].map((level) => level.toLocaleString().length));
|
|
163
|
-
/* The second axis wears its series' color when exactly one series is on it,
|
|
164
|
-
which is the case worth helping and the only one a single color can
|
|
165
|
-
describe honestly: with two sharing it, painting the axis either of their
|
|
166
|
-
colors says it belongs to that one.
|
|
167
|
-
*
|
|
168
|
-
**Written onto the axis and not onto the plot.** The axis is the plot's
|
|
169
|
-
sibling, being a column of the host grid, and a custom property inherits
|
|
170
|
-
down and never sideways: written onto the plot, both axes come out the
|
|
171
|
-
neutral. */
|
|
172
60
|
const lone = series.filter(own);
|
|
173
61
|
const tint = lone.length === 1 && lone[0] !== undefined
|
|
174
62
|
? `--bar-chart-secondary: var(--chart-series-${seriesIndex(series.indexOf(lone[0]))})`
|
|
175
63
|
: undefined;
|
|
176
64
|
return html `${z === undefined ? nothing : this.renderAxis(y, domain, 'primary', sideways, widest)}${this.renderPlot(series, categories, y, domain, floor, z)}${z === undefined ? nothing : this.renderAxis(z, other, 'secondary', sideways, widest, tint)}${this.renderLabels(categories)}`;
|
|
177
65
|
}
|
|
178
|
-
/**
|
|
179
|
-
* The columns themselves, behind a seam the styled tier can move.
|
|
180
|
-
*
|
|
181
|
-
* `aria-hidden`, because the table it was read from is the readable version
|
|
182
|
-
* and a screen reader hearing both hears the same numbers twice.
|
|
183
|
-
*/
|
|
184
66
|
renderPlot(series, categories, y, domain, floor, z) {
|
|
185
67
|
const sideways = this.direction === 'horizontal';
|
|
186
68
|
const slot = 100 / categories.length;
|
|
187
|
-
/* Split first, because the columns share the slot between themselves and a
|
|
188
|
-
line takes none of it. Sized by every series instead, a chart of two
|
|
189
|
-
columns and one line draws both columns two thirds as wide and leaves a
|
|
190
|
-
third of every slot empty, for a mark that is not in the slot at all. */
|
|
191
69
|
const lined = this.lined;
|
|
192
|
-
/* Kept with the index it has among *all* the series, because a column needs
|
|
193
|
-
two different numbers and they stopped being the same the moment one
|
|
194
|
-
series could be a line. `depth` is its place in the slot, so the columns
|
|
195
|
-
stand shoulder to shoulder with no gap where a line was; `tone` is its
|
|
196
|
-
place in the palette, so it keeps the color the legend gives it.
|
|
197
|
-
*
|
|
198
|
-
Passing the first for both moves a series into its neighbor's color as
|
|
199
|
-
well as its slot: with `Target` drawn as a line, a chart of four series
|
|
200
|
-
paints three columns in the first three colors and the line in the third
|
|
201
|
-
one as well. */
|
|
202
70
|
const scaled = this.scaled;
|
|
203
71
|
const own = (one) => scaled.has(one.name.trim().toLowerCase());
|
|
204
|
-
/* The scale a series is measured against, resolved once per series rather
|
|
205
|
-
than per point. `z` is absent unless `secondary` named something, so the
|
|
206
|
-
ordinary chart takes exactly the path it took before this existed. */
|
|
207
72
|
const against = (one) => z !== undefined && own(one) ? z : y;
|
|
208
73
|
const columns = series
|
|
209
74
|
.map((one, index) => ({ one, tone: index }))
|
|
210
75
|
.filter(({ one }) => !lined.has(one.name.trim().toLowerCase()));
|
|
211
|
-
/* **The stride is what the columns share and the thickness is what they
|
|
212
|
-
draw.** Back to back, two columns meet on one coordinate and each one's
|
|
213
|
-
edge is the other's: an outlined chart then shows a single line between
|
|
214
|
-
them rather than two, and a bar looks as though it were missing a side.
|
|
215
|
-
A gap is also just the right way to draw a group, which is why the split
|
|
216
|
-
stays whether anything is outlined or not.
|
|
217
|
-
|
|
218
|
-
Centered in the stride rather than flush against it, so the gap between two
|
|
219
|
-
columns is the same size everywhere and the group sits where it did. One
|
|
220
|
-
series has no neighbor to be separated from and keeps the full stride. */
|
|
221
76
|
const stride = (slot * 0.72) / Math.max(columns.length, 1);
|
|
222
77
|
const gap = columns.length > 1 ? stride * 0.08 : 0;
|
|
223
78
|
const thickness = stride - gap;
|
|
224
|
-
/* **The geometry is computed for the axis it is drawn on, not rotated onto
|
|
225
|
-
it.** Turning the whole plot with `rotate: 90deg` is the shorter CSS and
|
|
226
|
-
does not work: a rotation is about the element's own center, so the
|
|
227
|
-
drawing leaves its grid cell entirely while the labels, which are a
|
|
228
|
-
separate row, stay where they were. What that produces is one column
|
|
229
|
-
floating beside a list of names.
|
|
230
|
-
*
|
|
231
|
-
* Swapping the two axes here costs one branch and keeps every other fact
|
|
232
|
-
* about the chart the same. `value` runs along x when the bars are
|
|
233
|
-
* sideways and along y otherwise; `category` takes the other one. */
|
|
234
79
|
const bar = (value, index, depth, tone, scale) => {
|
|
235
80
|
const at = scale(value);
|
|
236
81
|
const along = Math.min(floor, at);
|
|
@@ -266,18 +111,7 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource
|
|
|
266
111
|
viewBox="0 0 100 ${HEIGHT}"
|
|
267
112
|
xmlns="http://www.w3.org/2000/svg"
|
|
268
113
|
>
|
|
269
|
-
${
|
|
270
|
-
/* **One gradient per series, rendered whether or not anything points
|
|
271
|
-
at it.** A `<defs>` entry draws nothing on its own, and this tier's
|
|
272
|
-
business is elements while which fill a bar takes is the styled
|
|
273
|
-
tier's: `wash="gradient"` there switches `fill` to
|
|
274
|
-
`--chart-bar-wash` and this is what it would find.
|
|
275
|
-
|
|
276
|
-
Colored through the series property rather than an attribute, so
|
|
277
|
-
the stops read a token this tier never names. The id resolves
|
|
278
|
-
within the shadow root, so two charts on a page carrying the same
|
|
279
|
-
ids stay independent. */
|
|
280
|
-
svg `<defs>
|
|
114
|
+
${svg `<defs>
|
|
281
115
|
${series.map((_, index) => svg `<linearGradient
|
|
282
116
|
id="helix-bar-wash-${seriesIndex(index)}"
|
|
283
117
|
style="--chart-series: var(--chart-series-${seriesIndex(index)})"
|
|
@@ -295,52 +129,16 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource
|
|
|
295
129
|
? svg `<line part="baseline" x1=${floor} x2=${floor} y1="0" y2="100" />`
|
|
296
130
|
: svg `<line part="baseline" x1="0" x2="100" y1=${floor} y2=${floor} />`}
|
|
297
131
|
${this.renderBand(categories.length, sideways)}
|
|
298
|
-
${categories.map((_, index) =>
|
|
299
|
-
/* The group the tooltip is about says so, and the stylesheet fades
|
|
300
|
-
the rest. A tooltip over a chart of five categories otherwise
|
|
301
|
-
makes a reader match its heading against the row of names to find
|
|
302
|
-
out which columns it means. */
|
|
303
|
-
svg `<g data-active=${this.pointer.at === index ? '' : nothing} part="group">
|
|
132
|
+
${categories.map((_, index) => svg `<g data-active=${this.pointer.at === index ? '' : nothing} part="group">
|
|
304
133
|
${columns.map(({ one, tone }, depth) => bar(one.points[index]?.value ?? 0, index, depth, tone, against(one)))}
|
|
305
134
|
</g>`)}
|
|
306
|
-
${
|
|
307
|
-
/* After the groups, so a line is never drawn under the columns it is
|
|
308
|
-
meant to be read against. Outside them too: a group is one
|
|
309
|
-
category and a line crosses all of them, so it belongs to the plot
|
|
310
|
-
rather than to any one slot. */
|
|
311
|
-
series.map((one) => lined.has(one.name.trim().toLowerCase())
|
|
135
|
+
${series.map((one) => lined.has(one.name.trim().toLowerCase())
|
|
312
136
|
? this.renderLine(one, series.indexOf(one), slot, against(one), sideways)
|
|
313
137
|
: nothing)}
|
|
314
138
|
</svg>
|
|
315
139
|
${this.renderTooltip()}
|
|
316
140
|
</div>`;
|
|
317
141
|
}
|
|
318
|
-
/**
|
|
319
|
-
* The numbers along one scale, drawn only where there are two of them.
|
|
320
|
-
*
|
|
321
|
-
* **A chart with one scale renders no axis, on purpose.** Columns measured
|
|
322
|
-
* against each other need no numbers to be compared, and a row of them beside
|
|
323
|
-
* a small chart is furniture that makes the drawing smaller for no reading a
|
|
324
|
-
* reader was going to do. With two scales that argument inverts: a reader
|
|
325
|
-
* cannot tell there are two, cannot tell which series is on which, and cannot
|
|
326
|
-
* recover either from the picture. So this appears with `secondary` and never
|
|
327
|
-
* without it.
|
|
328
|
-
*
|
|
329
|
-
* **Beside the plot and not inside it.** Inside, the numbers are drawn over
|
|
330
|
-
* the drawing, and a horizontal chart shows it immediately: its value axis
|
|
331
|
-
* runs along the bottom, which is where the last row of bars is. The vertical
|
|
332
|
-
* case has the same fault and enough slack to hide it. The plot is also the
|
|
333
|
-
* pointer's catchment and the tooltip's containing block, both measured from
|
|
334
|
-
* its box, so padding it to make room would move every reading half an axis
|
|
335
|
-
* to the right.
|
|
336
|
-
*
|
|
337
|
-
* A column of the host grid instead, which the stylesheet grows only when
|
|
338
|
-
* `secondary` is set. That is what the property reflects for.
|
|
339
|
-
*
|
|
340
|
-
* The primary hugs the edge the drawing starts at and the secondary the edge
|
|
341
|
-
* it ends at: down the two sides for columns, along the bottom and top for
|
|
342
|
-
* bars, which is where each axis is when the chart is turned.
|
|
343
|
-
*/
|
|
344
142
|
renderAxis(scale, domain, which, sideways, widest, tint) {
|
|
345
143
|
const sizing = sideways ? undefined : `--bar-chart-axis: ${widest}ch`;
|
|
346
144
|
return html `<div
|
|
@@ -356,25 +154,10 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource
|
|
|
356
154
|
>`)}
|
|
357
155
|
</div>`;
|
|
358
156
|
}
|
|
359
|
-
/**
|
|
360
|
-
* The grid, behind everything, or nothing at all.
|
|
361
|
-
*
|
|
362
|
-
* Across the measured axis, which turns with `direction`: a rule belongs to
|
|
363
|
-
* the axis the bars are measured on and not the one they are arranged on, so
|
|
364
|
-
* columns get horizontal rules and rows get vertical ones. Getting that
|
|
365
|
-
* backwards draws a grid that divides the categories, which are already
|
|
366
|
-
* divided by being separate bars.
|
|
367
|
-
*
|
|
368
|
-
* Drawn before the baseline, so where a gridline and zero coincide the
|
|
369
|
-
* baseline is what shows: zero is a fact about the data and a gridline is a
|
|
370
|
-
* ruler.
|
|
371
|
-
*/
|
|
372
157
|
renderGridlines(y, domain, sideways, count) {
|
|
373
158
|
if (this.gridlines === undefined) {
|
|
374
159
|
return nothing;
|
|
375
160
|
}
|
|
376
|
-
/* Through the middle of each group, which is where the column stands, and
|
|
377
|
-
not on the boundary between two of them. */
|
|
378
161
|
const slot = 100 / Math.max(count, 1);
|
|
379
162
|
const categories = this.rulesCategory
|
|
380
163
|
? Array.from({ length: count }, (_, index) => (index + 0.5) * slot).map((at) => sideways
|
|
@@ -388,20 +171,6 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource
|
|
|
388
171
|
: [];
|
|
389
172
|
return svg `${categories}${levels}`;
|
|
390
173
|
}
|
|
391
|
-
/**
|
|
392
|
-
* The slot the pointer is resting in, drawn behind the columns.
|
|
393
|
-
*
|
|
394
|
-
* **The honest version of the fade.** A group under the pointer already sets
|
|
395
|
-
* `data-active` and the stylesheet dims the others, which says which group is
|
|
396
|
-
* meant by making four of five quieter: it reads as the chart losing
|
|
397
|
-
* confidence rather than as one column being pointed at. A band says the same
|
|
398
|
-
* thing by lighting the region the pointer is actually in, and the region is
|
|
399
|
-
* the whole slot, because that is the catchment the pointer resolves against.
|
|
400
|
-
*
|
|
401
|
-
* Both are kept. The fade separates the active group from its neighbors and
|
|
402
|
-
* the band says where the neighborhood is, and on a chart of two categories
|
|
403
|
-
* the fade alone is ambiguous about which half is lit.
|
|
404
|
-
*/
|
|
405
174
|
renderBand(count, sideways) {
|
|
406
175
|
const at = this.pointer.at;
|
|
407
176
|
if (at < 0 || count === 0) {
|
|
@@ -417,31 +186,12 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource
|
|
|
417
186
|
${categories.map((one) => html `<span part="label">${one}</span>`)}
|
|
418
187
|
</div>`;
|
|
419
188
|
}
|
|
420
|
-
/**
|
|
421
|
-
* One series drawn as a line across the categories, behind a seam.
|
|
422
|
-
*
|
|
423
|
-
* Through the middle of each slot, which is where the group stands, so a
|
|
424
|
-
* point sits over its own columns rather than over the gap between two of
|
|
425
|
-
* them. The same arithmetic the category gridlines use, and for the same
|
|
426
|
-
* reason: the reading is the thing, the boundary is only where the pointer
|
|
427
|
-
* changes its mind.
|
|
428
|
-
*
|
|
429
|
-
* `data-series` keeps the index the series has among *all* of them, not among
|
|
430
|
-
* the lines, so the color a line takes is the color the legend gives it and
|
|
431
|
-
* moving a column between marks does not repaint the chart.
|
|
432
|
-
*/
|
|
433
189
|
renderLine(one, depth, slot, y, sideways) {
|
|
434
190
|
const points = one.points.map((point, index) => {
|
|
435
191
|
const across = index * slot + slot / 2;
|
|
436
192
|
const along = y(point.value);
|
|
437
193
|
return sideways ? [along, across] : [across, along];
|
|
438
194
|
});
|
|
439
|
-
/* Straight between the category centers, and no `curve` property to change
|
|
440
|
-
it. A bar chart's other axis is categorical: there is nothing between
|
|
441
|
-
Gateway and Billing, so a curve through them draws values at positions
|
|
442
|
-
that do not exist and bulges past the readings on the way. `LineChart`
|
|
443
|
-
offers the choice because its axis is continuous and the shape between
|
|
444
|
-
two readings is a claim somebody may legitimately want to make. */
|
|
445
195
|
return svg `<path
|
|
446
196
|
d=${linePath(points, 'straight')}
|
|
447
197
|
data-series=${seriesIndex(depth)}
|
|
@@ -449,22 +199,12 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource
|
|
|
449
199
|
style="--chart-series: var(--chart-series-${seriesIndex(depth)})"
|
|
450
200
|
/>`;
|
|
451
201
|
}
|
|
452
|
-
/**
|
|
453
|
-
* What a pointer resting on a reading shows.
|
|
454
|
-
*
|
|
455
|
-
* A seam over `drawTooltip`, which holds the markup every chart shares. The
|
|
456
|
-
* usual reason to override is arrangement: every cell of the row is already
|
|
457
|
-
* there, including the columns nothing was plotted from.
|
|
458
|
-
*/
|
|
459
202
|
renderTooltip() {
|
|
460
203
|
const at = this.pointer.at;
|
|
461
204
|
const series = this.series;
|
|
462
205
|
if (at < 0 || series.length === 0) {
|
|
463
206
|
return drawTooltip(this.sourceRows, at, this.tag('tooltip'));
|
|
464
207
|
}
|
|
465
|
-
/* Off the far end of the tallest bar in the group, so the box never covers
|
|
466
|
-
the columns it is describing. Horizontal bars grow rightward, so the far
|
|
467
|
-
end is along x and the arrow hangs off the box's start edge. */
|
|
468
208
|
const values = series.map((one) => one.points[at]?.value ?? 0);
|
|
469
209
|
const all = series.flatMap((one) => one.points.map((point) => point.value));
|
|
470
210
|
const [low, high] = extent(all.length > 0 ? all : [0]);
|
|
@@ -7,58 +7,15 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
import { customElement, HelixElement, slotFilled } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html, nothing } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
|
-
/**
|
|
11
|
-
* @summary A passage set off from the prose around it.
|
|
12
|
-
*
|
|
13
|
-
* @description
|
|
14
|
-
* Renders a real `<blockquote>` with a `<footer>` for the attribution, which is
|
|
15
|
-
* the structure the HTML spec describes for exactly this: the attribution is
|
|
16
|
-
* about the quotation rather than part of it, and putting it inside the quoted
|
|
17
|
-
* text claims the source said their own name.
|
|
18
|
-
*
|
|
19
|
-
* A quotation run inside a sentence is a `Quote`, which is a different element
|
|
20
|
-
* because it is a different job.
|
|
21
|
-
*
|
|
22
|
-
* @since 0.1.0
|
|
23
|
-
* @status stable
|
|
24
|
-
* @category typography
|
|
25
|
-
* @aliases Pullquote, Citation, Excerpt
|
|
26
|
-
* @documentation https://helix.fusion.dev/components/blockquote
|
|
27
|
-
*
|
|
28
|
-
* @slot - The quoted passage.
|
|
29
|
-
* @slot attribution - Who said it, and where.
|
|
30
|
-
*
|
|
31
|
-
* @csspart attribution - The footer carrying the source.
|
|
32
|
-
* @csspart blockquote - The quotation.
|
|
33
|
-
*/
|
|
34
10
|
let HelixHeadlessBlockquote = class HelixHeadlessBlockquote extends HelixElement {
|
|
35
11
|
constructor() {
|
|
36
12
|
super(...arguments);
|
|
37
|
-
/**
|
|
38
|
-
* URL identifying where the passage came from.
|
|
39
|
-
*/
|
|
40
13
|
this.cite = '';
|
|
41
14
|
}
|
|
42
|
-
/**
|
|
43
|
-
* Says on the footer whether there is an attribution in it.
|
|
44
|
-
*
|
|
45
|
-
* A `<footer>` with nothing in it is still a block in the quote's flow and
|
|
46
|
-
* still takes the container's gap, so every blockquote written without an
|
|
47
|
-
* attribution reserved a full space below itself. There is no selector that
|
|
48
|
-
* can ask, `slot:empty` being about a slot's fallback rather than its
|
|
49
|
-
* assigned nodes, so the component asks and says so on the footer inside this
|
|
50
|
-
* shadow root: an attribute on the host is a change to markup the consumer
|
|
51
|
-
* rendered, which React reports on hydration.
|
|
52
|
-
*/
|
|
53
15
|
markEmpty() {
|
|
54
16
|
const footer = this.renderRoot?.querySelector('[part="attribution"]');
|
|
55
17
|
const empty = !slotFilled(this.renderRoot?.querySelector('slot[name="attribution"]'));
|
|
56
18
|
footer?.toggleAttribute('data-empty', empty);
|
|
57
|
-
/* `hidden` beside the mirror, because `data-empty` is a hook the styled
|
|
58
|
-
sheet reads and this tier has no sheet: an empty region left in the box
|
|
59
|
-
tree is a gap a consumer cannot close without knowing to look for it. The
|
|
60
|
-
styled rule stays, because that sheet sets `display` on this part and so
|
|
61
|
-
outranks the UA one. */
|
|
62
19
|
footer?.toggleAttribute('hidden', empty);
|
|
63
20
|
}
|
|
64
21
|
updated(changed) {
|
|
@@ -66,9 +23,6 @@ let HelixHeadlessBlockquote = class HelixHeadlessBlockquote extends HelixElement
|
|
|
66
23
|
this.markEmpty();
|
|
67
24
|
}
|
|
68
25
|
render() {
|
|
69
|
-
// The footer is a bare slot inside a real `<footer>`: the element is the
|
|
70
|
-
// semantics and cannot be omitted, while an unfilled slot inside it is
|
|
71
|
-
// `display: contents` and generates no box.
|
|
72
26
|
return html `<blockquote part="blockquote" cite=${this.cite || nothing}>
|
|
73
27
|
<slot></slot>
|
|
74
28
|
<footer part="attribution"><slot name="attribution"></slot></footer>
|
|
@@ -9,85 +9,18 @@ import { caretRight } from '@fusion.dev/helix-icons';
|
|
|
9
9
|
import { html, nothing } from 'lit';
|
|
10
10
|
import { property } from 'lit/decorators.js';
|
|
11
11
|
import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
|
|
12
|
-
/**
|
|
13
|
-
* @summary One step in a breadcrumb trail.
|
|
14
|
-
*
|
|
15
|
-
* @description
|
|
16
|
-
* Contributes the separator, the list-item role, and the link itself where one
|
|
17
|
-
* is wanted.
|
|
18
|
-
*
|
|
19
|
-
* **`href` makes the crumb the link.** A router cannot reach an anchor inside a
|
|
20
|
-
* shadow root, and the consumer restores the soft navigation by intercepting the
|
|
21
|
-
* click on the way out, which is what `Tree.Item` and `List.Item` both do. What
|
|
22
|
-
* the anchor buys is the whole crumb as a target, Enter, middle click, the
|
|
23
|
-
* status bar, and a trail that works before any JavaScript runs.
|
|
24
|
-
*
|
|
25
|
-
* **And the parent needs it.** A trail that collapses has to rebuild its hidden
|
|
26
|
-
* crumbs somewhere else, which means reading each one's destination. From a
|
|
27
|
-
* property that is a read; from a consumer's slotted markup it is a hunt for an
|
|
28
|
-
* anchor and a guess about which element is the link.
|
|
29
|
-
*
|
|
30
|
-
* A crumb with no `href` is unchanged: the consumer slots whatever they like,
|
|
31
|
-
* including their own anchor, and this renders a plain `<span>` around it.
|
|
32
|
-
*
|
|
33
|
-
* Each item renders its own separator and hides it with `:host(:first-child)`,
|
|
34
|
-
* so the trail needs no index and no coordination from the parent. `:host()`
|
|
35
|
-
* takes a structural pseudo-class, unlike `:has()`, which it rejects.
|
|
36
|
-
*
|
|
37
|
-
* The current page is text, not a link. Linking a page to itself is a known
|
|
38
|
-
* screen-reader annoyance, and `aria-current="page"` on the host is what
|
|
39
|
-
* conveys position.
|
|
40
|
-
*
|
|
41
|
-
* @since 0.1.0
|
|
42
|
-
* @status stable
|
|
43
|
-
* @aliases Crumb
|
|
44
|
-
* @documentation https://helix.fusion.dev/components/breadcrumbs
|
|
45
|
-
*
|
|
46
|
-
* @slot - The step's label. Without an `href` this may be the consumer's own
|
|
47
|
-
* anchor, styled with `data-link="quiet"`, since a slotted anchor belongs to the
|
|
48
|
-
* outer tree and cannot be reached from this component's styles.
|
|
49
|
-
* @slot end - After it, for a count or a mark.
|
|
50
|
-
* @slot separator - Replaces the chevron shown before this item.
|
|
51
|
-
* @slot start - Before the label, for an icon.
|
|
52
|
-
*
|
|
53
|
-
* @csspart link - The crumb itself, which is an `<a>` where `href` is set.
|
|
54
|
-
* @csspart separator - The separator preceding the item.
|
|
55
|
-
* @csspart separator-mark - The glyph between this crumb and the next, where none is slotted.
|
|
56
|
-
*/
|
|
57
12
|
let HelixHeadlessBreadcrumb = class HelixHeadlessBreadcrumb extends HelixElement {
|
|
58
13
|
constructor() {
|
|
59
14
|
super(...arguments);
|
|
60
|
-
/**
|
|
61
|
-
* Whether this item is the page the reader is on.
|
|
62
|
-
*/
|
|
63
15
|
this.current = false;
|
|
64
|
-
/**
|
|
65
|
-
* What the link declares about where it goes.
|
|
66
|
-
*
|
|
67
|
-
* The consumer's decision.
|
|
68
|
-
*/
|
|
69
16
|
this.rel = '';
|
|
70
|
-
/**
|
|
71
|
-
* Where the link opens, for the rare crumb that leaves the site.
|
|
72
|
-
*/
|
|
73
17
|
this.target = '';
|
|
74
18
|
}
|
|
75
19
|
willUpdate(changed) {
|
|
76
20
|
super.willUpdate(changed);
|
|
77
|
-
// On the host, because the item is an `<ol>` child in the flat tree and the
|
|
78
|
-
// role has to be there for the list to have items. In `willUpdate` rather
|
|
79
|
-
// than `connectedCallback`, which the server never calls.
|
|
80
21
|
hostAria(this.internals, { role: 'listitem' });
|
|
81
22
|
this.internals.ariaCurrent = this.current ? 'page' : null;
|
|
82
23
|
}
|
|
83
|
-
/**
|
|
84
|
-
* The mark between one crumb and the next.
|
|
85
|
-
*
|
|
86
|
-
* The chevron is the slot's own fallback, so replacing it is a matter of
|
|
87
|
-
* filling the slot and costs the consumer no way to opt out of the default.
|
|
88
|
-
* The seam answers the other half: a tier drawing its own separator gets it
|
|
89
|
-
* without reimplementing the crumb, its link and its current-page branch.
|
|
90
|
-
*/
|
|
91
24
|
renderSeparator() {
|
|
92
25
|
return html `<slot name="separator"
|
|
93
26
|
><svg part="separator-mark" fill="currentColor" viewBox=${caretRight.viewBox}>
|
|
@@ -96,12 +29,7 @@ let HelixHeadlessBreadcrumb = class HelixHeadlessBreadcrumb extends HelixElement
|
|
|
96
29
|
>`;
|
|
97
30
|
}
|
|
98
31
|
render() {
|
|
99
|
-
/* The label and whatever flanks it, which is the same content whether or
|
|
100
|
-
not it is a link. Built once so the two branches below cannot drift. */
|
|
101
32
|
const label = html `<slot name="start"></slot><slot></slot><slot name="end"></slot>`;
|
|
102
|
-
/* The current page is text, not a link, whatever it was given, so `current`
|
|
103
|
-
wins over `href` rather than the two contradicting each other in the
|
|
104
|
-
markup. */
|
|
105
33
|
const linked = (this.href ?? '') !== '' && !this.current;
|
|
106
34
|
return html `<span part="separator" aria-hidden="true"
|
|
107
35
|
>${this.renderSeparator()}</span
|