Skip to content

Commit 412661a

Browse files
authored
Merge pull request #46857 from nextcloud/fix/file-list-filter
fix(files_sharing): Adjust design of account filter for file list
2 parents 8b4340a + 6711d65 commit 412661a

File tree

180 files changed

+391
-246
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

180 files changed

+391
-246
lines changed

apps/files/src/components/FileListFilters.vue

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,14 @@
1414
<NcChip :aria-label-close="t('files', 'Remove filter')"
1515
:icon-svg="chip.icon"
1616
:text="chip.text"
17-
@close="chip.onclick" />
17+
@close="chip.onclick">
18+
<template v-if="chip.user" #icon>
19+
<NcAvatar disable-menu
20+
:show-user-status="false"
21+
:size="24"
22+
:user="chip.user" />
23+
</template>
24+
</NcChip>
1825
</li>
1926
</ul>
2027
</div>
@@ -25,6 +32,7 @@ import { t } from '@nextcloud/l10n'
2532
import { computed, ref, watchEffect } from 'vue'
2633
import { useFiltersStore } from '../store/filters.ts'
2734
35+
import NcAvatar from '@nextcloud/vue/dist/Components/NcAvatar.js'
2836
import NcChip from '@nextcloud/vue/dist/Components/NcChip.js'
2937
3038
const filterStore = useFiltersStore()

apps/files/src/store/filters.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ export const useFiltersStore = defineStore('keyboard', {
6565
onFilterUpdateChips(event: FilterUpdateChipsEvent) {
6666
const id = (event.target as IFileListFilter).id
6767
this.chips = { ...this.chips, [id]: [...event.detail] }
68+
69+
logger.debug('File list filter chips updated', { filter: id, chips: event.detail })
6870
},
6971

7072
init() {

apps/files_sharing/src/components/FileListFilterAccount.vue

Lines changed: 86 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,53 @@
33
- SPDX-License-Identifier: AGPL-3.0-or-later
44
-->
55
<template>
6-
<NcSelect v-model="selectedAccounts"
7-
:aria-label-combobox="t('files_sharing', 'Accounts')"
8-
class="file-list-filter-accounts"
9-
multiple
10-
no-wrap
11-
:options="availableAccounts"
12-
:placeholder="t('files_sharing', 'Accounts')"
13-
user-select />
6+
<FileListFilter class="file-list-filter-accounts"
7+
:is-active="selectedAccounts.length > 0"
8+
:filter-name="t('files', 'People')"
9+
@reset-filter="resetFilter">
10+
<template #icon>
11+
<NcIconSvgWrapper :path="mdiAccountMultiple" />
12+
</template>
13+
<NcActionInput v-if="availableAccounts.length > 1"
14+
:label="t('files_sharing', 'Filter accounts')"
15+
:label-outside="false"
16+
:show-trailing-button="false"
17+
type="search"
18+
:value.sync="accountFilter" />
19+
<NcActionButton v-for="account of shownAccounts"
20+
:key="account.id"
21+
class="file-list-filter-accounts__item"
22+
type="radio"
23+
:model-value="selectedAccounts.includes(account)"
24+
:value="account.id"
25+
@click="toggleAccount(account.id)">
26+
<template #icon>
27+
<NcAvatar class="file-list-filter-accounts__avatar"
28+
v-bind="account"
29+
:size="24"
30+
disable-menu
31+
:show-user-status="false" />
32+
</template>
33+
{{ account.displayName }}
34+
</NcActionButton>
35+
</FileListFilter>
1436
</template>
1537

1638
<script setup lang="ts">
1739
import type { IAccountData } from '../filters/AccountFilter.ts'
1840
1941
import { translate as t } from '@nextcloud/l10n'
42+
import { mdiAccountMultiple } from '@mdi/js'
2043
import { useBrowserLocation } from '@vueuse/core'
21-
import { ref, watch, watchEffect } from 'vue'
44+
import { computed, ref, watch } from 'vue'
2245
import { useNavigation } from '../../../files/src/composables/useNavigation.ts'
2346
24-
import NcSelect from '@nextcloud/vue/dist/Components/NcSelect.js'
47+
import FileListFilter from '../../../files/src/components/FileListFilter/FileListFilter.vue'
48+
import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
49+
import NcActionInput from '@nextcloud/vue/dist/Components/NcActionInput.js'
50+
import NcAvatar from '@nextcloud/vue/dist/Components/NcAvatar.js'
51+
import NcIconSvgWrapper from '@nextcloud/vue/dist/Components/NcIconSvgWrapper.js'
52+
import { ShareType } from '@nextcloud/sharing'
2553
2654
interface IUserSelectData {
2755
id: string
@@ -35,9 +63,41 @@ const emit = defineEmits<{
3563
3664
const { currentView } = useNavigation()
3765
const currentLocation = useBrowserLocation()
66+
const accountFilter = ref('')
3867
const availableAccounts = ref<IUserSelectData[]>([])
3968
const selectedAccounts = ref<IUserSelectData[]>([])
4069
70+
/**
71+
* Currently shown accounts (filtered)
72+
*/
73+
const shownAccounts = computed(() => {
74+
if (!accountFilter.value) {
75+
return availableAccounts.value
76+
}
77+
const queryParts = accountFilter.value.toLocaleLowerCase().trim().split(' ')
78+
return availableAccounts.value.filter((account) =>
79+
queryParts.every((part) =>
80+
account.user.toLocaleLowerCase().includes(part)
81+
|| account.displayName.toLocaleLowerCase().includes(part),
82+
),
83+
)
84+
})
85+
86+
/**
87+
* Toggle an account as selected
88+
* @param accountId The account to toggle
89+
*/
90+
function toggleAccount(accountId: string) {
91+
const account = availableAccounts.value.find(({ id }) => id === accountId)
92+
if (account && selectedAccounts.value.includes(account)) {
93+
selectedAccounts.value = selectedAccounts.value.filter(({ id }) => id !== accountId)
94+
} else {
95+
if (account) {
96+
selectedAccounts.value = [...selectedAccounts.value, account]
97+
}
98+
}
99+
}
100+
41101
// Watch selected account, on change we emit the new account data to the filter instance
42102
watch(selectedAccounts, () => {
43103
// Emit selected accounts as account data
@@ -75,6 +135,9 @@ async function updateAvailableAccounts(path: string = '/') {
75135
if (sharee.id === '') {
76136
continue
77137
}
138+
if (sharee.type !== ShareType.User && sharee.type !== ShareType.Remote) {
139+
continue
140+
}
78141
// Add if not already added
79142
if (!available.has(sharee.id)) {
80143
available.set(sharee.id, {
@@ -94,23 +157,31 @@ async function updateAvailableAccounts(path: string = '/') {
94157
*/
95158
function resetFilter() {
96159
selectedAccounts.value = []
160+
accountFilter.value = ''
97161
}
98-
defineExpose({ resetFilter })
162+
defineExpose({ resetFilter, toggleAccount })
99163
100164
// When the current view changes or the current directory,
101165
// then we need to rebuild the available accounts
102-
watchEffect(() => {
166+
watch([currentView, currentLocation], () => {
103167
if (currentView.value) {
104168
// we have no access to the files router here...
105169
const path = (currentLocation.value.search ?? '?dir=/').match(/(?<=&|\?)dir=([^&#]+)/)?.[1]
106-
selectedAccounts.value = []
170+
resetFilter()
107171
updateAvailableAccounts(decodeURIComponent(path ?? '/'))
108172
}
109-
})
173+
}, { immediate: true })
110174
</script>
111175

112176
<style scoped lang="scss">
113177
.file-list-filter-accounts {
114-
max-width: 300px;
178+
&__item {
179+
min-width: 250px;
180+
}
181+
182+
&__avatar {
183+
// 24px is the avatar size
184+
margin: calc((var(--default-clickable-area) - 24px) / 2)
185+
}
115186
}
116187
</style>

apps/files_sharing/src/filters/AccountFilter.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
33
* SPDX-License-Identifier: AGPL-3.0-or-later
44
*/
5-
import type { INode } from '@nextcloud/files'
5+
import type { IFileListFilterChip, INode } from '@nextcloud/files'
66

77
import { FileListFilter, registerFileListFilter } from '@nextcloud/files'
88
import Vue from 'vue'
@@ -13,12 +13,14 @@ export interface IAccountData {
1313
displayName: string
1414
}
1515

16+
type CurrentInstance = Vue & { resetFilter: () => void, toggleAccount: (account: string) => void }
17+
1618
/**
1719
* File list filter to filter by owner / sharee
1820
*/
1921
class AccountFilter extends FileListFilter {
2022

21-
private currentInstance?: Vue
23+
private currentInstance?: CurrentInstance
2224
private filterAccounts?: IAccountData[]
2325

2426
constructor() {
@@ -35,7 +37,7 @@ class AccountFilter extends FileListFilter {
3537
el,
3638
})
3739
.$on('update:accounts', this.setAccounts.bind(this))
38-
.$mount()
40+
.$mount() as CurrentInstance
3941
}
4042

4143
public filter(nodes: INode[]): INode[] {
@@ -66,6 +68,16 @@ class AccountFilter extends FileListFilter {
6668

6769
public setAccounts(accounts?: IAccountData[]) {
6870
this.filterAccounts = accounts
71+
let chips: IFileListFilterChip[] = []
72+
if (this.filterAccounts && this.filterAccounts.length > 0) {
73+
chips = this.filterAccounts.map(({ displayName, uid }) => ({
74+
text: displayName,
75+
user: uid,
76+
onclick: () => this.currentInstance?.toggleAccount(uid),
77+
}))
78+
}
79+
80+
this.updateChips(chips)
6981
this.filterUpdated()
7082
}
7183

dist/1521-1521.js.license

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ This file is generated from multiple sources. Included packages:
3636
- version: 3.0.1
3737
- license: GPL-3.0-or-later
3838
- @nextcloud/vue
39-
- version: 8.15.0
39+
- version: 8.15.1
4040
- license: AGPL-3.0-or-later
4141
- @vueuse/core
4242
- version: 10.11.0

dist/1642-1642.js.license

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ This file is generated from multiple sources. Included packages:
5656
- version: 3.0.1
5757
- license: GPL-3.0-or-later
5858
- @nextcloud/vue
59-
- version: 8.15.0
59+
- version: 8.15.1
6060
- license: AGPL-3.0-or-later
6161
- @vueuse/core
6262
- version: 10.11.0

dist/2452-2452.js.license

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ This file is generated from multiple sources. Included packages:
5050
- version: 3.0.1
5151
- license: GPL-3.0-or-later
5252
- @nextcloud/vue
53-
- version: 8.15.0
53+
- version: 8.15.1
5454
- license: AGPL-3.0-or-later
5555
- @vueuse/core
5656
- version: 10.11.0

dist/2812-2812.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/2812-2812.js.license

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ This file is generated from multiple sources. Included packages:
134134
- version: 3.25.0
135135
- license: MIT
136136
- @nextcloud/vue
137-
- version: 8.15.0
137+
- version: 8.15.1
138138
- license: AGPL-3.0-or-later
139139
- @ungap/structured-clone
140140
- version: 1.2.0

dist/2812-2812.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)