@conduction/nextcloud-vue 2.56.0-beta.4 → 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 (300) 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/CnBoardView/CnBoardView.vue2.js +12 -5
  12. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
  13. package/dist/esm/components/CnBoardView/CnBoardView.vue3.js +2 -1
  14. package/dist/esm/components/CnBoardView/CnBoardView.vue3.js.map +1 -1
  15. package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js +9 -1
  16. package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js.map +1 -1
  17. package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js.map +1 -1
  18. package/dist/esm/components/CnContextMenu/CnContextMenu.vue2.js +6 -1
  19. package/dist/esm/components/CnContextMenu/CnContextMenu.vue2.js.map +1 -1
  20. package/dist/esm/components/CnContextMenu/CnContextMenu.vue3.js.map +1 -1
  21. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +116 -15
  22. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  23. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +27 -14
  24. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  25. package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue2.js +13 -6
  26. package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue2.js.map +1 -1
  27. package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue3.js +2 -0
  28. package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue3.js.map +1 -1
  29. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +29 -1
  30. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  31. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  32. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js.map +1 -1
  33. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js +1 -2
  34. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js.map +1 -1
  35. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +3 -1
  36. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  37. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +17 -17
  38. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  39. package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue2.js +4 -21
  40. package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue2.js.map +1 -1
  41. package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue3.js.map +1 -1
  42. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js +0 -13
  43. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js.map +1 -1
  44. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue3.js +8 -8
  45. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue3.js.map +1 -1
  46. package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue2.js.map +1 -1
  47. package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue3.js +3 -2
  48. package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue3.js.map +1 -1
  49. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +67 -13
  50. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  51. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +15 -8
  52. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  53. package/dist/esm/components/CnIndexPage/manifestActionDispatch.js +31 -11
  54. package/dist/esm/components/CnIndexPage/manifestActionDispatch.js.map +1 -1
  55. package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue2.js.map +1 -1
  56. package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue3.js +1 -2
  57. package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue3.js.map +1 -1
  58. package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue2.js.map +1 -1
  59. package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue3.js +0 -1
  60. package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue3.js.map +1 -1
  61. package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue2.js +2 -17
  62. package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue2.js.map +1 -1
  63. package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue3.js.map +1 -1
  64. package/dist/esm/components/CnLogsPage/CnLogsPage.vue2.js +35 -11
  65. package/dist/esm/components/CnLogsPage/CnLogsPage.vue2.js.map +1 -1
  66. package/dist/esm/components/CnLogsPage/CnLogsPage.vue3.js +3 -2
  67. package/dist/esm/components/CnLogsPage/CnLogsPage.vue3.js.map +1 -1
  68. package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js +23 -5
  69. package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js.map +1 -1
  70. package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js +1 -1
  71. package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js.map +1 -1
  72. package/dist/esm/components/CnObjectList/CnObjectList.vue2.js +19 -3
  73. package/dist/esm/components/CnObjectList/CnObjectList.vue2.js.map +1 -1
  74. package/dist/esm/components/CnObjectList/CnObjectList.vue3.js.map +1 -1
  75. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +45 -15
  76. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
  77. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +4 -2
  78. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
  79. package/dist/esm/components/CnObjectRow/CnObjectRow.vue2.js +24 -6
  80. package/dist/esm/components/CnObjectRow/CnObjectRow.vue2.js.map +1 -1
  81. package/dist/esm/components/CnObjectRow/CnObjectRow.vue3.js +1 -1
  82. package/dist/esm/components/CnObjectRow/CnObjectRow.vue3.js.map +1 -1
  83. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
  84. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +0 -1
  85. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
  86. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue2.js.map +1 -1
  87. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js +4 -2
  88. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js.map +1 -1
  89. package/dist/esm/components/CnRowActions/CnRowActions.vue2.js +7 -2
  90. package/dist/esm/components/CnRowActions/CnRowActions.vue2.js.map +1 -1
  91. package/dist/esm/components/CnRowActions/CnRowActions.vue3.js.map +1 -1
  92. package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue2.js +5 -3
  93. package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue2.js.map +1 -1
  94. package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue3.js.map +1 -1
  95. package/dist/esm/components/CnTabs/CnTabs.vue2.js.map +1 -1
  96. package/dist/esm/components/CnTabs/CnTabs.vue3.js.map +1 -1
  97. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js +3 -1
  98. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js.map +1 -1
  99. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js +1 -0
  100. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js.map +1 -1
  101. package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue2.js +3 -1
  102. package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue2.js.map +1 -1
  103. package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue3.js.map +1 -1
  104. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue2.js +3 -1
  105. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue2.js.map +1 -1
  106. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue3.js +2 -4
  107. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue3.js.map +1 -1
  108. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue2.js +6 -0
  109. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue2.js.map +1 -1
  110. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue3.js +68 -53
  111. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue3.js.map +1 -1
  112. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue2.js +17 -0
  113. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue2.js.map +1 -1
  114. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue3.js +21 -19
  115. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue3.js.map +1 -1
  116. package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue2.js +3 -1
  117. package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue2.js.map +1 -1
  118. package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue3.js.map +1 -1
  119. package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue2.js +3 -1
  120. package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue2.js.map +1 -1
  121. package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue3.js +2 -4
  122. package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue3.js.map +1 -1
  123. package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue2.js +2 -1
  124. package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue2.js.map +1 -1
  125. package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue3.js.map +1 -1
  126. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue2.js +2 -1
  127. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue2.js.map +1 -1
  128. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue3.js +1 -2
  129. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue3.js.map +1 -1
  130. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js +2 -1
  131. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js.map +1 -1
  132. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue3.js.map +1 -1
  133. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js +2 -1
  134. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js.map +1 -1
  135. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue3.js +1 -2
  136. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue3.js.map +1 -1
  137. package/dist/esm/integrations/builtin/email/CnEmailCard.vue2.js.map +1 -1
  138. package/dist/esm/integrations/builtin/email/CnEmailCard.vue3.js.map +1 -1
  139. package/dist/esm/integrations/builtin/email/CnEmailTab.vue2.js.map +1 -1
  140. package/dist/esm/integrations/builtin/email/CnEmailTab.vue3.js +1 -2
  141. package/dist/esm/integrations/builtin/email/CnEmailTab.vue3.js.map +1 -1
  142. package/dist/esm/integrations/builtin/flow/CnFlowCard.vue2.js +2 -1
  143. package/dist/esm/integrations/builtin/flow/CnFlowCard.vue2.js.map +1 -1
  144. package/dist/esm/integrations/builtin/flow/CnFlowCard.vue3.js.map +1 -1
  145. package/dist/esm/integrations/builtin/flow/CnFlowTab.vue2.js +2 -1
  146. package/dist/esm/integrations/builtin/flow/CnFlowTab.vue2.js.map +1 -1
  147. package/dist/esm/integrations/builtin/flow/CnFlowTab.vue3.js +1 -2
  148. package/dist/esm/integrations/builtin/flow/CnFlowTab.vue3.js.map +1 -1
  149. package/dist/esm/integrations/builtin/forms/CnFormsCard.vue2.js +3 -1
  150. package/dist/esm/integrations/builtin/forms/CnFormsCard.vue2.js.map +1 -1
  151. package/dist/esm/integrations/builtin/forms/CnFormsCard.vue3.js.map +1 -1
  152. package/dist/esm/integrations/builtin/forms/CnFormsTab.vue2.js +3 -1
  153. package/dist/esm/integrations/builtin/forms/CnFormsTab.vue2.js.map +1 -1
  154. package/dist/esm/integrations/builtin/forms/CnFormsTab.vue3.js +2 -4
  155. package/dist/esm/integrations/builtin/forms/CnFormsTab.vue3.js.map +1 -1
  156. package/dist/esm/integrations/builtin/maps/CnMapsCard.vue2.js +3 -1
  157. package/dist/esm/integrations/builtin/maps/CnMapsCard.vue2.js.map +1 -1
  158. package/dist/esm/integrations/builtin/maps/CnMapsCard.vue3.js.map +1 -1
  159. package/dist/esm/integrations/builtin/maps/CnMapsTab.vue2.js +3 -1
  160. package/dist/esm/integrations/builtin/maps/CnMapsTab.vue2.js.map +1 -1
  161. package/dist/esm/integrations/builtin/maps/CnMapsTab.vue3.js +2 -4
  162. package/dist/esm/integrations/builtin/maps/CnMapsTab.vue3.js.map +1 -1
  163. package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue2.js +3 -1
  164. package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue2.js.map +1 -1
  165. package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue3.js.map +1 -1
  166. package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue2.js +3 -1
  167. package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue2.js.map +1 -1
  168. package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue3.js +2 -4
  169. package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue3.js.map +1 -1
  170. package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue2.js +2 -1
  171. package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue2.js.map +1 -1
  172. package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue3.js.map +1 -1
  173. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue2.js +2 -1
  174. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue2.js.map +1 -1
  175. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue3.js +1 -2
  176. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue3.js.map +1 -1
  177. package/dist/esm/integrations/builtin/polls/CnPollsCard.vue2.js +2 -1
  178. package/dist/esm/integrations/builtin/polls/CnPollsCard.vue2.js.map +1 -1
  179. package/dist/esm/integrations/builtin/polls/CnPollsCard.vue3.js.map +1 -1
  180. package/dist/esm/integrations/builtin/polls/CnPollsTab.vue2.js +2 -1
  181. package/dist/esm/integrations/builtin/polls/CnPollsTab.vue2.js.map +1 -1
  182. package/dist/esm/integrations/builtin/polls/CnPollsTab.vue3.js +1 -2
  183. package/dist/esm/integrations/builtin/polls/CnPollsTab.vue3.js.map +1 -1
  184. package/dist/esm/integrations/builtin/shares/CnSharesTab.vue2.js.map +1 -1
  185. package/dist/esm/integrations/builtin/shares/CnSharesTab.vue3.js +3 -6
  186. package/dist/esm/integrations/builtin/shares/CnSharesTab.vue3.js.map +1 -1
  187. package/dist/esm/integrations/builtin/talk/CnTalkCard.vue2.js +3 -1
  188. package/dist/esm/integrations/builtin/talk/CnTalkCard.vue2.js.map +1 -1
  189. package/dist/esm/integrations/builtin/talk/CnTalkCard.vue3.js.map +1 -1
  190. package/dist/esm/integrations/builtin/talk/CnTalkTab.vue2.js +3 -1
  191. package/dist/esm/integrations/builtin/talk/CnTalkTab.vue2.js.map +1 -1
  192. package/dist/esm/integrations/builtin/talk/CnTalkTab.vue3.js +2 -4
  193. package/dist/esm/integrations/builtin/talk/CnTalkTab.vue3.js.map +1 -1
  194. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue2.js +2 -1
  195. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue2.js.map +1 -1
  196. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue3.js.map +1 -1
  197. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue2.js +2 -1
  198. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue2.js.map +1 -1
  199. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue3.js +1 -2
  200. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue3.js.map +1 -1
  201. package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue2.js +4 -1
  202. package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue2.js.map +1 -1
  203. package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue3.js.map +1 -1
  204. package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue2.js +6 -3
  205. package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue2.js.map +1 -1
  206. package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue3.js +2 -4
  207. package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue3.js.map +1 -1
  208. package/dist/esm/l10n/en.json.js +1 -0
  209. package/dist/esm/l10n/en.json.js.map +1 -1
  210. package/dist/esm/l10n/nl.json.js +1 -0
  211. package/dist/esm/l10n/nl.json.js.map +1 -1
  212. package/dist/esm/nextcloud-vue.css +71 -7
  213. package/dist/esm/utils/actionLink.js +5 -0
  214. package/dist/esm/utils/actionLink.js.map +1 -1
  215. package/dist/esm/utils/actionsDispatcher.js +16 -1
  216. package/dist/esm/utils/actionsDispatcher.js.map +1 -1
  217. package/dist/esm/utils/linkNavigation.js +6 -3
  218. package/dist/esm/utils/linkNavigation.js.map +1 -1
  219. package/dist/esm/utils/rowAuxClick.js +30 -5
  220. package/dist/esm/utils/rowAuxClick.js.map +1 -1
  221. package/dist/esm/utils/safeHref.js +18 -3
  222. package/dist/esm/utils/safeHref.js.map +1 -1
  223. package/dist/nextcloud-vue.cjs.js +809 -383
  224. package/dist/nextcloud-vue.cjs.js.map +2 -2
  225. package/dist/nextcloud-vue.css +71 -7
  226. package/l10n/en.json +1 -0
  227. package/l10n/nl.json +1 -0
  228. package/package.json +1 -1
  229. package/src/components/CnActionsBar/CnActionsBar.vue +14 -5
  230. package/src/components/CnActionsMenu/CnActionsMenu.vue +0 -3
  231. package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +1 -2
  232. package/src/components/CnBoardView/CnBoardView.vue +13 -5
  233. package/src/components/CnCardGrid/CnCardGrid.vue +9 -1
  234. package/src/components/CnContextMenu/CnContextMenu.vue +6 -1
  235. package/src/components/CnDataTable/CnDataTable.md +1 -1
  236. package/src/components/CnDataTable/CnDataTable.vue +131 -17
  237. package/src/components/CnDateAxisView/CnDateAxisView.vue +15 -6
  238. package/src/components/CnDetailPage/CnDetailPage.md +1 -1
  239. package/src/components/CnDetailPage/CnDetailPage.vue +29 -1
  240. package/src/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue +1 -2
  241. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +5 -3
  242. package/src/components/CnFilesWidget/CnFilesWidget.vue +4 -21
  243. package/src/components/CnFlowRunsWidget/CnFlowRunsWidget.vue +8 -21
  244. package/src/components/CnFlowsPage/CnFlowsPage.vue +2 -1
  245. package/src/components/CnIndexPage/CnIndexPage.vue +75 -14
  246. package/src/components/CnIndexPage/manifestActionDispatch.js +31 -11
  247. package/src/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue +1 -2
  248. package/src/components/CnInteractionFormWidget/CnInteractionFormWidget.vue +0 -1
  249. package/src/components/CnLinkButtonWidget/CnLinkButtonWidget.vue +2 -17
  250. package/src/components/CnLogsPage/CnLogsPage.vue +37 -12
  251. package/src/components/CnObjectCard/CnObjectCard.vue +24 -6
  252. package/src/components/CnObjectList/CnObjectList.vue +19 -3
  253. package/src/components/CnObjectListWidget/CnObjectListWidget.vue +47 -15
  254. package/src/components/CnObjectRow/CnObjectRow.vue +25 -7
  255. package/src/components/CnObjectSidebar/CnFilesTab.vue +0 -1
  256. package/src/components/CnPageRenderer/CnPageRenderer.vue +2 -0
  257. package/src/components/CnRowActions/CnRowActions.vue +7 -2
  258. package/src/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue +5 -3
  259. package/src/components/CnTabs/CnTabs.vue +7 -3
  260. package/src/components/CnTasksWidget/CnTasksWidget.vue +4 -1
  261. package/src/css/table.css +26 -0
  262. package/src/integrations/builtin/analytics/CnAnalyticsCard.vue +3 -2
  263. package/src/integrations/builtin/analytics/CnAnalyticsTab.vue +5 -6
  264. package/src/integrations/builtin/bookmarks/CnBookmarksCard.vue +12 -1
  265. package/src/integrations/builtin/bookmarks/CnBookmarksTab.vue +23 -6
  266. package/src/integrations/builtin/bookmarks/__tests__/CnBookmarksTab.spec.js +7 -2
  267. package/src/integrations/builtin/collectives/CnCollectivesCard.vue +3 -2
  268. package/src/integrations/builtin/collectives/CnCollectivesTab.vue +5 -6
  269. package/src/integrations/builtin/cospend/CnCospendCard.vue +2 -2
  270. package/src/integrations/builtin/cospend/CnCospendTab.vue +3 -4
  271. package/src/integrations/builtin/deck/CnDeckCard.vue +2 -2
  272. package/src/integrations/builtin/deck/CnDeckTab.vue +3 -4
  273. package/src/integrations/builtin/email/CnEmailCard.vue +6 -2
  274. package/src/integrations/builtin/email/CnEmailTab.vue +7 -4
  275. package/src/integrations/builtin/flow/CnFlowCard.vue +2 -2
  276. package/src/integrations/builtin/flow/CnFlowTab.vue +3 -4
  277. package/src/integrations/builtin/forms/CnFormsCard.vue +3 -2
  278. package/src/integrations/builtin/forms/CnFormsTab.vue +5 -6
  279. package/src/integrations/builtin/maps/CnMapsCard.vue +3 -2
  280. package/src/integrations/builtin/maps/CnMapsTab.vue +5 -6
  281. package/src/integrations/builtin/maps/__tests__/CnMapsTab.spec.js +9 -0
  282. package/src/integrations/builtin/openproject/CnOpenprojectCard.vue +3 -2
  283. package/src/integrations/builtin/openproject/CnOpenprojectTab.vue +5 -6
  284. package/src/integrations/builtin/photos/CnPhotosCard.vue +2 -2
  285. package/src/integrations/builtin/photos/CnPhotosTab.vue +3 -4
  286. package/src/integrations/builtin/polls/CnPollsCard.vue +2 -2
  287. package/src/integrations/builtin/polls/CnPollsTab.vue +3 -4
  288. package/src/integrations/builtin/shares/CnSharesTab.vue +3 -6
  289. package/src/integrations/builtin/talk/CnTalkCard.vue +3 -2
  290. package/src/integrations/builtin/talk/CnTalkTab.vue +5 -6
  291. package/src/integrations/builtin/time-tracker/CnTimeTrackerCard.vue +2 -2
  292. package/src/integrations/builtin/time-tracker/CnTimeTrackerTab.vue +3 -4
  293. package/src/integrations/builtin/xwiki/CnXwikiCard.vue +4 -2
  294. package/src/integrations/builtin/xwiki/CnXwikiTab.vue +7 -7
  295. package/src/integrations/builtin/xwiki/__tests__/CnXwikiTab.spec.js +9 -0
  296. package/src/utils/actionLink.js +5 -0
  297. package/src/utils/actionsDispatcher.js +15 -0
  298. package/src/utils/linkNavigation.js +6 -3
  299. package/src/utils/rowAuxClick.js +29 -4
  300. package/src/utils/safeHref.js +18 -3
@@ -1 +1 @@
1
- {"version":3,"file":"CnContextMenu.vue2.js","sources":["../../../../src/components/CnContextMenu/CnContextMenu.vue"],"sourcesContent":["<template>\n\t<div class=\"cn-context-menu-root\">\n\t\t<NcActions\n\t\t\tv-if=\"!activePanel\"\n\t\t\tref=\"actions\"\n\t\t\tv-model:open=\"internalOpen\"\n\t\t\t:manualOpen=\"true\"\n\t\t\t:forceMenu=\"true\"\n\t\t\tclass=\"cn-context-menu\"\n\t\t\tcontainer=\"body\"\n\t\t\tdata-testid=\"cn-context-menu\"\n\t\t\t@close=\"onClose\"\n\t\t\t@closed=\"onClosed\">\n\t\t\t<!-- Dynamic actions from array prop -->\n\t\t\t<template v-for=\"{ action, link } in renderedActions\" :key=\"action.label\">\n\t\t\t\t<!-- A navigate-only action is a real link, so it can be middle-clicked, opened in a new tab or copied. -->\n\t\t\t\t<NcActionLink\n\t\t\t\t\tv-if=\"link\"\n\t\t\t\t\t:href=\"link.href\"\n\t\t\t\t\t:target=\"link.target\"\n\t\t\t\t\t:title=\"resolveTitle(action)\"\n\t\t\t\t\t:class=\"{ 'cn-row-action--destructive': action.destructive }\"\n\t\t\t\t\t:data-testid=\"`cn-action-item-${slugifyLabel(action.label)}`\"\n\t\t\t\t\tcloseAfterClick\n\t\t\t\t\t@click=\"onLinkAction(action, link, $event)\">\n\t\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t\t<CnIcon v-if=\"typeof action.icon === 'string'\" :name=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t\t<component :is=\"action.icon\" v-else :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ action.label }}\n\t\t\t\t</NcActionLink>\n\t\t\t\t<NcActionButton\n\t\t\t\t\tv-else\n\t\t\t\t\t:title=\"resolveTitle(action)\"\n\t\t\t\t\t:disabled=\"resolveDisabled(action)\"\n\t\t\t\t\t:class=\"{ 'cn-row-action--destructive': action.destructive }\"\n\t\t\t\t\t:data-testid=\"`cn-action-item-${slugifyLabel(action.label)}`\"\n\t\t\t\t\tcloseAfterClick\n\t\t\t\t\t@click=\"onAction(action)\">\n\t\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t\t<CnIcon v-if=\"typeof action.icon === 'string'\" :name=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t\t<component :is=\"action.icon\" v-else :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ action.label }}\n\t\t\t\t</NcActionButton>\n\t\t\t</template>\n\n\t\t\t<!--\n\t\t\t@slot default\n\t\t\t@description Custom NcActionButton-family content rendered inside the\n\t\t\tNcActions menu. Use this for hardcoded buttons (Keepiq pattern) when\n\t\t\tthe `actions` prop is empty.\n\t\t-->\n\t\t\t<slot />\n\t\t</NcActions>\n\n\t\t<!-- CUSTOM PANEL — bypass NcActions, render arbitrary slot content\n\t\t anchored at the cursor. The `#panel:<name>` slot may contain any\n\t\t markup (grids, inputs, custom components) without the NcActions\n\t\t child-allowlist filter. -->\n\t\t<template v-if=\"activePanel && internalOpen\">\n\t\t\t<div\n\t\t\t\tclass=\"cn-context-menu__backdrop\"\n\t\t\t\t@click=\"onClose\"\n\t\t\t\t@contextmenu.prevent=\"onClose\" />\n\t\t\t<div\n\t\t\t\tref=\"panel\"\n\t\t\t\tclass=\"cn-context-menu__panel\"\n\t\t\t\trole=\"menu\"\n\t\t\t\ttabindex=\"-1\"\n\t\t\t\tdata-testid=\"cn-context-menu-panel\"\n\t\t\t\t:data-panel=\"activePanel\"\n\t\t\t\t@keydown.esc.stop=\"onClose\"\n\t\t\t\t@click.stop>\n\t\t\t\t<!-- @slot panel:<name> Free-form custom panel content shown when\n\t\t\t\t `activePanel === '<name>'`. Bypasses the NcActions child\n\t\t\t\t allowlist — put any markup here (grids, inputs, custom\n\t\t\t\t components, etc.). The slot name is dynamic: define one\n\t\t\t\t `#panel:<name>` per panel you want to support. -->\n\t\t\t\t<!-- @binding {Function} back Clear `activePanel`, returning to the default action list. -->\n\t\t\t\t<!-- @binding {Function} close Close the entire menu, equivalent to clicking outside. -->\n\t\t\t\t<!-- @binding {*} targetItem The right-clicked item, forwarded from the `targetItem` prop. -->\n\t\t\t\t<slot\n\t\t\t\t\t:name=\"`panel:${activePanel}`\"\n\t\t\t\t\t:back=\"back\"\n\t\t\t\t\t:close=\"onClose\"\n\t\t\t\t\t:targetItem=\"targetItem\" />\n\t\t\t</div>\n\t\t</template>\n\t</div>\n</template>\n\n<script>\nimport { NcActionButton, NcActionLink, NcActions } from '@nextcloud/vue'\nimport { CTX_MENU_DATA_ATTR, CTX_MENU_POPPER_ATTR } from '../../composables/useContextMenu.js'\nimport { followItemActionLink, resolveItemActionLink } from '../../utils/actionLink.js'\nimport { CnIcon } from '../CnIcon/index.js'\n\n/**\n * CnContextMenu — Right-click context menu wrapper around NcActions.\n *\n * Provides a pre-configured NcActions instance that positions itself at the\n * cursor via the `useContextMenu` composable's CSS custom properties. Accepts\n * an `actions` array for the common dynamic case (like CnRowActions), and a\n * default slot for hardcoded NcActionButton content.\n *\n * Pair with `useContextMenu()` for state management (open/close, target item,\n * cursor positioning). The composable handles the DOM attributes; this component\n * handles the NcActions template boilerplate.\n *\n * Dynamic actions (CnIndexPage pattern)\n * ```vue\n * <CnContextMenu\n * v-model:open=\"contextMenuOpen\"\n * :actions=\"mergedActions\"\n * :target-item=\"contextMenuRow\"\n * @action=\"$emit('action', $event)\"\n * @close=\"closeContextMenu\" />\n * ```\n *\n * Custom buttons via slot (Keepiq pattern)\n * ```vue\n * <CnContextMenu\n * v-model:open=\"contextMenuOpen\"\n * @close=\"closeContextMenu\">\n * <NcActionButton close-after-click @click=\"onRename\">\n * <template #icon><PencilIcon :size=\"20\" /></template>\n * Rename\n * </NcActionButton>\n * </CnContextMenu>\n * ```\n */\nexport default {\n\tname: 'CnContextMenu',\n\n\tcomponents: {\n\t\tNcActions,\n\t\tNcActionButton,\n\t\tNcActionLink,\n\t\tCnIcon,\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Whether the context menu is open. Bind with `v-model:open` — the `.sync`\n\t\t * modifier it replaces was removed in Vue 3 and binds nothing.\n\t\t * Bind to `useContextMenu().isOpen`.\n\t\t */\n\t\topen: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Action definitions rendered as NcActionButton items.\n\t\t * Same format as CnRowActions: `{ label, icon?, handler?, disabled?, visible?, title?, destructive? }`.\n\t\t * `icon` accepts either a component (rendered directly) or a string —\n\t\t * a string is treated as an MDI name and rendered via `CnIcon` (e.g.\n\t\t * `\"Eye\"`), which lets manifest (JSON) actions declare icons by name.\n\t\t * `visible` (boolean | (targetItem) => boolean) hides the entry when falsy\n\t\t * (default: shown). `title` (string | (targetItem) => string) renders as\n\t\t * a native tooltip — useful for explaining why an entry is disabled.\n\t\t * `href` (URL) or `to` (vue-router location), each a value or a\n\t\t * `(targetItem) => …` function, render the entry as a real link, with\n\t\t * `linkTarget` as its `target`; a link emits `action` but does not call\n\t\t * `handler`. When the entire array is empty (or all entries are filtered\n\t\t * out), only the default slot content is rendered.\n\t\t *\n\t\t * @type {Array<{label: string, icon: object | string, handler: (targetItem: object) => void, disabled: boolean | ((targetItem: object) => boolean), visible: boolean | ((targetItem: object) => boolean), title: string | ((targetItem: object) => string), destructive: boolean, href: string | ((targetItem: object) => string), to: string | object | ((targetItem: object) => string | object), linkTarget: string}>}\n\t\t */\n\t\tactions: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * The right-clicked item (row, folder, etc.). Passed to action `handler`\n\t\t * and `disabled` callbacks, and included in the `action` event payload.\n\t\t * Bind to `useContextMenu().targetItem`.\n\t\t */\n\t\ttargetItem: {\n\t\t\ttype: [Object, String, Number],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Name of the currently-active custom panel. When set (and the menu is\n\t\t * open), the default NcActions list is replaced by the matching\n\t\t * `#panel:<name>` slot, rendered free-form (no NcActions child filter).\n\t\t * Bind with `v-model:active-panel` — the component emits `update:activePanel(null)` when\n\t\t * the panel's `back()` binding is invoked or the menu closes.\n\t\t *\n\t\t * @type {string|null}\n\t\t */\n\t\tactivePanel: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: ['action', 'close', 'closed', 'update:activePanel', 'update:open'],\n\n\tdata() {\n\t\treturn {\n\t\t\tinternalOpen: this.open,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Filter actions by their `visible` predicate. Entries without\n\t\t * `visible` are always shown (backwards compatible).\n\t\t *\n\t\t * @return {Array} Visible actions for the current targetItem.\n\t\t */\n\t\tvisibleActions() {\n\t\t\treturn this.actions.filter((action) => {\n\t\t\t\tif (action.visible === undefined) {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tif (typeof action.visible === 'function') {\n\t\t\t\t\treturn !!action.visible(this.targetItem)\n\t\t\t\t}\n\t\t\t\treturn !!action.visible\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Visible actions paired with the link each renders as (null for a button).\n\t\t *\n\t\t * @return {Array<{action: object, link: object|null}>}\n\t\t */\n\t\trenderedActions() {\n\t\t\treturn this.visibleActions.map((action) => ({\n\t\t\t\taction,\n\t\t\t\tlink: this.resolveDisabled(action) ? null : resolveItemActionLink(action, this.targetItem, this.$router),\n\t\t\t}))\n\t\t},\n\t},\n\n\twatch: {\n\t\topen(val) {\n\t\t\tthis.internalOpen = val\n\t\t},\n\n\t\tinternalOpen(val) {\n\t\t\t// Description goes ABOVE `@event`, not inline after it:\n\t\t\t// vue-docgen-api's event-name splitter stops at the first `:`, so\n\t\t\t// `@event update:open <description>` is read as one long event NAME\n\t\t\t// and the generated docs show an empty description.\n\t\t\t/**\n\t\t\t * Fired whenever the menu's internal open state flips. Used by\n\t\t\t * callers that bind `v-model:open` to track visibility.\n\t\t\t *\n\t\t\t * @event update:open\n\t\t\t * @type {boolean}\n\t\t\t */\n\t\t\tthis.$emit('update:open', val)\n\n\t\t\tif (val) {\n\t\t\t\tthis.tagPopper()\n\t\t\t\tthis.startOutsideWatch()\n\t\t\t} else {\n\t\t\t\tthis.stopOutsideWatch()\n\t\t\t\t// NcActions' `@closed` never fires (see `onClosed`), so drop the\n\t\t\t\t// \"a context menu is open\" attribute here. The CSS vars stay put\n\t\t\t\t// — the hide animation still needs the transform, and the next\n\t\t\t\t// `open()` overwrites them before the popper shows again.\n\t\t\t\tdocument.documentElement.removeAttribute(CTX_MENU_DATA_ATTR)\n\t\t\t}\n\t\t},\n\t},\n\n\tmounted() {\n\t\t// NcActions renders its own NcButton as the popover trigger; the menu\n\t\t// opens only via right-click (via `:open.sync`), so the button is\n\t\t// offscreen-positioned (see the `.cn-context-menu` rule below).\n\t\t//\n\t\t// Use `inert` rather than `aria-hidden`: when the menu is dismissed\n\t\t// without picking an item, NcPopover's focus-trap restores focus to the\n\t\t// trigger button. With `aria-hidden` that produces a \"Blocked\n\t\t// aria-hidden on an element because its descendant retained focus\"\n\t\t// warning (focused element must not be hidden from AT). `inert` both\n\t\t// hides the button from the accessibility tree *and* makes it\n\t\t// unfocusable, so the trap's focus restore falls back to <body> instead\n\t\t// of landing on a hidden element. (The button never needs focus — the\n\t\t// menu opens via `:open.sync`, not by focusing the trigger.)\n\t\tconst trigger = this.$el?.querySelector('.action-item__menutoggle')\n\t\tif (trigger) {\n\t\t\ttrigger.setAttribute('inert', '')\n\t\t}\n\n\t\t// Silence NcPopover's dev-mode a11y warning for this specific instance.\n\t\t//\n\t\t// NcPopover (when `window.OC?.debug` is on) calls\n\t\t// `tabbable(triggerContainer)[0]` on every open and warns\n\t\t// \"It looks like you are using a custom button as a <NcPopover>\n\t\t// trigger…\" if nothing tabbable is found. We *are* using NcButton —\n\t\t// the warning's escape hatch — but the button is offscreen-positioned\n\t\t// and tabbable's visibility checks won't reliably find it across\n\t\t// browsers (Chromium's `Element.checkVisibility()` excludes some\n\t\t// offscreen / clipped elements). The advice in the warning (\"bind\n\t\t// #trigger slot attrs\") doesn't apply: NcActions controls its own\n\t\t// trigger and doesn't expose a `#trigger` slot to override.\n\t\t//\n\t\t// Neutralising the check on our own NcPopover instance (one level\n\t\t// down inside NcActions) leaves every other popover untouched and\n\t\t// keeps the check live for real custom triggers elsewhere.\n\t\t//\n\t\t// Vue 3: `this.$children` was removed (it hard-errors under @vue/compat\n\t\t// MODE 3 with \"INSTANCE_CHILDREN compat has been disabled\"), so reach the\n\t\t// NcActions instance through an explicit template ref instead.\n\t\tconst ncActions = this.$refs.actions\n\t\tconst ncPopover = ncActions?.$refs?.popover\n\t\tif (ncPopover) {\n\t\t\tncPopover.checkTriggerA11y = () => {}\n\t\t}\n\n\t\tthis.tagPopper()\n\n\t\t// Mounting with `open` already true never trips the watcher.\n\t\tif (this.internalOpen) {\n\t\t\tthis.startOutsideWatch()\n\t\t}\n\t},\n\n\tbeforeUnmount() {\n\t\tthis.stopOutsideWatch()\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Mark this menu's own popper element so the cursor-positioning CSS in\n\t\t * `src/css/context-menu.css` can target it — and nothing else.\n\t\t *\n\t\t * That CSS used to key on the `<html>` data attribute alone, which\n\t\t * matches *every* `.v-popper__popper` in the document: any other popover\n\t\t * alive while a context menu was open (a table row's actions menu, most\n\t\t * visibly) got dragged to the last right-click coordinates.\n\t\t *\n\t\t * The marker has to be applied imperatively — NcActions hardcodes\n\t\t * `popoverBaseClass: 'action-item__popper'` on its NcPopover and spreads\n\t\t * no `$attrs` onto it, so there is no prop to pass one in through.\n\t\t *\n\t\t * It must be an *attribute*, not a class. floating-vue binds a dynamic\n\t\t * `class` on this element (`--shown` / `--hidden` / `--show-from` / …)\n\t\t * and Vue's `patchClass` assigns `el.className` wholesale, so a class\n\t\t * added here is wiped the first time the popper opens — the rule then\n\t\t * matches nothing and the menu renders at its offscreen trigger. Vue\n\t\t * only patches props present in the vnode, so an attribute it never\n\t\t * renders is left alone.\n\t\t *\n\t\t * The element itself is stable: floating-vue keeps one `$_popperNode`\n\t\t * and `appendChild`s it into the container on first show, so marking it\n\t\t * once at `mounted()` holds for the component's lifetime. Re-running on\n\t\t * open is a cheap, idempotent guard for a not-yet-ready ref chain.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\ttagPopper() {\n\t\t\tconst el = this.$refs.actions?.$refs?.popover?.getPopoverContentElement?.()\n\t\t\tel?.setAttribute?.(CTX_MENU_POPPER_ATTR, '')\n\t\t},\n\n\t\t/**\n\t\t * Start watching for a press outside the menu. Driven from both the\n\t\t * `internalOpen` watcher and `mounted()`, because a consumer may mount\n\t\t * with `open` already true — in which case there is no state transition\n\t\t * for the watcher to see.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tstartOutsideWatch() {\n\t\t\t// nextTick: the contextmenu/click that opened the menu is still\n\t\t\t// propagating and would dismiss it again on the same gesture.\n\t\t\tthis.$nextTick(() => {\n\t\t\t\tif (this.internalOpen) {\n\t\t\t\t\t// CAPTURE. A canvas that swallows its own mousedown never lets it\n\t\t\t\t\t// bubble to document, so a menu opened over one could not be\n\t\t\t\t\t// dismissed by clicking the canvas — Vue Flow's d3-zoom pane calls\n\t\t\t\t\t// stopImmediatePropagation, which is exactly that shape.\n\t\t\t\t\tdocument.addEventListener('mousedown', this.onDocumentMouseDown, true)\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Stop watching for outside presses.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tstopOutsideWatch() {\n\t\t\tdocument.removeEventListener('mousedown', this.onDocumentMouseDown, true)\n\t\t},\n\n\t\t/**\n\t\t * Close the menu when the press lands outside it.\n\t\t *\n\t\t * NcActions has to run in `manual-open` mode here (the menu is opened\n\t\t * from a right-click via the `open` prop, not by activating the trigger\n\t\t * button). `@nextcloud/vue` 9 couples that mode to\n\t\t * `noCloseOnClickOutside: this.manualOpen`, and NcPopover derives\n\t\t * `autoHide: !noCloseOnClickOutside && closeOnClickOutside` — so the\n\t\t * popper's own outside-click dismissal is switched off and nothing else\n\t\t * closes the menu. v8 only cleared the popper's open `triggers`, leaving\n\t\t * autoHide intact, which is why dismissing worked before the Vue 3\n\t\t * upgrade. NcActions spreads no `$attrs` onto NcPopover, so the prop\n\t\t * cannot be overridden from outside — hence handling it here.\n\t\t *\n\t\t * @param {MouseEvent} event The document mousedown.\n\t\t * @return {void}\n\t\t */\n\t\tonDocumentMouseDown(event) {\n\t\t\tif (!this.internalOpen || this.isInsideMenu(event.target)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.onClose()\n\t\t},\n\n\t\t/**\n\t\t * Whether a node sits inside the menu's own surface — the custom panel,\n\t\t * or the popper NcActions teleports to `container=\"body\"` (so it is not\n\t\t * inside `this.$el` and `contains()` on the root would miss it).\n\t\t *\n\t\t * @param {EventTarget} target The event target to test.\n\t\t * @return {boolean} True when the target belongs to this menu.\n\t\t */\n\t\tisInsideMenu(target) {\n\t\t\tif (!(target instanceof Node)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif (this.$refs.panel?.contains(target)) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tconst content = this.$refs.actions?.$refs?.popover?.getPopoverContentElement?.()\n\t\t\tif (content?.contains(target)) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\t// Fallback for when the popover ref chain is unavailable —\n\t\t\t// `action-item__popper` is the base class NcActions gives its popper.\n\t\t\treturn target instanceof Element && !!target.closest('.action-item__popper')\n\t\t},\n\n\t\tresolveDisabled(action) {\n\t\t\tif (typeof action.disabled === 'function') {\n\t\t\t\treturn action.disabled(this.targetItem)\n\t\t\t}\n\t\t\treturn !!action.disabled\n\t\t},\n\n\t\t/**\n\t\t * Resolve the `title` field on an action descriptor — supports both\n\t\t * a static string and a function `(targetItem) => string`. Returns\n\t\t * undefined when no title is provided so the attribute isn't rendered.\n\t\t *\n\t\t * @param {object} action The action descriptor.\n\t\t * @return {string|undefined} The tooltip text, or undefined.\n\t\t */\n\t\tresolveTitle(action) {\n\t\t\tif (typeof action.title === 'function') {\n\t\t\t\treturn action.title(this.targetItem) || undefined\n\t\t\t}\n\t\t\treturn action.title || undefined\n\t\t},\n\n\t\t/**\n\t\t * Slugify an action label for use in stable `data-testid` selectors.\n\t\t * Lowercase, kebab-case, strip non-alphanumeric. Used solely by the\n\t\t * `:data-testid` binding on NcActionButton — does not affect runtime\n\t\t * behaviour or rendered text.\n\t\t *\n\t\t * @param {string} label The action's display label.\n\t\t * @return {string} kebab-case slug suitable for a testid suffix.\n\t\t */\n\t\tslugifyLabel(label) {\n\t\t\treturn String(label || '')\n\t\t\t\t.toLowerCase()\n\t\t\t\t.replace(/[^a-z0-9]+/g, '-')\n\t\t\t\t.replace(/^-+|-+$/g, '')\n\t\t},\n\n\t\tonAction(action) {\n\t\t\tif (action.handler && typeof action.handler === 'function') {\n\t\t\t\taction.handler(this.targetItem)\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event action User picked an entry from the menu. The action's own `handler(targetItem)` (when present) ran synchronously before this event fires; the event lets parents observe / log the choice.\n\t\t\t * @type {{ action: string, row: object|null }}\n\t\t\t */\n\t\t\tthis.$emit('action', { action: action.label, row: this.targetItem })\n\t\t},\n\n\t\t/**\n\t\t * A link entry was clicked: route a plain in-app click, leave the rest to\n\t\t * the browser, and emit `action` as a button would. The `handler` is not\n\t\t * called, since navigating is what the link already does.\n\t\t *\n\t\t * @param {object} action The action descriptor.\n\t\t * @param {object} link The resolved link.\n\t\t * @param {MouseEvent} event The click event.\n\t\t */\n\t\tonLinkAction(action, link, event) {\n\t\t\tfollowItemActionLink(event, link, this.$router)\n\t\t\tthis.$emit('action', { action: action.label, row: this.targetItem })\n\t\t},\n\n\t\tonClose() {\n\t\t\tthis.internalOpen = false\n\t\t\tif (this.activePanel) {\n\t\t\t\tthis.$emit('update:activePanel', null)\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event close Fired when the menu starts closing (before the popper's hide animation). This is the reliable one — prefer it over `closed`.\n\t\t\t */\n\t\t\tthis.$emit('close')\n\t\t},\n\n\t\t/**\n\t\t * Clear the active panel and return to the default action list.\n\t\t * Exposed to custom panel slots via the `back` scope binding.\n\t\t */\n\t\tback() {\n\t\t\t// Description above the tag — see the note on `update:open`.\n\t\t\t/**\n\t\t\t * Emitted with `null` to clear the active panel — fired by the\n\t\t\t * panel's `back()` binding and whenever the menu closes while a\n\t\t\t * panel is open. Bind with `v-model:active-panel`.\n\t\t\t *\n\t\t\t * @event update:activePanel\n\t\t\t * @type {null}\n\t\t\t */\n\t\t\tthis.$emit('update:activePanel', null)\n\t\t},\n\n\t\t/**\n\t\t * Fires after the popper's hide animation completes.\n\t\t *\n\t\t * IT MUST NOT CLEAR THE CURSOR POSITION, and it used to. This fires LATE\n\t\t * — a whole hide animation late — so a second click on the same target\n\t\t * reopens the menu before it arrives, and the tear-down then strips the\n\t\t * coordinates that reopen had just written. The transform resolves\n\t\t * against undefined vars and the menu lands in the corner, on every open\n\t\t * after the first.\n\t\t *\n\t\t * It was dead code when written: `@nextcloud/vue` 9 bound `onAfterClose`\n\t\t * against an NcPopover that only emitted `afterHide`. 9.11.0 binds\n\t\t * `onAfterHide`, so it runs now, and a tear-down nothing needed became a\n\t\t * bug. Nothing needs it still: positioning is scoped to this menu's own\n\t\t * popper (see `tagPopper`), the data attribute is dropped in the\n\t\t * `internalOpen` watcher, the next `open()` overwrites the vars, and the\n\t\t * composable's unmount hook clears them for good.\n\t\t */\n\t\tonClosed() {\n\t\t\t/**\n\t\t\t * @event closed Intended to fire after the popper's hide animation completes. Does not currently fire — `@nextcloud/vue` 9's NcActions listens for an `afterClose` event NcPopover never emits. Use `close` instead.\n\t\t\t */\n\t\t\tthis.$emit('closed')\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n/* Selector is deliberately two classes deep. `.cn-context-menu` lands on\n NcActions' root, which NcActions itself styles as `.action-item[data-v-…]\n { position: relative; display: inline-block }`. Scoped CSS appends the attr\n to the last compound only, so a bare `.cn-context-menu` tied that rule at\n specificity (0,2,0) and lost on injection order — the trigger stayed\n `position: relative`, so it kept its in-flow box (top/left just offset it\n visually) and showed up as a full-width ~34px-tall phantom row between the\n table and the pagination on CnIndexPage. Adding the parent class makes this\n (0,3,0) so it wins regardless of order. */\n.cn-context-menu-root > .cn-context-menu {\n\t/* Hide the NcActions trigger button — menu opens only via right-click.\n\t Off-screen rather than display:none / visibility:hidden so NcPopover's\n\t a11y check (`tabbable(triggerContainer)[0]`) still finds the NcButton\n\t and the \"custom button as a <NcPopover> trigger\" warning stays quiet.\n\t Avoid `clip` / `clip-path` here — modern browsers' `Element.checkVisibility()`,\n\t which tabbable calls into, treats those as invisible. Pure offscreen\n\t positioning leaves the element fully \"visible\" to layout while being\n\t off the user's screen. */\n\tposition: absolute;\n\ttop: -9999px;\n\tleft: -9999px;\n\topacity: 0;\n\tpointer-events: none;\n}\n\n.cn-context-menu__backdrop {\n\tposition: fixed;\n\tinset: 0;\n\tz-index: 9998;\n}\n\n.cn-context-menu__panel {\n\tposition: fixed;\n\ttop: var(--cn-ctx-menu-y, 0);\n\tleft: var(--cn-ctx-menu-x, 0);\n\tz-index: 9999;\n\tmin-width: 220px;\n\tpadding: 4px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-large, 12px);\n\tbox-shadow: 0 1px 5px rgba(0, 0, 0, 0.2);\n\toutline: none;\n}\n</style>\n"],"names":["CnIcon"],"mappings":"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"}
1
+ {"version":3,"file":"CnContextMenu.vue2.js","sources":["../../../../src/components/CnContextMenu/CnContextMenu.vue"],"sourcesContent":["<template>\n\t<div class=\"cn-context-menu-root\">\n\t\t<NcActions\n\t\t\tv-if=\"!activePanel\"\n\t\t\tref=\"actions\"\n\t\t\tv-model:open=\"internalOpen\"\n\t\t\t:manualOpen=\"true\"\n\t\t\t:forceMenu=\"true\"\n\t\t\tclass=\"cn-context-menu\"\n\t\t\tcontainer=\"body\"\n\t\t\tdata-testid=\"cn-context-menu\"\n\t\t\t@close=\"onClose\"\n\t\t\t@closed=\"onClosed\">\n\t\t\t<!-- Dynamic actions from array prop -->\n\t\t\t<template v-for=\"{ action, link } in renderedActions\" :key=\"action.label\">\n\t\t\t\t<!-- A navigate-only action is a real link, so it can be middle-clicked, opened in a new tab or copied. -->\n\t\t\t\t<NcActionLink\n\t\t\t\t\tv-if=\"link\"\n\t\t\t\t\t:href=\"link.href\"\n\t\t\t\t\t:target=\"link.target\"\n\t\t\t\t\t:title=\"resolveTitle(action)\"\n\t\t\t\t\t:class=\"{ 'cn-row-action--destructive': action.destructive }\"\n\t\t\t\t\t:data-testid=\"`cn-action-item-${slugifyLabel(action.label)}`\"\n\t\t\t\t\tcloseAfterClick\n\t\t\t\t\t@click=\"onLinkAction(action, link, $event)\">\n\t\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t\t<CnIcon v-if=\"typeof action.icon === 'string'\" :name=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t\t<component :is=\"action.icon\" v-else :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ action.label }}\n\t\t\t\t</NcActionLink>\n\t\t\t\t<NcActionButton\n\t\t\t\t\tv-else\n\t\t\t\t\t:title=\"resolveTitle(action)\"\n\t\t\t\t\t:disabled=\"resolveDisabled(action)\"\n\t\t\t\t\t:class=\"{ 'cn-row-action--destructive': action.destructive }\"\n\t\t\t\t\t:data-testid=\"`cn-action-item-${slugifyLabel(action.label)}`\"\n\t\t\t\t\tcloseAfterClick\n\t\t\t\t\t@click=\"onAction(action)\">\n\t\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t\t<CnIcon v-if=\"typeof action.icon === 'string'\" :name=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t\t<component :is=\"action.icon\" v-else :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ action.label }}\n\t\t\t\t</NcActionButton>\n\t\t\t</template>\n\n\t\t\t<!--\n\t\t\t@slot default\n\t\t\t@description Custom NcActionButton-family content rendered inside the\n\t\t\tNcActions menu. Use this for hardcoded buttons (Keepiq pattern) when\n\t\t\tthe `actions` prop is empty.\n\t\t-->\n\t\t\t<slot />\n\t\t</NcActions>\n\n\t\t<!-- CUSTOM PANEL — bypass NcActions, render arbitrary slot content\n\t\t anchored at the cursor. The `#panel:<name>` slot may contain any\n\t\t markup (grids, inputs, custom components) without the NcActions\n\t\t child-allowlist filter. -->\n\t\t<template v-if=\"activePanel && internalOpen\">\n\t\t\t<div\n\t\t\t\tclass=\"cn-context-menu__backdrop\"\n\t\t\t\t@click=\"onClose\"\n\t\t\t\t@contextmenu.prevent=\"onClose\" />\n\t\t\t<div\n\t\t\t\tref=\"panel\"\n\t\t\t\tclass=\"cn-context-menu__panel\"\n\t\t\t\trole=\"menu\"\n\t\t\t\ttabindex=\"-1\"\n\t\t\t\tdata-testid=\"cn-context-menu-panel\"\n\t\t\t\t:data-panel=\"activePanel\"\n\t\t\t\t@keydown.esc.stop=\"onClose\"\n\t\t\t\t@click.stop>\n\t\t\t\t<!-- @slot panel:<name> Free-form custom panel content shown when\n\t\t\t\t `activePanel === '<name>'`. Bypasses the NcActions child\n\t\t\t\t allowlist — put any markup here (grids, inputs, custom\n\t\t\t\t components, etc.). The slot name is dynamic: define one\n\t\t\t\t `#panel:<name>` per panel you want to support. -->\n\t\t\t\t<!-- @binding {Function} back Clear `activePanel`, returning to the default action list. -->\n\t\t\t\t<!-- @binding {Function} close Close the entire menu, equivalent to clicking outside. -->\n\t\t\t\t<!-- @binding {*} targetItem The right-clicked item, forwarded from the `targetItem` prop. -->\n\t\t\t\t<slot\n\t\t\t\t\t:name=\"`panel:${activePanel}`\"\n\t\t\t\t\t:back=\"back\"\n\t\t\t\t\t:close=\"onClose\"\n\t\t\t\t\t:targetItem=\"targetItem\" />\n\t\t\t</div>\n\t\t</template>\n\t</div>\n</template>\n\n<script>\nimport { NcActionButton, NcActionLink, NcActions } from '@nextcloud/vue'\nimport { CTX_MENU_DATA_ATTR, CTX_MENU_POPPER_ATTR } from '../../composables/useContextMenu.js'\nimport { followItemActionLink, resolveItemActionLink } from '../../utils/actionLink.js'\nimport { isModifiedClick } from '../../utils/linkNavigation.js'\nimport { CnIcon } from '../CnIcon/index.js'\n\n/**\n * CnContextMenu — Right-click context menu wrapper around NcActions.\n *\n * Provides a pre-configured NcActions instance that positions itself at the\n * cursor via the `useContextMenu` composable's CSS custom properties. Accepts\n * an `actions` array for the common dynamic case (like CnRowActions), and a\n * default slot for hardcoded NcActionButton content.\n *\n * Pair with `useContextMenu()` for state management (open/close, target item,\n * cursor positioning). The composable handles the DOM attributes; this component\n * handles the NcActions template boilerplate.\n *\n * Dynamic actions (CnIndexPage pattern)\n * ```vue\n * <CnContextMenu\n * v-model:open=\"contextMenuOpen\"\n * :actions=\"mergedActions\"\n * :target-item=\"contextMenuRow\"\n * @action=\"$emit('action', $event)\"\n * @close=\"closeContextMenu\" />\n * ```\n *\n * Custom buttons via slot (Keepiq pattern)\n * ```vue\n * <CnContextMenu\n * v-model:open=\"contextMenuOpen\"\n * @close=\"closeContextMenu\">\n * <NcActionButton close-after-click @click=\"onRename\">\n * <template #icon><PencilIcon :size=\"20\" /></template>\n * Rename\n * </NcActionButton>\n * </CnContextMenu>\n * ```\n */\nexport default {\n\tname: 'CnContextMenu',\n\n\tcomponents: {\n\t\tNcActions,\n\t\tNcActionButton,\n\t\tNcActionLink,\n\t\tCnIcon,\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Whether the context menu is open. Bind with `v-model:open` — the `.sync`\n\t\t * modifier it replaces was removed in Vue 3 and binds nothing.\n\t\t * Bind to `useContextMenu().isOpen`.\n\t\t */\n\t\topen: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Action definitions rendered as NcActionButton items.\n\t\t * Same format as CnRowActions: `{ label, icon?, handler?, disabled?, visible?, title?, destructive? }`.\n\t\t * `icon` accepts either a component (rendered directly) or a string —\n\t\t * a string is treated as an MDI name and rendered via `CnIcon` (e.g.\n\t\t * `\"Eye\"`), which lets manifest (JSON) actions declare icons by name.\n\t\t * `visible` (boolean | (targetItem) => boolean) hides the entry when falsy\n\t\t * (default: shown). `title` (string | (targetItem) => string) renders as\n\t\t * a native tooltip — useful for explaining why an entry is disabled.\n\t\t * `href` (URL) or `to` (vue-router location), each a value or a\n\t\t * `(targetItem) => …` function, render the entry as a real link, with\n\t\t * `linkTarget` as its `target`; a link emits `action` but does not call\n\t\t * `handler`. When the entire array is empty (or all entries are filtered\n\t\t * out), only the default slot content is rendered.\n\t\t *\n\t\t * @type {Array<{label: string, icon: object | string, handler: (targetItem: object) => void, disabled: boolean | ((targetItem: object) => boolean), visible: boolean | ((targetItem: object) => boolean), title: string | ((targetItem: object) => string), destructive: boolean, href: string | ((targetItem: object) => string), to: string | object | ((targetItem: object) => string | object), linkTarget: string}>}\n\t\t */\n\t\tactions: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * The right-clicked item (row, folder, etc.). Passed to action `handler`\n\t\t * and `disabled` callbacks, and included in the `action` event payload.\n\t\t * Bind to `useContextMenu().targetItem`.\n\t\t */\n\t\ttargetItem: {\n\t\t\ttype: [Object, String, Number],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Name of the currently-active custom panel. When set (and the menu is\n\t\t * open), the default NcActions list is replaced by the matching\n\t\t * `#panel:<name>` slot, rendered free-form (no NcActions child filter).\n\t\t * Bind with `v-model:active-panel` — the component emits `update:activePanel(null)` when\n\t\t * the panel's `back()` binding is invoked or the menu closes.\n\t\t *\n\t\t * @type {string|null}\n\t\t */\n\t\tactivePanel: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: ['action', 'close', 'closed', 'update:activePanel', 'update:open'],\n\n\tdata() {\n\t\treturn {\n\t\t\tinternalOpen: this.open,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Filter actions by their `visible` predicate. Entries without\n\t\t * `visible` are always shown (backwards compatible).\n\t\t *\n\t\t * @return {Array} Visible actions for the current targetItem.\n\t\t */\n\t\tvisibleActions() {\n\t\t\treturn this.actions.filter((action) => {\n\t\t\t\tif (action.visible === undefined) {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tif (typeof action.visible === 'function') {\n\t\t\t\t\treturn !!action.visible(this.targetItem)\n\t\t\t\t}\n\t\t\t\treturn !!action.visible\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Visible actions paired with the link each renders as (null for a button).\n\t\t *\n\t\t * @return {Array<{action: object, link: object|null}>}\n\t\t */\n\t\trenderedActions() {\n\t\t\treturn this.visibleActions.map((action) => ({\n\t\t\t\taction,\n\t\t\t\tlink: this.resolveDisabled(action) ? null : resolveItemActionLink(action, this.targetItem, this.$router),\n\t\t\t}))\n\t\t},\n\t},\n\n\twatch: {\n\t\topen(val) {\n\t\t\tthis.internalOpen = val\n\t\t},\n\n\t\tinternalOpen(val) {\n\t\t\t// Description goes ABOVE `@event`, not inline after it:\n\t\t\t// vue-docgen-api's event-name splitter stops at the first `:`, so\n\t\t\t// `@event update:open <description>` is read as one long event NAME\n\t\t\t// and the generated docs show an empty description.\n\t\t\t/**\n\t\t\t * Fired whenever the menu's internal open state flips. Used by\n\t\t\t * callers that bind `v-model:open` to track visibility.\n\t\t\t *\n\t\t\t * @event update:open\n\t\t\t * @type {boolean}\n\t\t\t */\n\t\t\tthis.$emit('update:open', val)\n\n\t\t\tif (val) {\n\t\t\t\tthis.tagPopper()\n\t\t\t\tthis.startOutsideWatch()\n\t\t\t} else {\n\t\t\t\tthis.stopOutsideWatch()\n\t\t\t\t// NcActions' `@closed` never fires (see `onClosed`), so drop the\n\t\t\t\t// \"a context menu is open\" attribute here. The CSS vars stay put\n\t\t\t\t// — the hide animation still needs the transform, and the next\n\t\t\t\t// `open()` overwrites them before the popper shows again.\n\t\t\t\tdocument.documentElement.removeAttribute(CTX_MENU_DATA_ATTR)\n\t\t\t}\n\t\t},\n\t},\n\n\tmounted() {\n\t\t// NcActions renders its own NcButton as the popover trigger; the menu\n\t\t// opens only via right-click (via `:open.sync`), so the button is\n\t\t// offscreen-positioned (see the `.cn-context-menu` rule below).\n\t\t//\n\t\t// Use `inert` rather than `aria-hidden`: when the menu is dismissed\n\t\t// without picking an item, NcPopover's focus-trap restores focus to the\n\t\t// trigger button. With `aria-hidden` that produces a \"Blocked\n\t\t// aria-hidden on an element because its descendant retained focus\"\n\t\t// warning (focused element must not be hidden from AT). `inert` both\n\t\t// hides the button from the accessibility tree *and* makes it\n\t\t// unfocusable, so the trap's focus restore falls back to <body> instead\n\t\t// of landing on a hidden element. (The button never needs focus — the\n\t\t// menu opens via `:open.sync`, not by focusing the trigger.)\n\t\tconst trigger = this.$el?.querySelector('.action-item__menutoggle')\n\t\tif (trigger) {\n\t\t\ttrigger.setAttribute('inert', '')\n\t\t}\n\n\t\t// Silence NcPopover's dev-mode a11y warning for this specific instance.\n\t\t//\n\t\t// NcPopover (when `window.OC?.debug` is on) calls\n\t\t// `tabbable(triggerContainer)[0]` on every open and warns\n\t\t// \"It looks like you are using a custom button as a <NcPopover>\n\t\t// trigger…\" if nothing tabbable is found. We *are* using NcButton —\n\t\t// the warning's escape hatch — but the button is offscreen-positioned\n\t\t// and tabbable's visibility checks won't reliably find it across\n\t\t// browsers (Chromium's `Element.checkVisibility()` excludes some\n\t\t// offscreen / clipped elements). The advice in the warning (\"bind\n\t\t// #trigger slot attrs\") doesn't apply: NcActions controls its own\n\t\t// trigger and doesn't expose a `#trigger` slot to override.\n\t\t//\n\t\t// Neutralising the check on our own NcPopover instance (one level\n\t\t// down inside NcActions) leaves every other popover untouched and\n\t\t// keeps the check live for real custom triggers elsewhere.\n\t\t//\n\t\t// Vue 3: `this.$children` was removed (it hard-errors under @vue/compat\n\t\t// MODE 3 with \"INSTANCE_CHILDREN compat has been disabled\"), so reach the\n\t\t// NcActions instance through an explicit template ref instead.\n\t\tconst ncActions = this.$refs.actions\n\t\tconst ncPopover = ncActions?.$refs?.popover\n\t\tif (ncPopover) {\n\t\t\tncPopover.checkTriggerA11y = () => {}\n\t\t}\n\n\t\tthis.tagPopper()\n\n\t\t// Mounting with `open` already true never trips the watcher.\n\t\tif (this.internalOpen) {\n\t\t\tthis.startOutsideWatch()\n\t\t}\n\t},\n\n\tbeforeUnmount() {\n\t\tthis.stopOutsideWatch()\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Mark this menu's own popper element so the cursor-positioning CSS in\n\t\t * `src/css/context-menu.css` can target it — and nothing else.\n\t\t *\n\t\t * That CSS used to key on the `<html>` data attribute alone, which\n\t\t * matches *every* `.v-popper__popper` in the document: any other popover\n\t\t * alive while a context menu was open (a table row's actions menu, most\n\t\t * visibly) got dragged to the last right-click coordinates.\n\t\t *\n\t\t * The marker has to be applied imperatively — NcActions hardcodes\n\t\t * `popoverBaseClass: 'action-item__popper'` on its NcPopover and spreads\n\t\t * no `$attrs` onto it, so there is no prop to pass one in through.\n\t\t *\n\t\t * It must be an *attribute*, not a class. floating-vue binds a dynamic\n\t\t * `class` on this element (`--shown` / `--hidden` / `--show-from` / …)\n\t\t * and Vue's `patchClass` assigns `el.className` wholesale, so a class\n\t\t * added here is wiped the first time the popper opens — the rule then\n\t\t * matches nothing and the menu renders at its offscreen trigger. Vue\n\t\t * only patches props present in the vnode, so an attribute it never\n\t\t * renders is left alone.\n\t\t *\n\t\t * The element itself is stable: floating-vue keeps one `$_popperNode`\n\t\t * and `appendChild`s it into the container on first show, so marking it\n\t\t * once at `mounted()` holds for the component's lifetime. Re-running on\n\t\t * open is a cheap, idempotent guard for a not-yet-ready ref chain.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\ttagPopper() {\n\t\t\tconst el = this.$refs.actions?.$refs?.popover?.getPopoverContentElement?.()\n\t\t\tel?.setAttribute?.(CTX_MENU_POPPER_ATTR, '')\n\t\t},\n\n\t\t/**\n\t\t * Start watching for a press outside the menu. Driven from both the\n\t\t * `internalOpen` watcher and `mounted()`, because a consumer may mount\n\t\t * with `open` already true — in which case there is no state transition\n\t\t * for the watcher to see.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tstartOutsideWatch() {\n\t\t\t// nextTick: the contextmenu/click that opened the menu is still\n\t\t\t// propagating and would dismiss it again on the same gesture.\n\t\t\tthis.$nextTick(() => {\n\t\t\t\tif (this.internalOpen) {\n\t\t\t\t\t// CAPTURE. A canvas that swallows its own mousedown never lets it\n\t\t\t\t\t// bubble to document, so a menu opened over one could not be\n\t\t\t\t\t// dismissed by clicking the canvas — Vue Flow's d3-zoom pane calls\n\t\t\t\t\t// stopImmediatePropagation, which is exactly that shape.\n\t\t\t\t\tdocument.addEventListener('mousedown', this.onDocumentMouseDown, true)\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Stop watching for outside presses.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tstopOutsideWatch() {\n\t\t\tdocument.removeEventListener('mousedown', this.onDocumentMouseDown, true)\n\t\t},\n\n\t\t/**\n\t\t * Close the menu when the press lands outside it.\n\t\t *\n\t\t * NcActions has to run in `manual-open` mode here (the menu is opened\n\t\t * from a right-click via the `open` prop, not by activating the trigger\n\t\t * button). `@nextcloud/vue` 9 couples that mode to\n\t\t * `noCloseOnClickOutside: this.manualOpen`, and NcPopover derives\n\t\t * `autoHide: !noCloseOnClickOutside && closeOnClickOutside` — so the\n\t\t * popper's own outside-click dismissal is switched off and nothing else\n\t\t * closes the menu. v8 only cleared the popper's open `triggers`, leaving\n\t\t * autoHide intact, which is why dismissing worked before the Vue 3\n\t\t * upgrade. NcActions spreads no `$attrs` onto NcPopover, so the prop\n\t\t * cannot be overridden from outside — hence handling it here.\n\t\t *\n\t\t * @param {MouseEvent} event The document mousedown.\n\t\t * @return {void}\n\t\t */\n\t\tonDocumentMouseDown(event) {\n\t\t\tif (!this.internalOpen || this.isInsideMenu(event.target)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.onClose()\n\t\t},\n\n\t\t/**\n\t\t * Whether a node sits inside the menu's own surface — the custom panel,\n\t\t * or the popper NcActions teleports to `container=\"body\"` (so it is not\n\t\t * inside `this.$el` and `contains()` on the root would miss it).\n\t\t *\n\t\t * @param {EventTarget} target The event target to test.\n\t\t * @return {boolean} True when the target belongs to this menu.\n\t\t */\n\t\tisInsideMenu(target) {\n\t\t\tif (!(target instanceof Node)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif (this.$refs.panel?.contains(target)) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tconst content = this.$refs.actions?.$refs?.popover?.getPopoverContentElement?.()\n\t\t\tif (content?.contains(target)) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\t// Fallback for when the popover ref chain is unavailable —\n\t\t\t// `action-item__popper` is the base class NcActions gives its popper.\n\t\t\treturn target instanceof Element && !!target.closest('.action-item__popper')\n\t\t},\n\n\t\tresolveDisabled(action) {\n\t\t\tif (typeof action.disabled === 'function') {\n\t\t\t\treturn action.disabled(this.targetItem)\n\t\t\t}\n\t\t\treturn !!action.disabled\n\t\t},\n\n\t\t/**\n\t\t * Resolve the `title` field on an action descriptor — supports both\n\t\t * a static string and a function `(targetItem) => string`. Returns\n\t\t * undefined when no title is provided so the attribute isn't rendered.\n\t\t *\n\t\t * @param {object} action The action descriptor.\n\t\t * @return {string|undefined} The tooltip text, or undefined.\n\t\t */\n\t\tresolveTitle(action) {\n\t\t\tif (typeof action.title === 'function') {\n\t\t\t\treturn action.title(this.targetItem) || undefined\n\t\t\t}\n\t\t\treturn action.title || undefined\n\t\t},\n\n\t\t/**\n\t\t * Slugify an action label for use in stable `data-testid` selectors.\n\t\t * Lowercase, kebab-case, strip non-alphanumeric. Used solely by the\n\t\t * `:data-testid` binding on NcActionButton — does not affect runtime\n\t\t * behaviour or rendered text.\n\t\t *\n\t\t * @param {string} label The action's display label.\n\t\t * @return {string} kebab-case slug suitable for a testid suffix.\n\t\t */\n\t\tslugifyLabel(label) {\n\t\t\treturn String(label || '')\n\t\t\t\t.toLowerCase()\n\t\t\t\t.replace(/[^a-z0-9]+/g, '-')\n\t\t\t\t.replace(/^-+|-+$/g, '')\n\t\t},\n\n\t\tonAction(action) {\n\t\t\tif (action.handler && typeof action.handler === 'function') {\n\t\t\t\taction.handler(this.targetItem)\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event action User picked an entry from the menu. The action's own `handler(targetItem)` (when present) ran synchronously before this event fires; the event lets parents observe / log the choice.\n\t\t\t * @type {{ action: string, row: object|null }}\n\t\t\t */\n\t\t\tthis.$emit('action', { action: action.label, row: this.targetItem })\n\t\t},\n\n\t\t/**\n\t\t * A link entry was clicked: route a plain in-app click, leave the rest to\n\t\t * the browser, and emit `action` as a button would. The `handler` is not\n\t\t * called, since navigating is what the link already does. A modified\n\t\t * click opens a new tab, so nothing is emitted in this one.\n\t\t *\n\t\t * @param {object} action The action descriptor.\n\t\t * @param {object} link The resolved link.\n\t\t * @param {MouseEvent} event The click event.\n\t\t */\n\t\tonLinkAction(action, link, event) {\n\t\t\tif (isModifiedClick(event)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfollowItemActionLink(event, link, this.$router)\n\t\t\tthis.$emit('action', { action: action.label, row: this.targetItem })\n\t\t},\n\n\t\tonClose() {\n\t\t\tthis.internalOpen = false\n\t\t\tif (this.activePanel) {\n\t\t\t\tthis.$emit('update:activePanel', null)\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event close Fired when the menu starts closing (before the popper's hide animation). This is the reliable one — prefer it over `closed`.\n\t\t\t */\n\t\t\tthis.$emit('close')\n\t\t},\n\n\t\t/**\n\t\t * Clear the active panel and return to the default action list.\n\t\t * Exposed to custom panel slots via the `back` scope binding.\n\t\t */\n\t\tback() {\n\t\t\t// Description above the tag — see the note on `update:open`.\n\t\t\t/**\n\t\t\t * Emitted with `null` to clear the active panel — fired by the\n\t\t\t * panel's `back()` binding and whenever the menu closes while a\n\t\t\t * panel is open. Bind with `v-model:active-panel`.\n\t\t\t *\n\t\t\t * @event update:activePanel\n\t\t\t * @type {null}\n\t\t\t */\n\t\t\tthis.$emit('update:activePanel', null)\n\t\t},\n\n\t\t/**\n\t\t * Fires after the popper's hide animation completes.\n\t\t *\n\t\t * IT MUST NOT CLEAR THE CURSOR POSITION, and it used to. This fires LATE\n\t\t * — a whole hide animation late — so a second click on the same target\n\t\t * reopens the menu before it arrives, and the tear-down then strips the\n\t\t * coordinates that reopen had just written. The transform resolves\n\t\t * against undefined vars and the menu lands in the corner, on every open\n\t\t * after the first.\n\t\t *\n\t\t * It was dead code when written: `@nextcloud/vue` 9 bound `onAfterClose`\n\t\t * against an NcPopover that only emitted `afterHide`. 9.11.0 binds\n\t\t * `onAfterHide`, so it runs now, and a tear-down nothing needed became a\n\t\t * bug. Nothing needs it still: positioning is scoped to this menu's own\n\t\t * popper (see `tagPopper`), the data attribute is dropped in the\n\t\t * `internalOpen` watcher, the next `open()` overwrites the vars, and the\n\t\t * composable's unmount hook clears them for good.\n\t\t */\n\t\tonClosed() {\n\t\t\t/**\n\t\t\t * @event closed Intended to fire after the popper's hide animation completes. Does not currently fire — `@nextcloud/vue` 9's NcActions listens for an `afterClose` event NcPopover never emits. Use `close` instead.\n\t\t\t */\n\t\t\tthis.$emit('closed')\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n/* Selector is deliberately two classes deep. `.cn-context-menu` lands on\n NcActions' root, which NcActions itself styles as `.action-item[data-v-…]\n { position: relative; display: inline-block }`. Scoped CSS appends the attr\n to the last compound only, so a bare `.cn-context-menu` tied that rule at\n specificity (0,2,0) and lost on injection order — the trigger stayed\n `position: relative`, so it kept its in-flow box (top/left just offset it\n visually) and showed up as a full-width ~34px-tall phantom row between the\n table and the pagination on CnIndexPage. Adding the parent class makes this\n (0,3,0) so it wins regardless of order. */\n.cn-context-menu-root > .cn-context-menu {\n\t/* Hide the NcActions trigger button — menu opens only via right-click.\n\t Off-screen rather than display:none / visibility:hidden so NcPopover's\n\t a11y check (`tabbable(triggerContainer)[0]`) still finds the NcButton\n\t and the \"custom button as a <NcPopover> trigger\" warning stays quiet.\n\t Avoid `clip` / `clip-path` here — modern browsers' `Element.checkVisibility()`,\n\t which tabbable calls into, treats those as invisible. Pure offscreen\n\t positioning leaves the element fully \"visible\" to layout while being\n\t off the user's screen. */\n\tposition: absolute;\n\ttop: -9999px;\n\tleft: -9999px;\n\topacity: 0;\n\tpointer-events: none;\n}\n\n.cn-context-menu__backdrop {\n\tposition: fixed;\n\tinset: 0;\n\tz-index: 9998;\n}\n\n.cn-context-menu__panel {\n\tposition: fixed;\n\ttop: var(--cn-ctx-menu-y, 0);\n\tleft: var(--cn-ctx-menu-x, 0);\n\tz-index: 9999;\n\tmin-width: 220px;\n\tpadding: 4px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-large, 12px);\n\tbox-shadow: 0 1px 5px rgba(0, 0, 0, 0.2);\n\toutline: none;\n}\n</style>\n"],"names":["CnIcon"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CnContextMenu.vue3.js","sources":["../../../../src/components/CnContextMenu/CnContextMenu.vue?vue&type=template&id=1bef81d7&lang.js"],"sourcesContent":["<template>\n\t<div class=\"cn-context-menu-root\">\n\t\t<NcActions\n\t\t\tv-if=\"!activePanel\"\n\t\t\tref=\"actions\"\n\t\t\tv-model:open=\"internalOpen\"\n\t\t\t:manualOpen=\"true\"\n\t\t\t:forceMenu=\"true\"\n\t\t\tclass=\"cn-context-menu\"\n\t\t\tcontainer=\"body\"\n\t\t\tdata-testid=\"cn-context-menu\"\n\t\t\t@close=\"onClose\"\n\t\t\t@closed=\"onClosed\">\n\t\t\t<!-- Dynamic actions from array prop -->\n\t\t\t<template v-for=\"{ action, link } in renderedActions\" :key=\"action.label\">\n\t\t\t\t<!-- A navigate-only action is a real link, so it can be middle-clicked, opened in a new tab or copied. -->\n\t\t\t\t<NcActionLink\n\t\t\t\t\tv-if=\"link\"\n\t\t\t\t\t:href=\"link.href\"\n\t\t\t\t\t:target=\"link.target\"\n\t\t\t\t\t:title=\"resolveTitle(action)\"\n\t\t\t\t\t:class=\"{ 'cn-row-action--destructive': action.destructive }\"\n\t\t\t\t\t:data-testid=\"`cn-action-item-${slugifyLabel(action.label)}`\"\n\t\t\t\t\tcloseAfterClick\n\t\t\t\t\t@click=\"onLinkAction(action, link, $event)\">\n\t\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t\t<CnIcon v-if=\"typeof action.icon === 'string'\" :name=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t\t<component :is=\"action.icon\" v-else :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ action.label }}\n\t\t\t\t</NcActionLink>\n\t\t\t\t<NcActionButton\n\t\t\t\t\tv-else\n\t\t\t\t\t:title=\"resolveTitle(action)\"\n\t\t\t\t\t:disabled=\"resolveDisabled(action)\"\n\t\t\t\t\t:class=\"{ 'cn-row-action--destructive': action.destructive }\"\n\t\t\t\t\t:data-testid=\"`cn-action-item-${slugifyLabel(action.label)}`\"\n\t\t\t\t\tcloseAfterClick\n\t\t\t\t\t@click=\"onAction(action)\">\n\t\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t\t<CnIcon v-if=\"typeof action.icon === 'string'\" :name=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t\t<component :is=\"action.icon\" v-else :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ action.label }}\n\t\t\t\t</NcActionButton>\n\t\t\t</template>\n\n\t\t\t<!--\n\t\t\t@slot default\n\t\t\t@description Custom NcActionButton-family content rendered inside the\n\t\t\tNcActions menu. Use this for hardcoded buttons (Keepiq pattern) when\n\t\t\tthe `actions` prop is empty.\n\t\t-->\n\t\t\t<slot />\n\t\t</NcActions>\n\n\t\t<!-- CUSTOM PANEL — bypass NcActions, render arbitrary slot content\n\t\t anchored at the cursor. The `#panel:<name>` slot may contain any\n\t\t markup (grids, inputs, custom components) without the NcActions\n\t\t child-allowlist filter. -->\n\t\t<template v-if=\"activePanel && internalOpen\">\n\t\t\t<div\n\t\t\t\tclass=\"cn-context-menu__backdrop\"\n\t\t\t\t@click=\"onClose\"\n\t\t\t\t@contextmenu.prevent=\"onClose\" />\n\t\t\t<div\n\t\t\t\tref=\"panel\"\n\t\t\t\tclass=\"cn-context-menu__panel\"\n\t\t\t\trole=\"menu\"\n\t\t\t\ttabindex=\"-1\"\n\t\t\t\tdata-testid=\"cn-context-menu-panel\"\n\t\t\t\t:data-panel=\"activePanel\"\n\t\t\t\t@keydown.esc.stop=\"onClose\"\n\t\t\t\t@click.stop>\n\t\t\t\t<!-- @slot panel:<name> Free-form custom panel content shown when\n\t\t\t\t `activePanel === '<name>'`. Bypasses the NcActions child\n\t\t\t\t allowlist — put any markup here (grids, inputs, custom\n\t\t\t\t components, etc.). The slot name is dynamic: define one\n\t\t\t\t `#panel:<name>` per panel you want to support. -->\n\t\t\t\t<!-- @binding {Function} back Clear `activePanel`, returning to the default action list. -->\n\t\t\t\t<!-- @binding {Function} close Close the entire menu, equivalent to clicking outside. -->\n\t\t\t\t<!-- @binding {*} targetItem The right-clicked item, forwarded from the `targetItem` prop. -->\n\t\t\t\t<slot\n\t\t\t\t\t:name=\"`panel:${activePanel}`\"\n\t\t\t\t\t:back=\"back\"\n\t\t\t\t\t:close=\"onClose\"\n\t\t\t\t\t:targetItem=\"targetItem\" />\n\t\t\t</div>\n\t\t</template>\n\t</div>\n</template>\n\n<script>\nimport { NcActionButton, NcActionLink, NcActions } from '@nextcloud/vue'\nimport { CTX_MENU_DATA_ATTR, CTX_MENU_POPPER_ATTR } from '../../composables/useContextMenu.js'\nimport { followItemActionLink, resolveItemActionLink } from '../../utils/actionLink.js'\nimport { CnIcon } from '../CnIcon/index.js'\n\n/**\n * CnContextMenu — Right-click context menu wrapper around NcActions.\n *\n * Provides a pre-configured NcActions instance that positions itself at the\n * cursor via the `useContextMenu` composable's CSS custom properties. Accepts\n * an `actions` array for the common dynamic case (like CnRowActions), and a\n * default slot for hardcoded NcActionButton content.\n *\n * Pair with `useContextMenu()` for state management (open/close, target item,\n * cursor positioning). The composable handles the DOM attributes; this component\n * handles the NcActions template boilerplate.\n *\n * Dynamic actions (CnIndexPage pattern)\n * ```vue\n * <CnContextMenu\n * v-model:open=\"contextMenuOpen\"\n * :actions=\"mergedActions\"\n * :target-item=\"contextMenuRow\"\n * @action=\"$emit('action', $event)\"\n * @close=\"closeContextMenu\" />\n * ```\n *\n * Custom buttons via slot (Keepiq pattern)\n * ```vue\n * <CnContextMenu\n * v-model:open=\"contextMenuOpen\"\n * @close=\"closeContextMenu\">\n * <NcActionButton close-after-click @click=\"onRename\">\n * <template #icon><PencilIcon :size=\"20\" /></template>\n * Rename\n * </NcActionButton>\n * </CnContextMenu>\n * ```\n */\nexport default {\n\tname: 'CnContextMenu',\n\n\tcomponents: {\n\t\tNcActions,\n\t\tNcActionButton,\n\t\tNcActionLink,\n\t\tCnIcon,\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Whether the context menu is open. Bind with `v-model:open` — the `.sync`\n\t\t * modifier it replaces was removed in Vue 3 and binds nothing.\n\t\t * Bind to `useContextMenu().isOpen`.\n\t\t */\n\t\topen: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Action definitions rendered as NcActionButton items.\n\t\t * Same format as CnRowActions: `{ label, icon?, handler?, disabled?, visible?, title?, destructive? }`.\n\t\t * `icon` accepts either a component (rendered directly) or a string —\n\t\t * a string is treated as an MDI name and rendered via `CnIcon` (e.g.\n\t\t * `\"Eye\"`), which lets manifest (JSON) actions declare icons by name.\n\t\t * `visible` (boolean | (targetItem) => boolean) hides the entry when falsy\n\t\t * (default: shown). `title` (string | (targetItem) => string) renders as\n\t\t * a native tooltip — useful for explaining why an entry is disabled.\n\t\t * `href` (URL) or `to` (vue-router location), each a value or a\n\t\t * `(targetItem) => …` function, render the entry as a real link, with\n\t\t * `linkTarget` as its `target`; a link emits `action` but does not call\n\t\t * `handler`. When the entire array is empty (or all entries are filtered\n\t\t * out), only the default slot content is rendered.\n\t\t *\n\t\t * @type {Array<{label: string, icon: object | string, handler: (targetItem: object) => void, disabled: boolean | ((targetItem: object) => boolean), visible: boolean | ((targetItem: object) => boolean), title: string | ((targetItem: object) => string), destructive: boolean, href: string | ((targetItem: object) => string), to: string | object | ((targetItem: object) => string | object), linkTarget: string}>}\n\t\t */\n\t\tactions: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * The right-clicked item (row, folder, etc.). Passed to action `handler`\n\t\t * and `disabled` callbacks, and included in the `action` event payload.\n\t\t * Bind to `useContextMenu().targetItem`.\n\t\t */\n\t\ttargetItem: {\n\t\t\ttype: [Object, String, Number],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Name of the currently-active custom panel. When set (and the menu is\n\t\t * open), the default NcActions list is replaced by the matching\n\t\t * `#panel:<name>` slot, rendered free-form (no NcActions child filter).\n\t\t * Bind with `v-model:active-panel` — the component emits `update:activePanel(null)` when\n\t\t * the panel's `back()` binding is invoked or the menu closes.\n\t\t *\n\t\t * @type {string|null}\n\t\t */\n\t\tactivePanel: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: ['action', 'close', 'closed', 'update:activePanel', 'update:open'],\n\n\tdata() {\n\t\treturn {\n\t\t\tinternalOpen: this.open,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Filter actions by their `visible` predicate. Entries without\n\t\t * `visible` are always shown (backwards compatible).\n\t\t *\n\t\t * @return {Array} Visible actions for the current targetItem.\n\t\t */\n\t\tvisibleActions() {\n\t\t\treturn this.actions.filter((action) => {\n\t\t\t\tif (action.visible === undefined) {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tif (typeof action.visible === 'function') {\n\t\t\t\t\treturn !!action.visible(this.targetItem)\n\t\t\t\t}\n\t\t\t\treturn !!action.visible\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Visible actions paired with the link each renders as (null for a button).\n\t\t *\n\t\t * @return {Array<{action: object, link: object|null}>}\n\t\t */\n\t\trenderedActions() {\n\t\t\treturn this.visibleActions.map((action) => ({\n\t\t\t\taction,\n\t\t\t\tlink: this.resolveDisabled(action) ? null : resolveItemActionLink(action, this.targetItem, this.$router),\n\t\t\t}))\n\t\t},\n\t},\n\n\twatch: {\n\t\topen(val) {\n\t\t\tthis.internalOpen = val\n\t\t},\n\n\t\tinternalOpen(val) {\n\t\t\t// Description goes ABOVE `@event`, not inline after it:\n\t\t\t// vue-docgen-api's event-name splitter stops at the first `:`, so\n\t\t\t// `@event update:open <description>` is read as one long event NAME\n\t\t\t// and the generated docs show an empty description.\n\t\t\t/**\n\t\t\t * Fired whenever the menu's internal open state flips. Used by\n\t\t\t * callers that bind `v-model:open` to track visibility.\n\t\t\t *\n\t\t\t * @event update:open\n\t\t\t * @type {boolean}\n\t\t\t */\n\t\t\tthis.$emit('update:open', val)\n\n\t\t\tif (val) {\n\t\t\t\tthis.tagPopper()\n\t\t\t\tthis.startOutsideWatch()\n\t\t\t} else {\n\t\t\t\tthis.stopOutsideWatch()\n\t\t\t\t// NcActions' `@closed` never fires (see `onClosed`), so drop the\n\t\t\t\t// \"a context menu is open\" attribute here. The CSS vars stay put\n\t\t\t\t// — the hide animation still needs the transform, and the next\n\t\t\t\t// `open()` overwrites them before the popper shows again.\n\t\t\t\tdocument.documentElement.removeAttribute(CTX_MENU_DATA_ATTR)\n\t\t\t}\n\t\t},\n\t},\n\n\tmounted() {\n\t\t// NcActions renders its own NcButton as the popover trigger; the menu\n\t\t// opens only via right-click (via `:open.sync`), so the button is\n\t\t// offscreen-positioned (see the `.cn-context-menu` rule below).\n\t\t//\n\t\t// Use `inert` rather than `aria-hidden`: when the menu is dismissed\n\t\t// without picking an item, NcPopover's focus-trap restores focus to the\n\t\t// trigger button. With `aria-hidden` that produces a \"Blocked\n\t\t// aria-hidden on an element because its descendant retained focus\"\n\t\t// warning (focused element must not be hidden from AT). `inert` both\n\t\t// hides the button from the accessibility tree *and* makes it\n\t\t// unfocusable, so the trap's focus restore falls back to <body> instead\n\t\t// of landing on a hidden element. (The button never needs focus — the\n\t\t// menu opens via `:open.sync`, not by focusing the trigger.)\n\t\tconst trigger = this.$el?.querySelector('.action-item__menutoggle')\n\t\tif (trigger) {\n\t\t\ttrigger.setAttribute('inert', '')\n\t\t}\n\n\t\t// Silence NcPopover's dev-mode a11y warning for this specific instance.\n\t\t//\n\t\t// NcPopover (when `window.OC?.debug` is on) calls\n\t\t// `tabbable(triggerContainer)[0]` on every open and warns\n\t\t// \"It looks like you are using a custom button as a <NcPopover>\n\t\t// trigger…\" if nothing tabbable is found. We *are* using NcButton —\n\t\t// the warning's escape hatch — but the button is offscreen-positioned\n\t\t// and tabbable's visibility checks won't reliably find it across\n\t\t// browsers (Chromium's `Element.checkVisibility()` excludes some\n\t\t// offscreen / clipped elements). The advice in the warning (\"bind\n\t\t// #trigger slot attrs\") doesn't apply: NcActions controls its own\n\t\t// trigger and doesn't expose a `#trigger` slot to override.\n\t\t//\n\t\t// Neutralising the check on our own NcPopover instance (one level\n\t\t// down inside NcActions) leaves every other popover untouched and\n\t\t// keeps the check live for real custom triggers elsewhere.\n\t\t//\n\t\t// Vue 3: `this.$children` was removed (it hard-errors under @vue/compat\n\t\t// MODE 3 with \"INSTANCE_CHILDREN compat has been disabled\"), so reach the\n\t\t// NcActions instance through an explicit template ref instead.\n\t\tconst ncActions = this.$refs.actions\n\t\tconst ncPopover = ncActions?.$refs?.popover\n\t\tif (ncPopover) {\n\t\t\tncPopover.checkTriggerA11y = () => {}\n\t\t}\n\n\t\tthis.tagPopper()\n\n\t\t// Mounting with `open` already true never trips the watcher.\n\t\tif (this.internalOpen) {\n\t\t\tthis.startOutsideWatch()\n\t\t}\n\t},\n\n\tbeforeUnmount() {\n\t\tthis.stopOutsideWatch()\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Mark this menu's own popper element so the cursor-positioning CSS in\n\t\t * `src/css/context-menu.css` can target it — and nothing else.\n\t\t *\n\t\t * That CSS used to key on the `<html>` data attribute alone, which\n\t\t * matches *every* `.v-popper__popper` in the document: any other popover\n\t\t * alive while a context menu was open (a table row's actions menu, most\n\t\t * visibly) got dragged to the last right-click coordinates.\n\t\t *\n\t\t * The marker has to be applied imperatively — NcActions hardcodes\n\t\t * `popoverBaseClass: 'action-item__popper'` on its NcPopover and spreads\n\t\t * no `$attrs` onto it, so there is no prop to pass one in through.\n\t\t *\n\t\t * It must be an *attribute*, not a class. floating-vue binds a dynamic\n\t\t * `class` on this element (`--shown` / `--hidden` / `--show-from` / …)\n\t\t * and Vue's `patchClass` assigns `el.className` wholesale, so a class\n\t\t * added here is wiped the first time the popper opens — the rule then\n\t\t * matches nothing and the menu renders at its offscreen trigger. Vue\n\t\t * only patches props present in the vnode, so an attribute it never\n\t\t * renders is left alone.\n\t\t *\n\t\t * The element itself is stable: floating-vue keeps one `$_popperNode`\n\t\t * and `appendChild`s it into the container on first show, so marking it\n\t\t * once at `mounted()` holds for the component's lifetime. Re-running on\n\t\t * open is a cheap, idempotent guard for a not-yet-ready ref chain.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\ttagPopper() {\n\t\t\tconst el = this.$refs.actions?.$refs?.popover?.getPopoverContentElement?.()\n\t\t\tel?.setAttribute?.(CTX_MENU_POPPER_ATTR, '')\n\t\t},\n\n\t\t/**\n\t\t * Start watching for a press outside the menu. Driven from both the\n\t\t * `internalOpen` watcher and `mounted()`, because a consumer may mount\n\t\t * with `open` already true — in which case there is no state transition\n\t\t * for the watcher to see.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tstartOutsideWatch() {\n\t\t\t// nextTick: the contextmenu/click that opened the menu is still\n\t\t\t// propagating and would dismiss it again on the same gesture.\n\t\t\tthis.$nextTick(() => {\n\t\t\t\tif (this.internalOpen) {\n\t\t\t\t\t// CAPTURE. A canvas that swallows its own mousedown never lets it\n\t\t\t\t\t// bubble to document, so a menu opened over one could not be\n\t\t\t\t\t// dismissed by clicking the canvas — Vue Flow's d3-zoom pane calls\n\t\t\t\t\t// stopImmediatePropagation, which is exactly that shape.\n\t\t\t\t\tdocument.addEventListener('mousedown', this.onDocumentMouseDown, true)\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Stop watching for outside presses.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tstopOutsideWatch() {\n\t\t\tdocument.removeEventListener('mousedown', this.onDocumentMouseDown, true)\n\t\t},\n\n\t\t/**\n\t\t * Close the menu when the press lands outside it.\n\t\t *\n\t\t * NcActions has to run in `manual-open` mode here (the menu is opened\n\t\t * from a right-click via the `open` prop, not by activating the trigger\n\t\t * button). `@nextcloud/vue` 9 couples that mode to\n\t\t * `noCloseOnClickOutside: this.manualOpen`, and NcPopover derives\n\t\t * `autoHide: !noCloseOnClickOutside && closeOnClickOutside` — so the\n\t\t * popper's own outside-click dismissal is switched off and nothing else\n\t\t * closes the menu. v8 only cleared the popper's open `triggers`, leaving\n\t\t * autoHide intact, which is why dismissing worked before the Vue 3\n\t\t * upgrade. NcActions spreads no `$attrs` onto NcPopover, so the prop\n\t\t * cannot be overridden from outside — hence handling it here.\n\t\t *\n\t\t * @param {MouseEvent} event The document mousedown.\n\t\t * @return {void}\n\t\t */\n\t\tonDocumentMouseDown(event) {\n\t\t\tif (!this.internalOpen || this.isInsideMenu(event.target)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.onClose()\n\t\t},\n\n\t\t/**\n\t\t * Whether a node sits inside the menu's own surface — the custom panel,\n\t\t * or the popper NcActions teleports to `container=\"body\"` (so it is not\n\t\t * inside `this.$el` and `contains()` on the root would miss it).\n\t\t *\n\t\t * @param {EventTarget} target The event target to test.\n\t\t * @return {boolean} True when the target belongs to this menu.\n\t\t */\n\t\tisInsideMenu(target) {\n\t\t\tif (!(target instanceof Node)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif (this.$refs.panel?.contains(target)) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tconst content = this.$refs.actions?.$refs?.popover?.getPopoverContentElement?.()\n\t\t\tif (content?.contains(target)) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\t// Fallback for when the popover ref chain is unavailable —\n\t\t\t// `action-item__popper` is the base class NcActions gives its popper.\n\t\t\treturn target instanceof Element && !!target.closest('.action-item__popper')\n\t\t},\n\n\t\tresolveDisabled(action) {\n\t\t\tif (typeof action.disabled === 'function') {\n\t\t\t\treturn action.disabled(this.targetItem)\n\t\t\t}\n\t\t\treturn !!action.disabled\n\t\t},\n\n\t\t/**\n\t\t * Resolve the `title` field on an action descriptor — supports both\n\t\t * a static string and a function `(targetItem) => string`. Returns\n\t\t * undefined when no title is provided so the attribute isn't rendered.\n\t\t *\n\t\t * @param {object} action The action descriptor.\n\t\t * @return {string|undefined} The tooltip text, or undefined.\n\t\t */\n\t\tresolveTitle(action) {\n\t\t\tif (typeof action.title === 'function') {\n\t\t\t\treturn action.title(this.targetItem) || undefined\n\t\t\t}\n\t\t\treturn action.title || undefined\n\t\t},\n\n\t\t/**\n\t\t * Slugify an action label for use in stable `data-testid` selectors.\n\t\t * Lowercase, kebab-case, strip non-alphanumeric. Used solely by the\n\t\t * `:data-testid` binding on NcActionButton — does not affect runtime\n\t\t * behaviour or rendered text.\n\t\t *\n\t\t * @param {string} label The action's display label.\n\t\t * @return {string} kebab-case slug suitable for a testid suffix.\n\t\t */\n\t\tslugifyLabel(label) {\n\t\t\treturn String(label || '')\n\t\t\t\t.toLowerCase()\n\t\t\t\t.replace(/[^a-z0-9]+/g, '-')\n\t\t\t\t.replace(/^-+|-+$/g, '')\n\t\t},\n\n\t\tonAction(action) {\n\t\t\tif (action.handler && typeof action.handler === 'function') {\n\t\t\t\taction.handler(this.targetItem)\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event action User picked an entry from the menu. The action's own `handler(targetItem)` (when present) ran synchronously before this event fires; the event lets parents observe / log the choice.\n\t\t\t * @type {{ action: string, row: object|null }}\n\t\t\t */\n\t\t\tthis.$emit('action', { action: action.label, row: this.targetItem })\n\t\t},\n\n\t\t/**\n\t\t * A link entry was clicked: route a plain in-app click, leave the rest to\n\t\t * the browser, and emit `action` as a button would. The `handler` is not\n\t\t * called, since navigating is what the link already does.\n\t\t *\n\t\t * @param {object} action The action descriptor.\n\t\t * @param {object} link The resolved link.\n\t\t * @param {MouseEvent} event The click event.\n\t\t */\n\t\tonLinkAction(action, link, event) {\n\t\t\tfollowItemActionLink(event, link, this.$router)\n\t\t\tthis.$emit('action', { action: action.label, row: this.targetItem })\n\t\t},\n\n\t\tonClose() {\n\t\t\tthis.internalOpen = false\n\t\t\tif (this.activePanel) {\n\t\t\t\tthis.$emit('update:activePanel', null)\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event close Fired when the menu starts closing (before the popper's hide animation). This is the reliable one — prefer it over `closed`.\n\t\t\t */\n\t\t\tthis.$emit('close')\n\t\t},\n\n\t\t/**\n\t\t * Clear the active panel and return to the default action list.\n\t\t * Exposed to custom panel slots via the `back` scope binding.\n\t\t */\n\t\tback() {\n\t\t\t// Description above the tag — see the note on `update:open`.\n\t\t\t/**\n\t\t\t * Emitted with `null` to clear the active panel — fired by the\n\t\t\t * panel's `back()` binding and whenever the menu closes while a\n\t\t\t * panel is open. Bind with `v-model:active-panel`.\n\t\t\t *\n\t\t\t * @event update:activePanel\n\t\t\t * @type {null}\n\t\t\t */\n\t\t\tthis.$emit('update:activePanel', null)\n\t\t},\n\n\t\t/**\n\t\t * Fires after the popper's hide animation completes.\n\t\t *\n\t\t * IT MUST NOT CLEAR THE CURSOR POSITION, and it used to. This fires LATE\n\t\t * — a whole hide animation late — so a second click on the same target\n\t\t * reopens the menu before it arrives, and the tear-down then strips the\n\t\t * coordinates that reopen had just written. The transform resolves\n\t\t * against undefined vars and the menu lands in the corner, on every open\n\t\t * after the first.\n\t\t *\n\t\t * It was dead code when written: `@nextcloud/vue` 9 bound `onAfterClose`\n\t\t * against an NcPopover that only emitted `afterHide`. 9.11.0 binds\n\t\t * `onAfterHide`, so it runs now, and a tear-down nothing needed became a\n\t\t * bug. Nothing needs it still: positioning is scoped to this menu's own\n\t\t * popper (see `tagPopper`), the data attribute is dropped in the\n\t\t * `internalOpen` watcher, the next `open()` overwrites the vars, and the\n\t\t * composable's unmount hook clears them for good.\n\t\t */\n\t\tonClosed() {\n\t\t\t/**\n\t\t\t * @event closed Intended to fire after the popper's hide animation completes. Does not currently fire — `@nextcloud/vue` 9's NcActions listens for an `afterClose` event NcPopover never emits. Use `close` instead.\n\t\t\t */\n\t\t\tthis.$emit('closed')\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n/* Selector is deliberately two classes deep. `.cn-context-menu` lands on\n NcActions' root, which NcActions itself styles as `.action-item[data-v-…]\n { position: relative; display: inline-block }`. Scoped CSS appends the attr\n to the last compound only, so a bare `.cn-context-menu` tied that rule at\n specificity (0,2,0) and lost on injection order — the trigger stayed\n `position: relative`, so it kept its in-flow box (top/left just offset it\n visually) and showed up as a full-width ~34px-tall phantom row between the\n table and the pagination on CnIndexPage. Adding the parent class makes this\n (0,3,0) so it wins regardless of order. */\n.cn-context-menu-root > .cn-context-menu {\n\t/* Hide the NcActions trigger button — menu opens only via right-click.\n\t Off-screen rather than display:none / visibility:hidden so NcPopover's\n\t a11y check (`tabbable(triggerContainer)[0]`) still finds the NcButton\n\t and the \"custom button as a <NcPopover> trigger\" warning stays quiet.\n\t Avoid `clip` / `clip-path` here — modern browsers' `Element.checkVisibility()`,\n\t which tabbable calls into, treats those as invisible. Pure offscreen\n\t positioning leaves the element fully \"visible\" to layout while being\n\t off the user's screen. */\n\tposition: absolute;\n\ttop: -9999px;\n\tleft: -9999px;\n\topacity: 0;\n\tpointer-events: none;\n}\n\n.cn-context-menu__backdrop {\n\tposition: fixed;\n\tinset: 0;\n\tz-index: 9998;\n}\n\n.cn-context-menu__panel {\n\tposition: fixed;\n\ttop: var(--cn-ctx-menu-y, 0);\n\tleft: var(--cn-ctx-menu-x, 0);\n\tz-index: 9999;\n\tmin-width: 220px;\n\tpadding: 4px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-large, 12px);\n\tbox-shadow: 0 1px 5px rgba(0, 0, 0, 0.2);\n\toutline: none;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createBlock","_Fragment","_renderList","_normalizeClass","_toDisplayString","_resolveDynamicComponent","_renderSlot","_createElementVNode"],"mappings":"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"}
1
+ {"version":3,"file":"CnContextMenu.vue3.js","sources":["../../../../src/components/CnContextMenu/CnContextMenu.vue?vue&type=template&id=1bef81d7&lang.js"],"sourcesContent":["<template>\n\t<div class=\"cn-context-menu-root\">\n\t\t<NcActions\n\t\t\tv-if=\"!activePanel\"\n\t\t\tref=\"actions\"\n\t\t\tv-model:open=\"internalOpen\"\n\t\t\t:manualOpen=\"true\"\n\t\t\t:forceMenu=\"true\"\n\t\t\tclass=\"cn-context-menu\"\n\t\t\tcontainer=\"body\"\n\t\t\tdata-testid=\"cn-context-menu\"\n\t\t\t@close=\"onClose\"\n\t\t\t@closed=\"onClosed\">\n\t\t\t<!-- Dynamic actions from array prop -->\n\t\t\t<template v-for=\"{ action, link } in renderedActions\" :key=\"action.label\">\n\t\t\t\t<!-- A navigate-only action is a real link, so it can be middle-clicked, opened in a new tab or copied. -->\n\t\t\t\t<NcActionLink\n\t\t\t\t\tv-if=\"link\"\n\t\t\t\t\t:href=\"link.href\"\n\t\t\t\t\t:target=\"link.target\"\n\t\t\t\t\t:title=\"resolveTitle(action)\"\n\t\t\t\t\t:class=\"{ 'cn-row-action--destructive': action.destructive }\"\n\t\t\t\t\t:data-testid=\"`cn-action-item-${slugifyLabel(action.label)}`\"\n\t\t\t\t\tcloseAfterClick\n\t\t\t\t\t@click=\"onLinkAction(action, link, $event)\">\n\t\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t\t<CnIcon v-if=\"typeof action.icon === 'string'\" :name=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t\t<component :is=\"action.icon\" v-else :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ action.label }}\n\t\t\t\t</NcActionLink>\n\t\t\t\t<NcActionButton\n\t\t\t\t\tv-else\n\t\t\t\t\t:title=\"resolveTitle(action)\"\n\t\t\t\t\t:disabled=\"resolveDisabled(action)\"\n\t\t\t\t\t:class=\"{ 'cn-row-action--destructive': action.destructive }\"\n\t\t\t\t\t:data-testid=\"`cn-action-item-${slugifyLabel(action.label)}`\"\n\t\t\t\t\tcloseAfterClick\n\t\t\t\t\t@click=\"onAction(action)\">\n\t\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t\t<CnIcon v-if=\"typeof action.icon === 'string'\" :name=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t\t<component :is=\"action.icon\" v-else :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ action.label }}\n\t\t\t\t</NcActionButton>\n\t\t\t</template>\n\n\t\t\t<!--\n\t\t\t@slot default\n\t\t\t@description Custom NcActionButton-family content rendered inside the\n\t\t\tNcActions menu. Use this for hardcoded buttons (Keepiq pattern) when\n\t\t\tthe `actions` prop is empty.\n\t\t-->\n\t\t\t<slot />\n\t\t</NcActions>\n\n\t\t<!-- CUSTOM PANEL — bypass NcActions, render arbitrary slot content\n\t\t anchored at the cursor. The `#panel:<name>` slot may contain any\n\t\t markup (grids, inputs, custom components) without the NcActions\n\t\t child-allowlist filter. -->\n\t\t<template v-if=\"activePanel && internalOpen\">\n\t\t\t<div\n\t\t\t\tclass=\"cn-context-menu__backdrop\"\n\t\t\t\t@click=\"onClose\"\n\t\t\t\t@contextmenu.prevent=\"onClose\" />\n\t\t\t<div\n\t\t\t\tref=\"panel\"\n\t\t\t\tclass=\"cn-context-menu__panel\"\n\t\t\t\trole=\"menu\"\n\t\t\t\ttabindex=\"-1\"\n\t\t\t\tdata-testid=\"cn-context-menu-panel\"\n\t\t\t\t:data-panel=\"activePanel\"\n\t\t\t\t@keydown.esc.stop=\"onClose\"\n\t\t\t\t@click.stop>\n\t\t\t\t<!-- @slot panel:<name> Free-form custom panel content shown when\n\t\t\t\t `activePanel === '<name>'`. Bypasses the NcActions child\n\t\t\t\t allowlist — put any markup here (grids, inputs, custom\n\t\t\t\t components, etc.). The slot name is dynamic: define one\n\t\t\t\t `#panel:<name>` per panel you want to support. -->\n\t\t\t\t<!-- @binding {Function} back Clear `activePanel`, returning to the default action list. -->\n\t\t\t\t<!-- @binding {Function} close Close the entire menu, equivalent to clicking outside. -->\n\t\t\t\t<!-- @binding {*} targetItem The right-clicked item, forwarded from the `targetItem` prop. -->\n\t\t\t\t<slot\n\t\t\t\t\t:name=\"`panel:${activePanel}`\"\n\t\t\t\t\t:back=\"back\"\n\t\t\t\t\t:close=\"onClose\"\n\t\t\t\t\t:targetItem=\"targetItem\" />\n\t\t\t</div>\n\t\t</template>\n\t</div>\n</template>\n\n<script>\nimport { NcActionButton, NcActionLink, NcActions } from '@nextcloud/vue'\nimport { CTX_MENU_DATA_ATTR, CTX_MENU_POPPER_ATTR } from '../../composables/useContextMenu.js'\nimport { followItemActionLink, resolveItemActionLink } from '../../utils/actionLink.js'\nimport { isModifiedClick } from '../../utils/linkNavigation.js'\nimport { CnIcon } from '../CnIcon/index.js'\n\n/**\n * CnContextMenu — Right-click context menu wrapper around NcActions.\n *\n * Provides a pre-configured NcActions instance that positions itself at the\n * cursor via the `useContextMenu` composable's CSS custom properties. Accepts\n * an `actions` array for the common dynamic case (like CnRowActions), and a\n * default slot for hardcoded NcActionButton content.\n *\n * Pair with `useContextMenu()` for state management (open/close, target item,\n * cursor positioning). The composable handles the DOM attributes; this component\n * handles the NcActions template boilerplate.\n *\n * Dynamic actions (CnIndexPage pattern)\n * ```vue\n * <CnContextMenu\n * v-model:open=\"contextMenuOpen\"\n * :actions=\"mergedActions\"\n * :target-item=\"contextMenuRow\"\n * @action=\"$emit('action', $event)\"\n * @close=\"closeContextMenu\" />\n * ```\n *\n * Custom buttons via slot (Keepiq pattern)\n * ```vue\n * <CnContextMenu\n * v-model:open=\"contextMenuOpen\"\n * @close=\"closeContextMenu\">\n * <NcActionButton close-after-click @click=\"onRename\">\n * <template #icon><PencilIcon :size=\"20\" /></template>\n * Rename\n * </NcActionButton>\n * </CnContextMenu>\n * ```\n */\nexport default {\n\tname: 'CnContextMenu',\n\n\tcomponents: {\n\t\tNcActions,\n\t\tNcActionButton,\n\t\tNcActionLink,\n\t\tCnIcon,\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Whether the context menu is open. Bind with `v-model:open` — the `.sync`\n\t\t * modifier it replaces was removed in Vue 3 and binds nothing.\n\t\t * Bind to `useContextMenu().isOpen`.\n\t\t */\n\t\topen: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Action definitions rendered as NcActionButton items.\n\t\t * Same format as CnRowActions: `{ label, icon?, handler?, disabled?, visible?, title?, destructive? }`.\n\t\t * `icon` accepts either a component (rendered directly) or a string —\n\t\t * a string is treated as an MDI name and rendered via `CnIcon` (e.g.\n\t\t * `\"Eye\"`), which lets manifest (JSON) actions declare icons by name.\n\t\t * `visible` (boolean | (targetItem) => boolean) hides the entry when falsy\n\t\t * (default: shown). `title` (string | (targetItem) => string) renders as\n\t\t * a native tooltip — useful for explaining why an entry is disabled.\n\t\t * `href` (URL) or `to` (vue-router location), each a value or a\n\t\t * `(targetItem) => …` function, render the entry as a real link, with\n\t\t * `linkTarget` as its `target`; a link emits `action` but does not call\n\t\t * `handler`. When the entire array is empty (or all entries are filtered\n\t\t * out), only the default slot content is rendered.\n\t\t *\n\t\t * @type {Array<{label: string, icon: object | string, handler: (targetItem: object) => void, disabled: boolean | ((targetItem: object) => boolean), visible: boolean | ((targetItem: object) => boolean), title: string | ((targetItem: object) => string), destructive: boolean, href: string | ((targetItem: object) => string), to: string | object | ((targetItem: object) => string | object), linkTarget: string}>}\n\t\t */\n\t\tactions: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * The right-clicked item (row, folder, etc.). Passed to action `handler`\n\t\t * and `disabled` callbacks, and included in the `action` event payload.\n\t\t * Bind to `useContextMenu().targetItem`.\n\t\t */\n\t\ttargetItem: {\n\t\t\ttype: [Object, String, Number],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Name of the currently-active custom panel. When set (and the menu is\n\t\t * open), the default NcActions list is replaced by the matching\n\t\t * `#panel:<name>` slot, rendered free-form (no NcActions child filter).\n\t\t * Bind with `v-model:active-panel` — the component emits `update:activePanel(null)` when\n\t\t * the panel's `back()` binding is invoked or the menu closes.\n\t\t *\n\t\t * @type {string|null}\n\t\t */\n\t\tactivePanel: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: ['action', 'close', 'closed', 'update:activePanel', 'update:open'],\n\n\tdata() {\n\t\treturn {\n\t\t\tinternalOpen: this.open,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Filter actions by their `visible` predicate. Entries without\n\t\t * `visible` are always shown (backwards compatible).\n\t\t *\n\t\t * @return {Array} Visible actions for the current targetItem.\n\t\t */\n\t\tvisibleActions() {\n\t\t\treturn this.actions.filter((action) => {\n\t\t\t\tif (action.visible === undefined) {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tif (typeof action.visible === 'function') {\n\t\t\t\t\treturn !!action.visible(this.targetItem)\n\t\t\t\t}\n\t\t\t\treturn !!action.visible\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Visible actions paired with the link each renders as (null for a button).\n\t\t *\n\t\t * @return {Array<{action: object, link: object|null}>}\n\t\t */\n\t\trenderedActions() {\n\t\t\treturn this.visibleActions.map((action) => ({\n\t\t\t\taction,\n\t\t\t\tlink: this.resolveDisabled(action) ? null : resolveItemActionLink(action, this.targetItem, this.$router),\n\t\t\t}))\n\t\t},\n\t},\n\n\twatch: {\n\t\topen(val) {\n\t\t\tthis.internalOpen = val\n\t\t},\n\n\t\tinternalOpen(val) {\n\t\t\t// Description goes ABOVE `@event`, not inline after it:\n\t\t\t// vue-docgen-api's event-name splitter stops at the first `:`, so\n\t\t\t// `@event update:open <description>` is read as one long event NAME\n\t\t\t// and the generated docs show an empty description.\n\t\t\t/**\n\t\t\t * Fired whenever the menu's internal open state flips. Used by\n\t\t\t * callers that bind `v-model:open` to track visibility.\n\t\t\t *\n\t\t\t * @event update:open\n\t\t\t * @type {boolean}\n\t\t\t */\n\t\t\tthis.$emit('update:open', val)\n\n\t\t\tif (val) {\n\t\t\t\tthis.tagPopper()\n\t\t\t\tthis.startOutsideWatch()\n\t\t\t} else {\n\t\t\t\tthis.stopOutsideWatch()\n\t\t\t\t// NcActions' `@closed` never fires (see `onClosed`), so drop the\n\t\t\t\t// \"a context menu is open\" attribute here. The CSS vars stay put\n\t\t\t\t// — the hide animation still needs the transform, and the next\n\t\t\t\t// `open()` overwrites them before the popper shows again.\n\t\t\t\tdocument.documentElement.removeAttribute(CTX_MENU_DATA_ATTR)\n\t\t\t}\n\t\t},\n\t},\n\n\tmounted() {\n\t\t// NcActions renders its own NcButton as the popover trigger; the menu\n\t\t// opens only via right-click (via `:open.sync`), so the button is\n\t\t// offscreen-positioned (see the `.cn-context-menu` rule below).\n\t\t//\n\t\t// Use `inert` rather than `aria-hidden`: when the menu is dismissed\n\t\t// without picking an item, NcPopover's focus-trap restores focus to the\n\t\t// trigger button. With `aria-hidden` that produces a \"Blocked\n\t\t// aria-hidden on an element because its descendant retained focus\"\n\t\t// warning (focused element must not be hidden from AT). `inert` both\n\t\t// hides the button from the accessibility tree *and* makes it\n\t\t// unfocusable, so the trap's focus restore falls back to <body> instead\n\t\t// of landing on a hidden element. (The button never needs focus — the\n\t\t// menu opens via `:open.sync`, not by focusing the trigger.)\n\t\tconst trigger = this.$el?.querySelector('.action-item__menutoggle')\n\t\tif (trigger) {\n\t\t\ttrigger.setAttribute('inert', '')\n\t\t}\n\n\t\t// Silence NcPopover's dev-mode a11y warning for this specific instance.\n\t\t//\n\t\t// NcPopover (when `window.OC?.debug` is on) calls\n\t\t// `tabbable(triggerContainer)[0]` on every open and warns\n\t\t// \"It looks like you are using a custom button as a <NcPopover>\n\t\t// trigger…\" if nothing tabbable is found. We *are* using NcButton —\n\t\t// the warning's escape hatch — but the button is offscreen-positioned\n\t\t// and tabbable's visibility checks won't reliably find it across\n\t\t// browsers (Chromium's `Element.checkVisibility()` excludes some\n\t\t// offscreen / clipped elements). The advice in the warning (\"bind\n\t\t// #trigger slot attrs\") doesn't apply: NcActions controls its own\n\t\t// trigger and doesn't expose a `#trigger` slot to override.\n\t\t//\n\t\t// Neutralising the check on our own NcPopover instance (one level\n\t\t// down inside NcActions) leaves every other popover untouched and\n\t\t// keeps the check live for real custom triggers elsewhere.\n\t\t//\n\t\t// Vue 3: `this.$children` was removed (it hard-errors under @vue/compat\n\t\t// MODE 3 with \"INSTANCE_CHILDREN compat has been disabled\"), so reach the\n\t\t// NcActions instance through an explicit template ref instead.\n\t\tconst ncActions = this.$refs.actions\n\t\tconst ncPopover = ncActions?.$refs?.popover\n\t\tif (ncPopover) {\n\t\t\tncPopover.checkTriggerA11y = () => {}\n\t\t}\n\n\t\tthis.tagPopper()\n\n\t\t// Mounting with `open` already true never trips the watcher.\n\t\tif (this.internalOpen) {\n\t\t\tthis.startOutsideWatch()\n\t\t}\n\t},\n\n\tbeforeUnmount() {\n\t\tthis.stopOutsideWatch()\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Mark this menu's own popper element so the cursor-positioning CSS in\n\t\t * `src/css/context-menu.css` can target it — and nothing else.\n\t\t *\n\t\t * That CSS used to key on the `<html>` data attribute alone, which\n\t\t * matches *every* `.v-popper__popper` in the document: any other popover\n\t\t * alive while a context menu was open (a table row's actions menu, most\n\t\t * visibly) got dragged to the last right-click coordinates.\n\t\t *\n\t\t * The marker has to be applied imperatively — NcActions hardcodes\n\t\t * `popoverBaseClass: 'action-item__popper'` on its NcPopover and spreads\n\t\t * no `$attrs` onto it, so there is no prop to pass one in through.\n\t\t *\n\t\t * It must be an *attribute*, not a class. floating-vue binds a dynamic\n\t\t * `class` on this element (`--shown` / `--hidden` / `--show-from` / …)\n\t\t * and Vue's `patchClass` assigns `el.className` wholesale, so a class\n\t\t * added here is wiped the first time the popper opens — the rule then\n\t\t * matches nothing and the menu renders at its offscreen trigger. Vue\n\t\t * only patches props present in the vnode, so an attribute it never\n\t\t * renders is left alone.\n\t\t *\n\t\t * The element itself is stable: floating-vue keeps one `$_popperNode`\n\t\t * and `appendChild`s it into the container on first show, so marking it\n\t\t * once at `mounted()` holds for the component's lifetime. Re-running on\n\t\t * open is a cheap, idempotent guard for a not-yet-ready ref chain.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\ttagPopper() {\n\t\t\tconst el = this.$refs.actions?.$refs?.popover?.getPopoverContentElement?.()\n\t\t\tel?.setAttribute?.(CTX_MENU_POPPER_ATTR, '')\n\t\t},\n\n\t\t/**\n\t\t * Start watching for a press outside the menu. Driven from both the\n\t\t * `internalOpen` watcher and `mounted()`, because a consumer may mount\n\t\t * with `open` already true — in which case there is no state transition\n\t\t * for the watcher to see.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tstartOutsideWatch() {\n\t\t\t// nextTick: the contextmenu/click that opened the menu is still\n\t\t\t// propagating and would dismiss it again on the same gesture.\n\t\t\tthis.$nextTick(() => {\n\t\t\t\tif (this.internalOpen) {\n\t\t\t\t\t// CAPTURE. A canvas that swallows its own mousedown never lets it\n\t\t\t\t\t// bubble to document, so a menu opened over one could not be\n\t\t\t\t\t// dismissed by clicking the canvas — Vue Flow's d3-zoom pane calls\n\t\t\t\t\t// stopImmediatePropagation, which is exactly that shape.\n\t\t\t\t\tdocument.addEventListener('mousedown', this.onDocumentMouseDown, true)\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Stop watching for outside presses.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tstopOutsideWatch() {\n\t\t\tdocument.removeEventListener('mousedown', this.onDocumentMouseDown, true)\n\t\t},\n\n\t\t/**\n\t\t * Close the menu when the press lands outside it.\n\t\t *\n\t\t * NcActions has to run in `manual-open` mode here (the menu is opened\n\t\t * from a right-click via the `open` prop, not by activating the trigger\n\t\t * button). `@nextcloud/vue` 9 couples that mode to\n\t\t * `noCloseOnClickOutside: this.manualOpen`, and NcPopover derives\n\t\t * `autoHide: !noCloseOnClickOutside && closeOnClickOutside` — so the\n\t\t * popper's own outside-click dismissal is switched off and nothing else\n\t\t * closes the menu. v8 only cleared the popper's open `triggers`, leaving\n\t\t * autoHide intact, which is why dismissing worked before the Vue 3\n\t\t * upgrade. NcActions spreads no `$attrs` onto NcPopover, so the prop\n\t\t * cannot be overridden from outside — hence handling it here.\n\t\t *\n\t\t * @param {MouseEvent} event The document mousedown.\n\t\t * @return {void}\n\t\t */\n\t\tonDocumentMouseDown(event) {\n\t\t\tif (!this.internalOpen || this.isInsideMenu(event.target)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.onClose()\n\t\t},\n\n\t\t/**\n\t\t * Whether a node sits inside the menu's own surface — the custom panel,\n\t\t * or the popper NcActions teleports to `container=\"body\"` (so it is not\n\t\t * inside `this.$el` and `contains()` on the root would miss it).\n\t\t *\n\t\t * @param {EventTarget} target The event target to test.\n\t\t * @return {boolean} True when the target belongs to this menu.\n\t\t */\n\t\tisInsideMenu(target) {\n\t\t\tif (!(target instanceof Node)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif (this.$refs.panel?.contains(target)) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tconst content = this.$refs.actions?.$refs?.popover?.getPopoverContentElement?.()\n\t\t\tif (content?.contains(target)) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\t// Fallback for when the popover ref chain is unavailable —\n\t\t\t// `action-item__popper` is the base class NcActions gives its popper.\n\t\t\treturn target instanceof Element && !!target.closest('.action-item__popper')\n\t\t},\n\n\t\tresolveDisabled(action) {\n\t\t\tif (typeof action.disabled === 'function') {\n\t\t\t\treturn action.disabled(this.targetItem)\n\t\t\t}\n\t\t\treturn !!action.disabled\n\t\t},\n\n\t\t/**\n\t\t * Resolve the `title` field on an action descriptor — supports both\n\t\t * a static string and a function `(targetItem) => string`. Returns\n\t\t * undefined when no title is provided so the attribute isn't rendered.\n\t\t *\n\t\t * @param {object} action The action descriptor.\n\t\t * @return {string|undefined} The tooltip text, or undefined.\n\t\t */\n\t\tresolveTitle(action) {\n\t\t\tif (typeof action.title === 'function') {\n\t\t\t\treturn action.title(this.targetItem) || undefined\n\t\t\t}\n\t\t\treturn action.title || undefined\n\t\t},\n\n\t\t/**\n\t\t * Slugify an action label for use in stable `data-testid` selectors.\n\t\t * Lowercase, kebab-case, strip non-alphanumeric. Used solely by the\n\t\t * `:data-testid` binding on NcActionButton — does not affect runtime\n\t\t * behaviour or rendered text.\n\t\t *\n\t\t * @param {string} label The action's display label.\n\t\t * @return {string} kebab-case slug suitable for a testid suffix.\n\t\t */\n\t\tslugifyLabel(label) {\n\t\t\treturn String(label || '')\n\t\t\t\t.toLowerCase()\n\t\t\t\t.replace(/[^a-z0-9]+/g, '-')\n\t\t\t\t.replace(/^-+|-+$/g, '')\n\t\t},\n\n\t\tonAction(action) {\n\t\t\tif (action.handler && typeof action.handler === 'function') {\n\t\t\t\taction.handler(this.targetItem)\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event action User picked an entry from the menu. The action's own `handler(targetItem)` (when present) ran synchronously before this event fires; the event lets parents observe / log the choice.\n\t\t\t * @type {{ action: string, row: object|null }}\n\t\t\t */\n\t\t\tthis.$emit('action', { action: action.label, row: this.targetItem })\n\t\t},\n\n\t\t/**\n\t\t * A link entry was clicked: route a plain in-app click, leave the rest to\n\t\t * the browser, and emit `action` as a button would. The `handler` is not\n\t\t * called, since navigating is what the link already does. A modified\n\t\t * click opens a new tab, so nothing is emitted in this one.\n\t\t *\n\t\t * @param {object} action The action descriptor.\n\t\t * @param {object} link The resolved link.\n\t\t * @param {MouseEvent} event The click event.\n\t\t */\n\t\tonLinkAction(action, link, event) {\n\t\t\tif (isModifiedClick(event)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfollowItemActionLink(event, link, this.$router)\n\t\t\tthis.$emit('action', { action: action.label, row: this.targetItem })\n\t\t},\n\n\t\tonClose() {\n\t\t\tthis.internalOpen = false\n\t\t\tif (this.activePanel) {\n\t\t\t\tthis.$emit('update:activePanel', null)\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event close Fired when the menu starts closing (before the popper's hide animation). This is the reliable one — prefer it over `closed`.\n\t\t\t */\n\t\t\tthis.$emit('close')\n\t\t},\n\n\t\t/**\n\t\t * Clear the active panel and return to the default action list.\n\t\t * Exposed to custom panel slots via the `back` scope binding.\n\t\t */\n\t\tback() {\n\t\t\t// Description above the tag — see the note on `update:open`.\n\t\t\t/**\n\t\t\t * Emitted with `null` to clear the active panel — fired by the\n\t\t\t * panel's `back()` binding and whenever the menu closes while a\n\t\t\t * panel is open. Bind with `v-model:active-panel`.\n\t\t\t *\n\t\t\t * @event update:activePanel\n\t\t\t * @type {null}\n\t\t\t */\n\t\t\tthis.$emit('update:activePanel', null)\n\t\t},\n\n\t\t/**\n\t\t * Fires after the popper's hide animation completes.\n\t\t *\n\t\t * IT MUST NOT CLEAR THE CURSOR POSITION, and it used to. This fires LATE\n\t\t * — a whole hide animation late — so a second click on the same target\n\t\t * reopens the menu before it arrives, and the tear-down then strips the\n\t\t * coordinates that reopen had just written. The transform resolves\n\t\t * against undefined vars and the menu lands in the corner, on every open\n\t\t * after the first.\n\t\t *\n\t\t * It was dead code when written: `@nextcloud/vue` 9 bound `onAfterClose`\n\t\t * against an NcPopover that only emitted `afterHide`. 9.11.0 binds\n\t\t * `onAfterHide`, so it runs now, and a tear-down nothing needed became a\n\t\t * bug. Nothing needs it still: positioning is scoped to this menu's own\n\t\t * popper (see `tagPopper`), the data attribute is dropped in the\n\t\t * `internalOpen` watcher, the next `open()` overwrites the vars, and the\n\t\t * composable's unmount hook clears them for good.\n\t\t */\n\t\tonClosed() {\n\t\t\t/**\n\t\t\t * @event closed Intended to fire after the popper's hide animation completes. Does not currently fire — `@nextcloud/vue` 9's NcActions listens for an `afterClose` event NcPopover never emits. Use `close` instead.\n\t\t\t */\n\t\t\tthis.$emit('closed')\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n/* Selector is deliberately two classes deep. `.cn-context-menu` lands on\n NcActions' root, which NcActions itself styles as `.action-item[data-v-…]\n { position: relative; display: inline-block }`. Scoped CSS appends the attr\n to the last compound only, so a bare `.cn-context-menu` tied that rule at\n specificity (0,2,0) and lost on injection order — the trigger stayed\n `position: relative`, so it kept its in-flow box (top/left just offset it\n visually) and showed up as a full-width ~34px-tall phantom row between the\n table and the pagination on CnIndexPage. Adding the parent class makes this\n (0,3,0) so it wins regardless of order. */\n.cn-context-menu-root > .cn-context-menu {\n\t/* Hide the NcActions trigger button — menu opens only via right-click.\n\t Off-screen rather than display:none / visibility:hidden so NcPopover's\n\t a11y check (`tabbable(triggerContainer)[0]`) still finds the NcButton\n\t and the \"custom button as a <NcPopover> trigger\" warning stays quiet.\n\t Avoid `clip` / `clip-path` here — modern browsers' `Element.checkVisibility()`,\n\t which tabbable calls into, treats those as invisible. Pure offscreen\n\t positioning leaves the element fully \"visible\" to layout while being\n\t off the user's screen. */\n\tposition: absolute;\n\ttop: -9999px;\n\tleft: -9999px;\n\topacity: 0;\n\tpointer-events: none;\n}\n\n.cn-context-menu__backdrop {\n\tposition: fixed;\n\tinset: 0;\n\tz-index: 9998;\n}\n\n.cn-context-menu__panel {\n\tposition: fixed;\n\ttop: var(--cn-ctx-menu-y, 0);\n\tleft: var(--cn-ctx-menu-x, 0);\n\tz-index: 9999;\n\tmin-width: 220px;\n\tpadding: 4px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-large, 12px);\n\tbox-shadow: 0 1px 5px rgba(0, 0, 0, 0.2);\n\toutline: none;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createBlock","_Fragment","_renderList","_normalizeClass","_toDisplayString","_resolveDynamicComponent","_renderSlot","_createElementVNode"],"mappings":"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"}
@@ -3,9 +3,9 @@ import { translate } from '@nextcloud/l10n';
3
3
  import { generateUrl } from '@nextcloud/router';
4
4
  import { NcLoadingIcon, NcCheckboxRadioSwitch } from '@nextcloud/vue';
5
5
  import { useClickDragGuard } from '../../composables/useClickDragGuard.js';
6
- import { openRowTarget } from '../../utils/linkNavigation.js';
6
+ import { resolveHref, openRowTarget, followLinkClick } from '../../utils/linkNavigation.js';
7
7
  import { nextSortState } from '../../utils/multiColumnSort.js';
8
- import { isRowMiddleClick, isNewTabHandled, markNewTabHandled } from '../../utils/rowAuxClick.js';
8
+ import { preventMiddleClickAutoscroll, isRowMiddleClick, isNewTabHandled, markNewTabHandled, isNewTabClick } from '../../utils/rowAuxClick.js';
9
9
  import { DEFAULT_ROW_INDICATOR_CAP, resolveRowIndicators } from '../../utils/rowIndicators.js';
10
10
  import { columnsFromSchema } from '../../utils/schema.js';
11
11
  import __sfc_main from '../CnCellRenderer/CnCellRenderer.vue.js';
@@ -416,9 +416,11 @@ var script = {
416
416
 
417
417
  /**
418
418
  * Convenience navigation (folded from CnTableWidget): a function that
419
- * receives the clicked row and returns a vue-router route to push. When
420
- * set, a row click navigates there (the `row-click` event still fires);
421
- * a ctrl/cmd/shift or middle click opens it in a new tab.
419
+ * receives a row and returns the vue-router route it opens. When set,
420
+ * each row renders as a real link to that route, stretched over the
421
+ * row: a click navigates there (the `row-click` event still fires), and
422
+ * a ctrl/cmd/shift or middle click opens it in a new tab. A table whose
423
+ * row click selects (`selectable` without `rowClickToView`) gets no link.
422
424
  *
423
425
  * @type {((row: object) => object)|null}
424
426
  */
@@ -441,7 +443,7 @@ var script = {
441
443
  },
442
444
  },
443
445
 
444
- emits: ['row-click', 'row-context-menu', 'select', 'select-all', 'sort', 'view-all'],
446
+ emits: ['row-click', 'row-aux-click', 'row-context-menu', 'select', 'select-all', 'sort', 'view-all'],
445
447
 
446
448
  setup() {
447
449
  // Tell a deliberate row click apart from a text-selection drag.
@@ -591,6 +593,32 @@ var script = {
591
593
  *
592
594
  * @return {Array<{key: string, order: 'asc'|'desc'}>}
593
595
  */
596
+ /**
597
+ * The link each row opens, keyed by row key: the `rowClickRoute`
598
+ * location and its href. Empty when rows have no route, or when a row
599
+ * click selects instead of navigating.
600
+ *
601
+ * @return {{[key: string]: {target: object, href: string}}}
602
+ */
603
+ rowLinks() {
604
+ const links = {};
605
+ if (!this.rowClickRoute || !this.$router || (this.selectable && !this.rowClickToView)) {
606
+ return links
607
+ }
608
+ for (const row of this.effectiveRows) {
609
+ const route = this.rowClickRoute(row);
610
+ if (!route) {
611
+ continue
612
+ }
613
+ const target = typeof route === 'string' ? { path: route } : route;
614
+ const href = resolveHref(target, this.$router);
615
+ if (href) {
616
+ links[String(row[this.rowKey])] = { target, href };
617
+ }
618
+ }
619
+ return links
620
+ },
621
+
594
622
  effectiveSortKeys() {
595
623
  if (this.sortKeys && this.sortKeys.length > 0) {
596
624
  return this.sortKeys
@@ -1067,9 +1095,23 @@ var script = {
1067
1095
  },
1068
1096
 
1069
1097
  /**
1070
- * Row-body middle click: emits `row-click` like a click, so the host
1071
- * (or `rowClickRoute`) can open the row in a new tab. Ignored for other
1072
- * buttons, nested controls, drags and select-on-click tables.
1098
+ * Row mousedown: record the press for the drag guard, and on a row a
1099
+ * middle click opens, keep the browser from starting autoscroll.
1100
+ *
1101
+ * @param {MouseEvent} event The mousedown event.
1102
+ */
1103
+ onRowMouseDown(event) {
1104
+ this.onPointerDown(event);
1105
+ if (!this.selectable || this.rowClickToView) {
1106
+ preventMiddleClickAutoscroll(event);
1107
+ }
1108
+ },
1109
+
1110
+ /**
1111
+ * Row-body middle click: emits `row-aux-click`, not `row-click`, so an
1112
+ * existing `row-click` listener that navigates never moves the current
1113
+ * tab away; `rowClickRoute` still opens the row in a new tab. Ignored
1114
+ * for other buttons, nested controls, drags and select-on-click tables.
1073
1115
  *
1074
1116
  * @param {object} row The clicked row object
1075
1117
  * @param {MouseEvent} event The originating auxclick event.
@@ -1081,23 +1123,39 @@ var script = {
1081
1123
  if (this.selectable && !this.rowClickToView) {
1082
1124
  return
1083
1125
  }
1084
- this.emitRowClick(row, event);
1126
+ /**
1127
+ * @event row-aux-click Emitted on a row-body middle click, under the same conditions as `row-click`, so a host can open the row in a new tab (see `openRowTarget`). Payload: `(row, event)` — the clicked row object and the native auxclick event.
1128
+ * @type {object} The clicked row object.
1129
+ */
1130
+ this.$emit('row-aux-click', row, event);
1131
+ this.followRowClickRoute(row, event);
1085
1132
  },
1086
1133
 
1087
1134
  /**
1088
1135
  * Emit `row-click` and follow `rowClickRoute` when set.
1089
1136
  *
1090
1137
  * @param {object} row The clicked row object
1091
- * @param {MouseEvent} [event] The originating click or auxclick event.
1138
+ * @param {MouseEvent} [event] The originating click event.
1092
1139
  */
1093
1140
  emitRowClick(row, event) {
1094
1141
  /**
1095
- * @event row-click Emitted on a row-body click for navigation, and on a middle click (auxclick). Fires when `selectable` is false, OR when `rowClickToView` is set (selection then happens via the checkbox column). Payload: `(row, event)` — the clicked row object and the native click/auxclick event, so a host can open the row in a new tab on a ctrl/cmd/shift or middle click (see `openRowTarget`).
1142
+ * @event row-click Emitted on a row-body click for navigation. Fires when `selectable` is false, OR when `rowClickToView` is set (selection then happens via the checkbox column). Payload: `(row, event)` — the clicked row object and the native click event, so a host can open the row in a new tab on a ctrl/cmd/shift click (see `openRowTarget`). A middle click emits `row-aux-click` instead.
1096
1143
  * @type {object} The clicked row object.
1097
1144
  */
1098
1145
  this.$emit('row-click', row, event);
1099
- // A ctrl/cmd/shift or middle click opens the route in a new tab,
1100
- // unless a row-click listener already did.
1146
+ this.followRowClickRoute(row, event);
1147
+ },
1148
+
1149
+ /**
1150
+ * Follow `rowClickRoute` for a row click: in place, or in a new tab on
1151
+ * a ctrl/cmd/shift or middle click.
1152
+ *
1153
+ * @param {object} row The clicked row object
1154
+ * @param {MouseEvent} [event] The originating click or auxclick event.
1155
+ */
1156
+ followRowClickRoute(row, event) {
1157
+ // A new-tab click opens the route in a new tab, unless a listener
1158
+ // already did.
1101
1159
  if (this.rowClickRoute && this.$router && !isNewTabHandled(event)) {
1102
1160
  const route = this.rowClickRoute(row);
1103
1161
  if (route) {
@@ -1106,14 +1164,57 @@ var script = {
1106
1164
  }
1107
1165
  },
1108
1166
 
1167
+ /**
1168
+ * A plain or alt click on a row link routes in place; a new-tab click
1169
+ * is left to the browser, which opens the href itself.
1170
+ *
1171
+ * @param {object} row The row the link belongs to.
1172
+ * @param {MouseEvent} event The click event.
1173
+ */
1174
+ onRowLinkClick(row, event) {
1175
+ const link = this.rowLinks[String(row[this.rowKey])];
1176
+ if (!link) {
1177
+ return
1178
+ }
1179
+ // Alt-click on a link downloads it; a row opens like a plain click.
1180
+ if (event.altKey && !isNewTabClick(event) && !event.defaultPrevented) {
1181
+ event.preventDefault();
1182
+ this.$router.push(link.target).catch(() => {});
1183
+ return
1184
+ }
1185
+ followLinkClick(event, link.target, this.$router);
1186
+ },
1187
+
1188
+ /**
1189
+ * The row link's accessible name: the row's first cell when it holds
1190
+ * plain text, else a generic name.
1191
+ *
1192
+ * @param {object} row The row.
1193
+ * @param {object} col The first data column.
1194
+ * @return {string}
1195
+ */
1196
+ rowLinkLabel(row, col) {
1197
+ const value = this.cellValue(row, col);
1198
+ if (['string', 'number'].includes(typeof value) && String(value).trim() !== '') {
1199
+ return String(value)
1200
+ }
1201
+ return translate('nextcloud-vue', 'Open row')
1202
+ },
1203
+
1109
1204
  /**
1110
1205
  * Row right-click: forward the row + originating event so a host can
1111
- * open a context menu (the browser default is prevented).
1206
+ * open a context menu. The browser menu is prevented, except on a row
1207
+ * link when no host listens, so its link actions stay available.
1112
1208
  *
1113
1209
  * @param {object} row The right-clicked row object.
1114
1210
  * @param {MouseEvent} event The originating contextmenu event.
1115
1211
  */
1116
1212
  onRowContextMenu(row, event) {
1213
+ const fromRowLink = event.target?.closest?.('.cn-table-row__link');
1214
+ // `$.vnode.props`, not `$attrs`: a declared emit is stripped from `$attrs`.
1215
+ if (!fromRowLink || this.$.vnode.props?.onRowContextMenu) {
1216
+ event.preventDefault();
1217
+ }
1117
1218
  /**
1118
1219
  * @event row-context-menu Emitted on a row right-click (contextmenu) for hosts that render a context menu.
1119
1220
  * @type {{ row: object, event: MouseEvent }}