Add `Toolbar` implementation for HTML renderer (#169)

`Toolbar` is new in SwiftUI. It is coupled fairly closely with `NavigationView`, so this should be integrated with that somehow (#130). It was made similar to macOS which allows more than a leading/trailing `ToolbarItem`.

Resolves #316.
This commit is contained in:
Carson Katri 2021-08-05 11:17:30 -04:00 committed by GitHub
parent a8c6eae94e
commit 21c21cd328
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 929 additions and 35 deletions

View File

@ -23,6 +23,31 @@ public extension View {
navigationTitle(title)
}
@available(
*,
deprecated,
message: "Use navigationTitle(_:) with navigationBarTitleDisplayMode(_:)"
)
func navigationBarTitle(_ title: Text,
displayMode: NavigationBarItem.TitleDisplayMode) -> some View
{
navigationTitle(title)
.navigationBarTitleDisplayMode(displayMode)
}
@available(
*,
deprecated,
message: "Use navigationTitle(_:) with navigationBarTitleDisplayMode(_:)"
)
func navigationBarTitle<S: StringProtocol>(
_ title: S,
displayMode: NavigationBarItem.TitleDisplayMode
) -> some View {
navigationTitle(title)
.navigationBarTitleDisplayMode(displayMode)
}
func navigationTitle(_ title: Text) -> some View {
navigationTitle { title }
}
@ -36,4 +61,10 @@ public extension View {
{
preference(key: NavigationTitleKey.self, value: AnyView(title()))
}
func navigationBarTitleDisplayMode(_ displayMode: NavigationBarItem
.TitleDisplayMode) -> some View
{
preference(key: NavigationBarItemKey.self, value: .init(displayMode: displayMode))
}
}

View File

@ -40,6 +40,14 @@ public struct _PreferenceValue<Key> where Key: PreferenceKey {
}
}
public extension _PreferenceValue {
func _force<V>(
_ transform: @escaping (Key.Value) -> V
) -> _PreferenceReadingView<Key, V> where V: View {
_PreferenceReadingView(value: self, transform: transform)
}
}
public struct _PreferenceStore {
/// The backing values of the `_PreferenceStore`.
private var values: [String: Any]

View File

@ -38,6 +38,20 @@ public struct _DelayedPreferenceView<Key, Content>: View, _PreferenceReadingView
}
}
public struct _PreferenceReadingView<Key, Content>: View where Key: PreferenceKey, Content: View {
public let value: _PreferenceValue<Key>
public let transform: (Key.Value) -> Content
public init(value: _PreferenceValue<Key>, transform: @escaping (Key.Value) -> Content) {
self.value = value
self.transform = transform
}
public var body: some View {
transform(value.value)
}
}
public extension PreferenceKey {
static func _delay<T>(
_ transform: @escaping (_PreferenceValue<Self>) -> T

View File

@ -15,30 +15,60 @@
// Created by Jed Fox on 06/30/2020.
//
public protocol TextFieldStyle {}
public struct _TextFieldStyleLabel: View {
public let body: AnyView
}
public protocol TextFieldStyle: _AnyTextFieldStyle {
associatedtype _Body: View
typealias _Label = _TextFieldStyleLabel
func _body(configuration: TextField<Self._Label>) -> Self._Body
}
public struct DefaultTextFieldStyle: TextFieldStyle {
public init() {}
public func _body(configuration: TextField<_Label>) -> some View {
configuration
}
}
public struct PlainTextFieldStyle: TextFieldStyle {
public init() {}
public func _body(configuration: TextField<_Label>) -> some View {
configuration
}
}
public struct RoundedBorderTextFieldStyle: TextFieldStyle {
public init() {}
public func _body(configuration: TextField<_Label>) -> some View {
configuration
}
}
public struct SquareBorderTextFieldStyle: TextFieldStyle {
public init() {}
public func _body(configuration: TextField<_Label>) -> some View {
configuration
}
}
public protocol _AnyTextFieldStyle {
func _anyBody(configuration: TextField<_TextFieldStyleLabel>) -> AnyView
}
public extension TextFieldStyle {
func _anyBody(configuration: TextField<_TextFieldStyleLabel>) -> AnyView {
.init(_body(configuration: configuration))
}
}
enum TextFieldStyleKey: EnvironmentKey {
static let defaultValue: TextFieldStyle = DefaultTextFieldStyle()
static let defaultValue: _AnyTextFieldStyle = DefaultTextFieldStyle()
}
extension EnvironmentValues {
var textFieldStyle: TextFieldStyle {
var textFieldStyle: _AnyTextFieldStyle {
get {
self[TextFieldStyleKey.self]
}
@ -49,7 +79,7 @@ extension EnvironmentValues {
}
public extension View {
func textFieldStyle(_ style: TextFieldStyle) -> some View {
func textFieldStyle<S>(_ style: S) -> some View where S: TextFieldStyle {
environment(\.textFieldStyle, style)
}
}

View File

@ -0,0 +1,38 @@
// 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 1/19/21.
//
public struct NavigationBarItem: Equatable {
let displayMode: TitleDisplayMode
public enum TitleDisplayMode: Hashable {
case automatic
case inline
case large
}
}
public struct _NavigationBarItemProxy {
let subject: NavigationBarItem
public init(_ subject: NavigationBarItem) {
self.subject = subject
}
public var displayMode: NavigationBarItem.TitleDisplayMode {
subject.displayMode
}
}

View File

@ -29,6 +29,23 @@ public struct NavigationView<Content>: _PrimitiveView where Content: View {
}
}
private struct ToolbarReader<Content>: View where Content: View {
let content: (_ title: AnyView?, _ toolbarContent: [AnyToolbarItem]?) -> Content
var body: some View {
ToolbarKey._delay {
$0._force { bar in
NavigationTitleKey._delay {
$0
._force {
content($0, bar.items.isEmpty && $0 == nil ? nil : bar.items)
}
}
}
}
}
}
/// This is a helper type that works around absence of "package private" access control in Swift
public struct _NavigationViewProxy<Content: View> {
public let subject: NavigationView<Content>
@ -37,6 +54,15 @@ public struct _NavigationViewProxy<Content: View> {
public var context: NavigationContext { subject.context }
/// Builds the content of the `NavigationView` by passing in the title and toolbar if present.
/// If `toolbarContent` is `nil`, you shouldn't render a toolbar.
public func makeToolbar<DeferredBar>(
@ViewBuilder _ content: @escaping (_ title: AnyView?, _ toolbarContent: [AnyToolbarItem]?)
-> DeferredBar
) -> some View where DeferredBar: View {
ToolbarReader(content: content)
}
public var content: some View {
subject.content
.environmentObject(context)
@ -69,3 +95,10 @@ struct NavigationTitleKey: PreferenceKey {
value = nextValue()
}
}
struct NavigationBarItemKey: PreferenceKey {
static let defaultValue: NavigationBarItem = .init(displayMode: .automatic)
static func reduce(value: inout NavigationBarItem, nextValue: () -> NavigationBarItem) {
value = nextValue()
}
}

View File

@ -0,0 +1,62 @@
// 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 7/7/20.
//
struct ToolbarKey: PreferenceKey {
static let defaultValue = ToolbarValue([])
static func reduce(value: inout ToolbarValue, nextValue: () -> ToolbarValue) {
value = nextValue()
}
final class ToolbarValue: Equatable {
let items: [AnyToolbarItem]
init(_ items: [AnyToolbarItem]) {
self.items = items
}
static func == (lhs: ToolbarValue, rhs: ToolbarValue) -> Bool {
lhs === rhs
}
}
}
public extension View {
@_disfavoredOverload
func toolbar<Content>(
@ViewBuilder content: @escaping () -> Content
) -> some View where Content: View {
toolbar {
ToolbarItem(placement: .automatic, content: content)
}
}
func toolbar<Items>(@ToolbarContentBuilder<()> items: () -> ToolbarItemGroup<(), Items>)
-> some View
{
preference(key: ToolbarKey.self, value: ToolbarKey.ToolbarValue(items()._items.compactMap {
$0.view as? AnyToolbarItem
}))
}
func toolbar<Items>(
id: String,
@ToolbarContentBuilder<String> items: () -> ToolbarItemGroup<String, Items>
) -> some View {
preference(key: ToolbarKey.self, value: ToolbarKey.ToolbarValue(items()._items.compactMap {
$0.view as? AnyToolbarItem
}))
}
}

View File

@ -72,3 +72,25 @@ public struct ToolbarItem<ID, Content>: View, AnyToolbarItem where Content: View
content
}
}
public extension ToolbarItem where ID == () {
init(
placement: ToolbarItemPlacement = .automatic,
@ViewBuilder content: () -> Content
) {
self.init(id: (), placement: placement, showsByDefault: true, content: content)
}
}
extension ToolbarItem: Identifiable where ID: Hashable {}
/// This is a helper class that works around absence of "package private" access control in Swift
public struct _ToolbarItemProxy<ID, Content> where Content: View {
public let subject: ToolbarItem<ID, Content>
public init(_ subject: ToolbarItem<ID, Content>) { self.subject = subject }
public var placement: ToolbarItemPlacement { subject.placement }
public var showsByDefault: Bool { subject.showsByDefault }
public var content: Content { subject.content }
}

View File

@ -71,14 +71,14 @@ extension TextField: ParentView {
}
/// This is a helper type that works around absence of "package private" access control in Swift
public struct _TextFieldProxy {
public let subject: TextField<Text>
public struct _TextFieldProxy<Label: View> {
public let subject: TextField<Label>
public init(_ subject: TextField<Text>) { self.subject = subject }
public init(_ subject: TextField<Label>) { self.subject = subject }
public var label: _TextProxy { _TextProxy(subject.label) }
public var label: Label { subject.label }
public var textBinding: Binding<String> { subject.textBinding }
public var onCommit: () -> () { subject.onCommit }
public var onEditingChanged: (Bool) -> () { subject.onEditingChanged }
public var textFieldStyle: TextFieldStyle { subject.style }
public var textFieldStyle: _AnyTextFieldStyle { subject.style }
}

View File

@ -161,6 +161,13 @@ public typealias View = TokamakCore.View
public typealias AnyView = TokamakCore.AnyView
public typealias EmptyView = TokamakCore.EmptyView
// MARK: Toolbars
public typealias ToolbarItem = TokamakCore.ToolbarItem
public typealias ToolbarItemGroup = TokamakCore.ToolbarItemGroup
public typealias ToolbarItemPlacement = TokamakCore.ToolbarItemPlacement
public typealias ToolbarContentBuilder = TokamakCore.ToolbarContentBuilder
// MARK: Text
public typealias TextAlignment = TokamakCore.TextAlignment

View File

@ -18,8 +18,8 @@
import TokamakCore
import TokamakStaticHTML
extension TextField: DOMPrimitive where Label == Text {
func css(for style: TextFieldStyle) -> String {
extension TextField: DOMPrimitive where Label == _TextFieldStyleLabel {
func css(for style: _AnyTextFieldStyle) -> String {
if style is PlainTextFieldStyle {
return """
background: transparent;
@ -30,12 +30,12 @@ extension TextField: DOMPrimitive where Label == Text {
}
}
func className(for style: TextFieldStyle) -> String {
func className(for style: _AnyTextFieldStyle) -> String {
switch style {
case is DefaultTextFieldStyle, is RoundedBorderTextFieldStyle:
return "_tokamak-formcontrol"
default:
return ""
return "_tokamak-formcontrol-reset"
}
}
@ -45,7 +45,9 @@ extension TextField: DOMPrimitive where Label == Text {
return AnyView(DynamicHTML("input", [
"type": proxy.textFieldStyle is RoundedBorderTextFieldStyle ? "search" : "text",
.value: proxy.textBinding.wrappedValue,
"placeholder": proxy.label.rawText,
"placeholder": mapAnyView(proxy.label.body, transform: { (v: Text) in
_TextProxy(v).rawText
}) ?? "",
"style": css(for: proxy.textFieldStyle),
"class": className(for: proxy.textFieldStyle),
], listeners: [

View File

@ -170,7 +170,31 @@ struct TokamakDemoView: View {
title: "Demos"
)
if #available(iOS 14.0, *) {
return AnyView(list.listStyle(SidebarListStyle()))
return AnyView(
list
.listStyle(SidebarListStyle())
.navigationTitle("Tokamak")
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Cancellation Action") {}
}
ToolbarItem(placement: .confirmationAction) {
Button("Confirmation Action") {}
}
ToolbarItem(placement: .destructiveAction) {
Button("Destructive Action") {}
}
ToolbarItem(placement: .navigation) {
Text("Some nav-text")
.italic()
}
ToolbarItem(placement: .status) {
Text("Status: Live")
.bold()
.foregroundColor(.green)
}
}
)
} else {
return AnyView(list)
}

View File

@ -78,11 +78,11 @@ extension TextField: GTKPrimitive where Label == Text {
let proxy = _TextFieldProxy(self)
return AnyView(WidgetView(
build: { _ in
build(textBinding: proxy.textBinding, label: proxy.label)
build(textBinding: proxy.textBinding, label: _TextProxy(proxy.label))
},
update: { a in
guard case let .widget(widget) = a.storage else { return }
update(entry: widget, textBinding: proxy.textBinding, label: proxy.label)
update(entry: widget, textBinding: proxy.textBinding, label: _TextProxy(proxy.label))
}
) {})
}

View File

@ -90,6 +90,13 @@ public typealias View = TokamakCore.View
public typealias AnyView = TokamakCore.AnyView
public typealias EmptyView = TokamakCore.EmptyView
// MARK: Toolbars
public typealias ToolbarItem = TokamakCore.ToolbarItem
public typealias ToolbarItemGroup = TokamakCore.ToolbarItemGroup
public typealias ToolbarItemPlacement = TokamakCore.ToolbarItemPlacement
public typealias ToolbarContentBuilder = TokamakCore.ToolbarContentBuilder
// MARK: App & Scene
public typealias App = TokamakCore.App

View File

@ -123,16 +123,90 @@ public let tokamakStyles = """
width: 100%;
height: 100%;
}
._tokamak-navigationview-content {
._tokamak-navigationview-with-toolbar-content ._tokamak-scrollview {
padding-top: 50px;
}
._tokamak-navigationview-destination {
display: flex; flex-direction: column;
align-items: center; justify-content: center;
flex-grow: 1;
height: 100%;
}
._tokamak-formcontrol {
._tokamak-toolbar {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 50px;
display: flex;
align-items: center;
overflow: hidden;
background: rgba(200, 200, 200, 0.2);
-webkit-backdrop-filter: saturate(180%) blur(20px);
backdrop-filter: saturate(180%) blur(20px);
}
._tokamak-toolbar-content {
flex: 1 1 auto;
display: flex;
height: 100%;
}
._tokamak-toolbar-leading > *, ._tokamak-toolbar-center > * {
margin-right: 8px;
}
._tokamak-toolbar-trailing > * {
margin-left: 8px;
}
._tokamak-toolbar-leading {
margin-right: auto;
align-items: center;
justify-content: flex-start;
padding-left: 16px;
}
._tokamak-toolbar-center {
align-items: center;
justify-content: center;
}
._tokamak-toolbar-trailing {
margin-left: auto;
align-items: center;
justify-content: flex-end;
padding-right: 16px;
}
._tokamak-toolbar-button {
padding: 2px 4px;
border-radius: 3px;
border: 1px solid rgba(0, 0, 0, 0.05);
height: 25px;
padding: 0 8px;
display: flex;
align-items: center;
}
._tokamak-toolbar-button:hover {
border-color: transparent;
background-color: rgba(0, 0, 0, 0.05);
}
._tokamak-toolbar-button:active {
border-color: transparent;
background-color: rgba(0, 0, 0, 0.1);
}
._tokamak-toolbar-textfield input {
padding: 4px 4px 4px 8px;
border-radius: 3px;
background-color: rgba(0, 0, 0, 0.05);
width: 100%;
height: 100%;
}
._tokamak-formcontrol, ._tokamak-formcontrol-reset {
color-scheme: light dark;
}
._tokamak-formcontrol-reset {
background: none;
border: none;
}
._tokamak-link {
text-decoration: none;
@ -160,6 +234,23 @@ public let tokamakStyles = """
border-right-color: rgba(255, 255, 255, 0.25);
border-top-color: rgba(255, 255, 255, 0.25);
}
._tokamak-toolbar {
background: rgba(100, 100, 100, 0.2);
}
._tokamak-toolbar-button {
border: 1px solid rgba(255, 255, 255, 0.1);
}
._tokamak-toolbar-button:hover {
background-color: rgba(255, 255, 255, 0.05);
}
._tokamak-toolbar-button:active {
background-color: rgba(255, 255, 255, 0.1);
}
._tokamak-toolbar-textfield input {
background-color: rgba(255, 255, 255, 0.05);
color: #FFFFFF;
}
}
"""

View File

@ -37,7 +37,8 @@ extension ScrollView: _HTMLPrimitive, SpacerContainer {
\(fillCrossAxis && scrollX ? "height: 100%;" : "")
\(fillCrossAxis && scrollY ? "width: 100%;" : "")
""",
"class": !showsIndicators ? "_tokamak-scrollview-hideindicators" : "",
"class": !showsIndicators ? "_tokamak-scrollview _tokamak-scrollview-hideindicators" :
"_tokamak-scrollview",
]) {
VStack {
content

View File

@ -21,12 +21,81 @@ extension NavigationView: _HTMLPrimitive {
return AnyView(HTML("div", [
"class": "_tokamak-navigationview",
]) {
proxy.content
HTML("div", [
"class": "_tokamak-navigationview-content",
]) {
proxy.destination
proxy.makeToolbar { title, toolbarContent in
if let toolbarContent = toolbarContent {
HTML("div", [
"class": "_tokamak-toolbar",
]) {
Group {
if toolbarContent.isEmpty {
HTML("div", ["class": "_tokamak-toolbar-content _tokamak-toolbar-leading"]) {
title.font(.headline)
}
} else {
HTML("div", ["class": "_tokamak-toolbar-content _tokamak-toolbar-leading"]) {
items(from: toolbarContent, at: .navigationBarLeading)
items(from: toolbarContent, at: .navigation)
title
.font(.headline)
items(from: toolbarContent, at: .navigationBarTrailing)
items(from: toolbarContent, at: .automatic, .primaryAction)
items(from: toolbarContent, at: .destructiveAction)
.foregroundColor(.red)
}
HTML("div", ["class": "_tokamak-toolbar-content _tokamak-toolbar-center"]) {
items(from: toolbarContent, at: .principal, .status)
}
HTML("div", ["class": "_tokamak-toolbar-content _tokamak-toolbar-trailing"]) {
items(from: toolbarContent, at: .cancellationAction)
items(from: toolbarContent, at: .confirmationAction)
.foregroundColor(.accentColor)
}
}
}
.font(.caption)
.buttonStyle(ToolbarButtonStyle())
.textFieldStyle(ToolbarTextFieldStyle())
}
}
HTML("div", [
"class": toolbarContent != nil ? "_tokamak-navigationview-with-toolbar-content" : "",
]) {
proxy.content
}
HTML("div", [
"class": "_tokamak-navigationview-destination",
"style": toolbarContent != nil ? "padding-top: 50px;" : "",
]) {
proxy.destination
}
}
})
}
func items(from items: [AnyToolbarItem], at placements: ToolbarItemPlacement...) -> some View {
ForEach(
Array(items.filter { placements.contains($0.placement) }.enumerated()),
id: \.offset
) { item in
item.element.anyContent
}
}
}
struct ToolbarButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
HTML("div", ["class": "_tokamak-toolbar-button"]) {
configuration.label
.opacity(configuration.isPressed ? 1 : 0.75)
}
}
}
struct ToolbarTextFieldStyle: TextFieldStyle {
func _body(configuration: TextField<_Label>) -> some View {
HTML("div", ["class": "_tokamak-toolbar-textfield"]) {
configuration
}
.frame(height: 27)
}
}

View File

@ -110,16 +110,90 @@
width: 100%;
height: 100%;
}
._tokamak-navigationview-content {
._tokamak-navigationview-with-toolbar-content ._tokamak-scrollview {
padding-top: 50px;
}
._tokamak-navigationview-destination {
display: flex; flex-direction: column;
align-items: center; justify-content: center;
flex-grow: 1;
height: 100%;
}
._tokamak-formcontrol {
._tokamak-toolbar {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 50px;
display: flex;
align-items: center;
overflow: hidden;
background: rgba(200, 200, 200, 0.2);
-webkit-backdrop-filter: saturate(180%) blur(20px);
backdrop-filter: saturate(180%) blur(20px);
}
._tokamak-toolbar-content {
flex: 1 1 auto;
display: flex;
height: 100%;
}
._tokamak-toolbar-leading > *, ._tokamak-toolbar-center > * {
margin-right: 8px;
}
._tokamak-toolbar-trailing > * {
margin-left: 8px;
}
._tokamak-toolbar-leading {
margin-right: auto;
align-items: center;
justify-content: flex-start;
padding-left: 16px;
}
._tokamak-toolbar-center {
align-items: center;
justify-content: center;
}
._tokamak-toolbar-trailing {
margin-left: auto;
align-items: center;
justify-content: flex-end;
padding-right: 16px;
}
._tokamak-toolbar-button {
padding: 2px 4px;
border-radius: 3px;
border: 1px solid rgba(0, 0, 0, 0.05);
height: 25px;
padding: 0 8px;
display: flex;
align-items: center;
}
._tokamak-toolbar-button:hover {
border-color: transparent;
background-color: rgba(0, 0, 0, 0.05);
}
._tokamak-toolbar-button:active {
border-color: transparent;
background-color: rgba(0, 0, 0, 0.1);
}
._tokamak-toolbar-textfield input {
padding: 4px 4px 4px 8px;
border-radius: 3px;
background-color: rgba(0, 0, 0, 0.05);
width: 100%;
height: 100%;
}
._tokamak-formcontrol, ._tokamak-formcontrol-reset {
color-scheme: light dark;
}
._tokamak-formcontrol-reset {
background: none;
border: none;
}
._tokamak-link {
text-decoration: none;
@ -147,6 +221,23 @@
border-right-color: rgba(255, 255, 255, 0.25);
border-top-color: rgba(255, 255, 255, 0.25);
}
._tokamak-toolbar {
background: rgba(100, 100, 100, 0.2);
}
._tokamak-toolbar-button {
border: 1px solid rgba(255, 255, 255, 0.1);
}
._tokamak-toolbar-button:hover {
background-color: rgba(255, 255, 255, 0.05);
}
._tokamak-toolbar-button:active {
background-color: rgba(255, 255, 255, 0.1);
}
._tokamak-toolbar-textfield input {
background-color: rgba(255, 255, 255, 0.05);
color: #FFFFFF;
}
}
</style>
</head>

View File

@ -110,16 +110,90 @@
width: 100%;
height: 100%;
}
._tokamak-navigationview-content {
._tokamak-navigationview-with-toolbar-content ._tokamak-scrollview {
padding-top: 50px;
}
._tokamak-navigationview-destination {
display: flex; flex-direction: column;
align-items: center; justify-content: center;
flex-grow: 1;
height: 100%;
}
._tokamak-formcontrol {
._tokamak-toolbar {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 50px;
display: flex;
align-items: center;
overflow: hidden;
background: rgba(200, 200, 200, 0.2);
-webkit-backdrop-filter: saturate(180%) blur(20px);
backdrop-filter: saturate(180%) blur(20px);
}
._tokamak-toolbar-content {
flex: 1 1 auto;
display: flex;
height: 100%;
}
._tokamak-toolbar-leading > *, ._tokamak-toolbar-center > * {
margin-right: 8px;
}
._tokamak-toolbar-trailing > * {
margin-left: 8px;
}
._tokamak-toolbar-leading {
margin-right: auto;
align-items: center;
justify-content: flex-start;
padding-left: 16px;
}
._tokamak-toolbar-center {
align-items: center;
justify-content: center;
}
._tokamak-toolbar-trailing {
margin-left: auto;
align-items: center;
justify-content: flex-end;
padding-right: 16px;
}
._tokamak-toolbar-button {
padding: 2px 4px;
border-radius: 3px;
border: 1px solid rgba(0, 0, 0, 0.05);
height: 25px;
padding: 0 8px;
display: flex;
align-items: center;
}
._tokamak-toolbar-button:hover {
border-color: transparent;
background-color: rgba(0, 0, 0, 0.05);
}
._tokamak-toolbar-button:active {
border-color: transparent;
background-color: rgba(0, 0, 0, 0.1);
}
._tokamak-toolbar-textfield input {
padding: 4px 4px 4px 8px;
border-radius: 3px;
background-color: rgba(0, 0, 0, 0.05);
width: 100%;
height: 100%;
}
._tokamak-formcontrol, ._tokamak-formcontrol-reset {
color-scheme: light dark;
}
._tokamak-formcontrol-reset {
background: none;
border: none;
}
._tokamak-link {
text-decoration: none;
@ -147,6 +221,23 @@
border-right-color: rgba(255, 255, 255, 0.25);
border-top-color: rgba(255, 255, 255, 0.25);
}
._tokamak-toolbar {
background: rgba(100, 100, 100, 0.2);
}
._tokamak-toolbar-button {
border: 1px solid rgba(255, 255, 255, 0.1);
}
._tokamak-toolbar-button:hover {
background-color: rgba(255, 255, 255, 0.05);
}
._tokamak-toolbar-button:active {
background-color: rgba(255, 255, 255, 0.1);
}
._tokamak-toolbar-textfield input {
background-color: rgba(255, 255, 255, 0.05);
color: #FFFFFF;
}
}
</style>
</head>

View File

@ -110,16 +110,90 @@
width: 100%;
height: 100%;
}
._tokamak-navigationview-content {
._tokamak-navigationview-with-toolbar-content ._tokamak-scrollview {
padding-top: 50px;
}
._tokamak-navigationview-destination {
display: flex; flex-direction: column;
align-items: center; justify-content: center;
flex-grow: 1;
height: 100%;
}
._tokamak-formcontrol {
._tokamak-toolbar {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 50px;
display: flex;
align-items: center;
overflow: hidden;
background: rgba(200, 200, 200, 0.2);
-webkit-backdrop-filter: saturate(180%) blur(20px);
backdrop-filter: saturate(180%) blur(20px);
}
._tokamak-toolbar-content {
flex: 1 1 auto;
display: flex;
height: 100%;
}
._tokamak-toolbar-leading > *, ._tokamak-toolbar-center > * {
margin-right: 8px;
}
._tokamak-toolbar-trailing > * {
margin-left: 8px;
}
._tokamak-toolbar-leading {
margin-right: auto;
align-items: center;
justify-content: flex-start;
padding-left: 16px;
}
._tokamak-toolbar-center {
align-items: center;
justify-content: center;
}
._tokamak-toolbar-trailing {
margin-left: auto;
align-items: center;
justify-content: flex-end;
padding-right: 16px;
}
._tokamak-toolbar-button {
padding: 2px 4px;
border-radius: 3px;
border: 1px solid rgba(0, 0, 0, 0.05);
height: 25px;
padding: 0 8px;
display: flex;
align-items: center;
}
._tokamak-toolbar-button:hover {
border-color: transparent;
background-color: rgba(0, 0, 0, 0.05);
}
._tokamak-toolbar-button:active {
border-color: transparent;
background-color: rgba(0, 0, 0, 0.1);
}
._tokamak-toolbar-textfield input {
padding: 4px 4px 4px 8px;
border-radius: 3px;
background-color: rgba(0, 0, 0, 0.05);
width: 100%;
height: 100%;
}
._tokamak-formcontrol, ._tokamak-formcontrol-reset {
color-scheme: light dark;
}
._tokamak-formcontrol-reset {
background: none;
border: none;
}
._tokamak-link {
text-decoration: none;
@ -147,6 +221,23 @@
border-right-color: rgba(255, 255, 255, 0.25);
border-top-color: rgba(255, 255, 255, 0.25);
}
._tokamak-toolbar {
background: rgba(100, 100, 100, 0.2);
}
._tokamak-toolbar-button {
border: 1px solid rgba(255, 255, 255, 0.1);
}
._tokamak-toolbar-button:hover {
background-color: rgba(255, 255, 255, 0.05);
}
._tokamak-toolbar-button:active {
background-color: rgba(255, 255, 255, 0.1);
}
._tokamak-toolbar-textfield input {
background-color: rgba(255, 255, 255, 0.05);
color: #FFFFFF;
}
}
</style>
</head>

View File

@ -110,16 +110,90 @@
width: 100%;
height: 100%;
}
._tokamak-navigationview-content {
._tokamak-navigationview-with-toolbar-content ._tokamak-scrollview {
padding-top: 50px;
}
._tokamak-navigationview-destination {
display: flex; flex-direction: column;
align-items: center; justify-content: center;
flex-grow: 1;
height: 100%;
}
._tokamak-formcontrol {
._tokamak-toolbar {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 50px;
display: flex;
align-items: center;
overflow: hidden;
background: rgba(200, 200, 200, 0.2);
-webkit-backdrop-filter: saturate(180%) blur(20px);
backdrop-filter: saturate(180%) blur(20px);
}
._tokamak-toolbar-content {
flex: 1 1 auto;
display: flex;
height: 100%;
}
._tokamak-toolbar-leading > *, ._tokamak-toolbar-center > * {
margin-right: 8px;
}
._tokamak-toolbar-trailing > * {
margin-left: 8px;
}
._tokamak-toolbar-leading {
margin-right: auto;
align-items: center;
justify-content: flex-start;
padding-left: 16px;
}
._tokamak-toolbar-center {
align-items: center;
justify-content: center;
}
._tokamak-toolbar-trailing {
margin-left: auto;
align-items: center;
justify-content: flex-end;
padding-right: 16px;
}
._tokamak-toolbar-button {
padding: 2px 4px;
border-radius: 3px;
border: 1px solid rgba(0, 0, 0, 0.05);
height: 25px;
padding: 0 8px;
display: flex;
align-items: center;
}
._tokamak-toolbar-button:hover {
border-color: transparent;
background-color: rgba(0, 0, 0, 0.05);
}
._tokamak-toolbar-button:active {
border-color: transparent;
background-color: rgba(0, 0, 0, 0.1);
}
._tokamak-toolbar-textfield input {
padding: 4px 4px 4px 8px;
border-radius: 3px;
background-color: rgba(0, 0, 0, 0.05);
width: 100%;
height: 100%;
}
._tokamak-formcontrol, ._tokamak-formcontrol-reset {
color-scheme: light dark;
}
._tokamak-formcontrol-reset {
background: none;
border: none;
}
._tokamak-link {
text-decoration: none;
@ -147,6 +221,23 @@
border-right-color: rgba(255, 255, 255, 0.25);
border-top-color: rgba(255, 255, 255, 0.25);
}
._tokamak-toolbar {
background: rgba(100, 100, 100, 0.2);
}
._tokamak-toolbar-button {
border: 1px solid rgba(255, 255, 255, 0.1);
}
._tokamak-toolbar-button:hover {
background-color: rgba(255, 255, 255, 0.05);
}
._tokamak-toolbar-button:active {
background-color: rgba(255, 255, 255, 0.1);
}
._tokamak-toolbar-textfield input {
background-color: rgba(255, 255, 255, 0.05);
color: #FFFFFF;
}
}
</style>
</head>

View File

@ -110,16 +110,90 @@
width: 100%;
height: 100%;
}
._tokamak-navigationview-content {
._tokamak-navigationview-with-toolbar-content ._tokamak-scrollview {
padding-top: 50px;
}
._tokamak-navigationview-destination {
display: flex; flex-direction: column;
align-items: center; justify-content: center;
flex-grow: 1;
height: 100%;
}
._tokamak-formcontrol {
._tokamak-toolbar {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 50px;
display: flex;
align-items: center;
overflow: hidden;
background: rgba(200, 200, 200, 0.2);
-webkit-backdrop-filter: saturate(180%) blur(20px);
backdrop-filter: saturate(180%) blur(20px);
}
._tokamak-toolbar-content {
flex: 1 1 auto;
display: flex;
height: 100%;
}
._tokamak-toolbar-leading > *, ._tokamak-toolbar-center > * {
margin-right: 8px;
}
._tokamak-toolbar-trailing > * {
margin-left: 8px;
}
._tokamak-toolbar-leading {
margin-right: auto;
align-items: center;
justify-content: flex-start;
padding-left: 16px;
}
._tokamak-toolbar-center {
align-items: center;
justify-content: center;
}
._tokamak-toolbar-trailing {
margin-left: auto;
align-items: center;
justify-content: flex-end;
padding-right: 16px;
}
._tokamak-toolbar-button {
padding: 2px 4px;
border-radius: 3px;
border: 1px solid rgba(0, 0, 0, 0.05);
height: 25px;
padding: 0 8px;
display: flex;
align-items: center;
}
._tokamak-toolbar-button:hover {
border-color: transparent;
background-color: rgba(0, 0, 0, 0.05);
}
._tokamak-toolbar-button:active {
border-color: transparent;
background-color: rgba(0, 0, 0, 0.1);
}
._tokamak-toolbar-textfield input {
padding: 4px 4px 4px 8px;
border-radius: 3px;
background-color: rgba(0, 0, 0, 0.05);
width: 100%;
height: 100%;
}
._tokamak-formcontrol, ._tokamak-formcontrol-reset {
color-scheme: light dark;
}
._tokamak-formcontrol-reset {
background: none;
border: none;
}
._tokamak-link {
text-decoration: none;
@ -147,6 +221,23 @@
border-right-color: rgba(255, 255, 255, 0.25);
border-top-color: rgba(255, 255, 255, 0.25);
}
._tokamak-toolbar {
background: rgba(100, 100, 100, 0.2);
}
._tokamak-toolbar-button {
border: 1px solid rgba(255, 255, 255, 0.1);
}
._tokamak-toolbar-button:hover {
background-color: rgba(255, 255, 255, 0.05);
}
._tokamak-toolbar-button:active {
background-color: rgba(255, 255, 255, 0.1);
}
._tokamak-toolbar-textfield input {
background-color: rgba(255, 255, 255, 0.05);
color: #FFFFFF;
}
}
</style>
</head>