@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,150 @@
1
+ // SPDX-FileCopyrightText: 2026 Conduction B.V.
2
+ // SPDX-License-Identifier: EUPL-1.2
3
+
4
+ import { translate as t } from '@nextcloud/l10n'
5
+
6
+ /**
7
+ * Saved views a user added to the app menu.
8
+ *
9
+ * Per user, in the app's preferences (`cn_saved_view_menu`), as a list of
10
+ * `{ id, page, name }`: the view id (or `default:<slug>`), the route name of
11
+ * the index page it filters, and the name to show. The view itself is not
12
+ * touched, so a view shared with the user can be added like an own one, and
13
+ * removing it from the menu never deletes it.
14
+ *
15
+ * A menu entry links to its page with `?savedView=<id>`; the page applies
16
+ * that view. The entries form the "My views" group at the bottom of the menu.
17
+ *
18
+ * @spec openspec/changes/saved-views-manage-and-defaults/tasks.md#task-7
19
+ */
20
+
21
+ /** The preference key the list is stored under. */
22
+ export const SAVED_VIEW_MENU_KEY = 'cn_saved_view_menu'
23
+
24
+ /** The id of the "My views" caption. */
25
+ export const SAVED_VIEW_MENU_CAPTION = 'saved-views-caption'
26
+
27
+ /** The id prefix of a menu entry made from a saved view. */
28
+ export const SAVED_VIEW_MENU_PREFIX = 'saved-view-'
29
+
30
+ /**
31
+ * The stored list, cleaned: entries with an id and a page, no repeats.
32
+ *
33
+ * @param {unknown} entries The stored value.
34
+ * @return {Array<{id: string, page: string, name: string}>} The list.
35
+ */
36
+ export function normalizeSavedViewMenu(entries) {
37
+ const seen = new Set()
38
+ const out = []
39
+ for (const e of Array.isArray(entries) ? entries : []) {
40
+ const id = e && (typeof e.id === 'string' || typeof e.id === 'number') ? String(e.id) : ''
41
+ const page = e && typeof e.page === 'string' ? e.page : ''
42
+ if (id === '' || page === '' || seen.has(`${page}|${id}`)) {
43
+ continue
44
+ }
45
+ seen.add(`${page}|${id}`)
46
+ out.push({ id, page, name: e && typeof e.name === 'string' && e.name !== '' ? e.name : id })
47
+ }
48
+ return out
49
+ }
50
+
51
+ /**
52
+ * Add an entry, or remove it when it is already there.
53
+ *
54
+ * @param {Array<object>} entries The current list.
55
+ * @param {{id: string, page: string, name: string}} entry The view.
56
+ * @return {Array<{id: string, page: string, name: string}>} The new list.
57
+ */
58
+ export function toggleSavedViewMenuEntry(entries, entry) {
59
+ const list = normalizeSavedViewMenu(entries)
60
+ const id = String(entry.id)
61
+ const present = list.some((e) => e.id === id && e.page === entry.page)
62
+ if (present) {
63
+ return list.filter((e) => !(e.id === id && e.page === entry.page))
64
+ }
65
+ return normalizeSavedViewMenu([...list, { id, page: entry.page, name: entry.name }])
66
+ }
67
+
68
+ /**
69
+ * Bring one page's entries in line with the views that page can show:
70
+ * current names, and no entry for a view that was deleted or is no longer
71
+ * shared with the user. Other pages' entries are left alone.
72
+ *
73
+ * @param {Array<object>} entries The current list.
74
+ * @param {string} page The page's route name.
75
+ * @param {Array<object>} views The views the page lists (defaults included).
76
+ * @return {Array<{id: string, page: string, name: string}>} The new list.
77
+ */
78
+ export function syncSavedViewMenu(entries, page, views) {
79
+ const byId = new Map((Array.isArray(views) ? views : []).map((v) => [String(v.id || v.slug), v]))
80
+ return normalizeSavedViewMenu(entries)
81
+ .filter((e) => e.page !== page || byId.has(e.id))
82
+ .map((e) => (e.page === page ? { ...e, name: byId.get(e.id).name || e.name } : e))
83
+ }
84
+
85
+ /** The id of the line the "My views" group shows while it is empty. */
86
+ export const SAVED_VIEW_MENU_EMPTY = 'saved-views-empty'
87
+
88
+ /**
89
+ * The manifest menu with the user's saved views in it, as the standard "My
90
+ * views" group at the bottom of the menu: a caption and one entry per view,
91
+ * after every main entry and so just above the footer entries (Help,
92
+ * Advanced), which CnAppNav draws apart. Every app gets the same group in the
93
+ * same place (Ruben's design rule, 19 Oct); it replaces app-specific groups
94
+ * such as dossiq's "My case types".
95
+ *
96
+ * With `showEmpty`, an app that offers saved views shows the group also while
97
+ * the user pinned none, with one line saying how to add one, so the group can
98
+ * be found.
99
+ *
100
+ * @param {Array<object>} menu The manifest's `menu`.
101
+ * @param {Array<object>} entries The user's list.
102
+ * @param {{showEmpty?: boolean}} [options] Options.
103
+ * @return {Array<object>} A new menu, in manifest order; the given one when there is nothing to add.
104
+ * @spec openspec/changes/my-views-menu-section/specs/app-nav/spec.md#requirement-every-app-menu-ends-with-my-views
105
+ */
106
+ export function withSavedViewMenu(menu, entries, options = {}) {
107
+ const list = normalizeSavedViewMenu(entries)
108
+ if (!Array.isArray(menu) || (list.length === 0 && options.showEmpty !== true)) {
109
+ return menu
110
+ }
111
+ const sorted = menu.map((item, index) => ({ item, index })).sort((a, b) => byOrder(a.item, b.item) || a.index - b.index).map(({ item }) => item)
112
+ const group = [
113
+ { id: SAVED_VIEW_MENU_CAPTION, type: 'caption', label: t('nextcloud-vue', 'My views') },
114
+ ...(list.length > 0
115
+ ? list.map((e) => ({
116
+ id: `${SAVED_VIEW_MENU_PREFIX}${e.id}`,
117
+ label: e.name,
118
+ route: e.page,
119
+ query: { savedView: e.id },
120
+ icon: 'FilterOutline',
121
+ }))
122
+ : [{ id: SAVED_VIEW_MENU_EMPTY, type: 'hint', label: t('nextcloud-vue', 'Add a view to the menu from Filters') }]),
123
+ ]
124
+ // Orders after the last ordered entry, so CnAppNav's own sort keeps the
125
+ // group at the bottom.
126
+ const orders = sorted.map((item) => item && item.order).filter((order) => typeof order === 'number')
127
+ if (orders.length > 0) {
128
+ const start = Math.max(...orders)
129
+ group.forEach((item, k) => {
130
+ item.order = start + k + 1
131
+ })
132
+ }
133
+ return [...sorted, ...group]
134
+ }
135
+
136
+ /**
137
+ * Manifest menu order: entries with an `order` first, by it; the rest keep their place.
138
+ *
139
+ * @param {object} a First entry.
140
+ * @param {object} b Second entry.
141
+ * @return {number}
142
+ */
143
+ function byOrder(a, b) {
144
+ const aHas = typeof a?.order === 'number'
145
+ const bHas = typeof b?.order === 'number'
146
+ if (aHas !== bHas) {
147
+ return aHas ? -1 : 1
148
+ }
149
+ return aHas ? a.order - b.order : 0
150
+ }
@@ -12,36 +12,71 @@ import axios from '@nextcloud/axios'
12
12
  import { generateOcsUrl } from '@nextcloud/router'
13
13
 
14
14
  /**
15
- * Search the sharee API for groups. Fails soft: any error answers `[]`, and so
16
- * does an instance where sharing is off, so the caller can hide the section.
15
+ * Search the sharee API for the given share types. Fails soft: any error
16
+ * answers `[]`, and so does an instance where sharing is off.
17
17
  *
18
- * @param {string} [query] The search term (empty loads a first page).
19
- * @param {object} [options] Tuning options.
20
- * @param {number} [options.limit] Max results (default 25).
21
- * @return {Promise<Array<{id: string, label: string}>>} Group options (`id` is the group id).
18
+ * @param {string} query The search term.
19
+ * @param {Array<number>} shareTypes Nextcloud share types (0 user, 1 group).
20
+ * @param {number} limit Max results per type.
21
+ * @return {Promise<Array<{id: string, kind: 'user'|'group', label: string}>>} The sharees, exact matches first.
22
22
  */
23
- export async function searchGroupSharees(query = '', options = {}) {
24
- const { limit = 25 } = options
23
+ async function searchSharees(query, shareTypes, limit) {
25
24
  try {
26
25
  const response = await axios.get(generateOcsUrl('apps/files_sharing/api/v1/sharees'), {
27
26
  headers: { 'OCS-APIRequest': 'true', Accept: 'application/json' },
28
- params: { search: query || '', itemType: 'file', shareType: [1], perPage: limit, lookup: false, format: 'json' },
27
+ params: { search: query || '', itemType: 'file', shareType: shareTypes, perPage: limit, lookup: false, format: 'json' },
29
28
  })
30
29
  const body = response && response.data
31
30
  const data = body && body.ocs ? body.ocs.data : body
32
- const lists = [...((data && data.exact && data.exact.groups) || []), ...((data && data.groups) || [])]
31
+ const exact = (data && data.exact) || {}
32
+ const lists = [
33
+ ...(exact.users || []).map((e) => ['user', e]),
34
+ ...(exact.groups || []).map((e) => ['group', e]),
35
+ ...((data && data.users) || []).map((e) => ['user', e]),
36
+ ...((data && data.groups) || []).map((e) => ['group', e]),
37
+ ]
33
38
  const seen = new Set()
34
39
  const out = []
35
- for (const entry of lists) {
40
+ for (const [kind, entry] of lists) {
36
41
  const id = entry && entry.value ? entry.value.shareWith : undefined
37
- if (id === undefined || id === null || id === '' || seen.has(String(id))) {
42
+ if (id === undefined || id === null || id === '' || seen.has(`${kind}:${id}`)) {
38
43
  continue
39
44
  }
40
- seen.add(String(id))
41
- out.push({ id: String(id), label: entry.label || String(id) })
45
+ seen.add(`${kind}:${id}`)
46
+ out.push({ id: String(id), kind, label: entry.label || String(id) })
42
47
  }
43
48
  return out
44
49
  } catch {
45
50
  return []
46
51
  }
47
52
  }
53
+
54
+ /**
55
+ * Search the sharee API for groups. Fails soft: any error answers `[]`, and so
56
+ * does an instance where sharing is off, so the caller can hide the section.
57
+ *
58
+ * @param {string} [query] The search term (empty loads a first page).
59
+ * @param {object} [options] Tuning options.
60
+ * @param {number} [options.limit] Max results (default 25).
61
+ * @return {Promise<Array<{id: string, label: string}>>} Group options (`id` is the group id).
62
+ */
63
+ export async function searchGroupSharees(query = '', options = {}) {
64
+ const { limit = 25 } = options
65
+ const found = await searchSharees(query, [1], limit)
66
+ return found.filter((s) => s.kind === 'group').map(({ id, label }) => ({ id, label }))
67
+ }
68
+
69
+ /**
70
+ * Search the sharee API for users and groups, within the instance's own
71
+ * sharing settings (the source the Files share dialog uses).
72
+ *
73
+ * @param {string} [query] The search term (empty loads a first page).
74
+ * @param {object} [options] Tuning options.
75
+ * @param {number} [options.limit] Max results per type (default 25).
76
+ * @return {Promise<Array<{id: string, kind: 'user'|'group', label: string}>>} The sharees.
77
+ * @spec openspec/changes/saved-views-manage-and-defaults/tasks.md#task-5
78
+ */
79
+ export async function searchUserAndGroupSharees(query = '', options = {}) {
80
+ const { limit = 25 } = options
81
+ return searchSharees(query, [0, 1], limit)
82
+ }