mirror of
https://github.com/amir20/dozzle.git
synced 2025-12-21 21:33:18 +01:00
* WIP vue3 * WIP vue3 * WIP vue3 * Migrates to vitejs * Fixes js tests and removes not needed modules * Fixes unmount * Updates to use css instead for space * Fixes tests and rebases one more time * Uses orgua * Fixes migrations bugs with oruga and fixes scroll * Fixes v-deep * Fixes icons to prod * Fixes icons to prod * Adds favicon back * Transitions some to composition api * Updates another component to comp api * Cleans defineProps * Updates log messages * Moves more to compose api * Cleans up styles and rewrites event source * Tries to fix DOMPurify * Removes postcss * WIP typescript * Improves importing * Converts all to ts * Converts main to ts * Makes changes for tsconfig * Moves more to ts * Adds typing to store * More typing * Updates to ts * Updates the rest to ts * Fixes computes * Fixes unmount * Adds cypress with custom base fixed * Fixes jest tests * Fixes golang tests * Adds gitignore for cypress * Removes int in favor of e2e with cypress * Tries to fix int tests again * Adds title * Updates e2e tests * Uses vue for isMobile * Removes app spec * Cleans up docker * Adds drop down for settings * Fixes bug with restart * Fixes scroll up bug * Adds tests for light mode
150 lines
3.5 KiB
Vue
150 lines
3.5 KiB
Vue
<template>
|
|
<div class="scroll-progress">
|
|
<svg width="100" height="100" viewBox="0 0 100 100" :class="{ indeterminate }">
|
|
<circle r="44" cx="50" cy="50" :style="{ '--progress': scrollProgress }" />
|
|
</svg>
|
|
<div class="is-overlay columns is-vcentered is-centered has-text-weight-light">
|
|
<template v-if="indeterminate">
|
|
<div class="column is-narrow is-paddingless is-size-2">∞</div>
|
|
</template>
|
|
<template v-else>
|
|
<span class="column is-narrow is-paddingless is-size-2">
|
|
{{ Math.ceil(scrollProgress * 100) }}
|
|
</span>
|
|
<span class="column is-narrow is-paddingless"> % </span>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { mapGetters } from "vuex";
|
|
import throttle from "lodash.throttle";
|
|
|
|
export default {
|
|
name: "ScrollProgress",
|
|
props: {
|
|
indeterminate: {
|
|
default: false,
|
|
type: Boolean,
|
|
},
|
|
autoHide: {
|
|
default: true,
|
|
type: Boolean,
|
|
},
|
|
},
|
|
data() {
|
|
return {
|
|
scrollProgress: 0,
|
|
animation: { cancel: () => {} },
|
|
parentElement: document,
|
|
};
|
|
},
|
|
created() {
|
|
this.onScrollThrottled = throttle(this.onScroll, 150);
|
|
},
|
|
mounted() {
|
|
this.attachEvents();
|
|
},
|
|
beforeUnmount() {
|
|
this.detachEvents();
|
|
},
|
|
watch: {
|
|
activeContainers() {
|
|
this.detachEvents();
|
|
this.attachEvents();
|
|
},
|
|
indeterminate() {
|
|
this.$nextTick(() => this.onScroll());
|
|
},
|
|
},
|
|
computed: {
|
|
...mapGetters(["activeContainers"]),
|
|
},
|
|
methods: {
|
|
attachEvents() {
|
|
this.parentElement = this.$el.closest("[data-scrolling]") || document;
|
|
this.parentElement.addEventListener("scroll", this.onScrollThrottled);
|
|
},
|
|
detachEvents() {
|
|
this.parentElement.removeEventListener("scroll", this.onScrollThrottled);
|
|
},
|
|
onScroll() {
|
|
const p = this.parentElement == document ? document.documentElement : this.parentElement;
|
|
this.scrollProgress = p.scrollTop / (p.scrollHeight - p.clientHeight);
|
|
this.animation.cancel();
|
|
if (this.autoHide) {
|
|
this.animation = this.$el.animate(
|
|
{ opacity: [1, 0] },
|
|
{
|
|
duration: 500,
|
|
delay: 2000,
|
|
fill: "both",
|
|
easing: "ease-out",
|
|
}
|
|
);
|
|
}
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
<style scoped lang="scss">
|
|
.scroll-progress {
|
|
display: inline-block;
|
|
position: relative;
|
|
pointer-events: none;
|
|
|
|
svg {
|
|
filter: drop-shadow(0px 1px 1px rgba(0, 0, 0, 0.2));
|
|
margin-top: 5px;
|
|
&.indeterminate {
|
|
animation: 2s linear infinite svg-animation;
|
|
|
|
circle {
|
|
animation: 1.4s ease-in-out infinite both circle-animation;
|
|
}
|
|
}
|
|
circle {
|
|
fill: var(--scheme-main-ter);
|
|
fill-opacity: 0.8;
|
|
transition: stroke-dashoffset 250ms ease-out;
|
|
transform: rotate(-90deg);
|
|
transform-origin: 50% 50%;
|
|
stroke: var(--primary-color);
|
|
stroke-dashoffset: calc(276.32px - var(--progress) * 276.32px);
|
|
stroke-dasharray: 276.32px 276.32px;
|
|
stroke-linecap: round;
|
|
stroke-width: 3;
|
|
will-change: stroke-dashoffset;
|
|
}
|
|
}
|
|
}
|
|
|
|
@keyframes svg-animation {
|
|
0% {
|
|
transform: rotateZ(0deg);
|
|
}
|
|
100% {
|
|
transform: rotateZ(360deg);
|
|
}
|
|
}
|
|
|
|
@keyframes circle-animation {
|
|
0%,
|
|
25% {
|
|
stroke-dashoffset: 275px;
|
|
transform: rotate(0);
|
|
}
|
|
50%,
|
|
75% {
|
|
stroke-dashoffset: 70px;
|
|
transform: rotate(45deg);
|
|
}
|
|
|
|
100% {
|
|
stroke-dashoffset: 275px;
|
|
transform: rotate(360deg);
|
|
}
|
|
}
|
|
</style>
|