Skip to content

Instantly share code, notes, and snippets.

@Ze0nC
Created May 17, 2021 14:48
Show Gist options
  • Save Ze0nC/6696c87d5324bbe7b7713e26e5cd8403 to your computer and use it in GitHub Desktop.
Save Ze0nC/6696c87d5324bbe7b7713e26e5cd8403 to your computer and use it in GitHub Desktop.
import Foundation
import Publish
import Plot
extension Theme where Site == CzjIo {
static var czj: Self {
Theme(
htmlFactory: CzjHTMLFactory(),
resourcePaths: ["Sources/CzjIo/styles.css"]
)
}
internal struct CzjHTMLFactory: HTMLFactory {
func makeIndexHTML(for index: Index,
context: PublishingContext<Site>) throws -> HTML {
HTML(
.lang(index.language!),
.head(for: index, on: context.site, stylesheetPaths: ["/styles.css"]),
.body {
SiteHeader(location: index, context: context, selectedSelectionID: nil)
Wrapper {
H2("\("Photos and More".localized(in: index.language!))")
ItemList(
items: context.allItems(in: index.language!),
site: context.site,
filter: { $0.sectionID.rawValue == "album" },
column: 3,
max: 6
)
Link("\("Browse all photos".localized(in: index.language!))", url: "/\(context.site.path(for: context.section(.album, in: index.language!)))")
.class("browse-all")
H2("\("Working with Publish".localized(in: index.language!))")
ItemList(
items: context.allItems(in: index.language!),
site: context.site,
filter: { (item) -> Bool in
item.tags.contains(Tag("Publish"))
},
column: 3,
max: 6
)
H2("\("Writing in LaTeX".localized(in: index.language!))")
ItemList(
items: context.allItems(in: index.language!),
site: context.site,
filter: { (item) -> Bool in
item.tags.contains(Tag("LaTeX")) && !item.tags.contains(Tag("macOS"))
},
column: 3,
max: 6
)
H2("\("Research in Chemistry".localized(in: index.language!))")
ItemList(
items: context.allItems(in: index.language!),
site: context.site,
filter: { (item) -> Bool in
item.tags.map{$0.normalizedString()}.contains("Academic".localizedTag(in: index.language!))
},
column: 3,
max: 6
)
H2("\("And More".localized(in: index.language!))")
ItemList(
items: context.allItems(in: index.language!),
site: context.site,
filter: { (item) -> Bool in
!item.tags.contains(Tag("\("Publish".localizedTag(in: index.language!))")) && !(item.tags.contains(Tag("\("LaTeX".localizedTag(in: index.language!))")) && !item.tags.contains(Tag("\("macOS".localizedTag(in: index.language!))"))) && !(item.tags.contains(Tag("\("Academic".localizedTag(in: index.language!))"))) &&
item.sectionID != .album
},
column: 3,
max: 12
)
Link("\("Browse all articles".localized(in: index.language!))", url: "/\(context.site.path(for: context.section(.article, in: index.language!)))")
.class("browse-all")
}
SiteFooter(site: context.site, language: index.language!)
}
)
}
func makeAlbumSectionHTML(for section: Section<Site>,
context: PublishingContext<Site>) throws -> HTML {
let photos = context.sections.filter{$0.id.rawValue == "album"}.first!.items(in: section.language!).sorted(by: { $0.date > $1.date} ).prefix(6).reversed()
return
HTML(
.lang(section.language!),
.head(for: section, on: context.site, stylesheetPaths: ["/styles.css", "/gallery.css"]),
.body {
SiteHeader(location: section, context: context, selectedSelectionID: section.id, title: section.id.rawValue.localized(in: section.language!))
Div {
for photo in photos {
Image("\(photo.imagePath!)")
Paragraph(photo.title)
}
}
.id("cf4a")
.class("index-background")
Div {
ItemList(
items: context.allItems(in: section.language!),
site: context.site,
filter: { $0.sectionID == .album },
column: 5
)
}
.class("wrapper")
.style("max-width: 6000px;")
SiteFooter(site: context.site, language: section.language!)
}
)
}
func makeAboutSectionHTML(for section: Section<Site>,
context: PublishingContext<Site>) throws -> HTML {
HTML(
.lang(section.language!),
.head(for: section, on: context.site, stylesheetPaths: ["/styles.css"]),
.body {
SiteHeader(location: section, context: context, selectedSelectionID: section.id, title: section.id.rawValue.localized(in: section.language!))
Wrapper {
// H1(.text(section.title))
Article {
Div(section.body)
.class("content")
LinkList()
.style(" margin: 12px;")
if section.language! == .chinese {
Paragraph("邮箱:[email protected]")
} else if section.language! == .english {
Paragraph("Contact me: [email protected]")
} else if section.language! == .japanese {
Paragraph("メールアドレス:[email protected]")
}
AboutList(language: section.language!)
}
ItemList(
items: context.allItems(in: section.language!),
site: context.site,
filter: { $0.sectionID == section.id },
column: 3
)
}
SiteFooter(site: context.site, language: section.language!)
}
)
}
func makeSectionHTML(for section: Section<Site>,
context: PublishingContext<Site>) throws -> HTML {
if section.id.rawValue == "album" {
return try makeAlbumSectionHTML(for: section, context: context)
}
if section.id.rawValue == "about" {
return try makeAboutSectionHTML(for: section, context: context)
}
return
HTML(
.lang(section.language!),
.head(for: section, on: context.site, stylesheetPaths: ["/styles.css"]),
.body {
SiteHeader(location: section, context: context, selectedSelectionID: section.id, title: section.id.rawValue.localized(in: section.language!))
Wrapper {
Article {
Div(section.body)
.class("content")
}
ItemList(
items: context.allItems(in: section.language!),
site: context.site,
filter: { $0.sectionID == section.id },
column: 3
)
}
SiteFooter(site: context.site, language: section.language!)
}
)
}
func makeItemHTML(for item: Item<Site>,
context: PublishingContext<Site>) throws -> HTML {
let allItems = context.allItems(in: item.language!).filter({$0.sectionID == item.sectionID})
let itemIndex = allItems.firstIndex(of: item)
let previous: Node<HTML.BodyContext>
let next: Node<HTML.BodyContext>
if allItems.first != item {
let previousItem = allItems[allItems.index(before: itemIndex!)]
previous = .a(
.href(context.site.path(for: previousItem)),
.text("" + previousItem.title),
.style("left: 0px;")
)
} else {
previous = .empty
}
if allItems.last != item {
let nextItem = allItems[allItems.index(after: itemIndex!)]
next = .a(
.href(context.site.path(for: nextItem)),
.text(nextItem.title + ""),
.style("right: 0px;")
)
} else {
next = .empty
}
return HTML(
.lang(item.language!),
.head(for: item, on: context.site, stylesheetPaths: ["/styles.css", "/gallery.css"]),
.body {
SiteHeader(location: item, context: context, selectedSelectionID: item.sectionID, backgroundImage: item.imagePath?.string)
Wrapper {
Article {
H1(item.title)
.class("hide-wide")
Div(item.body)
.class("content")
Span("\("Tagged with: ".localized(in: item.language!))")
ItemTagList(item: item, site: context.site)
Div {
previous
next
}
.class("prevNextItem")
}
}
SiteFooter(site: context.site, language: item.language!)
}
//.class("item-page")
)
}
func makePageHTML(for page: Page,
context: PublishingContext<Site>) throws -> HTML {
HTML(
.lang(page.language!),
.head(for: page, on: context.site, stylesheetPaths: ["/styles.css", "/gallery.css"]),
.body {
SiteHeader(location: page, context: context, selectedSelectionID: nil)
Wrapper {
H1(page.title)
.class("hide-wide")
page.body
//Node<HTML.BodyContext>.raw(page.content.body.html)
}
SiteFooter(site: context.site, language: page.language!)
}
)
}
func makeTagListHTML(for page: TagListPage,
context: PublishingContext<Site>) throws -> HTML? {
HTML(
.lang(page.language!),
.head(for: page, on: context.site, stylesheetPaths: ["/styles.css"]),
.body {
SiteHeader(location: page, context: context, selectedSelectionID: nil, title: "\("All tags".localized(in: page.language!))")
Wrapper {
List(page.tags.sorted(by: { (tag1, tag2) -> Bool in
tag1.string.lowercased() < tag2.string.lowercased()
})) { tag in
ListItem {
Link(tag.string, url: "\(context.site.path(for: tag, in: page.language!))")
}
.class(tag.colorfiedClass(in: page.language!))
}
.listStyle(.unordered)
.class("all-tags")
}
SiteFooter(site: context.site, language: page.language!)
}
)
}
func makeTagDetailsHTML(for page: TagDetailsPage,
context: PublishingContext<Site>) throws -> HTML? {
HTML(
.lang(page.language!),
.head(for: page, on: context.site, stylesheetPaths: ["/styles.css"]),
.body {
SiteHeader(location: page, context: context, selectedSelectionID: nil)
Wrapper {
H1 {
Text("\("Tagged with: ".localized(in: page.language!))")
Span(page.tag.string)
.class(page.tag.colorfiedClass(in: page.language!))
}
ItemList(
items: context.items(
taggedWith: page.tag,
sortedBy: \.date,
in: page.language!,
order: .descending
),
site: context.site,
column: 2
)
}
SiteFooter(site: context.site, language: page.language!)
}
)
}
}
}
extension Theme.CzjHTMLFactory {
private struct Wrapper: ComponentContainer {
@ComponentBuilder var content: ContentProvider
var body: Component {
Div(content: content).class("wrapper")
}
}
private struct SiteHeader<Site: MultiLanguageWebsite>: Component {
var location: Location
var context: PublishingContext<Site>
var selectedSelectionID: Site.SectionID?
var backgroundImage: String? = nil
var title: String? = nil
var body: Component {
Header {
Div{
Wrapper {
Link(url: "/\(location.language! == .english ? "" : context.site.pathPrefix(for: location.language!) + "/")") {
CZJLogo()
}
.class("header-item site-name")
Div {
if location is Index {
Div("ChenZhijin")
.style("font-size: 34px; line-height: 24px;")
Div(".com")
.style("font-size: 24px; line-height: 20px;")
} else if location is Section<Site> {
Span(title ?? location.title)
} else {
Span(title ?? location.title)
.class("show-wide")
Div {
Div("ChenZhijin")
.style("font-size: 34px; line-height: 24px;")
Div(".com")
.style("font-size: 24px; line-height: 20px;")
}
.class("hide-wide")
}
}
.class("page-title")
.style("font-weight: 280;")
Navigator(location: location, context: context, selectedSection: selectedSelectionID)
}
}
.class("blurred")
if backgroundImage != nil {
Image(backgroundImage!)
.class("header-background")
}
}
}
}
private struct CZJLogo: Component {
var body: Component {
Node<Any>.raw("<svg><circle cx=\"24\" cy=\"24\" r=\"22.5\"/><path d=\"M40,9H18L10,18H23L7,37 8,39H30L38,30 25,30 41,11M35 18H45M3 30H13\"/></svg>")
}
}
private struct Navigator<Site: MultiLanguageWebsite>: Component {
var location: Location
var context: PublishingContext<Site>
var selectedSection: Site.SectionID?
var body: Component {
var altnativeLinks = context.alternateLinks(for: location)
var languages: [Language] = altnativeLinks.keys.sorted(by: { $0.rawValue < $1.rawValue })
var paths: [Path] = languages.map{ altnativeLinks[$0]! }
if altnativeLinks.count != context.site.languages.count, let tagDetailsPage = location as? TagDetailsPage {
let alternatives = LocalizedTag.alternatives(for: tagDetailsPage.tag.string, in: tagDetailsPage.language!)
for language in alternatives.keys.sorted(by: { $0.rawValue < $1.rawValue }) {
languages.append(language)
paths.append(context.site.path(for: Tag(alternatives[language]!) , in: language))
}
}
return Navigation {
Input(type: .checkbox, name: "section", value: "show-section")
.id("section-icon")
Label("") {
Div {
Node<HTML.BodyContext>.raw("<svg><path d=\"M4,14H24 M4,6H24 M4,22H24\"/></svg>")
}
.class("dropbtn")
Div().class("hspacer")
Div {
for section in context.sections.ids {
Link(context.sections[section].id.rawValue.localized(in: location.language!), url: "/\(context.site.path(for: context.section(section, in: location.language!)))")
.class(section == selectedSection ? "selected" : "")
Div().class("line")
}
}
.class("dropdown-content nav-background")
}
.attribute(named: "for", value: "section-icon")
.class("dropdown header-item")
if languages.count > 1 {
Div().class("line")
Input(type: .checkbox, name: "language", value: "show-language")
.id("language-icon")
Label("") {
Div {
Node<HTML.BodyContext>.raw("<svg><circle cx=\"14\" cy=\"14\" r=\"12\"/><path d=\"M2,14 H26 M14,2 V26 M4,7 C8,8 11,8 14,8 C17,8 20,8 24,7 M4,21 C8,20 11,20 14,20 C17,20 20,20 24,21\"/><ellipse cx=\"14\" cy=\"14\" rx=\"6\" ry=\"12\"/></svg>")
}
.class("dropbtn")
Div().class("hspacer")
Div {
for (language, path) in zip(languages, paths) {
Link(context.site.name(for: language), url: language == location.language ? "#" : "/\(path)")
Div().class("line")
}
}
.class("dropdown-content nav-background")
}
.attribute(named: "for", value: "language-icon")
.class("dropdown header-item")
}
}
.class("section-nav")
}
}
struct ArticleItem<Site: MultiLanguageWebsite>: Component {
var item: Item<Site>
var site: Site
var body: Component {
Article {
H1 {
Link(item.title, url: "/\(site.path(for: item))")
}
ItemTagList(item: item, site: site)
Paragraph(item.description)
}
.class("article-item")
}
}
struct AlbumItem<Site: MultiLanguageWebsite>: Component {
var item: Item<Site>
var site: Site
var body: Component {
Article {
Link(url: "/\(site.path(for: item))") {
Image("\(item.imagePath!.string.replacingOccurrences(of: ".jpg", with: "-thumbnail.jpg"))")
.class("item-background")
H1(item.title)
}
}
.class(item.sectionID.rawValue == "album" ? "album-item" : "article-item")
}
}
struct ItemList<Site: MultiLanguageWebsite>: Component {
var items: [Item<Site>]
var site: Site
var filter: (Item<Site>) -> Bool = {_ in true}
var column: Int = 1
var max: Int = 0
private let columnClasses = ["", "single-column", "double-column", "triple-column", "quadruple-column", "quintuple-column"]
var body: Component {
List(items.filter(filter).prefix(max == 0 ? Int.max : max)) { item in
ListItem {
if item.sectionID.rawValue.contains("album") {
AlbumItem(item: item, site: site)
} else {
ArticleItem(item: item, site: site)
}
}
.listStyle(.unordered)
}
.class("item-list flex \(columnClasses[column])")
}
}
struct ItemTagList<Site: MultiLanguageWebsite>: Component {
var item: Item<Site>
var site: Site
var body: Component {
List(item.tags) { tag in
ListItem {
Link(tag.string, url: "/\(site.path(for: tag, in: item.language ?? site.language))")
}
.class(tag.colorfiedClass(in: item.language ?? site.language))
}
.listStyle(.unordered)
.class("tag-list")
}
}
struct SiteFooter<Site: MultiLanguageWebsite>: Component {
var site: Site
var language: Language
var body: Component {
Footer {
Link("\("Browse all tags".localized(in: language))", url: "/\(site.tagListPath(in: language))")
.class("browse-all")
Paragraph {
Text("©2021 Zhijin Chen")
}
Paragraph {
Text("\("Generated using ".localized(in: language))")
Link("Publish", url: "https://github.com/johnsundell/publish")
Text("\(". 100% JavaScript-free.".localized(in: language))")
}
Paragraph {
Link("\("RSS feed".localized(in: language))", url: "/\(site.pathPrefix(for: language))/feed.rss")
}
}
}
}
struct ItemBadge<Site: MultiLanguageWebsite>: Component {
var key: String
var value: String
var keyClass: String? = nil
var valueClass: String? = nil
var body: Component {
Div {
Div {
Text(key)
}
.class("badge-key \(keyClass ?? "variant-medium")")
Div {
Text(value.replacingCharacters(in: value.range(of: String(value.first!))!, with: value.first!.uppercased()))
}
.class("badge-value \(valueClass ?? "variant-\(Int.random(in: 0..<12))")")
}
.class("badge")
}
}
struct LinkList: Component {
var body: Component {
List {
ListItem(Link(url: "https://github.com/ze0nc") {Node<HTML.BodyContext>.raw("<svg><use href=\"/images/github.svg#github\"></use></svg>")})
ListItem(Link(url: "https://www.linkedin.com/in/chenzhijin/") {Node<HTML.BodyContext>.raw("<svg><use href=\"/images/linkedin.svg#linkedin\"></use></svg>")})
ListItem(Link(url: "https://researchmap.jp/chenzhijin") {Node<HTML.BodyContext>.raw("<svg><use href=\"/images/researchmap.svg#researchmap\"></use></svg>")})
ListItem(Link(url: "https://www.researchgate.net/profile/Zhijin_Chen3") {Node<HTML.BodyContext>.raw("<svg><use href=\"/images/researchgate.svg#researchgate\"></use></svg>")})
ListItem(Link(url: "https://www.instagram.com/chenzhijin1992/") {Node<HTML.BodyContext>.raw("<svg><use href=\"/images/instagram.svg#instagram\"></use></svg>")})
ListItem(Link(url: "https://twitter.com/chenzhijin") {Node<HTML.BodyContext>.raw("<svg><use href=\"/images/twitter.svg#twitter\"></use></svg>")})
}
.listStyle(.unordered)
.class("social-list")
}
}
struct AboutList: Component {
var language: Language
var body: Component {
List {
ListItem {
H2("Chemist".localized(in: language))
.style("padding: 0px;margin: 6px 12px;border-top: none;")
if language == .chinese {
Paragraph("我在2020年获得了化学博士学位。研究方向:")
} else if language == .japanese {
Paragraph("2020年に化学の博士号を取得しました。 研究領域:")
} else if language == .english {
Paragraph("In 2020 I got my Ph.D. degree in chemistry. Research interest:")
}
List {
if language == .chinese {
ListItem("卟啉化学")
ListItem("分子电子学")
ListItem("隧道扫描显微镜(STM)")
ListItem("单分子导电性")
ListItem("有机合成")
ListItem("高分子合成")
ListItem("化学领域的机器学习")
} else if language == .japanese {
ListItem("ポルフィリン化学")
ListItem("分子エレクトロニクス")
ListItem("走査型トンネル顕微鏡(STM)")
ListItem("単分子電気伝導性")
ListItem("有機合成")
ListItem("高分子合成")
ListItem("化学分野における機械学習")
} else if language == .english {
ListItem("Porphyrin")
ListItem("Molecular Electronics")
ListItem("Scanning Tunneling Miscroscopy (STM)")
ListItem("Single Molecular Conductance")
ListItem("Organic Synthesis")
ListItem("Macromolecular Synthesis")
ListItem("Machine Learning in Chemistry")
}
}
.listStyle(.unordered)
}
ListItem {
H2("Photographer".localized(in: language))
.style("padding: 0px;margin: 6px 12px;border-top: none;")
if language == .chinese {
Paragraph {
Text("在线相册:")
Link("chenzhijin.com", url: "https://chenzhijin.com/zh/album")
}
} else if language == .japanese {
Paragraph {
Text("写真ギャラリー:")
Link("chenzhijin.com", url: "https://chenzhijin.com/ja/album")
}
} else if language == .english {
Paragraph {
Text("My Gallery: ")
Link("chenzhijin.com", url: "https://chenzhijin.com/en/album")
}
}
}
ListItem {
H2("Developer".localized(in: language))
.style("padding: 0px;margin: 6px 12px;border-top: none;")
if language == .chinese {
H3("大阪大学班车时刻表App (iPhone, iPad, Mac)")
.style(" margin: 12px;")
} else if language == .japanese {
H3("大阪大学班车时刻表App (iPhone, iPad, Mac)")
.style(" margin: 12px;")
} else if language == .english {
H3("Osaka University Shuttle Bus App (iPhone, iPad, Mac)")
.style(" margin: 12px;")
}
Image("/images/article/HandaiBus-Icon512.png")
.style("width:80px; height:80px; margin: 12px; display: inline-block; vertical-align: middle;")
Link(url: "https://apps.apple.com/app/id755385889") {
Image("/images/article/Download_on_the_App_Store_Badge_US-UK_RGB_blk_092917.svg")
}
.style(" margin: 12px; display: inline-block; vertical-align: middle;")
H3("Chemy (iPhone, iPad, Mac)")
.style(" margin: 12px;")
Image("/images/article/Chemy-Icon512.png")
.style("width:80px; height:80px; margin: 12px; display: inline-block; vertical-align: middle;")
Link(url: "https://apps.apple.com/app/id1542390153") {
Image("/images/article/Download_on_the_App_Store_Badge_US-UK_RGB_blk_092917.svg")
}
.style(" margin: 12px; display: inline-block; vertical-align: middle;")
}
}
.listStyle(.unordered)
.class("item-list flex triple-column")
.style("margin: 0px;")
}
}
}
import Foundation
import Publish
import Plot
extension Theme where Site == CzjIo {
static var czj: Self {
Theme(
htmlFactory: CzjHTMLFactory(),
resourcePaths: ["Sources/CzjIo/styles.css"]
)
}
internal struct CzjHTMLFactory: HTMLFactory where Site: MultiLanguageWebsite {
func makeIndexHTML(for index: Index,
context: PublishingContext<CzjIo>) throws -> HTML {
HTML(
.lang(index.language!),
.head(for: index, on: context.site, stylesheetPaths: ["/styles.css"]),
.body(
.header(for: index, for: context, selectedSection: nil),
.wrapper(
.h2("\("Photos and More".localized(in: index.language!))"),
.itemList(
for: context.allItems(
sortedBy: \.date,
in: index.language!,
order: .descending
),
on: context.site,
filter: { $0.sectionID == .album },
column: 3,
max: 6
),
.a(
.class("browse-all"),
.text("\("Browse all photos".localized(in: index.language!))"),
.href(
context.site.path(
for: context.section(.album, in: index.language!)
)
)
),
.h2("\("Working with Publish".localized(in: index.language!))"),
.itemList(
for: context.allItems(
sortedBy: \.date,
in: index.language!,
order: .descending
),
on: context.site,
filter: { (item) -> Bool in
item.tags.contains(Tag("Publish"))
},
column: 3,
max: 6
),
.h2("\("Writing in LaTeX".localized(in: index.language!))"),
.itemList(
for: context.allItems(
sortedBy: \.date,
in: index.language!,
order: .descending
),
on: context.site,
filter: { (item) -> Bool in
item.tags.contains(Tag("LaTeX")) && !item.tags.contains(Tag("macOS")) //&& item.language == language
},
column: 3,
max: 6
),
.h2("\("Research in Chemistry".localized(in: index.language!))"),
.itemList(
for: context.allItems(
sortedBy: \.date,
in: index.language!,
order: .descending
),
on: context.site,
filter: { (item) -> Bool in
item.tags.map{$0.normalizedString()}.contains("Academic".localizedTag(in: index.language!)) //&& item.language == language
},
column: 3,
max: 6
),
.h2("\("And More".localized(in: index.language!))"),
.itemList(
for: context.allItems(
sortedBy: \.date,
in: index.language!,
order: .descending
),
on: context.site,
filter: { (item) -> Bool in
!item.tags.contains(Tag("\("Publish".localizedTag(in: index.language!))")) && !(item.tags.contains(Tag("\("LaTeX".localizedTag(in: index.language!))")) && !item.tags.contains(Tag("\("macOS".localizedTag(in: index.language!))"))) && !(item.tags.contains(Tag("\("Academic".localizedTag(in: index.language!))"))) &&
item.sectionID != .album
},
column: 3,
max: 12
),
.a(
.class("browse-all"),
.text("\("Browse all articles".localized(in: index.language!))"),
.href(
context.site.path(
for: context.section(.article, in: index.language!)
)
)
)
),
.footer(for: context.site, in: index.language!)
)
)
}
func makeAlbumSectionHTML(for section: Section<Site>,
context: PublishingContext<Site>) throws -> HTML {
let photos = context.sections[.album].items(in: section.language!).sorted(by: { $0.date > $1.date} ).prefix(6).reversed()
return
HTML(
.lang(section.language!),
.head(for: section, on: context.site, stylesheetPaths: ["/styles.css", "/gallery.css"]),
.body(
.header(
for: section,
for: context,
selectedSection: section.id,
backgroundImage: nil,
title: section.id.rawValue.localized(in: section.language!)
),
.div(
.class("index-background"),
.id("cf4a"),
.forEach(
photos,
{
.group(
.img(
.src($0.imagePath!)
),
.p(
.text($0.title)
)
)
}
)
),
.div(
.class("wrapper"),
.style("max-width: 3840px;"),
.itemList(
for: context.allItems(
sortedBy: \.date,
in: section.language!,
order: .descending
),
on: context.site,
filter: { $0.sectionID == .album },
column: 5
)
),
.footer(for: context.site, in: section.language!)
)
)
}
func makeSectionHTML(for section: Section<Site>,
context: PublishingContext<Site>) throws -> HTML where Site == CzjIo {
if section.id == .album {
return try makeAlbumSectionHTML(for: section, context: context)
}
return
HTML(
.lang(section.language!),
.head(for: section, on: context.site, stylesheetPaths: ["/styles.css"]),
.body(
.header(
for: section,
for: context,
selectedSection: section.id,
title: section.id.rawValue.localized(in: section.language!)
),
.wrapper(
.if(section.id == .album, .style("max-width: 3840px;")),
.article(
.div(
.class("content"),
.contentBody(section.body)
)
),
.itemList(
for: context.allItems(
sortedBy: \.date,
in: section.language!,
order: .descending
),
on: context.site,
filter: { $0.sectionID == section.id },
column: section.id == .album ? 5 : 2
)
),
.footer(for: context.site, in: section.language!)
)
)
}
func makeItemHTML(for item: Item<Site>,
context: PublishingContext<Site>) throws -> HTML {
return HTML(
.lang(item.language!),
.head(for: item, on: context.site, stylesheetPaths: ["/styles.css", "/gallery.css"]),
.body(
.class("item-page"),
.header(
for: item,
for: context,
selectedSection: item.sectionID,
backgroundImage: item.imagePath?.string
),
.wrapper(
.article(
.div(
.class("content"),
.contentBody(item.body)
),
.span("\("Tagged with: ".localized(in: item.language!))"),
.tagList(for: item, on: context.site),
.div(
.class("prevNextItem"),
{
let allItems = context.allItems(sortedBy: \.date, in: item.language!, order: .descending).filter({$0.sectionID == item.sectionID})
let itemIndex = allItems.firstIndex(of: item)
let previous: Node<HTML.BodyContext>
let next: Node<HTML.BodyContext>
if allItems.first != item {
let previousItem = allItems[allItems.index(before: itemIndex!)]
previous = .a(
.href(context.site.path(for: previousItem)),
.text("" + previousItem.title),
.style("left: 0px;")
)
} else {
previous = .empty
}
if allItems.last != item {
let nextItem = allItems[allItems.index(after: itemIndex!)]
next = .a(
.href(context.site.path(for: nextItem)),
.text(nextItem.title + ""),
.style("right: 0px;")
)
} else {
next = .empty
}
return .group(previous, next)
}()
)
)
),
.footer(for: context.site, in: item.language!)
)
)
}
func makePageHTML(for page: Page,
context: PublishingContext<Site>) throws -> HTML {
HTML(
.lang(page.language!),
.head(for: page, on: context.site, stylesheetPaths: ["/styles.css", "/gallery.css"]),
.body(
.header(for: page, for: context, selectedSection: nil),
.wrapper(
.raw(page.content.body.html)
),
.footer(for: context.site, in: page.language!)
)
)
}
func makeTagListHTML(for page: TagListPage,
context: PublishingContext<Site>) throws -> HTML? {
HTML(
.lang(page.language!),
.head(for: page, on: context.site, stylesheetPaths: ["/styles.css"]),
.body(
.header(for: page, for: context, selectedSection: nil, title: "\("All tags".localized(in: page.language!))"),
.wrapper(
.ul(
.class("all-tags"),
.forEach(page.tags.sorted(by: { (tag1, tag2) -> Bool in
tag1.string.lowercased() < tag2.string.lowercased()
})) { tag in
.li(
.class(tag.colorfiedClass(in: page.language!)),
.a(
.href(context.site.path(for: tag, in: page.language!)),
.text(tag.string)
)
)
}
)
),
.footer(for: context.site, in: page.language!)
)
)
}
func makeTagDetailsHTML(for page: TagDetailsPage,
context: PublishingContext<Site>) throws -> HTML? {
HTML(
.lang(page.language!),
.head(for: page, on: context.site, stylesheetPaths: ["/styles.css"]),
.body(
.header(for: page, for: context, selectedSection: nil),
.wrapper(
.h1(
"\("Tagged with: ".localized(in: page.language!))",
.span(
.class(page.tag.colorfiedClass(in: page.language!)),
.text(page.tag.string)
)
),
.itemList(
for: context.items(
taggedWith: page.tag,
sortedBy: \.date,
in: page.language!,
order: .descending
),
on: context.site,
column: 2
)
),
.footer(for: context.site, in: page.language!)
)
)
}
}
}
extension Node where Context == HTML.BodyContext {
static func altnativeLinks(for location: Location, for context: PublishingContext<CzjIo>) -> Node<HTML.BodyContext> {
var altnativeLinks = context.alternateLinks(for: location)
if altnativeLinks.count != context.site.languages.count, let tagDetailsPage = location as? TagDetailsPage {
let alternatives = LocalizedTag.alternatives(for: tagDetailsPage.tag.string, in: tagDetailsPage.language!)
for language in alternatives.keys {
altnativeLinks[language] = context.site.path(for: Tag(alternatives[language]!) , in: language)
}
}
if altnativeLinks.count <= 1 {
return .empty
}
return
.group(
.input(
.type(.checkbox),
.id("language-icon"),
.name("language"),
.value("show-language")
),
.label(
.class("dropdown header-item nav-background"),
.for("language-icon"),
.div(
.class("dropbtn"),
.raw("<svg><use href=\"/images/language.svg#language\"></use></svg>")
),
.div(.class("hspacer")),
.nav(
.class("dropdown-content nav-background"),
.forEach(altnativeLinks.sorted(by: {$0.0.rawValue < $1.0.rawValue})) {
.group(
.a(
.if(
$0.0 != location.language!,
.href($0.1)
),
.text(context.site.name(for: $0.0))
),
.div(.class("vline"))
)
}
)
)
)
}
}
extension Node where Context == HTML.BodyContext {
static func wrapper(_ nodes: Node...) -> Node {
.div(.class("wrapper"), .group(nodes))
}
static func sectionNav(
for location: Location,
for context: PublishingContext<CzjIo>,
selectedSection: CzjIo.SectionID?
) -> Node {
let sectionIDs = CzjIo.SectionID.allCases
return
.nav(
.class("section-nav nav-background"),
.forEach(sectionIDs.filter({$0 != .academic})) { section in
.group(
.a(
.class(section == selectedSection ? "selected" : ""),
.href(
context.site.path(
for: context.section(section, in: location.language!)
)
),
.text(context.sections[section].id.rawValue.localized(in: location.language!))
),
.div(.class("vline"))
)
}
)
}
static func navigator(
for location: Location,
for context: PublishingContext<CzjIo>,
selectedSection: CzjIo.SectionID?
) -> Node {
var altnativeLinks = context.alternateLinks(for: location)
if altnativeLinks.count != context.site.languages.count, let tagDetailsPage = location as? TagDetailsPage {
let alternatives = LocalizedTag.alternatives(for: tagDetailsPage.tag.string, in: tagDetailsPage.language!)
for language in alternatives.keys {
altnativeLinks[language] = context.site.path(for: Tag(alternatives[language]!) , in: language)
}
}
return
.nav(
.class("section-nav"),
.input(
.type(.checkbox),
.id("section-icon"),
.name("section"),
.value("show-section")
),
.label(
.class("dropdown header-item"),
.for("section-icon"),
.div(
.class("dropbtn"),
.raw("<svg><path d=\"M4,14H24 M4,6H24 M4,22H24\"/></svg>")
),
.div(.class("hspacer")),
.div(
.class("dropdown-content nav-background"),
.forEach(context.sections.ids) { section in
.group(
.a(
.class( section == selectedSection ? "selected" : ""),
.href(
context.site.path(
for: context.section(section, in: location.language!)
)
),
.text(context.sections[section].id.rawValue.localized(in: location.language!))
),
.div(.class("line"))
)
}
)
),
.if(altnativeLinks.count > 1,
.group(
.div(.class("line")),
.input(
.type(.checkbox),
.id("language-icon"),
.name("language"),
.value("show-language")
),
.label(
.class("dropdown header-item"),
.for("language-icon"),
.div(
.class("dropbtn"),
.raw("<svg><circle cx=\"14\" cy=\"14\" r=\"12\"/><path d=\"M2,14 H26 M14,2 V26 M4,7 C8,8 11,8 14,8 C17,8 20,8 24,7 M4,21 C8,20 11,20 14,20 C17,20 20,20 24,21\"/><ellipse cx=\"14\" cy=\"14\" rx=\"6\" ry=\"12\"/></svg>")
),
.div(.class("hspacer")),
.div(
.class("dropdown-content nav-background"),
.forEach(altnativeLinks.sorted(by: {$0.0.rawValue < $1.0.rawValue})) {
.group(
.a(
.if(
$0.0 != location.language!,
.href($0.1)
),
.text(context.site.name(for: $0.0))
),
.div(.class("line"))
)
}
)
)
)
)
)
}
static func header(
for location: Location,
for context: PublishingContext<CzjIo>,
selectedSection: CzjIo.SectionID?,
backgroundImage: String? = nil,//"/images/header.png",
title: String? = nil
) -> Node {
return .header(
.div(
.class("blurred"),
.wrapper(
.a(
.class("header-item site-name"),
.href("/\(location.language! == .english ? "" : context.site.pathPrefix(for: location.language!) + "/")"),
.raw("""
<svg><circle cx="24" cy="24" r="22.5"/><path d="M40,9H18L10,18H23L7,37 8,39H30L38,30 25,30 41,11M35 18H45M3 30H13"/></svg>
""")
),
.div(
.class("page-title"),
.text(title != nil ? title! : (location is Index ? "CZJ" : location.title))
),
.navigator(for: location, for: context, selectedSection: selectedSection)
)
),
.if(
backgroundImage != nil,
.img(
.src(backgroundImage ?? ""),
.class("header-background")
)
)
)
return .header(
.div(
.class("blurred"),
.wrapper(
.a(
.class("header-item site-name"),
.href("/\(location.language! == .english ? "" : context.site.pathPrefix(for: location.language!) + "/")"),
.element(
named: "svg",
nodes: [
.element(
named: "use",
nodes: [.href("/images/logo.svg#logo")]
)]
)
),
.sectionNav(for: location, for: context, selectedSection: selectedSection),
.altnativeLinks(
for: location,
for: context
)
)
),
.if(
backgroundImage != nil,
.img(
.src(backgroundImage ?? ""),
.attribute(named: "style", value: "height: 100%; width:100%; position: absolute; top:0px; left:0px; opacity: 0.6; z-index: -1000")
)
)
)
}
static func badge(key: String, value: String, keyClass: String? = nil, valueClass: String? = nil) -> Node {
return .div(
.class("badge"),
.div(
.class("badge-key \(keyClass ?? "variant-medium")"),
.text(key)
),
.div(
.class("badge-value \(valueClass ?? "variant-\(Int.random(in: 0..<12))")"),
.text(value.replacingCharacters(in: value.range(of: String(value.first!))!, with: value.first!.uppercased()))
)
)
}
static func itemList<T: MultiLanguageWebsite>(for items: [Item<T>], on site: T, filter: (Item<T>) -> Bool = {_ in true}, column: Int = 1, max: Int = 0) -> Node {
let columnClass: String = ["", "single-column", "double-column", "triple-column", "quadruple-column", "quintuple-column"][column]
return .ul(
.class("item-list flex \(columnClass)"),
.forEach(
items.filter(filter).prefix(max == 0 ? Int.max : max)
) { item in
if item.sectionID.rawValue == "album" {
return albumItem(for: item, on: site)
} else {
return articleItem(for: item, on: site)
}
}
)
}
static func articleItem<T: MultiLanguageWebsite>(for item: Item<T>, on site: T) -> Node<HTML.ListContext> {
Node<HTML.ListContext>
.li(
.article(
.class("article-item"),
.h1(
.a(
.href(site.path(for: item)),
.text(item.title)
)
),
.tagList(for: item, on: site),
.p( .text(item.description) )
)
)
}
static func albumItem<T: MultiLanguageWebsite>(for item: Item<T>, on site: T) -> Node<HTML.ListContext> {
Node<HTML.ListContext>.li(
.article(
.class("album-item"),
.a(
.href(site.path(for: item)),
.img(
.src(item.imagePath!.string.replacingOccurrences(of: ".jpg", with: "-thumbnail.jpg")),
.class("item-background")
),
.h1(
.text(item.title)
)
),
.p(.text(item.description))
))
}
static func tagList<T: MultiLanguageWebsite>(for item: Item<T>, on site: T) -> Node {
return .ul(.class("tag-list"), .forEach(item.tags) { tag in
.li(
.class(tag.colorfiedClass(in: item.language ?? site.language)),
.a(
.href(site.path(for: tag, in: item.language ?? site.language)),
.text(tag.string)
)
)
})
}
static func footer<T: MultiLanguageWebsite>(for site: T, in language: Language) -> Node {
return .footer(
.a(
.class("browse-all"),
.text("\("Browse all tags".localized(in: language))"),
.href(site.tagListPath(in: language))
),
.p(
.text("©2020 Zhijin Chen")
),
.p(
.text("\("Generated using ".localized(in: language))"),
.a(
.text("Publish"),
.href("https://github.com/johnsundell/publish")
),
.text("\(". 100% JavaScript-free.".localized(in: language))")
),
.p(.a(
.text("\("RSS feed".localized(in: language))"),
.href("/\(site.pathPrefix(for: language))/feed.rss")
))
)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment