@conduction/nextcloud-vue 2.80.0 → 2.81.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 (290) hide show
  1. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
  2. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +3 -3
  3. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
  4. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +80 -4
  5. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  6. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +95 -91
  7. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  8. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js +140 -10
  9. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js.map +1 -1
  10. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js +52 -16
  11. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js.map +1 -1
  12. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +17 -3
  13. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
  14. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +63 -52
  15. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
  16. package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue.js +10 -0
  17. package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue.js.map +1 -0
  18. package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue2.js +252 -0
  19. package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue2.js.map +1 -0
  20. package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue3.js +140 -0
  21. package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue3.js.map +1 -0
  22. package/dist/esm/components/CnChipsFieldWidget/index.js +26 -0
  23. package/dist/esm/components/CnChipsFieldWidget/index.js.map +1 -0
  24. package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue.js +10 -0
  25. package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue.js.map +1 -0
  26. package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue2.js +354 -0
  27. package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue2.js.map +1 -0
  28. package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue3.js +172 -0
  29. package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue3.js.map +1 -0
  30. package/dist/esm/components/CnChoicePickerWidget/index.js +27 -0
  31. package/dist/esm/components/CnChoicePickerWidget/index.js.map +1 -0
  32. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js +1 -1
  33. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js.map +1 -1
  34. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue3.js.map +1 -1
  35. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js +2 -2
  36. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js.map +1 -1
  37. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue3.js.map +1 -1
  38. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +65 -5
  39. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  40. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +2 -1
  41. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  42. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +19 -11
  43. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  44. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +3 -2
  45. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  46. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +72 -0
  47. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  48. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +21 -4
  49. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  50. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue2.js +1 -1
  51. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue2.js.map +1 -1
  52. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue3.js.map +1 -1
  53. package/dist/esm/components/CnFacetSidebar/CnFacetSidebar.vue2.js +7 -7
  54. package/dist/esm/components/CnFacetSidebar/CnFacetSidebar.vue2.js.map +1 -1
  55. package/dist/esm/components/CnFacetSidebar/CnFacetSidebar.vue3.js.map +1 -1
  56. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +131 -0
  57. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
  58. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
  59. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js +80 -5
  60. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js.map +1 -1
  61. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js +282 -69
  62. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js.map +1 -1
  63. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +302 -4
  64. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  65. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +417 -244
  66. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  67. package/dist/esm/components/CnFilesBrowser/filesBrowser.js +101 -1
  68. package/dist/esm/components/CnFilesBrowser/filesBrowser.js.map +1 -1
  69. package/dist/esm/components/CnFilesBrowser/filesBrowserColumns.js +141 -1
  70. package/dist/esm/components/CnFilesBrowser/filesBrowserColumns.js.map +1 -1
  71. package/dist/esm/components/CnFormPage/CnFormPage.vue2.js +60 -0
  72. package/dist/esm/components/CnFormPage/CnFormPage.vue2.js.map +1 -1
  73. package/dist/esm/components/CnFormPage/CnFormPage.vue3.js +74 -49
  74. package/dist/esm/components/CnFormPage/CnFormPage.vue3.js.map +1 -1
  75. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +29 -2
  76. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  77. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +48 -4
  78. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  79. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js +7 -7
  80. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js.map +1 -1
  81. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js.map +1 -1
  82. package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue2.js +1 -1
  83. package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue2.js.map +1 -1
  84. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +65 -2
  85. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
  86. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +6 -2
  87. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
  88. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +63 -1
  89. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
  90. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +8 -1
  91. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
  92. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js +14 -0
  93. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js.map +1 -1
  94. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue3.js +5 -1
  95. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue3.js.map +1 -1
  96. package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue.js +10 -0
  97. package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue.js.map +1 -0
  98. package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue2.js +174 -0
  99. package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue2.js.map +1 -0
  100. package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue3.js +75 -0
  101. package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue3.js.map +1 -0
  102. package/dist/esm/components/CnRadioCardsWidget/index.js +28 -0
  103. package/dist/esm/components/CnRadioCardsWidget/index.js.map +1 -0
  104. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +30 -6
  105. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
  106. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +34 -25
  107. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
  108. package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue.js +10 -0
  109. package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue.js.map +1 -0
  110. package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue2.js +222 -0
  111. package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue2.js.map +1 -0
  112. package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue3.js +64 -0
  113. package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue3.js.map +1 -0
  114. package/dist/esm/components/CnSavedViewsWidget/index.js +30 -0
  115. package/dist/esm/components/CnSavedViewsWidget/index.js.map +1 -0
  116. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue2.js.map +1 -1
  117. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue3.js.map +1 -1
  118. package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue.js +10 -0
  119. package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue.js.map +1 -0
  120. package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue2.js +347 -0
  121. package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue2.js.map +1 -0
  122. package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue3.js +184 -0
  123. package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue3.js.map +1 -0
  124. package/dist/esm/components/CnSettingsFormWidget/index.js +27 -0
  125. package/dist/esm/components/CnSettingsFormWidget/index.js.map +1 -0
  126. package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js +87 -6
  127. package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
  128. package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
  129. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js +84 -1
  130. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js.map +1 -1
  131. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js +85 -41
  132. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js.map +1 -1
  133. package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue.js +10 -0
  134. package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue.js.map +1 -0
  135. package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue2.js +159 -0
  136. package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue2.js.map +1 -0
  137. package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue3.js +60 -0
  138. package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue3.js.map +1 -0
  139. package/dist/esm/components/CnSwitchRowsWidget/index.js +26 -0
  140. package/dist/esm/components/CnSwitchRowsWidget/index.js.map +1 -0
  141. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +47 -1
  142. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
  143. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +16 -14
  144. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
  145. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js +80 -1
  146. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js.map +1 -1
  147. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js +75 -32
  148. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js.map +1 -1
  149. package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue.js +10 -0
  150. package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue.js.map +1 -0
  151. package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue2.js +269 -0
  152. package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue2.js.map +1 -0
  153. package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue3.js +106 -0
  154. package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue3.js.map +1 -0
  155. package/dist/esm/components/CnToggleTableWidget/index.js +27 -0
  156. package/dist/esm/components/CnToggleTableWidget/index.js.map +1 -0
  157. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +7 -0
  158. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
  159. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +7 -2
  160. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
  161. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
  162. package/dist/esm/components/CnWidgetObjectTable/dashboardRegistration.js +5 -0
  163. package/dist/esm/components/CnWidgetObjectTable/dashboardRegistration.js.map +1 -1
  164. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js +197 -6
  165. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js.map +1 -1
  166. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js +96 -50
  167. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js.map +1 -1
  168. package/dist/esm/composables/useListView.js +4 -4
  169. package/dist/esm/composables/useListView.js.map +1 -1
  170. package/dist/esm/index.js +17 -1
  171. package/dist/esm/index.js.map +1 -1
  172. package/dist/esm/l10n/en.json.js +9 -1
  173. package/dist/esm/l10n/en.json.js.map +1 -1
  174. package/dist/esm/l10n/nl.json.js +55 -1
  175. package/dist/esm/l10n/nl.json.js.map +1 -1
  176. package/dist/esm/nextcloud-vue.css +1041 -12
  177. package/dist/esm/utils/contentHeaderLink.js +51 -0
  178. package/dist/esm/utils/contentHeaderLink.js.map +1 -0
  179. package/dist/esm/utils/facets.js +75 -3
  180. package/dist/esm/utils/facets.js.map +1 -1
  181. package/dist/esm/utils/fetchFilterCounts.js +40 -5
  182. package/dist/esm/utils/fetchFilterCounts.js.map +1 -1
  183. package/dist/esm/utils/fillTemplate.js +7 -1
  184. package/dist/esm/utils/fillTemplate.js.map +1 -1
  185. package/dist/esm/utils/initials.js +32 -0
  186. package/dist/esm/utils/initials.js.map +1 -0
  187. package/dist/esm/utils/libraryWidgetKeys.js +7 -0
  188. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  189. package/dist/esm/utils/objectWrite.js +59 -0
  190. package/dist/esm/utils/objectWrite.js.map +1 -0
  191. package/dist/esm/utils/pageControls.js +81 -0
  192. package/dist/esm/utils/pageControls.js.map +1 -0
  193. package/dist/esm/utils/personalLenses.js +40 -3
  194. package/dist/esm/utils/personalLenses.js.map +1 -1
  195. package/dist/esm/utils/resolveFilterTokens.js +1 -1
  196. package/dist/esm/utils/resolveFilterTokens.js.map +1 -1
  197. package/dist/esm/utils/resolveManifestTokens.js +2 -0
  198. package/dist/esm/utils/resolveManifestTokens.js.map +1 -1
  199. package/dist/esm/utils/sentinelTokens.js +19 -1
  200. package/dist/esm/utils/sentinelTokens.js.map +1 -1
  201. package/dist/esm/utils/userGroupsPeek.js +91 -0
  202. package/dist/esm/utils/userGroupsPeek.js.map +1 -0
  203. package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
  204. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  205. package/dist/esm/utils/visibleWhen.js +57 -5
  206. package/dist/esm/utils/visibleWhen.js.map +1 -1
  207. package/dist/esm/utils/widgetDispatch.js +1 -1
  208. package/dist/esm/utils/widgetDispatch.js.map +1 -1
  209. package/dist/esm/utils/widgetVisibility.js +4 -57
  210. package/dist/esm/utils/widgetVisibility.js.map +1 -1
  211. package/dist/nextcloud-vue.cjs.js +13589 -7833
  212. package/dist/nextcloud-vue.cjs.js.map +2 -2
  213. package/dist/nextcloud-vue.css +1041 -12
  214. package/l10n/en.json +9 -1
  215. package/l10n/nl.json +55 -1
  216. package/package.json +2 -1
  217. package/scripts/check-docs-mdx.mjs +108 -0
  218. package/src/components/CnActionsBar/CnActionsBar.vue +6 -1
  219. package/src/components/CnAppRoot/CnAppRoot.md +4 -2
  220. package/src/components/CnAppRoot/CnAppRoot.vue +151 -59
  221. package/src/components/CnBannerWidget/CnBannerWidget.vue +205 -20
  222. package/src/components/CnCellRenderer/CnCellRenderer.md +2 -0
  223. package/src/components/CnCellRenderer/CnCellRenderer.vue +47 -4
  224. package/src/components/CnChipsFieldWidget/CnChipsFieldWidget.vue +433 -0
  225. package/src/components/CnChipsFieldWidget/index.js +25 -0
  226. package/src/components/CnChoicePickerWidget/CnChoicePickerWidget.vue +556 -0
  227. package/src/components/CnChoicePickerWidget/index.js +26 -0
  228. package/src/components/CnConversationThread/CnConversationWidget.vue +1 -1
  229. package/src/components/CnDashboardGrid/CnDashboardGrid.vue +2 -2
  230. package/src/components/CnDashboardPage/CnDashboardPage.vue +78 -5
  231. package/src/components/CnDataTable/CnDataTable.vue +21 -12
  232. package/src/components/CnDetailPage/CnDetailPage.vue +89 -3
  233. package/src/components/CnDocumentReviewList/CnDocumentReviewWidget.vue +1 -1
  234. package/src/components/CnFacetSidebar/CnFacetSidebar.vue +7 -7
  235. package/src/components/CnFactsWidget/CnFactsWidget.vue +142 -2
  236. package/src/components/CnFieldListWidget/CnFieldListWidget.vue +268 -6
  237. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +588 -117
  238. package/src/components/CnFilesBrowser/filesBrowser.js +100 -0
  239. package/src/components/CnFilesBrowser/filesBrowserColumns.js +140 -0
  240. package/src/components/CnFormPage/CnFormPage.vue +81 -0
  241. package/src/components/CnIndexPage/CnIndexPage.vue +56 -4
  242. package/src/components/CnIndexSidebar/CnIndexSidebar.vue +7 -7
  243. package/src/components/CnNextStepCard/CnNextStepWidget.vue +1 -1
  244. package/src/components/CnObjectListWidget/CnObjectListWidget.vue +69 -2
  245. package/src/components/CnObjectSidebar/CnFilesTab.vue +70 -1
  246. package/src/components/CnObjectSidebar/CnNotesTab.vue +16 -0
  247. package/src/components/CnRadioCardsWidget/CnRadioCardsWidget.vue +313 -0
  248. package/src/components/CnRadioCardsWidget/index.js +27 -0
  249. package/src/components/CnRecordListWidget/CnRecordListWidget.vue +55 -6
  250. package/src/components/CnSavedViewsWidget/CnSavedViewsWidget.vue +296 -0
  251. package/src/components/CnSavedViewsWidget/index.js +29 -0
  252. package/src/components/CnSegmentedControl/CnSegmentedControl.vue +13 -0
  253. package/src/components/CnSettingsFormWidget/CnSettingsFormWidget.vue +555 -0
  254. package/src/components/CnSettingsFormWidget/index.js +26 -0
  255. package/src/components/CnStatWidget/CnStatWidget.vue +87 -6
  256. package/src/components/CnStatsBlockWidget/CnStatsBlockWidget.vue +180 -3
  257. package/src/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue +226 -0
  258. package/src/components/CnSwitchRowsWidget/index.js +25 -0
  259. package/src/components/CnTabsWidget/CnTabsWidget.vue +82 -3
  260. package/src/components/CnTasksWidget/CnTasksWidget.vue +175 -15
  261. package/src/components/CnToggleTableWidget/CnToggleTableWidget.vue +386 -0
  262. package/src/components/CnToggleTableWidget/index.js +26 -0
  263. package/src/components/CnWidgetGrid/registerDashboardWidgets.js +7 -0
  264. package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +7 -2
  265. package/src/components/CnWidgetObjectTable/dashboardRegistration.js +5 -0
  266. package/src/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue +309 -11
  267. package/src/components/index.js +15 -0
  268. package/src/composables/useListView.js +4 -4
  269. package/src/css/look-board-detail.css +36 -2
  270. package/src/css/look-board-index.css +39 -8
  271. package/src/css/look-board.css +72 -0
  272. package/src/index.js +15 -0
  273. package/src/schemas/app-manifest-v2.schema.json +93 -9
  274. package/src/utils/contentHeaderLink.js +48 -0
  275. package/src/utils/facets.js +74 -2
  276. package/src/utils/fetchFilterCounts.js +39 -4
  277. package/src/utils/fillTemplate.js +7 -1
  278. package/src/utils/initials.js +29 -0
  279. package/src/utils/libraryWidgetKeys.js +7 -0
  280. package/src/utils/objectWrite.js +55 -0
  281. package/src/utils/pageControls.js +78 -0
  282. package/src/utils/personalLenses.js +39 -2
  283. package/src/utils/resolveFilterTokens.js +3 -1
  284. package/src/utils/resolveManifestTokens.js +2 -0
  285. package/src/utils/sentinelTokens.js +19 -1
  286. package/src/utils/userGroupsPeek.js +87 -0
  287. package/src/utils/validateManifestV2.compiled.js +3 -3
  288. package/src/utils/visibleWhen.js +56 -4
  289. package/src/utils/widgetDispatch.js +1 -1
  290. package/src/utils/widgetVisibility.js +7 -56
package/l10n/en.json CHANGED
@@ -54,6 +54,7 @@
54
54
  "Could not update the view \"{name}\"": "Could not update the view \"{name}\"",
55
55
  "Unread": "Unread",
56
56
  "Mark as unread": "Mark as unread",
57
+ "Open sidebar": "Open sidebar",
57
58
  "Could not mark this record as unread.": "Could not mark this record as unread.",
58
59
  "Add to favourites": "Add to favourites",
59
60
  "Remove from favourites": "Remove from favourites",
@@ -1368,7 +1369,14 @@
1368
1369
  "{count} filters": "{count} filters",
1369
1370
  "{count} more filters": "{count} more filters",
1370
1371
  "{label} — show the other filters": "{label} — show the other filters",
1371
- "{view}: not available on this list": "{view}: not available on this list"
1372
+ "Select {name}": "Select {name}",
1373
+ "Select all files": "Select all files",
1374
+ "No files match the filter.": "No files match the filter.",
1375
+ "Actions for the selected files": "Actions for the selected files",
1376
+ "{view}: not available on this list": "{view}: not available on this list",
1377
+ "Leave this field empty": "Leave this field empty",
1378
+ "This answer was refused.": "This answer was refused.",
1379
+ "No value ({count})": "No value ({count})"
1372
1380
  },
1373
1381
  "plurals": {
1374
1382
  "{count} selected": [
package/l10n/nl.json CHANGED
@@ -54,6 +54,7 @@
54
54
  "Could not update the view \"{name}\"": "Weergave \"{name}\" kon niet worden bijgewerkt",
55
55
  "Unread": "Ongelezen",
56
56
  "Mark as unread": "Markeren als ongelezen",
57
+ "Open sidebar": "Zijbalk openen",
57
58
  "Could not mark this record as unread.": "Dit dossier kon niet als ongelezen worden gemarkeerd.",
58
59
  "Add to favourites": "Aan favorieten toevoegen",
59
60
  "Remove from favourites": "Uit favorieten verwijderen",
@@ -171,6 +172,52 @@
171
172
  "No integrations available yet.": "Nog geen integraties beschikbaar.",
172
173
  "This integration is not available.": "Deze integratie is niet beschikbaar.",
173
174
  "Integrations": "Integraties",
175
+ "User settings": "Persoonlijke instellingen",
176
+ "Personal settings": "Persoonlijke instellingen",
177
+ "Admin settings": "Beheerinstellingen",
178
+ "Main navigation": "Hoofdnavigatie",
179
+ "Credentials": "Inloggegevens",
180
+ "Notifications": "Meldingen",
181
+ "Walkthrough": "Rondleiding",
182
+ "Take the guided tour of this app again.": "Doe de rondleiding door deze app nog een keer.",
183
+ "Restart walkthrough": "Rondleiding opnieuw starten",
184
+ "These open in another app.": "Deze openen in een andere app.",
185
+ "Notification preferences unavailable": "Meldingsvoorkeuren niet beschikbaar",
186
+ "Notification preferences are provided by OpenRegister and could not be loaded here.": "Meldingsvoorkeuren komen uit OpenRegister en konden hier niet worden geladen.",
187
+ "No notifications": "Geen meldingen",
188
+ "This app has no notifications.": "Deze app stuurt geen meldingen.",
189
+ "Choose which notifications you want to receive. Items you leave unchanged use the app default.": "Kies welke meldingen je wilt krijgen. Wat je niet aanpast, volgt de standaard van de app.",
190
+ "Reset to default": "Standaard herstellen",
191
+ "Could not save notification preference": "De meldingsvoorkeur kon niet worden opgeslagen",
192
+ "Could not reset notification preference": "De meldingsvoorkeur kon niet worden hersteld",
193
+ "When an item is created": "Als een item is aangemaakt",
194
+ "When an item is updated": "Als een item is gewijzigd",
195
+ "When an item is assigned or changes status": "Als een item is toegewezen of van status verandert",
196
+ "Learn about Keepiq": "Meer over Keepiq",
197
+ "What {app} uses": "Wat {app} gebruikt",
198
+ "This app does not use any external credentials.": "Deze app gebruikt geen inloggegevens van andere diensten.",
199
+ "Credentials are provided by OpenRegister and could not be loaded here.": "Inloggegevens komen uit OpenRegister en konden hier niet worden geladen.",
200
+ "Organisation credentials": "Inloggegevens van de organisatie",
201
+ "Your credentials": "Jouw inloggegevens",
202
+ "No credentials stored yet.": "Je hebt nog geen inloggegevens opgeslagen.",
203
+ "Confirm delete": "Verwijderen bevestigen",
204
+ "{app} may use this credential": "{app} mag deze inloggegevens gebruiken",
205
+ "Also allowed for: {apps}": "Ook toegestaan voor: {apps}",
206
+ "Allowed apps": "Toegestane apps",
207
+ "No app may use this credential yet": "Nog geen app mag deze inloggegevens gebruiken",
208
+ "Add credential": "Inloggegevens toevoegen",
209
+ "Which credential do you want to add?": "Welke inloggegevens wil je toevoegen?",
210
+ "Open {provider}": "{provider} openen",
211
+ "A label to recognise this credential later.": "Een naam waaraan je deze inloggegevens later herkent.",
212
+ "Secret": "Geheim",
213
+ "Sent to OpenRegister and stored in Keepiq. It is never shown again.": "Gaat naar OpenRegister en wordt in Keepiq bewaard. Je ziet het hierna nooit meer.",
214
+ "Choose which apps may use it": "Kies welke apps ze mogen gebruiken",
215
+ "{app} will be allowed to use this credential. You can change that afterwards.": "{app} mag deze inloggegevens gebruiken. Dat kun je later aanpassen.",
216
+ "This app": "Deze app",
217
+ "Could not save the credential": "De inloggegevens konden niet worden opgeslagen",
218
+ "Could not delete the credential": "De inloggegevens konden niet worden verwijderd",
219
+ "Could not update the credential": "De inloggegevens konden niet worden bijgewerkt",
220
+ "Could not update allowed apps": "De toegestane apps konden niet worden bijgewerkt",
174
221
  "View": "Bekijken",
175
222
  "Views": "Weergaven",
176
223
  "current step": "huidige stap",
@@ -1466,7 +1513,14 @@
1466
1513
  "{count} days left": "Nog {count} dagen",
1467
1514
  "1 day overdue": "1 dag te laat",
1468
1515
  "{count} days overdue": "{count} dagen te laat",
1469
- "{view}: not available on this list": "{view}: niet beschikbaar in deze lijst"
1516
+ "Select {name}": "{name} selecteren",
1517
+ "Select all files": "Alle bestanden selecteren",
1518
+ "No files match the filter.": "Geen bestanden passen bij het filter.",
1519
+ "Actions for the selected files": "Acties voor de geselecteerde bestanden",
1520
+ "{view}: not available on this list": "{view}: niet beschikbaar in deze lijst",
1521
+ "Leave this field empty": "Laat dit veld leeg",
1522
+ "This answer was refused.": "Dit antwoord is geweigerd.",
1523
+ "No value ({count})": "Geen waarde ({count})"
1470
1524
  },
1471
1525
  "plurals": {
1472
1526
  "{count} selected": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@conduction/nextcloud-vue",
3
- "version": "2.80.0",
3
+ "version": "2.81.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": {
@@ -72,6 +72,7 @@
72
72
  "check:css-entry": "node scripts/check-css-entry.js",
73
73
  "check:dist-sideeffects": "node scripts/check-dist-sideeffects.mjs",
74
74
  "check:docs": "node scripts/check-docs.js",
75
+ "check:docs-mdx": "node scripts/check-docs-mdx.mjs",
75
76
  "check:docs-fresh": "cd docusaurus && npm run prebuild:docs && cd .. && node scripts/check-docs-fresh.js",
76
77
  "check:integration-parity": "node scripts/check-integration-parity.js",
77
78
  "check:journey-vocabulary": "node scripts/gates/journey-vocabulary.mjs .",
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Compiles every page under docs/ the way the Docusaurus site does (MDX 3,
3
+ * GFM, directives), and fails on the first class of error that breaks the
4
+ * site build: a `<word>` read as a JSX tag, a `{` read as an expression, an
5
+ * unclosed code span inside a table cell.
6
+ *
7
+ * Why this exists: `check:docs` and `check:docs-fresh` read the pages as
8
+ * text, so a page that MDX cannot compile passed both and only failed in the
9
+ * "Deploy Documentation" run after the push to beta (docs/components/
10
+ * cn-actions-bar.md and cn-workspace-filter-widget.md, 11 Oct 2026). The full
11
+ * `docusaurus build` takes minutes; this compiles the same pages in seconds.
12
+ *
13
+ * Needs the docusaurus/ dependencies (`cd docusaurus && npm ci`), which every
14
+ * code-quality check leg already installs.
15
+ *
16
+ * Usage: node scripts/check-docs-mdx.mjs
17
+ */
18
+
19
+ import fs from 'node:fs'
20
+ import { createRequire } from 'node:module'
21
+ import path from 'node:path'
22
+ import { fileURLToPath, pathToFileURL } from 'node:url'
23
+
24
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
25
+ const DOCS_DIR = path.join(ROOT, 'docs')
26
+ const SITE_REQUIRE = createRequire(path.join(ROOT, 'docusaurus', 'package.json'))
27
+
28
+ /**
29
+ * Imports an ESM package from docusaurus/node_modules.
30
+ *
31
+ * @param {string} name package name
32
+ * @return {Promise<object>} the module namespace
33
+ */
34
+ async function importFromSite(name) {
35
+ let resolved
36
+ try {
37
+ resolved = SITE_REQUIRE.resolve(name)
38
+ } catch {
39
+ console.error(`check:docs-mdx: cannot resolve ${name} from docusaurus/. Run \`cd docusaurus && npm ci\` first.`)
40
+ process.exit(2)
41
+ }
42
+ return import(pathToFileURL(resolved).href)
43
+ }
44
+
45
+ /**
46
+ * @param {string} dir directory to walk
47
+ * @return {string[]} every .md / .mdx file below it
48
+ */
49
+ function listPages(dir) {
50
+ const out = []
51
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
52
+ const full = path.join(dir, entry.name)
53
+ if (entry.isDirectory()) {
54
+ out.push(...listPages(full))
55
+ } else if (/\.mdx?$/.test(entry.name)) {
56
+ out.push(full)
57
+ }
58
+ }
59
+ return out
60
+ }
61
+
62
+ const { compile } = await importFromSite('@mdx-js/mdx')
63
+ const remarkFrontmatter = (await importFromSite('remark-frontmatter')).default
64
+ const remarkComment = (await importFromSite('@slorber/remark-comment')).default
65
+ const remarkGfm = (await importFromSite('remark-gfm')).default
66
+ const remarkDirective = (await importFromSite('remark-directive')).default
67
+ const { unwrapMdxCodeBlocks, escapeMarkdownHeadingIds } = SITE_REQUIRE('@docusaurus/utils')
68
+
69
+ // The syntax extensions the site's MDX processor parses with
70
+ // (@docusaurus/mdx-loader lib/processor.js, mdx1Compat defaults on): front
71
+ // matter, HTML comments, GFM tables, admonition directives. A parse error is
72
+ // what breaks the build, so these are the ones that matter here.
73
+ const REMARK_PLUGINS = [remarkFrontmatter, remarkComment, remarkGfm, remarkDirective]
74
+
75
+ /**
76
+ * Applies the string preprocessing @docusaurus/mdx-loader runs before MDX
77
+ * (lib/preprocessor.js): unwrap mdx-code-block fences and escape custom
78
+ * heading ids.
79
+ *
80
+ * @param {string} source page source
81
+ * @return {string} the source MDX actually sees
82
+ */
83
+ function preprocess(source) {
84
+ return escapeMarkdownHeadingIds(unwrapMdxCodeBlocks(source))
85
+ }
86
+
87
+ const pages = listPages(DOCS_DIR)
88
+ const failures = []
89
+ for (const page of pages) {
90
+ const source = preprocess(fs.readFileSync(page, 'utf8'))
91
+ try {
92
+ await compile(source, { remarkPlugins: REMARK_PLUGINS })
93
+ } catch (error) {
94
+ const where = error.line ? `:${error.line}:${error.column ?? 1}` : ''
95
+ failures.push(`${path.relative(ROOT, page)}${where} ${error.reason ?? error.message}`)
96
+ }
97
+ }
98
+
99
+ if (failures.length > 0) {
100
+ console.error(`check:docs-mdx: ${failures.length} of ${pages.length} page(s) do not compile as MDX:\n`)
101
+ for (const line of failures) {
102
+ console.error(` ${line}`)
103
+ }
104
+ console.error('\nEscape a literal `<word>` or `{` with a backslash, or put it in a code span.')
105
+ process.exit(1)
106
+ }
107
+
108
+ console.log(`check:docs-mdx: all ${pages.length} pages compile as MDX.`)
@@ -23,7 +23,7 @@
23
23
  wrap together to the start of the next line (the DqZaken board),
24
24
  never the switch alone.
25
25
  -->
26
- <div v-if="showSidebarToggle || (showViewToggle && viewSegments.length > 1)"
26
+ <div v-if="showSidebarToggle || $slots['filter-menu'] || (showViewToggle && viewSegments.length > 1)"
27
27
  class="cn-actions-bar__view-controls"
28
28
  data-testid="cn-actions-bar-view-controls">
29
29
  <NcButton v-if="showSidebarToggle"
@@ -42,6 +42,11 @@
42
42
  data-testid="cn-actions-bar-filter-badge"
43
43
  :aria-label="filterCountLabel">{{ filterCount }}</span>
44
44
  </NcButton>
45
+ <!--
46
+ @slot filter-menu
47
+ @description Board look, row 1: the Filter button drawn as a menu, for a page with no filter sidebar (`filterMenu`). Ignored while the sidebar toggle is shown.
48
+ -->
49
+ <slot v-else name="filter-menu" />
45
50
  <div v-if="showViewToggle && viewSegments.length > 1"
46
51
  class="cn-actions-bar__view-toggle"
47
52
  role="group"
@@ -84,7 +84,8 @@ With loading state, custom components, translate, permissions, and custom page t
84
84
  | `notificationLabels` | Object | No | `{}` | Labels for the app's notification rules in the user-settings notification pane, keyed `<schema>.<key>` or `<key>`; values are translated strings or `{ nl, en }` maps. A rule without a label reads as its key split into words ("Case assigned"), never as the raw key. Provided to descendants as `cnNotificationLabels` |
85
85
  | `permissions` | Array | No | `[]` | Permission strings for the current user; forwarded to `CnAppNav` for menu filtering |
86
86
  | `pageTypes` | Object | No | `null` | Page-type registry map (`type → component`). Falls back to the library's `defaultPageTypes`. Extend with `{ ...defaultPageTypes, report: MyReportPage }` |
87
- | `userSettingsTitle` | String | No | `''` | Title shown at the top of the hosted `NcAppSettingsDialog`. Empty (the default) resolves to `translate('User settings')`. Override per app to brand the modal (e.g. `'Decidiq preferences'`). |
87
+ | `userSettingsTitle` | String | No | `''` | Title shown at the top of the hosted `NcAppSettingsDialog`. Empty (the default) resolves to "User settings" from the library catalogue ("Persoonlijke instellingen" in Dutch), then the app's `translate`. Override per app to brand the modal (e.g. `'Decidiq preferences'`). |
88
+ | `userSettingsSections` | Array | No | `null` | The user-settings sections to draw, in order: `app` (the app's slot content), `notifications`, `credentials`, `walkthrough`, `integrations`. A key left out is not drawn. `null` keeps the defaults: every section after `#user-settings-sections` content, or only the `#user-settings` content and the integrations section. |
88
89
  | `adminSettingsTitle` | String | No | `''` | Title shown at the top of the admin-settings `NcAppSettingsDialog`. Empty (the default) resolves to `translate('Administration')`. Override per app (e.g. `'Pipelinq administration'`). |
89
90
  | `requiresApps` | Array | No | `['openregister']` | App ids that MUST be installed for the host app to function. Checked against the OCS capabilities API on mount. When any required app is missing, CnAppRoot renders the empty state from the `or-missing` slot (default: `<NcEmptyContent>`) instead of the renderer. Pass `[]` to opt out (e.g. launchpad, the docs/styleguide app) — useful for any consumer that does not depend on OpenRegister. |
90
91
  | `look` | String | No | `''` | The look the app is drawn in: `board` or `nextcloud`. Empty falls back to the manifest's top-level `look`, then `nextcloud`. Provided to descendants as `cnLook`. |
@@ -104,7 +105,8 @@ With loading state, custom components, translate, permissions, and custom page t
104
105
  | `header-actions` | — | Extra buttons rendered in the app header |
105
106
  | `sidebar` | — | Sidebar area rendered alongside `NcAppContent` |
106
107
  | `footer` | — | Footer area rendered inside `NcAppContent` |
107
- | `user-settings` | — | `NcAppSettingsSection` children rendered inside the host `NcAppSettingsDialog`. CnAppRoot always mounts the dialog and exposes `cnOpenUserSettings` via inject; CnAppNav binds it to manifest items declaring `action: "user-settings"`. Defaults to a single placeholder section. |
108
+ | `user-settings-sections` | — | The app's own `NcAppSettingsSection` children, ADDED to the default sections (notifications, credentials, walkthrough replay, integrations). Place, order or hide the defaults with `userSettingsSections`. |
109
+ | `user-settings` | — | The older slot: the app's own `NcAppSettingsSection` children, REPLACING the default sections (only this content and the integrations section are drawn, unless `userSettingsSections` names more). CnAppRoot always mounts the dialog and exposes `cnOpenUserSettings` via inject; CnAppNav binds it to manifest items declaring `action: "user-settings"`. |
108
110
 
109
111
  ## AI Chat Companion — Context push
110
112
 
@@ -31,10 +31,12 @@
31
31
 
32
32
  Hosts a per-user `NcAppSettingsDialog` that any descendant can open
33
33
  via the injected `cnOpenUserSettings()` method. CnAppNav binds the
34
- inject to manifest entries with `action: "user-settings"`. Apps
35
- populate the modal by passing `NcAppSettingsSection`s into the
36
- `#user-settings` slot; the slot falls back to a single placeholder
37
- section when no content is supplied.
34
+ inject to manifest entries with `action: "user-settings"`. The modal
35
+ draws the library's default sections (notifications, credentials,
36
+ walkthrough replay, integrations). Apps add their own
37
+ `NcAppSettingsSection`s through `#user-settings-sections` and place,
38
+ order or hide the defaults with the `userSettingsSections` prop. The
39
+ older `#user-settings` slot replaces the defaults instead.
38
40
 
39
41
  A SECOND, distinct `NcAppSettingsDialog` hosts admin-only, app-level
40
42
  (not per-user) settings, driven GENERICALLY by `manifest.adminSettings[]`
@@ -534,18 +536,33 @@
534
536
  User-settings modal. Always mounted so descendants can
535
537
  open it via the `cnOpenUserSettings` inject (CnAppNav
536
538
  wires this to manifest entries with
537
- `action: "user-settings"`). The `#user-settings` slot
538
- hosts NcAppSettingsSection children; the placeholder
539
- fallback below renders when no slot content is supplied.
539
+ `action: "user-settings"`).
540
+
541
+ The dialog draws its sections in the order
542
+ `userSettingsSectionOrder()` returns: `app` (the app's own
543
+ sections from the slots), `notifications`, `credentials`,
544
+ `walkthrough` and `integrations`. A key that is left out is
545
+ not drawn. Two slots carry the app's sections:
546
+ - `#user-settings-sections` adds them: the library's default
547
+ sections stay unless `userSettingsSections` leaves them out.
548
+ - `#user-settings` (the original slot) replaces the defaults,
549
+ as it always did: only the app's content and the
550
+ integrations section are drawn, unless `userSettingsSections`
551
+ names more.
540
552
  -->
541
553
  <NcAppSettingsDialog
542
554
  :open="userSettingsOpen"
543
555
  :showNavigation="true"
544
556
  :name="resolvedUserSettingsTitle"
545
557
  @update:open="userSettingsOpen = $event">
546
- <!-- @slot user-settings Sections rendered inside the host NcAppSettingsDialog. Pass NcAppSettingsSection children. Defaults to the notification-preferences pane when omitted. -->
547
- <slot name="user-settings">
548
- <CnNotificationPreferences v-if="userSettingsOpen" />
558
+ <template v-for="section in userSettingsSectionOrder()" :key="section">
559
+ <template v-if="section === 'app'">
560
+ <!-- @slot user-settings The app's own sections (NcAppSettingsSection children), REPLACING the library's default sections: filling it draws only this content and the integrations section, unless the `userSettingsSections` prop names more. Prefer `#user-settings-sections`. -->
561
+ <slot name="user-settings" />
562
+ <!-- @slot user-settings-sections The app's own sections (NcAppSettingsSection children), ADDED to the library's default sections (notifications, credentials, walkthrough, integrations). Place, order or hide the defaults with the `userSettingsSections` prop. -->
563
+ <slot name="user-settings-sections" />
564
+ </template>
565
+ <CnNotificationPreferences v-else-if="section === 'notifications' && userSettingsOpen" />
549
566
  <!--
550
567
  Credential broker (OpenRegister). Lets the user manage the
551
568
  secrets OR holds on their behalf; apps call external providers
@@ -553,9 +570,9 @@
553
570
  `credentials[]` declarations drive the informational "Apps
554
571
  requesting credentials" list.
555
572
  -->
556
- <NcAppSettingsSection v-if="userSettingsOpen"
573
+ <NcAppSettingsSection v-else-if="section === 'credentials' && userSettingsOpen"
557
574
  id="credentials"
558
- :name="translate('Credentials')">
575
+ :name="libraryCopy('Credentials')">
559
576
  <CnCredentials
560
577
  scope="personal"
561
578
  :appId="appId"
@@ -567,7 +584,7 @@
567
584
  when the manifest declares an enabled tour, so apps
568
585
  without a walkthrough never show an empty section.
569
586
  -->
570
- <NcAppSettingsSection v-if="walkthroughEnabled"
587
+ <NcAppSettingsSection v-else-if="section === 'walkthrough' && walkthroughEnabled"
571
588
  id="cn-walkthrough"
572
589
  :name="restartWalkthroughSectionName">
573
590
  <p class="cn-app-root__walkthrough-hint">
@@ -595,48 +612,47 @@
595
612
  </NcButton>
596
613
  </div>
597
614
  </NcAppSettingsSection>
598
- </slot>
599
- <!--
600
- Integrations (ADR-110) — the links that leave this app for
601
- another one, gathered out of the navigation into one place.
602
-
603
- DELIBERATELY OUTSIDE the #user-settings slot. Six fleet apps
604
- override that slot, and anything placed inside the default
605
- fallback disappears the moment an app supplies its own
606
- content — silently, because an unmatched slot renders nothing
607
- and reports nothing. A contract every app is supposed to
608
- honour cannot live somewhere six apps switch off by accident.
609
-
610
- Renders nothing when the app declares no integration
611
- entries, so apps without any never show an empty section.
612
- -->
613
- <NcAppSettingsSection v-if="userSettingsOpen && integrationEntries.length"
614
- id="cn-integrations"
615
- :name="translate('Integrations')">
616
- <p class="cn-app-root__integrations-hint">
617
- {{ translate('These open in another app.') }}
618
- </p>
619
- <ul class="cn-app-root__integrations">
620
- <li v-for="entry in integrationEntries" :key="entry.id">
621
- <!--
622
- One uniform external-link icon rather than the
623
- entry's own: every row here goes to a different
624
- app, which is the thing worth signalling, and
625
- CnIcon falls back to a question mark for any
626
- name an app has not registered — a broken-looking
627
- glyph on a link that works fine.
628
- -->
629
- <NcButton variant="tertiary"
630
- :href="entry.href"
631
- :aria-label="entry.label">
632
- <template #icon>
633
- <OpenInNew :size="20" />
634
- </template>
635
- {{ entry.label }}
636
- </NcButton>
637
- </li>
638
- </ul>
639
- </NcAppSettingsSection>
615
+ <!--
616
+ Integrations (ADR-110): the links that leave this app for
617
+ another one, gathered out of the navigation into one place.
618
+
619
+ Drawn whether or not the app fills `#user-settings`: six
620
+ fleet apps fill that slot, and a section placed inside its
621
+ fallback disappeared for all of them in silence. Only an
622
+ explicit `userSettingsSections` without `integrations`
623
+ hides it, for an app whose board does not draw it.
624
+
625
+ Renders nothing when the app declares no integration
626
+ entries, so apps without any never show an empty section.
627
+ -->
628
+ <NcAppSettingsSection v-else-if="section === 'integrations' && userSettingsOpen && integrationEntries.length"
629
+ id="cn-integrations"
630
+ :name="translate('Integrations')">
631
+ <p class="cn-app-root__integrations-hint">
632
+ {{ libraryCopy('These open in another app.') }}
633
+ </p>
634
+ <ul class="cn-app-root__integrations">
635
+ <li v-for="entry in integrationEntries" :key="entry.id">
636
+ <!--
637
+ One uniform external-link icon rather than the
638
+ entry's own: every row here goes to a different
639
+ app, which is the thing worth signalling, and
640
+ CnIcon falls back to a question mark for any
641
+ name an app has not registered, a broken-looking
642
+ glyph on a link that works fine.
643
+ -->
644
+ <NcButton variant="tertiary"
645
+ :href="entry.href"
646
+ :aria-label="entry.label">
647
+ <template #icon>
648
+ <OpenInNew :size="20" />
649
+ </template>
650
+ {{ entry.label }}
651
+ </NcButton>
652
+ </li>
653
+ </ul>
654
+ </NcAppSettingsSection>
655
+ </template>
640
656
  </NcAppSettingsDialog>
641
657
 
642
658
  <!--
@@ -781,6 +797,12 @@ const KNOWN_REGISTRY_KINDS = Object.keys(REGISTRY_KIND_REQUIRED_FIELDS)
781
797
  */
782
798
  const OR_STORE_LINK = '/index.php/settings/apps/integration/openregister'
783
799
 
800
+ /**
801
+ * The sections of the user-settings modal, in their default order. `app`
802
+ * stands for the app's own slot content.
803
+ */
804
+ const USER_SETTINGS_SECTIONS = ['app', 'notifications', 'credentials', 'walkthrough', 'integrations']
805
+
784
806
  export default {
785
807
  name: 'CnAppRoot',
786
808
 
@@ -1625,6 +1647,28 @@ export default {
1625
1647
  default: '',
1626
1648
  },
1627
1649
 
1650
+ /**
1651
+ * The sections of the user-settings modal, in the order they are
1652
+ * drawn. Keys: `app` (the content of the `#user-settings-sections`
1653
+ * and `#user-settings` slots), `notifications`, `credentials`,
1654
+ * `walkthrough` and `integrations`. A key that is left out is not
1655
+ * drawn, so `['app', 'credentials', 'walkthrough']` hides the
1656
+ * notifications pane and the integrations section. Unknown keys are
1657
+ * ignored. The app's slot content is always drawn: without `app` in
1658
+ * the list it comes first.
1659
+ *
1660
+ * `null` (the default) keeps what each slot always did: all
1661
+ * defaults after the `#user-settings-sections` content, or only the
1662
+ * `#user-settings` content and the integrations section when an app
1663
+ * fills that older slot.
1664
+ *
1665
+ * @type {Array<string>|null}
1666
+ */
1667
+ userSettingsSections: {
1668
+ type: Array,
1669
+ default: null,
1670
+ },
1671
+
1628
1672
  /**
1629
1673
  * Title rendered at the top of the admin-settings modal
1630
1674
  * (NcAppSettingsDialog `name` prop). Defaults to the
@@ -3017,7 +3061,7 @@ export default {
3017
3061
  },
3018
3062
 
3019
3063
  resolvedUserSettingsTitle() {
3020
- return this.userSettingsTitle || this.translate('User settings')
3064
+ return this.userSettingsTitle || this.libraryCopy('User settings')
3021
3065
  },
3022
3066
 
3023
3067
  /**
@@ -3036,7 +3080,7 @@ export default {
3036
3080
  * @return {string}
3037
3081
  */
3038
3082
  restartWalkthroughSectionName() {
3039
- return this.translate('Walkthrough')
3083
+ return this.libraryCopy('Walkthrough')
3040
3084
  },
3041
3085
 
3042
3086
  /**
@@ -3045,7 +3089,7 @@ export default {
3045
3089
  * @return {string}
3046
3090
  */
3047
3091
  restartWalkthroughHint() {
3048
- return this.translate('Take the guided tour of this app again.')
3092
+ return this.libraryCopy('Take the guided tour of this app again.')
3049
3093
  },
3050
3094
 
3051
3095
  /**
@@ -3054,7 +3098,7 @@ export default {
3054
3098
  * @return {string}
3055
3099
  */
3056
3100
  restartWalkthroughLabel() {
3057
- return this.translate('Restart walkthrough')
3101
+ return this.libraryCopy('Restart walkthrough')
3058
3102
  },
3059
3103
 
3060
3104
  /**
@@ -3389,6 +3433,54 @@ export default {
3389
3433
  return legacy || null
3390
3434
  },
3391
3435
 
3436
+ /**
3437
+ * The keys of the user-settings sections to draw, in order. A method
3438
+ * rather than a computed, because which slots an app fills is read
3439
+ * from `$slots`, which a computed does not track.
3440
+ *
3441
+ * @return {Array<string>} Keys out of `USER_SETTINGS_SECTIONS`.
3442
+ * @spec openspec/changes/user-settings-sections/specs/user-settings-sections/spec.md#requirement-the-user-settings-dialog-keeps-its-default-sections-next-to-the-apps-own
3443
+ */
3444
+ userSettingsSectionOrder() {
3445
+ const replaces = Boolean(this.$slots['user-settings'])
3446
+ const adds = Boolean(this.$slots['user-settings-sections'])
3447
+ let order
3448
+ if (Array.isArray(this.userSettingsSections)) {
3449
+ order = [...new Set(this.userSettingsSections.filter((key) => USER_SETTINGS_SECTIONS.includes(key)))]
3450
+ } else if (replaces) {
3451
+ order = ['app', 'integrations']
3452
+ } else {
3453
+ order = [...USER_SETTINGS_SECTIONS]
3454
+ }
3455
+ if ((replaces || adds) && !order.includes('app')) {
3456
+ order.unshift('app')
3457
+ }
3458
+ return order
3459
+ },
3460
+
3461
+ /**
3462
+ * Copy for a part of the user settings dialog the library draws itself
3463
+ * (its title, the credentials and walkthrough sections). The library's
3464
+ * own catalogue answers first, because the app's catalogue may give
3465
+ * the same English word another meaning: learniq translates
3466
+ * "Credentials" as "Diploma's" for its own records, and the dialog
3467
+ * showed that above the vault pane. Only when the library has no
3468
+ * translation in the user's language does the host `translate` get a
3469
+ * turn, then the text as written.
3470
+ *
3471
+ * @param {string} key The English source string.
3472
+ * @return {string} The translated copy.
3473
+ * @spec openspec/changes/dutch-user-settings-dialog/specs/dutch-user-settings-dialog/spec.md#requirement-the-user-settings-dialog-reads-dutch-for-a-dutch-reader
3474
+ */
3475
+ libraryCopy(key) {
3476
+ const own = t('nextcloud-vue', key)
3477
+ if (typeof own === 'string' && own !== '' && own !== key) {
3478
+ return own
3479
+ }
3480
+ const host = this.translate(key)
3481
+ return typeof host === 'string' && host !== '' ? host : key
3482
+ },
3483
+
3392
3484
  /**
3393
3485
  * Return the translated copy for `key`, or `fallback` when the
3394
3486
  * `translate` prop leaves the key unchanged (its default is the