Internal tooling for Mac utility for storage management.

Swift 98.7% JSON 1.1% Markdown 0.2%
DetailPanelView.swift 217 lines (7 KB)
//
//  DetailPanelView.swift
//  MUA
//
//  Created by Mitchel Volkering on 21/12/2025.
//

import SwiftUI

/// Shows detailed info about the selected item
struct DetailPanelView: View {
    let item: FileItem?

    var body: some View {
        Group {
            if let item = item {
                ScrollView {
                    VStack(alignment: .leading, spacing: 20) {
                        // Header with icon and name
                        headerSection(item)

                        Divider()

                        // Size information
                        sizeSection(item)

                        if item.isDirectory && !item.children.isEmpty {
                            Divider()
                            childrenSection(item)
                        }

                        Divider()

                        // Path info
                        pathSection(item)

                        Spacer()
                    }
                    .padding()
                }
            } else {
                emptyState
            }
        }
        .frame(minWidth: 260, idealWidth: 280, maxWidth: 320)
        .background(.regularMaterial)
    }

    private func headerSection(_ item: FileItem) -> some View {
        HStack(spacing: 16) {
            // Large icon with glass effect
            ZStack {
                Circle()
                    .fill(
                        .linearGradient(
                            colors: [
                                item.color.opacity(0.6),
                                item.color.opacity(0.3)
                            ],
                            startPoint: .topLeading,
                            endPoint: .bottomTrailing
                        )
                    )
                    .frame(width: 64, height: 64)
                    .overlay(
                        Circle()
                            .fill(
                                .linearGradient(
                                    colors: [.white.opacity(0.3), .clear],
                                    startPoint: .topLeading,
                                    endPoint: .center
                                )
                            )
                    )
                    .overlay(
                        Circle()
                            .strokeBorder(.white.opacity(0.3), lineWidth: 1)
                    )
                    .shadow(color: item.color.opacity(0.3), radius: 8)

                Image(systemName: item.icon)
                    .font(.system(size: 28))
                    .foregroundStyle(.white)
            }

            VStack(alignment: .leading, spacing: 4) {
                Text(item.name)
                    .font(.headline)
                    .lineLimit(2)

                Text(item.isDirectory ? "Folder" : item.url.pathExtension.uppercased())
                    .font(.subheadline)
                    .foregroundStyle(.secondary)
            }
        }
    }

    private func sizeSection(_ item: FileItem) -> some View {
        VStack(alignment: .leading, spacing: 12) {
            Text("Size")
                .font(.subheadline)
                .foregroundStyle(.secondary)

            HStack(alignment: .firstTextBaseline) {
                Text(item.formattedSize)
                    .font(.system(size: 28, weight: .semibold, design: .rounded))

                if item.parent != nil {
                    Text("(\(String(format: "%.1f", item.percentageOfParent()))% of parent)")
                        .font(.caption)
                        .foregroundStyle(.secondary)
                }
            }

            if item.isDirectory {
                HStack {
                    Label("\(item.children.count) items", systemImage: "doc.on.doc")
                    if item.descendantCount > item.children.count {
                        Text("(\(item.descendantCount) total)")
                            .foregroundStyle(.tertiary)
                    }
                }
                .font(.caption)
                .foregroundStyle(.secondary)
            }
        }
    }

    private func childrenSection(_ item: FileItem) -> some View {
        VStack(alignment: .leading, spacing: 12) {
            Text("Largest Items")
                .font(.subheadline)
                .foregroundStyle(.secondary)

            ForEach(item.topChildren(count: 5)) { child in
                HStack {
                    Image(systemName: child.icon)
                        .foregroundStyle(child.color)
                        .frame(width: 20)

                    Text(child.name)
                        .lineLimit(1)
                        .truncationMode(.middle)

                    Spacer()

                    Text(child.formattedSize)
                        .font(.caption)
                        .foregroundStyle(.secondary)
                }
                .font(.caption)
            }
        }
    }

    private func pathSection(_ item: FileItem) -> some View {
        VStack(alignment: .leading, spacing: 8) {
            Text("Location")
                .font(.subheadline)
                .foregroundStyle(.secondary)

            Text(item.url.path)
                .font(.caption)
                .foregroundStyle(.tertiary)
                .lineLimit(3)
                .textSelection(.enabled)

            Button("Show in Finder") {
                NSWorkspace.shared.selectFile(item.url.path, inFileViewerRootedAtPath: item.url.deletingLastPathComponent().path)
            }
            .buttonStyle(.link)
            .font(.caption)
        }
    }

    private var emptyState: some View {
        VStack(spacing: 12) {
            Image(systemName: "square.dashed")
                .font(.system(size: 40))
                .foregroundStyle(.quaternary)

            Text("Select an item")
                .font(.subheadline)
                .foregroundStyle(.secondary)

            Text("Click a bubble to see details")
                .font(.caption)
                .foregroundStyle(.tertiary)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }
}

#Preview("With Item") {
    let parent = FileItem(url: URL(fileURLWithPath: "/Users"), isDirectory: true, size: 100_000_000_000)
    let item = FileItem(
        url: URL(fileURLWithPath: "/Users/mitchel"),
        isDirectory: true,
        size: 45_000_000_000,
        children: [
            FileItem(url: URL(fileURLWithPath: "/Users/mitchel/Documents"), isDirectory: true, size: 15_000_000_000),
            FileItem(url: URL(fileURLWithPath: "/Users/mitchel/Downloads"), isDirectory: true, size: 12_000_000_000),
            FileItem(url: URL(fileURLWithPath: "/Users/mitchel/Library"), isDirectory: true, size: 8_000_000_000),
            FileItem(url: URL(fileURLWithPath: "/Users/mitchel/movie.mp4"), isDirectory: false, size: 5_000_000_000),
        ],
        parent: parent
    )

    DetailPanelView(item: item)
        .frame(height: 500)
}

#Preview("Empty") {
    DetailPanelView(item: nil)
        .frame(height: 400)
}

About

Internal tooling for Mac utility for storage management.

0 stars
0 forks