@conduction/nextcloud-vue 2.106.0 → 2.107.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/CnActionDialog/CnActionDialog.vue.js +9 -0
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue.js.map +1 -0
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js +338 -0
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js.map +1 -0
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js +249 -0
- package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js.map +1 -0
- package/dist/esm/components/CnAdvancedFormDialog/CnPropertiesTab.vue2.js +2 -2
- package/dist/esm/components/CnAdvancedFormDialog/CnPropertiesTab.vue2.js.map +1 -1
- package/dist/esm/components/CnAppNav/CnAppNav.vue2.js.map +1 -1
- package/dist/esm/components/CnAppNav/CnAppNav.vue3.js +145 -138
- package/dist/esm/components/CnAppNav/CnAppNav.vue3.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +102 -2
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
- package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +83 -6
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +4 -2
- package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIcon/semanticIcons.js +2 -2
- package/dist/esm/components/CnIcon/semanticIcons.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +389 -20
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +68 -35
- package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
- package/dist/esm/components/CnIndexPage/useSelfFetchList.js +22 -2
- package/dist/esm/components/CnIndexPage/useSelfFetchList.js.map +1 -1
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js +69 -5
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js.map +1 -1
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js +38 -5
- package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js.map +1 -1
- package/dist/esm/components/CnMapWidget/CnMapWidget.vue.js +1 -1
- package/dist/esm/components/CnMapWidget/CnMapWidget.vue2.js +7 -1
- package/dist/esm/components/CnMapWidget/CnMapWidget.vue2.js.map +1 -1
- package/dist/esm/components/CnMapWidget/CnMapWidget.vue3.js.map +1 -1
- package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue.js +8 -0
- package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue.js.map +1 -0
- package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue2.js +297 -0
- package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue2.js.map +1 -0
- package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue3.js +24 -0
- package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue3.js.map +1 -0
- package/dist/esm/components/CnSavedViewEditDialog/CnSavedViewEditDialog.vue.js +9 -0
- package/dist/esm/components/CnSavedViewEditDialog/CnSavedViewEditDialog.vue.js.map +1 -0
- package/dist/esm/components/CnSavedViewEditDialog/CnSavedViewEditDialog.vue2.js +95 -0
- package/dist/esm/components/CnSavedViewEditDialog/CnSavedViewEditDialog.vue2.js.map +1 -0
- package/dist/esm/components/CnSavedViewEditDialog/CnSavedViewEditDialog.vue3.js +123 -0
- package/dist/esm/components/CnSavedViewEditDialog/CnSavedViewEditDialog.vue3.js.map +1 -0
- package/dist/esm/components/CnSavedViewShareDialog/CnSavedViewShareDialog.vue.js +1 -1
- package/dist/esm/components/CnSavedViewShareDialog/CnSavedViewShareDialog.vue2.js +13 -5
- package/dist/esm/components/CnSavedViewShareDialog/CnSavedViewShareDialog.vue2.js.map +1 -1
- package/dist/esm/components/CnSavedViewShareDialog/CnSavedViewShareDialog.vue3.js +3 -1
- package/dist/esm/components/CnSavedViewShareDialog/CnSavedViewShareDialog.vue3.js.map +1 -1
- package/dist/esm/components/CnSavedViewShareFields/CnSavedViewShareFields.vue.js +1 -1
- package/dist/esm/components/CnSavedViewShareFields/CnSavedViewShareFields.vue2.js +49 -23
- package/dist/esm/components/CnSavedViewShareFields/CnSavedViewShareFields.vue2.js.map +1 -1
- package/dist/esm/components/CnSavedViewShareFields/CnSavedViewShareFields.vue3.js +74 -27
- package/dist/esm/components/CnSavedViewShareFields/CnSavedViewShareFields.vue3.js.map +1 -1
- package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue.js +0 -1
- package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue.js.map +1 -1
- package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue2.js +18 -413
- package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue2.js.map +1 -1
- package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue3.js +27 -214
- package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue3.js.map +1 -1
- package/dist/esm/components/CnSavedViewsPanel/CnSavedViewsPanel.vue.js +9 -0
- package/dist/esm/components/CnSavedViewsPanel/CnSavedViewsPanel.vue.js.map +1 -0
- package/dist/esm/components/CnSavedViewsPanel/CnSavedViewsPanel.vue2.js +190 -0
- package/dist/esm/components/CnSavedViewsPanel/CnSavedViewsPanel.vue2.js.map +1 -0
- package/dist/esm/components/CnSavedViewsPanel/CnSavedViewsPanel.vue3.js +270 -0
- package/dist/esm/components/CnSavedViewsPanel/CnSavedViewsPanel.vue3.js.map +1 -0
- package/dist/esm/index.js +4 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/l10n/en.json.js +56 -1
- package/dist/esm/l10n/en.json.js.map +1 -1
- package/dist/esm/l10n/nl.json.js +56 -1
- package/dist/esm/l10n/nl.json.js.map +1 -1
- package/dist/esm/library-style.js +1 -1
- package/dist/esm/mixins/savedViewsList.js +235 -0
- package/dist/esm/mixins/savedViewsList.js.map +1 -0
- package/dist/esm/nextcloud-vue.css +348 -108
- package/dist/esm/utils/actionsDispatcher.js +2 -2
- package/dist/esm/utils/actionsDispatcher.js.map +1 -1
- package/dist/esm/utils/builtInModals.js +39 -0
- package/dist/esm/utils/builtInModals.js.map +1 -0
- package/dist/esm/utils/mapMarkerColor.js +121 -0
- package/dist/esm/utils/mapMarkerColor.js.map +1 -0
- package/dist/esm/utils/mergeManifestDelta.js +4 -0
- package/dist/esm/utils/mergeManifestDelta.js.map +1 -1
- package/dist/esm/utils/savedViewHelpers.js +59 -7
- package/dist/esm/utils/savedViewHelpers.js.map +1 -1
- package/dist/esm/utils/savedViewMenu.js +154 -0
- package/dist/esm/utils/savedViewMenu.js.map +1 -0
- package/dist/esm/utils/searchGroupSharees.js +50 -15
- package/dist/esm/utils/searchGroupSharees.js.map +1 -1
- package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
- package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
- package/dist/nextcloud-vue.cjs.js +13351 -10855
- package/dist/nextcloud-vue.cjs.js.map +2 -2
- package/dist/nextcloud-vue.css +348 -108
- package/l10n/en.json +56 -1
- package/l10n/nl.json +56 -1
- package/package.json +1 -1
- package/src/components/CnActionDialog/CnActionDialog.vue +545 -0
- package/src/components/CnActionDialog/index.js +12 -0
- package/src/components/CnAppNav/CnAppNav.vue +17 -0
- package/src/components/CnAppRoot/CnAppRoot.vue +102 -2
- package/src/components/CnDashboardPage/CnDashboardPage.vue +86 -7
- package/src/components/CnIndexPage/CnIndexPage.vue +409 -23
- package/src/components/CnIndexPage/useSelfFetchList.js +21 -1
- package/src/components/CnIndexSidebar/CnIndexSidebar.md +2 -0
- package/src/components/CnIndexSidebar/CnIndexSidebar.vue +91 -1
- package/src/components/CnMapWidget/CnMapWidget.vue +16 -1
- package/src/components/CnPartyPicker/CnPartyPicker.vue +317 -0
- package/src/components/CnPartyPicker/index.js +11 -0
- package/src/components/CnSavedViewEditDialog/CnSavedViewEditDialog.vue +192 -0
- package/src/components/CnSavedViewEditDialog/index.js +11 -0
- package/src/components/CnSavedViewShareDialog/CnSavedViewShareDialog.vue +21 -6
- package/src/components/CnSavedViewShareFields/CnSavedViewShareFields.vue +130 -37
- package/src/components/CnSavedViewsControl/CnSavedViewsControl.vue +36 -599
- package/src/components/CnSavedViewsPanel/CnSavedViewsPanel.vue +459 -0
- package/src/components/CnSavedViewsPanel/index.js +11 -0
- package/src/components/index.js +4 -0
- package/src/css/index-page.css +16 -0
- package/src/css/look-board-index.css +5 -3
- package/src/css/look-board.css +18 -3
- package/src/index.js +4 -0
- package/src/mixins/savedViewsList.js +231 -0
- package/src/schemas/app-manifest-v2.schema.json +98 -3
- package/src/utils/actionsDispatcher.js +2 -2
- package/src/utils/builtInModals.js +35 -0
- package/src/utils/mapMarkerColor.js +117 -0
- package/src/utils/mergeManifestDelta.js +4 -0
- package/src/utils/savedViewHelpers.js +58 -6
- package/src/utils/savedViewMenu.js +150 -0
- package/src/utils/searchGroupSharees.js +49 -14
- package/src/utils/validateManifestV2.compiled.js +3 -3
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
- SPDX-FileCopyrightText: 2026 Conduction B.V.
|
|
3
|
+
- SPDX-License-Identifier: EUPL-1.2
|
|
4
|
+
-->
|
|
5
|
+
<template>
|
|
6
|
+
<section class="cn-saved-views-panel" data-testid="cn-saved-views-panel" :aria-label="t('nextcloud-vue', 'Saved views')">
|
|
7
|
+
<div class="cn-saved-views-panel__head">
|
|
8
|
+
<NcButton
|
|
9
|
+
variant="secondary"
|
|
10
|
+
:wide="true"
|
|
11
|
+
data-testid="cn-saved-views-save"
|
|
12
|
+
@click="onSaveRequest">
|
|
13
|
+
<template #icon>
|
|
14
|
+
<ContentSaveOutline :size="20" />
|
|
15
|
+
</template>
|
|
16
|
+
{{ t('nextcloud-vue', 'Save view') }}
|
|
17
|
+
</NcButton>
|
|
18
|
+
<p class="cn-saved-views-panel__note">
|
|
19
|
+
{{ t('nextcloud-vue', 'Keeps the current search term, filters and sort under a name.') }}
|
|
20
|
+
</p>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<p v-if="loading" class="cn-saved-views-panel__note" data-testid="cn-saved-views-loading">
|
|
24
|
+
{{ t('nextcloud-vue', 'Loading…') }}
|
|
25
|
+
</p>
|
|
26
|
+
<p v-else-if="views.length === 0" class="cn-saved-views-panel__note" data-testid="cn-saved-views-empty">
|
|
27
|
+
{{ t('nextcloud-vue', 'No saved views yet') }}
|
|
28
|
+
</p>
|
|
29
|
+
|
|
30
|
+
<template v-else>
|
|
31
|
+
<div
|
|
32
|
+
v-if="offeredLabels.length > 0"
|
|
33
|
+
class="cn-saved-views-panel__labels"
|
|
34
|
+
role="group"
|
|
35
|
+
:aria-label="t('nextcloud-vue', 'Labels')">
|
|
36
|
+
<button
|
|
37
|
+
v-for="label in offeredLabels"
|
|
38
|
+
:key="`label-${label}`"
|
|
39
|
+
type="button"
|
|
40
|
+
class="cn-saved-views-panel__label"
|
|
41
|
+
data-testid="cn-saved-views-label"
|
|
42
|
+
:data-label="label"
|
|
43
|
+
:aria-pressed="labelFilter === label"
|
|
44
|
+
:aria-label="labelFilterLabel(label)"
|
|
45
|
+
@click="onLabelFilter(label)">
|
|
46
|
+
{{ label }}
|
|
47
|
+
</button>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<p
|
|
51
|
+
v-if="rows.length === 0"
|
|
52
|
+
class="cn-saved-views-panel__note"
|
|
53
|
+
data-testid="cn-saved-views-none-for-label">
|
|
54
|
+
{{ t('nextcloud-vue', 'No views with this label') }}
|
|
55
|
+
</p>
|
|
56
|
+
|
|
57
|
+
<template v-for="section in sections" :key="section.key">
|
|
58
|
+
<h4
|
|
59
|
+
v-if="section.label"
|
|
60
|
+
class="cn-saved-views-panel__section"
|
|
61
|
+
:data-testid="`cn-saved-views-section-${section.key}`">
|
|
62
|
+
{{ section.label }}
|
|
63
|
+
</h4>
|
|
64
|
+
<p v-if="sectionHint(section.key)" class="cn-saved-views-panel__note">
|
|
65
|
+
{{ sectionHint(section.key) }}
|
|
66
|
+
</p>
|
|
67
|
+
<ul class="cn-saved-views-panel__list">
|
|
68
|
+
<li
|
|
69
|
+
v-for="row in section.rows"
|
|
70
|
+
:key="`view-${idOf(row.view)}`"
|
|
71
|
+
class="cn-saved-views-panel__row"
|
|
72
|
+
:class="{ 'cn-saved-views-panel__row--selected': isSelected(row.view) }">
|
|
73
|
+
<button
|
|
74
|
+
type="button"
|
|
75
|
+
class="cn-saved-views-panel__name"
|
|
76
|
+
data-testid="cn-saved-views-item"
|
|
77
|
+
:data-view-id="idOf(row.view)"
|
|
78
|
+
:data-depth="row.depth"
|
|
79
|
+
:data-group="row.group"
|
|
80
|
+
:data-access="accessOf(row)"
|
|
81
|
+
:aria-pressed="isSelected(row.view)"
|
|
82
|
+
:aria-label="rowLabel(row)"
|
|
83
|
+
@click="emitFor('apply', row.view)">
|
|
84
|
+
<span class="cn-saved-views-panel__name-text">{{ rowName(row) }}</span>
|
|
85
|
+
<span v-if="subline(row, section.key)" class="cn-saved-views-panel__subline">{{ subline(row, section.key) }}</span>
|
|
86
|
+
</button>
|
|
87
|
+
<NcActions
|
|
88
|
+
:aria-label="manageLabel(row.view)"
|
|
89
|
+
:forceMenu="true"
|
|
90
|
+
data-testid="cn-saved-views-manage"
|
|
91
|
+
:data-view-id="idOf(row.view)">
|
|
92
|
+
<NcActionButton
|
|
93
|
+
v-if="can(row, 'edit')"
|
|
94
|
+
data-testid="cn-saved-views-edit"
|
|
95
|
+
:data-view-id="idOf(row.view)"
|
|
96
|
+
:description="t('nextcloud-vue', 'Name, search term, filters and sort')"
|
|
97
|
+
:closeAfterClick="true"
|
|
98
|
+
@click="emitFor('edit-request', row.view)">
|
|
99
|
+
<template #icon>
|
|
100
|
+
<PencilOutline :size="20" />
|
|
101
|
+
</template>
|
|
102
|
+
{{ t('nextcloud-vue', 'Edit') }}
|
|
103
|
+
</NcActionButton>
|
|
104
|
+
<NcActionButton
|
|
105
|
+
v-if="can(row, 'update')"
|
|
106
|
+
data-testid="cn-saved-views-update"
|
|
107
|
+
:data-view-id="idOf(row.view)"
|
|
108
|
+
:closeAfterClick="true"
|
|
109
|
+
@click="emitFor('update-request', row.view)">
|
|
110
|
+
<template #icon>
|
|
111
|
+
<ContentSaveOutline :size="20" />
|
|
112
|
+
</template>
|
|
113
|
+
{{ t('nextcloud-vue', 'Update with current filters') }}
|
|
114
|
+
</NcActionButton>
|
|
115
|
+
<NcActionButton
|
|
116
|
+
v-if="can(row, 'share')"
|
|
117
|
+
data-testid="cn-saved-views-share"
|
|
118
|
+
:data-view-id="idOf(row.view)"
|
|
119
|
+
:description="t('nextcloud-vue', 'With people and groups')"
|
|
120
|
+
:closeAfterClick="true"
|
|
121
|
+
@click="emitFor('share-request', row.view)">
|
|
122
|
+
<template #icon>
|
|
123
|
+
<ShareVariantOutline :size="20" />
|
|
124
|
+
</template>
|
|
125
|
+
{{ t('nextcloud-vue', 'Share') }}
|
|
126
|
+
</NcActionButton>
|
|
127
|
+
<NcActionButton
|
|
128
|
+
v-if="can(row, 'copy')"
|
|
129
|
+
data-testid="cn-saved-views-copy"
|
|
130
|
+
:data-view-id="idOf(row.view)"
|
|
131
|
+
:closeAfterClick="true"
|
|
132
|
+
@click="emitFor('copy-request', row.view)">
|
|
133
|
+
<template #icon>
|
|
134
|
+
<ContentCopy :size="20" />
|
|
135
|
+
</template>
|
|
136
|
+
{{ t('nextcloud-vue', 'Save a copy') }}
|
|
137
|
+
</NcActionButton>
|
|
138
|
+
<NcActionButton
|
|
139
|
+
v-if="canPin"
|
|
140
|
+
data-testid="cn-saved-views-menu"
|
|
141
|
+
:data-view-id="idOf(row.view)"
|
|
142
|
+
:description="isInMenu(row.view) ? t('nextcloud-vue', 'Leaves the menu on the left; the view stays') : t('nextcloud-vue', 'Appears under My views in the menu on the left')"
|
|
143
|
+
:closeAfterClick="true"
|
|
144
|
+
@click="emitFor('menu-toggle', row.view)">
|
|
145
|
+
<template #icon>
|
|
146
|
+
<PinOffOutline v-if="isInMenu(row.view)" :size="20" />
|
|
147
|
+
<PinOutline v-else :size="20" />
|
|
148
|
+
</template>
|
|
149
|
+
{{ isInMenu(row.view) ? t('nextcloud-vue', 'Remove from menu') : t('nextcloud-vue', 'Add to menu') }}
|
|
150
|
+
</NcActionButton>
|
|
151
|
+
<NcActionButton
|
|
152
|
+
v-if="can(row, 'delete')"
|
|
153
|
+
data-testid="cn-saved-views-delete"
|
|
154
|
+
:data-view-id="idOf(row.view)"
|
|
155
|
+
:description="t('nextcloud-vue', 'Also for whoever it is shared with')"
|
|
156
|
+
:closeAfterClick="true"
|
|
157
|
+
@click="emitFor('delete-request', row.view)">
|
|
158
|
+
<template #icon>
|
|
159
|
+
<TrashCanOutline :size="20" />
|
|
160
|
+
</template>
|
|
161
|
+
{{ t('nextcloud-vue', 'Delete') }}
|
|
162
|
+
</NcActionButton>
|
|
163
|
+
</NcActions>
|
|
164
|
+
</li>
|
|
165
|
+
</ul>
|
|
166
|
+
</template>
|
|
167
|
+
</template>
|
|
168
|
+
</section>
|
|
169
|
+
</template>
|
|
170
|
+
|
|
171
|
+
<script>
|
|
172
|
+
import { NcActionButton, NcActions, NcButton } from '@nextcloud/vue'
|
|
173
|
+
import ContentCopy from 'vue-material-design-icons/ContentCopy.vue'
|
|
174
|
+
import ContentSaveOutline from 'vue-material-design-icons/ContentSaveOutline.vue'
|
|
175
|
+
import PencilOutline from 'vue-material-design-icons/PencilOutline.vue'
|
|
176
|
+
import PinOffOutline from 'vue-material-design-icons/PinOffOutline.vue'
|
|
177
|
+
import PinOutline from 'vue-material-design-icons/PinOutline.vue'
|
|
178
|
+
import ShareVariantOutline from 'vue-material-design-icons/ShareVariantOutline.vue'
|
|
179
|
+
import TrashCanOutline from 'vue-material-design-icons/TrashCanOutline.vue'
|
|
180
|
+
import { savedViewsList } from '../../mixins/savedViewsList.js'
|
|
181
|
+
import { VIEW_GROUPS } from '../../utils/buildViewTree.js'
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* CnSavedViewsPanel — the saved views of an index page, in the Filters tab
|
|
185
|
+
* of its sidebar: Save view, then My views, Shared with me and the
|
|
186
|
+
* read-only Default views, and per view one "Manage {name}" menu with
|
|
187
|
+
* Edit, Update with current filters, Share, Save a copy, Add to menu or Remove
|
|
188
|
+
* from menu, and Delete. Entries show by the caller's access: the owner gets
|
|
189
|
+
* Edit, Update, Share and Delete; a view shared with write access gets
|
|
190
|
+
* Update and Save a copy; one shared to use, and a default, get Save a copy.
|
|
191
|
+
* Add to menu is there for every view when the app has a menu. The server
|
|
192
|
+
* stays the authority.
|
|
193
|
+
*
|
|
194
|
+
* Presentational: `CnIndexPage` owns fetching and every write.
|
|
195
|
+
*
|
|
196
|
+
* @event {object} apply — Apply the view. Payload: the View API object.
|
|
197
|
+
* @event {void} save-request — Save the current filters as a new one.
|
|
198
|
+
* @event {object} edit-request — Edit an own view.
|
|
199
|
+
* @event {object} share-request — Share an own view.
|
|
200
|
+
* @event {object} update-request — Save the current filters into it (owner, write).
|
|
201
|
+
* @event {object} copy-request — Save a copy of a shared or default view.
|
|
202
|
+
* @event {object} menu-toggle — Add the view to the app menu, or remove it.
|
|
203
|
+
* @event {object} delete-request — Delete an own view (after confirmation).
|
|
204
|
+
* @spec openspec/changes/saved-views-manage-and-defaults/tasks.md#task-6
|
|
205
|
+
*/
|
|
206
|
+
export default {
|
|
207
|
+
name: 'CnSavedViewsPanel',
|
|
208
|
+
|
|
209
|
+
components: {
|
|
210
|
+
NcActionButton,
|
|
211
|
+
NcActions,
|
|
212
|
+
NcButton,
|
|
213
|
+
ContentCopy,
|
|
214
|
+
ContentSaveOutline,
|
|
215
|
+
PencilOutline,
|
|
216
|
+
PinOffOutline,
|
|
217
|
+
PinOutline,
|
|
218
|
+
ShareVariantOutline,
|
|
219
|
+
TrashCanOutline,
|
|
220
|
+
},
|
|
221
|
+
|
|
222
|
+
mixins: [savedViewsList],
|
|
223
|
+
|
|
224
|
+
props: {
|
|
225
|
+
/**
|
|
226
|
+
* The ids of the views this user put in the app menu, or `null` when
|
|
227
|
+
* the page cannot pin (no app shell to hold the menu).
|
|
228
|
+
*
|
|
229
|
+
* @type {Array<string>|null}
|
|
230
|
+
*/
|
|
231
|
+
menuIds: {
|
|
232
|
+
type: Array,
|
|
233
|
+
default: null,
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
|
|
237
|
+
emits: [
|
|
238
|
+
/** Apply the view. Payload: the View API object. */
|
|
239
|
+
'apply',
|
|
240
|
+
/** Save a copy of a shared or default view. Payload: the view. */
|
|
241
|
+
'copy-request',
|
|
242
|
+
/** Delete an own view, after confirmation. Payload: the view. */
|
|
243
|
+
'delete-request',
|
|
244
|
+
/** Edit an own view. Payload: the view. */
|
|
245
|
+
'edit-request',
|
|
246
|
+
/** Add the view to the app menu, or remove it. Payload: the view. */
|
|
247
|
+
'menu-toggle',
|
|
248
|
+
/** Save the current filters as a new view. */
|
|
249
|
+
'save-request',
|
|
250
|
+
/** Share an own view. Payload: the view. */
|
|
251
|
+
'share-request',
|
|
252
|
+
/** Save the current filters into the view (owner, write). Payload: the view. */
|
|
253
|
+
'update-request',
|
|
254
|
+
],
|
|
255
|
+
|
|
256
|
+
computed: {
|
|
257
|
+
/** @return {boolean} Whether Add to menu is offered. */
|
|
258
|
+
canPin() {
|
|
259
|
+
return Array.isArray(this.menuIds)
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
|
|
263
|
+
methods: {
|
|
264
|
+
/**
|
|
265
|
+
* The stable id of a view: its id, else its slug.
|
|
266
|
+
*
|
|
267
|
+
* @param {object} view The view.
|
|
268
|
+
* @return {string}
|
|
269
|
+
*/
|
|
270
|
+
idOf(view) {
|
|
271
|
+
return String(view.id || view.slug)
|
|
272
|
+
},
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Whether a row offers an entry, by the caller's access. A default
|
|
276
|
+
* view offers a copy only.
|
|
277
|
+
*
|
|
278
|
+
* @param {object} row The row from buildViewTree().
|
|
279
|
+
* @param {'edit'|'share'|'update'|'copy'|'delete'} entry The entry.
|
|
280
|
+
* @return {boolean}
|
|
281
|
+
* @spec openspec/changes/saved-views-manage-and-defaults/tasks.md#task-2
|
|
282
|
+
*/
|
|
283
|
+
can(row, entry) {
|
|
284
|
+
if (row.group === VIEW_GROUPS.SEEDED) {
|
|
285
|
+
return entry === 'copy'
|
|
286
|
+
}
|
|
287
|
+
const allowed = {
|
|
288
|
+
owner: ['edit', 'update', 'share', 'delete'],
|
|
289
|
+
write: ['update', 'copy'],
|
|
290
|
+
read: ['copy'],
|
|
291
|
+
}
|
|
292
|
+
return (allowed[this.accessOf(row)] || []).includes(entry)
|
|
293
|
+
},
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Whether a view is in the app menu.
|
|
297
|
+
*
|
|
298
|
+
* @param {object} view The view.
|
|
299
|
+
* @return {boolean}
|
|
300
|
+
*/
|
|
301
|
+
isInMenu(view) {
|
|
302
|
+
return this.canPin && this.menuIds.includes(this.idOf(view))
|
|
303
|
+
},
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* The line under a section caption.
|
|
307
|
+
*
|
|
308
|
+
* @param {string} key The section key.
|
|
309
|
+
* @return {string}
|
|
310
|
+
*/
|
|
311
|
+
sectionHint(key) {
|
|
312
|
+
if (key === 'shared') {
|
|
313
|
+
return this.t('nextcloud-vue', 'From colleagues. You can use them and save a copy.')
|
|
314
|
+
}
|
|
315
|
+
if (key === 'seeded') {
|
|
316
|
+
return this.t('nextcloud-vue', 'Read only. Shipped with the app; a new version updates them.')
|
|
317
|
+
}
|
|
318
|
+
return ''
|
|
319
|
+
},
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* The second line of a row: the view's description, else who shared it.
|
|
323
|
+
*
|
|
324
|
+
* @param {object} row The row from buildViewTree().
|
|
325
|
+
* @param {string} sectionKey The section the row is in.
|
|
326
|
+
* @return {string}
|
|
327
|
+
*/
|
|
328
|
+
subline(row, sectionKey) {
|
|
329
|
+
const description = typeof row.view.description === 'string' ? row.view.description.trim() : ''
|
|
330
|
+
if (description !== '') {
|
|
331
|
+
return description
|
|
332
|
+
}
|
|
333
|
+
if (sectionKey === 'shared' && row.view.owner) {
|
|
334
|
+
return this.t('nextcloud-vue', 'By {owner}', { owner: row.view.ownerDisplayName || row.view.owner })
|
|
335
|
+
}
|
|
336
|
+
return ''
|
|
337
|
+
},
|
|
338
|
+
|
|
339
|
+
manageLabel(view) {
|
|
340
|
+
return this.t('nextcloud-vue', 'Manage "{name}"', { name: view.name })
|
|
341
|
+
},
|
|
342
|
+
|
|
343
|
+
onSaveRequest() {
|
|
344
|
+
/** @event save-request Save the current filters as a new one. */
|
|
345
|
+
this.$emit('save-request')
|
|
346
|
+
},
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Emit one of the per-view intents with its view.
|
|
350
|
+
*
|
|
351
|
+
* @param {string} event The event name, one of `emits`.
|
|
352
|
+
* @param {object} view The view.
|
|
353
|
+
*/
|
|
354
|
+
emitFor(event, view) {
|
|
355
|
+
this.$emit(event, view)
|
|
356
|
+
},
|
|
357
|
+
},
|
|
358
|
+
}
|
|
359
|
+
</script>
|
|
360
|
+
|
|
361
|
+
<style scoped>
|
|
362
|
+
.cn-saved-views-panel {
|
|
363
|
+
display: flex;
|
|
364
|
+
flex-direction: column;
|
|
365
|
+
gap: 8px;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.cn-saved-views-panel__head {
|
|
369
|
+
display: flex;
|
|
370
|
+
flex-direction: column;
|
|
371
|
+
gap: 4px;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.cn-saved-views-panel__section {
|
|
375
|
+
margin: 8px 0 0;
|
|
376
|
+
font-size: var(--font-size-small, 13px);
|
|
377
|
+
color: var(--color-text-maxcontrast);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.cn-saved-views-panel__note {
|
|
381
|
+
margin: 0;
|
|
382
|
+
color: var(--color-text-maxcontrast);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.cn-saved-views-panel__labels {
|
|
386
|
+
display: flex;
|
|
387
|
+
flex-wrap: wrap;
|
|
388
|
+
gap: 4px;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
.cn-saved-views-panel__label {
|
|
392
|
+
min-height: var(--default-clickable-area);
|
|
393
|
+
padding: 0 12px;
|
|
394
|
+
border: 1px solid var(--color-border-dark);
|
|
395
|
+
border-radius: var(--border-radius-pill);
|
|
396
|
+
background: var(--color-main-background);
|
|
397
|
+
color: var(--color-main-text);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.cn-saved-views-panel__label[aria-pressed='true'] {
|
|
401
|
+
background: var(--color-primary-element-light);
|
|
402
|
+
color: var(--color-primary-element-light-text);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
.cn-saved-views-panel__list {
|
|
406
|
+
list-style: none;
|
|
407
|
+
margin: 0;
|
|
408
|
+
padding: 0;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
.cn-saved-views-panel__row {
|
|
412
|
+
display: flex;
|
|
413
|
+
align-items: center;
|
|
414
|
+
gap: 4px;
|
|
415
|
+
border-radius: var(--border-radius-element, var(--border-radius-large));
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
.cn-saved-views-panel__row--selected {
|
|
419
|
+
background: var(--color-primary-element-light);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
.cn-saved-views-panel__name {
|
|
423
|
+
flex: 1 1 auto;
|
|
424
|
+
min-width: 0;
|
|
425
|
+
min-height: var(--default-clickable-area);
|
|
426
|
+
padding: 0 8px;
|
|
427
|
+
border: 0;
|
|
428
|
+
background: transparent;
|
|
429
|
+
color: var(--color-main-text);
|
|
430
|
+
text-align: start;
|
|
431
|
+
overflow: hidden;
|
|
432
|
+
text-overflow: ellipsis;
|
|
433
|
+
white-space: nowrap;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
.cn-saved-views-panel__name {
|
|
437
|
+
display: flex;
|
|
438
|
+
flex-direction: column;
|
|
439
|
+
justify-content: center;
|
|
440
|
+
align-items: flex-start;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
.cn-saved-views-panel__name-text,
|
|
444
|
+
.cn-saved-views-panel__subline {
|
|
445
|
+
max-width: 100%;
|
|
446
|
+
overflow: hidden;
|
|
447
|
+
text-overflow: ellipsis;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
.cn-saved-views-panel__subline {
|
|
451
|
+
font-size: var(--font-size-small, 13px);
|
|
452
|
+
color: var(--color-text-maxcontrast);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
.cn-saved-views-panel__name:focus-visible {
|
|
456
|
+
outline: 2px solid var(--color-primary-element);
|
|
457
|
+
outline-offset: -2px;
|
|
458
|
+
}
|
|
459
|
+
</style>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPDX-FileCopyrightText: 2026 Conduction B.V.
|
|
3
|
+
* SPDX-License-Identifier: EUPL-1.2
|
|
4
|
+
*
|
|
5
|
+
* CnSavedViewsPanel — the saved views of an index page, in its sidebar's Filters tab.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import CnSavedViewsPanel from './CnSavedViewsPanel.vue'
|
|
9
|
+
|
|
10
|
+
export { CnSavedViewsPanel }
|
|
11
|
+
export default CnSavedViewsPanel
|
package/src/components/index.js
CHANGED
|
@@ -83,6 +83,7 @@ export {
|
|
|
83
83
|
} from './CnIcon/semanticIcons.js'
|
|
84
84
|
export { CnPageHeader } from './CnPageHeader/index.js'
|
|
85
85
|
export { CnBreadcrumbs } from './CnBreadcrumbs/index.js'
|
|
86
|
+
export { CnActionDialog } from './CnActionDialog/index.js'
|
|
86
87
|
export { CnActionsBar } from './CnActionsBar/index.js'
|
|
87
88
|
export { CnActionsMenu } from './CnActionsMenu/index.js'
|
|
88
89
|
export { CnActionButtons } from './CnActionButtons/index.js'
|
|
@@ -131,6 +132,7 @@ export { CnContactPicker } from './CnContactPicker/index.js'
|
|
|
131
132
|
export { CnContactCreate } from './CnContactCreate/index.js'
|
|
132
133
|
export { CnResourceSelect } from './CnResourceSelect/index.js'
|
|
133
134
|
export { CnSearchSelect } from './CnSearchSelect/index.js'
|
|
135
|
+
export { CnPartyPicker } from './CnPartyPicker/index.js'
|
|
134
136
|
export { CnNoticeWidget } from './CnNoticeWidget/index.js'
|
|
135
137
|
export { CnPropertySourceField } from './CnPropertySourceField/index.js'
|
|
136
138
|
export { CnDurationField } from './CnDurationField/index.js'
|
|
@@ -138,6 +140,8 @@ export { CnFavouriteToggle } from './CnFavouriteToggle/index.js'
|
|
|
138
140
|
export { CnFollowToggle } from './CnFollowToggle/index.js'
|
|
139
141
|
export { CnUnreadMarker } from './CnUnreadMarker/index.js'
|
|
140
142
|
export { CnViewPresentationPicker } from './CnViewPresentationPicker/index.js'
|
|
143
|
+
export { CnSavedViewsPanel } from './CnSavedViewsPanel/index.js'
|
|
144
|
+
export { CnSavedViewEditDialog } from './CnSavedViewEditDialog/index.js'
|
|
141
145
|
export { CnSavedViewPresentationDialog } from './CnSavedViewPresentationDialog/index.js'
|
|
142
146
|
export { CnSavedViewShareDialog } from './CnSavedViewShareDialog/index.js'
|
|
143
147
|
export { CnSavedViewShareFields } from './CnSavedViewShareFields/index.js'
|
package/src/css/index-page.css
CHANGED
|
@@ -253,6 +253,22 @@
|
|
|
253
253
|
font-size: 14px;
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
+
/* The map's count line and the note when a map that plots every row hit its
|
|
257
|
+
limit. */
|
|
258
|
+
.cn-index-page__map-count {
|
|
259
|
+
display: flex;
|
|
260
|
+
flex-wrap: wrap;
|
|
261
|
+
align-items: center;
|
|
262
|
+
gap: 8px 16px;
|
|
263
|
+
padding: 12px 16px;
|
|
264
|
+
color: var(--color-text-maxcontrast);
|
|
265
|
+
font-size: 14px;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.cn-index-page__map-cap {
|
|
269
|
+
color: var(--color-warning-text, var(--color-main-text));
|
|
270
|
+
}
|
|
271
|
+
|
|
256
272
|
/* Table view owns both scroll axes. With the page column scrolling instead, the
|
|
257
273
|
table's horizontal scrollbar sat at the bottom of the (taller-than-viewport)
|
|
258
274
|
table, so it only appeared after scrolling down, and the pagination was
|
|
@@ -760,14 +760,16 @@
|
|
|
760
760
|
line-height: 16px;
|
|
761
761
|
}
|
|
762
762
|
|
|
763
|
-
/* A status pill: 3px 10px, radius 12, 13px weight 600
|
|
764
|
-
|
|
763
|
+
/* A status pill: 3px 10px, radius 12, 13px weight 600, 21px tall on a
|
|
764
|
+
normal line (a 1.4 line made it 24.2). The colours stay the status colour
|
|
765
|
+
map's.
|
|
766
|
+
@spec openspec/changes/board-tags-shape/specs/board-tags-shape/spec.md#requirement-a-board-tag-is-a-21px-rounded-rectangle */
|
|
765
767
|
.cn-look-board .cn-table-container--board .cn-data-table .cn-status-badge {
|
|
766
768
|
padding: 3px 10px;
|
|
767
769
|
border-radius: 12px;
|
|
768
770
|
font-size: 13px;
|
|
769
771
|
font-weight: 600;
|
|
770
|
-
line-height:
|
|
772
|
+
line-height: normal;
|
|
771
773
|
}
|
|
772
774
|
|
|
773
775
|
/* The row menu column holds one 34px button between 16px of padding, so 66px
|
package/src/css/look-board.css
CHANGED
|
@@ -171,6 +171,18 @@
|
|
|
171
171
|
padding: 0;
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
+
/* ---- Button weight ------------------------------------------------------ */
|
|
175
|
+
/* The boards draw a labelled button at 600 (DqZaken "Downloaden", DqZaak's
|
|
176
|
+
header, dialog footers, toolbar and row actions). An NcButton reads its
|
|
177
|
+
weight from --font-weight-element, which Nextcloud sets to 500; the board
|
|
178
|
+
look sets 600 on each button, so every NcButton on a board page draws the
|
|
179
|
+
board's weight without a rule per surface. Breadcrumb buttons keep the
|
|
180
|
+
crumb weight.
|
|
181
|
+
@spec openspec/changes/board-button-weight/specs/board-button-weight/spec.md#requirement-a-board-button-draws-at-600 */
|
|
182
|
+
.cn-look-board .button-vue:not(.cn-look-nextcloud *):not(.vue-crumb *) {
|
|
183
|
+
--font-weight-element: 600;
|
|
184
|
+
}
|
|
185
|
+
|
|
174
186
|
/* ---- The header button ------------------------------------------------- */
|
|
175
187
|
.cn-look-board .cn-index-page__header-buttons:not(.cn-look-nextcloud *),
|
|
176
188
|
.cn-look-board .cn-detail-page__header-actions:not(.cn-look-nextcloud *),
|
|
@@ -1084,9 +1096,12 @@
|
|
|
1084
1096
|
color: var(--cn-status-badge-ink);
|
|
1085
1097
|
}
|
|
1086
1098
|
|
|
1087
|
-
/* Every board
|
|
1088
|
-
(DqZaak's header pills, DqZaken's status column
|
|
1089
|
-
.
|
|
1099
|
+
/* Every board tag is 13px at 600 on a 3px 10px pad with a 12px corner, 21px
|
|
1100
|
+
tall (DqZaak's header pills, DqZaken's status column, DqZaaktype's version
|
|
1101
|
+
tag). The boards draw one tag size, so a small badge takes it too: it was
|
|
1102
|
+
a 17.8px full pill at 500.
|
|
1103
|
+
@spec openspec/changes/board-tags-shape/specs/board-tags-shape/spec.md#requirement-a-board-tag-is-a-21px-rounded-rectangle */
|
|
1104
|
+
.cn-look-board .cn-status-badge:not(.cn-look-nextcloud *) {
|
|
1090
1105
|
padding: 3px 10px;
|
|
1091
1106
|
border-radius: 12px;
|
|
1092
1107
|
font-size: 13px;
|
package/src/index.js
CHANGED
|
@@ -32,6 +32,7 @@ export { default as NcSelectTags } from './components/NcSelectTags/NcSelectTags.
|
|
|
32
32
|
// Components
|
|
33
33
|
export {
|
|
34
34
|
CnActionButtons,
|
|
35
|
+
CnActionDialog,
|
|
35
36
|
CnActionsBar,
|
|
36
37
|
CnActionsMenu,
|
|
37
38
|
CnAddWidgetModal,
|
|
@@ -244,6 +245,7 @@ export {
|
|
|
244
245
|
CnPageRenderer,
|
|
245
246
|
CnPagination,
|
|
246
247
|
CnPartyActionsWidget,
|
|
248
|
+
CnPartyPicker,
|
|
247
249
|
CnPeopleWidget,
|
|
248
250
|
CnPeopleWidgetForm,
|
|
249
251
|
CnPresenceAvatars,
|
|
@@ -270,10 +272,12 @@ export {
|
|
|
270
272
|
CnRoadmapItem,
|
|
271
273
|
CnRoadmapTab,
|
|
272
274
|
CnRowActions,
|
|
275
|
+
CnSavedViewEditDialog,
|
|
273
276
|
CnSavedViewPresentationDialog,
|
|
274
277
|
CnSavedViewsControl,
|
|
275
278
|
CnSavedViewShareDialog,
|
|
276
279
|
CnSavedViewShareFields,
|
|
280
|
+
CnSavedViewsPanel,
|
|
277
281
|
CnSavedViewsWidget,
|
|
278
282
|
CnSavedViewWidget,
|
|
279
283
|
CnSavedViewWidgetForm,
|