summaryrefslogtreecommitdiffhomepage
path: root/ios/MullvadVPN/Views/List/MullvadList.swift
blob: 45d4c938350b49d12153985cc8a116134cdc25c8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import SwiftUI

/// A plain simple list.
/// * separators reaching all the way
/// * the height of all items in the list are based on the highest element
/// * the list is not higher than all its items
/// * transparent background
struct MullvadList<Content: View, Data: RandomAccessCollection<ID>, ID: Hashable>: View {
    let data: Data
    let id: KeyPath<Data.Element, ID>
    let content: (Data.Element) -> Content
    let header: (() -> AnyView)?
    let footer: (() -> AnyView)?

    init(
        _ data: Data,
        id: KeyPath<Data.Element, ID>,
        header: (() -> some View)? = nil,
        footer: (() -> some View)? = nil,
        @ViewBuilder content: @escaping (Data.Element) -> Content
    ) {
        self.data = data
        self.id = id
        self.header = header.map { builder in { AnyView(builder()) } }
        self.footer = footer.map { builder in { AnyView(builder()) } }
        self.content = content
    }

    init(
        _ data: Data,
        header: (() -> some View)? = nil,
        footer: (() -> some View)? = nil,
        @ViewBuilder content: @escaping (Data.Element) -> Content
    ) where Data.Element == ID {
        self.init(data, id: \.self, header: header, footer: footer, content: content)
    }

    var body: some View {
        VStack(alignment: .leading) {
            List {
                if let headerView = header?() {
                    headerView
                        .listRowStyling(insets: EdgeInsets(UIMetrics.contentHeadingLayoutMargins))
                }

                let lastItem = data.last
                ForEach(data, id: id) { item in
                    VStack(spacing: 0) {
                        content(item)
                        if item != lastItem {
                            RowSeparator()
                        }
                    }
                    .listRowStyling()
                }

                if let footerView = footer?() {
                    footerView
                        .listRowStyling(insets: EdgeInsets(UIMetrics.contentFooterLayoutMargins))
                }
            }
            .listStyle(.plain)
            .listRowSpacing(.zero)
            .environment(\.defaultMinListRowHeight, 0)
        }
    }
}

extension View {
    func listRowStyling(
        background: Color = .clear,
        separator: Visibility = .hidden,
        insets: EdgeInsets = .init()
    ) -> some View {
        apply {
            $0
                .listRowBackground(background)
                .listRowSeparator(separator)
                .listRowInsets(insets)
        }
    }
}

#Preview {
    MullvadList(
        [1, 2, 3],
        header: {
            Text("Header")
        },
        footer: {
            Text("Footer")
        },
        content: { item in
            Text("Item \(item)").padding()
        }
    )
}