@adia-ai/web-components 0.4.6 → 0.4.7
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/USAGE.md +29 -9
- package/components/accordion/accordion.d.ts +17 -0
- package/components/accordion/accordion.js +10 -117
- package/components/accordion/class.js +132 -0
- package/components/action-list/action-list.d.ts +15 -0
- package/components/action-list/action-list.js +9 -140
- package/components/action-list/class.js +156 -0
- package/components/agent-artifact/agent-artifact.d.ts +25 -0
- package/components/agent-artifact/agent-artifact.js +8 -181
- package/components/agent-artifact/class.js +200 -0
- package/components/agent-feedback-bar/agent-feedback-bar.d.ts +21 -0
- package/components/agent-feedback-bar/agent-feedback-bar.js +8 -143
- package/components/agent-feedback-bar/class.js +162 -0
- package/components/agent-questions/agent-questions.d.ts +23 -0
- package/components/agent-questions/agent-questions.js +8 -180
- package/components/agent-questions/class.js +199 -0
- package/components/agent-reasoning/agent-reasoning.d.ts +23 -0
- package/components/agent-reasoning/agent-reasoning.js +8 -494
- package/components/agent-reasoning/class.js +513 -0
- package/components/agent-suggestions/agent-suggestions.d.ts +21 -0
- package/components/agent-suggestions/agent-suggestions.js +8 -78
- package/components/agent-suggestions/class.js +97 -0
- package/components/agent-trace/agent-trace.d.ts +19 -0
- package/components/alert/alert.d.ts +29 -0
- package/components/alert/alert.js +8 -175
- package/components/alert/class.js +194 -0
- package/components/avatar/avatar.d.ts +27 -0
- package/components/avatar/avatar.js +9 -159
- package/components/avatar/class.js +173 -0
- package/components/badge/badge.d.ts +27 -0
- package/components/badge/badge.js +9 -75
- package/components/badge/class.js +93 -0
- package/components/block/block.d.ts +19 -0
- package/components/block/block.js +9 -15
- package/components/block/class.js +33 -0
- package/components/breadcrumb/breadcrumb.d.ts +23 -0
- package/components/breadcrumb/breadcrumb.js +8 -113
- package/components/breadcrumb/class.js +132 -0
- package/components/button/button.d.ts +34 -0
- package/components/button/button.js +15 -66
- package/components/button/class.js +80 -0
- package/components/calendar-picker/calendar-picker.a2ui.json +6 -1
- package/components/calendar-picker/calendar-picker.js +8 -332
- package/components/calendar-picker/calendar-picker.yaml +51 -177
- package/components/calendar-picker/class.js +351 -0
- package/components/canvas/canvas.a2ui.json +6 -1
- package/components/canvas/canvas.d.ts +17 -0
- package/components/canvas/canvas.yaml +19 -36
- package/components/card/card.a2ui.json +3 -0
- package/components/card/card.d.ts +27 -0
- package/components/card/card.js +9 -50
- package/components/card/card.yaml +171 -433
- package/components/card/class.js +68 -0
- package/components/chart/chart.d.ts +41 -0
- package/components/chart/chart.js +8 -2131
- package/components/chart/class.js +2150 -0
- package/components/chart-legend/chart-legend.d.ts +27 -0
- package/components/chart-legend/chart-legend.js +8 -197
- package/components/chart-legend/class.js +215 -0
- package/components/chat-thread/chat-thread.d.ts +17 -0
- package/components/chat-thread/chat-thread.js +8 -157
- package/components/chat-thread/class.js +176 -0
- package/components/check/check.js +11 -52
- package/components/check/class.js +68 -0
- package/components/code/class.js +501 -0
- package/components/code/code.js +8 -482
- package/components/col/class.js +30 -0
- package/components/col/col.d.ts +23 -0
- package/components/col/col.js +10 -13
- package/components/color-picker/class.js +550 -0
- package/components/color-picker/color-picker.js +8 -531
- package/components/command/class.js +364 -0
- package/components/command/command.a2ui.json +3 -0
- package/components/command/command.d.ts +19 -0
- package/components/command/command.js +8 -345
- package/components/command/command.yaml +105 -124
- package/components/demo-toggle/class.js +153 -0
- package/components/demo-toggle/demo-toggle.d.ts +23 -0
- package/components/demo-toggle/demo-toggle.js +8 -135
- package/components/description-list/class.js +86 -0
- package/components/description-list/description-list.d.ts +21 -0
- package/components/description-list/description-list.js +8 -67
- package/components/divider/class.js +57 -0
- package/components/divider/divider.d.ts +19 -0
- package/components/divider/divider.js +10 -40
- package/components/drawer/class.js +306 -0
- package/components/drawer/drawer.d.ts +25 -0
- package/components/drawer/drawer.js +8 -287
- package/components/embed/class.js +73 -0
- package/components/embed/embed.d.ts +23 -0
- package/components/embed/embed.js +9 -55
- package/components/empty-state/class.js +108 -0
- package/components/empty-state/empty-state.d.ts +21 -0
- package/components/empty-state/empty-state.js +9 -90
- package/components/feed/class.js +381 -0
- package/components/feed/feed.d.ts +19 -0
- package/components/feed/feed.js +9 -367
- package/components/field/class.js +266 -0
- package/components/field/field.d.ts +23 -0
- package/components/field/field.js +8 -247
- package/components/fields/class.js +106 -0
- package/components/fields/fields.d.ts +19 -0
- package/components/fields/fields.js +8 -87
- package/components/grid/class.js +31 -0
- package/components/grid/grid.d.ts +23 -0
- package/components/grid/grid.js +10 -14
- package/components/heatmap/class.js +305 -0
- package/components/heatmap/heatmap.d.ts +31 -0
- package/components/heatmap/heatmap.js +8 -286
- package/components/icon/class.js +54 -0
- package/components/icon/icon.d.ts +23 -0
- package/components/icon/icon.js +13 -40
- package/components/image/class.js +112 -0
- package/components/image/image.d.ts +33 -0
- package/components/image/image.js +9 -94
- package/components/input/class.js +773 -0
- package/components/input/input.a2ui.json +3 -0
- package/components/input/input.js +8 -755
- package/components/input/input.yaml +171 -442
- package/components/inspector/class.js +142 -0
- package/components/inspector/inspector.a2ui.json +8 -1
- package/components/inspector/inspector.d.ts +17 -0
- package/components/inspector/inspector.js +8 -124
- package/components/inspector/inspector.yaml +15 -30
- package/components/kbd/class.js +34 -0
- package/components/kbd/kbd.a2ui.json +3 -0
- package/components/kbd/kbd.d.ts +17 -0
- package/components/kbd/kbd.js +10 -17
- package/components/kbd/kbd.yaml +54 -185
- package/components/link/class.js +187 -0
- package/components/link/link.d.ts +55 -0
- package/components/link/link.js +8 -168
- package/components/list/class.js +249 -0
- package/components/list/list.d.ts +23 -0
- package/components/list/list.js +9 -231
- package/components/menu/class.js +332 -0
- package/components/menu/menu.d.ts +21 -0
- package/components/menu/menu.js +11 -316
- package/components/modal/class.js +231 -0
- package/components/modal/modal.a2ui.json +5 -1
- package/components/modal/modal.d.ts +23 -0
- package/components/modal/modal.js +8 -212
- package/components/modal/modal.yaml +19 -39
- package/components/nav/class.js +150 -0
- package/components/nav/nav.d.ts +31 -0
- package/components/nav/nav.js +8 -131
- package/components/nav-group/class.js +152 -0
- package/components/nav-group/nav-group.d.ts +35 -0
- package/components/nav-group/nav-group.js +9 -134
- package/components/nav-item/class.js +86 -0
- package/components/nav-item/nav-item.d.ts +37 -0
- package/components/nav-item/nav-item.js +10 -69
- package/components/noodles/class.js +510 -0
- package/components/noodles/noodles.d.ts +33 -0
- package/components/noodles/noodles.js +9 -493
- package/components/option-card/class.js +167 -0
- package/components/option-card/option-card.js +8 -149
- package/components/otp-input/class.js +180 -0
- package/components/otp-input/otp-input.a2ui.json +5 -1
- package/components/otp-input/otp-input.js +9 -162
- package/components/otp-input/otp-input.yaml +45 -174
- package/components/page/class.js +97 -0
- package/components/page/page.d.ts +46 -0
- package/components/page/page.js +8 -79
- package/components/pagination/class.js +195 -0
- package/components/pagination/pagination.d.ts +23 -0
- package/components/pagination/pagination.js +9 -177
- package/components/pane/class.js +186 -0
- package/components/pane/pane.a2ui.json +12 -1
- package/components/pane/pane.d.ts +31 -0
- package/components/pane/pane.js +8 -167
- package/components/pane/pane.yaml +57 -157
- package/components/pipeline-status/class.js +189 -0
- package/components/pipeline-status/pipeline-status.a2ui.json +7 -1
- package/components/pipeline-status/pipeline-status.d.ts +21 -0
- package/components/pipeline-status/pipeline-status.js +9 -172
- package/components/pipeline-status/pipeline-status.yaml +34 -72
- package/components/popover/class.js +194 -0
- package/components/popover/popover.d.ts +23 -0
- package/components/popover/popover.js +9 -176
- package/components/progress/class.js +74 -0
- package/components/progress/progress.a2ui.json +3 -0
- package/components/progress/progress.d.ts +19 -0
- package/components/progress/progress.js +10 -57
- package/components/progress/progress.yaml +124 -287
- package/components/progress-row/class.js +110 -0
- package/components/progress-row/progress-row.d.ts +23 -0
- package/components/progress-row/progress-row.js +8 -92
- package/components/radio/class.js +83 -0
- package/components/radio/radio.js +11 -67
- package/components/range/class.js +194 -0
- package/components/range/range.js +9 -176
- package/components/rating/class.js +148 -0
- package/components/rating/rating.js +9 -130
- package/components/richtext/class.js +87 -0
- package/components/richtext/richtext.a2ui.json +7 -1
- package/components/richtext/richtext.d.ts +19 -0
- package/components/richtext/richtext.js +8 -68
- package/components/richtext/richtext.yaml +30 -65
- package/components/row/class.js +50 -0
- package/components/row/row.d.ts +27 -0
- package/components/row/row.js +10 -33
- package/components/search/class.js +134 -0
- package/components/search/search.js +10 -117
- package/components/segment/class.js +62 -0
- package/components/segment/segment.d.ts +25 -0
- package/components/segment/segment.js +10 -45
- package/components/segmented/class.js +165 -0
- package/components/segmented/segmented.a2ui.json +4 -0
- package/components/segmented/segmented.js +10 -148
- package/components/segmented/segmented.yaml +41 -59
- package/components/select/class.js +408 -0
- package/components/select/select.js +15 -396
- package/components/skeleton/class.js +52 -0
- package/components/skeleton/skeleton.d.ts +23 -0
- package/components/skeleton/skeleton.js +8 -34
- package/components/slider/class.js +184 -0
- package/components/slider/slider.js +9 -166
- package/components/stack/class.js +28 -0
- package/components/stack/stack.d.ts +17 -0
- package/components/stack/stack.js +10 -11
- package/components/step-progress/class.js +98 -0
- package/components/step-progress/step-progress.d.ts +27 -0
- package/components/step-progress/step-progress.js +8 -79
- package/components/stepper/class.js +126 -0
- package/components/stepper/stepper.d.ts +19 -0
- package/components/stepper/stepper.js +9 -112
- package/components/stream/class.js +109 -0
- package/components/stream/stream.d.ts +19 -0
- package/components/stream/stream.js +8 -90
- package/components/swatch/class.js +131 -0
- package/components/swatch/swatch.d.ts +28 -0
- package/components/swatch/swatch.js +8 -112
- package/components/swiper/class.js +373 -0
- package/components/swiper/swiper.a2ui.json +4 -0
- package/components/swiper/swiper.d.ts +31 -0
- package/components/swiper/swiper.js +8 -354
- package/components/swiper/swiper.yaml +68 -212
- package/components/switch/class.js +63 -0
- package/components/switch/switch.a2ui.json +6 -1
- package/components/switch/switch.js +11 -47
- package/components/switch/switch.yaml +70 -265
- package/components/table/class.js +1453 -0
- package/components/table/table.d.ts +37 -0
- package/components/table/table.js +8 -1435
- package/components/table-toolbar/class.js +680 -0
- package/components/table-toolbar/table-toolbar.d.ts +33 -0
- package/components/table-toolbar/table-toolbar.js +8 -689
- package/components/tabs/class.js +242 -0
- package/components/tabs/tabs.d.ts +21 -0
- package/components/tabs/tabs.js +8 -223
- package/components/tag/class.js +99 -0
- package/components/tag/tag.d.ts +27 -0
- package/components/tag/tag.js +8 -80
- package/components/text/class.js +46 -0
- package/components/text/text.d.ts +25 -0
- package/components/text/text.js +9 -28
- package/components/textarea/class.js +134 -0
- package/components/textarea/textarea.js +11 -118
- package/components/timeline/class.js +176 -0
- package/components/timeline/timeline.d.ts +19 -0
- package/components/timeline/timeline.js +9 -162
- package/components/toast/class.js +92 -0
- package/components/toast/toast.d.ts +23 -0
- package/components/toast/toast.js +9 -76
- package/components/toggle-group/class.js +154 -0
- package/components/toggle-group/toggle-group.d.ts +19 -0
- package/components/toggle-group/toggle-group.js +11 -140
- package/components/toggle-scheme/class.js +286 -0
- package/components/toggle-scheme/toggle-scheme.d.ts +41 -0
- package/components/toggle-scheme/toggle-scheme.js +8 -268
- package/components/toolbar/class.js +388 -0
- package/components/toolbar/toolbar.d.ts +23 -0
- package/components/toolbar/toolbar.js +10 -376
- package/components/tooltip/class.js +299 -0
- package/components/tooltip/tooltip.d.ts +27 -0
- package/components/tooltip/tooltip.js +8 -280
- package/components/tree/class.js +245 -0
- package/components/tree/tree.d.ts +15 -0
- package/components/tree/tree.js +9 -244
- package/components/upload/class.js +199 -0
- package/components/upload/upload.js +11 -183
- package/index.d.ts +159 -5
- package/package.json +5 -1
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Non-side-effect class export for `<heatmap-ui>`.
|
|
3
|
+
*
|
|
4
|
+
* Importing this file gives you the class(es) without auto-registering the tag.
|
|
5
|
+
* Useful for test isolation, subclassing with tag-name override, or selective
|
|
6
|
+
* composition.
|
|
7
|
+
*
|
|
8
|
+
* The auto-register path stays at `@adia-ai/web-components/components/heatmap`
|
|
9
|
+
* (which imports this file + calls `defineIfFree()`).
|
|
10
|
+
*
|
|
11
|
+
* @see ../../USAGE.md#registration--auto-vs-explicit
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* <heatmap-ui> — Grid-based heatmap / density / day-grid visualization.
|
|
16
|
+
*
|
|
17
|
+
* Attributes:
|
|
18
|
+
* type — 'day-grid' | 'matrix' | 'density' (default 'matrix')
|
|
19
|
+
* rows — Number (default 7)
|
|
20
|
+
* cols — Number (default 12)
|
|
21
|
+
* scale — 'linear' | 'log' | 'quantile' (default 'linear')
|
|
22
|
+
* data — JSON string OR .data property [{ r, c, v, label? }]
|
|
23
|
+
* no-legend — Boolean (default false — legend shown)
|
|
24
|
+
* color-scheme — 'accent' | 'success' | 'warning' | 'data-ramp' (default 'data-ramp')
|
|
25
|
+
* start-date — ISO date (for day-grid to label months)
|
|
26
|
+
* aspect — 'square' | 'wide' (default 'wide')
|
|
27
|
+
*
|
|
28
|
+
* Events:
|
|
29
|
+
* cell-hover — { detail: { r, c, v, label } }
|
|
30
|
+
* cell-click — { detail: { r, c, v, label } }
|
|
31
|
+
*
|
|
32
|
+
* Slots:
|
|
33
|
+
* title — optional header title
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
import { UIElement } from '../../core/element.js';
|
|
37
|
+
|
|
38
|
+
const MONTHS = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
|
|
39
|
+
|
|
40
|
+
export class UIHeatmap extends UIElement {
|
|
41
|
+
static properties = {
|
|
42
|
+
type: { type: String, default: 'matrix', reflect: true },
|
|
43
|
+
rows: { type: Number, default: 7, reflect: true },
|
|
44
|
+
cols: { type: Number, default: 12, reflect: true },
|
|
45
|
+
scale: { type: String, default: 'linear', reflect: true },
|
|
46
|
+
noLegend: { type: Boolean, default: false, reflect: true, attribute: 'no-legend' },
|
|
47
|
+
colorScheme: { type: String, default: 'data-ramp', reflect: true, attribute: 'color-scheme' },
|
|
48
|
+
startDate: { type: String, default: '', reflect: true, attribute: 'start-date' },
|
|
49
|
+
aspect: { type: String, default: 'wide', reflect: true },
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
static template = () => null;
|
|
53
|
+
|
|
54
|
+
#data = [];
|
|
55
|
+
#bound = false;
|
|
56
|
+
|
|
57
|
+
set data(arr) {
|
|
58
|
+
if (typeof arr === 'string') {
|
|
59
|
+
try { arr = JSON.parse(arr); } catch { arr = []; }
|
|
60
|
+
}
|
|
61
|
+
this.#data = Array.isArray(arr) ? arr : [];
|
|
62
|
+
this.#requestRender();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
get data() { return this.#data; }
|
|
66
|
+
|
|
67
|
+
connected() {
|
|
68
|
+
this.setAttribute('role', 'img');
|
|
69
|
+
// Parse data attribute if present
|
|
70
|
+
const raw = this.getAttribute('data');
|
|
71
|
+
if (raw && !this.#data.length) {
|
|
72
|
+
try { this.#data = JSON.parse(raw); } catch { /* ignore */ }
|
|
73
|
+
}
|
|
74
|
+
if (!this.#bound) {
|
|
75
|
+
this.#bound = true;
|
|
76
|
+
this.addEventListener('pointerover', this.#onHover);
|
|
77
|
+
this.addEventListener('pointermove', this.#onMove);
|
|
78
|
+
this.addEventListener('pointerleave', this.#onLeave);
|
|
79
|
+
this.addEventListener('click', this.#onClick);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
disconnected() {
|
|
84
|
+
this.removeEventListener('pointerover', this.#onHover);
|
|
85
|
+
this.removeEventListener('pointermove', this.#onMove);
|
|
86
|
+
this.removeEventListener('pointerleave', this.#onLeave);
|
|
87
|
+
this.removeEventListener('click', this.#onClick);
|
|
88
|
+
this.#bound = false;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
#raf = null;
|
|
92
|
+
#requestRender() {
|
|
93
|
+
if (!this.isConnected || this.#raf) return;
|
|
94
|
+
this.#raf = requestAnimationFrame(() => {
|
|
95
|
+
this.#raf = null;
|
|
96
|
+
this.render();
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ── Bucket value to 0..4 based on scale + data min/max ──
|
|
101
|
+
|
|
102
|
+
#buckets() {
|
|
103
|
+
const values = this.#data.map((d) => Number(d.v) || 0);
|
|
104
|
+
if (!values.length) return (_) => 0;
|
|
105
|
+
let min = Math.min(...values);
|
|
106
|
+
let max = Math.max(...values);
|
|
107
|
+
if (min === max) return (_) => 4;
|
|
108
|
+
const scale = this.scale;
|
|
109
|
+
if (scale === 'log') {
|
|
110
|
+
const lo = Math.log(Math.max(1, min));
|
|
111
|
+
const hi = Math.log(Math.max(1, max));
|
|
112
|
+
return (v) => {
|
|
113
|
+
const l = Math.log(Math.max(1, Number(v) || 0));
|
|
114
|
+
const t = (l - lo) / (hi - lo);
|
|
115
|
+
return Math.min(4, Math.max(0, Math.round(t * 4)));
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
if (scale === 'quantile') {
|
|
119
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
120
|
+
const q = (p) => sorted[Math.floor(p * (sorted.length - 1))];
|
|
121
|
+
const breaks = [q(0.2), q(0.4), q(0.6), q(0.8)];
|
|
122
|
+
return (v) => {
|
|
123
|
+
const n = Number(v) || 0;
|
|
124
|
+
if (n <= breaks[0]) return 0;
|
|
125
|
+
if (n <= breaks[1]) return 1;
|
|
126
|
+
if (n <= breaks[2]) return 2;
|
|
127
|
+
if (n <= breaks[3]) return 3;
|
|
128
|
+
return 4;
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
// linear
|
|
132
|
+
return (v) => {
|
|
133
|
+
const n = Number(v) || 0;
|
|
134
|
+
const t = (n - min) / (max - min);
|
|
135
|
+
return Math.min(4, Math.max(0, Math.round(t * 4)));
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
render() {
|
|
140
|
+
const rows = Math.max(1, this.rows || 7);
|
|
141
|
+
const cols = Math.max(1, this.cols || 12);
|
|
142
|
+
|
|
143
|
+
// Build a map for fast lookup
|
|
144
|
+
const map = new Map();
|
|
145
|
+
for (const d of this.#data) {
|
|
146
|
+
if (d && d.r != null && d.c != null) map.set(`${d.r},${d.c}`, d);
|
|
147
|
+
}
|
|
148
|
+
const bucketOf = this.#buckets();
|
|
149
|
+
|
|
150
|
+
// ── Outer host layout: clear everything except <title> slot ──
|
|
151
|
+
// Keep only slotted title children if present.
|
|
152
|
+
const title = this.querySelector(':scope > [slot="title"]');
|
|
153
|
+
this.replaceChildren();
|
|
154
|
+
if (title) this.appendChild(title);
|
|
155
|
+
|
|
156
|
+
// ── Optional month-labels row (day-grid mode) ──
|
|
157
|
+
if (this.type === 'day-grid' && this.startDate) {
|
|
158
|
+
const labels = document.createElement('div');
|
|
159
|
+
labels.setAttribute('data-months', '');
|
|
160
|
+
const start = new Date(this.startDate);
|
|
161
|
+
if (!Number.isNaN(start.getTime())) {
|
|
162
|
+
// For day-grid: rows=7 (days of week), cols=weeks. Label months at column
|
|
163
|
+
// positions where the 1st of the month falls.
|
|
164
|
+
const marks = [];
|
|
165
|
+
for (let c = 0; c < cols; c++) {
|
|
166
|
+
const d = new Date(start);
|
|
167
|
+
d.setDate(d.getDate() + c * 7);
|
|
168
|
+
if (c === 0 || d.getDate() <= 7) {
|
|
169
|
+
marks.push({ col: c, label: MONTHS[d.getMonth()] });
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
for (const m of marks) {
|
|
173
|
+
const span = document.createElement('span');
|
|
174
|
+
span.setAttribute('data-month-label', '');
|
|
175
|
+
span.style.gridColumn = String(m.col + 1);
|
|
176
|
+
span.textContent = m.label;
|
|
177
|
+
labels.appendChild(span);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
labels.style.gridTemplateColumns = `repeat(${cols}, minmax(0, 1fr))`;
|
|
181
|
+
this.appendChild(labels);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ── Grid of cells ──
|
|
185
|
+
const grid = document.createElement('div');
|
|
186
|
+
grid.setAttribute('data-grid', '');
|
|
187
|
+
grid.style.gridTemplateColumns = `repeat(${cols}, minmax(0, 1fr))`;
|
|
188
|
+
grid.style.gridTemplateRows = `repeat(${rows}, 1fr)`;
|
|
189
|
+
for (let r = 0; r < rows; r++) {
|
|
190
|
+
for (let c = 0; c < cols; c++) {
|
|
191
|
+
const d = map.get(`${r},${c}`);
|
|
192
|
+
const cell = document.createElement('div');
|
|
193
|
+
cell.setAttribute('data-cell', '');
|
|
194
|
+
cell.setAttribute('role', 'gridcell');
|
|
195
|
+
cell.dataset.r = String(r);
|
|
196
|
+
cell.dataset.c = String(c);
|
|
197
|
+
if (d != null) {
|
|
198
|
+
const v = Number(d.v) || 0;
|
|
199
|
+
const bucket = bucketOf(v);
|
|
200
|
+
cell.dataset.bucket = String(bucket);
|
|
201
|
+
cell.dataset.v = String(v);
|
|
202
|
+
if (d.label) cell.setAttribute('aria-label', d.label);
|
|
203
|
+
} else {
|
|
204
|
+
cell.dataset.empty = '';
|
|
205
|
+
}
|
|
206
|
+
grid.appendChild(cell);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
this.appendChild(grid);
|
|
210
|
+
|
|
211
|
+
// ── Legend ──
|
|
212
|
+
if (!this.noLegend) {
|
|
213
|
+
const legend = document.createElement('div');
|
|
214
|
+
legend.setAttribute('data-legend', '');
|
|
215
|
+
const less = document.createElement('span');
|
|
216
|
+
less.setAttribute('data-legend-label', '');
|
|
217
|
+
less.textContent = 'Less';
|
|
218
|
+
legend.appendChild(less);
|
|
219
|
+
for (let i = 0; i < 5; i++) {
|
|
220
|
+
const swatch = document.createElement('span');
|
|
221
|
+
swatch.setAttribute('data-legend-swatch', '');
|
|
222
|
+
swatch.dataset.bucket = String(i);
|
|
223
|
+
legend.appendChild(swatch);
|
|
224
|
+
}
|
|
225
|
+
const more = document.createElement('span');
|
|
226
|
+
more.setAttribute('data-legend-label', '');
|
|
227
|
+
more.textContent = 'More';
|
|
228
|
+
legend.appendChild(more);
|
|
229
|
+
this.appendChild(legend);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
#findCell(target) {
|
|
234
|
+
return target?.closest?.('[data-cell]');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/* Compose the cell-level event with the canonical chart-* shape so that
|
|
238
|
+
tooltip-ui[follows=pointer][for=this-heatmap] can render without caring
|
|
239
|
+
whether the source is a chart or a heatmap. */
|
|
240
|
+
#chartDetail(cell, event) {
|
|
241
|
+
const r = Number(cell.dataset.r);
|
|
242
|
+
const c = Number(cell.dataset.c);
|
|
243
|
+
const v = Number(cell.dataset.v);
|
|
244
|
+
const label = cell.getAttribute('aria-label') || '';
|
|
245
|
+
return {
|
|
246
|
+
r, c,
|
|
247
|
+
label,
|
|
248
|
+
value: Number.isFinite(v) ? v : null,
|
|
249
|
+
pct: null,
|
|
250
|
+
series: null,
|
|
251
|
+
slot: 0,
|
|
252
|
+
pointerX: event?.clientX ?? null,
|
|
253
|
+
pointerY: event?.clientY ?? null,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
#hoveredCell = null;
|
|
258
|
+
|
|
259
|
+
#onHover = (e) => {
|
|
260
|
+
const cell = this.#findCell(e.target);
|
|
261
|
+
if (!cell || !cell.dataset.v) return;
|
|
262
|
+
this.#hoveredCell = cell;
|
|
263
|
+
const detail = this.#chartDetail(cell, e);
|
|
264
|
+
/* Legacy cell-specific shape — kept for back-compat. */
|
|
265
|
+
this.dispatchEvent(new CustomEvent('cell-hover', { detail, bubbles: true }));
|
|
266
|
+
/* Canonical chart-hover shape for tooltip-ui[follows=pointer]. */
|
|
267
|
+
this.dispatchEvent(new CustomEvent('chart-hover', { detail, bubbles: true }));
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
#onMove = (e) => {
|
|
271
|
+
const cell = this.#findCell(e.target);
|
|
272
|
+
if (!cell || !cell.dataset.v) {
|
|
273
|
+
if (this.#hoveredCell) {
|
|
274
|
+
this.#hoveredCell = null;
|
|
275
|
+
this.dispatchEvent(new CustomEvent('chart-leave', { bubbles: true }));
|
|
276
|
+
}
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
if (cell !== this.#hoveredCell) {
|
|
280
|
+
this.#hoveredCell = cell;
|
|
281
|
+
const detail = this.#chartDetail(cell, e);
|
|
282
|
+
this.dispatchEvent(new CustomEvent('cell-hover', { detail, bubbles: true }));
|
|
283
|
+
this.dispatchEvent(new CustomEvent('chart-hover', { detail, bubbles: true }));
|
|
284
|
+
} else {
|
|
285
|
+
/* Pointer still inside the same cell — re-fire chart-hover so the
|
|
286
|
+
pointer-follow tooltip can reposition without re-painting content. */
|
|
287
|
+
const detail = this.#chartDetail(cell, e);
|
|
288
|
+
this.dispatchEvent(new CustomEvent('chart-hover', { detail, bubbles: true }));
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
#onLeave = () => {
|
|
293
|
+
if (!this.#hoveredCell) return;
|
|
294
|
+
this.#hoveredCell = null;
|
|
295
|
+
this.dispatchEvent(new CustomEvent('chart-leave', { bubbles: true }));
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
#onClick = (e) => {
|
|
299
|
+
const cell = this.#findCell(e.target);
|
|
300
|
+
if (!cell || !cell.dataset.v) return;
|
|
301
|
+
const detail = this.#chartDetail(cell, e);
|
|
302
|
+
this.dispatchEvent(new CustomEvent('cell-click', { detail, bubbles: true }));
|
|
303
|
+
this.dispatchEvent(new CustomEvent('chart-select', { detail, bubbles: true }));
|
|
304
|
+
};
|
|
305
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<heatmap-ui>` — Grid heatmap with 5-bucket data ramp — supports day-grid (GitHub contributions), matrix, and density modes.
|
|
3
|
+
*
|
|
4
|
+
* @see https://ui-kit.exe.xyz/site/components/heatmap
|
|
5
|
+
*
|
|
6
|
+
* Type declarations generated by scripts/build/dts-codegen.mjs from
|
|
7
|
+
* the component's `.a2ui.json` sidecar. Edit the source `.yaml`,
|
|
8
|
+
* run `npm run components`, then `npm run codegen:dts` to regenerate;
|
|
9
|
+
* or hand-author this file fully if rich event types are needed.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { UIElement } from '../../core/element.js';
|
|
13
|
+
|
|
14
|
+
export class UIHeatmap extends UIElement {
|
|
15
|
+
/** Heatmap type */
|
|
16
|
+
type: 'day-grid' | 'matrix' | 'density';
|
|
17
|
+
/** Aspect ratio */
|
|
18
|
+
aspect: 'square' | 'wide';
|
|
19
|
+
/** Color ramp */
|
|
20
|
+
colorScheme: 'accent' | 'success' | 'warning' | 'data-ramp';
|
|
21
|
+
/** Column count */
|
|
22
|
+
cols: number;
|
|
23
|
+
/** Hide the Less/More legend strip */
|
|
24
|
+
noLegend: boolean;
|
|
25
|
+
/** Row count */
|
|
26
|
+
rows: number;
|
|
27
|
+
/** Value-to-bucket scale */
|
|
28
|
+
scale: 'linear' | 'log' | 'quantile';
|
|
29
|
+
/** ISO start date; labels months in day-grid mode */
|
|
30
|
+
startDate: string;
|
|
31
|
+
}
|
|
@@ -1,295 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* `<heatmap-ui>` — auto-registers the tag on import.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* rows — Number (default 7)
|
|
7
|
-
* cols — Number (default 12)
|
|
8
|
-
* scale — 'linear' | 'log' | 'quantile' (default 'linear')
|
|
9
|
-
* data — JSON string OR .data property [{ r, c, v, label? }]
|
|
10
|
-
* no-legend — Boolean (default false — legend shown)
|
|
11
|
-
* color-scheme — 'accent' | 'success' | 'warning' | 'data-ramp' (default 'data-ramp')
|
|
12
|
-
* start-date — ISO date (for day-grid to label months)
|
|
13
|
-
* aspect — 'square' | 'wide' (default 'wide')
|
|
4
|
+
* For non-side-effect class import (test isolation, tag override), use
|
|
5
|
+
* the `class` subpath:
|
|
14
6
|
*
|
|
15
|
-
*
|
|
16
|
-
* cell-hover — { detail: { r, c, v, label } }
|
|
17
|
-
* cell-click — { detail: { r, c, v, label } }
|
|
7
|
+
* import { UIHeatmap } from '@adia-ai/web-components/components/heatmap/class';
|
|
18
8
|
*
|
|
19
|
-
*
|
|
20
|
-
* title — optional header title
|
|
9
|
+
* @see ../../USAGE.md#registration--auto-vs-explicit
|
|
21
10
|
*/
|
|
22
11
|
|
|
23
|
-
import {
|
|
12
|
+
import { defineIfFree } from '../../core/register.js';
|
|
13
|
+
import { UIHeatmap } from './class.js';
|
|
24
14
|
|
|
25
|
-
|
|
15
|
+
defineIfFree('heatmap-ui', UIHeatmap);
|
|
26
16
|
|
|
27
|
-
class UIHeatmap extends UIElement {
|
|
28
|
-
static properties = {
|
|
29
|
-
type: { type: String, default: 'matrix', reflect: true },
|
|
30
|
-
rows: { type: Number, default: 7, reflect: true },
|
|
31
|
-
cols: { type: Number, default: 12, reflect: true },
|
|
32
|
-
scale: { type: String, default: 'linear', reflect: true },
|
|
33
|
-
noLegend: { type: Boolean, default: false, reflect: true, attribute: 'no-legend' },
|
|
34
|
-
colorScheme: { type: String, default: 'data-ramp', reflect: true, attribute: 'color-scheme' },
|
|
35
|
-
startDate: { type: String, default: '', reflect: true, attribute: 'start-date' },
|
|
36
|
-
aspect: { type: String, default: 'wide', reflect: true },
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
static template = () => null;
|
|
40
|
-
|
|
41
|
-
#data = [];
|
|
42
|
-
#bound = false;
|
|
43
|
-
|
|
44
|
-
set data(arr) {
|
|
45
|
-
if (typeof arr === 'string') {
|
|
46
|
-
try { arr = JSON.parse(arr); } catch { arr = []; }
|
|
47
|
-
}
|
|
48
|
-
this.#data = Array.isArray(arr) ? arr : [];
|
|
49
|
-
this.#requestRender();
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
get data() { return this.#data; }
|
|
53
|
-
|
|
54
|
-
connected() {
|
|
55
|
-
this.setAttribute('role', 'img');
|
|
56
|
-
// Parse data attribute if present
|
|
57
|
-
const raw = this.getAttribute('data');
|
|
58
|
-
if (raw && !this.#data.length) {
|
|
59
|
-
try { this.#data = JSON.parse(raw); } catch { /* ignore */ }
|
|
60
|
-
}
|
|
61
|
-
if (!this.#bound) {
|
|
62
|
-
this.#bound = true;
|
|
63
|
-
this.addEventListener('pointerover', this.#onHover);
|
|
64
|
-
this.addEventListener('pointermove', this.#onMove);
|
|
65
|
-
this.addEventListener('pointerleave', this.#onLeave);
|
|
66
|
-
this.addEventListener('click', this.#onClick);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
disconnected() {
|
|
71
|
-
this.removeEventListener('pointerover', this.#onHover);
|
|
72
|
-
this.removeEventListener('pointermove', this.#onMove);
|
|
73
|
-
this.removeEventListener('pointerleave', this.#onLeave);
|
|
74
|
-
this.removeEventListener('click', this.#onClick);
|
|
75
|
-
this.#bound = false;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
#raf = null;
|
|
79
|
-
#requestRender() {
|
|
80
|
-
if (!this.isConnected || this.#raf) return;
|
|
81
|
-
this.#raf = requestAnimationFrame(() => {
|
|
82
|
-
this.#raf = null;
|
|
83
|
-
this.render();
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// ── Bucket value to 0..4 based on scale + data min/max ──
|
|
88
|
-
|
|
89
|
-
#buckets() {
|
|
90
|
-
const values = this.#data.map((d) => Number(d.v) || 0);
|
|
91
|
-
if (!values.length) return (_) => 0;
|
|
92
|
-
let min = Math.min(...values);
|
|
93
|
-
let max = Math.max(...values);
|
|
94
|
-
if (min === max) return (_) => 4;
|
|
95
|
-
const scale = this.scale;
|
|
96
|
-
if (scale === 'log') {
|
|
97
|
-
const lo = Math.log(Math.max(1, min));
|
|
98
|
-
const hi = Math.log(Math.max(1, max));
|
|
99
|
-
return (v) => {
|
|
100
|
-
const l = Math.log(Math.max(1, Number(v) || 0));
|
|
101
|
-
const t = (l - lo) / (hi - lo);
|
|
102
|
-
return Math.min(4, Math.max(0, Math.round(t * 4)));
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
if (scale === 'quantile') {
|
|
106
|
-
const sorted = [...values].sort((a, b) => a - b);
|
|
107
|
-
const q = (p) => sorted[Math.floor(p * (sorted.length - 1))];
|
|
108
|
-
const breaks = [q(0.2), q(0.4), q(0.6), q(0.8)];
|
|
109
|
-
return (v) => {
|
|
110
|
-
const n = Number(v) || 0;
|
|
111
|
-
if (n <= breaks[0]) return 0;
|
|
112
|
-
if (n <= breaks[1]) return 1;
|
|
113
|
-
if (n <= breaks[2]) return 2;
|
|
114
|
-
if (n <= breaks[3]) return 3;
|
|
115
|
-
return 4;
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
// linear
|
|
119
|
-
return (v) => {
|
|
120
|
-
const n = Number(v) || 0;
|
|
121
|
-
const t = (n - min) / (max - min);
|
|
122
|
-
return Math.min(4, Math.max(0, Math.round(t * 4)));
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
render() {
|
|
127
|
-
const rows = Math.max(1, this.rows || 7);
|
|
128
|
-
const cols = Math.max(1, this.cols || 12);
|
|
129
|
-
|
|
130
|
-
// Build a map for fast lookup
|
|
131
|
-
const map = new Map();
|
|
132
|
-
for (const d of this.#data) {
|
|
133
|
-
if (d && d.r != null && d.c != null) map.set(`${d.r},${d.c}`, d);
|
|
134
|
-
}
|
|
135
|
-
const bucketOf = this.#buckets();
|
|
136
|
-
|
|
137
|
-
// ── Outer host layout: clear everything except <title> slot ──
|
|
138
|
-
// Keep only slotted title children if present.
|
|
139
|
-
const title = this.querySelector(':scope > [slot="title"]');
|
|
140
|
-
this.replaceChildren();
|
|
141
|
-
if (title) this.appendChild(title);
|
|
142
|
-
|
|
143
|
-
// ── Optional month-labels row (day-grid mode) ──
|
|
144
|
-
if (this.type === 'day-grid' && this.startDate) {
|
|
145
|
-
const labels = document.createElement('div');
|
|
146
|
-
labels.setAttribute('data-months', '');
|
|
147
|
-
const start = new Date(this.startDate);
|
|
148
|
-
if (!Number.isNaN(start.getTime())) {
|
|
149
|
-
// For day-grid: rows=7 (days of week), cols=weeks. Label months at column
|
|
150
|
-
// positions where the 1st of the month falls.
|
|
151
|
-
const marks = [];
|
|
152
|
-
for (let c = 0; c < cols; c++) {
|
|
153
|
-
const d = new Date(start);
|
|
154
|
-
d.setDate(d.getDate() + c * 7);
|
|
155
|
-
if (c === 0 || d.getDate() <= 7) {
|
|
156
|
-
marks.push({ col: c, label: MONTHS[d.getMonth()] });
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
for (const m of marks) {
|
|
160
|
-
const span = document.createElement('span');
|
|
161
|
-
span.setAttribute('data-month-label', '');
|
|
162
|
-
span.style.gridColumn = String(m.col + 1);
|
|
163
|
-
span.textContent = m.label;
|
|
164
|
-
labels.appendChild(span);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
labels.style.gridTemplateColumns = `repeat(${cols}, minmax(0, 1fr))`;
|
|
168
|
-
this.appendChild(labels);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// ── Grid of cells ──
|
|
172
|
-
const grid = document.createElement('div');
|
|
173
|
-
grid.setAttribute('data-grid', '');
|
|
174
|
-
grid.style.gridTemplateColumns = `repeat(${cols}, minmax(0, 1fr))`;
|
|
175
|
-
grid.style.gridTemplateRows = `repeat(${rows}, 1fr)`;
|
|
176
|
-
for (let r = 0; r < rows; r++) {
|
|
177
|
-
for (let c = 0; c < cols; c++) {
|
|
178
|
-
const d = map.get(`${r},${c}`);
|
|
179
|
-
const cell = document.createElement('div');
|
|
180
|
-
cell.setAttribute('data-cell', '');
|
|
181
|
-
cell.setAttribute('role', 'gridcell');
|
|
182
|
-
cell.dataset.r = String(r);
|
|
183
|
-
cell.dataset.c = String(c);
|
|
184
|
-
if (d != null) {
|
|
185
|
-
const v = Number(d.v) || 0;
|
|
186
|
-
const bucket = bucketOf(v);
|
|
187
|
-
cell.dataset.bucket = String(bucket);
|
|
188
|
-
cell.dataset.v = String(v);
|
|
189
|
-
if (d.label) cell.setAttribute('aria-label', d.label);
|
|
190
|
-
} else {
|
|
191
|
-
cell.dataset.empty = '';
|
|
192
|
-
}
|
|
193
|
-
grid.appendChild(cell);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
this.appendChild(grid);
|
|
197
|
-
|
|
198
|
-
// ── Legend ──
|
|
199
|
-
if (!this.noLegend) {
|
|
200
|
-
const legend = document.createElement('div');
|
|
201
|
-
legend.setAttribute('data-legend', '');
|
|
202
|
-
const less = document.createElement('span');
|
|
203
|
-
less.setAttribute('data-legend-label', '');
|
|
204
|
-
less.textContent = 'Less';
|
|
205
|
-
legend.appendChild(less);
|
|
206
|
-
for (let i = 0; i < 5; i++) {
|
|
207
|
-
const swatch = document.createElement('span');
|
|
208
|
-
swatch.setAttribute('data-legend-swatch', '');
|
|
209
|
-
swatch.dataset.bucket = String(i);
|
|
210
|
-
legend.appendChild(swatch);
|
|
211
|
-
}
|
|
212
|
-
const more = document.createElement('span');
|
|
213
|
-
more.setAttribute('data-legend-label', '');
|
|
214
|
-
more.textContent = 'More';
|
|
215
|
-
legend.appendChild(more);
|
|
216
|
-
this.appendChild(legend);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
#findCell(target) {
|
|
221
|
-
return target?.closest?.('[data-cell]');
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/* Compose the cell-level event with the canonical chart-* shape so that
|
|
225
|
-
tooltip-ui[follows=pointer][for=this-heatmap] can render without caring
|
|
226
|
-
whether the source is a chart or a heatmap. */
|
|
227
|
-
#chartDetail(cell, event) {
|
|
228
|
-
const r = Number(cell.dataset.r);
|
|
229
|
-
const c = Number(cell.dataset.c);
|
|
230
|
-
const v = Number(cell.dataset.v);
|
|
231
|
-
const label = cell.getAttribute('aria-label') || '';
|
|
232
|
-
return {
|
|
233
|
-
r, c,
|
|
234
|
-
label,
|
|
235
|
-
value: Number.isFinite(v) ? v : null,
|
|
236
|
-
pct: null,
|
|
237
|
-
series: null,
|
|
238
|
-
slot: 0,
|
|
239
|
-
pointerX: event?.clientX ?? null,
|
|
240
|
-
pointerY: event?.clientY ?? null,
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
#hoveredCell = null;
|
|
245
|
-
|
|
246
|
-
#onHover = (e) => {
|
|
247
|
-
const cell = this.#findCell(e.target);
|
|
248
|
-
if (!cell || !cell.dataset.v) return;
|
|
249
|
-
this.#hoveredCell = cell;
|
|
250
|
-
const detail = this.#chartDetail(cell, e);
|
|
251
|
-
/* Legacy cell-specific shape — kept for back-compat. */
|
|
252
|
-
this.dispatchEvent(new CustomEvent('cell-hover', { detail, bubbles: true }));
|
|
253
|
-
/* Canonical chart-hover shape for tooltip-ui[follows=pointer]. */
|
|
254
|
-
this.dispatchEvent(new CustomEvent('chart-hover', { detail, bubbles: true }));
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
#onMove = (e) => {
|
|
258
|
-
const cell = this.#findCell(e.target);
|
|
259
|
-
if (!cell || !cell.dataset.v) {
|
|
260
|
-
if (this.#hoveredCell) {
|
|
261
|
-
this.#hoveredCell = null;
|
|
262
|
-
this.dispatchEvent(new CustomEvent('chart-leave', { bubbles: true }));
|
|
263
|
-
}
|
|
264
|
-
return;
|
|
265
|
-
}
|
|
266
|
-
if (cell !== this.#hoveredCell) {
|
|
267
|
-
this.#hoveredCell = cell;
|
|
268
|
-
const detail = this.#chartDetail(cell, e);
|
|
269
|
-
this.dispatchEvent(new CustomEvent('cell-hover', { detail, bubbles: true }));
|
|
270
|
-
this.dispatchEvent(new CustomEvent('chart-hover', { detail, bubbles: true }));
|
|
271
|
-
} else {
|
|
272
|
-
/* Pointer still inside the same cell — re-fire chart-hover so the
|
|
273
|
-
pointer-follow tooltip can reposition without re-painting content. */
|
|
274
|
-
const detail = this.#chartDetail(cell, e);
|
|
275
|
-
this.dispatchEvent(new CustomEvent('chart-hover', { detail, bubbles: true }));
|
|
276
|
-
}
|
|
277
|
-
};
|
|
278
|
-
|
|
279
|
-
#onLeave = () => {
|
|
280
|
-
if (!this.#hoveredCell) return;
|
|
281
|
-
this.#hoveredCell = null;
|
|
282
|
-
this.dispatchEvent(new CustomEvent('chart-leave', { bubbles: true }));
|
|
283
|
-
};
|
|
284
|
-
|
|
285
|
-
#onClick = (e) => {
|
|
286
|
-
const cell = this.#findCell(e.target);
|
|
287
|
-
if (!cell || !cell.dataset.v) return;
|
|
288
|
-
const detail = this.#chartDetail(cell, e);
|
|
289
|
-
this.dispatchEvent(new CustomEvent('cell-click', { detail, bubbles: true }));
|
|
290
|
-
this.dispatchEvent(new CustomEvent('chart-select', { detail, bubbles: true }));
|
|
291
|
-
};
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
customElements.define('heatmap-ui', UIHeatmap);
|
|
295
17
|
export { UIHeatmap };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Non-side-effect class export for `<icon-ui>`.
|
|
3
|
+
*
|
|
4
|
+
* Importing this file gives you the class(es) without auto-registering the tag.
|
|
5
|
+
* Useful for test isolation, subclassing with tag-name override, or selective
|
|
6
|
+
* composition.
|
|
7
|
+
*
|
|
8
|
+
* The auto-register path stays at `@adia-ai/web-components/components/icon`
|
|
9
|
+
* (which imports this file + calls `defineIfFree()`).
|
|
10
|
+
*
|
|
11
|
+
* @see ../../USAGE.md#registration--auto-vs-explicit
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { UIElement, signal, html } from '../../core/element.js';
|
|
15
|
+
import { getIcon } from '../../core/icons.js';
|
|
16
|
+
|
|
17
|
+
export class UIIcon extends UIElement {
|
|
18
|
+
static properties = {
|
|
19
|
+
name: { type: String, default: '', reflect: true },
|
|
20
|
+
size: { type: String, default: '', reflect: true },
|
|
21
|
+
label: { type: String, default: '', reflect: true },
|
|
22
|
+
/** regular | thin | light | bold | fill | duotone */
|
|
23
|
+
weight: { type: String, default: 'regular', reflect: true },
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
connected() {
|
|
27
|
+
this.setAttribute('role', this.label ? 'img' : 'presentation');
|
|
28
|
+
if (!this.label) this.setAttribute('aria-hidden', 'true');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
render() {
|
|
32
|
+
if (this.label) this.setAttribute('aria-label', this.label);
|
|
33
|
+
const svg = getIcon(this.name, this.weight || 'regular');
|
|
34
|
+
if (svg && this.innerHTML !== svg) this.innerHTML = svg;
|
|
35
|
+
|
|
36
|
+
// Pixel / rem / em passthrough — `size="48"` → 48px,
|
|
37
|
+
// `size="3.5rem"` → 3.5rem. Named-scale values (xs/sm/md/lg/xl/
|
|
38
|
+
// 2xl/3xl/4xl/fill) are handled by `:scope[size="…"]` rules in
|
|
39
|
+
// icon.css; we only set --icon-size inline for free-form values.
|
|
40
|
+
if (this.size && this.#isFreeFormSize(this.size)) {
|
|
41
|
+
this.style.setProperty('--icon-size', this.#normalizeSize(this.size));
|
|
42
|
+
} else if (this.style.getPropertyValue('--icon-size')) {
|
|
43
|
+
this.style.removeProperty('--icon-size');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
#isFreeFormSize(s) {
|
|
48
|
+
return /^\d+(\.\d+)?(px|rem|em)?$/.test(s);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
#normalizeSize(s) {
|
|
52
|
+
return /^\d+(\.\d+)?$/.test(s) ? `${s}px` : s;
|
|
53
|
+
}
|
|
54
|
+
}
|