| 1 |
// |
|
| 2 |
// ChargedDeviceDetailTabBarView.swift |
|
| 3 |
// USB Meter |
|
| 4 |
// |
|
| 5 |
// Created by Codex on 22/04/2026. |
|
| 6 |
// |
|
| 7 | ||
| 8 |
import SwiftUI |
|
| 9 | ||
| 10 |
struct ChargedDeviceDetailTabBarView<Tab: Hashable>: View {
|
|
| 11 |
let tabs: [Tab] |
|
| 12 |
@Binding var selection: Tab |
|
| 13 |
let tint: Color |
|
| 14 |
let presentation: AdaptiveTabBarPresentation |
|
| 15 |
let title: (Tab) -> String |
|
| 16 |
let systemImage: (Tab) -> String |
|
| 17 | ||
| 18 |
var body: some View {
|
|
| 19 |
HStack {
|
|
| 20 |
Spacer(minLength: 0) |
|
| 21 | ||
| 22 |
HStack(spacing: 8) {
|
|
| 23 |
ForEach(tabs, id: \.self) { tab in
|
|
| 24 |
let isSelected = selection == tab |
|
| 25 | ||
| 26 |
Button {
|
|
| 27 |
withAnimation(.easeInOut(duration: 0.2)) {
|
|
| 28 |
selection = tab |
|
| 29 |
} |
|
| 30 |
} label: {
|
|
| 31 |
HStack(spacing: 6) {
|
|
| 32 |
Image(systemName: systemImage(tab)) |
|
| 33 |
.font(.subheadline.weight(.semibold)) |
|
| 34 |
if presentation.showsTitles {
|
|
| 35 |
Text(title(tab)) |
|
| 36 |
.font(.subheadline.weight(.semibold)) |
|
| 37 |
.lineLimit(1) |
|
| 38 |
} |
|
| 39 |
} |
|
| 40 |
.foregroundColor(isSelected ? .white : .primary) |
|
| 41 |
.padding(.horizontal, presentation.showsTitles ? 10 : 12) |
|
| 42 |
.padding(.vertical, presentation.showsTitles ? 7 : 10) |
|
| 43 |
.frame(maxWidth: .infinity) |
|
| 44 |
.background( |
|
| 45 |
Capsule() |
|
| 46 |
.fill(isSelected ? tint : Color.secondary.opacity(0.12)) |
|
| 47 |
) |
|
| 48 |
} |
|
| 49 |
.buttonStyle(.plain) |
|
| 50 |
.accessibilityLabel(title(tab)) |
|
| 51 |
} |
|
| 52 |
} |
|
| 53 |
.frame(maxWidth: presentation.maxWidth) |
|
| 54 |
.padding(6) |
|
| 55 |
.background( |
|
| 56 |
RoundedRectangle(cornerRadius: presentation.showsTitles ? 14 : 22, style: .continuous) |
|
| 57 |
.fill(Color.secondary.opacity(0.10)) |
|
| 58 |
) |
|
| 59 |
.background {
|
|
| 60 |
RoundedRectangle(cornerRadius: presentation.showsTitles ? 14 : 22, style: .continuous) |
|
| 61 |
.fill(.ultraThinMaterial) |
|
| 62 |
.opacity(0.78) |
|
| 63 |
} |
|
| 64 | ||
| 65 |
Spacer(minLength: 0) |
|
| 66 |
} |
|
| 67 |
.padding(.horizontal, 16) |
|
| 68 |
.padding(.top, 10) |
|
| 69 |
.padding(.bottom, 8) |
|
| 70 |
.background {
|
|
| 71 |
Rectangle() |
|
| 72 |
.fill(.ultraThinMaterial) |
|
| 73 |
.opacity(0.78) |
|
| 74 |
.ignoresSafeArea(edges: .top) |
|
| 75 |
} |
|
| 76 |
.overlay(alignment: .bottom) {
|
|
| 77 |
Rectangle() |
|
| 78 |
.fill(Color.secondary.opacity(0.12)) |
|
| 79 |
.frame(height: 1) |
|
| 80 |
} |
|
| 81 |
} |
|
| 82 |
} |