Merge pull request #22143 from nextcloud/design/dashboard-design
Dashboard design enhancements
This commit is contained in:
commit
50fdd45e9b
|
@ -0,0 +1,5 @@
|
||||||
|
# Dashboard
|
||||||
|
|
||||||
|
Picture credit:
|
||||||
|
- [Clouds by Kamil Porembiński](https://www.flickr.com/photos/paszczak000/8715851521/)
|
||||||
|
- [Un beau soir dété by Tanguy Domenge](https://www.flickr.com/photos/148302424@N05/36591009215/)
|
Binary file not shown.
After Width: | Height: | Size: 627 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.1 MiB |
Binary file not shown.
After Width: | Height: | Size: 121 KiB |
Binary file not shown.
After Width: | Height: | Size: 165 KiB |
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="app-dashboard">
|
<div id="app-dashboard" :style="{ backgroundImage: `url(${backgroundImage})` }">
|
||||||
<h2>{{ greeting.icon }} {{ greeting.text }}</h2>
|
<h2>{{ greeting.text }}</h2>
|
||||||
<div class="statuses">
|
<div class="statuses">
|
||||||
<div v-for="status in registeredStatus"
|
<div v-for="status in registeredStatus"
|
||||||
:id="'status-' + status"
|
:id="'status-' + status"
|
||||||
|
@ -27,7 +27,7 @@
|
||||||
<a v-tooltip="tooltip"
|
<a v-tooltip="tooltip"
|
||||||
class="edit-panels icon-add"
|
class="edit-panels icon-add"
|
||||||
:class="{ firstrun: firstRun }"
|
:class="{ firstrun: firstRun }"
|
||||||
@click="showModal">{{ t('dashboard', 'Edit widgets') }}</a>
|
@click="showModal">{{ t('dashboard', 'Customize') }}</a>
|
||||||
|
|
||||||
<Modal v-if="modal" @close="closeModal">
|
<Modal v-if="modal" @close="closeModal">
|
||||||
<div class="modal__content">
|
<div class="modal__content">
|
||||||
|
@ -50,6 +50,9 @@
|
||||||
</Draggable>
|
</Draggable>
|
||||||
|
|
||||||
<a :href="appStoreUrl" class="button">{{ t('dashboard', 'Get more widgets from the app store') }}</a>
|
<a :href="appStoreUrl" class="button">{{ t('dashboard', 'Get more widgets from the app store') }}</a>
|
||||||
|
|
||||||
|
<h3>{{ t('dashboard', 'Credits') }}</h3>
|
||||||
|
<p>{{ t('dashboard', 'Photos') }}: <a href="https://www.flickr.com/photos/paszczak000/8715851521/" target="_blank" rel="noopener">Clouds (Kamil Porembiński)</a>, <a href="https://www.flickr.com/photos/148302424@N05/36591009215/" target="_blank" rel="noopener">Un beau soir dété (Tanguy Domenge)</a>.</p>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
</div>
|
</div>
|
||||||
|
@ -62,7 +65,8 @@ import { getCurrentUser } from '@nextcloud/auth'
|
||||||
import { Modal } from '@nextcloud/vue'
|
import { Modal } from '@nextcloud/vue'
|
||||||
import Draggable from 'vuedraggable'
|
import Draggable from 'vuedraggable'
|
||||||
import axios from '@nextcloud/axios'
|
import axios from '@nextcloud/axios'
|
||||||
import { generateUrl } from '@nextcloud/router'
|
import { generateUrl, generateFilePath } from '@nextcloud/router'
|
||||||
|
import isMobile from './mixins/isMobile'
|
||||||
|
|
||||||
const panels = loadState('dashboard', 'panels')
|
const panels = loadState('dashboard', 'panels')
|
||||||
const firstRun = loadState('dashboard', 'firstRun')
|
const firstRun = loadState('dashboard', 'firstRun')
|
||||||
|
@ -73,6 +77,9 @@ export default {
|
||||||
Modal,
|
Modal,
|
||||||
Draggable,
|
Draggable,
|
||||||
},
|
},
|
||||||
|
mixins: [
|
||||||
|
isMobile,
|
||||||
|
],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
timer: new Date(),
|
timer: new Date(),
|
||||||
|
@ -90,6 +97,13 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
backgroundImage() {
|
||||||
|
const prefixWithBaseUrl = (url) => generateFilePath('dashboard', '', 'img/') + url
|
||||||
|
if (window.OCA.Accessibility.theme === 'dark') {
|
||||||
|
return !isMobile ? prefixWithBaseUrl('flickr-148302424@N05-36591009215.jpg?v=1') : prefixWithBaseUrl('flickr-148302424@N05-36591009215-mobile.jpg?v=1')
|
||||||
|
}
|
||||||
|
return !isMobile ? prefixWithBaseUrl('flickr-paszczak000-8715851521.jpg?v=1') : prefixWithBaseUrl('flickr-paszczak000-8715851521-mobile.jpg?v=1')
|
||||||
|
},
|
||||||
tooltip() {
|
tooltip() {
|
||||||
if (!this.firstRun) {
|
if (!this.firstRun) {
|
||||||
return null
|
return null
|
||||||
|
@ -106,18 +120,15 @@ export default {
|
||||||
const shouldShowName = this.displayName && this.uid !== this.displayName
|
const shouldShowName = this.displayName && this.uid !== this.displayName
|
||||||
|
|
||||||
if (time > 18) {
|
if (time > 18) {
|
||||||
return { icon: '🌙', text: shouldShowName ? t('dashboard', 'Good evening, {name}', { name: this.displayName }) : t('dashboard', 'Good evening') }
|
return { text: shouldShowName ? t('dashboard', 'Good evening, {name}', { name: this.displayName }) : t('dashboard', 'Good evening') }
|
||||||
}
|
}
|
||||||
if (time > 12) {
|
if (time > 12) {
|
||||||
return { icon: '☀', text: shouldShowName ? t('dashboard', 'Good afternoon, {name}', { name: this.displayName }) : t('dashboard', 'Good afternoon') }
|
return { text: shouldShowName ? t('dashboard', 'Good afternoon, {name}', { name: this.displayName }) : t('dashboard', 'Good afternoon') }
|
||||||
}
|
|
||||||
if (time === 12) {
|
|
||||||
return { icon: '🍽', text: shouldShowName ? t('dashboard', 'Time for lunch, {name}', { name: this.displayName }) : t('dashboard', 'Time for lunch') }
|
|
||||||
}
|
}
|
||||||
if (time > 5) {
|
if (time > 5) {
|
||||||
return { icon: '🌄', text: shouldShowName ? t('dashboard', 'Good morning, {name}', { name: this.displayName }) : t('dashboard', 'Good morning') }
|
return { text: shouldShowName ? t('dashboard', 'Good morning, {name}', { name: this.displayName }) : t('dashboard', 'Good morning') }
|
||||||
}
|
}
|
||||||
return { icon: '🦉', text: shouldShowName ? t('dashboard', 'Have a night owl, {name}', { name: this.displayName }) : t('dashboard', 'Have a night owl') }
|
return { text: shouldShowName ? t('dashboard', 'Good night, {name}', { name: this.displayName }) : t('dashboard', 'Good night') }
|
||||||
},
|
},
|
||||||
isActive() {
|
isActive() {
|
||||||
return (panel) => this.layout.indexOf(panel.id) > -1
|
return (panel) => this.layout.indexOf(panel.id) > -1
|
||||||
|
@ -228,17 +239,61 @@ export default {
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
// Show Dashboard background image beneath header
|
||||||
|
#body-user #header {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#content {
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide triangle indicators from navigation since they are out of place without the header bar
|
||||||
|
#appmenu li a.active::before,
|
||||||
|
#appmenu li:hover a::before,
|
||||||
|
#appmenu li:hover a.active::before,
|
||||||
|
#appmenu li a:focus::before {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
#app-dashboard {
|
#app-dashboard {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: 100px;
|
padding-bottom: 100px;
|
||||||
|
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-attachment: fixed;
|
||||||
|
|
||||||
|
#body-user:not(.dark) & {
|
||||||
|
background-color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
#body-user.dark & {
|
||||||
|
background-color: var(--color-main-background);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
|
color: var(--color-primary-text);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 32px;
|
font-size: 32px;
|
||||||
line-height: 130%;
|
line-height: 130%;
|
||||||
padding: 80px 16px 0px;
|
padding: 120px 16px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statuses {
|
||||||
|
::v-deep #user-status-menu-item__subheader>button {
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
background-color: rgba(255, 255, 255, 0.8);
|
||||||
|
|
||||||
|
#body-user.dark & {
|
||||||
|
background-color: rgba(24, 24, 24, 0.8);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.panels {
|
.panels {
|
||||||
|
@ -256,9 +311,13 @@ export default {
|
||||||
width: 320px;
|
width: 320px;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
margin: 16px;
|
margin: 16px;
|
||||||
background-color: var(--color-main-background-translucent);
|
background-color: rgba(255, 255, 255, 0.8);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
border-radius: var(--border-radius-large);
|
border-radius: var(--border-radius-large);
|
||||||
border: 2px solid var(--color-border);
|
|
||||||
|
#body-user.dark & {
|
||||||
|
background-color: rgba(24, 24, 24, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
&.sortable-ghost {
|
&.sortable-ghost {
|
||||||
opacity: 0.1;
|
opacity: 0.1;
|
||||||
|
@ -269,11 +328,6 @@ export default {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
top: 50px;
|
top: 50px;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
// TO DO: use variables here
|
|
||||||
background: linear-gradient(170deg, rgba(0, 130,201, 0.2) 0%, rgba(255,255,255,.1) 50%, rgba(255,255,255,0) 100%);
|
|
||||||
border-top-left-radius: calc(var(--border-radius-large) - 2px);
|
|
||||||
border-top-right-radius: calc(var(--border-radius-large) - 2px);
|
|
||||||
backdrop-filter: blur(4px);
|
|
||||||
cursor: grab;
|
cursor: grab;
|
||||||
|
|
||||||
&, ::v-deep * {
|
&, ::v-deep * {
|
||||||
|
@ -356,6 +410,14 @@ export default {
|
||||||
background-position: left center;
|
background-position: left center;
|
||||||
padding-left: 26px;
|
padding-left: 26px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
&:not(:first-of-type) {
|
||||||
|
margin-top: 32px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.flip-list-move {
|
.flip-list-move {
|
||||||
|
|
|
@ -2,6 +2,13 @@ import Vue from 'vue'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import { translate as t } from '@nextcloud/l10n'
|
import { translate as t } from '@nextcloud/l10n'
|
||||||
import VTooltip from '@nextcloud/vue/dist/Directives/Tooltip'
|
import VTooltip from '@nextcloud/vue/dist/Directives/Tooltip'
|
||||||
|
import { getRequestToken } from '@nextcloud/auth'
|
||||||
|
import { generateFilePath } from '@nextcloud/router'
|
||||||
|
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
__webpack_nonce__ = btoa(getRequestToken())
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
__webpack_public_path__ = generateFilePath('dashboard', '', 'js/')
|
||||||
|
|
||||||
Vue.directive('Tooltip', VTooltip)
|
Vue.directive('Tooltip', VTooltip)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* @copyright Copyright (c) 2020 Julius Härtl <jus@bitgrid.net>
|
||||||
|
*
|
||||||
|
* @author Julius Härtl <jus@bitgrid.net>
|
||||||
|
*
|
||||||
|
* @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/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isMobile: this._isMobile(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeMount() {
|
||||||
|
window.addEventListener('resize', this._onResize)
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
window.removeEventListener('resize', this._onResize)
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
_onResize() {
|
||||||
|
// Update mobile mode
|
||||||
|
this.isMobile = this._isMobile()
|
||||||
|
},
|
||||||
|
_isMobile() {
|
||||||
|
// check if content width is under 768px
|
||||||
|
return document.documentElement.clientWidth < 768
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
Loading…
Reference in New Issue