@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
|
@@ -125,6 +125,19 @@ var script = {
|
|
|
125
125
|
default: 0,
|
|
126
126
|
validator: (v) => Number.isFinite(v) && v >= 0,
|
|
127
127
|
},
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* How many records each tab matches, keyed by tab index (an array in
|
|
131
|
+
* tab order works too). A tab with a number here shows it after its
|
|
132
|
+
* label; a tab without one shows no count. A tab can also carry its
|
|
133
|
+
* own `count`. `null` (the default) shows no counts at all.
|
|
134
|
+
*
|
|
135
|
+
* @type {{[index: number]: number}|Array<number|null>|null}
|
|
136
|
+
*/
|
|
137
|
+
counts: {
|
|
138
|
+
type: [Object, Array],
|
|
139
|
+
default: null,
|
|
140
|
+
},
|
|
128
141
|
},
|
|
129
142
|
|
|
130
143
|
emits: ['update:active-index', 'update:selected-indices'],
|
|
@@ -218,10 +231,19 @@ var script = {
|
|
|
218
231
|
* omitted — an empty selection already means "all".
|
|
219
232
|
*
|
|
220
233
|
* @return {Array<{label: string, index: number, icon: string}>}
|
|
234
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views
|
|
221
235
|
*/
|
|
222
236
|
dropdownOptions() {
|
|
223
237
|
return this.tabs
|
|
224
|
-
.map((tab, index) =>
|
|
238
|
+
.map((tab, index) => {
|
|
239
|
+
const count = this.countOf({ tab, index });
|
|
240
|
+
return {
|
|
241
|
+
label: count === null ? tab.label : `${tab.label} (${count})`,
|
|
242
|
+
icon: tab.icon,
|
|
243
|
+
index,
|
|
244
|
+
_empty: this.isEmptyFilter(tab),
|
|
245
|
+
}
|
|
246
|
+
})
|
|
225
247
|
.filter((o) => !o._empty)
|
|
226
248
|
},
|
|
227
249
|
|
|
@@ -240,6 +262,20 @@ var script = {
|
|
|
240
262
|
},
|
|
241
263
|
|
|
242
264
|
methods: {
|
|
265
|
+
/**
|
|
266
|
+
* The count to show on a tab, or null for none: `counts[index]`
|
|
267
|
+
* first, else the tab's own `count`.
|
|
268
|
+
*
|
|
269
|
+
* @param {{tab: object, index: number}} entry The tab and its index.
|
|
270
|
+
* @return {number|null}
|
|
271
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views
|
|
272
|
+
*/
|
|
273
|
+
countOf(entry) {
|
|
274
|
+
const fromProp = this.counts ? this.counts[entry.index] : undefined;
|
|
275
|
+
const value = (fromProp !== undefined && fromProp !== null) ? fromProp : entry.tab.count;
|
|
276
|
+
return (typeof value === 'number' && Number.isFinite(value)) ? value : null
|
|
277
|
+
},
|
|
278
|
+
|
|
243
279
|
/**
|
|
244
280
|
* Is tab `i` rendered active in chips mode (handles both single
|
|
245
281
|
* `activeIndex` and `multiple` `selectedIndices`).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnQuickFilterBar.vue2.js","sources":["../../../../src/components/CnQuickFilterBar/CnQuickFilterBar.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: AGPL-3.0-or-later\n -->\n\n<template>\n\t<!-- Dropdown mode: a single NcSelect (multi or single) instead of the chip strip. -->\n\t<div\n\t\tv-if=\"mode === 'dropdown'\"\n\t\tclass=\"cn-quick-filter-bar cn-quick-filter-bar--dropdown\"\n\t\t:class=\"{ 'cn-quick-filter-bar--inline': inline }\">\n\t\t<NcSelect\n\t\t\t:modelValue=\"dropdownValue\"\n\t\t\t:options=\"dropdownOptions\"\n\t\t\t:multiple=\"multiple\"\n\t\t\t:keepOpen=\"multiple\"\n\t\t\t:clearable=\"true\"\n\t\t\tlabel=\"label\"\n\t\t\tclass=\"cn-quick-filter-bar__select\"\n\t\t\t:inputLabel=\"selectLabel\"\n\t\t\t:aria-label-combobox=\"selectLabel\"\n\t\t\t:placeholder=\"placeholder || selectLabel\"\n\t\t\t@update:modelValue=\"onSelectInput\" />\n\t</div>\n\t<!-- Chips mode (default): the clickable tab strip. -->\n\t<div v-else\n\t\tclass=\"cn-quick-filter-bar\"\n\t\t:class=\"{ 'cn-quick-filter-bar--inline': inline }\">\n\t\t<!-- The tablist holds ONLY tabs: the overflow chip is a sibling, not a\n\t\t child, because a role=\"tablist\" may not contain a menu button. -->\n\t\t<div class=\"cn-quick-filter-bar__tabs\" role=\"tablist\">\n\t\t\t<button\n\t\t\t\tv-for=\"entry in visibleEntries\"\n\t\t\t\t:key=\"entry.tab.label + ':' + entry.index\"\n\t\t\t\ttype=\"button\"\n\t\t\t\trole=\"tab\"\n\t\t\t\t:aria-selected=\"isChipActive(entry.index) ? 'true' : 'false'\"\n\t\t\t\tclass=\"cn-quick-filter-bar__tab\"\n\t\t\t\t:class=\"[{ 'cn-quick-filter-bar__tab--active': isChipActive(entry.index) }]\"\n\t\t\t\t@click=\"onClick(entry.index)\">\n\t\t\t\t<CnIcon\n\t\t\t\t\tv-if=\"entry.tab.icon\"\n\t\t\t\t\t:name=\"entry.tab.icon\"\n\t\t\t\t\t:size=\"16\"\n\t\t\t\t\tclass=\"cn-quick-filter-bar__icon\" />\n\t\t\t\t<span class=\"cn-quick-filter-bar__label\">{{ entry.tab.label }}</span>\n\t\t\t</button>\n\t\t</div>\n\t\t<!-- The overflow is a CHIP and not a toolbar button: it is one of the\n\t\t lenses, so it carries the same pill, the same active fill and the\n\t\t label of whichever hidden lens is on. -->\n\t\t<NcPopover\n\t\t\tv-if=\"overflowEntries.length > 0\"\n\t\t\tv-model:shown=\"moreOpen\"\n\t\t\t:triggers=\"[]\"\n\t\t\tpopupRole=\"dialog\"\n\t\t\tplacement=\"bottom-end\"\n\t\t\tpopoverBaseClass=\"cn-quick-filter-bar__popper\">\n\t\t\t<template #trigger>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tdata-testid=\"cn-quick-filter-more\"\n\t\t\t\t\tclass=\"cn-quick-filter-bar__tab cn-quick-filter-bar__more\"\n\t\t\t\t\t:class=\"{\n\t\t\t\t\t\t'cn-quick-filter-bar__tab--active': activeOverflowEntries.length > 0,\n\t\t\t\t\t\t'cn-quick-filter-bar__more--icon-only': !overflowLabel,\n\t\t\t\t\t}\"\n\t\t\t\t\t:aria-expanded=\"moreOpen ? 'true' : 'false'\"\n\t\t\t\t\taria-haspopup=\"dialog\"\n\t\t\t\t\t:aria-label=\"overflowAriaLabel\"\n\t\t\t\t\t:title=\"overflowAriaLabel\"\n\t\t\t\t\t@click=\"moreOpen = !moreOpen\">\n\t\t\t\t\t<span v-if=\"overflowLabel\" class=\"cn-quick-filter-bar__label\">{{ overflowLabel }}</span>\n\t\t\t\t\t<DotsHorizontal :size=\"16\" class=\"cn-quick-filter-bar__icon\" />\n\t\t\t\t</button>\n\t\t\t</template>\n\t\t\t<div\n\t\t\t\tclass=\"cn-quick-filter-bar__more-panel\"\n\t\t\t\t@keydown.escape.stop=\"moreOpen = false\">\n\t\t\t\t<p class=\"cn-quick-filter-bar__more-heading\">\n\t\t\t\t\t{{ moreHeading }}\n\t\t\t\t</p>\n\t\t\t\t<div class=\"cn-quick-filter-bar__more-list\">\n\t\t\t\t\t<button\n\t\t\t\t\t\tv-for=\"entry in overflowEntries\"\n\t\t\t\t\t\t:key=\"entry.tab.label + ':' + entry.index\"\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tdata-testid=\"cn-quick-filter-more-item\"\n\t\t\t\t\t\tclass=\"cn-quick-filter-bar__tab cn-quick-filter-bar__more-item\"\n\t\t\t\t\t\t:class=\"{ 'cn-quick-filter-bar__tab--active': isChipActive(entry.index) }\"\n\t\t\t\t\t\t:aria-pressed=\"isChipActive(entry.index) ? 'true' : 'false'\"\n\t\t\t\t\t\t@click=\"onOverflowClick(entry.index)\">\n\t\t\t\t\t\t<CnIcon\n\t\t\t\t\t\t\tv-if=\"entry.tab.icon\"\n\t\t\t\t\t\t\t:name=\"entry.tab.icon\"\n\t\t\t\t\t\t\t:size=\"16\"\n\t\t\t\t\t\t\tclass=\"cn-quick-filter-bar__icon\" />\n\t\t\t\t\t\t<span class=\"cn-quick-filter-bar__label\">{{ entry.tab.label }}</span>\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</NcPopover>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcPopover, NcSelect } from '@nextcloud/vue'\nimport DotsHorizontal from 'vue-material-design-icons/DotsHorizontal.vue'\nimport CnIcon from '../CnIcon/CnIcon.vue'\n\n/**\n * CnQuickFilterBar — quick-filter control rendered above a `type:\"index\"`\n * page's table. Each tab carries a manifest `filter` map that `CnIndexPage`\n * merges into the `useListView` fetch when the tab is active.\n *\n * Two presentations, chosen by `mode`:\n * - `'chips'` (default) — a horizontal row of pill-shaped buttons; the\n * active one is filled, the rest outlined. `maxVisible` caps how many\n * pills render and moves the rest into an overflow menu.\n * - `'dropdown'` — a single `NcSelect`. The \"All\" / empty-filter tab is\n * dropped from the options (an empty selection means \"all\").\n *\n * Two selection cardinalities, chosen by `multiple`:\n * - single (default) — one active tab; uses the `activeIndex` v-model\n * (`update:active-index`).\n * - `multiple` — several tabs at once; uses the `selectedIndices` array\n * prop + `update:selected-indices` event. `CnIndexPage` ORs the selected\n * tabs' filters together.\n *\n * Implementation is deliberately a thin styled `<button>` list (chips) /\n * `NcSelect` (dropdown) rather than `NcAppNavigation*` — this lives INSIDE\n * the index page, not as the app's main nav.\n */\nexport default {\n\tname: 'CnQuickFilterBar',\n\n\tcomponents: { CnIcon, DotsHorizontal, NcPopover, NcSelect },\n\n\t// NO `model: { prop, event }` OPTION.\n\t//\n\t// The Vue-2 `model` option is REMOVED in Vue 3 — read by nothing, warned\n\t// about by nothing — so it survived the migration as a declaration that\n\t// looked authoritative while doing exactly zero. The component's real\n\t// contract is the explicit `:active-index` / `@update:active-index` pair\n\t// (and `:selected-indices` / `@update:selected-indices` in `multiple`\n\t// mode), which is what `CnIndexPage` binds. Surfaced by\n\t// `vue/no-deprecated-model-definition`, which this repo now arms through\n\t// its own published preset.\n\n\tprops: {\n\t\t/**\n\t\t * Tab definitions — same shape `pages[].config.quickFilters[]` uses.\n\t\t * `filter` is consumed by the parent (CnIndexPage), not this component.\n\t\t */\n\t\ttabs: {\n\t\t\ttype: Array,\n\t\t\trequired: true,\n\t\t\tvalidator: (arr) => Array.isArray(arr) && arr.every((t) => t && typeof t.label === 'string'),\n\t\t},\n\n\t\t/**\n\t\t * Render bare (no padding / bottom border / background) for embedding\n\t\t * inline inside another bar — e.g. the `#filters` slot of `CnActionsBar`,\n\t\t * so the tabs sit beside the view toggle instead of as a separate row.\n\t\t */\n\t\tinline: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Index of the currently active tab, or `null` for none active. Single-select only. */\n\t\tactiveIndex: {\n\t\t\ttype: Number,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Presentation: `'chips'` (pill button strip, default) or\n\t\t * `'dropdown'` (a single `NcSelect`).\n\t\t *\n\t\t * @type {'chips'|'dropdown'}\n\t\t */\n\t\tmode: {\n\t\t\ttype: String,\n\t\t\tdefault: 'chips',\n\t\t\tvalidator: (v) => ['chips', 'dropdown'].includes(v),\n\t\t},\n\n\t\t/**\n\t\t * Allow more than one tab active at once. Selection is exposed via\n\t\t * `selectedIndices` + `update:selected-indices`; the parent ORs the\n\t\t * selected tabs' filters together.\n\t\t */\n\t\tmultiple: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Active tab indices when `multiple` is set (the array v-model).\n\t\t *\n\t\t * @type {number[]}\n\t\t */\n\t\tselectedIndices: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Accessible label / placeholder for the dropdown control. */\n\t\tselectLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: 'Filter',\n\t\t},\n\n\t\t/** Placeholder text for the dropdown (falls back to `selectLabel`). */\n\t\tplaceholder: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Chips mode only: how many pills render inline before the rest move\n\t\t * into an overflow menu. `0` (the default) renders every tab, which is\n\t\t * the behaviour this component has always had. The visible set is the\n\t\t * FIRST `maxVisible` tabs in declared order — reorder `tabs` to change\n\t\t * which ones stay out. Ignored in `dropdown` mode.\n\t\t */\n\t\tmaxVisible: {\n\t\t\ttype: Number,\n\t\t\tdefault: 0,\n\t\t\tvalidator: (v) => Number.isFinite(v) && v >= 0,\n\t\t},\n\t},\n\n\temits: ['update:active-index', 'update:selected-indices'],\n\n\tdata() {\n\t\treturn {\n\t\t\t/** Whether the overflow chip's panel is open. */\n\t\t\tmoreOpen: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Every tab paired with its own index, so the overflow split can slice\n\t\t * the list without losing the index the parent's filters are keyed on.\n\t\t *\n\t\t * @return {Array<{tab: object, index: number}>}\n\t\t */\n\t\tentries() {\n\t\t\treturn this.tabs.map((tab, index) => ({ tab, index }))\n\t\t},\n\n\t\t/**\n\t\t * The tabs rendered as pills — all of them unless `maxVisible` caps it.\n\t\t *\n\t\t * @return {Array<{tab: object, index: number}>}\n\t\t */\n\t\tvisibleEntries() {\n\t\t\tif (!this.maxVisible || this.tabs.length <= this.maxVisible) {\n\t\t\t\treturn this.entries\n\t\t\t}\n\t\t\treturn this.entries.slice(0, this.maxVisible)\n\t\t},\n\n\t\t/**\n\t\t * The tabs moved into the overflow menu (empty when `maxVisible` is 0\n\t\t * or there is nothing to hide).\n\t\t *\n\t\t * @return {Array<{tab: object, index: number}>}\n\t\t */\n\t\toverflowEntries() {\n\t\t\tif (!this.maxVisible || this.tabs.length <= this.maxVisible) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn this.entries.slice(this.maxVisible)\n\t\t},\n\n\t\t/**\n\t\t * The active tabs that live in the overflow menu.\n\t\t *\n\t\t * @return {Array<{tab: object, index: number}>}\n\t\t */\n\t\tactiveOverflowEntries() {\n\t\t\treturn this.overflowEntries.filter((e) => this.isChipActive(e.index))\n\t\t},\n\n\t\t/**\n\t\t * Text on the overflow chip — an active hidden tab names itself there,\n\t\t * so the strip never reads \"All\" while the list is narrowed. Nothing\n\t\t * active leaves the chip as the bare `⋯` glyph.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\toverflowLabel() {\n\t\t\tconst active = this.activeOverflowEntries\n\t\t\tif (active.length === 1) {\n\t\t\t\treturn active[0].tab.label\n\t\t\t}\n\t\t\tif (active.length > 1) {\n\t\t\t\treturn t('nextcloud-vue', '{count} filters', { count: active.length })\n\t\t\t}\n\t\t\treturn ''\n\t\t},\n\n\t\t/** @return {string} Accessible name for the overflow chip. */\n\t\toverflowAriaLabel() {\n\t\t\tif (this.overflowLabel) {\n\t\t\t\treturn t('nextcloud-vue', '{label} — show the other filters', { label: this.overflowLabel })\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', '{count} more filters', { count: this.overflowEntries.length })\n\t\t},\n\n\t\t/** @return {string} Heading above the hidden chips in the panel. */\n\t\tmoreHeading() {\n\t\t\treturn t('nextcloud-vue', 'More filters')\n\t\t},\n\n\t\t/**\n\t\t * Dropdown options: every tab that carries a non-empty `filter`,\n\t\t * tagged with its original index. The \"All\" / empty-filter tab is\n\t\t * omitted — an empty selection already means \"all\".\n\t\t *\n\t\t * @return {Array<{label: string, index: number, icon: string}>}\n\t\t */\n\t\tdropdownOptions() {\n\t\t\treturn this.tabs\n\t\t\t\t.map((tab, index) => ({ label: tab.label, icon: tab.icon, index, _empty: this.isEmptyFilter(tab) }))\n\t\t\t\t.filter((o) => !o._empty)\n\t\t},\n\n\t\t/**\n\t\t * Current NcSelect value — an array of option objects when\n\t\t * `multiple`, a single option object (or null) otherwise.\n\t\t *\n\t\t * @return {object|object[]|null}\n\t\t */\n\t\tdropdownValue() {\n\t\t\tif (this.multiple) {\n\t\t\t\treturn this.dropdownOptions.filter((o) => this.selectedIndices.includes(o.index))\n\t\t\t}\n\t\t\treturn this.dropdownOptions.find((o) => o.index === this.activeIndex) || null\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Is tab `i` rendered active in chips mode (handles both single\n\t\t * `activeIndex` and `multiple` `selectedIndices`).\n\t\t *\n\t\t * @param {number} i Zero-based tab index.\n\t\t * @return {boolean}\n\t\t */\n\t\tisChipActive(i) {\n\t\t\treturn this.multiple ? this.selectedIndices.includes(i) : i === this.activeIndex\n\t\t},\n\n\t\t/**\n\t\t * Whether a tab's `filter` map is absent/empty (the \"All\" tab).\n\t\t *\n\t\t * @param {{label: string, filter?: object}} tab An entry from the `tabs` prop.\n\t\t * @return {boolean} True when the tab applies no filter, so selecting it\n\t\t * clears the multi-selection rather than adding to it.\n\t\t */\n\t\tisEmptyFilter(tab) {\n\t\t\treturn !tab || !tab.filter || Object.keys(tab.filter).length === 0\n\t\t},\n\n\t\t/**\n\t\t * Chip-click handler. In single mode emits the new active index; in\n\t\t * multiple mode toggles the index in/out of the selection. Clicking\n\t\t * an empty-filter (\"All\") chip clears the multi-selection.\n\t\t *\n\t\t * @param {number} i Zero-based tab index.\n\t\t */\n\t\tonClick(i) {\n\t\t\tif (this.multiple) {\n\t\t\t\tif (this.isEmptyFilter(this.tabs[i])) {\n\t\t\t\t\t/**\n\t\t\t\t\t * @event update:selected-indices Active tab indices changed (multiple mode).\n\t\t\t\t\t * @type {number[]}\n\t\t\t\t\t */\n\t\t\t\t\tthis.$emit('update:selected-indices', [])\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst next = this.selectedIndices.includes(i)\n\t\t\t\t\t? this.selectedIndices.filter((x) => x !== i)\n\t\t\t\t\t: [...this.selectedIndices, i]\n\t\t\t\tthis.$emit('update:selected-indices', next)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (i === this.activeIndex) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.$emit('update:active-index', i)\n\t\t},\n\n\t\t/**\n\t\t * A chip clicked inside the overflow panel. Single-select closes the\n\t\t * panel on the way out; multi-select keeps it open so several lenses\n\t\t * can be toggled in one visit.\n\t\t *\n\t\t * @param {number} i Zero-based tab index.\n\t\t */\n\t\tonOverflowClick(i) {\n\t\t\tthis.onClick(i)\n\t\t\tif (!this.multiple) {\n\t\t\t\tthis.moreOpen = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * NcSelect input handler — normalises the option object(s) back to\n\t\t * indices and emits the matching model event.\n\t\t *\n\t\t * @param {object|object[]|null} val Selected option object(s).\n\t\t */\n\t\tonSelectInput(val) {\n\t\t\tif (this.multiple) {\n\t\t\t\tconst arr = Array.isArray(val) ? val : []\n\t\t\t\tthis.$emit('update:selected-indices', arr.map((o) => o.index))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.$emit('update:active-index', val ? val.index : null)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-quick-filter-bar {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 8px 12px;\n\tborder-bottom: 1px solid var(--color-border);\n\tbackground: var(--color-main-background);\n}\n\n.cn-quick-filter-bar__tabs {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 8px;\n}\n\n/* Inline variant — bare strip for embedding inside another bar (e.g. the\n CnActionsBar #filters slot), so it reads as a control beside the view toggle\n rather than a separate bordered row. */\n.cn-quick-filter-bar--inline {\n\tpadding: 0;\n\tborder-bottom: none;\n\tbackground: transparent;\n}\n\n/* Dropdown variant — give the NcSelect a sensible minimum so multi-select\n chips have room without stretching the whole bar. */\n.cn-quick-filter-bar--dropdown {\n\talign-items: center;\n}\n\n.cn-quick-filter-bar__select {\n\tmin-width: 220px;\n}\n\n.cn-quick-filter-bar__tab {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 4px 12px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-pill, 999px);\n\tbackground: transparent;\n\tcolor: var(--color-main-text);\n\tfont-size: 13px;\n\tfont-weight: 500;\n\tcursor: pointer;\n\ttransition: background-color 120ms, color 120ms, border-color 120ms;\n}\n\n.cn-quick-filter-bar__tab:hover,\n.cn-quick-filter-bar__tab:focus-visible {\n\tbackground: var(--color-background-hover);\n\toutline: none;\n}\n\n.cn-quick-filter-bar__tab--active {\n\tbackground: var(--color-primary-element, var(--color-primary, #4376fc));\n\tcolor: var(--color-primary-element-text, #fff);\n\tborder-color: var(--color-primary-element, var(--color-primary, #4376fc));\n}\n\n.cn-quick-filter-bar__icon {\n\tdisplay: inline-flex;\n}\n\n.cn-quick-filter-bar__label {\n\tline-height: 1;\n}\n\n/* NcPopover wraps its trigger in a plain `<div class=\"v-popper\">`, which as a\n block would drop the chip onto its own line under the strip. */\n.cn-quick-filter-bar > :deep(.v-popper) {\n\tdisplay: inline-flex;\n}\n\n/* The `⋯` glyph is the chip's whole content while no hidden lens is on, so the\n pill tightens to a circle rather than sitting as a wide empty capsule. */\n.cn-quick-filter-bar__more {\n\tgap: 4px;\n}\n\n.cn-quick-filter-bar__more--icon-only {\n\tpadding: 4px 8px;\n}\n\n.cn-quick-filter-bar__more[aria-expanded='true'] {\n\tborder-color: var(--color-primary-element, var(--color-primary, #4376fc));\n}\n\n.cn-quick-filter-bar__more-panel {\n\tpadding: 10px 12px 12px;\n\tmax-width: 280px;\n}\n\n.cn-quick-filter-bar__more-heading {\n\tmargin: 0 0 8px;\n\tfont-size: 12px;\n\tfont-weight: 600;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n/* The hidden lenses stay chips — the panel is more of the strip, not a\n different control that happens to hold the same filters. */\n.cn-quick-filter-bar__more-list {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 8px;\n\tmax-height: 320px;\n\toverflow-y: auto;\n}\n\n.cn-quick-filter-bar__more-item {\n\tmax-width: 100%;\n}\n\n.cn-quick-filter-bar__more-item .cn-quick-filter-bar__label {\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["CnIcon","t"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnQuickFilterBar.vue2.js","sources":["../../../../src/components/CnQuickFilterBar/CnQuickFilterBar.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: AGPL-3.0-or-later\n -->\n\n<template>\n\t<!-- Dropdown mode: a single NcSelect (multi or single) instead of the chip strip. -->\n\t<div\n\t\tv-if=\"mode === 'dropdown'\"\n\t\tclass=\"cn-quick-filter-bar cn-quick-filter-bar--dropdown\"\n\t\t:class=\"{ 'cn-quick-filter-bar--inline': inline }\">\n\t\t<NcSelect\n\t\t\t:modelValue=\"dropdownValue\"\n\t\t\t:options=\"dropdownOptions\"\n\t\t\t:multiple=\"multiple\"\n\t\t\t:keepOpen=\"multiple\"\n\t\t\t:clearable=\"true\"\n\t\t\tlabel=\"label\"\n\t\t\tclass=\"cn-quick-filter-bar__select\"\n\t\t\t:inputLabel=\"selectLabel\"\n\t\t\t:aria-label-combobox=\"selectLabel\"\n\t\t\t:placeholder=\"placeholder || selectLabel\"\n\t\t\t@update:modelValue=\"onSelectInput\" />\n\t</div>\n\t<!-- Chips mode (default): the clickable tab strip. -->\n\t<div v-else\n\t\tclass=\"cn-quick-filter-bar\"\n\t\t:class=\"{ 'cn-quick-filter-bar--inline': inline }\">\n\t\t<!-- The tablist holds ONLY tabs: the overflow chip is a sibling, not a\n\t\t child, because a role=\"tablist\" may not contain a menu button. -->\n\t\t<div class=\"cn-quick-filter-bar__tabs\" role=\"tablist\">\n\t\t\t<button\n\t\t\t\tv-for=\"entry in visibleEntries\"\n\t\t\t\t:key=\"entry.tab.label + ':' + entry.index\"\n\t\t\t\ttype=\"button\"\n\t\t\t\trole=\"tab\"\n\t\t\t\t:aria-selected=\"isChipActive(entry.index) ? 'true' : 'false'\"\n\t\t\t\tclass=\"cn-quick-filter-bar__tab\"\n\t\t\t\t:class=\"[{ 'cn-quick-filter-bar__tab--active': isChipActive(entry.index) }]\"\n\t\t\t\t@click=\"onClick(entry.index)\">\n\t\t\t\t<CnIcon\n\t\t\t\t\tv-if=\"entry.tab.icon\"\n\t\t\t\t\t:name=\"entry.tab.icon\"\n\t\t\t\t\t:size=\"16\"\n\t\t\t\t\tclass=\"cn-quick-filter-bar__icon\" />\n\t\t\t\t<span class=\"cn-quick-filter-bar__label\">{{ entry.tab.label }}</span>\n\t\t\t\t<span\n\t\t\t\t\tv-if=\"countOf(entry) !== null\"\n\t\t\t\t\tclass=\"cn-quick-filter-bar__count\"\n\t\t\t\t\tdata-testid=\"cn-quick-filter-count\">{{ ' ' + countOf(entry) }}</span>\n\t\t\t</button>\n\t\t</div>\n\t\t<!-- The overflow is a CHIP and not a toolbar button: it is one of the\n\t\t lenses, so it carries the same pill, the same active fill and the\n\t\t label of whichever hidden lens is on. -->\n\t\t<NcPopover\n\t\t\tv-if=\"overflowEntries.length > 0\"\n\t\t\tv-model:shown=\"moreOpen\"\n\t\t\t:triggers=\"[]\"\n\t\t\tpopupRole=\"dialog\"\n\t\t\tplacement=\"bottom-end\"\n\t\t\tpopoverBaseClass=\"cn-quick-filter-bar__popper\">\n\t\t\t<template #trigger>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tdata-testid=\"cn-quick-filter-more\"\n\t\t\t\t\tclass=\"cn-quick-filter-bar__tab cn-quick-filter-bar__more\"\n\t\t\t\t\t:class=\"{\n\t\t\t\t\t\t'cn-quick-filter-bar__tab--active': activeOverflowEntries.length > 0,\n\t\t\t\t\t\t'cn-quick-filter-bar__more--icon-only': !overflowLabel,\n\t\t\t\t\t}\"\n\t\t\t\t\t:aria-expanded=\"moreOpen ? 'true' : 'false'\"\n\t\t\t\t\taria-haspopup=\"dialog\"\n\t\t\t\t\t:aria-label=\"overflowAriaLabel\"\n\t\t\t\t\t:title=\"overflowAriaLabel\"\n\t\t\t\t\t@click=\"moreOpen = !moreOpen\">\n\t\t\t\t\t<span v-if=\"overflowLabel\" class=\"cn-quick-filter-bar__label\">{{ overflowLabel }}</span>\n\t\t\t\t\t<DotsHorizontal :size=\"16\" class=\"cn-quick-filter-bar__icon\" />\n\t\t\t\t</button>\n\t\t\t</template>\n\t\t\t<div\n\t\t\t\tclass=\"cn-quick-filter-bar__more-panel\"\n\t\t\t\t@keydown.escape.stop=\"moreOpen = false\">\n\t\t\t\t<p class=\"cn-quick-filter-bar__more-heading\">\n\t\t\t\t\t{{ moreHeading }}\n\t\t\t\t</p>\n\t\t\t\t<div class=\"cn-quick-filter-bar__more-list\">\n\t\t\t\t\t<button\n\t\t\t\t\t\tv-for=\"entry in overflowEntries\"\n\t\t\t\t\t\t:key=\"entry.tab.label + ':' + entry.index\"\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tdata-testid=\"cn-quick-filter-more-item\"\n\t\t\t\t\t\tclass=\"cn-quick-filter-bar__tab cn-quick-filter-bar__more-item\"\n\t\t\t\t\t\t:class=\"{ 'cn-quick-filter-bar__tab--active': isChipActive(entry.index) }\"\n\t\t\t\t\t\t:aria-pressed=\"isChipActive(entry.index) ? 'true' : 'false'\"\n\t\t\t\t\t\t@click=\"onOverflowClick(entry.index)\">\n\t\t\t\t\t\t<CnIcon\n\t\t\t\t\t\t\tv-if=\"entry.tab.icon\"\n\t\t\t\t\t\t\t:name=\"entry.tab.icon\"\n\t\t\t\t\t\t\t:size=\"16\"\n\t\t\t\t\t\t\tclass=\"cn-quick-filter-bar__icon\" />\n\t\t\t\t\t\t<span class=\"cn-quick-filter-bar__label\">{{ entry.tab.label }}</span>\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\tv-if=\"countOf(entry) !== null\"\n\t\t\t\t\t\t\tclass=\"cn-quick-filter-bar__count\"\n\t\t\t\t\t\t\tdata-testid=\"cn-quick-filter-count\">{{ ' ' + countOf(entry) }}</span>\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</NcPopover>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcPopover, NcSelect } from '@nextcloud/vue'\nimport DotsHorizontal from 'vue-material-design-icons/DotsHorizontal.vue'\nimport CnIcon from '../CnIcon/CnIcon.vue'\n\n/**\n * CnQuickFilterBar — quick-filter control rendered above a `type:\"index\"`\n * page's table. Each tab carries a manifest `filter` map that `CnIndexPage`\n * merges into the `useListView` fetch when the tab is active.\n *\n * Two presentations, chosen by `mode`:\n * - `'chips'` (default) — a horizontal row of pill-shaped buttons; the\n * active one is filled, the rest outlined. `maxVisible` caps how many\n * pills render and moves the rest into an overflow menu.\n * - `'dropdown'` — a single `NcSelect`. The \"All\" / empty-filter tab is\n * dropped from the options (an empty selection means \"all\").\n *\n * Two selection cardinalities, chosen by `multiple`:\n * - single (default) — one active tab; uses the `activeIndex` v-model\n * (`update:active-index`).\n * - `multiple` — several tabs at once; uses the `selectedIndices` array\n * prop + `update:selected-indices` event. `CnIndexPage` ORs the selected\n * tabs' filters together.\n *\n * Implementation is deliberately a thin styled `<button>` list (chips) /\n * `NcSelect` (dropdown) rather than `NcAppNavigation*` — this lives INSIDE\n * the index page, not as the app's main nav.\n */\nexport default {\n\tname: 'CnQuickFilterBar',\n\n\tcomponents: { CnIcon, DotsHorizontal, NcPopover, NcSelect },\n\n\t// NO `model: { prop, event }` OPTION.\n\t//\n\t// The Vue-2 `model` option is REMOVED in Vue 3 — read by nothing, warned\n\t// about by nothing — so it survived the migration as a declaration that\n\t// looked authoritative while doing exactly zero. The component's real\n\t// contract is the explicit `:active-index` / `@update:active-index` pair\n\t// (and `:selected-indices` / `@update:selected-indices` in `multiple`\n\t// mode), which is what `CnIndexPage` binds. Surfaced by\n\t// `vue/no-deprecated-model-definition`, which this repo now arms through\n\t// its own published preset.\n\n\tprops: {\n\t\t/**\n\t\t * Tab definitions — same shape `pages[].config.quickFilters[]` uses.\n\t\t * `filter` is consumed by the parent (CnIndexPage), not this component.\n\t\t */\n\t\ttabs: {\n\t\t\ttype: Array,\n\t\t\trequired: true,\n\t\t\tvalidator: (arr) => Array.isArray(arr) && arr.every((t) => t && typeof t.label === 'string'),\n\t\t},\n\n\t\t/**\n\t\t * Render bare (no padding / bottom border / background) for embedding\n\t\t * inline inside another bar — e.g. the `#filters` slot of `CnActionsBar`,\n\t\t * so the tabs sit beside the view toggle instead of as a separate row.\n\t\t */\n\t\tinline: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Index of the currently active tab, or `null` for none active. Single-select only. */\n\t\tactiveIndex: {\n\t\t\ttype: Number,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Presentation: `'chips'` (pill button strip, default) or\n\t\t * `'dropdown'` (a single `NcSelect`).\n\t\t *\n\t\t * @type {'chips'|'dropdown'}\n\t\t */\n\t\tmode: {\n\t\t\ttype: String,\n\t\t\tdefault: 'chips',\n\t\t\tvalidator: (v) => ['chips', 'dropdown'].includes(v),\n\t\t},\n\n\t\t/**\n\t\t * Allow more than one tab active at once. Selection is exposed via\n\t\t * `selectedIndices` + `update:selected-indices`; the parent ORs the\n\t\t * selected tabs' filters together.\n\t\t */\n\t\tmultiple: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Active tab indices when `multiple` is set (the array v-model).\n\t\t *\n\t\t * @type {number[]}\n\t\t */\n\t\tselectedIndices: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Accessible label / placeholder for the dropdown control. */\n\t\tselectLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: 'Filter',\n\t\t},\n\n\t\t/** Placeholder text for the dropdown (falls back to `selectLabel`). */\n\t\tplaceholder: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Chips mode only: how many pills render inline before the rest move\n\t\t * into an overflow menu. `0` (the default) renders every tab, which is\n\t\t * the behaviour this component has always had. The visible set is the\n\t\t * FIRST `maxVisible` tabs in declared order — reorder `tabs` to change\n\t\t * which ones stay out. Ignored in `dropdown` mode.\n\t\t */\n\t\tmaxVisible: {\n\t\t\ttype: Number,\n\t\t\tdefault: 0,\n\t\t\tvalidator: (v) => Number.isFinite(v) && v >= 0,\n\t\t},\n\n\t\t/**\n\t\t * How many records each tab matches, keyed by tab index (an array in\n\t\t * tab order works too). A tab with a number here shows it after its\n\t\t * label; a tab without one shows no count. A tab can also carry its\n\t\t * own `count`. `null` (the default) shows no counts at all.\n\t\t *\n\t\t * @type {{[index: number]: number}|Array<number|null>|null}\n\t\t */\n\t\tcounts: {\n\t\t\ttype: [Object, Array],\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: ['update:active-index', 'update:selected-indices'],\n\n\tdata() {\n\t\treturn {\n\t\t\t/** Whether the overflow chip's panel is open. */\n\t\t\tmoreOpen: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Every tab paired with its own index, so the overflow split can slice\n\t\t * the list without losing the index the parent's filters are keyed on.\n\t\t *\n\t\t * @return {Array<{tab: object, index: number}>}\n\t\t */\n\t\tentries() {\n\t\t\treturn this.tabs.map((tab, index) => ({ tab, index }))\n\t\t},\n\n\t\t/**\n\t\t * The tabs rendered as pills — all of them unless `maxVisible` caps it.\n\t\t *\n\t\t * @return {Array<{tab: object, index: number}>}\n\t\t */\n\t\tvisibleEntries() {\n\t\t\tif (!this.maxVisible || this.tabs.length <= this.maxVisible) {\n\t\t\t\treturn this.entries\n\t\t\t}\n\t\t\treturn this.entries.slice(0, this.maxVisible)\n\t\t},\n\n\t\t/**\n\t\t * The tabs moved into the overflow menu (empty when `maxVisible` is 0\n\t\t * or there is nothing to hide).\n\t\t *\n\t\t * @return {Array<{tab: object, index: number}>}\n\t\t */\n\t\toverflowEntries() {\n\t\t\tif (!this.maxVisible || this.tabs.length <= this.maxVisible) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn this.entries.slice(this.maxVisible)\n\t\t},\n\n\t\t/**\n\t\t * The active tabs that live in the overflow menu.\n\t\t *\n\t\t * @return {Array<{tab: object, index: number}>}\n\t\t */\n\t\tactiveOverflowEntries() {\n\t\t\treturn this.overflowEntries.filter((e) => this.isChipActive(e.index))\n\t\t},\n\n\t\t/**\n\t\t * Text on the overflow chip — an active hidden tab names itself there,\n\t\t * so the strip never reads \"All\" while the list is narrowed. Nothing\n\t\t * active leaves the chip as the bare `⋯` glyph.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\toverflowLabel() {\n\t\t\tconst active = this.activeOverflowEntries\n\t\t\tif (active.length === 1) {\n\t\t\t\treturn active[0].tab.label\n\t\t\t}\n\t\t\tif (active.length > 1) {\n\t\t\t\treturn t('nextcloud-vue', '{count} filters', { count: active.length })\n\t\t\t}\n\t\t\treturn ''\n\t\t},\n\n\t\t/** @return {string} Accessible name for the overflow chip. */\n\t\toverflowAriaLabel() {\n\t\t\tif (this.overflowLabel) {\n\t\t\t\treturn t('nextcloud-vue', '{label} — show the other filters', { label: this.overflowLabel })\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', '{count} more filters', { count: this.overflowEntries.length })\n\t\t},\n\n\t\t/** @return {string} Heading above the hidden chips in the panel. */\n\t\tmoreHeading() {\n\t\t\treturn t('nextcloud-vue', 'More filters')\n\t\t},\n\n\t\t/**\n\t\t * Dropdown options: every tab that carries a non-empty `filter`,\n\t\t * tagged with its original index. The \"All\" / empty-filter tab is\n\t\t * omitted — an empty selection already means \"all\".\n\t\t *\n\t\t * @return {Array<{label: string, index: number, icon: string}>}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views\n\t\t */\n\t\tdropdownOptions() {\n\t\t\treturn this.tabs\n\t\t\t\t.map((tab, index) => {\n\t\t\t\t\tconst count = this.countOf({ tab, index })\n\t\t\t\t\treturn {\n\t\t\t\t\t\tlabel: count === null ? tab.label : `${tab.label} (${count})`,\n\t\t\t\t\t\ticon: tab.icon,\n\t\t\t\t\t\tindex,\n\t\t\t\t\t\t_empty: this.isEmptyFilter(tab),\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.filter((o) => !o._empty)\n\t\t},\n\n\t\t/**\n\t\t * Current NcSelect value — an array of option objects when\n\t\t * `multiple`, a single option object (or null) otherwise.\n\t\t *\n\t\t * @return {object|object[]|null}\n\t\t */\n\t\tdropdownValue() {\n\t\t\tif (this.multiple) {\n\t\t\t\treturn this.dropdownOptions.filter((o) => this.selectedIndices.includes(o.index))\n\t\t\t}\n\t\t\treturn this.dropdownOptions.find((o) => o.index === this.activeIndex) || null\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * The count to show on a tab, or null for none: `counts[index]`\n\t\t * first, else the tab's own `count`.\n\t\t *\n\t\t * @param {{tab: object, index: number}} entry The tab and its index.\n\t\t * @return {number|null}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views\n\t\t */\n\t\tcountOf(entry) {\n\t\t\tconst fromProp = this.counts ? this.counts[entry.index] : undefined\n\t\t\tconst value = (fromProp !== undefined && fromProp !== null) ? fromProp : entry.tab.count\n\t\t\treturn (typeof value === 'number' && Number.isFinite(value)) ? value : null\n\t\t},\n\n\t\t/**\n\t\t * Is tab `i` rendered active in chips mode (handles both single\n\t\t * `activeIndex` and `multiple` `selectedIndices`).\n\t\t *\n\t\t * @param {number} i Zero-based tab index.\n\t\t * @return {boolean}\n\t\t */\n\t\tisChipActive(i) {\n\t\t\treturn this.multiple ? this.selectedIndices.includes(i) : i === this.activeIndex\n\t\t},\n\n\t\t/**\n\t\t * Whether a tab's `filter` map is absent/empty (the \"All\" tab).\n\t\t *\n\t\t * @param {{label: string, filter?: object}} tab An entry from the `tabs` prop.\n\t\t * @return {boolean} True when the tab applies no filter, so selecting it\n\t\t * clears the multi-selection rather than adding to it.\n\t\t */\n\t\tisEmptyFilter(tab) {\n\t\t\treturn !tab || !tab.filter || Object.keys(tab.filter).length === 0\n\t\t},\n\n\t\t/**\n\t\t * Chip-click handler. In single mode emits the new active index; in\n\t\t * multiple mode toggles the index in/out of the selection. Clicking\n\t\t * an empty-filter (\"All\") chip clears the multi-selection.\n\t\t *\n\t\t * @param {number} i Zero-based tab index.\n\t\t */\n\t\tonClick(i) {\n\t\t\tif (this.multiple) {\n\t\t\t\tif (this.isEmptyFilter(this.tabs[i])) {\n\t\t\t\t\t/**\n\t\t\t\t\t * @event update:selected-indices Active tab indices changed (multiple mode).\n\t\t\t\t\t * @type {number[]}\n\t\t\t\t\t */\n\t\t\t\t\tthis.$emit('update:selected-indices', [])\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst next = this.selectedIndices.includes(i)\n\t\t\t\t\t? this.selectedIndices.filter((x) => x !== i)\n\t\t\t\t\t: [...this.selectedIndices, i]\n\t\t\t\tthis.$emit('update:selected-indices', next)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (i === this.activeIndex) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.$emit('update:active-index', i)\n\t\t},\n\n\t\t/**\n\t\t * A chip clicked inside the overflow panel. Single-select closes the\n\t\t * panel on the way out; multi-select keeps it open so several lenses\n\t\t * can be toggled in one visit.\n\t\t *\n\t\t * @param {number} i Zero-based tab index.\n\t\t */\n\t\tonOverflowClick(i) {\n\t\t\tthis.onClick(i)\n\t\t\tif (!this.multiple) {\n\t\t\t\tthis.moreOpen = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * NcSelect input handler — normalises the option object(s) back to\n\t\t * indices and emits the matching model event.\n\t\t *\n\t\t * @param {object|object[]|null} val Selected option object(s).\n\t\t */\n\t\tonSelectInput(val) {\n\t\t\tif (this.multiple) {\n\t\t\t\tconst arr = Array.isArray(val) ? val : []\n\t\t\t\tthis.$emit('update:selected-indices', arr.map((o) => o.index))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.$emit('update:active-index', val ? val.index : null)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-quick-filter-bar {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 8px 12px;\n\tborder-bottom: 1px solid var(--color-border);\n\tbackground: var(--color-main-background);\n}\n\n.cn-quick-filter-bar__tabs {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 8px;\n}\n\n/* Inline variant — bare strip for embedding inside another bar (e.g. the\n CnActionsBar #filters slot), so it reads as a control beside the view toggle\n rather than a separate bordered row. */\n.cn-quick-filter-bar--inline {\n\tpadding: 0;\n\tborder-bottom: none;\n\tbackground: transparent;\n}\n\n/* Dropdown variant — give the NcSelect a sensible minimum so multi-select\n chips have room without stretching the whole bar. */\n.cn-quick-filter-bar--dropdown {\n\talign-items: center;\n}\n\n.cn-quick-filter-bar__select {\n\tmin-width: 220px;\n}\n\n.cn-quick-filter-bar__tab {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 4px 12px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-pill, 999px);\n\tbackground: transparent;\n\tcolor: var(--color-main-text);\n\tfont-size: 13px;\n\tfont-weight: 500;\n\tcursor: pointer;\n\ttransition: background-color 120ms, color 120ms, border-color 120ms;\n}\n\n.cn-quick-filter-bar__tab:hover,\n.cn-quick-filter-bar__tab:focus-visible {\n\tbackground: var(--color-background-hover);\n\toutline: none;\n}\n\n.cn-quick-filter-bar__tab--active {\n\tbackground: var(--color-primary-element, var(--color-primary, #4376fc));\n\tcolor: var(--color-primary-element-text, #fff);\n\tborder-color: var(--color-primary-element, var(--color-primary, #4376fc));\n}\n\n.cn-quick-filter-bar__icon {\n\tdisplay: inline-flex;\n}\n\n.cn-quick-filter-bar__label {\n\tline-height: 1;\n}\n\n/* Inherits the chip's own text colour, so it stays readable on the active\n (primary) fill as well as on the resting one. The count's text starts with\n a space: the chip is a flex row, so the space is not drawn, but it keeps a\n screen reader from reading label and count as one word. */\n.cn-quick-filter-bar__count {\n\tmin-width: 1.5em;\n\tpadding: 2px 6px;\n\tborder: 1px solid currentColor;\n\tborder-radius: var(--border-radius-pill, 10px);\n\tfont-size: 0.85em;\n\tfont-weight: 600;\n\tline-height: 1;\n\ttext-align: center;\n}\n\n/* NcPopover wraps its trigger in a plain `<div class=\"v-popper\">`, which as a\n block would drop the chip onto its own line under the strip. */\n.cn-quick-filter-bar > :deep(.v-popper) {\n\tdisplay: inline-flex;\n}\n\n/* The `⋯` glyph is the chip's whole content while no hidden lens is on, so the\n pill tightens to a circle rather than sitting as a wide empty capsule. */\n.cn-quick-filter-bar__more {\n\tgap: 4px;\n}\n\n.cn-quick-filter-bar__more--icon-only {\n\tpadding: 4px 8px;\n}\n\n.cn-quick-filter-bar__more[aria-expanded='true'] {\n\tborder-color: var(--color-primary-element, var(--color-primary, #4376fc));\n}\n\n.cn-quick-filter-bar__more-panel {\n\tpadding: 10px 12px 12px;\n\tmax-width: 280px;\n}\n\n.cn-quick-filter-bar__more-heading {\n\tmargin: 0 0 8px;\n\tfont-size: 12px;\n\tfont-weight: 600;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n/* The hidden lenses stay chips — the panel is more of the strip, not a\n different control that happens to hold the same filters. */\n.cn-quick-filter-bar__more-list {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 8px;\n\tmax-height: 320px;\n\toverflow-y: auto;\n}\n\n.cn-quick-filter-bar__more-item {\n\tmax-width: 100%;\n}\n\n.cn-quick-filter-bar__more-item .cn-quick-filter-bar__label {\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["CnIcon","t"],"mappings":";;;;;AAuHA;AACC;AACA;;AAEA;AACA;;;AAGA;;;AAGA;AACA;;;;AAIA;AACA;AACA;;AAEA;AACA;;AAED,aAAe;CACd,IAAI,EAAE,kBAAkB;;CAExB,UAAU,EAAE,UAAEA,UAAM,EAAE,cAAc,EAAE,SAAS,EAAE,QAAO,EAAG;;AAE3D;;;;;AAKA;AACA;;AAEA;;;AAGA,CAAA,KAAK,EAAE;AACN;;;;EAIA,IAAI,EAAE;GACL,IAAI,EAAE,KAAK;GACX,QAAQ,EAAE,IAAI;AACd,GAAA,SAAS,EAAE,CAAC,GAAG,KAAK,KAAK,CAAC,OAAO,CAAC,GAAG,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,KAAK,OAAO,CAAC,CAAC,UAAU,QAAQ,CAAC;GAC5F;;AAED;;;;;AAKA,EAAA,MAAM,EAAE;GACP,IAAI,EAAE,OAAO;GACb,OAAO,EAAE,KAAK;GACd;;AAED;EACA,WAAW,EAAE;GACZ,IAAI,EAAE,MAAM;GACZ,OAAO,EAAE,IAAI;GACb;;AAED;;AAEC;AACA;;;EAGD,IAAI,EAAE;GACL,IAAI,EAAE,MAAM;AACZ,GAAA,OAAO,EAAE,OAAO;GAChB,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;GACnD;;AAED;;;;;AAKA,EAAA,QAAQ,EAAE;GACT,IAAI,EAAE,OAAO;GACb,OAAO,EAAE,KAAK;GACd;;AAED;;AAEC;AACA;;AAED,EAAA,eAAe,EAAE;GAChB,IAAI,EAAE,KAAK;AACX,GAAA,OAAO,EAAE,MAAM,EAAE;GACjB;;;EAGD,WAAW,EAAE;GACZ,IAAI,EAAE,MAAM;AACZ,GAAA,OAAO,EAAE,QAAQ;GACjB;;;EAGD,WAAW,EAAE;GACZ,IAAI,EAAE,MAAM;GACZ,OAAO,EAAE,EAAE;GACX;;AAED;;;;;AAKC;;EAED,UAAU,EAAE;GACX,IAAI,EAAE,MAAM;AACZ,GAAA,OAAO,EAAE,CAAC;AACV,GAAA,SAAS,EAAE,CAAC,CAAC,KAAK,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAE,IAAG,CAAE,IAAG,CAAC;GAC9C;;AAED;;;;;AAKC;;;AAGD,EAAA,MAAM,EAAE;AACP,GAAA,IAAI,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC;GACrB,OAAO,EAAE,IAAI;GACb;EACD;;CAED,KAAK,EAAE,CAAC,qBAAqB,EAAE,yBAAyB,CAAC;;AAEzD,CAAA,IAAI,GAAG;AACN,EAAA,OAAO;AACN;AACA,GAAA,QAAQ,EAAE,KAAK;AAChB,GAAA;EACA;;AAED,CAAA,QAAQ,EAAE;AACT;;;AAGC;AACA;;AAED,EAAA,OAAO,GAAG;GACT,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,KAAK,MAAM,EAAE,GAAG,EAAE,KAAI,EAAG,CAAC,CAAA;GACrD;;AAED;;AAEC;AACA;;AAED,EAAA,cAAc,GAAG;GAChB,IAAI,CAAC,IAAI,CAAC,UAAW,IAAG,IAAI,CAAC,IAAI,CAAC,MAAK,IAAK,IAAI,CAAC,UAAU,EAAE;AAC5D,IAAA,OAAO,IAAI,CAAC,OAAM;AACnB,IAAA;AACA,GAAA,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,UAAU,CAAA;GAC5C;;AAED;;;AAGC;AACA;;AAED,EAAA,eAAe,GAAG;GACjB,IAAI,CAAC,IAAI,CAAC,UAAW,IAAG,IAAI,CAAC,IAAI,CAAC,MAAK,IAAK,IAAI,CAAC,UAAU,EAAE;AAC5D,IAAA,OAAO,EAAC;AACT,IAAA;AACA,GAAA,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAA;GACzC;;AAED;AACC;AACA;AACA;;EAED,qBAAqB,GAAG;AACvB,GAAA,OAAO,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;GACpE;;AAED;;;AAGC;AACA;AACA;;AAED,EAAA,aAAa,GAAG;AACf,GAAA,MAAM,SAAS,IAAI,CAAC,sBAAoB;AACxC,GAAA,IAAI,MAAM,CAAC,MAAK,KAAM,CAAC,EAAE;IACxB,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAI;AAC1B,IAAA;AACA,GAAA,IAAI,MAAM,CAAC,MAAO,GAAE,CAAC,EAAE;AACtB,IAAA,OAAOC,SAAC,CAAC,eAAe,EAAE,iBAAiB,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,MAAK,EAAG,CAAA;AACtE,IAAA;AACA,GAAA,OAAO,EAAC;GACR;;;AAGD,EAAA,iBAAiB,GAAG;GACnB,IAAI,IAAI,CAAC,aAAa,EAAE;AACvB,IAAA,OAAOA,SAAC,CAAC,eAAe,EAAE,kCAAkC,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,eAAe,CAAA;AAC5F,IAAA;AACA,GAAA,OAAOA,SAAC,CAAC,eAAe,EAAE,sBAAsB,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAA;GACxF;;;EAGD,WAAW,GAAG;AACb,GAAA,OAAOA,SAAC,CAAC,eAAe,EAAE,cAAc,CAAA;GACxC;;AAED;;;AAGC;AACA;;AAEA;;AAED,EAAA,eAAe,GAAG;AACjB,GAAA,OAAO,IAAI,CAAC,IAAG;AACd,KAAC,GAAG,CAAC,CAAC,GAAG,EAAE,KAAK,KAAK;AACpB,KAAA,MAAM,KAAI,GAAI,IAAI,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,OAAO,EAAA;AACzC,KAAA,OAAO;MACN,KAAK,EAAE,KAAM,KAAI,IAAK,GAAE,GAAG,CAAC,KAAM,GAAE,CAAC,EAAE,GAAG,CAAC,KAAK,CAAE,EAAC,EAAE,KAAK,CAAC,CAAC,CAAC;MAC7D,IAAI,EAAE,GAAG,CAAC,IAAI;MACd,KAAK;AACL,MAAA,MAAM,EAAE,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC;AAChC,MAAA;KACA,CAAA;KACA,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAA;GACzB;;AAED;;;AAGC;;;AAGD,EAAA,aAAa,GAAG;AACf,GAAA,IAAI,IAAI,CAAC,QAAQ,EAAE;AAClB,IAAA,OAAO,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;AACjF,IAAA;AACA,GAAA,OAAO,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAM,KAAI,IAAI,CAAC,WAAW,CAAE,IAAG,IAAG;GAC5E;EACD;;AAED,CAAA,OAAO,EAAE;AACR;;;AAGC;;AAEA;AACA;;AAED,EAAA,OAAO,CAAC,KAAK,EAAE;GACd,MAAM,QAAS,GAAE,IAAI,CAAC,MAAK,GAAI,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAA,GAAI,UAAQ;AAClE,GAAA,MAAM,KAAI,GAAI,CAAC,QAAO,KAAM,SAAQ,IAAK,QAAS,KAAI,IAAI,IAAI,QAAS,GAAE,KAAK,CAAC,GAAG,CAAC,MAAI;AACvF,GAAA,OAAO,CAAC,OAAO,KAAM,KAAI,QAAO,IAAK,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAI,GAAI,IAAG;GAC1E;;AAED;;AAEC;AACA;AACA;AACA;;AAED,EAAA,YAAY,CAAC,CAAC,EAAE;AACf,GAAA,OAAO,IAAI,CAAC,QAAO,GAAI,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAA,KAAM,IAAI,CAAC,WAAU;GAC/E;;AAED;;AAEC;AACA;;AAEA;;AAED,EAAA,aAAa,CAAC,GAAG,EAAE;GAClB,OAAO,CAAC,GAAE,IAAK,CAAC,GAAG,CAAC,MAAK,IAAK,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,MAAK,KAAM,CAAA;GACjE;;AAED;;;;AAIC;AACA;;EAED,OAAO,CAAC,CAAC,EAAE;AACV,GAAA,IAAI,IAAI,CAAC,QAAQ,EAAE;AAClB,IAAA,IAAI,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE;AACrC;AACC;AACA;;AAED,KAAA,IAAI,CAAC,KAAK,CAAC,yBAAyB,EAAE,EAAE,EAAA;KACxC,MAAK;AACN,KAAA;AACA,IAAA,MAAM,IAAG,GAAI,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAA;AAC3C,OAAE,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,CAAA;OAC1C,CAAC,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC,EAAA;AAC9B,IAAA,IAAI,CAAC,KAAK,CAAC,yBAAyB,EAAE,IAAI,EAAA;IAC1C,MAAK;AACN,IAAA;GACA,IAAI,CAAE,KAAI,IAAI,CAAC,WAAW,EAAE;IAC3B,MAAK;AACN,IAAA;AACA,GAAA,IAAI,CAAC,KAAK,CAAC,qBAAqB,EAAE,CAAC,EAAA;GACnC;;AAED;;;;AAIC;AACA;;AAED,EAAA,eAAe,CAAC,CAAC,EAAE;AAClB,GAAA,IAAI,CAAC,OAAO,CAAC,CAAC,EAAA;AACd,GAAA,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;AACnB,IAAA,IAAI,CAAC,QAAS,GAAE,MAAI;AACrB,IAAA;GACA;;AAED;;AAEC;AACA;;;AAGD,EAAA,aAAa,CAAC,GAAG,EAAE;AAClB,GAAA,IAAI,IAAI,CAAC,QAAQ,EAAE;AAClB,IAAA,MAAM,GAAI,GAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAA,GAAI,GAAE,GAAI,GAAC;IACxC,IAAI,CAAC,KAAK,CAAC,yBAAyB,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,EAAA;IAC7D,MAAK;AACN,IAAA;GACA,IAAI,CAAC,KAAK,CAAC,qBAAqB,EAAE,GAAE,GAAI,GAAG,CAAC,QAAQ,IAAI,EAAA;GACxD;EACD;AACF;;;;"}
|
|
@@ -6,15 +6,25 @@ const _hoisted_1 = {
|
|
|
6
6
|
};
|
|
7
7
|
const _hoisted_2 = ["aria-selected", "onClick"];
|
|
8
8
|
const _hoisted_3 = { class: "cn-quick-filter-bar__label" };
|
|
9
|
-
const _hoisted_4 =
|
|
10
|
-
|
|
9
|
+
const _hoisted_4 = {
|
|
10
|
+
key: 1,
|
|
11
|
+
class: "cn-quick-filter-bar__count",
|
|
12
|
+
"data-testid": "cn-quick-filter-count"
|
|
13
|
+
};
|
|
14
|
+
const _hoisted_5 = ["aria-expanded", "aria-label", "title"];
|
|
15
|
+
const _hoisted_6 = {
|
|
11
16
|
key: 0,
|
|
12
17
|
class: "cn-quick-filter-bar__label"
|
|
13
18
|
};
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
19
|
+
const _hoisted_7 = { class: "cn-quick-filter-bar__more-heading" };
|
|
20
|
+
const _hoisted_8 = { class: "cn-quick-filter-bar__more-list" };
|
|
21
|
+
const _hoisted_9 = ["aria-pressed", "onClick"];
|
|
22
|
+
const _hoisted_10 = { class: "cn-quick-filter-bar__label" };
|
|
23
|
+
const _hoisted_11 = {
|
|
24
|
+
key: 1,
|
|
25
|
+
class: "cn-quick-filter-bar__count",
|
|
26
|
+
"data-testid": "cn-quick-filter-count"
|
|
27
|
+
};
|
|
18
28
|
|
|
19
29
|
function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
20
30
|
const _component_NcSelect = resolveComponent("NcSelect");
|
|
@@ -63,7 +73,10 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
63
73
|
class: "cn-quick-filter-bar__icon"
|
|
64
74
|
}, null, 8 /* PROPS */, ["name"]))
|
|
65
75
|
: createCommentVNode("v-if", true),
|
|
66
|
-
createElementVNode("span", _hoisted_3, toDisplayString(entry.tab.label), 1 /* TEXT */)
|
|
76
|
+
createElementVNode("span", _hoisted_3, toDisplayString(entry.tab.label), 1 /* TEXT */),
|
|
77
|
+
($options.countOf(entry) !== null)
|
|
78
|
+
? (openBlock(), createElementBlock("span", _hoisted_4, toDisplayString(' ' + $options.countOf(entry)), 1 /* TEXT */))
|
|
79
|
+
: createCommentVNode("v-if", true)
|
|
67
80
|
], 10 /* CLASS, PROPS */, _hoisted_2))
|
|
68
81
|
}), 128 /* KEYED_FRAGMENT */))
|
|
69
82
|
]),
|
|
@@ -92,21 +105,21 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
92
105
|
onClick: _cache[0] || (_cache[0] = $event => ($data.moreOpen = !$data.moreOpen))
|
|
93
106
|
}, [
|
|
94
107
|
($options.overflowLabel)
|
|
95
|
-
? (openBlock(), createElementBlock("span",
|
|
108
|
+
? (openBlock(), createElementBlock("span", _hoisted_6, toDisplayString($options.overflowLabel), 1 /* TEXT */))
|
|
96
109
|
: createCommentVNode("v-if", true),
|
|
97
110
|
createVNode(_component_DotsHorizontal, {
|
|
98
111
|
size: 16,
|
|
99
112
|
class: "cn-quick-filter-bar__icon"
|
|
100
113
|
})
|
|
101
|
-
], 10 /* CLASS, PROPS */,
|
|
114
|
+
], 10 /* CLASS, PROPS */, _hoisted_5)
|
|
102
115
|
]),
|
|
103
116
|
default: withCtx(() => [
|
|
104
117
|
createElementVNode("div", {
|
|
105
118
|
class: "cn-quick-filter-bar__more-panel",
|
|
106
119
|
onKeydown: _cache[1] || (_cache[1] = withKeys(withModifiers($event => ($data.moreOpen = false), ["stop"]), ["escape"]))
|
|
107
120
|
}, [
|
|
108
|
-
createElementVNode("p",
|
|
109
|
-
createElementVNode("div",
|
|
121
|
+
createElementVNode("p", _hoisted_7, toDisplayString($options.moreHeading), 1 /* TEXT */),
|
|
122
|
+
createElementVNode("div", _hoisted_8, [
|
|
110
123
|
(openBlock(true), createElementBlock(Fragment, null, renderList($options.overflowEntries, (entry) => {
|
|
111
124
|
return (openBlock(), createElementBlock("button", {
|
|
112
125
|
key: entry.tab.label + ':' + entry.index,
|
|
@@ -124,8 +137,11 @@ function render(_ctx, _cache, $props, $setup, $data, $options) {
|
|
|
124
137
|
class: "cn-quick-filter-bar__icon"
|
|
125
138
|
}, null, 8 /* PROPS */, ["name"]))
|
|
126
139
|
: createCommentVNode("v-if", true),
|
|
127
|
-
createElementVNode("span",
|
|
128
|
-
|
|
140
|
+
createElementVNode("span", _hoisted_10, toDisplayString(entry.tab.label), 1 /* TEXT */),
|
|
141
|
+
($options.countOf(entry) !== null)
|
|
142
|
+
? (openBlock(), createElementBlock("span", _hoisted_11, toDisplayString(' ' + $options.countOf(entry)), 1 /* TEXT */))
|
|
143
|
+
: createCommentVNode("v-if", true)
|
|
144
|
+
], 10 /* CLASS, PROPS */, _hoisted_9))
|
|
129
145
|
}), 128 /* KEYED_FRAGMENT */))
|
|
130
146
|
])
|
|
131
147
|
], 32 /* NEED_HYDRATION */)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CnQuickFilterBar.vue3.js","sources":["../../../../src/components/CnQuickFilterBar/CnQuickFilterBar.vue?vue&type=template&id=8e121822&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: AGPL-3.0-or-later\n -->\n\n<template>\n\t<!-- Dropdown mode: a single NcSelect (multi or single) instead of the chip strip. -->\n\t<div\n\t\tv-if=\"mode === 'dropdown'\"\n\t\tclass=\"cn-quick-filter-bar cn-quick-filter-bar--dropdown\"\n\t\t:class=\"{ 'cn-quick-filter-bar--inline': inline }\">\n\t\t<NcSelect\n\t\t\t:modelValue=\"dropdownValue\"\n\t\t\t:options=\"dropdownOptions\"\n\t\t\t:multiple=\"multiple\"\n\t\t\t:keepOpen=\"multiple\"\n\t\t\t:clearable=\"true\"\n\t\t\tlabel=\"label\"\n\t\t\tclass=\"cn-quick-filter-bar__select\"\n\t\t\t:inputLabel=\"selectLabel\"\n\t\t\t:aria-label-combobox=\"selectLabel\"\n\t\t\t:placeholder=\"placeholder || selectLabel\"\n\t\t\t@update:modelValue=\"onSelectInput\" />\n\t</div>\n\t<!-- Chips mode (default): the clickable tab strip. -->\n\t<div v-else\n\t\tclass=\"cn-quick-filter-bar\"\n\t\t:class=\"{ 'cn-quick-filter-bar--inline': inline }\">\n\t\t<!-- The tablist holds ONLY tabs: the overflow chip is a sibling, not a\n\t\t child, because a role=\"tablist\" may not contain a menu button. -->\n\t\t<div class=\"cn-quick-filter-bar__tabs\" role=\"tablist\">\n\t\t\t<button\n\t\t\t\tv-for=\"entry in visibleEntries\"\n\t\t\t\t:key=\"entry.tab.label + ':' + entry.index\"\n\t\t\t\ttype=\"button\"\n\t\t\t\trole=\"tab\"\n\t\t\t\t:aria-selected=\"isChipActive(entry.index) ? 'true' : 'false'\"\n\t\t\t\tclass=\"cn-quick-filter-bar__tab\"\n\t\t\t\t:class=\"[{ 'cn-quick-filter-bar__tab--active': isChipActive(entry.index) }]\"\n\t\t\t\t@click=\"onClick(entry.index)\">\n\t\t\t\t<CnIcon\n\t\t\t\t\tv-if=\"entry.tab.icon\"\n\t\t\t\t\t:name=\"entry.tab.icon\"\n\t\t\t\t\t:size=\"16\"\n\t\t\t\t\tclass=\"cn-quick-filter-bar__icon\" />\n\t\t\t\t<span class=\"cn-quick-filter-bar__label\">{{ entry.tab.label }}</span>\n\t\t\t</button>\n\t\t</div>\n\t\t<!-- The overflow is a CHIP and not a toolbar button: it is one of the\n\t\t lenses, so it carries the same pill, the same active fill and the\n\t\t label of whichever hidden lens is on. -->\n\t\t<NcPopover\n\t\t\tv-if=\"overflowEntries.length > 0\"\n\t\t\tv-model:shown=\"moreOpen\"\n\t\t\t:triggers=\"[]\"\n\t\t\tpopupRole=\"dialog\"\n\t\t\tplacement=\"bottom-end\"\n\t\t\tpopoverBaseClass=\"cn-quick-filter-bar__popper\">\n\t\t\t<template #trigger>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tdata-testid=\"cn-quick-filter-more\"\n\t\t\t\t\tclass=\"cn-quick-filter-bar__tab cn-quick-filter-bar__more\"\n\t\t\t\t\t:class=\"{\n\t\t\t\t\t\t'cn-quick-filter-bar__tab--active': activeOverflowEntries.length > 0,\n\t\t\t\t\t\t'cn-quick-filter-bar__more--icon-only': !overflowLabel,\n\t\t\t\t\t}\"\n\t\t\t\t\t:aria-expanded=\"moreOpen ? 'true' : 'false'\"\n\t\t\t\t\taria-haspopup=\"dialog\"\n\t\t\t\t\t:aria-label=\"overflowAriaLabel\"\n\t\t\t\t\t:title=\"overflowAriaLabel\"\n\t\t\t\t\t@click=\"moreOpen = !moreOpen\">\n\t\t\t\t\t<span v-if=\"overflowLabel\" class=\"cn-quick-filter-bar__label\">{{ overflowLabel }}</span>\n\t\t\t\t\t<DotsHorizontal :size=\"16\" class=\"cn-quick-filter-bar__icon\" />\n\t\t\t\t</button>\n\t\t\t</template>\n\t\t\t<div\n\t\t\t\tclass=\"cn-quick-filter-bar__more-panel\"\n\t\t\t\t@keydown.escape.stop=\"moreOpen = false\">\n\t\t\t\t<p class=\"cn-quick-filter-bar__more-heading\">\n\t\t\t\t\t{{ moreHeading }}\n\t\t\t\t</p>\n\t\t\t\t<div class=\"cn-quick-filter-bar__more-list\">\n\t\t\t\t\t<button\n\t\t\t\t\t\tv-for=\"entry in overflowEntries\"\n\t\t\t\t\t\t:key=\"entry.tab.label + ':' + entry.index\"\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tdata-testid=\"cn-quick-filter-more-item\"\n\t\t\t\t\t\tclass=\"cn-quick-filter-bar__tab cn-quick-filter-bar__more-item\"\n\t\t\t\t\t\t:class=\"{ 'cn-quick-filter-bar__tab--active': isChipActive(entry.index) }\"\n\t\t\t\t\t\t:aria-pressed=\"isChipActive(entry.index) ? 'true' : 'false'\"\n\t\t\t\t\t\t@click=\"onOverflowClick(entry.index)\">\n\t\t\t\t\t\t<CnIcon\n\t\t\t\t\t\t\tv-if=\"entry.tab.icon\"\n\t\t\t\t\t\t\t:name=\"entry.tab.icon\"\n\t\t\t\t\t\t\t:size=\"16\"\n\t\t\t\t\t\t\tclass=\"cn-quick-filter-bar__icon\" />\n\t\t\t\t\t\t<span class=\"cn-quick-filter-bar__label\">{{ entry.tab.label }}</span>\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</NcPopover>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcPopover, NcSelect } from '@nextcloud/vue'\nimport DotsHorizontal from 'vue-material-design-icons/DotsHorizontal.vue'\nimport CnIcon from '../CnIcon/CnIcon.vue'\n\n/**\n * CnQuickFilterBar — quick-filter control rendered above a `type:\"index\"`\n * page's table. Each tab carries a manifest `filter` map that `CnIndexPage`\n * merges into the `useListView` fetch when the tab is active.\n *\n * Two presentations, chosen by `mode`:\n * - `'chips'` (default) — a horizontal row of pill-shaped buttons; the\n * active one is filled, the rest outlined. `maxVisible` caps how many\n * pills render and moves the rest into an overflow menu.\n * - `'dropdown'` — a single `NcSelect`. The \"All\" / empty-filter tab is\n * dropped from the options (an empty selection means \"all\").\n *\n * Two selection cardinalities, chosen by `multiple`:\n * - single (default) — one active tab; uses the `activeIndex` v-model\n * (`update:active-index`).\n * - `multiple` — several tabs at once; uses the `selectedIndices` array\n * prop + `update:selected-indices` event. `CnIndexPage` ORs the selected\n * tabs' filters together.\n *\n * Implementation is deliberately a thin styled `<button>` list (chips) /\n * `NcSelect` (dropdown) rather than `NcAppNavigation*` — this lives INSIDE\n * the index page, not as the app's main nav.\n */\nexport default {\n\tname: 'CnQuickFilterBar',\n\n\tcomponents: { CnIcon, DotsHorizontal, NcPopover, NcSelect },\n\n\t// NO `model: { prop, event }` OPTION.\n\t//\n\t// The Vue-2 `model` option is REMOVED in Vue 3 — read by nothing, warned\n\t// about by nothing — so it survived the migration as a declaration that\n\t// looked authoritative while doing exactly zero. The component's real\n\t// contract is the explicit `:active-index` / `@update:active-index` pair\n\t// (and `:selected-indices` / `@update:selected-indices` in `multiple`\n\t// mode), which is what `CnIndexPage` binds. Surfaced by\n\t// `vue/no-deprecated-model-definition`, which this repo now arms through\n\t// its own published preset.\n\n\tprops: {\n\t\t/**\n\t\t * Tab definitions — same shape `pages[].config.quickFilters[]` uses.\n\t\t * `filter` is consumed by the parent (CnIndexPage), not this component.\n\t\t */\n\t\ttabs: {\n\t\t\ttype: Array,\n\t\t\trequired: true,\n\t\t\tvalidator: (arr) => Array.isArray(arr) && arr.every((t) => t && typeof t.label === 'string'),\n\t\t},\n\n\t\t/**\n\t\t * Render bare (no padding / bottom border / background) for embedding\n\t\t * inline inside another bar — e.g. the `#filters` slot of `CnActionsBar`,\n\t\t * so the tabs sit beside the view toggle instead of as a separate row.\n\t\t */\n\t\tinline: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Index of the currently active tab, or `null` for none active. Single-select only. */\n\t\tactiveIndex: {\n\t\t\ttype: Number,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Presentation: `'chips'` (pill button strip, default) or\n\t\t * `'dropdown'` (a single `NcSelect`).\n\t\t *\n\t\t * @type {'chips'|'dropdown'}\n\t\t */\n\t\tmode: {\n\t\t\ttype: String,\n\t\t\tdefault: 'chips',\n\t\t\tvalidator: (v) => ['chips', 'dropdown'].includes(v),\n\t\t},\n\n\t\t/**\n\t\t * Allow more than one tab active at once. Selection is exposed via\n\t\t * `selectedIndices` + `update:selected-indices`; the parent ORs the\n\t\t * selected tabs' filters together.\n\t\t */\n\t\tmultiple: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Active tab indices when `multiple` is set (the array v-model).\n\t\t *\n\t\t * @type {number[]}\n\t\t */\n\t\tselectedIndices: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Accessible label / placeholder for the dropdown control. */\n\t\tselectLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: 'Filter',\n\t\t},\n\n\t\t/** Placeholder text for the dropdown (falls back to `selectLabel`). */\n\t\tplaceholder: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Chips mode only: how many pills render inline before the rest move\n\t\t * into an overflow menu. `0` (the default) renders every tab, which is\n\t\t * the behaviour this component has always had. The visible set is the\n\t\t * FIRST `maxVisible` tabs in declared order — reorder `tabs` to change\n\t\t * which ones stay out. Ignored in `dropdown` mode.\n\t\t */\n\t\tmaxVisible: {\n\t\t\ttype: Number,\n\t\t\tdefault: 0,\n\t\t\tvalidator: (v) => Number.isFinite(v) && v >= 0,\n\t\t},\n\t},\n\n\temits: ['update:active-index', 'update:selected-indices'],\n\n\tdata() {\n\t\treturn {\n\t\t\t/** Whether the overflow chip's panel is open. */\n\t\t\tmoreOpen: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Every tab paired with its own index, so the overflow split can slice\n\t\t * the list without losing the index the parent's filters are keyed on.\n\t\t *\n\t\t * @return {Array<{tab: object, index: number}>}\n\t\t */\n\t\tentries() {\n\t\t\treturn this.tabs.map((tab, index) => ({ tab, index }))\n\t\t},\n\n\t\t/**\n\t\t * The tabs rendered as pills — all of them unless `maxVisible` caps it.\n\t\t *\n\t\t * @return {Array<{tab: object, index: number}>}\n\t\t */\n\t\tvisibleEntries() {\n\t\t\tif (!this.maxVisible || this.tabs.length <= this.maxVisible) {\n\t\t\t\treturn this.entries\n\t\t\t}\n\t\t\treturn this.entries.slice(0, this.maxVisible)\n\t\t},\n\n\t\t/**\n\t\t * The tabs moved into the overflow menu (empty when `maxVisible` is 0\n\t\t * or there is nothing to hide).\n\t\t *\n\t\t * @return {Array<{tab: object, index: number}>}\n\t\t */\n\t\toverflowEntries() {\n\t\t\tif (!this.maxVisible || this.tabs.length <= this.maxVisible) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn this.entries.slice(this.maxVisible)\n\t\t},\n\n\t\t/**\n\t\t * The active tabs that live in the overflow menu.\n\t\t *\n\t\t * @return {Array<{tab: object, index: number}>}\n\t\t */\n\t\tactiveOverflowEntries() {\n\t\t\treturn this.overflowEntries.filter((e) => this.isChipActive(e.index))\n\t\t},\n\n\t\t/**\n\t\t * Text on the overflow chip — an active hidden tab names itself there,\n\t\t * so the strip never reads \"All\" while the list is narrowed. Nothing\n\t\t * active leaves the chip as the bare `⋯` glyph.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\toverflowLabel() {\n\t\t\tconst active = this.activeOverflowEntries\n\t\t\tif (active.length === 1) {\n\t\t\t\treturn active[0].tab.label\n\t\t\t}\n\t\t\tif (active.length > 1) {\n\t\t\t\treturn t('nextcloud-vue', '{count} filters', { count: active.length })\n\t\t\t}\n\t\t\treturn ''\n\t\t},\n\n\t\t/** @return {string} Accessible name for the overflow chip. */\n\t\toverflowAriaLabel() {\n\t\t\tif (this.overflowLabel) {\n\t\t\t\treturn t('nextcloud-vue', '{label} — show the other filters', { label: this.overflowLabel })\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', '{count} more filters', { count: this.overflowEntries.length })\n\t\t},\n\n\t\t/** @return {string} Heading above the hidden chips in the panel. */\n\t\tmoreHeading() {\n\t\t\treturn t('nextcloud-vue', 'More filters')\n\t\t},\n\n\t\t/**\n\t\t * Dropdown options: every tab that carries a non-empty `filter`,\n\t\t * tagged with its original index. The \"All\" / empty-filter tab is\n\t\t * omitted — an empty selection already means \"all\".\n\t\t *\n\t\t * @return {Array<{label: string, index: number, icon: string}>}\n\t\t */\n\t\tdropdownOptions() {\n\t\t\treturn this.tabs\n\t\t\t\t.map((tab, index) => ({ label: tab.label, icon: tab.icon, index, _empty: this.isEmptyFilter(tab) }))\n\t\t\t\t.filter((o) => !o._empty)\n\t\t},\n\n\t\t/**\n\t\t * Current NcSelect value — an array of option objects when\n\t\t * `multiple`, a single option object (or null) otherwise.\n\t\t *\n\t\t * @return {object|object[]|null}\n\t\t */\n\t\tdropdownValue() {\n\t\t\tif (this.multiple) {\n\t\t\t\treturn this.dropdownOptions.filter((o) => this.selectedIndices.includes(o.index))\n\t\t\t}\n\t\t\treturn this.dropdownOptions.find((o) => o.index === this.activeIndex) || null\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Is tab `i` rendered active in chips mode (handles both single\n\t\t * `activeIndex` and `multiple` `selectedIndices`).\n\t\t *\n\t\t * @param {number} i Zero-based tab index.\n\t\t * @return {boolean}\n\t\t */\n\t\tisChipActive(i) {\n\t\t\treturn this.multiple ? this.selectedIndices.includes(i) : i === this.activeIndex\n\t\t},\n\n\t\t/**\n\t\t * Whether a tab's `filter` map is absent/empty (the \"All\" tab).\n\t\t *\n\t\t * @param {{label: string, filter?: object}} tab An entry from the `tabs` prop.\n\t\t * @return {boolean} True when the tab applies no filter, so selecting it\n\t\t * clears the multi-selection rather than adding to it.\n\t\t */\n\t\tisEmptyFilter(tab) {\n\t\t\treturn !tab || !tab.filter || Object.keys(tab.filter).length === 0\n\t\t},\n\n\t\t/**\n\t\t * Chip-click handler. In single mode emits the new active index; in\n\t\t * multiple mode toggles the index in/out of the selection. Clicking\n\t\t * an empty-filter (\"All\") chip clears the multi-selection.\n\t\t *\n\t\t * @param {number} i Zero-based tab index.\n\t\t */\n\t\tonClick(i) {\n\t\t\tif (this.multiple) {\n\t\t\t\tif (this.isEmptyFilter(this.tabs[i])) {\n\t\t\t\t\t/**\n\t\t\t\t\t * @event update:selected-indices Active tab indices changed (multiple mode).\n\t\t\t\t\t * @type {number[]}\n\t\t\t\t\t */\n\t\t\t\t\tthis.$emit('update:selected-indices', [])\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst next = this.selectedIndices.includes(i)\n\t\t\t\t\t? this.selectedIndices.filter((x) => x !== i)\n\t\t\t\t\t: [...this.selectedIndices, i]\n\t\t\t\tthis.$emit('update:selected-indices', next)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (i === this.activeIndex) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.$emit('update:active-index', i)\n\t\t},\n\n\t\t/**\n\t\t * A chip clicked inside the overflow panel. Single-select closes the\n\t\t * panel on the way out; multi-select keeps it open so several lenses\n\t\t * can be toggled in one visit.\n\t\t *\n\t\t * @param {number} i Zero-based tab index.\n\t\t */\n\t\tonOverflowClick(i) {\n\t\t\tthis.onClick(i)\n\t\t\tif (!this.multiple) {\n\t\t\t\tthis.moreOpen = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * NcSelect input handler — normalises the option object(s) back to\n\t\t * indices and emits the matching model event.\n\t\t *\n\t\t * @param {object|object[]|null} val Selected option object(s).\n\t\t */\n\t\tonSelectInput(val) {\n\t\t\tif (this.multiple) {\n\t\t\t\tconst arr = Array.isArray(val) ? val : []\n\t\t\t\tthis.$emit('update:selected-indices', arr.map((o) => o.index))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.$emit('update:active-index', val ? val.index : null)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-quick-filter-bar {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 8px 12px;\n\tborder-bottom: 1px solid var(--color-border);\n\tbackground: var(--color-main-background);\n}\n\n.cn-quick-filter-bar__tabs {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 8px;\n}\n\n/* Inline variant — bare strip for embedding inside another bar (e.g. the\n CnActionsBar #filters slot), so it reads as a control beside the view toggle\n rather than a separate bordered row. */\n.cn-quick-filter-bar--inline {\n\tpadding: 0;\n\tborder-bottom: none;\n\tbackground: transparent;\n}\n\n/* Dropdown variant — give the NcSelect a sensible minimum so multi-select\n chips have room without stretching the whole bar. */\n.cn-quick-filter-bar--dropdown {\n\talign-items: center;\n}\n\n.cn-quick-filter-bar__select {\n\tmin-width: 220px;\n}\n\n.cn-quick-filter-bar__tab {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 4px 12px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-pill, 999px);\n\tbackground: transparent;\n\tcolor: var(--color-main-text);\n\tfont-size: 13px;\n\tfont-weight: 500;\n\tcursor: pointer;\n\ttransition: background-color 120ms, color 120ms, border-color 120ms;\n}\n\n.cn-quick-filter-bar__tab:hover,\n.cn-quick-filter-bar__tab:focus-visible {\n\tbackground: var(--color-background-hover);\n\toutline: none;\n}\n\n.cn-quick-filter-bar__tab--active {\n\tbackground: var(--color-primary-element, var(--color-primary, #4376fc));\n\tcolor: var(--color-primary-element-text, #fff);\n\tborder-color: var(--color-primary-element, var(--color-primary, #4376fc));\n}\n\n.cn-quick-filter-bar__icon {\n\tdisplay: inline-flex;\n}\n\n.cn-quick-filter-bar__label {\n\tline-height: 1;\n}\n\n/* NcPopover wraps its trigger in a plain `<div class=\"v-popper\">`, which as a\n block would drop the chip onto its own line under the strip. */\n.cn-quick-filter-bar > :deep(.v-popper) {\n\tdisplay: inline-flex;\n}\n\n/* The `⋯` glyph is the chip's whole content while no hidden lens is on, so the\n pill tightens to a circle rather than sitting as a wide empty capsule. */\n.cn-quick-filter-bar__more {\n\tgap: 4px;\n}\n\n.cn-quick-filter-bar__more--icon-only {\n\tpadding: 4px 8px;\n}\n\n.cn-quick-filter-bar__more[aria-expanded='true'] {\n\tborder-color: var(--color-primary-element, var(--color-primary, #4376fc));\n}\n\n.cn-quick-filter-bar__more-panel {\n\tpadding: 10px 12px 12px;\n\tmax-width: 280px;\n}\n\n.cn-quick-filter-bar__more-heading {\n\tmargin: 0 0 8px;\n\tfont-size: 12px;\n\tfont-weight: 600;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n/* The hidden lenses stay chips — the panel is more of the strip, not a\n different control that happens to hold the same filters. */\n.cn-quick-filter-bar__more-list {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 8px;\n\tmax-height: 320px;\n\toverflow-y: auto;\n}\n\n.cn-quick-filter-bar__more-item {\n\tmax-width: 100%;\n}\n\n.cn-quick-filter-bar__more-item .cn-quick-filter-bar__label {\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["_normalizeClass","_createVNode","_createElementVNode","_Fragment","_renderList","_createBlock","_toDisplayString","_createElementBlock"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CnQuickFilterBar.vue3.js","sources":["../../../../src/components/CnQuickFilterBar/CnQuickFilterBar.vue?vue&type=template&id=8e121822&lang.js"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2026 Conduction B.V.\n - SPDX-License-Identifier: AGPL-3.0-or-later\n -->\n\n<template>\n\t<!-- Dropdown mode: a single NcSelect (multi or single) instead of the chip strip. -->\n\t<div\n\t\tv-if=\"mode === 'dropdown'\"\n\t\tclass=\"cn-quick-filter-bar cn-quick-filter-bar--dropdown\"\n\t\t:class=\"{ 'cn-quick-filter-bar--inline': inline }\">\n\t\t<NcSelect\n\t\t\t:modelValue=\"dropdownValue\"\n\t\t\t:options=\"dropdownOptions\"\n\t\t\t:multiple=\"multiple\"\n\t\t\t:keepOpen=\"multiple\"\n\t\t\t:clearable=\"true\"\n\t\t\tlabel=\"label\"\n\t\t\tclass=\"cn-quick-filter-bar__select\"\n\t\t\t:inputLabel=\"selectLabel\"\n\t\t\t:aria-label-combobox=\"selectLabel\"\n\t\t\t:placeholder=\"placeholder || selectLabel\"\n\t\t\t@update:modelValue=\"onSelectInput\" />\n\t</div>\n\t<!-- Chips mode (default): the clickable tab strip. -->\n\t<div v-else\n\t\tclass=\"cn-quick-filter-bar\"\n\t\t:class=\"{ 'cn-quick-filter-bar--inline': inline }\">\n\t\t<!-- The tablist holds ONLY tabs: the overflow chip is a sibling, not a\n\t\t child, because a role=\"tablist\" may not contain a menu button. -->\n\t\t<div class=\"cn-quick-filter-bar__tabs\" role=\"tablist\">\n\t\t\t<button\n\t\t\t\tv-for=\"entry in visibleEntries\"\n\t\t\t\t:key=\"entry.tab.label + ':' + entry.index\"\n\t\t\t\ttype=\"button\"\n\t\t\t\trole=\"tab\"\n\t\t\t\t:aria-selected=\"isChipActive(entry.index) ? 'true' : 'false'\"\n\t\t\t\tclass=\"cn-quick-filter-bar__tab\"\n\t\t\t\t:class=\"[{ 'cn-quick-filter-bar__tab--active': isChipActive(entry.index) }]\"\n\t\t\t\t@click=\"onClick(entry.index)\">\n\t\t\t\t<CnIcon\n\t\t\t\t\tv-if=\"entry.tab.icon\"\n\t\t\t\t\t:name=\"entry.tab.icon\"\n\t\t\t\t\t:size=\"16\"\n\t\t\t\t\tclass=\"cn-quick-filter-bar__icon\" />\n\t\t\t\t<span class=\"cn-quick-filter-bar__label\">{{ entry.tab.label }}</span>\n\t\t\t\t<span\n\t\t\t\t\tv-if=\"countOf(entry) !== null\"\n\t\t\t\t\tclass=\"cn-quick-filter-bar__count\"\n\t\t\t\t\tdata-testid=\"cn-quick-filter-count\">{{ ' ' + countOf(entry) }}</span>\n\t\t\t</button>\n\t\t</div>\n\t\t<!-- The overflow is a CHIP and not a toolbar button: it is one of the\n\t\t lenses, so it carries the same pill, the same active fill and the\n\t\t label of whichever hidden lens is on. -->\n\t\t<NcPopover\n\t\t\tv-if=\"overflowEntries.length > 0\"\n\t\t\tv-model:shown=\"moreOpen\"\n\t\t\t:triggers=\"[]\"\n\t\t\tpopupRole=\"dialog\"\n\t\t\tplacement=\"bottom-end\"\n\t\t\tpopoverBaseClass=\"cn-quick-filter-bar__popper\">\n\t\t\t<template #trigger>\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tdata-testid=\"cn-quick-filter-more\"\n\t\t\t\t\tclass=\"cn-quick-filter-bar__tab cn-quick-filter-bar__more\"\n\t\t\t\t\t:class=\"{\n\t\t\t\t\t\t'cn-quick-filter-bar__tab--active': activeOverflowEntries.length > 0,\n\t\t\t\t\t\t'cn-quick-filter-bar__more--icon-only': !overflowLabel,\n\t\t\t\t\t}\"\n\t\t\t\t\t:aria-expanded=\"moreOpen ? 'true' : 'false'\"\n\t\t\t\t\taria-haspopup=\"dialog\"\n\t\t\t\t\t:aria-label=\"overflowAriaLabel\"\n\t\t\t\t\t:title=\"overflowAriaLabel\"\n\t\t\t\t\t@click=\"moreOpen = !moreOpen\">\n\t\t\t\t\t<span v-if=\"overflowLabel\" class=\"cn-quick-filter-bar__label\">{{ overflowLabel }}</span>\n\t\t\t\t\t<DotsHorizontal :size=\"16\" class=\"cn-quick-filter-bar__icon\" />\n\t\t\t\t</button>\n\t\t\t</template>\n\t\t\t<div\n\t\t\t\tclass=\"cn-quick-filter-bar__more-panel\"\n\t\t\t\t@keydown.escape.stop=\"moreOpen = false\">\n\t\t\t\t<p class=\"cn-quick-filter-bar__more-heading\">\n\t\t\t\t\t{{ moreHeading }}\n\t\t\t\t</p>\n\t\t\t\t<div class=\"cn-quick-filter-bar__more-list\">\n\t\t\t\t\t<button\n\t\t\t\t\t\tv-for=\"entry in overflowEntries\"\n\t\t\t\t\t\t:key=\"entry.tab.label + ':' + entry.index\"\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tdata-testid=\"cn-quick-filter-more-item\"\n\t\t\t\t\t\tclass=\"cn-quick-filter-bar__tab cn-quick-filter-bar__more-item\"\n\t\t\t\t\t\t:class=\"{ 'cn-quick-filter-bar__tab--active': isChipActive(entry.index) }\"\n\t\t\t\t\t\t:aria-pressed=\"isChipActive(entry.index) ? 'true' : 'false'\"\n\t\t\t\t\t\t@click=\"onOverflowClick(entry.index)\">\n\t\t\t\t\t\t<CnIcon\n\t\t\t\t\t\t\tv-if=\"entry.tab.icon\"\n\t\t\t\t\t\t\t:name=\"entry.tab.icon\"\n\t\t\t\t\t\t\t:size=\"16\"\n\t\t\t\t\t\t\tclass=\"cn-quick-filter-bar__icon\" />\n\t\t\t\t\t\t<span class=\"cn-quick-filter-bar__label\">{{ entry.tab.label }}</span>\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\tv-if=\"countOf(entry) !== null\"\n\t\t\t\t\t\t\tclass=\"cn-quick-filter-bar__count\"\n\t\t\t\t\t\t\tdata-testid=\"cn-quick-filter-count\">{{ ' ' + countOf(entry) }}</span>\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</NcPopover>\n\t</div>\n</template>\n\n<script>\nimport { translate as t } from '@nextcloud/l10n'\nimport { NcPopover, NcSelect } from '@nextcloud/vue'\nimport DotsHorizontal from 'vue-material-design-icons/DotsHorizontal.vue'\nimport CnIcon from '../CnIcon/CnIcon.vue'\n\n/**\n * CnQuickFilterBar — quick-filter control rendered above a `type:\"index\"`\n * page's table. Each tab carries a manifest `filter` map that `CnIndexPage`\n * merges into the `useListView` fetch when the tab is active.\n *\n * Two presentations, chosen by `mode`:\n * - `'chips'` (default) — a horizontal row of pill-shaped buttons; the\n * active one is filled, the rest outlined. `maxVisible` caps how many\n * pills render and moves the rest into an overflow menu.\n * - `'dropdown'` — a single `NcSelect`. The \"All\" / empty-filter tab is\n * dropped from the options (an empty selection means \"all\").\n *\n * Two selection cardinalities, chosen by `multiple`:\n * - single (default) — one active tab; uses the `activeIndex` v-model\n * (`update:active-index`).\n * - `multiple` — several tabs at once; uses the `selectedIndices` array\n * prop + `update:selected-indices` event. `CnIndexPage` ORs the selected\n * tabs' filters together.\n *\n * Implementation is deliberately a thin styled `<button>` list (chips) /\n * `NcSelect` (dropdown) rather than `NcAppNavigation*` — this lives INSIDE\n * the index page, not as the app's main nav.\n */\nexport default {\n\tname: 'CnQuickFilterBar',\n\n\tcomponents: { CnIcon, DotsHorizontal, NcPopover, NcSelect },\n\n\t// NO `model: { prop, event }` OPTION.\n\t//\n\t// The Vue-2 `model` option is REMOVED in Vue 3 — read by nothing, warned\n\t// about by nothing — so it survived the migration as a declaration that\n\t// looked authoritative while doing exactly zero. The component's real\n\t// contract is the explicit `:active-index` / `@update:active-index` pair\n\t// (and `:selected-indices` / `@update:selected-indices` in `multiple`\n\t// mode), which is what `CnIndexPage` binds. Surfaced by\n\t// `vue/no-deprecated-model-definition`, which this repo now arms through\n\t// its own published preset.\n\n\tprops: {\n\t\t/**\n\t\t * Tab definitions — same shape `pages[].config.quickFilters[]` uses.\n\t\t * `filter` is consumed by the parent (CnIndexPage), not this component.\n\t\t */\n\t\ttabs: {\n\t\t\ttype: Array,\n\t\t\trequired: true,\n\t\t\tvalidator: (arr) => Array.isArray(arr) && arr.every((t) => t && typeof t.label === 'string'),\n\t\t},\n\n\t\t/**\n\t\t * Render bare (no padding / bottom border / background) for embedding\n\t\t * inline inside another bar — e.g. the `#filters` slot of `CnActionsBar`,\n\t\t * so the tabs sit beside the view toggle instead of as a separate row.\n\t\t */\n\t\tinline: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/** Index of the currently active tab, or `null` for none active. Single-select only. */\n\t\tactiveIndex: {\n\t\t\ttype: Number,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Presentation: `'chips'` (pill button strip, default) or\n\t\t * `'dropdown'` (a single `NcSelect`).\n\t\t *\n\t\t * @type {'chips'|'dropdown'}\n\t\t */\n\t\tmode: {\n\t\t\ttype: String,\n\t\t\tdefault: 'chips',\n\t\t\tvalidator: (v) => ['chips', 'dropdown'].includes(v),\n\t\t},\n\n\t\t/**\n\t\t * Allow more than one tab active at once. Selection is exposed via\n\t\t * `selectedIndices` + `update:selected-indices`; the parent ORs the\n\t\t * selected tabs' filters together.\n\t\t */\n\t\tmultiple: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Active tab indices when `multiple` is set (the array v-model).\n\t\t *\n\t\t * @type {number[]}\n\t\t */\n\t\tselectedIndices: {\n\t\t\ttype: Array,\n\t\t\tdefault: () => [],\n\t\t},\n\n\t\t/** Accessible label / placeholder for the dropdown control. */\n\t\tselectLabel: {\n\t\t\ttype: String,\n\t\t\tdefault: 'Filter',\n\t\t},\n\n\t\t/** Placeholder text for the dropdown (falls back to `selectLabel`). */\n\t\tplaceholder: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\t/**\n\t\t * Chips mode only: how many pills render inline before the rest move\n\t\t * into an overflow menu. `0` (the default) renders every tab, which is\n\t\t * the behaviour this component has always had. The visible set is the\n\t\t * FIRST `maxVisible` tabs in declared order — reorder `tabs` to change\n\t\t * which ones stay out. Ignored in `dropdown` mode.\n\t\t */\n\t\tmaxVisible: {\n\t\t\ttype: Number,\n\t\t\tdefault: 0,\n\t\t\tvalidator: (v) => Number.isFinite(v) && v >= 0,\n\t\t},\n\n\t\t/**\n\t\t * How many records each tab matches, keyed by tab index (an array in\n\t\t * tab order works too). A tab with a number here shows it after its\n\t\t * label; a tab without one shows no count. A tab can also carry its\n\t\t * own `count`. `null` (the default) shows no counts at all.\n\t\t *\n\t\t * @type {{[index: number]: number}|Array<number|null>|null}\n\t\t */\n\t\tcounts: {\n\t\t\ttype: [Object, Array],\n\t\t\tdefault: null,\n\t\t},\n\t},\n\n\temits: ['update:active-index', 'update:selected-indices'],\n\n\tdata() {\n\t\treturn {\n\t\t\t/** Whether the overflow chip's panel is open. */\n\t\t\tmoreOpen: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Every tab paired with its own index, so the overflow split can slice\n\t\t * the list without losing the index the parent's filters are keyed on.\n\t\t *\n\t\t * @return {Array<{tab: object, index: number}>}\n\t\t */\n\t\tentries() {\n\t\t\treturn this.tabs.map((tab, index) => ({ tab, index }))\n\t\t},\n\n\t\t/**\n\t\t * The tabs rendered as pills — all of them unless `maxVisible` caps it.\n\t\t *\n\t\t * @return {Array<{tab: object, index: number}>}\n\t\t */\n\t\tvisibleEntries() {\n\t\t\tif (!this.maxVisible || this.tabs.length <= this.maxVisible) {\n\t\t\t\treturn this.entries\n\t\t\t}\n\t\t\treturn this.entries.slice(0, this.maxVisible)\n\t\t},\n\n\t\t/**\n\t\t * The tabs moved into the overflow menu (empty when `maxVisible` is 0\n\t\t * or there is nothing to hide).\n\t\t *\n\t\t * @return {Array<{tab: object, index: number}>}\n\t\t */\n\t\toverflowEntries() {\n\t\t\tif (!this.maxVisible || this.tabs.length <= this.maxVisible) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn this.entries.slice(this.maxVisible)\n\t\t},\n\n\t\t/**\n\t\t * The active tabs that live in the overflow menu.\n\t\t *\n\t\t * @return {Array<{tab: object, index: number}>}\n\t\t */\n\t\tactiveOverflowEntries() {\n\t\t\treturn this.overflowEntries.filter((e) => this.isChipActive(e.index))\n\t\t},\n\n\t\t/**\n\t\t * Text on the overflow chip — an active hidden tab names itself there,\n\t\t * so the strip never reads \"All\" while the list is narrowed. Nothing\n\t\t * active leaves the chip as the bare `⋯` glyph.\n\t\t *\n\t\t * @return {string}\n\t\t */\n\t\toverflowLabel() {\n\t\t\tconst active = this.activeOverflowEntries\n\t\t\tif (active.length === 1) {\n\t\t\t\treturn active[0].tab.label\n\t\t\t}\n\t\t\tif (active.length > 1) {\n\t\t\t\treturn t('nextcloud-vue', '{count} filters', { count: active.length })\n\t\t\t}\n\t\t\treturn ''\n\t\t},\n\n\t\t/** @return {string} Accessible name for the overflow chip. */\n\t\toverflowAriaLabel() {\n\t\t\tif (this.overflowLabel) {\n\t\t\t\treturn t('nextcloud-vue', '{label} — show the other filters', { label: this.overflowLabel })\n\t\t\t}\n\t\t\treturn t('nextcloud-vue', '{count} more filters', { count: this.overflowEntries.length })\n\t\t},\n\n\t\t/** @return {string} Heading above the hidden chips in the panel. */\n\t\tmoreHeading() {\n\t\t\treturn t('nextcloud-vue', 'More filters')\n\t\t},\n\n\t\t/**\n\t\t * Dropdown options: every tab that carries a non-empty `filter`,\n\t\t * tagged with its original index. The \"All\" / empty-filter tab is\n\t\t * omitted — an empty selection already means \"all\".\n\t\t *\n\t\t * @return {Array<{label: string, index: number, icon: string}>}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views\n\t\t */\n\t\tdropdownOptions() {\n\t\t\treturn this.tabs\n\t\t\t\t.map((tab, index) => {\n\t\t\t\t\tconst count = this.countOf({ tab, index })\n\t\t\t\t\treturn {\n\t\t\t\t\t\tlabel: count === null ? tab.label : `${tab.label} (${count})`,\n\t\t\t\t\t\ticon: tab.icon,\n\t\t\t\t\t\tindex,\n\t\t\t\t\t\t_empty: this.isEmptyFilter(tab),\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.filter((o) => !o._empty)\n\t\t},\n\n\t\t/**\n\t\t * Current NcSelect value — an array of option objects when\n\t\t * `multiple`, a single option object (or null) otherwise.\n\t\t *\n\t\t * @return {object|object[]|null}\n\t\t */\n\t\tdropdownValue() {\n\t\t\tif (this.multiple) {\n\t\t\t\treturn this.dropdownOptions.filter((o) => this.selectedIndices.includes(o.index))\n\t\t\t}\n\t\t\treturn this.dropdownOptions.find((o) => o.index === this.activeIndex) || null\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * The count to show on a tab, or null for none: `counts[index]`\n\t\t * first, else the tab's own `count`.\n\t\t *\n\t\t * @param {{tab: object, index: number}} entry The tab and its index.\n\t\t * @return {number|null}\n\t\t * @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views\n\t\t */\n\t\tcountOf(entry) {\n\t\t\tconst fromProp = this.counts ? this.counts[entry.index] : undefined\n\t\t\tconst value = (fromProp !== undefined && fromProp !== null) ? fromProp : entry.tab.count\n\t\t\treturn (typeof value === 'number' && Number.isFinite(value)) ? value : null\n\t\t},\n\n\t\t/**\n\t\t * Is tab `i` rendered active in chips mode (handles both single\n\t\t * `activeIndex` and `multiple` `selectedIndices`).\n\t\t *\n\t\t * @param {number} i Zero-based tab index.\n\t\t * @return {boolean}\n\t\t */\n\t\tisChipActive(i) {\n\t\t\treturn this.multiple ? this.selectedIndices.includes(i) : i === this.activeIndex\n\t\t},\n\n\t\t/**\n\t\t * Whether a tab's `filter` map is absent/empty (the \"All\" tab).\n\t\t *\n\t\t * @param {{label: string, filter?: object}} tab An entry from the `tabs` prop.\n\t\t * @return {boolean} True when the tab applies no filter, so selecting it\n\t\t * clears the multi-selection rather than adding to it.\n\t\t */\n\t\tisEmptyFilter(tab) {\n\t\t\treturn !tab || !tab.filter || Object.keys(tab.filter).length === 0\n\t\t},\n\n\t\t/**\n\t\t * Chip-click handler. In single mode emits the new active index; in\n\t\t * multiple mode toggles the index in/out of the selection. Clicking\n\t\t * an empty-filter (\"All\") chip clears the multi-selection.\n\t\t *\n\t\t * @param {number} i Zero-based tab index.\n\t\t */\n\t\tonClick(i) {\n\t\t\tif (this.multiple) {\n\t\t\t\tif (this.isEmptyFilter(this.tabs[i])) {\n\t\t\t\t\t/**\n\t\t\t\t\t * @event update:selected-indices Active tab indices changed (multiple mode).\n\t\t\t\t\t * @type {number[]}\n\t\t\t\t\t */\n\t\t\t\t\tthis.$emit('update:selected-indices', [])\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst next = this.selectedIndices.includes(i)\n\t\t\t\t\t? this.selectedIndices.filter((x) => x !== i)\n\t\t\t\t\t: [...this.selectedIndices, i]\n\t\t\t\tthis.$emit('update:selected-indices', next)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (i === this.activeIndex) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.$emit('update:active-index', i)\n\t\t},\n\n\t\t/**\n\t\t * A chip clicked inside the overflow panel. Single-select closes the\n\t\t * panel on the way out; multi-select keeps it open so several lenses\n\t\t * can be toggled in one visit.\n\t\t *\n\t\t * @param {number} i Zero-based tab index.\n\t\t */\n\t\tonOverflowClick(i) {\n\t\t\tthis.onClick(i)\n\t\t\tif (!this.multiple) {\n\t\t\t\tthis.moreOpen = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * NcSelect input handler — normalises the option object(s) back to\n\t\t * indices and emits the matching model event.\n\t\t *\n\t\t * @param {object|object[]|null} val Selected option object(s).\n\t\t */\n\t\tonSelectInput(val) {\n\t\t\tif (this.multiple) {\n\t\t\t\tconst arr = Array.isArray(val) ? val : []\n\t\t\t\tthis.$emit('update:selected-indices', arr.map((o) => o.index))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.$emit('update:active-index', val ? val.index : null)\n\t\t},\n\t},\n}\n</script>\n\n<style scoped>\n.cn-quick-filter-bar {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 8px;\n\tpadding: 8px 12px;\n\tborder-bottom: 1px solid var(--color-border);\n\tbackground: var(--color-main-background);\n}\n\n.cn-quick-filter-bar__tabs {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\talign-items: center;\n\tgap: 8px;\n}\n\n/* Inline variant — bare strip for embedding inside another bar (e.g. the\n CnActionsBar #filters slot), so it reads as a control beside the view toggle\n rather than a separate bordered row. */\n.cn-quick-filter-bar--inline {\n\tpadding: 0;\n\tborder-bottom: none;\n\tbackground: transparent;\n}\n\n/* Dropdown variant — give the NcSelect a sensible minimum so multi-select\n chips have room without stretching the whole bar. */\n.cn-quick-filter-bar--dropdown {\n\talign-items: center;\n}\n\n.cn-quick-filter-bar__select {\n\tmin-width: 220px;\n}\n\n.cn-quick-filter-bar__tab {\n\tdisplay: inline-flex;\n\talign-items: center;\n\tgap: 6px;\n\tpadding: 4px 12px;\n\tborder: 1px solid var(--color-border);\n\tborder-radius: var(--border-radius-pill, 999px);\n\tbackground: transparent;\n\tcolor: var(--color-main-text);\n\tfont-size: 13px;\n\tfont-weight: 500;\n\tcursor: pointer;\n\ttransition: background-color 120ms, color 120ms, border-color 120ms;\n}\n\n.cn-quick-filter-bar__tab:hover,\n.cn-quick-filter-bar__tab:focus-visible {\n\tbackground: var(--color-background-hover);\n\toutline: none;\n}\n\n.cn-quick-filter-bar__tab--active {\n\tbackground: var(--color-primary-element, var(--color-primary, #4376fc));\n\tcolor: var(--color-primary-element-text, #fff);\n\tborder-color: var(--color-primary-element, var(--color-primary, #4376fc));\n}\n\n.cn-quick-filter-bar__icon {\n\tdisplay: inline-flex;\n}\n\n.cn-quick-filter-bar__label {\n\tline-height: 1;\n}\n\n/* Inherits the chip's own text colour, so it stays readable on the active\n (primary) fill as well as on the resting one. The count's text starts with\n a space: the chip is a flex row, so the space is not drawn, but it keeps a\n screen reader from reading label and count as one word. */\n.cn-quick-filter-bar__count {\n\tmin-width: 1.5em;\n\tpadding: 2px 6px;\n\tborder: 1px solid currentColor;\n\tborder-radius: var(--border-radius-pill, 10px);\n\tfont-size: 0.85em;\n\tfont-weight: 600;\n\tline-height: 1;\n\ttext-align: center;\n}\n\n/* NcPopover wraps its trigger in a plain `<div class=\"v-popper\">`, which as a\n block would drop the chip onto its own line under the strip. */\n.cn-quick-filter-bar > :deep(.v-popper) {\n\tdisplay: inline-flex;\n}\n\n/* The `⋯` glyph is the chip's whole content while no hidden lens is on, so the\n pill tightens to a circle rather than sitting as a wide empty capsule. */\n.cn-quick-filter-bar__more {\n\tgap: 4px;\n}\n\n.cn-quick-filter-bar__more--icon-only {\n\tpadding: 4px 8px;\n}\n\n.cn-quick-filter-bar__more[aria-expanded='true'] {\n\tborder-color: var(--color-primary-element, var(--color-primary, #4376fc));\n}\n\n.cn-quick-filter-bar__more-panel {\n\tpadding: 10px 12px 12px;\n\tmax-width: 280px;\n}\n\n.cn-quick-filter-bar__more-heading {\n\tmargin: 0 0 8px;\n\tfont-size: 12px;\n\tfont-weight: 600;\n\tcolor: var(--color-text-maxcontrast);\n}\n\n/* The hidden lenses stay chips — the panel is more of the strip, not a\n different control that happens to hold the same filters. */\n.cn-quick-filter-bar__more-list {\n\tdisplay: flex;\n\tflex-wrap: wrap;\n\tgap: 8px;\n\tmax-height: 320px;\n\toverflow-y: auto;\n}\n\n.cn-quick-filter-bar__more-item {\n\tmax-width: 100%;\n}\n\n.cn-quick-filter-bar__more-item .cn-quick-filter-bar__label {\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n}\n</style>\n"],"names":["_normalizeClass","_createVNode","_createElementVNode","_Fragment","_renderList","_createBlock","_toDisplayString","_openBlock","_createElementBlock"],"mappings":"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"}
|
|
@@ -43,6 +43,26 @@ const CORE_TABS = ['files', 'notes', 'tags', 'tasks', 'auditTrail', 'shares'];
|
|
|
43
43
|
/** One-time guard so the legacy-path deprecation warning logs only once. */
|
|
44
44
|
let legacyWarned = false;
|
|
45
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Schema titles by numeric schema id, shared by every widget on the page so a
|
|
48
|
+
* schema is asked for once. OpenRegister's `/uses` and `/used` answer
|
|
49
|
+
* `@self.schema` as that id ("120"), which is not a word anyone can read.
|
|
50
|
+
*
|
|
51
|
+
* @type {Map<string, Promise<string>>}
|
|
52
|
+
*/
|
|
53
|
+
const schemaTitleCache = new Map();
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Whether a value is a bare number, the way OpenRegister names a schema by id.
|
|
57
|
+
*
|
|
58
|
+
* @param {string|number|undefined|null} value The value.
|
|
59
|
+
* @return {boolean}
|
|
60
|
+
*/
|
|
61
|
+
function isNumericId(value) {
|
|
62
|
+
return (typeof value === 'number' && Number.isFinite(value))
|
|
63
|
+
|| (typeof value === 'string' && /^\d+$/.test(value.trim()))
|
|
64
|
+
}
|
|
65
|
+
|
|
46
66
|
/**
|
|
47
67
|
* Leaf groups surfaced by the aggregated `/relations` endpoint, in tab order.
|
|
48
68
|
* `responseKey` is the key in the `/relations` payload; `integrationId` is the
|
|
@@ -125,6 +145,14 @@ var script = {
|
|
|
125
145
|
NcEmptyContent,
|
|
126
146
|
},
|
|
127
147
|
|
|
148
|
+
inject: {
|
|
149
|
+
/**
|
|
150
|
+
* The host app's translator (CnAppRoot provides it), so a schema title
|
|
151
|
+
* reads in the app's language. Without it a title reads as stored.
|
|
152
|
+
*/
|
|
153
|
+
cnTranslate: { default: () => (key) => key },
|
|
154
|
+
},
|
|
155
|
+
|
|
128
156
|
props: {
|
|
129
157
|
/** Widget title shown in the header. */
|
|
130
158
|
title: {
|
|
@@ -388,6 +416,8 @@ var script = {
|
|
|
388
416
|
* @type {HTMLInputElement|null}
|
|
389
417
|
*/
|
|
390
418
|
fileInputEl: null,
|
|
419
|
+
/** Schema titles by numeric schema id, for object rows. */
|
|
420
|
+
schemaTitles: {},
|
|
391
421
|
/** Whether any section/tab is currently fetching. */
|
|
392
422
|
loading: false,
|
|
393
423
|
/** True once the first fetch has completed (gates the empty state). */
|
|
@@ -1080,9 +1110,74 @@ var script = {
|
|
|
1080
1110
|
self.name,
|
|
1081
1111
|
raw.summary,
|
|
1082
1112
|
raw.description,
|
|
1083
|
-
].find((v) => typeof v === 'string' && v.trim() && !isUuid(v))
|
|
1084
|
-
|
|
1085
|
-
|
|
1113
|
+
].find((v) => typeof v === 'string' && v.trim() && !isUuid(v))
|
|
1114
|
+
|| [raw.givenName, raw.familyName].filter((v) => typeof v === 'string' && v.trim()).join(' ');
|
|
1115
|
+
// OpenRegister names the schema by numeric id: show its title, never the number.
|
|
1116
|
+
const schema = this.schemaName(self.schema || raw.schema);
|
|
1117
|
+
const finalLabel = label || schema || String(id);
|
|
1118
|
+
return { id, label: finalLabel, meta: schema === finalLabel ? '' : schema, raw }
|
|
1119
|
+
},
|
|
1120
|
+
|
|
1121
|
+
/**
|
|
1122
|
+
* How a row names its schema: the translated title for a numeric id
|
|
1123
|
+
* (empty until it is read, or when it cannot be), a slug as it is.
|
|
1124
|
+
*
|
|
1125
|
+
* @param {string|number|undefined|null} schema - `@self.schema` of the related object.
|
|
1126
|
+
* @return {string}
|
|
1127
|
+
*/
|
|
1128
|
+
schemaName(schema) {
|
|
1129
|
+
if (isNumericId(schema)) {
|
|
1130
|
+
const title = this.schemaTitles[String(schema).trim()];
|
|
1131
|
+
return title ? this.cnTranslate(title) : ''
|
|
1132
|
+
}
|
|
1133
|
+
return typeof schema === 'string' ? schema : ''
|
|
1134
|
+
},
|
|
1135
|
+
|
|
1136
|
+
/**
|
|
1137
|
+
* Read the titles of the numeric schema ids in these envelopes once,
|
|
1138
|
+
* before the rows are built, so no row shows a number first.
|
|
1139
|
+
*
|
|
1140
|
+
* @param {Array<object|null>} envelopes - The `{ results }` payloads.
|
|
1141
|
+
* @return {Promise<void>}
|
|
1142
|
+
*/
|
|
1143
|
+
async resolveSchemaTitles(envelopes) {
|
|
1144
|
+
const ids = new Set();
|
|
1145
|
+
for (const envelope of envelopes) {
|
|
1146
|
+
for (const raw of ((envelope && envelope.results) || [])) {
|
|
1147
|
+
const schema = (raw && (raw['@self'] || {}).schema) ?? (raw && raw.schema);
|
|
1148
|
+
if (isNumericId(schema)) {
|
|
1149
|
+
ids.add(String(schema).trim());
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
const titles = { ...this.schemaTitles };
|
|
1154
|
+
await Promise.all([...ids].filter((id) => !(id in titles)).map(async (id) => {
|
|
1155
|
+
if (!schemaTitleCache.has(id)) {
|
|
1156
|
+
schemaTitleCache.set(id, this.fetchSchemaTitle(id));
|
|
1157
|
+
}
|
|
1158
|
+
titles[id] = await schemaTitleCache.get(id);
|
|
1159
|
+
}));
|
|
1160
|
+
this.schemaTitles = titles;
|
|
1161
|
+
},
|
|
1162
|
+
|
|
1163
|
+
/**
|
|
1164
|
+
* GET one schema's title; '' when it cannot be read.
|
|
1165
|
+
*
|
|
1166
|
+
* @param {string} id - The numeric schema id.
|
|
1167
|
+
* @return {Promise<string>}
|
|
1168
|
+
*/
|
|
1169
|
+
async fetchSchemaTitle(id) {
|
|
1170
|
+
try {
|
|
1171
|
+
const response = await fetch(generateUrl('/apps/openregister/api/schemas/{id}', { id }), { method: 'GET', headers: buildHeaders() });
|
|
1172
|
+
if (!response.ok) {
|
|
1173
|
+
return ''
|
|
1174
|
+
}
|
|
1175
|
+
const schema = await response.json();
|
|
1176
|
+
const title = schema && (schema.title || schema.slug);
|
|
1177
|
+
return typeof title === 'string' ? title : ''
|
|
1178
|
+
} catch {
|
|
1179
|
+
return ''
|
|
1180
|
+
}
|
|
1086
1181
|
},
|
|
1087
1182
|
|
|
1088
1183
|
/**
|
|
@@ -1186,6 +1281,7 @@ var script = {
|
|
|
1186
1281
|
const groups = [];
|
|
1187
1282
|
|
|
1188
1283
|
if (this.showObjects) {
|
|
1284
|
+
await this.resolveSchemaTitles([uses, used, contracts]);
|
|
1189
1285
|
const objectItems = this.mergeObjectResults([uses, used, contracts]);
|
|
1190
1286
|
groups.push({ key: 'objects', label: this.objectsLabel, icon: 'FileTreeOutline', integrationId: '', items: objectItems, total: objectItems.length });
|
|
1191
1287
|
}
|
|
@@ -1311,6 +1407,7 @@ var script = {
|
|
|
1311
1407
|
calls.push(store.fetchContracts(type, id));
|
|
1312
1408
|
}
|
|
1313
1409
|
const groups = await Promise.all(calls);
|
|
1410
|
+
await this.resolveSchemaTitles(groups.map((results) => ({ results: results || [] })));
|
|
1314
1411
|
const seen = new Set();
|
|
1315
1412
|
const merged = [];
|
|
1316
1413
|
for (const group of groups) {
|