summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.eslintrc.js9
-rw-r--r--css/icons.scss29
-rw-r--r--img/no-calendar.svg1
-rw-r--r--package-lock.json25
-rw-r--r--package.json3
-rw-r--r--src/ContactsRoot.vue (renamed from src/App.vue)0
-rw-r--r--src/components/Actions/ActionCopyNtoFN.vue51
-rw-r--r--src/components/Actions/ActionToggleYear.vue93
-rw-r--r--src/components/ContactDetails.vue2
-rw-r--r--src/components/ContactDetails/ContactDetailsAddNewProp.vue6
-rw-r--r--src/components/ContactDetails/ContactDetailsAvatar.vue2
-rw-r--r--src/components/ContactDetails/ContactDetailsProperty.vue14
-rw-r--r--src/components/Properties/PropertyActions.vue10
-rw-r--r--src/components/Properties/PropertyDateTime.vue70
-rw-r--r--src/components/Properties/PropertyGroups.vue2
-rw-r--r--src/components/Properties/PropertyMultipleText.vue8
-rw-r--r--src/components/Properties/PropertySelect.vue6
-rw-r--r--src/components/Properties/PropertyText.vue2
-rw-r--r--src/components/Settings/SettingsAddressbookShare.vue2
-rw-r--r--src/components/Settings/SettingsAddressbookSharee.vue2
-rw-r--r--src/main.js3
-rw-r--r--src/mixins/ActionsMixin.js32
-rw-r--r--src/mixins/PropertyMixin.js3
-rw-r--r--src/models/rfcProps.js27
-rw-r--r--src/services/checks/badGenderType.js2
-rw-r--r--src/views/Contacts.vue2
26 files changed, 309 insertions, 97 deletions
diff --git a/.eslintrc.js b/.eslintrc.js
index 7c652642..8aabc2d3 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -27,6 +27,7 @@ module.exports = {
'plugin:node/recommended',
'plugin:vue/essential',
'plugin:vue/recommended',
+ 'plugin:nextcloud/recommended',
'standard'
],
settings: {
@@ -73,8 +74,14 @@ module.exports = {
// es6 import/export and require
'node/no-unpublished-require': ['off'],
'node/no-unsupported-features/es-syntax': ['off'],
- // kebab case components for vuejs
+ // PascalCase components names for vuejs
+ // https://vuejs.org/v2/style-guide/#Single-file-component-filename-casing-strongly-recommended
'vue/component-name-in-template-casing': ['error', 'PascalCase'],
+ // force name
+ 'vue/match-component-file-name': ['error', {
+ 'extensions': ['jsx', 'vue', 'js'],
+ 'shouldMatchCase': true
+ }],
// space before self-closing elements
'vue/html-closing-bracket-spacing': 'error',
// no ending html tag on a new line
diff --git a/css/icons.scss b/css/icons.scss
index a543346f..4f74e7d4 100644
--- a/css/icons.scss
+++ b/css/icons.scss
@@ -20,29 +20,14 @@
*
*/
-.icon-social {
- @include icon-color('social', 'contacts', $color-black, 1);
-}
-
-.icon-qrcode {
- @include icon-color('qrcode', 'contacts', $color-black, 2);
-}
-
-.icon-address-book {
- @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);
-}
-
-.icon-up {
- @include icon-color('up', 'contacts', $color-black, 1);
-}
+@include icon-black-white('social', 'contacts', 1);
+@include icon-black-white('qrcode', 'contacts', 1);
+@include icon-black-white('address-book', 'contacts', 1);
+@include icon-black-white('phone', 'contacts', 1);
+@include icon-black-white('eye', 'contacts', 1);
+@include icon-black-white('up', 'contacts', 1);
+@include icon-black-white('no-calendar', 'contacts', 1);
.icon-up-force-white {
// using #fffffe to trick the accessibility dark theme icon invert
diff --git a/img/no-calendar.svg b/img/no-calendar.svg
new file mode 100644
index 00000000..6a328ca1
--- /dev/null
+++ b/img/no-calendar.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16"><path d="M4 1c-.5 0-1 .5-1 1v2c0 .5.5 1 1 1s1-.5 1-1V2c0-.5-.5-1-1-1zm8 0c-.5 0-1 .5-1 1v2c0 .223.11.439.264.617L13 2.881V2c0-.5-.5-1-1-1zM5.5 3v1c0 .831-.5 1.5-1.5 1.5S2.5 5 2.5 4v-.938A1.998 1.998 0 0 0 1 5v8c0 .524.202.996.53 1.352L3 12.88V8h4.88l2.942-2.941C10.61 4.81 10.5 4.46 10.5 4V3h-5zM15 5.123L12.123 8H13v5H7.123l-2 2H13c1.108 0 2-.892 2-2V5.123z"/><path d="M13.773 2.756L1.903 14.627 3.274 16 15.146 4.129z" paint-order="fill markers stroke"/></svg> \ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 6b1fa06d..fc4a8632 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4053,6 +4053,15 @@
}
}
},
+ "eslint-plugin-nextcloud": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-nextcloud/-/eslint-plugin-nextcloud-0.3.0.tgz",
+ "integrity": "sha512-LUD2qdirGL0BRt4uaMDGxen17mWVq9JwuGDt7P7Celz7bzdu0X48RrS8mhXn9e0w78+nYN5kPoULG2Bw04r4HA==",
+ "dev": true,
+ "requires": {
+ "requireindex": "~1.2.0"
+ }
+ },
"eslint-plugin-node": {
"version": "9.2.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-9.2.0.tgz",
@@ -5763,7 +5772,7 @@
"dependencies": {
"readable-stream": {
"version": "3.0.6",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.0.6.tgz",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-3.0.6.tgz",
"integrity": "sha512-9E1oLoOWfhSXHGv6QlwXJim7uNzd9EVlWK+21tCU9Ju/kR0/p2AZYPz4qSchgO8PlLIH4FpZYfzwS+rEksZjIg==",
"dev": true,
"requires": {
@@ -8810,7 +8819,7 @@
},
"readable-stream": {
"version": "2.3.6",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
"integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
"dev": true,
"requires": {
@@ -9127,6 +9136,12 @@
"integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
"dev": true
},
+ "requireindex": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz",
+ "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==",
+ "dev": true
+ },
"resolve": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz",
@@ -9946,7 +9961,7 @@
},
"stream-browserify": {
"version": "2.0.1",
- "resolved": "http://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz",
+ "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz",
"integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=",
"dev": true,
"requires": {
@@ -10012,7 +10027,7 @@
},
"string_decoder": {
"version": "1.1.1",
- "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dev": true,
"requires": {
@@ -10752,7 +10767,7 @@
},
"tty-browserify": {
"version": "0.0.0",
- "resolved": "http://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
+ "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
"integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=",
"dev": true
},
diff --git a/package.json b/package.json
index 8467372d..3e2358e2 100644
--- a/package.json
+++ b/package.json
@@ -46,7 +46,7 @@
"nextcloud-dialogs": "0.0.3",
"nextcloud-l10n": "0.1.0",
"nextcloud-router": "0.0.8",
- "nextcloud-vue": "^0.12.1",
+ "nextcloud-vue": "^0.12.2",
"p-limit": "^2.2.1",
"p-queue": "^6.1.1",
"qr-image": "^3.2.0",
@@ -81,6 +81,7 @@
"eslint-import-resolver-webpack": "^0.11.1",
"eslint-loader": "^3.0.0",
"eslint-plugin-import": "^2.18.2",
+ "eslint-plugin-nextcloud": "^0.3.0",
"eslint-plugin-node": "^9.2.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.1",
diff --git a/src/App.vue b/src/ContactsRoot.vue
index 06ab1b3e..06ab1b3e 100644
--- a/src/App.vue
+++ b/src/ContactsRoot.vue
diff --git a/src/components/Actions/ActionCopyNtoFN.vue b/src/components/Actions/ActionCopyNtoFN.vue
new file mode 100644
index 00000000..ccce1fb8
--- /dev/null
+++ b/src/components/Actions/ActionCopyNtoFN.vue
@@ -0,0 +1,51 @@
+<!--
+ - @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>
+ <ActionButton icon="icon-up" @click="copyNtoFN">
+ {{ t('contacts', 'Copy to full name') }}
+ </ActionButton>
+</template>
+<script>
+import { ActionButton } from 'nextcloud-vue'
+import ActionsMixin from 'Mixins/ActionsMixin'
+
+export default {
+ name: 'ActionCopyNtoFN',
+ components: {
+ ActionButton
+ },
+ mixins: [ActionsMixin],
+ methods: {
+ copyNToFN() {
+ console.info(this.component)
+ if (this.component.contact.vCard.hasProperty('n')) {
+ // Stevenson;John;Philip,Paul;Dr.;Jr.,M.D.,A.C.P.
+ // -> John Stevenson
+ const n = this.component.contact.vCard.getFirstPropertyValue('n')
+ this.component.contact.fullName = n.slice(0, 2).reverse().join(' ')
+ this.component.updateContact()
+ }
+ }
+ }
+}
+</script>
diff --git a/src/components/Actions/ActionToggleYear.vue b/src/components/Actions/ActionToggleYear.vue
new file mode 100644
index 00000000..2e43310d
--- /dev/null
+++ b/src/components/Actions/ActionToggleYear.vue
@@ -0,0 +1,93 @@
+<!--
+ - @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>
+ <ActionButton :icon="icon" @click="toggleYear">
+ {{ omitYear ? t('contacts', 'Add year') : t('contacts', 'Omit year') }}
+ </ActionButton>
+</template>
+<script>
+import { ActionButton } from 'nextcloud-vue'
+import ActionsMixin from 'Mixins/ActionsMixin'
+
+export default {
+ name: 'ActionToggleYear',
+ components: {
+ ActionButton
+ },
+ mixins: [ActionsMixin],
+ data() {
+ return {
+ omitYear: false
+ }
+ },
+
+ computed: {
+ icon() {
+ return this.omitYear
+ ? 'icon-calendar-dark'
+ : 'icon-no-calendar'
+ },
+ text() {
+ return this.omitYear
+ ? t('contacts', 'Add year')
+ : t('contacts', 'Omit year')
+ }
+ },
+
+ beforeMount() {
+ this.omitYear = !!this.component.property.getFirstParameter('x-apple-omit-year')
+ || !this.component.value.year // if null
+ },
+
+ methods: {
+ toggleYear() {
+ const dateObject = this.component.localValue.toJSON()
+
+ // year was already ignored: adding it back
+ if (this.omitYear) {
+ this.$nextTick(() => {
+ this.component.updateValue(dateObject, true)
+ })
+
+ } else if (this.component.localContact.version === '4.0') {
+ // year was already displayed: removing it
+ // and use --0124 format
+ dateObject.year = null
+ this.component.updateValue(dateObject)
+ } else {
+ // --0124 format is only for vcards 4.0
+ // using x-apple-omit-year custom parameter
+ const year = this.component.value.year
+ if (this.component.value.year) {
+ this.component.property.setParameter('x-apple-omit-year', parseInt(year).toString())
+ this.$nextTick(() => {
+ this.component.updateValue(dateObject)
+ })
+ }
+ }
+
+ this.omitYear = !this.omitYear
+ }
+ }
+}
+</script>
diff --git a/src/components/ContactDetails.vue b/src/components/ContactDetails.vue
index 9c8b6317..86b0333d 100644
--- a/src/components/ContactDetails.vue
+++ b/src/components/ContactDetails.vue
@@ -128,7 +128,7 @@
:prop-model="addressbookModel" :value.sync="addressbook"
:is-first-property="true" :is-last-property="true"
:property="{}"
- class="property--addressbooks property--last" />
+ class="property--addressbooks property--last property--without-actions" />
<!-- Groups always visible -->
<PropertyGroups :prop-model="groupsModel" :value.sync="groups" :contact="contact"
diff --git a/src/components/ContactDetails/ContactDetailsAddNewProp.vue b/src/components/ContactDetails/ContactDetailsAddNewProp.vue
index 8cbe9841..1400d624 100644
--- a/src/components/ContactDetails/ContactDetailsAddNewProp.vue
+++ b/src/components/ContactDetails/ContactDetailsAddNewProp.vue
@@ -21,7 +21,7 @@
-->
<template>
- <div class="grid-span-3 property property--last">
+ <div class="grid-span-3 property property--without-actions property--last">
<!-- title -->
<PropertyTitle :icon="'icon-add'" :readable-name="t('contacts', 'Add new property')" />
@@ -57,11 +57,11 @@ export default {
computed: {
/**
- * Rfc props scoped
+ * Rfc props
* @returns {Object}
*/
properties() {
- return rfcProps.properties(this)
+ return rfcProps.properties
},
/**
diff --git a/src/components/ContactDetails/ContactDetailsAvatar.vue b/src/components/ContactDetails/ContactDetailsAvatar.vue
index 2f0f587a..fbfea250 100644
--- a/src/components/ContactDetails/ContactDetailsAvatar.vue
+++ b/src/components/ContactDetails/ContactDetailsAvatar.vue
@@ -82,7 +82,7 @@ import { generateRemoteUrl } from 'nextcloud-router'
const axios = () => import('axios')
export default {
- name: 'ContactAvatar',
+ name: 'ContactDetailsAvatar',
components: {
ActionLink,
diff --git a/src/components/ContactDetails/ContactDetailsProperty.vue b/src/components/ContactDetails/ContactDetailsProperty.vue
index 0a4aaf38..ac2452f5 100644
--- a/src/components/ContactDetails/ContactDetailsProperty.vue
+++ b/src/components/ContactDetails/ContactDetailsProperty.vue
@@ -22,11 +22,11 @@
<template>
<!-- If not in the rfcProps then we don't want to display it -->
- <component :is="componentInstance" v-if="propModel && propType !== 'unknown'" :select-type.sync="selectType"
- :prop-model="propModel" :value.sync="value" :is-first-property="isFirstProperty"
- :property="property" :is-last-property="isLastProperty" :class="{'property--last': isLastProperty}"
- :local-contact="localContact" :prop-name="propName" :prop-type="propType"
- :options="sortedModelOptions" :is-read-only="isReadOnly"
+ <component :is="componentInstance" v-if="propModel && propType !== 'unknown'" ref="component"
+ :select-type.sync="selectType" :prop-model="propModel" :value.sync="value"
+ :is-first-property="isFirstProperty" :property="property" :is-last-property="isLastProperty"
+ :class="{'property--last': isLastProperty}" :local-contact="localContact" :prop-name="propName"
+ :prop-type="propType" :options="sortedModelOptions" :is-read-only="isReadOnly"
@delete="deleteProp" @update="updateContact" />
</template>
@@ -93,10 +93,8 @@ export default {
},
// rfc properties list
- // passing this to properties to allow us to scope the properties object
- // this make possible defining actions there
properties() {
- return rfcProps.properties(this)
+ return rfcProps.properties
},
fieldOrder() {
return rfcProps.fieldOrder
diff --git a/src/components/Properties/PropertyActions.vue b/src/components/Properties/PropertyActions.vue
index 8a611d37..bbf908ed 100644
--- a/src/components/Properties/PropertyActions.vue
+++ b/src/components/Properties/PropertyActions.vue
@@ -25,10 +25,8 @@
<ActionButton icon="icon-delete" @click="deleteProperty">
{{ t('contacts', 'Delete') }}
</ActionButton>
- <ActionButton v-for="(action, index) in actions" :key="index"
- :icon="action.icon" @click="action.action">
- {{ action.text }}
- </ActionButton>
+ <actions :is="action" v-for="(action, index) in actions" :key="index"
+ :component="propertyComponent" />
</Actions>
</template>
@@ -46,6 +44,10 @@ export default {
actions: {
type: Array,
default: () => []
+ },
+ propertyComponent: {
+ type: Object,
+ required: true
}
},
diff --git a/src/components/Properties/PropertyDateTime.vue b/src/components/Properties/PropertyDateTime.vue
index 71561c42..83b1a314 100644
--- a/src/components/Properties/PropertyDateTime.vue
+++ b/src/components/Properties/PropertyDateTime.vue
@@ -43,14 +43,14 @@
{{ propModel.readableName }}
</div>
- <!-- props actions -->
- <PropertyActions :actions="actions" @delete="deleteProperty" />
-
<!-- Real input where the picker shows -->
<DatetimePicker :value="vcardTimeLocalValue.toJSDate()" :minute-step="10" :lang="lang"
:clearable="false" :first-day-of-week="firstDay" :type="inputType"
:readonly="isReadOnly" :format="dateFormat" class="property__value"
- confirm @confirm="updateValue" />
+ confirm @confirm="debounceUpdateValue" />
+
+ <!-- props actions -->
+ <PropertyActions :actions="actions" :property-component="this" @delete="deleteProperty" />
</div>
</div>
</template>
@@ -159,42 +159,67 @@ export default {
/**
* Debounce and send update event to parent
*/
- updateValue: debounce(function(e) {
+ debounceUpdateValue: debounce(function(date) {
const objMap = ['year', 'month', 'day', 'hour', 'minute', 'second']
- let rawArray = moment(e).toArray()
+ const rawArray = moment(date).toArray()
- const rawObject = rawArray.reduce((acc, cur, index) => {
+ let dateObject = rawArray.reduce((acc, cur, index) => {
acc[objMap[index]] = cur
return acc
}, {})
/**
+ * VCardTime starts months at 1
+ * but moment and js starts at 0
+ * ! since we use moment to generate our time array
+ * ! we need to make sure the conversion to VCardTime is done well
+ */
+ dateObject.month++
+
+ this.updateValue(dateObject)
+ }, 500),
+
+ updateValue(dateObject, forceYear) {
+ const ignoreYear = this.property.getParameter('x-apple-omit-year')
+
+ /**
+ * If forceYear, we add back the year!
+ * taken from x-apple-omit-year parameter
+ * of from the current year if we don't have
+ * any other appropriate year data
+ */
+ if (forceYear) {
+ this.property.removeParameter('x-apple-omit-year')
+ dateObject.year = parseInt(ignoreYear) ? ignoreYear : moment().year()
+ } else
+
+ /**
* Use the current year to ensure we do not lose
* the year data on v4.0 since we currently have
* no options to remove the year selection.
* ! using this.value since this.localValue reflect the current change
* ! so we need to make sure we do not use the updated data
- * TODO: add option to omit year and not use already existing data
+ * If we force the removal of the year (vcard 4.0 only)
+ * year is still valid on the apple format x-apple-omit-year
*/
- if (this.value.year === null) {
- rawObject.year = null
+ if (!this.value.year) {
+ dateObject.year = null
+ } else
+
+ // Apple style omit year parameter
+ // if year changed and we were already
+ // ignoring the year, we update the parameter
+ if (ignoreYear && dateObject.year) {
+ this.property.setParameter('x-apple-omit-year', parseInt(dateObject.year).toString())
}
- /**
- * VCardTime starts months at 1
- * but moment and js starts at 0
- * ! since we use moment to generate our time array
- * ! we need to make sure the conversion to VCardTime is done well
- */
- rawObject.month++
-
// reset the VCardTime component to the selected date/time
- this.localValue = new VCardTime(rawObject, null, this.propType)
+ this.localValue = new VCardTime(dateObject, null, this.propType)
// 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),
+ },
/**
* Format time with locale to display only
@@ -211,6 +236,11 @@ export default {
let datetimeData = this.vcardTimeLocalValue.toJSON()
let datetime = ''
+ const ignoreYear = this.property.getParameter('x-apple-omit-year')
+ if (ignoreYear) {
+ datetimeData.year = null
+ }
+
// FUN FACT: JS date starts month at zero!
datetimeData.month--
diff --git a/src/components/Properties/PropertyGroups.vue b/src/components/Properties/PropertyGroups.vue
index 4cdf296b..c6e6c419 100644
--- a/src/components/Properties/PropertyGroups.vue
+++ b/src/components/Properties/PropertyGroups.vue
@@ -21,7 +21,7 @@
-->
<template>
- <div v-if="propModel" class="grid-span-2 property">
+ <div v-if="propModel" class="grid-span-2 property property--without-actions">
<!-- NO title if first element for groups -->
<div class="property__row">
diff --git a/src/components/Properties/PropertyMultipleText.vue b/src/components/Properties/PropertyMultipleText.vue
index d14e83e4..84753871 100644
--- a/src/components/Properties/PropertyMultipleText.vue
+++ b/src/components/Properties/PropertyMultipleText.vue
@@ -45,12 +45,14 @@
{{ isFirstProperty ? '' : propModel.readableName }}
</div>
- <!-- show the first input if not -->
+ <!-- show the first input if not a structured value -->
<input v-if="!property.isStructuredValue" v-model.trim="localValue[0]" :readonly="isReadOnly"
class="property__value" type="text" @input="updateValue">
<!-- props actions -->
- <PropertyActions :actions="actions" @delete="deleteProperty" />
+ <PropertyActions class="property__actions--floating"
+ :actions="actions" :property-component="this"
+ @delete="deleteProperty" />
</div>
<!-- force order based on model -->
@@ -82,7 +84,7 @@ import PropertyTitle from './PropertyTitle'
import PropertyActions from './PropertyActions'
export default {
- name: 'PropertyText',
+ name: 'PropertyMultipleText',
components: {
PropertyTitle,
diff --git a/src/components/Properties/PropertySelect.vue b/src/components/Properties/PropertySelect.vue
index 559aebc8..4b5893d3 100644
--- a/src/components/Properties/PropertySelect.vue
+++ b/src/components/Properties/PropertySelect.vue
@@ -37,12 +37,12 @@
{{ propModel.readableName }}
</div>
- <!-- props actions -->
- <PropertyActions :actions="actions" @delete="deleteProperty" />
-
<multiselect v-model="matchedOptions" :options="propModel.options" :placeholder="t('contacts', 'Select option')"
:disabled="isSingleOption || isReadOnly" class="property__value" track-by="id"
label="name" @input="updateValue" />
+
+ <!-- props actions -->
+ <PropertyActions :actions="actions" :property-component="this" @delete="deleteProperty" />
</div>
</div>
</template>
diff --git a/src/components/Properties/PropertyText.vue b/src/components/Properties/PropertyText.vue
index 8ccaf969..05ec9861 100644
--- a/src/components/Properties/PropertyText.vue
+++ b/src/components/Properties/PropertyText.vue
@@ -60,7 +60,7 @@
target="_blank" />
<!-- props actions -->
- <PropertyActions :actions="actions" @delete="deleteProperty" />
+ <PropertyActions :actions="actions" :property-component="this" @delete="deleteProperty" />
</div>
</div>
</template>
diff --git a/src/components/Settings/SettingsAddressbookShare.vue b/src/components/Settings/SettingsAddressbookShare.vue
index a54e8d1f..eb3c29ca 100644
--- a/src/components/Settings/SettingsAddressbookShare.vue
+++ b/src/components/Settings/SettingsAddressbookShare.vue
@@ -52,7 +52,7 @@ import addressBookSharee from './SettingsAddressbookSharee'
import debounce from 'debounce'
export default {
- name: 'SettingsShareAddressbook',
+ name: 'SettingsAddressbookShare',
components: {
addressBookSharee
},
diff --git a/src/components/Settings/SettingsAddressbookSharee.vue b/src/components/Settings/SettingsAddressbookSharee.vue
index 93a3ee71..d7bea024 100644
--- a/src/components/Settings/SettingsAddressbookSharee.vue
+++ b/src/components/Settings/SettingsAddressbookSharee.vue
@@ -53,7 +53,7 @@
<script>
export default {
- name: 'SettingsShareSharee',
+ name: 'SettingsAddressbookSharee',
props: {
addressbook: {
diff --git a/src/main.js b/src/main.js
index 021ef966..9f5addf8 100644
--- a/