230 lines
5.5 KiB
Vue
230 lines
5.5 KiB
Vue
<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> |