@kubex/zinc 1.1.185 → 2.0.0

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 (358) hide show
  1. package/.browserslistrc +2 -0
  2. package/.github/workflows/js_build_and_deploy.yaml +54 -1
  3. package/.github/workflows/test.yaml +27 -0
  4. package/dist/chunks/zn.UWJLIXDT.js +1 -0
  5. package/dist/custom-elements.json +9998 -2345
  6. package/dist/vscode.html-custom-data.json +1022 -67
  7. package/dist/web-types.json +2129 -177
  8. package/dist/zn.d.ts +2293 -416
  9. package/dist/zn.min.css +1 -1
  10. package/dist/zn.min.js +2310 -1637
  11. package/docs/_includes/default.njk +4 -1
  12. package/docs/assets/scripts/docs.js +22 -6
  13. package/docs/assets/styles/docs.css +9 -2
  14. package/docs/data/report-table-day-compare.json +380 -0
  15. package/docs/data/report-table.json +314 -0
  16. package/docs/pages/components/avatar-group.md +57 -0
  17. package/docs/pages/components/badge.md +67 -0
  18. package/docs/pages/components/button.md +41 -1
  19. package/docs/pages/components/chat-message.md +16 -0
  20. package/docs/pages/components/checkbox.md +1 -1
  21. package/docs/pages/components/chip.md +25 -13
  22. package/docs/pages/components/color-picker.md +37 -0
  23. package/docs/pages/components/copy-button.md +2 -2
  24. package/docs/pages/components/data-table-filter.md +33 -50
  25. package/docs/pages/components/data-table-search.md +4 -4
  26. package/docs/pages/components/data-table.md +27 -3
  27. package/docs/pages/components/date-range.md +116 -0
  28. package/docs/pages/components/dialog.md +29 -37
  29. package/docs/pages/components/diff-row.md +28 -0
  30. package/docs/pages/components/diff.md +46 -0
  31. package/docs/pages/components/editor.md +55 -0
  32. package/docs/pages/components/icon.md +46 -1
  33. package/docs/pages/components/input.md +30 -11
  34. package/docs/pages/components/item.md +35 -0
  35. package/docs/pages/components/key-value.md +25 -0
  36. package/docs/pages/components/menu.md +74 -7
  37. package/docs/pages/components/nav-item.md +15 -0
  38. package/docs/pages/components/nav-list.md +132 -0
  39. package/docs/pages/components/navbar.md +100 -1
  40. package/docs/pages/components/page.md +242 -74
  41. package/docs/pages/components/priority-list.md +43 -5
  42. package/docs/pages/components/query-builder.md +23 -0
  43. package/docs/pages/components/radio-group.md +33 -1
  44. package/docs/pages/components/radio.md +1 -1
  45. package/docs/pages/components/report-table.md +202 -0
  46. package/docs/pages/components/stat.md +30 -0
  47. package/docs/pages/components/tile.md +11 -0
  48. package/docs/pages/components/tooltip.md +1 -1
  49. package/docs/pages/components/translation-grid.md +86 -0
  50. package/docs/pages/components/vertical-stepper.md +108 -64
  51. package/package.json +2 -1
  52. package/scss/_global-spacing.scss +2 -2
  53. package/scss/_root.scss +11 -171
  54. package/scss/boot.scss +14 -0
  55. package/scss/mixins/_helper-mixins.scss +24 -2
  56. package/scss/mixins/_motion-mixins.scss +126 -0
  57. package/scss/mixins/_spacing-mixins.scss +4 -3
  58. package/scss/mixins/_tab-indicator-mixins.scss +12 -0
  59. package/scss/mixins/_typography-mixins.scss +1 -1
  60. package/scss/mixins/index.scss +14 -0
  61. package/scss/shared/_base.scss +32 -1
  62. package/scss/shared/_button-mixin.scss +7 -5
  63. package/scss/shared/_table.scss +35 -4
  64. package/scss/shared/nav.scss +4 -0
  65. package/scss/shared/shimmer.scss +1 -1
  66. package/scss/table-controls.scss +206 -0
  67. package/scss/themes/_dark.scss +1 -3
  68. package/scss/themes/_index.scss +1 -1
  69. package/scss/themes/_light.scss +46 -6
  70. package/scss/themes/_modes.scss +11 -11
  71. package/scss/themes/_openbyte.scss +944 -0
  72. package/scss/themes/_typography.scss +4 -2
  73. package/scss/variables/index.scss +0 -9
  74. package/src/components/alert/alert.component.ts +14 -4
  75. package/src/components/alert/alert.scss +55 -69
  76. package/src/components/alert/alert.test.ts +8 -0
  77. package/src/components/animated-button/animated-button.component.ts +3 -2
  78. package/src/components/animated-button/animated-button.scss +6 -23
  79. package/src/components/animated-button/animated-button.test.ts +4 -3
  80. package/src/components/avatar-group/avatar-group.component.ts +84 -0
  81. package/src/components/avatar-group/avatar-group.scss +54 -0
  82. package/src/components/avatar-group/avatar-group.test.ts +59 -0
  83. package/src/components/avatar-group/index.ts +12 -0
  84. package/src/components/badge/badge.component.ts +49 -0
  85. package/src/components/badge/badge.scss +80 -0
  86. package/src/components/badge/badge.test.ts +34 -0
  87. package/src/components/badge/index.ts +12 -0
  88. package/src/components/bulk-actions/bulk-actions.component.ts +14 -2
  89. package/src/components/button/button.component.ts +228 -8
  90. package/src/components/button/button.scss +115 -32
  91. package/src/components/button/button.test.ts +122 -0
  92. package/src/components/button-menu/button-menu.component.ts +32 -11
  93. package/src/components/button-menu/button-menu.scss +5 -1
  94. package/src/components/channel-tile/channel-tile.component.ts +34 -27
  95. package/src/components/channel-tile/channel-tile.scss +76 -43
  96. package/src/components/chart/builders.test.ts +46 -0
  97. package/src/components/chart/builders.ts +27 -3
  98. package/src/components/chart/chart-live.test.ts +4 -6
  99. package/src/components/chart/chart.component.ts +125 -5
  100. package/src/components/chart/chart.scss +121 -0
  101. package/src/components/chart/chart.test.ts +81 -0
  102. package/src/components/chat-message/chat-message.component.ts +21 -1
  103. package/src/components/chat-message/chat-message.scss +13 -2
  104. package/src/components/chat-message/chat-message.test.ts +9 -0
  105. package/src/components/checkbox/checkbox.component.ts +61 -27
  106. package/src/components/checkbox/checkbox.scss +58 -25
  107. package/src/components/checkbox-group/checkbox-group.component.ts +7 -1
  108. package/src/components/chip/chip.component.ts +11 -1
  109. package/src/components/chip/chip.scss +91 -26
  110. package/src/components/chip/chip.test.ts +17 -0
  111. package/src/components/collapsible/collapsible.component.ts +17 -3
  112. package/src/components/collapsible/collapsible.scss +19 -10
  113. package/src/components/color-picker/color-picker.component.ts +277 -0
  114. package/src/components/color-picker/color-picker.scss +141 -0
  115. package/src/components/color-picker/color-picker.test.ts +57 -0
  116. package/src/components/color-picker/index.ts +12 -0
  117. package/src/components/cols/cols.component.ts +10 -0
  118. package/src/components/cols/cols.test.ts +17 -0
  119. package/src/components/confirm/confirm.component.ts +20 -6
  120. package/src/components/confirm/confirm.test.ts +52 -0
  121. package/src/components/content-block/content-block.component.ts +38 -9
  122. package/src/components/content-block/content-block.scss +11 -5
  123. package/src/components/content-block/content-block.test.ts +26 -0
  124. package/src/components/copy-button/copy-button.component.ts +2 -2
  125. package/src/components/copy-button/copy-button.scss +17 -4
  126. package/src/components/data-select/data-select.component.ts +3 -1
  127. package/src/components/data-table/data-table.component.ts +362 -140
  128. package/src/components/data-table/data-table.scss +133 -60
  129. package/src/components/data-table/data-table.test.ts +356 -4
  130. package/src/components/data-table-filter/data-table-filter.component.ts +426 -406
  131. package/src/components/data-table-filter/data-table-filter.scss +176 -148
  132. package/src/components/data-table-filter/data-table-filter.test.ts +271 -273
  133. package/src/components/data-table-search/data-table-search.component.ts +4 -2
  134. package/src/components/data-table-search/data-table-search.scss +8 -1
  135. package/src/components/data-table-search/data-table-search.test.ts +29 -0
  136. package/src/components/date-range/date-range-math.test.ts +78 -0
  137. package/src/components/date-range/date-range-math.ts +105 -0
  138. package/src/components/date-range/date-range.component.ts +375 -0
  139. package/src/components/date-range/date-range.scss +272 -0
  140. package/src/components/date-range/date-range.test.ts +247 -0
  141. package/src/components/date-range/index.ts +12 -0
  142. package/src/components/datepicker/datepicker.component.ts +44 -7
  143. package/src/components/datepicker/datepicker.scss +9 -0
  144. package/src/components/datepicker/datepicker.test.ts +2 -2
  145. package/src/components/dialog/dialog.component.ts +38 -19
  146. package/src/components/dialog/dialog.scss +65 -36
  147. package/src/components/dialog/dialog.test.ts +14 -0
  148. package/src/components/diff/diff.component.ts +55 -0
  149. package/src/components/diff/diff.scss +46 -0
  150. package/src/components/diff/diff.test.ts +15 -0
  151. package/src/components/diff/index.ts +12 -0
  152. package/src/components/diff-row/diff-row.component.ts +75 -0
  153. package/src/components/diff-row/diff-row.scss +70 -0
  154. package/src/components/diff-row/diff-row.test.ts +37 -0
  155. package/src/components/diff-row/index.ts +12 -0
  156. package/src/components/dropdown/dropdown.component.ts +12 -10
  157. package/src/components/dropdown/dropdown.scss +2 -0
  158. package/src/components/editor/editor.component.ts +159 -11
  159. package/src/components/editor/editor.scss +250 -1
  160. package/src/components/editor/editor.test.ts +31 -3
  161. package/src/components/editor/modules/ai/panel/ai-panel.scss +0 -1
  162. package/src/components/editor/modules/attachment/attachment.ts +53 -31
  163. package/src/components/editor/modules/context-menu/context-menu.ts +17 -0
  164. package/src/components/editor/modules/dialog/dialog.component.ts +23 -1
  165. package/src/components/editor/modules/dialog/dialog.test.ts +28 -0
  166. package/src/components/editor/modules/emoji/emoji.ts +4 -5
  167. package/src/components/editor/modules/merge-field/merge-field.ts +83 -0
  168. package/src/components/editor/modules/toolbar/toolbar.component.ts +121 -68
  169. package/src/components/editor/modules/toolbar/toolbar.scss +38 -0
  170. package/src/components/editor/modules/toolbar/toolbar.ts +25 -1
  171. package/src/components/empty-state/empty-state.component.ts +6 -1
  172. package/src/components/empty-state/empty-state.scss +21 -4
  173. package/src/components/expanding-action/expanding-action.component.ts +22 -34
  174. package/src/components/expanding-action/expanding-action.scss +3 -45
  175. package/src/components/expanding-action/expanding-action.test.ts +2 -1
  176. package/src/components/file/file.component.ts +30 -2
  177. package/src/components/file/file.scss +6 -0
  178. package/src/components/flow-builder/flow-builder.component.ts +36 -3
  179. package/src/components/flow-builder/flow-builder.scss +0 -6
  180. package/src/components/flow-builder/modules/flow-canvas/flow-canvas.component.ts +8 -0
  181. package/src/components/flow-builder/modules/flow-canvas/flow-canvas.scss +10 -5
  182. package/src/components/flow-builder/modules/flow-node/flow-node.component.ts +17 -0
  183. package/src/components/flow-builder/modules/flow-node/flow-node.scss +10 -0
  184. package/src/components/form-actions/form-actions.component.ts +48 -2
  185. package/src/components/form-actions/form-actions.scss +1 -1
  186. package/src/components/form-group/form-group.component.ts +10 -3
  187. package/src/components/form-group/form-group.scss +37 -4
  188. package/src/components/form-group/form-group.test.ts +10 -0
  189. package/src/components/header/header.component.ts +11 -4
  190. package/src/components/header/header.scss +47 -5
  191. package/src/components/hover-container/hover-container.component.ts +4 -5
  192. package/src/components/icon/icon.component.ts +95 -9
  193. package/src/components/icon/icon.scss +70 -6
  194. package/src/components/icon/icon.test.ts +55 -0
  195. package/src/components/icon-picker/icon-picker.component.ts +35 -1
  196. package/src/components/inline-edit/inline-edit.component.ts +38 -8
  197. package/src/components/inline-edit/inline-edit.scss +128 -19
  198. package/src/components/input/input.component.ts +173 -297
  199. package/src/components/input/input.scss +65 -43
  200. package/src/components/input/input.test.ts +86 -4
  201. package/src/components/item/item.component.ts +12 -0
  202. package/src/components/item/item.scss +102 -3
  203. package/src/components/item/item.test.ts +23 -0
  204. package/src/components/key/key.component.ts +2 -2
  205. package/src/components/key/key.scss +11 -0
  206. package/src/components/key-value/index.ts +12 -0
  207. package/src/components/key-value/key-value.component.ts +160 -0
  208. package/src/components/key-value/key-value.scss +20 -0
  209. package/src/components/key-value/key-value.test.ts +22 -0
  210. package/src/components/markdown-editor/markdown-editor.component.ts +8 -1
  211. package/src/components/menu/menu.component.ts +21 -2
  212. package/src/components/menu/menu.scss +43 -23
  213. package/src/components/menu/menu.test.ts +33 -0
  214. package/src/components/menu-item/menu-item.component.ts +13 -2
  215. package/src/components/menu-item/menu-item.scss +56 -23
  216. package/src/components/menu-item/menu-item.test.ts +31 -0
  217. package/src/components/menu-item/submenu-controller.ts +25 -15
  218. package/src/components/menu-label/index.ts +12 -0
  219. package/src/components/menu-label/menu-label.component.ts +30 -0
  220. package/src/components/menu-label/menu-label.scss +18 -0
  221. package/src/components/nav-item/index.ts +12 -0
  222. package/src/components/nav-item/nav-item.component.ts +83 -0
  223. package/src/components/nav-item/nav-item.scss +128 -0
  224. package/src/components/nav-list/index.ts +12 -0
  225. package/src/components/nav-list/nav-list.component.ts +313 -0
  226. package/src/components/nav-list/nav-list.scss +87 -0
  227. package/src/components/nav-list/nav-list.test.ts +250 -0
  228. package/src/components/navbar/navbar.component.ts +235 -171
  229. package/src/components/navbar/navbar.scss +333 -169
  230. package/src/components/navbar/navbar.test.ts +63 -143
  231. package/src/components/page/page.component.ts +114 -29
  232. package/src/components/page/page.scss +223 -95
  233. package/src/components/page/page.test.ts +214 -3
  234. package/src/components/page-builder/page-builder.component.ts +51 -5
  235. package/src/components/page-builder/page-builder.scss +6 -1
  236. package/src/components/page-nav/page-nav.component.ts +25 -3
  237. package/src/components/page-nav/page-nav.scss +10 -2
  238. package/src/components/pagination/pagination.scss +1 -1
  239. package/src/components/panel/panel.component.ts +14 -13
  240. package/src/components/panel/panel.scss +14 -10
  241. package/src/components/panel/panel.test.ts +42 -0
  242. package/src/components/popup/popup.component.ts +20 -13
  243. package/src/components/popup/popup.test.ts +21 -1
  244. package/src/components/priority-list/priority-list.component.ts +157 -172
  245. package/src/components/priority-list/priority-list.scss +113 -17
  246. package/src/components/priority-list/priority-list.test.ts +301 -3
  247. package/src/components/progress-bar/progress-bar.component.ts +1 -1
  248. package/src/components/progress-bar/progress-bar.scss +16 -4
  249. package/src/components/progress-tile/progress-tile.scss +1 -1
  250. package/src/components/query-builder/query-builder.component.ts +85 -10
  251. package/src/components/query-builder/query-builder.scss +7 -1
  252. package/src/components/query-builder/query-builder.test.ts +40 -0
  253. package/src/components/radio/radio.component.ts +57 -18
  254. package/src/components/radio/radio.scss +101 -26
  255. package/src/components/radio-group/radio-group.component.ts +45 -7
  256. package/src/components/radio-group/radio-group.scss +17 -0
  257. package/src/components/radio-group/radio-group.test.ts +63 -2
  258. package/src/components/rating/rating.component.ts +23 -0
  259. package/src/components/rating/rating.scss +1 -1
  260. package/src/components/remarkd-editor/remarkd-editor.component.ts +60 -5
  261. package/src/components/remarkd-editor/remarkd-editor.scss +12 -4
  262. package/src/components/report-table/index.ts +12 -0
  263. package/src/components/report-table/report-format.test.ts +86 -0
  264. package/src/components/report-table/report-format.ts +72 -0
  265. package/src/components/report-table/report-math.test.ts +74 -0
  266. package/src/components/report-table/report-math.ts +252 -0
  267. package/src/components/report-table/report-table.component.ts +681 -0
  268. package/src/components/report-table/report-table.scss +328 -0
  269. package/src/components/report-table/report-table.test.ts +702 -0
  270. package/src/components/reveal/reveal.component.ts +15 -1
  271. package/src/components/reveal/reveal.scss +5 -0
  272. package/src/components/schedule-builder/schedule-builder.component.ts +23 -8
  273. package/src/components/scroll-container/scroll-container.component.ts +19 -1
  274. package/src/components/scroll-container/scroll-container.scss +6 -1
  275. package/src/components/select/select.component.ts +52 -36
  276. package/src/components/select/select.scss +42 -25
  277. package/src/components/select/select.test.ts +14 -2
  278. package/src/components/settings-container/settings-container.scss +18 -5
  279. package/src/components/sidebar/sidebar.component.ts +8 -1
  280. package/src/components/sidebar/sidebar.scss +6 -1
  281. package/src/components/skeleton/skeleton.component.ts +3 -3
  282. package/src/components/skeleton/skeleton.scss +6 -6
  283. package/src/components/skeleton/skeleton.test.ts +9 -0
  284. package/src/components/slash-menu/slash-menu.scss +1 -1
  285. package/src/components/slideout/slideout.component.ts +20 -3
  286. package/src/components/slideout/slideout.scss +13 -6
  287. package/src/components/slideout/slideout.test.ts +19 -0
  288. package/src/components/split-button/split-button.component.ts +1 -1
  289. package/src/components/split-pane/split-pane.component.ts +71 -8
  290. package/src/components/split-pane/split-pane.scss +145 -41
  291. package/src/components/split-pane/split-pane.test.ts +23 -3
  292. package/src/components/stat/stat.component.ts +209 -14
  293. package/src/components/stat/stat.scss +305 -5
  294. package/src/components/stat/stat.test.ts +143 -2
  295. package/src/components/status-indicator/status-indicator.scss +22 -21
  296. package/src/components/stepper/stepper.scss +3 -3
  297. package/src/components/tab/tab.component.ts +1 -0
  298. package/src/components/table/table.component.ts +12 -1
  299. package/src/components/table/table.scss +21 -13
  300. package/src/components/table/table.test.ts +17 -0
  301. package/src/components/tabs/tabs.component.ts +150 -37
  302. package/src/components/tabs/tabs.scss +48 -7
  303. package/src/components/tabs/tabs.test.ts +169 -0
  304. package/src/components/textarea/textarea.component.ts +25 -3
  305. package/src/components/textarea/textarea.scss +10 -0
  306. package/src/components/textarea/textarea.test.ts +8 -4
  307. package/src/components/theme-editor/theme-editor.scss +0 -6
  308. package/src/components/tile/tile.component.ts +32 -2
  309. package/src/components/tile/tile.scss +40 -7
  310. package/src/components/tile/tile.test.ts +10 -0
  311. package/src/components/toggle/toggle.component.ts +23 -7
  312. package/src/components/toggle/toggle.scss +36 -29
  313. package/src/components/toggle/toggle.test.ts +17 -0
  314. package/src/components/tooltip/tooltip.component.ts +3 -0
  315. package/src/components/tooltip/tooltip.scss +11 -6
  316. package/src/components/tooltip/tooltip.test.ts +12 -0
  317. package/src/components/translation-field/index.ts +12 -0
  318. package/src/components/translation-field/translation-field.component.ts +55 -0
  319. package/src/components/translation-field/translation-field.scss +101 -0
  320. package/src/components/translation-fields/index.ts +12 -0
  321. package/src/components/translation-fields/translation-fields.component.ts +80 -0
  322. package/src/components/translation-fields/translation-fields.scss +60 -0
  323. package/src/components/translation-grid/index.ts +12 -0
  324. package/src/components/translation-grid/translation-grid.component.ts +157 -0
  325. package/src/components/translation-grid/translation-grid.scss +138 -0
  326. package/src/components/translation-grid/translation-grid.test.ts +28 -0
  327. package/src/components/vertical-stepper/vertical-stepper.component.ts +35 -9
  328. package/src/components/vertical-stepper/vertical-stepper.scss +110 -39
  329. package/src/components/vertical-stepper/vertical-stepper.test.ts +35 -0
  330. package/src/components/well/well.scss +2 -2
  331. package/src/events/events.ts +2 -0
  332. package/src/events/zn-chart-click.ts +13 -0
  333. package/src/events/zn-report-change.ts +7 -0
  334. package/src/form-control.scss +36 -3
  335. package/src/internal/anchored-popover.ts +175 -0
  336. package/src/internal/animate.ts +19 -5
  337. package/src/internal/cosmic-border.ts +15 -0
  338. package/src/internal/form.ts +1 -1
  339. package/src/internal/hover-intent.ts +15 -0
  340. package/src/internal/input-modality.ts +15 -0
  341. package/src/internal/pointer-intent.ts +37 -0
  342. package/src/internal/reorder.ts +625 -0
  343. package/src/internal/scroll.ts +51 -3
  344. package/src/internal/selection-card.ts +1 -1
  345. package/src/internal/tab-indicator.ts +92 -0
  346. package/src/internal/table-sort.ts +25 -0
  347. package/src/internal/table-tabs.ts +108 -0
  348. package/src/internal/test-drag.ts +35 -0
  349. package/src/internal/theme-openbyte.test.ts +126 -0
  350. package/src/internal/theme.ts +4 -4
  351. package/src/internal/zinc-element.ts +2 -2
  352. package/src/selection-card.scss +25 -0
  353. package/src/types/web-test-runner-commands.d.ts +1 -0
  354. package/src/utilities/color.test.ts +43 -0
  355. package/src/utilities/color.ts +189 -0
  356. package/src/zinc.ts +14 -0
  357. package/.github/workflows/eleventy_build.yml +0 -42
  358. package/scss/themes/_aura.scss +0 -114
@@ -0,0 +1,80 @@
1
+ @use "../../wc";
2
+
3
+ :host {
4
+ display: inline-flex;
5
+ vertical-align: middle;
6
+ }
7
+
8
+ .badge {
9
+ --badge-fg: var(--zn-color-neutral-fg);
10
+ --badge-bg: var(--zn-color-neutral-bg);
11
+ --badge-line: var(--zn-color-neutral-line);
12
+ --badge-solid: var(--zn-color-neutral-solid);
13
+ --badge-ink: 255, 255, 255;
14
+
15
+ box-sizing: border-box;
16
+ display: inline-flex;
17
+ align-items: center;
18
+ gap: var(--zn-badge-gap, 4.8px);
19
+ height: var(--zn-badge-height, 20px);
20
+ padding-inline: var(--zn-badge-padding-inline, 7.2px);
21
+ font-family: var(--zn-font-family-sans);
22
+ font-size: var(--zn-badge-font-size, 11px);
23
+ font-weight: var(--zn-font-weight-semibold);
24
+ line-height: 1;
25
+ white-space: nowrap;
26
+ color: var(--badge-fg);
27
+ background: var(--badge-bg);
28
+ border: 1px solid var(--badge-line);
29
+ border-radius: var(--zn-badge-border-radius, var(--zn-border-radius));
30
+ }
31
+
32
+ .badge--large {
33
+ height: var(--zn-badge-large-height, 24px);
34
+ padding-inline: var(--zn-badge-large-padding-inline, 9.6px);
35
+ font-size: var(--zn-badge-large-font-size, 12px);
36
+ }
37
+
38
+ @each $type in (success, warning, danger, info, ai) {
39
+ .badge--#{$type} {
40
+ --badge-fg: var(--zn-color-#{$type}-fg);
41
+ --badge-bg: var(--zn-color-#{$type}-bg);
42
+ --badge-line: var(--zn-color-#{$type}-line);
43
+ --badge-solid: var(--zn-color-#{$type}-solid);
44
+ }
45
+ }
46
+
47
+ .badge--warning {
48
+ --badge-ink: var(--zn-color-on-warning, 255, 255, 255);
49
+ }
50
+
51
+ .badge--ai {
52
+ --badge-ink: var(--zn-color-on-ai, 255, 255, 255);
53
+ }
54
+
55
+ .badge--solid {
56
+ color: rgb(var(--badge-ink));
57
+ background: var(--badge-solid);
58
+ border-color: var(--badge-solid);
59
+ }
60
+
61
+ .badge--dot::before {
62
+ content: "";
63
+ flex: none;
64
+ width: 6.4px;
65
+ height: 6.4px;
66
+ border-radius: 50%;
67
+ background: var(--badge-solid);
68
+ }
69
+
70
+ .badge--solid.badge--dot::before {
71
+ background: currentColor;
72
+ }
73
+
74
+ .badge__icon {
75
+ flex: none;
76
+
77
+ &::part(icon) {
78
+ stroke-width: 1.75;
79
+ }
80
+ }
@@ -0,0 +1,34 @@
1
+ import '../../../dist/zn.min.js';
2
+ import { expect, fixture, html } from '@open-wc/testing';
3
+
4
+ const base = (el: HTMLElement) => el.shadowRoot!.querySelector<HTMLElement>('[part="base"]')!;
5
+
6
+ describe('<zn-badge>', () => {
7
+ it('should render a component', async () => {
8
+ const el = await fixture(html` <zn-badge></zn-badge> `);
9
+
10
+ expect(el).to.exist;
11
+ });
12
+
13
+ it('applies the type, size, solid and dot classes', async () => {
14
+ const el = await fixture<HTMLElement>(html`<zn-badge type="success" size="large" solid dot>Large</zn-badge>`);
15
+
16
+ expect([...base(el).classList]).to.include.members(
17
+ ['badge--success', 'badge--large', 'badge--solid', 'badge--dot']);
18
+ });
19
+
20
+ it('shows the sparkle icon only on ai badges', async () => {
21
+ const ai = await fixture<HTMLElement>(html`<zn-badge type="ai">AI suggested</zn-badge>`);
22
+ const info = await fixture<HTMLElement>(html`<zn-badge type="info">Trial</zn-badge>`);
23
+
24
+ expect(ai.shadowRoot!.querySelector('zn-icon')).to.exist;
25
+ expect(info.shadowRoot!.querySelector('zn-icon')).to.not.exist;
26
+ });
27
+
28
+ it('fills a solid badge with its type colour', async () => {
29
+ const el = await fixture<HTMLElement>(html`
30
+ <zn-badge type="danger" solid style="--zn-color-danger-solid: rgb(217, 46, 75)">Urgent</zn-badge>`);
31
+
32
+ expect(getComputedStyle(base(el)).backgroundColor).to.equal('rgb(217, 46, 75)');
33
+ });
34
+ });
@@ -0,0 +1,12 @@
1
+ import ZnBadge from './badge.component';
2
+
3
+ export * from './badge.component';
4
+ export default ZnBadge;
5
+
6
+ ZnBadge.define('zn-badge');
7
+
8
+ declare global {
9
+ interface HTMLElementTagNameMap {
10
+ 'zn-badge': ZnBadge;
11
+ }
12
+ }
@@ -147,6 +147,7 @@ export default class ZnBulkActions extends ZincElement {
147
147
  icon="delete"
148
148
  icon-size="24"
149
149
  color="transparent"
150
+ label="Remove rule"
150
151
  @click="${(e: Event) => this._removeRule(uniqueId, e)}">
151
152
  </zn-button>`;
152
153
 
@@ -260,21 +261,32 @@ export default class ZnBulkActions extends ZincElement {
260
261
  return position;
261
262
  }
262
263
 
263
- private _changeRule(id: string, event: ZnChangeEvent) {
264
+ private async _changeRule(id: string, event: ZnChangeEvent) {
264
265
  // remove the element from the dom
265
266
  const pos: number = this._getRulePosition(id);
266
267
  const select = event.target as ZnSelect;
268
+ const hadFocus = select.matches(':focus-within');
267
269
  select.popup.active = false;
268
270
  select?.parentElement?.remove();
269
271
  // recreate the element based on the selected value;
270
272
  this._removeRule(id, event as unknown as Event);
271
273
  this._addRule(event as unknown as Event, '', pos);
274
+
275
+ const replacement = this.container.children[pos]?.querySelector<ZnSelect>('.query-builder__key');
276
+ if (hadFocus && replacement) {
277
+ await replacement.updateComplete;
278
+ replacement.focus();
279
+ }
272
280
  }
273
281
 
274
282
  private _removeRule(id: string, event: Event) {
275
283
  this._selectedRules.delete(id);
276
284
  const button = event.target as ZnButton;
277
- button?.closest('.query-builder__row')?.remove();
285
+ const row = button?.closest('.query-builder__row');
286
+ const hadFocus = row?.matches(':focus-within');
287
+ const neighbour = row?.nextElementSibling?.matches('.query-builder__row') ? row.nextElementSibling : row?.previousElementSibling;
288
+ row?.remove();
289
+ if (hadFocus) (neighbour?.querySelector<ZnButton>('.query-builder__remove')?.button ?? this.addRule).focus();
278
290
  this._handleChange();
279
291
  }
280
292
 
@@ -4,7 +4,7 @@ import {FormControlController, validValidityState} from "../../internal/form";
4
4
  import {HasSlotController} from '../../internal/slot';
5
5
  import {html, literal} from 'lit/static-html.js';
6
6
  import {ifDefined} from 'lit/directives/if-defined.js';
7
- import {property, query, queryAll} from 'lit/decorators.js';
7
+ import {property, query, queryAll, state} from 'lit/decorators.js';
8
8
  import {styleMap} from "lit/directives/style-map.js";
9
9
  import {watch} from '../../internal/watch';
10
10
  import ZincElement from '../../internal/zinc-element';
@@ -12,6 +12,7 @@ import ZnDropdown from "../dropdown";
12
12
  import ZnIcon from "../icon";
13
13
  import ZnTooltip from "../tooltip";
14
14
  import type {IconColor, IconLibrary} from "../icon";
15
+ import type ZnMenu from "../menu";
15
16
  import type {ZincFormControl} from '../../internal/zinc-element';
16
17
 
17
18
  import sharedStyles from '../../../scss/shared/index.scss';
@@ -23,6 +24,7 @@ import styles from './button.scss';
23
24
  * @status stable
24
25
  * @since 2.0
25
26
  *
27
+ * @dependency zn-dropdown
26
28
  * @dependency zn-icon
27
29
  * @dependency zn-tooltip
28
30
  *
@@ -34,6 +36,7 @@ import styles from './button.scss';
34
36
  * @slot prefix - A presentational prefix icon or similar element.
35
37
  * @slot suffix - A presentational suffix icon or similar element.
36
38
  * @slot cancel - Slot for custom cancel button/content when autoClick is active.
39
+ * @slot menu - A `<zn-menu>` of advanced options, opened by a long press, a right click, or ArrowDown on the button.
37
40
  *
38
41
  * @csspart base - The component's base wrapper.
39
42
  * @csspart prefix - The container that wraps the prefix.
@@ -45,26 +48,43 @@ import styles from './button.scss';
45
48
  export default class ZnButton extends ZincElement implements ZincFormControl {
46
49
  static styles: CSSResultGroup = [unsafeCSS(sharedStyles), unsafeCSS(styles)];
47
50
  static dependencies = {
51
+ 'zn-dropdown': ZnDropdown,
48
52
  'zn-tooltip': ZnTooltip,
49
53
  'zn-icon': ZnIcon
50
54
  };
51
55
 
52
56
  private readonly formControlController = new FormControlController(this);
53
- private readonly hasSlotController = new HasSlotController(this, '[default]');
57
+ private readonly hasSlotController = new HasSlotController(this, '[default]', 'menu');
54
58
  private _autoClickTimeout: number | undefined;
55
59
  private _loadingState = {
56
60
  countdown: null as number | null,
57
61
  interval: undefined as number | undefined,
58
62
  };
59
63
 
64
+ // Long press menu
65
+ private _longPressTimeout: number | undefined;
66
+ private _longPressOrigin: { x: number, y: number } | null = null;
67
+ /** The pointer that opened the menu is still down, so releasing it over an item selects that item. */
68
+ private _menuPointerHeld = false;
69
+ /** Swallows the click that ends a long press, so the button's own action does not run. */
70
+ private _suppressClick = false;
71
+ private _menuHadFocus = false;
72
+
73
+ @state() private menuOpen = false;
74
+
60
75
  @query('.button') button: HTMLButtonElement;
76
+ @query('.button__menu') menuDropdown: ZnDropdown;
61
77
  @queryAll('.loading-countdown') countdownContainer: HTMLElement[];
62
78
 
63
- @property({}) color: 'default' | 'primary' | 'secondary' | 'error' | 'info' | 'success' | 'warning' |
79
+ /** `ai` is reserved for actions that invoke or act on AI-generated content. */
80
+ @property({}) color: 'default' | 'primary' | 'secondary' | 'error' | 'info' | 'success' | 'warning' | 'ai' |
64
81
  'transparent' | 'star' | (string & Record<never, never>) = 'default';
82
+ @property() size: 'small' | 'medium' | 'large' = 'medium';
65
83
  @property({attribute: 'hover-color'}) hoverColor: string;
66
84
  @property({type: Boolean}) text = false;
67
85
  @property({type: Boolean}) outline = false;
86
+ /** Keeps the filled look where a container forces `text`, such as a page header's action menu. */
87
+ @property({type: Boolean}) solid = false;
68
88
  @property({type: Boolean}) disabled = false;
69
89
  @property({type: Boolean}) grow = false;
70
90
  @property({type: Boolean}) square = false;
@@ -89,6 +109,13 @@ export default class ZnButton extends ZincElement implements ZincFormControl {
89
109
  @property({type: Boolean, reflect: true}) pill = false;
90
110
  @property({type: Boolean, attribute: 'panel-bg'}) panelBackground = false;
91
111
 
112
+ /** How long, in milliseconds, the button must be held to open the `menu` slot. */
113
+ @property({type: Number, attribute: 'long-press-delay'}) longPressDelay = 500;
114
+
115
+ /** Where the `menu` slot opens relative to the button. */
116
+ @property({attribute: 'menu-placement'}) menuPlacement: 'top' | 'top-start' | 'top-end' | 'right' | 'right-start' |
117
+ 'right-end' | 'bottom' | 'bottom-start' | 'bottom-end' | 'left' | 'left-start' | 'left-end' = 'bottom-start';
118
+
92
119
  @property({attribute: 'dropdown-closer', type: Boolean}) dropdownCloser = false;
93
120
 
94
121
  @property({type: Number}) notification: number;
@@ -131,6 +158,12 @@ export default class ZnButton extends ZincElement implements ZincFormControl {
131
158
  /** Toggle state, forwarded to the inner button's `aria-pressed`. */
132
159
  @property({attribute: 'aria-pressed'}) pressed: 'true' | 'false' | 'mixed';
133
160
 
161
+ /** Popup state, forwarded to the inner button's `aria-expanded`. */
162
+ @property({attribute: 'aria-expanded'}) expanded: 'true' | 'false';
163
+
164
+ /** Popup type, forwarded to the inner button's `aria-haspopup`. */
165
+ @property({attribute: 'aria-haspopup'}) hasPopup: string;
166
+
134
167
  // Auto Click Specific
135
168
  @property({type: Boolean, attribute: 'auto-click'}) autoClick = false;
136
169
  @property({type: Number, attribute: 'auto-click-delay'}) autoClickDelay = 2000;
@@ -169,9 +202,16 @@ export default class ZnButton extends ZincElement implements ZincFormControl {
169
202
  }
170
203
  }
171
204
 
205
+ connectedCallback() {
206
+ super.connectedCallback();
207
+ this.addEventListener('click', this.handleHostClickCapture, {capture: true});
208
+ }
209
+
172
210
  disconnectedCallback() {
173
211
  super.disconnectedCallback();
174
212
  this.teardownAutoClick();
213
+ this.cancelLongPress();
214
+ this.removeEventListener('click', this.handleHostClickCapture, {capture: true});
175
215
  }
176
216
 
177
217
  @watch('autoClick')
@@ -270,13 +310,180 @@ export default class ZnButton extends ZincElement implements ZincFormControl {
270
310
  dropdown?.hide();
271
311
  }
272
312
 
313
+ // Passing the button through carries its name, value and formaction onto the submission.
273
314
  if (this.type === 'submit') {
274
- this.formControlController.submit();
315
+ this.formControlController.submit(this);
275
316
  }
276
317
 
277
318
  if (this.type === 'reset') {
278
- this.formControlController.reset();
319
+ this.formControlController.reset(this);
320
+ }
321
+ }
322
+
323
+ private _hasMenu() {
324
+ return this.hasSlotController.test('menu');
325
+ }
326
+
327
+ private getMenu() {
328
+ return this.querySelector<ZnMenu>(':scope > zn-menu[slot="menu"], :scope > [slot="menu"] zn-menu');
329
+ }
330
+
331
+ /** Opens the advanced options in the `menu` slot. */
332
+ async showMenu() {
333
+ if (!this._hasMenu() || this.disabled || this.processing) return;
334
+ await this.updateComplete;
335
+ await this.menuDropdown?.show();
336
+ }
337
+
338
+ /** Closes the advanced options in the `menu` slot. */
339
+ async hideMenu() {
340
+ await this.menuDropdown?.hide();
341
+ }
342
+
343
+ // Runs before any click listener on the button, including the consumer's own
344
+ private handleHostClickCapture = (event: MouseEvent) => {
345
+ if (!this._suppressClick) return;
346
+
347
+ const menuSlot = this.shadowRoot?.querySelector('slot[name="menu"]');
348
+ const fromMenu = event.composedPath().some(el => (el as Element).assignedSlot === menuSlot);
349
+ if (fromMenu) return;
350
+
351
+ this._suppressClick = false;
352
+ event.preventDefault();
353
+ event.stopImmediatePropagation();
354
+ }
355
+
356
+ private handleMenuPointerDown = (event: PointerEvent) => {
357
+ if (!this._hasMenu() || this.disabled || this.processing || event.button !== 0) return;
358
+
359
+ // Pressing the button while its menu is open only closes the menu, which the dropdown does on mousedown
360
+ if (this.menuOpen) {
361
+ this._suppressClick = true;
362
+ document.addEventListener('pointerup', this.handleMenuPointerUp);
363
+ return;
364
+ }
365
+
366
+ this.cancelLongPress();
367
+ this._longPressOrigin = {x: event.clientX, y: event.clientY};
368
+ this._longPressTimeout = window.setTimeout(() => {
369
+ this._longPressTimeout = undefined;
370
+ this._suppressClick = true;
371
+ this._menuPointerHeld = true;
372
+ this.showMenu().then(r => r);
373
+ }, this.longPressDelay);
374
+
375
+ document.addEventListener('pointermove', this.handleMenuPointerMove);
376
+ document.addEventListener('pointerup', this.handleMenuPointerUp);
377
+ document.addEventListener('pointercancel', this.handleMenuPointerCancel);
378
+ }
379
+
380
+ private handleMenuPointerMove = (event: PointerEvent) => {
381
+ if (this._longPressTimeout === undefined || !this._longPressOrigin) return;
382
+
383
+ const moved = Math.hypot(event.clientX - this._longPressOrigin.x, event.clientY - this._longPressOrigin.y);
384
+ if (moved > 8) {
385
+ this.cancelLongPress();
386
+ }
387
+ }
388
+
389
+ private handleMenuPointerUp = (event: PointerEvent) => {
390
+ const held = this._menuPointerHeld;
391
+ this.cancelLongPress();
392
+
393
+ // Press, drag onto an item and release selects it, as in the macOS Dock
394
+ if (held) {
395
+ const item = document.elementFromPoint(event.clientX, event.clientY)?.closest('zn-menu-item');
396
+ if (item && this.contains(item) && !item.hasAttribute('disabled')) {
397
+ (item as HTMLElement).click();
398
+ }
399
+ }
400
+
401
+ // A long press on touch ends without a click, so the flag must not outlive this gesture
402
+ if (this._suppressClick) {
403
+ window.setTimeout(() => this._suppressClick = false, 300);
404
+ }
405
+ }
406
+
407
+ private handleMenuPointerCancel = () => {
408
+ this.cancelLongPress();
409
+ this._suppressClick = false;
410
+ }
411
+
412
+ private cancelLongPress() {
413
+ window.clearTimeout(this._longPressTimeout);
414
+ this._longPressTimeout = undefined;
415
+ this._longPressOrigin = null;
416
+ this._menuPointerHeld = false;
417
+ document.removeEventListener('pointermove', this.handleMenuPointerMove);
418
+ document.removeEventListener('pointerup', this.handleMenuPointerUp);
419
+ document.removeEventListener('pointercancel', this.handleMenuPointerCancel);
420
+ }
421
+
422
+ private handleMenuContextMenu = (event: MouseEvent) => {
423
+ if (!this._hasMenu()) return;
424
+
425
+ event.preventDefault();
426
+ // A touch long press also fires contextmenu, after the menu has already opened
427
+ if (!this.menuOpen) {
428
+ this.cancelLongPress();
429
+ this.showMenu().then(r => r);
430
+ }
431
+ }
432
+
433
+ private handleMenuKeyDown = async (event: KeyboardEvent) => {
434
+ if (!this._hasMenu()) return;
435
+
436
+ const opensMenu = event.key === 'ArrowDown' || event.key === 'ContextMenu' || (event.key === 'F10' && event.shiftKey);
437
+ if (!opensMenu) return;
438
+
439
+ event.preventDefault();
440
+ await this.showMenu();
441
+
442
+ const menu = this.getMenu();
443
+ const first = menu?.getAllItems()[0];
444
+ if (menu && first) {
445
+ menu.setCurrentItem(first);
446
+ first.focus();
447
+ }
448
+ }
449
+
450
+ private handleMenuShow = (event: Event) => {
451
+ if (event.target !== this.menuDropdown) return;
452
+ this.menuOpen = true;
453
+ }
454
+
455
+ private handleMenuHide = (event: Event) => {
456
+ if (event.target !== this.menuDropdown) return;
457
+ this.menuOpen = false;
458
+ this._menuHadFocus = !!this.getMenu()?.matches(':focus-within');
459
+ }
460
+
461
+ // The dropdown's anchor is not focusable, so focus returns to the button itself
462
+ private handleMenuAfterHide = (event: Event) => {
463
+ if (event.target !== this.menuDropdown) return;
464
+ if (this._menuHadFocus) {
465
+ this._menuHadFocus = false;
466
+ this.button?.focus();
467
+ }
468
+ }
469
+
470
+ private _renderMenu() {
471
+ if (!this._hasMenu()) {
472
+ // Kept in the tree so adding a menu later fires slotchange
473
+ return html`<slot name="menu" hidden></slot>`;
279
474
  }
475
+
476
+ return html`
477
+ <zn-dropdown class="button__menu"
478
+ placement=${this.menuPlacement}
479
+ @zn-show=${this.handleMenuShow}
480
+ @zn-hide=${this.handleMenuHide}
481
+ @zn-after-hide=${this.handleMenuAfterHide}>
482
+ <span slot="trigger" class="button__menu-anchor"></span>
483
+ <div class="button__menu-panel">
484
+ <slot name="menu"></slot>
485
+ </div>
486
+ </zn-dropdown>`;
280
487
  }
281
488
 
282
489
  private _isLink() {
@@ -305,6 +512,8 @@ export default class ZnButton extends ZincElement implements ZincFormControl {
305
512
  return 'rgb(var(--zn-color-success))';
306
513
  case 'warning':
307
514
  return 'rgb(var(--zn-color-warning))';
515
+ case 'ai':
516
+ return 'var(--zn-color-ai-fg)';
308
517
  case 'transparent':
309
518
  return 'inherit';
310
519
  case 'star':
@@ -382,6 +591,7 @@ export default class ZnButton extends ZincElement implements ZincFormControl {
382
591
 
383
592
  protected render(): unknown {
384
593
  const isLink = this._isLink();
594
+ const hasMenu = this._hasMenu();
385
595
  const showCancel = this.loading && this.autoClick;
386
596
  const resolvedIconSize = this.iconSize ? this.iconSize : 20;
387
597
  const icon = this.icon && !this.loading && !this.processing ? html`
@@ -417,10 +627,13 @@ export default class ZnButton extends ZincElement implements ZincFormControl {
417
627
  'button--info': this.color === 'info',
418
628
  'button--success': this.color === 'success',
419
629
  'button--warning': this.color === 'warning',
630
+ 'button--ai': this.color === 'ai',
631
+ 'button--small': this.size === 'small',
632
+ 'button--large': this.size === 'large',
420
633
  'button--transparent': this.color === 'transparent',
421
634
  'button--star': this.color === 'star',
422
635
  'button--outline': this.outline,
423
- 'button--text': (this.text && !this.outline),
636
+ 'button--text': (this.text && !this.outline && !this.solid),
424
637
  'button--icon-button': !!this.iconButton,
425
638
  'button--plain': !!this.iconButton && this.plain,
426
639
  'button--pill': this.pill,
@@ -438,6 +651,7 @@ export default class ZnButton extends ZincElement implements ZincFormControl {
438
651
  'button--muted-notification': this.mutedNotifications || notification === -2,
439
652
  'button--notification-dot': notification !== undefined && notification < 0,
440
653
  'button--processing': this.processing,
654
+ 'button--has-menu': hasMenu,
441
655
  })}"
442
656
  type="${ifDefined(this.type)}"
443
657
  href="${ifDefined(this.href)}"
@@ -447,11 +661,17 @@ export default class ZnButton extends ZincElement implements ZincFormControl {
447
661
  gaid="${ifDefined(this.gaid)}"
448
662
  aria-label="${ifDefined(ariaLabel)}"
449
663
  aria-pressed="${ifDefined(isLink ? undefined : this.pressed)}"
664
+ aria-expanded="${ifDefined(this.expanded ?? (hasMenu ? String(this.menuOpen) : undefined))}"
665
+ aria-haspopup="${ifDefined(this.hasPopup ?? (hasMenu ? 'menu' : undefined))}"
666
+ draggable="${ifDefined(isLink && hasMenu ? 'false' : undefined)}"
450
667
  aria-busy="${this.processing ? 'true' : nothing}"
451
668
  data-notification="${ifDefined(notification)}"
452
669
  disabled="${this.disabled || nothing}"
453
670
  style=${styleMap(buttonStyles)}
454
- @click="${this.handleClick}">
671
+ @click="${this.handleClick}"
672
+ @pointerdown="${this.handleMenuPointerDown}"
673
+ @contextmenu="${this.handleMenuContextMenu}"
674
+ @keydown="${this.handleMenuKeyDown}">
455
675
  ${this.iconPosition === 'left' ? icon : ''}
456
676
  ${this.processing ? html`<span part="spinner" class="button__spinner"></span>` : ''}
457
677
  ${this.processing && this.processingText ? html`<span class="button__processing-text">${this.processingText}</span>` : ''}
@@ -484,7 +704,7 @@ export default class ZnButton extends ZincElement implements ZincFormControl {
484
704
  </zn-tooltip>`;
485
705
  }
486
706
 
487
- return content;
707
+ return html`${content}${this._renderMenu()}`;
488
708
  }
489
709
 
490
710
  private _getLoadingContainer() {