diff --git a/frontend/src/assets/style.css b/frontend/src/assets/style.css index 364b270..36e9d98 100644 --- a/frontend/src/assets/style.css +++ b/frontend/src/assets/style.css @@ -33,6 +33,10 @@ --sidebar-bg: #00003d; --sidebar-text: #ffffff; --sidebar-width: 250px; + /* Vertical chrome around .main-content. A full-height page subtracts these; + hardcoding a different number is what put a scrollbar on the map. */ + --main-pad-top: 20px; + --main-pad-bottom: 70px; /* Hover variants */ --secondary-dark: #82503f; @@ -243,7 +247,7 @@ h1, h2, h3, h4, h5, h6 { .main-content { flex: 1; margin-left: var(--sidebar-width); - padding: 20px 10px 70px 10px; + padding: var(--main-pad-top) 10px var(--main-pad-bottom) 10px; overflow-x: hidden; } diff --git a/frontend/src/components/ShopFloorMap.vue b/frontend/src/components/ShopFloorMap.vue index b7de1c0..cf6e9cf 100644 --- a/frontend/src/components/ShopFloorMap.vue +++ b/frontend/src/components/ShopFloorMap.vue @@ -437,8 +437,18 @@ function renderMarkers() { overlayLayers.forEach(layer => layer.remove()) overlayLayers = [] + // Markers belong to ONE level (ADR-017). This is the drawing being shown, and + // anything positioned against a different one is not drawn on it. + const drawnLevel = drawnLevelId() ?? null + props.machines.forEach(item => { if (item.mapx == null || item.mapy == null) return + // A marker from another level, placed on THIS blueprint, looks entirely + // correct and points at the wrong part of the building - so it is omitted + // rather than approximated. A position with no level at all is omitted for + // the same reason: the map editor lists both, badged, so they can be fixed + // rather than silently misplaced here. + if ((item.levelid ?? null) !== drawnLevel) return // Transform coordinates (database Y is top-down, Leaflet is bottom-up) const leafletY = MAP_HEIGHT - item.mapy @@ -599,6 +609,31 @@ watch(() => props.theme, (newTheme) => { } }) +// Switching level changes the drawing, the coordinate space AND which markers +// belong on it. All three move together: the size is what marker coordinates +// mean, so swapping the image without the bounds would place every marker +// against the wrong scale, and keeping the markers would show the previous +// floor's assets on this floor's plan. +watch(() => drawnLevelId(), (levelid) => { + if (!map || !imageOverlay) return + + MAP_WIDTH = dimensionsFor(levelid).width + MAP_HEIGHT = dimensionsFor(levelid).height + const bounds = [[0, 0], [MAP_HEIGHT, MAP_WIDTH]] + + const url = blueprintUrlFor(props.theme, levelid) + // A level with no blueprint is left blank rather than showing the previous + // one, which would be a floor plan labelled as somewhere it is not. + imageOverlay.setUrl(url || '') + imageOverlay.setBounds(bounds) + + map.setMaxBounds(bounds) + map.setView([MAP_HEIGHT / 2, MAP_WIDTH / 2], map.getZoom()) + + renderMarkers() + loadOverlays() +}) + onMounted(async () => { // Load this facility's blueprint + dimensions before building the map so // bounds and coordinate math use the right size. Falls back to defaults. diff --git a/frontend/src/views/MapEditor.vue b/frontend/src/views/MapEditor.vue index 5d978e4..48e0735 100644 --- a/frontend/src/views/MapEditor.vue +++ b/frontend/src/views/MapEditor.vue @@ -492,7 +492,7 @@ function cancelEdit() { .map-editor { display: flex; flex-direction: column; - height: calc(100vh - 40px); + height: calc(100vh - var(--main-pad-top) - var(--main-pad-bottom)); } .header-actions { diff --git a/frontend/src/views/MapView.vue b/frontend/src/views/MapView.vue index f6b001d..724204a 100644 --- a/frontend/src/views/MapView.vue +++ b/frontend/src/views/MapView.vue @@ -12,6 +12,17 @@