@kubex/zinc 1.1.184 → 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 (362) 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 +10344 -2583
  6. package/dist/vscode.html-custom-data.json +1102 -125
  7. package/dist/web-types.json +2217 -224
  8. package/dist/zn.d.ts +2306 -417
  9. package/dist/zn.min.css +1 -1
  10. package/dist/zn.min.js +2313 -1635
  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/filter-wrapper.md +20 -0
  33. package/docs/pages/components/icon.md +46 -1
  34. package/docs/pages/components/input.md +30 -11
  35. package/docs/pages/components/item.md +35 -0
  36. package/docs/pages/components/key-value.md +25 -0
  37. package/docs/pages/components/menu.md +74 -7
  38. package/docs/pages/components/nav-item.md +15 -0
  39. package/docs/pages/components/nav-list.md +132 -0
  40. package/docs/pages/components/navbar.md +100 -1
  41. package/docs/pages/components/page.md +242 -74
  42. package/docs/pages/components/priority-list.md +43 -5
  43. package/docs/pages/components/query-builder.md +23 -0
  44. package/docs/pages/components/radio-group.md +33 -1
  45. package/docs/pages/components/radio.md +1 -1
  46. package/docs/pages/components/report-table.md +202 -0
  47. package/docs/pages/components/stat.md +30 -0
  48. package/docs/pages/components/tile.md +11 -0
  49. package/docs/pages/components/tooltip.md +1 -1
  50. package/docs/pages/components/translation-grid.md +86 -0
  51. package/docs/pages/components/vertical-stepper.md +108 -64
  52. package/package.json +2 -1
  53. package/scss/_global-spacing.scss +2 -2
  54. package/scss/_root.scss +11 -171
  55. package/scss/boot.scss +14 -0
  56. package/scss/mixins/_helper-mixins.scss +24 -2
  57. package/scss/mixins/_motion-mixins.scss +126 -0
  58. package/scss/mixins/_spacing-mixins.scss +4 -3
  59. package/scss/mixins/_tab-indicator-mixins.scss +12 -0
  60. package/scss/mixins/_typography-mixins.scss +1 -1
  61. package/scss/mixins/index.scss +14 -0
  62. package/scss/shared/_base.scss +32 -1
  63. package/scss/shared/_button-mixin.scss +7 -5
  64. package/scss/shared/_table.scss +35 -4
  65. package/scss/shared/nav.scss +4 -0
  66. package/scss/shared/shimmer.scss +1 -1
  67. package/scss/table-controls.scss +206 -0
  68. package/scss/themes/_dark.scss +1 -3
  69. package/scss/themes/_index.scss +1 -1
  70. package/scss/themes/_light.scss +46 -6
  71. package/scss/themes/_modes.scss +11 -11
  72. package/scss/themes/_openbyte.scss +944 -0
  73. package/scss/themes/_typography.scss +4 -2
  74. package/scss/variables/index.scss +0 -9
  75. package/src/components/alert/alert.component.ts +14 -4
  76. package/src/components/alert/alert.scss +55 -69
  77. package/src/components/alert/alert.test.ts +8 -0
  78. package/src/components/animated-button/animated-button.component.ts +3 -2
  79. package/src/components/animated-button/animated-button.scss +6 -23
  80. package/src/components/animated-button/animated-button.test.ts +4 -3
  81. package/src/components/avatar-group/avatar-group.component.ts +84 -0
  82. package/src/components/avatar-group/avatar-group.scss +54 -0
  83. package/src/components/avatar-group/avatar-group.test.ts +59 -0
  84. package/src/components/avatar-group/index.ts +12 -0
  85. package/src/components/badge/badge.component.ts +49 -0
  86. package/src/components/badge/badge.scss +80 -0
  87. package/src/components/badge/badge.test.ts +34 -0
  88. package/src/components/badge/index.ts +12 -0
  89. package/src/components/bulk-actions/bulk-actions.component.ts +14 -2
  90. package/src/components/button/button.component.ts +242 -11
  91. package/src/components/button/button.scss +133 -25
  92. package/src/components/button/button.test.ts +122 -0
  93. package/src/components/button-menu/button-menu.component.ts +32 -11
  94. package/src/components/button-menu/button-menu.scss +5 -1
  95. package/src/components/channel-tile/channel-tile.component.ts +34 -27
  96. package/src/components/channel-tile/channel-tile.scss +76 -43
  97. package/src/components/chart/builders.test.ts +46 -0
  98. package/src/components/chart/builders.ts +27 -3
  99. package/src/components/chart/chart-live.test.ts +4 -6
  100. package/src/components/chart/chart.component.ts +125 -5
  101. package/src/components/chart/chart.scss +121 -0
  102. package/src/components/chart/chart.test.ts +81 -0
  103. package/src/components/chat-message/chat-message.component.ts +21 -1
  104. package/src/components/chat-message/chat-message.scss +13 -2
  105. package/src/components/chat-message/chat-message.test.ts +9 -0
  106. package/src/components/checkbox/checkbox.component.ts +61 -27
  107. package/src/components/checkbox/checkbox.scss +58 -25
  108. package/src/components/checkbox-group/checkbox-group.component.ts +7 -1
  109. package/src/components/chip/chip.component.ts +11 -1
  110. package/src/components/chip/chip.scss +91 -26
  111. package/src/components/chip/chip.test.ts +17 -0
  112. package/src/components/collapsible/collapsible.component.ts +17 -3
  113. package/src/components/collapsible/collapsible.scss +19 -10
  114. package/src/components/color-picker/color-picker.component.ts +277 -0
  115. package/src/components/color-picker/color-picker.scss +141 -0
  116. package/src/components/color-picker/color-picker.test.ts +57 -0
  117. package/src/components/color-picker/index.ts +12 -0
  118. package/src/components/cols/cols.component.ts +17 -3
  119. package/src/components/cols/cols.scss +24 -13
  120. package/src/components/cols/cols.test.ts +33 -0
  121. package/src/components/confirm/confirm.component.ts +20 -6
  122. package/src/components/confirm/confirm.test.ts +52 -0
  123. package/src/components/content-block/content-block.component.ts +38 -9
  124. package/src/components/content-block/content-block.scss +11 -5
  125. package/src/components/content-block/content-block.test.ts +26 -0
  126. package/src/components/copy-button/copy-button.component.ts +2 -2
  127. package/src/components/copy-button/copy-button.scss +17 -4
  128. package/src/components/data-select/data-select.component.ts +3 -1
  129. package/src/components/data-table/data-table.component.ts +364 -142
  130. package/src/components/data-table/data-table.scss +133 -60
  131. package/src/components/data-table/data-table.test.ts +356 -4
  132. package/src/components/data-table-filter/data-table-filter.component.ts +426 -406
  133. package/src/components/data-table-filter/data-table-filter.scss +176 -148
  134. package/src/components/data-table-filter/data-table-filter.test.ts +271 -273
  135. package/src/components/data-table-search/data-table-search.component.ts +4 -2
  136. package/src/components/data-table-search/data-table-search.scss +8 -1
  137. package/src/components/data-table-search/data-table-search.test.ts +29 -0
  138. package/src/components/date-range/date-range-math.test.ts +78 -0
  139. package/src/components/date-range/date-range-math.ts +105 -0
  140. package/src/components/date-range/date-range.component.ts +375 -0
  141. package/src/components/date-range/date-range.scss +272 -0
  142. package/src/components/date-range/date-range.test.ts +247 -0
  143. package/src/components/date-range/index.ts +12 -0
  144. package/src/components/datepicker/datepicker.component.ts +44 -7
  145. package/src/components/datepicker/datepicker.scss +9 -0
  146. package/src/components/datepicker/datepicker.test.ts +2 -2
  147. package/src/components/dialog/dialog.component.ts +38 -19
  148. package/src/components/dialog/dialog.scss +65 -36
  149. package/src/components/dialog/dialog.test.ts +14 -0
  150. package/src/components/diff/diff.component.ts +55 -0
  151. package/src/components/diff/diff.scss +46 -0
  152. package/src/components/diff/diff.test.ts +15 -0
  153. package/src/components/diff/index.ts +12 -0
  154. package/src/components/diff-row/diff-row.component.ts +75 -0
  155. package/src/components/diff-row/diff-row.scss +70 -0
  156. package/src/components/diff-row/diff-row.test.ts +37 -0
  157. package/src/components/diff-row/index.ts +12 -0
  158. package/src/components/dropdown/dropdown.component.ts +12 -10
  159. package/src/components/dropdown/dropdown.scss +2 -0
  160. package/src/components/editor/editor.component.ts +159 -11
  161. package/src/components/editor/editor.scss +250 -1
  162. package/src/components/editor/editor.test.ts +31 -3
  163. package/src/components/editor/modules/ai/panel/ai-panel.scss +0 -1
  164. package/src/components/editor/modules/attachment/attachment.ts +53 -31
  165. package/src/components/editor/modules/context-menu/context-menu.ts +17 -0
  166. package/src/components/editor/modules/dialog/dialog.component.ts +23 -1
  167. package/src/components/editor/modules/dialog/dialog.test.ts +28 -0
  168. package/src/components/editor/modules/emoji/emoji.ts +4 -5
  169. package/src/components/editor/modules/merge-field/merge-field.ts +83 -0
  170. package/src/components/editor/modules/toolbar/toolbar.component.ts +121 -68
  171. package/src/components/editor/modules/toolbar/toolbar.scss +38 -0
  172. package/src/components/editor/modules/toolbar/toolbar.ts +25 -1
  173. package/src/components/empty-state/empty-state.component.ts +6 -1
  174. package/src/components/empty-state/empty-state.scss +21 -4
  175. package/src/components/expanding-action/expanding-action.component.ts +22 -34
  176. package/src/components/expanding-action/expanding-action.scss +3 -45
  177. package/src/components/expanding-action/expanding-action.test.ts +2 -1
  178. package/src/components/file/file.component.ts +30 -2
  179. package/src/components/file/file.scss +6 -0
  180. package/src/components/filter-wrapper/filter-wrapper.component.ts +45 -10
  181. package/src/components/filter-wrapper/filter-wrapper.test.ts +58 -2
  182. package/src/components/flow-builder/flow-builder.component.ts +36 -3
  183. package/src/components/flow-builder/flow-builder.scss +0 -6
  184. package/src/components/flow-builder/modules/flow-canvas/flow-canvas.component.ts +8 -0
  185. package/src/components/flow-builder/modules/flow-canvas/flow-canvas.scss +10 -5
  186. package/src/components/flow-builder/modules/flow-node/flow-node.component.ts +17 -0
  187. package/src/components/flow-builder/modules/flow-node/flow-node.scss +10 -0
  188. package/src/components/form-actions/form-actions.component.ts +48 -2
  189. package/src/components/form-actions/form-actions.scss +1 -1
  190. package/src/components/form-group/form-group.component.ts +10 -3
  191. package/src/components/form-group/form-group.scss +37 -4
  192. package/src/components/form-group/form-group.test.ts +10 -0
  193. package/src/components/header/header.component.ts +11 -4
  194. package/src/components/header/header.scss +47 -5
  195. package/src/components/hover-container/hover-container.component.ts +4 -5
  196. package/src/components/icon/icon.component.ts +95 -9
  197. package/src/components/icon/icon.scss +70 -6
  198. package/src/components/icon/icon.test.ts +55 -0
  199. package/src/components/icon-picker/icon-picker.component.ts +35 -1
  200. package/src/components/inline-edit/inline-edit.component.ts +38 -8
  201. package/src/components/inline-edit/inline-edit.scss +128 -19
  202. package/src/components/input/input.component.ts +173 -297
  203. package/src/components/input/input.scss +65 -43
  204. package/src/components/input/input.test.ts +86 -4
  205. package/src/components/item/item.component.ts +12 -0
  206. package/src/components/item/item.scss +102 -3
  207. package/src/components/item/item.test.ts +23 -0
  208. package/src/components/key/key.component.ts +2 -2
  209. package/src/components/key/key.scss +11 -0
  210. package/src/components/key-value/index.ts +12 -0
  211. package/src/components/key-value/key-value.component.ts +160 -0
  212. package/src/components/key-value/key-value.scss +20 -0
  213. package/src/components/key-value/key-value.test.ts +22 -0
  214. package/src/components/markdown-editor/markdown-editor.component.ts +8 -1
  215. package/src/components/menu/menu.component.ts +21 -2
  216. package/src/components/menu/menu.scss +43 -23
  217. package/src/components/menu/menu.test.ts +33 -0
  218. package/src/components/menu-item/menu-item.component.ts +13 -2
  219. package/src/components/menu-item/menu-item.scss +56 -23
  220. package/src/components/menu-item/menu-item.test.ts +31 -0
  221. package/src/components/menu-item/submenu-controller.ts +25 -15
  222. package/src/components/menu-label/index.ts +12 -0
  223. package/src/components/menu-label/menu-label.component.ts +30 -0
  224. package/src/components/menu-label/menu-label.scss +18 -0
  225. package/src/components/nav-item/index.ts +12 -0
  226. package/src/components/nav-item/nav-item.component.ts +83 -0
  227. package/src/components/nav-item/nav-item.scss +128 -0
  228. package/src/components/nav-list/index.ts +12 -0
  229. package/src/components/nav-list/nav-list.component.ts +313 -0
  230. package/src/components/nav-list/nav-list.scss +87 -0
  231. package/src/components/nav-list/nav-list.test.ts +250 -0
  232. package/src/components/navbar/navbar.component.ts +235 -171
  233. package/src/components/navbar/navbar.scss +333 -169
  234. package/src/components/navbar/navbar.test.ts +63 -143
  235. package/src/components/page/page.component.ts +114 -29
  236. package/src/components/page/page.scss +223 -95
  237. package/src/components/page/page.test.ts +214 -3
  238. package/src/components/page-builder/page-builder.component.ts +51 -5
  239. package/src/components/page-builder/page-builder.scss +6 -1
  240. package/src/components/page-nav/page-nav.component.ts +25 -3
  241. package/src/components/page-nav/page-nav.scss +10 -2
  242. package/src/components/pagination/pagination.scss +1 -1
  243. package/src/components/panel/panel.component.ts +14 -13
  244. package/src/components/panel/panel.scss +14 -10
  245. package/src/components/panel/panel.test.ts +42 -0
  246. package/src/components/popup/popup.component.ts +20 -13
  247. package/src/components/popup/popup.test.ts +21 -1
  248. package/src/components/priority-list/priority-list.component.ts +157 -172
  249. package/src/components/priority-list/priority-list.scss +113 -17
  250. package/src/components/priority-list/priority-list.test.ts +301 -3
  251. package/src/components/progress-bar/progress-bar.component.ts +1 -1
  252. package/src/components/progress-bar/progress-bar.scss +16 -4
  253. package/src/components/progress-tile/progress-tile.scss +1 -1
  254. package/src/components/query-builder/query-builder.component.ts +85 -10
  255. package/src/components/query-builder/query-builder.scss +7 -1
  256. package/src/components/query-builder/query-builder.test.ts +40 -0
  257. package/src/components/radio/radio.component.ts +57 -18
  258. package/src/components/radio/radio.scss +101 -26
  259. package/src/components/radio-group/radio-group.component.ts +45 -7
  260. package/src/components/radio-group/radio-group.scss +17 -0
  261. package/src/components/radio-group/radio-group.test.ts +63 -2
  262. package/src/components/rating/rating.component.ts +23 -0
  263. package/src/components/rating/rating.scss +1 -1
  264. package/src/components/remarkd-editor/remarkd-editor.component.ts +60 -5
  265. package/src/components/remarkd-editor/remarkd-editor.scss +12 -4
  266. package/src/components/report-table/index.ts +12 -0
  267. package/src/components/report-table/report-format.test.ts +86 -0
  268. package/src/components/report-table/report-format.ts +72 -0
  269. package/src/components/report-table/report-math.test.ts +74 -0
  270. package/src/components/report-table/report-math.ts +252 -0
  271. package/src/components/report-table/report-table.component.ts +681 -0
  272. package/src/components/report-table/report-table.scss +328 -0
  273. package/src/components/report-table/report-table.test.ts +702 -0
  274. package/src/components/reveal/reveal.component.ts +15 -1
  275. package/src/components/reveal/reveal.scss +5 -0
  276. package/src/components/schedule-builder/schedule-builder.component.ts +23 -8
  277. package/src/components/scroll-container/scroll-container.component.ts +19 -1
  278. package/src/components/scroll-container/scroll-container.scss +6 -1
  279. package/src/components/select/select.component.ts +52 -36
  280. package/src/components/select/select.scss +42 -25
  281. package/src/components/select/select.test.ts +14 -2
  282. package/src/components/settings-container/settings-container.scss +18 -5
  283. package/src/components/sidebar/sidebar.component.ts +8 -1
  284. package/src/components/sidebar/sidebar.scss +6 -1
  285. package/src/components/skeleton/skeleton.component.ts +3 -3
  286. package/src/components/skeleton/skeleton.scss +6 -6
  287. package/src/components/skeleton/skeleton.test.ts +9 -0
  288. package/src/components/slash-menu/slash-menu.scss +1 -1
  289. package/src/components/slideout/slideout.component.ts +20 -3
  290. package/src/components/slideout/slideout.scss +13 -6
  291. package/src/components/slideout/slideout.test.ts +19 -0
  292. package/src/components/split-button/split-button.component.ts +1 -1
  293. package/src/components/split-pane/split-pane.component.ts +71 -8
  294. package/src/components/split-pane/split-pane.scss +145 -41
  295. package/src/components/split-pane/split-pane.test.ts +23 -3
  296. package/src/components/stat/stat.component.ts +209 -14
  297. package/src/components/stat/stat.scss +305 -5
  298. package/src/components/stat/stat.test.ts +143 -2
  299. package/src/components/status-indicator/status-indicator.scss +22 -21
  300. package/src/components/stepper/stepper.scss +3 -3
  301. package/src/components/tab/tab.component.ts +1 -0
  302. package/src/components/table/table.component.ts +12 -1
  303. package/src/components/table/table.scss +21 -13
  304. package/src/components/table/table.test.ts +17 -0
  305. package/src/components/tabs/tabs.component.ts +150 -37
  306. package/src/components/tabs/tabs.scss +48 -7
  307. package/src/components/tabs/tabs.test.ts +169 -0
  308. package/src/components/textarea/textarea.component.ts +25 -3
  309. package/src/components/textarea/textarea.scss +10 -0
  310. package/src/components/textarea/textarea.test.ts +8 -4
  311. package/src/components/theme-editor/theme-editor.scss +0 -6
  312. package/src/components/tile/tile.component.ts +32 -2
  313. package/src/components/tile/tile.scss +40 -7
  314. package/src/components/tile/tile.test.ts +10 -0
  315. package/src/components/toggle/toggle.component.ts +23 -7
  316. package/src/components/toggle/toggle.scss +36 -29
  317. package/src/components/toggle/toggle.test.ts +17 -0
  318. package/src/components/tooltip/tooltip.component.ts +3 -0
  319. package/src/components/tooltip/tooltip.scss +11 -6
  320. package/src/components/tooltip/tooltip.test.ts +12 -0
  321. package/src/components/translation-field/index.ts +12 -0
  322. package/src/components/translation-field/translation-field.component.ts +55 -0
  323. package/src/components/translation-field/translation-field.scss +101 -0
  324. package/src/components/translation-fields/index.ts +12 -0
  325. package/src/components/translation-fields/translation-fields.component.ts +80 -0
  326. package/src/components/translation-fields/translation-fields.scss +60 -0
  327. package/src/components/translation-grid/index.ts +12 -0
  328. package/src/components/translation-grid/translation-grid.component.ts +157 -0
  329. package/src/components/translation-grid/translation-grid.scss +138 -0
  330. package/src/components/translation-grid/translation-grid.test.ts +28 -0
  331. package/src/components/vertical-stepper/vertical-stepper.component.ts +35 -9
  332. package/src/components/vertical-stepper/vertical-stepper.scss +110 -39
  333. package/src/components/vertical-stepper/vertical-stepper.test.ts +35 -0
  334. package/src/components/well/well.scss +2 -2
  335. package/src/events/events.ts +2 -0
  336. package/src/events/zn-chart-click.ts +13 -0
  337. package/src/events/zn-report-change.ts +7 -0
  338. package/src/form-control.scss +36 -3
  339. package/src/internal/anchored-popover.ts +175 -0
  340. package/src/internal/animate.ts +19 -5
  341. package/src/internal/cosmic-border.ts +15 -0
  342. package/src/internal/form.ts +1 -1
  343. package/src/internal/hover-intent.ts +15 -0
  344. package/src/internal/input-modality.ts +15 -0
  345. package/src/internal/pointer-intent.ts +37 -0
  346. package/src/internal/reorder.ts +625 -0
  347. package/src/internal/scroll.ts +51 -3
  348. package/src/internal/selection-card.ts +1 -1
  349. package/src/internal/tab-indicator.ts +92 -0
  350. package/src/internal/table-sort.ts +25 -0
  351. package/src/internal/table-tabs.ts +108 -0
  352. package/src/internal/test-drag.ts +35 -0
  353. package/src/internal/theme-openbyte.test.ts +126 -0
  354. package/src/internal/theme.ts +4 -4
  355. package/src/internal/zinc-element.ts +2 -2
  356. package/src/selection-card.scss +25 -0
  357. package/src/types/web-test-runner-commands.d.ts +1 -0
  358. package/src/utilities/color.test.ts +43 -0
  359. package/src/utilities/color.ts +189 -0
  360. package/src/zinc.ts +14 -0
  361. package/.github/workflows/eleventy_build.yml +0 -42
  362. package/scss/themes/_aura.scss +0 -114
@@ -5,9 +5,10 @@
5
5
  display: contents;
6
6
  }
7
7
 
8
- //::slotted(zn-sp) {
9
- // --zn-sp-padding: 0 !important
10
- //}
8
+ // Content loaded from a tab's uri sits inline in the tab: the tabs' own padding already spaces it
9
+ #content > [data-self-uri] > zn-sp {
10
+ --zn-sp-padding: 0;
11
+ }
11
12
 
12
13
  #mid, #content {
13
14
  flex: 1;
@@ -26,8 +27,12 @@
26
27
  max-height: 100%;
27
28
  width: 100%;
28
29
 
29
- zn-header::part(base) {
30
- //border-bottom-width: 0 !important;
30
+ zn-header {
31
+ padding: var(--zn-panel-header-padding, var(--zn-base-gap));
32
+ }
33
+
34
+ zn-header::part(content) {
35
+ padding: 0;
31
36
  }
32
37
  }
33
38
 
@@ -39,10 +44,34 @@ h1, h2 {
39
44
 
40
45
 
41
46
  #mid {
42
- container-type: inline-size;
43
47
  flex-direction: row;
44
48
  }
45
49
 
50
+ :host([gap]) #mid {
51
+ gap: var(--zn-gap, var(--zn-spacing-medium));
52
+ align-items: flex-start;
53
+
54
+ // The side card keeps its own width; the content takes the rest.
55
+ > ::slotted([slot="left"]),
56
+ > ::slotted([slot="right"]) {
57
+ flex-grow: 0;
58
+ flex-shrink: 0;
59
+ }
60
+ }
61
+
62
+ // The container for the side-card stacking below and for queries from the tab content: #mid is
63
+ // the same width, and every nested container costs Safari a full layout per restyle beneath it.
64
+ .tabs {
65
+ container: zn-tabs / inline-size;
66
+ }
67
+
68
+ @container zn-tabs (max-width: 900px) {
69
+ :host([gap]) #mid {
70
+ flex-direction: column;
71
+ align-items: stretch;
72
+ }
73
+ }
74
+
46
75
  #content {
47
76
  flex: 1;
48
77
  max-height: 100%;
@@ -60,8 +89,19 @@ h1, h2 {
60
89
  }
61
90
 
62
91
  :host([no-scroll]) {
92
+ // Clipped rather than hidden so focus rings on fields flush with the edge still show
93
+ .tabs, #mid, #content {
94
+ overflow: clip;
95
+ overflow-clip-margin: var(--zn-focus-ring-outset);
96
+ }
97
+
98
+ // Unlike hidden, clip keeps a flex item's minimum size at its content's, so #mid would grow past the tabs
99
+ #mid {
100
+ min-height: 0;
101
+ min-width: 0;
102
+ }
103
+
63
104
  #content {
64
- overflow: hidden;
65
105
 
66
106
  > div {
67
107
  max-height: 100%;
@@ -132,6 +172,7 @@ h1, h2 {
132
172
  }
133
173
 
134
174
  h2 {
175
+ font-family: var(--zn-text-h1-font-family, inherit);
135
176
  font-weight: var(--zn-font-weight-bold);
136
177
  flex-grow: 1;
137
178
  font-size: var(--zn-font-size-medium);
@@ -10,6 +10,79 @@ describe('<zn-tabs>', () => {
10
10
  expect(el).to.exist;
11
11
  });
12
12
 
13
+ describe('panel entrance', () => {
14
+ const renderTabs = () => fixture<ZnTabs>(html`
15
+ <zn-tabs active="first">
16
+ <zn-navbar slot="top">
17
+ <li tab="first">First</li>
18
+ <li tab="second">Second</li>
19
+ </zn-navbar>
20
+ <div id="first">First panel</div>
21
+ <div id="second">Second panel</div>
22
+ </zn-tabs>
23
+ `);
24
+ const fading = (el: Element) => el.getAnimations().filter(a => a.playState === 'running').length;
25
+
26
+ it('fades in a panel the pointer switched to', async () => {
27
+ const el = await renderTabs();
28
+ await el.updateComplete;
29
+
30
+ const second = el.querySelector<HTMLElement>('li[tab="second"]')!;
31
+ second.dispatchEvent(new PointerEvent('pointerdown', {bubbles: true, composed: true}));
32
+ second.click();
33
+ await aTimeout(40);
34
+
35
+ expect(fading(el.querySelector('#second')!)).to.equal(1);
36
+ });
37
+
38
+ it('stays still on first render and on keyboard switches', async () => {
39
+ // As if a click had just opened the page holding these tabs
40
+ document.body.dispatchEvent(new PointerEvent('pointerdown', {bubbles: true}));
41
+ const el = await fixture<ZnTabs>(html`
42
+ <zn-tabs>
43
+ <zn-navbar slot="top">
44
+ <li tab="first">First</li>
45
+ <li tab="second">Second</li>
46
+ </zn-navbar>
47
+ <div id="first">First panel</div>
48
+ <div id="second">Second panel</div>
49
+ </zn-tabs>
50
+ `);
51
+ await el.updateComplete;
52
+ expect(fading(el.querySelector('#first')!)).to.equal(0);
53
+
54
+ el.querySelector<HTMLElement>('li[tab="second"]')!.dispatchEvent(new KeyboardEvent('keydown', {key: 'Enter'}));
55
+ await aTimeout(40);
56
+
57
+ expect(el.querySelector('#second')!.hasAttribute('selected')).to.be.true;
58
+ expect(fading(el.querySelector('#second')!)).to.equal(0);
59
+ });
60
+ });
61
+
62
+ it('preloads a uri tab only once the pointer rests on it', async () => {
63
+ const el = await fixture<ZnTabs>(html`
64
+ <zn-tabs>
65
+ <zn-navbar slot="top">
66
+ <li tab-uri="/default">Default</li>
67
+ <li tab-uri="/passed-over">Passed</li>
68
+ <li tab-uri="/rested-on">Rested</li>
69
+ </zn-navbar>
70
+ </zn-tabs>
71
+ `);
72
+ await el.updateComplete;
73
+ const [, passed, rested] = Array.from(el.querySelectorAll<HTMLElement>('li[tab-uri]'));
74
+ expect(passed.hasAttribute('tab') || rested.hasAttribute('tab')).to.be.false;
75
+
76
+ passed.dispatchEvent(new MouseEvent('mouseenter'));
77
+ await aTimeout(50);
78
+ passed.dispatchEvent(new MouseEvent('mouseleave'));
79
+ rested.dispatchEvent(new MouseEvent('mouseenter'));
80
+ await aTimeout(250);
81
+
82
+ expect(passed.hasAttribute('tab')).to.be.false;
83
+ expect(rested.hasAttribute('tab')).to.be.true;
84
+ });
85
+
13
86
  it('does not register tabs belonging to a nested zn-page', async () => {
14
87
  const el = await fixture<ZnTabs>(html`
15
88
  <zn-tabs>
@@ -112,6 +185,47 @@ describe('<zn-tabs>', () => {
112
185
 
113
186
  expect(wrapper.scrollTop).to.equal(0);
114
187
  });
188
+
189
+ it('scrolls nested enclosing scrollers only as far as the tabs need', async () => {
190
+ const outer = await fixture<HTMLElement>(html`
191
+ <div style="height: 300px; overflow: auto;">
192
+ <div style="height: 2000px;">Above the inner scroller</div>
193
+ <div style="height: 300px; overflow: auto;">
194
+ <zn-tabs active="first">${panels}</zn-tabs>
195
+ </div>
196
+ <div style="height: 2000px;">Below the inner scroller</div>
197
+ </div>
198
+ `);
199
+ const inner = outer.children[1] as HTMLElement;
200
+ const el = inner.querySelector<ZnTabs>('zn-tabs')!;
201
+ await aTimeout(40);
202
+
203
+ inner.scrollTop = 500;
204
+ outer.scrollTop = 2100;
205
+
206
+ await openSecondTab(el);
207
+
208
+ expect(inner.scrollTop).to.equal(0);
209
+ expect(outer.scrollTop).to.equal(2000);
210
+ });
211
+
212
+ it('leaves an enclosing scroller alone while the tabs are still in view', async () => {
213
+ const wrapper = await fixture<HTMLElement>(html`
214
+ <div style="height: 200px; overflow: auto;">
215
+ <div style="height: 100px;">Above the tabs</div>
216
+ <zn-tabs active="first">${panels}</zn-tabs>
217
+ </div>
218
+ `);
219
+ const el = wrapper.querySelector<ZnTabs>('zn-tabs')!;
220
+ await aTimeout(40);
221
+
222
+ wrapper.scrollTop = 60;
223
+ expect(wrapper.scrollTop, 'the enclosing scroller never scrolled').to.equal(60);
224
+
225
+ await openSecondTab(el);
226
+
227
+ expect(wrapper.scrollTop).to.equal(60);
228
+ });
115
229
  });
116
230
 
117
231
  describe('selection persistence', () => {
@@ -158,6 +272,61 @@ describe('<zn-tabs>', () => {
158
272
  expect(restored.getAttribute('active')).to.equal('second');
159
273
  });
160
274
 
275
+ it('opens a tab added since the page was last shown, with select-added', async () => {
276
+ const render = (withThird: boolean) => fixture<ZnTabs>(html`
277
+ <zn-tabs store-key=${storeKey} active="first" select-added>
278
+ <zn-navbar slot="top">
279
+ <li tab="first">First</li>
280
+ <li tab="second">Second</li>
281
+ ${withThird ? html`<li tab="third">Third</li>` : null}
282
+ </zn-navbar>
283
+ <div id="first">First panel</div>
284
+ <div id="second">Second panel</div>
285
+ ${withThird ? html`<div id="third">Third panel</div>` : null}
286
+ </zn-tabs>
287
+ `);
288
+
289
+ const el = await render(false);
290
+ await aTimeout(40);
291
+ el.querySelector('zn-navbar')!.querySelectorAll<HTMLElement>('li')[1].click();
292
+ await aTimeout(40);
293
+ el.remove();
294
+
295
+ returnWithHistory();
296
+ const same = await render(false);
297
+ await aTimeout(40);
298
+ expect(same.getAttribute('active')).to.equal('second');
299
+ same.remove();
300
+
301
+ returnWithHistory();
302
+ const added = await render(true);
303
+ await aTimeout(40);
304
+ expect(added.getAttribute('active')).to.equal('third');
305
+ expect(added.querySelector('#third')!.hasAttribute('selected')).to.equal(true);
306
+ });
307
+
308
+ it('starts from the default tab when the restored tab no longer exists', async () => {
309
+ const el = await renderTabs();
310
+ await aTimeout(40);
311
+ el.querySelector('zn-navbar')!.querySelectorAll<HTMLElement>('li')[1].click();
312
+ await aTimeout(40);
313
+ el.remove();
314
+
315
+ returnWithHistory();
316
+ const restored = await fixture<ZnTabs>(html`
317
+ <zn-tabs store-key=${storeKey} active="first">
318
+ <zn-navbar slot="top">
319
+ <li tab="first">First</li>
320
+ </zn-navbar>
321
+ <div id="first">First panel</div>
322
+ </zn-tabs>
323
+ `);
324
+ await aTimeout(40);
325
+
326
+ expect(restored.getAttribute('active')).to.equal('first');
327
+ expect(restored.querySelector('#first')!.hasAttribute('selected')).to.equal(true);
328
+ });
329
+
161
330
  it('starts from the default tab when navigating to the location', async () => {
162
331
  const first = await renderTabs();
163
332
  await aTimeout(40);
@@ -8,6 +8,7 @@ import {ifDefined} from "lit/directives/if-defined.js";
8
8
  import {live} from "lit/directives/live.js";
9
9
  import {property, query, state} from 'lit/decorators.js';
10
10
  import {ResizeController} from '@lit-labs/observers/resize-controller.js';
11
+ import {revealBottom} from '../../internal/scroll';
11
12
  import {watch} from '../../internal/watch';
12
13
  import ZincElement, {type ZincFormControl} from '../../internal/zinc-element';
13
14
  import ZnSlashItem from "../slash-item";
@@ -64,10 +65,16 @@ export default class ZnTextarea extends ZincElement implements ZincFormControl {
64
65
  private readonly hasSlotController = new HasSlotController(this, 'help-text', 'label');
65
66
  private readonly resizeObserver = new ResizeController(this, {
66
67
  target: null,
67
- callback: () => this.setTextareaHeight(),
68
+ callback: () => {
69
+ this.setTextareaHeight();
70
+ // The native resize grip doesn't scroll, so a textarea at the bottom of a scroller would grow out of sight
71
+ if (this.pointerHeld) revealBottom(this.input);
72
+ },
68
73
  });
74
+ private pointerHeld = false;
69
75
  /** Ensures we only attempt to derive the initial value from light DOM content once */
70
76
  private _didInitFromContent = false;
77
+ private escapeSwallowed = false;
71
78
 
72
79
  private readonly slashController = new SlashMenuController(this, {
73
80
  menu: () => this.mountSlashMenu(),
@@ -139,6 +146,9 @@ export default class ZnTextarea extends ZincElement implements ZincFormControl {
139
146
  /** Makes the textarea a required field. */
140
147
  @property({type: Boolean, reflect: true}) required = false;
141
148
 
149
+ /** Short text shown next to the label, styled like the help text, e.g. `optional`. */
150
+ @property({attribute: 'label-note'}) labelNote = '';
151
+
142
152
  /** The minimum length of input that will be considered valid. */
143
153
  @property({type: Number}) minlength: number;
144
154
 
@@ -377,12 +387,22 @@ export default class ZnTextarea extends ZincElement implements ZincFormControl {
377
387
  // The slash menu claims its own keys before this handler runs; Escape closes it, not the field
378
388
  if (this.slashController.open) return;
379
389
 
390
+ // The first Escape stays in the field so it doesn't also dismiss a containing popup; a repeat bubbles
380
391
  if (event.key === 'Escape') {
381
- this.input.blur();
382
- event.stopPropagation();
392
+ if (!this.escapeSwallowed) {
393
+ this.escapeSwallowed = true;
394
+ event.stopPropagation();
395
+ }
396
+ return;
383
397
  }
398
+ this.escapeSwallowed = false;
384
399
  }
385
400
 
401
+ private handlePointerDown = () => {
402
+ this.pointerHeld = true;
403
+ window.addEventListener('pointerup', () => this.pointerHeld = false, {once: true});
404
+ };
405
+
386
406
  private setTextareaHeight() {
387
407
  if (this.resize === 'auto') {
388
408
  this.input.style.height = 'auto';
@@ -536,6 +556,7 @@ export default class ZnTextarea extends ZincElement implements ZincFormControl {
536
556
  <zn-icon library="fa" src="info"></zn-icon>
537
557
  </zn-tooltip>`
538
558
  : ''}
559
+ ${this.labelNote ? html`<span class="form-control__label-note">${this.labelNote}</span>` : ''}
539
560
  </label>
540
561
  ${hasContextNote
541
562
  ? html`<span class="form-control__label-context-note"><slot
@@ -585,6 +606,7 @@ export default class ZnTextarea extends ZincElement implements ZincFormControl {
585
606
  aria-describedby="help-text"
586
607
  @change=${this.handleChange}
587
608
  @input=${this.handleInput}
609
+ @pointerdown=${this.handlePointerDown}
588
610
  @invalid=${this.handleInvalid}
589
611
  @keydown=${this.handleKeyDown}
590
612
  @focus=${this.handleFocus}
@@ -31,6 +31,7 @@
31
31
  .textarea--standard {
32
32
  background-color: var(--zn-input-background-color);
33
33
  border: solid var(--zn-input-border-width) var(--zn-input-border-color);
34
+ box-shadow: var(--zn-input-shadow, none);
34
35
  }
35
36
 
36
37
  .textarea--standard:hover:not(.textarea--disabled) {
@@ -53,6 +54,15 @@
53
54
  color: var(--zn-input-color-focus);
54
55
  }
55
56
 
57
+ /* Invalid, once the user has interacted */
58
+ :host([data-user-invalid]) .textarea--standard:not(.textarea--disabled) {
59
+ border-color: var(--zn-input-border-color-invalid);
60
+ }
61
+
62
+ :host([data-user-invalid]) .textarea--standard.textarea--focused:not(.textarea--disabled) {
63
+ box-shadow: 0 0 0 var(--zn-focus-ring-width) var(--zn-input-focus-ring-color-invalid);
64
+ }
65
+
56
66
  .textarea--standard.textarea--disabled {
57
67
  background-color: var(--zn-input-background-color-disabled);
58
68
  border-color: var(--zn-input-border-color-disabled);
@@ -54,7 +54,7 @@ describe('<zn-textarea>', () => {
54
54
  });
55
55
 
56
56
  describe('Escape key', () => {
57
- it('blurs the inner textarea and stops propagation when focused', async () => {
57
+ it('keeps focus and swallows only the first Escape', async () => {
58
58
  const wrapper = await fixture<HTMLDivElement>(html`
59
59
  <div>
60
60
  <zn-textarea></zn-textarea>
@@ -72,16 +72,20 @@ describe('<zn-textarea>', () => {
72
72
  await el.updateComplete;
73
73
  expect(el.shadowRoot!.activeElement).to.equal(el.input);
74
74
 
75
- el.input.dispatchEvent(new KeyboardEvent('keydown', {
75
+ const pressEscape = () => el.input.dispatchEvent(new KeyboardEvent('keydown', {
76
76
  key: 'Escape',
77
77
  bubbles: true,
78
78
  composed: true,
79
79
  cancelable: true
80
80
  }));
81
- await el.updateComplete;
82
81
 
83
- expect(el.shadowRoot!.activeElement, 'inner textarea should be blurred').to.not.equal(el.input);
82
+ pressEscape();
83
+ await el.updateComplete;
84
+ expect(el.shadowRoot!.activeElement, 'inner textarea should keep focus').to.equal(el.input);
84
85
  expect(wrapperEscapes, 'wrapper should not see Escape on press 1').to.equal(0);
86
+
87
+ pressEscape();
88
+ expect(wrapperEscapes, 'wrapper should see Escape on press 2').to.equal(1);
85
89
  });
86
90
  });
87
91
 
@@ -70,12 +70,6 @@
70
70
  // rather than the navbar's default body colour (flow-builder pattern).
71
71
  --zn-active-nav-background: var(--zn-panel);
72
72
 
73
- // Themes with a glass page tab bar (e.g. Aura) frost inactive tabs on every
74
- // navbar under a zn-page — over the editor's opaque panel the translucent
75
- // white just reads as white. Pin the standard tab tint instead.
76
- --navbar-page-inactive-tab-background: rgb(var(--zn-color-tab));
77
- --navbar-page-inactive-tab-filter: none;
78
-
79
73
  position: relative;
80
74
  display: flex;
81
75
  flex-direction: column;
@@ -5,6 +5,7 @@ import {html, literal} from "lit/static-html.js";
5
5
  import {ifDefined} from "lit/directives/if-defined.js";
6
6
  import {property} from 'lit/decorators.js';
7
7
  import ZincElement from '../../internal/zinc-element';
8
+ import ZnIcon from '../icon';
8
9
 
9
10
  import styles from './tile.scss';
10
11
 
@@ -14,7 +15,7 @@ import styles from './tile.scss';
14
15
  * @status experimental
15
16
  * @since 1.0
16
17
  *
17
- * @dependency zn-example
18
+ * @dependency zn-icon
18
19
  *
19
20
  * @event zn-event-name - Emitted as an example.
20
21
  *
@@ -22,11 +23,17 @@ import styles from './tile.scss';
22
23
  * @slot example - An example slot.
23
24
  *
24
25
  * @csspart base - The component's base wrapper.
26
+ * @csspart icon - The tinted square around the icon.
25
27
  *
26
28
  * @cssproperty --example - An example CSS custom property.
29
+ * @cssproperty --zn-tile-hover-caption-color - The caption color while a linked tile is hovered.
30
+ * @cssproperty --zn-tile-hover-caption-decoration - The caption text decoration while a linked tile is hovered.
27
31
  */
28
32
  export default class ZnTile extends ZincElement {
29
33
  static styles: CSSResultGroup = unsafeCSS(styles);
34
+ static dependencies = {
35
+ 'zn-icon': ZnIcon,
36
+ };
30
37
 
31
38
  private readonly hasSlotController = new HasSlotController(this, '[default]', 'caption', 'description', 'properties', 'actions', 'image');
32
39
 
@@ -34,6 +41,12 @@ export default class ZnTile extends ZincElement {
34
41
 
35
42
  @property({attribute: 'description'}) description: string;
36
43
 
44
+ /** Shows the icon in a tinted square before the caption. */
45
+ @property() icon: string;
46
+
47
+ /** The tint of the icon square: `success`, `warning`, `danger`, `info`, `ai` or `neutral`. */
48
+ @property({attribute: 'icon-color'}) iconColor: 'success' | 'warning' | 'danger' | 'info' | 'ai' | 'neutral' = 'neutral';
49
+
37
50
  @property({attribute: 'href'}) href: string;
38
51
 
39
52
  @property({attribute: 'data-target'}) dataTarget: string;
@@ -63,6 +76,13 @@ export default class ZnTile extends ZincElement {
63
76
  return this.href || this.dataUri;
64
77
  }
65
78
 
79
+ // An <a> without href, as data-uri renders, is neither focusable nor activated by Enter on its own
80
+ private _handleLinkKeyDown(e: KeyboardEvent) {
81
+ if (this.href || !this.dataUri || e.key !== 'Enter' || e.target !== e.currentTarget) return;
82
+ e.preventDefault();
83
+ (e.currentTarget as HTMLElement).click();
84
+ }
85
+
66
86
  private _handleActionsClick(e: MouseEvent) {
67
87
  if (!this._isLink()) return;
68
88
 
@@ -90,6 +110,7 @@ export default class ZnTile extends ZincElement {
90
110
  const hasProperties = this.hasSlotController.test('properties');
91
111
  const hasActions = this.hasSlotController.test('actions');
92
112
  const hasImage = this.hasSlotController.test('image');
113
+ const hasIcon = !!this.icon;
93
114
 
94
115
  return html`
95
116
  <div
@@ -106,19 +127,28 @@ export default class ZnTile extends ZincElement {
106
127
  'tile--has-properties': hasProperties,
107
128
  'tile--has-actions': hasActions,
108
129
  'tile--has-image': hasImage,
130
+ 'tile--has-icon': hasIcon,
109
131
  })}">
110
132
  <${tag}
111
133
  href="${ifDefined(this.href)}"
112
134
  data-uri="${ifDefined(this.dataUri)}"
113
135
  gaid="${ifDefined(this.gaid)}"
114
136
  data-target="${ifDefined(this.dataTarget)}"
137
+ draggable="${ifDefined(isLink ? 'false' : undefined)}"
138
+ role="${ifDefined(this.dataUri && !this.href ? 'link' : undefined)}"
139
+ tabindex="${ifDefined(this.dataUri && !this.href ? '0' : undefined)}"
140
+ @keydown="${this._handleLinkKeyDown}"
115
141
  class="tile__link">
116
- ${!hasCaption && !hasDescription && !hasProperties && !hasActions ? html`
142
+ ${!hasCaption && !hasDescription && !hasProperties && !hasActions && !hasIcon ? html`
117
143
  <slot></slot>
118
144
  ` : html`
119
145
  <div class="tile__left">
120
146
  ${hasImage ? html`
121
147
  <slot name="image" part="image" class="tile__image"></slot>` : html``}
148
+ ${hasIcon ? html`
149
+ <span part="icon" class="tile__icon tile__icon--${this.iconColor}">
150
+ <zn-icon src="${this.icon}" size="18"></zn-icon>
151
+ </span>` : html``}
122
152
  <div class="tile__content">
123
153
  <p part="caption" class="tile__caption">
124
154
  <slot name="caption">${this.caption}</slot>
@@ -9,7 +9,7 @@
9
9
  display: flex;
10
10
  align-items: center;
11
11
  justify-content: space-between;
12
- min-height: 48px;
12
+ min-height: var(--zn-tile-min-height, 48px);
13
13
  column-gap: var(--zn-spacing-large);
14
14
 
15
15
  font-size: var(--zn-text-table-content-font-size);
@@ -18,15 +18,16 @@
18
18
  color: var(--zn-text-table-content-color);
19
19
 
20
20
  &--has-image,
21
+ &--has-icon,
21
22
  &--has-caption,
22
23
  &--has-description,
23
24
  &--has-properties,
24
25
  &--has-actions {
25
- padding-inline: var(--zn-base-gap);
26
+ padding-inline: var(--zn-tile-padding-inline, var(--zn-base-gap));
26
27
  }
27
28
 
28
29
  &--has-description#{&}--has-caption {
29
- padding-block: var(--zn-base-gap);
30
+ padding-block: var(--zn-tile-padding-block, var(--zn-base-gap));
30
31
  }
31
32
 
32
33
  &--has-image {
@@ -37,7 +38,12 @@
37
38
  cursor: pointer;
38
39
 
39
40
  &:hover {
40
- background-color: var(--zn-color-neutral-100);
41
+ background-color: var(--zn-tile-hover-background, var(--zn-color-neutral-100));
42
+
43
+ .tile__caption {
44
+ --tile-caption-hover-color: var(--zn-tile-hover-caption-color);
45
+ text-decoration: var(--zn-tile-hover-caption-decoration, none);
46
+ }
41
47
  }
42
48
  }
43
49
 
@@ -73,6 +79,11 @@ a {
73
79
  flex: 1;
74
80
  column-gap: var(--zn-spacing-small);
75
81
  min-width: 0;
82
+
83
+ &:focus-visible {
84
+ outline: var(--zn-focus-ring);
85
+ border-radius: var(--zn-border-radius-small);
86
+ }
76
87
  }
77
88
 
78
89
  .tile__left {
@@ -88,6 +99,25 @@ a {
88
99
  flex-shrink: 0;
89
100
  }
90
101
 
102
+ .tile__icon {
103
+ display: flex;
104
+ align-items: center;
105
+ justify-content: center;
106
+ flex-shrink: 0;
107
+ width: 36px;
108
+ height: 36px;
109
+ border-radius: var(--zn-border-radius-medium, var(--zn-border-radius));
110
+ color: var(--zn-color-neutral-fg);
111
+ background: var(--zn-color-neutral-bg);
112
+
113
+ @each $tone in success, warning, danger, info, ai {
114
+ &--#{$tone} {
115
+ color: var(--zn-color-#{$tone}-fg);
116
+ background: var(--zn-color-#{$tone}-bg);
117
+ }
118
+ }
119
+ }
120
+
91
121
  .tile__content {
92
122
  display: flex;
93
123
  flex-direction: column;
@@ -104,7 +134,8 @@ a {
104
134
  .tile__caption {
105
135
  margin: 0;
106
136
  font-weight: 600;
107
- color: rgb(var(--zn-text-heading));
137
+ // Unset outside a linked tile's hover, so each caption falls back to its resting colour
138
+ color: var(--tile-caption-hover-color, rgb(var(--zn-text-heading)));
108
139
  white-space: nowrap;
109
140
  overflow: hidden;
110
141
  text-overflow: ellipsis;
@@ -112,7 +143,7 @@ a {
112
143
 
113
144
  .tile--plain .tile__caption {
114
145
  font-weight: var(--zn-text-table-content-font-weight);
115
- color: var(--zn-text-table-content-color);
146
+ color: var(--tile-caption-hover-color, var(--zn-text-table-content-color));
116
147
  }
117
148
 
118
149
  .tile__description {
@@ -134,6 +165,8 @@ slot.tile__description {
134
165
  }
135
166
 
136
167
  .tile__right {
168
+ // Keeps actions clickable when a host such as zn-priority-list disables pointer events on the tile
169
+ pointer-events: auto;
137
170
  display: flex;
138
171
  align-items: center;
139
172
  justify-content: flex-end;
@@ -179,7 +212,7 @@ slot.tile__description {
179
212
  grid-template-columns: subgrid;
180
213
  align-items: center;
181
214
  justify-items: end;
182
- min-height: 48px;
215
+ min-height: var(--zn-tile-min-height, 48px);
183
216
  }
184
217
 
185
218
  :host([grouped]) .tile__link,
@@ -20,6 +20,16 @@ describe('<zn-tile>', () => {
20
20
  expect(el.shadowRoot!.querySelector('slot[name="image"]')).to.exist;
21
21
  });
22
22
 
23
+ it('draws the icon in a tinted square', async () => {
24
+ const el = await fixture<ZnTile>(html`
25
+ <zn-tile caption="Second chance" icon="tag@lu" icon-color="success"
26
+ style="--zn-color-success-bg: rgb(1, 2, 3)"></zn-tile>`);
27
+
28
+ const square = el.shadowRoot!.querySelector<HTMLElement>('.tile__icon')!;
29
+ expect(square.querySelector('zn-icon')).to.exist;
30
+ expect(getComputedStyle(square).backgroundColor).to.equal('rgb(1, 2, 3)');
31
+ });
32
+
23
33
  it('should render its content as a link when href is set', async () => {
24
34
  const el = await fixture<ZnTile>(html`
25
35
  <zn-tile href="/users/leslie" caption="Leslie Alexander"></zn-tile>`);