@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
|
@@ -6,7 +6,13 @@
|
|
|
6
6
|
<template>
|
|
7
7
|
<div
|
|
8
8
|
class="cn-workspace-filter-widget"
|
|
9
|
-
:class="{
|
|
9
|
+
:class="{
|
|
10
|
+
'cn-workspace-filter-widget--start': isStart,
|
|
11
|
+
'cn-workspace-filter-widget--chips': style === 'chips',
|
|
12
|
+
'cn-workspace-filter-widget--bar': isBar,
|
|
13
|
+
'cn-workspace-filter-widget--inline': content.inline === true,
|
|
14
|
+
'cn-workspace-filter-widget--end': content.align === 'end',
|
|
15
|
+
}"
|
|
10
16
|
data-testid="cn-workspace-filter-widget">
|
|
11
17
|
<!-- The start card (`layout: "start"`): a title and one line of
|
|
12
18
|
guidance beside the control, as the "Contact starten" card. -->
|
|
@@ -19,14 +25,24 @@
|
|
|
19
25
|
</p>
|
|
20
26
|
</div>
|
|
21
27
|
|
|
22
|
-
|
|
28
|
+
<!-- A bar (`layout: "bar"`): its `controls` side by side in one row,
|
|
29
|
+
each a workspace filter of its own (chips, a switch, a select). -->
|
|
30
|
+
<template v-if="isBar">
|
|
31
|
+
<CnWorkspaceFilterWidget
|
|
32
|
+
v-for="(control, index) in barControls"
|
|
33
|
+
:key="index"
|
|
34
|
+
:content="control"
|
|
35
|
+
@change="$emit('change', $event)" />
|
|
36
|
+
</template>
|
|
37
|
+
|
|
38
|
+
<p v-if="!isBar && label && style !== 'chips' && style !== 'toggle'" class="cn-workspace-filter-widget__label">
|
|
23
39
|
{{ label }}
|
|
24
40
|
</p>
|
|
25
41
|
|
|
26
42
|
<!-- An object picker (`objectSource`): searches OpenRegister on the
|
|
27
43
|
server, so it reaches every object, not a fetched page. -->
|
|
28
44
|
<CnResourceSelect
|
|
29
|
-
v-if="objectSource"
|
|
45
|
+
v-if="!isBar && objectSource"
|
|
30
46
|
class="cn-workspace-filter-widget__control"
|
|
31
47
|
:register="objectSource.register"
|
|
32
48
|
:schema="objectSource.schema"
|
|
@@ -44,6 +60,30 @@
|
|
|
44
60
|
{{ loadingLabel }}
|
|
45
61
|
</div>
|
|
46
62
|
|
|
63
|
+
<!-- Chips style (screens-page-lenses): one compact segmented group,
|
|
64
|
+
a count after each label, arrow keys move the choice. -->
|
|
65
|
+
<CnSegmentedControl
|
|
66
|
+
v-else-if="style === 'chips'"
|
|
67
|
+
class="cn-workspace-filter-widget__chips"
|
|
68
|
+
size="compact"
|
|
69
|
+
:options="chipOptions"
|
|
70
|
+
:modelValue="selected === undefined || selected === null ? null : String(selected)"
|
|
71
|
+
:ariaLabel="label || writeKey"
|
|
72
|
+
data-testid="cn-workspace-filter-chips"
|
|
73
|
+
@update:modelValue="applySelection" />
|
|
74
|
+
|
|
75
|
+
<!-- Toggle style (screens-page-controls): a switch that writes
|
|
76
|
+
`onValue` / `offValue` (true / false) to the key. -->
|
|
77
|
+
<NcCheckboxRadioSwitch
|
|
78
|
+
v-else-if="style === 'toggle'"
|
|
79
|
+
class="cn-workspace-filter-widget__toggle"
|
|
80
|
+
type="switch"
|
|
81
|
+
:modelValue="toggleOn"
|
|
82
|
+
data-testid="cn-workspace-filter-toggle"
|
|
83
|
+
@update:modelValue="onToggle">
|
|
84
|
+
{{ tr(label) }}
|
|
85
|
+
</NcCheckboxRadioSwitch>
|
|
86
|
+
|
|
47
87
|
<!-- Select style -->
|
|
48
88
|
<NcSelect
|
|
49
89
|
v-else-if="style === 'select'"
|
|
@@ -56,7 +96,7 @@
|
|
|
56
96
|
@update:modelValue="onSelect" />
|
|
57
97
|
|
|
58
98
|
<!-- Radio-list style (default) -->
|
|
59
|
-
<ul v-else
|
|
99
|
+
<ul v-else-if="!isBar"
|
|
60
100
|
class="cn-workspace-filter-widget__list"
|
|
61
101
|
role="radiogroup"
|
|
62
102
|
:aria-label="label || writeKey">
|
|
@@ -92,10 +132,13 @@
|
|
|
92
132
|
</template>
|
|
93
133
|
|
|
94
134
|
<script>
|
|
135
|
+
import { subscribe, unsubscribe } from '@nextcloud/event-bus'
|
|
95
136
|
import { translate as t } from '@nextcloud/l10n'
|
|
96
137
|
import { NcCheckboxRadioSwitch, NcSelect } from '@nextcloud/vue'
|
|
97
138
|
import CnResourceSelect from '../CnResourceSelect/CnResourceSelect.vue'
|
|
139
|
+
import CnSegmentedControl from '../CnSegmentedControl/CnSegmentedControl.vue'
|
|
98
140
|
import { fetchEndpointSource } from '../../composables/useEndpointSource.js'
|
|
141
|
+
import { fetchListTotal } from '../../utils/fetchFilterCounts.js'
|
|
99
142
|
import { resolveFilterTokens } from '../../utils/resolveFilterTokens.js'
|
|
100
143
|
|
|
101
144
|
/**
|
|
@@ -126,7 +169,7 @@ import { resolveFilterTokens } from '../../utils/resolveFilterTokens.js'
|
|
|
126
169
|
* content: {
|
|
127
170
|
* label: 'Queue',
|
|
128
171
|
* writes: '@workspace.queue',
|
|
129
|
-
* style: 'radio', // 'radio' (default) | 'select'
|
|
172
|
+
* style: 'radio', // 'radio' (default) | 'select' | 'chips'
|
|
130
173
|
* allLabel: 'All',
|
|
131
174
|
* showCounts: true,
|
|
132
175
|
* source: { register: 'pipelinq', schema: 'werkitem', groupBy: 'queue' },
|
|
@@ -136,7 +179,7 @@ import { resolveFilterTokens } from '../../utils/resolveFilterTokens.js'
|
|
|
136
179
|
export default {
|
|
137
180
|
name: 'CnWorkspaceFilterWidget',
|
|
138
181
|
|
|
139
|
-
components: { CnResourceSelect, NcSelect, NcCheckboxRadioSwitch },
|
|
182
|
+
components: { CnResourceSelect, CnSegmentedControl, NcSelect, NcCheckboxRadioSwitch },
|
|
140
183
|
|
|
141
184
|
inject: {
|
|
142
185
|
/**
|
|
@@ -162,7 +205,19 @@ export default {
|
|
|
162
205
|
* server and writes the picked object's id; without a selection and
|
|
163
206
|
* without `allLabel` it selects the first object.
|
|
164
207
|
*
|
|
165
|
-
*
|
|
208
|
+
* Chips (screens-page-lenses): `style: "chips"` draws the options as one
|
|
209
|
+
* compact segmented group with a count after each label. An option's
|
|
210
|
+
* `countFrom` (`{ register, schema, filter? }`, or a list of them that
|
|
211
|
+
* are added up) counts that list; a static `count` is used otherwise.
|
|
212
|
+
* `urlKey` keeps the choice in the address (`?<urlKey>=<value>`), and
|
|
213
|
+
* the address wins over `default` on load.
|
|
214
|
+
*
|
|
215
|
+
* Page controls (screens-page-controls): `style: "toggle"` is a switch
|
|
216
|
+
* that writes `onValue` / `offValue` (true / false); `layout: "bar"`
|
|
217
|
+
* with `controls` draws several controls side by side in one row, and
|
|
218
|
+
* `align: "end"` pushes a control to the right edge.
|
|
219
|
+
*
|
|
220
|
+
* @type {{label?: string, controls?: Array<object>, onValue?: unknown, offValue?: unknown, align?: string, urlKey?: string, writes?: string, style?: string, allLabel?: string, showCounts?: boolean, options?: Array, source?: object, endpointSource?: object, objectSource?: object, default?: (string|number), layout?: string, title?: string, description?: string, link?: object}}
|
|
166
221
|
*/
|
|
167
222
|
content: {
|
|
168
223
|
type: Object,
|
|
@@ -180,6 +235,8 @@ export default {
|
|
|
180
235
|
loading: false,
|
|
181
236
|
/** The currently-selected value (mirrors the workspace key). */
|
|
182
237
|
selected: undefined,
|
|
238
|
+
/** Counted totals of the options that ask (`countFrom`), by option position. */
|
|
239
|
+
optionCounts: {},
|
|
183
240
|
}
|
|
184
241
|
},
|
|
185
242
|
|
|
@@ -233,9 +290,73 @@ export default {
|
|
|
233
290
|
return src && typeof src === 'object' && src.register && src.schema ? src : null
|
|
234
291
|
},
|
|
235
292
|
|
|
236
|
-
/** Choice style: 'select' or 'radio' (default). */
|
|
293
|
+
/** Choice style: 'chips', 'select' or 'radio' (default). */
|
|
237
294
|
style() {
|
|
238
|
-
return this.content.style
|
|
295
|
+
return ['select', 'chips', 'toggle'].includes(this.content.style) ? this.content.style : 'radio'
|
|
296
|
+
},
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* The chips: each option with its label translated and its count,
|
|
300
|
+
* the static one or the counted one.
|
|
301
|
+
*
|
|
302
|
+
* @return {Array<{value: string, label: string, count?: number}>}
|
|
303
|
+
* @spec openspec/changes/screens-page-lenses/specs/page-lenses/spec.md#requirement-a-workspace-filter-can-draw-chips-with-counts
|
|
304
|
+
*/
|
|
305
|
+
chipOptions() {
|
|
306
|
+
return this.displayOptions.map((option, index) => {
|
|
307
|
+
const counted = this.optionCounts[index]
|
|
308
|
+
const count = counted !== undefined && counted !== null ? counted : option.count
|
|
309
|
+
const chip = { value: String(option.value), label: this.tr(option.label) }
|
|
310
|
+
if (this.showCounts && Number.isFinite(count)) {
|
|
311
|
+
chip.count = count
|
|
312
|
+
}
|
|
313
|
+
return chip
|
|
314
|
+
})
|
|
315
|
+
},
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Whether the widget is a bar of controls (`layout: "bar"`).
|
|
319
|
+
*
|
|
320
|
+
* @return {boolean}
|
|
321
|
+
* @spec openspec/changes/screens-page-controls/specs/page-controls/spec.md#requirement-a-bar-holds-the-pages-controls-in-one-row
|
|
322
|
+
*/
|
|
323
|
+
isBar() {
|
|
324
|
+
return this.content.layout === 'bar' && Array.isArray(this.content.controls)
|
|
325
|
+
},
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* The bar's controls, each an inline workspace filter of its own.
|
|
329
|
+
*
|
|
330
|
+
* @return {Array<object>}
|
|
331
|
+
*/
|
|
332
|
+
barControls() {
|
|
333
|
+
return this.content.controls
|
|
334
|
+
.filter((control) => control && typeof control === 'object')
|
|
335
|
+
.map((control) => ({ ...control, inline: true }))
|
|
336
|
+
},
|
|
337
|
+
|
|
338
|
+
/** @return {unknown} The value a switch writes when on. */
|
|
339
|
+
onValue() {
|
|
340
|
+
return this.content.onValue !== undefined ? this.content.onValue : true
|
|
341
|
+
},
|
|
342
|
+
|
|
343
|
+
/** @return {unknown} The value a switch writes when off. */
|
|
344
|
+
offValue() {
|
|
345
|
+
return this.content.offValue !== undefined ? this.content.offValue : false
|
|
346
|
+
},
|
|
347
|
+
|
|
348
|
+
/** @return {boolean} Whether the switch is on. */
|
|
349
|
+
toggleOn() {
|
|
350
|
+
return this.selected !== undefined && String(this.selected) === String(this.onValue)
|
|
351
|
+
},
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* The address key that carries the choice (`urlKey`), or ''.
|
|
355
|
+
*
|
|
356
|
+
* @return {string}
|
|
357
|
+
*/
|
|
358
|
+
urlKey() {
|
|
359
|
+
return typeof this.content.urlKey === 'string' ? this.content.urlKey : ''
|
|
239
360
|
},
|
|
240
361
|
|
|
241
362
|
/** Whether to render per-option counts. */
|
|
@@ -316,17 +437,115 @@ export default {
|
|
|
316
437
|
// Seed the selection: existing workspace value → content.default → the
|
|
317
438
|
// "All" clear option (empty) when present, else the first option once
|
|
318
439
|
// loaded (handled in loadOptions).
|
|
440
|
+
const fromAddress = this.readAddress()
|
|
319
441
|
const existing = this.readWorkspace()
|
|
320
|
-
if (
|
|
442
|
+
if (fromAddress !== undefined) {
|
|
443
|
+
this.selected = fromAddress
|
|
444
|
+
this.writeWorkspace(fromAddress)
|
|
445
|
+
} else if (existing !== undefined) {
|
|
321
446
|
this.selected = existing
|
|
322
447
|
} else if (this.content.default !== undefined) {
|
|
323
448
|
this.selected = this.content.default
|
|
324
449
|
this.writeWorkspace(this.content.default)
|
|
450
|
+
} else if (this.style === 'toggle') {
|
|
451
|
+
// A switch starts off, and says so, so a condition on its key holds.
|
|
452
|
+
this.selected = this.offValue
|
|
453
|
+
this.writeWorkspace(this.offValue)
|
|
325
454
|
}
|
|
326
455
|
this.loadOptions()
|
|
327
456
|
},
|
|
328
457
|
|
|
458
|
+
mounted() {
|
|
459
|
+
this.onPageRefresh = () => this.loadCounts()
|
|
460
|
+
subscribe('cn:page:refresh', this.onPageRefresh)
|
|
461
|
+
this.loadCounts()
|
|
462
|
+
},
|
|
463
|
+
|
|
464
|
+
beforeUnmount() {
|
|
465
|
+
unsubscribe('cn:page:refresh', this.onPageRefresh)
|
|
466
|
+
},
|
|
467
|
+
|
|
329
468
|
methods: {
|
|
469
|
+
/**
|
|
470
|
+
* The choice named in the address (`?<urlKey>=…`), when it is one of
|
|
471
|
+
* the options; undefined otherwise.
|
|
472
|
+
*
|
|
473
|
+
* @return {string|undefined}
|
|
474
|
+
* @spec openspec/changes/screens-page-lenses/specs/page-lenses/spec.md#requirement-the-choice-is-in-the-address
|
|
475
|
+
*/
|
|
476
|
+
readAddress() {
|
|
477
|
+
if (!this.urlKey || !this.$route || !this.$route.query) {
|
|
478
|
+
return undefined
|
|
479
|
+
}
|
|
480
|
+
const raw = this.$route.query[this.urlKey]
|
|
481
|
+
if (this.style === 'toggle') {
|
|
482
|
+
if (raw === undefined) {
|
|
483
|
+
return undefined
|
|
484
|
+
}
|
|
485
|
+
return [this.onValue, this.offValue].find((value) => String(value) === String(raw))
|
|
486
|
+
}
|
|
487
|
+
const known = this.displayOptions.some((option) => String(option.value) === String(raw))
|
|
488
|
+
return known ? String(raw) : undefined
|
|
489
|
+
},
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Write the choice to the address, replacing the history entry and
|
|
493
|
+
* keeping the other query keys.
|
|
494
|
+
*
|
|
495
|
+
* @param {unknown} value The chosen value.
|
|
496
|
+
* @return {void}
|
|
497
|
+
* @spec openspec/changes/screens-page-lenses/specs/page-lenses/spec.md#requirement-the-choice-is-in-the-address
|
|
498
|
+
*/
|
|
499
|
+
writeAddress(value) {
|
|
500
|
+
if (!this.urlKey || !this.$router || !this.$route) {
|
|
501
|
+
return
|
|
502
|
+
}
|
|
503
|
+
const query = this.$route.query || {}
|
|
504
|
+
if (String(query[this.urlKey]) === String(value)) {
|
|
505
|
+
return
|
|
506
|
+
}
|
|
507
|
+
const result = this.$router.replace({ query: { ...query, [this.urlKey]: String(value) } })
|
|
508
|
+
if (result && typeof result.catch === 'function') {
|
|
509
|
+
result.catch(() => {})
|
|
510
|
+
}
|
|
511
|
+
},
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* Count the lists behind the options that ask (`countFrom`: a list
|
|
515
|
+
* `{ register, schema, filter? }`, or several that are added up). One
|
|
516
|
+
* list request each; an option whose count failed shows none.
|
|
517
|
+
*
|
|
518
|
+
* @return {Promise<void>}
|
|
519
|
+
* @spec openspec/changes/screens-page-lenses/specs/page-lenses/spec.md#requirement-a-workspace-filter-can-draw-chips-with-counts
|
|
520
|
+
*/
|
|
521
|
+
async loadCounts() {
|
|
522
|
+
const options = this.displayOptions
|
|
523
|
+
const seq = (this.countSeq = (this.countSeq || 0) + 1)
|
|
524
|
+
const ctx = { workspace: this.readWorkspaceBag() }
|
|
525
|
+
await Promise.all(options.map(async (option, index) => {
|
|
526
|
+
const raw = (Array.isArray(this.content.options) ? this.content.options : [])
|
|
527
|
+
.find((candidate) => candidate && typeof candidate === 'object' && String(candidate.value ?? candidate.id) === String(option.value))
|
|
528
|
+
const from = raw ? raw.countFrom : null
|
|
529
|
+
const lists = (Array.isArray(from) ? from : [from]).filter((list) => list && typeof list === 'object' && list.register && list.schema)
|
|
530
|
+
if (lists.length === 0) {
|
|
531
|
+
return
|
|
532
|
+
}
|
|
533
|
+
let total
|
|
534
|
+
try {
|
|
535
|
+
const parts = await Promise.all(lists.map((list) => fetchListTotal(list.register, list.schema, list.filter || {}, ctx)))
|
|
536
|
+
if (parts.some((part) => part === null)) {
|
|
537
|
+
throw new Error('no total')
|
|
538
|
+
}
|
|
539
|
+
total = parts.reduce((sum, part) => sum + part, 0)
|
|
540
|
+
} catch {
|
|
541
|
+
total = null
|
|
542
|
+
}
|
|
543
|
+
if (seq === this.countSeq) {
|
|
544
|
+
this.optionCounts = { ...this.optionCounts, [index]: total }
|
|
545
|
+
}
|
|
546
|
+
}))
|
|
547
|
+
},
|
|
548
|
+
|
|
330
549
|
/**
|
|
331
550
|
* Normalise a raw option into `{ value, label, count? }`. Accepts
|
|
332
551
|
* `{ value, label, count }`, `{ id, name }`, `{ key, count }` (OR
|
|
@@ -503,6 +722,17 @@ export default {
|
|
|
503
722
|
this.applySelection(option.value)
|
|
504
723
|
},
|
|
505
724
|
|
|
725
|
+
/**
|
|
726
|
+
* Switch handler: write `onValue` or `offValue`.
|
|
727
|
+
*
|
|
728
|
+
* @param {boolean} on Whether the switch is on.
|
|
729
|
+
* @return {void}
|
|
730
|
+
* @spec openspec/changes/screens-page-controls/specs/page-controls/spec.md#requirement-a-toggle-and-a-select-are-page-controls
|
|
731
|
+
*/
|
|
732
|
+
onToggle(on) {
|
|
733
|
+
this.applySelection(on ? this.onValue : this.offValue)
|
|
734
|
+
},
|
|
735
|
+
|
|
506
736
|
/**
|
|
507
737
|
* NcSelect input handler.
|
|
508
738
|
*
|
|
@@ -523,6 +753,7 @@ export default {
|
|
|
523
753
|
applySelection(value) {
|
|
524
754
|
this.selected = value
|
|
525
755
|
this.writeWorkspace(value)
|
|
756
|
+
this.writeAddress(value)
|
|
526
757
|
/**
|
|
527
758
|
* @event change Emitted when the selection changes. Payload: `{ key, value }`.
|
|
528
759
|
*/
|
|
@@ -610,6 +841,46 @@ export default {
|
|
|
610
841
|
gap: 2px;
|
|
611
842
|
}
|
|
612
843
|
|
|
844
|
+
/* The chips: the compact segmented group, left, in the card the grid draws. */
|
|
845
|
+
.cn-workspace-filter-widget--chips {
|
|
846
|
+
flex-direction: row;
|
|
847
|
+
flex-wrap: wrap;
|
|
848
|
+
align-items: center;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
/* A bar: its controls in one row, an `align: "end"` control pushed right. */
|
|
852
|
+
.cn-workspace-filter-widget--bar {
|
|
853
|
+
flex-direction: row;
|
|
854
|
+
flex-wrap: wrap;
|
|
855
|
+
align-items: center;
|
|
856
|
+
gap: 12px 16px;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
/* An inline control sits in a bar: label left of the control, width to its content. */
|
|
860
|
+
.cn-workspace-filter-widget--inline {
|
|
861
|
+
flex-direction: row;
|
|
862
|
+
align-items: center;
|
|
863
|
+
gap: 8px;
|
|
864
|
+
width: auto;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
.cn-workspace-filter-widget--inline .cn-workspace-filter-widget__label {
|
|
868
|
+
font-weight: 400;
|
|
869
|
+
color: var(--color-text-maxcontrast);
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
.cn-workspace-filter-widget--end {
|
|
873
|
+
margin-inline-start: auto;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
.cn-workspace-filter-widget--end ~ .cn-workspace-filter-widget--end {
|
|
877
|
+
margin-inline-start: 0;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
.cn-workspace-filter-widget__toggle {
|
|
881
|
+
font-weight: 600;
|
|
882
|
+
}
|
|
883
|
+
|
|
613
884
|
/* The start card: text left, control (and link) right, as PqDashboard's "Contact starten". */
|
|
614
885
|
.cn-workspace-filter-widget--start {
|
|
615
886
|
flex-direction: row;
|
package/src/components/index.js
CHANGED
|
@@ -220,6 +220,14 @@ export { CnSectionsWidget } from './CnSectionsWidget/index.js'
|
|
|
220
220
|
export { CnFactsWidget } from './CnFactsWidget/index.js'
|
|
221
221
|
export { CnFieldListWidget } from './CnFieldListWidget/index.js'
|
|
222
222
|
export { CnRecordListWidget } from './CnRecordListWidget/index.js'
|
|
223
|
+
export { CnBannerWidget } from './CnBannerWidget/index.js'
|
|
224
|
+
export { default as CnConversationWidget } from './CnConversationThread/CnConversationWidget.vue'
|
|
225
|
+
export { default as CnDocumentReviewWidget } from './CnDocumentReviewList/CnDocumentReviewWidget.vue'
|
|
226
|
+
export { default as CnNextStepWidget } from './CnNextStepCard/CnNextStepWidget.vue'
|
|
227
|
+
export { default as CnAuditTrailWidget } from './CnAuditTrailWidget/CnAuditTrailWidget.vue'
|
|
228
|
+
export { CnKbSearchWidget } from './CnKbSearchWidget/index.js'
|
|
229
|
+
export { CnInteractionFormWidget } from './CnInteractionFormWidget/index.js'
|
|
230
|
+
export { CnWorkspaceFilterWidget } from './CnWorkspaceFilterWidget/index.js'
|
|
223
231
|
export { CnFilePreview } from './CnFilePreview/index.js'
|
|
224
232
|
export { CnTaskFormDialog } from './CnTaskFormDialog/index.js'
|
|
225
233
|
export { CnChildRecordsField } from './CnChildRecordsField/index.js'
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { computed, isRef, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
|
2
2
|
import { useObjectStore } from '../store/index.js'
|
|
3
|
+
import { facetFilterParams } from '../utils/facets.js'
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Composable for managing list view state with full objectStore integration.
|
|
@@ -163,10 +164,9 @@ export function useListView(objectTypeOrOptions, options) {
|
|
|
163
164
|
}
|
|
164
165
|
|
|
165
166
|
for (const [key, values] of Object.entries(activeFilters.value)) {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}
|
|
167
|
+
// Single-value arrays are unwrapped to scalar params; the
|
|
168
|
+
// missing-value option becomes `<key>_isnull=true`.
|
|
169
|
+
Object.assign(params, facetFilterParams(key, values))
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
// Fixed filters (e.g. a route-param-scoped `pages[].config.filter`) are
|
|
@@ -249,7 +249,8 @@
|
|
|
249
249
|
}
|
|
250
250
|
|
|
251
251
|
/* Filter: a labelled secondary button, 36px, radius 8, with the active count. */
|
|
252
|
-
.cn-look-board .cn-actions-bar__filter-button.button-vue
|
|
252
|
+
.cn-look-board .cn-actions-bar__filter-button.button-vue,
|
|
253
|
+
.cn-look-board .cn-actions-bar__filter-menu .button-vue {
|
|
253
254
|
min-height: 36px;
|
|
254
255
|
height: 36px;
|
|
255
256
|
border-radius: 8px;
|
|
@@ -499,11 +500,16 @@
|
|
|
499
500
|
max-width: calc(100% - 26px);
|
|
500
501
|
}
|
|
501
502
|
|
|
502
|
-
/* The selection column:
|
|
503
|
+
/* The selection column: 18px checkboxes, 28px of content after 16px of padding.
|
|
504
|
+
The box model is set with `!important` and the widths include the padding:
|
|
505
|
+
Nextcloud's server.css sets `box-sizing: border-box` on everything under
|
|
506
|
+
`#content[class*="app-"]`, an id selector no class rule outranks, so a plain
|
|
507
|
+
`content-box` here was ignored and the cell left 12px for the checkbox (the
|
|
508
|
+
header's checkbox showed as a stray bracket). */
|
|
503
509
|
.cn-look-board .cn-table-container--board .cn-data-table .cn-table-col--checkbox {
|
|
504
|
-
box-sizing:
|
|
505
|
-
width:
|
|
506
|
-
min-width:
|
|
510
|
+
box-sizing: border-box !important;
|
|
511
|
+
width: 44px;
|
|
512
|
+
min-width: 44px;
|
|
507
513
|
padding-inline: 16px 0;
|
|
508
514
|
text-align: start;
|
|
509
515
|
}
|
|
@@ -561,10 +567,12 @@
|
|
|
561
567
|
line-height: 1.4;
|
|
562
568
|
}
|
|
563
569
|
|
|
564
|
-
/* The row menu column holds one 34px button
|
|
570
|
+
/* The row menu column holds one 34px button between 16px of padding, so 66px
|
|
571
|
+
border to border (see the selection column for why the box model is forced). */
|
|
565
572
|
.cn-look-board .cn-table-container--board .cn-data-table .cn-table-col--actions {
|
|
566
|
-
|
|
567
|
-
|
|
573
|
+
box-sizing: border-box !important;
|
|
574
|
+
width: 66px;
|
|
575
|
+
min-width: 66px;
|
|
568
576
|
padding-inline: 16px;
|
|
569
577
|
text-align: end;
|
|
570
578
|
}
|
package/src/index.js
CHANGED
|
@@ -43,6 +43,8 @@ export {
|
|
|
43
43
|
CnAppNav,
|
|
44
44
|
CnAppRoot,
|
|
45
45
|
CnAuditTrailCard,
|
|
46
|
+
CnAuditTrailWidget,
|
|
47
|
+
CnBannerWidget,
|
|
46
48
|
CnBoardView,
|
|
47
49
|
CnBodySections,
|
|
48
50
|
CnBreadcrumbs,
|
|
@@ -70,6 +72,7 @@ export {
|
|
|
70
72
|
CnContainerWidgetForm,
|
|
71
73
|
CnContextMenu,
|
|
72
74
|
CnConversationThread,
|
|
75
|
+
CnConversationWidget,
|
|
73
76
|
CnCopyDialog,
|
|
74
77
|
CnCountdownWidget,
|
|
75
78
|
CnCountdownWidgetForm,
|
|
@@ -97,6 +100,7 @@ export {
|
|
|
97
100
|
CnDividerWidget,
|
|
98
101
|
CnDividerWidgetForm,
|
|
99
102
|
CnDocumentReviewList,
|
|
103
|
+
CnDocumentReviewWidget,
|
|
100
104
|
CnDurationField,
|
|
101
105
|
CnEditActionsModal,
|
|
102
106
|
CnEditDataModal,
|
|
@@ -164,11 +168,13 @@ export {
|
|
|
164
168
|
CnIntegrationTab,
|
|
165
169
|
CnIntegrationWidget,
|
|
166
170
|
CnIntegrationWidgetGrid,
|
|
171
|
+
CnInteractionFormWidget,
|
|
167
172
|
CnItemCard,
|
|
168
173
|
CnJourney,
|
|
169
174
|
CnJourneyDialog,
|
|
170
175
|
CnJourneyReviewList,
|
|
171
176
|
CnJsonViewer,
|
|
177
|
+
CnKbSearchWidget,
|
|
172
178
|
CnKpiGrid,
|
|
173
179
|
CnLabelWidget,
|
|
174
180
|
CnLabelWidgetForm,
|
|
@@ -200,6 +206,7 @@ export {
|
|
|
200
206
|
CnNcWidgetGridPicker,
|
|
201
207
|
CnNcWidgetWidget,
|
|
202
208
|
CnNextStepCard,
|
|
209
|
+
CnNextStepWidget,
|
|
203
210
|
CnNoteBody,
|
|
204
211
|
CnNoteCard,
|
|
205
212
|
CnNoteComposer,
|
|
@@ -324,6 +331,7 @@ export {
|
|
|
324
331
|
CnWidgetVisibilityRulesModal,
|
|
325
332
|
CnWidgetWrapper,
|
|
326
333
|
CnWizardDialog,
|
|
334
|
+
CnWorkspaceFilterWidget,
|
|
327
335
|
DEFAULT_DATE_RANGE_PRESETS,
|
|
328
336
|
defaultPageTypes,
|
|
329
337
|
fetchWebdavFolderTree,
|