@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.
Files changed (135) hide show
  1. package/dist/esm/components/CnActionDialog/CnActionDialog.vue.js +9 -0
  2. package/dist/esm/components/CnActionDialog/CnActionDialog.vue.js.map +1 -0
  3. package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js +338 -0
  4. package/dist/esm/components/CnActionDialog/CnActionDialog.vue2.js.map +1 -0
  5. package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js +249 -0
  6. package/dist/esm/components/CnActionDialog/CnActionDialog.vue3.js.map +1 -0
  7. package/dist/esm/components/CnAdvancedFormDialog/CnPropertiesTab.vue2.js +2 -2
  8. package/dist/esm/components/CnAdvancedFormDialog/CnPropertiesTab.vue2.js.map +1 -1
  9. package/dist/esm/components/CnAppNav/CnAppNav.vue2.js.map +1 -1
  10. package/dist/esm/components/CnAppNav/CnAppNav.vue3.js +145 -138
  11. package/dist/esm/components/CnAppNav/CnAppNav.vue3.js.map +1 -1
  12. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js +102 -2
  13. package/dist/esm/components/CnAppRoot/CnAppRoot.vue2.js.map +1 -1
  14. package/dist/esm/components/CnAppRoot/CnAppRoot.vue3.js.map +1 -1
  15. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue.js +1 -1
  16. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js +83 -6
  17. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue2.js.map +1 -1
  18. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js +4 -2
  19. package/dist/esm/components/CnDashboardPage/CnDashboardPage.vue3.js.map +1 -1
  20. package/dist/esm/components/CnIcon/semanticIcons.js +2 -2
  21. package/dist/esm/components/CnIcon/semanticIcons.js.map +1 -1
  22. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js +389 -20
  23. package/dist/esm/components/CnIndexPage/CnIndexPage.vue2.js.map +1 -1
  24. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js +68 -35
  25. package/dist/esm/components/CnIndexPage/CnIndexPage.vue3.js.map +1 -1
  26. package/dist/esm/components/CnIndexPage/useSelfFetchList.js +22 -2
  27. package/dist/esm/components/CnIndexPage/useSelfFetchList.js.map +1 -1
  28. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js +69 -5
  29. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue2.js.map +1 -1
  30. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js +38 -5
  31. package/dist/esm/components/CnIndexSidebar/CnIndexSidebar.vue3.js.map +1 -1
  32. package/dist/esm/components/CnMapWidget/CnMapWidget.vue.js +1 -1
  33. package/dist/esm/components/CnMapWidget/CnMapWidget.vue2.js +7 -1
  34. package/dist/esm/components/CnMapWidget/CnMapWidget.vue2.js.map +1 -1
  35. package/dist/esm/components/CnMapWidget/CnMapWidget.vue3.js.map +1 -1
  36. package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue.js +8 -0
  37. package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue.js.map +1 -0
  38. package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue2.js +297 -0
  39. package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue2.js.map +1 -0
  40. package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue3.js +24 -0
  41. package/dist/esm/components/CnPartyPicker/CnPartyPicker.vue3.js.map +1 -0
  42. package/dist/esm/components/CnSavedViewEditDialog/CnSavedViewEditDialog.vue.js +9 -0
  43. package/dist/esm/components/CnSavedViewEditDialog/CnSavedViewEditDialog.vue.js.map +1 -0
  44. package/dist/esm/components/CnSavedViewEditDialog/CnSavedViewEditDialog.vue2.js +95 -0
  45. package/dist/esm/components/CnSavedViewEditDialog/CnSavedViewEditDialog.vue2.js.map +1 -0
  46. package/dist/esm/components/CnSavedViewEditDialog/CnSavedViewEditDialog.vue3.js +123 -0
  47. package/dist/esm/components/CnSavedViewEditDialog/CnSavedViewEditDialog.vue3.js.map +1 -0
  48. package/dist/esm/components/CnSavedViewShareDialog/CnSavedViewShareDialog.vue.js +1 -1
  49. package/dist/esm/components/CnSavedViewShareDialog/CnSavedViewShareDialog.vue2.js +13 -5
  50. package/dist/esm/components/CnSavedViewShareDialog/CnSavedViewShareDialog.vue2.js.map +1 -1
  51. package/dist/esm/components/CnSavedViewShareDialog/CnSavedViewShareDialog.vue3.js +3 -1
  52. package/dist/esm/components/CnSavedViewShareDialog/CnSavedViewShareDialog.vue3.js.map +1 -1
  53. package/dist/esm/components/CnSavedViewShareFields/CnSavedViewShareFields.vue.js +1 -1
  54. package/dist/esm/components/CnSavedViewShareFields/CnSavedViewShareFields.vue2.js +49 -23
  55. package/dist/esm/components/CnSavedViewShareFields/CnSavedViewShareFields.vue2.js.map +1 -1
  56. package/dist/esm/components/CnSavedViewShareFields/CnSavedViewShareFields.vue3.js +74 -27
  57. package/dist/esm/components/CnSavedViewShareFields/CnSavedViewShareFields.vue3.js.map +1 -1
  58. package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue.js +0 -1
  59. package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue.js.map +1 -1
  60. package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue2.js +18 -413
  61. package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue2.js.map +1 -1
  62. package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue3.js +27 -214
  63. package/dist/esm/components/CnSavedViewsControl/CnSavedViewsControl.vue3.js.map +1 -1
  64. package/dist/esm/components/CnSavedViewsPanel/CnSavedViewsPanel.vue.js +9 -0
  65. package/dist/esm/components/CnSavedViewsPanel/CnSavedViewsPanel.vue.js.map +1 -0
  66. package/dist/esm/components/CnSavedViewsPanel/CnSavedViewsPanel.vue2.js +190 -0
  67. package/dist/esm/components/CnSavedViewsPanel/CnSavedViewsPanel.vue2.js.map +1 -0
  68. package/dist/esm/components/CnSavedViewsPanel/CnSavedViewsPanel.vue3.js +270 -0
  69. package/dist/esm/components/CnSavedViewsPanel/CnSavedViewsPanel.vue3.js.map +1 -0
  70. package/dist/esm/index.js +4 -0
  71. package/dist/esm/index.js.map +1 -1
  72. package/dist/esm/l10n/en.json.js +56 -1
  73. package/dist/esm/l10n/en.json.js.map +1 -1
  74. package/dist/esm/l10n/nl.json.js +56 -1
  75. package/dist/esm/l10n/nl.json.js.map +1 -1
  76. package/dist/esm/library-style.js +1 -1
  77. package/dist/esm/mixins/savedViewsList.js +235 -0
  78. package/dist/esm/mixins/savedViewsList.js.map +1 -0
  79. package/dist/esm/nextcloud-vue.css +348 -108
  80. package/dist/esm/utils/actionsDispatcher.js +2 -2
  81. package/dist/esm/utils/actionsDispatcher.js.map +1 -1
  82. package/dist/esm/utils/builtInModals.js +39 -0
  83. package/dist/esm/utils/builtInModals.js.map +1 -0
  84. package/dist/esm/utils/mapMarkerColor.js +121 -0
  85. package/dist/esm/utils/mapMarkerColor.js.map +1 -0
  86. package/dist/esm/utils/mergeManifestDelta.js +4 -0
  87. package/dist/esm/utils/mergeManifestDelta.js.map +1 -1
  88. package/dist/esm/utils/savedViewHelpers.js +59 -7
  89. package/dist/esm/utils/savedViewHelpers.js.map +1 -1
  90. package/dist/esm/utils/savedViewMenu.js +154 -0
  91. package/dist/esm/utils/savedViewMenu.js.map +1 -0
  92. package/dist/esm/utils/searchGroupSharees.js +50 -15
  93. package/dist/esm/utils/searchGroupSharees.js.map +1 -1
  94. package/dist/esm/utils/validateManifestV2.compiled.js +3 -3
  95. package/dist/esm/utils/validateManifestV2.compiled.js.map +1 -1
  96. package/dist/nextcloud-vue.cjs.js +13351 -10855
  97. package/dist/nextcloud-vue.cjs.js.map +2 -2
  98. package/dist/nextcloud-vue.css +348 -108
  99. package/l10n/en.json +56 -1
  100. package/l10n/nl.json +56 -1
  101. package/package.json +1 -1
  102. package/src/components/CnActionDialog/CnActionDialog.vue +545 -0
  103. package/src/components/CnActionDialog/index.js +12 -0
  104. package/src/components/CnAppNav/CnAppNav.vue +17 -0
  105. package/src/components/CnAppRoot/CnAppRoot.vue +102 -2
  106. package/src/components/CnDashboardPage/CnDashboardPage.vue +86 -7
  107. package/src/components/CnIndexPage/CnIndexPage.vue +409 -23
  108. package/src/components/CnIndexPage/useSelfFetchList.js +21 -1
  109. package/src/components/CnIndexSidebar/CnIndexSidebar.md +2 -0
  110. package/src/components/CnIndexSidebar/CnIndexSidebar.vue +91 -1
  111. package/src/components/CnMapWidget/CnMapWidget.vue +16 -1
  112. package/src/components/CnPartyPicker/CnPartyPicker.vue +317 -0
  113. package/src/components/CnPartyPicker/index.js +11 -0
  114. package/src/components/CnSavedViewEditDialog/CnSavedViewEditDialog.vue +192 -0
  115. package/src/components/CnSavedViewEditDialog/index.js +11 -0
  116. package/src/components/CnSavedViewShareDialog/CnSavedViewShareDialog.vue +21 -6
  117. package/src/components/CnSavedViewShareFields/CnSavedViewShareFields.vue +130 -37
  118. package/src/components/CnSavedViewsControl/CnSavedViewsControl.vue +36 -599
  119. package/src/components/CnSavedViewsPanel/CnSavedViewsPanel.vue +459 -0
  120. package/src/components/CnSavedViewsPanel/index.js +11 -0
  121. package/src/components/index.js +4 -0
  122. package/src/css/index-page.css +16 -0
  123. package/src/css/look-board-index.css +5 -3
  124. package/src/css/look-board.css +18 -3
  125. package/src/index.js +4 -0
  126. package/src/mixins/savedViewsList.js +231 -0
  127. package/src/schemas/app-manifest-v2.schema.json +98 -3
  128. package/src/utils/actionsDispatcher.js +2 -2
  129. package/src/utils/builtInModals.js +35 -0
  130. package/src/utils/mapMarkerColor.js +117 -0
  131. package/src/utils/mergeManifestDelta.js +4 -0
  132. package/src/utils/savedViewHelpers.js +58 -6
  133. package/src/utils/savedViewMenu.js +150 -0
  134. package/src/utils/searchGroupSharees.js +49 -14
  135. 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
@@ -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'
@@ -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. The colours stay the
764
- status colour map's. */
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: 1.4;
772
+ line-height: normal;
771
773
  }
772
774
 
773
775
  /* The row menu column holds one 34px button between 16px of padding, so 66px
@@ -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 pill is 13px at 600 on a 3px 10px pad with a 12px corner
1088
- (DqZaak's header pills, DqZaken's status column). */
1089
- .cn-look-board .cn-status-badge:not(.cn-status-badge--small):not(.cn-look-nextcloud *) {
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,