redesign: replace full calendar grid with compact date badge on splash
88pt badge: accent month strip on top, large day number below. Wordmark collapsed to single line — kisaniCAL. with orange dot.
This commit is contained in:
@@ -6,173 +6,94 @@ struct SplashView: View {
|
|||||||
@Environment(\.colorScheme) private var cs
|
@Environment(\.colorScheme) private var cs
|
||||||
var onDismiss: () -> Void
|
var onDismiss: () -> Void
|
||||||
|
|
||||||
@State private var calendarScale: CGFloat = 0.88
|
@State private var iconScale: CGFloat = 0.82
|
||||||
@State private var calendarOpacity: Double = 0
|
@State private var iconOpacity: Double = 0
|
||||||
@State private var wordmarkOpacity: Double = 0
|
@State private var markOpacity: Double = 0
|
||||||
@State private var splashOpacity: Double = 1
|
@State private var splashOpacity: Double = 1
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
AppColors.background(cs).ignoresSafeArea()
|
AppColors.background(cs).ignoresSafeArea()
|
||||||
|
|
||||||
VStack(spacing: 22) {
|
VStack(spacing: 18) {
|
||||||
SplashCalendarCard()
|
CalendarBadgeIcon()
|
||||||
.scaleEffect(calendarScale)
|
.scaleEffect(iconScale)
|
||||||
.opacity(calendarOpacity)
|
.opacity(iconOpacity)
|
||||||
|
|
||||||
VStack(spacing: 1) {
|
// kisaniCAL. — single line, orange dot
|
||||||
|
HStack(spacing: 0) {
|
||||||
Text("kisani")
|
Text("kisani")
|
||||||
.font(AppFonts.mono(11, weight: .regular))
|
.font(AppFonts.mono(22, weight: .regular))
|
||||||
.foregroundColor(AppColors.text2(cs))
|
.foregroundColor(AppColors.text(cs))
|
||||||
.kerning(4)
|
|
||||||
|
|
||||||
HStack(alignment: .firstTextBaseline, spacing: 0) {
|
|
||||||
Text("CAL")
|
Text("CAL")
|
||||||
.font(AppFonts.mono(38, weight: .bold))
|
.font(AppFonts.mono(22, weight: .bold))
|
||||||
.foregroundColor(AppColors.text(cs))
|
.foregroundColor(AppColors.text(cs))
|
||||||
Text(".")
|
Text(".")
|
||||||
.font(AppFonts.mono(38, weight: .bold))
|
.font(AppFonts.mono(22, weight: .bold))
|
||||||
.foregroundColor(AppColors.accent)
|
.foregroundColor(AppColors.accent)
|
||||||
}
|
}
|
||||||
}
|
.opacity(markOpacity)
|
||||||
.opacity(wordmarkOpacity)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.opacity(splashOpacity)
|
.opacity(splashOpacity)
|
||||||
.onAppear {
|
.onAppear {
|
||||||
withAnimation(.spring(response: 0.55, dampingFraction: 0.82).delay(0.05)) {
|
withAnimation(.spring(response: 0.5, dampingFraction: 0.76).delay(0.08)) {
|
||||||
calendarScale = 1
|
iconScale = 1
|
||||||
calendarOpacity = 1
|
iconOpacity = 1
|
||||||
}
|
}
|
||||||
withAnimation(.easeOut(duration: 0.35).delay(0.28)) {
|
withAnimation(.easeOut(duration: 0.3).delay(0.3)) {
|
||||||
wordmarkOpacity = 1
|
markOpacity = 1
|
||||||
}
|
}
|
||||||
withAnimation(.easeIn(duration: 0.32).delay(1.65)) {
|
withAnimation(.easeIn(duration: 0.28).delay(1.6)) {
|
||||||
splashOpacity = 0
|
splashOpacity = 0
|
||||||
}
|
}
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1.95) {
|
||||||
onDismiss()
|
onDismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Calendar Card
|
// MARK: - Calendar Badge Icon
|
||||||
|
|
||||||
private struct SplashCalendarCard: View {
|
private struct CalendarBadgeIcon: View {
|
||||||
@Environment(\.colorScheme) private var cs
|
@Environment(\.colorScheme) private var cs
|
||||||
|
|
||||||
private static let cal = Calendar.current
|
private static let cal = Calendar.current
|
||||||
private static let today = cal.component(.day, from: Date())
|
private static var day: Int { cal.component(.day, from: Date()) }
|
||||||
private static let monthFmt: DateFormatter = {
|
private static var month: String {
|
||||||
let f = DateFormatter(); f.dateFormat = "MMMM"; return f
|
let f = DateFormatter(); f.dateFormat = "MMM"
|
||||||
}()
|
return f.string(from: Date()).uppercased()
|
||||||
|
|
||||||
private static var days: [Int?] {
|
|
||||||
let now = Date()
|
|
||||||
let count = cal.range(of: .day, in: .month, for: now)!.count
|
|
||||||
let first = cal.date(from: cal.dateComponents([.year, .month], from: now))!
|
|
||||||
let lead = cal.component(.weekday, from: first) - 1
|
|
||||||
var grid: [Int?] = Array(repeating: nil, count: lead)
|
|
||||||
grid += (1...count).map { Optional($0) }
|
|
||||||
while grid.count % 7 != 0 { grid.append(nil) }
|
|
||||||
return grid
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
monthHeader
|
// Month strip
|
||||||
Spacer().frame(height: 10)
|
Text(Self.month)
|
||||||
weekdayRow
|
.font(AppFonts.mono(11, weight: .bold))
|
||||||
Spacer().frame(height: 6)
|
.foregroundColor(.white)
|
||||||
dayGrid
|
.kerning(1.5)
|
||||||
}
|
.frame(maxWidth: .infinity)
|
||||||
.padding(.horizontal, 18)
|
.frame(height: 28)
|
||||||
.padding(.vertical, 16)
|
.background(AppColors.accent)
|
||||||
.frame(width: 244)
|
|
||||||
|
// Day number
|
||||||
|
Text("\(Self.day)")
|
||||||
|
.font(AppFonts.mono(42, weight: .bold))
|
||||||
|
.foregroundColor(AppColors.text(cs))
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
.frame(height: 56)
|
||||||
.background(AppColors.surface(cs))
|
.background(AppColors.surface(cs))
|
||||||
.clipShape(RoundedRectangle(cornerRadius: 20))
|
}
|
||||||
|
.frame(width: 88)
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: 18))
|
||||||
.overlay(
|
.overlay(
|
||||||
RoundedRectangle(cornerRadius: 20)
|
RoundedRectangle(cornerRadius: 18)
|
||||||
.stroke(AppColors.border(cs), lineWidth: 1)
|
.stroke(AppColors.border(cs), lineWidth: 1)
|
||||||
)
|
)
|
||||||
.shadow(
|
.shadow(
|
||||||
color: Color.black.opacity(cs == .dark ? 0.3 : 0.06),
|
color: Color.black.opacity(cs == .dark ? 0.28 : 0.08),
|
||||||
radius: 24, y: 8
|
radius: 20, y: 6
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private var monthHeader: some View {
|
|
||||||
Text(Self.monthFmt.string(from: Date()).uppercased())
|
|
||||||
.font(AppFonts.mono(9, weight: .bold))
|
|
||||||
.foregroundColor(AppColors.text3(cs))
|
|
||||||
.kerning(2)
|
|
||||||
}
|
|
||||||
|
|
||||||
private var weekdayRow: some View {
|
|
||||||
let letters = ["S","M","T","W","T","F","S"]
|
|
||||||
return HStack(spacing: 0) {
|
|
||||||
ForEach(0..<7, id: \.self) { i in
|
|
||||||
Text(letters[i])
|
|
||||||
.font(AppFonts.mono(7.5, weight: .bold))
|
|
||||||
.foregroundColor(AppColors.text3(cs))
|
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var dayGrid: some View {
|
|
||||||
let rows = Self.days.count / 7
|
|
||||||
return VStack(spacing: 0) {
|
|
||||||
ForEach(0..<rows, id: \.self) { row in
|
|
||||||
SplashWeekRow(
|
|
||||||
days: Array(Self.days[(row * 7)..<(row * 7 + 7)]),
|
|
||||||
today: Self.today,
|
|
||||||
cs: cs
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Week Row
|
|
||||||
|
|
||||||
private struct SplashWeekRow: View {
|
|
||||||
let days: [Int?]
|
|
||||||
let today: Int
|
|
||||||
let cs: ColorScheme
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
HStack(spacing: 0) {
|
|
||||||
ForEach(0..<7, id: \.self) { col in
|
|
||||||
SplashDayCell(day: days[col], isToday: days[col] == today, cs: cs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Day Cell
|
|
||||||
|
|
||||||
private struct SplashDayCell: View {
|
|
||||||
let day: Int?
|
|
||||||
let isToday: Bool
|
|
||||||
let cs: ColorScheme
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
ZStack {
|
|
||||||
if isToday {
|
|
||||||
Circle()
|
|
||||||
.fill(AppColors.accent)
|
|
||||||
.frame(width: 26, height: 26)
|
|
||||||
}
|
|
||||||
if let d = day {
|
|
||||||
Text("\(d)")
|
|
||||||
.font(AppFonts.mono(isToday ? 10 : 9.5,
|
|
||||||
weight: isToday ? .bold : .regular))
|
|
||||||
.foregroundColor(isToday ? .white : AppColors.text(cs).opacity(0.72))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.frame(maxWidth: .infinity)
|
|
||||||
.frame(height: 28)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user