@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
@@ -1,540 +1,17 @@
1
1
  /**
2
- * <color-picker-ui value="#3b82f6" format="hex"></color-picker-ui>
2
+ * `<color-picker-ui>` auto-registers the tag on import.
3
3
  *
4
- * OKLCH-native color picker with 2D area + H/C/L sliders.
4
+ * For non-side-effect class import (test isolation, tag override), use
5
+ * the `class` subpath:
5
6
  *
6
- * Layout:
7
- * [═══════════ color area (chroma x lightness) ═══════════]
8
- * H [────────────── slider ──────────────] 260
9
- * C [────────────── slider ──────────────] 0.188
10
- * L [────────────── slider ──────────────] 0.623
11
- * [───────────────── hue track ──────────────────]
12
- * oklch(0.62 0.188 260) [copy]
13
- * #3b82f6 [copy]
7
+ * import { UIColorPicker } from '@adia-ai/web-components/components/color-picker/class';
14
8
  *
15
- * Dogfoods: slider-ui (H/C/L channels)
16
- * Form-associated via UIFormElement + ElementInternals.
9
+ * @see ../../USAGE.md#registration--auto-vs-explicit
17
10
  */
18
11
 
19
- import { UIFormElement } from '../../core/form.js';
12
+ import { defineIfFree } from '../../core/register.js';
13
+ import { UIColorPicker } from './class.js';
20
14
 
21
- // ── OKLCH ↔ sRGB conversion ──────────────────────────────
15
+ defineIfFree('color-picker-ui', UIColorPicker);
22
16
 
23
- const MAX_CHROMA = 0.4;
24
-
25
- function oklchToOklab(L, C, H) {
26
- const hRad = H * Math.PI / 180;
27
- return { L, a: C * Math.cos(hRad), b: C * Math.sin(hRad) };
28
- }
29
-
30
- function oklabToLinearSrgb(L, a, b) {
31
- const l_ = L + 0.3963377774 * a + 0.2158037573 * b;
32
- const m_ = L - 0.1055613458 * a - 0.0638541728 * b;
33
- const s_ = L - 0.0894841775 * a - 1.2914855480 * b;
34
- const l = l_ * l_ * l_;
35
- const m = m_ * m_ * m_;
36
- const s = s_ * s_ * s_;
37
- return [
38
- +4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,
39
- -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,
40
- -0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s,
41
- ];
42
- }
43
-
44
- function linearToSrgb(c) {
45
- return c <= 0.0031308 ? 12.92 * c : 1.055 * Math.pow(c, 1 / 2.4) - 0.055;
46
- }
47
-
48
- function srgbToLinear(c) {
49
- return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
50
- }
51
-
52
- function oklchToRgb(L, C, H) {
53
- const { a, b } = oklchToOklab(L, C, H);
54
- const [lr, lg, lb] = oklabToLinearSrgb(L, a, b);
55
- return [
56
- Math.max(0, Math.min(1, linearToSrgb(lr))),
57
- Math.max(0, Math.min(1, linearToSrgb(lg))),
58
- Math.max(0, Math.min(1, linearToSrgb(lb))),
59
- ];
60
- }
61
-
62
- function rgbToHex(r, g, b) {
63
- const h = c => Math.round(c * 255).toString(16).padStart(2, '0');
64
- return `#${h(r)}${h(g)}${h(b)}`;
65
- }
66
-
67
- function oklchToHex(L, C, H) {
68
- const [r, g, b] = oklchToRgb(L, C, H);
69
- return rgbToHex(r, g, b);
70
- }
71
-
72
- function hexToOklch(hex) {
73
- hex = hex.replace('#', '');
74
- if (hex.length === 3) hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
75
- const r = srgbToLinear(parseInt(hex.slice(0,2),16)/255);
76
- const g = srgbToLinear(parseInt(hex.slice(2,4),16)/255);
77
- const b = srgbToLinear(parseInt(hex.slice(4,6),16)/255);
78
- const l_ = Math.cbrt(0.4122214708*r + 0.5363325363*g + 0.0514459929*b);
79
- const m_ = Math.cbrt(0.2119034982*r + 0.6806995451*g + 0.1073969566*b);
80
- const s_ = Math.cbrt(0.0883024619*r + 0.2817188376*g + 0.6299787005*b);
81
- const L = 0.2104542553*l_ + 0.7936177850*m_ - 0.0040720468*s_;
82
- const a = 1.9779984951*l_ - 2.4285922050*m_ + 0.4505937099*s_;
83
- const bv = 0.0259040371*l_ + 0.7827717662*m_ - 0.8086757660*s_;
84
- const C = Math.sqrt(a*a + bv*bv);
85
- let H = Math.atan2(bv, a) * 180 / Math.PI;
86
- if (H < 0) H += 360;
87
- return { L, C, H };
88
- }
89
-
90
- function isInGamut(r, g, b) {
91
- const e = 0.001;
92
- return r >= -e && r <= 1+e && g >= -e && g <= 1+e && b >= -e && b <= 1+e;
93
- }
94
-
95
- function gamutMapChroma(L, C, H) {
96
- const { a, b } = oklchToOklab(L, C, H);
97
- const [lr, lg, lb] = oklabToLinearSrgb(L, a, b);
98
- if (isInGamut(linearToSrgb(lr), linearToSrgb(lg), linearToSrgb(lb))) return C;
99
- let lo = 0, hi = C;
100
- for (let i = 0; i < 8; i++) {
101
- const mid = (lo + hi) / 2;
102
- const { a: ma, b: mb } = oklchToOklab(L, mid, H);
103
- const [mr, mg, mbb] = oklabToLinearSrgb(L, ma, mb);
104
- if (isInGamut(linearToSrgb(mr), linearToSrgb(mg), linearToSrgb(mbb))) lo = mid;
105
- else hi = mid;
106
- }
107
- return lo;
108
- }
109
-
110
- // ── Component ────────────────────────────────────────────
111
-
112
- class UIColorPicker extends UIFormElement {
113
- static properties = {
114
- ...UIFormElement.properties,
115
- value: { type: String, default: '#3b82f6', reflect: true },
116
- format: { type: String, default: 'hex', reflect: true },
117
- disabled: { type: Boolean, default: false, reflect: true },
118
- };
119
-
120
- #L = 0.6; #C = 0.15; #H = 230;
121
- #bound = false;
122
- #dragging = null; // 'area' | 'hue' | null
123
- #internalUpdate = false;
124
-
125
- // DOM refs
126
- #canvas = null;
127
- #ctx = null;
128
- #areaEl = null;
129
- #areaThumb = null;
130
- #hueTrack = null;
131
- #hueThumb = null;
132
- #oklchVal = null;
133
- #hexVal = null;
134
- #areaResizeObs = null;
135
- #hueSlider = null;
136
- #chromaSlider = null;
137
- #lightnessSlider = null;
138
- #copyButtons = []; // [{ el, handler }]
139
- #copyTimer = null;
140
-
141
- static template = () => null;
142
-
143
- // ── Stable listeners ──
144
-
145
- #onSliderInput = (e) => {
146
- const slider = e.currentTarget;
147
- const channel = slider.getAttribute('data-channel');
148
- if (channel === 'hue') this.#H = +slider.value;
149
- else if (channel === 'chroma') this.#C = +slider.value;
150
- else if (channel === 'lightness') this.#L = +slider.value;
151
- this.#commit('input');
152
- };
153
-
154
- #onAreaPointerDown = (e) => {
155
- if (this.disabled) return;
156
- this.#dragging = 'area';
157
- this.#areaEl.setPointerCapture(e.pointerId);
158
- this.#updateAreaFromPointer(e);
159
- };
160
-
161
- #onAreaPointerMove = (e) => {
162
- if (this.#dragging === 'area') this.#updateAreaFromPointer(e);
163
- };
164
-
165
- #onAreaPointerUp = () => {
166
- if (this.#dragging === 'area') { this.#dragging = null; this.#commit('change'); }
167
- };
168
-
169
- #onHuePointerDown = (e) => {
170
- if (this.disabled) return;
171
- this.#dragging = 'hue';
172
- this.#hueTrack.setPointerCapture(e.pointerId);
173
- this.#updateHueFromPointer(e);
174
- };
175
-
176
- #onHuePointerMove = (e) => {
177
- if (this.#dragging === 'hue') this.#updateHueFromPointer(e);
178
- };
179
-
180
- #onHuePointerUp = () => {
181
- if (this.#dragging === 'hue') { this.#dragging = null; this.#commit('change'); }
182
- };
183
-
184
- #onAreaKeydown = (e) => {
185
- if (this.disabled) return;
186
- const step = e.shiftKey ? 0.05 : 0.01;
187
- const cStep = e.shiftKey ? 0.04 : 0.004;
188
- switch (e.key) {
189
- case 'ArrowRight': e.preventDefault(); this.#C = Math.min(MAX_CHROMA, this.#C + cStep); break;
190
- case 'ArrowLeft': e.preventDefault(); this.#C = Math.max(0, this.#C - cStep); break;
191
- case 'ArrowUp': e.preventDefault(); this.#L = Math.min(1, this.#L + step); break;
192
- case 'ArrowDown': e.preventDefault(); this.#L = Math.max(0, this.#L - step); break;
193
- default: return;
194
- }
195
- this.#commit('change');
196
- };
197
-
198
- #onHueKeydown = (e) => {
199
- if (this.disabled) return;
200
- const step = e.shiftKey ? 10 : 1;
201
- switch (e.key) {
202
- case 'ArrowRight': case 'ArrowUp': e.preventDefault(); this.#H = (this.#H + step) % 360; break;
203
- case 'ArrowLeft': case 'ArrowDown': e.preventDefault(); this.#H = (this.#H - step + 360) % 360; break;
204
- default: return;
205
- }
206
- this.#commit('change');
207
- };
208
-
209
- connected() {
210
- super.connected();
211
- this.#parseValue(this.value);
212
-
213
- if (this.#bound) return;
214
- this.#bound = true;
215
-
216
- // ── Build DOM ──
217
-
218
- // Color area
219
- this.#areaEl = document.createElement('div');
220
- this.#areaEl.setAttribute('data-area', '');
221
- this.#areaEl.setAttribute('tabindex', '0');
222
- this.#areaEl.setAttribute('role', 'slider');
223
- this.#areaEl.setAttribute('aria-label', 'Color area: chroma and lightness');
224
-
225
- this.#canvas = document.createElement('canvas');
226
- this.#canvas.style.cssText = 'width:100%;height:100%;display:block;border-radius:inherit;';
227
- this.#areaEl.appendChild(this.#canvas);
228
- this.#ctx = this.#canvas.getContext('2d', { willReadFrequently: true });
229
-
230
- this.#areaThumb = document.createElement('div');
231
- this.#areaThumb.setAttribute('data-area-thumb', '');
232
- this.#areaEl.appendChild(this.#areaThumb);
233
-
234
- this.appendChild(this.#areaEl);
235
-
236
- // Slider rows
237
- const sliders = document.createElement('div');
238
- sliders.setAttribute('data-sliders', '');
239
-
240
- const makeSlider = (label, channel, min, max, step) => {
241
- const slider = document.createElement('slider-ui');
242
- slider.setAttribute('data-channel', channel);
243
- slider.setAttribute('label', label);
244
- slider.setAttribute('min', min);
245
- slider.setAttribute('max', max);
246
- slider.setAttribute('step', step);
247
-
248
- slider.addEventListener('input', this.#onSliderInput);
249
-
250
- sliders.appendChild(slider);
251
- return slider;
252
- };
253
-
254
- this.#hueSlider = makeSlider('H', 'hue', 0, 360, 1);
255
- this.#chromaSlider = makeSlider('C', 'chroma', 0, MAX_CHROMA, 0.001);
256
- this.#lightnessSlider = makeSlider('L', 'lightness', 0, 1, 0.001);
257
- this.appendChild(sliders);
258
-
259
- // Hue gradient track
260
- this.#hueTrack = document.createElement('div');
261
- this.#hueTrack.setAttribute('data-hue-track', '');
262
- this.#hueTrack.setAttribute('tabindex', '0');
263
- this.#hueTrack.setAttribute('role', 'slider');
264
- this.#hueTrack.setAttribute('aria-label', 'Hue');
265
- this.#hueThumb = document.createElement('div');
266
- this.#hueThumb.setAttribute('data-hue-thumb', '');
267
- this.#hueTrack.appendChild(this.#hueThumb);
268
- this.appendChild(this.#hueTrack);
269
-
270
- // Output rows
271
- const output = document.createElement('div');
272
- output.setAttribute('data-output', '');
273
-
274
- const makeOutput = (format) => {
275
- const group = document.createElement('div');
276
- group.setAttribute('data-output-group', '');
277
-
278
- const val = document.createElement('span');
279
- val.setAttribute('data-output-value', '');
280
- val.setAttribute('data-format', format);
281
-
282
- const btn = document.createElement('div');
283
- btn.setAttribute('role', 'button');
284
- btn.setAttribute('tabindex', '0');
285
- btn.setAttribute('data-copy', format);
286
- btn.setAttribute('aria-label', `Copy ${format}`);
287
- btn.innerHTML = '<icon-ui name="copy"></icon-ui>';
288
- const handler = () => this.#copy(format, btn);
289
- btn.addEventListener('click', handler);
290
- this.#copyButtons.push({ el: btn, handler });
291
-
292
- group.appendChild(val);
293
- group.appendChild(btn);
294
- output.appendChild(group);
295
- return val;
296
- };
297
-
298
- this.#oklchVal = makeOutput('oklch');
299
- this.#hexVal = makeOutput('hex');
300
- this.appendChild(output);
301
-
302
- // ── Area drag ──
303
-
304
- this.#areaEl.addEventListener('pointerdown', this.#onAreaPointerDown);
305
- this.#areaEl.addEventListener('pointermove', this.#onAreaPointerMove);
306
- this.#areaEl.addEventListener('pointerup', this.#onAreaPointerUp);
307
-
308
- // ── Hue track drag ──
309
-
310
- this.#hueTrack.addEventListener('pointerdown', this.#onHuePointerDown);
311
- this.#hueTrack.addEventListener('pointermove', this.#onHuePointerMove);
312
- this.#hueTrack.addEventListener('pointerup', this.#onHuePointerUp);
313
-
314
- // ── Keyboard on area ──
315
-
316
- this.#areaEl.addEventListener('keydown', this.#onAreaKeydown);
317
-
318
- // ── Keyboard on hue ──
319
-
320
- this.#hueTrack.addEventListener('keydown', this.#onHueKeydown);
321
-
322
- // Observe area resize to redraw canvas
323
- this.#areaResizeObs = new ResizeObserver(() => this.#drawArea());
324
- this.#areaResizeObs.observe(this.#areaEl);
325
- }
326
-
327
- disconnected() {
328
- super.disconnected();
329
- if (this.#copyTimer != null) {
330
- clearTimeout(this.#copyTimer);
331
- this.#copyTimer = null;
332
- }
333
- if (this.#areaResizeObs) {
334
- this.#areaResizeObs.disconnect();
335
- this.#areaResizeObs = null;
336
- }
337
- if (this.#areaEl) {
338
- this.#areaEl.removeEventListener('pointerdown', this.#onAreaPointerDown);
339
- this.#areaEl.removeEventListener('pointermove', this.#onAreaPointerMove);
340
- this.#areaEl.removeEventListener('pointerup', this.#onAreaPointerUp);
341
- this.#areaEl.removeEventListener('keydown', this.#onAreaKeydown);
342
- }
343
- if (this.#hueTrack) {
344
- this.#hueTrack.removeEventListener('pointerdown', this.#onHuePointerDown);
345
- this.#hueTrack.removeEventListener('pointermove', this.#onHuePointerMove);
346
- this.#hueTrack.removeEventListener('pointerup', this.#onHuePointerUp);
347
- this.#hueTrack.removeEventListener('keydown', this.#onHueKeydown);
348
- }
349
- this.#hueSlider?.removeEventListener('input', this.#onSliderInput);
350
- this.#chromaSlider?.removeEventListener('input', this.#onSliderInput);
351
- this.#lightnessSlider?.removeEventListener('input', this.#onSliderInput);
352
- for (const { el, handler } of this.#copyButtons) {
353
- el.removeEventListener('click', handler);
354
- }
355
- this.#copyButtons = [];
356
- this.#bound = false;
357
- }
358
-
359
- // ── Pointer helpers ──
360
-
361
- #updateAreaFromPointer(e) {
362
- const rect = this.#areaEl.getBoundingClientRect();
363
- const x = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
364
- const y = Math.max(0, Math.min(1, (e.clientY - rect.top) / rect.height));
365
- this.#C = x * MAX_CHROMA;
366
- this.#L = 1 - y;
367
- this.#commit('input');
368
- }
369
-
370
- #updateHueFromPointer(e) {
371
- const rect = this.#hueTrack.getBoundingClientRect();
372
- const x = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
373
- this.#H = x * 360;
374
- this.#commit('input');
375
- }
376
-
377
- // ── Value parsing ──
378
-
379
- #parseValue(val) {
380
- if (!val) return;
381
- if (val.startsWith('#')) {
382
- const o = hexToOklch(val);
383
- this.#L = o.L; this.#C = o.C; this.#H = o.H;
384
- } else if (val.startsWith('oklch(')) {
385
- const m = val.match(/oklch\(\s*([\d.]+)\s+([\d.]+)\s+([\d.]+)/);
386
- if (m) { this.#L = +m[1]; this.#C = +m[2]; this.#H = +m[3]; }
387
- }
388
- }
389
-
390
- // ── Commit: update value + fire events ──
391
-
392
- #commit(eventType) {
393
- const clampedC = gamutMapChroma(this.#L, this.#C, this.#H);
394
- const hexVal = oklchToHex(this.#L, clampedC, this.#H);
395
- const oklchStr = this.#oklchStr();
396
-
397
- this.#internalUpdate = true;
398
- this.value = this.format === 'oklch' ? oklchStr : hexVal;
399
- this.syncValue();
400
-
401
- const detail = { value: this.value, l: this.#L, c: this.#C, h: this.#H, hex: hexVal, oklch: oklchStr };
402
- this.dispatchEvent(new CustomEvent(eventType, { bubbles: true, detail }));
403
- }
404
-
405
- #oklchStr() {
406
- return `oklch(${this.#L.toFixed(2)} ${this.#C.toFixed(3)} ${Math.round(this.#H)})`;
407
- }
408
-
409
- // ── Copy ──
410
-
411
- #copy(fmt, btn) {
412
- const text = fmt === 'oklch'
413
- ? this.#oklchStr()
414
- : oklchToHex(this.#L, gamutMapChroma(this.#L, this.#C, this.#H), this.#H);
415
-
416
- const iconEl = btn.querySelector('icon-ui');
417
- const onDone = (ok) => {
418
- if (iconEl) {
419
- iconEl.setAttribute('name', ok ? 'check' : 'warning');
420
- if (this.#copyTimer != null) clearTimeout(this.#copyTimer);
421
- this.#copyTimer = setTimeout(() => {
422
- this.#copyTimer = null;
423
- iconEl.setAttribute('name', 'copy');
424
- }, 1500);
425
- }
426
- };
427
-
428
- if (navigator.clipboard?.writeText) {
429
- navigator.clipboard.writeText(text).then(
430
- () => { if (!this.isConnected) return; onDone(true); },
431
- () => { if (!this.isConnected) return; onDone(false); },
432
- );
433
- } else {
434
- const ta = Object.assign(document.createElement('textarea'), { value: text });
435
- ta.style.cssText = 'position:fixed;opacity:0';
436
- document.body.appendChild(ta);
437
- ta.select();
438
- onDone(document.execCommand('copy'));
439
- ta.remove();
440
- }
441
- }
442
-
443
- // ── Canvas rendering ──
444
-
445
- #drawArea() {
446
- if (!this.#canvas || !this.#ctx || !this.#areaEl) return;
447
-
448
- const dpr = Math.min(window.devicePixelRatio || 1, 2) * 0.5;
449
- const w = Math.round(this.#areaEl.clientWidth * dpr);
450
- const h = Math.round(this.#areaEl.clientHeight * dpr);
451
- if (w <= 0 || h <= 0) return;
452
-
453
- this.#canvas.width = w;
454
- this.#canvas.height = h;
455
-
456
- const img = this.#ctx.createImageData(w, h);
457
- const data = img.data;
458
- const hue = this.#H;
459
-
460
- for (let y = 0; y < h; y++) {
461
- const L = 1 - y / (h - 1);
462
- for (let x = 0; x < w; x++) {
463
- const C = (x / (w - 1)) * MAX_CHROMA;
464
- const clampedC = gamutMapChroma(L, C, hue);
465
- const [r, g, b] = oklchToRgb(L, clampedC, hue);
466
- const i = (y * w + x) * 4;
467
- data[i] = Math.round(r * 255);
468
- data[i + 1] = Math.round(g * 255);
469
- data[i + 2] = Math.round(b * 255);
470
- data[i + 3] = 255;
471
- }
472
- }
473
-
474
- this.#ctx.putImageData(img, 0, 0);
475
- }
476
-
477
- // ── Render ──
478
-
479
- render() {
480
- if (this.#internalUpdate) {
481
- this.#internalUpdate = false;
482
- } else {
483
- this.#parseValue(this.value);
484
- }
485
-
486
- this.#drawArea();
487
-
488
- // Area thumb
489
- if (this.#areaThumb) {
490
- const xPct = (this.#C / MAX_CHROMA) * 100;
491
- const yPct = (1 - this.#L) * 100;
492
- this.#areaThumb.style.left = `${xPct}%`;
493
- this.#areaThumb.style.top = `${yPct}%`;
494
- this.#areaThumb.style.background = oklchToHex(this.#L, gamutMapChroma(this.#L, this.#C, this.#H), this.#H);
495
- }
496
-
497
- // Hue track gradient
498
- if (this.#hueTrack) {
499
- const stops = [];
500
- for (let h = 0; h <= 360; h += 30) stops.push(oklchToHex(0.7, 0.15, h));
501
- this.#hueTrack.style.background = `linear-gradient(to right, ${stops.join(', ')})`;
502
- this.#hueTrack.setAttribute('aria-valuenow', String(Math.round(this.#H)));
503
- }
504
-
505
- // Hue thumb
506
- if (this.#hueThumb) {
507
- this.#hueThumb.style.left = `${(this.#H / 360) * 100}%`;
508
- this.#hueThumb.style.background = oklchToHex(0.7, 0.15, this.#H);
509
- }
510
-
511
- // ARIA
512
- if (this.#areaEl) {
513
- this.#areaEl.setAttribute('aria-valuetext', this.#oklchStr());
514
- }
515
-
516
- // Slider values
517
- const hueSlider = this.querySelector('slider-ui[data-channel="hue"]');
518
- const chromaSlider = this.querySelector('slider-ui[data-channel="chroma"]');
519
- const lightnessSlider = this.querySelector('slider-ui[data-channel="lightness"]');
520
- if (hueSlider) hueSlider.value = Math.round(this.#H);
521
- if (chromaSlider) chromaSlider.value = +this.#C.toFixed(3);
522
- if (lightnessSlider) lightnessSlider.value = +this.#L.toFixed(3);
523
-
524
- // Disabled
525
- if (this.disabled) this.setAttribute('aria-disabled', 'true');
526
- else this.removeAttribute('aria-disabled');
527
-
528
- // Output values
529
- const hex = oklchToHex(this.#L, gamutMapChroma(this.#L, this.#C, this.#H), this.#H);
530
- if (this.#oklchVal) this.#oklchVal.textContent = this.#oklchStr();
531
- if (this.#hexVal) this.#hexVal.textContent = hex;
532
- }
533
-
534
- onFormReset() {
535
- this.value = '#3b82f6';
536
- }
537
- }
538
-
539
- customElements.define('color-picker-ui', UIColorPicker);
540
17
  export { UIColorPicker };