full frontend refactor

This commit is contained in:
2026-01-25 10:26:23 +01:00
parent 317ee96ba3
commit 559a4c3e9f
18 changed files with 2803 additions and 1224 deletions

View File

@@ -0,0 +1,169 @@
<template>
<div
v-if="visible"
class="context-menu"
:style="{ left: x + 'px', top: y + 'px' }"
>
<!-- ============================================= -->
<!-- HEADER - COORDINATES -->
<!-- ============================================= -->
<div class="context-menu-header">
{{ formatCoordinate(lngLat.lng, 'lng') }}, {{ formatCoordinate(lngLat.lat, 'lat') }}
<span v-if="tileData?.id" class="tile-name">{{ tileData.id }}</span>
<span v-if="apiError" class="tile-error"> Lookup failed</span>
</div>
<!-- ============================================= -->
<!-- MENU ITEMS -->
<!-- ============================================= -->
<!-- Always available -->
<button @click="$emit('dropPin')" class="context-menu-item">📍 Drop Pin</button>
<button @click="$emit('startMeasure')" class="context-menu-item">📏 Measure from here</button>
<!-- State A: No tile exists (or error checking) -->
<button
v-if="!tileData && !apiError"
@click="$emit('requestTile')"
class="context-menu-item"
>
📥 Request Tile
</button>
<!-- State B: Tile exists but images not loaded -->
<button
v-if="tileData && !imagesLoaded && (tileData.jpg_available || tileData.png_available)"
@click="$emit('loadImages')"
class="context-menu-item"
>
📦 Load Tile Images
</button>
<!-- State C: Images loaded, but mound not loaded -->
<button
v-if="tileData && !moundLoaded"
@click="$emit('loadMound')"
class="context-menu-item"
>
🔬 Load Interactive Data
</button>
<!-- State D: Mound loaded, ready to open sandbox -->
<button
v-if="moundLoaded"
@click="$emit('openSandbox')"
class="context-menu-item"
>
🔬 Open in Shading Sandbox
</button>
</div>
</template>
<script setup>
import { formatCoordinate } from '../utils/coordinates.js';
// ============================================================================
// INTERFACE
// ============================================================================
const props = defineProps({
visible: {
type: Boolean,
required: true
},
x: {
type: Number,
required: true
},
y: {
type: Number,
required: true
},
lngLat: {
type: Object, // { lng, lat }
required: true
},
tileData: {
type: Object, // Tile metadata from API
default: null
},
imagesLoaded: {
type: Boolean,
default: false
},
moundLoaded: {
type: Boolean,
default: false
},
apiError: {
type: Boolean,
default: false
}
});
defineEmits(['dropPin', 'startMeasure', 'requestTile', 'loadImages', 'loadMound', 'openSandbox']);
</script>
<style scoped>
/* ============================================= */
/* CONTEXT MENU CONTAINER */
/* ============================================= */
.context-menu {
position: absolute;
background: white;
border-radius: 4px;
box-shadow: 0 2px 12px rgba(0,0,0,0.4);
z-index: 1000;
min-width: 200px;
overflow: hidden;
}
/* ============================================= */
/* HEADER */
/* ============================================= */
.context-menu-header {
padding: 10px 12px;
background: #f5f5f5;
font-size: 12px;
font-family: monospace;
border-bottom: 1px solid #ddd;
color: #666;
}
.context-menu-header .tile-name {
display: block;
margin-top: 4px;
font-size: 11px;
color: #999;
}
.context-menu-header .tile-error {
display: block;
margin-top: 4px;
font-size: 11px;
color: #f44336;
font-weight: 600;
}
/* ============================================= */
/* MENU ITEMS */
/* ============================================= */
.context-menu-item {
display: block;
width: 100%;
padding: 10px 12px;
background: white;
border: none;
text-align: left;
font-size: 14px;
cursor: pointer;
transition: background 0.2s;
}
.context-menu-item:hover {
background: #f0f0f0;
}
</style>