Style fixes for NavigationLink, DisclosureGroup, Button, TextField, Picker, and more
This commit is contained in:
parent
cdc452b7a5
commit
abc59f048a
|
@ -0,0 +1,74 @@
|
|||
// Copyright 2020 Tokamak contributors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// Created by Carson Katri on 8/2/20.
|
||||
//
|
||||
|
||||
public struct _NavigationLinkStyleConfiguration: View {
|
||||
public let body: AnyView
|
||||
public let isSelected: Bool
|
||||
}
|
||||
|
||||
public protocol _NavigationLinkStyle {
|
||||
associatedtype Body: View
|
||||
typealias Configuration = _NavigationLinkStyleConfiguration
|
||||
func makeBody(configuration: Configuration) -> Self.Body
|
||||
}
|
||||
|
||||
public struct _DefaultNavigationLinkStyle: _NavigationLinkStyle {
|
||||
public func makeBody(configuration: Configuration) -> some View {
|
||||
configuration.foregroundColor(.accentColor)
|
||||
}
|
||||
}
|
||||
|
||||
public struct _AnyNavigationLinkStyle: _NavigationLinkStyle {
|
||||
public typealias Body = AnyView
|
||||
|
||||
private let bodyClosure: (_NavigationLinkStyleConfiguration) -> AnyView
|
||||
public let type: Any.Type
|
||||
|
||||
public init<S: _NavigationLinkStyle>(_ style: S) {
|
||||
type = S.self
|
||||
bodyClosure = { configuration in
|
||||
AnyView(style.makeBody(configuration: configuration))
|
||||
}
|
||||
}
|
||||
|
||||
public func makeBody(configuration: Configuration) -> AnyView {
|
||||
bodyClosure(configuration)
|
||||
}
|
||||
}
|
||||
|
||||
public enum _NavigationLinkStyleKey: EnvironmentKey {
|
||||
public static var defaultValue: _AnyNavigationLinkStyle {
|
||||
_AnyNavigationLinkStyle(_DefaultNavigationLinkStyle())
|
||||
}
|
||||
}
|
||||
|
||||
extension EnvironmentValues {
|
||||
var _navigationLinkStyle: _AnyNavigationLinkStyle {
|
||||
get {
|
||||
self[_NavigationLinkStyleKey.self]
|
||||
}
|
||||
set {
|
||||
self[_NavigationLinkStyleKey.self] = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
public func _navigationLinkStyle<S: _NavigationLinkStyle>(_ style: S) -> some View {
|
||||
environment(\._navigationLinkStyle, _AnyNavigationLinkStyle(style))
|
||||
}
|
||||
}
|
|
@ -19,7 +19,8 @@ public struct NavigationLink<Label, Destination>: View where Label: View, Destin
|
|||
let destination: Destination
|
||||
let label: Label
|
||||
|
||||
@Environment(_navigationDestinationKey) var navigationContext
|
||||
@Environment(\.navigationDestination) var navigationContext
|
||||
@Environment(\._navigationLinkStyle) var style
|
||||
|
||||
public init(destination: Destination, @ViewBuilder label: () -> Label) {
|
||||
self.destination = destination
|
||||
|
@ -46,8 +47,7 @@ extension NavigationLink where Label == Text {
|
|||
/// Creates an instance that presents `destination`, with a `Text` label
|
||||
/// generated from a title string.
|
||||
public init<S>(_ title: S, destination: Destination) where S: StringProtocol {
|
||||
self.destination = destination
|
||||
label = Text(title)
|
||||
self.init(destination: destination) { Text(title) }
|
||||
}
|
||||
|
||||
/// Creates an instance that presents `destination` when active, with a
|
||||
|
@ -71,7 +71,15 @@ public struct _NavigationLinkProxy<Label, Destination> where Label: View, Destin
|
|||
|
||||
public init(_ subject: NavigationLink<Label, Destination>) { self.subject = subject }
|
||||
|
||||
public var label: Label { subject.label }
|
||||
public var label: AnyView {
|
||||
subject.style.makeBody(configuration: .init(body: AnyView(subject.label),
|
||||
isSelected: isSelected))
|
||||
}
|
||||
|
||||
public var style: _AnyNavigationLinkStyle { subject.style }
|
||||
public var isSelected: Bool {
|
||||
true
|
||||
}
|
||||
|
||||
public func activate() {
|
||||
subject.navigationContext!.wrappedValue = AnyView(subject.destination)
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
// limitations under the License.
|
||||
|
||||
import TokamakCore
|
||||
import TokamakStaticHTML
|
||||
|
||||
extension NavigationLink: ViewDeferredToRenderer {
|
||||
public var deferredBody: AnyView {
|
||||
|
@ -20,11 +21,16 @@ extension NavigationLink: ViewDeferredToRenderer {
|
|||
return AnyView(
|
||||
DynamicHTML("a", [
|
||||
"href": "javascript:void%200",
|
||||
"style": proxy.style.type == _SidebarNavigationLinkStyle.self ?
|
||||
"width: 100%; text-decoration: none;"
|
||||
: "",
|
||||
], listeners: [
|
||||
// FIXME: Focus destination or something so assistive
|
||||
// technology knows where to look when clicking the link.
|
||||
"click": { _ in proxy.activate() },
|
||||
]) { proxy.label }
|
||||
]) {
|
||||
proxy.label
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -101,7 +101,8 @@ var links: [NavItem] {
|
|||
Counter(count: Count(value: 5), limit: 15)
|
||||
.padding()
|
||||
.background(Color(red: 0.9, green: 0.9, blue: 0.9, opacity: 1.0))
|
||||
.border(Color.red, width: 3)),
|
||||
.border(Color.red, width: 3)
|
||||
.foregroundColor(.black)),
|
||||
NavItem("ZStack", destination: ZStack {
|
||||
Text("I'm on bottom")
|
||||
Text("I'm forced to the top")
|
||||
|
|
|
@ -77,32 +77,23 @@ public let tokamakStyles = """
|
|||
border-radius: .1em;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme:dark) {
|
||||
._tokamak-buttonstyle-default {
|
||||
background-color: rgb(99, 95, 98);
|
||||
}
|
||||
|
||||
._tokamak-securefield {
|
||||
background-color: rgb(99, 95, 98);
|
||||
color: #FFFFFF;
|
||||
}
|
||||
._tokamak-textfield-default {
|
||||
background-color: rgb(99, 95, 98);
|
||||
color: #FFFFFF;
|
||||
}
|
||||
._tokamak-textfield-roundedborder {
|
||||
background-color: rgb(99, 95, 98);
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
._tokamak-buttonstyle-default,
|
||||
._tokamak-securefield,
|
||||
._tokamak-textfield-default,
|
||||
._tokamak-textfield-roundedborder,
|
||||
._tokamak-picker {
|
||||
background-color: rgb(99, 95, 98);
|
||||
color: #FFFFFF;
|
||||
color-scheme: light dark;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme:dark) {
|
||||
._tokamak-text-redacted::after {
|
||||
background-color: rgb(100, 100, 100);
|
||||
}
|
||||
|
||||
._tokamak-disclosuregroup-chevron {
|
||||
border-right-color: rgba(255, 255, 255, 0.25);
|
||||
border-top-color: rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
|
|
|
@ -90,16 +90,43 @@ extension InsetGroupedListStyle: ListStyleDeferredToRenderer {
|
|||
}
|
||||
|
||||
extension SidebarListStyle: ListStyleDeferredToRenderer {
|
||||
public func listRow<Row>(_ row: Row) -> AnyView where Row: View {
|
||||
AnyView(row)
|
||||
}
|
||||
|
||||
public func listBody<ListBody>(_ content: ListBody) -> AnyView where ListBody: View {
|
||||
AnyView(content
|
||||
.padding(.all)
|
||||
.padding(.leading, 20)
|
||||
._navigationLinkStyle(_SidebarNavigationLinkStyle())
|
||||
.padding([.horizontal, .top], 6)
|
||||
.background(Color._withScheme {
|
||||
switch $0 {
|
||||
case .light: return Color(0xF2F2F7)
|
||||
case .dark: return Color(.sRGB, red: 45 / 255, green: 43 / 255, blue: 48 / 255)
|
||||
case .dark: return Color(0x2D2B30)
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
public struct _SidebarNavigationLinkStyle: _NavigationLinkStyle {
|
||||
@ViewBuilder
|
||||
public func makeBody(configuration: _NavigationLinkStyleConfiguration) -> some View {
|
||||
if configuration.isSelected {
|
||||
configuration
|
||||
.padding(6)
|
||||
.font(.footnote)
|
||||
.background(Color._withScheme {
|
||||
switch $0 {
|
||||
case .light: return Color(.sRGB, white: 0, opacity: 0.1)
|
||||
case .dark: return Color(.sRGB, white: 1, opacity: 0.1)
|
||||
}
|
||||
})
|
||||
.cornerRadius(5)
|
||||
} else {
|
||||
configuration
|
||||
.padding(6)
|
||||
.foregroundColor(.primary)
|
||||
.font(.footnote)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue