feat(fe2): saved views panel keepalive (#5370)

* feat(fe2): saved views panel keepalive

* Remove KeepAlive. Add throttle

---------

Co-authored-by: andrewwallacespeckle <andrew@speckle.systems>
This commit is contained in:
Kristaps Fabians Geikins
2025-09-04 09:43:48 +03:00
committed by GitHub
parent d8d2105fd0
commit 49c8d1b47d
4 changed files with 44 additions and 12 deletions
@@ -139,9 +139,10 @@
]"
:style="`width: ${widthClass};`"
>
<KeepAlive v-show="activePanel === 'models'">
<ViewerModelsPanel v-model:sub-view="modelsSubView" />
</KeepAlive>
<ViewerModelsPanel
v-show="activePanel === 'models'"
v-model:sub-view="modelsSubView"
/>
<KeepAlive v-show="resourceItems.length !== 0 && activePanel === 'filters'">
<ViewerFiltersPanel />
</KeepAlive>
@@ -154,10 +155,12 @@
:summary="summary"
/>
<ViewerDataviewerPanel v-if="activePanel === 'devMode'" />
<ViewerSavedViewsPanel
v-if="isSavedViewsEnabled && activePanel === 'savedViews'"
@close="activePanel = 'none'"
/>
<KeepAlive>
<ViewerSavedViewsPanel
v-if="isSavedViewsEnabled && activePanel === 'savedViews'"
@close="activePanel = 'none'"
/>
</KeepAlive>
</div>
</aside>
</template>
@@ -126,7 +126,7 @@ import {
useTreeManagement,
type UnifiedVirtualItem
} from '~~/lib/viewer/composables/tree'
import { useVirtualList, useDebounceFn } from '@vueuse/core'
import { useVirtualList, useDebounceFn, useThrottleFn } from '@vueuse/core'
type ModelItem = NonNullable<Get<ViewerLoadedResourcesQuery, 'project.models.items[0]'>>
@@ -373,7 +373,7 @@ const handleSelectionChange = useDebounceFn(
)
// Simple scroll tracking - just switch headers
const handleScroll = (e: Event) => {
const handleScroll = useThrottleFn((e: Event) => {
const container = e.target as HTMLElement
if (!container) return
@@ -400,7 +400,7 @@ const handleScroll = (e: Event) => {
versionId: currentHeader.versionId
}
}
}
}, 16)
watch(selectedObjects, handleSelectionChange, { deep: true })
@@ -78,7 +78,10 @@
</ViewerButtonGroupButton>
</ViewerButtonGroup>
</div>
<div class="text-body-sm flex-1 min-h-0 overflow-y-auto simple-scrollbar">
<div
ref="groupsScrollArea"
class="text-body-sm flex-1 min-h-0 overflow-y-auto simple-scrollbar"
>
<ViewerSavedViewsPanelGroups
:views-type="selectedViewsType"
:search="searchMode ? search || undefined : undefined"
@@ -111,6 +114,7 @@ import { useCreateSavedView } from '~/lib/viewer/composables/savedViews/manageme
import { useInjectedViewerState } from '~/lib/viewer/composables/setup'
import { ViewsType, viewsTypeLabels } from '~/lib/viewer/helpers/savedViews'
import { useDebouncedTextInput } from '@speckle/ui-components'
import { useKeepAliveScrollState } from '~/lib/common/composables/dom'
graphql(`
fragment ViewerSavedViewsPanel_Project on Project {
@@ -155,6 +159,7 @@ const searchMode = ref(false)
const showCreateGroupDialog = ref(false)
const { getTooltipProps } = useSmartTooltipDelay()
useKeepAliveScrollState(useTemplateRef('groupsScrollArea'))
const canCreateViewOrGroup = computed(
() => project.value?.permissions.canCreateSavedView
@@ -1,6 +1,7 @@
import { TIME_MS, timeoutAt } from '@speckle/shared'
import { until } from '@vueuse/core'
import { until, useScroll } from '@vueuse/core'
import DOMPurify from 'dompurify'
import type { ShallowRef } from 'vue'
const purify = async (source: string) => {
let purify: DOMPurify.DOMPurifyI
@@ -55,3 +56,26 @@ export function usePurifiedHtml(
purifiedHtml: computed(() => asyncData.data.value || '')
}
}
/**
* KeepAlive loses scroll position - use this to cache and restore the position
* upon reactivation
*/
export const useKeepAliveScrollState = (el: ShallowRef<HTMLElement | null>) => {
const scrollTracker = useScroll(el, {
throttle: 100
})
const retainedX = ref(scrollTracker.x.value)
const retainedY = ref(scrollTracker.y.value)
onDeactivated(() => {
retainedX.value = scrollTracker.x.value
retainedY.value = scrollTracker.y.value
})
onActivated(() => {
scrollTracker.x.value = retainedX.value
scrollTracker.y.value = retainedY.value
})
}