Add Orbit style for voting layout and entry views
- Add orbit case to VotingLayoutStyle enum with celestial design - Create OrbitVotingView with center core and orbiting mood planets - Add orbit case to DayViewStyle enum for entry list - Create OrbitEntryView with mood icon center and orbiting day number - Add orbit icons to voting and entry style pickers in CustomizeView 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -66,6 +66,8 @@ struct EntryListView: View {
|
||||
motionStyle
|
||||
case .micro:
|
||||
microStyle
|
||||
case .orbit:
|
||||
orbitStyle
|
||||
}
|
||||
}
|
||||
.accessibilityElement(children: .combine)
|
||||
@@ -1767,6 +1769,163 @@ struct EntryListView: View {
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - Orbit Style (Celestial Circular)
|
||||
private var orbitStyle: some View {
|
||||
OrbitEntryView(
|
||||
entry: entry,
|
||||
imagePack: imagePack,
|
||||
moodColor: moodColor,
|
||||
textColor: textColor,
|
||||
colorScheme: colorScheme,
|
||||
isMissing: isMissing
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Orbit Entry View
|
||||
struct OrbitEntryView: View {
|
||||
let entry: MoodEntryModel
|
||||
let imagePack: MoodImages
|
||||
let moodColor: Color
|
||||
let textColor: Color
|
||||
let colorScheme: ColorScheme
|
||||
let isMissing: Bool
|
||||
|
||||
var body: some View {
|
||||
HStack(spacing: 0) {
|
||||
// Orbital system on left
|
||||
orbitalSystem
|
||||
.frame(width: 100, height: 100)
|
||||
|
||||
// Content on right
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
moodDisplay
|
||||
dateDisplay
|
||||
}
|
||||
.padding(.leading, 8)
|
||||
|
||||
Spacer()
|
||||
|
||||
chevron
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 12)
|
||||
.background(backgroundLayer)
|
||||
.overlay(borderLayer)
|
||||
}
|
||||
|
||||
private var orbitalSystem: some View {
|
||||
ZStack {
|
||||
// Orbital ring
|
||||
Circle()
|
||||
.stroke(
|
||||
(colorScheme == .dark ? Color.white : Color.black).opacity(0.1),
|
||||
lineWidth: 1
|
||||
)
|
||||
.frame(width: 80, height: 80)
|
||||
|
||||
// Center core (mood icon)
|
||||
ZStack {
|
||||
// Glow
|
||||
Circle()
|
||||
.fill(isMissing ? Color.gray.opacity(0.2) : moodColor.opacity(0.3))
|
||||
.frame(width: 60, height: 60)
|
||||
.blur(radius: 8)
|
||||
|
||||
// Planet
|
||||
Circle()
|
||||
.fill(isMissing ? Color.gray.opacity(0.4) : moodColor)
|
||||
.frame(width: 44, height: 44)
|
||||
.shadow(color: (isMissing ? Color.gray : moodColor).opacity(0.5), radius: 6, x: 0, y: 2)
|
||||
|
||||
// Icon
|
||||
imagePack.icon(forMood: entry.mood)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 24, height: 24)
|
||||
.foregroundColor(.white)
|
||||
.accessibilityLabel(entry.mood.strValue)
|
||||
}
|
||||
|
||||
// Orbiting day number
|
||||
orbitingDay
|
||||
}
|
||||
}
|
||||
|
||||
private var orbitingDay: some View {
|
||||
let angle = -Double.pi / 4 // Position at top-right
|
||||
let radius: CGFloat = 40
|
||||
|
||||
return ZStack {
|
||||
Circle()
|
||||
.fill(Color.white.opacity(colorScheme == .dark ? 0.9 : 1.0))
|
||||
.frame(width: 28, height: 28)
|
||||
.shadow(color: .black.opacity(0.15), radius: 4)
|
||||
|
||||
Text(entry.forDate, format: .dateTime.day())
|
||||
.font(.caption.weight(.bold))
|
||||
.foregroundColor(.black.opacity(0.7))
|
||||
}
|
||||
.offset(x: cos(angle) * radius, y: sin(angle) * radius)
|
||||
}
|
||||
|
||||
private var dateDisplay: some View {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(entry.forDate, format: .dateTime.weekday(.wide))
|
||||
.font(.body.weight(.semibold))
|
||||
.foregroundColor(textColor)
|
||||
|
||||
Text(entry.forDate, format: .dateTime.month(.abbreviated).year())
|
||||
.font(.caption)
|
||||
.foregroundColor(textColor.opacity(0.5))
|
||||
}
|
||||
}
|
||||
|
||||
private var moodDisplay: some View {
|
||||
Group {
|
||||
if isMissing {
|
||||
Text("No mood recorded")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.gray)
|
||||
} else {
|
||||
Text(entry.moodString)
|
||||
.font(.subheadline.weight(.semibold))
|
||||
.foregroundColor(moodColor)
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 5)
|
||||
.background(
|
||||
Capsule()
|
||||
.fill(moodColor.opacity(0.15))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var chevron: some View {
|
||||
Image(systemName: "chevron.right")
|
||||
.font(.subheadline.weight(.semibold))
|
||||
.foregroundColor(textColor.opacity(0.3))
|
||||
}
|
||||
|
||||
private var backgroundLayer: some View {
|
||||
RoundedRectangle(cornerRadius: 20)
|
||||
.fill(colorScheme == .dark ? Color(.systemGray6) : .white)
|
||||
.shadow(
|
||||
color: isMissing ? Color.black.opacity(0.05) : moodColor.opacity(0.15),
|
||||
radius: 12,
|
||||
x: 0,
|
||||
y: 6
|
||||
)
|
||||
}
|
||||
|
||||
private var borderLayer: some View {
|
||||
RoundedRectangle(cornerRadius: 20)
|
||||
.stroke(
|
||||
isMissing ? Color.gray.opacity(0.15) : moodColor.opacity(0.2),
|
||||
lineWidth: 1
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Motion Card with Accelerometer
|
||||
|
||||
Reference in New Issue
Block a user