|
|
@@ -115,6 +115,15 @@ struct BolusStatsView: View {
|
|
|
return "\(start.formatted()) - \(end.formatted())"
|
|
|
}
|
|
|
|
|
|
+ private func isSameTimeUnit(_ date1: Date, _ date2: Date) -> Bool {
|
|
|
+ switch selectedDuration {
|
|
|
+ case .Day:
|
|
|
+ return Calendar.current.isDate(date1, equalTo: date2, toGranularity: .hour)
|
|
|
+ default:
|
|
|
+ return Calendar.current.isDate(date1, inSameDayAs: date2)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/// Returns the initial scroll position date based on the selected duration
|
|
|
private func getInitialScrollPosition() -> Date {
|
|
|
let calendar = Calendar.current
|
|
|
@@ -204,33 +213,49 @@ struct BolusStatsView: View {
|
|
|
}
|
|
|
|
|
|
private var chartsView: some View {
|
|
|
- Chart(bolusStats) { stat in
|
|
|
- // Total Bolus Bar
|
|
|
- BarMark(
|
|
|
- x: .value("Date", stat.date, unit: selectedDuration == .Day ? .hour : .day),
|
|
|
- y: .value("Amount", stat.manualBolus)
|
|
|
- )
|
|
|
- .foregroundStyle(by: .value("Type", "Manual"))
|
|
|
- .position(by: .value("Type", "Boluses"))
|
|
|
-
|
|
|
- // Carb Bolus Bar
|
|
|
- BarMark(
|
|
|
- x: .value("Date", stat.date, unit: selectedDuration == .Day ? .hour : .day),
|
|
|
- y: .value("Amount", stat.smb)
|
|
|
- )
|
|
|
- .foregroundStyle(by: .value("Type", "SMB"))
|
|
|
- .position(by: .value("Type", "Boluses"))
|
|
|
+ Chart {
|
|
|
+ ForEach(bolusStats) { stat in
|
|
|
+ // Total Bolus Bar
|
|
|
+ BarMark(
|
|
|
+ x: .value("Date", stat.date, unit: selectedDuration == .Day ? .hour : .day),
|
|
|
+ y: .value("Amount", stat.manualBolus)
|
|
|
+ )
|
|
|
+ .foregroundStyle(by: .value("Type", "Manual"))
|
|
|
+ .position(by: .value("Type", "Boluses"))
|
|
|
+ .opacity(
|
|
|
+ selectedDate.map { date in
|
|
|
+ isSameTimeUnit(stat.date, date) ? 1 : 0.3
|
|
|
+ } ?? 1
|
|
|
+ )
|
|
|
|
|
|
- // Correction Bolus Bar
|
|
|
- BarMark(
|
|
|
- x: .value("Date", stat.date, unit: selectedDuration == .Day ? .hour : .day),
|
|
|
- y: .value("Amount", stat.external)
|
|
|
- )
|
|
|
- .foregroundStyle(by: .value("Type", "External"))
|
|
|
- .position(by: .value("Type", "Boluses"))
|
|
|
+ // Carb Bolus Bar
|
|
|
+ BarMark(
|
|
|
+ x: .value("Date", stat.date, unit: selectedDuration == .Day ? .hour : .day),
|
|
|
+ y: .value("Amount", stat.smb)
|
|
|
+ )
|
|
|
+ .foregroundStyle(by: .value("Type", "SMB"))
|
|
|
+ .position(by: .value("Type", "Boluses"))
|
|
|
+ .opacity(
|
|
|
+ selectedDate.map { date in
|
|
|
+ isSameTimeUnit(stat.date, date) ? 1 : 0.3
|
|
|
+ } ?? 1
|
|
|
+ )
|
|
|
+ // Correction Bolus Bar
|
|
|
+ BarMark(
|
|
|
+ x: .value("Date", stat.date, unit: selectedDuration == .Day ? .hour : .day),
|
|
|
+ y: .value("Amount", stat.external)
|
|
|
+ )
|
|
|
+ .foregroundStyle(by: .value("Type", "External"))
|
|
|
+ .position(by: .value("Type", "Boluses"))
|
|
|
+ .opacity(
|
|
|
+ selectedDate.map { date in
|
|
|
+ isSameTimeUnit(stat.date, date) ? 1 : 0.3
|
|
|
+ } ?? 1
|
|
|
+ )
|
|
|
+ }
|
|
|
|
|
|
- if let selectedDate,
|
|
|
- let selectedBolus = getBolusForDate(selectedDate)
|
|
|
+ // Selection popover outside of the ForEach loop!
|
|
|
+ if let selectedDate, let selectedBolus = getBolusForDate(selectedDate)
|
|
|
{
|
|
|
RuleMark(
|
|
|
x: .value("Selected Date", selectedDate)
|
|
|
@@ -239,7 +264,7 @@ struct BolusStatsView: View {
|
|
|
.annotation(
|
|
|
position: .top,
|
|
|
spacing: 0,
|
|
|
- overflowResolution: .init(x: .fit, y: .disabled)
|
|
|
+ overflowResolution: .init(x: .fit(to: .chart), y: .fit(to: .chart))
|
|
|
) {
|
|
|
BolusSelectionPopover(date: selectedDate, bolus: selectedBolus)
|
|
|
}
|
|
|
@@ -291,8 +316,8 @@ struct BolusStatsView: View {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- .chartXSelection(value: $selectedDate)
|
|
|
.chartScrollableAxes(.horizontal)
|
|
|
+ .chartXSelection(value: $selectedDate.animation(.easeInOut))
|
|
|
.chartScrollPosition(x: $scrollPosition)
|
|
|
.chartScrollTargetBehavior(
|
|
|
.valueAligned(
|
|
|
@@ -305,7 +330,7 @@ struct BolusStatsView: View {
|
|
|
)
|
|
|
)
|
|
|
.chartXVisibleDomain(length: visibleDomainLength)
|
|
|
- .frame(height: 200)
|
|
|
+ .frame(height: 250)
|
|
|
}
|
|
|
}
|
|
|
|