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
32+ v-if =" flatList.length > 20"
33+ v-model =" searchQuery"
34+ :label =" t('contacts', 'Search among current members')"
35+ trailing-button-icon =" close"
36+ :show-trailing-button =" searchQuery !== ''"
37+ @trailing-button-click =" clearSearchField" >
38+ <IconSearch :size =" 20" />
39+ </NcTextField >
40+ <RecycleScroller
41+ ref =" scroller"
42+ class =" member-scroller"
43+ :items =" filteredList"
44+ :item-size =" 56"
45+ :grid-items =" gridItems"
46+ :item-secondary-size =" itemSecondarySize" >
47+ <template #default =" { item } " >
48+ <MemberGridItem
49+ :key =" `member-grid-item-${item.id}`"
50+ :member =" item"
51+ :is-team =" !item.isUser" />
52+ </template >
53+ <template #empty v-if =" ! filteredList .length " >
54+ <div style =" margin-top : 2rem ;" >
55+ <NcEmptyContent :name =" t('contacts', 'No results found')" >
56+ <template #icon >
57+ <IconSearch :size =" 20" />
58+ </template >
59+ </NcEmptyContent >
60+ </div >
61+ </template >
62+ </RecycleScroller >
63+ </template >
3664
3765 <!-- member picker -->
3866 <EntityPicker v-if =" showPicker"
5280import {
5381 NcEmptyContent ,
5482 isMobile ,
83+ NcLoadingIcon as IconLoading ,
84+ NcTextField
5585} from ' @nextcloud/vue'
86+ import { RecycleScroller } from ' vue-virtual-scroller'
87+ import ' vue-virtual-scroller/dist/vue-virtual-scroller.css'
5688
5789import MemberGridItem from ' ./MemberGridItem.vue'
5890import EntityPicker from ' ../EntityPicker/EntityPicker.vue'
5991import IconContact from ' vue-material-design-icons/AccountMultipleOutline.vue'
92+ import IconSearch from ' vue-material-design-icons/Magnify.vue'
6093
6194import RouterMixin from ' ../../mixins/RouterMixin.js'
6295
@@ -73,8 +106,12 @@ export default defineComponent({
73106 components: {
74107 EntityPicker ,
75108 IconContact ,
109+ IconLoading ,
110+ IconSearch ,
76111 MemberGridItem ,
77112 NcEmptyContent ,
113+ RecycleScroller ,
114+ NcTextField ,
78115 },
79116
80117 mixins: [isMobile , RouterMixin ],
@@ -102,6 +139,8 @@ export default defineComponent({
102139 pickerData: [],
103140 pickerSelection: {},
104141 pickerTypes: CIRCLES_MEMBER_GROUPING ,
142+ windowWidth: window .innerWidth ,
143+ searchQuery: ' ' ,
105144 }
106145 },
107146
@@ -133,16 +172,58 @@ export default defineComponent({
133172 return [... teams , ... users ]
134173 },
135174
175+ filteredList() {
176+ const query = this .searchQuery .toLowerCase ()
177+
178+ return this .flatList .filter (member =>
179+ ! this .searchQuery || member .displayName .toLowerCase ().includes (query )
180+ );
181+ },
182+
136183 hasMembers() {
137184 return this .flatList .length > 0
138185 },
186+
187+ gridItems() {
188+ if (this .windowWidth < 768 ) {
189+ // undefined means that the grid will be rendered as a list
190+ return undefined
191+ }
192+
193+ return 2
194+ },
195+
196+ itemSecondarySize() {
197+ if (this .windowWidth < 768 ) {
198+ // undefined means that the grid will be rendered as a list
199+ return undefined
200+ }
201+
202+ // The maximum width of the member list is 500px,
203+ // so with two columns, each column is 242px wide (with scroll)
204+ return 242
205+ },
139206 },
140207
141208 mounted() {
142209 subscribe (' contacts:circles:append' , this .onShowPicker )
210+
211+ window .addEventListener (' resize' , this .onResize )
212+ },
213+
214+ beforeDestroy() {
215+ window .removeEventListener (' resize' , this .onResize )
143216 },
144217
145218 methods: {
219+ onResize() {
220+ this .windowWidth = window .innerWidth
221+ },
222+
223+ clearSearchField() {
224+ this .searchQuery = ' '
225+ },
226+
146227 /**
147228 * Show picker and fetch for recommendations
148229 * Cache the circleId in case the url change or something
@@ -253,18 +334,9 @@ export default defineComponent({
253334 }
254335}
255336
256- .member-grid {
257- display : grid ;
258- grid-template-columns : repeat (2 , 1fr );
259- gap : 8px ;
260-
261- @media (max-width : 768px ) {
262- grid-template-columns : 1fr ;
263- }
264-
265- @media (min-width : 1200px ) {
266- grid-template-columns : repeat (3 , 1fr );
267- }
337+ .member-scroller {
338+ height : 100% ;
339+ max-height : 200px ;
268340}
269341
270342.empty-content {
0 commit comments