summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--css/ContactDetails.scss151
-rw-r--r--css/ContactsList.scss35
-rw-r--r--css/Properties/Properties.scss1
-rw-r--r--css/contacts.scss13
-rw-r--r--package-lock.json60
-rw-r--r--package.json1
-rw-r--r--src/components/ContactDetails.vue225
-rw-r--r--src/components/ContactDetails/ContactDetailsAddNewProp.vue2
-rw-r--r--src/components/ContactDetails/ContactDetailsProperty.vue45
-rw-r--r--src/components/ContactsList.vue3
-rw-r--r--src/components/Properties/PropertyDateTime.vue9
-rw-r--r--src/components/Properties/PropertyGroups.vue2
-rw-r--r--src/components/Properties/PropertyMultipleText.vue10
-rw-r--r--src/components/Properties/PropertySelect.vue8
-rw-r--r--src/components/Properties/PropertyText.vue11
15 files changed, 293 insertions, 283 deletions
diff --git a/css/ContactDetails.scss b/css/ContactDetails.scss
deleted file mode 100644
index 6cf09749..00000000
--- a/css/ContactDetails.scss
+++ /dev/null
@@ -1,151 +0,0 @@
-/**
- * @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/>.
- *
- */
-
-#contact-details {
- $grid-column-gap: 20px;
- $grid-column-width: 350px;
- flex: 1 1 524px;
- // header
- header {
- display: flex;
- align-items: center;
- padding: 50px 0 20px;
- font-weight: bold;
-
- // ORG-TITLE-NAME
- #contact-header-infos {
- display: flex;
- flex: 1 1 auto; // shrink avatar before this one
- flex-direction: column;
- h2,
- #details-org-container {
- display: flex;
- flex-wrap: wrap;
- margin: 0;
- }
- input {
- overflow: hidden;
- flex: 1 1;
- min-width: 100px;
- max-width: 100%;
- margin: 0;
- padding: 4px 5px;
- white-space: nowrap;
- text-overflow: ellipsis;
- border: none;
- background: transparent;
- font-size: inherit;
- &#contact-fullname {
- font-weight: bold;
- }
- }
- #contact-org:placeholder-shown {
- max-width: 20%;
- }
- }
-
- // ACTIONS
- #contact-header-actions {
- position: relative;
- display: flex;
- .header-menu {
- margin-right: 10px;
- }
- .header-icon {
- width: 44px;
- height: 44px;
- padding: 14px;
- cursor: pointer;
- opacity: .7;
- border-radius: 22px;
- background-size: 16px;
- &:hover,
- &:focus {
- opacity: 1;
- }
- &.header-icon--pulse {
- width: 16px;
- height: 16px;
- margin: 8px;
- }
- }
- }
- }
-
- // contact details
- section.contact-details {
- display: grid;
- min-height: 200px;
- padding: 20px $grid-column-gap;
- /* unquote is a strange hack to avoid removal of the comma by the scss compiler */
- grid-template-columns: repeat(auto-fit, minmax(unquote('#{$grid-column-width}'), 1fr));
- grid-column-gap: $grid-column-gap;
- }
-
- // single column fix, better visual
- @media only screen and (max-width: $navigation-width + $list-min-width + 2 * $grid-column-gap +$grid-column-width) {
- section.contact-details {
- padding: 10px;
-
- grid-template-columns: 1fr;
- grid-column-gap: 10px;
- }
- }
-}
-
-.property--rev {
- position: absolute;
- right: 22px;
- bottom: 0;
- height: 44px;
- opacity: .5;
- color: var(--color-text-lighter);
- line-height: 44px;
-}
-
-#qrcode-modal {
- .modal-container {
- display: flex;
- padding: 10px;
- background-color: #fff;
- .qrcode {
- max-width: 100%;
- }
- }
-}
-
-#pick-addressbook-modal {
- .modal-container {
- display: flex;
- overflow: visible;
- flex-wrap: wrap;
- justify-content: space-evenly;
- margin-bottom: 20px;
- padding: 10px;
- background-color: #fff;
- .multiselect {
- flex: 1 1 100%;
- width: 100%;
- margin-bottom: 20px;
- }
- }
-}
diff --git a/css/ContactsList.scss b/css/ContactsList.scss
deleted file mode 100644
index 43e8f17c..00000000
--- a/css/ContactsList.scss
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
- * @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/>.
- *
- */
-
-#app-details-toggle {
- position: fixed;
- z-index: 1500;
- left: 0;
- display: inline-block;
- width: 44px;
- height: 44px;
- cursor: pointer;
- transform: rotate(180deg);
- background-color: var(--color-main-background);
- border-radius: 50%;
- opacity: 1;
-}
diff --git a/css/Properties/Properties.scss b/css/Properties/Properties.scss
index 5a9aca79..788a3c81 100644
--- a/css/Properties/Properties.scss
+++ b/css/Properties/Properties.scss
@@ -32,7 +32,6 @@ $property-ext-padding-right: 8px;
// the max width and keep the right alignment
max-width: $property-label-max-width + $property-value-max-width + 44px;
- @include generate-grid-span(1);
justify-self: center;
&--last {
diff --git a/css/contacts.scss b/css/contacts.scss
index 82c96a89..78288b20 100644
--- a/css/contacts.scss
+++ b/css/contacts.scss
@@ -24,19 +24,8 @@
$grid-height-unit: 40px;
$grid-input-padding: 7px;
$grid-input-margin: 3px;
-$grid-column-width: 380px;
$grid-input-height-with-margin: $grid-height-unit - $grid-input-margin * 2;
-@mixin generate-grid-span($default-unit) {
- // we only supports 10 props of the same type
- @for $i from 1 through 10 {
- &.grid-span-#{$i} {
- // default unit + title + bottom padding
- grid-row-start: span #{$i * $default-unit};
- }
- }
-}
-
#app-content-wrapper {
display: flex;
}
@@ -46,9 +35,7 @@ $grid-input-height-with-margin: $grid-height-unit - $grid-input-margin * 2;
@import 'Settings/SettingsAddressbook';
@import 'Settings/SettingsAddressbookShares';
@import 'Settings/SettingsAddressbookSharee';
-@import 'ContactDetails';
@import 'ContactDetailsAvatar';
-@import 'ContactsList';
@import 'ContactsListItem';
@import 'Properties/Properties';
@import 'Properties/PropertyTitle';
diff --git a/package-lock.json b/package-lock.json
index 44344547..80c68a53 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4533,6 +4533,11 @@
"minimalistic-assert": "^1.0.0"
}
},
+ "desandro-matches-selector": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/desandro-matches-selector/-/desandro-matches-selector-2.0.2.tgz",
+ "integrity": "sha1-cXvu1NwT59jzdi9wem1YpndCGOE="
+ },
"detect-file": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
@@ -5323,6 +5328,11 @@
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="
},
+ "ev-emitter": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ev-emitter/-/ev-emitter-1.1.1.tgz",
+ "integrity": "sha512-ipiDYhdQSCZ4hSbX4rMW+XzNKMD1prg/sTvoVmSLkuQ1MVlwjJQQA+sW8tMYR3BLUr9KjodFV4pvzunvRhd33Q=="
+ },
"eventemitter3": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz",
@@ -5715,6 +5725,14 @@
"resolve-dir": "^1.0.1"
}
},
+ "fizzy-ui-utils": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/fizzy-ui-utils/-/fizzy-ui-utils-2.0.7.tgz",
+ "integrity": "sha512-CZXDVXQ1If3/r8s0T+v+qVeMshhfcuq0rqIFgJnrtd+Bu8GmDmqMjntjUePypVtjHXKJ6V4sw9zeyox34n9aCg==",
+ "requires": {
+ "desandro-matches-selector": "^2.0.0"
+ }
+ },
"flat-cache": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
@@ -6472,6 +6490,11 @@
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true
},
+ "get-size": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/get-size/-/get-size-2.0.3.tgz",
+ "integrity": "sha512-lXNzT/h/dTjTxRbm9BXb+SGxxzkm97h/PCIKtlN/CBCxxmkkIVV21udumMS93MuVTDX583gqc94v3RjuHmI+2Q=="
+ },
"get-stdin": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
@@ -6900,6 +6923,14 @@
"integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
"dev": true
},
+ "imagesloaded": {
+ "version": "4.1.4",
+ "resolved": "https://registry.npmjs.org/imagesloaded/-/imagesloaded-4.1.4.tgz",
+ "integrity": "sha512-ltiBVcYpc/TYTF5nolkMNsnREHW+ICvfQ3Yla2Sgr71YFwQ86bDwV9hgpFhFtrGPuwEx5+LqOHIrdXBdoWwwsA==",
+ "requires": {
+ "ev-emitter": "^1.0.0"
+ }
+ },
"import-fresh": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
@@ -7727,6 +7758,15 @@
"repeat-string": "^1.0.0"
}
},
+ "masonry-layout": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/masonry-layout/-/masonry-layout-4.2.2.tgz",
+ "integrity": "sha512-iGtAlrpHNyxaR19CvKC3npnEcAwszXoyJiI8ARV2ePi7fmYhIud25MHK8Zx4P0LCC4d3TNO9+rFa1KoK1OEOaA==",
+ "requires": {
+ "get-size": "^2.0.2",
+ "outlayer": "^2.1.0"
+ }
+ },
"material-colors": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz",
@@ -8508,6 +8548,16 @@
"os-tmpdir": "^1.0.0"
}
},
+ "outlayer": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/outlayer/-/outlayer-2.1.1.tgz",
+ "integrity": "sha1-KYY7beEOpdrf/8rfoNcokHOH6aI=",
+ "requires": {
+ "ev-emitter": "^1.0.0",
+ "fizzy-ui-utils": "^2.0.0",
+ "get-size": "^2.0.2"
+ }
+ },
"p-finally": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
@@ -11563,6 +11613,16 @@
"vue-style-loader": "^4.1.0"
}
},
+ "vue-masonry": {
+ "version": "0.11.8",
+ "resolved": "https://registry.npmjs.org/vue-masonry/-/vue-masonry-0.11.8.tgz",
+ "integrity": "sha512-O+T+3zUghbKpjc+5aubXr8Kg1h9P334+Or9euYyXsQYa3mtScUqZFI6A16BijR9v4hYdtKksuPzU0mQplUvhDA==",
+ "requires": {
+ "imagesloaded": "4.1.4",
+ "masonry-layout": "^4.2.2",
+ "vue": "^2.0.0"
+ }
+ },
"vue-multiselect": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/vue-multiselect/-/vue-multiselect-2.1.6.tgz",
diff --git a/package.json b/package.json
index fd880287..6d6264e9 100644
--- a/package.json
+++ b/package.json
@@ -57,6 +57,7 @@
"vue": "^2.6.12",
"vue-click-outside": "^1.1.0",
"vue-clipboard2": "^0.3.1",
+ "vue-masonry": "^0.11.8",
"vue-router": "^3.4.3",
"vue-virtual-scroll-list": "^2.3.1",
"vue-virtual-scroller": "^1.0.10",
diff --git a/src/components/ContactDetails.vue b/src/components/ContactDetails.vue
index 58d68ff6..e806904a 100644
--- a/src/components/ContactDetails.vue
+++ b/src/components/ContactDetails.vue
@@ -183,60 +183,74 @@
<section v-if="loadingData" class="icon-loading contact-details" />
<!-- contact details -->
- <section v-else class="contact-details">
+ <section v-else
+ v-masonry="contactDetailsSelector"
+ class="contact-details"
+ :fit-width="true"
+ item-selector=".property-masonry"
+ :transition-duration="0">
<!-- properties iteration -->
<!-- using contact.key in the key and index as key to avoid conflicts between similar data and exact key -->
<!-- passing the debounceUpdateContact so that the contact-property component contains the function
and allow us to use it on the rfcProps since the scope is forwarded to the actions -->
- <ContactProperty v-for="(property, index) in sortedProperties"
- :key="`${index}-${contact.key}-${property.name}`"
- :index="index"
- :sorted-properties="sortedProperties"
- :property="property"
- :contact="contact"
- :local-contact="localContact"
- :update-contact="debounceUpdateContact" />
+ <div v-for="(properties, name) in groupedProperties"
+ :key="name"
+ v-masonry-tile
+ class="property-masonry">
+ <ContactProperty v-for="(property, index) in properties"
+ :key="`${index}-${contact.key}-${property.name}`"
+ :is-first-property="index===0"
+ :is-last-property="index === properties.length - 1"
+ :property="property"
+ :contact="contact"
+ :local-contact="localContact"
+ :update-contact="debounceUpdateContact" />
+ </div>
<!-- addressbook change select - no last property because class is not applied here,
empty property because this is a required prop on regular property-select. But since
we are hijacking this... (this is supposed to be used with a ICAL.property, but to avoid code
duplication, we created a fake propModel and property with our own options here) -->
- <PropertySelect v-if="addressbooksOptions.length > 1"
+ <PropertySelect v-masonry-tile
:prop-model="addressbookModel"
:value.sync="addressbook"
:is-first-property="true"
:is-last-property="true"
:property="{}"
- class="property--addressbooks property--last property--without-actions" />
+ class="property-masonry property--addressbooks property--last property--without-actions" />
<!-- Groups always visible -->
- <PropertyGroups :prop-model="groupsModel"
+ <PropertyGroups v-masonry-tile
+ :prop-model="groupsModel"
:value.sync="groups"
:contact="contact"
:is-read-only="isReadOnly"
- class="property--groups property--last" />
+ class="property-masonry property--groups property--last" />
+
+ <!-- new property select -->
+ <AddNewProp v-masonry-tile :contact="contact" class="property-masonry" />
<!-- Last modified-->
<PropertyRev v-if="contact.rev" :value="contact.rev" />
-
- <!-- new property select -->
- <AddNewProp v-if="!isReadOnly" :contact="contact" />
</section>
</template>
</div>
</template>
<script>
+import { showError } from '@nextcloud/dialogs'
+import { stringify } from 'ical.js'
import debounce from 'debounce'
import PQueue from 'p-queue'
import qr from 'qr-image'
-import { stringify } from 'ical.js'
-import Actions from '@nextcloud/vue/dist/Components/Actions'
-import ActionLink from '@nextcloud/vue/dist/Components/ActionLink'
+import Vue from 'vue'
+import { VueMasonryPlugin } from 'vue-masonry'
+
import ActionButton from '@nextcloud/vue/dist/Components/ActionButton'
-import Multiselect from '@nextcloud/vue/dist/Components/Multiselect'
+import ActionLink from '@nextcloud/vue/dist/Components/ActionLink'
+import Actions from '@nextcloud/vue/dist/Components/Actions'
import Modal from '@nextcloud/vue/dist/Components/Modal'
-import { showError } from '@nextcloud/dialogs'
+import Multiselect from '@nextcloud/vue/dist/Components/Multiselect'
import rfcProps from '../models/rfcProps'
import validate from '../services/validate'
@@ -249,6 +263,7 @@ import PropertyGroups from './Properties/PropertyGroups'
import PropertyRev from './Properties/PropertyRev'
import PropertySelect from './Properties/PropertySelect'
+Vue.use(VueMasonryPlugin)
const updateQueue = new PQueue({ concurrency: 1 })
export default {
@@ -297,6 +312,8 @@ export default {
qrcode: '',
showPickAddressbookModal: false,
pickedAddressbook: null,
+
+ contactDetailsSelector: '.contact-details',
}
},
@@ -356,6 +373,29 @@ export default {
},
/**
+ * Contact properties filtered and grouped by rfcProps.fieldOrder
+ *
+ * @returns {Object}
+ */
+ groupedProperties() {
+ return this.sortedProperties
+ .reduce((list, property) => {
+ // If there is no component to display this prop, ignore it
+ if (!this.canDisplay(property)) {
+ return list
+ }
+
+ // Init if needed
+ if (!list[property.name]) {
+ list[property.name] = []
+ }
+
+ list[property.name].push(property)
+ return list
+ }, {})
+ },
+
+ /**
* Fake model to use the propertySelect component
*
* @returns {Object}
@@ -442,9 +482,16 @@ export default {
if (this.contactKey && newContact !== oldContact) {
this.selectContact(this.contactKey)
}
+
+ // Reflow grid
+ this.$redrawVueMasonry(this.contactDetailsSelector)
},
},
+ updated() {
+ this.$redrawVueMasonry(this.contactDetailsSelector)
+ },
+
beforeMount() {
// load the desired data if we already selected a contact
if (this.contactKey) {
@@ -708,6 +755,142 @@ export default {
})
}
},
+
+ /**
+ * Should display the property
+ *
+ * @param {Property} property the property to check
+ * @returns {boolean}
+ */
+ canDisplay(property) {
+ const propModel = rfcProps.properties[property.name]
+ const propType = propModel && propModel.force
+ ? propModel.force
+ : property.getDefaultType()
+
+ return propModel && propType !== 'unknown'
+ },
},
}
</script>
+
+<style lang="scss">
+#contact-details {
+ flex: 1 1 100%;
+ min-width: 0;
+
+ // Header with avatar, name, position, actions...
+ header {
+ display: flex;
+ align-items: center;
+ padding: 50px 0 20px;
+ font-weight: bold;
+
+ // ORG-TITLE-NAME
+ #contact-header-infos {
+ display: flex;
+ flex: 1 1 auto; // shrink avatar before this one
+ flex-direction: column;
+ h2,
+ #details-org-container {
+ display: flex;
+ flex-wrap: wrap;
+ margin: 0;
+ }
+ input {
+ overflow: hidden;
+ flex: 1 1;
+ min-width: 100px;
+ max-width: 100%;
+ margin: 0;
+ padding: 4px 5px;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ border: none;
+ background: transparent;
+ font-size: inherit;
+ &#contact-fullname {
+ font-weight: bold;
+ }
+ }
+ #contact-org:placeholder-shown {
+ max-width: 20%;
+ }
+ }
+
+ // ACTIONS
+ #contact-header-actions {
+ position: relative;
+ display: flex;
+ .header-menu {
+ margin-right: 10px;
+ }
+ .header-icon {
+ width: 44px;
+ height: 44px;
+ padding: 14px;
+ cursor: pointer;
+ opacity: .7;
+ border-radius: 22px;
+ background-size: 16px;
+ &:hover,
+ &:focus {
+ opacity: 1;
+ }
+ &.header-icon--pulse {
+ width: 16px;
+ height: 16px;
+ margin: 8px;
+ }
+ }
+ }
+ }
+
+ // List of all properties
+ section.contact-details {
+ margin: 0 auto;
+
+ .property-masonry {
+ width: 350px;
+ }
+
+ .property--rev {
+ position: absolute;
+ right: 22px;
+ bottom: 0;
+ height: 44px;
+ opacity: .5;
+ color: var(--color-text-lighter);
+ line-height: 44px;
+ }
+ }
+
+ #qrcode-modal {
+ .modal-container {
+ display: flex;
+ padding: 10px;
+ background-color: #fff;
+ .qrcode {
+ max-width: 100%;
+ }
+ }
+ }
+
+ #pick-addressbook-modal {
+ .modal-container {
+ display: flex;
+ overflow: visible;
+ flex-wrap: wrap;
+ justify-content: space-evenly;
+ margin-bottom: 20px;
+ padding: 10px;
+ background-color: #fff;
+ .multiselect {
+ flex: 1 1 100%;
+ width: 100%;
+ margin-bottom: 20px;
+ }
+ }
+ }
+}
+</style>
diff --git a/src/components/ContactDetails/ContactDetailsAddNewProp.vue b/src/components/ContactDetails/ContactDetailsAddNewProp.vue
index 7dc86f00..a4fe6b37 100644
--- a/src/components/ContactDetails/ContactDetailsAddNewProp.vue
+++ b/src/components/ContactDetails/ContactDetailsAddNewProp.vue
@@ -21,7 +21,7 @@
-->
<template>
- <div class="grid-span-3 property property--without-actions property--last">
+ <div class="property property--without-actions property--last">
<!-- title -->
<PropertyTitle :icon="'icon-add'" :readable-name="t('contacts', 'Add new property')" />
diff --git a/src/components/ContactDetails/ContactDetailsProperty.vue b/src/components/ContactDetails/ContactDetailsProperty.vue
index bcbfbe4e..ecc641cb 100644
--- a/src/components/ContactDetails/ContactDetailsProperty.vue
+++ b/src/components/ContactDetails/ContactDetailsProperty.vue
@@ -23,7 +23,6 @@
<template>
<!-- If not in the rfcProps then we don't want to display it -->
<component :is="componentInstance"
- v-if="propModel && propType !== 'unknown'"
ref="component"
:select-type.sync="selectType"
:prop-model="propModel"
@@ -31,7 +30,10 @@
:is-first-property="isFirstProperty"
:property="property"
:is-last-property="isLastProperty"
- :class="{'property--last': isLastProperty}"
+ :class="{
+ 'property--last': isLastProperty,
+ [`property-${propName}`]: true
+ }"
:local-contact="localContact"
:prop-name="propName"
:prop-type="propType"
@@ -59,16 +61,22 @@ export default {
type: Property,
default: true,
},
- sortedProperties: {
- type: Array,
- default() {
- return []
- },
+
+ /**
+ * Is it the first property of its kind
+ */
+ isFirstProperty: {
+ type: Boolean,
+ default: false,
},
- index: {
- type: Number,
- default: 0,
+ /**
+ * Is it the last property of its kind
+ */
+ isLastProperty: {
+ type: Boolean,
+ default: false,
},
+
contact: {
type: Contact,
default: null,
@@ -110,22 +118,6 @@ export default {
fieldOrder() {
return rfcProps.fieldOrder
},
-
- // is this the first property of its kind
- isFirstProperty() {
- if (this.index > 0) {
- return this.sortedProperties[this.index - 1].name.split('.').pop() !== this.propName
- }
- return true
- },
- // is this the last property of its kind
- isLastProperty() {
- // array starts at 0, length starts at 1
- if (this.index < this.sortedProperties.length - 1) {
- return this.sortedProperties[this.index + 1].name.split('.').pop() !== this.propName
- }
- return true
- },
isReadOnly() {
if (this.contact.addressbook) {
return this.contact.addressbook.readOnly
@@ -146,6 +138,7 @@ export default {
return this.property.name
},
+
/**
* Return the type or property
*
diff --git a/src/components/ContactsList.vue b/src/components/ContactsList.vue
index d89d493c..6548faf5 100644
--- a/src/components/ContactsList.vue
+++ b/src/components/ContactsList.vue
@@ -189,6 +189,9 @@ export default {
// Virtual scroller overrides
.vue-recycle-scroller {
position: sticky !important;
+ min-width: 0;
+ // try to take some width
+ flex: 1 1 300px;
}
.vue-recycle-scroller__item-view {
diff --git a/src/components/Properties/PropertyDateTime.vue b/src/components/Properties/PropertyDateTime.vue
index 7acc7233..3b2430f6 100644
--- a/src/components/Properties/PropertyDateTime.vue
+++ b/src/components/Properties/PropertyDateTime.vue
@@ -21,7 +21,7 @@
-->
<template>
- <div v-if="propModel" :class="`grid-span-${gridLength}`" class="property">
+ <div v-if="propModel" class="property">
<!-- title if first element -->
<PropertyTitle v-if="isFirstProperty && propModel.icon"
:icon="propModel.icon"
@@ -140,13 +140,6 @@ export default {
},
computed: {
- gridLength() {
- const hasTitle = this.isFirstProperty && this.propModel.icon ? 1 : 0
- const isLast = this.isLastProperty ? 1 : 0
- // length is always one & add one space at the end
- return hasTitle + 1 + isLast
- },
-
// make sure the property is valid
vcardTimeLocalValue() {
if (typeof this.localValue === 'string') {
diff --git a/src/components/Properties/PropertyGroups.vue b/src/components/Properties/PropertyGroups.vue
index bb1b03f2..170565e1 100644
--- a/src/components/Properties/PropertyGroups.vue
+++ b/src/components/Properties/PropertyGroups.vue
@@ -21,7 +21,7 @@
-->
<template>
- <div v-if="propModel" class="grid-span-2 property property--without-actions">
+ <div v-if="propModel" class="property property--without-actions">
<PropertyTitle
icon="icon-contacts"
:readable-name="t('contacts', 'Groups')" />
diff --git a/src/components/Properties/PropertyMultipleText.vue b/src/components/Properties/PropertyMultipleText.vue
index d23d9434..3b4be4c1 100644
--- a/src/components/Properties/PropertyMultipleText.vue
+++ b/src/components/Properties/PropertyMultipleText.vue
@@ -21,7 +21,7 @@
-->
<template>
- <div v-if="propModel" :class="`grid-span-${gridLength}`" class="property">
+ <div v-if="propModel" class="property">
<!-- title if first element -->
<PropertyTitle v-if="isFirstProperty && propModel.icon"
:icon="propModel.icon"
@@ -125,14 +125,6 @@ export default {
},
computed: {
- gridLength() {
- const hasTitle = this.isFirstProperty && this.propModel.icon ? 1 : 0
- const isLast = this.isLastProperty
- const hasValueNames = this.propModel.options || this.selectType || !this.property.isStructuredValue ? 1 : 0
- const length = this.propModel.displayOrder ? this.propModel.displayOrder.length : this.value.length
- return hasValueNames + hasTitle + length + isLast
- },
-
filteredValue() {
return this.localValue.filter((value, index) => index > 0)
},
diff --git a/src/components/Properties/PropertySelect.vue b/src/components/Properties/PropertySelect.vue
index 67b7a35c..8e4ef727 100644
--- a/