AddAlarmSheet.swift 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. // LoopFollow
  2. // AddAlarmSheet.swift
  3. import SwiftUI
  4. struct AddAlarmSheet: View {
  5. let onSelect: (AlarmType) -> Void
  6. @Environment(\.dismiss) private var dismiss
  7. @State private var searchText = ""
  8. private let columns = [
  9. GridItem(.adaptive(minimum: 110), spacing: 16),
  10. ]
  11. private func matches(_ type: AlarmType) -> Bool {
  12. let query = searchText.trimmingCharacters(in: .whitespacesAndNewlines)
  13. guard !query.isEmpty else { return true }
  14. return type.rawValue.localizedCaseInsensitiveContains(query)
  15. || type.blurb.localizedCaseInsensitiveContains(query)
  16. || type.group.rawValue.localizedCaseInsensitiveContains(query)
  17. }
  18. var body: some View {
  19. NavigationStack {
  20. ScrollView {
  21. LazyVGrid(columns: columns, spacing: 16) {
  22. ForEach(AlarmType.Group.allCases, id: \.self) { group in
  23. let types = AlarmType.allCases.filter { $0.group == group && matches($0) }
  24. if !types.isEmpty {
  25. Section(header: Text(group.rawValue)
  26. .font(.headline)
  27. .frame(maxWidth: .infinity, alignment: .leading)
  28. .padding(.horizontal, 4)
  29. ) {
  30. ForEach(types, id: \.self) { type in
  31. AlarmTile(type: type) {
  32. onSelect(type)
  33. }
  34. }
  35. }
  36. }
  37. }
  38. }
  39. .padding()
  40. if !AlarmType.allCases.contains(where: matches) {
  41. VStack(spacing: 8) {
  42. Image(systemName: "magnifyingglass")
  43. .font(.largeTitle)
  44. .foregroundColor(.secondary)
  45. Text("No Results")
  46. .font(.headline)
  47. Text("No alarms match “\(searchText)”.")
  48. .font(.subheadline)
  49. .foregroundColor(.secondary)
  50. .multilineTextAlignment(.center)
  51. }
  52. .padding(.top, 40)
  53. .padding(.horizontal)
  54. }
  55. }
  56. .navigationTitle("Add Alarm")
  57. .searchable(text: $searchText, prompt: "Search alarms")
  58. .toolbar {
  59. ToolbarItem(placement: .cancellationAction) {
  60. Button("Cancel") { dismiss() }
  61. }
  62. }
  63. }
  64. .preferredColorScheme(Storage.shared.appearanceMode.value.colorScheme)
  65. }
  66. }