|
8 | 8 |
|
9 | 9 | import SwiftUI |
10 | 10 |
|
11 | | -/** |
12 | | - This view lists tags in a leading to trailing flow and lets |
13 | | - you tap tags to add and remove them from a provided binding. |
14 | | - |
15 | | - The view takes a list of tags and use a tag view builder to |
16 | | - render a view for each tag. You can use any custom tag view, |
17 | | - for instance a ``TagCapsule``. |
18 | | - |
19 | | - The view can take a list of additional tags. Tho avoid that |
20 | | - tags disappear from the list when you toggle them off. Make |
21 | | - sure to use the `additionalTags` parameter to specify which |
22 | | - tags you always want to show in the list. |
23 | | - |
24 | | - You must specify a `container`, since this list is rendered |
25 | | - differently depending on if it's added to a `ScrollView` or |
26 | | - a `VerticalStack`. |
27 | | - */ |
| 11 | +/// This view lists a collection of tags, that can be tapped |
| 12 | +/// to toggle them in the provided tags binding. |
| 13 | +/// |
| 14 | +/// This view will list all tags in the provided binding, as |
| 15 | +/// well as a list of additional tags which should be listed |
| 16 | +/// even when they are not set in the binding. |
| 17 | +/// |
| 18 | +/// Note that this list only renders the tag views. You must |
| 19 | +/// specify the container in which they will be rendered. |
28 | 20 | public struct TagEditList<TagView: View>: View { |
29 | 21 |
|
30 | 22 | /// Create a tag edit list. |
31 | 23 | /// |
32 | 24 | /// - Parameters: |
33 | 25 | /// - tags: The items to render in the layout. |
34 | 26 | /// - additionalTags: Additional tags to pick from. |
35 | | - /// - container: The container type, by default `.scrollView`. |
36 | | - /// - horizontalSpacing: The horizontal spacing between items. |
37 | | - /// - verticalSpacing: The vertical spacing between items. |
38 | 27 | /// - tagView: The tag view builder. |
39 | 28 | public init( |
40 | 29 | tags: Binding<[String]>, |
41 | 30 | additionalTags: [String], |
42 | | - container: TagListContainer = .scrollView, |
43 | | - horizontalSpacing: CGFloat = 5, |
44 | | - verticalSpacing: CGFloat = 5, |
45 | 31 | @ViewBuilder tagView: @escaping TagViewBuilder |
46 | 32 | ) { |
47 | 33 | self.tags = tags |
48 | 34 | self.additionalTags = additionalTags |
49 | | - self.container = container |
50 | | - self.horizontalSpacing = horizontalSpacing |
51 | | - self.verticalSpacing = verticalSpacing |
52 | 35 | self.tagView = tagView |
53 | | - let initialHeight: CGFloat = container == .scrollView ? .zero : .infinity |
54 | | - _totalHeight = State(initialValue: initialHeight) |
55 | 36 | } |
56 | 37 |
|
57 | 38 | private let tags: Binding<[String]> |
58 | 39 | private let additionalTags: [String] |
59 | | - private let container: TagListContainer |
60 | | - private let horizontalSpacing: CGFloat |
61 | | - private let verticalSpacing: CGFloat |
62 | | - |
| 40 | + |
63 | 41 | @ViewBuilder |
64 | 42 | private let tagView: TagViewBuilder |
65 | 43 |
|
66 | 44 | /// This type defines the tag view builder for the list. |
67 | 45 | public typealias TagViewBuilder = (_ tag: String, _ hasTag: Bool) -> TagView |
68 | 46 |
|
69 | | - @State |
70 | | - private var totalHeight: CGFloat |
71 | | - |
72 | 47 | public var body: some View { |
73 | 48 | TagList( |
74 | 49 | tags: allTags, |
75 | | - container: container, |
76 | | - horizontalSpacing: horizontalSpacing, |
77 | | - verticalSpacing: verticalSpacing |
78 | 50 | ) { tag in |
79 | 51 | Button(action: { toggleTag(tag) }) { |
80 | 52 | tagView(tag, hasTag(tag)) |
@@ -124,104 +96,60 @@ private extension TagEditList { |
124 | 96 |
|
125 | 97 | struct Preview: View { |
126 | 98 |
|
127 | | - @State |
128 | | - var newTag = "" |
129 | | - |
130 | | - @State |
131 | | - var tags = ["tag-1"] |
132 | | - |
133 | | - @State |
134 | | - var added: [String] = [] |
| 99 | + @State var newTag = "" |
| 100 | + @State var tags = ["tag-1"] |
| 101 | + |
| 102 | + let slugConfiguration = SlugConfiguration.standard |
135 | 103 |
|
136 | 104 | var body: some View { |
137 | 105 | NavigationView { |
138 | 106 | ScrollView { |
139 | | - VStack(alignment: .leading, spacing: 30) { |
140 | | - list("Standard Style", .standard, .standardSelected) |
141 | | - list("Custom Style", .custom, .customSelected) |
| 107 | + TagEditList( |
| 108 | + tags: $tags, |
| 109 | + additionalTags: ["always-visible"] |
| 110 | + ) { tag, isAdded in |
| 111 | + Text(tag.slugified()) |
| 112 | + .font(.system(size: 12)) |
| 113 | + .foregroundColor(.black) |
| 114 | + .padding(.horizontal, 8) |
| 115 | + .padding(.vertical, 4) |
| 116 | + .background(isAdded ? Color.green : Color.primary.opacity(0.1), in: .capsule) |
142 | 117 | } |
143 | 118 | .padding() |
144 | | - |
145 | 119 | } |
146 | | -// .background( |
147 | | -// LinearGradient( |
148 | | -// colors: [.red, .blue], |
149 | | -// startPoint: .top, |
150 | | -// endPoint: .bottom |
151 | | -// ) |
152 | | -// ) |
153 | 120 | .toolbar { |
154 | 121 | ToolbarItem { |
155 | 122 | HStack { |
156 | 123 | TagTextField( |
157 | 124 | text: $newTag, |
158 | | - placeholder: "Add new tag" |
| 125 | + placeholder: "Add new tag", |
| 126 | + configuration: slugConfiguration |
159 | 127 | ) |
160 | 128 | #if os(iOS) |
161 | 129 | .autocorrectionDisabled() |
162 | 130 | .textFieldStyle(.roundedBorder) |
163 | 131 | #endif |
164 | 132 | Button("Add") { |
165 | | - addTag(tag: newTag) |
| 133 | + addNewTag(tag: newTag) |
166 | 134 | } |
167 | 135 | .disabled(newTag.isEmpty) |
168 | 136 | } |
169 | 137 | } |
170 | 138 | } |
171 | 139 | } |
172 | 140 | } |
173 | | - |
174 | | - private func list( |
175 | | - _ title: String, |
176 | | - _ style: TagCapsuleStyle, |
177 | | - _ selected: TagCapsuleStyle |
178 | | - ) -> some View { |
179 | | - VStack(alignment: .leading) { |
180 | | - Text(title) |
181 | | - .font(.footnote) |
182 | | - |
183 | | - TagEditList( |
184 | | - tags: $tags, |
185 | | - additionalTags: ["tag-1", "tag-2", "tag-3"] + added |
186 | | - ) { tag, hasTag in |
187 | | - TagCapsule(tag) |
188 | | - .tagCapsuleStyle(hasTag ? selected : style) |
189 | | - } |
190 | | - } |
191 | | - } |
192 | 141 |
|
193 | | - private func addTag( |
| 142 | + private func addNewTag( |
194 | 143 | tag: String, |
195 | 144 | selected: Bool = true |
196 | 145 | ) { |
197 | | - let slug = tag.slugified() |
| 146 | + let slug = tag.slugified(with: slugConfiguration) |
198 | 147 | if selected { |
199 | 148 | tags.append(slug) |
200 | 149 | } |
201 | | - added.append(slug) |
202 | 150 | newTag = "" |
203 | 151 | } |
204 | 152 | } |
205 | 153 |
|
206 | 154 | return Preview() |
207 | 155 | } |
208 | | - |
209 | | -private extension TagCapsuleStyle { |
210 | | - |
211 | | - static var custom: TagCapsuleStyle { |
212 | | - .init( |
213 | | - foregroundColor: .black, |
214 | | - backgroundColor: .red, |
215 | | - border: .init(width: 4) |
216 | | - ) |
217 | | - } |
218 | | - |
219 | | - static var customSelected: TagCapsuleStyle { |
220 | | - .init( |
221 | | - foregroundColor: .black, |
222 | | - backgroundColor: .red, |
223 | | - border: .init(width: 4), |
224 | | - shadow: .standardSelected |
225 | | - ) |
226 | | - } |
227 | | -} |
0 commit comments