diff options
author | John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com> | 2018-08-20 17:40:17 +0200 |
---|---|---|
committer | John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com> | 2018-08-21 18:17:45 +0200 |
commit | 0b5f975b5288e1a9548260f9ac8f47453982f9a7 (patch) | |
tree | 41c70a22dc26fe6bf42eb4eb5bee22e94d645e87 /src/components | |
parent | 48d0f179817b784eb5133b857ad9981d72dd0ac1 (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.vue | 13 | ||||
-rw-r--r-- | src/components/ContactDetails/ContactDetailsProperty.vue | 45 | ||||
-rw-r--r-- | src/components/properties/PropertyDateTime.vue | 17 | ||||
-rw-r--r-- | src/components/properties/PropertyGroups.vue | 45 | ||||
-rw-r--r-- | src/components/properties/PropertyMultipleText.vue | 17 | ||||
-rw-r--r-- | src/components/properties/PropertyText.vue | 19 |
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) } } |