Skip to content

Commit

Permalink
Cleanup code with Property Mixin
Browse files Browse the repository at this point in the history
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
  • Loading branch information
skjnldsv committed Nov 12, 2018
1 parent faf4e4a commit 6822a6d
Show file tree
Hide file tree
Showing 18 changed files with 231 additions and 435 deletions.
6 changes: 6 additions & 0 deletions css/ContactDetails.scss
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@
padding: 14px;
border-radius: 22px;
cursor: pointer;
background-size: 16px;
opacity: .7;
&:hover,
&:focus {
opacity: 1;
}
&.header-icon--pulse {
margin: 8px;
width: 16px;
Expand Down
28 changes: 21 additions & 7 deletions css/Properties/Properties.scss
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,21 @@
}

// mouse feedback
&.multiselect {
&:hover,
&:focus,
&:active {
opacity: 1;
.multiselect__tags {
border-color: var(--color-border-dark);
&:hover,
&:focus,
&:active {
opacity: 1;
.multiselect__tags {
border-color: var(--color-border-dark);
}
}

// read-only mode
&.multiselect--disabled {
&, .multiselect__single {
&, &:hover, &:focus &:active {
background-color: var(--color-main-background) !important;
border-color: transparent !important;
}
}
}
Expand All @@ -124,10 +132,16 @@
// property value within row, after label
&__value {
flex: 1 1;

textarea& {
align-self: flex-start;
min-height: 2 * $grid-height-unit - 2*$grid-input-margin;
max-height: 5 * $grid-height-unit - 2*$grid-input-margin;
}

// read-only mode
&:read-only {
border-color: var(--color-border-dark);
}
}
}
2 changes: 1 addition & 1 deletion css/Settings/SettingsAddressbook.scss
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,4 @@
&--disabled &__name {
opacity: .5;
}
}
}
6 changes: 5 additions & 1 deletion css/icons.scss
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,8 @@

.icon-address-book {
@include icon-color('address-book', 'contacts', $color-black, 1);
}
}

.icon-eye-white {
@include icon-color('eye', 'contacts', $color-white, 1);
}
2 changes: 1 addition & 1 deletion img/address-book.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions img/eye.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
},
"dependencies": {
"@babel/polyfill": "^7.0.0",
"axios": "^0.18.0",
"cdav-library": "github:nextcloud/cdav-library",
"debounce": "^1.2.0",
"ical.js": "^1.3.0",
Expand All @@ -48,7 +47,6 @@
"vue": "^2.5.17",
"vue-click-outside": "^1.0.7",
"vue-clipboard2": "^0.2.1",
"vue-multiselect": "^2.1.3",
"vue-router": "^3.0.1",
"vuex": "^3.0.1",
"vuex-router-sync": "^5.0.0"
Expand Down
47 changes: 36 additions & 11 deletions src/components/ContactDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,17 @@
<!-- fullname, org, title -->
<div id="contact-header-infos">
<h2>
<input id="contact-fullname" v-model="contact.fullName" :disabled="contact.addressbook.readOnly"
<input id="contact-fullname" v-model="contact.fullName" :readonly="contact.addressbook.readOnly"
:placeholder="t('contacts', 'Name')" type="text" autocomplete="off"
autocorrect="off" spellcheck="false" name="fullname"
value="" @input="debounceUpdateContact">
</h2>
<div id="details-org-container">
<input id="contact-org" v-model="contact.org" :disabled="contact.addressbook.readOnly"
<input id="contact-org" v-model="contact.org" :readonly="contact.addressbook.readOnly"
:placeholder="t('contacts', 'Company')" type="text" autocomplete="off"
autocorrect="off" spellcheck="false" name="org"
value="" @input="debounceUpdateContact">
<input id="contact-title" v-model="contact.title" :disabled="contact.addressbook.readOnly"
<input id="contact-title" v-model="contact.title" :readonly="contact.addressbook.readOnly"
:placeholder="t('contacts', 'Title')" type="text" autocomplete="off"
autocorrect="off" spellcheck="false" name="title"
value="" @input="debounceUpdateContact">
Expand All @@ -67,7 +67,13 @@

<!-- actions -->
<div id="contact-header-actions">
<div v-tooltip.bottom="warning" :class="{'icon-loading-small': loadingUpdate, 'header-icon--pulse icon-error-white': warning}" class="header-icon" />
<a v-tooltip.bottom="{
content: warning ? warning.msg : '',
trigger: 'hover focus'
}"
v-if="loadingUpdate || warning"
:class="{'icon-loading-small': loadingUpdate,
[`${warning.icon}`]: warning}" class="header-icon" href="#" />
<div v-tooltip="{
content: conflict,
show: true,
Expand Down Expand Up @@ -95,12 +101,15 @@
:sorted-properties="sortedProperties" :property="property" :contact="contact"
@updatedcontact="updateContact" />

<!-- addressbook change select - no last property because class is not applied here-->
<!-- 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) -->
<property-select :prop-model="addressbookModel" :value.sync="addressbook" :is-first-property="true"
:is-last-property="false" class="property--addressbooks" />
:is-last-property="false" :property="{}" class="property--addressbooks" />

<!-- new property select -->
<add-new-prop :contact="contact" />
<add-new-prop v-if="!isReadOnly" :contact="contact" />
</section>
</template>
</div>
Expand Down Expand Up @@ -155,27 +164,43 @@ export default {
},
computed: {
isReadOnly() {
if (this.contact.addressbook) {
return this.contact.addressbook.readOnly
}
return false
},
/**
* Warning message
* Warning messages
*
* @returns {string|undefined}
* @returns {Object|Boolean}
*/
warning() {
if (!this.contact.dav) {
return t('contacts', 'This contact is not yet synced. Edit it to trigger a change.')
return {
icon: 'icon-error-white header-icon--pulse',
msg: t('contacts', 'This contact is not yet synced. Edit it to trigger a change.')
}
} else if (this.isReadOnly) {
return {
icon: 'icon-eye-white',
msg: t('contacts', 'This contact is in read-only mode. You do not have permission to edit this contact.')
}
}
return false
},
/**
* Conflict message
*
* @returns {string|undefined}
* @returns {String|Boolean}
*/
conflict() {
if (this.contact.conflict) {
return t('contacts', 'The contact you were trying to edit has changed. Please manually refresh the contact. Any further edits will be discarded.')
}
return false
},
/**
Expand Down
8 changes: 7 additions & 1 deletion src/components/ContactDetails/ContactDetailsProperty.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
:prop-model="propModel" :value.sync="value" :is-first-property="isFirstProperty"
:property="property" :is-last-property="isLastProperty" :class="{'property--last': isLastProperty}"
:contact="contact" :prop-name="propName" :prop-type="propType"
:options="sortedModelOptions"
:options="sortedModelOptions" :is-read-only="isReadOnly"
@delete="deleteProp" />
</template>

Expand Down Expand Up @@ -109,6 +109,12 @@ export default {
}
return true
},
isReadOnly() {
if (this.contact.addressbook) {
return this.contact.addressbook.readOnly
}
return false
},
/**
* Return the type of the prop e.g. FN
Expand Down
68 changes: 8 additions & 60 deletions src/components/Properties/PropertyDateTime.vue
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
<!-- type selector -->
<multiselect v-if="propModel.options" v-model="localType"
:options="options" :searchable="false" :placeholder="t('contacts', 'Select type')"
class="property__label" track-by="id" label="name"
@input="updateType" />
:disabled="isReadOnly" class="property__label" track-by="id"
label="name" @input="updateType" />

<!-- if we do not support any type on our model but one is set anyway -->
<div v-else-if="selectType" class="property__label">{{ selectType.name }}</div>
Expand All @@ -40,12 +40,13 @@
<div v-else class="property__label">{{ propModel.readableName }}</div>

<!-- delete the prop -->
<button :title="t('contacts', 'Delete')" class="property__delete icon-delete" @click="deleteProperty" />
<button v-if="!isReadOnly" :title="t('contacts', 'Delete')" class="property__delete icon-delete"
@click="deleteProperty" />

<!-- Real input where the picker shows -->
<datetime-picker :value="localValue.toJSDate()" :minute-step="10" :lang="lang"
:clearable="false" :first-day-of-week="firstDay" :type="inputType"
confirm @confirm="updateValue" />
:readonly="isReadOnly" confirm @confirm="updateValue" />
</div>
</div>
</template>
Expand All @@ -56,6 +57,7 @@ import moment from 'moment'
import { DatetimePicker } from 'nextcloud-vue'
import { VCardTime } from 'ical.js'
import PropertyMixin from 'Mixins/PropertyMixin'
import PropertyTitle from './PropertyTitle'
/**
Expand Down Expand Up @@ -137,45 +139,18 @@ export default {
PropertyTitle
},
mixins: [PropertyMixin],
props: {
selectType: {
type: [Object, Boolean],
default: () => {}
},
propModel: {
type: Object,
default: () => {},
required: true
},
value: {
type: VCardTime,
default: '',
required: true
},
options: {
type: Array,
default: () => []
},
property: {
type: Object,
default: () => {},
required: true
},
isFirstProperty: {
type: Boolean,
default: true
},
isLastProperty: {
type: Boolean,
default: true
}
},
data() {
return {
localValue: this.value,
localType: this.selectType,
// input type following DatePicker docs
inputType: this.property.getDefaultType() === 'date-time' || this.property.getDefaultType() === 'date-and-or-time'
? 'datetime'
Expand Down Expand Up @@ -205,20 +180,6 @@ export default {
}
},
watch: {
/**
* Since we're updating a local data based on the value prop,
* we need to make sure to update the local data on pop change
* TODO: check if this create performance drop
*/
value: function() {
this.localValue = this.value
},
selectType: function() {
this.localType = this.selectType
}
},
mounted() {
// Load the locale
// convert format like en_GB to en-gb for `moment.js`
Expand All @@ -245,14 +206,6 @@ export default {
},
methods: {
/**
* Delete the property
*/
deleteProperty() {
this.$emit('delete')
},
/**
* Debounce and send update event to parent
*/
Expand All @@ -274,11 +227,6 @@ export default {
// https://vuejs.org/v2/guide/components-custom-events.html#sync-Modifier
// Use moment to convert the JsDate to Object
this.$emit('update:value', this.localValue)
}, 500),
updateType: debounce(function(e) {
// https://vuejs.org/v2/guide/components-custom-events.html#sync-Modifier
this.$emit('update:selectType', this.localType)
}, 500)
}
}
Expand Down
Loading

0 comments on commit 6822a6d

Please sign in to comment.