@conduction/nextcloud-vue 2.79.0 → 2.81.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (390) hide show
  1. package/dist/esm/components/CnActionButtons/CnActionButtons.vue2.js.map +1 -1
  2. package/dist/esm/components/CnActionButtons/CnActionButtons.vue3.js +3 -1
  3. package/dist/esm/components/CnActionButtons/CnActionButtons.vue3.js.map +1 -1
  4. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js +32 -3
  5. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
  6. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +13 -11
  7. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
  8. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +80 -4
  9. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  10. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +95 -91
  11. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  12. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js +179 -13
  13. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js.map +1 -1
  14. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js +52 -16
  15. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js.map +1 -1
  16. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +96 -4
  17. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
  18. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +158 -110
  19. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
  20. package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue.js +10 -0
  21. package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue.js.map +1 -0
  22. package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue2.js +252 -0
  23. package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue2.js.map +1 -0
  24. package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue3.js +140 -0
  25. package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue3.js.map +1 -0
  26. package/dist/esm/components/CnChipsFieldWidget/index.js +26 -0
  27. package/dist/esm/components/CnChipsFieldWidget/index.js.map +1 -0
  28. package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue.js +10 -0
  29. package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue.js.map +1 -0
  30. package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue2.js +354 -0
  31. package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue2.js.map +1 -0
  32. package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue3.js +172 -0
  33. package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue3.js.map +1 -0
  34. package/dist/esm/components/CnChoicePickerWidget/index.js +27 -0
  35. package/dist/esm/components/CnChoicePickerWidget/index.js.map +1 -0
  36. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js +1 -1
  37. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js.map +1 -1
  38. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue3.js.map +1 -1
  39. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js +2 -2
  40. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js.map +1 -1
  41. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue3.js.map +1 -1
  42. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +65 -5
  43. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  44. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +2 -1
  45. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  46. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +19 -11
  47. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  48. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +4 -2
  49. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  50. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +134 -4
  51. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  52. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +21 -4
  53. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  54. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +24 -0
  55. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
  56. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +43 -4
  57. package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
  58. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue2.js +1 -1
  59. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue2.js.map +1 -1
  60. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue3.js.map +1 -1
  61. package/dist/esm/components/CnFacetSidebar/CnFacetSidebar.vue2.js +7 -7
  62. package/dist/esm/components/CnFacetSidebar/CnFacetSidebar.vue2.js.map +1 -1
  63. package/dist/esm/components/CnFacetSidebar/CnFacetSidebar.vue3.js.map +1 -1
  64. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js +11 -0
  65. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue.js.map +1 -0
  66. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +867 -0
  67. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -0
  68. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js +238 -0
  69. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -0
  70. package/dist/esm/components/CnFactsWidget/index.js +30 -0
  71. package/dist/esm/components/CnFactsWidget/index.js.map +1 -0
  72. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue.js +10 -0
  73. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue.js.map +1 -0
  74. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js +626 -0
  75. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js.map +1 -0
  76. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js +396 -0
  77. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js.map +1 -0
  78. package/dist/esm/components/CnFieldListWidget/index.js +29 -0
  79. package/dist/esm/components/CnFieldListWidget/index.js.map +1 -0
  80. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +302 -4
  81. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  82. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +417 -244
  83. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  84. package/dist/esm/components/CnFilesBrowser/filesBrowser.js +101 -1
  85. package/dist/esm/components/CnFilesBrowser/filesBrowser.js.map +1 -1
  86. package/dist/esm/components/CnFilesBrowser/filesBrowserColumns.js +141 -1
  87. package/dist/esm/components/CnFilesBrowser/filesBrowserColumns.js.map +1 -1
  88. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js +65 -17
  89. package/dist/esm/components/CnFormDialog/CnFormDialog.vue2.js.map +1 -1
  90. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js +4 -4
  91. package/dist/esm/components/CnFormDialog/CnFormDialog.vue3.js.map +1 -1
  92. package/dist/esm/components/CnFormPage/CnFormPage.vue2.js +78 -3
  93. package/dist/esm/components/CnFormPage/CnFormPage.vue2.js.map +1 -1
  94. package/dist/esm/components/CnFormPage/CnFormPage.vue3.js +74 -49
  95. package/dist/esm/components/CnFormPage/CnFormPage.vue3.js.map +1 -1
  96. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +333 -73
  97. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  98. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +79 -17
  99. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  100. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue.js +10 -0
  101. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue.js.map +1 -0
  102. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js +99 -0
  103. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue2.js.map +1 -0
  104. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js +48 -0
  105. package/dist/esm/components/CnIndexPage/CnIndexPageWidgets.vue3.js.map +1 -0
  106. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js +7 -7
  107. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js.map +1 -1
  108. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js.map +1 -1
  109. package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue2.js +1 -1
  110. package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue2.js.map +1 -1
  111. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +65 -2
  112. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
  113. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +6 -2
  114. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
  115. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +63 -1
  116. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
  117. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +8 -1
  118. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
  119. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js +14 -0
  120. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js.map +1 -1
  121. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue3.js +5 -1
  122. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue3.js.map +1 -1
  123. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue2.js +29 -1
  124. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue2.js.map +1 -1
  125. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js.map +1 -1
  126. package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue.js +10 -0
  127. package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue.js.map +1 -0
  128. package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue2.js +174 -0
  129. package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue2.js.map +1 -0
  130. package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue3.js +75 -0
  131. package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue3.js.map +1 -0
  132. package/dist/esm/components/CnRadioCardsWidget/index.js +28 -0
  133. package/dist/esm/components/CnRadioCardsWidget/index.js.map +1 -0
  134. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js +10 -0
  135. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue.js.map +1 -0
  136. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +411 -0
  137. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -0
  138. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +178 -0
  139. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -0
  140. package/dist/esm/components/CnRecordListWidget/index.js +31 -0
  141. package/dist/esm/components/CnRecordListWidget/index.js.map +1 -0
  142. package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue.js +10 -0
  143. package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue.js.map +1 -0
  144. package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue2.js +222 -0
  145. package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue2.js.map +1 -0
  146. package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue3.js +64 -0
  147. package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue3.js.map +1 -0
  148. package/dist/esm/components/CnSavedViewsWidget/index.js +30 -0
  149. package/dist/esm/components/CnSavedViewsWidget/index.js.map +1 -0
  150. package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue.js +10 -0
  151. package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue.js.map +1 -0
  152. package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue2.js +351 -0
  153. package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue2.js.map +1 -0
  154. package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue3.js +71 -0
  155. package/dist/esm/components/CnSectionsWidget/CnSectionsWidget.vue3.js.map +1 -0
  156. package/dist/esm/components/CnSectionsWidget/index.js +35 -0
  157. package/dist/esm/components/CnSectionsWidget/index.js.map +1 -0
  158. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue2.js.map +1 -1
  159. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue3.js.map +1 -1
  160. package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue.js +10 -0
  161. package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue.js.map +1 -0
  162. package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue2.js +347 -0
  163. package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue2.js.map +1 -0
  164. package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue3.js +184 -0
  165. package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue3.js.map +1 -0
  166. package/dist/esm/components/CnSettingsFormWidget/index.js +27 -0
  167. package/dist/esm/components/CnSettingsFormWidget/index.js.map +1 -0
  168. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js +81 -2
  169. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js.map +1 -1
  170. package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js.map +1 -1
  171. package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js +87 -6
  172. package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
  173. package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js +1 -0
  174. package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
  175. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js +84 -1
  176. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js.map +1 -1
  177. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js +85 -41
  178. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js.map +1 -1
  179. package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue.js +10 -0
  180. package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue.js.map +1 -0
  181. package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue2.js +159 -0
  182. package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue2.js.map +1 -0
  183. package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue3.js +60 -0
  184. package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue3.js.map +1 -0
  185. package/dist/esm/components/CnSwitchRowsWidget/index.js +26 -0
  186. package/dist/esm/components/CnSwitchRowsWidget/index.js.map +1 -0
  187. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +168 -7
  188. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
  189. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +86 -23
  190. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
  191. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js +80 -1
  192. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js.map +1 -1
  193. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js +75 -32
  194. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js.map +1 -1
  195. package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue.js +10 -0
  196. package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue.js.map +1 -0
  197. package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue2.js +269 -0
  198. package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue2.js.map +1 -0
  199. package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue3.js +106 -0
  200. package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue3.js.map +1 -0
  201. package/dist/esm/components/CnToggleTableWidget/index.js +27 -0
  202. package/dist/esm/components/CnToggleTableWidget/index.js.map +1 -0
  203. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +11 -0
  204. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
  205. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +7 -2
  206. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
  207. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
  208. package/dist/esm/components/CnWidgetObjectTable/dashboardRegistration.js +5 -0
  209. package/dist/esm/components/CnWidgetObjectTable/dashboardRegistration.js.map +1 -1
  210. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js +301 -6
  211. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js.map +1 -1
  212. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js +153 -55
  213. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js.map +1 -1
  214. package/dist/esm/composables/useFormDraft.js +140 -9
  215. package/dist/esm/composables/useFormDraft.js.map +1 -1
  216. package/dist/esm/composables/useListView.js +4 -4
  217. package/dist/esm/composables/useListView.js.map +1 -1
  218. package/dist/esm/index.js +21 -1
  219. package/dist/esm/index.js.map +1 -1
  220. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue2.js +28 -20
  221. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue2.js.map +1 -1
  222. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue3.js +9 -0
  223. package/dist/esm/integrations/builtin/field-inspection/CnFieldInspectionCard.vue3.js.map +1 -1
  224. package/dist/esm/integrations/builtin/field-inspection.js +21 -0
  225. package/dist/esm/integrations/builtin/field-inspection.js.map +1 -1
  226. package/dist/esm/integrations/offline/checklistSubmission.js +141 -0
  227. package/dist/esm/integrations/offline/checklistSubmission.js.map +1 -0
  228. package/dist/esm/integrations/offline/fieldCollectionHelpers.js +70 -5
  229. package/dist/esm/integrations/offline/fieldCollectionHelpers.js.map +1 -1
  230. package/dist/esm/integrations/offline/index.js +2 -1
  231. package/dist/esm/integrations/offline/index.js.map +1 -1
  232. package/dist/esm/integrations/offline/offlineDb.js +6 -1
  233. package/dist/esm/integrations/offline/offlineDb.js.map +1 -1
  234. package/dist/esm/integrations/offline/syncReplayService.js +8 -2
  235. package/dist/esm/integrations/offline/syncReplayService.js.map +1 -1
  236. package/dist/esm/l10n/en.json.js +20 -1
  237. package/dist/esm/l10n/en.json.js.map +1 -1
  238. package/dist/esm/l10n/nl.json.js +70 -2
  239. package/dist/esm/l10n/nl.json.js.map +1 -1
  240. package/dist/esm/nextcloud-vue.css +2192 -222
  241. package/dist/esm/utils/contentHeaderLink.js +51 -0
  242. package/dist/esm/utils/contentHeaderLink.js.map +1 -0
  243. package/dist/esm/utils/detailActionModel.js +60 -3
  244. package/dist/esm/utils/detailActionModel.js.map +1 -1
  245. package/dist/esm/utils/facets.js +75 -3
  246. package/dist/esm/utils/facets.js.map +1 -1
  247. package/dist/esm/utils/fetchFilterCounts.js +40 -5
  248. package/dist/esm/utils/fetchFilterCounts.js.map +1 -1
  249. package/dist/esm/utils/fillTemplate.js +120 -0
  250. package/dist/esm/utils/fillTemplate.js.map +1 -0
  251. package/dist/esm/utils/initials.js +32 -0
  252. package/dist/esm/utils/initials.js.map +1 -0
  253. package/dist/esm/utils/libraryWidgetKeys.js +11 -0
  254. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  255. package/dist/esm/utils/objectWrite.js +59 -0
  256. package/dist/esm/utils/objectWrite.js.map +1 -0
  257. package/dist/esm/utils/pageControls.js +81 -0
  258. package/dist/esm/utils/pageControls.js.map +1 -0
  259. package/dist/esm/utils/personalLenses.js +40 -3
  260. package/dist/esm/utils/personalLenses.js.map +1 -1
  261. package/dist/esm/utils/queryPresets.js +96 -0
  262. package/dist/esm/utils/queryPresets.js.map +1 -0
  263. package/dist/esm/utils/resolveFilterTokens.js +12 -1
  264. package/dist/esm/utils/resolveFilterTokens.js.map +1 -1
  265. package/dist/esm/utils/resolveManifestTokens.js +2 -0
  266. package/dist/esm/utils/resolveManifestTokens.js.map +1 -1
  267. package/dist/esm/utils/schema.js +21 -0
  268. package/dist/esm/utils/schema.js.map +1 -1
  269. package/dist/esm/utils/sentinelTokens.js +22 -4
  270. package/dist/esm/utils/sentinelTokens.js.map +1 -1
  271. package/dist/esm/utils/userGroupsPeek.js +91 -0
  272. package/dist/esm/utils/userGroupsPeek.js.map +1 -0
  273. package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
  274. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  275. package/dist/esm/utils/visibleWhen.js +57 -5
  276. package/dist/esm/utils/visibleWhen.js.map +1 -1
  277. package/dist/esm/utils/widgetDispatch.js +7 -1
  278. package/dist/esm/utils/widgetDispatch.js.map +1 -1
  279. package/dist/esm/utils/widgetVisibility.js +4 -57
  280. package/dist/esm/utils/widgetVisibility.js.map +1 -1
  281. package/dist/nextcloud-vue.cjs.js +19708 -9301
  282. package/dist/nextcloud-vue.cjs.js.map +2 -2
  283. package/dist/nextcloud-vue.css +2192 -222
  284. package/l10n/en.json +20 -1
  285. package/l10n/nl.json +70 -2
  286. package/package.json +2 -1
  287. package/scripts/check-docs-mdx.mjs +108 -0
  288. package/src/components/CnActionButtons/CnActionButtons.vue +2 -0
  289. package/src/components/CnActionsBar/CnActionsBar.md +4 -0
  290. package/src/components/CnActionsBar/CnActionsBar.vue +48 -11
  291. package/src/components/CnAppRoot/CnAppRoot.md +4 -2
  292. package/src/components/CnAppRoot/CnAppRoot.vue +151 -59
  293. package/src/components/CnBannerWidget/CnBannerWidget.vue +244 -23
  294. package/src/components/CnCellRenderer/CnCellRenderer.md +2 -0
  295. package/src/components/CnCellRenderer/CnCellRenderer.vue +235 -5
  296. package/src/components/CnChipsFieldWidget/CnChipsFieldWidget.vue +433 -0
  297. package/src/components/CnChipsFieldWidget/index.js +25 -0
  298. package/src/components/CnChoicePickerWidget/CnChoicePickerWidget.vue +556 -0
  299. package/src/components/CnChoicePickerWidget/index.js +26 -0
  300. package/src/components/CnConversationThread/CnConversationWidget.vue +1 -1
  301. package/src/components/CnDashboardGrid/CnDashboardGrid.vue +2 -2
  302. package/src/components/CnDashboardPage/CnDashboardPage.vue +78 -5
  303. package/src/components/CnDataTable/CnDataTable.vue +22 -12
  304. package/src/components/CnDetailPage/CnDetailPage.md +1 -1
  305. package/src/components/CnDetailPage/CnDetailPage.vue +152 -6
  306. package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +66 -0
  307. package/src/components/CnDocumentReviewList/CnDocumentReviewWidget.vue +1 -1
  308. package/src/components/CnFacetSidebar/CnFacetSidebar.vue +7 -7
  309. package/src/components/CnFactsWidget/CnFactsWidget.vue +1190 -0
  310. package/src/components/CnFactsWidget/index.js +29 -0
  311. package/src/components/CnFieldListWidget/CnFieldListWidget.vue +1008 -0
  312. package/src/components/CnFieldListWidget/index.js +28 -0
  313. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +588 -117
  314. package/src/components/CnFilesBrowser/filesBrowser.js +100 -0
  315. package/src/components/CnFilesBrowser/filesBrowserColumns.js +140 -0
  316. package/src/components/CnFormDialog/CnFormDialog.md +9 -3
  317. package/src/components/CnFormDialog/CnFormDialog.vue +69 -21
  318. package/src/components/CnFormPage/CnFormPage.vue +99 -3
  319. package/src/components/CnIndexPage/CnIndexPage.vue +309 -9
  320. package/src/components/CnIndexPage/CnIndexPageWidgets.vue +169 -0
  321. package/src/components/CnIndexSidebar/CnIndexSidebar.vue +7 -7
  322. package/src/components/CnNextStepCard/CnNextStepWidget.vue +1 -1
  323. package/src/components/CnObjectListWidget/CnObjectListWidget.vue +69 -2
  324. package/src/components/CnObjectSidebar/CnFilesTab.vue +70 -1
  325. package/src/components/CnObjectSidebar/CnNotesTab.vue +16 -0
  326. package/src/components/CnPageRenderer/CnPageRenderer.vue +29 -1
  327. package/src/components/CnRadioCardsWidget/CnRadioCardsWidget.vue +313 -0
  328. package/src/components/CnRadioCardsWidget/index.js +27 -0
  329. package/src/components/CnRecordListWidget/CnRecordListWidget.vue +652 -0
  330. package/src/components/CnRecordListWidget/index.js +30 -0
  331. package/src/components/CnSavedViewsWidget/CnSavedViewsWidget.vue +296 -0
  332. package/src/components/CnSavedViewsWidget/index.js +29 -0
  333. package/src/components/CnSectionsWidget/CnSectionsWidget.vue +489 -0
  334. package/src/components/CnSectionsWidget/index.js +34 -0
  335. package/src/components/CnSegmentedControl/CnSegmentedControl.vue +13 -0
  336. package/src/components/CnSettingsFormWidget/CnSettingsFormWidget.vue +555 -0
  337. package/src/components/CnSettingsFormWidget/index.js +26 -0
  338. package/src/components/CnStagesWidget/CnStagesWidget.vue +81 -2
  339. package/src/components/CnStatWidget/CnStatWidget.vue +88 -7
  340. package/src/components/CnStatsBlockWidget/CnStatsBlockWidget.vue +180 -3
  341. package/src/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue +226 -0
  342. package/src/components/CnSwitchRowsWidget/index.js +25 -0
  343. package/src/components/CnTabsWidget/CnTabsWidget.vue +263 -8
  344. package/src/components/CnTasksWidget/CnTasksWidget.vue +175 -15
  345. package/src/components/CnToggleTableWidget/CnToggleTableWidget.vue +386 -0
  346. package/src/components/CnToggleTableWidget/index.js +26 -0
  347. package/src/components/CnWidgetGrid/registerDashboardWidgets.js +11 -0
  348. package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +7 -2
  349. package/src/components/CnWidgetObjectTable/dashboardRegistration.js +5 -0
  350. package/src/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue +493 -11
  351. package/src/components/index.js +19 -0
  352. package/src/composables/__tests__/useFormDraft.spec.js +69 -0
  353. package/src/composables/useFormDraft.js +139 -8
  354. package/src/composables/useListView.js +4 -4
  355. package/src/css/form-field.css +14 -0
  356. package/src/css/index-page.css +71 -0
  357. package/src/css/kpi-card.css +32 -0
  358. package/src/css/look-board-detail.css +113 -2
  359. package/src/css/look-board-index.css +90 -8
  360. package/src/css/look-board.css +109 -2
  361. package/src/css/table.css +19 -0
  362. package/src/index.js +19 -0
  363. package/src/integrations/builtin/field-inspection/CnFieldInspectionCard.vue +35 -20
  364. package/src/integrations/builtin/field-inspection.js +21 -0
  365. package/src/integrations/offline/checklistSubmission.js +137 -0
  366. package/src/integrations/offline/fieldCollectionHelpers.js +69 -4
  367. package/src/integrations/offline/index.js +3 -0
  368. package/src/integrations/offline/offlineDb.js +6 -1
  369. package/src/integrations/offline/syncReplayService.js +8 -2
  370. package/src/schemas/app-manifest-v2.schema.json +196 -13
  371. package/src/utils/contentHeaderLink.js +48 -0
  372. package/src/utils/detailActionModel.js +59 -2
  373. package/src/utils/facets.js +74 -2
  374. package/src/utils/fetchFilterCounts.js +39 -4
  375. package/src/utils/fillTemplate.js +116 -0
  376. package/src/utils/initials.js +29 -0
  377. package/src/utils/libraryWidgetKeys.js +11 -0
  378. package/src/utils/objectWrite.js +55 -0
  379. package/src/utils/pageControls.js +78 -0
  380. package/src/utils/personalLenses.js +39 -2
  381. package/src/utils/queryPresets.js +93 -0
  382. package/src/utils/resolveFilterTokens.js +14 -1
  383. package/src/utils/resolveManifestTokens.js +2 -0
  384. package/src/utils/schema.js +21 -0
  385. package/src/utils/sentinelTokens.js +22 -4
  386. package/src/utils/userGroupsPeek.js +87 -0
  387. package/src/utils/validateManifestV2.compiled.js +3 -3
  388. package/src/utils/visibleWhen.js +56 -4
  389. package/src/utils/widgetDispatch.js +7 -1
  390. package/src/utils/widgetVisibility.js +7 -56
@@ -0,0 +1,1008 @@
1
+ <!--
2
+ - SPDX-FileCopyrightText: 2026 Conduction B.V.
3
+ - SPDX-License-Identifier: EUPL-1.2
4
+ -->
5
+
6
+ <template>
7
+ <div class="cn-field-list-widget" data-testid="cn-field-list-widget">
8
+ <div v-if="canAdd && addTrigger && addPlacement === 'header'" class="cn-field-list-widget__toolbar">
9
+ <NcButton
10
+ variant="secondary"
11
+ :disabled="busy !== ''"
12
+ data-testid="cn-field-list-open-add"
13
+ @click="adding = !adding">
14
+ + {{ addLabel }}
15
+ </NcButton>
16
+ </div>
17
+ <form
18
+ v-if="canAdd && addTrigger && addPlacement === 'header' && adding"
19
+ class="cn-field-list-widget__add"
20
+ data-testid="cn-field-list-add"
21
+ @submit.prevent="add">
22
+ <div
23
+ v-for="field in addFields"
24
+ :key="field.key"
25
+ class="cn-field-list-widget__field"
26
+ :data-testid="`cn-field-list-input-${field.key}`">
27
+ <NcSelect
28
+ v-if="field.type === 'select'"
29
+ v-model="draft[field.key]"
30
+ :inputLabel="field.label"
31
+ :options="field.options"
32
+ :reduce="(option) => option.value"
33
+ :clearable="false"
34
+ label="label" />
35
+ <NcTextField
36
+ v-else
37
+ v-model="draft[field.key]"
38
+ :label="field.label"
39
+ :error="Boolean(errors[field.key])"
40
+ :helperText="errors[field.key] || ''" />
41
+ </div>
42
+ <NcButton type="submit"
43
+ variant="primary"
44
+ :disabled="busy !== ''"
45
+ data-testid="cn-field-list-submit">
46
+ {{ addLabel }}
47
+ </NcButton>
48
+ </form>
49
+ <p v-if="rows.length === 0" class="cn-field-list-widget__empty" data-testid="cn-field-list-empty">
50
+ {{ emptyLabel }}
51
+ </p>
52
+ <div v-else-if="isTable" class="cn-field-list-widget__table-wrap">
53
+ <table class="cn-field-list-widget__table" data-testid="cn-field-list-table">
54
+ <thead>
55
+ <tr>
56
+ <th
57
+ v-for="column in tableColumns"
58
+ :key="column.key"
59
+ scope="col"
60
+ class="cn-field-list-widget__th">
61
+ {{ column.header }}
62
+ </th>
63
+ <th v-if="hasMenu" scope="col" class="cn-field-list-widget__th cn-field-list-widget__th--menu">
64
+ <span class="cn-field-list-widget__sr">{{ tr('Actions') }}</span>
65
+ </th>
66
+ </tr>
67
+ </thead>
68
+ <tbody>
69
+ <tr
70
+ v-for="row in rows"
71
+ :key="row.key"
72
+ class="cn-field-list-widget__tr"
73
+ :data-testid="`cn-field-list-row-${row.key}`">
74
+ <td
75
+ v-for="(column, index) in tableColumns"
76
+ :key="column.key"
77
+ class="cn-field-list-widget__td">
78
+ <CnStatusBadge
79
+ v-if="column.pill && row.cells[index].pill"
80
+ :label="row.cells[index].pill.label"
81
+ :variant="row.cells[index].pill.variant"
82
+ size="small" />
83
+ <strong v-else-if="column.strong">{{ row.cells[index].text }}</strong>
84
+ <template v-else>
85
+ {{ row.cells[index].text }}
86
+ </template>
87
+ </td>
88
+ <td v-if="hasMenu" class="cn-field-list-widget__td cn-field-list-widget__td--menu">
89
+ <NcActions
90
+ v-if="menuItems(row).length > 0"
91
+ :forceMenu="true"
92
+ :disabled="busy !== ''"
93
+ :ariaLabel="`${tr('More actions')}: ${row.title}`"
94
+ :data-testid="`cn-field-list-menu-${row.key}`">
95
+ <NcActionButton
96
+ v-for="item in menuItems(row)"
97
+ :key="item.id"
98
+ :data-testid="`cn-field-list-menu-${item.id}-${row.key}`"
99
+ @click="item.run">
100
+ {{ item.label }}
101
+ </NcActionButton>
102
+ </NcActions>
103
+ </td>
104
+ </tr>
105
+ </tbody>
106
+ </table>
107
+ <div
108
+ v-for="row in bandRows"
109
+ :key="row.key"
110
+ class="cn-field-list-widget__band"
111
+ role="note"
112
+ :data-testid="`cn-field-list-band-${row.key}`">
113
+ <span class="cn-field-list-widget__band-text">{{ row.detail }}</span>
114
+ <NcButton
115
+ v-for="action in row.bandActions"
116
+ :key="action.id"
117
+ variant="secondary"
118
+ :disabled="busy !== ''"
119
+ :data-testid="`cn-field-list-band-action-${action.id}-${row.key}`"
120
+ @click="runAction(action, row)">
121
+ {{ action.label }}
122
+ </NcButton>
123
+ </div>
124
+ </div>
125
+ <ul v-else class="cn-field-list-widget__rows">
126
+ <li
127
+ v-for="row in rows"
128
+ :key="row.key"
129
+ class="cn-field-list-widget__row"
130
+ :data-testid="`cn-field-list-row-${row.key}`">
131
+ <div class="cn-field-list-widget__line">
132
+ <span v-if="row.initials" class="cn-field-list-widget__avatar" aria-hidden="true">{{ row.initials }}</span>
133
+ <span class="cn-field-list-widget__main">
134
+ <span class="cn-field-list-widget__title">{{ row.title }}</span>
135
+ <span v-if="row.meta" class="cn-field-list-widget__meta">{{ row.meta }}</span>
136
+ </span>
137
+ <CnStatusBadge
138
+ v-if="row.pill"
139
+ :label="row.pill.label"
140
+ :variant="row.pill.variant"
141
+ size="small" />
142
+ <span v-if="row.aside" class="cn-field-list-widget__aside">{{ row.aside }}</span>
143
+ <NcButton
144
+ v-for="action in row.actions"
145
+ :key="action.id"
146
+ variant="tertiary"
147
+ :disabled="busy !== ''"
148
+ :data-testid="`cn-field-list-action-${action.id}-${row.key}`"
149
+ @click="runAction(action, row)">
150
+ {{ action.label }}
151
+ </NcButton>
152
+ <NcButton
153
+ v-if="canRemove"
154
+ variant="tertiary"
155
+ :disabled="busy !== ''"
156
+ :aria-label="removeAriaLabel(row)"
157
+ :data-testid="`cn-field-list-remove-${row.key}`"
158
+ @click="remove(row)">
159
+ {{ removeLabel }}
160
+ </NcButton>
161
+ </div>
162
+ <p v-if="row.detail" class="cn-field-list-widget__detail">
163
+ {{ row.detail }}
164
+ </p>
165
+ </li>
166
+ </ul>
167
+
168
+ <p v-if="hint" class="cn-field-list-widget__hint">
169
+ {{ hint }}
170
+ </p>
171
+
172
+ <div v-if="canAdd && addTrigger && addPlacement !== 'header' && !adding" class="cn-field-list-widget__toolbar">
173
+ <NcButton
174
+ variant="secondary"
175
+ :disabled="busy !== ''"
176
+ data-testid="cn-field-list-open-add"
177
+ @click="adding = true">
178
+ + {{ addLabel }}
179
+ </NcButton>
180
+ </div>
181
+ <form
182
+ v-if="canAdd && !(addTrigger && addPlacement === 'header') && (!addTrigger || adding)"
183
+ class="cn-field-list-widget__add"
184
+ data-testid="cn-field-list-add"
185
+ @submit.prevent="add">
186
+ <div
187
+ v-for="field in addFields"
188
+ :key="field.key"
189
+ class="cn-field-list-widget__field"
190
+ :data-testid="`cn-field-list-input-${field.key}`">
191
+ <NcSelect
192
+ v-if="field.type === 'select'"
193
+ v-model="draft[field.key]"
194
+ :inputLabel="field.label"
195
+ :options="field.options"
196
+ :reduce="(option) => option.value"
197
+ :clearable="false"
198
+ label="label" />
199
+ <NcTextField
200
+ v-else
201
+ v-model="draft[field.key]"
202
+ :label="field.label"
203
+ :error="Boolean(errors[field.key])"
204
+ :helperText="errors[field.key] || ''" />
205
+ </div>
206
+ <NcButton type="submit"
207
+ variant="secondary"
208
+ :disabled="busy !== ''"
209
+ data-testid="cn-field-list-submit">
210
+ {{ addLabel }}
211
+ </NcButton>
212
+ </form>
213
+
214
+ <NcNoteCard v-if="notice" :type="noticeType" data-testid="cn-field-list-notice">
215
+ {{ notice }}
216
+ </NcNoteCard>
217
+ </div>
218
+ </template>
219
+
220
+ <script>
221
+ import { NcActionButton, NcActions, NcButton, NcNoteCard, NcSelect, NcTextField } from '@nextcloud/vue'
222
+ import CnStatusBadge from '../CnStatusBadge/CnStatusBadge.vue'
223
+ import { isBadgeVariant } from '../../utils/badgeVariants.js'
224
+ import { formatBoardDate } from '../../utils/boardDate.js'
225
+ import { parseDateValue } from '../../utils/dateVariant.js'
226
+ import { prefixUrl } from '../../utils/headers.js'
227
+ import { readPath } from '../../utils/readPath.js'
228
+ import { serverErrorMessage } from '../../utils/serverErrorMessage.js'
229
+
230
+ /**
231
+ * CnFieldListWidget — the `field-list` widget: a list stored INSIDE one field
232
+ * of the current object (a portal's `domains[]`, its `editors[]`), shown as
233
+ * rows and edited in place.
234
+ *
235
+ * Rows are drawn from configuration (title, meta line, pill, aside, a detail
236
+ * line); a small form adds an entry, a button removes one, and each change is
237
+ * saved as ONE PATCH of that field on the object, after re-reading the stored
238
+ * value so a concurrent change to the same list is not overwritten with a
239
+ * stale copy. Optional row actions call a configured endpoint (a "check
240
+ * again"), and an optional `rowSource` merges live per-row state read from an
241
+ * endpoint by key without writing it back.
242
+ *
243
+ * The server decides who may write: a refused PATCH shows the server's words
244
+ * and leaves the list as stored.
245
+ *
246
+ * @spec openspec/changes/abstract-field-list/specs/field-list/spec.md
247
+ */
248
+ export default {
249
+ name: 'CnFieldListWidget',
250
+
251
+ components: { CnStatusBadge, NcActionButton, NcActions, NcButton, NcNoteCard, NcSelect, NcTextField },
252
+
253
+ inject: {
254
+ cnTranslate: { default: () => (key) => key },
255
+ },
256
+
257
+ props: {
258
+ /**
259
+ * The widget configuration.
260
+ *
261
+ * - `field` (required) — the object field holding the array.
262
+ * - `key` — the item field that identifies an entry (default: the item itself for strings).
263
+ * - `row` — `{ title, titleFallback?, avatar?, meta?: [paths|{ path, map?, format? }], pill?: { path, map?, variantMap? }, aside?: { path, format?, prefix?, fallback? }, detail?: { template, when? } }`.
264
+ * - `add` — `{ label?, fields: [{ key, label, type?: 'text'|'select', options?, required?, pattern?, patternMessage? }], unique?, defaults? }`.
265
+ * - `remove` — `{ label? }`; omitted = no remove.
266
+ * - `rowActions` — `[{ id, label, when?, placement?: 'band', request: { method?, url, payload? } }]`; tokens `@object.<field>`, `@objectId`, `@row.<field>`.
267
+ * - `rowSource` — `{ url, path?, key }`: live per-row state merged by key.
268
+ * - `variant: 'table'` + `columns` — `[{ header, path, map?, format?, strong?, pill?, variantMap? }]`: rows as a table; a per-row "..." menu holds the row actions and remove; `detail` lines become a warning band under the table, with the row actions that have `placement: 'band'`.
269
+ * - `add.trigger: 'button'`, `add.placement: 'header' | 'footer'` — the add form opens from a button.
270
+ * - `readOnly`, `hint`, `emptyText`.
271
+ *
272
+ * @type {object}
273
+ */
274
+ content: {
275
+ type: Object,
276
+ default: () => ({}),
277
+ },
278
+
279
+ /** The current detail-page object. */
280
+ objectData: {
281
+ type: Object,
282
+ default: null,
283
+ },
284
+
285
+ /** The current object id. */
286
+ objectId: {
287
+ type: [String, Number],
288
+ default: '',
289
+ },
290
+
291
+ /** OpenRegister register slug of the surface. */
292
+ register: {
293
+ type: [String, Object],
294
+ default: '',
295
+ },
296
+
297
+ /** OpenRegister schema slug of the surface. */
298
+ schema: {
299
+ type: [String, Object],
300
+ default: '',
301
+ },
302
+ },
303
+
304
+ emits: ['saved'],
305
+
306
+ data() {
307
+ return {
308
+ /** The list as last saved by this widget; null = read it off objectData. */
309
+ saved: null,
310
+ /** Live per-row state from `rowSource`, by key. */
311
+ live: {},
312
+ draft: {},
313
+ /** Whether the add form is open (only with `add.trigger: 'button'`). */
314
+ adding: false,
315
+ errors: {},
316
+ busy: '',
317
+ notice: '',
318
+ noticeType: 'error',
319
+ }
320
+ },
321
+
322
+ computed: {
323
+ /**
324
+ * The stored list.
325
+ *
326
+ * @return {Array}
327
+ */
328
+ items() {
329
+ const list = this.saved !== null ? this.saved : readPath(this.objectData || {}, this.content.field)
330
+ return Array.isArray(list) ? list : []
331
+ },
332
+
333
+ /**
334
+ * The rows to draw.
335
+ *
336
+ * @return {Array<object>}
337
+ * @spec openspec/changes/abstract-field-list/specs/field-list/spec.md#requirement-a-field-list-draws-the-entries-of-one-object-field
338
+ */
339
+ rows() {
340
+ const spec = this.content.row || {}
341
+ return this.items.map((item, index) => {
342
+ const key = this.keyOf(item, index)
343
+ const entry = item && typeof item === 'object'
344
+ ? { ...item, ...(this.live[key] || {}) }
345
+ : { value: item, ...(this.live[key] || {}) }
346
+ const title = this.text(entry, spec.title) || this.text(entry, spec.titleFallback) || (typeof item === 'string' ? item : key)
347
+ const meta = (Array.isArray(spec.meta) ? spec.meta : [])
348
+ .map((part) => this.text(entry, part))
349
+ .filter((part) => part !== '')
350
+ .join(' · ')
351
+ return {
352
+ key,
353
+ item,
354
+ entry,
355
+ title,
356
+ initials: spec.avatar ? this.initials(title) : '',
357
+ meta,
358
+ pill: this.pillOf(entry, spec.pill),
359
+ aside: spec.aside ? this.asideOf(entry, spec.aside) : '',
360
+ detail: spec.detail && this.matches(entry, spec.detail.when) ? this.fill(spec.detail.template, entry) : '',
361
+ actions: this.actionsFor(entry),
362
+ cells: this.isTable ? this.cellsOf(entry) : [],
363
+ }
364
+ })
365
+ },
366
+
367
+ /** Whether the list is read-only. */
368
+ readOnly() {
369
+ return this.content.readOnly === true
370
+ },
371
+
372
+ /** Whether entries can be added. */
373
+ canAdd() {
374
+ return !this.readOnly && this.addFields.length > 0 && Boolean(this.content.field)
375
+ },
376
+
377
+ /** Whether entries can be removed. */
378
+ canRemove() {
379
+ return !this.readOnly && Boolean(this.content.remove) && Boolean(this.content.field)
380
+ },
381
+
382
+ /**
383
+ * The add form's fields, labels translated, select options translated.
384
+ *
385
+ * @return {Array<object>}
386
+ */
387
+ addFields() {
388
+ const fields = this.content.add && Array.isArray(this.content.add.fields) ? this.content.add.fields : []
389
+ return fields
390
+ .filter((field) => field && field.key)
391
+ .map((field) => ({
392
+ ...field,
393
+ label: this.tr(field.label || field.key),
394
+ options: (Array.isArray(field.options) ? field.options : []).map((option) => (
395
+ typeof option === 'object' ? { value: option.value, label: this.tr(option.label || String(option.value)) } : { value: option, label: this.tr(String(option)) }
396
+ )),
397
+ }))
398
+ },
399
+
400
+ /** Whether the rows are drawn as a table (`variant: 'table'`). */
401
+ isTable() {
402
+ return this.content.variant === 'table' && this.tableColumns.length > 0
403
+ },
404
+
405
+ /**
406
+ * The table columns, headers translated.
407
+ *
408
+ * @return {Array<object>}
409
+ */
410
+ tableColumns() {
411
+ return (Array.isArray(this.content.columns) ? this.content.columns : [])
412
+ .filter((column) => column && column.path)
413
+ .map((column, index) => ({ ...column, key: `${column.path}-${index}`, header: this.tr(column.header || '') }))
414
+ },
415
+
416
+ /** Whether each row has a "more actions" menu (row actions not placed in the band, and remove). */
417
+ hasMenu() {
418
+ return this.isTable && !this.readOnly && (this.canRemove || this.rows.some((row) => this.menuItems(row).length > 0))
419
+ },
420
+
421
+ /** Rows with a detail line: in the table variant it is a warning band under the table. */
422
+ bandRows() {
423
+ return this.rows
424
+ .filter((row) => row.detail !== '')
425
+ .map((row) => ({ ...row, bandActions: row.actions.filter((action) => action.placement === 'band') }))
426
+ },
427
+
428
+ /** Whether the add form opens from a button (`add.trigger: 'button'`). */
429
+ addTrigger() {
430
+ return Boolean(this.content.add && this.content.add.trigger === 'button')
431
+ },
432
+
433
+ /** Where the add button sits: `header` (above the rows) or `footer` (default). */
434
+ addPlacement() {
435
+ return this.content.add && this.content.add.placement === 'header' ? 'header' : 'footer'
436
+ },
437
+
438
+ addLabel() {
439
+ return this.tr((this.content.add && this.content.add.label) || 'Add')
440
+ },
441
+
442
+ removeLabel() {
443
+ return this.tr((this.content.remove && this.content.remove.label) || 'Remove')
444
+ },
445
+
446
+ hint() {
447
+ return this.tr(this.content.hint || '')
448
+ },
449
+
450
+ emptyLabel() {
451
+ return this.tr(this.content.emptyText || 'Nothing listed yet')
452
+ },
453
+
454
+ /** The object URL the list is saved to, or '' when it is not known. */
455
+ objectUrl() {
456
+ const id = this.objectId || (this.objectData && this.objectData.id)
457
+ if (!this.register || !this.schema || !id || typeof this.register !== 'string' || typeof this.schema !== 'string') {
458
+ return ''
459
+ }
460
+ return `/apps/openregister/api/objects/${encodeURIComponent(this.register)}/${encodeURIComponent(this.schema)}/${encodeURIComponent(String(id))}`
461
+ },
462
+ },
463
+
464
+ watch: {
465
+ objectData() {
466
+ this.saved = null
467
+ this.loadLive()
468
+ },
469
+ },
470
+
471
+ created() {
472
+ this.resetDraft()
473
+ this.loadLive()
474
+ },
475
+
476
+ methods: {
477
+ tr(text) {
478
+ if (typeof text !== 'string' || text === '') {
479
+ return ''
480
+ }
481
+ return typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text
482
+ },
483
+
484
+ /**
485
+ * The cells of one table row, one per column.
486
+ *
487
+ * @param {object} entry The entry.
488
+ * @return {Array<{text: string, pill: object|null}>}
489
+ */
490
+ cellsOf(entry) {
491
+ return this.tableColumns.map((column) => ({
492
+ text: this.text(entry, column),
493
+ pill: column.pill ? this.pillOf(entry, column) : null,
494
+ }))
495
+ },
496
+
497
+ /**
498
+ * The items of a row's menu in the table variant: the row actions not
499
+ * placed in the band, then remove.
500
+ *
501
+ * @param {object} row The row.
502
+ * @return {Array<{id: string, label: string, run: Function}>}
503
+ */
504
+ menuItems(row) {
505
+ const items = row.actions
506
+ .filter((action) => action.placement !== 'band')
507
+ .map((action) => ({ id: action.id, label: action.label, run: () => this.runAction(action, row) }))
508
+ if (this.canRemove) {
509
+ items.push({ id: 'remove', label: this.removeLabel, run: () => this.remove(row) })
510
+ }
511
+ return items
512
+ },
513
+
514
+ /**
515
+ * The identity of an entry.
516
+ *
517
+ * @param {unknown} item The entry.
518
+ * @param {number} index Its position.
519
+ * @return {string}
520
+ */
521
+ keyOf(item, index) {
522
+ if (item === null || typeof item !== 'object') {
523
+ return String(item)
524
+ }
525
+ const key = this.content.key ? readPath(item, this.content.key) : undefined
526
+ return key === undefined || key === null || key === '' ? `item-${index}` : String(key)
527
+ },
528
+
529
+ /**
530
+ * A display text off an entry: a path, or `{ path, map?, format? }`.
531
+ *
532
+ * @param {object} entry The entry.
533
+ * @param {string|object} spec The spec.
534
+ * @return {string}
535
+ */
536
+ text(entry, spec) {
537
+ if (!spec) {
538
+ return ''
539
+ }
540
+ const def = typeof spec === 'string' ? { path: spec } : spec
541
+ const raw = readPath(entry, def.path)
542
+ if (raw === null || raw === undefined || raw === '') {
543
+ return ''
544
+ }
545
+ if (def.format === 'date' || def.format === 'date-long') {
546
+ const date = parseDateValue(raw)
547
+ if (date === null) {
548
+ return ''
549
+ }
550
+ return def.format === 'date-long' ? formatBoardDate(date, new Date(date.getFullYear() + 1, 0, 1)) : formatBoardDate(date)
551
+ }
552
+ if (def.map && Object.hasOwn(def.map, String(raw))) {
553
+ return this.tr(def.map[String(raw)])
554
+ }
555
+ return typeof raw === 'object' ? '' : String(raw)
556
+ },
557
+
558
+ pillOf(entry, spec) {
559
+ if (!spec || !spec.path) {
560
+ return null
561
+ }
562
+ const raw = readPath(entry, spec.path)
563
+ if (raw === null || raw === undefined || raw === '') {
564
+ return null
565
+ }
566
+ const label = this.text(entry, spec)
567
+ const variant = spec.variantMap && spec.variantMap[String(raw)]
568
+ return { label, variant: isBadgeVariant(variant) ? variant : 'default' }
569
+ },
570
+
571
+ asideOf(entry, spec) {
572
+ const value = this.text(entry, spec)
573
+ if (value === '') {
574
+ // No value: the optional `fallback` label, translated, instead of an empty aside.
575
+ return this.tr(spec.fallback)
576
+ }
577
+ return spec.prefix ? `${this.tr(spec.prefix)} ${value}` : value
578
+ },
579
+
580
+ /**
581
+ * Whether an entry matches a `{ field: value }` condition (every field equal).
582
+ *
583
+ * @param {object} entry The entry.
584
+ * @param {object|undefined} when The condition.
585
+ * @return {boolean}
586
+ */
587
+ matches(entry, when) {
588
+ if (!when || typeof when !== 'object') {
589
+ return true
590
+ }
591
+ return Object.entries(when).every(([field, expected]) => {
592
+ const actual = readPath(entry, field)
593
+ if (expected === false) {
594
+ return actual === false || actual === undefined || actual === null || actual === ''
595
+ }
596
+ return String(actual) === String(expected)
597
+ })
598
+ },
599
+
600
+ /**
601
+ * Fill `{path}` placeholders from an entry; '' when one stays empty.
602
+ *
603
+ * @param {string} template The template, translated first.
604
+ * @param {object} entry The entry.
605
+ * @return {string}
606
+ */
607
+ fill(template, entry) {
608
+ if (typeof template !== 'string') {
609
+ return ''
610
+ }
611
+ let missing = false
612
+ const out = this.tr(template).replace(/\{([\w.@]+)\}/g, (match, path) => {
613
+ const value = readPath(entry, path)
614
+ if (value === undefined || value === null || value === '') {
615
+ missing = true
616
+ return match
617
+ }
618
+ return String(value)
619
+ })
620
+ return missing ? '' : out
621
+ },
622
+
623
+ actionsFor(entry) {
624
+ if (this.readOnly) {
625
+ return []
626
+ }
627
+ const actions = Array.isArray(this.content.rowActions) ? this.content.rowActions : []
628
+ return actions
629
+ .filter((action) => action && action.id && action.request && action.request.url && this.matches(entry, action.when))
630
+ .map((action) => ({ ...action, label: this.tr(action.label || action.id) }))
631
+ },
632
+
633
+ initials(name) {
634
+ return String(name || '').split(/[\s@._-]+/).filter(Boolean).slice(0, 2).map((part) => part.charAt(0).toUpperCase()).join('')
635
+ },
636
+
637
+ removeAriaLabel(row) {
638
+ return `${this.removeLabel}: ${row.title}`
639
+ },
640
+
641
+ resetDraft() {
642
+ const draft = {}
643
+ for (const field of this.addFields) {
644
+ const first = field.type === 'select' && field.options.length > 0 ? field.options[0].value : ''
645
+ draft[field.key] = field.default !== undefined ? field.default : first
646
+ }
647
+ this.draft = draft
648
+ this.errors = {}
649
+ },
650
+
651
+ /**
652
+ * Resolve `@objectId`, `@object.<field>` and `@row.<field>` inside a string or a payload.
653
+ *
654
+ * @param {unknown} value The value.
655
+ * @param {object} entry The row entry.
656
+ * @return {unknown}
657
+ */
658
+ resolve(value, entry) {
659
+ if (Array.isArray(value)) {
660
+ return value.map((v) => this.resolve(v, entry))
661
+ }
662
+ if (value && typeof value === 'object') {
663
+ return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, this.resolve(v, entry)]))
664
+ }
665
+ if (typeof value !== 'string') {
666
+ return value
667
+ }
668
+ const object = this.objectData || {}
669
+ const read = (source, path) => {
670
+ const v = readPath(source || {}, path)
671
+ return v === undefined || v === null ? '' : v
672
+ }
673
+ const whole = value.match(/^@(row|object)\.([\w.@]+)$/)
674
+ if (whole) {
675
+ return read(whole[1] === 'row' ? entry : object, whole[2])
676
+ }
677
+ return value
678
+ .replace(/@objectId/g, () => encodeURIComponent(String(this.objectId || object.id || '')))
679
+ .replace(/@(row|object)\.([\w.@]+)/g, (m, scope, path) => encodeURIComponent(String(read(scope === 'row' ? entry : object, path))))
680
+ },
681
+
682
+ async http() {
683
+ const { default: axios } = await import('@nextcloud/axios')
684
+ return { axios }
685
+ },
686
+
687
+ /**
688
+ * Read live per-row state from `rowSource` and keep it by key.
689
+ *
690
+ * @return {Promise<void>}
691
+ */
692
+ async loadLive() {
693
+ const source = this.content.rowSource
694
+ if (!source || !source.url || !source.key || !this.objectData) {
695
+ return
696
+ }
697
+ try {
698
+ const { axios } = await this.http()
699
+ const response = await axios.get(prefixUrl(this.resolve(source.url, {})))
700
+ const data = response && response.data
701
+ const list = source.path ? readPath(data, source.path) : data
702
+ const live = {}
703
+ for (const item of Array.isArray(list) ? list : []) {
704
+ const key = item && readPath(item, source.key)
705
+ if (key !== undefined && key !== null) {
706
+ live[String(key)] = item
707
+ }
708
+ }
709
+ this.live = live
710
+ } catch (e) {
711
+ // eslint-disable-next-line no-console
712
+ console.warn('[CnFieldListWidget] rowSource could not be read:', e?.message || e)
713
+ }
714
+ },
715
+
716
+ /**
717
+ * Save a new list: re-read the stored value, apply the change to it, and
718
+ * PATCH that one field.
719
+ *
720
+ * @param {(stored: Array) => Array} change Builds the next list from the stored one, or throws an Error with a `userMessage`.
721
+ * @param {string} busyKey What is busy.
722
+ * @return {Promise<boolean>} Whether it saved.
723
+ * @spec openspec/changes/abstract-field-list/specs/field-list/spec.md#requirement-a-field-list-saves-one-field-with-one-patch
724
+ */
725
+ async save(change, busyKey) {
726
+ if (!this.objectUrl) {
727
+ this.notify(this.tr('This list cannot be saved here.'))
728
+ return false
729
+ }
730
+ this.busy = busyKey
731
+ this.notice = ''
732
+ try {
733
+ const { axios } = await this.http()
734
+ const { data: stored } = await axios.get(prefixUrl(this.objectUrl))
735
+ const current = readPath(stored || {}, this.content.field)
736
+ const next = change(Array.isArray(current) ? current : [])
737
+ const { data } = await axios.patch(prefixUrl(this.objectUrl), { [this.content.field]: next })
738
+ const written = readPath(data || {}, this.content.field)
739
+ this.saved = Array.isArray(written) ? written : next
740
+ /**
741
+ * @event saved The list was saved: one PATCH of the field succeeded.
742
+ * @type {{field: string, value: Array}}
743
+ */
744
+ this.$emit('saved', { field: this.content.field, value: this.saved })
745
+ return true
746
+ } catch (e) {
747
+ this.notify(e && e.userMessage ? e.userMessage : serverErrorMessage(e))
748
+ return false
749
+ } finally {
750
+ this.busy = ''
751
+ }
752
+ },
753
+
754
+ notify(message, type = 'error') {
755
+ this.notice = message
756
+ this.noticeType = type
757
+ },
758
+
759
+ /**
760
+ * Validate the draft and add it.
761
+ *
762
+ * @return {Promise<void>}
763
+ * @spec openspec/changes/abstract-field-list/specs/field-list/spec.md#requirement-a-field-list-adds-and-removes-entries
764
+ */
765
+ async add() {
766
+ const errors = {}
767
+ for (const field of this.addFields) {
768
+ const value = this.draft[field.key]
769
+ const empty = value === undefined || value === null || String(value).trim() === ''
770
+ if (field.required && empty) {
771
+ errors[field.key] = this.tr('Fill this in.')
772
+ } else if (!empty && field.pattern && !new RegExp(field.pattern).test(String(value).trim())) {
773
+ errors[field.key] = this.tr(field.patternMessage || 'This value is not valid.')
774
+ }
775
+ }
776
+ this.errors = errors
777
+ if (Object.keys(errors).length > 0) {
778
+ return
779
+ }
780
+ const entry = { ...((this.content.add && this.content.add.defaults) || {}) }
781
+ for (const field of this.addFields) {
782
+ const value = this.draft[field.key]
783
+ if (value !== undefined && value !== null && String(value).trim() !== '') {
784
+ entry[field.key] = typeof value === 'string' ? value.trim() : value
785
+ }
786
+ }
787
+ const unique = this.content.add && this.content.add.unique
788
+ const saved = await this.save((stored) => {
789
+ if (unique && stored.some((item) => item && String(readPath(item, unique)) === String(entry[unique]))) {
790
+ const error = new Error('duplicate')
791
+ error.userMessage = this.tr('This is already in the list.')
792
+ throw error
793
+ }
794
+ return [...stored, entry]
795
+ }, '*')
796
+ if (saved) {
797
+ this.resetDraft()
798
+ this.adding = false
799
+ this.loadLive()
800
+ }
801
+ },
802
+
803
+ /**
804
+ * Remove an entry by its key.
805
+ *
806
+ * @param {object} row The row.
807
+ * @return {Promise<void>}
808
+ */
809
+ async remove(row) {
810
+ await this.save((stored) => stored.filter((item, index) => this.keyOf(item, index) !== row.key), row.key)
811
+ },
812
+
813
+ /**
814
+ * Run a row action's request, then re-read the live state.
815
+ *
816
+ * @param {object} action The action.
817
+ * @param {object} row The row.
818
+ * @return {Promise<void>}
819
+ * @spec openspec/changes/abstract-field-list/specs/field-list/spec.md#requirement-a-field-list-row-can-run-a-configured-request
820
+ */
821
+ async runAction(action, row) {
822
+ this.busy = row.key
823
+ this.notice = ''
824
+ try {
825
+ const { axios } = await this.http()
826
+ const method = String(action.request.method || 'POST').toLowerCase()
827
+ const payload = this.resolve(action.request.payload || {}, row.entry)
828
+ const response = method === 'get'
829
+ ? await axios.get(prefixUrl(this.resolve(action.request.url, row.entry)))
830
+ : await axios[method](prefixUrl(this.resolve(action.request.url, row.entry)), payload)
831
+ const message = response && response.data && typeof response.data.message === 'string' ? response.data.message : ''
832
+ if (message) {
833
+ this.notify(message, 'info')
834
+ }
835
+ await this.loadLive()
836
+ } catch (e) {
837
+ this.notify(serverErrorMessage(e))
838
+ } finally {
839
+ this.busy = ''
840
+ }
841
+ },
842
+ },
843
+ }
844
+ </script>
845
+
846
+ <style scoped>
847
+ .cn-field-list-widget {
848
+ display: flex;
849
+ flex-direction: column;
850
+ gap: 12px;
851
+ min-width: 0;
852
+ }
853
+
854
+ .cn-field-list-widget__rows {
855
+ list-style: none;
856
+ margin: 0;
857
+ padding: 0;
858
+ display: flex;
859
+ flex-direction: column;
860
+ }
861
+
862
+ .cn-field-list-widget__row {
863
+ padding: 10px 0;
864
+ border-top: 1px solid var(--color-border);
865
+ }
866
+
867
+ .cn-field-list-widget__row:first-child {
868
+ border-top: none;
869
+ padding-top: 0;
870
+ }
871
+
872
+ .cn-field-list-widget__line {
873
+ display: flex;
874
+ align-items: center;
875
+ gap: 12px;
876
+ min-width: 0;
877
+ }
878
+
879
+ .cn-field-list-widget__avatar {
880
+ flex: 0 0 auto;
881
+ display: inline-flex;
882
+ align-items: center;
883
+ justify-content: center;
884
+ width: 32px;
885
+ height: 32px;
886
+ border-radius: 50%;
887
+ background: var(--color-primary-element-light);
888
+ color: var(--color-primary-element-light-text, var(--color-main-text));
889
+ font-size: 12px;
890
+ font-weight: 700;
891
+ }
892
+
893
+ .cn-field-list-widget__main {
894
+ display: flex;
895
+ flex-direction: column;
896
+ flex: 1 1 auto;
897
+ min-width: 0;
898
+ }
899
+
900
+ .cn-field-list-widget__title {
901
+ font-weight: 600;
902
+ overflow-wrap: anywhere;
903
+ }
904
+
905
+ .cn-field-list-widget__meta,
906
+ .cn-field-list-widget__aside,
907
+ .cn-field-list-widget__detail,
908
+ .cn-field-list-widget__hint,
909
+ .cn-field-list-widget__empty {
910
+ color: var(--color-text-maxcontrast);
911
+ }
912
+
913
+ .cn-field-list-widget__meta,
914
+ .cn-field-list-widget__aside,
915
+ .cn-field-list-widget__detail {
916
+ font-size: 13px;
917
+ }
918
+
919
+ .cn-field-list-widget__aside {
920
+ white-space: nowrap;
921
+ }
922
+
923
+ .cn-field-list-widget__detail,
924
+ .cn-field-list-widget__hint,
925
+ .cn-field-list-widget__empty {
926
+ margin: 4px 0 0;
927
+ overflow-wrap: anywhere;
928
+ }
929
+
930
+ .cn-field-list-widget__add {
931
+ display: flex;
932
+ flex-wrap: wrap;
933
+ align-items: flex-end;
934
+ gap: 8px 12px;
935
+ }
936
+
937
+ .cn-field-list-widget__add > * {
938
+ flex: 1 1 180px;
939
+ }
940
+
941
+ .cn-field-list-widget__add > button {
942
+ flex: 0 0 auto;
943
+ }
944
+
945
+ .cn-field-list-widget__toolbar {
946
+ display: flex;
947
+ justify-content: flex-end;
948
+ }
949
+
950
+ .cn-field-list-widget__table-wrap {
951
+ display: flex;
952
+ flex-direction: column;
953
+ gap: 12px;
954
+ overflow-x: auto;
955
+ }
956
+
957
+ .cn-field-list-widget__table {
958
+ width: 100%;
959
+ border-collapse: collapse;
960
+ }
961
+
962
+ .cn-field-list-widget__th {
963
+ padding: 8px 12px;
964
+ text-align: start;
965
+ font-size: 13px;
966
+ font-weight: 600;
967
+ white-space: nowrap;
968
+ color: var(--color-text-maxcontrast);
969
+ background: var(--color-background-hover);
970
+ }
971
+
972
+ .cn-field-list-widget__th--menu,
973
+ .cn-field-list-widget__td--menu {
974
+ width: 1%;
975
+ text-align: end;
976
+ }
977
+
978
+ .cn-field-list-widget__tr {
979
+ border-top: 1px solid var(--cn-board-hairline, var(--color-border));
980
+ }
981
+
982
+ .cn-field-list-widget__td {
983
+ padding: 10px 12px;
984
+ vertical-align: middle;
985
+ overflow-wrap: anywhere;
986
+ }
987
+
988
+ .cn-field-list-widget__sr {
989
+ position: absolute;
990
+ width: 1px;
991
+ height: 1px;
992
+ overflow: hidden;
993
+ clip-path: inset(50%);
994
+ }
995
+
996
+ .cn-field-list-widget__band {
997
+ display: flex;
998
+ flex-wrap: wrap;
999
+ align-items: center;
1000
+ justify-content: space-between;
1001
+ gap: 8px 12px;
1002
+ padding: 12px 14px;
1003
+ border: 1px solid var(--color-warning);
1004
+ border-radius: 10px;
1005
+ background: color-mix(in srgb, var(--color-warning) 14%, var(--color-main-background));
1006
+ font-size: 14px;
1007
+ }
1008
+ </style>