console: add a theme provider to not rely on CSS only

This commit is contained in:
Vincent Bernat
2022-05-10 15:54:56 +02:00
parent 2319262340
commit 1c0c660451
8 changed files with 164 additions and 119 deletions

View File

@@ -1,4 +1,5 @@
<template> <template>
<ThemeProvider>
<AppNotifications /> <AppNotifications />
<div class="flex max-h-screen flex-col"> <div class="flex max-h-screen flex-col">
<NavigationBar class="flex-none" /> <NavigationBar class="flex-none" />
@@ -6,6 +7,7 @@
<router-view /> <router-view />
</main> </main>
</div> </div>
</ThemeProvider>
</template> </template>
<script setup> <script setup>
@@ -13,4 +15,5 @@ import "./tailwind.css";
import NavigationBar from "./components/NavigationBar.vue"; import NavigationBar from "./components/NavigationBar.vue";
import AppNotifications from "./components/AppNotifications.vue"; import AppNotifications from "./components/AppNotifications.vue";
import ThemeProvider from "./components/ThemeProvider.vue";
</script> </script>

View File

@@ -1,42 +0,0 @@
<template>
<button
type="button"
class="rounded-lg p-2.5 text-sm text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-4 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-700"
@click="toggle()"
>
<MoonIcon v-if="!dark" class="h-5 w-5" />
<SunIcon v-if="dark" class="h-5 w-5" />
</button>
</template>
<script setup>
import { ref, watch } from "vue";
import { SunIcon, MoonIcon } from "@heroicons/vue/solid";
const dark = ref(false);
if (
localStorage.getItem("color-theme") === "dark" ||
(!("color-theme" in localStorage) &&
window.matchMedia("(prefers-color-scheme: dark)").matches)
) {
dark.value = true;
}
watch(
dark,
(dark) => {
if (dark) {
document.documentElement.classList.add("dark");
} else {
document.documentElement.classList.remove("dark");
}
},
{ immediate: true }
);
const toggle = () => {
dark.value = !dark.value;
localStorage.setItem("color-theme", dark.value ? "dark" : "light");
};
</script>

View File

@@ -0,0 +1,16 @@
<template>
<button
type="button"
class="rounded-lg p-2.5 text-sm text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-4 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-700"
@click="toggle()"
>
<MoonIcon v-if="!isDark()" class="h-5 w-5" />
<SunIcon v-if="isDark()" class="h-5 w-5" />
</button>
</template>
<script setup>
import { inject } from "vue";
import { SunIcon, MoonIcon } from "@heroicons/vue/solid";
const { isDark, toggle } = inject("darkMode");
</script>

View File

@@ -17,7 +17,7 @@
> >
</router-link> </router-link>
<div class="flex md:order-2"> <div class="flex md:order-2">
<DarkMode /> <DarkModeSwitcher />
<DisclosureButton <DisclosureButton
class="ml-3 inline-flex items-center rounded-lg p-2 text-sm text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-600 md:hidden" class="ml-3 inline-flex items-center rounded-lg p-2 text-sm text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-600 md:hidden"
> >
@@ -70,7 +70,7 @@ import {
XIcon, XIcon,
PresentationChartLineIcon, PresentationChartLineIcon,
} from "@heroicons/vue/solid"; } from "@heroicons/vue/solid";
import DarkMode from "./DarkMode.vue"; import DarkModeSwitcher from "./DarkModeSwitcher.vue";
const route = useRoute(); const route = useRoute();
const navigation = computed(() => [ const navigation = computed(() => [

View File

@@ -0,0 +1,40 @@
<template>
<slot></slot>
</template>
<script setup>
import { ref, provide, watch } from "vue";
const isDark = ref(false);
const toggleDarkMode = () => {
isDark.value = !isDark.value;
localStorage.setItem("color-theme", isDark.value ? "dark" : "light");
};
if (
localStorage.getItem("color-theme") === "dark" ||
(!("color-theme" in localStorage) &&
window.matchMedia("(prefers-color-scheme: dark)").matches)
) {
isDark.value = true;
}
watch(
isDark,
(isDark) => {
if (isDark) {
document.documentElement.classList.add("dark");
} else {
document.documentElement.classList.remove("dark");
}
},
{ immediate: true }
);
provide("darkMode", {
isDark() {
return isDark.value;
},
toggle: toggleDarkMode,
});
</script>

View File

@@ -7,12 +7,14 @@
</template> </template>
<script setup> <script setup>
import { ref, watch } from "vue"; import { ref, watch, inject } from "vue";
import { use, graphic } from "echarts/core"; import { use, graphic } from "echarts/core";
import { CanvasRenderer } from "echarts/renderers"; import { CanvasRenderer } from "echarts/renderers";
import { LineChart } from "echarts/charts"; import { LineChart } from "echarts/charts";
import { TooltipComponent, GridComponent } from "echarts/components"; import { TooltipComponent, GridComponent } from "echarts/components";
import VChart from "vue-echarts"; import VChart from "vue-echarts";
import { dataColor } from "../utils/palette.js";
const { isDark } = inject("darkMode");
use([CanvasRenderer, LineChart, TooltipComponent, GridComponent]); use([CanvasRenderer, LineChart, TooltipComponent, GridComponent]);
@@ -55,21 +57,30 @@ const option = ref({
type: "line", type: "line",
symbol: "none", symbol: "none",
lineStyle: { lineStyle: {
color: "#5470c6",
width: 1, width: 1,
}, },
areaStyle: {
opacity: 1,
color: new graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "#5470c6" },
{ offset: 1, color: "#5572c8" },
]),
},
data: [], data: [],
}, },
], ],
}); });
watch(
isDark,
(isDark) => {
const theme = isDark ? "dark" : "light";
option.value.darkMode = isDark;
option.value.series[0].areaStyle = {
opacity: 0.9,
color: new graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: dataColor(0, false, theme) },
{ offset: 1, color: dataColor(0, true, theme) },
]),
};
option.value.series[0].lineStyle.color = dataColor(0, false, theme);
},
{ immediate: true }
);
watch( watch(
() => props.refresh, () => props.refresh,
async () => { async () => {

View File

@@ -8,12 +8,14 @@
</template> </template>
<script setup> <script setup>
import { ref, watch } from "vue"; import { ref, watch, inject } from "vue";
import { use } from "echarts/core"; import { use } from "echarts/core";
import { CanvasRenderer } from "echarts/renderers"; import { CanvasRenderer } from "echarts/renderers";
import { PieChart } from "echarts/charts"; import { PieChart } from "echarts/charts";
import { TooltipComponent, LegendComponent } from "echarts/components"; import { TooltipComponent, LegendComponent } from "echarts/components";
import VChart from "vue-echarts"; import VChart from "vue-echarts";
import { dataColor, dataColorGrey } from "../utils/palette.js";
const { isDark } = inject("darkMode");
use([CanvasRenderer, PieChart, TooltipComponent, LegendComponent]); use([CanvasRenderer, PieChart, TooltipComponent, LegendComponent]);
@@ -45,7 +47,6 @@ const option = ref({
itemGap: 5, itemGap: 5,
itemWidth: 14, itemWidth: 14,
itemHeight: 14, itemHeight: 14,
backgroundColor: "rgba(255, 255, 255, 0.4)",
textStyle: { fontSize: 10 }, textStyle: { fontSize: 10 },
formatter(name) { formatter(name) {
return name.split(": ")[0]; return name.split(": ")[0];
@@ -57,21 +58,29 @@ const option = ref({
label: { show: false }, label: { show: false },
center: ["50%", "40%"], center: ["50%", "40%"],
radius: "60%", radius: "60%",
itemStyle: {
color({ name, dataIndex }) {
if (name === "Others") {
return "#aaa";
}
return ["#5470c6", "#91cc75", "#fac858", "#ee6666", "#73c0de"][
dataIndex % 5
];
},
},
data: [], data: [],
}, },
], ],
}); });
watch(
isDark,
(isDark) => {
const theme = isDark ? "dark" : "light";
option.value.darkMode = isDark;
option.value.series.itemStyle = {
color({ name, dataIndex }) {
if (name === "Others") {
return dataColorGrey(0, false, theme);
}
return dataColor(dataIndex, false, theme);
},
};
option.value.legend.textStyle.color = isDark ? "#eee" : "#111";
},
{ immediate: true }
);
watch( watch(
() => props.refresh, () => props.refresh,
async () => { async () => {

View File

@@ -65,7 +65,7 @@
</template> </template>
<script setup> <script setup>
import { ref, watch } from "vue"; import { ref, watch, inject } from "vue";
import { notify } from "notiwind"; import { notify } from "notiwind";
import { Date as SugarDate } from "sugar-date"; import { Date as SugarDate } from "sugar-date";
import { use, graphic } from "echarts/core"; import { use, graphic } from "echarts/core";
@@ -75,6 +75,7 @@ import { TooltipComponent, GridComponent } from "echarts/components";
import VChart from "vue-echarts"; import VChart from "vue-echarts";
import { ResizeRow } from "vue-resizer"; import { ResizeRow } from "vue-resizer";
import { dataColor, dataColorGrey } from "../utils/palette.js"; import { dataColor, dataColorGrey } from "../utils/palette.js";
const { isDark } = inject("darkMode");
use([CanvasRenderer, LineChart, TooltipComponent, GridComponent]); use([CanvasRenderer, LineChart, TooltipComponent, GridComponent]);
@@ -139,6 +140,7 @@ const request = ref({
], ],
}, },
}); });
const fetchedData = ref({});
watch( watch(
request, request,
@@ -161,8 +163,16 @@ watch(
return; return;
} }
const data = await response.json(); const data = await response.json();
fetchedData.value = data;
},
{ immediate: true }
);
watch([fetchedData, isDark], ([data, isDark]) => {
const theme = isDark ? "dark" : "light";
// Graphic // Graphic
graph.value.darkMode = isDark;
graph.value.xAxis.data = data.t.slice(1, -1); graph.value.xAxis.data = data.t.slice(1, -1);
graph.value.series = data.rows.map((rows, idx) => { graph.value.series = data.rows.map((rows, idx) => {
const color = rows.some((name) => name === "Other") const color = rows.some((name) => name === "Other")
@@ -173,17 +183,17 @@ watch(
name: rows.join(" — "), name: rows.join(" — "),
symbol: "none", symbol: "none",
itemStyle: { itemStyle: {
color: color(idx), color: color(idx, false, theme),
}, },
lineStyle: { lineStyle: {
color: color(idx), color: color(idx, false, theme),
width: 1, width: 1,
}, },
areaStyle: { areaStyle: {
opacity: 0.95, opacity: 0.95,
color: new graphic.LinearGradient(0, 0, 0, 1, [ color: new graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: color(idx) }, { offset: 0, color: color(idx, false, theme) },
{ offset: 1, color: color(idx, true) }, { offset: 1, color: color(idx, true, theme) },
]), ]),
}, },
emphasis: { emphasis: {
@@ -203,14 +213,12 @@ watch(
: dataColor; : dataColor;
return { return {
dimensions: rows, dimensions: rows,
style: `background-color: ${color(idx)}`, style: `background-color: ${color(idx, false, theme)}`,
min: data.min[idx], min: data.min[idx],
max: data.max[idx], max: data.max[idx],
average: data.average[idx], average: data.average[idx],
}; };
}), }),
}; };
}, });
{ immediate: true }
);
</script> </script>