@conduction/nextcloud-vue 2.104.0 → 2.106.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 (224) hide show
  1. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue.js +1 -1
  2. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js.map +1 -1
  3. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js +1 -1
  4. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js.map +1 -1
  5. package/dist/esm/components/CnCardGrid/CnCardGrid.vue.js +1 -1
  6. package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js +18 -0
  7. package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js.map +1 -1
  8. package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js +3 -1
  9. package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js.map +1 -1
  10. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue.js +1 -1
  11. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +18 -0
  12. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
  13. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +1 -1
  14. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
  15. package/dist/esm/components/CnChartWidget/CnChartWidget.vue.js +1 -1
  16. package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js +93 -1
  17. package/dist/esm/components/CnChartWidget/CnChartWidget.vue2.js.map +1 -1
  18. package/dist/esm/components/CnChartWidget/CnChartWidget.vue3.js.map +1 -1
  19. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
  20. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +6 -3
  21. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  22. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +3 -3
  23. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  24. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +112 -5
  25. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  26. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  27. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue.js +9 -0
  28. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue.js.map +1 -0
  29. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue2.js +24 -0
  30. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue2.js.map +1 -0
  31. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue3.js +49 -0
  32. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue3.js.map +1 -0
  33. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue.js +1 -1
  34. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +36 -18
  35. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
  36. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +17 -38
  37. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
  38. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +1 -1
  39. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
  40. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
  41. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue.js +1 -1
  42. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js +1 -1
  43. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js.map +1 -1
  44. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js.map +1 -1
  45. package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue.js +1 -1
  46. package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue2.js +2 -0
  47. package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue2.js.map +1 -1
  48. package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue3.js +32 -29
  49. package/dist/esm/components/CnFeaturesBoard/CnFeaturesBoard.vue3.js.map +1 -1
  50. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue.js +1 -1
  51. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +53 -2
  52. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  53. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +119 -111
  54. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  55. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue.js +1 -1
  56. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js +62 -1
  57. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js.map +1 -1
  58. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue3.js.map +1 -1
  59. package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue.js +1 -1
  60. package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue2.js +73 -4
  61. package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue2.js.map +1 -1
  62. package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue3.js +74 -27
  63. package/dist/esm/components/CnFlowsPage/CnFlowBoard.vue3.js.map +1 -1
  64. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +208 -46
  65. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  66. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +121 -98
  67. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  68. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue.js +1 -1
  69. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js +24 -0
  70. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js.map +1 -1
  71. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js +20 -4
  72. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js.map +1 -1
  73. package/dist/esm/components/CnIndexPage/useSelfFetchList.js +25 -6
  74. package/dist/esm/components/CnIndexPage/useSelfFetchList.js.map +1 -1
  75. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js +112 -1
  76. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js.map +1 -1
  77. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js +100 -44
  78. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js.map +1 -1
  79. package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue.js +1 -1
  80. package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue2.js +67 -4
  81. package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue2.js.map +1 -1
  82. package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue3.js +1 -1
  83. package/dist/esm/components/CnNcWidgetWidget/CnNcWidgetWidget.vue3.js.map +1 -1
  84. package/dist/esm/components/CnNoticeWidget/CnNoticeWidget.vue.js +9 -0
  85. package/dist/esm/components/CnNoticeWidget/CnNoticeWidget.vue.js.map +1 -0
  86. package/dist/esm/components/CnNoticeWidget/CnNoticeWidget.vue2.js +88 -0
  87. package/dist/esm/components/CnNoticeWidget/CnNoticeWidget.vue2.js.map +1 -0
  88. package/dist/esm/components/CnNoticeWidget/CnNoticeWidget.vue3.js +39 -0
  89. package/dist/esm/components/CnNoticeWidget/CnNoticeWidget.vue3.js.map +1 -0
  90. package/dist/esm/components/CnObjectCard/CnObjectCard.vue.js +1 -1
  91. package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js +295 -12
  92. package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js.map +1 -1
  93. package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js +267 -144
  94. package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js.map +1 -1
  95. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue.js +1 -1
  96. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +4 -0
  97. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
  98. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +3 -1
  99. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
  100. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +1 -1
  101. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +93 -1
  102. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
  103. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +322 -246
  104. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
  105. package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue.js +1 -1
  106. package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue2.js +122 -7
  107. package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue2.js.map +1 -1
  108. package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue3.js +189 -93
  109. package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue3.js.map +1 -1
  110. package/dist/esm/components/CnSettingsField/CnSettingsField.vue.js +1 -1
  111. package/dist/esm/components/CnSettingsField/CnSettingsField.vue2.js.map +1 -1
  112. package/dist/esm/components/CnSettingsField/CnSettingsField.vue3.js +3 -3
  113. package/dist/esm/components/CnSettingsField/CnSettingsField.vue3.js.map +1 -1
  114. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue.js +1 -1
  115. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
  116. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js +16 -4
  117. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
  118. package/dist/esm/components/CnTabs/CnTabs.vue.js +1 -1
  119. package/dist/esm/components/CnTabs/CnTabs.vue2.js.map +1 -1
  120. package/dist/esm/components/CnTabs/CnTabs.vue3.js.map +1 -1
  121. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue.js +1 -1
  122. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +42 -0
  123. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
  124. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +38 -19
  125. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
  126. package/dist/esm/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue.js +9 -0
  127. package/dist/esm/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue.js.map +1 -0
  128. package/dist/esm/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue2.js +258 -0
  129. package/dist/esm/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue2.js.map +1 -0
  130. package/dist/esm/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue3.js +69 -0
  131. package/dist/esm/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue3.js.map +1 -0
  132. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue.js +1 -1
  133. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue2.js +11 -0
  134. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue2.js.map +1 -1
  135. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue3.js +4 -4
  136. package/dist/esm/components/CnVersionInfoCard/CnVersionInfoCard.vue3.js.map +1 -1
  137. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js +16 -14
  138. package/dist/esm/components/CnWidgetGrid/builtInWidgets.js.map +1 -1
  139. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +12 -0
  140. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
  141. package/dist/esm/index.js +2 -0
  142. package/dist/esm/index.js.map +1 -1
  143. package/dist/esm/l10n/en.json.js +24 -1
  144. package/dist/esm/l10n/en.json.js.map +1 -1
  145. package/dist/esm/l10n/nl.json.js +24 -1
  146. package/dist/esm/l10n/nl.json.js.map +1 -1
  147. package/dist/esm/library-style.js +1 -1
  148. package/dist/esm/nextcloud-vue.css +1369 -748
  149. package/dist/esm/utils/dueText.js +18 -3
  150. package/dist/esm/utils/dueText.js.map +1 -1
  151. package/dist/esm/utils/libraryWidgetKeys.js +2 -0
  152. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  153. package/dist/esm/utils/nextcloudApps.js +45 -0
  154. package/dist/esm/utils/nextcloudApps.js.map +1 -0
  155. package/dist/esm/utils/scopeListLayout.js +9 -2
  156. package/dist/esm/utils/scopeListLayout.js.map +1 -1
  157. package/dist/esm/utils/sortOrder.js +11 -1
  158. package/dist/esm/utils/sortOrder.js.map +1 -1
  159. package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
  160. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  161. package/dist/esm/utils/visibleWhen.js +12 -4
  162. package/dist/esm/utils/visibleWhen.js.map +1 -1
  163. package/dist/nextcloud-vue.cjs.js +8965 -6497
  164. package/dist/nextcloud-vue.cjs.js.map +2 -2
  165. package/dist/nextcloud-vue.css +1369 -748
  166. package/l10n/en.json +24 -1
  167. package/l10n/nl.json +24 -1
  168. package/package.json +1 -1
  169. package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +1 -1
  170. package/src/components/CnCardGrid/CnCardGrid.md +2 -0
  171. package/src/components/CnCardGrid/CnCardGrid.vue +20 -0
  172. package/src/components/CnCellRenderer/CnCellRenderer.vue +19 -1
  173. package/src/components/CnChartWidget/CnChartWidget.md +2 -0
  174. package/src/components/CnChartWidget/CnChartWidget.vue +93 -1
  175. package/src/components/CnDashboardPage/CnDashboardPage.vue +9 -6
  176. package/src/components/CnDetailPage/CnDetailPage.md +3 -2
  177. package/src/components/CnDetailPage/CnDetailPage.vue +112 -5
  178. package/src/components/CnDetailWidgetHost/CnDetailWidgetHeaderItems.vue +60 -0
  179. package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +36 -29
  180. package/src/components/CnFactsWidget/CnFactsWidget.vue +4 -0
  181. package/src/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue +1 -1
  182. package/src/components/CnFeaturesBoard/CnFeaturesBoard.vue +4 -1
  183. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +70 -4
  184. package/src/components/CnFlowRunsWidget/CnFlowRunsWidget.vue +62 -1
  185. package/src/components/CnFlowsPage/CnFlowBoard.vue +132 -8
  186. package/src/components/CnIndexPage/CnIndexPage.md +8 -5
  187. package/src/components/CnIndexPage/CnIndexPage.vue +236 -54
  188. package/src/components/CnIndexPage/CnIndexPageWidgets.vue +36 -1
  189. package/src/components/CnIndexPage/useSelfFetchList.js +25 -6
  190. package/src/components/CnIndexSidebar/CnIndexSidebar.md +2 -0
  191. package/src/components/CnIndexSidebar/CnIndexSidebar.vue +148 -1
  192. package/src/components/CnNcWidgetWidget/CnNcWidgetWidget.vue +68 -5
  193. package/src/components/CnNoticeWidget/CnNoticeWidget.vue +153 -0
  194. package/src/components/CnNoticeWidget/index.js +12 -0
  195. package/src/components/CnObjectCard/CnObjectCard.md +1 -0
  196. package/src/components/CnObjectCard/CnObjectCard.vue +354 -13
  197. package/src/components/CnObjectSidebar/CnFilesTab.vue +6 -0
  198. package/src/components/CnRecordListWidget/CnRecordListWidget.vue +190 -4
  199. package/src/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue +209 -9
  200. package/src/components/CnSettingsField/CnSettingsField.vue +4 -1
  201. package/src/components/CnSetupWizard/CnSetupWizard.vue +9 -1
  202. package/src/components/CnTabs/CnTabs.vue +11 -0
  203. package/src/components/CnTabsWidget/CnTabsWidget.vue +91 -18
  204. package/src/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.md +11 -0
  205. package/src/components/CnTeamWorkloadWidget/CnTeamWorkloadWidget.vue +382 -0
  206. package/src/components/CnTeamWorkloadWidget/index.js +14 -0
  207. package/src/components/CnVersionInfoCard/CnVersionInfoCard.vue +15 -4
  208. package/src/components/CnWidgetGrid/builtInWidgets.js +2 -0
  209. package/src/components/CnWidgetGrid/registerDashboardWidgets.js +12 -0
  210. package/src/components/index.js +2 -0
  211. package/src/css/dialog.css +20 -2
  212. package/src/css/index-page.css +14 -2
  213. package/src/css/look-board-detail.css +125 -8
  214. package/src/css/look-board-index.css +100 -4
  215. package/src/css/look-board.css +122 -14
  216. package/src/index.js +2 -1
  217. package/src/schemas/app-manifest-v2.schema.json +110 -8
  218. package/src/utils/dueText.js +18 -3
  219. package/src/utils/libraryWidgetKeys.js +2 -0
  220. package/src/utils/nextcloudApps.js +50 -0
  221. package/src/utils/scopeListLayout.js +9 -2
  222. package/src/utils/sortOrder.js +10 -0
  223. package/src/utils/validateManifestV2.compiled.js +3 -3
  224. package/src/utils/visibleWhen.js +12 -4
package/l10n/en.json CHANGED
@@ -1724,7 +1724,26 @@
1724
1724
  "No contacts linked": "No contacts linked",
1725
1725
  "No relations yet": "No relations yet",
1726
1726
  "Period": "Period",
1727
- "On": "On"
1727
+ "On": "On",
1728
+ "Select a step on the canvas to see its settings.": "Select a step on the canvas to see its settings.",
1729
+ "Show runs": "Show runs",
1730
+ "Back to the step settings": "Back to the step settings",
1731
+ "Sort": "Sort",
1732
+ "Direction": "Direction",
1733
+ "Ascending": "Ascending",
1734
+ "Descending": "Descending",
1735
+ "ascending": "ascending",
1736
+ "descending": "descending",
1737
+ "Not sorted": "Not sorted",
1738
+ "Current order: {order}": "Current order: {order}",
1739
+ "Soon": "Soon",
1740
+ "{name} (you)": "{name} (you)",
1741
+ "{present} of {total} present": "{present} of {total} present",
1742
+ "Absent today": "Absent today",
1743
+ "{message} until {time}": "{message} until {time}",
1744
+ "Away": "Away",
1745
+ "Busy": "Busy",
1746
+ "No one in the team": "No one in the team"
1728
1747
  },
1729
1748
  "plurals": {
1730
1749
  "{count} member": [
@@ -1842,6 +1861,10 @@
1842
1861
  "downloaded %n time": [
1843
1862
  "downloaded %n time",
1844
1863
  "downloaded %n times"
1864
+ ],
1865
+ "{count} open": [
1866
+ "{count} open",
1867
+ "{count} open"
1845
1868
  ]
1846
1869
  }
1847
1870
  }
package/l10n/nl.json CHANGED
@@ -1937,7 +1937,26 @@
1937
1937
  "No contacts linked": "Geen contacten gekoppeld",
1938
1938
  "No relations yet": "Nog geen relaties",
1939
1939
  "Period": "Periode",
1940
- "On": "Aan"
1940
+ "On": "Aan",
1941
+ "Select a step on the canvas to see its settings.": "Kies een stap op het canvas om de instellingen te zien.",
1942
+ "Show runs": "Uitvoeringen tonen",
1943
+ "Back to the step settings": "Terug naar de stapinstellingen",
1944
+ "Sort": "Sorteren",
1945
+ "Direction": "Richting",
1946
+ "Ascending": "Oplopend",
1947
+ "Descending": "Aflopend",
1948
+ "ascending": "oplopend",
1949
+ "descending": "aflopend",
1950
+ "Not sorted": "Niet gesorteerd",
1951
+ "Current order: {order}": "Huidige volgorde: {order}",
1952
+ "Soon": "Bijna",
1953
+ "{name} (you)": "{name} (u)",
1954
+ "{present} of {total} present": "{present} van {total} aanwezig",
1955
+ "Absent today": "Vandaag afwezig",
1956
+ "{message} until {time}": "{message} tot {time}",
1957
+ "Away": "Afwezig",
1958
+ "Busy": "Bezet",
1959
+ "No one in the team": "Niemand in het team"
1941
1960
  },
1942
1961
  "plurals": {
1943
1962
  "{count} member": [
@@ -2055,6 +2074,10 @@
2055
2074
  "downloaded %n time": [
2056
2075
  "%n keer gedownload",
2057
2076
  "%n keer gedownload"
2077
+ ],
2078
+ "{count} open": [
2079
+ "{count} open",
2080
+ "{count} open"
2058
2081
  ]
2059
2082
  }
2060
2083
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@conduction/nextcloud-vue",
3
- "version": "2.104.0",
3
+ "version": "2.106.0",
4
4
  "description": "Shared Vue component library for Conduction Nextcloud apps — complements @nextcloud/vue with higher-level components, OpenRegister integration, and NL Design System support",
5
5
  "homepage": "https://github.com/ConductionNL/nextcloud-vue#readme",
6
6
  "bugs": {
@@ -160,7 +160,7 @@
160
160
  <template #additional-items>
161
161
  <!-- The board card: support as one fact row, the SLA address on the same line. -->
162
162
  <div v-if="compactCard && showSupport && !supportAtEnd && !$slots.footer" class="cn-version-info__item" data-testid="cn-admin-settings-support-row">
163
- <span class="cn-version-info__label">{{ supportTitle }}:</span>
163
+ <span class="cn-version-info__label">{{ supportTitle }}</span>
164
164
  <span class="cn-version-info__value">
165
165
  <a :href="`mailto:${supportEmail}`">{{ supportEmail }}</a><template v-if="slaEmail">, {{ slaInline }} <a :href="`mailto:${slaEmail}`">{{ slaEmail }}</a></template>
166
166
  </span>
@@ -113,6 +113,8 @@ Custom card slot — replace the default CnObjectCard with your own rendering:
113
113
  | Prop | Type | Default | Description |
114
114
  |---|---|---|---|
115
115
  | `cardFields` | Array | `null` | Property keys each card's facts list shows |
116
+ | `cardFieldColumns` | Object | `null` | The page's columns by key, passed to each CnObjectCard (see CnObjectCard `cardFieldColumns`) |
117
+ | `cardLayout` | Object | `null` | Board look: the record layout each card draws, passed to CnObjectCard `layout` |
116
118
  | `statusOf` | Function | `null` | `(object) => status` for each card's status pill |
117
119
  | `leadingOf` | Function | `null` | `(object) => { initials | icon }` for each card's leading element |
118
120
  | `footerActionOf` | Function | `null` | `(object) => { label, meta? }` for each card's footer action |
@@ -37,6 +37,8 @@
37
37
  :clickToView="clickToView"
38
38
  :selected="isSelected(object)"
39
39
  :cardFields="cardFields"
40
+ :layout="cardLayout"
41
+ :cardFieldColumns="cardFieldColumns"
40
42
  :status="statusOf ? statusOf(object) : null"
41
43
  :leading="leadingOf ? leadingOf(object) : null"
42
44
  :footerAction="footerActionOf ? footerActionOf(object) : null"
@@ -165,6 +167,24 @@ export default {
165
167
  default: null,
166
168
  },
167
169
 
170
+ /**
171
+ * Board look: the record layout each card draws (manifest key
172
+ * `config.cardLayout`), passed through to CnObjectCard `layout`.
173
+ *
174
+ * @type {object}
175
+ * @spec openspec/changes/board-record-card-layout/specs/board-record-card-layout/spec.md#requirement-a-board-card-can-draw-the-record-layout-from-config
176
+ */
177
+ cardLayout: {
178
+ type: Object,
179
+ default: null,
180
+ },
181
+
182
+ /** The page's columns by key, passed through to CnObjectCard. */
183
+ cardFieldColumns: {
184
+ type: Object,
185
+ default: null,
186
+ },
187
+
168
188
  /** Board look: `(object) => status` for each card's status pill. See CnObjectCard `status`. */
169
189
  statusOf: {
170
190
  type: Function,
@@ -194,7 +194,7 @@
194
194
  <time
195
195
  v-if="dateTimestamp"
196
196
  class="cn-cell-renderer__date"
197
- :class="dueResult.tone === 'danger' ? 'cn-cell-renderer__date--error' : null"
197
+ :class="dueClass"
198
198
  :datetime="dateTimestamp.toISOString()"
199
199
  :title="absoluteDateLabel"
200
200
  data-testid="cn-cell-renderer-due">{{ dueResult.text }}</time>
@@ -780,6 +780,24 @@ export default {
780
780
  return Number.isFinite(size) && size > 0 ? size : (this.isBoardLook ? 32 : 24)
781
781
  },
782
782
 
783
+ /**
784
+ * `date` widget with `widgetProps.due`: the colour class. The
785
+ * `variantWhen` rules win when they match; else an overdue date reads
786
+ * as an error and a soon one (`due.soonWithin`) as a warning.
787
+ *
788
+ * @return {string|null} The class, or null for the plain colour.
789
+ * @spec openspec/changes/due-text-soon/specs/due-text-soon/spec.md#requirement-a-due-date-within-a-few-days-reads-as-soon
790
+ */
791
+ dueClass() {
792
+ if (this.dateVariant) {
793
+ return 'cn-cell-renderer__date--' + this.dateVariant
794
+ }
795
+ if (this.dueResult && this.dueResult.tone === 'danger') {
796
+ return 'cn-cell-renderer__date--error'
797
+ }
798
+ return this.dueResult && this.dueResult.soon ? 'cn-cell-renderer__date--warning' : null
799
+ },
800
+
783
801
  /**
784
802
  * `date` widget: the variant of the first `widgetProps.variantWhen`
785
803
  * rule the date matches, or '' for none.
@@ -108,6 +108,8 @@ Fallback slot — shown when ApexCharts is not available:
108
108
  | `legendPosition` | String | `''` | Legend placement override (`top`/`bottom`/`left`/`right`); empty keeps the automatic placement |
109
109
  | `barMax` | Number | `null` | `bar-list`: the value a full track stands for (100 for percentages); null takes the largest value |
110
110
  | `footnote` | String | `''` | A line under the chart, through the host's translate: under the rows of a `bar-list` or `matrix`, under the plot of a `line`, `area` or `bar` chart |
111
+ | `referenceLines` | Array | `[]` | Dashed horizontal reference lines on the value axis of a line, area or bar chart, such as a norm: `[{ value, label?, color? }]`. The label goes through the host's translate; the colour defaults to the theme's error colour (board DqDoorlooptijd). |
112
+ | `categoryFormat` | String | `''` | How date categories read on the x-axis: `month` ("jan") or `month-year` ("jan 2026"), in the user's language. Categories that are not dates are left as is. |
111
113
  | `emptyCellLabel` | String | `''` | `matrix`: the text of a cell without a value ("geen"), through the host's translate. Empty leaves the cell blank |
112
114
  | `highlight` | Object | `null` | `matrix`: the cell to outline, `{ row, column, columnKeys? }`, tokens such as `@object.behaviour` resolved |
113
115
  | `cellSource` | Object | `null` | `matrix`: read the cells from records that each hold one cell (`{ register, schema, filter?, rowField, columnField, valueField, … }`), or with `cellsPath` from the cells inside one record, see [Bar list and matrix](#bar-list-and-matrix) |
@@ -220,6 +220,22 @@ function parseBucketKey(key) {
220
220
  )
221
221
  }
222
222
 
223
+ /**
224
+ * A theme colour by CSS variable name, as a literal: ApexCharts writes colours
225
+ * into SVG attributes, where `var()` does not resolve.
226
+ *
227
+ * @param {string} name The variable, e.g. `--color-error`.
228
+ * @param {string} fallback The colour when the variable is unset (or no DOM).
229
+ * @return {string} The colour.
230
+ */
231
+ function themeColor(name, fallback) {
232
+ if (typeof document === 'undefined' || typeof getComputedStyle !== 'function') {
233
+ return fallback
234
+ }
235
+ const value = getComputedStyle(document.documentElement).getPropertyValue(name).trim()
236
+ return value || fallback
237
+ }
238
+
223
239
  /** Event-bus channel CnWidgetWrapper's Refresh action broadcasts on. */
224
240
  const REFRESH_BUS_CHANNEL = 'cn:widget:refresh'
225
241
 
@@ -385,6 +401,31 @@ export default {
385
401
  default: '',
386
402
  },
387
403
 
404
+ /**
405
+ * Horizontal reference lines on the value axis of a line, area or bar
406
+ * chart, such as a norm: `[{ value, label?, color? }]`. Drawn dashed;
407
+ * the label goes through the host's translate. The colour defaults to
408
+ * the theme's error colour.
409
+ *
410
+ * @type {Array<{value: number, label?: string, color?: string}>}
411
+ */
412
+ referenceLines: {
413
+ type: Array,
414
+ default: () => [],
415
+ },
416
+
417
+ /**
418
+ * How date categories read on the x-axis: `month` ("jan") or
419
+ * `month-year` ("jan 2026"), in the user's language. A category that
420
+ * is not a date is shown as is. Empty shows the categories unchanged.
421
+ *
422
+ * @type {''|'month'|'month-year'}
423
+ */
424
+ categoryFormat: {
425
+ type: String,
426
+ default: '',
427
+ },
428
+
388
429
  /**
389
430
  * The `matrix` cell to outline, usually the record's on a detail page:
390
431
  * `{ row, column, columnKeys? }`, where `row` and `column` take tokens
@@ -1505,6 +1546,51 @@ export default {
1505
1546
  return this.footnote ? this.hostTranslate(this.footnote) : t('nextcloud-vue', 'Chart data')
1506
1547
  },
1507
1548
 
1549
+ /**
1550
+ * The x-axis categories as shown: with `categoryFormat`, date
1551
+ * categories as short months in the user's language.
1552
+ *
1553
+ * @return {Array<string|number>} The categories.
1554
+ */
1555
+ displayCategories() {
1556
+ const categories = Array.isArray(this.resolvedCategories) ? this.resolvedCategories : []
1557
+ if (this.categoryFormat !== 'month' && this.categoryFormat !== 'month-year') {
1558
+ return categories
1559
+ }
1560
+ const options = this.categoryFormat === 'month' ? { month: 'short' } : { month: 'short', year: 'numeric' }
1561
+ return categories.map((category) => {
1562
+ const date = typeof category === 'string' || typeof category === 'number' ? parseBucketKey(category) : null
1563
+ return date ? date.toLocaleDateString(userLocale(), options) : category
1564
+ })
1565
+ },
1566
+
1567
+ /**
1568
+ * The `referenceLines` as ApexCharts annotations: dashed, labelled,
1569
+ * in the theme's error colour unless a line names its own.
1570
+ *
1571
+ * @return {Array<object>} The annotations, possibly empty.
1572
+ */
1573
+ referenceAnnotations() {
1574
+ const lines = (Array.isArray(this.referenceLines) ? this.referenceLines : [])
1575
+ .filter((line) => line && Number.isFinite(Number(line.value)))
1576
+ if (lines.length === 0) {
1577
+ return []
1578
+ }
1579
+ const fallback = themeColor('--color-error', '#e04224')
1580
+ return lines.map((line) => {
1581
+ const color = line.color || fallback
1582
+ return {
1583
+ y: Number(line.value),
1584
+ x: Number(line.value),
1585
+ borderColor: color,
1586
+ strokeDashArray: 6,
1587
+ label: line.label
1588
+ ? { text: this.hostTranslate(line.label), borderColor: color, style: { color: '#fff', background: color } }
1589
+ : { text: '' },
1590
+ }
1591
+ })
1592
+ },
1593
+
1508
1594
  mergedOptions() {
1509
1595
  const isPieType = ['pie', 'donut', 'radialBar'].includes(this.type)
1510
1596
 
@@ -1592,7 +1678,7 @@ export default {
1592
1678
  if (!isPieType) {
1593
1679
  if (this.resolvedCategories.length > 0) {
1594
1680
  defaults.xaxis = {
1595
- categories: this.resolvedCategories,
1681
+ categories: this.displayCategories,
1596
1682
  labels: {
1597
1683
  style: {
1598
1684
  colors: 'var(--color-text-maxcontrast, #767676)',
@@ -1634,6 +1720,12 @@ export default {
1634
1720
  }
1635
1721
  }
1636
1722
 
1723
+ // Reference lines, such as a norm, on the value axis.
1724
+ if (!isPieType && this.referenceAnnotations.length > 0) {
1725
+ const axisKey = (this.type === 'bar' && this.horizontal) ? 'xaxis' : 'yaxis'
1726
+ defaults.annotations = { [axisKey]: this.referenceAnnotations }
1727
+ }
1728
+
1637
1729
  // Add labels for pie/donut
1638
1730
  if (isPieType && this.resolvedLabels.length > 0) {
1639
1731
  defaults.labels = this.resolvedLabels
@@ -347,12 +347,12 @@
347
347
  <label
348
348
  v-else
349
349
  class="cn-dashboard-page__page-filter">
350
- <span v-if="pf.label" class="cn-dashboard-page__page-filter-label">{{ pf.label }}</span>
350
+ <span v-if="pf.label" class="cn-dashboard-page__page-filter-label">{{ effectiveTranslate(pf.label) }}</span>
351
351
  <NcSelect
352
352
  :modelValue="selectedPageFilterOption(pf)"
353
- :options="pf.options || []"
353
+ :options="pageFilterSegments(pf)"
354
354
  :clearable="false"
355
- :inputLabel="pf.label || pf.key"
355
+ :inputLabel="pf.label ? effectiveTranslate(pf.label) : pf.key"
356
356
  label="label"
357
357
  :data-testid="'cn-page-filter-' + pf.key"
358
358
  @update:modelValue="onPageFilterChange(pf, $event)" />
@@ -1106,6 +1106,9 @@ const CHART_PROP_KEYS = [
1106
1106
  'highlight',
1107
1107
  'cellSource',
1108
1108
  'emptyCellLabel',
1109
+ // A norm line and month categories on a line chart (DqDoorlooptijd).
1110
+ 'referenceLines',
1111
+ 'categoryFormat',
1109
1112
  ]
1110
1113
 
1111
1114
  /**
@@ -2886,12 +2889,12 @@ export default {
2886
2889
  return null
2887
2890
  }
2888
2891
  const current = this.workspaceContext[pf.key]
2889
- return (pf.options || []).find((o) => o.value === current) || null
2892
+ return this.pageFilterSegments(pf).find((o) => o.value === current) || null
2890
2893
  },
2891
2894
 
2892
2895
  /**
2893
- * A page filter's options as segments, labels through the host
2894
- * translate.
2896
+ * A page filter's options, labels through the host translate: the
2897
+ * segments of a segmented filter and the options of a select.
2895
2898
  *
2896
2899
  * @spec openspec/changes/dashboard-board-termijnen/specs/dashboard-page/spec.md#requirement-a-page-filter-as-a-segmented-group-on-the-description-line
2897
2900
  * @param {object} pf The page-filter descriptor.
@@ -421,12 +421,13 @@ Four levels of action and the case surfaces around them. All opt in. See [the re
421
421
  | `stageField` | String | `'status'` | Dot-path to the record's stage. |
422
422
  | `quickActions` | Array | `[]` | At most three always visible header buttons. |
423
423
  | `actionsMenu` | Object \| null | `null` | `{ showRefresh?, showHelpLinks?, label? }` for the overflow menu. |
424
- | `nextStep` | Object \| null | `null` | The "what now" card per stage, rendered by `CnNextStepCard`. |
424
+ | `nextStep` | Object \| null | `null` | The "what now" card per stage, rendered by `CnNextStepCard`. `stagesFrom` (a field path such as `@context.caseType.nextStep`) reads the stages from the record when it holds them. |
425
425
  | `typePill` | Object \| null | `null` | `{ field, colorMap?, labels?, variant? }`, a pill above the title. Instead of `field`: `text` (a constant) or `countFrom` + `countLabel` (a count of a related list). |
426
426
  | `statusPill` | Object \| null | `null` | A second pill, same shape. |
427
427
  | `headerFields` | Array | `[]` | Fields shown as chips under the title (manifest `config.headerFields`). Each entry is a property key, or `{ key, format, labelField, colorField, warnWhenPast }`; `format` is `text` (default), `mono`, `badge`, `user` or `date`. A `$ref` value shows `labelField` of the referenced object and falls back to the raw id in mono; a badge maps `colorField` (`success`, `warning`, `error`, `info`, `neutral`) to a variant; `warnWhenPast` gives a past date the error variant. An empty value shows no chip, a page without the key shows no row. Each chip reads "Title: value" to a screen reader, and the row prints as plain text. |
428
- | `sideColumn` | Array | `[]` | Widget definitions or widget ids, rendered as a column of cards beside the body. |
428
+ | `sideColumn` | Array | `[]` | Widget definitions or widget ids, rendered as a column of cards beside the body. An entry with `tabs` shows only while one of those body tabs is open. |
429
429
  | `sideColumnFrom` | String | `''` | A field path, read like a `visibleWhen` field (e.g. `@context.caseType.sideCards`), whose list of ids picks the `sideColumn` entries this record shows and their order. Any other value keeps `sideColumn` as declared. |
430
+ | `tabsFrom` | String | `''` | A field path, read like a `visibleWhen` field, whose list of tab ids picks the tabs of the page's `tabs` widgets and their order for this record: `@context.caseType.tabs` reads the list a case type keeps (DqAanbesteding: Inschrijvingen, Publicaties, Bestanden, Historie). A tab's id is its `id`, else its `widgetId`, or the string entry. An empty or missing list keeps the declared tabs; edit mode shows them all. |
430
431
  | `isAdmin` | Boolean \| null | `null` | Shows `adminOnly` header actions. `null` reads it from Nextcloud. |
431
432
 
432
433
  ## The record as a place
@@ -1540,6 +1540,12 @@ export default {
1540
1540
  },
1541
1541
  },
1542
1542
 
1543
+ // The open body tab, reported by a `tabs` widget, so a side entry
1544
+ // with `tabs` shows only beside those tabs.
1545
+ cnDetailReportTab: (id) => {
1546
+ this.activeBodyTab = typeof id === 'string' ? id : ''
1547
+ },
1548
+
1543
1549
  // The records `visibilityContext` names, keyed `@context.<name>`, so a tab's
1544
1550
  // `visibleWhen` reads the case type as a grid widget's does. Null
1545
1551
  // while they are on their way.
@@ -2462,8 +2468,12 @@ export default {
2462
2468
 
2463
2469
  /**
2464
2470
  * The "what now" card (manifest `config.nextStep`):
2465
- * `{ field?, stages: { <stage>: { title?, checklist, after? } } }`.
2471
+ * `{ field?, stagesFrom?, stages: { <stage>: { title?, checklist, after? } } }`.
2466
2472
  * Each checklist item is `{ label, doneField?, doneWhen?, hint? }`.
2473
+ * `stagesFrom` is a field path read like a `visibleWhen` field
2474
+ * (`@context.caseType.nextStep`): when it holds a stages map (or an
2475
+ * object with `stages`), that map replaces `stages` for this record, so
2476
+ * a case type keeps its own stage texts. Otherwise `stages` stays.
2467
2477
  * Rendered above the body for a stage that declares a checklist, with
2468
2478
  * the primary button inside it. See `CnNextStepCard`.
2469
2479
  *
@@ -2567,13 +2577,31 @@ export default {
2567
2577
  default: null,
2568
2578
  },
2569
2579
 
2580
+ /**
2581
+ * A field path whose value picks the tabs of the page's `tabs` widgets
2582
+ * and their order for this record (manifest `config.tabsFrom`), read
2583
+ * like a `visibleWhen` field: `@context.caseType.tabs` reads the list a
2584
+ * case type keeps. A non-empty list of tab ids (a tab's `id`, else its
2585
+ * `widgetId`, or the string entry itself) keeps those tabs in that
2586
+ * order and leaves the rest out. Any other value keeps the declared
2587
+ * tabs. Edit mode shows them all.
2588
+ *
2589
+ * @spec openspec/changes/detail-tabs-from/specs/detail-page/spec.md#requirement-a-record-can-pick-its-tabs-and-their-order
2590
+ */
2591
+ tabsFrom: {
2592
+ type: String,
2593
+ default: '',
2594
+ },
2595
+
2570
2596
  /**
2571
2597
  * A column of cards beside the body (manifest `config.sideColumn`),
2572
2598
  * for the record's facts: deadline, requester, handler, links. Each
2573
2599
  * entry is a widget definition (`{ id?, type, title?, content? }`) or
2574
2600
  * the id of a widget declared in `widgets`. The column stacks under
2575
2601
  * the body on a narrow page. Empty (the default) leaves the layout as
2576
- * it is.
2602
+ * it is. An entry with `tabs` (tab ids of the body's `tabs` widget)
2603
+ * shows only while one of those tabs is open (DqZaaktypeVelden: the
2604
+ * option list beside Velden).
2577
2605
  *
2578
2606
  * @type {Array<string|object>}
2579
2607
  */
@@ -2886,6 +2914,8 @@ export default {
2886
2914
 
2887
2915
  data() {
2888
2916
  return {
2917
+ /** The id of the open body tab a `tabs` widget reported, '' before one did. */
2918
+ activeBodyTab: '',
2889
2919
  /**
2890
2920
  * The records `visibilityContext` names, keyed `@context.<name>`: the row,
2891
2921
  * `null` when it could not be read, absent while it is on its way.
@@ -3386,6 +3416,25 @@ export default {
3386
3416
  return !this.objectNotFound && !this.showLoadingState && !this.error && !this.empty
3387
3417
  },
3388
3418
 
3419
+ /**
3420
+ * `nextStep` with its stages read from the record when `stagesFrom`
3421
+ * names a stages map there (a case type's own texts), else as declared.
3422
+ *
3423
+ * @spec openspec/changes/detail-next-step-from-record/specs/detail-page/spec.md#requirement-the-next-step-stages-can-come-from-the-record
3424
+ * @return {object|null}
3425
+ */
3426
+ effectiveNextStep() {
3427
+ const config = this.nextStep
3428
+ if (!config || typeof config !== 'object' || typeof config.stagesFrom !== 'string' || config.stagesFrom === '' || !this.widgetVisibilityData) {
3429
+ return config
3430
+ }
3431
+ const found = readVisibleWhenPath(this.widgetVisibilityData, config.stagesFrom)
3432
+ const stages = found && typeof found === 'object' && !Array.isArray(found)
3433
+ ? (found.stages && typeof found.stages === 'object' && !Array.isArray(found.stages) ? found.stages : found)
3434
+ : null
3435
+ return stages && Object.keys(stages).length > 0 ? { ...config, stages } : config
3436
+ },
3437
+
3389
3438
  /**
3390
3439
  * The "what now" card for the record's stage, labels translated.
3391
3440
  *
@@ -3393,7 +3442,7 @@ export default {
3393
3442
  * @spec openspec/changes/detail-action-model-and-case-surfaces/specs/detail-action-model/spec.md#requirement-next-step-card
3394
3443
  */
3395
3444
  nextStepCard() {
3396
- const card = resolveNextStep(this.nextStep, this.currentObject, this.stageField)
3445
+ const card = resolveNextStep(this.effectiveNextStep, this.currentObject, this.stageField)
3397
3446
  if (!card) {
3398
3447
  return null
3399
3448
  }
@@ -3508,7 +3557,7 @@ export default {
3508
3557
  * @spec openspec/changes/detail-side-visible-when/specs/detail-page/spec.md#requirement-a-side-column-widget-shows-only-while-its-visiblewhen-holds
3509
3558
  */
3510
3559
  sideColumnWidgets() {
3511
- const resolved = this.pickedSideWidgets.filter((widget) => this.sideWidgetVisible(widget))
3560
+ const resolved = this.pickedSideWidgets.filter((widget) => this.sideWidgetOnTab(widget) && this.sideWidgetVisible(widget))
3512
3561
  if (!this.isBoard) {
3513
3562
  return resolved
3514
3563
  }
@@ -4551,7 +4600,7 @@ export default {
4551
4600
  */
4552
4601
  bodyGridWidgets() {
4553
4602
  if (this.hasGridLayout) {
4554
- return this.widgets
4603
+ return this.pickTabs(this.widgets)
4555
4604
  }
4556
4605
  if (this.shouldRenderAutoBody) {
4557
4606
  return this.autoBodyWidgets || []
@@ -4559,6 +4608,21 @@ export default {
4559
4608
  return []
4560
4609
  },
4561
4610
 
4611
+ /**
4612
+ * The tab ids `tabsFrom` reads off this record, or null when it reads
4613
+ * no non-empty list (or the body is being edited).
4614
+ *
4615
+ * @spec openspec/changes/detail-tabs-from/specs/detail-page/spec.md#requirement-a-record-can-pick-its-tabs-and-their-order
4616
+ * @return {Array<string>|null}
4617
+ */
4618
+ pickedTabIds() {
4619
+ if (!this.tabsFrom || this.editingBody || !this.widgetVisibilityData) {
4620
+ return null
4621
+ }
4622
+ const ids = readVisibleWhenPath(this.widgetVisibilityData, this.tabsFrom)
4623
+ return Array.isArray(ids) && ids.length > 0 ? ids.map((id) => String(id)) : null
4624
+ },
4625
+
4562
4626
  /**
4563
4627
  * Whether the body renders as an adjustable widget grid (vs. the
4564
4628
  * hand-authored default slot). True for explicit grid pages and for the
@@ -4977,6 +5041,33 @@ export default {
4977
5041
  },
4978
5042
 
4979
5043
  methods: {
5044
+ /**
5045
+ * The widget definitions with each `tabs` widget's tabs narrowed and
5046
+ * ordered by `pickedTabIds`; the definitions as given without a pick.
5047
+ * A new definition is returned for a changed widget; the declared one
5048
+ * is never mutated.
5049
+ *
5050
+ * @spec openspec/changes/detail-tabs-from/specs/detail-page/spec.md#requirement-a-record-can-pick-its-tabs-and-their-order
5051
+ * @param {Array<object>} widgets The widget definitions.
5052
+ * @return {Array<object>}
5053
+ */
5054
+ pickTabs(widgets) {
5055
+ const ids = this.pickedTabIds
5056
+ if (!ids || !Array.isArray(widgets)) {
5057
+ return widgets
5058
+ }
5059
+ const idOf = (tab) => String(typeof tab === 'string' ? tab : (tab && (tab.id ?? tab.widgetId)) ?? '')
5060
+ return widgets.map((widget) => {
5061
+ if (!widget || widget.type !== 'tabs' || !Array.isArray(widget.content?.tabs)) {
5062
+ return widget
5063
+ }
5064
+ const tabs = ids
5065
+ .map((id) => widget.content.tabs.find((tab) => idOf(tab) === id))
5066
+ .filter(Boolean)
5067
+ return tabs.length > 0 ? { ...widget, content: { ...widget.content, tabs } } : widget
5068
+ })
5069
+ },
5070
+
4980
5071
  /**
4981
5072
  * The tag after an admin-only entry in the board look ("beheerder",
4982
5073
  * DqZaakMeer), or null for any other entry or look. It is drawn by
@@ -6158,6 +6249,22 @@ export default {
6158
6249
  return compactLayoutRows(drawable)
6159
6250
  },
6160
6251
 
6252
+ /**
6253
+ * Whether a side entry belongs beside the open body tab: an entry with
6254
+ * `tabs` (tab ids) shows only while one of them is open; an entry
6255
+ * without shows beside every tab. Edit mode shows them all.
6256
+ *
6257
+ * @spec openspec/changes/detail-side-cards-per-tab/specs/detail-page/spec.md#requirement-a-side-card-can-follow-the-open-tab
6258
+ * @param {object} widget The side entry's definition.
6259
+ * @return {boolean}
6260
+ */
6261
+ sideWidgetOnTab(widget) {
6262
+ if (this.editingBody || !Array.isArray(widget.tabs) || widget.tabs.length === 0) {
6263
+ return true
6264
+ }
6265
+ return widget.tabs.map((id) => String(id)).includes(this.activeBodyTab)
6266
+ },
6267
+
6161
6268
  /**
6162
6269
  * Whether a side column widget shows: always in edit mode or without a
6163
6270
  * `visibleWhen`, else while it holds for the record and the records
@@ -0,0 +1,60 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: 2026 Conduction B.V.
3
+ SPDX-License-Identifier: EUPL-1.2
4
+ -->
5
+ <template>
6
+ <span class="cn-detail-widget-header-items">
7
+ <CnStatusBadge
8
+ v-if="header.pill"
9
+ :label="header.pill.label"
10
+ :variant="header.pill.variant"
11
+ data-testid="cn-detail-widget-header-pill" />
12
+ <span
13
+ v-if="header.note"
14
+ class="cn-detail-page__catalog-note"
15
+ data-testid="cn-detail-widget-header-note">{{ header.note }}</span>
16
+ <button
17
+ v-for="(action, index) in header.actions"
18
+ :key="`${index}-${action.label}`"
19
+ type="button"
20
+ class="cn-detail-page__catalog-action"
21
+ data-testid="cn-detail-widget-header-action"
22
+ @click="action.run()">
23
+ <CnIcon v-if="action.icon" :name="action.icon" :size="16" />
24
+ {{ action.label }}
25
+ </button>
26
+ </span>
27
+ </template>
28
+
29
+ <script>
30
+ import CnIcon from '../CnIcon/CnIcon.vue'
31
+ import CnStatusBadge from '../CnStatusBadge/CnStatusBadge.vue'
32
+
33
+ /**
34
+ * The extras a widget reports for its card's title row through its `header`
35
+ * event: a status pill, a note and its actions. Drawn by CnDetailWidgetHost in
36
+ * the `#actions` slot of the card it wraps a widget in.
37
+ */
38
+ export default {
39
+ name: 'CnDetailWidgetHeaderItems',
40
+
41
+ components: { CnIcon, CnStatusBadge },
42
+
43
+ props: {
44
+ /** The reported header: `{ pill, note, actions: [{ label, icon?, run }] }`. */
45
+ header: {
46
+ type: Object,
47
+ required: true,
48
+ },
49
+ },
50
+ }
51
+ </script>
52
+
53
+ <style scoped>
54
+ .cn-detail-widget-header-items {
55
+ display: inline-flex;
56
+ flex-wrap: wrap;
57
+ align-items: center;
58
+ gap: 12px;
59
+ }
60
+ </style>