@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,192 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
- SPDX-FileCopyrightText: 2026 Conduction B.V.
|
|
3
|
+
- SPDX-License-Identifier: EUPL-1.2
|
|
4
|
+
-->
|
|
5
|
+
<template>
|
|
6
|
+
<NcDialog
|
|
7
|
+
:name="resolvedTitle"
|
|
8
|
+
size="small"
|
|
9
|
+
:noClose="loading"
|
|
10
|
+
@closing="onClose">
|
|
11
|
+
<div class="cn-edit-view" data-testid="cn-modal" data-testid-modal="cn-saved-view-edit-dialog">
|
|
12
|
+
<NcNoteCard v-if="error" type="error">
|
|
13
|
+
{{ error }}
|
|
14
|
+
</NcNoteCard>
|
|
15
|
+
<p class="cn-edit-view__intro">
|
|
16
|
+
{{ t('nextcloud-vue', 'Give the view another name, or replace its criteria with what the list shows now.') }}
|
|
17
|
+
</p>
|
|
18
|
+
<NcTextField
|
|
19
|
+
:modelValue="name"
|
|
20
|
+
:label="t('nextcloud-vue', 'Name')"
|
|
21
|
+
:labelVisible="true"
|
|
22
|
+
data-testid="cn-saved-view-edit-name"
|
|
23
|
+
@update:modelValue="(v) => name = v" />
|
|
24
|
+
<fieldset class="cn-edit-view__criteria" data-testid="cn-saved-view-edit-replace">
|
|
25
|
+
<legend>{{ t('nextcloud-vue', 'Criteria') }}</legend>
|
|
26
|
+
<NcCheckboxRadioSwitch
|
|
27
|
+
type="radio"
|
|
28
|
+
name="cn-edit-view-criteria"
|
|
29
|
+
value="keep"
|
|
30
|
+
:modelValue="replaceCriteria ? 'replace' : 'keep'"
|
|
31
|
+
data-testid="cn-saved-view-edit-keep"
|
|
32
|
+
@update:modelValue="replaceCriteria = false">
|
|
33
|
+
{{ t('nextcloud-vue', 'Keep what is saved') }}
|
|
34
|
+
</NcCheckboxRadioSwitch>
|
|
35
|
+
<NcCheckboxRadioSwitch
|
|
36
|
+
type="radio"
|
|
37
|
+
name="cn-edit-view-criteria"
|
|
38
|
+
value="replace"
|
|
39
|
+
:modelValue="replaceCriteria ? 'replace' : 'keep'"
|
|
40
|
+
data-testid="cn-saved-view-edit-replace-current"
|
|
41
|
+
@update:modelValue="replaceCriteria = true">
|
|
42
|
+
{{ t('nextcloud-vue', 'Replace with the current search term, filters and sort') }}
|
|
43
|
+
</NcCheckboxRadioSwitch>
|
|
44
|
+
</fieldset>
|
|
45
|
+
<p v-if="isShared" class="cn-edit-view__note">
|
|
46
|
+
{{ t('nextcloud-vue', 'Whoever you share the view with sees the change at once.') }}
|
|
47
|
+
</p>
|
|
48
|
+
</div>
|
|
49
|
+
<template #actions>
|
|
50
|
+
<NcButton :disabled="loading" @click="onClose">
|
|
51
|
+
{{ t('nextcloud-vue', 'Cancel') }}
|
|
52
|
+
</NcButton>
|
|
53
|
+
<NcButton
|
|
54
|
+
variant="primary"
|
|
55
|
+
:disabled="loading || !name.trim()"
|
|
56
|
+
data-testid="cn-saved-view-edit-confirm"
|
|
57
|
+
@click="onConfirm">
|
|
58
|
+
<template #icon>
|
|
59
|
+
<NcLoadingIcon v-if="loading" :size="20" :name="t('nextcloud-vue', 'Loading …')" />
|
|
60
|
+
</template>
|
|
61
|
+
{{ t('nextcloud-vue', 'Save') }}
|
|
62
|
+
</NcButton>
|
|
63
|
+
</template>
|
|
64
|
+
</NcDialog>
|
|
65
|
+
</template>
|
|
66
|
+
|
|
67
|
+
<script>
|
|
68
|
+
import { translate as t } from '@nextcloud/l10n'
|
|
69
|
+
import { NcButton, NcCheckboxRadioSwitch, NcDialog, NcLoadingIcon, NcNoteCard, NcTextField } from '@nextcloud/vue'
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* CnSavedViewEditDialog — rename an own saved view and, when asked, replace
|
|
73
|
+
* its criteria with the filters, search and sort the list shows now. Opened by
|
|
74
|
+
* `CnIndexPage` from the Edit entry of `CnSavedViewsControl`. Emits
|
|
75
|
+
* `confirm({ name, replaceCriteria })`; the parent saves and either closes the
|
|
76
|
+
* dialog or reports a failure through `setError()`, which keeps the form open.
|
|
77
|
+
*
|
|
78
|
+
* @spec openspec/changes/saved-views-manage-and-defaults/tasks.md#task-3
|
|
79
|
+
*/
|
|
80
|
+
export default {
|
|
81
|
+
name: 'CnSavedViewEditDialog',
|
|
82
|
+
|
|
83
|
+
components: { NcButton, NcCheckboxRadioSwitch, NcDialog, NcLoadingIcon, NcNoteCard, NcTextField },
|
|
84
|
+
|
|
85
|
+
props: {
|
|
86
|
+
/** The view being edited (its `name` fills the field). */
|
|
87
|
+
view: {
|
|
88
|
+
type: Object,
|
|
89
|
+
default: () => ({}),
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
/** Dialog title; empty reads "Edit view: {name}". */
|
|
93
|
+
dialogTitle: {
|
|
94
|
+
type: String,
|
|
95
|
+
default: '',
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
emits: ['close', 'confirm'],
|
|
100
|
+
|
|
101
|
+
data() {
|
|
102
|
+
return {
|
|
103
|
+
name: (this.view && typeof this.view.name === 'string') ? this.view.name : '',
|
|
104
|
+
replaceCriteria: false,
|
|
105
|
+
loading: false,
|
|
106
|
+
error: '',
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
computed: {
|
|
111
|
+
/** @return {string} The title. */
|
|
112
|
+
resolvedTitle() {
|
|
113
|
+
return this.dialogTitle || t('nextcloud-vue', 'Edit view: {name}', { name: (this.view && this.view.name) || '' })
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
/** @return {boolean} Whether the view is shared with anyone. */
|
|
117
|
+
isShared() {
|
|
118
|
+
return Array.isArray(this.view && this.view.sharedWith) && this.view.sharedWith.length > 0
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
methods: {
|
|
123
|
+
t,
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Save clicked with a non-empty name.
|
|
127
|
+
*
|
|
128
|
+
* @spec openspec/changes/saved-views-manage-and-defaults/tasks.md#task-3
|
|
129
|
+
*/
|
|
130
|
+
onConfirm() {
|
|
131
|
+
if (!this.name.trim()) {
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
this.loading = true
|
|
135
|
+
this.error = ''
|
|
136
|
+
/**
|
|
137
|
+
* @event confirm Save clicked. Payload: `{ name, replaceCriteria }`.
|
|
138
|
+
* @type {object}
|
|
139
|
+
*/
|
|
140
|
+
this.$emit('confirm', { name: this.name.trim(), replaceCriteria: this.replaceCriteria })
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
onClose() {
|
|
144
|
+
/** @event close Dialog dismissed. */
|
|
145
|
+
this.$emit('close')
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Parent-callable (via ref): show a refused save and re-enable the form.
|
|
150
|
+
*
|
|
151
|
+
* @param {string} message The server's message.
|
|
152
|
+
* @public
|
|
153
|
+
*/
|
|
154
|
+
setError(message) {
|
|
155
|
+
this.loading = false
|
|
156
|
+
this.error = message || t('nextcloud-vue', 'Could not save the view')
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
}
|
|
160
|
+
</script>
|
|
161
|
+
|
|
162
|
+
<style scoped>
|
|
163
|
+
.cn-edit-view__intro,
|
|
164
|
+
.cn-edit-view__note {
|
|
165
|
+
margin: 0;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.cn-edit-view__note {
|
|
169
|
+
color: var(--color-text-maxcontrast);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.cn-edit-view__criteria {
|
|
173
|
+
display: flex;
|
|
174
|
+
flex-direction: column;
|
|
175
|
+
gap: 4px;
|
|
176
|
+
border: 0;
|
|
177
|
+
margin: 0;
|
|
178
|
+
padding: 0;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.cn-edit-view__criteria legend {
|
|
182
|
+
font-weight: bold;
|
|
183
|
+
margin-bottom: 4px;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.cn-edit-view {
|
|
187
|
+
display: flex;
|
|
188
|
+
flex-direction: column;
|
|
189
|
+
gap: 12px;
|
|
190
|
+
padding-block: 8px;
|
|
191
|
+
}
|
|
192
|
+
</style>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SPDX-FileCopyrightText: 2026 Conduction B.V.
|
|
3
|
+
* SPDX-License-Identifier: EUPL-1.2
|
|
4
|
+
*
|
|
5
|
+
* CnSavedViewEditDialog — rename a saved view, optionally with the current filters.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import CnSavedViewEditDialog from './CnSavedViewEditDialog.vue'
|
|
9
|
+
|
|
10
|
+
export { CnSavedViewEditDialog }
|
|
11
|
+
export default CnSavedViewEditDialog
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
-->
|
|
5
5
|
<template>
|
|
6
6
|
<NcDialog
|
|
7
|
-
:name="
|
|
7
|
+
:name="resolvedTitle"
|
|
8
8
|
size="small"
|
|
9
9
|
:noClose="loading"
|
|
10
10
|
@closing="onClose">
|
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
<NcNoteCard v-if="error" type="error">
|
|
13
13
|
{{ error }}
|
|
14
14
|
</NcNoteCard>
|
|
15
|
+
<p class="cn-share-view__intro">
|
|
16
|
+
{{ t('nextcloud-vue', 'Whoever gets the view sees it under Shared with me. It is yours and stays yours.') }}
|
|
17
|
+
</p>
|
|
15
18
|
<CnSavedViewShareFields v-model="sharedWith" />
|
|
16
19
|
</div>
|
|
17
20
|
<template #actions>
|
|
@@ -36,6 +39,7 @@
|
|
|
36
39
|
import { translate as t } from '@nextcloud/l10n'
|
|
37
40
|
import { NcButton, NcDialog, NcLoadingIcon, NcNoteCard } from '@nextcloud/vue'
|
|
38
41
|
import CnSavedViewShareFields from '../CnSavedViewShareFields/CnSavedViewShareFields.vue'
|
|
42
|
+
import { normalizeSharedWith } from '../../utils/savedViewHelpers.js'
|
|
39
43
|
|
|
40
44
|
/**
|
|
41
45
|
* CnSavedViewShareDialog — change who a saved view is shared with. Opened by
|
|
@@ -55,10 +59,10 @@ export default {
|
|
|
55
59
|
default: () => ({}),
|
|
56
60
|
},
|
|
57
61
|
|
|
58
|
-
/** Dialog title. */
|
|
62
|
+
/** Dialog title; empty reads "Share view: {name}". */
|
|
59
63
|
dialogTitle: {
|
|
60
64
|
type: String,
|
|
61
|
-
default:
|
|
65
|
+
default: '',
|
|
62
66
|
},
|
|
63
67
|
},
|
|
64
68
|
|
|
@@ -66,12 +70,19 @@ export default {
|
|
|
66
70
|
|
|
67
71
|
data() {
|
|
68
72
|
return {
|
|
69
|
-
sharedWith:
|
|
73
|
+
sharedWith: normalizeSharedWith(this.view && this.view.sharedWith),
|
|
70
74
|
loading: false,
|
|
71
75
|
error: '',
|
|
72
76
|
}
|
|
73
77
|
},
|
|
74
78
|
|
|
79
|
+
computed: {
|
|
80
|
+
/** @return {string} The title: `dialogTitle`, else "Share view: {name}". */
|
|
81
|
+
resolvedTitle() {
|
|
82
|
+
return this.dialogTitle || t('nextcloud-vue', 'Share view: {name}', { name: (this.view && this.view.name) || '' })
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
|
|
75
86
|
methods: {
|
|
76
87
|
t,
|
|
77
88
|
|
|
@@ -79,8 +90,8 @@ export default {
|
|
|
79
90
|
this.loading = true
|
|
80
91
|
this.error = ''
|
|
81
92
|
/**
|
|
82
|
-
* @event confirm Save clicked. Payload: the audience, `[{ group, mode }]` (`[]` to stop sharing).
|
|
83
|
-
* @type {Array<{group
|
|
93
|
+
* @event confirm Save clicked. Payload: the audience, `[{ group, mode }]` and `[{ user, mode }]` (`[]` to stop sharing).
|
|
94
|
+
* @type {Array<{group?: string, user?: string, mode: string}>}
|
|
84
95
|
*/
|
|
85
96
|
this.$emit('confirm', this.sharedWith)
|
|
86
97
|
},
|
|
@@ -105,6 +116,10 @@ export default {
|
|
|
105
116
|
</script>
|
|
106
117
|
|
|
107
118
|
<style scoped>
|
|
119
|
+
.cn-share-view__intro {
|
|
120
|
+
margin: 0;
|
|
121
|
+
}
|
|
122
|
+
|
|
108
123
|
.cn-share-view {
|
|
109
124
|
display: flex;
|
|
110
125
|
flex-direction: column;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<template>
|
|
6
6
|
<div v-if="available" class="cn-saved-view-share" data-testid="cn-saved-view-share">
|
|
7
7
|
<NcSelect
|
|
8
|
-
:inputLabel="t('nextcloud-vue', 'Share with
|
|
8
|
+
:inputLabel="t('nextcloud-vue', 'Share with')"
|
|
9
9
|
:modelValue="selectedOptions"
|
|
10
10
|
:options="options"
|
|
11
11
|
:multiple="true"
|
|
@@ -16,32 +16,75 @@
|
|
|
16
16
|
data-testid="cn-saved-view-share-groups"
|
|
17
17
|
@search="onSearch"
|
|
18
18
|
@update:modelValue="onGroups" />
|
|
19
|
-
<
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
19
|
+
<p class="cn-saved-view-share__hint">
|
|
20
|
+
{{ t('nextcloud-vue', 'People and groups from your Nextcloud, as when sharing a file.') }}
|
|
21
|
+
</p>
|
|
22
|
+
<template v-if="modelValue.length > 0">
|
|
23
|
+
<h4 class="cn-saved-view-share__caption">
|
|
24
|
+
{{ t('nextcloud-vue', 'Shared with') }}
|
|
25
|
+
</h4>
|
|
26
|
+
<ul class="cn-saved-view-share__modes">
|
|
27
|
+
<li v-for="entry in modelValue" :key="keyOf(entry)" class="cn-saved-view-share__mode">
|
|
28
|
+
<span class="cn-saved-view-share__who">
|
|
29
|
+
<span class="cn-saved-view-share__group">{{ labelOf(keyOf(entry)) }}</span>
|
|
30
|
+
<span class="cn-saved-view-share__kind">{{ entry.user ? t('nextcloud-vue', 'Person') : t('nextcloud-vue', 'Group') }}</span>
|
|
31
|
+
</span>
|
|
32
|
+
<span
|
|
33
|
+
class="cn-saved-view-share__rights"
|
|
34
|
+
role="radiogroup"
|
|
35
|
+
:aria-label="t('nextcloud-vue', 'Rights of {name}', { name: labelOf(keyOf(entry)) })"
|
|
36
|
+
:data-testid="`cn-saved-view-share-mode-${keyOf(entry)}`">
|
|
37
|
+
<NcCheckboxRadioSwitch
|
|
38
|
+
type="radio"
|
|
39
|
+
:name="`cn-share-mode-${keyOf(entry)}`"
|
|
40
|
+
value="read"
|
|
41
|
+
:modelValue="entry.mode === 'write' ? 'write' : 'read'"
|
|
42
|
+
:disabled="disabled"
|
|
43
|
+
@update:modelValue="onMode(keyOf(entry), 'read')">
|
|
44
|
+
{{ t('nextcloud-vue', 'May use') }}
|
|
45
|
+
</NcCheckboxRadioSwitch>
|
|
46
|
+
<NcCheckboxRadioSwitch
|
|
47
|
+
type="radio"
|
|
48
|
+
:name="`cn-share-mode-${keyOf(entry)}`"
|
|
49
|
+
value="write"
|
|
50
|
+
:modelValue="entry.mode === 'write' ? 'write' : 'read'"
|
|
51
|
+
:disabled="disabled"
|
|
52
|
+
@update:modelValue="onMode(keyOf(entry), 'write')">
|
|
53
|
+
{{ t('nextcloud-vue', 'May update') }}
|
|
54
|
+
</NcCheckboxRadioSwitch>
|
|
55
|
+
</span>
|
|
56
|
+
<NcButton
|
|
57
|
+
variant="tertiary"
|
|
58
|
+
:aria-label="t('nextcloud-vue', 'Stop sharing with {name}', { name: labelOf(keyOf(entry)) })"
|
|
59
|
+
:disabled="disabled"
|
|
60
|
+
:data-testid="`cn-saved-view-share-remove-${keyOf(entry)}`"
|
|
61
|
+
@click="onRemove(keyOf(entry))">
|
|
62
|
+
<template #icon>
|
|
63
|
+
<Close :size="20" />
|
|
64
|
+
</template>
|
|
65
|
+
</NcButton>
|
|
66
|
+
</li>
|
|
67
|
+
</ul>
|
|
68
|
+
<p class="cn-saved-view-share__hint">
|
|
69
|
+
{{ t('nextcloud-vue', 'May use: sees the view under Shared with me. May update: can also replace the criteria. Deleting and sharing stay with you.') }}
|
|
70
|
+
</p>
|
|
71
|
+
</template>
|
|
31
72
|
</div>
|
|
32
73
|
</template>
|
|
33
74
|
|
|
34
75
|
<script>
|
|
35
76
|
import { translate as t } from '@nextcloud/l10n'
|
|
36
|
-
import { NcCheckboxRadioSwitch, NcSelect } from '@nextcloud/vue'
|
|
37
|
-
import
|
|
77
|
+
import { NcButton, NcCheckboxRadioSwitch, NcSelect } from '@nextcloud/vue'
|
|
78
|
+
import Close from 'vue-material-design-icons/Close.vue'
|
|
79
|
+
import { searchUserAndGroupSharees } from '../../utils/searchGroupSharees.js'
|
|
38
80
|
|
|
39
81
|
/**
|
|
40
|
-
* CnSavedViewShareFields — the sharing section of a saved view: pick
|
|
41
|
-
* from Nextcloud's sharee API
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
82
|
+
* CnSavedViewShareFields — the sharing section of a saved view: pick people
|
|
83
|
+
* and groups from Nextcloud's sharee API, within the instance's own sharing
|
|
84
|
+
* settings, and choose per entry whether it may only read the view or also
|
|
85
|
+
* edit it. Used by `CnSaveViewDialog` and the share dialog of `CnIndexPage`.
|
|
86
|
+
* Renders nothing when the sharee API answers nobody (sharing off, or nobody
|
|
87
|
+
* the user may share with). A group is keyed by its id, a user by `user:<uid>`.
|
|
45
88
|
*
|
|
46
89
|
* Example:
|
|
47
90
|
* ```vue
|
|
@@ -51,13 +94,14 @@ import { searchGroupSharees } from '../../utils/searchGroupSharees.js'
|
|
|
51
94
|
export default {
|
|
52
95
|
name: 'CnSavedViewShareFields',
|
|
53
96
|
|
|
54
|
-
components: { NcCheckboxRadioSwitch, NcSelect },
|
|
97
|
+
components: { Close, NcButton, NcCheckboxRadioSwitch, NcSelect },
|
|
55
98
|
|
|
56
99
|
props: {
|
|
57
100
|
/**
|
|
58
|
-
* The chosen audience, `[{ group, mode }]`
|
|
101
|
+
* The chosen audience, `[{ group, mode }]` and `[{ user, mode }]` with
|
|
102
|
+
* `mode` `read` or `write`.
|
|
59
103
|
*
|
|
60
|
-
* @type {Array<{group
|
|
104
|
+
* @type {Array<{group?: string, user?: string, mode: 'read'|'write'}>}
|
|
61
105
|
*/
|
|
62
106
|
modelValue: {
|
|
63
107
|
type: Array,
|
|
@@ -78,14 +122,14 @@ export default {
|
|
|
78
122
|
options: [],
|
|
79
123
|
known: {},
|
|
80
124
|
loading: false,
|
|
81
|
-
/** False until the first search came back with at least one
|
|
125
|
+
/** False until the first search came back with at least one sharee. */
|
|
82
126
|
available: false,
|
|
83
127
|
}
|
|
84
128
|
},
|
|
85
129
|
|
|
86
130
|
computed: {
|
|
87
131
|
selectedOptions() {
|
|
88
|
-
return this.modelValue.map((e) => ({ id: e
|
|
132
|
+
return this.modelValue.map((e) => ({ id: this.keyOf(e), label: this.labelOf(this.keyOf(e)) }))
|
|
89
133
|
},
|
|
90
134
|
},
|
|
91
135
|
|
|
@@ -100,17 +144,28 @@ export default {
|
|
|
100
144
|
methods: {
|
|
101
145
|
t,
|
|
102
146
|
|
|
103
|
-
|
|
104
|
-
|
|
147
|
+
/**
|
|
148
|
+
* The key an audience entry or option goes by: the group id, or `user:<uid>`.
|
|
149
|
+
*
|
|
150
|
+
* @param {{group?: string, user?: string}} entry The entry.
|
|
151
|
+
* @return {string}
|
|
152
|
+
* @spec openspec/changes/saved-views-manage-and-defaults/tasks.md#task-5
|
|
153
|
+
*/
|
|
154
|
+
keyOf(entry) {
|
|
155
|
+
return typeof entry.user === 'string' && entry.user !== '' ? `user:${entry.user}` : entry.group
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
labelOf(key) {
|
|
159
|
+
return this.known[key] || (key.startsWith('user:') ? key.slice(5) : key)
|
|
105
160
|
},
|
|
106
161
|
|
|
107
162
|
async onSearch(query) {
|
|
108
163
|
this.loading = true
|
|
109
|
-
const found = await
|
|
164
|
+
const found = await searchUserAndGroupSharees(query || '')
|
|
110
165
|
this.loading = false
|
|
111
|
-
this.options = found
|
|
112
|
-
|
|
113
|
-
this.known = { ...this.known, [
|
|
166
|
+
this.options = found.map((s) => ({ id: s.kind === 'user' ? `user:${s.id}` : s.id, label: s.label }))
|
|
167
|
+
this.options.forEach((o) => {
|
|
168
|
+
this.known = { ...this.known, [o.id]: o.label }
|
|
114
169
|
})
|
|
115
170
|
if (found.length > 0) {
|
|
116
171
|
this.available = true
|
|
@@ -120,15 +175,27 @@ export default {
|
|
|
120
175
|
onGroups(list) {
|
|
121
176
|
const groups = Array.isArray(list) ? list : []
|
|
122
177
|
const next = groups.map((g) => {
|
|
123
|
-
const existing = this.modelValue.find((e) => e
|
|
124
|
-
|
|
178
|
+
const existing = this.modelValue.find((e) => this.keyOf(e) === g.id)
|
|
179
|
+
if (existing) {
|
|
180
|
+
return existing
|
|
181
|
+
}
|
|
182
|
+
return g.id.startsWith('user:') ? { user: g.id.slice(5), mode: 'read' } : { group: g.id, mode: 'read' }
|
|
125
183
|
})
|
|
126
|
-
/** @event update:modelValue The audience, `[{ group, mode }]`; `[]` when
|
|
184
|
+
/** @event update:modelValue The audience, `[{ group, mode }]` and `[{ user, mode }]`; `[]` when nobody is picked. */
|
|
127
185
|
this.$emit('update:modelValue', next)
|
|
128
186
|
},
|
|
129
187
|
|
|
130
|
-
|
|
131
|
-
|
|
188
|
+
/**
|
|
189
|
+
* Stop sharing with one person or group.
|
|
190
|
+
*
|
|
191
|
+
* @param {string} key The entry's key.
|
|
192
|
+
*/
|
|
193
|
+
onRemove(key) {
|
|
194
|
+
this.$emit('update:modelValue', this.modelValue.filter((e) => this.keyOf(e) !== key))
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
onMode(key, mode) {
|
|
198
|
+
this.$emit('update:modelValue', this.modelValue.map((e) => (this.keyOf(e) === key ? { ...e, mode } : e)))
|
|
132
199
|
},
|
|
133
200
|
},
|
|
134
201
|
}
|
|
@@ -145,6 +212,32 @@ export default {
|
|
|
145
212
|
display: flex;
|
|
146
213
|
align-items: center;
|
|
147
214
|
justify-content: space-between;
|
|
148
|
-
|
|
215
|
+
flex-wrap: wrap;
|
|
216
|
+
gap: 8px 12px;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.cn-saved-view-share__who {
|
|
220
|
+
display: flex;
|
|
221
|
+
flex-direction: column;
|
|
222
|
+
flex: 1 1 160px;
|
|
223
|
+
min-width: 0;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.cn-saved-view-share__kind,
|
|
227
|
+
.cn-saved-view-share__hint {
|
|
228
|
+
margin: 0;
|
|
229
|
+
font-size: var(--font-size-small, 13px);
|
|
230
|
+
color: var(--color-text-maxcontrast);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.cn-saved-view-share__rights {
|
|
234
|
+
display: flex;
|
|
235
|
+
gap: 8px;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
.cn-saved-view-share__caption {
|
|
239
|
+
margin: 8px 0 0;
|
|
240
|
+
font-size: var(--default-font-size);
|
|
241
|
+
font-weight: bold;
|
|
149
242
|
}
|
|
150
243
|
</style>
|