add PromiseResolver.vue, TimeFormatter.vue

main
adb-sh 1 year ago
parent b91950d9ab
commit e4eb578d51

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width='200px' height='200px' xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" class="uil-ring-alt">
<rect x="0" y="0" width="100" height="100" fill="none" class="bk"></rect>
<circle cx="50" cy="50" r="40" stroke="#d9d9d9" fill="none" stroke-width="10" stroke-linecap="round"></circle>
<circle cx="50" cy="50" r="40" stroke="#009eff" fill="none" stroke-width="10" stroke-linecap="round">
<animate attributeName="stroke-dashoffset" dur="2s" repeatCount="indefinite" from="502" to="0"></animate>
<animate attributeName="stroke-dasharray" dur="2s" repeatCount="indefinite" values="125.5 125.5;1 250;125.5 125.5"></animate>
</circle>
</svg>

After

Width:  |  Height:  |  Size: 751 B

@ -0,0 +1,114 @@
<template>
<div
class="position-relative PromiseResolver"
:class="{ loading: loading && showThrobber }"
>
<slot
v-if="!error && (!loading || (overlay && data !== null))"
:data="getter(data)"
:update="update"
/>
<slot
v-if="error"
name="error"
:error="error"
:update="update"
>
<div class="alert alert-danger">
{{ error.message || 'Oops! Something went wrong :/' }}
</div>
</slot>
<transition name="fade">
<slot v-if="loading && showThrobber" name="loading">
<div class="overlay">
<div class="wrapper">
<div class="background bg-darkmode-dark bg-light" />
<ThrobberLoading class="position-relative" />
</div>
</div>
</slot>
</transition>
</div>
</template>
<script setup lang="ts">
import { ref, defineProps, watch } from "vue";
import ThrobberLoading from "@/components/ThrobberLoading.vue";
const props = defineProps({
promise: [Promise, Object],
getter: {
type: Function,
default: (data: unknown) => data,
},
overlay: {
type: Boolean,
default: true,
},
throbber: {
type: Boolean,
default: true,
}
});
const loading = ref(true);
const showThrobber = ref(true);
const error = ref(null);
const data = ref(null);
const update = async (promise: Promise | unknown) => {
loading.value = true;
if (props.throbber) setTimeout(() => {
if (loading.value) showThrobber.value = true;
}, 500);
try {
data.value = await (promise.isPromiseList
? Promise.all(promise.promises)
: promise);
error.value = null;
} catch (e) {
error.value = e;
} finally {
loading.value = false;
showThrobber.value = false;
}
};
update(props.promise);
watch(() => props.promise, (to) => {
update(to);
});
</script>
<style scoped lang="scss">
.PromiseResolver {
&.loading {
min-height: 5rem;
}
.overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
min-height: 5rem;
.wrapper {
position: sticky;
display: flex;
justify-content: center;
align-items: center;
top: 0;
height: 100%;
max-height: 100vh;
.background {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
opacity: 0.9;
}
}
}
}
</style>

@ -0,0 +1,19 @@
<template>
<div class="Throbber">
<img src="../assets/throbber.svg" alt="throbber" />
<div class="info"><slot>loading</slot></div>
</div>
</template>
<style scoped lang="scss">
.Throbber {
padding: 0.5rem;
display: flex;
flex-flow: column;
align-items: center;
img {
height: 3rem;
width: 3rem;
}
}
</style>

@ -0,0 +1,29 @@
<script setup lang="ts">
import { defineProps, watch, ref } from "vue";
const props = defineProps({
seconds: Number,
});
const getHmsFromSeconds = (d: number) => ({
hours: Math.floor(d / 3600),
minutes: Math.floor(d % 3600 / 60),
seconds: Math.floor(d % 3600 % 60),
});
const getLeadingZero = (a: number, digits: number) =>
('0'.repeat(digits) + a.toString())
.slice(- digits);
const hms = ref(getHmsFromSeconds(props.seconds as number));
watch(() => props.seconds, (to) => {
hms.value = getHmsFromSeconds(to as number);
});
</script>
<template>
<span v-if="hms.hours" class="hours">{{ getLeadingZero(hms.hours, 2) }}:</span>
<span class="minutes">{{ getLeadingZero(hms.minutes, 2) }}:</span>
<span class="seconds">{{ getLeadingZero(hms.seconds, 2) }}</span>
</template>
Loading…
Cancel
Save