nextcloud-contacts/src/components/ContactsList.vue

166 lines
4.3 KiB
Vue

<!--
- @copyright Copyright (c) 2018 John Molakvoæ <skjnldsv@protonmail.com>
-
- @author John Molakvoæ <skjnldsv@protonmail.com>
-
- @license GNU AGPL version 3 or any later version
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-->
<template>
<!-- same uid can coexists between different addressbooks
so we need to use the addressbook id as key as well -->
<RecycleScroller
id="contacts-list"
ref="scroller"
:class="{'icon-loading': loading, showdetails: selectedContact}"
class="app-content-list"
:items="filteredList"
:item-size="itemHeight"
key-field="key">
<template v-slot="{ item, index }">
<ContactsListItem
v-if="contacts[item.key]"
:key="item.key"
:contact="contacts[item.key]"
:index="index"
@deleted="selectContact" />
</template>
</RecycleScroller>
</template>
<script>
import ContactsListItem from './ContactsList/ContactsListItem'
import { RecycleScroller } from 'vue-virtual-scroller/dist/vue-virtual-scroller.umd.js'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
export default {
name: 'ContactsList',
components: {
ContactsListItem,
RecycleScroller,
},
props: {
list: {
type: Array,
required: true,
},
contacts: {
type: Object,
required: true,
},
loading: {
type: Boolean,
default: true,
},
searchQuery: {
type: String,
default: '',
},
},
data() {
return {
itemHeight: 68,
}
},
computed: {
selectedContact() {
return this.$route.params.selectedContact
},
selectedGroup() {
return this.$route.params.selectedGroup
},
filteredList() {
return this.list.filter(contact => this.matchSearch(this.contacts[contact.key]))
},
},
watch: {
selectedContact: function(key) {
this.$nextTick(() => {
this.scrollToContact(key)
})
},
list: function(val, old) {
// we just loaded the list and the url already have a selected contact
// if not, the selectedContact watcher will take over
// to select the first entry
if (val.length !== 0 && old.length === 0 && this.selectedContact) {
this.$nextTick(() => {
this.scrollToContact(this.selectedContact)
})
}
},
},
methods: {
// Select closest contact on deletion
selectContact(oldIndex) {
if (this.list.length > 0 && oldIndex < this.list.length) {
// priority to the one above then the one after
const newContact = oldIndex === 0 ? this.list[oldIndex + 1] : this.list[oldIndex - 1]
if (newContact) {
this.$router.push({ name: 'contact', params: { selectedGroup: this.selectedGroup, selectedContact: newContact.key } })
}
}
},
/**
* Scroll to the desired contact if in the list and not visible
*
* @param {String} key the contact unique key
*/
scrollToContact(key) {
const item = this.$el.querySelector('#' + btoa(key).slice(0, -2))
// if the item is not visible in the list or barely visible
if (!(item && item.getBoundingClientRect().y > 50)) { // header height
const index = this.list.findIndex(contact => contact.key === key)
if (index > -1) {
this.$refs.scroller.scrollToItem(index)
}
}
// if item is a bit out (bottom) of the list, let's just scroll a bit to the top
if (item) {
const pos = item.getBoundingClientRect().y + this.itemHeight - (this.$el.offsetHeight + 50)
if (pos > 0) {
const scroller = this.$refs.scroller.$el
scroller.scrollTop = scroller.scrollTop + pos
}
}
},
/**
* Is this matching the current search ?
*
* @param {Contact} contact the contact to search
* @returns {boolean}
*/
matchSearch(contact) {
if (this.searchQuery.trim() !== '') {
return contact.searchData.toString().toLowerCase().search(this.searchQuery.trim().toLowerCase()) !== -1
}
return true
},
},
}
</script>