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