feat: Adds styleguide, working on result view container.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import Elementary
|
||||
import Styleguide
|
||||
|
||||
struct MainPage<Inner: HTML>: SendableHTMLDocument where Inner: Sendable {
|
||||
let title = "HVAC-Toolbox"
|
||||
@@ -33,36 +34,36 @@ struct MainPage<Inner: HTML>: SendableHTMLDocument where Inner: Sendable {
|
||||
main(.class("bg-white dark:bg-gray-800")) {
|
||||
div(.class("min-h-screen")) {
|
||||
Header()
|
||||
inner()
|
||||
PageContent(body: inner)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Header: HTML {
|
||||
private struct Header: HTML {
|
||||
|
||||
var content: some HTML {
|
||||
header(.class("bg-blue-500 mb-8 flex flex-row gap-2 border-b border-yellow-300")) {
|
||||
header(.class("\(bg: .blue) mb-8 flex flex-row gap-2 border \(border: .yellow)")) {
|
||||
a(
|
||||
.href(route: .index),
|
||||
.class("flex flex-row gap-2 bg-yellow-300 pe-2 rounded-e-lg text-blue-500 [&:hover]:text-blue-600")
|
||||
.class("flex flex-row gap-2 \(bg: .yellow) pe-2 rounded-e-lg \(text: .blue) hover:\(text: .darkBlue)")
|
||||
) {
|
||||
img(.src("/images/toolbox.svg"), .width(40), .height(40), .class("py-1"))
|
||||
div(.class("flex flex-row mt-2")) {
|
||||
h2(.class("text-2xl font-extrabold")) { "HVAC-Toolbox" }
|
||||
SVG.wind
|
||||
h2(.class("text-2xl font-extrabold pe-3")) { "HVAC-Toolbox" }
|
||||
SVG(.wind, color: .blue)
|
||||
}
|
||||
}
|
||||
nav(.class("flex flex-row gap-2 p-2 mt-2")) {
|
||||
// TODO: Add class active, to button that is the active route.
|
||||
ul(.class("flex flex-wrap gap-x-2 lg:gap-x-5 text-yellow-300 font-bold")) {
|
||||
ul(.class("flex flex-wrap gap-x-2 lg:gap-x-5 \(text: .yellow) font-bold")) {
|
||||
li {
|
||||
a(.href(route: .moldRisk(.index)), .class("[&:hover]:border-b border-yellow-300")) {
|
||||
a(.href(route: .moldRisk(.index)), .class("hover:border-b \(border: .yellow)")) {
|
||||
"Mold-Risk"
|
||||
}
|
||||
}
|
||||
li {
|
||||
a(.href("#"), .class("[&:hover]:border-b border-yellow-300")) {
|
||||
a(.href("#"), .class("[&:hover]:border-b \(border: .yellow)")) {
|
||||
"Dehumidifier-Sizing"
|
||||
}
|
||||
}
|
||||
@@ -72,4 +73,16 @@ struct Header: HTML {
|
||||
}
|
||||
}
|
||||
|
||||
private struct PageContent<Body: HTML>: HTML where Body: Sendable {
|
||||
let body: () -> Body
|
||||
|
||||
var content: some HTML {
|
||||
div(.class("mx-5 lg:mx-20")) {
|
||||
div(.class("rounded-xl shadow-lg \(bg: .slate) dark:\(bg: .darkSlate) p-8")) {
|
||||
body()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protocol SendableHTMLDocument: HTMLDocument, Sendable {}
|
||||
|
||||
@@ -1,56 +1,91 @@
|
||||
import Elementary
|
||||
import PsychrometricClient
|
||||
import Routes
|
||||
import Styleguide
|
||||
|
||||
struct MoldRiskForm: HTML {
|
||||
|
||||
var content: some HTML {
|
||||
div(.class("grid grid-cols-1 lg:grid-cols-12")) {
|
||||
div(.class("col-span-1")) {}
|
||||
div(.class("col-span-10 rounded-xl shadow-lg bg-slate-300 dark:bg-slate-700 p-8")) {
|
||||
div(.class("flex items-center gap-3 mb-6")) {
|
||||
SVG.thermometer
|
||||
h2(.class("text-2xl font-extrabold dark:text-white")) { "Mold Risk Calculator" }
|
||||
FormHeader(label: "Mold Risk Calculator", svg: .thermometer)
|
||||
form {
|
||||
div(.class("space-y-6")) {
|
||||
LabeledContent(label: "Indoor Temperature (°F)") {
|
||||
Input(id: "temperature", placeholder: "Dry bulb temperature")
|
||||
.attributes(.type(.number), .step("0.1"), .min("0.1"), .autofocus)
|
||||
}
|
||||
form {
|
||||
div(.class("space-y-6")) {
|
||||
div {
|
||||
label(.for("temperature"), .class("block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2")) {
|
||||
"Indoor Temperature"
|
||||
}
|
||||
input(
|
||||
.type(.number), .id("temperature"), .placeholder("Dry bulb temperature"), .required,
|
||||
.step("0.1"), .min("0.1"),
|
||||
.class("""
|
||||
w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-yellow-800
|
||||
focus:border-yellow-800 text-gray-700 dark:text-white
|
||||
""")
|
||||
)
|
||||
}
|
||||
div {
|
||||
label(.for("humidity"), .class("block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2")) {
|
||||
"Indoor Humidity (%)"
|
||||
}
|
||||
input(
|
||||
.type(.number), .id("humidity"), .name("humidity"), .placeholder("Relative humidity"), .required,
|
||||
.step("0.1"), .min("0.1"),
|
||||
.class("""
|
||||
w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-yellow-800
|
||||
focus:border-yellow-800 text-gray-700 dark:text-white
|
||||
""")
|
||||
)
|
||||
}
|
||||
div {
|
||||
button(.type(.submit), .class("""
|
||||
w-full bg-\(Colors.blue) text-\(Colors.yellow) font-bold py-3 rounded-md hover:bg-blue-600
|
||||
transition-colors
|
||||
""")) {
|
||||
"Calculate Mold Risk"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LabeledContent(label: "Indor Humdity (%)") {
|
||||
Input(id: "humidity", placeholder: "Relative humidity")
|
||||
.attributes(.type(.number), .step("0.1"), .min("0.1"))
|
||||
}
|
||||
|
||||
div {
|
||||
SubmitButton(label: "Calculate Mold Risk")
|
||||
}
|
||||
|
||||
div(.id("result")) {
|
||||
MoldRiskResponse(response: .mock)
|
||||
}
|
||||
div(.class("col-span-1")) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MoldRiskResponse: HTML {
|
||||
let response: MoldRisk.Response
|
||||
|
||||
var content: some HTML {
|
||||
ResultContainer {
|
||||
// TODO: Color needs to be derived from risk level.
|
||||
div(.class("p-2 rounded-lg shadow-lg bg-lime-100 border-2 border border-lime-600")) {
|
||||
LabeledContent {
|
||||
p(.class("text-lg font-semibold \(text: .green) dark:text-lime-600 mt-2")) {
|
||||
"Risk Level: \(response.riskLevel.rawValue.capitalized)"
|
||||
}
|
||||
} label: {
|
||||
SVG(.exclamation, color: "\(text: .green) dark:text-lime-600")
|
||||
}
|
||||
.attributes(.class("flex items-center gap-2"))
|
||||
}
|
||||
|
||||
PsychrometricPropertiesGrid(properties: response.psychrometricProperties)
|
||||
.attributes(.class("mx-6"))
|
||||
|
||||
div(.class("mt-8 p-4 bg-gray-700 rounded-md shadow-md border border-blue-500")) {
|
||||
p(.class("text-sm \(text: .blue)")) {
|
||||
span(.class("font-extrabold pe-2")) { "Note:" }
|
||||
"""
|
||||
These calculations are based on typical indoor conditions and common mold species. Actual mold growth can
|
||||
vary based on surface materials, air movement, and other environmental factors. Always address moisture
|
||||
issues promptly and consult professionals for severe cases.
|
||||
"""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct PsychrometricPropertiesGrid: HTML {
|
||||
let properties: PsychrometricProperties
|
||||
|
||||
var content: some HTML<HTMLTag.div> {
|
||||
div(.class("grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 mt-6 md:mx-20")) {
|
||||
displayProperty("Dew Point", "\(properties.dewPoint.value) \(properties.dewPoint.units.symbol)")
|
||||
displayProperty("Wet Bulb", "\(properties.wetBulb.value) \(properties.wetBulb.units.symbol)")
|
||||
displayProperty("Enthalpy", "\(properties.enthalpy.value) \(properties.wetBulb.units.symbol)")
|
||||
displayProperty("Density", "\(properties.density.value) \(properties.density.units.rawValue)")
|
||||
displayProperty("Vapor Pressure", "\(properties.vaporPressure.value) \(properties.vaporPressure.units.symbol)")
|
||||
displayProperty("Specific Volume", "\(properties.specificVolume.rawValue)")
|
||||
displayProperty("Absolute Humidity", "\(properties.absoluteHumidity.value) \(properties.absoluteHumidity.units.symbol)")
|
||||
displayProperty("Humidity Ratio", "\(properties.humidityRatio.value)")
|
||||
displayProperty("Degree of Saturation", "\(properties.degreeOfSaturation.value)")
|
||||
}
|
||||
}
|
||||
|
||||
func displayProperty(_ label: String, _ value: String) -> some HTML {
|
||||
p(.class("\(text: .darkGray) dark:\(text: .white)")) {
|
||||
span(.class("font-semibold")) { "\(label): " }
|
||||
span(.class("font-light")) { value }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
import Elementary
|
||||
|
||||
enum SVG {
|
||||
static let wind = HTMLRaw("""
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
|
||||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
||||
class="w-8 h-8">
|
||||
<path d="M17.7 7.7a2.5 2.5 0 1 1 1.8 4.3H2"></path>
|
||||
<path d="M9.6 4.6A2 2 0 1 1 11 8H2"></path>
|
||||
<path d="M12.6 19.4A2 2 0 1 0 14 16H2"></path>
|
||||
</svg>
|
||||
""")
|
||||
|
||||
static let calculator = HTMLRaw("""
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
|
||||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
||||
class="w-5 h-5">
|
||||
<rect width="16" height="20" x="4" y="2" rx="2"></rect>
|
||||
<line x1="8" x2="16" y1="6" y2="6"></line>
|
||||
<line x1="16" x2="16" y1="14" y2="18"></line>
|
||||
<path d="M16 10h.01"></path>
|
||||
<path d="M12 10h.01"></path>
|
||||
<path d="M8 10h.01"></path>
|
||||
<path d="M12 14h.01"></path>
|
||||
<path d="M8 14h.01"></path>
|
||||
<path d="M12 18h.01"></path><path d="M8 18h.01"></path>
|
||||
</svg>
|
||||
""")
|
||||
|
||||
static let thermometer = HTMLRaw("""
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
|
||||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
||||
class="w-8 h-8 text-blue-500">
|
||||
<path d="M14 4v10.54a4 4 0 1 1-4 0V4a2 2 0 0 1 4 0Z"></path>
|
||||
</svg>
|
||||
""")
|
||||
|
||||
static let menu = HTMLRaw("""
|
||||
<svg class="block h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor"
|
||||
viewBox="0 0 24 24" x="0" y="0" id="menu-icon">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
|
||||
</svg>
|
||||
|
||||
""")
|
||||
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
// NOTE: These don't always work as expected with tailwind they generally
|
||||
// need to be in the class itself, but here for reference of the primary
|
||||
// colors.
|
||||
enum Colors {
|
||||
static let yellow = "yellow-300"
|
||||
static let blue = "blue-500"
|
||||
}
|
||||
Reference in New Issue
Block a user