@conduction/nextcloud-vue 2.59.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/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 +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 +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/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 +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/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 +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/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/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 +10 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +54 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +54 -1
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +931 -4
- 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/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 +14970 -8991
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +931 -4
- package/l10n/en.json +54 -1
- package/l10n/nl.json +54 -1
- package/package.json +1 -1
- package/scripts/.jsdoc-baselines.json +3 -0
- package/src/components/CnActionsMenu/CnActionsMenu.vue +14 -1
- 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 +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/CnDetailPage/CnDetailPage.md +16 -0
- package/src/components/CnDetailPage/CnDetailPage.vue +683 -48
- 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 +189 -14
- package/src/components/CnHeaderWidgetForm/CnHeaderWidgetForm.vue +53 -3
- package/src/components/CnIndexPage/CnIndexPage.vue +135 -3
- 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/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 +11 -0
- package/src/css/detail-page.css +47 -0
- package/src/index.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/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/readPath.js +27 -0
- package/src/utils/validateManifestV2.compiled.js +2 -2
|
@@ -0,0 +1,668 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
SPDX-FileCopyrightText: 2026 Conduction B.V.
|
|
3
|
+
SPDX-License-Identifier: EUPL-1.2
|
|
4
|
+
-->
|
|
5
|
+
<template>
|
|
6
|
+
<div class="cn-week-strip" data-testid="cn-week-strip">
|
|
7
|
+
<div v-if="loading" class="cn-week-strip__state" role="status">
|
|
8
|
+
<NcLoadingIcon :size="20" />
|
|
9
|
+
<span class="cn-week-strip__state-text">{{ loadingLabel }}</span>
|
|
10
|
+
</div>
|
|
11
|
+
<p v-else-if="error" class="cn-week-strip__state" role="status">
|
|
12
|
+
{{ errorLabel }}
|
|
13
|
+
</p>
|
|
14
|
+
<!-- A scroll container a keyboard user cannot focus cannot be scrolled,
|
|
15
|
+
so the region is focusable and named. -->
|
|
16
|
+
<div
|
|
17
|
+
v-else
|
|
18
|
+
class="cn-week-strip__scroll"
|
|
19
|
+
role="region"
|
|
20
|
+
tabindex="0"
|
|
21
|
+
:aria-label="regionLabel">
|
|
22
|
+
<ol class="cn-week-strip__days" :style="{ '--cn-week-strip-columns': dayColumns.length }">
|
|
23
|
+
<li
|
|
24
|
+
v-for="day in dayColumns"
|
|
25
|
+
:key="day.key"
|
|
26
|
+
class="cn-week-strip__day"
|
|
27
|
+
:class="{ 'cn-week-strip__day--today': day.today }"
|
|
28
|
+
:aria-current="day.today ? 'date' : null"
|
|
29
|
+
:data-day="day.key">
|
|
30
|
+
<span class="cn-week-strip__head">
|
|
31
|
+
<span class="cn-week-strip__label">{{ day.label }}</span>
|
|
32
|
+
<span v-if="day.today" class="cn-week-strip__today">{{ todayLabel }}</span>
|
|
33
|
+
</span>
|
|
34
|
+
<ul v-if="day.items.length > 0" class="cn-week-strip__items">
|
|
35
|
+
<li v-for="item in day.items" :key="item.key" class="cn-week-strip__item-row">
|
|
36
|
+
<component
|
|
37
|
+
:is="item.href ? 'a' : 'div'"
|
|
38
|
+
class="cn-week-strip__item"
|
|
39
|
+
:class="{ 'cn-week-strip__item--late': item.late, 'cn-week-strip__item--link': !!item.href }"
|
|
40
|
+
:href="item.href || null"
|
|
41
|
+
data-testid="cn-week-strip-item"
|
|
42
|
+
@click="onItemClick($event, item)">
|
|
43
|
+
<strong class="cn-week-strip__title">{{ item.title }}</strong>
|
|
44
|
+
<span v-if="item.meta" class="cn-week-strip__meta">{{ item.meta }}</span>
|
|
45
|
+
<span v-if="item.late" class="cn-week-strip__late">{{ lateLabel }}</span>
|
|
46
|
+
</component>
|
|
47
|
+
</li>
|
|
48
|
+
</ul>
|
|
49
|
+
<span v-else class="cn-week-strip__empty">{{ emptyLabel }}</span>
|
|
50
|
+
</li>
|
|
51
|
+
</ol>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
</template>
|
|
55
|
+
|
|
56
|
+
<script>
|
|
57
|
+
import { getCanonicalLocale, translate as t } from '@nextcloud/l10n'
|
|
58
|
+
import { NcLoadingIcon } from '@nextcloud/vue'
|
|
59
|
+
import { dayKey, daysUntil, parseDateValue } from '../../utils/dateVariant.js'
|
|
60
|
+
import { followLinkClick, resolveHref } from '../../utils/linkNavigation.js'
|
|
61
|
+
import { objectDisplayName, objectFieldValue } from '../../utils/objectName.js'
|
|
62
|
+
import { resolveFilterTokens } from '../../utils/resolveFilterTokens.js'
|
|
63
|
+
import { safeHref } from '../../utils/safeHref.js'
|
|
64
|
+
import { compareVisibleWhen } from '../../utils/visibleWhen.js'
|
|
65
|
+
|
|
66
|
+
const DEFAULT_LIMIT = 100
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* CnWeekStripWidget shows the current week as day columns, with the items
|
|
70
|
+
* that fall on each day listed under it. Use it for deadlines, appointments
|
|
71
|
+
* or anything else that has a date and that somebody should see coming.
|
|
72
|
+
*
|
|
73
|
+
* It renders the five working days by default (`days: 7` adds the weekend),
|
|
74
|
+
* highlights today, marks items that are late, and says so when a day has
|
|
75
|
+
* nothing. On a narrow screen the strip scrolls sideways.
|
|
76
|
+
*
|
|
77
|
+
* Items come from an OpenRegister source (one request for the whole week) or
|
|
78
|
+
* from static `items`. Resolved by its registry type key `week-strip`.
|
|
79
|
+
*
|
|
80
|
+
* ```json
|
|
81
|
+
* {
|
|
82
|
+
* "widgetKey": "week-strip",
|
|
83
|
+
* "props": {
|
|
84
|
+
* "content": {
|
|
85
|
+
* "source": { "register": "dossiq", "schema": "case", "filter": { "assignee": "@me" } },
|
|
86
|
+
* "dateField": "deadline",
|
|
87
|
+
* "titleField": "title",
|
|
88
|
+
* "metaFields": ["identifier", "caseType"],
|
|
89
|
+
* "itemRoute": "CaseDetail",
|
|
90
|
+
* "emptyText": "No deadlines"
|
|
91
|
+
* }
|
|
92
|
+
* }
|
|
93
|
+
* }
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
export default {
|
|
97
|
+
name: 'CnWeekStripWidget',
|
|
98
|
+
|
|
99
|
+
components: { NcLoadingIcon },
|
|
100
|
+
|
|
101
|
+
inject: {
|
|
102
|
+
/**
|
|
103
|
+
* Host translate function provided by CnAppRoot. The manifest-authored
|
|
104
|
+
* `emptyText` and static item texts run through it. Identity by default.
|
|
105
|
+
*/
|
|
106
|
+
cnTranslate: { default: () => (key) => key },
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
props: {
|
|
110
|
+
/**
|
|
111
|
+
* The widget's configuration. `days` is `5` (working days, the
|
|
112
|
+
* default) or `7`. `source` names the OpenRegister register and schema,
|
|
113
|
+
* with an optional `filter` (the shared @-token grammar) and `limit`.
|
|
114
|
+
* `dateField` is the field the items are bucketed by. `titleField` and
|
|
115
|
+
* `metaFields` pick what each item shows. `itemRoute` is the route name
|
|
116
|
+
* an item links to (with the record id as `params.id`). `lateWhen` is a
|
|
117
|
+
* `{ op, value }` rule on the days until the date (default: before
|
|
118
|
+
* today), `lateField` a boolean field that marks a record late.
|
|
119
|
+
* `items` are static entries `{ title, meta?, date, route?, href?, late? }`.
|
|
120
|
+
* `emptyText` is the text of a day without items. `weekOffset` moves
|
|
121
|
+
* the strip whole weeks from the current one.
|
|
122
|
+
*
|
|
123
|
+
* @type {{days?: (5|7), source?: {register?: string, schema?: string, filter?: object, limit?: number}, dateField?: string, titleField?: string, metaFields?: Array<string>, itemRoute?: string, lateWhen?: {op?: string, value?: number}, lateField?: string, items?: Array<{title: string, meta?: string, date: string, route?: (string|object), href?: string, late?: boolean}>, emptyText?: string, weekOffset?: number}}
|
|
124
|
+
*/
|
|
125
|
+
content: {
|
|
126
|
+
type: Object,
|
|
127
|
+
default: () => ({}),
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Translate function. Falls back to the injected `cnTranslate`. Pass it
|
|
132
|
+
* when mounting the widget outside a CnAppRoot.
|
|
133
|
+
*
|
|
134
|
+
* @type {((key: string) => string)|null}
|
|
135
|
+
*/
|
|
136
|
+
translate: {
|
|
137
|
+
type: Function,
|
|
138
|
+
default: null,
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* The moment the strip treats as "now". Leave empty for the current
|
|
143
|
+
* time; set it to render another week or to test.
|
|
144
|
+
*
|
|
145
|
+
* @type {Date|null}
|
|
146
|
+
*/
|
|
147
|
+
now: {
|
|
148
|
+
type: Date,
|
|
149
|
+
default: null,
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
data() {
|
|
154
|
+
return {
|
|
155
|
+
rows: [],
|
|
156
|
+
loading: false,
|
|
157
|
+
error: '',
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
computed: {
|
|
162
|
+
/**
|
|
163
|
+
* The translate function in use.
|
|
164
|
+
*
|
|
165
|
+
* @return {(key: string) => string}
|
|
166
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget
|
|
167
|
+
*/
|
|
168
|
+
effectiveTranslate() {
|
|
169
|
+
return this.translate ?? this.cnTranslate
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* The reference moment.
|
|
174
|
+
*
|
|
175
|
+
* @return {Date}
|
|
176
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget
|
|
177
|
+
*/
|
|
178
|
+
reference() {
|
|
179
|
+
return this.now instanceof Date ? this.now : new Date()
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* How many day columns render: 7, or 5 for anything else.
|
|
184
|
+
*
|
|
185
|
+
* @return {number}
|
|
186
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget
|
|
187
|
+
*/
|
|
188
|
+
dayCount() {
|
|
189
|
+
return Number(this.content.days) === 7 ? 7 : 5
|
|
190
|
+
},
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* The days of the strip, Monday first.
|
|
194
|
+
*
|
|
195
|
+
* @return {Array<Date>}
|
|
196
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget
|
|
197
|
+
*/
|
|
198
|
+
weekDays() {
|
|
199
|
+
const ref = this.reference
|
|
200
|
+
const offset = Number.isInteger(this.content.weekOffset) ? this.content.weekOffset : 0
|
|
201
|
+
const mondayDelta = (ref.getDay() + 6) % 7
|
|
202
|
+
const days = []
|
|
203
|
+
for (let i = 0; i < this.dayCount; i++) {
|
|
204
|
+
days.push(new Date(ref.getFullYear(), ref.getMonth(), ref.getDate() - mondayDelta + (offset * 7) + i))
|
|
205
|
+
}
|
|
206
|
+
return days
|
|
207
|
+
},
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* The field the items are bucketed by.
|
|
211
|
+
*
|
|
212
|
+
* @return {string}
|
|
213
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget
|
|
214
|
+
*/
|
|
215
|
+
dateField() {
|
|
216
|
+
return typeof this.content.dateField === 'string' ? this.content.dateField : ''
|
|
217
|
+
},
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Whether the items come from OpenRegister.
|
|
221
|
+
*
|
|
222
|
+
* @return {boolean}
|
|
223
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget
|
|
224
|
+
*/
|
|
225
|
+
hasSource() {
|
|
226
|
+
const s = this.content.source || {}
|
|
227
|
+
return Boolean(s.register && s.schema && this.dateField)
|
|
228
|
+
},
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Every item, from the source rows or the static list, in one shape.
|
|
232
|
+
*
|
|
233
|
+
* @return {Array<{key: string, title: string, meta: string, date: (Date|null), late: boolean, href: string, target: (object|string|null)}>}
|
|
234
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget
|
|
235
|
+
*/
|
|
236
|
+
normalizedItems() {
|
|
237
|
+
if (this.hasSource) {
|
|
238
|
+
return this.rows.map((row, index) => this.itemFromRow(row, index))
|
|
239
|
+
}
|
|
240
|
+
const items = Array.isArray(this.content.items) ? this.content.items : []
|
|
241
|
+
return items
|
|
242
|
+
.filter((item) => item && typeof item === 'object')
|
|
243
|
+
.map((item, index) => this.itemFromStatic(item, index))
|
|
244
|
+
},
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* The columns: one per day, each with its items.
|
|
248
|
+
*
|
|
249
|
+
* @return {Array<{key: string, label: string, today: boolean, items: Array<object>}>}
|
|
250
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget
|
|
251
|
+
*/
|
|
252
|
+
dayColumns() {
|
|
253
|
+
const todayKey = dayKey(this.reference)
|
|
254
|
+
const buckets = {}
|
|
255
|
+
for (const item of this.normalizedItems) {
|
|
256
|
+
if (item.date === null) {
|
|
257
|
+
continue
|
|
258
|
+
}
|
|
259
|
+
const key = dayKey(item.date)
|
|
260
|
+
;(buckets[key] = buckets[key] || []).push(item)
|
|
261
|
+
}
|
|
262
|
+
const formatter = this.dayFormatter
|
|
263
|
+
return this.weekDays.map((day) => {
|
|
264
|
+
const key = dayKey(day)
|
|
265
|
+
return {
|
|
266
|
+
key,
|
|
267
|
+
label: formatter.format(day),
|
|
268
|
+
today: key === todayKey,
|
|
269
|
+
items: buckets[key] || [],
|
|
270
|
+
}
|
|
271
|
+
})
|
|
272
|
+
},
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Formats a day as a short weekday and a day number ("Mon 5").
|
|
276
|
+
*
|
|
277
|
+
* @return {Intl.DateTimeFormat}
|
|
278
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget
|
|
279
|
+
*/
|
|
280
|
+
dayFormatter() {
|
|
281
|
+
let locale
|
|
282
|
+
try {
|
|
283
|
+
locale = getCanonicalLocale()
|
|
284
|
+
} catch {
|
|
285
|
+
locale = undefined
|
|
286
|
+
}
|
|
287
|
+
try {
|
|
288
|
+
return new Intl.DateTimeFormat(locale || undefined, { weekday: 'short', day: 'numeric' })
|
|
289
|
+
} catch {
|
|
290
|
+
return new Intl.DateTimeFormat(undefined, { weekday: 'short', day: 'numeric' })
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* The text of a day without items.
|
|
296
|
+
*
|
|
297
|
+
* @return {string}
|
|
298
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget
|
|
299
|
+
*/
|
|
300
|
+
emptyLabel() {
|
|
301
|
+
const text = this.content.emptyText
|
|
302
|
+
return (typeof text === 'string' && text !== '')
|
|
303
|
+
? this.effectiveTranslate(text)
|
|
304
|
+
: t('nextcloud-vue', 'Nothing planned')
|
|
305
|
+
},
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* @return {string} The badge on today's column.
|
|
309
|
+
*
|
|
310
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget
|
|
311
|
+
*/
|
|
312
|
+
todayLabel() {
|
|
313
|
+
return t('nextcloud-vue', 'today')
|
|
314
|
+
},
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* @return {string} The marker on a late item.
|
|
318
|
+
*
|
|
319
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget
|
|
320
|
+
*/
|
|
321
|
+
lateLabel() {
|
|
322
|
+
return t('nextcloud-vue', 'Late')
|
|
323
|
+
},
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* @return {string} The accessible name of the scrolling region.
|
|
327
|
+
*
|
|
328
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget
|
|
329
|
+
*/
|
|
330
|
+
regionLabel() {
|
|
331
|
+
return t('nextcloud-vue', 'This week, day by day')
|
|
332
|
+
},
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* @return {string} The loading text.
|
|
336
|
+
*
|
|
337
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget
|
|
338
|
+
*/
|
|
339
|
+
loadingLabel() {
|
|
340
|
+
return t('nextcloud-vue', 'Loading…')
|
|
341
|
+
},
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* @return {string} The text shown when the items could not be loaded.
|
|
345
|
+
*
|
|
346
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget
|
|
347
|
+
*/
|
|
348
|
+
errorLabel() {
|
|
349
|
+
return t('nextcloud-vue', 'The items for this week could not be loaded.')
|
|
350
|
+
},
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Changes whenever a new fetch is needed.
|
|
354
|
+
*
|
|
355
|
+
* @return {string}
|
|
356
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget
|
|
357
|
+
*/
|
|
358
|
+
sourceKey() {
|
|
359
|
+
return JSON.stringify({
|
|
360
|
+
s: this.content.source || {},
|
|
361
|
+
f: this.dateField,
|
|
362
|
+
d: this.weekDays.length > 0 ? [dayKey(this.weekDays[0]), this.weekDays.length] : [],
|
|
363
|
+
})
|
|
364
|
+
},
|
|
365
|
+
},
|
|
366
|
+
|
|
367
|
+
watch: {
|
|
368
|
+
/** @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget */
|
|
369
|
+
sourceKey() {
|
|
370
|
+
this.fetchItems()
|
|
371
|
+
},
|
|
372
|
+
},
|
|
373
|
+
|
|
374
|
+
mounted() {
|
|
375
|
+
this.fetchItems()
|
|
376
|
+
},
|
|
377
|
+
|
|
378
|
+
methods: {
|
|
379
|
+
/**
|
|
380
|
+
* Whether a date counts as late under `content.lateWhen` (default:
|
|
381
|
+
* before today).
|
|
382
|
+
*
|
|
383
|
+
* @param {Date|null} date The item's date.
|
|
384
|
+
* @return {boolean}
|
|
385
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget
|
|
386
|
+
*/
|
|
387
|
+
isLateDate(date) {
|
|
388
|
+
if (date === null) {
|
|
389
|
+
return false
|
|
390
|
+
}
|
|
391
|
+
const rule = (this.content.lateWhen && typeof this.content.lateWhen === 'object')
|
|
392
|
+
? this.content.lateWhen
|
|
393
|
+
: { op: 'lt', value: 0 }
|
|
394
|
+
return compareVisibleWhen(daysUntil(date, this.reference), rule.op || 'lt', rule.value ?? 0)
|
|
395
|
+
},
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* One OpenRegister record as a strip item.
|
|
399
|
+
*
|
|
400
|
+
* @param {object} row The record.
|
|
401
|
+
* @param {number} index Its position, used as a key of last resort.
|
|
402
|
+
* @return {object} The item.
|
|
403
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget
|
|
404
|
+
*/
|
|
405
|
+
itemFromRow(row, index) {
|
|
406
|
+
const c = this.content
|
|
407
|
+
const id = row && (row.id || (row['@self'] && row['@self'].id))
|
|
408
|
+
const date = parseDateValue(objectFieldValue(row, this.dateField))
|
|
409
|
+
const titleValue = c.titleField ? objectFieldValue(row, c.titleField) : undefined
|
|
410
|
+
const title = (titleValue !== undefined && titleValue !== null && titleValue !== '')
|
|
411
|
+
? String(titleValue)
|
|
412
|
+
: objectDisplayName(row)
|
|
413
|
+
const metaFields = Array.isArray(c.metaFields) ? c.metaFields : []
|
|
414
|
+
const meta = metaFields
|
|
415
|
+
.map((field) => objectFieldValue(row, field))
|
|
416
|
+
.filter((value) => value !== undefined && value !== null && value !== '' && typeof value !== 'object')
|
|
417
|
+
.map(String)
|
|
418
|
+
.join(' · ')
|
|
419
|
+
const flagged = c.lateField ? objectFieldValue(row, c.lateField) === true : false
|
|
420
|
+
const target = (c.itemRoute && id) ? { name: c.itemRoute, params: { id } } : null
|
|
421
|
+
return {
|
|
422
|
+
key: String(id || `row-${index}`),
|
|
423
|
+
title,
|
|
424
|
+
meta,
|
|
425
|
+
date,
|
|
426
|
+
late: flagged || this.isLateDate(date),
|
|
427
|
+
target,
|
|
428
|
+
href: target ? resolveHref(target, this.$router) : '',
|
|
429
|
+
}
|
|
430
|
+
},
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* One static manifest entry as a strip item.
|
|
434
|
+
*
|
|
435
|
+
* @param {object} item The entry.
|
|
436
|
+
* @param {number} index Its position.
|
|
437
|
+
* @return {object} The item.
|
|
438
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget
|
|
439
|
+
*/
|
|
440
|
+
itemFromStatic(item, index) {
|
|
441
|
+
const date = parseDateValue(item.date)
|
|
442
|
+
let target = null
|
|
443
|
+
let href = ''
|
|
444
|
+
if (item.route) {
|
|
445
|
+
target = typeof item.route === 'string' ? { name: item.route } : item.route
|
|
446
|
+
href = resolveHref(target, this.$router)
|
|
447
|
+
} else if (typeof item.href === 'string' && item.href !== '') {
|
|
448
|
+
const safe = safeHref(item.href)
|
|
449
|
+
href = safe === '#' ? '' : safe
|
|
450
|
+
}
|
|
451
|
+
return {
|
|
452
|
+
key: `item-${index}`,
|
|
453
|
+
title: typeof item.title === 'string' ? this.effectiveTranslate(item.title) : '',
|
|
454
|
+
meta: typeof item.meta === 'string' ? this.effectiveTranslate(item.meta) : '',
|
|
455
|
+
date,
|
|
456
|
+
late: item.late === true || (item.late !== false && this.isLateDate(date)),
|
|
457
|
+
target,
|
|
458
|
+
href,
|
|
459
|
+
}
|
|
460
|
+
},
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Load the week's records in one request.
|
|
464
|
+
*
|
|
465
|
+
* @return {Promise<void>}
|
|
466
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget
|
|
467
|
+
*/
|
|
468
|
+
async fetchItems() {
|
|
469
|
+
if (!this.hasSource) {
|
|
470
|
+
this.rows = []
|
|
471
|
+
this.error = ''
|
|
472
|
+
return
|
|
473
|
+
}
|
|
474
|
+
const s = this.content.source
|
|
475
|
+
const requestKey = this.sourceKey
|
|
476
|
+
const first = this.weekDays[0]
|
|
477
|
+
const last = this.weekDays[this.weekDays.length - 1]
|
|
478
|
+
const dayAfter = new Date(last.getFullYear(), last.getMonth(), last.getDate() + 1)
|
|
479
|
+
this.loading = true
|
|
480
|
+
this.error = ''
|
|
481
|
+
try {
|
|
482
|
+
const [{ default: axios }, { generateUrl }] = await Promise.all([
|
|
483
|
+
import('@nextcloud/axios'),
|
|
484
|
+
import('@nextcloud/router'),
|
|
485
|
+
])
|
|
486
|
+
const url = generateUrl(
|
|
487
|
+
'/apps/openregister/api/objects/{register}/{schema}',
|
|
488
|
+
{ register: s.register, schema: s.schema },
|
|
489
|
+
)
|
|
490
|
+
const params = {
|
|
491
|
+
_limit: Number.isInteger(s.limit) && s.limit > 0 ? s.limit : DEFAULT_LIMIT,
|
|
492
|
+
[`_order[${this.dateField}]`]: 'asc',
|
|
493
|
+
}
|
|
494
|
+
const filter = resolveFilterTokens(s.filter || {}, {})
|
|
495
|
+
for (const [k, v] of Object.entries(filter || {})) {
|
|
496
|
+
if (v && typeof v === 'object') {
|
|
497
|
+
for (const [op, ov] of Object.entries(v)) {
|
|
498
|
+
params[`${k}[${op}]`] = ov
|
|
499
|
+
}
|
|
500
|
+
} else if (v !== '' && v !== null && v !== undefined) {
|
|
501
|
+
params[k] = v
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
// The week window goes last, so a manifest filter on the date
|
|
505
|
+
// field cannot widen the strip beyond the days it shows.
|
|
506
|
+
params[`${this.dateField}[gte]`] = dayKey(first)
|
|
507
|
+
params[`${this.dateField}[lt]`] = dayKey(dayAfter)
|
|
508
|
+
const res = await axios.get(url, { params })
|
|
509
|
+
if (requestKey !== this.sourceKey) {
|
|
510
|
+
return
|
|
511
|
+
}
|
|
512
|
+
this.rows = (res && res.data && Array.isArray(res.data.results)) ? res.data.results : []
|
|
513
|
+
} catch (e) {
|
|
514
|
+
// eslint-disable-next-line no-console
|
|
515
|
+
console.warn('[CnWeekStripWidget] failed to load items:', e)
|
|
516
|
+
this.error = (e && e.message) || 'error'
|
|
517
|
+
this.rows = []
|
|
518
|
+
} finally {
|
|
519
|
+
this.loading = false
|
|
520
|
+
}
|
|
521
|
+
},
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Route a plain click on an in-app item; the browser handles the rest.
|
|
525
|
+
*
|
|
526
|
+
* @param {MouseEvent} event The click.
|
|
527
|
+
* @param {object} item The clicked item.
|
|
528
|
+
* @return {void}
|
|
529
|
+
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-week-strip-widget
|
|
530
|
+
*/
|
|
531
|
+
onItemClick(event, item) {
|
|
532
|
+
if (item.target) {
|
|
533
|
+
followLinkClick(event, item.target, this.$router)
|
|
534
|
+
}
|
|
535
|
+
},
|
|
536
|
+
},
|
|
537
|
+
}
|
|
538
|
+
</script>
|
|
539
|
+
|
|
540
|
+
<style scoped>
|
|
541
|
+
.cn-week-strip {
|
|
542
|
+
width: 100%;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
.cn-week-strip__state {
|
|
546
|
+
display: flex;
|
|
547
|
+
align-items: center;
|
|
548
|
+
gap: 8px;
|
|
549
|
+
margin: 0;
|
|
550
|
+
padding: 12px 4px;
|
|
551
|
+
color: var(--color-text-maxcontrast);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
.cn-week-strip__scroll {
|
|
555
|
+
overflow-x: auto;
|
|
556
|
+
border-radius: var(--border-radius-large, 8px);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
.cn-week-strip__scroll:focus-visible {
|
|
560
|
+
outline: 2px solid var(--color-primary-element);
|
|
561
|
+
outline-offset: 2px;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
.cn-week-strip__days {
|
|
565
|
+
display: grid;
|
|
566
|
+
grid-template-columns: repeat(var(--cn-week-strip-columns, 5), minmax(128px, 1fr));
|
|
567
|
+
gap: 10px;
|
|
568
|
+
/* 5 columns of 128px plus gaps: below this the region scrolls sideways. */
|
|
569
|
+
min-width: calc(var(--cn-week-strip-columns, 5) * 136px);
|
|
570
|
+
margin: 0;
|
|
571
|
+
padding: 0;
|
|
572
|
+
list-style: none;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
.cn-week-strip__day {
|
|
576
|
+
display: flex;
|
|
577
|
+
flex-direction: column;
|
|
578
|
+
gap: 8px;
|
|
579
|
+
min-width: 0;
|
|
580
|
+
padding: 10px;
|
|
581
|
+
border: 1px solid var(--color-border);
|
|
582
|
+
border-radius: var(--border-radius-large, 8px);
|
|
583
|
+
background: var(--color-background-hover);
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
.cn-week-strip__day--today {
|
|
587
|
+
border-color: var(--color-primary-element);
|
|
588
|
+
background: var(--color-primary-element-light);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
.cn-week-strip__head {
|
|
592
|
+
display: flex;
|
|
593
|
+
align-items: center;
|
|
594
|
+
justify-content: space-between;
|
|
595
|
+
gap: 6px;
|
|
596
|
+
font-size: 0.85em;
|
|
597
|
+
font-weight: 600;
|
|
598
|
+
color: var(--color-main-text);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
.cn-week-strip__today {
|
|
602
|
+
padding: 1px 8px;
|
|
603
|
+
border-radius: var(--border-radius-pill, 10px);
|
|
604
|
+
background: var(--color-primary-element);
|
|
605
|
+
color: var(--color-primary-element-text);
|
|
606
|
+
font-size: 0.85em;
|
|
607
|
+
font-weight: 700;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
.cn-week-strip__items {
|
|
611
|
+
display: flex;
|
|
612
|
+
flex-direction: column;
|
|
613
|
+
gap: 6px;
|
|
614
|
+
margin: 0;
|
|
615
|
+
padding: 0;
|
|
616
|
+
list-style: none;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
.cn-week-strip__item {
|
|
620
|
+
display: flex;
|
|
621
|
+
flex-direction: column;
|
|
622
|
+
gap: 4px;
|
|
623
|
+
padding: 10px 12px;
|
|
624
|
+
border: 1px solid var(--color-border);
|
|
625
|
+
border-radius: var(--border-radius-large, 8px);
|
|
626
|
+
background: var(--color-main-background);
|
|
627
|
+
color: var(--color-main-text);
|
|
628
|
+
text-decoration: none;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
.cn-week-strip__item--link:hover {
|
|
632
|
+
border-color: var(--color-primary-element);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
.cn-week-strip__item--link:focus-visible {
|
|
636
|
+
outline: 2px solid var(--color-primary-element);
|
|
637
|
+
outline-offset: 2px;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/* The edge is drawn inside the box, so a late item is as wide as the others. */
|
|
641
|
+
.cn-week-strip__item--late {
|
|
642
|
+
border-color: var(--color-element-error, var(--color-error));
|
|
643
|
+
box-shadow: inset 3px 0 0 0 var(--color-element-error, var(--color-error));
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
.cn-week-strip__title {
|
|
647
|
+
font-size: 0.95em;
|
|
648
|
+
line-height: 1.3;
|
|
649
|
+
overflow-wrap: anywhere;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
.cn-week-strip__meta {
|
|
653
|
+
font-size: 0.85em;
|
|
654
|
+
color: var(--color-text-maxcontrast);
|
|
655
|
+
overflow-wrap: anywhere;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
.cn-week-strip__late {
|
|
659
|
+
font-size: 0.8em;
|
|
660
|
+
font-weight: 600;
|
|
661
|
+
color: var(--color-text-error, var(--color-error-text));
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
.cn-week-strip__empty {
|
|
665
|
+
font-size: 0.9em;
|
|
666
|
+
color: var(--color-text-maxcontrast);
|
|
667
|
+
}
|
|
668
|
+
</style>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPDX-FileCopyrightText: 2026 Conduction B.V.
|
|
3
|
+
* SPDX-License-Identifier: EUPL-1.2
|
|
4
|
+
*
|
|
5
|
+
* CnWeekStripWidget: the `week-strip` dashboard widget (the current week as
|
|
6
|
+
* day columns with the dated items under each day). It is registered in
|
|
7
|
+
* `CnWidgetGrid/registerDashboardWidgets.js`, inline, because a bare
|
|
8
|
+
* side-effect import of this module may be tree-shaken away.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import CnWeekStripWidget from './CnWeekStripWidget.vue'
|
|
12
|
+
|
|
13
|
+
export { CnWeekStripWidget }
|
|
14
|
+
export default CnWeekStripWidget
|