@kubex/zinc 1.1.185 → 2.0.1

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 +10319 -2666
  6. package/dist/vscode.html-custom-data.json +1012 -57
  7. package/dist/web-types.json +2097 -145
  8. package/dist/zn.d.ts +2294 -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 +136 -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 +632 -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
@@ -64,6 +64,7 @@ export enum QueryBuilderOperators {
64
64
  NMatch = 'nmatch',
65
65
  Contains = 'contains',
66
66
  DoesNotContain = 'doesnotcontain',
67
+ DnContain = 'dncontain',
67
68
  Starts = 'starts',
68
69
  NStarts = 'nstarts',
69
70
  Ends = 'ends',
@@ -72,6 +73,8 @@ export enum QueryBuilderOperators {
72
73
  NWild = 'nwild',
73
74
  Like = 'like',
74
75
  NLike = 'nlike',
76
+ LikeIn = 'likein',
77
+ NLikeIn = 'nlikein',
75
78
  Fuzzy = 'fuzzy',
76
79
  NFuzzy = 'nfuzzy',
77
80
  Gte = 'gte',
@@ -97,6 +100,7 @@ export const operatorText: { [key in QueryBuilderOperators]: string } = {
97
100
  [QueryBuilderOperators.NMatch]: 'Does Not Match',
98
101
  [QueryBuilderOperators.Contains]: 'Contains',
99
102
  [QueryBuilderOperators.DoesNotContain]: 'Does Not Contain',
103
+ [QueryBuilderOperators.DnContain]: 'Does Not Contain',
100
104
  [QueryBuilderOperators.Starts]: 'Starts With',
101
105
  [QueryBuilderOperators.NStarts]: 'Does Not Start With',
102
106
  [QueryBuilderOperators.Ends]: 'Ends With',
@@ -105,6 +109,8 @@ export const operatorText: { [key in QueryBuilderOperators]: string } = {
105
109
  [QueryBuilderOperators.NWild]: 'Does Not Match Wildcard',
106
110
  [QueryBuilderOperators.Like]: 'Like Match With',
107
111
  [QueryBuilderOperators.NLike]: 'Does Not Like Match With',
112
+ [QueryBuilderOperators.LikeIn]: 'Like Any Of',
113
+ [QueryBuilderOperators.NLikeIn]: 'Not Like Any Of',
108
114
  [QueryBuilderOperators.Fuzzy]: 'Fuzzy Match With',
109
115
  [QueryBuilderOperators.NFuzzy]: 'Does Not Match Fuzzy With',
110
116
  [QueryBuilderOperators.Gte]: 'Greater Than or Equals',
@@ -113,6 +119,12 @@ export const operatorText: { [key in QueryBuilderOperators]: string } = {
113
119
  [QueryBuilderOperators.Lte]: 'Less Than or Equals',
114
120
  };
115
121
 
122
+ export interface QueryBuilderRule {
123
+ key: string;
124
+ comparator: string;
125
+ value: string | string[];
126
+ }
127
+
116
128
  export interface CreatedRule {
117
129
  id: string;
118
130
  name: string;
@@ -167,8 +179,15 @@ export default class ZnQueryBuilder extends ZincElement implements ZincFormContr
167
179
  }
168
180
  }) showValues: string[] = [];
169
181
 
182
+ /** Rules to start with, in the shape the builder submits. */
183
+ @property({type: Array}) rules: QueryBuilderRule[] = [];
184
+
185
+ /** Allows a filter to be added more than once, e.g. for a date range. */
186
+ @property({type: Boolean}) repeatable: boolean = false;
187
+
170
188
 
171
189
  private get _usedFilterIds(): Set<string> {
190
+ if (this.repeatable) return new Set();
172
191
  return new Set([...this._selectedRules.values()].map(rule => rule.id));
173
192
  }
174
193
 
@@ -187,6 +206,7 @@ export default class ZnQueryBuilder extends ZincElement implements ZincFormContr
187
206
  this._addRule(null, item);
188
207
  });
189
208
  }
209
+ this.rules?.forEach(rule => this._addRule(null, rule.key, undefined, rule));
190
210
  this._handleChange();
191
211
  this._formController.updateValidity();
192
212
  }
@@ -225,7 +245,7 @@ export default class ZnQueryBuilder extends ZincElement implements ZincFormContr
225
245
  this.emit("zn-change");
226
246
  }
227
247
 
228
- private _addRule(event: Event | null, value: string, pos?: number) {
248
+ private _addRule(event: Event | null, value: string, pos?: number, initial?: QueryBuilderRule) {
229
249
  const target = event?.target as ZnSelect;
230
250
  const id = value ? value : target.value;
231
251
  if (id === '') return;
@@ -236,11 +256,16 @@ export default class ZnQueryBuilder extends ZincElement implements ZincFormContr
236
256
 
237
257
  const uniqueId = Math.random().toString(36).substring(7);
238
258
 
259
+ const defaultOperator = filter.operators.length > 0 ? filter.operators[0] : QueryBuilderOperators.Eq;
260
+ const selectedComparator = initial && filter.operators.includes(initial.comparator as QueryBuilderOperators)
261
+ ? initial.comparator as QueryBuilderOperators
262
+ : defaultOperator;
263
+
239
264
  this._selectedRules.set(uniqueId, {
240
265
  id: filter.id,
241
266
  name: filter.name,
242
- operator: filter.operators.length > 0 ? filter.operators[0] : QueryBuilderOperators.Eq,
243
- value: ''
267
+ operator: selectedComparator,
268
+ value: (initial?.value ?? '') as string
244
269
  });
245
270
 
246
271
  const availableFilters = this.filters.filter(item => item.id === filter.id || !this._usedFilterIds.has(item.id));
@@ -271,7 +296,7 @@ export default class ZnQueryBuilder extends ZincElement implements ZincFormContr
271
296
  this._updateOperatorValue(uniqueId, e)
272
297
  this._changeValueInput(uniqueId, e, filter);
273
298
  }}"
274
- value="${filter.operators[0]}"
299
+ value="${selectedComparator}"
275
300
  ?disabled="${filter.operators.length === 1}">
276
301
  ${filter.operators.map((item: QueryBuilderOperators) => {
277
302
  return html`
@@ -285,13 +310,13 @@ export default class ZnQueryBuilder extends ZincElement implements ZincFormContr
285
310
  icon="delete"
286
311
  icon-size="24"
287
312
  color="transparent"
313
+ label="Remove rule"
288
314
  @click="${(e: Event) => this._removeRule(uniqueId, e)}">
289
315
  </zn-button>`;
290
316
 
291
- const selectedComparator = filter.operators.length > 0 ? filter.operators[0] : QueryBuilderOperators.Eq;
292
317
  const wrapper = html`
293
318
  <div class="query-builder__wrapper">
294
- ${this._createInput(filter, uniqueId, selectedComparator)}
319
+ ${this._createInput(filter, uniqueId, selectedComparator, initial?.value)}
295
320
  ${remove}
296
321
  </div>
297
322
  `;
@@ -315,7 +340,8 @@ export default class ZnQueryBuilder extends ZincElement implements ZincFormContr
315
340
  // Reset back to default placeholder
316
341
  this.addRule.value = '';
317
342
  this.addRule.displayLabel = '';
318
- this.addRule.selectedOptions[0].selected = false;
343
+ const selected = this.addRule.selectedOptions[0];
344
+ if (selected) selected.selected = false;
319
345
 
320
346
  // Auto scroll to keep the add rule select in view
321
347
  if (target === this.addRule && this.parentElement?.classList.contains('dropdown__query-builder')) {
@@ -328,7 +354,7 @@ export default class ZnQueryBuilder extends ZincElement implements ZincFormContr
328
354
  this._handleChange();
329
355
  }
330
356
 
331
- private _createInput(filter: QueryBuilderItem, uniqueId: string, selectedComparator: QueryBuilderOperators) {
357
+ private _createInput(filter: QueryBuilderItem, uniqueId: string, selectedComparator: QueryBuilderOperators, value?: string | string[]) {
332
358
  let input: ZnSelect | ZnInput | ZnDatepicker | null;
333
359
  switch (filter.type) {
334
360
  case 'bool':
@@ -353,9 +379,47 @@ export default class ZnQueryBuilder extends ZincElement implements ZincFormContr
353
379
  break;
354
380
  }
355
381
  }
382
+
383
+ if (input && value !== undefined && value !== '') {
384
+ input.value = this._inputValue(filter, selectedComparator, value);
385
+ }
356
386
  return input;
357
387
  }
358
388
 
389
+ private _inputValue(filter: QueryBuilderItem, operator: QueryBuilderOperators, value: string | string[]): string | string[] {
390
+ switch (filter.type) {
391
+ case 'bool':
392
+ case 'boolean':
393
+ return ['1', 'true'].includes(String(value).toLowerCase()) ? '1' : '0';
394
+ case 'date':
395
+ case 'dateTime': {
396
+ const date = this._submittedDate(String(value), operator, filter.dateSubmitFormat);
397
+ const pad = (n: number) => String(n).padStart(2, '0');
398
+ return `${pad(date.getMonth() + 1)}/${pad(date.getDate())}/${date.getFullYear()}`;
399
+ }
400
+ default:
401
+ if (filter.options && (operator === QueryBuilderOperators.In || operator === QueryBuilderOperators.Nin)) {
402
+ return Array.isArray(value) ? value.map(String) : String(value).split(',');
403
+ }
404
+ return Array.isArray(value) ? value.join(',') : String(value);
405
+ }
406
+ }
407
+
408
+ /** Reverses _updateDateValue, so a submitted date value can be shown again. */
409
+ private _submittedDate(value: string, operator: QueryBuilderOperators, submitFormat: QueryBuilderDateSubmitFormat = 'legacy'): Date {
410
+ switch (submitFormat) {
411
+ case 'timestamp':
412
+ return new Date(Number(value));
413
+ case 'iso':
414
+ return new Date(value);
415
+ default:
416
+ if (operator === QueryBuilderOperators.Eq || operator === QueryBuilderOperators.Neq) {
417
+ return new Date(Number(value) * 1000);
418
+ }
419
+ return new Date(Date.now() - Math.abs(Number(value)) * 60_000);
420
+ }
421
+ }
422
+
359
423
  private _changeValueInput(uniqueId: string, changeEvent: ZnChangeEvent, filter: QueryBuilderItem) {
360
424
  // Only comparisons with options need to change input
361
425
  if (!filter.options) return;
@@ -519,15 +583,22 @@ export default class ZnQueryBuilder extends ZincElement implements ZincFormContr
519
583
  this._handleChange();
520
584
  }
521
585
 
522
- private _changeRule(id: string, event: ZnChangeEvent) {
586
+ private async _changeRule(id: string, event: ZnChangeEvent) {
523
587
  // remove the element from the dom
524
588
  const pos: number = this._getRulePosition(id);
525
589
  const select = event.target as ZnSelect;
590
+ const hadFocus = select.matches(':focus-within');
526
591
  select.popup.active = false;
527
592
  select?.parentElement?.remove();
528
593
  // recreate the element based on the selected value;
529
594
  this._removeRule(id, event);
530
595
  this._addRule(event, '', pos);
596
+
597
+ const replacement = this.container.children[pos]?.querySelector<ZnSelect>('.query-builder__key');
598
+ if (hadFocus && replacement) {
599
+ await replacement.updateComplete;
600
+ replacement.focus();
601
+ }
531
602
  }
532
603
 
533
604
  private _getRulePosition(id: string): number {
@@ -544,7 +615,11 @@ export default class ZnQueryBuilder extends ZincElement implements ZincFormContr
544
615
  private _removeRule(id: string, event: Event) {
545
616
  this._selectedRules.delete(id);
546
617
  const button = event.target as ZnButton;
547
- button?.closest('.query-builder__row')?.remove();
618
+ const row = button?.closest('.query-builder__row');
619
+ const hadFocus = row?.matches(':focus-within');
620
+ const neighbour = row?.nextElementSibling?.matches('.query-builder__row') ? row.nextElementSibling : row?.previousElementSibling;
621
+ row?.remove();
622
+ if (hadFocus) (neighbour?.querySelector<ZnButton>('.query-builder__remove')?.button ?? this.addRule).focus();
548
623
  this._handleChange();
549
624
  }
550
625
 
@@ -35,9 +35,15 @@
35
35
  padding-bottom: 10px;
36
36
  }
37
37
 
38
+ // A row's parts would otherwise all shrink from their bases, leaving the selects only as wide as their arrow
38
39
  &__key,
39
40
  &__comparator {
40
- flex-basis: 60%;
41
+ flex: 0 1 220px;
42
+ min-width: 140px;
43
+ }
44
+
45
+ &__row > .query-builder__wrapper {
46
+ flex: 1 1 200px;
41
47
  }
42
48
 
43
49
  &__value {
@@ -1,5 +1,14 @@
1
1
  import '../../../dist/zn.min.js';
2
2
  import { expect, fixture, html } from '@open-wc/testing';
3
+ import type ZnQueryBuilder from './query-builder.component';
4
+
5
+ const filters = [
6
+ {id: 'email', name: 'Email', operators: ['eq', 'starts']},
7
+ {id: 'vip', name: 'VIP', type: 'bool', operators: ['eq']},
8
+ {id: 'country', name: 'Country', options: {GB: 'United Kingdom', US: 'United States'}, operators: ['eq', 'in']},
9
+ ];
10
+
11
+ const submitted = (el: ZnQueryBuilder) => JSON.parse(atob(String(el.value))) as object[];
3
12
 
4
13
  describe('<zn-query-builder>', () => {
5
14
  it('should render a component', async () => {
@@ -7,4 +16,35 @@ describe('<zn-query-builder>', () => {
7
16
 
8
17
  expect(el).to.exist;
9
18
  });
19
+
20
+ it('starts with the given rules and submits them unchanged', async () => {
21
+ const rules = [
22
+ {key: 'email', comparator: 'starts', value: 'kyle'},
23
+ {key: 'vip', comparator: 'eq', value: '1'},
24
+ {key: 'country', comparator: 'in', value: ['GB', 'US']},
25
+ ];
26
+ const el = await fixture<ZnQueryBuilder>(html`
27
+ <zn-query-builder .filters=${filters} .rules=${rules}></zn-query-builder>`);
28
+
29
+ expect(submitted(el)).to.deep.equal(rules);
30
+ const rows = el.shadowRoot!.querySelectorAll('.query-builder__row');
31
+ expect(rows.length).to.equal(3);
32
+ expect((rows[0].querySelector('.query-builder__comparator') as HTMLInputElement).value).to.equal('starts');
33
+ expect((rows[0].querySelector('.query-builder__value') as HTMLInputElement).value).to.equal('kyle');
34
+ expect((rows[2].querySelector('.query-builder__value') as HTMLInputElement).value).to.deep.equal(['GB', 'US']);
35
+ });
36
+
37
+ it('only offers a filter more than once when repeatable', async () => {
38
+ const rules = [{key: 'email', comparator: 'eq', value: 'a'}];
39
+ const options = (el: ZnQueryBuilder) =>
40
+ [...el.shadowRoot!.querySelectorAll('.add-rule zn-option')].map(o => o.getAttribute('value'));
41
+
42
+ const single = await fixture<ZnQueryBuilder>(html`
43
+ <zn-query-builder .filters=${filters} .rules=${rules}></zn-query-builder>`);
44
+ expect(options(single)).to.not.include('email');
45
+
46
+ const repeatable = await fixture<ZnQueryBuilder>(html`
47
+ <zn-query-builder repeatable .filters=${filters} .rules=${rules}></zn-query-builder>`);
48
+ expect(options(repeatable)).to.include('email');
49
+ });
10
50
  });
@@ -1,3 +1,4 @@
1
+ import {animateTo, EASE_OUT} from "../../internal/animate";
1
2
  import {classMap} from "lit/directives/class-map.js";
2
3
  import {type CSSResultGroup, html, unsafeCSS} from 'lit';
3
4
  import {defaultValue} from "../../internal/default-value";
@@ -5,6 +6,7 @@ import {FormControlController} from "../../internal/form";
5
6
  import {HasSlotController} from "../../internal/slot";
6
7
  import {ifDefined} from "lit/directives/if-defined.js";
7
8
  import {live} from "lit/directives/live.js";
9
+ import {PointerIntentController} from "../../internal/pointer-intent";
8
10
  import {property, query, state} from 'lit/decorators.js';
9
11
  import {watch} from '../../internal/watch';
10
12
  import ZincElement from '../../internal/zinc-element';
@@ -38,7 +40,7 @@ import styles from './radio.scss';
38
40
  * @csspart base - The component's base wrapper.
39
41
  * @csspart control - The square container that wraps the radio's checked state.
40
42
  * @csspart control--checked - Matches the control part when the radio is checked.
41
- * @csspart checked-icon - The checked icon, an `<zn-icon>` element.
43
+ * @csspart checked-icon - The checked dot, a `<span>` element.
42
44
  * @csspart image-container - The wrapper around the built-in image or image slot.
43
45
  * @csspart image - The built-in image.
44
46
  * @csspart card-title - The title inside a selection card.
@@ -68,6 +70,9 @@ export default class ZnRadio extends ZincElement implements ZincFormControl {
68
70
  private readonly hasSlotController = new HasSlotController(this, '[default]', 'description', 'image');
69
71
 
70
72
  @query('input[type="radio"]') input: HTMLInputElement;
73
+ @query('.radio__control') control: HTMLElement;
74
+
75
+ private readonly pointerIntent = new PointerIntentController(this);
71
76
 
72
77
  @state() private hasFocus = false;
73
78
 
@@ -97,6 +102,9 @@ export default class ZnRadio extends ZincElement implements ZincFormControl {
97
102
  /** Squares off the corners of the container drawn by `contained`, which is rounded by default. */
98
103
  @property({type: Boolean, reflect: true}) square = false;
99
104
 
105
+ /** Centres the text of a contained radio that has no image or `card-title`. */
106
+ @property({type: Boolean, reflect: true}) centered = false;
107
+
100
108
  /** URL for the image shown in a contained radio. */
101
109
  @property() src = '';
102
110
 
@@ -107,14 +115,20 @@ export default class ZnRadio extends ZincElement implements ZincFormControl {
107
115
  @property({attribute: 'image-position', reflect: true}) imagePosition: SelectionCardImagePosition = 'left';
108
116
 
109
117
  /**
110
- * Places the radio indicator within a contained card. Use `none` to hide the indicator so the card itself
111
- * shows the selected state. Requires `contained`.
118
+ * Places the radio indicator within a contained card. A contained radio without an image or `card-title` hides it
119
+ * at `start`, as does `none`. Requires `contained`.
112
120
  */
113
121
  @property({attribute: 'control-position', reflect: true}) controlPosition: SelectionCardControlPosition = 'start';
114
122
 
115
123
  /** Applies styles relevant to radios in a horizontal layout. */
116
124
  @property({type: Boolean, reflect: true}) horizontal = false;
117
125
 
126
+ /** Draws the radio as one segment of a scale. Set by a radio group with `scale`. */
127
+ @property({type: Boolean, reflect: true}) scale = false;
128
+
129
+ /** Fills a scale segment, as one at or before the group's checked radio. Set by a radio group with `scale`. */
130
+ @property({type: Boolean, reflect: true}) filled = false;
131
+
118
132
  /** The default value of the form control. Primarily used for resetting the form control. */
119
133
  @defaultValue('checked') defaultChecked = false;
120
134
 
@@ -150,14 +164,31 @@ export default class ZnRadio extends ZincElement implements ZincFormControl {
150
164
  }
151
165
 
152
166
  private handleClick() {
153
- if (this.disabled) {
167
+ // A radio group checks its radio on the label click, before this input click, so grouped radios must still emit.
168
+ if (this.disabled || (this.checked && !this.closest('zn-radio-group'))) {
154
169
  return;
155
170
  }
156
171
 
157
- this.checked = !this.checked;
172
+ this.checked = true;
173
+ this.uncheckNamedSiblings();
158
174
  this.emit('zn-change');
159
175
  }
160
176
 
177
+ /** Each input sits in its own shadow root, so the browser can't group same-named radios outside a radio group. */
178
+ private uncheckNamedSiblings() {
179
+ if (!this.name || this.closest('zn-radio-group')) {
180
+ return;
181
+ }
182
+
183
+ const root = this.getRootNode() as Document | ShadowRoot;
184
+ const form = this.closest('form');
185
+ root.querySelectorAll<ZnRadio>('zn-radio').forEach(radio => {
186
+ if (radio !== this && radio.name === this.name && radio.closest('form') === form) {
187
+ radio.checked = false;
188
+ }
189
+ });
190
+ }
191
+
161
192
  private handleBlur() {
162
193
  this.hasFocus = false;
163
194
  this.emit('zn-blur');
@@ -189,9 +220,17 @@ export default class ZnRadio extends ZincElement implements ZincFormControl {
189
220
  }
190
221
 
191
222
  @watch(['checked'], {waitUntilFirstUpdate: true})
192
- handleStateChange() {
223
+ async handleStateChange() {
193
224
  this.input.checked = this.checked; // force a sync update
194
225
  this.formControlController.updateValidity();
226
+
227
+ const animate = this.pointerIntent.consume();
228
+ await this.updateComplete;
229
+ const dot = this.control.querySelector('.radio__checked-icon');
230
+ if (dot && animate) {
231
+ animateTo(this.control, [{scale: '0.9'}, {scale: '1'}], {duration: 160, easing: EASE_OUT});
232
+ animateTo(dot, [{scale: '0.5', opacity: 0}, {scale: '1', opacity: 1}], {duration: 180, easing: EASE_OUT});
233
+ }
195
234
  }
196
235
 
197
236
  /** Simulates a click on the radio. */
@@ -285,6 +324,8 @@ export default class ZnRadio extends ZincElement implements ZincFormControl {
285
324
  'radio--focused': this.hasFocus,
286
325
  'radio--contained': this.contained,
287
326
  'radio--horizontal': this.horizontal,
327
+ 'radio--scale': this.scale,
328
+ 'radio--filled': this.scale && this.filled,
288
329
  'radio--small': this.size === 'small',
289
330
  'radio--medium': this.size === 'medium',
290
331
  'radio--large': this.size === 'large',
@@ -318,8 +359,7 @@ export default class ZnRadio extends ZincElement implements ZincFormControl {
318
359
  class="radio__control selection-card__control">
319
360
  ${this.checked
320
361
  ? html`
321
- <zn-icon part="checked-icon" size="18" class="radio__checked-icon"
322
- src="radio_button_checked"></zn-icon>`
362
+ <span part="checked-icon" class="radio__checked-icon"></span>`
323
363
  : ''}
324
364
  </span>
325
365
 
@@ -345,16 +385,15 @@ export default class ZnRadio extends ZincElement implements ZincFormControl {
345
385
  part="description">
346
386
  <slot name="description">${this.description}</slot>
347
387
  </div>
348
- ${this.checked
349
- ? html`
350
- <zn-animation name="fadeIn" easing="ease" duration="300" iterations="1" play>
351
- <slot
352
- name="selected-content"
353
- part="selected-content"
354
- class="radio__selected-content"
355
- @click=${this.handleSelectedContentClick}></slot>
356
- </zn-animation>`
357
- : ''}
388
+ <div class="radio__selected-content-wrapper">
389
+ <div class="radio__selected-content-inner">
390
+ <slot
391
+ name="selected-content"
392
+ part="selected-content"
393
+ class="radio__selected-content"
394
+ @click=${this.handleSelectedContentClick}></slot>
395
+ </div>
396
+ </div>
358
397
  </div>
359
398
  </label>
360
399
  </div>
@@ -55,7 +55,7 @@
55
55
  justify-content: center;
56
56
  width: var(--toggle-size);
57
57
  height: var(--toggle-size);
58
- border: solid var(--zn-input-border-width) var(--zn-input-border-color);
58
+ border: solid var(--zn-checkbox-border-width, var(--zn-input-border-width)) var(--zn-checkbox-border-color, var(--zn-input-border-color));
59
59
  border-radius: 50%;
60
60
  background-color: var(--zn-input-background-color);
61
61
  color: var(--zn-color-neutral-0);
@@ -74,28 +74,27 @@
74
74
  }
75
75
 
76
76
  .radio__checked-icon {
77
- --icon-size: var(--toggle-size);
78
- --icon-color: var(--zn-color-primary);
79
-
80
- display: inline-flex;
77
+ width: 40%;
78
+ height: 40%;
79
+ border-radius: 50%;
80
+ background-color: currentColor;
81
81
  }
82
82
 
83
83
  /* Hover */
84
84
  .radio:not(.radio--checked):not(.radio--disabled) .radio__control:hover {
85
- border-color: rgb(var(--zn-primary));
86
- background-color: rgb(var(--zn-primary), .2);
85
+ border-color: var(--zn-color-primary-500);
87
86
  }
88
87
 
89
88
  /* Checked */
90
89
  .radio--checked .radio__control {
91
- color: rgb(var(--zn-primary));
92
- border: none;
90
+ border-color: var(--zn-color-primary-600);
91
+ background-color: var(--zn-color-primary-600);
93
92
  }
94
93
 
95
94
  /* Checked + hover */
96
95
  .radio.radio--checked:not(.radio--disabled) .radio__control:hover {
97
- color: rgba(var(--zn-primary), .6);
98
- border: none;
96
+ border-color: var(--zn-color-primary-500);
97
+ background-color: var(--zn-color-primary-500);
99
98
  }
100
99
 
101
100
  /* Focus */
@@ -122,14 +121,6 @@
122
121
  opacity: 0;
123
122
  }
124
123
 
125
- :host-context([required]) .radio__label::after {
126
- display: none;
127
- }
128
-
129
- :host([required]) .radio__label::after {
130
- display: none;
131
- }
132
-
133
124
  /* If parent Radio Group has 'data-user-invalid', style all radios in the group as 'data-user-invalid' by targeting with a class ('radio-user-invalid'), since the radios can't be targeted using ::slotted */
134
125
  :host(.radio-user-invalid) .radio__control {
135
126
  border-color: var(--zn-color-danger-600);
@@ -165,22 +156,95 @@
165
156
  -webkit-user-select: none;
166
157
  }
167
158
 
159
+ /* Scale: the control becomes a bar segment with the label centred beneath it. */
160
+ .radio--scale {
161
+ flex-direction: column;
162
+ align-items: stretch;
163
+ gap: var(--zn-spacing-x-small);
164
+ width: 100%;
165
+ padding-block: var(--zn-spacing-x-small);
166
+
167
+ .radio__control {
168
+ width: 100%;
169
+ height: var(--zn-radio-scale-segment-height, 0.875rem);
170
+ border: none;
171
+ border-radius: var(--zn-radio-scale-segment-radius, 4px);
172
+ background-color: var(--zn-color-neutral-200);
173
+ transition: background-color var(--zn-transition-fast) ease-out;
174
+ }
175
+
176
+ .radio__checked-icon {
177
+ display: none;
178
+ }
179
+
180
+ .radio__label {
181
+ margin-inline-start: 0;
182
+ text-align: center;
183
+ font-size: var(--zn-font-size-small);
184
+ font-weight: var(--zn-font-weight-medium, 500);
185
+ line-height: var(--zn-line-height-dense);
186
+ color: var(--zn-text-color-muted, var(--zn-color-neutral-500));
187
+ transition: color var(--zn-transition-fast) ease-out;
188
+ }
189
+ }
190
+
191
+ .radio.radio--scale:not(.radio--disabled):hover {
192
+ .radio__control {
193
+ background-color: var(--zn-color-primary-400);
194
+ }
195
+
196
+ .radio__label {
197
+ color: var(--zn-color-primary-fg, var(--zn-color-primary-600));
198
+ }
199
+ }
200
+
201
+ .radio.radio--scale.radio--filled .radio__control {
202
+ background-color: var(--zn-color-primary-600);
203
+ }
204
+
205
+ .radio.radio--scale.radio--filled:not(.radio--disabled):hover .radio__control {
206
+ background-color: var(--zn-color-primary-700);
207
+ }
208
+
209
+ .radio.radio--scale.radio--checked .radio__control {
210
+ outline: 2px solid var(--zn-color-primary-600);
211
+ outline-offset: 2px;
212
+ }
213
+
214
+ .radio.radio--scale.radio--checked .radio__label {
215
+ color: var(--zn-color-primary-fg, var(--zn-color-primary-600));
216
+ font-weight: var(--zn-font-weight-semibold, 600);
217
+ }
218
+
168
219
  /* Contained */
169
220
  .radio--contained {
170
221
  padding: 1.375rem var(--zn-spacing-large) 1.375rem var(--zn-spacing-medium);
171
222
  border: 1px solid var(--zn-color-gray-400);
223
+ // A transparent outline from the start lets the checked outline fade in rather than pop.
224
+ outline: 1px solid transparent;
172
225
  width: 100%;
173
226
  height: 100%;
227
+ transition: background-color var(--zn-transition-fast) ease-out,
228
+ border-color var(--zn-transition-fast) ease-out,
229
+ outline-color var(--zn-transition-fast) ease-out,
230
+ scale var(--zn-transition-fast) var(--zn-ease-out);
231
+
232
+ .radio__label {
233
+ transition: color var(--zn-transition-fast) ease-out;
234
+ }
174
235
  }
175
236
 
176
237
  :not(.radio--disabled).radio--contained:hover,
177
238
  :not(.radio--disabled).radio--contained.radio--checked:hover {
178
- background-color: var(--zn-color-purple-50);
179
- transition: var(--zn-transition-medium) all;
239
+ background-color: var(--zn-selection-card-hover-background, var(--zn-color-purple-50));
240
+ }
241
+
242
+ :not(.radio--disabled).radio--contained:active {
243
+ scale: 0.99;
180
244
  }
181
245
 
182
246
  .radio--contained.radio--checked .radio__label {
183
- color: var(--zn-color-purple-600);
247
+ color: var(--zn-selection-card-checked-color, var(--zn-color-purple-600));
184
248
  font-weight: var(--zn-font-weight-semibold);
185
249
  }
186
250
 
@@ -190,13 +254,24 @@
190
254
  }
191
255
 
192
256
  .radio--contained.radio--checked {
193
- background-color: var(--zn-color-purple-100);
194
- border: 1px solid var(--zn-color-purple-600);
195
- outline: 1px solid var(--zn-color-purple-600);
196
- transition: var(--zn-transition-medium) all;
257
+ background-color: var(--zn-selection-card-checked-background, var(--zn-color-purple-100));
258
+ border-color: var(--zn-selection-card-checked-color, var(--zn-color-purple-600));
259
+ outline-color: var(--zn-selection-card-checked-color, var(--zn-color-purple-600));
197
260
  }
198
261
 
199
262
  /* When the control isn't checked, hide the circle for Windows High Contrast mode a11y */
200
263
  .radio:not(.radio--checked) svg circle {
201
264
  opacity: 0;
202
265
  }
266
+
267
+ /* Icon and SVG card images take the card's state colour */
268
+ .radio--contained .selection-card__image-container {
269
+ color: var(--zn-color-gray-500);
270
+ transition: color var(--zn-transition-fast) ease-out;
271
+ }
272
+
273
+ .radio--contained.radio--checked .selection-card__image-container {
274
+ color: var(--zn-selection-card-checked-color, var(--zn-color-purple-600));
275
+ }
276
+
277
+ @include wc.selected-content-reveal(radio);