WIP: Adds a modal form view and integrates into current forms.

This commit is contained in:
2026-01-02 08:27:31 -05:00
parent 8fe650e142
commit 54847d0b34
8 changed files with 106 additions and 66 deletions

View File

@@ -0,0 +1,37 @@
import Elementary
public struct ModalForm<T: HTML>: 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
}
}
}
}

View File

@@ -24,14 +24,11 @@ extension SiteRoute.View.ProjectRoute {
func renderView(isHtmxRequest: Bool) async throws -> AnySendableHTML { func renderView(isHtmxRequest: Bool) async throws -> AnySendableHTML {
switch self { switch self {
case .index: case .index:
return MainPage { return MainPage(active: .projects) {
ProjectView(project: .mock) ProjectView(project: .mock)
} }
case .form(let dismiss): case .form(let dismiss):
guard !dismiss else { return ProjectForm(dismiss: dismiss)
return div(.id("projectForm")) {}
}
return ProjectForm()
case .create: case .create:
return mainPage return mainPage
@@ -48,7 +45,7 @@ extension SiteRoute.View.RoomRoute {
} }
return RoomForm() return RoomForm()
case .index: case .index:
return MainPage { return MainPage(active: .rooms) {
RoomsView(rooms: Room.mocks) RoomsView(rooms: Room.mocks)
} }
} }
@@ -59,19 +56,16 @@ extension SiteRoute.View.FrictionRateRoute {
func renderView(isHtmxRequest: Bool) async throws -> AnySendableHTML { func renderView(isHtmxRequest: Bool) async throws -> AnySendableHTML {
switch self { switch self {
case .index: case .index:
return MainPage { return MainPage(active: .frictionRate) {
FrictionRateView() FrictionRateView()
} }
case .form(let type, let dismiss): case .form(let type, let dismiss):
guard !dismiss else {
return div(.id(type.id)) {}
}
// FIX: Forms need to reference existing items. // FIX: Forms need to reference existing items.
switch type { switch type {
case .equipmentInfo: case .equipmentInfo:
return EquipmentForm() return EquipmentForm(dismiss: dismiss)
case .componentPressureLoss: case .componentPressureLoss:
return ComponentLossForm() return ComponentLossForm(dismiss: dismiss)
} }
} }
} }
@@ -93,14 +87,11 @@ extension SiteRoute.View.EffectiveLengthRoute {
func renderView(isHtmxRequest: Bool) async throws -> AnySendableHTML { func renderView(isHtmxRequest: Bool) async throws -> AnySendableHTML {
switch self { switch self {
case .index: case .index:
return MainPage { return MainPage(active: .effectiveLength) {
EffectiveLengthsView(effectiveLengths: EffectiveLength.mocks) EffectiveLengthsView(effectiveLengths: EffectiveLength.mocks)
} }
case .form(let dismiss): case .form(let dismiss):
guard !dismiss else { return EffectiveLengthForm(dismiss: dismiss)
return div(.id("effectiveLengthForm")) {}
}
return EffectiveLengthForm()
case .field(let type): case .field(let type):
switch type { switch type {
@@ -114,7 +105,7 @@ extension SiteRoute.View.EffectiveLengthRoute {
} }
private let mainPage: AnySendableHTML = { private let mainPage: AnySendableHTML = {
MainPage { MainPage(active: .projects) {
div { div {
h1 { "It works!" } h1 { "It works!" }
} }

View File

@@ -4,18 +4,20 @@ import ManualDCore
import Styleguide import Styleguide
struct EffectiveLengthForm: HTML, Sendable { struct EffectiveLengthForm: HTML, Sendable {
let dismiss: Bool
var body: some HTML { var body: some HTML {
div( // div(
.id("effectiveLengthForm"), // .id("effectiveLengthForm"),
.class( // .class(
""" // """
fixed top-40 left-[25vw] w-1/2 z-50 text-gray-800 // fixed top-40 left-[25vw] w-1/2 z-50 text-gray-800
bg-gray-200 border border-gray-400 // bg-gray-200 border border-gray-400
rounded-lg shadow-lg mx-10 // rounded-lg shadow-lg mx-10
""" // """
) // )
) { // ) {
ModalForm(id: "effectiveLengthForm", dismiss: dismiss) {
h1(.class("text-2xl font-bold")) { "Effective Length" } h1(.class("text-2xl font-bold")) { "Effective Length" }
form(.class("space-y-4 p-4")) { form(.class("space-y-4 p-4")) {
div { div {
@@ -25,7 +27,6 @@ struct EffectiveLengthForm: HTML, Sendable {
} }
div { div {
label(.for("type")) { "Type" } label(.for("type")) { "Type" }
// FIX: Add select field.
select( select(
.id("type"), .name("type"), .id("type"), .name("type"),
.class("w-full border rounded-md") .class("w-full border rounded-md")

View File

@@ -4,17 +4,10 @@ import ManualDCore
import Styleguide import Styleguide
struct ComponentLossForm: HTML, Sendable { struct ComponentLossForm: HTML, Sendable {
let dismiss: Bool
var body: some HTML { var body: some HTML {
div( ModalForm(id: "componentLossForm", dismiss: dismiss) {
.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
"""
)
) {
h1(.class("text-2xl font-bold")) { "Component Loss" } h1(.class("text-2xl font-bold")) { "Component Loss" }
form(.class("space-y-4 p-4")) { form(.class("space-y-4 p-4")) {
div { div {

View File

@@ -5,17 +5,10 @@ import Styleguide
// TODO: Have form hold onto equipment info model to edit. // TODO: Have form hold onto equipment info model to edit.
struct EquipmentForm: HTML, Sendable { struct EquipmentForm: HTML, Sendable {
let dismiss: Bool
var body: some HTML { var body: some HTML {
div( ModalForm(id: "equipmentForm", dismiss: dismiss) {
.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
"""
)
) {
h1(.class("text-3xl font-bold pb-6 ps-2")) { "Equipment Info" } h1(.class("text-3xl font-bold pb-6 ps-2")) { "Equipment Info" }
form(.class("space-y-4 p-4")) { form(.class("space-y-4 p-4")) {
div { div {

View File

@@ -4,8 +4,10 @@ public struct MainPage<Inner: HTML>: SendableHTMLDocument where Inner: Sendable
public var title: String { "Manual-D" } public var title: String { "Manual-D" }
public var lang: String { "en" } public var lang: String { "en" }
let inner: Inner let inner: Inner
let activeTab: Sidebar.ActiveTab
init(_ inner: () -> Inner) { init(active activeTab: Sidebar.ActiveTab, _ inner: () -> Inner) {
self.activeTab = activeTab
self.inner = inner() self.inner = inner()
} }
@@ -22,7 +24,7 @@ public struct MainPage<Inner: HTML>: SendableHTMLDocument where Inner: Sendable
public var body: some HTML { public var body: some HTML {
div(.class("bg-white dark:bg-gray-800 dark:text-white")) { div(.class("bg-white dark:bg-gray-800 dark:text-white")) {
div(.class("flex flex-row")) { div(.class("flex flex-row")) {
Sidebar() Sidebar(active: activeTab)
main(.class("flex flex-col h-screen w-full")) { main(.class("flex flex-col h-screen w-full")) {
inner inner
} }

View File

@@ -6,24 +6,18 @@ import Styleguide
struct ProjectForm: HTML, Sendable { struct ProjectForm: HTML, Sendable {
let project: Project? let project: Project?
let dismiss: Bool
init( init(
dismiss: Bool,
project: Project? = nil project: Project? = nil
) { ) {
self.dismiss = dismiss
self.project = project self.project = project
} }
var body: some HTML { var body: some HTML {
div( ModalForm(id: "projectForm", dismiss: dismiss) {
.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
"""
)
) {
h1(.class("text-3xl font-bold pb-6 ps-2")) { "Project" } h1(.class("text-3xl font-bold pb-6 ps-2")) { "Project" }
form(.class("space-y-4 p-4")) { form(.class("space-y-4 p-4")) {
div { div {

View File

@@ -1,9 +1,12 @@
import Elementary import Elementary
import ManualDCore
import Styleguide import Styleguide
// TODO: Need to add active to sidebar links. // TODO: Need to add active to sidebar links.
struct Sidebar: HTML { struct Sidebar: HTML {
let active: ActiveTab
var body: some HTML { var body: some HTML {
aside( aside(
.class( .class(
@@ -14,12 +17,20 @@ struct Sidebar: HTML {
""" """
) )
) { ) {
row(title: "Project", icon: .mapPin, href: "/projects") row(title: "Project", icon: .mapPin, route: .project(.index))
row(title: "Rooms", icon: .doorClosed, href: "/rooms") .attributes(.data("active", value: active == .projects ? "true" : "false"))
row(title: "Equivalent Lengths", icon: .rulerDimensionLine, href: "/effective-lengths")
row(title: "Friction Rate", icon: .squareFunction, href: "/friction-rate") row(title: "Rooms", icon: .doorClosed, route: .room(.index))
.attributes(.data("active", value: "true")) .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: "#") 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<HTMLTag.a> {
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
}
} }