From 54847d0b345633eedc68a133ecc2b3cc8e939b8a Mon Sep 17 00:00:00 2001 From: Michael Housh Date: Fri, 2 Jan 2026 08:27:31 -0500 Subject: [PATCH] WIP: Adds a modal form view and integrates into current forms. --- Sources/Styleguide/ModalForm.swift | 37 ++++++++++++++++++ Sources/ViewController/Live.swift | 27 +++++-------- .../EffectiveLength/EffectiveLengthForm.swift | 23 +++++------ .../FrictionRate/ComponentLossForm.swift | 13 ++----- .../Views/FrictionRate/EquipmentForm.swift | 13 ++----- Sources/ViewController/Views/MainPage.swift | 6 ++- .../Views/Project/ProjectForm.swift | 14 ++----- Sources/ViewController/Views/Sidebar.swift | 39 ++++++++++++++++--- 8 files changed, 106 insertions(+), 66 deletions(-) create mode 100644 Sources/Styleguide/ModalForm.swift diff --git a/Sources/Styleguide/ModalForm.swift b/Sources/Styleguide/ModalForm.swift new file mode 100644 index 0000000..53ab925 --- /dev/null +++ b/Sources/Styleguide/ModalForm.swift @@ -0,0 +1,37 @@ +import Elementary + +public struct ModalForm: HTML, Sendable where T: Sendable { + + let dismiss: Bool + let id: String + let inner: T + + public init( + id: String, + dismiss: Bool, + @HTMLBuilder inner: () -> T + ) { + self.dismiss = dismiss + self.id = id + self.inner = inner() + } + + public var body: some HTML { + if dismiss { + div(.id(id)) {} + } else { + div( + .id(id), + .class( + """ + fixed top-40 left-[25vw] w-1/2 z-50 text-gray-800 + bg-gray-200 border border-gray-400 + rounded-lg shadow-lg mx-10 + """ + ) + ) { + inner + } + } + } +} diff --git a/Sources/ViewController/Live.swift b/Sources/ViewController/Live.swift index 17b6e6f..e654502 100644 --- a/Sources/ViewController/Live.swift +++ b/Sources/ViewController/Live.swift @@ -24,14 +24,11 @@ extension SiteRoute.View.ProjectRoute { func renderView(isHtmxRequest: Bool) async throws -> AnySendableHTML { switch self { case .index: - return MainPage { + return MainPage(active: .projects) { ProjectView(project: .mock) } case .form(let dismiss): - guard !dismiss else { - return div(.id("projectForm")) {} - } - return ProjectForm() + return ProjectForm(dismiss: dismiss) case .create: return mainPage @@ -48,7 +45,7 @@ extension SiteRoute.View.RoomRoute { } return RoomForm() case .index: - return MainPage { + return MainPage(active: .rooms) { RoomsView(rooms: Room.mocks) } } @@ -59,19 +56,16 @@ extension SiteRoute.View.FrictionRateRoute { func renderView(isHtmxRequest: Bool) async throws -> AnySendableHTML { switch self { case .index: - return MainPage { + return MainPage(active: .frictionRate) { FrictionRateView() } case .form(let type, let dismiss): - guard !dismiss else { - return div(.id(type.id)) {} - } // FIX: Forms need to reference existing items. switch type { case .equipmentInfo: - return EquipmentForm() + return EquipmentForm(dismiss: dismiss) case .componentPressureLoss: - return ComponentLossForm() + return ComponentLossForm(dismiss: dismiss) } } } @@ -93,14 +87,11 @@ extension SiteRoute.View.EffectiveLengthRoute { func renderView(isHtmxRequest: Bool) async throws -> AnySendableHTML { switch self { case .index: - return MainPage { + return MainPage(active: .effectiveLength) { EffectiveLengthsView(effectiveLengths: EffectiveLength.mocks) } case .form(let dismiss): - guard !dismiss else { - return div(.id("effectiveLengthForm")) {} - } - return EffectiveLengthForm() + return EffectiveLengthForm(dismiss: dismiss) case .field(let type): switch type { @@ -114,7 +105,7 @@ extension SiteRoute.View.EffectiveLengthRoute { } private let mainPage: AnySendableHTML = { - MainPage { + MainPage(active: .projects) { div { h1 { "It works!" } } diff --git a/Sources/ViewController/Views/EffectiveLength/EffectiveLengthForm.swift b/Sources/ViewController/Views/EffectiveLength/EffectiveLengthForm.swift index ae0146a..613f0f5 100644 --- a/Sources/ViewController/Views/EffectiveLength/EffectiveLengthForm.swift +++ b/Sources/ViewController/Views/EffectiveLength/EffectiveLengthForm.swift @@ -4,18 +4,20 @@ import ManualDCore import Styleguide struct EffectiveLengthForm: HTML, Sendable { + let dismiss: Bool var body: some HTML { - div( - .id("effectiveLengthForm"), - .class( - """ - fixed top-40 left-[25vw] w-1/2 z-50 text-gray-800 - bg-gray-200 border border-gray-400 - rounded-lg shadow-lg mx-10 - """ - ) - ) { + // div( + // .id("effectiveLengthForm"), + // .class( + // """ + // fixed top-40 left-[25vw] w-1/2 z-50 text-gray-800 + // bg-gray-200 border border-gray-400 + // rounded-lg shadow-lg mx-10 + // """ + // ) + // ) { + ModalForm(id: "effectiveLengthForm", dismiss: dismiss) { h1(.class("text-2xl font-bold")) { "Effective Length" } form(.class("space-y-4 p-4")) { div { @@ -25,7 +27,6 @@ struct EffectiveLengthForm: HTML, Sendable { } div { label(.for("type")) { "Type" } - // FIX: Add select field. select( .id("type"), .name("type"), .class("w-full border rounded-md") diff --git a/Sources/ViewController/Views/FrictionRate/ComponentLossForm.swift b/Sources/ViewController/Views/FrictionRate/ComponentLossForm.swift index 61f167f..df9f456 100644 --- a/Sources/ViewController/Views/FrictionRate/ComponentLossForm.swift +++ b/Sources/ViewController/Views/FrictionRate/ComponentLossForm.swift @@ -4,17 +4,10 @@ import ManualDCore import Styleguide struct ComponentLossForm: HTML, Sendable { + let dismiss: Bool + var body: some HTML { - div( - .id("componentLossForm"), - .class( - """ - fixed top-40 left-[25vw] w-1/2 z-50 text-gray-800 - bg-gray-200 border border-gray-400 - rounded-lg shadow-lg mx-10 - """ - ) - ) { + ModalForm(id: "componentLossForm", dismiss: dismiss) { h1(.class("text-2xl font-bold")) { "Component Loss" } form(.class("space-y-4 p-4")) { div { diff --git a/Sources/ViewController/Views/FrictionRate/EquipmentForm.swift b/Sources/ViewController/Views/FrictionRate/EquipmentForm.swift index d7168ce..e196008 100644 --- a/Sources/ViewController/Views/FrictionRate/EquipmentForm.swift +++ b/Sources/ViewController/Views/FrictionRate/EquipmentForm.swift @@ -5,17 +5,10 @@ import Styleguide // TODO: Have form hold onto equipment info model to edit. struct EquipmentForm: HTML, Sendable { + let dismiss: Bool + var body: some HTML { - div( - .id("equipmentForm"), - .class( - """ - fixed top-40 left-[25vw] w-1/2 z-50 text-gray-800 - bg-gray-200 border border-gray-400 - rounded-lg shadow-lg mx-10 - """ - ) - ) { + ModalForm(id: "equipmentForm", dismiss: dismiss) { h1(.class("text-3xl font-bold pb-6 ps-2")) { "Equipment Info" } form(.class("space-y-4 p-4")) { div { diff --git a/Sources/ViewController/Views/MainPage.swift b/Sources/ViewController/Views/MainPage.swift index 667a64d..4478e02 100644 --- a/Sources/ViewController/Views/MainPage.swift +++ b/Sources/ViewController/Views/MainPage.swift @@ -4,8 +4,10 @@ public struct MainPage: SendableHTMLDocument where Inner: Sendable public var title: String { "Manual-D" } public var lang: String { "en" } let inner: Inner + let activeTab: Sidebar.ActiveTab - init(_ inner: () -> Inner) { + init(active activeTab: Sidebar.ActiveTab, _ inner: () -> Inner) { + self.activeTab = activeTab self.inner = inner() } @@ -22,7 +24,7 @@ public struct MainPage: SendableHTMLDocument where Inner: Sendable public var body: some HTML { div(.class("bg-white dark:bg-gray-800 dark:text-white")) { div(.class("flex flex-row")) { - Sidebar() + Sidebar(active: activeTab) main(.class("flex flex-col h-screen w-full")) { inner } diff --git a/Sources/ViewController/Views/Project/ProjectForm.swift b/Sources/ViewController/Views/Project/ProjectForm.swift index 2bea579..ab92e36 100644 --- a/Sources/ViewController/Views/Project/ProjectForm.swift +++ b/Sources/ViewController/Views/Project/ProjectForm.swift @@ -6,24 +6,18 @@ import Styleguide struct ProjectForm: HTML, Sendable { let project: Project? + let dismiss: Bool init( + dismiss: Bool, project: Project? = nil ) { + self.dismiss = dismiss self.project = project } var body: some HTML { - div( - .id("projectForm"), - .class( - """ - fixed top-40 left-[25vw] w-1/2 z-50 text-gray-800 - bg-gray-200 border border-gray-400 - rounded-lg shadow-lg mx-10 - """ - ) - ) { + ModalForm(id: "projectForm", dismiss: dismiss) { h1(.class("text-3xl font-bold pb-6 ps-2")) { "Project" } form(.class("space-y-4 p-4")) { div { diff --git a/Sources/ViewController/Views/Sidebar.swift b/Sources/ViewController/Views/Sidebar.swift index eec40cf..7e8a80e 100644 --- a/Sources/ViewController/Views/Sidebar.swift +++ b/Sources/ViewController/Views/Sidebar.swift @@ -1,9 +1,12 @@ import Elementary +import ManualDCore import Styleguide // TODO: Need to add active to sidebar links. struct Sidebar: HTML { + let active: ActiveTab + var body: some HTML { aside( .class( @@ -14,12 +17,20 @@ struct Sidebar: HTML { """ ) ) { - row(title: "Project", icon: .mapPin, href: "/projects") - row(title: "Rooms", icon: .doorClosed, href: "/rooms") - row(title: "Equivalent Lengths", icon: .rulerDimensionLine, href: "/effective-lengths") - row(title: "Friction Rate", icon: .squareFunction, href: "/friction-rate") - .attributes(.data("active", value: "true")) + row(title: "Project", icon: .mapPin, route: .project(.index)) + .attributes(.data("active", value: active == .projects ? "true" : "false")) + + row(title: "Rooms", icon: .doorClosed, route: .room(.index)) + .attributes(.data("active", value: active == .rooms ? "true" : "false")) + + row(title: "Equivalent Lengths", icon: .rulerDimensionLine, route: .effectiveLength(.index)) + .attributes(.data("active", value: active == .effectiveLength ? "true" : "false")) + + row(title: "Friction Rate", icon: .squareFunction, route: .frictionRate(.index)) + .attributes(.data("active", value: active == .frictionRate ? "true" : "false")) + row(title: "Duct Sizes", icon: .wind, href: "#") + .attributes(.data("active", value: active == .ductSizing ? "true" : "false")) } } @@ -46,4 +57,22 @@ struct Sidebar: HTML { } } } + + private func row( + title: String, + icon: Icon.Key, + route: SiteRoute.View + ) -> some HTML { + row(title: title, icon: icon, href: SiteRoute.View.router.path(for: route)) + } +} + +extension Sidebar { + enum ActiveTab: Equatable, Sendable { + case projects + case rooms + case effectiveLength + case frictionRate + case ductSizing + } }