Add navigation stack
This commit is contained in:
parent
7f53c267d2
commit
8391997848
@ -30,10 +30,10 @@ public struct Label: SwiftUIWidget {
|
|||||||
public static func view(properties: Self) -> some SwiftUI.View {
|
public static func view(properties: Self) -> some SwiftUI.View {
|
||||||
SwiftUI.Label {
|
SwiftUI.Label {
|
||||||
SwiftUI.Text(properties.label)
|
SwiftUI.Text(properties.label)
|
||||||
|
SwiftUI.Spacer()
|
||||||
} icon: {
|
} icon: {
|
||||||
properties.icon.image
|
properties.icon.image
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
120
Sources/Core/View/NavigationStack.swift
Normal file
120
Sources/Core/View/NavigationStack.swift
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
//
|
||||||
|
// NavigationStack.swift
|
||||||
|
// MacBackend
|
||||||
|
//
|
||||||
|
// Created by david-swift on 14.12.2024.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
/// The navigation stack view.
|
||||||
|
public struct NavigationStack<Component>: SwiftUIWidget where Component: CustomStringConvertible {
|
||||||
|
|
||||||
|
/// The current navigation path.
|
||||||
|
@Meta.Binding var path: NavigationPath
|
||||||
|
/// The content.
|
||||||
|
var content: (Component) -> Body
|
||||||
|
/// The initial view.
|
||||||
|
var initialView: Body
|
||||||
|
|
||||||
|
/// The wrapped views.
|
||||||
|
public var wrappedViews: [String: Meta.AnyView] {
|
||||||
|
if let current = path.current {
|
||||||
|
_ = current
|
||||||
|
return [.mainContent: initialView, current.description: content(current)]
|
||||||
|
} else {
|
||||||
|
return [.mainContent: initialView]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize the navigation stack.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - path: The path.
|
||||||
|
/// - content: The content.
|
||||||
|
/// - initialView: The initial view.
|
||||||
|
public init(
|
||||||
|
path: Meta.Binding<NavigationPath>,
|
||||||
|
@Meta.ViewBuilder content: @escaping (Component) -> Body,
|
||||||
|
@Meta.ViewBuilder initialView: () -> Body
|
||||||
|
) {
|
||||||
|
self._path = path
|
||||||
|
self.content = content
|
||||||
|
self.initialView = initialView()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The navigation path.
|
||||||
|
public struct NavigationPath {
|
||||||
|
|
||||||
|
/// The path.
|
||||||
|
var path: [Component] = []
|
||||||
|
/// The current component.
|
||||||
|
var current: Component? { path.last }
|
||||||
|
|
||||||
|
/// The path as an array of strings.
|
||||||
|
var hashable: [String] {
|
||||||
|
get {
|
||||||
|
path.map { $0.description }
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if newValue.count < hashable.count {
|
||||||
|
_ = path.popLast()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize a navigation stack.
|
||||||
|
public init() { }
|
||||||
|
|
||||||
|
/// Pop the last element.
|
||||||
|
public mutating func pop() {
|
||||||
|
_ = path.popLast()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a new element.
|
||||||
|
/// - Parameter component: The element.
|
||||||
|
public mutating func push(_ component: Component) {
|
||||||
|
path.append(component)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Receive the SwiftUI view.
|
||||||
|
/// - Parameter properties: The properties.
|
||||||
|
/// - Returns: The SwiftUI view.
|
||||||
|
public static func view(properties: Self) -> some SwiftUI.View {
|
||||||
|
SwiftUINavigationStack(path: properties.$path.swiftUI)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The SwiftUI navigation stack.
|
||||||
|
struct SwiftUINavigationStack<Component>: SwiftUI.View where Component: CustomStringConvertible {
|
||||||
|
|
||||||
|
/// The path.
|
||||||
|
@SwiftUI.Binding var path: NavigationPath<Component>
|
||||||
|
/// The views.
|
||||||
|
@SwiftUI.Environment(\.views)
|
||||||
|
var views
|
||||||
|
|
||||||
|
/// The view's body.
|
||||||
|
var body: some SwiftUI.View {
|
||||||
|
SwiftUI.NavigationStack(path: $path.hashable) {
|
||||||
|
let view = MacBackendView(.mainContent)
|
||||||
|
if path.current != nil {
|
||||||
|
view
|
||||||
|
.navigationDestination(for: String.self) { string in
|
||||||
|
SwiftUI.VStack {
|
||||||
|
MacBackendView(string)
|
||||||
|
.environment(\.views, views)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
view
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The navigation path.
|
||||||
|
public typealias NavigationPath<Component: CustomStringConvertible> = NavigationStack<Component>.NavigationPath
|
||||||
@ -59,7 +59,6 @@ public struct MenuBar: MacSceneElement {
|
|||||||
let scene = SceneStorage(id: id, pointer: app.mainMenu) { }
|
let scene = SceneStorage(id: id, pointer: app.mainMenu) { }
|
||||||
let data = WidgetData(sceneStorage: scene, appStorage: app)
|
let data = WidgetData(sceneStorage: scene, appStorage: app)
|
||||||
let storage = MenuCollection { self.content }.getMenu(data: data, menu: app.mainMenu)
|
let storage = MenuCollection { self.content }.getMenu(data: data, menu: app.mainMenu)
|
||||||
print(app.helpItem.submenu)
|
|
||||||
let appStorage = MenuCollection { self.app }.getMenu(data: data, menu: app.appItem.submenu)
|
let appStorage = MenuCollection { self.app }.getMenu(data: data, menu: app.appItem.submenu)
|
||||||
let windowStorage = MenuCollection { self.window }.getMenu(data: data, menu: app.windowsItem.submenu)
|
let windowStorage = MenuCollection { self.window }.getMenu(data: data, menu: app.windowsItem.submenu)
|
||||||
let helpStorage = MenuCollection { self.help }.getMenu(data: data, menu: app.helpItem.submenu)
|
let helpStorage = MenuCollection { self.help }.getMenu(data: data, menu: app.helpItem.submenu)
|
||||||
|
|||||||
@ -20,53 +20,38 @@ struct Demo: App {
|
|||||||
@State(blockUpdates: true)
|
@State(blockUpdates: true)
|
||||||
private var width = 500
|
private var width = 500
|
||||||
@State(blockUpdates: true)
|
@State(blockUpdates: true)
|
||||||
private var height = 400
|
private var height = 450
|
||||||
@State private var elements: [Element] = [.init()]
|
@State private var elements: [Element] = [.init()]
|
||||||
@State private var selectedElement: String?
|
@State private var selectedElement: String?
|
||||||
@State private var alert = false
|
|
||||||
@State private var presentMenu: Signal = .init()
|
|
||||||
@State private var selection = "Hello"
|
@State private var selection = "Hello"
|
||||||
|
@State private var path: NavigationPath<Navigation> = .init()
|
||||||
|
|
||||||
var scene: Scene {
|
var scene: Scene {
|
||||||
Window(id: "secondary") { _ in
|
|
||||||
Text(selection)
|
|
||||||
.centeredToolbar {
|
|
||||||
Picker("Test", items: ["Hello", "World"], selection: $selection)
|
|
||||||
.segmented()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.frame(width: .constant(800), height: .constant(600))
|
|
||||||
Window("Main", id: "main") { _ in
|
Window("Main", id: "main") { _ in
|
||||||
NavigationSplitView {
|
NavigationSplitView {
|
||||||
List(elements, selection: $selectedElement) { element in
|
List(elements, selection: $selectedElement) { element in
|
||||||
Label(element.id, icon: .system(name: "play.fill"))
|
Label(element.id, icon: .system(name: "play.fill"))
|
||||||
}
|
}
|
||||||
} detail: {
|
} detail: {
|
||||||
VStack {
|
NavigationStack(path: $path) { navigation in
|
||||||
Button(selectedElement ?? "World") {
|
Button(navigation.description) {
|
||||||
let element = Element()
|
selectedElement = nil
|
||||||
elements.append(element)
|
|
||||||
selectedElement = element.id
|
|
||||||
}
|
}
|
||||||
Button(alert.description) {
|
} initialView: {
|
||||||
presentMenu.signal()
|
VStack {
|
||||||
}
|
Button(selectedElement ?? "World") {
|
||||||
.menu(present: presentMenu) {
|
let element = Element()
|
||||||
MenuButton(alert.description) {
|
elements.append(element)
|
||||||
print("Hi")
|
selectedElement = element.id
|
||||||
|
}
|
||||||
|
Button("Navigate") {
|
||||||
|
path.push(.test)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
.toolbar {
|
||||||
.alert("Hello", isPresented: $alert)
|
Button("World", icon: .system(name: "globe")) {
|
||||||
.cancelButton("Cancel") {
|
print("World")
|
||||||
print("Cancel")
|
}
|
||||||
}
|
|
||||||
.destructiveButton("Destructive", default: true) {
|
|
||||||
print("Destructive")
|
|
||||||
}
|
|
||||||
.toolbar {
|
|
||||||
Button("World", icon: .system(name: "globe")) {
|
|
||||||
print("World")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -138,4 +123,14 @@ struct Element: Identifiable {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum Navigation: CustomStringConvertible {
|
||||||
|
|
||||||
|
case test
|
||||||
|
|
||||||
|
var description: String {
|
||||||
|
"Test"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// swiftlint:enable missing_docs no_magic_numbers closure_body_length
|
// swiftlint:enable missing_docs no_magic_numbers closure_body_length
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user