gpstracker/frontend/src/components/Timeline.vue

146 lines
2.8 KiB
Vue

<template>
<div class="timeline-container">
<h1 v-if="loading">Loading...</h1>
<h1 v-else-if="!points.length">No data to display</h1>
<div class="timeline" v-else>
<Line :data="graphData" :options="graphOptions" />
</div>
</div>
</template>
<script lang="ts">
import {
CategoryScale,
Chart as ChartJS,
LineElement,
LinearScale,
PointElement,
TimeScale,
Title,
Tooltip,
} from 'chart.js';
import { Line } from 'vue-chartjs';
import 'chartjs-adapter-date-fns';
import GPSPoint from '../models/GPSPoint';
ChartJS.register(
CategoryScale,
LineElement,
LinearScale,
PointElement,
TimeScale,
Title,
Tooltip,
);
export default {
emits: ['point-hover'],
components: {
Line,
},
props: {
loading: {
type: Boolean,
default: false,
},
points: {
type: Array as () => GPSPoint[],
default: () => [],
},
},
computed: {
graphData() {
return {
labels: this.points.map((point: GPSPoint) => point.timestamp),
datasets: [
{
label: 'Altitude (m)',
backgroundColor: '#7979f8',
borderColor: '#5959a8',
fill: false,
data: this.points.map((point: GPSPoint) => point.altitude),
}
]
}
},
graphOptions() {
return {
responsive: true,
maintainAspectRatio: false,
elements: {
point: {
borderWidth: 1,
hoverRadius: 4,
hoverBorderWidth: 2,
},
line: {
tension: 0.5,
borderWidth: 2,
fill: false,
}
},
interaction: {
mode: 'index',
intersect: false,
},
onHover: (_: MouseEvent, activeElements: any) => {
if (activeElements.length) {
const index = activeElements[0].index;
const point = this.points[index];
this.$emit('point-hover', point);
}
},
scales: {
x: {
type: 'time',
grid: {
drawOnChartArea: true,
drawTicks: true,
},
time: {
tooltipFormat: 'MMM dd, HH:mm',
unit: 'minute',
},
title: {
display: true,
text: 'Date'
},
},
y: {
beginAtZero: true,
title: {
display: true,
text: 'Altitude (m)'
},
}
}
}
},
},
}
</script>
<style scoped lang="scss">
.timeline-container {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
width: 100%;
}
.timeline {
width: 100%;
height: 100%;
canvas {
width: 100% !important;
height: 100% !important;
}
}
</style>