@conduction/nextcloud-vue 2.68.0 → 2.70.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 +25 -0
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue2.js.map +1 -1
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +155 -152
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
- package/dist/esm/components/CnAppNav/CnAppNav.vue2.js +77 -0
- package/dist/esm/components/CnAppNav/CnAppNav.vue2.js.map +1 -1
- package/dist/esm/components/CnAppNav/CnAppNav.vue3.js +8 -8
- package/dist/esm/components/CnAppNav/CnAppNav.vue3.js.map +1 -1
- package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue.js +1 -0
- package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue.js.map +1 -1
- package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue2.js +28 -0
- package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue2.js.map +1 -1
- package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue3.js +4 -2
- package/dist/esm/components/CnBreadcrumbs/CnBreadcrumbs.vue3.js.map +1 -1
- package/dist/esm/components/CnCalendarWidget/CnCalendarWidget.vue2.js +25 -1
- package/dist/esm/components/CnCalendarWidget/CnCalendarWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnCalendarWidget/CnCalendarWidget.vue3.js +32 -17
- package/dist/esm/components/CnCalendarWidget/CnCalendarWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnCapabilityTable/CnCapabilityTable.vue2.js +14 -1
- package/dist/esm/components/CnCapabilityTable/CnCapabilityTable.vue2.js.map +1 -1
- package/dist/esm/components/CnCapabilityTable/CnCapabilityTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js +43 -2
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue3.js +3 -3
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +45 -35
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +19 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js +35 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue2.js.map +1 -1
- package/dist/esm/components/CnDataTable/CnDataTable.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js +94 -6
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +64 -19
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +28 -0
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js +3 -2
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
- package/dist/esm/components/CnFeaturesAndRoadmapPage/CnFeaturesAndRoadmapPage.vue2.js +12 -0
- package/dist/esm/components/CnFeaturesAndRoadmapPage/CnFeaturesAndRoadmapPage.vue2.js.map +1 -1
- package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js +9 -0
- package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue2.js.map +1 -1
- package/dist/esm/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue3.js.map +1 -1
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js +54 -7
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue3.js +26 -8
- package/dist/esm/components/CnHeaderWidget/CnHeaderWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +143 -0
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +68 -25
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue2.js.map +1 -1
- package/dist/esm/components/CnNextStepCard/CnNextStepCard.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +4 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue2.js +17 -3
- package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue3.js +25 -24
- package/dist/esm/components/CnStackedBarWidget/CnStackedBarWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStagesWidget/CnStagesWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js +29 -2
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js +7 -0
- package/dist/esm/components/CnStatWidget/CnStatWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnStatsBlock/CnStatsBlock.vue2.js +24 -1
- package/dist/esm/components/CnStatsBlock/CnStatsBlock.vue2.js.map +1 -1
- package/dist/esm/components/CnStatsBlock/CnStatsBlock.vue3.js +1 -1
- package/dist/esm/components/CnStatsBlock/CnStatsBlock.vue3.js.map +1 -1
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js +81 -1
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js +25 -3
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue2.js +15 -2
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue3.js +31 -32
- package/dist/esm/components/CnWeekStripWidget/CnWeekStripWidget.vue3.js.map +1 -1
- package/dist/esm/l10n/en.json.js +3 -0
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +3 -0
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/mixins/pageViews.js +278 -11
- package/dist/esm/mixins/pageViews.js.map +1 -1
- package/dist/esm/nextcloud-vue.css +145 -0
- package/dist/esm/store/plugins/dashboardLayouts.js +35 -1
- package/dist/esm/store/plugins/dashboardLayouts.js.map +1 -1
- package/dist/esm/utils/capabilityComparison.js +29 -1
- package/dist/esm/utils/capabilityComparison.js.map +1 -1
- package/dist/esm/utils/formatMetric.js +137 -16
- package/dist/esm/utils/formatMetric.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 +2840 -1477
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +145 -0
- package/l10n/en.json +3 -0
- package/l10n/nl.json +3 -0
- package/package.json +1 -1
- package/src/components/CnActionsBar/CnActionsBar.md +2 -0
- package/src/components/CnActionsBar/CnActionsBar.vue +28 -2
- package/src/components/CnAppNav/CnAppNav.vue +85 -8
- package/src/components/CnBreadcrumbs/CnBreadcrumbs.vue +45 -0
- package/src/components/CnCalendarWidget/CnCalendarWidget.vue +32 -4
- package/src/components/CnCapabilityTable/CnCapabilityTable.vue +14 -0
- package/src/components/CnDashboardGrid/CnDashboardGrid.md +2 -0
- package/src/components/CnDashboardGrid/CnDashboardGrid.vue +54 -2
- package/src/components/CnDashboardPage/CnDashboardPage.md +2 -0
- package/src/components/CnDashboardPage/CnDashboardPage.vue +59 -35
- package/src/components/CnDataTable/CnDataTable.vue +35 -1
- package/src/components/CnDetailPage/CnDetailPage.md +8 -0
- package/src/components/CnDetailPage/CnDetailPage.vue +124 -8
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +30 -1
- package/src/components/CnFeaturesAndRoadmapPage/CnFeaturesAndRoadmapPage.vue +12 -0
- package/src/components/CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue +9 -0
- package/src/components/CnHeaderWidget/CnHeaderWidget.vue +95 -8
- package/src/components/CnIndexPage/CnIndexPage.vue +171 -6
- package/src/components/CnNextStepCard/CnNextStepCard.vue +11 -0
- package/src/components/CnObjectListWidget/CnObjectListWidget.vue +4 -1
- package/src/components/CnStackedBarWidget/CnStackedBarWidget.vue +34 -7
- package/src/components/CnStagesWidget/CnStagesWidget.vue +19 -0
- package/src/components/CnStatWidget/CnStatWidget.vue +38 -2
- package/src/components/CnStatsBlock/CnStatsBlock.md +2 -0
- package/src/components/CnStatsBlock/CnStatsBlock.vue +25 -2
- package/src/components/CnStatsBlockWidget/CnStatsBlockWidget.vue +95 -3
- package/src/components/CnWeekStripWidget/CnWeekStripWidget.vue +24 -3
- package/src/css/detail-page.css +10 -0
- package/src/css/index-page.css +12 -0
- package/src/css/kpi-card.css +15 -0
- package/src/mixins/pageViews.js +277 -10
- package/src/schemas/app-manifest-v2.schema.json +73 -6
- package/src/store/plugins/dashboardLayouts.js +34 -0
- package/src/utils/capabilityComparison.js +28 -0
- package/src/utils/formatMetric.js +143 -15
- package/src/utils/validateManifestV2.compiled.js +2 -2
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
v-else-if="isData && schemaObject"
|
|
86
86
|
:title="resolvedTitle"
|
|
87
87
|
:chromeless="isBare"
|
|
88
|
-
:showActions="!isBare"
|
|
88
|
+
:showActions="!isBare && actionsShown"
|
|
89
89
|
:icon="widget.icon || null"
|
|
90
90
|
:schema="schemaObject"
|
|
91
91
|
:objectData="object"
|
|
@@ -165,6 +165,7 @@
|
|
|
165
165
|
v-else-if="renderer && isContentOnly && !isBare"
|
|
166
166
|
:title="widget.title || ''"
|
|
167
167
|
titleIconPosition="left"
|
|
168
|
+
:showActions="actionsShown || catalogAddEnabled"
|
|
168
169
|
:showRefresh="false"
|
|
169
170
|
:showRequestFeature="false"
|
|
170
171
|
class="cn-detail-page__catalog-card">
|
|
@@ -473,6 +474,21 @@ export default {
|
|
|
473
474
|
default: null,
|
|
474
475
|
},
|
|
475
476
|
|
|
477
|
+
/**
|
|
478
|
+
* Whether a widget on a card keeps its overflow Actions menu when its
|
|
479
|
+
* definition does not say (`showActions`). `false` (CnDetailPage
|
|
480
|
+
* `showWidgetActions: false`) drops it from the record's data cards; a
|
|
481
|
+
* definition with `showActions: true` keeps it, one with
|
|
482
|
+
* `showActions: false` drops it whatever this says. True by default,
|
|
483
|
+
* which is the menu every card had.
|
|
484
|
+
*
|
|
485
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-a-detail-page-can-drop-the-widget-actions-menu
|
|
486
|
+
*/
|
|
487
|
+
showActions: {
|
|
488
|
+
type: Boolean,
|
|
489
|
+
default: true,
|
|
490
|
+
},
|
|
491
|
+
|
|
476
492
|
/**
|
|
477
493
|
* The consumer's component registry, consulted before the built-in
|
|
478
494
|
* catalog so a custom widget type overrides a built-in (REQ-MVR-005).
|
|
@@ -839,6 +855,19 @@ export default {
|
|
|
839
855
|
return this.content.allowCreate !== false
|
|
840
856
|
},
|
|
841
857
|
|
|
858
|
+
/**
|
|
859
|
+
* Whether the widget's overflow Actions menu renders: the definition's
|
|
860
|
+
* own `showActions` when it is a boolean, else the surface's
|
|
861
|
+
* `showActions`.
|
|
862
|
+
*
|
|
863
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-a-detail-page-can-drop-the-widget-actions-menu
|
|
864
|
+
* @return {boolean} true when the menu renders.
|
|
865
|
+
*/
|
|
866
|
+
actionsShown() {
|
|
867
|
+
const own = this.widget && this.widget.showActions
|
|
868
|
+
return typeof own === 'boolean' ? own : this.showActions
|
|
869
|
+
},
|
|
870
|
+
|
|
842
871
|
/**
|
|
843
872
|
* The card header rule, with the surface's layout-driven answer winning
|
|
844
873
|
* when it supplied one.
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
// unconditionally.
|
|
41
41
|
import { loadState } from '@nextcloud/initial-state'
|
|
42
42
|
import CnFeaturesAndRoadmapView from '../CnFeaturesAndRoadmapView/CnFeaturesAndRoadmapView.vue'
|
|
43
|
+
import { warnCapabilityComparisonDeprecated } from '../../utils/capabilityComparison.js'
|
|
43
44
|
import { DEFAULT_FORGE } from '../../utils/forge.js'
|
|
44
45
|
|
|
45
46
|
/**
|
|
@@ -175,6 +176,11 @@ export default {
|
|
|
175
176
|
* Manifest config > initialState (`features_roadmap_capabilities`) >
|
|
176
177
|
* null. When null the view renders its two stops as it always has.
|
|
177
178
|
*
|
|
179
|
+
* Deprecated: publish the comparison on the app's docs site and link to it (as dossiq #3312 and pipelinq #2195 did). Still rendered; removal waits for a major version. The same goes for the `features_roadmap_capabilities` initial
|
|
180
|
+
* state key. A development build warns once when either is set.
|
|
181
|
+
*
|
|
182
|
+
* @deprecated since 2.66.0, publish the comparison on the app's docs site and link to it.
|
|
183
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-the-in-app-capability-comparison-is-deprecated
|
|
178
184
|
* @type {object|null}
|
|
179
185
|
*/
|
|
180
186
|
capabilityComparison: {
|
|
@@ -298,5 +304,11 @@ export default {
|
|
|
298
304
|
return readInitialState(this.effectiveAppId, 'features_roadmap_capabilities', null)
|
|
299
305
|
},
|
|
300
306
|
},
|
|
307
|
+
|
|
308
|
+
mounted() {
|
|
309
|
+
if (this.resolvedCapabilityComparison) {
|
|
310
|
+
warnCapabilityComparisonDeprecated('CnFeaturesAndRoadmapPage `capabilityComparison` / the `features_roadmap_capabilities` initial state')
|
|
311
|
+
}
|
|
312
|
+
},
|
|
301
313
|
}
|
|
302
314
|
</script>
|
|
@@ -114,6 +114,7 @@ import CnFeaturesAndRoadmapSidebar from '../CnFeaturesAndRoadmapSidebar/CnFeatur
|
|
|
114
114
|
import CnFeaturesTab from '../CnFeaturesTab/CnFeaturesTab.vue'
|
|
115
115
|
import CnRoadmapTab from '../CnRoadmapTab/CnRoadmapTab.vue'
|
|
116
116
|
import CnSupportDialog from '../CnSupportDialog/CnSupportDialog.vue'
|
|
117
|
+
import { warnCapabilityComparisonDeprecated } from '../../utils/capabilityComparison.js'
|
|
117
118
|
import { buildFeatureRequestUrl, DEFAULT_FORGE } from '../../utils/forge.js'
|
|
118
119
|
|
|
119
120
|
const DEFAULT_OPENBUILT_PATH = '/apps/openbuilt'
|
|
@@ -214,6 +215,11 @@ export default {
|
|
|
214
215
|
* When null (default) the toggle keeps its two stops and the view
|
|
215
216
|
* renders exactly as it does for every app that passes nothing.
|
|
216
217
|
*
|
|
218
|
+
* Deprecated: publish the comparison on the app's docs site and link to it (as dossiq #3312 and pipelinq #2195 did). Still rendered; removal waits for a major version.
|
|
219
|
+
* A development build warns once when it is set.
|
|
220
|
+
*
|
|
221
|
+
* @deprecated since 2.66.0, publish the comparison on the app's docs site and link to it.
|
|
222
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-the-in-app-capability-comparison-is-deprecated
|
|
217
223
|
* @type {object|null}
|
|
218
224
|
*/
|
|
219
225
|
capabilityComparison: {
|
|
@@ -527,6 +533,9 @@ export default {
|
|
|
527
533
|
|
|
528
534
|
mounted() {
|
|
529
535
|
this.publishHoistedSidebar()
|
|
536
|
+
if (this.capabilityComparison) {
|
|
537
|
+
warnCapabilityComparisonDeprecated('CnFeaturesAndRoadmapView `capabilityComparison`')
|
|
538
|
+
}
|
|
530
539
|
},
|
|
531
540
|
|
|
532
541
|
beforeUnmount() {
|
|
@@ -4,12 +4,25 @@
|
|
|
4
4
|
-->
|
|
5
5
|
|
|
6
6
|
<template>
|
|
7
|
-
<div class="cn-header-widget" :class="{ 'cn-header-widget--plain': isPlain, 'cn-header-widget--ground': isGround, 'cn-header-widget--with-views': viewOptions.length > 0 }" :style="wrapperStyle">
|
|
7
|
+
<div class="cn-header-widget" :class="{ 'cn-header-widget--plain': isPlain, 'cn-header-widget--ground': isGround, 'cn-header-widget--with-views': viewOptions.length > 0, 'cn-header-widget--with-emblem': emblem !== null }" :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
|
+
<!-- The emblem beside the greeting (`content.emblem`): a URL, or
|
|
14
|
+
`true` for the theme's emblem (--nldesign-emblem-url). Decoration. -->
|
|
15
|
+
<img
|
|
16
|
+
v-if="emblem && emblem !== true"
|
|
17
|
+
class="cn-header-widget__emblem"
|
|
18
|
+
:src="emblem"
|
|
19
|
+
alt=""
|
|
20
|
+
data-testid="cn-header-widget-emblem">
|
|
21
|
+
<span
|
|
22
|
+
v-else-if="emblem === true"
|
|
23
|
+
class="cn-header-widget__emblem cn-header-widget__emblem--theme"
|
|
24
|
+
aria-hidden="true"
|
|
25
|
+
data-testid="cn-header-widget-emblem" />
|
|
13
26
|
<div class="cn-header-widget__content" :style="contentStyle">
|
|
14
27
|
<p
|
|
15
28
|
v-if="dateLine"
|
|
@@ -140,7 +153,7 @@ export default {
|
|
|
140
153
|
* Persisted widget content: `{title, subtitle, backgroundImageUrl,
|
|
141
154
|
* backgroundImageFileId, backgroundColor, overlayMode, overlayColor,
|
|
142
155
|
* overlayOpacity, textColor, textAlign, verticalAlign, height, cta,
|
|
143
|
-
* greeting, showDate, plain, ground, views}`. All fields are optional except
|
|
156
|
+
* greeting, showDate, kicker, emblem, plain, ground, views}`. All fields are optional except
|
|
144
157
|
* `title` (or `greeting`); unknown enum values collapse to documented
|
|
145
158
|
* defaults and the renderer never throws. `views` is
|
|
146
159
|
* `{ ariaLabel?, options: [{ label, route, params? } | { label, view }] }`:
|
|
@@ -151,7 +164,12 @@ export default {
|
|
|
151
164
|
* own. `ground: true`
|
|
152
165
|
* draws the greeting on the page ground: the plain look with no
|
|
153
166
|
* padding, a 32px heading and the date line 6px above it (a dashboard
|
|
154
|
-
* also drops the widget's card for it).
|
|
167
|
+
* also drops the widget's card for it). A ground greeting takes its
|
|
168
|
+
* own height rather than its grid cell's, so a view switch sits on
|
|
169
|
+
* the heading's line. `kicker` (an i18n key) prefixes the date line, as
|
|
170
|
+
* "Customer contact · Tuesday 6 October". `emblem` (a URL, or `true`
|
|
171
|
+
* for the theme's `--nldesign-emblem-url`) draws the emblem beside the
|
|
172
|
+
* date line and heading, 52px high (`--cn-header-emblem-size`).
|
|
155
173
|
*
|
|
156
174
|
* @type {object}
|
|
157
175
|
*/
|
|
@@ -396,13 +414,30 @@ export default {
|
|
|
396
414
|
},
|
|
397
415
|
|
|
398
416
|
/**
|
|
399
|
-
*
|
|
400
|
-
*
|
|
417
|
+
* The kicker line above the heading: `content.kicker` (through the
|
|
418
|
+
* host translate function, e.g. "Customer contact") and today's date
|
|
419
|
+
* written out ("Monday 5 October 2026") when `showDate` is on, joined
|
|
420
|
+
* by " · ". '' when neither is set. Without `kicker` this is the date
|
|
421
|
+
* line as before.
|
|
401
422
|
*
|
|
402
423
|
* @return {string}
|
|
403
424
|
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header
|
|
404
425
|
*/
|
|
405
426
|
dateLine() {
|
|
427
|
+
const kicker = this.content && typeof this.content.kicker === 'string' && this.content.kicker !== ''
|
|
428
|
+
? this.effectiveTranslate(this.content.kicker)
|
|
429
|
+
: ''
|
|
430
|
+
const date = this.dateText
|
|
431
|
+
return [kicker, date].filter((part) => part !== '').join(' · ')
|
|
432
|
+
},
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Today's date written out, or '' when `showDate` is off.
|
|
436
|
+
*
|
|
437
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-a-greeting-kicker-can-carry-a-prefix
|
|
438
|
+
* @return {string}
|
|
439
|
+
*/
|
|
440
|
+
dateText() {
|
|
406
441
|
if (!this.content || this.content.showDate !== true) {
|
|
407
442
|
return ''
|
|
408
443
|
}
|
|
@@ -434,6 +469,21 @@ export default {
|
|
|
434
469
|
}
|
|
435
470
|
},
|
|
436
471
|
|
|
472
|
+
/**
|
|
473
|
+
* The emblem beside the greeting: the URL `content.emblem` names,
|
|
474
|
+
* `true` for the theme's emblem, or null without the key.
|
|
475
|
+
*
|
|
476
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-a-greeting-can-carry-the-emblem
|
|
477
|
+
* @return {string|true|null}
|
|
478
|
+
*/
|
|
479
|
+
emblem() {
|
|
480
|
+
const value = this.content && this.content.emblem
|
|
481
|
+
if (value === true) {
|
|
482
|
+
return true
|
|
483
|
+
}
|
|
484
|
+
return typeof value === 'string' && value !== '' ? resolveImageUrl(value) : null
|
|
485
|
+
},
|
|
486
|
+
|
|
437
487
|
/** Whether a non-empty subtitle is set. */
|
|
438
488
|
hasSubtitle() {
|
|
439
489
|
return this.subtitle !== ''
|
|
@@ -574,7 +624,12 @@ export default {
|
|
|
574
624
|
return this.isLightColor(this.backgroundColor) ? '#000000' : '#ffffff'
|
|
575
625
|
},
|
|
576
626
|
|
|
577
|
-
/**
|
|
627
|
+
/**
|
|
628
|
+
* Inline style for the banner wrapper.
|
|
629
|
+
*
|
|
630
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-a-ground-greeting-lines-its-switch-up-with-the-heading
|
|
631
|
+
* @return {object}
|
|
632
|
+
*/
|
|
578
633
|
wrapperStyle() {
|
|
579
634
|
const style = {
|
|
580
635
|
position: 'relative',
|
|
@@ -583,7 +638,11 @@ export default {
|
|
|
583
638
|
// widget. A fixed pixel height ignored the cell, which made the
|
|
584
639
|
// banner un-resizable and made it overflow/scroll inside a
|
|
585
640
|
// smaller cell. The grid controls the size now.
|
|
586
|
-
|
|
641
|
+
// On the page ground there is no banner to fill the cell with:
|
|
642
|
+
// the greeting takes its own height, so the view switch lines
|
|
643
|
+
// up with the heading instead of the bottom of a taller cell,
|
|
644
|
+
// and a size-to-content cell can shrink to it.
|
|
645
|
+
height: this.isGround ? 'auto' : '100%',
|
|
587
646
|
overflow: 'hidden',
|
|
588
647
|
'background-color': this.backgroundColor,
|
|
589
648
|
}
|
|
@@ -619,6 +678,7 @@ export default {
|
|
|
619
678
|
* Inline style for the content flex container.
|
|
620
679
|
*
|
|
621
680
|
* @spec openspec/changes/workplace-dashboard-primitives/specs/workplace-dashboard-primitives/spec.md#requirement-greeting-header
|
|
681
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-a-ground-greeting-lines-its-switch-up-with-the-heading
|
|
622
682
|
*/
|
|
623
683
|
contentStyle() {
|
|
624
684
|
return {
|
|
@@ -627,7 +687,7 @@ export default {
|
|
|
627
687
|
// Beside a view switch the content shares the row instead of
|
|
628
688
|
// taking the whole width (see .cn-header-widget--with-views).
|
|
629
689
|
width: this.viewOptions.length > 0 ? 'auto' : '100%',
|
|
630
|
-
height: '100%',
|
|
690
|
+
height: this.isGround ? 'auto' : '100%',
|
|
631
691
|
display: 'flex',
|
|
632
692
|
'flex-direction': 'column',
|
|
633
693
|
'align-items': this.flexAlignFromTextAlign,
|
|
@@ -834,6 +894,33 @@ export default {
|
|
|
834
894
|
width: auto;
|
|
835
895
|
}
|
|
836
896
|
|
|
897
|
+
/* The emblem beside the greeting (`content.emblem`): on one row with the
|
|
898
|
+
date line and heading, centred on them, as LpStart draws it. */
|
|
899
|
+
.cn-header-widget--with-emblem {
|
|
900
|
+
display: flex;
|
|
901
|
+
column-gap: var(--cn-header-emblem-gap, 16px);
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
.cn-header-widget--with-emblem .cn-header-widget__content {
|
|
905
|
+
flex: 1 1 auto;
|
|
906
|
+
width: auto;
|
|
907
|
+
min-width: 0;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
.cn-header-widget__emblem {
|
|
911
|
+
position: relative;
|
|
912
|
+
z-index: 1;
|
|
913
|
+
flex: none;
|
|
914
|
+
align-self: center;
|
|
915
|
+
height: var(--cn-header-emblem-size, 52px);
|
|
916
|
+
width: auto;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
.cn-header-widget__emblem--theme {
|
|
920
|
+
width: var(--cn-header-emblem-size, 52px);
|
|
921
|
+
background: var(--nldesign-emblem-url) center / contain no-repeat;
|
|
922
|
+
}
|
|
923
|
+
|
|
837
924
|
.cn-header-widget__views {
|
|
838
925
|
position: relative;
|
|
839
926
|
z-index: 1;
|
|
@@ -19,8 +19,28 @@
|
|
|
19
19
|
<CnPageHeader
|
|
20
20
|
:title="title"
|
|
21
21
|
:description="headerDescription"
|
|
22
|
-
:icon="resolvedIcon"
|
|
23
|
-
:visuallyHidden="!showTitle"
|
|
22
|
+
:icon="showTitleIcon ? resolvedIcon : ''"
|
|
23
|
+
:visuallyHidden="!showTitle">
|
|
24
|
+
<!-- The board's header buttons (`headerButtons`), beside the
|
|
25
|
+
title. Declaring them takes the Views and Actions menus
|
|
26
|
+
(and the Add / Export controls a button takes over) out of
|
|
27
|
+
the actions bar. -->
|
|
28
|
+
<template v-if="headerButtonsShown" #extra>
|
|
29
|
+
<div class="cn-index-page__header-buttons" data-testid="cn-index-header-buttons">
|
|
30
|
+
<NcButton
|
|
31
|
+
v-for="button in resolvedHeaderButtons"
|
|
32
|
+
:key="button.key"
|
|
33
|
+
:variant="button.variant"
|
|
34
|
+
:data-testid="`cn-index-header-button-${button.key}`"
|
|
35
|
+
@click="onHeaderButton(button)">
|
|
36
|
+
<template v-if="button.icon" #icon>
|
|
37
|
+
<CnIcon :name="button.icon" :size="20" />
|
|
38
|
+
</template>
|
|
39
|
+
{{ button.label }}
|
|
40
|
+
</NcButton>
|
|
41
|
+
</div>
|
|
42
|
+
</template>
|
|
43
|
+
</CnPageHeader>
|
|
24
44
|
</slot>
|
|
25
45
|
|
|
26
46
|
<!-- Optional content below header, above actions bar -->
|
|
@@ -64,7 +84,9 @@
|
|
|
64
84
|
:refreshDisabled="refreshDisabled"
|
|
65
85
|
:addDisabled="addDisabled"
|
|
66
86
|
:addTo="addLinkTo"
|
|
67
|
-
:showAdd="effectiveShowAdd"
|
|
87
|
+
:showAdd="effectiveShowAdd && !headerButtonTakes('add')"
|
|
88
|
+
:showCount="showCount"
|
|
89
|
+
:showActionsMenu="!headerButtonsShown"
|
|
68
90
|
:showSidebarToggle="hasSidebar"
|
|
69
91
|
:sidebarOpen="sidebarOpen"
|
|
70
92
|
:headerActions="mergedHeaderActions"
|
|
@@ -111,12 +133,12 @@
|
|
|
111
133
|
views, export, page config — grouped together and kept apart from
|
|
112
134
|
the app-specific buttons in `#actions`, with Add sitting between
|
|
113
135
|
the two groups rather than before both (dossiq Cases/Queue). -->
|
|
114
|
-
<template v-if="isEditMode ||
|
|
136
|
+
<template v-if="isEditMode || barShowsExportMenu || barShowsSavedViews" #actions-end>
|
|
115
137
|
<!-- Saved views (opt-in via `allowSavedViews`): lists the user's
|
|
116
138
|
OpenRegister saved-search views; applying one writes its stored
|
|
117
139
|
filters/search/sort into the route query. -->
|
|
118
140
|
<CnSavedViewsControl
|
|
119
|
-
v-if="
|
|
141
|
+
v-if="barShowsSavedViews"
|
|
120
142
|
:views="viewsForControl"
|
|
121
143
|
:loading="savedViewsLoading"
|
|
122
144
|
:currentUserId="currentSavedViewsUserId"
|
|
@@ -128,7 +150,7 @@
|
|
|
128
150
|
CSV/Excel entries navigate to OR's export-leaf URL, passing the
|
|
129
151
|
current route's query params through as filters. -->
|
|
130
152
|
<NcActions
|
|
131
|
-
v-if="
|
|
153
|
+
v-if="barShowsExportMenu"
|
|
132
154
|
:forceName="true"
|
|
133
155
|
:menuName="t('nextcloud-vue', 'Export')"
|
|
134
156
|
data-testid="cn-index-export-menu"
|
|
@@ -1225,6 +1247,51 @@ export default {
|
|
|
1225
1247
|
default: false,
|
|
1226
1248
|
},
|
|
1227
1249
|
|
|
1250
|
+
/**
|
|
1251
|
+
* Whether the page header draws its icon before the title. `false`
|
|
1252
|
+
* (manifest `config.showTitleIcon: false`) drops it, for a title that
|
|
1253
|
+
* stands alone as the board draws it. True by default.
|
|
1254
|
+
*
|
|
1255
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-an-index-page-can-take-the-board-header
|
|
1256
|
+
*/
|
|
1257
|
+
showTitleIcon: {
|
|
1258
|
+
type: Boolean,
|
|
1259
|
+
default: true,
|
|
1260
|
+
},
|
|
1261
|
+
|
|
1262
|
+
/**
|
|
1263
|
+
* Whether the actions bar shows its "Showing 20 of 258" line.
|
|
1264
|
+
* `false` (manifest `config.showCount: false`) drops it, for a page
|
|
1265
|
+
* whose title line already gives the total (`countSubtitle`). True by
|
|
1266
|
+
* default.
|
|
1267
|
+
*
|
|
1268
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-an-index-page-can-take-the-board-header
|
|
1269
|
+
*/
|
|
1270
|
+
showCount: {
|
|
1271
|
+
type: Boolean,
|
|
1272
|
+
default: true,
|
|
1273
|
+
},
|
|
1274
|
+
|
|
1275
|
+
/**
|
|
1276
|
+
* Buttons beside the page title, as the board's "Export" and "New
|
|
1277
|
+
* case" (manifest `config.headerButtons`). Each is `{ label?, action,
|
|
1278
|
+
* variant?, icon?, format?, id? }`: `action` is `add` (the Add flow;
|
|
1279
|
+
* its label defaults to the Add label), `export` (the export leaf in
|
|
1280
|
+
* `format`, `csv` by default, else the export dialog), `import`,
|
|
1281
|
+
* `refresh`, or the id of a `headerActions` entry; `variant` is
|
|
1282
|
+
* `primary` or `secondary` (the default). Shown only with
|
|
1283
|
+
* `showTitle`. When they show, the actions bar drops its Views and
|
|
1284
|
+
* Actions menus, and the Add button and Export menu when a button
|
|
1285
|
+
* takes their action. Empty (the default) changes nothing.
|
|
1286
|
+
*
|
|
1287
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-an-index-page-can-take-the-board-header
|
|
1288
|
+
* @type {Array<{label?: string, action: string, variant?: ('primary'|'secondary'), icon?: string, format?: ('csv'|'excel'), id?: string}>}
|
|
1289
|
+
*/
|
|
1290
|
+
headerButtons: {
|
|
1291
|
+
type: Array,
|
|
1292
|
+
default: () => [],
|
|
1293
|
+
},
|
|
1294
|
+
|
|
1228
1295
|
/** Optional MDI icon name. Defaults to schema.icon when a schema is provided. */
|
|
1229
1296
|
icon: {
|
|
1230
1297
|
type: String,
|
|
@@ -4284,6 +4351,67 @@ export default {
|
|
|
4284
4351
|
return resolveDeepTokens(seed, {})
|
|
4285
4352
|
},
|
|
4286
4353
|
|
|
4354
|
+
/**
|
|
4355
|
+
* The declared header buttons with a label, translated, each with a
|
|
4356
|
+
* stable key and a variant.
|
|
4357
|
+
*
|
|
4358
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-an-index-page-can-take-the-board-header
|
|
4359
|
+
* @return {Array<{key: string, label: string, action: string, variant: string, icon: string, format: string}>}
|
|
4360
|
+
*/
|
|
4361
|
+
resolvedHeaderButtons() {
|
|
4362
|
+
const declared = Array.isArray(this.headerButtons) ? this.headerButtons : []
|
|
4363
|
+
return declared
|
|
4364
|
+
.filter((button) => button && typeof button === 'object' && typeof button.action === 'string' && button.action !== '')
|
|
4365
|
+
.map((button, index) => {
|
|
4366
|
+
let label = typeof button.label === 'string' && button.label !== '' ? this.cnTranslate(button.label) : ''
|
|
4367
|
+
if (label === '' && button.action === 'add') {
|
|
4368
|
+
label = this.resolvedAddLabel
|
|
4369
|
+
}
|
|
4370
|
+
return {
|
|
4371
|
+
key: String(button.id || button.action || index),
|
|
4372
|
+
label,
|
|
4373
|
+
action: button.action,
|
|
4374
|
+
variant: button.variant === 'primary' ? 'primary' : 'secondary',
|
|
4375
|
+
icon: typeof button.icon === 'string' ? button.icon : '',
|
|
4376
|
+
format: button.format === 'excel' ? 'excel' : 'csv',
|
|
4377
|
+
}
|
|
4378
|
+
})
|
|
4379
|
+
.filter((button) => button.label !== '')
|
|
4380
|
+
},
|
|
4381
|
+
|
|
4382
|
+
/**
|
|
4383
|
+
* Whether the header buttons render: there are some, the title shows
|
|
4384
|
+
* (they sit beside it) and no `#header` slot replaces the header.
|
|
4385
|
+
*
|
|
4386
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-an-index-page-can-take-the-board-header
|
|
4387
|
+
* @return {boolean}
|
|
4388
|
+
*/
|
|
4389
|
+
headerButtonsShown() {
|
|
4390
|
+
return this.showTitle && !this.$slots.header && this.resolvedHeaderButtons.length > 0
|
|
4391
|
+
},
|
|
4392
|
+
|
|
4393
|
+
/**
|
|
4394
|
+
* Whether the actions bar shows the saved-views control: opted in
|
|
4395
|
+
* with `allowSavedViews` and not given up for header buttons.
|
|
4396
|
+
*
|
|
4397
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-an-index-page-can-take-the-board-header
|
|
4398
|
+
* @return {boolean}
|
|
4399
|
+
*/
|
|
4400
|
+
barShowsSavedViews() {
|
|
4401
|
+
return Boolean(this.allowSavedViews) && !this.headerButtonsShown
|
|
4402
|
+
},
|
|
4403
|
+
|
|
4404
|
+
/**
|
|
4405
|
+
* Whether the actions bar shows the Export menu: `showExportMenu`,
|
|
4406
|
+
* unless a header button took the export over.
|
|
4407
|
+
*
|
|
4408
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-an-index-page-can-take-the-board-header
|
|
4409
|
+
* @return {boolean}
|
|
4410
|
+
*/
|
|
4411
|
+
barShowsExportMenu() {
|
|
4412
|
+
return this.showExportMenu && !this.headerButtonTakes('export')
|
|
4413
|
+
},
|
|
4414
|
+
|
|
4287
4415
|
/**
|
|
4288
4416
|
* The header's description line: `countSubtitle` with the total
|
|
4289
4417
|
* filled in when one is known, else `description`.
|
|
@@ -5086,6 +5214,43 @@ export default {
|
|
|
5086
5214
|
this.$emit('open-modal', { target, props: { selectedIds, count, ...own } })
|
|
5087
5215
|
},
|
|
5088
5216
|
|
|
5217
|
+
/**
|
|
5218
|
+
* Whether a shown header button takes `action` over from the bar.
|
|
5219
|
+
*
|
|
5220
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-an-index-page-can-take-the-board-header
|
|
5221
|
+
* @param {string} action The action, e.g. `add`.
|
|
5222
|
+
* @return {boolean}
|
|
5223
|
+
*/
|
|
5224
|
+
headerButtonTakes(action) {
|
|
5225
|
+
return this.headerButtonsShown && this.resolvedHeaderButtons.some((button) => button.action === action)
|
|
5226
|
+
},
|
|
5227
|
+
|
|
5228
|
+
/**
|
|
5229
|
+
* A header button was clicked: run its built-in action, or dispatch
|
|
5230
|
+
* it as the `headerActions` entry with that id.
|
|
5231
|
+
*
|
|
5232
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-an-index-page-can-take-the-board-header
|
|
5233
|
+
* @param {{action: string, format: string}} button The resolved button.
|
|
5234
|
+
* @return {void}
|
|
5235
|
+
*/
|
|
5236
|
+
onHeaderButton(button) {
|
|
5237
|
+
if (button.action === 'add') {
|
|
5238
|
+
this.onAddClick()
|
|
5239
|
+
} else if (button.action === 'export') {
|
|
5240
|
+
if (this.register && this.exportSchemaSlug) {
|
|
5241
|
+
this.onExportClick(button.format)
|
|
5242
|
+
} else {
|
|
5243
|
+
this.showExportDialog = true
|
|
5244
|
+
}
|
|
5245
|
+
} else if (button.action === 'import') {
|
|
5246
|
+
this.showImportDialog = true
|
|
5247
|
+
} else if (button.action === 'refresh') {
|
|
5248
|
+
this.onRefreshEvent()
|
|
5249
|
+
} else {
|
|
5250
|
+
this.onHeaderAction({ action: button.action, id: button.action })
|
|
5251
|
+
}
|
|
5252
|
+
},
|
|
5253
|
+
|
|
5089
5254
|
/**
|
|
5090
5255
|
* Click dispatch from CnActionsBar's `@header-action`. Looks
|
|
5091
5256
|
* up the resolved entry by id, invokes its handler if any,
|
|
@@ -241,6 +241,17 @@ export default {
|
|
|
241
241
|
text-transform: uppercase;
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
+
/* The title is a small kicker, not a section heading, but it is an <h3> (or
|
|
245
|
+
whatever `titleTag` says) for the outline, and a theme that sizes every
|
|
246
|
+
heading with !important (thematiq's nldesign sheet: `h3 { font-size:
|
|
247
|
+
var(--nldesign-component-heading-3-font-size) !important }`) blew it up to
|
|
248
|
+
an 18px uppercase heading. Only !important beats !important; the doubled
|
|
249
|
+
class makes this the more specific rule. Without such a theme the size is
|
|
250
|
+
the 0.85em above, unchanged. */
|
|
251
|
+
.cn-next-step-card__title.cn-next-step-card__title {
|
|
252
|
+
font-size: 0.85em !important;
|
|
253
|
+
}
|
|
254
|
+
|
|
244
255
|
.cn-next-step-card__list {
|
|
245
256
|
display: grid;
|
|
246
257
|
gap: calc(2 * var(--default-grid-baseline));
|
|
@@ -614,7 +614,10 @@ export default {
|
|
|
614
614
|
return { key: c, label: headingFor(c) }
|
|
615
615
|
}
|
|
616
616
|
const out = { key: c.key, label: c.label || headingFor(c.key) }
|
|
617
|
-
|
|
617
|
+
// `secondary` (a second, muted line under the cell) rides
|
|
618
|
+
// along too: the table draws it, and dropping it here took the
|
|
619
|
+
// list rows' second line away (zuiddrecht-pixel-gaps-3).
|
|
620
|
+
for (const k of ['format', 'widget', 'widgetProps', 'formatter', 'align', 'width', 'type', 'enum', 'enumLabels', 'sortable', 'secondary']) {
|
|
618
621
|
if (c[k] !== undefined) {
|
|
619
622
|
out[k] = c[k]
|
|
620
623
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
SPDX-License-Identifier: EUPL-1.2
|
|
4
4
|
-->
|
|
5
5
|
<template>
|
|
6
|
-
<div class="cn-stacked-bar" data-testid="cn-stacked-bar">
|
|
6
|
+
<div class="cn-stacked-bar" :class="{ 'cn-stacked-bar--inset': inset }" data-testid="cn-stacked-bar">
|
|
7
7
|
<div v-if="loading" class="cn-stacked-bar__state" role="status">
|
|
8
8
|
<NcLoadingIcon :size="20" />
|
|
9
9
|
<span>{{ loadingLabel }}</span>
|
|
@@ -11,9 +11,14 @@
|
|
|
11
11
|
<p v-else-if="error" class="cn-stacked-bar__state" role="status">
|
|
12
12
|
{{ errorLabel }}
|
|
13
13
|
</p>
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
<!-- The designed empty state the list widgets use, compact: a bare
|
|
15
|
+
line of text read as a broken widget in an empty card. -->
|
|
16
|
+
<CnWidgetEmptyState
|
|
17
|
+
v-else-if="total === 0"
|
|
18
|
+
:name="emptyLabel"
|
|
19
|
+
compact
|
|
20
|
+
class="cn-stacked-bar__empty"
|
|
21
|
+
data-testid="cn-stacked-bar-empty" />
|
|
17
22
|
<template v-else>
|
|
18
23
|
<!-- The bar is a picture of the legend below it. The legend holds
|
|
19
24
|
every label and number, so the bar itself is hidden from
|
|
@@ -50,6 +55,7 @@
|
|
|
50
55
|
<script>
|
|
51
56
|
import { translate as t } from '@nextcloud/l10n'
|
|
52
57
|
import { NcLoadingIcon } from '@nextcloud/vue'
|
|
58
|
+
import CnWidgetEmptyState from '../CnWidgetEmptyState/CnWidgetEmptyState.vue'
|
|
53
59
|
import { fetchGroupedCounts } from '../../utils/fetchAggregate.js'
|
|
54
60
|
|
|
55
61
|
/** The lightest step keeps this share of the primary colour, so it still stands out from the page. */
|
|
@@ -114,7 +120,7 @@ export function rampColor(index, count) {
|
|
|
114
120
|
export default {
|
|
115
121
|
name: 'CnStackedBarWidget',
|
|
116
122
|
|
|
117
|
-
components: { NcLoadingIcon },
|
|
123
|
+
components: { CnWidgetEmptyState, NcLoadingIcon },
|
|
118
124
|
|
|
119
125
|
inject: {
|
|
120
126
|
/**
|
|
@@ -133,9 +139,11 @@ export default {
|
|
|
133
139
|
* group keys in the order they render; groups it does not list follow
|
|
134
140
|
* in the order they arrive, and a listed key without records renders
|
|
135
141
|
* with a count of 0. `labels` maps a group key to its label.
|
|
136
|
-
* `emptyText` is shown when there is nothing to count.
|
|
142
|
+
* `emptyText` is shown when there is nothing to count. `inset: true`
|
|
143
|
+
* draws the bar and legend inside the board's inset (16px above, 24px
|
|
144
|
+
* at the sides, 22px below) instead of from card edge to card edge.
|
|
137
145
|
*
|
|
138
|
-
* @type {{source?: {register?: string, schema?: string, groupBy?: string, filter?: object}, segments?: Array<{key?: string, label: string, value: number}>, order?: Array<string>, labels?: {[key: string]: string}, emptyText?: string}}
|
|
146
|
+
* @type {{inset?: boolean, source?: {register?: string, schema?: string, groupBy?: string, filter?: object}, segments?: Array<{key?: string, label: string, value: number}>, order?: Array<string>, labels?: {[key: string]: string}, emptyText?: string}}
|
|
139
147
|
*/
|
|
140
148
|
content: {
|
|
141
149
|
type: Object,
|
|
@@ -163,6 +171,17 @@ export default {
|
|
|
163
171
|
},
|
|
164
172
|
|
|
165
173
|
computed: {
|
|
174
|
+
/**
|
|
175
|
+
* Whether the widget draws the board's inset (`content.inset`). Off
|
|
176
|
+
* by default, which keeps it edge to edge as before.
|
|
177
|
+
*
|
|
178
|
+
* @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-a-strip-and-a-stacked-bar-can-take-the-board-inset
|
|
179
|
+
* @return {boolean}
|
|
180
|
+
*/
|
|
181
|
+
inset() {
|
|
182
|
+
return Boolean(this.content && this.content.inset === true)
|
|
183
|
+
},
|
|
184
|
+
|
|
166
185
|
/**
|
|
167
186
|
* The translate function in use.
|
|
168
187
|
*
|
|
@@ -353,6 +372,14 @@ export default {
|
|
|
353
372
|
width: 100%;
|
|
354
373
|
}
|
|
355
374
|
|
|
375
|
+
/* `content.inset: true`: the board's inset inside the card (zuiddrecht-pixel-
|
|
376
|
+
gaps-3). A dashboard renders its widgets flush, so without it the strip ran
|
|
377
|
+
from card edge to card edge. Theme hook: --cn-widget-board-inset. */
|
|
378
|
+
.cn-stacked-bar--inset {
|
|
379
|
+
box-sizing: border-box;
|
|
380
|
+
padding: var(--cn-widget-board-inset, 16px 24px 22px);
|
|
381
|
+
}
|
|
382
|
+
|
|
356
383
|
.cn-stacked-bar__state {
|
|
357
384
|
display: flex;
|
|
358
385
|
align-items: center;
|
|
@@ -1502,6 +1502,25 @@ button.cn-stages-widget__bar-label {
|
|
|
1502
1502
|
cursor: pointer;
|
|
1503
1503
|
}
|
|
1504
1504
|
|
|
1505
|
+
/* The same label under a theme that restyles every button with !important
|
|
1506
|
+
(thematiq's nldesign sheet: `button:not(.action-button)` gets 8px 16px
|
|
1507
|
+
padding, `min-width: max-content` and `overflow: visible`), and under core's
|
|
1508
|
+
`margin: 3px 3px 3px 0`, which outranks the scoped base rule. Together they
|
|
1509
|
+
put each clickable label about 10px below the current step's <span> and let
|
|
1510
|
+
a long label run over its neighbours, so eight Woo steps overlapped instead
|
|
1511
|
+
of ending in an ellipsis. Only !important beats !important; this selector is
|
|
1512
|
+
the more specific of the two. Without such a theme nothing changes: these
|
|
1513
|
+
are the values the base rule already sets. */
|
|
1514
|
+
button.cn-stages-widget__bar-label.cn-stages-widget__bar-label {
|
|
1515
|
+
min-width: 0 !important;
|
|
1516
|
+
margin: 0 !important;
|
|
1517
|
+
padding: 0 !important;
|
|
1518
|
+
border-width: 0 !important;
|
|
1519
|
+
overflow: hidden !important;
|
|
1520
|
+
text-overflow: ellipsis !important;
|
|
1521
|
+
white-space: nowrap !important;
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1505
1524
|
button.cn-stages-widget__bar-label[aria-disabled="true"] {
|
|
1506
1525
|
cursor: not-allowed;
|
|
1507
1526
|
}
|