diff options
author | John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com> | 2020-08-21 09:46:20 +0200 |
---|---|---|
committer | John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com> | 2020-08-21 17:25:14 +0200 |
commit | 417448cac5e1ebac691a8fc417a578f45e9bf08e (patch) | |
tree | 56c46b30a9a42dd721f8426b3faee4fd9a54b56d | |
parent | 68c5b1ce9c2739e2909dc32dbf8f302f52b7aacb (diff) |
Implement masonry
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
-rw-r--r-- | css/ContactDetails.scss | 151 | ||||
-rw-r--r-- | css/ContactsList.scss | 35 | ||||
-rw-r--r-- | css/Properties/Properties.scss | 1 | ||||
-rw-r--r-- | css/contacts.scss | 13 | ||||
-rw-r--r-- | package-lock.json | 60 | ||||
-rw-r--r-- | package.json | 1 | ||||
-rw-r--r-- | src/components/ContactDetails.vue | 225 | ||||
-rw-r--r-- | src/components/ContactDetails/ContactDetailsAddNewProp.vue | 2 | ||||
-rw-r--r-- | src/components/ContactDetails/ContactDetailsProperty.vue | 45 | ||||
-rw-r--r-- | src/components/ContactsList.vue | 3 | ||||
-rw-r--r-- | src/components/Properties/PropertyDateTime.vue | 9 | ||||
-rw-r--r-- | src/components/Properties/PropertyGroups.vue | 2 | ||||
-rw-r--r-- | src/components/Properties/PropertyMultipleText.vue | 10 | ||||
-rw-r--r-- | src/components/Properties/PropertySelect.vue | 8 | ||||
-rw-r--r-- | src/components/Properties/PropertyText.vue | 11 |
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"> + &l |