@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
@@ -0,0 +1,120 @@
1
+ import { formatBoardDate, boardDateLocale } from './boardDate.js';
2
+ import { parseDateValue } from './dateVariant.js';
3
+ import { readPath } from './readPath.js';
4
+
5
+ // SPDX-FileCopyrightText: 2026 Conduction B.V.
6
+ // SPDX-License-Identifier: EUPL-1.2
7
+
8
+
9
+ const TOKEN = /\{([^{}\s|]+)(?:\|([a-z]+))?\}/g;
10
+
11
+ /**
12
+ * A moment in the board's short form with the time: "4 okt, 10:52", or
13
+ * "14 feb 2024, 09:00" outside the current year.
14
+ *
15
+ * @param {unknown} value A Date, a timestamp or an ISO string.
16
+ * @param {Date} [now] The reference moment.
17
+ * @return {string} The text, or '' when the value is not a date.
18
+ * @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-record-list-shows-the-objects-that-point-at-this-record
19
+ */
20
+ function formatBoardDateTime(value, now = new Date()) {
21
+ const day = formatBoardDate(value, now);
22
+ const date = parseDateValue(value);
23
+ if (day === '' || date === null) {
24
+ return ''
25
+ }
26
+ let time;
27
+ try {
28
+ time = new Intl.DateTimeFormat(boardDateLocale(), { hour: '2-digit', minute: '2-digit' }).format(date);
29
+ } catch {
30
+ time = new Intl.DateTimeFormat(undefined, { hour: '2-digit', minute: '2-digit' }).format(date);
31
+ }
32
+ return `${day}, ${time}`
33
+ }
34
+
35
+ /**
36
+ * Format one token value.
37
+ *
38
+ * @param {unknown} value The raw value.
39
+ * @param {string} format `date`, `datetime` or '' (as is).
40
+ * @param {Date} now The reference moment.
41
+ * @return {string} The text, '' when nothing can be shown.
42
+ */
43
+ function formatValue(value, format, now) {
44
+ if (value === null || value === undefined || value === '' || typeof value === 'object') {
45
+ return ''
46
+ }
47
+ if (format === 'date') {
48
+ return formatBoardDate(value, now)
49
+ }
50
+ if (format === 'datetime') {
51
+ return formatBoardDateTime(value, now)
52
+ }
53
+ if (format === 'days') {
54
+ // Whole days from the date to now (0 for today; negative for a future date).
55
+ const date = parseDateValue(value);
56
+ return date === null ? '' : String(Math.floor((now.getTime() - date.getTime()) / 86400000))
57
+ }
58
+ return String(value)
59
+ }
60
+
61
+ /**
62
+ * Fill a row template such as `"{identifier} · {caseTypeName}"` or
63
+ * `"Termijn {deadline|date}"` from an object.
64
+ *
65
+ * Every `{path}` token is a dot path into the object; `{path|date}` and
66
+ * `{path|datetime}` format it as the boards write dates, and `{path|days}` is the
67
+ * whole days since that date ("has waited {createdAt|days} days"). A token without a
68
+ * value empties the WHOLE template, because "Termijn " is worse than nothing.
69
+ * A template without tokens is returned as written (a constant label).
70
+ *
71
+ * @param {string} template The template, already translated.
72
+ * @param {object|null} object The object to read.
73
+ * @param {Date} [now] The reference moment for dates.
74
+ * @return {string} The filled text, or ''.
75
+ * @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-record-list-shows-the-objects-that-point-at-this-record
76
+ */
77
+ function fillTemplate(template, object, now = new Date()) {
78
+ if (typeof template !== 'string' || template.trim() === '') {
79
+ return ''
80
+ }
81
+ let missing = false;
82
+ const text = template.replace(TOKEN, (_match, path, format) => {
83
+ const value = object && typeof object === 'object' ? readPath(object, path) : undefined;
84
+ const shown = formatValue(value, format || '', now);
85
+ if (shown === '') {
86
+ missing = true;
87
+ }
88
+ return shown
89
+ });
90
+ return missing ? '' : text.trim()
91
+ }
92
+
93
+ /**
94
+ * The first of several templates that fills: `["Klaar {closedAt|date}",
95
+ * "Termijn {deadline|date}"]` reads "Klaar ..." once the case is closed and
96
+ * "Termijn ..." while it is open. A single string is one candidate.
97
+ *
98
+ * @param {string|string[]} templates The candidates, in order.
99
+ * @param {object|null} object The object to read.
100
+ * @param {(key: string) => string} [translate] Applied to each template before filling.
101
+ * @param {Date} [now] The reference moment for dates.
102
+ * @return {string} The first filled text, or ''.
103
+ * @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-record-list-shows-the-objects-that-point-at-this-record
104
+ */
105
+ function firstFilled(templates, object, translate = (key) => key, now = new Date()) {
106
+ const list = Array.isArray(templates) ? templates : [templates];
107
+ for (const template of list) {
108
+ if (typeof template !== 'string') {
109
+ continue
110
+ }
111
+ const text = fillTemplate(translate(template), object, now);
112
+ if (text !== '') {
113
+ return text
114
+ }
115
+ }
116
+ return ''
117
+ }
118
+
119
+ export { fillTemplate, firstFilled, formatBoardDateTime };
120
+ //# sourceMappingURL=fillTemplate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fillTemplate.js","sources":["../../../src/utils/fillTemplate.js"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 Conduction B.V.\n// SPDX-License-Identifier: EUPL-1.2\n\nimport { boardDateLocale, formatBoardDate } from './boardDate.js'\nimport { parseDateValue } from './dateVariant.js'\nimport { readPath } from './readPath.js'\n\nconst TOKEN = /\\{([^{}\\s|]+)(?:\\|([a-z]+))?\\}/g\n\n/**\n * A moment in the board's short form with the time: \"4 okt, 10:52\", or\n * \"14 feb 2024, 09:00\" outside the current year.\n *\n * @param {unknown} value A Date, a timestamp or an ISO string.\n * @param {Date} [now] The reference moment.\n * @return {string} The text, or '' when the value is not a date.\n * @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-record-list-shows-the-objects-that-point-at-this-record\n */\nexport function formatBoardDateTime(value, now = new Date()) {\n\tconst day = formatBoardDate(value, now)\n\tconst date = parseDateValue(value)\n\tif (day === '' || date === null) {\n\t\treturn ''\n\t}\n\tlet time\n\ttry {\n\t\ttime = new Intl.DateTimeFormat(boardDateLocale(), { hour: '2-digit', minute: '2-digit' }).format(date)\n\t} catch {\n\t\ttime = new Intl.DateTimeFormat(undefined, { hour: '2-digit', minute: '2-digit' }).format(date)\n\t}\n\treturn `${day}, ${time}`\n}\n\n/**\n * Format one token value.\n *\n * @param {unknown} value The raw value.\n * @param {string} format `date`, `datetime` or '' (as is).\n * @param {Date} now The reference moment.\n * @return {string} The text, '' when nothing can be shown.\n */\nfunction formatValue(value, format, now) {\n\tif (value === null || value === undefined || value === '' || typeof value === 'object') {\n\t\treturn ''\n\t}\n\tif (format === 'date') {\n\t\treturn formatBoardDate(value, now)\n\t}\n\tif (format === 'datetime') {\n\t\treturn formatBoardDateTime(value, now)\n\t}\n\tif (format === 'days') {\n\t\t// Whole days from the date to now (0 for today; negative for a future date).\n\t\tconst date = parseDateValue(value)\n\t\treturn date === null ? '' : String(Math.floor((now.getTime() - date.getTime()) / 86400000))\n\t}\n\treturn String(value)\n}\n\n/**\n * Fill a row template such as `\"{identifier} · {caseTypeName}\"` or\n * `\"Termijn {deadline|date}\"` from an object.\n *\n * Every `{path}` token is a dot path into the object; `{path|date}` and\n * `{path|datetime}` format it as the boards write dates, and `{path|days}` is the\n * whole days since that date (\"has waited {createdAt|days} days\"). A token without a\n * value empties the WHOLE template, because \"Termijn \" is worse than nothing.\n * A template without tokens is returned as written (a constant label).\n *\n * @param {string} template The template, already translated.\n * @param {object|null} object The object to read.\n * @param {Date} [now] The reference moment for dates.\n * @return {string} The filled text, or ''.\n * @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-record-list-shows-the-objects-that-point-at-this-record\n */\nexport function fillTemplate(template, object, now = new Date()) {\n\tif (typeof template !== 'string' || template.trim() === '') {\n\t\treturn ''\n\t}\n\tlet missing = false\n\tconst text = template.replace(TOKEN, (_match, path, format) => {\n\t\tconst value = object && typeof object === 'object' ? readPath(object, path) : undefined\n\t\tconst shown = formatValue(value, format || '', now)\n\t\tif (shown === '') {\n\t\t\tmissing = true\n\t\t}\n\t\treturn shown\n\t})\n\treturn missing ? '' : text.trim()\n}\n\n/**\n * The first of several templates that fills: `[\"Klaar {closedAt|date}\",\n * \"Termijn {deadline|date}\"]` reads \"Klaar ...\" once the case is closed and\n * \"Termijn ...\" while it is open. A single string is one candidate.\n *\n * @param {string|string[]} templates The candidates, in order.\n * @param {object|null} object The object to read.\n * @param {(key: string) => string} [translate] Applied to each template before filling.\n * @param {Date} [now] The reference moment for dates.\n * @return {string} The first filled text, or ''.\n * @spec openspec/changes/screens-relation-pages/specs/relation-pages/spec.md#requirement-a-record-list-shows-the-objects-that-point-at-this-record\n */\nexport function firstFilled(templates, object, translate = (key) => key, now = new Date()) {\n\tconst list = Array.isArray(templates) ? templates : [templates]\n\tfor (const template of list) {\n\t\tif (typeof template !== 'string') {\n\t\t\tcontinue\n\t\t}\n\t\tconst text = fillTemplate(translate(template), object, now)\n\t\tif (text !== '') {\n\t\t\treturn text\n\t\t}\n\t}\n\treturn ''\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * SPDX-FileCopyrightText: 2026 Conduction B.V.
3
+ * SPDX-License-Identifier: EUPL-1.2
4
+ *
5
+ * initials: the letters an avatar shows for a free name.
6
+ *
7
+ * @module utils/initials
8
+ */
9
+
10
+ /**
11
+ * The initials of a name: the first letters of its first and its last word,
12
+ * upper-cased ("Sanne de Vries" reads "SV", "Jan" reads "J").
13
+ *
14
+ * @param {unknown} name The name.
15
+ * @return {string} One or two letters, or '' when there is no name.
16
+ * @spec openspec/changes/screens-record-list-avatar/specs/record-list-avatar/spec.md#requirement-a-record-list-row-can-carry-an-avatar
17
+ */
18
+ function initialsOf(name) {
19
+ if (typeof name !== 'string') {
20
+ return ''
21
+ }
22
+ const words = name.trim().split(/\s+/).filter((word) => word !== '');
23
+ if (words.length === 0) {
24
+ return ''
25
+ }
26
+ const first = Array.from(words[0])[0] || '';
27
+ const last = words.length > 1 ? (Array.from(words[words.length - 1])[0] || '') : '';
28
+ return (first + last).toLocaleUpperCase()
29
+ }
30
+
31
+ export { initialsOf };
32
+ //# sourceMappingURL=initials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"initials.js","sources":["../../../src/utils/initials.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * initials: the letters an avatar shows for a free name.\n *\n * @module utils/initials\n */\n\n/**\n * The initials of a name: the first letters of its first and its last word,\n * upper-cased (\"Sanne de Vries\" reads \"SV\", \"Jan\" reads \"J\").\n *\n * @param {unknown} name The name.\n * @return {string} One or two letters, or '' when there is no name.\n * @spec openspec/changes/screens-record-list-avatar/specs/record-list-avatar/spec.md#requirement-a-record-list-row-can-carry-an-avatar\n */\nexport function initialsOf(name) {\n\tif (typeof name !== 'string') {\n\t\treturn ''\n\t}\n\tconst words = name.trim().split(/\\s+/).filter((word) => word !== '')\n\tif (words.length === 0) {\n\t\treturn ''\n\t}\n\tconst first = Array.from(words[0])[0] || ''\n\tconst last = words.length > 1 ? (Array.from(words[words.length - 1])[0] || '') : ''\n\treturn (first + last).toLocaleUpperCase()\n}\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,UAAU,CAAC,IAAI,EAAE;AACjC,CAAC,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE;AAC/B,EAAE,OAAO,EAAE;AACX,EAAE;AACF,CAAC,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,IAAI,KAAK,EAAE,EAAC;AACrE,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE;AACzB,EAAE,OAAO,EAAE;AACX,EAAE;AACF,CAAC,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,GAAE;AAC5C,CAAC,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,GAAE;AACpF,CAAC,OAAO,CAAC,KAAK,GAAG,IAAI,EAAE,iBAAiB,EAAE;AAC1C;;;;"}
@@ -94,6 +94,8 @@ const DASHBOARD_CATALOG_WIDGET_KEYS = Object.freeze([
94
94
  'banner',
95
95
  'calendar',
96
96
  'chart',
97
+ 'chips-field',
98
+ 'choice-picker',
97
99
  'container',
98
100
  'conversation',
99
101
  'countdown',
@@ -101,6 +103,8 @@ const DASHBOARD_CATALOG_WIDGET_KEYS = Object.freeze([
101
103
  'delta',
102
104
  'divider',
103
105
  'document-review',
106
+ 'facts',
107
+ 'field-list',
104
108
  'files',
105
109
  'flow-runs',
106
110
  'gauge',
@@ -124,19 +128,26 @@ const DASHBOARD_CATALOG_WIDGET_KEYS = Object.freeze([
124
128
  'object-table',
125
129
  'people',
126
130
  'quicklinks',
131
+ 'radio-cards',
132
+ 'record-list',
127
133
  'related',
128
134
  'saved-view',
135
+ 'saved-views',
136
+ 'sections',
137
+ 'settings-form',
129
138
  'spend-analytics',
130
139
  'stacked-bar',
131
140
  'stages',
132
141
  'stat',
133
142
  'stats-block',
143
+ 'switch-rows',
134
144
  'table',
135
145
  'tabs',
136
146
  'tasks',
137
147
  'text',
138
148
  'tile',
139
149
  'timeline',
150
+ 'toggle-table',
140
151
  'video',
141
152
  'week-strip',
142
153
  'workspace-filter',
@@ -1 +1 @@
1
- {"version":3,"file":"libraryWidgetKeys.js","sources":["../../../src/utils/libraryWidgetKeys.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * libraryWidgetKeys — the one vocabulary of widget keys the LIBRARY itself\n * renders, and the only list `validateManifestV2` is allowed to consult.\n *\n * WHY THIS MODULE EXISTS\n * ----------------------\n * The validator used to carry its own hand-written array of \"library built-in\n * widget keys\", with a comment instructing whoever added a built-in to append\n * it \"in the same PR\". That instruction was followed for five keys and missed\n * for six, so the validator knew 11 keys while the library actually rendered\n * 45. Nothing failed when the two disagreed, because the only consumer is an\n * exemption: a key the validator does not recognise is treated as a CUSTOM\n * registry component, which makes a legitimate single-widget library dashboard\n * fail the ADR-036 Decision 1 anti-pattern rule with advice (\"declare it as\n * type:'custom'\") that is wrong for a library widget.\n *\n * A comment cannot enforce anything, so the array moved here and\n * `tests/utils/libraryWidgetKeys.spec.js` now binds it to the two registries\n * that actually resolve a key at runtime:\n *\n * 1. `BUILT_IN_WIDGETS` (components/CnWidgetGrid/builtInWidgets.js) — the v2\n * manifest widget set `CnWidgetGrid` resolves a `widgetKey` against.\n * 2. The dashboard widget catalog (`dashboardWidgetRegistry`), populated by\n * `registerDashboardWidgets.js`. `CnWidgetGrid` falls back to it (third in\n * the chain, after the consumer registry and `BUILT_IN_WIDGETS`), so a\n * catalog key renders on a v2 page exactly like a `BUILT_IN_WIDGETS` one.\n *\n * The test asserts SET EQUALITY in both directions against the live registries,\n * so adding a widget to either registry without touching this file fails CI,\n * and deleting one without removing it here fails too. The lists below are the\n * validator's view; the registries are the truth the test holds them to.\n *\n * THE CATALOG IS NOT LEGACY. It is a live half of the same vocabulary, and the\n * old array already conceded the point by exempting `chart` and `stats-block`,\n * which are catalog-only keys that `BUILT_IN_WIDGETS` has never held. What it\n * got wrong was doing that for two keys and not the other thirty-seven.\n * `widgetDispatch.js` unified the two resolution paths for the same reason: a\n * widget authored against one vocabulary was invisible to the other and said\n * nothing about why (101 of 236 detail widgets on hrmq).\n *\n * @module utils/libraryWidgetKeys\n */\n\nimport { WIDGET_TYPE_ALIASES } from './widgetTypeAliases.js'\n\n/**\n * Keys of `BUILT_IN_WIDGETS` — the v2 manifest widget set.\n *\n * Kept in the registry's own declaration order so a diff against\n * `builtInWidgets.js` reads straight down.\n *\n * @type {string[]} Frozen.\n */\nexport const BUILT_IN_WIDGET_KEYS = Object.freeze([\n\t'object-table',\n\t'form-renderer',\n\t'map-viewer',\n\t'object-geo',\n\t'card-grid',\n\t'nav-card-grid',\n\t'data',\n\t'metadata',\n\t'related',\n\t'integration',\n\t'banner',\n\t'audit-trail',\n\t'timeline',\n\t'header',\n\t'text',\n\t'divider',\n])\n\n/**\n * Widget types the LIBRARY registers into the dashboard catalog at import time\n * (`registerDashboardWidgets.js` and the widget `index.js` modules it pulls in).\n *\n * Deliberately a static list rather than a read of `dashboardWidgetRegistry`:\n * that object is mutable and consumer-extensible, so deriving the exemption\n * from it at call time would let an app register its own full-page component in\n * the catalog and walk straight through the ADR-036 Decision 1 rule the\n * exemption exists to enforce. Only keys the library itself ships are exempt.\n *\n * Sorted, because unlike `BUILT_IN_WIDGETS` these registrations are spread over\n * ~30 files and have no meaningful declaration order.\n *\n * @type {string[]} Frozen.\n */\nexport const DASHBOARD_CATALOG_WIDGET_KEYS = Object.freeze([\n\t'audit-trail',\n\t'banner',\n\t'calendar',\n\t'chart',\n\t'container',\n\t'conversation',\n\t'countdown',\n\t'data',\n\t'delta',\n\t'divider',\n\t'document-review',\n\t'files',\n\t'flow-runs',\n\t'gauge',\n\t'header',\n\t'image',\n\t'interaction-form',\n\t'kb-search',\n\t'label',\n\t'link',\n\t'links',\n\t'map',\n\t'markdown',\n\t'menu',\n\t'nc-widget',\n\t'next-step',\n\t'notepad',\n\t// `news` was here until the widget moved to LaunchPad, which owns the feed\n\t// fetching. An app that renders one now gets it from LaunchPad's registry.\n\t'object-geo',\n\t'object-list',\n\t'object-table',\n\t'people',\n\t'quicklinks',\n\t'related',\n\t'saved-view',\n\t'spend-analytics',\n\t'stacked-bar',\n\t'stages',\n\t'stat',\n\t'stats-block',\n\t'table',\n\t'tabs',\n\t'tasks',\n\t'text',\n\t'tile',\n\t'timeline',\n\t'video',\n\t'week-strip',\n\t'workspace-filter',\n])\n\n/**\n * Expand a key set over `WIDGET_TYPE_ALIASES` so both spellings of an aliased\n * widget are present whenever either one is.\n *\n * `CnWidgetGrid` resolves a key and its canonical form (`object-table` ⇄\n * `table`, `map-viewer` ⇄ `map`), so both spellings render and both must be\n * exempt. Today each registry happens to hold one side of each pair, which is\n * exactly the accident that would silently stop being true when the next alias\n * lands.\n *\n * @param {Set<string>} keys The key set to expand, mutated in place.\n * @return {Set<string>} The same set.\n */\nfunction expandAliases(keys) {\n\tfor (const [alias, canonical] of Object.entries(WIDGET_TYPE_ALIASES)) {\n\t\tif (keys.has(alias) || keys.has(canonical)) {\n\t\t\tkeys.add(alias)\n\t\t\tkeys.add(canonical)\n\t\t}\n\t}\n\treturn keys\n}\n\n/**\n * Every widget key the library renders itself: both registries, plus both\n * spellings of every alias.\n *\n * A `widgetKey` in this list is served by a library `Cn*` SFC and does NOT\n * count as a custom registry component for the single-12×12-widget dashboard\n * rule (ADR-036 Decision 1).\n *\n * @type {string[]} Frozen.\n */\nexport const LIBRARY_WIDGET_KEYS = Object.freeze([\n\t...expandAliases(new Set([\n\t\t...BUILT_IN_WIDGET_KEYS,\n\t\t...DASHBOARD_CATALOG_WIDGET_KEYS,\n\t])),\n])\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACY,MAAC,oBAAoB,GAAG,MAAM,CAAC,MAAM,CAAC;AAClD,CAAC,cAAc;AACf,CAAC,eAAe;AAChB,CAAC,YAAY;AACb,CAAC,YAAY;AACb,CAAC,WAAW;AACZ,CAAC,eAAe;AAChB,CAAC,MAAM;AACP,CAAC,UAAU;AACX,CAAC,SAAS;AACV,CAAC,aAAa;AACd,CAAC,QAAQ;AACT,CAAC,aAAa;AACd,CAAC,UAAU;AACX,CAAC,QAAQ;AACT,CAAC,MAAM;AACP,CAAC,SAAS;AACV,CAAC,EAAC;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACY,MAAC,6BAA6B,GAAG,MAAM,CAAC,MAAM,CAAC;AAC3D,CAAC,aAAa;AACd,CAAC,QAAQ;AACT,CAAC,UAAU;AACX,CAAC,OAAO;AACR,CAAC,WAAW;AACZ,CAAC,cAAc;AACf,CAAC,WAAW;AACZ,CAAC,MAAM;AACP,CAAC,OAAO;AACR,CAAC,SAAS;AACV,CAAC,iBAAiB;AAClB,CAAC,OAAO;AACR,CAAC,WAAW;AACZ,CAAC,OAAO;AACR,CAAC,QAAQ;AACT,CAAC,OAAO;AACR,CAAC,kBAAkB;AACnB,CAAC,WAAW;AACZ,CAAC,OAAO;AACR,CAAC,MAAM;AACP,CAAC,OAAO;AACR,CAAC,KAAK;AACN,CAAC,UAAU;AACX,CAAC,MAAM;AACP,CAAC,WAAW;AACZ,CAAC,WAAW;AACZ,CAAC,SAAS;AACV;AACA;AACA,CAAC,YAAY;AACb,CAAC,aAAa;AACd,CAAC,cAAc;AACf,CAAC,QAAQ;AACT,CAAC,YAAY;AACb,CAAC,SAAS;AACV,CAAC,YAAY;AACb,CAAC,iBAAiB;AAClB,CAAC,aAAa;AACd,CAAC,QAAQ;AACT,CAAC,MAAM;AACP,CAAC,aAAa;AACd,CAAC,OAAO;AACR,CAAC,MAAM;AACP,CAAC,OAAO;AACR,CAAC,MAAM;AACP,CAAC,MAAM;AACP,CAAC,UAAU;AACX,CAAC,OAAO;AACR,CAAC,YAAY;AACb,CAAC,kBAAkB;AACnB,CAAC,EAAC;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,aAAa,CAAC,IAAI,EAAE;AAC7B,CAAC,KAAK,MAAM,CAAC,KAAK,EAAE,SAAS,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC,EAAE;AACvE,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE;AAC9C,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAC;AAClB,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,EAAC;AACtB,GAAG;AACH,EAAE;AACF,CAAC,OAAO,IAAI;AACZ,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACY,MAAC,mBAAmB,GAAG,MAAM,CAAC,MAAM,CAAC;AACjD,CAAC,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC;AAC1B,EAAE,GAAG,oBAAoB;AACzB,EAAE,GAAG,6BAA6B;AAClC,EAAE,CAAC,CAAC;AACJ,CAAC;;;;"}
1
+ {"version":3,"file":"libraryWidgetKeys.js","sources":["../../../src/utils/libraryWidgetKeys.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * libraryWidgetKeys — the one vocabulary of widget keys the LIBRARY itself\n * renders, and the only list `validateManifestV2` is allowed to consult.\n *\n * WHY THIS MODULE EXISTS\n * ----------------------\n * The validator used to carry its own hand-written array of \"library built-in\n * widget keys\", with a comment instructing whoever added a built-in to append\n * it \"in the same PR\". That instruction was followed for five keys and missed\n * for six, so the validator knew 11 keys while the library actually rendered\n * 45. Nothing failed when the two disagreed, because the only consumer is an\n * exemption: a key the validator does not recognise is treated as a CUSTOM\n * registry component, which makes a legitimate single-widget library dashboard\n * fail the ADR-036 Decision 1 anti-pattern rule with advice (\"declare it as\n * type:'custom'\") that is wrong for a library widget.\n *\n * A comment cannot enforce anything, so the array moved here and\n * `tests/utils/libraryWidgetKeys.spec.js` now binds it to the two registries\n * that actually resolve a key at runtime:\n *\n * 1. `BUILT_IN_WIDGETS` (components/CnWidgetGrid/builtInWidgets.js) — the v2\n * manifest widget set `CnWidgetGrid` resolves a `widgetKey` against.\n * 2. The dashboard widget catalog (`dashboardWidgetRegistry`), populated by\n * `registerDashboardWidgets.js`. `CnWidgetGrid` falls back to it (third in\n * the chain, after the consumer registry and `BUILT_IN_WIDGETS`), so a\n * catalog key renders on a v2 page exactly like a `BUILT_IN_WIDGETS` one.\n *\n * The test asserts SET EQUALITY in both directions against the live registries,\n * so adding a widget to either registry without touching this file fails CI,\n * and deleting one without removing it here fails too. The lists below are the\n * validator's view; the registries are the truth the test holds them to.\n *\n * THE CATALOG IS NOT LEGACY. It is a live half of the same vocabulary, and the\n * old array already conceded the point by exempting `chart` and `stats-block`,\n * which are catalog-only keys that `BUILT_IN_WIDGETS` has never held. What it\n * got wrong was doing that for two keys and not the other thirty-seven.\n * `widgetDispatch.js` unified the two resolution paths for the same reason: a\n * widget authored against one vocabulary was invisible to the other and said\n * nothing about why (101 of 236 detail widgets on hrmq).\n *\n * @module utils/libraryWidgetKeys\n */\n\nimport { WIDGET_TYPE_ALIASES } from './widgetTypeAliases.js'\n\n/**\n * Keys of `BUILT_IN_WIDGETS` — the v2 manifest widget set.\n *\n * Kept in the registry's own declaration order so a diff against\n * `builtInWidgets.js` reads straight down.\n *\n * @type {string[]} Frozen.\n */\nexport const BUILT_IN_WIDGET_KEYS = Object.freeze([\n\t'object-table',\n\t'form-renderer',\n\t'map-viewer',\n\t'object-geo',\n\t'card-grid',\n\t'nav-card-grid',\n\t'data',\n\t'metadata',\n\t'related',\n\t'integration',\n\t'banner',\n\t'audit-trail',\n\t'timeline',\n\t'header',\n\t'text',\n\t'divider',\n])\n\n/**\n * Widget types the LIBRARY registers into the dashboard catalog at import time\n * (`registerDashboardWidgets.js` and the widget `index.js` modules it pulls in).\n *\n * Deliberately a static list rather than a read of `dashboardWidgetRegistry`:\n * that object is mutable and consumer-extensible, so deriving the exemption\n * from it at call time would let an app register its own full-page component in\n * the catalog and walk straight through the ADR-036 Decision 1 rule the\n * exemption exists to enforce. Only keys the library itself ships are exempt.\n *\n * Sorted, because unlike `BUILT_IN_WIDGETS` these registrations are spread over\n * ~30 files and have no meaningful declaration order.\n *\n * @type {string[]} Frozen.\n */\nexport const DASHBOARD_CATALOG_WIDGET_KEYS = Object.freeze([\n\t'audit-trail',\n\t'banner',\n\t'calendar',\n\t'chart',\n\t'chips-field',\n\t'choice-picker',\n\t'container',\n\t'conversation',\n\t'countdown',\n\t'data',\n\t'delta',\n\t'divider',\n\t'document-review',\n\t'facts',\n\t'field-list',\n\t'files',\n\t'flow-runs',\n\t'gauge',\n\t'header',\n\t'image',\n\t'interaction-form',\n\t'kb-search',\n\t'label',\n\t'link',\n\t'links',\n\t'map',\n\t'markdown',\n\t'menu',\n\t'nc-widget',\n\t'next-step',\n\t'notepad',\n\t// `news` was here until the widget moved to LaunchPad, which owns the feed\n\t// fetching. An app that renders one now gets it from LaunchPad's registry.\n\t'object-geo',\n\t'object-list',\n\t'object-table',\n\t'people',\n\t'quicklinks',\n\t'radio-cards',\n\t'record-list',\n\t'related',\n\t'saved-view',\n\t'saved-views',\n\t'sections',\n\t'settings-form',\n\t'spend-analytics',\n\t'stacked-bar',\n\t'stages',\n\t'stat',\n\t'stats-block',\n\t'switch-rows',\n\t'table',\n\t'tabs',\n\t'tasks',\n\t'text',\n\t'tile',\n\t'timeline',\n\t'toggle-table',\n\t'video',\n\t'week-strip',\n\t'workspace-filter',\n])\n\n/**\n * Expand a key set over `WIDGET_TYPE_ALIASES` so both spellings of an aliased\n * widget are present whenever either one is.\n *\n * `CnWidgetGrid` resolves a key and its canonical form (`object-table` ⇄\n * `table`, `map-viewer` ⇄ `map`), so both spellings render and both must be\n * exempt. Today each registry happens to hold one side of each pair, which is\n * exactly the accident that would silently stop being true when the next alias\n * lands.\n *\n * @param {Set<string>} keys The key set to expand, mutated in place.\n * @return {Set<string>} The same set.\n */\nfunction expandAliases(keys) {\n\tfor (const [alias, canonical] of Object.entries(WIDGET_TYPE_ALIASES)) {\n\t\tif (keys.has(alias) || keys.has(canonical)) {\n\t\t\tkeys.add(alias)\n\t\t\tkeys.add(canonical)\n\t\t}\n\t}\n\treturn keys\n}\n\n/**\n * Every widget key the library renders itself: both registries, plus both\n * spellings of every alias.\n *\n * A `widgetKey` in this list is served by a library `Cn*` SFC and does NOT\n * count as a custom registry component for the single-12×12-widget dashboard\n * rule (ADR-036 Decision 1).\n *\n * @type {string[]} Frozen.\n */\nexport const LIBRARY_WIDGET_KEYS = Object.freeze([\n\t...expandAliases(new Set([\n\t\t...BUILT_IN_WIDGET_KEYS,\n\t\t...DASHBOARD_CATALOG_WIDGET_KEYS,\n\t])),\n])\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACY,MAAC,oBAAoB,GAAG,MAAM,CAAC,MAAM,CAAC;AAClD,CAAC,cAAc;AACf,CAAC,eAAe;AAChB,CAAC,YAAY;AACb,CAAC,YAAY;AACb,CAAC,WAAW;AACZ,CAAC,eAAe;AAChB,CAAC,MAAM;AACP,CAAC,UAAU;AACX,CAAC,SAAS;AACV,CAAC,aAAa;AACd,CAAC,QAAQ;AACT,CAAC,aAAa;AACd,CAAC,UAAU;AACX,CAAC,QAAQ;AACT,CAAC,MAAM;AACP,CAAC,SAAS;AACV,CAAC,EAAC;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACY,MAAC,6BAA6B,GAAG,MAAM,CAAC,MAAM,CAAC;AAC3D,CAAC,aAAa;AACd,CAAC,QAAQ;AACT,CAAC,UAAU;AACX,CAAC,OAAO;AACR,CAAC,aAAa;AACd,CAAC,eAAe;AAChB,CAAC,WAAW;AACZ,CAAC,cAAc;AACf,CAAC,WAAW;AACZ,CAAC,MAAM;AACP,CAAC,OAAO;AACR,CAAC,SAAS;AACV,CAAC,iBAAiB;AAClB,CAAC,OAAO;AACR,CAAC,YAAY;AACb,CAAC,OAAO;AACR,CAAC,WAAW;AACZ,CAAC,OAAO;AACR,CAAC,QAAQ;AACT,CAAC,OAAO;AACR,CAAC,kBAAkB;AACnB,CAAC,WAAW;AACZ,CAAC,OAAO;AACR,CAAC,MAAM;AACP,CAAC,OAAO;AACR,CAAC,KAAK;AACN,CAAC,UAAU;AACX,CAAC,MAAM;AACP,CAAC,WAAW;AACZ,CAAC,WAAW;AACZ,CAAC,SAAS;AACV;AACA;AACA,CAAC,YAAY;AACb,CAAC,aAAa;AACd,CAAC,cAAc;AACf,CAAC,QAAQ;AACT,CAAC,YAAY;AACb,CAAC,aAAa;AACd,CAAC,aAAa;AACd,CAAC,SAAS;AACV,CAAC,YAAY;AACb,CAAC,aAAa;AACd,CAAC,UAAU;AACX,CAAC,eAAe;AAChB,CAAC,iBAAiB;AAClB,CAAC,aAAa;AACd,CAAC,QAAQ;AACT,CAAC,MAAM;AACP,CAAC,aAAa;AACd,CAAC,aAAa;AACd,CAAC,OAAO;AACR,CAAC,MAAM;AACP,CAAC,OAAO;AACR,CAAC,MAAM;AACP,CAAC,MAAM;AACP,CAAC,UAAU;AACX,CAAC,cAAc;AACf,CAAC,OAAO;AACR,CAAC,YAAY;AACb,CAAC,kBAAkB;AACnB,CAAC,EAAC;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,aAAa,CAAC,IAAI,EAAE;AAC7B,CAAC,KAAK,MAAM,CAAC,KAAK,EAAE,SAAS,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,mBAAmB,CAAC,EAAE;AACvE,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE;AAC9C,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAC;AAClB,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,EAAC;AACtB,GAAG;AACH,EAAE;AACF,CAAC,OAAO,IAAI;AACZ,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACY,MAAC,mBAAmB,GAAG,MAAM,CAAC,MAAM,CAAC;AACjD,CAAC,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC;AAC1B,EAAE,GAAG,oBAAoB;AACzB,EAAE,GAAG,6BAA6B;AAClC,EAAE,CAAC,CAAC;AACJ,CAAC;;;;"}
@@ -0,0 +1,59 @@
1
+ import { prefixUrl } from './headers.js';
2
+
3
+ /**
4
+ * SPDX-FileCopyrightText: 2026 Conduction B.V.
5
+ * SPDX-License-Identifier: EUPL-1.2
6
+ *
7
+ * objectWrite: the one-object PATCH the settings widgets share.
8
+ *
9
+ * @module utils/objectWrite
10
+ */
11
+
12
+
13
+ /**
14
+ * The OpenRegister URL of one object, or '' when register, schema or id is missing.
15
+ *
16
+ * @param {string|object} register Register slug.
17
+ * @param {string|object} schema Schema slug.
18
+ * @param {string|number} id Object id.
19
+ * @return {string} The object URL.
20
+ */
21
+ function objectUrlOf(register, schema, id) {
22
+ if (typeof register !== 'string' || typeof schema !== 'string' || !register || !schema || id === undefined || id === null || id === '') {
23
+ return ''
24
+ }
25
+ return `/apps/openregister/api/objects/${encodeURIComponent(register)}/${encodeURIComponent(schema)}/${encodeURIComponent(String(id))}`
26
+ }
27
+
28
+ /**
29
+ * PATCH only the given fields of one object.
30
+ *
31
+ * @param {string} url The object URL (see objectUrlOf).
32
+ * @param {object} patch The fields to write.
33
+ * @return {Promise<object>} The object the server answered with.
34
+ */
35
+ async function patchObject(url, patch) {
36
+ const { default: axios } = await import('@nextcloud/axios');
37
+ const { data } = await axios.patch(prefixUrl(url), patch);
38
+ return data && typeof data === 'object' ? data : {}
39
+ }
40
+
41
+ /**
42
+ * The fields of `next` that differ from `base`, compared by value.
43
+ *
44
+ * @param {object} base What the server last held.
45
+ * @param {object} next What the form holds.
46
+ * @return {object} The changed fields only.
47
+ */
48
+ function changedFields(base, next) {
49
+ const patch = {};
50
+ for (const [key, value] of Object.entries(next)) {
51
+ if (JSON.stringify(value === undefined ? null : value) !== JSON.stringify(base[key] === undefined ? null : base[key])) {
52
+ patch[key] = value;
53
+ }
54
+ }
55
+ return patch
56
+ }
57
+
58
+ export { changedFields, objectUrlOf, patchObject };
59
+ //# sourceMappingURL=objectWrite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"objectWrite.js","sources":["../../../src/utils/objectWrite.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * objectWrite: the one-object PATCH the settings widgets share.\n *\n * @module utils/objectWrite\n */\n\nimport { prefixUrl } from './headers.js'\n\n/**\n * The OpenRegister URL of one object, or '' when register, schema or id is missing.\n *\n * @param {string|object} register Register slug.\n * @param {string|object} schema Schema slug.\n * @param {string|number} id Object id.\n * @return {string} The object URL.\n */\nexport function objectUrlOf(register, schema, id) {\n\tif (typeof register !== 'string' || typeof schema !== 'string' || !register || !schema || id === undefined || id === null || id === '') {\n\t\treturn ''\n\t}\n\treturn `/apps/openregister/api/objects/${encodeURIComponent(register)}/${encodeURIComponent(schema)}/${encodeURIComponent(String(id))}`\n}\n\n/**\n * PATCH only the given fields of one object.\n *\n * @param {string} url The object URL (see objectUrlOf).\n * @param {object} patch The fields to write.\n * @return {Promise<object>} The object the server answered with.\n */\nexport async function patchObject(url, patch) {\n\tconst { default: axios } = await import('@nextcloud/axios')\n\tconst { data } = await axios.patch(prefixUrl(url), patch)\n\treturn data && typeof data === 'object' ? data : {}\n}\n\n/**\n * The fields of `next` that differ from `base`, compared by value.\n *\n * @param {object} base What the server last held.\n * @param {object} next What the form holds.\n * @return {object} The changed fields only.\n */\nexport function changedFields(base, next) {\n\tconst patch = {}\n\tfor (const [key, value] of Object.entries(next)) {\n\t\tif (JSON.stringify(value === undefined ? null : value) !== JSON.stringify(base[key] === undefined ? null : base[key])) {\n\t\t\tpatch[key] = value\n\t\t}\n\t}\n\treturn patch\n}\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE;AAClD,CAAC,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,QAAQ,IAAI,CAAC,MAAM,IAAI,EAAE,KAAK,SAAS,IAAI,EAAE,KAAK,IAAI,IAAI,EAAE,KAAK,EAAE,EAAE;AACzI,EAAE,OAAO,EAAE;AACX,EAAE;AACF,CAAC,OAAO,CAAC,+BAA+B,EAAE,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AACxI,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,eAAe,WAAW,CAAC,GAAG,EAAE,KAAK,EAAE;AAC9C,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,OAAO,kBAAkB,EAAC;AAC5D,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,KAAK,EAAC;AAC1D,CAAC,OAAO,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,GAAG,IAAI,GAAG,EAAE;AACpD,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE;AAC1C,CAAC,MAAM,KAAK,GAAG,GAAE;AACjB,CAAC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;AAClD,EAAE,IAAI,IAAI,CAAC,SAAS,CAAC,KAAK,KAAK,SAAS,GAAG,IAAI,GAAG,KAAK,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,SAAS,GAAG,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE;AACzH,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,MAAK;AACrB,GAAG;AACH,EAAE;AACF,CAAC,OAAO,KAAK;AACb;;;;"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * SPDX-FileCopyrightText: 2026 Conduction B.V.
3
+ * SPDX-License-Identifier: EUPL-1.2
4
+ *
5
+ * pageControls: how a list widget listens to the page's controls.
6
+ *
7
+ * A page control (a `workspace-filter` select or switch) writes a value to the
8
+ * page workspace. A list widget declares which control it listens to and what
9
+ * the value means for it:
10
+ *
11
+ * pageControls: {
12
+ * sort: { key: "sort", map: { deadline: { field: "slaDeadline", dir: "asc" } } },
13
+ * toggle: { key: "showDone", removes: ["status"] }
14
+ * }
15
+ *
16
+ * `sort`: the workspace value at `key` picks an entry of `map` (the sort the
17
+ * list uses); a value with no entry leaves the list's own sort.
18
+ * `toggle`: while the workspace value at `key` is on (true, "1", "true"), the
19
+ * filter keys in `removes` are dropped, so a list that hides finished work
20
+ * shows it.
21
+ *
22
+ * @module utils/pageControls
23
+ */
24
+
25
+ /**
26
+ * Whether a workspace value reads as on.
27
+ *
28
+ * @param {unknown} value The value.
29
+ * @return {boolean} True for true, 1, "1" and "true".
30
+ */
31
+ function isOn(value) {
32
+ return value === true || value === 1 || value === '1' || value === 'true'
33
+ }
34
+
35
+ /**
36
+ * The sort a list uses once the page's sort control is read.
37
+ *
38
+ * @param {object|null} pageControls The widget's `pageControls`.
39
+ * @param {object} workspace The plain workspace bag.
40
+ * @param {{field: string, dir?: string}|null} own The list's own sort.
41
+ * @return {{field: string, dir?: string}|null} The sort to use.
42
+ * @spec openspec/changes/screens-page-controls/specs/page-controls/spec.md#requirement-a-list-listens-to-the-pages-controls
43
+ */
44
+ function pageSort(pageControls, workspace, own) {
45
+ const listen = pageControls && typeof pageControls === 'object' ? pageControls.sort : null;
46
+ if (!listen || typeof listen.key !== 'string' || !listen.map || typeof listen.map !== 'object') {
47
+ return own
48
+ }
49
+ const chosen = workspace ? workspace[listen.key] : undefined;
50
+ const entry = chosen !== undefined && chosen !== null ? listen.map[String(chosen)] : null;
51
+ return entry && typeof entry === 'object' && typeof entry.field === 'string' && entry.field !== '' ? entry : own
52
+ }
53
+
54
+ /**
55
+ * The filter a list uses once the page's toggle is read: with the toggle on,
56
+ * the keys it `removes` are gone.
57
+ *
58
+ * @param {object|null} pageControls The widget's `pageControls`.
59
+ * @param {object} workspace The plain workspace bag.
60
+ * @param {object} filter The list's own filter (before token resolution).
61
+ * @return {object} The filter to use (the same object when nothing is removed).
62
+ * @spec openspec/changes/screens-page-controls/specs/page-controls/spec.md#requirement-a-list-listens-to-the-pages-controls
63
+ */
64
+ function pageFilter(pageControls, workspace, filter) {
65
+ const listen = pageControls && typeof pageControls === 'object' ? pageControls.toggle : null;
66
+ const base = filter && typeof filter === 'object' ? filter : {};
67
+ if (!listen || typeof listen.key !== 'string' || !Array.isArray(listen.removes)) {
68
+ return base
69
+ }
70
+ if (!isOn(workspace ? workspace[listen.key] : undefined)) {
71
+ return base
72
+ }
73
+ const out = { ...base };
74
+ for (const key of listen.removes) {
75
+ delete out[key];
76
+ }
77
+ return out
78
+ }
79
+
80
+ export { isOn, pageFilter, pageSort };
81
+ //# sourceMappingURL=pageControls.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pageControls.js","sources":["../../../src/utils/pageControls.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * pageControls: how a list widget listens to the page's controls.\n *\n * A page control (a `workspace-filter` select or switch) writes a value to the\n * page workspace. A list widget declares which control it listens to and what\n * the value means for it:\n *\n * pageControls: {\n * sort: { key: \"sort\", map: { deadline: { field: \"slaDeadline\", dir: \"asc\" } } },\n * toggle: { key: \"showDone\", removes: [\"status\"] }\n * }\n *\n * `sort`: the workspace value at `key` picks an entry of `map` (the sort the\n * list uses); a value with no entry leaves the list's own sort.\n * `toggle`: while the workspace value at `key` is on (true, \"1\", \"true\"), the\n * filter keys in `removes` are dropped, so a list that hides finished work\n * shows it.\n *\n * @module utils/pageControls\n */\n\n/**\n * Whether a workspace value reads as on.\n *\n * @param {unknown} value The value.\n * @return {boolean} True for true, 1, \"1\" and \"true\".\n */\nexport function isOn(value) {\n\treturn value === true || value === 1 || value === '1' || value === 'true'\n}\n\n/**\n * The sort a list uses once the page's sort control is read.\n *\n * @param {object|null} pageControls The widget's `pageControls`.\n * @param {object} workspace The plain workspace bag.\n * @param {{field: string, dir?: string}|null} own The list's own sort.\n * @return {{field: string, dir?: string}|null} The sort to use.\n * @spec openspec/changes/screens-page-controls/specs/page-controls/spec.md#requirement-a-list-listens-to-the-pages-controls\n */\nexport function pageSort(pageControls, workspace, own) {\n\tconst listen = pageControls && typeof pageControls === 'object' ? pageControls.sort : null\n\tif (!listen || typeof listen.key !== 'string' || !listen.map || typeof listen.map !== 'object') {\n\t\treturn own\n\t}\n\tconst chosen = workspace ? workspace[listen.key] : undefined\n\tconst entry = chosen !== undefined && chosen !== null ? listen.map[String(chosen)] : null\n\treturn entry && typeof entry === 'object' && typeof entry.field === 'string' && entry.field !== '' ? entry : own\n}\n\n/**\n * The filter a list uses once the page's toggle is read: with the toggle on,\n * the keys it `removes` are gone.\n *\n * @param {object|null} pageControls The widget's `pageControls`.\n * @param {object} workspace The plain workspace bag.\n * @param {object} filter The list's own filter (before token resolution).\n * @return {object} The filter to use (the same object when nothing is removed).\n * @spec openspec/changes/screens-page-controls/specs/page-controls/spec.md#requirement-a-list-listens-to-the-pages-controls\n */\nexport function pageFilter(pageControls, workspace, filter) {\n\tconst listen = pageControls && typeof pageControls === 'object' ? pageControls.toggle : null\n\tconst base = filter && typeof filter === 'object' ? filter : {}\n\tif (!listen || typeof listen.key !== 'string' || !Array.isArray(listen.removes)) {\n\t\treturn base\n\t}\n\tif (!isOn(workspace ? workspace[listen.key] : undefined)) {\n\t\treturn base\n\t}\n\tconst out = { ...base }\n\tfor (const key of listen.removes) {\n\t\tdelete out[key]\n\t}\n\treturn out\n}\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,IAAI,CAAC,KAAK,EAAE;AAC5B,CAAC,OAAO,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,CAAC,IAAI,KAAK,KAAK,GAAG,IAAI,KAAK,KAAK,MAAM;AAC1E,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,QAAQ,CAAC,YAAY,EAAE,SAAS,EAAE,GAAG,EAAE;AACvD,CAAC,MAAM,MAAM,GAAG,YAAY,IAAI,OAAO,YAAY,KAAK,QAAQ,GAAG,YAAY,CAAC,IAAI,GAAG,KAAI;AAC3F,CAAC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,EAAE;AACjG,EAAE,OAAO,GAAG;AACZ,EAAE;AACF,CAAC,MAAM,MAAM,GAAG,SAAS,GAAG,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,UAAS;AAC7D,CAAC,MAAM,KAAK,GAAG,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,KAAI;AAC1F,CAAC,OAAO,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,KAAK,KAAK,EAAE,GAAG,KAAK,GAAG,GAAG;AACjH,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,UAAU,CAAC,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE;AAC5D,CAAC,MAAM,MAAM,GAAG,YAAY,IAAI,OAAO,YAAY,KAAK,QAAQ,GAAG,YAAY,CAAC,MAAM,GAAG,KAAI;AAC7F,CAAC,MAAM,IAAI,GAAG,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,GAAG,MAAM,GAAG,GAAE;AAChE,CAAC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE;AAClF,EAAE,OAAO,IAAI;AACb,EAAE;AACF,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,EAAE;AAC3D,EAAE,OAAO,IAAI;AACb,EAAE;AACF,CAAC,MAAM,GAAG,GAAG,EAAE,GAAG,IAAI,GAAE;AACxB,CAAC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,OAAO,EAAE;AACnC,EAAE,OAAO,GAAG,CAAC,GAAG,EAAC;AACjB,EAAE;AACF,CAAC,OAAO,GAAG;AACX;;;;"}
@@ -38,6 +38,42 @@ function personalLensTabs(lenses) {
38
38
  .map((id) => ({ label: labels[id], filter: { [LENS_FILTER_KEYS[id]]: true }, lens: id }))
39
39
  }
40
40
 
41
+ /**
42
+ * Turn the `empty` / `notEmpty` shorthand of quick filters into a filter.
43
+ *
44
+ * `{ label: "Not logged in yet", empty: "lastLoginAt" }` is the rows where the
45
+ * field has no value: the filter `{ lastLoginAt: { isnull: true } }`, which
46
+ * OpenRegister reads as `lastLoginAt[isnull]=true`. `notEmpty` is the rows
47
+ * where it has one. Either takes a field or a list of fields and merges with
48
+ * the entry's own `filter`. Returns the same array when no entry uses it.
49
+ *
50
+ * @param {Array<object>|null} quickFilters The page's quick filters.
51
+ * @return {Array<object>|null} The quick filters with the shorthand expanded.
52
+ */
53
+ function expandEmptyFilters(quickFilters) {
54
+ if (!Array.isArray(quickFilters)) {
55
+ return quickFilters
56
+ }
57
+ const uses = (tab) => tab && typeof tab === 'object' && (tab.empty || tab.notEmpty);
58
+ if (!quickFilters.some(uses)) {
59
+ return quickFilters
60
+ }
61
+ const fieldsOf = (value) => (Array.isArray(value) ? value : [value]).filter((f) => typeof f === 'string' && f !== '');
62
+ return quickFilters.map((tab) => {
63
+ if (!uses(tab)) {
64
+ return tab
65
+ }
66
+ const filter = { ...(tab.filter && typeof tab.filter === 'object' ? tab.filter : {}) };
67
+ for (const field of fieldsOf(tab.empty)) {
68
+ filter[field] = { isnull: true };
69
+ }
70
+ for (const field of fieldsOf(tab.notEmpty)) {
71
+ filter[field] = { isnull: false };
72
+ }
73
+ return { ...tab, filter }
74
+ })
75
+ }
76
+
41
77
  /**
42
78
  * The page's quick filters with its personal lenses appended. A page with no
43
79
  * quick filters of its own gets an "All" tab first, so a lens is not active
@@ -49,14 +85,15 @@ function personalLensTabs(lenses) {
49
85
  */
50
86
  function withPersonalLenses(quickFilters, lenses) {
51
87
  const tabs = personalLensTabs(lenses);
88
+ const expanded = expandEmptyFilters(quickFilters);
52
89
  if (tabs.length === 0) {
53
- return quickFilters
90
+ return expanded
54
91
  }
55
- const own = Array.isArray(quickFilters) ? quickFilters : [];
92
+ const own = Array.isArray(expanded) ? expanded : [];
56
93
  return own.length > 0
57
94
  ? [...own, ...tabs]
58
95
  : [{ label: translate('nextcloud-vue', 'All'), filter: {}, default: true }, ...tabs]
59
96
  }
60
97
 
61
- export { LENS_FILTER_KEYS, personalLensTabs, withPersonalLenses };
98
+ export { LENS_FILTER_KEYS, expandEmptyFilters, personalLensTabs, withPersonalLenses };
62
99
  //# sourceMappingURL=personalLenses.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"personalLenses.js","sources":["../../../src/utils/personalLenses.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * The personal lenses of an index page (Following, Recent, Unread) as quick\n * filters, appended after the page's own.\n *\n * There is no Favourites lens any more: a favourite is a follow with\n * notifications off (OpenRegister `merge-follow-and-favourites`), so\n * `favourite` is an alias of `watching` and asking for both gives one tab.\n *\n * @spec openspec/changes/one-follow-control/specs/record-follow/spec.md#requirement-index-pages-offer-one-following-lens-and-a-follow-column\n */\nimport { translate as t } from '@nextcloud/l10n'\n\n/** Lens id to the query key OpenRegister reads. */\nexport const LENS_FILTER_KEYS = { recent: '_recent', watching: '_watching', unread: '_unread' }\n\n/** Deprecated lens ids and the lens they now mean. */\nconst LENS_ALIASES = { favourite: 'watching' }\n\n/**\n * Quick filters for the requested lenses. Unknown ids are ignored.\n *\n * @param {string[]} lenses Any of `watching`, `recent`, `unread`, and the deprecated `favourite` (read as `watching`).\n * @return {Array<{label: string, filter: object, lens: string}>} One tab per lens, in the order asked.\n */\nexport function personalLensTabs(lenses) {\n\tconst labels = {\n\t\trecent: t('nextcloud-vue', 'Recent'),\n\t\twatching: t('nextcloud-vue', 'Following'),\n\t\tunread: t('nextcloud-vue', 'Unread'),\n\t}\n\treturn (Array.isArray(lenses) ? lenses : [])\n\t\t.map((id) => (Object.hasOwn(LENS_ALIASES, id) ? LENS_ALIASES[id] : id))\n\t\t.filter((id, i, all) => Object.hasOwn(LENS_FILTER_KEYS, id) && all.indexOf(id) === i)\n\t\t.map((id) => ({ label: labels[id], filter: { [LENS_FILTER_KEYS[id]]: true }, lens: id }))\n}\n\n/**\n * The page's quick filters with its personal lenses appended. A page with no\n * quick filters of its own gets an \"All\" tab first, so a lens is not active\n * before the person asks for it.\n *\n * @param {Array<object>|null} quickFilters The page's own quick filters.\n * @param {string[]} lenses The requested lenses.\n * @return {Array<object>|null} The combined list, or the page's own list unchanged when no lens is asked for.\n */\nexport function withPersonalLenses(quickFilters, lenses) {\n\tconst tabs = personalLensTabs(lenses)\n\tif (tabs.length === 0) {\n\t\treturn quickFilters\n\t}\n\tconst own = Array.isArray(quickFilters) ? quickFilters : []\n\treturn own.length > 0\n\t\t? [...own, ...tabs]\n\t\t: [{ label: t('nextcloud-vue', 'All'), filter: {}, default: true }, ...tabs]\n}\n"],"names":["t"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACY,MAAC,gBAAgB,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,GAAE;AAC/F;AACA;AACA,MAAM,YAAY,GAAG,EAAE,SAAS,EAAE,UAAU,GAAE;AAC9C;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,gBAAgB,CAAC,MAAM,EAAE;AACzC,CAAC,MAAM,MAAM,GAAG;AAChB,EAAE,MAAM,EAAEA,SAAC,CAAC,eAAe,EAAE,QAAQ,CAAC;AACtC,EAAE,QAAQ,EAAEA,SAAC,CAAC,eAAe,EAAE,WAAW,CAAC;AAC3C,EAAE,MAAM,EAAEA,SAAC,CAAC,eAAe,EAAE,QAAQ,CAAC;AACtC,GAAE;AACF,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,GAAG,EAAE;AAC5C,GAAG,GAAG,CAAC,CAAC,EAAE,MAAM,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,EAAE,CAAC,GAAG,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC;AACzE,GAAG,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,GAAG,KAAK,MAAM,CAAC,MAAM,CAAC,gBAAgB,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;AACvF,GAAG,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,gBAAgB,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;AAC3F,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,kBAAkB,CAAC,YAAY,EAAE,MAAM,EAAE;AACzD,CAAC,MAAM,IAAI,GAAG,gBAAgB,CAAC,MAAM,EAAC;AACtC,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE;AACxB,EAAE,OAAO,YAAY;AACrB,EAAE;AACF,CAAC,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,GAAG,YAAY,GAAG,GAAE;AAC5D,CAAC,OAAO,GAAG,CAAC,MAAM,GAAG,CAAC;AACtB,IAAI,CAAC,GAAG,GAAG,EAAE,GAAG,IAAI,CAAC;AACrB,IAAI,CAAC,EAAE,KAAK,EAAEA,SAAC,CAAC,eAAe,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,GAAG,IAAI,CAAC;AAC9E;;;;"}
1
+ {"version":3,"file":"personalLenses.js","sources":["../../../src/utils/personalLenses.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * The personal lenses of an index page (Following, Recent, Unread) as quick\n * filters, appended after the page's own.\n *\n * There is no Favourites lens any more: a favourite is a follow with\n * notifications off (OpenRegister `merge-follow-and-favourites`), so\n * `favourite` is an alias of `watching` and asking for both gives one tab.\n *\n * @spec openspec/changes/one-follow-control/specs/record-follow/spec.md#requirement-index-pages-offer-one-following-lens-and-a-follow-column\n */\nimport { translate as t } from '@nextcloud/l10n'\n\n/** Lens id to the query key OpenRegister reads. */\nexport const LENS_FILTER_KEYS = { recent: '_recent', watching: '_watching', unread: '_unread' }\n\n/** Deprecated lens ids and the lens they now mean. */\nconst LENS_ALIASES = { favourite: 'watching' }\n\n/**\n * Quick filters for the requested lenses. Unknown ids are ignored.\n *\n * @param {string[]} lenses Any of `watching`, `recent`, `unread`, and the deprecated `favourite` (read as `watching`).\n * @return {Array<{label: string, filter: object, lens: string}>} One tab per lens, in the order asked.\n */\nexport function personalLensTabs(lenses) {\n\tconst labels = {\n\t\trecent: t('nextcloud-vue', 'Recent'),\n\t\twatching: t('nextcloud-vue', 'Following'),\n\t\tunread: t('nextcloud-vue', 'Unread'),\n\t}\n\treturn (Array.isArray(lenses) ? lenses : [])\n\t\t.map((id) => (Object.hasOwn(LENS_ALIASES, id) ? LENS_ALIASES[id] : id))\n\t\t.filter((id, i, all) => Object.hasOwn(LENS_FILTER_KEYS, id) && all.indexOf(id) === i)\n\t\t.map((id) => ({ label: labels[id], filter: { [LENS_FILTER_KEYS[id]]: true }, lens: id }))\n}\n\n/**\n * Turn the `empty` / `notEmpty` shorthand of quick filters into a filter.\n *\n * `{ label: \"Not logged in yet\", empty: \"lastLoginAt\" }` is the rows where the\n * field has no value: the filter `{ lastLoginAt: { isnull: true } }`, which\n * OpenRegister reads as `lastLoginAt[isnull]=true`. `notEmpty` is the rows\n * where it has one. Either takes a field or a list of fields and merges with\n * the entry's own `filter`. Returns the same array when no entry uses it.\n *\n * @param {Array<object>|null} quickFilters The page's quick filters.\n * @return {Array<object>|null} The quick filters with the shorthand expanded.\n */\nexport function expandEmptyFilters(quickFilters) {\n\tif (!Array.isArray(quickFilters)) {\n\t\treturn quickFilters\n\t}\n\tconst uses = (tab) => tab && typeof tab === 'object' && (tab.empty || tab.notEmpty)\n\tif (!quickFilters.some(uses)) {\n\t\treturn quickFilters\n\t}\n\tconst fieldsOf = (value) => (Array.isArray(value) ? value : [value]).filter((f) => typeof f === 'string' && f !== '')\n\treturn quickFilters.map((tab) => {\n\t\tif (!uses(tab)) {\n\t\t\treturn tab\n\t\t}\n\t\tconst filter = { ...(tab.filter && typeof tab.filter === 'object' ? tab.filter : {}) }\n\t\tfor (const field of fieldsOf(tab.empty)) {\n\t\t\tfilter[field] = { isnull: true }\n\t\t}\n\t\tfor (const field of fieldsOf(tab.notEmpty)) {\n\t\t\tfilter[field] = { isnull: false }\n\t\t}\n\t\treturn { ...tab, filter }\n\t})\n}\n\n/**\n * The page's quick filters with its personal lenses appended. A page with no\n * quick filters of its own gets an \"All\" tab first, so a lens is not active\n * before the person asks for it.\n *\n * @param {Array<object>|null} quickFilters The page's own quick filters.\n * @param {string[]} lenses The requested lenses.\n * @return {Array<object>|null} The combined list, or the page's own list unchanged when no lens is asked for.\n */\nexport function withPersonalLenses(quickFilters, lenses) {\n\tconst tabs = personalLensTabs(lenses)\n\tconst expanded = expandEmptyFilters(quickFilters)\n\tif (tabs.length === 0) {\n\t\treturn expanded\n\t}\n\tconst own = Array.isArray(expanded) ? expanded : []\n\treturn own.length > 0\n\t\t? [...own, ...tabs]\n\t\t: [{ label: t('nextcloud-vue', 'All'), filter: {}, default: true }, ...tabs]\n}\n"],"names":["t"],"mappings":"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"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * SPDX-FileCopyrightText: 2026 Conduction B.V.
3
+ * SPDX-License-Identifier: EUPL-1.2
4
+ *
5
+ * Query presets of an index page (`config.queryPresets`): a menu entry that
6
+ * deep-links to a list with a query (`{ caseType: '<uuid>' }`, the shape hydra
7
+ * gate-68 asks for instead of a second page) can bring its own lenses,
8
+ * columns and copy. The page names, per preset, the query it answers to and
9
+ * the keys it replaces; the first preset whose every `match` pair is in the
10
+ * current route query wins.
11
+ *
12
+ * @module utils/queryPresets
13
+ * @spec openspec/changes/screens-index-query-presets/specs/index-query-presets/spec.md#requirement-a-query-preset-overlays-lenses-columns-and-copy
14
+ */
15
+
16
+ /**
17
+ * The config keys a preset may replace. Anything else in a preset is ignored:
18
+ * a preset changes what the list shows, never which register or schema it
19
+ * reads or what it may write.
20
+ *
21
+ * @type {ReadonlyArray<string>}
22
+ */
23
+ const QUERY_PRESET_KEYS = Object.freeze([
24
+ 'title',
25
+ 'quickFilters',
26
+ 'columns',
27
+ 'cardFields',
28
+ 'countText',
29
+ 'searchPlaceholder',
30
+ 'footerNote',
31
+ 'defaultSort',
32
+ 'viewSwitch',
33
+ ]);
34
+
35
+ /**
36
+ * Whether one route query value satisfies one `match` value. A repeated query
37
+ * parameter (an array) matches when it holds the value.
38
+ *
39
+ * @param {unknown} actual The route query value.
40
+ * @param {string|number|boolean} expected The preset's value.
41
+ * @return {boolean} True when they match.
42
+ */
43
+ function queryValueMatches(actual, expected) {
44
+ const want = String(expected);
45
+ if (Array.isArray(actual)) {
46
+ return actual.some((v) => String(v) === want)
47
+ }
48
+ return actual !== undefined && actual !== null && String(actual) === want
49
+ }
50
+
51
+ /**
52
+ * The index of the first preset whose every `match` pair is in the query.
53
+ * A preset without a non-empty `match` never matches.
54
+ *
55
+ * @param {Array<object>} presets The page's `queryPresets`.
56
+ * @param {object} query The current route query.
57
+ * @return {number} The preset index, or -1.
58
+ */
59
+ function matchQueryPreset(presets, query) {
60
+ if (!Array.isArray(presets)) {
61
+ return -1
62
+ }
63
+ const q = query && typeof query === 'object' ? query : {};
64
+ return presets.findIndex((preset) => {
65
+ const match = preset && typeof preset.match === 'object' && preset.match !== null ? preset.match : null;
66
+ if (!match || Object.keys(match).length === 0) {
67
+ return false
68
+ }
69
+ return Object.entries(match).every(([key, value]) => queryValueMatches(q[key], value))
70
+ })
71
+ }
72
+
73
+ /**
74
+ * The page config with the preset's allowed keys laid over it, and without
75
+ * `queryPresets` itself (the page component has no such prop).
76
+ *
77
+ * @param {object} config The page config.
78
+ * @param {object|null} preset The matched preset, or null.
79
+ * @return {object} The effective config.
80
+ */
81
+ function applyQueryPreset(config, preset) {
82
+ const { queryPresets, ...rest } = config || {};
83
+ if (!preset || typeof preset !== 'object') {
84
+ return rest
85
+ }
86
+ const out = { ...rest };
87
+ for (const key of QUERY_PRESET_KEYS) {
88
+ if (key !== 'title' && preset[key] !== undefined) {
89
+ out[key] = preset[key];
90
+ }
91
+ }
92
+ return out
93
+ }
94
+
95
+ export { QUERY_PRESET_KEYS, applyQueryPreset, matchQueryPreset };
96
+ //# sourceMappingURL=queryPresets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queryPresets.js","sources":["../../../src/utils/queryPresets.js"],"sourcesContent":["/**\n * SPDX-FileCopyrightText: 2026 Conduction B.V.\n * SPDX-License-Identifier: EUPL-1.2\n *\n * Query presets of an index page (`config.queryPresets`): a menu entry that\n * deep-links to a list with a query (`{ caseType: '<uuid>' }`, the shape hydra\n * gate-68 asks for instead of a second page) can bring its own lenses,\n * columns and copy. The page names, per preset, the query it answers to and\n * the keys it replaces; the first preset whose every `match` pair is in the\n * current route query wins.\n *\n * @module utils/queryPresets\n * @spec openspec/changes/screens-index-query-presets/specs/index-query-presets/spec.md#requirement-a-query-preset-overlays-lenses-columns-and-copy\n */\n\n/**\n * The config keys a preset may replace. Anything else in a preset is ignored:\n * a preset changes what the list shows, never which register or schema it\n * reads or what it may write.\n *\n * @type {ReadonlyArray<string>}\n */\nexport const QUERY_PRESET_KEYS = Object.freeze([\n\t'title',\n\t'quickFilters',\n\t'columns',\n\t'cardFields',\n\t'countText',\n\t'searchPlaceholder',\n\t'footerNote',\n\t'defaultSort',\n\t'viewSwitch',\n])\n\n/**\n * Whether one route query value satisfies one `match` value. A repeated query\n * parameter (an array) matches when it holds the value.\n *\n * @param {unknown} actual The route query value.\n * @param {string|number|boolean} expected The preset's value.\n * @return {boolean} True when they match.\n */\nfunction queryValueMatches(actual, expected) {\n\tconst want = String(expected)\n\tif (Array.isArray(actual)) {\n\t\treturn actual.some((v) => String(v) === want)\n\t}\n\treturn actual !== undefined && actual !== null && String(actual) === want\n}\n\n/**\n * The index of the first preset whose every `match` pair is in the query.\n * A preset without a non-empty `match` never matches.\n *\n * @param {Array<object>} presets The page's `queryPresets`.\n * @param {object} query The current route query.\n * @return {number} The preset index, or -1.\n */\nexport function matchQueryPreset(presets, query) {\n\tif (!Array.isArray(presets)) {\n\t\treturn -1\n\t}\n\tconst q = query && typeof query === 'object' ? query : {}\n\treturn presets.findIndex((preset) => {\n\t\tconst match = preset && typeof preset.match === 'object' && preset.match !== null ? preset.match : null\n\t\tif (!match || Object.keys(match).length === 0) {\n\t\t\treturn false\n\t\t}\n\t\treturn Object.entries(match).every(([key, value]) => queryValueMatches(q[key], value))\n\t})\n}\n\n/**\n * The page config with the preset's allowed keys laid over it, and without\n * `queryPresets` itself (the page component has no such prop).\n *\n * @param {object} config The page config.\n * @param {object|null} preset The matched preset, or null.\n * @return {object} The effective config.\n */\nexport function applyQueryPreset(config, preset) {\n\tconst { queryPresets, ...rest } = config || {}\n\tif (!preset || typeof preset !== 'object') {\n\t\treturn rest\n\t}\n\tconst out = { ...rest }\n\tfor (const key of QUERY_PRESET_KEYS) {\n\t\tif (key !== 'title' && preset[key] !== undefined) {\n\t\t\tout[key] = preset[key]\n\t\t}\n\t}\n\treturn out\n}\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACY,MAAC,iBAAiB,GAAG,MAAM,CAAC,MAAM,CAAC;AAC/C,CAAC,OAAO;AACR,CAAC,cAAc;AACf,CAAC,SAAS;AACV,CAAC,YAAY;AACb,CAAC,WAAW;AACZ,CAAC,mBAAmB;AACpB,CAAC,YAAY;AACb,CAAC,aAAa;AACd,CAAC,YAAY;AACb,CAAC,EAAC;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAAS,iBAAiB,CAAC,MAAM,EAAE,QAAQ,EAAE;AAC7C,CAAC,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,EAAC;AAC9B,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE;AAC5B,EAAE,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;AAC/C,EAAE;AACF,CAAC,OAAO,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,IAAI;AAC1E,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,gBAAgB,CAAC,OAAO,EAAE,KAAK,EAAE;AACjD,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE;AAC9B,EAAE,OAAO,CAAC,CAAC;AACX,EAAE;AACF,CAAC,MAAM,CAAC,GAAG,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,GAAG,KAAK,GAAG,GAAE;AAC1D,CAAC,OAAO,OAAO,CAAC,SAAS,CAAC,CAAC,MAAM,KAAK;AACtC,EAAE,MAAM,KAAK,GAAG,MAAM,IAAI,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,KAAK,KAAK,IAAI,GAAG,MAAM,CAAC,KAAK,GAAG,KAAI;AACzG,EAAE,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE;AACjD,GAAG,OAAO,KAAK;AACf,GAAG;AACH,EAAE,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,KAAK,iBAAiB,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC;AACxF,EAAE,CAAC;AACH,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE;AACjD,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,IAAI,EAAE,GAAG,MAAM,IAAI,GAAE;AAC/C,CAAC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE;AAC5C,EAAE,OAAO,IAAI;AACb,EAAE;AACF,CAAC,MAAM,GAAG,GAAG,EAAE,GAAG,IAAI,GAAE;AACxB,CAAC,KAAK,MAAM,GAAG,IAAI,iBAAiB,EAAE;AACtC,EAAE,IAAI,GAAG,KAAK,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,KAAK,SAAS,EAAE;AACpD,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,EAAC;AACzB,GAAG;AACH,EAAE;AACF,CAAC,OAAO,GAAG;AACX;;;;"}