Add route block

This commit is contained in:
Christoph Hagen
2025-04-29 16:56:46 +02:00
parent bbb1143600
commit 3c7681b769
13 changed files with 446 additions and 9 deletions

View File

@ -19,9 +19,41 @@ struct PageImage: HtmlProducer {
/// The optional caption text below the fullscreen image
let caption: String?
/// The id of the image container
let id: String?
/// Additional class names for the image container
let className: String?
/// Additional content for the image container
let imageContent: String?
init(imageId: String, thumbnail: ImageSet, largeImage: ImageSet, caption: String?, id: String? = nil, className: String? = nil, imageContent: String? = nil) {
self.imageId = imageId
self.thumbnail = thumbnail
self.largeImage = largeImage
self.caption = caption
self.id = id
self.className = className
self.imageContent = imageContent
}
var idString: String {
guard let id else { return "" }
return " id='\(id)'"
}
var classString: String {
guard let className else { return "" }
return " \(className)"
}
func populate(_ result: inout String) {
result += "<div class='content-image' onclick=\"document.getElementById('\(imageId)').classList.add('active')\">"
result += "<div\(idString) class='content-image\(classString)' onclick=\"document.getElementById('\(imageId)').classList.add('active')\">"
result += thumbnail.content
if let imageContent {
result += imageContent
}
result += "</div>"
result += "<div id='\(imageId)' class='fullscreen-image' onclick=\"document.getElementById('\(imageId)').classList.remove('active')\">"
result += largeImage.content

View File

@ -0,0 +1,50 @@
struct RouteLocalization {
let elevation: String
let speed: String
let pace: String
let heartRate: String
let fallback: String
let hourUnit: String
let duration: String
let time: String
let distance: String
let loadFail: String
}
extension RouteLocalization {
static let german: RouteLocalization = .init(
elevation: "Höhe",
speed: "Geschw.",
pace: "Pace",
heartRate: "Herzfrequenz",
fallback: "Zur Anzeige der Statistiken wird JavaScript und Unterstützung für HTML5 Canvas benötigt.",
hourUnit: "Std",
duration: "Dauer",
time: "Zeit",
distance: "Distanz",
loadFail: "Die Statistiken konnten nicht geladen werden")
static let english: RouteLocalization = .init(
elevation: "Elevation",
speed: "Speed",
pace: "Pace",
heartRate: "Heart Rate",
fallback: "Javascript and HTML5 Canvas Support are required to display statistics",
hourUnit: "h",
duration: "Duration",
time: "Time",
distance: "Distance",
loadFail: "The statistics could not be loaded")
}

View File

@ -0,0 +1,7 @@
enum RouteViewComponents: String {
case onlyElevation = "elevation"
case all = "all"
case withoutHeartRate = "no-hr"
}

View File

@ -0,0 +1,98 @@
struct RouteViews: HtmlProducer {
let localization: RouteLocalization
/// The HTML id attribute used to enable fullscreen images
let map: PageImage
let components: RouteViewComponents
let mapId: String
let mapTitle: String?
let filePath: String
let chartId: String
let chartTitle: String?
init(localization: RouteLocalization,
chartTitle: String?,
chartId: String,
components: RouteViewComponents,
mapTitle: String?,
mapId: String,
filePath: String,
imageId: String,
thumbnail: ImageSet,
largeImage: ImageSet,
caption: String?
) {
self.localization = localization
self.components = components
self.mapId = mapId
self.filePath = filePath
self.map = PageImage(
imageId: imageId,
thumbnail: thumbnail,
largeImage: largeImage,
caption: caption,
id: mapId,
className: "map-container",
imageContent: "<div class='marker'></div>")
self.chartTitle = chartTitle
self.chartId = chartId
self.mapTitle = mapTitle
}
var pickerHiddenText: String {
guard components == .onlyElevation else { return "" }
return " style='display:none'"
}
func populate(_ result: inout String) {
if let mapTitle {
result += "<h2>\(mapTitle)</h2>"
}
map.populate(&result)
if let chartTitle {
result += "<h2>\(chartTitle)</h2>"
}
result += "<div id='\(chartId)' class='charts'>"
result += "<div class='picker y-picker'\(pickerHiddenText)>"
result += "<button data-type='elevation' unit='m' class='active'>\(localization.elevation)</button>"
result += "<button data-type='speed' unit='km/h'>\(localization.speed)</button>"
result += "<button data-type='pace' unit='min/km'>\(localization.pace)</button>"
if components == .all {
result += "<button data-type='hr' unit='bpm'>\(localization.heartRate)</button>"
}
result += "</div>"
result += "<div class='graph'>"
result += "<canvas>"
result += "<div class='fallback'>\(localization.fallback)</div>"
result += "</canvas>"
result += "<div class='line'></div>"
result += "<div class='tooltip'></div>"
result += "<div class='load-error'>\(localization.loadFail)</div>"
result += "</div>"
result += "<div class='picker x-picker'\(pickerHiddenText)>"
result += "<button data-type='distance' unit='km' class='active'>\(localization.distance)</button>"
result += "<button data-type='duration' unit='\(localization.hourUnit)'>\(localization.duration)</button>"
result += "<button data-type='time' unit=''>\(localization.time)</button>"
result += "</div>"
result += "</div>"
}
var script: String {
"""
<script>
window.addEventListener('DOMContentLoaded', () => {
initializeGraphs(document, '\(mapId)', '\(chartId)', '\(filePath)');
});
</script>
"""
}
}