import Elementary import ElementaryHTMX import Routes import Styleguide struct RoomPressureForm: HTML, Sendable { let response: RoomPressure.Response? let mode: RoomPressure.Mode init(mode: RoomPressure.Mode? = nil, response: RoomPressure.Response? = nil) { self.mode = mode ?? .knownAirflow self.response = response } var content: some HTML { div(.class("relative")) { FormHeader(label: "Room Pressure Calculator", svg: .leftRightArrow) // FIX: Remove when done testing. WarningBox( "This calculator is currently under construction, so it does not do anything when the form is submitted." ) .attributes(.class("mb-8")) // Mode toggle / buttons. div(.class("absolute top-0 right-0 flex items-center gap-x-0")) { switch mode { case .knownAirflow: SecondaryButton(label: "Known Airflow") .attributes(.class("rounded-s-lg")) .attributes(.disabled, when: mode == .knownAirflow) .attributes( .hx.get(route: .roomPressure(.index(mode: .knownAirflow))), .hx.target("#content"), when: mode == .measuredPressure ) PrimaryButton(label: "Measured Pressure") .attributes(.class("rounded-e-lg")) .attributes( .hx.get(route: .roomPressure(.index(mode: .measuredPressure))), .hx.target("#content"), when: mode == .knownAirflow ) case .measuredPressure: PrimaryButton(label: "Known Airflow") .attributes(.class("rounded-s-lg")) .attributes( .hx.get(route: .roomPressure(.index(mode: .knownAirflow))), .hx.target("#content"), when: mode == .measuredPressure ) SecondaryButton(label: "Measured Pressure") .attributes(.class("rounded-e-lg")) .attributes(.disabled, when: mode == .measuredPressure) .attributes( .hx.get(route: .roomPressure(.index(mode: .measuredPressure))), .hx.target("#content"), when: mode == .knownAirflow ) } } Form(mode: mode) div(.id("result")) { if let response { RoomPressureResult(response: response) } } } } struct Form: HTML, Sendable { let mode: RoomPressure.Mode var content: some HTML { form { div(.class("space-y-6")) { LabeledContent(label: pressureLabel) { // NB: using .attributes(..., when:...) not working, so using a switch statement. switch mode { case .knownAirflow: Input(id: pressureID, placeholder: pressurePlaceholder) .attributes( .type(.number), .step("0.1"), .min("0.1"), .max("3.0"), .autofocus, .required ) case .measuredPressure: Input(id: pressureID, placeholder: pressurePlaceholder) .attributes(.type(.number), .step("0.1"), .min("0.1"), .autofocus, .required) } } DoorDetails() if mode == .knownAirflow { LabeledContent(label: "Supply Airflow (CFM)") { Input(id: "supplyAirflow", placeholder: "Airflow") .attributes(.type(.number), .step("0.1"), .min("0.1"), .required) } } PreferredGrilleHeight() div { SubmitButton(label: "Calculate Return Path Size") } } } } private var pressureLabel: String { switch mode { case .knownAirflow: return "Target Room Pressure (Pascals)" case .measuredPressure: return "Measured Room Pressure (Pascals)" } } private var pressureID: String { switch mode { case .knownAirflow: return "targetRoomPressure" case .measuredPressure: return "measuredRoomPressure" } } private var pressurePlaceholder: String { switch mode { case .knownAirflow: return "Room pressure (max 3 pa.)" case .measuredPressure: return "Measured pressure" } } } private struct DoorDetails: HTML, Sendable { var content: some HTML { div(.class("grid grid-cols-1 lg:grid-cols-2 gap-6")) { LabeledContent(label: "Door Width (in.)") { Input(id: "doorWidth", placeholder: "Width") .attributes(.type(.number), .step("0.1"), .min("0.1"), .required) } LabeledContent(label: "Door Height (in.)") { Input(id: "doorHeight", placeholder: "Height") .attributes(.type(.number), .step("0.1"), .min("0.1"), .required) } } LabeledContent(label: "Door Undercut (in.)") { Input(id: "doorUndercut", placeholder: "Undercut height") .attributes(.type(.number), .step("0.1"), .min("0.1"), .required) } } } private struct PreferredGrilleHeight: HTML, Sendable { var content: some HTML { InputLabel(for: "preferredGrilleHeight") { "Preferred Grille Height" } select( .id("preferredGrilleHeight"), .name("preferredGrilleHeight"), .class("w-full px-4 py-2 rounded-md border") ) { for height in RoomPressure.CommonReturnGrilleHeight.allCases { option(.value("\(height.rawValue)")) { height.label } } } } } } struct RoomPressureResult: HTML, Sendable { let response: RoomPressure.Response }