Style fixes for NavigationLink, DisclosureGroup, Button, TextField, Picker, and more

This commit is contained in:
Carson Katri 2020-08-03 17:07:44 -04:00
parent cdc452b7a5
commit abc59f048a
6 changed files with 138 additions and 31 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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