feat(editor): dock the sim panel and refit the canvas to the rest

The results panel floated over the canvas and hid the lower part of the
diagram. Dock it beneath the canvas instead, and refit the view when it
opens or closes so the whole diagram stays framed in the height that
remains. The refit fires on Vue Flow's `dimensions` (post-measure), not
on the toggle, so it frames against the settled size.
This commit is contained in:
Julien Calixte
2026-06-21 12:41:57 +02:00
parent 2ae7cf87d7
commit 59e3aafa63
2 changed files with 28 additions and 5 deletions

View File

@@ -24,7 +24,7 @@ import {
VueFlow, VueFlow,
type XYPosition, type XYPosition,
} from "@vue-flow/core" } from "@vue-flow/core"
import { computed, onBeforeUnmount, onMounted, ref, useTemplateRef } from "vue" import { computed, onBeforeUnmount, onMounted, ref, useTemplateRef, watch } from "vue"
import { useAutosave } from "@/composables/useAutosave" import { useAutosave } from "@/composables/useAutosave"
import { usePlayback } from "@/composables/usePlayback" import { usePlayback } from "@/composables/usePlayback"
import { parseModel, serializeModel } from "@/model/io" import { parseModel, serializeModel } from "@/model/io"
@@ -74,6 +74,7 @@ const {
getSelectedEdges, getSelectedEdges,
viewport, viewport,
vueFlowRef, vueFlowRef,
dimensions,
fitView, fitView,
} = useVueFlow("meadows") } = useVueFlow("meadows")
@@ -89,6 +90,26 @@ onNodesInitialized(() => {
fitView({ padding: 0.2 }) fitView({ padding: 0.2 })
}) })
// Docking the results panel (Simulate) shrinks the canvas; closing it gives the
// space back. Re-fit either way so the whole diagram stays framed in whatever
// height remains. Vue Flow re-measures the pane a frame after the panel
// mounts/unmounts and republishes the size via `dimensions`; fitting on *that*
// signal (not straight after the toggle) frames against the settled size, so the
// diagram never lands jammed against the new edge. The flag scopes the fit to
// panel toggles, leaving ordinary window resizes to Vue Flow's own handling.
let fitOnResize = false
watch(showResults, () => {
fitOnResize = true
})
watch(
() => [dimensions.value.width, dimensions.value.height],
() => {
if (!fitOnResize) return
fitOnResize = false
fitView({ padding: 0.2 })
},
)
// Restore the last document on mount and persist every change (F7). Arm the same // Restore the last document on mount and persist every change (F7). Arm the same
// post-measure fit a load uses; fitting synchronously in onRestore would frame // post-measure fit a load uses; fitting synchronously in onRestore would frame
// against unmeasured 0×0 nodes, landing the restored model jammed top-left. // against unmeasured 0×0 nodes, landing the restored model jammed top-left.
@@ -460,7 +481,6 @@ onBeforeUnmount(() => {
<LoopOverlay /> <LoopOverlay />
<GlossPanel /> <GlossPanel />
<Inspector /> <Inspector />
<ResultsPanel v-if="showResults" @close="showResults = false" />
<!-- Self-dismissing teaching hint, e.g. when a Flow is dragged back onto its <!-- Self-dismissing teaching hint, e.g. when a Flow is dragged back onto its
own Stock to "close the loop". Click to dismiss early; z-30 keeps it above own Stock to "close the loop". Click to dismiss early; z-30 keeps it above
@@ -479,5 +499,10 @@ onBeforeUnmount(() => {
</div> </div>
</div> </div>
</div> </div>
<!-- Docked beneath the canvas (not floating over it): opening Simulate shrinks
the canvas to the space above, where the diagram re-fits (see the
`dimensions` watcher in <script>). -->
<ResultsPanel v-if="showResults" @close="showResults = false" />
</div> </div>
</template> </template>

View File

@@ -59,9 +59,7 @@ const chart = computed(() => {
</script> </script>
<template> <template>
<div <div class="shrink-0 border-t border-base-300 bg-base-100 p-3">
class="absolute inset-x-3 bottom-3 z-30 rounded-box border border-base-300 bg-base-100/95 p-3 shadow-lg backdrop-blur"
>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<span class="text-sm font-semibold">Behaviour over time</span> <span class="text-sm font-semibold">Behaviour over time</span>
<span class="truncate text-xs text-base-content/50">{{ store.model.name }}</span> <span class="truncate text-xs text-base-content/50">{{ store.model.name }}</span>