@conduction/nextcloud-vue 2.58.0 → 2.60.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +14 -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/CnAppNav/CnAppNav.vue2.js +39 -0
- package/dist/esm/components/CnAppNav/CnAppNav.vue2.js.map +1 -1
- package/dist/esm/components/CnAppNav/CnAppNav.vue3.js +46 -4
- package/dist/esm/components/CnAppNav/CnAppNav.vue3.js.map +1 -1
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js +224 -9
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js +79 -25
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnBannerWidgetForm/CnBannerWidgetForm.vue2.js +42 -3
- package/dist/esm/components/CnBannerWidgetForm/CnBannerWidgetForm.vue2.js.map +1 -1
- package/dist/esm/components/CnBannerWidgetForm/CnBannerWidgetForm.vue3.js +39 -6
- package/dist/esm/components/CnBannerWidgetForm/CnBannerWidgetForm.vue3.js.map +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue2.js +58 -5
- package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue3.js +13 -5
- package/dist/esm/components/CnBoardView/CnBoardView.vue3.js.map +1 -1
- package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue.js +10 -0
- package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue.js.map +1 -0
- package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue2.js +39 -0
- package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue2.js.map +1 -0
- package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue3.js +16 -0
- package/dist/esm/components/CnBrandStripe/CnBrandStripe.vue3.js.map +1 -0
- 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/CnCellRenderer/CnCellRenderer.vue2.js +117 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js +147 -89
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.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/CnConversationThread/CnConversationThread.vue.js +10 -0
- package/dist/esm/components/CnConversationThread/CnConversationThread.vue.js.map +1 -0
- package/dist/esm/components/CnConversationThread/CnConversationThread.vue2.js +259 -0
- package/dist/esm/components/CnConversationThread/CnConversationThread.vue2.js.map +1 -0
- package/dist/esm/components/CnConversationThread/CnConversationThread.vue3.js +131 -0
- package/dist/esm/components/CnConversationThread/CnConversationThread.vue3.js.map +1 -0
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue.js +10 -0
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue.js.map +1 -0
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js +116 -0
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue3.js +24 -0
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue3.js.map +1 -0
- 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 +532 -29
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +300 -142
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewList.vue.js +10 -0
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewList.vue.js.map +1 -0
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewList.vue2.js +237 -0
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewList.vue2.js.map +1 -0
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewList.vue3.js +120 -0
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewList.vue3.js.map +1 -0
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue.js +10 -0
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue.js.map +1 -0
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue2.js +77 -0
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue3.js +27 -0
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue3.js.map +1 -0
- 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/CnHeaderWidget/CnHeaderWidget.vue2.js +169 -12
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue3.js +15 -7
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnHeaderWidgetForm/CnHeaderWidgetForm.vue2.js +37 -3
- package/dist/esm/components/CnHeaderWidgetForm/CnHeaderWidgetForm.vue2.js.map +1 -1
- package/dist/esm/components/CnHeaderWidgetForm/CnHeaderWidgetForm.vue3.js +44 -16
- package/dist/esm/components/CnHeaderWidgetForm/CnHeaderWidgetForm.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +199 -16
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +20 -10
- 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/CnNextStepCard/CnNextStepCard.vue.js +10 -0
- package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue.js.map +1 -0
- package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue2.js +150 -0
- package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue2.js.map +1 -0
- package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue3.js +106 -0
- package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue3.js.map +1 -0
- package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue.js +9 -0
- package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue.js.map +1 -0
- package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue2.js +47 -0
- package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue3.js +17 -0
- package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue3.js.map +1 -0
- 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/CnObjectDataWidget/CnObjectDataWidget.vue2.js +49 -0
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectDataWidget/CnObjectDataWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectDataWidgetForm/CnObjectDataWidgetForm.vue2.js +6 -0
- package/dist/esm/components/CnObjectDataWidgetForm/CnObjectDataWidgetForm.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectDataWidgetForm/CnObjectDataWidgetForm.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectKanban/CnObjectKanban.vue2.js +46 -0
- package/dist/esm/components/CnObjectKanban/CnObjectKanban.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectKanban/CnObjectKanban.vue3.js +15 -3
- package/dist/esm/components/CnObjectKanban/CnObjectKanban.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/CnQuickFilterBar/CnQuickFilterBar.vue2.js +37 -1
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue2.js.map +1 -1
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue3.js +29 -13
- package/dist/esm/components/CnQuickFilterBar/CnQuickFilterBar.vue3.js.map +1 -1
- package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue2.js +100 -3
- package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.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/CnSavedViewsControl/CnSavedViewsControl.vue2.js +50 -3
- package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue2.js.map +1 -1
- package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue3.js.map +1 -1
- package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue.js +10 -0
- package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue.js.map +1 -0
- package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue2.js +186 -0
- package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue2.js.map +1 -0
- package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue3.js +46 -0
- package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue3.js.map +1 -0
- 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/CnStackedBarWidget/CnStackedBarWidget.vue.js +11 -0
- package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue.js.map +1 -0
- package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue2.js +298 -0
- package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue3.js +77 -0
- package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue.js +10 -0
- package/dist/esm/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue.js.map +1 -0
- package/dist/esm/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue2.js +169 -0
- package/dist/esm/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue2.js.map +1 -0
- package/dist/esm/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue3.js +48 -0
- package/dist/esm/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue3.js.map +1 -0
- package/dist/esm/components/CnTabs/CnTab.vue2.js +35 -0
- package/dist/esm/components/CnTabs/CnTab.vue2.js.map +1 -1
- package/dist/esm/components/CnTabs/CnTab.vue3.js.map +1 -1
- package/dist/esm/components/CnTabs/CnTabs.vue2.js +120 -7
- package/dist/esm/components/CnTabs/CnTabs.vue2.js.map +1 -1
- package/dist/esm/components/CnTabs/CnTabs.vue3.js +57 -5
- package/dist/esm/components/CnTabs/CnTabs.vue3.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +59 -4
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +6 -2
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.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/components/CnWeekStripWidget/CnWeekStripWidget.vue.js +10 -0
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue.js.map +1 -0
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue2.js +484 -0
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue3.js +115 -0
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue.js +10 -0
- package/dist/esm/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue.js.map +1 -0
- package/dist/esm/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue2.js +189 -0
- package/dist/esm/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue2.js.map +1 -0
- package/dist/esm/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue3.js +74 -0
- package/dist/esm/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue3.js.map +1 -0
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +82 -8
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
- package/dist/esm/index.js +10 -0
- package/dist/esm/index.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 +55 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +55 -1
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +998 -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/dateVariant.js +124 -0
- package/dist/esm/utils/dateVariant.js.map +1 -0
- package/dist/esm/utils/detailActionModel.js +260 -0
- package/dist/esm/utils/detailActionModel.js.map +1 -0
- package/dist/esm/utils/dueRule.js +84 -0
- package/dist/esm/utils/dueRule.js.map +1 -0
- package/dist/esm/utils/fetchAggregate.js +37 -1
- package/dist/esm/utils/fetchAggregate.js.map +1 -1
- package/dist/esm/utils/fetchFilterCounts.js +186 -0
- package/dist/esm/utils/fetchFilterCounts.js.map +1 -0
- package/dist/esm/utils/libraryWidgetKeys.js +5 -0
- package/dist/esm/utils/libraryWidgetKeys.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/readPath.js +30 -0
- package/dist/esm/utils/readPath.js.map +1 -0
- 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 +15855 -9353
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +998 -7
- package/l10n/en.json +55 -1
- package/l10n/nl.json +55 -1
- package/package.json +1 -1
- package/scripts/.jsdoc-baselines.json +3 -0
- package/src/components/CnActionsBar/CnActionsBar.vue +14 -5
- package/src/components/CnActionsMenu/CnActionsMenu.vue +14 -4
- package/src/components/CnAdminSettingsShell/CnAdminSettingsShell.vue +1 -2
- package/src/components/CnAppNav/CnAppNav.md +26 -0
- package/src/components/CnAppNav/CnAppNav.vue +104 -1
- package/src/components/CnBannerWidget/CnBannerWidget.vue +365 -10
- package/src/components/CnBannerWidgetForm/CnBannerWidgetForm.vue +65 -3
- package/src/components/CnBoardView/CnBoardView.vue +90 -5
- package/src/components/CnBrandStripe/CnBrandStripe.md +22 -0
- package/src/components/CnBrandStripe/CnBrandStripe.vue +100 -0
- package/src/components/CnBrandStripe/index.js +4 -0
- package/src/components/CnCardGrid/CnCardGrid.vue +9 -1
- package/src/components/CnCellRenderer/CnCellRenderer.vue +207 -1
- package/src/components/CnContextMenu/CnContextMenu.vue +6 -1
- package/src/components/CnConversationThread/CnConversationThread.md +51 -0
- package/src/components/CnConversationThread/CnConversationThread.vue +457 -0
- package/src/components/CnConversationThread/CnConversationWidget.vue +144 -0
- package/src/components/CnConversationThread/index.js +4 -0
- 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 +17 -1
- package/src/components/CnDetailPage/CnDetailPage.vue +712 -49
- package/src/components/CnDocumentReviewList/CnDocumentReviewList.md +55 -0
- package/src/components/CnDocumentReviewList/CnDocumentReviewList.vue +384 -0
- package/src/components/CnDocumentReviewList/CnDocumentReviewWidget.vue +108 -0
- package/src/components/CnDocumentReviewList/index.js +4 -0
- 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/CnHeaderWidget/CnHeaderWidget.vue +189 -14
- package/src/components/CnHeaderWidgetForm/CnHeaderWidgetForm.vue +53 -3
- package/src/components/CnIndexPage/CnIndexPage.vue +210 -17
- 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/CnNextStepCard/CnNextStepCard.md +40 -0
- package/src/components/CnNextStepCard/CnNextStepCard.vue +319 -0
- package/src/components/CnNextStepCard/CnNextStepWidget.vue +59 -0
- package/src/components/CnNextStepCard/index.js +4 -0
- package/src/components/CnObjectCard/CnObjectCard.vue +24 -6
- package/src/components/CnObjectDataWidget/CnObjectDataWidget.vue +49 -0
- package/src/components/CnObjectDataWidgetForm/CnObjectDataWidgetForm.vue +6 -0
- package/src/components/CnObjectKanban/CnObjectKanban.vue +86 -2
- 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/CnQuickFilterBar/CnQuickFilterBar.md +3 -0
- package/src/components/CnQuickFilterBar/CnQuickFilterBar.vue +60 -1
- package/src/components/CnRelatedObjectsWidget/CnRelatedObjectsWidget.vue +100 -3
- package/src/components/CnRowActions/CnRowActions.vue +7 -2
- package/src/components/CnSavedViewsControl/CnSavedViewsControl.vue +50 -3
- package/src/components/CnSegmentedControl/CnSegmentedControl.md +46 -0
- package/src/components/CnSegmentedControl/CnSegmentedControl.vue +291 -0
- package/src/components/CnSegmentedControl/index.js +4 -0
- package/src/components/CnSpendAnalyticsWidget/CnSpendAnalyticsWidget.vue +5 -3
- package/src/components/CnStackedBarWidget/CnStackedBarWidget.md +15 -0
- package/src/components/CnStackedBarWidget/CnStackedBarWidget.vue +414 -0
- package/src/components/CnStackedBarWidget/index.js +14 -0
- package/src/components/CnStackedBarWidgetForm/CnStackedBarWidgetForm.vue +224 -0
- package/src/components/CnStackedBarWidgetForm/index.js +4 -0
- package/src/components/CnTabs/CnTab.vue +35 -0
- package/src/components/CnTabs/CnTabs.vue +251 -13
- package/src/components/CnTabsWidget/CnTabsWidget.vue +63 -4
- package/src/components/CnTasksWidget/CnTasksWidget.vue +4 -1
- package/src/components/CnWeekStripWidget/CnWeekStripWidget.md +31 -0
- package/src/components/CnWeekStripWidget/CnWeekStripWidget.vue +668 -0
- package/src/components/CnWeekStripWidget/index.js +14 -0
- package/src/components/CnWeekStripWidgetForm/CnWeekStripWidgetForm.vue +265 -0
- package/src/components/CnWeekStripWidgetForm/index.js +4 -0
- package/src/components/CnWidgetGrid/registerDashboardWidgets.js +74 -0
- package/src/components/index.js +11 -0
- package/src/css/detail-page.css +47 -0
- package/src/css/table.css +26 -0
- package/src/index.js +9 -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 +389 -1
- package/src/schemas/app-manifest.schema.json +2 -1
- package/src/utils/actionLink.js +5 -0
- package/src/utils/actionsDispatcher.js +15 -0
- package/src/utils/dateVariant.js +120 -0
- package/src/utils/detailActionModel.js +256 -0
- package/src/utils/dueRule.js +94 -0
- package/src/utils/fetchAggregate.js +36 -0
- package/src/utils/fetchFilterCounts.js +182 -0
- package/src/utils/index.js +1 -0
- package/src/utils/libraryWidgetKeys.js +5 -0
- package/src/utils/linkNavigation.js +6 -3
- package/src/utils/readPath.js +27 -0
- package/src/utils/rowAuxClick.js +29 -4
- package/src/utils/safeHref.js +18 -3
- package/src/utils/validateManifestV2.compiled.js +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnCellRenderer.vue3.js","sources":["../../../../src/components/CnCellRenderer/CnCellRenderer.vue?vue&type=template&id=37d7b88f&lang.js"],"sourcesContent":["<template>\n\t<span class=\"cn-cell-renderer\" :class=\"cellClass\">\n\t\t<!-- Consumer-registered cell widget (cnCellWidgets[column.widget]) -->\n\t\t<component\n\t\t\t:is=\"widgetComponent\"\n\t\t\tv-if=\"widgetComponent\"\n\t\t\t:value=\"value\"\n\t\t\t:row=\"row\"\n\t\t\t:property=\"property\"\n\t\t\t:formatted=\"formattedValue\"\n\t\t\tv-bind=\"widgetProps\" />\n\n\t\t<!-- Built-in \"badge\" widget — renders the (possibly formatter-shaped) value as a status pill -->\n\t\t<template v-else-if=\"widget === 'badge'\">\n\t\t\t<CnStatusBadge v-if=\"hasValue\"\n\t\t\t\t:label=\"String(formattedValue)\"\n\t\t\t\t:variant=\"badgeVariant\"\n\t\t\t\t:colorMap=\"badgeColorMap\" />\n\t\t\t<span v-else class=\"cn-cell-renderer__dash\">—</span>\n\t\t</template>\n\n\t\t<!-- Built-in \"fkResolve\" widget — resolves a reference uuid to the related\n\t\t object's display label via the object store (per-schema caching).\n\t\t Config: widgetProps { register, schema, labelField }. -->\n\t\t<template v-else-if=\"widget === 'fkResolve'\">\n\t\t\t<CnFkResolveCell\n\t\t\t\tv-if=\"hasValue\"\n\t\t\t\t:value=\"value\"\n\t\t\t\t:register=\"(widgetProps && widgetProps.register) || ''\"\n\t\t\t\t:schema=\"(widgetProps && widgetProps.schema) || ''\"\n\t\t\t\t:labelField=\"(widgetProps && widgetProps.labelField) || 'name'\" />\n\t\t\t<span v-else class=\"cn-cell-renderer__dash\">—</span>\n\t\t</template>\n\n\t\t<!-- Built-in \"link\" widget — renders the (possibly formatter-shaped) value\n\t\t as a router-link (when widgetProps.route is a manifest page id, or\n\t\t widgetProps.routeMap picks one per row by widgetProps.routeField) or\n\t\t an external anchor (when widgetProps.href is set). Falls back to\n\t\t plain text + a once-per-session console.warn when neither resolves. -->\n\t\t<template v-else-if=\"widget === 'link'\">\n\t\t\t<router-link\n\t\t\t\tv-if=\"linkRoute\"\n\t\t\t\t:to=\"linkRoute\"\n\t\t\t\tclass=\"cn-cell-renderer__link\">\n\t\t\t\t{{ formattedValue }}\n\t\t\t</router-link>\n\t\t\t<a\n\t\t\t\tv-else-if=\"linkHref\"\n\t\t\t\t:href=\"linkHref\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\trel=\"noopener\"\n\t\t\t\tclass=\"cn-cell-renderer__link\">\n\t\t\t\t{{ formattedValue }}\n\t\t\t</a>\n\t\t\t<span v-else :title=\"rawTitle\">{{ formattedValue }}</span>\n\t\t</template>\n\n\t\t<!-- Explicit column formatter — overrides the type-aware paths below -->\n\t\t<template v-else-if=\"hasFormatter\">\n\t\t\t<span :title=\"rawTitle\">{{ formattedValue }}</span>\n\t\t</template>\n\n\t\t<!-- Declarative swatch — a colour dot (from a sibling row field) + text -->\n\t\t<template v-else-if=\"isSwatch\">\n\t\t\t<span class=\"cn-cell-renderer__swatch-wrap\">\n\t\t\t\t<span\n\t\t\t\t\tv-if=\"swatchColor\"\n\t\t\t\t\tclass=\"cn-cell-renderer__swatch-dot\"\n\t\t\t\t\t:style=\"{ backgroundColor: swatchColor }\"\n\t\t\t\t\taria-hidden=\"true\" />\n\t\t\t\t<span v-if=\"hasValue\" :title=\"rawTitle\">{{ formattedValue }}</span>\n\t\t\t\t<span v-else class=\"cn-cell-renderer__dash\">—</span>\n\t\t\t</span>\n\t\t</template>\n\n\t\t<!-- Declarative built-in format (currency / duration / number / percent) -->\n\t\t<template v-else-if=\"hasBuiltinFormat\">\n\t\t\t<span :title=\"rawTitle\" class=\"cn-cell-renderer--number\">{{ formattedValue }}</span>\n\t\t</template>\n\n\t\t<!-- Date / date-time: dynamic NcDateTime (relative time, absolute on hover) -->\n\t\t<template v-else-if=\"isDate\">\n\t\t\t<NcDateTime v-if=\"dateTimestamp\" :timestamp=\"dateTimestamp\" />\n\t\t\t<span v-else class=\"cn-cell-renderer__dash\">—</span>\n\t\t</template>\n\n\t\t<!-- URI / URL: external link -->\n\t\t<template v-else-if=\"isUri\">\n\t\t\t<a\n\t\t\t\tv-if=\"uriHref\"\n\t\t\t\t:href=\"uriHref\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\trel=\"noopener\"\n\t\t\t\tclass=\"cn-cell-renderer__link\">\n\t\t\t\t{{ formattedValue }}\n\t\t\t</a>\n\t\t\t<span v-else-if=\"hasValue\" :title=\"rawTitle\">{{ formattedValue }}</span>\n\t\t\t<span v-else class=\"cn-cell-renderer__dash\">—</span>\n\t\t</template>\n\n\t\t<!-- Boolean: icon -->\n\t\t<template v-else-if=\"propertyType === 'boolean'\">\n\t\t\t<CheckBold v-if=\"value\" :size=\"16\" class=\"cn-cell-renderer__icon cn-cell-renderer__icon--success\" />\n\t\t\t<span v-else class=\"cn-cell-renderer__dash\">—</span>\n\t\t</template>\n\n\t\t<!-- Enum: status badge (auto-coloured from the property's optional colorMap) -->\n\t\t<template v-else-if=\"isEnum\">\n\t\t\t<CnStatusBadge v-if=\"value\"\n\t\t\t\t:label=\"enumLabel\"\n\t\t\t\t:colorKey=\"String(value)\"\n\t\t\t\t:colorMap=\"enumColorMap\" />\n\t\t\t<span v-else class=\"cn-cell-renderer__dash\">—</span>\n\t\t</template>\n\n\t\t<!-- Array: comma-joined or count -->\n\t\t<template v-else-if=\"propertyType === 'array'\">\n\t\t\t<span v-if=\"!value || !value.length\" class=\"cn-cell-renderer__dash\">—</span>\n\t\t\t<span v-else :title=\"Array.isArray(value) ? value.join(', ') : ''\">\n\t\t\t\t{{ formattedValue }}\n\t\t\t</span>\n\t\t</template>\n\n\t\t<!-- Default: formatted text -->\n\t\t<template v-else>\n\t\t\t<span :title=\"rawTitle\">{{ formattedValue }}</span>\n\t\t</template>\n\t</span>\n</template>\n\n<script>\nimport { NcDateTime } from '@nextcloud/vue'\nimport CheckBold from 'vue-material-design-icons/CheckBold.vue'\nimport CnFkResolveCell from '../CnFkResolveCell/CnFkResolveCell.vue'\nimport { safeCurrencyCode } from '../../utils/formatMetric.js'\nimport { safeHref } from '../../utils/safeHref.js'\nimport { formatValue } from '../../utils/schema.js'\nimport { CnStatusBadge } from '../CnStatusBadge/index.js'\n\n/**\n * Module-level set of column keys already warned about for a\n * `widget:\"link\"` declaration with no resolvable target — guarantees\n * one warning per (page, column-key) rather than per row × render.\n */\nconst WARNED_LINK_KEYS = new Set()\n\n/**\n * CnCellRenderer — Type-aware cell renderer for schema-driven tables.\n *\n * Renders a single cell value based on its schema property definition.\n * Booleans render as icons, enums as status badges, dates as formatted strings,\n * and everything else as truncated text via `formatValue()`.\n *\n * ```vue\n * <CnCellRenderer :value=\"row.status\" :property=\"schema.properties.status\" />\n * ```\n */\nexport default {\n\tname: 'CnCellRenderer',\n\n\tcomponents: {\n\t\tCnStatusBadge,\n\t\tCnFkResolveCell,\n\t\tCheckBold,\n\t\tNcDateTime,\n\t},\n\n\tinject: {\n\t\t/**\n\t\t * Cell-formatter registry, provided by CnAppRoot (`cnFormatters`).\n\t\t * Map of formatter-id → `(value, row, property) => string|number`.\n\t\t * Defaults to an empty object so standalone use (no CnAppRoot\n\t\t * ancestor) is unaffected.\n\t\t */\n\t\tcnFormatters: { default: () => ({}) },\n\t\t/**\n\t\t * Cell-widget registry, provided by CnAppRoot (`cnCellWidgets`).\n\t\t * Map of widget-id → Vue component. Resolves a column's `widget` id;\n\t\t * the built-in `\"badge\"` is handled inline (no registry entry needed).\n\t\t * Defaults to an empty object.\n\t\t */\n\t\tcnCellWidgets: { default: () => ({}) },\n\t\t/**\n\t\t * Display-layer translation function, provided by CnAppRoot\n\t\t * (`cnTranslate: this.translate`, bound to the host app's id). Applied\n\t\t * to the enum badge label only — the underlying value is untouched.\n\t\t * Defaults to identity so standalone use is unaffected.\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/** The raw cell value */\n\t\tvalue: {\n\t\t\ttype: [Boolean, String, Number, Array, Object],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Schema property definition: { type, format, enum, items, title } */\n\t\tproperty: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Optional cell-formatter id (e.g. `currency`, `automationTrigger`).\n\t\t * When set and resolvable in the injected `cnFormatters` registry,\n\t\t * the cell renders `cnFormatters[formatter](value, row, property,\n\t\t * formatterOptions)` as text — an explicit override of the type-aware\n\t\t * rendering below.\n\t\t */\n\t\tformatter: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Declarative options map passed as the formatter's fourth argument\n\t\t * (e.g. `{ currency: 'USD' }` for the built-in `currency` formatter,\n\t\t * or `{ negative, zero, positive }` phrases for `conditionalPhrase`).\n\t\t * Undefined-safe: three-argument formatters simply ignore it.\n\t\t */\n\t\tformatterOptions: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Optional cell-widget id (e.g. `badge`, or a consumer-registered\n\t\t * name). When it resolves in `cnCellWidgets` the cell renders that\n\t\t * component with `{ value, row, property, formatted, ...widgetProps }`;\n\t\t * the built-in id `\"badge\"` renders `CnStatusBadge` and the built-in\n\t\t * id `\"fkResolve\"` renders `CnFkResolveCell` (uuid → related object\n\t\t * label, config via `widgetProps { register, schema, labelField }`).\n\t\t * Takes precedence over `formatter`/the type-aware rendering, but the\n\t\t * value handed to the widget is the formatter-shaped `formatted` when\n\t\t * `formatter` is also set.\n\t\t */\n\t\twidget: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Extra props spread onto the resolved cell-widget component. The\n\t\t * built-in `\"link\"` reads its target from here: `route` (a manifest\n\t\t * page id), `params` (route param to row field), `href` (URL with\n\t\t * `{field}` placeholders), and `routeField` + `routeMap` to pick the\n\t\t * page per row from a sibling field (`{ person: 'ContactDetail' }`),\n\t\t * with `route` as the fallback for a value the map does not hold.\n\t\t */\n\t\twidgetProps: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Optional declarative cell-format spec — a no-code alternative to a\n\t\t * registry `formatter`. Recognised `style` values:\n\t\t *\n\t\t * - `'currency'` → `Intl.NumberFormat` currency (e.g. `€ 1.234,56`),\n\t\t * honouring `currency` (ISO code, default `'EUR'`) and `decimals`\n\t\t * (default 2).\n\t\t * - `'number'` / `'percent'` → localized number (percent appends `%`),\n\t\t * honouring `decimals` (default 0).\n\t\t * - `'duration'` → a seconds value rendered compact (`1u 23m`, `45m 10s`,\n\t\t * `12s`); pass `unit: 'milliseconds'` / `'minutes'` / `'hours'` when the\n\t\t * raw value is not in seconds. Sub-second millisecond values render as\n\t\t * `245ms` rather than rounding down to `0s`.\n\t\t * - `'swatch'` → a colour dot read from a sibling row field named by\n\t\t * `colorField` (the value renders as the cell text beside it).\n\t\t *\n\t\t * `prefix` / `suffix` are prepended/appended to the numeric styles.\n\t\t * Resolved AFTER `formatter` / `widget` (those win), but BEFORE the\n\t\t * type-aware rendering, so a manifest column can opt into currency or a\n\t\t * colour swatch without registering a function.\n\t\t *\n\t\t * @type {{style?: 'currency'|'number'|'percent'|'duration'|'swatch', currency?: string, decimals?: number, unit?: 'milliseconds'|'seconds'|'minutes'|'hours', prefix?: string, suffix?: string, colorField?: string}}\n\t\t */\n\t\tformat: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * The full row object — passed so a formatter can be a function of\n\t\t * the whole record (e.g. \"days since `@self.updated`\"), not just\n\t\t * this one cell value.\n\t\t */\n\t\trow: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** Maximum string length before truncation */\n\t\ttruncate: {\n\t\t\ttype: Number,\n\t\t\tdefault: 100,\n\t\t},\n\n\t\t/**\n\t\t * Row identifier field — used by the built-in `widget:\"link\"` when\n\t\t * the manifest doesn't specify an explicit `widgetProps.params`\n\t\t * map. Defaults to `'id'` so router-link param resolution works\n\t\t * with the manifest convention (`/x/:id`).\n\t\t */\n\t\trowKey: {\n\t\t\ttype: String,\n\t\t\tdefault: 'id',\n\t\t},\n\t},\n\n\tcomputed: {\n\t\tpropertyType() {\n\t\t\treturn this.property?.type || (typeof this.value)\n\t\t},\n\n\t\tisEnum() {\n\t\t\treturn !!(this.property?.enum && this.property.enum.length > 0)\n\t\t},\n\n\t\t/**\n\t\t * Badge text for an enum cell. Schema enum values are English source\n\t\t * codes, so a Dutch session otherwise reads `submitted` in a table whose\n\t\t * headers are Dutch. Only the label is translated — the cell's value,\n\t\t * sorting and filtering keep using the raw code.\n\t\t */\n\t\tenumLabel() {\n\t\t\tconst raw = String(this.value)\n\t\t\tconst labels = (this.property && (this.property.enumLabels || this.property['x-enum-labels'])) || {}\n\t\t\treturn this.cnTranslate(labels[raw] || raw)\n\t\t},\n\n\t\t/** True when the property is a date / date-time (rendered via NcDateTime). */\n\t\tisDate() {\n\t\t\treturn this.property?.format === 'date-time' || this.property?.format === 'date'\n\t\t},\n\n\t\t/**\n\t\t * The cell value as a Date for NcDateTime, or `null` when absent or\n\t\t * unparseable (the cell then falls back to a dash / plain text).\n\t\t *\n\t\t * @return {Date|null}\n\t\t */\n\t\tdateTimestamp() {\n\t\t\tif (!this.hasValue) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst date = new Date(this.value)\n\t\t\treturn Number.isNaN(date.getTime()) ? null : date\n\t\t},\n\n\t\t/** True when the property is a URI / URL (rendered as an external link). */\n\t\tisUri() {\n\t\t\treturn this.property?.format === 'uri' || this.property?.format === 'url'\n\t\t},\n\n\t\t/**\n\t\t * Safe external href for a URI/URL cell, or `null` when the value is\n\t\t * absent or fails `safeHref` validation (e.g. an unsafe scheme).\n\t\t *\n\t\t * @return {string|null}\n\t\t */\n\t\turiHref() {\n\t\t\tif (!this.hasValue) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn safeHref(String(this.value))\n\t\t},\n\n\t\t/** True when the cell has a renderable value (not null/undefined/empty string). */\n\t\thasValue() {\n\t\t\treturn this.value !== null && this.value !== undefined && this.value !== ''\n\t\t},\n\n\t\t/**\n\t\t * Resolved cell-widget component for this column, or `null`. A column's\n\t\t * `widget` id resolves against the injected `cnCellWidgets` registry;\n\t\t * the built-in `\"badge\"` is NOT resolved here (handled inline in the\n\t\t * template) so apps can still override `\"badge\"` via the registry.\n\t\t *\n\t\t * @return {import('vue').Component|null}\n\t\t */\n\t\twidgetComponent() {\n\t\t\tif (!this.widget) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst c = this.cnCellWidgets && this.cnCellWidgets[this.widget]\n\t\t\treturn c || null\n\t\t},\n\n\t\t/** Variant for the built-in `badge` widget — `widgetProps.variant` or `'default'`. */\n\t\tbadgeVariant() {\n\t\t\treturn (this.widgetProps && this.widgetProps.variant) || 'default'\n\t\t},\n\n\t\t/**\n\t\t * Color map for the built-in `badge` widget — `widgetProps.colorMap`,\n\t\t * a `{ value: variant }` map (e.g. `{ placed: 'primary', delivered: 'success' }`)\n\t\t * that CnStatusBadge resolves per label. Null when unset (falls back to\n\t\t * `badgeVariant`). Lets a manifest colour a status column without code.\n\t\t *\n\t\t * @return {object|null}\n\t\t */\n\t\tbadgeColorMap() {\n\t\t\treturn (this.widgetProps && this.widgetProps.colorMap) || null\n\t\t},\n\n\t\t/**\n\t\t * Color map for the auto-rendered enum badge — read from the schema\n\t\t * property's optional `colorMap` (or `x-color-map`). Null when unset, so\n\t\t * enum cells stay the default grey unless the schema opts into colours.\n\t\t *\n\t\t * @return {object|null}\n\t\t */\n\t\tenumColorMap() {\n\t\t\treturn (this.property && (this.property.colorMap || this.property['x-color-map'])) || null\n\t\t},\n\n\t\t/**\n\t\t * The manifest page id the built-in `widget:\"link\"` routes this row\n\t\t * to, or null.\n\t\t *\n\t\t * `widgetProps.routeField` names a sibling field on the row, and\n\t\t * `widgetProps.routeMap` maps that field's values to page ids, so one\n\t\t * column can link a person to `ContactDetail` and an organisation to\n\t\t * `OrganisationDetail` off `initiatorType`. A row whose value has no\n\t\t * entry in the map falls back to the fixed `widgetProps.route`, and\n\t\t * without one the cell renders as text. The map is the whole\n\t\t * vocabulary: a row value is only ever used as a KEY into it, never\n\t\t * as a route name itself, so row data cannot send a link to a page\n\t\t * the manifest did not name.\n\t\t *\n\t\t * @return {string|null} The page id, or null when none resolves.\n\t\t */\n\t\tlinkRouteName() {\n\t\t\tconst wp = this.widgetProps || {}\n\t\t\tconst map = wp.routeMap\n\t\t\tif (typeof wp.routeField === 'string' && wp.routeField !== '' && map && typeof map === 'object') {\n\t\t\t\tconst key = this.row ? this.row[wp.routeField] : undefined\n\t\t\t\tif (key !== undefined && key !== null && Object.hasOwn(map, String(key))) {\n\t\t\t\t\tconst mapped = map[String(key)]\n\t\t\t\t\tif (typeof mapped === 'string' && mapped !== '') {\n\t\t\t\t\t\treturn mapped\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn (typeof wp.route === 'string' && wp.route !== '') ? wp.route : null\n\t\t},\n\n\t\t/**\n\t\t * Resolved router-link target for the built-in `widget:\"link\"`. When\n\t\t * a page id resolves (`widgetProps.route`, or the row's entry in\n\t\t * `widgetProps.routeMap`, see `linkRouteName`), returns\n\t\t * `{ name: route, params }`. Param map is `widgetProps.params`\n\t\t * (a map of route-param-name → row-field-name); when omitted,\n\t\t * defaults to `{ id: row[rowKey] }`. Returns null when no page id\n\t\t * resolves (the template falls through to `linkHref` or plain\n\t\t * text).\n\t\t *\n\t\t * @return {object|null}\n\t\t */\n\t\tlinkRoute() {\n\t\t\tif (this.widget !== 'link') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst route = this.linkRouteName\n\t\t\tif (!route) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst paramMap = (this.widgetProps && this.widgetProps.params)\n\t\t\t\t|| { id: this.rowKey || 'id' }\n\t\t\tconst params = {}\n\t\t\tfor (const [routeParam, rowField] of Object.entries(paramMap)) {\n\t\t\t\tparams[routeParam] = this.row && this.row[rowField]\n\t\t\t}\n\t\t\treturn { name: route, params }\n\t\t},\n\n\t\t/**\n\t\t * Resolved external href for the built-in `widget:\"link\"` when\n\t\t * `widgetProps.href` is set. `{key}` placeholders in the href\n\t\t * are substituted from the row (`\"/x/{id}\"` + `row.id === \"42\"`\n\t\t * → `\"/x/42\"`). The final computed value is validated with\n\t\t * `safeHref` so row-injected values cannot introduce unsafe\n\t\t * schemes: a `javascript:` or `data:` result renders as plain text\n\t\t * instead of a link. Returns null when `href` isn't set.\n\t\t *\n\t\t * @return {string|null}\n\t\t */\n\t\tlinkHref() {\n\t\t\tif (this.widget !== 'link') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst href = this.widgetProps && this.widgetProps.href\n\t\t\tif (!href) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst resolved = String(href).replace(/\\{(\\w+)\\}/g, (_, key) => this.row && this.row[key] !== null && this.row[key] !== undefined ? String(this.row[key]) : '')\n\t\t\t// The safeHref retrofit (18700fd94) put this check here and a later\n\t\t\t// lint pass (e5ea00d51) dropped it, leaving the docblock above\n\t\t\t// promising a check the code no longer made: a template of `{url}`\n\t\t\t// put a row's `javascript:` value straight into `:href`. safeHref\n\t\t\t// answers '#' for anything unsafe; that is not a link worth\n\t\t\t// rendering, so fall through to plain text.\n\t\t\tconst safe = safeHref(resolved)\n\t\t\treturn safe === '#' ? null : safe\n\t\t},\n\n\t\t/**\n\t\t * Resolved formatter function for this cell, or `null`. A column's\n\t\t * `formatter` id resolves against the injected `cnFormatters` registry.\n\t\t *\n\t\t * @return {((value: unknown, row: object, property: object, options: object) => unknown)|null}\n\t\t */\n\t\tformatterFn() {\n\t\t\tconst fn = this.formatter && this.cnFormatters && this.cnFormatters[this.formatter]\n\t\t\treturn typeof fn === 'function' ? fn : null\n\t\t},\n\n\t\t/** True when an explicit formatter is in play for this cell. */\n\t\thasFormatter() {\n\t\t\treturn this.formatterFn !== null\n\t\t},\n\n\t\t/**\n\t\t * The declarative format `style`, or null when no (recognised) `format`\n\t\t * spec is set. Drives the built-in currency / duration / number / percent\n\t\t * / swatch rendering paths.\n\t\t *\n\t\t * @return {string|null}\n\t\t */\n\t\tformatStyle() {\n\t\t\tconst s = this.format && this.format.style\n\t\t\treturn typeof s === 'string' ? s : null\n\t\t},\n\n\t\t/** True when a built-in NUMERIC format (currency/number/percent/duration) applies. */\n\t\thasBuiltinFormat() {\n\t\t\treturn ['currency', 'number', 'percent', 'duration'].includes(this.formatStyle)\n\t\t},\n\n\t\t/** True when this cell renders as a colour swatch (`format.style:\"swatch\"`). */\n\t\tisSwatch() {\n\t\t\treturn this.formatStyle === 'swatch'\n\t\t},\n\n\t\t/**\n\t\t * The swatch colour — read from the sibling row field named by\n\t\t * `format.colorField` (default `'color'`). Returns null when no usable\n\t\t * colour string is present (the dot is then omitted).\n\t\t *\n\t\t * @return {string|null}\n\t\t */\n\t\tswatchColor() {\n\t\t\tif (!this.isSwatch) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst field = (this.format && this.format.colorField) || 'color'\n\t\t\tconst c = this.row && this.row[field]\n\t\t\treturn (typeof c === 'string' && c.trim() !== '') ? c : null\n\t\t},\n\n\t\tformattedValue() {\n\t\t\tif (this.formatterFn) {\n\t\t\t\ttry {\n\t\t\t\t\treturn this.formatterFn(this.value, this.row, this.property, this.formatterOptions || undefined)\n\t\t\t\t} catch (e) {\n\t\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\t\tconsole.warn(`[CnCellRenderer] formatter \"${this.formatter}\" threw; falling back`, e)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (this.hasBuiltinFormat) {\n\t\t\t\treturn this.applyBuiltinFormat()\n\t\t\t}\n\t\t\treturn formatValue(this.value, this.property, { truncate: this.truncate })\n\t\t},\n\n\t\trawTitle() {\n\t\t\t// Show full value on hover if it was truncated\n\t\t\tconst raw = this.value\n\t\t\tif (typeof raw === 'string' && raw.length > this.truncate) {\n\t\t\t\treturn raw\n\t\t\t}\n\t\t\treturn undefined\n\t\t},\n\n\t\tcellClass() {\n\t\t\tconst classes = []\n\t\t\tif (this.propertyType === 'boolean') {\n\t\t\t\tclasses.push('cn-cell-renderer--boolean')\n\t\t\t}\n\t\t\tif (this.isEnum) {\n\t\t\t\tclasses.push('cn-cell-renderer--enum')\n\t\t\t}\n\t\t\tif (this.property?.format === 'date-time' || this.property?.format === 'date') {\n\t\t\t\tclasses.push('cn-cell-renderer--date')\n\t\t\t}\n\t\t\tif (this.property?.format === 'uuid') {\n\t\t\t\tclasses.push('cn-cell-renderer--uuid')\n\t\t\t}\n\t\t\tif (this.propertyType === 'integer' || this.propertyType === 'number') {\n\t\t\t\tclasses.push('cn-cell-renderer--number')\n\t\t\t}\n\t\t\treturn classes\n\t\t},\n\t},\n\n\tmounted() {\n\t\t// Warn once per column-property-name when `widget:\"link\"` resolves\n\t\t// to neither a `route` nor an `href` — silent fallback hides\n\t\t// manifest mistakes, an every-row warn is too noisy.\n\t\t// A column with a `routeMap` has declared its targets; a row whose\n\t\t// value is not in the map (an empty requester, say) is data, not a\n\t\t// manifest mistake, so it renders as text without the warning. The\n\t\t// same holds for a declared `href` whose resolved value safeHref\n\t\t// refused: the manifest is fine, the row value is not.\n\t\tif (\n\t\t\tthis.widget === 'link'\n\t\t\t&& !this.linkRoute\n\t\t\t&& !this.widgetProps?.href\n\t\t\t&& this.widgetProps?.fallback !== 'silent'\n\t\t\t&& !(this.widgetProps?.routeMap && typeof this.widgetProps.routeMap === 'object')\n\t\t) {\n\t\t\tconst key = this.property?.title || String(this.value).slice(0, 20)\n\t\t\tif (!WARNED_LINK_KEYS.has(key)) {\n\t\t\t\tWARNED_LINK_KEYS.add(key)\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn(`[CnCellRenderer] widget:\"link\" on \"${key}\" has no resolvable target — set widgetProps.route (page id) or widgetProps.href (URL with optional {field} placeholders); cell falls back to plain text.`)\n\t\t\t}\n\t\t}\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Render the cell value per the declarative `format` spec (currency /\n\t\t * number / percent / duration). Mirrors CnStatWidget's formatting so a\n\t\t * KPI tile and a table column read identically. Non-numeric values fall\n\t\t * back to `formatValue`; an empty value renders an em-dash.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tapplyBuiltinFormat() {\n\t\t\tif (!this.hasValue) {\n\t\t\t\treturn '—'\n\t\t\t}\n\t\t\tconst fmt = this.format || {}\n\t\t\tif (fmt.style === 'duration') {\n\t\t\t\treturn this.formatDuration()\n\t\t\t}\n\t\t\tconst num = Number(this.value)\n\t\t\tif (!Number.isFinite(num)) {\n\t\t\t\treturn formatValue(this.value, this.property, { truncate: this.truncate })\n\t\t\t}\n\t\t\tconst decimals = Number.isFinite(fmt.decimals)\n\t\t\t\t? fmt.decimals\n\t\t\t\t: (fmt.style === 'currency' ? 2 : 0)\n\t\t\tlet body\n\t\t\tif (fmt.style === 'currency') {\n\t\t\t\tbody = new Intl.NumberFormat(undefined, {\n\t\t\t\t\tstyle: 'currency',\n\t\t\t\t\tcurrency: safeCurrencyCode(fmt.currency),\n\t\t\t\t\tminimumFractionDigits: decimals,\n\t\t\t\t\tmaximumFractionDigits: decimals,\n\t\t\t\t}).format(num)\n\t\t\t} else if (fmt.style === 'percent') {\n\t\t\t\t// Values are stored as the literal percent (83.3), not a 0–1 ratio.\n\t\t\t\tbody = new Intl.NumberFormat(undefined, {\n\t\t\t\t\tminimumFractionDigits: decimals,\n\t\t\t\t\tmaximumFractionDigits: decimals,\n\t\t\t\t}).format(num) + '%'\n\t\t\t} else {\n\t\t\t\tbody = new Intl.NumberFormat(undefined, {\n\t\t\t\t\tminimumFractionDigits: decimals,\n\t\t\t\t\tmaximumFractionDigits: decimals,\n\t\t\t\t}).format(num)\n\t\t\t}\n\t\t\treturn `${fmt.prefix || ''}${body}${fmt.suffix || ''}`\n\t\t},\n\n\t\t/**\n\t\t * Render a numeric duration compactly (`1u 23m`, `45m 10s`, `12s`). The\n\t\t * raw value is seconds unless `format.unit` is `'milliseconds'` /\n\t\t * `'minutes'` / `'hours'`. The hour suffix is `u` (uur) to read naturally\n\t\t * under NL theming while staying digit-led for other locales.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tformatDuration() {\n\t\t\tconst fmt = this.format || {}\n\t\t\tlet secs = Number(this.value)\n\t\t\tif (!Number.isFinite(secs)) {\n\t\t\t\treturn formatValue(this.value, this.property, { truncate: this.truncate })\n\t\t\t}\n\t\t\tif (fmt.unit === 'milliseconds') {\n\t\t\t\t// Sub-second durations are the common case for a millisecond\n\t\t\t\t// field (request/step timings), and they would all collapse to\n\t\t\t\t// the `0s` floor below — so render them in ms directly.\n\t\t\t\tconst ms = Math.round(secs)\n\t\t\t\tif (Math.abs(ms) < 1000) {\n\t\t\t\t\treturn `${fmt.prefix || ''}${ms}ms${fmt.suffix || ''}`\n\t\t\t\t}\n\t\t\t\tsecs = ms / 1000\n\t\t\t} else if (fmt.unit === 'minutes') {\n\t\t\t\tsecs *= 60\n\t\t\t} else if (fmt.unit === 'hours') {\n\t\t\t\tsecs *= 3600\n\t\t\t}\n\t\t\tsecs = Math.round(secs)\n\t\t\tconst sign = secs < 0 ? '-' : ''\n\t\t\tsecs = Math.abs(secs)\n\t\t\tconst h = Math.floor(secs / 3600)\n\t\t\tconst m = Math.floor((secs % 3600) / 60)\n\t\t\tconst s = secs % 60\n\t\t\tconst parts = []\n\t\t\tif (h > 0) {\n\t\t\t\tparts.push(`${h}u`)\n\t\t\t}\n\t\t\tif (m > 0) {\n\t\t\t\tparts.push(`${m}m`)\n\t\t\t}\n\t\t\tif (s > 0 && h === 0) {\n\t\t\t\tparts.push(`${s}s`)\n\t\t\t}\n\t\t\tif (parts.length === 0) {\n\t\t\t\tparts.push('0s')\n\t\t\t}\n\t\t\treturn `${fmt.prefix || ''}${sign}${parts.join(' ')}${fmt.suffix || ''}`\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-cell-renderer--uuid {\n\tfont-family: monospace;\n\tfont-size: 13px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-cell-renderer--number {\n\tfont-variant-numeric: tabular-nums;\n}\n\n.cn-cell-renderer--date {\n\twhite-space: nowrap;\n}\n\n.cn-cell-renderer__dash {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-cell-renderer__icon--success {\n\tcolor: var(--color-success);\n}\n\n.cn-cell-renderer__swatch-wrap {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-cell-renderer__swatch-dot {\n\tdisplay: inline-block;\n\twidth: 12px;\n\theight: 12px;\n\tborder-radius: 50%;\n\tflex-shrink: 0;\n\tborder: 1px solid var(--color-border-dark);\n}\n</style>\n"],"names":["_createElementBlock","_normalizeClass","_createBlock","_resolveDynamicComponent","_mergeProps","_Fragment","_openBlock"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnCellRenderer.vue3.js","sources":["../../../../src/components/CnCellRenderer/CnCellRenderer.vue?vue&type=template&id=37d7b88f&lang.js"],"sourcesContent":["<template>\n\t<span class=\"cn-cell-renderer\" :class=\"cellClass\">\n\t\t<!-- Consumer-registered cell widget (cnCellWidgets[column.widget]) -->\n\t\t<component\n\t\t\t:is=\"widgetComponent\"\n\t\t\tv-if=\"widgetComponent\"\n\t\t\t:value=\"value\"\n\t\t\t:row=\"row\"\n\t\t\t:property=\"property\"\n\t\t\t:formatted=\"formattedValue\"\n\t\t\tv-bind=\"widgetProps\" />\n\n\t\t<!-- Built-in \"badge\" widget — renders the (possibly formatter-shaped) value as a status pill -->\n\t\t<template v-else-if=\"widget === 'badge'\">\n\t\t\t<CnStatusBadge v-if=\"hasValue\"\n\t\t\t\t:label=\"String(formattedValue)\"\n\t\t\t\t:variant=\"badgeVariant\"\n\t\t\t\t:colorMap=\"badgeColorMap\" />\n\t\t\t<span v-else class=\"cn-cell-renderer__dash\">—</span>\n\t\t</template>\n\n\t\t<!-- Built-in \"fkResolve\" widget — resolves a reference uuid to the related\n\t\t object's display label via the object store (per-schema caching).\n\t\t Config: widgetProps { register, schema, labelField }. -->\n\t\t<template v-else-if=\"widget === 'fkResolve'\">\n\t\t\t<CnFkResolveCell\n\t\t\t\tv-if=\"hasValue\"\n\t\t\t\t:value=\"value\"\n\t\t\t\t:register=\"(widgetProps && widgetProps.register) || ''\"\n\t\t\t\t:schema=\"(widgetProps && widgetProps.schema) || ''\"\n\t\t\t\t:labelField=\"(widgetProps && widgetProps.labelField) || 'name'\" />\n\t\t\t<span v-else class=\"cn-cell-renderer__dash\">—</span>\n\t\t</template>\n\n\t\t<!-- Built-in \"link\" widget — renders the (possibly formatter-shaped) value\n\t\t as a router-link (when widgetProps.route is a manifest page id, or\n\t\t widgetProps.routeMap picks one per row by widgetProps.routeField) or\n\t\t an external anchor (when widgetProps.href is set). Falls back to\n\t\t plain text + a once-per-session console.warn when neither resolves. -->\n\t\t<template v-else-if=\"widget === 'link'\">\n\t\t\t<router-link\n\t\t\t\tv-if=\"linkRoute\"\n\t\t\t\t:to=\"linkRoute\"\n\t\t\t\tclass=\"cn-cell-renderer__link\">\n\t\t\t\t{{ formattedValue }}\n\t\t\t</router-link>\n\t\t\t<a\n\t\t\t\tv-else-if=\"linkHref\"\n\t\t\t\t:href=\"linkHref\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\trel=\"noopener\"\n\t\t\t\tclass=\"cn-cell-renderer__link\">\n\t\t\t\t{{ formattedValue }}\n\t\t\t</a>\n\t\t\t<span v-else :title=\"rawTitle\">{{ formattedValue }}</span>\n\t\t</template>\n\n\t\t<!-- Built-in \"avatar\" widget: a person as an avatar with the name beside\n\t\t it. A user id (widgetProps.userField, or widgetProps.user for a cell\n\t\t that holds the id itself) gets the Nextcloud avatar; a free name\n\t\t gets its initials. The picture is decoration: the name is the text. -->\n\t\t<template v-else-if=\"widget === 'avatar'\">\n\t\t\t<span v-if=\"avatarName || avatarUserId\" class=\"cn-cell-renderer__avatar\">\n\t\t\t\t<span v-if=\"avatarUserId\" class=\"cn-cell-renderer__avatar-picture\" aria-hidden=\"true\">\n\t\t\t\t\t<NcAvatar\n\t\t\t\t\t\t:user=\"avatarUserId\"\n\t\t\t\t\t\t:displayName=\"avatarName || avatarUserId\"\n\t\t\t\t\t\t:size=\"avatarSize\"\n\t\t\t\t\t\t:disableMenu=\"true\"\n\t\t\t\t\t\t:disableTooltip=\"true\" />\n\t\t\t\t</span>\n\t\t\t\t<span\n\t\t\t\t\tv-else\n\t\t\t\t\tclass=\"cn-cell-renderer__avatar-initials\"\n\t\t\t\t\t:style=\"{ width: avatarSize + 'px', height: avatarSize + 'px' }\"\n\t\t\t\t\taria-hidden=\"true\">{{ avatarInitials }}</span>\n\t\t\t\t<span class=\"cn-cell-renderer__avatar-name\" :title=\"avatarName || avatarUserId\">{{ avatarName || avatarUserId }}</span>\n\t\t\t</span>\n\t\t\t<span v-else class=\"cn-cell-renderer__dash\">—</span>\n\t\t</template>\n\n\t\t<!-- Built-in \"date\" widget: a date whose colour follows\n\t\t widgetProps.variantWhen, rules on the number of days until it\n\t\t (0 = today, negative = overdue). -->\n\t\t<template v-else-if=\"widget === 'date'\">\n\t\t\t<time\n\t\t\t\tv-if=\"dateTimestamp\"\n\t\t\t\tclass=\"cn-cell-renderer__date\"\n\t\t\t\t:class=\"dateVariant ? 'cn-cell-renderer__date--' + dateVariant : null\"\n\t\t\t\t:datetime=\"dateTimestamp.toISOString()\"\n\t\t\t\t:title=\"rawTitle\">{{ dateLabel }}</time>\n\t\t\t<span v-else class=\"cn-cell-renderer__dash\">—</span>\n\t\t</template>\n\n\t\t<!-- Explicit column formatter — overrides the type-aware paths below -->\n\t\t<template v-else-if=\"hasFormatter\">\n\t\t\t<span :title=\"rawTitle\">{{ formattedValue }}</span>\n\t\t</template>\n\n\t\t<!-- Declarative swatch — a colour dot (from a sibling row field) + text -->\n\t\t<template v-else-if=\"isSwatch\">\n\t\t\t<span class=\"cn-cell-renderer__swatch-wrap\">\n\t\t\t\t<span\n\t\t\t\t\tv-if=\"swatchColor\"\n\t\t\t\t\tclass=\"cn-cell-renderer__swatch-dot\"\n\t\t\t\t\t:style=\"{ backgroundColor: swatchColor }\"\n\t\t\t\t\taria-hidden=\"true\" />\n\t\t\t\t<span v-if=\"hasValue\" :title=\"rawTitle\">{{ formattedValue }}</span>\n\t\t\t\t<span v-else class=\"cn-cell-renderer__dash\">—</span>\n\t\t\t</span>\n\t\t</template>\n\n\t\t<!-- Declarative built-in format (currency / duration / number / percent) -->\n\t\t<template v-else-if=\"hasBuiltinFormat\">\n\t\t\t<span :title=\"rawTitle\" class=\"cn-cell-renderer--number\">{{ formattedValue }}</span>\n\t\t</template>\n\n\t\t<!-- Date / date-time: dynamic NcDateTime (relative time, absolute on hover) -->\n\t\t<template v-else-if=\"isDate\">\n\t\t\t<NcDateTime v-if=\"dateTimestamp\" :timestamp=\"dateTimestamp\" />\n\t\t\t<span v-else class=\"cn-cell-renderer__dash\">—</span>\n\t\t</template>\n\n\t\t<!-- URI / URL: external link -->\n\t\t<template v-else-if=\"isUri\">\n\t\t\t<a\n\t\t\t\tv-if=\"uriHref\"\n\t\t\t\t:href=\"uriHref\"\n\t\t\t\ttarget=\"_blank\"\n\t\t\t\trel=\"noopener\"\n\t\t\t\tclass=\"cn-cell-renderer__link\">\n\t\t\t\t{{ formattedValue }}\n\t\t\t</a>\n\t\t\t<span v-else-if=\"hasValue\" :title=\"rawTitle\">{{ formattedValue }}</span>\n\t\t\t<span v-else class=\"cn-cell-renderer__dash\">—</span>\n\t\t</template>\n\n\t\t<!-- Boolean: icon -->\n\t\t<template v-else-if=\"propertyType === 'boolean'\">\n\t\t\t<CheckBold v-if=\"value\" :size=\"16\" class=\"cn-cell-renderer__icon cn-cell-renderer__icon--success\" />\n\t\t\t<span v-else class=\"cn-cell-renderer__dash\">—</span>\n\t\t</template>\n\n\t\t<!-- Enum: status badge (auto-coloured from the property's optional colorMap) -->\n\t\t<template v-else-if=\"isEnum\">\n\t\t\t<CnStatusBadge v-if=\"value\"\n\t\t\t\t:label=\"enumLabel\"\n\t\t\t\t:colorKey=\"String(value)\"\n\t\t\t\t:colorMap=\"enumColorMap\" />\n\t\t\t<span v-else class=\"cn-cell-renderer__dash\">—</span>\n\t\t</template>\n\n\t\t<!-- Array: comma-joined or count -->\n\t\t<template v-else-if=\"propertyType === 'array'\">\n\t\t\t<span v-if=\"!value || !value.length\" class=\"cn-cell-renderer__dash\">—</span>\n\t\t\t<span v-else :title=\"Array.isArray(value) ? value.join(', ') : ''\">\n\t\t\t\t{{ formattedValue }}\n\t\t\t</span>\n\t\t</template>\n\n\t\t<!-- Default: formatted text -->\n\t\t<template v-else>\n\t\t\t<span :title=\"rawTitle\">{{ formattedValue }}</span>\n\t\t</template>\n\t</span>\n</template>\n\n<script>\nimport { getCanonicalLocale } from '@nextcloud/l10n'\nimport { NcAvatar, NcDateTime } from '@nextcloud/vue'\nimport CheckBold from 'vue-material-design-icons/CheckBold.vue'\nimport CnFkResolveCell from '../CnFkResolveCell/CnFkResolveCell.vue'\nimport { parseDateValue, resolveDateVariant } from '../../utils/dateVariant.js'\nimport { safeCurrencyCode } from '../../utils/formatMetric.js'\nimport { objectFieldValue } from '../../utils/objectName.js'\nimport { safeHref } from '../../utils/safeHref.js'\nimport { formatValue } from '../../utils/schema.js'\nimport { CnStatusBadge } from '../CnStatusBadge/index.js'\n\n/**\n * Module-level set of column keys already warned about for a\n * `widget:\"link\"` declaration with no resolvable target — guarantees\n * one warning per (page, column-key) rather than per row × render.\n */\nconst WARNED_LINK_KEYS = new Set()\n\n/**\n * CnCellRenderer — Type-aware cell renderer for schema-driven tables.\n *\n * Renders a single cell value based on its schema property definition.\n * Booleans render as icons, enums as status badges, dates as formatted strings,\n * and everything else as truncated text via `formatValue()`.\n *\n * ```vue\n * <CnCellRenderer :value=\"row.status\" :property=\"schema.properties.status\" />\n * ```\n */\nexport default {\n\tname: 'CnCellRenderer',\n\n\tcomponents: {\n\t\tCnStatusBadge,\n\t\tCnFkResolveCell,\n\t\tCheckBold,\n\t\tNcAvatar,\n\t\tNcDateTime,\n\t},\n\n\tinject: {\n\t\t/**\n\t\t * Cell-formatter registry, provided by CnAppRoot (`cnFormatters`).\n\t\t * Map of formatter-id → `(value, row, property) => string|number`.\n\t\t * Defaults to an empty object so standalone use (no CnAppRoot\n\t\t * ancestor) is unaffected.\n\t\t */\n\t\tcnFormatters: { default: () => ({}) },\n\t\t/**\n\t\t * Cell-widget registry, provided by CnAppRoot (`cnCellWidgets`).\n\t\t * Map of widget-id → Vue component. Resolves a column's `widget` id;\n\t\t * the built-in `\"badge\"` is handled inline (no registry entry needed).\n\t\t * Defaults to an empty object.\n\t\t */\n\t\tcnCellWidgets: { default: () => ({}) },\n\t\t/**\n\t\t * Display-layer translation function, provided by CnAppRoot\n\t\t * (`cnTranslate: this.translate`, bound to the host app's id). Applied\n\t\t * to the enum badge label only — the underlying value is untouched.\n\t\t * Defaults to identity so standalone use is unaffected.\n\t\t */\n\t\tcnTranslate: { default: () => (key) => key },\n\t},\n\n\tprops: {\n\t\t/** The raw cell value */\n\t\tvalue: {\n\t\t\ttype: [Boolean, String, Number, Array, Object],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/** Schema property definition: { type, format, enum, items, title } */\n\t\tproperty: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Optional cell-formatter id (e.g. `currency`, `automationTrigger`).\n\t\t * When set and resolvable in the injected `cnFormatters` registry,\n\t\t * the cell renders `cnFormatters[formatter](value, row, property,\n\t\t * formatterOptions)` as text — an explicit override of the type-aware\n\t\t * rendering below.\n\t\t */\n\t\tformatter: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Declarative options map passed as the formatter's fourth argument\n\t\t * (e.g. `{ currency: 'USD' }` for the built-in `currency` formatter,\n\t\t * or `{ negative, zero, positive }` phrases for `conditionalPhrase`).\n\t\t * Undefined-safe: three-argument formatters simply ignore it.\n\t\t */\n\t\tformatterOptions: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Optional cell-widget id (e.g. `badge`, or a consumer-registered\n\t\t * name). When it resolves in `cnCellWidgets` the cell renders that\n\t\t * component with `{ value, row, property, formatted, ...widgetProps }`;\n\t\t * the built-in id `\"badge\"` renders `CnStatusBadge` and the built-in\n\t\t * id `\"fkResolve\"` renders `CnFkResolveCell` (uuid → related object\n\t\t * label, config via `widgetProps { register, schema, labelField }`).\n\t\t * The built-in id `\"avatar\"` renders a person as an avatar with the\n\t\t * name beside it (`widgetProps { userField?, user?, nameField?, size? }`;\n\t\t * a free name gets its initials), and the built-in id `\"date\"` renders\n\t\t * a date whose colour follows `widgetProps.variantWhen`, rules on the\n\t\t * number of days until the date (`[{ op: \"lt\", value: 0, variant:\n\t\t * \"error\" }, { op: \"lte\", value: 5, variant: \"warning\" }]`).\n\t\t * Takes precedence over `formatter`/the type-aware rendering, but the\n\t\t * value handed to the widget is the formatter-shaped `formatted` when\n\t\t * `formatter` is also set.\n\t\t */\n\t\twidget: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Extra props spread onto the resolved cell-widget component. The\n\t\t * built-in `\"link\"` reads its target from here: `route` (a manifest\n\t\t * page id), `params` (route param to row field), `href` (URL with\n\t\t * `{field}` placeholders), and `routeField` + `routeMap` to pick the\n\t\t * page per row from a sibling field (`{ person: 'ContactDetail' }`),\n\t\t * with `route` as the fallback for a value the map does not hold.\n\t\t */\n\t\twidgetProps: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/**\n\t\t * Optional declarative cell-format spec — a no-code alternative to a\n\t\t * registry `formatter`. Recognised `style` values:\n\t\t *\n\t\t * - `'currency'` → `Intl.NumberFormat` currency (e.g. `€ 1.234,56`),\n\t\t * honouring `currency` (ISO code, default `'EUR'`) and `decimals`\n\t\t * (default 2).\n\t\t * - `'number'` / `'percent'` → localized number (percent appends `%`),\n\t\t * honouring `decimals` (default 0).\n\t\t * - `'duration'` → a seconds value rendered compact (`1u 23m`, `45m 10s`,\n\t\t * `12s`); pass `unit: 'milliseconds'` / `'minutes'` / `'hours'` when the\n\t\t * raw value is not in seconds. Sub-second millisecond values render as\n\t\t * `245ms` rather than rounding down to `0s`.\n\t\t * - `'swatch'` → a colour dot read from a sibling row field named by\n\t\t * `colorField` (the value renders as the cell text beside it).\n\t\t *\n\t\t * `prefix` / `suffix` are prepended/appended to the numeric styles.\n\t\t * Resolved AFTER `formatter` / `widget` (those win), but BEFORE the\n\t\t * type-aware rendering, so a manifest column can opt into currency or a\n\t\t * colour swatch without registering a function.\n\t\t *\n\t\t * @type {{style?: 'currency'|'number'|'percent'|'duration'|'swatch', currency?: string, decimals?: number, unit?: 'milliseconds'|'seconds'|'minutes'|'hours', prefix?: string, suffix?: string, colorField?: string}}\n\t\t */\n\t\tformat: {\n\t\t\ttype: Object,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * The full row object — passed so a formatter can be a function of\n\t\t * the whole record (e.g. \"days since `@self.updated`\"), not just\n\t\t * this one cell value.\n\t\t */\n\t\trow: {\n\t\t\ttype: Object,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\t/** Maximum string length before truncation */\n\t\ttruncate: {\n\t\t\ttype: Number,\n\t\t\tdefault: 100,\n\t\t},\n\n\t\t/**\n\t\t * Row identifier field — used by the built-in `widget:\"link\"` when\n\t\t * the manifest doesn't specify an explicit `widgetProps.params`\n\t\t * map. Defaults to `'id'` so router-link param resolution works\n\t\t * with the manifest convention (`/x/:id`).\n\t\t */\n\t\trowKey: {\n\t\t\ttype: String,\n\t\t\tdefault: 'id',\n\t\t},\n\t},\n\n\tcomputed: {\n\t\tpropertyType() {\n\t\t\treturn this.property?.type || (typeof this.value)\n\t\t},\n\n\t\tisEnum() {\n\t\t\treturn !!(this.property?.enum && this.property.enum.length > 0)\n\t\t},\n\n\t\t/**\n\t\t * Badge text for an enum cell. Schema enum values are English source\n\t\t * codes, so a Dutch session otherwise reads `submitted` in a table whose\n\t\t * headers are Dutch. Only the label is translated — the cell's value,\n\t\t * sorting and filtering keep using the raw code.\n\t\t */\n\t\tenumLabel() {\n\t\t\tconst raw = String(this.value)\n\t\t\tconst labels = (this.property && (this.property.enumLabels || this.property['x-enum-labels'])) || {}\n\t\t\treturn this.cnTranslate(labels[raw] || raw)\n\t\t},\n\n\t\t/** True when the property is a date / date-time (rendered via NcDateTime). */\n\t\tisDate() {\n\t\t\treturn this.property?.format === 'date-time' || this.property?.format === 'date'\n\t\t},\n\n\t\t/**\n\t\t * The cell value as a Date for NcDateTime, or `null` when absent or\n\t\t * unparseable (the cell then falls back to a dash / plain text).\n\t\t *\n\t\t * @return {Date|null}\n\t\t */\n\t\tdateTimestamp() {\n\t\t\tif (!this.hasValue) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst date = new Date(this.value)\n\t\t\treturn Number.isNaN(date.getTime()) ? null : date\n\t\t},\n\n\t\t/**\n\t\t * `avatar` widget: the Nextcloud user id whose avatar to show, or ''\n\t\t * for a free name. Read from the row field `widgetProps.userField`,\n\t\t * or from the cell value itself when `widgetProps.user` is true.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-avatar-and-date-cells\n\t\t */\n\t\tavatarUserId() {\n\t\t\tconst props = this.widgetProps || {}\n\t\t\tlet id = ''\n\t\t\tif (typeof props.userField === 'string' && props.userField !== '') {\n\t\t\t\tid = objectFieldValue(this.row, props.userField)\n\t\t\t} else if (props.user === true) {\n\t\t\t\tid = this.value\n\t\t\t}\n\t\t\treturn (typeof id === 'string' || typeof id === 'number') ? String(id) : ''\n\t\t},\n\n\t\t/**\n\t\t * `avatar` widget: the name shown beside the picture. Read from the\n\t\t * row field `widgetProps.nameField` when set, else the cell value.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-avatar-and-date-cells\n\t\t */\n\t\tavatarName() {\n\t\t\tconst props = this.widgetProps || {}\n\t\t\tif (typeof props.nameField === 'string' && props.nameField !== '') {\n\t\t\t\tconst name = objectFieldValue(this.row, props.nameField)\n\t\t\t\tif (typeof name === 'string' && name !== '') {\n\t\t\t\t\treturn name\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!this.hasValue || typeof this.value === 'object') {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\treturn String(this.hasFormatter ? this.formattedValue : this.value)\n\t\t},\n\n\t\t/**\n\t\t * `avatar` widget: the initials of a free name, at most two letters\n\t\t * (first and last word).\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-avatar-and-date-cells\n\t\t */\n\t\tavatarInitials() {\n\t\t\tconst words = this.avatarName.trim().split(/\\s+/).filter((word) => word !== '')\n\t\t\tif (words.length === 0) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst first = Array.from(words[0])[0] || ''\n\t\t\tconst last = words.length > 1 ? (Array.from(words[words.length - 1])[0] || '') : ''\n\t\t\treturn (first + last).toLocaleUpperCase()\n\t\t},\n\n\t\t/**\n\t\t * `avatar` widget: the picture size in pixels (`widgetProps.size`, default 24).\n\t\t *\n\t\t * @return {number}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-avatar-and-date-cells\n\t\t */\n\t\tavatarSize() {\n\t\t\tconst size = Number(this.widgetProps && this.widgetProps.size)\n\t\t\treturn Number.isFinite(size) && size > 0 ? size : 24\n\t\t},\n\n\t\t/**\n\t\t * `date` widget: the variant of the first `widgetProps.variantWhen`\n\t\t * rule the date matches, or '' for none.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-avatar-and-date-cells\n\t\t */\n\t\tdateVariant() {\n\t\t\tconst rules = this.widgetProps && this.widgetProps.variantWhen\n\t\t\tconst variant = resolveDateVariant(this.value, rules)\n\t\t\treturn variant === 'default' ? '' : variant\n\t\t},\n\n\t\t/**\n\t\t * `date` widget: the date written out in the user's locale.\n\t\t *\n\t\t * @return {string}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-avatar-and-date-cells\n\t\t */\n\t\tdateLabel() {\n\t\t\tconst date = parseDateValue(this.value)\n\t\t\tif (date === null) {\n\t\t\t\treturn ''\n\t\t\t}\n\t\t\tconst options = { day: 'numeric', month: 'short', year: 'numeric' }\n\t\t\tlet locale\n\t\t\ttry {\n\t\t\t\tlocale = getCanonicalLocale()\n\t\t\t} catch {\n\t\t\t\tlocale = undefined\n\t\t\t}\n\t\t\ttry {\n\t\t\t\treturn new Intl.DateTimeFormat(locale || undefined, options).format(date)\n\t\t\t} catch {\n\t\t\t\treturn new Intl.DateTimeFormat(undefined, options).format(date)\n\t\t\t}\n\t\t},\n\n\t\t/** True when the property is a URI / URL (rendered as an external link). */\n\t\tisUri() {\n\t\t\treturn this.property?.format === 'uri' || this.property?.format === 'url'\n\t\t},\n\n\t\t/**\n\t\t * Safe external href for a URI/URL cell, or `null` when the value is\n\t\t * absent or fails `safeHref` validation (e.g. an unsafe scheme).\n\t\t *\n\t\t * @return {string|null}\n\t\t */\n\t\turiHref() {\n\t\t\tif (!this.hasValue) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn safeHref(String(this.value))\n\t\t},\n\n\t\t/** True when the cell has a renderable value (not null/undefined/empty string). */\n\t\thasValue() {\n\t\t\treturn this.value !== null && this.value !== undefined && this.value !== ''\n\t\t},\n\n\t\t/**\n\t\t * Resolved cell-widget component for this column, or `null`. A column's\n\t\t * `widget` id resolves against the injected `cnCellWidgets` registry;\n\t\t * the built-in `\"badge\"` is NOT resolved here (handled inline in the\n\t\t * template) so apps can still override `\"badge\"` via the registry.\n\t\t *\n\t\t * @return {import('vue').Component|null}\n\t\t */\n\t\twidgetComponent() {\n\t\t\tif (!this.widget) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst c = this.cnCellWidgets && this.cnCellWidgets[this.widget]\n\t\t\treturn c || null\n\t\t},\n\n\t\t/** Variant for the built-in `badge` widget — `widgetProps.variant` or `'default'`. */\n\t\tbadgeVariant() {\n\t\t\treturn (this.widgetProps && this.widgetProps.variant) || 'default'\n\t\t},\n\n\t\t/**\n\t\t * Color map for the built-in `badge` widget — `widgetProps.colorMap`,\n\t\t * a `{ value: variant }` map (e.g. `{ placed: 'primary', delivered: 'success' }`)\n\t\t * that CnStatusBadge resolves per label. Null when unset (falls back to\n\t\t * `badgeVariant`). Lets a manifest colour a status column without code.\n\t\t *\n\t\t * @return {object|null}\n\t\t */\n\t\tbadgeColorMap() {\n\t\t\treturn (this.widgetProps && this.widgetProps.colorMap) || null\n\t\t},\n\n\t\t/**\n\t\t * Color map for the auto-rendered enum badge — read from the schema\n\t\t * property's optional `colorMap` (or `x-color-map`). Null when unset, so\n\t\t * enum cells stay the default grey unless the schema opts into colours.\n\t\t *\n\t\t * @return {object|null}\n\t\t */\n\t\tenumColorMap() {\n\t\t\treturn (this.property && (this.property.colorMap || this.property['x-color-map'])) || null\n\t\t},\n\n\t\t/**\n\t\t * The manifest page id the built-in `widget:\"link\"` routes this row\n\t\t * to, or null.\n\t\t *\n\t\t * `widgetProps.routeField` names a sibling field on the row, and\n\t\t * `widgetProps.routeMap` maps that field's values to page ids, so one\n\t\t * column can link a person to `ContactDetail` and an organisation to\n\t\t * `OrganisationDetail` off `initiatorType`. A row whose value has no\n\t\t * entry in the map falls back to the fixed `widgetProps.route`, and\n\t\t * without one the cell renders as text. The map is the whole\n\t\t * vocabulary: a row value is only ever used as a KEY into it, never\n\t\t * as a route name itself, so row data cannot send a link to a page\n\t\t * the manifest did not name.\n\t\t *\n\t\t * @return {string|null} The page id, or null when none resolves.\n\t\t */\n\t\tlinkRouteName() {\n\t\t\tconst wp = this.widgetProps || {}\n\t\t\tconst map = wp.routeMap\n\t\t\tif (typeof wp.routeField === 'string' && wp.routeField !== '' && map && typeof map === 'object') {\n\t\t\t\tconst key = this.row ? this.row[wp.routeField] : undefined\n\t\t\t\tif (key !== undefined && key !== null && Object.hasOwn(map, String(key))) {\n\t\t\t\t\tconst mapped = map[String(key)]\n\t\t\t\t\tif (typeof mapped === 'string' && mapped !== '') {\n\t\t\t\t\t\treturn mapped\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn (typeof wp.route === 'string' && wp.route !== '') ? wp.route : null\n\t\t},\n\n\t\t/**\n\t\t * Resolved router-link target for the built-in `widget:\"link\"`. When\n\t\t * a page id resolves (`widgetProps.route`, or the row's entry in\n\t\t * `widgetProps.routeMap`, see `linkRouteName`), returns\n\t\t * `{ name: route, params }`. Param map is `widgetProps.params`\n\t\t * (a map of route-param-name → row-field-name); when omitted,\n\t\t * defaults to `{ id: row[rowKey] }`. Returns null when no page id\n\t\t * resolves (the template falls through to `linkHref` or plain\n\t\t * text).\n\t\t *\n\t\t * @return {object|null}\n\t\t */\n\t\tlinkRoute() {\n\t\t\tif (this.widget !== 'link') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst route = this.linkRouteName\n\t\t\tif (!route) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst paramMap = (this.widgetProps && this.widgetProps.params)\n\t\t\t\t|| { id: this.rowKey || 'id' }\n\t\t\tconst params = {}\n\t\t\tfor (const [routeParam, rowField] of Object.entries(paramMap)) {\n\t\t\t\tparams[routeParam] = this.row && this.row[rowField]\n\t\t\t}\n\t\t\treturn { name: route, params }\n\t\t},\n\n\t\t/**\n\t\t * Resolved external href for the built-in `widget:\"link\"` when\n\t\t * `widgetProps.href` is set. `{key}` placeholders in the href\n\t\t * are substituted from the row (`\"/x/{id}\"` + `row.id === \"42\"`\n\t\t * → `\"/x/42\"`). The final computed value is validated with\n\t\t * `safeHref` so row-injected values cannot introduce unsafe\n\t\t * schemes: a `javascript:` or `data:` result renders as plain text\n\t\t * instead of a link. Returns null when `href` isn't set.\n\t\t *\n\t\t * @return {string|null}\n\t\t */\n\t\tlinkHref() {\n\t\t\tif (this.widget !== 'link') {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst href = this.widgetProps && this.widgetProps.href\n\t\t\tif (!href) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst resolved = String(href).replace(/\\{(\\w+)\\}/g, (_, key) => this.row && this.row[key] !== null && this.row[key] !== undefined ? String(this.row[key]) : '')\n\t\t\t// The safeHref retrofit (18700fd94) put this check here and a later\n\t\t\t// lint pass (e5ea00d51) dropped it, leaving the docblock above\n\t\t\t// promising a check the code no longer made: a template of `{url}`\n\t\t\t// put a row's `javascript:` value straight into `:href`. safeHref\n\t\t\t// answers '#' for anything unsafe; that is not a link worth\n\t\t\t// rendering, so fall through to plain text.\n\t\t\tconst safe = safeHref(resolved)\n\t\t\treturn safe === '#' ? null : safe\n\t\t},\n\n\t\t/**\n\t\t * Resolved formatter function for this cell, or `null`. A column's\n\t\t * `formatter` id resolves against the injected `cnFormatters` registry.\n\t\t *\n\t\t * @return {((value: unknown, row: object, property: object, options: object) => unknown)|null}\n\t\t */\n\t\tformatterFn() {\n\t\t\tconst fn = this.formatter && this.cnFormatters && this.cnFormatters[this.formatter]\n\t\t\treturn typeof fn === 'function' ? fn : null\n\t\t},\n\n\t\t/** True when an explicit formatter is in play for this cell. */\n\t\thasFormatter() {\n\t\t\treturn this.formatterFn !== null\n\t\t},\n\n\t\t/**\n\t\t * The declarative format `style`, or null when no (recognised) `format`\n\t\t * spec is set. Drives the built-in currency / duration / number / percent\n\t\t * / swatch rendering paths.\n\t\t *\n\t\t * @return {string|null}\n\t\t */\n\t\tformatStyle() {\n\t\t\tconst s = this.format && this.format.style\n\t\t\treturn typeof s === 'string' ? s : null\n\t\t},\n\n\t\t/** True when a built-in NUMERIC format (currency/number/percent/duration) applies. */\n\t\thasBuiltinFormat() {\n\t\t\treturn ['currency', 'number', 'percent', 'duration'].includes(this.formatStyle)\n\t\t},\n\n\t\t/** True when this cell renders as a colour swatch (`format.style:\"swatch\"`). */\n\t\tisSwatch() {\n\t\t\treturn this.formatStyle === 'swatch'\n\t\t},\n\n\t\t/**\n\t\t * The swatch colour — read from the sibling row field named by\n\t\t * `format.colorField` (default `'color'`). Returns null when no usable\n\t\t * colour string is present (the dot is then omitted).\n\t\t *\n\t\t * @return {string|null}\n\t\t */\n\t\tswatchColor() {\n\t\t\tif (!this.isSwatch) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\tconst field = (this.format && this.format.colorField) || 'color'\n\t\t\tconst c = this.row && this.row[field]\n\t\t\treturn (typeof c === 'string' && c.trim() !== '') ? c : null\n\t\t},\n\n\t\tformattedValue() {\n\t\t\tif (this.formatterFn) {\n\t\t\t\ttry {\n\t\t\t\t\treturn this.formatterFn(this.value, this.row, this.property, this.formatterOptions || undefined)\n\t\t\t\t} catch (e) {\n\t\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\t\tconsole.warn(`[CnCellRenderer] formatter \"${this.formatter}\" threw; falling back`, e)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (this.hasBuiltinFormat) {\n\t\t\t\treturn this.applyBuiltinFormat()\n\t\t\t}\n\t\t\treturn formatValue(this.value, this.property, { truncate: this.truncate })\n\t\t},\n\n\t\trawTitle() {\n\t\t\t// Show full value on hover if it was truncated\n\t\t\tconst raw = this.value\n\t\t\tif (typeof raw === 'string' && raw.length > this.truncate) {\n\t\t\t\treturn raw\n\t\t\t}\n\t\t\treturn undefined\n\t\t},\n\n\t\tcellClass() {\n\t\t\tconst classes = []\n\t\t\tif (this.propertyType === 'boolean') {\n\t\t\t\tclasses.push('cn-cell-renderer--boolean')\n\t\t\t}\n\t\t\tif (this.isEnum) {\n\t\t\t\tclasses.push('cn-cell-renderer--enum')\n\t\t\t}\n\t\t\tif (this.property?.format === 'date-time' || this.property?.format === 'date') {\n\t\t\t\tclasses.push('cn-cell-renderer--date')\n\t\t\t}\n\t\t\tif (this.property?.format === 'uuid') {\n\t\t\t\tclasses.push('cn-cell-renderer--uuid')\n\t\t\t}\n\t\t\tif (this.propertyType === 'integer' || this.propertyType === 'number') {\n\t\t\t\tclasses.push('cn-cell-renderer--number')\n\t\t\t}\n\t\t\treturn classes\n\t\t},\n\t},\n\n\tmounted() {\n\t\t// Warn once per column-property-name when `widget:\"link\"` resolves\n\t\t// to neither a `route` nor an `href` — silent fallback hides\n\t\t// manifest mistakes, an every-row warn is too noisy.\n\t\t// A column with a `routeMap` has declared its targets; a row whose\n\t\t// value is not in the map (an empty requester, say) is data, not a\n\t\t// manifest mistake, so it renders as text without the warning. The\n\t\t// same holds for a declared `href` whose resolved value safeHref\n\t\t// refused: the manifest is fine, the row value is not.\n\t\tif (\n\t\t\tthis.widget === 'link'\n\t\t\t&& !this.linkRoute\n\t\t\t&& !this.widgetProps?.href\n\t\t\t&& this.widgetProps?.fallback !== 'silent'\n\t\t\t&& !(this.widgetProps?.routeMap && typeof this.widgetProps.routeMap === 'object')\n\t\t) {\n\t\t\tconst key = this.property?.title || String(this.value).slice(0, 20)\n\t\t\tif (!WARNED_LINK_KEYS.has(key)) {\n\t\t\t\tWARNED_LINK_KEYS.add(key)\n\t\t\t\t// eslint-disable-next-line no-console\n\t\t\t\tconsole.warn(`[CnCellRenderer] widget:\"link\" on \"${key}\" has no resolvable target — set widgetProps.route (page id) or widgetProps.href (URL with optional {field} placeholders); cell falls back to plain text.`)\n\t\t\t}\n\t\t}\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Render the cell value per the declarative `format` spec (currency /\n\t\t * number / percent / duration). Mirrors CnStatWidget's formatting so a\n\t\t * KPI tile and a table column read identically. Non-numeric values fall\n\t\t * back to `formatValue`; an empty value renders an em-dash.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tapplyBuiltinFormat() {\n\t\t\tif (!this.hasValue) {\n\t\t\t\treturn '—'\n\t\t\t}\n\t\t\tconst fmt = this.format || {}\n\t\t\tif (fmt.style === 'duration') {\n\t\t\t\treturn this.formatDuration()\n\t\t\t}\n\t\t\tconst num = Number(this.value)\n\t\t\tif (!Number.isFinite(num)) {\n\t\t\t\treturn formatValue(this.value, this.property, { truncate: this.truncate })\n\t\t\t}\n\t\t\tconst decimals = Number.isFinite(fmt.decimals)\n\t\t\t\t? fmt.decimals\n\t\t\t\t: (fmt.style === 'currency' ? 2 : 0)\n\t\t\tlet body\n\t\t\tif (fmt.style === 'currency') {\n\t\t\t\tbody = new Intl.NumberFormat(undefined, {\n\t\t\t\t\tstyle: 'currency',\n\t\t\t\t\tcurrency: safeCurrencyCode(fmt.currency),\n\t\t\t\t\tminimumFractionDigits: decimals,\n\t\t\t\t\tmaximumFractionDigits: decimals,\n\t\t\t\t}).format(num)\n\t\t\t} else if (fmt.style === 'percent') {\n\t\t\t\t// Values are stored as the literal percent (83.3), not a 0–1 ratio.\n\t\t\t\tbody = new Intl.NumberFormat(undefined, {\n\t\t\t\t\tminimumFractionDigits: decimals,\n\t\t\t\t\tmaximumFractionDigits: decimals,\n\t\t\t\t}).format(num) + '%'\n\t\t\t} else {\n\t\t\t\tbody = new Intl.NumberFormat(undefined, {\n\t\t\t\t\tminimumFractionDigits: decimals,\n\t\t\t\t\tmaximumFractionDigits: decimals,\n\t\t\t\t}).format(num)\n\t\t\t}\n\t\t\treturn `${fmt.prefix || ''}${body}${fmt.suffix || ''}`\n\t\t},\n\n\t\t/**\n\t\t * Render a numeric duration compactly (`1u 23m`, `45m 10s`, `12s`). The\n\t\t * raw value is seconds unless `format.unit` is `'milliseconds'` /\n\t\t * `'minutes'` / `'hours'`. The hour suffix is `u` (uur) to read naturally\n\t\t * under NL theming while staying digit-led for other locales.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\tformatDuration() {\n\t\t\tconst fmt = this.format || {}\n\t\t\tlet secs = Number(this.value)\n\t\t\tif (!Number.isFinite(secs)) {\n\t\t\t\treturn formatValue(this.value, this.property, { truncate: this.truncate })\n\t\t\t}\n\t\t\tif (fmt.unit === 'milliseconds') {\n\t\t\t\t// Sub-second durations are the common case for a millisecond\n\t\t\t\t// field (request/step timings), and they would all collapse to\n\t\t\t\t// the `0s` floor below — so render them in ms directly.\n\t\t\t\tconst ms = Math.round(secs)\n\t\t\t\tif (Math.abs(ms) < 1000) {\n\t\t\t\t\treturn `${fmt.prefix || ''}${ms}ms${fmt.suffix || ''}`\n\t\t\t\t}\n\t\t\t\tsecs = ms / 1000\n\t\t\t} else if (fmt.unit === 'minutes') {\n\t\t\t\tsecs *= 60\n\t\t\t} else if (fmt.unit === 'hours') {\n\t\t\t\tsecs *= 3600\n\t\t\t}\n\t\t\tsecs = Math.round(secs)\n\t\t\tconst sign = secs < 0 ? '-' : ''\n\t\t\tsecs = Math.abs(secs)\n\t\t\tconst h = Math.floor(secs / 3600)\n\t\t\tconst m = Math.floor((secs % 3600) / 60)\n\t\t\tconst s = secs % 60\n\t\t\tconst parts = []\n\t\t\tif (h > 0) {\n\t\t\t\tparts.push(`${h}u`)\n\t\t\t}\n\t\t\tif (m > 0) {\n\t\t\t\tparts.push(`${m}m`)\n\t\t\t}\n\t\t\tif (s > 0 && h === 0) {\n\t\t\t\tparts.push(`${s}s`)\n\t\t\t}\n\t\t\tif (parts.length === 0) {\n\t\t\t\tparts.push('0s')\n\t\t\t}\n\t\t\treturn `${fmt.prefix || ''}${sign}${parts.join(' ')}${fmt.suffix || ''}`\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-cell-renderer--uuid {\n\tfont-family: monospace;\n\tfont-size: 13px;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-cell-renderer--number {\n\tfont-variant-numeric: tabular-nums;\n}\n\n.cn-cell-renderer--date {\n\twhite-space: nowrap;\n}\n\n.cn-cell-renderer__dash {\n\tcolor: var(--color-text-maxcontrast);\n}\n\n.cn-cell-renderer__avatar {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 8px;\n\tmax-width: 100%;\n\tvertical-align: middle;\n}\n\n.cn-cell-renderer__avatar-picture {\n\tdisplay: inline-flex;\n\tflex: none;\n}\n\n.cn-cell-renderer__avatar-initials {\n\tdisplay: inline-flex;\n\tflex: none;\n\talign-items: center;\n\tjustify-content: center;\n\tborder-radius: 50%;\n\tbackground-color: var(--color-primary-element-light);\n\tcolor: var(--color-primary-element-light-text);\n\tfont-size: 0.75em;\n\tfont-weight: 700;\n\tline-height: 1;\n}\n\n.cn-cell-renderer__avatar-name {\n\tmin-width: 0;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n\n/* A date that matched a rule is also set in a heavier weight, so the signal\n does not rest on colour alone. */\n.cn-cell-renderer__date--success,\n.cn-cell-renderer__date--warning,\n.cn-cell-renderer__date--error {\n\tfont-weight: 600;\n}\n\n.cn-cell-renderer__date--success {\n\tcolor: var(--color-text-success, var(--color-success-text));\n}\n\n.cn-cell-renderer__date--warning {\n\tcolor: var(--color-text-warning, var(--color-warning-text));\n}\n\n.cn-cell-renderer__date--error {\n\tcolor: var(--color-text-error, var(--color-error-text));\n}\n\n.cn-cell-renderer__icon--success {\n\tcolor: var(--color-success);\n}\n\n.cn-cell-renderer__swatch-wrap {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 8px;\n}\n\n.cn-cell-renderer__swatch-dot {\n\tdisplay: inline-block;\n\twidth: 12px;\n\theight: 12px;\n\tborder-radius: 50%;\n\tflex-shrink: 0;\n\tborder: 1px solid var(--color-border-dark);\n}\n</style>\n"],"names":["_createElementBlock","_normalizeClass","_createBlock","_resolveDynamicComponent","_mergeProps","_Fragment","_openBlock","_createVNode","_normalizeStyle","_createElementVNode","_toDisplayString"],"mappings":";;;;AAkBgB,EAAA,KAAK,EAAC,wBAAwB;;;;AAa9B,EAAA,KAAK,EAAC,wBAAwB;;;;;;AA+BH,EAAA,KAAK,EAAC,0BAA0B;;;;AAC7C,EAAA,KAAK,EAAC,kCAAiC;AAAE,EAAA,aAAW,EAAC,MAAM;;;;;AAezE,EAAA,KAAK,EAAC,wBAAwB;;;;;AAa9B,EAAA,KAAK,EAAC,wBAAwB;;;;;AAUrC,EAAA,KAAK,EAAC,+BAA+B;;;;;AAO7B,EAAA,KAAK,EAAC,wBAAwB;;;;;AAY/B,EAAA,KAAK,EAAC,wBAAwB;;;;;;AAc9B,EAAA,KAAK,EAAC,wBAAwB;;;;AAM9B,EAAA,KAAK,EAAC,wBAAwB;;;;AAS9B,EAAA,KAAK,EAAC,wBAAwB;;;;AAKN,EAAA,KAAK,EAAC,wBAAwB;;;;;;;;;;;;;uBAzJrEA,kBAmKM,CAAA,MAAA,EAAA;IAnKA,KAAK,EAAAC,cAAA,CAAA,CAAC,kBAAiB,EAAU,QAAS,CAAA,SAAA,CAAA,CAAA;;KAIxC,QAAe,CAAA,eAAA;sBAFtBC,WAOuB,CAAAC,uBAAA,CANjB,QAAe,CAAA,eAAA,CAAA,EADrBC,UAOuB,CAAA;;AAJrB,UAAA,KAAK,EAAE,MAAK,CAAA,KAAA;AACZ,UAAA,GAAG,EAAE,MAAG,CAAA,GAAA;AACR,UAAA,QAAQ,EAAE,MAAQ,CAAA,QAAA;AAClB,UAAA,SAAS,EAAE,QAAc,CAAA,cAAA;WAClB,MAAW,CAAA,WAAA,CAAA,EAAA,IAAA,EAAA,EAAA,mBAAA,CAAA,OAAA,EAAA,KAAA,EAAA,UAAA,EAAA,WAAA,CAAA,CAAA;SAGC;wBAArBJ,kBAMU,CAAAK,QAAA,EAAA,EAAA,GAAA,EAAA,CAAA,EAAA,EAAA;aALY,QAAQ,CAAA,QAAA;8BAA7BH,WAG4B,CAAA,wBAAA,EAAA;;kBAF1B,KAAK,EAAE,MAAM,CAAC,QAAc,CAAA,cAAA,CAAA;AAC5B,kBAAA,OAAO,EAAE,QAAY,CAAA,YAAA;AACrB,kBAAA,QAAQ,EAAE,QAAa,CAAA,aAAA;;8BACzBF,kBAAmD,CAAA,MAAA,EAAnD,UAAmD,EAAP,GAAC,CAAA,CAAA;;WAMzB;0BAArBA,kBAQU,CAAAK,QAAA,EAAA,EAAA,GAAA,EAAA,CAAA,EAAA,EAAA;eANF,QAAQ,CAAA,QAAA;gCADfH,WAKkE,CAAA,0BAAA,EAAA;;AAHhE,oBAAA,KAAK,EAAE,MAAK,CAAA,KAAA;AACZ,oBAAA,QAAQ,EAAG,CAAA,MAAA,CAAA,WAAU,IAAK,MAAA,CAAA,WAAW,CAAC,QAAQ,KAAA,EAAA;AAC9C,oBAAA,MAAM,EAAG,CAAA,MAAA,CAAA,eAAe,MAAA,CAAA,WAAW,CAAC,MAAM,KAAA,EAAA;AAC1C,oBAAA,UAAU,EAAG,CAAA,MAAA,CAAA,WAAY,IAAG,MAAA,CAAA,WAAW,CAAC,UAAU,KAAA,MAAA;;gCACpDF,kBAAmD,CAAA,MAAA,EAAnD,UAAmD,EAAP,GAAC,CAAA,CAAA;;aAQzB,MAAO,CAAA,MAAA,KAAA,MAAA;4BAA5BA,kBAgBU,CAAAK,QAAA,EAAA,EAAA,GAAA,EAAA,CAAA,EAAA,EAAA;iBAdF,QAAS,CAAA,SAAA;kCADhBH,WAKa,CAAA,sBAAA,EAAA;;AAHX,sBAAA,EAAE,EAAE,QAAS,CAAA,SAAA;AACd,sBAAA,KAAK,EAAC,wBAAwB;;uCAC9B,MAAmB;wDAAhB;;;;qBAGQ,QAAQ,CAAA,QAAA;oCADpBF,kBAOG,CAAA,GAAA,EAAA;;AALD,wBAAA,IAAI,EAAE,QAAQ,CAAA,QAAA;AACf,wBAAA,MAAM,EAAC,QAAO;AACd,wBAAA,GAAG,EAAC,UAAS;AACb,wBAAA,KAAK,EAAC,wBAAwB;yCAC3B;oCAEJA,kBAAyD,CAAA,MAAA,EAAA;;AAA3C,wBAAA,KAAK,EAAE,QAAQ,CAAA,QAAA;yCAAK,QAAa,CAAA,cAAA,CAAA,EAAA,CAAA,oBAAA,UAAA,CAAA,CAAA;;eAO3B,MAAK,CAAA,MAAA,KAAA,QAAA;8BAA1BA,kBAkBU,CAAAK,QAAA,EAAA,EAAA,GAAA,EAAA,CAAA,EAAA,EAAA;AAjBG,kBAAA,CAAA,QAAA,CAAA,UAAW,IAAG,QAAY,CAAA,YAAA;AAAtC,uBAAAC,SAAA,EAAA,EAAAN,kBAAA,CAeM,QAfN,UAeM,EAAA;yBAdO,QAAY,CAAA,YAAA;AAAxB,6BAAAM,SAAA,EAAA,EAAAN,kBAAA,CAOM,QAPN,UAOM,EAAA;8BANLO,WAKyB,CAAA,mBAAA,EAAA;AAJvB,gCAAA,IAAI,EAAE,QAAY,CAAA,YAAA;gCAClB,WAAW,EAAE,QAAS,CAAA,UAAA,IAAK,QAAY,CAAA,YAAA;AACvC,gCAAA,IAAI,EAAE,QAAU,CAAA,UAAA;AAChB,gCAAA,WAAW,EAAE,IAAI;AACjB,gCAAA,cAAc,EAAE,IAAI;;;0CAEvBP,kBAI8C,CAAA,MAAA,EAAA;;AAF7C,8BAAA,KAAK,EAAC,mCAAkC;8BACvC,KAAK,EAAAQ,cAAA,CAAA,EAAA,KAAA,EAAW,QAAS,CAAA,UAAA,GAAA,IAAA,EAAA,MAAA,EAAkB,QAAS,CAAA,UAAA,GAAA,IAAA,EAAA,CAAA;AACrD,8BAAA,aAAW,EAAC,MAAM;+CAAI,QAAa,CAAA,cAAA,CAAA,EAAA,CAAA,mBAAA,CAAA;wBACpCC,kBAAsH,CAAA,MAAA,EAAA;AAAhH,0BAAA,KAAK,EAAC;0BAAiC,KAAK,EAAE,QAAS,CAAA,UAAA,IAAK,QAAY,CAAA,YAAA;AAAK,yBAAA,EAAAC,eAAA,CAAA,QAAA,CAAA,UAAS,IAAK,QAAW,CAAA,YAAA,CAAA,EAAA,CAAA,oBAAA,UAAA,CAAA;;oCAE7GV,kBAAmD,CAAA,MAAA,EAAnD,UAAmD,EAAP,GAAC,CAAA,CAAA;;iBAMzB,MAAO,CAAA,MAAA,KAAA,MAAA;gCAA5BA,kBAQU,CAAAK,QAAA,EAAA,EAAA,GAAA,EAAA,CAAA,EAAA,EAAA;qBANF,QAAa,CAAA,aAAA;sCADpBL,kBAKwC,CAAA,MAAA,EAAA;;AAHvC,0BAAA,KAAK,EAACC,cAAA,CAAA,CAAA,wBAAuB,EACrB,QAAA,CAAA,WAAU,gCAAiC,QAAY,CAAA,WAAA,GAAA,IAAA,CAAA,CAAA;0BAC9D,QAAQ,EAAE,QAAa,CAAA,aAAA,CAAC,WAAW,EAAA;AACnC,0BAAA,KAAK,EAAE,QAAQ,CAAA,QAAA;2CAAK,QAAU,CAAA,SAAA,CAAA,EAAA,EAAA,2BAAA,UAAA,CAAA;sCAChCD,kBAAmD,CAAA,MAAA,EAAnD,WAAmD,EAAP,GAAC,CAAA,CAAA;;mBAIzB,QAAY,CAAA,YAAA;kCAChCA,kBAAkD,CAAA,MAAA,EAAA;;AAA3C,sBAAA,KAAK,EAAE,QAAQ,CAAA,QAAA;uCAAK;qBAIP,QAAQ,CAAA,QAAA;AAC5B,uBAAAM,SAAA,EAAA,EAAAN,kBAAA,CAQM,QARN,WAQM,EAAA;yBANE,QAAW,CAAA,WAAA;0CADlBA,kBAIqB,CAAA,MAAA,EAAA;;AAFpB,8BAAA,KAAK,EAAC,8BAA6B;AAClC,8BAAA,KAAK,oCAAqB;AAC3B,8BAAA,aAAW,EAAC;;;yBACD,QAAQ,CAAA,QAAA;0CAApBA,kBAAkE,CAAA,MAAA,EAAA;;AAA3C,8BAAA,KAAK,EAAE,QAAQ,CAAA,QAAA;+CAAK,QAAe,CAAA,cAAA,CAAA,EAAA,CAAA,oBAAA,WAAA,CAAA;0CAC1DA,kBAAmD,CAAA,MAAA,EAAnD,WAAmD,EAAP,GAAC,CAAA,CAAA;;uBAK1B,QAAgB,CAAA,gBAAA;sCACpCA,kBAAmF,CAAA,MAAA,EAAA;;AAA5E,0BAAA,KAAK,EAAE,QAAQ,CAAA,QAAA;AAAE,0BAAA,KAAK,EAAC,0BAA0B;2CAAI,QAAa,CAAA,cAAA,CAAA,EAAA,CAAA,oBAAA,WAAA,CAAA;yBAIrD,QAAM,CAAA,MAAA;wCAA3BA,kBAGU,CAAAK,QAAA,EAAA,EAAA,GAAA,EAAA,CAAA,EAAA,EAAA;6BAFS,QAAa,CAAA,aAAA;8CAA/BH,WAA6D,CAAA,qBAAA,EAAA;;AAA3B,kCAAA,SAAS,EAAE,QAAa,CAAA,aAAA;;8CAC1DF,kBAAmD,CAAA,MAAA,EAAnD,WAAmD,EAAP,GAAC,CAAA,CAAA;;2BAIzB,QAAK,CAAA,KAAA;0CAA1BA,kBAWU,CAAAK,QAAA,EAAA,EAAA,GAAA,EAAA,EAAA,EAAA,EAAA;+BATF,QAAO,CAAA,OAAA;gDADdL,kBAOG,CAAA,GAAA,EAAA;;AALD,oCAAA,IAAI,EAAE,QAAO,CAAA,OAAA;AACd,oCAAA,MAAM,EAAC,QAAO;AACd,oCAAA,GAAG,EAAC,UAAS;AACb,oCAAA,KAAK,EAAC,wBAAwB;qDAC3B;mCAEa,QAAQ,CAAA,QAAA;kDAAzBA,kBAAuE,CAAA,MAAA,EAAA;;AAA3C,sCAAA,KAAK,EAAE,QAAQ,CAAA,QAAA;uDAAK,QAAa,CAAA,cAAA,CAAA,EAAA,CAAA,oBAAA,WAAA,CAAA;kDAC7DA,kBAAmD,CAAA,MAAA,EAAnD,WAAmD,EAAP,GAAC,CAAA,CAAA;;6BAIzB;4CAArBA,kBAGU,CAAAK,QAAA,EAAA,EAAA,GAAA,EAAA,EAAA,EAAA,EAAA;iCAFQ,MAAK,CAAA,KAAA;kDAAtBH,WAAmG,CAAA,oBAAA,EAAA;;AAA1E,sCAAA,IAAI,EAAE,EAAE;AAAE,sCAAA,KAAK,EAAC;;kDACzCF,kBAAmD,CAAA,MAAA,EAAnD,WAAmD,EAAP,GAAC,CAAA,CAAA;;+BAIzB,QAAM,CAAA,MAAA;8CAA3BA,kBAMU,CAAAK,QAAA,EAAA,EAAA,GAAA,EAAA,EAAA,EAAA,EAAA;mCALY,MAAK,CAAA,KAAA;oDAA1BH,WAG2B,CAAA,wBAAA,EAAA;;AAFzB,wCAAA,KAAK,EAAE,QAAS,CAAA,SAAA;wCAChB,QAAQ,EAAE,MAAM,CAAC,MAAK,CAAA,KAAA,CAAA;AACtB,wCAAA,QAAQ,EAAE,QAAY,CAAA,YAAA;;oDACxBF,kBAAmD,CAAA,MAAA,EAAnD,WAAmD,EAAP,GAAC,CAAA,CAAA;;iCAIzB,QAAW,CAAA,YAAA,KAAA,OAAA;gDAAhCA,kBAKU,CAAAK,QAAA,EAAA,EAAA,GAAA,EAAA,EAAA,EAAA,EAAA;sCAJI,MAAI,CAAA,KAAA,IAAA,CAAM,MAAK,CAAA,KAAA,CAAC,MAAM;sDAAnCL,kBAA2E,CAAA,MAAA,EAA3E,WAA2E,EAAP,GAAC,CAAA;sDACrEA,kBAEM,CAAA,MAAA,EAAA;;0CAFQ,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,MAAK,CAAA,KAAA,CAAA,GAAI,MAAK,CAAA,KAAA,CAAC,IAAI,CAAA,IAAA,CAAA,GAAA,EAAA;2DAClD;;gDAMJA,kBAAkD,CAAA,MAAA,EAAA;;AAA3C,oCAAA,KAAK,EAAE,QAAQ,CAAA,QAAA;qDAAK;;;;;;"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { NcActions, NcActionButton, NcActionLink } from '@nextcloud/vue';
|
|
2
2
|
import { CTX_MENU_DATA_ATTR, CTX_MENU_POPPER_ATTR } from '../../composables/useContextMenu.js';
|
|
3
3
|
import { resolveItemActionLink, followItemActionLink } from '../../utils/actionLink.js';
|
|
4
|
+
import { isModifiedClick } from '../../utils/linkNavigation.js';
|
|
4
5
|
import __sfc_main from '../CnIcon/CnIcon.vue.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -400,13 +401,17 @@ var script = {
|
|
|
400
401
|
/**
|
|
401
402
|
* A link entry was clicked: route a plain in-app click, leave the rest to
|
|
402
403
|
* the browser, and emit `action` as a button would. The `handler` is not
|
|
403
|
-
* called, since navigating is what the link already does.
|
|
404
|
+
* called, since navigating is what the link already does. A modified
|
|
405
|
+
* click opens a new tab, so nothing is emitted in this one.
|
|
404
406
|
*
|
|
405
407
|
* @param {object} action The action descriptor.
|
|
406
408
|
* @param {object} link The resolved link.
|
|
407
409
|
* @param {MouseEvent} event The click event.
|
|
408
410
|
*/
|
|
409
411
|
onLinkAction(action, link, event) {
|
|
412
|
+
if (isModifiedClick(event)) {
|
|
413
|
+
return
|
|
414
|
+
}
|
|
410
415
|
followItemActionLink(event, link, this.$router);
|
|
411
416
|
this.$emit('action', { action: action.label, row: this.targetItem });
|
|
412
417
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnContextMenu.vue2.js","sources":["../../../../src/components/CnContextMenu/CnContextMenu.vue"],"sourcesContent":["<template>\n\t<div class=\"cn-context-menu-root\">\n\t\t<NcActions\n\t\t\tv-if=\"!activePanel\"\n\t\t\tref=\"actions\"\n\t\t\tv-model:open=\"internalOpen\"\n\t\t\t:manualOpen=\"true\"\n\t\t\t:forceMenu=\"true\"\n\t\t\tclass=\"cn-context-menu\"\n\t\t\tcontainer=\"body\"\n\t\t\tdata-testid=\"cn-context-menu\"\n\t\t\t@close=\"onClose\"\n\t\t\t@closed=\"onClosed\">\n\t\t\t<!-- Dynamic actions from array prop -->\n\t\t\t<template v-for=\"{ action, link } in renderedActions\" :key=\"action.label\">\n\t\t\t\t<!-- A navigate-only action is a real link, so it can be middle-clicked, opened in a new tab or copied. -->\n\t\t\t\t<NcActionLink\n\t\t\t\t\tv-if=\"link\"\n\t\t\t\t\t:href=\"link.href\"\n\t\t\t\t\t:target=\"link.target\"\n\t\t\t\t\t:title=\"resolveTitle(action)\"\n\t\t\t\t\t:class=\"{ 'cn-row-action--destructive': action.destructive }\"\n\t\t\t\t\t:data-testid=\"`cn-action-item-${slugifyLabel(action.label)}`\"\n\t\t\t\t\tcloseAfterClick\n\t\t\t\t\t@click=\"onLinkAction(action, link, $event)\">\n\t\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t\t<CnIcon v-if=\"typeof action.icon === 'string'\" :name=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t\t<component :is=\"action.icon\" v-else :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ action.label }}\n\t\t\t\t</NcActionLink>\n\t\t\t\t<NcActionButton\n\t\t\t\t\tv-else\n\t\t\t\t\t:title=\"resolveTitle(action)\"\n\t\t\t\t\t:disabled=\"resolveDisabled(action)\"\n\t\t\t\t\t:class=\"{ 'cn-row-action--destructive': action.destructive }\"\n\t\t\t\t\t:data-testid=\"`cn-action-item-${slugifyLabel(action.label)}`\"\n\t\t\t\t\tcloseAfterClick\n\t\t\t\t\t@click=\"onAction(action)\">\n\t\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t\t<CnIcon v-if=\"typeof action.icon === 'string'\" :name=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t\t<component :is=\"action.icon\" v-else :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ action.label }}\n\t\t\t\t</NcActionButton>\n\t\t\t</template>\n\n\t\t\t<!--\n\t\t\t@slot default\n\t\t\t@description Custom NcActionButton-family content rendered inside the\n\t\t\tNcActions menu. Use this for hardcoded buttons (Keepiq pattern) when\n\t\t\tthe `actions` prop is empty.\n\t\t-->\n\t\t\t<slot />\n\t\t</NcActions>\n\n\t\t<!-- CUSTOM PANEL — bypass NcActions, render arbitrary slot content\n\t\t anchored at the cursor. The `#panel:<name>` slot may contain any\n\t\t markup (grids, inputs, custom components) without the NcActions\n\t\t child-allowlist filter. -->\n\t\t<template v-if=\"activePanel && internalOpen\">\n\t\t\t<div\n\t\t\t\tclass=\"cn-context-menu__backdrop\"\n\t\t\t\t@click=\"onClose\"\n\t\t\t\t@contextmenu.prevent=\"onClose\" />\n\t\t\t<div\n\t\t\t\tref=\"panel\"\n\t\t\t\tclass=\"cn-context-menu__panel\"\n\t\t\t\trole=\"menu\"\n\t\t\t\ttabindex=\"-1\"\n\t\t\t\tdata-testid=\"cn-context-menu-panel\"\n\t\t\t\t:data-panel=\"activePanel\"\n\t\t\t\t@keydown.esc.stop=\"onClose\"\n\t\t\t\t@click.stop>\n\t\t\t\t<!-- @slot panel:<name> Free-form custom panel content shown when\n\t\t\t\t `activePanel === '<name>'`. Bypasses the NcActions child\n\t\t\t\t allowlist — put any markup here (grids, inputs, custom\n\t\t\t\t components, etc.). The slot name is dynamic: define one\n\t\t\t\t `#panel:<name>` per panel you want to support. -->\n\t\t\t\t<!-- @binding {Function} back Clear `activePanel`, returning to the default action list. -->\n\t\t\t\t<!-- @binding {Function} close Close the entire menu, equivalent to clicking outside. -->\n\t\t\t\t<!-- @binding {*} targetItem The right-clicked item, forwarded from the `targetItem` prop. -->\n\t\t\t\t<slot\n\t\t\t\t\t:name=\"`panel:${activePanel}`\"\n\t\t\t\t\t:back=\"back\"\n\t\t\t\t\t:close=\"onClose\"\n\t\t\t\t\t:targetItem=\"targetItem\" />\n\t\t\t</div>\n\t\t</template>\n\t</div>\n</template>\n\n<script>\nimport { NcActionButton, NcActionLink, NcActions } from '@nextcloud/vue'\nimport { CTX_MENU_DATA_ATTR, CTX_MENU_POPPER_ATTR } from '../../composables/useContextMenu.js'\nimport { followItemActionLink, resolveItemActionLink } from '../../utils/actionLink.js'\nimport { CnIcon } from '../CnIcon/index.js'\n\n/**\n * CnContextMenu — Right-click context menu wrapper around NcActions.\n *\n * Provides a pre-configured NcActions instance that positions itself at the\n * cursor via the `useContextMenu` composable's CSS custom properties. Accepts\n * an `actions` array for the common dynamic case (like CnRowActions), and a\n * default slot for hardcoded NcActionButton content.\n *\n * Pair with `useContextMenu()` for state management (open/close, target item,\n * cursor positioning). The composable handles the DOM attributes; this component\n * handles the NcActions template boilerplate.\n *\n * Dynamic actions (CnIndexPage pattern)\n * ```vue\n * <CnContextMenu\n * v-model:open=\"contextMenuOpen\"\n * :actions=\"mergedActions\"\n * :target-item=\"contextMenuRow\"\n * @action=\"$emit('action', $event)\"\n * @close=\"closeContextMenu\" />\n * ```\n *\n * Custom buttons via slot (Keepiq pattern)\n * ```vue\n * <CnContextMenu\n * v-model:open=\"contextMenuOpen\"\n * @close=\"closeContextMenu\">\n * <NcActionButton close-after-click @click=\"onRename\">\n * <template #icon><PencilIcon :size=\"20\" /></template>\n * Rename\n * </NcActionButton>\n * </CnContextMenu>\n * ```\n */\nexport default {\n\tname: 'CnContextMenu',\n\n\tcomponents: {\n\t\tNcActions,\n\t\tNcActionButton,\n\t\tNcActionLink,\n\t\tCnIcon,\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Whether the context menu is open. Bind with `v-model:open` — the `.sync`\n\t\t * modifier it replaces was removed in Vue 3 and binds nothing.\n\t\t * Bind to `useContextMenu().isOpen`.\n\t\t */\n\t\topen: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Action definitions rendered as NcActionButton items.\n\t\t * Same format as CnRowActions: `{ label, icon?, handler?, disabled?, visible?, title?, destructive? }`.\n\t\t * `icon` accepts either a component (rendered directly) or a string —\n\t\t * a string is treated as an MDI name and rendered via `CnIcon` (e.g.\n\t\t * `\"Eye\"`), which lets manifest (JSON) actions declare icons by name.\n\t\t * `visible` (boolean | (targetItem) => boolean) hides the entry when falsy\n\t\t * (default: shown). `title` (string | (targetItem) => string) renders as\n\t\t * a native tooltip — useful for explaining why an entry is disabled.\n\t\t * `href` (URL) or `to` (vue-router location), each a value or a\n\t\t * `(targetItem) => …` function, render the entry as a real link, with\n\t\t * `linkTarget` as its `target`; a link emits `action` but does not call\n\t\t * `handler`. When the entire array is empty (or all entries are filtered\n\t\t * out), only the default slot content is rendered.\n\t\t *\n\t\t * @type {Array<{label: string, icon: object | string, handler: (targetItem: object) => void, disabled: boolean | ((targetItem: object) => boolean), visible: boolean | ((targetItem: object) => boolean), title: string | ((targetItem: object) => string), destructive: boolean, href: string | ((targetItem: object) => string), to: string | object | ((targetItem: object) => string | object), linkTarget: string}>}\n\t\t */\n\t\tactions: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * The right-clicked item (row, folder, etc.). Passed to action `handler`\n\t\t * and `disabled` callbacks, and included in the `action` event payload.\n\t\t * Bind to `useContextMenu().targetItem`.\n\t\t */\n\t\ttargetItem: {\n\t\t\ttype: [Object, String, Number],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Name of the currently-active custom panel. When set (and the menu is\n\t\t * open), the default NcActions list is replaced by the matching\n\t\t * `#panel:<name>` slot, rendered free-form (no NcActions child filter).\n\t\t * Bind with `v-model:active-panel` — the component emits `update:activePanel(null)` when\n\t\t * the panel's `back()` binding is invoked or the menu closes.\n\t\t *\n\t\t * @type {string|null}\n\t\t */\n\t\tactivePanel: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: ['action', 'close', 'closed', 'update:activePanel', 'update:open'],\n\n\tdata() {\n\t\treturn {\n\t\t\tinternalOpen: this.open,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Filter actions by their `visible` predicate. Entries without\n\t\t * `visible` are always shown (backwards compatible).\n\t\t *\n\t\t * @return {Array} Visible actions for the current targetItem.\n\t\t */\n\t\tvisibleActions() {\n\t\t\treturn this.actions.filter((action) => {\n\t\t\t\tif (action.visible === undefined) {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tif (typeof action.visible === 'function') {\n\t\t\t\t\treturn !!action.visible(this.targetItem)\n\t\t\t\t}\n\t\t\t\treturn !!action.visible\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Visible actions paired with the link each renders as (null for a button).\n\t\t *\n\t\t * @return {Array<{action: object, link: object|null}>}\n\t\t */\n\t\trenderedActions() {\n\t\t\treturn this.visibleActions.map((action) => ({\n\t\t\t\taction,\n\t\t\t\tlink: this.resolveDisabled(action) ? null : resolveItemActionLink(action, this.targetItem, this.$router),\n\t\t\t}))\n\t\t},\n\t},\n\n\twatch: {\n\t\topen(val) {\n\t\t\tthis.internalOpen = val\n\t\t},\n\n\t\tinternalOpen(val) {\n\t\t\t// Description goes ABOVE `@event`, not inline after it:\n\t\t\t// vue-docgen-api's event-name splitter stops at the first `:`, so\n\t\t\t// `@event update:open <description>` is read as one long event NAME\n\t\t\t// and the generated docs show an empty description.\n\t\t\t/**\n\t\t\t * Fired whenever the menu's internal open state flips. Used by\n\t\t\t * callers that bind `v-model:open` to track visibility.\n\t\t\t *\n\t\t\t * @event update:open\n\t\t\t * @type {boolean}\n\t\t\t */\n\t\t\tthis.$emit('update:open', val)\n\n\t\t\tif (val) {\n\t\t\t\tthis.tagPopper()\n\t\t\t\tthis.startOutsideWatch()\n\t\t\t} else {\n\t\t\t\tthis.stopOutsideWatch()\n\t\t\t\t// NcActions' `@closed` never fires (see `onClosed`), so drop the\n\t\t\t\t// \"a context menu is open\" attribute here. The CSS vars stay put\n\t\t\t\t// — the hide animation still needs the transform, and the next\n\t\t\t\t// `open()` overwrites them before the popper shows again.\n\t\t\t\tdocument.documentElement.removeAttribute(CTX_MENU_DATA_ATTR)\n\t\t\t}\n\t\t},\n\t},\n\n\tmounted() {\n\t\t// NcActions renders its own NcButton as the popover trigger; the menu\n\t\t// opens only via right-click (via `:open.sync`), so the button is\n\t\t// offscreen-positioned (see the `.cn-context-menu` rule below).\n\t\t//\n\t\t// Use `inert` rather than `aria-hidden`: when the menu is dismissed\n\t\t// without picking an item, NcPopover's focus-trap restores focus to the\n\t\t// trigger button. With `aria-hidden` that produces a \"Blocked\n\t\t// aria-hidden on an element because its descendant retained focus\"\n\t\t// warning (focused element must not be hidden from AT). `inert` both\n\t\t// hides the button from the accessibility tree *and* makes it\n\t\t// unfocusable, so the trap's focus restore falls back to <body> instead\n\t\t// of landing on a hidden element. (The button never needs focus — the\n\t\t// menu opens via `:open.sync`, not by focusing the trigger.)\n\t\tconst trigger = this.$el?.querySelector('.action-item__menutoggle')\n\t\tif (trigger) {\n\t\t\ttrigger.setAttribute('inert', '')\n\t\t}\n\n\t\t// Silence NcPopover's dev-mode a11y warning for this specific instance.\n\t\t//\n\t\t// NcPopover (when `window.OC?.debug` is on) calls\n\t\t// `tabbable(triggerContainer)[0]` on every open and warns\n\t\t// \"It looks like you are using a custom button as a <NcPopover>\n\t\t// trigger…\" if nothing tabbable is found. We *are* using NcButton —\n\t\t// the warning's escape hatch — but the button is offscreen-positioned\n\t\t// and tabbable's visibility checks won't reliably find it across\n\t\t// browsers (Chromium's `Element.checkVisibility()` excludes some\n\t\t// offscreen / clipped elements). The advice in the warning (\"bind\n\t\t// #trigger slot attrs\") doesn't apply: NcActions controls its own\n\t\t// trigger and doesn't expose a `#trigger` slot to override.\n\t\t//\n\t\t// Neutralising the check on our own NcPopover instance (one level\n\t\t// down inside NcActions) leaves every other popover untouched and\n\t\t// keeps the check live for real custom triggers elsewhere.\n\t\t//\n\t\t// Vue 3: `this.$children` was removed (it hard-errors under @vue/compat\n\t\t// MODE 3 with \"INSTANCE_CHILDREN compat has been disabled\"), so reach the\n\t\t// NcActions instance through an explicit template ref instead.\n\t\tconst ncActions = this.$refs.actions\n\t\tconst ncPopover = ncActions?.$refs?.popover\n\t\tif (ncPopover) {\n\t\t\tncPopover.checkTriggerA11y = () => {}\n\t\t}\n\n\t\tthis.tagPopper()\n\n\t\t// Mounting with `open` already true never trips the watcher.\n\t\tif (this.internalOpen) {\n\t\t\tthis.startOutsideWatch()\n\t\t}\n\t},\n\n\tbeforeUnmount() {\n\t\tthis.stopOutsideWatch()\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Mark this menu's own popper element so the cursor-positioning CSS in\n\t\t * `src/css/context-menu.css` can target it — and nothing else.\n\t\t *\n\t\t * That CSS used to key on the `<html>` data attribute alone, which\n\t\t * matches *every* `.v-popper__popper` in the document: any other popover\n\t\t * alive while a context menu was open (a table row's actions menu, most\n\t\t * visibly) got dragged to the last right-click coordinates.\n\t\t *\n\t\t * The marker has to be applied imperatively — NcActions hardcodes\n\t\t * `popoverBaseClass: 'action-item__popper'` on its NcPopover and spreads\n\t\t * no `$attrs` onto it, so there is no prop to pass one in through.\n\t\t *\n\t\t * It must be an *attribute*, not a class. floating-vue binds a dynamic\n\t\t * `class` on this element (`--shown` / `--hidden` / `--show-from` / …)\n\t\t * and Vue's `patchClass` assigns `el.className` wholesale, so a class\n\t\t * added here is wiped the first time the popper opens — the rule then\n\t\t * matches nothing and the menu renders at its offscreen trigger. Vue\n\t\t * only patches props present in the vnode, so an attribute it never\n\t\t * renders is left alone.\n\t\t *\n\t\t * The element itself is stable: floating-vue keeps one `$_popperNode`\n\t\t * and `appendChild`s it into the container on first show, so marking it\n\t\t * once at `mounted()` holds for the component's lifetime. Re-running on\n\t\t * open is a cheap, idempotent guard for a not-yet-ready ref chain.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\ttagPopper() {\n\t\t\tconst el = this.$refs.actions?.$refs?.popover?.getPopoverContentElement?.()\n\t\t\tel?.setAttribute?.(CTX_MENU_POPPER_ATTR, '')\n\t\t},\n\n\t\t/**\n\t\t * Start watching for a press outside the menu. Driven from both the\n\t\t * `internalOpen` watcher and `mounted()`, because a consumer may mount\n\t\t * with `open` already true — in which case there is no state transition\n\t\t * for the watcher to see.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tstartOutsideWatch() {\n\t\t\t// nextTick: the contextmenu/click that opened the menu is still\n\t\t\t// propagating and would dismiss it again on the same gesture.\n\t\t\tthis.$nextTick(() => {\n\t\t\t\tif (this.internalOpen) {\n\t\t\t\t\t// CAPTURE. A canvas that swallows its own mousedown never lets it\n\t\t\t\t\t// bubble to document, so a menu opened over one could not be\n\t\t\t\t\t// dismissed by clicking the canvas — Vue Flow's d3-zoom pane calls\n\t\t\t\t\t// stopImmediatePropagation, which is exactly that shape.\n\t\t\t\t\tdocument.addEventListener('mousedown', this.onDocumentMouseDown, true)\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Stop watching for outside presses.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tstopOutsideWatch() {\n\t\t\tdocument.removeEventListener('mousedown', this.onDocumentMouseDown, true)\n\t\t},\n\n\t\t/**\n\t\t * Close the menu when the press lands outside it.\n\t\t *\n\t\t * NcActions has to run in `manual-open` mode here (the menu is opened\n\t\t * from a right-click via the `open` prop, not by activating the trigger\n\t\t * button). `@nextcloud/vue` 9 couples that mode to\n\t\t * `noCloseOnClickOutside: this.manualOpen`, and NcPopover derives\n\t\t * `autoHide: !noCloseOnClickOutside && closeOnClickOutside` — so the\n\t\t * popper's own outside-click dismissal is switched off and nothing else\n\t\t * closes the menu. v8 only cleared the popper's open `triggers`, leaving\n\t\t * autoHide intact, which is why dismissing worked before the Vue 3\n\t\t * upgrade. NcActions spreads no `$attrs` onto NcPopover, so the prop\n\t\t * cannot be overridden from outside — hence handling it here.\n\t\t *\n\t\t * @param {MouseEvent} event The document mousedown.\n\t\t * @return {void}\n\t\t */\n\t\tonDocumentMouseDown(event) {\n\t\t\tif (!this.internalOpen || this.isInsideMenu(event.target)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.onClose()\n\t\t},\n\n\t\t/**\n\t\t * Whether a node sits inside the menu's own surface — the custom panel,\n\t\t * or the popper NcActions teleports to `container=\"body\"` (so it is not\n\t\t * inside `this.$el` and `contains()` on the root would miss it).\n\t\t *\n\t\t * @param {EventTarget} target The event target to test.\n\t\t * @return {boolean} True when the target belongs to this menu.\n\t\t */\n\t\tisInsideMenu(target) {\n\t\t\tif (!(target instanceof Node)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif (this.$refs.panel?.contains(target)) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tconst content = this.$refs.actions?.$refs?.popover?.getPopoverContentElement?.()\n\t\t\tif (content?.contains(target)) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\t// Fallback for when the popover ref chain is unavailable —\n\t\t\t// `action-item__popper` is the base class NcActions gives its popper.\n\t\t\treturn target instanceof Element && !!target.closest('.action-item__popper')\n\t\t},\n\n\t\tresolveDisabled(action) {\n\t\t\tif (typeof action.disabled === 'function') {\n\t\t\t\treturn action.disabled(this.targetItem)\n\t\t\t}\n\t\t\treturn !!action.disabled\n\t\t},\n\n\t\t/**\n\t\t * Resolve the `title` field on an action descriptor — supports both\n\t\t * a static string and a function `(targetItem) => string`. Returns\n\t\t * undefined when no title is provided so the attribute isn't rendered.\n\t\t *\n\t\t * @param {object} action The action descriptor.\n\t\t * @return {string|undefined} The tooltip text, or undefined.\n\t\t */\n\t\tresolveTitle(action) {\n\t\t\tif (typeof action.title === 'function') {\n\t\t\t\treturn action.title(this.targetItem) || undefined\n\t\t\t}\n\t\t\treturn action.title || undefined\n\t\t},\n\n\t\t/**\n\t\t * Slugify an action label for use in stable `data-testid` selectors.\n\t\t * Lowercase, kebab-case, strip non-alphanumeric. Used solely by the\n\t\t * `:data-testid` binding on NcActionButton — does not affect runtime\n\t\t * behaviour or rendered text.\n\t\t *\n\t\t * @param {string} label The action's display label.\n\t\t * @return {string} kebab-case slug suitable for a testid suffix.\n\t\t */\n\t\tslugifyLabel(label) {\n\t\t\treturn String(label || '')\n\t\t\t\t.toLowerCase()\n\t\t\t\t.replace(/[^a-z0-9]+/g, '-')\n\t\t\t\t.replace(/^-+|-+$/g, '')\n\t\t},\n\n\t\tonAction(action) {\n\t\t\tif (action.handler && typeof action.handler === 'function') {\n\t\t\t\taction.handler(this.targetItem)\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event action User picked an entry from the menu. The action's own `handler(targetItem)` (when present) ran synchronously before this event fires; the event lets parents observe / log the choice.\n\t\t\t * @type {{ action: string, row: object|null }}\n\t\t\t */\n\t\t\tthis.$emit('action', { action: action.label, row: this.targetItem })\n\t\t},\n\n\t\t/**\n\t\t * A link entry was clicked: route a plain in-app click, leave the rest to\n\t\t * the browser, and emit `action` as a button would. The `handler` is not\n\t\t * called, since navigating is what the link already does.\n\t\t *\n\t\t * @param {object} action The action descriptor.\n\t\t * @param {object} link The resolved link.\n\t\t * @param {MouseEvent} event The click event.\n\t\t */\n\t\tonLinkAction(action, link, event) {\n\t\t\tfollowItemActionLink(event, link, this.$router)\n\t\t\tthis.$emit('action', { action: action.label, row: this.targetItem })\n\t\t},\n\n\t\tonClose() {\n\t\t\tthis.internalOpen = false\n\t\t\tif (this.activePanel) {\n\t\t\t\tthis.$emit('update:activePanel', null)\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event close Fired when the menu starts closing (before the popper's hide animation). This is the reliable one — prefer it over `closed`.\n\t\t\t */\n\t\t\tthis.$emit('close')\n\t\t},\n\n\t\t/**\n\t\t * Clear the active panel and return to the default action list.\n\t\t * Exposed to custom panel slots via the `back` scope binding.\n\t\t */\n\t\tback() {\n\t\t\t// Description above the tag — see the note on `update:open`.\n\t\t\t/**\n\t\t\t * Emitted with `null` to clear the active panel — fired by the\n\t\t\t * panel's `back()` binding and whenever the menu closes while a\n\t\t\t * panel is open. Bind with `v-model:active-panel`.\n\t\t\t *\n\t\t\t * @event update:activePanel\n\t\t\t * @type {null}\n\t\t\t */\n\t\t\tthis.$emit('update:activePanel', null)\n\t\t},\n\n\t\t/**\n\t\t * Fires after the popper's hide animation completes.\n\t\t *\n\t\t * IT MUST NOT CLEAR THE CURSOR POSITION, and it used to. This fires LATE\n\t\t * — a whole hide animation late — so a second click on the same target\n\t\t * reopens the menu before it arrives, and the tear-down then strips the\n\t\t * coordinates that reopen had just written. The transform resolves\n\t\t * against undefined vars and the menu lands in the corner, on every open\n\t\t * after the first.\n\t\t *\n\t\t * It was dead code when written: `@nextcloud/vue` 9 bound `onAfterClose`\n\t\t * against an NcPopover that only emitted `afterHide`. 9.11.0 binds\n\t\t * `onAfterHide`, so it runs now, and a tear-down nothing needed became a\n\t\t * bug. Nothing needs it still: positioning is scoped to this menu's own\n\t\t * popper (see `tagPopper`), the data attribute is dropped in the\n\t\t * `internalOpen` watcher, the next `open()` overwrites the vars, and the\n\t\t * composable's unmount hook clears them for good.\n\t\t */\n\t\tonClosed() {\n\t\t\t/**\n\t\t\t * @event closed Intended to fire after the popper's hide animation completes. Does not currently fire — `@nextcloud/vue` 9's NcActions listens for an `afterClose` event NcPopover never emits. Use `close` instead.\n\t\t\t */\n\t\t\tthis.$emit('closed')\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n/* Selector is deliberately two classes deep. `.cn-context-menu` lands on\n NcActions' root, which NcActions itself styles as `.action-item[data-v-…]\n { position: relative; display: inline-block }`. Scoped CSS appends the attr\n to the last compound only, so a bare `.cn-context-menu` tied that rule at\n specificity (0,2,0) and lost on injection order — the trigger stayed\n `position: relative`, so it kept its in-flow box (top/left just offset it\n visually) and showed up as a full-width ~34px-tall phantom row between the\n table and the pagination on CnIndexPage. Adding the parent class makes this\n (0,3,0) so it wins regardless of order. */\n.cn-context-menu-root > .cn-context-menu {\n\t/* Hide the NcActions trigger button — menu opens only via right-click.\n\t Off-screen rather than display:none / visibility:hidden so NcPopover's\n\t a11y check (`tabbable(triggerContainer)[0]`) still finds the NcButton\n\t and the \"custom button as a <NcPopover> trigger\" warning stays quiet.\n\t Avoid `clip` / `clip-path` here — modern browsers' `Element.checkVisibility()`,\n\t which tabbable calls into, treats those as invisible. Pure offscreen\n\t positioning leaves the element fully \"visible\" to layout while being\n\t off the user's screen. */\n\tposition: absolute;\n\ttop: -9999px;\n\tleft: -9999px;\n\topacity: 0;\n\tpointer-events: none;\n}\n\n.cn-context-menu__backdrop {\n\tposition: fixed;\n\tinset: 0;\n\tz-index: 9998;\n}\n\n.cn-context-menu__panel {\n\tposition: fixed;\n\ttop: var(--cn-ctx-menu-y, 0);\n\tleft: var(--cn-ctx-menu-x, 0);\n\tz-index: 9999;\n\tmin-width: 220px;\n\tpadding: 4px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-large, 12px);\n\tbox-shadow: 0 1px 5px rgba(0, 0, 0, 0.2);\n\toutline: none;\n}\n</style>\n"],"names":["CnIcon"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnContextMenu.vue2.js","sources":["../../../../src/components/CnContextMenu/CnContextMenu.vue"],"sourcesContent":["<template>\n\t<div class=\"cn-context-menu-root\">\n\t\t<NcActions\n\t\t\tv-if=\"!activePanel\"\n\t\t\tref=\"actions\"\n\t\t\tv-model:open=\"internalOpen\"\n\t\t\t:manualOpen=\"true\"\n\t\t\t:forceMenu=\"true\"\n\t\t\tclass=\"cn-context-menu\"\n\t\t\tcontainer=\"body\"\n\t\t\tdata-testid=\"cn-context-menu\"\n\t\t\t@close=\"onClose\"\n\t\t\t@closed=\"onClosed\">\n\t\t\t<!-- Dynamic actions from array prop -->\n\t\t\t<template v-for=\"{ action, link } in renderedActions\" :key=\"action.label\">\n\t\t\t\t<!-- A navigate-only action is a real link, so it can be middle-clicked, opened in a new tab or copied. -->\n\t\t\t\t<NcActionLink\n\t\t\t\t\tv-if=\"link\"\n\t\t\t\t\t:href=\"link.href\"\n\t\t\t\t\t:target=\"link.target\"\n\t\t\t\t\t:title=\"resolveTitle(action)\"\n\t\t\t\t\t:class=\"{ 'cn-row-action--destructive': action.destructive }\"\n\t\t\t\t\t:data-testid=\"`cn-action-item-${slugifyLabel(action.label)}`\"\n\t\t\t\t\tcloseAfterClick\n\t\t\t\t\t@click=\"onLinkAction(action, link, $event)\">\n\t\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t\t<CnIcon v-if=\"typeof action.icon === 'string'\" :name=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t\t<component :is=\"action.icon\" v-else :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ action.label }}\n\t\t\t\t</NcActionLink>\n\t\t\t\t<NcActionButton\n\t\t\t\t\tv-else\n\t\t\t\t\t:title=\"resolveTitle(action)\"\n\t\t\t\t\t:disabled=\"resolveDisabled(action)\"\n\t\t\t\t\t:class=\"{ 'cn-row-action--destructive': action.destructive }\"\n\t\t\t\t\t:data-testid=\"`cn-action-item-${slugifyLabel(action.label)}`\"\n\t\t\t\t\tcloseAfterClick\n\t\t\t\t\t@click=\"onAction(action)\">\n\t\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t\t<CnIcon v-if=\"typeof action.icon === 'string'\" :name=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t\t<component :is=\"action.icon\" v-else :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ action.label }}\n\t\t\t\t</NcActionButton>\n\t\t\t</template>\n\n\t\t\t<!--\n\t\t\t@slot default\n\t\t\t@description Custom NcActionButton-family content rendered inside the\n\t\t\tNcActions menu. Use this for hardcoded buttons (Keepiq pattern) when\n\t\t\tthe `actions` prop is empty.\n\t\t-->\n\t\t\t<slot />\n\t\t</NcActions>\n\n\t\t<!-- CUSTOM PANEL — bypass NcActions, render arbitrary slot content\n\t\t anchored at the cursor. The `#panel:<name>` slot may contain any\n\t\t markup (grids, inputs, custom components) without the NcActions\n\t\t child-allowlist filter. -->\n\t\t<template v-if=\"activePanel && internalOpen\">\n\t\t\t<div\n\t\t\t\tclass=\"cn-context-menu__backdrop\"\n\t\t\t\t@click=\"onClose\"\n\t\t\t\t@contextmenu.prevent=\"onClose\" />\n\t\t\t<div\n\t\t\t\tref=\"panel\"\n\t\t\t\tclass=\"cn-context-menu__panel\"\n\t\t\t\trole=\"menu\"\n\t\t\t\ttabindex=\"-1\"\n\t\t\t\tdata-testid=\"cn-context-menu-panel\"\n\t\t\t\t:data-panel=\"activePanel\"\n\t\t\t\t@keydown.esc.stop=\"onClose\"\n\t\t\t\t@click.stop>\n\t\t\t\t<!-- @slot panel:<name> Free-form custom panel content shown when\n\t\t\t\t `activePanel === '<name>'`. Bypasses the NcActions child\n\t\t\t\t allowlist — put any markup here (grids, inputs, custom\n\t\t\t\t components, etc.). The slot name is dynamic: define one\n\t\t\t\t `#panel:<name>` per panel you want to support. -->\n\t\t\t\t<!-- @binding {Function} back Clear `activePanel`, returning to the default action list. -->\n\t\t\t\t<!-- @binding {Function} close Close the entire menu, equivalent to clicking outside. -->\n\t\t\t\t<!-- @binding {*} targetItem The right-clicked item, forwarded from the `targetItem` prop. -->\n\t\t\t\t<slot\n\t\t\t\t\t:name=\"`panel:${activePanel}`\"\n\t\t\t\t\t:back=\"back\"\n\t\t\t\t\t:close=\"onClose\"\n\t\t\t\t\t:targetItem=\"targetItem\" />\n\t\t\t</div>\n\t\t</template>\n\t</div>\n</template>\n\n<script>\nimport { NcActionButton, NcActionLink, NcActions } from '@nextcloud/vue'\nimport { CTX_MENU_DATA_ATTR, CTX_MENU_POPPER_ATTR } from '../../composables/useContextMenu.js'\nimport { followItemActionLink, resolveItemActionLink } from '../../utils/actionLink.js'\nimport { isModifiedClick } from '../../utils/linkNavigation.js'\nimport { CnIcon } from '../CnIcon/index.js'\n\n/**\n * CnContextMenu — Right-click context menu wrapper around NcActions.\n *\n * Provides a pre-configured NcActions instance that positions itself at the\n * cursor via the `useContextMenu` composable's CSS custom properties. Accepts\n * an `actions` array for the common dynamic case (like CnRowActions), and a\n * default slot for hardcoded NcActionButton content.\n *\n * Pair with `useContextMenu()` for state management (open/close, target item,\n * cursor positioning). The composable handles the DOM attributes; this component\n * handles the NcActions template boilerplate.\n *\n * Dynamic actions (CnIndexPage pattern)\n * ```vue\n * <CnContextMenu\n * v-model:open=\"contextMenuOpen\"\n * :actions=\"mergedActions\"\n * :target-item=\"contextMenuRow\"\n * @action=\"$emit('action', $event)\"\n * @close=\"closeContextMenu\" />\n * ```\n *\n * Custom buttons via slot (Keepiq pattern)\n * ```vue\n * <CnContextMenu\n * v-model:open=\"contextMenuOpen\"\n * @close=\"closeContextMenu\">\n * <NcActionButton close-after-click @click=\"onRename\">\n * <template #icon><PencilIcon :size=\"20\" /></template>\n * Rename\n * </NcActionButton>\n * </CnContextMenu>\n * ```\n */\nexport default {\n\tname: 'CnContextMenu',\n\n\tcomponents: {\n\t\tNcActions,\n\t\tNcActionButton,\n\t\tNcActionLink,\n\t\tCnIcon,\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Whether the context menu is open. Bind with `v-model:open` — the `.sync`\n\t\t * modifier it replaces was removed in Vue 3 and binds nothing.\n\t\t * Bind to `useContextMenu().isOpen`.\n\t\t */\n\t\topen: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Action definitions rendered as NcActionButton items.\n\t\t * Same format as CnRowActions: `{ label, icon?, handler?, disabled?, visible?, title?, destructive? }`.\n\t\t * `icon` accepts either a component (rendered directly) or a string —\n\t\t * a string is treated as an MDI name and rendered via `CnIcon` (e.g.\n\t\t * `\"Eye\"`), which lets manifest (JSON) actions declare icons by name.\n\t\t * `visible` (boolean | (targetItem) => boolean) hides the entry when falsy\n\t\t * (default: shown). `title` (string | (targetItem) => string) renders as\n\t\t * a native tooltip — useful for explaining why an entry is disabled.\n\t\t * `href` (URL) or `to` (vue-router location), each a value or a\n\t\t * `(targetItem) => …` function, render the entry as a real link, with\n\t\t * `linkTarget` as its `target`; a link emits `action` but does not call\n\t\t * `handler`. When the entire array is empty (or all entries are filtered\n\t\t * out), only the default slot content is rendered.\n\t\t *\n\t\t * @type {Array<{label: string, icon: object | string, handler: (targetItem: object) => void, disabled: boolean | ((targetItem: object) => boolean), visible: boolean | ((targetItem: object) => boolean), title: string | ((targetItem: object) => string), destructive: boolean, href: string | ((targetItem: object) => string), to: string | object | ((targetItem: object) => string | object), linkTarget: string}>}\n\t\t */\n\t\tactions: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * The right-clicked item (row, folder, etc.). Passed to action `handler`\n\t\t * and `disabled` callbacks, and included in the `action` event payload.\n\t\t * Bind to `useContextMenu().targetItem`.\n\t\t */\n\t\ttargetItem: {\n\t\t\ttype: [Object, String, Number],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Name of the currently-active custom panel. When set (and the menu is\n\t\t * open), the default NcActions list is replaced by the matching\n\t\t * `#panel:<name>` slot, rendered free-form (no NcActions child filter).\n\t\t * Bind with `v-model:active-panel` — the component emits `update:activePanel(null)` when\n\t\t * the panel's `back()` binding is invoked or the menu closes.\n\t\t *\n\t\t * @type {string|null}\n\t\t */\n\t\tactivePanel: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: ['action', 'close', 'closed', 'update:activePanel', 'update:open'],\n\n\tdata() {\n\t\treturn {\n\t\t\tinternalOpen: this.open,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Filter actions by their `visible` predicate. Entries without\n\t\t * `visible` are always shown (backwards compatible).\n\t\t *\n\t\t * @return {Array} Visible actions for the current targetItem.\n\t\t */\n\t\tvisibleActions() {\n\t\t\treturn this.actions.filter((action) => {\n\t\t\t\tif (action.visible === undefined) {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tif (typeof action.visible === 'function') {\n\t\t\t\t\treturn !!action.visible(this.targetItem)\n\t\t\t\t}\n\t\t\t\treturn !!action.visible\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Visible actions paired with the link each renders as (null for a button).\n\t\t *\n\t\t * @return {Array<{action: object, link: object|null}>}\n\t\t */\n\t\trenderedActions() {\n\t\t\treturn this.visibleActions.map((action) => ({\n\t\t\t\taction,\n\t\t\t\tlink: this.resolveDisabled(action) ? null : resolveItemActionLink(action, this.targetItem, this.$router),\n\t\t\t}))\n\t\t},\n\t},\n\n\twatch: {\n\t\topen(val) {\n\t\t\tthis.internalOpen = val\n\t\t},\n\n\t\tinternalOpen(val) {\n\t\t\t// Description goes ABOVE `@event`, not inline after it:\n\t\t\t// vue-docgen-api's event-name splitter stops at the first `:`, so\n\t\t\t// `@event update:open <description>` is read as one long event NAME\n\t\t\t// and the generated docs show an empty description.\n\t\t\t/**\n\t\t\t * Fired whenever the menu's internal open state flips. Used by\n\t\t\t * callers that bind `v-model:open` to track visibility.\n\t\t\t *\n\t\t\t * @event update:open\n\t\t\t * @type {boolean}\n\t\t\t */\n\t\t\tthis.$emit('update:open', val)\n\n\t\t\tif (val) {\n\t\t\t\tthis.tagPopper()\n\t\t\t\tthis.startOutsideWatch()\n\t\t\t} else {\n\t\t\t\tthis.stopOutsideWatch()\n\t\t\t\t// NcActions' `@closed` never fires (see `onClosed`), so drop the\n\t\t\t\t// \"a context menu is open\" attribute here. The CSS vars stay put\n\t\t\t\t// — the hide animation still needs the transform, and the next\n\t\t\t\t// `open()` overwrites them before the popper shows again.\n\t\t\t\tdocument.documentElement.removeAttribute(CTX_MENU_DATA_ATTR)\n\t\t\t}\n\t\t},\n\t},\n\n\tmounted() {\n\t\t// NcActions renders its own NcButton as the popover trigger; the menu\n\t\t// opens only via right-click (via `:open.sync`), so the button is\n\t\t// offscreen-positioned (see the `.cn-context-menu` rule below).\n\t\t//\n\t\t// Use `inert` rather than `aria-hidden`: when the menu is dismissed\n\t\t// without picking an item, NcPopover's focus-trap restores focus to the\n\t\t// trigger button. With `aria-hidden` that produces a \"Blocked\n\t\t// aria-hidden on an element because its descendant retained focus\"\n\t\t// warning (focused element must not be hidden from AT). `inert` both\n\t\t// hides the button from the accessibility tree *and* makes it\n\t\t// unfocusable, so the trap's focus restore falls back to <body> instead\n\t\t// of landing on a hidden element. (The button never needs focus — the\n\t\t// menu opens via `:open.sync`, not by focusing the trigger.)\n\t\tconst trigger = this.$el?.querySelector('.action-item__menutoggle')\n\t\tif (trigger) {\n\t\t\ttrigger.setAttribute('inert', '')\n\t\t}\n\n\t\t// Silence NcPopover's dev-mode a11y warning for this specific instance.\n\t\t//\n\t\t// NcPopover (when `window.OC?.debug` is on) calls\n\t\t// `tabbable(triggerContainer)[0]` on every open and warns\n\t\t// \"It looks like you are using a custom button as a <NcPopover>\n\t\t// trigger…\" if nothing tabbable is found. We *are* using NcButton —\n\t\t// the warning's escape hatch — but the button is offscreen-positioned\n\t\t// and tabbable's visibility checks won't reliably find it across\n\t\t// browsers (Chromium's `Element.checkVisibility()` excludes some\n\t\t// offscreen / clipped elements). The advice in the warning (\"bind\n\t\t// #trigger slot attrs\") doesn't apply: NcActions controls its own\n\t\t// trigger and doesn't expose a `#trigger` slot to override.\n\t\t//\n\t\t// Neutralising the check on our own NcPopover instance (one level\n\t\t// down inside NcActions) leaves every other popover untouched and\n\t\t// keeps the check live for real custom triggers elsewhere.\n\t\t//\n\t\t// Vue 3: `this.$children` was removed (it hard-errors under @vue/compat\n\t\t// MODE 3 with \"INSTANCE_CHILDREN compat has been disabled\"), so reach the\n\t\t// NcActions instance through an explicit template ref instead.\n\t\tconst ncActions = this.$refs.actions\n\t\tconst ncPopover = ncActions?.$refs?.popover\n\t\tif (ncPopover) {\n\t\t\tncPopover.checkTriggerA11y = () => {}\n\t\t}\n\n\t\tthis.tagPopper()\n\n\t\t// Mounting with `open` already true never trips the watcher.\n\t\tif (this.internalOpen) {\n\t\t\tthis.startOutsideWatch()\n\t\t}\n\t},\n\n\tbeforeUnmount() {\n\t\tthis.stopOutsideWatch()\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Mark this menu's own popper element so the cursor-positioning CSS in\n\t\t * `src/css/context-menu.css` can target it — and nothing else.\n\t\t *\n\t\t * That CSS used to key on the `<html>` data attribute alone, which\n\t\t * matches *every* `.v-popper__popper` in the document: any other popover\n\t\t * alive while a context menu was open (a table row's actions menu, most\n\t\t * visibly) got dragged to the last right-click coordinates.\n\t\t *\n\t\t * The marker has to be applied imperatively — NcActions hardcodes\n\t\t * `popoverBaseClass: 'action-item__popper'` on its NcPopover and spreads\n\t\t * no `$attrs` onto it, so there is no prop to pass one in through.\n\t\t *\n\t\t * It must be an *attribute*, not a class. floating-vue binds a dynamic\n\t\t * `class` on this element (`--shown` / `--hidden` / `--show-from` / …)\n\t\t * and Vue's `patchClass` assigns `el.className` wholesale, so a class\n\t\t * added here is wiped the first time the popper opens — the rule then\n\t\t * matches nothing and the menu renders at its offscreen trigger. Vue\n\t\t * only patches props present in the vnode, so an attribute it never\n\t\t * renders is left alone.\n\t\t *\n\t\t * The element itself is stable: floating-vue keeps one `$_popperNode`\n\t\t * and `appendChild`s it into the container on first show, so marking it\n\t\t * once at `mounted()` holds for the component's lifetime. Re-running on\n\t\t * open is a cheap, idempotent guard for a not-yet-ready ref chain.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\ttagPopper() {\n\t\t\tconst el = this.$refs.actions?.$refs?.popover?.getPopoverContentElement?.()\n\t\t\tel?.setAttribute?.(CTX_MENU_POPPER_ATTR, '')\n\t\t},\n\n\t\t/**\n\t\t * Start watching for a press outside the menu. Driven from both the\n\t\t * `internalOpen` watcher and `mounted()`, because a consumer may mount\n\t\t * with `open` already true — in which case there is no state transition\n\t\t * for the watcher to see.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tstartOutsideWatch() {\n\t\t\t// nextTick: the contextmenu/click that opened the menu is still\n\t\t\t// propagating and would dismiss it again on the same gesture.\n\t\t\tthis.$nextTick(() => {\n\t\t\t\tif (this.internalOpen) {\n\t\t\t\t\t// CAPTURE. A canvas that swallows its own mousedown never lets it\n\t\t\t\t\t// bubble to document, so a menu opened over one could not be\n\t\t\t\t\t// dismissed by clicking the canvas — Vue Flow's d3-zoom pane calls\n\t\t\t\t\t// stopImmediatePropagation, which is exactly that shape.\n\t\t\t\t\tdocument.addEventListener('mousedown', this.onDocumentMouseDown, true)\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Stop watching for outside presses.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tstopOutsideWatch() {\n\t\t\tdocument.removeEventListener('mousedown', this.onDocumentMouseDown, true)\n\t\t},\n\n\t\t/**\n\t\t * Close the menu when the press lands outside it.\n\t\t *\n\t\t * NcActions has to run in `manual-open` mode here (the menu is opened\n\t\t * from a right-click via the `open` prop, not by activating the trigger\n\t\t * button). `@nextcloud/vue` 9 couples that mode to\n\t\t * `noCloseOnClickOutside: this.manualOpen`, and NcPopover derives\n\t\t * `autoHide: !noCloseOnClickOutside && closeOnClickOutside` — so the\n\t\t * popper's own outside-click dismissal is switched off and nothing else\n\t\t * closes the menu. v8 only cleared the popper's open `triggers`, leaving\n\t\t * autoHide intact, which is why dismissing worked before the Vue 3\n\t\t * upgrade. NcActions spreads no `$attrs` onto NcPopover, so the prop\n\t\t * cannot be overridden from outside — hence handling it here.\n\t\t *\n\t\t * @param {MouseEvent} event The document mousedown.\n\t\t * @return {void}\n\t\t */\n\t\tonDocumentMouseDown(event) {\n\t\t\tif (!this.internalOpen || this.isInsideMenu(event.target)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.onClose()\n\t\t},\n\n\t\t/**\n\t\t * Whether a node sits inside the menu's own surface — the custom panel,\n\t\t * or the popper NcActions teleports to `container=\"body\"` (so it is not\n\t\t * inside `this.$el` and `contains()` on the root would miss it).\n\t\t *\n\t\t * @param {EventTarget} target The event target to test.\n\t\t * @return {boolean} True when the target belongs to this menu.\n\t\t */\n\t\tisInsideMenu(target) {\n\t\t\tif (!(target instanceof Node)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif (this.$refs.panel?.contains(target)) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tconst content = this.$refs.actions?.$refs?.popover?.getPopoverContentElement?.()\n\t\t\tif (content?.contains(target)) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\t// Fallback for when the popover ref chain is unavailable —\n\t\t\t// `action-item__popper` is the base class NcActions gives its popper.\n\t\t\treturn target instanceof Element && !!target.closest('.action-item__popper')\n\t\t},\n\n\t\tresolveDisabled(action) {\n\t\t\tif (typeof action.disabled === 'function') {\n\t\t\t\treturn action.disabled(this.targetItem)\n\t\t\t}\n\t\t\treturn !!action.disabled\n\t\t},\n\n\t\t/**\n\t\t * Resolve the `title` field on an action descriptor — supports both\n\t\t * a static string and a function `(targetItem) => string`. Returns\n\t\t * undefined when no title is provided so the attribute isn't rendered.\n\t\t *\n\t\t * @param {object} action The action descriptor.\n\t\t * @return {string|undefined} The tooltip text, or undefined.\n\t\t */\n\t\tresolveTitle(action) {\n\t\t\tif (typeof action.title === 'function') {\n\t\t\t\treturn action.title(this.targetItem) || undefined\n\t\t\t}\n\t\t\treturn action.title || undefined\n\t\t},\n\n\t\t/**\n\t\t * Slugify an action label for use in stable `data-testid` selectors.\n\t\t * Lowercase, kebab-case, strip non-alphanumeric. Used solely by the\n\t\t * `:data-testid` binding on NcActionButton — does not affect runtime\n\t\t * behaviour or rendered text.\n\t\t *\n\t\t * @param {string} label The action's display label.\n\t\t * @return {string} kebab-case slug suitable for a testid suffix.\n\t\t */\n\t\tslugifyLabel(label) {\n\t\t\treturn String(label || '')\n\t\t\t\t.toLowerCase()\n\t\t\t\t.replace(/[^a-z0-9]+/g, '-')\n\t\t\t\t.replace(/^-+|-+$/g, '')\n\t\t},\n\n\t\tonAction(action) {\n\t\t\tif (action.handler && typeof action.handler === 'function') {\n\t\t\t\taction.handler(this.targetItem)\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event action User picked an entry from the menu. The action's own `handler(targetItem)` (when present) ran synchronously before this event fires; the event lets parents observe / log the choice.\n\t\t\t * @type {{ action: string, row: object|null }}\n\t\t\t */\n\t\t\tthis.$emit('action', { action: action.label, row: this.targetItem })\n\t\t},\n\n\t\t/**\n\t\t * A link entry was clicked: route a plain in-app click, leave the rest to\n\t\t * the browser, and emit `action` as a button would. The `handler` is not\n\t\t * called, since navigating is what the link already does. A modified\n\t\t * click opens a new tab, so nothing is emitted in this one.\n\t\t *\n\t\t * @param {object} action The action descriptor.\n\t\t * @param {object} link The resolved link.\n\t\t * @param {MouseEvent} event The click event.\n\t\t */\n\t\tonLinkAction(action, link, event) {\n\t\t\tif (isModifiedClick(event)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfollowItemActionLink(event, link, this.$router)\n\t\t\tthis.$emit('action', { action: action.label, row: this.targetItem })\n\t\t},\n\n\t\tonClose() {\n\t\t\tthis.internalOpen = false\n\t\t\tif (this.activePanel) {\n\t\t\t\tthis.$emit('update:activePanel', null)\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event close Fired when the menu starts closing (before the popper's hide animation). This is the reliable one — prefer it over `closed`.\n\t\t\t */\n\t\t\tthis.$emit('close')\n\t\t},\n\n\t\t/**\n\t\t * Clear the active panel and return to the default action list.\n\t\t * Exposed to custom panel slots via the `back` scope binding.\n\t\t */\n\t\tback() {\n\t\t\t// Description above the tag — see the note on `update:open`.\n\t\t\t/**\n\t\t\t * Emitted with `null` to clear the active panel — fired by the\n\t\t\t * panel's `back()` binding and whenever the menu closes while a\n\t\t\t * panel is open. Bind with `v-model:active-panel`.\n\t\t\t *\n\t\t\t * @event update:activePanel\n\t\t\t * @type {null}\n\t\t\t */\n\t\t\tthis.$emit('update:activePanel', null)\n\t\t},\n\n\t\t/**\n\t\t * Fires after the popper's hide animation completes.\n\t\t *\n\t\t * IT MUST NOT CLEAR THE CURSOR POSITION, and it used to. This fires LATE\n\t\t * — a whole hide animation late — so a second click on the same target\n\t\t * reopens the menu before it arrives, and the tear-down then strips the\n\t\t * coordinates that reopen had just written. The transform resolves\n\t\t * against undefined vars and the menu lands in the corner, on every open\n\t\t * after the first.\n\t\t *\n\t\t * It was dead code when written: `@nextcloud/vue` 9 bound `onAfterClose`\n\t\t * against an NcPopover that only emitted `afterHide`. 9.11.0 binds\n\t\t * `onAfterHide`, so it runs now, and a tear-down nothing needed became a\n\t\t * bug. Nothing needs it still: positioning is scoped to this menu's own\n\t\t * popper (see `tagPopper`), the data attribute is dropped in the\n\t\t * `internalOpen` watcher, the next `open()` overwrites the vars, and the\n\t\t * composable's unmount hook clears them for good.\n\t\t */\n\t\tonClosed() {\n\t\t\t/**\n\t\t\t * @event closed Intended to fire after the popper's hide animation completes. Does not currently fire — `@nextcloud/vue` 9's NcActions listens for an `afterClose` event NcPopover never emits. Use `close` instead.\n\t\t\t */\n\t\t\tthis.$emit('closed')\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n/* Selector is deliberately two classes deep. `.cn-context-menu` lands on\n NcActions' root, which NcActions itself styles as `.action-item[data-v-…]\n { position: relative; display: inline-block }`. Scoped CSS appends the attr\n to the last compound only, so a bare `.cn-context-menu` tied that rule at\n specificity (0,2,0) and lost on injection order — the trigger stayed\n `position: relative`, so it kept its in-flow box (top/left just offset it\n visually) and showed up as a full-width ~34px-tall phantom row between the\n table and the pagination on CnIndexPage. Adding the parent class makes this\n (0,3,0) so it wins regardless of order. */\n.cn-context-menu-root > .cn-context-menu {\n\t/* Hide the NcActions trigger button — menu opens only via right-click.\n\t Off-screen rather than display:none / visibility:hidden so NcPopover's\n\t a11y check (`tabbable(triggerContainer)[0]`) still finds the NcButton\n\t and the \"custom button as a <NcPopover> trigger\" warning stays quiet.\n\t Avoid `clip` / `clip-path` here — modern browsers' `Element.checkVisibility()`,\n\t which tabbable calls into, treats those as invisible. Pure offscreen\n\t positioning leaves the element fully \"visible\" to layout while being\n\t off the user's screen. */\n\tposition: absolute;\n\ttop: -9999px;\n\tleft: -9999px;\n\topacity: 0;\n\tpointer-events: none;\n}\n\n.cn-context-menu__backdrop {\n\tposition: fixed;\n\tinset: 0;\n\tz-index: 9998;\n}\n\n.cn-context-menu__panel {\n\tposition: fixed;\n\ttop: var(--cn-ctx-menu-y, 0);\n\tleft: var(--cn-ctx-menu-x, 0);\n\tz-index: 9999;\n\tmin-width: 220px;\n\tpadding: 4px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-large, 12px);\n\tbox-shadow: 0 1px 5px rgba(0, 0, 0, 0.2);\n\toutline: none;\n}\n</style>\n"],"names":["CnIcon"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnContextMenu.vue3.js","sources":["../../../../src/components/CnContextMenu/CnContextMenu.vue?vue&type=template&id=1bef81d7&lang.js"],"sourcesContent":["<template>\n\t<div class=\"cn-context-menu-root\">\n\t\t<NcActions\n\t\t\tv-if=\"!activePanel\"\n\t\t\tref=\"actions\"\n\t\t\tv-model:open=\"internalOpen\"\n\t\t\t:manualOpen=\"true\"\n\t\t\t:forceMenu=\"true\"\n\t\t\tclass=\"cn-context-menu\"\n\t\t\tcontainer=\"body\"\n\t\t\tdata-testid=\"cn-context-menu\"\n\t\t\t@close=\"onClose\"\n\t\t\t@closed=\"onClosed\">\n\t\t\t<!-- Dynamic actions from array prop -->\n\t\t\t<template v-for=\"{ action, link } in renderedActions\" :key=\"action.label\">\n\t\t\t\t<!-- A navigate-only action is a real link, so it can be middle-clicked, opened in a new tab or copied. -->\n\t\t\t\t<NcActionLink\n\t\t\t\t\tv-if=\"link\"\n\t\t\t\t\t:href=\"link.href\"\n\t\t\t\t\t:target=\"link.target\"\n\t\t\t\t\t:title=\"resolveTitle(action)\"\n\t\t\t\t\t:class=\"{ 'cn-row-action--destructive': action.destructive }\"\n\t\t\t\t\t:data-testid=\"`cn-action-item-${slugifyLabel(action.label)}`\"\n\t\t\t\t\tcloseAfterClick\n\t\t\t\t\t@click=\"onLinkAction(action, link, $event)\">\n\t\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t\t<CnIcon v-if=\"typeof action.icon === 'string'\" :name=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t\t<component :is=\"action.icon\" v-else :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ action.label }}\n\t\t\t\t</NcActionLink>\n\t\t\t\t<NcActionButton\n\t\t\t\t\tv-else\n\t\t\t\t\t:title=\"resolveTitle(action)\"\n\t\t\t\t\t:disabled=\"resolveDisabled(action)\"\n\t\t\t\t\t:class=\"{ 'cn-row-action--destructive': action.destructive }\"\n\t\t\t\t\t:data-testid=\"`cn-action-item-${slugifyLabel(action.label)}`\"\n\t\t\t\t\tcloseAfterClick\n\t\t\t\t\t@click=\"onAction(action)\">\n\t\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t\t<CnIcon v-if=\"typeof action.icon === 'string'\" :name=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t\t<component :is=\"action.icon\" v-else :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ action.label }}\n\t\t\t\t</NcActionButton>\n\t\t\t</template>\n\n\t\t\t<!--\n\t\t\t@slot default\n\t\t\t@description Custom NcActionButton-family content rendered inside the\n\t\t\tNcActions menu. Use this for hardcoded buttons (Keepiq pattern) when\n\t\t\tthe `actions` prop is empty.\n\t\t-->\n\t\t\t<slot />\n\t\t</NcActions>\n\n\t\t<!-- CUSTOM PANEL — bypass NcActions, render arbitrary slot content\n\t\t anchored at the cursor. The `#panel:<name>` slot may contain any\n\t\t markup (grids, inputs, custom components) without the NcActions\n\t\t child-allowlist filter. -->\n\t\t<template v-if=\"activePanel && internalOpen\">\n\t\t\t<div\n\t\t\t\tclass=\"cn-context-menu__backdrop\"\n\t\t\t\t@click=\"onClose\"\n\t\t\t\t@contextmenu.prevent=\"onClose\" />\n\t\t\t<div\n\t\t\t\tref=\"panel\"\n\t\t\t\tclass=\"cn-context-menu__panel\"\n\t\t\t\trole=\"menu\"\n\t\t\t\ttabindex=\"-1\"\n\t\t\t\tdata-testid=\"cn-context-menu-panel\"\n\t\t\t\t:data-panel=\"activePanel\"\n\t\t\t\t@keydown.esc.stop=\"onClose\"\n\t\t\t\t@click.stop>\n\t\t\t\t<!-- @slot panel:<name> Free-form custom panel content shown when\n\t\t\t\t `activePanel === '<name>'`. Bypasses the NcActions child\n\t\t\t\t allowlist — put any markup here (grids, inputs, custom\n\t\t\t\t components, etc.). The slot name is dynamic: define one\n\t\t\t\t `#panel:<name>` per panel you want to support. -->\n\t\t\t\t<!-- @binding {Function} back Clear `activePanel`, returning to the default action list. -->\n\t\t\t\t<!-- @binding {Function} close Close the entire menu, equivalent to clicking outside. -->\n\t\t\t\t<!-- @binding {*} targetItem The right-clicked item, forwarded from the `targetItem` prop. -->\n\t\t\t\t<slot\n\t\t\t\t\t:name=\"`panel:${activePanel}`\"\n\t\t\t\t\t:back=\"back\"\n\t\t\t\t\t:close=\"onClose\"\n\t\t\t\t\t:targetItem=\"targetItem\" />\n\t\t\t</div>\n\t\t</template>\n\t</div>\n</template>\n\n<script>\nimport { NcActionButton, NcActionLink, NcActions } from '@nextcloud/vue'\nimport { CTX_MENU_DATA_ATTR, CTX_MENU_POPPER_ATTR } from '../../composables/useContextMenu.js'\nimport { followItemActionLink, resolveItemActionLink } from '../../utils/actionLink.js'\nimport { CnIcon } from '../CnIcon/index.js'\n\n/**\n * CnContextMenu — Right-click context menu wrapper around NcActions.\n *\n * Provides a pre-configured NcActions instance that positions itself at the\n * cursor via the `useContextMenu` composable's CSS custom properties. Accepts\n * an `actions` array for the common dynamic case (like CnRowActions), and a\n * default slot for hardcoded NcActionButton content.\n *\n * Pair with `useContextMenu()` for state management (open/close, target item,\n * cursor positioning). The composable handles the DOM attributes; this component\n * handles the NcActions template boilerplate.\n *\n * Dynamic actions (CnIndexPage pattern)\n * ```vue\n * <CnContextMenu\n * v-model:open=\"contextMenuOpen\"\n * :actions=\"mergedActions\"\n * :target-item=\"contextMenuRow\"\n * @action=\"$emit('action', $event)\"\n * @close=\"closeContextMenu\" />\n * ```\n *\n * Custom buttons via slot (Keepiq pattern)\n * ```vue\n * <CnContextMenu\n * v-model:open=\"contextMenuOpen\"\n * @close=\"closeContextMenu\">\n * <NcActionButton close-after-click @click=\"onRename\">\n * <template #icon><PencilIcon :size=\"20\" /></template>\n * Rename\n * </NcActionButton>\n * </CnContextMenu>\n * ```\n */\nexport default {\n\tname: 'CnContextMenu',\n\n\tcomponents: {\n\t\tNcActions,\n\t\tNcActionButton,\n\t\tNcActionLink,\n\t\tCnIcon,\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Whether the context menu is open. Bind with `v-model:open` — the `.sync`\n\t\t * modifier it replaces was removed in Vue 3 and binds nothing.\n\t\t * Bind to `useContextMenu().isOpen`.\n\t\t */\n\t\topen: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Action definitions rendered as NcActionButton items.\n\t\t * Same format as CnRowActions: `{ label, icon?, handler?, disabled?, visible?, title?, destructive? }`.\n\t\t * `icon` accepts either a component (rendered directly) or a string —\n\t\t * a string is treated as an MDI name and rendered via `CnIcon` (e.g.\n\t\t * `\"Eye\"`), which lets manifest (JSON) actions declare icons by name.\n\t\t * `visible` (boolean | (targetItem) => boolean) hides the entry when falsy\n\t\t * (default: shown). `title` (string | (targetItem) => string) renders as\n\t\t * a native tooltip — useful for explaining why an entry is disabled.\n\t\t * `href` (URL) or `to` (vue-router location), each a value or a\n\t\t * `(targetItem) => …` function, render the entry as a real link, with\n\t\t * `linkTarget` as its `target`; a link emits `action` but does not call\n\t\t * `handler`. When the entire array is empty (or all entries are filtered\n\t\t * out), only the default slot content is rendered.\n\t\t *\n\t\t * @type {Array<{label: string, icon: object | string, handler: (targetItem: object) => void, disabled: boolean | ((targetItem: object) => boolean), visible: boolean | ((targetItem: object) => boolean), title: string | ((targetItem: object) => string), destructive: boolean, href: string | ((targetItem: object) => string), to: string | object | ((targetItem: object) => string | object), linkTarget: string}>}\n\t\t */\n\t\tactions: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * The right-clicked item (row, folder, etc.). Passed to action `handler`\n\t\t * and `disabled` callbacks, and included in the `action` event payload.\n\t\t * Bind to `useContextMenu().targetItem`.\n\t\t */\n\t\ttargetItem: {\n\t\t\ttype: [Object, String, Number],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Name of the currently-active custom panel. When set (and the menu is\n\t\t * open), the default NcActions list is replaced by the matching\n\t\t * `#panel:<name>` slot, rendered free-form (no NcActions child filter).\n\t\t * Bind with `v-model:active-panel` — the component emits `update:activePanel(null)` when\n\t\t * the panel's `back()` binding is invoked or the menu closes.\n\t\t *\n\t\t * @type {string|null}\n\t\t */\n\t\tactivePanel: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: ['action', 'close', 'closed', 'update:activePanel', 'update:open'],\n\n\tdata() {\n\t\treturn {\n\t\t\tinternalOpen: this.open,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Filter actions by their `visible` predicate. Entries without\n\t\t * `visible` are always shown (backwards compatible).\n\t\t *\n\t\t * @return {Array} Visible actions for the current targetItem.\n\t\t */\n\t\tvisibleActions() {\n\t\t\treturn this.actions.filter((action) => {\n\t\t\t\tif (action.visible === undefined) {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tif (typeof action.visible === 'function') {\n\t\t\t\t\treturn !!action.visible(this.targetItem)\n\t\t\t\t}\n\t\t\t\treturn !!action.visible\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Visible actions paired with the link each renders as (null for a button).\n\t\t *\n\t\t * @return {Array<{action: object, link: object|null}>}\n\t\t */\n\t\trenderedActions() {\n\t\t\treturn this.visibleActions.map((action) => ({\n\t\t\t\taction,\n\t\t\t\tlink: this.resolveDisabled(action) ? null : resolveItemActionLink(action, this.targetItem, this.$router),\n\t\t\t}))\n\t\t},\n\t},\n\n\twatch: {\n\t\topen(val) {\n\t\t\tthis.internalOpen = val\n\t\t},\n\n\t\tinternalOpen(val) {\n\t\t\t// Description goes ABOVE `@event`, not inline after it:\n\t\t\t// vue-docgen-api's event-name splitter stops at the first `:`, so\n\t\t\t// `@event update:open <description>` is read as one long event NAME\n\t\t\t// and the generated docs show an empty description.\n\t\t\t/**\n\t\t\t * Fired whenever the menu's internal open state flips. Used by\n\t\t\t * callers that bind `v-model:open` to track visibility.\n\t\t\t *\n\t\t\t * @event update:open\n\t\t\t * @type {boolean}\n\t\t\t */\n\t\t\tthis.$emit('update:open', val)\n\n\t\t\tif (val) {\n\t\t\t\tthis.tagPopper()\n\t\t\t\tthis.startOutsideWatch()\n\t\t\t} else {\n\t\t\t\tthis.stopOutsideWatch()\n\t\t\t\t// NcActions' `@closed` never fires (see `onClosed`), so drop the\n\t\t\t\t// \"a context menu is open\" attribute here. The CSS vars stay put\n\t\t\t\t// — the hide animation still needs the transform, and the next\n\t\t\t\t// `open()` overwrites them before the popper shows again.\n\t\t\t\tdocument.documentElement.removeAttribute(CTX_MENU_DATA_ATTR)\n\t\t\t}\n\t\t},\n\t},\n\n\tmounted() {\n\t\t// NcActions renders its own NcButton as the popover trigger; the menu\n\t\t// opens only via right-click (via `:open.sync`), so the button is\n\t\t// offscreen-positioned (see the `.cn-context-menu` rule below).\n\t\t//\n\t\t// Use `inert` rather than `aria-hidden`: when the menu is dismissed\n\t\t// without picking an item, NcPopover's focus-trap restores focus to the\n\t\t// trigger button. With `aria-hidden` that produces a \"Blocked\n\t\t// aria-hidden on an element because its descendant retained focus\"\n\t\t// warning (focused element must not be hidden from AT). `inert` both\n\t\t// hides the button from the accessibility tree *and* makes it\n\t\t// unfocusable, so the trap's focus restore falls back to <body> instead\n\t\t// of landing on a hidden element. (The button never needs focus — the\n\t\t// menu opens via `:open.sync`, not by focusing the trigger.)\n\t\tconst trigger = this.$el?.querySelector('.action-item__menutoggle')\n\t\tif (trigger) {\n\t\t\ttrigger.setAttribute('inert', '')\n\t\t}\n\n\t\t// Silence NcPopover's dev-mode a11y warning for this specific instance.\n\t\t//\n\t\t// NcPopover (when `window.OC?.debug` is on) calls\n\t\t// `tabbable(triggerContainer)[0]` on every open and warns\n\t\t// \"It looks like you are using a custom button as a <NcPopover>\n\t\t// trigger…\" if nothing tabbable is found. We *are* using NcButton —\n\t\t// the warning's escape hatch — but the button is offscreen-positioned\n\t\t// and tabbable's visibility checks won't reliably find it across\n\t\t// browsers (Chromium's `Element.checkVisibility()` excludes some\n\t\t// offscreen / clipped elements). The advice in the warning (\"bind\n\t\t// #trigger slot attrs\") doesn't apply: NcActions controls its own\n\t\t// trigger and doesn't expose a `#trigger` slot to override.\n\t\t//\n\t\t// Neutralising the check on our own NcPopover instance (one level\n\t\t// down inside NcActions) leaves every other popover untouched and\n\t\t// keeps the check live for real custom triggers elsewhere.\n\t\t//\n\t\t// Vue 3: `this.$children` was removed (it hard-errors under @vue/compat\n\t\t// MODE 3 with \"INSTANCE_CHILDREN compat has been disabled\"), so reach the\n\t\t// NcActions instance through an explicit template ref instead.\n\t\tconst ncActions = this.$refs.actions\n\t\tconst ncPopover = ncActions?.$refs?.popover\n\t\tif (ncPopover) {\n\t\t\tncPopover.checkTriggerA11y = () => {}\n\t\t}\n\n\t\tthis.tagPopper()\n\n\t\t// Mounting with `open` already true never trips the watcher.\n\t\tif (this.internalOpen) {\n\t\t\tthis.startOutsideWatch()\n\t\t}\n\t},\n\n\tbeforeUnmount() {\n\t\tthis.stopOutsideWatch()\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Mark this menu's own popper element so the cursor-positioning CSS in\n\t\t * `src/css/context-menu.css` can target it — and nothing else.\n\t\t *\n\t\t * That CSS used to key on the `<html>` data attribute alone, which\n\t\t * matches *every* `.v-popper__popper` in the document: any other popover\n\t\t * alive while a context menu was open (a table row's actions menu, most\n\t\t * visibly) got dragged to the last right-click coordinates.\n\t\t *\n\t\t * The marker has to be applied imperatively — NcActions hardcodes\n\t\t * `popoverBaseClass: 'action-item__popper'` on its NcPopover and spreads\n\t\t * no `$attrs` onto it, so there is no prop to pass one in through.\n\t\t *\n\t\t * It must be an *attribute*, not a class. floating-vue binds a dynamic\n\t\t * `class` on this element (`--shown` / `--hidden` / `--show-from` / …)\n\t\t * and Vue's `patchClass` assigns `el.className` wholesale, so a class\n\t\t * added here is wiped the first time the popper opens — the rule then\n\t\t * matches nothing and the menu renders at its offscreen trigger. Vue\n\t\t * only patches props present in the vnode, so an attribute it never\n\t\t * renders is left alone.\n\t\t *\n\t\t * The element itself is stable: floating-vue keeps one `$_popperNode`\n\t\t * and `appendChild`s it into the container on first show, so marking it\n\t\t * once at `mounted()` holds for the component's lifetime. Re-running on\n\t\t * open is a cheap, idempotent guard for a not-yet-ready ref chain.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\ttagPopper() {\n\t\t\tconst el = this.$refs.actions?.$refs?.popover?.getPopoverContentElement?.()\n\t\t\tel?.setAttribute?.(CTX_MENU_POPPER_ATTR, '')\n\t\t},\n\n\t\t/**\n\t\t * Start watching for a press outside the menu. Driven from both the\n\t\t * `internalOpen` watcher and `mounted()`, because a consumer may mount\n\t\t * with `open` already true — in which case there is no state transition\n\t\t * for the watcher to see.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tstartOutsideWatch() {\n\t\t\t// nextTick: the contextmenu/click that opened the menu is still\n\t\t\t// propagating and would dismiss it again on the same gesture.\n\t\t\tthis.$nextTick(() => {\n\t\t\t\tif (this.internalOpen) {\n\t\t\t\t\t// CAPTURE. A canvas that swallows its own mousedown never lets it\n\t\t\t\t\t// bubble to document, so a menu opened over one could not be\n\t\t\t\t\t// dismissed by clicking the canvas — Vue Flow's d3-zoom pane calls\n\t\t\t\t\t// stopImmediatePropagation, which is exactly that shape.\n\t\t\t\t\tdocument.addEventListener('mousedown', this.onDocumentMouseDown, true)\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Stop watching for outside presses.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tstopOutsideWatch() {\n\t\t\tdocument.removeEventListener('mousedown', this.onDocumentMouseDown, true)\n\t\t},\n\n\t\t/**\n\t\t * Close the menu when the press lands outside it.\n\t\t *\n\t\t * NcActions has to run in `manual-open` mode here (the menu is opened\n\t\t * from a right-click via the `open` prop, not by activating the trigger\n\t\t * button). `@nextcloud/vue` 9 couples that mode to\n\t\t * `noCloseOnClickOutside: this.manualOpen`, and NcPopover derives\n\t\t * `autoHide: !noCloseOnClickOutside && closeOnClickOutside` — so the\n\t\t * popper's own outside-click dismissal is switched off and nothing else\n\t\t * closes the menu. v8 only cleared the popper's open `triggers`, leaving\n\t\t * autoHide intact, which is why dismissing worked before the Vue 3\n\t\t * upgrade. NcActions spreads no `$attrs` onto NcPopover, so the prop\n\t\t * cannot be overridden from outside — hence handling it here.\n\t\t *\n\t\t * @param {MouseEvent} event The document mousedown.\n\t\t * @return {void}\n\t\t */\n\t\tonDocumentMouseDown(event) {\n\t\t\tif (!this.internalOpen || this.isInsideMenu(event.target)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.onClose()\n\t\t},\n\n\t\t/**\n\t\t * Whether a node sits inside the menu's own surface — the custom panel,\n\t\t * or the popper NcActions teleports to `container=\"body\"` (so it is not\n\t\t * inside `this.$el` and `contains()` on the root would miss it).\n\t\t *\n\t\t * @param {EventTarget} target The event target to test.\n\t\t * @return {boolean} True when the target belongs to this menu.\n\t\t */\n\t\tisInsideMenu(target) {\n\t\t\tif (!(target instanceof Node)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif (this.$refs.panel?.contains(target)) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tconst content = this.$refs.actions?.$refs?.popover?.getPopoverContentElement?.()\n\t\t\tif (content?.contains(target)) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\t// Fallback for when the popover ref chain is unavailable —\n\t\t\t// `action-item__popper` is the base class NcActions gives its popper.\n\t\t\treturn target instanceof Element && !!target.closest('.action-item__popper')\n\t\t},\n\n\t\tresolveDisabled(action) {\n\t\t\tif (typeof action.disabled === 'function') {\n\t\t\t\treturn action.disabled(this.targetItem)\n\t\t\t}\n\t\t\treturn !!action.disabled\n\t\t},\n\n\t\t/**\n\t\t * Resolve the `title` field on an action descriptor — supports both\n\t\t * a static string and a function `(targetItem) => string`. Returns\n\t\t * undefined when no title is provided so the attribute isn't rendered.\n\t\t *\n\t\t * @param {object} action The action descriptor.\n\t\t * @return {string|undefined} The tooltip text, or undefined.\n\t\t */\n\t\tresolveTitle(action) {\n\t\t\tif (typeof action.title === 'function') {\n\t\t\t\treturn action.title(this.targetItem) || undefined\n\t\t\t}\n\t\t\treturn action.title || undefined\n\t\t},\n\n\t\t/**\n\t\t * Slugify an action label for use in stable `data-testid` selectors.\n\t\t * Lowercase, kebab-case, strip non-alphanumeric. Used solely by the\n\t\t * `:data-testid` binding on NcActionButton — does not affect runtime\n\t\t * behaviour or rendered text.\n\t\t *\n\t\t * @param {string} label The action's display label.\n\t\t * @return {string} kebab-case slug suitable for a testid suffix.\n\t\t */\n\t\tslugifyLabel(label) {\n\t\t\treturn String(label || '')\n\t\t\t\t.toLowerCase()\n\t\t\t\t.replace(/[^a-z0-9]+/g, '-')\n\t\t\t\t.replace(/^-+|-+$/g, '')\n\t\t},\n\n\t\tonAction(action) {\n\t\t\tif (action.handler && typeof action.handler === 'function') {\n\t\t\t\taction.handler(this.targetItem)\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event action User picked an entry from the menu. The action's own `handler(targetItem)` (when present) ran synchronously before this event fires; the event lets parents observe / log the choice.\n\t\t\t * @type {{ action: string, row: object|null }}\n\t\t\t */\n\t\t\tthis.$emit('action', { action: action.label, row: this.targetItem })\n\t\t},\n\n\t\t/**\n\t\t * A link entry was clicked: route a plain in-app click, leave the rest to\n\t\t * the browser, and emit `action` as a button would. The `handler` is not\n\t\t * called, since navigating is what the link already does.\n\t\t *\n\t\t * @param {object} action The action descriptor.\n\t\t * @param {object} link The resolved link.\n\t\t * @param {MouseEvent} event The click event.\n\t\t */\n\t\tonLinkAction(action, link, event) {\n\t\t\tfollowItemActionLink(event, link, this.$router)\n\t\t\tthis.$emit('action', { action: action.label, row: this.targetItem })\n\t\t},\n\n\t\tonClose() {\n\t\t\tthis.internalOpen = false\n\t\t\tif (this.activePanel) {\n\t\t\t\tthis.$emit('update:activePanel', null)\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event close Fired when the menu starts closing (before the popper's hide animation). This is the reliable one — prefer it over `closed`.\n\t\t\t */\n\t\t\tthis.$emit('close')\n\t\t},\n\n\t\t/**\n\t\t * Clear the active panel and return to the default action list.\n\t\t * Exposed to custom panel slots via the `back` scope binding.\n\t\t */\n\t\tback() {\n\t\t\t// Description above the tag — see the note on `update:open`.\n\t\t\t/**\n\t\t\t * Emitted with `null` to clear the active panel — fired by the\n\t\t\t * panel's `back()` binding and whenever the menu closes while a\n\t\t\t * panel is open. Bind with `v-model:active-panel`.\n\t\t\t *\n\t\t\t * @event update:activePanel\n\t\t\t * @type {null}\n\t\t\t */\n\t\t\tthis.$emit('update:activePanel', null)\n\t\t},\n\n\t\t/**\n\t\t * Fires after the popper's hide animation completes.\n\t\t *\n\t\t * IT MUST NOT CLEAR THE CURSOR POSITION, and it used to. This fires LATE\n\t\t * — a whole hide animation late — so a second click on the same target\n\t\t * reopens the menu before it arrives, and the tear-down then strips the\n\t\t * coordinates that reopen had just written. The transform resolves\n\t\t * against undefined vars and the menu lands in the corner, on every open\n\t\t * after the first.\n\t\t *\n\t\t * It was dead code when written: `@nextcloud/vue` 9 bound `onAfterClose`\n\t\t * against an NcPopover that only emitted `afterHide`. 9.11.0 binds\n\t\t * `onAfterHide`, so it runs now, and a tear-down nothing needed became a\n\t\t * bug. Nothing needs it still: positioning is scoped to this menu's own\n\t\t * popper (see `tagPopper`), the data attribute is dropped in the\n\t\t * `internalOpen` watcher, the next `open()` overwrites the vars, and the\n\t\t * composable's unmount hook clears them for good.\n\t\t */\n\t\tonClosed() {\n\t\t\t/**\n\t\t\t * @event closed Intended to fire after the popper's hide animation completes. Does not currently fire — `@nextcloud/vue` 9's NcActions listens for an `afterClose` event NcPopover never emits. Use `close` instead.\n\t\t\t */\n\t\t\tthis.$emit('closed')\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n/* Selector is deliberately two classes deep. `.cn-context-menu` lands on\n NcActions' root, which NcActions itself styles as `.action-item[data-v-…]\n { position: relative; display: inline-block }`. Scoped CSS appends the attr\n to the last compound only, so a bare `.cn-context-menu` tied that rule at\n specificity (0,2,0) and lost on injection order — the trigger stayed\n `position: relative`, so it kept its in-flow box (top/left just offset it\n visually) and showed up as a full-width ~34px-tall phantom row between the\n table and the pagination on CnIndexPage. Adding the parent class makes this\n (0,3,0) so it wins regardless of order. */\n.cn-context-menu-root > .cn-context-menu {\n\t/* Hide the NcActions trigger button — menu opens only via right-click.\n\t Off-screen rather than display:none / visibility:hidden so NcPopover's\n\t a11y check (`tabbable(triggerContainer)[0]`) still finds the NcButton\n\t and the \"custom button as a <NcPopover> trigger\" warning stays quiet.\n\t Avoid `clip` / `clip-path` here — modern browsers' `Element.checkVisibility()`,\n\t which tabbable calls into, treats those as invisible. Pure offscreen\n\t positioning leaves the element fully \"visible\" to layout while being\n\t off the user's screen. */\n\tposition: absolute;\n\ttop: -9999px;\n\tleft: -9999px;\n\topacity: 0;\n\tpointer-events: none;\n}\n\n.cn-context-menu__backdrop {\n\tposition: fixed;\n\tinset: 0;\n\tz-index: 9998;\n}\n\n.cn-context-menu__panel {\n\tposition: fixed;\n\ttop: var(--cn-ctx-menu-y, 0);\n\tleft: var(--cn-ctx-menu-x, 0);\n\tz-index: 9999;\n\tmin-width: 220px;\n\tpadding: 4px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-large, 12px);\n\tbox-shadow: 0 1px 5px rgba(0, 0, 0, 0.2);\n\toutline: none;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createBlock","_Fragment","_renderList","_normalizeClass","_toDisplayString","_resolveDynamicComponent","_renderSlot","_createElementVNode"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnContextMenu.vue3.js","sources":["../../../../src/components/CnContextMenu/CnContextMenu.vue?vue&type=template&id=1bef81d7&lang.js"],"sourcesContent":["<template>\n\t<div class=\"cn-context-menu-root\">\n\t\t<NcActions\n\t\t\tv-if=\"!activePanel\"\n\t\t\tref=\"actions\"\n\t\t\tv-model:open=\"internalOpen\"\n\t\t\t:manualOpen=\"true\"\n\t\t\t:forceMenu=\"true\"\n\t\t\tclass=\"cn-context-menu\"\n\t\t\tcontainer=\"body\"\n\t\t\tdata-testid=\"cn-context-menu\"\n\t\t\t@close=\"onClose\"\n\t\t\t@closed=\"onClosed\">\n\t\t\t<!-- Dynamic actions from array prop -->\n\t\t\t<template v-for=\"{ action, link } in renderedActions\" :key=\"action.label\">\n\t\t\t\t<!-- A navigate-only action is a real link, so it can be middle-clicked, opened in a new tab or copied. -->\n\t\t\t\t<NcActionLink\n\t\t\t\t\tv-if=\"link\"\n\t\t\t\t\t:href=\"link.href\"\n\t\t\t\t\t:target=\"link.target\"\n\t\t\t\t\t:title=\"resolveTitle(action)\"\n\t\t\t\t\t:class=\"{ 'cn-row-action--destructive': action.destructive }\"\n\t\t\t\t\t:data-testid=\"`cn-action-item-${slugifyLabel(action.label)}`\"\n\t\t\t\t\tcloseAfterClick\n\t\t\t\t\t@click=\"onLinkAction(action, link, $event)\">\n\t\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t\t<CnIcon v-if=\"typeof action.icon === 'string'\" :name=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t\t<component :is=\"action.icon\" v-else :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ action.label }}\n\t\t\t\t</NcActionLink>\n\t\t\t\t<NcActionButton\n\t\t\t\t\tv-else\n\t\t\t\t\t:title=\"resolveTitle(action)\"\n\t\t\t\t\t:disabled=\"resolveDisabled(action)\"\n\t\t\t\t\t:class=\"{ 'cn-row-action--destructive': action.destructive }\"\n\t\t\t\t\t:data-testid=\"`cn-action-item-${slugifyLabel(action.label)}`\"\n\t\t\t\t\tcloseAfterClick\n\t\t\t\t\t@click=\"onAction(action)\">\n\t\t\t\t\t<template v-if=\"action.icon\" #icon>\n\t\t\t\t\t\t<CnIcon v-if=\"typeof action.icon === 'string'\" :name=\"action.icon\" :size=\"20\" />\n\t\t\t\t\t\t<component :is=\"action.icon\" v-else :size=\"20\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ action.label }}\n\t\t\t\t</NcActionButton>\n\t\t\t</template>\n\n\t\t\t<!--\n\t\t\t@slot default\n\t\t\t@description Custom NcActionButton-family content rendered inside the\n\t\t\tNcActions menu. Use this for hardcoded buttons (Keepiq pattern) when\n\t\t\tthe `actions` prop is empty.\n\t\t-->\n\t\t\t<slot />\n\t\t</NcActions>\n\n\t\t<!-- CUSTOM PANEL — bypass NcActions, render arbitrary slot content\n\t\t anchored at the cursor. The `#panel:<name>` slot may contain any\n\t\t markup (grids, inputs, custom components) without the NcActions\n\t\t child-allowlist filter. -->\n\t\t<template v-if=\"activePanel && internalOpen\">\n\t\t\t<div\n\t\t\t\tclass=\"cn-context-menu__backdrop\"\n\t\t\t\t@click=\"onClose\"\n\t\t\t\t@contextmenu.prevent=\"onClose\" />\n\t\t\t<div\n\t\t\t\tref=\"panel\"\n\t\t\t\tclass=\"cn-context-menu__panel\"\n\t\t\t\trole=\"menu\"\n\t\t\t\ttabindex=\"-1\"\n\t\t\t\tdata-testid=\"cn-context-menu-panel\"\n\t\t\t\t:data-panel=\"activePanel\"\n\t\t\t\t@keydown.esc.stop=\"onClose\"\n\t\t\t\t@click.stop>\n\t\t\t\t<!-- @slot panel:<name> Free-form custom panel content shown when\n\t\t\t\t `activePanel === '<name>'`. Bypasses the NcActions child\n\t\t\t\t allowlist — put any markup here (grids, inputs, custom\n\t\t\t\t components, etc.). The slot name is dynamic: define one\n\t\t\t\t `#panel:<name>` per panel you want to support. -->\n\t\t\t\t<!-- @binding {Function} back Clear `activePanel`, returning to the default action list. -->\n\t\t\t\t<!-- @binding {Function} close Close the entire menu, equivalent to clicking outside. -->\n\t\t\t\t<!-- @binding {*} targetItem The right-clicked item, forwarded from the `targetItem` prop. -->\n\t\t\t\t<slot\n\t\t\t\t\t:name=\"`panel:${activePanel}`\"\n\t\t\t\t\t:back=\"back\"\n\t\t\t\t\t:close=\"onClose\"\n\t\t\t\t\t:targetItem=\"targetItem\" />\n\t\t\t</div>\n\t\t</template>\n\t</div>\n</template>\n\n<script>\nimport { NcActionButton, NcActionLink, NcActions } from '@nextcloud/vue'\nimport { CTX_MENU_DATA_ATTR, CTX_MENU_POPPER_ATTR } from '../../composables/useContextMenu.js'\nimport { followItemActionLink, resolveItemActionLink } from '../../utils/actionLink.js'\nimport { isModifiedClick } from '../../utils/linkNavigation.js'\nimport { CnIcon } from '../CnIcon/index.js'\n\n/**\n * CnContextMenu — Right-click context menu wrapper around NcActions.\n *\n * Provides a pre-configured NcActions instance that positions itself at the\n * cursor via the `useContextMenu` composable's CSS custom properties. Accepts\n * an `actions` array for the common dynamic case (like CnRowActions), and a\n * default slot for hardcoded NcActionButton content.\n *\n * Pair with `useContextMenu()` for state management (open/close, target item,\n * cursor positioning). The composable handles the DOM attributes; this component\n * handles the NcActions template boilerplate.\n *\n * Dynamic actions (CnIndexPage pattern)\n * ```vue\n * <CnContextMenu\n * v-model:open=\"contextMenuOpen\"\n * :actions=\"mergedActions\"\n * :target-item=\"contextMenuRow\"\n * @action=\"$emit('action', $event)\"\n * @close=\"closeContextMenu\" />\n * ```\n *\n * Custom buttons via slot (Keepiq pattern)\n * ```vue\n * <CnContextMenu\n * v-model:open=\"contextMenuOpen\"\n * @close=\"closeContextMenu\">\n * <NcActionButton close-after-click @click=\"onRename\">\n * <template #icon><PencilIcon :size=\"20\" /></template>\n * Rename\n * </NcActionButton>\n * </CnContextMenu>\n * ```\n */\nexport default {\n\tname: 'CnContextMenu',\n\n\tcomponents: {\n\t\tNcActions,\n\t\tNcActionButton,\n\t\tNcActionLink,\n\t\tCnIcon,\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * Whether the context menu is open. Bind with `v-model:open` — the `.sync`\n\t\t * modifier it replaces was removed in Vue 3 and binds nothing.\n\t\t * Bind to `useContextMenu().isOpen`.\n\t\t */\n\t\topen: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Action definitions rendered as NcActionButton items.\n\t\t * Same format as CnRowActions: `{ label, icon?, handler?, disabled?, visible?, title?, destructive? }`.\n\t\t * `icon` accepts either a component (rendered directly) or a string —\n\t\t * a string is treated as an MDI name and rendered via `CnIcon` (e.g.\n\t\t * `\"Eye\"`), which lets manifest (JSON) actions declare icons by name.\n\t\t * `visible` (boolean | (targetItem) => boolean) hides the entry when falsy\n\t\t * (default: shown). `title` (string | (targetItem) => string) renders as\n\t\t * a native tooltip — useful for explaining why an entry is disabled.\n\t\t * `href` (URL) or `to` (vue-router location), each a value or a\n\t\t * `(targetItem) => …` function, render the entry as a real link, with\n\t\t * `linkTarget` as its `target`; a link emits `action` but does not call\n\t\t * `handler`. When the entire array is empty (or all entries are filtered\n\t\t * out), only the default slot content is rendered.\n\t\t *\n\t\t * @type {Array<{label: string, icon: object | string, handler: (targetItem: object) => void, disabled: boolean | ((targetItem: object) => boolean), visible: boolean | ((targetItem: object) => boolean), title: string | ((targetItem: object) => string), destructive: boolean, href: string | ((targetItem: object) => string), to: string | object | ((targetItem: object) => string | object), linkTarget: string}>}\n\t\t */\n\t\tactions: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/**\n\t\t * The right-clicked item (row, folder, etc.). Passed to action `handler`\n\t\t * and `disabled` callbacks, and included in the `action` event payload.\n\t\t * Bind to `useContextMenu().targetItem`.\n\t\t */\n\t\ttargetItem: {\n\t\t\ttype: [Object, String, Number],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Name of the currently-active custom panel. When set (and the menu is\n\t\t * open), the default NcActions list is replaced by the matching\n\t\t * `#panel:<name>` slot, rendered free-form (no NcActions child filter).\n\t\t * Bind with `v-model:active-panel` — the component emits `update:activePanel(null)` when\n\t\t * the panel's `back()` binding is invoked or the menu closes.\n\t\t *\n\t\t * @type {string|null}\n\t\t */\n\t\tactivePanel: {\n\t\t\ttype: String,\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: ['action', 'close', 'closed', 'update:activePanel', 'update:open'],\n\n\tdata() {\n\t\treturn {\n\t\t\tinternalOpen: this.open,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Filter actions by their `visible` predicate. Entries without\n\t\t * `visible` are always shown (backwards compatible).\n\t\t *\n\t\t * @return {Array} Visible actions for the current targetItem.\n\t\t */\n\t\tvisibleActions() {\n\t\t\treturn this.actions.filter((action) => {\n\t\t\t\tif (action.visible === undefined) {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t\tif (typeof action.visible === 'function') {\n\t\t\t\t\treturn !!action.visible(this.targetItem)\n\t\t\t\t}\n\t\t\t\treturn !!action.visible\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Visible actions paired with the link each renders as (null for a button).\n\t\t *\n\t\t * @return {Array<{action: object, link: object|null}>}\n\t\t */\n\t\trenderedActions() {\n\t\t\treturn this.visibleActions.map((action) => ({\n\t\t\t\taction,\n\t\t\t\tlink: this.resolveDisabled(action) ? null : resolveItemActionLink(action, this.targetItem, this.$router),\n\t\t\t}))\n\t\t},\n\t},\n\n\twatch: {\n\t\topen(val) {\n\t\t\tthis.internalOpen = val\n\t\t},\n\n\t\tinternalOpen(val) {\n\t\t\t// Description goes ABOVE `@event`, not inline after it:\n\t\t\t// vue-docgen-api's event-name splitter stops at the first `:`, so\n\t\t\t// `@event update:open <description>` is read as one long event NAME\n\t\t\t// and the generated docs show an empty description.\n\t\t\t/**\n\t\t\t * Fired whenever the menu's internal open state flips. Used by\n\t\t\t * callers that bind `v-model:open` to track visibility.\n\t\t\t *\n\t\t\t * @event update:open\n\t\t\t * @type {boolean}\n\t\t\t */\n\t\t\tthis.$emit('update:open', val)\n\n\t\t\tif (val) {\n\t\t\t\tthis.tagPopper()\n\t\t\t\tthis.startOutsideWatch()\n\t\t\t} else {\n\t\t\t\tthis.stopOutsideWatch()\n\t\t\t\t// NcActions' `@closed` never fires (see `onClosed`), so drop the\n\t\t\t\t// \"a context menu is open\" attribute here. The CSS vars stay put\n\t\t\t\t// — the hide animation still needs the transform, and the next\n\t\t\t\t// `open()` overwrites them before the popper shows again.\n\t\t\t\tdocument.documentElement.removeAttribute(CTX_MENU_DATA_ATTR)\n\t\t\t}\n\t\t},\n\t},\n\n\tmounted() {\n\t\t// NcActions renders its own NcButton as the popover trigger; the menu\n\t\t// opens only via right-click (via `:open.sync`), so the button is\n\t\t// offscreen-positioned (see the `.cn-context-menu` rule below).\n\t\t//\n\t\t// Use `inert` rather than `aria-hidden`: when the menu is dismissed\n\t\t// without picking an item, NcPopover's focus-trap restores focus to the\n\t\t// trigger button. With `aria-hidden` that produces a \"Blocked\n\t\t// aria-hidden on an element because its descendant retained focus\"\n\t\t// warning (focused element must not be hidden from AT). `inert` both\n\t\t// hides the button from the accessibility tree *and* makes it\n\t\t// unfocusable, so the trap's focus restore falls back to <body> instead\n\t\t// of landing on a hidden element. (The button never needs focus — the\n\t\t// menu opens via `:open.sync`, not by focusing the trigger.)\n\t\tconst trigger = this.$el?.querySelector('.action-item__menutoggle')\n\t\tif (trigger) {\n\t\t\ttrigger.setAttribute('inert', '')\n\t\t}\n\n\t\t// Silence NcPopover's dev-mode a11y warning for this specific instance.\n\t\t//\n\t\t// NcPopover (when `window.OC?.debug` is on) calls\n\t\t// `tabbable(triggerContainer)[0]` on every open and warns\n\t\t// \"It looks like you are using a custom button as a <NcPopover>\n\t\t// trigger…\" if nothing tabbable is found. We *are* using NcButton —\n\t\t// the warning's escape hatch — but the button is offscreen-positioned\n\t\t// and tabbable's visibility checks won't reliably find it across\n\t\t// browsers (Chromium's `Element.checkVisibility()` excludes some\n\t\t// offscreen / clipped elements). The advice in the warning (\"bind\n\t\t// #trigger slot attrs\") doesn't apply: NcActions controls its own\n\t\t// trigger and doesn't expose a `#trigger` slot to override.\n\t\t//\n\t\t// Neutralising the check on our own NcPopover instance (one level\n\t\t// down inside NcActions) leaves every other popover untouched and\n\t\t// keeps the check live for real custom triggers elsewhere.\n\t\t//\n\t\t// Vue 3: `this.$children` was removed (it hard-errors under @vue/compat\n\t\t// MODE 3 with \"INSTANCE_CHILDREN compat has been disabled\"), so reach the\n\t\t// NcActions instance through an explicit template ref instead.\n\t\tconst ncActions = this.$refs.actions\n\t\tconst ncPopover = ncActions?.$refs?.popover\n\t\tif (ncPopover) {\n\t\t\tncPopover.checkTriggerA11y = () => {}\n\t\t}\n\n\t\tthis.tagPopper()\n\n\t\t// Mounting with `open` already true never trips the watcher.\n\t\tif (this.internalOpen) {\n\t\t\tthis.startOutsideWatch()\n\t\t}\n\t},\n\n\tbeforeUnmount() {\n\t\tthis.stopOutsideWatch()\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Mark this menu's own popper element so the cursor-positioning CSS in\n\t\t * `src/css/context-menu.css` can target it — and nothing else.\n\t\t *\n\t\t * That CSS used to key on the `<html>` data attribute alone, which\n\t\t * matches *every* `.v-popper__popper` in the document: any other popover\n\t\t * alive while a context menu was open (a table row's actions menu, most\n\t\t * visibly) got dragged to the last right-click coordinates.\n\t\t *\n\t\t * The marker has to be applied imperatively — NcActions hardcodes\n\t\t * `popoverBaseClass: 'action-item__popper'` on its NcPopover and spreads\n\t\t * no `$attrs` onto it, so there is no prop to pass one in through.\n\t\t *\n\t\t * It must be an *attribute*, not a class. floating-vue binds a dynamic\n\t\t * `class` on this element (`--shown` / `--hidden` / `--show-from` / …)\n\t\t * and Vue's `patchClass` assigns `el.className` wholesale, so a class\n\t\t * added here is wiped the first time the popper opens — the rule then\n\t\t * matches nothing and the menu renders at its offscreen trigger. Vue\n\t\t * only patches props present in the vnode, so an attribute it never\n\t\t * renders is left alone.\n\t\t *\n\t\t * The element itself is stable: floating-vue keeps one `$_popperNode`\n\t\t * and `appendChild`s it into the container on first show, so marking it\n\t\t * once at `mounted()` holds for the component's lifetime. Re-running on\n\t\t * open is a cheap, idempotent guard for a not-yet-ready ref chain.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\ttagPopper() {\n\t\t\tconst el = this.$refs.actions?.$refs?.popover?.getPopoverContentElement?.()\n\t\t\tel?.setAttribute?.(CTX_MENU_POPPER_ATTR, '')\n\t\t},\n\n\t\t/**\n\t\t * Start watching for a press outside the menu. Driven from both the\n\t\t * `internalOpen` watcher and `mounted()`, because a consumer may mount\n\t\t * with `open` already true — in which case there is no state transition\n\t\t * for the watcher to see.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tstartOutsideWatch() {\n\t\t\t// nextTick: the contextmenu/click that opened the menu is still\n\t\t\t// propagating and would dismiss it again on the same gesture.\n\t\t\tthis.$nextTick(() => {\n\t\t\t\tif (this.internalOpen) {\n\t\t\t\t\t// CAPTURE. A canvas that swallows its own mousedown never lets it\n\t\t\t\t\t// bubble to document, so a menu opened over one could not be\n\t\t\t\t\t// dismissed by clicking the canvas — Vue Flow's d3-zoom pane calls\n\t\t\t\t\t// stopImmediatePropagation, which is exactly that shape.\n\t\t\t\t\tdocument.addEventListener('mousedown', this.onDocumentMouseDown, true)\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * Stop watching for outside presses.\n\t\t *\n\t\t * @return {void}\n\t\t */\n\t\tstopOutsideWatch() {\n\t\t\tdocument.removeEventListener('mousedown', this.onDocumentMouseDown, true)\n\t\t},\n\n\t\t/**\n\t\t * Close the menu when the press lands outside it.\n\t\t *\n\t\t * NcActions has to run in `manual-open` mode here (the menu is opened\n\t\t * from a right-click via the `open` prop, not by activating the trigger\n\t\t * button). `@nextcloud/vue` 9 couples that mode to\n\t\t * `noCloseOnClickOutside: this.manualOpen`, and NcPopover derives\n\t\t * `autoHide: !noCloseOnClickOutside && closeOnClickOutside` — so the\n\t\t * popper's own outside-click dismissal is switched off and nothing else\n\t\t * closes the menu. v8 only cleared the popper's open `triggers`, leaving\n\t\t * autoHide intact, which is why dismissing worked before the Vue 3\n\t\t * upgrade. NcActions spreads no `$attrs` onto NcPopover, so the prop\n\t\t * cannot be overridden from outside — hence handling it here.\n\t\t *\n\t\t * @param {MouseEvent} event The document mousedown.\n\t\t * @return {void}\n\t\t */\n\t\tonDocumentMouseDown(event) {\n\t\t\tif (!this.internalOpen || this.isInsideMenu(event.target)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.onClose()\n\t\t},\n\n\t\t/**\n\t\t * Whether a node sits inside the menu's own surface — the custom panel,\n\t\t * or the popper NcActions teleports to `container=\"body\"` (so it is not\n\t\t * inside `this.$el` and `contains()` on the root would miss it).\n\t\t *\n\t\t * @param {EventTarget} target The event target to test.\n\t\t * @return {boolean} True when the target belongs to this menu.\n\t\t */\n\t\tisInsideMenu(target) {\n\t\t\tif (!(target instanceof Node)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif (this.$refs.panel?.contains(target)) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tconst content = this.$refs.actions?.$refs?.popover?.getPopoverContentElement?.()\n\t\t\tif (content?.contains(target)) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\t// Fallback for when the popover ref chain is unavailable —\n\t\t\t// `action-item__popper` is the base class NcActions gives its popper.\n\t\t\treturn target instanceof Element && !!target.closest('.action-item__popper')\n\t\t},\n\n\t\tresolveDisabled(action) {\n\t\t\tif (typeof action.disabled === 'function') {\n\t\t\t\treturn action.disabled(this.targetItem)\n\t\t\t}\n\t\t\treturn !!action.disabled\n\t\t},\n\n\t\t/**\n\t\t * Resolve the `title` field on an action descriptor — supports both\n\t\t * a static string and a function `(targetItem) => string`. Returns\n\t\t * undefined when no title is provided so the attribute isn't rendered.\n\t\t *\n\t\t * @param {object} action The action descriptor.\n\t\t * @return {string|undefined} The tooltip text, or undefined.\n\t\t */\n\t\tresolveTitle(action) {\n\t\t\tif (typeof action.title === 'function') {\n\t\t\t\treturn action.title(this.targetItem) || undefined\n\t\t\t}\n\t\t\treturn action.title || undefined\n\t\t},\n\n\t\t/**\n\t\t * Slugify an action label for use in stable `data-testid` selectors.\n\t\t * Lowercase, kebab-case, strip non-alphanumeric. Used solely by the\n\t\t * `:data-testid` binding on NcActionButton — does not affect runtime\n\t\t * behaviour or rendered text.\n\t\t *\n\t\t * @param {string} label The action's display label.\n\t\t * @return {string} kebab-case slug suitable for a testid suffix.\n\t\t */\n\t\tslugifyLabel(label) {\n\t\t\treturn String(label || '')\n\t\t\t\t.toLowerCase()\n\t\t\t\t.replace(/[^a-z0-9]+/g, '-')\n\t\t\t\t.replace(/^-+|-+$/g, '')\n\t\t},\n\n\t\tonAction(action) {\n\t\t\tif (action.handler && typeof action.handler === 'function') {\n\t\t\t\taction.handler(this.targetItem)\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event action User picked an entry from the menu. The action's own `handler(targetItem)` (when present) ran synchronously before this event fires; the event lets parents observe / log the choice.\n\t\t\t * @type {{ action: string, row: object|null }}\n\t\t\t */\n\t\t\tthis.$emit('action', { action: action.label, row: this.targetItem })\n\t\t},\n\n\t\t/**\n\t\t * A link entry was clicked: route a plain in-app click, leave the rest to\n\t\t * the browser, and emit `action` as a button would. The `handler` is not\n\t\t * called, since navigating is what the link already does. A modified\n\t\t * click opens a new tab, so nothing is emitted in this one.\n\t\t *\n\t\t * @param {object} action The action descriptor.\n\t\t * @param {object} link The resolved link.\n\t\t * @param {MouseEvent} event The click event.\n\t\t */\n\t\tonLinkAction(action, link, event) {\n\t\t\tif (isModifiedClick(event)) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfollowItemActionLink(event, link, this.$router)\n\t\t\tthis.$emit('action', { action: action.label, row: this.targetItem })\n\t\t},\n\n\t\tonClose() {\n\t\t\tthis.internalOpen = false\n\t\t\tif (this.activePanel) {\n\t\t\t\tthis.$emit('update:activePanel', null)\n\t\t\t}\n\t\t\t/**\n\t\t\t * @event close Fired when the menu starts closing (before the popper's hide animation). This is the reliable one — prefer it over `closed`.\n\t\t\t */\n\t\t\tthis.$emit('close')\n\t\t},\n\n\t\t/**\n\t\t * Clear the active panel and return to the default action list.\n\t\t * Exposed to custom panel slots via the `back` scope binding.\n\t\t */\n\t\tback() {\n\t\t\t// Description above the tag — see the note on `update:open`.\n\t\t\t/**\n\t\t\t * Emitted with `null` to clear the active panel — fired by the\n\t\t\t * panel's `back()` binding and whenever the menu closes while a\n\t\t\t * panel is open. Bind with `v-model:active-panel`.\n\t\t\t *\n\t\t\t * @event update:activePanel\n\t\t\t * @type {null}\n\t\t\t */\n\t\t\tthis.$emit('update:activePanel', null)\n\t\t},\n\n\t\t/**\n\t\t * Fires after the popper's hide animation completes.\n\t\t *\n\t\t * IT MUST NOT CLEAR THE CURSOR POSITION, and it used to. This fires LATE\n\t\t * — a whole hide animation late — so a second click on the same target\n\t\t * reopens the menu before it arrives, and the tear-down then strips the\n\t\t * coordinates that reopen had just written. The transform resolves\n\t\t * against undefined vars and the menu lands in the corner, on every open\n\t\t * after the first.\n\t\t *\n\t\t * It was dead code when written: `@nextcloud/vue` 9 bound `onAfterClose`\n\t\t * against an NcPopover that only emitted `afterHide`. 9.11.0 binds\n\t\t * `onAfterHide`, so it runs now, and a tear-down nothing needed became a\n\t\t * bug. Nothing needs it still: positioning is scoped to this menu's own\n\t\t * popper (see `tagPopper`), the data attribute is dropped in the\n\t\t * `internalOpen` watcher, the next `open()` overwrites the vars, and the\n\t\t * composable's unmount hook clears them for good.\n\t\t */\n\t\tonClosed() {\n\t\t\t/**\n\t\t\t * @event closed Intended to fire after the popper's hide animation completes. Does not currently fire — `@nextcloud/vue` 9's NcActions listens for an `afterClose` event NcPopover never emits. Use `close` instead.\n\t\t\t */\n\t\t\tthis.$emit('closed')\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n/* Selector is deliberately two classes deep. `.cn-context-menu` lands on\n NcActions' root, which NcActions itself styles as `.action-item[data-v-…]\n { position: relative; display: inline-block }`. Scoped CSS appends the attr\n to the last compound only, so a bare `.cn-context-menu` tied that rule at\n specificity (0,2,0) and lost on injection order — the trigger stayed\n `position: relative`, so it kept its in-flow box (top/left just offset it\n visually) and showed up as a full-width ~34px-tall phantom row between the\n table and the pagination on CnIndexPage. Adding the parent class makes this\n (0,3,0) so it wins regardless of order. */\n.cn-context-menu-root > .cn-context-menu {\n\t/* Hide the NcActions trigger button — menu opens only via right-click.\n\t Off-screen rather than display:none / visibility:hidden so NcPopover's\n\t a11y check (`tabbable(triggerContainer)[0]`) still finds the NcButton\n\t and the \"custom button as a <NcPopover> trigger\" warning stays quiet.\n\t Avoid `clip` / `clip-path` here — modern browsers' `Element.checkVisibility()`,\n\t which tabbable calls into, treats those as invisible. Pure offscreen\n\t positioning leaves the element fully \"visible\" to layout while being\n\t off the user's screen. */\n\tposition: absolute;\n\ttop: -9999px;\n\tleft: -9999px;\n\topacity: 0;\n\tpointer-events: none;\n}\n\n.cn-context-menu__backdrop {\n\tposition: fixed;\n\tinset: 0;\n\tz-index: 9998;\n}\n\n.cn-context-menu__panel {\n\tposition: fixed;\n\ttop: var(--cn-ctx-menu-y, 0);\n\tleft: var(--cn-ctx-menu-x, 0);\n\tz-index: 9999;\n\tmin-width: 220px;\n\tpadding: 4px;\n\tbackground: var(--color-main-background);\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-large, 12px);\n\tbox-shadow: 0 1px 5px rgba(0, 0, 0, 0.2);\n\toutline: none;\n}\n</style>\n"],"names":["_openBlock","_createElementBlock","_createBlock","_Fragment","_renderList","_normalizeClass","_toDisplayString","_resolveDynamicComponent","_renderSlot","_createElementVNode"],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import script from './CnConversationThread.vue2.js';
|
|
2
|
+
import { render } from './CnConversationThread.vue3.js';
|
|
3
|
+
|
|
4
|
+
script.render = render;
|
|
5
|
+
script.__scopeId = "data-v-22b6724f";
|
|
6
|
+
script.__file = "src/components/CnConversationThread/CnConversationThread.vue";
|
|
7
|
+
const __sfc_main = script;
|
|
8
|
+
|
|
9
|
+
export { __sfc_main as default };
|
|
10
|
+
//# sourceMappingURL=CnConversationThread.vue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CnConversationThread.vue.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;"}
|