@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
@@ -1,277 +1,17 @@
1
1
  /**
2
- * <toggle-scheme-ui>
2
+ * `<toggle-scheme-ui>` — auto-registers the tag on import.
3
3
  *
4
- * Icon-button primitive that toggles `color-scheme` between light and dark
5
- * on a target element (default `:root`). Replaces the hand-wired
6
- * `<button-ui id="theme-toggle"> + applyScheme()` boilerplate previously
7
- * duplicated across apps/genui, apps/construct-canvas, playgrounds/chat.
4
+ * For non-side-effect class import (test isolation, tag override), use
5
+ * the `class` subpath:
8
6
  *
9
- * Composition: light-DOM host, internally stamps a single <button-ui>.
7
+ * import { UIToggleScheme } from '@adia-ai/web-components/components/toggle-scheme/class';
10
8
  *
11
- * Authoring API:
12
- * [scheme="auto"|"light"|"dark"] initial scheme (default "auto")
13
- * [target=":root"] CSS selector for target element
14
- * [persist] write selection to localStorage
15
- * [storage-prefix="adia-theme-"] LS key namespace (matches theme-panel)
16
- * [icon-light="sun"] icon shown when active-scheme is light
17
- * [icon-dark="moon"] icon shown when active-scheme is dark
18
- * [label-light] aria-label / title in light mode
19
- * [label-dark] aria-label / title in dark mode
20
- * [size="md"] forwarded to internal <button-ui>
21
- * [variant="ghost"] forwarded to internal <button-ui>
22
- * [color=""] forwarded to internal <button-ui>
23
- * [disabled] forwarded to internal <button-ui>
24
- *
25
- * Reflected state (read-only externally; use .setScheme() to mutate):
26
- * [active-scheme] resolved scheme ("light" | "dark")
27
- *
28
- * Events:
29
- * scheme-change — bubbles. detail: { scheme, source }
30
- * source ∈ "press" | "media" | "programmatic"
31
- *
32
- * Public methods:
33
- * .toggle() flip between light and dark (defeats auto)
34
- * .setScheme(s) "light" | "dark" | "auto"; programmatic write
9
+ * @see ../../USAGE.md#registration--auto-vs-explicit
35
10
  */
36
11
 
37
- import { UIElement } from '../../core/element.js';
38
- import '../button/button.js';
39
-
40
- const LIGHT = 'light';
41
- const DARK = 'dark';
42
- const AUTO = 'auto';
43
-
44
- class UIToggleScheme extends UIElement {
45
- static properties = {
46
- scheme: { type: String, default: AUTO, reflect: true },
47
- target: { type: String, default: ':root', reflect: true },
48
- persist: { type: Boolean, default: false, reflect: true },
49
- storagePrefix: { type: String, default: 'adia-theme-', reflect: true, attribute: 'storage-prefix' },
50
- iconLight: { type: String, default: 'sun', reflect: true, attribute: 'icon-light' },
51
- iconDark: { type: String, default: 'moon', reflect: true, attribute: 'icon-dark' },
52
- labelLight: { type: String, default: 'Switch to dark mode', reflect: true, attribute: 'label-light' },
53
- labelDark: { type: String, default: 'Switch to light mode', reflect: true, attribute: 'label-dark' },
54
- size: { type: String, default: 'md', reflect: true },
55
- variant: { type: String, default: 'ghost', reflect: true },
56
- color: { type: String, default: '', reflect: true },
57
- disabled: { type: Boolean, default: false, reflect: true },
58
- activeScheme: { type: String, default: '', reflect: true, attribute: 'active-scheme' },
59
- };
60
-
61
- static template = () => null;
62
-
63
- #button = null;
64
- #mql = null;
65
- #mqlHandler = null;
66
- #onPress = null;
67
- #stamped = false;
68
-
69
- connected() {
70
- if (!this.#stamped) {
71
- this.#stamp();
72
- this.#stamped = true;
73
- }
74
- this.#initState();
75
- }
76
-
77
- disconnected() {
78
- if (this.#button && this.#onPress) {
79
- this.#button.removeEventListener('press', this.#onPress);
80
- }
81
- this.#detachPcm();
82
- }
83
-
84
- // ── Public API ──────────────────────────────────────────────────────
85
-
86
- /** Flip between light and dark — defeats auto. */
87
- toggle() {
88
- const next = this.activeScheme === DARK ? LIGHT : DARK;
89
- this.#apply(next, 'programmatic');
90
- }
91
-
92
- /**
93
- * @param {"light"|"dark"|"auto"} s
94
- */
95
- setScheme(s) {
96
- if (s === AUTO) {
97
- this.#clearTargetOverride();
98
- const resolved = this.#resolvePrefersScheme();
99
- this.activeScheme = resolved;
100
- this.#syncButton();
101
- this.#attachPcm();
102
- this.#emit('programmatic');
103
- } else if (s === LIGHT || s === DARK) {
104
- this.#apply(s, 'programmatic');
105
- }
106
- }
107
-
108
- // ── Internal — stamping ─────────────────────────────────────────────
109
-
110
- #stamp() {
111
- const btn = document.createElement('button-ui');
112
- btn.setAttribute('part', 'button');
113
- btn.setAttribute('variant', this.variant || 'ghost');
114
- btn.setAttribute('size', this.size || 'md');
115
- if (this.color) btn.setAttribute('color', this.color);
116
- if (this.disabled) btn.setAttribute('disabled', '');
117
- this.appendChild(btn);
118
-
119
- this.#button = btn;
120
- this.#onPress = (e) => {
121
- // Internal button bubbles 'press'; we re-emit our own 'scheme-change'
122
- // so consumers see one semantic event, not the inner button's.
123
- e.stopPropagation();
124
- if (this.disabled) return;
125
- const next = this.activeScheme === DARK ? LIGHT : DARK;
126
- this.#apply(next, 'press');
127
- };
128
- btn.addEventListener('press', this.#onPress);
129
- }
130
-
131
- // ── Internal — initial state ────────────────────────────────────────
132
-
133
- #initState() {
134
- // Priority: persisted > [scheme] attr > auto
135
- let initial = null;
136
- if (this.persist) {
137
- try {
138
- const v = localStorage.getItem(`${this.storagePrefix}scheme`);
139
- if (v === LIGHT || v === DARK) initial = v;
140
- } catch {}
141
- }
142
- if (initial === null) {
143
- const attr = (this.scheme || AUTO);
144
- if (attr === LIGHT || attr === DARK) initial = attr;
145
- }
146
-
147
- if (initial === LIGHT || initial === DARK) {
148
- // Explicit choice — write to target, no MQL listening
149
- this.#writeTarget(initial);
150
- this.activeScheme = initial;
151
- } else {
152
- // Auto — follow prefers-color-scheme, attach listener
153
- this.activeScheme = this.#resolvePrefersScheme();
154
- this.#attachPcm();
155
- }
156
- this.#syncButton();
157
- }
158
-
159
- // ── Internal — mutation (single path) ───────────────────────────────
160
-
161
- #apply(next, source) {
162
- if (next !== LIGHT && next !== DARK) return;
163
- this.#writeTarget(next);
164
- this.activeScheme = next;
165
- if (this.persist) {
166
- try { localStorage.setItem(`${this.storagePrefix}scheme`, next); } catch {}
167
- }
168
- // User picked explicitly — stop tracking OS preference
169
- this.#detachPcm();
170
- this.#syncButton();
171
- this.#emit(source);
172
- }
173
-
174
- #syncButton() {
175
- if (!this.#button) return;
176
- const dark = this.activeScheme === DARK;
177
- const icon = dark ? this.iconDark : this.iconLight;
178
- const label = dark ? this.labelDark : this.labelLight;
179
- if (this.#button.getAttribute('icon') !== icon) {
180
- this.#button.setAttribute('icon', icon);
181
- }
182
- this.#button.setAttribute('aria-label', label);
183
- this.#button.setAttribute('title', label);
184
- // Forward style passthrough so authors can change variant/size/color
185
- // at runtime and the internal button reflects it.
186
- if (this.#button.getAttribute('variant') !== this.variant) {
187
- this.#button.setAttribute('variant', this.variant || 'ghost');
188
- }
189
- if (this.#button.getAttribute('size') !== this.size) {
190
- this.#button.setAttribute('size', this.size || 'md');
191
- }
192
- if (this.color) {
193
- this.#button.setAttribute('color', this.color);
194
- } else {
195
- this.#button.removeAttribute('color');
196
- }
197
- if (this.disabled) {
198
- this.#button.setAttribute('disabled', '');
199
- } else {
200
- this.#button.removeAttribute('disabled');
201
- }
202
- }
203
-
204
- render() {
205
- // Property writes after connect (e.g. el.size = 'sm') route through here.
206
- this.#syncButton();
207
- }
208
-
209
- #emit(source) {
210
- this.dispatchEvent(new CustomEvent('scheme-change', {
211
- bubbles: true,
212
- detail: { scheme: this.activeScheme, source },
213
- }));
214
- }
215
-
216
- // ── Internal — target resolution + write ────────────────────────────
217
-
218
- #resolveTarget() {
219
- const sel = this.target || ':root';
220
- if (sel === ':root') return document.documentElement;
221
- try {
222
- return document.querySelector(sel) ?? document.documentElement;
223
- } catch {
224
- return document.documentElement;
225
- }
226
- }
227
-
228
- #writeTarget(scheme) {
229
- const t = this.#resolveTarget();
230
- t.style.setProperty('color-scheme', scheme);
231
- }
232
-
233
- #clearTargetOverride() {
234
- const t = this.#resolveTarget();
235
- t.style.removeProperty('color-scheme');
236
- if (this.persist) {
237
- try { localStorage.removeItem(`${this.storagePrefix}scheme`); } catch {}
238
- }
239
- }
240
-
241
- // ── Internal — prefers-color-scheme ─────────────────────────────────
242
-
243
- #resolvePrefersScheme() {
244
- try {
245
- if (typeof matchMedia === 'function' &&
246
- matchMedia('(prefers-color-scheme: dark)').matches) return DARK;
247
- } catch {}
248
- return LIGHT;
249
- }
250
-
251
- #attachPcm() {
252
- if (this.#mql || typeof matchMedia !== 'function') return;
253
- try {
254
- this.#mql = matchMedia('(prefers-color-scheme: dark)');
255
- this.#mqlHandler = () => {
256
- const resolved = this.#mql.matches ? DARK : LIGHT;
257
- if (resolved === this.activeScheme) return;
258
- this.activeScheme = resolved;
259
- this.#syncButton();
260
- this.#emit('media');
261
- };
262
- this.#mql.addEventListener('change', this.#mqlHandler);
263
- } catch {}
264
- }
265
-
266
- #detachPcm() {
267
- if (this.#mql && this.#mqlHandler) {
268
- try { this.#mql.removeEventListener('change', this.#mqlHandler); } catch {}
269
- }
270
- this.#mql = null;
271
- this.#mqlHandler = null;
272
- }
273
- }
12
+ import { defineIfFree } from '../../core/register.js';
13
+ import { UIToggleScheme } from './class.js';
274
14
 
275
- customElements.define('toggle-scheme-ui', UIToggleScheme);
15
+ defineIfFree('toggle-scheme-ui', UIToggleScheme);
276
16
 
277
17
  export { UIToggleScheme };
@@ -0,0 +1,388 @@
1
+ /**
2
+ * Non-side-effect class export for `<toolbar-ui>` (+ co-located primitives).
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/toolbar`
9
+ * (which imports this file + calls `defineIfFree()`).
10
+ *
11
+ * @see ../../USAGE.md#registration--auto-vs-explicit
12
+ */
13
+
14
+ /**
15
+ * <toolbar-ui>
16
+ * <toolbar-group-ui>
17
+ * <button-ui icon="text-bold" variant="ghost" size="sm"></button-ui>
18
+ * <button-ui icon="text-italic" variant="ghost" size="sm"></button-ui>
19
+ * </toolbar-group-ui>
20
+ * <divider-ui vertical></divider-ui>
21
+ * <toolbar-group-ui>
22
+ * <button-ui icon="text-align-left" variant="ghost" size="sm"></button-ui>
23
+ * </toolbar-group-ui>
24
+ * </toolbar-ui>
25
+ *
26
+ * Horizontal action bar with automatic overflow:
27
+ * • ResizeObserver measures available width.
28
+ * • Items that don't fit are moved (from the end) into a spillover popover.
29
+ * • `toolbar-group-ui` wraps siblings so they move as an atomic unit
30
+ * (no half-group split between visible bar and overflow menu).
31
+ * • Trailing dividers are trimmed. Dividers are not moved to the menu.
32
+ *
33
+ * Platform: Popover API (top-layer) + CSS Anchor Positioning via @core/anchor.js.
34
+ */
35
+
36
+ import { UIElement } from '../../core/element.js';
37
+ import { anchorPopover } from '../../core/anchor.js';
38
+
39
+ const SPILLOVER_ATTR = 'data-toolbar-spillover';
40
+ const SPILLOVER_MENU_ATTR = 'data-toolbar-spillover-menu';
41
+
42
+ /* Headroom kept free at the right edge before an item starts spilling over.
43
+ A non-zero value makes overflow more responsive — items move to the menu
44
+ while there's still breathing room, instead of waiting until they actually
45
+ touch the toolbar's right edge. */
46
+ const OVERFLOW_BUFFER = 8;
47
+
48
+ const SPILLOVER_LABEL_FLAG = 'data-toolbar-spillover-label';
49
+
50
+ function isDivider(el) {
51
+ return el?.tagName && el.tagName.toLowerCase() === 'divider-ui';
52
+ }
53
+
54
+ function humanizeIcon(name) {
55
+ if (!name) return '';
56
+ return name
57
+ .split('-')
58
+ .filter(Boolean)
59
+ .map(w => w[0].toUpperCase() + w.slice(1))
60
+ .join(' ');
61
+ }
62
+
63
+ function applySpilloverLabels(root) {
64
+ const buttons = root.matches?.('button-ui') ? [root] : Array.from(root.querySelectorAll?.('button-ui') || []);
65
+ for (const btn of buttons) {
66
+ if (btn.hasAttribute(SPILLOVER_LABEL_FLAG)) continue;
67
+ if (btn.getAttribute('text')) continue;
68
+ const label = btn.getAttribute('aria-label') || humanizeIcon(btn.getAttribute('icon'));
69
+ if (!label) continue;
70
+ btn.setAttribute(SPILLOVER_LABEL_FLAG, '');
71
+ btn.setAttribute('text', label);
72
+ }
73
+ }
74
+
75
+ function stripSpilloverLabels(root) {
76
+ const buttons = root.matches?.('button-ui') ? [root] : Array.from(root.querySelectorAll?.('button-ui') || []);
77
+ for (const btn of buttons) {
78
+ if (!btn.hasAttribute(SPILLOVER_LABEL_FLAG)) continue;
79
+ btn.removeAttribute(SPILLOVER_LABEL_FLAG);
80
+ btn.removeAttribute('text');
81
+ }
82
+ }
83
+
84
+ function outerWidth(el) {
85
+ const r = el.getBoundingClientRect();
86
+ const cs = getComputedStyle(el);
87
+ const ml = parseFloat(cs.marginLeft) || 0;
88
+ const mr = parseFloat(cs.marginRight) || 0;
89
+ return r.width + ml + mr;
90
+ }
91
+
92
+ export class UIToolbar extends UIElement {
93
+ static properties = {
94
+ gap: { type: String, default: 'sm', reflect: true },
95
+ align: { type: String, default: 'start', reflect: true },
96
+ overflow: { type: String, default: 'menu', reflect: true },
97
+ };
98
+
99
+ static template = () => null;
100
+
101
+ #ro = null;
102
+ #mo = null;
103
+ #trigger = null;
104
+ #popover = null;
105
+ #spilloverCleanup = null;
106
+ #measuring = false;
107
+ #reflowQueued = false;
108
+ #reflowRaf = null;
109
+ #rafId = null;
110
+
111
+ connected() {
112
+ this.setAttribute('role', 'toolbar');
113
+ this.#ensureSpillover();
114
+ this.#ro = new ResizeObserver(() => this.#queueReflow());
115
+ this.#ro.observe(this);
116
+ this.#mo = new MutationObserver(() => {
117
+ if (this.#measuring) return;
118
+ this.#queueReflow();
119
+ });
120
+ this.#moObserve();
121
+ this.#queueReflow();
122
+ }
123
+
124
+ disconnected() {
125
+ if (this.#reflowRaf != null) {
126
+ cancelAnimationFrame(this.#reflowRaf);
127
+ this.#reflowRaf = null;
128
+ }
129
+ this.#reflowQueued = false;
130
+ this.#ro?.disconnect();
131
+ this.#mo?.disconnect();
132
+ this.#closeSpillover();
133
+ this.#ro = null;
134
+ this.#mo = null;
135
+ this.#trigger = null;
136
+ this.#popover = null;
137
+ }
138
+
139
+ // ── Spillover infrastructure ──
140
+
141
+ #ensureSpillover() {
142
+ if (this.#trigger) return;
143
+
144
+ const trigger = document.createElement('button-ui');
145
+ trigger.setAttribute('icon', 'dots-three');
146
+ trigger.setAttribute('variant', 'ghost');
147
+ trigger.setAttribute('size', 'sm');
148
+ trigger.setAttribute(SPILLOVER_ATTR, '');
149
+ trigger.setAttribute('aria-label', 'More actions');
150
+ trigger.setAttribute('aria-haspopup', 'menu');
151
+ trigger.hidden = true;
152
+
153
+ const popover = document.createElement('div');
154
+ popover.setAttribute(SPILLOVER_MENU_ATTR, '');
155
+ popover.setAttribute('popover', 'manual');
156
+ popover.setAttribute('role', 'menu');
157
+
158
+ trigger.addEventListener('click', this.#onTriggerClick);
159
+ popover.addEventListener('click', this.#onMenuClick);
160
+ popover.addEventListener('toggle', this.#onToggle);
161
+
162
+ this.appendChild(trigger);
163
+ this.appendChild(popover);
164
+
165
+ this.#trigger = trigger;
166
+ this.#popover = popover;
167
+ }
168
+
169
+ #openSpillover() {
170
+ const menu = this.#popover;
171
+ if (!menu || menu.matches(':popover-open')) return;
172
+ menu.showPopover?.();
173
+ this.#spilloverCleanup?.();
174
+ this.#spilloverCleanup = anchorPopover(this.#trigger, menu, {
175
+ placement: 'bottom-end',
176
+ gap: 4,
177
+ });
178
+ this.#rafId = requestAnimationFrame(() => {
179
+ this.#rafId = null;
180
+ document.addEventListener('pointerdown', this.#onOutside, true);
181
+ document.addEventListener('keydown', this.#onKey, true);
182
+ });
183
+ }
184
+
185
+ #closeSpillover() {
186
+ this.#spilloverCleanup?.();
187
+ this.#spilloverCleanup = null;
188
+ if (this.#popover?.matches(':popover-open')) this.#popover.hidePopover?.();
189
+ if (this.#rafId != null) {
190
+ cancelAnimationFrame(this.#rafId);
191
+ this.#rafId = null;
192
+ }
193
+ document.removeEventListener('pointerdown', this.#onOutside, true);
194
+ document.removeEventListener('keydown', this.#onKey, true);
195
+ }
196
+
197
+ #onTriggerClick = (e) => {
198
+ e.stopPropagation();
199
+ if (this.#popover?.matches(':popover-open')) this.#closeSpillover();
200
+ else this.#openSpillover();
201
+ };
202
+
203
+ #onMenuClick = (e) => {
204
+ // Only auto-close after a "terminal" action (button-ui press). Composite
205
+ // controls inside the menu — select-ui, segmented-ui, etc. — manage their
206
+ // own state; clicking them must not dismiss the spillover.
207
+ const btn = e.target.closest('button-ui');
208
+ if (!btn) return;
209
+ if (btn.matches(`[${SPILLOVER_ATTR}]`)) return;
210
+ queueMicrotask(() => this.#closeSpillover());
211
+ };
212
+
213
+ #onOutside = (e) => {
214
+ if (this.#trigger?.contains(e.target)) return;
215
+ if (this.#popover?.contains(e.target)) return;
216
+ // Ignore clicks inside any other open popover (e.g., a select-ui dropdown
217
+ // launched from a select inside our spillover, which lives in the top
218
+ // layer outside the spillover popover's DOM subtree).
219
+ const path = e.composedPath?.() || [];
220
+ for (const node of path) {
221
+ if (node === document) break;
222
+ if (node?.nodeType === 1 && node.hasAttribute?.('popover')) return;
223
+ }
224
+ this.#closeSpillover();
225
+ };
226
+
227
+ #onKey = (e) => {
228
+ if (e.key === 'Escape') {
229
+ e.stopPropagation();
230
+ this.#closeSpillover();
231
+ this.#trigger?.focus?.({ preventScroll: true });
232
+ }
233
+ };
234
+
235
+ #onToggle = (e) => {
236
+ if (e.newState === 'closed') {
237
+ // Keep listeners cleaned up if closed externally.
238
+ this.#spilloverCleanup?.();
239
+ this.#spilloverCleanup = null;
240
+ if (this.#rafId != null) {
241
+ cancelAnimationFrame(this.#rafId);
242
+ this.#rafId = null;
243
+ }
244
+ document.removeEventListener('pointerdown', this.#onOutside, true);
245
+ document.removeEventListener('keydown', this.#onKey, true);
246
+ }
247
+ };
248
+
249
+ // ── Reflow / overflow ──
250
+
251
+ #queueReflow() {
252
+ if (this.#reflowQueued) return;
253
+ this.#reflowQueued = true;
254
+ this.#reflowRaf = requestAnimationFrame(() => {
255
+ this.#reflowRaf = null;
256
+ this.#reflowQueued = false;
257
+ if (!this.isConnected) return;
258
+ this.#reflow();
259
+ });
260
+ }
261
+
262
+ #moObserve() {
263
+ this.#mo?.observe(this, { childList: true });
264
+ }
265
+
266
+ #moPause() {
267
+ // MutationObserver delivers records asynchronously, so the #measuring
268
+ // flag alone doesn't stop self-triggered reflows — by the time the
269
+ // observer's microtask fires, the flag has already been cleared. We must
270
+ // physically disconnect during reflow, flush any pending records the
271
+ // observer had already queued, then reconnect on the next microtask.
272
+ if (!this.#mo) return;
273
+ this.#mo.disconnect();
274
+ this.#mo.takeRecords();
275
+ }
276
+
277
+ #reflow() {
278
+ if (!this.isConnected) return;
279
+ const trigger = this.#trigger;
280
+ const popover = this.#popover;
281
+ if (!trigger || !popover) return;
282
+
283
+ this.#measuring = true;
284
+ this.#moPause();
285
+ try {
286
+ // 1. Move everything from the popover back into the toolbar (before the trigger).
287
+ // Strip injected spillover labels so the buttons measure as icon-only again.
288
+ while (popover.firstChild) {
289
+ stripSpilloverLabels(popover.firstChild);
290
+ this.insertBefore(popover.firstChild, trigger);
291
+ }
292
+
293
+ // Stamp the original DOM order on each item the first time we see it.
294
+ // (Items moved to the popover and back must restore their original slot —
295
+ // otherwise dividers end up clumped next to dividers and groups split.)
296
+ let idx = 0;
297
+ for (const el of this.children) {
298
+ if (el === trigger || el === popover) continue;
299
+ if (el.dataset.toolbarOrder == null) el.dataset.toolbarOrder = String(idx);
300
+ idx += 1;
301
+ }
302
+
303
+ // Sort items back into their original order before measurement.
304
+ const ordered = Array.from(this.children)
305
+ .filter(c => c !== trigger && c !== popover)
306
+ .sort((a, b) => Number(a.dataset.toolbarOrder) - Number(b.dataset.toolbarOrder));
307
+ for (const el of ordered) this.insertBefore(el, trigger);
308
+
309
+ const all = ordered;
310
+ for (const el of all) el.hidden = false;
311
+ trigger.hidden = true;
312
+
313
+ // 2. Measure.
314
+ const style = getComputedStyle(this);
315
+ const padL = parseFloat(style.paddingLeft) || 0;
316
+ const padR = parseFloat(style.paddingRight) || 0;
317
+ const gap = parseFloat(style.columnGap || style.gap) || 0;
318
+ const available = this.clientWidth - padL - padR;
319
+
320
+ // Include horizontal margins — components like divider-ui add their own
321
+ // margin-inline which getBoundingClientRect() does NOT include but flex
322
+ // layout DOES count toward the row width.
323
+ const widths = all.map(el => outerWidth(el));
324
+ const total = widths.reduce((a, w) => a + w, 0) + Math.max(0, all.length - 1) * gap;
325
+
326
+ // Reserve OVERFLOW_BUFFER worth of headroom — start spilling sooner
327
+ // rather than waiting until items literally touch the right edge.
328
+ const fitsCleanly = total <= available - OVERFLOW_BUFFER;
329
+
330
+ if (this.overflow === 'none' || fitsCleanly) {
331
+ return;
332
+ }
333
+
334
+ // 3. Need to move some items out. Reserve trigger width + buffer.
335
+ trigger.hidden = false;
336
+ const triggerW = outerWidth(trigger);
337
+
338
+ let fitIdx = all.length;
339
+ let running = total;
340
+ // Drop items from the end (and subtract their width + preceding gap)
341
+ // until visible items + trigger fit with buffer headroom.
342
+ while (fitIdx > 0) {
343
+ const wWithTrigger = running + gap + triggerW;
344
+ if (wWithTrigger <= available - OVERFLOW_BUFFER) break;
345
+ fitIdx -= 1;
346
+ running -= widths[fitIdx];
347
+ if (fitIdx > 0) running -= gap;
348
+ }
349
+
350
+ // 4. Trim trailing dividers from visible section.
351
+ while (fitIdx > 0 && isDivider(all[fitIdx - 1])) fitIdx -= 1;
352
+
353
+ // 5. Move overflowed items into popover (skip dividers entirely).
354
+ const toMove = all.slice(fitIdx);
355
+ for (const el of toMove) {
356
+ if (isDivider(el)) {
357
+ el.hidden = true;
358
+ continue;
359
+ }
360
+ // Inject derived labels on icon-only buttons so the spillover row
361
+ // shows "Bold" instead of just an unlabeled icon.
362
+ applySpilloverLabels(el);
363
+ popover.appendChild(el);
364
+ }
365
+
366
+ // 6. If nothing actually ended up in the popover (e.g. only a divider overflowed),
367
+ // hide the trigger again.
368
+ if (!popover.firstElementChild) {
369
+ trigger.hidden = true;
370
+ }
371
+ } finally {
372
+ this.#measuring = false;
373
+ // Drop any mutation records our reflow produced before reconnecting,
374
+ // otherwise they'd fire as soon as we observe() again and loop forever.
375
+ queueMicrotask(() => {
376
+ this.#mo?.takeRecords();
377
+ this.#moObserve();
378
+ });
379
+ }
380
+ }
381
+ }
382
+
383
+ export class UIToolbarGroup extends UIElement {
384
+ static template = () => null;
385
+ connected() {
386
+ this.setAttribute('role', 'group');
387
+ }
388
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * `<toolbar-ui>` — Horizontal action bar with automatic overflow. Items that don't fit move to a spillover popover menu.
3
+ *
4
+ * @see https://ui-kit.exe.xyz/site/components/toolbar
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 UIToolbar extends UIElement {
15
+ /** Item alignment */
16
+ align: string;
17
+ /** Adds a border around the toolbar for visual separation from surrounding content. */
18
+ bordered: boolean;
19
+ /** Gap between items */
20
+ gap: string;
21
+ /** Overflow behavior */
22
+ overflow: 'menu' | 'none';
23
+ }