Skip to content

Commit

Permalink
ListView; fallback Key to String (#33)
Browse files Browse the repository at this point in the history
  • Loading branch information
eagleoflqj authored Dec 14, 2024
1 parent b2ac076 commit 0d76450
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 5 deletions.
2 changes: 1 addition & 1 deletion src/config/BooleanView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ struct BooleanView: OptionViewProtocol {
Toggle(
isOn: Binding<Bool>(
get: { value as! String == "True" },
set: { x in value = x ? "True" : "False" }
set: { value = $0 ? "True" : "False" }
)
) {
Text(label)
Expand Down
2 changes: 1 addition & 1 deletion src/config/EnumView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ struct EnumView: OptionViewProtocol {
label,
selection: Binding<String>(
get: { value as! String },
set: { x in value = x }
set: { value = $0 }
)
) {
ForEach(dataToOptions(data), id: \.0) { pair in
Expand Down
81 changes: 81 additions & 0 deletions src/config/ListView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import SwiftUI

private func deserialize(_ value: Any) -> [Any] {
guard let value = value as? [String: Any] else {
return []
}
return (0..<value.count).compactMap { i in value[String(i)] }
}

private func serialize(_ value: [Any]) -> [String: Any] {
return value.enumerated().reduce(into: [String: Any]()) { result, pair in
result[String(pair.offset)] = pair.element
}
}

private struct ListSectionHeader: View {
@Binding var value: Any

var body: some View {
HStack {
Spacer()
Button {
var list = deserialize(value)
list.append("")
value = serialize(list)
} label: {
Image(systemName: "plus")
}
}
}
}

struct ListSubView: OptionViewProtocol {
let label: String
let data: [String: Any]
@Binding var value: Any

var body: some View {
let type = data["Type"] as! String
let optionViewType = toOptionViewType(["Type": String(type.suffix(type.count - "List|".count))])
var list = deserialize(value)
List {
Section(header: ListSectionHeader(value: $value)) {
ForEach(list.indices, id: \.self) { i in
AnyView(
optionViewType.init(
label: "", data: [:],
value: Binding<Any>(
get: { list[i] },
set: {
list[i] = $0
value = serialize(list)
}
)))
}
.onDelete { offsets in
list.remove(atOffsets: offsets)
value = serialize(list)
}
.onMove { indices, newOffset in
list.move(fromOffsets: indices, toOffset: newOffset)
value = serialize(list)
}
}
}.navigationTitle(label)
}
}

struct ListView: OptionViewProtocol {
let label: String
let data: [String: Any]
@Binding var value: Any

var body: some View {
NavigationLink(
destination: ListSubView(label: label, data: data, value: $value)
) {
Text(label)
}
}
}
6 changes: 4 additions & 2 deletions src/config/StringView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ struct StringView: View, OptionViewProtocol {
"",
text: Binding<String>(
get: { value as! String },
set: { x in value = x }
set: { value = $0 }
)
).multilineTextAlignment(.trailing)
)
// Leading for List item, trailing for String option.
.multilineTextAlignment(label.isEmpty ? .leading : .trailing)
}
}
}
5 changes: 4 additions & 1 deletion src/config/option.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,17 @@ func toOptionViewType(_ data: [String: Any]) -> any OptionViewProtocol.Type {
return EnumView.self
case "Integer":
return IntegerView.self
case "String":
case "String", "Key":
if data["IsEnum"] as? String == "True" {
return EnumView.self
}
return StringView.self
case "External":
return ExternalView.self
default:
if type.starts(with: "List|") {
return ListView.self
}
return UnknownView.self
}
}

0 comments on commit 0d76450

Please sign in to comment.