Make the map editor accept a click, and unclutter its panel
Some checks failed
CI / backend (push) Failing after 8s
CI / naming (push) Successful in 2s
CI / frontend (push) Successful in 9s
CI / migrations-mysql (push) Failing after 7s

Clicking the map in the editor did nothing, ever. The click handler was bound
inside `if (props.pickerMode)` at mount, and the editor mounts with no asset
selected, so the handler was never attached; selecting an asset flipped the prop
but nothing rebound it. The asset forms were unaffected because their picker
mounts inside a modal that is already in picker mode, which is why this looked
like an editor-only fault. The handler is now bound unconditionally and
handleMapClick keeps its own picker-mode guard.

Verified in a browser against the dev instance, both ways: with the old binding
a click on a selected asset produced no position at all; with the fix the same
click reports 1652, 1138.

Markers are now drawn in picker mode too. Placing one relative to the machines
already on the floor is the entire task, and the old code skipped rendering them
whenever the map was a picker.

Two things that looked like stray widgets:
- The editor's panel header put a heading and three controls on one row inside a
  320px panel, squeezing the search box until its placeholder read "Search na".
  The heading takes its own row and the controls share the next.
- The legend drew its bar and border even with nothing to put in it, which read
  as an empty input box under the toolbar. It renders only when it has entries.
This commit is contained in:
cproudlock
2026-08-17 15:20:44 -04:00
parent aa6db94179
commit 89248407e7
2 changed files with 38 additions and 11 deletions

View File

@@ -45,7 +45,7 @@
</div>
<!-- Legend for asset type mode - shows subtypes when a type is selected -->
<div class="map-legend" v-if="!pickerMode && assetTypeMode">
<div class="map-legend" v-if="!pickerMode && assetTypeMode && hasLegendEntries">
<!-- Show subtype legend when a specific type is selected -->
<template v-if="selectedAssetType && Object.keys(visibleSubtypes).length">
<span
@@ -120,6 +120,14 @@ const props = defineProps({
const emit = defineEmits(['markerClick', 'positionPicked'])
// An empty legend still drew its bar and border, which looked like a stray input
// box under the toolbar. It renders only when it has something in it.
const hasLegendEntries = computed(() => {
if (props.selectedAssetType && Object.keys(visibleSubtypes.value).length) return true
if (Object.keys(visibleAssetTypes.value).length) return true
return overlayLegend.value.length > 0
})
const mapContainer = ref(null)
let map = null
let imageOverlay = null
@@ -296,17 +304,22 @@ function initMap() {
map.setView([MAP_HEIGHT / 2, MAP_WIDTH / 2], initialZoom)
map.setMaxBounds(bounds)
// Picker mode: click to set position
if (props.pickerMode) {
map.on('click', handleMapClick)
// Bound UNCONDITIONALLY, and handleMapClick ignores the click when picker mode
// is off. Binding it only when pickerMode was true AT MOUNT meant a map that
// becomes a picker later never got a click handler at all: the map editor
// opens with nothing selected, so clicking to place a marker did nothing, for
// the whole life of the page. The asset forms only worked because their picker
// is mounted inside a modal that is already in picker mode.
map.on('click', handleMapClick)
// Show initial position if provided
if (props.initialPosition) {
setPickerPosition(props.initialPosition.left, props.initialPosition.top)
}
} else {
renderMarkers()
if (props.pickerMode && props.initialPosition) {
setPickerPosition(props.initialPosition.left, props.initialPosition.top)
}
// Existing markers are drawn in picker mode too: placing a marker relative to
// the ones already on the floor is the whole task. The forms pass no markers,
// so this costs them nothing.
renderMarkers()
}
function handleMapClick(e) {

View File

@@ -518,13 +518,27 @@ function cancelEdit() {
}
.panel-header {
/* The heading takes its own row and the three controls share the next one(s).
All four on a single row inside a 320px panel squeezed the search box until
its placeholder read "Search na". */
display: flex;
justify-content: space-between;
flex-wrap: wrap;
gap: 0.5rem;
align-items: center;
padding: 1rem;
border-bottom: 1px solid var(--border);
}
.panel-header h3 {
flex: 1 1 100%;
}
.panel-header .form-control {
/* Shrinkable and equal, so nothing gets crushed to fit a sibling. */
flex: 1 1 8rem;
min-width: 0;
}
.panel-header h3 {
margin: 0;
font-size: 1rem;