diff options
author | John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com> | 2020-05-11 18:38:15 +0200 |
---|---|---|
committer | John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com> | 2020-08-21 09:56:07 +0200 |
commit | d8ede82510afe55a06c754710463a45eab6e5d7c (patch) | |
tree | 7d161912f44afafd47366b9a4f8ec4165ddf657d | |
parent | c3ae90c6887931b2adb12c19dc20e19ae39228d3 (diff) |
Add create group entry
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
-rw-r--r-- | css/ContactsList.scss | 14 | ||||
-rw-r--r-- | src/components/ContactDetails.vue | 26 | ||||
-rw-r--r-- | src/components/ContactsList.vue | 37 | ||||
-rw-r--r-- | src/components/EmptyContent.vue | 77 | ||||
-rw-r--r-- | src/store/groups.js | 23 | ||||
-rw-r--r-- | src/views/Contacts.vue | 38 |
6 files changed, 188 insertions, 27 deletions
diff --git a/css/ContactsList.scss b/css/ContactsList.scss index 75ef932a..43e8f17c 100644 --- a/css/ContactsList.scss +++ b/css/ContactsList.scss @@ -33,17 +33,3 @@ border-radius: 50%; opacity: 1; } - -// Virtual scroller overrides -.vue-recycle-scroller { - position: sticky !important; -} - -.vue-recycle-scroller__item-view { - // TODO: find better solution? - // https://github.com/Akryum/vue-virtual-scroller/issues/70 - // hack to not show the transition - overflow: hidden; - // same as app-content-list-item - height: 68px; -} diff --git a/src/components/ContactDetails.vue b/src/components/ContactDetails.vue index 10c69a04..da1b7e87 100644 --- a/src/components/ContactDetails.vue +++ b/src/components/ContactDetails.vue @@ -23,17 +23,17 @@ <template> <div id="contact-details" class="app-content-details"> <!-- nothing selected or contact not found --> - <div v-if="!contact && !loading" id="emptycontent"> - <div class="icon-contacts" /> - <h2>{{ t('contacts', 'No contact selected') }}</h2> - <p>{{ t('contacts', 'Select a contact on the list to begin') }}</p> - </div> + <EmptyContent v-if="!contact && !loading" icon="icon-contacts-dark"> + {{ t('contacts', 'No contact selected') }} + <template #desc> + {{ t('contacts', 'Select a contact on the list to begin') }} + </template> + </EmptyContent> <!-- loading --> - <div v-else-if="loading" id="emptycontent"> - <div class="icon-contacts" /> - <h2>{{ t('contacts', 'Loading') }}</h2> - </div> + <EmptyContent v-else-if="loading" icon="icon-contacts-dark"> + {{ t('contacts', 'Loading contacts …') }} + </EmptyContent> <template v-else> <!-- contact header --> @@ -244,6 +244,7 @@ import validate from '../services/validate' import AddNewProp from './ContactDetails/ContactDetailsAddNewProp' import ContactAvatar from './ContactDetails/ContactDetailsAvatar' import ContactProperty from './ContactDetails/ContactDetailsProperty' +import EmptyContent from './EmptyContent' import PropertyGroups from './Properties/PropertyGroups' import PropertyRev from './Properties/PropertyRev' import PropertySelect from './Properties/PropertySelect' @@ -254,17 +255,18 @@ export default { name: 'ContactDetails', components: { - Actions, ActionButton, ActionLink, + Actions, AddNewProp, ContactAvatar, ContactProperty, + EmptyContent, + Modal, + Multiselect, PropertyGroups, PropertyRev, PropertySelect, - Modal, - Multiselect, }, props: { diff --git a/src/components/ContactsList.vue b/src/components/ContactsList.vue index f2200693..222b4f1e 100644 --- a/src/components/ContactsList.vue +++ b/src/components/ContactsList.vue @@ -24,6 +24,7 @@ <!-- same uid can coexists between different addressbooks so we need to use the addressbook id as key as well --> <RecycleScroller + v-if="haveContact" id="contacts-list" ref="scroller" :class="{'icon-loading': loading, showdetails: selectedContact}" @@ -40,10 +41,22 @@ @deleted="selectContact" /> </template> </RecycleScroller> + + <div v-else class="app-content-list"> + <EmptyContent> + {{ t('contacts', 'No contacts in this group') }} + <template #action> + <button class="primary" @click="onAddContactsToGroup"> + {{ t('forms', 'Add some') }} + </button> + </template> + </EmptyContent> + </div> </template> <script> import ContactsListItem from './ContactsList/ContactsListItem' +import EmptyContent from './EmptyContent' import { RecycleScroller } from 'vue-virtual-scroller/dist/vue-virtual-scroller.umd.js' import 'vue-virtual-scroller/dist/vue-virtual-scroller.css' @@ -52,6 +65,7 @@ export default { components: { ContactsListItem, + EmptyContent, RecycleScroller, }, @@ -90,6 +104,9 @@ export default { filteredList() { return this.list.filter(contact => this.matchSearch(this.contacts[contact.key])) }, + haveContact() { + return this.selectedGroup && this.filteredList.length > 0 + }, }, watch: { @@ -160,6 +177,26 @@ export default { } return true }, + + onAddContactsToGroup() { + // TODO: add popup + } }, } </script> + +<style lang="scss" scoped> +// Virtual scroller overrides +.vue-recycle-scroller { + position: sticky !important; +} + +.vue-recycle-scroller__item-view { + // TODO: find better solution? + // https://github.com/Akryum/vue-virtual-scroller/issues/70 + // hack to not show the transition + overflow: hidden; + // same as app-content-list-item + height: 68px; +} +</style>
\ No newline at end of file diff --git a/src/components/EmptyContent.vue b/src/components/EmptyContent.vue new file mode 100644 index 00000000..5198f8ce --- /dev/null +++ b/src/components/EmptyContent.vue @@ -0,0 +1,77 @@ +<!-- + - @copyright Copyright (c) 2020 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> + <div class="empty-content" role="note"> + <div class="empty-content__icon" :class="icon" role="img" /> + <h2 class="empty-content__title"> + <slot /> + </h2> + <p v-show="$slots.desc" class="empty-content__desc"> + <slot name="desc" /> + </p> + <div v-show="$slots.action" class="empty-content__action"> + <slot name="action" /> + </div> + </div> +</template> + +<script> +export default { + name: 'EmptyContent', + + props: { + icon: { + type: String, + default: 'icon-forms', + }, + }, +} +</script> + +<style lang="scss"> +.empty-content { + margin-top: 20vh; + display: flex; + flex-direction: column; + align-items: center; + + &__icon { + width: 64px; + height: 64px; + margin: 0 auto 15px; + opacity: .4; + background-size: 64px; + background-repeat: no-repeat; + background-position: center; + } + + &__title { + margin-bottom: 8px; + } + + &__desc { + margin-bottom: 16px; + } +} + +</style> diff --git a/src/store/groups.js b/src/store/groups.js index 8df01b19..981e0932 100644 --- a/src/store/groups.js +++ b/src/store/groups.js @@ -105,6 +105,19 @@ const mutations = { } }) }, + + /** + * Add a group + * + * @param {Object} state the store data + * @param {string} groupName the name of the group + */ + addGroup(state, groupName) { + state.groups.push({ + name: groupName, + contacts: [], + }) + }, } const getters = { @@ -146,6 +159,16 @@ const actions = { removeContactToGroup(context, { groupName, contact }) { context.commit('removeContactToGroup', { groupName, contact }) }, + + /** + * Add a group + * + * @param {Object} context the store mutations + * @param {string} groupName the name of the group + */ + addGroup(context, groupName) { + context.commit('addGroup', groupName) + }, } export default { state, mutations, getters, actions } diff --git a/src/views/Contacts.vue b/src/views/Contacts.vue index 13c5dc16..60784a97 100644 --- a/src/views/Contacts.vue +++ b/src/views/Contacts.vue @@ -52,6 +52,20 @@ {{ item.utils.counter }} </AppNavigationCounter> </AppNavigationItem> + + <AppNavigationItem + :force-menu="true" + :menu-open.sync="isNewGroupMenuOpen" + :title="t('contacts', '+ New group')" + menu-icon="icon-add" + @click.prevent.stop="toggleNewGroupMenu"> + <template slot="actions"> + <ActionInput + icon="icon-contacts-dark" + :placeholder="t('contacts','Group name')" + @submit.prevent.stop="createNewGroup" /> + </template> + </AppNavigationItem> </ul> <!-- settings --> @@ -70,7 +84,9 @@ <div id="app-content-wrapper"> <!-- contacts list --> - <ContactsList :list="contactsList" + <ContactsList + v-if="!loading" + :list="contactsList" :contacts="contacts" :loading="loading" :search-query="searchQuery" /> @@ -96,6 +112,7 @@ import AppNavigationCounter from '@nextcloud/vue/dist/Components/AppNavigationCo import AppNavigationNew from '@nextcloud/vue/dist/Components/AppNavigationNew' import AppNavigationSettings from '@nextcloud/vue/dist/Components/AppNavigationSettings' import ActionButton from '@nextcloud/vue/dist/Components/ActionButton' +import ActionInput from '@nextcloud/vue/dist/Components/ActionInput' import Content from '@nextcloud/vue/dist/Components/Content' import Modal from '@nextcloud/vue/dist/Components/Modal' import isMobile from '@nextcloud/vue/dist/Mixins/isMobile' @@ -128,6 +145,7 @@ export default { AppNavigationNew, AppNavigationSettings, ActionButton, + ActionInput, ContactDetails, ContactsList, Content, @@ -155,6 +173,8 @@ export default { data() { return { + isNewGroupMenuOpen: false, + isCreatingGroup: false, loading: true, searchQuery: '', } @@ -510,6 +530,22 @@ export default { this.$store.dispatch('changeStage', 'default') } }, + + toggleNewGroupMenu() { + this.isNewGroupMenuOpen = !this.isNewGroupMenuOpen + }, + createNewGroup(e) { + const input = e.target.querySelector('input[type=text]') + const groupName = input.value.trim() + this.$store.dispatch('addGroup', groupName) + this.isNewGroupMenuOpen = false + }, }, } </script> + +<style lang="scss" scoped> +#app-content-wrapper { + display: flex; +} +</style> |