@fusion.dev/helix-headless 0.142.1 → 0.143.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 +16710 -707
- package/dist/accordion/item/accordion-item.d.ts +38 -2
- package/dist/accordion/item/accordion-item.js +38 -6
- package/dist/accordion/root/accordion-root.d.ts +23 -1
- package/dist/accordion/root/accordion-root.js +39 -3
- package/dist/announcement/root/announcement-root.d.ts +11 -7
- package/dist/announcement/root/announcement-root.js +1 -1
- package/dist/app-header/root/app-header-root.d.ts +21 -0
- package/dist/app-header/root/app-header-root.js +31 -5
- package/dist/area-chart/root/area-chart-root.js +16 -5
- package/dist/avatar/group/avatar-group.d.ts +24 -3
- package/dist/avatar/group/avatar-group.js +34 -2
- package/dist/avatar/root/avatar-root.d.ts +3 -1
- package/dist/avatar/root/avatar-root.js +8 -1
- package/dist/badge/root/badge-root.d.ts +8 -0
- package/dist/bar-chart/root/bar-chart-root.d.ts +19 -3
- package/dist/bar-chart/root/bar-chart-root.js +43 -13
- package/dist/blockquote/root/blockquote-root.d.ts +12 -7
- package/dist/blockquote/root/blockquote-root.js +6 -4
- package/dist/breadcrumbs/root/breadcrumbs-root.d.ts +9 -3
- package/dist/breadcrumbs/root/breadcrumbs-root.js +9 -1
- package/dist/button/root/button-root.d.ts +37 -0
- package/dist/button/root/button-root.js +44 -0
- package/dist/callout/root/callout-root.d.ts +14 -5
- package/dist/callout/root/callout-root.js +11 -3
- package/dist/canvas/root/canvas-root.d.ts +1 -0
- package/dist/canvas/root/canvas-root.js +10 -1
- package/dist/carousel/root/carousel-root.d.ts +47 -5
- package/dist/carousel/root/carousel-root.js +53 -21
- package/dist/chat/composer/chat-composer.d.ts +13 -11
- package/dist/chat/composer/chat-composer.js +6 -12
- package/dist/chat/message/chat-message.js +8 -1
- package/dist/chat/root/chat-root.d.ts +1 -0
- package/dist/chat/root/chat-root.js +2 -1
- package/dist/checkbox/root/checkbox-root.d.ts +7 -0
- package/dist/checkbox/root/checkbox-root.js +7 -3
- package/dist/checkbox-group/root/checkbox-group-root.d.ts +65 -32
- package/dist/checkbox-group/root/checkbox-group-root.js +85 -37
- package/dist/code/root/code-root.d.ts +1 -1
- package/dist/code-block/root/code-block-root.d.ts +19 -0
- package/dist/code-block/root/code-block-root.js +21 -1
- package/dist/color-input/root/color-input-root.d.ts +26 -1
- package/dist/color-input/root/color-input-root.js +32 -3
- package/dist/color-picker/root/color-picker-root.d.ts +37 -7
- package/dist/color-picker/root/color-picker-root.js +83 -6
- package/dist/combobox/root/combobox-root.d.ts +90 -2
- package/dist/combobox/root/combobox-root.js +160 -19
- package/dist/command/root/command-root.d.ts +17 -6
- package/dist/command/root/command-root.js +12 -1
- package/dist/copy-button/root/copy-button-root.d.ts +21 -6
- package/dist/copy-button/root/copy-button-root.js +11 -1
- package/dist/data-table/root/data-table-root.d.ts +164 -10
- package/dist/data-table/root/data-table-root.js +488 -57
- package/dist/date-input/root/date-input-root.d.ts +53 -8
- package/dist/date-input/root/date-input-root.js +70 -18
- package/dist/date-picker/root/date-picker-root.d.ts +84 -17
- package/dist/date-picker/root/date-picker-root.js +125 -20
- package/dist/dialog/root/dialog-root.d.ts +38 -10
- package/dist/dialog/root/dialog-root.js +55 -2
- package/dist/donut-chart/root/donut-chart-root.d.ts +39 -15
- package/dist/donut-chart/root/donut-chart-root.js +30 -10
- package/dist/drawer/root/drawer-root.d.ts +32 -7
- package/dist/drawer/root/drawer-root.js +50 -5
- package/dist/drilldown/item/drilldown-item.d.ts +16 -13
- package/dist/drilldown/item/drilldown-item.js +6 -2
- package/dist/drilldown/root/drilldown-root.d.ts +21 -5
- package/dist/drilldown/root/drilldown-root.js +26 -10
- package/dist/element-events.generated.js +35 -9
- package/dist/file-input/root/file-input-root.d.ts +18 -1
- package/dist/file-input/root/file-input-root.js +34 -8
- package/dist/filter-input/menu/filter-menu.d.ts +9 -0
- package/dist/filter-input/menu/filter-menu.js +8 -0
- package/dist/filter-input/root/filter-input-root.d.ts +5 -2
- package/dist/filter-input/root/filter-input-root.js +22 -7
- package/dist/format-date/root/format-date-root.d.ts +11 -3
- package/dist/format-date/root/format-date-root.js +26 -5
- package/dist/graph/root/graph-root.d.ts +24 -3
- package/dist/graph/root/graph-root.js +66 -12
- package/dist/heatmap/root/buckets.d.ts +2 -1
- package/dist/heatmap/root/buckets.js +5 -1
- package/dist/heatmap/root/heatmap-root.d.ts +27 -6
- package/dist/heatmap/root/heatmap-root.js +23 -6
- package/dist/icon/root/icon-root.d.ts +24 -3
- package/dist/icon/root/icon-root.js +34 -1
- package/dist/icon-button/root/icon-button-root.d.ts +1 -1
- package/dist/image/root/image-root.d.ts +9 -2
- package/dist/image/root/image-root.js +8 -2
- package/dist/input/root/input-root.d.ts +51 -9
- package/dist/input/root/input-root.js +46 -5
- package/dist/kbd/root/kbd-root.d.ts +12 -4
- package/dist/kbd/root/kbd-root.js +10 -11
- package/dist/legend/root/legend-root.d.ts +7 -0
- package/dist/legend/root/legend-root.js +7 -2
- package/dist/lightbox/item/lightbox-item.d.ts +2 -1
- package/dist/lightbox/root/lightbox-root.d.ts +16 -5
- package/dist/lightbox/root/lightbox-root.js +24 -3
- package/dist/line-chart/root/line-chart-root.d.ts +19 -2
- package/dist/line-chart/root/line-chart-root.js +40 -13
- package/dist/list/item/list-item.d.ts +11 -5
- package/dist/list/item/list-item.js +5 -6
- package/dist/listbox/root/listbox-root.d.ts +111 -2
- package/dist/listbox/root/listbox-root.js +144 -16
- package/dist/map/root/map-root.d.ts +19 -4
- package/dist/map/root/map-root.js +36 -7
- package/dist/megamenu/root/megamenu-root.d.ts +10 -1
- package/dist/megamenu/root/megamenu-root.js +10 -1
- package/dist/menu/item/menu-item.d.ts +41 -12
- package/dist/menu/item/menu-item.js +46 -3
- package/dist/menu/root/menu-root.d.ts +32 -2
- package/dist/menu/root/menu-root.js +48 -8
- package/dist/meter/root/meter-root.d.ts +6 -0
- package/dist/meter/root/meter-root.js +18 -2
- package/dist/navigation/root/navigation-root.d.ts +10 -2
- package/dist/navigation/root/navigation-root.js +13 -2
- package/dist/notifications/group/notification-group.d.ts +1 -0
- package/dist/notifications/notification/notification.d.ts +12 -0
- package/dist/notifications/notification/notification.js +31 -2
- package/dist/notifications/root/notifications-root.d.ts +7 -0
- package/dist/notifications/root/notifications-root.js +17 -2
- package/dist/number-input/root/number-input-root.d.ts +62 -7
- package/dist/number-input/root/number-input-root.js +68 -16
- package/dist/object-inspector/root/object-inspector-root.d.ts +48 -5
- package/dist/object-inspector/root/object-inspector-root.js +91 -11
- package/dist/option/root/option-root.d.ts +4 -1
- package/dist/option/root/option-root.js +1 -0
- package/dist/outline/root/outline-root.d.ts +13 -2
- package/dist/outline/root/outline-root.js +4 -2
- package/dist/pagination/root/pagination-root.d.ts +19 -1
- package/dist/pagination/root/pagination-root.js +49 -9
- package/dist/pagination/size/pagination-size.d.ts +13 -3
- package/dist/pagination/size/pagination-size.js +7 -0
- package/dist/pin-input/root/pin-input-root.d.ts +16 -0
- package/dist/pin-input/root/pin-input-root.js +19 -5
- package/dist/popover/root/popover-root.d.ts +19 -2
- package/dist/popover/root/popover-root.js +22 -1
- package/dist/progress/root/progress-root.d.ts +16 -1
- package/dist/progress/root/progress-root.js +31 -7
- package/dist/qr-code/root/qr-code-root.d.ts +19 -0
- package/dist/qr-code/root/qr-code-root.js +29 -1
- package/dist/radio/group/radio-group.d.ts +41 -0
- package/dist/radio/group/radio-group.js +50 -14
- package/dist/radio/root/radio-root.d.ts +8 -5
- package/dist/radio/root/radio-root.js +1 -2
- package/dist/rating/root/rating-root.d.ts +24 -1
- package/dist/rating/root/rating-root.js +24 -7
- package/dist/scatter-chart/root/scatter-chart-root.js +15 -7
- package/dist/select/root/select-root.d.ts +69 -3
- package/dist/select/root/select-root.js +92 -8
- package/dist/skeleton/root/skeleton-root.d.ts +14 -3
- package/dist/skeleton/root/skeleton-root.js +17 -3
- package/dist/skip-link/root/skip-link-root.d.ts +10 -0
- package/dist/skip-link/root/skip-link-root.js +15 -1
- package/dist/slider/root/slider-root.d.ts +20 -2
- package/dist/slider/root/slider-root.js +41 -3
- package/dist/sparkline/root/sparkline-root.d.ts +9 -10
- package/dist/speed-dial/item/speed-dial-item.d.ts +11 -5
- package/dist/speed-dial/item/speed-dial-item.js +7 -2
- package/dist/speed-dial/root/speed-dial-root.d.ts +15 -4
- package/dist/speed-dial/root/speed-dial-root.js +19 -5
- package/dist/splitter/root/splitter-root.d.ts +28 -6
- package/dist/splitter/root/splitter-root.js +35 -1
- package/dist/stat/root/stat-root.d.ts +7 -1
- package/dist/stat/root/stat-root.js +14 -1
- package/dist/steps/root/steps-root.js +1 -1
- package/dist/switch/root/switch-root.d.ts +2 -0
- package/dist/switch/root/switch-root.js +1 -0
- package/dist/table/cell/table-cell.d.ts +24 -4
- package/dist/table/cell/table-cell.js +21 -1
- package/dist/table/column/table-column.d.ts +32 -6
- package/dist/table/column/table-column.js +36 -6
- package/dist/table/root/table-root.d.ts +94 -3
- package/dist/table/root/table-root.js +128 -5
- package/dist/table/row/table-row.d.ts +7 -0
- package/dist/table/row/table-row.js +9 -1
- package/dist/tabs/root/tabs-root.d.ts +6 -1
- package/dist/tabs/root/tabs-root.js +6 -2
- package/dist/tag/root/tag-root.d.ts +23 -1
- package/dist/tag/root/tag-root.js +16 -2
- package/dist/textarea/root/textarea-root.d.ts +4 -0
- package/dist/textarea/root/textarea-root.js +1 -1
- package/dist/time-input/root/time-input-root.d.ts +28 -2
- package/dist/time-input/root/time-input-root.js +46 -10
- package/dist/time-picker/root/time-picker-root.d.ts +101 -18
- package/dist/time-picker/root/time-picker-root.js +115 -16
- package/dist/toast/root/toast-root.d.ts +64 -1
- package/dist/toast/root/toast-root.js +147 -2
- package/dist/toggle/group/toggle-group.d.ts +38 -1
- package/dist/toggle/group/toggle-group.js +34 -8
- package/dist/toggle/root/toggle-root.d.ts +14 -10
- package/dist/toggle/root/toggle-root.js +3 -3
- package/dist/toolbar/root/toolbar-root.d.ts +55 -15
- package/dist/toolbar/root/toolbar-root.js +99 -16
- package/dist/tooltip/root/tooltip-root.d.ts +37 -4
- package/dist/tooltip/root/tooltip-root.js +48 -0
- package/dist/tour/root/tour-root.d.ts +34 -5
- package/dist/tour/root/tour-root.js +51 -14
- package/dist/tour/stop/tour-stop.d.ts +18 -1
- package/dist/tour/stop/tour-stop.js +25 -5
- package/dist/tree/item/tree-item.d.ts +21 -5
- package/dist/tree/item/tree-item.js +14 -4
- package/dist/tree/root/tree-root.d.ts +29 -5
- package/dist/tree/root/tree-root.js +64 -8
- package/dist/trend/root/trend-root.d.ts +23 -1
- package/dist/trend/root/trend-root.js +28 -7
- package/dist/video/root/keys.d.ts +5 -0
- package/dist/video/root/keys.js +13 -1
- package/dist/video/root/video-root.d.ts +19 -0
- package/dist/video/root/video-root.js +48 -4
- package/dist/video-playlist/menu/video-playlist-menu.d.ts +20 -2
- package/dist/video-playlist/menu/video-playlist-menu.js +27 -5
- package/dist/video-playlist/root/video-playlist-root.js +6 -3
- package/dist/virtual-list/root/virtual-list-root.d.ts +52 -4
- package/dist/virtual-list/root/virtual-list-root.js +103 -21
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +4348 -965
- package/types/vue.d.ts +972 -232
|
@@ -26,17 +26,34 @@ const size = (bytes, locale) => {
|
|
|
26
26
|
return `${written} ${unit}`;
|
|
27
27
|
};
|
|
28
28
|
const kind = (file) => {
|
|
29
|
-
const
|
|
30
|
-
if (
|
|
31
|
-
return
|
|
29
|
+
const extension = /\.([a-z0-9]{1,6})$/i.exec(file.name)?.[1];
|
|
30
|
+
if (extension) {
|
|
31
|
+
return extension.toUpperCase();
|
|
32
32
|
}
|
|
33
|
-
const
|
|
34
|
-
return
|
|
33
|
+
const subtype = file.type.split('/')[1]?.replace(/^x-/, '').split('+')[0] ?? '';
|
|
34
|
+
return /^[a-z0-9]{1,6}$/i.test(subtype) ? subtype.toUpperCase() : 'File';
|
|
35
|
+
};
|
|
36
|
+
const accepts = (file, accept) => {
|
|
37
|
+
const tokens = accept
|
|
38
|
+
.split(',')
|
|
39
|
+
.map((token) => token.trim().toLowerCase())
|
|
40
|
+
.filter(Boolean);
|
|
41
|
+
if (tokens.length === 0) {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
const name = file.name.toLowerCase();
|
|
45
|
+
const type = file.type.toLowerCase();
|
|
46
|
+
return tokens.some((token) => token.startsWith('.')
|
|
47
|
+
? name.endsWith(token)
|
|
48
|
+
: token.endsWith('/*')
|
|
49
|
+
? type.startsWith(token.slice(0, -1))
|
|
50
|
+
: type === token);
|
|
35
51
|
};
|
|
36
52
|
let HelixHeadlessFileInput = class HelixHeadlessFileInput extends MixinField(HelixFormElement) {
|
|
37
53
|
constructor() {
|
|
38
54
|
super(...arguments);
|
|
39
55
|
this.aria = new AriaForwardController(this, {
|
|
56
|
+
named: () => this.labeled,
|
|
40
57
|
target: () => this.partNamed('control'),
|
|
41
58
|
});
|
|
42
59
|
this.accept = '';
|
|
@@ -67,7 +84,7 @@ let HelixHeadlessFileInput = class HelixHeadlessFileInput extends MixinField(Hel
|
|
|
67
84
|
return;
|
|
68
85
|
}
|
|
69
86
|
event.preventDefault();
|
|
70
|
-
const dropped = [...event.dataTransfer.files];
|
|
87
|
+
const dropped = [...event.dataTransfer.files].filter((file) => accepts(file, this.accept));
|
|
71
88
|
if (dropped.length === 0) {
|
|
72
89
|
return;
|
|
73
90
|
}
|
|
@@ -75,9 +92,14 @@ let HelixHeadlessFileInput = class HelixHeadlessFileInput extends MixinField(Hel
|
|
|
75
92
|
};
|
|
76
93
|
this.handleRemove = (event) => {
|
|
77
94
|
const at = Number(event.currentTarget.dataset.at);
|
|
78
|
-
if (Number.isFinite(at)) {
|
|
79
|
-
|
|
95
|
+
if (!Number.isFinite(at)) {
|
|
96
|
+
return;
|
|
80
97
|
}
|
|
98
|
+
this.removeAt(at);
|
|
99
|
+
this.updateComplete.then(() => {
|
|
100
|
+
const buttons = [...this.renderRoot.querySelectorAll('[part="remove"]')];
|
|
101
|
+
(buttons[Math.min(at, buttons.length - 1)] ?? this.control)?.focus();
|
|
102
|
+
});
|
|
81
103
|
};
|
|
82
104
|
}
|
|
83
105
|
get files() {
|
|
@@ -152,6 +174,10 @@ let HelixHeadlessFileInput = class HelixHeadlessFileInput extends MixinField(Hel
|
|
|
152
174
|
this.committed = this.files;
|
|
153
175
|
this.sync();
|
|
154
176
|
}
|
|
177
|
+
updated(changed) {
|
|
178
|
+
super.updated(changed);
|
|
179
|
+
this.sync();
|
|
180
|
+
}
|
|
155
181
|
focus(options) {
|
|
156
182
|
this.control?.focus(options);
|
|
157
183
|
}
|
|
@@ -151,6 +151,15 @@ export declare class HelixHeadlessFilterMenu extends HelixElement {
|
|
|
151
151
|
* a filter is never left looking at a menu that says it took.
|
|
152
152
|
*/
|
|
153
153
|
apply(field: string, values: readonly string[], operator?: string): boolean;
|
|
154
|
+
/**
|
|
155
|
+
* Keeps each choice row's tick where this element puts it.
|
|
156
|
+
*
|
|
157
|
+
* A choice row checks itself after `helix-before-toggle`, and this element
|
|
158
|
+
* already sets `checked` on every row from its own state. The request is
|
|
159
|
+
* refused, so a tick moves only when the choice is applied, and stopped, so
|
|
160
|
+
* it does not reach a page that never placed the row.
|
|
161
|
+
*/
|
|
162
|
+
private readonly keepTick;
|
|
154
163
|
/**
|
|
155
164
|
* A row was chosen: a value to tick, or a comparison to use.
|
|
156
165
|
*
|
|
@@ -17,6 +17,12 @@ let HelixHeadlessFilterMenu = class HelixHeadlessFilterMenu extends HelixElement
|
|
|
17
17
|
this.label = 'Filter';
|
|
18
18
|
this.picked = new Map();
|
|
19
19
|
this.comparisons = new Map();
|
|
20
|
+
this.keepTick = (event) => {
|
|
21
|
+
if (event.target === event.currentTarget) {
|
|
22
|
+
event.preventDefault();
|
|
23
|
+
event.stopPropagation();
|
|
24
|
+
}
|
|
25
|
+
};
|
|
20
26
|
this.handleSelect = (event) => {
|
|
21
27
|
const row = event.composedPath()[0];
|
|
22
28
|
if (!(row instanceof HTMLElement)) {
|
|
@@ -152,6 +158,7 @@ let HelixHeadlessFilterMenu = class HelixHeadlessFilterMenu extends HelixElement
|
|
|
152
158
|
checkable
|
|
153
159
|
exclusive
|
|
154
160
|
?checked=${operator.name === using?.name}
|
|
161
|
+
@helix-before-toggle=${this.keepTick}
|
|
155
162
|
data-facet="operator"
|
|
156
163
|
data-field=${name}
|
|
157
164
|
data-value=${operator.name}
|
|
@@ -173,6 +180,7 @@ let HelixHeadlessFilterMenu = class HelixHeadlessFilterMenu extends HelixElement
|
|
|
173
180
|
checkable
|
|
174
181
|
?exclusive=${using?.arity === 'one'}
|
|
175
182
|
?checked=${ticked.has(option.value)}
|
|
183
|
+
@helix-before-toggle=${this.keepTick}
|
|
176
184
|
data-facet="value"
|
|
177
185
|
data-field=${name}
|
|
178
186
|
data-value=${option.value}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AriaForwardController, HelixFormElement, PopoverController } from '@fusion.dev/helix-core';
|
|
2
2
|
import { type PropertyValues, type TemplateResult } from 'lit';
|
|
3
|
-
import
|
|
3
|
+
import { HelixHeadlessFilterField } from '../field/filter-field.js';
|
|
4
4
|
declare const HelixHeadlessFilterInput_base: (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerOpenInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinTokensInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinControllerValueInterface) & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinFieldInterface) & typeof HelixFormElement;
|
|
5
5
|
/**
|
|
6
6
|
* @summary A text field for filters, with the active filters shown in it as tokens.
|
|
@@ -69,6 +69,9 @@ declare const HelixHeadlessFilterInput_base: (new (...args: any[]) => import("@f
|
|
|
69
69
|
* @documentation https://fusion.dev/helix/components/filter-input
|
|
70
70
|
*
|
|
71
71
|
* @slot end - Content after the text, such as a count or a control.
|
|
72
|
+
* @slot feedback - The message that says what is wrong, for content richer than a string. It shows while there is a message.
|
|
73
|
+
* @slot hint - Help under the control, for content richer than the `hint` text.
|
|
74
|
+
* @slot label - The label, for content richer than the `label` text.
|
|
72
75
|
* @slot start - Content before the text, such as a search icon.
|
|
73
76
|
* @slot suggestions - Suggestions for what to type next, as `Option`s and `Option.Group`s.
|
|
74
77
|
* @slot tags - The active filters, as `FilterChip`s or `Tag`s.
|
|
@@ -81,7 +84,7 @@ declare const HelixHeadlessFilterInput_base: (new (...args: any[]) => import("@f
|
|
|
81
84
|
* @event {HelixFacetEvent} helix-facet - Fired when a filter changes, whether it was typed, picked, or removed. Cancelable: call `preventDefault()` to leave the text and the chip as they were.
|
|
82
85
|
* @event {HelixSelectEvent} helix-select - Fired by an option in `suggestions` when it is chosen, and bubbles out of the field. Cancelable: call `preventDefault()` to stop other listeners acting on it. The field takes no action on a choice; you decide what it does.
|
|
83
86
|
* @event {Event} input - Fired when the text changes.
|
|
84
|
-
* @event {
|
|
87
|
+
* @event {ToggleEvent} toggle - Fired when the suggestions panel opens or closes, with `newState` and `oldState`.
|
|
85
88
|
*
|
|
86
89
|
* @csspart clear - The button that empties the field.
|
|
87
90
|
* @csspart control - The input itself.
|
|
@@ -8,7 +8,9 @@ import { AriaForwardController, customElement, drawIcon, HelixFacetEvent, HelixF
|
|
|
8
8
|
import { x } from '@fusion.dev/helix-icons';
|
|
9
9
|
import { html, nothing } from 'lit';
|
|
10
10
|
import { property, state } from 'lit/decorators.js';
|
|
11
|
+
import { HelixHeadlessFilterField } from '../field/filter-field.js';
|
|
11
12
|
import { readFilterText } from '../grammar.js';
|
|
13
|
+
import { HelixHeadlessFilterMenu } from '../menu/filter-menu.js';
|
|
12
14
|
const asksToGo = (node) => typeof node?.dismiss === 'function';
|
|
13
15
|
const isOption = (node) => 'active' in node && 'value' in node;
|
|
14
16
|
let HelixHeadlessFilterInput = class HelixHeadlessFilterInput extends MixinControllerOpen(MixinTokens(MixinControllerValue(MixinField(HelixFormElement)))) {
|
|
@@ -66,7 +68,9 @@ let HelixHeadlessFilterInput = class HelixHeadlessFilterInput extends MixinContr
|
|
|
66
68
|
menu.apply(field, []);
|
|
67
69
|
};
|
|
68
70
|
this.handleClear = () => {
|
|
69
|
-
this.requestValue('')
|
|
71
|
+
if (this.value !== '' && this.requestValue('')) {
|
|
72
|
+
this.announceChange();
|
|
73
|
+
}
|
|
70
74
|
const control = this.control;
|
|
71
75
|
if (control) {
|
|
72
76
|
restoreValue(control, this.value);
|
|
@@ -192,12 +196,13 @@ let HelixHeadlessFilterInput = class HelixHeadlessFilterInput extends MixinContr
|
|
|
192
196
|
this.control?.setSelectionRange(start, end, direction);
|
|
193
197
|
}
|
|
194
198
|
get fields() {
|
|
195
|
-
return [
|
|
196
|
-
...this.querySelectorAll('helix-headless-filter-field, helix-filter-field'),
|
|
197
|
-
];
|
|
199
|
+
return [...this.querySelectorAll('*')].filter((element) => element instanceof HelixHeadlessFilterField);
|
|
198
200
|
}
|
|
199
201
|
menuFor(field) {
|
|
200
|
-
for (const menu of this.querySelectorAll('
|
|
202
|
+
for (const menu of this.querySelectorAll('*')) {
|
|
203
|
+
if (!(menu instanceof HelixHeadlessFilterMenu)) {
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
201
206
|
if (menu.fields.some((declared) => (declared.name ?? '') === field)) {
|
|
202
207
|
return menu;
|
|
203
208
|
}
|
|
@@ -264,7 +269,10 @@ let HelixHeadlessFilterInput = class HelixHeadlessFilterInput extends MixinContr
|
|
|
264
269
|
this.activeAt = this.activeAt < options.length ? this.activeAt : -1;
|
|
265
270
|
}
|
|
266
271
|
removeLastToken() {
|
|
267
|
-
const last = this.tokens
|
|
272
|
+
const last = this.tokens
|
|
273
|
+
?.assignedElements({ flatten: true })
|
|
274
|
+
.filter((token) => !token.hidden)
|
|
275
|
+
.at(-1);
|
|
268
276
|
if (asksToGo(last)) {
|
|
269
277
|
last.dismiss();
|
|
270
278
|
}
|
|
@@ -373,7 +381,14 @@ let HelixHeadlessFilterInput = class HelixHeadlessFilterInput extends MixinContr
|
|
|
373
381
|
built only once there were options could never learn that there are
|
|
374
382
|
any. A manual popover puts it in the top layer, so it is not clipped by
|
|
375
383
|
a scrolling table or an overflow on an ancestor. -->
|
|
376
|
-
<div
|
|
384
|
+
<div
|
|
385
|
+
part="surface"
|
|
386
|
+
id="suggestions"
|
|
387
|
+
role="listbox"
|
|
388
|
+
aria-labelledby=${this.labeled ? this.labelId : nothing}
|
|
389
|
+
popover="manual"
|
|
390
|
+
?hidden=${!this.open}
|
|
391
|
+
>
|
|
377
392
|
<slot name="suggestions" @slotchange=${this.handleSuggestions}></slot>
|
|
378
393
|
</div>
|
|
379
394
|
${this.renderHint()}${this.renderFeedback()}`;
|
|
@@ -16,9 +16,15 @@ import { nothing, type PropertyValues, type TemplateResult } from 'lit';
|
|
|
16
16
|
* schedules an update for when its current reading expires, and an element
|
|
17
17
|
* showing a date schedules none, so a long feed left open costs almost nothing.
|
|
18
18
|
*
|
|
19
|
-
* **The output is always `<time datetime>`**, so
|
|
20
|
-
*
|
|
21
|
-
*
|
|
19
|
+
* **The output is always `<time datetime>`**, so copy and paste and scripts
|
|
20
|
+
* parsing the page get the exact instant, whatever the visible text says. A
|
|
21
|
+
* screen reader reads the text, so an `elapsed` reading is named in full, such
|
|
22
|
+
* as "3 hours ago", and carries the whole date as its title.
|
|
23
|
+
*
|
|
24
|
+
* **A date with no time, such as `2026-08-04`, is that day everywhere.** It is
|
|
25
|
+
* formatted in UTC unless `time-zone` says otherwise, as Date Input and Date
|
|
26
|
+
* Picker format their dates, so a reader west of UTC sees August 4 and not
|
|
27
|
+
* August 3.
|
|
22
28
|
*
|
|
23
29
|
* @since 0.1.0
|
|
24
30
|
* @status stable
|
|
@@ -114,6 +120,8 @@ export declare class HelixHeadlessFormatDate extends HelixElement {
|
|
|
114
120
|
private written;
|
|
115
121
|
private stopTicking;
|
|
116
122
|
private watching;
|
|
123
|
+
/** Whether the timestamp is a day with no time, which is formatted in UTC. */
|
|
124
|
+
private get dateOnly();
|
|
117
125
|
/** The timestamp, or nothing where there is not one to read. */
|
|
118
126
|
private get moment();
|
|
119
127
|
/** Every `Intl.DateTimeFormat` option a consumer set, and none they did not. */
|
|
@@ -4,9 +4,10 @@ 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, elapsedTime, HelixElement, subscribe } from '@fusion.dev/helix-core';
|
|
7
|
+
import { customElement, elapsedTime, HelixElement, relativeTime, subscribe, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html, nothing } from 'lit';
|
|
9
9
|
import { property, state } from 'lit/decorators.js';
|
|
10
|
+
const DATE_ONLY = /^\d{4}-\d{2}-\d{2}$/;
|
|
10
11
|
let HelixHeadlessFormatDate = class HelixHeadlessFormatDate extends HelixElement {
|
|
11
12
|
constructor() {
|
|
12
13
|
super(...arguments);
|
|
@@ -18,6 +19,9 @@ let HelixHeadlessFormatDate = class HelixHeadlessFormatDate extends HelixElement
|
|
|
18
19
|
this.stopTicking = undefined;
|
|
19
20
|
this.watching = undefined;
|
|
20
21
|
}
|
|
22
|
+
get dateOnly() {
|
|
23
|
+
return DATE_ONLY.test((this.date || this.written).trim());
|
|
24
|
+
}
|
|
21
25
|
get moment() {
|
|
22
26
|
const source = this.date || this.written;
|
|
23
27
|
if (source === '') {
|
|
@@ -46,6 +50,9 @@ let HelixHeadlessFormatDate = class HelixHeadlessFormatDate extends HelixElement
|
|
|
46
50
|
if (this.timeZone) {
|
|
47
51
|
asked.timeZone = this.timeZone;
|
|
48
52
|
}
|
|
53
|
+
else if (this.dateOnly) {
|
|
54
|
+
asked.timeZone = 'UTC';
|
|
55
|
+
}
|
|
49
56
|
if (this.timeZoneName) {
|
|
50
57
|
asked.timeZoneName = this.timeZoneName;
|
|
51
58
|
}
|
|
@@ -120,10 +127,24 @@ let HelixHeadlessFormatDate = class HelixHeadlessFormatDate extends HelixElement
|
|
|
120
127
|
if (!moment) {
|
|
121
128
|
return nothing;
|
|
122
129
|
}
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
130
|
+
const datetime = this.dateOnly ? (this.date || this.written).trim() : moment.toISOString();
|
|
131
|
+
if (!this.elapsed) {
|
|
132
|
+
const text = new Intl.DateTimeFormat(this.locale || undefined, this.options).format(moment);
|
|
133
|
+
return html `<time part="time" datetime=${datetime}>${text}</time>`;
|
|
134
|
+
}
|
|
135
|
+
const now = new Date(this.clock ?? Date.now());
|
|
136
|
+
const zone = this.timeZone || (this.dateOnly ? 'UTC' : undefined);
|
|
137
|
+
const whole = new Intl.DateTimeFormat(this.locale || undefined, {
|
|
138
|
+
dateStyle: 'long',
|
|
139
|
+
...(this.dateOnly ? {} : { timeStyle: 'short' }),
|
|
140
|
+
...(zone ? { timeZone: zone } : {}),
|
|
141
|
+
}).format(moment);
|
|
142
|
+
return html `<time
|
|
143
|
+
part="time"
|
|
144
|
+
datetime=${datetime}
|
|
145
|
+
aria-label=${relativeTime(this.locale)(moment, now).text}
|
|
146
|
+
title=${whole}
|
|
147
|
+
>${elapsedTime(this.locale)(moment, now).text}</time>`;
|
|
127
148
|
}
|
|
128
149
|
};
|
|
129
150
|
__decorate([
|
|
@@ -232,16 +232,37 @@ export declare class HelixHeadlessGraph extends HelixHeadlessGraph_base {
|
|
|
232
232
|
/**
|
|
233
233
|
* Moves the keyboard to a row, and the list to show it.
|
|
234
234
|
*
|
|
235
|
-
* **
|
|
236
|
-
*
|
|
237
|
-
*
|
|
235
|
+
* **The row the keys are on is always drawn**, beside the window where it is
|
|
236
|
+
* outside it, so `aria-activedescendant` names a row that exists. The list
|
|
237
|
+
* then scrolls to it, see `revealCursor`.
|
|
238
238
|
*/
|
|
239
239
|
private moveTo;
|
|
240
|
+
/**
|
|
241
|
+
* Brings the cursor's row into the list's box.
|
|
242
|
+
*
|
|
243
|
+
* **By the row's own box once it is in the window, and by the window
|
|
244
|
+
* otherwise.** The offset of a row that has not been drawn is a guess at its
|
|
245
|
+
* height, and a guess at 32 pixels was where End and Page Down sent the list
|
|
246
|
+
* when rows were taller: the list landed far from the row, which then was
|
|
247
|
+
* not drawn. So the window is moved toward the row, the rows it draws are
|
|
248
|
+
* measured, and the next try starts from there, a few times at most.
|
|
249
|
+
*/
|
|
250
|
+
private revealCursor;
|
|
251
|
+
/** The cursor's row, where it is outside the window and drawn beside it. */
|
|
252
|
+
private get keptRow();
|
|
240
253
|
/**
|
|
241
254
|
* The keys in the list. **Kept from the frame**, whose arrows pan the
|
|
242
255
|
* drawing: a reader moving through the list was moving the graph under it.
|
|
243
256
|
*/
|
|
244
257
|
private readonly handleListKeydown;
|
|
258
|
+
/** What has been typed toward a note's name, and when the last letter came. */
|
|
259
|
+
private typed;
|
|
260
|
+
/**
|
|
261
|
+
* Moves to the next note whose name starts with what was typed, as a
|
|
262
|
+
* listbox does. Letters within half a second of each other run together,
|
|
263
|
+
* and a repeated letter steps through the names that start with it.
|
|
264
|
+
*/
|
|
265
|
+
private typeahead;
|
|
245
266
|
/**
|
|
246
267
|
* The search field's keys, which belong to the field and never pan the
|
|
247
268
|
* graph. Escape empties a search that has words in it, and passes through
|
|
@@ -4,10 +4,11 @@ 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, fillTemplate, formatNumbers, HelixElement, HelixGraphFilterEvent, HelixGraphLayoutEvent, HelixGraphPanEvent, HelixGraphPartChangeEvent, HelixGraphReadyEvent, HelixGraphSignalEvent, HelixGraphZoomEvent, MixinControllerActive, MixinViewport, VirtualizeController, } from '@fusion.dev/helix-core';
|
|
7
|
+
import { customElement, fillTemplate, formatNumbers, HelixElement, HelixGraphFilterEvent, HelixGraphLayoutEvent, HelixGraphPanEvent, HelixGraphPartChangeEvent, HelixGraphReadyEvent, HelixGraphSignalEvent, HelixGraphZoomEvent, MixinControllerActive, MixinViewport, scrollItemIntoView, VirtualizeController, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { list, magnifyingGlass } from '@fusion.dev/helix-icons';
|
|
9
9
|
import { html, nothing } from 'lit';
|
|
10
10
|
import { property, state } from 'lit/decorators.js';
|
|
11
|
+
import { repeat } from 'lit/directives/repeat.js';
|
|
11
12
|
import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
|
|
12
13
|
import { adjacencyOf, entriesOf, groupsOf, nameOf, sortedOf, within, withoutGroups, } from './graph-model.js';
|
|
13
14
|
const PAGE = 10;
|
|
@@ -65,8 +66,15 @@ let HelixHeadlessGraph = class HelixHeadlessGraph extends MixinViewport(MixinCon
|
|
|
65
66
|
event.preventDefault();
|
|
66
67
|
event.stopPropagation();
|
|
67
68
|
this.choose(this.cursor);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (event.key.length === 1 && !event.altKey && !event.ctrlKey && !event.metaKey) {
|
|
72
|
+
event.preventDefault();
|
|
73
|
+
event.stopPropagation();
|
|
74
|
+
this.typeahead(event.key);
|
|
68
75
|
}
|
|
69
76
|
};
|
|
77
|
+
this.typed = { at: 0, text: '' };
|
|
70
78
|
this.handleSearchKeydown = (event) => {
|
|
71
79
|
if (event.key === 'ArrowDown') {
|
|
72
80
|
event.preventDefault();
|
|
@@ -236,18 +244,50 @@ let HelixHeadlessGraph = class HelixHeadlessGraph extends MixinViewport(MixinCon
|
|
|
236
244
|
moveTo(index) {
|
|
237
245
|
const last = this.entries.length - 1;
|
|
238
246
|
this.cursor = Math.min(Math.max(index, 0), Math.max(last, 0));
|
|
247
|
+
void this.updateComplete.then(() => this.revealCursor());
|
|
248
|
+
}
|
|
249
|
+
revealCursor(attempts = 8) {
|
|
239
250
|
const list = this.partNamed('list');
|
|
240
251
|
if (!list) {
|
|
241
252
|
return;
|
|
242
253
|
}
|
|
243
|
-
const
|
|
244
|
-
if (
|
|
245
|
-
row.
|
|
254
|
+
const { first, last } = this.virtualize.window;
|
|
255
|
+
if (this.cursor >= first && this.cursor < last) {
|
|
256
|
+
const row = this.renderRoot.querySelector?.(`[data-index="${this.cursor}"]`);
|
|
257
|
+
if (row) {
|
|
258
|
+
scrollItemIntoView(row, { scroller: list });
|
|
259
|
+
}
|
|
260
|
+
return;
|
|
246
261
|
}
|
|
247
|
-
|
|
248
|
-
|
|
262
|
+
if (attempts === 0) {
|
|
263
|
+
return;
|
|
249
264
|
}
|
|
265
|
+
this.virtualize.reveal(this.cursor);
|
|
250
266
|
this.virtualize.measure();
|
|
267
|
+
void this.updateComplete.then(() => this.revealCursor(attempts - 1));
|
|
268
|
+
}
|
|
269
|
+
get keptRow() {
|
|
270
|
+
const { first, last } = this.virtualize.window;
|
|
271
|
+
const cursor = this.cursor;
|
|
272
|
+
return this.entries.length > 0 && (cursor < first || cursor >= last) ? cursor : undefined;
|
|
273
|
+
}
|
|
274
|
+
typeahead(letter) {
|
|
275
|
+
const now = Date.now();
|
|
276
|
+
const text = now - this.typed.at < 500 ? this.typed.text + letter : letter;
|
|
277
|
+
this.typed = { at: now, text };
|
|
278
|
+
const wanted = text.toLocaleLowerCase();
|
|
279
|
+
const repeated = [...wanted].every((one) => one === wanted[0]);
|
|
280
|
+
const prefix = repeated ? (wanted[0] ?? '') : wanted;
|
|
281
|
+
const entries = this.entries;
|
|
282
|
+
const start = repeated ? this.cursor + 1 : this.cursor;
|
|
283
|
+
for (let step = 0; step < entries.length; step += 1) {
|
|
284
|
+
const at = (start + step) % entries.length;
|
|
285
|
+
const entry = entries[at];
|
|
286
|
+
if (entry && nameOf(entry.node).toLocaleLowerCase().startsWith(prefix)) {
|
|
287
|
+
this.moveTo(at);
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
251
291
|
}
|
|
252
292
|
togglePanel(open = !this.panelOpen) {
|
|
253
293
|
const inside = this.panelOpen && this.partNamed('panel')?.contains(this.shadowRoot?.activeElement ?? null);
|
|
@@ -290,8 +330,18 @@ let HelixHeadlessGraph = class HelixHeadlessGraph extends MixinViewport(MixinCon
|
|
|
290
330
|
renderRows() {
|
|
291
331
|
const { first, last } = this.virtualize.window;
|
|
292
332
|
const total = this.entries.length;
|
|
293
|
-
|
|
294
|
-
|
|
333
|
+
const indices = Array.from({ length: Math.max(0, Math.min(last, total) - first) }, (_, at) => first + at);
|
|
334
|
+
const kept = this.keptRow;
|
|
335
|
+
if (kept !== undefined && kept < total) {
|
|
336
|
+
if (kept < first) {
|
|
337
|
+
indices.unshift(kept);
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
indices.push(kept);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return html `${repeat(indices, (index) => index, (index) => {
|
|
344
|
+
const entry = this.entries[index];
|
|
295
345
|
const relation = this.relationText(entry);
|
|
296
346
|
const chosen = entry.relation === 'selected';
|
|
297
347
|
return html `<div
|
|
@@ -311,10 +361,14 @@ let HelixHeadlessGraph = class HelixHeadlessGraph extends MixinViewport(MixinCon
|
|
|
311
361
|
<span part="note-name">${nameOf(entry.node)}</span>
|
|
312
362
|
${relation ? html `<span part="note-relation" aria-hidden="true">${relation}</span>` : nothing}
|
|
313
363
|
</div>`;
|
|
314
|
-
})
|
|
364
|
+
})}`;
|
|
315
365
|
}
|
|
316
366
|
render() {
|
|
317
|
-
const
|
|
367
|
+
const window = this.virtualize.window;
|
|
368
|
+
const kept = this.keptRow;
|
|
369
|
+
const room = kept === undefined ? 0 : this.virtualize.sizeOf(kept);
|
|
370
|
+
const before = Math.max(0, window.before - (kept !== undefined && kept < window.first ? room : 0));
|
|
371
|
+
const after = Math.max(0, window.after - (kept !== undefined && kept >= window.last ? room : 0));
|
|
318
372
|
const cursor = this.entries.length > 0 ? `note-${this.cursor}` : nothing;
|
|
319
373
|
return html `<div
|
|
320
374
|
part="region"
|
|
@@ -368,9 +422,9 @@ let HelixHeadlessGraph = class HelixHeadlessGraph extends MixinViewport(MixinCon
|
|
|
368
422
|
@keydown=${this.handleListKeydown}
|
|
369
423
|
@scroll=${() => this.virtualize.measure()}
|
|
370
424
|
>
|
|
371
|
-
<div part="before" style="block-size: ${before}px"></div>
|
|
425
|
+
<div part="before" role="presentation" style="block-size: ${before}px"></div>
|
|
372
426
|
${this.renderRows()}
|
|
373
|
-
<div part="after" style="block-size: ${after}px"></div>
|
|
427
|
+
<div part="after" role="presentation" style="block-size: ${after}px"></div>
|
|
374
428
|
</div>
|
|
375
429
|
</div>
|
|
376
430
|
</div>
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
/** A reading, reduced to what the bucketing needs. */
|
|
11
11
|
export interface Reading {
|
|
12
12
|
tone?: string | undefined;
|
|
13
|
-
|
|
13
|
+
/** The number, or undefined for a row whose cell was blank. */
|
|
14
|
+
value: number | undefined;
|
|
14
15
|
}
|
|
15
16
|
/**
|
|
16
17
|
* The tone for one reading, from its own `data-tone` or from the thresholds.
|
|
@@ -6,8 +6,12 @@ export const toneOf = (reading, thresholds, tones) => {
|
|
|
6
6
|
return undefined;
|
|
7
7
|
}
|
|
8
8
|
let chosen;
|
|
9
|
+
const value = reading.value;
|
|
10
|
+
if (value === undefined) {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
9
13
|
for (const [index, bound] of thresholds.entries()) {
|
|
10
|
-
if (
|
|
14
|
+
if (value >= bound) {
|
|
11
15
|
chosen = tones[index] ?? chosen;
|
|
12
16
|
}
|
|
13
17
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HelixElement } from '@fusion.dev/helix-core';
|
|
1
|
+
import { AriaForwardController, HelixElement } from '@fusion.dev/helix-core';
|
|
2
2
|
import { nothing, type PropertyValues, type TemplateResult } from 'lit';
|
|
3
3
|
import '../../tooltip/index.js';
|
|
4
4
|
declare const HelixHeadlessHeatmap_base: typeof HelixElement & (new (...args: any[]) => import("@fusion.dev/helix-core").MixinChartSourceInterface);
|
|
@@ -44,7 +44,7 @@ declare const HelixHeadlessHeatmap_base: typeof HelixElement & (new (...args: an
|
|
|
44
44
|
* @csspart footer - The three-slot row under the strip.
|
|
45
45
|
* @csspart mark - The invisible point the tooltip is anchored to.
|
|
46
46
|
* @csspart start - The leading slot's box.
|
|
47
|
-
* @csspart strip - The grid of cells.
|
|
47
|
+
* @csspart strip - The grid of cells. It is an image, named by the host's `aria-labelledby` or `aria-label`, then by `label`, then by the caption of the table.
|
|
48
48
|
* @csspart summary - The middle slot's box.
|
|
49
49
|
* @csspart tooltip - The bubble, forwarded from `Tooltip`.
|
|
50
50
|
* @csspart tooltip-anchor - The `Tooltip` element itself.
|
|
@@ -188,14 +188,37 @@ export declare class HelixHeadlessHeatmap extends HelixHeadlessHeatmap_base {
|
|
|
188
188
|
* none.
|
|
189
189
|
*/
|
|
190
190
|
private hovered;
|
|
191
|
+
/**
|
|
192
|
+
* Carries a host `aria-labelledby` and `aria-describedby` to the strip, the
|
|
193
|
+
* image a reader lands on. The host is a generic around it.
|
|
194
|
+
*/
|
|
195
|
+
protected readonly aria: AriaForwardController;
|
|
196
|
+
/**
|
|
197
|
+
* Hears an `aria-label` written on the element, which no property mirrors.
|
|
198
|
+
*
|
|
199
|
+
* Lit observes the attributes of its own properties only, and `ariaLabel`
|
|
200
|
+
* belongs to the platform.
|
|
201
|
+
*/
|
|
202
|
+
static get observedAttributes(): string[];
|
|
203
|
+
/**
|
|
204
|
+
* Called by the browser when an observed attribute changes. A new
|
|
205
|
+
* `aria-label` asks for an update, because it names the strip.
|
|
206
|
+
*/
|
|
207
|
+
attributeChangedCallback(name: string, old: string | null, value: string | null): void;
|
|
191
208
|
private observer;
|
|
192
209
|
private announced;
|
|
193
210
|
/**
|
|
194
|
-
* Every
|
|
211
|
+
* Every row in the table, oldest first, as the table is written, with its
|
|
212
|
+
* reading where the row has one.
|
|
195
213
|
*
|
|
196
214
|
* **Not `readings`, which the mixin already has and which means a count.**
|
|
197
215
|
* Two members of the same name meaning a number and an array is the kind of
|
|
198
216
|
* collision that type-checks in one direction and reads wrong in both.
|
|
217
|
+
*
|
|
218
|
+
* **One cell per row, and a blank row is a cell with no reading.** Taken from
|
|
219
|
+
* the series alone, a blank dropped its day and every day after it moved one
|
|
220
|
+
* cell along, while the tooltip still counted rows and named the day beside
|
|
221
|
+
* the one under the pointer.
|
|
199
222
|
*/
|
|
200
223
|
private get points();
|
|
201
224
|
/**
|
|
@@ -262,7 +285,6 @@ export declare class HelixHeadlessHeatmap extends HelixHeadlessHeatmap_base {
|
|
|
262
285
|
private get drawn();
|
|
263
286
|
connectedCallback(): void;
|
|
264
287
|
disconnectedCallback(): void;
|
|
265
|
-
protected willUpdate(changed: PropertyValues): void;
|
|
266
288
|
/**
|
|
267
289
|
* Says on the footer whether any of its three slots holds anything.
|
|
268
290
|
*
|
|
@@ -303,8 +325,7 @@ export declare class HelixHeadlessHeatmap extends HelixHeadlessHeatmap_base {
|
|
|
303
325
|
*
|
|
304
326
|
* `pointerover` on the strip rather than a listener per cell: the event
|
|
305
327
|
* bubbles, so one handler covers however many cells there are, and unlike
|
|
306
|
-
* `mouseover` it is delivered for a pen
|
|
307
|
-
* press.
|
|
328
|
+
* `mouseover` it is delivered for a pen.
|
|
308
329
|
*/
|
|
309
330
|
private readonly onPointerOver;
|
|
310
331
|
private readonly onPointerLeave;
|
|
@@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
import { CHART_PLOT, customElement, drawTooltip, HelixElement, HelixResizeEvent,
|
|
7
|
+
import { AriaForwardController, byRow, CHART_PLOT, customElement, drawTooltip, HelixElement, HelixResizeEvent, MixinChartSource, markRegion, searchableById, writtenList, writtenNumbers, } from '@fusion.dev/helix-core';
|
|
8
8
|
import { html, nothing } from 'lit';
|
|
9
9
|
import { property, state } from 'lit/decorators.js';
|
|
10
10
|
import '../../tooltip/index.js';
|
|
@@ -21,9 +21,15 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
|
|
|
21
21
|
this.width = undefined;
|
|
22
22
|
this.metrics = undefined;
|
|
23
23
|
this.hovered = -1;
|
|
24
|
+
this.aria = new AriaForwardController(this, {
|
|
25
|
+
target: () => this.partNamed('strip'),
|
|
26
|
+
});
|
|
24
27
|
this.observer = undefined;
|
|
25
28
|
this.announced = undefined;
|
|
26
29
|
this.onPointerOver = (event) => {
|
|
30
|
+
if (!this.pointer.accepts(event)) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
27
33
|
const cell = event.target?.closest?.('[part="cell"]');
|
|
28
34
|
const index = Number(cell?.getAttribute('data-index') ?? -1);
|
|
29
35
|
this.hovered = Number.isInteger(index) ? index : -1;
|
|
@@ -35,8 +41,21 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
|
|
|
35
41
|
get visible() {
|
|
36
42
|
return Math.min(this.points.length, this.room);
|
|
37
43
|
}
|
|
44
|
+
static get observedAttributes() {
|
|
45
|
+
return [...super.observedAttributes, 'aria-label'];
|
|
46
|
+
}
|
|
47
|
+
attributeChangedCallback(name, old, value) {
|
|
48
|
+
super.attributeChangedCallback(name, old, value);
|
|
49
|
+
if (name === 'aria-label') {
|
|
50
|
+
this.requestUpdate();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
38
53
|
get points() {
|
|
39
|
-
|
|
54
|
+
const series = this.series[0];
|
|
55
|
+
if (!series) {
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
return byRow(series, this.sourceRows.length).map((point) => point ?? { value: undefined });
|
|
40
59
|
}
|
|
41
60
|
get sourceKnown() {
|
|
42
61
|
if (this.for === '') {
|
|
@@ -96,10 +115,6 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
|
|
|
96
115
|
this.observer?.disconnect();
|
|
97
116
|
this.observer = undefined;
|
|
98
117
|
}
|
|
99
|
-
willUpdate(changed) {
|
|
100
|
-
super.willUpdate(changed);
|
|
101
|
-
hostAria(this.internals, { label: this.label || 'Heatmap', role: 'img' });
|
|
102
|
-
}
|
|
103
118
|
markEmpty() {
|
|
104
119
|
markRegion(this.renderRoot, 'footer', ['start', 'summary', 'end']);
|
|
105
120
|
}
|
|
@@ -127,8 +142,10 @@ let HelixHeadlessHeatmap = class HelixHeadlessHeatmap extends MixinChartSource(H
|
|
|
127
142
|
const palette = writtenList(this.tones);
|
|
128
143
|
const cells = this.drawn;
|
|
129
144
|
return html `<div
|
|
145
|
+
aria-label=${this.getAttribute?.('aria-label') || this.named || 'Heatmap'}
|
|
130
146
|
part="strip"
|
|
131
147
|
id=${CHART_PLOT}
|
|
148
|
+
role="img"
|
|
132
149
|
style="--heatmap-rows: ${this.rowCount}"
|
|
133
150
|
@pointerover=${this.onPointerOver}
|
|
134
151
|
@pointerleave=${this.onPointerLeave}
|