@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
@@ -40,6 +40,27 @@ describe('<zn-page>', () => {
40
40
  expect(navItems[3].getAttribute('tab-uri')).to.equal('/tab-three');
41
41
  });
42
42
 
43
+ it('shows the footer only when the footer slot has content', async () => {
44
+ const el = await fixture<ZnPage>(html`
45
+ <zn-page caption="Page Title">
46
+ <zn-tab caption="Overview">Content</zn-tab>
47
+ </zn-page>
48
+ `);
49
+ await aTimeout(20);
50
+
51
+ const footer = el.shadowRoot!.querySelector<HTMLElement>('[part="footer"]')!;
52
+ expect(footer.hidden).to.equal(true);
53
+
54
+ const actions = document.createElement('zn-form-actions');
55
+ actions.slot = 'footer';
56
+ el.appendChild(actions);
57
+ await aTimeout(20);
58
+
59
+ const footerSlot = el.shadowRoot!.querySelector<HTMLSlotElement>('slot[name="footer"]')!;
60
+ expect(footer.hidden).to.equal(false);
61
+ expect(footerSlot.assignedElements()[0]).to.equal(actions);
62
+ });
63
+
43
64
  it('pushes nested expanding actions into the generated navbar', async () => {
44
65
  const el = await fixture<ZnPage>(html`
45
66
  <zn-page caption="Page Title">
@@ -152,6 +173,27 @@ describe('<zn-page>', () => {
152
173
  expect(freshPage.getAttribute('active')).to.equal('');
153
174
  });
154
175
 
176
+ it('loads the first uri tab when the stored tab belongs to a previous entity', async () => {
177
+ const renderUriPage = (entity: string) => fixture<ZnPage>(html`
178
+ <zn-page>
179
+ <zn-tab caption="Accounts" uri="/${entity}/accounts"></zn-tab>
180
+ <zn-tab caption="Invoices" uri="/${entity}/invoices"></zn-tab>
181
+ </zn-page>
182
+ `);
183
+
184
+ const first = await renderUriPage('a');
185
+ await aTimeout(40);
186
+ first.shadowRoot!.querySelector('zn-navbar')!.querySelector<HTMLElement>('li[tab-uri="/a/accounts"]')!.click();
187
+ await aTimeout(40);
188
+ first.remove();
189
+
190
+ window.dispatchEvent(new PopStateEvent('popstate'));
191
+ const second = await renderUriPage('b');
192
+ await aTimeout(60);
193
+ const panel = second.shadowRoot!.querySelector('#content > div[selected]');
194
+ expect(panel?.getAttribute('data-self-uri')).to.equal('/b/accounts');
195
+ });
196
+
155
197
  it('restores a tab cycled without the navigation', async () => {
156
198
  const page = await renderPage();
157
199
  await aTimeout(40);
@@ -378,6 +420,135 @@ describe('<zn-page>', () => {
378
420
  expect(activeItem?.getAttribute('tab')).to.equal('details');
379
421
  });
380
422
 
423
+ it('opens a tab from a tab attribute inside a uri tab, which loads into the page\'s shadow root', async () => {
424
+ const el = await fixture<ZnPage>(html`
425
+ <zn-page caption="Uri Tab Link">
426
+ <zn-tab caption="Details" id="details">Details Content</zn-tab>
427
+ <zn-tab caption="Publish" uri="/publish-tab"></zn-tab>
428
+ </zn-page>
429
+ `);
430
+ await aTimeout(40);
431
+
432
+ const navbar = el.shadowRoot!.querySelector('zn-navbar')!;
433
+ navbar.shadowRoot!.querySelector<HTMLElement>('li[tab-uri="/publish-tab"]')!.click();
434
+ await aTimeout(100);
435
+
436
+ const panel = el.shadowRoot!.querySelector<HTMLElement>('#content > div[data-self-uri="/publish-tab"]')!;
437
+ const link = document.createElement('span');
438
+ link.setAttribute('tab', 'details');
439
+ link.textContent = 'Edit in Details';
440
+ panel.replaceChildren(link);
441
+ expect(el.getAttribute('active')).to.not.equal('details');
442
+
443
+ link.click();
444
+ await aTimeout(40);
445
+ expect(el.getAttribute('active')).to.equal('details');
446
+ });
447
+
448
+ it('shows tabs inside a tab as a pill tab set without a header', async () => {
449
+ const el = await fixture<ZnPage>(html`
450
+ <zn-page caption="Outer Page">
451
+ <zn-tab caption="Overview">
452
+ <zn-tab caption="Invoices">Invoices Content</zn-tab>
453
+ <zn-tab caption="Payments" selected>Payments Content</zn-tab>
454
+ </zn-tab>
455
+ <zn-tab caption="Other">Other Content</zn-tab>
456
+ </zn-page>
457
+ `);
458
+ await aTimeout(50);
459
+
460
+ const group = el.querySelector<ZnPage>('zn-tab > zn-page')!;
461
+ expect(group.getAttribute('navigation-style')).to.equal('pills');
462
+ expect(group.querySelectorAll(':scope > zn-tab').length).to.equal(2);
463
+ expect(el.shadowRoot!.querySelector('zn-navbar')!.hasAttribute('pill')).to.be.false;
464
+
465
+ const navbar = group.shadowRoot!.querySelector('zn-navbar')!;
466
+ expect(navbar.hasAttribute('pill')).to.be.true;
467
+ expect(Array.from(navbar.querySelectorAll('li')).map(li => li.textContent?.trim())).to.deep.equal(['Invoices', 'Payments']);
468
+ expect(getComputedStyle(group.shadowRoot!.querySelector('.header')!).display).to.equal('none');
469
+ expect(group.getAttribute('active')).to.equal('payments');
470
+
471
+ // An app sidebar restyles the group, which must still sit inside the parent's padding without adding its own
472
+ group.setAttribute('navigation-style', 'sidebar');
473
+ await aTimeout(20);
474
+ expect(getComputedStyle(group.shadowRoot!.querySelector('#content')!).paddingLeft).to.equal('0px');
475
+ });
476
+
477
+ it('keeps a nested page\'s tab bar and header', async () => {
478
+ const el = await fixture<ZnPage>(html`
479
+ <zn-page caption="Outer Page">
480
+ <zn-tab caption="Overview">
481
+ <zn-page caption="Inner Page" nested>
482
+ <zn-tab caption="Overview">Inner Overview</zn-tab>
483
+ <zn-tab caption="Details">Inner Details</zn-tab>
484
+ </zn-page>
485
+ </zn-tab>
486
+ <zn-tab caption="Other">Other Content</zn-tab>
487
+ </zn-page>
488
+ `);
489
+ await aTimeout(20);
490
+
491
+ const inner = el.querySelector<ZnPage>('zn-page')!;
492
+ expect(inner.shadowRoot!.querySelector('zn-navbar')!.hasAttribute('pill')).to.be.false;
493
+ expect(getComputedStyle(inner.shadowRoot!.querySelector('.header')!).display).to.not.equal('none');
494
+ });
495
+
496
+ it('gives a nested page\'s header the card title style and a single inset', async () => {
497
+ const el = await fixture<ZnPage>(html`
498
+ <zn-page caption="Outer Page">
499
+ <zn-tab caption="Overview">
500
+ <zn-page caption="Inner Page" summary="Inner summary" nested>
501
+ <zn-tab caption="Overview">Inner Overview</zn-tab>
502
+ <zn-tab caption="Details">Inner Details</zn-tab>
503
+ </zn-page>
504
+ </zn-tab>
505
+ </zn-page>
506
+ `);
507
+ await aTimeout(20);
508
+
509
+ const inner = el.querySelector<ZnPage>('zn-page')!;
510
+ inner.style.setProperty('--zn-text-h1-font-size', '14px');
511
+ inner.style.setProperty('--zn-base-gap', '16px');
512
+ const root = inner.shadowRoot!;
513
+ const header = getComputedStyle(root.querySelector('.header')!);
514
+ expect(header.paddingLeft).to.equal('16px');
515
+ expect(header.paddingRight).to.equal('16px');
516
+ expect(getComputedStyle(root.querySelector('.header .content')!).maxWidth).to.equal('100%');
517
+ expect(getComputedStyle(root.querySelector('.header__caption')!).fontSize).to.equal('14px');
518
+ expect(getComputedStyle(root.querySelector('.header__left')!).rowGap).to.equal('0px');
519
+ });
520
+
521
+ it('hides an empty header', async () => {
522
+ const el = await fixture<ZnPage>(html`
523
+ <zn-page>
524
+ <zn-tab caption="Overview">Overview Content</zn-tab>
525
+ <zn-tab caption="Details">Details Content</zn-tab>
526
+ </zn-page>
527
+ `);
528
+ await aTimeout(20);
529
+
530
+ expect(getComputedStyle(el.shadowRoot!.querySelector('.header')!).display).to.equal('none');
531
+ });
532
+
533
+ it('hides its navbar in the sidebar style unless it has bottom content', async () => {
534
+ const el = await fixture<ZnPage>(html`
535
+ <zn-page caption="Page Title" navigation-style="sidebar">
536
+ <zn-tab caption="Overview">Overview Content</zn-tab>
537
+ <zn-tab caption="Details">Details Content</zn-tab>
538
+ </zn-page>
539
+ `);
540
+ await aTimeout(20);
541
+
542
+ const navbar = el.shadowRoot!.querySelector('zn-navbar')!;
543
+ expect(getComputedStyle(navbar).display).to.equal('none');
544
+
545
+ const chip = document.createElement('zn-chip');
546
+ chip.slot = 'bottom';
547
+ el.append(chip);
548
+ await aTimeout(20);
549
+ expect(getComputedStyle(navbar).display).to.not.equal('none');
550
+ });
551
+
381
552
  it('leaves content tab links inside a nested page to that page', async () => {
382
553
  const outerPage = await fixture<ZnPage>(html`
383
554
  <zn-page caption="Nested Content Tab Link">
@@ -492,9 +663,9 @@ describe('<zn-page>', () => {
492
663
  const el = await fixture<ZnPage>(html`
493
664
  <zn-page caption="Page Title">
494
665
  <zn-tab caption="Later">Later Content</zn-tab>
495
- <zn-tab caption="Second" priority="20">Second Content</zn-tab>
666
+ <zn-tab caption="Second" priority="10">Second Content</zn-tab>
496
667
  <zn-tab caption="Overview">Overview Content</zn-tab>
497
- <zn-tab caption="First" priority="10">First Content</zn-tab>
668
+ <zn-tab caption="First" priority="20">First Content</zn-tab>
498
669
  <zn-tab caption="Dynamic" priority="15" uri="/dynamic"></zn-tab>
499
670
  </zn-page>
500
671
  `);
@@ -514,6 +685,22 @@ describe('<zn-page>', () => {
514
685
  expect(el.querySelector<HTMLElement>('zn-tab[caption="Dynamic"]')!.getAttribute('id')).to.equal('dynamic');
515
686
  });
516
687
 
688
+ it('shows a tab icon before its caption', async () => {
689
+ const el = await fixture<ZnPage>(html`
690
+ <zn-page caption="Page Title">
691
+ <zn-tab caption="Overview">Overview Content</zn-tab>
692
+ <zn-tab caption="History" icon="history" uri="/history"></zn-tab>
693
+ </zn-page>
694
+ `);
695
+ await aTimeout(20);
696
+
697
+ const navbar = el.shadowRoot!.querySelector('zn-navbar')!;
698
+ const historyItem = navbar.shadowRoot!.querySelector('li[tab-uri="/history"]')!;
699
+ expect(historyItem.querySelector('zn-icon')!.getAttribute('src')).to.equal('history');
700
+ expect(historyItem.textContent?.trim()).to.equal('History');
701
+ expect(navbar.shadowRoot!.querySelector('li[tab=""] zn-icon')).to.be.null;
702
+ });
703
+
517
704
  it('preserves source order for tabs without priority', async () => {
518
705
  const el = await fixture<ZnPage>(html`
519
706
  <zn-page caption="Page Title">
@@ -703,7 +890,8 @@ describe('<zn-page>', () => {
703
890
  await aTimeout(120);
704
891
 
705
892
  expect(getComputedStyle(activeNavItem).backgroundColor).to.equal('rgb(1, 2, 3)');
706
- expect(getComputedStyle(activeNavItem).transitionDuration).to.equal('0.1s');
893
+ expect(getComputedStyle(activeNavItem).transitionProperty).to.equal('background-color, color');
894
+ expect(getComputedStyle(activeNavItem).transitionDuration).to.equal('0.1s, 0.15s');
707
895
  });
708
896
 
709
897
  it('opens the tab flagged with the selected attribute instead of the first', async () => {
@@ -737,4 +925,27 @@ describe('<zn-page>', () => {
737
925
  expect(header.classList.contains('header--has-breadcrumb')).to.equal(false);
738
926
  expect(el.shadowRoot!.querySelector('slot[name="breadcrumb"]')).to.equal(null);
739
927
  });
928
+
929
+ it('keeps breadcrumbs inline in the title by default', async () => {
930
+ const el = await fixture<ZnPage>(html`
931
+ <zn-page caption="Performance"><a href="#" slot="breadcrumb">Quality Assurance</a></zn-page>`);
932
+ const crumbs = el.shadowRoot!.querySelector('.header__crumbs')!;
933
+
934
+ expect(getComputedStyle(crumbs).display).to.equal('contents');
935
+ expect(getComputedStyle(el.shadowRoot!.querySelector('.breadcrumb__current')!).display).to.equal('none');
936
+ });
937
+
938
+ it('lifts breadcrumbs onto their own row when the theme asks', async () => {
939
+ const el = await fixture<ZnPage>(html`
940
+ <zn-page caption="Performance"
941
+ style="--zn-page-breadcrumb-display: flex; --zn-page-breadcrumb-current-display: inline; --zn-page-breadcrumb-separator-icon: 'chevron-right@lu'">
942
+ <a href="#" slot="breadcrumb">Quality Assurance</a>
943
+ </zn-page>`);
944
+ await el.updateComplete;
945
+ const root = el.shadowRoot!;
946
+
947
+ expect(getComputedStyle(root.querySelector('.header__crumbs')!).display).to.equal('flex');
948
+ expect(root.querySelector('.breadcrumb__current')!.textContent).to.equal('Performance');
949
+ expect(root.querySelector('.breadcrumb__separator')!.getAttribute('src')).to.contain('chevron-right');
950
+ });
740
951
  });
@@ -721,6 +721,12 @@ export default class ZnPageBuilder extends ZincElement {
721
721
  const [removed, sections] = this._extract(id);
722
722
  if (!removed) return;
723
723
  this._pushHistory();
724
+ const cardIds = [...(this.shadowRoot?.querySelectorAll<HTMLElement>('zn-page-section-card') ?? [])]
725
+ .map(card => card.dataset.id!)
726
+ .filter(cardId => !removed.children?.some(c => c?.id === cardId));
727
+ const at = cardIds.indexOf(id);
728
+ const neighbour = cardIds[at + 1] ?? cardIds[at - 1];
729
+ const hadFocus = !!this.shadowRoot?.activeElement?.closest('zn-page-section-card');
724
730
  // Cards are rendered unkeyed, so the focused action button survives the
725
731
  // re-render as part of whichever section shifts into this slot.
726
732
  this._blurActiveCard();
@@ -730,6 +736,7 @@ export default class ZnPageBuilder extends ZincElement {
730
736
  }
731
737
  // Removing the leading section hands the lead to the next one that may hold it.
732
738
  this._commit({ sections: this._requireFirst(sections) });
739
+ if (hadFocus && neighbour) void this._focusCard(neighbour);
733
740
  }
734
741
 
735
742
  private _duplicateSection(id: string) {
@@ -1023,7 +1030,16 @@ export default class ZnPageBuilder extends ZincElement {
1023
1030
  };
1024
1031
 
1025
1032
  private _onCardKeydown(e: KeyboardEvent, id: string) {
1026
- if (e.key === 'Delete' || e.key === 'Backspace') {
1033
+ if (e.altKey && (e.key === 'ArrowUp' || e.key === 'ArrowDown')) {
1034
+ e.preventDefault();
1035
+ const index = this._state.sections.findIndex(s => s.id === id);
1036
+ if (index === -1) return;
1037
+ // _moveSection inserts before the given index, so moving down one skips past the next card
1038
+ const target = e.key === 'ArrowUp' ? index - 1 : index + 2;
1039
+ if (target > this._state.sections.length) return;
1040
+ this._moveSection(id, target);
1041
+ void this._focusCard(id);
1042
+ } else if (e.key === 'Delete' || e.key === 'Backspace') {
1027
1043
  e.preventDefault();
1028
1044
  this._removeSection(id);
1029
1045
  } else if (e.key === 'Enter' || e.key === ' ') {
@@ -1226,9 +1242,20 @@ export default class ZnPageBuilder extends ZincElement {
1226
1242
  }
1227
1243
  }}"
1228
1244
  @drop="${(e: DragEvent) => this._onSlotDrop(e, container.id, index)}"
1245
+ role="button"
1246
+ tabindex="0"
1247
+ aria-label="Add section"
1248
+ aria-expanded="${pickerOpen}"
1229
1249
  @click="${(e: Event) => {
1230
1250
  e.stopPropagation();
1231
1251
  this._slotPicker = pickerOpen ? null : { containerId: container.id, index };
1252
+ if (!pickerOpen) void this._focusFirstPickerItem(e.currentTarget as HTMLElement);
1253
+ }}"
1254
+ @keydown="${(e: KeyboardEvent) => {
1255
+ // The open picker renders inside this slot; its keys are its own
1256
+ if (e.target !== e.currentTarget || (e.key !== 'Enter' && e.key !== ' ')) return;
1257
+ e.preventDefault();
1258
+ (e.currentTarget as HTMLElement).click();
1232
1259
  }}">
1233
1260
  <zn-icon src="add" size="16"></zn-icon>
1234
1261
  ${pickerOpen
@@ -1244,10 +1271,26 @@ export default class ZnPageBuilder extends ZincElement {
1244
1271
  !t.slots && (!containerType?.accepts || containerType.accepts.includes(t.type)));
1245
1272
  }
1246
1273
 
1274
+ private async _focusFirstPickerItem(zone: HTMLElement) {
1275
+ await this.updateComplete;
1276
+ zone.querySelector<HTMLElement>('.picker__item')?.focus();
1277
+ }
1278
+
1247
1279
  /** The one type-picker template both drop zones and slot cells render. */
1248
- private _renderTypePicker(types: PageSectionType[], pick: (type: string) => void) {
1280
+ private _renderTypePicker(types: PageSectionType[], pick: (type: string) => PageSection | null) {
1249
1281
  return html`
1250
- <div class="picker" @click="${(e: Event) => e.stopPropagation()}">
1282
+ <div
1283
+ class="picker"
1284
+ @click="${(e: Event) => e.stopPropagation()}"
1285
+ @keydown="${(e: KeyboardEvent) => {
1286
+ if (e.key !== 'Escape') return;
1287
+ e.preventDefault();
1288
+ e.stopPropagation();
1289
+ const zone = (e.currentTarget as HTMLElement).parentElement!;
1290
+ (zone.querySelector<HTMLElement>(':scope > .drop__add') ?? zone).focus();
1291
+ this._pickerIndex = null;
1292
+ this._slotPicker = null;
1293
+ }}">
1251
1294
  ${types.map(type => html`
1252
1295
  <button
1253
1296
  type="button"
@@ -1255,7 +1298,8 @@ export default class ZnPageBuilder extends ZincElement {
1255
1298
  @click="${() => {
1256
1299
  this._pickerIndex = null;
1257
1300
  this._slotPicker = null;
1258
- pick(type.type);
1301
+ const section = pick(type.type);
1302
+ if (section) void this._focusCard(section.id);
1259
1303
  }}">
1260
1304
  <zn-icon src="${type.icon ?? 'widgets'}" library="${ifDefined(type.iconLibrary)}" size="14"></zn-icon>
1261
1305
  ${type.label}
@@ -1278,7 +1322,9 @@ export default class ZnPageBuilder extends ZincElement {
1278
1322
  title="Add section"
1279
1323
  @click="${(e: Event) => {
1280
1324
  e.stopPropagation();
1281
- this._pickerIndex = this._pickerIndex === index ? null : index;
1325
+ const opening = this._pickerIndex !== index;
1326
+ this._pickerIndex = opening ? index : null;
1327
+ if (opening) void this._focusFirstPickerItem((e.currentTarget as HTMLElement).parentElement!);
1282
1328
  }}">
1283
1329
  <zn-icon src="add" size="14"></zn-icon>
1284
1330
  </button>
@@ -533,7 +533,8 @@ zn-page-palette-item {
533
533
  }
534
534
 
535
535
  &:hover &__add,
536
- &--active &__add {
536
+ &--active &__add,
537
+ &__add:focus-visible {
537
538
  opacity: 1;
538
539
  }
539
540
  }
@@ -582,6 +583,10 @@ zn-page-section-card.dragging,
582
583
  border-color: rgb(var(--zn-color-border-active));
583
584
  color: rgba(var(--zn-text), 0.7);
584
585
  }
586
+
587
+ &:focus-visible {
588
+ outline: var(--zn-focus-ring);
589
+ }
585
590
  }
586
591
 
587
592
  &--active {
@@ -45,9 +45,16 @@ export default class ZnPageNav extends ZnTabs {
45
45
 
46
46
  toggleNavigation() {
47
47
  const navigationElement = this.shadowRoot?.querySelector('.navigation');
48
+ const toggle = this.shadowRoot?.querySelector<HTMLElement>('.breadcrumb-menu-toggle');
48
49
  if (navigationElement) {
49
50
  // Toggle the 'active' class to show/hide the navigation
50
- navigationElement.classList.toggle('active');
51
+ const open = navigationElement.classList.toggle('active');
52
+ toggle?.setAttribute('aria-expanded', String(open));
53
+
54
+ // The closed menu is hidden, so focus inside it would be lost. The toggle is only shown on narrow layouts.
55
+ if (!open && navigationElement.contains(this.shadowRoot!.activeElement) && toggle?.checkVisibility()) {
56
+ toggle.focus();
57
+ }
51
58
  }
52
59
  // toggle menu overlay
53
60
  const menuOverlay = this.shadowRoot?.querySelector('.menu-overlay');
@@ -56,6 +63,20 @@ export default class ZnPageNav extends ZnTabs {
56
63
  }
57
64
  }
58
65
 
66
+ private handleToggleKeyDown = (e: KeyboardEvent) => {
67
+ if (e.key === 'Enter' || e.key === ' ') {
68
+ e.preventDefault();
69
+ this.toggleNavigation();
70
+ }
71
+ };
72
+
73
+ private handleNavigationKeyDown = (e: KeyboardEvent) => {
74
+ if (e.key === 'Escape' && (e.currentTarget as HTMLElement).classList.contains('active')) {
75
+ e.stopPropagation();
76
+ this.toggleNavigation();
77
+ }
78
+ };
79
+
59
80
  setActiveTab(tabName: string, store: boolean, refresh: boolean, refTab: string | null = null) {
60
81
  super.setActiveTab(tabName, store, refresh, refTab);
61
82
 
@@ -96,13 +117,14 @@ export default class ZnPageNav extends ZnTabs {
96
117
 
97
118
  return html`
98
119
  <div class="breadcrumb">
99
- <div @click="${this.toggleNavigation}" class="breadcrumb-menu-toggle">
120
+ <div @click="${this.toggleNavigation}" @keydown="${this.handleToggleKeyDown}" class="breadcrumb-menu-toggle"
121
+ role="button" tabindex="0" aria-expanded="false">
100
122
  <zn-icon src="menu" size="24"></zn-icon>
101
123
  <span>${this.breadcrumb}</span>
102
124
  </div>
103
125
  </div>
104
126
  <div class="tabs">
105
- <div class="navigation">
127
+ <div class="navigation" @keydown="${this.handleNavigationKeyDown}">
106
128
  ${navItems}
107
129
  </div>
108
130
  <div class="menu-overlay" @click="${this.toggleNavigation}"></div>
@@ -84,7 +84,7 @@
84
84
  }
85
85
 
86
86
  .navigation-item.active {
87
- color: rgb(var(--zn-primary));
87
+ color: var(--zn-color-primary-fg, rgb(var(--zn-primary)));
88
88
  }
89
89
 
90
90
  .navigation-group + .navigation-group {
@@ -104,6 +104,10 @@
104
104
  }
105
105
 
106
106
  .breadcrumb-menu-toggle {
107
+ &:focus-visible {
108
+ outline: var(--zn-focus-ring);
109
+ }
110
+
107
111
  cursor: pointer;
108
112
  margin-left: var(--zn-base-gap);
109
113
 
@@ -148,7 +152,10 @@
148
152
  height: 100%;
149
153
  bottom: 0;
150
154
  z-index: var(--zn-z-index-page-nav);
151
- transition: left var(--page-nav-animation-duration) ease-in-out, opacity var(--page-nav-animation-duration) ease-in-out;
155
+ // Visibility keeps the closed menu out of the tab order; it flips only after the slide-out finishes
156
+ visibility: hidden;
157
+ transition: left var(--page-nav-animation-duration) ease-in-out, opacity var(--page-nav-animation-duration) ease-in-out,
158
+ visibility var(--page-nav-animation-duration);
152
159
  }
153
160
 
154
161
  .tabs {
@@ -158,6 +165,7 @@
158
165
  }
159
166
 
160
167
  .navigation.active {
168
+ visibility: visible;
161
169
  pointer-events: all;
162
170
  left: 0;
163
171
  opacity: 1;
@@ -69,7 +69,7 @@
69
69
  }
70
70
 
71
71
  &__button--active {
72
- color: rgb(var(--zn-primary)) !important;
72
+ color: var(--zn-color-primary-fg, rgb(var(--zn-primary))) !important;
73
73
  font-weight: 600 !important;
74
74
  pointer-events: none;
75
75
  }
@@ -1,7 +1,9 @@
1
+ import {ifDefined} from "lit/directives/if-defined.js";
1
2
  import {classMap} from "lit/directives/class-map.js";
2
3
  import {type CSSResultGroup, html, type PropertyValues, unsafeCSS} from 'lit';
3
4
  import {HasSlotController} from "../../internal/slot";
4
5
  import {property, state} from 'lit/decorators.js';
6
+ import {registerCosmicBorder} from "../../internal/cosmic-border";
5
7
  import ZincElement from '../../internal/zinc-element';
6
8
 
7
9
  import styles from './panel.scss';
@@ -19,11 +21,14 @@ import styles from './panel.scss';
19
21
  * @csspart base - The component's base wrapper.
20
22
  * @csspart header - The header region, when a caption or actions are given.
21
23
  * @csspart header-content - The padded row inside the header, forwarded from zn-header.
24
+ * @csspart header-left - The caption and description, forwarded from zn-header.
22
25
  * @csspart footer - The footer region, when the footer slot is filled.
23
26
  *
24
27
  * @cssproperty --zn-panel-basis - The flex-basis of the panel. Can be set using the basis-px attribute.
25
28
  * @cssproperty --zn-panel-header-padding - Padding around the header row. Defaults to `--zn-base-gap`.
26
29
  * @cssproperty --zn-panel-footer-padding - Padding around the footer row.
30
+ * @cssproperty --zn-panel-padding - Padding around the body. Defaults to `--zn-base-gap`.
31
+ * @cssproperty --zn-panel-shadow - Resting shadow. Defaults to none.
27
32
  */
28
33
  export default class ZnPanel extends ZincElement {
29
34
  static styles: CSSResultGroup = unsafeCSS(styles);
@@ -32,6 +37,11 @@ export default class ZnPanel extends ZincElement {
32
37
 
33
38
  @property({attribute: 'basis-px', type: Number}) basis: number;
34
39
  @property() caption: string;
40
+ /** Help text under the caption, in the panel's header. */
41
+ @property() description: string;
42
+
43
+ /** Sets the description beside the caption rather than under it. */
44
+ @property({attribute: 'inline-description', type: Boolean}) inlineDescription = false;
35
45
  @property() icon: string;
36
46
  @property({type: Boolean}) tabbed: boolean;
37
47
  @property({attribute: 'header-borderless', type: Boolean}) headerBorderless: boolean;
@@ -72,18 +82,7 @@ export default class ZnPanel extends ZincElement {
72
82
  this.observeBodyContent();
73
83
  }
74
84
 
75
- if (window.CSS.registerProperty) {
76
- try {
77
- window.CSS.registerProperty({
78
- inherits: false,
79
- initialValue: '0deg',
80
- name: '--rotate',
81
- syntax: '<angle>',
82
- });
83
- } catch (e) {
84
- // do nothing
85
- }
86
- }
85
+ registerCosmicBorder();
87
86
  }
88
87
 
89
88
  disconnectedCallback() {
@@ -142,13 +141,15 @@ export default class ZnPanel extends ZincElement {
142
141
  <div class="panel__inner">
143
142
  ${hasHeader ? html`
144
143
  <zn-header part="header"
145
- exportparts="content:header-content"
144
+ exportparts="content:header-content, header-left"
146
145
  class="${classMap({
147
146
  "panel__header": true,
148
147
  "panel__header--underline": underlineHeader,
149
148
  })}"
150
149
  icon="${this.icon}"
151
150
  caption="${this.caption}"
151
+ description="${ifDefined(this.description)}"
152
+ ?inline-description="${this.inlineDescription}"
152
153
  transparent>
153
154
  ${hasActionSlot ? html`
154
155
  <slot name="actions" slot="actions" class="panel__header__actions"></slot>` : null}
@@ -19,7 +19,8 @@
19
19
  position: relative;
20
20
  border: 1px solid rgb(var(--zn-border-color));
21
21
  display: flex;
22
- border-radius: var(--zn-border-radius);
22
+ border-radius: var(--zn-panel-border-radius, var(--zn-border-radius));
23
+ box-shadow: var(--zn-panel-shadow, none);
23
24
  flex-direction: column;
24
25
  flex-grow: 1;
25
26
  flex-basis: var(--zn-spacing-small-panel);
@@ -29,7 +30,7 @@
29
30
  &__inner {
30
31
  overflow: hidden;
31
32
  display: flex;
32
- border-radius: var(--zn-border-radius);
33
+ border-radius: var(--zn-panel-border-radius, var(--zn-border-radius));
33
34
  flex-direction: column;
34
35
  flex-grow: 1;
35
36
  }
@@ -58,7 +59,7 @@
58
59
  }
59
60
 
60
61
  &--underline {
61
- border-bottom: 1px solid rgb(var(--zn-border-color));
62
+ border-bottom: 1px solid var(--zn-panel-header-divider-color, rgb(var(--zn-border-color)));
62
63
  }
63
64
 
64
65
  &::part(content) {
@@ -71,7 +72,7 @@
71
72
  flex-direction: column;
72
73
  overflow-y: auto;
73
74
  flex-grow: 1;
74
- padding: var(--zn-base-gap);
75
+ padding: var(--zn-panel-padding, var(--zn-base-gap));
75
76
 
76
77
  > :not([hidden]) ~ :not([hidden]) {
77
78
  border: 1px solid rgb(var(--zn-border-color));
@@ -82,9 +83,10 @@
82
83
  font-size: var(--zn-font-size-small);
83
84
  display: block;
84
85
  padding: var(--zn-panel-footer-padding, var(--zn-spacing-small) var(--zn-base-gap));
85
- background: rgb(var(--zn-body));
86
- border-bottom-left-radius: var(--zn-border-radius);
87
- border-bottom-right-radius: var(--zn-border-radius);
86
+ background: var(--zn-panel-footer-background, rgb(var(--zn-body)));
87
+ border-top: var(--zn-panel-footer-divider, none);
88
+ border-bottom-left-radius: var(--zn-panel-border-radius, var(--zn-border-radius));
89
+ border-bottom-right-radius: var(--zn-panel-border-radius, var(--zn-border-radius));
88
90
  }
89
91
 
90
92
  &--tabbed .panel__body {
@@ -94,6 +96,7 @@
94
96
  &--transparent {
95
97
  background-color: transparent;
96
98
  border: none;
99
+ box-shadow: none;
97
100
 
98
101
  &:before {
99
102
  display: none;
@@ -145,14 +148,15 @@
145
148
  // The body scrolls, so it can't take a clip margin — it gets a gutter cancelled by a negative margin instead.
146
149
  &--transparent {
147
150
  .panel__header {
151
+ position: relative;
152
+ z-index: 1;
148
153
  padding-left: 0;
149
154
  padding-right: 0;
150
155
  }
151
156
 
152
157
  .panel__inner,
153
158
  .panel__content {
154
- overflow: clip;
155
- overflow-clip-margin: var(--zn-focus-ring-outset);
159
+ @include wc.clip-focus-room;
156
160
  }
157
161
 
158
162
  &.panel--flush .panel__body {
@@ -176,7 +180,7 @@
176
180
  }
177
181
 
178
182
  &--shadow {
179
- box-shadow: 0 6px 20px 10px rgba(0, 0, 0, 0.25);
183
+ box-shadow: var(--zn-panel-shadow-raised, 0 6px 20px 10px rgba(0, 0, 0, 0.25));
180
184
  }
181
185
 
182
186
  }