mirror of
https://github.com/akvorado/akvorado.git
synced 2025-12-11 22:14:02 +01:00
console: add a theme provider to not rely on CSS only
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
<template>
|
||||
<AppNotifications />
|
||||
<div class="flex max-h-screen flex-col">
|
||||
<NavigationBar class="flex-none" />
|
||||
<main class="relative flex grow overflow-y-auto">
|
||||
<router-view />
|
||||
</main>
|
||||
</div>
|
||||
<ThemeProvider>
|
||||
<AppNotifications />
|
||||
<div class="flex max-h-screen flex-col">
|
||||
<NavigationBar class="flex-none" />
|
||||
<main class="relative flex grow overflow-y-auto">
|
||||
<router-view />
|
||||
</main>
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -13,4 +15,5 @@ import "./tailwind.css";
|
||||
|
||||
import NavigationBar from "./components/NavigationBar.vue";
|
||||
import AppNotifications from "./components/AppNotifications.vue";
|
||||
import ThemeProvider from "./components/ThemeProvider.vue";
|
||||
</script>
|
||||
|
||||
@@ -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>
|
||||
16
console/frontend/src/components/DarkModeSwitcher.vue
Normal file
16
console/frontend/src/components/DarkModeSwitcher.vue
Normal 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>
|
||||
@@ -17,7 +17,7 @@
|
||||
>
|
||||
</router-link>
|
||||
<div class="flex md:order-2">
|
||||
<DarkMode />
|
||||
<DarkModeSwitcher />
|
||||
<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"
|
||||
>
|
||||
@@ -70,7 +70,7 @@ import {
|
||||
XIcon,
|
||||
PresentationChartLineIcon,
|
||||
} from "@heroicons/vue/solid";
|
||||
import DarkMode from "./DarkMode.vue";
|
||||
import DarkModeSwitcher from "./DarkModeSwitcher.vue";
|
||||
|
||||
const route = useRoute();
|
||||
const navigation = computed(() => [
|
||||
|
||||
40
console/frontend/src/components/ThemeProvider.vue
Normal file
40
console/frontend/src/components/ThemeProvider.vue
Normal 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>
|
||||
@@ -7,12 +7,14 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from "vue";
|
||||
import { ref, watch, inject } from "vue";
|
||||
import { use, graphic } from "echarts/core";
|
||||
import { CanvasRenderer } from "echarts/renderers";
|
||||
import { LineChart } from "echarts/charts";
|
||||
import { TooltipComponent, GridComponent } from "echarts/components";
|
||||
import VChart from "vue-echarts";
|
||||
import { dataColor } from "../utils/palette.js";
|
||||
const { isDark } = inject("darkMode");
|
||||
|
||||
use([CanvasRenderer, LineChart, TooltipComponent, GridComponent]);
|
||||
|
||||
@@ -55,21 +57,30 @@ const option = ref({
|
||||
type: "line",
|
||||
symbol: "none",
|
||||
lineStyle: {
|
||||
color: "#5470c6",
|
||||
width: 1,
|
||||
},
|
||||
areaStyle: {
|
||||
opacity: 1,
|
||||
color: new graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: "#5470c6" },
|
||||
{ offset: 1, color: "#5572c8" },
|
||||
]),
|
||||
},
|
||||
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(
|
||||
() => props.refresh,
|
||||
async () => {
|
||||
|
||||
@@ -8,12 +8,14 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from "vue";
|
||||
import { ref, watch, inject } from "vue";
|
||||
import { use } from "echarts/core";
|
||||
import { CanvasRenderer } from "echarts/renderers";
|
||||
import { PieChart } from "echarts/charts";
|
||||
import { TooltipComponent, LegendComponent } from "echarts/components";
|
||||
import VChart from "vue-echarts";
|
||||
import { dataColor, dataColorGrey } from "../utils/palette.js";
|
||||
const { isDark } = inject("darkMode");
|
||||
|
||||
use([CanvasRenderer, PieChart, TooltipComponent, LegendComponent]);
|
||||
|
||||
@@ -45,7 +47,6 @@ const option = ref({
|
||||
itemGap: 5,
|
||||
itemWidth: 14,
|
||||
itemHeight: 14,
|
||||
backgroundColor: "rgba(255, 255, 255, 0.4)",
|
||||
textStyle: { fontSize: 10 },
|
||||
formatter(name) {
|
||||
return name.split(": ")[0];
|
||||
@@ -57,21 +58,29 @@ const option = ref({
|
||||
label: { show: false },
|
||||
center: ["50%", "40%"],
|
||||
radius: "60%",
|
||||
itemStyle: {
|
||||
color({ name, dataIndex }) {
|
||||
if (name === "Others") {
|
||||
return "#aaa";
|
||||
}
|
||||
return ["#5470c6", "#91cc75", "#fac858", "#ee6666", "#73c0de"][
|
||||
dataIndex % 5
|
||||
];
|
||||
},
|
||||
},
|
||||
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(
|
||||
() => props.refresh,
|
||||
async () => {
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from "vue";
|
||||
import { ref, watch, inject } from "vue";
|
||||
import { notify } from "notiwind";
|
||||
import { Date as SugarDate } from "sugar-date";
|
||||
import { use, graphic } from "echarts/core";
|
||||
@@ -75,6 +75,7 @@ import { TooltipComponent, GridComponent } from "echarts/components";
|
||||
import VChart from "vue-echarts";
|
||||
import { ResizeRow } from "vue-resizer";
|
||||
import { dataColor, dataColorGrey } from "../utils/palette.js";
|
||||
const { isDark } = inject("darkMode");
|
||||
|
||||
use([CanvasRenderer, LineChart, TooltipComponent, GridComponent]);
|
||||
|
||||
@@ -139,6 +140,7 @@ const request = ref({
|
||||
],
|
||||
},
|
||||
});
|
||||
const fetchedData = ref({});
|
||||
|
||||
watch(
|
||||
request,
|
||||
@@ -161,56 +163,62 @@ watch(
|
||||
return;
|
||||
}
|
||||
const data = await response.json();
|
||||
fetchedData.value = data;
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// Graphic
|
||||
graph.value.xAxis.data = data.t.slice(1, -1);
|
||||
graph.value.series = data.rows.map((rows, idx) => {
|
||||
watch([fetchedData, isDark], ([data, isDark]) => {
|
||||
const theme = isDark ? "dark" : "light";
|
||||
|
||||
// Graphic
|
||||
graph.value.darkMode = isDark;
|
||||
graph.value.xAxis.data = data.t.slice(1, -1);
|
||||
graph.value.series = data.rows.map((rows, idx) => {
|
||||
const color = rows.some((name) => name === "Other")
|
||||
? dataColorGrey
|
||||
: dataColor;
|
||||
return {
|
||||
type: "line",
|
||||
name: rows.join(" — "),
|
||||
symbol: "none",
|
||||
itemStyle: {
|
||||
color: color(idx, false, theme),
|
||||
},
|
||||
lineStyle: {
|
||||
color: color(idx, false, theme),
|
||||
width: 1,
|
||||
},
|
||||
areaStyle: {
|
||||
opacity: 0.95,
|
||||
color: new graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: color(idx, false, theme) },
|
||||
{ offset: 1, color: color(idx, true, theme) },
|
||||
]),
|
||||
},
|
||||
emphasis: {
|
||||
focus: "series",
|
||||
},
|
||||
stack: "all",
|
||||
data: data.t.map((t, idx2) => [t, data.points[idx][idx2]]).slice(1, -1),
|
||||
};
|
||||
});
|
||||
|
||||
// Table
|
||||
table.value = {
|
||||
columns: request.value.dimensions,
|
||||
rows: data.rows.map((rows, idx) => {
|
||||
const color = rows.some((name) => name === "Other")
|
||||
? dataColorGrey
|
||||
: dataColor;
|
||||
return {
|
||||
type: "line",
|
||||
name: rows.join(" — "),
|
||||
symbol: "none",
|
||||
itemStyle: {
|
||||
color: color(idx),
|
||||
},
|
||||
lineStyle: {
|
||||
color: color(idx),
|
||||
width: 1,
|
||||
},
|
||||
areaStyle: {
|
||||
opacity: 0.95,
|
||||
color: new graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: color(idx) },
|
||||
{ offset: 1, color: color(idx, true) },
|
||||
]),
|
||||
},
|
||||
emphasis: {
|
||||
focus: "series",
|
||||
},
|
||||
stack: "all",
|
||||
data: data.t.map((t, idx2) => [t, data.points[idx][idx2]]).slice(1, -1),
|
||||
dimensions: rows,
|
||||
style: `background-color: ${color(idx, false, theme)}`,
|
||||
min: data.min[idx],
|
||||
max: data.max[idx],
|
||||
average: data.average[idx],
|
||||
};
|
||||
});
|
||||
|
||||
// Table
|
||||
table.value = {
|
||||
columns: request.value.dimensions,
|
||||
rows: data.rows.map((rows, idx) => {
|
||||
const color = rows.some((name) => name === "Other")
|
||||
? dataColorGrey
|
||||
: dataColor;
|
||||
return {
|
||||
dimensions: rows,
|
||||
style: `background-color: ${color(idx)}`,
|
||||
min: data.min[idx],
|
||||
max: data.max[idx],
|
||||
average: data.average[idx],
|
||||
};
|
||||
}),
|
||||
};
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
}),
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user