diff options
author | John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com> | 2018-11-13 17:33:46 +0100 |
---|---|---|
committer | John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com> | 2018-11-13 17:44:02 +0100 |
commit | bbc3d8e130bda3d17ad8de310a94c01c4531134c (patch) | |
tree | 99928de259217361e3359ce08f8cdb1362886dc0 | |
parent | 91d64916333490b70c636a5a25627e8d6b39db5d (diff) |
Added external link support
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
-rw-r--r-- | css/ContactDetailsAvatar.scss | 4 | ||||
-rw-r--r-- | css/Properties/Properties.scss | 82 | ||||
-rw-r--r-- | css/icons.scss | 4 | ||||
-rw-r--r-- | img/phone.svg | 2 | ||||
-rw-r--r-- | src/components/ContactDetails.vue | 4 | ||||
-rw-r--r-- | src/components/Properties/PropertyText.vue | 37 | ||||
-rw-r--r-- | src/models/rfcProps.js | 2 | ||||
-rw-r--r-- | src/views/Contacts.vue | 2 |
8 files changed, 99 insertions, 38 deletions
diff --git a/css/ContactDetailsAvatar.scss b/css/ContactDetailsAvatar.scss index 95a7cf1a..34920f37 100644 --- a/css/ContactDetailsAvatar.scss +++ b/css/ContactDetailsAvatar.scss @@ -52,8 +52,8 @@ width: 100%; height: 100%; &:hover, - a:active, - a:focus { + &:active, + &:focus { opacity: .7; } } diff --git a/css/Properties/Properties.scss b/css/Properties/Properties.scss index 817627c8..d27ee5f8 100644 --- a/css/Properties/Properties.scss +++ b/css/Properties/Properties.scss @@ -35,37 +35,12 @@ &--addressbooks &__delete { display: none !important; } - - &__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; } @@ -129,7 +104,7 @@ } } - // property value within row, after label + // Property value within row, after label &__value { flex: 1 1; @@ -143,5 +118,58 @@ &:read-only { border-color: var(--color-border-dark); } + input&--with-ext { + // ext icon width + 8px padding + padding-right: 24px; + &:hover, + &:focus, + &:active { + ~ .property__ext, + ~ .property__delete { + opacity: .5; + } + } + } + } + + // show ext & delete button on full row hover + &:hover &__ext, + &:hover &__delete { + opacity: .5; + } + + // External link (tel, mailto, http, ftp...) + &__ext { + position: absolute; + // property row rightPadding + 8px; + right: 52px; + opacity: 0; + &:hover, + &:focus, + &:active { + opacity: .7; + // still show the delete button for keyboard accessibility + ~ .property__delete { + opacity: .7; + } + } + } + + // Delete property button + &__delete { + position: absolute; + top: 0; + right: 0; + width: $grid-height-unit; + height: $grid-height-unit; + margin: 0; + border: 0; + background-color: transparent; + opacity: 0; + &:hover, + &:active, + &:focus { + opacity: .7; + } } } diff --git a/css/icons.scss b/css/icons.scss index 00f390e9..5549a162 100644 --- a/css/icons.scss +++ b/css/icons.scss @@ -28,6 +28,10 @@ @include icon-color('address-book', 'contacts', $color-black, 1); } +.icon-phone { + @include icon-color('phone', 'contacts', $color-black, 1); +} + .icon-eye-white { @include icon-color('eye', 'contacts', $color-white, 1); } diff --git a/img/phone.svg b/img/phone.svg new file mode 100644 index 00000000..8ced1725 --- /dev/null +++ b/img/phone.svg @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="utf-8"?> +<svg width="16" height="16" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1600 1240q0 27-10 70.5t-21 68.5q-21 50-122 106-94 51-186 51-27 0-53-3.5t-57.5-12.5-47-14.5-55.5-20.5-49-18q-98-35-175-83-127-79-264-216t-216-264q-48-77-83-175-3-9-18-49t-20.5-55.5-14.5-47-12.5-57.5-3.5-53q0-92 51-186 56-101 106-122 25-11 68.5-21t70.5-10q14 0 21 3 18 6 53 76 11 19 30 54t35 63.5 31 53.5q3 4 17.5 25t21.5 35.5 7 28.5q0 20-28.5 50t-62 55-62 53-28.5 46q0 9 5 22.5t8.5 20.5 14 24 11.5 19q76 137 174 235t235 174q2 1 19 11.5t24 14 20.5 8.5 22.5 5q18 0 46-28.5t53-62 55-62 50-28.5q14 0 28.5 7t35.5 21.5 25 17.5q25 15 53.5 31t63.5 35 54 30q70 35 76 53 3 7 3 21z"/></svg>
\ No newline at end of file diff --git a/src/components/ContactDetails.vue b/src/components/ContactDetails.vue index 05a4d483..dd4cb3ca 100644 --- a/src/components/ContactDetails.vue +++ b/src/components/ContactDetails.vue @@ -97,7 +97,7 @@ <!-- properties iteration --> <!-- using contact.key in the key and index as key to avoid conflicts between similar data and exact key --> - <contact-property v-for="(property, index) in sortedProperties" :key="index+contact.key" :index="index" + <contact-property v-for="(property, index) in sortedProperties" :key="`${index}-${contact.key}-${property.name}`" :index="index" :sorted-properties="sortedProperties" :property="property" :contact="contact" @updatedcontact="updateContact" /> @@ -106,7 +106,7 @@ 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="true" :property="{}" class="property--addressbooks" /> + :is-last-property="true" :property="{}" class="property--addressbooks property--last" /> <!-- new property select --> <add-new-prop v-if="!isReadOnly" :contact="contact" /> diff --git a/src/components/Properties/PropertyText.vue b/src/components/Properties/PropertyText.vue index 7c9fd99e..fa5ac71a 100644 --- a/src/components/Properties/PropertyText.vue +++ b/src/components/Properties/PropertyText.vue @@ -39,10 +39,6 @@ <!-- no options, empty space --> <div v-else class="property__label">{{ propModel.readableName }}</div> - <!-- delete the prop --> - <button v-if="!isReadOnly" :title="t('contacts', 'Delete')" class="property__delete icon-delete" - @click="deleteProperty" /> - <!-- textarea for note --> <textarea v-if="propName === 'note'" id="textarea" ref="textarea" v-model.trim="localValue" :type="type" :readonly="isReadOnly" @@ -51,7 +47,16 @@ <!-- OR default to input --> <input v-else v-model.trim="localValue" :type="type" - :readonly="isReadOnly" class="property__value" @input="updateValue"> + :readonly="isReadOnly" :class="{'property__value--with-ext': haveExtHandler}" class="property__value" + @input="updateValue"> + + <!-- external link --> + <a v-if="haveExtHandler" :href="externalHandler" class="property__ext icon-external" + target="_blank" /> + + <!-- delete the prop --> + <button v-if="!isReadOnly" :title="t('contacts', 'Delete')" class="property__delete icon-delete" + @click="deleteProperty" /> </div> </div> </template> @@ -115,6 +120,28 @@ export default { return 'url' } return 'text' + }, + URLScheme() { + if (this.propName === 'tel') { + return 'tel:' + } else if (this.propName === 'email') { + return 'mailto:' + // if no scheme (roughly checking for the colon char) + } else if (this.propType === 'uri' && this.value.indexOf(':') === -1) { + return 'https://' + } else if (this.propType === 'uri') { + return '' // return empty, we already have a scheme in the value + } + return false + }, + // format external link + externalHandler() { + if (this.URLScheme !== false) { + return `${this.URLScheme}${this.value}` + } + }, + haveExtHandler() { + return this.externalHandler && this.value && this.value.length > 0 } }, diff --git a/src/models/rfcProps.js b/src/models/rfcProps.js index 8051c1a0..3f7efe53 100644 --- a/src/models/rfcProps.js +++ b/src/models/rfcProps.js @@ -146,7 +146,7 @@ const properties = { tel: { multiple: true, readableName: t('contacts', 'Phone'), - icon: 'icon-comment', + icon: 'icon-phone', default: true, defaultValue: { value: '', diff --git a/src/views/Contacts.vue b/src/views/Contacts.vue index 14395b89..971e631b 100644 --- a/src/views/Contacts.vue +++ b/src/views/Contacts.vue @@ -108,7 +108,7 @@ export default { importState() { return this.$store.getters.getImportState }, - + // first enabled addressbook of the list defaultAddressbook() { return this.addressbooks.find(addressbook => !addressbook.readOnly && addressbook.enabled) |