@conduction/nextcloud-vue 2.59.0 → 2.61.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/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.map +1 -1
- package/dist/esm/components/CnAppNav/CnAppNav.vue2.js +80 -1
- 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 +232 -10
- 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 +46 -0
- package/dist/esm/components/CnBoardView/CnBoardView.vue2.js.map +1 -1
- package/dist/esm/components/CnBoardView/CnBoardView.vue3.js +11 -4
- 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/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/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/CnDashboardPage/CnDashboardPage.vue2.js +10 -2
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +503 -28
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +303 -144
- 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/CnHeaderWidget/CnHeaderWidget.vue2.js +171 -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 +132 -3
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +6 -3
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue.js +10 -0
- package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue.js.map +1 -0
- package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue2.js +316 -0
- package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue2.js.map +1 -0
- package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue3.js +105 -0
- package/dist/esm/components/CnLinkCardsPage/CnLinkCardsPage.vue3.js.map +1 -0
- 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/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/CnPageRenderer/pageTypes.js +1 -0
- package/dist/esm/components/CnPageRenderer/pageTypes.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/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/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/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 +16 -0
- package/dist/esm/index.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 +1036 -19
- 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/headers.js +28 -2
- package/dist/esm/utils/headers.js.map +1 -1
- package/dist/esm/utils/libraryWidgetKeys.js +5 -0
- package/dist/esm/utils/libraryWidgetKeys.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/validateManifestV2.compiled.js +2 -2
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +16019 -9528
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +1036 -19
- package/l10n/en.json +55 -1
- package/l10n/nl.json +55 -1
- package/package.json +1 -1
- package/scripts/.jsdoc-baselines.json +4 -0
- package/src/components/CnActionsMenu/CnActionsMenu.vue +14 -1
- package/src/components/CnAppNav/CnAppNav.md +26 -0
- package/src/components/CnAppNav/CnAppNav.vue +145 -2
- package/src/components/CnBannerWidget/CnBannerWidget.vue +373 -11
- package/src/components/CnBannerWidgetForm/CnBannerWidgetForm.vue +65 -3
- package/src/components/CnBoardView/CnBoardView.vue +77 -0
- 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/CnCellRenderer/CnCellRenderer.vue +207 -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/CnDashboardPage/CnDashboardPage.vue +10 -2
- package/src/components/CnDetailPage/CnDetailPage.md +16 -0
- package/src/components/CnDetailPage/CnDetailPage.vue +687 -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/CnHeaderWidget/CnHeaderWidget.vue +191 -14
- package/src/components/CnHeaderWidgetForm/CnHeaderWidgetForm.vue +53 -3
- package/src/components/CnIndexPage/CnIndexPage.vue +135 -3
- package/src/components/CnLinkCardsPage/CnLinkCardsPage.vue +484 -0
- package/src/components/CnLinkCardsPage/index.js +4 -0
- 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/CnObjectDataWidget/CnObjectDataWidget.vue +49 -0
- package/src/components/CnObjectDataWidgetForm/CnObjectDataWidgetForm.vue +6 -0
- package/src/components/CnObjectKanban/CnObjectKanban.vue +86 -2
- package/src/components/CnPageRenderer/pageTypes.js +1 -0
- package/src/components/CnQuickFilterBar/CnQuickFilterBar.md +3 -0
- package/src/components/CnQuickFilterBar/CnQuickFilterBar.vue +60 -1
- 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/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 +244 -10
- package/src/components/CnTabsWidget/CnTabsWidget.vue +63 -4
- 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 +12 -0
- package/src/css/detail-page.css +57 -2
- package/src/index.js +16 -0
- package/src/schemas/app-manifest-v2.schema.json +433 -3
- package/src/schemas/app-manifest.schema.json +2 -1
- package/src/types/manifest.d.ts +1 -1
- 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/headers.js +28 -2
- package/src/utils/index.js +1 -0
- package/src/utils/libraryWidgetKeys.js +5 -0
- package/src/utils/readPath.js +27 -0
- package/src/utils/validateManifestV2.compiled.js +2 -2
|
@@ -4,15 +4,22 @@
|
|
|
4
4
|
-->
|
|
5
5
|
|
|
6
6
|
<template>
|
|
7
|
-
<div class="cn-header-widget" :style="wrapperStyle">
|
|
7
|
+
<div class="cn-header-widget" :class="{ 'cn-header-widget--plain': isPlain }" :style="wrapperStyle">
|
|
8
8
|
<div
|
|
9
9
|
v-if="hasOverlay"
|
|
10
10
|
class="cn-header-widget__overlay"
|
|
11
11
|
:style="overlayStyle"
|
|
12
12
|
aria-hidden="true" />
|
|
13
13
|
<div class="cn-header-widget__content" :style="contentStyle">
|
|
14
|
+
<p
|
|
15
|
+
v-if="dateLine"
|
|
16
|
+
class="cn-header-widget__date"
|
|
17
|
+
:style="dateStyle"
|
|
18
|
+
data-testid="cn-header-widget-date">
|
|
19
|
+
{{ dateLine }}
|
|
20
|
+
</p>
|
|
14
21
|
<h2 v-if="hasTitle" class="cn-header-widget__title" :style="textStyle">
|
|
15
|
-
{{
|
|
22
|
+
{{ headingText }}
|
|
16
23
|
</h2>
|
|
17
24
|
<p v-if="hasSubtitle" class="cn-header-widget__subtitle" :style="textStyle">
|
|
18
25
|
{{ subtitle }}
|
|
@@ -38,7 +45,8 @@
|
|
|
38
45
|
</template>
|
|
39
46
|
|
|
40
47
|
<script>
|
|
41
|
-
import {
|
|
48
|
+
import { getCurrentUser } from '@nextcloud/auth'
|
|
49
|
+
import { getCanonicalLocale, translate as t } from '@nextcloud/l10n'
|
|
42
50
|
import { resolveImageUrl } from '../../utils/resolveImageUrl.js'
|
|
43
51
|
|
|
44
52
|
const ALLOWED_OVERLAY_MODES = ['none', 'tint', 'gradient-bottom']
|
|
@@ -75,6 +83,16 @@ const VERTICAL_ALIGN_FLEX = Object.freeze({
|
|
|
75
83
|
* External CTA URLs (http/https) open in a new tab with
|
|
76
84
|
* `rel="noopener noreferrer"`; relative / anchor URLs stay in the current tab.
|
|
77
85
|
*
|
|
86
|
+
* Optional greeting: `greeting: true` makes the heading greet the signed-in
|
|
87
|
+
* user by time of day and first name ("Good afternoon, Pieter"; `"full"` uses
|
|
88
|
+
* the whole display name), and `showDate: true` adds a line with today's
|
|
89
|
+
* date above it. `plain: true` drops the coloured background and aligns the
|
|
90
|
+
* text to the start, which is how a greeting usually sits on a dashboard.
|
|
91
|
+
*
|
|
92
|
+
* ```json
|
|
93
|
+
* { "widgetKey": "header", "props": { "content": { "greeting": true, "showDate": true, "plain": true } } }
|
|
94
|
+
* ```
|
|
95
|
+
*
|
|
78
96
|
* Registered as the `header` dashboard widget type via the renderer's
|
|
79
97
|
* `index.js`.
|
|
80
98
|
*/
|
|
@@ -96,9 +114,10 @@ export default {
|
|
|
96
114
|
/**
|
|
97
115
|
* Persisted widget content: `{title, subtitle, backgroundImageUrl,
|
|
98
116
|
* backgroundImageFileId, backgroundColor, overlayMode, overlayColor,
|
|
99
|
-
* overlayOpacity, textColor, textAlign, verticalAlign, height, cta
|
|
100
|
-
* All fields are optional except `title
|
|
101
|
-
*
|
|
117
|
+
* overlayOpacity, textColor, textAlign, verticalAlign, height, cta,
|
|
118
|
+
* greeting, showDate, plain}`. All fields are optional except `title`
|
|
119
|
+
* (or `greeting`); unknown enum values collapse to documented defaults
|
|
120
|
+
* and the renderer never throws.
|
|
102
121
|
*
|
|
103
122
|
* @type {object}
|
|
104
123
|
*/
|
|
@@ -118,6 +137,17 @@ export default {
|
|
|
118
137
|
default: null,
|
|
119
138
|
},
|
|
120
139
|
/* eslint-enable vue/no-unused-properties */
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* The moment the greeting and the date line are computed for. Leave
|
|
143
|
+
* empty for the current time.
|
|
144
|
+
*
|
|
145
|
+
* @type {Date|null}
|
|
146
|
+
*/
|
|
147
|
+
now: {
|
|
148
|
+
type: Date,
|
|
149
|
+
default: null,
|
|
150
|
+
},
|
|
121
151
|
},
|
|
122
152
|
|
|
123
153
|
data() {
|
|
@@ -153,9 +183,121 @@ export default {
|
|
|
153
183
|
return typeof value === 'string' && value !== '' ? this.effectiveTranslate(value) : ''
|
|
154
184
|
},
|
|
155
185
|
|
|
156
|
-
/** Whether a non-empty title
|
|
186
|
+
/** Whether a heading renders: a non-empty title or a greeting. */
|
|
157
187
|
hasTitle() {
|
|
158
|
-
return this.
|
|
188
|
+
return this.headingText !== ''
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* The moment the greeting and date line are computed for.
|
|
193
|
+
*
|
|
194
|
+
* @return {Date}
|
|
195
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header
|
|
196
|
+
*/
|
|
197
|
+
reference() {
|
|
198
|
+
return this.now instanceof Date ? this.now : new Date()
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Whether the plain presentation is on (no coloured background).
|
|
203
|
+
*
|
|
204
|
+
* @return {boolean}
|
|
205
|
+
*/
|
|
206
|
+
isPlain() {
|
|
207
|
+
return Boolean(this.content && this.content.plain === true)
|
|
208
|
+
},
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* The name the greeting uses: the first word of the display name, or
|
|
212
|
+
* the whole display name for `greeting: "full"`. '' when unknown.
|
|
213
|
+
*
|
|
214
|
+
* @return {string}
|
|
215
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header
|
|
216
|
+
*/
|
|
217
|
+
greetingName() {
|
|
218
|
+
let user
|
|
219
|
+
try {
|
|
220
|
+
user = getCurrentUser()
|
|
221
|
+
} catch {
|
|
222
|
+
user = null
|
|
223
|
+
}
|
|
224
|
+
const displayName = (user && typeof user.displayName === 'string') ? user.displayName.trim() : ''
|
|
225
|
+
if (displayName === '') {
|
|
226
|
+
return ''
|
|
227
|
+
}
|
|
228
|
+
return this.content.greeting === 'full' ? displayName : displayName.split(/\s+/)[0]
|
|
229
|
+
},
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* The greeting for the time of day, with the user's name when known,
|
|
233
|
+
* or '' when the greeting is off.
|
|
234
|
+
*
|
|
235
|
+
* @return {string}
|
|
236
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header
|
|
237
|
+
*/
|
|
238
|
+
greetingText() {
|
|
239
|
+
const greeting = this.content && this.content.greeting
|
|
240
|
+
if (greeting !== true && greeting !== 'full' && greeting !== 'first') {
|
|
241
|
+
return ''
|
|
242
|
+
}
|
|
243
|
+
const hour = this.reference.getHours()
|
|
244
|
+
const name = this.greetingName
|
|
245
|
+
if (hour < 12) {
|
|
246
|
+
return name ? t('nextcloud-vue', 'Good morning, {name}', { name }) : t('nextcloud-vue', 'Good morning')
|
|
247
|
+
}
|
|
248
|
+
if (hour < 18) {
|
|
249
|
+
return name ? t('nextcloud-vue', 'Good afternoon, {name}', { name }) : t('nextcloud-vue', 'Good afternoon')
|
|
250
|
+
}
|
|
251
|
+
return name ? t('nextcloud-vue', 'Good evening, {name}', { name }) : t('nextcloud-vue', 'Good evening')
|
|
252
|
+
},
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* The heading: the greeting when it is on, else the title.
|
|
256
|
+
*
|
|
257
|
+
* @return {string}
|
|
258
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header
|
|
259
|
+
*/
|
|
260
|
+
headingText() {
|
|
261
|
+
return this.greetingText || this.title
|
|
262
|
+
},
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Today's date written out ("Monday 5 October 2026"), or '' when
|
|
266
|
+
* `showDate` is off.
|
|
267
|
+
*
|
|
268
|
+
* @return {string}
|
|
269
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header
|
|
270
|
+
*/
|
|
271
|
+
dateLine() {
|
|
272
|
+
if (!this.content || this.content.showDate !== true) {
|
|
273
|
+
return ''
|
|
274
|
+
}
|
|
275
|
+
const options = { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' }
|
|
276
|
+
let locale
|
|
277
|
+
try {
|
|
278
|
+
locale = getCanonicalLocale()
|
|
279
|
+
} catch {
|
|
280
|
+
locale = undefined
|
|
281
|
+
}
|
|
282
|
+
try {
|
|
283
|
+
return new Intl.DateTimeFormat(locale || undefined, options).format(this.reference)
|
|
284
|
+
} catch {
|
|
285
|
+
return new Intl.DateTimeFormat(undefined, options).format(this.reference)
|
|
286
|
+
}
|
|
287
|
+
},
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Inline style of the date line: the heading's colour, or the muted
|
|
291
|
+
* text colour on a plain header.
|
|
292
|
+
*
|
|
293
|
+
* @return {object}
|
|
294
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header
|
|
295
|
+
*/
|
|
296
|
+
dateStyle() {
|
|
297
|
+
return {
|
|
298
|
+
color: this.isPlain && !(this.content && this.content.textColor) ? 'var(--color-text-maxcontrast)' : this.textColor,
|
|
299
|
+
margin: 0,
|
|
300
|
+
}
|
|
159
301
|
},
|
|
160
302
|
|
|
161
303
|
/** Whether a non-empty subtitle is set. */
|
|
@@ -187,12 +329,19 @@ export default {
|
|
|
187
329
|
return this.backgroundImageUrl !== '' && this.imageFailed === false
|
|
188
330
|
},
|
|
189
331
|
|
|
190
|
-
/**
|
|
332
|
+
/**
|
|
333
|
+
* Resolved background colour (default theme primary).
|
|
334
|
+
*
|
|
335
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header
|
|
336
|
+
*/
|
|
191
337
|
backgroundColor() {
|
|
192
338
|
const value = this.content && this.content.backgroundColor
|
|
193
339
|
if (typeof value === 'string' && value !== '') {
|
|
194
340
|
return value
|
|
195
341
|
}
|
|
342
|
+
if (this.isPlain) {
|
|
343
|
+
return 'transparent'
|
|
344
|
+
}
|
|
196
345
|
return 'var(--color-primary, #0070c0)'
|
|
197
346
|
},
|
|
198
347
|
|
|
@@ -250,13 +399,17 @@ export default {
|
|
|
250
399
|
return HEIGHT_PIXELS[this.height]
|
|
251
400
|
},
|
|
252
401
|
|
|
253
|
-
/**
|
|
402
|
+
/**
|
|
403
|
+
* Resolved text alignment (default `center`).
|
|
404
|
+
*
|
|
405
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header
|
|
406
|
+
*/
|
|
254
407
|
textAlign() {
|
|
255
408
|
const declared = this.content && this.content.textAlign
|
|
256
409
|
if (typeof declared === 'string' && ALLOWED_TEXT_ALIGN.includes(declared)) {
|
|
257
410
|
return declared
|
|
258
411
|
}
|
|
259
|
-
return 'center'
|
|
412
|
+
return this.isPlain ? 'left' : 'center'
|
|
260
413
|
},
|
|
261
414
|
|
|
262
415
|
/** Resolved vertical alignment (default `middle`). */
|
|
@@ -268,7 +421,11 @@ export default {
|
|
|
268
421
|
return 'middle'
|
|
269
422
|
},
|
|
270
423
|
|
|
271
|
-
/**
|
|
424
|
+
/**
|
|
425
|
+
* Resolved text colour with an auto-contrast fallback.
|
|
426
|
+
*
|
|
427
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header
|
|
428
|
+
*/
|
|
272
429
|
textColor() {
|
|
273
430
|
const value = this.content && this.content.textColor
|
|
274
431
|
if (typeof value === 'string' && value !== '') {
|
|
@@ -277,6 +434,9 @@ export default {
|
|
|
277
434
|
if (this.hasBackgroundImage) {
|
|
278
435
|
return '#ffffff'
|
|
279
436
|
}
|
|
437
|
+
if (this.isPlain && !(this.content && this.content.backgroundColor)) {
|
|
438
|
+
return 'var(--color-main-text)'
|
|
439
|
+
}
|
|
280
440
|
return this.isLightColor(this.backgroundColor) ? '#000000' : '#ffffff'
|
|
281
441
|
},
|
|
282
442
|
|
|
@@ -321,7 +481,11 @@ export default {
|
|
|
321
481
|
}
|
|
322
482
|
},
|
|
323
483
|
|
|
324
|
-
/**
|
|
484
|
+
/**
|
|
485
|
+
* Inline style for the content flex container.
|
|
486
|
+
*
|
|
487
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header
|
|
488
|
+
*/
|
|
325
489
|
contentStyle() {
|
|
326
490
|
return {
|
|
327
491
|
position: 'relative',
|
|
@@ -332,7 +496,9 @@ export default {
|
|
|
332
496
|
'flex-direction': 'column',
|
|
333
497
|
'align-items': this.flexAlignFromTextAlign,
|
|
334
498
|
'justify-content': VERTICAL_ALIGN_FLEX[this.verticalAlign],
|
|
335
|
-
|
|
499
|
+
// Plain drops the coloured background, not the card padding:
|
|
500
|
+
// at 0 the greeting sat flush against the card's left edge.
|
|
501
|
+
padding: this.isPlain ? '16px' : '16px 24px',
|
|
336
502
|
'box-sizing': 'border-box',
|
|
337
503
|
gap: '8px',
|
|
338
504
|
'text-align': this.textAlign,
|
|
@@ -484,6 +650,17 @@ export default {
|
|
|
484
650
|
overflow: hidden;
|
|
485
651
|
}
|
|
486
652
|
|
|
653
|
+
.cn-header-widget--plain {
|
|
654
|
+
min-height: 0;
|
|
655
|
+
border-radius: 0;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
.cn-header-widget__date {
|
|
659
|
+
font-size: 14px;
|
|
660
|
+
line-height: 1.4;
|
|
661
|
+
margin: 0;
|
|
662
|
+
}
|
|
663
|
+
|
|
487
664
|
.cn-header-widget__overlay {
|
|
488
665
|
position: absolute;
|
|
489
666
|
inset: 0;
|
|
@@ -18,6 +18,22 @@
|
|
|
18
18
|
:placeholder="t('nextcloud-vue', 'Optional subtitle')"
|
|
19
19
|
@update:modelValue="updateField('subtitle', $event)" />
|
|
20
20
|
|
|
21
|
+
<NcCheckboxRadioSwitch
|
|
22
|
+
:modelValue="greeting"
|
|
23
|
+
@update:modelValue="updateField('greeting', $event)">
|
|
24
|
+
{{ t('nextcloud-vue', 'Greet the user by name instead of showing the title') }}
|
|
25
|
+
</NcCheckboxRadioSwitch>
|
|
26
|
+
<NcCheckboxRadioSwitch
|
|
27
|
+
:modelValue="showDate"
|
|
28
|
+
@update:modelValue="updateField('showDate', $event)">
|
|
29
|
+
{{ t('nextcloud-vue', 'Show the date of today above the heading') }}
|
|
30
|
+
</NcCheckboxRadioSwitch>
|
|
31
|
+
<NcCheckboxRadioSwitch
|
|
32
|
+
:modelValue="plain"
|
|
33
|
+
@update:modelValue="updateField('plain', $event)">
|
|
34
|
+
{{ t('nextcloud-vue', 'Plain header without a coloured background') }}
|
|
35
|
+
</NcCheckboxRadioSwitch>
|
|
36
|
+
|
|
21
37
|
<!-- Pick a background image. Selection does NOT upload — the file is held
|
|
22
38
|
and only uploaded when the host modal calls commit() on submit, so
|
|
23
39
|
re-picking or cancelling writes nothing. Uploads are the reliable
|
|
@@ -162,7 +178,7 @@
|
|
|
162
178
|
|
|
163
179
|
<script>
|
|
164
180
|
import { translate as t } from '@nextcloud/l10n'
|
|
165
|
-
import { NcButton, NcSelect, NcTextField } from '@nextcloud/vue'
|
|
181
|
+
import { NcButton, NcCheckboxRadioSwitch, NcSelect, NcTextField } from '@nextcloud/vue'
|
|
166
182
|
import CnColorPicker from '../CnColorPicker/CnColorPicker.vue'
|
|
167
183
|
import { embedAsDataUrl, extractTransportUrl, readFileAsDataUrl, warnUploadFnDeprecated } from '../../utils/widgetUpload.js'
|
|
168
184
|
|
|
@@ -206,6 +222,7 @@ export default {
|
|
|
206
222
|
NcTextField,
|
|
207
223
|
NcSelect,
|
|
208
224
|
NcButton,
|
|
225
|
+
NcCheckboxRadioSwitch,
|
|
209
226
|
CnColorPicker,
|
|
210
227
|
},
|
|
211
228
|
|
|
@@ -282,6 +299,11 @@ export default {
|
|
|
282
299
|
return {
|
|
283
300
|
title: typeof initial.title === 'string' ? initial.title : DEFAULT_CONTENT.title,
|
|
284
301
|
subtitle: typeof initial.subtitle === 'string' ? initial.subtitle : DEFAULT_CONTENT.subtitle,
|
|
302
|
+
greeting: initial.greeting === true || initial.greeting === 'full' || initial.greeting === 'first',
|
|
303
|
+
// `"full"` (whole display name) is a manifest choice; kept as written.
|
|
304
|
+
greetingMode: initial.greeting === 'full' ? 'full' : true,
|
|
305
|
+
showDate: initial.showDate === true,
|
|
306
|
+
plain: initial.plain === true,
|
|
285
307
|
backgroundImageUrl: typeof initial.backgroundImageUrl === 'string'
|
|
286
308
|
? initial.backgroundImageUrl
|
|
287
309
|
: DEFAULT_CONTENT.backgroundImageUrl,
|
|
@@ -394,9 +416,35 @@ export default {
|
|
|
394
416
|
}
|
|
395
417
|
},
|
|
396
418
|
|
|
397
|
-
/**
|
|
419
|
+
/**
|
|
420
|
+
* The greeting keys, written only when they are on, so a header that
|
|
421
|
+
* never used them keeps the content blob it always had.
|
|
422
|
+
*
|
|
423
|
+
* @return {object}
|
|
424
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header
|
|
425
|
+
*/
|
|
426
|
+
greetingContent() {
|
|
427
|
+
const content = {}
|
|
428
|
+
if (this.greeting) {
|
|
429
|
+
content.greeting = this.greetingMode
|
|
430
|
+
}
|
|
431
|
+
if (this.showDate) {
|
|
432
|
+
content.showDate = true
|
|
433
|
+
}
|
|
434
|
+
if (this.plain) {
|
|
435
|
+
content.plain = true
|
|
436
|
+
}
|
|
437
|
+
return content
|
|
438
|
+
},
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* The full content blob assembled from the current field values.
|
|
442
|
+
*
|
|
443
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header
|
|
444
|
+
*/
|
|
398
445
|
assembledContent() {
|
|
399
446
|
return {
|
|
447
|
+
...this.greetingContent,
|
|
400
448
|
title: this.title,
|
|
401
449
|
subtitle: this.subtitle,
|
|
402
450
|
backgroundImageUrl: this.backgroundImageUrl,
|
|
@@ -538,10 +586,12 @@ export default {
|
|
|
538
586
|
* when either CTA field is filled.
|
|
539
587
|
*
|
|
540
588
|
* @return {string[]} the validation errors.
|
|
589
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header
|
|
541
590
|
*/
|
|
542
591
|
validate() {
|
|
543
592
|
const errors = []
|
|
544
|
-
|
|
593
|
+
// A greeting is the heading, so a greeting header needs no title.
|
|
594
|
+
if (!this.greeting && (typeof this.title !== 'string' || this.title.trim() === '')) {
|
|
545
595
|
errors.push(t('nextcloud-vue', 'Title is required'))
|
|
546
596
|
}
|
|
547
597
|
if (typeof this.backgroundImageUrl === 'string'
|
|
@@ -119,6 +119,7 @@
|
|
|
119
119
|
:views="viewsForControl"
|
|
120
120
|
:loading="savedViewsLoading"
|
|
121
121
|
:currentUserId="currentSavedViewsUserId"
|
|
122
|
+
:counts="savedViewCounts"
|
|
122
123
|
@apply="onApplySavedView"
|
|
123
124
|
@saveRequest="showSaveViewDialog = true"
|
|
124
125
|
@deleteRequest="onDeleteViewRequest" />
|
|
@@ -171,6 +172,7 @@
|
|
|
171
172
|
:multiple="quickFilterMultiple"
|
|
172
173
|
:activeIndex="activeQuickFilterIndex"
|
|
173
174
|
:selectedIndices="selectedQuickFilterIndices"
|
|
175
|
+
:counts="tabCounts"
|
|
174
176
|
@update:activeIndex="onQuickFilterChange"
|
|
175
177
|
@update:selectedIndices="onQuickFilterMultiChange" />
|
|
176
178
|
</template>
|
|
@@ -545,6 +547,7 @@
|
|
|
545
547
|
:statusField="board.statusField || 'status'"
|
|
546
548
|
:cardFields="board.cardFields || []"
|
|
547
549
|
:swimlaneField="board.swimlaneField || ''"
|
|
550
|
+
:dueRule="board.dueRule || null"
|
|
548
551
|
:rowKey="rowKey"
|
|
549
552
|
:runTransition="runTransition"
|
|
550
553
|
:paged="isPaged"
|
|
@@ -781,12 +784,14 @@ import { useSavedViewsApi } from '../../composables/useSavedViewsApi.js'
|
|
|
781
784
|
import { METADATA_COLUMNS } from '../../constants/metadata.js'
|
|
782
785
|
import { routeHref } from '../../utils/actionLink.js'
|
|
783
786
|
import { buildOnSuccessRoute, resolveRegisteredHandler } from '../../utils/actionsDispatcher.js'
|
|
787
|
+
import { fetchFilterCounts } from '../../utils/fetchFilterCounts.js'
|
|
784
788
|
import { buildExportUrl } from '../../utils/indexExportHelpers.js'
|
|
785
789
|
import { openRowTarget } from '../../utils/linkNavigation.js'
|
|
786
|
-
import { resolveClaimedTeams, resolveClaimTokens, splitViewsIntoTabs, viewAsTab } from '../../utils/listLenses.js'
|
|
790
|
+
import { resolveClaimedTeams, resolveClaimTokens, splitViewsIntoTabs, viewAsTab, viewIdOf } from '../../utils/listLenses.js'
|
|
787
791
|
import { LIST_SHORTCUTS, listPaletteCommands, shortcutFor } from '../../utils/listShortcuts.js'
|
|
788
792
|
import { multiKeySort } from '../../utils/multiKeySort.js'
|
|
789
793
|
import { resolveDeepTokens, resolveFilterValue } from '../../utils/resolveFilterTokens.js'
|
|
794
|
+
import { resolveFilterMap } from '../../utils/routeFilters.js'
|
|
790
795
|
import { availableRowActions, DEFAULT_ROW_ACTION_FIELD, refusalReasonFor, undeclaredRowActions } from '../../utils/rowActionAvailability.js'
|
|
791
796
|
import { isNewTabClick, isNewTabHandled, isRowMiddleClick, markNewTabHandled, preventMiddleClickAutoscroll } from '../../utils/rowAuxClick.js'
|
|
792
797
|
import { DEFAULT_ROW_INDICATOR_CAP } from '../../utils/rowIndicators.js'
|
|
@@ -1199,7 +1204,6 @@ export default {
|
|
|
1199
1204
|
default: null,
|
|
1200
1205
|
},
|
|
1201
1206
|
|
|
1202
|
-
/* eslint-disable vue/no-unused-properties -- used in useSelfFetchList */
|
|
1203
1207
|
/**
|
|
1204
1208
|
* Base filter for the self-fetch path. String values of the form
|
|
1205
1209
|
* `"@route.<name>"` / `":<name>"` are interpolated from `$route.params`.
|
|
@@ -1208,7 +1212,6 @@ export default {
|
|
|
1208
1212
|
type: Object,
|
|
1209
1213
|
default: null,
|
|
1210
1214
|
},
|
|
1211
|
-
/* eslint-enable vue/no-unused-properties */
|
|
1212
1215
|
|
|
1213
1216
|
/**
|
|
1214
1217
|
* Self-fetch mode only — an array of clickable filter tabs rendered as
|
|
@@ -2211,6 +2214,19 @@ export default {
|
|
|
2211
2214
|
default: () => [],
|
|
2212
2215
|
},
|
|
2213
2216
|
|
|
2217
|
+
/**
|
|
2218
|
+
* Which saved views show how many records they match: `true` for all
|
|
2219
|
+
* of them (tabs and the entries in the views control), or a list of
|
|
2220
|
+
* view ids. Off by default, so no count request is made. A quick
|
|
2221
|
+
* filter opts in on its own entry with `showCount: true`.
|
|
2222
|
+
*
|
|
2223
|
+
* @type {boolean|string[]}
|
|
2224
|
+
*/
|
|
2225
|
+
viewCounts: {
|
|
2226
|
+
type: [Boolean, Array],
|
|
2227
|
+
default: false,
|
|
2228
|
+
},
|
|
2229
|
+
|
|
2214
2230
|
/**
|
|
2215
2231
|
* The teams this person may claim. The instance decides this list; it
|
|
2216
2232
|
* is the membership side of `claimedTeams`.
|
|
@@ -2649,6 +2665,10 @@ export default {
|
|
|
2649
2665
|
data() {
|
|
2650
2666
|
return {
|
|
2651
2667
|
currentViewMode: this.viewMode,
|
|
2668
|
+
/** Count per tab-strip index, for the entries that asked for one; null = none. */
|
|
2669
|
+
tabCounts: null,
|
|
2670
|
+
/** Count per saved-view id, for the views control; null = none. */
|
|
2671
|
+
savedViewCounts: null,
|
|
2652
2672
|
/**
|
|
2653
2673
|
* The non-`_` query keys this page owns, i.e. may clear on the next
|
|
2654
2674
|
* persist. Seeded with what it adopted from the query on load, and
|
|
@@ -3241,6 +3261,55 @@ export default {
|
|
|
3241
3261
|
return this.splitSavedViews.control
|
|
3242
3262
|
},
|
|
3243
3263
|
|
|
3264
|
+
/**
|
|
3265
|
+
* The filters whose record count this page shows: quick filters that
|
|
3266
|
+
* set `showCount`, and the saved views `viewCounts` names. Empty when
|
|
3267
|
+
* nothing opted in, which is what keeps the page from making a count
|
|
3268
|
+
* request nobody asked for.
|
|
3269
|
+
*
|
|
3270
|
+
* @return {Array<{key: string, filter: object}>} The filters to count.
|
|
3271
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views
|
|
3272
|
+
*/
|
|
3273
|
+
countEntries() {
|
|
3274
|
+
const entries = []
|
|
3275
|
+
const tabs = this.tabStripEntries || []
|
|
3276
|
+
const lens = this.lensTabs.length > 0
|
|
3277
|
+
tabs.forEach((tab, index) => {
|
|
3278
|
+
const wants = lens ? this.viewWantsCount(tab.id) : (tab && tab.showCount === true)
|
|
3279
|
+
if (wants && tab.narrowsToNothing !== true) {
|
|
3280
|
+
entries.push({ key: `tab:${index}`, filter: tab.filter || {} })
|
|
3281
|
+
}
|
|
3282
|
+
})
|
|
3283
|
+
for (const view of this.viewsForControl) {
|
|
3284
|
+
const id = viewIdOf(view)
|
|
3285
|
+
if (this.viewWantsCount(id)) {
|
|
3286
|
+
entries.push({ key: `view:${id}`, filter: viewAsTab(view).filter })
|
|
3287
|
+
}
|
|
3288
|
+
}
|
|
3289
|
+
return entries
|
|
3290
|
+
},
|
|
3291
|
+
|
|
3292
|
+
/**
|
|
3293
|
+
* Changes whenever the counts have to be fetched again: another set
|
|
3294
|
+
* of filters, another register or schema, or a list whose total moved
|
|
3295
|
+
* (a record was added, removed, or left a filter).
|
|
3296
|
+
*
|
|
3297
|
+
* @return {string} The signature.
|
|
3298
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views
|
|
3299
|
+
*/
|
|
3300
|
+
countRequestKey() {
|
|
3301
|
+
if (this.countEntries.length === 0) {
|
|
3302
|
+
return ''
|
|
3303
|
+
}
|
|
3304
|
+
return JSON.stringify({
|
|
3305
|
+
r: this.register,
|
|
3306
|
+
s: this.exportSchemaSlug,
|
|
3307
|
+
f: this.filter || null,
|
|
3308
|
+
e: this.countEntries,
|
|
3309
|
+
t: Number(this.effectivePagination?.total ?? 0),
|
|
3310
|
+
})
|
|
3311
|
+
},
|
|
3312
|
+
|
|
3244
3313
|
/**
|
|
3245
3314
|
* What the tab strip shows: the page's lens tabs when it named any,
|
|
3246
3315
|
* else its quick filters. One strip, never two, so a person is not
|
|
@@ -4279,6 +4348,14 @@ export default {
|
|
|
4279
4348
|
},
|
|
4280
4349
|
|
|
4281
4350
|
watch: {
|
|
4351
|
+
countRequestKey: {
|
|
4352
|
+
immediate: true,
|
|
4353
|
+
/** @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views */
|
|
4354
|
+
handler() {
|
|
4355
|
+
this.loadFilterCounts()
|
|
4356
|
+
},
|
|
4357
|
+
},
|
|
4358
|
+
|
|
4282
4359
|
// Remember the whole folder set whenever no folder narrows the query.
|
|
4283
4360
|
folderSidebarLiveFacetValues: {
|
|
4284
4361
|
immediate: true,
|
|
@@ -4488,6 +4565,61 @@ export default {
|
|
|
4488
4565
|
},
|
|
4489
4566
|
|
|
4490
4567
|
methods: {
|
|
4568
|
+
/**
|
|
4569
|
+
* Whether a saved view shows its record count (`viewCounts`).
|
|
4570
|
+
*
|
|
4571
|
+
* @param {string} id The view id.
|
|
4572
|
+
* @return {boolean} True when the view opted in.
|
|
4573
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views
|
|
4574
|
+
*/
|
|
4575
|
+
viewWantsCount(id) {
|
|
4576
|
+
if (this.viewCounts === true) {
|
|
4577
|
+
return true
|
|
4578
|
+
}
|
|
4579
|
+
return Array.isArray(this.viewCounts) && this.viewCounts.map(String).includes(String(id))
|
|
4580
|
+
},
|
|
4581
|
+
|
|
4582
|
+
/**
|
|
4583
|
+
* Fetch the record counts for the filters and views that asked for
|
|
4584
|
+
* one. Filters that narrow the same single field share one grouped
|
|
4585
|
+
* request. A failed count leaves that entry without a number.
|
|
4586
|
+
*
|
|
4587
|
+
* @return {Promise<void>}
|
|
4588
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-counts-on-filters-and-views
|
|
4589
|
+
*/
|
|
4590
|
+
async loadFilterCounts() {
|
|
4591
|
+
const requestKey = this.countRequestKey
|
|
4592
|
+
const schema = this.exportSchemaSlug
|
|
4593
|
+
if (requestKey === '' || !this.register || !schema) {
|
|
4594
|
+
this.tabCounts = null
|
|
4595
|
+
this.savedViewCounts = null
|
|
4596
|
+
return
|
|
4597
|
+
}
|
|
4598
|
+
const params = (this.$route && this.$route.params) || {}
|
|
4599
|
+
const ctx = typeof this.selfFetchTokenCtx === 'function' ? this.selfFetchTokenCtx() : {}
|
|
4600
|
+
const counts = await fetchFilterCounts({
|
|
4601
|
+
register: this.register,
|
|
4602
|
+
schema,
|
|
4603
|
+
entries: this.countEntries.map((entry) => ({ key: entry.key, filter: resolveFilterMap(entry.filter, params, ctx) })),
|
|
4604
|
+
baseFilter: resolveFilterMap(this.filter, params, ctx),
|
|
4605
|
+
ctx,
|
|
4606
|
+
})
|
|
4607
|
+
if (requestKey !== this.countRequestKey) {
|
|
4608
|
+
return
|
|
4609
|
+
}
|
|
4610
|
+
const tabCounts = {}
|
|
4611
|
+
const viewCounts = {}
|
|
4612
|
+
for (const [key, value] of Object.entries(counts)) {
|
|
4613
|
+
if (key.startsWith('tab:')) {
|
|
4614
|
+
tabCounts[Number(key.slice(4))] = value
|
|
4615
|
+
} else {
|
|
4616
|
+
viewCounts[key.slice(5)] = value
|
|
4617
|
+
}
|
|
4618
|
+
}
|
|
4619
|
+
this.tabCounts = tabCounts
|
|
4620
|
+
this.savedViewCounts = viewCounts
|
|
4621
|
+
},
|
|
4622
|
+
|
|
4491
4623
|
// ── Split view (case-page-and-list-as-a-place) ──────────────────
|
|
4492
4624
|
|
|
4493
4625
|
/**
|