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">
1739import type { IAccountData } from ' ../filters/AccountFilter.ts'
1840
1941import { translate as t } from ' @nextcloud/l10n'
42+ import { mdiAccountMultiple } from ' @mdi/js'
2043import { useBrowserLocation } from ' @vueuse/core'
21- import { ref , watch , watchEffect } from ' vue'
44+ import { computed , ref , watch } from ' vue'
2245import { 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
2654interface IUserSelectData {
2755 id: string
@@ -35,9 +63,41 @@ const emit = defineEmits<{
3563
3664const { currentView } = useNavigation ()
3765const currentLocation = useBrowserLocation ()
66+ const accountFilter = ref (' ' )
3867const availableAccounts = ref <IUserSelectData []>([])
3968const 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
42102watch (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 */
95158function 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 >
0 commit comments