From 41d5da964fb6f1a77c7dfca56906d2d76df8465a Mon Sep 17 00:00:00 2001
From: Pierre-Jean CHANCELLIER <paidge_cs@hotmail.com>
Date: Wed, 15 Dec 2021 16:07:31 +0100
Subject: [PATCH] add theme selector

---
 assets/css/style.scss                  | 26 +------
 components/btn/Theme.vue               | 97 ++++++++++++++++++++++++++
 components/navigation/Bar.vue          | 38 ++++++++--
 components/navigation/Breadcrumb.vue   | 13 ++--
 components/navigation/menu/Sidebar.vue |  4 +-
 layouts/default.vue                    | 43 +++++++++++-
 nuxt.config.js                         |  5 +-
 pages/appolo.vue                       |  2 +-
 pages/chartjs.vue                      |  5 +-
 pages/explore.vue                      |  2 +-
 pages/index.vue                        |  2 +-
 pages/test.vue                         | 48 +++++++++++++
 12 files changed, 240 insertions(+), 45 deletions(-)
 create mode 100644 components/btn/Theme.vue
 create mode 100644 pages/test.vue

diff --git a/assets/css/style.scss b/assets/css/style.scss
index 20aefe5..b402489 100644
--- a/assets/css/style.scss
+++ b/assets/css/style.scss
@@ -1,29 +1,7 @@
 $link-hover-decoration: none;
 $text-muted: #a6a4b0;
+$enable-responsive-font-sizes: true;
 $font-family-base: Montserrat, Helvetica, Arial, serif;
 
 @import 'font';
-@import 'bootstrap';
-
-:root {
-    --menu-size: 320px;
-}
-
-.content {
-    @extend .container-fluid;
-}
-
-.table.striped tbody {
-    tr {
-        opacity: .9;
-        cursor: pointer;
-
-        &:nth-child(2n+1) {
-            opacity: .7;
-        }
-
-        &:hover {
-            opacity: 1;
-        }
-    }
-}
\ No newline at end of file
+@import 'bootstrap';
\ No newline at end of file
diff --git a/components/btn/Theme.vue b/components/btn/Theme.vue
new file mode 100644
index 0000000..cb98626
--- /dev/null
+++ b/components/btn/Theme.vue
@@ -0,0 +1,97 @@
+<template>
+  <div>
+    <input @change="toggleTheme" id="checkbox" type="checkbox" class="switch-checkbox" />
+    <label for="checkbox" class="switch-label">
+      <span>🌙</span>
+      <span>☀️</span>
+      <div class="switch-toggle" :class="{ 'switch-toggle-checked': userTheme === 'dark-theme' }"></div>
+    </label>
+  </div>
+</template>
+
+<script>
+export default {
+  mounted() {
+    const initUserTheme = this.getMediaPreference();
+    this.setTheme(initUserTheme);
+  },
+
+  data() {
+    return {
+      userTheme: "light-theme",
+    };
+  },
+
+  methods: {
+    toggleTheme() {
+      const activeTheme = localStorage.getItem("user-theme");
+      if (activeTheme === "light-theme") {
+        this.setTheme("dark-theme");
+      } else {
+        this.setTheme("light-theme");
+      }
+    },
+
+    setTheme(theme) {
+      localStorage.setItem("user-theme", theme);
+      this.userTheme = theme;
+      document.documentElement.className = theme;
+    },
+
+    getMediaPreference() {
+      const hasDarkPreference = window.matchMedia(
+        "(prefers-color-scheme: dark)"
+      ).matches;
+      if (hasDarkPreference) {
+        return "dark-theme";
+      } else {
+        return "light-theme";
+      }
+    },
+  },
+};
+</script>
+
+<!-- Add "scoped" attribute to limit CSS to this component only -->
+<style scoped>
+*, ::before, ::after {
+    box-sizing: initial;
+}
+.switch-checkbox {
+  display: none;
+}
+
+.switch-label {
+  align-items: center;
+  background: var(--text-primary-color);
+  border: calc(var(--element-size) * 0.025) solid var(--accent-color);
+  border-radius: var(--element-size);
+  cursor: pointer;
+  display: flex;
+  font-size: calc(var(--element-size) * 0.3);
+  height: calc(var(--element-size) * 0.35);
+  position: relative;
+  padding: calc(var(--element-size) * 0.1);
+  transition: background 0.5s ease;
+  justify-content: space-between;
+  width: var(--element-size);
+  z-index: 1;
+  margin-bottom: 0;
+}
+
+.switch-toggle {
+  position: absolute;
+  background-color: var(--background-color-primary);
+  border-radius: 50%;
+  top: calc(var(--element-size) * 0.07);
+  left: calc(var(--element-size) * 0.07);
+  height: calc(var(--element-size) * 0.4);
+  width: calc(var(--element-size) * 0.4);
+  transform: translateX(0);
+  transition: transform 0.3s ease, background-color 0.5s ease;
+}
+
+.switch-toggle-checked {
+  transform: translateX(calc(var(--element-size) * 0.6)) !important;
+}
+</style>
diff --git a/components/navigation/Bar.vue b/components/navigation/Bar.vue
index 6f9f89f..6fb9982 100644
--- a/components/navigation/Bar.vue
+++ b/components/navigation/Bar.vue
@@ -1,8 +1,8 @@
 <template>
   <header>
     <div class="position-relative">
-      <button class="toggle btn border-secondary position-absolute p-1 m-3" @click="toggleMenu"><span></span></button>
-      <NavigationBreadcrumb :breadcrumb="breadcrumb" class="ml-5 py-3 px-4" />
+      <button class="toggle btn border-secondary position-absolute p-1 m-1 ml-3" @click="toggleMenu"><span></span></button>
+      <NavigationBreadcrumb :breadcrumb="breadcrumb" class="breadcrumb p-1" />
     </div>
     <NavigationMenuSidebar @toggleMenu="toggleMenu" :menus="menus" />
     <div class="bg_overlay" @click="toggleMenu"></div>
@@ -28,11 +28,32 @@ $bg-menu: #fff;
 $btn-width: 50px;
 $line-color: #000;
 
+nav.breadcrumb {
+  margin: .5rem .5rem .5rem 5rem;
+  justify-content: space-between;
+  align-items: center;
+
+  a {color: var(--text-primary-color)}
+
+  .breadcrumb-item {
+    &.active {
+      color: var(--text-primary-color);
+      opacity: .7;
+    }
+
+    & + .breadcrumb-item::before {color: var(--text-primary-color)}
+  }
+
+  &, .breadcrumb {
+    background-color: var(--background-color-secondary);
+  }
+}
+
 %hamburger-line {
   display: block;
   height: 4px;
   width: .8 * $btn-width;
-  background: var(--color);
+  background: var(--text-primary-color);
   content: "";
   position: absolute;
   transition-property: transform;
@@ -40,7 +61,6 @@ $line-color: #000;
 }
 
 .toggle {
-  --color: #{$line-color};
   height: $btn-width;
   width: $btn-width;
   line-height: $btn-width;
@@ -83,7 +103,7 @@ $line-color: #000;
 
 .menu {
   width: var(--menu-size);
-  background: $bg-menu;
+  background: var(--background-color-primary);
   position: fixed;
   top: 0;
   z-index: 1200;
@@ -95,7 +115,15 @@ $line-color: #000;
   transition: left .5s ease-in-out;
   left: -400px;
 
+  h1 {color: var(--text-primary-color);}
+
   .list-group-item {
+    background-color: var(--background-color-primary);
+
+    &-action {
+      color: var(--text-primary-color);
+    }
+
     &.nuxt-link-active {
       z-index: 2;
       color: #fff;
diff --git a/components/navigation/Breadcrumb.vue b/components/navigation/Breadcrumb.vue
index 4338751..e5d6383 100644
--- a/components/navigation/Breadcrumb.vue
+++ b/components/navigation/Breadcrumb.vue
@@ -1,11 +1,12 @@
 <template>
     <nav aria-label="breadcrumb">
-        <ol class="breadcrumb m-0">
-            <li class="breadcrumb-item" :class="{ 'active': item.active }" :aria-current="item.active ? 'page' : null" v-for="item in breadcrumb" :key="item.text">
-                <NuxtLink :to="item.to" v-if="item.to">{{ item.text }}</NuxtLink>
-                <span v-else>{{ item.text }}</span>
-            </li>
-        </ol>
+      <ol class="breadcrumb m-0">
+          <li class="breadcrumb-item" :class="{ 'active': item.active }" :aria-current="item.active ? 'page' : null" v-for="item in breadcrumb" :key="item.text">
+              <NuxtLink :to="item.to" v-if="item.to">{{ item.text }}</NuxtLink>
+              <span v-else>{{ item.text }}</span>
+          </li>
+      </ol>
+      <BtnTheme />
     </nav>
 </template>
 
diff --git a/components/navigation/menu/Sidebar.vue b/components/navigation/menu/Sidebar.vue
index 2114ce6..d76a2ae 100644
--- a/components/navigation/menu/Sidebar.vue
+++ b/components/navigation/menu/Sidebar.vue
@@ -1,12 +1,12 @@
 <template>
     <aside class="menu shadow">
         <div class="nav_header border-bottom pb-3 mb-5">
-            <nuxt-link to="/" class="text-dark"><h1 class="h2 d-flex"><img src="@/assets/img/logo.png" alt="Accueil" class="logo">&nbsp;Wotwizard</h1></nuxt-link>
+            <nuxt-link to="/"><h1 class="h2 d-flex"><img src="@/assets/img/logo.png" alt="Accueil" class="logo">&nbsp;Wotwizard</h1></nuxt-link>
             <button type="button" class="close position-absolute d-xl-none" aria-label="Close" @click="toggleMenu">
                 <span aria-hidden="true">&times;</span>
             </button>
         </div>
-        <nav class="navbar-light">
+        <nav>
             <NavigationMenuGroup v-for="menu in menus" :key="menu.title" :menu="menu"/>
         </nav>
     </aside>
diff --git a/layouts/default.vue b/layouts/default.vue
index ae8a265..37ec8bd 100644
--- a/layouts/default.vue
+++ b/layouts/default.vue
@@ -16,7 +16,8 @@ export default {
           items : [
               {path: '/explore',title: 'Explorer la toile de confiance'},
               {path: '/appolo',title: 'Appolo'},
-              {path: '/chartjs',title: 'ChartJS'}
+              {path: '/chartjs',title: 'ChartJS'},
+              {path: '/test',title: 'Test'}
           ]},
           {
           title: 'Un menu',
@@ -36,7 +37,47 @@ export default {
 </script>
 
 <style lang="scss">
+/* Define styles for the default root window element */
+:root {
+  --text-primary-color: var(--dark);
+  --text-secondary-color: var(--white);
+  --accent-color: var(--light);
+  --background-color-primary: var(--white);
+  --background-color-secondary: #e9ecef;
+  --element-size: 4rem;
+  --menu-size: 320px;
+}
+
+/* Define styles for the root window with dark - mode preference */
+:root.dark-theme {
+  --text-primary-color: var(--white);
+  --text-secondary-color: var(--dark);
+  --accent-color: var(--dark);
+  --background-color-primary: var(--dark);
+  --background-color-secondary: hsl(210, 16%, 60%);
+}
+
+body {
+  background-color: var(--background-color-primary);
+  color: var(--text-primary-color);
+}
+
 .app {
     transition: margin .5s ease-in-out;
 }
+
+.table.striped tbody {
+    tr {
+        opacity: .9;
+        cursor: pointer;
+
+        &:nth-child(2n+1) {
+            opacity: .7;
+        }
+
+        &:hover {
+            opacity: 1;
+        }
+    }
+}
 </style>
\ No newline at end of file
diff --git a/nuxt.config.js b/nuxt.config.js
index bf1608d..1c3d5d5 100644
--- a/nuxt.config.js
+++ b/nuxt.config.js
@@ -34,12 +34,13 @@ export default {
 
   // Modules for dev and build (recommended): https://go.nuxtjs.dev/config-modules
   buildModules: [
+    // https://go.nuxtjs.dev/pwa
+    '@nuxtjs/pwa',
   ],
 
   // Modules: https://go.nuxtjs.dev/config-modules
   modules: [
-    // https://go.nuxtjs.dev/pwa
-    '@nuxtjs/pwa',
+    // https://github.com/nuxt-community/apollo-module
     '@nuxtjs/apollo',
   ],
 
diff --git a/pages/appolo.vue b/pages/appolo.vue
index eed3c80..1f879b0 100644
--- a/pages/appolo.vue
+++ b/pages/appolo.vue
@@ -1,5 +1,5 @@
 <template>
-<main class="content">
+<main class="container-fluid">
   <h2 class="display-2 text-center mb-5">Test Appolo</h2>
   <div class="row">
     <div class="col-6 m-auto text-center">
diff --git a/pages/chartjs.vue b/pages/chartjs.vue
index 71b19a8..222ccb0 100644
--- a/pages/chartjs.vue
+++ b/pages/chartjs.vue
@@ -1,5 +1,5 @@
 <template>
-  <div class="demo">
+  <main class="container-fluid demo">
     <p class="text-center">Pour activer/désactiver des types de graphiques, il faut éditer le fichier <code>/plugins/chart.js</code></p>
     <select class="form-control" id="exampleFormControlSelect1" v-model="activeType">
       <option v-for="type in types" :key="type">{{type}}</option>
@@ -8,10 +8,11 @@
       <component :is="type+'-chart'" :chart-data="datacollection" :options="chartOptions" v-if="activeType==type" />
     </div>
     <button @click="fillData()" class="btn btn-primary">Randomize</button>
-  </div>
+  </main>
 </template>
 
 <script>
+// Import to access to chartTypes
 import chartJS from "~/plugins/chart.js"
 
   export default {
diff --git a/pages/explore.vue b/pages/explore.vue
index 8a38b76..b6651fe 100644
--- a/pages/explore.vue
+++ b/pages/explore.vue
@@ -1,5 +1,5 @@
 <template>
-<main class="content">
+<main class="container-fluid">
   <h2 class="display-2 text-center mb-5">Explorer avec fetch</h2>
   <div class="row">
     <div class="col-6 m-auto">
diff --git a/pages/index.vue b/pages/index.vue
index b69cbbb..541c0bc 100644
--- a/pages/index.vue
+++ b/pages/index.vue
@@ -1,5 +1,5 @@
 <template>
-<main class="content">
+<main class="container-fluid">
   <h2 class="display-2 text-center mb-5">Page d'accueil</h2>
   <div class="row">
     <div class="col-6 m-auto text-center">
diff --git a/pages/test.vue b/pages/test.vue
new file mode 100644
index 0000000..4071e11
--- /dev/null
+++ b/pages/test.vue
@@ -0,0 +1,48 @@
+<template>
+  <main class="container-fluid">
+    <h1>Hello Nuxters! 👋</h1>
+    <p>
+      This page is rendered on the <strong>{{ rendering }}</strong>
+    </p>
+    <p v-if="rendering === 'server'">
+      First load or hard refresh is done on server side.
+    </p>
+    <p v-if="rendering === 'client'">Navigation is done on client side.</p>
+    <ul>
+      <li>Refresh the page for server side rendering.</li>
+      <li>Click the links to see client side rendering.</li>
+    </ul>
+    <NuxtLink to="/">Home Page</NuxtLink>
+  </main>
+</template>
+
+<script>
+export default {
+  data() {
+    // Variables locales
+    return {
+      // Fil d'ariane
+      breadcrumb: [
+        {
+          text: 'Accueil',
+          to: "/"
+        },
+        {
+          text: 'Test',
+          active: true
+        }
+      ],
+      hello: ''
+    }
+  },
+  asyncData() {
+    return {
+      rendering: process.server ? 'server' : 'client'
+    }
+  },
+  mounted () {
+    // Mise à jour du fil d'ariane au chargement
+    $nuxt.$emit('changeRoute',this.breadcrumb)
+  }
+}
+</script>
-- 
GitLab