Add _NavigationLinkStyle for sidebar lists

This commit is contained in:
Carson Katri 2020-08-03 18:09:40 -04:00
parent 4b05fd8523
commit cb6359431d
5 changed files with 82 additions and 17 deletions

View File

@ -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)
}
}

View File

@ -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
} }
} }

View File

@ -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)
} }
} }

View File

@ -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;

View File

@ -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 {