Rework content commands, add audio player
This commit is contained in:
@ -5,10 +5,17 @@ struct AdditionalPageHeaders {
|
||||
|
||||
let assetPath: String
|
||||
|
||||
#warning("Provide paths in settings, import files")
|
||||
var content: String {
|
||||
headers.map { header in
|
||||
let module = header.asModule ? " type='module'" : ""
|
||||
return "<script\(module) src='\(assetPath)/\(header.rawValue)'></script>"
|
||||
}.sorted().joined()
|
||||
headers.map(header).sorted().joined()
|
||||
}
|
||||
|
||||
private func header(for asset: HeaderFile) -> String {
|
||||
let file = asset.rawValue
|
||||
guard file.hasSuffix(".js") else {
|
||||
return "<link rel='stylesheet' type='text/css' href='\(assetPath)/css/\(file)'>"
|
||||
}
|
||||
let module = asset.asModule ? " type='module'" : ""
|
||||
return "<script\(module) src='\(assetPath)/js/\(file)'></script>"
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,128 @@
|
||||
|
||||
struct AudioPlayer: HtmlProducer {
|
||||
|
||||
let playingText: String
|
||||
|
||||
let items: [PlaylistItem]
|
||||
|
||||
private var top: String {
|
||||
"""
|
||||
<div class='top'>
|
||||
<div> </div>
|
||||
<div class='top-center'>\(playingText)</div>
|
||||
<div class='show-playlist'><svg><use href='#\(AudioPlayerPlaylistIcon.name)'></use></svg></div>
|
||||
</div>
|
||||
"""
|
||||
}
|
||||
|
||||
private var center: String {
|
||||
"""
|
||||
<div class='center'>
|
||||
<img data-amplitude-song-info='cover_art_url' class='main-album-art'/>
|
||||
<div class='song-meta-data'>
|
||||
<span data-amplitude-song-info='name' class='song-name'></span>
|
||||
<span data-amplitude-song-info='artist' class='song-artist'></span>
|
||||
</div>
|
||||
<div class='time-progress'>
|
||||
<div id='progress-container'>
|
||||
<input type='range' class='amplitude-song-slider'/>
|
||||
<progress id='song-played-progress' class='amplitude-song-played-progress'></progress>
|
||||
<progress id='song-buffered-progress' class='amplitude-buffered-progress' value='0'></progress>
|
||||
</div>
|
||||
<div class='time-container'>
|
||||
<span class='current-time'>
|
||||
<span class='amplitude-current-hours'></span>:<span class='amplitude-current-minutes'></span>:<span class='amplitude-current-seconds'></span>
|
||||
</span>
|
||||
<span class='duration'>
|
||||
<span class='amplitude-duration-hours'></span>:<span class='amplitude-duration-minutes'></span>:<span class='amplitude-duration-seconds'></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
}
|
||||
|
||||
private var controls: String {
|
||||
"""
|
||||
<div id="audio-player-controls">
|
||||
<svg id="previous" class="amplitude-prev"><use href='#\(AudioPlayerPreviousIcon.name)'></use></svg>
|
||||
<div class="amplitude-play-pause" id="play-pause">
|
||||
<svg class="play-icon"><use href='#\(AudioPlayerPlayIcon.name)'></use></svg>
|
||||
<svg class="pause-icon"><use href='#\(AudioPlayerPauseIcon.name)'></use></svg>
|
||||
</div>
|
||||
<svg id="next" class="amplitude-next"><use href='#\(AudioPlayerNextIcon.name)'></use></svg>
|
||||
</div>
|
||||
"""
|
||||
}
|
||||
|
||||
private var playlistStart: String {
|
||||
"""
|
||||
<div id="playlist-container">
|
||||
<div class="top">
|
||||
<div class="queue">Playlist</div>
|
||||
<div class="close-playlist"><svg><use href='#\(AudioPlayerCloseIcon.name)'></use></svg></div>
|
||||
</div>
|
||||
<div class="playlist">
|
||||
"""
|
||||
}
|
||||
|
||||
private var playlistEnd: String {
|
||||
"""
|
||||
</div>
|
||||
|
||||
<div class="white-player-playlist-controls">
|
||||
<img data-amplitude-song-info="cover_art_url" class="playlist-album-art"/>
|
||||
<div class="playlist-controls">
|
||||
<div class="playlist-meta-data">
|
||||
<span data-amplitude-song-info="name" class="song-name"></span>
|
||||
<span data-amplitude-song-info="artist" class="song-artist"></span>
|
||||
</div>
|
||||
<div class="playlist-control-wrapper">
|
||||
<svg class="amplitude-prev" id="playlist-previous"><use href='#\(AudioPlayerPreviousIcon.name)'></use></svg>
|
||||
<div class="amplitude-play-pause" id="playlist-play-pause">
|
||||
<svg class="play-icon"><use href='#\(AudioPlayerPlayIcon.name)'></use></svg>
|
||||
<svg class="pause-icon"><use href='#\(AudioPlayerPauseIcon.name)'></use></svg>
|
||||
</div>
|
||||
<svg class="amplitude-next" id="playlist-next"><use href='#\(AudioPlayerNextIcon.name)'></use></svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
}
|
||||
|
||||
func populate(_ result: inout String) {
|
||||
result += "<div class='audio-player'>"
|
||||
result += top
|
||||
result += center
|
||||
result += controls
|
||||
result += playlistStart
|
||||
for item in items {
|
||||
result += item.content
|
||||
}
|
||||
result += playlistEnd
|
||||
result += "</div>"
|
||||
}
|
||||
|
||||
struct PlaylistItem {
|
||||
|
||||
let index: Int
|
||||
|
||||
let image: String
|
||||
|
||||
let name: String
|
||||
|
||||
let album: String
|
||||
|
||||
let track: Int
|
||||
|
||||
let artist: String
|
||||
|
||||
var content: String {
|
||||
"""
|
||||
<div class="playlist-song amplitude-song-container amplitude-play-pause amplitude-paused" data-amplitude-song-index="\(index)"><img src="\(image)"><div class="playlist-song-meta"><span class="playlist-song-name">\(name)</span><span class="playlist-song-artist">\(album) • \(artist)</span></div></div>
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
import Foundation
|
||||
|
||||
struct AmplitudeSong: Codable {
|
||||
let name: String
|
||||
let artist: String
|
||||
let album: String
|
||||
let track: String
|
||||
let url: String
|
||||
let cover_art_url: String
|
||||
}
|
||||
|
||||
struct AudioPlayerScript: HtmlProducer {
|
||||
|
||||
let items: [AmplitudeSong]
|
||||
|
||||
init(items: [AmplitudeSong]) {
|
||||
self.items = items
|
||||
}
|
||||
|
||||
func populate(_ result: inout String) {
|
||||
result += "<script>\n"
|
||||
result += "Amplitude.init({ songs: "
|
||||
let songData = try! JSONEncoder().encode(items)
|
||||
result += String(data: songData, encoding: .utf8)!
|
||||
result += "});"
|
||||
result += "function playEntry(index) { Amplitude.playSongAtIndex(index) };"
|
||||
result += animatePlaylist
|
||||
result += "</script>"
|
||||
}
|
||||
|
||||
private var animatePlaylist: String {
|
||||
"""
|
||||
const el = document.getElementById('playlist-container')
|
||||
document.getElementsByClassName('show-playlist')[0].addEventListener('click', function(){
|
||||
el.classList.remove('slide-out-top');
|
||||
el.classList.add('slide-in-top');
|
||||
el.style.display = "block";
|
||||
});
|
||||
document.getElementsByClassName('close-playlist')[0].addEventListener('click', function(){
|
||||
el.classList.remove('slide-in-top');
|
||||
el.classList.add('slide-out-top');
|
||||
el.style.display = "none";
|
||||
});
|
||||
"""
|
||||
}
|
||||
}
|
@ -5,17 +5,20 @@ struct ContentButtons {
|
||||
|
||||
let icon: PageIcon
|
||||
|
||||
let filePath: String
|
||||
let filePath: String?
|
||||
|
||||
let text: String
|
||||
|
||||
let downloadFileName: String?
|
||||
|
||||
init(icon: PageIcon, filePath: String, text: String, downloadFileName: String? = nil) {
|
||||
let onClickText: String?
|
||||
|
||||
init(icon: PageIcon, filePath: String?, text: String, downloadFileName: String? = nil, onClickText: String? = nil) {
|
||||
self.icon = icon
|
||||
self.filePath = filePath
|
||||
self.text = text
|
||||
self.downloadFileName = downloadFileName
|
||||
self.onClickText = onClickText
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,8 +39,10 @@ struct ContentButtons {
|
||||
|
||||
private func addButton(of item: Item, to result: inout String) {
|
||||
let downloadText = item.downloadFileName.map { " download='\($0)'" } ?? ""
|
||||
result += "<a class='tag' href='\(item.filePath)'\(downloadText)>"
|
||||
result += "<svg><use href='#\(item.icon.name)'></use></svg>\(item.text)"
|
||||
let linkText = item.filePath.map { " href='\($0)'" } ?? ""
|
||||
let onClickText = item.onClickText.map { " onClick='\($0)'" } ?? ""
|
||||
result += "<a class='tag'\(linkText)\(downloadText)\(onClickText)>"
|
||||
result += "<svg><use href='#\(item.icon.icon.name)'></use></svg>\(item.text)"
|
||||
result += "</a>"
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,28 @@
|
||||
|
||||
struct ContentLabel {
|
||||
|
||||
let icon: PageIcon
|
||||
|
||||
let value: String
|
||||
}
|
||||
|
||||
struct ContentLabels {
|
||||
|
||||
private let labels: [ContentLabel]
|
||||
|
||||
init(labels: [ContentLabel]) {
|
||||
self.labels = labels
|
||||
}
|
||||
|
||||
var content: String {
|
||||
guard !labels.isEmpty else {
|
||||
return ""
|
||||
}
|
||||
var result = "<div class='labels-container'>"
|
||||
for label in labels {
|
||||
result += "<div><svg><use href='#\(label.icon.icon.name)'></use></svg>\(label.value)</div>"
|
||||
}
|
||||
result += "</div>"
|
||||
return result
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
|
||||
struct HikingStatistics {
|
||||
|
||||
private let time: String?
|
||||
|
||||
private let elevationUp: String?
|
||||
|
||||
private let elevationDown: String?
|
||||
|
||||
private let distance: String?
|
||||
|
||||
private let calories: String?
|
||||
|
||||
init(time: String?, elevationUp: String?, elevationDown: String?, distance: String?, calories: String?) {
|
||||
self.time = time
|
||||
self.elevationUp = elevationUp
|
||||
self.elevationDown = elevationDown
|
||||
self.distance = distance
|
||||
self.calories = calories
|
||||
}
|
||||
|
||||
var content: String {
|
||||
var result = "<div class='stats-container'>"
|
||||
if let time {
|
||||
result += "<div><svg><use href='#icon-clock'></use></svg>\(time)</div>"
|
||||
}
|
||||
if let elevationUp {
|
||||
result += "<div><svg><use href='#icon-arrow-up'></use></svg>\(elevationUp)</div>"
|
||||
}
|
||||
if let elevationDown {
|
||||
result += "<div><svg><use href='#icon-arrow-down'></use></svg>\(elevationDown)</div>"
|
||||
}
|
||||
if let distance {
|
||||
result += "<div><svg><use href='#icon-sign'></use></svg>\(distance)</div>"
|
||||
}
|
||||
if let calories {
|
||||
result += "<div><svg><use href='#icon-flame'></use></svg>\(calories)</div>"
|
||||
}
|
||||
result += "</div>"
|
||||
return result
|
||||
}
|
||||
}
|
@ -1,107 +0,0 @@
|
||||
|
||||
enum PageIcon: CaseIterable {
|
||||
|
||||
case time
|
||||
|
||||
case elevationUp
|
||||
|
||||
case elevationDown
|
||||
|
||||
case distance
|
||||
|
||||
case calories
|
||||
|
||||
case download
|
||||
|
||||
case externalLink
|
||||
|
||||
case gitLink
|
||||
|
||||
var icon: String {
|
||||
switch self {
|
||||
case .time: return PageIcon.timeIcon
|
||||
case .elevationUp: return PageIcon.elevationUpIcon
|
||||
case .elevationDown: return PageIcon.elevationDownIcon
|
||||
case .distance: return PageIcon.distanceIcon
|
||||
case .calories: return PageIcon.caloriesIcon
|
||||
case .download: return PageIcon.downloadIcon
|
||||
case .externalLink: return PageIcon.externalLinkIcon
|
||||
case .gitLink: return PageIcon.gitLinkIcon
|
||||
}
|
||||
}
|
||||
|
||||
var name: String {
|
||||
switch self {
|
||||
case .time: return "icon-clock"
|
||||
case .elevationUp: return "icon-arrow-up"
|
||||
case .elevationDown: return "icon-arrow-down"
|
||||
case .distance: return "icon-sign"
|
||||
case .calories: return "icon-flame"
|
||||
case .download: return "icon-download"
|
||||
case .externalLink: return "icon-external"
|
||||
case .gitLink: return "icon-git"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension PageIcon {
|
||||
|
||||
|
||||
private static let timeIcon =
|
||||
"""
|
||||
<svg id="icon-clock" width="16" height="16" viewBox="0 0 16 16">
|
||||
<path fill="currentColor" d="M8 3.5a.5.5 0 0 0-1 0V9a.5.5 0 0 0 .3.4l3.5 2a.5.5 0 0 0 .4-.8L8 8.7V3.5z"/>
|
||||
<path fill="currentColor" d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm7-8A7 7 0 1 1 1 8a7 7 0 0 1 14 0z"/>
|
||||
</svg>
|
||||
"""
|
||||
|
||||
private static let elevationUpIcon =
|
||||
"""
|
||||
<svg id="icon-arrow-up" width="16" height="16">
|
||||
<path fill="currentColor" d="m14 2.5a.5.5 0 0 0 -.5-.5h-6a.5.5 0 0 0 0 1h4.8l-10.16 10.15a.5.5 0 0 0 .7.7l10.16-10.14v4.79a.5.5 0 0 0 1 0z"/>
|
||||
</svg>
|
||||
"""
|
||||
|
||||
private static let elevationDownIcon =
|
||||
"""
|
||||
<svg id="icon-arrow-down" width="16" height="16">
|
||||
<path fill="currentColor" fill-rule="evenodd" d="M14 13.5a.5.5 0 0 1-.5.5h-6a.5.5 0 0 1 0-1h4.8L2.14 2.85a.5.5 0 1 1 .7-.7L13 12.29V7.5a.5.5 0 0 1 1 0v6z"/>
|
||||
</svg>
|
||||
"""
|
||||
|
||||
private static let distanceIcon =
|
||||
"""
|
||||
<svg id="icon-sign" width="16" height="16">
|
||||
<path fill="currentColor" d="M7 1.4V4H2a1 1 0 0 0-1 1v4a1 1 0 0 0 1 1h5v6h2v-6h3.5a1 1 0 0 0 .8-.4l2-2.3a.5.5 0 0 0 0-.6l-2-2.3a1 1 0 0 0-.8-.4H9V1.4a1 1 0 0 0-2 0zM12.5 5l1.7 2-1.7 2H2V5h10.5z"/>
|
||||
</svg>
|
||||
|
||||
"""
|
||||
|
||||
private static let caloriesIcon =
|
||||
"""
|
||||
<svg id="icon-flame" width="16" height="16">
|
||||
<path fill="currentColor" d="M8 16c3.3 0 6-2 6-5.5 0-1.5-.5-4-2.5-6 .3 1.5-1.3 2-1.3 2C11 4 9 .5 6 0c.4 2 .5 4-2 6-1.3 1-2 2.7-2 4.5C2 14 4.7 16 8 16Zm0-1c-1.7 0-3-1-3-2.8 0-.7.3-2 1.3-3-.2.8.7 1.3.7 1.3-.4-1.3.5-3.3 2-3.5-.2 1-.3 2 1 3a3 3 0 0 1 1 2.3C11 14 9.7 15 8 15Z"/>
|
||||
</svg>
|
||||
"""
|
||||
|
||||
private static let downloadIcon: String =
|
||||
"""
|
||||
<svg id="icon-download" viewBox="0 0 40 40">
|
||||
<path fill="currentColor" fill-rule="evenodd" stroke="none" d="M20 40a20 20 0 1 1 20-20 20 20 0 0 1-20 20zm0-36.8A16.8 16.8 0 1 0 36.8 20 16.8 16.8 0 0 0 20 3.2zm.8 27a1 1 0 0 1-1.6 0L12.1 21c-.4-.4 0-1 .7-1H17v-8.7a.8.8 0 0 1 .8-.8h4.4a.8.8 0 0 1 .8.8V20h4.2c.6 0 1.1.5.7 1l-7.1 9.2z"/>
|
||||
</svg>
|
||||
"""
|
||||
|
||||
private static let externalLinkIcon: String =
|
||||
"""
|
||||
<svg id="icon-external" viewBox="0 0 16 16">
|
||||
<path fill="currentColor" d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8m7.5-6.923c-.67.204-1.335.82-1.887 1.855A8 8 0 0 0 5.145 4H7.5zM4.09 4a9.3 9.3 0 0 1 .64-1.539 7 7 0 0 1 .597-.933A7.03 7.03 0 0 0 2.255 4zm-.582 3.5c.03-.877.138-1.718.312-2.5H1.674a7 7 0 0 0-.656 2.5zM4.847 5a12.5 12.5 0 0 0-.338 2.5H7.5V5zM8.5 5v2.5h2.99a12.5 12.5 0 0 0-.337-2.5zM4.51 8.5a12.5 12.5 0 0 0 .337 2.5H7.5V8.5zm3.99 0V11h2.653c.187-.765.306-1.608.338-2.5zM5.145 12q.208.58.468 1.068c.552 1.035 1.218 1.65 1.887 1.855V12zm.182 2.472a7 7 0 0 1-.597-.933A9.3 9.3 0 0 1 4.09 12H2.255a7 7 0 0 0 3.072 2.472M3.82 11a13.7 13.7 0 0 1-.312-2.5h-2.49c.062.89.291 1.733.656 2.5zm6.853 3.472A7 7 0 0 0 13.745 12H11.91a9.3 9.3 0 0 1-.64 1.539 7 7 0 0 1-.597.933M8.5 12v2.923c.67-.204 1.335-.82 1.887-1.855q.26-.487.468-1.068zm3.68-1h2.146c.365-.767.594-1.61.656-2.5h-2.49a13.7 13.7 0 0 1-.312 2.5m2.802-3.5a7 7 0 0 0-.656-2.5H12.18c.174.782.282 1.623.312 2.5zM11.27 2.461c.247.464.462.98.64 1.539h1.835a7 7 0 0 0-3.072-2.472c.218.284.418.598.597.933M10.855 4a8 8 0 0 0-.468-1.068C9.835 1.897 9.17 1.282 8.5 1.077V4z"/>
|
||||
</svg>
|
||||
"""
|
||||
|
||||
private static let gitLinkIcon: String =
|
||||
"""
|
||||
<svg id="icon-git" viewBox="0 0 16 16">
|
||||
<path fill="currentColor" d="M15.698 7.287 8.712.302a1.03 1.03 0 0 0-1.457 0l-1.45 1.45 1.84 1.84a1.223 1.223 0 0 1 1.55 1.56l1.773 1.774a1.224 1.224 0 0 1 1.267 2.025 1.226 1.226 0 0 1-2.002-1.334L8.58 5.963v4.353a1.226 1.226 0 1 1-1.008-.036V5.887a1.226 1.226 0 0 1-.666-1.608L5.093 2.465l-4.79 4.79a1.03 1.03 0 0 0 0 1.457l6.986 6.986a1.03 1.03 0 0 0 1.457 0l6.953-6.953a1.03 1.03 0 0 0 0-1.457"/>
|
||||
</svg>
|
||||
"""
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
|
||||
struct AudioPlayerPlaylistIcon: ContentIcon {
|
||||
|
||||
static let name = "icon-playlist"
|
||||
|
||||
static let content = """
|
||||
<svg id='icon-playlist' viewBox="0 0 28 20"><g fill="none"><rect width="15" height="4" x="13" fill="currentColor" rx="2"/><g fill="currentColor"><path d="M0 1.2C0 .7.4.4.8.7l9.3 5.8c.5.3.5.7 0 1L.8 13.3c-.4.2-.8 0-.8-.5z"/><rect width="14" height="4" x="14" y="8" rx="2"/><rect width="28" height="4" y="16" rx="2"/></g></g></svg>
|
||||
"""
|
||||
}
|
||||
|
||||
struct AudioPlayerCloseIcon: ContentIcon {
|
||||
|
||||
static let name = "icon-close"
|
||||
|
||||
static let content = """
|
||||
<svg id='icon-close' viewBox="0 0 18 18"><path fill="currentColor" d="M9 6.194 3.392.586A1.986 1.986 0 0 0 .582.582c-.78.78-.773 2.033.004 2.81L6.194 9 .586 14.608a1.986 1.986 0 0 0-.004 2.81c.78.78 2.033.773 2.81-.004L9 11.806l5.608 5.608a1.986 1.986 0 0 0 2.81.004c.78-.78.773-2.033-.004-2.81L11.806 9l5.608-5.608a1.986 1.986 0 0 0 .004-2.81 1.982 1.982 0 0 0-2.81.004z"/></svg>
|
||||
"""
|
||||
}
|
||||
|
||||
struct AudioPlayerPauseIcon: ContentIcon {
|
||||
|
||||
static let name = "icon-pause"
|
||||
|
||||
static let content = """
|
||||
<svg id='icon-pause' viewBox="0 0 85 85"><g fill="none"><circle cx="42.5" cy="42.5" fill="currentColor" r="42.5"/><path d="m34 55h6v-24h-6zm12 0h6v-24h-6z" fill="#fff" stroke="#fff"/></g></svg>
|
||||
"""
|
||||
}
|
||||
|
||||
struct AudioPlayerPlayIcon: ContentIcon {
|
||||
|
||||
static let name = "icon-play"
|
||||
|
||||
static let content = """
|
||||
<svg id='icon-play' viewBox="0 0 85 85"><g fill="none"><circle cx="42.5" cy="42.5" r="42.5" fill="currentColor"/><path fill="#fff" d="M33.3 31.3c0-2.3 1.5-3.1 3.4-2l18.8 11.5c2 1.1 2 3 0 4.1L36.7 56.3c-1.9 1.2-3.4.3-3.4-1.9z"/></g>
|
||||
</svg>
|
||||
"""
|
||||
}
|
||||
|
||||
struct AudioPlayerPreviousIcon: ContentIcon {
|
||||
|
||||
static let name = "icon-previous"
|
||||
|
||||
static let content = """
|
||||
<svg id='icon-previous' viewBox="0 0 53 53"><g fill="none" transform="matrix(-1 0 0 1 53 0)"><circle cx="26.5" cy="26.5" r="26.5" fill="currentColor"/><g fill="#fff" transform="translate(16 17)"><path d="M.4 1.8C.4.6 1.2.2 2.2.8l12.3 7.5c1 .5 1 1.5 0 2L2.2 17.8c-1 .6-1.8.1-1.8-1z"/><rect width="3" height="17" x="18" y="1" rx="1.5"/></g></g></svg>
|
||||
"""
|
||||
}
|
||||
|
||||
struct AudioPlayerNextIcon: ContentIcon {
|
||||
|
||||
static let name = "icon-next"
|
||||
|
||||
static let content = """
|
||||
<svg id='icon-next' viewBox="0 0 53 53"><g fill="none"><circle cx="26.5" cy="26.5" r="26.5" fill="currentColor"/><g fill="#fff" transform="translate(16 17)"><path d="M.4 1.8C.4.6 1.2.2 2.2.8l12.3 7.5c1 .5 1 1.5 0 2L2.2 17.8c-1 .6-1.8.1-1.8-1z"/><rect width="3" height="17" x="18" y="1" rx="1.5"/></g></g></svg>
|
||||
"""
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
|
||||
struct ButtonDownloadIcon: ContentIcon {
|
||||
|
||||
static let name = "icon-download"
|
||||
|
||||
static let content = """
|
||||
<svg id="icon-download" viewBox="0 0 40 40"><path fill="currentColor" fill-rule="evenodd" stroke="none" d="M20 40a20 20 0 1 1 20-20 20 20 0 0 1-20 20zm0-36.8A16.8 16.8 0 1 0 36.8 20 16.8 16.8 0 0 0 20 3.2zm.8 27a1 1 0 0 1-1.6 0L12.1 21c-.4-.4 0-1 .7-1H17v-8.7a.8.8 0 0 1 .8-.8h4.4a.8.8 0 0 1 .8.8V20h4.2c.6 0 1.1.5.7 1l-7.1 9.2z"/></svg>
|
||||
"""
|
||||
}
|
||||
|
||||
struct ButtonExternalIcon: ContentIcon {
|
||||
|
||||
static let name = "icon-external"
|
||||
|
||||
static let content = """
|
||||
<svg id="icon-external" viewBox="0 0 16 16"><path fill="currentColor" d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8m7.5-6.923c-.67.204-1.335.82-1.887 1.855A8 8 0 0 0 5.145 4H7.5zM4.09 4a9.3 9.3 0 0 1 .64-1.539 7 7 0 0 1 .597-.933A7.03 7.03 0 0 0 2.255 4zm-.582 3.5c.03-.877.138-1.718.312-2.5H1.674a7 7 0 0 0-.656 2.5zM4.847 5a12.5 12.5 0 0 0-.338 2.5H7.5V5zM8.5 5v2.5h2.99a12.5 12.5 0 0 0-.337-2.5zM4.51 8.5a12.5 12.5 0 0 0 .337 2.5H7.5V8.5zm3.99 0V11h2.653c.187-.765.306-1.608.338-2.5zM5.145 12q.208.58.468 1.068c.552 1.035 1.218 1.65 1.887 1.855V12zm.182 2.472a7 7 0 0 1-.597-.933A9.3 9.3 0 0 1 4.09 12H2.255a7 7 0 0 0 3.072 2.472M3.82 11a13.7 13.7 0 0 1-.312-2.5h-2.49c.062.89.291 1.733.656 2.5zm6.853 3.472A7 7 0 0 0 13.745 12H11.91a9.3 9.3 0 0 1-.64 1.539 7 7 0 0 1-.597.933M8.5 12v2.923c.67-.204 1.335-.82 1.887-1.855q.26-.487.468-1.068zm3.68-1h2.146c.365-.767.594-1.61.656-2.5h-2.49a13.7 13.7 0 0 1-.312 2.5m2.802-3.5a7 7 0 0 0-.656-2.5H12.18c.174.782.282 1.623.312 2.5zM11.27 2.461c.247.464.462.98.64 1.539h1.835a7 7 0 0 0-3.072-2.472c.218.284.418.598.597.933M10.855 4a8 8 0 0 0-.468-1.068C9.835 1.897 9.17 1.282 8.5 1.077V4z"/></svg>
|
||||
"""
|
||||
}
|
||||
|
||||
struct ButtonGitIcon: ContentIcon {
|
||||
|
||||
static let name = "icon-git"
|
||||
|
||||
static let content = """
|
||||
<svg id="icon-git" viewBox="0 0 16 16"><path fill="currentColor" d="M15.698 7.287 8.712.302a1.03 1.03 0 0 0-1.457 0l-1.45 1.45 1.84 1.84a1.223 1.223 0 0 1 1.55 1.56l1.773 1.774a1.224 1.224 0 0 1 1.267 2.025 1.226 1.226 0 0 1-2.002-1.334L8.58 5.963v4.353a1.226 1.226 0 1 1-1.008-.036V5.887a1.226 1.226 0 0 1-.666-1.608L5.093 2.465l-4.79 4.79a1.03 1.03 0 0 0 0 1.457l6.986 6.986a1.03 1.03 0 0 0 1.457 0l6.953-6.953a1.03 1.03 0 0 0 0-1.457"/></svg>
|
||||
"""
|
||||
}
|
||||
|
||||
struct ButtonPlayIcon: ContentIcon {
|
||||
|
||||
static let name = "icon-play-circle"
|
||||
|
||||
static let content = """
|
||||
<svg id='icon-play-circle' viewBox="0 0 1000 1000"><g fill="currentColor"><path d="M452.6 11.2A495 495 0 0 0 90.1 229.8 525.5 525.5 0 0 0 19.8 398c-8.3 40.7-9.8 56.6-9.8 101.6s1.5 60.5 9.8 101.5A529.7 529.7 0 0 0 90 769.5 493.9 493.9 0 0 0 499.6 990c185 0 355.6-106 438.6-272.3a486.8 486.8 0 0 0-46.8-512.3A494.2 494.2 0 0 0 568.6 13.9c-24-3.7-91.5-5.4-116-2.7zm85.5 76.4c31 3.1 59.6 9.2 90.6 19.4a413.4 413.4 0 0 1 263.9 264.2 412 412 0 0 1-100.8 420.6A413.7 413.7 0 0 1 460 911.4 415.2 415.2 0 0 1 87.6 538a416.4 416.4 0 0 1 143.7-353.3 417.2 417.2 0 0 1 306.8-97.2z"/><path d="M375.4 291.7c-4.2 2-7.5 5.4-10 11-3.6 8-3.8 14.1-3.8 196.9 0 183 .2 189 3.8 197 5 10.9 14.4 15.3 26 12.2 11.4-3 320-183.1 329.5-192.3 6.9-6.8 7.6-8.3 7.6-17s-.7-10-7.6-16.8c-7.9-7.6-314-187.2-326.5-191.6-8.3-2.9-11.1-2.9-19 .6z"/></g></svg>
|
||||
"""
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
|
||||
protocol ContentIcon {
|
||||
|
||||
static var name: String { get }
|
||||
|
||||
static var content: String { get }
|
||||
}
|
||||
|
||||
extension ContentIcon {
|
||||
|
||||
var name: String {
|
||||
Self.name
|
||||
}
|
||||
|
||||
var content: String {
|
||||
Self.content
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
|
||||
enum PageIcon: String, CaseIterable {
|
||||
|
||||
// MARK: Statistics
|
||||
|
||||
case statisticsTime = "time"
|
||||
|
||||
case statisticsElevationUp = "elevation-up"
|
||||
|
||||
case statisticsElevationDown = "elevation-down"
|
||||
|
||||
case statisticsDistance = "distance"
|
||||
|
||||
case statisticsEnergy = "energy"
|
||||
|
||||
// MARK: Buttons
|
||||
|
||||
case buttonDownload = "download"
|
||||
|
||||
case buttonExternalLink = "external"
|
||||
|
||||
case buttonGitLink = "git"
|
||||
|
||||
case buttonPlay = "play-circle"
|
||||
|
||||
// MARK: Audio player
|
||||
|
||||
case audioPlayerPlaylist = "playlist"
|
||||
|
||||
case audioPlayerClose = "close"
|
||||
|
||||
case audioPlayerPlay = "play"
|
||||
|
||||
case audioPlayerPause = "pause"
|
||||
|
||||
case audioPlayerPrevious = "previous"
|
||||
|
||||
case audioPlayerNext = "next"
|
||||
|
||||
var icon: ContentIcon.Type {
|
||||
switch self {
|
||||
case .statisticsTime: return StatisticsTimeIcon.self
|
||||
case .statisticsElevationUp: return StatisticsElevationUpIcon.self
|
||||
case .statisticsElevationDown: return StatisticsElevationDownIcon.self
|
||||
case .statisticsDistance: return StatisticsDistanceIcon.self
|
||||
case .statisticsEnergy: return StatisticsEnergyIcon.self
|
||||
case .buttonDownload: return ButtonDownloadIcon.self
|
||||
case .buttonExternalLink: return ButtonExternalIcon.self
|
||||
case .buttonGitLink: return ButtonGitIcon.self
|
||||
case .buttonPlay: return ButtonPlayIcon.self
|
||||
case .audioPlayerPlaylist: return AudioPlayerPlaylistIcon.self
|
||||
case .audioPlayerClose: return AudioPlayerCloseIcon.self
|
||||
case .audioPlayerPlay: return AudioPlayerPlayIcon.self
|
||||
case .audioPlayerPause: return AudioPlayerPauseIcon.self
|
||||
case .audioPlayerPrevious: return AudioPlayerPreviousIcon.self
|
||||
case .audioPlayerNext: return AudioPlayerNextIcon.self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension PageIcon: Hashable {
|
||||
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
|
||||
struct StatisticsTimeIcon: ContentIcon {
|
||||
|
||||
static let name = "icon-time"
|
||||
|
||||
static let content = """
|
||||
<svg id="icon-time" width="16" height="16" viewBox="0 0 16 16"><path fill="currentColor" d="M8 3.5a.5.5 0 0 0-1 0V9a.5.5 0 0 0 .3.4l3.5 2a.5.5 0 0 0 .4-.8L8 8.7V3.5z"/><path fill="currentColor" d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm7-8A7 7 0 1 1 1 8a7 7 0 0 1 14 0z"/></svg>
|
||||
"""
|
||||
}
|
||||
|
||||
struct StatisticsElevationUpIcon: ContentIcon {
|
||||
|
||||
static let name = "icon-elevation-up"
|
||||
|
||||
static let content = """
|
||||
<svg id="icon-elevation-up" width="16" height="16"><path fill="currentColor" d="m14 2.5a.5.5 0 0 0 -.5-.5h-6a.5.5 0 0 0 0 1h4.8l-10.16 10.15a.5.5 0 0 0 .7.7l10.16-10.14v4.79a.5.5 0 0 0 1 0z"/></svg>
|
||||
"""
|
||||
}
|
||||
|
||||
struct StatisticsElevationDownIcon: ContentIcon {
|
||||
|
||||
static let name = "icon-elevation-down"
|
||||
|
||||
static let content = """
|
||||
<svg id="icon-elevation-down" width="16" height="16"><path fill="currentColor" fill-rule="evenodd" d="M14 13.5a.5.5 0 0 1-.5.5h-6a.5.5 0 0 1 0-1h4.8L2.14 2.85a.5.5 0 1 1 .7-.7L13 12.29V7.5a.5.5 0 0 1 1 0v6z"/></svg>
|
||||
"""
|
||||
}
|
||||
|
||||
struct StatisticsDistanceIcon: ContentIcon {
|
||||
|
||||
static let name = "icon-distance"
|
||||
|
||||
static let content = """
|
||||
<svg id="icon-distance" width="16" height="16"><path fill="currentColor" d="M7 1.4V4H2a1 1 0 0 0-1 1v4a1 1 0 0 0 1 1h5v6h2v-6h3.5a1 1 0 0 0 .8-.4l2-2.3a.5.5 0 0 0 0-.6l-2-2.3a1 1 0 0 0-.8-.4H9V1.4a1 1 0 0 0-2 0zM12.5 5l1.7 2-1.7 2H2V5h10.5z"/></svg>
|
||||
"""
|
||||
}
|
||||
|
||||
struct StatisticsEnergyIcon: ContentIcon {
|
||||
|
||||
static let name = "icon-energy"
|
||||
|
||||
static let content = """
|
||||
<svg id="icon-energy" width="16" height="16"><path fill="currentColor" d="M8 16c3.3 0 6-2 6-5.5 0-1.5-.5-4-2.5-6 .3 1.5-1.3 2-1.3 2C11 4 9 .5 6 0c.4 2 .5 4-2 6-1.3 1-2 2.7-2 4.5C2 14 4.7 16 8 16Zm0-1c-1.7 0-3-1-3-2.8 0-.7.3-2 1.3-3-.2.8.7 1.3.7 1.3-.4-1.3.5-3.3 2-3.5-.2 1-.3 2 1 3a3 3 0 0 1 1 2.3C11 14 9.7 15 8 15Z"/></svg>
|
||||
"""
|
||||
}
|
Reference in New Issue
Block a user