@conduction/nextcloud-vue 2.79.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 (390) hide show
  1. package/dist/esm/components/CnActionButtons/CnActionButtons.vue2.js.map +1 -1
  2. package/dist/esm/components/CnActionButtons/CnActionButtons.vue3.js +3 -1
  3. package/dist/esm/components/CnActionButtons/CnActionButtons.vue3.js.map +1 -1
  4. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js +32 -3
  5. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
  6. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +13 -11
  7. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
  8. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +80 -4
  9. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  10. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +95 -91
  11. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  12. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js +179 -13
  13. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js.map +1 -1
  14. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js +52 -16
  15. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js.map +1 -1
  16. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +96 -4
  17. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
  18. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +158 -110
  19. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
  20. package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue.js +10 -0
  21. package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue.js.map +1 -0
  22. package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue2.js +252 -0
  23. package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue2.js.map +1 -0
  24. package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue3.js +140 -0
  25. package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue3.js.map +1 -0
  26. package/dist/esm/components/CnChipsFieldWidget/index.js +26 -0
  27. package/dist/esm/components/CnChipsFieldWidget/index.js.map +1 -0
  28. package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue.js +10 -0
  29. package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue.js.map +1 -0
  30. package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue2.js +354 -0
  31. package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue2.js.map +1 -0
  32. package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue3.js +172 -0
  33. package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue3.js.map +1 -0
  34. package/dist/esm/components/CnChoicePickerWidget/index.js +27 -0
  35. package/dist/esm/components/CnChoicePickerWidget/index.js.map +1 -0
  36. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js +1 -1
  37. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js.map +1 -1
  38. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue3.js.map +1 -1
  39. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js +2 -2
  40. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js.map +1 -1
  41. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue3.js.map +1 -1
  42. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +65 -5
  43. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  44. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +2 -1
  45. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  46. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +19 -11
  47. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  48. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +4 -2
  49. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  50. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +134 -4
  51. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  52. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +21 -4
  53. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  54. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +24 -0
  55. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
  56. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +43 -4
  57. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
  58. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue2.js +1 -1
  59. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue2.js.map +1 -1
  60. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue3.js.map +1 -1
  61. package/dist/esm/components/CnFacetSidebar/CnFacetSidebar.vue2.js +7 -7
  62. package/dist/esm/components/CnFacetSidebar/CnFacetSidebar.vue2.js.map +1 -1
  63. package/dist/esm/components/CnFacetSidebar/CnFacetSidebar.vue3.js.map +1 -1
  64. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +11 -0
  65. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js.map +1 -0
  66. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +867 -0
  67. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -0
  68. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js +238 -0
  69. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -0
  70. package/dist/esm/components/CnFactsWidget/index.js +30 -0
  71. package/dist/esm/components/CnFactsWidget/index.js.map +1 -0
  72. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue.js +10 -0
  73. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue.js.map +1 -0
  74. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js +626 -0
  75. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js.map +1 -0
  76. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js +396 -0
  77. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js.map +1 -0
  78. package/dist/esm/components/CnFieldListWidget/index.js +29 -0
  79. package/dist/esm/components/CnFieldListWidget/index.js.map +1 -0
  80. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +302 -4
  81. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  82. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +417 -244
  83. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  84. package/dist/esm/components/CnFilesBrowser/filesBrowser.js +101 -1
  85. package/dist/esm/components/CnFilesBrowser/filesBrowser.js.map +1 -1
  86. package/dist/esm/components/CnFilesBrowser/filesBrowserColumns.js +141 -1
  87. package/dist/esm/components/CnFilesBrowser/filesBrowserColumns.js.map +1 -1
  88. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +65 -17
  89. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
  90. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js +4 -4
  91. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
  92. package/dist/esm/components/CnFormPage/CnFormPage.vue2.js +78 -3
  93. package/dist/esm/components/CnFormPage/CnFormPage.vue2.js.map +1 -1
  94. package/dist/esm/components/CnFormPage/CnFormPage.vue3.js +74 -49
  95. package/dist/esm/components/CnFormPage/CnFormPage.vue3.js.map +1 -1
  96. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +333 -73
  97. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  98. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +79 -17
  99. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  100. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue.js +10 -0
  101. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue.js.map +1 -0
  102. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js +99 -0
  103. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js.map +1 -0
  104. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js +48 -0
  105. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js.map +1 -0
  106. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js +7 -7
  107. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js.map +1 -1
  108. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js.map +1 -1
  109. package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue2.js +1 -1
  110. package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue2.js.map +1 -1
  111. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +65 -2
  112. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
  113. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +6 -2
  114. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
  115. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +63 -1
  116. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
  117. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +8 -1
  118. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
  119. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js +14 -0
  120. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js.map +1 -1
  121. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue3.js +5 -1
  122. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue3.js.map +1 -1
  123. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue2.js +29 -1
  124. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue2.js.map +1 -1
  125. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js.map +1 -1
  126. package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue.js +10 -0
  127. package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue.js.map +1 -0
  128. package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue2.js +174 -0
  129. package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue2.js.map +1 -0
  130. package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue3.js +75 -0
  131. package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue3.js.map +1 -0
  132. package/dist/esm/components/CnRadioCardsWidget/index.js +28 -0
  133. package/dist/esm/components/CnRadioCardsWidget/index.js.map +1 -0
  134. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +10 -0
  135. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js.map +1 -0
  136. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +411 -0
  137. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -0
  138. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +178 -0
  139. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -0
  140. package/dist/esm/components/CnRecordListWidget/index.js +31 -0
  141. package/dist/esm/components/CnRecordListWidget/index.js.map +1 -0
  142. package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue.js +10 -0
  143. package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue.js.map +1 -0
  144. package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue2.js +222 -0
  145. package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue2.js.map +1 -0
  146. package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue3.js +64 -0
  147. package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue3.js.map +1 -0
  148. package/dist/esm/components/CnSavedViewsWidget/index.js +30 -0
  149. package/dist/esm/components/CnSavedViewsWidget/index.js.map +1 -0
  150. package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue.js +10 -0
  151. package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue.js.map +1 -0
  152. package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue2.js +351 -0
  153. package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue2.js.map +1 -0
  154. package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue3.js +71 -0
  155. package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue3.js.map +1 -0
  156. package/dist/esm/components/CnSectionsWidget/index.js +35 -0
  157. package/dist/esm/components/CnSectionsWidget/index.js.map +1 -0
  158. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue2.js.map +1 -1
  159. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue3.js.map +1 -1
  160. package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue.js +10 -0
  161. package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue.js.map +1 -0
  162. package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue2.js +347 -0
  163. package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue2.js.map +1 -0
  164. package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue3.js +184 -0
  165. package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue3.js.map +1 -0
  166. package/dist/esm/components/CnSettingsFormWidget/index.js +27 -0
  167. package/dist/esm/components/CnSettingsFormWidget/index.js.map +1 -0
  168. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js +81 -2
  169. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js.map +1 -1
  170. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js.map +1 -1
  171. package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js +87 -6
  172. package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
  173. package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js +1 -0
  174. package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
  175. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js +84 -1
  176. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js.map +1 -1
  177. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js +85 -41
  178. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js.map +1 -1
  179. package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue.js +10 -0
  180. package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue.js.map +1 -0
  181. package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue2.js +159 -0
  182. package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue2.js.map +1 -0
  183. package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue3.js +60 -0
  184. package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue3.js.map +1 -0
  185. package/dist/esm/components/CnSwitchRowsWidget/index.js +26 -0
  186. package/dist/esm/components/CnSwitchRowsWidget/index.js.map +1 -0
  187. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +168 -7
  188. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
  189. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +86 -23
  190. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
  191. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js +80 -1
  192. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js.map +1 -1
  193. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js +75 -32
  194. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js.map +1 -1
  195. package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue.js +10 -0
  196. package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue.js.map +1 -0
  197. package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue2.js +269 -0
  198. package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue2.js.map +1 -0
  199. package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue3.js +106 -0
  200. package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue3.js.map +1 -0
  201. package/dist/esm/components/CnToggleTableWidget/index.js +27 -0
  202. package/dist/esm/components/CnToggleTableWidget/index.js.map +1 -0
  203. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +11 -0
  204. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
  205. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +7 -2
  206. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
  207. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
  208. package/dist/esm/components/CnWidgetObjectTable/dashboardRegistration.js +5 -0
  209. package/dist/esm/components/CnWidgetObjectTable/dashboardRegistration.js.map +1 -1
  210. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js +301 -6
  211. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js.map +1 -1
  212. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js +153 -55
  213. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js.map +1 -1
  214. package/dist/esm/composables/useFormDraft.js +140 -9
  215. package/dist/esm/composables/useFormDraft.js.map +1 -1
  216. package/dist/esm/composables/useListView.js +4 -4
  217. package/dist/esm/composables/useListView.js.map +1 -1
  218. package/dist/esm/index.js +21 -1
  219. package/dist/esm/index.js.map +1 -1
  220. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue2.js +28 -20
  221. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue2.js.map +1 -1
  222. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue3.js +9 -0
  223. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue3.js.map +1 -1
  224. package/dist/esm/integrations/builtin/field-inspection.js +21 -0
  225. package/dist/esm/integrations/builtin/field-inspection.js.map +1 -1
  226. package/dist/esm/integrations/offline/checklistSubmission.js +141 -0
  227. package/dist/esm/integrations/offline/checklistSubmission.js.map +1 -0
  228. package/dist/esm/integrations/offline/fieldCollectionHelpers.js +70 -5
  229. package/dist/esm/integrations/offline/fieldCollectionHelpers.js.map +1 -1
  230. package/dist/esm/integrations/offline/index.js +2 -1
  231. package/dist/esm/integrations/offline/index.js.map +1 -1
  232. package/dist/esm/integrations/offline/offlineDb.js +6 -1
  233. package/dist/esm/integrations/offline/offlineDb.js.map +1 -1
  234. package/dist/esm/integrations/offline/syncReplayService.js +8 -2
  235. package/dist/esm/integrations/offline/syncReplayService.js.map +1 -1
  236. package/dist/esm/l10n/en.json.js +20 -1
  237. package/dist/esm/l10n/en.json.js.map +1 -1
  238. package/dist/esm/l10n/nl.json.js +70 -2
  239. package/dist/esm/l10n/nl.json.js.map +1 -1
  240. package/dist/esm/nextcloud-vue.css +2192 -222
  241. package/dist/esm/utils/contentHeaderLink.js +51 -0
  242. package/dist/esm/utils/contentHeaderLink.js.map +1 -0
  243. package/dist/esm/utils/detailActionModel.js +60 -3
  244. package/dist/esm/utils/detailActionModel.js.map +1 -1
  245. package/dist/esm/utils/facets.js +75 -3
  246. package/dist/esm/utils/facets.js.map +1 -1
  247. package/dist/esm/utils/fetchFilterCounts.js +40 -5
  248. package/dist/esm/utils/fetchFilterCounts.js.map +1 -1
  249. package/dist/esm/utils/fillTemplate.js +120 -0
  250. package/dist/esm/utils/fillTemplate.js.map +1 -0
  251. package/dist/esm/utils/initials.js +32 -0
  252. package/dist/esm/utils/initials.js.map +1 -0
  253. package/dist/esm/utils/libraryWidgetKeys.js +11 -0
  254. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  255. package/dist/esm/utils/objectWrite.js +59 -0
  256. package/dist/esm/utils/objectWrite.js.map +1 -0
  257. package/dist/esm/utils/pageControls.js +81 -0
  258. package/dist/esm/utils/pageControls.js.map +1 -0
  259. package/dist/esm/utils/personalLenses.js +40 -3
  260. package/dist/esm/utils/personalLenses.js.map +1 -1
  261. package/dist/esm/utils/queryPresets.js +96 -0
  262. package/dist/esm/utils/queryPresets.js.map +1 -0
  263. package/dist/esm/utils/resolveFilterTokens.js +12 -1
  264. package/dist/esm/utils/resolveFilterTokens.js.map +1 -1
  265. package/dist/esm/utils/resolveManifestTokens.js +2 -0
  266. package/dist/esm/utils/resolveManifestTokens.js.map +1 -1
  267. package/dist/esm/utils/schema.js +21 -0
  268. package/dist/esm/utils/schema.js.map +1 -1
  269. package/dist/esm/utils/sentinelTokens.js +22 -4
  270. package/dist/esm/utils/sentinelTokens.js.map +1 -1
  271. package/dist/esm/utils/userGroupsPeek.js +91 -0
  272. package/dist/esm/utils/userGroupsPeek.js.map +1 -0
  273. package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
  274. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  275. package/dist/esm/utils/visibleWhen.js +57 -5
  276. package/dist/esm/utils/visibleWhen.js.map +1 -1
  277. package/dist/esm/utils/widgetDispatch.js +7 -1
  278. package/dist/esm/utils/widgetDispatch.js.map +1 -1
  279. package/dist/esm/utils/widgetVisibility.js +4 -57
  280. package/dist/esm/utils/widgetVisibility.js.map +1 -1
  281. package/dist/nextcloud-vue.cjs.js +19708 -9301
  282. package/dist/nextcloud-vue.cjs.js.map +2 -2
  283. package/dist/nextcloud-vue.css +2192 -222
  284. package/l10n/en.json +20 -1
  285. package/l10n/nl.json +70 -2
  286. package/package.json +2 -1
  287. package/scripts/check-docs-mdx.mjs +108 -0
  288. package/src/components/CnActionButtons/CnActionButtons.vue +2 -0
  289. package/src/components/CnActionsBar/CnActionsBar.md +4 -0
  290. package/src/components/CnActionsBar/CnActionsBar.vue +48 -11
  291. package/src/components/CnAppRoot/CnAppRoot.md +4 -2
  292. package/src/components/CnAppRoot/CnAppRoot.vue +151 -59
  293. package/src/components/CnBannerWidget/CnBannerWidget.vue +244 -23
  294. package/src/components/CnCellRenderer/CnCellRenderer.md +2 -0
  295. package/src/components/CnCellRenderer/CnCellRenderer.vue +235 -5
  296. package/src/components/CnChipsFieldWidget/CnChipsFieldWidget.vue +433 -0
  297. package/src/components/CnChipsFieldWidget/index.js +25 -0
  298. package/src/components/CnChoicePickerWidget/CnChoicePickerWidget.vue +556 -0
  299. package/src/components/CnChoicePickerWidget/index.js +26 -0
  300. package/src/components/CnConversationThread/CnConversationWidget.vue +1 -1
  301. package/src/components/CnDashboardGrid/CnDashboardGrid.vue +2 -2
  302. package/src/components/CnDashboardPage/CnDashboardPage.vue +78 -5
  303. package/src/components/CnDataTable/CnDataTable.vue +22 -12
  304. package/src/components/CnDetailPage/CnDetailPage.md +1 -1
  305. package/src/components/CnDetailPage/CnDetailPage.vue +152 -6
  306. package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +66 -0
  307. package/src/components/CnDocumentReviewList/CnDocumentReviewWidget.vue +1 -1
  308. package/src/components/CnFacetSidebar/CnFacetSidebar.vue +7 -7
  309. package/src/components/CnFactsWidget/CnFactsWidget.vue +1190 -0
  310. package/src/components/CnFactsWidget/index.js +29 -0
  311. package/src/components/CnFieldListWidget/CnFieldListWidget.vue +1008 -0
  312. package/src/components/CnFieldListWidget/index.js +28 -0
  313. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +588 -117
  314. package/src/components/CnFilesBrowser/filesBrowser.js +100 -0
  315. package/src/components/CnFilesBrowser/filesBrowserColumns.js +140 -0
  316. package/src/components/CnFormDialog/CnFormDialog.md +9 -3
  317. package/src/components/CnFormDialog/CnFormDialog.vue +69 -21
  318. package/src/components/CnFormPage/CnFormPage.vue +99 -3
  319. package/src/components/CnIndexPage/CnIndexPage.vue +309 -9
  320. package/src/components/CnIndexPage/CnIndexPageWidgets.vue +169 -0
  321. package/src/components/CnIndexSidebar/CnIndexSidebar.vue +7 -7
  322. package/src/components/CnNextStepCard/CnNextStepWidget.vue +1 -1
  323. package/src/components/CnObjectListWidget/CnObjectListWidget.vue +69 -2
  324. package/src/components/CnObjectSidebar/CnFilesTab.vue +70 -1
  325. package/src/components/CnObjectSidebar/CnNotesTab.vue +16 -0
  326. package/src/components/CnPageRenderer/CnPageRenderer.vue +29 -1
  327. package/src/components/CnRadioCardsWidget/CnRadioCardsWidget.vue +313 -0
  328. package/src/components/CnRadioCardsWidget/index.js +27 -0
  329. package/src/components/CnRecordListWidget/CnRecordListWidget.vue +652 -0
  330. package/src/components/CnRecordListWidget/index.js +30 -0
  331. package/src/components/CnSavedViewsWidget/CnSavedViewsWidget.vue +296 -0
  332. package/src/components/CnSavedViewsWidget/index.js +29 -0
  333. package/src/components/CnSectionsWidget/CnSectionsWidget.vue +489 -0
  334. package/src/components/CnSectionsWidget/index.js +34 -0
  335. package/src/components/CnSegmentedControl/CnSegmentedControl.vue +13 -0
  336. package/src/components/CnSettingsFormWidget/CnSettingsFormWidget.vue +555 -0
  337. package/src/components/CnSettingsFormWidget/index.js +26 -0
  338. package/src/components/CnStagesWidget/CnStagesWidget.vue +81 -2
  339. package/src/components/CnStatWidget/CnStatWidget.vue +88 -7
  340. package/src/components/CnStatsBlockWidget/CnStatsBlockWidget.vue +180 -3
  341. package/src/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue +226 -0
  342. package/src/components/CnSwitchRowsWidget/index.js +25 -0
  343. package/src/components/CnTabsWidget/CnTabsWidget.vue +263 -8
  344. package/src/components/CnTasksWidget/CnTasksWidget.vue +175 -15
  345. package/src/components/CnToggleTableWidget/CnToggleTableWidget.vue +386 -0
  346. package/src/components/CnToggleTableWidget/index.js +26 -0
  347. package/src/components/CnWidgetGrid/registerDashboardWidgets.js +11 -0
  348. package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +7 -2
  349. package/src/components/CnWidgetObjectTable/dashboardRegistration.js +5 -0
  350. package/src/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue +493 -11
  351. package/src/components/index.js +19 -0
  352. package/src/composables/__tests__/useFormDraft.spec.js +69 -0
  353. package/src/composables/useFormDraft.js +139 -8
  354. package/src/composables/useListView.js +4 -4
  355. package/src/css/form-field.css +14 -0
  356. package/src/css/index-page.css +71 -0
  357. package/src/css/kpi-card.css +32 -0
  358. package/src/css/look-board-detail.css +113 -2
  359. package/src/css/look-board-index.css +90 -8
  360. package/src/css/look-board.css +109 -2
  361. package/src/css/table.css +19 -0
  362. package/src/index.js +19 -0
  363. package/src/integrations/builtin/field-inspection/CnFieldInspectionCard.vue +35 -20
  364. package/src/integrations/builtin/field-inspection.js +21 -0
  365. package/src/integrations/offline/checklistSubmission.js +137 -0
  366. package/src/integrations/offline/fieldCollectionHelpers.js +69 -4
  367. package/src/integrations/offline/index.js +3 -0
  368. package/src/integrations/offline/offlineDb.js +6 -1
  369. package/src/integrations/offline/syncReplayService.js +8 -2
  370. package/src/schemas/app-manifest-v2.schema.json +196 -13
  371. package/src/utils/contentHeaderLink.js +48 -0
  372. package/src/utils/detailActionModel.js +59 -2
  373. package/src/utils/facets.js +74 -2
  374. package/src/utils/fetchFilterCounts.js +39 -4
  375. package/src/utils/fillTemplate.js +116 -0
  376. package/src/utils/initials.js +29 -0
  377. package/src/utils/libraryWidgetKeys.js +11 -0
  378. package/src/utils/objectWrite.js +55 -0
  379. package/src/utils/pageControls.js +78 -0
  380. package/src/utils/personalLenses.js +39 -2
  381. package/src/utils/queryPresets.js +93 -0
  382. package/src/utils/resolveFilterTokens.js +14 -1
  383. package/src/utils/resolveManifestTokens.js +2 -0
  384. package/src/utils/schema.js +21 -0
  385. package/src/utils/sentinelTokens.js +22 -4
  386. package/src/utils/userGroupsPeek.js +87 -0
  387. package/src/utils/validateManifestV2.compiled.js +3 -3
  388. package/src/utils/visibleWhen.js +56 -4
  389. package/src/utils/widgetDispatch.js +7 -1
  390. package/src/utils/widgetVisibility.js +7 -56
@@ -151,6 +151,8 @@
151
151
  :sortKey="localSort.field || null"
152
152
  :sortOrder="localSort.dir || 'asc'"
153
153
  borderless
154
+ :fitWidth="fitWidthActive"
155
+ :fixedLayout="fixedLayoutActive"
154
156
  :hideHeader="content.hideHeader === true"
155
157
  @rowClick="onRowClick"
156
158
  @rowAuxClick="onRowAuxClick"
@@ -173,6 +175,8 @@
173
175
  :sortKey="localSort.field || null"
174
176
  :sortOrder="localSort.dir || 'asc'"
175
177
  borderless
178
+ :fitWidth="fitWidthActive"
179
+ :fixedLayout="fixedLayoutActive"
176
180
  :hideHeader="content.hideHeader === true"
177
181
  @rowClick="onRowClick"
178
182
  @rowAuxClick="onRowAuxClick"
@@ -448,6 +452,15 @@ export default {
448
452
  page: 1,
449
453
  /** Rows that fit the host cell; null = unconstrained (dashboards). */
450
454
  fitRows: null,
455
+ /**
456
+ * How far the table has been squeezed to the widget's width, so the
457
+ * card never scrolls sideways: 0 = its own auto layout, 1 = one column
458
+ * takes what is left and is cut with an ellipsis while the rest keep
459
+ * their content on one line, 2 = a fixed layout, every column an equal
460
+ * share with its text wrapped. Each step is taken only while the table
461
+ * still scrolls sideways. `content.fitWidth` forces stage 1 on or off.
462
+ */
463
+ fitStage: 0,
451
464
  /** Whether the create dialog is open. */
452
465
  showCreate: false,
453
466
  /**
@@ -480,6 +493,27 @@ export default {
480
493
  },
481
494
 
482
495
  computed: {
496
+ /**
497
+ * Whether the table fits the widget's width (stage 1): `content.fitWidth`
498
+ * when it says, else once the columns were measured not to fit.
499
+ *
500
+ * @return {boolean}
501
+ */
502
+ fitWidthActive() {
503
+ const forced = this.content?.fitWidth
504
+ return typeof forced === 'boolean' ? forced : this.fitStage === 1
505
+ },
506
+
507
+ /**
508
+ * Whether the table takes a fixed layout, the last resort when even the
509
+ * fitted table is wider than the widget (not with `content.fitWidth` set).
510
+ *
511
+ * @return {boolean}
512
+ */
513
+ fixedLayoutActive() {
514
+ return typeof this.content?.fitWidth !== 'boolean' && this.fitStage >= 2
515
+ },
516
+
483
517
  /**
484
518
  * The unwrapped detail-page object context for token resolution, or null
485
519
  * on surfaces (dashboards) that don't provide one.
@@ -619,7 +653,7 @@ export default {
619
653
  // `secondary` (a second, muted line under the cell) rides
620
654
  // along too: the table draws it, and dropping it here took the
621
655
  // list rows' second line away (zuiddrecht-pixel-gaps-3).
622
- for (const k of ['format', 'widget', 'widgetProps', 'formatter', 'align', 'width', 'type', 'enum', 'enumLabels', 'sortable', 'secondary']) {
656
+ for (const k of ['format', 'widget', 'widgetProps', 'formatter', 'align', 'width', 'type', 'enum', 'enumLabels', 'sortable', 'secondary', 'colorMap', 'grow']) {
623
657
  if (c[k] !== undefined) {
624
658
  out[k] = c[k]
625
659
  }
@@ -1165,6 +1199,12 @@ export default {
1165
1199
  mounted() {
1166
1200
  this.fetchRows()
1167
1201
  this.loadHeadingsIfNeeded()
1202
+ // A table wider than the widget fits its columns to the width instead of
1203
+ // scrolling sideways (no inner scrollbars).
1204
+ if (typeof ResizeObserver !== 'undefined') {
1205
+ this._widthObserver = new ResizeObserver(() => this.measureWidthFit())
1206
+ this._widthObserver.observe(this.$el)
1207
+ }
1168
1208
  // Re-read on a page-level refresh. This list fetches its own rows from
1169
1209
  // OpenRegister and subscribed to nothing, so after a write elsewhere on
1170
1210
  // the page it went on rendering the result set it fetched on mount —
@@ -1203,6 +1243,9 @@ export default {
1203
1243
  if (this._fitObserver) {
1204
1244
  this._fitObserver.disconnect()
1205
1245
  }
1246
+ if (this._widthObserver) {
1247
+ this._widthObserver.disconnect()
1248
+ }
1206
1249
  },
1207
1250
 
1208
1251
  methods: {
@@ -1277,7 +1320,10 @@ export default {
1277
1320
  const res = await axios.get(url, { params })
1278
1321
  this.rows = (res && res.data && res.data.results) || []
1279
1322
  this.total = (res && res.data && typeof res.data.total === 'number') ? res.data.total : this.rows.length
1280
- this.$nextTick(() => this.measureFit())
1323
+ this.$nextTick(() => {
1324
+ this.measureFit()
1325
+ this.measureWidthFit()
1326
+ })
1281
1327
  } catch (e) {
1282
1328
  // Keep the raw message OUT of the template — log it for
1283
1329
  // debugging, surface only the quiet `loadErrorLabel` line.
@@ -1291,6 +1337,27 @@ export default {
1291
1337
  }
1292
1338
  },
1293
1339
 
1340
+ /**
1341
+ * Squeeze the table to the widget's width while it scrolls sideways: its
1342
+ * `.cn-data-table__scroll` wrapper is wider than it is visible. First the
1343
+ * fit (stage 1), then, if that is still too wide, a fixed layout
1344
+ * (stage 2). It never goes back (a squeezed table does not overflow, so
1345
+ * there is nothing to measure to relax it). No-op when `content.fitWidth`
1346
+ * is set.
1347
+ *
1348
+ * @return {void}
1349
+ */
1350
+ measureWidthFit() {
1351
+ if (this.fitStage >= 2 || typeof this.content?.fitWidth === 'boolean') {
1352
+ return
1353
+ }
1354
+ const scroller = this.$el && this.$el.querySelector ? this.$el.querySelector('.cn-data-table__scroll') : null
1355
+ if (scroller && scroller.scrollWidth > scroller.clientWidth + 1) {
1356
+ this.fitStage += 1
1357
+ this.$nextTick(() => this.measureWidthFit())
1358
+ }
1359
+ },
1360
+
1294
1361
  /**
1295
1362
  * Fit the visible row count to the host grid cell (ADR-062 — the cell
1296
1363
  * is the budget). Measures the cell's remaining height below the table
@@ -16,6 +16,13 @@
16
16
  :rowActions="rowActions"
17
17
  :newActions="newActions"
18
18
  :linkedItems="linkedItems"
19
+ :columns="columns"
20
+ :rowData="rowDataSource"
21
+ :preferenceApp="preferenceApp"
22
+ :preferenceKey="preferenceKey"
23
+ :bulkActions="bulkActions"
24
+ :groupBy="groupBy"
25
+ :facets="facets"
19
26
  @changed="onBrowserChanged" />
20
27
  <template v-else>
21
28
  <!-- Upload error -->
@@ -240,7 +247,7 @@ import CnFilePreview from '../CnFilePreview/CnFilePreview.vue'
240
247
  import CnFilesBrowser from '../CnFilesBrowser/CnFilesBrowser.vue'
241
248
  import { useFileOpener } from '../../composables/useFileOpener.js'
242
249
  import { buildHeaders, prefixUrl } from '../../utils/index.js'
243
- import { objectDisplayName, resolveObjectFolderInfo } from '../CnFilesBrowser/filesBrowser.js'
250
+ import { indexRowData, objectDisplayName, resolveObjectFolderInfo } from '../CnFilesBrowser/filesBrowser.js'
244
251
  import { createOpenRegisterSource } from '../CnFilesBrowser/openRegisterSource.js'
245
252
 
246
253
  /**
@@ -359,6 +366,47 @@ export default {
359
366
  * Null fetches nothing.
360
367
  */
361
368
  linkedItemsUrl: { type: String, default: null },
369
+ /**
370
+ * Forwarded to CnFilesBrowser: the columns the browser shows, built-in
371
+ * names or declared columns (`{ key, label, source, formatter }`).
372
+ * A `source: 'row'` column reads the host's record for the file from
373
+ * `rowDataUrl`. None keeps name, size and modified.
374
+ *
375
+ * @type {Array<string|object>}
376
+ */
377
+ columns: { type: Array, default: () => [] },
378
+ /** Forwarded to CnFilesBrowser: the app id the user's column choice is stored under; unset offers no Columns chooser. */
379
+ preferenceApp: { type: String, default: '' },
380
+ /** Forwarded to CnFilesBrowser: the key the column choice is stored under. */
381
+ preferenceKey: { type: String, default: 'files-browser-columns' },
382
+ /**
383
+ * An app endpoint that answers the host's own record per file, for the
384
+ * browser's `source: 'row'` columns. App-relative; `{objectId}` is
385
+ * replaced. Read once per listing. The answer is a list of records
386
+ * (or the list at `rowDataPath`, or under `items` or `results`), each
387
+ * naming its file in `rowDataKey`. Null reads nothing.
388
+ */
389
+ rowDataUrl: { type: String, default: null },
390
+ /**
391
+ * Forwarded to CnFilesBrowser: the host's actions on a selection of
392
+ * files. With at least one, files get checkboxes and a bulk bar.
393
+ *
394
+ * @type {Array<{id: string, label: string, icon?: string, type?: string, target?: string, props?: object}>}
395
+ */
396
+ bulkActions: { type: Array, default: () => [] },
397
+ /** Forwarded to CnFilesBrowser: the key of a declared column to group the files on. */
398
+ groupBy: { type: String, default: '' },
399
+ /**
400
+ * Forwarded to CnFilesBrowser: keys of declared columns to filter on,
401
+ * a chip per value in use.
402
+ *
403
+ * @type {string[]}
404
+ */
405
+ facets: { type: Array, default: () => [] },
406
+ /** A dotted path to the list of records in the `rowDataUrl` answer; empty reads the answer itself. */
407
+ rowDataPath: { type: String, default: '' },
408
+ /** The record field that holds the Nextcloud file id. */
409
+ rowDataKey: { type: String, default: 'fileId' },
362
410
  /** Label of the Download action. */
363
411
  downloadLabel: { type: String, default: () => t('nextcloud-vue', 'Download') },
364
412
  /** Label of the action that opens the Files sidebar on the file. */
@@ -424,6 +472,27 @@ export default {
424
472
  },
425
473
 
426
474
  computed: {
475
+ /**
476
+ * The browser's `rowData`: a function that reads the host's records
477
+ * from `rowDataUrl` and keys them by file id, or null without a url.
478
+ * The browser calls it once per listing.
479
+ *
480
+ * @return {(function(): Promise<object>)|null} The row data source.
481
+ * @spec openspec/changes/files-browser-hosts-a-documents-list/specs/files-browser/spec.md
482
+ */
483
+ rowDataSource() {
484
+ if (!this.rowDataUrl || !this.objectId) {
485
+ return null
486
+ }
487
+ const url = generateUrl(this.rowDataUrl.replace('{objectId}', encodeURIComponent(this.objectId)))
488
+ const path = this.rowDataPath
489
+ const key = this.rowDataKey
490
+ return async () => {
491
+ const { data } = await axios.get(url)
492
+ return indexRowData(data, { path, key })
493
+ }
494
+ },
495
+
427
496
  /**
428
497
  * The root crumb: the host's label, else the object's name (its folder
429
498
  * on disk is called after its uuid), else the folder's own name.
@@ -18,6 +18,8 @@
18
18
  {{ cancelLabel }}
19
19
  </NcButton>
20
20
  </p>
21
+ <!-- @slot composer-before An app's own control above the composer, e.g. a template picker. Scope: `{ setText, text }`; `setText(text)` fills the composer, `text` is what it holds now. -->
22
+ <slot name="composer-before" :setText="setComposerText" :text="newNoteText" />
21
23
  <CnNoteComposer
22
24
  class="cn-sidebar-tab__composer"
23
25
  :modelValue="newNoteText"
@@ -481,6 +483,20 @@ export default {
481
483
  }
482
484
  },
483
485
 
486
+ /**
487
+ * Fill the composer from outside, for the `composer-before` slot.
488
+ *
489
+ * The app decides what goes in (a template body, a quoted line); the
490
+ * composer stays the one place the text lives, so sending, editing and
491
+ * the mention picker all see it exactly as if it had been typed.
492
+ *
493
+ * @param {string} text The text the composer should hold.
494
+ * @return {void}
495
+ */
496
+ setComposerText(text) {
497
+ this.newNoteText = (text === null || text === undefined) ? '' : String(text)
498
+ },
499
+
484
500
  startEdit(note) {
485
501
  this.replyTo = null
486
502
  this.editingNoteId = note.id
@@ -305,6 +305,7 @@ import { pageHasSplitView, pageIdForRoute, splitIdForRoute, splitRouteName } fro
305
305
  import { reportDiagnostic, reportDiagnosticOnce } from '../../utils/diagnostics.js'
306
306
  import { openRowTarget } from '../../utils/linkNavigation.js'
307
307
  import { listContextFromRoute, listContextToQuery } from '../../utils/listNavigation.js'
308
+ import { applyQueryPreset, matchQueryPreset } from '../../utils/queryPresets.js'
308
309
  import { resolveRouteSentinels } from '../../utils/resolveRouteSentinels.js'
309
310
  import { parseSortKeys } from '../../utils/routeFilters.js'
310
311
  import { buildRouteParams, routePathFor } from '../../utils/routeParams.js'
@@ -1050,7 +1051,25 @@ export default {
1050
1051
  return 'none'
1051
1052
  }
1052
1053
  const cfg = (page.config && typeof page.config === 'object' && !Array.isArray(page.config)) ? page.config : {}
1053
- return [page.id, cfg.register || '', cfg.schema || ''].join(':')
1054
+ // A query preset swaps the lenses and columns, so it remounts the
1055
+ // page: the active lens index belongs to the lenses it indexes.
1056
+ const preset = this.activeQueryPresetIndex >= 0 ? `:preset-${this.activeQueryPresetIndex}` : ''
1057
+ return [page.id, cfg.register || '', cfg.schema || ''].join(':') + preset
1058
+ },
1059
+
1060
+ /**
1061
+ * The index of the current index page's query preset that the route
1062
+ * query matches, or -1.
1063
+ *
1064
+ * @return {number}
1065
+ * @spec openspec/changes/screens-index-query-presets/specs/index-query-presets/spec.md#requirement-a-query-preset-overlays-lenses-columns-and-copy
1066
+ */
1067
+ activeQueryPresetIndex() {
1068
+ const page = this.currentPage
1069
+ if (!page || page.type !== 'index' || !page.config || !Array.isArray(page.config.queryPresets)) {
1070
+ return -1
1071
+ }
1072
+ return matchQueryPreset(page.config.queryPresets, this.$route?.query || {})
1054
1073
  },
1055
1074
 
1056
1075
  /**
@@ -1297,6 +1316,15 @@ export default {
1297
1316
  // container before forwarding — CnIndexPage has no
1298
1317
  // `actionToggles` prop.
1299
1318
  const isIndex = page?.type === 'index'
1319
+ // A query preset (`config.queryPresets`): the menu entry's query
1320
+ // brings its own lenses, columns and copy to the same page.
1321
+ if (isIndex && Array.isArray(config.queryPresets)) {
1322
+ const preset = config.queryPresets[this.activeQueryPresetIndex] || null
1323
+ config = applyQueryPreset(config, preset)
1324
+ if (preset && typeof preset.title === 'string' && preset.title !== '') {
1325
+ topLevel.title = preset.title
1326
+ }
1327
+ }
1300
1328
  // When an index page has somewhere to open a row, make a row click
1301
1329
  // go there: set `rowClickToView` so the row body emits `row-click`
1302
1330
  // (→ onRowOpen navigates) even though the page is selectable.
@@ -0,0 +1,313 @@
1
+ <!--
2
+ - SPDX-FileCopyrightText: 2026 Conduction B.V.
3
+ - SPDX-License-Identifier: EUPL-1.2
4
+ -->
5
+
6
+ <template>
7
+ <fieldset class="cn-radio-cards" data-testid="cn-radio-cards" :aria-busy="busy ? 'true' : 'false'">
8
+ <legend v-if="label" class="cn-radio-cards__legend">
9
+ {{ label }}
10
+ </legend>
11
+ <div class="cn-radio-cards__grid" :class="`cn-radio-cards__grid--${columns}`">
12
+ <label
13
+ v-for="option in options"
14
+ :key="String(option.value)"
15
+ class="cn-radio-cards__card"
16
+ :class="{ 'cn-radio-cards__card--selected': isSelected(option) }"
17
+ :data-testid="`cn-radio-cards-option-${option.value}`">
18
+ <input
19
+ class="cn-radio-cards__input"
20
+ type="radio"
21
+ :name="groupName"
22
+ :value="String(option.value)"
23
+ :checked="isSelected(option)"
24
+ :disabled="readOnly || busy"
25
+ @change="choose(option)">
26
+ <span class="cn-radio-cards__dot" aria-hidden="true" />
27
+ <span class="cn-radio-cards__text">
28
+ <span class="cn-radio-cards__title">{{ option.label }}</span>
29
+ <span v-if="option.description" class="cn-radio-cards__description">{{ option.description }}</span>
30
+ </span>
31
+ </label>
32
+ </div>
33
+ <NcNoteCard v-if="notice" type="error" data-testid="cn-radio-cards-notice">
34
+ {{ notice }}
35
+ </NcNoteCard>
36
+ </fieldset>
37
+ </template>
38
+
39
+ <script>
40
+ import { NcNoteCard } from '@nextcloud/vue'
41
+ import { objectUrlOf, patchObject } from '../../utils/objectWrite.js'
42
+ import { readPath } from '../../utils/readPath.js'
43
+ import { serverErrorMessage } from '../../utils/serverErrorMessage.js'
44
+
45
+ let uid = 0
46
+
47
+ /**
48
+ * CnRadioCardsWidget — the `radio-cards` widget: one choice out of a few cards,
49
+ * each with a title and a description, bound to an enum field of the CURRENT
50
+ * object. Choosing a card saves at once with ONE PATCH of that field; a refused
51
+ * PATCH puts the choice back and shows the server's words. Real radio inputs
52
+ * underneath, so the choice is perceivable without colour and works from the
53
+ * keyboard.
54
+ *
55
+ * @spec openspec/changes/abstract-radio-cards/specs/radio-cards/spec.md
56
+ */
57
+ export default {
58
+ name: 'CnRadioCardsWidget',
59
+
60
+ components: { NcNoteCard },
61
+
62
+ inject: {
63
+ cnTranslate: { default: () => (key) => key },
64
+ },
65
+
66
+ props: {
67
+ /**
68
+ * The widget configuration.
69
+ *
70
+ * - `field` (required) — the top-level enum field.
71
+ * - `options` (required) — `[{ value, label, description? }]`.
72
+ * - `label` — a legend over the cards (visually shown).
73
+ * - `columns` — 1, 2 or 3 (default 2).
74
+ * - `readOnly`.
75
+ *
76
+ * @type {object}
77
+ */
78
+ content: {
79
+ type: Object,
80
+ default: () => ({}),
81
+ },
82
+
83
+ /** The current detail-page object. */
84
+ objectData: {
85
+ type: Object,
86
+ default: null,
87
+ },
88
+
89
+ /** The current object id. */
90
+ objectId: {
91
+ type: [String, Number],
92
+ default: '',
93
+ },
94
+
95
+ /** OpenRegister register slug of the surface. */
96
+ register: {
97
+ type: [String, Object],
98
+ default: '',
99
+ },
100
+
101
+ /** OpenRegister schema slug of the surface. */
102
+ schema: {
103
+ type: [String, Object],
104
+ default: '',
105
+ },
106
+ },
107
+
108
+ emits: ['saved'],
109
+
110
+ data() {
111
+ uid += 1
112
+ return {
113
+ groupName: `cn-radio-cards-${uid}`,
114
+ saved: '',
115
+ value: '',
116
+ busy: false,
117
+ notice: '',
118
+ }
119
+ },
120
+
121
+ computed: {
122
+ options() {
123
+ const list = Array.isArray(this.content.options) ? this.content.options : []
124
+ return list
125
+ .filter((option) => option !== null && option !== undefined)
126
+ .map((option) => (typeof option === 'object'
127
+ ? { value: option.value, label: this.tr(String(option.label ?? option.value)), description: this.tr(option.description || '') }
128
+ : { value: option, label: this.tr(String(option)), description: '' }))
129
+ },
130
+
131
+ label() {
132
+ return this.tr(this.content.label || '')
133
+ },
134
+
135
+ columns() {
136
+ return [1, 3].includes(this.content.columns) ? this.content.columns : 2
137
+ },
138
+
139
+ readOnly() {
140
+ return this.content.readOnly === true
141
+ },
142
+
143
+ objectUrl() {
144
+ return objectUrlOf(this.register, this.schema, this.objectId || (this.objectData && this.objectData.id))
145
+ },
146
+ },
147
+
148
+ watch: {
149
+ objectData() {
150
+ this.adopt()
151
+ },
152
+ },
153
+
154
+ created() {
155
+ this.adopt()
156
+ },
157
+
158
+ methods: {
159
+ tr(text) {
160
+ if (typeof text !== 'string' || text === '') {
161
+ return ''
162
+ }
163
+ return typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text
164
+ },
165
+
166
+ /** Read the choice off the object. */
167
+ adopt() {
168
+ const stored = readPath(this.objectData || {}, this.content.field)
169
+ this.saved = stored === undefined || stored === null ? '' : String(stored)
170
+ this.value = this.saved
171
+ },
172
+
173
+ isSelected(option) {
174
+ return this.value === String(option.value)
175
+ },
176
+
177
+ /**
178
+ * Choose a card and save that one field.
179
+ *
180
+ * @param {object} option The chosen option.
181
+ * @return {Promise<void>}
182
+ * @spec openspec/changes/abstract-radio-cards/specs/radio-cards/spec.md#requirement-choosing-a-card-saves-that-one-field
183
+ */
184
+ async choose(option) {
185
+ if (!this.objectUrl || !this.content.field) {
186
+ this.notice = this.tr('This choice cannot be saved here.')
187
+ return
188
+ }
189
+ this.notice = ''
190
+ this.busy = true
191
+ this.value = String(option.value)
192
+ try {
193
+ const data = await patchObject(this.objectUrl, { [this.content.field]: option.value })
194
+ const written = readPath(data, this.content.field)
195
+ this.saved = String(written !== undefined && written !== null ? written : option.value)
196
+ this.value = this.saved
197
+ /**
198
+ * @event saved The choice was written.
199
+ * @type {{field: string, value: unknown}}
200
+ */
201
+ this.$emit('saved', { field: this.content.field, value: option.value })
202
+ } catch (e) {
203
+ this.value = this.saved
204
+ this.notice = serverErrorMessage(e)
205
+ } finally {
206
+ this.busy = false
207
+ }
208
+ },
209
+ },
210
+ }
211
+ </script>
212
+
213
+ <style scoped>
214
+ .cn-radio-cards {
215
+ display: flex;
216
+ flex-direction: column;
217
+ gap: 8px;
218
+ min-width: 0;
219
+ margin: 0;
220
+ padding: 0;
221
+ border: 0;
222
+ }
223
+
224
+ .cn-radio-cards__legend {
225
+ padding: 0;
226
+ margin-bottom: 6px;
227
+ font-weight: 600;
228
+ }
229
+
230
+ .cn-radio-cards__grid {
231
+ display: grid;
232
+ gap: 10px;
233
+ grid-template-columns: minmax(0, 1fr);
234
+ }
235
+
236
+ .cn-radio-cards__grid--2 {
237
+ grid-template-columns: repeat(2, minmax(0, 1fr));
238
+ }
239
+
240
+ .cn-radio-cards__grid--3 {
241
+ grid-template-columns: repeat(3, minmax(0, 1fr));
242
+ }
243
+
244
+ .cn-radio-cards__card {
245
+ position: relative;
246
+ display: flex;
247
+ align-items: flex-start;
248
+ gap: 10px;
249
+ padding: 12px 14px;
250
+ border: 1px solid var(--color-border-maxcontrast);
251
+ border-radius: var(--cn-board-control-radius, 8px);
252
+ background: var(--color-main-background);
253
+ cursor: pointer;
254
+ }
255
+
256
+ .cn-radio-cards__card--selected {
257
+ border-color: var(--color-primary-element);
258
+ background: var(--color-primary-element-light);
259
+ box-shadow: 0 0 0 1px var(--color-primary-element);
260
+ }
261
+
262
+ .cn-radio-cards__card:focus-within {
263
+ outline: 2px solid var(--color-primary-element);
264
+ outline-offset: 2px;
265
+ }
266
+
267
+ /* The real input stays in the DOM and in the accessibility tree; the dot is its picture. */
268
+ .cn-radio-cards__input {
269
+ position: absolute;
270
+ opacity: 0;
271
+ inset: 0;
272
+ margin: 0;
273
+ cursor: pointer;
274
+ }
275
+
276
+ .cn-radio-cards__dot {
277
+ flex: 0 0 auto;
278
+ width: 14px;
279
+ height: 14px;
280
+ margin-top: 3px;
281
+ box-sizing: border-box;
282
+ border: 1px solid var(--color-border-maxcontrast);
283
+ border-radius: 50%;
284
+ background: var(--color-main-background);
285
+ }
286
+
287
+ .cn-radio-cards__card--selected .cn-radio-cards__dot {
288
+ border: 4px solid var(--color-primary-element);
289
+ }
290
+
291
+ .cn-radio-cards__text {
292
+ display: flex;
293
+ flex-direction: column;
294
+ gap: 2px;
295
+ min-width: 0;
296
+ }
297
+
298
+ .cn-radio-cards__title {
299
+ font-weight: 600;
300
+ }
301
+
302
+ .cn-radio-cards__description {
303
+ font-size: 12px;
304
+ color: var(--color-text-maxcontrast);
305
+ }
306
+
307
+ @media (max-width: 640px) {
308
+ .cn-radio-cards__grid--2,
309
+ .cn-radio-cards__grid--3 {
310
+ grid-template-columns: minmax(0, 1fr);
311
+ }
312
+ }
313
+ </style>
@@ -0,0 +1,27 @@
1
+ /**
2
+ * SPDX-FileCopyrightText: 2026 Conduction B.V.
3
+ * SPDX-License-Identifier: EUPL-1.2
4
+ *
5
+ * CnRadioCardsWidget — self-registers the `radio-cards` widget type (one
6
+ * choice out of a few cards with a title and a description, bound to an enum
7
+ * field of the current object and saved on selection) into the shared
8
+ * dashboardWidgetRegistry at module load.
9
+ */
10
+
11
+ import CnRadioCardsWidget from './CnRadioCardsWidget.vue'
12
+ import { registerDashboardWidget } from '../CnWidgetGrid/dashboardWidgetRegistry.js'
13
+
14
+ registerDashboardWidget('radio-cards', {
15
+ renderer: CnRadioCardsWidget,
16
+ form: null,
17
+ defaultContent: {
18
+ field: '',
19
+ options: [],
20
+ },
21
+ displayName: 'Radio cards',
22
+ icon: 'RadioboxMarked',
23
+ surfaces: ['detail-page'],
24
+ })
25
+
26
+ export { CnRadioCardsWidget }
27
+ export default CnRadioCardsWidget