@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.
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js +12 -2
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +1 -2
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
- package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue2.js.map +1 -1
- package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js +0 -3
- package/dist/esm/components/CnActionsMenu/CnActionsMenu.vue3.js.map +1 -1
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue2.js.map +1 -1
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js +1 -2
- package/dist/esm/components/CnAdminSettingsShell/CnAdminSettingsShell.vue3.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +21 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue2.js +12 -5
- package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue3.js +2 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue3.js.map +1 -1
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js +9 -1
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue2.js.map +1 -1
- package/dist/esm/components/CnCardGrid/CnCardGrid.vue3.js.map +1 -1
- package/dist/esm/components/CnContextMenu/CnContextMenu.vue2.js +6 -1
- package/dist/esm/components/CnContextMenu/CnContextMenu.vue2.js.map +1 -1
- package/dist/esm/components/CnContextMenu/CnContextMenu.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +116 -15
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js +27 -14
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue2.js +13 -6
- package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue2.js.map +1 -1
- package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue3.js +2 -0
- package/dist/esm/components/CnDateAxisView/CnDateAxisView.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +29 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js.map +1 -1
- package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js +1 -2
- package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +3 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +17 -17
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue2.js +4 -21
- package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFilesWidget/CnFilesWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js +0 -13
- package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue3.js +8 -8
- package/dist/esm/components/CnFlowRunsWidget/CnFlowRunsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue2.js.map +1 -1
- package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue3.js +3 -2
- package/dist/esm/components/CnFlowsPage/CnFlowsPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +67 -13
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +15 -8
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/manifestActionDispatch.js +31 -11
- package/dist/esm/components/CnIndexPage/manifestActionDispatch.js.map +1 -1
- package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue2.js.map +1 -1
- package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue3.js +1 -2
- package/dist/esm/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue3.js.map +1 -1
- package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue3.js +0 -1
- package/dist/esm/components/CnInteractionFormWidget/CnInteractionFormWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue2.js +2 -17
- package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnLinkButtonWidget/CnLinkButtonWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnLogsPage/CnLogsPage.vue2.js +35 -11
- package/dist/esm/components/CnLogsPage/CnLogsPage.vue2.js.map +1 -1
- package/dist/esm/components/CnLogsPage/CnLogsPage.vue3.js +3 -2
- package/dist/esm/components/CnLogsPage/CnLogsPage.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js +23 -5
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js +1 -1
- package/dist/esm/components/CnObjectCard/CnObjectCard.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectList/CnObjectList.vue2.js +19 -3
- package/dist/esm/components/CnObjectList/CnObjectList.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectList/CnObjectList.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +45 -15
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js +4 -2
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectRow/CnObjectRow.vue2.js +24 -6
- package/dist/esm/components/CnObjectRow/CnObjectRow.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectRow/CnObjectRow.vue3.js +1 -1
- package/dist/esm/components/CnObjectRow/CnObjectRow.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +0 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
- package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js +4 -2
- package/dist/esm/components/CnPageRenderer/CnPageRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnRowActions/CnRowActions.vue2.js +7 -2
- package/dist/esm/components/CnRowActions/CnRowActions.vue2.js.map +1 -1
- package/dist/esm/components/CnRowActions/CnRowActions.vue3.js.map +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js +49 -5
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
- package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue2.js +5 -3
- package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTabs/CnTabs.vue2.js.map +1 -1
- package/dist/esm/components/CnTabs/CnTabs.vue3.js.map +1 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js +3 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js +1 -0
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue2.js +3 -1
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue2.js +3 -1
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/analytics/CnAnalyticsTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue2.js +6 -0
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue3.js +68 -53
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue2.js +17 -0
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue3.js +21 -19
- package/dist/esm/integrations/builtin/bookmarks/CnBookmarksTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue2.js +3 -1
- package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/collectives/CnCollectivesCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue2.js +3 -1
- package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/collectives/CnCollectivesTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/cospend/CnCospendTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/deck/CnDeckCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/deck/CnDeckCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/deck/CnDeckTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/deck/CnDeckTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/deck/CnDeckTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/email/CnEmailCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/email/CnEmailCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/email/CnEmailTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/email/CnEmailTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/email/CnEmailTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/flow/CnFlowCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/flow/CnFlowCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/flow/CnFlowCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/flow/CnFlowTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/flow/CnFlowTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/flow/CnFlowTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/flow/CnFlowTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/forms/CnFormsCard.vue2.js +3 -1
- package/dist/esm/integrations/builtin/forms/CnFormsCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/forms/CnFormsCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/forms/CnFormsTab.vue2.js +3 -1
- package/dist/esm/integrations/builtin/forms/CnFormsTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/forms/CnFormsTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/forms/CnFormsTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/maps/CnMapsCard.vue2.js +3 -1
- package/dist/esm/integrations/builtin/maps/CnMapsCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/maps/CnMapsCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/maps/CnMapsTab.vue2.js +3 -1
- package/dist/esm/integrations/builtin/maps/CnMapsTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/maps/CnMapsTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/maps/CnMapsTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue2.js +3 -1
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue2.js +3 -1
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/openproject/CnOpenprojectTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/photos/CnPhotosTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/polls/CnPollsCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/polls/CnPollsCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/polls/CnPollsCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/polls/CnPollsTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/polls/CnPollsTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/polls/CnPollsTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/polls/CnPollsTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/shares/CnSharesTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/shares/CnSharesTab.vue3.js +3 -6
- package/dist/esm/integrations/builtin/shares/CnSharesTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/talk/CnTalkCard.vue2.js +3 -1
- package/dist/esm/integrations/builtin/talk/CnTalkCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/talk/CnTalkCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/talk/CnTalkTab.vue2.js +3 -1
- package/dist/esm/integrations/builtin/talk/CnTalkTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/talk/CnTalkTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/talk/CnTalkTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue2.js +2 -1
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue2.js +2 -1
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue3.js +1 -2
- package/dist/esm/integrations/builtin/time-tracker/CnTimeTrackerTab.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue2.js +4 -1
- package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/xwiki/CnXwikiCard.vue3.js.map +1 -1
- package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue2.js +6 -3
- package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue2.js.map +1 -1
- package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue3.js +2 -4
- package/dist/esm/integrations/builtin/xwiki/CnXwikiTab.vue3.js.map +1 -1
- package/dist/esm/l10n/en.json.js +2 -0
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +2 -0
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +78 -7
- package/dist/esm/utils/actionLink.js +5 -0
- package/dist/esm/utils/actionLink.js.map +1 -1
- package/dist/esm/utils/actionsDispatcher.js +16 -1
- package/dist/esm/utils/actionsDispatcher.js.map +1 -1
- package/dist/esm/utils/linkNavigation.js +6 -3
- package/dist/esm/utils/linkNavigation.js.map +1 -1
- package/dist/esm/utils/rowAuxClick.js +30 -5
- package/dist/esm/utils/rowAuxClick.js.map +1 -1
- package/dist/esm/utils/safeHref.js +18 -3
- package/dist/esm/utils/safeHref.js.map +1 -1
- package/dist/esm/utils/validateManifestV2.compiled.js +2 -2
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +885 -393
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +78 -7
- package/l10n/en.json +2 -0
- package/l10n/nl.json +2 -0
- package/package.json +1 -1
- package/src/components/CnActionsBar/CnActionsBar.vue +14 -5
- package/src/components/CnActionsMenu/CnActionsMenu.vue +0 -3
- package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +1 -2
- package/src/components/CnAppRoot/CnAppRoot.vue +31 -2
- package/src/components/CnBoardView/CnBoardView.vue +13 -5
- package/src/components/CnCardGrid/CnCardGrid.vue +9 -1
- package/src/components/CnContextMenu/CnContextMenu.vue +6 -1
- package/src/components/CnDataTable/CnDataTable.md +1 -1
- package/src/components/CnDataTable/CnDataTable.vue +131 -17
- package/src/components/CnDateAxisView/CnDateAxisView.vue +15 -6
- package/src/components/CnDetailPage/CnDetailPage.md +1 -1
- package/src/components/CnDetailPage/CnDetailPage.vue +29 -1
- package/src/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue +1 -2
- package/src/components/CnFilesBrowser/CnFilesBrowser.vue +5 -3
- package/src/components/CnFilesWidget/CnFilesWidget.vue +4 -21
- package/src/components/CnFlowRunsWidget/CnFlowRunsWidget.vue +8 -21
- package/src/components/CnFlowsPage/CnFlowsPage.vue +2 -1
- package/src/components/CnIndexPage/CnIndexPage.vue +75 -14
- package/src/components/CnIndexPage/manifestActionDispatch.js +31 -11
- package/src/components/CnIntegrationWidget/CnIntegrationWidgetEmpty.vue +1 -2
- package/src/components/CnInteractionFormWidget/CnInteractionFormWidget.vue +0 -1
- package/src/components/CnLinkButtonWidget/CnLinkButtonWidget.vue +2 -17
- package/src/components/CnLogsPage/CnLogsPage.vue +37 -12
- package/src/components/CnObjectCard/CnObjectCard.vue +24 -6
- package/src/components/CnObjectList/CnObjectList.vue +19 -3
- package/src/components/CnObjectListWidget/CnObjectListWidget.vue +47 -15
- package/src/components/CnObjectRow/CnObjectRow.vue +25 -7
- package/src/components/CnObjectSidebar/CnFilesTab.vue +0 -1
- package/src/components/CnPageRenderer/CnPageRenderer.vue +2 -0
- package/src/components/CnRowActions/CnRowActions.vue +7 -2
- package/src/components/CnSetupWizard/CnSetupWizard.vue +57 -6
- package/src/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue +5 -3
- package/src/components/CnTabs/CnTabs.vue +7 -3
- package/src/components/CnTasksWidget/CnTasksWidget.vue +4 -1
- package/src/css/table.css +26 -0
- package/src/integrations/builtin/analytics/CnAnalyticsCard.vue +3 -2
- package/src/integrations/builtin/analytics/CnAnalyticsTab.vue +5 -6
- package/src/integrations/builtin/bookmarks/CnBookmarksCard.vue +12 -1
- package/src/integrations/builtin/bookmarks/CnBookmarksTab.vue +23 -6
- package/src/integrations/builtin/bookmarks/__tests__/CnBookmarksTab.spec.js +7 -2
- package/src/integrations/builtin/collectives/CnCollectivesCard.vue +3 -2
- package/src/integrations/builtin/collectives/CnCollectivesTab.vue +5 -6
- package/src/integrations/builtin/cospend/CnCospendCard.vue +2 -2
- package/src/integrations/builtin/cospend/CnCospendTab.vue +3 -4
- package/src/integrations/builtin/deck/CnDeckCard.vue +2 -2
- package/src/integrations/builtin/deck/CnDeckTab.vue +3 -4
- package/src/integrations/builtin/email/CnEmailCard.vue +6 -2
- package/src/integrations/builtin/email/CnEmailTab.vue +7 -4
- package/src/integrations/builtin/flow/CnFlowCard.vue +2 -2
- package/src/integrations/builtin/flow/CnFlowTab.vue +3 -4
- package/src/integrations/builtin/forms/CnFormsCard.vue +3 -2
- package/src/integrations/builtin/forms/CnFormsTab.vue +5 -6
- package/src/integrations/builtin/maps/CnMapsCard.vue +3 -2
- package/src/integrations/builtin/maps/CnMapsTab.vue +5 -6
- package/src/integrations/builtin/maps/__tests__/CnMapsTab.spec.js +9 -0
- package/src/integrations/builtin/openproject/CnOpenprojectCard.vue +3 -2
- package/src/integrations/builtin/openproject/CnOpenprojectTab.vue +5 -6
- package/src/integrations/builtin/photos/CnPhotosCard.vue +2 -2
- package/src/integrations/builtin/photos/CnPhotosTab.vue +3 -4
- package/src/integrations/builtin/polls/CnPollsCard.vue +2 -2
- package/src/integrations/builtin/polls/CnPollsTab.vue +3 -4
- package/src/integrations/builtin/shares/CnSharesTab.vue +3 -6
- package/src/integrations/builtin/talk/CnTalkCard.vue +3 -2
- package/src/integrations/builtin/talk/CnTalkTab.vue +5 -6
- package/src/integrations/builtin/time-tracker/CnTimeTrackerCard.vue +2 -2
- package/src/integrations/builtin/time-tracker/CnTimeTrackerTab.vue +3 -4
- package/src/integrations/builtin/xwiki/CnXwikiCard.vue +4 -2
- package/src/integrations/builtin/xwiki/CnXwikiTab.vue +7 -7
- package/src/integrations/builtin/xwiki/__tests__/CnXwikiTab.spec.js +9 -0
- package/src/schemas/app-manifest-v2.schema.json +2 -1
- package/src/utils/actionLink.js +5 -0
- package/src/utils/actionsDispatcher.js +15 -0
- package/src/utils/linkNavigation.js +6 -3
- package/src/utils/rowAuxClick.js +29 -4
- package/src/utils/safeHref.js +18 -3
- package/src/utils/validateManifestV2.compiled.js +2 -2
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<div
|
|
3
3
|
class="cn-object-row"
|
|
4
4
|
:class="{ 'cn-object-row--selected': selected }"
|
|
5
|
-
@mousedown="
|
|
5
|
+
@mousedown="onRowMouseDown"
|
|
6
6
|
@click="onRowClick($event)"
|
|
7
7
|
@auxclick="onRowAuxClick($event)">
|
|
8
8
|
<!-- Selection checkbox -->
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
import { NcCheckboxRadioSwitch } from '@nextcloud/vue'
|
|
69
69
|
import { useClickDragGuard } from '../../composables/useClickDragGuard.js'
|
|
70
70
|
import { resolveImageUrl } from '../../utils/resolveImageUrl.js'
|
|
71
|
-
import { isRowMiddleClick } from '../../utils/rowAuxClick.js'
|
|
71
|
+
import { isRowMiddleClick, preventMiddleClickAutoscroll } from '../../utils/rowAuxClick.js'
|
|
72
72
|
import { CnIcon } from '../CnIcon/index.js'
|
|
73
73
|
import { CnStatusBadge } from '../CnStatusBadge/index.js'
|
|
74
74
|
|
|
@@ -146,7 +146,7 @@ export default {
|
|
|
146
146
|
// again from `$emit('click', object)` — twice per click, with different
|
|
147
147
|
// payloads. Declaring the names here removes them from `$attrs`, which is
|
|
148
148
|
// also what stops VTU v2 recording the native DOM event into `emitted()`.
|
|
149
|
-
emits: ['select', 'click'],
|
|
149
|
+
emits: ['select', 'click', 'aux-click'],
|
|
150
150
|
|
|
151
151
|
setup() {
|
|
152
152
|
// Tell a deliberate row click apart from a text-selection drag.
|
|
@@ -237,15 +237,29 @@ export default {
|
|
|
237
237
|
return
|
|
238
238
|
}
|
|
239
239
|
/**
|
|
240
|
-
* @event click Emitted when a non-selectable row is clicked
|
|
240
|
+
* @event click Emitted when a non-selectable row is clicked (navigation). Payload: `(object, event)` — the row's object and the native click event, for opening it in a new tab on a ctrl/cmd/shift click. A middle click emits `aux-click` instead.
|
|
241
241
|
* @type {object} The row's object.
|
|
242
242
|
*/
|
|
243
243
|
this.$emit('click', this.object, event)
|
|
244
244
|
},
|
|
245
245
|
|
|
246
246
|
/**
|
|
247
|
-
* Row
|
|
248
|
-
* non-selectable row.
|
|
247
|
+
* Row mousedown: record the press for the drag guard, and on a
|
|
248
|
+
* non-selectable row keep a middle press from starting autoscroll.
|
|
249
|
+
*
|
|
250
|
+
* @param {MouseEvent} event The mousedown event.
|
|
251
|
+
*/
|
|
252
|
+
onRowMouseDown(event) {
|
|
253
|
+
this.onPointerDown(event)
|
|
254
|
+
if (!this.selectable) {
|
|
255
|
+
preventMiddleClickAutoscroll(event)
|
|
256
|
+
}
|
|
257
|
+
},
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Row-body middle click on a non-selectable row: emits `aux-click`, not
|
|
261
|
+
* `click`, so an existing `click` listener that navigates never moves
|
|
262
|
+
* the current tab away.
|
|
249
263
|
*
|
|
250
264
|
* @param {MouseEvent} event The auxclick event.
|
|
251
265
|
*/
|
|
@@ -253,7 +267,11 @@ export default {
|
|
|
253
267
|
if (this.selectable || !isRowMiddleClick(event)) {
|
|
254
268
|
return
|
|
255
269
|
}
|
|
256
|
-
|
|
270
|
+
/**
|
|
271
|
+
* @event aux-click Emitted when a non-selectable row is middle-clicked, for opening it in a new tab (see `openRowTarget`). Payload: `(object, event)` — the row's object and the native auxclick event.
|
|
272
|
+
* @type {object} The row's object.
|
|
273
|
+
*/
|
|
274
|
+
this.$emit('aux-click', this.object, event)
|
|
257
275
|
},
|
|
258
276
|
},
|
|
259
277
|
}
|
|
@@ -128,6 +128,7 @@
|
|
|
128
128
|
v-bind="{ ...$attrs, ...resolvedProps }"
|
|
129
129
|
@view="onRowOpen"
|
|
130
130
|
@rowClick="onRowOpen"
|
|
131
|
+
@rowAuxClick="onRowOpen"
|
|
131
132
|
@editOpen="onRowOpen"
|
|
132
133
|
@configure="showConfigModal = true">
|
|
133
134
|
<!-- This `<template v-for>` defines dynamic SLOTS, not a
|
|
@@ -203,6 +204,7 @@
|
|
|
203
204
|
v-bind="{ ...$attrs, ...resolvedProps }"
|
|
204
205
|
@view="onRowOpen"
|
|
205
206
|
@rowClick="onRowOpen"
|
|
207
|
+
@rowAuxClick="onRowOpen"
|
|
206
208
|
@editOpen="onRowOpen"
|
|
207
209
|
@configure="showConfigModal = true">
|
|
208
210
|
<!-- Dynamic slot definition, not a rendered list — see the note on
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
<script>
|
|
42
42
|
import { NcActionButton, NcActionLink, NcActions } from '@nextcloud/vue'
|
|
43
43
|
import { followItemActionLink, resolveItemActionLink } from '../../utils/actionLink.js'
|
|
44
|
+
import { isModifiedClick } from '../../utils/linkNavigation.js'
|
|
44
45
|
import { evaluateVisibleWhenLocal, isLocallyDecidableVisibleWhen } from '../../utils/visibleWhen.js'
|
|
45
46
|
import { CnIcon } from '../CnIcon/index.js'
|
|
46
47
|
|
|
@@ -215,16 +216,20 @@ export default {
|
|
|
215
216
|
/**
|
|
216
217
|
* A link entry was clicked: route a plain in-app click, leave the rest to
|
|
217
218
|
* the browser, and emit `action` as a button would. The `handler` is not
|
|
218
|
-
* called, since navigating is what the link already does.
|
|
219
|
+
* called, since navigating is what the link already does. A modified
|
|
220
|
+
* click opens a new tab, so nothing is emitted in this one.
|
|
219
221
|
*
|
|
220
222
|
* @param {object} action The action definition.
|
|
221
223
|
* @param {object} link The resolved link.
|
|
222
224
|
* @param {MouseEvent} event The click event.
|
|
223
225
|
*/
|
|
224
226
|
onLinkAction(action, link, event) {
|
|
227
|
+
if (isModifiedClick(event)) {
|
|
228
|
+
return
|
|
229
|
+
}
|
|
225
230
|
followItemActionLink(event, link, this.$router)
|
|
226
231
|
/**
|
|
227
|
-
* @event action User picked an entry. Payload: the action's label and the row. A button entry has already run its `handler`; a link entry navigates instead.
|
|
232
|
+
* @event action User picked an entry. Payload: the action's label and the row. A button entry has already run its `handler`; a link entry navigates instead, and a modified (new-tab) click on a link emits nothing.
|
|
228
233
|
* @type {{ action: string, row: object|null }}
|
|
229
234
|
*/
|
|
230
235
|
this.$emit('action', { action: action.label, row: this.row })
|
|
@@ -133,7 +133,7 @@
|
|
|
133
133
|
v-for="item in summaryItems"
|
|
134
134
|
:key="item.id"
|
|
135
135
|
class="cn-setup-summary__item"
|
|
136
|
-
:class="{ 'cn-setup-summary__item--done': item.done }">
|
|
136
|
+
:class="{ 'cn-setup-summary__item--done': item.done, 'cn-setup-summary__item--not-run': item.notRun }">
|
|
137
137
|
<span class="cn-setup-summary__mark">{{ item.done ? '✓' : '○' }}</span>
|
|
138
138
|
<span class="cn-setup-summary__label">{{ item.title }}</span>
|
|
139
139
|
<span v-if="item.value" class="cn-setup-summary__value">{{ item.value }}</span>
|
|
@@ -380,7 +380,14 @@ export default {
|
|
|
380
380
|
* a done marker (info steps always done; choice steps done when picked;
|
|
381
381
|
* run-action steps done when the action succeeded).
|
|
382
382
|
*
|
|
383
|
-
*
|
|
383
|
+
* An on-demand step (`onDemand: true`) is the exception: it is done
|
|
384
|
+
* only when its action succeeded in THIS session. Apps report such a
|
|
385
|
+
* step as `done` on purpose so it never auto-runs (learniq's "Remove
|
|
386
|
+
* the example data"), and reading that flag here ticked a removal
|
|
387
|
+
* nobody ran. When it did not run it gets `notRun: true` and the value
|
|
388
|
+
* "Not run" instead.
|
|
389
|
+
*
|
|
390
|
+
* @return {Array<{ id: string, title: string, value: string, done: boolean, notRun: boolean }>}
|
|
384
391
|
*/
|
|
385
392
|
summaryItems() {
|
|
386
393
|
return this.setupSteps
|
|
@@ -399,6 +406,16 @@ export default {
|
|
|
399
406
|
.map((f) => `${f.label}: ${this.configModel[f.key] !== null && this.configModel[f.key] !== undefined ? this.configModel[f.key] : ''}`)
|
|
400
407
|
.join(', ')
|
|
401
408
|
}
|
|
409
|
+
if (this.isOnDemand(step)) {
|
|
410
|
+
const ran = this.ranThisSession(step.id)
|
|
411
|
+
return {
|
|
412
|
+
id: step.id,
|
|
413
|
+
title: this.stepTitle(step),
|
|
414
|
+
value: ran ? '' : t('nextcloud-vue', 'Not run'),
|
|
415
|
+
done: ran,
|
|
416
|
+
notRun: !ran,
|
|
417
|
+
}
|
|
418
|
+
}
|
|
402
419
|
let done
|
|
403
420
|
if (step.type === 'info') {
|
|
404
421
|
done = true
|
|
@@ -407,7 +424,7 @@ export default {
|
|
|
407
424
|
} else {
|
|
408
425
|
done = this.isStepDone(step.id)
|
|
409
426
|
}
|
|
410
|
-
return { id: step.id, title: this.stepTitle(step), value, done }
|
|
427
|
+
return { id: step.id, title: this.stepTitle(step), value, done, notRun: false }
|
|
411
428
|
})
|
|
412
429
|
},
|
|
413
430
|
|
|
@@ -427,7 +444,9 @@ export default {
|
|
|
427
444
|
if (this.completedStepIds.length === 0) {
|
|
428
445
|
return ''
|
|
429
446
|
}
|
|
430
|
-
|
|
447
|
+
// An on-demand step is never outstanding work, so it is never the
|
|
448
|
+
// step to resume on.
|
|
449
|
+
const actionable = this.setupSteps.filter((s) => s.type !== 'info' && s.type !== 'summary' && !this.isOnDemand(s))
|
|
431
450
|
const firstUnmetIndex = actionable.findIndex((s) => {
|
|
432
451
|
if (s.type === 'choice') {
|
|
433
452
|
return !(this.hasChoice(s) || this.isServerDone(s.id))
|
|
@@ -459,14 +478,16 @@ export default {
|
|
|
459
478
|
* the step has nothing else for the user to decide by then (the
|
|
460
479
|
* choice it acts on was already made on an earlier step), so
|
|
461
480
|
* requiring a click before showing progress just adds a stall.
|
|
462
|
-
* No-ops for any other step type, or one already run/running
|
|
481
|
+
* No-ops for any other step type, or one already run/running, and
|
|
482
|
+
* never fires an on-demand step (`onDemand: true`): those run only
|
|
483
|
+
* when the user clicks Run.
|
|
463
484
|
*
|
|
464
485
|
* @param {string} stepId The step id becoming current.
|
|
465
486
|
* @return {void}
|
|
466
487
|
*/
|
|
467
488
|
maybeAutoRunStep(stepId) {
|
|
468
489
|
const step = this.setupSteps.find((s) => s.id === stepId)
|
|
469
|
-
if (step && step.type === 'run-action' && !this.isStepDone(step.id) && !this.running[step.id]) {
|
|
490
|
+
if (step && step.type === 'run-action' && !this.isOnDemand(step) && !this.isStepDone(step.id) && !this.running[step.id]) {
|
|
470
491
|
this.runAction(step)
|
|
471
492
|
}
|
|
472
493
|
},
|
|
@@ -847,6 +868,29 @@ export default {
|
|
|
847
868
|
}
|
|
848
869
|
},
|
|
849
870
|
|
|
871
|
+
/**
|
|
872
|
+
* Whether a step is marked on-demand (`onDemand: true`): it runs only
|
|
873
|
+
* when the user asks, never automatically, and the summary ticks it
|
|
874
|
+
* only when it ran in this session.
|
|
875
|
+
*
|
|
876
|
+
* @param {object} step The manifest step.
|
|
877
|
+
* @return {boolean}
|
|
878
|
+
*/
|
|
879
|
+
isOnDemand(step) {
|
|
880
|
+
return !!step && step.onDemand === true
|
|
881
|
+
},
|
|
882
|
+
|
|
883
|
+
/**
|
|
884
|
+
* Whether a step's action succeeded in the CURRENT session, ignoring
|
|
885
|
+
* whatever the server reports.
|
|
886
|
+
*
|
|
887
|
+
* @param {string} id Step id.
|
|
888
|
+
* @return {boolean}
|
|
889
|
+
*/
|
|
890
|
+
ranThisSession(id) {
|
|
891
|
+
return !!(this.actionResult[id] && this.actionResult[id].success)
|
|
892
|
+
},
|
|
893
|
+
|
|
850
894
|
isStepDone(id) {
|
|
851
895
|
return this.isServerDone(id) || this.localDone[id] === true || (this.actionResult[id] && this.actionResult[id].success)
|
|
852
896
|
},
|
|
@@ -964,4 +1008,11 @@ export default {
|
|
|
964
1008
|
font-weight: 600;
|
|
965
1009
|
color: var(--color-main-text);
|
|
966
1010
|
}
|
|
1011
|
+
|
|
1012
|
+
/* An on-demand step that did not run says so quietly: it is a choice the
|
|
1013
|
+
user did not make, not a failure. */
|
|
1014
|
+
.cn-setup-summary__item--not-run .cn-setup-summary__value {
|
|
1015
|
+
font-weight: normal;
|
|
1016
|
+
color: var(--color-text-maxcontrast);
|
|
1017
|
+
}
|
|
967
1018
|
</style>
|
|
@@ -145,7 +145,8 @@ const DEBOUNCE_MS = 200
|
|
|
145
145
|
* Data origin (decoupling seam): all data flows through a consumer-supplied
|
|
146
146
|
* `dataSource` prop OR the `cnSpendAnalyticsSource` injection — an object
|
|
147
147
|
* exposing `fetchSummary(args)`, `fetchVendorCommitments(args)`,
|
|
148
|
-
* `fetchNarrative(args)`, and optionally `resolveDeepLink(app, kind, id)
|
|
148
|
+
* `fetchNarrative(args)`, and optionally `resolveDeepLink(app, kind, id)`
|
|
149
|
+
* (pure and synchronous: it runs for every row on every render). The
|
|
149
150
|
* renderer imports NO financeq / procest / launchpad service module and never
|
|
150
151
|
* calls axios itself; the consuming app (typically over GraphQL) owns the
|
|
151
152
|
* transport. The `requires.graphql` hint lives only in the registry entry, not
|
|
@@ -182,8 +183,9 @@ export default {
|
|
|
182
183
|
* Consumer-supplied data source overriding the
|
|
183
184
|
* `cnSpendAnalyticsSource` injection. Must expose `fetchSummary`,
|
|
184
185
|
* `fetchVendorCommitments`, and `fetchNarrative`; may expose
|
|
185
|
-
* `resolveDeepLink
|
|
186
|
-
*
|
|
186
|
+
* `resolveDeepLink`, which must be pure and synchronous, since it is
|
|
187
|
+
* called for every row on every render. When `null` the injection
|
|
188
|
+
* (then the empty state) is used.
|
|
187
189
|
*
|
|
188
190
|
* @type {object|null}
|
|
189
191
|
*/
|
|
@@ -467,7 +467,11 @@ export default defineComponent({
|
|
|
467
467
|
}
|
|
468
468
|
|
|
469
469
|
/* A fade over the clipped edge, only on the side that hides tabs. Stops a
|
|
470
|
-
pixel above the bar's rule so the rule keeps its full colour.
|
|
470
|
+
pixel above the bar's rule so the rule keeps its full colour.
|
|
471
|
+
|
|
472
|
+
z-index 2 so the fades outrank a lifted tab: CnTabsWidget puts the open tab
|
|
473
|
+
at 1, and a tie goes to DOM order, which would draw that tab over the start
|
|
474
|
+
fade (it precedes the tabs) while the end fade still covered it. */
|
|
471
475
|
.cn-tabs__strip::before,
|
|
472
476
|
.cn-tabs__strip::after {
|
|
473
477
|
content: '';
|
|
@@ -476,7 +480,7 @@ export default defineComponent({
|
|
|
476
480
|
pointer-events: none;
|
|
477
481
|
position: absolute;
|
|
478
482
|
width: 56px;
|
|
479
|
-
z-index:
|
|
483
|
+
z-index: 2;
|
|
480
484
|
}
|
|
481
485
|
|
|
482
486
|
.cn-tabs__strip::before {
|
|
@@ -513,7 +517,7 @@ export default defineComponent({
|
|
|
513
517
|
padding: 0;
|
|
514
518
|
position: absolute;
|
|
515
519
|
width: 28px;
|
|
516
|
-
z-index:
|
|
520
|
+
z-index: 3;
|
|
517
521
|
}
|
|
518
522
|
|
|
519
523
|
/* Nextcloud's `button:hover` rule would otherwise fill the chevron. */
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
class="cn-tasks-widget__row"
|
|
32
32
|
:data-state="task.state"
|
|
33
33
|
@click="onRowClick(task, $event)"
|
|
34
|
+
@mousedown="preventMiddleClickAutoscroll"
|
|
34
35
|
@auxclick="onRowAuxClick(task, $event)">
|
|
35
36
|
<span class="cn-tasks-widget__body">
|
|
36
37
|
<span class="cn-tasks-widget__name">{{ titleOf(task) }}</span>
|
|
@@ -90,7 +91,7 @@ import { inject, ref } from 'vue'
|
|
|
90
91
|
import { taskDeepLink, taskDueLabel } from '../../composables/indexSources.js'
|
|
91
92
|
import { useEndpointSource } from '../../composables/useEndpointSource.js'
|
|
92
93
|
import { openRowTarget } from '../../utils/linkNavigation.js'
|
|
93
|
-
import { isRowMiddleClick } from '../../utils/rowAuxClick.js'
|
|
94
|
+
import { isRowMiddleClick, preventMiddleClickAutoscroll } from '../../utils/rowAuxClick.js'
|
|
94
95
|
|
|
95
96
|
/**
|
|
96
97
|
* The OpenRegister inbox read (openregister flow-task-entity). One endpoint
|
|
@@ -367,6 +368,8 @@ export default {
|
|
|
367
368
|
},
|
|
368
369
|
|
|
369
370
|
methods: {
|
|
371
|
+
preventMiddleClickAutoscroll,
|
|
372
|
+
|
|
370
373
|
/**
|
|
371
374
|
* Start (or restart) the refetch interval, unless polling is off.
|
|
372
375
|
*
|
package/src/css/table.css
CHANGED
|
@@ -334,6 +334,32 @@
|
|
|
334
334
|
background-color: var(--color-primary-light);
|
|
335
335
|
}
|
|
336
336
|
|
|
337
|
+
/* A row with a route is a real link (see CnDataTable's row link): the anchor
|
|
338
|
+
in its first cell is stretched over the whole row. The row's own controls
|
|
339
|
+
sit above it so they stay usable; keep the list in step with NESTED_CONTROL
|
|
340
|
+
in utils/rowAuxClick.js. */
|
|
341
|
+
.cn-table-row--linked {
|
|
342
|
+
position: relative;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.cn-table-row__link {
|
|
346
|
+
position: absolute;
|
|
347
|
+
inset: 0;
|
|
348
|
+
cursor: pointer;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
.cn-table-row__link:focus-visible {
|
|
352
|
+
outline: 2px solid var(--color-main-text);
|
|
353
|
+
outline-offset: -2px;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
.cn-table-row--linked .cn-table-col--checkbox,
|
|
357
|
+
.cn-table-row--linked .cn-table-col--actions,
|
|
358
|
+
.cn-table-row--linked td :is(a, button, input, select, textarea, label, summary, [role='button'], [role='checkbox'], [role='link'], [contenteditable='true']):not(.cn-table-row__link) {
|
|
359
|
+
position: relative;
|
|
360
|
+
z-index: 1;
|
|
361
|
+
}
|
|
362
|
+
|
|
337
363
|
/* Column Types */
|
|
338
364
|
.cn-table-col--checkbox {
|
|
339
365
|
width: 50px;
|
|
@@ -122,7 +122,7 @@ import ChartPie from 'vue-material-design-icons/ChartPie.vue'
|
|
|
122
122
|
import TableIcon from 'vue-material-design-icons/Table.vue'
|
|
123
123
|
import ViewDashboard from 'vue-material-design-icons/ViewDashboard.vue'
|
|
124
124
|
import CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'
|
|
125
|
-
import { buildHeaders, prefixUrl } from '../../../utils/index.js'
|
|
125
|
+
import { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'
|
|
126
126
|
|
|
127
127
|
const VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']
|
|
128
128
|
const COMPACT_LIMIT = 5
|
|
@@ -287,8 +287,9 @@ export default {
|
|
|
287
287
|
return `cn-analytics-card__badge--type-${type ?? 'unknown'}`
|
|
288
288
|
},
|
|
289
289
|
|
|
290
|
+
// A provider URL is used only when it is a safe scheme; else the app link.
|
|
290
291
|
reportUrl(report) {
|
|
291
|
-
if (report && report.url) {
|
|
292
|
+
if (report && report.url && safeHref(report.url) !== '#') {
|
|
292
293
|
return report.url
|
|
293
294
|
}
|
|
294
295
|
const id = this.reportKey(report)
|
|
@@ -82,8 +82,7 @@
|
|
|
82
82
|
<NcButton
|
|
83
83
|
variant="primary"
|
|
84
84
|
:href="analyticsAppUrl"
|
|
85
|
-
target="_blank"
|
|
86
|
-
rel="noopener noreferrer">
|
|
85
|
+
target="_blank">
|
|
87
86
|
<template #icon>
|
|
88
87
|
<ChartBar :size="20" />
|
|
89
88
|
</template>
|
|
@@ -131,8 +130,7 @@
|
|
|
131
130
|
<NcActionLink
|
|
132
131
|
:closeAfterClick="true"
|
|
133
132
|
:href="reportUrl(report)"
|
|
134
|
-
target="_blank"
|
|
135
|
-
rel="noopener noreferrer">
|
|
133
|
+
target="_blank">
|
|
136
134
|
<template #icon>
|
|
137
135
|
<OpenInNew :size="20" />
|
|
138
136
|
</template>
|
|
@@ -178,7 +176,7 @@ import ViewDashboard from 'vue-material-design-icons/ViewDashboard.vue'
|
|
|
178
176
|
import CnAnalyticsReportCreate from '../../../components/CnAnalyticsReportCreate/CnAnalyticsReportCreate.vue'
|
|
179
177
|
import CnAnalyticsReportPicker from '../../../components/CnAnalyticsReportPicker/CnAnalyticsReportPicker.vue'
|
|
180
178
|
import CnStatusBadge from '../../../components/CnStatusBadge/CnStatusBadge.vue'
|
|
181
|
-
import { buildHeaders, prefixUrl } from '../../../utils/index.js'
|
|
179
|
+
import { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'
|
|
182
180
|
|
|
183
181
|
/**
|
|
184
182
|
* CnAnalyticsTab — bespoke dataset list for the `analytics` integration.
|
|
@@ -408,8 +406,9 @@ export default {
|
|
|
408
406
|
return t('nextcloud-vue', 'Latest value')
|
|
409
407
|
},
|
|
410
408
|
|
|
409
|
+
// A provider URL is used only when it is a safe scheme; else the app link.
|
|
411
410
|
reportUrl(report) {
|
|
412
|
-
if (report && report.url) {
|
|
411
|
+
if (report && report.url && safeHref(report.url) !== '#') {
|
|
413
412
|
return report.url
|
|
414
413
|
}
|
|
415
414
|
const id = this.reportKey(report)
|
|
@@ -34,9 +34,11 @@
|
|
|
34
34
|
@error="onFaviconError(entity)">
|
|
35
35
|
<Bookmark v-else :size="14" />
|
|
36
36
|
<a
|
|
37
|
+
v-if="hasSafeUrl(entity)"
|
|
37
38
|
:href="entity.url"
|
|
38
39
|
target="_blank"
|
|
39
40
|
rel="noopener noreferrer">{{ bookmarkTitle(entity) }}</a>
|
|
41
|
+
<span v-else>{{ bookmarkTitle(entity) }}</span>
|
|
40
42
|
</span>
|
|
41
43
|
<span v-else class="cn-bookmarks-card__empty">{{ emptyLabel }}</span>
|
|
42
44
|
</template>
|
|
@@ -62,9 +64,11 @@
|
|
|
62
64
|
@error="onFaviconError(mostRecent)">
|
|
63
65
|
<Bookmark v-else :size="14" />
|
|
64
66
|
<a
|
|
67
|
+
v-if="hasSafeUrl(mostRecent)"
|
|
65
68
|
:href="mostRecent.url"
|
|
66
69
|
target="_blank"
|
|
67
70
|
rel="noopener noreferrer">{{ bookmarkTitle(mostRecent) }}</a>
|
|
71
|
+
<span v-else>{{ bookmarkTitle(mostRecent) }}</span>
|
|
68
72
|
</div>
|
|
69
73
|
<div v-if="bookmarks.length > 1" class="cn-bookmarks-card__view-all">
|
|
70
74
|
<a
|
|
@@ -99,10 +103,12 @@
|
|
|
99
103
|
</div>
|
|
100
104
|
<div class="cn-bookmarks-card__row-main">
|
|
101
105
|
<a
|
|
106
|
+
v-if="hasSafeUrl(bookmark)"
|
|
102
107
|
:href="bookmark.url"
|
|
103
108
|
target="_blank"
|
|
104
109
|
rel="noopener noreferrer"
|
|
105
110
|
class="cn-bookmarks-card__title">{{ bookmarkTitle(bookmark) }}</a>
|
|
111
|
+
<span v-else class="cn-bookmarks-card__title">{{ bookmarkTitle(bookmark) }}</span>
|
|
106
112
|
</div>
|
|
107
113
|
</li>
|
|
108
114
|
</ul>
|
|
@@ -121,7 +127,7 @@ import { translatePlural as n, translate as t } from '@nextcloud/l10n'
|
|
|
121
127
|
import { NcLoadingIcon } from '@nextcloud/vue'
|
|
122
128
|
import Bookmark from 'vue-material-design-icons/Bookmark.vue'
|
|
123
129
|
import CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'
|
|
124
|
-
import { buildHeaders, prefixUrl } from '../../../utils/index.js'
|
|
130
|
+
import { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'
|
|
125
131
|
|
|
126
132
|
const VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']
|
|
127
133
|
const COMPACT_LIMIT = 5
|
|
@@ -245,6 +251,11 @@ export default {
|
|
|
245
251
|
return bookmark.title || bookmark.url || ''
|
|
246
252
|
},
|
|
247
253
|
|
|
254
|
+
// Bookmark URLs are user-entered (bookmarklets are `javascript:`), so only a safe one is a link.
|
|
255
|
+
hasSafeUrl(bookmark) {
|
|
256
|
+
return safeHref(bookmark.url) !== '#'
|
|
257
|
+
},
|
|
258
|
+
|
|
248
259
|
chipSubtitle(bookmark) {
|
|
249
260
|
return bookmark.description || bookmark.url || this.bookmarkTitle(bookmark)
|
|
250
261
|
},
|
|
@@ -57,8 +57,7 @@
|
|
|
57
57
|
<NcButton
|
|
58
58
|
variant="primary"
|
|
59
59
|
:href="bookmarksAppUrl"
|
|
60
|
-
target="_blank"
|
|
61
|
-
rel="noopener noreferrer">
|
|
60
|
+
target="_blank">
|
|
62
61
|
<template #icon>
|
|
63
62
|
<Bookmark :size="20" />
|
|
64
63
|
</template>
|
|
@@ -92,8 +91,9 @@
|
|
|
92
91
|
:name="bookmarkTitle(bookmark)"
|
|
93
92
|
:bold="true"
|
|
94
93
|
:href="bookmarkHref(bookmark)"
|
|
95
|
-
target="_blank"
|
|
96
|
-
:forceDisplayActions="true"
|
|
94
|
+
:target="hasSafeUrl(bookmark) ? '_blank' : undefined"
|
|
95
|
+
:forceDisplayActions="true"
|
|
96
|
+
@click="onBookmarkClick(bookmark, $event)">
|
|
97
97
|
<template #icon>
|
|
98
98
|
<span class="cn-bookmarks-tab__row-icon">
|
|
99
99
|
<Bookmark :size="20" />
|
|
@@ -121,10 +121,10 @@
|
|
|
121
121
|
</template>
|
|
122
122
|
<template #actions>
|
|
123
123
|
<NcActionLink
|
|
124
|
+
v-if="hasSafeUrl(bookmark)"
|
|
124
125
|
:closeAfterClick="true"
|
|
125
126
|
:href="bookmarkHref(bookmark)"
|
|
126
|
-
target="_blank"
|
|
127
|
-
rel="noopener noreferrer">
|
|
127
|
+
target="_blank">
|
|
128
128
|
<template #icon>
|
|
129
129
|
<OpenInNew :size="20" />
|
|
130
130
|
</template>
|
|
@@ -393,6 +393,23 @@ export default {
|
|
|
393
393
|
return safeHref(bookmark.url)
|
|
394
394
|
},
|
|
395
395
|
|
|
396
|
+
/**
|
|
397
|
+
* Whether the bookmark's URL passed `safeHref`, so its row is a link.
|
|
398
|
+
*
|
|
399
|
+
* @param {object} bookmark The bookmark row.
|
|
400
|
+
* @return {boolean}
|
|
401
|
+
*/
|
|
402
|
+
hasSafeUrl(bookmark) {
|
|
403
|
+
return this.bookmarkHref(bookmark) !== '#'
|
|
404
|
+
},
|
|
405
|
+
|
|
406
|
+
// NcListItem always renders an anchor; one without a safe URL must not follow `#`.
|
|
407
|
+
onBookmarkClick(bookmark, event) {
|
|
408
|
+
if (!this.hasSafeUrl(bookmark)) {
|
|
409
|
+
event?.preventDefault?.()
|
|
410
|
+
}
|
|
411
|
+
},
|
|
412
|
+
|
|
396
413
|
bookmarkTags(bookmark) {
|
|
397
414
|
const tags = bookmark.tags
|
|
398
415
|
if (!Array.isArray(tags)) {
|
|
@@ -78,7 +78,7 @@ describe('CnBookmarksTab', () => {
|
|
|
78
78
|
expect(openLink.attributes('target')).toBe('_blank')
|
|
79
79
|
})
|
|
80
80
|
|
|
81
|
-
it('
|
|
81
|
+
it('renders a bookmark with an unsafe URL as an inert row with no Open action', async () => {
|
|
82
82
|
global.fetch = jest.fn().mockResolvedValueOnce({
|
|
83
83
|
ok: true,
|
|
84
84
|
status: 200,
|
|
@@ -90,7 +90,12 @@ describe('CnBookmarksTab', () => {
|
|
|
90
90
|
await flushPromises()
|
|
91
91
|
const row = wrapper.find('.cn-bookmarks-tab__row')
|
|
92
92
|
expect(row.attributes('href')).toBe('#')
|
|
93
|
-
expect(row.
|
|
93
|
+
expect(row.attributes('target')).toBeUndefined()
|
|
94
|
+
expect(row.find('.stub.NcActionLink').exists()).toBe(false)
|
|
95
|
+
|
|
96
|
+
const event = { preventDefault: jest.fn() }
|
|
97
|
+
wrapper.vm.onBookmarkClick(wrapper.vm.filteredBookmarks[0], event)
|
|
98
|
+
expect(event.preventDefault).toHaveBeenCalled()
|
|
94
99
|
wrapper.unmount()
|
|
95
100
|
})
|
|
96
101
|
|
|
@@ -126,7 +126,7 @@ import { translatePlural as n, translate as t } from '@nextcloud/l10n'
|
|
|
126
126
|
import { NcLoadingIcon } from '@nextcloud/vue'
|
|
127
127
|
import BookOpenPageVariant from 'vue-material-design-icons/BookOpenPageVariant.vue'
|
|
128
128
|
import CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'
|
|
129
|
-
import { buildHeaders, prefixUrl } from '../../../utils/index.js'
|
|
129
|
+
import { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'
|
|
130
130
|
import { stripMarker } from '../../utils/marker.js'
|
|
131
131
|
|
|
132
132
|
const VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']
|
|
@@ -263,8 +263,9 @@ export default {
|
|
|
263
263
|
return typeof emoji === 'string' ? emoji.trim() : ''
|
|
264
264
|
},
|
|
265
265
|
|
|
266
|
+
// A provider URL is used only when it is a safe scheme; else the app link.
|
|
266
267
|
pageUrl(page) {
|
|
267
|
-
if (typeof page.url === 'string' && page.url !== '') {
|
|
268
|
+
if (typeof page.url === 'string' && page.url !== '' && safeHref(page.url) !== '#') {
|
|
268
269
|
return page.url
|
|
269
270
|
}
|
|
270
271
|
const d = this.dataOf(page)
|
|
@@ -72,8 +72,7 @@
|
|
|
72
72
|
<NcButton
|
|
73
73
|
variant="primary"
|
|
74
74
|
:href="collectivesAppUrl"
|
|
75
|
-
target="_blank"
|
|
76
|
-
rel="noopener noreferrer">
|
|
75
|
+
target="_blank">
|
|
77
76
|
<template #icon>
|
|
78
77
|
<BookOpenPageVariant :size="20" />
|
|
79
78
|
</template>
|
|
@@ -122,8 +121,7 @@
|
|
|
122
121
|
<NcActionLink
|
|
123
122
|
:closeAfterClick="true"
|
|
124
123
|
:href="pageUrl(page)"
|
|
125
|
-
target="_blank"
|
|
126
|
-
rel="noopener noreferrer">
|
|
124
|
+
target="_blank">
|
|
127
125
|
<template #icon>
|
|
128
126
|
<OpenInNew :size="20" />
|
|
129
127
|
</template>
|
|
@@ -168,7 +166,7 @@ import OpenInNew from 'vue-material-design-icons/OpenInNew.vue'
|
|
|
168
166
|
import Plus from 'vue-material-design-icons/Plus.vue'
|
|
169
167
|
import CnCollectivePageCreate from '../../../components/CnCollectivePageCreate/CnCollectivePageCreate.vue'
|
|
170
168
|
import CnCollectivePagePicker from '../../../components/CnCollectivePagePicker/CnCollectivePagePicker.vue'
|
|
171
|
-
import { buildHeaders, prefixUrl } from '../../../utils/index.js'
|
|
169
|
+
import { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'
|
|
172
170
|
import { stripMarker } from '../../utils/marker.js'
|
|
173
171
|
|
|
174
172
|
const MS_PER_DAY = 24 * 60 * 60 * 1000
|
|
@@ -333,8 +331,9 @@ export default {
|
|
|
333
331
|
return text
|
|
334
332
|
},
|
|
335
333
|
|
|
334
|
+
// A provider URL is used only when it is a safe scheme; else the app link.
|
|
336
335
|
pageUrl(page) {
|
|
337
|
-
if (typeof page.url === 'string' && page.url !== '') {
|
|
336
|
+
if (typeof page.url === 'string' && page.url !== '' && safeHref(page.url) !== '#') {
|
|
338
337
|
return page.url
|
|
339
338
|
}
|
|
340
339
|
const d = this.dataOf(page)
|
|
@@ -108,7 +108,7 @@ import { translatePlural as n, translate as t } from '@nextcloud/l10n'
|
|
|
108
108
|
import { NcLoadingIcon } from '@nextcloud/vue'
|
|
109
109
|
import CurrencyEur from 'vue-material-design-icons/CurrencyEur.vue'
|
|
110
110
|
import CnDetailCard from '../../../components/CnDetailCard/CnDetailCard.vue'
|
|
111
|
-
import { buildHeaders, prefixUrl } from '../../../utils/index.js'
|
|
111
|
+
import { buildHeaders, prefixUrl, safeHref } from '../../../utils/index.js'
|
|
112
112
|
|
|
113
113
|
const VALID_SURFACES = ['user-dashboard', 'app-dashboard', 'detail-page', 'single-entity']
|
|
114
114
|
|
|
@@ -250,7 +250,7 @@ export default {
|
|
|
250
250
|
},
|
|
251
251
|
|
|
252
252
|
rowUrl(row) {
|
|
253
|
-
if (row.url) {
|
|
253
|
+
if (row.url && safeHref(row.url) !== '#') {
|
|
254
254
|
return row.url
|
|
255
255
|
}
|
|
256
256
|
const projectId = row.projectId ?? row.project_id ?? row.data?.projectid ?? row.data?.project_id ?? null
|