@aceshooting/lyra-ui 1.2.0 → 2.0.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/README.md +11 -0
- package/custom-elements.json +23341 -11202
- package/dist/components/app-rail/app-rail.d.ts +171 -0
- package/dist/components/app-rail/app-rail.js +483 -0
- package/dist/components/app-rail/app-rail.styles.d.ts +1 -0
- package/dist/components/app-rail/app-rail.styles.js +125 -0
- package/dist/components/attachment-chip/attachment-chip.d.ts +114 -0
- package/dist/components/attachment-chip/attachment-chip.js +312 -0
- package/dist/components/attachment-chip/attachment-chip.styles.d.ts +1 -0
- package/dist/components/attachment-chip/attachment-chip.styles.js +184 -0
- package/dist/components/attachment-trigger/attachment-trigger.d.ts +83 -0
- package/dist/components/attachment-trigger/attachment-trigger.js +258 -0
- package/dist/components/attachment-trigger/attachment-trigger.styles.d.ts +1 -0
- package/dist/components/attachment-trigger/attachment-trigger.styles.js +68 -0
- package/dist/components/chart/box-plot.js +11 -1
- package/dist/components/chart/chart-loader.d.ts +44 -13
- package/dist/components/chart/chart-loader.js +65 -18
- package/dist/components/chart/chart.d.ts +1 -2
- package/dist/components/chart/chart.js +67 -12
- package/dist/components/chart/histogram-bin.d.ts +4 -1
- package/dist/components/chart/histogram-bin.js +24 -14
- package/dist/components/chart/lite-chart.d.ts +54 -0
- package/dist/components/chart/lite-chart.js +140 -22
- package/dist/components/chart/lite-chart.styles.js +9 -0
- package/dist/components/chat-composer/chat-composer.d.ts +92 -0
- package/dist/components/chat-composer/chat-composer.js +285 -0
- package/dist/components/chat-composer/chat-composer.styles.d.ts +1 -0
- package/dist/components/chat-composer/chat-composer.styles.js +135 -0
- package/dist/components/chat-message/chat-message.d.ts +113 -0
- package/dist/components/chat-message/chat-message.js +291 -0
- package/dist/components/chat-message/chat-message.styles.d.ts +1 -0
- package/dist/components/chat-message/chat-message.styles.js +203 -0
- package/dist/components/checkbox/checkbox.d.ts +59 -0
- package/dist/components/checkbox/checkbox.js +230 -0
- package/dist/components/checkbox/checkbox.styles.d.ts +1 -0
- package/dist/components/checkbox/checkbox.styles.js +70 -0
- package/dist/components/chip/chip-group.d.ts +56 -0
- package/dist/components/chip/chip-group.js +124 -0
- package/dist/components/chip/chip-group.styles.d.ts +1 -0
- package/dist/components/chip/chip-group.styles.js +57 -0
- package/dist/components/chip/chip.d.ts +64 -0
- package/dist/components/chip/chip.js +115 -0
- package/dist/components/chip/chip.styles.d.ts +1 -0
- package/dist/components/chip/chip.styles.js +115 -0
- package/dist/components/citation-badge/citation-badge.d.ts +113 -0
- package/dist/components/citation-badge/citation-badge.js +300 -0
- package/dist/components/citation-badge/citation-badge.styles.d.ts +1 -0
- package/dist/components/citation-badge/citation-badge.styles.js +96 -0
- package/dist/components/code-block/code-block.d.ts +89 -0
- package/dist/components/code-block/code-block.js +317 -0
- package/dist/components/code-block/code-block.styles.d.ts +1 -0
- package/dist/components/code-block/code-block.styles.js +187 -0
- package/dist/components/code-block/code-loader.d.ts +44 -0
- package/dist/components/code-block/code-loader.js +79 -0
- package/dist/components/combobox/combobox.d.ts +19 -1
- package/dist/components/combobox/combobox.js +84 -14
- package/dist/components/combobox/option.d.ts +5 -1
- package/dist/components/combobox/option.js +35 -0
- package/dist/components/context-meter/context-meter.d.ts +53 -0
- package/dist/components/context-meter/context-meter.js +150 -0
- package/dist/components/context-meter/context-meter.styles.d.ts +1 -0
- package/dist/components/context-meter/context-meter.styles.js +120 -0
- package/dist/components/conversation-item/conversation-item.d.ts +128 -0
- package/dist/components/conversation-item/conversation-item.js +322 -0
- package/dist/components/conversation-item/conversation-item.styles.d.ts +1 -0
- package/dist/components/conversation-item/conversation-item.styles.js +155 -0
- package/dist/components/date-picker/date-input.d.ts +24 -0
- package/dist/components/date-picker/date-input.js +134 -22
- package/dist/components/date-picker/date-picker.d.ts +7 -0
- package/dist/components/date-picker/date-picker.js +85 -16
- package/dist/components/dialog/confirm.d.ts +40 -0
- package/dist/components/dialog/confirm.js +79 -0
- package/dist/components/dialog/dialog.d.ts +85 -0
- package/dist/components/dialog/dialog.js +272 -0
- package/dist/components/dialog/dialog.styles.d.ts +1 -0
- package/dist/components/dialog/dialog.styles.js +52 -0
- package/dist/components/dock-panel/dock-panel.d.ts +134 -0
- package/dist/components/dock-panel/dock-panel.js +345 -0
- package/dist/components/dock-panel/dock-panel.styles.d.ts +1 -0
- package/dist/components/dock-panel/dock-panel.styles.js +145 -0
- package/dist/components/document-preview/document-preview.d.ts +135 -0
- package/dist/components/document-preview/document-preview.js +372 -0
- package/dist/components/document-preview/document-preview.styles.d.ts +1 -0
- package/dist/components/document-preview/document-preview.styles.js +191 -0
- package/dist/components/export-button/csv.js +1 -1
- package/dist/components/export-button/export-button.js +2 -0
- package/dist/components/flag/flag.d.ts +29 -13
- package/dist/components/flag/flag.js +39 -13
- package/dist/components/gauge/gauge.js +19 -10
- package/dist/components/generation-status/generation-status.d.ts +109 -0
- package/dist/components/generation-status/generation-status.js +273 -0
- package/dist/components/generation-status/generation-status.styles.d.ts +1 -0
- package/dist/components/generation-status/generation-status.styles.js +84 -0
- package/dist/components/heatmap/calendar-grid.d.ts +4 -1
- package/dist/components/heatmap/calendar-grid.js +25 -12
- package/dist/components/heatmap/heatmap-scale.d.ts +6 -0
- package/dist/components/heatmap/heatmap-scale.js +7 -1
- package/dist/components/heatmap/heatmap.d.ts +74 -0
- package/dist/components/heatmap/heatmap.js +135 -23
- package/dist/components/json-viewer/json-viewer.d.ts +53 -0
- package/dist/components/json-viewer/json-viewer.js +283 -0
- package/dist/components/json-viewer/json-viewer.styles.d.ts +1 -0
- package/dist/components/json-viewer/json-viewer.styles.js +176 -0
- package/dist/components/kbd/kbd.d.ts +77 -0
- package/dist/components/kbd/kbd.js +214 -0
- package/dist/components/kbd/kbd.styles.d.ts +1 -0
- package/dist/components/kbd/kbd.styles.js +52 -0
- package/dist/components/live-region/live-region.d.ts +81 -0
- package/dist/components/live-region/live-region.js +153 -0
- package/dist/components/live-region/live-region.styles.d.ts +1 -0
- package/dist/components/live-region/live-region.styles.js +10 -0
- package/dist/components/map/map.d.ts +8 -6
- package/dist/components/map/map.js +44 -8
- package/dist/components/markdown/markdown-loader.d.ts +34 -0
- package/dist/components/markdown/markdown-loader.js +43 -0
- package/dist/components/markdown/markdown.d.ts +81 -0
- package/dist/components/markdown/markdown.js +284 -0
- package/dist/components/markdown/markdown.styles.d.ts +1 -0
- package/dist/components/markdown/markdown.styles.js +82 -0
- package/dist/components/media-card/media-card.d.ts +122 -0
- package/dist/components/media-card/media-card.js +295 -0
- package/dist/components/media-card/media-card.styles.d.ts +1 -0
- package/dist/components/media-card/media-card.styles.js +132 -0
- package/dist/components/mention-popover/mention-popover.d.ts +200 -0
- package/dist/components/mention-popover/mention-popover.js +475 -0
- package/dist/components/mention-popover/mention-popover.styles.d.ts +1 -0
- package/dist/components/mention-popover/mention-popover.styles.js +101 -0
- package/dist/components/menu/menu-item.d.ts +62 -0
- package/dist/components/menu/menu-item.js +119 -0
- package/dist/components/menu/menu-item.styles.d.ts +1 -0
- package/dist/components/menu/menu-item.styles.js +67 -0
- package/dist/components/menu/menu.d.ts +143 -0
- package/dist/components/menu/menu.js +365 -0
- package/dist/components/menu/menu.styles.d.ts +1 -0
- package/dist/components/menu/menu.styles.js +66 -0
- package/dist/components/model-select/model-select.d.ts +125 -0
- package/dist/components/model-select/model-select.js +490 -0
- package/dist/components/model-select/model-select.styles.d.ts +1 -0
- package/dist/components/model-select/model-select.styles.js +189 -0
- package/dist/components/model-settings-panel/model-settings-panel.d.ts +69 -0
- package/dist/components/model-settings-panel/model-settings-panel.js +145 -0
- package/dist/components/model-settings-panel/model-settings-panel.styles.d.ts +1 -0
- package/dist/components/model-settings-panel/model-settings-panel.styles.js +82 -0
- package/dist/components/playback/playback.d.ts +13 -1
- package/dist/components/playback/playback.js +47 -14
- package/dist/components/responsive-panel/responsive-panel.d.ts +150 -0
- package/dist/components/responsive-panel/responsive-panel.js +365 -0
- package/dist/components/responsive-panel/responsive-panel.styles.d.ts +1 -0
- package/dist/components/responsive-panel/responsive-panel.styles.js +103 -0
- package/dist/components/result-card/result-card.d.ts +44 -0
- package/dist/components/result-card/result-card.js +83 -0
- package/dist/components/result-card/result-card.styles.d.ts +1 -0
- package/dist/components/result-card/result-card.styles.js +50 -0
- package/dist/components/result-card/result-field.d.ts +36 -0
- package/dist/components/result-card/result-field.js +87 -0
- package/dist/components/result-card/result-field.styles.d.ts +1 -0
- package/dist/components/result-card/result-field.styles.js +32 -0
- package/dist/components/select/select.d.ts +43 -2
- package/dist/components/select/select.js +150 -17
- package/dist/components/slider/slider.d.ts +89 -0
- package/dist/components/slider/slider.js +352 -0
- package/dist/components/slider/slider.styles.d.ts +1 -0
- package/dist/components/slider/slider.styles.js +87 -0
- package/dist/components/source-card/source-card.d.ts +92 -0
- package/dist/components/source-card/source-card.js +148 -0
- package/dist/components/source-card/source-card.styles.d.ts +1 -0
- package/dist/components/source-card/source-card.styles.js +69 -0
- package/dist/components/source-list/source-list.d.ts +63 -0
- package/dist/components/source-list/source-list.js +122 -0
- package/dist/components/source-list/source-list.styles.d.ts +1 -0
- package/dist/components/source-list/source-list.styles.js +59 -0
- package/dist/components/sparkline/sparkline.js +27 -13
- package/dist/components/split/split.d.ts +93 -1
- package/dist/components/split/split.js +284 -15
- package/dist/components/split/split.styles.js +30 -0
- package/dist/components/stat/stat.d.ts +6 -1
- package/dist/components/stat/stat.js +8 -2
- package/dist/components/stream-status/stream-status.d.ts +110 -0
- package/dist/components/stream-status/stream-status.js +210 -0
- package/dist/components/stream-status/stream-status.styles.d.ts +1 -0
- package/dist/components/stream-status/stream-status.styles.js +103 -0
- package/dist/components/streaming-text/streaming-text.d.ts +98 -0
- package/dist/components/streaming-text/streaming-text.js +209 -0
- package/dist/components/streaming-text/streaming-text.styles.d.ts +1 -0
- package/dist/components/streaming-text/streaming-text.styles.js +62 -0
- package/dist/components/switch/switch.d.ts +58 -0
- package/dist/components/switch/switch.js +179 -0
- package/dist/components/switch/switch.styles.d.ts +1 -0
- package/dist/components/switch/switch.styles.js +75 -0
- package/dist/components/table/table.d.ts +73 -4
- package/dist/components/table/table.js +169 -20
- package/dist/components/table/table.styles.js +6 -1
- package/dist/components/tabs/tabs.d.ts +51 -0
- package/dist/components/tabs/tabs.js +190 -0
- package/dist/components/tabs/tabs.styles.d.ts +1 -0
- package/dist/components/tabs/tabs.styles.js +63 -0
- package/dist/components/thinking-panel/thinking-panel.d.ts +121 -0
- package/dist/components/thinking-panel/thinking-panel.js +235 -0
- package/dist/components/thinking-panel/thinking-panel.styles.d.ts +1 -0
- package/dist/components/thinking-panel/thinking-panel.styles.js +117 -0
- package/dist/components/toast/toast-item.d.ts +1 -0
- package/dist/components/toast/toast-item.js +31 -6
- package/dist/components/tool-approval-dialog/tool-approval-dialog.d.ts +130 -0
- package/dist/components/tool-approval-dialog/tool-approval-dialog.js +403 -0
- package/dist/components/tool-approval-dialog/tool-approval-dialog.styles.d.ts +1 -0
- package/dist/components/tool-approval-dialog/tool-approval-dialog.styles.js +160 -0
- package/dist/components/tool-call-chip/tool-call-chip.d.ts +89 -0
- package/dist/components/tool-call-chip/tool-call-chip.js +288 -0
- package/dist/components/tool-call-chip/tool-call-chip.styles.d.ts +1 -0
- package/dist/components/tool-call-chip/tool-call-chip.styles.js +203 -0
- package/dist/components/tool-param-form/tool-param-form.d.ts +145 -0
- package/dist/components/tool-param-form/tool-param-form.js +366 -0
- package/dist/components/tool-param-form/tool-param-form.styles.d.ts +1 -0
- package/dist/components/tool-param-form/tool-param-form.styles.js +75 -0
- package/dist/components/tool-result-dialog/tool-result-dialog.d.ts +102 -0
- package/dist/components/tool-result-dialog/tool-result-dialog.js +393 -0
- package/dist/components/tool-result-dialog/tool-result-dialog.styles.d.ts +1 -0
- package/dist/components/tool-result-dialog/tool-result-dialog.styles.js +176 -0
- package/dist/components/tool-result-view/registry.d.ts +81 -0
- package/dist/components/tool-result-view/registry.js +84 -0
- package/dist/components/tool-result-view/tool-result-view.d.ts +55 -0
- package/dist/components/tool-result-view/tool-result-view.js +138 -0
- package/dist/components/tool-result-view/tool-result-view.styles.d.ts +1 -0
- package/dist/components/tool-result-view/tool-result-view.styles.js +13 -0
- package/dist/components/tool-select-dialog/tool-select-dialog.d.ts +151 -0
- package/dist/components/tool-select-dialog/tool-select-dialog.js +439 -0
- package/dist/components/tool-select-dialog/tool-select-dialog.styles.d.ts +1 -0
- package/dist/components/tool-select-dialog/tool-select-dialog.styles.js +182 -0
- package/dist/components/tree/tree-node.d.ts +11 -2
- package/dist/components/tree/tree-node.js +16 -6
- package/dist/components/tree/tree.d.ts +8 -2
- package/dist/components/tree/tree.js +18 -11
- package/dist/components/typing-indicator/typing-indicator.d.ts +60 -0
- package/dist/components/typing-indicator/typing-indicator.js +96 -0
- package/dist/components/typing-indicator/typing-indicator.styles.d.ts +1 -0
- package/dist/components/typing-indicator/typing-indicator.styles.js +132 -0
- package/dist/components/virtual-list/virtual-list.d.ts +173 -0
- package/dist/components/virtual-list/virtual-list.js +461 -0
- package/dist/components/virtual-list/virtual-list.styles.d.ts +1 -0
- package/dist/components/virtual-list/virtual-list.styles.js +44 -0
- package/dist/components/word-cloud/word-cloud-layout.js +14 -1
- package/dist/components/word-cloud/word-cloud.d.ts +6 -0
- package/dist/components/word-cloud/word-cloud.js +25 -14
- package/dist/components/word-cloud/word-cloud.styles.js +3 -2
- package/dist/internal/announcer.d.ts +57 -0
- package/dist/internal/announcer.js +71 -0
- package/dist/internal/form-associated.d.ts +1 -0
- package/dist/internal/form-associated.js +55 -11
- package/dist/internal/group-by-recency.d.ts +50 -0
- package/dist/internal/group-by-recency.js +80 -0
- package/dist/internal/rtl.d.ts +23 -0
- package/dist/internal/rtl.js +28 -0
- package/dist/internal/scroll-lock.d.ts +10 -5
- package/dist/internal/scroll-lock.js +30 -14
- package/dist/internal/tokens.styles.js +6 -0
- package/dist/lyra.d.ts +130 -31
- package/dist/lyra.js +94 -26
- package/llms-full.txt +3836 -17
- package/llms.txt +16 -1
- package/package.json +23 -5
- package/dist/components/chart/bar-chart.stories.d.ts +0 -5
- package/dist/components/chart/bar-chart.stories.js +0 -20
- package/dist/components/chart/box-plot.stories.d.ts +0 -5
- package/dist/components/chart/box-plot.stories.js +0 -29
- package/dist/components/chart/bubble-chart.stories.d.ts +0 -5
- package/dist/components/chart/bubble-chart.stories.js +0 -26
- package/dist/components/chart/chart.stories.d.ts +0 -32
- package/dist/components/chart/chart.stories.js +0 -141
- package/dist/components/chart/doughnut-chart.stories.d.ts +0 -5
- package/dist/components/chart/doughnut-chart.stories.js +0 -26
- package/dist/components/chart/histogram.stories.d.ts +0 -5
- package/dist/components/chart/histogram.stories.js +0 -17
- package/dist/components/chart/line-chart.stories.d.ts +0 -22
- package/dist/components/chart/line-chart.stories.js +0 -99
- package/dist/components/chart/lite-chart.stories.d.ts +0 -14
- package/dist/components/chart/lite-chart.stories.js +0 -114
- package/dist/components/chart/pie-chart.stories.d.ts +0 -5
- package/dist/components/chart/pie-chart.stories.js +0 -26
- package/dist/components/chart/polar-area-chart.stories.d.ts +0 -5
- package/dist/components/chart/polar-area-chart.stories.js +0 -20
- package/dist/components/chart/radar-chart.stories.d.ts +0 -5
- package/dist/components/chart/radar-chart.stories.js +0 -20
- package/dist/components/chart/scatter-chart.stories.d.ts +0 -5
- package/dist/components/chart/scatter-chart.stories.js +0 -31
- package/dist/components/combobox/combobox.stories.d.ts +0 -24
- package/dist/components/combobox/combobox.stories.js +0 -102
- package/dist/components/date-picker/date-input.stories.d.ts +0 -10
- package/dist/components/date-picker/date-input.stories.js +0 -48
- package/dist/components/date-picker/date-picker.stories.d.ts +0 -10
- package/dist/components/date-picker/date-picker.stories.js +0 -25
- package/dist/components/empty/empty.stories.d.ts +0 -7
- package/dist/components/empty/empty.stories.js +0 -34
- package/dist/components/export-button/export-button.stories.d.ts +0 -7
- package/dist/components/export-button/export-button.stories.js +0 -37
- package/dist/components/file-input/file-input.stories.d.ts +0 -7
- package/dist/components/file-input/file-input.stories.js +0 -19
- package/dist/components/flag/flag.stories.d.ts +0 -6
- package/dist/components/flag/flag.stories.js +0 -31
- package/dist/components/gauge/gauge.stories.d.ts +0 -8
- package/dist/components/gauge/gauge.stories.js +0 -19
- package/dist/components/graph/graph.stories.d.ts +0 -8
- package/dist/components/graph/graph.stories.js +0 -68
- package/dist/components/heatmap/heatmap.stories.d.ts +0 -27
- package/dist/components/heatmap/heatmap.stories.js +0 -179
- package/dist/components/map/map.stories.d.ts +0 -15
- package/dist/components/map/map.stories.js +0 -135
- package/dist/components/playback/playback.stories.d.ts +0 -7
- package/dist/components/playback/playback.stories.js +0 -16
- package/dist/components/select/select.stories.d.ts +0 -21
- package/dist/components/select/select.stories.js +0 -91
- package/dist/components/skeleton/skeleton.stories.d.ts +0 -5
- package/dist/components/skeleton/skeleton.stories.js +0 -16
- package/dist/components/sparkline/sparkline.stories.d.ts +0 -11
- package/dist/components/sparkline/sparkline.stories.js +0 -39
- package/dist/components/split/split.stories.d.ts +0 -7
- package/dist/components/split/split.stories.js +0 -36
- package/dist/components/stat/stat.stories.d.ts +0 -9
- package/dist/components/stat/stat.stories.js +0 -81
- package/dist/components/table/table.stories.d.ts +0 -12
- package/dist/components/table/table.stories.js +0 -67
- package/dist/components/time-range/time-range.stories.d.ts +0 -8
- package/dist/components/time-range/time-range.stories.js +0 -26
- package/dist/components/toast/toast.stories.d.ts +0 -8
- package/dist/components/toast/toast.stories.js +0 -72
- package/dist/components/tree/tree.stories.d.ts +0 -9
- package/dist/components/tree/tree.stories.js +0 -52
- package/dist/components/widget/widget.stories.d.ts +0 -9
- package/dist/components/widget/widget.stories.js +0 -74
- package/dist/components/word-cloud/word-cloud.stories.d.ts +0 -10
- package/dist/components/word-cloud/word-cloud.stories.js +0 -55
|
@@ -5,6 +5,12 @@ export function parseIsoDate(iso) {
|
|
|
5
5
|
const [y, m, d] = iso.split('-').map(Number);
|
|
6
6
|
return new Date(Date.UTC(y, m - 1, d));
|
|
7
7
|
}
|
|
8
|
+
/** Whether `iso` (already run through parseIsoDate) actually round-trips —
|
|
9
|
+
* Date.UTC silently rolls e.g. day 30 of a 28-day February into March. */
|
|
10
|
+
function isCalendarValid(iso, parsed) {
|
|
11
|
+
const [y, m, d] = iso.split('-').map(Number);
|
|
12
|
+
return parsed.getUTCFullYear() === y && parsed.getUTCMonth() === m - 1 && parsed.getUTCDate() === d;
|
|
13
|
+
}
|
|
8
14
|
/**
|
|
9
15
|
* Lays `days` out onto a GitHub-style Sunday-Saturday x week grid. `days`
|
|
10
16
|
* need not be sorted or contiguous; the grid spans from the earliest to the
|
|
@@ -15,12 +21,15 @@ export function parseIsoDate(iso) {
|
|
|
15
21
|
* string yields an `Invalid Date` (`getTime()` is `NaN`), and letting that
|
|
16
22
|
* single `NaN` reach the min/max below would poison `firstWeekStart` (and
|
|
17
23
|
* therefore every cell's `week`) for the whole grid instead of just the bad
|
|
18
|
-
* entry.
|
|
24
|
+
* entry. Likewise, an entry whose `date` is numerically well-formed but
|
|
25
|
+
* calendar-invalid (e.g. `'2026-02-30'`, which `Date.UTC` silently rolls
|
|
26
|
+
* over into March) is dropped rather than silently renormalized — see
|
|
27
|
+
* `isCalendarValid()`.
|
|
19
28
|
*/
|
|
20
29
|
export function buildCalendarGrid(days) {
|
|
21
30
|
const parsed = days
|
|
22
31
|
.map((d) => ({ ...d, dateObj: parseIsoDate(d.date) }))
|
|
23
|
-
.filter((d) => !Number.isNaN(d.dateObj.getTime()));
|
|
32
|
+
.filter((d) => !Number.isNaN(d.dateObj.getTime()) && isCalendarValid(d.date, d.dateObj));
|
|
24
33
|
if (parsed.length === 0) {
|
|
25
34
|
return { cells: [], weekCount: 0, firstWeekStart: new Date(0), monthLabels: [] };
|
|
26
35
|
}
|
|
@@ -34,18 +43,22 @@ export function buildCalendarGrid(days) {
|
|
|
34
43
|
return { date: d.date, value: d.value, week, weekday };
|
|
35
44
|
});
|
|
36
45
|
const weekCount = minMax(cells.map((c) => c.week))[1] + 1;
|
|
46
|
+
// Label every distinct (year, month) present in the data, not just months
|
|
47
|
+
// that happen to contain a Sunday-anchored cell — a sparse calendar (few
|
|
48
|
+
// days per month, none landing on a Sunday) would otherwise render zero
|
|
49
|
+
// month labels for months that do have data.
|
|
37
50
|
const monthLabels = [];
|
|
38
|
-
|
|
39
|
-
for (const cell of cells.slice().sort((a, b) => a.week - b.week)) {
|
|
51
|
+
const seenMonths = new Set();
|
|
52
|
+
for (const cell of cells.slice().sort((a, b) => a.week - b.week || a.weekday - b.weekday)) {
|
|
40
53
|
const cellDate = parseIsoDate(cell.date);
|
|
41
|
-
const
|
|
42
|
-
if (
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
})
|
|
48
|
-
}
|
|
54
|
+
const key = `${cellDate.getUTCFullYear()}-${cellDate.getUTCMonth()}`;
|
|
55
|
+
if (seenMonths.has(key))
|
|
56
|
+
continue;
|
|
57
|
+
seenMonths.add(key);
|
|
58
|
+
monthLabels.push({
|
|
59
|
+
week: cell.week,
|
|
60
|
+
label: cellDate.toLocaleString(undefined, { month: 'short', timeZone: 'UTC' }),
|
|
61
|
+
});
|
|
49
62
|
}
|
|
50
63
|
return { cells, weekCount, firstWeekStart, monthLabels };
|
|
51
64
|
}
|
|
@@ -12,5 +12,11 @@ export declare function minMax(values: number[]): [number, number] | null;
|
|
|
12
12
|
* Square-root-scaled bucket index in `[0, steps-1]`, or `-1` for no-data.
|
|
13
13
|
* Compresses large counts so a single heavy cell doesn't wash out the rest
|
|
14
14
|
* of a sequential color ramp.
|
|
15
|
+
*
|
|
16
|
+
* Only a negative `count` means "no data" here — the true no-data sentinel
|
|
17
|
+
* (`v < 0 || !Number.isFinite(v)`) is already filtered out by the caller
|
|
18
|
+
* (`drawMatrix()`) before `sqrtStep` is ever invoked, so a real `count === 0`
|
|
19
|
+
* (e.g. "zero events that day") reaches this function and must bucket to the
|
|
20
|
+
* lowest ramp step like any other legitimate value, not render as no-data.
|
|
15
21
|
*/
|
|
16
22
|
export declare function sqrtStep(count: number, max: number, steps: number): number;
|
|
@@ -29,9 +29,15 @@ export function minMax(values) {
|
|
|
29
29
|
* Square-root-scaled bucket index in `[0, steps-1]`, or `-1` for no-data.
|
|
30
30
|
* Compresses large counts so a single heavy cell doesn't wash out the rest
|
|
31
31
|
* of a sequential color ramp.
|
|
32
|
+
*
|
|
33
|
+
* Only a negative `count` means "no data" here — the true no-data sentinel
|
|
34
|
+
* (`v < 0 || !Number.isFinite(v)`) is already filtered out by the caller
|
|
35
|
+
* (`drawMatrix()`) before `sqrtStep` is ever invoked, so a real `count === 0`
|
|
36
|
+
* (e.g. "zero events that day") reaches this function and must bucket to the
|
|
37
|
+
* lowest ramp step like any other legitimate value, not render as no-data.
|
|
32
38
|
*/
|
|
33
39
|
export function sqrtStep(count, max, steps) {
|
|
34
|
-
if (count
|
|
40
|
+
if (count < 0 || max <= 0)
|
|
35
41
|
return -1;
|
|
36
42
|
const ratio = Math.sqrt(count) / Math.sqrt(max);
|
|
37
43
|
return Math.min(steps - 1, Math.floor(ratio * steps));
|
|
@@ -79,6 +79,12 @@ export declare function mixColor(fromColor: string, toColor: string, t: number):
|
|
|
79
79
|
* specific cells (e.g. to call out an anomaly), each one optionally
|
|
80
80
|
* surfaced in the legend too via `[part="legend-annotation"]`.
|
|
81
81
|
*
|
|
82
|
+
* `columnX` (calendar mode only) overrides the x-origin computed for each
|
|
83
|
+
* week column — drawing, hit-testing, the focus ring, and month-label
|
|
84
|
+
* positioning all consult it consistently, so a consumer can pixel-align a
|
|
85
|
+
* calendar's week columns with a sibling chart's coordinate system. Unset
|
|
86
|
+
* (the default) keeps the original evenly-spaced formula.
|
|
87
|
+
*
|
|
82
88
|
* @customElement lyra-heatmap
|
|
83
89
|
* @event lyra-cell-click - Fired on click, or Enter/Space on the
|
|
84
90
|
* focused/hovered cell. `detail: { row, col, value }` in matrix mode,
|
|
@@ -113,6 +119,19 @@ export declare class LyraHeatmap extends LyraElement {
|
|
|
113
119
|
* (`MatrixCellPos` in matrix mode, `CalendarCellPos` in calendar mode) and its value. Falls back to
|
|
114
120
|
* the built-in English "Row X, Col Y: value" / "Mon DD: value" template when unset. */
|
|
115
121
|
cellText?: (pos: MatrixCellPos | CalendarCellPos, value: number) => string;
|
|
122
|
+
/**
|
|
123
|
+
* Calendar mode only: overrides the x-origin (canvas-local CSS px) of week
|
|
124
|
+
* column `index` (0-based, same indexing as `CalendarCellPos.week`).
|
|
125
|
+
* Consulted consistently by every calendar-mode geometry call site —
|
|
126
|
+
* drawing, hit-testing, the keyboard focus ring, and month-label
|
|
127
|
+
* positioning — via the private `columnXFor()` helper, so painted
|
|
128
|
+
* geometry and pointer hit-testing never disagree. Lets a consumer
|
|
129
|
+
* pixel-align a calendar's week columns with a sibling chart's bars by
|
|
130
|
+
* supplying that chart the same coordinate function. Unset (the default)
|
|
131
|
+
* keeps today's evenly-spaced `CAL_PAD_LEFT + week * (CAL_CELL + CAL_GAP)`
|
|
132
|
+
* formula unchanged. Ignored in matrix mode.
|
|
133
|
+
*/
|
|
134
|
+
columnX?: (index: number) => number;
|
|
116
135
|
private canvas?;
|
|
117
136
|
private resizeObserver?;
|
|
118
137
|
private dprQuery?;
|
|
@@ -125,6 +144,19 @@ export declare class LyraHeatmap extends LyraElement {
|
|
|
125
144
|
private focusedCell;
|
|
126
145
|
/** Text of the visually-hidden `aria-live="polite"` status announcement, refreshed on every focus move. */
|
|
127
146
|
private liveText;
|
|
147
|
+
/**
|
|
148
|
+
* Whether `role`/`aria-label` were present on the host *before* this
|
|
149
|
+
* element's own code ever wrote to them — snapshotted once, in the very
|
|
150
|
+
* first `willUpdate()` (guarded by `hasUpdated`, which Lit only flips to
|
|
151
|
+
* `true` after that first update commits). Re-checking `hasAttribute()` on
|
|
152
|
+
* every update instead would self-poison: once this element writes its own
|
|
153
|
+
* default `aria-label` on the first render, `hasAttribute('aria-label')`
|
|
154
|
+
* is permanently `true` afterwards, which would wrongly look like an
|
|
155
|
+
* author-supplied value and freeze the label instead of refreshing it as
|
|
156
|
+
* `values`/`days` change on later updates.
|
|
157
|
+
*/
|
|
158
|
+
private authorSuppliedRole;
|
|
159
|
+
private authorSuppliedAriaLabel;
|
|
128
160
|
connectedCallback(): void;
|
|
129
161
|
disconnectedCallback(): void;
|
|
130
162
|
private watchDpr;
|
|
@@ -154,6 +186,40 @@ export declare class LyraHeatmap extends LyraElement {
|
|
|
154
186
|
/** Reads the customizable canvas-drawn annotation-ring stroke color off the host's computed style. */
|
|
155
187
|
private annotationColor;
|
|
156
188
|
private draw;
|
|
189
|
+
/**
|
|
190
|
+
* Calendar-mode week-column x-origin (canvas-local CSS px) — `columnX(week)`
|
|
191
|
+
* when set, otherwise the original evenly-spaced formula. Shared by every
|
|
192
|
+
* calendar-mode drawing and hit-testing call site (`drawCalendar()`,
|
|
193
|
+
* `hitTestCalendar()`/`weekAtX()`, `cellRect()`) so painted geometry and
|
|
194
|
+
* interactive hit-testing never disagree — mirrors the existing
|
|
195
|
+
* `matrixCellSize()` invariant for matrix mode's
|
|
196
|
+
* `drawMatrix()`/`hitTestMatrix()`/`cellRect()`.
|
|
197
|
+
*/
|
|
198
|
+
private columnXFor;
|
|
199
|
+
/**
|
|
200
|
+
* Inverse of `columnXFor()`: resolves an x position (canvas-local CSS px)
|
|
201
|
+
* to the week column it falls in, or `null` if it's outside every column
|
|
202
|
+
* (`weekCount` is 0, or `x` doesn't land inside `[0, weekCount)`'s span).
|
|
203
|
+
* The default spacing has a closed-form inverse (division); an arbitrary
|
|
204
|
+
* `columnX` override doesn't, so that case instead scans each column's
|
|
205
|
+
* `[columnXFor(week), columnXFor(week + 1))` span — algebraically the same
|
|
206
|
+
* span the default formula's division derives — so hit-testing always
|
|
207
|
+
* agrees with wherever `columnXFor()` actually painted that column.
|
|
208
|
+
*/
|
|
209
|
+
private weekAtX;
|
|
210
|
+
/**
|
|
211
|
+
* Locale-derived weekday-axis labels for the Sunday..Saturday rows drawn
|
|
212
|
+
* down the left of the calendar grid. Only indices 1 (Mon), 3 (Wed), and 5
|
|
213
|
+
* (Fri) are filled in — matching today's sparse every-other-day label
|
|
214
|
+
* density — the rest stay blank. `firstWeekStart` is always a UTC Sunday
|
|
215
|
+
* by construction (see `buildCalendarGrid()`), so weekday index 0..6 is a
|
|
216
|
+
* fixed Sun..Sat positional mapping; only the label *text* for indices
|
|
217
|
+
* 1/3/5 is derived here, via `Intl.DateTimeFormat` on a real UTC date that
|
|
218
|
+
* actually falls on that weekday, so it follows the runtime locale instead
|
|
219
|
+
* of a hardcoded English array (see `calendarCellText()`'s tooltip text
|
|
220
|
+
* for the same pattern).
|
|
221
|
+
*/
|
|
222
|
+
private weekdayLabels;
|
|
157
223
|
private drawCalendar;
|
|
158
224
|
/**
|
|
159
225
|
* Effective per-cell size in matrix mode — `fitToWidth` derives it from
|
|
@@ -207,6 +273,14 @@ export declare class LyraHeatmap extends LyraElement {
|
|
|
207
273
|
private emitCellClick;
|
|
208
274
|
/** Inline `style` for `[part="tooltip"]`, centered above the given cell. */
|
|
209
275
|
private tooltipStyle;
|
|
276
|
+
/**
|
|
277
|
+
* Value-equality check for a `CellPos`, since `hoverCell` is assigned a
|
|
278
|
+
* fresh object literal on every `pointermove` — Lit's `@state()`
|
|
279
|
+
* change-detection only no-ops an identical *reference*, so without this,
|
|
280
|
+
* `{row,col} !== {row,col}` by reference triggers a full canvas clear +
|
|
281
|
+
* redraw on every pixel of pointer movement, even within the same cell.
|
|
282
|
+
*/
|
|
283
|
+
private samePos;
|
|
210
284
|
private onPointerMove;
|
|
211
285
|
private onPointerLeave;
|
|
212
286
|
private onCanvasClick;
|
|
@@ -167,6 +167,12 @@ export function mixColor(fromColor, toColor, t) {
|
|
|
167
167
|
* specific cells (e.g. to call out an anomaly), each one optionally
|
|
168
168
|
* surfaced in the legend too via `[part="legend-annotation"]`.
|
|
169
169
|
*
|
|
170
|
+
* `columnX` (calendar mode only) overrides the x-origin computed for each
|
|
171
|
+
* week column — drawing, hit-testing, the focus ring, and month-label
|
|
172
|
+
* positioning all consult it consistently, so a consumer can pixel-align a
|
|
173
|
+
* calendar's week columns with a sibling chart's coordinate system. Unset
|
|
174
|
+
* (the default) keeps the original evenly-spaced formula.
|
|
175
|
+
*
|
|
170
176
|
* @customElement lyra-heatmap
|
|
171
177
|
* @event lyra-cell-click - Fired on click, or Enter/Space on the
|
|
172
178
|
* focused/hovered cell. `detail: { row, col, value }` in matrix mode,
|
|
@@ -207,21 +213,37 @@ export class LyraHeatmap extends LyraElement {
|
|
|
207
213
|
this.focusedCell = null;
|
|
208
214
|
/** Text of the visually-hidden `aria-live="polite"` status announcement, refreshed on every focus move. */
|
|
209
215
|
this.liveText = '';
|
|
216
|
+
/**
|
|
217
|
+
* Whether `role`/`aria-label` were present on the host *before* this
|
|
218
|
+
* element's own code ever wrote to them — snapshotted once, in the very
|
|
219
|
+
* first `willUpdate()` (guarded by `hasUpdated`, which Lit only flips to
|
|
220
|
+
* `true` after that first update commits). Re-checking `hasAttribute()` on
|
|
221
|
+
* every update instead would self-poison: once this element writes its own
|
|
222
|
+
* default `aria-label` on the first render, `hasAttribute('aria-label')`
|
|
223
|
+
* is permanently `true` afterwards, which would wrongly look like an
|
|
224
|
+
* author-supplied value and freeze the label instead of refreshing it as
|
|
225
|
+
* `values`/`days` change on later updates.
|
|
226
|
+
*/
|
|
227
|
+
this.authorSuppliedRole = false;
|
|
228
|
+
this.authorSuppliedAriaLabel = false;
|
|
210
229
|
this.onDprChange = () => {
|
|
211
230
|
this.watchDpr();
|
|
212
231
|
this.draw();
|
|
213
232
|
};
|
|
214
233
|
this.onPointerMove = (e) => {
|
|
215
|
-
|
|
234
|
+
const next = this.hitTest(e.offsetX, e.offsetY);
|
|
235
|
+
if (!this.samePos(this.hoverCell, next))
|
|
236
|
+
this.hoverCell = next;
|
|
216
237
|
};
|
|
217
238
|
this.onPointerLeave = () => {
|
|
218
239
|
this.hoverCell = null;
|
|
219
240
|
};
|
|
220
241
|
this.onCanvasClick = (e) => {
|
|
221
|
-
//
|
|
222
|
-
//
|
|
223
|
-
//
|
|
224
|
-
|
|
242
|
+
// Only the click's own hit-tested position counts — a click that lands
|
|
243
|
+
// outside the grid (e.g. on the canvas's own padding) is a genuine
|
|
244
|
+
// out-of-grid click and should do nothing, not fall back to whatever
|
|
245
|
+
// cell happens to still be keyboard-focused from an earlier interaction.
|
|
246
|
+
const pos = this.hitTest(e.offsetX, e.offsetY);
|
|
225
247
|
if (!pos)
|
|
226
248
|
return;
|
|
227
249
|
this.focusedCell = pos;
|
|
@@ -269,17 +291,31 @@ export class LyraHeatmap extends LyraElement {
|
|
|
269
291
|
this.hoverCell = null;
|
|
270
292
|
this.liveText = '';
|
|
271
293
|
}
|
|
294
|
+
if (!this.hasUpdated) {
|
|
295
|
+
this.authorSuppliedRole = this.hasAttribute('role');
|
|
296
|
+
this.authorSuppliedAriaLabel = this.hasAttribute('aria-label');
|
|
297
|
+
}
|
|
272
298
|
this.cachedValueRange = this.computeValueRange();
|
|
273
299
|
const bounds = this.cachedValueRange;
|
|
274
300
|
const range = bounds ? `${bounds[0]}–${bounds[1]}` : 'no data';
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
301
|
+
// Only default role/aria-label when the author hasn't already supplied
|
|
302
|
+
// one — the canvas is a genuinely focusable/interactive control
|
|
303
|
+
// (tabindex="0", click/keydown handlers) plus a live role="status"
|
|
304
|
+
// region, so unconditionally forcing a role here would tell assistive
|
|
305
|
+
// tech to flatten the whole subtree, hiding both from the accessibility
|
|
306
|
+
// tree; it would also silently overwrite any author-supplied
|
|
307
|
+
// role/aria-label on every update.
|
|
308
|
+
if (!this.authorSuppliedRole)
|
|
309
|
+
this.setAttribute('role', 'group');
|
|
310
|
+
if (!this.authorSuppliedAriaLabel) {
|
|
311
|
+
if (this.mode === 'calendar') {
|
|
312
|
+
this.setAttribute('aria-label', `Calendar heatmap of ${this.days.length} days, ${this.valueLabel} range ${range}`);
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
const rows = this.rowLabels.length;
|
|
316
|
+
const cols = this.colLabels.length;
|
|
317
|
+
this.setAttribute('aria-label', `Heatmap of ${rows} × ${cols} cells, ${this.valueLabel} range ${range}`);
|
|
318
|
+
}
|
|
283
319
|
}
|
|
284
320
|
}
|
|
285
321
|
/**
|
|
@@ -331,11 +367,66 @@ export class LyraHeatmap extends LyraElement {
|
|
|
331
367
|
else
|
|
332
368
|
this.drawMatrix();
|
|
333
369
|
}
|
|
370
|
+
/**
|
|
371
|
+
* Calendar-mode week-column x-origin (canvas-local CSS px) — `columnX(week)`
|
|
372
|
+
* when set, otherwise the original evenly-spaced formula. Shared by every
|
|
373
|
+
* calendar-mode drawing and hit-testing call site (`drawCalendar()`,
|
|
374
|
+
* `hitTestCalendar()`/`weekAtX()`, `cellRect()`) so painted geometry and
|
|
375
|
+
* interactive hit-testing never disagree — mirrors the existing
|
|
376
|
+
* `matrixCellSize()` invariant for matrix mode's
|
|
377
|
+
* `drawMatrix()`/`hitTestMatrix()`/`cellRect()`.
|
|
378
|
+
*/
|
|
379
|
+
columnXFor(week) {
|
|
380
|
+
return this.columnX ? this.columnX(week) : CAL_PAD_LEFT + week * (CAL_CELL + CAL_GAP);
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Inverse of `columnXFor()`: resolves an x position (canvas-local CSS px)
|
|
384
|
+
* to the week column it falls in, or `null` if it's outside every column
|
|
385
|
+
* (`weekCount` is 0, or `x` doesn't land inside `[0, weekCount)`'s span).
|
|
386
|
+
* The default spacing has a closed-form inverse (division); an arbitrary
|
|
387
|
+
* `columnX` override doesn't, so that case instead scans each column's
|
|
388
|
+
* `[columnXFor(week), columnXFor(week + 1))` span — algebraically the same
|
|
389
|
+
* span the default formula's division derives — so hit-testing always
|
|
390
|
+
* agrees with wherever `columnXFor()` actually painted that column.
|
|
391
|
+
*/
|
|
392
|
+
weekAtX(x, weekCount) {
|
|
393
|
+
if (!this.columnX) {
|
|
394
|
+
const week = Math.floor((x - CAL_PAD_LEFT) / (CAL_CELL + CAL_GAP));
|
|
395
|
+
return week >= 0 && week < weekCount ? week : null;
|
|
396
|
+
}
|
|
397
|
+
for (let week = 0; week < weekCount; week++) {
|
|
398
|
+
const start = this.columnXFor(week);
|
|
399
|
+
const end = week + 1 < weekCount ? this.columnXFor(week + 1) : start + (CAL_CELL + CAL_GAP);
|
|
400
|
+
if (x >= start && x < end)
|
|
401
|
+
return week;
|
|
402
|
+
}
|
|
403
|
+
return null;
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Locale-derived weekday-axis labels for the Sunday..Saturday rows drawn
|
|
407
|
+
* down the left of the calendar grid. Only indices 1 (Mon), 3 (Wed), and 5
|
|
408
|
+
* (Fri) are filled in — matching today's sparse every-other-day label
|
|
409
|
+
* density — the rest stay blank. `firstWeekStart` is always a UTC Sunday
|
|
410
|
+
* by construction (see `buildCalendarGrid()`), so weekday index 0..6 is a
|
|
411
|
+
* fixed Sun..Sat positional mapping; only the label *text* for indices
|
|
412
|
+
* 1/3/5 is derived here, via `Intl.DateTimeFormat` on a real UTC date that
|
|
413
|
+
* actually falls on that weekday, so it follows the runtime locale instead
|
|
414
|
+
* of a hardcoded English array (see `calendarCellText()`'s tooltip text
|
|
415
|
+
* for the same pattern).
|
|
416
|
+
*/
|
|
417
|
+
weekdayLabels(firstWeekStart) {
|
|
418
|
+
const formatter = new Intl.DateTimeFormat(undefined, { weekday: 'short', timeZone: 'UTC' });
|
|
419
|
+
const labels = ['', '', '', '', '', '', ''];
|
|
420
|
+
for (const weekday of [1, 3, 5]) {
|
|
421
|
+
labels[weekday] = formatter.format(new Date(firstWeekStart.getTime() + weekday * MS_PER_DAY));
|
|
422
|
+
}
|
|
423
|
+
return labels;
|
|
424
|
+
}
|
|
334
425
|
drawCalendar() {
|
|
335
426
|
if (!this.canvas)
|
|
336
427
|
return;
|
|
337
|
-
const { cells, weekCount, monthLabels } = buildCalendarGrid(this.days);
|
|
338
|
-
const w =
|
|
428
|
+
const { cells, weekCount, firstWeekStart, monthLabels } = buildCalendarGrid(this.days);
|
|
429
|
+
const w = this.columnXFor(Math.max(1, weekCount));
|
|
339
430
|
const h = CAL_LABEL_H + 7 * (CAL_CELL + CAL_GAP);
|
|
340
431
|
const dpr = window.devicePixelRatio || 1;
|
|
341
432
|
this.canvas.width = w * dpr;
|
|
@@ -358,7 +449,7 @@ export class LyraHeatmap extends LyraElement {
|
|
|
358
449
|
const ramp = Array.from({ length: buckets }, (_, i) => mixRgb(loRgb, hiRgb, i / (buckets - 1)));
|
|
359
450
|
const noDataFill = this.noDataFill();
|
|
360
451
|
for (const cell of cells) {
|
|
361
|
-
const x =
|
|
452
|
+
const x = this.columnXFor(cell.week);
|
|
362
453
|
const y = CAL_LABEL_H + cell.weekday * (CAL_CELL + CAL_GAP);
|
|
363
454
|
ctx.fillStyle =
|
|
364
455
|
cell.value < 0 || !Number.isFinite(cell.value)
|
|
@@ -378,7 +469,7 @@ export class LyraHeatmap extends LyraElement {
|
|
|
378
469
|
const match = cells.find((c) => c.date === ann.date);
|
|
379
470
|
if (!match)
|
|
380
471
|
continue;
|
|
381
|
-
const x =
|
|
472
|
+
const x = this.columnXFor(match.week);
|
|
382
473
|
const y = CAL_LABEL_H + match.weekday * (CAL_CELL + CAL_GAP);
|
|
383
474
|
ctx.strokeRect(x + 0.5, y + 0.5, CAL_CELL - 1, CAL_CELL - 1);
|
|
384
475
|
}
|
|
@@ -390,7 +481,7 @@ export class LyraHeatmap extends LyraElement {
|
|
|
390
481
|
if (week < weekCount && weekday < 7) {
|
|
391
482
|
ctx.lineWidth = RING_LINE_WIDTH;
|
|
392
483
|
ctx.strokeStyle = this.focusRingColor();
|
|
393
|
-
const x =
|
|
484
|
+
const x = this.columnXFor(week);
|
|
394
485
|
const y = CAL_LABEL_H + weekday * (CAL_CELL + CAL_GAP);
|
|
395
486
|
ctx.strokeRect(x + 0.5, y + 0.5, CAL_CELL - 1, CAL_CELL - 1);
|
|
396
487
|
}
|
|
@@ -398,9 +489,9 @@ export class LyraHeatmap extends LyraElement {
|
|
|
398
489
|
ctx.fillStyle = this.labelColor();
|
|
399
490
|
ctx.font = this.labelFont();
|
|
400
491
|
for (const m of monthLabels) {
|
|
401
|
-
ctx.fillText(m.label,
|
|
492
|
+
ctx.fillText(m.label, this.columnXFor(m.week), CAL_LABEL_H - 4);
|
|
402
493
|
}
|
|
403
|
-
const WEEKDAY_LABELS =
|
|
494
|
+
const WEEKDAY_LABELS = this.weekdayLabels(firstWeekStart);
|
|
404
495
|
WEEKDAY_LABELS.forEach((label, weekday) => {
|
|
405
496
|
if (label)
|
|
406
497
|
ctx.fillText(label, 2, CAL_LABEL_H + weekday * (CAL_CELL + CAL_GAP) + CAL_CELL - 1);
|
|
@@ -539,9 +630,9 @@ export class LyraHeatmap extends LyraElement {
|
|
|
539
630
|
const { weekCount } = buildCalendarGrid(this.days);
|
|
540
631
|
if (weekCount === 0)
|
|
541
632
|
return null;
|
|
542
|
-
const week =
|
|
633
|
+
const week = this.weekAtX(x, weekCount);
|
|
543
634
|
const weekday = Math.floor((y - CAL_LABEL_H) / (CAL_CELL + CAL_GAP));
|
|
544
|
-
if (week
|
|
635
|
+
if (week === null || weekday < 0 || weekday > 6)
|
|
545
636
|
return null;
|
|
546
637
|
return { week, weekday };
|
|
547
638
|
}
|
|
@@ -569,7 +660,7 @@ export class LyraHeatmap extends LyraElement {
|
|
|
569
660
|
cellRect(pos) {
|
|
570
661
|
if ('week' in pos) {
|
|
571
662
|
return {
|
|
572
|
-
x:
|
|
663
|
+
x: this.columnXFor(pos.week),
|
|
573
664
|
y: CAL_LABEL_H + pos.weekday * (CAL_CELL + CAL_GAP),
|
|
574
665
|
w: CAL_CELL,
|
|
575
666
|
h: CAL_CELL,
|
|
@@ -630,6 +721,24 @@ export class LyraHeatmap extends LyraElement {
|
|
|
630
721
|
const rect = this.cellRect(pos);
|
|
631
722
|
return { left: `${rect.x + rect.w / 2}px`, top: `${rect.y}px` };
|
|
632
723
|
}
|
|
724
|
+
/**
|
|
725
|
+
* Value-equality check for a `CellPos`, since `hoverCell` is assigned a
|
|
726
|
+
* fresh object literal on every `pointermove` — Lit's `@state()`
|
|
727
|
+
* change-detection only no-ops an identical *reference*, so without this,
|
|
728
|
+
* `{row,col} !== {row,col}` by reference triggers a full canvas clear +
|
|
729
|
+
* redraw on every pixel of pointer movement, even within the same cell.
|
|
730
|
+
*/
|
|
731
|
+
samePos(a, b) {
|
|
732
|
+
if (a === b)
|
|
733
|
+
return true;
|
|
734
|
+
if (!a || !b)
|
|
735
|
+
return false;
|
|
736
|
+
if ('week' in a && 'week' in b)
|
|
737
|
+
return a.week === b.week && a.weekday === b.weekday;
|
|
738
|
+
if ('row' in a && 'row' in b)
|
|
739
|
+
return a.row === b.row && a.col === b.col;
|
|
740
|
+
return false;
|
|
741
|
+
}
|
|
633
742
|
onMatrixKeyDown(e) {
|
|
634
743
|
const rows = this.rowLabels.length;
|
|
635
744
|
const cols = this.colLabels.length;
|
|
@@ -767,6 +876,9 @@ __decorate([
|
|
|
767
876
|
__decorate([
|
|
768
877
|
property({ attribute: false })
|
|
769
878
|
], LyraHeatmap.prototype, "cellText", void 0);
|
|
879
|
+
__decorate([
|
|
880
|
+
property({ attribute: false })
|
|
881
|
+
], LyraHeatmap.prototype, "columnX", void 0);
|
|
770
882
|
__decorate([
|
|
771
883
|
query('canvas')
|
|
772
884
|
], LyraHeatmap.prototype, "canvas", void 0);
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type TemplateResult } from 'lit';
|
|
2
|
+
import { LyraElement } from '../../internal/lyra-element.js';
|
|
3
|
+
/**
|
|
4
|
+
* `<lyra-json-viewer>` — a collapsible, copyable tree view for an arbitrary
|
|
5
|
+
* JSON-serializable value (object/array/string/number/boolean/null/
|
|
6
|
+
* undefined). Serves as the fallback renderer wherever a raw payload (tool
|
|
7
|
+
* call arguments, a tool result, an API response) needs inspecting without a
|
|
8
|
+
* bespoke view.
|
|
9
|
+
*
|
|
10
|
+
* Expand/collapse state is keyed by structural path (not by object identity),
|
|
11
|
+
* so it survives a `data` reassignment that keeps the same shape -- e.g. a
|
|
12
|
+
* streaming tool result being patched in place.
|
|
13
|
+
*
|
|
14
|
+
* @customElement lyra-json-viewer
|
|
15
|
+
* @event lyra-copy - `detail: { text }` -- fired by the top-level copy button or a per-node one. Fires even when `navigator.clipboard` silently failed or is unavailable (insecure context, older browser, stubbed-out test environment), so a consumer can still observe copy *intent*.
|
|
16
|
+
* @csspart base - The root scroll container; respects `max-height`.
|
|
17
|
+
* @csspart toolbar - The wrapper around the top-level copy button (only rendered when `copyable`).
|
|
18
|
+
* @csspart tree - The wrapper around the rendered node tree.
|
|
19
|
+
* @csspart key - An object property key or array index label.
|
|
20
|
+
* @csspart value - A primitive value's text -- carries `data-type` (`string`/`number`/`boolean`/`null`/`undefined`) for per-type coloring, and `data-match` while it matches `search`.
|
|
21
|
+
* @csspart bracket - A `{`, `}`, `[`, or `]` delimiter.
|
|
22
|
+
* @csspart toggle - A container node's expand/collapse button (hidden, but present for row alignment, on leaf/empty nodes).
|
|
23
|
+
* @csspart copy-button - A copy-to-clipboard button -- the top-level one (in `toolbar`) or a per-node one (only rendered when `copyable`).
|
|
24
|
+
*/
|
|
25
|
+
export declare class LyraJsonViewer extends LyraElement {
|
|
26
|
+
static styles: import("lit").CSSResultGroup[];
|
|
27
|
+
/** The value to render. Any JSON-serializable value, plus `undefined`. */
|
|
28
|
+
data: unknown;
|
|
29
|
+
/** Nodes at or beyond this nesting depth (root = 0) start collapsed. Omit/undefined: nothing auto-collapses. */
|
|
30
|
+
collapsedDepth?: number;
|
|
31
|
+
/** A CSS length (e.g. `"20rem"`); once set, the viewer scrolls internally past this height instead of growing the page. */
|
|
32
|
+
maxHeight: string;
|
|
33
|
+
/** Shows copy-to-clipboard affordances: one for the whole value, plus one per node. */
|
|
34
|
+
copyable: boolean;
|
|
35
|
+
/** Case-insensitive substring match against keys/values; matches are highlighted and their ancestors auto-expanded. */
|
|
36
|
+
search: string;
|
|
37
|
+
/** Per-path (`JSON.stringify(path)`) explicit expand/collapse, overriding the `collapsedDepth`/search defaults once a node's toggle has been used. */
|
|
38
|
+
private expandedOverrides;
|
|
39
|
+
private isExpanded;
|
|
40
|
+
private toggleNode;
|
|
41
|
+
private writeClipboard;
|
|
42
|
+
private copy;
|
|
43
|
+
/** Builds the key/value-match sets and the ancestor-paths-of-a-match set that `search` drives. */
|
|
44
|
+
private computeSearch;
|
|
45
|
+
private renderCopyButton;
|
|
46
|
+
private renderNode;
|
|
47
|
+
render(): TemplateResult;
|
|
48
|
+
}
|
|
49
|
+
declare global {
|
|
50
|
+
interface HTMLElementTagNameMap {
|
|
51
|
+
'lyra-json-viewer': LyraJsonViewer;
|
|
52
|
+
}
|
|
53
|
+
}
|