@adia-ai/web-components 0.6.33 → 0.6.35

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 (391) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/color/index.js +1 -1
  3. package/components/accordion/accordion-item.yaml +2 -2
  4. package/components/accordion/accordion.css +2 -2
  5. package/components/accordion/accordion.js +1 -1
  6. package/components/action-list/action-item.yaml +2 -2
  7. package/components/action-list/action-list.css +2 -2
  8. package/components/action-list/action-list.js +1 -1
  9. package/components/agent-artifact/{class.js → agent-artifact.class.js} +1 -1
  10. package/components/agent-artifact/agent-artifact.css +31 -31
  11. package/components/agent-artifact/agent-artifact.js +1 -1
  12. package/components/agent-feedback-bar/agent-feedback-bar.css +10 -10
  13. package/components/agent-feedback-bar/agent-feedback-bar.js +1 -1
  14. package/components/agent-questions/agent-questions.css +57 -57
  15. package/components/agent-questions/agent-questions.js +1 -1
  16. package/components/agent-reasoning/agent-reasoning.css +62 -62
  17. package/components/agent-reasoning/agent-reasoning.js +1 -1
  18. package/components/agent-suggestions/agent-suggestions.css +4 -4
  19. package/components/agent-suggestions/agent-suggestions.js +1 -1
  20. package/components/agent-trace/agent-trace.css +53 -53
  21. package/components/alert/alert.a2ui.json +64 -1
  22. package/components/alert/{class.js → alert.class.js} +189 -2
  23. package/components/alert/alert.css +119 -41
  24. package/components/alert/alert.d.ts +14 -0
  25. package/components/alert/alert.js +1 -1
  26. package/components/alert/alert.test.js +184 -0
  27. package/components/alert/alert.yaml +114 -1
  28. package/components/avatar/avatar-group.yaml +2 -2
  29. package/components/avatar/avatar.css +27 -27
  30. package/components/avatar/avatar.js +1 -1
  31. package/components/badge/badge.css +27 -27
  32. package/components/badge/badge.js +1 -1
  33. package/components/block/block.css +16 -16
  34. package/components/block/block.js +1 -1
  35. package/components/breadcrumb/breadcrumb.css +23 -23
  36. package/components/breadcrumb/breadcrumb.js +1 -1
  37. package/components/button/button.css +101 -91
  38. package/components/button/button.js +1 -1
  39. package/components/calendar-grid/calendar-grid.a2ui.json +146 -0
  40. package/components/calendar-grid/calendar-grid.class.js +326 -0
  41. package/components/calendar-grid/calendar-grid.css +246 -0
  42. package/components/calendar-grid/calendar-grid.d.ts +41 -0
  43. package/components/calendar-grid/calendar-grid.js +17 -0
  44. package/components/calendar-grid/calendar-grid.yaml +136 -0
  45. package/components/calendar-picker/calendar-picker.css +139 -139
  46. package/components/calendar-picker/calendar-picker.js +1 -1
  47. package/components/canvas/canvas.css +12 -12
  48. package/components/card/card.css +83 -83
  49. package/components/card/card.js +1 -1
  50. package/components/chart/chart.css +224 -224
  51. package/components/chart/chart.js +1 -1
  52. package/components/chart-legend/chart-legend.css +26 -26
  53. package/components/chart-legend/chart-legend.js +1 -1
  54. package/components/chat-thread/chat-input.a2ui.json +1 -1
  55. package/components/chat-thread/chat-input.js +6 -1
  56. package/components/chat-thread/chat-input.yaml +4 -1
  57. package/components/chat-thread/chat-thread.js +1 -1
  58. package/components/check/check.css +40 -40
  59. package/components/check/check.js +1 -1
  60. package/components/code/code.css +125 -125
  61. package/components/code/code.js +1 -1
  62. package/components/col/col.css +15 -15
  63. package/components/col/col.js +1 -1
  64. package/components/color-input/color-input.js +1 -1
  65. package/components/color-picker/color-picker.css +55 -55
  66. package/components/color-picker/color-picker.js +1 -1
  67. package/components/combobox/combobox.a2ui.json +363 -0
  68. package/components/combobox/combobox.class.js +861 -0
  69. package/components/combobox/combobox.css +244 -0
  70. package/components/combobox/combobox.d.ts +113 -0
  71. package/components/combobox/combobox.examples.md +59 -0
  72. package/components/combobox/combobox.js +17 -0
  73. package/components/combobox/combobox.test.js +181 -0
  74. package/components/combobox/combobox.yaml +369 -0
  75. package/components/command/command.css +90 -90
  76. package/components/command/command.js +1 -1
  77. package/components/date-range-picker/date-range-picker.a2ui.json +300 -0
  78. package/components/date-range-picker/date-range-picker.class.js +791 -0
  79. package/components/date-range-picker/date-range-picker.css +224 -0
  80. package/components/date-range-picker/date-range-picker.d.ts +82 -0
  81. package/components/date-range-picker/date-range-picker.examples.md +37 -0
  82. package/components/date-range-picker/date-range-picker.js +17 -0
  83. package/components/date-range-picker/date-range-picker.test.js +387 -0
  84. package/components/date-range-picker/date-range-picker.yaml +285 -0
  85. package/components/datetime-picker/datetime-picker.a2ui.json +334 -0
  86. package/components/datetime-picker/datetime-picker.class.js +706 -0
  87. package/components/datetime-picker/datetime-picker.css +150 -0
  88. package/components/datetime-picker/datetime-picker.d.ts +86 -0
  89. package/components/datetime-picker/datetime-picker.examples.md +46 -0
  90. package/components/datetime-picker/datetime-picker.js +17 -0
  91. package/components/datetime-picker/datetime-picker.test.js +454 -0
  92. package/components/datetime-picker/datetime-picker.yaml +332 -0
  93. package/components/demo-toggle/demo-toggle.css +27 -27
  94. package/components/demo-toggle/demo-toggle.js +1 -1
  95. package/components/description-list/description-list.css +18 -18
  96. package/components/description-list/description-list.js +1 -1
  97. package/components/divider/divider.css +24 -24
  98. package/components/divider/divider.js +1 -1
  99. package/components/drawer/drawer.js +1 -1
  100. package/components/embed/embed.css +6 -6
  101. package/components/embed/embed.js +1 -1
  102. package/components/empty-state/empty-state.css +27 -27
  103. package/components/empty-state/empty-state.js +1 -1
  104. package/components/feed/feed.css +12 -12
  105. package/components/feed/feed.js +1 -1
  106. package/components/field/field.css +28 -28
  107. package/components/field/field.js +1 -1
  108. package/components/field/field.test.js +1 -1
  109. package/components/fields/fields.css +5 -5
  110. package/components/fields/fields.js +1 -1
  111. package/components/grid/grid.css +5 -5
  112. package/components/grid/grid.js +1 -1
  113. package/components/heatmap/heatmap.css +63 -63
  114. package/components/heatmap/heatmap.js +1 -1
  115. package/components/icon/icon.css +12 -12
  116. package/components/icon/icon.js +1 -1
  117. package/components/image/image.css +14 -14
  118. package/components/image/image.js +1 -1
  119. package/components/index.js +11 -0
  120. package/components/inline-message/inline-message.a2ui.json +143 -0
  121. package/components/inline-message/inline-message.class.js +169 -0
  122. package/components/inline-message/inline-message.css +75 -0
  123. package/components/inline-message/inline-message.d.ts +31 -0
  124. package/components/inline-message/inline-message.examples.md +19 -0
  125. package/components/inline-message/inline-message.js +17 -0
  126. package/components/inline-message/inline-message.test.js +203 -0
  127. package/components/inline-message/inline-message.yaml +205 -0
  128. package/components/input/input.css +67 -67
  129. package/components/input/input.js +1 -1
  130. package/components/input/input.yaml +5 -4
  131. package/components/inspector/inspector.css +6 -6
  132. package/components/inspector/inspector.js +1 -1
  133. package/components/integration-card/integration-card.a2ui.json +268 -0
  134. package/components/integration-card/integration-card.class.js +410 -0
  135. package/components/integration-card/integration-card.css +169 -0
  136. package/components/integration-card/integration-card.d.ts +63 -0
  137. package/components/integration-card/integration-card.examples.md +41 -0
  138. package/components/integration-card/integration-card.js +17 -0
  139. package/components/integration-card/integration-card.test.js +306 -0
  140. package/components/integration-card/integration-card.yaml +280 -0
  141. package/components/kbd/kbd.css +32 -32
  142. package/components/kbd/kbd.js +1 -1
  143. package/components/link/link.css +12 -12
  144. package/components/link/link.js +1 -1
  145. package/components/list/list-item.yaml +2 -2
  146. package/components/list/list.css +8 -8
  147. package/components/list/list.js +1 -1
  148. package/components/list-window/list-window.a2ui.json +277 -0
  149. package/components/list-window/list-window.class.js +688 -0
  150. package/components/list-window/list-window.css +124 -0
  151. package/components/list-window/list-window.d.ts +84 -0
  152. package/components/list-window/list-window.examples.md +73 -0
  153. package/components/list-window/list-window.js +17 -0
  154. package/components/list-window/list-window.test.js +303 -0
  155. package/components/list-window/list-window.yaml +270 -0
  156. package/components/loading-overlay/loading-overlay.a2ui.json +176 -0
  157. package/components/loading-overlay/loading-overlay.class.js +203 -0
  158. package/components/loading-overlay/loading-overlay.css +81 -0
  159. package/components/loading-overlay/loading-overlay.d.ts +24 -0
  160. package/components/loading-overlay/loading-overlay.examples.md +50 -0
  161. package/components/loading-overlay/loading-overlay.js +17 -0
  162. package/components/loading-overlay/loading-overlay.test.js +257 -0
  163. package/components/loading-overlay/loading-overlay.yaml +260 -0
  164. package/components/menu/menu-divider.yaml +1 -1
  165. package/components/menu/menu-item.yaml +1 -1
  166. package/components/menu/menu.a2ui.json +3 -0
  167. package/components/menu/menu.css +8 -8
  168. package/components/menu/menu.js +1 -1
  169. package/components/menu/menu.yaml +7 -0
  170. package/components/modal/{class.js → modal.class.js} +12 -1
  171. package/components/modal/modal.css +54 -44
  172. package/components/modal/modal.js +1 -1
  173. package/components/nav/nav.css +40 -40
  174. package/components/nav/nav.js +1 -1
  175. package/components/nav-group/nav-group.css +52 -52
  176. package/components/nav-group/nav-group.js +1 -1
  177. package/components/nav-item/nav-item.css +44 -44
  178. package/components/nav-item/nav-item.js +1 -1
  179. package/components/noodles/noodles.css +31 -31
  180. package/components/noodles/noodles.js +1 -1
  181. package/components/option-card/option-card.css +69 -69
  182. package/components/option-card/option-card.js +1 -1
  183. package/components/otp-input/otp-input.css +30 -30
  184. package/components/otp-input/otp-input.js +1 -1
  185. package/components/page/page.css +18 -18
  186. package/components/page/page.js +1 -1
  187. package/components/pagination/pagination.css +61 -61
  188. package/components/pagination/pagination.js +1 -1
  189. package/components/pane/pane.css +57 -57
  190. package/components/pane/pane.js +1 -1
  191. package/components/pipeline-status/pipeline-status.css +65 -65
  192. package/components/pipeline-status/pipeline-status.js +1 -1
  193. package/components/popover/popover.a2ui.json +8 -1
  194. package/components/popover/popover.css +17 -17
  195. package/components/popover/popover.js +1 -1
  196. package/components/popover/popover.yaml +14 -1
  197. package/components/progress/progress.css +23 -23
  198. package/components/progress/progress.js +1 -1
  199. package/components/progress-row/progress-row.css +17 -17
  200. package/components/progress-row/progress-row.js +1 -1
  201. package/components/radio/radio.css +39 -39
  202. package/components/radio/radio.js +1 -1
  203. package/components/range/range.css +55 -55
  204. package/components/range/range.js +1 -1
  205. package/components/rating/rating.css +28 -28
  206. package/components/rating/rating.js +1 -1
  207. package/components/richtext/richtext.css +133 -133
  208. package/components/richtext/richtext.js +1 -1
  209. package/components/row/row.css +19 -19
  210. package/components/row/row.js +1 -1
  211. package/components/search/search.css +5 -5
  212. package/components/search/search.js +1 -1
  213. package/components/segment/segment.css +24 -24
  214. package/components/segment/segment.js +1 -1
  215. package/components/segmented/segmented.css +25 -25
  216. package/components/segmented/segmented.js +1 -1
  217. package/components/select/select.a2ui.json +58 -4
  218. package/components/select/{class.js → select.class.js} +415 -6
  219. package/components/select/select.css +242 -84
  220. package/components/select/select.d.ts +31 -1
  221. package/components/select/select.js +1 -1
  222. package/components/select/select.test.js +202 -0
  223. package/components/select/select.yaml +126 -5
  224. package/components/skeleton/skeleton.css +14 -14
  225. package/components/skeleton/skeleton.js +1 -1
  226. package/components/slider/slider.css +46 -46
  227. package/components/slider/slider.js +1 -1
  228. package/components/spinner/spinner.a2ui.json +198 -0
  229. package/components/spinner/spinner.class.js +99 -0
  230. package/components/spinner/spinner.css +221 -0
  231. package/components/spinner/spinner.d.ts +26 -0
  232. package/components/spinner/spinner.examples.md +26 -0
  233. package/components/spinner/spinner.js +17 -0
  234. package/components/spinner/spinner.test.js +272 -0
  235. package/components/spinner/spinner.yaml +238 -0
  236. package/components/stack/stack.css +11 -11
  237. package/components/stack/stack.js +1 -1
  238. package/components/stat/stat.css +25 -25
  239. package/components/step-progress/step-progress.css +20 -20
  240. package/components/step-progress/step-progress.js +1 -1
  241. package/components/stepper/stepper-item.yaml +1 -1
  242. package/components/stepper/stepper.css +29 -29
  243. package/components/stepper/stepper.js +1 -1
  244. package/components/stream/stream.css +12 -12
  245. package/components/stream/stream.js +1 -1
  246. package/components/swatch/swatch.css +68 -68
  247. package/components/swatch/swatch.js +1 -1
  248. package/components/swiper/swiper.css +57 -57
  249. package/components/swiper/swiper.js +1 -1
  250. package/components/switch/switch.css +52 -52
  251. package/components/switch/switch.js +1 -1
  252. package/components/table/table.css +163 -163
  253. package/components/table/table.js +1 -1
  254. package/components/table-toolbar/{class.js → table-toolbar.class.js} +1 -1
  255. package/components/table-toolbar/table-toolbar.css +32 -32
  256. package/components/table-toolbar/table-toolbar.js +1 -1
  257. package/components/tabs/tab.yaml +2 -2
  258. package/components/tabs/tabs.css +51 -51
  259. package/components/tabs/tabs.js +1 -1
  260. package/components/tag/tag.css +48 -48
  261. package/components/tag/tag.js +1 -1
  262. package/components/tags-input/tags-input.a2ui.json +337 -0
  263. package/components/tags-input/tags-input.class.js +776 -0
  264. package/components/tags-input/tags-input.css +201 -0
  265. package/components/tags-input/tags-input.d.ts +120 -0
  266. package/components/tags-input/tags-input.examples.md +92 -0
  267. package/components/tags-input/tags-input.js +17 -0
  268. package/components/tags-input/tags-input.test.js +368 -0
  269. package/components/tags-input/tags-input.yaml +367 -0
  270. package/components/text/text.css +44 -44
  271. package/components/text/text.js +1 -1
  272. package/components/textarea/textarea.a2ui.json +1 -1
  273. package/components/textarea/textarea.css +46 -46
  274. package/components/textarea/textarea.js +1 -1
  275. package/components/textarea/textarea.yaml +11 -8
  276. package/components/time-picker/time-picker.a2ui.json +267 -0
  277. package/components/time-picker/time-picker.class.js +693 -0
  278. package/components/time-picker/time-picker.css +122 -0
  279. package/components/time-picker/time-picker.d.ts +75 -0
  280. package/components/time-picker/time-picker.examples.md +35 -0
  281. package/components/time-picker/time-picker.js +17 -0
  282. package/components/time-picker/time-picker.test.js +287 -0
  283. package/components/time-picker/time-picker.yaml +256 -0
  284. package/components/timeline/timeline-item.yaml +2 -2
  285. package/components/timeline/{class.js → timeline.class.js} +1 -1
  286. package/components/timeline/timeline.css +50 -50
  287. package/components/timeline/timeline.js +1 -1
  288. package/components/toast/toast.css +58 -58
  289. package/components/toast/toast.js +1 -1
  290. package/components/toggle-group/toggle-group.css +6 -6
  291. package/components/toggle-group/toggle-group.js +1 -1
  292. package/components/toggle-group/toggle-option.yaml +1 -1
  293. package/components/toggle-scheme/toggle-scheme.css +2 -2
  294. package/components/toggle-scheme/toggle-scheme.js +1 -1
  295. package/components/toolbar/toolbar-group.yaml +1 -1
  296. package/components/toolbar/toolbar.css +17 -17
  297. package/components/toolbar/toolbar.js +1 -1
  298. package/components/tooltip/tooltip.css +2 -2
  299. package/components/tooltip/tooltip.js +1 -1
  300. package/components/tree/tree-item.yaml +1 -1
  301. package/components/tree/tree.css +37 -37
  302. package/components/tree/tree.js +1 -1
  303. package/components/upload/upload.css +49 -49
  304. package/components/upload/upload.js +1 -1
  305. package/dist/web-components.min.css +1 -1
  306. package/dist/web-components.min.js +146 -87
  307. package/package.json +3 -3
  308. package/styles/components.css +11 -0
  309. /package/components/accordion/{class.js → accordion.class.js} +0 -0
  310. /package/components/action-list/{class.js → action-list.class.js} +0 -0
  311. /package/components/agent-feedback-bar/{class.js → agent-feedback-bar.class.js} +0 -0
  312. /package/components/agent-questions/{class.js → agent-questions.class.js} +0 -0
  313. /package/components/agent-reasoning/{class.js → agent-reasoning.class.js} +0 -0
  314. /package/components/agent-suggestions/{class.js → agent-suggestions.class.js} +0 -0
  315. /package/components/avatar/{class.js → avatar.class.js} +0 -0
  316. /package/components/badge/{class.js → badge.class.js} +0 -0
  317. /package/components/block/{class.js → block.class.js} +0 -0
  318. /package/components/breadcrumb/{class.js → breadcrumb.class.js} +0 -0
  319. /package/components/button/{class.js → button.class.js} +0 -0
  320. /package/components/calendar-picker/{class.js → calendar-picker.class.js} +0 -0
  321. /package/components/card/{class.js → card.class.js} +0 -0
  322. /package/components/chart/{class.js → chart.class.js} +0 -0
  323. /package/components/chart-legend/{class.js → chart-legend.class.js} +0 -0
  324. /package/components/chat-thread/{class.js → chat-thread.class.js} +0 -0
  325. /package/components/check/{class.js → check.class.js} +0 -0
  326. /package/components/code/{class.js → code.class.js} +0 -0
  327. /package/components/col/{class.js → col.class.js} +0 -0
  328. /package/components/color-input/{class.js → color-input.class.js} +0 -0
  329. /package/components/color-picker/{class.js → color-picker.class.js} +0 -0
  330. /package/components/command/{class.js → command.class.js} +0 -0
  331. /package/components/demo-toggle/{class.js → demo-toggle.class.js} +0 -0
  332. /package/components/description-list/{class.js → description-list.class.js} +0 -0
  333. /package/components/divider/{class.js → divider.class.js} +0 -0
  334. /package/components/drawer/{class.js → drawer.class.js} +0 -0
  335. /package/components/embed/{class.js → embed.class.js} +0 -0
  336. /package/components/empty-state/{class.js → empty-state.class.js} +0 -0
  337. /package/components/feed/{class.js → feed.class.js} +0 -0
  338. /package/components/field/{class.js → field.class.js} +0 -0
  339. /package/components/fields/{class.js → fields.class.js} +0 -0
  340. /package/components/grid/{class.js → grid.class.js} +0 -0
  341. /package/components/heatmap/{class.js → heatmap.class.js} +0 -0
  342. /package/components/icon/{class.js → icon.class.js} +0 -0
  343. /package/components/image/{class.js → image.class.js} +0 -0
  344. /package/components/input/{class.js → input.class.js} +0 -0
  345. /package/components/inspector/{class.js → inspector.class.js} +0 -0
  346. /package/components/kbd/{class.js → kbd.class.js} +0 -0
  347. /package/components/link/{class.js → link.class.js} +0 -0
  348. /package/components/list/{class.js → list.class.js} +0 -0
  349. /package/components/menu/{class.js → menu.class.js} +0 -0
  350. /package/components/nav/{class.js → nav.class.js} +0 -0
  351. /package/components/nav-group/{class.js → nav-group.class.js} +0 -0
  352. /package/components/nav-item/{class.js → nav-item.class.js} +0 -0
  353. /package/components/noodles/{class.js → noodles.class.js} +0 -0
  354. /package/components/option-card/{class.js → option-card.class.js} +0 -0
  355. /package/components/otp-input/{class.js → otp-input.class.js} +0 -0
  356. /package/components/page/{class.js → page.class.js} +0 -0
  357. /package/components/pagination/{class.js → pagination.class.js} +0 -0
  358. /package/components/pane/{class.js → pane.class.js} +0 -0
  359. /package/components/pipeline-status/{class.js → pipeline-status.class.js} +0 -0
  360. /package/components/popover/{class.js → popover.class.js} +0 -0
  361. /package/components/progress/{class.js → progress.class.js} +0 -0
  362. /package/components/progress-row/{class.js → progress-row.class.js} +0 -0
  363. /package/components/radio/{class.js → radio.class.js} +0 -0
  364. /package/components/range/{class.js → range.class.js} +0 -0
  365. /package/components/rating/{class.js → rating.class.js} +0 -0
  366. /package/components/richtext/{class.js → richtext.class.js} +0 -0
  367. /package/components/row/{class.js → row.class.js} +0 -0
  368. /package/components/search/{class.js → search.class.js} +0 -0
  369. /package/components/segment/{class.js → segment.class.js} +0 -0
  370. /package/components/segmented/{class.js → segmented.class.js} +0 -0
  371. /package/components/skeleton/{class.js → skeleton.class.js} +0 -0
  372. /package/components/slider/{class.js → slider.class.js} +0 -0
  373. /package/components/stack/{class.js → stack.class.js} +0 -0
  374. /package/components/step-progress/{class.js → step-progress.class.js} +0 -0
  375. /package/components/stepper/{class.js → stepper.class.js} +0 -0
  376. /package/components/stream/{class.js → stream.class.js} +0 -0
  377. /package/components/swatch/{class.js → swatch.class.js} +0 -0
  378. /package/components/swiper/{class.js → swiper.class.js} +0 -0
  379. /package/components/switch/{class.js → switch.class.js} +0 -0
  380. /package/components/table/{class.js → table.class.js} +0 -0
  381. /package/components/tabs/{class.js → tabs.class.js} +0 -0
  382. /package/components/tag/{class.js → tag.class.js} +0 -0
  383. /package/components/text/{class.js → text.class.js} +0 -0
  384. /package/components/textarea/{class.js → textarea.class.js} +0 -0
  385. /package/components/toast/{class.js → toast.class.js} +0 -0
  386. /package/components/toggle-group/{class.js → toggle-group.class.js} +0 -0
  387. /package/components/toggle-scheme/{class.js → toggle-scheme.class.js} +0 -0
  388. /package/components/toolbar/{class.js → toolbar.class.js} +0 -0
  389. /package/components/tooltip/{class.js → tooltip.class.js} +0 -0
  390. /package/components/tree/{class.js → tree.class.js} +0 -0
  391. /package/components/upload/{class.js → upload.class.js} +0 -0
@@ -0,0 +1,410 @@
1
+ /**
2
+ * Non-side-effect class export for `<integration-card-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/integration-card`
9
+ * (which imports this file + calls `defineIfFree()`).
10
+ *
11
+ * @see ../../USAGE.md#registration--auto-vs-explicit
12
+ */
13
+
14
+ /**
15
+ * <integration-card-ui provider="slack" name="Slack"
16
+ * logo="/integrations/slack.svg"
17
+ * description="Send AI replies and notifications to channels."
18
+ * status="available">
19
+ * </integration-card-ui>
20
+ *
21
+ * Single tile representing one third-party integration. Status drives the
22
+ * button label + variant; tile chrome borrows from <card-ui> but the
23
+ * primitive is its own surface (one tile = one provider, never compose
24
+ * <card-ui> around it).
25
+ *
26
+ * Events:
27
+ * connect — { detail: { provider } } fired when `status="available"`
28
+ * configure — { detail: { provider } } fired when `status="connected"`
29
+ * retry — { detail: { provider } } fired when `status="error"`
30
+ * disconnect — bubbled from consumer-provided overflow menu items
31
+ * with `data-action="disconnect"` in the `actions` slot
32
+ */
33
+
34
+ import { UIElement } from '../../core/element.js';
35
+
36
+ const STATUS = Object.freeze({
37
+ AVAILABLE: 'available',
38
+ CONNECTED: 'connected',
39
+ ERROR: 'error',
40
+ PENDING: 'pending',
41
+ COMING_SOON: 'coming-soon',
42
+ });
43
+
44
+ const ACTION_FOR_STATUS = Object.freeze({
45
+ available: { event: 'press', emits: 'connect', label: 'Connect', variant: 'primary', icon: '' },
46
+ connected: { event: 'press', emits: 'configure', label: 'Configure', variant: 'outline', icon: '' },
47
+ error: { event: 'press', emits: 'retry', label: 'Retry', variant: 'outline', icon: 'arrow-clockwise' },
48
+ pending: { event: 'press', emits: '', label: 'Connecting…', variant: 'outline', icon: '' },
49
+ });
50
+
51
+ const BADGE_FOR_STATUS = Object.freeze({
52
+ connected: { variant: 'success', text: 'Connected', icon: 'check' },
53
+ error: { variant: 'danger', text: 'Error', icon: 'warning-circle' },
54
+ pending: { variant: 'info', text: 'Connecting…', icon: '' },
55
+ 'coming-soon':{ variant: 'muted', text: 'Coming soon', icon: '' },
56
+ });
57
+
58
+ export class UIIntegrationCard extends UIElement {
59
+ // Phosphor icons this primitive auto-stamps (without consumer markup).
60
+ // Aggregated by installIconLoadersForRegistered() across all defined
61
+ // elements. Audited by check-required-icons.mjs (slot 11).
62
+ static requiredIcons = ['check', 'warning-circle', 'arrow-clockwise'];
63
+
64
+ static properties = {
65
+ provider: { type: String, default: '', reflect: true },
66
+ name: { type: String, default: '', reflect: false },
67
+ logo: { type: String, default: '', reflect: false },
68
+ description: { type: String, default: '', reflect: false },
69
+ status: { type: String, default: 'available', reflect: true },
70
+ 'error-message': { type: String, default: '', attribute: 'error-message' },
71
+ disabled: { type: Boolean, default: false, reflect: true },
72
+ };
73
+
74
+ static parts = {
75
+ body: '<div slot="body" data-integration-card-body></div>',
76
+ logo: '<div slot="logo" data-integration-card-logo></div>',
77
+ heading: '<div slot="heading" data-integration-card-heading></div>',
78
+ desc: '<p slot="description" data-integration-card-description></p>',
79
+ error: '<p slot="error" data-integration-card-error></p>',
80
+ status: '<badge-ui slot="status" data-integration-card-status></badge-ui>',
81
+ footer: '<footer slot="footer" data-integration-card-footer></footer>',
82
+ button: '<button-ui slot="action" data-integration-card-action></button-ui>',
83
+ };
84
+
85
+ static template = () => null;
86
+
87
+ // Stable ID for aria-labelledby pointing at the provider name.
88
+ #headingId = '';
89
+
90
+ // Cached element refs (lazily filled in `connected()`).
91
+ #bodyEl = null;
92
+ #logoEl = null;
93
+ #headingEl = null;
94
+ #descEl = null;
95
+ #errorEl = null;
96
+ #statusEl = null;
97
+ #footerEl = null;
98
+ #buttonEl = null;
99
+
100
+ connected() {
101
+ // Group semantic — labeled region announced by SR.
102
+ this.setAttribute('role', 'group');
103
+
104
+ // Build the static frame once. ensure() is idempotent.
105
+ this.#bodyEl = this.ensure('body');
106
+ this.#logoEl = this.ensure('logo');
107
+ this.#headingEl = this.ensure('heading');
108
+ this.#descEl = this.ensure('desc');
109
+ this.#statusEl = this.ensure('status');
110
+ this.#footerEl = this.ensure('footer');
111
+
112
+ // Slot/structural arrangement: logo + heading + status sit inside body row;
113
+ // description sits below; error sits below description; button sits in footer.
114
+ // We arrange via DOM order so CSS direct-child selectors apply consistently.
115
+ if (this.#logoEl.parentElement !== this.#bodyEl) this.#bodyEl.appendChild(this.#logoEl);
116
+ if (this.#headingEl.parentElement !== this.#bodyEl) this.#bodyEl.appendChild(this.#headingEl);
117
+ if (this.#statusEl.parentElement !== this.#bodyEl) this.#bodyEl.appendChild(this.#statusEl);
118
+
119
+ // Assign a stable id for aria-labelledby (don't clobber author-set id).
120
+ if (!this.#headingEl.id) {
121
+ this.#headingId = `integration-card-heading-${Math.random().toString(36).slice(2, 9)}`;
122
+ this.#headingEl.id = this.#headingId;
123
+ } else {
124
+ this.#headingId = this.#headingEl.id;
125
+ }
126
+ this.setAttribute('aria-labelledby', this.#headingId);
127
+
128
+ // Wire action button activation. The button-ui dispatches `press`; we
129
+ // re-dispatch a typed event (connect / configure / retry) based on status.
130
+ this.addEventListener('press', this.#onPress);
131
+
132
+ // Catch consumer-wired overflow actions in the `actions` slot. The
133
+ // menu-ui dispatches `action` with the action name in detail — when
134
+ // it's `disconnect`, we re-dispatch as a typed event with the
135
+ // provider key. Same recipe for `reauth` / `re-authenticate`.
136
+ this.addEventListener('action', this.#onMenuAction);
137
+ }
138
+
139
+ disconnected() {
140
+ this.removeEventListener('press', this.#onPress);
141
+ this.removeEventListener('action', this.#onMenuAction);
142
+ this.#bodyEl = null;
143
+ this.#logoEl = null;
144
+ this.#headingEl = null;
145
+ this.#descEl = null;
146
+ this.#errorEl = null;
147
+ this.#statusEl = null;
148
+ this.#footerEl = null;
149
+ this.#buttonEl = null;
150
+ }
151
+
152
+ render() {
153
+ if (!this.#headingEl) return;
154
+
155
+ const status = this.#normalizedStatus();
156
+
157
+ // ── Heading ──────────────────────────────────────────────────────
158
+ if (this.name && this.#headingEl.textContent !== this.name) {
159
+ this.#headingEl.textContent = this.name;
160
+ }
161
+
162
+ // ── Description ─────────────────────────────────────────────────
163
+ // The default slot supersedes the description prop when non-empty.
164
+ // We detect "consumer-authored description" via any direct child that
165
+ // isn't one of our stamped data-integration-card-* elements and isn't
166
+ // a slotted action / menu.
167
+ const hasCustomDesc = this.#hasConsumerDescription();
168
+ if (hasCustomDesc) {
169
+ if (this.#descEl.parentElement === this) {
170
+ this.#descEl.remove();
171
+ }
172
+ } else {
173
+ if (!this.#descEl.parentElement) this.appendChild(this.#descEl);
174
+ if (this.description) {
175
+ if (this.#descEl.textContent !== this.description) {
176
+ this.#descEl.textContent = this.description;
177
+ }
178
+ this.#descEl.hidden = false;
179
+ } else {
180
+ this.#descEl.textContent = '';
181
+ this.#descEl.hidden = true;
182
+ }
183
+ }
184
+
185
+ // ── Logo ────────────────────────────────────────────────────────
186
+ this.#renderLogo();
187
+
188
+ // ── Status badge ────────────────────────────────────────────────
189
+ this.#renderStatusBadge(status);
190
+
191
+ // ── Error message (status="error" only) ─────────────────────────
192
+ this.#renderError(status);
193
+
194
+ // ── Action button (label + variant + visibility) ────────────────
195
+ this.#renderActionButton(status);
196
+ }
197
+
198
+ // ── Private rendering helpers ─────────────────────────────────────
199
+
200
+ #normalizedStatus() {
201
+ const s = (this.status || '').trim() || STATUS.AVAILABLE;
202
+ // Validate against the enum; fall back to `available` on bad input.
203
+ if (Object.values(STATUS).includes(s)) return s;
204
+ return STATUS.AVAILABLE;
205
+ }
206
+
207
+ #hasConsumerDescription() {
208
+ // Author description = direct child element that is neither one of our
209
+ // stamped data- markers nor an [slot="actions"] / [slot="action"] node.
210
+ for (const child of this.children) {
211
+ if (child === this.#bodyEl) continue;
212
+ if (child === this.#descEl) continue;
213
+ if (child === this.#errorEl) continue;
214
+ if (child === this.#footerEl) continue;
215
+ if (child.getAttribute && child.getAttribute('slot') === 'actions') continue;
216
+ if (child.dataset && (
217
+ child.dataset.integrationCardBody !== undefined ||
218
+ child.dataset.integrationCardDescription !== undefined ||
219
+ child.dataset.integrationCardError !== undefined ||
220
+ child.dataset.integrationCardFooter !== undefined
221
+ )) continue;
222
+ // Any other direct child counts as consumer-authored description content.
223
+ return true;
224
+ }
225
+ return false;
226
+ }
227
+
228
+ #renderLogo() {
229
+ if (!this.#logoEl) return;
230
+ const logo = (this.logo || '').trim();
231
+
232
+ // No logo → strip any prior content and hide.
233
+ if (!logo) {
234
+ this.#logoEl.replaceChildren();
235
+ this.#logoEl.hidden = true;
236
+ return;
237
+ }
238
+ this.#logoEl.hidden = false;
239
+
240
+ // URL vs icon-name sniff: presence of '/' → URL.
241
+ const isUrl = logo.includes('/');
242
+
243
+ if (isUrl) {
244
+ // Reuse existing <img> if same src; otherwise re-stamp.
245
+ let img = this.#logoEl.querySelector(':scope > img');
246
+ if (img && img.getAttribute('src') === logo) {
247
+ img.setAttribute('alt', `${this.name || this.provider || 'Integration'} logo`);
248
+ return;
249
+ }
250
+ this.#logoEl.replaceChildren();
251
+ img = document.createElement('img');
252
+ img.setAttribute('src', logo);
253
+ img.setAttribute('alt', `${this.name || this.provider || 'Integration'} logo`);
254
+ img.setAttribute('data-integration-logo', '');
255
+ img.setAttribute('loading', 'lazy');
256
+ img.setAttribute('decoding', 'async');
257
+ this.#logoEl.appendChild(img);
258
+ return;
259
+ }
260
+
261
+ // Icon name → <icon-ui>.
262
+ let icon = this.#logoEl.querySelector(':scope > icon-ui');
263
+ if (icon && icon.getAttribute('name') === logo) return;
264
+ this.#logoEl.replaceChildren();
265
+ icon = document.createElement('icon-ui');
266
+ icon.setAttribute('name', logo);
267
+ icon.setAttribute('aria-hidden', 'true');
268
+ this.#logoEl.appendChild(icon);
269
+ }
270
+
271
+ #renderStatusBadge(status) {
272
+ if (!this.#statusEl) return;
273
+ const cfg = BADGE_FOR_STATUS[status];
274
+ if (!cfg) {
275
+ // No badge for `available` — keep DOM but hide.
276
+ this.#statusEl.hidden = true;
277
+ this.#statusEl.removeAttribute('aria-label');
278
+ return;
279
+ }
280
+ this.#statusEl.hidden = false;
281
+ this.#statusEl.setAttribute('variant', cfg.variant);
282
+ this.#statusEl.setAttribute('text', cfg.text);
283
+ this.#statusEl.setAttribute('size', 'sm');
284
+ if (cfg.icon) this.#statusEl.setAttribute('icon', cfg.icon);
285
+ else this.#statusEl.removeAttribute('icon');
286
+ // Explicit aria-label so SR announces "Connected" even when text rendering
287
+ // is delayed by icon-registry boot.
288
+ this.#statusEl.setAttribute('aria-label', cfg.text);
289
+ }
290
+
291
+ #renderError(status) {
292
+ const msg = (this['error-message'] || this.getAttribute('error-message') || '').trim();
293
+ if (status === STATUS.ERROR && msg) {
294
+ if (!this.#errorEl) this.#errorEl = this.ensure('error');
295
+ if (!this.#errorEl.parentElement) {
296
+ // Insert after description, before footer if possible.
297
+ if (this.#descEl?.parentElement === this) {
298
+ this.#descEl.after(this.#errorEl);
299
+ } else if (this.#footerEl?.parentElement === this) {
300
+ this.insertBefore(this.#errorEl, this.#footerEl);
301
+ } else {
302
+ this.appendChild(this.#errorEl);
303
+ }
304
+ }
305
+ if (this.#errorEl.textContent !== msg) this.#errorEl.textContent = msg;
306
+ this.#errorEl.hidden = false;
307
+ this.#errorEl.setAttribute('role', 'status');
308
+ } else if (this.#errorEl) {
309
+ this.#errorEl.hidden = true;
310
+ this.#errorEl.textContent = '';
311
+ }
312
+ }
313
+
314
+ #renderActionButton(status) {
315
+ // coming-soon → hide the button entirely; the badge carries the label.
316
+ if (status === STATUS.COMING_SOON) {
317
+ this.drop('button');
318
+ this.#buttonEl = null;
319
+ // Footer with no button can still hold the [slot="actions"] menu,
320
+ // so we keep the footer node attached.
321
+ return;
322
+ }
323
+
324
+ const cfg = ACTION_FOR_STATUS[status];
325
+ if (!cfg) {
326
+ this.drop('button');
327
+ this.#buttonEl = null;
328
+ return;
329
+ }
330
+
331
+ // Ensure the button exists and sits inside the footer (light DOM
332
+ // positioning: button is a direct child of footer, footer is a direct
333
+ // child of host).
334
+ if (!this.#buttonEl) this.#buttonEl = this.ensure('button');
335
+ if (this.#buttonEl.parentElement !== this.#footerEl) {
336
+ this.#footerEl.insertBefore(this.#buttonEl, this.#footerEl.firstChild);
337
+ }
338
+
339
+ this.#buttonEl.setAttribute('text', cfg.label);
340
+ this.#buttonEl.setAttribute('variant', cfg.variant);
341
+ this.#buttonEl.setAttribute('size', 'sm');
342
+ if (cfg.icon) this.#buttonEl.setAttribute('icon', cfg.icon);
343
+ else this.#buttonEl.removeAttribute('icon');
344
+
345
+ // Toggle semantics — aria-pressed reflects "is the integration on?"
346
+ // Provides screen-reader feedback on the action's effect.
347
+ if (status === STATUS.CONNECTED) {
348
+ this.#buttonEl.setAttribute('aria-pressed', 'true');
349
+ } else if (status === STATUS.AVAILABLE) {
350
+ this.#buttonEl.setAttribute('aria-pressed', 'false');
351
+ } else {
352
+ this.#buttonEl.removeAttribute('aria-pressed');
353
+ }
354
+
355
+ // Explicit aria-label so SR announces "Connect to Slack" outside group ctx.
356
+ const verbForStatus = {
357
+ available: 'Connect to',
358
+ connected: 'Configure',
359
+ error: 'Retry connecting',
360
+ pending: 'Connecting to',
361
+ }[status] || cfg.label;
362
+ const labelTarget = this.name || this.provider || 'integration';
363
+ this.#buttonEl.setAttribute('aria-label', `${verbForStatus} ${labelTarget}`);
364
+
365
+ // pending → non-interactive; disabled prop → non-interactive too.
366
+ if (status === STATUS.PENDING || this.disabled) {
367
+ this.#buttonEl.setAttribute('disabled', '');
368
+ } else {
369
+ this.#buttonEl.removeAttribute('disabled');
370
+ }
371
+ }
372
+
373
+ // ── Event handlers ────────────────────────────────────────────────
374
+
375
+ #onPress = (e) => {
376
+ // Only react to presses on OUR action button — overflow menu items
377
+ // dispatch `action` instead.
378
+ if (!this.#buttonEl) return;
379
+ if (e.target !== this.#buttonEl && !e.composedPath?.().includes(this.#buttonEl)) return;
380
+ if (this.disabled) { e.stopPropagation(); return; }
381
+
382
+ const status = this.#normalizedStatus();
383
+ if (status === STATUS.PENDING || status === STATUS.COMING_SOON) {
384
+ e.stopPropagation();
385
+ return;
386
+ }
387
+
388
+ const cfg = ACTION_FOR_STATUS[status];
389
+ if (!cfg || !cfg.emits) return;
390
+
391
+ this.dispatchEvent(new CustomEvent(cfg.emits, {
392
+ bubbles: true,
393
+ detail: { provider: this.provider },
394
+ }));
395
+ };
396
+
397
+ #onMenuAction = (e) => {
398
+ // Bubbled `action` events from the consumer-provided overflow menu
399
+ // in [slot="actions"]. Detail shape from menu-item-ui: { action: '<name>' }.
400
+ const action = e.detail?.action || e.target?.getAttribute?.('action');
401
+ if (!action) return;
402
+ if (action === 'disconnect' || action === 'reauth' || action === 're-authenticate') {
403
+ const emits = action === 'disconnect' ? 'disconnect' : 'reauth';
404
+ this.dispatchEvent(new CustomEvent(emits, {
405
+ bubbles: true,
406
+ detail: { provider: this.provider },
407
+ }));
408
+ }
409
+ };
410
+ }
@@ -0,0 +1,169 @@
1
+ @scope (integration-card-ui) {
2
+ :where(:scope) {
3
+ /* ── Layout ── */
4
+ --integration-card-bg-default: var(--a-bg);
5
+ --integration-card-border-default: 1px solid var(--a-border-subtle);
6
+ --integration-card-radius-default: var(--a-radius-md);
7
+ --integration-card-px-default: var(--a-space-4);
8
+ --integration-card-py-default: var(--a-space-4);
9
+ --integration-card-gap-default: var(--a-space-3);
10
+
11
+ /* ── Inner rhythm ── */
12
+ --integration-card-row-gap-default: var(--a-space-3);
13
+ --integration-card-text-gap-default: var(--a-space-1);
14
+
15
+ /* ── Logo ── */
16
+ --integration-card-logo-size-default: var(--a-space-7);
17
+ --integration-card-logo-radius-default: var(--a-radius-sm);
18
+
19
+ /* ── Typography ── */
20
+ --integration-card-heading-fg-default: var(--a-fg-strong);
21
+ --integration-card-heading-size-default: var(--a-ui-size);
22
+ --integration-card-heading-weight-default: var(--a-weight-medium);
23
+ --integration-card-description-fg-default: var(--a-fg-muted);
24
+ --integration-card-description-size-default: var(--a-ui-sm);
25
+ --integration-card-error-fg-default: var(--a-danger-text);
26
+ --integration-card-error-size-default: var(--a-ui-sm);
27
+
28
+ /* ── States ── */
29
+ --integration-card-disabled-opacity-default: 0.6;
30
+ }
31
+
32
+ /* ── Base — vertical stack: body row + (description) + (error) + footer ── */
33
+ :scope {
34
+ box-sizing: border-box;
35
+ display: grid;
36
+ grid-template-rows: auto 1fr auto;
37
+ gap: var(--integration-card-gap, var(--integration-card-gap-default));
38
+ padding: var(--integration-card-py, var(--integration-card-py-default)) var(--integration-card-px, var(--integration-card-px-default));
39
+ background: var(--integration-card-bg, var(--integration-card-bg-default));
40
+ border: var(--integration-card-border, var(--integration-card-border-default));
41
+ border-radius: var(--integration-card-radius, var(--integration-card-radius-default));
42
+ width: stretch;
43
+ min-width: 0;
44
+ }
45
+
46
+ /* ── Status-driven border tints ── */
47
+ :scope[status="connected"] {
48
+ --integration-card-border-default: 1px solid var(--a-success-border, var(--a-success-strong));
49
+ }
50
+ :scope[status="error"] {
51
+ --integration-card-border-default: 1px solid var(--a-danger-border, var(--a-danger-strong));
52
+ }
53
+
54
+ /* ── Coming-soon + disabled → opacity reduction ── */
55
+ :scope[status="coming-soon"],
56
+ :scope[disabled] {
57
+ opacity: var(--integration-card-disabled-opacity, var(--integration-card-disabled-opacity-default));
58
+ }
59
+
60
+ /* ── Body row — logo | heading | status badge ── */
61
+ :scope > [data-integration-card-body] {
62
+ display: grid;
63
+ grid-template-columns: auto minmax(0, 1fr) auto;
64
+ align-items: center;
65
+ column-gap: var(--integration-card-row-gap, var(--integration-card-row-gap-default));
66
+ }
67
+
68
+ /* ── Logo square ── */
69
+ :scope > [data-integration-card-body] > [data-integration-card-logo] {
70
+ grid-column: 1;
71
+ inline-size: var(--integration-card-logo-size, var(--integration-card-logo-size-default));
72
+ block-size: var(--integration-card-logo-size, var(--integration-card-logo-size-default));
73
+ border-radius: var(--integration-card-logo-radius, var(--integration-card-logo-radius-default));
74
+ display: inline-flex;
75
+ align-items: center;
76
+ justify-content: center;
77
+ overflow: hidden;
78
+ flex-shrink: 0;
79
+ }
80
+ :scope > [data-integration-card-body] > [data-integration-card-logo][hidden] {
81
+ display: none;
82
+ }
83
+ :scope [data-integration-card-logo] > img {
84
+ inline-size: 100%;
85
+ block-size: 100%;
86
+ object-fit: contain;
87
+ display: block;
88
+ }
89
+ :scope [data-integration-card-logo] > icon-ui {
90
+ --a-icon-size: calc(var(--integration-card-logo-size, var(--integration-card-logo-size-default)) - var(--a-space-1));
91
+ color: var(--a-fg-muted);
92
+ }
93
+
94
+ /* ── Heading (provider name) ── */
95
+ :scope > [data-integration-card-body] > [data-integration-card-heading] {
96
+ grid-column: 2;
97
+ color: var(--integration-card-heading-fg, var(--integration-card-heading-fg-default));
98
+ font-size: var(--integration-card-heading-size, var(--integration-card-heading-size-default));
99
+ font-weight: var(--integration-card-heading-weight, var(--integration-card-heading-weight-default));
100
+ line-height: 1.3;
101
+ min-width: 0;
102
+ overflow: hidden;
103
+ text-overflow: ellipsis;
104
+ white-space: nowrap;
105
+ }
106
+
107
+ /* ── Status badge (sits in body row, right-aligned) ── */
108
+ :scope > [data-integration-card-body] > [data-integration-card-status] {
109
+ grid-column: 3;
110
+ justify-self: end;
111
+ }
112
+ :scope > [data-integration-card-body] > [data-integration-card-status][hidden] {
113
+ display: none;
114
+ }
115
+
116
+ /* ── Description ── */
117
+ :scope > [data-integration-card-description] {
118
+ margin: 0;
119
+ color: var(--integration-card-description-fg, var(--integration-card-description-fg-default));
120
+ font-size: var(--integration-card-description-size, var(--integration-card-description-size-default));
121
+ line-height: 1.4;
122
+ }
123
+ :scope > [data-integration-card-description][hidden] {
124
+ display: none;
125
+ }
126
+
127
+ /* ── Author-provided description (default slot) — same typography
128
+ by selector rather than slot to support bare <p>, <text-ui>, <small>. */
129
+ :scope > p:not([data-integration-card-description]):not([data-integration-card-error]),
130
+ :scope > small:not([data-integration-card-error]) {
131
+ margin: 0;
132
+ color: var(--integration-card-description-fg, var(--integration-card-description-fg-default));
133
+ font-size: var(--integration-card-description-size, var(--integration-card-description-size-default));
134
+ line-height: 1.4;
135
+ }
136
+
137
+ /* ── Error message ── */
138
+ :scope > [data-integration-card-error] {
139
+ margin: 0;
140
+ color: var(--integration-card-error-fg, var(--integration-card-error-fg-default));
141
+ font-size: var(--integration-card-error-size, var(--integration-card-error-size-default));
142
+ line-height: 1.4;
143
+ }
144
+ :scope > [data-integration-card-error][hidden] {
145
+ display: none;
146
+ }
147
+
148
+ /* ── Footer — action button + optional [slot="actions"] overflow ── */
149
+ :scope > [data-integration-card-footer] {
150
+ display: flex;
151
+ align-items: center;
152
+ gap: var(--integration-card-row-gap, var(--integration-card-row-gap-default));
153
+ margin: 0;
154
+ padding: 0;
155
+ }
156
+
157
+ /* Action button is first child of footer; menu / overflow goes to the right. */
158
+ :scope > [data-integration-card-footer] > [data-integration-card-action] {
159
+ flex: 0 0 auto;
160
+ }
161
+ :scope > [data-integration-card-footer] > [slot="actions"] {
162
+ margin-inline-start: auto;
163
+ }
164
+
165
+ /* ── Disabled state — button-ui already dims; ensure card chrome too. */
166
+ :scope[disabled] {
167
+ pointer-events: none;
168
+ }
169
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * `<integration-card-ui>` — Single-tile primitive representing one third-party integration (Slack, GitHub, Stripe, …). Shows the provider logo + name + description + status pill + a single primary action button whose label and variant are derived from `status`. Composes into a grid via the SPEC-063 integrations-page composite. One tile = one provider; the button variant is computed from `status`, not a separate prop. Distinct from <option-card-ui> (a single-select radio with no status / async action) and <card-ui> (the generic bordered surface this specializes).
3
+ *
4
+ * @see https://ui-kit.exe.xyz/site/components/integration-card
5
+ *
6
+ * Type declarations generated by scripts/build/dts-codegen.mjs from
7
+ * the component's `.a2ui.json` sidecar(s). Edit the source `.yaml`,
8
+ * run `npm run build:components`, then `npm run codegen:dts` to
9
+ * regenerate; or hand-author this file fully if rich event types are
10
+ * needed beyond what the yaml `events:` block can express.
11
+ */
12
+
13
+ import { UIElement } from '../../core/element.js';
14
+
15
+ export interface IntegrationCardConfigureEventDetail {
16
+ /** The provider key for this card. */
17
+ provider: string;
18
+ }
19
+
20
+ export type IntegrationCardConfigureEvent = CustomEvent<IntegrationCardConfigureEventDetail>;
21
+ export interface IntegrationCardConnectEventDetail {
22
+ /** The provider key for this card. */
23
+ provider: string;
24
+ }
25
+
26
+ export type IntegrationCardConnectEvent = CustomEvent<IntegrationCardConnectEventDetail>;
27
+ export interface IntegrationCardDisconnectEventDetail {
28
+ /** The provider key for this card. */
29
+ provider: string;
30
+ }
31
+
32
+ export type IntegrationCardDisconnectEvent = CustomEvent<IntegrationCardDisconnectEventDetail>;
33
+ export interface IntegrationCardRetryEventDetail {
34
+ /** The provider key for this card. */
35
+ provider: string;
36
+ }
37
+
38
+ export type IntegrationCardRetryEvent = CustomEvent<IntegrationCardRetryEventDetail>;
39
+
40
+ export class UIIntegrationCard extends UIElement {
41
+ /** One-line description of what the integration does. */
42
+ description: string;
43
+ /** Disables the action button. */
44
+ disabled: boolean;
45
+ /** Logo URL (preferred — renders as <img>) or icon name (renders as <icon-ui>). URLs are sniffed by presence of `/`. */
46
+ logo: string;
47
+ /** Display name shown as the card title. Required. */
48
+ name: string;
49
+ /** Provider key — `slack`, `github`, etc. — used for analytics + a11y label. Required. */
50
+ provider: string;
51
+ /** Current connection state — drives the button label, variant, and visibility. `available` shows a primary `Connect`; `connected` shows an outline `Configure`; `error` shows an outline `Retry`; `pending` shows a non-interactive spinner; `coming-soon` hides the button. */
52
+ status: string;
53
+
54
+ addEventListener<K extends keyof HTMLElementEventMap>(
55
+ type: K,
56
+ listener: (this: UIIntegrationCard, ev: HTMLElementEventMap[K]) => unknown,
57
+ options?: boolean | AddEventListenerOptions,
58
+ ): void;
59
+ addEventListener(type: 'configure', listener: (ev: IntegrationCardConfigureEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
60
+ addEventListener(type: 'connect', listener: (ev: IntegrationCardConnectEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
61
+ addEventListener(type: 'disconnect', listener: (ev: IntegrationCardDisconnectEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
62
+ addEventListener(type: 'retry', listener: (ev: IntegrationCardRetryEvent) => unknown, options?: boolean | AddEventListenerOptions): void;
63
+ }
@@ -0,0 +1,41 @@
1
+ # integration-card — Examples
2
+
3
+ ## Available (default)
4
+
5
+ ```html
6
+ <integration-card-ui
7
+ provider="slack"
8
+ name="Slack"
9
+ logo="/integrations/slack.svg"
10
+ description="Send AI replies and notifications to channels."
11
+ status="available">
12
+ </integration-card-ui>
13
+ ```
14
+
15
+ ## Connected — overflow menu
16
+
17
+ ```html
18
+ <integration-card-ui provider="github" name="GitHub" status="connected"
19
+ logo="/integrations/github.svg"
20
+ description="Sync issues and pull requests.">
21
+ <menu-ui slot="actions" label="More" trigger-icon="dots-three" trigger-variant="ghost">
22
+ <menu-item-ui action="reauth">Re-authenticate</menu-item-ui>
23
+ <menu-item-ui action="disconnect" variant="danger">Disconnect</menu-item-ui>
24
+ </menu-ui>
25
+ </integration-card-ui>
26
+ ```
27
+
28
+ ## Error — Retry
29
+
30
+ ```html
31
+ <integration-card-ui provider="stripe" name="Stripe" status="error"
32
+ error-message="Token expired — re-authenticate.">
33
+ </integration-card-ui>
34
+ ```
35
+
36
+ ## Coming soon (non-interactive)
37
+
38
+ ```html
39
+ <integration-card-ui provider="zapier" name="Zapier" status="coming-soon">
40
+ </integration-card-ui>
41
+ ```