214 lines
6.8 KiB
Swift
214 lines
6.8 KiB
Swift
import Elementary
|
|
import ElementaryHTMX
|
|
import Routes
|
|
import Styleguide
|
|
|
|
struct CapacitorForm: HTML, Sendable {
|
|
let mode: Capacitor.Mode
|
|
let response: Capacitor.Response?
|
|
|
|
init(mode: Capacitor.Mode?, response: Capacitor.Response? = nil) {
|
|
self.mode = mode ?? .test
|
|
self.response = response
|
|
}
|
|
|
|
var content: some HTML {
|
|
div(.class("relative")) {
|
|
div(.class("flex flex-wrap justify-between")) {
|
|
FormHeader(label: "Capacitor Calculator - \(mode.rawValue.capitalized) Capacitor", svg: .zap)
|
|
// Mode toggle / buttons.
|
|
|
|
// Mode toggle / buttons.
|
|
Toggle(
|
|
isOn: mode == .test,
|
|
onLabel: "Test Capacitor",
|
|
onAttributes: .hxDefaults(get: .capacitor(.index(mode: .test))),
|
|
offLabel: "Size Capacitor",
|
|
offAttributes: .hxDefaults(get: .capacitor(.index(mode: .size)))
|
|
)
|
|
.attributes(.class("mb-6"))
|
|
}
|
|
|
|
Form(mode: mode).attributes(.class("mt-6"))
|
|
|
|
div(.id("result")) {
|
|
if let response {
|
|
CapacitorResponse(response: response)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct Form: HTML, Sendable {
|
|
let mode: Capacitor.Mode
|
|
|
|
var content: some HTML<HTMLTag.form> {
|
|
form(
|
|
.hx.post(route: .capacitor(.index)),
|
|
.hx.target("#result")
|
|
) {
|
|
div(.class("space-y-6")) {
|
|
switch mode {
|
|
case .size:
|
|
LabeledContent(label: "Running Current: (amps)") {
|
|
Input(id: "runningAmps", placeholder: "Current amps")
|
|
.attributes(.type(.number), .step("0.1"), .min("0.1"), .autofocus, .required)
|
|
}
|
|
LabeledContent(label: "Line Voltage") {
|
|
Input(id: "lineVoltage", placeholder: "Voltage")
|
|
.attributes(.type(.number), .step("0.1"), .min("0.1"), .required)
|
|
}
|
|
LabeledContent(label: "Power Factor") {
|
|
Input(id: "powerFactor", placeholder: "Power factor (0-1)")
|
|
.attributes(.type(.number), .step("0.01"), .min("0.1"), .max("1.00"), .required)
|
|
}
|
|
case .test:
|
|
LabeledContent(label: "Start Winding: (amps)") {
|
|
Input(id: "startWindingAmps", placeholder: "Current amps")
|
|
.attributes(.type(.number), .step("0.1"), .min("0.1"), .autofocus, .required)
|
|
}
|
|
LabeledContent(label: "Run to Common: (volts)") {
|
|
Input(id: "runToCommonVoltage", placeholder: "Voltage")
|
|
.attributes(.type(.number), .step("0.1"), .min("0.1"), .required)
|
|
}
|
|
LabeledContent(label: "Capacitor Rated Size: (µF)") {
|
|
Input(id: "ratedCapacitorSize", placeholder: "Size (optional)")
|
|
.attributes(.type(.number), .step("0.1"), .min("0.1"))
|
|
}
|
|
}
|
|
|
|
div {
|
|
SubmitButton(label: "\(mode == .size ? "Calculate Size" : "Test Capacitor")")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct CapacitorResponse: HTML, Sendable {
|
|
let response: Capacitor.Response
|
|
|
|
var content: some HTML {
|
|
ResultContainer(reset: .capacitor(.index(mode: response.mode))) {
|
|
switch response {
|
|
case let .size(result: result):
|
|
sizeResponse(result)
|
|
case let .test(result: result):
|
|
testResponse(result)
|
|
}
|
|
}
|
|
}
|
|
|
|
private func sizeResponse(_ response: Capacitor.Response.SizeResponse) -> some HTML {
|
|
div {
|
|
div(
|
|
.class("""
|
|
w-full rounded-lg shadow-lg p-4
|
|
border-2 border-blue-600 bg-blue-100 text-blue-600
|
|
""")
|
|
) {
|
|
div(.class("flex justify-center")) {
|
|
span(.class("font-extrabold")) { "Required Capacitor Size" }
|
|
}
|
|
row("Recommended Standard Size", "\(response.standardSize) µF")
|
|
row("Calculated Size:", "\(double: response.capacitance, fractionDigits: 1) µF")
|
|
toleranceRow(response.tolerance)
|
|
}
|
|
WarningBox(
|
|
"Always verify voltage rating matches the application.",
|
|
"Use the next larger size if exact match is unavailable.",
|
|
"Ensure capacitor is rated for continuous duty.",
|
|
"Consider ambient temperature in final selection."
|
|
) { _ in
|
|
span(.class("font-extrabold")) { "Important Notes:" }
|
|
}
|
|
}
|
|
}
|
|
|
|
private func testResponse(_ response: Capacitor.Response.TestResponse) -> some HTML {
|
|
div {
|
|
if let comparison = response.ratedComparison {
|
|
div(
|
|
.class("""
|
|
w-full rounded-lg shadow-lg p-4
|
|
border-2 \(comparison.borderColor) \(comparison.bgColor) \(comparison.textColor)
|
|
""")
|
|
) {
|
|
div(.class("flex font-bold gap-x-4")) {
|
|
SVG(
|
|
comparison.isInRange ? .checkCircle : .exclamation,
|
|
color: comparison.textColor
|
|
)
|
|
span(.class("font-normal")) {
|
|
"Capacitor is \(comparison.isInRange ? "within" : "outside of") acceptable range: (± 6% of rated value)"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
div(.class("grid grid-cols-1 \(response.ratedComparison != nil ? "lg:grid-cols-2" : "") gap-4 mt-8")) {
|
|
div(.class("bg-blue-100 rounded-lg border-2 border-blue-600 text-blue-500 px-4 pb-4")) {
|
|
div(.class("flex justify-center mb-6 mt-2")) {
|
|
span(.class("text-2xl font-extrabold")) { "Measured" }
|
|
}
|
|
row("Capacitance", "\(double: response.capacitance) µF")
|
|
toleranceRow(response.tolerance)
|
|
}
|
|
|
|
if let comparison = response.ratedComparison {
|
|
div(.class("bg-blue-100 rounded-lg border-2 border-blue-600 text-blue-500 px-4 pb-4")) {
|
|
div(.class("flex justify-center mb-6 mt-2")) {
|
|
span(.class("text-2xl font-extrabold")) { "Rated Comparison" }
|
|
}
|
|
row("Rated Value", "\(comparison.value) µF")
|
|
row("Deviation", "\(double: comparison.percentDeviation, fractionDigits: 1)%")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private func row(_ label: String, _ value: String) -> some HTML {
|
|
div(.class("flex justify-between mb-2")) {
|
|
span(.class("font-semibold")) { label }
|
|
span { value }
|
|
}
|
|
}
|
|
|
|
private func toleranceRow(_ tolerance: Capacitor.Tolerance) -> some HTML {
|
|
row(
|
|
"Acceptable Range (±6%):",
|
|
"\(double: tolerance.minimum, fractionDigits: 1) - \(double: tolerance.maximum, fractionDigits: 1) µF"
|
|
)
|
|
}
|
|
|
|
}
|
|
|
|
private extension Capacitor.RatedComparison {
|
|
|
|
var bgColor: String {
|
|
guard isInRange else { return "bg-red-100" }
|
|
return "bg-green-100"
|
|
}
|
|
|
|
var borderColor: String {
|
|
guard isInRange else { return "border-red-600" }
|
|
return "border-green-600"
|
|
}
|
|
|
|
var textColor: String {
|
|
guard isInRange else { return "text-red-600" }
|
|
return "text-green-600"
|
|
}
|
|
}
|
|
|
|
private extension Capacitor.Response {
|
|
var mode: Capacitor.Mode {
|
|
switch self {
|
|
case .size: return .size
|
|
case .test: return .test
|
|
}
|
|
}
|
|
}
|