Skip to content

Commit

Permalink
Expose BindingSource
Browse files Browse the repository at this point in the history
  • Loading branch information
PavelHolec committed Jun 17, 2024
1 parent ee1c97c commit 82a3ae6
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 23 deletions.
15 changes: 9 additions & 6 deletions Sources/Orbit/Components/Collapse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ public struct Collapse<Header: View, Content: View>: View {

private let headerVerticalPadding: CGFloat
private let showSeparator: Bool
private let isExpanded: Binding<Bool>?
private let isExpanded: OptionalBindingSource<Bool>
@ViewBuilder private let content: Content
@ViewBuilder private let header: Header

public var body: some View {
BindingSource(isExpanded, fallbackInitialValue: false) { $isExpanded in
OptionalBinding(isExpanded) { $isExpanded in
VStack(alignment: .leading, spacing: 0) {
SwiftUI.Button {
withAnimation(.easeInOut(duration: 0.2)) {
Expand Down Expand Up @@ -71,7 +71,7 @@ public extension Collapse {
self.headerVerticalPadding = 0
self.content = content()
self.showSeparator = showSeparator
self.isExpanded = isExpanded
self.isExpanded = .binding(isExpanded)
}

/// Creates Orbit ``Collapse`` component.
Expand All @@ -80,7 +80,7 @@ public extension Collapse {
self.headerVerticalPadding = 0
self.content = content()
self.showSeparator = showSeparator
self.isExpanded = nil
self.isExpanded = .state(false)
}
}

Expand All @@ -92,7 +92,7 @@ public extension Collapse where Header == Text {
self.headerVerticalPadding = .small
self.content = content()
self.showSeparator = showSeparator
self.isExpanded = isExpanded
self.isExpanded = .binding(isExpanded)
}

/// Creates Orbit ``Collapse`` component.
Expand All @@ -101,7 +101,7 @@ public extension Collapse where Header == Text {
self.headerVerticalPadding = .small
self.content = content()
self.showSeparator = showSeparator
self.isExpanded = nil
self.isExpanded = .state(false)
}
}

Expand Down Expand Up @@ -144,6 +144,9 @@ struct CollapsePreviews: PreviewProvider {
headerPlaceholder
}
}
Collapse("Toggle with internal state") {
contentPlaceholder
}
}
.padding(.medium)
}
Expand Down
46 changes: 29 additions & 17 deletions Sources/Orbit/Support/BindingSource.swift
Original file line number Diff line number Diff line change
@@ -1,30 +1,42 @@
import SwiftUI

/// A view that provides a binding to its content.
/// A binding source for the ``OptionalBinding``.
public enum OptionalBindingSource<Value> {
case binding(Binding<Value>)
case state(Value)
}

/// A view that provides either a binding to its content or an internal state.
///
/// This binding can either be supplied, in which case it is used directly,
/// or one is derived from internal state (starting with `defaultValue`).
/// The binding can either be supplied, in which case it is used directly,
/// or one is derived from internal state.
///
/// This is is useful for components that can manage their own state,
/// but we also want to make it possible for that state to be driven
/// from the outside if a binding is passed.
struct BindingSource<Value, Content: View>: View {

let outer: Binding<Value>?
@State var inner: Value
/// This is is useful for components that need to manage their own state,
/// but also allow that state to be overridden
/// using the binding provided from the outside.
public struct OptionalBinding<Value, Content: View>: View {
let binding: Binding<Value>?
@State var state: Value
let content: (Binding<Value>) -> Content

var body: some View {
content(outer ?? $inner)
public var body: some View {
content(binding ?? $state)
}

init(
_ binding: Binding<Value>?,
fallbackInitialValue: Value,
public init(
_ source: OptionalBindingSource<Value>,
@ViewBuilder content: @escaping (Binding<Value>) -> Content
) {
self.outer = binding
self._inner = State(wrappedValue: fallbackInitialValue)
switch source {
case .binding(let binding):
self.binding = binding
self._state = .init(wrappedValue: binding.wrappedValue)
case .state(let value):
self.binding = nil
self._state = State(wrappedValue: value)
}

self.content = content
}
}

0 comments on commit 82a3ae6

Please sign in to comment.