Files
MoundHunters/ui/src/components/FeaturePopup.vue
2026-01-25 10:26:23 +01:00

230 lines
5.5 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div
v-if="visible"
class="popup"
:style="{ left: x + 'px', top: y + 'px' }"
>
<!-- ============================================= -->
<!-- POPUP CONTENT -->
<!-- ============================================= -->
<div class="popup-content">
<!-- PIN -->
<div v-if="type === 'pin'">
<strong>Pin #{{ feature.properties.number }}</strong>
<div>{{ formatCoordinate(feature.geometry.coordinates[0], 'lng') }}, {{ formatCoordinate(feature.geometry.coordinates[1], 'lat') }}</div>
<button @click="$emit('delete', feature.id)" class="popup-btn danger">Delete</button>
</div>
<!-- LINE or RAY -->
<div v-else-if="type === 'line' || type === 'ray'">
<strong>{{ type === 'line' ? 'Line' : 'Ray' }}</strong>
<div>Length: {{ formatDistance(feature.properties.length, imperialUnits) }}</div>
<div>Bearing: {{ formatBearing(feature.properties.bearing) }}</div>
<button @click="$emit('delete', feature.id)" class="popup-btn danger">Delete</button>
</div>
<!-- HISTORIC SITE -->
<div v-else-if="type === 'site'" class="site-popup">
<strong>{{ feature.properties.name }}</strong>
<div class="site-type">{{ feature.properties.type === 'road_confirmed' ? 'Confirmed Road Segment' : 'Earthwork' }}</div>
<div class="site-description">
<template v-for="(segment, idx) in parsedDescription" :key="idx">
<template v-if="segment.type === 'text'">{{ segment.content }}</template>
<a
v-else-if="segment.type === 'citation'"
href="#"
@click.prevent="$emit('showBibliography', segment.key)"
class="citation-link"
:title="`View ${segment.key} in bibliography`"
>[{{ segment.key }}]</a>
</template>
</div>
</div>
</div>
<!-- ============================================= -->
<!-- CLOSE BUTTON -->
<!-- ============================================= -->
<button @click="$emit('close')" class="popup-close">×</button>
</div>
</template>
<script setup>
import { computed } from 'vue';
import { formatCoordinate, formatDistance, formatBearing } from '../utils/coordinates.js';
import { parseCitations } from '../utils/citations.js';
// ============================================================================
// INTERFACE
// ============================================================================
const props = defineProps({
visible: {
type: Boolean,
required: true
},
x: {
type: Number,
required: true
},
y: {
type: Number,
required: true
},
type: {
type: String, // 'pin', 'line', 'ray', 'site'
default: null
},
feature: {
type: Object,
default: null
},
imperialUnits: {
type: Boolean,
default: false
}
});
const emit = defineEmits(['close', 'delete', 'showBibliography']);
// ============================================================================
// COMPUTED
// ============================================================================
const parsedDescription = computed(() => {
if (props.type === 'site' && props.feature?.properties?.description) {
return parseCitations(props.feature.properties.description);
}
return [];
});
</script>
<style scoped>
/* ============================================= */
/* POPUP CONTAINER */
/* ============================================= */
.popup {
position: absolute;
background: white;
border-radius: 4px;
box-shadow: 0 2px 12px rgba(0,0,0,0.4);
z-index: 1000;
min-width: 180px;
max-width: 350px;
overflow: hidden;
}
/* ============================================= */
/* POPUP CONTENT */
/* ============================================= */
.popup-content {
padding: 12px;
font-size: 14px;
}
.popup-content strong {
display: block;
margin-bottom: 8px;
color: #333;
}
.popup-content div {
margin: 4px 0;
font-size: 13px;
color: #666;
}
/* ============================================= */
/* SITE-SPECIFIC STYLING */
/* ============================================= */
.site-popup {
max-width: 320px;
}
.site-type {
font-size: 11px !important;
text-transform: uppercase;
letter-spacing: 0.5px;
color: #999 !important;
margin-bottom: 8px !important;
}
.site-description {
line-height: 1.5;
color: #333 !important;
font-size: 13px !important;
}
.citation-link {
color: #4A9EFF;
text-decoration: none;
font-family: monospace;
font-size: 11px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
white-space: nowrap;
}
.citation-link:hover {
color: #2E8FE3;
text-decoration: underline;
}
/* ============================================= */
/* POPUP BUTTONS */
/* ============================================= */
.popup-btn {
display: block;
width: 100%;
padding: 8px;
margin-top: 10px;
background: white;
border: 1px solid #ddd;
border-radius: 3px;
font-size: 13px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
}
.popup-btn:hover {
background: #f5f5f5;
}
.popup-btn.danger {
color: #d32f2f;
border-color: #d32f2f;
}
.popup-btn.danger:hover {
background: #ffebee;
}
/* ============================================= */
/* CLOSE BUTTON */
/* ============================================= */
.popup-close {
position: absolute;
top: 4px;
right: 4px;
width: 24px;
height: 24px;
background: none;
border: none;
font-size: 20px;
line-height: 1;
cursor: pointer;
color: #999;
transition: color 0.2s;
}
.popup-close:hover {
color: #333;
}
</style>