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', 'Accounts')"
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'
2552
2653interface IUserSelectData {
2754 id: string
@@ -35,9 +62,41 @@ const emit = defineEmits<{
3562
3663const { currentView } = useNavigation ()
3764const currentLocation = useBrowserLocation ()
65+ const accountFilter = ref (' ' )
3866const availableAccounts = ref <IUserSelectData []>([])
3967const selectedAccounts = ref <IUserSelectData []>([])
4068
69+ /**
70+ * Currently shown accounts (filtered)
71+ */
72+ const shownAccounts = computed (() => {
73+ if (! accountFilter .value ) {
74+ return availableAccounts .value
75+ }
76+ const queryParts = accountFilter .value .toLocaleLowerCase ().trim ().split (' ' )
77+ return availableAccounts .value .filter ((account ) =>
78+ queryParts .every ((part ) =>
79+ account .user .toLocaleLowerCase ().includes (part )
80+ || account .displayName .toLocaleLowerCase ().includes (part ),
81+ ),
82+ )
83+ })
84+
85+ /**
86+ * Toggle an account as selected
87+ * @param accountId The account to toggle
88+ */
89+ function toggleAccount(accountId : string ) {
90+ const account = availableAccounts .value .find (({ id }) => id === accountId )
91+ if (account && selectedAccounts .value .includes (account )) {
92+ selectedAccounts .value = selectedAccounts .value .filter (({ id }) => id !== accountId )
93+ } else {
94+ if (account ) {
95+ selectedAccounts .value = [... selectedAccounts .value , account ]
96+ }
97+ }
98+ }
99+
41100// Watch selected account, on change we emit the new account data to the filter instance
42101watch (selectedAccounts , () => {
43102 // Emit selected accounts as account data
@@ -94,23 +153,31 @@ async function updateAvailableAccounts(path: string = '/') {
94153 */
95154function resetFilter() {
96155 selectedAccounts .value = []
156+ accountFilter .value = ' '
97157}
98- defineExpose ({ resetFilter })
158+ defineExpose ({ resetFilter , toggleAccount })
99159
100160// When the current view changes or the current directory,
101161// then we need to rebuild the available accounts
102- watchEffect ( () => {
162+ watch ([ currentView , currentLocation ], () => {
103163 if (currentView .value ) {
104164 // we have no access to the files router here...
105165 const path = (currentLocation .value .search ?? ' ?dir=/' ).match (/ (?<=&| \? )dir=([^ &#] + )/ )?.[1 ]
106- selectedAccounts . value = []
166+ resetFilter ()
107167 updateAvailableAccounts (decodeURIComponent (path ?? ' /' ))
108168 }
109- })
169+ }, { immediate: true } )
110170 </script >
111171
112172<style scoped lang="scss">
113173.file-list-filter-accounts {
114- max-width : 300px ;
174+ & __item {
175+ min-width : 250px ;
176+ }
177+
178+ & __avatar {
179+ // 24px is the avatar size
180+ margin : calc ((var (--default-clickable-area ) - 24px ) / 2 )
181+ }
115182}
116183 </style >
0 commit comments