@conduction/nextcloud-vue 2.80.0 → 2.81.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (290) hide show
  1. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
  2. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +3 -3
  3. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
  4. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +80 -4
  5. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  6. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +95 -91
  7. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  8. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js +140 -10
  9. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js.map +1 -1
  10. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js +52 -16
  11. package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js.map +1 -1
  12. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +17 -3
  13. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
  14. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +63 -52
  15. package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
  16. package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue.js +10 -0
  17. package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue.js.map +1 -0
  18. package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue2.js +252 -0
  19. package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue2.js.map +1 -0
  20. package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue3.js +140 -0
  21. package/dist/esm/components/CnChipsFieldWidget/CnChipsFieldWidget.vue3.js.map +1 -0
  22. package/dist/esm/components/CnChipsFieldWidget/index.js +26 -0
  23. package/dist/esm/components/CnChipsFieldWidget/index.js.map +1 -0
  24. package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue.js +10 -0
  25. package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue.js.map +1 -0
  26. package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue2.js +354 -0
  27. package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue2.js.map +1 -0
  28. package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue3.js +172 -0
  29. package/dist/esm/components/CnChoicePickerWidget/CnChoicePickerWidget.vue3.js.map +1 -0
  30. package/dist/esm/components/CnChoicePickerWidget/index.js +27 -0
  31. package/dist/esm/components/CnChoicePickerWidget/index.js.map +1 -0
  32. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js +1 -1
  33. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js.map +1 -1
  34. package/dist/esm/components/CnConversationThread/CnConversationWidget.vue3.js.map +1 -1
  35. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js +2 -2
  36. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js.map +1 -1
  37. package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue3.js.map +1 -1
  38. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +65 -5
  39. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  40. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +2 -1
  41. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  42. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +19 -11
  43. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  44. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +3 -2
  45. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  46. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +72 -0
  47. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  48. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +21 -4
  49. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  50. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue2.js +1 -1
  51. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue2.js.map +1 -1
  52. package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue3.js.map +1 -1
  53. package/dist/esm/components/CnFacetSidebar/CnFacetSidebar.vue2.js +7 -7
  54. package/dist/esm/components/CnFacetSidebar/CnFacetSidebar.vue2.js.map +1 -1
  55. package/dist/esm/components/CnFacetSidebar/CnFacetSidebar.vue3.js.map +1 -1
  56. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +131 -0
  57. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
  58. package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
  59. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js +80 -5
  60. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js.map +1 -1
  61. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js +282 -69
  62. package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js.map +1 -1
  63. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +302 -4
  64. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  65. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +417 -244
  66. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  67. package/dist/esm/components/CnFilesBrowser/filesBrowser.js +101 -1
  68. package/dist/esm/components/CnFilesBrowser/filesBrowser.js.map +1 -1
  69. package/dist/esm/components/CnFilesBrowser/filesBrowserColumns.js +141 -1
  70. package/dist/esm/components/CnFilesBrowser/filesBrowserColumns.js.map +1 -1
  71. package/dist/esm/components/CnFormPage/CnFormPage.vue2.js +60 -0
  72. package/dist/esm/components/CnFormPage/CnFormPage.vue2.js.map +1 -1
  73. package/dist/esm/components/CnFormPage/CnFormPage.vue3.js +74 -49
  74. package/dist/esm/components/CnFormPage/CnFormPage.vue3.js.map +1 -1
  75. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +29 -2
  76. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  77. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +48 -4
  78. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  79. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js +7 -7
  80. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js.map +1 -1
  81. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js.map +1 -1
  82. package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue2.js +1 -1
  83. package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue2.js.map +1 -1
  84. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +65 -2
  85. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
  86. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +6 -2
  87. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
  88. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js +63 -1
  89. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
  90. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +8 -1
  91. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
  92. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js +14 -0
  93. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js.map +1 -1
  94. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue3.js +5 -1
  95. package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue3.js.map +1 -1
  96. package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue.js +10 -0
  97. package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue.js.map +1 -0
  98. package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue2.js +174 -0
  99. package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue2.js.map +1 -0
  100. package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue3.js +75 -0
  101. package/dist/esm/components/CnRadioCardsWidget/CnRadioCardsWidget.vue3.js.map +1 -0
  102. package/dist/esm/components/CnRadioCardsWidget/index.js +28 -0
  103. package/dist/esm/components/CnRadioCardsWidget/index.js.map +1 -0
  104. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +30 -6
  105. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
  106. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js +34 -25
  107. package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
  108. package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue.js +10 -0
  109. package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue.js.map +1 -0
  110. package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue2.js +222 -0
  111. package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue2.js.map +1 -0
  112. package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue3.js +64 -0
  113. package/dist/esm/components/CnSavedViewsWidget/CnSavedViewsWidget.vue3.js.map +1 -0
  114. package/dist/esm/components/CnSavedViewsWidget/index.js +30 -0
  115. package/dist/esm/components/CnSavedViewsWidget/index.js.map +1 -0
  116. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue2.js.map +1 -1
  117. package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue3.js.map +1 -1
  118. package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue.js +10 -0
  119. package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue.js.map +1 -0
  120. package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue2.js +347 -0
  121. package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue2.js.map +1 -0
  122. package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue3.js +184 -0
  123. package/dist/esm/components/CnSettingsFormWidget/CnSettingsFormWidget.vue3.js.map +1 -0
  124. package/dist/esm/components/CnSettingsFormWidget/index.js +27 -0
  125. package/dist/esm/components/CnSettingsFormWidget/index.js.map +1 -0
  126. package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js +87 -6
  127. package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
  128. package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
  129. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js +84 -1
  130. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js.map +1 -1
  131. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js +85 -41
  132. package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js.map +1 -1
  133. package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue.js +10 -0
  134. package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue.js.map +1 -0
  135. package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue2.js +159 -0
  136. package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue2.js.map +1 -0
  137. package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue3.js +60 -0
  138. package/dist/esm/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue3.js.map +1 -0
  139. package/dist/esm/components/CnSwitchRowsWidget/index.js +26 -0
  140. package/dist/esm/components/CnSwitchRowsWidget/index.js.map +1 -0
  141. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +47 -1
  142. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
  143. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +16 -14
  144. package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
  145. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js +80 -1
  146. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js.map +1 -1
  147. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js +75 -32
  148. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js.map +1 -1
  149. package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue.js +10 -0
  150. package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue.js.map +1 -0
  151. package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue2.js +269 -0
  152. package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue2.js.map +1 -0
  153. package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue3.js +106 -0
  154. package/dist/esm/components/CnToggleTableWidget/CnToggleTableWidget.vue3.js.map +1 -0
  155. package/dist/esm/components/CnToggleTableWidget/index.js +27 -0
  156. package/dist/esm/components/CnToggleTableWidget/index.js.map +1 -0
  157. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +7 -0
  158. package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
  159. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +7 -2
  160. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
  161. package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
  162. package/dist/esm/components/CnWidgetObjectTable/dashboardRegistration.js +5 -0
  163. package/dist/esm/components/CnWidgetObjectTable/dashboardRegistration.js.map +1 -1
  164. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js +197 -6
  165. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js.map +1 -1
  166. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js +96 -50
  167. package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js.map +1 -1
  168. package/dist/esm/composables/useListView.js +4 -4
  169. package/dist/esm/composables/useListView.js.map +1 -1
  170. package/dist/esm/index.js +17 -1
  171. package/dist/esm/index.js.map +1 -1
  172. package/dist/esm/l10n/en.json.js +9 -1
  173. package/dist/esm/l10n/en.json.js.map +1 -1
  174. package/dist/esm/l10n/nl.json.js +55 -1
  175. package/dist/esm/l10n/nl.json.js.map +1 -1
  176. package/dist/esm/nextcloud-vue.css +1041 -12
  177. package/dist/esm/utils/contentHeaderLink.js +51 -0
  178. package/dist/esm/utils/contentHeaderLink.js.map +1 -0
  179. package/dist/esm/utils/facets.js +75 -3
  180. package/dist/esm/utils/facets.js.map +1 -1
  181. package/dist/esm/utils/fetchFilterCounts.js +40 -5
  182. package/dist/esm/utils/fetchFilterCounts.js.map +1 -1
  183. package/dist/esm/utils/fillTemplate.js +7 -1
  184. package/dist/esm/utils/fillTemplate.js.map +1 -1
  185. package/dist/esm/utils/initials.js +32 -0
  186. package/dist/esm/utils/initials.js.map +1 -0
  187. package/dist/esm/utils/libraryWidgetKeys.js +7 -0
  188. package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
  189. package/dist/esm/utils/objectWrite.js +59 -0
  190. package/dist/esm/utils/objectWrite.js.map +1 -0
  191. package/dist/esm/utils/pageControls.js +81 -0
  192. package/dist/esm/utils/pageControls.js.map +1 -0
  193. package/dist/esm/utils/personalLenses.js +40 -3
  194. package/dist/esm/utils/personalLenses.js.map +1 -1
  195. package/dist/esm/utils/resolveFilterTokens.js +1 -1
  196. package/dist/esm/utils/resolveFilterTokens.js.map +1 -1
  197. package/dist/esm/utils/resolveManifestTokens.js +2 -0
  198. package/dist/esm/utils/resolveManifestTokens.js.map +1 -1
  199. package/dist/esm/utils/sentinelTokens.js +19 -1
  200. package/dist/esm/utils/sentinelTokens.js.map +1 -1
  201. package/dist/esm/utils/userGroupsPeek.js +91 -0
  202. package/dist/esm/utils/userGroupsPeek.js.map +1 -0
  203. package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
  204. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  205. package/dist/esm/utils/visibleWhen.js +57 -5
  206. package/dist/esm/utils/visibleWhen.js.map +1 -1
  207. package/dist/esm/utils/widgetDispatch.js +1 -1
  208. package/dist/esm/utils/widgetDispatch.js.map +1 -1
  209. package/dist/esm/utils/widgetVisibility.js +4 -57
  210. package/dist/esm/utils/widgetVisibility.js.map +1 -1
  211. package/dist/nextcloud-vue.cjs.js +13589 -7833
  212. package/dist/nextcloud-vue.cjs.js.map +2 -2
  213. package/dist/nextcloud-vue.css +1041 -12
  214. package/l10n/en.json +9 -1
  215. package/l10n/nl.json +55 -1
  216. package/package.json +2 -1
  217. package/scripts/check-docs-mdx.mjs +108 -0
  218. package/src/components/CnActionsBar/CnActionsBar.vue +6 -1
  219. package/src/components/CnAppRoot/CnAppRoot.md +4 -2
  220. package/src/components/CnAppRoot/CnAppRoot.vue +151 -59
  221. package/src/components/CnBannerWidget/CnBannerWidget.vue +205 -20
  222. package/src/components/CnCellRenderer/CnCellRenderer.md +2 -0
  223. package/src/components/CnCellRenderer/CnCellRenderer.vue +47 -4
  224. package/src/components/CnChipsFieldWidget/CnChipsFieldWidget.vue +433 -0
  225. package/src/components/CnChipsFieldWidget/index.js +25 -0
  226. package/src/components/CnChoicePickerWidget/CnChoicePickerWidget.vue +556 -0
  227. package/src/components/CnChoicePickerWidget/index.js +26 -0
  228. package/src/components/CnConversationThread/CnConversationWidget.vue +1 -1
  229. package/src/components/CnDashboardGrid/CnDashboardGrid.vue +2 -2
  230. package/src/components/CnDashboardPage/CnDashboardPage.vue +78 -5
  231. package/src/components/CnDataTable/CnDataTable.vue +21 -12
  232. package/src/components/CnDetailPage/CnDetailPage.vue +89 -3
  233. package/src/components/CnDocumentReviewList/CnDocumentReviewWidget.vue +1 -1
  234. package/src/components/CnFacetSidebar/CnFacetSidebar.vue +7 -7
  235. package/src/components/CnFactsWidget/CnFactsWidget.vue +142 -2
  236. package/src/components/CnFieldListWidget/CnFieldListWidget.vue +268 -6
  237. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +588 -117
  238. package/src/components/CnFilesBrowser/filesBrowser.js +100 -0
  239. package/src/components/CnFilesBrowser/filesBrowserColumns.js +140 -0
  240. package/src/components/CnFormPage/CnFormPage.vue +81 -0
  241. package/src/components/CnIndexPage/CnIndexPage.vue +56 -4
  242. package/src/components/CnIndexSidebar/CnIndexSidebar.vue +7 -7
  243. package/src/components/CnNextStepCard/CnNextStepWidget.vue +1 -1
  244. package/src/components/CnObjectListWidget/CnObjectListWidget.vue +69 -2
  245. package/src/components/CnObjectSidebar/CnFilesTab.vue +70 -1
  246. package/src/components/CnObjectSidebar/CnNotesTab.vue +16 -0
  247. package/src/components/CnRadioCardsWidget/CnRadioCardsWidget.vue +313 -0
  248. package/src/components/CnRadioCardsWidget/index.js +27 -0
  249. package/src/components/CnRecordListWidget/CnRecordListWidget.vue +55 -6
  250. package/src/components/CnSavedViewsWidget/CnSavedViewsWidget.vue +296 -0
  251. package/src/components/CnSavedViewsWidget/index.js +29 -0
  252. package/src/components/CnSegmentedControl/CnSegmentedControl.vue +13 -0
  253. package/src/components/CnSettingsFormWidget/CnSettingsFormWidget.vue +555 -0
  254. package/src/components/CnSettingsFormWidget/index.js +26 -0
  255. package/src/components/CnStatWidget/CnStatWidget.vue +87 -6
  256. package/src/components/CnStatsBlockWidget/CnStatsBlockWidget.vue +180 -3
  257. package/src/components/CnSwitchRowsWidget/CnSwitchRowsWidget.vue +226 -0
  258. package/src/components/CnSwitchRowsWidget/index.js +25 -0
  259. package/src/components/CnTabsWidget/CnTabsWidget.vue +82 -3
  260. package/src/components/CnTasksWidget/CnTasksWidget.vue +175 -15
  261. package/src/components/CnToggleTableWidget/CnToggleTableWidget.vue +386 -0
  262. package/src/components/CnToggleTableWidget/index.js +26 -0
  263. package/src/components/CnWidgetGrid/registerDashboardWidgets.js +7 -0
  264. package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +7 -2
  265. package/src/components/CnWidgetObjectTable/dashboardRegistration.js +5 -0
  266. package/src/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue +309 -11
  267. package/src/components/index.js +15 -0
  268. package/src/composables/useListView.js +4 -4
  269. package/src/css/look-board-detail.css +36 -2
  270. package/src/css/look-board-index.css +39 -8
  271. package/src/css/look-board.css +72 -0
  272. package/src/index.js +15 -0
  273. package/src/schemas/app-manifest-v2.schema.json +93 -9
  274. package/src/utils/contentHeaderLink.js +48 -0
  275. package/src/utils/facets.js +74 -2
  276. package/src/utils/fetchFilterCounts.js +39 -4
  277. package/src/utils/fillTemplate.js +7 -1
  278. package/src/utils/initials.js +29 -0
  279. package/src/utils/libraryWidgetKeys.js +7 -0
  280. package/src/utils/objectWrite.js +55 -0
  281. package/src/utils/pageControls.js +78 -0
  282. package/src/utils/personalLenses.js +39 -2
  283. package/src/utils/resolveFilterTokens.js +3 -1
  284. package/src/utils/resolveManifestTokens.js +2 -0
  285. package/src/utils/sentinelTokens.js +19 -1
  286. package/src/utils/userGroupsPeek.js +87 -0
  287. package/src/utils/validateManifestV2.compiled.js +3 -3
  288. package/src/utils/visibleWhen.js +56 -4
  289. package/src/utils/widgetDispatch.js +1 -1
  290. package/src/utils/widgetVisibility.js +7 -56
@@ -1,4 +1,4 @@
1
- import { resolveComponent, openBlock, createElementBlock, toDisplayString, Fragment, renderList, createElementVNode, createCommentVNode, createBlock, withCtx, createTextVNode, withModifiers, createVNode } from 'vue';
1
+ import { resolveComponent, openBlock, createElementBlock, createVNode, withCtx, createTextVNode, toDisplayString, createCommentVNode, withModifiers, Fragment, renderList, createBlock, createElementVNode } from 'vue';
2
2
 
3
3
  const _hoisted_1 = {
4
4
  class: "cn-field-list-widget",
@@ -6,84 +6,237 @@ const _hoisted_1 = {
6
6
  };
7
7
  const _hoisted_2 = {
8
8
  key: 0,
9
+ class: "cn-field-list-widget__toolbar"
10
+ };
11
+ const _hoisted_3 = ["data-testid"];
12
+ const _hoisted_4 = {
13
+ key: 2,
9
14
  class: "cn-field-list-widget__empty",
10
15
  "data-testid": "cn-field-list-empty"
11
16
  };
12
- const _hoisted_3 = {
13
- key: 1,
14
- class: "cn-field-list-widget__rows"
17
+ const _hoisted_5 = {
18
+ key: 3,
19
+ class: "cn-field-list-widget__table-wrap"
15
20
  };
16
- const _hoisted_4 = ["data-testid"];
17
- const _hoisted_5 = { class: "cn-field-list-widget__line" };
18
21
  const _hoisted_6 = {
22
+ class: "cn-field-list-widget__table",
23
+ "data-testid": "cn-field-list-table"
24
+ };
25
+ const _hoisted_7 = {
26
+ key: 0,
27
+ scope: "col",
28
+ class: "cn-field-list-widget__th cn-field-list-widget__th--menu"
29
+ };
30
+ const _hoisted_8 = { class: "cn-field-list-widget__sr" };
31
+ const _hoisted_9 = ["data-testid"];
32
+ const _hoisted_10 = { key: 1 };
33
+ const _hoisted_11 = {
34
+ key: 0,
35
+ class: "cn-field-list-widget__td cn-field-list-widget__td--menu"
36
+ };
37
+ const _hoisted_12 = ["data-testid"];
38
+ const _hoisted_13 = { class: "cn-field-list-widget__band-text" };
39
+ const _hoisted_14 = {
40
+ key: 4,
41
+ class: "cn-field-list-widget__rows"
42
+ };
43
+ const _hoisted_15 = ["data-testid"];
44
+ const _hoisted_16 = { class: "cn-field-list-widget__line" };
45
+ const _hoisted_17 = {
19
46
  key: 0,
20
47
  class: "cn-field-list-widget__avatar",
21
48
  "aria-hidden": "true"
22
49
  };
23
- const _hoisted_7 = { class: "cn-field-list-widget__main" };
24
- const _hoisted_8 = { class: "cn-field-list-widget__title" };
25
- const _hoisted_9 = {
50
+ const _hoisted_18 = { class: "cn-field-list-widget__main" };
51
+ const _hoisted_19 = { class: "cn-field-list-widget__title" };
52
+ const _hoisted_20 = {
26
53
  key: 0,
27
54
  class: "cn-field-list-widget__meta"
28
55
  };
29
- const _hoisted_10 = {
56
+ const _hoisted_21 = {
30
57
  key: 2,
31
58
  class: "cn-field-list-widget__aside"
32
59
  };
33
- const _hoisted_11 = {
60
+ const _hoisted_22 = {
34
61
  key: 0,
35
62
  class: "cn-field-list-widget__detail"
36
63
  };
37
- const _hoisted_12 = {
38
- key: 2,
64
+ const _hoisted_23 = {
65
+ key: 5,
39
66
  class: "cn-field-list-widget__hint"
40
67
  };
41
- const _hoisted_13 = ["data-testid"];
68
+ const _hoisted_24 = {
69
+ key: 6,
70
+ class: "cn-field-list-widget__toolbar"
71
+ };
72
+ const _hoisted_25 = ["data-testid"];
42
73
 
43
74
  function render(_ctx, _cache, $props, $setup, $data, $options) {
44
- const _component_CnStatusBadge = resolveComponent("CnStatusBadge");
45
75
  const _component_NcButton = resolveComponent("NcButton");
46
76
  const _component_NcSelect = resolveComponent("NcSelect");
47
77
  const _component_NcTextField = resolveComponent("NcTextField");
78
+ const _component_CnStatusBadge = resolveComponent("CnStatusBadge");
79
+ const _component_NcActionButton = resolveComponent("NcActionButton");
80
+ const _component_NcActions = resolveComponent("NcActions");
48
81
  const _component_NcNoteCard = resolveComponent("NcNoteCard");
49
82
 
50
83
  return (openBlock(), createElementBlock("div", _hoisted_1, [
51
- ($options.rows.length === 0)
52
- ? (openBlock(), createElementBlock("p", _hoisted_2, toDisplayString($options.emptyLabel), 1 /* TEXT */))
53
- : (openBlock(), createElementBlock("ul", _hoisted_3, [
54
- (openBlock(true), createElementBlock(Fragment, null, renderList($options.rows, (row) => {
55
- return (openBlock(), createElementBlock("li", {
56
- key: row.key,
57
- class: "cn-field-list-widget__row",
58
- "data-testid": `cn-field-list-row-${row.key}`
84
+ ($options.canAdd && $options.addTrigger && $options.addPlacement === 'header')
85
+ ? (openBlock(), createElementBlock("div", _hoisted_2, [
86
+ createVNode(_component_NcButton, {
87
+ variant: "secondary",
88
+ disabled: $data.busy !== '',
89
+ "data-testid": "cn-field-list-open-add",
90
+ onClick: _cache[0] || (_cache[0] = $event => ($data.adding = !$data.adding))
91
+ }, {
92
+ default: withCtx(() => [
93
+ createTextVNode(" + " + toDisplayString($options.addLabel), 1 /* TEXT */)
94
+ ]),
95
+ _: 1 /* STABLE */
96
+ }, 8 /* PROPS */, ["disabled"])
97
+ ]))
98
+ : createCommentVNode("v-if", true),
99
+ ($options.canAdd && $options.addTrigger && $options.addPlacement === 'header' && $data.adding)
100
+ ? (openBlock(), createElementBlock("form", {
101
+ key: 1,
102
+ class: "cn-field-list-widget__add",
103
+ "data-testid": "cn-field-list-add",
104
+ onSubmit: _cache[1] || (_cache[1] = withModifiers((...args) => ($options.add && $options.add(...args)), ["prevent"]))
105
+ }, [
106
+ (openBlock(true), createElementBlock(Fragment, null, renderList($options.addFields, (field) => {
107
+ return (openBlock(), createElementBlock("div", {
108
+ key: field.key,
109
+ class: "cn-field-list-widget__field",
110
+ "data-testid": `cn-field-list-input-${field.key}`
59
111
  }, [
60
- createElementVNode("div", _hoisted_5, [
61
- (row.initials)
62
- ? (openBlock(), createElementBlock("span", _hoisted_6, toDisplayString(row.initials), 1 /* TEXT */))
63
- : createCommentVNode("v-if", true),
64
- createElementVNode("span", _hoisted_7, [
65
- createElementVNode("span", _hoisted_8, toDisplayString(row.title), 1 /* TEXT */),
66
- (row.meta)
67
- ? (openBlock(), createElementBlock("span", _hoisted_9, toDisplayString(row.meta), 1 /* TEXT */))
112
+ (field.type === 'select')
113
+ ? (openBlock(), createBlock(_component_NcSelect, {
114
+ key: 0,
115
+ modelValue: $data.draft[field.key],
116
+ "onUpdate:modelValue": $event => (($data.draft[field.key]) = $event),
117
+ inputLabel: field.label,
118
+ options: field.options,
119
+ reduce: (option) => option.value,
120
+ clearable: false,
121
+ label: "label"
122
+ }, null, 8 /* PROPS */, ["modelValue", "onUpdate:modelValue", "inputLabel", "options", "reduce"]))
123
+ : (openBlock(), createBlock(_component_NcTextField, {
124
+ key: 1,
125
+ modelValue: $data.draft[field.key],
126
+ "onUpdate:modelValue": $event => (($data.draft[field.key]) = $event),
127
+ label: field.label,
128
+ error: Boolean($data.errors[field.key]),
129
+ helperText: $data.errors[field.key] || ''
130
+ }, null, 8 /* PROPS */, ["modelValue", "onUpdate:modelValue", "label", "error", "helperText"]))
131
+ ], 8 /* PROPS */, _hoisted_3))
132
+ }), 128 /* KEYED_FRAGMENT */)),
133
+ createVNode(_component_NcButton, {
134
+ type: "submit",
135
+ variant: "primary",
136
+ disabled: $data.busy !== '',
137
+ "data-testid": "cn-field-list-submit"
138
+ }, {
139
+ default: withCtx(() => [
140
+ createTextVNode(toDisplayString($options.addLabel), 1 /* TEXT */)
141
+ ]),
142
+ _: 1 /* STABLE */
143
+ }, 8 /* PROPS */, ["disabled"])
144
+ ], 32 /* NEED_HYDRATION */))
145
+ : createCommentVNode("v-if", true),
146
+ ($options.rows.length === 0)
147
+ ? (openBlock(), createElementBlock("p", _hoisted_4, toDisplayString($options.emptyLabel), 1 /* TEXT */))
148
+ : ($options.isTable)
149
+ ? (openBlock(), createElementBlock("div", _hoisted_5, [
150
+ createElementVNode("table", _hoisted_6, [
151
+ createElementVNode("thead", null, [
152
+ createElementVNode("tr", null, [
153
+ (openBlock(true), createElementBlock(Fragment, null, renderList($options.tableColumns, (column) => {
154
+ return (openBlock(), createElementBlock("th", {
155
+ key: column.key,
156
+ scope: "col",
157
+ class: "cn-field-list-widget__th"
158
+ }, toDisplayString(column.header), 1 /* TEXT */))
159
+ }), 128 /* KEYED_FRAGMENT */)),
160
+ ($options.hasMenu)
161
+ ? (openBlock(), createElementBlock("th", _hoisted_7, [
162
+ createElementVNode("span", _hoisted_8, toDisplayString($options.tr('Actions')), 1 /* TEXT */)
163
+ ]))
68
164
  : createCommentVNode("v-if", true)
69
- ]),
70
- (row.pill)
71
- ? (openBlock(), createBlock(_component_CnStatusBadge, {
72
- key: 1,
73
- label: row.pill.label,
74
- variant: row.pill.variant,
75
- size: "small"
76
- }, null, 8 /* PROPS */, ["label", "variant"]))
77
- : createCommentVNode("v-if", true),
78
- (row.aside)
79
- ? (openBlock(), createElementBlock("span", _hoisted_10, toDisplayString(row.aside), 1 /* TEXT */))
80
- : createCommentVNode("v-if", true),
81
- (openBlock(true), createElementBlock(Fragment, null, renderList(row.actions, (action) => {
165
+ ])
166
+ ]),
167
+ createElementVNode("tbody", null, [
168
+ (openBlock(true), createElementBlock(Fragment, null, renderList($options.rows, (row) => {
169
+ return (openBlock(), createElementBlock("tr", {
170
+ key: row.key,
171
+ class: "cn-field-list-widget__tr",
172
+ "data-testid": `cn-field-list-row-${row.key}`
173
+ }, [
174
+ (openBlock(true), createElementBlock(Fragment, null, renderList($options.tableColumns, (column, index) => {
175
+ return (openBlock(), createElementBlock("td", {
176
+ key: column.key,
177
+ class: "cn-field-list-widget__td"
178
+ }, [
179
+ (column.pill && row.cells[index].pill)
180
+ ? (openBlock(), createBlock(_component_CnStatusBadge, {
181
+ key: 0,
182
+ label: row.cells[index].pill.label,
183
+ variant: row.cells[index].pill.variant,
184
+ size: "small"
185
+ }, null, 8 /* PROPS */, ["label", "variant"]))
186
+ : (column.strong)
187
+ ? (openBlock(), createElementBlock("strong", _hoisted_10, toDisplayString(row.cells[index].text), 1 /* TEXT */))
188
+ : (openBlock(), createElementBlock(Fragment, { key: 2 }, [
189
+ createTextVNode(toDisplayString(row.cells[index].text), 1 /* TEXT */)
190
+ ], 64 /* STABLE_FRAGMENT */))
191
+ ]))
192
+ }), 128 /* KEYED_FRAGMENT */)),
193
+ ($options.hasMenu)
194
+ ? (openBlock(), createElementBlock("td", _hoisted_11, [
195
+ ($options.menuItems(row).length > 0)
196
+ ? (openBlock(), createBlock(_component_NcActions, {
197
+ key: 0,
198
+ forceMenu: true,
199
+ disabled: $data.busy !== '',
200
+ ariaLabel: `${$options.tr('More actions')}: ${row.title}`,
201
+ "data-testid": `cn-field-list-menu-${row.key}`
202
+ }, {
203
+ default: withCtx(() => [
204
+ (openBlock(true), createElementBlock(Fragment, null, renderList($options.menuItems(row), (item) => {
205
+ return (openBlock(), createBlock(_component_NcActionButton, {
206
+ key: item.id,
207
+ "data-testid": `cn-field-list-menu-${item.id}-${row.key}`,
208
+ onClick: item.run
209
+ }, {
210
+ default: withCtx(() => [
211
+ createTextVNode(toDisplayString(item.label), 1 /* TEXT */)
212
+ ]),
213
+ _: 2 /* DYNAMIC */
214
+ }, 1032 /* PROPS, DYNAMIC_SLOTS */, ["data-testid", "onClick"]))
215
+ }), 128 /* KEYED_FRAGMENT */))
216
+ ]),
217
+ _: 2 /* DYNAMIC */
218
+ }, 1032 /* PROPS, DYNAMIC_SLOTS */, ["disabled", "ariaLabel", "data-testid"]))
219
+ : createCommentVNode("v-if", true)
220
+ ]))
221
+ : createCommentVNode("v-if", true)
222
+ ], 8 /* PROPS */, _hoisted_9))
223
+ }), 128 /* KEYED_FRAGMENT */))
224
+ ])
225
+ ]),
226
+ (openBlock(true), createElementBlock(Fragment, null, renderList($options.bandRows, (row) => {
227
+ return (openBlock(), createElementBlock("div", {
228
+ key: row.key,
229
+ class: "cn-field-list-widget__band",
230
+ role: "note",
231
+ "data-testid": `cn-field-list-band-${row.key}`
232
+ }, [
233
+ createElementVNode("span", _hoisted_13, toDisplayString(row.detail), 1 /* TEXT */),
234
+ (openBlock(true), createElementBlock(Fragment, null, renderList(row.bandActions, (action) => {
82
235
  return (openBlock(), createBlock(_component_NcButton, {
83
236
  key: action.id,
84
- variant: "tertiary",
237
+ variant: "secondary",
85
238
  disabled: $data.busy !== '',
86
- "data-testid": `cn-field-list-action-${action.id}-${row.key}`,
239
+ "data-testid": `cn-field-list-band-action-${action.id}-${row.key}`,
87
240
  onClick: $event => ($options.runAction(action, row))
88
241
  }, {
89
242
  default: withCtx(() => [
@@ -91,38 +244,98 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
91
244
  ]),
92
245
  _: 2 /* DYNAMIC */
93
246
  }, 1032 /* PROPS, DYNAMIC_SLOTS */, ["disabled", "data-testid", "onClick"]))
94
- }), 128 /* KEYED_FRAGMENT */)),
95
- ($options.canRemove)
96
- ? (openBlock(), createBlock(_component_NcButton, {
97
- key: 3,
247
+ }), 128 /* KEYED_FRAGMENT */))
248
+ ], 8 /* PROPS */, _hoisted_12))
249
+ }), 128 /* KEYED_FRAGMENT */))
250
+ ]))
251
+ : (openBlock(), createElementBlock("ul", _hoisted_14, [
252
+ (openBlock(true), createElementBlock(Fragment, null, renderList($options.rows, (row) => {
253
+ return (openBlock(), createElementBlock("li", {
254
+ key: row.key,
255
+ class: "cn-field-list-widget__row",
256
+ "data-testid": `cn-field-list-row-${row.key}`
257
+ }, [
258
+ createElementVNode("div", _hoisted_16, [
259
+ (row.initials)
260
+ ? (openBlock(), createElementBlock("span", _hoisted_17, toDisplayString(row.initials), 1 /* TEXT */))
261
+ : createCommentVNode("v-if", true),
262
+ createElementVNode("span", _hoisted_18, [
263
+ createElementVNode("span", _hoisted_19, toDisplayString(row.title), 1 /* TEXT */),
264
+ (row.meta)
265
+ ? (openBlock(), createElementBlock("span", _hoisted_20, toDisplayString(row.meta), 1 /* TEXT */))
266
+ : createCommentVNode("v-if", true)
267
+ ]),
268
+ (row.pill)
269
+ ? (openBlock(), createBlock(_component_CnStatusBadge, {
270
+ key: 1,
271
+ label: row.pill.label,
272
+ variant: row.pill.variant,
273
+ size: "small"
274
+ }, null, 8 /* PROPS */, ["label", "variant"]))
275
+ : createCommentVNode("v-if", true),
276
+ (row.aside)
277
+ ? (openBlock(), createElementBlock("span", _hoisted_21, toDisplayString(row.aside), 1 /* TEXT */))
278
+ : createCommentVNode("v-if", true),
279
+ (openBlock(true), createElementBlock(Fragment, null, renderList(row.actions, (action) => {
280
+ return (openBlock(), createBlock(_component_NcButton, {
281
+ key: action.id,
98
282
  variant: "tertiary",
99
283
  disabled: $data.busy !== '',
100
- "aria-label": $options.removeAriaLabel(row),
101
- "data-testid": `cn-field-list-remove-${row.key}`,
102
- onClick: $event => ($options.remove(row))
284
+ "data-testid": `cn-field-list-action-${action.id}-${row.key}`,
285
+ onClick: $event => ($options.runAction(action, row))
103
286
  }, {
104
287
  default: withCtx(() => [
105
- createTextVNode(toDisplayString($options.removeLabel), 1 /* TEXT */)
288
+ createTextVNode(toDisplayString(action.label), 1 /* TEXT */)
106
289
  ]),
107
- _: 1 /* STABLE */
108
- }, 8 /* PROPS */, ["disabled", "aria-label", "data-testid", "onClick"]))
290
+ _: 2 /* DYNAMIC */
291
+ }, 1032 /* PROPS, DYNAMIC_SLOTS */, ["disabled", "data-testid", "onClick"]))
292
+ }), 128 /* KEYED_FRAGMENT */)),
293
+ ($options.canRemove)
294
+ ? (openBlock(), createBlock(_component_NcButton, {
295
+ key: 3,
296
+ variant: "tertiary",
297
+ disabled: $data.busy !== '',
298
+ "aria-label": $options.removeAriaLabel(row),
299
+ "data-testid": `cn-field-list-remove-${row.key}`,
300
+ onClick: $event => ($options.remove(row))
301
+ }, {
302
+ default: withCtx(() => [
303
+ createTextVNode(toDisplayString($options.removeLabel), 1 /* TEXT */)
304
+ ]),
305
+ _: 1 /* STABLE */
306
+ }, 8 /* PROPS */, ["disabled", "aria-label", "data-testid", "onClick"]))
307
+ : createCommentVNode("v-if", true)
308
+ ]),
309
+ (row.detail)
310
+ ? (openBlock(), createElementBlock("p", _hoisted_22, toDisplayString(row.detail), 1 /* TEXT */))
109
311
  : createCommentVNode("v-if", true)
110
- ]),
111
- (row.detail)
112
- ? (openBlock(), createElementBlock("p", _hoisted_11, toDisplayString(row.detail), 1 /* TEXT */))
113
- : createCommentVNode("v-if", true)
114
- ], 8 /* PROPS */, _hoisted_4))
115
- }), 128 /* KEYED_FRAGMENT */))
116
- ])),
312
+ ], 8 /* PROPS */, _hoisted_15))
313
+ }), 128 /* KEYED_FRAGMENT */))
314
+ ])),
117
315
  ($options.hint)
118
- ? (openBlock(), createElementBlock("p", _hoisted_12, toDisplayString($options.hint), 1 /* TEXT */))
316
+ ? (openBlock(), createElementBlock("p", _hoisted_23, toDisplayString($options.hint), 1 /* TEXT */))
317
+ : createCommentVNode("v-if", true),
318
+ ($options.canAdd && $options.addTrigger && $options.addPlacement !== 'header' && !$data.adding)
319
+ ? (openBlock(), createElementBlock("div", _hoisted_24, [
320
+ createVNode(_component_NcButton, {
321
+ variant: "secondary",
322
+ disabled: $data.busy !== '',
323
+ "data-testid": "cn-field-list-open-add",
324
+ onClick: _cache[2] || (_cache[2] = $event => ($data.adding = true))
325
+ }, {
326
+ default: withCtx(() => [
327
+ createTextVNode(" + " + toDisplayString($options.addLabel), 1 /* TEXT */)
328
+ ]),
329
+ _: 1 /* STABLE */
330
+ }, 8 /* PROPS */, ["disabled"])
331
+ ]))
119
332
  : createCommentVNode("v-if", true),
120
- ($options.canAdd)
333
+ ($options.canAdd && !($options.addTrigger && $options.addPlacement === 'header') && (!$options.addTrigger || $data.adding))
121
334
  ? (openBlock(), createElementBlock("form", {
122
- key: 3,
335
+ key: 7,
123
336
  class: "cn-field-list-widget__add",
124
337
  "data-testid": "cn-field-list-add",
125
- onSubmit: _cache[0] || (_cache[0] = withModifiers((...args) => ($options.add && $options.add(...args)), ["prevent"]))
338
+ onSubmit: _cache[3] || (_cache[3] = withModifiers((...args) => ($options.add && $options.add(...args)), ["prevent"]))
126
339
  }, [
127
340
  (openBlock(true), createElementBlock(Fragment, null, renderList($options.addFields, (field) => {
128
341
  return (openBlock(), createElementBlock("div", {
@@ -149,7 +362,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
149
362
  error: Boolean($data.errors[field.key]),
150
363
  helperText: $data.errors[field.key] || ''
151
364
  }, null, 8 /* PROPS */, ["modelValue", "onUpdate:modelValue", "label", "error", "helperText"]))
152
- ], 8 /* PROPS */, _hoisted_13))
365
+ ], 8 /* PROPS */, _hoisted_25))
153
366
  }), 128 /* KEYED_FRAGMENT */)),
154
367
  createVNode(_component_NcButton, {
155
368
  type: "submit",
@@ -166,7 +379,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
166
379
  : createCommentVNode("v-if", true),
167
380
  ($data.notice)
168
381
  ? (openBlock(), createBlock(_component_NcNoteCard, {
169
- key: 4,
382
+ key: 8,
170
383
  type: $data.noticeType,
171
384
  "data-testid": "cn-field-list-notice"
172
385
  }, {
@@ -1 +1 @@
1
- {"version":3,"file":"CnFieldListWidget.vue3.js","sources":["../../../../src/components/CnFieldListWidget/CnFieldListWidget.vue?vue&type=template&id=0f29a833&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div class=\"cn-field-list-widget\" data-testid=\"cn-field-list-widget\">\n\t\t<p v-if=\"rows.length === 0\" class=\"cn-field-list-widget__empty\" data-testid=\"cn-field-list-empty\">\n\t\t\t{{ emptyLabel }}\n\t\t</p>\n\t\t<ul v-else class=\"cn-field-list-widget__rows\">\n\t\t\t<li\n\t\t\t\tv-for=\"row in rows\"\n\t\t\t\t:key=\"row.key\"\n\t\t\t\tclass=\"cn-field-list-widget__row\"\n\t\t\t\t:data-testid=\"`cn-field-list-row-${row.key}`\">\n\t\t\t\t<div class=\"cn-field-list-widget__line\">\n\t\t\t\t\t<span v-if=\"row.initials\" class=\"cn-field-list-widget__avatar\" aria-hidden=\"true\">{{ row.initials }}</span>\n\t\t\t\t\t<span class=\"cn-field-list-widget__main\">\n\t\t\t\t\t\t<span class=\"cn-field-list-widget__title\">{{ row.title }}</span>\n\t\t\t\t\t\t<span v-if=\"row.meta\" class=\"cn-field-list-widget__meta\">{{ row.meta }}</span>\n\t\t\t\t\t</span>\n\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\tv-if=\"row.pill\"\n\t\t\t\t\t\t:label=\"row.pill.label\"\n\t\t\t\t\t\t:variant=\"row.pill.variant\"\n\t\t\t\t\t\tsize=\"small\" />\n\t\t\t\t\t<span v-if=\"row.aside\" class=\"cn-field-list-widget__aside\">{{ row.aside }}</span>\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tv-for=\"action in row.actions\"\n\t\t\t\t\t\t:key=\"action.id\"\n\t\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t\t:disabled=\"busy !== ''\"\n\t\t\t\t\t\t:data-testid=\"`cn-field-list-action-${action.id}-${row.key}`\"\n\t\t\t\t\t\t@click=\"runAction(action, row)\">\n\t\t\t\t\t\t{{ action.label }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tv-if=\"canRemove\"\n\t\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t\t:disabled=\"busy !== ''\"\n\t\t\t\t\t\t:aria-label=\"removeAriaLabel(row)\"\n\t\t\t\t\t\t:data-testid=\"`cn-field-list-remove-${row.key}`\"\n\t\t\t\t\t\t@click=\"remove(row)\">\n\t\t\t\t\t\t{{ removeLabel }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t</div>\n\t\t\t\t<p v-if=\"row.detail\" class=\"cn-field-list-widget__detail\">\n\t\t\t\t\t{{ row.detail }}\n\t\t\t\t</p>\n\t\t\t</li>\n\t\t</ul>\n\n\t\t<p v-if=\"hint\" class=\"cn-field-list-widget__hint\">\n\t\t\t{{ hint }}\n\t\t</p>\n\n\t\t<form\n\t\t\tv-if=\"canAdd\"\n\t\t\tclass=\"cn-field-list-widget__add\"\n\t\t\tdata-testid=\"cn-field-list-add\"\n\t\t\t@submit.prevent=\"add\">\n\t\t\t<div\n\t\t\t\tv-for=\"field in addFields\"\n\t\t\t\t:key=\"field.key\"\n\t\t\t\tclass=\"cn-field-list-widget__field\"\n\t\t\t\t:data-testid=\"`cn-field-list-input-${field.key}`\">\n\t\t\t\t<NcSelect\n\t\t\t\t\tv-if=\"field.type === 'select'\"\n\t\t\t\t\tv-model=\"draft[field.key]\"\n\t\t\t\t\t:inputLabel=\"field.label\"\n\t\t\t\t\t:options=\"field.options\"\n\t\t\t\t\t:reduce=\"(option) => option.value\"\n\t\t\t\t\t:clearable=\"false\"\n\t\t\t\t\tlabel=\"label\" />\n\t\t\t\t<NcTextField\n\t\t\t\t\tv-else\n\t\t\t\t\tv-model=\"draft[field.key]\"\n\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t:error=\"Boolean(errors[field.key])\"\n\t\t\t\t\t:helperText=\"errors[field.key] || ''\" />\n\t\t\t</div>\n\t\t\t<NcButton type=\"submit\"\n\t\t\t\tvariant=\"secondary\"\n\t\t\t\t:disabled=\"busy !== ''\"\n\t\t\t\tdata-testid=\"cn-field-list-submit\">\n\t\t\t\t{{ addLabel }}\n\t\t\t</NcButton>\n\t\t</form>\n\n\t\t<NcNoteCard v-if=\"notice\" :type=\"noticeType\" data-testid=\"cn-field-list-notice\">\n\t\t\t{{ notice }}\n\t\t</NcNoteCard>\n\t</div>\n</template>\n\n<script>\nimport { NcButton, NcNoteCard, NcSelect, NcTextField } from '@nextcloud/vue'\nimport CnStatusBadge from '../CnStatusBadge/CnStatusBadge.vue'\nimport { isBadgeVariant } from '../../utils/badgeVariants.js'\nimport { formatBoardDate } from '../../utils/boardDate.js'\nimport { parseDateValue } from '../../utils/dateVariant.js'\nimport { prefixUrl } from '../../utils/headers.js'\nimport { readPath } from '../../utils/readPath.js'\nimport { serverErrorMessage } from '../../utils/serverErrorMessage.js'\n\n/**\n * CnFieldListWidget — the `field-list` widget: a list stored INSIDE one field\n * of the current object (a portal's `domains[]`, its `editors[]`), shown as\n * rows and edited in place.\n *\n * Rows are drawn from configuration (title, meta line, pill, aside, a detail\n * line); a small form adds an entry, a button removes one, and each change is\n * saved as ONE PATCH of that field on the object, after re-reading the stored\n * value so a concurrent change to the same list is not overwritten with a\n * stale copy. Optional row actions call a configured endpoint (a \"check\n * again\"), and an optional `rowSource` merges live per-row state read from an\n * endpoint by key without writing it back.\n *\n * The server decides who may write: a refused PATCH shows the server's words\n * and leaves the list as stored.\n *\n * @spec openspec/changes/abstract-field-list/specs/field-list/spec.md\n */\nexport default {\n\tname: 'CnFieldListWidget',\n\n\tcomponents: { CnStatusBadge, NcButton, NcNoteCard, NcSelect, NcTextField },\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 * - `field` (required) — the object field holding the array.\n\t\t * - `key` — the item field that identifies an entry (default: the item itself for strings).\n\t\t * - `row` — `{ title, titleFallback?, avatar?, meta?: [paths|{ path, map?, format? }], pill?: { path, map?, variantMap? }, aside?: { path, format?, prefix? }, detail?: { template, when? } }`.\n\t\t * - `add` — `{ label?, fields: [{ key, label, type?: 'text'|'select', options?, required?, pattern?, patternMessage? }], unique?, defaults? }`.\n\t\t * - `remove` — `{ label? }`; omitted = no remove.\n\t\t * - `rowActions` — `[{ id, label, when?, request: { method?, url, payload? } }]`; tokens `@object.<field>`, `@objectId`, `@row.<field>`.\n\t\t * - `rowSource` — `{ url, path?, key }`: live per-row state merged by key.\n\t\t * - `readOnly`, `hint`, `emptyText`.\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/** The list as last saved by this widget; null = read it off objectData. */\n\t\t\tsaved: null,\n\t\t\t/** Live per-row state from `rowSource`, by key. */\n\t\t\tlive: {},\n\t\t\tdraft: {},\n\t\t\terrors: {},\n\t\t\tbusy: '',\n\t\t\tnotice: '',\n\t\t\tnoticeType: 'error',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The stored list.\n\t\t *\n\t\t * @return {Array}\n\t\t */\n\t\titems() {\n\t\t\tconst list = this.saved !== null ? this.saved : readPath(this.objectData || {}, this.content.field)\n\t\t\treturn Array.isArray(list) ? list : []\n\t\t},\n\n\t\t/**\n\t\t * The rows to draw.\n\t\t *\n\t\t * @return {Array<object>}\n\t\t * @spec openspec/changes/abstract-field-list/specs/field-list/spec.md#requirement-a-field-list-draws-the-entries-of-one-object-field\n\t\t */\n\t\trows() {\n\t\t\tconst spec = this.content.row || {}\n\t\t\treturn this.items.map((item, index) => {\n\t\t\t\tconst key = this.keyOf(item, index)\n\t\t\t\tconst entry = item && typeof item === 'object'\n\t\t\t\t\t? { ...item, ...(this.live[key] || {}) }\n\t\t\t\t\t: { value: item, ...(this.live[key] || {}) }\n\t\t\t\tconst title = this.text(entry, spec.title) || this.text(entry, spec.titleFallback) || (typeof item === 'string' ? item : key)\n\t\t\t\tconst meta = (Array.isArray(spec.meta) ? spec.meta : [])\n\t\t\t\t\t.map((part) => this.text(entry, part))\n\t\t\t\t\t.filter((part) => part !== '')\n\t\t\t\t\t.join(' · ')\n\t\t\t\treturn {\n\t\t\t\t\tkey,\n\t\t\t\t\titem,\n\t\t\t\t\tentry,\n\t\t\t\t\ttitle,\n\t\t\t\t\tinitials: spec.avatar ? this.initials(title) : '',\n\t\t\t\t\tmeta,\n\t\t\t\t\tpill: this.pillOf(entry, spec.pill),\n\t\t\t\t\taside: spec.aside ? this.asideOf(entry, spec.aside) : '',\n\t\t\t\t\tdetail: spec.detail && this.matches(entry, spec.detail.when) ? this.fill(spec.detail.template, entry) : '',\n\t\t\t\t\tactions: this.actionsFor(entry),\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/** Whether the list is read-only. */\n\t\treadOnly() {\n\t\t\treturn this.content.readOnly === true\n\t\t},\n\n\t\t/** Whether entries can be added. */\n\t\tcanAdd() {\n\t\t\treturn !this.readOnly && this.addFields.length > 0 && Boolean(this.content.field)\n\t\t},\n\n\t\t/** Whether entries can be removed. */\n\t\tcanRemove() {\n\t\t\treturn !this.readOnly && Boolean(this.content.remove) && Boolean(this.content.field)\n\t\t},\n\n\t\t/**\n\t\t * The add form's fields, labels translated, select options translated.\n\t\t *\n\t\t * @return {Array<object>}\n\t\t */\n\t\taddFields() {\n\t\t\tconst fields = this.content.add && Array.isArray(this.content.add.fields) ? this.content.add.fields : []\n\t\t\treturn fields\n\t\t\t\t.filter((field) => field && field.key)\n\t\t\t\t.map((field) => ({\n\t\t\t\t\t...field,\n\t\t\t\t\tlabel: this.tr(field.label || field.key),\n\t\t\t\t\toptions: (Array.isArray(field.options) ? field.options : []).map((option) => (\n\t\t\t\t\t\ttypeof option === 'object' ? { value: option.value, label: this.tr(option.label || String(option.value)) } : { value: option, label: this.tr(String(option)) }\n\t\t\t\t\t)),\n\t\t\t\t}))\n\t\t},\n\n\t\taddLabel() {\n\t\t\treturn this.tr((this.content.add && this.content.add.label) || 'Add')\n\t\t},\n\n\t\tremoveLabel() {\n\t\t\treturn this.tr((this.content.remove && this.content.remove.label) || 'Remove')\n\t\t},\n\n\t\thint() {\n\t\t\treturn this.tr(this.content.hint || '')\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\t/** The object URL the list is saved to, or '' when it is not known. */\n\t\tobjectUrl() {\n\t\t\tconst id = this.objectId || (this.objectData && this.objectData.id)\n\t\t\tif (!this.register || !this.schema || !id || typeof this.register !== 'string' || typeof this.schema !== 'string') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn `/apps/openregister/api/objects/${encodeURIComponent(this.register)}/${encodeURIComponent(this.schema)}/${encodeURIComponent(String(id))}`\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectData() {\n\t\t\tthis.saved = null\n\t\t\tthis.loadLive()\n\t\t},\n\t},\n\n\tcreated() {\n\t\tthis.resetDraft()\n\t\tthis.loadLive()\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 identity of an entry.\n\t\t *\n\t\t * @param {unknown} item The entry.\n\t\t * @param {number} index Its position.\n\t\t * @return {string}\n\t\t */\n\t\tkeyOf(item, index) {\n\t\t\tif (item === null || typeof item !== 'object') {\n\t\t\t\treturn String(item)\n\t\t\t}\n\t\t\tconst key = this.content.key ? readPath(item, this.content.key) : undefined\n\t\t\treturn key === undefined || key === null || key === '' ? `item-${index}` : String(key)\n\t\t},\n\n\t\t/**\n\t\t * A display text off an entry: a path, or `{ path, map?, format? }`.\n\t\t *\n\t\t * @param {object} entry The entry.\n\t\t * @param {string|object} spec The spec.\n\t\t * @return {string}\n\t\t */\n\t\ttext(entry, spec) {\n\t\t\tif (!spec) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst def = typeof spec === 'string' ? { path: spec } : spec\n\t\t\tconst raw = readPath(entry, def.path)\n\t\t\tif (raw === null || raw === undefined || raw === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (def.format === 'date' || def.format === 'date-long') {\n\t\t\t\tconst date = parseDateValue(raw)\n\t\t\t\tif (date === null) {\n\t\t\t\t\treturn ''\n\t\t\t\t}\n\t\t\t\treturn def.format === 'date-long' ? formatBoardDate(date, new Date(date.getFullYear() + 1, 0, 1)) : formatBoardDate(date)\n\t\t\t}\n\t\t\tif (def.map && Object.hasOwn(def.map, String(raw))) {\n\t\t\t\treturn this.tr(def.map[String(raw)])\n\t\t\t}\n\t\t\treturn typeof raw === 'object' ? '' : String(raw)\n\t\t},\n\n\t\tpillOf(entry, spec) {\n\t\t\tif (!spec || !spec.path) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst raw = readPath(entry, spec.path)\n\t\t\tif (raw === null || raw === undefined || raw === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst label = this.text(entry, spec)\n\t\t\tconst variant = spec.variantMap && spec.variantMap[String(raw)]\n\t\t\treturn { label, variant: isBadgeVariant(variant) ? variant : 'default' }\n\t\t},\n\n\t\tasideOf(entry, spec) {\n\t\t\tconst value = this.text(entry, spec)\n\t\t\tif (value === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn spec.prefix ? `${this.tr(spec.prefix)} ${value}` : value\n\t\t},\n\n\t\t/**\n\t\t * Whether an entry matches a `{ field: value }` condition (every field equal).\n\t\t *\n\t\t * @param {object} entry The entry.\n\t\t * @param {object|undefined} when The condition.\n\t\t * @return {boolean}\n\t\t */\n\t\tmatches(entry, when) {\n\t\t\tif (!when || typeof when !== 'object') {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn Object.entries(when).every(([field, expected]) => {\n\t\t\t\tconst actual = readPath(entry, field)\n\t\t\t\tif (expected === false) {\n\t\t\t\t\treturn actual === false || actual === undefined || actual === null || actual === ''\n\t\t\t\t}\n\t\t\t\treturn String(actual) === String(expected)\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Fill `{path}` placeholders from an entry; '' when one stays empty.\n\t\t *\n\t\t * @param {string} template The template, translated first.\n\t\t * @param {object} entry The entry.\n\t\t * @return {string}\n\t\t */\n\t\tfill(template, entry) {\n\t\t\tif (typeof template !== 'string') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tlet missing = false\n\t\t\tconst out = this.tr(template).replace(/\\{([\\w.@]+)\\}/g, (match, path) => {\n\t\t\t\tconst value = readPath(entry, path)\n\t\t\t\tif (value === undefined || value === null || value === '') {\n\t\t\t\t\tmissing = true\n\t\t\t\t\treturn match\n\t\t\t\t}\n\t\t\t\treturn String(value)\n\t\t\t})\n\t\t\treturn missing ? '' : out\n\t\t},\n\n\t\tactionsFor(entry) {\n\t\t\tif (this.readOnly) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst actions = Array.isArray(this.content.rowActions) ? this.content.rowActions : []\n\t\t\treturn actions\n\t\t\t\t.filter((action) => action && action.id && action.request && action.request.url && this.matches(entry, action.when))\n\t\t\t\t.map((action) => ({ ...action, label: this.tr(action.label || action.id) }))\n\t\t},\n\n\t\tinitials(name) {\n\t\t\treturn String(name || '').split(/[\\s@._-]+/).filter(Boolean).slice(0, 2).map((part) => part.charAt(0).toUpperCase()).join('')\n\t\t},\n\n\t\tremoveAriaLabel(row) {\n\t\t\treturn `${this.removeLabel}: ${row.title}`\n\t\t},\n\n\t\tresetDraft() {\n\t\t\tconst draft = {}\n\t\t\tfor (const field of this.addFields) {\n\t\t\t\tconst first = field.type === 'select' && field.options.length > 0 ? field.options[0].value : ''\n\t\t\t\tdraft[field.key] = field.default !== undefined ? field.default : first\n\t\t\t}\n\t\t\tthis.draft = draft\n\t\t\tthis.errors = {}\n\t\t},\n\n\t\t/**\n\t\t * Resolve `@objectId`, `@object.<field>` and `@row.<field>` inside a string or a payload.\n\t\t *\n\t\t * @param {unknown} value The value.\n\t\t * @param {object} entry The row entry.\n\t\t * @return {unknown}\n\t\t */\n\t\tresolve(value, entry) {\n\t\t\tif (Array.isArray(value)) {\n\t\t\t\treturn value.map((v) => this.resolve(v, entry))\n\t\t\t}\n\t\t\tif (value && typeof value === 'object') {\n\t\t\t\treturn Object.fromEntries(Object.entries(value).map(([k, v]) => [k, this.resolve(v, entry)]))\n\t\t\t}\n\t\t\tif (typeof value !== 'string') {\n\t\t\t\treturn value\n\t\t\t}\n\t\t\tconst object = this.objectData || {}\n\t\t\tconst read = (source, path) => {\n\t\t\t\tconst v = readPath(source || {}, path)\n\t\t\t\treturn v === undefined || v === null ? '' : v\n\t\t\t}\n\t\t\tconst whole = value.match(/^@(row|object)\\.([\\w.@]+)$/)\n\t\t\tif (whole) {\n\t\t\t\treturn read(whole[1] === 'row' ? entry : object, whole[2])\n\t\t\t}\n\t\t\treturn value\n\t\t\t\t.replace(/@objectId/g, () => encodeURIComponent(String(this.objectId || object.id || '')))\n\t\t\t\t.replace(/@(row|object)\\.([\\w.@]+)/g, (m, scope, path) => encodeURIComponent(String(read(scope === 'row' ? entry : object, path))))\n\t\t},\n\n\t\tasync http() {\n\t\t\tconst { default: axios } = await import('@nextcloud/axios')\n\t\t\treturn { axios }\n\t\t},\n\n\t\t/**\n\t\t * Read live per-row state from `rowSource` and keep it by key.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync loadLive() {\n\t\t\tconst source = this.content.rowSource\n\t\t\tif (!source || !source.url || !source.key || !this.objectData) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst { axios } = await this.http()\n\t\t\t\tconst response = await axios.get(prefixUrl(this.resolve(source.url, {})))\n\t\t\t\tconst data = response && response.data\n\t\t\t\tconst list = source.path ? readPath(data, source.path) : data\n\t\t\t\tconst live = {}\n\t\t\t\tfor (const item of Array.isArray(list) ? list : []) {\n\t\t\t\t\tconst key = item && readPath(item, source.key)\n\t\t\t\t\tif (key !== undefined && key !== null) {\n\t\t\t\t\t\tlive[String(key)] = item\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthis.live = live\n\t\t\t} catch (e) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[CnFieldListWidget] rowSource could not be read:', e?.message || e)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Save a new list: re-read the stored value, apply the change to it, and\n\t\t * PATCH that one field.\n\t\t *\n\t\t * @param {(stored: Array) => Array} change Builds the next list from the stored one, or throws an Error with a `userMessage`.\n\t\t * @param {string} busyKey What is busy.\n\t\t * @return {Promise<boolean>} Whether it saved.\n\t\t * @spec openspec/changes/abstract-field-list/specs/field-list/spec.md#requirement-a-field-list-saves-one-field-with-one-patch\n\t\t */\n\t\tasync save(change, busyKey) {\n\t\t\tif (!this.objectUrl) {\n\t\t\t\tthis.notify(this.tr('This list cannot be saved here.'))\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tthis.busy = busyKey\n\t\t\tthis.notice = ''\n\t\t\ttry {\n\t\t\t\tconst { axios } = await this.http()\n\t\t\t\tconst { data: stored } = await axios.get(prefixUrl(this.objectUrl))\n\t\t\t\tconst current = readPath(stored || {}, this.content.field)\n\t\t\t\tconst next = change(Array.isArray(current) ? current : [])\n\t\t\t\tconst { data } = await axios.patch(prefixUrl(this.objectUrl), { [this.content.field]: next })\n\t\t\t\tconst written = readPath(data || {}, this.content.field)\n\t\t\t\tthis.saved = Array.isArray(written) ? written : next\n\t\t\t\t/**\n\t\t\t\t * @event saved The list was saved: one PATCH of the field succeeded.\n\t\t\t\t * @type {{field: string, value: Array}}\n\t\t\t\t */\n\t\t\t\tthis.$emit('saved', { field: this.content.field, value: this.saved })\n\t\t\t\treturn true\n\t\t\t} catch (e) {\n\t\t\t\tthis.notify(e && e.userMessage ? e.userMessage : serverErrorMessage(e))\n\t\t\t\treturn false\n\t\t\t} finally {\n\t\t\t\tthis.busy = ''\n\t\t\t}\n\t\t},\n\n\t\tnotify(message, type = 'error') {\n\t\t\tthis.notice = message\n\t\t\tthis.noticeType = type\n\t\t},\n\n\t\t/**\n\t\t * Validate the draft and add it.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t * @spec openspec/changes/abstract-field-list/specs/field-list/spec.md#requirement-a-field-list-adds-and-removes-entries\n\t\t */\n\t\tasync add() {\n\t\t\tconst errors = {}\n\t\t\tfor (const field of this.addFields) {\n\t\t\t\tconst value = this.draft[field.key]\n\t\t\t\tconst empty = value === undefined || value === null || String(value).trim() === ''\n\t\t\t\tif (field.required && empty) {\n\t\t\t\t\terrors[field.key] = this.tr('Fill this in.')\n\t\t\t\t} else if (!empty && field.pattern && !new RegExp(field.pattern).test(String(value).trim())) {\n\t\t\t\t\terrors[field.key] = this.tr(field.patternMessage || 'This value is not valid.')\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.errors = errors\n\t\t\tif (Object.keys(errors).length > 0) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst entry = { ...((this.content.add && this.content.add.defaults) || {}) }\n\t\t\tfor (const field of this.addFields) {\n\t\t\t\tconst value = this.draft[field.key]\n\t\t\t\tif (value !== undefined && value !== null && String(value).trim() !== '') {\n\t\t\t\t\tentry[field.key] = typeof value === 'string' ? value.trim() : value\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst unique = this.content.add && this.content.add.unique\n\t\t\tconst saved = await this.save((stored) => {\n\t\t\t\tif (unique && stored.some((item) => item && String(readPath(item, unique)) === String(entry[unique]))) {\n\t\t\t\t\tconst error = new Error('duplicate')\n\t\t\t\t\terror.userMessage = this.tr('This is already in the list.')\n\t\t\t\t\tthrow error\n\t\t\t\t}\n\t\t\t\treturn [...stored, entry]\n\t\t\t}, '*')\n\t\t\tif (saved) {\n\t\t\t\tthis.resetDraft()\n\t\t\t\tthis.loadLive()\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Remove an entry by its key.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync remove(row) {\n\t\t\tawait this.save((stored) => stored.filter((item, index) => this.keyOf(item, index) !== row.key), row.key)\n\t\t},\n\n\t\t/**\n\t\t * Run a row action's request, then re-read the live state.\n\t\t *\n\t\t * @param {object} action The action.\n\t\t * @param {object} row The row.\n\t\t * @return {Promise<void>}\n\t\t * @spec openspec/changes/abstract-field-list/specs/field-list/spec.md#requirement-a-field-list-row-can-run-a-configured-request\n\t\t */\n\t\tasync runAction(action, row) {\n\t\t\tthis.busy = row.key\n\t\t\tthis.notice = ''\n\t\t\ttry {\n\t\t\t\tconst { axios } = await this.http()\n\t\t\t\tconst method = String(action.request.method || 'POST').toLowerCase()\n\t\t\t\tconst payload = this.resolve(action.request.payload || {}, row.entry)\n\t\t\t\tconst response = method === 'get'\n\t\t\t\t\t? await axios.get(prefixUrl(this.resolve(action.request.url, row.entry)))\n\t\t\t\t\t: await axios[method](prefixUrl(this.resolve(action.request.url, row.entry)), payload)\n\t\t\t\tconst message = response && response.data && typeof response.data.message === 'string' ? response.data.message : ''\n\t\t\t\tif (message) {\n\t\t\t\t\tthis.notify(message, 'info')\n\t\t\t\t}\n\t\t\t\tawait this.loadLive()\n\t\t\t} catch (e) {\n\t\t\t\tthis.notify(serverErrorMessage(e))\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-field-list-widget {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n\tmin-width: 0;\n}\n\n.cn-field-list-widget__rows {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n}\n\n.cn-field-list-widget__row {\n\tpadding: 10px 0;\n\tborder-top: 1px solid var(--color-border);\n}\n\n.cn-field-list-widget__row:first-child {\n\tborder-top: none;\n\tpadding-top: 0;\n}\n\n.cn-field-list-widget__line {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 12px;\n\tmin-width: 0;\n}\n\n.cn-field-list-widget__avatar {\n\tflex: 0 0 auto;\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 32px;\n\theight: 32px;\n\tborder-radius: 50%;\n\tbackground: var(--color-primary-element-light);\n\tcolor: var(--color-primary-element-light-text, var(--color-main-text));\n\tfont-size: 12px;\n\tfont-weight: 700;\n}\n\n.cn-field-list-widget__main {\n\tdisplay: flex;\n\tflex-direction: column;\n\tflex: 1 1 auto;\n\tmin-width: 0;\n}\n\n.cn-field-list-widget__title {\n\tfont-weight: 600;\n\toverflow-wrap: anywhere;\n}\n\n.cn-field-list-widget__meta,\n.cn-field-list-widget__aside,\n.cn-field-list-widget__detail,\n.cn-field-list-widget__hint,\n.cn-field-list-widget__empty {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-field-list-widget__meta,\n.cn-field-list-widget__aside,\n.cn-field-list-widget__detail {\n\tfont-size: 13px;\n}\n\n.cn-field-list-widget__aside {\n\twhite-space: nowrap;\n}\n\n.cn-field-list-widget__detail,\n.cn-field-list-widget__hint,\n.cn-field-list-widget__empty {\n\tmargin: 4px 0 0;\n\toverflow-wrap: anywhere;\n}\n\n.cn-field-list-widget__add {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: flex-end;\n\tgap: 8px 12px;\n}\n\n.cn-field-list-widget__add > * {\n\tflex: 1 1 180px;\n}\n\n.cn-field-list-widget__add > button {\n\tflex: 0 0 auto;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_toDisplayString","_Fragment","_renderList","_createElementVNode","_createBlock","_createTextVNode","_createVNode"],"mappings":"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"}
1
+ {"version":3,"file":"CnFieldListWidget.vue3.js","sources":["../../../../src/components/CnFieldListWidget/CnFieldListWidget.vue?vue&type=template&id=0f29a833&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div class=\"cn-field-list-widget\" data-testid=\"cn-field-list-widget\">\n\t\t<div v-if=\"canAdd && addTrigger && addPlacement === 'header'\" class=\"cn-field-list-widget__toolbar\">\n\t\t\t<NcButton\n\t\t\t\tvariant=\"secondary\"\n\t\t\t\t:disabled=\"busy !== ''\"\n\t\t\t\tdata-testid=\"cn-field-list-open-add\"\n\t\t\t\t@click=\"adding = !adding\">\n\t\t\t\t+ {{ addLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<form\n\t\t\tv-if=\"canAdd && addTrigger && addPlacement === 'header' && adding\"\n\t\t\tclass=\"cn-field-list-widget__add\"\n\t\t\tdata-testid=\"cn-field-list-add\"\n\t\t\t@submit.prevent=\"add\">\n\t\t\t<div\n\t\t\t\tv-for=\"field in addFields\"\n\t\t\t\t:key=\"field.key\"\n\t\t\t\tclass=\"cn-field-list-widget__field\"\n\t\t\t\t:data-testid=\"`cn-field-list-input-${field.key}`\">\n\t\t\t\t<NcSelect\n\t\t\t\t\tv-if=\"field.type === 'select'\"\n\t\t\t\t\tv-model=\"draft[field.key]\"\n\t\t\t\t\t:inputLabel=\"field.label\"\n\t\t\t\t\t:options=\"field.options\"\n\t\t\t\t\t:reduce=\"(option) => option.value\"\n\t\t\t\t\t:clearable=\"false\"\n\t\t\t\t\tlabel=\"label\" />\n\t\t\t\t<NcTextField\n\t\t\t\t\tv-else\n\t\t\t\t\tv-model=\"draft[field.key]\"\n\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t:error=\"Boolean(errors[field.key])\"\n\t\t\t\t\t:helperText=\"errors[field.key] || ''\" />\n\t\t\t</div>\n\t\t\t<NcButton type=\"submit\"\n\t\t\t\tvariant=\"primary\"\n\t\t\t\t:disabled=\"busy !== ''\"\n\t\t\t\tdata-testid=\"cn-field-list-submit\">\n\t\t\t\t{{ addLabel }}\n\t\t\t</NcButton>\n\t\t</form>\n\t\t<p v-if=\"rows.length === 0\" class=\"cn-field-list-widget__empty\" data-testid=\"cn-field-list-empty\">\n\t\t\t{{ emptyLabel }}\n\t\t</p>\n\t\t<div v-else-if=\"isTable\" class=\"cn-field-list-widget__table-wrap\">\n\t\t\t<table class=\"cn-field-list-widget__table\" data-testid=\"cn-field-list-table\">\n\t\t\t\t<thead>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t<th\n\t\t\t\t\t\t\tv-for=\"column in tableColumns\"\n\t\t\t\t\t\t\t:key=\"column.key\"\n\t\t\t\t\t\t\tscope=\"col\"\n\t\t\t\t\t\t\tclass=\"cn-field-list-widget__th\">\n\t\t\t\t\t\t\t{{ column.header }}\n\t\t\t\t\t\t</th>\n\t\t\t\t\t\t<th v-if=\"hasMenu\" scope=\"col\" class=\"cn-field-list-widget__th cn-field-list-widget__th--menu\">\n\t\t\t\t\t\t\t<span class=\"cn-field-list-widget__sr\">{{ tr('Actions') }}</span>\n\t\t\t\t\t\t</th>\n\t\t\t\t\t</tr>\n\t\t\t\t</thead>\n\t\t\t\t<tbody>\n\t\t\t\t\t<tr\n\t\t\t\t\t\tv-for=\"row in rows\"\n\t\t\t\t\t\t:key=\"row.key\"\n\t\t\t\t\t\tclass=\"cn-field-list-widget__tr\"\n\t\t\t\t\t\t:data-testid=\"`cn-field-list-row-${row.key}`\">\n\t\t\t\t\t\t<td\n\t\t\t\t\t\t\tv-for=\"(column, index) in tableColumns\"\n\t\t\t\t\t\t\t:key=\"column.key\"\n\t\t\t\t\t\t\tclass=\"cn-field-list-widget__td\">\n\t\t\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\t\t\tv-if=\"column.pill && row.cells[index].pill\"\n\t\t\t\t\t\t\t\t:label=\"row.cells[index].pill.label\"\n\t\t\t\t\t\t\t\t:variant=\"row.cells[index].pill.variant\"\n\t\t\t\t\t\t\t\tsize=\"small\" />\n\t\t\t\t\t\t\t<strong v-else-if=\"column.strong\">{{ row.cells[index].text }}</strong>\n\t\t\t\t\t\t\t<template v-else>\n\t\t\t\t\t\t\t\t{{ row.cells[index].text }}\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t</td>\n\t\t\t\t\t\t<td v-if=\"hasMenu\" class=\"cn-field-list-widget__td cn-field-list-widget__td--menu\">\n\t\t\t\t\t\t\t<NcActions\n\t\t\t\t\t\t\t\tv-if=\"menuItems(row).length > 0\"\n\t\t\t\t\t\t\t\t:forceMenu=\"true\"\n\t\t\t\t\t\t\t\t:disabled=\"busy !== ''\"\n\t\t\t\t\t\t\t\t:ariaLabel=\"`${tr('More actions')}: ${row.title}`\"\n\t\t\t\t\t\t\t\t:data-testid=\"`cn-field-list-menu-${row.key}`\">\n\t\t\t\t\t\t\t\t<NcActionButton\n\t\t\t\t\t\t\t\t\tv-for=\"item in menuItems(row)\"\n\t\t\t\t\t\t\t\t\t:key=\"item.id\"\n\t\t\t\t\t\t\t\t\t:data-testid=\"`cn-field-list-menu-${item.id}-${row.key}`\"\n\t\t\t\t\t\t\t\t\t@click=\"item.run\">\n\t\t\t\t\t\t\t\t\t{{ item.label }}\n\t\t\t\t\t\t\t\t</NcActionButton>\n\t\t\t\t\t\t\t</NcActions>\n\t\t\t\t\t\t</td>\n\t\t\t\t\t</tr>\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t\t<div\n\t\t\t\tv-for=\"row in bandRows\"\n\t\t\t\t:key=\"row.key\"\n\t\t\t\tclass=\"cn-field-list-widget__band\"\n\t\t\t\trole=\"note\"\n\t\t\t\t:data-testid=\"`cn-field-list-band-${row.key}`\">\n\t\t\t\t<span class=\"cn-field-list-widget__band-text\">{{ row.detail }}</span>\n\t\t\t\t<NcButton\n\t\t\t\t\tv-for=\"action in row.bandActions\"\n\t\t\t\t\t:key=\"action.id\"\n\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t:disabled=\"busy !== ''\"\n\t\t\t\t\t:data-testid=\"`cn-field-list-band-action-${action.id}-${row.key}`\"\n\t\t\t\t\t@click=\"runAction(action, row)\">\n\t\t\t\t\t{{ action.label }}\n\t\t\t\t</NcButton>\n\t\t\t</div>\n\t\t</div>\n\t\t<ul v-else class=\"cn-field-list-widget__rows\">\n\t\t\t<li\n\t\t\t\tv-for=\"row in rows\"\n\t\t\t\t:key=\"row.key\"\n\t\t\t\tclass=\"cn-field-list-widget__row\"\n\t\t\t\t:data-testid=\"`cn-field-list-row-${row.key}`\">\n\t\t\t\t<div class=\"cn-field-list-widget__line\">\n\t\t\t\t\t<span v-if=\"row.initials\" class=\"cn-field-list-widget__avatar\" aria-hidden=\"true\">{{ row.initials }}</span>\n\t\t\t\t\t<span class=\"cn-field-list-widget__main\">\n\t\t\t\t\t\t<span class=\"cn-field-list-widget__title\">{{ row.title }}</span>\n\t\t\t\t\t\t<span v-if=\"row.meta\" class=\"cn-field-list-widget__meta\">{{ row.meta }}</span>\n\t\t\t\t\t</span>\n\t\t\t\t\t<CnStatusBadge\n\t\t\t\t\t\tv-if=\"row.pill\"\n\t\t\t\t\t\t:label=\"row.pill.label\"\n\t\t\t\t\t\t:variant=\"row.pill.variant\"\n\t\t\t\t\t\tsize=\"small\" />\n\t\t\t\t\t<span v-if=\"row.aside\" class=\"cn-field-list-widget__aside\">{{ row.aside }}</span>\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tv-for=\"action in row.actions\"\n\t\t\t\t\t\t:key=\"action.id\"\n\t\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t\t:disabled=\"busy !== ''\"\n\t\t\t\t\t\t:data-testid=\"`cn-field-list-action-${action.id}-${row.key}`\"\n\t\t\t\t\t\t@click=\"runAction(action, row)\">\n\t\t\t\t\t\t{{ action.label }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<NcButton\n\t\t\t\t\t\tv-if=\"canRemove\"\n\t\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t\t:disabled=\"busy !== ''\"\n\t\t\t\t\t\t:aria-label=\"removeAriaLabel(row)\"\n\t\t\t\t\t\t:data-testid=\"`cn-field-list-remove-${row.key}`\"\n\t\t\t\t\t\t@click=\"remove(row)\">\n\t\t\t\t\t\t{{ removeLabel }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t</div>\n\t\t\t\t<p v-if=\"row.detail\" class=\"cn-field-list-widget__detail\">\n\t\t\t\t\t{{ row.detail }}\n\t\t\t\t</p>\n\t\t\t</li>\n\t\t</ul>\n\n\t\t<p v-if=\"hint\" class=\"cn-field-list-widget__hint\">\n\t\t\t{{ hint }}\n\t\t</p>\n\n\t\t<div v-if=\"canAdd && addTrigger && addPlacement !== 'header' && !adding\" class=\"cn-field-list-widget__toolbar\">\n\t\t\t<NcButton\n\t\t\t\tvariant=\"secondary\"\n\t\t\t\t:disabled=\"busy !== ''\"\n\t\t\t\tdata-testid=\"cn-field-list-open-add\"\n\t\t\t\t@click=\"adding = true\">\n\t\t\t\t+ {{ addLabel }}\n\t\t\t</NcButton>\n\t\t</div>\n\t\t<form\n\t\t\tv-if=\"canAdd && !(addTrigger && addPlacement === 'header') && (!addTrigger || adding)\"\n\t\t\tclass=\"cn-field-list-widget__add\"\n\t\t\tdata-testid=\"cn-field-list-add\"\n\t\t\t@submit.prevent=\"add\">\n\t\t\t<div\n\t\t\t\tv-for=\"field in addFields\"\n\t\t\t\t:key=\"field.key\"\n\t\t\t\tclass=\"cn-field-list-widget__field\"\n\t\t\t\t:data-testid=\"`cn-field-list-input-${field.key}`\">\n\t\t\t\t<NcSelect\n\t\t\t\t\tv-if=\"field.type === 'select'\"\n\t\t\t\t\tv-model=\"draft[field.key]\"\n\t\t\t\t\t:inputLabel=\"field.label\"\n\t\t\t\t\t:options=\"field.options\"\n\t\t\t\t\t:reduce=\"(option) => option.value\"\n\t\t\t\t\t:clearable=\"false\"\n\t\t\t\t\tlabel=\"label\" />\n\t\t\t\t<NcTextField\n\t\t\t\t\tv-else\n\t\t\t\t\tv-model=\"draft[field.key]\"\n\t\t\t\t\t:label=\"field.label\"\n\t\t\t\t\t:error=\"Boolean(errors[field.key])\"\n\t\t\t\t\t:helperText=\"errors[field.key] || ''\" />\n\t\t\t</div>\n\t\t\t<NcButton type=\"submit\"\n\t\t\t\tvariant=\"secondary\"\n\t\t\t\t:disabled=\"busy !== ''\"\n\t\t\t\tdata-testid=\"cn-field-list-submit\">\n\t\t\t\t{{ addLabel }}\n\t\t\t</NcButton>\n\t\t</form>\n\n\t\t<NcNoteCard v-if=\"notice\" :type=\"noticeType\" data-testid=\"cn-field-list-notice\">\n\t\t\t{{ notice }}\n\t\t</NcNoteCard>\n\t</div>\n</template>\n\n<script>\nimport { NcActionButton, NcActions, NcButton, NcNoteCard, NcSelect, NcTextField } from '@nextcloud/vue'\nimport CnStatusBadge from '../CnStatusBadge/CnStatusBadge.vue'\nimport { isBadgeVariant } from '../../utils/badgeVariants.js'\nimport { formatBoardDate } from '../../utils/boardDate.js'\nimport { parseDateValue } from '../../utils/dateVariant.js'\nimport { prefixUrl } from '../../utils/headers.js'\nimport { readPath } from '../../utils/readPath.js'\nimport { serverErrorMessage } from '../../utils/serverErrorMessage.js'\n\n/**\n * CnFieldListWidget — the `field-list` widget: a list stored INSIDE one field\n * of the current object (a portal's `domains[]`, its `editors[]`), shown as\n * rows and edited in place.\n *\n * Rows are drawn from configuration (title, meta line, pill, aside, a detail\n * line); a small form adds an entry, a button removes one, and each change is\n * saved as ONE PATCH of that field on the object, after re-reading the stored\n * value so a concurrent change to the same list is not overwritten with a\n * stale copy. Optional row actions call a configured endpoint (a \"check\n * again\"), and an optional `rowSource` merges live per-row state read from an\n * endpoint by key without writing it back.\n *\n * The server decides who may write: a refused PATCH shows the server's words\n * and leaves the list as stored.\n *\n * @spec openspec/changes/abstract-field-list/specs/field-list/spec.md\n */\nexport default {\n\tname: 'CnFieldListWidget',\n\n\tcomponents: { CnStatusBadge, NcActionButton, NcActions, NcButton, NcNoteCard, NcSelect, NcTextField },\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 * - `field` (required) — the object field holding the array.\n\t\t * - `key` — the item field that identifies an entry (default: the item itself for strings).\n\t\t * - `row` — `{ title, titleFallback?, avatar?, meta?: [paths|{ path, map?, format? }], pill?: { path, map?, variantMap? }, aside?: { path, format?, prefix?, fallback? }, detail?: { template, when? } }`.\n\t\t * - `add` — `{ label?, fields: [{ key, label, type?: 'text'|'select', options?, required?, pattern?, patternMessage? }], unique?, defaults? }`.\n\t\t * - `remove` — `{ label? }`; omitted = no remove.\n\t\t * - `rowActions` — `[{ id, label, when?, placement?: 'band', request: { method?, url, payload? } }]`; tokens `@object.<field>`, `@objectId`, `@row.<field>`.\n\t\t * - `rowSource` — `{ url, path?, key }`: live per-row state merged by key.\n\t\t * - `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'`.\n\t\t * - `add.trigger: 'button'`, `add.placement: 'header' | 'footer'` — the add form opens from a button.\n\t\t * - `readOnly`, `hint`, `emptyText`.\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/** The list as last saved by this widget; null = read it off objectData. */\n\t\t\tsaved: null,\n\t\t\t/** Live per-row state from `rowSource`, by key. */\n\t\t\tlive: {},\n\t\t\tdraft: {},\n\t\t\t/** Whether the add form is open (only with `add.trigger: 'button'`). */\n\t\t\tadding: false,\n\t\t\terrors: {},\n\t\t\tbusy: '',\n\t\t\tnotice: '',\n\t\t\tnoticeType: 'error',\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * The stored list.\n\t\t *\n\t\t * @return {Array}\n\t\t */\n\t\titems() {\n\t\t\tconst list = this.saved !== null ? this.saved : readPath(this.objectData || {}, this.content.field)\n\t\t\treturn Array.isArray(list) ? list : []\n\t\t},\n\n\t\t/**\n\t\t * The rows to draw.\n\t\t *\n\t\t * @return {Array<object>}\n\t\t * @spec openspec/changes/abstract-field-list/specs/field-list/spec.md#requirement-a-field-list-draws-the-entries-of-one-object-field\n\t\t */\n\t\trows() {\n\t\t\tconst spec = this.content.row || {}\n\t\t\treturn this.items.map((item, index) => {\n\t\t\t\tconst key = this.keyOf(item, index)\n\t\t\t\tconst entry = item && typeof item === 'object'\n\t\t\t\t\t? { ...item, ...(this.live[key] || {}) }\n\t\t\t\t\t: { value: item, ...(this.live[key] || {}) }\n\t\t\t\tconst title = this.text(entry, spec.title) || this.text(entry, spec.titleFallback) || (typeof item === 'string' ? item : key)\n\t\t\t\tconst meta = (Array.isArray(spec.meta) ? spec.meta : [])\n\t\t\t\t\t.map((part) => this.text(entry, part))\n\t\t\t\t\t.filter((part) => part !== '')\n\t\t\t\t\t.join(' · ')\n\t\t\t\treturn {\n\t\t\t\t\tkey,\n\t\t\t\t\titem,\n\t\t\t\t\tentry,\n\t\t\t\t\ttitle,\n\t\t\t\t\tinitials: spec.avatar ? this.initials(title) : '',\n\t\t\t\t\tmeta,\n\t\t\t\t\tpill: this.pillOf(entry, spec.pill),\n\t\t\t\t\taside: spec.aside ? this.asideOf(entry, spec.aside) : '',\n\t\t\t\t\tdetail: spec.detail && this.matches(entry, spec.detail.when) ? this.fill(spec.detail.template, entry) : '',\n\t\t\t\t\tactions: this.actionsFor(entry),\n\t\t\t\t\tcells: this.isTable ? this.cellsOf(entry) : [],\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/** Whether the list is read-only. */\n\t\treadOnly() {\n\t\t\treturn this.content.readOnly === true\n\t\t},\n\n\t\t/** Whether entries can be added. */\n\t\tcanAdd() {\n\t\t\treturn !this.readOnly && this.addFields.length > 0 && Boolean(this.content.field)\n\t\t},\n\n\t\t/** Whether entries can be removed. */\n\t\tcanRemove() {\n\t\t\treturn !this.readOnly && Boolean(this.content.remove) && Boolean(this.content.field)\n\t\t},\n\n\t\t/**\n\t\t * The add form's fields, labels translated, select options translated.\n\t\t *\n\t\t * @return {Array<object>}\n\t\t */\n\t\taddFields() {\n\t\t\tconst fields = this.content.add && Array.isArray(this.content.add.fields) ? this.content.add.fields : []\n\t\t\treturn fields\n\t\t\t\t.filter((field) => field && field.key)\n\t\t\t\t.map((field) => ({\n\t\t\t\t\t...field,\n\t\t\t\t\tlabel: this.tr(field.label || field.key),\n\t\t\t\t\toptions: (Array.isArray(field.options) ? field.options : []).map((option) => (\n\t\t\t\t\t\ttypeof option === 'object' ? { value: option.value, label: this.tr(option.label || String(option.value)) } : { value: option, label: this.tr(String(option)) }\n\t\t\t\t\t)),\n\t\t\t\t}))\n\t\t},\n\n\t\t/** Whether the rows are drawn as a table (`variant: 'table'`). */\n\t\tisTable() {\n\t\t\treturn this.content.variant === 'table' && this.tableColumns.length > 0\n\t\t},\n\n\t\t/**\n\t\t * The table columns, headers translated.\n\t\t *\n\t\t * @return {Array<object>}\n\t\t */\n\t\ttableColumns() {\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, index) => ({ ...column, key: `${column.path}-${index}`, header: this.tr(column.header || '') }))\n\t\t},\n\n\t\t/** Whether each row has a \"more actions\" menu (row actions not placed in the band, and remove). */\n\t\thasMenu() {\n\t\t\treturn this.isTable && !this.readOnly && (this.canRemove || this.rows.some((row) => this.menuItems(row).length > 0))\n\t\t},\n\n\t\t/** Rows with a detail line: in the table variant it is a warning band under the table. */\n\t\tbandRows() {\n\t\t\treturn this.rows\n\t\t\t\t.filter((row) => row.detail !== '')\n\t\t\t\t.map((row) => ({ ...row, bandActions: row.actions.filter((action) => action.placement === 'band') }))\n\t\t},\n\n\t\t/** Whether the add form opens from a button (`add.trigger: 'button'`). */\n\t\taddTrigger() {\n\t\t\treturn Boolean(this.content.add && this.content.add.trigger === 'button')\n\t\t},\n\n\t\t/** Where the add button sits: `header` (above the rows) or `footer` (default). */\n\t\taddPlacement() {\n\t\t\treturn this.content.add && this.content.add.placement === 'header' ? 'header' : 'footer'\n\t\t},\n\n\t\taddLabel() {\n\t\t\treturn this.tr((this.content.add && this.content.add.label) || 'Add')\n\t\t},\n\n\t\tremoveLabel() {\n\t\t\treturn this.tr((this.content.remove && this.content.remove.label) || 'Remove')\n\t\t},\n\n\t\thint() {\n\t\t\treturn this.tr(this.content.hint || '')\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\t/** The object URL the list is saved to, or '' when it is not known. */\n\t\tobjectUrl() {\n\t\t\tconst id = this.objectId || (this.objectData && this.objectData.id)\n\t\t\tif (!this.register || !this.schema || !id || typeof this.register !== 'string' || typeof this.schema !== 'string') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn `/apps/openregister/api/objects/${encodeURIComponent(this.register)}/${encodeURIComponent(this.schema)}/${encodeURIComponent(String(id))}`\n\t\t},\n\t},\n\n\twatch: {\n\t\tobjectData() {\n\t\t\tthis.saved = null\n\t\t\tthis.loadLive()\n\t\t},\n\t},\n\n\tcreated() {\n\t\tthis.resetDraft()\n\t\tthis.loadLive()\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 cells of one table row, one per column.\n\t\t *\n\t\t * @param {object} entry The entry.\n\t\t * @return {Array<{text: string, pill: object|null}>}\n\t\t */\n\t\tcellsOf(entry) {\n\t\t\treturn this.tableColumns.map((column) => ({\n\t\t\t\ttext: this.text(entry, column),\n\t\t\t\tpill: column.pill ? this.pillOf(entry, column) : null,\n\t\t\t}))\n\t\t},\n\n\t\t/**\n\t\t * The items of a row's menu in the table variant: the row actions not\n\t\t * placed in the band, then remove.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {Array<{id: string, label: string, run: Function}>}\n\t\t */\n\t\tmenuItems(row) {\n\t\t\tconst items = row.actions\n\t\t\t\t.filter((action) => action.placement !== 'band')\n\t\t\t\t.map((action) => ({ id: action.id, label: action.label, run: () => this.runAction(action, row) }))\n\t\t\tif (this.canRemove) {\n\t\t\t\titems.push({ id: 'remove', label: this.removeLabel, run: () => this.remove(row) })\n\t\t\t}\n\t\t\treturn items\n\t\t},\n\n\t\t/**\n\t\t * The identity of an entry.\n\t\t *\n\t\t * @param {unknown} item The entry.\n\t\t * @param {number} index Its position.\n\t\t * @return {string}\n\t\t */\n\t\tkeyOf(item, index) {\n\t\t\tif (item === null || typeof item !== 'object') {\n\t\t\t\treturn String(item)\n\t\t\t}\n\t\t\tconst key = this.content.key ? readPath(item, this.content.key) : undefined\n\t\t\treturn key === undefined || key === null || key === '' ? `item-${index}` : String(key)\n\t\t},\n\n\t\t/**\n\t\t * A display text off an entry: a path, or `{ path, map?, format? }`.\n\t\t *\n\t\t * @param {object} entry The entry.\n\t\t * @param {string|object} spec The spec.\n\t\t * @return {string}\n\t\t */\n\t\ttext(entry, spec) {\n\t\t\tif (!spec) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst def = typeof spec === 'string' ? { path: spec } : spec\n\t\t\tconst raw = readPath(entry, def.path)\n\t\t\tif (raw === null || raw === undefined || raw === '') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tif (def.format === 'date' || def.format === 'date-long') {\n\t\t\t\tconst date = parseDateValue(raw)\n\t\t\t\tif (date === null) {\n\t\t\t\t\treturn ''\n\t\t\t\t}\n\t\t\t\treturn def.format === 'date-long' ? formatBoardDate(date, new Date(date.getFullYear() + 1, 0, 1)) : formatBoardDate(date)\n\t\t\t}\n\t\t\tif (def.map && Object.hasOwn(def.map, String(raw))) {\n\t\t\t\treturn this.tr(def.map[String(raw)])\n\t\t\t}\n\t\t\treturn typeof raw === 'object' ? '' : String(raw)\n\t\t},\n\n\t\tpillOf(entry, spec) {\n\t\t\tif (!spec || !spec.path) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst raw = readPath(entry, spec.path)\n\t\t\tif (raw === null || raw === undefined || raw === '') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst label = this.text(entry, spec)\n\t\t\tconst variant = spec.variantMap && spec.variantMap[String(raw)]\n\t\t\treturn { label, variant: isBadgeVariant(variant) ? variant : 'default' }\n\t\t},\n\n\t\tasideOf(entry, spec) {\n\t\t\tconst value = this.text(entry, spec)\n\t\t\tif (value === '') {\n\t\t\t\t// No value: the optional `fallback` label, translated, instead of an empty aside.\n\t\t\t\treturn this.tr(spec.fallback)\n\t\t\t}\n\t\t\treturn spec.prefix ? `${this.tr(spec.prefix)} ${value}` : value\n\t\t},\n\n\t\t/**\n\t\t * Whether an entry matches a `{ field: value }` condition (every field equal).\n\t\t *\n\t\t * @param {object} entry The entry.\n\t\t * @param {object|undefined} when The condition.\n\t\t * @return {boolean}\n\t\t */\n\t\tmatches(entry, when) {\n\t\t\tif (!when || typeof when !== 'object') {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn Object.entries(when).every(([field, expected]) => {\n\t\t\t\tconst actual = readPath(entry, field)\n\t\t\t\tif (expected === false) {\n\t\t\t\t\treturn actual === false || actual === undefined || actual === null || actual === ''\n\t\t\t\t}\n\t\t\t\treturn String(actual) === String(expected)\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Fill `{path}` placeholders from an entry; '' when one stays empty.\n\t\t *\n\t\t * @param {string} template The template, translated first.\n\t\t * @param {object} entry The entry.\n\t\t * @return {string}\n\t\t */\n\t\tfill(template, entry) {\n\t\t\tif (typeof template !== 'string') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tlet missing = false\n\t\t\tconst out = this.tr(template).replace(/\\{([\\w.@]+)\\}/g, (match, path) => {\n\t\t\t\tconst value = readPath(entry, path)\n\t\t\t\tif (value === undefined || value === null || value === '') {\n\t\t\t\t\tmissing = true\n\t\t\t\t\treturn match\n\t\t\t\t}\n\t\t\t\treturn String(value)\n\t\t\t})\n\t\t\treturn missing ? '' : out\n\t\t},\n\n\t\tactionsFor(entry) {\n\t\t\tif (this.readOnly) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tconst actions = Array.isArray(this.content.rowActions) ? this.content.rowActions : []\n\t\t\treturn actions\n\t\t\t\t.filter((action) => action && action.id && action.request && action.request.url && this.matches(entry, action.when))\n\t\t\t\t.map((action) => ({ ...action, label: this.tr(action.label || action.id) }))\n\t\t},\n\n\t\tinitials(name) {\n\t\t\treturn String(name || '').split(/[\\s@._-]+/).filter(Boolean).slice(0, 2).map((part) => part.charAt(0).toUpperCase()).join('')\n\t\t},\n\n\t\tremoveAriaLabel(row) {\n\t\t\treturn `${this.removeLabel}: ${row.title}`\n\t\t},\n\n\t\tresetDraft() {\n\t\t\tconst draft = {}\n\t\t\tfor (const field of this.addFields) {\n\t\t\t\tconst first = field.type === 'select' && field.options.length > 0 ? field.options[0].value : ''\n\t\t\t\tdraft[field.key] = field.default !== undefined ? field.default : first\n\t\t\t}\n\t\t\tthis.draft = draft\n\t\t\tthis.errors = {}\n\t\t},\n\n\t\t/**\n\t\t * Resolve `@objectId`, `@object.<field>` and `@row.<field>` inside a string or a payload.\n\t\t *\n\t\t * @param {unknown} value The value.\n\t\t * @param {object} entry The row entry.\n\t\t * @return {unknown}\n\t\t */\n\t\tresolve(value, entry) {\n\t\t\tif (Array.isArray(value)) {\n\t\t\t\treturn value.map((v) => this.resolve(v, entry))\n\t\t\t}\n\t\t\tif (value && typeof value === 'object') {\n\t\t\t\treturn Object.fromEntries(Object.entries(value).map(([k, v]) => [k, this.resolve(v, entry)]))\n\t\t\t}\n\t\t\tif (typeof value !== 'string') {\n\t\t\t\treturn value\n\t\t\t}\n\t\t\tconst object = this.objectData || {}\n\t\t\tconst read = (source, path) => {\n\t\t\t\tconst v = readPath(source || {}, path)\n\t\t\t\treturn v === undefined || v === null ? '' : v\n\t\t\t}\n\t\t\tconst whole = value.match(/^@(row|object)\\.([\\w.@]+)$/)\n\t\t\tif (whole) {\n\t\t\t\treturn read(whole[1] === 'row' ? entry : object, whole[2])\n\t\t\t}\n\t\t\treturn value\n\t\t\t\t.replace(/@objectId/g, () => encodeURIComponent(String(this.objectId || object.id || '')))\n\t\t\t\t.replace(/@(row|object)\\.([\\w.@]+)/g, (m, scope, path) => encodeURIComponent(String(read(scope === 'row' ? entry : object, path))))\n\t\t},\n\n\t\tasync http() {\n\t\t\tconst { default: axios } = await import('@nextcloud/axios')\n\t\t\treturn { axios }\n\t\t},\n\n\t\t/**\n\t\t * Read live per-row state from `rowSource` and keep it by key.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync loadLive() {\n\t\t\tconst source = this.content.rowSource\n\t\t\tif (!source || !source.url || !source.key || !this.objectData) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst { axios } = await this.http()\n\t\t\t\tconst response = await axios.get(prefixUrl(this.resolve(source.url, {})))\n\t\t\t\tconst data = response && response.data\n\t\t\t\tconst list = source.path ? readPath(data, source.path) : data\n\t\t\t\tconst live = {}\n\t\t\t\tfor (const item of Array.isArray(list) ? list : []) {\n\t\t\t\t\tconst key = item && readPath(item, source.key)\n\t\t\t\t\tif (key !== undefined && key !== null) {\n\t\t\t\t\t\tlive[String(key)] = item\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tthis.live = live\n\t\t\t} catch (e) {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn('[CnFieldListWidget] rowSource could not be read:', e?.message || e)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Save a new list: re-read the stored value, apply the change to it, and\n\t\t * PATCH that one field.\n\t\t *\n\t\t * @param {(stored: Array) => Array} change Builds the next list from the stored one, or throws an Error with a `userMessage`.\n\t\t * @param {string} busyKey What is busy.\n\t\t * @return {Promise<boolean>} Whether it saved.\n\t\t * @spec openspec/changes/abstract-field-list/specs/field-list/spec.md#requirement-a-field-list-saves-one-field-with-one-patch\n\t\t */\n\t\tasync save(change, busyKey) {\n\t\t\tif (!this.objectUrl) {\n\t\t\t\tthis.notify(this.tr('This list cannot be saved here.'))\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tthis.busy = busyKey\n\t\t\tthis.notice = ''\n\t\t\ttry {\n\t\t\t\tconst { axios } = await this.http()\n\t\t\t\tconst { data: stored } = await axios.get(prefixUrl(this.objectUrl))\n\t\t\t\tconst current = readPath(stored || {}, this.content.field)\n\t\t\t\tconst next = change(Array.isArray(current) ? current : [])\n\t\t\t\tconst { data } = await axios.patch(prefixUrl(this.objectUrl), { [this.content.field]: next })\n\t\t\t\tconst written = readPath(data || {}, this.content.field)\n\t\t\t\tthis.saved = Array.isArray(written) ? written : next\n\t\t\t\t/**\n\t\t\t\t * @event saved The list was saved: one PATCH of the field succeeded.\n\t\t\t\t * @type {{field: string, value: Array}}\n\t\t\t\t */\n\t\t\t\tthis.$emit('saved', { field: this.content.field, value: this.saved })\n\t\t\t\treturn true\n\t\t\t} catch (e) {\n\t\t\t\tthis.notify(e && e.userMessage ? e.userMessage : serverErrorMessage(e))\n\t\t\t\treturn false\n\t\t\t} finally {\n\t\t\t\tthis.busy = ''\n\t\t\t}\n\t\t},\n\n\t\tnotify(message, type = 'error') {\n\t\t\tthis.notice = message\n\t\t\tthis.noticeType = type\n\t\t},\n\n\t\t/**\n\t\t * Validate the draft and add it.\n\t\t *\n\t\t * @return {Promise<void>}\n\t\t * @spec openspec/changes/abstract-field-list/specs/field-list/spec.md#requirement-a-field-list-adds-and-removes-entries\n\t\t */\n\t\tasync add() {\n\t\t\tconst errors = {}\n\t\t\tfor (const field of this.addFields) {\n\t\t\t\tconst value = this.draft[field.key]\n\t\t\t\tconst empty = value === undefined || value === null || String(value).trim() === ''\n\t\t\t\tif (field.required && empty) {\n\t\t\t\t\terrors[field.key] = this.tr('Fill this in.')\n\t\t\t\t} else if (!empty && field.pattern && !new RegExp(field.pattern).test(String(value).trim())) {\n\t\t\t\t\terrors[field.key] = this.tr(field.patternMessage || 'This value is not valid.')\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.errors = errors\n\t\t\tif (Object.keys(errors).length > 0) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst entry = { ...((this.content.add && this.content.add.defaults) || {}) }\n\t\t\tfor (const field of this.addFields) {\n\t\t\t\tconst value = this.draft[field.key]\n\t\t\t\tif (value !== undefined && value !== null && String(value).trim() !== '') {\n\t\t\t\t\tentry[field.key] = typeof value === 'string' ? value.trim() : value\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst unique = this.content.add && this.content.add.unique\n\t\t\tconst saved = await this.save((stored) => {\n\t\t\t\tif (unique && stored.some((item) => item && String(readPath(item, unique)) === String(entry[unique]))) {\n\t\t\t\t\tconst error = new Error('duplicate')\n\t\t\t\t\terror.userMessage = this.tr('This is already in the list.')\n\t\t\t\t\tthrow error\n\t\t\t\t}\n\t\t\t\treturn [...stored, entry]\n\t\t\t}, '*')\n\t\t\tif (saved) {\n\t\t\t\tthis.resetDraft()\n\t\t\t\tthis.adding = false\n\t\t\t\tthis.loadLive()\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Remove an entry by its key.\n\t\t *\n\t\t * @param {object} row The row.\n\t\t * @return {Promise<void>}\n\t\t */\n\t\tasync remove(row) {\n\t\t\tawait this.save((stored) => stored.filter((item, index) => this.keyOf(item, index) !== row.key), row.key)\n\t\t},\n\n\t\t/**\n\t\t * Run a row action's request, then re-read the live state.\n\t\t *\n\t\t * @param {object} action The action.\n\t\t * @param {object} row The row.\n\t\t * @return {Promise<void>}\n\t\t * @spec openspec/changes/abstract-field-list/specs/field-list/spec.md#requirement-a-field-list-row-can-run-a-configured-request\n\t\t */\n\t\tasync runAction(action, row) {\n\t\t\tthis.busy = row.key\n\t\t\tthis.notice = ''\n\t\t\ttry {\n\t\t\t\tconst { axios } = await this.http()\n\t\t\t\tconst method = String(action.request.method || 'POST').toLowerCase()\n\t\t\t\tconst payload = this.resolve(action.request.payload || {}, row.entry)\n\t\t\t\tconst response = method === 'get'\n\t\t\t\t\t? await axios.get(prefixUrl(this.resolve(action.request.url, row.entry)))\n\t\t\t\t\t: await axios[method](prefixUrl(this.resolve(action.request.url, row.entry)), payload)\n\t\t\t\tconst message = response && response.data && typeof response.data.message === 'string' ? response.data.message : ''\n\t\t\t\tif (message) {\n\t\t\t\t\tthis.notify(message, 'info')\n\t\t\t\t}\n\t\t\t\tawait this.loadLive()\n\t\t\t} catch (e) {\n\t\t\t\tthis.notify(serverErrorMessage(e))\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-field-list-widget {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n\tmin-width: 0;\n}\n\n.cn-field-list-widget__rows {\n\tlist-style: none;\n\tmargin: 0;\n\tpadding: 0;\n\tdisplay: flex;\n\tflex-direction: column;\n}\n\n.cn-field-list-widget__row {\n\tpadding: 10px 0;\n\tborder-top: 1px solid var(--color-border);\n}\n\n.cn-field-list-widget__row:first-child {\n\tborder-top: none;\n\tpadding-top: 0;\n}\n\n.cn-field-list-widget__line {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 12px;\n\tmin-width: 0;\n}\n\n.cn-field-list-widget__avatar {\n\tflex: 0 0 auto;\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 32px;\n\theight: 32px;\n\tborder-radius: 50%;\n\tbackground: var(--color-primary-element-light);\n\tcolor: var(--color-primary-element-light-text, var(--color-main-text));\n\tfont-size: 12px;\n\tfont-weight: 700;\n}\n\n.cn-field-list-widget__main {\n\tdisplay: flex;\n\tflex-direction: column;\n\tflex: 1 1 auto;\n\tmin-width: 0;\n}\n\n.cn-field-list-widget__title {\n\tfont-weight: 600;\n\toverflow-wrap: anywhere;\n}\n\n.cn-field-list-widget__meta,\n.cn-field-list-widget__aside,\n.cn-field-list-widget__detail,\n.cn-field-list-widget__hint,\n.cn-field-list-widget__empty {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-field-list-widget__meta,\n.cn-field-list-widget__aside,\n.cn-field-list-widget__detail {\n\tfont-size: 13px;\n}\n\n.cn-field-list-widget__aside {\n\twhite-space: nowrap;\n}\n\n.cn-field-list-widget__detail,\n.cn-field-list-widget__hint,\n.cn-field-list-widget__empty {\n\tmargin: 4px 0 0;\n\toverflow-wrap: anywhere;\n}\n\n.cn-field-list-widget__add {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: flex-end;\n\tgap: 8px 12px;\n}\n\n.cn-field-list-widget__add > * {\n\tflex: 1 1 180px;\n}\n\n.cn-field-list-widget__add > button {\n\tflex: 0 0 auto;\n}\n\n.cn-field-list-widget__toolbar {\n\tdisplay: flex;\n\tjustify-content: flex-end;\n}\n\n.cn-field-list-widget__table-wrap {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 12px;\n\toverflow-x: auto;\n}\n\n.cn-field-list-widget__table {\n\twidth: 100%;\n\tborder-collapse: collapse;\n}\n\n.cn-field-list-widget__th {\n\tpadding: 8px 12px;\n\ttext-align: start;\n\tfont-size: 13px;\n\tfont-weight: 600;\n\twhite-space: nowrap;\n\tcolor: var(--color-text-maxcontrast);\n\tbackground: var(--color-background-hover);\n}\n\n.cn-field-list-widget__th--menu,\n.cn-field-list-widget__td--menu {\n\twidth: 1%;\n\ttext-align: end;\n}\n\n.cn-field-list-widget__tr {\n\tborder-top: 1px solid var(--cn-board-hairline, var(--color-border));\n}\n\n.cn-field-list-widget__td {\n\tpadding: 10px 12px;\n\tvertical-align: middle;\n\toverflow-wrap: anywhere;\n}\n\n.cn-field-list-widget__sr {\n\tposition: absolute;\n\twidth: 1px;\n\theight: 1px;\n\toverflow: hidden;\n\tclip-path: inset(50%);\n}\n\n.cn-field-list-widget__band {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tjustify-content: space-between;\n\tgap: 8px 12px;\n\tpadding: 12px 14px;\n\tborder: 1px solid var(--color-warning);\n\tborder-radius: 10px;\n\tbackground: color-mix(in srgb, var(--color-warning) 14%, var(--color-main-background));\n\tfont-size: 14px;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createTextVNode","_Fragment","_renderList","_createBlock","_createVNode","_toDisplayString","_createElementVNode"],"mappings":"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"}