refactor(fe): Models panel with versions and diff
refactor(fe): Models panel with versions and diff
This commit is contained in:
@@ -133,7 +133,7 @@
|
||||
:style="`width: ${widthClass};`"
|
||||
>
|
||||
<KeepAlive v-show="activePanel === 'models'">
|
||||
<ViewerModelsPanel />
|
||||
<ViewerModelsPanel v-model:sub-view="modelsSubView" />
|
||||
</KeepAlive>
|
||||
<KeepAlive v-show="resourceItems.length !== 0 && activePanel === 'filters'">
|
||||
<ViewerFiltersPanel />
|
||||
@@ -167,6 +167,7 @@ import { useIntercomEnabled } from '~~/lib/intercom/composables/enabled'
|
||||
import { viewerDocsRoute } from '~~/lib/common/helpers/route'
|
||||
import { useAreSavedViewsEnabled } from '~/lib/viewer/composables/savedViews/general'
|
||||
import { Camera } from 'lucide-vue-next'
|
||||
import { ModelsSubView } from '~~/lib/viewer/helpers/sceneExplorer'
|
||||
|
||||
type ActivePanel =
|
||||
| 'none'
|
||||
@@ -237,6 +238,7 @@ const { $intercom } = useNuxtApp()
|
||||
const { hasActiveFilters } = useFilterUtilities()
|
||||
|
||||
const activePanel = ref<ActivePanel>('none')
|
||||
const modelsSubView = ref<ModelsSubView>(ModelsSubView.Main)
|
||||
|
||||
const hasActivePanel = computed(() => activePanel.value !== 'none')
|
||||
|
||||
@@ -277,7 +279,28 @@ registerShortcuts({
|
||||
|
||||
const toggleActivePanel = (panel: ActivePanel) => {
|
||||
const wasNone = activePanel.value === 'none'
|
||||
activePanel.value = activePanel.value === panel ? 'none' : panel
|
||||
|
||||
if (panel === 'models') {
|
||||
if (activePanel.value === 'models') {
|
||||
if (
|
||||
modelsSubView.value === ModelsSubView.Versions ||
|
||||
modelsSubView.value === ModelsSubView.Diff
|
||||
) {
|
||||
// Go back to main models view instead of closing
|
||||
modelsSubView.value = ModelsSubView.Main
|
||||
return
|
||||
} else {
|
||||
activePanel.value = 'none'
|
||||
}
|
||||
} else {
|
||||
// Open models panel and reset to main view
|
||||
activePanel.value = 'models'
|
||||
modelsSubView.value = ModelsSubView.Main
|
||||
}
|
||||
} else {
|
||||
activePanel.value = activePanel.value === panel ? 'none' : panel
|
||||
modelsSubView.value = ModelsSubView.Main
|
||||
}
|
||||
|
||||
// If a panel is being opened (not closed) on mobile, emit event to parent
|
||||
if (wasNone && activePanel.value !== 'none' && isMobile.value) {
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
<template>
|
||||
<div class="select-none h-full">
|
||||
<ViewerCompareChangesPanel
|
||||
v-if="subView === 'diff'"
|
||||
:clear-on-back="false"
|
||||
@close="handleDiffClose"
|
||||
/>
|
||||
<ViewerModelsVersions
|
||||
v-if="showVersions"
|
||||
v-else-if="subView === 'versions'"
|
||||
:expanded-model-id="expandedModelId"
|
||||
@close="handleVersionsClose"
|
||||
/>
|
||||
@@ -15,7 +20,7 @@
|
||||
<ViewerModelsActions
|
||||
v-if="!hasObjects"
|
||||
:hide-versions="resourceItems.length === 0 && objects.length === 0"
|
||||
@show-versions="showVersions = true"
|
||||
@show-versions="subView = ModelsSubView.Versions"
|
||||
@add-model="showAddModel = true"
|
||||
/>
|
||||
</template>
|
||||
@@ -106,7 +111,7 @@ import {
|
||||
useInjectedViewerState
|
||||
} from '~~/lib/viewer/composables/setup'
|
||||
|
||||
import type { ExplorerNode } from '~~/lib/viewer/helpers/sceneExplorer'
|
||||
import { ModelsSubView, type ExplorerNode } from '~~/lib/viewer/helpers/sceneExplorer'
|
||||
import type { ViewerLoadedResourcesQuery } from '~~/lib/common/generated/gql/graphql'
|
||||
import type { Get } from 'type-fest'
|
||||
import { useDiffUtilities, useSelectionUtilities } from '~~/lib/viewer/composables/ui'
|
||||
@@ -118,12 +123,11 @@ import { useVirtualList, useDebounceFn } from '@vueuse/core'
|
||||
|
||||
type ModelItem = NonNullable<Get<ViewerLoadedResourcesQuery, 'project.models.items[0]'>>
|
||||
|
||||
defineEmits(['close'])
|
||||
|
||||
const showVersions = ref(false)
|
||||
const showAddModel = ref(false)
|
||||
const subView = defineModel<ModelsSubView>('subView', { default: ModelsSubView.Main })
|
||||
const expandedModelId = ref<string | null>(null)
|
||||
|
||||
const showAddModel = ref(false)
|
||||
|
||||
const expandedNodes = ref<Set<string>>(new Set())
|
||||
const expandedModels = ref<Set<string>>(new Set())
|
||||
const disableScrollOnNextSelection = ref(false)
|
||||
@@ -139,7 +143,8 @@ const {
|
||||
const {
|
||||
resources: {
|
||||
response: { resourceItems: stateResourceItems }
|
||||
}
|
||||
},
|
||||
ui: { diff: diffState }
|
||||
} = useInjectedViewerState()
|
||||
const {
|
||||
objects: selectedObjects,
|
||||
@@ -147,7 +152,7 @@ const {
|
||||
clearSelection,
|
||||
removeFromSelection
|
||||
} = useSelectionUtilities()
|
||||
const { diffModelVersions } = useDiffUtilities()
|
||||
const { diffModelVersions, endDiff } = useDiffUtilities()
|
||||
const {
|
||||
flattenModelTree,
|
||||
getRootNodesForModel,
|
||||
@@ -211,22 +216,31 @@ const modelHeaderPositions = computed(() => {
|
||||
return headers
|
||||
})
|
||||
|
||||
const hasDiffActive = computed(() => {
|
||||
return !!(diffState.oldVersion.value && diffState.newVersion.value)
|
||||
})
|
||||
|
||||
const handleShowVersions = (modelId: string) => {
|
||||
expandedModelId.value = modelId
|
||||
showVersions.value = true
|
||||
subView.value = ModelsSubView.Versions
|
||||
}
|
||||
|
||||
const handleShowDiff = async (modelId: string, versionA: string, versionB: string) => {
|
||||
await diffModelVersions(modelId, versionA, versionB)
|
||||
expandedModelId.value = modelId
|
||||
showVersions.value = true
|
||||
subView.value = ModelsSubView.Diff
|
||||
}
|
||||
|
||||
const handleVersionsClose = () => {
|
||||
showVersions.value = false
|
||||
subView.value = ModelsSubView.Main
|
||||
expandedModelId.value = null
|
||||
}
|
||||
|
||||
const handleDiffClose = async () => {
|
||||
await endDiff()
|
||||
subView.value = ModelsSubView.Versions
|
||||
}
|
||||
|
||||
const toggleModelExpansion = (modelId: string) => {
|
||||
if (expandedModels.value.has(modelId)) {
|
||||
expandedModels.value.delete(modelId)
|
||||
@@ -383,6 +397,18 @@ const handleScroll = (e: Event) => {
|
||||
|
||||
watch(selectedObjects, handleSelectionChange, { deep: true })
|
||||
|
||||
watch(subView, (newSubView) => {
|
||||
if (newSubView === ModelsSubView.Main) {
|
||||
expandedModelId.value = null
|
||||
}
|
||||
})
|
||||
|
||||
watch(hasDiffActive, (isActive) => {
|
||||
if (isActive && subView.value !== ModelsSubView.Diff) {
|
||||
subView.value = ModelsSubView.Diff
|
||||
}
|
||||
})
|
||||
|
||||
// Initialize and update sticky header when models change
|
||||
watch(
|
||||
unifiedVirtualItems,
|
||||
|
||||
@@ -1,58 +1,51 @@
|
||||
<template>
|
||||
<div class="h-full">
|
||||
<ViewerCompareChangesPanel
|
||||
v-if="showDiff"
|
||||
:clear-on-back="false"
|
||||
@close="handleDiffClose"
|
||||
/>
|
||||
<ViewerLayoutSidePanel v-else>
|
||||
<template #title>
|
||||
<div class="flex items-center gap-x-1">
|
||||
<FormButton
|
||||
:icon-left="ChevronLeftIcon"
|
||||
color="subtle"
|
||||
class="-ml-3"
|
||||
hide-text
|
||||
size="sm"
|
||||
@click="handleClose"
|
||||
<ViewerLayoutSidePanel>
|
||||
<template #title>
|
||||
<div class="flex items-center gap-x-1">
|
||||
<FormButton
|
||||
:icon-left="ChevronLeftIcon"
|
||||
color="subtle"
|
||||
class="-ml-3"
|
||||
hide-text
|
||||
size="sm"
|
||||
@click="handleClose"
|
||||
>
|
||||
Exit versions
|
||||
</FormButton>
|
||||
Versions
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="flex flex-col h-full">
|
||||
<template v-if="resourceItems.length">
|
||||
<!-- Versions with single scroll container for sticky headers -->
|
||||
<div class="flex-1 overflow-y-auto simple-scrollbar">
|
||||
<div
|
||||
v-for="({ model, versionId }, index) in modelsAndVersionIds"
|
||||
:key="model.id"
|
||||
>
|
||||
Exit versions
|
||||
</FormButton>
|
||||
Versions
|
||||
<ViewerModelsVersionsCard
|
||||
:model="model"
|
||||
:version-id="versionId"
|
||||
:last="index === modelsAndVersionIds.length - 1"
|
||||
:initially-expanded="
|
||||
props.expandedModelId === model.id || modelsAndVersionIds.length === 1
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<template v-if="objects.length !== 0">
|
||||
<ViewerResourcesObjectCard
|
||||
v-for="object in objects"
|
||||
:key="object.objectId"
|
||||
:object="object"
|
||||
:show-remove="false"
|
||||
@remove="(id: string) => removeModel(id)"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="flex flex-col h-full">
|
||||
<template v-if="resourceItems.length">
|
||||
<!-- Versions with single scroll container for sticky headers -->
|
||||
<div class="flex-1 overflow-y-auto simple-scrollbar">
|
||||
<div
|
||||
v-for="({ model, versionId }, index) in modelsAndVersionIds"
|
||||
:key="model.id"
|
||||
>
|
||||
<ViewerModelsVersionsCard
|
||||
:model="model"
|
||||
:version-id="versionId"
|
||||
:last="index === modelsAndVersionIds.length - 1"
|
||||
:initially-expanded="
|
||||
props.expandedModelId === model.id || modelsAndVersionIds.length === 1
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<template v-if="objects.length !== 0">
|
||||
<ViewerResourcesObjectCard
|
||||
v-for="object in objects"
|
||||
:key="object.objectId"
|
||||
:object="object"
|
||||
:show-remove="false"
|
||||
@remove="(id: string) => removeModel(id)"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</ViewerLayoutSidePanel>
|
||||
</div>
|
||||
</div>
|
||||
</ViewerLayoutSidePanel>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@@ -84,19 +77,10 @@ const { endDiff } = useDiffUtilities()
|
||||
|
||||
const mp = useMixpanel()
|
||||
|
||||
const showDiff = ref(false)
|
||||
|
||||
const hasDiffActive = computed(() => {
|
||||
return !!(diffState.oldVersion.value && diffState.newVersion.value)
|
||||
})
|
||||
|
||||
const handleDiffClose = async () => {
|
||||
showDiff.value = false
|
||||
if (hasDiffActive.value) {
|
||||
await endDiff()
|
||||
}
|
||||
}
|
||||
|
||||
const handleClose = async () => {
|
||||
if (hasDiffActive.value) {
|
||||
await endDiff()
|
||||
@@ -129,15 +113,4 @@ const refhack = ref(1)
|
||||
useViewerEventListener(ViewerEvent.LoadComplete, () => {
|
||||
refhack.value++
|
||||
})
|
||||
|
||||
// Watch for diff becoming active and show it
|
||||
watch(
|
||||
hasDiffActive,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
showDiff.value = true
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
</script>
|
||||
|
||||
@@ -32,3 +32,9 @@ export type TreeItemComponentModel = {
|
||||
}
|
||||
|
||||
export type { SpeckleObject, SpeckleReference }
|
||||
|
||||
export enum ModelsSubView {
|
||||
Main = 'main',
|
||||
Versions = 'versions',
|
||||
Diff = 'diff'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user