Odjezdova-tabule-MHD/server/static/index.html

115 lines
3.9 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<title>Odjezdová tabule</title>
<style>
:root {
--site-bg: #1C1C1C;
--site-alt-bg: #272727;
}
body {background-color: var(--site-bg); color: #fffd; margin: 0; margin-top: 35px; font-family: sans-serif; box-sizing: border-box;}
.container {max-width: 700px; padding: 25px; margin: auto;}
.departure-grid {display: grid; grid-template-columns: 30px 1fr max-content max-content; gap: 10px 20px; line-height: 31px;}
.header {line-height: 100%; opacity: .5; margin-bottom: 10px; font-size: 90%;}
.line {background-color: var(--site-alt-bg); border-radius: 3px; box-shadow: 0 0 5px 0 #0004; text-align: center; font-weight: 700; width: 30px; height: 30px; color: #fff; text-shadow: 0 0 15px #000;}
.last-stop {font-weight: 700;}
.departure {text-align: right;}
.delay {text-align: right; color: #ff6e6e; font-size: 80%;}
.line.type1 {background-color: #F0BE32;}
.line.type2 {background-color: #1E9641;}
.line.type3 {background-color: #CD2837;}
h3 {color: #fff;}
.nav {position: fixed; top: 0; left: 0; width: 100%; z-index: 10;}
.nav .inner {background: linear-gradient(to bottom, var(--site-bg) 70%, transparent 100%); padding: 5px 25px; margin: auto; max-width: 700px; padding-bottom: 25px; transition: padding .1s;}
.nav.top .inner {padding-bottom: 0px;}
.nav .inner::after {content: ""; position: fixed; bottom: 0; left: 0; height: 20px; width: 100%; background: linear-gradient(to top, var(--site-bg) 0%, transparent 100%)}
</style>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div class="container" id="app">
<div class="nav" :class="{'top': top == 0}">
<div class="inner" v-if="stop">
<h3>{{ stop.name }}</h3>
</div>
</div>
<div class="departure-grid">
<div class="header">spoj</div>
<div class="header">poslední zastávka</div>
<div class="header">zpoždění</div>
<div class="header">odjezd</div>
<template v-for="d in departures">
<template v-if="d.departure > -0.7">
<div class="line" :class="'type'+d.type">{{ d.line }}</div>
<div class="last-stop">{{ d.last_stop }}</div>
<div class="delay">{{ d.delay == 0 ? '' : '+'+d.delay }}</div>
<div class="departure">{{ d.departure < 1 ? '<1' : Math.floor(d.departure) }}</div>
</template>
</template>
</div>
</div>
<script>
const { createApp } = Vue;
let app = createApp({
data() {
return {
top: 0,
interval: null,
stop: null,
stops: [],
departures: []
}
},
methods: {
async update() {
if(app.$data.stop) app.$data.departures = (await api("/departures/"+app.$data.stop.id)).departures;
},
async setup() {
app.$data.stops = (await api("/stops")).stops;
if(app.$data.stops.length < 1) {
alert("Žádné zastávky nejsou k dispozici!");
if(app.$data.interval) clearTimeout(app.$data.interval);
return;
}
if(localStorage.getItem("favstop")) {
app.$data.stops.forEach(stop => {
if(stop.id == localStorage.getItem("favstop")) app.$data.stop = stop;
});
}
if(!app.$data.stop) app.$data.stop = app.$data.stops[0];
window.addEventListener("scroll", () => {
app.$data.top = document.querySelector('html').scrollTop;
});
app.$data.interval = setInterval(app.update, 5000);
app.update();
}
}
}).mount('#app');
async function api(url) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if(this.readyState !== 4) return;
if(!this.responseText) reject(this);
try {
resolve(JSON.parse(this.responseText));
} catch(e) {
resolve(null);
}
};
xhr.open("GET", url, true);
xhr.send();
});
}
app.setup();
</script>
</body>
</html>