summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>2019-05-13 15:03:08 +0200
committerJohn Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>2019-05-13 15:03:08 +0200
commitf7842265d839f355f6df463c7497aec78f8453e6 (patch)
tree173cb3d58f9e4b68ebaabb59225665e8d5ac8a43
parent8463ce63c2d5651a4a4c979de1a333a8bee148bf (diff)
Update template and css
Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
-rw-r--r--css/ContactDetails.scss13
-rw-r--r--css/ContactDetailsAvatar.scss11
-rw-r--r--css/Properties/Properties.scss16
-rw-r--r--src/components/ContactDetails.vue72
-rw-r--r--src/components/ContactDetails/ContactDetailsAvatar.vue73
-rw-r--r--src/components/Properties/PropertyActions.vue58
-rw-r--r--src/components/Properties/PropertyDateTime.vue6
-rw-r--r--src/components/Properties/PropertyMultipleText.vue6
-rw-r--r--src/components/Properties/PropertySelect.vue6
-rw-r--r--src/components/Properties/PropertyText.vue6
-rw-r--r--src/main.js4
-rw-r--r--src/mixins/PropertyMixin.js7
12 files changed, 158 insertions, 120 deletions
diff --git a/css/ContactDetails.scss b/css/ContactDetails.scss
index 2be765e8..6c4e53f7 100644
--- a/css/ContactDetails.scss
+++ b/css/ContactDetails.scss
@@ -68,14 +68,11 @@
#contact-header-actions {
position: relative;
display: flex;
- .menu-icon {
- position: relative;
- height: 44px;
- width: 44px;
- // ! override default server class
- > .icon-more-white {
- // using #fffffe to trick the accessibility dark theme icon invert
- @include icon-color('more', 'actions', '#fffffe', 1, true);
+ .header-menu {
+ margin-right: 10px;
+ .action-item__menutoggle {
+ // force white over header colour
+ color: white;
}
}
.header-icon {
diff --git a/css/ContactDetailsAvatar.scss b/css/ContactDetailsAvatar.scss
index 3f1dd3bf..f86c1709 100644
--- a/css/ContactDetailsAvatar.scss
+++ b/css/ContactDetailsAvatar.scss
@@ -74,8 +74,15 @@
opacity: .7;
}
&__popovermenu {
- top: calc(50% + 24px);
- right: calc(50% - 22px);
+ // center
+ margin: calc((100% - 44px) / 2);
+ & /deep/ .action-item__menutoggle {
+ // hide three dot menu, in favour of icon-picture-force-white
+ z-index: -1;
+ &::before {
+ opacity: 0;
+ }
+ }
}
}
// if picture is set, hide the menu
diff --git a/css/Properties/Properties.scss b/css/Properties/Properties.scss
index 8aa23f78..9ba2f0d3 100644
--- a/css/Properties/Properties.scss
+++ b/css/Properties/Properties.scss
@@ -179,24 +179,10 @@ $property-value-max-width: 250px;
position: absolute !important;
top: 0;
left: 100%;
- margin: 0;
- margin-top: -3px; // align with line because of the 44x44px size
+ margin: -2px 2px; // align with line because of the 44x44px size
border: 0;
background-color: transparent;
z-index: 10;
- // opacity applies on the single action OR
- &:not(.action-item--multiple),
- &.action-item--multiple .icon-more {
- opacity: 0.5;
- }
- &:hover,
- &:active,
- &:focus {
- &:not(.action-item--multiple),
- &.action-item--multiple .icon-more {
- opacity: 0.7;
- }
- }
}
.property__value {
margin-right: 0;
diff --git a/src/components/ContactDetails.vue b/src/components/ContactDetails.vue
index da538483..5ec86643 100644
--- a/src/components/ContactDetails.vue
+++ b/src/components/ContactDetails.vue
@@ -66,6 +66,7 @@
<!-- actions -->
<div id="contact-header-actions">
+ <!-- warning message -->
<a v-if="loadingUpdate || warning"
v-tooltip.bottom="{
content: warning ? warning.msg : '',
@@ -73,25 +74,35 @@
}"
:class="{'icon-loading-small': loadingUpdate,
[`${warning.icon}`]: warning}" class="header-icon" href="#" />
+
+ <!-- conflict message -->
<div v-if="conflict" v-tooltip="{
content: conflict,
show: true,
trigger: 'manual',
}" class="header-icon header-icon--pulse icon-history-force-white"
@click="refreshContact" />
- <div class="menu-icon">
- <div v-click-outside="closeMenu" class="header-icon icon-more-white" @click="toggleMenu" />
- <div :class="{ 'open': openedMenu }" class="popovermenu">
- <popover-menu :menu="contactActions" />
- </div>
- </div>
+
+ <!-- menu actions -->
+ <Actions class="header-menu" menu-align="right">
+ <ActionLink :href="contact.url" :download="`${contact.displayName}.vcf`"
+ icon="icon-download">
+ {{ t('contacts', 'Download') }}
+ </ActionLink>
+ <ActionButton icon="icon-qrcode" @click="showQRcode">
+ {{ t('contacts', 'Generate QR Code') }}
+ </ActionButton>
+ <ActionButton v-if="!isReadOnly" icon="icon-delete" @click="deleteContact">
+ {{ t('contacts', 'Delete') }}
+ </ActionButton>
+ </Actions>
</div>
<!-- qrcode -->
- <modal v-if="qrcode" id="qrcode-modal" :title="contact.displayName"
+ <Modal v-if="qrcode" id="qrcode-modal" :title="contact.displayName"
@close="closeQrModal">
<img :src="`data:image/svg+xml;base64,${qrcode}`" class="qrcode" width="400">
- </modal>
+ </Modal>
</header>
<!-- contact details loading -->
@@ -138,16 +149,17 @@ import debounce from 'debounce'
import PQueue from 'p-queue'
import qr from 'qr-image'
import { stringify } from 'ical.js'
+import { ActionLink, ActionButton } from 'nextcloud-vue'
import rfcProps from 'Models/rfcProps'
import validate from 'Services/validate'
-import ContactProperty from './ContactDetails/ContactDetailsProperty'
import AddNewProp from './ContactDetails/ContactDetailsAddNewProp'
-import PropertySelect from './Properties/PropertySelect'
+import ContactAvatar from './ContactDetails/ContactDetailsAvatar'
+import ContactProperty from './ContactDetails/ContactDetailsProperty'
import PropertyGroups from './Properties/PropertyGroups'
import PropertyRev from './Properties/PropertyRev'
-import ContactAvatar from './ContactDetails/ContactDetailsAvatar'
+import PropertySelect from './Properties/PropertySelect'
const updateQueue = new PQueue({ concurrency: 1 })
@@ -155,12 +167,14 @@ export default {
name: 'ContactDetails',
components: {
+ ActionButton,
+ ActionLink,
+ AddNewProp,
+ ContactAvatar,
ContactProperty,
- PropertySelect,
PropertyGroups,
PropertyRev,
- AddNewProp,
- ContactAvatar
+ PropertySelect
},
props: {
@@ -247,36 +261,6 @@ export default {
},
/**
- * Header actions for the contact
- *
- * @returns {Array}
- */
- contactActions() {
- let actions = [
- {
- icon: 'icon-download',
- text: t('contacts', 'Download'),
- href: this.contact.url,
- download: `${this.contact.displayName}.vcf`
- },
- {
- icon: 'icon-qrcode',
- text: t('contacts', 'Generate QR Code'),
- action: this.showQRcode
- }
- ]
- if (!this.contact.addressbook.readOnly) {
- actions.push({
- icon: 'icon-delete',
- text: t('contacts', 'Delete'),
- action: this.deleteContact
- })
- }
-
- return actions
- },
-
- /**
* Contact properties copied and sorted by rfcProps.fieldOrder
*
* @returns {Array}
diff --git a/src/components/ContactDetails/ContactDetailsAvatar.vue b/src/components/ContactDetails/ContactDetailsAvatar.vue
index 4ee26ac0..fb54cd89 100644
--- a/src/components/ContactDetails/ContactDetailsAvatar.vue
+++ b/src/components/ContactDetails/ContactDetailsAvatar.vue
@@ -32,28 +32,49 @@
<div v-click-outside="closeMenu" class="contact-header-avatar__options">
<a v-tooltip.bottom="t('contacts', 'Add a new picture')" href="#" class="contact-avatar-options"
:class="loading ? 'icon-loading-small' : 'icon-picture-force-white'"
- @click.prevent="toggleMenu" />
+ @click.stop.prevent="toggleMenu" />
<input id="contact-avatar-upload" ref="uploadInput" type="file"
class="hidden" accept="image/*" @change="processFile">
</div>
- <modal v-if="maximizeAvatar" ref="modal" class="contact-header-modal"
- :actions="modalActions" size="large" :title="contact.displayName"
+ <Modal v-if="maximizeAvatar"
+ ref="modal" class="contact-header-modal"
+ size="large" :title="contact.displayName"
@close="toggleModal">
+ <template #actions>
+ <ActionButton v-if="!isReadOnly" icon="icon-upload" @click="selectFileInput">
+ {{ t('contacts', 'Upload a new picture') }}
+ </ActionButton>
+ <ActionButton v-if="!isReadOnly" icon="icon-picture" @click="selectFilePicker">
+ {{ t('contacts', 'Choose from files') }}
+ </ActionButton>
+ <ActionButton v-if="!isReadOnly" icon="icon-delete" @click="removePhoto">
+ {{ t('contacts', 'Delete picture') }}
+ </ActionButton>
+ <ActionLink :href="`${contact.url}?photo`" icon="icon-download" target="_blank">
+ {{ t('contacts', 'Download picture') }}
+ </ActionLink>
+ </template>
<img ref="img" :src="photo" class="contact-header-modal__photo"
:style="{ width, height }" @load="updateImgSize">
- </modal>
+ </Modal>
<!-- out of the avatar__options because of the overflow hidden -->
- <div :class="{ 'open': opened }" class="contact-avatar-options__popovermenu popovermenu">
- <popover-menu :menu="actions" />
- </div>
+ <Actions :open="opened" class="contact-avatar-options__popovermenu">
+ <ActionButton v-if="!isReadOnly" icon="icon-upload" @click="selectFileInput">
+ {{ t('contacts', 'Upload a new picture') }}
+ </ActionButton>
+ <ActionButton v-if="!isReadOnly" icon="icon-picture" @click="selectFilePicker">
+ {{ t('contacts', 'Choose from files') }}
+ </ActionButton>
+ </Actions>
</div>
</div>
</template>
<script>
import debounce from 'debounce'
+import { ActionLink, ActionButton } from 'nextcloud-vue'
import { pickFileOrDirectory } from 'nextcloud-server/dist/files'
import { generateRemoteUrl } from 'nextcloud-server/dist/router'
@@ -63,6 +84,11 @@ const axios = () => import('axios')
export default {
name: 'ContactAvatar',
+ components: {
+ ActionLink,
+ ActionButton
+ },
+
props: {
contact: {
type: Object,
@@ -90,34 +116,11 @@ export default {
}
return this.contact.photo
},
- actions() {
- return [
- {
- icon: 'icon-upload',
- text: t('contacts', 'Upload a new picture'),
- action: this.selectFileInput
- },
- {
- icon: 'icon-picture',
- text: t('contacts', 'Choose from files'),
- action: this.selectFilePicker
- }
- ]
- },
- modalActions() {
- return [...this.actions, ...[
- {
- icon: 'icon-delete',
- text: t('contacts', 'Delete picture'),
- action: this.removePhoto
- },
- {
- icon: 'icon-download',
- text: t('contacts', 'Download picture'),
- href: this.contact.url + '?photo',
- target: '_blank'
- }
- ]]
+ isReadOnly() {
+ if (this.contact.addressbook) {
+ return this.contact.addressbook.readOnly
+ }
+ return false
}
},
mounted() {
diff --git a/src/components/Properties/PropertyActions.vue b/src/components/Properties/PropertyActions.vue
new file mode 100644
index 00000000..8a611d37
--- /dev/null
+++ b/src/components/Properties/PropertyActions.vue
@@ -0,0 +1,58 @@
+<!--
+ - @copyright Copyright (c) 2019 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>
+ <Actions class="property__actions">
+ <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>
+</template>
+
+<script>
+import { ActionButton } from 'nextcloud-vue'
+
+export default {
+ name: 'PropertyActions',
+
+ components: {
+ ActionButton
+ },
+
+ props: {
+ actions: {
+ type: Array,
+ default: () => []
+ }
+ },
+
+ methods: {
+ deleteProperty() {
+ this.$emit('delete')
+ }
+ }
+}
+</script>
diff --git a/src/components/Properties/PropertyDateTime.vue b/src/components/Properties/PropertyDateTime.vue
index 7f003aa9..4700615f 100644
--- a/src/components/Properties/PropertyDateTime.vue
+++ b/src/components/Properties/PropertyDateTime.vue
@@ -44,7 +44,7 @@
</div>
<!-- props actions -->
- <action :actions="actions" class="property__actions" />
+ <PropertyActions :actions="actions" @delete="deleteProperty" />
<!-- Real input where the picker shows -->
<DatetimePicker :value="vcardTimeLocalValue.toJSDate()" :minute-step="10" :lang="lang"
@@ -63,13 +63,15 @@ import { VCardTime } from 'ical.js'
import PropertyMixin from 'Mixins/PropertyMixin'
import PropertyTitle from './PropertyTitle'
+import PropertyActions from './PropertyActions'
export default {
name: 'PropertyDateTime',
components: {
DatetimePicker,
- PropertyTitle
+ PropertyTitle,
+ PropertyActions
},
mixins: [PropertyMixin],
diff --git a/src/components/Properties/PropertyMultipleText.vue b/src/components/Properties/PropertyMultipleText.vue
index 48d65bf8..d14e83e4 100644
--- a/src/components/Properties/PropertyMultipleText.vue
+++ b/src/components/Properties/PropertyMultipleText.vue
@@ -50,7 +50,7 @@
class="property__value" type="text" @input="updateValue">
<!-- props actions -->
- <action :actions="actions" class="property__actions" />
+ <PropertyActions :actions="actions" @delete="deleteProperty" />
</div>
<!-- force order based on model -->
@@ -79,12 +79,14 @@
<script>
import PropertyMixin from 'Mixins/PropertyMixin'
import PropertyTitle from './PropertyTitle'
+import PropertyActions from './PropertyActions'
export default {
name: 'PropertyText',
components: {
- PropertyTitle
+ PropertyTitle,
+ PropertyActions
},
mixins: [PropertyMixin],
diff --git a/src/components/Properties/PropertySelect.vue b/src/components/Properties/PropertySelect.vue
index 47be44ec..559aebc8 100644
--- a/src/components/Properties/PropertySelect.vue
+++ b/src/components/Properties/PropertySelect.vue
@@ -38,7 +38,7 @@
</div>
<!-- props actions -->
- <action :actions="actions" class="property__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"
@@ -50,12 +50,14 @@
<script>
import PropertyMixin from 'Mixins/PropertyMixin'
import PropertyTitle from './PropertyTitle'
+import PropertyActions from './PropertyActions'
export default {
name: 'PropertySelect',
components: {
- PropertyTitle
+ PropertyTitle,
+ PropertyActions
},
mixins: [PropertyMixin],
diff --git a/src/components/Properties/PropertyText.vue b/src/components/Properties/PropertyText.vue
index 48745571..8ccaf969 100644
--- a/src/components/Properties/PropertyText.vue
+++ b/src/components/Properties/PropertyText.vue
@@ -60,7 +60,7 @@
target="_blank" />
<!-- props actions -->
- <action :actions="actions" class="property__actions" />
+ <PropertyActions :actions="actions" @delete="deleteProperty" />
</div>
</div>
</template>
@@ -69,12 +69,14 @@
import debounce from 'debounce'
import PropertyMixin from 'Mixins/PropertyMixin'
import PropertyTitle from './PropertyTitle'
+import PropertyActions from './PropertyActions'
export default {
name: 'PropertyText',
components: {
- PropertyTitle
+ PropertyTitle,
+ PropertyActions
},
mixins: [PropertyMixin],
diff --git a/src/main.js b/src/main.js
index d43ff739..c6d0f8b7 100644
--- a/src/main.js
+++ b/src/main.js
@@ -27,7 +27,7 @@ import { sync } from 'vuex-router-sync'
import { generateFilePath } from 'nextcloud-server/dist/router'
/** GLOBAL COMPONENTS AND DIRECTIVE */
-import { Action, DatetimePicker, Multiselect, PopoverMenu, Modal } from 'nextcloud-vue'
+import { Actions, DatetimePicker, Multiselect, PopoverMenu, Modal } from 'nextcloud-vue'
import ClickOutside from 'vue-click-outside'
import VTooltip from 'nextcloud-vue/dist/Directives/Tooltip'
import VueClipboard from 'vue-clipboard2'
@@ -44,7 +44,7 @@ __webpack_nonce__ = btoa(OC.requestToken)
__webpack_public_path__ = generateFilePath('contacts', '', 'js/')
// Register global components
-Vue.component('Action', Action)
+Vue.component('Actions', Actions)
Vue.component('DatetimePicker', DatetimePicker)
Vue.component('Modal', Modal)
Vue.component('Multiselect', Multiselect)
diff --git a/src/mixins/PropertyMixin.js b/src/mixins/PropertyMixin.js
index 170d461a..9fa3ed0a 100644
--- a/src/mixins/PropertyMixin.js
+++ b/src/mixins/PropertyMixin.js
@@ -88,12 +88,7 @@ export default {
computed: {
actions() {
- const del = {
- text: t('contacts', 'Delete'),
- icon: 'icon-delete',
- action: this.deleteProperty
- }
- return [...this.propModel.actions ? this.propModel.actions : [], del]
+ return this.propModel.actions ? this.propModel.actions : []
}
},