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 See the License for the specific language governing permissions and
limitations under the License. --> limitations under the License. -->
<template> <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"> <div :id="'item' + item.i" class="item" v-for="item in items" :key="item.i">
<slot v-if="items.length"> <slot v-if="items.length">
<component :is="item.type" :data="item" /> <component :is="item.type" :data="item" />
@ -22,11 +31,10 @@ limitations under the License. -->
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { ref, watch, reactive, defineComponent } from "vue"; import { ref, watch, onMounted, defineComponent } from "vue";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { useRoute } from "vue-router"; import { useRoute } from "vue-router";
import { useDashboardStore } from "@/store/modules/dashboard"; import { useDashboardStore } from "@/store/modules/dashboard";
// import { useAppStoreWithOut } from "@/store/modules/app";
import Configuration from "../views/dashboard/configuration"; import Configuration from "../views/dashboard/configuration";
import controls from "../views/dashboard/controls/index"; import controls from "../views/dashboard/controls/index";
@ -41,51 +49,21 @@ export default defineComponent({
components: { ...Configuration, ...controls }, components: { ...Configuration, ...controls },
setup() { setup() {
const dashboardStore = useDashboardStore(); const dashboardStore = useDashboardStore();
const tobewatched = reactive(dashboardStore);
// const appStore = useAppStoreWithOut();
const { t } = useI18n(); const { t } = useI18n();
const p = useRoute().params; const p = useRoute().params;
// const layoutKey = ref<string>(`${p.layerId}_${p.entity}_${p.name}`);
// setTemplate();
const currentItem = ref(""); const currentItem = ref("");
const scrollWrapRef = ref<any>(null);
watch( watch(
() => tobewatched.layout, () => dashboardStore.layout,
() => { () => {
setTimeout(() => { setTimeout(() => {
observeItems(); observeItems();
}, 500); }, 500);
} }
); );
function scrollToGraph(e: any) {
// async function setTemplate() { document?.getElementById(`item${e}`)?.scrollIntoView();
// 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 observeItems() { function observeItems() {
const observer = new IntersectionObserver((entries) => { const observer = new IntersectionObserver((entries) => {
entries.forEach((element) => { entries.forEach((element) => {
@ -97,11 +75,30 @@ export default defineComponent({
document.querySelectorAll(".item").forEach((element) => { document.querySelectorAll(".item").forEach((element) => {
observer.observe(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 { return {
t, t,
dashboardStore, dashboardStore,
currentItem, currentItem,
scrollToGraph,
scrollWrapRef,
}; };
}, },
}); });
@ -119,6 +116,31 @@ export default defineComponent({
scroll-snap-type: y mandatory; scroll-snap-type: y mandatory;
scroll-snap-type: mandatory; scroll-snap-type: mandatory;
scroll-behavior: smooth; 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 { .scroll-snap-container::-webkit-scrollbar {
display: none; 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'] ElMenuItemGroup: typeof import('element-plus/es')['ElMenuItemGroup']
ElOption: typeof import('element-plus/es')['ElOption'] ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination'] ElPagination: typeof import('element-plus/es')['ElPagination']
ElPopconfirm: typeof import('element-plus/es')['ElPopconfirm']
ElPopover: typeof import('element-plus/es')['ElPopover'] ElPopover: typeof import('element-plus/es')['ElPopover']
ElProgress: typeof import('element-plus/es')['ElProgress'] ElProgress: typeof import('element-plus/es')['ElProgress']
ElRadio: typeof import('element-plus/es')['ElRadio'] ElRadio: typeof import('element-plus/es')['ElRadio']

View File

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

View File

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