diff options
author | John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com> | 2018-08-16 08:23:58 +0200 |
---|---|---|
committer | John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com> | 2018-08-16 08:23:58 +0200 |
commit | 36b9a168810bc4b5997a653b3e91fa2c717b5063 (patch) | |
tree | 29750b3167b5e03f6ef7dd1717c4ab9f977aee5f | |
parent | 82ecf80ffeb3cbe9a59f8df961e4406fa282e71d (diff) |
Grid fixes and title update
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
-rw-r--r-- | css/Properties/Properties.scss | 99 | ||||
-rw-r--r-- | css/Properties/PropertyTitle.scss | 33 | ||||
-rw-r--r-- | css/contacts.scss | 19 | ||||
-rw-r--r-- | css/details.scss | 78 | ||||
-rw-r--r-- | src/components/ContactDetails.vue | 3 | ||||
-rw-r--r-- | src/components/ContactDetails/ContactDetailsProperty.vue | 17 | ||||
-rw-r--r-- | src/components/properties/PropertyDateTime.vue | 34 | ||||
-rw-r--r-- | src/components/properties/PropertyMultipleText.vue | 33 | ||||
-rw-r--r-- | src/components/properties/PropertyText.vue | 31 | ||||
-rw-r--r-- | src/components/properties/PropertyTitle.vue | 46 |
10 files changed, 286 insertions, 107 deletions
diff --git a/css/Properties/Properties.scss b/css/Properties/Properties.scss new file mode 100644 index 00000000..bddf8edf --- /dev/null +++ b/css/Properties/Properties.scss @@ -0,0 +1,99 @@ +/** + * @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/>. + * + */ + +.property { + @include generate-grid-span(1); + display: flex; + flex-wrap: wrap; + flex-direction: column; + position: relative; + width: $grid-column-width; + margin-bottom: $grid-height-unit; + + &__delete { + position: absolute; + top: 0; + right: 0; + width: $grid-height-unit; + height: $grid-height-unit; + margin: 0; + border: 0; + background-color: transparent; + opacity: .5; + display: none; + &:hover, + a:active, + a:focus { + opacity: .7; + } + } + + &:hover &__delete, + a:active &__delete, + a:focus &__delete { + display: block; + } + + // property row + &__row { + display: flex; + align-items: center; + padding-right: 44px; + height: $grid-height-unit; + position: relative; + + input { + flex: 1 1; + } + + } + + // label or multiselect + &__label, + &__label.multiselect { + margin: $grid-input-margin; + margin: $grid-input-margin 5px $grid-input-margin 0; + height: $grid-input-height-with-margin; + padding: $grid-input-padding 0; + width: 120px; + opacity: .7; + + &, + .multiselect__input::placeholder { + text-align: right; + } + + &:not(.multiselect) { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + overflow-x: hidden; + } + + &.multiselect { + &:hover, + &:focus { + opacity: 1; + } + } + } +} diff --git a/css/Properties/PropertyTitle.scss b/css/Properties/PropertyTitle.scss new file mode 100644 index 00000000..c70ba855 --- /dev/null +++ b/css/Properties/PropertyTitle.scss @@ -0,0 +1,33 @@ +/** + * @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/>. + * + */ + +.property__title { + display: flex; + align-items: center; + margin: 0; + opacity: 0.6; + user-select: none; + + .property__title--icon { + background-origin: center right; + } +}
\ No newline at end of file diff --git a/css/contacts.scss b/css/contacts.scss index d1b2da3e..0d8ed0e6 100644 --- a/css/contacts.scss +++ b/css/contacts.scss @@ -1,2 +1,21 @@ +$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}; + } + } +} + + @import 'settings'; @import 'details'; +@import './Properties/Properties'; +@import './Properties/PropertyTitle';
\ No newline at end of file diff --git a/css/details.scss b/css/details.scss index 09807a9b..c8c8a31e 100644 --- a/css/details.scss +++ b/css/details.scss @@ -113,21 +113,7 @@ } } - $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}; - } - } - } // contact details section.contact-details { @@ -135,69 +121,5 @@ /* 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: 20px; - .contact-details-property { - @include generate-grid-span(1); - display: flex; - flex-wrap: wrap; - flex-direction: column; - position: relative; - width: $grid-column-width; - .icon-delete { - position: absolute; - top: 0; - right: 0; - width: 44px; - height: 100%; - margin: 0; - border: 0; - background-color: transparent; - opacity: .5; - display: none; - &:hover, - a:active, - a:focus { - opacity: .7; - } - } - &:hover, - a:active, - a:focus { - .icon-delete { - display: block; - } - } - } - .contact-details-label { - margin: $grid-input-margin; - margin: $grid-input-margin 5px $grid-input-margin 0; - width: 120px; - height: $grid-input-height-with-margin; - padding: $grid-input-padding 0; - opacity: .7; - &, .multiselect__input::placeholder { - text-align: right; - } - &:not(.multiselect-vue) { - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; - overflow-x: hidden; - } - &.multiselect-vue { - &:hover, - &:focus { - opacity: 1; - } - } - } - .contact-details-property-row { - display: flex; - align-items: center; - margin-right: 44px; - input { - flex: 1 1; - } - } - } }
\ No newline at end of file diff --git a/src/components/ContactDetails.vue b/src/components/ContactDetails.vue index 1ceda06c..46be417e 100644 --- a/src/components/ContactDetails.vue +++ b/src/components/ContactDetails.vue @@ -80,7 +80,8 @@ <!-- contact details --> <section class="contact-details"> - <contact-details-property v-for="(property, index) in sortedProperties" :key="index" :property="property" /> + <contact-details-property v-for="(property, index) in sortedProperties" :key="index" :index="index" + :sorted-properties="sortedProperties" :property="property" /> </section> </template> </div> diff --git a/src/components/ContactDetails/ContactDetailsProperty.vue b/src/components/ContactDetails/ContactDetailsProperty.vue index a9800fe0..ff973bdc 100644 --- a/src/components/ContactDetails/ContactDetailsProperty.vue +++ b/src/components/ContactDetails/ContactDetailsProperty.vue @@ -23,7 +23,7 @@ <template> <!-- If not in the rfcProps then we don't want to display it --> <component v-if="propModel" :is="componentInstance" :select-type="selectType" - :prop-model="propModel" :value="value" /> + :prop-model="propModel" :value="value" :is-first-property="isFirstProperty" /> </template> <script> @@ -41,6 +41,16 @@ export default { property: { type: Property, default: true + }, + sortedProperties: { + type: Array, + default() { + return [] + } + }, + index: { + type: Number, + default: 0 } }, @@ -65,6 +75,11 @@ export default { return rfcProps.fieldOrder }, + // is this the first property of its kind + isFirstProperty() { + return true + }, + // the type of the prop e.g. FN propName() { return this.property.name diff --git a/src/components/properties/PropertyDateTime.vue b/src/components/properties/PropertyDateTime.vue index c7d79c25..53f50391 100644 --- a/src/components/properties/PropertyDateTime.vue +++ b/src/components/properties/PropertyDateTime.vue @@ -21,21 +21,24 @@ --> <template> - <div v-if="propModel" class="contact-details-property grid-span-1"> - <div class="contact-details-property-row"> + <div v-if="propModel" :class="`grid-span-${gridLength}`" class="property"> + <!-- title if first element --> + <property-title v-if="isFirstProperty && propModel.icon" :icon="propModel.icon" :readable-name="propModel.readableName" /> + + <div class="property__row"> <!-- type selector --> <multiselect v-if="propModel.options" v-model="selectType" :options="propModel.options" :searchable="false" :placeholder="t('contacts', 'Select type')" - class="multiselect-vue contact-details-label" track-by="id" label="name" /> + class="multiselect-vue property__label" track-by="id" label="name" /> <!-- if we do not support any type on our model but one is set anyway --> - <div v-else-if="selectType" class="contact-details-label">{{ selectType.name }}</div> + <div v-else-if="selectType" class="property__label">{{ selectType.name }}</div> <!-- no options, empty space --> - <div v-else class="contact-details-label">{{ propModel.readableName }}</div> + <div v-else class="property__label">{{ propModel.readableName }}</div> <!-- delete the prop --> - <button :title="t('contacts', 'Delete')" class="icon-delete" @click="deleteProperty" /> + <button :title="t('contacts', 'Delete')" class="property__delete icon-delete" @click="deleteProperty" /> <input v-model="value" type="text"> </div> @@ -44,13 +47,14 @@ <script> import Multiselect from 'vue-multiselect' -import { VCardTime } from 'ical.js' +import propertyTitle from './PropertyTitle' export default { name: 'PropertyDateTime', components: { - Multiselect + Multiselect, + propertyTitle }, props: { @@ -63,8 +67,20 @@ export default { default: () => {} }, value: { - type: [VCardTime], + type: [String], default: '' + }, + isFirstProperty: { + type: Boolean, + default: true + } + }, + + computed: { + gridLength() { + let hasTitle = this.isFirstProperty && this.propModel.icon ? 1 : 0 + // length is one & add one space at the end + return hasTitle + 1 + 1 } }, diff --git a/src/components/properties/PropertyMultipleText.vue b/src/components/properties/PropertyMultipleText.vue index 69975db9..3a52b843 100644 --- a/src/components/properties/PropertyMultipleText.vue +++ b/src/components/properties/PropertyMultipleText.vue @@ -21,25 +21,28 @@ --> <template> - <div v-if="propModel" :class="`grid-span-${gridLength}`" class="contact-details-property"> - <div class="contact-details-property-row"> + <div v-if="propModel" :class="`grid-span-${gridLength}`" class="property"> + <!-- title if first element --> + <property-title v-if="isFirstProperty && propModel.icon" :icon="propModel.icon" :readable-name="propModel.readableName" /> + + <div class="property__row"> <!-- type selector --> <multiselect v-if="propModel.options" v-model="selectType" :options="propModel.options" :searchable="false" :placeholder="t('contacts', 'Select type')" - class="multiselect-vue contact-details-label" track-by="id" label="name" /> + class="multiselect-vue property__label" track-by="id" label="name" /> <!-- if we do not support any type on our model but one is set anyway --> - <div v-else-if="selectType" class="contact-details-label">{{ selectType.name }}</div> + <div v-else-if="selectType" class="property__label">{{ selectType.name }}</div> <!-- no options, empty space --> - <div v-else class="contact-details-label">{{ propModel.readableName }}</div> + <div v-else class="property__label">{{ propModel.readableName }}</div> <!-- delete the prop --> - <button :title="t('contacts', 'Delete')" class="icon-delete" @click="deleteProperty" /> + <button :title="t('contacts', 'Delete')" class="property__delete icon-delete" @click="deleteProperty" /> </div> - <div v-for="index in propModel.displayOrder" :key="index" class="contact-details-property-row"> - <div class="contact-details-label">{{ propModel.readableValues[index] }}</div> + <div v-for="index in propModel.displayOrder" :key="index" class="property__row"> + <div class="property__label">{{ propModel.readableValues[index] }}</div> <input v-model="value[index]" type="text"> </div> </div> @@ -47,12 +50,14 @@ <script> import Multiselect from 'vue-multiselect' +import propertyTitle from './PropertyTitle' export default { name: 'PropertyText', components: { - Multiselect + Multiselect, + propertyTitle }, props: { @@ -67,14 +72,20 @@ export default { value: { type: [Array, String, Object], default: '' + }, + isFirstProperty: { + type: Boolean, + default: true } }, computed: { gridLength() { - let hasType = this.propModel.options || this.selectType + let hasType = this.propModel.options || this.selectType ? 1 : 0 + let hasTitle = this.isFirstProperty && this.propModel.icon ? 1 : 0 let length = this.propModel.displayOrder ? this.propModel.displayOrder.length : this.value.length - return hasType ? length + 1 : length + // add one space at the end + return hasType + hasTitle + length + 1 } }, diff --git a/src/components/properties/PropertyText.vue b/src/components/properties/PropertyText.vue index 91019237..6dd72eda 100644 --- a/src/components/properties/PropertyText.vue +++ b/src/components/properties/PropertyText.vue @@ -21,21 +21,24 @@ --> <template> - <div v-if="propModel" class="contact-details-property grid-span-1"> - <div class="contact-details-property-row"> + <div v-if="propModel" :class="`grid-span-${gridLength}`" class="property"> + <!-- title if first element --> + <property-title v-if="isFirstProperty && propModel.icon" :icon="propModel.icon" :readable-name="propModel.readableName" /> + + <div class="property__row"> <!-- type selector --> <multiselect v-if="propModel.options" v-model="selectType" :options="propModel.options" :searchable="false" :placeholder="t('contacts', 'Select type')" - class="multiselect-vue contact-details-label" track-by="id" label="name" /> + class="multiselect-vue property__label" track-by="id" label="name" /> <!-- if we do not support any type on our model but one is set anyway --> - <div v-else-if="selectType" class="contact-details-label">{{ selectType.name }}</div> + <div v-else-if="selectType" class="property__label">{{ selectType.name }}</div> <!-- no options, empty space --> - <div v-else class="contact-details-label">{{ propModel.readableName }}</div> + <div v-else class="property__label">{{ propModel.readableName }}</div> <!-- delete the prop --> - <button :title="t('contacts', 'Delete')" class="icon-delete" @click="deleteProperty" /> + <button :title="t('contacts', 'Delete')" class="property__delete icon-delete" @click="deleteProperty" /> <input v-model="value" type="text"> </div> @@ -44,12 +47,14 @@ <script> import Multiselect from 'vue-multiselect' +import propertyTitle from './PropertyTitle' export default { name: 'PropertyText', components: { - Multiselect + Multiselect, + propertyTitle }, props: { @@ -64,6 +69,18 @@ export default { value: { type: [String], default: '' + }, + isFirstProperty: { + type: Boolean, + default: true + } + }, + + computed: { + gridLength() { + let hasTitle = this.isFirstProperty && this.propModel.icon ? 1 : 0 + // length is one & add one space at the end + return hasTitle + 1 + 1 } }, diff --git a/src/components/properties/PropertyTitle.vue b/src/components/properties/PropertyTitle.vue new file mode 100644 index 00000000..d857a647 --- /dev/null +++ b/src/components/properties/PropertyTitle.vue @@ -0,0 +1,46 @@ +<!-- + - @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> + <h3 class="property__title property__row"> + <div :class="icon" class="property__label property__title--icon" /> + {{ readableName }} + </h3> +</template> + +<script> +export default { + name: 'PropertyTitle', + + props: { + icon: { + type: String, + default: '' + }, + readableName: { + type: String, + default: '' + } + } + +} +</script> |