Skip to content

Commit

Permalink
feat(UI): Theme toggle (#988)
Browse files Browse the repository at this point in the history
This PR introduces a theme toggle that overwrites user preferences.
Depends on #926 that introduces class based theme management.

# UI preview


https://github.com/user-attachments/assets/5a220d2b-d201-4787-98a2-db0b5c9917f7
  • Loading branch information
rouk1 authored Dec 20, 2024
1 parent e78610c commit d96e41d
Show file tree
Hide file tree
Showing 24 changed files with 557 additions and 142 deletions.
107 changes: 48 additions & 59 deletions skore-ui/src/SkoreUi.vue
Original file line number Diff line number Diff line change
@@ -1,82 +1,71 @@
<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref } from "vue";
import { RouterView, useRoute, useRouter } from "vue-router";
import AppToolbar from "@/components/AppToolbar.vue";
import LoadingBars from "@/components/LoadingBars.vue";
import ModalDialog from "@/components/ModalDialog.vue";
import NavigationButton from "@/components/NavigationButton.vue";
import ToastNotificationArea from "@/components/ToastNotificationArea.vue";
import { isUserInDarkMode } from "./services/utils";
import { useThemesStore } from "@/stores/themes";
import AppToolbar from "@/views/AppToolbar.vue";
const route = useRoute();
const router = useRouter();
const theme = ref(isUserInDarkMode() ? "skore-dark" : "skore-light");
function switchTheme(m: MediaQueryListEvent) {
theme.value = m.matches ? "skore-dark" : "skore-light";
}
const preferredColorScheme = window.matchMedia("(prefers-color-scheme: dark)");
onMounted(() => {
preferredColorScheme.addEventListener("change", switchTheme);
});
onBeforeUnmount(() => {
preferredColorScheme.removeEventListener("change", switchTheme);
});
const themesStore = useThemesStore();
</script>

<template>
<div class="skore" :class="[theme]">
<AppToolbar>
<NavigationButton
v-for="(r, i) in router.getRoutes()"
:key="i"
:icon="`${r.meta['icon']}`"
:is-selected="r.name == route.name"
:to="r.path"
/>
</AppToolbar>
<RouterView v-slot="{ Component, route }">
<template v-if="Component">
<Transition name="fade" mode="out-in">
<Suspense timeout="0">
<template #default>
<component :is="Component" :key="route.path" />
</template>
<template #fallback>
<div class="loader">
<LoadingBars />
</div>
</template>
</Suspense>
</Transition>
</template>
</RouterView>
<div class="skore" :class="themesStore.currentThemeClass">
<div class="app-shell">
<AppToolbar>
<NavigationButton
v-for="(r, i) in router.getRoutes()"
:key="i"
:icon="`${r.meta['icon']}`"
:is-selected="r.name == route.name"
:to="r.path"
/>
</AppToolbar>
<RouterView v-slot="{ Component, route }">
<template v-if="Component">
<Transition name="fade" mode="out-in">
<Suspense timeout="0">
<template #default>
<component :is="Component" :key="route.path" />
</template>
<template #fallback>
<div class="loader">
<LoadingBars />
</div>
</template>
</Suspense>
</Transition>
</template>
</RouterView>
</div>
<ToastNotificationArea />
<ModalDialog />
</div>
<ToastNotificationArea />
<ModalDialog />
</template>

<style scoped>
.skore {
display: flex;
flex-direction: row;
background-color: var(--color-background-primary);
& .app-shell {
display: flex;
flex-direction: row;
background-color: var(--color-background-primary);
main {
width: calc(100dvw - var(--width-toolbar));
height: 100vh;
}
main {
width: calc(100dvw - var(--width-toolbar));
height: 100vh;
}
.loader {
display: flex;
height: 100dvh;
flex: 1;
align-items: center;
justify-content: center;
.loader {
display: flex;
height: 100dvh;
flex: 1;
align-items: center;
justify-content: center;
}
}
}
</style>
20 changes: 5 additions & 15 deletions skore-ui/src/StandaloneWidget.vue
Original file line number Diff line number Diff line change
@@ -1,32 +1,22 @@
<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref } from "vue";
import { onBeforeUnmount } from "vue";
import MediaWidgetSelector from "@/components/MediaWidgetSelector.vue";
import { deserializeProjectItemDto } from "@/models";
import { isUserInDarkMode } from "@/services/utils";
import { useThemesStore } from "@/stores/themes";
const themesStore = useThemesStore();
const dataId = `skore-item-data-${window.skoreWidgetId}`;
const itemData = JSON.parse(document.getElementById(dataId)?.innerText || "{}");
const item = deserializeProjectItemDto(itemData);
const theme = ref(isUserInDarkMode() ? "skore-dark" : "skore-light");
function switchTheme(m: MediaQueryListEvent) {
theme.value = m.matches ? "skore-dark" : "skore-light";
}
const preferredColorScheme = window.matchMedia("(prefers-color-scheme: dark)");
onMounted(() => {
preferredColorScheme.addEventListener("change", switchTheme);
});
onBeforeUnmount(() => {
preferredColorScheme.removeEventListener("change", switchTheme);
themesStore.dispose();
});
</script>

<template>
<div class="widget" :class="theme">
<div class="widget" :class="themesStore.currentThemeClass">
<MediaWidgetSelector :item="item" />
</div>
</template>
Expand Down
Binary file modified skore-ui/src/assets/fonts/icomoon.eot
Binary file not shown.
2 changes: 2 additions & 0 deletions skore-ui/src/assets/fonts/icomoon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified skore-ui/src/assets/fonts/icomoon.ttf
Binary file not shown.
Binary file modified skore-ui/src/assets/fonts/icomoon.woff
Binary file not shown.
9 changes: 9 additions & 0 deletions skore-ui/src/assets/styles/_animations.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,12 @@
.fade-leave-to {
opacity: 0;
}

.skore-ui {
& * {
transition:
background-color var(--animation-duration) var(--animation-easing),
border-color var(--animation-duration) var(--animation-easing),
color var(--animation-duration) var(--animation-easing);
}
}
Loading

0 comments on commit d96e41d

Please sign in to comment.