Initial Commit
This commit is contained in:
118
Calendarr iOS/Views/Calendar/QuarterView.swift
Normal file
118
Calendarr iOS/Views/Calendar/QuarterView.swift
Normal file
@@ -0,0 +1,118 @@
|
||||
import SwiftUI
|
||||
|
||||
struct QuarterView: View {
|
||||
let store: CalendarStore
|
||||
let onEventTap: (CalEvent) -> Void
|
||||
|
||||
private var cal: Calendar { store.userCalendar }
|
||||
|
||||
private var months: [Date] {
|
||||
let start = cal.date(from: cal.dateComponents([.year, .month], from: store.currentDate))!
|
||||
return (0..<3).compactMap { cal.date(byAdding: .month, value: $0, to: start) }
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
VStack(spacing: 0) {
|
||||
ForEach(months, id: \.self) { month in
|
||||
MiniMonthBlock(month: month, store: store, onEventTap: onEventTap)
|
||||
Divider()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct MiniMonthBlock: View {
|
||||
let month: Date
|
||||
let store: CalendarStore
|
||||
let onEventTap: (CalEvent) -> Void
|
||||
|
||||
private var cal: Calendar { store.userCalendar }
|
||||
|
||||
private let monthFmt: DateFormatter = {
|
||||
let f = DateFormatter(); f.dateFormat = "MMMM yyyy"; return f
|
||||
}()
|
||||
|
||||
private var gridDays: [Date] {
|
||||
let firstWeekday = cal.firstWeekday
|
||||
let weekday = cal.component(.weekday, from: month)
|
||||
let offset = ((weekday - firstWeekday) + 7) % 7
|
||||
let gridStart = cal.date(byAdding: .day, value: -offset, to: month)!
|
||||
let rows = 6
|
||||
return (0..<(rows * 7)).compactMap { cal.date(byAdding: .day, value: $0, to: gridStart) }
|
||||
}
|
||||
|
||||
private var weekdayHeaders: [String] {
|
||||
let symbols = cal.veryShortWeekdaySymbols
|
||||
let start = cal.firstWeekday - 1
|
||||
return (0..<7).map { symbols[(start + $0) % 7] }
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(monthFmt.string(from: month))
|
||||
.font(.headline.weight(.semibold))
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.top, 12)
|
||||
|
||||
HStack(spacing: 0) {
|
||||
ForEach(weekdayHeaders, id: \.self) { d in
|
||||
Text(d)
|
||||
.font(.system(size: 10, weight: .medium))
|
||||
.foregroundStyle(.secondary)
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 8)
|
||||
|
||||
LazyVGrid(columns: Array(repeating: GridItem(.flexible(), spacing: 0), count: 7), spacing: 2) {
|
||||
ForEach(gridDays, id: \.self) { day in
|
||||
MiniDayCell(
|
||||
date: day,
|
||||
isCurrentMonth: cal.isDate(day, equalTo: month, toGranularity: .month),
|
||||
isToday: cal.isDateInToday(day),
|
||||
events: store.events(on: day),
|
||||
onEventTap: onEventTap
|
||||
)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 8)
|
||||
.padding(.bottom, 12)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct MiniDayCell: View {
|
||||
let date: Date
|
||||
let isCurrentMonth: Bool
|
||||
let isToday: Bool
|
||||
let events: [CalEvent]
|
||||
let onEventTap: (CalEvent) -> Void
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 1) {
|
||||
Text("\(Calendar.current.component(.day, from: date))")
|
||||
.font(.system(size: 12, weight: isToday ? .bold : .regular))
|
||||
.foregroundStyle(
|
||||
isToday ? Color.white :
|
||||
isCurrentMonth ? Color.primary : Color.secondary.opacity(0.3)
|
||||
)
|
||||
.frame(width: 22, height: 22)
|
||||
.background(isToday ? Color.accentColor : Color.clear)
|
||||
.clipShape(Circle())
|
||||
|
||||
// Up to 3 event dots
|
||||
HStack(spacing: 2) {
|
||||
ForEach(events.prefix(3)) { ev in
|
||||
Circle()
|
||||
.fill(Color(hex: ev.effectiveColor))
|
||||
.frame(width: 4, height: 4)
|
||||
.onTapGesture { onEventTap(ev) }
|
||||
}
|
||||
}
|
||||
.frame(height: 6)
|
||||
}
|
||||
.frame(minHeight: 36)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user