✨ Replace circular with linear loading indicator
This commit is contained in:
@ -4,7 +4,7 @@ import TitleBar from "./components/titlebar-element.vue";
|
||||
import BottomNavbar from "./components/bottom-navbar.vue";
|
||||
import DateSelector from "./components/date-selector.vue";
|
||||
import LoadingElement from "./components/loading-element.vue";
|
||||
import { loading, theme } from "./store";
|
||||
import { loading, loadingProgress, theme } from "./store";
|
||||
import { computed, ref } from "vue";
|
||||
|
||||
const autoThemes = { true: "dark", false: "light" };
|
||||
@ -31,10 +31,10 @@ const isDataView = computed(() => {
|
||||
:class="theme == 'auto' ? `theme-${autoTheme}` : `theme-${theme}`"
|
||||
>
|
||||
<TitleBar />
|
||||
<DateSelector v-show="isDataView" />
|
||||
<LoadingElement :active="loading" :progress="loadingProgress" />
|
||||
<div class="center">
|
||||
<main>
|
||||
<LoadingElement :active="loading" v-show="isDataView" />
|
||||
<DateSelector v-show="isDataView" />
|
||||
<RouterView />
|
||||
</main>
|
||||
</div>
|
||||
|
@ -16,6 +16,7 @@
|
||||
--bottomnav-active-color: #7ca74b;
|
||||
--bottomnav-shadow: 5px 7px 19px 0px rgba(0, 0, 0, 0.25);
|
||||
--loader-color: #7ca74b;
|
||||
--loader-error-color: #a54a4a;
|
||||
--substitution-background-change: #095079;
|
||||
--substitution-background-cancellation: #7d2b2d;
|
||||
--substitution-background-addition: #375c1d;
|
||||
@ -42,7 +43,8 @@
|
||||
--bottomnav-icon-active-color: #456c47;
|
||||
--bottomnav-active-color: #99b677;
|
||||
--bottomnav-shadow: 5px 7px 19px 0px rgba(0, 0, 0, 0.25);
|
||||
--loader-color: #7ca74b;
|
||||
--loader-color: #8bab7c;
|
||||
--loader-error-color: #db5a5a;
|
||||
--substitution-background-change: #9bb7c7;
|
||||
--substitution-background-cancellation: #e4a7a9;
|
||||
--substitution-background-addition: #b0d396;
|
||||
|
@ -26,7 +26,11 @@ const loadedDates = ref([
|
||||
|
||||
// Load left or right date on slide change
|
||||
function slideChange(swiper) {
|
||||
selectedDate.value = loadedDates.value[swiper.activeIndex];
|
||||
// Only trigger data refresh if date is different
|
||||
const newSelectedDate = loadedDates.value[swiper.activeIndex];
|
||||
if (selectedDate.value.getTime() != newSelectedDate.getTime())
|
||||
selectedDate.value = newSelectedDate;
|
||||
|
||||
const activeSlide = swiper.activeIndex;
|
||||
if (activeSlide == loadedDates.value.length - 1) {
|
||||
const lastDate = loadedDates.value[loadedDates.value.length - 1];
|
||||
|
@ -1,54 +1,73 @@
|
||||
<script setup>
|
||||
defineProps({
|
||||
import { ref, watch } from "vue";
|
||||
const props = defineProps({
|
||||
active: {
|
||||
required: true,
|
||||
type: Boolean,
|
||||
},
|
||||
progress: {
|
||||
default: 0,
|
||||
type: Number,
|
||||
},
|
||||
error: {
|
||||
default: false,
|
||||
type: Boolean,
|
||||
},
|
||||
});
|
||||
|
||||
const computedProgress = ref(0);
|
||||
const visible = ref(true);
|
||||
var hideTimeout;
|
||||
watch(
|
||||
() => props.progress,
|
||||
() => {
|
||||
if (visible.value) computedProgress.value = props.progress;
|
||||
}
|
||||
);
|
||||
watch(
|
||||
() => props.active,
|
||||
() => {
|
||||
if (hideTimeout) {
|
||||
clearTimeout(hideTimeout);
|
||||
hideTimeout = undefined;
|
||||
}
|
||||
if (props.active) {
|
||||
visible.value = true;
|
||||
computedProgress.value = props.progress;
|
||||
} else {
|
||||
hideTimeout = setTimeout(() => {
|
||||
if (!props.active) {
|
||||
visible.value = false;
|
||||
computedProgress.value = 0;
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container" :class="active ? 'active' : ''">
|
||||
<span class="loader"></span>
|
||||
</div>
|
||||
<div
|
||||
class="bar"
|
||||
:class="(active ? 'active' : '') + (error ? ' error' : '')"
|
||||
:style="`width: ${computedProgress * 100}%;`"
|
||||
></div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
overflow: hidden;
|
||||
background-color: var(--bg-color);
|
||||
height: 0px;
|
||||
margin: 0px;
|
||||
.bar {
|
||||
width: 0%;
|
||||
opacity: 0;
|
||||
transition: all 0.8s;
|
||||
transition-delay: 0.2s;
|
||||
background-color: var(--loader-color);
|
||||
height: 5px;
|
||||
transition: 0.5s;
|
||||
transition-property: width opacity background-color;
|
||||
}
|
||||
|
||||
.container.active {
|
||||
height: 100px;
|
||||
.bar.active {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.loader {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border: 5px solid;
|
||||
border-color: var(--loader-color) transparent;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
animation: rotation 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes rotation {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
.bar.error {
|
||||
background-color: var(--loader-error-color);
|
||||
}
|
||||
</style>
|
||||
|
10
src/store.js
10
src/store.js
@ -5,6 +5,7 @@ import i18n from "./main";
|
||||
|
||||
export const lastRoute = ref();
|
||||
export const loading = ref(false);
|
||||
export const loadingProgress = ref(0);
|
||||
|
||||
export const classFilter = ref(localStorage.getItem("classFilter") || "none");
|
||||
export const timetableGroups = ref(
|
||||
@ -45,9 +46,12 @@ if (selectedDay.value == -1)
|
||||
|
||||
// Load new data if date changes
|
||||
watch(selectedDate, async () => {
|
||||
loadingProgress.value = 0.1;
|
||||
loading.value = true;
|
||||
await fetchSubstitutions();
|
||||
loadingProgress.value = 1 / 2;
|
||||
await fetchHistory();
|
||||
loadingProgress.value = 1;
|
||||
loading.value = false;
|
||||
});
|
||||
|
||||
@ -124,16 +128,22 @@ export const possibleTimetableGroups = computed(() => {
|
||||
const baseUrl = import.meta.env.VITE_API_ENDPOINT || "/api";
|
||||
|
||||
export async function fetchData() {
|
||||
loadingProgress.value = 0.1;
|
||||
loading.value = true;
|
||||
|
||||
const checkResponse = await fetch(`${baseUrl}/check`);
|
||||
if (checkResponse.status != 200) router.push("/login");
|
||||
loadingProgress.value = 1 / 5;
|
||||
|
||||
await fetchClassList();
|
||||
loadingProgress.value = 2 / 5;
|
||||
await fetchTimetable();
|
||||
loadingProgress.value = 3 / 5;
|
||||
await fetchSubstitutions();
|
||||
loadingProgress.value = 4 / 5;
|
||||
await fetchHistory();
|
||||
|
||||
loadingProgress.value = 1;
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user