diff --git a/DeltaFeatures/Option.swift b/DeltaFeatures/Option.swift index 3f828c5..6b4b9d6 100644 --- a/DeltaFeatures/Option.swift +++ b/DeltaFeatures/Option.swift @@ -10,8 +10,14 @@ import SwiftUI import Combine @propertyWrapper -public class Option: _AnyOption +public class Option: _AnyOption { + // Nil name == hidden option. + public let name: LocalizedStringKey? + public let description: LocalizedStringKey? + + public private(set) var detailView: () -> DetailView? = { nil } + // Assigned to property name. public internal(set) var key: String = "" @@ -27,6 +33,14 @@ public class Option: _AnyOption private let defaultValue: Value + private var valueBinding: Binding { + Binding(get: { + self.wrappedValue + }, set: { newValue in + self.wrappedValue = newValue + }) + } + /// @propertyWrapper public var projectedValue: some Option { return self @@ -59,21 +73,75 @@ public class Option: _AnyOption } } - // Non-Optional - public init(wrappedValue: Value) + private init(defaultValue: Value, name: LocalizedStringKey?, description: LocalizedStringKey?) { - self.defaultValue = wrappedValue + self.defaultValue = defaultValue + + self.name = name + self.description = description + self.detailView = { nil } + } +} + +// "Hidden" Option (no name or custom SwiftUI view) +public extension Option where DetailView == EmptyView +{ + // Non-Optional + convenience init(wrappedValue: Value) + { + self.init(defaultValue: wrappedValue, name: nil, description: nil) } // Optional, default = nil - public init() where Value: OptionalProtocol + convenience init() where Value: OptionalProtocol { - self.defaultValue = Value.none + self.init(defaultValue: Value.none, name: nil, description: nil) } // Optional, default = non-nil - public init(wrappedValue: Value) where Value: OptionalProtocol + convenience init(wrappedValue: Value) where Value: OptionalProtocol { - self.defaultValue = wrappedValue + self.init(defaultValue: wrappedValue, name: nil, description: nil) + +// "Custom" Option (User-visible, provides SwiftUI view to configure option) +public extension Option where Value: LocalizedOptionValue +{ + // Non-Optional + convenience init(wrappedValue: Value, name: LocalizedStringKey, description: LocalizedStringKey? = nil, @ViewBuilder detailView: @escaping (Binding) -> DetailView) + { + self.init(defaultValue: wrappedValue, name: name, description: description) + + self.detailView = { [weak self] in + guard let self else { return nil } + + let view = detailView(self.valueBinding) + return view + } + } + + // Optional, default = nil + convenience init(name: LocalizedStringKey, description: LocalizedStringKey? = nil, @ViewBuilder detailView: @escaping (Binding) -> DetailView) where Value: OptionalProtocol, Value.Wrapped: LocalizedOptionValue + { + self.init(defaultValue: Value.none, name: name, description: description) + + self.detailView = { [weak self] in + guard let self else { return nil } + + let view = detailView(self.valueBinding) + return view + } + } + + // Optional, default = non-nil + convenience init(wrappedValue: Value, name: LocalizedStringKey, description: LocalizedStringKey? = nil, @ViewBuilder detailView: @escaping (Binding) -> DetailView) where Value: OptionalProtocol, Value.Wrapped: LocalizedOptionValue + { + self.init(defaultValue: wrappedValue, name: name, description: description) + + self.detailView = { [weak self] in + guard let self else { return nil } + + let view = detailView(self.valueBinding) + return view + } } } diff --git a/DeltaFeatures/Protocols/AnyOption.swift b/DeltaFeatures/Protocols/AnyOption.swift index 32177df..310dc69 100644 --- a/DeltaFeatures/Protocols/AnyOption.swift +++ b/DeltaFeatures/Protocols/AnyOption.swift @@ -11,10 +11,16 @@ import SwiftUI public protocol AnyOption: AnyObject, Identifiable { associatedtype Value: OptionValue + associatedtype DetailView: View + + var name: LocalizedStringKey? { get } + var description: LocalizedStringKey? { get } var key: String { get } var settingsKey: SettingsName { get } + var detailView: () -> DetailView? { get } + var value: Value { get set } }