@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
@@ -1,26 +1,38 @@
1
- import { resolveComponent, openBlock, createElementBlock, toDisplayString, createVNode, Fragment, createElementVNode, renderList, normalizeClass, createBlock, withModifiers, withCtx, createTextVNode, createCommentVNode } from 'vue';
1
+ import { resolveComponent, openBlock, createElementBlock, normalizeClass, toDisplayString, createVNode, Fragment, createCommentVNode, createElementVNode, renderList, withModifiers, createBlock, withCtx, createTextVNode } from 'vue';
2
2
 
3
- const _hoisted_1 = { class: "cn-tasks-widget" };
4
- const _hoisted_2 = {
3
+ const _hoisted_1 = {
5
4
  key: 0,
6
5
  class: "cn-tasks-widget__error"
7
6
  };
8
- const _hoisted_3 = {
7
+ const _hoisted_2 = {
9
8
  key: 1,
10
9
  class: "cn-tasks-widget__loading"
11
10
  };
12
- const _hoisted_4 = {
11
+ const _hoisted_3 = {
13
12
  key: 2,
14
13
  class: "cn-tasks-widget__empty"
15
14
  };
16
- const _hoisted_5 = { class: "cn-tasks-widget__count" };
17
- const _hoisted_6 = { class: "cn-tasks-widget__list" };
18
- const _hoisted_7 = ["data-state", "onClick", "onAuxclick"];
19
- const _hoisted_8 = { class: "cn-tasks-widget__body" };
20
- const _hoisted_9 = { class: "cn-tasks-widget__name" };
21
- const _hoisted_10 = { class: "cn-tasks-widget__meta" };
22
- const _hoisted_11 = {
15
+ const _hoisted_4 = {
23
16
  key: 0,
17
+ class: "cn-tasks-widget__count"
18
+ };
19
+ const _hoisted_5 = { class: "cn-tasks-widget__list" };
20
+ const _hoisted_6 = ["data-state", "onClick", "onAuxclick"];
21
+ const _hoisted_7 = ["checked", "disabled", "aria-label", "onChange"];
22
+ const _hoisted_8 = {
23
+ key: 1,
24
+ class: "cn-tasks-widget__check-spacer",
25
+ "aria-hidden": "true"
26
+ };
27
+ const _hoisted_9 = { class: "cn-tasks-widget__body" };
28
+ const _hoisted_10 = { class: "cn-tasks-widget__name" };
29
+ const _hoisted_11 = { class: "cn-tasks-widget__meta cn-tasks-widget__meta--split" };
30
+ const _hoisted_12 = { class: "cn-tasks-widget__subject" };
31
+ const _hoisted_13 = { class: "cn-tasks-widget__body" };
32
+ const _hoisted_14 = { class: "cn-tasks-widget__name" };
33
+ const _hoisted_15 = { class: "cn-tasks-widget__meta" };
34
+ const _hoisted_16 = {
35
+ key: 1,
24
36
  class: "cn-tasks-widget__more"
25
37
  };
26
38
 
@@ -29,41 +41,72 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
29
41
  const _component_NcActionButton = resolveComponent("NcActionButton");
30
42
  const _component_NcActions = resolveComponent("NcActions");
31
43
 
32
- return (openBlock(), createElementBlock("div", _hoisted_1, [
44
+ return (openBlock(), createElementBlock("div", {
45
+ class: normalizeClass(["cn-tasks-widget", { 'cn-tasks-widget--list': $options.isList }])
46
+ }, [
33
47
  ($setup.error)
34
- ? (openBlock(), createElementBlock("p", _hoisted_2, toDisplayString($options.tr('Could not load the tasks')), 1 /* TEXT */))
48
+ ? (openBlock(), createElementBlock("p", _hoisted_1, toDisplayString($options.tr('Could not load the tasks')), 1 /* TEXT */))
35
49
  : ($setup.loading && $options.rows.length === 0)
36
- ? (openBlock(), createElementBlock("div", _hoisted_3, [
50
+ ? (openBlock(), createElementBlock("div", _hoisted_2, [
37
51
  createVNode(_component_NcLoadingIcon, { size: 24 })
38
52
  ]))
39
53
  : ($options.rows.length === 0)
40
- ? (openBlock(), createElementBlock("p", _hoisted_4, toDisplayString($options.emptyLabel), 1 /* TEXT */))
54
+ ? (openBlock(), createElementBlock("p", _hoisted_3, toDisplayString($options.emptyLabel), 1 /* TEXT */))
41
55
  : (openBlock(), createElementBlock(Fragment, { key: 3 }, [
42
- createElementVNode("p", _hoisted_5, toDisplayString($options.countLabel), 1 /* TEXT */),
43
- createElementVNode("ul", _hoisted_6, [
56
+ (!$options.isList)
57
+ ? (openBlock(), createElementBlock("p", _hoisted_4, toDisplayString($options.countLabel), 1 /* TEXT */))
58
+ : createCommentVNode("v-if", true),
59
+ createElementVNode("ul", _hoisted_5, [
44
60
  (openBlock(true), createElementBlock(Fragment, null, renderList($options.rows, (task) => {
45
61
  return (openBlock(), createElementBlock("li", {
46
62
  key: task.uuid,
47
63
  class: "cn-tasks-widget__row",
48
64
  "data-state": task.state,
49
65
  onClick: $event => ($options.onRowClick(task, $event)),
50
- onMousedown: _cache[1] || (_cache[1] = (...args) => ($options.preventMiddleClickAutoscroll && $options.preventMiddleClickAutoscroll(...args))),
66
+ onMousedown: _cache[2] || (_cache[2] = (...args) => ($options.preventMiddleClickAutoscroll && $options.preventMiddleClickAutoscroll(...args))),
51
67
  onAuxclick: $event => ($options.onRowAuxClick(task, $event))
52
68
  }, [
53
- createElementVNode("span", _hoisted_8, [
54
- createElementVNode("span", _hoisted_9, toDisplayString($options.titleOf(task)), 1 /* TEXT */),
55
- createElementVNode("span", _hoisted_10, toDisplayString($options.metaLine(task)), 1 /* TEXT */)
56
- ]),
57
- createElementVNode("span", {
58
- class: normalizeClass(["cn-tasks-widget__due", { 'cn-tasks-widget__due--overdue': task.overdue === true }])
59
- }, toDisplayString($options.dueLabel(task)), 3 /* TEXT, CLASS */),
60
- ($options.canClaim(task) || $options.canComplete(task))
69
+ ($options.isList)
70
+ ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
71
+ ($options.canCheck(task))
72
+ ? (openBlock(), createElementBlock("input", {
73
+ key: 0,
74
+ type: "checkbox",
75
+ class: "cn-tasks-widget__check",
76
+ checked: $data.completing.includes(String(task.uuid)),
77
+ disabled: $data.completing.includes(String(task.uuid)),
78
+ "aria-label": $options.tr('Mark {title} as done').replace('{title}', $options.titleOf(task)),
79
+ "data-testid": "cn-tasks-widget-check",
80
+ onClick: _cache[0] || (_cache[0] = withModifiers(() => {}, ["stop"])),
81
+ onChange: $event => ($options.check(task))
82
+ }, null, 40 /* PROPS, NEED_HYDRATION */, _hoisted_7))
83
+ : (openBlock(), createElementBlock("span", _hoisted_8)),
84
+ createElementVNode("span", _hoisted_9, [
85
+ createElementVNode("span", _hoisted_10, toDisplayString($options.titleOf(task)), 1 /* TEXT */),
86
+ createElementVNode("span", _hoisted_11, [
87
+ createElementVNode("span", _hoisted_12, toDisplayString($options.subjectOf(task)), 1 /* TEXT */),
88
+ createElementVNode("span", {
89
+ class: normalizeClass(["cn-tasks-widget__due", { 'cn-tasks-widget__due--overdue': task.overdue === true }])
90
+ }, toDisplayString($options.dueLabel(task)), 3 /* TEXT, CLASS */)
91
+ ])
92
+ ])
93
+ ], 64 /* STABLE_FRAGMENT */))
94
+ : (openBlock(), createElementBlock(Fragment, { key: 1 }, [
95
+ createElementVNode("span", _hoisted_13, [
96
+ createElementVNode("span", _hoisted_14, toDisplayString($options.titleOf(task)), 1 /* TEXT */),
97
+ createElementVNode("span", _hoisted_15, toDisplayString($options.metaLine(task)), 1 /* TEXT */)
98
+ ]),
99
+ createElementVNode("span", {
100
+ class: normalizeClass(["cn-tasks-widget__due", { 'cn-tasks-widget__due--overdue': task.overdue === true }])
101
+ }, toDisplayString($options.dueLabel(task)), 3 /* TEXT, CLASS */)
102
+ ], 64 /* STABLE_FRAGMENT */)),
103
+ (($options.canClaim(task) || $options.canComplete(task)) && !($options.isList && $options.canCheck(task)))
61
104
  ? (openBlock(), createBlock(_component_NcActions, {
62
- key: 0,
105
+ key: 2,
63
106
  class: "cn-tasks-widget__actions",
64
107
  forceMenu: true,
65
108
  "aria-label": $options.tr('Task actions'),
66
- onClick: _cache[0] || (_cache[0] = withModifiers(() => {}, ["stop"]))
109
+ onClick: _cache[1] || (_cache[1] = withModifiers(() => {}, ["stop"]))
67
110
  }, {
68
111
  default: withCtx(() => [
69
112
  ($options.canClaim(task))
@@ -96,14 +139,14 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
96
139
  _: 2 /* DYNAMIC */
97
140
  }, 1032 /* PROPS, DYNAMIC_SLOTS */, ["aria-label"]))
98
141
  : createCommentVNode("v-if", true)
99
- ], 40 /* PROPS, NEED_HYDRATION */, _hoisted_7))
142
+ ], 40 /* PROPS, NEED_HYDRATION */, _hoisted_6))
100
143
  }), 128 /* KEYED_FRAGMENT */))
101
144
  ]),
102
145
  ($options.hiddenCount > 0)
103
- ? (openBlock(), createElementBlock("p", _hoisted_11, toDisplayString($options.moreLabel), 1 /* TEXT */))
146
+ ? (openBlock(), createElementBlock("p", _hoisted_16, toDisplayString($options.moreLabel), 1 /* TEXT */))
104
147
  : createCommentVNode("v-if", true)
105
148
  ], 64 /* STABLE_FRAGMENT */))
106
- ]))
149
+ ], 2 /* CLASS */))
107
150
  }
108
151
 
109
152
  export { render };
@@ -1 +1 @@
1
- {"version":3,"file":"CnTasksWidget.vue3.js","sources":["../../../../src/components/CnTasksWidget/CnTasksWidget.vue?vue&type=template&id=ff399342&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-tasks-widget\">\n\t\t<!-- One quiet line on failure, never a leaked axios status string\n\t\t (ADR-062). The real error goes to the console. -->\n\t\t<p v-if=\"error\" class=\"cn-tasks-widget__error\">\n\t\t\t{{ tr('Could not load the tasks') }}\n\t\t</p>\n\t\t<div v-else-if=\"loading && rows.length === 0\" class=\"cn-tasks-widget__loading\">\n\t\t\t<NcLoadingIcon :size=\"24\" />\n\t\t</div>\n\t\t<!-- An empty inbox is the GOOD state, not an error and not a void:\n\t\t one muted line, no illustration. -->\n\t\t<p v-else-if=\"rows.length === 0\" class=\"cn-tasks-widget__empty\">\n\t\t\t{{ emptyLabel }}\n\t\t</p>\n\t\t<template v-else>\n\t\t\t<!-- The count states the server TOTAL, never the rendered length\n\t\t\t (ADR-062): the number a person acts on is how much work\n\t\t\t exists, not how much fits the cell. -->\n\t\t\t<p class=\"cn-tasks-widget__count\">\n\t\t\t\t{{ countLabel }}\n\t\t\t</p>\n\t\t\t<ul class=\"cn-tasks-widget__list\">\n\t\t\t\t<li\n\t\t\t\t\tv-for=\"task in rows\"\n\t\t\t\t\t:key=\"task.uuid\"\n\t\t\t\t\tclass=\"cn-tasks-widget__row\"\n\t\t\t\t\t:data-state=\"task.state\"\n\t\t\t\t\t@click=\"onRowClick(task, $event)\"\n\t\t\t\t\t@mousedown=\"preventMiddleClickAutoscroll\"\n\t\t\t\t\t@auxclick=\"onRowAuxClick(task, $event)\">\n\t\t\t\t\t<span class=\"cn-tasks-widget__body\">\n\t\t\t\t\t\t<span class=\"cn-tasks-widget__name\">{{ titleOf(task) }}</span>\n\t\t\t\t\t\t<span class=\"cn-tasks-widget__meta\">{{ metaLine(task) }}</span>\n\t\t\t\t\t</span>\n\t\t\t\t\t<!-- Overdue is carried by the WORDING (taskDueLabel) and by\n\t\t\t\t\t weight; the colour only reinforces it. -->\n\t\t\t\t\t<span\n\t\t\t\t\t\tclass=\"cn-tasks-widget__due\"\n\t\t\t\t\t\t:class=\"{ 'cn-tasks-widget__due--overdue': task.overdue === true }\">\n\t\t\t\t\t\t{{ dueLabel(task) }}\n\t\t\t\t\t</span>\n\t\t\t\t\t<!-- Only verbs the contract can accept are offered; the\n\t\t\t\t\t server still authorizes, and a refusal is toasted in\n\t\t\t\t\t its own words. Stopped clicks: the menu must not also\n\t\t\t\t\t open the row. -->\n\t\t\t\t\t<NcActions\n\t\t\t\t\t\tv-if=\"canClaim(task) || canComplete(task)\"\n\t\t\t\t\t\tclass=\"cn-tasks-widget__actions\"\n\t\t\t\t\t\t:forceMenu=\"true\"\n\t\t\t\t\t\t:aria-label=\"tr('Task actions')\"\n\t\t\t\t\t\t@click.stop>\n\t\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\t\tv-if=\"canClaim(task)\"\n\t\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t\t@click=\"claim(task)\">\n\t\t\t\t\t\t\t{{ tr('Claim') }}\n\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t<template v-if=\"canComplete(task)\">\n\t\t\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\t\t\tv-for=\"outcome in outcomesOf(task)\"\n\t\t\t\t\t\t\t\t:key=\"outcomeId(outcome)\"\n\t\t\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t\t\t@click=\"complete(task, outcomeId(outcome))\">\n\t\t\t\t\t\t\t\t{{ completeLabel(outcome) }}\n\t\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcActions>\n\t\t\t\t</li>\n\t\t\t</ul>\n\t\t\t<!-- The remainder is a count, never a scrollbar (ADR-062). -->\n\t\t\t<p v-if=\"hiddenCount > 0\" class=\"cn-tasks-widget__more\">\n\t\t\t\t{{ moreLabel }}\n\t\t\t</p>\n\t\t</template>\n\t</div>\n</template>\n\n<script>\nimport { getCurrentUser } from '@nextcloud/auth'\nimport axios from '@nextcloud/axios'\nimport { showError } from '@nextcloud/dialogs'\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcActionButton, NcActions, NcLoadingIcon } from '@nextcloud/vue'\nimport { inject, ref } from 'vue'\nimport { taskDeepLink, taskDueLabel } from '../../composables/indexSources.js'\nimport { useEndpointSource } from '../../composables/useEndpointSource.js'\nimport { openRowTarget } from '../../utils/linkNavigation.js'\nimport { isRowMiddleClick, preventMiddleClickAutoscroll } from '../../utils/rowAuxClick.js'\n\n/**\n * The OpenRegister inbox read (openregister flow-task-entity). One endpoint\n * for the whole fleet: every app's human tasks live on OpenRegister's one\n * task store, so the widget is app-agnostic and ships no controller.\n *\n * @type {string}\n */\nconst FLOW_TASKS_URL = '/apps/openregister/api/flow-tasks'\n\n/**\n * The CMMN states, in the reading a dashboard needs. Labels double as the\n * meta-line words, so state never rests on styling.\n *\n * @type {Record<string, string>}\n */\nconst STATE_LABELS = {\n\tavailable: 'Available',\n\tenabled: 'Ready to claim',\n\tactive: 'In progress',\n\tcompleted: 'Completed',\n\tterminated: 'Terminated',\n\tdisabled: 'Disabled',\n}\n\n/**\n * The priority vocabulary (openregister Task::PRIORITIES).\n *\n * @type {Record<string, string>}\n */\nconst PRIORITY_LABELS = {\n\tlow: 'Low',\n\tnormal: 'Normal',\n\thigh: 'High',\n\turgent: 'Urgent',\n}\n\n/**\n * CnTasksWidget — the viewer's open tasks, from OpenRegister's task inbox.\n *\n * Reads `flow-tasks` with `isTerminal=false` (an inbox widget that showed\n * finished tasks would never drain), scoped by `content.scope` and sorted by\n * due date so the most urgent row sits on top. Placed by its registry type\n * key `tasks`:\n *\n * ```js\n * { id: 'my-tasks', type: 'tasks', title: 'My tasks',\n * content: { scope: 'assigned', limit: 6, pollSeconds: 30 } }\n * ```\n *\n * Why it polls: tasks are handed out, claimed and completed by OTHER people\n * and by flows, so a widget that only reflected its mount moment would be\n * wrong within minutes and look identical to being right. Polling pauses\n * while the tab is hidden.\n *\n * Quick actions offer only what the row's contract can accept: claim on a\n * pooled row, complete (per declared outcome) on the viewer's own open row.\n * The server still authorizes; a refusal surfaces its `error` message as a\n * toast and the widget refetches, so a lost claim race corrects the row.\n *\n * @spec openspec/changes/cn-tasks-entity-source/specs/cn-tasks-entity-source/spec.md\n */\nexport default {\n\tname: 'CnTasksWidget',\n\n\tcomponents: {\n\t\tNcActions,\n\t\tNcActionButton,\n\t\tNcLoadingIcon,\n\t},\n\n\tinject: {\n\t\t/**\n\t\t * Host translate function, provided by CnAppRoot (identity by default)\n\t\t * so a library string localises through the consuming app's l10n.\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The widget's persisted configuration blob.\n\t\t *\n\t\t * - `scope` — whose relationship to list: `assigned` (default),\n\t\t * `pooled`, `watched` or `all`. Whose INBOX it is stays the\n\t\t * endpoint's decision; scope never names another user.\n\t\t * - `limit` — rows to request AND show (1–50, default 6). The count\n\t\t * line always states the server total regardless of this cap.\n\t\t * - `pollSeconds` — refetch interval (default 30; `0` disables).\n\t\t * - `rowRoute` — vue-router route NAME to open on a row click,\n\t\t * receiving the task uuid as `:id`. Omitted = the click opens the\n\t\t * task's openregister deep link instead.\n\t\t * - `emptyText` — override for the empty-inbox line.\n\t\t *\n\t\t * @type {{scope?: string, limit?: number, pollSeconds?: number, rowRoute?: string, emptyText?: string}}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Widget placement id, so the per-widget Refresh action\n\t\t * (`cn:widget:refresh`) reaches this instance's fetch.\n\t\t */\n\t\twidgetId: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Translate function. Falls back to the injected `cnTranslate`.\n\t\t * Provide explicitly when mounting outside a CnAppRoot ancestor.\n\t\t *\n\t\t * @type {((key: string) => string)|null}\n\t\t */\n\t\ttranslate: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\tsetup(props) {\n\t\t// The shared endpoint engine owns request dedup, the short-TTL cache\n\t\t// and the cn:page:refresh / cn:widget:refresh subscriptions — the\n\t\t// same binding CnFlowRunsWidget uses, so a page-level Refresh updates\n\t\t// this widget with everything else on the page.\n\t\tconst widgetIdRef = ref(props.widgetId)\n\t\tconst source = () => ({\n\t\t\turl: FLOW_TASKS_URL,\n\t\t\tmethod: 'GET',\n\t\t\tparams: {\n\t\t\t\tscope: normaliseScope(props.content && props.content.scope),\n\t\t\t\t// An inbox drains: finished tasks leave the widget. The index\n\t\t\t\t// page's \"everything\" tab is the place to read history.\n\t\t\t\tisTerminal: 'false',\n\t\t\t\t// Ascending due date: the most urgent (overdue first) on top.\n\t\t\t\tsort: 'dueAt',\n\t\t\t\tlimit: normaliseLimit(props.content && props.content.limit),\n\t\t\t},\n\t\t})\n\t\tconst { data, loading, error, refetch } = useEndpointSource(source, {\n\t\t\twidgetId: widgetIdRef,\n\t\t})\n\n\t\t// `inject` is re-read in setup so Vue 2.7 and Vue 3 resolve it\n\t\t// identically to the Options `inject` block (CnStatWidget precedent).\n\t\tconst injectedTranslate = inject('cnTranslate', null)\n\n\t\treturn {\n\t\t\tpayload: data,\n\t\t\tloading,\n\t\t\terror,\n\t\t\trefetch,\n\t\t\tinjectedTranslate,\n\t\t}\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tpollTimer: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Effective translate function: the explicit prop, then the injected\n\t\t * host one, then `@nextcloud/l10n` under the library's own app id.\n\t\t *\n\t\t * @return {(key: string) => string}\n\t\t */\n\t\ttr() {\n\t\t\tconst fn = this.translate ?? this.injectedTranslate ?? this.cnTranslate\n\t\t\tif (typeof fn === 'function') {\n\t\t\t\treturn (key) => fn(key)\n\t\t\t}\n\t\t\treturn (key) => t('nextcloud-vue', key)\n\t\t},\n\n\t\t/**\n\t\t * The signed-in viewer's uid, for the per-row action offer. Empty\n\t\t * outside a Nextcloud session (the offer then collapses to claim on\n\t\t * pooled rows only, which is the safe reading).\n\t\t *\n\t\t * @return {string} The uid, or ''.\n\t\t */\n\t\tviewerUid() {\n\t\t\treturn getCurrentUser()?.uid || ''\n\t\t},\n\n\t\t/**\n\t\t * The rows to render — the endpoint's bounded page, defensively\n\t\t * re-capped so a wider server default can never overflow the cell.\n\t\t *\n\t\t * @return {Array<object>} The task rows.\n\t\t */\n\t\trows() {\n\t\t\tconst results = (this.payload && this.payload.results) || []\n\t\t\tif (Array.isArray(results) === false) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn results.slice(0, normaliseLimit(this.content.limit))\n\t\t},\n\n\t\t/**\n\t\t * The server's honest total, independent of the rendered page.\n\t\t *\n\t\t * @return {number} The open-task total (never negative).\n\t\t */\n\t\ttotal() {\n\t\t\tconst total = Number((this.payload && this.payload.total) ?? this.rows.length)\n\t\t\tif (Number.isFinite(total) === false) {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\treturn Math.max(0, total)\n\t\t},\n\n\t\t/** The count line above the rows. */\n\t\tcountLabel() {\n\t\t\tif (this.total === 1) {\n\t\t\t\treturn this.tr('1 open task')\n\t\t\t}\n\t\t\treturn this.tr('{count} open tasks').replace('{count}', String(this.total))\n\t\t},\n\n\t\t/**\n\t\t * How many open tasks exist beyond the rendered rows.\n\t\t *\n\t\t * @return {number} The hidden task count (never negative).\n\t\t */\n\t\thiddenCount() {\n\t\t\treturn Math.max(0, this.total - this.rows.length)\n\t\t},\n\n\t\t/** The \"+N more\" line. */\n\t\tmoreLabel() {\n\t\t\treturn this.tr('+{count} more').replace('{count}', String(this.hiddenCount))\n\t\t},\n\n\t\t/** The empty-inbox line. */\n\t\temptyLabel() {\n\t\t\tconst override = this.content.emptyText\n\t\t\tif (typeof override === 'string' && override !== '') {\n\t\t\t\treturn this.tr(override)\n\t\t\t}\n\t\t\treturn this.tr('No open tasks')\n\t\t},\n\n\t\t/**\n\t\t * Poll interval in ms, or 0 when polling is off. Floored at 5s so a\n\t\t * mis-authored `pollSeconds: 0.1` cannot turn a dashboard into a\n\t\t * request storm; `0` remains an explicit opt-out.\n\t\t *\n\t\t * @return {number} The interval in milliseconds.\n\t\t */\n\t\tpollMs() {\n\t\t\tconst raw = this.content.pollSeconds\n\t\t\tconst seconds = (raw === undefined || raw === null) ? 30 : Number(raw)\n\t\t\tif (Number.isFinite(seconds) === false || seconds <= 0) {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\treturn Math.max(5, seconds) * 1000\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.startPolling()\n\t\tdocument.addEventListener('visibilitychange', this.onVisibilityChange)\n\t},\n\n\tbeforeUnmount() {\n\t\tthis.stopPolling()\n\t\tdocument.removeEventListener('visibilitychange', this.onVisibilityChange)\n\t},\n\n\tmethods: {\n\t\tpreventMiddleClickAutoscroll,\n\n\t\t/**\n\t\t * Start (or restart) the refetch interval, unless polling is off.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tstartPolling() {\n\t\t\tthis.stopPolling()\n\t\t\tif (this.pollMs === 0 || document.hidden === true) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.pollTimer = setInterval(() => {\n\t\t\t\tthis.refetch(true)\n\t\t\t}, this.pollMs)\n\t\t},\n\n\t\t/**\n\t\t * Clear the refetch interval.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tstopPolling() {\n\t\t\tif (this.pollTimer !== null) {\n\t\t\t\tclearInterval(this.pollTimer)\n\t\t\t\tthis.pollTimer = null\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Pause polling while the tab is hidden; refetch once and resume on\n\t\t * return, so the dashboard shows current work rather than whatever\n\t\t * was true when the tab was backgrounded.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tonVisibilityChange() {\n\t\t\tif (document.hidden === true) {\n\t\t\t\tthis.stopPolling()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.refetch(true)\n\t\t\tthis.startPolling()\n\t\t},\n\n\t\t/**\n\t\t * The row's display title (server-synthesized when untitled).\n\t\t *\n\t\t * @param {object} task The task row.\n\t\t * @return {string} The title.\n\t\t */\n\t\ttitleOf(task) {\n\t\t\treturn task.displayTitle || task.title || String(task.uuid || '')\n\t\t},\n\n\t\t/**\n\t\t * The secondary line: subject, state and priority — whichever of\n\t\t * those the row actually carries, in words.\n\t\t *\n\t\t * @param {object} task The task row.\n\t\t * @return {string} The meta line.\n\t\t */\n\t\tmetaLine(task) {\n\t\t\tconst parts = []\n\t\t\tif (task.subject && task.subject.title) {\n\t\t\t\tparts.push(String(task.subject.title))\n\t\t\t}\n\t\t\tconst state = STATE_LABELS[task.state]\n\t\t\tparts.push(state ? this.tr(state) : String(task.state || ''))\n\t\t\tconst priority = PRIORITY_LABELS[task.priority]\n\t\t\tif (priority && task.priority !== 'normal') {\n\t\t\t\tparts.push(this.tr(priority))\n\t\t\t}\n\t\t\treturn parts.join(' · ')\n\t\t},\n\n\t\t/**\n\t\t * The due wording for a row, from the server's derived projection.\n\t\t *\n\t\t * @param {object} task The task row.\n\t\t * @return {string} The due label, or '' without a due date.\n\t\t */\n\t\tdueLabel(task) {\n\t\t\treturn taskDueLabel(task)\n\t\t},\n\n\t\t/**\n\t\t * Whether the row offers claim: pooled (no assignee) and still open.\n\t\t *\n\t\t * @param {object} task The task row.\n\t\t * @return {boolean} True when claim is offered.\n\t\t */\n\t\tcanClaim(task) {\n\t\t\treturn !task.assignee && task.isTerminal !== true\n\t\t},\n\n\t\t/**\n\t\t * Whether the row offers complete: the viewer's own open task.\n\t\t *\n\t\t * @param {object} task The task row.\n\t\t * @return {boolean} True when complete is offered.\n\t\t */\n\t\tcanComplete(task) {\n\t\t\treturn this.viewerUid !== ''\n\t\t\t\t&& task.assignee === this.viewerUid\n\t\t\t\t&& task.isTerminal !== true\n\t\t},\n\n\t\t/**\n\t\t * The outcomes to offer on complete: the row's declared list, else\n\t\t * one entry for the server's default outcome.\n\t\t *\n\t\t * @param {object} task The task row.\n\t\t * @return {Array<object|string>} The outcome entries.\n\t\t */\n\t\toutcomesOf(task) {\n\t\t\tif (Array.isArray(task.outcomes) && task.outcomes.length > 0) {\n\t\t\t\treturn task.outcomes\n\t\t\t}\n\t\t\treturn ['done']\n\t\t},\n\n\t\t/**\n\t\t * The wire value of one outcome entry (a string, or `{id, label}`).\n\t\t *\n\t\t * @param {object|string} outcome The outcome entry.\n\t\t * @return {string} The outcome id.\n\t\t */\n\t\toutcomeId(outcome) {\n\t\t\tif (outcome && typeof outcome === 'object') {\n\t\t\t\treturn String(outcome.id ?? outcome.value ?? outcome.label ?? '')\n\t\t\t}\n\t\t\treturn String(outcome ?? '')\n\t\t},\n\n\t\t/**\n\t\t * The menu label for one complete entry.\n\t\t *\n\t\t * @param {object|string} outcome The outcome entry.\n\t\t * @return {string} The label.\n\t\t */\n\t\tcompleteLabel(outcome) {\n\t\t\tconst id = this.outcomeId(outcome)\n\t\t\tconst label = (outcome && typeof outcome === 'object' && outcome.label)\n\t\t\t\t? String(outcome.label)\n\t\t\t\t: id\n\t\t\tif (id === 'done') {\n\t\t\t\treturn this.tr('Complete')\n\t\t\t}\n\t\t\treturn this.tr('Complete: {outcome}').replace('{outcome}', label)\n\t\t},\n\n\t\t/**\n\t\t * Claim a pooled task.\n\t\t *\n\t\t * @param {object} task The task row.\n\t\t * @return {Promise<void>} Resolves when the verb settled.\n\t\t */\n\t\tclaim(task) {\n\t\t\treturn this.postVerb(task, 'claim', {})\n\t\t},\n\n\t\t/**\n\t\t * Complete the viewer's own task with an outcome.\n\t\t *\n\t\t * @param {object} task The task row.\n\t\t * @param {string} outcome The chosen outcome id.\n\t\t * @return {Promise<void>} Resolves when the verb settled.\n\t\t */\n\t\tcomplete(task, outcome) {\n\t\t\treturn this.postVerb(task, 'complete', { outcome })\n\t\t},\n\n\t\t/**\n\t\t * POST one lifecycle verb and refetch either way: on success the row\n\t\t * moved, and on a refusal (a lost claim race, a revoked task) the\n\t\t * list is stale in exactly the way the refusal proves. The refusal\n\t\t * itself is toasted in the SERVER'S words (ADR-062 forbids leaking\n\t\t * the raw status line, and the endpoint's `error` string is written\n\t\t * for people).\n\t\t *\n\t\t * @param {object} task The task row.\n\t\t * @param {string} verb The lifecycle verb.\n\t\t * @param {object} body The verb body.\n\t\t * @return {Promise<void>} Resolves when the verb settled.\n\t\t */\n\t\tasync postVerb(task, verb, body) {\n\t\t\tconst uuid = String(task.uuid || '')\n\t\t\tif (uuid === '') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait axios.post(generateUrl(`${FLOW_TASKS_URL}/${uuid}/${verb}`), body)\n\t\t\t} catch (error) {\n\t\t\t\tconst message = error?.response?.data?.error\n\t\t\t\tshowError(typeof message === 'string' && message !== ''\n\t\t\t\t\t? message\n\t\t\t\t\t: this.tr('The task action was refused'))\n\t\t\t}\n\t\t\tthis.refetch(true)\n\t\t},\n\n\t\t/**\n\t\t * Open the clicked task: the configured route with the uuid as `id`,\n\t\t * else the task's openregister deep link. A ctrl/cmd/shift or middle\n\t\t * click opens it in a new tab.\n\t\t *\n\t\t * @param {object} task The clicked task row.\n\t\t * @param {MouseEvent} [event] The originating click/auxclick event.\n\t\t * @return {void}\n\t\t */\n\t\tonRowClick(task, event) {\n\t\t\tconst uuid = String(task.uuid || '')\n\t\t\tif (uuid === '') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst rowRoute = this.content.rowRoute\n\t\t\tif (typeof rowRoute === 'string' && rowRoute !== '' && this.$router) {\n\t\t\t\topenRowTarget(event, { name: rowRoute, params: { id: uuid } }, this.$router)\n\t\t\t\treturn\n\t\t\t}\n\t\t\topenRowTarget(event, taskDeepLink(uuid))\n\t\t},\n\n\t\t/**\n\t\t * Middle click on a task row: open it in a new tab.\n\t\t *\n\t\t * @param {object} task The clicked task row.\n\t\t * @param {MouseEvent} event The auxclick event.\n\t\t * @return {void}\n\t\t */\n\t\tonRowAuxClick(task, event) {\n\t\t\tif (isRowMiddleClick(event)) {\n\t\t\t\tthis.onRowClick(task, event)\n\t\t\t}\n\t\t},\n\t},\n}\n\n/**\n * Clamp a configured row limit into the range the endpoint accepts.\n *\n * @param {unknown} raw The configured limit.\n * @return {number} A limit between 1 and 50 (default 6).\n */\nfunction normaliseLimit(raw) {\n\tconst n = Number(raw)\n\tif (Number.isFinite(n) === false || n <= 0) {\n\t\treturn 6\n\t}\n\treturn Math.min(50, Math.round(n))\n}\n\n/**\n * Clamp a configured scope onto the endpoint's vocabulary.\n *\n * @param {unknown} raw The configured scope.\n * @return {string} One of assigned, pooled, watched, all (default assigned).\n */\nfunction normaliseScope(raw) {\n\treturn ['assigned', 'pooled', 'watched', 'all'].includes(raw) ? raw : 'assigned'\n}\n</script>\n\n<style scoped>\n.cn-tasks-widget {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n\tmin-width: 0;\n}\n\n.cn-tasks-widget__loading {\n\tdisplay: flex;\n\tjustify-content: center;\n\tpadding: 8px 0;\n}\n\n.cn-tasks-widget__empty,\n.cn-tasks-widget__error,\n.cn-tasks-widget__more {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-tasks-widget__more {\n\tpadding-top: 4px;\n}\n\n.cn-tasks-widget__count {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 12px;\n\tfont-weight: 600;\n\ttext-transform: uppercase;\n\tletter-spacing: 0.04em;\n}\n\n.cn-tasks-widget__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tmin-width: 0;\n}\n\n.cn-tasks-widget__row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 10px;\n\tpadding: 6px 0;\n\tborder-bottom: 1px solid var(--color-border);\n\tmin-width: 0;\n\tcursor: pointer;\n}\n\n.cn-tasks-widget__row:last-child {\n\tborder-bottom: none;\n}\n\n.cn-tasks-widget__body {\n\tdisplay: flex;\n\tflex-direction: column;\n\tmin-width: 0;\n\tflex: 1 1 auto;\n}\n\n.cn-tasks-widget__name {\n\tfont-weight: 600;\n\twhite-space: nowrap;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n.cn-tasks-widget__row:hover .cn-tasks-widget__name {\n\ttext-decoration: underline;\n}\n\n.cn-tasks-widget__meta {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 12px;\n\twhite-space: nowrap;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n.cn-tasks-widget__due {\n\tflex: 0 0 auto;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 12px;\n\tfont-variant-numeric: tabular-nums;\n}\n\n/* Overdue: weight AND colour on top of the wording, which already says\n \"Overdue by N days\" — the signal survives monochrome. */\n.cn-tasks-widget__due--overdue {\n\tcolor: var(--color-error, #d91f2d);\n\tfont-weight: 600;\n}\n\n.cn-tasks-widget__actions {\n\tflex: 0 0 auto;\n}\n</style>\n"],"names":["_toDisplayString","_openBlock","_createElementBlock","_createVNode","_createElementVNode","_Fragment","_renderList","_normalizeClass","_createBlock","_createTextVNode"],"mappings":"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"}
1
+ {"version":3,"file":"CnTasksWidget.vue3.js","sources":["../../../../src/components/CnTasksWidget/CnTasksWidget.vue?vue&type=template&id=ff399342&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-tasks-widget\" :class=\"{ 'cn-tasks-widget--list': isList }\">\n\t\t<!-- One quiet line on failure, never a leaked axios status string\n\t\t (ADR-062). The real error goes to the console. -->\n\t\t<p v-if=\"error\" class=\"cn-tasks-widget__error\">\n\t\t\t{{ tr('Could not load the tasks') }}\n\t\t</p>\n\t\t<div v-else-if=\"loading && rows.length === 0\" class=\"cn-tasks-widget__loading\">\n\t\t\t<NcLoadingIcon :size=\"24\" />\n\t\t</div>\n\t\t<!-- An empty inbox is the GOOD state, not an error and not a void:\n\t\t one muted line, no illustration. -->\n\t\t<p v-else-if=\"rows.length === 0\" class=\"cn-tasks-widget__empty\">\n\t\t\t{{ emptyLabel }}\n\t\t</p>\n\t\t<template v-else>\n\t\t\t<!-- The count states the server TOTAL, never the rendered length\n\t\t\t (ADR-062): the number a person acts on is how much work\n\t\t\t exists, not how much fits the cell. -->\n\t\t\t<p v-if=\"!isList\" class=\"cn-tasks-widget__count\">\n\t\t\t\t{{ countLabel }}\n\t\t\t</p>\n\t\t\t<ul class=\"cn-tasks-widget__list\">\n\t\t\t\t<li\n\t\t\t\t\tv-for=\"task in rows\"\n\t\t\t\t\t:key=\"task.uuid\"\n\t\t\t\t\tclass=\"cn-tasks-widget__row\"\n\t\t\t\t\t:data-state=\"task.state\"\n\t\t\t\t\t@click=\"onRowClick(task, $event)\"\n\t\t\t\t\t@mousedown=\"preventMiddleClickAutoscroll\"\n\t\t\t\t\t@auxclick=\"onRowAuxClick(task, $event)\">\n\t\t\t\t\t<!-- List variant: a checkbox completes the viewer's own task\n\t\t\t\t\t (the task with a declared outcome list keeps the menu, as\n\t\t\t\t\t a checkbox cannot choose an outcome). Other rows get a\n\t\t\t\t\t spacer so the titles stay aligned. -->\n\t\t\t\t\t<template v-if=\"isList\">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\tv-if=\"canCheck(task)\"\n\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\tclass=\"cn-tasks-widget__check\"\n\t\t\t\t\t\t\t:checked=\"completing.includes(String(task.uuid))\"\n\t\t\t\t\t\t\t:disabled=\"completing.includes(String(task.uuid))\"\n\t\t\t\t\t\t\t:aria-label=\"tr('Mark {title} as done').replace('{title}', titleOf(task))\"\n\t\t\t\t\t\t\tdata-testid=\"cn-tasks-widget-check\"\n\t\t\t\t\t\t\t@click.stop\n\t\t\t\t\t\t\t@change=\"check(task)\">\n\t\t\t\t\t\t<span v-else class=\"cn-tasks-widget__check-spacer\" aria-hidden=\"true\" />\n\t\t\t\t\t\t<span class=\"cn-tasks-widget__body\">\n\t\t\t\t\t\t\t<span class=\"cn-tasks-widget__name\">{{ titleOf(task) }}</span>\n\t\t\t\t\t\t\t<span class=\"cn-tasks-widget__meta cn-tasks-widget__meta--split\">\n\t\t\t\t\t\t\t\t<span class=\"cn-tasks-widget__subject\">{{ subjectOf(task) }}</span>\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tclass=\"cn-tasks-widget__due\"\n\t\t\t\t\t\t\t\t\t:class=\"{ 'cn-tasks-widget__due--overdue': task.overdue === true }\">\n\t\t\t\t\t\t\t\t\t{{ dueLabel(task) }}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template v-else>\n\t\t\t\t\t\t<span class=\"cn-tasks-widget__body\">\n\t\t\t\t\t\t\t<span class=\"cn-tasks-widget__name\">{{ titleOf(task) }}</span>\n\t\t\t\t\t\t\t<span class=\"cn-tasks-widget__meta\">{{ metaLine(task) }}</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<!-- Overdue is carried by the WORDING (taskDueLabel) and by\n\t\t\t\t\t\t weight; the colour only reinforces it. -->\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\tclass=\"cn-tasks-widget__due\"\n\t\t\t\t\t\t\t:class=\"{ 'cn-tasks-widget__due--overdue': task.overdue === true }\">\n\t\t\t\t\t\t\t{{ dueLabel(task) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</template>\n\t\t\t\t\t<!-- Only verbs the contract can accept are offered; the\n\t\t\t\t\t server still authorizes, and a refusal is toasted in\n\t\t\t\t\t its own words. Stopped clicks: the menu must not also\n\t\t\t\t\t open the row. -->\n\t\t\t\t\t<NcActions\n\t\t\t\t\t\tv-if=\"(canClaim(task) || canComplete(task)) && !(isList && canCheck(task))\"\n\t\t\t\t\t\tclass=\"cn-tasks-widget__actions\"\n\t\t\t\t\t\t:forceMenu=\"true\"\n\t\t\t\t\t\t:aria-label=\"tr('Task actions')\"\n\t\t\t\t\t\t@click.stop>\n\t\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\t\tv-if=\"canClaim(task)\"\n\t\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t\t@click=\"claim(task)\">\n\t\t\t\t\t\t\t{{ tr('Claim') }}\n\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t<template v-if=\"canComplete(task)\">\n\t\t\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\t\t\tv-for=\"outcome in outcomesOf(task)\"\n\t\t\t\t\t\t\t\t:key=\"outcomeId(outcome)\"\n\t\t\t\t\t\t\t\t:closeAfterClick=\"true\"\n\t\t\t\t\t\t\t\t@click=\"complete(task, outcomeId(outcome))\">\n\t\t\t\t\t\t\t\t{{ completeLabel(outcome) }}\n\t\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcActions>\n\t\t\t\t</li>\n\t\t\t</ul>\n\t\t\t<!-- The remainder is a count, never a scrollbar (ADR-062). -->\n\t\t\t<p v-if=\"hiddenCount > 0\" class=\"cn-tasks-widget__more\">\n\t\t\t\t{{ moreLabel }}\n\t\t\t</p>\n\t\t</template>\n\t</div>\n</template>\n\n<script>\nimport { getCurrentUser } from '@nextcloud/auth'\nimport axios from '@nextcloud/axios'\nimport { showError } from '@nextcloud/dialogs'\nimport { translate as t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { NcActionButton, NcActions, NcLoadingIcon } from '@nextcloud/vue'\nimport { inject, ref } from 'vue'\nimport { taskDeepLink, taskDueLabel } from '../../composables/indexSources.js'\nimport { useEndpointSource } from '../../composables/useEndpointSource.js'\nimport { contentHeaderLink } from '../../utils/contentHeaderLink.js'\nimport { openRowTarget } from '../../utils/linkNavigation.js'\nimport { isRowMiddleClick, preventMiddleClickAutoscroll } from '../../utils/rowAuxClick.js'\n\n/**\n * The OpenRegister inbox read (openregister flow-task-entity). One endpoint\n * for the whole fleet: every app's human tasks live on OpenRegister's one\n * task store, so the widget is app-agnostic and ships no controller.\n *\n * @type {string}\n */\nconst FLOW_TASKS_URL = '/apps/openregister/api/flow-tasks'\n\n/**\n * The CMMN states, in the reading a dashboard needs. Labels double as the\n * meta-line words, so state never rests on styling.\n *\n * @type {Record<string, string>}\n */\nconst STATE_LABELS = {\n\tavailable: 'Available',\n\tenabled: 'Ready to claim',\n\tactive: 'In progress',\n\tcompleted: 'Completed',\n\tterminated: 'Terminated',\n\tdisabled: 'Disabled',\n}\n\n/**\n * The priority vocabulary (openregister Task::PRIORITIES).\n *\n * @type {Record<string, string>}\n */\nconst PRIORITY_LABELS = {\n\tlow: 'Low',\n\tnormal: 'Normal',\n\thigh: 'High',\n\turgent: 'Urgent',\n}\n\n/**\n * CnTasksWidget — the viewer's open tasks, from OpenRegister's task inbox.\n *\n * Reads `flow-tasks` with `isTerminal=false` (an inbox widget that showed\n * finished tasks would never drain), scoped by `content.scope` and sorted by\n * due date so the most urgent row sits on top. Placed by its registry type\n * key `tasks`:\n *\n * ```js\n * { id: 'my-tasks', type: 'tasks', title: 'My tasks',\n * content: { scope: 'assigned', limit: 6, pollSeconds: 30 } }\n * ```\n *\n * Why it polls: tasks are handed out, claimed and completed by OTHER people\n * and by flows, so a widget that only reflected its mount moment would be\n * wrong within minutes and look identical to being right. Polling pauses\n * while the tab is hidden.\n *\n * Quick actions offer only what the row's contract can accept: claim on a\n * pooled row, complete (per declared outcome) on the viewer's own open row.\n * The server still authorizes; a refusal surfaces its `error` message as a\n * toast and the widget refetches, so a lost claim race corrects the row.\n *\n * @spec openspec/changes/cn-tasks-entity-source/specs/cn-tasks-entity-source/spec.md\n */\nexport default {\n\tname: 'CnTasksWidget',\n\n\tcomponents: {\n\t\tNcActions,\n\t\tNcActionButton,\n\t\tNcLoadingIcon,\n\t},\n\n\tinject: {\n\t\t/**\n\t\t * Host translate function, provided by CnAppRoot (identity by default)\n\t\t * so a library string localises through the consuming app's l10n.\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The widget's persisted configuration blob.\n\t\t *\n\t\t * - `scope` — whose relationship to list: `assigned` (default),\n\t\t * `pooled`, `watched` or `all`. Whose INBOX it is stays the\n\t\t * endpoint's decision; scope never names another user.\n\t\t * - `limit` — rows to request AND show (1–50, default 6). The count\n\t\t * line always states the server total regardless of this cap.\n\t\t * - `pollSeconds` — refetch interval (default 30; `0` disables).\n\t\t * - `rowRoute` — vue-router route NAME to open on a row click,\n\t\t * receiving the task uuid as `:id`. Omitted = the click opens the\n\t\t * task's openregister deep link instead.\n\t\t * - `emptyText` — override for the empty-inbox line.\n\t\t * - `variant` — `list` draws a checkbox list: a checkbox that completes\n\t\t * the viewer's own task, the title, the subject on a second muted\n\t\t * line and the due date at its right. Default: the compact rows.\n\t\t * - `viewAllRoute` / `viewAllLabel` — a \"View all\" text link in the\n\t\t * card header: a route name or location `{ name, params?, query? }`,\n\t\t * and its label (default \"View all\"). `headerLink` works too.\n\t\t *\n\t\t * @type {{scope?: string, limit?: number, pollSeconds?: number, rowRoute?: string, emptyText?: string, variant?: string, viewAllRoute?: (string|object), viewAllLabel?: string}}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Widget placement id, so the per-widget Refresh action\n\t\t * (`cn:widget:refresh`) reaches this instance's fetch.\n\t\t */\n\t\twidgetId: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Translate function. Falls back to the injected `cnTranslate`.\n\t\t * Provide explicitly when mounting outside a CnAppRoot ancestor.\n\t\t *\n\t\t * @type {((key: string) => string)|null}\n\t\t */\n\t\ttranslate: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: ['header'],\n\n\tsetup(props) {\n\t\t// The shared endpoint engine owns request dedup, the short-TTL cache\n\t\t// and the cn:page:refresh / cn:widget:refresh subscriptions — the\n\t\t// same binding CnFlowRunsWidget uses, so a page-level Refresh updates\n\t\t// this widget with everything else on the page.\n\t\tconst widgetIdRef = ref(props.widgetId)\n\t\tconst source = () => ({\n\t\t\turl: FLOW_TASKS_URL,\n\t\t\tmethod: 'GET',\n\t\t\tparams: {\n\t\t\t\tscope: normaliseScope(props.content && props.content.scope),\n\t\t\t\t// An inbox drains: finished tasks leave the widget. The index\n\t\t\t\t// page's \"everything\" tab is the place to read history.\n\t\t\t\tisTerminal: 'false',\n\t\t\t\t// Ascending due date: the most urgent (overdue first) on top.\n\t\t\t\tsort: 'dueAt',\n\t\t\t\tlimit: normaliseLimit(props.content && props.content.limit),\n\t\t\t},\n\t\t})\n\t\tconst { data, loading, error, refetch } = useEndpointSource(source, {\n\t\t\twidgetId: widgetIdRef,\n\t\t})\n\n\t\t// `inject` is re-read in setup so Vue 2.7 and Vue 3 resolve it\n\t\t// identically to the Options `inject` block (CnStatWidget precedent).\n\t\tconst injectedTranslate = inject('cnTranslate', null)\n\n\t\treturn {\n\t\t\tpayload: data,\n\t\t\tloading,\n\t\t\terror,\n\t\t\trefetch,\n\t\t\tinjectedTranslate,\n\t\t}\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tpollTimer: null,\n\t\t\t/** Uuids of the tasks a checkbox is completing, until the next read. */\n\t\t\tcompleting: [],\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Whether the rows draw as the checkbox list (`content.variant: 'list'`).\n\t\t *\n\t\t * @return {boolean}\n\t\t */\n\t\tisList() {\n\t\t\treturn this.content.variant === 'list'\n\t\t},\n\n\t\t/**\n\t\t * The header \"View all\" link the content declares, or null.\n\t\t *\n\t\t * @return {object|null}\n\t\t */\n\t\theaderLink() {\n\t\t\treturn contentHeaderLink(this.content)\n\t\t},\n\n\t\t/**\n\t\t * Effective translate function: the explicit prop, then the injected\n\t\t * host one, then `@nextcloud/l10n` under the library's own app id.\n\t\t *\n\t\t * @return {(key: string) => string}\n\t\t */\n\t\ttr() {\n\t\t\tconst fn = this.translate ?? this.injectedTranslate ?? this.cnTranslate\n\t\t\tif (typeof fn === 'function') {\n\t\t\t\treturn (key) => fn(key)\n\t\t\t}\n\t\t\treturn (key) => t('nextcloud-vue', key)\n\t\t},\n\n\t\t/**\n\t\t * The signed-in viewer's uid, for the per-row action offer. Empty\n\t\t * outside a Nextcloud session (the offer then collapses to claim on\n\t\t * pooled rows only, which is the safe reading).\n\t\t *\n\t\t * @return {string} The uid, or ''.\n\t\t */\n\t\tviewerUid() {\n\t\t\treturn getCurrentUser()?.uid || ''\n\t\t},\n\n\t\t/**\n\t\t * The rows to render — the endpoint's bounded page, defensively\n\t\t * re-capped so a wider server default can never overflow the cell.\n\t\t *\n\t\t * @return {Array<object>} The task rows.\n\t\t */\n\t\trows() {\n\t\t\tconst results = (this.payload && this.payload.results) || []\n\t\t\tif (Array.isArray(results) === false) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn results.slice(0, normaliseLimit(this.content.limit))\n\t\t},\n\n\t\t/**\n\t\t * The server's honest total, independent of the rendered page.\n\t\t *\n\t\t * @return {number} The open-task total (never negative).\n\t\t */\n\t\ttotal() {\n\t\t\tconst total = Number((this.payload && this.payload.total) ?? this.rows.length)\n\t\t\tif (Number.isFinite(total) === false) {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\treturn Math.max(0, total)\n\t\t},\n\n\t\t/** The count line above the rows. */\n\t\tcountLabel() {\n\t\t\tif (this.total === 1) {\n\t\t\t\treturn this.tr('1 open task')\n\t\t\t}\n\t\t\treturn this.tr('{count} open tasks').replace('{count}', String(this.total))\n\t\t},\n\n\t\t/**\n\t\t * How many open tasks exist beyond the rendered rows.\n\t\t *\n\t\t * @return {number} The hidden task count (never negative).\n\t\t */\n\t\thiddenCount() {\n\t\t\treturn Math.max(0, this.total - this.rows.length)\n\t\t},\n\n\t\t/** The \"+N more\" line. */\n\t\tmoreLabel() {\n\t\t\treturn this.tr('+{count} more').replace('{count}', String(this.hiddenCount))\n\t\t},\n\n\t\t/** The empty-inbox line. */\n\t\temptyLabel() {\n\t\t\tconst override = this.content.emptyText\n\t\t\tif (typeof override === 'string' && override !== '') {\n\t\t\t\treturn this.tr(override)\n\t\t\t}\n\t\t\treturn this.tr('No open tasks')\n\t\t},\n\n\t\t/**\n\t\t * Poll interval in ms, or 0 when polling is off. Floored at 5s so a\n\t\t * mis-authored `pollSeconds: 0.1` cannot turn a dashboard into a\n\t\t * request storm; `0` remains an explicit opt-out.\n\t\t *\n\t\t * @return {number} The interval in milliseconds.\n\t\t */\n\t\tpollMs() {\n\t\t\tconst raw = this.content.pollSeconds\n\t\t\tconst seconds = (raw === undefined || raw === null) ? 30 : Number(raw)\n\t\t\tif (Number.isFinite(seconds) === false || seconds <= 0) {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\treturn Math.max(5, seconds) * 1000\n\t\t},\n\t},\n\n\twatch: {\n\t\theaderLink: {\n\t\t\timmediate: true,\n\t\t\thandler(link) {\n\t\t\t\t/**\n\t\t\t\t * @event header Emitted with the card header's parts: the \"View all\" link, if any.\n\t\t\t\t * @type {{link: (object|null), note: string}}\n\t\t\t\t */\n\t\t\t\tthis.$emit('header', { link, note: '' })\n\t\t\t},\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.startPolling()\n\t\tdocument.addEventListener('visibilitychange', this.onVisibilityChange)\n\t},\n\n\tbeforeUnmount() {\n\t\tthis.stopPolling()\n\t\tdocument.removeEventListener('visibilitychange', this.onVisibilityChange)\n\t},\n\n\tmethods: {\n\t\tpreventMiddleClickAutoscroll,\n\n\t\t/**\n\t\t * Start (or restart) the refetch interval, unless polling is off.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tstartPolling() {\n\t\t\tthis.stopPolling()\n\t\t\tif (this.pollMs === 0 || document.hidden === true) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.pollTimer = setInterval(() => {\n\t\t\t\tthis.refetch(true)\n\t\t\t}, this.pollMs)\n\t\t},\n\n\t\t/**\n\t\t * Clear the refetch interval.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tstopPolling() {\n\t\t\tif (this.pollTimer !== null) {\n\t\t\t\tclearInterval(this.pollTimer)\n\t\t\t\tthis.pollTimer = null\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Pause polling while the tab is hidden; refetch once and resume on\n\t\t * return, so the dashboard shows current work rather than whatever\n\t\t * was true when the tab was backgrounded.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tonVisibilityChange() {\n\t\t\tif (document.hidden === true) {\n\t\t\t\tthis.stopPolling()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.refetch(true)\n\t\t\tthis.startPolling()\n\t\t},\n\n\t\t/**\n\t\t * The row's display title (server-synthesized when untitled).\n\t\t *\n\t\t * @param {object} task The task row.\n\t\t * @return {string} The title.\n\t\t */\n\t\ttitleOf(task) {\n\t\t\treturn task.displayTitle || task.title || String(task.uuid || '')\n\t\t},\n\n\t\t/**\n\t\t * The subject (the case a task sits on), for the list variant's second line.\n\t\t *\n\t\t * @param {object} task The task row.\n\t\t * @return {string} The subject's title, or ''.\n\t\t */\n\t\tsubjectOf(task) {\n\t\t\treturn task.subject && task.subject.title ? String(task.subject.title) : ''\n\t\t},\n\n\t\t/**\n\t\t * Whether the list variant draws a checkbox on the row: the viewer's own\n\t\t * open task, with no outcome list to choose from.\n\t\t *\n\t\t * @param {object} task The task row.\n\t\t * @return {boolean}\n\t\t */\n\t\tcanCheck(task) {\n\t\t\treturn this.canComplete(task) && !(Array.isArray(task.outcomes) && task.outcomes.length > 0)\n\t\t},\n\n\t\t/**\n\t\t * Complete a task from its checkbox, with the server's default outcome.\n\t\t *\n\t\t * @param {object} task The task row.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync check(task) {\n\t\t\tconst uuid = String(task.uuid || '')\n\t\t\tif (uuid === '' || this.completing.includes(uuid)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.completing = [...this.completing, uuid]\n\t\t\tawait this.postVerb(task, 'complete', { outcome: 'done' })\n\t\t\tthis.completing = this.completing.filter((id) => id !== uuid)\n\t\t},\n\n\t\t/**\n\t\t * The secondary line: subject, state and priority — whichever of\n\t\t * those the row actually carries, in words.\n\t\t *\n\t\t * @param {object} task The task row.\n\t\t * @return {string} The meta line.\n\t\t */\n\t\tmetaLine(task) {\n\t\t\tconst parts = []\n\t\t\tif (task.subject && task.subject.title) {\n\t\t\t\tparts.push(String(task.subject.title))\n\t\t\t}\n\t\t\tconst state = STATE_LABELS[task.state]\n\t\t\tparts.push(state ? this.tr(state) : String(task.state || ''))\n\t\t\tconst priority = PRIORITY_LABELS[task.priority]\n\t\t\tif (priority && task.priority !== 'normal') {\n\t\t\t\tparts.push(this.tr(priority))\n\t\t\t}\n\t\t\treturn parts.join(' · ')\n\t\t},\n\n\t\t/**\n\t\t * The due wording for a row, from the server's derived projection.\n\t\t *\n\t\t * @param {object} task The task row.\n\t\t * @return {string} The due label, or '' without a due date.\n\t\t */\n\t\tdueLabel(task) {\n\t\t\treturn taskDueLabel(task)\n\t\t},\n\n\t\t/**\n\t\t * Whether the row offers claim: pooled (no assignee) and still open.\n\t\t *\n\t\t * @param {object} task The task row.\n\t\t * @return {boolean} True when claim is offered.\n\t\t */\n\t\tcanClaim(task) {\n\t\t\treturn !task.assignee && task.isTerminal !== true\n\t\t},\n\n\t\t/**\n\t\t * Whether the row offers complete: the viewer's own open task.\n\t\t *\n\t\t * @param {object} task The task row.\n\t\t * @return {boolean} True when complete is offered.\n\t\t */\n\t\tcanComplete(task) {\n\t\t\treturn this.viewerUid !== ''\n\t\t\t\t&& task.assignee === this.viewerUid\n\t\t\t\t&& task.isTerminal !== true\n\t\t},\n\n\t\t/**\n\t\t * The outcomes to offer on complete: the row's declared list, else\n\t\t * one entry for the server's default outcome.\n\t\t *\n\t\t * @param {object} task The task row.\n\t\t * @return {Array<object|string>} The outcome entries.\n\t\t */\n\t\toutcomesOf(task) {\n\t\t\tif (Array.isArray(task.outcomes) && task.outcomes.length > 0) {\n\t\t\t\treturn task.outcomes\n\t\t\t}\n\t\t\treturn ['done']\n\t\t},\n\n\t\t/**\n\t\t * The wire value of one outcome entry (a string, or `{id, label}`).\n\t\t *\n\t\t * @param {object|string} outcome The outcome entry.\n\t\t * @return {string} The outcome id.\n\t\t */\n\t\toutcomeId(outcome) {\n\t\t\tif (outcome && typeof outcome === 'object') {\n\t\t\t\treturn String(outcome.id ?? outcome.value ?? outcome.label ?? '')\n\t\t\t}\n\t\t\treturn String(outcome ?? '')\n\t\t},\n\n\t\t/**\n\t\t * The menu label for one complete entry.\n\t\t *\n\t\t * @param {object|string} outcome The outcome entry.\n\t\t * @return {string} The label.\n\t\t */\n\t\tcompleteLabel(outcome) {\n\t\t\tconst id = this.outcomeId(outcome)\n\t\t\tconst label = (outcome && typeof outcome === 'object' && outcome.label)\n\t\t\t\t? String(outcome.label)\n\t\t\t\t: id\n\t\t\tif (id === 'done') {\n\t\t\t\treturn this.tr('Complete')\n\t\t\t}\n\t\t\treturn this.tr('Complete: {outcome}').replace('{outcome}', label)\n\t\t},\n\n\t\t/**\n\t\t * Claim a pooled task.\n\t\t *\n\t\t * @param {object} task The task row.\n\t\t * @return {Promise<void>} Resolves when the verb settled.\n\t\t */\n\t\tclaim(task) {\n\t\t\treturn this.postVerb(task, 'claim', {})\n\t\t},\n\n\t\t/**\n\t\t * Complete the viewer's own task with an outcome.\n\t\t *\n\t\t * @param {object} task The task row.\n\t\t * @param {string} outcome The chosen outcome id.\n\t\t * @return {Promise<void>} Resolves when the verb settled.\n\t\t */\n\t\tcomplete(task, outcome) {\n\t\t\treturn this.postVerb(task, 'complete', { outcome })\n\t\t},\n\n\t\t/**\n\t\t * POST one lifecycle verb and refetch either way: on success the row\n\t\t * moved, and on a refusal (a lost claim race, a revoked task) the\n\t\t * list is stale in exactly the way the refusal proves. The refusal\n\t\t * itself is toasted in the SERVER'S words (ADR-062 forbids leaking\n\t\t * the raw status line, and the endpoint's `error` string is written\n\t\t * for people).\n\t\t *\n\t\t * @param {object} task The task row.\n\t\t * @param {string} verb The lifecycle verb.\n\t\t * @param {object} body The verb body.\n\t\t * @return {Promise<void>} Resolves when the verb settled.\n\t\t */\n\t\tasync postVerb(task, verb, body) {\n\t\t\tconst uuid = String(task.uuid || '')\n\t\t\tif (uuid === '') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait axios.post(generateUrl(`${FLOW_TASKS_URL}/${uuid}/${verb}`), body)\n\t\t\t} catch (error) {\n\t\t\t\tconst message = error?.response?.data?.error\n\t\t\t\tshowError(typeof message === 'string' && message !== ''\n\t\t\t\t\t? message\n\t\t\t\t\t: this.tr('The task action was refused'))\n\t\t\t}\n\t\t\tthis.refetch(true)\n\t\t},\n\n\t\t/**\n\t\t * Open the clicked task: the configured route with the uuid as `id`,\n\t\t * else the task's openregister deep link. A ctrl/cmd/shift or middle\n\t\t * click opens it in a new tab.\n\t\t *\n\t\t * @param {object} task The clicked task row.\n\t\t * @param {MouseEvent} [event] The originating click/auxclick event.\n\t\t * @return {void}\n\t\t */\n\t\tonRowClick(task, event) {\n\t\t\tconst uuid = String(task.uuid || '')\n\t\t\tif (uuid === '') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst rowRoute = this.content.rowRoute\n\t\t\tif (typeof rowRoute === 'string' && rowRoute !== '' && this.$router) {\n\t\t\t\topenRowTarget(event, { name: rowRoute, params: { id: uuid } }, this.$router)\n\t\t\t\treturn\n\t\t\t}\n\t\t\topenRowTarget(event, taskDeepLink(uuid))\n\t\t},\n\n\t\t/**\n\t\t * Middle click on a task row: open it in a new tab.\n\t\t *\n\t\t * @param {object} task The clicked task row.\n\t\t * @param {MouseEvent} event The auxclick event.\n\t\t * @return {void}\n\t\t */\n\t\tonRowAuxClick(task, event) {\n\t\t\tif (isRowMiddleClick(event)) {\n\t\t\t\tthis.onRowClick(task, event)\n\t\t\t}\n\t\t},\n\t},\n}\n\n/**\n * Clamp a configured row limit into the range the endpoint accepts.\n *\n * @param {unknown} raw The configured limit.\n * @return {number} A limit between 1 and 50 (default 6).\n */\nfunction normaliseLimit(raw) {\n\tconst n = Number(raw)\n\tif (Number.isFinite(n) === false || n <= 0) {\n\t\treturn 6\n\t}\n\treturn Math.min(50, Math.round(n))\n}\n\n/**\n * Clamp a configured scope onto the endpoint's vocabulary.\n *\n * @param {unknown} raw The configured scope.\n * @return {string} One of assigned, pooled, watched, all (default assigned).\n */\nfunction normaliseScope(raw) {\n\treturn ['assigned', 'pooled', 'watched', 'all'].includes(raw) ? raw : 'assigned'\n}\n</script>\n\n<style scoped>\n.cn-tasks-widget {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 2px;\n\tmin-width: 0;\n}\n\n.cn-tasks-widget__loading {\n\tdisplay: flex;\n\tjustify-content: center;\n\tpadding: 8px 0;\n}\n\n.cn-tasks-widget__empty,\n.cn-tasks-widget__error,\n.cn-tasks-widget__more {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 13px;\n}\n\n.cn-tasks-widget__more {\n\tpadding-top: 4px;\n}\n\n.cn-tasks-widget__count {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 12px;\n\tfont-weight: 600;\n\ttext-transform: uppercase;\n\tletter-spacing: 0.04em;\n}\n\n.cn-tasks-widget__list {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tmin-width: 0;\n}\n\n.cn-tasks-widget__row {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 10px;\n\tpadding: 6px 0;\n\tborder-bottom: 1px solid var(--color-border);\n\tmin-width: 0;\n\tcursor: pointer;\n}\n\n.cn-tasks-widget__row:last-child {\n\tborder-bottom: none;\n}\n\n.cn-tasks-widget__body {\n\tdisplay: flex;\n\tflex-direction: column;\n\tmin-width: 0;\n\tflex: 1 1 auto;\n}\n\n.cn-tasks-widget__name {\n\tfont-weight: 600;\n\twhite-space: nowrap;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n.cn-tasks-widget__row:hover .cn-tasks-widget__name {\n\ttext-decoration: underline;\n}\n\n.cn-tasks-widget__meta {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 12px;\n\twhite-space: nowrap;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n.cn-tasks-widget__due {\n\tflex: 0 0 auto;\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 12px;\n\tfont-variant-numeric: tabular-nums;\n}\n\n/* List variant, after the dashboard board's My tasks card: a checkbox, the\n title, the subject on a muted second line and the due date at its right. */\n.cn-tasks-widget--list .cn-tasks-widget__row {\n\talign-items: flex-start;\n\tgap: 12px;\n\tpadding: 12px 0;\n}\n\n.cn-tasks-widget--list .cn-tasks-widget__name {\n\tfont-weight: 400;\n\tfont-size: 15px;\n\tline-height: 1.35;\n\tcolor: var(--color-main-text);\n}\n\n.cn-tasks-widget__check,\n.cn-tasks-widget__check-spacer {\n\tflex: none;\n\twidth: 20px;\n\theight: 20px;\n\tmargin: 1px 0 0;\n}\n\n.cn-tasks-widget__check {\n\taccent-color: var(--color-primary-element);\n\tcursor: pointer;\n}\n\n.cn-tasks-widget__meta--split {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tfont-size: 13px;\n\toverflow: visible;\n}\n\n.cn-tasks-widget__subject {\n\tmin-width: 0;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n}\n\n.cn-tasks-widget--list .cn-tasks-widget__due {\n\tfont-size: 13px;\n\tfont-weight: 500;\n}\n\n.cn-tasks-widget--list .cn-tasks-widget__due--overdue {\n\tfont-weight: 600;\n}\n\n/* Overdue: weight AND colour on top of the wording, which already says\n \"Overdue by N days\" — the signal survives monochrome. */\n.cn-tasks-widget__due--overdue {\n\tcolor: var(--color-error, #d91f2d);\n\tfont-weight: 600;\n}\n\n.cn-tasks-widget__actions {\n\tflex: 0 0 auto;\n}\n</style>\n"],"names":["_normalizeClass","_toDisplayString","_openBlock","_createElementBlock","_createVNode","_Fragment","_renderList","_createElementVNode","_createBlock","_createTextVNode"],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import script from './CnToggleTableWidget.vue2.js';
2
+ import { render } from './CnToggleTableWidget.vue3.js';
3
+
4
+ script.render = render;
5
+ script.__scopeId = "data-v-53a57112";
6
+ script.__file = "src/components/CnToggleTableWidget/CnToggleTableWidget.vue";
7
+ const __sfc_main = script;
8
+
9
+ export { __sfc_main as default };
10
+ //# sourceMappingURL=CnToggleTableWidget.vue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CnToggleTableWidget.vue.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;"}
@@ -0,0 +1,269 @@
1
+ import { NcCheckboxRadioSwitch, NcNoteCard } from '@nextcloud/vue';
2
+ import { prefixUrl } from '../../utils/headers.js';
3
+ import { objectUrlOf, patchObject } from '../../utils/objectWrite.js';
4
+ import { readPath } from '../../utils/readPath.js';
5
+ import { serverErrorMessage } from '../../utils/serverErrorMessage.js';
6
+
7
+ /**
8
+ * CnToggleTableWidget — the `toggle-table` widget: rows from a configured list
9
+ * (`rows`) or read from a source (`source`), each with a switch bound to the
10
+ * CURRENT object, and optional extra columns read off the row. A switch is
11
+ * stored in one of two ways:
12
+ *
13
+ * - `bind.mode: 'flag'` — each row names its own boolean field of the object
14
+ * (`row.field`); the switch is that field.
15
+ * - `bind.mode: 'list'` — ONE array field of the object (`bind.field`) holds
16
+ * the keys of the rows that are in it; the switch is on when the row's key is
17
+ * in the list, or NOT in it with `bind.invert` (a list of hidden items shown
18
+ * as "visible" switches).
19
+ *
20
+ * A toggle saves at once with ONE PATCH of that field; a refused PATCH puts the
21
+ * switch back and shows the server's words.
22
+ *
23
+ * @spec openspec/changes/abstract-toggle-table/specs/toggle-table/spec.md
24
+ */
25
+ var script = {
26
+ name: 'CnToggleTableWidget',
27
+
28
+ components: { NcCheckboxRadioSwitch, NcNoteCard },
29
+
30
+ inject: {
31
+ cnTranslate: { default: () => (key) => key },
32
+ },
33
+
34
+ props: {
35
+ /**
36
+ * The widget configuration.
37
+ *
38
+ * - `bind` — `{ mode: 'flag' }` (default) or `{ mode: 'list', field, invert? }`.
39
+ * - `rows` — static rows `[{ key, label, hint?, field?, ...extra }]`.
40
+ * - `source` — `{ url, path?, key, label, hint? }`: rows read from an endpoint (tokens `@objectId`, `@object.<field>`), in addition to `rows`.
41
+ * - `columns` — extra columns `[{ header, path, map? }]` read off each row.
42
+ * - `titleHeader`, `toggleHeader`, `emptyText`, `readOnly`.
43
+ *
44
+ * @type {object}
45
+ */
46
+ content: {
47
+ type: Object,
48
+ default: () => ({}),
49
+ },
50
+
51
+ /** The current detail-page object. */
52
+ objectData: {
53
+ type: Object,
54
+ default: null,
55
+ },
56
+
57
+ /** The current object id. */
58
+ objectId: {
59
+ type: [String, Number],
60
+ default: '',
61
+ },
62
+
63
+ /** OpenRegister register slug of the surface. */
64
+ register: {
65
+ type: [String, Object],
66
+ default: '',
67
+ },
68
+
69
+ /** OpenRegister schema slug of the surface. */
70
+ schema: {
71
+ type: [String, Object],
72
+ default: '',
73
+ },
74
+ },
75
+
76
+ emits: ['saved'],
77
+
78
+ data() {
79
+ return {
80
+ /** Rows read from `source`. */
81
+ sourced: [],
82
+ /** What the object holds, by field: the flags and the list, as last saved. */
83
+ saved: {},
84
+ busy: '',
85
+ notice: '',
86
+ /** Per row, how often a refused toggle forced the switch to be drawn again from the stored value. */
87
+ undone: {},
88
+ }
89
+ },
90
+
91
+ computed: {
92
+ bind() {
93
+ const bind = this.content.bind || {};
94
+ return bind.mode === 'list' ? { mode: 'list', field: bind.field, invert: bind.invert === true } : { mode: 'flag' }
95
+ },
96
+
97
+ /** The drawn rows: configured first, then sourced, each with a key, title and hint. */
98
+ rows() {
99
+ const configured = (Array.isArray(this.content.rows) ? this.content.rows : [])
100
+ .filter((row) => row && row.key !== undefined)
101
+ .map((row) => ({ ...row, key: String(row.key), title: this.tr(row.label || String(row.key)), hint: this.tr(row.hint || '') }));
102
+ return [...configured, ...this.sourced]
103
+ },
104
+
105
+ columns() {
106
+ return (Array.isArray(this.content.columns) ? this.content.columns : [])
107
+ .filter((column) => column && column.path)
108
+ .map((column) => ({ ...column, header: this.tr(column.header || '') }))
109
+ },
110
+
111
+ showHeader() {
112
+ return this.columns.length > 0 || Boolean(this.content.titleHeader || this.content.toggleHeader)
113
+ },
114
+
115
+ titleHeader() {
116
+ return this.tr(this.content.titleHeader || '')
117
+ },
118
+
119
+ toggleHeader() {
120
+ return this.tr(this.content.toggleHeader || '')
121
+ },
122
+
123
+ emptyLabel() {
124
+ return this.tr(this.content.emptyText || 'Nothing listed yet')
125
+ },
126
+
127
+ readOnly() {
128
+ return this.content.readOnly === true
129
+ },
130
+
131
+ objectUrl() {
132
+ return objectUrlOf(this.register, this.schema, this.objectId || (this.objectData && this.objectData.id))
133
+ },
134
+ },
135
+
136
+ watch: {
137
+ objectData() {
138
+ this.saved = {};
139
+ this.loadSource();
140
+ },
141
+ },
142
+
143
+ created() {
144
+ this.loadSource();
145
+ },
146
+
147
+ methods: {
148
+ tr(text) {
149
+ if (typeof text !== 'string' || text === '') {
150
+ return ''
151
+ }
152
+ return typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text
153
+ },
154
+
155
+ /**
156
+ * The stored value of a field: what this widget last saved, else the object's.
157
+ *
158
+ * @param {string} field The field.
159
+ * @return {unknown}
160
+ */
161
+ stored(field) {
162
+ return Object.hasOwn(this.saved, field) ? this.saved[field] : readPath(this.objectData || {}, field)
163
+ },
164
+
165
+ /**
166
+ * Whether a row's switch is on.
167
+ *
168
+ * @param {object} row The row.
169
+ * @return {boolean}
170
+ */
171
+ isOn(row) {
172
+ if (this.bind.mode === 'list') {
173
+ const list = this.stored(this.bind.field);
174
+ const inList = Array.isArray(list) && list.map(String).includes(row.key);
175
+ return this.bind.invert ? !inList : inList
176
+ }
177
+ return row.field ? this.stored(row.field) === true : false
178
+ },
179
+
180
+ /**
181
+ * The text of an extra column for a row.
182
+ *
183
+ * @param {object} row The row.
184
+ * @param {object} column The column.
185
+ * @return {string}
186
+ */
187
+ cell(row, column) {
188
+ const raw = readPath(row, column.path);
189
+ if (raw === undefined || raw === null || raw === '') {
190
+ return ''
191
+ }
192
+ if (column.map && Object.hasOwn(column.map, String(raw))) {
193
+ return this.tr(column.map[String(raw)])
194
+ }
195
+ return typeof raw === 'object' ? '' : String(raw)
196
+ },
197
+
198
+ /** Read the rows of `source`, resolving tokens in its url. */
199
+ async loadSource() {
200
+ const source = this.content.source;
201
+ if (!source || !source.url || !source.key) {
202
+ return
203
+ }
204
+ const object = this.objectData || {};
205
+ const url = String(source.url)
206
+ .replace(/@objectId/g, () => encodeURIComponent(String(this.objectId || object.id || '')))
207
+ .replace(/@object\.([\w.@]+)/g, (m, path) => encodeURIComponent(String(readPath(object, path) ?? '')));
208
+ try {
209
+ const { default: axios } = await import('@nextcloud/axios');
210
+ const { data } = await axios.get(prefixUrl(url));
211
+ const list = source.path ? readPath(data, source.path) : data;
212
+ this.sourced = (Array.isArray(list) ? list : [])
213
+ .filter((item) => item && readPath(item, source.key) !== undefined)
214
+ .map((item) => ({
215
+ ...item,
216
+ key: String(readPath(item, source.key)),
217
+ title: String(readPath(item, source.label || source.key) ?? readPath(item, source.key)),
218
+ hint: source.hint ? String(readPath(item, source.hint) ?? '') : '',
219
+ }));
220
+ } catch (e) {
221
+ this.notice = serverErrorMessage(e);
222
+ }
223
+ },
224
+
225
+ /**
226
+ * Flip one switch and save that one field.
227
+ *
228
+ * @param {object} row The row.
229
+ * @param {boolean} on The new switch state.
230
+ * @return {Promise<void>}
231
+ * @spec openspec/changes/abstract-toggle-table/specs/toggle-table/spec.md#requirement-a-toggle-saves-one-field
232
+ */
233
+ async toggle(row, on) {
234
+ const field = this.bind.mode === 'list' ? this.bind.field : row.field;
235
+ if (!this.objectUrl || !field) {
236
+ this.notice = this.tr('This switch cannot be saved here.');
237
+ return
238
+ }
239
+ let next;
240
+ if (this.bind.mode === 'list') {
241
+ const list = (Array.isArray(this.stored(field)) ? this.stored(field) : []).map(String);
242
+ const wantInList = this.bind.invert ? !on : on;
243
+ next = wantInList ? (list.includes(row.key) ? list : [...list, row.key]) : list.filter((key) => key !== row.key);
244
+ } else {
245
+ next = on;
246
+ }
247
+ this.notice = '';
248
+ this.busy = row.key;
249
+ try {
250
+ const data = await patchObject(this.objectUrl, { [field]: next });
251
+ const written = data[field] !== undefined ? data[field] : next;
252
+ this.saved = { ...this.saved, [field]: written };
253
+ /**
254
+ * @event saved A switch was written.
255
+ * @type {{field: string, value: unknown}}
256
+ */
257
+ this.$emit('saved', { field, value: written });
258
+ } catch (e) {
259
+ this.notice = serverErrorMessage(e);
260
+ this.undone = { ...this.undone, [row.key]: (this.undone[row.key] || 0) + 1 };
261
+ } finally {
262
+ this.busy = '';
263
+ }
264
+ },
265
+ },
266
+ };
267
+
268
+ export { script as default };
269
+ //# sourceMappingURL=CnToggleTableWidget.vue2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CnToggleTableWidget.vue2.js","sources":["../../../../src/components/CnToggleTableWidget/CnToggleTableWidget.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div class=\"cn-toggle-table\" data-testid=\"cn-toggle-table\">\n\t\t<p v-if=\"rows.length === 0\" class=\"cn-toggle-table__empty\" data-testid=\"cn-toggle-table-empty\">\n\t\t\t{{ emptyLabel }}\n\t\t</p>\n\t\t<table v-else class=\"cn-toggle-table__table\">\n\t\t\t<thead v-if=\"showHeader\">\n\t\t\t\t<tr>\n\t\t\t\t\t<th scope=\"col\" class=\"cn-toggle-table__head\">\n\t\t\t\t\t\t{{ titleHeader }}\n\t\t\t\t\t</th>\n\t\t\t\t\t<th\n\t\t\t\t\t\tv-for=\"column in columns\"\n\t\t\t\t\t\t:key=\"column.path\"\n\t\t\t\t\t\tscope=\"col\"\n\t\t\t\t\t\tclass=\"cn-toggle-table__head\">\n\t\t\t\t\t\t{{ column.header }}\n\t\t\t\t\t</th>\n\t\t\t\t\t<th scope=\"col\" class=\"cn-toggle-table__head cn-toggle-table__head--toggle\">\n\t\t\t\t\t\t{{ toggleHeader }}\n\t\t\t\t\t</th>\n\t\t\t\t</tr>\n\t\t\t</thead>\n\t\t\t<tbody>\n\t\t\t\t<tr\n\t\t\t\t\tv-for=\"row in rows\"\n\t\t\t\t\t:key=\"row.key\"\n\t\t\t\t\tclass=\"cn-toggle-table__row\"\n\t\t\t\t\t:data-testid=\"`cn-toggle-table-row-${row.key}`\">\n\t\t\t\t\t<td class=\"cn-toggle-table__cell cn-toggle-table__cell--main\">\n\t\t\t\t\t\t<span class=\"cn-toggle-table__title\">{{ row.title }}</span>\n\t\t\t\t\t\t<span v-if=\"row.hint\" class=\"cn-toggle-table__hint\">{{ row.hint }}</span>\n\t\t\t\t\t</td>\n\t\t\t\t\t<td\n\t\t\t\t\t\tv-for=\"column in columns\"\n\t\t\t\t\t\t:key=\"column.path\"\n\t\t\t\t\t\tclass=\"cn-toggle-table__cell\">\n\t\t\t\t\t\t{{ cell(row, column) }}\n\t\t\t\t\t</td>\n\t\t\t\t\t<td class=\"cn-toggle-table__cell cn-toggle-table__cell--toggle\">\n\t\t\t\t\t\t<NcCheckboxRadioSwitch\n\t\t\t\t\t\t\t:key=\"`${row.key}-${undone[row.key] || 0}`\"\n\t\t\t\t\t\t\t:modelValue=\"isOn(row)\"\n\t\t\t\t\t\t\ttype=\"switch\"\n\t\t\t\t\t\t\t:disabled=\"readOnly || busy === row.key\"\n\t\t\t\t\t\t\t:aria-label=\"row.title\"\n\t\t\t\t\t\t\t@update:modelValue=\"toggle(row, $event)\" />\n\t\t\t\t\t</td>\n\t\t\t\t</tr>\n\t\t\t</tbody>\n\t\t</table>\n\t\t<NcNoteCard v-if=\"notice\" type=\"error\" data-testid=\"cn-toggle-table-notice\">\n\t\t\t{{ notice }}\n\t\t</NcNoteCard>\n\t</div>\n</template>\n\n<script>\nimport { NcCheckboxRadioSwitch, NcNoteCard } from '@nextcloud/vue'\nimport { prefixUrl } from '../../utils/headers.js'\nimport { objectUrlOf, patchObject } from '../../utils/objectWrite.js'\nimport { readPath } from '../../utils/readPath.js'\nimport { serverErrorMessage } from '../../utils/serverErrorMessage.js'\n\n/**\n * CnToggleTableWidget — the `toggle-table` widget: rows from a configured list\n * (`rows`) or read from a source (`source`), each with a switch bound to the\n * CURRENT object, and optional extra columns read off the row. A switch is\n * stored in one of two ways:\n *\n * - `bind.mode: 'flag'` — each row names its own boolean field of the object\n * (`row.field`); the switch is that field.\n * - `bind.mode: 'list'` — ONE array field of the object (`bind.field`) holds\n * the keys of the rows that are in it; the switch is on when the row's key is\n * in the list, or NOT in it with `bind.invert` (a list of hidden items shown\n * as \"visible\" switches).\n *\n * A toggle saves at once with ONE PATCH of that field; a refused PATCH puts the\n * switch back and shows the server's words.\n *\n * @spec openspec/changes/abstract-toggle-table/specs/toggle-table/spec.md\n */\nexport default {\n\tname: 'CnToggleTableWidget',\n\n\tcomponents: { NcCheckboxRadioSwitch, NcNoteCard },\n\n\tinject: {\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The widget configuration.\n\t\t *\n\t\t * - `bind` — `{ mode: 'flag' }` (default) or `{ mode: 'list', field, invert? }`.\n\t\t * - `rows` — static rows `[{ key, label, hint?, field?, ...extra }]`.\n\t\t * - `source` — `{ url, path?, key, label, hint? }`: rows read from an endpoint (tokens `@objectId`, `@object.<field>`), in addition to `rows`.\n\t\t * - `columns` — extra columns `[{ header, path, map? }]` read off each row.\n\t\t * - `titleHeader`, `toggleHeader`, `emptyText`, `readOnly`.\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** The current detail-page object. */\n\t\tobjectData: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The current object id. */\n\t\tobjectId: {\n\t\t\ttype: [String, Number],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** OpenRegister register slug of the surface. */\n\t\tregister: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** OpenRegister schema slug of the surface. */\n\t\tschema: {\n\t\t\ttype: [String, Object],\n\t\t\tdefault: '',\n\t\t},\n\t},\n\n\temits: ['saved'],\n\n\tdata() {\n\t\treturn {\n\t\t\t/** Rows read from `source`. */\n\t\t\tsourced: [],\n\t\t\t/** What the object holds, by field: the flags and the list, as last saved. */\n\t\t\tsaved: {},\n\t\t\tbusy: '',\n\t\t\tnotice: '',\n\t\t\t/** Per row, how often a refused toggle forced the switch to be drawn again from the stored value. */\n\t\t\tundone: {},\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tbind() {\n\t\t\tconst bind = this.content.bind || {}\n\t\t\treturn bind.mode === 'list' ? { mode: 'list', field: bind.field, invert: bind.invert === true } : { mode: 'flag' }\n\t\t},\n\n\t\t/** The drawn rows: configured first, then sourced, each with a key, title and hint. */\n\t\trows() {\n\t\t\tconst configured = (Array.isArray(this.content.rows) ? this.content.rows : [])\n\t\t\t\t.filter((row) => row && row.key !== undefined)\n\t\t\t\t.map((row) => ({ ...row, key: String(row.key), title: this.tr(row.label || String(row.key)), hint: this.tr(row.hint || '') }))\n\t\t\treturn [...configured, ...this.sourced]\n\t\t},\n\n\t\tcolumns() {\n\t\t\treturn (Array.isArray(this.content.columns) ? this.content.columns : [])\n\t\t\t\t.filter((column) => column && column.path)\n\t\t\t\t.map((column) => ({ ...column, header: this.tr(column.header || '') }))\n\t\t},\n\n\t\tshowHeader() {\n\t\t\treturn this.columns.length > 0 || Boolean(this.content.titleHeader || this.content.toggleHeader)\n\t\t},\n\n\t\ttitleHeader() {\n\t\t\treturn this.tr(this.content.titleHeader || '')\n\t\t},\n\n\t\ttoggleHeader() {\n\t\t\treturn this.tr(this.content.toggleHeader || '')\n\t\t},\n\n\t\temptyLabel() {\n\t\t\treturn this.tr(this.content.emptyText || 'Nothing listed yet')\n\t\t},\n\n\t\treadOnly() {\n\t\t\treturn this.content.readOnly === true\n\t\t},\n\n\t\tobjectUrl() {\n\t\t\treturn objectUrlOf(this.register, this.schema, this.objectId || (this.objectData && this.objectData.id))\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectData() {\n\t\t\tthis.saved = {}\n\t\t\tthis.loadSource()\n\t\t},\n\t},\n\n\tcreated() {\n\t\tthis.loadSource()\n\t},\n\n\tmethods: {\n\t\ttr(text) {\n\t\t\tif (typeof text !== 'string' || text === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text\n\t\t},\n\n\t\t/**\n\t\t * The stored value of a field: what this widget last saved, else the object's.\n\t\t *\n\t\t * @param {string} field The field.\n\t\t * @return {unknown}\n\t\t */\n\t\tstored(field) {\n\t\t\treturn Object.hasOwn(this.saved, field) ? this.saved[field] : readPath(this.objectData || {}, field)\n\t\t},\n\n\t\t/**\n\t\t * Whether a row's switch is on.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {boolean}\n\t\t */\n\t\tisOn(row) {\n\t\t\tif (this.bind.mode === 'list') {\n\t\t\t\tconst list = this.stored(this.bind.field)\n\t\t\t\tconst inList = Array.isArray(list) && list.map(String).includes(row.key)\n\t\t\t\treturn this.bind.invert ? !inList : inList\n\t\t\t}\n\t\t\treturn row.field ? this.stored(row.field) === true : false\n\t\t},\n\n\t\t/**\n\t\t * The text of an extra column for a row.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @param {object} column The column.\n\t\t * @return {string}\n\t\t */\n\t\tcell(row, column) {\n\t\t\tconst raw = readPath(row, column.path)\n\t\t\tif (raw === undefined || raw === null || raw === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (column.map && Object.hasOwn(column.map, String(raw))) {\n\t\t\t\treturn this.tr(column.map[String(raw)])\n\t\t\t}\n\t\t\treturn typeof raw === 'object' ? '' : String(raw)\n\t\t},\n\n\t\t/** Read the rows of `source`, resolving tokens in its url. */\n\t\tasync loadSource() {\n\t\t\tconst source = this.content.source\n\t\t\tif (!source || !source.url || !source.key) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst object = this.objectData || {}\n\t\t\tconst url = String(source.url)\n\t\t\t\t.replace(/@objectId/g, () => encodeURIComponent(String(this.objectId || object.id || '')))\n\t\t\t\t.replace(/@object\\.([\\w.@]+)/g, (m, path) => encodeURIComponent(String(readPath(object, path) ?? '')))\n\t\t\ttry {\n\t\t\t\tconst { default: axios } = await import('@nextcloud/axios')\n\t\t\t\tconst { data } = await axios.get(prefixUrl(url))\n\t\t\t\tconst list = source.path ? readPath(data, source.path) : data\n\t\t\t\tthis.sourced = (Array.isArray(list) ? list : [])\n\t\t\t\t\t.filter((item) => item && readPath(item, source.key) !== undefined)\n\t\t\t\t\t.map((item) => ({\n\t\t\t\t\t\t...item,\n\t\t\t\t\t\tkey: String(readPath(item, source.key)),\n\t\t\t\t\t\ttitle: String(readPath(item, source.label || source.key) ?? readPath(item, source.key)),\n\t\t\t\t\t\thint: source.hint ? String(readPath(item, source.hint) ?? '') : '',\n\t\t\t\t\t}))\n\t\t\t} catch (e) {\n\t\t\t\tthis.notice = serverErrorMessage(e)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Flip one switch and save that one field.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @param {boolean} on The new switch state.\n\t\t * @return {Promise<void>}\n\t\t * @spec openspec/changes/abstract-toggle-table/specs/toggle-table/spec.md#requirement-a-toggle-saves-one-field\n\t\t */\n\t\tasync toggle(row, on) {\n\t\t\tconst field = this.bind.mode === 'list' ? this.bind.field : row.field\n\t\t\tif (!this.objectUrl || !field) {\n\t\t\t\tthis.notice = this.tr('This switch cannot be saved here.')\n\t\t\t\treturn\n\t\t\t}\n\t\t\tlet next\n\t\t\tif (this.bind.mode === 'list') {\n\t\t\t\tconst list = (Array.isArray(this.stored(field)) ? this.stored(field) : []).map(String)\n\t\t\t\tconst wantInList = this.bind.invert ? !on : on\n\t\t\t\tnext = wantInList ? (list.includes(row.key) ? list : [...list, row.key]) : list.filter((key) => key !== row.key)\n\t\t\t} else {\n\t\t\t\tnext = on\n\t\t\t}\n\t\t\tthis.notice = ''\n\t\t\tthis.busy = row.key\n\t\t\ttry {\n\t\t\t\tconst data = await patchObject(this.objectUrl, { [field]: next })\n\t\t\t\tconst written = data[field] !== undefined ? data[field] : next\n\t\t\t\tthis.saved = { ...this.saved, [field]: written }\n\t\t\t\t/**\n\t\t\t\t * @event saved A switch was written.\n\t\t\t\t * @type {{field: string, value: unknown}}\n\t\t\t\t */\n\t\t\t\tthis.$emit('saved', { field, value: written })\n\t\t\t} catch (e) {\n\t\t\t\tthis.notice = serverErrorMessage(e)\n\t\t\t\tthis.undone = { ...this.undone, [row.key]: (this.undone[row.key] || 0) + 1 }\n\t\t\t} finally {\n\t\t\t\tthis.busy = ''\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-toggle-table {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 8px;\n\tmin-width: 0;\n}\n\n.cn-toggle-table__table {\n\twidth: 100%;\n\tborder-collapse: collapse;\n}\n\n.cn-toggle-table__head {\n\tpadding: 8px 12px;\n\ttext-align: start;\n\tfont-size: 13px;\n\tfont-weight: 600;\n\tcolor: var(--color-text-maxcontrast);\n\tbackground: var(--color-background-hover);\n}\n\n.cn-toggle-table__head--toggle,\n.cn-toggle-table__cell--toggle {\n\twidth: 1%;\n\twhite-space: nowrap;\n\ttext-align: end;\n}\n\n.cn-toggle-table__row {\n\tborder-top: 1px solid var(--cn-board-hairline, var(--color-border));\n}\n\n.cn-toggle-table__cell {\n\tpadding: 10px 12px;\n\tvertical-align: middle;\n\toverflow-wrap: anywhere;\n}\n\n.cn-toggle-table__title {\n\tdisplay: block;\n\tfont-weight: 600;\n}\n\n.cn-toggle-table__hint {\n\tdisplay: block;\n\tfont-size: 12px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-toggle-table__empty {\n\tmargin: 0;\n\tcolor: var(--color-text-maxcontrast);\n}\n</style>\n"],"names":[],"mappings":"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"}