@conduction/nextcloud-vue 2.56.0-beta.6 → 2.56.0-beta.8

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 (254) hide show
  1. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue2.js +14 -1
  2. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue2.js.map +1 -1
  3. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js.map +1 -1
  4. package/dist/esm/components/CnAppNav/CnAppNav.vue2.js +80 -1
  5. package/dist/esm/components/CnAppNav/CnAppNav.vue2.js.map +1 -1
  6. package/dist/esm/components/CnAppNav/CnAppNav.vue3.js +46 -4
  7. package/dist/esm/components/CnAppNav/CnAppNav.vue3.js.map +1 -1
  8. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js +232 -10
  9. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js.map +1 -1
  10. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js +79 -25
  11. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js.map +1 -1
  12. package/dist/esm/components/CnBannerWidgetForm/CnBannerWidgetForm.vue2.js +42 -3
  13. package/dist/esm/components/CnBannerWidgetForm/CnBannerWidgetForm.vue2.js.map +1 -1
  14. package/dist/esm/components/CnBannerWidgetForm/CnBannerWidgetForm.vue3.js +39 -6
  15. package/dist/esm/components/CnBannerWidgetForm/CnBannerWidgetForm.vue3.js.map +1 -1
  16. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js +46 -0
  17. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
  18. package/dist/esm/components/CnBoardView/CnBoardView.vue3.js +11 -4
  19. package/dist/esm/components/CnBoardView/CnBoardView.vue3.js.map +1 -1
  20. package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue.js +10 -0
  21. package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue.js.map +1 -0
  22. package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue2.js +39 -0
  23. package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue2.js.map +1 -0
  24. package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue3.js +16 -0
  25. package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue3.js.map +1 -0
  26. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +117 -1
  27. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
  28. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +147 -89
  29. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
  30. package/dist/esm/components/CnConversationThread/CnConversationThread.vue.js +10 -0
  31. package/dist/esm/components/CnConversationThread/CnConversationThread.vue.js.map +1 -0
  32. package/dist/esm/components/CnConversationThread/CnConversationThread.vue2.js +259 -0
  33. package/dist/esm/components/CnConversationThread/CnConversationThread.vue2.js.map +1 -0
  34. package/dist/esm/components/CnConversationThread/CnConversationThread.vue3.js +131 -0
  35. package/dist/esm/components/CnConversationThread/CnConversationThread.vue3.js.map +1 -0
  36. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue.js +10 -0
  37. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue.js.map +1 -0
  38. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js +116 -0
  39. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js.map +1 -0
  40. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue3.js +24 -0
  41. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue3.js.map +1 -0
  42. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +10 -2
  43. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  44. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  45. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +503 -28
  46. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  47. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +303 -144
  48. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  49. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewList.vue.js +10 -0
  50. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewList.vue.js.map +1 -0
  51. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewList.vue2.js +237 -0
  52. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewList.vue2.js.map +1 -0
  53. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewList.vue3.js +120 -0
  54. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewList.vue3.js.map +1 -0
  55. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue.js +10 -0
  56. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue.js.map +1 -0
  57. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue2.js +77 -0
  58. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue2.js.map +1 -0
  59. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue3.js +27 -0
  60. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue3.js.map +1 -0
  61. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js +171 -12
  62. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js.map +1 -1
  63. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue3.js +15 -7
  64. package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue3.js.map +1 -1
  65. package/dist/esm/components/CnHeaderWidgetForm/CnHeaderWidgetForm.vue2.js +37 -3
  66. package/dist/esm/components/CnHeaderWidgetForm/CnHeaderWidgetForm.vue2.js.map +1 -1
  67. package/dist/esm/components/CnHeaderWidgetForm/CnHeaderWidgetForm.vue3.js +44 -16
  68. package/dist/esm/components/CnHeaderWidgetForm/CnHeaderWidgetForm.vue3.js.map +1 -1
  69. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +132 -3
  70. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  71. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +6 -3
  72. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  73. package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue.js +10 -0
  74. package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue.js.map +1 -0
  75. package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue2.js +316 -0
  76. package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue2.js.map +1 -0
  77. package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue3.js +105 -0
  78. package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue3.js.map +1 -0
  79. package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue.js +10 -0
  80. package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue.js.map +1 -0
  81. package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue2.js +150 -0
  82. package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue2.js.map +1 -0
  83. package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue3.js +106 -0
  84. package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue3.js.map +1 -0
  85. package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue.js +9 -0
  86. package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue.js.map +1 -0
  87. package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue2.js +47 -0
  88. package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue2.js.map +1 -0
  89. package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue3.js +17 -0
  90. package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue3.js.map +1 -0
  91. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js +49 -0
  92. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js.map +1 -1
  93. package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js.map +1 -1
  94. package/dist/esm/components/CnObjectDataWidgetForm/CnObjectDataWidgetForm.vue2.js +6 -0
  95. package/dist/esm/components/CnObjectDataWidgetForm/CnObjectDataWidgetForm.vue2.js.map +1 -1
  96. package/dist/esm/components/CnObjectDataWidgetForm/CnObjectDataWidgetForm.vue3.js.map +1 -1
  97. package/dist/esm/components/CnObjectKanban/CnObjectKanban.vue2.js +46 -0
  98. package/dist/esm/components/CnObjectKanban/CnObjectKanban.vue2.js.map +1 -1
  99. package/dist/esm/components/CnObjectKanban/CnObjectKanban.vue3.js +15 -3
  100. package/dist/esm/components/CnObjectKanban/CnObjectKanban.vue3.js.map +1 -1
  101. package/dist/esm/components/CnPageRenderer/pageTypes.js +1 -0
  102. package/dist/esm/components/CnPageRenderer/pageTypes.js.map +1 -1
  103. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js +37 -1
  104. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js.map +1 -1
  105. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue3.js +29 -13
  106. package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue3.js.map +1 -1
  107. package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue2.js +50 -3
  108. package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue2.js.map +1 -1
  109. package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue3.js.map +1 -1
  110. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue.js +10 -0
  111. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue.js.map +1 -0
  112. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue2.js +186 -0
  113. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue2.js.map +1 -0
  114. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue3.js +46 -0
  115. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue3.js.map +1 -0
  116. package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue.js +11 -0
  117. package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue.js.map +1 -0
  118. package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue2.js +298 -0
  119. package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue2.js.map +1 -0
  120. package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue3.js +77 -0
  121. package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue3.js.map +1 -0
  122. package/dist/esm/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue.js +10 -0
  123. package/dist/esm/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue.js.map +1 -0
  124. package/dist/esm/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue2.js +169 -0
  125. package/dist/esm/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue2.js.map +1 -0
  126. package/dist/esm/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue3.js +48 -0
  127. package/dist/esm/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue3.js.map +1 -0
  128. package/dist/esm/components/CnTabs/CnTab.vue2.js +35 -0
  129. package/dist/esm/components/CnTabs/CnTab.vue2.js.map +1 -1
  130. package/dist/esm/components/CnTabs/CnTab.vue3.js.map +1 -1
  131. package/dist/esm/components/CnTabs/CnTabs.vue2.js +120 -7
  132. package/dist/esm/components/CnTabs/CnTabs.vue2.js.map +1 -1
  133. package/dist/esm/components/CnTabs/CnTabs.vue3.js +57 -5
  134. package/dist/esm/components/CnTabs/CnTabs.vue3.js.map +1 -1
  135. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +59 -4
  136. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
  137. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +6 -2
  138. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
  139. package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue.js +10 -0
  140. package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue.js.map +1 -0
  141. package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue2.js +484 -0
  142. package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue2.js.map +1 -0
  143. package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue3.js +115 -0
  144. package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue3.js.map +1 -0
  145. package/dist/esm/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue.js +10 -0
  146. package/dist/esm/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue.js.map +1 -0
  147. package/dist/esm/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue2.js +189 -0
  148. package/dist/esm/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue2.js.map +1 -0
  149. package/dist/esm/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue3.js +74 -0
  150. package/dist/esm/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue3.js.map +1 -0
  151. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +82 -8
  152. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
  153. package/dist/esm/index.js +16 -0
  154. package/dist/esm/index.js.map +1 -1
  155. package/dist/esm/l10n/en.json.js +55 -1
  156. package/dist/esm/l10n/en.json.js.map +1 -1
  157. package/dist/esm/l10n/nl.json.js +55 -1
  158. package/dist/esm/l10n/nl.json.js.map +1 -1
  159. package/dist/esm/nextcloud-vue.css +1036 -19
  160. package/dist/esm/utils/dateVariant.js +124 -0
  161. package/dist/esm/utils/dateVariant.js.map +1 -0
  162. package/dist/esm/utils/detailActionModel.js +260 -0
  163. package/dist/esm/utils/detailActionModel.js.map +1 -0
  164. package/dist/esm/utils/dueRule.js +84 -0
  165. package/dist/esm/utils/dueRule.js.map +1 -0
  166. package/dist/esm/utils/fetchAggregate.js +37 -1
  167. package/dist/esm/utils/fetchAggregate.js.map +1 -1
  168. package/dist/esm/utils/fetchFilterCounts.js +186 -0
  169. package/dist/esm/utils/fetchFilterCounts.js.map +1 -0
  170. package/dist/esm/utils/headers.js +28 -2
  171. package/dist/esm/utils/headers.js.map +1 -1
  172. package/dist/esm/utils/libraryWidgetKeys.js +5 -0
  173. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  174. package/dist/esm/utils/readPath.js +30 -0
  175. package/dist/esm/utils/readPath.js.map +1 -0
  176. package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
  177. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  178. package/dist/nextcloud-vue.cjs.js +16019 -9528
  179. package/dist/nextcloud-vue.cjs.js.map +2 -2
  180. package/dist/nextcloud-vue.css +1036 -19
  181. package/l10n/en.json +55 -1
  182. package/l10n/nl.json +55 -1
  183. package/package.json +1 -1
  184. package/scripts/.jsdoc-baselines.json +4 -0
  185. package/src/components/CnActionsMenu/CnActionsMenu.vue +14 -1
  186. package/src/components/CnAppNav/CnAppNav.md +26 -0
  187. package/src/components/CnAppNav/CnAppNav.vue +145 -2
  188. package/src/components/CnBannerWidget/CnBannerWidget.vue +373 -11
  189. package/src/components/CnBannerWidgetForm/CnBannerWidgetForm.vue +65 -3
  190. package/src/components/CnBoardView/CnBoardView.vue +77 -0
  191. package/src/components/CnBrandStripe/CnBrandStripe.md +22 -0
  192. package/src/components/CnBrandStripe/CnBrandStripe.vue +100 -0
  193. package/src/components/CnBrandStripe/index.js +4 -0
  194. package/src/components/CnCellRenderer/CnCellRenderer.vue +207 -1
  195. package/src/components/CnConversationThread/CnConversationThread.md +51 -0
  196. package/src/components/CnConversationThread/CnConversationThread.vue +457 -0
  197. package/src/components/CnConversationThread/CnConversationWidget.vue +144 -0
  198. package/src/components/CnConversationThread/index.js +4 -0
  199. package/src/components/CnDashboardPage/CnDashboardPage.vue +10 -2
  200. package/src/components/CnDetailPage/CnDetailPage.md +16 -0
  201. package/src/components/CnDetailPage/CnDetailPage.vue +687 -49
  202. package/src/components/CnDocumentReviewList/CnDocumentReviewList.md +55 -0
  203. package/src/components/CnDocumentReviewList/CnDocumentReviewList.vue +384 -0
  204. package/src/components/CnDocumentReviewList/CnDocumentReviewWidget.vue +108 -0
  205. package/src/components/CnDocumentReviewList/index.js +4 -0
  206. package/src/components/CnHeaderWidget/CnHeaderWidget.vue +191 -14
  207. package/src/components/CnHeaderWidgetForm/CnHeaderWidgetForm.vue +53 -3
  208. package/src/components/CnIndexPage/CnIndexPage.vue +135 -3
  209. package/src/components/CnLinkCardsPage/CnLinkCardsPage.vue +484 -0
  210. package/src/components/CnLinkCardsPage/index.js +4 -0
  211. package/src/components/CnNextStepCard/CnNextStepCard.md +40 -0
  212. package/src/components/CnNextStepCard/CnNextStepCard.vue +319 -0
  213. package/src/components/CnNextStepCard/CnNextStepWidget.vue +59 -0
  214. package/src/components/CnNextStepCard/index.js +4 -0
  215. package/src/components/CnObjectDataWidget/CnObjectDataWidget.vue +49 -0
  216. package/src/components/CnObjectDataWidgetForm/CnObjectDataWidgetForm.vue +6 -0
  217. package/src/components/CnObjectKanban/CnObjectKanban.vue +86 -2
  218. package/src/components/CnPageRenderer/pageTypes.js +1 -0
  219. package/src/components/CnQuickFilterBar/CnQuickFilterBar.md +3 -0
  220. package/src/components/CnQuickFilterBar/CnQuickFilterBar.vue +60 -1
  221. package/src/components/CnSavedViewsControl/CnSavedViewsControl.vue +50 -3
  222. package/src/components/CnSegmentedControl/CnSegmentedControl.md +46 -0
  223. package/src/components/CnSegmentedControl/CnSegmentedControl.vue +291 -0
  224. package/src/components/CnSegmentedControl/index.js +4 -0
  225. package/src/components/CnStackedBarWidget/CnStackedBarWidget.md +15 -0
  226. package/src/components/CnStackedBarWidget/CnStackedBarWidget.vue +414 -0
  227. package/src/components/CnStackedBarWidget/index.js +14 -0
  228. package/src/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue +224 -0
  229. package/src/components/CnStackedBarWidgetForm/index.js +4 -0
  230. package/src/components/CnTabs/CnTab.vue +35 -0
  231. package/src/components/CnTabs/CnTabs.vue +244 -10
  232. package/src/components/CnTabsWidget/CnTabsWidget.vue +63 -4
  233. package/src/components/CnWeekStripWidget/CnWeekStripWidget.md +31 -0
  234. package/src/components/CnWeekStripWidget/CnWeekStripWidget.vue +668 -0
  235. package/src/components/CnWeekStripWidget/index.js +14 -0
  236. package/src/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue +265 -0
  237. package/src/components/CnWeekStripWidgetForm/index.js +4 -0
  238. package/src/components/CnWidgetGrid/registerDashboardWidgets.js +74 -0
  239. package/src/components/index.js +12 -0
  240. package/src/css/detail-page.css +57 -2
  241. package/src/index.js +16 -0
  242. package/src/schemas/app-manifest-v2.schema.json +433 -3
  243. package/src/schemas/app-manifest.schema.json +2 -1
  244. package/src/types/manifest.d.ts +1 -1
  245. package/src/utils/dateVariant.js +120 -0
  246. package/src/utils/detailActionModel.js +256 -0
  247. package/src/utils/dueRule.js +94 -0
  248. package/src/utils/fetchAggregate.js +36 -0
  249. package/src/utils/fetchFilterCounts.js +182 -0
  250. package/src/utils/headers.js +28 -2
  251. package/src/utils/index.js +1 -0
  252. package/src/utils/libraryWidgetKeys.js +5 -0
  253. package/src/utils/readPath.js +27 -0
  254. package/src/utils/validateManifestV2.compiled.js +2 -2
@@ -44,6 +44,10 @@
44
44
  :size="16"
45
45
  class="cn-quick-filter-bar__icon" />
46
46
  <span class="cn-quick-filter-bar__label">{{ entry.tab.label }}</span>
47
+ <span
48
+ v-if="countOf(entry) !== null"
49
+ class="cn-quick-filter-bar__count"
50
+ data-testid="cn-quick-filter-count">{{ ' ' + countOf(entry) }}</span>
47
51
  </button>
48
52
  </div>
49
53
  <!-- The overflow is a CHIP and not a toolbar button: it is one of the
@@ -96,6 +100,10 @@
96
100
  :size="16"
97
101
  class="cn-quick-filter-bar__icon" />
98
102
  <span class="cn-quick-filter-bar__label">{{ entry.tab.label }}</span>
103
+ <span
104
+ v-if="countOf(entry) !== null"
105
+ class="cn-quick-filter-bar__count"
106
+ data-testid="cn-quick-filter-count">{{ ' ' + countOf(entry) }}</span>
99
107
  </button>
100
108
  </div>
101
109
  </div>
@@ -231,6 +239,19 @@ export default {
231
239
  default: 0,
232
240
  validator: (v) => Number.isFinite(v) && v >= 0,
233
241
  },
242
+
243
+ /**
244
+ * How many records each tab matches, keyed by tab index (an array in
245
+ * tab order works too). A tab with a number here shows it after its
246
+ * label; a tab without one shows no count. A tab can also carry its
247
+ * own `count`. `null` (the default) shows no counts at all.
248
+ *
249
+ * @type {{[index: number]: number}|Array<number|null>|null}
250
+ */
251
+ counts: {
252
+ type: [Object, Array],
253
+ default: null,
254
+ },
234
255
  },
235
256
 
236
257
  emits: ['update:active-index', 'update:selected-indices'],
@@ -324,10 +345,19 @@ export default {
324
345
  * omitted — an empty selection already means "all".
325
346
  *
326
347
  * @return {Array<{label: string, index: number, icon: string}>}
348
+ * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views
327
349
  */
328
350
  dropdownOptions() {
329
351
  return this.tabs
330
- .map((tab, index) => ({ label: tab.label, icon: tab.icon, index, _empty: this.isEmptyFilter(tab) }))
352
+ .map((tab, index) => {
353
+ const count = this.countOf({ tab, index })
354
+ return {
355
+ label: count === null ? tab.label : `${tab.label} (${count})`,
356
+ icon: tab.icon,
357
+ index,
358
+ _empty: this.isEmptyFilter(tab),
359
+ }
360
+ })
331
361
  .filter((o) => !o._empty)
332
362
  },
333
363
 
@@ -346,6 +376,20 @@ export default {
346
376
  },
347
377
 
348
378
  methods: {
379
+ /**
380
+ * The count to show on a tab, or null for none: `counts[index]`
381
+ * first, else the tab's own `count`.
382
+ *
383
+ * @param {{tab: object, index: number}} entry The tab and its index.
384
+ * @return {number|null}
385
+ * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views
386
+ */
387
+ countOf(entry) {
388
+ const fromProp = this.counts ? this.counts[entry.index] : undefined
389
+ const value = (fromProp !== undefined && fromProp !== null) ? fromProp : entry.tab.count
390
+ return (typeof value === 'number' && Number.isFinite(value)) ? value : null
391
+ },
392
+
349
393
  /**
350
394
  * Is tab `i` rendered active in chips mode (handles both single
351
395
  * `activeIndex` and `multiple` `selectedIndices`).
@@ -501,6 +545,21 @@ export default {
501
545
  line-height: 1;
502
546
  }
503
547
 
548
+ /* Inherits the chip's own text colour, so it stays readable on the active
549
+ (primary) fill as well as on the resting one. The count's text starts with
550
+ a space: the chip is a flex row, so the space is not drawn, but it keeps a
551
+ screen reader from reading label and count as one word. */
552
+ .cn-quick-filter-bar__count {
553
+ min-width: 1.5em;
554
+ padding: 2px 6px;
555
+ border: 1px solid currentColor;
556
+ border-radius: var(--border-radius-pill, 10px);
557
+ font-size: 0.85em;
558
+ font-weight: 600;
559
+ line-height: 1;
560
+ text-align: center;
561
+ }
562
+
504
563
  /* NcPopover wraps its trigger in a plain `<div class="v-popper">`, which as a
505
564
  block would drop the chip onto its own line under the strip. */
506
565
  .cn-quick-filter-bar > :deep(.v-popper) {
@@ -182,6 +182,18 @@ export default {
182
182
  type: Number,
183
183
  default: 3,
184
184
  },
185
+
186
+ /**
187
+ * How many records each view matches, keyed by view id (or slug). A
188
+ * view with a number here shows it after its name; a view can also
189
+ * carry its own `count`. `null` (the default) shows no counts.
190
+ *
191
+ * @type {{[viewId: string]: number}|null}
192
+ */
193
+ counts: {
194
+ type: Object,
195
+ default: null,
196
+ },
185
197
  },
186
198
 
187
199
  emits: ['apply', 'delete-request', 'save-request'],
@@ -245,9 +257,42 @@ export default {
245
257
  *
246
258
  * @param {object} row The row from buildViewTree().
247
259
  * @return {string} The name.
260
+ * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views
248
261
  */
249
262
  rowName(row) {
250
- return `${'\u2007'.repeat(row.depth * 2)}${row.view.name}`
263
+ return `${'\u2007'.repeat(row.depth * 2)}${this.nameWithCount(row.view)}`
264
+ },
265
+
266
+ /**
267
+ * The number of records a view matches, or null when none is known:
268
+ * `counts[id]` (or `counts[slug]`) first, else the view's own `count`.
269
+ *
270
+ * @param {object} view The saved view.
271
+ * @return {number|null}
272
+ * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views
273
+ */
274
+ countOf(view) {
275
+ const counts = this.counts || {}
276
+ let value = counts[view.id]
277
+ if (value === undefined || value === null) {
278
+ value = counts[view.slug]
279
+ }
280
+ if (value === undefined || value === null) {
281
+ value = view.count
282
+ }
283
+ return (typeof value === 'number' && Number.isFinite(value)) ? value : null
284
+ },
285
+
286
+ /**
287
+ * A view's name, followed by its count when one is known.
288
+ *
289
+ * @param {object} view The saved view.
290
+ * @return {string}
291
+ * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views
292
+ */
293
+ nameWithCount(view) {
294
+ const count = this.countOf(view)
295
+ return count === null ? view.name : `${view.name} (${count})`
251
296
  },
252
297
 
253
298
  /**
@@ -259,11 +304,13 @@ export default {
259
304
  *
260
305
  * @param {object} row The row from buildViewTree().
261
306
  * @return {string} The accessible label.
307
+ * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views
262
308
  */
263
309
  rowLabel(row) {
310
+ const viewName = this.nameWithCount(row.view)
264
311
  const name = row.depth === 0
265
- ? row.view.name
266
- : t('nextcloud-vue', '{name}, level {level}', { name: row.view.name, level: row.depth + 1 })
312
+ ? viewName
313
+ : t('nextcloud-vue', '{name}, level {level}', { name: viewName, level: row.depth + 1 })
267
314
 
268
315
  if (row.orphaned !== true) {
269
316
  return name
@@ -0,0 +1,46 @@
1
+ A switch between a few views of the same thing. It is a radio group: the arrow keys move the choice.
2
+
3
+ ```vue
4
+ <template>
5
+ <div>
6
+ <CnSegmentedControl
7
+ v-model="view"
8
+ aria-label="View"
9
+ :options="[
10
+ { value: 'mine', label: 'My work' },
11
+ { value: 'team', label: 'My team', count: 12 },
12
+ ]" />
13
+ <p>Chosen: {{ view }}</p>
14
+ </div>
15
+ </template>
16
+
17
+ <script>
18
+ export default {
19
+ data() { return { view: 'mine' } },
20
+ }
21
+ </script>
22
+ ```
23
+
24
+ Stretched, with a disabled option:
25
+
26
+ ```vue
27
+ <template>
28
+ <CnSegmentedControl
29
+ v-model="period"
30
+ stretch
31
+ aria-label="Period"
32
+ :options="[
33
+ { value: 'week', label: 'Week' },
34
+ { value: 'month', label: 'Month' },
35
+ { value: 'year', label: 'Year', disabled: true },
36
+ ]" />
37
+ </template>
38
+
39
+ <script>
40
+ export default {
41
+ data() { return { period: 'week' } },
42
+ }
43
+ </script>
44
+ ```
45
+
46
+ Props: `options` lists the choices, `modelValue` is the chosen value (use `v-model`), `aria-label` names the group (or point `aria-labelledby` at a visible heading), and `stretch` fills the width.
@@ -0,0 +1,291 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Conduction B.V.
3
+ SPDX-License-Identifier: EUPL-1.2
4
+ -->
5
+ <template>
6
+ <div
7
+ class="cn-segmented-control"
8
+ :class="{ 'cn-segmented-control--stretch': stretch }"
9
+ role="radiogroup"
10
+ :aria-label="ariaLabel || null"
11
+ :aria-labelledby="ariaLabelledby || null"
12
+ @keydown="onKeydown">
13
+ <button
14
+ v-for="(option, index) in normalizedOptions"
15
+ :key="option.key"
16
+ type="button"
17
+ role="radio"
18
+ class="cn-segmented-control__option"
19
+ :class="{ 'cn-segmented-control__option--checked': isChecked(option) }"
20
+ :aria-checked="isChecked(option) ? 'true' : 'false'"
21
+ :tabindex="index === focusableIndex ? 0 : -1"
22
+ :disabled="option.disabled || null"
23
+ :data-value="option.key"
24
+ @click="select(option)">
25
+ <!-- @slot option Replaces the content of one option. -->
26
+ <!-- @binding {object} option The option: `{ value, label, disabled, count }`. -->
27
+ <!-- @binding {boolean} checked Whether this option is the chosen one. -->
28
+ <slot name="option" :option="option" :checked="isChecked(option)">
29
+ <span class="cn-segmented-control__label">{{ option.label }}</span>
30
+ <!-- The leading space is not drawn in this flex row; it keeps a
31
+ screen reader from reading label and count as one word. -->
32
+ <span v-if="option.count !== null" class="cn-segmented-control__count">{{ ' ' + option.count }}</span>
33
+ </slot>
34
+ </button>
35
+ </div>
36
+ </template>
37
+
38
+ <script>
39
+ /**
40
+ * CnSegmentedControl is a switch between a few views of the same thing, such
41
+ * as "My work / My team". One option is always the chosen one.
42
+ *
43
+ * It is a radio group: screen readers announce the group and which option is
44
+ * checked, the arrow keys move the choice, and Tab enters and leaves the group
45
+ * in one step. Give it a name with `aria-label` (or `aria-labelledby`).
46
+ *
47
+ * ```vue
48
+ * <CnSegmentedControl
49
+ * v-model="view"
50
+ * aria-label="View"
51
+ * :options="[
52
+ * { value: 'mine', label: 'My work' },
53
+ * { value: 'team', label: 'My team' },
54
+ * ]" />
55
+ * ```
56
+ *
57
+ * To switch between panels of content, use `CnTabs` with `variant="segmented"`
58
+ * instead: that keeps the tab and panel semantics and looks the same.
59
+ */
60
+ export default {
61
+ name: 'CnSegmentedControl',
62
+
63
+ props: {
64
+ /**
65
+ * The options. Each is `{ value, label, disabled?, count? }`, or a
66
+ * plain string that is both the value and the label. `count` shows a
67
+ * number after the label.
68
+ *
69
+ * @type {Array<string|{value: (string|number|boolean), label: string, disabled?: boolean, count?: number}>}
70
+ */
71
+ options: {
72
+ type: Array,
73
+ required: true,
74
+ },
75
+
76
+ /**
77
+ * The value of the chosen option (v-model).
78
+ *
79
+ * @type {string|number|boolean|null}
80
+ */
81
+ modelValue: {
82
+ type: [Boolean, String, Number],
83
+ default: null,
84
+ },
85
+
86
+ /** Accessible name of the group, read out when focus enters it. */
87
+ ariaLabel: {
88
+ type: String,
89
+ default: '',
90
+ },
91
+
92
+ /** Id of an element that names the group, as an alternative to `ariaLabel`. */
93
+ ariaLabelledby: {
94
+ type: String,
95
+ default: '',
96
+ },
97
+
98
+ /** Stretch the control to the full width, with options of equal width. */
99
+ stretch: {
100
+ type: Boolean,
101
+ default: false,
102
+ },
103
+ },
104
+
105
+ emits: [
106
+ /**
107
+ * Emitted with the value of the newly chosen option.
108
+ *
109
+ * @event update:modelValue
110
+ * @type {string|number|boolean}
111
+ */
112
+ 'update:modelValue',
113
+ ],
114
+
115
+ computed: {
116
+ /**
117
+ * The options in one shape.
118
+ *
119
+ * @return {Array<{key: string, value: (string|number|boolean), label: string, disabled: boolean, count: (number|null)}>}
120
+ * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-segmented-control
121
+ */
122
+ normalizedOptions() {
123
+ return (Array.isArray(this.options) ? this.options : [])
124
+ .filter((option) => option !== null && option !== undefined)
125
+ .map((option) => {
126
+ if (typeof option !== 'object') {
127
+ return { key: String(option), value: option, label: String(option), disabled: false, count: null }
128
+ }
129
+ return {
130
+ key: String(option.value),
131
+ value: option.value,
132
+ label: String(option.label ?? option.value),
133
+ disabled: option.disabled === true,
134
+ count: Number.isFinite(option.count) ? option.count : null,
135
+ }
136
+ })
137
+ },
138
+
139
+ /**
140
+ * The option Tab lands on: the chosen one, else the first enabled one.
141
+ *
142
+ * @return {number} Its index, or -1 when no option can take focus.
143
+ * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-segmented-control
144
+ */
145
+ focusableIndex() {
146
+ const checked = this.normalizedOptions.findIndex((option) => this.isChecked(option) && !option.disabled)
147
+ if (checked !== -1) {
148
+ return checked
149
+ }
150
+ return this.normalizedOptions.findIndex((option) => !option.disabled)
151
+ },
152
+ },
153
+
154
+ methods: {
155
+ /**
156
+ * Whether an option is the chosen one.
157
+ *
158
+ * @param {{value: (string|number|boolean)}} option The option.
159
+ * @return {boolean}
160
+ * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-segmented-control
161
+ */
162
+ isChecked(option) {
163
+ return this.modelValue !== null && String(option.value) === String(this.modelValue)
164
+ },
165
+
166
+ /**
167
+ * Choose an option.
168
+ *
169
+ * @param {{value: (string|number|boolean), disabled: boolean}} option The option.
170
+ * @return {void}
171
+ * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-segmented-control
172
+ */
173
+ select(option) {
174
+ if (option.disabled || this.isChecked(option)) {
175
+ return
176
+ }
177
+ this.$emit('update:modelValue', option.value)
178
+ },
179
+
180
+ /**
181
+ * Arrow keys, Home and End move the choice, as in a native radio
182
+ * group. Disabled options are passed over and the ends wrap around.
183
+ *
184
+ * @param {KeyboardEvent} event The key press.
185
+ * @return {void}
186
+ * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-segmented-control
187
+ */
188
+ onKeydown(event) {
189
+ const enabled = this.normalizedOptions
190
+ .map((option, index) => ({ option, index }))
191
+ .filter((entry) => !entry.option.disabled)
192
+ if (enabled.length === 0) {
193
+ return
194
+ }
195
+ const buttons = Array.from(this.$el.querySelectorAll('[role="radio"]'))
196
+ const focused = buttons.indexOf(document.activeElement)
197
+ const from = focused !== -1 ? focused : this.focusableIndex
198
+ let position = enabled.findIndex((entry) => entry.index === from)
199
+ if (position === -1) {
200
+ position = 0
201
+ }
202
+ if (event.key === 'ArrowRight' || event.key === 'ArrowDown') {
203
+ position = (position + 1) % enabled.length
204
+ } else if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') {
205
+ position = (position - 1 + enabled.length) % enabled.length
206
+ } else if (event.key === 'Home') {
207
+ position = 0
208
+ } else if (event.key === 'End') {
209
+ position = enabled.length - 1
210
+ } else {
211
+ return
212
+ }
213
+ event.preventDefault()
214
+ const target = enabled[position]
215
+ this.select(target.option)
216
+ if (buttons[target.index] && typeof buttons[target.index].focus === 'function') {
217
+ buttons[target.index].focus()
218
+ }
219
+ },
220
+ },
221
+ }
222
+ </script>
223
+
224
+ <style scoped>
225
+ .cn-segmented-control {
226
+ display: inline-flex;
227
+ gap: 4px;
228
+ max-width: 100%;
229
+ padding: 4px;
230
+ border-radius: var(--border-radius-container, var(--border-radius-large, 10px));
231
+ background-color: var(--color-background-dark);
232
+ overflow-x: auto;
233
+ }
234
+
235
+ .cn-segmented-control--stretch {
236
+ display: flex;
237
+ }
238
+
239
+ .cn-segmented-control--stretch .cn-segmented-control__option {
240
+ flex: 1 1 0;
241
+ }
242
+
243
+ /* The parent class outscores Nextcloud's server rule that gives every plain
244
+ `button` a margin and a border. */
245
+ .cn-segmented-control .cn-segmented-control__option {
246
+ display: inline-flex;
247
+ align-items: center;
248
+ justify-content: center;
249
+ gap: 8px;
250
+ box-sizing: border-box;
251
+ min-height: 36px;
252
+ margin: 0;
253
+ padding: 0 16px;
254
+ border: 1px solid transparent;
255
+ border-radius: var(--border-radius-element, var(--border-radius-large, 8px));
256
+ background-color: transparent;
257
+ /* Full-contrast label on every option: the choice is carried by the
258
+ raised surface, not by dimming the others. */
259
+ color: var(--color-main-text);
260
+ font: inherit;
261
+ font-weight: 600;
262
+ white-space: nowrap;
263
+ cursor: pointer;
264
+ }
265
+
266
+ .cn-segmented-control .cn-segmented-control__option:hover {
267
+ background-color: var(--color-background-hover);
268
+ }
269
+
270
+ .cn-segmented-control .cn-segmented-control__option:focus-visible {
271
+ outline: 2px solid var(--color-primary-element);
272
+ outline-offset: 2px;
273
+ }
274
+
275
+ /* The border survives forced-colours mode, which drops background and shadow. */
276
+ .cn-segmented-control .cn-segmented-control__option--checked,
277
+ .cn-segmented-control .cn-segmented-control__option--checked:hover {
278
+ border-color: var(--color-border-dark);
279
+ background-color: var(--color-main-background);
280
+ box-shadow: 0 1px 2px var(--color-box-shadow);
281
+ }
282
+
283
+ .cn-segmented-control .cn-segmented-control__option[disabled] {
284
+ cursor: default;
285
+ opacity: 0.5;
286
+ }
287
+
288
+ .cn-segmented-control__count {
289
+ font-weight: 400;
290
+ }
291
+ </style>
@@ -0,0 +1,4 @@
1
+ import CnSegmentedControl from './CnSegmentedControl.vue'
2
+
3
+ export default CnSegmentedControl
4
+ export { CnSegmentedControl }
@@ -0,0 +1,15 @@
1
+ One bar cut into segments, with a legend that carries the numbers. This example uses static segments; in an app they usually come from an OpenRegister `source` with `groupBy`.
2
+
3
+ ```vue
4
+ <CnStackedBarWidget
5
+ :content="{
6
+ segments: [
7
+ { label: 'Received', value: 3 },
8
+ { label: 'In progress', value: 6 },
9
+ { label: 'Decision', value: 3 },
10
+ { label: 'Publish', value: 2 },
11
+ ],
12
+ }" />
13
+ ```
14
+
15
+ Props: `content` holds the configuration (see the component description) and `translate` is an optional translate function for the labels in it.