Merge pull request #3 from pemeraldy/feature/scroll-accuracy

Feature/scroll accuracy
This commit is contained in:
Peter Olu 2022-04-25 19:55:12 +01:00 committed by GitHub
commit db9d6ddfe5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 179 additions and 64 deletions

View File

@ -13,7 +13,16 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License. -->
<template>
<div class="scroll-snap-container">
<div ref="scrollWrapRef" class="scroll-snap-container">
<div v-if="items.length > 1" class="scroll-handler__wrapper">
<div
@click="scrollToGraph(item.i)"
v-for="item in items"
:key="item.i"
:class="[currentItem === `item${item.i}` ? 'active' : '']"
class="full-scroll-to"
></div>
</div>
<div :id="'item' + item.i" class="item" v-for="item in items" :key="item.i">
<slot v-if="items.length">
<component :is="item.type" :data="item" />
@ -22,11 +31,10 @@ limitations under the License. -->
</div>
</template>
<script lang="ts">
import { ref, watch, reactive, defineComponent } from "vue";
import { ref, watch, onMounted, defineComponent } from "vue";
import { useI18n } from "vue-i18n";
import { useRoute } from "vue-router";
import { useDashboardStore } from "@/store/modules/dashboard";
// import { useAppStoreWithOut } from "@/store/modules/app";
import Configuration from "../views/dashboard/configuration";
import controls from "../views/dashboard/controls/index";
@ -41,51 +49,21 @@ export default defineComponent({
components: { ...Configuration, ...controls },
setup() {
const dashboardStore = useDashboardStore();
const tobewatched = reactive(dashboardStore);
// const appStore = useAppStoreWithOut();
const { t } = useI18n();
const p = useRoute().params;
// const layoutKey = ref<string>(`${p.layerId}_${p.entity}_${p.name}`);
// setTemplate();
const currentItem = ref("");
const scrollWrapRef = ref<any>(null);
watch(
() => tobewatched.layout,
() => dashboardStore.layout,
() => {
setTimeout(() => {
observeItems();
}, 500);
}
);
// async function setTemplate() {
// await dashboardStore.setDashboards();
// if (!p.entity) {
// if (!dashboardStore.currentDashboard) {
// return;
// }
// const { layer, entity, name } = dashboardStore.currentDashboard;
// layoutKey.value = `${layer}_${entity}_${name}`;
// }
// const c: { configuration: string; id: string } = JSON.parse(
// sessionStorage.getItem(layoutKey.value) || "{}"
// );
// const layout: any = c.configuration || {};
// dashboardStore.setLayout(layout.children || []);
// appStore.setPageTitle(layout.name);
// // observeItems();
// if (p.entity) {
// dashboardStore.setCurrentDashboard({
// layer: p.layerId,
// entity: p.entity,
// name: p.name,
// id: c.id,
// isRoot: layout.isRoot,
// });
// }
// }
function scrollToGraph(e: any) {
document?.getElementById(`item${e}`)?.scrollIntoView();
}
function observeItems() {
const observer = new IntersectionObserver((entries) => {
entries.forEach((element) => {
@ -97,11 +75,30 @@ export default defineComponent({
document.querySelectorAll(".item").forEach((element) => {
observer.observe(element);
});
}
function initScroller() {
scrollWrapRef?.value?.addEventListener("scroll", (e: Event) => {
const isBottom =
scrollWrapRef?.value?.offsetHeight +
scrollWrapRef?.value?.scrollTop +
40 >
scrollWrapRef?.value?.scrollHeight;
if (isBottom) {
scrollWrapRef?.value.scroll(0, 0);
}
});
}
onMounted(() => {
observeItems();
initScroller();
});
return {
t,
dashboardStore,
currentItem,
scrollToGraph,
scrollWrapRef,
};
},
});
@ -119,6 +116,31 @@ export default defineComponent({
scroll-snap-type: y mandatory;
scroll-snap-type: mandatory;
scroll-behavior: smooth;
.scroll-handler__wrapper {
z-index: 20;
position: fixed;
display: flex;
flex-direction: column;
right: 0;
// top: 50%;
transform: translateY(60%);
height: auto;
width: 20px;
.full-scroll-to {
opacity: 0.5;
width: 10px;
height: 10px;
margin: 5px 0;
border-radius: 50%;
cursor: pointer;
background: #4f4f4f;
}
.full-scroll-to.active {
opacity: 1;
padding: 6px;
background: #252a2f;
}
}
}
.scroll-snap-container::-webkit-scrollbar {
display: none;

View File

@ -0,0 +1,52 @@
// Generated by 'unplugin-auto-import'
// We suggest you to commit this file into source control
declare global {
const computed: typeof import('vue')['computed']
const createApp: typeof import('vue')['createApp']
const customRef: typeof import('vue')['customRef']
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
const defineComponent: typeof import('vue')['defineComponent']
const effectScope: typeof import('vue')['effectScope']
const EffectScope: typeof import('vue')['EffectScope']
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const getCurrentScope: typeof import('vue')['getCurrentScope']
const h: typeof import('vue')['h']
const inject: typeof import('vue')['inject']
const isReadonly: typeof import('vue')['isReadonly']
const isRef: typeof import('vue')['isRef']
const markRaw: typeof import('vue')['markRaw']
const nextTick: typeof import('vue')['nextTick']
const onActivated: typeof import('vue')['onActivated']
const onBeforeMount: typeof import('vue')['onBeforeMount']
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
const onDeactivated: typeof import('vue')['onDeactivated']
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
const onMounted: typeof import('vue')['onMounted']
const onRenderTracked: typeof import('vue')['onRenderTracked']
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
const onScopeDispose: typeof import('vue')['onScopeDispose']
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
const onUnmounted: typeof import('vue')['onUnmounted']
const onUpdated: typeof import('vue')['onUpdated']
const provide: typeof import('vue')['provide']
const reactive: typeof import('vue')['reactive']
const readonly: typeof import('vue')['readonly']
const ref: typeof import('vue')['ref']
const resolveComponent: typeof import('vue')['resolveComponent']
const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: typeof import('vue')['shallowRef']
const toRaw: typeof import('vue')['toRaw']
const toRef: typeof import('vue')['toRef']
const toRefs: typeof import('vue')['toRefs']
const triggerRef: typeof import('vue')['triggerRef']
const unref: typeof import('vue')['unref']
const useAttrs: typeof import('vue')['useAttrs']
const useCssModule: typeof import('vue')['useCssModule']
const useCssVars: typeof import('vue')['useCssVars']
const useSlots: typeof import('vue')['useSlots']
const watch: typeof import('vue')['watch']
const watchEffect: typeof import('vue')['watchEffect']
}
export {}

View File

@ -21,6 +21,7 @@ declare module '@vue/runtime-core' {
ElMenuItemGroup: typeof import('element-plus/es')['ElMenuItemGroup']
ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElPopconfirm: typeof import('element-plus/es')['ElPopconfirm']
ElPopover: typeof import('element-plus/es')['ElPopover']
ElProgress: typeof import('element-plus/es')['ElProgress']
ElRadio: typeof import('element-plus/es')['ElRadio']

View File

@ -98,4 +98,11 @@ export default defineComponent({
.ds-main {
overflow: auto;
}
.ds-main::-webkit-scrollbar {
display: none;
}
.ds-main{
-ms-overflow-style: none;
scrollbar-width: none;
}
</style>

View File

@ -87,7 +87,11 @@ limitations under the License. -->
:is-resizable="dashboardStore.editMode"
@layout-updated="layoutUpdatedEvent"
>
<div class="scroll-snap-container" v-if="dashboardStore.fullView">
<div
ref="tabObserveContainer"
class="scroll-snap-container"
v-if="dashboardStore.fullView"
>
<div
v-if="dashboardStore.currentTabItems.length > 1"
class="scroll-handler__wrapper"
@ -96,7 +100,7 @@ limitations under the License. -->
@click="scrollToGraph(item.i)"
v-for="item in dashboardStore.currentTabItems"
:key="item.i"
:class="[currentItem === `tabitem${item.i}` ? 'active': '']"
:class="[currentItem === `tabitem${item.i}` ? 'active' : '']"
class="scroll-to"
></div>
</div>
@ -143,7 +147,7 @@ limitations under the License. -->
</div>
</template>
<script lang="ts">
import { ref, watch, onMounted, defineComponent, toRefs } from "vue";
import { ref, watch, onMounted, onBeforeUnmount, defineComponent, toRefs } from "vue";
import { useI18n } from "vue-i18n";
import type { PropType } from "vue";
import { LayoutConfig } from "@/types/dashboard";
@ -154,7 +158,6 @@ import Trace from "./Trace.vue";
import Profile from "./Profile.vue";
import Log from "./Log.vue";
import Text from "./Text.vue";
import FullVueWrapper from "@/components/FullVueWrapper.vue";
const props = {
data: {
@ -165,7 +168,7 @@ const props = {
};
export default defineComponent({
name: "Tab",
components: { Topology, Widget, Trace, Profile, Log, Text, FullVueWrapper },
components: { Topology, Widget, Trace, Profile, Log, Text },
props,
setup(props) {
const { t } = useI18n();
@ -177,43 +180,52 @@ export default defineComponent({
const needQuery = ref<boolean>(false);
const showTools = ref<boolean>(false);
const tabRef = ref<any>("");
const tabObserveContainer = ref<any>(null);
const currentItem = ref("");
const l = dashboardStore.layout.findIndex(
(d: LayoutConfig) => d.i === props.data.i
);
const l = dashboardStore.layout.findIndex((d: LayoutConfig) => d.i === props.data.i);
if (dashboardStore.layout[l].children.length) {
dashboardStore.setCurrentTabItems(
dashboardStore.layout[l].children[activeTabIndex.value].children
);
dashboardStore.setActiveTabIndex(activeTabIndex.value, props.data.i);
setTimeout(() => {
observeItems()
}, 1500)
observeItems();
}, 1500);
}
function scrollToGraph(e: any) {
document?.getElementById(`tabitem${e}`)?.scrollIntoView();
}
function observeItems() {
function observeItems(kill = false) {
const observer = new IntersectionObserver((entries) => {
entries.forEach((element) => {
console.log("Inter ratio:", element.intersectionRatio, 'Ele:', element.target.id);
if (element.intersectionRatio > 0) {
currentItem.value = element.target.id;
console.log(element.target.id)
if (element.isIntersecting && element.intersectionRatio > 0) {
setTimeout(() => {
currentItem.value = element.target.id;
}, 200);
}
});
});
document.querySelectorAll(".tabitem").forEach((element) => {
observer.observe(element);
});
if (kill) {
document.querySelectorAll(".tabitem").forEach((element) => {
observer.unobserve(element);
});
}
}
watch(() => dashboardStore.currentTabItems, () => {
setTimeout(() => {
observeItems();
}, 500)
} )
watch(
() => dashboardStore.currentTabItems,
() => {
setTimeout(() => {
observeItems();
}, 500);
}
);
function clickTabs(e: Event, idx: number) {
e.stopPropagation();
@ -227,7 +239,7 @@ export default defineComponent({
dashboardStore.setCurrentTabItems(
dashboardStore.layout[l].children[activeTabIndex.value].children
);
needQuery.value = true;
needQuery.value = true;
}
function removeTab(e: Event) {
e.stopPropagation();
@ -263,9 +275,7 @@ export default defineComponent({
function clickTabGrid(e: Event, item: LayoutConfig) {
e.stopPropagation();
activeTabWidget.value = item.i;
dashboardStore.activeGridItem(
`${props.data.i}-${activeTabIndex.value}-${item.i}`
);
dashboardStore.activeGridItem(`${props.data.i}-${activeTabIndex.value}-${item.i}`);
handleClick(e);
}
function layoutUpdatedEvent() {
@ -276,6 +286,20 @@ export default defineComponent({
dashboardStore.layout[l].children[activeTabIndex.value].children
);
}
function initScrollWatcher() {
tabObserveContainer?.value?.addEventListener("scroll", (e: Event) => {
const isBottom =
tabObserveContainer?.value?.offsetHeight +
tabObserveContainer?.value?.scrollTop +
70 >
tabObserveContainer?.value?.scrollHeight;
if (isBottom) {
tabObserveContainer?.value.scroll(0, 0);
}
});
}
document.body.addEventListener("click", handleClick, false);
watch(
() => dashboardStore.activedGridItem,
@ -293,11 +317,15 @@ export default defineComponent({
}
);
onMounted(() => {
initScrollWatcher();
tabRef?.value["parentElement"]?.classList?.toggle("item");
console.log(tabRef.value);
});
onBeforeUnmount(() => {
observeItems(true);
});
return {
currentItem,
tabObserveContainer,
scrollToGraph,
handleClick,
layoutUpdatedEvent,
@ -341,9 +369,14 @@ export default defineComponent({
.scroll-snap-container::-webkit-scrollbar {
display: none;
}
.scroll-snap-container {
-ms-overflow-style: none;
scrollbar-width: none;
}
.tabitem {
scroll-snap-align: start;
height: 100%;
margin: 70px 0;
}
.scroll-handler__wrapper {
z-index: 20;
@ -353,7 +386,7 @@ export default defineComponent({
right: 0;
top: 40vh;
height: auto;
width: 20px;
width: 20px;
.scroll-to {
opacity: 0.5;
width: 10px;