@conduction/nextcloud-vue 2.56.0-beta.3 → 2.56.0-beta.5

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 (314) hide show
  1. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js +12 -2
  2. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
  3. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +1 -2
  4. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
  5. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue2.js.map +1 -1
  6. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js +0 -3
  7. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js.map +1 -1
  8. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js.map +1 -1
  9. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js +1 -2
  10. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js.map +1 -1
  11. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +21 -1
  12. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  13. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +1 -1
  14. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  15. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js +12 -5
  16. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
  17. package/dist/esm/components/CnBoardView/CnBoardView.vue3.js +2 -1
  18. package/dist/esm/components/CnBoardView/CnBoardView.vue3.js.map +1 -1
  19. package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js +9 -1
  20. package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js.map +1 -1
  21. package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js.map +1 -1
  22. package/dist/esm/components/CnContextMenu/CnContextMenu.vue2.js +6 -1
  23. package/dist/esm/components/CnContextMenu/CnContextMenu.vue2.js.map +1 -1
  24. package/dist/esm/components/CnContextMenu/CnContextMenu.vue3.js.map +1 -1
  25. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +116 -15
  26. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  27. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +27 -14
  28. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  29. package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue2.js +13 -6
  30. package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue2.js.map +1 -1
  31. package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue3.js +2 -0
  32. package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue3.js.map +1 -1
  33. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +29 -1
  34. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  35. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  36. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js.map +1 -1
  37. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js +1 -2
  38. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js.map +1 -1
  39. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +3 -1
  40. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  41. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +17 -17
  42. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  43. package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue2.js +4 -21
  44. package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue2.js.map +1 -1
  45. package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue3.js.map +1 -1
  46. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js +0 -13
  47. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js.map +1 -1
  48. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue3.js +8 -8
  49. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue3.js.map +1 -1
  50. package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue2.js.map +1 -1
  51. package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue3.js +3 -2
  52. package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue3.js.map +1 -1
  53. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +67 -13
  54. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  55. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +15 -8
  56. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  57. package/dist/esm/components/CnIndexPage/manifestActionDispatch.js +31 -11
  58. package/dist/esm/components/CnIndexPage/manifestActionDispatch.js.map +1 -1
  59. package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue2.js.map +1 -1
  60. package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue3.js +1 -2
  61. package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue3.js.map +1 -1
  62. package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue2.js.map +1 -1
  63. package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue3.js +0 -1
  64. package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue3.js.map +1 -1
  65. package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue2.js +2 -17
  66. package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue2.js.map +1 -1
  67. package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue3.js.map +1 -1
  68. package/dist/esm/components/CnLogsPage/CnLogsPage.vue2.js +35 -11
  69. package/dist/esm/components/CnLogsPage/CnLogsPage.vue2.js.map +1 -1
  70. package/dist/esm/components/CnLogsPage/CnLogsPage.vue3.js +3 -2
  71. package/dist/esm/components/CnLogsPage/CnLogsPage.vue3.js.map +1 -1
  72. package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js +23 -5
  73. package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js.map +1 -1
  74. package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js +1 -1
  75. package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js.map +1 -1
  76. package/dist/esm/components/CnObjectList/CnObjectList.vue2.js +19 -3
  77. package/dist/esm/components/CnObjectList/CnObjectList.vue2.js.map +1 -1
  78. package/dist/esm/components/CnObjectList/CnObjectList.vue3.js.map +1 -1
  79. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +45 -15
  80. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
  81. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +4 -2
  82. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
  83. package/dist/esm/components/CnObjectRow/CnObjectRow.vue2.js +24 -6
  84. package/dist/esm/components/CnObjectRow/CnObjectRow.vue2.js.map +1 -1
  85. package/dist/esm/components/CnObjectRow/CnObjectRow.vue3.js +1 -1
  86. package/dist/esm/components/CnObjectRow/CnObjectRow.vue3.js.map +1 -1
  87. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
  88. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +0 -1
  89. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
  90. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue2.js.map +1 -1
  91. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js +4 -2
  92. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js.map +1 -1
  93. package/dist/esm/components/CnRowActions/CnRowActions.vue2.js +7 -2
  94. package/dist/esm/components/CnRowActions/CnRowActions.vue2.js.map +1 -1
  95. package/dist/esm/components/CnRowActions/CnRowActions.vue3.js.map +1 -1
  96. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js +49 -5
  97. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
  98. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js +1 -1
  99. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
  100. package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue2.js +5 -3
  101. package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue2.js.map +1 -1
  102. package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue3.js.map +1 -1
  103. package/dist/esm/components/CnTabs/CnTabs.vue2.js.map +1 -1
  104. package/dist/esm/components/CnTabs/CnTabs.vue3.js.map +1 -1
  105. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js +3 -1
  106. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js.map +1 -1
  107. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js +1 -0
  108. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js.map +1 -1
  109. package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue2.js +3 -1
  110. package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue2.js.map +1 -1
  111. package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue3.js.map +1 -1
  112. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue2.js +3 -1
  113. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue2.js.map +1 -1
  114. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue3.js +2 -4
  115. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue3.js.map +1 -1
  116. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue2.js +6 -0
  117. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue2.js.map +1 -1
  118. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue3.js +68 -53
  119. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue3.js.map +1 -1
  120. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue2.js +17 -0
  121. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue2.js.map +1 -1
  122. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue3.js +21 -19
  123. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue3.js.map +1 -1
  124. package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue2.js +3 -1
  125. package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue2.js.map +1 -1
  126. package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue3.js.map +1 -1
  127. package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue2.js +3 -1
  128. package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue2.js.map +1 -1
  129. package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue3.js +2 -4
  130. package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue3.js.map +1 -1
  131. package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue2.js +2 -1
  132. package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue2.js.map +1 -1
  133. package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue3.js.map +1 -1
  134. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue2.js +2 -1
  135. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue2.js.map +1 -1
  136. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue3.js +1 -2
  137. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue3.js.map +1 -1
  138. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js +2 -1
  139. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js.map +1 -1
  140. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue3.js.map +1 -1
  141. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js +2 -1
  142. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js.map +1 -1
  143. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue3.js +1 -2
  144. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue3.js.map +1 -1
  145. package/dist/esm/integrations/builtin/email/CnEmailCard.vue2.js.map +1 -1
  146. package/dist/esm/integrations/builtin/email/CnEmailCard.vue3.js.map +1 -1
  147. package/dist/esm/integrations/builtin/email/CnEmailTab.vue2.js.map +1 -1
  148. package/dist/esm/integrations/builtin/email/CnEmailTab.vue3.js +1 -2
  149. package/dist/esm/integrations/builtin/email/CnEmailTab.vue3.js.map +1 -1
  150. package/dist/esm/integrations/builtin/flow/CnFlowCard.vue2.js +2 -1
  151. package/dist/esm/integrations/builtin/flow/CnFlowCard.vue2.js.map +1 -1
  152. package/dist/esm/integrations/builtin/flow/CnFlowCard.vue3.js.map +1 -1
  153. package/dist/esm/integrations/builtin/flow/CnFlowTab.vue2.js +2 -1
  154. package/dist/esm/integrations/builtin/flow/CnFlowTab.vue2.js.map +1 -1
  155. package/dist/esm/integrations/builtin/flow/CnFlowTab.vue3.js +1 -2
  156. package/dist/esm/integrations/builtin/flow/CnFlowTab.vue3.js.map +1 -1
  157. package/dist/esm/integrations/builtin/forms/CnFormsCard.vue2.js +3 -1
  158. package/dist/esm/integrations/builtin/forms/CnFormsCard.vue2.js.map +1 -1
  159. package/dist/esm/integrations/builtin/forms/CnFormsCard.vue3.js.map +1 -1
  160. package/dist/esm/integrations/builtin/forms/CnFormsTab.vue2.js +3 -1
  161. package/dist/esm/integrations/builtin/forms/CnFormsTab.vue2.js.map +1 -1
  162. package/dist/esm/integrations/builtin/forms/CnFormsTab.vue3.js +2 -4
  163. package/dist/esm/integrations/builtin/forms/CnFormsTab.vue3.js.map +1 -1
  164. package/dist/esm/integrations/builtin/maps/CnMapsCard.vue2.js +3 -1
  165. package/dist/esm/integrations/builtin/maps/CnMapsCard.vue2.js.map +1 -1
  166. package/dist/esm/integrations/builtin/maps/CnMapsCard.vue3.js.map +1 -1
  167. package/dist/esm/integrations/builtin/maps/CnMapsTab.vue2.js +3 -1
  168. package/dist/esm/integrations/builtin/maps/CnMapsTab.vue2.js.map +1 -1
  169. package/dist/esm/integrations/builtin/maps/CnMapsTab.vue3.js +2 -4
  170. package/dist/esm/integrations/builtin/maps/CnMapsTab.vue3.js.map +1 -1
  171. package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue2.js +3 -1
  172. package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue2.js.map +1 -1
  173. package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue3.js.map +1 -1
  174. package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue2.js +3 -1
  175. package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue2.js.map +1 -1
  176. package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue3.js +2 -4
  177. package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue3.js.map +1 -1
  178. package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue2.js +2 -1
  179. package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue2.js.map +1 -1
  180. package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue3.js.map +1 -1
  181. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue2.js +2 -1
  182. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue2.js.map +1 -1
  183. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue3.js +1 -2
  184. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue3.js.map +1 -1
  185. package/dist/esm/integrations/builtin/polls/CnPollsCard.vue2.js +2 -1
  186. package/dist/esm/integrations/builtin/polls/CnPollsCard.vue2.js.map +1 -1
  187. package/dist/esm/integrations/builtin/polls/CnPollsCard.vue3.js.map +1 -1
  188. package/dist/esm/integrations/builtin/polls/CnPollsTab.vue2.js +2 -1
  189. package/dist/esm/integrations/builtin/polls/CnPollsTab.vue2.js.map +1 -1
  190. package/dist/esm/integrations/builtin/polls/CnPollsTab.vue3.js +1 -2
  191. package/dist/esm/integrations/builtin/polls/CnPollsTab.vue3.js.map +1 -1
  192. package/dist/esm/integrations/builtin/shares/CnSharesTab.vue2.js.map +1 -1
  193. package/dist/esm/integrations/builtin/shares/CnSharesTab.vue3.js +3 -6
  194. package/dist/esm/integrations/builtin/shares/CnSharesTab.vue3.js.map +1 -1
  195. package/dist/esm/integrations/builtin/talk/CnTalkCard.vue2.js +3 -1
  196. package/dist/esm/integrations/builtin/talk/CnTalkCard.vue2.js.map +1 -1
  197. package/dist/esm/integrations/builtin/talk/CnTalkCard.vue3.js.map +1 -1
  198. package/dist/esm/integrations/builtin/talk/CnTalkTab.vue2.js +3 -1
  199. package/dist/esm/integrations/builtin/talk/CnTalkTab.vue2.js.map +1 -1
  200. package/dist/esm/integrations/builtin/talk/CnTalkTab.vue3.js +2 -4
  201. package/dist/esm/integrations/builtin/talk/CnTalkTab.vue3.js.map +1 -1
  202. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue2.js +2 -1
  203. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue2.js.map +1 -1
  204. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue3.js.map +1 -1
  205. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue2.js +2 -1
  206. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue2.js.map +1 -1
  207. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue3.js +1 -2
  208. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue3.js.map +1 -1
  209. package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue2.js +4 -1
  210. package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue2.js.map +1 -1
  211. package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue3.js.map +1 -1
  212. package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue2.js +6 -3
  213. package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue2.js.map +1 -1
  214. package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue3.js +2 -4
  215. package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue3.js.map +1 -1
  216. package/dist/esm/l10n/en.json.js +2 -0
  217. package/dist/esm/l10n/en.json.js.map +1 -1
  218. package/dist/esm/l10n/nl.json.js +2 -0
  219. package/dist/esm/l10n/nl.json.js.map +1 -1
  220. package/dist/esm/nextcloud-vue.css +78 -7
  221. package/dist/esm/utils/actionLink.js +5 -0
  222. package/dist/esm/utils/actionLink.js.map +1 -1
  223. package/dist/esm/utils/actionsDispatcher.js +16 -1
  224. package/dist/esm/utils/actionsDispatcher.js.map +1 -1
  225. package/dist/esm/utils/linkNavigation.js +6 -3
  226. package/dist/esm/utils/linkNavigation.js.map +1 -1
  227. package/dist/esm/utils/rowAuxClick.js +30 -5
  228. package/dist/esm/utils/rowAuxClick.js.map +1 -1
  229. package/dist/esm/utils/safeHref.js +18 -3
  230. package/dist/esm/utils/safeHref.js.map +1 -1
  231. package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
  232. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  233. package/dist/nextcloud-vue.cjs.js +885 -393
  234. package/dist/nextcloud-vue.cjs.js.map +2 -2
  235. package/dist/nextcloud-vue.css +78 -7
  236. package/l10n/en.json +2 -0
  237. package/l10n/nl.json +2 -0
  238. package/package.json +1 -1
  239. package/src/components/CnActionsBar/CnActionsBar.vue +14 -5
  240. package/src/components/CnActionsMenu/CnActionsMenu.vue +0 -3
  241. package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +1 -2
  242. package/src/components/CnAppRoot/CnAppRoot.vue +31 -2
  243. package/src/components/CnBoardView/CnBoardView.vue +13 -5
  244. package/src/components/CnCardGrid/CnCardGrid.vue +9 -1
  245. package/src/components/CnContextMenu/CnContextMenu.vue +6 -1
  246. package/src/components/CnDataTable/CnDataTable.md +1 -1
  247. package/src/components/CnDataTable/CnDataTable.vue +131 -17
  248. package/src/components/CnDateAxisView/CnDateAxisView.vue +15 -6
  249. package/src/components/CnDetailPage/CnDetailPage.md +1 -1
  250. package/src/components/CnDetailPage/CnDetailPage.vue +29 -1
  251. package/src/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue +1 -2
  252. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +5 -3
  253. package/src/components/CnFilesWidget/CnFilesWidget.vue +4 -21
  254. package/src/components/CnFlowRunsWidget/CnFlowRunsWidget.vue +8 -21
  255. package/src/components/CnFlowsPage/CnFlowsPage.vue +2 -1
  256. package/src/components/CnIndexPage/CnIndexPage.vue +75 -14
  257. package/src/components/CnIndexPage/manifestActionDispatch.js +31 -11
  258. package/src/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue +1 -2
  259. package/src/components/CnInteractionFormWidget/CnInteractionFormWidget.vue +0 -1
  260. package/src/components/CnLinkButtonWidget/CnLinkButtonWidget.vue +2 -17
  261. package/src/components/CnLogsPage/CnLogsPage.vue +37 -12
  262. package/src/components/CnObjectCard/CnObjectCard.vue +24 -6
  263. package/src/components/CnObjectList/CnObjectList.vue +19 -3
  264. package/src/components/CnObjectListWidget/CnObjectListWidget.vue +47 -15
  265. package/src/components/CnObjectRow/CnObjectRow.vue +25 -7
  266. package/src/components/CnObjectSidebar/CnFilesTab.vue +0 -1
  267. package/src/components/CnPageRenderer/CnPageRenderer.vue +2 -0
  268. package/src/components/CnRowActions/CnRowActions.vue +7 -2
  269. package/src/components/CnSetupWizard/CnSetupWizard.vue +57 -6
  270. package/src/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue +5 -3
  271. package/src/components/CnTabs/CnTabs.vue +7 -3
  272. package/src/components/CnTasksWidget/CnTasksWidget.vue +4 -1
  273. package/src/css/table.css +26 -0
  274. package/src/integrations/builtin/analytics/CnAnalyticsCard.vue +3 -2
  275. package/src/integrations/builtin/analytics/CnAnalyticsTab.vue +5 -6
  276. package/src/integrations/builtin/bookmarks/CnBookmarksCard.vue +12 -1
  277. package/src/integrations/builtin/bookmarks/CnBookmarksTab.vue +23 -6
  278. package/src/integrations/builtin/bookmarks/__tests__/CnBookmarksTab.spec.js +7 -2
  279. package/src/integrations/builtin/collectives/CnCollectivesCard.vue +3 -2
  280. package/src/integrations/builtin/collectives/CnCollectivesTab.vue +5 -6
  281. package/src/integrations/builtin/cospend/CnCospendCard.vue +2 -2
  282. package/src/integrations/builtin/cospend/CnCospendTab.vue +3 -4
  283. package/src/integrations/builtin/deck/CnDeckCard.vue +2 -2
  284. package/src/integrations/builtin/deck/CnDeckTab.vue +3 -4
  285. package/src/integrations/builtin/email/CnEmailCard.vue +6 -2
  286. package/src/integrations/builtin/email/CnEmailTab.vue +7 -4
  287. package/src/integrations/builtin/flow/CnFlowCard.vue +2 -2
  288. package/src/integrations/builtin/flow/CnFlowTab.vue +3 -4
  289. package/src/integrations/builtin/forms/CnFormsCard.vue +3 -2
  290. package/src/integrations/builtin/forms/CnFormsTab.vue +5 -6
  291. package/src/integrations/builtin/maps/CnMapsCard.vue +3 -2
  292. package/src/integrations/builtin/maps/CnMapsTab.vue +5 -6
  293. package/src/integrations/builtin/maps/__tests__/CnMapsTab.spec.js +9 -0
  294. package/src/integrations/builtin/openproject/CnOpenprojectCard.vue +3 -2
  295. package/src/integrations/builtin/openproject/CnOpenprojectTab.vue +5 -6
  296. package/src/integrations/builtin/photos/CnPhotosCard.vue +2 -2
  297. package/src/integrations/builtin/photos/CnPhotosTab.vue +3 -4
  298. package/src/integrations/builtin/polls/CnPollsCard.vue +2 -2
  299. package/src/integrations/builtin/polls/CnPollsTab.vue +3 -4
  300. package/src/integrations/builtin/shares/CnSharesTab.vue +3 -6
  301. package/src/integrations/builtin/talk/CnTalkCard.vue +3 -2
  302. package/src/integrations/builtin/talk/CnTalkTab.vue +5 -6
  303. package/src/integrations/builtin/time-tracker/CnTimeTrackerCard.vue +2 -2
  304. package/src/integrations/builtin/time-tracker/CnTimeTrackerTab.vue +3 -4
  305. package/src/integrations/builtin/xwiki/CnXwikiCard.vue +4 -2
  306. package/src/integrations/builtin/xwiki/CnXwikiTab.vue +7 -7
  307. package/src/integrations/builtin/xwiki/__tests__/CnXwikiTab.spec.js +9 -0
  308. package/src/schemas/app-manifest-v2.schema.json +2 -1
  309. package/src/utils/actionLink.js +5 -0
  310. package/src/utils/actionsDispatcher.js +15 -0
  311. package/src/utils/linkNavigation.js +6 -3
  312. package/src/utils/rowAuxClick.js +29 -4
  313. package/src/utils/safeHref.js +18 -3
  314. package/src/utils/validateManifestV2.compiled.js +2 -2
@@ -2,7 +2,7 @@ import { translate } from '@nextcloud/l10n';
2
2
  import { buildBoardColumns } from '../../utils/boardColumns.js';
3
3
  import { buildSwimlanes } from '../../utils/boardSwimlanes.js';
4
4
  import { runBoardDrop, DROP_OUTCOMES } from '../../utils/boardTransition.js';
5
- import { isRowMiddleClick } from '../../utils/rowAuxClick.js';
5
+ import { preventMiddleClickAutoscroll, isRowMiddleClick } from '../../utils/rowAuxClick.js';
6
6
 
7
7
  /**
8
8
  * CnBoardView — the index page's rows as a board, one column per stage.
@@ -94,7 +94,7 @@ var script = {
94
94
  },
95
95
  },
96
96
 
97
- emits: ['card-click', 'moved', 'refused', 'stale'],
97
+ emits: ['card-click', 'card-aux-click', 'moved', 'refused', 'stale'],
98
98
 
99
99
  data() {
100
100
  return {
@@ -174,6 +174,8 @@ var script = {
174
174
  methods: {
175
175
  t: translate,
176
176
 
177
+ preventMiddleClickAutoscroll,
178
+
177
179
  /**
178
180
  * Open one card. The pointer and the keyboard both come through here,
179
181
  * so a reader on Enter reaches the same record as a reader on click.
@@ -184,13 +186,15 @@ var script = {
184
186
  */
185
187
  openCard(card, event) {
186
188
  /**
187
- * @event card-click Emitted when a reader opens a card, by click, Enter or middle click. Payload: `(card, event)` — the card row and the native event, so the host can open it in a new tab on a ctrl/cmd/shift or middle click.
189
+ * @event card-click Emitted when a reader opens a card, by click or Enter. Payload: `(card, event)` — the card row and the native event, so the host can open it in a new tab on a ctrl/cmd/shift click. A middle click emits `card-aux-click` instead.
188
190
  */
189
191
  this.$emit('card-click', card, event);
190
192
  },
191
193
 
192
194
  /**
193
- * Middle click on a card's open button: open it like a click would.
195
+ * Middle click on a card's open button: emits `card-aux-click`, not
196
+ * `card-click`, so an existing listener that navigates never moves the
197
+ * current tab away.
194
198
  *
195
199
  * @param {object} card The card the reader picked.
196
200
  * @param {MouseEvent} event The auxclick event.
@@ -198,7 +202,10 @@ var script = {
198
202
  */
199
203
  onCardAuxClick(card, event) {
200
204
  if (isRowMiddleClick(event)) {
201
- this.openCard(card, event);
205
+ /**
206
+ * @event card-aux-click Emitted when a reader middle-clicks a card, for opening it in a new tab (see `openRowTarget`). Payload: `(card, event)` — the card row and the native auxclick event.
207
+ */
208
+ this.$emit('card-aux-click', card, event);
202
209
  }
203
210
  },
204
211
 
@@ -1 +1 @@
1
- {"version":3,"file":"CnBoardView.vue2.js","sources":["../../../../src/components/CnBoardView/CnBoardView.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-board-view\" data-testid=\"cn-board-view\">\n\t\t<p v-if=\"!usable\" class=\"cn-board-view__unusable\" data-testid=\"cn-board-unusable\">\n\t\t\t{{ unusableText }}\n\t\t</p>\n\n\t\t<div\n\t\t\tv-for=\"lane in lanes\"\n\t\t\tv-else\n\t\t\t:key=\"`lane-${lane.key}`\"\n\t\t\tclass=\"cn-board-view__lane\"\n\t\t\tdata-testid=\"cn-board-lane\"\n\t\t\t:data-lane=\"lane.key\">\n\t\t\t<button\n\t\t\t\tv-if=\"lane.key !== ''\"\n\t\t\t\tclass=\"cn-board-view__lane-header\"\n\t\t\t\tdata-testid=\"cn-board-lane-header\"\n\t\t\t\t:aria-expanded=\"String(!isCollapsed(lane.key))\"\n\t\t\t\t@click=\"toggleLane(lane.key)\">\n\t\t\t\t{{ lane.label }} ({{ lane.count }})\n\t\t\t</button>\n\n\t\t\t<div\n\t\t\t\tv-show=\"!isCollapsed(lane.key)\"\n\t\t\t\tclass=\"cn-board-view__columns\"\n\t\t\t\trole=\"list\"\n\t\t\t\t:aria-label=\"lane.key === '' ? boardLabel : lane.label\">\n\t\t\t\t<section\n\t\t\t\t\tv-for=\"column in lane.columns\"\n\t\t\t\t\t:key=\"`col-${lane.key}-${column.key}`\"\n\t\t\t\t\tclass=\"cn-board-view__column\"\n\t\t\t\t\trole=\"listitem\"\n\t\t\t\t\tdata-testid=\"cn-board-column\"\n\t\t\t\t\t:data-column=\"column.key\"\n\t\t\t\t\t@dragover.prevent\n\t\t\t\t\t@drop=\"onDrop(column, $event)\">\n\t\t\t\t\t<h3 class=\"cn-board-view__column-header\">\n\t\t\t\t\t\t{{ column.label }}\n\t\t\t\t\t\t<span class=\"cn-board-view__count\" data-testid=\"cn-board-count\">\n\t\t\t\t\t\t\t{{ countLabel(column) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</h3>\n\n\t\t\t\t\t<!--\n\t\t\t\t\t\tA list of its own, so a reader is told how many cards\n\t\t\t\t\t\tthis column holds. The column is a listitem of the\n\t\t\t\t\t\tboard's list of columns; the cards are a list inside\n\t\t\t\t\t\tit, not more items of that same list.\n\t\t\t\t\t-->\n\t\t\t\t\t<div\n\t\t\t\t\t\tv-if=\"column.cards.length > 0\"\n\t\t\t\t\t\tclass=\"cn-board-view__cards\"\n\t\t\t\t\t\trole=\"list\"\n\t\t\t\t\t\t:aria-label=\"column.label\">\n\t\t\t\t\t\t<!--\n\t\t\t\t\t\t\tThe card is a CONTAINER, and every action on it is\n\t\t\t\t\t\t\tits own control. It carries the drag and nothing\n\t\t\t\t\t\t\telse: no tabindex, no click, no key handler, and\n\t\t\t\t\t\t\tnever role=\"button\".\n\n\t\t\t\t\t\t\t🔴 role=\"button\" HERE WOULD BREAK THE MOVE. An\n\t\t\t\t\t\t\telement with that role has presentational children,\n\t\t\t\t\t\t\tso the <select> below would be an interactive\n\t\t\t\t\t\t\tcontrol inside a button. That is invalid, and it\n\t\t\t\t\t\t\ttakes away the only way a keyboard user can move a\n\t\t\t\t\t\t\tcard. It would also collapse every field on the\n\t\t\t\t\t\t\tcard into one button label. See\n\t\t\t\t\t\t\topenspec/changes/board-card-role-and-keyboard.\n\n\t\t\t\t\t\t\tMeasured rather than argued: putting role=\"button\"\n\t\t\t\t\t\t\there makes axe report nested-interactive (serious,\n\t\t\t\t\t\t\t\"Interactive controls must not be nested\") and\n\t\t\t\t\t\t\taria-required-children (critical), and four tests\n\t\t\t\t\t\t\tin tests/a11y/CnBoardView.a11y.spec.js go red.\n\t\t\t\t\t\t\tGate 32 suggests exactly this change. The gate is a\n\t\t\t\t\t\t\tmechanical floor, not the standard.\n\t\t\t\t\t\t-->\n\t\t\t\t\t\t<article\n\t\t\t\t\t\t\tv-for=\"card in column.cards\"\n\t\t\t\t\t\t\t:key=\"`card-${cardKey(card)}`\"\n\t\t\t\t\t\t\tclass=\"cn-board-view__card\"\n\t\t\t\t\t\t\trole=\"listitem\"\n\t\t\t\t\t\t\tdata-testid=\"cn-board-card\"\n\t\t\t\t\t\t\t:data-card-id=\"cardKey(card)\"\n\t\t\t\t\t\t\t:draggable=\"canMove\"\n\t\t\t\t\t\t\t@dragstart=\"onDragStart(card, column, $event)\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-for=\"field in cardFields\"\n\t\t\t\t\t\t\t\t:key=\"`f-${cardKey(card)}-${field}`\"\n\t\t\t\t\t\t\t\tclass=\"cn-board-view__card-field\">\n\t\t\t\t\t\t\t\t{{ card[field] }}\n\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t<!--\n\t\t\t\t\t\t\t\tOpening the card. A native button, so Enter and\n\t\t\t\t\t\t\t\tSpace both work: the old markup handled Enter\n\t\t\t\t\t\t\t\tonly, and Space is what most people try on\n\t\t\t\t\t\t\t\tsomething that looks like a button.\n\t\t\t\t\t\t\t-->\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tclass=\"cn-board-view__card-open\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-board-card-open\"\n\t\t\t\t\t\t\t\t:data-card-id=\"cardKey(card)\"\n\t\t\t\t\t\t\t\t:aria-label=\"cardLabel(card, column)\"\n\t\t\t\t\t\t\t\t@click=\"openCard(card, $event)\"\n\t\t\t\t\t\t\t\t@auxclick=\"onCardAuxClick(card, $event)\">\n\t\t\t\t\t\t\t\t{{ openLabel }}\n\t\t\t\t\t\t\t</button>\n\n\t\t\t\t\t\t\t<!--\n\t\t\t\t\t\t\t\tThe keyboard's way to do what a drag does. A board\n\t\t\t\t\t\t\t\twhose only move is a drag is a board a keyboard\n\t\t\t\t\t\t\t\tuser cannot use at all, and \"drag the card\" is not\n\t\t\t\t\t\t\t\tan instruction you can follow with a keyboard.\n\t\t\t\t\t\t\t-->\n\t\t\t\t\t\t\t<label v-if=\"canMove\" class=\"cn-board-view__move\">\n\t\t\t\t\t\t\t\t<span class=\"cn-board-view__move-label\">{{ moveLabel }}</span>\n\t\t\t\t\t\t\t\t<select\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-board-move\"\n\t\t\t\t\t\t\t\t\t:data-card-id=\"cardKey(card)\"\n\t\t\t\t\t\t\t\t\t:value=\"column.key\"\n\t\t\t\t\t\t\t\t\t@change=\"onMoveTo(card, column, $event)\">\n\t\t\t\t\t\t\t\t\t<option\n\t\t\t\t\t\t\t\t\t\tv-for=\"target in lane.columns\"\n\t\t\t\t\t\t\t\t\t\t:key=\"`t-${cardKey(card)}-${target.key}`\"\n\t\t\t\t\t\t\t\t\t\t:value=\"target.key\">\n\t\t\t\t\t\t\t\t\t\t{{ target.label }}\n\t\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t</article>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<p\n\t\t\t\t\t\tv-else\n\t\t\t\t\t\tclass=\"cn-board-view__empty\"\n\t\t\t\t\t\tdata-testid=\"cn-board-column-empty\">\n\t\t\t\t\t\t{{ emptyColumnText }}\n\t\t\t\t\t</p>\n\t\t\t\t</section>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<p\n\t\t\tv-if=\"refusal\"\n\t\t\tclass=\"cn-board-view__refusal\"\n\t\t\tdata-testid=\"cn-board-refusal\"\n\t\t\trole=\"alert\">\n\t\t\t{{ refusal }}\n\t\t</p>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { buildBoardColumns } from '../../utils/boardColumns.js'\nimport { buildSwimlanes } from '../../utils/boardSwimlanes.js'\nimport { DROP_OUTCOMES, runBoardDrop } from '../../utils/boardTransition.js'\nimport { isRowMiddleClick } from '../../utils/rowAuxClick.js'\n\n/**\n * CnBoardView — the index page's rows as a board, one column per stage.\n *\n * THE COLUMNS ARE THE SCHEMA'S, NOT THE DATA'S. `buildBoardColumns` decides\n * them and is tested on its own; this renders what it returns. A board that\n * built its columns from the values present would lose the stage nothing is\n * in, which is the one you drag a card into.\n *\n * 🔴 A MOVE GOES THROUGH THE HOST'S TRANSITION AND NEVER WRITES THE STATUS\n * FIELD. Writing it here would skip every guard, side effect and audit entry\n * the transition carries, and the board would become a way to move a case past\n * a rule the case page enforces. `runBoardDrop` owns that contract; this\n * component owns the gestures that reach it.\n *\n * 🔴 THERE ARE TWO GESTURES, ALWAYS. A board whose only move is a drag is a\n * board a keyboard user cannot use, and \"drag the card\" is not an instruction\n * anybody can follow with a keyboard. Move to on the card runs the identical\n * call.\n *\n * @event {object} card-click — A card was opened. Payload: the row.\n * @event {object} moved — A card moved. Payload: `{ card, toKey }`.\n * @event {object} refused — A move was refused. Payload: `{ card, message }`.\n * @event {object} stale — The card had already moved. Payload: `{ card }`.\n */\nexport default {\n\tname: 'CnBoardView',\n\n\tprops: {\n\t\t/** The rows the list holds. */\n\t\trows: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** The status field's schema: its enum or its lifecycle states. */\n\t\tstatusFieldSchema: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The property a row's status lives on. */\n\t\tstatusField: {\n\t\t\ttype: String,\n\t\t\tdefault: 'status',\n\t\t},\n\n\t\t/** The fields a card shows. */\n\t\tcardFields: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** A second field to group the board into rows by. */\n\t\tswimlaneField: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The row key, so a card is identifiable. */\n\t\trowKey: {\n\t\t\ttype: String,\n\t\t\tdefault: 'id',\n\t\t},\n\n\t\t/**\n\t\t * The host's transition. Absent, the board is read-only: no drag, no\n\t\t * Move to, because a gesture that cannot do anything is worse than no\n\t\t * gesture at all.\n\t\t */\n\t\trunTransition: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Re-read one card, to check it has not moved under the dragger. */\n\t\treread: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Whether the counts are of one page. A count of a page shown as\n\t\t * though it were the total is a number somebody quotes in a meeting.\n\t\t */\n\t\tpaged: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\t},\n\n\temits: ['card-click', 'moved', 'refused', 'stale'],\n\n\tdata() {\n\t\treturn {\n\t\t\t/** Lanes the reader has collapsed, for as long as they are here. */\n\t\t\tcollapsed: [],\n\t\t\t/** The guard's own words, when it refused the last move. */\n\t\t\trefusal: '',\n\t\t\t/** The card being dragged, and where from. */\n\t\t\tdragging: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {boolean} Whether the status field can back a board at all. */\n\t\tusable() {\n\t\t\treturn this.board.usable\n\t\t},\n\n\t\t/** @return {object} The columns, from the schema. */\n\t\tboard() {\n\t\t\treturn buildBoardColumns({\n\t\t\t\tfield: this.statusFieldSchema,\n\t\t\t\trows: this.rows,\n\t\t\t\tstatusField: this.statusField,\n\t\t\t\toffBoardLabel: t('nextcloud-vue', 'Elsewhere'),\n\t\t\t})\n\t\t},\n\n\t\t/** @return {Array<object>} The board as rows of columns. */\n\t\tlanes() {\n\t\t\treturn buildSwimlanes({\n\t\t\t\tcolumns: this.board.columns,\n\t\t\t\tswimlaneField: this.swimlaneField,\n\t\t\t\tunassignedLabel: t('nextcloud-vue', 'Unassigned'),\n\t\t\t})\n\t\t},\n\n\t\t/** @return {boolean} Whether a move is possible at all. */\n\t\tcanMove() {\n\t\t\treturn typeof this.runTransition === 'function'\n\t\t},\n\n\t\t/** @return {string} What to say when the field cannot back a board. */\n\t\tunusableText() {\n\t\t\treturn t('nextcloud-vue', 'This list has no status field with stages, so it cannot be shown as a board.')\n\t\t},\n\n\t\t/** @return {string} The board's accessible name. */\n\t\tboardLabel() {\n\t\t\treturn t('nextcloud-vue', 'Board')\n\t\t},\n\n\t\t/** @return {string} The Move to control's label. */\n\t\tmoveLabel() {\n\t\t\treturn t('nextcloud-vue', 'Move to')\n\t\t},\n\n\t\t/**\n\t\t * The visible text on a card's opening control.\n\t\t *\n\t\t * Short, because it repeats on every card. Which card it opens is\n\t\t * said by the control's `aria-label`, so a reader hearing a list of\n\t\t * buttons hears the card's own name rather than \"Open\" nine times.\n\t\t *\n\t\t * @return {string} The label.\n\t\t */\n\t\topenLabel() {\n\t\t\treturn t('nextcloud-vue', 'Open')\n\t\t},\n\n\t\t/** @return {string} What an empty column says. */\n\t\temptyColumnText() {\n\t\t\treturn t('nextcloud-vue', 'Nothing here')\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Open one card. The pointer and the keyboard both come through here,\n\t\t * so a reader on Enter reaches the same record as a reader on click.\n\t\t *\n\t\t * @param {object} card The card the reader picked.\n\t\t * @param {MouseEvent|KeyboardEvent} [event] The originating event.\n\t\t * @return {void} Nothing.\n\t\t */\n\t\topenCard(card, event) {\n\t\t\t/**\n\t\t\t * @event card-click Emitted when a reader opens a card, by click, Enter or middle click. Payload: `(card, event)` — the card row and the native event, so the host can open it in a new tab on a ctrl/cmd/shift or middle click.\n\t\t\t */\n\t\t\tthis.$emit('card-click', card, event)\n\t\t},\n\n\t\t/**\n\t\t * Middle click on a card's open button: open it like a click would.\n\t\t *\n\t\t * @param {object} card The card the reader picked.\n\t\t * @param {MouseEvent} event The auxclick event.\n\t\t * @return {void} Nothing.\n\t\t */\n\t\tonCardAuxClick(card, event) {\n\t\t\tif (isRowMiddleClick(event)) {\n\t\t\t\tthis.openCard(card, event)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * A card's identity.\n\t\t *\n\t\t * @param {object} card The row.\n\t\t * @return {string} Its key.\n\t\t */\n\t\tcardKey(card) {\n\t\t\treturn String(card?.[this.rowKey] ?? '')\n\t\t},\n\n\t\t/**\n\t\t * A column's count, said as a page count when that is what it is.\n\t\t *\n\t\t * @param {object} column The column.\n\t\t * @return {string} The count.\n\t\t */\n\t\tcountLabel(column) {\n\t\t\treturn this.paged\n\t\t\t\t? t('nextcloud-vue', '{count} on this page', { count: column.count })\n\t\t\t\t: String(column.count)\n\t\t},\n\n\t\t/**\n\t\t * What a card is called to somebody who cannot see which column it is in.\n\t\t *\n\t\t * @param {object} card The row.\n\t\t * @param {object} column Its column.\n\t\t * @return {string} The accessible name.\n\t\t */\n\t\tcardLabel(card, column) {\n\t\t\tconst name = this.cardFields.map((field) => card?.[field]).filter(Boolean).join(', ')\n\t\t\treturn t('nextcloud-vue', '{name}, in {column}', { name: name || this.cardKey(card), column: column.label })\n\t\t},\n\n\t\t/**\n\t\t * Whether a lane is collapsed.\n\t\t *\n\t\t * @param {string} key The lane.\n\t\t * @return {boolean} True when collapsed.\n\t\t */\n\t\tisCollapsed(key) {\n\t\t\treturn this.collapsed.includes(key)\n\t\t},\n\n\t\t/**\n\t\t * Collapse a lane, or open it again.\n\t\t *\n\t\t * Held in the component for as long as the reader is on the view, and\n\t\t * deliberately not persisted: a collapsed lane somebody forgot they\n\t\t * collapsed is work that has gone missing for them.\n\t\t *\n\t\t * @param {string} key The lane.\n\t\t */\n\t\ttoggleLane(key) {\n\t\t\tthis.collapsed = this.isCollapsed(key)\n\t\t\t\t? this.collapsed.filter((entry) => entry !== key)\n\t\t\t\t: [...this.collapsed, key]\n\t\t},\n\n\t\t/**\n\t\t * Remember which card is being dragged and where from.\n\t\t *\n\t\t * @param {object} card The row.\n\t\t * @param {object} column Its column.\n\t\t * @param {DragEvent} event The event.\n\t\t */\n\t\tonDragStart(card, column, event) {\n\t\t\tthis.dragging = { card, fromKey: column.key }\n\t\t\t// Firefox drops a drag that carries no data.\n\t\t\tevent?.dataTransfer?.setData?.('text/plain', this.cardKey(card))\n\t\t},\n\n\t\t/**\n\t\t * A drop on a column.\n\t\t *\n\t\t * @param {object} column The column dropped on.\n\t\t * @return {Promise<void>} Nothing.\n\t\t */\n\t\tasync onDrop(column) {\n\t\t\tif (!this.dragging) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst { card, fromKey } = this.dragging\n\t\t\tthis.dragging = null\n\t\t\tawait this.move(card, fromKey, column.key)\n\t\t},\n\n\t\t/**\n\t\t * Move to, from the card's own menu.\n\t\t *\n\t\t * @param {object} card The row.\n\t\t * @param {object} column Its column.\n\t\t * @param {Event} event The change event.\n\t\t * @return {Promise<void>} Nothing.\n\t\t */\n\t\tasync onMoveTo(card, column, event) {\n\t\t\tawait this.move(card, column.key, event?.target?.value ?? '')\n\t\t},\n\n\t\t/**\n\t\t * The one move, whichever gesture asked for it.\n\t\t *\n\t\t * @param {object} card The row.\n\t\t * @param {string} fromKey Where it was.\n\t\t * @param {string} toKey Where it is going.\n\t\t * @return {Promise<void>} Nothing.\n\t\t */\n\t\tasync move(card, fromKey, toKey) {\n\t\t\tthis.refusal = ''\n\n\t\t\tconst result = await runBoardDrop({\n\t\t\t\tcard,\n\t\t\t\tfromKey,\n\t\t\t\ttoKey,\n\t\t\t\tstatusField: this.statusField,\n\t\t\t\trunTransition: this.runTransition,\n\t\t\t\treread: this.reread,\n\t\t\t})\n\n\t\t\tif (result.outcome === DROP_OUTCOMES.MOVED) {\n\t\t\t\t/**\n\t\t\t\t * @event moved Emitted when the host's transition accepted the move and the card now sits in the new lane. Payload: `{ card, toKey }`.\n\t\t\t\t */\n\t\t\t\tthis.$emit('moved', { card: result.card, toKey })\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (result.outcome === DROP_OUTCOMES.REFUSED) {\n\t\t\t\t// The guard's own sentence when it gave one. Our own only when\n\t\t\t\t// it did not, so the two are never confused.\n\t\t\t\tthis.refusal = result.message || t('nextcloud-vue', 'That move was refused.')\n\t\t\t\t/**\n\t\t\t\t * @event refused Emitted when the guard turned the move down. Payload: `{ card, message }`, the message being the guard's own sentence when it gave one.\n\t\t\t\t */\n\t\t\t\tthis.$emit('refused', { card: result.card, message: this.refusal })\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (result.outcome === DROP_OUTCOMES.STALE) {\n\t\t\t\tthis.refusal = t('nextcloud-vue', 'Somebody else moved this card. It is shown where it is now.')\n\t\t\t\t/**\n\t\t\t\t * @event stale Emitted when somebody else moved the card first, so the board shows it where it is now. Payload: `{ card }`.\n\t\t\t\t */\n\t\t\t\tthis.$emit('stale', { card: result.card })\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped lang=\"scss\">\n.cn-board-view__columns {\n\tdisplay: flex;\n\tgap: 12px;\n\toverflow-x: auto;\n\talign-items: flex-start;\n}\n\n.cn-board-view__column {\n\tflex: 0 0 260px;\n\tbackground-color: var(--color-background-hover);\n\tborder-radius: var(--border-radius-large);\n\tpadding: 8px;\n}\n\n.cn-board-view__column-header {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tfont-size: 1rem;\n\tmargin: 0 0 8px;\n}\n\n.cn-board-view__count,\n.cn-board-view__empty,\n.cn-board-view__unusable {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-board-view__card {\n\tdisplay: block;\n\tbackground-color: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tpadding: 8px;\n\tmargin-bottom: 8px;\n}\n\n.cn-board-view__card-field {\n\tdisplay: block;\n}\n\n/*\n * The card's opening control. The cursor and the focus ring live here now,\n * on the thing that actually does something, rather than on the box.\n */\n.cn-board-view__card-open {\n\tbackground: none;\n\tborder: none;\n\tpadding: 0;\n\tcursor: pointer;\n\tcolor: var(--color-primary-element);\n\tfont-size: 0.8rem;\n}\n\n.cn-board-view__card-open:focus-visible {\n\toutline: 2px solid var(--color-primary-element);\n\toutline-offset: 2px;\n}\n\n.cn-board-view__lane-header {\n\tbackground: none;\n\tborder: none;\n\tfont-weight: bold;\n\tpadding: 8px 0;\n\tcursor: pointer;\n\tcolor: var(--color-main-text);\n}\n\n.cn-board-view__move-label {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.8rem;\n}\n\n.cn-board-view__refusal {\n\tcolor: var(--color-error);\n}\n</style>\n"],"names":["t"],"mappings":"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"}
1
+ {"version":3,"file":"CnBoardView.vue2.js","sources":["../../../../src/components/CnBoardView/CnBoardView.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-board-view\" data-testid=\"cn-board-view\">\n\t\t<p v-if=\"!usable\" class=\"cn-board-view__unusable\" data-testid=\"cn-board-unusable\">\n\t\t\t{{ unusableText }}\n\t\t</p>\n\n\t\t<div\n\t\t\tv-for=\"lane in lanes\"\n\t\t\tv-else\n\t\t\t:key=\"`lane-${lane.key}`\"\n\t\t\tclass=\"cn-board-view__lane\"\n\t\t\tdata-testid=\"cn-board-lane\"\n\t\t\t:data-lane=\"lane.key\">\n\t\t\t<button\n\t\t\t\tv-if=\"lane.key !== ''\"\n\t\t\t\tclass=\"cn-board-view__lane-header\"\n\t\t\t\tdata-testid=\"cn-board-lane-header\"\n\t\t\t\t:aria-expanded=\"String(!isCollapsed(lane.key))\"\n\t\t\t\t@click=\"toggleLane(lane.key)\">\n\t\t\t\t{{ lane.label }} ({{ lane.count }})\n\t\t\t</button>\n\n\t\t\t<div\n\t\t\t\tv-show=\"!isCollapsed(lane.key)\"\n\t\t\t\tclass=\"cn-board-view__columns\"\n\t\t\t\trole=\"list\"\n\t\t\t\t:aria-label=\"lane.key === '' ? boardLabel : lane.label\">\n\t\t\t\t<section\n\t\t\t\t\tv-for=\"column in lane.columns\"\n\t\t\t\t\t:key=\"`col-${lane.key}-${column.key}`\"\n\t\t\t\t\tclass=\"cn-board-view__column\"\n\t\t\t\t\trole=\"listitem\"\n\t\t\t\t\tdata-testid=\"cn-board-column\"\n\t\t\t\t\t:data-column=\"column.key\"\n\t\t\t\t\t@dragover.prevent\n\t\t\t\t\t@drop=\"onDrop(column, $event)\">\n\t\t\t\t\t<h3 class=\"cn-board-view__column-header\">\n\t\t\t\t\t\t{{ column.label }}\n\t\t\t\t\t\t<span class=\"cn-board-view__count\" data-testid=\"cn-board-count\">\n\t\t\t\t\t\t\t{{ countLabel(column) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</h3>\n\n\t\t\t\t\t<!--\n\t\t\t\t\t\tA list of its own, so a reader is told how many cards\n\t\t\t\t\t\tthis column holds. The column is a listitem of the\n\t\t\t\t\t\tboard's list of columns; the cards are a list inside\n\t\t\t\t\t\tit, not more items of that same list.\n\t\t\t\t\t-->\n\t\t\t\t\t<div\n\t\t\t\t\t\tv-if=\"column.cards.length > 0\"\n\t\t\t\t\t\tclass=\"cn-board-view__cards\"\n\t\t\t\t\t\trole=\"list\"\n\t\t\t\t\t\t:aria-label=\"column.label\">\n\t\t\t\t\t\t<!--\n\t\t\t\t\t\t\tThe card is a CONTAINER, and every action on it is\n\t\t\t\t\t\t\tits own control. It carries the drag and nothing\n\t\t\t\t\t\t\telse: no tabindex, no click, no key handler, and\n\t\t\t\t\t\t\tnever role=\"button\".\n\n\t\t\t\t\t\t\t🔴 role=\"button\" HERE WOULD BREAK THE MOVE. An\n\t\t\t\t\t\t\telement with that role has presentational children,\n\t\t\t\t\t\t\tso the <select> below would be an interactive\n\t\t\t\t\t\t\tcontrol inside a button. That is invalid, and it\n\t\t\t\t\t\t\ttakes away the only way a keyboard user can move a\n\t\t\t\t\t\t\tcard. It would also collapse every field on the\n\t\t\t\t\t\t\tcard into one button label. See\n\t\t\t\t\t\t\topenspec/changes/board-card-role-and-keyboard.\n\n\t\t\t\t\t\t\tMeasured rather than argued: putting role=\"button\"\n\t\t\t\t\t\t\there makes axe report nested-interactive (serious,\n\t\t\t\t\t\t\t\"Interactive controls must not be nested\") and\n\t\t\t\t\t\t\taria-required-children (critical), and four tests\n\t\t\t\t\t\t\tin tests/a11y/CnBoardView.a11y.spec.js go red.\n\t\t\t\t\t\t\tGate 32 suggests exactly this change. The gate is a\n\t\t\t\t\t\t\tmechanical floor, not the standard.\n\t\t\t\t\t\t-->\n\t\t\t\t\t\t<article\n\t\t\t\t\t\t\tv-for=\"card in column.cards\"\n\t\t\t\t\t\t\t:key=\"`card-${cardKey(card)}`\"\n\t\t\t\t\t\t\tclass=\"cn-board-view__card\"\n\t\t\t\t\t\t\trole=\"listitem\"\n\t\t\t\t\t\t\tdata-testid=\"cn-board-card\"\n\t\t\t\t\t\t\t:data-card-id=\"cardKey(card)\"\n\t\t\t\t\t\t\t:draggable=\"canMove\"\n\t\t\t\t\t\t\t@dragstart=\"onDragStart(card, column, $event)\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-for=\"field in cardFields\"\n\t\t\t\t\t\t\t\t:key=\"`f-${cardKey(card)}-${field}`\"\n\t\t\t\t\t\t\t\tclass=\"cn-board-view__card-field\">\n\t\t\t\t\t\t\t\t{{ card[field] }}\n\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t<!--\n\t\t\t\t\t\t\t\tOpening the card. A native button, so Enter and\n\t\t\t\t\t\t\t\tSpace both work: the old markup handled Enter\n\t\t\t\t\t\t\t\tonly, and Space is what most people try on\n\t\t\t\t\t\t\t\tsomething that looks like a button.\n\t\t\t\t\t\t\t-->\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tclass=\"cn-board-view__card-open\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-board-card-open\"\n\t\t\t\t\t\t\t\t:data-card-id=\"cardKey(card)\"\n\t\t\t\t\t\t\t\t:aria-label=\"cardLabel(card, column)\"\n\t\t\t\t\t\t\t\t@click=\"openCard(card, $event)\"\n\t\t\t\t\t\t\t\t@mousedown=\"preventMiddleClickAutoscroll\"\n\t\t\t\t\t\t\t\t@auxclick=\"onCardAuxClick(card, $event)\">\n\t\t\t\t\t\t\t\t{{ openLabel }}\n\t\t\t\t\t\t\t</button>\n\n\t\t\t\t\t\t\t<!--\n\t\t\t\t\t\t\t\tThe keyboard's way to do what a drag does. A board\n\t\t\t\t\t\t\t\twhose only move is a drag is a board a keyboard\n\t\t\t\t\t\t\t\tuser cannot use at all, and \"drag the card\" is not\n\t\t\t\t\t\t\t\tan instruction you can follow with a keyboard.\n\t\t\t\t\t\t\t-->\n\t\t\t\t\t\t\t<label v-if=\"canMove\" class=\"cn-board-view__move\">\n\t\t\t\t\t\t\t\t<span class=\"cn-board-view__move-label\">{{ moveLabel }}</span>\n\t\t\t\t\t\t\t\t<select\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-board-move\"\n\t\t\t\t\t\t\t\t\t:data-card-id=\"cardKey(card)\"\n\t\t\t\t\t\t\t\t\t:value=\"column.key\"\n\t\t\t\t\t\t\t\t\t@change=\"onMoveTo(card, column, $event)\">\n\t\t\t\t\t\t\t\t\t<option\n\t\t\t\t\t\t\t\t\t\tv-for=\"target in lane.columns\"\n\t\t\t\t\t\t\t\t\t\t:key=\"`t-${cardKey(card)}-${target.key}`\"\n\t\t\t\t\t\t\t\t\t\t:value=\"target.key\">\n\t\t\t\t\t\t\t\t\t\t{{ target.label }}\n\t\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t</article>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<p\n\t\t\t\t\t\tv-else\n\t\t\t\t\t\tclass=\"cn-board-view__empty\"\n\t\t\t\t\t\tdata-testid=\"cn-board-column-empty\">\n\t\t\t\t\t\t{{ emptyColumnText }}\n\t\t\t\t\t</p>\n\t\t\t\t</section>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<p\n\t\t\tv-if=\"refusal\"\n\t\t\tclass=\"cn-board-view__refusal\"\n\t\t\tdata-testid=\"cn-board-refusal\"\n\t\t\trole=\"alert\">\n\t\t\t{{ refusal }}\n\t\t</p>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { buildBoardColumns } from '../../utils/boardColumns.js'\nimport { buildSwimlanes } from '../../utils/boardSwimlanes.js'\nimport { DROP_OUTCOMES, runBoardDrop } from '../../utils/boardTransition.js'\nimport { isRowMiddleClick, preventMiddleClickAutoscroll } from '../../utils/rowAuxClick.js'\n\n/**\n * CnBoardView — the index page's rows as a board, one column per stage.\n *\n * THE COLUMNS ARE THE SCHEMA'S, NOT THE DATA'S. `buildBoardColumns` decides\n * them and is tested on its own; this renders what it returns. A board that\n * built its columns from the values present would lose the stage nothing is\n * in, which is the one you drag a card into.\n *\n * 🔴 A MOVE GOES THROUGH THE HOST'S TRANSITION AND NEVER WRITES THE STATUS\n * FIELD. Writing it here would skip every guard, side effect and audit entry\n * the transition carries, and the board would become a way to move a case past\n * a rule the case page enforces. `runBoardDrop` owns that contract; this\n * component owns the gestures that reach it.\n *\n * 🔴 THERE ARE TWO GESTURES, ALWAYS. A board whose only move is a drag is a\n * board a keyboard user cannot use, and \"drag the card\" is not an instruction\n * anybody can follow with a keyboard. Move to on the card runs the identical\n * call.\n *\n * @event {object} card-click — A card was opened. Payload: the row.\n * @event {object} moved — A card moved. Payload: `{ card, toKey }`.\n * @event {object} refused — A move was refused. Payload: `{ card, message }`.\n * @event {object} stale — The card had already moved. Payload: `{ card }`.\n */\nexport default {\n\tname: 'CnBoardView',\n\n\tprops: {\n\t\t/** The rows the list holds. */\n\t\trows: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** The status field's schema: its enum or its lifecycle states. */\n\t\tstatusFieldSchema: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The property a row's status lives on. */\n\t\tstatusField: {\n\t\t\ttype: String,\n\t\t\tdefault: 'status',\n\t\t},\n\n\t\t/** The fields a card shows. */\n\t\tcardFields: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** A second field to group the board into rows by. */\n\t\tswimlaneField: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The row key, so a card is identifiable. */\n\t\trowKey: {\n\t\t\ttype: String,\n\t\t\tdefault: 'id',\n\t\t},\n\n\t\t/**\n\t\t * The host's transition. Absent, the board is read-only: no drag, no\n\t\t * Move to, because a gesture that cannot do anything is worse than no\n\t\t * gesture at all.\n\t\t */\n\t\trunTransition: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Re-read one card, to check it has not moved under the dragger. */\n\t\treread: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Whether the counts are of one page. A count of a page shown as\n\t\t * though it were the total is a number somebody quotes in a meeting.\n\t\t */\n\t\tpaged: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\t},\n\n\temits: ['card-click', 'card-aux-click', 'moved', 'refused', 'stale'],\n\n\tdata() {\n\t\treturn {\n\t\t\t/** Lanes the reader has collapsed, for as long as they are here. */\n\t\t\tcollapsed: [],\n\t\t\t/** The guard's own words, when it refused the last move. */\n\t\t\trefusal: '',\n\t\t\t/** The card being dragged, and where from. */\n\t\t\tdragging: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {boolean} Whether the status field can back a board at all. */\n\t\tusable() {\n\t\t\treturn this.board.usable\n\t\t},\n\n\t\t/** @return {object} The columns, from the schema. */\n\t\tboard() {\n\t\t\treturn buildBoardColumns({\n\t\t\t\tfield: this.statusFieldSchema,\n\t\t\t\trows: this.rows,\n\t\t\t\tstatusField: this.statusField,\n\t\t\t\toffBoardLabel: t('nextcloud-vue', 'Elsewhere'),\n\t\t\t})\n\t\t},\n\n\t\t/** @return {Array<object>} The board as rows of columns. */\n\t\tlanes() {\n\t\t\treturn buildSwimlanes({\n\t\t\t\tcolumns: this.board.columns,\n\t\t\t\tswimlaneField: this.swimlaneField,\n\t\t\t\tunassignedLabel: t('nextcloud-vue', 'Unassigned'),\n\t\t\t})\n\t\t},\n\n\t\t/** @return {boolean} Whether a move is possible at all. */\n\t\tcanMove() {\n\t\t\treturn typeof this.runTransition === 'function'\n\t\t},\n\n\t\t/** @return {string} What to say when the field cannot back a board. */\n\t\tunusableText() {\n\t\t\treturn t('nextcloud-vue', 'This list has no status field with stages, so it cannot be shown as a board.')\n\t\t},\n\n\t\t/** @return {string} The board's accessible name. */\n\t\tboardLabel() {\n\t\t\treturn t('nextcloud-vue', 'Board')\n\t\t},\n\n\t\t/** @return {string} The Move to control's label. */\n\t\tmoveLabel() {\n\t\t\treturn t('nextcloud-vue', 'Move to')\n\t\t},\n\n\t\t/**\n\t\t * The visible text on a card's opening control.\n\t\t *\n\t\t * Short, because it repeats on every card. Which card it opens is\n\t\t * said by the control's `aria-label`, so a reader hearing a list of\n\t\t * buttons hears the card's own name rather than \"Open\" nine times.\n\t\t *\n\t\t * @return {string} The label.\n\t\t */\n\t\topenLabel() {\n\t\t\treturn t('nextcloud-vue', 'Open')\n\t\t},\n\n\t\t/** @return {string} What an empty column says. */\n\t\temptyColumnText() {\n\t\t\treturn t('nextcloud-vue', 'Nothing here')\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\tpreventMiddleClickAutoscroll,\n\n\t\t/**\n\t\t * Open one card. The pointer and the keyboard both come through here,\n\t\t * so a reader on Enter reaches the same record as a reader on click.\n\t\t *\n\t\t * @param {object} card The card the reader picked.\n\t\t * @param {MouseEvent|KeyboardEvent} [event] The originating event.\n\t\t * @return {void} Nothing.\n\t\t */\n\t\topenCard(card, event) {\n\t\t\t/**\n\t\t\t * @event card-click Emitted when a reader opens a card, by click or Enter. Payload: `(card, event)` — the card row and the native event, so the host can open it in a new tab on a ctrl/cmd/shift click. A middle click emits `card-aux-click` instead.\n\t\t\t */\n\t\t\tthis.$emit('card-click', card, event)\n\t\t},\n\n\t\t/**\n\t\t * Middle click on a card's open button: emits `card-aux-click`, not\n\t\t * `card-click`, so an existing listener that navigates never moves the\n\t\t * current tab away.\n\t\t *\n\t\t * @param {object} card The card the reader picked.\n\t\t * @param {MouseEvent} event The auxclick event.\n\t\t * @return {void} Nothing.\n\t\t */\n\t\tonCardAuxClick(card, event) {\n\t\t\tif (isRowMiddleClick(event)) {\n\t\t\t\t/**\n\t\t\t\t * @event card-aux-click Emitted when a reader middle-clicks a card, for opening it in a new tab (see `openRowTarget`). Payload: `(card, event)` — the card row and the native auxclick event.\n\t\t\t\t */\n\t\t\t\tthis.$emit('card-aux-click', card, event)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * A card's identity.\n\t\t *\n\t\t * @param {object} card The row.\n\t\t * @return {string} Its key.\n\t\t */\n\t\tcardKey(card) {\n\t\t\treturn String(card?.[this.rowKey] ?? '')\n\t\t},\n\n\t\t/**\n\t\t * A column's count, said as a page count when that is what it is.\n\t\t *\n\t\t * @param {object} column The column.\n\t\t * @return {string} The count.\n\t\t */\n\t\tcountLabel(column) {\n\t\t\treturn this.paged\n\t\t\t\t? t('nextcloud-vue', '{count} on this page', { count: column.count })\n\t\t\t\t: String(column.count)\n\t\t},\n\n\t\t/**\n\t\t * What a card is called to somebody who cannot see which column it is in.\n\t\t *\n\t\t * @param {object} card The row.\n\t\t * @param {object} column Its column.\n\t\t * @return {string} The accessible name.\n\t\t */\n\t\tcardLabel(card, column) {\n\t\t\tconst name = this.cardFields.map((field) => card?.[field]).filter(Boolean).join(', ')\n\t\t\treturn t('nextcloud-vue', '{name}, in {column}', { name: name || this.cardKey(card), column: column.label })\n\t\t},\n\n\t\t/**\n\t\t * Whether a lane is collapsed.\n\t\t *\n\t\t * @param {string} key The lane.\n\t\t * @return {boolean} True when collapsed.\n\t\t */\n\t\tisCollapsed(key) {\n\t\t\treturn this.collapsed.includes(key)\n\t\t},\n\n\t\t/**\n\t\t * Collapse a lane, or open it again.\n\t\t *\n\t\t * Held in the component for as long as the reader is on the view, and\n\t\t * deliberately not persisted: a collapsed lane somebody forgot they\n\t\t * collapsed is work that has gone missing for them.\n\t\t *\n\t\t * @param {string} key The lane.\n\t\t */\n\t\ttoggleLane(key) {\n\t\t\tthis.collapsed = this.isCollapsed(key)\n\t\t\t\t? this.collapsed.filter((entry) => entry !== key)\n\t\t\t\t: [...this.collapsed, key]\n\t\t},\n\n\t\t/**\n\t\t * Remember which card is being dragged and where from.\n\t\t *\n\t\t * @param {object} card The row.\n\t\t * @param {object} column Its column.\n\t\t * @param {DragEvent} event The event.\n\t\t */\n\t\tonDragStart(card, column, event) {\n\t\t\tthis.dragging = { card, fromKey: column.key }\n\t\t\t// Firefox drops a drag that carries no data.\n\t\t\tevent?.dataTransfer?.setData?.('text/plain', this.cardKey(card))\n\t\t},\n\n\t\t/**\n\t\t * A drop on a column.\n\t\t *\n\t\t * @param {object} column The column dropped on.\n\t\t * @return {Promise<void>} Nothing.\n\t\t */\n\t\tasync onDrop(column) {\n\t\t\tif (!this.dragging) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst { card, fromKey } = this.dragging\n\t\t\tthis.dragging = null\n\t\t\tawait this.move(card, fromKey, column.key)\n\t\t},\n\n\t\t/**\n\t\t * Move to, from the card's own menu.\n\t\t *\n\t\t * @param {object} card The row.\n\t\t * @param {object} column Its column.\n\t\t * @param {Event} event The change event.\n\t\t * @return {Promise<void>} Nothing.\n\t\t */\n\t\tasync onMoveTo(card, column, event) {\n\t\t\tawait this.move(card, column.key, event?.target?.value ?? '')\n\t\t},\n\n\t\t/**\n\t\t * The one move, whichever gesture asked for it.\n\t\t *\n\t\t * @param {object} card The row.\n\t\t * @param {string} fromKey Where it was.\n\t\t * @param {string} toKey Where it is going.\n\t\t * @return {Promise<void>} Nothing.\n\t\t */\n\t\tasync move(card, fromKey, toKey) {\n\t\t\tthis.refusal = ''\n\n\t\t\tconst result = await runBoardDrop({\n\t\t\t\tcard,\n\t\t\t\tfromKey,\n\t\t\t\ttoKey,\n\t\t\t\tstatusField: this.statusField,\n\t\t\t\trunTransition: this.runTransition,\n\t\t\t\treread: this.reread,\n\t\t\t})\n\n\t\t\tif (result.outcome === DROP_OUTCOMES.MOVED) {\n\t\t\t\t/**\n\t\t\t\t * @event moved Emitted when the host's transition accepted the move and the card now sits in the new lane. Payload: `{ card, toKey }`.\n\t\t\t\t */\n\t\t\t\tthis.$emit('moved', { card: result.card, toKey })\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (result.outcome === DROP_OUTCOMES.REFUSED) {\n\t\t\t\t// The guard's own sentence when it gave one. Our own only when\n\t\t\t\t// it did not, so the two are never confused.\n\t\t\t\tthis.refusal = result.message || t('nextcloud-vue', 'That move was refused.')\n\t\t\t\t/**\n\t\t\t\t * @event refused Emitted when the guard turned the move down. Payload: `{ card, message }`, the message being the guard's own sentence when it gave one.\n\t\t\t\t */\n\t\t\t\tthis.$emit('refused', { card: result.card, message: this.refusal })\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (result.outcome === DROP_OUTCOMES.STALE) {\n\t\t\t\tthis.refusal = t('nextcloud-vue', 'Somebody else moved this card. It is shown where it is now.')\n\t\t\t\t/**\n\t\t\t\t * @event stale Emitted when somebody else moved the card first, so the board shows it where it is now. Payload: `{ card }`.\n\t\t\t\t */\n\t\t\t\tthis.$emit('stale', { card: result.card })\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped lang=\"scss\">\n.cn-board-view__columns {\n\tdisplay: flex;\n\tgap: 12px;\n\toverflow-x: auto;\n\talign-items: flex-start;\n}\n\n.cn-board-view__column {\n\tflex: 0 0 260px;\n\tbackground-color: var(--color-background-hover);\n\tborder-radius: var(--border-radius-large);\n\tpadding: 8px;\n}\n\n.cn-board-view__column-header {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tfont-size: 1rem;\n\tmargin: 0 0 8px;\n}\n\n.cn-board-view__count,\n.cn-board-view__empty,\n.cn-board-view__unusable {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-board-view__card {\n\tdisplay: block;\n\tbackground-color: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tpadding: 8px;\n\tmargin-bottom: 8px;\n}\n\n.cn-board-view__card-field {\n\tdisplay: block;\n}\n\n/*\n * The card's opening control. The cursor and the focus ring live here now,\n * on the thing that actually does something, rather than on the box.\n */\n.cn-board-view__card-open {\n\tbackground: none;\n\tborder: none;\n\tpadding: 0;\n\tcursor: pointer;\n\tcolor: var(--color-primary-element);\n\tfont-size: 0.8rem;\n}\n\n.cn-board-view__card-open:focus-visible {\n\toutline: 2px solid var(--color-primary-element);\n\toutline-offset: 2px;\n}\n\n.cn-board-view__lane-header {\n\tbackground: none;\n\tborder: none;\n\tfont-weight: bold;\n\tpadding: 8px 0;\n\tcursor: pointer;\n\tcolor: var(--color-main-text);\n}\n\n.cn-board-view__move-label {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.8rem;\n}\n\n.cn-board-view__refusal {\n\tcolor: var(--color-error);\n}\n</style>\n"],"names":["t"],"mappings":"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"}
@@ -72,7 +72,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
72
72
  role: "listitem",
73
73
  "data-testid": "cn-board-column",
74
74
  "data-column": column.key,
75
- onDragover: _cache[0] || (_cache[0] = withModifiers(() => {}, ["prevent"])),
75
+ onDragover: _cache[1] || (_cache[1] = withModifiers(() => {}, ["prevent"])),
76
76
  onDrop: $event => ($options.onDrop(column, $event))
77
77
  }, [
78
78
  createElementVNode("h3", _hoisted_7, [
@@ -109,6 +109,7 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
109
109
  "data-card-id": $options.cardKey(card),
110
110
  "aria-label": $options.cardLabel(card, column),
111
111
  onClick: $event => ($options.openCard(card, $event)),
112
+ onMousedown: _cache[0] || (_cache[0] = (...args) => ($options.preventMiddleClickAutoscroll && $options.preventMiddleClickAutoscroll(...args))),
112
113
  onAuxclick: $event => ($options.onCardAuxClick(card, $event))
113
114
  }, toDisplayString($options.openLabel), 41 /* TEXT, PROPS, NEED_HYDRATION */, _hoisted_11),
114
115
  ($options.canMove)
@@ -1 +1 @@
1
- {"version":3,"file":"CnBoardView.vue3.js","sources":["../../../../src/components/CnBoardView/CnBoardView.vue?vue&type=template&id=997a7a5e&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-board-view\" data-testid=\"cn-board-view\">\n\t\t<p v-if=\"!usable\" class=\"cn-board-view__unusable\" data-testid=\"cn-board-unusable\">\n\t\t\t{{ unusableText }}\n\t\t</p>\n\n\t\t<div\n\t\t\tv-for=\"lane in lanes\"\n\t\t\tv-else\n\t\t\t:key=\"`lane-${lane.key}`\"\n\t\t\tclass=\"cn-board-view__lane\"\n\t\t\tdata-testid=\"cn-board-lane\"\n\t\t\t:data-lane=\"lane.key\">\n\t\t\t<button\n\t\t\t\tv-if=\"lane.key !== ''\"\n\t\t\t\tclass=\"cn-board-view__lane-header\"\n\t\t\t\tdata-testid=\"cn-board-lane-header\"\n\t\t\t\t:aria-expanded=\"String(!isCollapsed(lane.key))\"\n\t\t\t\t@click=\"toggleLane(lane.key)\">\n\t\t\t\t{{ lane.label }} ({{ lane.count }})\n\t\t\t</button>\n\n\t\t\t<div\n\t\t\t\tv-show=\"!isCollapsed(lane.key)\"\n\t\t\t\tclass=\"cn-board-view__columns\"\n\t\t\t\trole=\"list\"\n\t\t\t\t:aria-label=\"lane.key === '' ? boardLabel : lane.label\">\n\t\t\t\t<section\n\t\t\t\t\tv-for=\"column in lane.columns\"\n\t\t\t\t\t:key=\"`col-${lane.key}-${column.key}`\"\n\t\t\t\t\tclass=\"cn-board-view__column\"\n\t\t\t\t\trole=\"listitem\"\n\t\t\t\t\tdata-testid=\"cn-board-column\"\n\t\t\t\t\t:data-column=\"column.key\"\n\t\t\t\t\t@dragover.prevent\n\t\t\t\t\t@drop=\"onDrop(column, $event)\">\n\t\t\t\t\t<h3 class=\"cn-board-view__column-header\">\n\t\t\t\t\t\t{{ column.label }}\n\t\t\t\t\t\t<span class=\"cn-board-view__count\" data-testid=\"cn-board-count\">\n\t\t\t\t\t\t\t{{ countLabel(column) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</h3>\n\n\t\t\t\t\t<!--\n\t\t\t\t\t\tA list of its own, so a reader is told how many cards\n\t\t\t\t\t\tthis column holds. The column is a listitem of the\n\t\t\t\t\t\tboard's list of columns; the cards are a list inside\n\t\t\t\t\t\tit, not more items of that same list.\n\t\t\t\t\t-->\n\t\t\t\t\t<div\n\t\t\t\t\t\tv-if=\"column.cards.length > 0\"\n\t\t\t\t\t\tclass=\"cn-board-view__cards\"\n\t\t\t\t\t\trole=\"list\"\n\t\t\t\t\t\t:aria-label=\"column.label\">\n\t\t\t\t\t\t<!--\n\t\t\t\t\t\t\tThe card is a CONTAINER, and every action on it is\n\t\t\t\t\t\t\tits own control. It carries the drag and nothing\n\t\t\t\t\t\t\telse: no tabindex, no click, no key handler, and\n\t\t\t\t\t\t\tnever role=\"button\".\n\n\t\t\t\t\t\t\t🔴 role=\"button\" HERE WOULD BREAK THE MOVE. An\n\t\t\t\t\t\t\telement with that role has presentational children,\n\t\t\t\t\t\t\tso the <select> below would be an interactive\n\t\t\t\t\t\t\tcontrol inside a button. That is invalid, and it\n\t\t\t\t\t\t\ttakes away the only way a keyboard user can move a\n\t\t\t\t\t\t\tcard. It would also collapse every field on the\n\t\t\t\t\t\t\tcard into one button label. See\n\t\t\t\t\t\t\topenspec/changes/board-card-role-and-keyboard.\n\n\t\t\t\t\t\t\tMeasured rather than argued: putting role=\"button\"\n\t\t\t\t\t\t\there makes axe report nested-interactive (serious,\n\t\t\t\t\t\t\t\"Interactive controls must not be nested\") and\n\t\t\t\t\t\t\taria-required-children (critical), and four tests\n\t\t\t\t\t\t\tin tests/a11y/CnBoardView.a11y.spec.js go red.\n\t\t\t\t\t\t\tGate 32 suggests exactly this change. The gate is a\n\t\t\t\t\t\t\tmechanical floor, not the standard.\n\t\t\t\t\t\t-->\n\t\t\t\t\t\t<article\n\t\t\t\t\t\t\tv-for=\"card in column.cards\"\n\t\t\t\t\t\t\t:key=\"`card-${cardKey(card)}`\"\n\t\t\t\t\t\t\tclass=\"cn-board-view__card\"\n\t\t\t\t\t\t\trole=\"listitem\"\n\t\t\t\t\t\t\tdata-testid=\"cn-board-card\"\n\t\t\t\t\t\t\t:data-card-id=\"cardKey(card)\"\n\t\t\t\t\t\t\t:draggable=\"canMove\"\n\t\t\t\t\t\t\t@dragstart=\"onDragStart(card, column, $event)\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-for=\"field in cardFields\"\n\t\t\t\t\t\t\t\t:key=\"`f-${cardKey(card)}-${field}`\"\n\t\t\t\t\t\t\t\tclass=\"cn-board-view__card-field\">\n\t\t\t\t\t\t\t\t{{ card[field] }}\n\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t<!--\n\t\t\t\t\t\t\t\tOpening the card. A native button, so Enter and\n\t\t\t\t\t\t\t\tSpace both work: the old markup handled Enter\n\t\t\t\t\t\t\t\tonly, and Space is what most people try on\n\t\t\t\t\t\t\t\tsomething that looks like a button.\n\t\t\t\t\t\t\t-->\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tclass=\"cn-board-view__card-open\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-board-card-open\"\n\t\t\t\t\t\t\t\t:data-card-id=\"cardKey(card)\"\n\t\t\t\t\t\t\t\t:aria-label=\"cardLabel(card, column)\"\n\t\t\t\t\t\t\t\t@click=\"openCard(card, $event)\"\n\t\t\t\t\t\t\t\t@auxclick=\"onCardAuxClick(card, $event)\">\n\t\t\t\t\t\t\t\t{{ openLabel }}\n\t\t\t\t\t\t\t</button>\n\n\t\t\t\t\t\t\t<!--\n\t\t\t\t\t\t\t\tThe keyboard's way to do what a drag does. A board\n\t\t\t\t\t\t\t\twhose only move is a drag is a board a keyboard\n\t\t\t\t\t\t\t\tuser cannot use at all, and \"drag the card\" is not\n\t\t\t\t\t\t\t\tan instruction you can follow with a keyboard.\n\t\t\t\t\t\t\t-->\n\t\t\t\t\t\t\t<label v-if=\"canMove\" class=\"cn-board-view__move\">\n\t\t\t\t\t\t\t\t<span class=\"cn-board-view__move-label\">{{ moveLabel }}</span>\n\t\t\t\t\t\t\t\t<select\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-board-move\"\n\t\t\t\t\t\t\t\t\t:data-card-id=\"cardKey(card)\"\n\t\t\t\t\t\t\t\t\t:value=\"column.key\"\n\t\t\t\t\t\t\t\t\t@change=\"onMoveTo(card, column, $event)\">\n\t\t\t\t\t\t\t\t\t<option\n\t\t\t\t\t\t\t\t\t\tv-for=\"target in lane.columns\"\n\t\t\t\t\t\t\t\t\t\t:key=\"`t-${cardKey(card)}-${target.key}`\"\n\t\t\t\t\t\t\t\t\t\t:value=\"target.key\">\n\t\t\t\t\t\t\t\t\t\t{{ target.label }}\n\t\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t</article>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<p\n\t\t\t\t\t\tv-else\n\t\t\t\t\t\tclass=\"cn-board-view__empty\"\n\t\t\t\t\t\tdata-testid=\"cn-board-column-empty\">\n\t\t\t\t\t\t{{ emptyColumnText }}\n\t\t\t\t\t</p>\n\t\t\t\t</section>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<p\n\t\t\tv-if=\"refusal\"\n\t\t\tclass=\"cn-board-view__refusal\"\n\t\t\tdata-testid=\"cn-board-refusal\"\n\t\t\trole=\"alert\">\n\t\t\t{{ refusal }}\n\t\t</p>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { buildBoardColumns } from '../../utils/boardColumns.js'\nimport { buildSwimlanes } from '../../utils/boardSwimlanes.js'\nimport { DROP_OUTCOMES, runBoardDrop } from '../../utils/boardTransition.js'\nimport { isRowMiddleClick } from '../../utils/rowAuxClick.js'\n\n/**\n * CnBoardView — the index page's rows as a board, one column per stage.\n *\n * THE COLUMNS ARE THE SCHEMA'S, NOT THE DATA'S. `buildBoardColumns` decides\n * them and is tested on its own; this renders what it returns. A board that\n * built its columns from the values present would lose the stage nothing is\n * in, which is the one you drag a card into.\n *\n * 🔴 A MOVE GOES THROUGH THE HOST'S TRANSITION AND NEVER WRITES THE STATUS\n * FIELD. Writing it here would skip every guard, side effect and audit entry\n * the transition carries, and the board would become a way to move a case past\n * a rule the case page enforces. `runBoardDrop` owns that contract; this\n * component owns the gestures that reach it.\n *\n * 🔴 THERE ARE TWO GESTURES, ALWAYS. A board whose only move is a drag is a\n * board a keyboard user cannot use, and \"drag the card\" is not an instruction\n * anybody can follow with a keyboard. Move to on the card runs the identical\n * call.\n *\n * @event {object} card-click — A card was opened. Payload: the row.\n * @event {object} moved — A card moved. Payload: `{ card, toKey }`.\n * @event {object} refused — A move was refused. Payload: `{ card, message }`.\n * @event {object} stale — The card had already moved. Payload: `{ card }`.\n */\nexport default {\n\tname: 'CnBoardView',\n\n\tprops: {\n\t\t/** The rows the list holds. */\n\t\trows: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** The status field's schema: its enum or its lifecycle states. */\n\t\tstatusFieldSchema: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The property a row's status lives on. */\n\t\tstatusField: {\n\t\t\ttype: String,\n\t\t\tdefault: 'status',\n\t\t},\n\n\t\t/** The fields a card shows. */\n\t\tcardFields: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** A second field to group the board into rows by. */\n\t\tswimlaneField: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The row key, so a card is identifiable. */\n\t\trowKey: {\n\t\t\ttype: String,\n\t\t\tdefault: 'id',\n\t\t},\n\n\t\t/**\n\t\t * The host's transition. Absent, the board is read-only: no drag, no\n\t\t * Move to, because a gesture that cannot do anything is worse than no\n\t\t * gesture at all.\n\t\t */\n\t\trunTransition: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Re-read one card, to check it has not moved under the dragger. */\n\t\treread: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Whether the counts are of one page. A count of a page shown as\n\t\t * though it were the total is a number somebody quotes in a meeting.\n\t\t */\n\t\tpaged: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\t},\n\n\temits: ['card-click', 'moved', 'refused', 'stale'],\n\n\tdata() {\n\t\treturn {\n\t\t\t/** Lanes the reader has collapsed, for as long as they are here. */\n\t\t\tcollapsed: [],\n\t\t\t/** The guard's own words, when it refused the last move. */\n\t\t\trefusal: '',\n\t\t\t/** The card being dragged, and where from. */\n\t\t\tdragging: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {boolean} Whether the status field can back a board at all. */\n\t\tusable() {\n\t\t\treturn this.board.usable\n\t\t},\n\n\t\t/** @return {object} The columns, from the schema. */\n\t\tboard() {\n\t\t\treturn buildBoardColumns({\n\t\t\t\tfield: this.statusFieldSchema,\n\t\t\t\trows: this.rows,\n\t\t\t\tstatusField: this.statusField,\n\t\t\t\toffBoardLabel: t('nextcloud-vue', 'Elsewhere'),\n\t\t\t})\n\t\t},\n\n\t\t/** @return {Array<object>} The board as rows of columns. */\n\t\tlanes() {\n\t\t\treturn buildSwimlanes({\n\t\t\t\tcolumns: this.board.columns,\n\t\t\t\tswimlaneField: this.swimlaneField,\n\t\t\t\tunassignedLabel: t('nextcloud-vue', 'Unassigned'),\n\t\t\t})\n\t\t},\n\n\t\t/** @return {boolean} Whether a move is possible at all. */\n\t\tcanMove() {\n\t\t\treturn typeof this.runTransition === 'function'\n\t\t},\n\n\t\t/** @return {string} What to say when the field cannot back a board. */\n\t\tunusableText() {\n\t\t\treturn t('nextcloud-vue', 'This list has no status field with stages, so it cannot be shown as a board.')\n\t\t},\n\n\t\t/** @return {string} The board's accessible name. */\n\t\tboardLabel() {\n\t\t\treturn t('nextcloud-vue', 'Board')\n\t\t},\n\n\t\t/** @return {string} The Move to control's label. */\n\t\tmoveLabel() {\n\t\t\treturn t('nextcloud-vue', 'Move to')\n\t\t},\n\n\t\t/**\n\t\t * The visible text on a card's opening control.\n\t\t *\n\t\t * Short, because it repeats on every card. Which card it opens is\n\t\t * said by the control's `aria-label`, so a reader hearing a list of\n\t\t * buttons hears the card's own name rather than \"Open\" nine times.\n\t\t *\n\t\t * @return {string} The label.\n\t\t */\n\t\topenLabel() {\n\t\t\treturn t('nextcloud-vue', 'Open')\n\t\t},\n\n\t\t/** @return {string} What an empty column says. */\n\t\temptyColumnText() {\n\t\t\treturn t('nextcloud-vue', 'Nothing here')\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\t/**\n\t\t * Open one card. The pointer and the keyboard both come through here,\n\t\t * so a reader on Enter reaches the same record as a reader on click.\n\t\t *\n\t\t * @param {object} card The card the reader picked.\n\t\t * @param {MouseEvent|KeyboardEvent} [event] The originating event.\n\t\t * @return {void} Nothing.\n\t\t */\n\t\topenCard(card, event) {\n\t\t\t/**\n\t\t\t * @event card-click Emitted when a reader opens a card, by click, Enter or middle click. Payload: `(card, event)` — the card row and the native event, so the host can open it in a new tab on a ctrl/cmd/shift or middle click.\n\t\t\t */\n\t\t\tthis.$emit('card-click', card, event)\n\t\t},\n\n\t\t/**\n\t\t * Middle click on a card's open button: open it like a click would.\n\t\t *\n\t\t * @param {object} card The card the reader picked.\n\t\t * @param {MouseEvent} event The auxclick event.\n\t\t * @return {void} Nothing.\n\t\t */\n\t\tonCardAuxClick(card, event) {\n\t\t\tif (isRowMiddleClick(event)) {\n\t\t\t\tthis.openCard(card, event)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * A card's identity.\n\t\t *\n\t\t * @param {object} card The row.\n\t\t * @return {string} Its key.\n\t\t */\n\t\tcardKey(card) {\n\t\t\treturn String(card?.[this.rowKey] ?? '')\n\t\t},\n\n\t\t/**\n\t\t * A column's count, said as a page count when that is what it is.\n\t\t *\n\t\t * @param {object} column The column.\n\t\t * @return {string} The count.\n\t\t */\n\t\tcountLabel(column) {\n\t\t\treturn this.paged\n\t\t\t\t? t('nextcloud-vue', '{count} on this page', { count: column.count })\n\t\t\t\t: String(column.count)\n\t\t},\n\n\t\t/**\n\t\t * What a card is called to somebody who cannot see which column it is in.\n\t\t *\n\t\t * @param {object} card The row.\n\t\t * @param {object} column Its column.\n\t\t * @return {string} The accessible name.\n\t\t */\n\t\tcardLabel(card, column) {\n\t\t\tconst name = this.cardFields.map((field) => card?.[field]).filter(Boolean).join(', ')\n\t\t\treturn t('nextcloud-vue', '{name}, in {column}', { name: name || this.cardKey(card), column: column.label })\n\t\t},\n\n\t\t/**\n\t\t * Whether a lane is collapsed.\n\t\t *\n\t\t * @param {string} key The lane.\n\t\t * @return {boolean} True when collapsed.\n\t\t */\n\t\tisCollapsed(key) {\n\t\t\treturn this.collapsed.includes(key)\n\t\t},\n\n\t\t/**\n\t\t * Collapse a lane, or open it again.\n\t\t *\n\t\t * Held in the component for as long as the reader is on the view, and\n\t\t * deliberately not persisted: a collapsed lane somebody forgot they\n\t\t * collapsed is work that has gone missing for them.\n\t\t *\n\t\t * @param {string} key The lane.\n\t\t */\n\t\ttoggleLane(key) {\n\t\t\tthis.collapsed = this.isCollapsed(key)\n\t\t\t\t? this.collapsed.filter((entry) => entry !== key)\n\t\t\t\t: [...this.collapsed, key]\n\t\t},\n\n\t\t/**\n\t\t * Remember which card is being dragged and where from.\n\t\t *\n\t\t * @param {object} card The row.\n\t\t * @param {object} column Its column.\n\t\t * @param {DragEvent} event The event.\n\t\t */\n\t\tonDragStart(card, column, event) {\n\t\t\tthis.dragging = { card, fromKey: column.key }\n\t\t\t// Firefox drops a drag that carries no data.\n\t\t\tevent?.dataTransfer?.setData?.('text/plain', this.cardKey(card))\n\t\t},\n\n\t\t/**\n\t\t * A drop on a column.\n\t\t *\n\t\t * @param {object} column The column dropped on.\n\t\t * @return {Promise<void>} Nothing.\n\t\t */\n\t\tasync onDrop(column) {\n\t\t\tif (!this.dragging) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst { card, fromKey } = this.dragging\n\t\t\tthis.dragging = null\n\t\t\tawait this.move(card, fromKey, column.key)\n\t\t},\n\n\t\t/**\n\t\t * Move to, from the card's own menu.\n\t\t *\n\t\t * @param {object} card The row.\n\t\t * @param {object} column Its column.\n\t\t * @param {Event} event The change event.\n\t\t * @return {Promise<void>} Nothing.\n\t\t */\n\t\tasync onMoveTo(card, column, event) {\n\t\t\tawait this.move(card, column.key, event?.target?.value ?? '')\n\t\t},\n\n\t\t/**\n\t\t * The one move, whichever gesture asked for it.\n\t\t *\n\t\t * @param {object} card The row.\n\t\t * @param {string} fromKey Where it was.\n\t\t * @param {string} toKey Where it is going.\n\t\t * @return {Promise<void>} Nothing.\n\t\t */\n\t\tasync move(card, fromKey, toKey) {\n\t\t\tthis.refusal = ''\n\n\t\t\tconst result = await runBoardDrop({\n\t\t\t\tcard,\n\t\t\t\tfromKey,\n\t\t\t\ttoKey,\n\t\t\t\tstatusField: this.statusField,\n\t\t\t\trunTransition: this.runTransition,\n\t\t\t\treread: this.reread,\n\t\t\t})\n\n\t\t\tif (result.outcome === DROP_OUTCOMES.MOVED) {\n\t\t\t\t/**\n\t\t\t\t * @event moved Emitted when the host's transition accepted the move and the card now sits in the new lane. Payload: `{ card, toKey }`.\n\t\t\t\t */\n\t\t\t\tthis.$emit('moved', { card: result.card, toKey })\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (result.outcome === DROP_OUTCOMES.REFUSED) {\n\t\t\t\t// The guard's own sentence when it gave one. Our own only when\n\t\t\t\t// it did not, so the two are never confused.\n\t\t\t\tthis.refusal = result.message || t('nextcloud-vue', 'That move was refused.')\n\t\t\t\t/**\n\t\t\t\t * @event refused Emitted when the guard turned the move down. Payload: `{ card, message }`, the message being the guard's own sentence when it gave one.\n\t\t\t\t */\n\t\t\t\tthis.$emit('refused', { card: result.card, message: this.refusal })\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (result.outcome === DROP_OUTCOMES.STALE) {\n\t\t\t\tthis.refusal = t('nextcloud-vue', 'Somebody else moved this card. It is shown where it is now.')\n\t\t\t\t/**\n\t\t\t\t * @event stale Emitted when somebody else moved the card first, so the board shows it where it is now. Payload: `{ card }`.\n\t\t\t\t */\n\t\t\t\tthis.$emit('stale', { card: result.card })\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped lang=\"scss\">\n.cn-board-view__columns {\n\tdisplay: flex;\n\tgap: 12px;\n\toverflow-x: auto;\n\talign-items: flex-start;\n}\n\n.cn-board-view__column {\n\tflex: 0 0 260px;\n\tbackground-color: var(--color-background-hover);\n\tborder-radius: var(--border-radius-large);\n\tpadding: 8px;\n}\n\n.cn-board-view__column-header {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tfont-size: 1rem;\n\tmargin: 0 0 8px;\n}\n\n.cn-board-view__count,\n.cn-board-view__empty,\n.cn-board-view__unusable {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-board-view__card {\n\tdisplay: block;\n\tbackground-color: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tpadding: 8px;\n\tmargin-bottom: 8px;\n}\n\n.cn-board-view__card-field {\n\tdisplay: block;\n}\n\n/*\n * The card's opening control. The cursor and the focus ring live here now,\n * on the thing that actually does something, rather than on the box.\n */\n.cn-board-view__card-open {\n\tbackground: none;\n\tborder: none;\n\tpadding: 0;\n\tcursor: pointer;\n\tcolor: var(--color-primary-element);\n\tfont-size: 0.8rem;\n}\n\n.cn-board-view__card-open:focus-visible {\n\toutline: 2px solid var(--color-primary-element);\n\toutline-offset: 2px;\n}\n\n.cn-board-view__lane-header {\n\tbackground: none;\n\tborder: none;\n\tfont-weight: bold;\n\tpadding: 8px 0;\n\tcursor: pointer;\n\tcolor: var(--color-main-text);\n}\n\n.cn-board-view__move-label {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.8rem;\n}\n\n.cn-board-view__refusal {\n\tcolor: var(--color-error);\n}\n</style>\n"],"names":["_createElementBlock","_toDisplayString","_Fragment","_renderList","_openBlock","_createElementVNode"],"mappings":"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"}
1
+ {"version":3,"file":"CnBoardView.vue3.js","sources":["../../../../src/components/CnBoardView/CnBoardView.vue?vue&type=template&id=997a7a5e&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n<template>\n\t<div class=\"cn-board-view\" data-testid=\"cn-board-view\">\n\t\t<p v-if=\"!usable\" class=\"cn-board-view__unusable\" data-testid=\"cn-board-unusable\">\n\t\t\t{{ unusableText }}\n\t\t</p>\n\n\t\t<div\n\t\t\tv-for=\"lane in lanes\"\n\t\t\tv-else\n\t\t\t:key=\"`lane-${lane.key}`\"\n\t\t\tclass=\"cn-board-view__lane\"\n\t\t\tdata-testid=\"cn-board-lane\"\n\t\t\t:data-lane=\"lane.key\">\n\t\t\t<button\n\t\t\t\tv-if=\"lane.key !== ''\"\n\t\t\t\tclass=\"cn-board-view__lane-header\"\n\t\t\t\tdata-testid=\"cn-board-lane-header\"\n\t\t\t\t:aria-expanded=\"String(!isCollapsed(lane.key))\"\n\t\t\t\t@click=\"toggleLane(lane.key)\">\n\t\t\t\t{{ lane.label }} ({{ lane.count }})\n\t\t\t</button>\n\n\t\t\t<div\n\t\t\t\tv-show=\"!isCollapsed(lane.key)\"\n\t\t\t\tclass=\"cn-board-view__columns\"\n\t\t\t\trole=\"list\"\n\t\t\t\t:aria-label=\"lane.key === '' ? boardLabel : lane.label\">\n\t\t\t\t<section\n\t\t\t\t\tv-for=\"column in lane.columns\"\n\t\t\t\t\t:key=\"`col-${lane.key}-${column.key}`\"\n\t\t\t\t\tclass=\"cn-board-view__column\"\n\t\t\t\t\trole=\"listitem\"\n\t\t\t\t\tdata-testid=\"cn-board-column\"\n\t\t\t\t\t:data-column=\"column.key\"\n\t\t\t\t\t@dragover.prevent\n\t\t\t\t\t@drop=\"onDrop(column, $event)\">\n\t\t\t\t\t<h3 class=\"cn-board-view__column-header\">\n\t\t\t\t\t\t{{ column.label }}\n\t\t\t\t\t\t<span class=\"cn-board-view__count\" data-testid=\"cn-board-count\">\n\t\t\t\t\t\t\t{{ countLabel(column) }}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</h3>\n\n\t\t\t\t\t<!--\n\t\t\t\t\t\tA list of its own, so a reader is told how many cards\n\t\t\t\t\t\tthis column holds. The column is a listitem of the\n\t\t\t\t\t\tboard's list of columns; the cards are a list inside\n\t\t\t\t\t\tit, not more items of that same list.\n\t\t\t\t\t-->\n\t\t\t\t\t<div\n\t\t\t\t\t\tv-if=\"column.cards.length > 0\"\n\t\t\t\t\t\tclass=\"cn-board-view__cards\"\n\t\t\t\t\t\trole=\"list\"\n\t\t\t\t\t\t:aria-label=\"column.label\">\n\t\t\t\t\t\t<!--\n\t\t\t\t\t\t\tThe card is a CONTAINER, and every action on it is\n\t\t\t\t\t\t\tits own control. It carries the drag and nothing\n\t\t\t\t\t\t\telse: no tabindex, no click, no key handler, and\n\t\t\t\t\t\t\tnever role=\"button\".\n\n\t\t\t\t\t\t\t🔴 role=\"button\" HERE WOULD BREAK THE MOVE. An\n\t\t\t\t\t\t\telement with that role has presentational children,\n\t\t\t\t\t\t\tso the <select> below would be an interactive\n\t\t\t\t\t\t\tcontrol inside a button. That is invalid, and it\n\t\t\t\t\t\t\ttakes away the only way a keyboard user can move a\n\t\t\t\t\t\t\tcard. It would also collapse every field on the\n\t\t\t\t\t\t\tcard into one button label. See\n\t\t\t\t\t\t\topenspec/changes/board-card-role-and-keyboard.\n\n\t\t\t\t\t\t\tMeasured rather than argued: putting role=\"button\"\n\t\t\t\t\t\t\there makes axe report nested-interactive (serious,\n\t\t\t\t\t\t\t\"Interactive controls must not be nested\") and\n\t\t\t\t\t\t\taria-required-children (critical), and four tests\n\t\t\t\t\t\t\tin tests/a11y/CnBoardView.a11y.spec.js go red.\n\t\t\t\t\t\t\tGate 32 suggests exactly this change. The gate is a\n\t\t\t\t\t\t\tmechanical floor, not the standard.\n\t\t\t\t\t\t-->\n\t\t\t\t\t\t<article\n\t\t\t\t\t\t\tv-for=\"card in column.cards\"\n\t\t\t\t\t\t\t:key=\"`card-${cardKey(card)}`\"\n\t\t\t\t\t\t\tclass=\"cn-board-view__card\"\n\t\t\t\t\t\t\trole=\"listitem\"\n\t\t\t\t\t\t\tdata-testid=\"cn-board-card\"\n\t\t\t\t\t\t\t:data-card-id=\"cardKey(card)\"\n\t\t\t\t\t\t\t:draggable=\"canMove\"\n\t\t\t\t\t\t\t@dragstart=\"onDragStart(card, column, $event)\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tv-for=\"field in cardFields\"\n\t\t\t\t\t\t\t\t:key=\"`f-${cardKey(card)}-${field}`\"\n\t\t\t\t\t\t\t\tclass=\"cn-board-view__card-field\">\n\t\t\t\t\t\t\t\t{{ card[field] }}\n\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t<!--\n\t\t\t\t\t\t\t\tOpening the card. A native button, so Enter and\n\t\t\t\t\t\t\t\tSpace both work: the old markup handled Enter\n\t\t\t\t\t\t\t\tonly, and Space is what most people try on\n\t\t\t\t\t\t\t\tsomething that looks like a button.\n\t\t\t\t\t\t\t-->\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tclass=\"cn-board-view__card-open\"\n\t\t\t\t\t\t\t\tdata-testid=\"cn-board-card-open\"\n\t\t\t\t\t\t\t\t:data-card-id=\"cardKey(card)\"\n\t\t\t\t\t\t\t\t:aria-label=\"cardLabel(card, column)\"\n\t\t\t\t\t\t\t\t@click=\"openCard(card, $event)\"\n\t\t\t\t\t\t\t\t@mousedown=\"preventMiddleClickAutoscroll\"\n\t\t\t\t\t\t\t\t@auxclick=\"onCardAuxClick(card, $event)\">\n\t\t\t\t\t\t\t\t{{ openLabel }}\n\t\t\t\t\t\t\t</button>\n\n\t\t\t\t\t\t\t<!--\n\t\t\t\t\t\t\t\tThe keyboard's way to do what a drag does. A board\n\t\t\t\t\t\t\t\twhose only move is a drag is a board a keyboard\n\t\t\t\t\t\t\t\tuser cannot use at all, and \"drag the card\" is not\n\t\t\t\t\t\t\t\tan instruction you can follow with a keyboard.\n\t\t\t\t\t\t\t-->\n\t\t\t\t\t\t\t<label v-if=\"canMove\" class=\"cn-board-view__move\">\n\t\t\t\t\t\t\t\t<span class=\"cn-board-view__move-label\">{{ moveLabel }}</span>\n\t\t\t\t\t\t\t\t<select\n\t\t\t\t\t\t\t\t\tdata-testid=\"cn-board-move\"\n\t\t\t\t\t\t\t\t\t:data-card-id=\"cardKey(card)\"\n\t\t\t\t\t\t\t\t\t:value=\"column.key\"\n\t\t\t\t\t\t\t\t\t@change=\"onMoveTo(card, column, $event)\">\n\t\t\t\t\t\t\t\t\t<option\n\t\t\t\t\t\t\t\t\t\tv-for=\"target in lane.columns\"\n\t\t\t\t\t\t\t\t\t\t:key=\"`t-${cardKey(card)}-${target.key}`\"\n\t\t\t\t\t\t\t\t\t\t:value=\"target.key\">\n\t\t\t\t\t\t\t\t\t\t{{ target.label }}\n\t\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t</article>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<p\n\t\t\t\t\t\tv-else\n\t\t\t\t\t\tclass=\"cn-board-view__empty\"\n\t\t\t\t\t\tdata-testid=\"cn-board-column-empty\">\n\t\t\t\t\t\t{{ emptyColumnText }}\n\t\t\t\t\t</p>\n\t\t\t\t</section>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<p\n\t\t\tv-if=\"refusal\"\n\t\t\tclass=\"cn-board-view__refusal\"\n\t\t\tdata-testid=\"cn-board-refusal\"\n\t\t\trole=\"alert\">\n\t\t\t{{ refusal }}\n\t\t</p>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { buildBoardColumns } from '../../utils/boardColumns.js'\nimport { buildSwimlanes } from '../../utils/boardSwimlanes.js'\nimport { DROP_OUTCOMES, runBoardDrop } from '../../utils/boardTransition.js'\nimport { isRowMiddleClick, preventMiddleClickAutoscroll } from '../../utils/rowAuxClick.js'\n\n/**\n * CnBoardView — the index page's rows as a board, one column per stage.\n *\n * THE COLUMNS ARE THE SCHEMA'S, NOT THE DATA'S. `buildBoardColumns` decides\n * them and is tested on its own; this renders what it returns. A board that\n * built its columns from the values present would lose the stage nothing is\n * in, which is the one you drag a card into.\n *\n * 🔴 A MOVE GOES THROUGH THE HOST'S TRANSITION AND NEVER WRITES THE STATUS\n * FIELD. Writing it here would skip every guard, side effect and audit entry\n * the transition carries, and the board would become a way to move a case past\n * a rule the case page enforces. `runBoardDrop` owns that contract; this\n * component owns the gestures that reach it.\n *\n * 🔴 THERE ARE TWO GESTURES, ALWAYS. A board whose only move is a drag is a\n * board a keyboard user cannot use, and \"drag the card\" is not an instruction\n * anybody can follow with a keyboard. Move to on the card runs the identical\n * call.\n *\n * @event {object} card-click — A card was opened. Payload: the row.\n * @event {object} moved — A card moved. Payload: `{ card, toKey }`.\n * @event {object} refused — A move was refused. Payload: `{ card, message }`.\n * @event {object} stale — The card had already moved. Payload: `{ card }`.\n */\nexport default {\n\tname: 'CnBoardView',\n\n\tprops: {\n\t\t/** The rows the list holds. */\n\t\trows: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** The status field's schema: its enum or its lifecycle states. */\n\t\tstatusFieldSchema: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** The property a row's status lives on. */\n\t\tstatusField: {\n\t\t\ttype: String,\n\t\t\tdefault: 'status',\n\t\t},\n\n\t\t/** The fields a card shows. */\n\t\tcardFields: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** A second field to group the board into rows by. */\n\t\tswimlaneField: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** The row key, so a card is identifiable. */\n\t\trowKey: {\n\t\t\ttype: String,\n\t\t\tdefault: 'id',\n\t\t},\n\n\t\t/**\n\t\t * The host's transition. Absent, the board is read-only: no drag, no\n\t\t * Move to, because a gesture that cannot do anything is worse than no\n\t\t * gesture at all.\n\t\t */\n\t\trunTransition: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Re-read one card, to check it has not moved under the dragger. */\n\t\treread: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Whether the counts are of one page. A count of a page shown as\n\t\t * though it were the total is a number somebody quotes in a meeting.\n\t\t */\n\t\tpaged: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\t},\n\n\temits: ['card-click', 'card-aux-click', 'moved', 'refused', 'stale'],\n\n\tdata() {\n\t\treturn {\n\t\t\t/** Lanes the reader has collapsed, for as long as they are here. */\n\t\t\tcollapsed: [],\n\t\t\t/** The guard's own words, when it refused the last move. */\n\t\t\trefusal: '',\n\t\t\t/** The card being dragged, and where from. */\n\t\t\tdragging: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** @return {boolean} Whether the status field can back a board at all. */\n\t\tusable() {\n\t\t\treturn this.board.usable\n\t\t},\n\n\t\t/** @return {object} The columns, from the schema. */\n\t\tboard() {\n\t\t\treturn buildBoardColumns({\n\t\t\t\tfield: this.statusFieldSchema,\n\t\t\t\trows: this.rows,\n\t\t\t\tstatusField: this.statusField,\n\t\t\t\toffBoardLabel: t('nextcloud-vue', 'Elsewhere'),\n\t\t\t})\n\t\t},\n\n\t\t/** @return {Array<object>} The board as rows of columns. */\n\t\tlanes() {\n\t\t\treturn buildSwimlanes({\n\t\t\t\tcolumns: this.board.columns,\n\t\t\t\tswimlaneField: this.swimlaneField,\n\t\t\t\tunassignedLabel: t('nextcloud-vue', 'Unassigned'),\n\t\t\t})\n\t\t},\n\n\t\t/** @return {boolean} Whether a move is possible at all. */\n\t\tcanMove() {\n\t\t\treturn typeof this.runTransition === 'function'\n\t\t},\n\n\t\t/** @return {string} What to say when the field cannot back a board. */\n\t\tunusableText() {\n\t\t\treturn t('nextcloud-vue', 'This list has no status field with stages, so it cannot be shown as a board.')\n\t\t},\n\n\t\t/** @return {string} The board's accessible name. */\n\t\tboardLabel() {\n\t\t\treturn t('nextcloud-vue', 'Board')\n\t\t},\n\n\t\t/** @return {string} The Move to control's label. */\n\t\tmoveLabel() {\n\t\t\treturn t('nextcloud-vue', 'Move to')\n\t\t},\n\n\t\t/**\n\t\t * The visible text on a card's opening control.\n\t\t *\n\t\t * Short, because it repeats on every card. Which card it opens is\n\t\t * said by the control's `aria-label`, so a reader hearing a list of\n\t\t * buttons hears the card's own name rather than \"Open\" nine times.\n\t\t *\n\t\t * @return {string} The label.\n\t\t */\n\t\topenLabel() {\n\t\t\treturn t('nextcloud-vue', 'Open')\n\t\t},\n\n\t\t/** @return {string} What an empty column says. */\n\t\temptyColumnText() {\n\t\t\treturn t('nextcloud-vue', 'Nothing here')\n\t\t},\n\t},\n\n\tmethods: {\n\t\tt,\n\n\t\tpreventMiddleClickAutoscroll,\n\n\t\t/**\n\t\t * Open one card. The pointer and the keyboard both come through here,\n\t\t * so a reader on Enter reaches the same record as a reader on click.\n\t\t *\n\t\t * @param {object} card The card the reader picked.\n\t\t * @param {MouseEvent|KeyboardEvent} [event] The originating event.\n\t\t * @return {void} Nothing.\n\t\t */\n\t\topenCard(card, event) {\n\t\t\t/**\n\t\t\t * @event card-click Emitted when a reader opens a card, by click or Enter. Payload: `(card, event)` — the card row and the native event, so the host can open it in a new tab on a ctrl/cmd/shift click. A middle click emits `card-aux-click` instead.\n\t\t\t */\n\t\t\tthis.$emit('card-click', card, event)\n\t\t},\n\n\t\t/**\n\t\t * Middle click on a card's open button: emits `card-aux-click`, not\n\t\t * `card-click`, so an existing listener that navigates never moves the\n\t\t * current tab away.\n\t\t *\n\t\t * @param {object} card The card the reader picked.\n\t\t * @param {MouseEvent} event The auxclick event.\n\t\t * @return {void} Nothing.\n\t\t */\n\t\tonCardAuxClick(card, event) {\n\t\t\tif (isRowMiddleClick(event)) {\n\t\t\t\t/**\n\t\t\t\t * @event card-aux-click Emitted when a reader middle-clicks a card, for opening it in a new tab (see `openRowTarget`). Payload: `(card, event)` — the card row and the native auxclick event.\n\t\t\t\t */\n\t\t\t\tthis.$emit('card-aux-click', card, event)\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * A card's identity.\n\t\t *\n\t\t * @param {object} card The row.\n\t\t * @return {string} Its key.\n\t\t */\n\t\tcardKey(card) {\n\t\t\treturn String(card?.[this.rowKey] ?? '')\n\t\t},\n\n\t\t/**\n\t\t * A column's count, said as a page count when that is what it is.\n\t\t *\n\t\t * @param {object} column The column.\n\t\t * @return {string} The count.\n\t\t */\n\t\tcountLabel(column) {\n\t\t\treturn this.paged\n\t\t\t\t? t('nextcloud-vue', '{count} on this page', { count: column.count })\n\t\t\t\t: String(column.count)\n\t\t},\n\n\t\t/**\n\t\t * What a card is called to somebody who cannot see which column it is in.\n\t\t *\n\t\t * @param {object} card The row.\n\t\t * @param {object} column Its column.\n\t\t * @return {string} The accessible name.\n\t\t */\n\t\tcardLabel(card, column) {\n\t\t\tconst name = this.cardFields.map((field) => card?.[field]).filter(Boolean).join(', ')\n\t\t\treturn t('nextcloud-vue', '{name}, in {column}', { name: name || this.cardKey(card), column: column.label })\n\t\t},\n\n\t\t/**\n\t\t * Whether a lane is collapsed.\n\t\t *\n\t\t * @param {string} key The lane.\n\t\t * @return {boolean} True when collapsed.\n\t\t */\n\t\tisCollapsed(key) {\n\t\t\treturn this.collapsed.includes(key)\n\t\t},\n\n\t\t/**\n\t\t * Collapse a lane, or open it again.\n\t\t *\n\t\t * Held in the component for as long as the reader is on the view, and\n\t\t * deliberately not persisted: a collapsed lane somebody forgot they\n\t\t * collapsed is work that has gone missing for them.\n\t\t *\n\t\t * @param {string} key The lane.\n\t\t */\n\t\ttoggleLane(key) {\n\t\t\tthis.collapsed = this.isCollapsed(key)\n\t\t\t\t? this.collapsed.filter((entry) => entry !== key)\n\t\t\t\t: [...this.collapsed, key]\n\t\t},\n\n\t\t/**\n\t\t * Remember which card is being dragged and where from.\n\t\t *\n\t\t * @param {object} card The row.\n\t\t * @param {object} column Its column.\n\t\t * @param {DragEvent} event The event.\n\t\t */\n\t\tonDragStart(card, column, event) {\n\t\t\tthis.dragging = { card, fromKey: column.key }\n\t\t\t// Firefox drops a drag that carries no data.\n\t\t\tevent?.dataTransfer?.setData?.('text/plain', this.cardKey(card))\n\t\t},\n\n\t\t/**\n\t\t * A drop on a column.\n\t\t *\n\t\t * @param {object} column The column dropped on.\n\t\t * @return {Promise<void>} Nothing.\n\t\t */\n\t\tasync onDrop(column) {\n\t\t\tif (!this.dragging) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst { card, fromKey } = this.dragging\n\t\t\tthis.dragging = null\n\t\t\tawait this.move(card, fromKey, column.key)\n\t\t},\n\n\t\t/**\n\t\t * Move to, from the card's own menu.\n\t\t *\n\t\t * @param {object} card The row.\n\t\t * @param {object} column Its column.\n\t\t * @param {Event} event The change event.\n\t\t * @return {Promise<void>} Nothing.\n\t\t */\n\t\tasync onMoveTo(card, column, event) {\n\t\t\tawait this.move(card, column.key, event?.target?.value ?? '')\n\t\t},\n\n\t\t/**\n\t\t * The one move, whichever gesture asked for it.\n\t\t *\n\t\t * @param {object} card The row.\n\t\t * @param {string} fromKey Where it was.\n\t\t * @param {string} toKey Where it is going.\n\t\t * @return {Promise<void>} Nothing.\n\t\t */\n\t\tasync move(card, fromKey, toKey) {\n\t\t\tthis.refusal = ''\n\n\t\t\tconst result = await runBoardDrop({\n\t\t\t\tcard,\n\t\t\t\tfromKey,\n\t\t\t\ttoKey,\n\t\t\t\tstatusField: this.statusField,\n\t\t\t\trunTransition: this.runTransition,\n\t\t\t\treread: this.reread,\n\t\t\t})\n\n\t\t\tif (result.outcome === DROP_OUTCOMES.MOVED) {\n\t\t\t\t/**\n\t\t\t\t * @event moved Emitted when the host's transition accepted the move and the card now sits in the new lane. Payload: `{ card, toKey }`.\n\t\t\t\t */\n\t\t\t\tthis.$emit('moved', { card: result.card, toKey })\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (result.outcome === DROP_OUTCOMES.REFUSED) {\n\t\t\t\t// The guard's own sentence when it gave one. Our own only when\n\t\t\t\t// it did not, so the two are never confused.\n\t\t\t\tthis.refusal = result.message || t('nextcloud-vue', 'That move was refused.')\n\t\t\t\t/**\n\t\t\t\t * @event refused Emitted when the guard turned the move down. Payload: `{ card, message }`, the message being the guard's own sentence when it gave one.\n\t\t\t\t */\n\t\t\t\tthis.$emit('refused', { card: result.card, message: this.refusal })\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (result.outcome === DROP_OUTCOMES.STALE) {\n\t\t\t\tthis.refusal = t('nextcloud-vue', 'Somebody else moved this card. It is shown where it is now.')\n\t\t\t\t/**\n\t\t\t\t * @event stale Emitted when somebody else moved the card first, so the board shows it where it is now. Payload: `{ card }`.\n\t\t\t\t */\n\t\t\t\tthis.$emit('stale', { card: result.card })\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped lang=\"scss\">\n.cn-board-view__columns {\n\tdisplay: flex;\n\tgap: 12px;\n\toverflow-x: auto;\n\talign-items: flex-start;\n}\n\n.cn-board-view__column {\n\tflex: 0 0 260px;\n\tbackground-color: var(--color-background-hover);\n\tborder-radius: var(--border-radius-large);\n\tpadding: 8px;\n}\n\n.cn-board-view__column-header {\n\tdisplay: flex;\n\tjustify-content: space-between;\n\tgap: 8px;\n\tfont-size: 1rem;\n\tmargin: 0 0 8px;\n}\n\n.cn-board-view__count,\n.cn-board-view__empty,\n.cn-board-view__unusable {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-board-view__card {\n\tdisplay: block;\n\tbackground-color: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius);\n\tpadding: 8px;\n\tmargin-bottom: 8px;\n}\n\n.cn-board-view__card-field {\n\tdisplay: block;\n}\n\n/*\n * The card's opening control. The cursor and the focus ring live here now,\n * on the thing that actually does something, rather than on the box.\n */\n.cn-board-view__card-open {\n\tbackground: none;\n\tborder: none;\n\tpadding: 0;\n\tcursor: pointer;\n\tcolor: var(--color-primary-element);\n\tfont-size: 0.8rem;\n}\n\n.cn-board-view__card-open:focus-visible {\n\toutline: 2px solid var(--color-primary-element);\n\toutline-offset: 2px;\n}\n\n.cn-board-view__lane-header {\n\tbackground: none;\n\tborder: none;\n\tfont-weight: bold;\n\tpadding: 8px 0;\n\tcursor: pointer;\n\tcolor: var(--color-main-text);\n}\n\n.cn-board-view__move-label {\n\tcolor: var(--color-text-maxcontrast);\n\tfont-size: 0.8rem;\n}\n\n.cn-board-view__refusal {\n\tcolor: var(--color-error);\n}\n</style>\n"],"names":["_createElementBlock","_toDisplayString","_Fragment","_renderList","_openBlock","_createElementVNode"],"mappings":"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"}
@@ -99,7 +99,7 @@ var script = {
99
99
  },
100
100
  },
101
101
 
102
- emits: ['click', 'select'],
102
+ emits: ['click', 'select', 'aux-click'],
103
103
 
104
104
  computed: {
105
105
  /**
@@ -134,6 +134,14 @@ var script = {
134
134
  const listeners = { select: () => this.toggleSelect(object) };
135
135
  if (!this.selectable || this.clickToView) {
136
136
  listeners.click = (_card, event) => this.$emit('click', object, event);
137
+ // A middle click, kept off `click` so a navigating listener stays put.
138
+ listeners['aux-click'] = (_card, event) => {
139
+ /**
140
+ * @event aux-click Emitted when a navigating card is middle-clicked, for opening it in a new tab (see `openRowTarget`). Payload: `(object, event)` — the card's object and the native auxclick event.
141
+ * @type {object} The card's object.
142
+ */
143
+ this.$emit('aux-click', object, event);
144
+ };
137
145
  }
138
146
  return listeners
139
147
  },
@@ -1 +1 @@
1
- {"version":3,"file":"CnCardGrid.vue2.js","sources":["../../../../src/components/CnCardGrid/CnCardGrid.vue"],"sourcesContent":["<template>\n\t<div class=\"cn-card-grid\">\n\t\t<!-- Loading state -->\n\t\t<div v-if=\"loading\" class=\"cn-card-grid__loading\">\n\t\t\t<NcLoadingIcon :size=\"32\" />\n\t\t</div>\n\n\t\t<!-- Empty state -->\n\t\t<div v-else-if=\"objects.length === 0\" class=\"cn-card-grid__empty\">\n\t\t\t<slot name=\"empty\">\n\t\t\t\t<NcEmptyContent :name=\"resolvedEmptyText\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<ViewGrid :size=\"64\" />\n\t\t\t\t\t</template>\n\t\t\t\t</NcEmptyContent>\n\t\t\t</slot>\n\t\t</div>\n\n\t\t<!-- Card grid -->\n\t\t<div v-else class=\"cn-card-grid__grid\">\n\t\t\t<div\n\t\t\t\tv-for=\"object in objects\"\n\t\t\t\t:key=\"object[rowKey]\"\n\t\t\t\tclass=\"cn-card-grid__item\">\n\t\t\t\t<slot\n\t\t\t\t\tname=\"card\"\n\t\t\t\t\t:object=\"object\"\n\t\t\t\t\t:selected=\"isSelected(object)\"\n\t\t\t\t\t:schema=\"schema\">\n\t\t\t\t\t<CnObjectCard\n\t\t\t\t\t\t:object=\"object\"\n\t\t\t\t\t\t:schema=\"schema\"\n\t\t\t\t\t\t:selectable=\"selectable\"\n\t\t\t\t\t\t:clickToView=\"clickToView\"\n\t\t\t\t\t\t:selected=\"isSelected(object)\"\n\t\t\t\t\t\tv-on=\"cardListeners(object)\">\n\t\t\t\t\t\t<template v-if=\"$slots['card-actions']\" #actions=\"{ object: obj }\">\n\t\t\t\t\t\t\t<slot name=\"card-actions\" :object=\"obj\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template v-if=\"$slots['card-badges']\" #badges=\"{ object: obj }\">\n\t\t\t\t\t\t\t<slot name=\"card-badges\" :object=\"obj\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</CnObjectCard>\n\t\t\t\t</slot>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcEmptyContent, NcLoadingIcon } from '@nextcloud/vue'\nimport ViewGrid from 'vue-material-design-icons/ViewGrid.vue'\nimport { CnObjectCard } from '../CnObjectCard/index.js'\n\n/**\n * CnCardGrid — Responsive grid container for CnObjectCard instances.\n *\n * Displays objects in a responsive CSS grid layout using schema-driven cards.\n * Supports selection, loading/empty states, and custom card rendering via slots.\n *\n * ```vue\n * <CnCardGrid\n * :objects=\"publications\"\n * :schema=\"pubSchema\"\n * :selectable=\"true\"\n * :selected-ids=\"selectedIds\"\n * @click=\"openPublication\"\n * @select=\"onSelect\">\n * <template #card-actions=\"{ object }\">\n * <NcActions><NcActionButton>Edit</NcActionButton></NcActions>\n * </template>\n * </CnCardGrid>\n * ```\n */\nexport default {\n\tname: 'CnCardGrid',\n\n\tcomponents: {\n\t\tNcLoadingIcon,\n\t\tNcEmptyContent,\n\t\tViewGrid,\n\t\tCnObjectCard,\n\t},\n\n\tinject: {\n\t\t/**\n\t\t * Host translate function provided by CnAppRoot as\n\t\t * `cnTranslate: this.translate` (bound to the host app's id). The\n\t\t * manifest-authored empty-state copy is run through it. Defaults to\n\t\t * an identity function so an untranslated key renders as itself.\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/** Array of objects to display as cards */\n\t\tobjects: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Schema definition (passed through to CnObjectCard) */\n\t\tschema: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Whether data is loading */\n\t\tloading: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Whether cards can be selected */\n\t\tselectable: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * When true, a body click on a selectable card emits `click`\n\t\t * (navigation) and selection happens via the checkbox only — the\n\t\t * card-grid counterpart of CnIndexPage's `rowClickToView`.\n\t\t *\n\t\t * @type {boolean}\n\t\t */\n\t\tclickToView: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Array of currently selected object IDs */\n\t\tselectedIds: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Property name used as unique identifier */\n\t\trowKey: {\n\t\t\ttype: String,\n\t\t\tdefault: 'id',\n\t\t},\n\n\t\t/** Text shown when there are no objects */\n\t\temptyText: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'No items found'),\n\t\t},\n\t},\n\n\temits: ['click', 'select'],\n\n\tcomputed: {\n\t\t/**\n\t\t * The empty-state copy run through the host translate function.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tresolvedEmptyText() {\n\t\t\tconst fn = typeof this.cnTranslate === 'function' ? this.cnTranslate : (k) => k\n\t\t\treturn this.emptyText ? fn(this.emptyText) : this.emptyText\n\t\t},\n\t},\n\n\tmethods: {\n\t\tisSelected(object) {\n\t\t\treturn this.selectedIds.includes(object[this.rowKey])\n\t\t},\n\n\t\t/**\n\t\t * Listeners bound on each CnObjectCard. Selectable cards select via\n\t\t * `@select` only; the `@click` (navigation) listener is bound for\n\t\t * non-selectable cards — otherwise CnObjectCard's deprecated\n\t\t * click-to-select path fires (a body click would both select AND\n\t\t * navigate, unlike a table row which only selects) — and for\n\t\t * `clickToView` cards, where a body click navigates and the checkbox\n\t\t * is the only selection surface.\n\t\t *\n\t\t * @param {object} object The card's object.\n\t\t * @return {object} Event listeners for the card.\n\t\t */\n\t\tcardListeners(object) {\n\t\t\tconst listeners = { select: () => this.toggleSelect(object) }\n\t\t\tif (!this.selectable || this.clickToView) {\n\t\t\t\tlisteners.click = (_card, event) => this.$emit('click', object, event)\n\t\t\t}\n\t\t\treturn listeners\n\t\t},\n\n\t\ttoggleSelect(object) {\n\t\t\tconst id = object[this.rowKey]\n\t\t\tconst newIds = this.isSelected(object)\n\t\t\t\t? this.selectedIds.filter((i) => i !== id)\n\t\t\t\t: [...this.selectedIds, id]\n\t\t\tthis.$emit('select', newIds)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-card-grid__grid {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fill, minmax(320px, 1fr));\n\tgap: 16px;\n}\n\n/* A card slot whose component renders nothing (e.g. a custom card that hides\n itself with `v-if` for a client-side filter) leaves an empty grid item that\n would still occupy a grid track and a row gap — pushing the visible cards\n down with a large blank gap above them. Collapse those empty cells so the\n remaining cards start at the top. A comment-only node (`v-if` false) still\n matches `:empty` per the CSS spec, so this catches the v-if-hidden case. */\n.cn-card-grid__item:empty {\n\tdisplay: none;\n}\n\n.cn-card-grid__loading {\n\tdisplay: flex;\n\tjustify-content: center;\n\tpadding: 40px;\n}\n\n.cn-card-grid__empty {\n\tpadding: 40px 20px;\n\ttext-align: center;\n}\n</style>\n"],"names":["CnObjectCard","t"],"mappings":";;;;;AAuDA;;AAEC;AACA;AACA;AACA;AACA;;;AAGA;AACA;;;AAGA;AACA;;;;AAIA;;AAED,aAAe;AACd,CAAA,IAAI,EAAE,YAAY;;CAElB,UAAU,EAAE;EACX,aAAa;AACb,EAAA,cAAc;AACd,EAAA,QAAQ;gBACRA,UAAY;EACZ;;AAED,CAAA,MAAM,EAAE;AACP;AACC;;;;;AAKD,EAAA,WAAW,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,GAAG,KAAK,KAAK;EAC5C;;AAED,CAAA,KAAK,EAAE;AACN;AACA,EAAA,OAAO,EAAE;GACR,IAAI,EAAE,KAAK;AACX,GAAA,OAAO,EAAE,MAAM,EAAE;GACjB;;;AAGD,EAAA,MAAM,EAAE;GACP,IAAI,EAAE,MAAM;GACZ,OAAO,EAAE,IAAI;GACb;;;AAGD,EAAA,OAAO,EAAE;GACR,IAAI,EAAE,OAAO;GACb,OAAO,EAAE,KAAK;GACd;;;EAGD,UAAU,EAAE;GACX,IAAI,EAAE,OAAO;GACb,OAAO,EAAE,KAAK;GACd;;AAED;;;;AAIC;AACA;;EAED,WAAW,EAAE;GACZ,IAAI,EAAE,OAAO;GACb,OAAO,EAAE,KAAK;GACd;;AAED;EACA,WAAW,EAAE;GACZ,IAAI,EAAE,KAAK;AACX,GAAA,OAAO,EAAE,MAAM,EAAE;GACjB;;AAED;AACA,EAAA,MAAM,EAAE;GACP,IAAI,EAAE,MAAM;GACZ,OAAO,EAAE,IAAI;GACb;;AAED;EACA,SAAS,EAAE;GACV,IAAI,EAAE,MAAM;AACZ,GAAA,OAAO,EAAE,MAAMC,SAAC,CAAC,eAAe,EAAE,gBAAgB,CAAC;GACnD;EACD;;CAED,KAAK,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC;;AAE1B,CAAA,QAAQ,EAAE;AACT;;AAEC;AACA;;AAED,EAAA,iBAAiB,GAAG;AACnB,GAAA,MAAM,EAAC,GAAI,OAAO,IAAI,CAAC,WAAU,KAAM,UAAS,GAAI,IAAI,CAAC,WAAU,GAAI,CAAC,CAAC,KAAK,EAAA;GAC9E,OAAO,IAAI,CAAC,YAAY,EAAE,CAAC,IAAI,CAAC,SAAS,CAAE,GAAE,IAAI,CAAC,SAAQ;GAC1D;EACD;;AAED,CAAA,OAAO,EAAE;AACR,EAAA,UAAU,CAAC,MAAM,EAAE;GAClB,OAAO,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;GACpD;;AAED;;;;;;;;AAQC;AACA;AACA;;AAED,EAAA,aAAa,CAAC,MAAM,EAAE;GACrB,MAAM,SAAU,GAAE,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC,YAAY,CAAC,MAAM,CAAE,GAAA;AAC5D,GAAA,IAAI,CAAC,IAAI,CAAC,UAAS,IAAK,IAAI,CAAC,WAAW,EAAE;AACzC,IAAA,SAAS,CAAC,KAAI,GAAI,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAA;AACtE,IAAA;AACA,GAAA,OAAO,SAAQ;GACf;;AAED,EAAA,YAAY,CAAC,MAAM,EAAE;GACpB,MAAM,KAAK,MAAM,CAAC,IAAI,CAAC,MAAM,EAAA;AAC7B,GAAA,MAAM,MAAO,GAAE,IAAI,CAAC,UAAU,CAAC,MAAM,CAAA;AACpC,MAAE,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAE,KAAI,EAAE,CAAA;MACvC,CAAC,GAAG,IAAI,CAAC,WAAW,EAAE,EAAE,EAAA;GAC3B,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAA;GAC3B;EACD;AACF;;;;"}
1
+ {"version":3,"file":"CnCardGrid.vue2.js","sources":["../../../../src/components/CnCardGrid/CnCardGrid.vue"],"sourcesContent":["<template>\n\t<div class=\"cn-card-grid\">\n\t\t<!-- Loading state -->\n\t\t<div v-if=\"loading\" class=\"cn-card-grid__loading\">\n\t\t\t<NcLoadingIcon :size=\"32\" />\n\t\t</div>\n\n\t\t<!-- Empty state -->\n\t\t<div v-else-if=\"objects.length === 0\" class=\"cn-card-grid__empty\">\n\t\t\t<slot name=\"empty\">\n\t\t\t\t<NcEmptyContent :name=\"resolvedEmptyText\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<ViewGrid :size=\"64\" />\n\t\t\t\t\t</template>\n\t\t\t\t</NcEmptyContent>\n\t\t\t</slot>\n\t\t</div>\n\n\t\t<!-- Card grid -->\n\t\t<div v-else class=\"cn-card-grid__grid\">\n\t\t\t<div\n\t\t\t\tv-for=\"object in objects\"\n\t\t\t\t:key=\"object[rowKey]\"\n\t\t\t\tclass=\"cn-card-grid__item\">\n\t\t\t\t<slot\n\t\t\t\t\tname=\"card\"\n\t\t\t\t\t:object=\"object\"\n\t\t\t\t\t:selected=\"isSelected(object)\"\n\t\t\t\t\t:schema=\"schema\">\n\t\t\t\t\t<CnObjectCard\n\t\t\t\t\t\t:object=\"object\"\n\t\t\t\t\t\t:schema=\"schema\"\n\t\t\t\t\t\t:selectable=\"selectable\"\n\t\t\t\t\t\t:clickToView=\"clickToView\"\n\t\t\t\t\t\t:selected=\"isSelected(object)\"\n\t\t\t\t\t\tv-on=\"cardListeners(object)\">\n\t\t\t\t\t\t<template v-if=\"$slots['card-actions']\" #actions=\"{ object: obj }\">\n\t\t\t\t\t\t\t<slot name=\"card-actions\" :object=\"obj\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template v-if=\"$slots['card-badges']\" #badges=\"{ object: obj }\">\n\t\t\t\t\t\t\t<slot name=\"card-badges\" :object=\"obj\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</CnObjectCard>\n\t\t\t\t</slot>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcEmptyContent, NcLoadingIcon } from '@nextcloud/vue'\nimport ViewGrid from 'vue-material-design-icons/ViewGrid.vue'\nimport { CnObjectCard } from '../CnObjectCard/index.js'\n\n/**\n * CnCardGrid — Responsive grid container for CnObjectCard instances.\n *\n * Displays objects in a responsive CSS grid layout using schema-driven cards.\n * Supports selection, loading/empty states, and custom card rendering via slots.\n *\n * ```vue\n * <CnCardGrid\n * :objects=\"publications\"\n * :schema=\"pubSchema\"\n * :selectable=\"true\"\n * :selected-ids=\"selectedIds\"\n * @click=\"openPublication\"\n * @select=\"onSelect\">\n * <template #card-actions=\"{ object }\">\n * <NcActions><NcActionButton>Edit</NcActionButton></NcActions>\n * </template>\n * </CnCardGrid>\n * ```\n */\nexport default {\n\tname: 'CnCardGrid',\n\n\tcomponents: {\n\t\tNcLoadingIcon,\n\t\tNcEmptyContent,\n\t\tViewGrid,\n\t\tCnObjectCard,\n\t},\n\n\tinject: {\n\t\t/**\n\t\t * Host translate function provided by CnAppRoot as\n\t\t * `cnTranslate: this.translate` (bound to the host app's id). The\n\t\t * manifest-authored empty-state copy is run through it. Defaults to\n\t\t * an identity function so an untranslated key renders as itself.\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/** Array of objects to display as cards */\n\t\tobjects: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Schema definition (passed through to CnObjectCard) */\n\t\tschema: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Whether data is loading */\n\t\tloading: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Whether cards can be selected */\n\t\tselectable: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * When true, a body click on a selectable card emits `click`\n\t\t * (navigation) and selection happens via the checkbox only — the\n\t\t * card-grid counterpart of CnIndexPage's `rowClickToView`.\n\t\t *\n\t\t * @type {boolean}\n\t\t */\n\t\tclickToView: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Array of currently selected object IDs */\n\t\tselectedIds: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Property name used as unique identifier */\n\t\trowKey: {\n\t\t\ttype: String,\n\t\t\tdefault: 'id',\n\t\t},\n\n\t\t/** Text shown when there are no objects */\n\t\temptyText: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'No items found'),\n\t\t},\n\t},\n\n\temits: ['click', 'select', 'aux-click'],\n\n\tcomputed: {\n\t\t/**\n\t\t * The empty-state copy run through the host translate function.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tresolvedEmptyText() {\n\t\t\tconst fn = typeof this.cnTranslate === 'function' ? this.cnTranslate : (k) => k\n\t\t\treturn this.emptyText ? fn(this.emptyText) : this.emptyText\n\t\t},\n\t},\n\n\tmethods: {\n\t\tisSelected(object) {\n\t\t\treturn this.selectedIds.includes(object[this.rowKey])\n\t\t},\n\n\t\t/**\n\t\t * Listeners bound on each CnObjectCard. Selectable cards select via\n\t\t * `@select` only; the `@click` (navigation) listener is bound for\n\t\t * non-selectable cards — otherwise CnObjectCard's deprecated\n\t\t * click-to-select path fires (a body click would both select AND\n\t\t * navigate, unlike a table row which only selects) — and for\n\t\t * `clickToView` cards, where a body click navigates and the checkbox\n\t\t * is the only selection surface.\n\t\t *\n\t\t * @param {object} object The card's object.\n\t\t * @return {object} Event listeners for the card.\n\t\t */\n\t\tcardListeners(object) {\n\t\t\tconst listeners = { select: () => this.toggleSelect(object) }\n\t\t\tif (!this.selectable || this.clickToView) {\n\t\t\t\tlisteners.click = (_card, event) => this.$emit('click', object, event)\n\t\t\t\t// A middle click, kept off `click` so a navigating listener stays put.\n\t\t\t\tlisteners['aux-click'] = (_card, event) => {\n\t\t\t\t\t/**\n\t\t\t\t\t * @event aux-click Emitted when a navigating card is middle-clicked, for opening it in a new tab (see `openRowTarget`). Payload: `(object, event)` — the card's object and the native auxclick event.\n\t\t\t\t\t * @type {object} The card's object.\n\t\t\t\t\t */\n\t\t\t\t\tthis.$emit('aux-click', object, event)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn listeners\n\t\t},\n\n\t\ttoggleSelect(object) {\n\t\t\tconst id = object[this.rowKey]\n\t\t\tconst newIds = this.isSelected(object)\n\t\t\t\t? this.selectedIds.filter((i) => i !== id)\n\t\t\t\t: [...this.selectedIds, id]\n\t\t\tthis.$emit('select', newIds)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-card-grid__grid {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fill, minmax(320px, 1fr));\n\tgap: 16px;\n}\n\n/* A card slot whose component renders nothing (e.g. a custom card that hides\n itself with `v-if` for a client-side filter) leaves an empty grid item that\n would still occupy a grid track and a row gap — pushing the visible cards\n down with a large blank gap above them. Collapse those empty cells so the\n remaining cards start at the top. A comment-only node (`v-if` false) still\n matches `:empty` per the CSS spec, so this catches the v-if-hidden case. */\n.cn-card-grid__item:empty {\n\tdisplay: none;\n}\n\n.cn-card-grid__loading {\n\tdisplay: flex;\n\tjustify-content: center;\n\tpadding: 40px;\n}\n\n.cn-card-grid__empty {\n\tpadding: 40px 20px;\n\ttext-align: center;\n}\n</style>\n"],"names":["CnObjectCard","t"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CnCardGrid.vue3.js","sources":["../../../../src/components/CnCardGrid/CnCardGrid.vue?vue&type=template&id=1d1f4122&lang.js"],"sourcesContent":["<template>\n\t<div class=\"cn-card-grid\">\n\t\t<!-- Loading state -->\n\t\t<div v-if=\"loading\" class=\"cn-card-grid__loading\">\n\t\t\t<NcLoadingIcon :size=\"32\" />\n\t\t</div>\n\n\t\t<!-- Empty state -->\n\t\t<div v-else-if=\"objects.length === 0\" class=\"cn-card-grid__empty\">\n\t\t\t<slot name=\"empty\">\n\t\t\t\t<NcEmptyContent :name=\"resolvedEmptyText\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<ViewGrid :size=\"64\" />\n\t\t\t\t\t</template>\n\t\t\t\t</NcEmptyContent>\n\t\t\t</slot>\n\t\t</div>\n\n\t\t<!-- Card grid -->\n\t\t<div v-else class=\"cn-card-grid__grid\">\n\t\t\t<div\n\t\t\t\tv-for=\"object in objects\"\n\t\t\t\t:key=\"object[rowKey]\"\n\t\t\t\tclass=\"cn-card-grid__item\">\n\t\t\t\t<slot\n\t\t\t\t\tname=\"card\"\n\t\t\t\t\t:object=\"object\"\n\t\t\t\t\t:selected=\"isSelected(object)\"\n\t\t\t\t\t:schema=\"schema\">\n\t\t\t\t\t<CnObjectCard\n\t\t\t\t\t\t:object=\"object\"\n\t\t\t\t\t\t:schema=\"schema\"\n\t\t\t\t\t\t:selectable=\"selectable\"\n\t\t\t\t\t\t:clickToView=\"clickToView\"\n\t\t\t\t\t\t:selected=\"isSelected(object)\"\n\t\t\t\t\t\tv-on=\"cardListeners(object)\">\n\t\t\t\t\t\t<template v-if=\"$slots['card-actions']\" #actions=\"{ object: obj }\">\n\t\t\t\t\t\t\t<slot name=\"card-actions\" :object=\"obj\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template v-if=\"$slots['card-badges']\" #badges=\"{ object: obj }\">\n\t\t\t\t\t\t\t<slot name=\"card-badges\" :object=\"obj\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</CnObjectCard>\n\t\t\t\t</slot>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcEmptyContent, NcLoadingIcon } from '@nextcloud/vue'\nimport ViewGrid from 'vue-material-design-icons/ViewGrid.vue'\nimport { CnObjectCard } from '../CnObjectCard/index.js'\n\n/**\n * CnCardGrid — Responsive grid container for CnObjectCard instances.\n *\n * Displays objects in a responsive CSS grid layout using schema-driven cards.\n * Supports selection, loading/empty states, and custom card rendering via slots.\n *\n * ```vue\n * <CnCardGrid\n * :objects=\"publications\"\n * :schema=\"pubSchema\"\n * :selectable=\"true\"\n * :selected-ids=\"selectedIds\"\n * @click=\"openPublication\"\n * @select=\"onSelect\">\n * <template #card-actions=\"{ object }\">\n * <NcActions><NcActionButton>Edit</NcActionButton></NcActions>\n * </template>\n * </CnCardGrid>\n * ```\n */\nexport default {\n\tname: 'CnCardGrid',\n\n\tcomponents: {\n\t\tNcLoadingIcon,\n\t\tNcEmptyContent,\n\t\tViewGrid,\n\t\tCnObjectCard,\n\t},\n\n\tinject: {\n\t\t/**\n\t\t * Host translate function provided by CnAppRoot as\n\t\t * `cnTranslate: this.translate` (bound to the host app's id). The\n\t\t * manifest-authored empty-state copy is run through it. Defaults to\n\t\t * an identity function so an untranslated key renders as itself.\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/** Array of objects to display as cards */\n\t\tobjects: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Schema definition (passed through to CnObjectCard) */\n\t\tschema: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Whether data is loading */\n\t\tloading: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Whether cards can be selected */\n\t\tselectable: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * When true, a body click on a selectable card emits `click`\n\t\t * (navigation) and selection happens via the checkbox only — the\n\t\t * card-grid counterpart of CnIndexPage's `rowClickToView`.\n\t\t *\n\t\t * @type {boolean}\n\t\t */\n\t\tclickToView: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Array of currently selected object IDs */\n\t\tselectedIds: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Property name used as unique identifier */\n\t\trowKey: {\n\t\t\ttype: String,\n\t\t\tdefault: 'id',\n\t\t},\n\n\t\t/** Text shown when there are no objects */\n\t\temptyText: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'No items found'),\n\t\t},\n\t},\n\n\temits: ['click', 'select'],\n\n\tcomputed: {\n\t\t/**\n\t\t * The empty-state copy run through the host translate function.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tresolvedEmptyText() {\n\t\t\tconst fn = typeof this.cnTranslate === 'function' ? this.cnTranslate : (k) => k\n\t\t\treturn this.emptyText ? fn(this.emptyText) : this.emptyText\n\t\t},\n\t},\n\n\tmethods: {\n\t\tisSelected(object) {\n\t\t\treturn this.selectedIds.includes(object[this.rowKey])\n\t\t},\n\n\t\t/**\n\t\t * Listeners bound on each CnObjectCard. Selectable cards select via\n\t\t * `@select` only; the `@click` (navigation) listener is bound for\n\t\t * non-selectable cards — otherwise CnObjectCard's deprecated\n\t\t * click-to-select path fires (a body click would both select AND\n\t\t * navigate, unlike a table row which only selects) — and for\n\t\t * `clickToView` cards, where a body click navigates and the checkbox\n\t\t * is the only selection surface.\n\t\t *\n\t\t * @param {object} object The card's object.\n\t\t * @return {object} Event listeners for the card.\n\t\t */\n\t\tcardListeners(object) {\n\t\t\tconst listeners = { select: () => this.toggleSelect(object) }\n\t\t\tif (!this.selectable || this.clickToView) {\n\t\t\t\tlisteners.click = (_card, event) => this.$emit('click', object, event)\n\t\t\t}\n\t\t\treturn listeners\n\t\t},\n\n\t\ttoggleSelect(object) {\n\t\t\tconst id = object[this.rowKey]\n\t\t\tconst newIds = this.isSelected(object)\n\t\t\t\t? this.selectedIds.filter((i) => i !== id)\n\t\t\t\t: [...this.selectedIds, id]\n\t\t\tthis.$emit('select', newIds)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-card-grid__grid {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fill, minmax(320px, 1fr));\n\tgap: 16px;\n}\n\n/* A card slot whose component renders nothing (e.g. a custom card that hides\n itself with `v-if` for a client-side filter) leaves an empty grid item that\n would still occupy a grid track and a row gap — pushing the visible cards\n down with a large blank gap above them. Collapse those empty cells so the\n remaining cards start at the top. A comment-only node (`v-if` false) still\n matches `:empty` per the CSS spec, so this catches the v-if-hidden case. */\n.cn-card-grid__item:empty {\n\tdisplay: none;\n}\n\n.cn-card-grid__loading {\n\tdisplay: flex;\n\tjustify-content: center;\n\tpadding: 40px;\n}\n\n.cn-card-grid__empty {\n\tpadding: 40px 20px;\n\ttext-align: center;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createVNode","_renderSlot","_Fragment","_renderList","_mergeProps","_toHandlers","_createSlots","_withCtx"],"mappings":";;AACM,MAAA,UAAA,GAAA,EAAA,KAAK,EAAC,cAAc,GAAA;;;AAEJ,EAAA,KAAK,EAAC,uBAAuB;;;;AAKX,EAAA,KAAK,EAAC,qBAAqB;;;;AAWrD,EAAA,KAAK,EAAC,oBAAoB;;;;;;;;;AAlBvC,EAAA,QAAAA,SAAA,EAAA,EAAAC,kBAAA,CA6CK,OA7CL,UA6CK,EAAA;KA3CO,MAAO,CAAA,OAAA;AAAlB,SAAAD,SAAA,EAAA,EAAAC,kBAAA,CAEK,OAFL,UAEK,EAAA;UADJC,WAA2B,CAAA,wBAAA,EAAA,EAAX,IAAI,EAAE,EAAE,EAAA,CAAA;;AAIT,QAAA,CAAA,MAAA,CAAA,OAAO,CAAC,MAAO,KAAA,CAAA;AAA/B,WAAAF,SAAA,EAAA,EAAAC,kBAAA,CAQK,OARL,UAQK,EAAA;AAPJ,YAAAE,UAAA,CAMM,0BANN,MAMM;cALLD,WAIgB,CAAA,yBAAA,EAAA,EAJC,IAAI,EAAE,QAAiB,CAAA,iBAAA,EAAA,EAAA;AAC5B,gBAAA,IAAI,UACd,MAAsB;kBAAtBA,WAAsB,CAAA,mBAAA,EAAA,EAAX,IAAI,EAAE,EAAE,EAAA,CAAA;;;;;;AAOvB,WAAAF,SAAA,EAAA,EAAAC,kBAAA,CA0BK,OA1BL,UA0BK,EAAA;8BAzBJA,kBAwBK,CAAAG,QAAA,EAAA,IAAA,EAAAC,UAAA,CAvBa,MAAO,CAAA,OAAA,EAAA,CAAjB,MAAO,KAAA;mCADfJ,kBAwBK,CAAA,KAAA,EAAA;gBAtBH,GAAG,EAAE,MAAM,CAAC,MAAM,CAAA,MAAA,CAAA;AACnB,gBAAA,KAAK,EAAC,oBAAoB;;gBAC1BE,UAmBM,CAAA,IAAA,CAAA,MAAA,EAAA,MAAA,EAAA;AAjBJ,kBAAA,MAAM,EAAE,MAAM;kBACd,QAAQ,EAAE,QAAU,CAAA,UAAA,CAAC,MAAM,CAAA;AAC3B,kBAAA,MAAM,EAAE,MAAM,CAAA,MAAA;mBAJhB,MAmBM;AAdL,kBAAAD,WAAA,CAac,yBAbdI,UAac,CAAA;AAZZ,oBAAA,MAAM,EAAE,MAAM;AACd,oBAAA,MAAM,EAAE,MAAM,CAAA,MAAA;AACd,oBAAA,UAAU,EAAE,MAAU,CAAA,UAAA;AACtB,oBAAA,WAAW,EAAE,MAAW,CAAA,WAAA;oBACxB,QAAQ,EAAE,QAAU,CAAA,UAAA,CAAC,MAAM,CAAA;qBAC5BC,UAAM,CAAsB,QAAT,CAAA,aAAA,CAAC,MAAM,CAAA,CAAA,CAAA,EAAAC,WAAA,CAAA,EAAA,CAAA,EAAA,CAAA,gBAAA,EAAA;qBACV,IAAM,CAAA,MAAA,CAAA,cAAA,CAAA;;gCAAmB,SAAO;AAC/C,0BAAA,EAAA,EAAAC,OAAA,CAAA,CAAyC,UADkB,GAAI,EAAA,KAAA;4BAC/DN,UAAyC,CAAA,IAAA,CAAA,MAAA,EAAA,cAAA,EAAA,EAAd,MAAM,EAAE,GAAG,EAAA,CAAA;;;;;qBAEvB,IAAM,CAAA,MAAA,CAAA,aAAA,CAAA;;gCAAkB,QAAM;AAC7C,0BAAA,EAAA,EAAAM,OAAA,CAAA,CAAwC,UADiB,GAAI,EAAA,KAAA;4BAC7DN,UAAwC,CAAA,IAAA,CAAA,MAAA,EAAA,aAAA,EAAA,EAAd,MAAM,EAAE,GAAG,EAAA,CAAA;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"CnCardGrid.vue3.js","sources":["../../../../src/components/CnCardGrid/CnCardGrid.vue?vue&type=template&id=1d1f4122&lang.js"],"sourcesContent":["<template>\n\t<div class=\"cn-card-grid\">\n\t\t<!-- Loading state -->\n\t\t<div v-if=\"loading\" class=\"cn-card-grid__loading\">\n\t\t\t<NcLoadingIcon :size=\"32\" />\n\t\t</div>\n\n\t\t<!-- Empty state -->\n\t\t<div v-else-if=\"objects.length === 0\" class=\"cn-card-grid__empty\">\n\t\t\t<slot name=\"empty\">\n\t\t\t\t<NcEmptyContent :name=\"resolvedEmptyText\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<ViewGrid :size=\"64\" />\n\t\t\t\t\t</template>\n\t\t\t\t</NcEmptyContent>\n\t\t\t</slot>\n\t\t</div>\n\n\t\t<!-- Card grid -->\n\t\t<div v-else class=\"cn-card-grid__grid\">\n\t\t\t<div\n\t\t\t\tv-for=\"object in objects\"\n\t\t\t\t:key=\"object[rowKey]\"\n\t\t\t\tclass=\"cn-card-grid__item\">\n\t\t\t\t<slot\n\t\t\t\t\tname=\"card\"\n\t\t\t\t\t:object=\"object\"\n\t\t\t\t\t:selected=\"isSelected(object)\"\n\t\t\t\t\t:schema=\"schema\">\n\t\t\t\t\t<CnObjectCard\n\t\t\t\t\t\t:object=\"object\"\n\t\t\t\t\t\t:schema=\"schema\"\n\t\t\t\t\t\t:selectable=\"selectable\"\n\t\t\t\t\t\t:clickToView=\"clickToView\"\n\t\t\t\t\t\t:selected=\"isSelected(object)\"\n\t\t\t\t\t\tv-on=\"cardListeners(object)\">\n\t\t\t\t\t\t<template v-if=\"$slots['card-actions']\" #actions=\"{ object: obj }\">\n\t\t\t\t\t\t\t<slot name=\"card-actions\" :object=\"obj\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t<template v-if=\"$slots['card-badges']\" #badges=\"{ object: obj }\">\n\t\t\t\t\t\t\t<slot name=\"card-badges\" :object=\"obj\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</CnObjectCard>\n\t\t\t\t</slot>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcEmptyContent, NcLoadingIcon } from '@nextcloud/vue'\nimport ViewGrid from 'vue-material-design-icons/ViewGrid.vue'\nimport { CnObjectCard } from '../CnObjectCard/index.js'\n\n/**\n * CnCardGrid — Responsive grid container for CnObjectCard instances.\n *\n * Displays objects in a responsive CSS grid layout using schema-driven cards.\n * Supports selection, loading/empty states, and custom card rendering via slots.\n *\n * ```vue\n * <CnCardGrid\n * :objects=\"publications\"\n * :schema=\"pubSchema\"\n * :selectable=\"true\"\n * :selected-ids=\"selectedIds\"\n * @click=\"openPublication\"\n * @select=\"onSelect\">\n * <template #card-actions=\"{ object }\">\n * <NcActions><NcActionButton>Edit</NcActionButton></NcActions>\n * </template>\n * </CnCardGrid>\n * ```\n */\nexport default {\n\tname: 'CnCardGrid',\n\n\tcomponents: {\n\t\tNcLoadingIcon,\n\t\tNcEmptyContent,\n\t\tViewGrid,\n\t\tCnObjectCard,\n\t},\n\n\tinject: {\n\t\t/**\n\t\t * Host translate function provided by CnAppRoot as\n\t\t * `cnTranslate: this.translate` (bound to the host app's id). The\n\t\t * manifest-authored empty-state copy is run through it. Defaults to\n\t\t * an identity function so an untranslated key renders as itself.\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/** Array of objects to display as cards */\n\t\tobjects: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Schema definition (passed through to CnObjectCard) */\n\t\tschema: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Whether data is loading */\n\t\tloading: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Whether cards can be selected */\n\t\tselectable: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * When true, a body click on a selectable card emits `click`\n\t\t * (navigation) and selection happens via the checkbox only — the\n\t\t * card-grid counterpart of CnIndexPage's `rowClickToView`.\n\t\t *\n\t\t * @type {boolean}\n\t\t */\n\t\tclickToView: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Array of currently selected object IDs */\n\t\tselectedIds: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Property name used as unique identifier */\n\t\trowKey: {\n\t\t\ttype: String,\n\t\t\tdefault: 'id',\n\t\t},\n\n\t\t/** Text shown when there are no objects */\n\t\temptyText: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'No items found'),\n\t\t},\n\t},\n\n\temits: ['click', 'select', 'aux-click'],\n\n\tcomputed: {\n\t\t/**\n\t\t * The empty-state copy run through the host translate function.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tresolvedEmptyText() {\n\t\t\tconst fn = typeof this.cnTranslate === 'function' ? this.cnTranslate : (k) => k\n\t\t\treturn this.emptyText ? fn(this.emptyText) : this.emptyText\n\t\t},\n\t},\n\n\tmethods: {\n\t\tisSelected(object) {\n\t\t\treturn this.selectedIds.includes(object[this.rowKey])\n\t\t},\n\n\t\t/**\n\t\t * Listeners bound on each CnObjectCard. Selectable cards select via\n\t\t * `@select` only; the `@click` (navigation) listener is bound for\n\t\t * non-selectable cards — otherwise CnObjectCard's deprecated\n\t\t * click-to-select path fires (a body click would both select AND\n\t\t * navigate, unlike a table row which only selects) — and for\n\t\t * `clickToView` cards, where a body click navigates and the checkbox\n\t\t * is the only selection surface.\n\t\t *\n\t\t * @param {object} object The card's object.\n\t\t * @return {object} Event listeners for the card.\n\t\t */\n\t\tcardListeners(object) {\n\t\t\tconst listeners = { select: () => this.toggleSelect(object) }\n\t\t\tif (!this.selectable || this.clickToView) {\n\t\t\t\tlisteners.click = (_card, event) => this.$emit('click', object, event)\n\t\t\t\t// A middle click, kept off `click` so a navigating listener stays put.\n\t\t\t\tlisteners['aux-click'] = (_card, event) => {\n\t\t\t\t\t/**\n\t\t\t\t\t * @event aux-click Emitted when a navigating card is middle-clicked, for opening it in a new tab (see `openRowTarget`). Payload: `(object, event)` — the card's object and the native auxclick event.\n\t\t\t\t\t * @type {object} The card's object.\n\t\t\t\t\t */\n\t\t\t\t\tthis.$emit('aux-click', object, event)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn listeners\n\t\t},\n\n\t\ttoggleSelect(object) {\n\t\t\tconst id = object[this.rowKey]\n\t\t\tconst newIds = this.isSelected(object)\n\t\t\t\t? this.selectedIds.filter((i) => i !== id)\n\t\t\t\t: [...this.selectedIds, id]\n\t\t\tthis.$emit('select', newIds)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-card-grid__grid {\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fill, minmax(320px, 1fr));\n\tgap: 16px;\n}\n\n/* A card slot whose component renders nothing (e.g. a custom card that hides\n itself with `v-if` for a client-side filter) leaves an empty grid item that\n would still occupy a grid track and a row gap — pushing the visible cards\n down with a large blank gap above them. Collapse those empty cells so the\n remaining cards start at the top. A comment-only node (`v-if` false) still\n matches `:empty` per the CSS spec, so this catches the v-if-hidden case. */\n.cn-card-grid__item:empty {\n\tdisplay: none;\n}\n\n.cn-card-grid__loading {\n\tdisplay: flex;\n\tjustify-content: center;\n\tpadding: 40px;\n}\n\n.cn-card-grid__empty {\n\tpadding: 40px 20px;\n\ttext-align: center;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createVNode","_renderSlot","_Fragment","_renderList","_mergeProps","_toHandlers","_createSlots","_withCtx"],"mappings":";;AACM,MAAA,UAAA,GAAA,EAAA,KAAK,EAAC,cAAc,GAAA;;;AAEJ,EAAA,KAAK,EAAC,uBAAuB;;;;AAKX,EAAA,KAAK,EAAC,qBAAqB;;;;AAWrD,EAAA,KAAK,EAAC,oBAAoB;;;;;;;;;AAlBvC,EAAA,QAAAA,SAAA,EAAA,EAAAC,kBAAA,CA6CK,OA7CL,UA6CK,EAAA;KA3CO,MAAO,CAAA,OAAA;AAAlB,SAAAD,SAAA,EAAA,EAAAC,kBAAA,CAEK,OAFL,UAEK,EAAA;UADJC,WAA2B,CAAA,wBAAA,EAAA,EAAX,IAAI,EAAE,EAAE,EAAA,CAAA;;AAIT,QAAA,CAAA,MAAA,CAAA,OAAO,CAAC,MAAO,KAAA,CAAA;AAA/B,WAAAF,SAAA,EAAA,EAAAC,kBAAA,CAQK,OARL,UAQK,EAAA;AAPJ,YAAAE,UAAA,CAMM,0BANN,MAMM;cALLD,WAIgB,CAAA,yBAAA,EAAA,EAJC,IAAI,EAAE,QAAiB,CAAA,iBAAA,EAAA,EAAA;AAC5B,gBAAA,IAAI,UACd,MAAsB;kBAAtBA,WAAsB,CAAA,mBAAA,EAAA,EAAX,IAAI,EAAE,EAAE,EAAA,CAAA;;;;;;AAOvB,WAAAF,SAAA,EAAA,EAAAC,kBAAA,CA0BK,OA1BL,UA0BK,EAAA;8BAzBJA,kBAwBK,CAAAG,QAAA,EAAA,IAAA,EAAAC,UAAA,CAvBa,MAAO,CAAA,OAAA,EAAA,CAAjB,MAAO,KAAA;mCADfJ,kBAwBK,CAAA,KAAA,EAAA;gBAtBH,GAAG,EAAE,MAAM,CAAC,MAAM,CAAA,MAAA,CAAA;AACnB,gBAAA,KAAK,EAAC,oBAAoB;;gBAC1BE,UAmBM,CAAA,IAAA,CAAA,MAAA,EAAA,MAAA,EAAA;AAjBJ,kBAAA,MAAM,EAAE,MAAM;kBACd,QAAQ,EAAE,QAAU,CAAA,UAAA,CAAC,MAAM,CAAA;AAC3B,kBAAA,MAAM,EAAE,MAAM,CAAA,MAAA;mBAJhB,MAmBM;AAdL,kBAAAD,WAAA,CAac,yBAbdI,UAac,CAAA;AAZZ,oBAAA,MAAM,EAAE,MAAM;AACd,oBAAA,MAAM,EAAE,MAAM,CAAA,MAAA;AACd,oBAAA,UAAU,EAAE,MAAU,CAAA,UAAA;AACtB,oBAAA,WAAW,EAAE,MAAW,CAAA,WAAA;oBACxB,QAAQ,EAAE,QAAU,CAAA,UAAA,CAAC,MAAM,CAAA;qBAC5BC,UAAM,CAAsB,QAAT,CAAA,aAAA,CAAC,MAAM,CAAA,CAAA,CAAA,EAAAC,WAAA,CAAA,EAAA,CAAA,EAAA,CAAA,gBAAA,EAAA;qBACV,IAAM,CAAA,MAAA,CAAA,cAAA,CAAA;;gCAAmB,SAAO;AAC/C,0BAAA,EAAA,EAAAC,OAAA,CAAA,CAAyC,UADkB,GAAI,EAAA,KAAA;4BAC/DN,UAAyC,CAAA,IAAA,CAAA,MAAA,EAAA,cAAA,EAAA,EAAd,MAAM,EAAE,GAAG,EAAA,CAAA;;;;;qBAEvB,IAAM,CAAA,MAAA,CAAA,aAAA,CAAA;;gCAAkB,QAAM;AAC7C,0BAAA,EAAA,EAAAM,OAAA,CAAA,CAAwC,UADiB,GAAI,EAAA,KAAA;4BAC7DN,UAAwC,CAAA,IAAA,CAAA,MAAA,EAAA,aAAA,EAAA,EAAd,MAAM,EAAE,GAAG,EAAA,CAAA;;;;;;;;;;;;;;;"}
@@ -1,6 +1,7 @@
1
1
  import { NcActions, NcActionButton, NcActionLink } from '@nextcloud/vue';
2
2
  import { CTX_MENU_DATA_ATTR, CTX_MENU_POPPER_ATTR } from '../../composables/useContextMenu.js';
3
3
  import { resolveItemActionLink, followItemActionLink } from '../../utils/actionLink.js';
4
+ import { isModifiedClick } from '../../utils/linkNavigation.js';
4
5
  import __sfc_main from '../CnIcon/CnIcon.vue.js';
5
6
 
6
7
  /**
@@ -400,13 +401,17 @@ var script = {
400
401
  /**
401
402
  * A link entry was clicked: route a plain in-app click, leave the rest to
402
403
  * the browser, and emit `action` as a button would. The `handler` is not
403
- * called, since navigating is what the link already does.
404
+ * called, since navigating is what the link already does. A modified
405
+ * click opens a new tab, so nothing is emitted in this one.
404
406
  *
405
407
  * @param {object} action The action descriptor.
406
408
  * @param {object} link The resolved link.
407
409
  * @param {MouseEvent} event The click event.
408
410
  */
409
411
  onLinkAction(action, link, event) {
412
+ if (isModifiedClick(event)) {
413
+ return
414
+ }
410
415
  followItemActionLink(event, link, this.$router);
411
416
  this.$emit('action', { action: action.label, row: this.targetItem });
412
417
  },