Files
swift-hvac-toolbox/Sources/Styleguide/FormElements.swift
Michael Housh 159031a023
Some checks are pending
CI / macOS (debug, 16.2) (push) Waiting to run
CI / macOS (release, 16.2) (push) Waiting to run
CI / ubuntu (push) Successful in 6m58s
feat: Adds hydronic system pressure calculator.
2025-03-07 15:15:28 -05:00

152 lines
3.2 KiB
Swift

import Elementary
/// A styled header for a form element, which consists of an
/// svg image and label / name for the form.
///
public struct FormHeader: HTML, Sendable {
let label: String
let svg: SVGType
public init(
label: String,
svg: SVGType
) {
self.label = label
self.svg = svg
}
public var content: some HTML {
LabeledContent {
h2(.class("text-2xl font-extrabold")) { label }
} label: {
SVG(svg, color: .blue)
}
.attributes(.class("flex items-center gap-3 mb-6"))
}
}
/// A styled form input, does not contain the input type which is generally
/// added at the call site.
///
/// **Example:**
/// ```swift
/// Input(id: "email", placeholder: "Email")
/// .attributes(.type(.email))
/// ```
///
public struct Input: HTML, Sendable {
let id: String
let name: String?
let placeholder: String
public init(id: String, name: String? = nil, placeholder: String) {
self.id = id
self.name = name
self.placeholder = placeholder
}
public var content: some HTML<HTMLTag.input> {
input(
.id(id), .placeholder(placeholder), .name(name ?? id),
.class("""
w-full px-4 py-2 border rounded-md min-h-11
focus:ring-2 focus:ring-yellow-800 focus:border-yellow-800
placeholder-shown:!border-gray-400
invalid:border-red-500 out-of-range:border-red-500
""")
)
}
}
public extension Input {
init<Key>(
id: Key,
name: String? = nil,
placeholder: String
) where Key: RawRepresentable, Key.RawValue == String {
self.init(id: id.rawValue, name: name, placeholder: placeholder)
}
}
/// A style form input label.
public struct InputLabel<InputLabel: HTML>: HTML {
let forInputId: String
let inputLabel: InputLabel
public init(
for forInputId: String,
@HTMLBuilder label: () -> InputLabel
) {
self.forInputId = forInputId
self.inputLabel = label()
}
public var content: some HTML<HTMLTag.label> {
label(
.for(forInputId),
.class("block text-sm font-medium mb-2")
) {
self.inputLabel
}
}
}
extension InputLabel: Sendable where InputLabel: Sendable {}
public struct Select<T>: HTML {
let id: String
let name: String?
let values: [T]
let label: @Sendable (T) -> String
let value: @Sendable (T) -> String
public init(
id: String,
name: String? = nil,
values: [T],
label: @escaping @Sendable (T) -> String,
value: @escaping @Sendable (T) -> String
) {
self.id = id
self.name = name
self.values = values
self.label = label
self.value = value
}
public var content: some HTML<HTMLTag.select> {
select(
.id(id),
.name(name ?? id),
.class("w-full rounded-md border px-4 py-2 min-h-11")
) {
for value in values {
option(.value(self.value(value))) { label(value) }
}
}
}
}
extension Select: Sendable where T: Sendable {}
public extension Select where T: CaseIterable, T: RawRepresentable, T.RawValue: CustomStringConvertible {
init(
for type: T.Type,
id: String,
name: String? = nil,
label: @escaping @Sendable (T) -> String
) {
self.init(
id: id,
name: name,
values: Array(T.allCases),
label: label,
value: { $0.rawValue.description }
)
}
}