navigation fixed
This commit is contained in:
@@ -30,8 +30,22 @@ struct SystemsView: View {
|
||||
@Environment(\.modelContext) private var modelContext
|
||||
@EnvironmentObject var unitSettings: UnitSystemSettings
|
||||
@Query(sort: \ElectricalSystem.timestamp, order: .reverse) private var systems: [ElectricalSystem]
|
||||
@State private var newSystemToEdit: ElectricalSystem?
|
||||
@State private var systemToEdit: ElectricalSystem?
|
||||
@State private var systemNavigationTarget: SystemNavigationTarget?
|
||||
|
||||
private struct SystemNavigationTarget: Identifiable, Hashable {
|
||||
let id = UUID()
|
||||
let system: ElectricalSystem
|
||||
let presentSystemEditor: Bool
|
||||
let loadToOpenOnAppear: SavedLoad?
|
||||
|
||||
static func == (lhs: SystemNavigationTarget, rhs: SystemNavigationTarget) -> Bool {
|
||||
lhs.id == rhs.id
|
||||
}
|
||||
|
||||
func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(id)
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
@@ -52,9 +66,6 @@ struct SystemsView: View {
|
||||
.font(.title3)
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
.onTapGesture {
|
||||
systemToEdit = system
|
||||
}
|
||||
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(system.name)
|
||||
@@ -93,27 +104,11 @@ struct SystemsView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationDestination(item: $newSystemToEdit) { system in
|
||||
LoadsView(system: system)
|
||||
}
|
||||
.sheet(item: $systemToEdit) { system in
|
||||
SystemEditorView(
|
||||
systemName: Binding(
|
||||
get: { system.name },
|
||||
set: { system.name = $0 }
|
||||
),
|
||||
location: Binding(
|
||||
get: { system.location },
|
||||
set: { system.location = $0 }
|
||||
),
|
||||
iconName: Binding(
|
||||
get: { system.iconName },
|
||||
set: { system.iconName = $0 }
|
||||
),
|
||||
colorName: Binding(
|
||||
get: { system.colorName },
|
||||
set: { system.colorName = $0 }
|
||||
)
|
||||
.navigationDestination(item: $systemNavigationTarget) { target in
|
||||
LoadsView(
|
||||
system: target.system,
|
||||
presentSystemEditorOnAppear: target.presentSystemEditor,
|
||||
loadToOpenOnAppear: target.loadToOpenOnAppear
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -129,49 +124,113 @@ struct SystemsView: View {
|
||||
.fill(Color.blue.opacity(0.1))
|
||||
.frame(width: 80, height: 80)
|
||||
|
||||
Image(systemName: "building.2")
|
||||
Image(systemName: "bolt.circle")
|
||||
.font(.system(size: 40))
|
||||
.foregroundColor(.blue)
|
||||
}
|
||||
|
||||
VStack(spacing: 8) {
|
||||
Text("No Systems Yet")
|
||||
Text("Welcome to Cable by VoltPlan")
|
||||
.font(.title2)
|
||||
.fontWeight(.semibold)
|
||||
.foregroundColor(.primary)
|
||||
|
||||
Text("Create your first electrical system to start managing loads and calculations.")
|
||||
Text("We'll create your first system and component so you can jump straight into the calculator.")
|
||||
.font(.body)
|
||||
.foregroundColor(.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.padding(.horizontal, 32)
|
||||
}
|
||||
|
||||
Button(action: {
|
||||
createNewSystem()
|
||||
}) {
|
||||
HStack(spacing: 8) {
|
||||
Image(systemName: "plus.circle.fill")
|
||||
.font(.system(size: 16))
|
||||
Text("Create System")
|
||||
.fontWeight(.medium)
|
||||
VStack(spacing: 12) {
|
||||
Button(action: {
|
||||
startComponentOnboarding()
|
||||
}) {
|
||||
HStack(spacing: 8) {
|
||||
Image(systemName: "plus.circle.fill")
|
||||
.font(.system(size: 16))
|
||||
Text("Create Component")
|
||||
.fontWeight(.medium)
|
||||
}
|
||||
.foregroundColor(.white)
|
||||
.frame(maxWidth: .infinity)
|
||||
.frame(height: 50)
|
||||
.background(Color.blue)
|
||||
.cornerRadius(12)
|
||||
}
|
||||
.foregroundColor(.white)
|
||||
.frame(maxWidth: .infinity)
|
||||
.frame(height: 50)
|
||||
.background(Color.blue)
|
||||
.cornerRadius(12)
|
||||
.buttonStyle(.plain)
|
||||
|
||||
Button(action: {
|
||||
// TODO: Open VoltPlan component library
|
||||
print("Opening VoltPlan component library...")
|
||||
}) {
|
||||
HStack(spacing: 8) {
|
||||
Image(systemName: "square.grid.3x3")
|
||||
.font(.system(size: 16))
|
||||
Text("Browse VoltPlan Library")
|
||||
.fontWeight(.medium)
|
||||
Image(systemName: "arrow.up.right")
|
||||
.font(.system(size: 12))
|
||||
}
|
||||
.foregroundColor(.blue)
|
||||
.frame(maxWidth: .infinity)
|
||||
.frame(height: 50)
|
||||
.background(Color.blue.opacity(0.1))
|
||||
.cornerRadius(12)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.padding(.horizontal, 32)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
Spacer()
|
||||
|
||||
VStack(spacing: 8) {
|
||||
HStack(spacing: 6) {
|
||||
Image(systemName: "exclamationmark.triangle.fill")
|
||||
.foregroundColor(.orange)
|
||||
.font(.system(size: 16))
|
||||
Text("Important Safety Notice")
|
||||
.font(.headline)
|
||||
.fontWeight(.semibold)
|
||||
}
|
||||
|
||||
Text("This app provides estimates for educational purposes only. Always consult qualified electricians and follow local electrical codes for actual installations. Electrical work can be dangerous and should only be performed by licensed professionals.")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
.padding(.horizontal, 24)
|
||||
}
|
||||
.padding(.bottom, 32)
|
||||
}
|
||||
}
|
||||
|
||||
private func createNewSystem() {
|
||||
let system = makeSystem()
|
||||
navigateToSystem(system, presentSystemEditor: true, loadToOpen: nil)
|
||||
}
|
||||
|
||||
private func navigateToSystem(_ system: ElectricalSystem, presentSystemEditor: Bool, loadToOpen: SavedLoad?, animated: Bool = true) {
|
||||
let target = SystemNavigationTarget(
|
||||
system: system,
|
||||
presentSystemEditor: presentSystemEditor,
|
||||
loadToOpenOnAppear: loadToOpen
|
||||
)
|
||||
|
||||
if animated {
|
||||
systemNavigationTarget = target
|
||||
} else {
|
||||
var transaction = Transaction()
|
||||
transaction.disablesAnimations = true
|
||||
withTransaction(transaction) {
|
||||
systemNavigationTarget = target
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
private func makeSystem() -> ElectricalSystem {
|
||||
let existingNames = Set(systems.map { $0.name })
|
||||
var systemName = "New System"
|
||||
var counter = 1
|
||||
@@ -188,14 +247,47 @@ struct SystemsView: View {
|
||||
colorName: "blue"
|
||||
)
|
||||
modelContext.insert(newSystem)
|
||||
|
||||
newSystemToEdit = newSystem
|
||||
return newSystem
|
||||
}
|
||||
|
||||
private func startComponentOnboarding() {
|
||||
let system = makeSystem()
|
||||
let load = createNewLoad(in: system)
|
||||
navigateToSystem(system, presentSystemEditor: false, loadToOpen: load, animated: false)
|
||||
}
|
||||
|
||||
private func createNewLoad(in system: ElectricalSystem) -> SavedLoad {
|
||||
let newLoad = SavedLoad(
|
||||
name: "New Load",
|
||||
voltage: 12.0,
|
||||
current: 5.0,
|
||||
power: 60.0,
|
||||
length: 10.0,
|
||||
crossSection: 1.0,
|
||||
iconName: "lightbulb",
|
||||
colorName: "blue",
|
||||
isWattMode: false,
|
||||
system: system
|
||||
)
|
||||
modelContext.insert(newLoad)
|
||||
return newLoad
|
||||
}
|
||||
|
||||
private func deleteSystems(offsets: IndexSet) {
|
||||
withAnimation {
|
||||
for index in offsets {
|
||||
modelContext.delete(systems[index])
|
||||
let system = systems[index]
|
||||
deleteLoads(for: system)
|
||||
modelContext.delete(system)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func deleteLoads(for system: ElectricalSystem) {
|
||||
let descriptor = FetchDescriptor<SavedLoad>()
|
||||
if let loads = try? modelContext.fetch(descriptor) {
|
||||
for load in loads where load.system == system {
|
||||
modelContext.delete(load)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -225,11 +317,18 @@ struct LoadsView: View {
|
||||
@EnvironmentObject var unitSettings: UnitSystemSettings
|
||||
@Query(sort: \SavedLoad.timestamp, order: .reverse) private var allLoads: [SavedLoad]
|
||||
@State private var newLoadToEdit: SavedLoad?
|
||||
@State private var showingSystemEditor = false
|
||||
@State private var hasPresentedSystemEditorOnAppear = false
|
||||
@State private var hasOpenedLoadOnAppear = false
|
||||
|
||||
let system: ElectricalSystem
|
||||
private let presentSystemEditorOnAppear: Bool
|
||||
private let loadToOpenOnAppear: SavedLoad?
|
||||
|
||||
init(system: ElectricalSystem) {
|
||||
init(system: ElectricalSystem, presentSystemEditorOnAppear: Bool = false, loadToOpenOnAppear: SavedLoad? = nil) {
|
||||
self.system = system
|
||||
self.presentSystemEditorOnAppear = presentSystemEditorOnAppear
|
||||
self.loadToOpenOnAppear = loadToOpenOnAppear
|
||||
}
|
||||
|
||||
private var savedLoads: [SavedLoad] {
|
||||
@@ -237,117 +336,173 @@ struct LoadsView: View {
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
VStack(spacing: 0) {
|
||||
librarySection
|
||||
|
||||
if savedLoads.isEmpty {
|
||||
emptyStateView
|
||||
} else {
|
||||
List {
|
||||
ForEach(savedLoads) { load in
|
||||
NavigationLink(destination: CalculatorView(savedLoad: load)) {
|
||||
HStack(spacing: 12) {
|
||||
ZStack {
|
||||
RoundedRectangle(cornerRadius: 10)
|
||||
.fill(colorForName(load.colorName))
|
||||
.frame(width: 44, height: 44)
|
||||
|
||||
Image(systemName: load.iconName)
|
||||
.font(.title3)
|
||||
.foregroundColor(.white)
|
||||
VStack(spacing: 0) {
|
||||
librarySection
|
||||
|
||||
if savedLoads.isEmpty {
|
||||
emptyStateView
|
||||
} else {
|
||||
List {
|
||||
ForEach(savedLoads) { load in
|
||||
NavigationLink(destination: CalculatorView(savedLoad: load)) {
|
||||
HStack(spacing: 12) {
|
||||
ZStack {
|
||||
RoundedRectangle(cornerRadius: 10)
|
||||
.fill(colorForName(load.colorName))
|
||||
.frame(width: 44, height: 44)
|
||||
|
||||
Image(systemName: load.iconName)
|
||||
.font(.title3)
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
HStack {
|
||||
Text(load.name)
|
||||
.fontWeight(.medium)
|
||||
Spacer()
|
||||
Text(load.timestamp, format: .dateTime.month().day().hour().minute())
|
||||
.foregroundColor(.secondary)
|
||||
.font(.caption)
|
||||
}
|
||||
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
HStack {
|
||||
Text(load.name)
|
||||
|
||||
// Secondary info
|
||||
HStack {
|
||||
Group {
|
||||
Text(String(format: "%.1fV", load.voltage))
|
||||
Text("•")
|
||||
if load.isWattMode {
|
||||
Text(String(format: "%.0fW", load.power))
|
||||
} else {
|
||||
Text(String(format: "%.1fA", load.current))
|
||||
}
|
||||
Text("•")
|
||||
Text(String(format: "%.1f%@",
|
||||
unitSettings.unitSystem == .metric ? load.length : load.length * 3.28084,
|
||||
unitSettings.unitSystem.lengthUnit))
|
||||
}
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
Spacer()
|
||||
}
|
||||
|
||||
// Prominent fuse and wire gauge display
|
||||
HStack(spacing: 12) {
|
||||
HStack(spacing: 4) {
|
||||
Text("FUSE")
|
||||
.font(.caption2)
|
||||
.fontWeight(.medium)
|
||||
Spacer()
|
||||
Text(load.timestamp, format: .dateTime.month().day().hour().minute())
|
||||
.foregroundColor(.secondary)
|
||||
.font(.caption)
|
||||
Text("\(recommendedFuse(for: load))A")
|
||||
.font(.subheadline)
|
||||
.fontWeight(.bold)
|
||||
.foregroundColor(.orange)
|
||||
}
|
||||
.padding(.horizontal, 8)
|
||||
.padding(.vertical, 4)
|
||||
.background(Color.orange.opacity(0.1))
|
||||
.cornerRadius(6)
|
||||
|
||||
|
||||
// Secondary info
|
||||
HStack {
|
||||
Group {
|
||||
Text(String(format: "%.1fV", load.voltage))
|
||||
Text("•")
|
||||
if load.isWattMode {
|
||||
Text(String(format: "%.0fW", load.power))
|
||||
} else {
|
||||
Text(String(format: "%.1fA", load.current))
|
||||
}
|
||||
Text("•")
|
||||
Text(String(format: "%.1f%@",
|
||||
unitSettings.unitSystem == .metric ? load.length : load.length * 3.28084,
|
||||
unitSettings.unitSystem.lengthUnit))
|
||||
}
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
Spacer()
|
||||
HStack(spacing: 4) {
|
||||
Text("WIRE")
|
||||
.font(.caption2)
|
||||
.fontWeight(.medium)
|
||||
.foregroundColor(.secondary)
|
||||
Text(String(format: unitSettings.unitSystem == .imperial ? "%.0f AWG" : "%.1fmm²",
|
||||
unitSettings.unitSystem == .imperial ? awgFromCrossSection(load.crossSection) : load.crossSection))
|
||||
.font(.subheadline)
|
||||
.fontWeight(.bold)
|
||||
.foregroundColor(.blue)
|
||||
}
|
||||
.padding(.horizontal, 8)
|
||||
.padding(.vertical, 4)
|
||||
.background(Color.blue.opacity(0.1))
|
||||
.cornerRadius(6)
|
||||
|
||||
// Prominent fuse and wire gauge display
|
||||
HStack(spacing: 12) {
|
||||
HStack(spacing: 4) {
|
||||
Text("FUSE")
|
||||
.font(.caption2)
|
||||
.fontWeight(.medium)
|
||||
.foregroundColor(.secondary)
|
||||
Text("\(recommendedFuse(for: load))A")
|
||||
.font(.subheadline)
|
||||
.fontWeight(.bold)
|
||||
.foregroundColor(.orange)
|
||||
}
|
||||
.padding(.horizontal, 8)
|
||||
.padding(.vertical, 4)
|
||||
.background(Color.orange.opacity(0.1))
|
||||
.cornerRadius(6)
|
||||
|
||||
HStack(spacing: 4) {
|
||||
Text("WIRE")
|
||||
.font(.caption2)
|
||||
.fontWeight(.medium)
|
||||
.foregroundColor(.secondary)
|
||||
Text(String(format: unitSettings.unitSystem == .imperial ? "%.0f AWG" : "%.1fmm²",
|
||||
unitSettings.unitSystem == .imperial ? awgFromCrossSection(load.crossSection) : load.crossSection))
|
||||
.font(.subheadline)
|
||||
.fontWeight(.bold)
|
||||
.foregroundColor(.blue)
|
||||
}
|
||||
.padding(.horizontal, 8)
|
||||
.padding(.vertical, 4)
|
||||
.background(Color.blue.opacity(0.1))
|
||||
.cornerRadius(6)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
Spacer()
|
||||
}
|
||||
}
|
||||
.padding(.vertical, 4)
|
||||
}
|
||||
.padding(.vertical, 4)
|
||||
}
|
||||
.onDelete(perform: deleteLoads)
|
||||
}
|
||||
.onDelete(perform: deleteLoads)
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .principal) {
|
||||
Button(action: {
|
||||
showingSystemEditor = true
|
||||
}) {
|
||||
HStack(spacing: 8) {
|
||||
ZStack {
|
||||
RoundedRectangle(cornerRadius: 6)
|
||||
.fill(colorForName(system.colorName))
|
||||
.frame(width: 24, height: 24)
|
||||
|
||||
Image(systemName: system.iconName)
|
||||
.font(.system(size: 12))
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
|
||||
Text(system.name)
|
||||
.font(.headline)
|
||||
.fontWeight(.semibold)
|
||||
.foregroundColor(.primary)
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle(system.name)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
HStack {
|
||||
Button(action: {
|
||||
createNewLoad()
|
||||
}) {
|
||||
Image(systemName: "plus")
|
||||
}
|
||||
EditButton()
|
||||
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
HStack {
|
||||
Button(action: {
|
||||
createNewLoad()
|
||||
}) {
|
||||
Image(systemName: "plus")
|
||||
}
|
||||
EditButton()
|
||||
}
|
||||
}
|
||||
.navigationDestination(item: $newLoadToEdit) { load in
|
||||
CalculatorView(savedLoad: load)
|
||||
}
|
||||
.navigationDestination(item: $newLoadToEdit) { load in
|
||||
CalculatorView(savedLoad: load)
|
||||
}
|
||||
.sheet(isPresented: $showingSystemEditor) {
|
||||
SystemEditorView(
|
||||
systemName: Binding(
|
||||
get: { system.name },
|
||||
set: { system.name = $0 }
|
||||
),
|
||||
location: Binding(
|
||||
get: { system.location },
|
||||
set: { system.location = $0 }
|
||||
),
|
||||
iconName: Binding(
|
||||
get: { system.iconName },
|
||||
set: { system.iconName = $0 }
|
||||
),
|
||||
colorName: Binding(
|
||||
get: { system.colorName },
|
||||
set: { system.colorName = $0 }
|
||||
)
|
||||
)
|
||||
}
|
||||
.onAppear {
|
||||
if presentSystemEditorOnAppear && !hasPresentedSystemEditorOnAppear {
|
||||
hasPresentedSystemEditorOnAppear = true
|
||||
DispatchQueue.main.async {
|
||||
showingSystemEditor = true
|
||||
}
|
||||
}
|
||||
|
||||
if let loadToOpen = loadToOpenOnAppear, !hasOpenedLoadOnAppear {
|
||||
hasOpenedLoadOnAppear = true
|
||||
DispatchQueue.main.async {
|
||||
newLoadToEdit = loadToOpen
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -393,94 +548,50 @@ struct LoadsView: View {
|
||||
VStack(spacing: 0) {
|
||||
Spacer()
|
||||
|
||||
VStack(spacing: 24) {
|
||||
// Icon
|
||||
VStack(spacing: 20) {
|
||||
ZStack {
|
||||
Circle()
|
||||
.fill(Color.blue.opacity(0.1))
|
||||
.frame(width: 80, height: 80)
|
||||
.frame(width: 72, height: 72)
|
||||
|
||||
Image(systemName: "bolt.circle")
|
||||
.font(.system(size: 40))
|
||||
.font(.system(size: 34))
|
||||
.foregroundColor(.blue)
|
||||
}
|
||||
|
||||
// Title and subtitle
|
||||
VStack(spacing: 8) {
|
||||
Text("No Loads Yet")
|
||||
.font(.title2)
|
||||
VStack(spacing: 6) {
|
||||
Text("No Components Yet")
|
||||
.font(.title3)
|
||||
.fontWeight(.semibold)
|
||||
.foregroundColor(.primary)
|
||||
|
||||
Text("Get started by creating a new electrical load calculation or browse components from the VoltPlan library.")
|
||||
Text("Add a component to this system to see cable and fuse recommendations.")
|
||||
.font(.body)
|
||||
.foregroundColor(.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
.padding(.horizontal, 32)
|
||||
}
|
||||
|
||||
// Action buttons
|
||||
VStack(spacing: 12) {
|
||||
Button(action: {
|
||||
createNewLoad()
|
||||
}) {
|
||||
HStack(spacing: 8) {
|
||||
Image(systemName: "plus.circle.fill")
|
||||
.font(.system(size: 16))
|
||||
Text("Create New Load")
|
||||
.fontWeight(.medium)
|
||||
}
|
||||
.foregroundColor(.white)
|
||||
.frame(maxWidth: .infinity)
|
||||
.frame(height: 50)
|
||||
.background(Color.blue)
|
||||
.cornerRadius(12)
|
||||
Button(action: {
|
||||
createNewLoad()
|
||||
}) {
|
||||
HStack(spacing: 8) {
|
||||
Image(systemName: "plus.circle.fill")
|
||||
.font(.system(size: 16))
|
||||
Text("Create Component")
|
||||
.fontWeight(.medium)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
|
||||
Button(action: {
|
||||
// TODO: Open VoltPlan component library
|
||||
print("Opening VoltPlan component library...")
|
||||
}) {
|
||||
HStack(spacing: 8) {
|
||||
Image(systemName: "square.grid.3x3")
|
||||
.font(.system(size: 16))
|
||||
Text("Browse VoltPlan Library")
|
||||
.fontWeight(.medium)
|
||||
Image(systemName: "arrow.up.right")
|
||||
.font(.system(size: 12))
|
||||
}
|
||||
.foregroundColor(.blue)
|
||||
.frame(maxWidth: .infinity)
|
||||
.frame(height: 50)
|
||||
.background(Color.blue.opacity(0.1))
|
||||
.cornerRadius(12)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.foregroundColor(.white)
|
||||
.frame(maxWidth: .infinity)
|
||||
.frame(height: 48)
|
||||
.background(Color.blue)
|
||||
.cornerRadius(12)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.padding(.horizontal, 32)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
// Safety disclaimer
|
||||
VStack(spacing: 8) {
|
||||
HStack(spacing: 6) {
|
||||
Image(systemName: "exclamationmark.triangle.fill")
|
||||
.foregroundColor(.orange)
|
||||
.font(.system(size: 16))
|
||||
Text("Important Safety Notice")
|
||||
.font(.headline)
|
||||
.fontWeight(.semibold)
|
||||
}
|
||||
|
||||
Text("This app provides estimates for educational purposes only. Always consult qualified electricians and follow local electrical codes for actual installations. Electrical work can be dangerous and should only be performed by licensed professionals.")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
.padding(.horizontal, 24)
|
||||
}
|
||||
.padding(.bottom, 32)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
struct ItemEditorView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@@ -93,8 +94,11 @@ struct ItemEditorView: View {
|
||||
}
|
||||
|
||||
Section("Details") {
|
||||
TextField(nameFieldLabel, text: $tempName)
|
||||
.autocapitalization(.words)
|
||||
AutoSelectTextField(
|
||||
text: $tempName,
|
||||
placeholder: nameFieldLabel,
|
||||
autoFocus: true
|
||||
)
|
||||
|
||||
additionalFields()
|
||||
}
|
||||
@@ -174,4 +178,58 @@ struct ItemEditorView: View {
|
||||
iconName = tempIconName
|
||||
colorName = tempColorName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct AutoSelectTextField: UIViewRepresentable {
|
||||
@Binding var text: String
|
||||
let placeholder: String
|
||||
let autoFocus: Bool
|
||||
|
||||
func makeUIView(context: Context) -> UITextField {
|
||||
let textField = UITextField(frame: .zero)
|
||||
textField.delegate = context.coordinator
|
||||
textField.placeholder = placeholder
|
||||
textField.text = text
|
||||
textField.autocapitalizationType = .words
|
||||
textField.clearButtonMode = .whileEditing
|
||||
textField.returnKeyType = .done
|
||||
textField.addTarget(context.coordinator, action: #selector(Coordinator.textChanged(_:)), for: .editingChanged)
|
||||
return textField
|
||||
}
|
||||
|
||||
func updateUIView(_ uiView: UITextField, context: Context) {
|
||||
uiView.text = text
|
||||
uiView.placeholder = placeholder
|
||||
uiView.autocapitalizationType = .words
|
||||
|
||||
if autoFocus, !context.coordinator.didFocus {
|
||||
context.coordinator.didFocus = true
|
||||
DispatchQueue.main.async {
|
||||
uiView.becomeFirstResponder()
|
||||
uiView.selectAll(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
Coordinator(text: $text)
|
||||
}
|
||||
|
||||
final class Coordinator: NSObject, UITextFieldDelegate {
|
||||
@Binding var text: String
|
||||
var didFocus = false
|
||||
|
||||
init(text: Binding<String>) {
|
||||
self._text = text
|
||||
}
|
||||
|
||||
@objc func textChanged(_ sender: UITextField) {
|
||||
text = sender.text ?? ""
|
||||
}
|
||||
|
||||
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
||||
textField.resignFirstResponder()
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,10 @@ struct SystemEditorView: View {
|
||||
@State private var tempLocation: String
|
||||
|
||||
private let systemIcons = [
|
||||
"building.2", "house", "building", "factory", "office.building", "tent", "car.garage", "sailboat",
|
||||
"airplane", "ferry", "bus", "truck.box", "van.side", "rv",
|
||||
"building.2", "house", "building", "tent", "sailboat",
|
||||
"airplane", "ferry", "bus", "truck.box",
|
||||
"server.rack", "externaldrive", "cpu", "gear", "wrench.adjustable", "hammer",
|
||||
"lightbulb", "bolt", "powerplug", "battery.100", "solar.panel", "windmill",
|
||||
"lightbulb", "bolt", "powerplug", "battery.100","sun.max",
|
||||
"engine.combustion", "fuelpump", "drop", "flame", "snowflake", "thermometer"
|
||||
]
|
||||
|
||||
@@ -63,4 +63,4 @@ struct SystemEditorView: View {
|
||||
@State var color = "blue"
|
||||
|
||||
return SystemEditorView(systemName: $name, location: $location, iconName: $icon, colorName: $color)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user