Add _NavigationLinkStyle for sidebar lists
This commit is contained in:
parent
4b05fd8523
commit
cb6359431d
|
@ -79,8 +79,49 @@ public extension ForEach where Data == Range<Int>, ID == Int {
|
||||||
|
|
||||||
extension ForEach: ParentView {
|
extension ForEach: ParentView {
|
||||||
public var children: [AnyView] {
|
public var children: [AnyView] {
|
||||||
data.map { AnyView(content($0)) }
|
data.map { AnyView(IDView(content($0), id: $0[keyPath: id])) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ForEach: GroupView {}
|
extension ForEach: GroupView {}
|
||||||
|
|
||||||
|
struct _IDKey: EnvironmentKey {
|
||||||
|
static let defaultValue: AnyHashable? = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
extension EnvironmentValues {
|
||||||
|
public var _id: AnyHashable? {
|
||||||
|
get {
|
||||||
|
self[_IDKey.self]
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
self[_IDKey.self] = newValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public protocol _AnyIDView {
|
||||||
|
var anyId: AnyHashable { get }
|
||||||
|
}
|
||||||
|
|
||||||
|
struct IDView<Content, ID>: View, _AnyIDView where Content: View, ID: Hashable {
|
||||||
|
let content: Content
|
||||||
|
let id: ID
|
||||||
|
var anyId: AnyHashable { AnyHashable(id) }
|
||||||
|
|
||||||
|
init(_ content: Content, id: ID) {
|
||||||
|
self.content = content
|
||||||
|
self.id = id
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
content
|
||||||
|
.environment(\._id, AnyHashable(id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension View {
|
||||||
|
public func id<ID>(_ id: ID) -> some View where ID: Hashable {
|
||||||
|
IDView(self, id: id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -15,15 +15,24 @@
|
||||||
// Created by Jed Fox on 06/30/2020.
|
// Created by Jed Fox on 06/30/2020.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import OpenCombine
|
||||||
|
|
||||||
|
final class NavigationLinkDestination {
|
||||||
|
let view: AnyView
|
||||||
|
init<V: View>(_ destination: V) {
|
||||||
|
view = AnyView(destination)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public struct NavigationLink<Label, Destination>: View where Label: View, Destination: View {
|
public struct NavigationLink<Label, Destination>: View where Label: View, Destination: View {
|
||||||
let destination: Destination
|
@State var destination: NavigationLinkDestination
|
||||||
let label: Label
|
let label: Label
|
||||||
|
|
||||||
@Environment(\.navigationDestination) var navigationContext
|
@EnvironmentObject var navigationContext: NavigationContext
|
||||||
@Environment(\._navigationLinkStyle) var style
|
@Environment(\._navigationLinkStyle) var style
|
||||||
|
|
||||||
public init(destination: Destination, @ViewBuilder label: () -> Label) {
|
public init(destination: Destination, @ViewBuilder label: () -> Label) {
|
||||||
self.destination = destination
|
_destination = State(wrappedValue: NavigationLinkDestination(destination))
|
||||||
self.label = label()
|
self.label = label()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +78,9 @@ extension NavigationLink where Label == Text {
|
||||||
public struct _NavigationLinkProxy<Label, Destination> where Label: View, Destination: View {
|
public struct _NavigationLinkProxy<Label, Destination> where Label: View, Destination: View {
|
||||||
public let subject: NavigationLink<Label, Destination>
|
public let subject: NavigationLink<Label, Destination>
|
||||||
|
|
||||||
public init(_ subject: NavigationLink<Label, Destination>) { self.subject = subject }
|
public init(_ subject: NavigationLink<Label, Destination>) {
|
||||||
|
self.subject = subject
|
||||||
|
}
|
||||||
|
|
||||||
public var label: AnyView {
|
public var label: AnyView {
|
||||||
subject.style.makeBody(configuration: .init(body: AnyView(subject.label),
|
subject.style.makeBody(configuration: .init(body: AnyView(subject.label),
|
||||||
|
@ -78,10 +89,10 @@ public struct _NavigationLinkProxy<Label, Destination> where Label: View, Destin
|
||||||
|
|
||||||
public var style: _AnyNavigationLinkStyle { subject.style }
|
public var style: _AnyNavigationLinkStyle { subject.style }
|
||||||
public var isSelected: Bool {
|
public var isSelected: Bool {
|
||||||
true
|
ObjectIdentifier(subject.destination) == ObjectIdentifier(subject.navigationContext.destination)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func activate() {
|
public func activate() {
|
||||||
subject.navigationContext!.wrappedValue = AnyView(subject.destination)
|
subject.navigationContext.destination = subject.destination
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,10 +15,14 @@
|
||||||
// Created by Jed Fox on 06/30/2020.
|
// Created by Jed Fox on 06/30/2020.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
final class NavigationContext: ObservableObject {
|
||||||
|
@Published var destination = NavigationLinkDestination(EmptyView())
|
||||||
|
}
|
||||||
|
|
||||||
public struct NavigationView<Content>: View where Content: View {
|
public struct NavigationView<Content>: View where Content: View {
|
||||||
let content: Content
|
let content: Content
|
||||||
|
|
||||||
@State var destination = AnyView(EmptyView())
|
@ObservedObject var context = NavigationContext()
|
||||||
|
|
||||||
public init(@ViewBuilder content: () -> Content) {
|
public init(@ViewBuilder content: () -> Content) {
|
||||||
self.content = content()
|
self.content = content()
|
||||||
|
@ -39,8 +43,9 @@ public struct _NavigationViewProxy<Content: View>: View {
|
||||||
public var body: some View {
|
public var body: some View {
|
||||||
HStack {
|
HStack {
|
||||||
content
|
content
|
||||||
subject.destination
|
subject.context.destination.view
|
||||||
}.environment(\.navigationDestination, subject.$destination)
|
}
|
||||||
|
.environmentObject(subject.context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,11 +15,17 @@
|
||||||
import TokamakCore
|
import TokamakCore
|
||||||
|
|
||||||
private extension DOMViewModifier {
|
private extension DOMViewModifier {
|
||||||
func unwrapToStyle<T>(_ key: KeyPath<Self, T?>, property: String) -> String {
|
func unwrapToStyle<T>(_ key: KeyPath<Self, T?>,
|
||||||
|
property: String? = nil,
|
||||||
|
defaultValue: String = "") -> String {
|
||||||
if let val = self[keyPath: key] {
|
if let val = self[keyPath: key] {
|
||||||
return "\(property): \(val)px;"
|
if let property = property {
|
||||||
|
return "\(property): \(val)px;"
|
||||||
|
} else {
|
||||||
|
return "\(val)px;"
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return ""
|
return defaultValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,12 +46,14 @@ extension _FrameLayout: DOMViewModifier {
|
||||||
|
|
||||||
extension _FlexFrameLayout: DOMViewModifier {
|
extension _FlexFrameLayout: DOMViewModifier {
|
||||||
public var attributes: [String: String] {
|
public var attributes: [String: String] {
|
||||||
["style": """
|
let flexibleWidth = minWidth == 0 && maxWidth == .infinity
|
||||||
|
let flexibleHeight = minHeight == 0 && maxHeight == .infinity
|
||||||
|
return ["style": """
|
||||||
\(unwrapToStyle(\.minWidth, property: "min-width"))
|
\(unwrapToStyle(\.minWidth, property: "min-width"))
|
||||||
\(unwrapToStyle(\.idealWidth, property: "width"))
|
width: \(unwrapToStyle(\.idealWidth, defaultValue: flexibleWidth ? "100%" : "auto"));
|
||||||
\(unwrapToStyle(\.maxWidth, property: "max-width"))
|
\(unwrapToStyle(\.maxWidth, property: "max-width"))
|
||||||
\(unwrapToStyle(\.minHeight, property: "min-height"))
|
\(unwrapToStyle(\.minHeight, property: "min-height"))
|
||||||
\(unwrapToStyle(\.idealHeight, property: "height"))
|
height: \(unwrapToStyle(\.idealHeight, defaultValue: flexibleHeight ? "100%" : "auto"));
|
||||||
\(unwrapToStyle(\.maxHeight, property: "max-height"))
|
\(unwrapToStyle(\.maxHeight, property: "max-height"))
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
|
|
@ -91,7 +91,7 @@ extension InsetGroupedListStyle: ListStyleDeferredToRenderer {
|
||||||
|
|
||||||
extension SidebarListStyle: ListStyleDeferredToRenderer {
|
extension SidebarListStyle: ListStyleDeferredToRenderer {
|
||||||
public func listRow<Row>(_ row: Row) -> AnyView where Row: View {
|
public func listRow<Row>(_ row: Row) -> AnyView where Row: View {
|
||||||
AnyView(row)
|
AnyView(row.frame(minWidth: 0, maxWidth: .infinity))
|
||||||
}
|
}
|
||||||
|
|
||||||
public func listBody<ListBody>(_ content: ListBody) -> AnyView where ListBody: View {
|
public func listBody<ListBody>(_ content: ListBody) -> AnyView where ListBody: View {
|
||||||
|
|
Loading…
Reference in New Issue