FavoriteFoodListRow.swift 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. //
  2. // FavoriteFoodListRow.swift
  3. // LoopKitUI
  4. //
  5. // Created by Noah Brauner on 8/9/23.
  6. // Copyright © 2023 LoopKit Authors. All rights reserved.
  7. //
  8. import SwiftUI
  9. import LoopKit
  10. import HealthKit
  11. public struct FavoriteFoodListRow: View {
  12. @Environment(\.editMode) var editMode
  13. private let cornerRadius: CGFloat = 10
  14. let food: StoredFavoriteFood
  15. @Binding var foodToConfirmDeleteId: String?
  16. let onTap: (StoredFavoriteFood) -> ()
  17. let onDelete: (StoredFavoriteFood) -> ()
  18. let carbFormatter: QuantityFormatter
  19. let absorptionTimeFormatter: DateComponentsFormatter
  20. let preferredCarbUnit: HKUnit
  21. public init(food: StoredFavoriteFood, foodToConfirmDeleteId: Binding<String?>, onFoodTap: @escaping (StoredFavoriteFood) -> Void, onFoodDelete: @escaping (StoredFavoriteFood) -> Void, carbFormatter: QuantityFormatter, absorptionTimeFormatter: DateComponentsFormatter, preferredCarbUnit: HKUnit = .gram()) {
  22. self.food = food
  23. self._foodToConfirmDeleteId = foodToConfirmDeleteId
  24. self.onTap = onFoodTap
  25. self.onDelete = onFoodDelete
  26. self.carbFormatter = carbFormatter
  27. self.absorptionTimeFormatter = absorptionTimeFormatter
  28. self.preferredCarbUnit = preferredCarbUnit
  29. }
  30. public var body: some View {
  31. let isEditing = editMode?.wrappedValue == .active
  32. let isConfirmingDelete = foodToConfirmDeleteId == food.id
  33. HStack(spacing: 0) {
  34. if isEditing {
  35. deleteButton
  36. .onTapGesture {
  37. if isConfirmingDelete {
  38. onDelete(food)
  39. }
  40. else {
  41. withAnimation(.easeInOut(duration: 0.3)) {
  42. foodToConfirmDeleteId = food.id
  43. }
  44. }
  45. }
  46. }
  47. HStack {
  48. foodCardContent
  49. .frame(maxWidth: .infinity, alignment: .leading)
  50. if isEditing {
  51. editBars
  52. }
  53. else {
  54. disclosure
  55. }
  56. }
  57. .padding(.horizontal)
  58. .padding(.vertical, 8)
  59. .contentShape(Rectangle())
  60. .onTapGesture {
  61. onTap(food)
  62. }
  63. }
  64. }
  65. }
  66. extension FavoriteFoodListRow {
  67. private var foodCardContent: some View {
  68. VStack(alignment: .leading, spacing: 6) {
  69. Text(food.title)
  70. Text("\(food.carbsString(formatter: carbFormatter)) carbs, \(food.absorptionTimeString(formatter: absorptionTimeFormatter)) absorption")
  71. .font(.footnote)
  72. }
  73. .foregroundColor(.primary)
  74. }
  75. private var deleteButton: some View {
  76. let isEditing = editMode?.wrappedValue == .active
  77. let isConfirmingDelete = foodToConfirmDeleteId == food.id
  78. return ZStack {
  79. Color.red
  80. .clipShape(RoundedRectangle(cornerRadius: isConfirmingDelete ? 0 : 12.5))
  81. .frame(width: isConfirmingDelete ? nil : 25, height: isConfirmingDelete ? nil : 25)
  82. if isConfirmingDelete {
  83. Text("Delete")
  84. .foregroundColor(.white)
  85. }
  86. else {
  87. Image(systemName: "minus")
  88. .foregroundColor(.white)
  89. }
  90. }
  91. .frame(width: isEditing ? isConfirmingDelete ? 72 : 45 : 0, alignment: .trailing)
  92. .contentShape(Rectangle())
  93. }
  94. private var disclosure: some View {
  95. Image(systemName: "chevron.forward")
  96. .font(.footnote.weight(.semibold))
  97. .foregroundColor(Color(UIColor.tertiaryLabel))
  98. }
  99. private var editBars: some View {
  100. Image(systemName: "line.3.horizontal")
  101. .foregroundColor(Color(UIColor.tertiaryLabel))
  102. .font(.title2)
  103. }
  104. }