feat(Layout): add loading spinner component and to layout
Reusable spinner component Apply spinner to layout to show a "loading state" between transitions, the spinner only shows if the transition is taking more than .2 secconds
This commit is contained in:
parent
d770f51948
commit
a1528a2ad8
3 changed files with 109 additions and 1 deletions
|
|
@ -102,3 +102,7 @@ li:not(:last-child) {
|
||||||
height: 100% !important;
|
height: 100% !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.d-none {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
|
||||||
63
src/components/Spinner.astro
Normal file
63
src/components/Spinner.astro
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
---
|
||||||
|
interface Props {
|
||||||
|
size?: number;
|
||||||
|
color?: string;
|
||||||
|
bgColor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {size = 200, color= "#cad3f5", bgColor} = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="spinner">
|
||||||
|
<div class="container">
|
||||||
|
<svg viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="">
|
||||||
|
<g id="SVGRepo_bgCarrier" stroke-width="0"></g>
|
||||||
|
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g>
|
||||||
|
<g id="SVGRepo_iconCarrier">
|
||||||
|
<path class="animation animation-normal" d="M4 24C4 35.0457 12.9543 44 24 44V44C35.0457 44 44 35.0457 44 24C44 12.9543 35.0457 4 24 4" stroke={color} stroke-width="4" stroke-linecap="round" stroke-linejoin="round"></path>
|
||||||
|
<path class="animation animation-reverse" d="M36 24C36 17.3726 30.6274 12 24 12C17.3726 12 12 17.3726 12 24C12 30.6274 17.3726 36 24 36V36" stroke={color} stroke-width="4" stroke-linecap="round" stroke-linejoin="round"></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<style define:vars={{size: `${size}px`, bgColor: bgColor ?? "var(--prj-bg)"}}>
|
||||||
|
.spinner {
|
||||||
|
background: var(--bgColor);
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 10px 0px -10px 0px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
width: var(--size);
|
||||||
|
}
|
||||||
|
|
||||||
|
.animation {
|
||||||
|
animation: rotate 1.5s linear infinite;
|
||||||
|
transform-box: fill-box;
|
||||||
|
transform-origin: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animation-normal {
|
||||||
|
animation-direction: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animation-reverse {
|
||||||
|
animation-direction: reverse;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rotate {
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -4,9 +4,11 @@ export interface Props {
|
||||||
title: string;
|
title: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { title } = Astro.props;
|
|
||||||
import '../assets/style/global.css';
|
import '../assets/style/global.css';
|
||||||
import Navbar from '../components/Navbar.astro';
|
import Navbar from '../components/Navbar.astro';
|
||||||
|
import Spinner from '../components/Spinner.astro';
|
||||||
|
|
||||||
|
const { title } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
|
|
@ -28,12 +30,51 @@ import Navbar from '../components/Navbar.astro';
|
||||||
<Navbar />
|
<Navbar />
|
||||||
</header>
|
</header>
|
||||||
<main transition:animate="fade">
|
<main transition:animate="fade">
|
||||||
|
<div id="layout-loading-spinner" class="d-none">
|
||||||
|
<Spinner />
|
||||||
|
</div>
|
||||||
<slot />
|
<slot />
|
||||||
</main>
|
</main>
|
||||||
<style>
|
<style>
|
||||||
header > :global(*) {
|
header > :global(*) {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body > main {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Position spinner in the center of the screen instead of the center of it's parent */
|
||||||
|
/* This is because the height of the main div can change */
|
||||||
|
/* but we still want the background to hide the content */
|
||||||
|
#layout-loading-spinner :global(.spinner) :global(svg) {
|
||||||
|
position: fixed;
|
||||||
|
width: 200px;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<script is:inline>
|
||||||
|
document.addEventListener('astro:before-preparation', (ev) => {
|
||||||
|
const originalLoader = ev.loader;
|
||||||
|
ev.loader = async function () {
|
||||||
|
const spinner = document.querySelector('#layout-loading-spinner');
|
||||||
|
|
||||||
|
// Only show the animation if page load is > than timeout seconds
|
||||||
|
const timeoutId = setTimeout(
|
||||||
|
() => spinner.classList.remove('d-none'),
|
||||||
|
200,
|
||||||
|
);
|
||||||
|
|
||||||
|
await originalLoader();
|
||||||
|
|
||||||
|
// cancel timeout if is not run yet
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
// spinner.classList.add('d-none');
|
||||||
|
};
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue