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"
5075</template >
5176
5277<script lang="ts">
53- import { NcEmptyContent } from ' @nextcloud/vue'
78+ import {
79+ NcEmptyContent ,
80+ isMobile ,
81+ NcLoadingIcon as IconLoading ,
82+ NcTextField
83+ } from ' @nextcloud/vue'
84+ import { RecycleScroller } from ' vue-virtual-scroller'
85+ import ' vue-virtual-scroller/dist/vue-virtual-scroller.css'
5486
5587import MemberGridItem from ' ./MemberGridItem.vue'
5688import EntityPicker from ' ../EntityPicker/EntityPicker.vue'
5789import IconContact from ' vue-material-design-icons/AccountMultipleOutline.vue'
90+ import IconSearch from ' vue-material-design-icons/Magnify.vue'
5891
5992import RouterMixin from ' ../../mixins/RouterMixin.js'
6093
@@ -72,8 +105,12 @@ export default defineComponent({
72105 components: {
73106 EntityPicker ,
74107 IconContact ,
108+ IconLoading ,
109+ IconSearch ,
75110 MemberGridItem ,
76111 NcEmptyContent ,
112+ RecycleScroller ,
113+ NcTextField ,
77114 },
78115
79116 mixins: [IsMobileMixin , RouterMixin ],
@@ -101,6 +138,8 @@ export default defineComponent({
101138 pickerData: [],
102139 pickerSelection: {},
103140 pickerTypes: CIRCLES_MEMBER_GROUPING ,
141+ windowWidth: window .innerWidth ,
142+ searchQuery: ' ' ,
104143 }
105144 },
106145
@@ -132,17 +171,59 @@ export default defineComponent({
132171 return [... teams , ... users ]
133172 },
134173
174+ filteredList() {
175+ const query = this .searchQuery .toLowerCase ()
176+
177+ return this .flatList .filter (member =>
178+ ! this .searchQuery || member .displayName .toLowerCase ().includes (query )
179+ )
180+ },
181+
135182 hasMembers() {
136183 return this .flatList .length > 0
137184 },
185+
186+ gridItems() {
187+ if (this .windowWidth < 768 ) {
188+ // undefined means that the grid will be rendered as a list
189+ return undefined
190+ }
191+
192+ return 2
193+ },
194+
195+ itemSecondarySize() {
196+ if (this .windowWidth < 768 ) {
197+ // undefined means that the grid will be rendered as a list
198+ return undefined
199+ }
200+
201+ // The maximum width of the member list is 500px,
202+ // so with two columns, each column is 242px wide (with scroll)
203+ return 242
204+ },
138205 },
139206
140207 mounted() {
141208 subscribe (' contacts:circles:append' , this .onShowPicker )
142209 subscribe (' guests:user:created' , this .onGuestCreated )
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
@@ -258,21 +339,16 @@ export default defineComponent({
258339 }
259340}
260341
261- .member-grid {
262- display : grid ;
263- grid-template-columns : repeat (2 , 1fr );
264- gap : 8px ;
265-
266- @media (max-width : 768px ) {
267- grid-template-columns : 1fr ;
268- }
269-
270- @media (min-width : 1200px ) {
271- grid-template-columns : repeat (3 , 1fr );
272- }
342+ .member-scroller {
343+ height : 100% ;
344+ max-height : 200px ;
273345}
274346
275347.empty-content {
276348 height : 100% ;
277349}
350+
351+ .empty-search-results {
352+ margin-top : 2rem ;
353+ }
278354 </style >
0 commit comments