// // NewRuleSheet.swift // Severed // import SwiftData import SwiftUI /// "New Rule": the three rule-type cards up top, then the preset gallery. /// Picking either one morphs the sheet into the editor; committing saves the /// rule and closes the sheet. struct NewRuleSheet: View { @Environment(\.dismiss) private var dismiss @Environment(\.modelContext) private var modelContext @State private var pendingDraft: RuleDraft? var body: some View { Group { if let pendingDraft { RuleEditorView( mode: .create, draft: pendingDraft, onBack: { self.pendingDraft = nil }, onCommit: { draft in modelContext.insert(draft.makeRule()) dismiss() } ) } else { chooser } } .background(Theme.background) } private var chooser: some View { VStack(spacing: 0) { HStack { CircleIconButton(systemImage: "xmark") { dismiss() } .accessibilityIdentifier("closeNewRuleButton") Spacer() } .overlay( Text("New Rule") .font(.system(size: 18, weight: .semibold)) ) .padding(.horizontal, 20) .padding(.top, 18) ScrollView { VStack(alignment: .leading, spacing: 26) { HStack(spacing: 10) { ForEach(RuleKind.allCases, id: \.self) { kind in kindCard(kind) } } ForEach(RulePresetSection.all) { section in presetSection(section) } } .padding(.horizontal, 20) .padding(.top, 20) .padding(.bottom, 30) } } .foregroundStyle(.white) } private func kindCard(_ kind: RuleKind) -> some View { Button { pendingDraft = RuleDraft(kind: kind) } label: { VStack(spacing: 8) { Image(systemName: kind.symbolName) .font(.system(size: 20, weight: .semibold)) .foregroundStyle(Theme.accent) .frame(height: 26) Text(kind.displayName) .font(.system(size: 14, weight: .semibold)) Text(kind.exampleText) .font(.system(size: 11)) .foregroundStyle(Theme.textTertiary) } .frame(maxWidth: .infinity) .padding(.vertical, 18) .background(Theme.surface, in: RoundedRectangle(cornerRadius: 18)) } .buttonStyle(.plain) .accessibilityIdentifier("ruleKind-\(kind.rawValue)") } private func presetSection(_ section: RulePresetSection) -> some View { VStack(alignment: .leading, spacing: 4) { Text(section.title) .font(.system(size: 18, weight: .bold)) Text(section.subtitle) .font(.system(size: 13)) .foregroundStyle(Theme.textSecondary) LazyVGrid( columns: [GridItem(.flexible(), spacing: 12), GridItem(.flexible())], spacing: 12 ) { ForEach(section.presets) { preset in presetCard(preset) } } .padding(.top, 12) } } private func presetCard(_ preset: RulePreset) -> some View { Button { pendingDraft = RuleDraft(preset: preset) } label: { VStack(alignment: .leading, spacing: 4) { HStack { RuleIconPair(kind: .schedule) Spacer() } Spacer() Image(systemName: preset.symbolName) .font(.system(size: 22, weight: .semibold)) .foregroundStyle(.white.opacity(0.8)) .padding(.bottom, 8) Text(preset.schedule.timeRangeLabel) .font(.system(size: 12)) .foregroundStyle(Theme.textSecondary) Text(preset.name) .font(.system(size: 16, weight: .semibold)) HStack { Text("Block") .font(.system(size: 12)) .foregroundStyle(Theme.textSecondary) Spacer() Image(systemName: "plus.circle.fill") .font(.system(size: 22)) .foregroundStyle(.white.opacity(0.85)) } } .padding(14) .frame(height: 190) .background( LinearGradient( colors: [preset.gradientTop, preset.gradientBottom], startPoint: .top, endPoint: .bottom ), in: RoundedRectangle(cornerRadius: 20) ) .overlay( RoundedRectangle(cornerRadius: 20) .strokeBorder(.white.opacity(0.08), lineWidth: 1) ) } .buttonStyle(.plain) .accessibilityIdentifier("preset-\(preset.id)") } }