mirror of
https://github.com/akvorado/akvorado.git
synced 2025-12-12 06:24:10 +01:00
console: add a theme provider to not rely on CSS only
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
</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(() => [
|
||||||
|
|||||||
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>
|
</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 () => {
|
||||||
|
|||||||
@@ -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 () => {
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user