First version

This commit is contained in:
Christoph Hagen
2024-10-14 19:22:32 +02:00
parent 7c812de089
commit 0989f06d87
51 changed files with 2477 additions and 234 deletions

View File

@ -0,0 +1,41 @@
import Foundation
struct FeedEntry {
let data: FeedEntryData
init(data: FeedEntryData) {
self.data = data
}
func addContent(to result: inout String) {
#warning("TODO: Select CSS classes based on existence of link (hover effects, mouse pointer")
result += "<div class='card'>"
ImageGallery(id: data.entryId, images: data.images)
.addContent(to: &result)
if let url = data.link?.url {
result += "<div class=\"card-content\" onclick=\"window.location.href='\(url)'\">"
} else {
result += "<div class=\"card-content\">"
}
result += "<h3>\(data.textAboveTitle)</h3>"
if let title = data.title {
result += "<h2>\(title.htmlEscaped())</h2>"
}
if !data.tags.isEmpty {
result += "<div class=\"tags\">"
for tag in data.tags {
result += "<a class=\"tag\" href=\"\(tag.url)\">\(tag.name)</a>"
}
result += "</div>"
}
for paragraph in data.text {
result += "<p>\(paragraph)</p>"
}
if let url = data.link {
result += "<div class=\"link-center\"><div class=\"link\">\(url.text)</div></div>"
}
result += "</div></div>" // Closes card-content and card
}
}

View File

@ -0,0 +1,44 @@
//import Elementary
struct FeedEntryContent<Content> {
let url: String?
let inner: Content
init(url: String?, inner: Content) {
self.url = url
self.inner = inner
}
func addContent(to result: inout String, inner: () -> Void) -> Void {
if let url {
result += "<div class=\"card-content\" onclick=\"window.location.href='\(url)'\">"
} else {
result += "<div class=\"card-content\">"
}
inner()
result += "</div>"
}
}
/*
extension FeedEntryContent: HTML where Content: HTML {
init(url: String?, @HTMLBuilder content: () -> Content) {
self.init(url: url, inner: content())
}
var content: some HTML {
if let url {
div(.class("card-content"), .on(.click, "window.location.href='\(url)'")) {
inner
}
} else {
div(.class("card-content")) {
inner
}
}
}
}
*/

View File

@ -0,0 +1,51 @@
struct FeedEntryData {
let entryId: String
let title: String?
let textAboveTitle: String
let link: Link?
let tags: [Tag]
let text: [String]
let images: [Image]
init(entryId: String, title: String?, textAboveTitle: String, link: Link?, tags: [Tag], text: [String], images: [Image]) {
self.entryId = entryId
self.title = title
self.textAboveTitle = textAboveTitle
self.link = link
self.tags = tags
self.text = text
self.images = images
}
struct Link {
let url: String
let text: String
}
struct Tag {
let name: String
let url: String
}
struct Image {
let mainImageUrl: String
let altText: String
}
}

View File

@ -0,0 +1,83 @@
import Foundation
struct ImageGallery {
let id: String
let images: [FeedEntryData.Image]
init(id: String, images: [FeedEntryData.Image]) {
self.id = id
self.images = images
}
func addContent(to result: inout String) {
guard !images.isEmpty else {
return
}
result += "<div id=\"s\(id)\" class=\"swiper\"><div class=\"swiper-wrapper\">"
guard images.count > 1 else {
let image = images[0]
result += "<div class=\"swiper-slide\"><img src=\(image.mainImageUrl) loading=\"lazy\" alt=\"\(image.altText.htmlEscaped())\"></div>"
result += "</div></div>" // Close swiper, swiper-wrapper
return
}
for image in images {
// TODO: Use different images based on device
result += "<div class=\"swiper-slide\">"
result += "<img src=\(image.mainImageUrl) loading=\"lazy\" alt=\"\(image.altText.htmlEscaped())\">"
result += "<div class=\"swiper-lazy-preloader swiper-lazy-preloader-white\"></div>"
result += "</div>" // Close swiper-slide
}
result += "<div class=\"swiper-button-next\"></div>"
result += "<div class=\"swiper-button-prev\"></div>"
result += "<div class=\"swiper-pagination\"></div>"
result += "</div></div>" // Close swiper, swiper-wrapper
}
static func swiperInit(id: String) -> String {
"""
var swiper\(id) = new Swiper("#\(id)", {
loop: true,
slidesPerView: 1,
spaceBetween: 30,
centeredSlides: true,
keyboard: { enabled: true },
pagination: {
el: "#\(id) .swiper-pagination",
clickable: true
},
navigation: {
nextEl: "#\(id) .swiper-button-next",
prevEl: "#\(id) .swiper-button-prev"
}
});
"""
}
}
/*
extension ImageGallery: HTML {
var content: some HTML {
div(.id(id), .class("swiper")) {
div(.class("swiper-wrapper")) {
for image in images {
div(.class("swiper-slide")) {
// TODO: Use different images based on device
img(.src(image.mainImageUrl), .lazyLoad)
div(.class("swiper-lazy-preloader"), .class("swiper-lazy-preloader-white")) { }
}
}
}
div(.class("swiper-button-next")) { }
div(.class("swiper-button-prev")) { }
div(.class("swiper-pagination")) { }
}
}
}
*/

View File

@ -0,0 +1,36 @@
import Foundation
//import Elementary
struct PageHead {
let title: String
let description: String
var content: String {
"""
<head>
<meta charset="utf-8" />
<title>\(title)</title>
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1" />
<meta name="description" content="\(description)">
<link rel="stylesheet" href="/assets/swiper/swiper.css" />
<link rel="stylesheet" href="/assets/css/style.css" />
</head>
"""
}
}
/*
extension PageHead: HTML {
var content: some HTML {
meta(.charset(.utf8))
meta(.title(title))
meta(.name(.viewport), .content("width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1"))
meta(.name(.description), .content(description))
link(.rel(.stylesheet), .href("style.css"))
link(.rel(.stylesheet), .href("swiper.css"))
}
}
*/