SidebarView.swift 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. import SwiftUI
  2. struct SidebarView: View {
  3. @Binding var selectedDestination: NavigationDestination
  4. let isPremium: Bool
  5. let onUpgradeTapped: () -> Void
  6. var body: some View {
  7. VStack(alignment: .leading, spacing: 0) {
  8. logoHeader
  9. .padding(.horizontal, 20)
  10. .padding(.top, 24)
  11. .padding(.bottom, 20)
  12. VStack(spacing: 2) {
  13. ForEach(NavigationDestination.allCases) { destination in
  14. SidebarNavItem(
  15. destination: destination,
  16. isSelected: selectedDestination == destination
  17. ) {
  18. selectedDestination = destination
  19. }
  20. }
  21. }
  22. .padding(.horizontal, 12)
  23. Spacer(minLength: 12)
  24. if !isPremium {
  25. PremiumCardView(onUpgradeTapped: onUpgradeTapped)
  26. .padding(.horizontal, 16)
  27. .padding(.bottom, 20)
  28. }
  29. }
  30. .frame(width: AppTheme.sidebarWidth, alignment: .top)
  31. .frame(maxHeight: .infinity)
  32. .background {
  33. AppTheme.sidebarBackground
  34. .ignoresSafeArea(edges: .top)
  35. }
  36. }
  37. private var logoHeader: some View {
  38. HStack(spacing: 10) {
  39. ZStack {
  40. Circle()
  41. .fill(AppTheme.teal)
  42. .frame(width: 36, height: 36)
  43. Text("G")
  44. .font(.system(size: 18, weight: .bold))
  45. .foregroundStyle(.white)
  46. }
  47. Text("Gramora")
  48. .font(.system(size: 18, weight: .bold))
  49. .foregroundStyle(AppTheme.textPrimary)
  50. }
  51. }
  52. }
  53. private struct SidebarNavItem: View {
  54. let destination: NavigationDestination
  55. let isSelected: Bool
  56. let action: () -> Void
  57. @State private var isHovered = false
  58. var body: some View {
  59. Button(action: action) {
  60. HStack(spacing: 10) {
  61. Image(systemName: destination.iconName(isSelected: isSelected))
  62. .font(.system(size: 14))
  63. .frame(width: 20)
  64. Text(destination.title)
  65. .font(.system(size: 13, weight: isSelected ? .semibold : .regular))
  66. .lineLimit(1)
  67. Spacer()
  68. }
  69. .foregroundStyle(foregroundColor)
  70. .padding(.horizontal, 12)
  71. .padding(.vertical, 9)
  72. .background(
  73. RoundedRectangle(cornerRadius: AppTheme.navItemCornerRadius)
  74. .fill(backgroundColor)
  75. )
  76. .scaleEffect(isHovered ? 1.02 : 1.0)
  77. .animation(.easeOut(duration: 0.15), value: isHovered)
  78. }
  79. .buttonStyle(.plain)
  80. .onHover { isHovered = $0 }
  81. }
  82. private var foregroundColor: Color {
  83. if isSelected {
  84. return AppTheme.teal
  85. }
  86. return isHovered ? AppTheme.navForegroundHover : AppTheme.textSecondary
  87. }
  88. private var backgroundColor: Color {
  89. if isSelected {
  90. return isHovered ? AppTheme.navBackgroundHover : AppTheme.tealLight
  91. }
  92. return isHovered ? AppTheme.tealLight : Color.clear
  93. }
  94. }