@conduction/nextcloud-vue 2.56.0-beta.30 → 2.56.0-beta.31
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.map +1 -1
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js +3 -3
- package/dist/esm/components/CnActionsBar/CnActionsBar.vue3.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +80 -4
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js +95 -91
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js +85 -6
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js +1 -1
- package/dist/esm/components/CnBannerWidget/CnBannerWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +4 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue3.js.map +1 -1
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js +1 -1
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnConversationThread/CnConversationWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js +2 -2
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardGrid/CnDashboardGrid.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +40 -3
- 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/CnDataTable/CnDataTable.vue2.js +19 -11
- 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 +43 -0
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js +1 -1
- package/dist/esm/components/CnDetailPage/CnDetailPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue2.js +1 -1
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnDocumentReviewList/CnDocumentReviewWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnFacetSidebar/CnFacetSidebar.vue2.js +7 -7
- package/dist/esm/components/CnFacetSidebar/CnFacetSidebar.vue2.js.map +1 -1
- package/dist/esm/components/CnFacetSidebar/CnFacetSidebar.vue3.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js +125 -0
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFactsWidget/CnFactsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js +3 -2
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnFieldListWidget/CnFieldListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js +302 -4
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue2.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js +417 -244
- package/dist/esm/components/CnFilesBrowser/CnFilesBrowser.vue3.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/filesBrowser.js +101 -1
- package/dist/esm/components/CnFilesBrowser/filesBrowser.js.map +1 -1
- package/dist/esm/components/CnFilesBrowser/filesBrowserColumns.js +141 -1
- package/dist/esm/components/CnFilesBrowser/filesBrowserColumns.js.map +1 -1
- package/dist/esm/components/CnFormPage/CnFormPage.vue2.js +60 -0
- package/dist/esm/components/CnFormPage/CnFormPage.vue2.js.map +1 -1
- package/dist/esm/components/CnFormPage/CnFormPage.vue3.js +74 -49
- package/dist/esm/components/CnFormPage/CnFormPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +29 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +48 -4
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js +7 -7
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js.map +1 -1
- package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue2.js +1 -1
- package/dist/esm/components/CnNextStepCard/CnNextStepWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectListWidget/CnObjectListWidget.vue2.js +1 -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/CnObjectSidebar/CnFilesTab.vue2.js +63 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js +8 -1
- package/dist/esm/components/CnObjectSidebar/CnFilesTab.vue3.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js +14 -0
- package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue2.js.map +1 -1
- package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue3.js +5 -1
- package/dist/esm/components/CnObjectSidebar/CnNotesTab.vue3.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js +13 -6
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRecordListWidget/CnRecordListWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js +84 -1
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js +85 -41
- package/dist/esm/components/CnStatsBlockWidget/CnStatsBlockWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js +7 -2
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue2.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/CnWidgetObjectTable.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetObjectTable/dashboardRegistration.js +5 -0
- package/dist/esm/components/CnWidgetObjectTable/dashboardRegistration.js.map +1 -1
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js +197 -6
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js +94 -50
- package/dist/esm/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue3.js.map +1 -1
- package/dist/esm/composables/useListView.js +4 -4
- package/dist/esm/composables/useListView.js.map +1 -1
- package/dist/esm/index.js +10 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +8 -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 +198 -8
- package/dist/esm/utils/facets.js +75 -3
- package/dist/esm/utils/facets.js.map +1 -1
- package/dist/esm/utils/fillTemplate.js +7 -1
- package/dist/esm/utils/fillTemplate.js.map +1 -1
- package/dist/esm/utils/pageControls.js +81 -0
- package/dist/esm/utils/pageControls.js.map +1 -0
- package/dist/esm/utils/personalLenses.js +40 -3
- package/dist/esm/utils/personalLenses.js.map +1 -1
- package/dist/esm/utils/resolveFilterTokens.js +1 -1
- package/dist/esm/utils/resolveFilterTokens.js.map +1 -1
- package/dist/esm/utils/resolveManifestTokens.js +2 -0
- package/dist/esm/utils/resolveManifestTokens.js.map +1 -1
- package/dist/esm/utils/sentinelTokens.js +19 -1
- package/dist/esm/utils/sentinelTokens.js.map +1 -1
- package/dist/esm/utils/userGroupsPeek.js +91 -0
- package/dist/esm/utils/userGroupsPeek.js.map +1 -0
- package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/esm/utils/visibleWhen.js +57 -5
- package/dist/esm/utils/visibleWhen.js.map +1 -1
- package/dist/esm/utils/widgetVisibility.js +4 -57
- package/dist/esm/utils/widgetVisibility.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +4309 -2233
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +198 -8
- package/l10n/en.json +8 -1
- package/l10n/nl.json +54 -1
- package/package.json +2 -1
- package/scripts/check-docs-mdx.mjs +108 -0
- package/src/components/CnActionsBar/CnActionsBar.vue +6 -1
- package/src/components/CnAppRoot/CnAppRoot.md +4 -2
- package/src/components/CnAppRoot/CnAppRoot.vue +151 -59
- package/src/components/CnBannerWidget/CnBannerWidget.vue +112 -7
- package/src/components/CnCellRenderer/CnCellRenderer.vue +4 -1
- package/src/components/CnConversationThread/CnConversationWidget.vue +1 -1
- package/src/components/CnDashboardGrid/CnDashboardGrid.vue +2 -2
- package/src/components/CnDashboardPage/CnDashboardPage.vue +40 -3
- package/src/components/CnDataTable/CnDataTable.vue +19 -11
- package/src/components/CnDetailPage/CnDetailPage.vue +44 -1
- package/src/components/CnDocumentReviewList/CnDocumentReviewWidget.vue +1 -1
- package/src/components/CnFacetSidebar/CnFacetSidebar.vue +7 -7
- package/src/components/CnFactsWidget/CnFactsWidget.vue +125 -0
- package/src/components/CnFieldListWidget/CnFieldListWidget.vue +3 -2
- package/src/components/CnFilesBrowser/CnFilesBrowser.vue +588 -117
- package/src/components/CnFilesBrowser/filesBrowser.js +100 -0
- package/src/components/CnFilesBrowser/filesBrowserColumns.js +140 -0
- package/src/components/CnFormPage/CnFormPage.vue +81 -0
- package/src/components/CnIndexPage/CnIndexPage.vue +56 -3
- package/src/components/CnIndexSidebar/CnIndexSidebar.vue +7 -7
- package/src/components/CnNextStepCard/CnNextStepWidget.vue +1 -1
- package/src/components/CnObjectListWidget/CnObjectListWidget.vue +1 -1
- package/src/components/CnObjectSidebar/CnFilesTab.vue +70 -1
- package/src/components/CnObjectSidebar/CnNotesTab.vue +16 -0
- package/src/components/CnRecordListWidget/CnRecordListWidget.vue +13 -6
- package/src/components/CnStatsBlockWidget/CnStatsBlockWidget.vue +180 -3
- package/src/components/CnWidgetObjectTable/CnWidgetObjectTable.vue +7 -2
- package/src/components/CnWidgetObjectTable/dashboardRegistration.js +5 -0
- package/src/components/CnWorkspaceFilterWidget/CnWorkspaceFilterWidget.vue +281 -10
- package/src/components/index.js +8 -0
- package/src/composables/useListView.js +4 -4
- package/src/css/look-board-index.css +16 -8
- package/src/index.js +8 -0
- package/src/schemas/app-manifest-v2.schema.json +93 -9
- package/src/utils/facets.js +74 -2
- package/src/utils/fillTemplate.js +7 -1
- package/src/utils/pageControls.js +78 -0
- package/src/utils/personalLenses.js +39 -2
- package/src/utils/resolveFilterTokens.js +3 -1
- package/src/utils/resolveManifestTokens.js +2 -0
- package/src/utils/sentinelTokens.js +19 -1
- package/src/utils/userGroupsPeek.js +87 -0
- package/src/utils/validateManifestV2.compiled.js +3 -3
- package/src/utils/visibleWhen.js +56 -4
- package/src/utils/widgetVisibility.js +7 -56
|
@@ -81,6 +81,7 @@ import CnIcon from '../CnIcon/CnIcon.vue'
|
|
|
81
81
|
import CnStatusBadge from '../CnStatusBadge/CnStatusBadge.vue'
|
|
82
82
|
import { isBadgeVariant } from '../../utils/badgeVariants.js'
|
|
83
83
|
import { fillTemplate, firstFilled } from '../../utils/fillTemplate.js'
|
|
84
|
+
import { pageFilter, pageSort } from '../../utils/pageControls.js'
|
|
84
85
|
import { readPath } from '../../utils/readPath.js'
|
|
85
86
|
import { dropOptionalUnresolved, hasUnresolvedTokens, resolveFilterTokens } from '../../utils/resolveFilterTokens.js'
|
|
86
87
|
|
|
@@ -108,7 +109,7 @@ function unwrap(value) {
|
|
|
108
109
|
* objects filtered on a reference to this record. The source keys are those of
|
|
109
110
|
* `object-list` (`register`, `schema`, `filter` with the `@objectId` /
|
|
110
111
|
* `@object.<field>` / `@workspace.<key>` tokens, `sort`, `limit`, `extend`,
|
|
111
|
-
* `rowRoute`); what differs is the drawing: `presentation` is `rows`, `feed`
|
|
112
|
+
* `rowRoute`, `pageControls`); what differs is the drawing: `presentation` is `rows`, `feed`
|
|
112
113
|
* or `inline`, and each line is a template (`"{identifier} · {caseTypeName}"`,
|
|
113
114
|
* `"Termijn {deadline|date}"`).
|
|
114
115
|
*
|
|
@@ -188,8 +189,8 @@ export default {
|
|
|
188
189
|
|
|
189
190
|
/** @return {object} The filter with its tokens resolved. */
|
|
190
191
|
resolvedFilter() {
|
|
191
|
-
const filter = this.content?.filter
|
|
192
|
-
return dropOptionalUnresolved(resolveFilterTokens(filter
|
|
192
|
+
const filter = pageFilter(this.content?.pageControls, this.tokenCtx.workspace, this.content?.filter)
|
|
193
|
+
return dropOptionalUnresolved(resolveFilterTokens(filter, this.tokenCtx))
|
|
193
194
|
},
|
|
194
195
|
|
|
195
196
|
/** @return {boolean} A required token (the record id, say) has no value yet. */
|
|
@@ -197,6 +198,11 @@ export default {
|
|
|
197
198
|
return hasUnresolvedTokens(this.resolvedFilter)
|
|
198
199
|
},
|
|
199
200
|
|
|
201
|
+
/** @return {object|null} The sort: the page's sort control when the widget listens to one, else its own. */
|
|
202
|
+
effectiveSort() {
|
|
203
|
+
return pageSort(this.content?.pageControls, this.tokenCtx.workspace, this.content?.sort)
|
|
204
|
+
},
|
|
205
|
+
|
|
200
206
|
/** @return {number} The fetch cap. */
|
|
201
207
|
limit() {
|
|
202
208
|
const value = Number(this.content?.limit)
|
|
@@ -206,7 +212,7 @@ export default {
|
|
|
206
212
|
/** @return {string} What a fetch depends on; a change fetches again. */
|
|
207
213
|
requestKey() {
|
|
208
214
|
const c = this.content || {}
|
|
209
|
-
return JSON.stringify([c.register, c.schema, this.resolvedFilter,
|
|
215
|
+
return JSON.stringify([c.register, c.schema, this.resolvedFilter, this.effectiveSort, this.limit, c.extend, this.waiting])
|
|
210
216
|
},
|
|
211
217
|
|
|
212
218
|
/** @return {Array<object>} The rows ready to draw. */
|
|
@@ -422,8 +428,9 @@ export default {
|
|
|
422
428
|
])
|
|
423
429
|
const url = generateUrl('/apps/openregister/api/objects/{register}/{schema}', { register: c.register, schema: c.schema })
|
|
424
430
|
const params = { _limit: this.limit }
|
|
425
|
-
|
|
426
|
-
|
|
431
|
+
const sort = this.effectiveSort
|
|
432
|
+
if (sort && typeof sort === 'object' && sort.field) {
|
|
433
|
+
params[`_order[${sort.field}]`] = sort.dir === 'desc' ? 'desc' : 'asc'
|
|
427
434
|
}
|
|
428
435
|
if (Array.isArray(c.extend)) {
|
|
429
436
|
const extend = c.extend.filter((e) => typeof e === 'string' && e !== '')
|
|
@@ -17,10 +17,42 @@
|
|
|
17
17
|
<template>
|
|
18
18
|
<div
|
|
19
19
|
class="cn-stats-block-widget"
|
|
20
|
-
:class="[iconClass, { 'cn-stats-block-widget--multi': hasEntries }]">
|
|
20
|
+
:class="[iconClass, { 'cn-stats-block-widget--multi': hasEntries && !isRows, 'cn-stats-block-widget--rows': isRows }]">
|
|
21
|
+
<!-- Rows layout: a title and one label-left, number-right row per entry
|
|
22
|
+
(the board's "Mijn Zuiddrecht nu" card). -->
|
|
23
|
+
<template v-if="isRows">
|
|
24
|
+
<h2 v-if="title" class="cn-stats-block-widget__rows-title" data-testid="cn-stats-block-rows-title">
|
|
25
|
+
{{ tl(title) }}
|
|
26
|
+
</h2>
|
|
27
|
+
<dl class="cn-stats-block-widget__rows" data-testid="cn-stats-block-rows">
|
|
28
|
+
<div
|
|
29
|
+
v-for="view in rowViews"
|
|
30
|
+
:key="view.key"
|
|
31
|
+
class="cn-stats-block-widget__row"
|
|
32
|
+
data-testid="cn-stats-block-row">
|
|
33
|
+
<dt class="cn-stats-block-widget__row-label">
|
|
34
|
+
<a
|
|
35
|
+
v-if="view.href"
|
|
36
|
+
:href="view.href"
|
|
37
|
+
data-testid="cn-stats-block-row-link"
|
|
38
|
+
@click="onRowClick($event, view.route)">{{ view.label }}</a>
|
|
39
|
+
<template v-else>
|
|
40
|
+
{{ view.label }}
|
|
41
|
+
</template>
|
|
42
|
+
</dt>
|
|
43
|
+
<dd
|
|
44
|
+
class="cn-stats-block-widget__row-value"
|
|
45
|
+
:class="'cn-stats-block-widget__row-value--' + view.variant"
|
|
46
|
+
data-testid="cn-stats-block-row-value">
|
|
47
|
+
{{ view.display }}
|
|
48
|
+
</dd>
|
|
49
|
+
</div>
|
|
50
|
+
</dl>
|
|
51
|
+
</template>
|
|
52
|
+
|
|
21
53
|
<!-- Multi-entry mode: one KPI per entry, all inside this widget card.
|
|
22
54
|
An entry with hideWhenZero whose resolved count is 0 is omitted. -->
|
|
23
|
-
<template v-if="hasEntries">
|
|
55
|
+
<template v-else-if="hasEntries">
|
|
24
56
|
<CnStatsBlock
|
|
25
57
|
v-for="view in entryViews"
|
|
26
58
|
:key="view.key"
|
|
@@ -71,6 +103,7 @@ import { subscribe, unsubscribe } from '@nextcloud/event-bus'
|
|
|
71
103
|
import CnStatsBlock from '../CnStatsBlock/CnStatsBlock.vue'
|
|
72
104
|
import { useDataSource } from '../../composables/useDataSource.js'
|
|
73
105
|
import { formatMetricValue, normalizeMetricFormat, unwrapAppConfig } from '../../utils/formatMetric.js'
|
|
106
|
+
import { followLinkClick, resolveHref } from '../../utils/linkNavigation.js'
|
|
74
107
|
import { dropOptionalUnresolved, hasUnresolvedTokens, resolveFilterTokens } from '../../utils/resolveFilterTokens.js'
|
|
75
108
|
|
|
76
109
|
// The canonical KPI scale (`--cn-kpi-*`) lives in one stylesheet. Imported
|
|
@@ -167,6 +200,8 @@ export default {
|
|
|
167
200
|
* currency (`currency`) and `@config.<key>` tokens in `format`.
|
|
168
201
|
*/
|
|
169
202
|
cnAppConfig: { default: () => ({}) },
|
|
203
|
+
/** The host's label lookup (CnAppRoot provides it); identity without one. */
|
|
204
|
+
cnTranslate: { default: () => (key) => key },
|
|
170
205
|
},
|
|
171
206
|
|
|
172
207
|
props: {
|
|
@@ -264,7 +299,10 @@ export default {
|
|
|
264
299
|
|
|
265
300
|
/**
|
|
266
301
|
* `stacked` gives every block the board look (see CnStatsBlock
|
|
267
|
-
* `layout`).
|
|
302
|
+
* `layout`). `rows` draws the title and then one row per entry (or the
|
|
303
|
+
* single source): the label on the left, the number on the right, the
|
|
304
|
+
* label a link when the entry has a `route`. Empty (the default) keeps
|
|
305
|
+
* the KPI card.
|
|
268
306
|
*
|
|
269
307
|
* @spec openspec/changes/zuiddrecht-pixel-gaps-3/specs/zuiddrecht-pixel-gaps-3/spec.md#requirement-a-stats-block-can-take-the-stacked-board-look
|
|
270
308
|
*/
|
|
@@ -353,6 +391,52 @@ export default {
|
|
|
353
391
|
return Array.isArray(this.entries) && this.entries.length > 0
|
|
354
392
|
},
|
|
355
393
|
|
|
394
|
+
/**
|
|
395
|
+
* Whether the widget draws label/number rows (`layout: "rows"`).
|
|
396
|
+
*
|
|
397
|
+
* @return {boolean}
|
|
398
|
+
*/
|
|
399
|
+
isRows() {
|
|
400
|
+
return this.layout === 'rows'
|
|
401
|
+
},
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* The rows of the rows layout: one per entry, or a single row from the
|
|
405
|
+
* `dataSource` (labelled by `countLabel`). A row is `{ key, label,
|
|
406
|
+
* display, variant, route, href }`; `display` is the formatted value,
|
|
407
|
+
* a dash while it loads or when the count could not be read.
|
|
408
|
+
*
|
|
409
|
+
* @return {Array<object>}
|
|
410
|
+
*/
|
|
411
|
+
rowViews() {
|
|
412
|
+
if (!this.hasEntries) {
|
|
413
|
+
const formatted = this.formattedSingle
|
|
414
|
+
return [{
|
|
415
|
+
key: 'single',
|
|
416
|
+
label: this.tl(this.countLabel),
|
|
417
|
+
display: this.loading ? '\u2014' : (formatted !== null ? formatted : this.formatCount(this.resolvedCount)),
|
|
418
|
+
variant: this.variant || 'default',
|
|
419
|
+
route: this.route,
|
|
420
|
+
href: this.route ? resolveHref(this.route, this.$router) : '',
|
|
421
|
+
}]
|
|
422
|
+
}
|
|
423
|
+
return this.entryViews.map((view) => {
|
|
424
|
+
const resolved = view.resolved === true
|
|
425
|
+
let display = '\u2014'
|
|
426
|
+
if (resolved) {
|
|
427
|
+
display = view.formatted !== null ? view.formatted : this.formatCount(view.count)
|
|
428
|
+
}
|
|
429
|
+
return {
|
|
430
|
+
key: view.key,
|
|
431
|
+
label: this.tl(view.entry.title || ''),
|
|
432
|
+
display,
|
|
433
|
+
variant: view.entry.variant || 'default',
|
|
434
|
+
route: view.route,
|
|
435
|
+
href: view.route ? resolveHref(view.route, this.$router) : '',
|
|
436
|
+
}
|
|
437
|
+
})
|
|
438
|
+
},
|
|
439
|
+
|
|
356
440
|
/**
|
|
357
441
|
* The unwrapped detail-page object context for token resolution, or
|
|
358
442
|
* null on surfaces (dashboards) that don't provide one.
|
|
@@ -566,6 +650,37 @@ export default {
|
|
|
566
650
|
},
|
|
567
651
|
|
|
568
652
|
methods: {
|
|
653
|
+
/**
|
|
654
|
+
* A manifest string through the host's label lookup.
|
|
655
|
+
*
|
|
656
|
+
* @param {string} text The text as written in the manifest.
|
|
657
|
+
* @return {string} The translated text.
|
|
658
|
+
*/
|
|
659
|
+
tl(text) {
|
|
660
|
+
return typeof this.cnTranslate === 'function' ? this.cnTranslate(text) : text
|
|
661
|
+
},
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* A count in the user's locale.
|
|
665
|
+
*
|
|
666
|
+
* @param {number} count The count.
|
|
667
|
+
* @return {string} The localized count.
|
|
668
|
+
*/
|
|
669
|
+
formatCount(count) {
|
|
670
|
+
return new Intl.NumberFormat().format(count)
|
|
671
|
+
},
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Follow a row label's link in the router, leaving a modified click to the browser.
|
|
675
|
+
*
|
|
676
|
+
* @param {MouseEvent} event The click.
|
|
677
|
+
* @param {object} route The route location.
|
|
678
|
+
* @return {void}
|
|
679
|
+
*/
|
|
680
|
+
onRowClick(event, route) {
|
|
681
|
+
followLinkClick(event, route, this.$router)
|
|
682
|
+
},
|
|
683
|
+
|
|
569
684
|
/**
|
|
570
685
|
* Re-query the tile's counts — the REST `/value` aggregation, the
|
|
571
686
|
* per-entry counts of multi-entry mode, and the `dataSource` GraphQL
|
|
@@ -758,4 +873,66 @@ export default {
|
|
|
758
873
|
flex-direction: column;
|
|
759
874
|
gap: var(--cn-kpi-stack-gap, 12px);
|
|
760
875
|
}
|
|
876
|
+
|
|
877
|
+
/* Rows layout: the board's "label left, number right" card body. */
|
|
878
|
+
.cn-stats-block-widget--rows {
|
|
879
|
+
display: flex;
|
|
880
|
+
flex-direction: column;
|
|
881
|
+
gap: 12px;
|
|
882
|
+
box-sizing: border-box;
|
|
883
|
+
padding: 20px 22px;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
.cn-stats-block-widget__rows-title {
|
|
887
|
+
margin: 0;
|
|
888
|
+
font-size: 15px;
|
|
889
|
+
font-weight: 600;
|
|
890
|
+
color: var(--color-text-maxcontrast);
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
.cn-stats-block-widget__rows {
|
|
894
|
+
display: flex;
|
|
895
|
+
flex-direction: column;
|
|
896
|
+
gap: 8px;
|
|
897
|
+
margin: 0;
|
|
898
|
+
font-size: 14px;
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
.cn-stats-block-widget__row {
|
|
902
|
+
display: flex;
|
|
903
|
+
align-items: baseline;
|
|
904
|
+
justify-content: space-between;
|
|
905
|
+
gap: 16px;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
.cn-stats-block-widget__row-label {
|
|
909
|
+
min-width: 0;
|
|
910
|
+
margin: 0;
|
|
911
|
+
overflow: hidden;
|
|
912
|
+
text-overflow: ellipsis;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
.cn-stats-block-widget__row-value {
|
|
916
|
+
flex: none;
|
|
917
|
+
margin: 0;
|
|
918
|
+
font-size: 17px;
|
|
919
|
+
font-weight: 700;
|
|
920
|
+
text-align: right;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
.cn-stats-block-widget__row-value--success {
|
|
924
|
+
color: var(--color-text-success, var(--color-success-text));
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
.cn-stats-block-widget__row-value--warning {
|
|
928
|
+
color: var(--color-text-warning, var(--color-warning-text));
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
.cn-stats-block-widget__row-value--error {
|
|
932
|
+
color: var(--color-text-error, var(--color-error-text));
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
.cn-stats-block-widget__row-value--primary {
|
|
936
|
+
color: var(--color-primary-element);
|
|
937
|
+
}
|
|
761
938
|
</style>
|
|
@@ -86,6 +86,7 @@ import { useEndpointSource } from '../../composables/useEndpointSource.js'
|
|
|
86
86
|
import { useObjectStore } from '../../store/useObjectStore.js'
|
|
87
87
|
import { dispatchAction, resolveObjectOpType } from '../../utils/actionsDispatcher.js'
|
|
88
88
|
import { resolveObjectTokenContext } from '../../utils/detailObjectContext.js'
|
|
89
|
+
import { pageFilter, pageSort } from '../../utils/pageControls.js'
|
|
89
90
|
import { dropOptionalUnresolved, hasUnresolvedTokens, resolveFilterTokens } from '../../utils/resolveFilterTokens.js'
|
|
90
91
|
import { compareVisibleWhen, readVisibleWhenPath } from '../../utils/visibleWhen.js'
|
|
91
92
|
import { CnIcon } from '../CnIcon/index.js'
|
|
@@ -517,7 +518,8 @@ export default {
|
|
|
517
518
|
* @return {object}
|
|
518
519
|
*/
|
|
519
520
|
resolvedFilter() {
|
|
520
|
-
|
|
521
|
+
const filter = pageFilter(this.source && this.source.pageControls, this.workspaceCtx, this.source && this.source.filter)
|
|
522
|
+
return dropOptionalUnresolved(resolveFilterTokens(filter, this.tokenCtx))
|
|
521
523
|
},
|
|
522
524
|
|
|
523
525
|
/**
|
|
@@ -590,7 +592,10 @@ export default {
|
|
|
590
592
|
resolvedFetchParams() {
|
|
591
593
|
const src = this.source || {}
|
|
592
594
|
const params = {}
|
|
593
|
-
|
|
595
|
+
// A page sort control (`source.pageControls.sort`) replaces the order.
|
|
596
|
+
const chosen = pageSort(src.pageControls, this.workspaceCtx, null)
|
|
597
|
+
const order = chosen ? { [chosen.field]: chosen.dir } : (src.order || {})
|
|
598
|
+
for (const [field, dir] of Object.entries(order)) {
|
|
594
599
|
params[`_order[${field}]`] = dir === 'desc' ? 'desc' : 'asc'
|
|
595
600
|
}
|
|
596
601
|
if (typeof src.limit === 'number' && src.limit > 0) {
|
|
@@ -73,6 +73,11 @@ export function objectTableContentToProps(content) {
|
|
|
73
73
|
if (hasLimit) {
|
|
74
74
|
source.limit = limit
|
|
75
75
|
}
|
|
76
|
+
// The page controls the table listens to belong to its source.
|
|
77
|
+
if (rest.pageControls && typeof rest.pageControls === 'object') {
|
|
78
|
+
source.pageControls = rest.pageControls
|
|
79
|
+
delete rest.pageControls
|
|
80
|
+
}
|
|
76
81
|
return { ...rest, source }
|
|
77
82
|
}
|
|
78
83
|
|