import SwiftUI struct GroupListView: View { @Bindable var model: ScanViewModel var body: some View { List(selection: $model.selectedGroupID) { if !model.groups.isEmpty { Section { ForEach(model.groups) { group in GroupRow(group: group, markedCount: markedCount(in: group)) .tag(group.id) } } header: { Text("\(model.groups.count) グループ・重複 \(model.totalDuplicateCount) 枚") } } } .listStyle(.sidebar) .overlay { if model.groups.isEmpty && !model.isScanning { sidebarPlaceholder } } .safeAreaInset(edge: .bottom) { if !model.groups.isEmpty { summaryBar } } } private func markedCount(in group: SimilarityGroup) -> Int { group.members.filter { model.markedForDeletion.contains($0.id) }.count } private var sidebarPlaceholder: some View { VStack(spacing: 6) { if model.folder == nil { Text("フォルダが未選択です") } else if model.hasScanned { Text("似た写真は見つかりませんでした") } else { Text("スキャンしていません") } } .font(.callout) .foregroundStyle(.secondary) .padding() } private var summaryBar: some View { VStack(alignment: .leading, spacing: 2) { Divider() Text("最大 \(model.totalReclaimableText) 削減できます") .font(.callout) if model.markedCount > 0 { Text("\(model.markedCount) 枚を選択中(\(model.markedBytesText))") .font(.caption) .foregroundStyle(.secondary) } } .frame(maxWidth: .infinity, alignment: .leading) .padding(.horizontal, 12) .padding(.bottom, 8) .background(.bar) } } struct GroupRow: View { let group: SimilarityGroup let markedCount: Int var body: some View { HStack(spacing: 10) { ThumbnailView(url: group.representativeID, size: 52) VStack(alignment: .leading, spacing: 3) { Text(group.representative.fileName) .lineLimit(1) .truncationMode(.middle) HStack(spacing: 6) { Text(group.kindText) .font(.caption2) .padding(.horizontal, 5) .padding(.vertical, 1) .background( group.isAllExact ? Color.orange.opacity(0.22) : Color.accentColor.opacity(0.18), in: Capsule() ) Text("\(group.members.count) 枚") .font(.caption) .foregroundStyle(.secondary) } Text("\(group.reclaimableBytesText) 削減可") .font(.caption) .foregroundStyle(.secondary) } Spacer(minLength: 0) if markedCount > 0 { Text("\(markedCount)") .font(.caption.monospacedDigit()) .foregroundStyle(.white) .padding(.horizontal, 6) .padding(.vertical, 2) .background(Color.red, in: Capsule()) } } .padding(.vertical, 3) } }