Files
ccn/Packages/Infrastructure/Sources/Public/Protocols/Page.swift
T

88 lines
2.5 KiB
Swift
Raw Normal View History

import Elementary
import Foundation
/// A page of a website: an HTML document with the shared scaffolding assembled around the page's content.
///
/// A conforming page supplies its locale, its title, the stylesheets and scripts it needs, its head metadata, and its content; the protocol assembles the
/// rest of the document around them: the viewport declaration and stylesheet links followed by the metadata in the head, and the content followed by
/// the script tags in the body.
public protocol Page: HTMLDocument, Sendable {
// MARK: Associated types
/// The type of the page's markup.
associatedtype Content: HTML
/// The type of the page's head metadata markup.
associatedtype Metadata: HTML
// MARK: Properties
/// The version token appended to the page's asset URLs, or `nil` to leave them unversioned.
var assetVersion: String? { get }
/// The page's markup, rendered before the ``scripts``.
@HTMLBuilder
var content: Content { get }
/// The locale the page content is localized to.
var locale: Locale { get }
/// The markup placed in the document head after the ``stylesheets``: icon and manifest
/// links, extra meta tags, and the like.
@HTMLBuilder
var metadata: Metadata { get }
/// The scripts loaded at the end of the document body, in order.
var scripts: [any Asset] { get }
/// The stylesheets linked in the document head, in order.
var stylesheets: [any Asset] { get }
}
// MARK: - Implementations
public extension Page {
// MARK: Computed
/// The page ``content`` followed by its ``scripts``.
@HTMLBuilder
var body: some HTML {
content
for file in scripts {
script(.src(file.urlPath(
for: .js,
version: assetVersion
))) {}
}
}
/// The viewport declaration and ``stylesheets`` links followed by the ``metadata``, placed in the document head.
///
/// The charset declaration is omitted: Elementary's `HTMLDocument` scaffolding already
/// emits `<meta charset="UTF-8">` before this markup, and HTML5 allows only one.
@HTMLBuilder
var head: some HTML {
meta(
.name(.viewport),
.content("width=device-width, initial-scale=1")
)
metadata
for file in stylesheets {
link(
.rel(.stylesheet),
.href(file.urlPath(
for: .css,
version: assetVersion
))
)
}
}
}