@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.
Files changed (284) hide show
  1. package/USAGE.md +29 -9
  2. package/components/accordion/accordion.d.ts +17 -0
  3. package/components/accordion/accordion.js +10 -117
  4. package/components/accordion/class.js +132 -0
  5. package/components/action-list/action-list.d.ts +15 -0
  6. package/components/action-list/action-list.js +9 -140
  7. package/components/action-list/class.js +156 -0
  8. package/components/agent-artifact/agent-artifact.d.ts +25 -0
  9. package/components/agent-artifact/agent-artifact.js +8 -181
  10. package/components/agent-artifact/class.js +200 -0
  11. package/components/agent-feedback-bar/agent-feedback-bar.d.ts +21 -0
  12. package/components/agent-feedback-bar/agent-feedback-bar.js +8 -143
  13. package/components/agent-feedback-bar/class.js +162 -0
  14. package/components/agent-questions/agent-questions.d.ts +23 -0
  15. package/components/agent-questions/agent-questions.js +8 -180
  16. package/components/agent-questions/class.js +199 -0
  17. package/components/agent-reasoning/agent-reasoning.d.ts +23 -0
  18. package/components/agent-reasoning/agent-reasoning.js +8 -494
  19. package/components/agent-reasoning/class.js +513 -0
  20. package/components/agent-suggestions/agent-suggestions.d.ts +21 -0
  21. package/components/agent-suggestions/agent-suggestions.js +8 -78
  22. package/components/agent-suggestions/class.js +97 -0
  23. package/components/agent-trace/agent-trace.d.ts +19 -0
  24. package/components/alert/alert.d.ts +29 -0
  25. package/components/alert/alert.js +8 -175
  26. package/components/alert/class.js +194 -0
  27. package/components/avatar/avatar.d.ts +27 -0
  28. package/components/avatar/avatar.js +9 -159
  29. package/components/avatar/class.js +173 -0
  30. package/components/badge/badge.d.ts +27 -0
  31. package/components/badge/badge.js +9 -75
  32. package/components/badge/class.js +93 -0
  33. package/components/block/block.d.ts +19 -0
  34. package/components/block/block.js +9 -15
  35. package/components/block/class.js +33 -0
  36. package/components/breadcrumb/breadcrumb.d.ts +23 -0
  37. package/components/breadcrumb/breadcrumb.js +8 -113
  38. package/components/breadcrumb/class.js +132 -0
  39. package/components/button/button.d.ts +34 -0
  40. package/components/button/button.js +15 -66
  41. package/components/button/class.js +80 -0
  42. package/components/calendar-picker/calendar-picker.a2ui.json +6 -1
  43. package/components/calendar-picker/calendar-picker.js +8 -332
  44. package/components/calendar-picker/calendar-picker.yaml +51 -177
  45. package/components/calendar-picker/class.js +351 -0
  46. package/components/canvas/canvas.a2ui.json +6 -1
  47. package/components/canvas/canvas.d.ts +17 -0
  48. package/components/canvas/canvas.yaml +19 -36
  49. package/components/card/card.a2ui.json +3 -0
  50. package/components/card/card.d.ts +27 -0
  51. package/components/card/card.js +9 -50
  52. package/components/card/card.yaml +171 -433
  53. package/components/card/class.js +68 -0
  54. package/components/chart/chart.d.ts +41 -0
  55. package/components/chart/chart.js +8 -2131
  56. package/components/chart/class.js +2150 -0
  57. package/components/chart-legend/chart-legend.d.ts +27 -0
  58. package/components/chart-legend/chart-legend.js +8 -197
  59. package/components/chart-legend/class.js +215 -0
  60. package/components/chat-thread/chat-thread.d.ts +17 -0
  61. package/components/chat-thread/chat-thread.js +8 -157
  62. package/components/chat-thread/class.js +176 -0
  63. package/components/check/check.js +11 -52
  64. package/components/check/class.js +68 -0
  65. package/components/code/class.js +501 -0
  66. package/components/code/code.js +8 -482
  67. package/components/col/class.js +30 -0
  68. package/components/col/col.d.ts +23 -0
  69. package/components/col/col.js +10 -13
  70. package/components/color-picker/class.js +550 -0
  71. package/components/color-picker/color-picker.js +8 -531
  72. package/components/command/class.js +364 -0
  73. package/components/command/command.a2ui.json +3 -0
  74. package/components/command/command.d.ts +19 -0
  75. package/components/command/command.js +8 -345
  76. package/components/command/command.yaml +105 -124
  77. package/components/demo-toggle/class.js +153 -0
  78. package/components/demo-toggle/demo-toggle.d.ts +23 -0
  79. package/components/demo-toggle/demo-toggle.js +8 -135
  80. package/components/description-list/class.js +86 -0
  81. package/components/description-list/description-list.d.ts +21 -0
  82. package/components/description-list/description-list.js +8 -67
  83. package/components/divider/class.js +57 -0
  84. package/components/divider/divider.d.ts +19 -0
  85. package/components/divider/divider.js +10 -40
  86. package/components/drawer/class.js +306 -0
  87. package/components/drawer/drawer.d.ts +25 -0
  88. package/components/drawer/drawer.js +8 -287
  89. package/components/embed/class.js +73 -0
  90. package/components/embed/embed.d.ts +23 -0
  91. package/components/embed/embed.js +9 -55
  92. package/components/empty-state/class.js +108 -0
  93. package/components/empty-state/empty-state.d.ts +21 -0
  94. package/components/empty-state/empty-state.js +9 -90
  95. package/components/feed/class.js +381 -0
  96. package/components/feed/feed.d.ts +19 -0
  97. package/components/feed/feed.js +9 -367
  98. package/components/field/class.js +266 -0
  99. package/components/field/field.d.ts +23 -0
  100. package/components/field/field.js +8 -247
  101. package/components/fields/class.js +106 -0
  102. package/components/fields/fields.d.ts +19 -0
  103. package/components/fields/fields.js +8 -87
  104. package/components/grid/class.js +31 -0
  105. package/components/grid/grid.d.ts +23 -0
  106. package/components/grid/grid.js +10 -14
  107. package/components/heatmap/class.js +305 -0
  108. package/components/heatmap/heatmap.d.ts +31 -0
  109. package/components/heatmap/heatmap.js +8 -286
  110. package/components/icon/class.js +54 -0
  111. package/components/icon/icon.d.ts +23 -0
  112. package/components/icon/icon.js +13 -40
  113. package/components/image/class.js +112 -0
  114. package/components/image/image.d.ts +33 -0
  115. package/components/image/image.js +9 -94
  116. package/components/input/class.js +773 -0
  117. package/components/input/input.a2ui.json +3 -0
  118. package/components/input/input.js +8 -755
  119. package/components/input/input.yaml +171 -442
  120. package/components/inspector/class.js +142 -0
  121. package/components/inspector/inspector.a2ui.json +8 -1
  122. package/components/inspector/inspector.d.ts +17 -0
  123. package/components/inspector/inspector.js +8 -124
  124. package/components/inspector/inspector.yaml +15 -30
  125. package/components/kbd/class.js +34 -0
  126. package/components/kbd/kbd.a2ui.json +3 -0
  127. package/components/kbd/kbd.d.ts +17 -0
  128. package/components/kbd/kbd.js +10 -17
  129. package/components/kbd/kbd.yaml +54 -185
  130. package/components/link/class.js +187 -0
  131. package/components/link/link.d.ts +55 -0
  132. package/components/link/link.js +8 -168
  133. package/components/list/class.js +249 -0
  134. package/components/list/list.d.ts +23 -0
  135. package/components/list/list.js +9 -231
  136. package/components/menu/class.js +332 -0
  137. package/components/menu/menu.d.ts +21 -0
  138. package/components/menu/menu.js +11 -316
  139. package/components/modal/class.js +231 -0
  140. package/components/modal/modal.a2ui.json +5 -1
  141. package/components/modal/modal.d.ts +23 -0
  142. package/components/modal/modal.js +8 -212
  143. package/components/modal/modal.yaml +19 -39
  144. package/components/nav/class.js +150 -0
  145. package/components/nav/nav.d.ts +31 -0
  146. package/components/nav/nav.js +8 -131
  147. package/components/nav-group/class.js +152 -0
  148. package/components/nav-group/nav-group.d.ts +35 -0
  149. package/components/nav-group/nav-group.js +9 -134
  150. package/components/nav-item/class.js +86 -0
  151. package/components/nav-item/nav-item.d.ts +37 -0
  152. package/components/nav-item/nav-item.js +10 -69
  153. package/components/noodles/class.js +510 -0
  154. package/components/noodles/noodles.d.ts +33 -0
  155. package/components/noodles/noodles.js +9 -493
  156. package/components/option-card/class.js +167 -0
  157. package/components/option-card/option-card.js +8 -149
  158. package/components/otp-input/class.js +180 -0
  159. package/components/otp-input/otp-input.a2ui.json +5 -1
  160. package/components/otp-input/otp-input.js +9 -162
  161. package/components/otp-input/otp-input.yaml +45 -174
  162. package/components/page/class.js +97 -0
  163. package/components/page/page.d.ts +46 -0
  164. package/components/page/page.js +8 -79
  165. package/components/pagination/class.js +195 -0
  166. package/components/pagination/pagination.d.ts +23 -0
  167. package/components/pagination/pagination.js +9 -177
  168. package/components/pane/class.js +186 -0
  169. package/components/pane/pane.a2ui.json +12 -1
  170. package/components/pane/pane.d.ts +31 -0
  171. package/components/pane/pane.js +8 -167
  172. package/components/pane/pane.yaml +57 -157
  173. package/components/pipeline-status/class.js +189 -0
  174. package/components/pipeline-status/pipeline-status.a2ui.json +7 -1
  175. package/components/pipeline-status/pipeline-status.d.ts +21 -0
  176. package/components/pipeline-status/pipeline-status.js +9 -172
  177. package/components/pipeline-status/pipeline-status.yaml +34 -72
  178. package/components/popover/class.js +194 -0
  179. package/components/popover/popover.d.ts +23 -0
  180. package/components/popover/popover.js +9 -176
  181. package/components/progress/class.js +74 -0
  182. package/components/progress/progress.a2ui.json +3 -0
  183. package/components/progress/progress.d.ts +19 -0
  184. package/components/progress/progress.js +10 -57
  185. package/components/progress/progress.yaml +124 -287
  186. package/components/progress-row/class.js +110 -0
  187. package/components/progress-row/progress-row.d.ts +23 -0
  188. package/components/progress-row/progress-row.js +8 -92
  189. package/components/radio/class.js +83 -0
  190. package/components/radio/radio.js +11 -67
  191. package/components/range/class.js +194 -0
  192. package/components/range/range.js +9 -176
  193. package/components/rating/class.js +148 -0
  194. package/components/rating/rating.js +9 -130
  195. package/components/richtext/class.js +87 -0
  196. package/components/richtext/richtext.a2ui.json +7 -1
  197. package/components/richtext/richtext.d.ts +19 -0
  198. package/components/richtext/richtext.js +8 -68
  199. package/components/richtext/richtext.yaml +30 -65
  200. package/components/row/class.js +50 -0
  201. package/components/row/row.d.ts +27 -0
  202. package/components/row/row.js +10 -33
  203. package/components/search/class.js +134 -0
  204. package/components/search/search.js +10 -117
  205. package/components/segment/class.js +62 -0
  206. package/components/segment/segment.d.ts +25 -0
  207. package/components/segment/segment.js +10 -45
  208. package/components/segmented/class.js +165 -0
  209. package/components/segmented/segmented.a2ui.json +4 -0
  210. package/components/segmented/segmented.js +10 -148
  211. package/components/segmented/segmented.yaml +41 -59
  212. package/components/select/class.js +408 -0
  213. package/components/select/select.js +15 -396
  214. package/components/skeleton/class.js +52 -0
  215. package/components/skeleton/skeleton.d.ts +23 -0
  216. package/components/skeleton/skeleton.js +8 -34
  217. package/components/slider/class.js +184 -0
  218. package/components/slider/slider.js +9 -166
  219. package/components/stack/class.js +28 -0
  220. package/components/stack/stack.d.ts +17 -0
  221. package/components/stack/stack.js +10 -11
  222. package/components/step-progress/class.js +98 -0
  223. package/components/step-progress/step-progress.d.ts +27 -0
  224. package/components/step-progress/step-progress.js +8 -79
  225. package/components/stepper/class.js +126 -0
  226. package/components/stepper/stepper.d.ts +19 -0
  227. package/components/stepper/stepper.js +9 -112
  228. package/components/stream/class.js +109 -0
  229. package/components/stream/stream.d.ts +19 -0
  230. package/components/stream/stream.js +8 -90
  231. package/components/swatch/class.js +131 -0
  232. package/components/swatch/swatch.d.ts +28 -0
  233. package/components/swatch/swatch.js +8 -112
  234. package/components/swiper/class.js +373 -0
  235. package/components/swiper/swiper.a2ui.json +4 -0
  236. package/components/swiper/swiper.d.ts +31 -0
  237. package/components/swiper/swiper.js +8 -354
  238. package/components/swiper/swiper.yaml +68 -212
  239. package/components/switch/class.js +63 -0
  240. package/components/switch/switch.a2ui.json +6 -1
  241. package/components/switch/switch.js +11 -47
  242. package/components/switch/switch.yaml +70 -265
  243. package/components/table/class.js +1453 -0
  244. package/components/table/table.d.ts +37 -0
  245. package/components/table/table.js +8 -1435
  246. package/components/table-toolbar/class.js +680 -0
  247. package/components/table-toolbar/table-toolbar.d.ts +33 -0
  248. package/components/table-toolbar/table-toolbar.js +8 -689
  249. package/components/tabs/class.js +242 -0
  250. package/components/tabs/tabs.d.ts +21 -0
  251. package/components/tabs/tabs.js +8 -223
  252. package/components/tag/class.js +99 -0
  253. package/components/tag/tag.d.ts +27 -0
  254. package/components/tag/tag.js +8 -80
  255. package/components/text/class.js +46 -0
  256. package/components/text/text.d.ts +25 -0
  257. package/components/text/text.js +9 -28
  258. package/components/textarea/class.js +134 -0
  259. package/components/textarea/textarea.js +11 -118
  260. package/components/timeline/class.js +176 -0
  261. package/components/timeline/timeline.d.ts +19 -0
  262. package/components/timeline/timeline.js +9 -162
  263. package/components/toast/class.js +92 -0
  264. package/components/toast/toast.d.ts +23 -0
  265. package/components/toast/toast.js +9 -76
  266. package/components/toggle-group/class.js +154 -0
  267. package/components/toggle-group/toggle-group.d.ts +19 -0
  268. package/components/toggle-group/toggle-group.js +11 -140
  269. package/components/toggle-scheme/class.js +286 -0
  270. package/components/toggle-scheme/toggle-scheme.d.ts +41 -0
  271. package/components/toggle-scheme/toggle-scheme.js +8 -268
  272. package/components/toolbar/class.js +388 -0
  273. package/components/toolbar/toolbar.d.ts +23 -0
  274. package/components/toolbar/toolbar.js +10 -376
  275. package/components/tooltip/class.js +299 -0
  276. package/components/tooltip/tooltip.d.ts +27 -0
  277. package/components/tooltip/tooltip.js +8 -280
  278. package/components/tree/class.js +245 -0
  279. package/components/tree/tree.d.ts +15 -0
  280. package/components/tree/tree.js +9 -244
  281. package/components/upload/class.js +199 -0
  282. package/components/upload/upload.js +11 -183
  283. package/index.d.ts +159 -5
  284. 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
- * <heatmap-ui>Grid-based heatmap / density / day-grid visualization.
2
+ * `<heatmap-ui>`auto-registers the tag on import.
3
3
  *
4
- * Attributes:
5
- * type — 'day-grid' | 'matrix' | 'density' (default 'matrix')
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
- * Events:
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
- * Slots:
20
- * title — optional header title
9
+ * @see ../../USAGE.md#registration--auto-vs-explicit
21
10
  */
22
11
 
23
- import { UIElement } from '../../core/element.js';
12
+ import { defineIfFree } from '../../core/register.js';
13
+ import { UIHeatmap } from './class.js';
24
14
 
25
- const MONTHS = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
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
+ }