added dark mode toggle with animation, continued re-organization of settings tab

This commit is contained in:
Roberto Tonino 2020-05-03 22:08:59 +02:00
parent d3b87a008c
commit 289ffb3ecc
9 changed files with 180 additions and 114 deletions

View File

@ -1,15 +1,10 @@
/* Normalizing */
* { * {
margin: 0; transition: background-color 500ms ease-in-out;
padding: 0;
box-sizing: border-box;
} }
body { html {
font-size: 16px; height: 100%;
}
:root {
--main-background: #ffffff; --main-background: #ffffff;
--secondary-background: #eeeeee; --secondary-background: #eeeeee;
--main-text: #333333; --main-text: #333333;
@ -26,8 +21,7 @@ body {
--toast-text: #ffffffde; --toast-text: #ffffffde;
} }
/* Add to body to switch to dark mode */ html[data-theme='dark'] {
.dark-theme {
--main-background: #141414; --main-background: #141414;
--secondary-background: #242424; --secondary-background: #242424;
--main-text: #eeeeee; --main-text: #eeeeee;
@ -35,23 +29,12 @@ body {
--panels-text: #ffffff; --panels-text: #ffffff;
} }
html {
height: 100%;
}
body { body {
margin: 0px; margin: 0px;
width: 100%; width: 100%;
height: 100%; height: 100%;
font-family: 'Open Sans'; font-family: 'Open Sans';
overflow: hidden; overflow: hidden;
background-color: var(--main-background); background: var(--main-background);
color: var(--main-text); color: var(--main-text);
} }
#main_content {
margin-left: 48px;
width: calc(100% - 48px);
height: 100%;
display: flex;
}

View File

@ -1,4 +1,10 @@
/* Global stuff */ #main_content {
margin-left: 48px;
width: calc(100% - 48px);
height: 100%;
display: flex;
}
img.rounded { img.rounded {
border-radius: 5px; border-radius: 5px;
} }
@ -260,3 +266,21 @@ th.sort-desc:after {
font-size: 24px; font-size: 24px;
padding: 16px; padding: 16px;
} }
.with_checkbox {
display: flex;
align-items: center;
}
.with_checkbox [type='checkbox'] {
cursor: pointer;
}
.with_checkbox .checkbox_text {
margin-left: 10px;
cursor: pointer;
}
.with_checkbox .checkbox_text::selection {
background: none;
}

View File

@ -15,7 +15,7 @@
border: 0px; border: 0px;
border-radius: 6px; border-radius: 6px;
background-color: var(--secondary-background); background-color: var(--secondary-background);
color: var(--primary-text) color: var(--primary-text);
} }
#content { #content {
@ -24,7 +24,7 @@
height: calc(100% - 48px); height: calc(100% - 48px);
overflow-y: scroll; overflow-y: scroll;
overflow-x: hidden; overflow-x: hidden;
padding-left: 10px padding-left: 10px;
} }
#content::-webkit-scrollbar { #content::-webkit-scrollbar {
@ -79,19 +79,22 @@
} }
@media only screen and (min-width: 601px) { @media only screen and (min-width: 601px) {
#container, .smallmodal-content { #container,
.smallmodal-content {
width: 85%; width: 85%;
} }
} }
@media only screen and (min-width: 993px) { @media only screen and (min-width: 993px) {
#container, .smallmodal-content { #container,
.smallmodal-content {
width: 70%; width: 70%;
} }
} }
@media only screen and (max-width: 600px) { @media only screen and (max-width: 600px) {
#container, .smallmodal-content { #container,
.smallmodal-content {
width: 100%; width: 100%;
} }
} }

9
public/css/modules/normalize.css vendored Normal file
View File

@ -0,0 +1,9 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-size: 16px;
}

View File

@ -57,3 +57,11 @@
.settings_group > * { .settings_group > * {
margin-bottom: 15px; margin-bottom: 15px;
} }
.input_group {
margin-bottom: 25px;
}
.input_group .input_group_text {
margin-bottom: 7px;
}

View File

@ -3,6 +3,7 @@
@import './vendor/OpenSans.css'; @import './vendor/OpenSans.css';
@import './vendor/toastify.css'; @import './vendor/toastify.css';
@import './modules/normalize.css';
@import './modules/base.css'; @import './modules/base.css';
@import './modules/globals.css'; @import './modules/globals.css';
@import './modules/progressbar.css'; @import './modules/progressbar.css';

View File

@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" dir="ltr"> <html lang="en" dir="ltr" data-theme="default">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
@ -9,7 +9,7 @@
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=0"> content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=0">
<script> <script>
if ('true' === localStorage.getItem('darkMode')) { if ('true' === localStorage.getItem('darkMode')) {
document.documentElement.classList.add('dark-theme') document.documentElement.setAttribute('data-theme', 'dark')
} }
</script> </script>
</head> </head>
@ -165,7 +165,9 @@ <h1>No Tracks found</h1>
v-if="track.preview" class="material-icons preview_controls">play_arrow</i><img v-if="track.preview" class="material-icons preview_controls">play_arrow</i><img
class="rounded coverart" :src="track.album.cover_small"> class="rounded coverart" :src="track.album.cover_small">
</td> </td>
<td class="breakline">{{track.title + (track.title_version ? ' '+track.title_version : '')}}</td> <td class="breakline">
{{ track.title + (track.title_version && track.title.indexOf(track.title_version) == -1 ? ' '+ track.title_version : '') }}
</td>
<td class="breakline clickable" @click="artistView" :data-id="track.artist.id"> <td class="breakline clickable" @click="artistView" :data-id="track.artist.id">
{{track.artist.name}}</td> {{track.artist.name}}</td>
<td class="breakline clickable" @click="albumView" :data-id="track.album.id"> <td class="breakline clickable" @click="albumView" :data-id="track.album.id">
@ -260,7 +262,9 @@ <h2>Top Tracks</h2>
v-if="track.preview" class="material-icons preview_controls">play_arrow</i><img v-if="track.preview" class="material-icons preview_controls">play_arrow</i><img
class="rounded coverart" :src="track.album.cover_small"> class="rounded coverart" :src="track.album.cover_small">
</td> </td>
<td class="breakline">{{track.title + (track.title_version ? ' '+track.title_version : '')}}</td> <td class="breakline">
{{ track.title + (track.title_version && track.title.indexOf(track.title_version) == -1 ? ' '+ track.title_version : '') }}
</td>
<td class="breakline clickable" @click="artistView" :data-id="track.artist.id"> <td class="breakline clickable" @click="artistView" :data-id="track.artist.id">
{{track.artist.name}}</td> {{track.artist.name}}</td>
<td class="breakline clickable" @click="albumView" :data-id="track.album.id"> <td class="breakline clickable" @click="albumView" :data-id="track.album.id">
@ -323,15 +327,16 @@ <h2>Top Playlists</h2>
<h1>Charts</h1> <h1>Charts</h1>
<div v-if='country == ""' id="charts_selection"> <div v-if='country == ""' id="charts_selection">
<div class="release_grid"> <div class="release_grid">
<div v-for="release in countries" class="release clickable" @click="getTrackList" :data-title="release.title" :data-id="release.id"> <div v-for="release in countries" class="release clickable" @click="getTrackList"
:data-title="release.title" :data-id="release.id">
<img class="rounded coverart" :src="release.picture_medium"> <img class="rounded coverart" :src="release.picture_medium">
</div> </div>
</div> </div>
</div> </div>
<div v-else id="charts_table"> <div v-else id="charts_table">
<button @click="changeCountry">Change Country</button> <button @click="changeCountry">Change Country</button>
<button @contextmenu.prevent="openQualityModal" <button @contextmenu.prevent="openQualityModal" @click.stop="addToQueue"
@click.stop="addToQueue" :data-link="'https://www.deezer.com/playlist/'+id">Download Chart</button> :data-link="'https://www.deezer.com/playlist/'+id">Download Chart</button>
<table> <table>
<tr v-for="track in chart" class="track_row"> <tr v-for="track in chart" class="track_row">
<td class="top-tracks-position" :class="{ first: track.position === 1 }">{{ track.position }}</td> <td class="top-tracks-position" :class="{ first: track.position === 1 }">{{ track.position }}</td>
@ -341,7 +346,9 @@ <h1>Charts</h1>
v-if="track.preview" class="material-icons preview_controls">play_arrow</i><img v-if="track.preview" class="material-icons preview_controls">play_arrow</i><img
class="rounded coverart" :src="track.album.cover_small"> class="rounded coverart" :src="track.album.cover_small">
</td> </td>
<td class="breakline">{{track.title + (track.title_version ? ' '+track.title_version : '')}}</td> <td class="breakline">
{{ track.title + (track.title_version && track.title.indexOf(track.title_version) == -1 ? ' '+ track.title_version : '') }}
</td>
<td class="breakline clickable" @click="artistView" :data-id="track.artist.id"> <td class="breakline clickable" @click="artistView" :data-id="track.artist.id">
{{track.artist.name}}</td> {{track.artist.name}}</td>
<td class="breakline clickable" @click="albumView" :data-id="track.album.id"> <td class="breakline clickable" @click="albumView" :data-id="track.album.id">
@ -420,6 +427,13 @@ <h2 id="settings_heading">Settings</h2>
<button id="settings_btn_logout" @click="logout">Logout</button> <button id="settings_btn_logout" @click="logout">Logout</button>
</div> </div>
<div class="settings_group">
<label class="with_checkbox">
<input type="checkbox" v-model="darkMode">
<span class="checkbox_text">Dark Mode</span>
</label>
</div>
<div class="settings_group"> <div class="settings_group">
<h3>Login</h3> <h3>Login</h3>
<div class="inline-flex"> <div class="inline-flex">
@ -455,70 +469,75 @@ <h3>Templates</h3>
<div class="settings_group"> <div class="settings_group">
<h3>Folders</h3> <h3>Folders</h3>
<label class="with_checkbox">
<input type="checkbox" v-model="settings.createPlaylistFolder">
<span class="checkbox_text">Create folder for playlist</span>
</label>
<label><input type="checkbox" v-model="settings.createPlaylistFolder">Create folder for playlist</label> <div class="input_group" v-if="settings.createPlaylistFolder">
<p class="input_group_text">Playlist folder template</p>
<div v-if="settings.createPlaylistFolder">
<p>Playlist folder template</p>
<input type="text" v-model="settings.playlistNameTemplate"> <input type="text" v-model="settings.playlistNameTemplate">
</div> </div>
<div> <label class="with_checkbox">
<p>Create folder for artist</p>
<input type="checkbox" v-model="settings.createArtistFolder"> <input type="checkbox" v-model="settings.createArtistFolder">
</div> <span class="checkbox_text">Create folder for artist</span>
</label>
<div v-if="settings.createArtistFolder"> <div class="input_group" v-if="settings.createArtistFolder">
<p>Artist folder template</p> <p class="input_group_text">Artist folder template</p>
<input type="text" v-model="settings.artistNameTemplate"> <input type="text" v-model="settings.artistNameTemplate">
</div> </div>
<div> <label class="with_checkbox">
<p>Create folder for album</p>
<input type="checkbox" v-model="settings.createAlbumFolder"> <input type="checkbox" v-model="settings.createAlbumFolder">
</div> <span class="checkbox_text">Create folder for album</span>
</label>
<div v-if="settings.createAlbumFolder"> <div class="input_group" v-if="settings.createAlbumFolder">
<p>Album folder template</p> <p class="input_group_text">Album folder template</p>
<input type="text" v-model="settings.albumNameTemplate"> <input type="text" v-model="settings.albumNameTemplate">
</div> </div>
<div> <label class="with_checkbox">
<p>Create folder for CDs</p>
<input type="checkbox" v-model="settings.createCDFolder"> <input type="checkbox" v-model="settings.createCDFolder">
</div> <span class="checkbox_text">Create folder for CDs</span>
</label>
<div> <label class="with_checkbox">
<p>Create folder structure for playlists</p>
<input type="checkbox" v-model="settings.createStructurePlaylist"> <input type="checkbox" v-model="settings.createStructurePlaylist">
</div> <span class="checkbox_text">Create folder structure for playlists</span>
</label>
<div> <label class="with_checkbox">
<p>Create folder structure for singles</p>
<input type="checkbox" v-model="settings.createSingleFolder"> <input type="checkbox" v-model="settings.createSingleFolder">
</div> <span class="checkbox_text">Create folder structure for singles</span>
</label>
</div> </div>
<div id="settings_generic_tab"> <div class="settings_group">
<h3>Track titles</h3>
<div>
<div class="input_group">
<p>Pad tracks</p> <p>Pad tracks</p>
<input type="checkbox" v-model="settings.padTracks"> <input type="checkbox" v-model="settings.padTracks">
</div> </div>
<div class="input_group"> <div>
<p>Overwrite padding size</p> <p>Overwrite padding size</p>
<input type="number" v-model="settings.paddingSize"> <input type="number" v-model="settings.paddingSize">
</div> </div>
<div class="input_group"> <div>
<p>Illegal Character replacer</p> <p>Illegal Character replacer</p>
<input type="text" v-model="settings.illegalCharacterReplacer"> <input type="text" v-model="settings.illegalCharacterReplacer">
</div> </div>
<div class="input_group"> </div>
<div class="settings_group">
<h3>Downloads</h3>
<div>
<p>Concurrent Downloads</p> <p>Concurrent Downloads</p>
<input type="number" v-model.number="settings.queueConcurrency"> <input type="number" v-model.number="settings.queueConcurrency">
</div> </div>
<div class="input_group"> <div>
<p>Preferred Bitrate</p> <p>Preferred Bitrate</p>
<select v-model="settings.maxBitrate"> <select v-model="settings.maxBitrate">
<option value="9">FLAC 1411kbps</option> <option value="9">FLAC 1411kbps</option>
@ -526,75 +545,84 @@ <h3>Folders</h3>
<option value="1">MP3 128kbps</option> <option value="1">MP3 128kbps</option>
</select> </select>
</div> </div>
<div class="input_group"> <div>
<p>Bitrate fallback</p> <p>Bitrate fallback</p>
<input type="checkbox" v-model="settings.fallbackBitrate"> <input type="checkbox" v-model="settings.fallbackBitrate">
</div> </div>
<div class="input_group"> <div>
<p>Search fallback</p> <p>Search fallback</p>
<input type="checkbox" v-model="settings.fallbackSearch"> <input type="checkbox" v-model="settings.fallbackSearch">
</div> </div>
<div class="input_group">
<div>
<p>Create log file for errors</p> <p>Create log file for errors</p>
<input type="checkbox" v-model="settings.logErrors"> <input type="checkbox" v-model="settings.logErrors">
</div> </div>
<div class="input_group"> <div>
<p>Create log file for searched tracks</p> <p>Create log file for searched tracks</p>
<input type="checkbox" v-model="settings.logSearched"> <input type="checkbox" v-model="settings.logSearched">
</div> </div>
<div class="input_group"> <div>
<p>Create playlist file</p> <p>Create playlist file</p>
<input type="checkbox" v-model="settings.createM3U8File"> <input type="checkbox" v-model="settings.createM3U8File">
</div> </div>
<div class="input_group"> <div>
<p>Create .lyr files (Sync Lyrics)</p> <p>Create .lyr files (Sync Lyrics)</p>
<input type="checkbox" v-model="settings.syncedLyrics"> <input type="checkbox" v-model="settings.syncedLyrics">
</div> </div>
<div class="input_group"> </div>
<div class="settings_group">
<h3>Album covers</h3>
<div>
<p>Save covers</p> <p>Save covers</p>
<input type="checkbox" v-model="settings.saveArtwork"> <input type="checkbox" v-model="settings.saveArtwork">
</div> </div>
<div class="input_group"> <div>
<p>Cover name template</p> <p>Cover name template</p>
<input type="text" v-model="settings.coverImageTemplate"> <input type="text" v-model="settings.coverImageTemplate">
</div> </div>
<div class="input_group"> <div>
<p>Save artist image</p> <p>Save artist image</p>
<input type="checkbox" v-model="settings.saveArtworkArtist"> <input type="checkbox" v-model="settings.saveArtworkArtist">
</div> </div>
<div class="input_group"> <div>
<p>Artist image name template</p> <p>Artist image name template</p>
<input type="text" v-model="settings.artistImageTemplate"> <input type="text" v-model="settings.artistImageTemplate">
</div> </div>
<div class="input_group"> <div>
<p>Local artwork size</p> <p>Local artwork size</p>
<input type="number" min="100" max="1800" step="100" v-model.number="settings.localArtworkSize"> <input type="number" min="100" max="1800" step="100" v-model.number="settings.localArtworkSize">
</div> </div>
<div class="input_group"> <div>
<p>Embedded artwork size</p> <p>Embedded artwork size</p>
<input type="number" min="100" max="1800" step="100" v-model.number="settings.embeddedArtworkSize"> <input type="number" min="100" max="1800" step="100" v-model.number="settings.embeddedArtworkSize">
</div> </div>
<div class="input_group"> <div>
<p>Save images as png</p> <p>Save images as png</p>
<input type="checkbox" v-model="settings.PNGcovers"> <input type="checkbox" v-model="settings.PNGcovers">
</div> </div>
<div class="input_group"> <div>
<p>JPEG image quality</p> <p>JPEG image quality</p>
<input type="number" min="1" max="100" v-model.number="settings.jpegImageQuality"> <input type="number" min="1" max="100" v-model.number="settings.jpegImageQuality">
</div> </div>
<div class="input_group">
</div>
<div class="settings_group">
<div>
<p>Save playlists as compilation</p> <p>Save playlists as compilation</p>
<input type="checkbox" v-model="settings.tags.savePlaylistAsCompilation"> <input type="checkbox" v-model="settings.tags.savePlaylistAsCompilation">
</div> </div>
<div class="input_group"> <div>
<p>Use null separator</p> <p>Use null separator</p>
<input type="checkbox" v-model="settings.tags.useNullSeparator"> <input type="checkbox" v-model="settings.tags.useNullSeparator">
</div> </div>
<div class="input_group"> <div>
<p>Save ID3v1 as well</p> <p>Save ID3v1 as well</p>
<input type="checkbox" v-model="settings.tags.saveID3v1"> <input type="checkbox" v-model="settings.tags.saveID3v1">
</div> </div>
<div class="input_group"> <div>
<p>How would you like to separate your artists?</p> <p>How would you like to separate your artists?</p>
<select v-model="settings.tags.multitagSeparator"> <select v-model="settings.tags.multitagSeparator">
<option value="default">Using standard specification</option> <option value="default">Using standard specification</option>
@ -608,11 +636,11 @@ <h3>Folders</h3>
<option value="; ">Using "; "</option> <option value="; ">Using "; "</option>
</select> </select>
</div> </div>
<div class="input_group"> <div>
<p>Remove album version from track title</p> <p>Remove album version from track title</p>
<input type="checkbox" v-model="settings.removeAlbumVersion"> <input type="checkbox" v-model="settings.removeAlbumVersion">
</div> </div>
<div class="input_group"> <div>
<p>Date format for FLAC files</p> <p>Date format for FLAC files</p>
<select v-model="settings.dateFormat"> <select v-model="settings.dateFormat">
<option value="Y-M-D">YYYY-MM-DD</option> <option value="Y-M-D">YYYY-MM-DD</option>
@ -622,7 +650,7 @@ <h3>Folders</h3>
<option value="Y">YYYY</option> <option value="Y">YYYY</option>
</select> </select>
</div> </div>
<div class="input_group"> <div>
<p>What should I do with featured artists</p> <p>What should I do with featured artists</p>
<select v-model="settings.featuredToTitle"> <select v-model="settings.featuredToTitle">
<option value="0">Nothing</option> <option value="0">Nothing</option>
@ -630,7 +658,7 @@ <h3>Folders</h3>
<option value="2">Move it to the title</option> <option value="2">Move it to the title</option>
</select> </select>
</div> </div>
<div class="input_group"> <div>
<p>Title casing</p> <p>Title casing</p>
<select v-model="settings.titleCasing"> <select v-model="settings.titleCasing">
<option value="nothing">Keep unchanged</option> <option value="nothing">Keep unchanged</option>
@ -640,7 +668,7 @@ <h3>Folders</h3>
<option value="sentence">Like a sentence</option> <option value="sentence">Like a sentence</option>
</select> </select>
</div> </div>
<div class="input_group"> <div>
<p>Artist casing</p> <p>Artist casing</p>
<select v-model="settings.artistCasing"> <select v-model="settings.artistCasing">
<option value="nothing">Keep unchanged</option> <option value="nothing">Keep unchanged</option>
@ -650,18 +678,18 @@ <h3>Folders</h3>
<option value="sentence">Like a sentence</option> <option value="sentence">Like a sentence</option>
</select> </select>
</div> </div>
<div class="input_group"> <div>
<p>Command to execute after download</p> <p>Command to execute after download</p>
<p class="secondary-text">Leave blank for no action</p> <p class="secondary-text">Leave blank for no action</p>
<input type="text" v-model="settings.executeCommand"> <input type="text" v-model="settings.executeCommand">
</div> </div>
</div> </div>
<div id="settings_spotify_tab"> <div id="settings_spotify_tab">
<div class="input_group"> <div>
<p>Spotify clientID</p> <p>Spotify clientID</p>
<input type="text" v-model="spotifyFeatures.clientId"> <input type="text" v-model="spotifyFeatures.clientId">
</div> </div>
<div class="input_group"> <div>
<p>Spotify Client Secret</p> <p>Spotify Client Secret</p>
<input type="password" v-model="spotifyFeatures.clientSecret"> <input type="password" v-model="spotifyFeatures.clientSecret">
</div> </div>

View File

@ -2,13 +2,23 @@ import { toast } from '../toasts.js'
import { socket } from '../socket.js' import { socket } from '../socket.js'
const SettingsTab = new Vue({ const SettingsTab = new Vue({
data() { data: () => ({
return {
settings: { tags: {} }, settings: { tags: {} },
lastSettings: {}, lastSettings: {},
lastCredentials: {}, lastCredentials: {},
spotifyFeatures: {}, spotifyFeatures: {},
defaultSettings: {} defaultSettings: {}
}),
computed: {
darkMode: {
get() {
return 'true' === localStorage.getItem('darkMode')
},
set(wantDarkMode) {
document.documentElement.setAttribute('data-theme', wantDarkMode ? 'dark' : 'default')
localStorage.setItem('darkMode', wantDarkMode)
}
} }
}, },
methods: { methods: {

View File

@ -5,7 +5,7 @@ import QualityModal from '../quality-modal.js'
import TrackPreview from '../track-preview.js' import TrackPreview from '../track-preview.js'
const TracklistTab = new Vue({ const TracklistTab = new Vue({
data: { data: () => ({
title: '', title: '',
metadata: '', metadata: '',
release_date: '', release_date: '',
@ -16,7 +16,7 @@ const TracklistTab = new Vue({
link: '', link: '',
head: null, head: null,
body: [] body: []
}, }),
methods: { methods: {
artistView, artistView,
albumView, albumView,
@ -32,21 +32,21 @@ const TracklistTab = new Vue({
this.head = [] this.head = []
this.body = [] this.body = []
}, },
addToQueue: function (e) { addToQueue(e) {
e.stopPropagation() e.stopPropagation()
Downloads.sendAddToQueue(e.currentTarget.dataset.link) Downloads.sendAddToQueue(e.currentTarget.dataset.link)
}, },
openQualityModal: function (e) { openQualityModal(e) {
QualityModal.open(e.currentTarget.dataset.link) QualityModal.open(e.currentTarget.dataset.link)
}, },
toggleAll: function (e) { toggleAll(e) {
this.body.forEach(item => { this.body.forEach(item => {
if (item.type == 'track') { if (item.type == 'track') {
item.selected = e.currentTarget.checked item.selected = e.currentTarget.checked
} }
}) })
}, },
selectedLinks: function () { selectedLinks() {
var selected = [] var selected = []
if (this.body) { if (this.body) {
this.body.forEach(item => { this.body.forEach(item => {