added search bar + platform select + refresh btn
This commit is contained in:
		
							
								
								
									
										151
									
								
								index.css
									
									
									
									
									
								
							
							
						
						
									
										151
									
								
								index.css
									
									
									
									
									
								
							| @@ -10,8 +10,11 @@ | ||||
|  | ||||
| :root { | ||||
| 	--blue: #2D327D; | ||||
| 	--granite: #686868; | ||||
| 	--graphite: #B7B7B7; | ||||
| 	--cloud: #E5E5E5; | ||||
| 	--white: #FFFFFF; | ||||
| 	--black: #000000; | ||||
| 	--red: #EB0000; | ||||
| 	--red125: #C60018; | ||||
| 	--red150: #A20013; | ||||
| @@ -35,7 +38,48 @@ body { | ||||
| } | ||||
|  | ||||
| #station { | ||||
| 	color: black; | ||||
| 	display: flex; | ||||
| 	gap: 1em; | ||||
| 	align-items: flex-end; | ||||
| 	color: var(--black); | ||||
| 	margin: 1em 0; | ||||
| } | ||||
|  | ||||
| #refresh { | ||||
| 	width: 3.4em; | ||||
| 	height: 3.4em; | ||||
| 	cursor: pointer; | ||||
| 	position: relative; | ||||
| 	background-color: var(--white); | ||||
| 	border: none; | ||||
| 	transition: background-color 0.2s; | ||||
| } | ||||
|  | ||||
| #refresh::after { | ||||
| 	content: ""; | ||||
| 	position: absolute; | ||||
| 	inset: 0; | ||||
| 	width: 100%; | ||||
| 	height: 100%; | ||||
| 	background-color: var(--granite); | ||||
| 	mask-image: url(icons/arrows-circle-medium.svg); | ||||
| 	-webkit-mask-image: url(icons/arrows-circle-medium.svg); | ||||
| 	mask-size: contain; | ||||
| 	-webkit-mask-size: contain; | ||||
| 	mask-position: center; | ||||
| 	-webkit-mask-position: center; | ||||
| 	mask-repeat: no-repeat; | ||||
| 	-webkit-mask-repeat: no-repeat; | ||||
| 	transition: transform 0.2s; | ||||
| 	will-change: transform; | ||||
| } | ||||
|  | ||||
| #refresh:hover { | ||||
| 	background-color: var(--cloud); | ||||
| } | ||||
|  | ||||
| #refresh:hover::after { | ||||
| 	transform: rotate(90deg); | ||||
| } | ||||
|  | ||||
| #grid { | ||||
| @@ -211,3 +255,108 @@ body { | ||||
| 	mask-position: center; | ||||
| 	-webkit-mask-position: center; | ||||
| } | ||||
|  | ||||
| #search { | ||||
| 	display: flex; | ||||
| 	gap: 0.5em; | ||||
| } | ||||
|  | ||||
| #search-input { | ||||
| 	font-family: inherit; | ||||
| 	font-size: 80%; | ||||
| 	padding: 6px 8px 6px 8px; | ||||
| 	border: solid var(--graphite) 1px; | ||||
| } | ||||
|  | ||||
| #search-input:focus-visible { | ||||
| 	outline: none; | ||||
| 	border-color: var(--granite); | ||||
| } | ||||
|  | ||||
| #search-btn { | ||||
| 	font-family: inherit; | ||||
| 	font-size: 80%; | ||||
| 	padding: 11px 40px 11px 40px; | ||||
| 	background-color: var(--red); | ||||
| 	color: var(--white); | ||||
| 	border: none; | ||||
| 	cursor: pointer; | ||||
| } | ||||
|  | ||||
| #search-btn:hover { | ||||
| 	background-color: var(--red125); | ||||
| } | ||||
|  | ||||
| #stations { | ||||
| 	display: flex; | ||||
| 	flex-direction: column; | ||||
| } | ||||
|  | ||||
| #stations .station { | ||||
| 	padding: 0.5em 2em; | ||||
| 	border-bottom: solid var(--graphite) 1px; | ||||
| 	cursor: pointer; | ||||
| 	color: var(--black); | ||||
| } | ||||
|  | ||||
| #stations .station:last-child { | ||||
| 	border-bottom: none; | ||||
| } | ||||
|  | ||||
| #stations .station:hover { | ||||
| 	background-color: var(--cloud); | ||||
| } | ||||
|  | ||||
| .select-wrapper { | ||||
| 	position: relative; | ||||
| 	width: 10em; | ||||
| } | ||||
| .select-wrapper select { | ||||
| 	font-family: inherit; | ||||
| 	font-size: inherit; | ||||
| 	background-color: var(--white); | ||||
| 	border: solid var(--graphite) 1px; | ||||
| 	border-radius: 0; | ||||
| 	padding: 11px 40px 11px 40px; | ||||
| 	color: var(--granite); | ||||
| 	text-align: left; | ||||
| 	appearance: none; | ||||
| 	-webkit-appearance: none; | ||||
| 	width: 100%; | ||||
| 	cursor: pointer; | ||||
| } | ||||
|  | ||||
| .select-wrapper .select-arrow { | ||||
| 	width: 2em; | ||||
| 	height: 100%; | ||||
| 	position: absolute; | ||||
| 	right: 0; | ||||
| 	background-color: var(--granite); | ||||
| 	mask-image: url(icons/chevron-small-down-medium.svg); | ||||
| 	-webkit-mask-image: url(icons/chevron-small-down-medium.svg); | ||||
| 	mask-size: contain; | ||||
| 	-webkit-mask-size: contain; | ||||
| 	mask-position: center; | ||||
| 	-webkit-mask-position: center; | ||||
| 	mask-repeat: no-repeat; | ||||
| 	-webkit-mask-repeat: no-repeat; | ||||
| 	pointer-events: none; | ||||
| } | ||||
|  | ||||
| .select-wrapper select:focus-visible { | ||||
| 	outline: none; | ||||
| 	border-color: var(--granite); | ||||
| } | ||||
|  | ||||
| .field { | ||||
| 	display: flex; | ||||
| 	flex-direction: column; | ||||
| 	gap: 0.2em; | ||||
| } | ||||
|  | ||||
| .field label { | ||||
| 	font-family: inherit; | ||||
| 	font-size: 80%; | ||||
| 	color: var(--granite); | ||||
| 	padding-left: 0.5em; | ||||
| } | ||||
							
								
								
									
										17
									
								
								index.html
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								index.html
									
									
									
									
									
								
							| @@ -15,7 +15,22 @@ | ||||
| 			<div class="services"></div> | ||||
| 		</div> | ||||
| 	</div> | ||||
| 	<h1 id="station">St-Maurice</h1> | ||||
| 	<form id="search"> | ||||
| 		<input type="text" id="search-input" placeholder="Station..."> | ||||
| 		<button id="search-btn">Search</button> | ||||
| 	</form> | ||||
| 	<div id="stations"></div> | ||||
| 	<div id="station"> | ||||
| 		<h1 class="name">St-Maurice</h1> | ||||
| 		<div class="field"> | ||||
| 			<label for="platforms">Platform</label> | ||||
| 			<div class="select-wrapper"> | ||||
| 				<select id="platforms"></select> | ||||
| 				<span class="select-arrow"></span> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 		<button id="refresh"></button> | ||||
| 	</div> | ||||
| 	<div id="grid"> | ||||
| 		<div id="type-time"> | ||||
| 			<img id="train-type" src="icons/ic-61.svg" alt="InterCity 61"> | ||||
|   | ||||
							
								
								
									
										95
									
								
								index.js
									
									
									
									
									
								
							
							
						
						
									
										95
									
								
								index.js
									
									
									
									
									
								
							| @@ -1,12 +1,93 @@ | ||||
| const MAX_DISPLAYED_VIAS = 4 | ||||
| let currentBpuic = null | ||||
| let currentPlatform = null | ||||
| let currentInfo = null | ||||
|  | ||||
| function initSearch() { | ||||
| 	const sInput = document.getElementById("search-input") | ||||
| 	const form = document.getElementById("search") | ||||
| 	const refresh = document.getElementById("refresh") | ||||
| 	const platforms = document.getElementById("platforms") | ||||
|  | ||||
| 	form.addEventListener("submit", e => { | ||||
| 		e.preventDefault() | ||||
| 		searchStation(sInput.value) | ||||
| 			.then(stations => updateStationsList(stations)) | ||||
| 	}) | ||||
|  | ||||
| 	refresh.addEventListener("click", () => { | ||||
| 		if (currentBpuic !== null) { | ||||
| 			setStation(currentBpuic) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	platforms.addEventListener("change", () => { | ||||
| 		setPlatform(platforms.value) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| function updateStationsList(stations) { | ||||
| 	const stationsList = document.getElementById("stations") | ||||
| 	stationsList.innerHTML = "" | ||||
| 	stations.forEach(station => { | ||||
| 		const div = document.createElement("div") | ||||
| 		div.classList.add("station") | ||||
| 		div.innerText = station.bezeichnungOffiziell | ||||
| 		div.addEventListener("click", () => setStation(station.bpuic)) | ||||
| 		stationsList.appendChild(div) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| function setStation(bpuic) { | ||||
| 	if (bpuic !== currentBpuic) { | ||||
| 		currentPlatform = null | ||||
| 	} | ||||
| 	currentBpuic = bpuic | ||||
| 	fetchInfo(bpuic).then(res => { | ||||
| 		currentInfo = res | ||||
| 		document.getElementById("search-input").value = "" | ||||
| 		document.getElementById("stations").innerHTML = "" | ||||
|  | ||||
| 		const platformsList = document.getElementById("platforms") | ||||
| 		platformsList.innerHTML = "" | ||||
|  | ||||
| 		let platforms = res.contents[0].verkehrsmittels.map(train => train.gleisAbIst) | ||||
| 		platforms = [... new Set(platforms)] | ||||
| 		platforms = platforms.sort() | ||||
| 		platforms.forEach(platform => { | ||||
| 			const opt = document.createElement("option") | ||||
| 			opt.value = platform | ||||
| 			opt.innerText = platform | ||||
| 			if (currentPlatform === platform) { | ||||
| 				opt.selected = true | ||||
| 			} | ||||
| 			platformsList.appendChild(opt) | ||||
| 		}) | ||||
|  | ||||
| 		const station = document.getElementById("station") | ||||
| 		station.querySelector(".name").innerText = res.contents[0].betriebspunkt.bezeichnungOffiziell | ||||
| 		updateDisplay(res, 0, currentPlatform) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| function setPlatform(platform) { | ||||
| 	const platforms = document.getElementById("platforms") | ||||
| 	currentPlatform = platforms.value | ||||
| 	if (currentInfo === null) return | ||||
| 	updateDisplay(currentInfo, 0, platform) | ||||
| } | ||||
|  | ||||
| function searchStation(query) { | ||||
| 	return fetch(`https://displays.api.sbb.ch/internal/api/v1/betriebspunkt?query=${query}`, { | ||||
| 		headers: { | ||||
| 			"X-API-Key": API_KEY | ||||
| 		} | ||||
| 	}).then(res => { | ||||
| 		return res.json() | ||||
| 	}).then(async (res) => { | ||||
| 		let stations = [] | ||||
| 		if (res.status === 200) { | ||||
| 			stations = await res.json() | ||||
| 		} | ||||
| 		return stations | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| @@ -47,8 +128,8 @@ const example = { | ||||
| 					}, | ||||
| 					zeitAbKb: "2024-04-08T18:32:00",  // heure de départ prévue | ||||
| 					zeitAbErw: "2024-04-08T18:33:00",  // heure de départ réelle | ||||
| 					gleisAbKb: 1,  // voie prévue | ||||
| 					gleisAbIst: 1,  // voie réelle | ||||
| 					gleisAbKb: "1",  // voie prévue | ||||
| 					gleisAbIst: "1",  // voie réelle | ||||
| 					verspaetungsminutenAb: 0,  // au moins n minutes de retard | ||||
| 					verspaetungsminutenAn: 0,  // au plus n minutes de retard | ||||
| 					ziele: [ | ||||
| @@ -204,8 +285,6 @@ function buildComposition(formation) { | ||||
| } | ||||
|  | ||||
| function updateDisplay(result, idx=0, platform=null) { | ||||
| 	document.getElementById("station").innerText = result.config.stationName | ||||
|  | ||||
| 	let trains = result.contents[0].verkehrsmittels | ||||
| 	if (platform !== null) { | ||||
| 		trains = trains.filter(t => t.gleisAbIst === platform) | ||||
| @@ -236,3 +315,7 @@ function updateDisplay(result, idx=0, platform=null) { | ||||
|  | ||||
| 	buildComposition(train.formation) | ||||
| } | ||||
|  | ||||
| window.addEventListener("load", () => { | ||||
| 	initSearch() | ||||
| }) | ||||
		Reference in New Issue
	
	Block a user