aboutsummaryrefslogtreecommitdiffstats
path: root/apps/theming/src/components/BackgroundSettings.vue
diff options
context:
space:
mode:
Diffstat (limited to 'apps/theming/src/components/BackgroundSettings.vue')
-rw-r--r--apps/theming/src/components/BackgroundSettings.vue208
1 files changed, 75 insertions, 133 deletions
diff --git a/apps/theming/src/components/BackgroundSettings.vue b/apps/theming/src/components/BackgroundSettings.vue
index 0a8c49be45e..58b76dd9602 100644
--- a/apps/theming/src/components/BackgroundSettings.vue
+++ b/apps/theming/src/components/BackgroundSettings.vue
@@ -1,27 +1,7 @@
<!--
- - @copyright Copyright (c) 2020 Julius Härtl <jus@bitgrid.net>
- -
- - @author Christopher Ng <chrng8@gmail.com>
- - @author Greta Doci <gretadoci@gmail.com>
- - @author John Molakvoæ <skjnldsv@protonmail.com>
- - @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/>.
- -
- -->
+ - SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
<template>
<div class="background-selector" data-user-theming-background-settings>
@@ -32,15 +12,34 @@
'background background__filepicker': true,
'background--active': backgroundImage === 'custom'
}"
- :data-color-bright="invertTextColor(Theming.color)"
data-user-theming-background-custom
tabindex="0"
@click="pickFile">
{{ t('theming', 'Custom background') }}
- <ImageEdit v-if="backgroundImage !== 'custom'" :size="26" />
+ <ImageEdit v-if="backgroundImage !== 'custom'" :size="20" />
<Check :size="44" />
</button>
+ <!-- Custom color picker -->
+ <NcColorPicker v-model="Theming.backgroundColor" @update:value="debouncePickColor">
+ <button :class="{
+ 'icon-loading': loading === 'color',
+ 'background background__color': true,
+ 'background--active': backgroundImage === 'color'
+ }"
+ :aria-pressed="backgroundImage === 'color'"
+ :data-color="Theming.backgroundColor"
+ :data-color-bright="invertTextColor(Theming.backgroundColor)"
+ :style="{ backgroundColor: Theming.backgroundColor, '--border-color': Theming.backgroundColor}"
+ data-user-theming-background-color
+ tabindex="0"
+ @click="backgroundImage !== 'color' && debouncePickColor(Theming.backgroundColor)">
+ {{ t('theming', 'Plain background') /* TRANSLATORS: Background using a single color */ }}
+ <ColorPalette v-if="backgroundImage !== 'color'" :size="20" />
+ <Check :size="44" />
+ </button>
+ </NcColorPicker>
+
<!-- Default background -->
<button :aria-pressed="backgroundImage === 'default'"
:class="{
@@ -48,8 +47,8 @@
'background background__default': true,
'background--active': backgroundImage === 'default'
}"
- :data-color-bright="invertTextColor(Theming.defaultColor)"
- :style="{ '--border-color': Theming.defaultColor }"
+ :data-color-bright="invertTextColor(Theming.defaultBackgroundColor)"
+ :style="{ '--border-color': Theming.defaultBackgroundColor }"
data-user-theming-background-default
tabindex="0"
@click="setDefault">
@@ -57,32 +56,6 @@
<Check :size="44" />
</button>
- <!-- Custom color picker -->
- <NcColorPicker v-model="Theming.color" @input="debouncePickColor">
- <button class="background background__color"
- :data-color="Theming.color"
- :data-color-bright="invertTextColor(Theming.color)"
- :style="{ backgroundColor: Theming.color, '--border-color': Theming.color}"
- data-user-theming-background-color
- tabindex="0">
- {{ t('theming', 'Change color') }}
- </button>
- </NcColorPicker>
-
- <!-- Remove background -->
- <button :aria-pressed="isBackgroundDisabled"
- :class="{
- 'background background__delete': true,
- 'background--active': isBackgroundDisabled
- }"
- data-user-theming-background-clear
- tabindex="0"
- @click="removeBackground">
- {{ t('theming', 'No background') }}
- <Close v-if="!isBackgroundDisabled" :size="32" />
- <Check :size="44" />
- </button>
-
<!-- Background set selection -->
<button v-for="shippedBackground in shippedBackgrounds"
:key="shippedBackground.name"
@@ -94,7 +67,7 @@
'icon-loading': loading === shippedBackground.name,
'background--active': backgroundImage === shippedBackground.name
}"
- :data-color-bright="shippedBackground.details.theming === 'dark'"
+ :data-color-bright="invertTextColor(shippedBackground.details.background_color)"
:data-user-theming-background-shipped="shippedBackground.name"
:style="{ backgroundImage: 'url(' + shippedBackground.preview + ')', '--border-color': shippedBackground.details.primary_color }"
tabindex="0"
@@ -105,24 +78,25 @@
</template>
<script>
-import { generateFilePath, generateRemoteUrl, generateUrl } from '@nextcloud/router'
-import { getCurrentUser } from '@nextcloud/auth'
+import { generateFilePath, generateUrl } from '@nextcloud/router'
import { getFilePickerBuilder, showError } from '@nextcloud/dialogs'
import { loadState } from '@nextcloud/initial-state'
-import { Palette } from 'node-vibrant/lib/color.js'
import axios from '@nextcloud/axios'
import debounce from 'debounce'
-import NcColorPicker from '@nextcloud/vue/dist/Components/NcColorPicker.js'
-import Vibrant from 'node-vibrant'
+import NcColorPicker from '@nextcloud/vue/components/NcColorPicker'
import Check from 'vue-material-design-icons/Check.vue'
-import Close from 'vue-material-design-icons/Close.vue'
import ImageEdit from 'vue-material-design-icons/ImageEdit.vue'
+import ColorPalette from 'vue-material-design-icons/PaletteOutline.vue'
-const backgroundImage = loadState('theming', 'backgroundImage')
const shippedBackgroundList = loadState('theming', 'shippedBackgrounds')
-const themingDefaultBackground = loadState('theming', 'themingDefaultBackground')
-const defaultShippedBackground = loadState('theming', 'defaultShippedBackground')
+const backgroundImage = loadState('theming', 'userBackgroundImage')
+const {
+ backgroundImage: defaultBackgroundImage,
+ // backgroundColor: defaultBackgroundColor,
+ backgroundMime: defaultBackgroundMime,
+ defaultShippedBackground,
+} = loadState('theming', 'themingDefaults')
const prefixWithBaseUrl = (url) => generateFilePath('theming', '', 'img/background/') + url
@@ -131,7 +105,7 @@ export default {
components: {
Check,
- Close,
+ ColorPalette,
ImageEdit,
NcColorPicker,
},
@@ -149,7 +123,12 @@ export default {
computed: {
shippedBackgrounds() {
return Object.keys(shippedBackgroundList)
- .map(fileName => {
+ .filter((background) => {
+ // If the admin did not changed the global background
+ // let's hide the default background to not show it twice
+ return background !== defaultShippedBackground || !this.isGlobalBackgroundDefault
+ })
+ .map((fileName) => {
return {
name: fileName,
url: prefixWithBaseUrl(fileName),
@@ -157,27 +136,18 @@ export default {
details: shippedBackgroundList[fileName],
}
})
- .filter(background => {
- // If the admin did not changed the global background
- // let's hide the default background to not show it twice
- if (!this.isGlobalBackgroundDeleted && !this.isGlobalBackgroundDefault) {
- return background.name !== defaultShippedBackground
- }
- return true
- })
},
isGlobalBackgroundDefault() {
- return !!themingDefaultBackground
+ return defaultBackgroundMime === ''
},
isGlobalBackgroundDeleted() {
- return themingDefaultBackground === 'backgroundColor'
+ return defaultBackgroundMime === 'backgroundColor'
},
- isBackgroundDisabled() {
- return this.backgroundImage === 'disabled'
- || !this.backgroundImage
+ cssDefaultBackgroundImage() {
+ return `url('${defaultBackgroundImage}')`
},
},
@@ -225,7 +195,7 @@ export default {
async update(data) {
// Update state
this.backgroundImage = data.backgroundImage
- this.Theming.color = data.backgroundColor
+ this.Theming.backgroundColor = data.backgroundColor
// Notify parent and reload style
this.$emit('update:background')
@@ -244,9 +214,9 @@ export default {
this.update(result.data)
},
- async setFile(path, color = null) {
+ async setFile(path) {
this.loading = 'custom'
- const result = await axios.post(generateUrl('/apps/theming/background/custom'), { value: path, color })
+ const result = await axios.post(generateUrl('/apps/theming/background/custom'), { value: path })
this.update(result.data)
},
@@ -256,15 +226,15 @@ export default {
this.update(result.data)
},
- async pickColor(event) {
+ async pickColor(color) {
this.loading = 'color'
- const color = event?.target?.dataset?.color || this.Theming?.color || '#0082c9'
- const result = await axios.post(generateUrl('/apps/theming/background/color'), { color })
- this.update(result.data)
+ const { data } = await axios.post(generateUrl('/apps/theming/background/color'), { color: color || '#0082c9' })
+ this.update(data)
},
+
debouncePickColor: debounce(function(...args) {
this.pickColor(...args)
- }, 200),
+ }, 1000),
pickFile() {
const picker = getFilePickerBuilder(t('theming', 'Select a background from your files'))
@@ -291,45 +261,7 @@ export default {
}
this.loading = 'custom'
-
- // Extract primary color from image
- let response = null
- let color = null
- try {
- const fileUrl = generateRemoteUrl('dav/files/' + getCurrentUser().uid + path)
- response = await axios.get(fileUrl, { responseType: 'blob' })
- const blobUrl = URL.createObjectURL(response.data)
- const palette = await this.getColorPaletteFromBlob(blobUrl)
-
- // DarkVibrant is accessible AND visually pleasing
- // Vibrant is not accessible enough and others are boring
- color = palette?.DarkVibrant?.hex
- this.setFile(path, color)
-
- // Log data
- console.debug('Extracted colour', color, 'from custom image', path, palette)
- } catch (error) {
- this.setFile(path)
- console.error('Unable to extract colour from custom image', { error, path, response, color })
- }
- },
-
- /**
- * Extract a Vibrant color palette from a blob URL
- *
- * @param {string} blobUrl the blob URL
- * @return {Promise<Palette>}
- */
- getColorPaletteFromBlob(blobUrl) {
- return new Promise((resolve, reject) => {
- const vibrant = new Vibrant(blobUrl)
- vibrant.getPalette((error, palette) => {
- if (error) {
- reject(error)
- }
- resolve(palette)
- })
- })
+ this.setFile(path)
},
},
}
@@ -341,38 +273,48 @@ export default {
flex-wrap: wrap;
justify-content: center;
+ .background-color {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 176px;
+ height: 96px;
+ margin: 8px;
+ border-radius: var(--border-radius-large);
+ background-color: var(--color-primary);
+ }
+
.background {
overflow: hidden;
width: 176px;
height: 96px;
margin: 8px;
text-align: center;
+ word-wrap: break-word;
+ hyphens: auto;
border: 2px solid var(--color-main-background);
border-radius: var(--border-radius-large);
background-position: center center;
background-size: cover;
&__filepicker {
+ background-color: var(--color-background-dark);
+
&.background--active {
- color: white;
+ color: var(--color-background-plain-text);
background-image: var(--image-background);
}
}
&__default {
- background-color: var(--color-primary-default);
- background-image: linear-gradient(to bottom, rgba(23, 23, 23, 0.5), rgba(23, 23, 23, 0.5)), var(--image-background-plain, var(--image-background-default));
+ background-color: var(--color-background-plain);
+ background-image: linear-gradient(to bottom, rgba(23, 23, 23, 0.5), rgba(23, 23, 23, 0.5)), v-bind(cssDefaultBackgroundImage);
}
&__filepicker, &__default, &__color {
border-color: var(--color-border);
}
- &__color {
- color: var(--color-primary-text);
- background-color: var(--color-primary-default);
- }
-
// Over a background image
&__default,
&__shipped {