jellyflood/Shared/Objects/EnumPicker.swift

85 lines
2.2 KiB
Swift

//
// Swiftfin is subject to the terms of the Mozilla Public
// License, v2.0. If a copy of the MPL was not distributed with this
// file, you can obtain one at https://mozilla.org/MPL/2.0/.
//
// Copyright (c) 2023 Jellyfin & Jellyfin Contributors
//
import SwiftUI
struct EnumPicker<EnumType: CaseIterable & Displayable & Hashable>: View {
enum NoneStyle: Displayable {
case text
case dash(Int)
case custom(String)
var displayTitle: String {
switch self {
case .text:
return L10n.none
case let .dash(length):
precondition(length >= 1, "Dash must have length of at least 1.")
return String(repeating: "-", count: length)
case let .custom(text):
precondition(!text.isEmpty, "Custom text must have length of at least 1.")
return text
}
}
}
@Binding
private var selection: EnumType?
private let title: String
private let hasNil: Bool
private var noneStyle: NoneStyle
var body: some View {
Picker(title, selection: $selection) {
if hasNil {
Text(noneStyle.displayTitle)
.tag(nil as EnumType?)
}
ForEach(EnumType.allCases.asArray, id: \.hashValue) {
Text($0.displayTitle)
.tag($0 as EnumType?)
}
}
}
}
extension EnumPicker {
init(title: String, selection: Binding<EnumType?>) {
self.title = title
self._selection = selection
self.hasNil = true
self.noneStyle = .text
}
init(title: String, selection: Binding<EnumType>) {
self.title = title
let binding = Binding<EnumType?> {
selection.wrappedValue
} set: { newValue, _ in
assert(newValue != nil, "Should not have nil new value with non-optional binding")
selection.wrappedValue = newValue!
}
self._selection = binding
self.hasNil = false
self.noneStyle = .text
}
func noneStyle(_ newStyle: NoneStyle) -> Self {
copy(modifying: \.noneStyle, with: newStyle)
}
}