gitbay/Views/Shared/SidebarRouting.swift
52 lines · 1875 bytes
3 symbols in this file
1import SwiftUI
2
3extension EnvironmentValues {
4 /// Set on a list shown as a `NavigationSplitView` sidebar: what it
5 /// selects opens in the detail column's stack. Nil in a plain stack.
6 @Entry var sidebarSelection: Binding<AnyHashable?>?
7
8 /// Appends a route to the enclosing stack's path. Screens that push
9 /// from a button use it instead of `navigationDestination(item:)`,
10 /// which puts any later value-link push underneath the pushed screen.
11 @Entry var pushRoute: (any Hashable) -> Void = { _ in }
12}
13
14extension View {
15 /// Lets screens in this stack push onto `path` with `pushRoute`.
16 func pushesRoutes(into path: Binding<NavigationPath>) -> some View {
17 environment(\.pushRoute) { path.wrappedValue.append($0) }
18 }
19}
20
21/// A `List` whose rows select into `sidebarSelection` when there is one.
22/// A value-based link in a sidebar only reaches the detail column's stack
23/// through the list's selection.
24struct RoutingList<Content: View>: View {
25 @ViewBuilder let content: () -> Content
26 @Environment(\.sidebarSelection) private var selection
27
28 var body: some View {
29 if let selection {
30 List(selection: selection) { content() }
31 } else {
32 List { content() }
33 }
34 }
35}
36
37/// A `NavigationLink` in a stack; in a sidebar, a button that opens the
38/// route in the detail column. For links outside the list, such as
39/// toolbar items, which the list's selection does not cover.
40struct RouteLink<Value: Hashable, Label: View>: View {
41 let value: Value
42 @ViewBuilder let label: () -> Label
43 @Environment(\.sidebarSelection) private var selection
44
45 var body: some View {
46 if let selection {
47 Button { selection.wrappedValue = AnyHashable(value) } label: { label() }
48 } else {
49 NavigationLink(value: value) { label() }
50 }
51 }
52}