- Fix LargeVotingView mood icons getting clipped at edges by using flexible HStack spacing with maxWidth: .infinity - Fix VotingView medium layout with smaller icons and even distribution - Add comprehensive #Preview macros for all widget states: - Vote widget: small/medium, voted/not voted, all mood states - Timeline widget: small/medium/large with various data states - Reduce icon sizes and padding to fit within widget bounds - Update accessibility labels and hints across views 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
153 lines
6.2 KiB
Swift
153 lines
6.2 KiB
Swift
//
|
|
// VotingLayoutPickerView.swift
|
|
// Feels (iOS)
|
|
//
|
|
// Created by Claude Code on 12/9/24.
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
struct VotingLayoutPickerView: View {
|
|
@AppStorage(UserDefaultsStore.Keys.theme.rawValue, store: GroupUserDefaults.groupDefaults) private var theme: Theme = .system
|
|
@AppStorage(UserDefaultsStore.Keys.textColor.rawValue, store: GroupUserDefaults.groupDefaults) private var textColor: Color = DefaultTextColor.textColor
|
|
@AppStorage(UserDefaultsStore.Keys.votingLayoutStyle.rawValue, store: GroupUserDefaults.groupDefaults) private var votingLayoutStyle: Int = 0
|
|
|
|
private var currentLayout: VotingLayoutStyle {
|
|
VotingLayoutStyle(rawValue: votingLayoutStyle) ?? .horizontal
|
|
}
|
|
|
|
var body: some View {
|
|
ZStack {
|
|
theme.currentTheme.secondaryBGColor
|
|
|
|
VStack(alignment: .leading, spacing: 12) {
|
|
Text("Voting Layout")
|
|
.font(.headline)
|
|
.foregroundColor(textColor)
|
|
.padding(.horizontal)
|
|
.padding(.top)
|
|
|
|
ScrollView(.horizontal, showsIndicators: false) {
|
|
HStack(spacing: 8) {
|
|
ForEach(VotingLayoutStyle.allCases, id: \.rawValue) { layout in
|
|
Button(action: {
|
|
if UIAccessibility.isReduceMotionEnabled {
|
|
votingLayoutStyle = layout.rawValue
|
|
} else {
|
|
withAnimation(.easeInOut(duration: 0.2)) {
|
|
votingLayoutStyle = layout.rawValue
|
|
}
|
|
}
|
|
EventLogger.log(event: "change_voting_layout", withData: ["layout": layout.displayName])
|
|
}) {
|
|
VStack(spacing: 6) {
|
|
layoutIcon(for: layout)
|
|
.frame(width: 44, height: 44)
|
|
.foregroundColor(currentLayout == layout ? .accentColor : textColor.opacity(0.6))
|
|
|
|
Text(layout.displayName)
|
|
.font(.caption)
|
|
.foregroundColor(currentLayout == layout ? .accentColor : textColor.opacity(0.8))
|
|
}
|
|
.frame(width: 70)
|
|
.padding(.vertical, 12)
|
|
.background(
|
|
RoundedRectangle(cornerRadius: 10)
|
|
.fill(currentLayout == layout ? Color.accentColor.opacity(0.15) : Color.clear)
|
|
)
|
|
.overlay(
|
|
RoundedRectangle(cornerRadius: 10)
|
|
.stroke(currentLayout == layout ? Color.accentColor : Color.clear, lineWidth: 2)
|
|
)
|
|
}
|
|
.buttonStyle(.plain)
|
|
}
|
|
}
|
|
.padding(.horizontal)
|
|
}
|
|
.padding(.bottom)
|
|
}
|
|
}
|
|
.fixedSize(horizontal: false, vertical: true)
|
|
.cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight])
|
|
}
|
|
|
|
@ViewBuilder
|
|
private func layoutIcon(for layout: VotingLayoutStyle) -> some View {
|
|
switch layout {
|
|
case .horizontal:
|
|
HStack(spacing: 4) {
|
|
ForEach(0..<5) { _ in
|
|
Circle()
|
|
.frame(width: 6, height: 6)
|
|
}
|
|
}
|
|
case .cards:
|
|
LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible()), GridItem(.flexible())], spacing: 3) {
|
|
ForEach(0..<6) { _ in
|
|
RoundedRectangle(cornerRadius: 2)
|
|
.frame(width: 10, height: 12)
|
|
}
|
|
}
|
|
case .radial:
|
|
ZStack {
|
|
ForEach(0..<5) { index in
|
|
Circle()
|
|
.frame(width: 6, height: 6)
|
|
.offset(radialOffset(index: index, total: 5, radius: 16))
|
|
}
|
|
}
|
|
case .stacked:
|
|
VStack(spacing: 3) {
|
|
ForEach(0..<4) { _ in
|
|
RoundedRectangle(cornerRadius: 2)
|
|
.frame(width: 32, height: 6)
|
|
}
|
|
}
|
|
case .aura:
|
|
// Glowing orbs in 2 rows
|
|
VStack(spacing: 4) {
|
|
HStack(spacing: 6) {
|
|
ForEach(0..<3, id: \.self) { _ in
|
|
ZStack {
|
|
Circle()
|
|
.fill(RadialGradient(colors: [.green.opacity(0.5), .clear], center: .center, startRadius: 0, endRadius: 8))
|
|
.frame(width: 14, height: 14)
|
|
Circle()
|
|
.fill(.green)
|
|
.frame(width: 8, height: 8)
|
|
}
|
|
}
|
|
}
|
|
HStack(spacing: 10) {
|
|
ForEach(0..<2, id: \.self) { _ in
|
|
ZStack {
|
|
Circle()
|
|
.fill(RadialGradient(colors: [.green.opacity(0.5), .clear], center: .center, startRadius: 0, endRadius: 8))
|
|
.frame(width: 14, height: 14)
|
|
Circle()
|
|
.fill(.green)
|
|
.frame(width: 8, height: 8)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private func radialOffset(index: Int, total: Int, radius: CGFloat) -> CGSize {
|
|
let angle = Double.pi - (Double.pi * Double(index) / Double(total - 1))
|
|
return CGSize(
|
|
width: radius * CGFloat(cos(angle)),
|
|
height: -radius * CGFloat(sin(angle)) + 4
|
|
)
|
|
}
|
|
}
|
|
|
|
struct VotingLayoutPickerView_Previews: PreviewProvider {
|
|
static var previews: some View {
|
|
VotingLayoutPickerView()
|
|
.padding()
|
|
}
|
|
}
|