Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ui): Dark Mode #2493

Merged
merged 2 commits into from
May 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 75 additions & 46 deletions src_assets/common/assets/web/Navbar.vue
Original file line number Diff line number Diff line change
@@ -1,60 +1,89 @@
<template>
<nav class="navbar navbar-expand-lg navbar-light" style="background-color: #ffc400">
<div class="container-fluid">
<a class="navbar-brand" href="/" title="Sunshine">
<img src="/images/logo-sunshine-45.png" height="45" alt="Sunshine">
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" href="/"><i class="fas fa-fw fa-home"></i> {{ $t('navbar.home') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/pin"><i class="fas fa-fw fa-unlock"></i> {{ $t('navbar.pin') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/apps"><i class="fas fa-fw fa-stream"></i> {{ $t('navbar.applications') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/config"><i class="fas fa-fw fa-cog"></i> {{ $t('navbar.configuration') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/password"><i class="fas fa-fw fa-user-shield"></i> {{ $t('navbar.password') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/troubleshooting"><i class="fas fa-fw fa-info"></i> {{ $t('navbar.troubleshoot') }}</a>
</li>
</ul>
</div>
</div>
</nav>
<nav class="navbar navbar-light navbar-expand-lg navbar-background header">
<div class="container-fluid">
<a class="navbar-brand" href="/" title="Sunshine">
<img src="/images/logo-sunshine-45.png" height="45" alt="Sunshine">
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent"
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" href="/"><i class="fas fa-fw fa-home"></i> {{ $t('navbar.home') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/pin"><i class="fas fa-fw fa-unlock"></i> {{ $t('navbar.pin') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/apps"><i class="fas fa-fw fa-stream"></i> {{ $t('navbar.applications') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/config"><i class="fas fa-fw fa-cog"></i> {{ $t('navbar.configuration') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/password"><i class="fas fa-fw fa-user-shield"></i> {{ $t('navbar.password') }}</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/troubleshooting"><i class="fas fa-fw fa-info"></i> {{ $t('navbar.troubleshoot') }}</a>
</li>
<li class="nav-item">
<ThemeToggle/>
</li>
</ul>
</div>
</div>
</nav>
</template>

<script>
import ThemeToggle from './ThemeToggle.vue'

export default {
created() {
console.log("Header mounted!")
},
mounted() {
let el = document.querySelector("a[href='" + document.location.pathname + "']");
if (el) el.classList.add("active")
let discordWidget = document.createElement('script')
discordWidget.setAttribute('src', 'https://app.lizardbyte.dev/js/discord.js')
document.head.appendChild(discordWidget)
}
components: { ThemeToggle },
created() {
console.log("Header mounted!")
},
mounted() {
let el = document.querySelector("a[href='" + document.location.pathname + "']");
if (el) el.classList.add("active")
let discordWidget = document.createElement('script')
discordWidget.setAttribute('src', 'https://app.lizardbyte.dev/js/discord.js')
document.head.appendChild(discordWidget)
}
}
</script>

<style>
.nav-link.active {
font-weight: 500;
.navbar-background {
Hazer marked this conversation as resolved.
Show resolved Hide resolved
background-color: #ffc400
}

.header .nav-link {
color: rgba(0, 0, 0, .65) !important;
}

.header .nav-link.active {
color: rgb(0, 0, 0) !important;
font-weight: 500;
}

.header .nav-link:hover {
color: rgb(0, 0, 0) !important;
font-weight: 500;
}

.header .navbar-toggler {
color: rgba(var(--bs-dark-rgb), .65) !important;
border: var(--bs-border-width) solid rgba(var(--bs-dark-rgb), 0.15) !important;
}

.header .navbar-toggler-icon {
--bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%2833, 37, 41, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") !important;
}

.form-control::placeholder {
opacity: 0.5;
opacity: 0.5;
}
</style>
46 changes: 46 additions & 0 deletions src_assets/common/assets/web/ThemeToggle.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<script setup>
import { loadAutoTheme, setupThemeToggleListener } from './theme'
import { onMounted } from 'vue'

onMounted(() => {
loadAutoTheme()
setupThemeToggleListener()
})
</script>

<template>
<div class="dropdown bd-mode-toggle">
<a class="nav-link dropdown-toggle align-items-center"
id="bd-theme"
type="button"
aria-expanded="false"
data-bs-toggle="dropdown"
aria-label="{{ $t('navbar.toggle_theme') }} ({{ $t('navbar.theme_auto') }})">
<span class="bi my-1 theme-icon-active"><i class="fa-solid fa-circle-half-stroke"></i></span>
<span id="bd-theme-text">{{ $t('navbar.toggle_theme') }}</span>
</a>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="bd-theme-text">
<li>
<button type="button" class="dropdown-item d-flex align-items-center" data-bs-theme-value="light" aria-pressed="false">
<i class="bi me-2 theme-icon fas fa-fw fa-solid fa-sun"></i>
{{ $t('navbar.theme_light') }}
</button>
</li>
<li>
<button type="button" class="dropdown-item d-flex align-items-center" data-bs-theme-value="dark" aria-pressed="false">
<i class="bi me-2 theme-icon fas fa-fw fa-solid fa-moon"></i>
{{ $t('navbar.theme_dark') }}
</button>
</li>
<li>
<button type="button" class="dropdown-item d-flex align-items-center active" data-bs-theme-value="auto" aria-pressed="true">
<i class="bi me-2 theme-icon fas fa-fw fa-solid fa-circle-half-stroke"></i>
{{ $t('navbar.theme_auto') }}
</button>
</li>
</ul>
</div>
</template>

<style scoped>
</style>
6 changes: 3 additions & 3 deletions src_assets/common/assets/web/apps.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<html lang="en" data-bs-theme="auto">

<head>
<%- header %>
Expand Down Expand Up @@ -355,10 +355,10 @@ <h4>{{ $t('apps.env_vars_about') }}</h4>
</div>
</body>
<script type="module">
import { createApp } from 'vue';
import { createApp } from 'vue'
import { initApp } from './init'
import Navbar from './Navbar.vue'
import {Dropdown} from 'bootstrap'
import { Dropdown } from 'bootstrap/dist/js/bootstrap'

const app = createApp({
components: {
Expand Down
8 changes: 1 addition & 7 deletions src_assets/common/assets/web/config.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<html lang="en" data-bs-theme="auto">

<head>
<%- header %>
Expand All @@ -13,12 +13,6 @@
.buttons {
padding: 1em 0;
}

.ms-item {
background-color: #ccc;
font-size: 12px;
font-weight: bold;
}
</style>
</head>

Expand Down
3 changes: 3 additions & 0 deletions src_assets/common/assets/web/configs/tabs/AudioVideo.vue
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,6 @@ const config = ref(props.config)

</div>
</template>

<style scoped>
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,11 @@ const fpsIn = ref("")
<div class="form-text">{{ $t('config.res_fps_desc') }}</div>
</div>
</template>

<style scoped>
.ms-item {
background-color: var(--bs-dark-bg-subtle);
font-size: 12px;
font-weight: bold;
}
</style>
2 changes: 1 addition & 1 deletion src_assets/common/assets/web/index.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<html lang="en" data-bs-theme="auto">

<head>
<%- header %>
Expand Down
5 changes: 5 additions & 0 deletions src_assets/common/assets/web/init.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import i18n from './locale'

// must import even if not implicitly using here
// https://github.com/aurelia/skeleton-navigation/issues/894
// https://discourse.aurelia.io/t/bootstrap-import-bootstrap-breaks-dropdown-menu-in-navbar/641/9
import 'bootstrap/dist/js/bootstrap'

export function initApp(app, config) {
//Wait for locale initialization, then render
i18n().then(i18n => {
Expand Down
2 changes: 1 addition & 1 deletion src_assets/common/assets/web/password.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<html lang="en" data-bs-theme="auto">

<head>
<%- header %>
Expand Down
2 changes: 1 addition & 1 deletion src_assets/common/assets/web/pin.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<html lang="en" data-bs-theme="auto">

<head>
<%- header %>
Expand Down
12 changes: 12 additions & 0 deletions src_assets/common/assets/web/public/assets/css/sunshine.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,15 @@
[v-cloak] {
display: none;
}

[data-bs-theme=dark] .element {
color: var(--bs-primary-text-emphasis);
background-color: var(--bs-primary-bg-subtle);
}

@media (prefers-color-scheme: dark) {
.element {
color: var(--bs-primary-text-emphasis);
background-color: var(--bs-primary-bg-subtle);
}
}
4 changes: 4 additions & 0 deletions src_assets/common/assets/web/public/assets/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,10 @@
"home": "Home",
"password": "Change Password",
"pin": "Pin",
"theme_auto": "Auto",
"theme_dark": "Dark",
"theme_light": "Light",
"toggle_theme": "Theme",
"troubleshoot": "Troubleshooting"
},
"password": {
Expand Down
1 change: 0 additions & 1 deletion src_assets/common/assets/web/template_header.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,3 @@
<link href="@fortawesome/fontawesome-free/css/all.min.css" rel="stylesheet">
<link href="bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" />
<link href="/assets/css/sunshine.css" rel="stylesheet" />
<script type="module" src="bootstrap/dist/js/bootstrap.bundle.min.js"></script>
84 changes: 84 additions & 0 deletions src_assets/common/assets/web/theme.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
const getStoredTheme = () => localStorage.getItem('theme')
const setStoredTheme = theme => localStorage.setItem('theme', theme)

export const getPreferredTheme = () => {
const storedTheme = getStoredTheme()
if (storedTheme) {
return storedTheme
}

return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
}

const setTheme = theme => {
if (theme === 'auto') {
document.documentElement.setAttribute(
'data-bs-theme',
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
)
} else {
document.documentElement.setAttribute('data-bs-theme', theme)
}
}

export const showActiveTheme = (theme, focus = false) => {
const themeSwitcher = document.querySelector('#bd-theme')

if (!themeSwitcher) {
return
}

const themeSwitcherText = document.querySelector('#bd-theme-text')
const activeThemeIcon = document.querySelector('.theme-icon-active i')
const btnToActive = document.querySelector(`[data-bs-theme-value="${theme}"]`)
const classListOfActiveBtn = btnToActive.querySelector('i').classList

document.querySelectorAll('[data-bs-theme-value]').forEach(element => {
element.classList.remove('active')
element.setAttribute('aria-pressed', 'false')
})

btnToActive.classList.add('active')
btnToActive.setAttribute('aria-pressed', 'true')
activeThemeIcon.classList.remove(...activeThemeIcon.classList.values())
activeThemeIcon.classList.add(...classListOfActiveBtn)
const themeSwitcherLabel = `${themeSwitcherText.textContent} (${btnToActive.textContent.trim()})`
themeSwitcher.setAttribute('aria-label', themeSwitcherLabel)

if (focus) {
themeSwitcher.focus()
}
}

export function setupThemeToggleListener() {
document.querySelectorAll('[data-bs-theme-value]')
.forEach(toggle => {
toggle.addEventListener('click', () => {
const theme = toggle.getAttribute('data-bs-theme-value')
setStoredTheme(theme)
setTheme(theme)
showActiveTheme(theme, true)
})
})

showActiveTheme(getPreferredTheme(), false)
}

export function loadAutoTheme() {
(() => {
'use strict'

setTheme(getPreferredTheme())

window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
const storedTheme = getStoredTheme()
if (storedTheme !== 'light' && storedTheme !== 'dark') {
setTheme(getPreferredTheme())
}
})

window.addEventListener('DOMContentLoaded', () => {
showActiveTheme(getPreferredTheme())
})
})()
}
2 changes: 1 addition & 1 deletion src_assets/common/assets/web/troubleshooting.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<html lang="en" data-bs-theme="auto">

<head>
<%- header %>
Expand Down
Loading
Loading