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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (314) hide show
  1. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js +12 -2
  2. package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
  3. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +1 -2
  4. package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
  5. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue2.js.map +1 -1
  6. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js +0 -3
  7. package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js.map +1 -1
  8. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js.map +1 -1
  9. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js +1 -2
  10. package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js.map +1 -1
  11. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +21 -1
  12. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  13. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +1 -1
  14. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  15. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js +12 -5
  16. package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
  17. package/dist/esm/components/CnBoardView/CnBoardView.vue3.js +2 -1
  18. package/dist/esm/components/CnBoardView/CnBoardView.vue3.js.map +1 -1
  19. package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js +9 -1
  20. package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js.map +1 -1
  21. package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js.map +1 -1
  22. package/dist/esm/components/CnContextMenu/CnContextMenu.vue2.js +6 -1
  23. package/dist/esm/components/CnContextMenu/CnContextMenu.vue2.js.map +1 -1
  24. package/dist/esm/components/CnContextMenu/CnContextMenu.vue3.js.map +1 -1
  25. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +116 -15
  26. package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
  27. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +27 -14
  28. package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
  29. package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue2.js +13 -6
  30. package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue2.js.map +1 -1
  31. package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue3.js +2 -0
  32. package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue3.js.map +1 -1
  33. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +29 -1
  34. package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
  35. package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
  36. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js.map +1 -1
  37. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js +1 -2
  38. package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js.map +1 -1
  39. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +3 -1
  40. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
  41. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +17 -17
  42. package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
  43. package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue2.js +4 -21
  44. package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue2.js.map +1 -1
  45. package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue3.js.map +1 -1
  46. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js +0 -13
  47. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js.map +1 -1
  48. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue3.js +8 -8
  49. package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue3.js.map +1 -1
  50. package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue2.js.map +1 -1
  51. package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue3.js +3 -2
  52. package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue3.js.map +1 -1
  53. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +67 -13
  54. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  55. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +15 -8
  56. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  57. package/dist/esm/components/CnIndexPage/manifestActionDispatch.js +31 -11
  58. package/dist/esm/components/CnIndexPage/manifestActionDispatch.js.map +1 -1
  59. package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue2.js.map +1 -1
  60. package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue3.js +1 -2
  61. package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue3.js.map +1 -1
  62. package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue2.js.map +1 -1
  63. package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue3.js +0 -1
  64. package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue3.js.map +1 -1
  65. package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue2.js +2 -17
  66. package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue2.js.map +1 -1
  67. package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue3.js.map +1 -1
  68. package/dist/esm/components/CnLogsPage/CnLogsPage.vue2.js +35 -11
  69. package/dist/esm/components/CnLogsPage/CnLogsPage.vue2.js.map +1 -1
  70. package/dist/esm/components/CnLogsPage/CnLogsPage.vue3.js +3 -2
  71. package/dist/esm/components/CnLogsPage/CnLogsPage.vue3.js.map +1 -1
  72. package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js +23 -5
  73. package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js.map +1 -1
  74. package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js +1 -1
  75. package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js.map +1 -1
  76. package/dist/esm/components/CnObjectList/CnObjectList.vue2.js +19 -3
  77. package/dist/esm/components/CnObjectList/CnObjectList.vue2.js.map +1 -1
  78. package/dist/esm/components/CnObjectList/CnObjectList.vue3.js.map +1 -1
  79. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +45 -15
  80. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
  81. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +4 -2
  82. package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
  83. package/dist/esm/components/CnObjectRow/CnObjectRow.vue2.js +24 -6
  84. package/dist/esm/components/CnObjectRow/CnObjectRow.vue2.js.map +1 -1
  85. package/dist/esm/components/CnObjectRow/CnObjectRow.vue3.js +1 -1
  86. package/dist/esm/components/CnObjectRow/CnObjectRow.vue3.js.map +1 -1
  87. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
  88. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +0 -1
  89. package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
  90. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue2.js.map +1 -1
  91. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js +4 -2
  92. package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js.map +1 -1
  93. package/dist/esm/components/CnRowActions/CnRowActions.vue2.js +7 -2
  94. package/dist/esm/components/CnRowActions/CnRowActions.vue2.js.map +1 -1
  95. package/dist/esm/components/CnRowActions/CnRowActions.vue3.js.map +1 -1
  96. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js +49 -5
  97. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
  98. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js +1 -1
  99. package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
  100. package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue2.js +5 -3
  101. package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue2.js.map +1 -1
  102. package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue3.js.map +1 -1
  103. package/dist/esm/components/CnTabs/CnTabs.vue2.js.map +1 -1
  104. package/dist/esm/components/CnTabs/CnTabs.vue3.js.map +1 -1
  105. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js +3 -1
  106. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js.map +1 -1
  107. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js +1 -0
  108. package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js.map +1 -1
  109. package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue2.js +3 -1
  110. package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue2.js.map +1 -1
  111. package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue3.js.map +1 -1
  112. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue2.js +3 -1
  113. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue2.js.map +1 -1
  114. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue3.js +2 -4
  115. package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue3.js.map +1 -1
  116. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue2.js +6 -0
  117. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue2.js.map +1 -1
  118. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue3.js +68 -53
  119. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue3.js.map +1 -1
  120. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue2.js +17 -0
  121. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue2.js.map +1 -1
  122. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue3.js +21 -19
  123. package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue3.js.map +1 -1
  124. package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue2.js +3 -1
  125. package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue2.js.map +1 -1
  126. package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue3.js.map +1 -1
  127. package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue2.js +3 -1
  128. package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue2.js.map +1 -1
  129. package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue3.js +2 -4
  130. package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue3.js.map +1 -1
  131. package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue2.js +2 -1
  132. package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue2.js.map +1 -1
  133. package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue3.js.map +1 -1
  134. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue2.js +2 -1
  135. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue2.js.map +1 -1
  136. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue3.js +1 -2
  137. package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue3.js.map +1 -1
  138. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js +2 -1
  139. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js.map +1 -1
  140. package/dist/esm/integrations/builtin/deck/CnDeckCard.vue3.js.map +1 -1
  141. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js +2 -1
  142. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js.map +1 -1
  143. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue3.js +1 -2
  144. package/dist/esm/integrations/builtin/deck/CnDeckTab.vue3.js.map +1 -1
  145. package/dist/esm/integrations/builtin/email/CnEmailCard.vue2.js.map +1 -1
  146. package/dist/esm/integrations/builtin/email/CnEmailCard.vue3.js.map +1 -1
  147. package/dist/esm/integrations/builtin/email/CnEmailTab.vue2.js.map +1 -1
  148. package/dist/esm/integrations/builtin/email/CnEmailTab.vue3.js +1 -2
  149. package/dist/esm/integrations/builtin/email/CnEmailTab.vue3.js.map +1 -1
  150. package/dist/esm/integrations/builtin/flow/CnFlowCard.vue2.js +2 -1
  151. package/dist/esm/integrations/builtin/flow/CnFlowCard.vue2.js.map +1 -1
  152. package/dist/esm/integrations/builtin/flow/CnFlowCard.vue3.js.map +1 -1
  153. package/dist/esm/integrations/builtin/flow/CnFlowTab.vue2.js +2 -1
  154. package/dist/esm/integrations/builtin/flow/CnFlowTab.vue2.js.map +1 -1
  155. package/dist/esm/integrations/builtin/flow/CnFlowTab.vue3.js +1 -2
  156. package/dist/esm/integrations/builtin/flow/CnFlowTab.vue3.js.map +1 -1
  157. package/dist/esm/integrations/builtin/forms/CnFormsCard.vue2.js +3 -1
  158. package/dist/esm/integrations/builtin/forms/CnFormsCard.vue2.js.map +1 -1
  159. package/dist/esm/integrations/builtin/forms/CnFormsCard.vue3.js.map +1 -1
  160. package/dist/esm/integrations/builtin/forms/CnFormsTab.vue2.js +3 -1
  161. package/dist/esm/integrations/builtin/forms/CnFormsTab.vue2.js.map +1 -1
  162. package/dist/esm/integrations/builtin/forms/CnFormsTab.vue3.js +2 -4
  163. package/dist/esm/integrations/builtin/forms/CnFormsTab.vue3.js.map +1 -1
  164. package/dist/esm/integrations/builtin/maps/CnMapsCard.vue2.js +3 -1
  165. package/dist/esm/integrations/builtin/maps/CnMapsCard.vue2.js.map +1 -1
  166. package/dist/esm/integrations/builtin/maps/CnMapsCard.vue3.js.map +1 -1
  167. package/dist/esm/integrations/builtin/maps/CnMapsTab.vue2.js +3 -1
  168. package/dist/esm/integrations/builtin/maps/CnMapsTab.vue2.js.map +1 -1
  169. package/dist/esm/integrations/builtin/maps/CnMapsTab.vue3.js +2 -4
  170. package/dist/esm/integrations/builtin/maps/CnMapsTab.vue3.js.map +1 -1
  171. package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue2.js +3 -1
  172. package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue2.js.map +1 -1
  173. package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue3.js.map +1 -1
  174. package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue2.js +3 -1
  175. package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue2.js.map +1 -1
  176. package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue3.js +2 -4
  177. package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue3.js.map +1 -1
  178. package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue2.js +2 -1
  179. package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue2.js.map +1 -1
  180. package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue3.js.map +1 -1
  181. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue2.js +2 -1
  182. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue2.js.map +1 -1
  183. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue3.js +1 -2
  184. package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue3.js.map +1 -1
  185. package/dist/esm/integrations/builtin/polls/CnPollsCard.vue2.js +2 -1
  186. package/dist/esm/integrations/builtin/polls/CnPollsCard.vue2.js.map +1 -1
  187. package/dist/esm/integrations/builtin/polls/CnPollsCard.vue3.js.map +1 -1
  188. package/dist/esm/integrations/builtin/polls/CnPollsTab.vue2.js +2 -1
  189. package/dist/esm/integrations/builtin/polls/CnPollsTab.vue2.js.map +1 -1
  190. package/dist/esm/integrations/builtin/polls/CnPollsTab.vue3.js +1 -2
  191. package/dist/esm/integrations/builtin/polls/CnPollsTab.vue3.js.map +1 -1
  192. package/dist/esm/integrations/builtin/shares/CnSharesTab.vue2.js.map +1 -1
  193. package/dist/esm/integrations/builtin/shares/CnSharesTab.vue3.js +3 -6
  194. package/dist/esm/integrations/builtin/shares/CnSharesTab.vue3.js.map +1 -1
  195. package/dist/esm/integrations/builtin/talk/CnTalkCard.vue2.js +3 -1
  196. package/dist/esm/integrations/builtin/talk/CnTalkCard.vue2.js.map +1 -1
  197. package/dist/esm/integrations/builtin/talk/CnTalkCard.vue3.js.map +1 -1
  198. package/dist/esm/integrations/builtin/talk/CnTalkTab.vue2.js +3 -1
  199. package/dist/esm/integrations/builtin/talk/CnTalkTab.vue2.js.map +1 -1
  200. package/dist/esm/integrations/builtin/talk/CnTalkTab.vue3.js +2 -4
  201. package/dist/esm/integrations/builtin/talk/CnTalkTab.vue3.js.map +1 -1
  202. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue2.js +2 -1
  203. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue2.js.map +1 -1
  204. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue3.js.map +1 -1
  205. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue2.js +2 -1
  206. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue2.js.map +1 -1
  207. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue3.js +1 -2
  208. package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue3.js.map +1 -1
  209. package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue2.js +4 -1
  210. package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue2.js.map +1 -1
  211. package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue3.js.map +1 -1
  212. package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue2.js +6 -3
  213. package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue2.js.map +1 -1
  214. package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue3.js +2 -4
  215. package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue3.js.map +1 -1
  216. package/dist/esm/l10n/en.json.js +2 -0
  217. package/dist/esm/l10n/en.json.js.map +1 -1
  218. package/dist/esm/l10n/nl.json.js +2 -0
  219. package/dist/esm/l10n/nl.json.js.map +1 -1
  220. package/dist/esm/nextcloud-vue.css +78 -7
  221. package/dist/esm/utils/actionLink.js +5 -0
  222. package/dist/esm/utils/actionLink.js.map +1 -1
  223. package/dist/esm/utils/actionsDispatcher.js +16 -1
  224. package/dist/esm/utils/actionsDispatcher.js.map +1 -1
  225. package/dist/esm/utils/linkNavigation.js +6 -3
  226. package/dist/esm/utils/linkNavigation.js.map +1 -1
  227. package/dist/esm/utils/rowAuxClick.js +30 -5
  228. package/dist/esm/utils/rowAuxClick.js.map +1 -1
  229. package/dist/esm/utils/safeHref.js +18 -3
  230. package/dist/esm/utils/safeHref.js.map +1 -1
  231. package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
  232. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  233. package/dist/nextcloud-vue.cjs.js +885 -393
  234. package/dist/nextcloud-vue.cjs.js.map +2 -2
  235. package/dist/nextcloud-vue.css +78 -7
  236. package/l10n/en.json +2 -0
  237. package/l10n/nl.json +2 -0
  238. package/package.json +1 -1
  239. package/src/components/CnActionsBar/CnActionsBar.vue +14 -5
  240. package/src/components/CnActionsMenu/CnActionsMenu.vue +0 -3
  241. package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +1 -2
  242. package/src/components/CnAppRoot/CnAppRoot.vue +31 -2
  243. package/src/components/CnBoardView/CnBoardView.vue +13 -5
  244. package/src/components/CnCardGrid/CnCardGrid.vue +9 -1
  245. package/src/components/CnContextMenu/CnContextMenu.vue +6 -1
  246. package/src/components/CnDataTable/CnDataTable.md +1 -1
  247. package/src/components/CnDataTable/CnDataTable.vue +131 -17
  248. package/src/components/CnDateAxisView/CnDateAxisView.vue +15 -6
  249. package/src/components/CnDetailPage/CnDetailPage.md +1 -1
  250. package/src/components/CnDetailPage/CnDetailPage.vue +29 -1
  251. package/src/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue +1 -2
  252. package/src/components/CnFilesBrowser/CnFilesBrowser.vue +5 -3
  253. package/src/components/CnFilesWidget/CnFilesWidget.vue +4 -21
  254. package/src/components/CnFlowRunsWidget/CnFlowRunsWidget.vue +8 -21
  255. package/src/components/CnFlowsPage/CnFlowsPage.vue +2 -1
  256. package/src/components/CnIndexPage/CnIndexPage.vue +75 -14
  257. package/src/components/CnIndexPage/manifestActionDispatch.js +31 -11
  258. package/src/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue +1 -2
  259. package/src/components/CnInteractionFormWidget/CnInteractionFormWidget.vue +0 -1
  260. package/src/components/CnLinkButtonWidget/CnLinkButtonWidget.vue +2 -17
  261. package/src/components/CnLogsPage/CnLogsPage.vue +37 -12
  262. package/src/components/CnObjectCard/CnObjectCard.vue +24 -6
  263. package/src/components/CnObjectList/CnObjectList.vue +19 -3
  264. package/src/components/CnObjectListWidget/CnObjectListWidget.vue +47 -15
  265. package/src/components/CnObjectRow/CnObjectRow.vue +25 -7
  266. package/src/components/CnObjectSidebar/CnFilesTab.vue +0 -1
  267. package/src/components/CnPageRenderer/CnPageRenderer.vue +2 -0
  268. package/src/components/CnRowActions/CnRowActions.vue +7 -2
  269. package/src/components/CnSetupWizard/CnSetupWizard.vue +57 -6
  270. package/src/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue +5 -3
  271. package/src/components/CnTabs/CnTabs.vue +7 -3
  272. package/src/components/CnTasksWidget/CnTasksWidget.vue +4 -1
  273. package/src/css/table.css +26 -0
  274. package/src/integrations/builtin/analytics/CnAnalyticsCard.vue +3 -2
  275. package/src/integrations/builtin/analytics/CnAnalyticsTab.vue +5 -6
  276. package/src/integrations/builtin/bookmarks/CnBookmarksCard.vue +12 -1
  277. package/src/integrations/builtin/bookmarks/CnBookmarksTab.vue +23 -6
  278. package/src/integrations/builtin/bookmarks/__tests__/CnBookmarksTab.spec.js +7 -2
  279. package/src/integrations/builtin/collectives/CnCollectivesCard.vue +3 -2
  280. package/src/integrations/builtin/collectives/CnCollectivesTab.vue +5 -6
  281. package/src/integrations/builtin/cospend/CnCospendCard.vue +2 -2
  282. package/src/integrations/builtin/cospend/CnCospendTab.vue +3 -4
  283. package/src/integrations/builtin/deck/CnDeckCard.vue +2 -2
  284. package/src/integrations/builtin/deck/CnDeckTab.vue +3 -4
  285. package/src/integrations/builtin/email/CnEmailCard.vue +6 -2
  286. package/src/integrations/builtin/email/CnEmailTab.vue +7 -4
  287. package/src/integrations/builtin/flow/CnFlowCard.vue +2 -2
  288. package/src/integrations/builtin/flow/CnFlowTab.vue +3 -4
  289. package/src/integrations/builtin/forms/CnFormsCard.vue +3 -2
  290. package/src/integrations/builtin/forms/CnFormsTab.vue +5 -6
  291. package/src/integrations/builtin/maps/CnMapsCard.vue +3 -2
  292. package/src/integrations/builtin/maps/CnMapsTab.vue +5 -6
  293. package/src/integrations/builtin/maps/__tests__/CnMapsTab.spec.js +9 -0
  294. package/src/integrations/builtin/openproject/CnOpenprojectCard.vue +3 -2
  295. package/src/integrations/builtin/openproject/CnOpenprojectTab.vue +5 -6
  296. package/src/integrations/builtin/photos/CnPhotosCard.vue +2 -2
  297. package/src/integrations/builtin/photos/CnPhotosTab.vue +3 -4
  298. package/src/integrations/builtin/polls/CnPollsCard.vue +2 -2
  299. package/src/integrations/builtin/polls/CnPollsTab.vue +3 -4
  300. package/src/integrations/builtin/shares/CnSharesTab.vue +3 -6
  301. package/src/integrations/builtin/talk/CnTalkCard.vue +3 -2
  302. package/src/integrations/builtin/talk/CnTalkTab.vue +5 -6
  303. package/src/integrations/builtin/time-tracker/CnTimeTrackerCard.vue +2 -2
  304. package/src/integrations/builtin/time-tracker/CnTimeTrackerTab.vue +3 -4
  305. package/src/integrations/builtin/xwiki/CnXwikiCard.vue +4 -2
  306. package/src/integrations/builtin/xwiki/CnXwikiTab.vue +7 -7
  307. package/src/integrations/builtin/xwiki/__tests__/CnXwikiTab.spec.js +9 -0
  308. package/src/schemas/app-manifest-v2.schema.json +2 -1
  309. package/src/utils/actionLink.js +5 -0
  310. package/src/utils/actionsDispatcher.js +15 -0
  311. package/src/utils/linkNavigation.js +6 -3
  312. package/src/utils/rowAuxClick.js +29 -4
  313. package/src/utils/safeHref.js +18 -3
  314. package/src/utils/validateManifestV2.compiled.js +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"CnLinkButtonWidget.vue2.js","sources":["../../../../src/components/CnLinkButtonWidget/CnLinkButtonWidget.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div class=\"cn-link-button-widget\" :class=\"rootClass\">\n\t\t<!-- Single-button mode. Default for legacy / button-mode placements. -->\n\t\t<!-- An external URL is a real link, except in edit mode or while busy,\n\t\t where it renders as a button so a click cannot navigate. -->\n\t\t<component\n\t\t\t:is=\"singleLinkAttrs ? 'a' : 'button'\"\n\t\t\tv-if=\"!isListMode\"\n\t\t\tv-bind=\"singleLinkAttrs || { type: 'button', disabled: isExecuting }\"\n\t\t\tclass=\"cn-link-button-widget__button\"\n\t\t\t:style=\"buttonStyle\"\n\t\t\t@click=\"onSingleClick\">\n\t\t\t<span v-if=\"hasIcon\" class=\"cn-link-button-widget__icon\">\n\t\t\t\t<CnWidgetIcon :name=\"icon\" :size=\"48\" />\n\t\t\t</span>\n\t\t\t<span class=\"cn-link-button-widget__label\">{{ displayLabel }}</span>\n\t\t</component>\n\n\t\t<!-- Vertical list mode. -->\n\t\t<ul\n\t\t\tv-else-if=\"isVerticalList\"\n\t\t\trole=\"list\"\n\t\t\tclass=\"cn-link-button-widget__list cn-link-button-widget__list--vertical\"\n\t\t\t:style=\"listContainerStyle\">\n\t\t\t<li\n\t\t\t\tv-for=\"(link, index) in renderableLinks\"\n\t\t\t\t:key=\"`link-${index}`\"\n\t\t\t\tclass=\"cn-link-button-widget__list-item-wrap\">\n\t\t\t\t<component\n\t\t\t\t\t:is=\"linkAttrsFor(link.actionType, link.url) ? 'a' : 'button'\"\n\t\t\t\t\tv-bind=\"linkAttrsFor(link.actionType, link.url) || { type: 'button', disabled: isExecuting }\"\n\t\t\t\t\tclass=\"cn-link-button-widget__list-item\"\n\t\t\t\t\t:style=\"listItemStyle(link)\"\n\t\t\t\t\t:aria-label=\"link.label || ''\"\n\t\t\t\t\t@click=\"onListClick(link)\">\n\t\t\t\t\t<span v-if=\"link.icon\" class=\"cn-link-button-widget__list-icon\">\n\t\t\t\t\t\t<CnWidgetIcon :name=\"link.icon\" :size=\"24\" />\n\t\t\t\t\t</span>\n\t\t\t\t\t<span class=\"cn-link-button-widget__list-label\">{{ link.label || '' }}</span>\n\t\t\t\t</component>\n\t\t\t</li>\n\t\t</ul>\n\n\t\t<!-- Horizontal list mode. -->\n\t\t<div\n\t\t\tv-else\n\t\t\trole=\"list\"\n\t\t\tclass=\"cn-link-button-widget__list cn-link-button-widget__list--horizontal\"\n\t\t\t:style=\"listContainerStyle\">\n\t\t\t<div\n\t\t\t\tv-for=\"(link, index) in renderableLinks\"\n\t\t\t\t:key=\"`link-${index}`\"\n\t\t\t\trole=\"listitem\"\n\t\t\t\tclass=\"cn-link-button-widget__list-item-wrap cn-link-button-widget__list-item-wrap--horizontal\">\n\t\t\t\t<component\n\t\t\t\t\t:is=\"linkAttrsFor(link.actionType, link.url) ? 'a' : 'button'\"\n\t\t\t\t\tv-bind=\"linkAttrsFor(link.actionType, link.url) || { type: 'button', disabled: isExecuting }\"\n\t\t\t\t\tclass=\"cn-link-button-widget__list-item cn-link-button-widget__list-item--horizontal\"\n\t\t\t\t\t:style=\"listItemStyle(link)\"\n\t\t\t\t\t:aria-label=\"link.label || ''\"\n\t\t\t\t\t@click=\"onListClick(link)\">\n\t\t\t\t\t<span v-if=\"link.icon\" class=\"cn-link-button-widget__list-icon\">\n\t\t\t\t\t\t<CnWidgetIcon :name=\"link.icon\" :size=\"24\" />\n\t\t\t\t\t</span>\n\t\t\t\t\t<span class=\"cn-link-button-widget__list-label\">{{ link.label || '' }}</span>\n\t\t\t\t</component>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport CnWidgetIcon from '../CnWidgetGrid/CnWidgetIcon.vue'\nimport { safeHref } from '../../utils/safeHref.js'\n\nconst ACTION_TYPES = Object.freeze({\n\tEXTERNAL: 'external',\n\tINTERNAL: 'internal',\n\tCREATE_FILE: 'createFile',\n})\n\nconst DISPLAY_MODES = Object.freeze({\n\tBUTTON: 'button',\n\tLIST: 'list',\n})\n\nconst ORIENTATIONS = Object.freeze({\n\tVERTICAL: 'vertical',\n\tHORIZONTAL: 'horizontal',\n})\n\nconst GAPS = Object.freeze({\n\tCOMPACT: 'compact',\n\tNORMAL: 'normal',\n\tSPACIOUS: 'spacious',\n})\n\nconst GAP_REM = Object.freeze({\n\tcompact: '0.5rem',\n\tnormal: '1rem',\n\tspacious: '1.5rem',\n})\n\n/**\n * CnLinkButtonWidget — renders a styled clickable tile (or a list of tiles)\n * that dispatches one of three action types per entry:\n *\n * - `external` → renders as an `<a target=\"_blank\" rel=\"noopener\n * noreferrer\">` to the configured `url` (a button in edit mode).\n * - `internal` → emits an `internal-action` event with the action id so a\n * host app can dispatch its own registered handler (the library ships no\n * action registry). An empty/missing handler is a silent no-op.\n * - `createFile` → emits a `create-file` event with the extension token so\n * a host app can run its file-creation flow.\n *\n * `displayMode = 'list'` renders `content.links[]` as a vertical `<ul>` stack\n * or a horizontal `<div>` row of pills; each link reuses the same action\n * types, icon resolution, and edit-mode suppression as the single button.\n * Default / legacy placements (`displayMode` absent) render the single button\n * unchanged.\n *\n * Click is suppressed while the dashboard is in edit mode (`isAdmin === true`\n * AND `canEdit === true`) and while an action is in flight.\n *\n * Registered as the `link` dashboard widget type via the renderer's\n * `index.js`.\n */\nexport default {\n\tname: 'CnLinkButtonWidget',\n\n\tcomponents: {\n\t\tCnWidgetIcon,\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Persisted widget content. Single-button shape: `{label, url, icon,\n\t\t * actionType, backgroundColor, textColor}`. List shape adds\n\t\t * `{displayMode: 'list', listOrientation, listItemGap, links: [{label,\n\t\t * url, icon, actionType, value?, backgroundColor?, textColor?}, …]}`.\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Whether the current user is an admin. Combined with `canEdit` to\n\t\t * suppress click handlers in edit mode.\n\t\t *\n\t\t * @type {boolean}\n\t\t */\n\t\tisAdmin: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Whether the surrounding dashboard shell is in edit mode. Suppresses\n\t\t * click handlers when both this and `isAdmin` are true.\n\t\t *\n\t\t * @type {boolean}\n\t\t */\n\t\tcanEdit: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\t},\n\n\temits: [\n\t\t/**\n\t\t * Emitted on an `internal` action click so a host app can dispatch its\n\t\t * own handler.\n\t\t *\n\t\t * @event internal-action\n\t\t * @type {string}\n\t\t */\n\t\t'internal-action',\n\t\t/**\n\t\t * Emitted on a `createFile` action click; payload is the extension\n\t\t * token (e.g. `'docx'`).\n\t\t *\n\t\t * @event create-file\n\t\t * @type {string}\n\t\t */\n\t\t'create-file',\n\t],\n\n\tdata() {\n\t\treturn {\n\t\t\tisExecuting: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** The single-button label, or '' when absent. */\n\t\tlabel() {\n\t\t\treturn typeof this.content?.label === 'string' ? this.content.label : ''\n\t\t},\n\n\t\t/** The single-button URL / action token, or '' when absent. */\n\t\turl() {\n\t\t\treturn typeof this.content?.url === 'string' ? this.content.url : ''\n\t\t},\n\n\t\t/** The single-button icon, or '' when absent. */\n\t\ticon() {\n\t\t\treturn typeof this.content?.icon === 'string' ? this.content.icon : ''\n\t\t},\n\n\t\t/** The single-button action type (defaults to `external`). */\n\t\tactionType() {\n\t\t\tconst declared = this.content?.actionType\n\t\t\tif (declared === ACTION_TYPES.INTERNAL || declared === ACTION_TYPES.CREATE_FILE) {\n\t\t\t\treturn declared\n\t\t\t}\n\t\t\treturn ACTION_TYPES.EXTERNAL\n\t\t},\n\n\t\t/** Resolved widget background colour (default theme primary). */\n\t\tbackgroundColor() {\n\t\t\tconst value = this.content?.backgroundColor\n\t\t\treturn (typeof value === 'string' && value !== '') ? value : 'var(--color-primary)'\n\t\t},\n\n\t\t/** Resolved widget text colour (default theme primary text). */\n\t\ttextColor() {\n\t\t\tconst value = this.content?.textColor\n\t\t\treturn (typeof value === 'string' && value !== '') ? value : 'var(--color-primary-text)'\n\t\t},\n\n\t\t/** Whether the single button has an icon. */\n\t\thasIcon() {\n\t\t\treturn this.icon !== ''\n\t\t},\n\n\t\t/** The button label, falling back to a localised placeholder. */\n\t\tdisplayLabel() {\n\t\t\treturn this.label !== '' ? this.label : t('nextcloud-vue', 'Link Button')\n\t\t},\n\n\t\t/** Inline style for the single button. */\n\t\tbuttonStyle() {\n\t\t\treturn {\n\t\t\t\t'background-color': this.backgroundColor,\n\t\t\t\tcolor: this.textColor,\n\t\t\t}\n\t\t},\n\n\t\t/** Whether the dashboard is in edit mode (clicks suppressed). */\n\t\tisInEditMode() {\n\t\t\treturn this.isAdmin === true && this.canEdit === true\n\t\t},\n\n\t\t/** Anchor attributes for the single button, or null when it stays a button. */\n\t\tsingleLinkAttrs() {\n\t\t\treturn this.linkAttrsFor(this.actionType, this.url)\n\t\t},\n\n\t\t/** Resolved display mode (legacy / unset falls back to `button`). */\n\t\tdisplayMode() {\n\t\t\treturn this.content?.displayMode === DISPLAY_MODES.LIST\n\t\t\t\t? DISPLAY_MODES.LIST\n\t\t\t\t: DISPLAY_MODES.BUTTON\n\t\t},\n\n\t\t/** Whether list mode is active (mode is list AND links non-empty). */\n\t\tisListMode() {\n\t\t\treturn this.displayMode === DISPLAY_MODES.LIST\n\t\t\t\t&& Array.isArray(this.content?.links)\n\t\t\t\t&& this.content.links.length > 0\n\t\t},\n\n\t\t/** Resolved list orientation (default `vertical`). */\n\t\tlistOrientation() {\n\t\t\treturn this.content?.listOrientation === ORIENTATIONS.HORIZONTAL\n\t\t\t\t? ORIENTATIONS.HORIZONTAL\n\t\t\t\t: ORIENTATIONS.VERTICAL\n\t\t},\n\n\t\t/** Whether the list renders vertically. */\n\t\tisVerticalList() {\n\t\t\treturn this.listOrientation === ORIENTATIONS.VERTICAL\n\t\t},\n\n\t\t/** Resolved list item spacing (default `normal`). */\n\t\tlistItemGap() {\n\t\t\tconst declared = this.content?.listItemGap\n\t\t\tif (declared === GAPS.COMPACT || declared === GAPS.SPACIOUS) {\n\t\t\t\treturn declared\n\t\t\t}\n\t\t\treturn GAPS.NORMAL\n\t\t},\n\n\t\t/** The CSS gap value for the active spacing setting. */\n\t\tlistGapValue() {\n\t\t\treturn GAP_REM[this.listItemGap]\n\t\t},\n\n\t\t/** Inline style applying the list gap. */\n\t\tlistContainerStyle() {\n\t\t\treturn { gap: this.listGapValue }\n\t\t},\n\n\t\t/** Normalised list of link entries safe for the renderer. */\n\t\trenderableLinks() {\n\t\t\tif (Array.isArray(this.content?.links) === false) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn this.content.links.map((raw) => {\n\t\t\t\tconst link = (raw !== null && typeof raw === 'object') ? raw : {}\n\t\t\t\tconst declaredAction = link.actionType\n\t\t\t\tconst actionType = (declaredAction === ACTION_TYPES.INTERNAL\n\t\t\t\t\t|| declaredAction === ACTION_TYPES.CREATE_FILE)\n\t\t\t\t\t? declaredAction\n\t\t\t\t\t: ACTION_TYPES.EXTERNAL\n\t\t\t\treturn {\n\t\t\t\t\tlabel: typeof link.label === 'string' ? link.label : '',\n\t\t\t\t\turl: typeof link.url === 'string' ? link.url : '',\n\t\t\t\t\ticon: typeof link.icon === 'string' ? link.icon : '',\n\t\t\t\t\tactionType,\n\t\t\t\t\tvalue: typeof link.value === 'string' ? link.value : '',\n\t\t\t\t\tbackgroundColor: typeof link.backgroundColor === 'string' ? link.backgroundColor : '',\n\t\t\t\t\ttextColor: typeof link.textColor === 'string' ? link.textColor : '',\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/** Root CSS class map (list-mode + orientation modifiers). */\n\t\trootClass() {\n\t\t\treturn {\n\t\t\t\t'cn-link-button-widget--list': this.isListMode,\n\t\t\t\t[`cn-link-button-widget--list-${this.listOrientation}`]: this.isListMode,\n\t\t\t}\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Anchor attributes for an entry that should be a real link: an\n\t\t * `external` action with a URL, outside edit mode and not busy.\n\t\t *\n\t\t * @param {string} actionType the entry's action type.\n\t\t * @param {string} url the entry's URL.\n\t\t * @return {object|null} `{ href, target, rel }`, or null for a button.\n\t\t */\n\t\tlinkAttrsFor(actionType, url) {\n\t\t\tif (actionType !== ACTION_TYPES.EXTERNAL || typeof url !== 'string' || url === ''\n\t\t\t\t|| this.isInEditMode || this.isExecuting) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn { href: safeHref(url), target: '_blank', rel: 'noopener noreferrer' }\n\t\t},\n\n\t\t/**\n\t\t * Per-link inline style, falling back to the widget-level colours.\n\t\t *\n\t\t * @param {object} link the normalised link entry.\n\t\t * @return {object} the inline style object.\n\t\t */\n\t\tlistItemStyle(link) {\n\t\t\tconst bg = (typeof link.backgroundColor === 'string' && link.backgroundColor !== '')\n\t\t\t\t? link.backgroundColor\n\t\t\t\t: this.backgroundColor\n\t\t\tconst fg = (typeof link.textColor === 'string' && link.textColor !== '')\n\t\t\t\t? link.textColor\n\t\t\t\t: this.textColor\n\t\t\treturn {\n\t\t\t\t'background-color': bg,\n\t\t\t\tcolor: fg,\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Dispatch the single-button click (suppressed in edit mode / busy).\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tonSingleClick() {\n\t\t\t// A link navigates by itself.\n\t\t\tif (this.isInEditMode || this.isExecuting || this.singleLinkAttrs) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.dispatchAction({\n\t\t\t\tactionType: this.actionType,\n\t\t\t\turl: this.url,\n\t\t\t\tvalue: '',\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Dispatch a list-item click (suppressed in edit mode / busy).\n\t\t *\n\t\t * @param {object} link the normalised link entry.\n\t\t * @return {void}\n\t\t */\n\t\tonListClick(link) {\n\t\t\tif (this.isInEditMode || this.isExecuting || this.linkAttrsFor(link.actionType, link.url)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.dispatchAction({\n\t\t\t\tactionType: link.actionType,\n\t\t\t\turl: link.url,\n\t\t\t\tvalue: link.value,\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Route a click to the handler for its action type.\n\t\t *\n\t\t * @param {{actionType: string, url: string, value: string}} payload the dispatch payload.\n\t\t * @return {void}\n\t\t */\n\t\tdispatchAction({ actionType, url, value }) {\n\t\t\tswitch (actionType) {\n\t\t\t\tcase ACTION_TYPES.EXTERNAL:\n\t\t\t\t\tthis.handleExternal(url)\n\t\t\t\t\tbreak\n\t\t\t\tcase ACTION_TYPES.INTERNAL:\n\t\t\t\t\tthis.$emit('internal-action', url)\n\t\t\t\t\tbreak\n\t\t\t\tcase ACTION_TYPES.CREATE_FILE:\n\t\t\t\t// For list items the extension lives in `value`; for the\n\t\t\t\t// single button it lives in `url`.\n\t\t\t\t\tthis.$emit('create-file', value !== '' ? value : url)\n\t\t\t\t\tbreak\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Open an external URL in a new tab; `noopener,noreferrer` prevents\n\t\t * the opened page reaching back via `window.opener`.\n\t\t *\n\t\t * @param {string} url the URL to open.\n\t\t * @return {void}\n\t\t */\n\t\thandleExternal(url) {\n\t\t\tif (typeof url !== 'string' || url === '') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\twindow.open(url, '_blank', 'noopener,noreferrer')\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-link-button-widget {\n\twidth: 100%;\n\theight: 100%;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tpadding: 8px;\n}\n\n.cn-link-button-widget--list {\n\talign-items: stretch;\n\tjustify-content: stretch;\n}\n\n.cn-link-button-widget__button {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tjustify-content: center;\n\tgap: 8px;\n\twidth: 100%;\n\theight: 100%;\n\tmin-height: 96px;\n\tpadding: 12px;\n\tborder: none;\n\tborder-radius: var(--border-radius-large, 8px);\n\tbox-sizing: border-box;\n\tcursor: pointer;\n\tfont-size: 14px;\n\tfont-weight: 600;\n\ttext-decoration: none;\n\ttransition: transform 0.15s ease, box-shadow 0.15s ease;\n}\n\n.cn-link-button-widget__button:disabled {\n\topacity: 0.6;\n\tcursor: not-allowed;\n}\n\n.cn-link-button-widget__button:hover:not(:disabled) {\n\ttransform: translateY(-2px);\n\tbox-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);\n}\n\n.cn-link-button-widget__icon {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 48px;\n\theight: 48px;\n}\n\n.cn-link-button-widget__label {\n\tdisplay: block;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\tmax-width: 100%;\n}\n\n.cn-link-button-widget__list {\n\twidth: 100%;\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n\tdisplay: flex;\n}\n\n.cn-link-button-widget__list--vertical {\n\tflex-direction: column;\n}\n\n.cn-link-button-widget__list--horizontal {\n\tflex-direction: row;\n\tflex-wrap: wrap;\n\talign-items: flex-start;\n}\n\n.cn-link-button-widget__list-item-wrap {\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-link-button-widget__list-item {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\twidth: 100%;\n\tpadding: 8px 12px;\n\tborder: none;\n\tborder-radius: var(--border-radius, 6px);\n\tbox-sizing: border-box;\n\tcursor: pointer;\n\tfont-size: 14px;\n\tfont-weight: 500;\n\ttext-align: left;\n\ttext-decoration: none;\n\ttransition: transform 0.15s ease, box-shadow 0.15s ease;\n}\n\n.cn-link-button-widget__list-item--horizontal {\n\twidth: auto;\n}\n\n.cn-link-button-widget__list-item:disabled {\n\topacity: 0.6;\n\tcursor: not-allowed;\n}\n\n.cn-link-button-widget__list-item:hover:not(:disabled) {\n\ttransform: translateY(-2px);\n\tbox-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);\n}\n\n.cn-link-button-widget__list-icon {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 24px;\n\theight: 24px;\n\tflex-shrink: 0;\n}\n\n.cn-link-button-widget__list-label {\n\tdisplay: inline-block;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["CnWidgetIcon","t"],"mappings":"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"}
1
+ {"version":3,"file":"CnLinkButtonWidget.vue2.js","sources":["../../../../src/components/CnLinkButtonWidget/CnLinkButtonWidget.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div class=\"cn-link-button-widget\" :class=\"rootClass\">\n\t\t<!-- Single-button mode. Default for legacy / button-mode placements. -->\n\t\t<!-- An external URL is a real link, except in edit mode or while busy,\n\t\t where it renders as a button so a click cannot navigate. -->\n\t\t<component\n\t\t\t:is=\"singleLinkAttrs ? 'a' : 'button'\"\n\t\t\tv-if=\"!isListMode\"\n\t\t\tv-bind=\"singleLinkAttrs || { type: 'button', disabled: isExecuting }\"\n\t\t\tclass=\"cn-link-button-widget__button\"\n\t\t\t:style=\"buttonStyle\"\n\t\t\t@click=\"onSingleClick\">\n\t\t\t<span v-if=\"hasIcon\" class=\"cn-link-button-widget__icon\">\n\t\t\t\t<CnWidgetIcon :name=\"icon\" :size=\"48\" />\n\t\t\t</span>\n\t\t\t<span class=\"cn-link-button-widget__label\">{{ displayLabel }}</span>\n\t\t</component>\n\n\t\t<!-- Vertical list mode. -->\n\t\t<ul\n\t\t\tv-else-if=\"isVerticalList\"\n\t\t\trole=\"list\"\n\t\t\tclass=\"cn-link-button-widget__list cn-link-button-widget__list--vertical\"\n\t\t\t:style=\"listContainerStyle\">\n\t\t\t<li\n\t\t\t\tv-for=\"(link, index) in renderableLinks\"\n\t\t\t\t:key=\"`link-${index}`\"\n\t\t\t\tclass=\"cn-link-button-widget__list-item-wrap\">\n\t\t\t\t<component\n\t\t\t\t\t:is=\"linkAttrsFor(link.actionType, link.url) ? 'a' : 'button'\"\n\t\t\t\t\tv-bind=\"linkAttrsFor(link.actionType, link.url) || { type: 'button', disabled: isExecuting }\"\n\t\t\t\t\tclass=\"cn-link-button-widget__list-item\"\n\t\t\t\t\t:style=\"listItemStyle(link)\"\n\t\t\t\t\t:aria-label=\"link.label || ''\"\n\t\t\t\t\t@click=\"onListClick(link)\">\n\t\t\t\t\t<span v-if=\"link.icon\" class=\"cn-link-button-widget__list-icon\">\n\t\t\t\t\t\t<CnWidgetIcon :name=\"link.icon\" :size=\"24\" />\n\t\t\t\t\t</span>\n\t\t\t\t\t<span class=\"cn-link-button-widget__list-label\">{{ link.label || '' }}</span>\n\t\t\t\t</component>\n\t\t\t</li>\n\t\t</ul>\n\n\t\t<!-- Horizontal list mode. -->\n\t\t<div\n\t\t\tv-else\n\t\t\trole=\"list\"\n\t\t\tclass=\"cn-link-button-widget__list cn-link-button-widget__list--horizontal\"\n\t\t\t:style=\"listContainerStyle\">\n\t\t\t<div\n\t\t\t\tv-for=\"(link, index) in renderableLinks\"\n\t\t\t\t:key=\"`link-${index}`\"\n\t\t\t\trole=\"listitem\"\n\t\t\t\tclass=\"cn-link-button-widget__list-item-wrap cn-link-button-widget__list-item-wrap--horizontal\">\n\t\t\t\t<component\n\t\t\t\t\t:is=\"linkAttrsFor(link.actionType, link.url) ? 'a' : 'button'\"\n\t\t\t\t\tv-bind=\"linkAttrsFor(link.actionType, link.url) || { type: 'button', disabled: isExecuting }\"\n\t\t\t\t\tclass=\"cn-link-button-widget__list-item cn-link-button-widget__list-item--horizontal\"\n\t\t\t\t\t:style=\"listItemStyle(link)\"\n\t\t\t\t\t:aria-label=\"link.label || ''\"\n\t\t\t\t\t@click=\"onListClick(link)\">\n\t\t\t\t\t<span v-if=\"link.icon\" class=\"cn-link-button-widget__list-icon\">\n\t\t\t\t\t\t<CnWidgetIcon :name=\"link.icon\" :size=\"24\" />\n\t\t\t\t\t</span>\n\t\t\t\t\t<span class=\"cn-link-button-widget__list-label\">{{ link.label || '' }}</span>\n\t\t\t\t</component>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport CnWidgetIcon from '../CnWidgetGrid/CnWidgetIcon.vue'\nimport { safeHref } from '../../utils/safeHref.js'\n\nconst ACTION_TYPES = Object.freeze({\n\tEXTERNAL: 'external',\n\tINTERNAL: 'internal',\n\tCREATE_FILE: 'createFile',\n})\n\nconst DISPLAY_MODES = Object.freeze({\n\tBUTTON: 'button',\n\tLIST: 'list',\n})\n\nconst ORIENTATIONS = Object.freeze({\n\tVERTICAL: 'vertical',\n\tHORIZONTAL: 'horizontal',\n})\n\nconst GAPS = Object.freeze({\n\tCOMPACT: 'compact',\n\tNORMAL: 'normal',\n\tSPACIOUS: 'spacious',\n})\n\nconst GAP_REM = Object.freeze({\n\tcompact: '0.5rem',\n\tnormal: '1rem',\n\tspacious: '1.5rem',\n})\n\n/**\n * CnLinkButtonWidget — renders a styled clickable tile (or a list of tiles)\n * that dispatches one of three action types per entry:\n *\n * - `external` → renders as an `<a target=\"_blank\" rel=\"noopener\n * noreferrer\">` to the configured `url` (a button in edit mode).\n * - `internal` → emits an `internal-action` event with the action id so a\n * host app can dispatch its own registered handler (the library ships no\n * action registry). An empty/missing handler is a silent no-op.\n * - `createFile` → emits a `create-file` event with the extension token so\n * a host app can run its file-creation flow.\n *\n * `displayMode = 'list'` renders `content.links[]` as a vertical `<ul>` stack\n * or a horizontal `<div>` row of pills; each link reuses the same action\n * types, icon resolution, and edit-mode suppression as the single button.\n * Default / legacy placements (`displayMode` absent) render the single button\n * unchanged.\n *\n * Click is suppressed while the dashboard is in edit mode (`isAdmin === true`\n * AND `canEdit === true`) and while an action is in flight.\n *\n * Registered as the `link` dashboard widget type via the renderer's\n * `index.js`.\n */\nexport default {\n\tname: 'CnLinkButtonWidget',\n\n\tcomponents: {\n\t\tCnWidgetIcon,\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Persisted widget content. Single-button shape: `{label, url, icon,\n\t\t * actionType, backgroundColor, textColor}`. List shape adds\n\t\t * `{displayMode: 'list', listOrientation, listItemGap, links: [{label,\n\t\t * url, icon, actionType, value?, backgroundColor?, textColor?}, …]}`.\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Whether the current user is an admin. Combined with `canEdit` to\n\t\t * suppress click handlers in edit mode.\n\t\t *\n\t\t * @type {boolean}\n\t\t */\n\t\tisAdmin: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Whether the surrounding dashboard shell is in edit mode. Suppresses\n\t\t * click handlers when both this and `isAdmin` are true.\n\t\t *\n\t\t * @type {boolean}\n\t\t */\n\t\tcanEdit: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\t},\n\n\temits: [\n\t\t/**\n\t\t * Emitted on an `internal` action click so a host app can dispatch its\n\t\t * own handler.\n\t\t *\n\t\t * @event internal-action\n\t\t * @type {string}\n\t\t */\n\t\t'internal-action',\n\t\t/**\n\t\t * Emitted on a `createFile` action click; payload is the extension\n\t\t * token (e.g. `'docx'`).\n\t\t *\n\t\t * @event create-file\n\t\t * @type {string}\n\t\t */\n\t\t'create-file',\n\t],\n\n\tdata() {\n\t\treturn {\n\t\t\tisExecuting: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** The single-button label, or '' when absent. */\n\t\tlabel() {\n\t\t\treturn typeof this.content?.label === 'string' ? this.content.label : ''\n\t\t},\n\n\t\t/** The single-button URL / action token, or '' when absent. */\n\t\turl() {\n\t\t\treturn typeof this.content?.url === 'string' ? this.content.url : ''\n\t\t},\n\n\t\t/** The single-button icon, or '' when absent. */\n\t\ticon() {\n\t\t\treturn typeof this.content?.icon === 'string' ? this.content.icon : ''\n\t\t},\n\n\t\t/** The single-button action type (defaults to `external`). */\n\t\tactionType() {\n\t\t\tconst declared = this.content?.actionType\n\t\t\tif (declared === ACTION_TYPES.INTERNAL || declared === ACTION_TYPES.CREATE_FILE) {\n\t\t\t\treturn declared\n\t\t\t}\n\t\t\treturn ACTION_TYPES.EXTERNAL\n\t\t},\n\n\t\t/** Resolved widget background colour (default theme primary). */\n\t\tbackgroundColor() {\n\t\t\tconst value = this.content?.backgroundColor\n\t\t\treturn (typeof value === 'string' && value !== '') ? value : 'var(--color-primary)'\n\t\t},\n\n\t\t/** Resolved widget text colour (default theme primary text). */\n\t\ttextColor() {\n\t\t\tconst value = this.content?.textColor\n\t\t\treturn (typeof value === 'string' && value !== '') ? value : 'var(--color-primary-text)'\n\t\t},\n\n\t\t/** Whether the single button has an icon. */\n\t\thasIcon() {\n\t\t\treturn this.icon !== ''\n\t\t},\n\n\t\t/** The button label, falling back to a localised placeholder. */\n\t\tdisplayLabel() {\n\t\t\treturn this.label !== '' ? this.label : t('nextcloud-vue', 'Link Button')\n\t\t},\n\n\t\t/** Inline style for the single button. */\n\t\tbuttonStyle() {\n\t\t\treturn {\n\t\t\t\t'background-color': this.backgroundColor,\n\t\t\t\tcolor: this.textColor,\n\t\t\t}\n\t\t},\n\n\t\t/** Whether the dashboard is in edit mode (clicks suppressed). */\n\t\tisInEditMode() {\n\t\t\treturn this.isAdmin === true && this.canEdit === true\n\t\t},\n\n\t\t/** Anchor attributes for the single button, or null when it stays a button. */\n\t\tsingleLinkAttrs() {\n\t\t\treturn this.linkAttrsFor(this.actionType, this.url)\n\t\t},\n\n\t\t/** Resolved display mode (legacy / unset falls back to `button`). */\n\t\tdisplayMode() {\n\t\t\treturn this.content?.displayMode === DISPLAY_MODES.LIST\n\t\t\t\t? DISPLAY_MODES.LIST\n\t\t\t\t: DISPLAY_MODES.BUTTON\n\t\t},\n\n\t\t/** Whether list mode is active (mode is list AND links non-empty). */\n\t\tisListMode() {\n\t\t\treturn this.displayMode === DISPLAY_MODES.LIST\n\t\t\t\t&& Array.isArray(this.content?.links)\n\t\t\t\t&& this.content.links.length > 0\n\t\t},\n\n\t\t/** Resolved list orientation (default `vertical`). */\n\t\tlistOrientation() {\n\t\t\treturn this.content?.listOrientation === ORIENTATIONS.HORIZONTAL\n\t\t\t\t? ORIENTATIONS.HORIZONTAL\n\t\t\t\t: ORIENTATIONS.VERTICAL\n\t\t},\n\n\t\t/** Whether the list renders vertically. */\n\t\tisVerticalList() {\n\t\t\treturn this.listOrientation === ORIENTATIONS.VERTICAL\n\t\t},\n\n\t\t/** Resolved list item spacing (default `normal`). */\n\t\tlistItemGap() {\n\t\t\tconst declared = this.content?.listItemGap\n\t\t\tif (declared === GAPS.COMPACT || declared === GAPS.SPACIOUS) {\n\t\t\t\treturn declared\n\t\t\t}\n\t\t\treturn GAPS.NORMAL\n\t\t},\n\n\t\t/** The CSS gap value for the active spacing setting. */\n\t\tlistGapValue() {\n\t\t\treturn GAP_REM[this.listItemGap]\n\t\t},\n\n\t\t/** Inline style applying the list gap. */\n\t\tlistContainerStyle() {\n\t\t\treturn { gap: this.listGapValue }\n\t\t},\n\n\t\t/** Normalised list of link entries safe for the renderer. */\n\t\trenderableLinks() {\n\t\t\tif (Array.isArray(this.content?.links) === false) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn this.content.links.map((raw) => {\n\t\t\t\tconst link = (raw !== null && typeof raw === 'object') ? raw : {}\n\t\t\t\tconst declaredAction = link.actionType\n\t\t\t\tconst actionType = (declaredAction === ACTION_TYPES.INTERNAL\n\t\t\t\t\t|| declaredAction === ACTION_TYPES.CREATE_FILE)\n\t\t\t\t\t? declaredAction\n\t\t\t\t\t: ACTION_TYPES.EXTERNAL\n\t\t\t\treturn {\n\t\t\t\t\tlabel: typeof link.label === 'string' ? link.label : '',\n\t\t\t\t\turl: typeof link.url === 'string' ? link.url : '',\n\t\t\t\t\ticon: typeof link.icon === 'string' ? link.icon : '',\n\t\t\t\t\tactionType,\n\t\t\t\t\tvalue: typeof link.value === 'string' ? link.value : '',\n\t\t\t\t\tbackgroundColor: typeof link.backgroundColor === 'string' ? link.backgroundColor : '',\n\t\t\t\t\ttextColor: typeof link.textColor === 'string' ? link.textColor : '',\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/** Root CSS class map (list-mode + orientation modifiers). */\n\t\trootClass() {\n\t\t\treturn {\n\t\t\t\t'cn-link-button-widget--list': this.isListMode,\n\t\t\t\t[`cn-link-button-widget--list-${this.listOrientation}`]: this.isListMode,\n\t\t\t}\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Anchor attributes for an entry that should be a real link: an\n\t\t * `external` action with a URL, outside edit mode and not busy.\n\t\t *\n\t\t * @param {string} actionType the entry's action type.\n\t\t * @param {string} url the entry's URL.\n\t\t * @return {object|null} `{ href, target, rel }`, or null for a button.\n\t\t */\n\t\tlinkAttrsFor(actionType, url) {\n\t\t\tif (actionType !== ACTION_TYPES.EXTERNAL || typeof url !== 'string' || url === ''\n\t\t\t\t|| this.isInEditMode || this.isExecuting) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn { href: safeHref(url), target: '_blank', rel: 'noopener noreferrer' }\n\t\t},\n\n\t\t/**\n\t\t * Per-link inline style, falling back to the widget-level colours.\n\t\t *\n\t\t * @param {object} link the normalised link entry.\n\t\t * @return {object} the inline style object.\n\t\t */\n\t\tlistItemStyle(link) {\n\t\t\tconst bg = (typeof link.backgroundColor === 'string' && link.backgroundColor !== '')\n\t\t\t\t? link.backgroundColor\n\t\t\t\t: this.backgroundColor\n\t\t\tconst fg = (typeof link.textColor === 'string' && link.textColor !== '')\n\t\t\t\t? link.textColor\n\t\t\t\t: this.textColor\n\t\t\treturn {\n\t\t\t\t'background-color': bg,\n\t\t\t\tcolor: fg,\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Dispatch the single-button click (suppressed in edit mode / busy).\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tonSingleClick() {\n\t\t\t// A link navigates by itself.\n\t\t\tif (this.isInEditMode || this.isExecuting || this.singleLinkAttrs) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.dispatchAction({\n\t\t\t\tactionType: this.actionType,\n\t\t\t\turl: this.url,\n\t\t\t\tvalue: '',\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Dispatch a list-item click (suppressed in edit mode / busy).\n\t\t *\n\t\t * @param {object} link the normalised link entry.\n\t\t * @return {void}\n\t\t */\n\t\tonListClick(link) {\n\t\t\tif (this.isInEditMode || this.isExecuting || this.linkAttrsFor(link.actionType, link.url)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.dispatchAction({\n\t\t\t\tactionType: link.actionType,\n\t\t\t\turl: link.url,\n\t\t\t\tvalue: link.value,\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Route a click to the handler for its action type.\n\t\t *\n\t\t * @param {{actionType: string, url: string, value: string}} payload the dispatch payload.\n\t\t * @return {void}\n\t\t */\n\t\tdispatchAction({ actionType, url, value }) {\n\t\t\t// An `external` entry with a URL is a real link (see linkAttrsFor);\n\t\t\t// one without a URL has nothing to open.\n\t\t\tswitch (actionType) {\n\t\t\t\tcase ACTION_TYPES.INTERNAL:\n\t\t\t\t\tthis.$emit('internal-action', url)\n\t\t\t\t\tbreak\n\t\t\t\tcase ACTION_TYPES.CREATE_FILE:\n\t\t\t\t// For list items the extension lives in `value`; for the\n\t\t\t\t// single button it lives in `url`.\n\t\t\t\t\tthis.$emit('create-file', value !== '' ? value : url)\n\t\t\t\t\tbreak\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-link-button-widget {\n\twidth: 100%;\n\theight: 100%;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tpadding: 8px;\n}\n\n.cn-link-button-widget--list {\n\talign-items: stretch;\n\tjustify-content: stretch;\n}\n\n.cn-link-button-widget__button {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tjustify-content: center;\n\tgap: 8px;\n\twidth: 100%;\n\theight: 100%;\n\tmin-height: 96px;\n\tpadding: 12px;\n\tborder: none;\n\tborder-radius: var(--border-radius-large, 8px);\n\tbox-sizing: border-box;\n\tcursor: pointer;\n\tfont-size: 14px;\n\tfont-weight: 600;\n\ttext-decoration: none;\n\ttransition: transform 0.15s ease, box-shadow 0.15s ease;\n}\n\n.cn-link-button-widget__button:disabled {\n\topacity: 0.6;\n\tcursor: not-allowed;\n}\n\n.cn-link-button-widget__button:hover:not(:disabled) {\n\ttransform: translateY(-2px);\n\tbox-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);\n}\n\n.cn-link-button-widget__icon {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 48px;\n\theight: 48px;\n}\n\n.cn-link-button-widget__label {\n\tdisplay: block;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\tmax-width: 100%;\n}\n\n.cn-link-button-widget__list {\n\twidth: 100%;\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n\tdisplay: flex;\n}\n\n.cn-link-button-widget__list--vertical {\n\tflex-direction: column;\n}\n\n.cn-link-button-widget__list--horizontal {\n\tflex-direction: row;\n\tflex-wrap: wrap;\n\talign-items: flex-start;\n}\n\n.cn-link-button-widget__list-item-wrap {\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-link-button-widget__list-item {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\twidth: 100%;\n\tpadding: 8px 12px;\n\tborder: none;\n\tborder-radius: var(--border-radius, 6px);\n\tbox-sizing: border-box;\n\tcursor: pointer;\n\tfont-size: 14px;\n\tfont-weight: 500;\n\ttext-align: left;\n\ttext-decoration: none;\n\ttransition: transform 0.15s ease, box-shadow 0.15s ease;\n}\n\n.cn-link-button-widget__list-item--horizontal {\n\twidth: auto;\n}\n\n.cn-link-button-widget__list-item:disabled {\n\topacity: 0.6;\n\tcursor: not-allowed;\n}\n\n.cn-link-button-widget__list-item:hover:not(:disabled) {\n\ttransform: translateY(-2px);\n\tbox-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);\n}\n\n.cn-link-button-widget__list-icon {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 24px;\n\theight: 24px;\n\tflex-shrink: 0;\n}\n\n.cn-link-button-widget__list-label {\n\tdisplay: inline-block;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["CnWidgetIcon","t"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CnLinkButtonWidget.vue3.js","sources":["../../../../src/components/CnLinkButtonWidget/CnLinkButtonWidget.vue?vue&type=template&id=23af432f&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div class=\"cn-link-button-widget\" :class=\"rootClass\">\n\t\t<!-- Single-button mode. Default for legacy / button-mode placements. -->\n\t\t<!-- An external URL is a real link, except in edit mode or while busy,\n\t\t where it renders as a button so a click cannot navigate. -->\n\t\t<component\n\t\t\t:is=\"singleLinkAttrs ? 'a' : 'button'\"\n\t\t\tv-if=\"!isListMode\"\n\t\t\tv-bind=\"singleLinkAttrs || { type: 'button', disabled: isExecuting }\"\n\t\t\tclass=\"cn-link-button-widget__button\"\n\t\t\t:style=\"buttonStyle\"\n\t\t\t@click=\"onSingleClick\">\n\t\t\t<span v-if=\"hasIcon\" class=\"cn-link-button-widget__icon\">\n\t\t\t\t<CnWidgetIcon :name=\"icon\" :size=\"48\" />\n\t\t\t</span>\n\t\t\t<span class=\"cn-link-button-widget__label\">{{ displayLabel }}</span>\n\t\t</component>\n\n\t\t<!-- Vertical list mode. -->\n\t\t<ul\n\t\t\tv-else-if=\"isVerticalList\"\n\t\t\trole=\"list\"\n\t\t\tclass=\"cn-link-button-widget__list cn-link-button-widget__list--vertical\"\n\t\t\t:style=\"listContainerStyle\">\n\t\t\t<li\n\t\t\t\tv-for=\"(link, index) in renderableLinks\"\n\t\t\t\t:key=\"`link-${index}`\"\n\t\t\t\tclass=\"cn-link-button-widget__list-item-wrap\">\n\t\t\t\t<component\n\t\t\t\t\t:is=\"linkAttrsFor(link.actionType, link.url) ? 'a' : 'button'\"\n\t\t\t\t\tv-bind=\"linkAttrsFor(link.actionType, link.url) || { type: 'button', disabled: isExecuting }\"\n\t\t\t\t\tclass=\"cn-link-button-widget__list-item\"\n\t\t\t\t\t:style=\"listItemStyle(link)\"\n\t\t\t\t\t:aria-label=\"link.label || ''\"\n\t\t\t\t\t@click=\"onListClick(link)\">\n\t\t\t\t\t<span v-if=\"link.icon\" class=\"cn-link-button-widget__list-icon\">\n\t\t\t\t\t\t<CnWidgetIcon :name=\"link.icon\" :size=\"24\" />\n\t\t\t\t\t</span>\n\t\t\t\t\t<span class=\"cn-link-button-widget__list-label\">{{ link.label || '' }}</span>\n\t\t\t\t</component>\n\t\t\t</li>\n\t\t</ul>\n\n\t\t<!-- Horizontal list mode. -->\n\t\t<div\n\t\t\tv-else\n\t\t\trole=\"list\"\n\t\t\tclass=\"cn-link-button-widget__list cn-link-button-widget__list--horizontal\"\n\t\t\t:style=\"listContainerStyle\">\n\t\t\t<div\n\t\t\t\tv-for=\"(link, index) in renderableLinks\"\n\t\t\t\t:key=\"`link-${index}`\"\n\t\t\t\trole=\"listitem\"\n\t\t\t\tclass=\"cn-link-button-widget__list-item-wrap cn-link-button-widget__list-item-wrap--horizontal\">\n\t\t\t\t<component\n\t\t\t\t\t:is=\"linkAttrsFor(link.actionType, link.url) ? 'a' : 'button'\"\n\t\t\t\t\tv-bind=\"linkAttrsFor(link.actionType, link.url) || { type: 'button', disabled: isExecuting }\"\n\t\t\t\t\tclass=\"cn-link-button-widget__list-item cn-link-button-widget__list-item--horizontal\"\n\t\t\t\t\t:style=\"listItemStyle(link)\"\n\t\t\t\t\t:aria-label=\"link.label || ''\"\n\t\t\t\t\t@click=\"onListClick(link)\">\n\t\t\t\t\t<span v-if=\"link.icon\" class=\"cn-link-button-widget__list-icon\">\n\t\t\t\t\t\t<CnWidgetIcon :name=\"link.icon\" :size=\"24\" />\n\t\t\t\t\t</span>\n\t\t\t\t\t<span class=\"cn-link-button-widget__list-label\">{{ link.label || '' }}</span>\n\t\t\t\t</component>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport CnWidgetIcon from '../CnWidgetGrid/CnWidgetIcon.vue'\nimport { safeHref } from '../../utils/safeHref.js'\n\nconst ACTION_TYPES = Object.freeze({\n\tEXTERNAL: 'external',\n\tINTERNAL: 'internal',\n\tCREATE_FILE: 'createFile',\n})\n\nconst DISPLAY_MODES = Object.freeze({\n\tBUTTON: 'button',\n\tLIST: 'list',\n})\n\nconst ORIENTATIONS = Object.freeze({\n\tVERTICAL: 'vertical',\n\tHORIZONTAL: 'horizontal',\n})\n\nconst GAPS = Object.freeze({\n\tCOMPACT: 'compact',\n\tNORMAL: 'normal',\n\tSPACIOUS: 'spacious',\n})\n\nconst GAP_REM = Object.freeze({\n\tcompact: '0.5rem',\n\tnormal: '1rem',\n\tspacious: '1.5rem',\n})\n\n/**\n * CnLinkButtonWidget — renders a styled clickable tile (or a list of tiles)\n * that dispatches one of three action types per entry:\n *\n * - `external` → renders as an `<a target=\"_blank\" rel=\"noopener\n * noreferrer\">` to the configured `url` (a button in edit mode).\n * - `internal` → emits an `internal-action` event with the action id so a\n * host app can dispatch its own registered handler (the library ships no\n * action registry). An empty/missing handler is a silent no-op.\n * - `createFile` → emits a `create-file` event with the extension token so\n * a host app can run its file-creation flow.\n *\n * `displayMode = 'list'` renders `content.links[]` as a vertical `<ul>` stack\n * or a horizontal `<div>` row of pills; each link reuses the same action\n * types, icon resolution, and edit-mode suppression as the single button.\n * Default / legacy placements (`displayMode` absent) render the single button\n * unchanged.\n *\n * Click is suppressed while the dashboard is in edit mode (`isAdmin === true`\n * AND `canEdit === true`) and while an action is in flight.\n *\n * Registered as the `link` dashboard widget type via the renderer's\n * `index.js`.\n */\nexport default {\n\tname: 'CnLinkButtonWidget',\n\n\tcomponents: {\n\t\tCnWidgetIcon,\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Persisted widget content. Single-button shape: `{label, url, icon,\n\t\t * actionType, backgroundColor, textColor}`. List shape adds\n\t\t * `{displayMode: 'list', listOrientation, listItemGap, links: [{label,\n\t\t * url, icon, actionType, value?, backgroundColor?, textColor?}, …]}`.\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Whether the current user is an admin. Combined with `canEdit` to\n\t\t * suppress click handlers in edit mode.\n\t\t *\n\t\t * @type {boolean}\n\t\t */\n\t\tisAdmin: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Whether the surrounding dashboard shell is in edit mode. Suppresses\n\t\t * click handlers when both this and `isAdmin` are true.\n\t\t *\n\t\t * @type {boolean}\n\t\t */\n\t\tcanEdit: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\t},\n\n\temits: [\n\t\t/**\n\t\t * Emitted on an `internal` action click so a host app can dispatch its\n\t\t * own handler.\n\t\t *\n\t\t * @event internal-action\n\t\t * @type {string}\n\t\t */\n\t\t'internal-action',\n\t\t/**\n\t\t * Emitted on a `createFile` action click; payload is the extension\n\t\t * token (e.g. `'docx'`).\n\t\t *\n\t\t * @event create-file\n\t\t * @type {string}\n\t\t */\n\t\t'create-file',\n\t],\n\n\tdata() {\n\t\treturn {\n\t\t\tisExecuting: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** The single-button label, or '' when absent. */\n\t\tlabel() {\n\t\t\treturn typeof this.content?.label === 'string' ? this.content.label : ''\n\t\t},\n\n\t\t/** The single-button URL / action token, or '' when absent. */\n\t\turl() {\n\t\t\treturn typeof this.content?.url === 'string' ? this.content.url : ''\n\t\t},\n\n\t\t/** The single-button icon, or '' when absent. */\n\t\ticon() {\n\t\t\treturn typeof this.content?.icon === 'string' ? this.content.icon : ''\n\t\t},\n\n\t\t/** The single-button action type (defaults to `external`). */\n\t\tactionType() {\n\t\t\tconst declared = this.content?.actionType\n\t\t\tif (declared === ACTION_TYPES.INTERNAL || declared === ACTION_TYPES.CREATE_FILE) {\n\t\t\t\treturn declared\n\t\t\t}\n\t\t\treturn ACTION_TYPES.EXTERNAL\n\t\t},\n\n\t\t/** Resolved widget background colour (default theme primary). */\n\t\tbackgroundColor() {\n\t\t\tconst value = this.content?.backgroundColor\n\t\t\treturn (typeof value === 'string' && value !== '') ? value : 'var(--color-primary)'\n\t\t},\n\n\t\t/** Resolved widget text colour (default theme primary text). */\n\t\ttextColor() {\n\t\t\tconst value = this.content?.textColor\n\t\t\treturn (typeof value === 'string' && value !== '') ? value : 'var(--color-primary-text)'\n\t\t},\n\n\t\t/** Whether the single button has an icon. */\n\t\thasIcon() {\n\t\t\treturn this.icon !== ''\n\t\t},\n\n\t\t/** The button label, falling back to a localised placeholder. */\n\t\tdisplayLabel() {\n\t\t\treturn this.label !== '' ? this.label : t('nextcloud-vue', 'Link Button')\n\t\t},\n\n\t\t/** Inline style for the single button. */\n\t\tbuttonStyle() {\n\t\t\treturn {\n\t\t\t\t'background-color': this.backgroundColor,\n\t\t\t\tcolor: this.textColor,\n\t\t\t}\n\t\t},\n\n\t\t/** Whether the dashboard is in edit mode (clicks suppressed). */\n\t\tisInEditMode() {\n\t\t\treturn this.isAdmin === true && this.canEdit === true\n\t\t},\n\n\t\t/** Anchor attributes for the single button, or null when it stays a button. */\n\t\tsingleLinkAttrs() {\n\t\t\treturn this.linkAttrsFor(this.actionType, this.url)\n\t\t},\n\n\t\t/** Resolved display mode (legacy / unset falls back to `button`). */\n\t\tdisplayMode() {\n\t\t\treturn this.content?.displayMode === DISPLAY_MODES.LIST\n\t\t\t\t? DISPLAY_MODES.LIST\n\t\t\t\t: DISPLAY_MODES.BUTTON\n\t\t},\n\n\t\t/** Whether list mode is active (mode is list AND links non-empty). */\n\t\tisListMode() {\n\t\t\treturn this.displayMode === DISPLAY_MODES.LIST\n\t\t\t\t&& Array.isArray(this.content?.links)\n\t\t\t\t&& this.content.links.length > 0\n\t\t},\n\n\t\t/** Resolved list orientation (default `vertical`). */\n\t\tlistOrientation() {\n\t\t\treturn this.content?.listOrientation === ORIENTATIONS.HORIZONTAL\n\t\t\t\t? ORIENTATIONS.HORIZONTAL\n\t\t\t\t: ORIENTATIONS.VERTICAL\n\t\t},\n\n\t\t/** Whether the list renders vertically. */\n\t\tisVerticalList() {\n\t\t\treturn this.listOrientation === ORIENTATIONS.VERTICAL\n\t\t},\n\n\t\t/** Resolved list item spacing (default `normal`). */\n\t\tlistItemGap() {\n\t\t\tconst declared = this.content?.listItemGap\n\t\t\tif (declared === GAPS.COMPACT || declared === GAPS.SPACIOUS) {\n\t\t\t\treturn declared\n\t\t\t}\n\t\t\treturn GAPS.NORMAL\n\t\t},\n\n\t\t/** The CSS gap value for the active spacing setting. */\n\t\tlistGapValue() {\n\t\t\treturn GAP_REM[this.listItemGap]\n\t\t},\n\n\t\t/** Inline style applying the list gap. */\n\t\tlistContainerStyle() {\n\t\t\treturn { gap: this.listGapValue }\n\t\t},\n\n\t\t/** Normalised list of link entries safe for the renderer. */\n\t\trenderableLinks() {\n\t\t\tif (Array.isArray(this.content?.links) === false) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn this.content.links.map((raw) => {\n\t\t\t\tconst link = (raw !== null && typeof raw === 'object') ? raw : {}\n\t\t\t\tconst declaredAction = link.actionType\n\t\t\t\tconst actionType = (declaredAction === ACTION_TYPES.INTERNAL\n\t\t\t\t\t|| declaredAction === ACTION_TYPES.CREATE_FILE)\n\t\t\t\t\t? declaredAction\n\t\t\t\t\t: ACTION_TYPES.EXTERNAL\n\t\t\t\treturn {\n\t\t\t\t\tlabel: typeof link.label === 'string' ? link.label : '',\n\t\t\t\t\turl: typeof link.url === 'string' ? link.url : '',\n\t\t\t\t\ticon: typeof link.icon === 'string' ? link.icon : '',\n\t\t\t\t\tactionType,\n\t\t\t\t\tvalue: typeof link.value === 'string' ? link.value : '',\n\t\t\t\t\tbackgroundColor: typeof link.backgroundColor === 'string' ? link.backgroundColor : '',\n\t\t\t\t\ttextColor: typeof link.textColor === 'string' ? link.textColor : '',\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/** Root CSS class map (list-mode + orientation modifiers). */\n\t\trootClass() {\n\t\t\treturn {\n\t\t\t\t'cn-link-button-widget--list': this.isListMode,\n\t\t\t\t[`cn-link-button-widget--list-${this.listOrientation}`]: this.isListMode,\n\t\t\t}\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Anchor attributes for an entry that should be a real link: an\n\t\t * `external` action with a URL, outside edit mode and not busy.\n\t\t *\n\t\t * @param {string} actionType the entry's action type.\n\t\t * @param {string} url the entry's URL.\n\t\t * @return {object|null} `{ href, target, rel }`, or null for a button.\n\t\t */\n\t\tlinkAttrsFor(actionType, url) {\n\t\t\tif (actionType !== ACTION_TYPES.EXTERNAL || typeof url !== 'string' || url === ''\n\t\t\t\t|| this.isInEditMode || this.isExecuting) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn { href: safeHref(url), target: '_blank', rel: 'noopener noreferrer' }\n\t\t},\n\n\t\t/**\n\t\t * Per-link inline style, falling back to the widget-level colours.\n\t\t *\n\t\t * @param {object} link the normalised link entry.\n\t\t * @return {object} the inline style object.\n\t\t */\n\t\tlistItemStyle(link) {\n\t\t\tconst bg = (typeof link.backgroundColor === 'string' && link.backgroundColor !== '')\n\t\t\t\t? link.backgroundColor\n\t\t\t\t: this.backgroundColor\n\t\t\tconst fg = (typeof link.textColor === 'string' && link.textColor !== '')\n\t\t\t\t? link.textColor\n\t\t\t\t: this.textColor\n\t\t\treturn {\n\t\t\t\t'background-color': bg,\n\t\t\t\tcolor: fg,\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Dispatch the single-button click (suppressed in edit mode / busy).\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tonSingleClick() {\n\t\t\t// A link navigates by itself.\n\t\t\tif (this.isInEditMode || this.isExecuting || this.singleLinkAttrs) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.dispatchAction({\n\t\t\t\tactionType: this.actionType,\n\t\t\t\turl: this.url,\n\t\t\t\tvalue: '',\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Dispatch a list-item click (suppressed in edit mode / busy).\n\t\t *\n\t\t * @param {object} link the normalised link entry.\n\t\t * @return {void}\n\t\t */\n\t\tonListClick(link) {\n\t\t\tif (this.isInEditMode || this.isExecuting || this.linkAttrsFor(link.actionType, link.url)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.dispatchAction({\n\t\t\t\tactionType: link.actionType,\n\t\t\t\turl: link.url,\n\t\t\t\tvalue: link.value,\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Route a click to the handler for its action type.\n\t\t *\n\t\t * @param {{actionType: string, url: string, value: string}} payload the dispatch payload.\n\t\t * @return {void}\n\t\t */\n\t\tdispatchAction({ actionType, url, value }) {\n\t\t\tswitch (actionType) {\n\t\t\t\tcase ACTION_TYPES.EXTERNAL:\n\t\t\t\t\tthis.handleExternal(url)\n\t\t\t\t\tbreak\n\t\t\t\tcase ACTION_TYPES.INTERNAL:\n\t\t\t\t\tthis.$emit('internal-action', url)\n\t\t\t\t\tbreak\n\t\t\t\tcase ACTION_TYPES.CREATE_FILE:\n\t\t\t\t// For list items the extension lives in `value`; for the\n\t\t\t\t// single button it lives in `url`.\n\t\t\t\t\tthis.$emit('create-file', value !== '' ? value : url)\n\t\t\t\t\tbreak\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Open an external URL in a new tab; `noopener,noreferrer` prevents\n\t\t * the opened page reaching back via `window.opener`.\n\t\t *\n\t\t * @param {string} url the URL to open.\n\t\t * @return {void}\n\t\t */\n\t\thandleExternal(url) {\n\t\t\tif (typeof url !== 'string' || url === '') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\twindow.open(url, '_blank', 'noopener,noreferrer')\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-link-button-widget {\n\twidth: 100%;\n\theight: 100%;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tpadding: 8px;\n}\n\n.cn-link-button-widget--list {\n\talign-items: stretch;\n\tjustify-content: stretch;\n}\n\n.cn-link-button-widget__button {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tjustify-content: center;\n\tgap: 8px;\n\twidth: 100%;\n\theight: 100%;\n\tmin-height: 96px;\n\tpadding: 12px;\n\tborder: none;\n\tborder-radius: var(--border-radius-large, 8px);\n\tbox-sizing: border-box;\n\tcursor: pointer;\n\tfont-size: 14px;\n\tfont-weight: 600;\n\ttext-decoration: none;\n\ttransition: transform 0.15s ease, box-shadow 0.15s ease;\n}\n\n.cn-link-button-widget__button:disabled {\n\topacity: 0.6;\n\tcursor: not-allowed;\n}\n\n.cn-link-button-widget__button:hover:not(:disabled) {\n\ttransform: translateY(-2px);\n\tbox-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);\n}\n\n.cn-link-button-widget__icon {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 48px;\n\theight: 48px;\n}\n\n.cn-link-button-widget__label {\n\tdisplay: block;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\tmax-width: 100%;\n}\n\n.cn-link-button-widget__list {\n\twidth: 100%;\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n\tdisplay: flex;\n}\n\n.cn-link-button-widget__list--vertical {\n\tflex-direction: column;\n}\n\n.cn-link-button-widget__list--horizontal {\n\tflex-direction: row;\n\tflex-wrap: wrap;\n\talign-items: flex-start;\n}\n\n.cn-link-button-widget__list-item-wrap {\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-link-button-widget__list-item {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\twidth: 100%;\n\tpadding: 8px 12px;\n\tborder: none;\n\tborder-radius: var(--border-radius, 6px);\n\tbox-sizing: border-box;\n\tcursor: pointer;\n\tfont-size: 14px;\n\tfont-weight: 500;\n\ttext-align: left;\n\ttext-decoration: none;\n\ttransition: transform 0.15s ease, box-shadow 0.15s ease;\n}\n\n.cn-link-button-widget__list-item--horizontal {\n\twidth: auto;\n}\n\n.cn-link-button-widget__list-item:disabled {\n\topacity: 0.6;\n\tcursor: not-allowed;\n}\n\n.cn-link-button-widget__list-item:hover:not(:disabled) {\n\ttransform: translateY(-2px);\n\tbox-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);\n}\n\n.cn-link-button-widget__list-icon {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 24px;\n\theight: 24px;\n\tflex-shrink: 0;\n}\n\n.cn-link-button-widget__list-label {\n\tdisplay: inline-block;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["_createElementBlock","_normalizeClass","_openBlock","_createBlock","_resolveDynamicComponent","_mergeProps","_createVNode","_createElementVNode","_toDisplayString","_Fragment","_renderList"],"mappings":"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"}
1
+ {"version":3,"file":"CnLinkButtonWidget.vue3.js","sources":["../../../../src/components/CnLinkButtonWidget/CnLinkButtonWidget.vue?vue&type=template&id=23af432f&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: EUPL-1.2\n-->\n\n<template>\n\t<div class=\"cn-link-button-widget\" :class=\"rootClass\">\n\t\t<!-- Single-button mode. Default for legacy / button-mode placements. -->\n\t\t<!-- An external URL is a real link, except in edit mode or while busy,\n\t\t where it renders as a button so a click cannot navigate. -->\n\t\t<component\n\t\t\t:is=\"singleLinkAttrs ? 'a' : 'button'\"\n\t\t\tv-if=\"!isListMode\"\n\t\t\tv-bind=\"singleLinkAttrs || { type: 'button', disabled: isExecuting }\"\n\t\t\tclass=\"cn-link-button-widget__button\"\n\t\t\t:style=\"buttonStyle\"\n\t\t\t@click=\"onSingleClick\">\n\t\t\t<span v-if=\"hasIcon\" class=\"cn-link-button-widget__icon\">\n\t\t\t\t<CnWidgetIcon :name=\"icon\" :size=\"48\" />\n\t\t\t</span>\n\t\t\t<span class=\"cn-link-button-widget__label\">{{ displayLabel }}</span>\n\t\t</component>\n\n\t\t<!-- Vertical list mode. -->\n\t\t<ul\n\t\t\tv-else-if=\"isVerticalList\"\n\t\t\trole=\"list\"\n\t\t\tclass=\"cn-link-button-widget__list cn-link-button-widget__list--vertical\"\n\t\t\t:style=\"listContainerStyle\">\n\t\t\t<li\n\t\t\t\tv-for=\"(link, index) in renderableLinks\"\n\t\t\t\t:key=\"`link-${index}`\"\n\t\t\t\tclass=\"cn-link-button-widget__list-item-wrap\">\n\t\t\t\t<component\n\t\t\t\t\t:is=\"linkAttrsFor(link.actionType, link.url) ? 'a' : 'button'\"\n\t\t\t\t\tv-bind=\"linkAttrsFor(link.actionType, link.url) || { type: 'button', disabled: isExecuting }\"\n\t\t\t\t\tclass=\"cn-link-button-widget__list-item\"\n\t\t\t\t\t:style=\"listItemStyle(link)\"\n\t\t\t\t\t:aria-label=\"link.label || ''\"\n\t\t\t\t\t@click=\"onListClick(link)\">\n\t\t\t\t\t<span v-if=\"link.icon\" class=\"cn-link-button-widget__list-icon\">\n\t\t\t\t\t\t<CnWidgetIcon :name=\"link.icon\" :size=\"24\" />\n\t\t\t\t\t</span>\n\t\t\t\t\t<span class=\"cn-link-button-widget__list-label\">{{ link.label || '' }}</span>\n\t\t\t\t</component>\n\t\t\t</li>\n\t\t</ul>\n\n\t\t<!-- Horizontal list mode. -->\n\t\t<div\n\t\t\tv-else\n\t\t\trole=\"list\"\n\t\t\tclass=\"cn-link-button-widget__list cn-link-button-widget__list--horizontal\"\n\t\t\t:style=\"listContainerStyle\">\n\t\t\t<div\n\t\t\t\tv-for=\"(link, index) in renderableLinks\"\n\t\t\t\t:key=\"`link-${index}`\"\n\t\t\t\trole=\"listitem\"\n\t\t\t\tclass=\"cn-link-button-widget__list-item-wrap cn-link-button-widget__list-item-wrap--horizontal\">\n\t\t\t\t<component\n\t\t\t\t\t:is=\"linkAttrsFor(link.actionType, link.url) ? 'a' : 'button'\"\n\t\t\t\t\tv-bind=\"linkAttrsFor(link.actionType, link.url) || { type: 'button', disabled: isExecuting }\"\n\t\t\t\t\tclass=\"cn-link-button-widget__list-item cn-link-button-widget__list-item--horizontal\"\n\t\t\t\t\t:style=\"listItemStyle(link)\"\n\t\t\t\t\t:aria-label=\"link.label || ''\"\n\t\t\t\t\t@click=\"onListClick(link)\">\n\t\t\t\t\t<span v-if=\"link.icon\" class=\"cn-link-button-widget__list-icon\">\n\t\t\t\t\t\t<CnWidgetIcon :name=\"link.icon\" :size=\"24\" />\n\t\t\t\t\t</span>\n\t\t\t\t\t<span class=\"cn-link-button-widget__list-label\">{{ link.label || '' }}</span>\n\t\t\t\t</component>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport CnWidgetIcon from '../CnWidgetGrid/CnWidgetIcon.vue'\nimport { safeHref } from '../../utils/safeHref.js'\n\nconst ACTION_TYPES = Object.freeze({\n\tEXTERNAL: 'external',\n\tINTERNAL: 'internal',\n\tCREATE_FILE: 'createFile',\n})\n\nconst DISPLAY_MODES = Object.freeze({\n\tBUTTON: 'button',\n\tLIST: 'list',\n})\n\nconst ORIENTATIONS = Object.freeze({\n\tVERTICAL: 'vertical',\n\tHORIZONTAL: 'horizontal',\n})\n\nconst GAPS = Object.freeze({\n\tCOMPACT: 'compact',\n\tNORMAL: 'normal',\n\tSPACIOUS: 'spacious',\n})\n\nconst GAP_REM = Object.freeze({\n\tcompact: '0.5rem',\n\tnormal: '1rem',\n\tspacious: '1.5rem',\n})\n\n/**\n * CnLinkButtonWidget — renders a styled clickable tile (or a list of tiles)\n * that dispatches one of three action types per entry:\n *\n * - `external` → renders as an `<a target=\"_blank\" rel=\"noopener\n * noreferrer\">` to the configured `url` (a button in edit mode).\n * - `internal` → emits an `internal-action` event with the action id so a\n * host app can dispatch its own registered handler (the library ships no\n * action registry). An empty/missing handler is a silent no-op.\n * - `createFile` → emits a `create-file` event with the extension token so\n * a host app can run its file-creation flow.\n *\n * `displayMode = 'list'` renders `content.links[]` as a vertical `<ul>` stack\n * or a horizontal `<div>` row of pills; each link reuses the same action\n * types, icon resolution, and edit-mode suppression as the single button.\n * Default / legacy placements (`displayMode` absent) render the single button\n * unchanged.\n *\n * Click is suppressed while the dashboard is in edit mode (`isAdmin === true`\n * AND `canEdit === true`) and while an action is in flight.\n *\n * Registered as the `link` dashboard widget type via the renderer's\n * `index.js`.\n */\nexport default {\n\tname: 'CnLinkButtonWidget',\n\n\tcomponents: {\n\t\tCnWidgetIcon,\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Persisted widget content. Single-button shape: `{label, url, icon,\n\t\t * actionType, backgroundColor, textColor}`. List shape adds\n\t\t * `{displayMode: 'list', listOrientation, listItemGap, links: [{label,\n\t\t * url, icon, actionType, value?, backgroundColor?, textColor?}, …]}`.\n\t\t *\n\t\t * @type {object}\n\t\t */\n\t\tcontent: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Whether the current user is an admin. Combined with `canEdit` to\n\t\t * suppress click handlers in edit mode.\n\t\t *\n\t\t * @type {boolean}\n\t\t */\n\t\tisAdmin: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Whether the surrounding dashboard shell is in edit mode. Suppresses\n\t\t * click handlers when both this and `isAdmin` are true.\n\t\t *\n\t\t * @type {boolean}\n\t\t */\n\t\tcanEdit: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\t},\n\n\temits: [\n\t\t/**\n\t\t * Emitted on an `internal` action click so a host app can dispatch its\n\t\t * own handler.\n\t\t *\n\t\t * @event internal-action\n\t\t * @type {string}\n\t\t */\n\t\t'internal-action',\n\t\t/**\n\t\t * Emitted on a `createFile` action click; payload is the extension\n\t\t * token (e.g. `'docx'`).\n\t\t *\n\t\t * @event create-file\n\t\t * @type {string}\n\t\t */\n\t\t'create-file',\n\t],\n\n\tdata() {\n\t\treturn {\n\t\t\tisExecuting: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** The single-button label, or '' when absent. */\n\t\tlabel() {\n\t\t\treturn typeof this.content?.label === 'string' ? this.content.label : ''\n\t\t},\n\n\t\t/** The single-button URL / action token, or '' when absent. */\n\t\turl() {\n\t\t\treturn typeof this.content?.url === 'string' ? this.content.url : ''\n\t\t},\n\n\t\t/** The single-button icon, or '' when absent. */\n\t\ticon() {\n\t\t\treturn typeof this.content?.icon === 'string' ? this.content.icon : ''\n\t\t},\n\n\t\t/** The single-button action type (defaults to `external`). */\n\t\tactionType() {\n\t\t\tconst declared = this.content?.actionType\n\t\t\tif (declared === ACTION_TYPES.INTERNAL || declared === ACTION_TYPES.CREATE_FILE) {\n\t\t\t\treturn declared\n\t\t\t}\n\t\t\treturn ACTION_TYPES.EXTERNAL\n\t\t},\n\n\t\t/** Resolved widget background colour (default theme primary). */\n\t\tbackgroundColor() {\n\t\t\tconst value = this.content?.backgroundColor\n\t\t\treturn (typeof value === 'string' && value !== '') ? value : 'var(--color-primary)'\n\t\t},\n\n\t\t/** Resolved widget text colour (default theme primary text). */\n\t\ttextColor() {\n\t\t\tconst value = this.content?.textColor\n\t\t\treturn (typeof value === 'string' && value !== '') ? value : 'var(--color-primary-text)'\n\t\t},\n\n\t\t/** Whether the single button has an icon. */\n\t\thasIcon() {\n\t\t\treturn this.icon !== ''\n\t\t},\n\n\t\t/** The button label, falling back to a localised placeholder. */\n\t\tdisplayLabel() {\n\t\t\treturn this.label !== '' ? this.label : t('nextcloud-vue', 'Link Button')\n\t\t},\n\n\t\t/** Inline style for the single button. */\n\t\tbuttonStyle() {\n\t\t\treturn {\n\t\t\t\t'background-color': this.backgroundColor,\n\t\t\t\tcolor: this.textColor,\n\t\t\t}\n\t\t},\n\n\t\t/** Whether the dashboard is in edit mode (clicks suppressed). */\n\t\tisInEditMode() {\n\t\t\treturn this.isAdmin === true && this.canEdit === true\n\t\t},\n\n\t\t/** Anchor attributes for the single button, or null when it stays a button. */\n\t\tsingleLinkAttrs() {\n\t\t\treturn this.linkAttrsFor(this.actionType, this.url)\n\t\t},\n\n\t\t/** Resolved display mode (legacy / unset falls back to `button`). */\n\t\tdisplayMode() {\n\t\t\treturn this.content?.displayMode === DISPLAY_MODES.LIST\n\t\t\t\t? DISPLAY_MODES.LIST\n\t\t\t\t: DISPLAY_MODES.BUTTON\n\t\t},\n\n\t\t/** Whether list mode is active (mode is list AND links non-empty). */\n\t\tisListMode() {\n\t\t\treturn this.displayMode === DISPLAY_MODES.LIST\n\t\t\t\t&& Array.isArray(this.content?.links)\n\t\t\t\t&& this.content.links.length > 0\n\t\t},\n\n\t\t/** Resolved list orientation (default `vertical`). */\n\t\tlistOrientation() {\n\t\t\treturn this.content?.listOrientation === ORIENTATIONS.HORIZONTAL\n\t\t\t\t? ORIENTATIONS.HORIZONTAL\n\t\t\t\t: ORIENTATIONS.VERTICAL\n\t\t},\n\n\t\t/** Whether the list renders vertically. */\n\t\tisVerticalList() {\n\t\t\treturn this.listOrientation === ORIENTATIONS.VERTICAL\n\t\t},\n\n\t\t/** Resolved list item spacing (default `normal`). */\n\t\tlistItemGap() {\n\t\t\tconst declared = this.content?.listItemGap\n\t\t\tif (declared === GAPS.COMPACT || declared === GAPS.SPACIOUS) {\n\t\t\t\treturn declared\n\t\t\t}\n\t\t\treturn GAPS.NORMAL\n\t\t},\n\n\t\t/** The CSS gap value for the active spacing setting. */\n\t\tlistGapValue() {\n\t\t\treturn GAP_REM[this.listItemGap]\n\t\t},\n\n\t\t/** Inline style applying the list gap. */\n\t\tlistContainerStyle() {\n\t\t\treturn { gap: this.listGapValue }\n\t\t},\n\n\t\t/** Normalised list of link entries safe for the renderer. */\n\t\trenderableLinks() {\n\t\t\tif (Array.isArray(this.content?.links) === false) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn this.content.links.map((raw) => {\n\t\t\t\tconst link = (raw !== null && typeof raw === 'object') ? raw : {}\n\t\t\t\tconst declaredAction = link.actionType\n\t\t\t\tconst actionType = (declaredAction === ACTION_TYPES.INTERNAL\n\t\t\t\t\t|| declaredAction === ACTION_TYPES.CREATE_FILE)\n\t\t\t\t\t? declaredAction\n\t\t\t\t\t: ACTION_TYPES.EXTERNAL\n\t\t\t\treturn {\n\t\t\t\t\tlabel: typeof link.label === 'string' ? link.label : '',\n\t\t\t\t\turl: typeof link.url === 'string' ? link.url : '',\n\t\t\t\t\ticon: typeof link.icon === 'string' ? link.icon : '',\n\t\t\t\t\tactionType,\n\t\t\t\t\tvalue: typeof link.value === 'string' ? link.value : '',\n\t\t\t\t\tbackgroundColor: typeof link.backgroundColor === 'string' ? link.backgroundColor : '',\n\t\t\t\t\ttextColor: typeof link.textColor === 'string' ? link.textColor : '',\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/** Root CSS class map (list-mode + orientation modifiers). */\n\t\trootClass() {\n\t\t\treturn {\n\t\t\t\t'cn-link-button-widget--list': this.isListMode,\n\t\t\t\t[`cn-link-button-widget--list-${this.listOrientation}`]: this.isListMode,\n\t\t\t}\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Anchor attributes for an entry that should be a real link: an\n\t\t * `external` action with a URL, outside edit mode and not busy.\n\t\t *\n\t\t * @param {string} actionType the entry's action type.\n\t\t * @param {string} url the entry's URL.\n\t\t * @return {object|null} `{ href, target, rel }`, or null for a button.\n\t\t */\n\t\tlinkAttrsFor(actionType, url) {\n\t\t\tif (actionType !== ACTION_TYPES.EXTERNAL || typeof url !== 'string' || url === ''\n\t\t\t\t|| this.isInEditMode || this.isExecuting) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn { href: safeHref(url), target: '_blank', rel: 'noopener noreferrer' }\n\t\t},\n\n\t\t/**\n\t\t * Per-link inline style, falling back to the widget-level colours.\n\t\t *\n\t\t * @param {object} link the normalised link entry.\n\t\t * @return {object} the inline style object.\n\t\t */\n\t\tlistItemStyle(link) {\n\t\t\tconst bg = (typeof link.backgroundColor === 'string' && link.backgroundColor !== '')\n\t\t\t\t? link.backgroundColor\n\t\t\t\t: this.backgroundColor\n\t\t\tconst fg = (typeof link.textColor === 'string' && link.textColor !== '')\n\t\t\t\t? link.textColor\n\t\t\t\t: this.textColor\n\t\t\treturn {\n\t\t\t\t'background-color': bg,\n\t\t\t\tcolor: fg,\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Dispatch the single-button click (suppressed in edit mode / busy).\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tonSingleClick() {\n\t\t\t// A link navigates by itself.\n\t\t\tif (this.isInEditMode || this.isExecuting || this.singleLinkAttrs) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.dispatchAction({\n\t\t\t\tactionType: this.actionType,\n\t\t\t\turl: this.url,\n\t\t\t\tvalue: '',\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Dispatch a list-item click (suppressed in edit mode / busy).\n\t\t *\n\t\t * @param {object} link the normalised link entry.\n\t\t * @return {void}\n\t\t */\n\t\tonListClick(link) {\n\t\t\tif (this.isInEditMode || this.isExecuting || this.linkAttrsFor(link.actionType, link.url)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.dispatchAction({\n\t\t\t\tactionType: link.actionType,\n\t\t\t\turl: link.url,\n\t\t\t\tvalue: link.value,\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Route a click to the handler for its action type.\n\t\t *\n\t\t * @param {{actionType: string, url: string, value: string}} payload the dispatch payload.\n\t\t * @return {void}\n\t\t */\n\t\tdispatchAction({ actionType, url, value }) {\n\t\t\t// An `external` entry with a URL is a real link (see linkAttrsFor);\n\t\t\t// one without a URL has nothing to open.\n\t\t\tswitch (actionType) {\n\t\t\t\tcase ACTION_TYPES.INTERNAL:\n\t\t\t\t\tthis.$emit('internal-action', url)\n\t\t\t\t\tbreak\n\t\t\t\tcase ACTION_TYPES.CREATE_FILE:\n\t\t\t\t// For list items the extension lives in `value`; for the\n\t\t\t\t// single button it lives in `url`.\n\t\t\t\t\tthis.$emit('create-file', value !== '' ? value : url)\n\t\t\t\t\tbreak\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-link-button-widget {\n\twidth: 100%;\n\theight: 100%;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tpadding: 8px;\n}\n\n.cn-link-button-widget--list {\n\talign-items: stretch;\n\tjustify-content: stretch;\n}\n\n.cn-link-button-widget__button {\n\tdisplay: flex;\n\tflex-direction: column;\n\talign-items: center;\n\tjustify-content: center;\n\tgap: 8px;\n\twidth: 100%;\n\theight: 100%;\n\tmin-height: 96px;\n\tpadding: 12px;\n\tborder: none;\n\tborder-radius: var(--border-radius-large, 8px);\n\tbox-sizing: border-box;\n\tcursor: pointer;\n\tfont-size: 14px;\n\tfont-weight: 600;\n\ttext-decoration: none;\n\ttransition: transform 0.15s ease, box-shadow 0.15s ease;\n}\n\n.cn-link-button-widget__button:disabled {\n\topacity: 0.6;\n\tcursor: not-allowed;\n}\n\n.cn-link-button-widget__button:hover:not(:disabled) {\n\ttransform: translateY(-2px);\n\tbox-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);\n}\n\n.cn-link-button-widget__icon {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 48px;\n\theight: 48px;\n}\n\n.cn-link-button-widget__label {\n\tdisplay: block;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\tmax-width: 100%;\n}\n\n.cn-link-button-widget__list {\n\twidth: 100%;\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n\tdisplay: flex;\n}\n\n.cn-link-button-widget__list--vertical {\n\tflex-direction: column;\n}\n\n.cn-link-button-widget__list--horizontal {\n\tflex-direction: row;\n\tflex-wrap: wrap;\n\talign-items: flex-start;\n}\n\n.cn-link-button-widget__list-item-wrap {\n\tmargin: 0;\n\tpadding: 0;\n\tlist-style: none;\n}\n\n.cn-link-button-widget__list-item {\n\tdisplay: flex;\n\talign-items: center;\n\tgap: 8px;\n\twidth: 100%;\n\tpadding: 8px 12px;\n\tborder: none;\n\tborder-radius: var(--border-radius, 6px);\n\tbox-sizing: border-box;\n\tcursor: pointer;\n\tfont-size: 14px;\n\tfont-weight: 500;\n\ttext-align: left;\n\ttext-decoration: none;\n\ttransition: transform 0.15s ease, box-shadow 0.15s ease;\n}\n\n.cn-link-button-widget__list-item--horizontal {\n\twidth: auto;\n}\n\n.cn-link-button-widget__list-item:disabled {\n\topacity: 0.6;\n\tcursor: not-allowed;\n}\n\n.cn-link-button-widget__list-item:hover:not(:disabled) {\n\ttransform: translateY(-2px);\n\tbox-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);\n}\n\n.cn-link-button-widget__list-icon {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 24px;\n\theight: 24px;\n\tflex-shrink: 0;\n}\n\n.cn-link-button-widget__list-label {\n\tdisplay: inline-block;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["_createElementBlock","_normalizeClass","_openBlock","_createBlock","_resolveDynamicComponent","_mergeProps","_createVNode","_createElementVNode","_toDisplayString","_Fragment","_renderList"],"mappings":"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"}
@@ -314,14 +314,21 @@ var script = {
314
314
  */
315
315
  'action',
316
316
  /**
317
- * @event row-click Emitted when a log row's body is clicked or
318
- * middle-clicked, whether or not `rowDetail` opened the detail dialog —
319
- * so a host can navigate instead of (or as well as) showing the dialog.
320
- * Payload: `(row, event)`, the second being the native click/auxclick
321
- * event.
317
+ * @event row-click Emitted when a log row's body is clicked, whether or
318
+ * not `rowDetail` opened the detail dialog — so a host can navigate
319
+ * instead of (or as well as) showing the dialog. Payload: `(row, event)`,
320
+ * the second being the native click event. A middle click emits
321
+ * `row-aux-click` instead.
322
322
  * @type {object}
323
323
  */
324
324
  'row-click',
325
+ /**
326
+ * @event row-aux-click Emitted when a log row's body is middle-clicked,
327
+ * for opening it in a new tab. Payload: `(row, event)`, the second being
328
+ * the native auxclick event.
329
+ * @type {object}
330
+ */
331
+ 'row-aux-click',
325
332
  ],
326
333
 
327
334
  setup(props) {
@@ -824,26 +831,43 @@ var script = {
824
831
  /**
825
832
  * Handle a row-body click: navigate to `rowRoute` when one is declared,
826
833
  * else open the detail dialog when `rowDetail` is set. Always re-emits
827
- * so a host can do its own thing regardless. A ctrl/cmd/shift or middle
828
- * click opens `rowRoute` in a new tab; a middle click never opens the
829
- * dialog.
834
+ * so a host can do its own thing regardless. A ctrl/cmd/shift click
835
+ * opens `rowRoute` in a new tab.
830
836
  *
831
837
  * @param {object} row The clicked log entry.
832
- * @param {MouseEvent} [event] The originating click/auxclick event.
838
+ * @param {MouseEvent} [event] The originating click event.
833
839
  */
834
840
  onRowClick(row, event) {
835
841
  if (this.rowRoute) {
836
842
  markNewTabHandled(event, openRowTarget(event, { name: this.rowRoute, params: { id: row?.[this.rowKey] } }, this.$router));
837
- } else if (this.rowDetail && !(event && event.button === 1)) {
843
+ } else if (this.rowDetail) {
838
844
  this.detailRow = row;
839
845
  }
840
846
  /**
841
- * @event row-click Emitted when a log row's body is clicked or middle-clicked. Payload: `(row, event)` — the log entry and the native click/auxclick event.
847
+ * @event row-click Emitted when a log row's body is clicked. Payload: `(row, event)` — the log entry and the native click event.
842
848
  * @type {object}
843
849
  */
844
850
  this.$emit('row-click', row, event);
845
851
  },
846
852
 
853
+ /**
854
+ * Handle a row-body middle click: open `rowRoute` in a new tab, never
855
+ * the detail dialog, and emit `row-aux-click` rather than `row-click`.
856
+ *
857
+ * @param {object} row The clicked log entry.
858
+ * @param {MouseEvent} event The originating auxclick event.
859
+ */
860
+ onRowAuxClick(row, event) {
861
+ if (this.rowRoute) {
862
+ markNewTabHandled(event, openRowTarget(event, { name: this.rowRoute, params: { id: row?.[this.rowKey] } }, this.$router));
863
+ }
864
+ /**
865
+ * @event row-aux-click Emitted when a log row's body is middle-clicked. Payload: `(row, event)` — the log entry and the native auxclick event.
866
+ * @type {object}
867
+ */
868
+ this.$emit('row-aux-click', row, event);
869
+ },
870
+
847
871
  /** Close the row-detail dialog. */
848
872
  closeDetail() {
849
873
  this.detailRow = null;
@@ -1 +1 @@
1
- {"version":3,"file":"CnLogsPage.vue2.js","sources":["../../../../src/components/CnLogsPage/CnLogsPage.vue"],"sourcesContent":["<!--\n CnLogsPage — Read-only audit-trail / activity-log surface.\n\n Wraps CnDataTable with server-side sorting, pagination and filtering.\n Reads its data source from `config`:\n - { register, schema } → fetched via the OpenRegister object store\n - { source: '/api/url' } → fetched via axios.get(source)\n\n In store mode the list state (params, paging, sort, filters) is owned by\n the shared `useListView` composable, so a `?jobId=<uuid>`-style deep link\n lands the page pre-filtered — the same behaviour CnIndexPage has.\n\n Mounted by CnPageRenderer when a manifest page declares\n `type: \"logs\"`. Honours `headerComponent`, `actionsComponent`, and\n the generic `slots` map from `pages[]` (forwarded by the renderer\n through scoped slots; this component just exposes `#header`,\n `#actions`, and `#row-actions` slots which the renderer fills).\n\n Backwards-compat: when neither `register+schema` nor `source` is\n set, the component renders an empty-state with a console warning\n rather than throwing — this keeps a misconfigured manifest from\n breaking the app shell.\n-->\n<template>\n\t<div class=\"cn-logs-page\" data-testid=\"cn-logs-page\">\n\t\t<!-- @slot Replaces the default CnPageHeader. CnPageRenderer fills it from `pages[].headerComponent` / `slots.header`. -->\n\t\t<!-- @binding {string} title The page title. -->\n\t\t<!-- @binding {string} description The page description. -->\n\t\t<!-- @binding {string} icon The header's MDI icon name. -->\n\t\t<slot\n\t\t\tname=\"header\"\n\t\t\t:title=\"title\"\n\t\t\t:description=\"description\"\n\t\t\t:icon=\"icon\">\n\t\t\t<CnPageHeader\n\t\t\t\tv-if=\"title\"\n\t\t\t\t:title=\"title\"\n\t\t\t\t:description=\"description\"\n\t\t\t\t:icon=\"icon\"\n\t\t\t\t:visuallyHidden=\"!showTitle\" />\n\t\t</slot>\n\n\t\t<div v-if=\"$slots.actions\" class=\"cn-logs-page__actions\">\n\t\t\t<!-- @slot Right-aligned action area (refresh, export, …). CnPageRenderer fills it from `pages[].actionsComponent`. -->\n\t\t\t<slot name=\"actions\" />\n\t\t</div>\n\n\t\t<!-- Body -->\n\t\t<div class=\"cn-logs-page__body\">\n\t\t\t<!-- Loading state -->\n\t\t\t<div v-if=\"loading\" class=\"cn-logs-page__loading\">\n\t\t\t\t<NcLoadingIcon :size=\"32\" />\n\t\t\t</div>\n\n\t\t\t<!-- The error block below is a SIBLING of this chain, not part of it, so\n\t\t\t BOTH guards here are load-bearing and neither is sufficient alone.\n\t\t\t A failed fetch leaves the collection empty, so:\n\t\t\t - without `&& !error`, this branch renders \"no log entries\"\n\t\t\t stacked above \"could not load log entries\";\n\t\t\t - without the `rows.length > 0` guard on the table branch, the\n\t\t\t `v-else` catches that same state instead and mounts CnDataTable\n\t\t\t with zero rows, which renders its OWN empty row from the same\n\t\t\t `emptyText` — the identical contradiction, just inside a table.\n\t\t\t So: error + no rows shows the error alone; no error + no rows shows\n\t\t\t this block; rows present shows the table, with the error beneath it\n\t\t\t when a refresh failed over a still-populated collection. -->\n\t\t\t<div v-else-if=\"rows.length === 0 && !error\" class=\"cn-logs-page__empty\">\n\t\t\t\t<!-- @slot Replaces the empty-state block shown when there are no log entries. -->\n\t\t\t\t<slot name=\"empty\">\n\t\t\t\t\t<NcEmptyContent :name=\"emptyText\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<HistoryIcon :size=\"64\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcEmptyContent>\n\t\t\t\t</slot>\n\t\t\t</div>\n\n\t\t\t<!-- Table. `v-else-if=\"rows.length > 0\"` rather than a bare `v-else` —\n\t\t\t see the guard note above. -->\n\t\t\t<template v-else-if=\"rows.length > 0\">\n\t\t\t\t<CnDataTable\n\t\t\t\t\t:schema=\"tableSchema\"\n\t\t\t\t\t:columns=\"resolvedColumns\"\n\t\t\t\t\t:rows=\"rows\"\n\t\t\t\t\t:rowKey=\"rowKey\"\n\t\t\t\t\t:fixedLayout=\"fixedLayout\"\n\t\t\t\t\t:sortKey=\"effectiveSortKey\"\n\t\t\t\t\t:sortOrder=\"effectiveSortOrder\"\n\t\t\t\t\t:sortKeys=\"effectiveSortKeys\"\n\t\t\t\t\t:emptyText=\"emptyText\"\n\t\t\t\t\t@sort=\"onSort\"\n\t\t\t\t\t@rowClick=\"onRowClick\">\n\t\t\t\t\t<template\n\t\t\t\t\t\tv-for=\"col in slotColumns\"\n\t\t\t\t\t\t#[`column-${col}`]=\"{ row, value }\">\n\t\t\t\t\t\t<!-- @slot Per-column cell renderer (`#column-<key>`), one per configured column key. -->\n\t\t\t\t\t\t<!-- @binding {object} row The log entry being rendered. -->\n\t\t\t\t\t\t<!-- @binding {*} value The cell's resolved value. -->\n\t\t\t\t\t\t<slot :name=\"'column-' + col\" :row=\"row\" :value=\"value\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t<template v-if=\"$slots['row-actions']\" #row-actions=\"{ row }\">\n\t\t\t\t\t\t<!-- @slot Per-row action menu cell. Supplying it adds the trailing actions column. -->\n\t\t\t\t\t\t<!-- @binding {object} row The log entry for this row. -->\n\t\t\t\t\t\t<slot name=\"row-actions\" :row=\"row\" />\n\t\t\t\t\t</template>\n\t\t\t\t</CnDataTable>\n\n\t\t\t\t<CnPagination\n\t\t\t\t\tv-if=\"paginationState && paginationState.pages > 1\"\n\t\t\t\t\tclass=\"cn-logs-page__pagination\"\n\t\t\t\t\t:currentPage=\"paginationState.page || 1\"\n\t\t\t\t\t:totalPages=\"paginationState.pages || 1\"\n\t\t\t\t\t:totalItems=\"paginationState.total || 0\"\n\t\t\t\t\t:currentPageSize=\"paginationState.limit || 20\"\n\t\t\t\t\t@pageChanged=\"onPageChange\"\n\t\t\t\t\t@pageSizeChanged=\"onPageSizeChange\" />\n\t\t\t</template>\n\n\t\t\t<div v-if=\"error\" class=\"cn-logs-page__error\">\n\t\t\t\t<!-- @slot Replaces the error block shown when the fetch fails. -->\n\t\t\t\t<!-- @binding {*} error The recorded fetch error. -->\n\t\t\t\t<slot name=\"error\" :error=\"error\">\n\t\t\t\t\t<NcEmptyContent :name=\"errorText\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<AlertCircleOutline :size=\"64\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcEmptyContent>\n\t\t\t\t</slot>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- Row-detail dialog (opt-in via `rowDetail`). A log row's interesting\n\t\t payload is usually a nested object (a stack trace, an argument bag)\n\t\t that a table cell can only summarise. -->\n\t\t<NcDialog\n\t\t\tv-if=\"detailRow\"\n\t\t\t:open=\"true\"\n\t\t\t:name=\"detailTitle\"\n\t\t\tsize=\"large\"\n\t\t\t@update:open=\"closeDetail\">\n\t\t\t<!-- @slot Replaces the built-in row-detail dialog body. -->\n\t\t\t<!-- @binding {object} row The clicked log row. -->\n\t\t\t<slot name=\"row-detail\" :row=\"detailRow\">\n\t\t\t\t<div class=\"cn-logs-page__detail\">\n\t\t\t\t\t<CnDetailGrid\n\t\t\t\t\t\tv-if=\"detailScalarItems.length > 0\"\n\t\t\t\t\t\tlayout=\"horizontal\"\n\t\t\t\t\t\t:items=\"detailScalarItems\" />\n\t\t\t\t\t<template v-for=\"block in detailObjectBlocks\" :key=\"block.key\">\n\t\t\t\t\t\t<h4 class=\"cn-logs-page__detail-heading\">\n\t\t\t\t\t\t\t{{ block.label }}\n\t\t\t\t\t\t</h4>\n\t\t\t\t\t\t<CnDetailGrid\n\t\t\t\t\t\t\tv-if=\"block.items\"\n\t\t\t\t\t\t\tlayout=\"horizontal\"\n\t\t\t\t\t\t\t:items=\"block.items\" />\n\t\t\t\t\t\t<pre v-else class=\"cn-logs-page__detail-json\">{{ block.json }}</pre>\n\t\t\t\t\t</template>\n\t\t\t\t</div>\n\t\t\t</slot>\n\t\t\t<template #actions>\n\t\t\t\t<NcButton @click=\"closeDetail\">\n\t\t\t\t\t{{ closeLabel }}\n\t\t\t\t</NcButton>\n\t\t\t</template>\n\t\t</NcDialog>\n\t</div>\n</template>\n\n<script>\nimport axios from '@nextcloud/axios'\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcDialog, NcEmptyContent, NcLoadingIcon } from '@nextcloud/vue'\nimport { getCurrentInstance, inject } from 'vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport HistoryIcon from 'vue-material-design-icons/History.vue'\nimport { useListView } from '../../composables/index.js'\nimport { useObjectStore } from '../../store/index.js'\nimport { prefixUrl } from '../../utils/headers.js'\nimport { openRowTarget } from '../../utils/linkNavigation.js'\nimport { multiKeySort } from '../../utils/multiKeySort.js'\nimport { parseSortKeysFromQuery, resolveFilterMap, resolveQueryFilters } from '../../utils/routeFilters.js'\nimport { markNewTabHandled } from '../../utils/rowAuxClick.js'\nimport { CnDataTable } from '../CnDataTable/index.js'\nimport { CnDetailGrid } from '../CnDetailGrid/index.js'\nimport { CnPageHeader } from '../CnPageHeader/index.js'\nimport { CnPagination } from '../CnPagination/index.js'\n\n/**\n * Legacy default columns. Retained ONLY for `source` mode and for a store\n * mode whose schema could not be loaded — see `resolvedColumns`. They match\n * no OpenRegister log schema, so a store-backed page derives its columns\n * from the schema instead.\n *\n * A function, not a constant: `t()` at module scope would resolve before the\n * consuming app calls `registerTranslations()`, pinning the labels to English.\n *\n * @return {Array<{key: string, label: string, sortable?: boolean}>} The legacy column set.\n */\nfunction legacyDefaultColumns() {\n\treturn [\n\t\t{ key: 'timestamp', label: t('nextcloud-vue', 'Timestamp'), sortable: true },\n\t\t{ key: 'actor', label: t('nextcloud-vue', 'Actor'), sortable: true },\n\t\t{ key: 'action', label: t('nextcloud-vue', 'Action'), sortable: true },\n\t\t{ key: 'target', label: t('nextcloud-vue', 'Target') },\n\t\t{ key: 'details', label: t('nextcloud-vue', 'Details') },\n\t]\n}\n\n/**\n * CnLogsPage — Read-only audit-trail / activity-log page.\n *\n * Renders a CnDataTable of log entries with server-side sorting,\n * pagination and filtering. Columns come from `columns` (manifest:\n * `pages[].config.columns`); when none are given, a store-backed page\n * derives them from the schema.\n *\n * Two data-fetch modes:\n * - `register` + `schema`: fetched via `useObjectStore` as a regular\n * OpenRegister collection. The store registers the type as\n * `${register}-${schema}` per existing convention. List state is\n * owned by `useListView`, so `_limit` / `_page` / `_order` and any\n * `$route.query` / `filter` entries are sent with every request.\n * - `source`: a custom URL fetched via `axios.get(source)`. The\n * response shape may be either `{ results: [...] }` or a bare\n * array; the component handles both. This escape hatch supports\n * apps whose log surface is not OR-backed (e.g. a flat file).\n * Sorting is applied client-side; there is no pagination.\n *\n * Filtering (store mode) merges two sources, `filter` winning a collision:\n * 1. `$route.query` — every non-`_`-prefixed entry becomes a filter, so\n * `/jobs/logs?jobId=<uuid>` lands the page scoped to one job.\n * 2. `filter` — the manifest's own scoping, with `@route.<param>` /\n * `:<param>` / `@me` / `@today±Nd` tokens resolved at fetch time.\n *\n * The merged result is watched, so anything that moves it — a same-path query\n * change, a route param, the `filter` prop, or the workspace / app-config bag a\n * token in it reads — refetches from page 1, and nothing else does.\n *\n * Slots:\n * - `#header` — Replaces the default CnPageHeader.\n * - `#actions` — Renders a right-aligned action area (refresh,\n * export, etc.). The CnPageRenderer wires this from\n * `pages[].actionsComponent` when present.\n * - `#empty` — Replaces the empty-state block.\n * - `#error` — Replaces the error block. Scope: `{ error }`.\n * - `#row-actions` — Per-row action menu in the table. Scope:\n * `{ row }`.\n * - `#row-detail` — Replaces the row-detail dialog body. Scope: `{ row }`.\n * - `#column-<key>` — Per-column custom cell renderer. Scope:\n * `{ row, value }`.\n *\n * Props are intentionally permissive — every default has a sensible\n * fallback, so a manifest entry as small as\n * `{ type: \"logs\", config: { source: \"/api/logs\" } }` renders.\n */\nexport default {\n\tname: 'CnLogsPage',\n\n\tcomponents: {\n\t\tNcButton,\n\t\tNcDialog,\n\t\tNcEmptyContent,\n\t\tNcLoadingIcon,\n\t\tHistoryIcon,\n\t\tAlertCircleOutline,\n\t\tCnDataTable,\n\t\tCnDetailGrid,\n\t\tCnPageHeader,\n\t\tCnPagination,\n\t},\n\n\tprops: {\n\t\t/** Page title. Defaults to \"Activity log\" so a bare manifest entry still renders meaningfully. */\n\t\ttitle: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Activity log'),\n\t\t},\n\n\t\t/** Description shown under the title when `showTitle` is set. */\n\t\tdescription: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Whether to render the inline page header VISIBLY. Defaults to true:\n\t\t * unlike CnIndexPage (which surfaces the title in its sidebar header\n\t\t * when this is false), a logs page has no sidebar fallback, so a false\n\t\t * default left the page with no heading at all. Setting it to false is\n\t\t * now safe for accessibility either way — the `<h1>` is still rendered\n\t\t * visually-hidden, so the `<main>` landmark keeps its accessible\n\t\t * heading (WCAG 2.4.6 / 1.3.1).\n\t\t */\n\t\tshowTitle: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\n\t\t/** MDI icon name for the header. */\n\t\ticon: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * OpenRegister register slug. Required (with `schema`) for store-backed\n\t\t * mode. Changing it after mount requires a remount — CnPageRenderer keys\n\t\t * the dispatched page on register+schema, so a manifest page swap already\n\t\t * remounts. A direct consumer that rebinds it instead gets a development\n\t\t * console warning, since the bound data source does not follow.\n\t\t */\n\t\tregister: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * OpenRegister schema slug. Required (with `register`) for store-backed\n\t\t * mode. Changing it after mount requires a remount (see `register`).\n\t\t */\n\t\tschema: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Custom log source URL — used when `register`+`schema` is not set. */\n\t\tsource: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Column definitions for the table. When omitted, a store-backed page\n\t\t * derives its columns from the loaded schema; a `source`-backed page\n\t\t * (or one whose schema failed to load) falls back to\n\t\t * `[timestamp, actor, action, target, details]`.\n\t\t * Pass either an array of strings (treated as keys + auto-labels)\n\t\t * OR an array of `{ key, label, sortable, width }` objects.\n\t\t *\n\t\t * @type {Array<string|{key: string, label: string}>}\n\t\t */\n\t\tcolumns: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * Fixed filter map merged into every fetch, ABOVE the `$route.query`\n\t\t * deep-link filters so the page's own scoping wins on a key collision.\n\t\t * Values support the shared token grammar: `@route.<param>` / `:<param>`\n\t\t * (route params), `@me`, `@today±Nd`, `@workspace.<key>`. Null = no fixed\n\t\t * filter. Store mode only. Reactive: changing the map — or changing what a\n\t\t * token in it resolves to — refetches from page 1.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tfilter: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Pagination config; only `limit` is read, as the page size sent as\n\t\t * `_limit`. Null = the store default of 20. Store mode only.\n\t\t *\n\t\t * @type {{limit?: number}|null}\n\t\t */\n\t\tpagination: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Initial sort column. Null (the default) sends no `_order`, leaving the\n\t\t * server's own ordering in place.\n\t\t */\n\t\tsortKey: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Initial sort direction. Inert while `sortKey` is null. */\n\t\tsortOrder: {\n\t\t\ttype: String,\n\t\t\tdefault: 'asc',\n\t\t\tvalidator: (v) => v === null || ['asc', 'desc'].includes(v),\n\t\t},\n\n\t\t/**\n\t\t * Initial MULTI-column sort as an ordered priority list. Takes precedence\n\t\t * over `sortKey`/`sortOrder` when non-empty; empty (the default) is inert.\n\t\t * A `?_order=` param on the URL (the JSON-encoded list CnIndexPage\n\t\t * persists when a header is clicked) outranks both, so a shared or\n\t\t * reloaded logs link opens on the sort it carried.\n\t\t *\n\t\t * @type {Array<{key: string, order: 'asc'|'desc'}>}\n\t\t */\n\t\tsortKeys: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * Make the columns' declared `width` authoritative (`table-layout: fixed`)\n\t\t * instead of a hint the browser may override from cell content. Worth\n\t\t * setting on a log table: message text and long unbreakable values like a\n\t\t * PHP FQCN otherwise dictate the column widths between them. Long values\n\t\t * then wrap inside their cell rather than overflowing it. Default false\n\t\t * keeps the previous auto layout.\n\t\t */\n\t\tfixedLayout: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Open a read-only detail dialog when a row is clicked, rendering the\n\t\t * entry's fields — including nested bags like a stack trace or an\n\t\t * argument map that a table cell can only summarise. Default false\n\t\t * keeps a row click inert, as before. Ignored when `rowRoute` is set:\n\t\t * a purpose-built page beats a generic dialog.\n\t\t */\n\t\trowDetail: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Manifest page id (route name) to open on a row click, pushed as\n\t\t * `{ name: rowRoute, params: { id: row[rowKey] } }` — the same shape\n\t\t * CnIndexPage's `open-page` actions use. For a log whose detail surface\n\t\t * is a real page (a step timeline, a replay action) rather than a\n\t\t * read-only dump. Takes precedence over `rowDetail`.\n\t\t */\n\t\trowRoute: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Row identifier property. Defaults to `id` (matches OR + most custom log shapes). */\n\t\trowKey: {\n\t\t\ttype: String,\n\t\t\tdefault: 'id',\n\t\t},\n\n\t\t/** Text shown when there are no log entries. */\n\t\temptyText: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'No log entries to show'),\n\t\t},\n\n\t\t/** Text shown when the fetch fails. */\n\t\terrorText: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Could not load log entries'),\n\t\t},\n\n\t\t/** Label for the row-detail dialog's close button. */\n\t\tcloseLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Close'),\n\t\t},\n\n\t\t/**\n\t\t * Override the object store. Useful when the consuming app calls\n\t\t * `createObjectStore` with a custom ID. When null, the default\n\t\t * `useObjectStore()` is used. The component skips store wiring\n\t\t * entirely when in `source` mode.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tstore: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: [\n\t\t/**\n\t\t * @event action Declared for hosts that dispatch log-row actions through\n\t\t * the `#row-actions` slot. Never emitted by this component itself; kept\n\t\t * in the surface because consumers may already listen for it.\n\t\t */\n\t\t'action',\n\t\t/**\n\t\t * @event row-click Emitted when a log row's body is clicked or\n\t\t * middle-clicked, whether or not `rowDetail` opened the detail dialog —\n\t\t * so a host can navigate instead of (or as well as) showing the dialog.\n\t\t * Payload: `(row, event)`, the second being the native click/auxclick\n\t\t * event.\n\t\t * @type {object}\n\t\t */\n\t\t'row-click',\n\t],\n\n\tsetup(props) {\n\t\t// Always returned, even when the composable is not used: Vue warns when\n\t\t// a computed reads a key setup() omitted, and `source` mode reads these.\n\t\t// `resolveFixedFilters` is included for the same reason — the\n\t\t// `filterSignature` computed calls it by name, and `source` mode (which\n\t\t// has no fixed filters at all) takes this early return.\n\t\tconst NO_LIST = {\n\t\t\tlist: null,\n\t\t\tobjectType: '',\n\t\t\tlistStore: null,\n\t\t\tresolveFixedFilters: () => ({}),\n\t\t}\n\t\tif (!props.register || !props.schema) {\n\t\t\treturn NO_LIST\n\t\t}\n\n\t\tconst store = props.store || useObjectStore()\n\t\t// The `store` prop has always accepted a hand-rolled object, but\n\t\t// useListView assumes a full OpenRegister store (pagination/errors/\n\t\t// fetchSchema). Duck-type it and fall back to the legacy no-params\n\t\t// fetch rather than throwing on a partial store.\n\t\tif (typeof store.fetchCollection !== 'function' || !store.pagination) {\n\t\t\treturn NO_LIST\n\t\t}\n\n\t\tconst objectType = `${props.register}-${props.schema}`\n\t\t// Must run synchronously: useListView's onMounted calls fetchSchema(),\n\t\t// which throws when the type is not registered yet. Positional\n\t\t// signature: (slug, schemaId, registerId).\n\t\tif (typeof store.registerObjectType === 'function') {\n\t\t\tstore.registerObjectType(objectType, props.schema, props.register)\n\t\t}\n\n\t\tconst instance = getCurrentInstance()\n\n\t\t// Token-resolution context for the `filter` prop's `@workspace.<key>` /\n\t\t// `@config.<key>` / `@object.<field>` tokens. Same injects + unwrap shape\n\t\t// as useSelfFetchList's `tokenCtx`. Without it `resolveFilterMap` was\n\t\t// called with no ctx, and `resolveFilterValue` returns an unresolvable\n\t\t// token VERBATIM — so a page declaring `filter: { client:\n\t\t// '@workspace.selectedClient' }` sent the literal string\n\t\t// \"@workspace.selectedClient\" to OpenRegister as a property filter and got\n\t\t// an empty table with no error, despite the prop doc naming that token.\n\t\t// All three default to absent, so an app that never provides them is\n\t\t// unaffected.\n\t\tconst objectCtxRaw = inject('cnObjectContext', null)\n\t\tconst workspaceCtxRaw = inject('cnWorkspaceContext', null)\n\t\tconst appConfigRaw = inject('cnAppConfig', null)\n\t\tconst unwrapCtx = (v) => ((v && typeof v === 'object' && 'value' in v) ? v.value : v)\n\t\tconst tokenCtx = () => {\n\t\t\tconst objCtx = unwrapCtx(objectCtxRaw)\n\t\t\tconst base = (objCtx && typeof objCtx === 'object') ? { ...objCtx } : {}\n\t\t\tbase.workspace = unwrapCtx(workspaceCtxRaw) || {}\n\t\t\tbase.config = unwrapCtx(appConfigRaw) || {}\n\t\t\treturn base\n\t\t}\n\t\t// The complete fetch scope: the deep-link query filters plus the `filter`\n\t\t// prop's route/`@`-token resolution. A getter, not a computed — it is\n\t\t// re-read on every fetch, so a `?jobId=` change re-scopes the list\n\t\t// without re-creating it, and `tokenCtx()` reflects the latest bags.\n\t\tconst resolveFixedFilters = () => {\n\t\t\tconst route = instance && instance.proxy && instance.proxy.$route\n\t\t\tconst ctx = tokenCtx()\n\t\t\treturn {\n\t\t\t\t...resolveQueryFilters(route && route.query, ctx),\n\t\t\t\t...resolveFilterMap(props.filter, (route && route.params) || {}, ctx),\n\t\t\t}\n\t\t}\n\n\t\tconst list = useListView(objectType, {\n\t\t\tobjectStore: store,\n\t\t\tdefaultPageSize: (props.pagination && props.pagination.limit) || undefined,\n\t\t\t// A `?_order=` deep link wins over the configured default, so a\n\t\t\t// reloaded or shared logs URL reproduces the sort the link carried —\n\t\t\t// the same precedence CnIndexPage's useSelfFetchList applies.\n\t\t\tdefaultSort: props.sortKey ? { key: props.sortKey, order: props.sortOrder || 'asc' } : undefined,\n\t\t\tdefaultSortKeys: parseSortKeysFromQuery(instance && instance.proxy && instance.proxy.$route)\n\t\t\t\t|| (props.sortKeys.length > 0 ? props.sortKeys : undefined),\n\t\t\tfixedFilters: resolveFixedFilters,\n\t\t})\n\n\t\treturn { list, objectType, listStore: store, resolveFixedFilters }\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tlocalRows: [],\n\t\t\tlocalLoading: false,\n\t\t\tlocalError: null,\n\t\t\tlocalSortKeys: [],\n\t\t\tdetailRow: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** Whether the component fetches via the object store + useListView. */\n\t\tusesStore() {\n\t\t\treturn !!this.list\n\t\t},\n\n\t\t/**\n\t\t * Whether the component should fetch via axios. Store mode needs BOTH\n\t\t * `register` and `schema`, so a half-configured pair with a `source` set\n\t\t * still falls back to the URL rather than the empty state.\n\t\t */\n\t\tusesSource() {\n\t\t\treturn !(this.register && this.schema) && !!this.source\n\t\t},\n\n\t\t/** Loading state of the active fetch mode. */\n\t\tloading() {\n\t\t\t// `list` is a bag of refs returned from setup(); only TOP-LEVEL setup\n\t\t\t// refs are auto-unwrapped, so every read below goes through `.value`.\n\t\t\treturn this.list ? this.list.loading.value : this.localLoading\n\t\t},\n\n\t\t/**\n\t\t * Fetch error. `fetchCollection` records failures on the store rather\n\t\t * than throwing, so store mode reads them from there — without this the\n\t\t * `#error` slot never rendered for a store-backed page.\n\t\t */\n\t\terror() {\n\t\t\tif (this.localError) {\n\t\t\t\treturn this.localError\n\t\t\t}\n\t\t\tif (this.list && this.listStore) {\n\t\t\t\treturn this.listStore.errors[this.objectType] || null\n\t\t\t}\n\t\t\treturn null\n\t\t},\n\n\t\t/** Rows to render: the store collection, or locally sorted axios rows. */\n\t\trows() {\n\t\t\tif (this.list) {\n\t\t\t\treturn this.list.objects.value\n\t\t\t}\n\t\t\t// Legacy-store fallback: read the store's collection LIVE rather than\n\t\t\t// the snapshot `fetch()` took. `rows` used to reach the collection\n\t\t\t// through a computed; snapshotting into `localRows` meant a store that\n\t\t\t// REPLACES the array (rather than mutating it in place) no longer\n\t\t\t// reached the table. Only `this.store` is consulted — in this path it is\n\t\t\t// the partial store prop that made setup() bail — so `source` mode,\n\t\t\t// which has no store, still falls through to localRows.\n\t\t\tconst live = (this.register && this.schema && this.store)\n\t\t\t\t? this.store.collections?.[`${this.register}-${this.schema}`]\n\t\t\t\t: null\n\t\t\tconst base = Array.isArray(live) ? live : this.localRows\n\t\t\tif (this.localSortKeys.length === 0) {\n\t\t\t\treturn base\n\t\t\t}\n\t\t\t// CnDataTable is presentational — it never sorts its own rows — so\n\t\t\t// `source` mode has to apply the header sort itself.\n\t\t\treturn multiKeySort(base, this.localSortKeys.map((k) => ({ field: k.key, order: k.order })))\n\t\t},\n\n\t\t/**\n\t\t * Server pagination STATE (`{ total, page, pages, limit }`); null in\n\t\t * source mode, which has no paging. Distinct from the `pagination`\n\t\t * prop, which is the manifest's page-size config.\n\t\t */\n\t\tpaginationState() {\n\t\t\treturn this.list ? this.list.pagination.value : null\n\t\t},\n\n\t\t/**\n\t\t * The LOADED schema object (not the `schema` slug prop), forwarded to\n\t\t * CnDataTable for type-aware cell rendering and schema-derived columns.\n\t\t */\n\t\ttableSchema() {\n\t\t\treturn (this.list && this.list.schema.value) || null\n\t\t},\n\n\t\t/** Active ordered sort keys, from the composable or local source state. */\n\t\teffectiveSortKeys() {\n\t\t\treturn this.list ? this.list.sortKeys.value : this.localSortKeys\n\t\t},\n\n\t\t/** Primary sort key (mirrors `effectiveSortKeys[0]`) for CnDataTable. */\n\t\teffectiveSortKey() {\n\t\t\treturn this.effectiveSortKeys[0]?.key ?? null\n\t\t},\n\n\t\t/** Primary sort direction (mirrors `effectiveSortKeys[0]`). */\n\t\teffectiveSortOrder() {\n\t\t\treturn this.effectiveSortKeys[0]?.order ?? 'asc'\n\t\t},\n\n\t\t/**\n\t\t * Resolved columns. A consumer-provided list wins. Otherwise, when a\n\t\t * schema is loaded, return `[]` so CnDataTable derives the columns from\n\t\t * it — the legacy `timestamp/actor/action/target/details` default matched\n\t\t * no OpenRegister log schema and rendered five blank cells. `source` mode\n\t\t * and a failed schema load still get that legacy default.\n\t\t */\n\t\tresolvedColumns() {\n\t\t\tif (this.columns.length > 0) {\n\t\t\t\treturn this.columns.map((c) => (typeof c === 'string' ? { key: c, label: this.humanise(c) } : c))\n\t\t\t}\n\t\t\tif (this.tableSchema) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn legacyDefaultColumns()\n\t\t},\n\n\t\t/** Column slot names that the parent has provided (for pass-through). */\n\t\tslotColumns() {\n\t\t\treturn Object.keys(this.$slots || {})\n\t\t\t\t.filter((name) => name.startsWith('column-'))\n\t\t\t\t.map((name) => name.replace('column-', ''))\n\t\t},\n\n\t\t/** Dialog heading: the entry's message when it has one, else its id. */\n\t\tdetailTitle() {\n\t\t\tif (!this.detailRow) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst message = this.detailRow.message\n\t\t\tif (typeof message === 'string' && message !== '') {\n\t\t\t\treturn message\n\t\t\t}\n\t\t\tconst id = this.detailRow[this.rowKey]\n\t\t\treturn id ? String(id) : t('nextcloud-vue', 'Log entry')\n\t\t},\n\n\t\t/** The clicked row's primitive fields, as CnDetailGrid items. */\n\t\tdetailScalarItems() {\n\t\t\treturn this.detailEntries\n\t\t\t\t.filter(([, value]) => !this.isBag(value))\n\t\t\t\t.map(([key, value]) => ({ label: this.propertyLabel(key), value: String(value) }))\n\t\t},\n\n\t\t/**\n\t\t * The clicked row's nested fields. A bag of primitives (a stack trace's\n\t\t * frames, an argument map) renders as its own labelled grid; anything\n\t\t * deeper falls back to pretty-printed JSON.\n\t\t */\n\t\tdetailObjectBlocks() {\n\t\t\treturn this.detailEntries\n\t\t\t\t.filter(([, value]) => this.isBag(value))\n\t\t\t\t.map(([key, value]) => {\n\t\t\t\t\tconst entries = Object.entries(value)\n\t\t\t\t\tconst flat = entries.every(([, v]) => v === null || typeof v !== 'object')\n\t\t\t\t\treturn {\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tlabel: this.propertyLabel(key),\n\t\t\t\t\t\titems: flat ? entries.map(([k, v]) => ({ label: this.humanise(k), value: String(v) })) : null,\n\t\t\t\t\t\tjson: flat ? null : JSON.stringify(value, null, 2),\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Stable signature of the RESOLVED fetch scope — the query deep-link\n\t\t * filters plus the `filter` prop after route-param and `@`-token\n\t\t * resolution. Reading through the same getter the fetch uses is what\n\t\t * makes it exact: it tracks `$route`, `props.filter` and the injected\n\t\t * workspace / app-config bags, and it changes only when the request the\n\t\t * list would issue actually changes. Stringified so the watcher fires on\n\t\t * content, not identity. Empty string in `source` mode (no list).\n\t\t *\n\t\t * @return {string} The serialized filter map.\n\t\t */\n\t\tfilterSignature() {\n\t\t\tif (!this.list) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn JSON.stringify(this.resolveFixedFilters())\n\t\t},\n\n\t\t/** The clicked row's renderable entries — `@self` and empties dropped. */\n\t\tdetailEntries() {\n\t\t\tif (!this.detailRow) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn Object.entries(this.detailRow)\n\t\t\t\t.filter(([key, value]) => key !== '@self' && value !== null && value !== undefined && value !== '')\n\t\t},\n\t},\n\n\twatch: {\n\t\t// ONE watcher over the whole fetch scope, rather than one per reactive\n\t\t// source that feeds it. `fixedFilters` is a plain getter re-read at fetch\n\t\t// time, so a refresh is all any of them needs — but watching them\n\t\t// individually got three things wrong:\n\t\t//\n\t\t// - `$route.query` and `$route.params` both fire on a single navigation,\n\t\t// so one route change issued TWO identical concurrent fetches, which\n\t\t// `fetchCollection` does not dedup and whose responses can land in\n\t\t// either order;\n\t\t// - they fired on navigation AWAY as well, because the route updates\n\t\t// before this component tears down. The refetch was then scoped by the\n\t\t// DESTINATION route's query (`/cases?status=open` → `GET\n\t\t// …/job_log?status=open`), overwriting the shared store collection that\n\t\t// any other mounted widget on the same objectType reads;\n\t\t// - a workspace / app-config bag mutation refetched even when no\n\t\t// `@workspace.`/`@config.` token was in play, so a volatile bag reset a\n\t\t// token-free page to page 1 on every write.\n\t\t//\n\t\t// The signature is the RESOLVED filter map, so it changes only when the\n\t\t// scope actually changes, and the route-name guard drops the fire that\n\t\t// belongs to the page being navigated to. No `immediate` — useListView\n\t\t// owns the initial fetch. This also covers a reactive `filter` prop\n\t\t// change, which had no watcher of its own at all.\n\t\tfilterSignature(next, prev) {\n\t\t\tif (!this.list || next === prev) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// Guarded only when this page HAS a route name to compare against —\n\t\t\t// a router-less host, or one whose routes are unnamed, keeps the\n\t\t\t// unguarded behaviour rather than losing refetches to a comparison\n\t\t\t// that can never match.\n\t\t\tif (this._ownRouteName !== undefined && this.$route?.name !== this._ownRouteName) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.list.refresh(1)\n\t\t},\n\n\t\tregister(value, previous) {\n\t\t\tthis.onSourcePropChange('register', value, previous)\n\t\t},\n\n\t\tschema(value, previous) {\n\t\t\tthis.onSourcePropChange('schema', value, previous)\n\t\t},\n\n\t\tsource(value, previous) {\n\t\t\tthis.onSourcePropChange('source', value, previous)\n\t\t},\n\t},\n\n\tcreated() {\n\t\t// The route this page belongs to, captured BEFORE any navigation can\n\t\t// change it — the `filterSignature` watcher's guard. CnPageRenderer mounts\n\t\t// a page when `$route.name === page.id`, so this is the page's own id.\n\t\tthis._ownRouteName = this.$route ? this.$route.name : undefined\n\t},\n\n\tmounted() {\n\t\t// In store mode useListView's own onMounted owns the initial fetch;\n\t\t// calling fetch() here too would double-request on every page load.\n\t\tif (!this.list) {\n\t\t\tthis.fetch()\n\t\t}\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Capitalise + space a snake_case / camelCase key for a default column label.\n\t\t *\n\t\t * @param {string} key A log-entry property name, e.g. `'created_at'` or `'userAgent'`.\n\t\t * @return {string} The sentence-cased label, e.g. `'Created at'` / `'User agent'`.\n\t\t */\n\t\thumanise(key) {\n\t\t\tconst spaced = String(key).replace(/([A-Z])/g, ' $1').replace(/_/g, ' ').toLowerCase()\n\t\t\treturn spaced.charAt(0).toUpperCase() + spaced.slice(1)\n\t\t},\n\n\t\t/**\n\t\t * Label for a row property — the schema's own title when one is loaded.\n\t\t *\n\t\t * @param {string} key The property name.\n\t\t * @return {string} The display label.\n\t\t */\n\t\tpropertyLabel(key) {\n\t\t\tconst title = this.tableSchema?.properties?.[key]?.title\n\t\t\treturn (typeof title === 'string' && title !== '') ? title : this.humanise(key)\n\t\t},\n\n\t\t/**\n\t\t * Whether a value is a nested bag (object/array) rather than a scalar.\n\t\t *\n\t\t * @param {unknown} value The value to test.\n\t\t * @return {boolean} True for non-null objects and arrays.\n\t\t */\n\t\tisBag(value) {\n\t\t\treturn value !== null && typeof value === 'object'\n\t\t},\n\n\t\t/**\n\t\t * Fetch log entries from the resolved data source.\n\t\t *\n\t\t * Only used in `source` mode and in the legacy store fallback — the\n\t\t * composable owns fetching whenever it is active.\n\t\t */\n\t\tasync fetch() {\n\t\t\tif (this.register && this.schema) {\n\t\t\t\t// Legacy fallback: a partial `store` prop that useListView\n\t\t\t\t// cannot drive (see setup). Unfiltered + unpaginated, as before.\n\t\t\t\tconst store = this.store || useObjectStore()\n\t\t\t\tthis.localLoading = true\n\t\t\t\tthis.localError = null\n\t\t\t\ttry {\n\t\t\t\t\tif (typeof store.registerObjectType === 'function') {\n\t\t\t\t\t\tstore.registerObjectType(`${this.register}-${this.schema}`, this.schema, this.register)\n\t\t\t\t\t}\n\t\t\t\t\tif (typeof store.fetchCollection === 'function') {\n\t\t\t\t\t\tawait store.fetchCollection(`${this.register}-${this.schema}`)\n\t\t\t\t\t}\n\t\t\t\t\tthis.localRows = store.collections?.[`${this.register}-${this.schema}`] ?? []\n\t\t\t\t} catch (err) {\n\t\t\t\t\tthis.localError = err\n\t\t\t\t} finally {\n\t\t\t\t\tthis.localLoading = false\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (this.usesSource) {\n\t\t\t\tthis.localLoading = true\n\t\t\t\tthis.localError = null\n\t\t\t\ttry {\n\t\t\t\t\tconst response = await axios.get(prefixUrl(this.source))\n\t\t\t\t\tconst body = response?.data\n\t\t\t\t\tif (Array.isArray(body)) {\n\t\t\t\t\t\tthis.localRows = body\n\t\t\t\t\t} else if (body && Array.isArray(body.results)) {\n\t\t\t\t\t\tthis.localRows = body.results\n\t\t\t\t\t} else if (body && Array.isArray(body.entries)) {\n\t\t\t\t\t\tthis.localRows = body.entries\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.localRows = []\n\t\t\t\t\t}\n\t\t\t\t} catch (err) {\n\t\t\t\t\tthis.localError = err\n\t\t\t\t\tthis.localRows = []\n\t\t\t\t} finally {\n\t\t\t\t\tthis.localLoading = false\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// Misconfigured — surface a console warning so a developer notices.\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.warn('[CnLogsPage] Neither register+schema nor source configured; rendering empty state.')\n\t\t},\n\n\t\t/**\n\t\t * React to a data-source prop changing on a mounted page.\n\t\t *\n\t\t * Only the legacy / `source` paths can act on it, by refetching. Store\n\t\t * mode cannot: the object type, its store registration and the\n\t\t * `useListView` instance are all built once in `setup()` from the\n\t\t * initial pair, so a new `register`/`schema` needs a remount to take\n\t\t * effect (which CnPageRenderer does on a manifest page swap). That is\n\t\t * documented on the props, but it used to happen in complete silence —\n\t\t * a direct consumer rebinding the prop saw the old collection and no\n\t\t * indication why. Warns in development only, once per change.\n\t\t *\n\t\t * @param {string} name The prop that changed.\n\t\t * @param {unknown} value The new value.\n\t\t * @param {unknown} previous The previous value.\n\t\t * @return {void}\n\t\t */\n\t\tonSourcePropChange(name, value, previous) {\n\t\t\tif (!this.list) {\n\t\t\t\tthis.fetch()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (process.env.NODE_ENV !== 'production') {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn(`[CnLogsPage] \\`${name}\\` changed from \"${previous}\" to \"${value}\" on a mounted store-backed page. `\n\t\t\t\t\t+ 'The data source is bound once at setup, so this has no effect — remount the page (e.g. give it a '\n\t\t\t\t\t+ ':key) to pick up the new register/schema.')\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Apply a header sort. Server-side in store mode, client-side otherwise.\n\t\t *\n\t\t * @param {{key: string|null, order: string|null, keys?: Array<{key: string, order: string}>}} sort CnDataTable's sort payload.\n\t\t */\n\t\tonSort(sort) {\n\t\t\tif (this.list) {\n\t\t\t\tthis.list.onSort(sort)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.localSortKeys = Array.isArray(sort.keys)\n\t\t\t\t? sort.keys\n\t\t\t\t: (sort.key ? [{ key: sort.key, order: sort.order || 'asc' }] : [])\n\t\t},\n\n\t\t/**\n\t\t * Navigate to a page of results.\n\t\t *\n\t\t * @param {number} page The 1-based page number.\n\t\t */\n\t\tonPageChange(page) {\n\t\t\tthis.list?.onPageChange(page)\n\t\t},\n\n\t\t/**\n\t\t * Change the page size, returning to page 1.\n\t\t *\n\t\t * @param {number} size The new page size.\n\t\t */\n\t\tonPageSizeChange(size) {\n\t\t\tthis.list?.onPageSizeChange(size)\n\t\t},\n\n\t\t/**\n\t\t * Handle a row-body click: navigate to `rowRoute` when one is declared,\n\t\t * else open the detail dialog when `rowDetail` is set. Always re-emits\n\t\t * so a host can do its own thing regardless. A ctrl/cmd/shift or middle\n\t\t * click opens `rowRoute` in a new tab; a middle click never opens the\n\t\t * dialog.\n\t\t *\n\t\t * @param {object} row The clicked log entry.\n\t\t * @param {MouseEvent} [event] The originating click/auxclick event.\n\t\t */\n\t\tonRowClick(row, event) {\n\t\t\tif (this.rowRoute) {\n\t\t\t\tmarkNewTabHandled(event, openRowTarget(event, { name: this.rowRoute, params: { id: row?.[this.rowKey] } }, this.$router))\n\t\t\t} else if (this.rowDetail && !(event && event.button === 1)) {\n\t\t\t\tthis.detailRow = row\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event row-click Emitted when a log row's body is clicked or middle-clicked. Payload: `(row, event)` — the log entry and the native click/auxclick event.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('row-click', row, event)\n\t\t},\n\n\t\t/** Close the row-detail dialog. */\n\t\tcloseDetail() {\n\t\t\tthis.detailRow = null\n\t\t},\n\n\t\t/**\n\t\t * Re-fetch from the source. Exposed so refresh buttons in\n\t\t * actionsComponent can call `$parent.refresh()`.\n\t\t *\n\t\t * @public\n\t\t */\n\t\trefresh() {\n\t\t\tif (this.list) {\n\t\t\t\tthis.list.refresh(1)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.fetch()\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n/* Padding matches CnIndexPage / CnDetailPage (see css/index-page.css and\n css/detail-page.css) — a logs page had none at all, so its table ran flush\n into both edges of the app content area. Gaps are expressed in the same\n grid-baseline unit as the rest of the fleet rather than raw pixels. */\n.cn-logs-page {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: calc(4 * var(--default-grid-baseline));\n\tpadding: calc(5 * var(--default-grid-baseline));\n\tbox-sizing: border-box;\n}\n\n.cn-logs-page__actions {\n\tdisplay: flex;\n\tjustify-content: flex-end;\n\tgap: calc(2 * var(--default-grid-baseline));\n}\n\n.cn-logs-page__loading,\n.cn-logs-page__empty,\n.cn-logs-page__error {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tmin-height: 200px;\n}\n\n.cn-logs-page__pagination {\n\tmargin-top: calc(3 * var(--default-grid-baseline));\n}\n\n.cn-logs-page__detail {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: calc(2 * var(--default-grid-baseline));\n\tpadding: 0 calc(3 * var(--default-grid-baseline)) calc(3 * var(--default-grid-baseline));\n}\n\n.cn-logs-page__detail-heading {\n\tmargin: calc(3 * var(--default-grid-baseline)) 0 0;\n\tfont-weight: bold;\n}\n\n.cn-logs-page__detail-json {\n\tmargin: 0;\n\tpadding: calc(2 * var(--default-grid-baseline));\n\toverflow-x: auto;\n\tborder-radius: var(--border-radius);\n\tbackground-color: var(--color-background-dark);\n\tfont-family: monospace;\n\twhite-space: pre;\n}\n\n/* Same breakpoint + step-down as CnDetailPage, so the three page types stay in\n sync on narrow viewports. */\n@media (max-width: 768px) {\n\t.cn-logs-page {\n\t\tpadding: calc(3 * var(--default-grid-baseline));\n\t}\n}\n</style>\n"],"names":["t","HistoryIcon","CnDataTable","CnDetailGrid","CnPageHeader","CnPagination","axios"],"mappings":"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"}
1
+ {"version":3,"file":"CnLogsPage.vue2.js","sources":["../../../../src/components/CnLogsPage/CnLogsPage.vue"],"sourcesContent":["<!--\n CnLogsPage — Read-only audit-trail / activity-log surface.\n\n Wraps CnDataTable with server-side sorting, pagination and filtering.\n Reads its data source from `config`:\n - { register, schema } → fetched via the OpenRegister object store\n - { source: '/api/url' } → fetched via axios.get(source)\n\n In store mode the list state (params, paging, sort, filters) is owned by\n the shared `useListView` composable, so a `?jobId=<uuid>`-style deep link\n lands the page pre-filtered — the same behaviour CnIndexPage has.\n\n Mounted by CnPageRenderer when a manifest page declares\n `type: \"logs\"`. Honours `headerComponent`, `actionsComponent`, and\n the generic `slots` map from `pages[]` (forwarded by the renderer\n through scoped slots; this component just exposes `#header`,\n `#actions`, and `#row-actions` slots which the renderer fills).\n\n Backwards-compat: when neither `register+schema` nor `source` is\n set, the component renders an empty-state with a console warning\n rather than throwing — this keeps a misconfigured manifest from\n breaking the app shell.\n-->\n<template>\n\t<div class=\"cn-logs-page\" data-testid=\"cn-logs-page\">\n\t\t<!-- @slot Replaces the default CnPageHeader. CnPageRenderer fills it from `pages[].headerComponent` / `slots.header`. -->\n\t\t<!-- @binding {string} title The page title. -->\n\t\t<!-- @binding {string} description The page description. -->\n\t\t<!-- @binding {string} icon The header's MDI icon name. -->\n\t\t<slot\n\t\t\tname=\"header\"\n\t\t\t:title=\"title\"\n\t\t\t:description=\"description\"\n\t\t\t:icon=\"icon\">\n\t\t\t<CnPageHeader\n\t\t\t\tv-if=\"title\"\n\t\t\t\t:title=\"title\"\n\t\t\t\t:description=\"description\"\n\t\t\t\t:icon=\"icon\"\n\t\t\t\t:visuallyHidden=\"!showTitle\" />\n\t\t</slot>\n\n\t\t<div v-if=\"$slots.actions\" class=\"cn-logs-page__actions\">\n\t\t\t<!-- @slot Right-aligned action area (refresh, export, …). CnPageRenderer fills it from `pages[].actionsComponent`. -->\n\t\t\t<slot name=\"actions\" />\n\t\t</div>\n\n\t\t<!-- Body -->\n\t\t<div class=\"cn-logs-page__body\">\n\t\t\t<!-- Loading state -->\n\t\t\t<div v-if=\"loading\" class=\"cn-logs-page__loading\">\n\t\t\t\t<NcLoadingIcon :size=\"32\" />\n\t\t\t</div>\n\n\t\t\t<!-- The error block below is a SIBLING of this chain, not part of it, so\n\t\t\t BOTH guards here are load-bearing and neither is sufficient alone.\n\t\t\t A failed fetch leaves the collection empty, so:\n\t\t\t - without `&& !error`, this branch renders \"no log entries\"\n\t\t\t stacked above \"could not load log entries\";\n\t\t\t - without the `rows.length > 0` guard on the table branch, the\n\t\t\t `v-else` catches that same state instead and mounts CnDataTable\n\t\t\t with zero rows, which renders its OWN empty row from the same\n\t\t\t `emptyText` — the identical contradiction, just inside a table.\n\t\t\t So: error + no rows shows the error alone; no error + no rows shows\n\t\t\t this block; rows present shows the table, with the error beneath it\n\t\t\t when a refresh failed over a still-populated collection. -->\n\t\t\t<div v-else-if=\"rows.length === 0 && !error\" class=\"cn-logs-page__empty\">\n\t\t\t\t<!-- @slot Replaces the empty-state block shown when there are no log entries. -->\n\t\t\t\t<slot name=\"empty\">\n\t\t\t\t\t<NcEmptyContent :name=\"emptyText\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<HistoryIcon :size=\"64\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcEmptyContent>\n\t\t\t\t</slot>\n\t\t\t</div>\n\n\t\t\t<!-- Table. `v-else-if=\"rows.length > 0\"` rather than a bare `v-else` —\n\t\t\t see the guard note above. -->\n\t\t\t<template v-else-if=\"rows.length > 0\">\n\t\t\t\t<CnDataTable\n\t\t\t\t\t:schema=\"tableSchema\"\n\t\t\t\t\t:columns=\"resolvedColumns\"\n\t\t\t\t\t:rows=\"rows\"\n\t\t\t\t\t:rowKey=\"rowKey\"\n\t\t\t\t\t:fixedLayout=\"fixedLayout\"\n\t\t\t\t\t:sortKey=\"effectiveSortKey\"\n\t\t\t\t\t:sortOrder=\"effectiveSortOrder\"\n\t\t\t\t\t:sortKeys=\"effectiveSortKeys\"\n\t\t\t\t\t:emptyText=\"emptyText\"\n\t\t\t\t\t@sort=\"onSort\"\n\t\t\t\t\t@rowClick=\"onRowClick\"\n\t\t\t\t\t@rowAuxClick=\"onRowAuxClick\">\n\t\t\t\t\t<template\n\t\t\t\t\t\tv-for=\"col in slotColumns\"\n\t\t\t\t\t\t#[`column-${col}`]=\"{ row, value }\">\n\t\t\t\t\t\t<!-- @slot Per-column cell renderer (`#column-<key>`), one per configured column key. -->\n\t\t\t\t\t\t<!-- @binding {object} row The log entry being rendered. -->\n\t\t\t\t\t\t<!-- @binding {*} value The cell's resolved value. -->\n\t\t\t\t\t\t<slot :name=\"'column-' + col\" :row=\"row\" :value=\"value\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t<template v-if=\"$slots['row-actions']\" #row-actions=\"{ row }\">\n\t\t\t\t\t\t<!-- @slot Per-row action menu cell. Supplying it adds the trailing actions column. -->\n\t\t\t\t\t\t<!-- @binding {object} row The log entry for this row. -->\n\t\t\t\t\t\t<slot name=\"row-actions\" :row=\"row\" />\n\t\t\t\t\t</template>\n\t\t\t\t</CnDataTable>\n\n\t\t\t\t<CnPagination\n\t\t\t\t\tv-if=\"paginationState && paginationState.pages > 1\"\n\t\t\t\t\tclass=\"cn-logs-page__pagination\"\n\t\t\t\t\t:currentPage=\"paginationState.page || 1\"\n\t\t\t\t\t:totalPages=\"paginationState.pages || 1\"\n\t\t\t\t\t:totalItems=\"paginationState.total || 0\"\n\t\t\t\t\t:currentPageSize=\"paginationState.limit || 20\"\n\t\t\t\t\t@pageChanged=\"onPageChange\"\n\t\t\t\t\t@pageSizeChanged=\"onPageSizeChange\" />\n\t\t\t</template>\n\n\t\t\t<div v-if=\"error\" class=\"cn-logs-page__error\">\n\t\t\t\t<!-- @slot Replaces the error block shown when the fetch fails. -->\n\t\t\t\t<!-- @binding {*} error The recorded fetch error. -->\n\t\t\t\t<slot name=\"error\" :error=\"error\">\n\t\t\t\t\t<NcEmptyContent :name=\"errorText\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<AlertCircleOutline :size=\"64\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t</NcEmptyContent>\n\t\t\t\t</slot>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- Row-detail dialog (opt-in via `rowDetail`). A log row's interesting\n\t\t payload is usually a nested object (a stack trace, an argument bag)\n\t\t that a table cell can only summarise. -->\n\t\t<NcDialog\n\t\t\tv-if=\"detailRow\"\n\t\t\t:open=\"true\"\n\t\t\t:name=\"detailTitle\"\n\t\t\tsize=\"large\"\n\t\t\t@update:open=\"closeDetail\">\n\t\t\t<!-- @slot Replaces the built-in row-detail dialog body. -->\n\t\t\t<!-- @binding {object} row The clicked log row. -->\n\t\t\t<slot name=\"row-detail\" :row=\"detailRow\">\n\t\t\t\t<div class=\"cn-logs-page__detail\">\n\t\t\t\t\t<CnDetailGrid\n\t\t\t\t\t\tv-if=\"detailScalarItems.length > 0\"\n\t\t\t\t\t\tlayout=\"horizontal\"\n\t\t\t\t\t\t:items=\"detailScalarItems\" />\n\t\t\t\t\t<template v-for=\"block in detailObjectBlocks\" :key=\"block.key\">\n\t\t\t\t\t\t<h4 class=\"cn-logs-page__detail-heading\">\n\t\t\t\t\t\t\t{{ block.label }}\n\t\t\t\t\t\t</h4>\n\t\t\t\t\t\t<CnDetailGrid\n\t\t\t\t\t\t\tv-if=\"block.items\"\n\t\t\t\t\t\t\tlayout=\"horizontal\"\n\t\t\t\t\t\t\t:items=\"block.items\" />\n\t\t\t\t\t\t<pre v-else class=\"cn-logs-page__detail-json\">{{ block.json }}</pre>\n\t\t\t\t\t</template>\n\t\t\t\t</div>\n\t\t\t</slot>\n\t\t\t<template #actions>\n\t\t\t\t<NcButton @click=\"closeDetail\">\n\t\t\t\t\t{{ closeLabel }}\n\t\t\t\t</NcButton>\n\t\t\t</template>\n\t\t</NcDialog>\n\t</div>\n</template>\n\n<script>\nimport axios from '@nextcloud/axios'\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcButton, NcDialog, NcEmptyContent, NcLoadingIcon } from '@nextcloud/vue'\nimport { getCurrentInstance, inject } from 'vue'\nimport AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport HistoryIcon from 'vue-material-design-icons/History.vue'\nimport { useListView } from '../../composables/index.js'\nimport { useObjectStore } from '../../store/index.js'\nimport { prefixUrl } from '../../utils/headers.js'\nimport { openRowTarget } from '../../utils/linkNavigation.js'\nimport { multiKeySort } from '../../utils/multiKeySort.js'\nimport { parseSortKeysFromQuery, resolveFilterMap, resolveQueryFilters } from '../../utils/routeFilters.js'\nimport { markNewTabHandled } from '../../utils/rowAuxClick.js'\nimport { CnDataTable } from '../CnDataTable/index.js'\nimport { CnDetailGrid } from '../CnDetailGrid/index.js'\nimport { CnPageHeader } from '../CnPageHeader/index.js'\nimport { CnPagination } from '../CnPagination/index.js'\n\n/**\n * Legacy default columns. Retained ONLY for `source` mode and for a store\n * mode whose schema could not be loaded — see `resolvedColumns`. They match\n * no OpenRegister log schema, so a store-backed page derives its columns\n * from the schema instead.\n *\n * A function, not a constant: `t()` at module scope would resolve before the\n * consuming app calls `registerTranslations()`, pinning the labels to English.\n *\n * @return {Array<{key: string, label: string, sortable?: boolean}>} The legacy column set.\n */\nfunction legacyDefaultColumns() {\n\treturn [\n\t\t{ key: 'timestamp', label: t('nextcloud-vue', 'Timestamp'), sortable: true },\n\t\t{ key: 'actor', label: t('nextcloud-vue', 'Actor'), sortable: true },\n\t\t{ key: 'action', label: t('nextcloud-vue', 'Action'), sortable: true },\n\t\t{ key: 'target', label: t('nextcloud-vue', 'Target') },\n\t\t{ key: 'details', label: t('nextcloud-vue', 'Details') },\n\t]\n}\n\n/**\n * CnLogsPage — Read-only audit-trail / activity-log page.\n *\n * Renders a CnDataTable of log entries with server-side sorting,\n * pagination and filtering. Columns come from `columns` (manifest:\n * `pages[].config.columns`); when none are given, a store-backed page\n * derives them from the schema.\n *\n * Two data-fetch modes:\n * - `register` + `schema`: fetched via `useObjectStore` as a regular\n * OpenRegister collection. The store registers the type as\n * `${register}-${schema}` per existing convention. List state is\n * owned by `useListView`, so `_limit` / `_page` / `_order` and any\n * `$route.query` / `filter` entries are sent with every request.\n * - `source`: a custom URL fetched via `axios.get(source)`. The\n * response shape may be either `{ results: [...] }` or a bare\n * array; the component handles both. This escape hatch supports\n * apps whose log surface is not OR-backed (e.g. a flat file).\n * Sorting is applied client-side; there is no pagination.\n *\n * Filtering (store mode) merges two sources, `filter` winning a collision:\n * 1. `$route.query` — every non-`_`-prefixed entry becomes a filter, so\n * `/jobs/logs?jobId=<uuid>` lands the page scoped to one job.\n * 2. `filter` — the manifest's own scoping, with `@route.<param>` /\n * `:<param>` / `@me` / `@today±Nd` tokens resolved at fetch time.\n *\n * The merged result is watched, so anything that moves it — a same-path query\n * change, a route param, the `filter` prop, or the workspace / app-config bag a\n * token in it reads — refetches from page 1, and nothing else does.\n *\n * Slots:\n * - `#header` — Replaces the default CnPageHeader.\n * - `#actions` — Renders a right-aligned action area (refresh,\n * export, etc.). The CnPageRenderer wires this from\n * `pages[].actionsComponent` when present.\n * - `#empty` — Replaces the empty-state block.\n * - `#error` — Replaces the error block. Scope: `{ error }`.\n * - `#row-actions` — Per-row action menu in the table. Scope:\n * `{ row }`.\n * - `#row-detail` — Replaces the row-detail dialog body. Scope: `{ row }`.\n * - `#column-<key>` — Per-column custom cell renderer. Scope:\n * `{ row, value }`.\n *\n * Props are intentionally permissive — every default has a sensible\n * fallback, so a manifest entry as small as\n * `{ type: \"logs\", config: { source: \"/api/logs\" } }` renders.\n */\nexport default {\n\tname: 'CnLogsPage',\n\n\tcomponents: {\n\t\tNcButton,\n\t\tNcDialog,\n\t\tNcEmptyContent,\n\t\tNcLoadingIcon,\n\t\tHistoryIcon,\n\t\tAlertCircleOutline,\n\t\tCnDataTable,\n\t\tCnDetailGrid,\n\t\tCnPageHeader,\n\t\tCnPagination,\n\t},\n\n\tprops: {\n\t\t/** Page title. Defaults to \"Activity log\" so a bare manifest entry still renders meaningfully. */\n\t\ttitle: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Activity log'),\n\t\t},\n\n\t\t/** Description shown under the title when `showTitle` is set. */\n\t\tdescription: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Whether to render the inline page header VISIBLY. Defaults to true:\n\t\t * unlike CnIndexPage (which surfaces the title in its sidebar header\n\t\t * when this is false), a logs page has no sidebar fallback, so a false\n\t\t * default left the page with no heading at all. Setting it to false is\n\t\t * now safe for accessibility either way — the `<h1>` is still rendered\n\t\t * visually-hidden, so the `<main>` landmark keeps its accessible\n\t\t * heading (WCAG 2.4.6 / 1.3.1).\n\t\t */\n\t\tshowTitle: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\n\t\t/** MDI icon name for the header. */\n\t\ticon: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * OpenRegister register slug. Required (with `schema`) for store-backed\n\t\t * mode. Changing it after mount requires a remount — CnPageRenderer keys\n\t\t * the dispatched page on register+schema, so a manifest page swap already\n\t\t * remounts. A direct consumer that rebinds it instead gets a development\n\t\t * console warning, since the bound data source does not follow.\n\t\t */\n\t\tregister: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * OpenRegister schema slug. Required (with `register`) for store-backed\n\t\t * mode. Changing it after mount requires a remount (see `register`).\n\t\t */\n\t\tschema: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Custom log source URL — used when `register`+`schema` is not set. */\n\t\tsource: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Column definitions for the table. When omitted, a store-backed page\n\t\t * derives its columns from the loaded schema; a `source`-backed page\n\t\t * (or one whose schema failed to load) falls back to\n\t\t * `[timestamp, actor, action, target, details]`.\n\t\t * Pass either an array of strings (treated as keys + auto-labels)\n\t\t * OR an array of `{ key, label, sortable, width }` objects.\n\t\t *\n\t\t * @type {Array<string|{key: string, label: string}>}\n\t\t */\n\t\tcolumns: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * Fixed filter map merged into every fetch, ABOVE the `$route.query`\n\t\t * deep-link filters so the page's own scoping wins on a key collision.\n\t\t * Values support the shared token grammar: `@route.<param>` / `:<param>`\n\t\t * (route params), `@me`, `@today±Nd`, `@workspace.<key>`. Null = no fixed\n\t\t * filter. Store mode only. Reactive: changing the map — or changing what a\n\t\t * token in it resolves to — refetches from page 1.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tfilter: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Pagination config; only `limit` is read, as the page size sent as\n\t\t * `_limit`. Null = the store default of 20. Store mode only.\n\t\t *\n\t\t * @type {{limit?: number}|null}\n\t\t */\n\t\tpagination: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Initial sort column. Null (the default) sends no `_order`, leaving the\n\t\t * server's own ordering in place.\n\t\t */\n\t\tsortKey: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Initial sort direction. Inert while `sortKey` is null. */\n\t\tsortOrder: {\n\t\t\ttype: String,\n\t\t\tdefault: 'asc',\n\t\t\tvalidator: (v) => v === null || ['asc', 'desc'].includes(v),\n\t\t},\n\n\t\t/**\n\t\t * Initial MULTI-column sort as an ordered priority list. Takes precedence\n\t\t * over `sortKey`/`sortOrder` when non-empty; empty (the default) is inert.\n\t\t * A `?_order=` param on the URL (the JSON-encoded list CnIndexPage\n\t\t * persists when a header is clicked) outranks both, so a shared or\n\t\t * reloaded logs link opens on the sort it carried.\n\t\t *\n\t\t * @type {Array<{key: string, order: 'asc'|'desc'}>}\n\t\t */\n\t\tsortKeys: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * Make the columns' declared `width` authoritative (`table-layout: fixed`)\n\t\t * instead of a hint the browser may override from cell content. Worth\n\t\t * setting on a log table: message text and long unbreakable values like a\n\t\t * PHP FQCN otherwise dictate the column widths between them. Long values\n\t\t * then wrap inside their cell rather than overflowing it. Default false\n\t\t * keeps the previous auto layout.\n\t\t */\n\t\tfixedLayout: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Open a read-only detail dialog when a row is clicked, rendering the\n\t\t * entry's fields — including nested bags like a stack trace or an\n\t\t * argument map that a table cell can only summarise. Default false\n\t\t * keeps a row click inert, as before. Ignored when `rowRoute` is set:\n\t\t * a purpose-built page beats a generic dialog.\n\t\t */\n\t\trowDetail: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Manifest page id (route name) to open on a row click, pushed as\n\t\t * `{ name: rowRoute, params: { id: row[rowKey] } }` — the same shape\n\t\t * CnIndexPage's `open-page` actions use. For a log whose detail surface\n\t\t * is a real page (a step timeline, a replay action) rather than a\n\t\t * read-only dump. Takes precedence over `rowDetail`.\n\t\t */\n\t\trowRoute: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/** Row identifier property. Defaults to `id` (matches OR + most custom log shapes). */\n\t\trowKey: {\n\t\t\ttype: String,\n\t\t\tdefault: 'id',\n\t\t},\n\n\t\t/** Text shown when there are no log entries. */\n\t\temptyText: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'No log entries to show'),\n\t\t},\n\n\t\t/** Text shown when the fetch fails. */\n\t\terrorText: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Could not load log entries'),\n\t\t},\n\n\t\t/** Label for the row-detail dialog's close button. */\n\t\tcloseLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: () => t('nextcloud-vue', 'Close'),\n\t\t},\n\n\t\t/**\n\t\t * Override the object store. Useful when the consuming app calls\n\t\t * `createObjectStore` with a custom ID. When null, the default\n\t\t * `useObjectStore()` is used. The component skips store wiring\n\t\t * entirely when in `source` mode.\n\t\t *\n\t\t * @type {object|null}\n\t\t */\n\t\tstore: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: [\n\t\t/**\n\t\t * @event action Declared for hosts that dispatch log-row actions through\n\t\t * the `#row-actions` slot. Never emitted by this component itself; kept\n\t\t * in the surface because consumers may already listen for it.\n\t\t */\n\t\t'action',\n\t\t/**\n\t\t * @event row-click Emitted when a log row's body is clicked, whether or\n\t\t * not `rowDetail` opened the detail dialog — so a host can navigate\n\t\t * instead of (or as well as) showing the dialog. Payload: `(row, event)`,\n\t\t * the second being the native click event. A middle click emits\n\t\t * `row-aux-click` instead.\n\t\t * @type {object}\n\t\t */\n\t\t'row-click',\n\t\t/**\n\t\t * @event row-aux-click Emitted when a log row's body is middle-clicked,\n\t\t * for opening it in a new tab. Payload: `(row, event)`, the second being\n\t\t * the native auxclick event.\n\t\t * @type {object}\n\t\t */\n\t\t'row-aux-click',\n\t],\n\n\tsetup(props) {\n\t\t// Always returned, even when the composable is not used: Vue warns when\n\t\t// a computed reads a key setup() omitted, and `source` mode reads these.\n\t\t// `resolveFixedFilters` is included for the same reason — the\n\t\t// `filterSignature` computed calls it by name, and `source` mode (which\n\t\t// has no fixed filters at all) takes this early return.\n\t\tconst NO_LIST = {\n\t\t\tlist: null,\n\t\t\tobjectType: '',\n\t\t\tlistStore: null,\n\t\t\tresolveFixedFilters: () => ({}),\n\t\t}\n\t\tif (!props.register || !props.schema) {\n\t\t\treturn NO_LIST\n\t\t}\n\n\t\tconst store = props.store || useObjectStore()\n\t\t// The `store` prop has always accepted a hand-rolled object, but\n\t\t// useListView assumes a full OpenRegister store (pagination/errors/\n\t\t// fetchSchema). Duck-type it and fall back to the legacy no-params\n\t\t// fetch rather than throwing on a partial store.\n\t\tif (typeof store.fetchCollection !== 'function' || !store.pagination) {\n\t\t\treturn NO_LIST\n\t\t}\n\n\t\tconst objectType = `${props.register}-${props.schema}`\n\t\t// Must run synchronously: useListView's onMounted calls fetchSchema(),\n\t\t// which throws when the type is not registered yet. Positional\n\t\t// signature: (slug, schemaId, registerId).\n\t\tif (typeof store.registerObjectType === 'function') {\n\t\t\tstore.registerObjectType(objectType, props.schema, props.register)\n\t\t}\n\n\t\tconst instance = getCurrentInstance()\n\n\t\t// Token-resolution context for the `filter` prop's `@workspace.<key>` /\n\t\t// `@config.<key>` / `@object.<field>` tokens. Same injects + unwrap shape\n\t\t// as useSelfFetchList's `tokenCtx`. Without it `resolveFilterMap` was\n\t\t// called with no ctx, and `resolveFilterValue` returns an unresolvable\n\t\t// token VERBATIM — so a page declaring `filter: { client:\n\t\t// '@workspace.selectedClient' }` sent the literal string\n\t\t// \"@workspace.selectedClient\" to OpenRegister as a property filter and got\n\t\t// an empty table with no error, despite the prop doc naming that token.\n\t\t// All three default to absent, so an app that never provides them is\n\t\t// unaffected.\n\t\tconst objectCtxRaw = inject('cnObjectContext', null)\n\t\tconst workspaceCtxRaw = inject('cnWorkspaceContext', null)\n\t\tconst appConfigRaw = inject('cnAppConfig', null)\n\t\tconst unwrapCtx = (v) => ((v && typeof v === 'object' && 'value' in v) ? v.value : v)\n\t\tconst tokenCtx = () => {\n\t\t\tconst objCtx = unwrapCtx(objectCtxRaw)\n\t\t\tconst base = (objCtx && typeof objCtx === 'object') ? { ...objCtx } : {}\n\t\t\tbase.workspace = unwrapCtx(workspaceCtxRaw) || {}\n\t\t\tbase.config = unwrapCtx(appConfigRaw) || {}\n\t\t\treturn base\n\t\t}\n\t\t// The complete fetch scope: the deep-link query filters plus the `filter`\n\t\t// prop's route/`@`-token resolution. A getter, not a computed — it is\n\t\t// re-read on every fetch, so a `?jobId=` change re-scopes the list\n\t\t// without re-creating it, and `tokenCtx()` reflects the latest bags.\n\t\tconst resolveFixedFilters = () => {\n\t\t\tconst route = instance && instance.proxy && instance.proxy.$route\n\t\t\tconst ctx = tokenCtx()\n\t\t\treturn {\n\t\t\t\t...resolveQueryFilters(route && route.query, ctx),\n\t\t\t\t...resolveFilterMap(props.filter, (route && route.params) || {}, ctx),\n\t\t\t}\n\t\t}\n\n\t\tconst list = useListView(objectType, {\n\t\t\tobjectStore: store,\n\t\t\tdefaultPageSize: (props.pagination && props.pagination.limit) || undefined,\n\t\t\t// A `?_order=` deep link wins over the configured default, so a\n\t\t\t// reloaded or shared logs URL reproduces the sort the link carried —\n\t\t\t// the same precedence CnIndexPage's useSelfFetchList applies.\n\t\t\tdefaultSort: props.sortKey ? { key: props.sortKey, order: props.sortOrder || 'asc' } : undefined,\n\t\t\tdefaultSortKeys: parseSortKeysFromQuery(instance && instance.proxy && instance.proxy.$route)\n\t\t\t\t|| (props.sortKeys.length > 0 ? props.sortKeys : undefined),\n\t\t\tfixedFilters: resolveFixedFilters,\n\t\t})\n\n\t\treturn { list, objectType, listStore: store, resolveFixedFilters }\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tlocalRows: [],\n\t\t\tlocalLoading: false,\n\t\t\tlocalError: null,\n\t\t\tlocalSortKeys: [],\n\t\t\tdetailRow: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/** Whether the component fetches via the object store + useListView. */\n\t\tusesStore() {\n\t\t\treturn !!this.list\n\t\t},\n\n\t\t/**\n\t\t * Whether the component should fetch via axios. Store mode needs BOTH\n\t\t * `register` and `schema`, so a half-configured pair with a `source` set\n\t\t * still falls back to the URL rather than the empty state.\n\t\t */\n\t\tusesSource() {\n\t\t\treturn !(this.register && this.schema) && !!this.source\n\t\t},\n\n\t\t/** Loading state of the active fetch mode. */\n\t\tloading() {\n\t\t\t// `list` is a bag of refs returned from setup(); only TOP-LEVEL setup\n\t\t\t// refs are auto-unwrapped, so every read below goes through `.value`.\n\t\t\treturn this.list ? this.list.loading.value : this.localLoading\n\t\t},\n\n\t\t/**\n\t\t * Fetch error. `fetchCollection` records failures on the store rather\n\t\t * than throwing, so store mode reads them from there — without this the\n\t\t * `#error` slot never rendered for a store-backed page.\n\t\t */\n\t\terror() {\n\t\t\tif (this.localError) {\n\t\t\t\treturn this.localError\n\t\t\t}\n\t\t\tif (this.list && this.listStore) {\n\t\t\t\treturn this.listStore.errors[this.objectType] || null\n\t\t\t}\n\t\t\treturn null\n\t\t},\n\n\t\t/** Rows to render: the store collection, or locally sorted axios rows. */\n\t\trows() {\n\t\t\tif (this.list) {\n\t\t\t\treturn this.list.objects.value\n\t\t\t}\n\t\t\t// Legacy-store fallback: read the store's collection LIVE rather than\n\t\t\t// the snapshot `fetch()` took. `rows` used to reach the collection\n\t\t\t// through a computed; snapshotting into `localRows` meant a store that\n\t\t\t// REPLACES the array (rather than mutating it in place) no longer\n\t\t\t// reached the table. Only `this.store` is consulted — in this path it is\n\t\t\t// the partial store prop that made setup() bail — so `source` mode,\n\t\t\t// which has no store, still falls through to localRows.\n\t\t\tconst live = (this.register && this.schema && this.store)\n\t\t\t\t? this.store.collections?.[`${this.register}-${this.schema}`]\n\t\t\t\t: null\n\t\t\tconst base = Array.isArray(live) ? live : this.localRows\n\t\t\tif (this.localSortKeys.length === 0) {\n\t\t\t\treturn base\n\t\t\t}\n\t\t\t// CnDataTable is presentational — it never sorts its own rows — so\n\t\t\t// `source` mode has to apply the header sort itself.\n\t\t\treturn multiKeySort(base, this.localSortKeys.map((k) => ({ field: k.key, order: k.order })))\n\t\t},\n\n\t\t/**\n\t\t * Server pagination STATE (`{ total, page, pages, limit }`); null in\n\t\t * source mode, which has no paging. Distinct from the `pagination`\n\t\t * prop, which is the manifest's page-size config.\n\t\t */\n\t\tpaginationState() {\n\t\t\treturn this.list ? this.list.pagination.value : null\n\t\t},\n\n\t\t/**\n\t\t * The LOADED schema object (not the `schema` slug prop), forwarded to\n\t\t * CnDataTable for type-aware cell rendering and schema-derived columns.\n\t\t */\n\t\ttableSchema() {\n\t\t\treturn (this.list && this.list.schema.value) || null\n\t\t},\n\n\t\t/** Active ordered sort keys, from the composable or local source state. */\n\t\teffectiveSortKeys() {\n\t\t\treturn this.list ? this.list.sortKeys.value : this.localSortKeys\n\t\t},\n\n\t\t/** Primary sort key (mirrors `effectiveSortKeys[0]`) for CnDataTable. */\n\t\teffectiveSortKey() {\n\t\t\treturn this.effectiveSortKeys[0]?.key ?? null\n\t\t},\n\n\t\t/** Primary sort direction (mirrors `effectiveSortKeys[0]`). */\n\t\teffectiveSortOrder() {\n\t\t\treturn this.effectiveSortKeys[0]?.order ?? 'asc'\n\t\t},\n\n\t\t/**\n\t\t * Resolved columns. A consumer-provided list wins. Otherwise, when a\n\t\t * schema is loaded, return `[]` so CnDataTable derives the columns from\n\t\t * it — the legacy `timestamp/actor/action/target/details` default matched\n\t\t * no OpenRegister log schema and rendered five blank cells. `source` mode\n\t\t * and a failed schema load still get that legacy default.\n\t\t */\n\t\tresolvedColumns() {\n\t\t\tif (this.columns.length > 0) {\n\t\t\t\treturn this.columns.map((c) => (typeof c === 'string' ? { key: c, label: this.humanise(c) } : c))\n\t\t\t}\n\t\t\tif (this.tableSchema) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn legacyDefaultColumns()\n\t\t},\n\n\t\t/** Column slot names that the parent has provided (for pass-through). */\n\t\tslotColumns() {\n\t\t\treturn Object.keys(this.$slots || {})\n\t\t\t\t.filter((name) => name.startsWith('column-'))\n\t\t\t\t.map((name) => name.replace('column-', ''))\n\t\t},\n\n\t\t/** Dialog heading: the entry's message when it has one, else its id. */\n\t\tdetailTitle() {\n\t\t\tif (!this.detailRow) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst message = this.detailRow.message\n\t\t\tif (typeof message === 'string' && message !== '') {\n\t\t\t\treturn message\n\t\t\t}\n\t\t\tconst id = this.detailRow[this.rowKey]\n\t\t\treturn id ? String(id) : t('nextcloud-vue', 'Log entry')\n\t\t},\n\n\t\t/** The clicked row's primitive fields, as CnDetailGrid items. */\n\t\tdetailScalarItems() {\n\t\t\treturn this.detailEntries\n\t\t\t\t.filter(([, value]) => !this.isBag(value))\n\t\t\t\t.map(([key, value]) => ({ label: this.propertyLabel(key), value: String(value) }))\n\t\t},\n\n\t\t/**\n\t\t * The clicked row's nested fields. A bag of primitives (a stack trace's\n\t\t * frames, an argument map) renders as its own labelled grid; anything\n\t\t * deeper falls back to pretty-printed JSON.\n\t\t */\n\t\tdetailObjectBlocks() {\n\t\t\treturn this.detailEntries\n\t\t\t\t.filter(([, value]) => this.isBag(value))\n\t\t\t\t.map(([key, value]) => {\n\t\t\t\t\tconst entries = Object.entries(value)\n\t\t\t\t\tconst flat = entries.every(([, v]) => v === null || typeof v !== 'object')\n\t\t\t\t\treturn {\n\t\t\t\t\t\tkey,\n\t\t\t\t\t\tlabel: this.propertyLabel(key),\n\t\t\t\t\t\titems: flat ? entries.map(([k, v]) => ({ label: this.humanise(k), value: String(v) })) : null,\n\t\t\t\t\t\tjson: flat ? null : JSON.stringify(value, null, 2),\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Stable signature of the RESOLVED fetch scope — the query deep-link\n\t\t * filters plus the `filter` prop after route-param and `@`-token\n\t\t * resolution. Reading through the same getter the fetch uses is what\n\t\t * makes it exact: it tracks `$route`, `props.filter` and the injected\n\t\t * workspace / app-config bags, and it changes only when the request the\n\t\t * list would issue actually changes. Stringified so the watcher fires on\n\t\t * content, not identity. Empty string in `source` mode (no list).\n\t\t *\n\t\t * @return {string} The serialized filter map.\n\t\t */\n\t\tfilterSignature() {\n\t\t\tif (!this.list) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn JSON.stringify(this.resolveFixedFilters())\n\t\t},\n\n\t\t/** The clicked row's renderable entries — `@self` and empties dropped. */\n\t\tdetailEntries() {\n\t\t\tif (!this.detailRow) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn Object.entries(this.detailRow)\n\t\t\t\t.filter(([key, value]) => key !== '@self' && value !== null && value !== undefined && value !== '')\n\t\t},\n\t},\n\n\twatch: {\n\t\t// ONE watcher over the whole fetch scope, rather than one per reactive\n\t\t// source that feeds it. `fixedFilters` is a plain getter re-read at fetch\n\t\t// time, so a refresh is all any of them needs — but watching them\n\t\t// individually got three things wrong:\n\t\t//\n\t\t// - `$route.query` and `$route.params` both fire on a single navigation,\n\t\t// so one route change issued TWO identical concurrent fetches, which\n\t\t// `fetchCollection` does not dedup and whose responses can land in\n\t\t// either order;\n\t\t// - they fired on navigation AWAY as well, because the route updates\n\t\t// before this component tears down. The refetch was then scoped by the\n\t\t// DESTINATION route's query (`/cases?status=open` → `GET\n\t\t// …/job_log?status=open`), overwriting the shared store collection that\n\t\t// any other mounted widget on the same objectType reads;\n\t\t// - a workspace / app-config bag mutation refetched even when no\n\t\t// `@workspace.`/`@config.` token was in play, so a volatile bag reset a\n\t\t// token-free page to page 1 on every write.\n\t\t//\n\t\t// The signature is the RESOLVED filter map, so it changes only when the\n\t\t// scope actually changes, and the route-name guard drops the fire that\n\t\t// belongs to the page being navigated to. No `immediate` — useListView\n\t\t// owns the initial fetch. This also covers a reactive `filter` prop\n\t\t// change, which had no watcher of its own at all.\n\t\tfilterSignature(next, prev) {\n\t\t\tif (!this.list || next === prev) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// Guarded only when this page HAS a route name to compare against —\n\t\t\t// a router-less host, or one whose routes are unnamed, keeps the\n\t\t\t// unguarded behaviour rather than losing refetches to a comparison\n\t\t\t// that can never match.\n\t\t\tif (this._ownRouteName !== undefined && this.$route?.name !== this._ownRouteName) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.list.refresh(1)\n\t\t},\n\n\t\tregister(value, previous) {\n\t\t\tthis.onSourcePropChange('register', value, previous)\n\t\t},\n\n\t\tschema(value, previous) {\n\t\t\tthis.onSourcePropChange('schema', value, previous)\n\t\t},\n\n\t\tsource(value, previous) {\n\t\t\tthis.onSourcePropChange('source', value, previous)\n\t\t},\n\t},\n\n\tcreated() {\n\t\t// The route this page belongs to, captured BEFORE any navigation can\n\t\t// change it — the `filterSignature` watcher's guard. CnPageRenderer mounts\n\t\t// a page when `$route.name === page.id`, so this is the page's own id.\n\t\tthis._ownRouteName = this.$route ? this.$route.name : undefined\n\t},\n\n\tmounted() {\n\t\t// In store mode useListView's own onMounted owns the initial fetch;\n\t\t// calling fetch() here too would double-request on every page load.\n\t\tif (!this.list) {\n\t\t\tthis.fetch()\n\t\t}\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Capitalise + space a snake_case / camelCase key for a default column label.\n\t\t *\n\t\t * @param {string} key A log-entry property name, e.g. `'created_at'` or `'userAgent'`.\n\t\t * @return {string} The sentence-cased label, e.g. `'Created at'` / `'User agent'`.\n\t\t */\n\t\thumanise(key) {\n\t\t\tconst spaced = String(key).replace(/([A-Z])/g, ' $1').replace(/_/g, ' ').toLowerCase()\n\t\t\treturn spaced.charAt(0).toUpperCase() + spaced.slice(1)\n\t\t},\n\n\t\t/**\n\t\t * Label for a row property — the schema's own title when one is loaded.\n\t\t *\n\t\t * @param {string} key The property name.\n\t\t * @return {string} The display label.\n\t\t */\n\t\tpropertyLabel(key) {\n\t\t\tconst title = this.tableSchema?.properties?.[key]?.title\n\t\t\treturn (typeof title === 'string' && title !== '') ? title : this.humanise(key)\n\t\t},\n\n\t\t/**\n\t\t * Whether a value is a nested bag (object/array) rather than a scalar.\n\t\t *\n\t\t * @param {unknown} value The value to test.\n\t\t * @return {boolean} True for non-null objects and arrays.\n\t\t */\n\t\tisBag(value) {\n\t\t\treturn value !== null && typeof value === 'object'\n\t\t},\n\n\t\t/**\n\t\t * Fetch log entries from the resolved data source.\n\t\t *\n\t\t * Only used in `source` mode and in the legacy store fallback — the\n\t\t * composable owns fetching whenever it is active.\n\t\t */\n\t\tasync fetch() {\n\t\t\tif (this.register && this.schema) {\n\t\t\t\t// Legacy fallback: a partial `store` prop that useListView\n\t\t\t\t// cannot drive (see setup). Unfiltered + unpaginated, as before.\n\t\t\t\tconst store = this.store || useObjectStore()\n\t\t\t\tthis.localLoading = true\n\t\t\t\tthis.localError = null\n\t\t\t\ttry {\n\t\t\t\t\tif (typeof store.registerObjectType === 'function') {\n\t\t\t\t\t\tstore.registerObjectType(`${this.register}-${this.schema}`, this.schema, this.register)\n\t\t\t\t\t}\n\t\t\t\t\tif (typeof store.fetchCollection === 'function') {\n\t\t\t\t\t\tawait store.fetchCollection(`${this.register}-${this.schema}`)\n\t\t\t\t\t}\n\t\t\t\t\tthis.localRows = store.collections?.[`${this.register}-${this.schema}`] ?? []\n\t\t\t\t} catch (err) {\n\t\t\t\t\tthis.localError = err\n\t\t\t\t} finally {\n\t\t\t\t\tthis.localLoading = false\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (this.usesSource) {\n\t\t\t\tthis.localLoading = true\n\t\t\t\tthis.localError = null\n\t\t\t\ttry {\n\t\t\t\t\tconst response = await axios.get(prefixUrl(this.source))\n\t\t\t\t\tconst body = response?.data\n\t\t\t\t\tif (Array.isArray(body)) {\n\t\t\t\t\t\tthis.localRows = body\n\t\t\t\t\t} else if (body && Array.isArray(body.results)) {\n\t\t\t\t\t\tthis.localRows = body.results\n\t\t\t\t\t} else if (body && Array.isArray(body.entries)) {\n\t\t\t\t\t\tthis.localRows = body.entries\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.localRows = []\n\t\t\t\t\t}\n\t\t\t\t} catch (err) {\n\t\t\t\t\tthis.localError = err\n\t\t\t\t\tthis.localRows = []\n\t\t\t\t} finally {\n\t\t\t\t\tthis.localLoading = false\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// Misconfigured — surface a console warning so a developer notices.\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.warn('[CnLogsPage] Neither register+schema nor source configured; rendering empty state.')\n\t\t},\n\n\t\t/**\n\t\t * React to a data-source prop changing on a mounted page.\n\t\t *\n\t\t * Only the legacy / `source` paths can act on it, by refetching. Store\n\t\t * mode cannot: the object type, its store registration and the\n\t\t * `useListView` instance are all built once in `setup()` from the\n\t\t * initial pair, so a new `register`/`schema` needs a remount to take\n\t\t * effect (which CnPageRenderer does on a manifest page swap). That is\n\t\t * documented on the props, but it used to happen in complete silence —\n\t\t * a direct consumer rebinding the prop saw the old collection and no\n\t\t * indication why. Warns in development only, once per change.\n\t\t *\n\t\t * @param {string} name The prop that changed.\n\t\t * @param {unknown} value The new value.\n\t\t * @param {unknown} previous The previous value.\n\t\t * @return {void}\n\t\t */\n\t\tonSourcePropChange(name, value, previous) {\n\t\t\tif (!this.list) {\n\t\t\t\tthis.fetch()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (process.env.NODE_ENV !== 'production') {\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn(`[CnLogsPage] \\`${name}\\` changed from \"${previous}\" to \"${value}\" on a mounted store-backed page. `\n\t\t\t\t\t+ 'The data source is bound once at setup, so this has no effect — remount the page (e.g. give it a '\n\t\t\t\t\t+ ':key) to pick up the new register/schema.')\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Apply a header sort. Server-side in store mode, client-side otherwise.\n\t\t *\n\t\t * @param {{key: string|null, order: string|null, keys?: Array<{key: string, order: string}>}} sort CnDataTable's sort payload.\n\t\t */\n\t\tonSort(sort) {\n\t\t\tif (this.list) {\n\t\t\t\tthis.list.onSort(sort)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.localSortKeys = Array.isArray(sort.keys)\n\t\t\t\t? sort.keys\n\t\t\t\t: (sort.key ? [{ key: sort.key, order: sort.order || 'asc' }] : [])\n\t\t},\n\n\t\t/**\n\t\t * Navigate to a page of results.\n\t\t *\n\t\t * @param {number} page The 1-based page number.\n\t\t */\n\t\tonPageChange(page) {\n\t\t\tthis.list?.onPageChange(page)\n\t\t},\n\n\t\t/**\n\t\t * Change the page size, returning to page 1.\n\t\t *\n\t\t * @param {number} size The new page size.\n\t\t */\n\t\tonPageSizeChange(size) {\n\t\t\tthis.list?.onPageSizeChange(size)\n\t\t},\n\n\t\t/**\n\t\t * Handle a row-body click: navigate to `rowRoute` when one is declared,\n\t\t * else open the detail dialog when `rowDetail` is set. Always re-emits\n\t\t * so a host can do its own thing regardless. A ctrl/cmd/shift click\n\t\t * opens `rowRoute` in a new tab.\n\t\t *\n\t\t * @param {object} row The clicked log entry.\n\t\t * @param {MouseEvent} [event] The originating click event.\n\t\t */\n\t\tonRowClick(row, event) {\n\t\t\tif (this.rowRoute) {\n\t\t\t\tmarkNewTabHandled(event, openRowTarget(event, { name: this.rowRoute, params: { id: row?.[this.rowKey] } }, this.$router))\n\t\t\t} else if (this.rowDetail) {\n\t\t\t\tthis.detailRow = row\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event row-click Emitted when a log row's body is clicked. Payload: `(row, event)` — the log entry and the native click event.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('row-click', row, event)\n\t\t},\n\n\t\t/**\n\t\t * Handle a row-body middle click: open `rowRoute` in a new tab, never\n\t\t * the detail dialog, and emit `row-aux-click` rather than `row-click`.\n\t\t *\n\t\t * @param {object} row The clicked log entry.\n\t\t * @param {MouseEvent} event The originating auxclick event.\n\t\t */\n\t\tonRowAuxClick(row, event) {\n\t\t\tif (this.rowRoute) {\n\t\t\t\tmarkNewTabHandled(event, openRowTarget(event, { name: this.rowRoute, params: { id: row?.[this.rowKey] } }, this.$router))\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event row-aux-click Emitted when a log row's body is middle-clicked. Payload: `(row, event)` — the log entry and the native auxclick event.\n\t\t\t * @type {object}\n\t\t\t */\n\t\t\tthis.$emit('row-aux-click', row, event)\n\t\t},\n\n\t\t/** Close the row-detail dialog. */\n\t\tcloseDetail() {\n\t\t\tthis.detailRow = null\n\t\t},\n\n\t\t/**\n\t\t * Re-fetch from the source. Exposed so refresh buttons in\n\t\t * actionsComponent can call `$parent.refresh()`.\n\t\t *\n\t\t * @public\n\t\t */\n\t\trefresh() {\n\t\t\tif (this.list) {\n\t\t\t\tthis.list.refresh(1)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.fetch()\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n/* Padding matches CnIndexPage / CnDetailPage (see css/index-page.css and\n css/detail-page.css) — a logs page had none at all, so its table ran flush\n into both edges of the app content area. Gaps are expressed in the same\n grid-baseline unit as the rest of the fleet rather than raw pixels. */\n.cn-logs-page {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: calc(4 * var(--default-grid-baseline));\n\tpadding: calc(5 * var(--default-grid-baseline));\n\tbox-sizing: border-box;\n}\n\n.cn-logs-page__actions {\n\tdisplay: flex;\n\tjustify-content: flex-end;\n\tgap: calc(2 * var(--default-grid-baseline));\n}\n\n.cn-logs-page__loading,\n.cn-logs-page__empty,\n.cn-logs-page__error {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\tmin-height: 200px;\n}\n\n.cn-logs-page__pagination {\n\tmargin-top: calc(3 * var(--default-grid-baseline));\n}\n\n.cn-logs-page__detail {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: calc(2 * var(--default-grid-baseline));\n\tpadding: 0 calc(3 * var(--default-grid-baseline)) calc(3 * var(--default-grid-baseline));\n}\n\n.cn-logs-page__detail-heading {\n\tmargin: calc(3 * var(--default-grid-baseline)) 0 0;\n\tfont-weight: bold;\n}\n\n.cn-logs-page__detail-json {\n\tmargin: 0;\n\tpadding: calc(2 * var(--default-grid-baseline));\n\toverflow-x: auto;\n\tborder-radius: var(--border-radius);\n\tbackground-color: var(--color-background-dark);\n\tfont-family: monospace;\n\twhite-space: pre;\n}\n\n/* Same breakpoint + step-down as CnDetailPage, so the three page types stay in\n sync on narrow viewports. */\n@media (max-width: 768px) {\n\t.cn-logs-page {\n\t\tpadding: calc(3 * var(--default-grid-baseline));\n\t}\n}\n</style>\n"],"names":["t","HistoryIcon","CnDataTable","CnDetailGrid","CnPageHeader","CnPagination","axios"],"mappings":"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"}