2727 </template >
2828 </NcEmptyContent >
2929
30- <div v-else class =" member-grid" >
31- <MemberGridItem v-for =" member in flatList"
32- :key =" `member-grid-item-${member.id}`"
33- :member =" member"
34- :is-team =" !member.isUser" />
35- </div >
30+ <template v-else >
31+ <NcTextField v-if =" flatList.length > 20"
32+ v-model =" searchQuery"
33+ :label =" t('contacts', 'Search among current members')"
34+ trailing-button-icon =" close"
35+ :show-trailing-button =" searchQuery !== ''"
36+ @trailing-button-click =" clearSearchField" >
37+ <IconSearch :size =" 20" />
38+ </NcTextField >
39+ <RecycleScroller ref =" scroller"
40+ class =" member-scroller"
41+ :items =" filteredList"
42+ :item-size =" 56"
43+ :grid-items =" gridItems"
44+ :item-secondary-size =" itemSecondarySize" >
45+ <template #default =" { item } " >
46+ <MemberGridItem :key =" `member-grid-item-${item.id}`"
47+ :member =" item"
48+ :is-team =" !item.isUser" />
49+ </template >
50+ <template #empty v-if =" ! filteredList .length " >
51+ <div class =" empty-search-results" >
52+ <NcEmptyContent :name =" t('contacts', 'No results found')" >
53+ <template #icon >
54+ <IconSearch :size =" 20" />
55+ </template >
56+ </NcEmptyContent >
57+ </div >
58+ </template >
59+ </RecycleScroller >
60+ </template >
3661
3762 <!-- member picker -->
3863 <EntityPicker v-if =" showPicker"
5378import {
5479 NcEmptyContent ,
5580 isMobile ,
81+ NcLoadingIcon as IconLoading ,
82+ NcTextField
5683} from ' @nextcloud/vue'
84+ import { RecycleScroller } from ' vue-virtual-scroller'
85+ import ' vue-virtual-scroller/dist/vue-virtual-scroller.css'
5786
5887import MemberGridItem from ' ./MemberGridItem.vue'
5988import EntityPicker from ' ../EntityPicker/EntityPicker.vue'
6089import IconContact from ' vue-material-design-icons/AccountMultipleOutline.vue'
90+ import IconSearch from ' vue-material-design-icons/Magnify.vue'
6191
6292import RouterMixin from ' ../../mixins/RouterMixin.js'
6393
@@ -74,8 +104,12 @@ export default defineComponent({
74104 components: {
75105 EntityPicker ,
76106 IconContact ,
107+ IconLoading ,
108+ IconSearch ,
77109 MemberGridItem ,
78110 NcEmptyContent ,
111+ RecycleScroller ,
112+ NcTextField ,
79113 },
80114
81115 mixins: [isMobile , RouterMixin ],
@@ -103,6 +137,8 @@ export default defineComponent({
103137 pickerData: [],
104138 pickerSelection: {},
105139 pickerTypes: CIRCLES_MEMBER_GROUPING ,
140+ windowWidth: window .innerWidth ,
141+ searchQuery: ' ' ,
106142 }
107143 },
108144
@@ -134,17 +170,59 @@ export default defineComponent({
134170 return [... teams , ... users ]
135171 },
136172
173+ filteredList() {
174+ const query = this .searchQuery .toLowerCase ()
175+
176+ return this .flatList .filter (member =>
177+ ! this .searchQuery || member .displayName .toLowerCase ().includes (query )
178+ )
179+ },
180+
137181 hasMembers() {
138182 return this .flatList .length > 0
139183 },
184+
185+ gridItems() {
186+ if (this .windowWidth < 768 ) {
187+ // undefined means that the grid will be rendered as a list
188+ return undefined
189+ }
190+
191+ return 2
192+ },
193+
194+ itemSecondarySize() {
195+ if (this .windowWidth < 768 ) {
196+ // undefined means that the grid will be rendered as a list
197+ return undefined
198+ }
199+
200+ // The maximum width of the member list is 500px,
201+ // so with two columns, each column is 242px wide (with scroll)
202+ return 242
203+ },
140204 },
141205
142206 mounted() {
143207 subscribe (' contacts:circles:append' , this .onShowPicker )
144208 subscribe (' guests:user:created' , this .onGuestCreated )
209+
210+ window .addEventListener (' resize' , this .onResize )
211+ },
212+
213+ beforeDestroy() {
214+ window .removeEventListener (' resize' , this .onResize )
145215 },
146216
147217 methods: {
218+ onResize() {
219+ this .windowWidth = window .innerWidth
220+ },
221+
222+ clearSearchField() {
223+ this .searchQuery = ' '
224+ },
225+
148226 /**
149227 * Show picker and fetch for recommendations
150228 * Cache the circleId in case the url change or something
@@ -260,21 +338,16 @@ export default defineComponent({
260338 }
261339}
262340
263- .member-grid {
264- display : grid ;
265- grid-template-columns : repeat (2 , 1fr );
266- gap : 8px ;
267-
268- @media (max-width : 768px ) {
269- grid-template-columns : 1fr ;
270- }
271-
272- @media (min-width : 1200px ) {
273- grid-template-columns : repeat (3 , 1fr );
274- }
341+ .member-scroller {
342+ height : 100% ;
343+ max-height : 200px ;
275344}
276345
277346.empty-content {
278347 height : 100% ;
279348}
349+
350+ .empty-search-results {
351+ margin-top : 2rem ;
352+ }
280353 </style >
0 commit comments