Просмотр исходного кода

Limit out-of-bounds scroll behavior for bolus popover WIP

Deniz Cengiz 1 год назад
Родитель
Сommit
1e290aaab9

+ 70 - 6
Trio/Sources/Modules/Stat/View/ViewElements/Insulin/BolusStatsView.swift

@@ -24,6 +24,8 @@ struct BolusStatsView: View {
     /// Timer to throttle updates when scrolling.
     @State private var updateTimer = Stat.UpdateTimer()
 
+    @State private var chartWidth: CGFloat = 0
+
     /// Computes the visible date range based on the current scroll position.
     private var visibleDateRange: (start: Date, end: Date) {
         StatChartUtils.visibleDateRange(from: scrollPosition, for: selectedInterval)
@@ -108,6 +110,13 @@ struct BolusStatsView: View {
                     .padding(.bottom, 4)
 
                 chartsView
+                    .background(
+                        GeometryReader { geo in
+                            Color.clear
+                                .onAppear { chartWidth = geo.size.width }
+                                .onChange(of: geo.size.width) { _, newValue in chartWidth = newValue }
+                        }
+                    )
             }
         }
         .onAppear {
@@ -191,12 +200,24 @@ struct BolusStatsView: View {
                     x: .value("Selected Date", selectedDate)
                 )
                 .foregroundStyle(.secondary.opacity(0.5))
+//                .annotation(
+//                    position: .top,
+//                    spacing: 0,
+//                    overflowResolution: .init(x: .fit(to: .chart), y: .fit(to: .chart))
+//                ) { context in
                 .annotation(
-                    position: .automatic,
+                    position: .overlay,
+                    alignment: .top,
                     spacing: 0,
                     overflowResolution: .init(x: .fit(to: .chart), y: .fit(to: .chart))
-                ) {
-                    BolusSelectionPopover(date: selectedDate, bolus: selectedBolus, selectedInterval: selectedInterval)
+                ) { _ in
+                    BolusSelectionPopover(
+                        selectedDate: selectedDate,
+                        bolus: selectedBolus,
+                        selectedInterval: selectedInterval,
+                        domain: visibleDateRange,
+                        chartWidth: chartWidth
+                    )
                 }
             }
         }
@@ -285,19 +306,52 @@ struct BolusStatsView: View {
 }
 
 private struct BolusSelectionPopover: View {
-    let date: Date
+    let selectedDate: Date
     let bolus: BolusStats
     let selectedInterval: Stat.StateModel.StatsTimeInterval
+    let domain: (start: Date, end: Date)
+    let chartWidth: CGFloat
+
+    @State private var popoverSize: CGSize = .zero
 
     private var timeText: String {
         if selectedInterval == .day {
-            let hour = Calendar.current.component(.hour, from: date)
+            let hour = Calendar.current.component(.hour, from: selectedDate)
             return "\(hour):00-\(hour + 1):00"
         } else {
-            return date.formatted(.dateTime.month().day())
+            return selectedDate.formatted(.dateTime.month().day())
         }
     }
 
+    private func xOffset() -> CGFloat {
+        let domainDuration = domain.end.timeIntervalSince(domain.start)
+        guard domainDuration > 0, chartWidth > 0 else { return 0 }
+
+        let popoverWidth = popoverSize.width
+
+        // Convert dates to pixel'd x-condition
+        let dateFraction = selectedDate.timeIntervalSince(domain.start) / domainDuration
+        let x_selected = dateFraction * chartWidth
+
+        // TODO: this is semi hacky, can this be improved?
+        let x_left = x_selected - (popoverWidth / 2) // Left edge of popover
+        let x_right = x_selected + (popoverWidth / 2) // Right edge of popover
+
+        var offset: CGFloat = 0 // Default = no shift
+
+        // Push popover to right if its left edge is (nearing) out-of-bounds
+        if x_left < 0 {
+            offset = abs(x_left) // push to right
+        }
+
+        // Push popover to left if its right edge is (nearing) out-of-bounds)
+        if x_right > chartWidth {
+            offset = -(x_right - chartWidth) // push to left
+        }
+
+        return offset
+    }
+
     var body: some View {
         VStack(alignment: .leading, spacing: 4) {
             Text(timeText)
@@ -340,5 +394,15 @@ private struct BolusSelectionPopover: View {
             RoundedRectangle(cornerRadius: 10)
                 .fill(Color.insulin)
         )
+        .frame(minWidth: 180, maxWidth: .infinity) // Ensures proper width
+        .background(
+            GeometryReader { geo in
+                Color.clear
+                    .onAppear { popoverSize = geo.size }
+                    .onChange(of: geo.size) { _, newValue in popoverSize = newValue }
+            }
+        )
+        // Apply calculated xOffset to keep within bounds
+        .offset(x: xOffset(), y: 0)
     }
 }