@fusion.dev/helix-headless 0.145.0 → 0.146.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 +7373 -3849
- package/dist/accordion/item/accordion-item.d.ts +14 -0
- package/dist/accordion/item/accordion-item.js +16 -6
- package/dist/accordion/root/accordion-root.d.ts +8 -5
- package/dist/announcement/root/announcement-root.d.ts +3 -2
- package/dist/announcement/root/announcement-root.js +3 -1
- package/dist/app-header/root/app-header-root.d.ts +7 -2
- package/dist/app-header/root/app-header-root.js +9 -1
- package/dist/app-header/sectioning.d.ts +3 -1
- package/dist/app-header/sectioning.js +3 -2
- package/dist/avatar/group/avatar-group.d.ts +20 -14
- package/dist/avatar/group/avatar-group.js +17 -19
- package/dist/badge/root/badge-root.d.ts +3 -1
- package/dist/badge/root/badge-root.js +2 -2
- package/dist/bar-chart/root/bar-chart-root.d.ts +18 -1
- package/dist/bar-chart/root/bar-chart-root.js +52 -15
- package/dist/bar-chart/stack.d.ts +15 -0
- package/dist/bar-chart/stack.js +19 -0
- package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +25 -0
- package/dist/breadcrumbs/root/breadcrumbs-root.js +50 -3
- package/dist/button/root/button-root.d.ts +26 -0
- package/dist/button/root/button-root.js +38 -18
- package/dist/button-group/root/button-group-root.d.ts +2 -1
- package/dist/button-group/root/button-group-root.js +5 -1
- package/dist/canvas/node/canvas-node.d.ts +4 -3
- package/dist/canvas/node/canvas-node.js +3 -2
- package/dist/canvas/root/canvas-root.js +2 -2
- package/dist/card/root/card-root.d.ts +24 -3
- package/dist/card/root/card-root.js +20 -8
- package/dist/carousel/root/carousel-root.d.ts +43 -4
- package/dist/carousel/root/carousel-root.js +40 -12
- package/dist/carousel/stops.d.ts +20 -0
- package/dist/carousel/stops.js +30 -0
- package/dist/chat/message/chat-message.js +5 -4
- package/dist/chat/naming.d.ts +5 -1
- package/dist/chat/naming.js +4 -2
- package/dist/chat/root/chat-root.d.ts +25 -3
- package/dist/chat/root/chat-root.js +22 -5
- package/dist/checkbox/root/checkbox-root.d.ts +20 -0
- package/dist/checkbox/root/checkbox-root.js +14 -1
- package/dist/checkbox-group/root/checkbox-group-root.d.ts +8 -2
- package/dist/checkbox-group/root/checkbox-group-root.js +12 -10
- package/dist/checkbox-group/select-all.d.ts +27 -0
- package/dist/checkbox-group/select-all.js +12 -0
- package/dist/code-block/highlight.d.ts +15 -0
- package/dist/code-block/highlight.js +4 -0
- package/dist/code-block/root/code-block-root.d.ts +49 -1
- package/dist/code-block/root/code-block-root.js +58 -3
- package/dist/color-input/root/color-input-root.d.ts +3 -0
- package/dist/color-input/root/color-input-root.js +6 -3
- package/dist/color-picker/root/color-picker-root.d.ts +33 -4
- package/dist/color-picker/root/color-picker-root.js +80 -21
- package/dist/color-picker/swatches.d.ts +12 -0
- package/dist/color-picker/swatches.js +26 -0
- package/dist/combobox/root/combobox-root.d.ts +43 -0
- package/dist/combobox/root/combobox-root.js +79 -15
- package/dist/command/root/command-root.d.ts +16 -1
- package/dist/command/root/command-root.js +22 -4
- package/dist/copy-button/root/copy-button-root.d.ts +5 -0
- package/dist/copy-button/root/copy-button-root.js +10 -3
- package/dist/data-table/index.d.ts +1 -1
- package/dist/data-table/index.js +1 -1
- package/dist/data-table/keys.d.ts +24 -0
- package/dist/data-table/keys.js +24 -0
- package/dist/data-table/root/data-table-root.d.ts +27 -18
- package/dist/data-table/root/data-table-root.js +20 -52
- package/dist/data-table/tree.d.ts +16 -1
- package/dist/data-table/tree.js +2 -3
- package/dist/date-input/root/date-input-root.d.ts +3 -1
- package/dist/date-input/root/date-input-root.js +18 -7
- package/dist/date-picker/root/date-picker-root.js +12 -6
- package/dist/dialog/description.js +15 -1
- package/dist/dialog/root/dialog-root.d.ts +8 -2
- package/dist/dialog/root/dialog-root.js +16 -1
- package/dist/divider/root/divider-root.d.ts +3 -0
- package/dist/divider/root/divider-root.js +2 -1
- package/dist/donut-chart/root/donut-chart-root.d.ts +31 -0
- package/dist/donut-chart/root/donut-chart-root.js +48 -1
- package/dist/drawer/reveal.d.ts +8 -0
- package/dist/drawer/reveal.js +1 -0
- package/dist/drawer/root/drawer-root.d.ts +59 -0
- package/dist/drawer/root/drawer-root.js +63 -4
- package/dist/drilldown/item/drilldown-item.js +1 -1
- package/dist/drilldown/root/drilldown-root.d.ts +21 -3
- package/dist/drilldown/root/drilldown-root.js +21 -3
- package/dist/element-events.generated.js +31 -5
- package/dist/empty/root/empty-root.d.ts +3 -1
- package/dist/empty/root/empty-root.js +3 -2
- package/dist/file-input/root/file-input-root.d.ts +69 -6
- package/dist/file-input/root/file-input-root.js +82 -19
- package/dist/file-input/screen.d.ts +2 -1
- package/dist/filter-input/menu/filter-menu.d.ts +2 -0
- package/dist/filter-input/menu/filter-menu.js +12 -4
- package/dist/filter-input/root/filter-input-root.d.ts +30 -6
- package/dist/filter-input/root/filter-input-root.js +1 -1
- package/dist/filter-input/suggest.d.ts +28 -3
- package/dist/filter-input/suggest.js +18 -8
- package/dist/format-date/root/format-date-root.d.ts +9 -16
- package/dist/format-date/root/format-date-root.js +10 -27
- package/dist/heatmap/root/heatmap-root.d.ts +24 -13
- package/dist/heatmap/root/heatmap-root.js +34 -12
- package/dist/icon/root/icon-root.d.ts +12 -1
- package/dist/icon/root/icon-root.js +21 -10
- package/dist/icon/settle.d.ts +7 -0
- package/dist/icon/settle.js +10 -0
- package/dist/icon-button/root/icon-button-root.d.ts +2 -0
- package/dist/image/root/image-root.d.ts +5 -1
- package/dist/image/root/image-root.js +1 -1
- package/dist/input/root/input-root.d.ts +12 -2
- package/dist/input/root/input-root.js +11 -2
- package/dist/kbd/root/kbd-root.d.ts +35 -1
- package/dist/kbd/root/kbd-root.js +23 -4
- package/dist/legend/root/legend-root.d.ts +55 -8
- package/dist/legend/root/legend-root.js +55 -13
- package/dist/lightbox/root/lightbox-root.d.ts +49 -6
- package/dist/lightbox/root/lightbox-root.js +86 -19
- package/dist/lightbox/zoom-controller.d.ts +59 -0
- package/dist/lightbox/zoom-controller.js +128 -0
- package/dist/lightbox/zoom.d.ts +22 -0
- package/dist/lightbox/zoom.js +16 -0
- package/dist/line-chart/root/line-chart-root.d.ts +22 -1
- package/dist/line-chart/root/line-chart-root.js +21 -1
- package/dist/list/item/list-item.d.ts +2 -0
- package/dist/list/item/list-item.js +11 -2
- package/dist/list/root/list-root.d.ts +62 -13
- package/dist/list/root/list-root.js +70 -13
- package/dist/list/selection.d.ts +37 -7
- package/dist/list/selection.js +16 -7
- package/dist/listbox/root/listbox-root.d.ts +37 -1
- package/dist/listbox/root/listbox-root.js +49 -3
- package/dist/loader/busy.d.ts +10 -0
- package/dist/loader/busy.js +11 -0
- package/dist/loader/root/loader-root.d.ts +9 -2
- package/dist/loader/root/loader-root.js +23 -3
- package/dist/megamenu/root/megamenu-root.d.ts +51 -10
- package/dist/megamenu/root/megamenu-root.js +76 -5
- package/dist/menu/item/menu-item.d.ts +51 -2
- package/dist/menu/item/menu-item.js +47 -23
- package/dist/menu/root/menu-root.d.ts +14 -0
- package/dist/menu/root/menu-root.js +2 -2
- package/dist/meter/root/meter-root.d.ts +20 -1
- package/dist/meter/root/meter-root.js +11 -15
- package/dist/navigation/root/navigation-root.d.ts +2 -1
- package/dist/navigation/root/navigation-root.js +2 -2
- package/dist/notifications/root/notifications-root.d.ts +10 -3
- package/dist/notifications/root/notifications-root.js +19 -7
- package/dist/number-input/root/number-input-root.d.ts +17 -8
- package/dist/number-input/root/number-input-root.js +2 -7
- package/dist/object-inspector/root/object-inspector-root.d.ts +75 -27
- package/dist/object-inspector/root/object-inspector-root.js +117 -76
- package/dist/object-inspector/rows.d.ts +18 -0
- package/dist/object-inspector/rows.js +33 -0
- package/dist/option/root/option-root.d.ts +5 -3
- package/dist/outline/root/outline-root.d.ts +18 -0
- package/dist/outline/root/outline-root.js +23 -5
- package/dist/pagination/size/pagination-size.d.ts +1 -0
- package/dist/pagination/size/pagination-size.js +1 -1
- package/dist/pin-input/code.d.ts +10 -0
- package/dist/pin-input/code.js +13 -0
- package/dist/pin-input/root/pin-input-root.d.ts +25 -1
- package/dist/pin-input/root/pin-input-root.js +10 -11
- package/dist/popover/hide-outside.d.ts +14 -0
- package/dist/popover/hide-outside.js +41 -0
- package/dist/popover/root/popover-root.d.ts +57 -7
- package/dist/popover/root/popover-root.js +58 -3
- package/dist/progress/root/progress-root.d.ts +20 -0
- package/dist/progress/root/progress-root.js +15 -15
- package/dist/qr-code/export.d.ts +26 -1
- package/dist/qr-code/export.js +14 -0
- package/dist/qr-code/root/qr-code-root.d.ts +19 -5
- package/dist/qr-code/root/qr-code-root.js +58 -9
- package/dist/quote/marks.d.ts +8 -0
- package/dist/quote/marks.js +5 -0
- package/dist/quote/root/quote-root.d.ts +14 -2
- package/dist/quote/root/quote-root.js +23 -1
- package/dist/radio/root/radio-root.d.ts +6 -2
- package/dist/radio/root/radio-root.js +6 -1
- package/dist/rating/root/rating-root.d.ts +9 -4
- package/dist/rating/root/rating-root.js +31 -7
- package/dist/rating/steps.d.ts +7 -0
- package/dist/rating/steps.js +9 -0
- package/dist/scatter-chart/fit.d.ts +15 -0
- package/dist/scatter-chart/fit.js +35 -0
- package/dist/scatter-chart/root/scatter-chart-root.d.ts +21 -1
- package/dist/scatter-chart/root/scatter-chart-root.js +30 -17
- package/dist/scroll-area/root/scroll-area-root.d.ts +22 -51
- package/dist/scroll-area/root/scroll-area-root.js +57 -173
- package/dist/scroll-area/scrollbar-controller.d.ts +111 -0
- package/dist/scroll-area/scrollbar-controller.js +172 -0
- package/dist/scroll-area/scrollbar.d.ts +27 -0
- package/dist/scroll-area/scrollbar.js +9 -0
- package/dist/select/root/select-root.d.ts +15 -0
- package/dist/select/root/select-root.js +18 -6
- package/dist/skeleton/root/skeleton-root.d.ts +3 -0
- package/dist/skeleton/root/skeleton-root.js +4 -2
- package/dist/skip-link/root/skip-link-root.js +2 -2
- package/dist/slider/root/slider-root.d.ts +41 -4
- package/dist/slider/root/slider-root.js +23 -3
- package/dist/sparkline/readings.d.ts +18 -0
- package/dist/sparkline/readings.js +19 -0
- package/dist/sparkline/root/sparkline-root.d.ts +78 -7
- package/dist/sparkline/root/sparkline-root.js +118 -23
- package/dist/speed-dial/root/speed-dial-root.js +2 -2
- package/dist/splitter/root/splitter-root.d.ts +7 -12
- package/dist/splitter/root/splitter-root.js +209 -204
- package/dist/stat/root/stat-root.d.ts +14 -5
- package/dist/stat/root/stat-root.js +21 -5
- package/dist/steps/reach.d.ts +5 -0
- package/dist/steps/reach.js +3 -0
- package/dist/steps/root/steps-root.d.ts +49 -3
- package/dist/steps/root/steps-root.js +44 -5
- package/dist/steps/step/step.d.ts +7 -0
- package/dist/steps/step/step.js +7 -2
- package/dist/switch/root/switch-root.d.ts +10 -0
- package/dist/table/column/table-column.d.ts +0 -10
- package/dist/table/column/table-column.js +3 -19
- package/dist/table/root/table-root.d.ts +31 -10
- package/dist/table/root/table-root.js +2 -13
- package/dist/tabs/panel/tab-panel.d.ts +16 -3
- package/dist/tabs/panel/tab-panel.js +34 -5
- package/dist/tabs/root/tabs-root.d.ts +12 -0
- package/dist/tabs/root/tabs-root.js +1 -1
- package/dist/tabs/tab/tab.d.ts +5 -0
- package/dist/tabs/tab/tab.js +5 -2
- package/dist/tag/group/tag-group.d.ts +31 -4
- package/dist/tag/group/tag-group.js +46 -7
- package/dist/tag/root/tag-root.js +9 -2
- package/dist/textarea/root/textarea-root.d.ts +28 -1
- package/dist/textarea/root/textarea-root.js +40 -1
- package/dist/time-input/root/time-input-root.d.ts +5 -1
- package/dist/time-input/root/time-input-root.js +16 -8
- package/dist/time-picker/root/time-picker-root.d.ts +18 -4
- package/dist/time-picker/root/time-picker-root.js +7 -10
- package/dist/time-picker/wheel-name.d.ts +9 -0
- package/dist/time-picker/wheel-name.js +15 -0
- package/dist/timeline/group/timeline-group.js +2 -2
- package/dist/toast/item/toast-item.d.ts +14 -1
- package/dist/toast/item/toast-item.js +22 -5
- package/dist/toast/root/toast-root.d.ts +6 -0
- package/dist/toast/root/toast-root.js +16 -1
- package/dist/toggle/group/toggle-group.d.ts +24 -3
- package/dist/toggle/group/toggle-group.js +34 -5
- package/dist/tooltip/root/tooltip-root.d.ts +13 -2
- package/dist/tooltip/root/tooltip-root.js +9 -1
- package/dist/tour/back.d.ts +10 -0
- package/dist/tour/back.js +8 -0
- package/dist/tour/root/tour-root.d.ts +30 -2
- package/dist/tour/root/tour-root.js +34 -8
- package/dist/tree/item/tree-item.d.ts +7 -3
- package/dist/tree/item/tree-item.js +3 -0
- package/dist/tree/root/tree-root.d.ts +24 -3
- package/dist/tree/root/tree-root.js +32 -13
- package/dist/tree/selection.js +2 -15
- package/dist/trend/root/trend-root.d.ts +43 -18
- package/dist/trend/root/trend-root.js +31 -6
- package/dist/video/media-events.d.ts +13 -18
- package/dist/video/media-events.js +1 -5
- package/dist/video/root/video-root.d.ts +18 -5
- package/dist/video/root/video-root.js +19 -10
- package/dist/video-playlist/menu/video-playlist-menu.d.ts +2 -0
- package/dist/video-playlist/menu/video-playlist-menu.js +2 -1
- package/dist/video-playlist/root/video-playlist-root.d.ts +7 -1
- package/dist/virtual-list/fill.d.ts +0 -8
- package/dist/virtual-list/fill.js +1 -17
- package/dist/virtual-list/plan.d.ts +49 -0
- package/dist/virtual-list/plan.js +16 -0
- package/dist/virtual-list/root/virtual-list-root.d.ts +114 -13
- package/dist/virtual-list/root/virtual-list-root.js +183 -40
- package/lint.json +1 -1
- package/package.json +8 -8
- package/types/jsx.d.ts +4577 -835
- package/types/vue.d.ts +926 -168
- package/dist/chat/relative-clock.d.ts +0 -39
- package/dist/chat/relative-clock.js +0 -31
- package/dist/data-table/root/order.d.ts +0 -141
- package/dist/data-table/root/order.js +0 -76
- package/dist/tag/selection.d.ts +0 -10
- package/dist/tag/selection.js +0 -12
|
@@ -18,6 +18,10 @@ declare const HelixHeadlessAccordionItem_base: (new (...args: any[]) => import("
|
|
|
18
18
|
* out of the tab order and the accessibility tree after the styled tier's
|
|
19
19
|
* `display` overrides `hidden`.
|
|
20
20
|
*
|
|
21
|
+
* While it is open, the content part carries the content's height as
|
|
22
|
+
* `--accordion-content-height`, as Radix's accordion does, for a stylesheet
|
|
23
|
+
* that animates the section open with keyframes of its own.
|
|
24
|
+
*
|
|
21
25
|
* Set `hidden-until-found` and the browser's find in page searches a closed
|
|
22
26
|
* section too: a match opens it, with `helix-before-open` first, as React
|
|
23
27
|
* Aria's disclosure does. The content is then `hidden="until-found"` while
|
|
@@ -43,6 +47,8 @@ declare const HelixHeadlessAccordionItem_base: (new (...args: any[]) => import("
|
|
|
43
47
|
* @csspart heading - The heading that holds the trigger.
|
|
44
48
|
* @csspart trigger - The button that opens and closes the section, holding the summary.
|
|
45
49
|
*
|
|
50
|
+
* @cssproperty --accordion-content-height - The height of the content, in pixels, written on the `content` part while the section is open.
|
|
51
|
+
*
|
|
46
52
|
* @cssstate open - While the item is open.
|
|
47
53
|
*/
|
|
48
54
|
export declare class HelixHeadlessAccordionItem extends HelixHeadlessAccordionItem_base {
|
|
@@ -166,6 +172,12 @@ export declare class HelixHeadlessAccordionItem extends HelixHeadlessAccordionIt
|
|
|
166
172
|
* settle, once every item is there to compare.
|
|
167
173
|
*/
|
|
168
174
|
protected updated(changed: PropertyValues): void;
|
|
175
|
+
/**
|
|
176
|
+
* Writes the content's height onto the content part while the section is
|
|
177
|
+
* open. `scrollHeight`, so a sheet that clips the box while it grows still
|
|
178
|
+
* reads the whole height.
|
|
179
|
+
*/
|
|
180
|
+
private readonly measure;
|
|
169
181
|
/**
|
|
170
182
|
* Moves focus to this item's trigger.
|
|
171
183
|
*
|
|
@@ -203,6 +215,8 @@ export declare class HelixHeadlessAccordionItem extends HelixHeadlessAccordionIt
|
|
|
203
215
|
* overrides `display`, `inert` is the only one of the two still in force.
|
|
204
216
|
* `content-visibility: hidden` keeps skipped content out of the tab order and
|
|
205
217
|
* the accessibility tree by itself, and an inert region is never searched.
|
|
218
|
+
* It leaves the region's own box in the tree, so a closed findable section
|
|
219
|
+
* drops `role="region"` and its name until it opens.
|
|
206
220
|
*/
|
|
207
221
|
protected render(): TemplateResult;
|
|
208
222
|
}
|
|
@@ -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 { closestInstance, customElement, HelixElement, MixinControllerOpen, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { closestInstance, customElement, HelixElement, headingLevel, MixinControllerOpen, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { caretDown } from '@fusion.dev/helix-icons';
|
|
9
9
|
import { html, nothing } from 'lit';
|
|
10
10
|
import { property } from 'lit/decorators.js';
|
|
@@ -43,12 +43,20 @@ let HelixHeadlessAccordionItem = class HelixHeadlessAccordionItem extends MixinC
|
|
|
43
43
|
this.closePeers();
|
|
44
44
|
}
|
|
45
45
|
};
|
|
46
|
+
this.measure = () => {
|
|
47
|
+
const content = this.partNamed('content');
|
|
48
|
+
const inner = this.partNamed('content-inner');
|
|
49
|
+
if (!content || !inner || !this.open) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
content.style.setProperty('--accordion-content-height', `${inner.scrollHeight}px`);
|
|
53
|
+
};
|
|
46
54
|
}
|
|
47
55
|
renderChevron() {
|
|
48
56
|
if (this.chevronPlacement === 'none') {
|
|
49
57
|
return nothing;
|
|
50
58
|
}
|
|
51
|
-
return html `<slot name="chevron"
|
|
59
|
+
return html `<slot name="chevron" aria-hidden="true"
|
|
52
60
|
><svg part="chevron" fill="currentColor" viewBox=${caretDown.viewBox} aria-hidden="true">
|
|
53
61
|
${unsafeSVG(caretDown.content)}
|
|
54
62
|
</svg></slot
|
|
@@ -89,13 +97,15 @@ let HelixHeadlessAccordionItem = class HelixHeadlessAccordionItem extends MixinC
|
|
|
89
97
|
this.closePeers();
|
|
90
98
|
}
|
|
91
99
|
this.wasOpen = this.open;
|
|
100
|
+
this.measure();
|
|
92
101
|
}
|
|
93
102
|
focusTrigger() {
|
|
94
103
|
this.renderRoot.querySelector(`#${TRIGGER_ID}`)?.focus();
|
|
95
104
|
}
|
|
96
105
|
render() {
|
|
97
106
|
const open = this.open;
|
|
98
|
-
const
|
|
107
|
+
const landmark = open || !this.findable;
|
|
108
|
+
const level = headingLevel(this.headingLevel ?? this.groupHeadingLevel);
|
|
99
109
|
return html `
|
|
100
110
|
<div part="container">
|
|
101
111
|
<div part="heading" role="heading" aria-level=${level}>
|
|
@@ -115,15 +125,15 @@ let HelixHeadlessAccordionItem = class HelixHeadlessAccordionItem extends MixinC
|
|
|
115
125
|
</div>
|
|
116
126
|
<div
|
|
117
127
|
id=${CONTENT_ID}
|
|
118
|
-
aria-labelledby=${TRIGGER_ID}
|
|
128
|
+
aria-labelledby=${landmark ? TRIGGER_ID : nothing}
|
|
119
129
|
part="content"
|
|
120
130
|
hidden=${open ? nothing : this.findable ? 'until-found' : ''}
|
|
121
131
|
?inert=${!open && !this.findable}
|
|
122
|
-
role
|
|
132
|
+
role=${landmark ? 'region' : nothing}
|
|
123
133
|
@beforematch=${this.handleBeforeMatch}
|
|
124
134
|
>
|
|
125
135
|
<div part="content-inner">
|
|
126
|
-
<slot></slot>
|
|
136
|
+
<slot @slotchange=${this.measure}></slot>
|
|
127
137
|
</div>
|
|
128
138
|
</div>
|
|
129
139
|
</div>
|
|
@@ -17,12 +17,15 @@ import { HelixHeadlessAccordionItem } from '../item/accordion-item.js';
|
|
|
17
17
|
* matched with `instanceof HelixHeadlessAccordionItem`, so the headless tier
|
|
18
18
|
* does not depend on the styled tier's tag names.
|
|
19
19
|
*
|
|
20
|
-
*
|
|
21
|
-
* so
|
|
20
|
+
* Keys, on a trigger: Up and Down Arrow move to the previous and the next
|
|
21
|
+
* trigger, and so do Left and Right, wrapping at the ends; Home and End go to
|
|
22
|
+
* the first and the last. Enter and Space open and close the section. Disabled
|
|
23
|
+
* sections are passed over.
|
|
22
24
|
*
|
|
23
|
-
* Each trigger
|
|
24
|
-
*
|
|
25
|
-
*
|
|
25
|
+
* Each trigger is a button with `aria-expanded` and `aria-controls`, inside a
|
|
26
|
+
* heading, level 3 unless `heading-level` says otherwise, so the sections are
|
|
27
|
+
* in a screen reader's list of headings. An open section is a region named by
|
|
28
|
+
* its trigger. Set the level that fits the page's outline.
|
|
26
29
|
*
|
|
27
30
|
* @since 0.1.0
|
|
28
31
|
* @status stable
|
|
@@ -16,8 +16,9 @@ declare const HelixHeadlessAnnouncement_base: (new (...args: any[]) => import("@
|
|
|
16
16
|
*
|
|
17
17
|
* The live region is its own part. When the announcement appears after the page
|
|
18
18
|
* has loaded, the message is written into the region one frame later, so screen
|
|
19
|
-
* readers read it reliably, and the region is emptied seven seconds later
|
|
20
|
-
* the
|
|
19
|
+
* readers read it reliably, and the region is emptied seven seconds later.
|
|
20
|
+
* While the region holds the copy, the message itself is hidden from screen
|
|
21
|
+
* readers, so it is in the page once. When the announcement is in the served
|
|
21
22
|
* HTML, the region stays empty and nothing is read out.
|
|
22
23
|
*
|
|
23
24
|
* The dismiss button fires `helix-dismiss`, and the announcement stays in the
|
|
@@ -64,7 +64,9 @@ let HelixHeadlessAnnouncement = class HelixHeadlessAnnouncement extends MixinDis
|
|
|
64
64
|
</slot>`;
|
|
65
65
|
}
|
|
66
66
|
render() {
|
|
67
|
-
return html `<div
|
|
67
|
+
return html `<div aria-hidden=${this.arrival.said === '' ? nothing : 'true'} part="content"
|
|
68
|
+
><slot @slotchange=${() => this.arrival.say()}></slot
|
|
69
|
+
></div>
|
|
68
70
|
<span part="live" role="status">${this.arrival.said}</span>
|
|
69
71
|
<div part="actions"><slot name="actions" @slotchange=${() => this.markEmpty()}></slot></div>
|
|
70
72
|
${this.dismissible
|
|
@@ -22,8 +22,11 @@ import { type PropertyValues, type TemplateResult } from 'lit';
|
|
|
22
22
|
* The host is the page's `banner` landmark. Placed inside `main`, `article`,
|
|
23
23
|
* `aside`, `nav` or `section`, it is a plain box instead, as a native
|
|
24
24
|
* `<header>` is there, so a header inside a card or a pane does not add a
|
|
25
|
-
* second banner.
|
|
26
|
-
* the
|
|
25
|
+
* second banner. The ancestors are read as the accessibility tree reads them,
|
|
26
|
+
* through the slots that take the header in, so one slotted into a component
|
|
27
|
+
* that draws a `<nav>`, such as `Navigation`, is not a banner either. A server
|
|
28
|
+
* cannot see the ancestors, so it serves the banner and the browser corrects
|
|
29
|
+
* it when the element connects.
|
|
27
30
|
*
|
|
28
31
|
* @since 0.1.0
|
|
29
32
|
* @status stable
|
|
@@ -105,6 +108,8 @@ export declare class HelixHeadlessAppHeader extends HelixElement {
|
|
|
105
108
|
private readonly handleScroll;
|
|
106
109
|
protected willUpdate(changed: PropertyValues): void;
|
|
107
110
|
connectedCallback(): void;
|
|
111
|
+
/** Whether this header is the page's banner, read from where it sits. */
|
|
112
|
+
private readLandmark;
|
|
108
113
|
disconnectedCallback(): void;
|
|
109
114
|
protected updated(changed: PropertyValues): void;
|
|
110
115
|
protected render(): TemplateResult;
|
|
@@ -47,9 +47,17 @@ let HelixHeadlessAppHeader = class HelixHeadlessAppHeader extends HelixElement {
|
|
|
47
47
|
}
|
|
48
48
|
connectedCallback() {
|
|
49
49
|
super.connectedCallback();
|
|
50
|
+
this.readLandmark();
|
|
51
|
+
setTimeout(() => {
|
|
52
|
+
if (this.isConnected) {
|
|
53
|
+
this.readLandmark();
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
this.listen();
|
|
57
|
+
}
|
|
58
|
+
readLandmark() {
|
|
50
59
|
this.landmark = inSectioningContent(this) ? '' : 'banner';
|
|
51
60
|
hostAria(this.internals, { role: this.landmark });
|
|
52
|
-
this.listen();
|
|
53
61
|
}
|
|
54
62
|
disconnectedCallback() {
|
|
55
63
|
super.disconnectedCallback();
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Whether an element sits inside sectioning content, read across shadow
|
|
3
|
-
* boundaries as the accessibility tree reads it
|
|
3
|
+
* boundaries as the accessibility tree reads it: through the slot an element
|
|
4
|
+
* is assigned to, so a header slotted into a component that draws a `<nav>`
|
|
5
|
+
* around its slot, as `Navigation` does, is inside that `<nav>`.
|
|
4
6
|
*
|
|
5
7
|
* Client only: a server has no ancestors to read.
|
|
6
8
|
*/
|
|
@@ -11,15 +11,16 @@ const SECTIONING = [
|
|
|
11
11
|
'[role="region"]',
|
|
12
12
|
].join(', ');
|
|
13
13
|
export const inSectioningContent = (element) => {
|
|
14
|
-
let node =
|
|
14
|
+
let node = above(element);
|
|
15
15
|
while (node) {
|
|
16
16
|
if (node.matches(SECTIONING)) {
|
|
17
17
|
return true;
|
|
18
18
|
}
|
|
19
|
-
node =
|
|
19
|
+
node = above(node);
|
|
20
20
|
}
|
|
21
21
|
return false;
|
|
22
22
|
};
|
|
23
|
+
const above = (element) => element.assignedSlot ?? element.parentElement ?? hostOf(element);
|
|
23
24
|
const hostOf = (element) => {
|
|
24
25
|
const root = element.getRootNode();
|
|
25
26
|
return root instanceof ShadowRoot ? root.host : null;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { HelixElement } from '@fusion.dev/helix-core';
|
|
2
2
|
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
3
3
|
/**
|
|
4
|
-
* The largest `max` a stylesheet
|
|
4
|
+
* The largest `max` a stylesheet folds a server's markup at.
|
|
5
5
|
*
|
|
6
|
-
* The
|
|
6
|
+
* The rule is `:host([max="N"]) ::slotted(:nth-child(n + N+1))`, one selector
|
|
7
7
|
* per value, because `An+B` takes a literal and no custom property can be read
|
|
8
|
-
* into it. So the range
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* into it. So the range a stylesheet covers is finite. The group folds any
|
|
9
|
+
* `max` itself once it runs, with the platform's `hidden`; past this one, a
|
|
10
|
+
* server's markup shows every avatar until the page hydrates.
|
|
11
11
|
*/
|
|
12
12
|
export declare const FOLDS_UP_TO = 12;
|
|
13
13
|
/**
|
|
@@ -21,9 +21,10 @@ export declare const FOLDS_UP_TO = 12;
|
|
|
21
21
|
* hidden.
|
|
22
22
|
*
|
|
23
23
|
* Avatars past `max` stay in the DOM and are hidden. The group writes `hidden`
|
|
24
|
-
* on each of them, so they are hidden on the headless tier as well, and
|
|
25
|
-
* count is read out as `overflow-label`, such as "3 more
|
|
26
|
-
*
|
|
24
|
+
* on each of them, so they are hidden on the headless tier as well, and at any
|
|
25
|
+
* `max`. The count is read out as `overflow-label`, such as "3 more: Grace
|
|
26
|
+
* Hopper, Alan Turing, Barbara Liskov", so a screen reader hears who is there
|
|
27
|
+
* past the ones it can reach.
|
|
27
28
|
*
|
|
28
29
|
* @since 0.1.0
|
|
29
30
|
* @status stable
|
|
@@ -59,10 +60,11 @@ export declare class HelixHeadlessAvatarGroup extends HelixElement {
|
|
|
59
60
|
*/
|
|
60
61
|
max?: number;
|
|
61
62
|
/**
|
|
62
|
-
* What a screen reader is told the count is, with `{count}` for the number
|
|
63
|
+
* What a screen reader is told the count is, with `{count}` for the number
|
|
64
|
+
* and `{names}` for the people it stands for, by each avatar's `name`.
|
|
63
65
|
*
|
|
64
66
|
* The count shows "+3", which reads as "plus three" and says nothing about
|
|
65
|
-
* what was counted.
|
|
67
|
+
* what was counted, nor who.
|
|
66
68
|
*/
|
|
67
69
|
overflowLabel: string;
|
|
68
70
|
/** The avatars this group hid, so it unhides only those. */
|
|
@@ -78,8 +80,6 @@ export declare class HelixHeadlessAvatarGroup extends HelixElement {
|
|
|
78
80
|
* the attribute that hides the whole group.
|
|
79
81
|
*/
|
|
80
82
|
get overflow(): number;
|
|
81
|
-
/** Said once per element, because a re-render is not a second mistake. */
|
|
82
|
-
private warned;
|
|
83
83
|
protected willUpdate(changed: PropertyValues): void;
|
|
84
84
|
protected firstUpdated(changed: PropertyValues): void;
|
|
85
85
|
protected updated(changed: PropertyValues): void;
|
|
@@ -89,10 +89,16 @@ export declare class HelixHeadlessAvatarGroup extends HelixElement {
|
|
|
89
89
|
* **State in markup, so the headless tier folds too.** The fold was a styled
|
|
90
90
|
* rule alone, and on the headless tier every avatar was drawn beside a "+3".
|
|
91
91
|
* The styled rule stays, because it is what folds a server's markup before
|
|
92
|
-
* this runs
|
|
93
|
-
*
|
|
92
|
+
* this runs, up to `FOLDS_UP_TO`, and this folds at any `max`. Written
|
|
93
|
+
* through `whenHydrated`, so an avatar's hydrating render is the server's,
|
|
94
|
+
* and undone only on the avatars this group hid.
|
|
94
95
|
*/
|
|
95
96
|
private fold;
|
|
97
|
+
/**
|
|
98
|
+
* The names of the people past `max`, by each avatar's `name`, then its
|
|
99
|
+
* `aria-label`, in the order they are read.
|
|
100
|
+
*/
|
|
101
|
+
private foldedNames;
|
|
96
102
|
protected render(): TemplateResult;
|
|
97
103
|
}
|
|
98
104
|
declare global {
|
|
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
import { customElement, HelixElement, hostAria,
|
|
7
|
+
import { customElement, fillTemplate, HelixElement, hostAria, whenHydrated, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
10
|
export const FOLDS_UP_TO = 12;
|
|
@@ -12,17 +12,15 @@ let HelixHeadlessAvatarGroup = class HelixHeadlessAvatarGroup extends HelixEleme
|
|
|
12
12
|
constructor() {
|
|
13
13
|
super(...arguments);
|
|
14
14
|
this.label = '';
|
|
15
|
-
this.overflowLabel = '{count} more';
|
|
15
|
+
this.overflowLabel = '{count} more: {names}';
|
|
16
16
|
this.folded = new WeakSet();
|
|
17
|
-
this.warned = false;
|
|
18
17
|
}
|
|
19
18
|
get avatars() {
|
|
20
19
|
return this.knownChildren.filter((child) => child instanceof HTMLElement);
|
|
21
20
|
}
|
|
22
21
|
get overflow() {
|
|
23
22
|
const limit = this.max ?? 0;
|
|
24
|
-
|
|
25
|
-
return limited ? Math.max(this.avatars.length - limit, 0) : 0;
|
|
23
|
+
return limit > 0 ? Math.max(this.avatars.length - limit, 0) : 0;
|
|
26
24
|
}
|
|
27
25
|
willUpdate(changed) {
|
|
28
26
|
super.willUpdate(changed);
|
|
@@ -35,23 +33,11 @@ let HelixHeadlessAvatarGroup = class HelixHeadlessAvatarGroup extends HelixEleme
|
|
|
35
33
|
updated(changed) {
|
|
36
34
|
super.updated(changed);
|
|
37
35
|
this.fold();
|
|
38
|
-
const limit = this.max ?? 0;
|
|
39
|
-
if (limit > FOLDS_UP_TO && this.avatars.length > limit && !this.warned) {
|
|
40
|
-
this.warned = true;
|
|
41
|
-
reportRuntime({
|
|
42
|
-
kind: 'max-past-fold',
|
|
43
|
-
message: `<${this.localName}> has max="${this.max}", past the ${FOLDS_UP_TO} the fold is written for, ` +
|
|
44
|
-
'so every avatar is shown and none are counted. Lower `max`, or hand the group the ' +
|
|
45
|
-
'avatars you want shown.',
|
|
46
|
-
tag: this.localName,
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
36
|
}
|
|
50
37
|
fold() {
|
|
51
38
|
const limit = this.max ?? 0;
|
|
52
|
-
const limited = limit > 0 && limit <= FOLDS_UP_TO;
|
|
53
39
|
for (const [index, avatar] of this.avatars.entries()) {
|
|
54
|
-
const past =
|
|
40
|
+
const past = limit > 0 && index >= limit;
|
|
55
41
|
if (past && !avatar.hidden) {
|
|
56
42
|
this.folded.add(avatar);
|
|
57
43
|
whenHydrated(avatar, () => {
|
|
@@ -66,6 +52,15 @@ let HelixHeadlessAvatarGroup = class HelixHeadlessAvatarGroup extends HelixEleme
|
|
|
66
52
|
}
|
|
67
53
|
}
|
|
68
54
|
}
|
|
55
|
+
foldedNames() {
|
|
56
|
+
const limit = this.max ?? 0;
|
|
57
|
+
return this.avatars
|
|
58
|
+
.slice(limit)
|
|
59
|
+
.map((avatar) => avatar.name ||
|
|
60
|
+
avatar.getAttribute('aria-label') ||
|
|
61
|
+
'')
|
|
62
|
+
.filter(Boolean);
|
|
63
|
+
}
|
|
69
64
|
render() {
|
|
70
65
|
const past = this.overflow;
|
|
71
66
|
return html `<slot @slotchange=${() => this.requestUpdate()}></slot>
|
|
@@ -73,7 +68,10 @@ let HelixHeadlessAvatarGroup = class HelixHeadlessAvatarGroup extends HelixEleme
|
|
|
73
68
|
? html `<span
|
|
74
69
|
part="overflow"
|
|
75
70
|
role="img"
|
|
76
|
-
aria-label=${this.overflowLabel
|
|
71
|
+
aria-label=${fillTemplate(this.overflowLabel, {
|
|
72
|
+
count: String(past),
|
|
73
|
+
names: this.foldedNames().join(', '),
|
|
74
|
+
}).replace(/[\s:,]+$/, '')}
|
|
77
75
|
>+${past}</span
|
|
78
76
|
>`
|
|
79
77
|
: ''}`;
|
|
@@ -44,7 +44,9 @@ export declare class HelixHeadlessBadge extends HelixElement {
|
|
|
44
44
|
/**
|
|
45
45
|
* The badge's name, read by screen readers.
|
|
46
46
|
*
|
|
47
|
-
* Required for a dot, which has no text to read.
|
|
47
|
+
* Required for a dot, which has no text to read. `{count}` is replaced with
|
|
48
|
+
* what the badge shows, so `label="{count} unread"` reads "8 unread" and
|
|
49
|
+
* stays current as `value` changes, with no label of yours to keep in step.
|
|
48
50
|
*/
|
|
49
51
|
label: string;
|
|
50
52
|
/**
|
|
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
import { customElement, HelixElement } from '@fusion.dev/helix-core';
|
|
7
|
+
import { customElement, fillTemplate, HelixElement } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html, nothing } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
10
|
let HelixHeadlessBadge = class HelixHeadlessBadge extends HelixElement {
|
|
@@ -36,7 +36,7 @@ let HelixHeadlessBadge = class HelixHeadlessBadge extends HelixElement {
|
|
|
36
36
|
data-blank=${blank ? '' : nothing}
|
|
37
37
|
data-single=${text.length <= 1 ? '' : nothing}
|
|
38
38
|
role="status"
|
|
39
|
-
aria-label=${announced ? this.label || text : nothing}
|
|
39
|
+
aria-label=${announced ? fillTemplate(this.label, { count: text }) || text : nothing}
|
|
40
40
|
>${blank ? html `<span aria-hidden="true">${text}</span>` : text}</span
|
|
41
41
|
>`;
|
|
42
42
|
}
|
|
@@ -176,6 +176,17 @@ export declare class HelixHeadlessBarChart extends HelixHeadlessBarChart_base {
|
|
|
176
176
|
* of any scale question the picture raises.
|
|
177
177
|
*/
|
|
178
178
|
secondary?: string;
|
|
179
|
+
/**
|
|
180
|
+
* Stacks a category's columns into one, each series standing on the ones
|
|
181
|
+
* before it, so the column's height is the category's total.
|
|
182
|
+
*
|
|
183
|
+
* Absent stands them side by side, as it always did. A negative reading
|
|
184
|
+
* hangs below zero on the negatives before it, so neither direction hides
|
|
185
|
+
* the other. The scale fits the stacks. A series
|
|
186
|
+
* in `lines` is still a line, measured on its own reading; columns on the
|
|
187
|
+
* second scale stack among themselves.
|
|
188
|
+
*/
|
|
189
|
+
stacked: boolean;
|
|
179
190
|
/** The series names on the second scale, lowercased and trimmed. */
|
|
180
191
|
private get scaled();
|
|
181
192
|
/** The series names drawn as a line, lowercased and trimmed for matching. */
|
|
@@ -204,7 +215,8 @@ export declare class HelixHeadlessBarChart extends HelixHeadlessBarChart_base {
|
|
|
204
215
|
*
|
|
205
216
|
* The highest, because which category leads is the question a bar chart is
|
|
206
217
|
* read for, and the table beside it has every other number. The reading is
|
|
207
|
-
* written the way the table writes it, such as `$128k` for `128000`.
|
|
218
|
+
* written the way the table writes it, such as `$128k` for `128000`. A
|
|
219
|
+
* series a legend hid is not drawn and is not said.
|
|
208
220
|
*/
|
|
209
221
|
protected renderSummary(series: Series[]): TemplateResult;
|
|
210
222
|
/**
|
|
@@ -214,6 +226,11 @@ export declare class HelixHeadlessBarChart extends HelixHeadlessBarChart_base {
|
|
|
214
226
|
* and a screen reader hearing both hears the same numbers twice.
|
|
215
227
|
*/
|
|
216
228
|
protected renderPlot(series: Series[], categories: string[], y: (value: number) => number, domain: [number, number], floor: number, z?: (value: number) => number): TemplateResult;
|
|
229
|
+
/**
|
|
230
|
+
* One category's columns stacked into one, each on the ones before it on
|
|
231
|
+
* its own scale, through the pure `stackSpans`.
|
|
232
|
+
*/
|
|
233
|
+
private renderStack;
|
|
217
234
|
/**
|
|
218
235
|
* The numbers along one scale, drawn only where there are two of them.
|
|
219
236
|
*
|
|
@@ -7,11 +7,13 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
import { byRow, CHART_PLOT, customElement, drawTooltip, extent, formatNumbers, HelixElement, linear, linePath, MixinChartSource, PLOT_HEIGHT, PLOT_WIDTH, seriesIndex, seriesNames, ticks, unbroken, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html, nothing, svg } from 'lit';
|
|
9
9
|
import { property } from 'lit/decorators.js';
|
|
10
|
+
import { stackEnds, stackSpans } from '../stack.js';
|
|
10
11
|
import '../../tooltip/index.js';
|
|
11
12
|
let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource(HelixElement) {
|
|
12
13
|
constructor() {
|
|
13
14
|
super(...arguments);
|
|
14
15
|
this.highestLabel = 'Highest';
|
|
16
|
+
this.stacked = false;
|
|
15
17
|
}
|
|
16
18
|
get scaled() {
|
|
17
19
|
return new Set(seriesNames(this.secondary));
|
|
@@ -39,8 +41,22 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource
|
|
|
39
41
|
const own = (one) => scaled.has(one.name.trim().toLowerCase());
|
|
40
42
|
const shown = series.filter((one) => this.showsSeries(one.name));
|
|
41
43
|
const values = shown.flatMap((one) => one.points.map((point) => point.value));
|
|
44
|
+
const lined = this.lined;
|
|
45
|
+
const readingsOf = (group) => {
|
|
46
|
+
if (!this.stacked) {
|
|
47
|
+
return group.flatMap((one) => one.points.map((point) => point.value));
|
|
48
|
+
}
|
|
49
|
+
const columns = group.filter((one) => !lined.has(one.name.trim().toLowerCase()));
|
|
50
|
+
const slots = columns.map((one) => byRow(one, categories.length));
|
|
51
|
+
return [
|
|
52
|
+
...group
|
|
53
|
+
.filter((one) => lined.has(one.name.trim().toLowerCase()))
|
|
54
|
+
.flatMap((one) => one.points.map((point) => point.value)),
|
|
55
|
+
...stackEnds(categories.map((_, at) => slots.map((one) => one[at]?.value))),
|
|
56
|
+
];
|
|
57
|
+
};
|
|
42
58
|
const on = (which) => {
|
|
43
|
-
const found = shown.filter(which)
|
|
59
|
+
const found = readingsOf(shown.filter(which));
|
|
44
60
|
return found.length > 0 ? found : values.length > 0 ? values : [0];
|
|
45
61
|
};
|
|
46
62
|
const [low, high] = extent(scaled.size === 0 ? on(() => true) : on((one) => !own(one)));
|
|
@@ -66,7 +82,7 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource
|
|
|
66
82
|
const columns = this.plottedColumns;
|
|
67
83
|
const highest = series.flatMap((one, depth) => {
|
|
68
84
|
const top = one.points.reduce((best, point) => (best === undefined || point.value > best.value ? point : best), undefined);
|
|
69
|
-
if (top === undefined) {
|
|
85
|
+
if (top === undefined || !this.showsSeries(one.name)) {
|
|
70
86
|
return [];
|
|
71
87
|
}
|
|
72
88
|
const text = rows[top.at]?.cells[columns[depth] ?? -1]?.text ?? String(top.value);
|
|
@@ -85,13 +101,15 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource
|
|
|
85
101
|
const columns = series
|
|
86
102
|
.map((one, index) => ({ one, slots: byRow(one, categories.length), tone: index }))
|
|
87
103
|
.filter(({ one }) => !lined.has(one.name.trim().toLowerCase()) && this.showsSeries(one.name));
|
|
88
|
-
const
|
|
89
|
-
const
|
|
104
|
+
const abreast = this.stacked ? 1 : columns.length;
|
|
105
|
+
const stride = (slot * 0.72) / Math.max(abreast, 1);
|
|
106
|
+
const gap = abreast > 1 ? stride * 0.08 : 0;
|
|
90
107
|
const thickness = stride - gap;
|
|
91
|
-
const bar = (value, index, depth, tone, scale) => {
|
|
108
|
+
const bar = (value, index, depth, tone, scale, from) => {
|
|
92
109
|
const at = scale(value);
|
|
93
|
-
const
|
|
94
|
-
const
|
|
110
|
+
const base = from === undefined ? floor : scale(from);
|
|
111
|
+
const along = Math.min(base, at);
|
|
112
|
+
const size = Math.abs(base - at);
|
|
95
113
|
const across = index * slot + slot * 0.14 + depth * stride + gap / 2;
|
|
96
114
|
return sideways
|
|
97
115
|
? svg `<rect
|
|
@@ -151,12 +169,14 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource
|
|
|
151
169
|
data-active=${this.pointer.at === index ? '' : nothing}
|
|
152
170
|
part="group${this.pointer.at === index ? ' group-active' : ''}"
|
|
153
171
|
>
|
|
154
|
-
${
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
172
|
+
${this.stacked
|
|
173
|
+
? this.renderStack(columns, index, against, bar)
|
|
174
|
+
: columns.map(({ slots, one, tone }, depth) => {
|
|
175
|
+
const point = slots[index];
|
|
176
|
+
return point === undefined
|
|
177
|
+
? nothing
|
|
178
|
+
: bar(point.value, index, depth, tone, against(one));
|
|
179
|
+
})}
|
|
160
180
|
</g>`)}
|
|
161
181
|
${series.map((one) => lined.has(one.name.trim().toLowerCase()) && this.showsSeries(one.name)
|
|
162
182
|
? this.renderLine(one, series.indexOf(one), slot, against(one), sideways)
|
|
@@ -165,6 +185,17 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource
|
|
|
165
185
|
${this.renderTooltip()}
|
|
166
186
|
</div>`;
|
|
167
187
|
}
|
|
188
|
+
renderStack(columns, index, against, bar) {
|
|
189
|
+
const scales = [...new Set(columns.map(({ one }) => against(one)))];
|
|
190
|
+
return scales.flatMap((scale) => {
|
|
191
|
+
const mine = columns.filter(({ one }) => against(one) === scale);
|
|
192
|
+
const spans = stackSpans(mine.map(({ slots }) => slots[index]?.value));
|
|
193
|
+
return mine.flatMap(({ tone }, place) => {
|
|
194
|
+
const span = spans[place];
|
|
195
|
+
return span === undefined ? [] : [bar(span[1], index, 0, tone, scale, span[0])];
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
}
|
|
168
199
|
renderAxis(scale, domain, which, sideways, widest, tint) {
|
|
169
200
|
const sizing = sideways ? undefined : `--bar-chart-axis: ${widest}ch`;
|
|
170
201
|
return html `<div
|
|
@@ -234,10 +265,13 @@ let HelixHeadlessBarChart = class HelixHeadlessBarChart extends MixinChartSource
|
|
|
234
265
|
return drawTooltip(this.sourceRows, at, this.tag('tooltip'), undefined, undefined, this.pointer);
|
|
235
266
|
}
|
|
236
267
|
const shown = series.filter((one) => this.showsSeries(one.name));
|
|
237
|
-
const
|
|
268
|
+
const reached = shown
|
|
238
269
|
.map((one) => byRow(one, this.readings)[at]?.value)
|
|
239
270
|
.filter((one) => one !== undefined);
|
|
240
|
-
const
|
|
271
|
+
const values = this.stacked ? stackEnds([reached]) : reached;
|
|
272
|
+
const all = this.stacked
|
|
273
|
+
? stackEnds(Array.from({ length: this.readings }, (_, row) => shown.map((one) => byRow(one, this.readings)[row]?.value)))
|
|
274
|
+
: shown.flatMap((one) => one.points.map((point) => point.value));
|
|
241
275
|
const [low, high] = extent(all.length > 0 ? all : [0]);
|
|
242
276
|
const domain = [Math.min(0, low), Math.max(0, high)];
|
|
243
277
|
const sideways = this.orientation === 'horizontal';
|
|
@@ -268,6 +302,9 @@ __decorate([
|
|
|
268
302
|
__decorate([
|
|
269
303
|
property({ reflect: true })
|
|
270
304
|
], HelixHeadlessBarChart.prototype, "secondary", void 0);
|
|
305
|
+
__decorate([
|
|
306
|
+
property({ reflect: true, type: Boolean })
|
|
307
|
+
], HelixHeadlessBarChart.prototype, "stacked", void 0);
|
|
271
308
|
HelixHeadlessBarChart = __decorate([
|
|
272
309
|
customElement('helix-headless-bar-chart')
|
|
273
310
|
], HelixHeadlessBarChart);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Where a column starts and ends along the measure, in the data's own units. */
|
|
2
|
+
export type Span = readonly [from: number, to: number];
|
|
3
|
+
/**
|
|
4
|
+
* Each column's span in one category, in the order given: a positive reading
|
|
5
|
+
* stacks up from zero on the positives before it, and a negative one down
|
|
6
|
+
* from zero on the negatives before it, so neither hides the other. A blank
|
|
7
|
+
* has no span.
|
|
8
|
+
*/
|
|
9
|
+
export declare const stackSpans: (values: readonly (number | undefined)[]) => (Span | undefined)[];
|
|
10
|
+
/**
|
|
11
|
+
* Every end a stack reaches across the categories, which is what its scale
|
|
12
|
+
* has to hold: the top of each stack of positives and the foot of each stack
|
|
13
|
+
* of negatives. `categories` holds each category's readings, one per column.
|
|
14
|
+
*/
|
|
15
|
+
export declare const stackEnds: (categories: readonly (readonly (number | undefined)[])[]) => number[];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const stackSpans = (values) => {
|
|
2
|
+
let up = 0;
|
|
3
|
+
let down = 0;
|
|
4
|
+
return values.map((value) => {
|
|
5
|
+
if (value === undefined) {
|
|
6
|
+
return undefined;
|
|
7
|
+
}
|
|
8
|
+
const from = value >= 0 ? up : down;
|
|
9
|
+
const to = from + value;
|
|
10
|
+
if (value >= 0) {
|
|
11
|
+
up = to;
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
down = to;
|
|
15
|
+
}
|
|
16
|
+
return [from, to];
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
export const stackEnds = (categories) => categories.flatMap((values) => stackSpans(values).flatMap((span) => (span === undefined ? [] : [span[1]])));
|