@kubex/zinc 1.1.185 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (358) hide show
  1. package/.browserslistrc +2 -0
  2. package/.github/workflows/js_build_and_deploy.yaml +54 -1
  3. package/.github/workflows/test.yaml +27 -0
  4. package/dist/chunks/zn.UWJLIXDT.js +1 -0
  5. package/dist/custom-elements.json +10319 -2666
  6. package/dist/vscode.html-custom-data.json +1012 -57
  7. package/dist/web-types.json +2097 -145
  8. package/dist/zn.d.ts +2294 -416
  9. package/dist/zn.min.css +1 -1
  10. package/dist/zn.min.js +2310 -1637
  11. package/docs/_includes/default.njk +4 -1
  12. package/docs/assets/scripts/docs.js +22 -6
  13. package/docs/assets/styles/docs.css +9 -2
  14. package/docs/data/report-table-day-compare.json +380 -0
  15. package/docs/data/report-table.json +314 -0
  16. package/docs/pages/components/avatar-group.md +57 -0
  17. package/docs/pages/components/badge.md +67 -0
  18. package/docs/pages/components/button.md +41 -1
  19. package/docs/pages/components/chat-message.md +16 -0
  20. package/docs/pages/components/checkbox.md +1 -1
  21. package/docs/pages/components/chip.md +25 -13
  22. package/docs/pages/components/color-picker.md +37 -0
  23. package/docs/pages/components/copy-button.md +2 -2
  24. package/docs/pages/components/data-table-filter.md +33 -50
  25. package/docs/pages/components/data-table-search.md +4 -4
  26. package/docs/pages/components/data-table.md +27 -3
  27. package/docs/pages/components/date-range.md +116 -0
  28. package/docs/pages/components/dialog.md +29 -37
  29. package/docs/pages/components/diff-row.md +28 -0
  30. package/docs/pages/components/diff.md +46 -0
  31. package/docs/pages/components/editor.md +55 -0
  32. package/docs/pages/components/icon.md +46 -1
  33. package/docs/pages/components/input.md +30 -11
  34. package/docs/pages/components/item.md +35 -0
  35. package/docs/pages/components/key-value.md +25 -0
  36. package/docs/pages/components/menu.md +74 -7
  37. package/docs/pages/components/nav-item.md +15 -0
  38. package/docs/pages/components/nav-list.md +132 -0
  39. package/docs/pages/components/navbar.md +100 -1
  40. package/docs/pages/components/page.md +242 -74
  41. package/docs/pages/components/priority-list.md +43 -5
  42. package/docs/pages/components/query-builder.md +23 -0
  43. package/docs/pages/components/radio-group.md +33 -1
  44. package/docs/pages/components/radio.md +1 -1
  45. package/docs/pages/components/report-table.md +202 -0
  46. package/docs/pages/components/stat.md +30 -0
  47. package/docs/pages/components/tile.md +11 -0
  48. package/docs/pages/components/tooltip.md +1 -1
  49. package/docs/pages/components/translation-grid.md +86 -0
  50. package/docs/pages/components/vertical-stepper.md +108 -64
  51. package/package.json +2 -1
  52. package/scss/_global-spacing.scss +2 -2
  53. package/scss/_root.scss +11 -171
  54. package/scss/boot.scss +14 -0
  55. package/scss/mixins/_helper-mixins.scss +24 -2
  56. package/scss/mixins/_motion-mixins.scss +126 -0
  57. package/scss/mixins/_spacing-mixins.scss +4 -3
  58. package/scss/mixins/_tab-indicator-mixins.scss +12 -0
  59. package/scss/mixins/_typography-mixins.scss +1 -1
  60. package/scss/mixins/index.scss +14 -0
  61. package/scss/shared/_base.scss +32 -1
  62. package/scss/shared/_button-mixin.scss +7 -5
  63. package/scss/shared/_table.scss +35 -4
  64. package/scss/shared/nav.scss +4 -0
  65. package/scss/shared/shimmer.scss +1 -1
  66. package/scss/table-controls.scss +206 -0
  67. package/scss/themes/_dark.scss +1 -3
  68. package/scss/themes/_index.scss +1 -1
  69. package/scss/themes/_light.scss +46 -6
  70. package/scss/themes/_modes.scss +11 -11
  71. package/scss/themes/_openbyte.scss +944 -0
  72. package/scss/themes/_typography.scss +4 -2
  73. package/scss/variables/index.scss +0 -9
  74. package/src/components/alert/alert.component.ts +14 -4
  75. package/src/components/alert/alert.scss +55 -69
  76. package/src/components/alert/alert.test.ts +8 -0
  77. package/src/components/animated-button/animated-button.component.ts +3 -2
  78. package/src/components/animated-button/animated-button.scss +6 -23
  79. package/src/components/animated-button/animated-button.test.ts +4 -3
  80. package/src/components/avatar-group/avatar-group.component.ts +84 -0
  81. package/src/components/avatar-group/avatar-group.scss +54 -0
  82. package/src/components/avatar-group/avatar-group.test.ts +59 -0
  83. package/src/components/avatar-group/index.ts +12 -0
  84. package/src/components/badge/badge.component.ts +49 -0
  85. package/src/components/badge/badge.scss +80 -0
  86. package/src/components/badge/badge.test.ts +34 -0
  87. package/src/components/badge/index.ts +12 -0
  88. package/src/components/bulk-actions/bulk-actions.component.ts +14 -2
  89. package/src/components/button/button.component.ts +228 -8
  90. package/src/components/button/button.scss +115 -32
  91. package/src/components/button/button.test.ts +122 -0
  92. package/src/components/button-menu/button-menu.component.ts +32 -11
  93. package/src/components/button-menu/button-menu.scss +5 -1
  94. package/src/components/channel-tile/channel-tile.component.ts +34 -27
  95. package/src/components/channel-tile/channel-tile.scss +76 -43
  96. package/src/components/chart/builders.test.ts +46 -0
  97. package/src/components/chart/builders.ts +27 -3
  98. package/src/components/chart/chart-live.test.ts +4 -6
  99. package/src/components/chart/chart.component.ts +125 -5
  100. package/src/components/chart/chart.scss +121 -0
  101. package/src/components/chart/chart.test.ts +81 -0
  102. package/src/components/chat-message/chat-message.component.ts +21 -1
  103. package/src/components/chat-message/chat-message.scss +13 -2
  104. package/src/components/chat-message/chat-message.test.ts +9 -0
  105. package/src/components/checkbox/checkbox.component.ts +61 -27
  106. package/src/components/checkbox/checkbox.scss +58 -25
  107. package/src/components/checkbox-group/checkbox-group.component.ts +7 -1
  108. package/src/components/chip/chip.component.ts +11 -1
  109. package/src/components/chip/chip.scss +91 -26
  110. package/src/components/chip/chip.test.ts +17 -0
  111. package/src/components/collapsible/collapsible.component.ts +17 -3
  112. package/src/components/collapsible/collapsible.scss +19 -10
  113. package/src/components/color-picker/color-picker.component.ts +277 -0
  114. package/src/components/color-picker/color-picker.scss +141 -0
  115. package/src/components/color-picker/color-picker.test.ts +57 -0
  116. package/src/components/color-picker/index.ts +12 -0
  117. package/src/components/cols/cols.component.ts +10 -0
  118. package/src/components/cols/cols.test.ts +17 -0
  119. package/src/components/confirm/confirm.component.ts +20 -6
  120. package/src/components/confirm/confirm.test.ts +52 -0
  121. package/src/components/content-block/content-block.component.ts +38 -9
  122. package/src/components/content-block/content-block.scss +11 -5
  123. package/src/components/content-block/content-block.test.ts +26 -0
  124. package/src/components/copy-button/copy-button.component.ts +2 -2
  125. package/src/components/copy-button/copy-button.scss +17 -4
  126. package/src/components/data-select/data-select.component.ts +3 -1
  127. package/src/components/data-table/data-table.component.ts +362 -140
  128. package/src/components/data-table/data-table.scss +136 -60
  129. package/src/components/data-table/data-table.test.ts +356 -4
  130. package/src/components/data-table-filter/data-table-filter.component.ts +426 -406
  131. package/src/components/data-table-filter/data-table-filter.scss +176 -148
  132. package/src/components/data-table-filter/data-table-filter.test.ts +271 -273
  133. package/src/components/data-table-search/data-table-search.component.ts +4 -2
  134. package/src/components/data-table-search/data-table-search.scss +8 -1
  135. package/src/components/data-table-search/data-table-search.test.ts +29 -0
  136. package/src/components/date-range/date-range-math.test.ts +78 -0
  137. package/src/components/date-range/date-range-math.ts +105 -0
  138. package/src/components/date-range/date-range.component.ts +375 -0
  139. package/src/components/date-range/date-range.scss +272 -0
  140. package/src/components/date-range/date-range.test.ts +247 -0
  141. package/src/components/date-range/index.ts +12 -0
  142. package/src/components/datepicker/datepicker.component.ts +44 -7
  143. package/src/components/datepicker/datepicker.scss +9 -0
  144. package/src/components/datepicker/datepicker.test.ts +2 -2
  145. package/src/components/dialog/dialog.component.ts +38 -19
  146. package/src/components/dialog/dialog.scss +65 -36
  147. package/src/components/dialog/dialog.test.ts +14 -0
  148. package/src/components/diff/diff.component.ts +55 -0
  149. package/src/components/diff/diff.scss +46 -0
  150. package/src/components/diff/diff.test.ts +15 -0
  151. package/src/components/diff/index.ts +12 -0
  152. package/src/components/diff-row/diff-row.component.ts +75 -0
  153. package/src/components/diff-row/diff-row.scss +70 -0
  154. package/src/components/diff-row/diff-row.test.ts +37 -0
  155. package/src/components/diff-row/index.ts +12 -0
  156. package/src/components/dropdown/dropdown.component.ts +12 -10
  157. package/src/components/dropdown/dropdown.scss +2 -0
  158. package/src/components/editor/editor.component.ts +159 -11
  159. package/src/components/editor/editor.scss +250 -1
  160. package/src/components/editor/editor.test.ts +31 -3
  161. package/src/components/editor/modules/ai/panel/ai-panel.scss +0 -1
  162. package/src/components/editor/modules/attachment/attachment.ts +53 -31
  163. package/src/components/editor/modules/context-menu/context-menu.ts +17 -0
  164. package/src/components/editor/modules/dialog/dialog.component.ts +23 -1
  165. package/src/components/editor/modules/dialog/dialog.test.ts +28 -0
  166. package/src/components/editor/modules/emoji/emoji.ts +4 -5
  167. package/src/components/editor/modules/merge-field/merge-field.ts +83 -0
  168. package/src/components/editor/modules/toolbar/toolbar.component.ts +121 -68
  169. package/src/components/editor/modules/toolbar/toolbar.scss +38 -0
  170. package/src/components/editor/modules/toolbar/toolbar.ts +25 -1
  171. package/src/components/empty-state/empty-state.component.ts +6 -1
  172. package/src/components/empty-state/empty-state.scss +21 -4
  173. package/src/components/expanding-action/expanding-action.component.ts +22 -34
  174. package/src/components/expanding-action/expanding-action.scss +3 -45
  175. package/src/components/expanding-action/expanding-action.test.ts +2 -1
  176. package/src/components/file/file.component.ts +30 -2
  177. package/src/components/file/file.scss +6 -0
  178. package/src/components/flow-builder/flow-builder.component.ts +36 -3
  179. package/src/components/flow-builder/flow-builder.scss +0 -6
  180. package/src/components/flow-builder/modules/flow-canvas/flow-canvas.component.ts +8 -0
  181. package/src/components/flow-builder/modules/flow-canvas/flow-canvas.scss +10 -5
  182. package/src/components/flow-builder/modules/flow-node/flow-node.component.ts +17 -0
  183. package/src/components/flow-builder/modules/flow-node/flow-node.scss +10 -0
  184. package/src/components/form-actions/form-actions.component.ts +48 -2
  185. package/src/components/form-actions/form-actions.scss +1 -1
  186. package/src/components/form-group/form-group.component.ts +10 -3
  187. package/src/components/form-group/form-group.scss +37 -4
  188. package/src/components/form-group/form-group.test.ts +10 -0
  189. package/src/components/header/header.component.ts +11 -4
  190. package/src/components/header/header.scss +47 -5
  191. package/src/components/hover-container/hover-container.component.ts +4 -5
  192. package/src/components/icon/icon.component.ts +95 -9
  193. package/src/components/icon/icon.scss +70 -6
  194. package/src/components/icon/icon.test.ts +55 -0
  195. package/src/components/icon-picker/icon-picker.component.ts +35 -1
  196. package/src/components/inline-edit/inline-edit.component.ts +38 -8
  197. package/src/components/inline-edit/inline-edit.scss +128 -19
  198. package/src/components/input/input.component.ts +173 -297
  199. package/src/components/input/input.scss +65 -43
  200. package/src/components/input/input.test.ts +86 -4
  201. package/src/components/item/item.component.ts +12 -0
  202. package/src/components/item/item.scss +102 -3
  203. package/src/components/item/item.test.ts +23 -0
  204. package/src/components/key/key.component.ts +2 -2
  205. package/src/components/key/key.scss +11 -0
  206. package/src/components/key-value/index.ts +12 -0
  207. package/src/components/key-value/key-value.component.ts +160 -0
  208. package/src/components/key-value/key-value.scss +20 -0
  209. package/src/components/key-value/key-value.test.ts +22 -0
  210. package/src/components/markdown-editor/markdown-editor.component.ts +8 -1
  211. package/src/components/menu/menu.component.ts +21 -2
  212. package/src/components/menu/menu.scss +43 -23
  213. package/src/components/menu/menu.test.ts +33 -0
  214. package/src/components/menu-item/menu-item.component.ts +13 -2
  215. package/src/components/menu-item/menu-item.scss +56 -23
  216. package/src/components/menu-item/menu-item.test.ts +31 -0
  217. package/src/components/menu-item/submenu-controller.ts +25 -15
  218. package/src/components/menu-label/index.ts +12 -0
  219. package/src/components/menu-label/menu-label.component.ts +30 -0
  220. package/src/components/menu-label/menu-label.scss +18 -0
  221. package/src/components/nav-item/index.ts +12 -0
  222. package/src/components/nav-item/nav-item.component.ts +83 -0
  223. package/src/components/nav-item/nav-item.scss +128 -0
  224. package/src/components/nav-list/index.ts +12 -0
  225. package/src/components/nav-list/nav-list.component.ts +313 -0
  226. package/src/components/nav-list/nav-list.scss +87 -0
  227. package/src/components/nav-list/nav-list.test.ts +250 -0
  228. package/src/components/navbar/navbar.component.ts +235 -171
  229. package/src/components/navbar/navbar.scss +333 -169
  230. package/src/components/navbar/navbar.test.ts +63 -143
  231. package/src/components/page/page.component.ts +114 -29
  232. package/src/components/page/page.scss +223 -95
  233. package/src/components/page/page.test.ts +214 -3
  234. package/src/components/page-builder/page-builder.component.ts +51 -5
  235. package/src/components/page-builder/page-builder.scss +6 -1
  236. package/src/components/page-nav/page-nav.component.ts +25 -3
  237. package/src/components/page-nav/page-nav.scss +10 -2
  238. package/src/components/pagination/pagination.scss +1 -1
  239. package/src/components/panel/panel.component.ts +14 -13
  240. package/src/components/panel/panel.scss +14 -10
  241. package/src/components/panel/panel.test.ts +42 -0
  242. package/src/components/popup/popup.component.ts +20 -13
  243. package/src/components/popup/popup.test.ts +21 -1
  244. package/src/components/priority-list/priority-list.component.ts +157 -172
  245. package/src/components/priority-list/priority-list.scss +113 -17
  246. package/src/components/priority-list/priority-list.test.ts +301 -3
  247. package/src/components/progress-bar/progress-bar.component.ts +1 -1
  248. package/src/components/progress-bar/progress-bar.scss +16 -4
  249. package/src/components/progress-tile/progress-tile.scss +1 -1
  250. package/src/components/query-builder/query-builder.component.ts +85 -10
  251. package/src/components/query-builder/query-builder.scss +7 -1
  252. package/src/components/query-builder/query-builder.test.ts +40 -0
  253. package/src/components/radio/radio.component.ts +57 -18
  254. package/src/components/radio/radio.scss +101 -26
  255. package/src/components/radio-group/radio-group.component.ts +45 -7
  256. package/src/components/radio-group/radio-group.scss +17 -0
  257. package/src/components/radio-group/radio-group.test.ts +63 -2
  258. package/src/components/rating/rating.component.ts +23 -0
  259. package/src/components/rating/rating.scss +1 -1
  260. package/src/components/remarkd-editor/remarkd-editor.component.ts +60 -5
  261. package/src/components/remarkd-editor/remarkd-editor.scss +12 -4
  262. package/src/components/report-table/index.ts +12 -0
  263. package/src/components/report-table/report-format.test.ts +86 -0
  264. package/src/components/report-table/report-format.ts +72 -0
  265. package/src/components/report-table/report-math.test.ts +74 -0
  266. package/src/components/report-table/report-math.ts +252 -0
  267. package/src/components/report-table/report-table.component.ts +681 -0
  268. package/src/components/report-table/report-table.scss +328 -0
  269. package/src/components/report-table/report-table.test.ts +702 -0
  270. package/src/components/reveal/reveal.component.ts +15 -1
  271. package/src/components/reveal/reveal.scss +5 -0
  272. package/src/components/schedule-builder/schedule-builder.component.ts +23 -8
  273. package/src/components/scroll-container/scroll-container.component.ts +19 -1
  274. package/src/components/scroll-container/scroll-container.scss +6 -1
  275. package/src/components/select/select.component.ts +52 -36
  276. package/src/components/select/select.scss +42 -25
  277. package/src/components/select/select.test.ts +14 -2
  278. package/src/components/settings-container/settings-container.scss +18 -5
  279. package/src/components/sidebar/sidebar.component.ts +8 -1
  280. package/src/components/sidebar/sidebar.scss +6 -1
  281. package/src/components/skeleton/skeleton.component.ts +3 -3
  282. package/src/components/skeleton/skeleton.scss +6 -6
  283. package/src/components/skeleton/skeleton.test.ts +9 -0
  284. package/src/components/slash-menu/slash-menu.scss +1 -1
  285. package/src/components/slideout/slideout.component.ts +20 -3
  286. package/src/components/slideout/slideout.scss +13 -6
  287. package/src/components/slideout/slideout.test.ts +19 -0
  288. package/src/components/split-button/split-button.component.ts +1 -1
  289. package/src/components/split-pane/split-pane.component.ts +71 -8
  290. package/src/components/split-pane/split-pane.scss +145 -41
  291. package/src/components/split-pane/split-pane.test.ts +23 -3
  292. package/src/components/stat/stat.component.ts +209 -14
  293. package/src/components/stat/stat.scss +305 -5
  294. package/src/components/stat/stat.test.ts +143 -2
  295. package/src/components/status-indicator/status-indicator.scss +22 -21
  296. package/src/components/stepper/stepper.scss +3 -3
  297. package/src/components/tab/tab.component.ts +1 -0
  298. package/src/components/table/table.component.ts +12 -1
  299. package/src/components/table/table.scss +21 -13
  300. package/src/components/table/table.test.ts +17 -0
  301. package/src/components/tabs/tabs.component.ts +150 -37
  302. package/src/components/tabs/tabs.scss +48 -7
  303. package/src/components/tabs/tabs.test.ts +169 -0
  304. package/src/components/textarea/textarea.component.ts +25 -3
  305. package/src/components/textarea/textarea.scss +10 -0
  306. package/src/components/textarea/textarea.test.ts +8 -4
  307. package/src/components/theme-editor/theme-editor.scss +0 -6
  308. package/src/components/tile/tile.component.ts +32 -2
  309. package/src/components/tile/tile.scss +40 -7
  310. package/src/components/tile/tile.test.ts +10 -0
  311. package/src/components/toggle/toggle.component.ts +23 -7
  312. package/src/components/toggle/toggle.scss +36 -29
  313. package/src/components/toggle/toggle.test.ts +17 -0
  314. package/src/components/tooltip/tooltip.component.ts +3 -0
  315. package/src/components/tooltip/tooltip.scss +11 -6
  316. package/src/components/tooltip/tooltip.test.ts +12 -0
  317. package/src/components/translation-field/index.ts +12 -0
  318. package/src/components/translation-field/translation-field.component.ts +55 -0
  319. package/src/components/translation-field/translation-field.scss +101 -0
  320. package/src/components/translation-fields/index.ts +12 -0
  321. package/src/components/translation-fields/translation-fields.component.ts +80 -0
  322. package/src/components/translation-fields/translation-fields.scss +60 -0
  323. package/src/components/translation-grid/index.ts +12 -0
  324. package/src/components/translation-grid/translation-grid.component.ts +157 -0
  325. package/src/components/translation-grid/translation-grid.scss +138 -0
  326. package/src/components/translation-grid/translation-grid.test.ts +28 -0
  327. package/src/components/vertical-stepper/vertical-stepper.component.ts +35 -9
  328. package/src/components/vertical-stepper/vertical-stepper.scss +110 -39
  329. package/src/components/vertical-stepper/vertical-stepper.test.ts +35 -0
  330. package/src/components/well/well.scss +2 -2
  331. package/src/events/events.ts +2 -0
  332. package/src/events/zn-chart-click.ts +13 -0
  333. package/src/events/zn-report-change.ts +7 -0
  334. package/src/form-control.scss +36 -3
  335. package/src/internal/anchored-popover.ts +175 -0
  336. package/src/internal/animate.ts +19 -5
  337. package/src/internal/cosmic-border.ts +15 -0
  338. package/src/internal/form.ts +1 -1
  339. package/src/internal/hover-intent.ts +15 -0
  340. package/src/internal/input-modality.ts +15 -0
  341. package/src/internal/pointer-intent.ts +37 -0
  342. package/src/internal/reorder.ts +632 -0
  343. package/src/internal/scroll.ts +51 -3
  344. package/src/internal/selection-card.ts +1 -1
  345. package/src/internal/tab-indicator.ts +92 -0
  346. package/src/internal/table-sort.ts +25 -0
  347. package/src/internal/table-tabs.ts +108 -0
  348. package/src/internal/test-drag.ts +35 -0
  349. package/src/internal/theme-openbyte.test.ts +126 -0
  350. package/src/internal/theme.ts +4 -4
  351. package/src/internal/zinc-element.ts +2 -2
  352. package/src/selection-card.scss +25 -0
  353. package/src/types/web-test-runner-commands.d.ts +1 -0
  354. package/src/utilities/color.test.ts +43 -0
  355. package/src/utilities/color.ts +189 -0
  356. package/src/zinc.ts +14 -0
  357. package/.github/workflows/eleventy_build.yml +0 -42
  358. package/scss/themes/_aura.scss +0 -114
@@ -10,6 +10,29 @@ describe('<zn-navbar>', () => {
10
10
  expect(el).to.exist;
11
11
  });
12
12
 
13
+ it('fills the active tab with the sliding indicator when drawn as pills', async () => {
14
+ const el = await fixture<ZnNavbar>(html`
15
+ <zn-navbar pill navigation='[{"tab":"","title":"All","active":true},{"tab":"draft","title":"Draft"}]'></zn-navbar>
16
+ `);
17
+ await aTimeout(100);
18
+
19
+ const indicator = el.shadowRoot!.querySelector<HTMLElement>('[part="indicator"]')!;
20
+ const tab = el.shadowRoot!.querySelector<HTMLElement>('ul.navbar > li')!;
21
+ expect(getComputedStyle(indicator).opacity).to.equal('1');
22
+ expect(indicator.getBoundingClientRect().height).to.be.closeTo(tab.getBoundingClientRect().height, 1);
23
+ });
24
+
25
+ it('spaces a tab off its top edge by --zn-navbar-tab-padding-top', async () => {
26
+ const el = await fixture<ZnNavbar>(html`
27
+ <zn-navbar style="--zn-navbar-tab-padding-top: 13px"
28
+ navigation='[{"tab":"","title":"Charge","active":true},{"tab":"items","title":"Items"}]'></zn-navbar>
29
+ `);
30
+ await aTimeout(100);
31
+
32
+ const tab = el.shadowRoot!.querySelector<HTMLElement>('ul.navbar > li')!;
33
+ expect(getComputedStyle(tab).paddingTop).to.equal('13px');
34
+ });
35
+
13
36
  it('does not move Lit-owned light DOM items when the parent rerenders', async () => {
14
37
  const host = document.createElement('div');
15
38
  document.body.append(host);
@@ -44,187 +67,73 @@ describe('<zn-navbar>', () => {
44
67
  }
45
68
  });
46
69
 
47
- it('reserves at least 200px for visible nav items when expandables need space', async () => {
70
+ it('keeps at least 200px for the tabs beside expandables', async () => {
48
71
  const el = await fixture<ZnNavbar>(html`
49
72
  <zn-navbar>
50
73
  <li>One</li>
51
74
  <li>Two</li>
52
- <li>Three</li>
53
- <li>Four</li>
54
75
  <zn-expanding-action></zn-expanding-action>
55
76
  </zn-navbar>
56
77
  `);
57
78
  await aTimeout(150);
58
79
 
59
- const nav = el.shadowRoot!.querySelector<HTMLElement>('ul.navbar')!;
60
- const expandable = el.shadowRoot!.querySelector<HTMLElement>('.expandables')!;
61
- const items = Array.from(nav.querySelectorAll<HTMLElement>(':scope > li:not(.more)'));
62
-
63
- Object.defineProperty(el, 'offsetWidth', {configurable: true, get: () => 500});
64
- Object.defineProperty(expandable, 'offsetWidth', {configurable: true, get: () => 400});
65
- Object.defineProperty(expandable, 'getBoundingClientRect', {
66
- configurable: true,
67
- value: () => ({width: 400})
68
- });
69
- items.forEach(item => {
70
- Object.defineProperty(item, 'offsetWidth', {configurable: true, get: () => 120});
71
- Object.defineProperty(item, 'getBoundingClientRect', {
72
- configurable: true,
73
- value: () => ({width: 120})
74
- });
75
- });
76
-
77
- el.handleResize();
78
-
79
- expect(getComputedStyle(nav).minWidth).to.equal('200px');
80
- expect(items[0].classList.contains('hidden')).to.equal(false);
81
- expect(items[1].classList.contains('hidden')).to.equal(true);
80
+ const strip = el.shadowRoot!.querySelector<HTMLElement>('.navbar__strip')!;
81
+ expect(getComputedStyle(strip).minWidth).to.equal('200px');
82
+ expect(strip.style.maxWidth).to.equal('');
82
83
  });
83
84
 
84
- it('accounts for open fill expandables when collapsing nav items', async () => {
85
+ it('gives an open fill expandable room by narrowing the tabs', async () => {
85
86
  const el = await fixture<ZnNavbar>(html`
86
87
  <zn-navbar>
87
88
  <li>One</li>
88
89
  <li>Two</li>
89
- <li>Three</li>
90
- <li>Four</li>
91
90
  <zn-expanding-action method="fill" open></zn-expanding-action>
92
91
  </zn-navbar>
93
92
  `);
94
93
  await aTimeout(150);
95
94
 
96
- const nav = el.shadowRoot!.querySelector<HTMLElement>('ul.navbar')!;
97
- const expandable = el.shadowRoot!.querySelector<HTMLElement>('.expandables')!;
98
- const items = Array.from(nav.querySelectorAll<HTMLElement>(':scope > li:not(.more)'));
99
-
100
- Object.defineProperty(el, 'offsetWidth', {configurable: true, get: () => 1000});
101
- Object.defineProperty(expandable, 'offsetWidth', {configurable: true, get: () => 40});
102
- Object.defineProperty(expandable, 'getBoundingClientRect', {
103
- configurable: true,
104
- value: () => ({width: 40})
105
- });
106
- items.forEach(item => {
107
- Object.defineProperty(item, 'offsetWidth', {configurable: true, get: () => 120});
108
- Object.defineProperty(item, 'getBoundingClientRect', {
109
- configurable: true,
110
- value: () => ({width: 120})
111
- });
112
- });
95
+ const strip = el.shadowRoot!.querySelector<HTMLElement>('.navbar__strip')!;
96
+ Object.defineProperty(el, 'offsetWidth', {configurable: true, get: () => 1100});
113
97
 
114
98
  el.handleResize();
115
99
 
116
- expect(items[0].classList.contains('hidden')).to.equal(false);
117
- expect(items[1].classList.contains('hidden')).to.equal(true);
100
+ expect(strip.style.maxWidth).to.equal('300px');
118
101
  });
119
102
 
120
- it('reserves nav padding and the more button before leaving items visible', async () => {
103
+ it('shows a scroll arrow at each edge with tabs beyond it', async () => {
104
+ const tabs = Array.from({length: 12}, (_, i) => ({tab: `t${i}`, title: `Tab number ${i}`}));
121
105
  const el = await fixture<ZnNavbar>(html`
122
- <zn-navbar>
123
- <li>One</li>
124
- <li>Two</li>
125
- <li>Three</li>
126
- </zn-navbar>
106
+ <zn-navbar style="width: 400px" navigation="${JSON.stringify(tabs)}"></zn-navbar>
127
107
  `);
128
108
  await aTimeout(150);
129
109
 
130
- const nav = el.shadowRoot!.querySelector<HTMLElement>('ul.navbar')!;
131
- const more = nav.querySelector<HTMLElement>(':scope > li.more')!;
132
- const items = Array.from(nav.querySelectorAll<HTMLElement>(':scope > li:not(.more)'));
133
- nav.style.paddingLeft = '20px';
134
- nav.style.paddingRight = '20px';
135
-
136
- Object.defineProperty(el, 'offsetWidth', {configurable: true, get: () => 290});
137
- Object.defineProperty(more, 'offsetWidth', {configurable: true, get: () => 60});
138
- Object.defineProperty(more, 'getBoundingClientRect', {
139
- configurable: true,
140
- value: () => ({width: 60})
141
- });
142
- items.forEach(item => {
143
- Object.defineProperty(item, 'offsetWidth', {configurable: true, get: () => 120});
144
- Object.defineProperty(item, 'getBoundingClientRect', {
145
- configurable: true,
146
- value: () => ({width: 120})
147
- });
148
- });
110
+ const strip = el.shadowRoot!.querySelector<HTMLElement>('.navbar__strip')!;
111
+ const end = el.shadowRoot!.querySelector<HTMLElement>('.navbar__scroll--end')!;
112
+ expect(strip.classList.contains('navbar__strip--scroll-start')).to.equal(false);
113
+ expect(strip.classList.contains('navbar__strip--scroll-end')).to.equal(true);
114
+ await waitUntil(() => getComputedStyle(end).opacity === '1', 'end arrow should show');
149
115
 
150
- el.handleResize();
116
+ strip.scrollLeft = strip.scrollWidth;
117
+ strip.dispatchEvent(new Event('scroll'));
151
118
 
152
- expect(items[0].classList.contains('hidden')).to.equal(false);
153
- expect(items[1].classList.contains('hidden')).to.equal(true);
154
- expect(nav.classList.contains('has-hidden')).to.equal(true);
119
+ expect(strip.classList.contains('navbar__strip--scroll-start')).to.equal(true);
120
+ expect(strip.classList.contains('navbar__strip--scroll-end')).to.equal(false);
155
121
  });
156
122
 
157
- it('rounds the more button instead of the last visible nav item when items are hidden', async () => {
123
+ it('scrolls a newly active tab clear of the arrows', async () => {
124
+ const tabs = Array.from({length: 12}, (_, i) => ({tab: `t${i}`, title: `Tab number ${i}`}));
158
125
  const el = await fixture<ZnNavbar>(html`
159
- <zn-navbar>
160
- <li>One</li>
161
- <li>Two</li>
162
- <li>Three</li>
163
- </zn-navbar>
126
+ <zn-navbar style="width: 400px" navigation="${JSON.stringify(tabs)}"></zn-navbar>
164
127
  `);
165
128
  await aTimeout(150);
166
129
 
167
- const nav = el.shadowRoot!.querySelector<HTMLElement>('ul.navbar')!;
168
- const more = nav.querySelector<HTMLElement>(':scope > li.more')!;
169
- const items = Array.from(nav.querySelectorAll<HTMLElement>(':scope > li:not(.more)'));
170
-
171
- Object.defineProperty(el, 'offsetWidth', {configurable: true, get: () => 250});
172
- Object.defineProperty(more, 'offsetWidth', {configurable: true, get: () => 60});
173
- Object.defineProperty(more, 'getBoundingClientRect', {
174
- configurable: true,
175
- value: () => ({width: 60})
176
- });
177
- items.forEach(item => {
178
- Object.defineProperty(item, 'offsetWidth', {configurable: true, get: () => 100});
179
- Object.defineProperty(item, 'getBoundingClientRect', {
180
- configurable: true,
181
- value: () => ({width: 100})
182
- });
183
- });
184
-
185
- el.handleResize();
130
+ const strip = el.shadowRoot!.querySelector<HTMLElement>('.navbar__strip')!;
131
+ const tab = strip.querySelectorAll<HTMLElement>('ul.navbar > li')[6];
132
+ tab.classList.add('active');
133
+ await waitUntil(() => strip.scrollLeft > 0, 'strip should scroll to the active tab');
134
+ await aTimeout(600);
186
135
 
187
- expect(nav.classList.contains('has-hidden')).to.equal(true);
188
- expect(items[0].classList.contains('last-visible')).to.equal(true);
189
- expect(items[1].classList.contains('hidden')).to.equal(true);
190
- expect(getComputedStyle(items[0]).borderTopRightRadius).to.equal('0px');
191
- expect(getComputedStyle(more).borderTopRightRadius).to.equal('6px');
192
- });
193
-
194
- it('keeps the left border when only the more button is visible', async () => {
195
- const el = await fixture<ZnNavbar>(html`
196
- <zn-navbar>
197
- <li>One</li>
198
- <li>Two</li>
199
- </zn-navbar>
200
- `);
201
- await aTimeout(150);
202
-
203
- const nav = el.shadowRoot!.querySelector<HTMLElement>('ul.navbar')!;
204
- const more = nav.querySelector<HTMLElement>(':scope > li.more')!;
205
- const items = Array.from(nav.querySelectorAll<HTMLElement>(':scope > li:not(.more)'));
206
-
207
- Object.defineProperty(el, 'offsetWidth', {configurable: true, get: () => 100});
208
- Object.defineProperty(more, 'offsetWidth', {configurable: true, get: () => 60});
209
- Object.defineProperty(more, 'getBoundingClientRect', {
210
- configurable: true,
211
- value: () => ({width: 60})
212
- });
213
- items.forEach(item => {
214
- Object.defineProperty(item, 'offsetWidth', {configurable: true, get: () => 150});
215
- Object.defineProperty(item, 'getBoundingClientRect', {
216
- configurable: true,
217
- value: () => ({width: 150})
218
- });
219
- });
220
-
221
- el.handleResize();
222
-
223
- expect(nav.classList.contains('has-hidden')).to.equal(true);
224
- expect(nav.classList.contains('more-only')).to.equal(true);
225
- expect(items.every(item => item.classList.contains('hidden'))).to.equal(true);
226
- expect(getComputedStyle(more).borderLeftWidth).to.equal('1px');
227
- expect(getComputedStyle(more).borderTopLeftRadius).to.equal('6px');
136
+ expect(tab.offsetLeft + tab.offsetWidth).to.be.at.most(strip.scrollLeft + strip.clientWidth - 55);
228
137
  });
229
138
 
230
139
  describe('hide-one', () => {
@@ -271,4 +180,15 @@ describe('<zn-navbar>', () => {
271
180
  await waitUntil(() => getComputedStyle(el).display === 'none', 'navbar should hide after dropping back to one item');
272
181
  });
273
182
  });
183
+
184
+ it('paints tabs from the tab tokens', async () => {
185
+ const el = await fixture<ZnNavbar>(html`
186
+ <zn-navbar style="--zn-navbar-tab-background: rgb(1, 2, 3); --zn-navbar-tab-radius: 0px"
187
+ navigation='[{"path":"#a","title":"A"},{"path":"#b","title":"B"}]'></zn-navbar>`);
188
+ await el.updateComplete;
189
+ const tab = el.shadowRoot!.querySelector<HTMLElement>('ul.navbar > li:not(.active)')!;
190
+
191
+ expect(getComputedStyle(tab).backgroundColor).to.equal('rgb(1, 2, 3)');
192
+ expect(getComputedStyle(el.shadowRoot!.querySelector('ul.navbar > li')!).borderTopLeftRadius).to.equal('0px');
193
+ });
274
194
  });
@@ -17,6 +17,7 @@ import styles from './page.scss';
17
17
  interface TabDefinition {
18
18
  id: string;
19
19
  caption: string;
20
+ icon: string | null;
20
21
  priority: number | null;
21
22
  sourceIndex: number;
22
23
  uri: string | null;
@@ -30,10 +31,16 @@ interface TabDefinition {
30
31
  * @status experimental
31
32
  * @since 1.0
32
33
  *
33
- * @slot - Page content. Use zn-tab for named tabs and header-action/header-actions for header actions.
34
+ * @slot - Page content. Use zn-tab for named tabs; without any, the content is shown as the page's only view. A zn-tab
35
+ * holding zn-tabs shows them as a second tab set, drawn as pills. Use header-action/header-actions for header actions.
34
36
  * Clicking an element in the content with `tab="<tab id>"` opens that tab.
37
+ * @slot caption-badges - Badges shown beside the caption, wrapping onto the next line as one group.
35
38
  * @slot description - Rich subtitle/description content. Falls back to the `summary` attribute when empty.
36
39
  * @slot bottom - Content rendered below the navbar row (e.g. chips, filters). Forwarded to the navbar's bottom slot.
40
+ * @slot footer - Actions pinned below the content, such as a form's `zn-form-actions`. In a modal page it takes the
41
+ * dialog footer's styling.
42
+ *
43
+ * @csspart footer - The footer wrapping the footer slot.
37
44
  */
38
45
  export default class ZnPage extends ZnTabs {
39
46
  static styles: CSSResultGroup = [ZnTabs.styles, unsafeCSS(styles)];
@@ -46,7 +53,7 @@ export default class ZnPage extends ZnTabs {
46
53
  'zn-tab': ZnTab
47
54
  };
48
55
 
49
- private readonly pageSlotController = new HasSlotController(this, 'breadcrumb', 'actions', 'caption', 'description');
56
+ private readonly pageSlotController = new HasSlotController(this, 'breadcrumb', 'actions', 'bottom', 'caption', 'description', 'footer');
50
57
 
51
58
  @property() caption: string;
52
59
  @property({attribute: 'entity-id'}) entityId: string;
@@ -54,6 +61,11 @@ export default class ZnPage extends ZnTabs {
54
61
  @property({attribute: 'full-location'}) fullLocation: string;
55
62
  @property({type: Boolean, reflect: true}) modal = false;
56
63
  @property({type: Boolean, reflect: true}) nested = false;
64
+ /**
65
+ * The tab design: `tabs` (the default), `pills` for a second tab set below a main tab bar, shown without the page
66
+ * header, or `sidebar` when the tabs are shown outside the page, which hides the tab bar unless it has bottom content.
67
+ */
68
+ @property({attribute: 'navigation-style', reflect: true}) navigationStyle: 'tabs' | 'pills' | 'sidebar' | undefined;
57
69
  @property({type: Boolean, reflect: true}) primary = false; // margin
58
70
  @property({attribute: 'previous-path'}) previousPath: string;
59
71
  @property({attribute: 'previous-target'}) previousTarget: string;
@@ -108,7 +120,27 @@ export default class ZnPage extends ZnTabs {
108
120
  this.classList.toggle('alt-pressed', false);
109
121
  };
110
122
 
111
- // Inject a real chevron icon between breadcrumb links (idempotent: only mutates when needed)
123
+ // A theme picks the separator through --zn-page-breadcrumb-separator-icon and its size through
124
+ // --zn-page-breadcrumb-separator-size, as zn-icon sizes itself from its size attribute.
125
+ private breadcrumbSeparatorIcon(): string {
126
+ const value = getComputedStyle(this).getPropertyValue('--zn-page-breadcrumb-separator-icon').trim();
127
+ return value.replace(/^["']|["']$/g, '') || 'arrow-right@lu';
128
+ }
129
+
130
+ private breadcrumbSeparatorSize(): string {
131
+ return String(parseInt(getComputedStyle(this).getPropertyValue('--zn-page-breadcrumb-separator-size')) || 16);
132
+ }
133
+
134
+ private syncBreadcrumbSeparators() {
135
+ const src = this.breadcrumbSeparatorIcon();
136
+ const size = this.breadcrumbSeparatorSize();
137
+ this.querySelectorAll(':scope > [data-breadcrumb-separator]').forEach(icon => {
138
+ if (icon.getAttribute('src') !== src) icon.setAttribute('src', src);
139
+ if (icon.getAttribute('size') !== size) icon.setAttribute('size', size);
140
+ });
141
+ }
142
+
143
+ // Inject a separator icon between breadcrumb links (idempotent: only mutates when needed)
112
144
  private handleBreadcrumbSlotChange = (e: Event) => {
113
145
  const slot = e.target as HTMLSlotElement;
114
146
  const assigned = slot.assignedElements();
@@ -129,8 +161,8 @@ export default class ZnPage extends ZnTabs {
129
161
  if (next?.hasAttribute('data-breadcrumb-separator')) return;
130
162
 
131
163
  const icon = document.createElement('zn-icon');
132
- icon.setAttribute('src', 'arrow-right@lu');
133
- icon.setAttribute('size', '16');
164
+ icon.setAttribute('src', this.breadcrumbSeparatorIcon());
165
+ icon.setAttribute('size', this.breadcrumbSeparatorSize());
134
166
  icon.setAttribute('slot', 'breadcrumb');
135
167
  icon.setAttribute('data-breadcrumb-separator', '');
136
168
  anchor.after(icon);
@@ -187,6 +219,7 @@ export default class ZnPage extends ZnTabs {
187
219
 
188
220
  tabElements.forEach((tab, index) => {
189
221
  const caption = tab.getAttribute('caption') || 'Tab';
222
+ const icon = tab.getAttribute('icon');
190
223
  const explicitId = tab.getAttribute('id');
191
224
  const id = this.uniqueId(explicitId !== null ? explicitId : this.captionToId(caption), usedIds);
192
225
  const priority = this.parsePriority(tab.getAttribute('priority'));
@@ -195,17 +228,38 @@ export default class ZnPage extends ZnTabs {
195
228
  const selected = tab.hasAttribute('selected');
196
229
 
197
230
  tab.id = id;
231
+ this.groupInnerTabs(tab);
198
232
 
199
233
  if (!uri) {
200
234
  tab.slot = slotName!;
201
235
  }
202
236
 
203
- tabs.push({id, caption, priority, sourceIndex: index, uri, slotName, selected});
237
+ tabs.push({id, caption, icon, priority, sourceIndex: index, uri, slotName, selected});
204
238
  });
205
239
 
206
240
  this.tabDefinitions = tabs.sort((a, b) => this.compareTabs(a, b));
207
241
  }
208
242
 
243
+ // shortcut: inner tabs added after the group is built stay plain content; move them into the group page to show them
244
+ private groupInnerTabs(tab: HTMLElement) {
245
+ const innerTabs = Array.from(tab.children).filter(child => child.tagName === 'ZN-TAB');
246
+ if (innerTabs.length === 0) {
247
+ return;
248
+ }
249
+
250
+ const group = document.createElement('zn-page');
251
+ // flush keeps it from padding the parent's content again when an app sidebar restyles it from pills
252
+ group.setAttribute('nested', '');
253
+ group.setAttribute('flush', '');
254
+ group.setAttribute('navigation-style', 'pills');
255
+ innerTabs[0].before(group);
256
+ group.append(...innerTabs);
257
+ }
258
+
259
+ private renderTabCaption(tab: TabDefinition) {
260
+ return tab.icon ? html`<zn-icon src="${tab.icon}" size="20"></zn-icon>${tab.caption}` : tab.caption;
261
+ }
262
+
209
263
  private parsePriority(value: string | null): number | null {
210
264
  if (value === null || value.trim() === '') {
211
265
  return null;
@@ -225,7 +279,7 @@ export default class ZnPage extends ZnTabs {
225
279
  }
226
280
 
227
281
  if (a.priority !== null && b.priority !== null && a.priority !== b.priority) {
228
- return a.priority - b.priority;
282
+ return b.priority - a.priority;
229
283
  }
230
284
 
231
285
  if (a.priority !== null && b.priority === null) {
@@ -275,6 +329,10 @@ export default class ZnPage extends ZnTabs {
275
329
  updated(changedProperties: PropertyValues) {
276
330
  super.updated(changedProperties);
277
331
 
332
+ if (changedProperties.has('t') || changedProperties.has('m')) {
333
+ this.syncBreadcrumbSeparators();
334
+ }
335
+
278
336
  if (changedProperties.has('tabDefinitions')) {
279
337
  this.registerPagePanels();
280
338
  }
@@ -285,12 +343,18 @@ export default class ZnPage extends ZnTabs {
285
343
  }
286
344
 
287
345
  // Only elements whose nearest tab container is this page count, so links inside a nested
288
- // zn-tabs or zn-page drive that container instead.
346
+ // zn-tabs or zn-page drive that container instead. The container is found along the event path
347
+ // because uri tabs load into this page's shadow root, which closest() cannot climb out of.
289
348
  private handleContentTabClick = (event: MouseEvent) => {
290
- const link = event.composedPath().find((node): node is HTMLElement =>
291
- node instanceof HTMLElement && node.hasAttribute('tab') && node.closest(tabContainerSelector) === this
292
- );
293
- if (!link || link.hasAttribute('disabled')) {
349
+ const path = event.composedPath();
350
+ const index = path.findIndex(node => node instanceof HTMLElement && node.hasAttribute('tab'));
351
+ const link = path[index] as HTMLElement | undefined;
352
+ // The page's own navbar items select through zn-select.
353
+ if (!link || link.hasAttribute('disabled') || link.closest('zn-navbar')) {
354
+ return;
355
+ }
356
+ const container = path.slice(index + 1).find(node => node instanceof Element && node.matches(tabContainerSelector));
357
+ if (container !== this) {
294
358
  return;
295
359
  }
296
360
 
@@ -368,7 +432,7 @@ export default class ZnPage extends ZnTabs {
368
432
  const firstTab = this.tabDefinitions[0];
369
433
 
370
434
  if (!selectedPanel && firstTab) {
371
- this.activateTab(firstTab.id, false);
435
+ this.activateTabDefinition(firstTab);
372
436
  }
373
437
  }
374
438
 
@@ -399,7 +463,7 @@ export default class ZnPage extends ZnTabs {
399
463
  }
400
464
 
401
465
  this.pageHistoryKey = pages
402
- .map(page => page.id || page.getAttribute('caption') || 'page')
466
+ .map(page => page.id || page.getAttribute('caption') || page.parentElement?.getAttribute('caption') || 'page')
403
467
  .join('/');
404
468
  return this.pageHistoryKey;
405
469
  }
@@ -464,22 +528,29 @@ export default class ZnPage extends ZnTabs {
464
528
  const hasFullLocation = this.fullLocation;
465
529
  const hasPreviousPath = this.previousPath;
466
530
  const hasDescription = !!this.summary || this.pageSlotController.test('description');
531
+ const hasFooter = this.pageSlotController.test('footer');
532
+ const externalNavigation = this.navigationStyle === 'sidebar';
533
+ const hasHeader = this.navigationStyle !== 'pills' && Boolean(
534
+ this.caption || hasDescription || hasBreadcrumb || hasEntityId || hasFullLocation || hasPreviousPath ||
535
+ this.pageSlotController.test('caption') || this.pageSlotController.test('actions')
536
+ );
467
537
 
468
538
  return html`
469
539
  <div class="page" part="base" @scroll="${this.handlePageScroll}">
470
540
  <div class="${classMap({
471
541
  'page__header': true,
472
- 'page__header--scrolled-no-navigation': this.scrolled && !hasNavigation
542
+ 'page__header--scrolled-no-navigation': this.scrolled && !hasNavigation,
543
+ 'page__header--headless': !hasHeader && hasNavigation
473
544
  })}" part="header">
474
545
  <div class="${classMap({
475
546
  'header': true,
476
547
  'header--has-breadcrumb': hasBreadcrumb,
477
548
  'header--has-entity-id': hasEntityId,
478
549
  'header--has-full-location': hasFullLocation,
479
- 'header--has-navigation': hasNavigation,
550
+ 'header--has-navigation': hasNavigation && !externalNavigation,
480
551
  'header--has-previous': hasPreviousPath,
481
552
  'header--has-description': hasDescription
482
- })}">
553
+ })}" ?hidden="${!hasHeader}">
483
554
  ${hasFullLocation || hasEntityId ? html`
484
555
  <div class="${classMap({
485
556
  'alt-overlay': true,
@@ -501,19 +572,24 @@ export default class ZnPage extends ZnTabs {
501
572
 
502
573
  <div class="content">
503
574
  ${hasPreviousPath ? html`
504
- <a href="${this.previousPath}" class="caption__back"
505
- data-target="${this.previousTarget ? this.previousTarget : ''}">
506
- <zn-button icon="arrow-left@lu" icon-size="24" icon-button="small" plain></zn-button>
507
- </a>` : null}
575
+ <zn-button href="${this.previousPath}" class="caption__back" label="Back"
576
+ data-target="${this.previousTarget ? this.previousTarget : ''}"
577
+ icon="arrow-left@lu" icon-size="24" icon-button="small" plain></zn-button>` : null}
508
578
 
509
579
  <div class="caption">
510
580
  <div class="header__left">
511
581
  <span class="header__caption">
512
582
  ${hasBreadcrumb ? html`
513
- <slot name="breadcrumb" class="breadcrumb"
514
- @slotchange="${this.handleBreadcrumbSlotChange}"></slot>
515
- <zn-icon class="breadcrumb__separator" src="arrow-right@lu" size="16"></zn-icon>` : null}
516
- <slot name="caption">${this.caption}</slot>
583
+ <span class="header__crumbs">
584
+ <slot name="breadcrumb" class="breadcrumb"
585
+ @slotchange="${this.handleBreadcrumbSlotChange}"></slot>
586
+ <zn-icon class="breadcrumb__separator" src="${this.breadcrumbSeparatorIcon()}" size="${this.breadcrumbSeparatorSize()}"></zn-icon>
587
+ ${this.caption ? html`<span class="breadcrumb__current">${this.caption}</span>` : null}
588
+ </span>` : null}
589
+ <span class="header__title">
590
+ <slot name="caption">${this.caption}</slot>
591
+ <slot name="caption-badges"></slot>
592
+ </span>
517
593
  </span>
518
594
  <span class="header__description"><slot name="description">${this.summary}</slot></span>
519
595
  </div>
@@ -526,11 +602,13 @@ export default class ZnPage extends ZnTabs {
526
602
  </div>
527
603
 
528
604
  ${hasNavigation ? html`
529
- <zn-navbar hide-one @zn-select="${this.handleNavigationSelect}">
605
+ <zn-navbar class="page__navbar" hide-one ?pill="${this.navigationStyle === 'pills'}"
606
+ ?hidden="${externalNavigation && !this.pageSlotController.test('bottom')}"
607
+ @zn-select="${this.handleNavigationSelect}">
530
608
  ${this.tabDefinitions.map(tab => tab.uri ? html`
531
- <li tab-uri="${tab.uri}">${tab.caption}</li>
609
+ <li tab-uri="${tab.uri}">${this.renderTabCaption(tab)}</li>
532
610
  ` : html`
533
- <li tab="${tab.id}">${tab.caption}</li>
611
+ <li tab="${tab.id}">${this.renderTabCaption(tab)}</li>
534
612
  `)}
535
613
  <slot name="bottom" slot="bottom"></slot>
536
614
  </zn-navbar>
@@ -539,7 +617,10 @@ export default class ZnPage extends ZnTabs {
539
617
 
540
618
  <div class="page__tabs">
541
619
  <div id="content" class="page__content" part="content" @scroll="${this.handlePageScroll}">
542
- ${this.tabDefinitions
620
+ ${this.tabDefinitions.length === 0 ? html`
621
+ <div class="page__default" selected>
622
+ <slot></slot>
623
+ </div>` : this.tabDefinitions
543
624
  .filter(tab => tab.slotName !== null)
544
625
  .map(tab => html`
545
626
  <div id="${tab.id}" ?selected="${tab.selected}">
@@ -548,6 +629,10 @@ export default class ZnPage extends ZnTabs {
548
629
  `)}
549
630
  </div>
550
631
  </div>
632
+
633
+ <div class="page__footer" part="footer" ?hidden="${!hasFooter}">
634
+ <slot name="footer"></slot>
635
+ </div>
551
636
  </div>
552
637
  `;
553
638
  }