summaryrefslogtreecommitdiffstats
path: root/src/components
diff options
context:
space:
mode:
authorJohn Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>2018-08-20 17:40:17 +0200
committerJohn Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>2018-08-21 18:17:45 +0200
commit0b5f975b5288e1a9548260f9ac8f47453982f9a7 (patch)
tree41c70a22dc26fe6bf42eb4eb5bee22e94d645e87 /src/components
parent48d0f179817b784eb5133b857ad9981d72dd0ac1 (diff)
Type update, management and auto selection fix
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
Diffstat (limited to 'src/components')
-rw-r--r--src/components/ContactDetails.vue13
-rw-r--r--src/components/ContactDetails/ContactDetailsProperty.vue45
-rw-r--r--src/components/properties/PropertyDateTime.vue17
-rw-r--r--src/components/properties/PropertyGroups.vue45
-rw-r--r--src/components/properties/PropertyMultipleText.vue17
-rw-r--r--src/components/properties/PropertyText.vue19
6 files changed, 112 insertions, 44 deletions
diff --git a/src/components/ContactDetails.vue b/src/components/ContactDetails.vue
index f732befd..95de110d 100644
--- a/src/components/ContactDetails.vue
+++ b/src/components/ContactDetails.vue
@@ -81,24 +81,25 @@
<!-- contact details -->
<section class="contact-details">
<contact-details-property v-for="(property, index) in sortedProperties" :key="index" :index="index"
- :sorted-properties="sortedProperties" :property="property" @updatedcontact="updateContact" />
+ :sorted-properties="sortedProperties" :property="property" :contact="contact"
+ @updatedcontact="updateContact" />
</section>
</template>
</div>
</template>
<script>
-import popoverMenu from './core/popoverMenu'
-import contactDetailsProperty from './ContactDetails/ContactDetailsProperty'
-
-import Contact from '../models/contact'
-import rfcProps from '../models/rfcProps.js'
import ICAL from 'ical.js'
import ClickOutside from 'vue-click-outside'
import Vue from 'vue'
import VTooltip from 'v-tooltip'
import debounce from 'debounce'
+import Contact from '../models/contact'
+import rfcProps from '../models/rfcProps.js'
+
+import popoverMenu from './core/popoverMenu'
+import contactDetailsProperty from './ContactDetails/ContactDetailsProperty'
Vue.use(VTooltip)
diff --git a/src/components/ContactDetails/ContactDetailsProperty.vue b/src/components/ContactDetails/ContactDetailsProperty.vue
index 5b7b2e27..7aa6b0c9 100644
--- a/src/components/ContactDetails/ContactDetailsProperty.vue
+++ b/src/components/ContactDetails/ContactDetailsProperty.vue
@@ -22,14 +22,16 @@
<template>
<!-- If not in the rfcProps then we don't want to display it -->
- <component v-if="propModel && propType !== 'unknown'" :is="componentInstance" :select-type="selectType"
+ <component v-if="propModel && propType !== 'unknown'" :is="componentInstance" :select-type.sync="selectType"
:prop-model="propModel" :value.sync="value" :is-first-property="isFirstProperty"
- :class="{'property--last': isLastProperty}" @delete="deleteProp" />
+ :class="{'property--last': isLastProperty}" :contact="contact" @delete="deleteProp" />
</template>
<script>
import { Property } from 'ical.js'
import rfcProps from '../../models/rfcProps.js'
+import Contact from '../../models/contact'
+
import PropertyText from '../properties/PropertyText'
import PropertyMultipleText from '../properties/PropertyMultipleText'
import PropertyDateTime from '../properties/PropertyDateTime'
@@ -53,6 +55,10 @@ export default {
index: {
type: Number,
default: 0
+ },
+ contact: {
+ type: Contact,
+ default: null
}
},
@@ -122,16 +128,32 @@ export default {
// we only use uppercase strings
.map(str => str.toUpperCase())
- // Compare array and check if the number of exact matches
- // equals the array length to find the exact property
- return this.propModel.options.find(option => selectedType.length === option.id.split(',').reduce((matches, type) => {
- matches += selectedType.indexOf(type) > -1 ? 1 : 0
- return matches
- }, 0))
- } else if (this.type) {
+ // Compare array and score them by how many matches they have to the selected type
+ // sorting directly is cleaner but slower
+ // https://jsperf.com/array-map-and-intersection-perf
+ let matchingTypes = this.propModel.options.map(type => {
+ return {
+ type,
+ // "WORK,HOME" => ['WORK', 'HOME']
+ score: type.id.split(',').filter(value => selectedType.indexOf(value) !== -1).length
+ }
+ })
+
+ // Sort by score, filtering out the null score and selecting the first match
+ let matchingType = matchingTypes
+ .sort((a, b) => b.score - a.score)
+ .filter(type => type.score > 0)[0]
+
+ if (matchingType) {
+ return matchingType.type
+ }
+ }
+ if (this.type) {
+ // vcard 3.0 save pref alongside TYPE
+ let selectedType = this.type.filter(type => type !== 'pref').join(',')
return {
- id: this.type.join(','),
- name: this.type.join(',')
+ id: selectedType,
+ name: selectedType
}
}
return false
@@ -139,6 +161,7 @@ export default {
set(data) {
// ical.js take types as arrays
this.type = data.id.split(',')
+ this.$emit('updatedcontact')
}
},
diff --git a/src/components/properties/PropertyDateTime.vue b/src/components/properties/PropertyDateTime.vue
index b8a1c940..2d6205b4 100644
--- a/src/components/properties/PropertyDateTime.vue
+++ b/src/components/properties/PropertyDateTime.vue
@@ -27,9 +27,10 @@
<div class="property__row">
<!-- type selector -->
- <multiselect v-if="propModel.options" v-model="selectType"
+ <multiselect v-if="propModel.options" v-model="localType"
:options="propModel.options" :searchable="false" :placeholder="t('contacts', 'Select type')"
- class="multiselect-vue property__label" track-by="id" label="name" />
+ class="multiselect-vue 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>
@@ -41,7 +42,7 @@
<button :title="t('contacts', 'Delete')" class="property__delete icon-delete" @click="deleteProperty" />
<input v-model.trim="localValue" class="property__value" type="text"
- @input="updateProp">
+ @input="updateValue">
</div>
</div>
</template>
@@ -85,7 +86,8 @@ export default {
data() {
return {
- localValue: this.value
+ localValue: this.value,
+ localType: this.selectType
}
},
@@ -110,9 +112,14 @@ export default {
/**
* Debounce and send update event to parent
*/
- updateProp: debounce(function(e) {
+ updateValue: debounce(function(e) {
// https://vuejs.org/v2/guide/components-custom-events.html#sync-Modifier
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)
}
}
diff --git a/src/components/properties/PropertyGroups.vue b/src/components/properties/PropertyGroups.vue
index 0fb0cd2b..01976b4a 100644
--- a/src/components/properties/PropertyGroups.vue
+++ b/src/components/properties/PropertyGroups.vue
@@ -25,11 +25,13 @@
<div class="property__row">
<div class="property__label">{{ propModel.readableName }}</div>
+ <!-- multiselect taggable groups with a limit to 3 groups shown -->
<multiselect v-model="localValue" :options="groups" :placeholder="t('contacts', 'Add contact in group')"
:limit="3" :multiple="true" :taggable="true"
:close-on-select="false" tag-placeholder="create" class="multiselect-vue property__value"
- @tag="createGroup" @select="addContactToGroup" @remove="removeContactToGroup">
- <span v-tooltip.auto="formatGroupsTitle" slot="limit" class="multiselect__limit">+{{ localValue.length - 2 }}</span>
+ @tag="validateGroup" @select="addContactToGroup" @remove="removeContactToGroup">
+ <!-- show how many groups are hidden and add tooltip -->
+ <span v-tooltip.auto="formatGroupsTitle" slot="limit" class="multiselect__limit">+{{ localValue.length - 3 }}</span>
<span slot="noResult">{{ t('settings', 'No results') }}</span>
</multiselect>
</div>
@@ -88,29 +90,50 @@ export default {
/**
* Debounce and send update event to parent
*/
- updateProp: debounce(function(e) {
+ updateValue: debounce(function(e) {
// https://vuejs.org/v2/guide/components-custom-events.html#sync-Modifier
this.$emit('update:value', this.localValue)
}, 500),
- addContactToGroup(group) {
- console.log(group)
+ /**
+ * Dispatch contact addition to group
+ *
+ * @param {String} groupName the group name
+ */
+ addContactToGroup(groupName) {
this.$store.dispatch('addContactToGroup', {
contact: this.contact,
- group
+ groupName
})
},
- removeContactToGroup(group) {
- console.log(group)
+ /**
+ * Dispatch contact removal from group
+ *
+ * @param {String} groupName the group name
+ */
+ removeContactToGroup(groupName) {
this.$store.dispatch('removeContactToGroup', {
contact: this.contact,
- group
+ groupName
})
},
- createGroup(group) {
- console.log(group)
+ /**
+ * Validate groupname and dispatch creation
+ *
+ * @param {String} groupName the group name
+ * @returns {Boolean}
+ */
+ validateGroup(groupName) {
+ // Only allow characters without vcard special chars
+ let groupRegex = /^[^;,:]+$/gmi
+ if (groupName.match(groupRegex)) {
+ this.addContactToGroup(groupName)
+ this.localValue.push(groupName)
+ return true
+ }
+ return false
}
}
}
diff --git a/src/components/properties/PropertyMultipleText.vue b/src/components/properties/PropertyMultipleText.vue
index 3252d3da..1ac0e8ad 100644
--- a/src/components/properties/PropertyMultipleText.vue
+++ b/src/components/properties/PropertyMultipleText.vue
@@ -27,9 +27,10 @@
<div class="property__row">
<!-- type selector -->
- <multiselect v-if="propModel.options" v-model="selectType"
+ <multiselect v-if="propModel.options" v-model="localType"
:options="propModel.options" :searchable="false" :placeholder="t('contacts', 'Select type')"
- class="multiselect-vue property__label" track-by="id" label="name" />
+ class="multiselect-vue 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>
@@ -44,7 +45,7 @@
<div v-for="index in propModel.displayOrder" :key="index" class="property__row">
<div class="property__label">{{ propModel.readableValues[index] }}</div>
<input v-model.trim="localValue[index]" class="property__value" type="text"
- @input="updateProp">
+ @input="updateValue">
</div>
</div>
</template>
@@ -87,7 +88,8 @@ export default {
data() {
return {
- localValue: this.value
+ localValue: this.value,
+ localType: this.selectType
}
},
@@ -113,9 +115,14 @@ export default {
/**
* Debounce and send update event to parent
*/
- updateProp: debounce(function(e) {
+ updateValue: debounce(function(e) {
// https://vuejs.org/v2/guide/components-custom-events.html#sync-Modifier
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)
}
}
diff --git a/src/components/properties/PropertyText.vue b/src/components/properties/PropertyText.vue
index cdd613a9..a15caa4d 100644
--- a/src/components/properties/PropertyText.vue
+++ b/src/components/properties/PropertyText.vue
@@ -27,9 +27,10 @@
<div class="property__row">
<!-- type selector -->
- <multiselect v-if="propModel.options" v-model="selectType"
+ <multiselect v-if="propModel.options" v-model="localType"
:options="propModel.options" :searchable="false" :placeholder="t('contacts', 'Select type')"
- class="multiselect-vue property__label" track-by="id" label="name" />
+ class="multiselect-vue 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>
@@ -40,8 +41,8 @@
<!-- delete the prop -->
<button :title="t('contacts', 'Delete')" class="property__delete icon-delete" @click="deleteProperty" />
- <input v-model.trim="localValue"class="property__value" type="text"
- @input="updateProp">
+ <input v-model.trim="localValue" class="property__value" type="text"
+ @input="updateValue">
</div>
</div>
</template>
@@ -84,7 +85,8 @@ export default {
data() {
return {
- localValue: this.value
+ localValue: this.value,
+ localType: this.selectType
}
},
@@ -108,9 +110,14 @@ export default {
/**
* Debounce and send update event to parent
*/
- updateProp: debounce(function(e) {
+ updateValue: debounce(function(e) {
// https://vuejs.org/v2/guide/components-custom-events.html#sync-Modifier
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)
}
}