v1 Release
This commit is contained in:
@@ -1,43 +1,46 @@
|
||||
<template>
|
||||
<div class="flight-map-wrapper">
|
||||
<PlaneLoader v-if="!mapReady" class="map-loader" />
|
||||
<div ref="mapContainer" class="map-container" :class="{ 'map-hidden': !mapReady }" />
|
||||
<button @click="exportMapBasic()" class="export-btn" title="Download as Image">
|
||||
<span class="mdi mdi-download" />
|
||||
</button>
|
||||
<button class="projection-btn" @click="toggleProjection" :title="isGlobe ? 'Switch to flat map' : 'Switch to globe'">
|
||||
<span class="mdi" :class="isGlobe ? 'mdi-map' : 'mdi-earth'"/>
|
||||
</button>
|
||||
<div v-if="!flights.length" class="empty-state">
|
||||
<span class="mdi mdi-earth-off" />
|
||||
<p>No flight data available</p>
|
||||
</div>
|
||||
<div v-if="showLegend" class="map-legend" :class="{ 'map-legend--open': legendOpen }">
|
||||
<button class="map-legend__toggle" @click="toggleLegend">
|
||||
<span class="mdi mdi-format-list-bulleted" />
|
||||
<span class="map-legend__toggle-label">Legend</span>
|
||||
<span class="mdi" :class="legendOpen ? 'mdi-chevron-down' : 'mdi-chevron-up'" />
|
||||
<div>
|
||||
<div class="flight-map-wrapper">
|
||||
<PlaneLoader v-if="!mapReady" class="map-loader" />
|
||||
<div ref="mapContainer" class="map-container" :class="{ 'map-hidden': !mapReady }" />
|
||||
<button @click="exportMapBasic()" class="export-btn" title="Download as Image">
|
||||
<span class="mdi mdi-download" />
|
||||
</button>
|
||||
<div class="map-legend__body">
|
||||
<div class="map-legend__row" v-for="item in legendItems" :key="item.label">
|
||||
<svg width="28" height="10" class="map-legend__swatch">
|
||||
<line
|
||||
x1="2" y1="5" x2="26" y2="5"
|
||||
:stroke="item.color"
|
||||
:stroke-dasharray="item.dashed ? '4 3' : undefined"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
</svg>
|
||||
<span class="map-legend__label">{{ item.label }}</span>
|
||||
<button class="projection-btn" @click="toggleProjection" :title="isGlobe ? 'Switch to flat map' : 'Switch to globe'">
|
||||
<span class="mdi" :class="isGlobe ? 'mdi-map' : 'mdi-earth'"/>
|
||||
</button>
|
||||
<div v-if="!flights.length" class="empty-state">
|
||||
<span class="mdi mdi-earth-off" />
|
||||
<p>No flight data available</p>
|
||||
</div>
|
||||
<div v-if="showLegend" class="map-legend" :class="{ 'map-legend--open': legendOpen }">
|
||||
<button class="map-legend__toggle" @click="toggleLegend">
|
||||
<span class="mdi mdi-format-list-bulleted" />
|
||||
<span class="map-legend__toggle-label">Legend</span>
|
||||
<span class="mdi" :class="legendOpen ? 'mdi-chevron-down' : 'mdi-chevron-up'" />
|
||||
</button>
|
||||
<div class="map-legend__body">
|
||||
<div class="map-legend__row" v-for="item in legendItems" :key="item.label">
|
||||
<svg width="28" height="10" class="map-legend__swatch">
|
||||
<line
|
||||
x1="2" y1="5" x2="26" y2="5"
|
||||
:stroke="item.color"
|
||||
:stroke-dasharray="item.dashed ? '4 3' : undefined"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
</svg>
|
||||
<span class="map-legend__label">{{ item.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<small class="attribution" v-if="compact" v-html="cartoAttribution" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, onMounted, onBeforeUnmount, watch, nextTick, PropType } from 'vue'
|
||||
import {defineComponent, ref, onMounted, onBeforeUnmount, watch, nextTick, PropType, computed} from 'vue'
|
||||
import maplibregl from 'maplibre-gl'
|
||||
import 'maplibre-gl/dist/maplibre-gl.css'
|
||||
import { Flight, Airport, SharedProps } from '@/Types/types'
|
||||
@@ -72,6 +75,7 @@ interface RoutesGeoJSON {
|
||||
|
||||
|
||||
|
||||
const cartoAttribution = computed(() => `© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors © <a href="https://carto.com/attributions">CARTO</a>`)
|
||||
// ── Constants ─────────────────────────────────────────────────────────────────
|
||||
|
||||
const PULSE_PHASES = 6
|
||||
@@ -688,7 +692,7 @@ export default defineComponent({
|
||||
map = new maplibregl.Map({
|
||||
container: mapContainer.value!,
|
||||
cooperativeGestures: true,
|
||||
attributionControl: false,
|
||||
attributionControl: props.compact ? false : {compact: true},
|
||||
center: [0, 20],
|
||||
zoom: 2,
|
||||
renderWorldCopies: true,
|
||||
@@ -700,6 +704,7 @@ export default defineComponent({
|
||||
type: 'raster',
|
||||
tileSize: 256,
|
||||
maxzoom: 19,
|
||||
attribution: cartoAttribution.value,
|
||||
tiles: [
|
||||
'https://a.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}@2x.png',
|
||||
'https://b.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}@2x.png',
|
||||
@@ -767,7 +772,7 @@ export default defineComponent({
|
||||
if (map) { map.remove(); map = null }
|
||||
})
|
||||
|
||||
return { mapContainer, mapReady, exportMapBasic, legendOpen, legendItems, isGlobe, toggleProjection, toggleLegend }
|
||||
return { mapContainer, mapReady, exportMapBasic, legendOpen, legendItems, isGlobe, toggleProjection, toggleLegend, cartoAttribution }
|
||||
},
|
||||
})
|
||||
|
||||
@@ -816,6 +821,19 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
|
||||
small.attribution{
|
||||
font-size: 0.7em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--muted);
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
small.attribution * {
|
||||
color: var(--muted)
|
||||
}
|
||||
|
||||
.map-container { position: absolute; inset: 0; }
|
||||
.map-loader { z-index: 10; }
|
||||
.map-hidden { visibility: hidden; }
|
||||
@@ -991,4 +1009,9 @@ export default defineComponent({
|
||||
}
|
||||
.projection-btn:hover { background: rgba(77,166,255,0.15); color: #4da6ff; }
|
||||
.projection-btn.globe-active { color: #4da6ff; border-color: rgba(77,166,255,0.35); }
|
||||
.maplibregl-ctrl-bottom-right {
|
||||
left: 50%;
|
||||
right: auto;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user