Files
ComfyUI_frontend/src/renderer/extensions/minimap/MiniMap.vue
Christian Byrne 03f597a496 fix: open minimap settings panel above on mobile (#8589)
## Summary

On mobile viewports, the minimap settings panel now opens above the
minimap instead of to the left, preventing it from extending off-screen
on narrow viewports.

<img width="918" height="974" alt="image"
src="https://github.com/user-attachments/assets/bd42fb38-207f-437e-86f3-65cd2eccc666"
/>

<img width="1074" height="970" alt="image"
src="https://github.com/user-attachments/assets/3fdd2109-a492-4570-a8ee-e67de171126b"
/>


## Changes

- Add mobile breakpoint detection using `useBreakpoints` from VueUse
- Use `flex-col-reverse` on mobile to position panel above the minimap
- Change margin from `mr-2` (right) to `mb-2` (bottom) on mobile

## Testing

- On desktop (≥768px width): Panel opens to the left of minimap
(unchanged)
- On mobile (<768px width): Panel opens above the minimap

## Related

- Fixes [Bug: Mobile minimap settings popover opens left instead of
up](https://www.notion.so/comfy-org/Bug-Mobile-minimap-settings-popover-opens-left-instead-of-up-2fc6d73d365081549a57c9132526edca)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Minimap now adapts layout between mobile and desktop for improved
usability.
* Panel spacing and alignment adjust automatically to better fit small
screens, improving readability and control placement.
* Responsive behavior provides a more consistent experience across
device sizes, with smoother transitions between compact (mobile) and
wide (desktop) layouts.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8589-fix-open-minimap-settings-panel-above-on-mobile-2fc6d73d365081ed8125c16051865c2b)
by [Unito](https://www.unito.io)
2026-02-20 02:12:19 -08:00

158 lines
3.6 KiB
Vue

<template>
<div
v-if="visible && initialized"
ref="minimapRef"
:class="
cn(
'minimap-main-container absolute right-0 bottom-[54px] z-1000 flex',
isMobile ? 'flex-col' : 'flex-row'
)
"
>
<MiniMapPanel
v-if="showOptionsPanel"
:panel-styles="panelStyles"
:node-colors="nodeColors"
:show-links="showLinks"
:show-groups="showGroups"
:render-bypass="renderBypass"
:render-error="renderError"
:is-mobile="isMobile"
@update-option="updateOption"
/>
<div
ref="containerRef"
class="litegraph-minimap relative border border-interface-stroke bg-comfy-menu-bg shadow-interface"
:style="containerStyles"
>
<Button
class="absolute top-0 left-0 z-10"
size="icon"
variant="muted-textonly"
:aria-label="$t('g.settings')"
@click.stop="toggleOptionsPanel"
>
<i class="icon-[lucide--settings-2]" />
</Button>
<Button
class="absolute top-0 right-0 z-10"
size="icon"
variant="muted-textonly"
:aria-label="$t('g.close')"
data-testid="close-minmap-button"
@click.stop="() => commandStore.execute('Comfy.Canvas.ToggleMinimap')"
>
<i class="icon-[lucide--x]" />
</Button>
<hr
class="absolute top-6 h-px border-0 bg-node-component-border"
:style="{
width: containerStyles.width
}"
/>
<canvas
ref="canvasRef"
:width="width"
:height="height"
class="minimap-canvas"
/>
<div class="minimap-viewport" :style="viewportStyles" />
<div
class="absolute inset-0 touch-none"
@pointerdown="handlePointerDown"
@pointermove="handlePointerMove"
@pointerup="handlePointerUp"
@pointerleave="handlePointerUp"
@pointercancel="handlePointerCancel"
@wheel="handleWheel"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { breakpointsTailwind, useBreakpoints } from '@vueuse/core'
import { onMounted, onUnmounted, ref, useTemplateRef } from 'vue'
import Button from '@/components/ui/button/Button.vue'
import { useMinimap } from '@/renderer/extensions/minimap/composables/useMinimap'
import { useCommandStore } from '@/stores/commandStore'
import { cn } from '@/utils/tailwindUtil'
import MiniMapPanel from './MiniMapPanel.vue'
const isMobile = useBreakpoints(breakpointsTailwind).smaller('md')
const commandStore = useCommandStore()
const minimapRef = ref<HTMLDivElement>()
const containerRef = useTemplateRef<HTMLDivElement>('containerRef')
const canvasRef = useTemplateRef<HTMLCanvasElement>('canvasRef')
const {
initialized,
visible,
containerStyles,
viewportStyles,
width,
height,
panelStyles,
nodeColors,
showLinks,
showGroups,
renderBypass,
renderError,
updateOption,
destroy,
handlePointerDown,
handlePointerMove,
handlePointerUp,
handlePointerCancel,
handleWheel,
setMinimapRef
} = useMinimap({
containerRefMaybe: containerRef,
canvasRefMaybe: canvasRef
})
const showOptionsPanel = ref(false)
const toggleOptionsPanel = () => {
showOptionsPanel.value = !showOptionsPanel.value
}
onMounted(() => {
if (minimapRef.value) {
setMinimapRef(minimapRef.value)
}
})
onUnmounted(() => {
destroy()
})
</script>
<style scoped>
.litegraph-minimap {
overflow: hidden;
}
.minimap-canvas {
display: block;
width: 100%;
height: 100%;
pointer-events: none;
}
.minimap-viewport {
position: absolute;
top: 0;
left: 0;
pointer-events: none;
}
</style>