@conduction/nextcloud-vue 2.114.0 → 2.115.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/CnAdvancedFormDialog/CnPropertyValueCell.vue.js +1 -1
- package/dist/esm/components/CnAdvancedFormDialog/CnPropertyValueCell.vue2.js +1 -1
- package/dist/esm/components/CnAdvancedFormDialog/CnPropertyValueCell.vue2.js.map +1 -1
- package/dist/esm/components/CnAdvancedFormDialog/CnPropertyValueCell.vue3.js.map +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue.js +1 -1
- package/dist/esm/components/CnCellRenderer/CnCellRenderer.vue2.js +6 -2
- 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/CnChoiceCards/CnChoiceCards.vue.js +1 -1
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue2.js +2 -1
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue2.js.map +1 -1
- package/dist/esm/components/CnChoiceCards/CnChoiceCards.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +19 -2
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +22 -3
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue.js +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js +2 -2
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue2.js.map +1 -1
- package/dist/esm/components/CnDetailWidgetHost/CnDetailWidgetHost.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +37 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/rowsToCsv.js +90 -0
- package/dist/esm/components/CnIndexPage/rowsToCsv.js.map +1 -0
- package/dist/esm/components/CnMacrosWidget/CnMacrosWidget.vue.js +9 -0
- package/dist/esm/components/CnMacrosWidget/CnMacrosWidget.vue.js.map +1 -0
- package/dist/esm/components/CnMacrosWidget/CnMacrosWidget.vue2.js +161 -0
- package/dist/esm/components/CnMacrosWidget/CnMacrosWidget.vue2.js.map +1 -0
- package/dist/esm/components/CnMacrosWidget/CnMacrosWidget.vue3.js +97 -0
- package/dist/esm/components/CnMacrosWidget/CnMacrosWidget.vue3.js.map +1 -0
- package/dist/esm/components/CnRetentionWidget/CnRetentionRecomputeDialog.vue.js +9 -0
- package/dist/esm/components/CnRetentionWidget/CnRetentionRecomputeDialog.vue.js.map +1 -0
- package/dist/esm/components/CnRetentionWidget/CnRetentionRecomputeDialog.vue2.js +63 -0
- package/dist/esm/components/CnRetentionWidget/CnRetentionRecomputeDialog.vue2.js.map +1 -0
- package/dist/esm/components/CnRetentionWidget/CnRetentionRecomputeDialog.vue3.js +54 -0
- package/dist/esm/components/CnRetentionWidget/CnRetentionRecomputeDialog.vue3.js.map +1 -0
- package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue.js +1 -1
- package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue2.js +15 -9
- package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue3.js +55 -63
- package/dist/esm/components/CnRetentionWidget/CnRetentionWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue.js +1 -1
- package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue2.js +2 -1
- package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue2.js.map +1 -1
- package/dist/esm/components/CnSegmentedControl/CnSegmentedControl.vue3.js.map +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue.js +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js +13 -4
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue2.js.map +1 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js +2 -1
- package/dist/esm/components/CnSetupWizard/CnSetupWizard.vue3.js.map +1 -1
- package/dist/esm/components/CnStepper/CnStepper.vue.js +1 -1
- package/dist/esm/components/CnStepper/CnStepper.vue2.js.map +1 -1
- package/dist/esm/components/CnStepper/CnStepper.vue3.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue.js +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js +10 -5
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js +1 -1
- package/dist/esm/components/CnTabsWidget/CnTabsWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue.js +1 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js +2 -2
- package/dist/esm/components/CnTasksWidget/CnTasksWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js +32 -20
- package/dist/esm/components/CnWidgetGrid/registerDashboardWidgets.js.map +1 -1
- package/dist/esm/composables/indexSources.js +43 -11
- package/dist/esm/composables/indexSources.js.map +1 -1
- package/dist/esm/composables/useTaskInboxStore.js +41 -0
- package/dist/esm/composables/useTaskInboxStore.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +4 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +9 -1
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/nextcloud-vue.css +482 -330
- package/dist/esm/utils/contentHeaderLink.js +37 -1
- package/dist/esm/utils/contentHeaderLink.js.map +1 -1
- package/dist/esm/utils/declaredMacros.js +31 -0
- package/dist/esm/utils/declaredMacros.js.map +1 -0
- package/dist/esm/utils/libraryWidgetKeys.js +1 -0
- package/dist/esm/utils/libraryWidgetKeys.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +2673 -1977
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +482 -330
- package/l10n/en.json +4 -1
- package/l10n/nl.json +9 -1
- package/package.json +1 -1
- package/src/components/CnAdvancedFormDialog/CnPropertyValueCell.vue +1 -1
- package/src/components/CnCellRenderer/CnCellRenderer.vue +6 -2
- package/src/components/CnChoiceCards/CnChoiceCards.vue +2 -1
- package/src/components/CnDashboardPage/CnDashboardPage.vue +45 -3
- package/src/components/CnDetailWidgetHost/CnDetailWidgetHost.vue +2 -2
- package/src/components/CnIndexPage/CnIndexPage.vue +37 -1
- package/src/components/CnIndexPage/rowsToCsv.js +87 -0
- package/src/components/CnMacrosWidget/CnMacrosWidget.md +30 -0
- package/src/components/CnMacrosWidget/CnMacrosWidget.vue +266 -0
- package/src/components/CnMacrosWidget/index.js +6 -0
- package/src/components/CnRetentionWidget/CnRetentionRecomputeDialog.vue +104 -0
- package/src/components/CnRetentionWidget/CnRetentionWidget.vue +53 -38
- package/src/components/CnSegmentedControl/CnSegmentedControl.vue +2 -1
- package/src/components/CnSetupWizard/CnSetupWizard.vue +28 -4
- package/src/components/CnStepper/CnStepper.vue +2 -1
- package/src/components/CnTabsWidget/CnTabsWidget.vue +11 -6
- package/src/components/CnTasksWidget/CnTasksWidget.vue +2 -2
- package/src/components/CnWidgetGrid/registerDashboardWidgets.js +12 -0
- package/src/components/index.js +1 -0
- package/src/composables/indexSources.js +43 -11
- package/src/composables/useTaskInboxStore.js +41 -0
- package/src/css/look-board-detail.css +4 -3
- package/src/css/look-board-index.css +62 -0
- package/src/css/look-board.css +20 -0
- package/src/index.js +1 -0
- package/src/utils/contentHeaderLink.js +35 -0
- package/src/utils/declaredMacros.js +28 -0
- package/src/utils/libraryWidgetKeys.js +1 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
- SPDX-FileCopyrightText: 2026 Conduction B.V.
|
|
3
|
+
- SPDX-License-Identifier: EUPL-1.2
|
|
4
|
+
-->
|
|
5
|
+
<template>
|
|
6
|
+
<CnDialog
|
|
7
|
+
:name="tr('Recompute the nomination')"
|
|
8
|
+
width="confirm"
|
|
9
|
+
:noClose="busy"
|
|
10
|
+
data-testid="cn-retention-recompute-dialog"
|
|
11
|
+
@closing="$emit('close')">
|
|
12
|
+
<NcTextField
|
|
13
|
+
:modelValue="reason"
|
|
14
|
+
:label="tr('Why are you recomputing this?')"
|
|
15
|
+
data-testid="cn-retention-reason"
|
|
16
|
+
@update:modelValue="(value) => (reason = String(value ?? ''))" />
|
|
17
|
+
<p class="cn-retention-recompute__muted">
|
|
18
|
+
{{ recordedAgainst }}
|
|
19
|
+
</p>
|
|
20
|
+
<template #actions>
|
|
21
|
+
<NcButton :disabled="busy" data-testid="cn-retention-recompute-cancel" @click="$emit('close')">
|
|
22
|
+
{{ tr('Cancel') }}
|
|
23
|
+
</NcButton>
|
|
24
|
+
<NcButton
|
|
25
|
+
variant="primary"
|
|
26
|
+
:disabled="busy || reason.trim() === ''"
|
|
27
|
+
data-testid="cn-retention-recompute"
|
|
28
|
+
@click="$emit('confirm', reason.trim())">
|
|
29
|
+
{{ tr('Recompute') }}
|
|
30
|
+
</NcButton>
|
|
31
|
+
</template>
|
|
32
|
+
</CnDialog>
|
|
33
|
+
</template>
|
|
34
|
+
|
|
35
|
+
<script>
|
|
36
|
+
import { translate as t } from '@nextcloud/l10n'
|
|
37
|
+
import { NcButton, NcTextField } from '@nextcloud/vue'
|
|
38
|
+
import { CnDialog } from '../CnDialog/index.js'
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* CnRetentionRecomputeDialog — asks why a record's archival nomination is
|
|
42
|
+
* recomputed, the reason OpenRegister records with it. The retention widget
|
|
43
|
+
* opens it from its card header (board DqZaakArchivering, "Nominatie
|
|
44
|
+
* herberekenen") and posts the recompute itself.
|
|
45
|
+
*
|
|
46
|
+
* @event close The dialog is closed without recomputing.
|
|
47
|
+
* @event confirm The person confirmed, with the reason (trimmed, never empty).
|
|
48
|
+
* @spec openspec/changes/retention-recompute-header/specs/retention-recompute-header/spec.md#requirement-recomputing-the-nomination-starts-in-the-card-header-and-asks-for-a-reason
|
|
49
|
+
*/
|
|
50
|
+
export default {
|
|
51
|
+
name: 'CnRetentionRecomputeDialog',
|
|
52
|
+
|
|
53
|
+
components: { CnDialog, NcButton, NcTextField },
|
|
54
|
+
|
|
55
|
+
inject: {
|
|
56
|
+
/** The host app's translate (CnAppRoot `cnTranslate`). */
|
|
57
|
+
cnTranslate: { default: () => (key) => key },
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
props: {
|
|
61
|
+
/** The user the recompute is recorded against. */
|
|
62
|
+
user: { type: String, default: '' },
|
|
63
|
+
/** Whether the recompute is being posted. */
|
|
64
|
+
busy: { type: Boolean, default: false },
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
emits: ['close', 'confirm'],
|
|
68
|
+
|
|
69
|
+
data() {
|
|
70
|
+
return { reason: '' }
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
computed: {
|
|
74
|
+
/**
|
|
75
|
+
* The line naming who the recompute is recorded against.
|
|
76
|
+
*
|
|
77
|
+
* @return {string} The line.
|
|
78
|
+
*/
|
|
79
|
+
recordedAgainst() {
|
|
80
|
+
return t('nextcloud-vue', 'This is recorded against {user}.', { user: this.user })
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
methods: {
|
|
85
|
+
/**
|
|
86
|
+
* A library string in the reader's language.
|
|
87
|
+
*
|
|
88
|
+
* @param {string} key The English text.
|
|
89
|
+
* @return {string} The text.
|
|
90
|
+
*/
|
|
91
|
+
tr(key) {
|
|
92
|
+
return t('nextcloud-vue', key)
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
}
|
|
96
|
+
</script>
|
|
97
|
+
|
|
98
|
+
<style scoped>
|
|
99
|
+
.cn-retention-recompute__muted {
|
|
100
|
+
margin: 0;
|
|
101
|
+
color: var(--color-text-maxcontrast);
|
|
102
|
+
font-size: 14px;
|
|
103
|
+
}
|
|
104
|
+
</style>
|
|
@@ -5,13 +5,35 @@
|
|
|
5
5
|
<template>
|
|
6
6
|
<div class="cn-retention" data-testid="cn-retention">
|
|
7
7
|
<header class="cn-retention__head">
|
|
8
|
-
<
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
<div class="cn-retention__head-text">
|
|
9
|
+
<h3 class="cn-retention__title">
|
|
10
|
+
{{ text(content.title) || t('nextcloud-vue', 'Archiving') }}
|
|
11
|
+
</h3>
|
|
12
|
+
<p class="cn-retention__muted">
|
|
13
|
+
{{ text(content.subtitle) || t('nextcloud-vue', 'What happens to this record once it is closed') }}
|
|
14
|
+
</p>
|
|
15
|
+
</div>
|
|
16
|
+
<!-- Recompute starts in the header (board DqZaakArchivering); the
|
|
17
|
+
dialog asks for the reason OpenRegister records with it. -->
|
|
18
|
+
<NcButton
|
|
19
|
+
v-if="content.recompute !== false && !loading && !error"
|
|
20
|
+
:disabled="!mayRecompute || busy"
|
|
21
|
+
:title="mayRecompute ? undefined : t('nextcloud-vue', 'Recomputing needs the archivist role')"
|
|
22
|
+
:aria-description="mayRecompute ? undefined : t('nextcloud-vue', 'Recomputing needs the archivist role')"
|
|
23
|
+
:data-testid="mayRecompute ? 'cn-retention-recompute-open' : 'cn-retention-recompute-denied'"
|
|
24
|
+
@click="recomputeOpen = true">
|
|
25
|
+
<template #icon>
|
|
26
|
+
<Refresh :size="20" />
|
|
27
|
+
</template>
|
|
28
|
+
{{ t('nextcloud-vue', 'Recompute nomination') }}
|
|
29
|
+
</NcButton>
|
|
14
30
|
</header>
|
|
31
|
+
<CnRetentionRecomputeDialog
|
|
32
|
+
v-if="recomputeOpen"
|
|
33
|
+
:user="currentUid"
|
|
34
|
+
:busy="busy"
|
|
35
|
+
@close="recomputeOpen = false"
|
|
36
|
+
@confirm="recompute" />
|
|
15
37
|
|
|
16
38
|
<NcLoadingIcon v-if="loading" :size="24" />
|
|
17
39
|
<NcEmptyContent v-else-if="error"
|
|
@@ -65,23 +87,6 @@
|
|
|
65
87
|
</p>
|
|
66
88
|
</div>
|
|
67
89
|
|
|
68
|
-
<div v-if="content.recompute !== false" class="cn-retention__recompute">
|
|
69
|
-
<NcButton v-if="!mayRecompute" disabled data-testid="cn-retention-recompute-denied">
|
|
70
|
-
{{ t('nextcloud-vue', 'Recomputing needs the archivist role') }}
|
|
71
|
-
</NcButton>
|
|
72
|
-
<template v-else>
|
|
73
|
-
<NcTextField :modelValue="reason"
|
|
74
|
-
:label="t('nextcloud-vue', 'Why are you recomputing this?')"
|
|
75
|
-
data-testid="cn-retention-reason"
|
|
76
|
-
@update:modelValue="(v) => (reason = String(v ?? ''))" />
|
|
77
|
-
<p class="cn-retention__muted">
|
|
78
|
-
{{ t('nextcloud-vue', 'This is recorded against {user}.', { user: currentUid }) }}
|
|
79
|
-
</p>
|
|
80
|
-
<NcButton :disabled="busy || reason.trim() === ''" data-testid="cn-retention-recompute" @click="recompute">
|
|
81
|
-
{{ t('nextcloud-vue', 'Recompute the nomination') }}
|
|
82
|
-
</NcButton>
|
|
83
|
-
</template>
|
|
84
|
-
</div>
|
|
85
90
|
</template>
|
|
86
91
|
</div>
|
|
87
92
|
</template>
|
|
@@ -91,7 +96,9 @@ import { getCurrentUser } from '@nextcloud/auth'
|
|
|
91
96
|
import axios from '@nextcloud/axios'
|
|
92
97
|
import { translate as t } from '@nextcloud/l10n'
|
|
93
98
|
import { generateUrl } from '@nextcloud/router'
|
|
94
|
-
import { NcButton, NcEmptyContent, NcLoadingIcon, NcNoteCard
|
|
99
|
+
import { NcButton, NcEmptyContent, NcLoadingIcon, NcNoteCard } from '@nextcloud/vue'
|
|
100
|
+
import Refresh from 'vue-material-design-icons/Refresh.vue'
|
|
101
|
+
import CnRetentionRecomputeDialog from './CnRetentionRecomputeDialog.vue'
|
|
95
102
|
import { readPath } from '../../utils/readPath.js'
|
|
96
103
|
|
|
97
104
|
/**
|
|
@@ -120,7 +127,7 @@ import { readPath } from '../../utils/readPath.js'
|
|
|
120
127
|
export default {
|
|
121
128
|
name: 'CnRetentionWidget',
|
|
122
129
|
|
|
123
|
-
components: { NcButton, NcEmptyContent, NcLoadingIcon, NcNoteCard,
|
|
130
|
+
components: { CnRetentionRecomputeDialog, NcButton, NcEmptyContent, NcLoadingIcon, NcNoteCard, Refresh },
|
|
124
131
|
|
|
125
132
|
inject: {
|
|
126
133
|
/** The host app's translate (CnAppRoot `cnTranslate`). */
|
|
@@ -176,7 +183,8 @@ export default {
|
|
|
176
183
|
loading: true,
|
|
177
184
|
error: '',
|
|
178
185
|
record: null,
|
|
179
|
-
|
|
186
|
+
/** Whether the recompute dialog is open. */
|
|
187
|
+
recomputeOpen: false,
|
|
180
188
|
busy: false,
|
|
181
189
|
}
|
|
182
190
|
},
|
|
@@ -338,19 +346,22 @@ export default {
|
|
|
338
346
|
},
|
|
339
347
|
|
|
340
348
|
/**
|
|
341
|
-
* Ask OpenRegister to derive the nomination again
|
|
349
|
+
* Ask OpenRegister to derive the nomination again with the reason the
|
|
350
|
+
* dialog asked for, then read it back.
|
|
342
351
|
*
|
|
343
|
-
* @spec openspec/changes/retention-
|
|
352
|
+
* @spec openspec/changes/retention-recompute-header/specs/retention-recompute-header/spec.md#requirement-recomputing-the-nomination-starts-in-the-card-header-and-asks-for-a-reason
|
|
353
|
+
* @param {string} reason Why it is recomputed.
|
|
344
354
|
* @return {Promise<void>}
|
|
345
355
|
*/
|
|
346
|
-
async recompute() {
|
|
347
|
-
|
|
356
|
+
async recompute(reason) {
|
|
357
|
+
const why = String(reason ?? '').trim()
|
|
358
|
+
if (this.busy || why === '') {
|
|
348
359
|
return
|
|
349
360
|
}
|
|
350
361
|
this.busy = true
|
|
351
362
|
try {
|
|
352
|
-
await axios.post(generateUrl(`/apps/openregister/api/archival/objects/${encodeURIComponent(this.id)}/nomination/recompute`), { reason:
|
|
353
|
-
this.
|
|
363
|
+
await axios.post(generateUrl(`/apps/openregister/api/archival/objects/${encodeURIComponent(this.id)}/nomination/recompute`), { reason: why })
|
|
364
|
+
this.recomputeOpen = false
|
|
354
365
|
await this.load()
|
|
355
366
|
} catch (failure) {
|
|
356
367
|
const body = failure && failure.response && failure.response.data
|
|
@@ -372,8 +383,18 @@ export default {
|
|
|
372
383
|
|
|
373
384
|
.cn-retention__head {
|
|
374
385
|
display: flex;
|
|
386
|
+
flex-wrap: wrap;
|
|
387
|
+
align-items: flex-start;
|
|
388
|
+
justify-content: space-between;
|
|
389
|
+
gap: 10px 16px;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.cn-retention__head-text {
|
|
393
|
+
display: flex;
|
|
394
|
+
flex: 1 1 260px;
|
|
375
395
|
flex-direction: column;
|
|
376
396
|
gap: 2px;
|
|
397
|
+
min-width: 0;
|
|
377
398
|
}
|
|
378
399
|
|
|
379
400
|
.cn-retention__title {
|
|
@@ -404,10 +425,4 @@ export default {
|
|
|
404
425
|
font-weight: 600;
|
|
405
426
|
}
|
|
406
427
|
|
|
407
|
-
.cn-retention__recompute {
|
|
408
|
-
display: flex;
|
|
409
|
-
flex-direction: column;
|
|
410
|
-
gap: 8px;
|
|
411
|
-
max-width: 480px;
|
|
412
|
-
}
|
|
413
428
|
</style>
|
|
@@ -81,7 +81,8 @@ export default {
|
|
|
81
81
|
* @type {string|number|boolean|null}
|
|
82
82
|
*/
|
|
83
83
|
modelValue: {
|
|
84
|
-
type
|
|
84
|
+
// eslint-disable-next-line vue/prefer-prop-type-boolean-first -- an option value of '' must stay a string
|
|
85
|
+
type: [String, Number, Boolean],
|
|
85
86
|
default: null,
|
|
86
87
|
},
|
|
87
88
|
|
|
@@ -78,8 +78,11 @@
|
|
|
78
78
|
<NcNoteCard v-else-if="step.body" type="info">
|
|
79
79
|
{{ stepBody(step) }}
|
|
80
80
|
</NcNoteCard>
|
|
81
|
+
<!-- In the board look the step's title is the dialog's heading,
|
|
82
|
+
so the cards' legend is kept for assistive tech only. -->
|
|
81
83
|
<CnChoiceCards
|
|
82
84
|
v-if="isCardChoice(step)"
|
|
85
|
+
:class="{ 'cn-setup-step__choices--titled': isBoard }"
|
|
83
86
|
:layout="step.display === 'rows' ? 'rows' : 'cards'"
|
|
84
87
|
:label="stepTitle(step)"
|
|
85
88
|
:options="optionsFor(step)"
|
|
@@ -1134,7 +1137,9 @@ export default {
|
|
|
1134
1137
|
/**
|
|
1135
1138
|
* CnWizardDialog `validate` hook — intercepts Next/Submit to persist
|
|
1136
1139
|
* the active step before advancing. `choice`/`config-fields` steps
|
|
1137
|
-
* POST their value via the setup contract
|
|
1140
|
+
* POST their value via the setup contract (a `config-fields` step only
|
|
1141
|
+
* the fields the person filled in, and nothing when they filled in
|
|
1142
|
+
* none); required `choice` steps
|
|
1138
1143
|
* block advance until a value is picked; required `run-action` steps
|
|
1139
1144
|
* block until the action has succeeded. Returns `true` to advance or
|
|
1140
1145
|
* an error string to surface above the step and block navigation.
|
|
@@ -1168,11 +1173,18 @@ export default {
|
|
|
1168
1173
|
}
|
|
1169
1174
|
}
|
|
1170
1175
|
if (step.type === 'config-fields') {
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1176
|
+
// Only what the person filled in is written: a field left alone
|
|
1177
|
+
// keeps what is stored, and a step left alone writes nothing.
|
|
1178
|
+
const patch = {}
|
|
1179
|
+
for (const field of this.fieldsFor(step)) {
|
|
1180
|
+
if (Object.hasOwn(this.configModel, field.key)) {
|
|
1174
1181
|
patch[field.key] = this.configModel[field.key]
|
|
1175
1182
|
}
|
|
1183
|
+
}
|
|
1184
|
+
if (Object.keys(patch).length === 0) {
|
|
1185
|
+
return true
|
|
1186
|
+
}
|
|
1187
|
+
try {
|
|
1176
1188
|
await this.saveConfig(patch)
|
|
1177
1189
|
this.localDone[step.id] = true
|
|
1178
1190
|
return true
|
|
@@ -1457,6 +1469,18 @@ export default {
|
|
|
1457
1469
|
line-height: 1.5;
|
|
1458
1470
|
}
|
|
1459
1471
|
|
|
1472
|
+
.cn-setup-step__choices--titled :deep(.cn-choice-cards__legend) {
|
|
1473
|
+
position: absolute;
|
|
1474
|
+
width: 1px;
|
|
1475
|
+
height: 1px;
|
|
1476
|
+
margin: -1px;
|
|
1477
|
+
padding: 0;
|
|
1478
|
+
overflow: hidden;
|
|
1479
|
+
clip-path: inset(50%);
|
|
1480
|
+
white-space: nowrap;
|
|
1481
|
+
border: 0;
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1460
1484
|
.cn-setup-step__note {
|
|
1461
1485
|
margin: 0;
|
|
1462
1486
|
font-size: 13px;
|
|
@@ -230,13 +230,14 @@ button.cn-stepper__step {
|
|
|
230
230
|
}
|
|
231
231
|
|
|
232
232
|
/* The board look: one row, 28px circles, the label beside the circle. */
|
|
233
|
+
/* The board leaves 20px between the stepper and the step's body (DqInstellen). */
|
|
233
234
|
.cn-stepper--board {
|
|
234
235
|
flex-wrap: wrap;
|
|
235
236
|
align-items: center;
|
|
236
237
|
justify-content: flex-start;
|
|
237
238
|
gap: 8px;
|
|
238
239
|
padding: 0;
|
|
239
|
-
margin: 0;
|
|
240
|
+
margin: 0 0 20px;
|
|
240
241
|
}
|
|
241
242
|
|
|
242
243
|
.cn-stepper--board .cn-stepper__item {
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
<!-- A stack (`widgetIds`): every widget is a titled card; the
|
|
112
112
|
first joins the open tab, the next ones stand below it. -->
|
|
113
113
|
<div
|
|
114
|
-
v-if="entry.widget && entry.
|
|
114
|
+
v-if="entry.widget && entry.isStack"
|
|
115
115
|
class="cn-tabs-widget__stack"
|
|
116
116
|
:class="{ 'cn-tabs-widget__stack--grid': !!entry.grid }"
|
|
117
117
|
data-testid="cn-tabs-widget-stack">
|
|
@@ -211,7 +211,7 @@ import CnIcon from '../CnIcon/CnIcon.vue'
|
|
|
211
211
|
import CnTab from '../CnTabs/CnTab.vue'
|
|
212
212
|
import CnTabs from '../CnTabs/CnTabs.vue'
|
|
213
213
|
import { normalizeLook } from '../../composables/useLook.js'
|
|
214
|
-
import { headerLinkTarget, widgetHeaderLink } from '../../utils/contentHeaderLink.js'
|
|
214
|
+
import { headerLinkTarget, resolveHeaderLink, widgetHeaderLink } from '../../utils/contentHeaderLink.js'
|
|
215
215
|
import { resolveTabCount } from '../../utils/detailActionModel.js'
|
|
216
216
|
import { detailWidgetVisible } from '../../utils/detailWidgetVisibility.js'
|
|
217
217
|
import { fetchListTotal } from '../../utils/fetchFilterCounts.js'
|
|
@@ -653,10 +653,10 @@ export default {
|
|
|
653
653
|
// A stack (`widgetIds`) resolves every id; one that names no
|
|
654
654
|
// widget is skipped. The first that resolves is the panel's.
|
|
655
655
|
const ids = this.tabWidgetIds(tab)
|
|
656
|
-
const
|
|
657
|
-
? ids.map((id) => this.availableWidgets.find((w) => w && w.id === id) || null)
|
|
658
|
-
.filter((w) => w && !this.hiddenByWorkspace(w) && !this.hiddenByRecord(w))
|
|
656
|
+
const named = ids.length > 1
|
|
657
|
+
? ids.map((id) => this.availableWidgets.find((w) => w && w.id === id) || null).filter(Boolean)
|
|
659
658
|
: []
|
|
659
|
+
const widgets = named.filter((w) => !this.hiddenByWorkspace(w) && !this.hiddenByRecord(w))
|
|
660
660
|
const widget = ids.length > 1
|
|
661
661
|
? (widgets[0] || null)
|
|
662
662
|
: (this.availableWidgets.find((w) => w && w.id === ids[0]) || null)
|
|
@@ -687,6 +687,11 @@ export default {
|
|
|
687
687
|
icon: (tab && tab.icon) || widget?.icon || '',
|
|
688
688
|
widget,
|
|
689
689
|
stack: widgets.slice(1),
|
|
690
|
+
// A stack of two or more widgets stays a stack of titled cards
|
|
691
|
+
// when conditions leave one of them showing (DqSociaalDomein
|
|
692
|
+
// "De melding"): the record decides which cards show, not
|
|
693
|
+
// whether the panel is a card.
|
|
694
|
+
isStack: named.length > 1,
|
|
690
695
|
grid: this.stackGrid(tab, widgets),
|
|
691
696
|
count,
|
|
692
697
|
overflow,
|
|
@@ -762,7 +767,7 @@ export default {
|
|
|
762
767
|
* @return {object|null}
|
|
763
768
|
*/
|
|
764
769
|
activeHeaderLink() {
|
|
765
|
-
return widgetHeaderLink(this.activeTab?.widget)
|
|
770
|
+
return resolveHeaderLink(widgetHeaderLink(this.activeTab?.widget), { objectId: this.objectId, object: this.objectData })
|
|
766
771
|
},
|
|
767
772
|
|
|
768
773
|
/**
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
<NcTextField
|
|
31
31
|
:modelValue="newDue"
|
|
32
32
|
type="date"
|
|
33
|
-
:label="tr('
|
|
33
|
+
:label="tr('Task deadline')"
|
|
34
34
|
data-testid="cn-tasks-widget-create-due"
|
|
35
35
|
@update:modelValue="(value) => (newDue = String(value ?? ''))" />
|
|
36
36
|
<NcButton
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
{{ tr('Handler') }}
|
|
55
55
|
</th>
|
|
56
56
|
<th scope="col">
|
|
57
|
-
{{ tr('
|
|
57
|
+
{{ tr('Task deadline') }}
|
|
58
58
|
</th>
|
|
59
59
|
<th scope="col">
|
|
60
60
|
{{ tr('Status') }}
|
|
@@ -22,6 +22,7 @@ import CnConversationWidget from '../CnConversationThread/CnConversationWidget.v
|
|
|
22
22
|
import CnCountdownWidget from '../CnCountdownWidget/CnCountdownWidget.vue'
|
|
23
23
|
import CnCountdownWidgetForm from '../CnCountdownWidgetForm/CnCountdownWidgetForm.vue'
|
|
24
24
|
import CnDocumentReviewWidget from '../CnDocumentReviewList/CnDocumentReviewWidget.vue'
|
|
25
|
+
import CnMacrosWidget from '../CnMacrosWidget/CnMacrosWidget.vue'
|
|
25
26
|
import CnMapWidget from '../CnMapWidget/CnMapWidget.vue'
|
|
26
27
|
import CnMapWidgetForm from '../CnMapWidgetForm/CnMapWidgetForm.vue'
|
|
27
28
|
import CnMarkdownWidget2 from '../CnMarkdownWidget/CnMarkdownWidget.vue'
|
|
@@ -345,6 +346,17 @@ registerDashboardWidget('ai-chat', {
|
|
|
345
346
|
ownsTitle: true,
|
|
346
347
|
})
|
|
347
348
|
|
|
349
|
+
// The macros the record's schema declares, each run by a button
|
|
350
|
+
// (DqZaakAssistent "Macro's").
|
|
351
|
+
registerDashboardWidget('macros', {
|
|
352
|
+
renderer: CnMacrosWidget,
|
|
353
|
+
form: null,
|
|
354
|
+
defaultContent: { description: '', emptyText: '' },
|
|
355
|
+
displayName: 'Macros',
|
|
356
|
+
icon: 'PlayBoxMultipleOutline',
|
|
357
|
+
surfaces: ['detail-page'],
|
|
358
|
+
})
|
|
359
|
+
|
|
348
360
|
registerDashboardWidget('conversation', {
|
|
349
361
|
renderer: CnConversationWidget,
|
|
350
362
|
form: null,
|
package/src/components/index.js
CHANGED
|
@@ -256,6 +256,7 @@ export { CnChoicePickerWidget } from './CnChoicePickerWidget/index.js'
|
|
|
256
256
|
export { CnRecordListWidget } from './CnRecordListWidget/index.js'
|
|
257
257
|
export { CnBannerWidget } from './CnBannerWidget/index.js'
|
|
258
258
|
export { default as CnConversationWidget } from './CnConversationThread/CnConversationWidget.vue'
|
|
259
|
+
export { CnMacrosWidget } from './CnMacrosWidget/index.js'
|
|
259
260
|
export { default as CnDocumentReviewWidget } from './CnDocumentReviewList/CnDocumentReviewWidget.vue'
|
|
260
261
|
export { default as CnNextStepWidget } from './CnNextStepCard/CnNextStepWidget.vue'
|
|
261
262
|
export { default as CnAuditTrailWidget } from './CnAuditTrailWidget/CnAuditTrailWidget.vue'
|
|
@@ -497,6 +497,23 @@ function tasksSource() {
|
|
|
497
497
|
[priorityLabels.urgent]: 'error',
|
|
498
498
|
}
|
|
499
499
|
|
|
500
|
+
/**
|
|
501
|
+
* A task with the display fields a list row and a detail page read. Keyed
|
|
502
|
+
* on its uuid, the identity of the task contract.
|
|
503
|
+
*
|
|
504
|
+
* @param {object} task The task as the engine returned it.
|
|
505
|
+
* @return {object} The task with `id`, `title` and the labels.
|
|
506
|
+
*/
|
|
507
|
+
const taskRow = (task) => ({
|
|
508
|
+
...task,
|
|
509
|
+
id: task.uuid,
|
|
510
|
+
title: task.displayTitle || task.title || task.uuid,
|
|
511
|
+
subjectLabel: (task.subject && task.subject.title) || '',
|
|
512
|
+
stateLabel: stateLabels[task.state] || String(task.state || ''),
|
|
513
|
+
priorityLabel: priorityLabels[task.priority] || String(task.priority || ''),
|
|
514
|
+
dueLabel: taskDueLabel(task),
|
|
515
|
+
})
|
|
516
|
+
|
|
500
517
|
return {
|
|
501
518
|
store,
|
|
502
519
|
|
|
@@ -513,17 +530,32 @@ function tasksSource() {
|
|
|
513
530
|
/**
|
|
514
531
|
* @return {Array<object>} The rows, with the display fields resolved.
|
|
515
532
|
*/
|
|
516
|
-
rows: () => (store.tasks || []).map(
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
533
|
+
rows: () => (store.tasks || []).map(taskRow),
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Read one task for a detail page (`type: "detail"` with
|
|
537
|
+
* `entitySource: "tasks"`).
|
|
538
|
+
*
|
|
539
|
+
* @param {string} id The task uuid.
|
|
540
|
+
* @return {Promise<object|null>} The task, or null when it could not be read.
|
|
541
|
+
* @spec openspec/changes/tasks-record-source/specs/tasks-record-source/spec.md#requirement-the-tasks-source-reads-one-task
|
|
542
|
+
*/
|
|
543
|
+
loadRecord: (id) => store.fetchRecord(id),
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* @param {string} id The task uuid.
|
|
547
|
+
* @return {object|null} The task read for this id, with the row's display fields, or null.
|
|
548
|
+
*/
|
|
549
|
+
record: (id) => {
|
|
550
|
+
const task = store.records?.[String(id)]
|
|
551
|
+
return task ? taskRow(task) : null
|
|
552
|
+
},
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* @param {string} id The task uuid.
|
|
556
|
+
* @return {number|null} The HTTP status of a failed read of this id, or null.
|
|
557
|
+
*/
|
|
558
|
+
recordError: (id) => store.recordErrors?.[String(id)] ?? null,
|
|
527
559
|
|
|
528
560
|
/**
|
|
529
561
|
* @return {boolean} Whether a load is in flight.
|
|
@@ -118,9 +118,50 @@ export const useTaskInboxStore = defineStore('cnTaskInbox', {
|
|
|
118
118
|
loading: false,
|
|
119
119
|
/** @type {string|null} The last load failure, for the console trail. */
|
|
120
120
|
error: null,
|
|
121
|
+
/** @type {Object<string, object>} Single tasks read for a detail page, keyed on their uuid. */
|
|
122
|
+
records: {},
|
|
123
|
+
/** @type {Object<string, number|null>} The HTTP status of a failed single read, keyed on the uuid. */
|
|
124
|
+
recordErrors: {},
|
|
121
125
|
}),
|
|
122
126
|
|
|
123
127
|
actions: {
|
|
128
|
+
/**
|
|
129
|
+
* Read one task into `records`, for a `type: "detail"` page on the
|
|
130
|
+
* `tasks` source. A refusal (403) or a missing task (404) leaves no
|
|
131
|
+
* record and keeps the status in `recordErrors`, so the page can tell
|
|
132
|
+
* "not found" from "still loading". The engine decides what the caller
|
|
133
|
+
* may read; nothing here widens that.
|
|
134
|
+
*
|
|
135
|
+
* @param {string} uuid The task uuid.
|
|
136
|
+
* @return {Promise<object|null>} The task, or null when it could not be read.
|
|
137
|
+
* @spec openspec/changes/tasks-record-source/specs/tasks-record-source/spec.md#requirement-the-tasks-source-reads-one-task
|
|
138
|
+
*/
|
|
139
|
+
async fetchRecord(uuid) {
|
|
140
|
+
const key = String(uuid || '').trim()
|
|
141
|
+
if (key === '') {
|
|
142
|
+
return null
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
const response = await axios.get(generateUrl(`${FLOW_TASKS_URL}/${encodeURIComponent(key)}`))
|
|
146
|
+
const data = response.data
|
|
147
|
+
const task = data && typeof data === 'object' && data.results && !Array.isArray(data.results) ? data.results : data
|
|
148
|
+
const { [key]: dropped, ...errors } = this.recordErrors
|
|
149
|
+
this.recordErrors = errors
|
|
150
|
+
if (!task || typeof task !== 'object' || Array.isArray(task)) {
|
|
151
|
+
return null
|
|
152
|
+
}
|
|
153
|
+
this.records = { ...this.records, [key]: task }
|
|
154
|
+
return task
|
|
155
|
+
} catch (error) {
|
|
156
|
+
const { [key]: dropped, ...records } = this.records
|
|
157
|
+
this.records = records
|
|
158
|
+
this.recordErrors = { ...this.recordErrors, [key]: error?.response?.status ?? null }
|
|
159
|
+
// eslint-disable-next-line no-console
|
|
160
|
+
console.error(`[useTaskInboxStore] Loading task "${key}" failed`, error)
|
|
161
|
+
return null
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
|
|
124
165
|
/**
|
|
125
166
|
* Read one inbox page WITHOUT touching the store's state, so a surface
|
|
126
167
|
* that lists a different slice (the Tasks tab of one record) cannot
|
|
@@ -932,9 +932,10 @@
|
|
|
932
932
|
/* ---- The archiving card (CnRetentionWidget, DqZaakArchivering) ----------- */
|
|
933
933
|
/* The board titles it 17px at 1.2 with a 14px muted line 3px under it, and
|
|
934
934
|
draws each fact as a 13px label over a 15px value at 500 above a hairline.
|
|
935
|
-
|
|
935
|
+
"Recompute nomination" sits at the header's end. The inset is the board
|
|
936
|
+
card's own, from the panel around it.
|
|
936
937
|
@spec openspec/changes/board-retention-card/specs/board-retention-card/spec.md#requirement-the-archiving-card-is-drawn-as-the-board-draws-it */
|
|
937
|
-
.cn-look-board .cn-retention .cn-retention__head {
|
|
938
|
+
.cn-look-board .cn-retention .cn-retention__head-text {
|
|
938
939
|
gap: 3px;
|
|
939
940
|
}
|
|
940
941
|
|
|
@@ -943,7 +944,7 @@
|
|
|
943
944
|
line-height: 1.2;
|
|
944
945
|
}
|
|
945
946
|
|
|
946
|
-
.cn-look-board .cn-retention .cn-retention__head .cn-retention__muted {
|
|
947
|
+
.cn-look-board .cn-retention .cn-retention__head-text .cn-retention__muted {
|
|
947
948
|
font-size: 14px;
|
|
948
949
|
}
|
|
949
950
|
|
|
@@ -43,6 +43,19 @@
|
|
|
43
43
|
color: var(--color-text-maxcontrast);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
/* The breadcrumb row stays under the title, in the title's column, with a
|
|
47
|
+
count line below it. Unplaced, the trail took the next free cell: beside
|
|
48
|
+
the count line, under the header buttons (DqZaaktypen).
|
|
49
|
+
@spec openspec/changes/index-count-subtitle-keeps-crumb-row/specs/index-count-subtitle-keeps-crumb-row/spec.md#requirement-the-breadcrumb-row-stays-under-the-title */
|
|
50
|
+
.cn-look-board .cn-index-page .cn-page-header .cn-header-trail {
|
|
51
|
+
grid-column: 1;
|
|
52
|
+
grid-row: 2;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.cn-look-board .cn-index-page .cn-page-header .cn-header-trail ~ .cn-page-header__description {
|
|
56
|
+
grid-row: 3;
|
|
57
|
+
}
|
|
58
|
+
|
|
46
59
|
.cn-look-board .cn-index-page .cn-page-header .cn-index-page__header-buttons {
|
|
47
60
|
grid-column: 2;
|
|
48
61
|
grid-row: 1;
|
|
@@ -1285,6 +1298,55 @@
|
|
|
1285
1298
|
font-weight: 600;
|
|
1286
1299
|
}
|
|
1287
1300
|
|
|
1301
|
+
/* The folder rows as the board draws them (DqAfdelingen), like a tree menu's
|
|
1302
|
+
folder rows: 8px 10px, an 8px corner, 14px at 400 without a folder icon,
|
|
1303
|
+
each level 18px further in inside the row so the highlight spans the pane,
|
|
1304
|
+
the open folder on the primary tint in the deep primary at 700. The weight
|
|
1305
|
+
goes through the theme's button token too: a row is a button, and the
|
|
1306
|
+
nldesign theme sets every button's weight with `!important`.
|
|
1307
|
+
@spec openspec/changes/board-folder-pane-rows/specs/board-folder-pane-rows/spec.md#requirement-a-board-folder-pane-draws-the-boards-folder-rows */
|
|
1308
|
+
.cn-look-board .cn-index-page__folder-pane .cn-folder-tree__children {
|
|
1309
|
+
padding-inline-start: 0;
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
.cn-look-board .cn-index-page__folder-pane .cn-folder-tree__item,
|
|
1313
|
+
.cn-look-board .cn-index-page__folder-pane .cn-folder-sidebar__all {
|
|
1314
|
+
--nldesign-component-button-font-weight: 400;
|
|
1315
|
+
min-height: 36px;
|
|
1316
|
+
margin: 0;
|
|
1317
|
+
padding: 8px 10px;
|
|
1318
|
+
border-radius: 8px;
|
|
1319
|
+
color: var(--color-main-text);
|
|
1320
|
+
font-size: 14px;
|
|
1321
|
+
font-weight: 400;
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
.cn-look-board .cn-index-page__folder-pane .cn-folder-tree__children .cn-folder-tree__item {
|
|
1325
|
+
padding-inline-start: 28px;
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
.cn-look-board .cn-index-page__folder-pane .cn-folder-tree__children .cn-folder-tree__children .cn-folder-tree__item {
|
|
1329
|
+
padding-inline-start: 46px;
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
.cn-look-board .cn-index-page__folder-pane .cn-folder-tree__children .cn-folder-tree__children .cn-folder-tree__children .cn-folder-tree__item {
|
|
1333
|
+
padding-inline-start: 64px;
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
.cn-look-board .cn-index-page__folder-pane .cn-folder-tree__item > :not(.cn-folder-tree__name):not(.cn-folder-tree__count),
|
|
1337
|
+
.cn-look-board .cn-index-page__folder-pane .cn-folder-sidebar__all > :not(.cn-folder-sidebar__all-label) {
|
|
1338
|
+
display: none;
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
.cn-look-board .cn-index-page__folder-pane .cn-folder-tree__item--active,
|
|
1342
|
+
.cn-look-board .cn-index-page__folder-pane .cn-folder-sidebar__all--active {
|
|
1343
|
+
--nldesign-component-button-font-weight: 700;
|
|
1344
|
+
--nldesign-color-on-surface: var(--color-primary-element-light-text);
|
|
1345
|
+
background-color: var(--color-primary-element-light);
|
|
1346
|
+
color: var(--color-primary-element-light-text);
|
|
1347
|
+
font-weight: 700;
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1288
1350
|
/* The selected folder's heading above the list: a 20px title over a soft line. */
|
|
1289
1351
|
.cn-look-board .cn-index-page__scope-heading {
|
|
1290
1352
|
margin-bottom: 16px;
|