Add Toggle implementation (#159)
* Fix Button.body * Add support for renderers customizing default environment values * Add ParentView conformances * Add Toggle * long lines * Update Path.swift * Update Path.swift * Update Sources/TokamakDOM/DOMRenderer.swift Co-authored-by: Max Desiatov <max@desiatov.com> * bodyBuild → bodyClosure * Update progress.md * Update progress.md, implement Toggle(_ configuration: ToggleStyleConfiguration) * Fix demo on native * Hopefully fix issue * Hopefully fix issue for real * maybe this will work * Update ToggleDemo.swift * AnyToggleStyle → _AnyToggleStyle * Fix remaining AnyToggleStyle * Clean up unnecessary files * Move typealias to Core.swift * Revert change to ListDemo, remove unused let Co-authored-by: Max Desiatov <max@desiatov.com>
This commit is contained in:
parent
5f3822257d
commit
f0e2b054dc
|
@ -7,6 +7,8 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
854A1A9124B3E3630027BC32 /* ToggleDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CBD5DE24B3BF090066468A /* ToggleDemo.swift */; };
|
||||
854A1A9324B3F28F0027BC32 /* ToggleDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CBD5DE24B3BF090066468A /* ToggleDemo.swift */; };
|
||||
85ED186A24AD38F20085DFA0 /* UIAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85ED186924AD38F20085DFA0 /* UIAppDelegate.swift */; };
|
||||
85ED188A24AD3CD60085DFA0 /* macOS.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 85ED188724AD3CC30085DFA0 /* macOS.storyboard */; };
|
||||
85ED188C24AD3CF10085DFA0 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 85ED188B24AD3CF10085DFA0 /* LaunchScreen.storyboard */; };
|
||||
|
@ -72,6 +74,7 @@
|
|||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
85CBD5DE24B3BF090066468A /* ToggleDemo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToggleDemo.swift; sourceTree = "<group>"; };
|
||||
85ED184A24AD379A0085DFA0 /* TokamakDemo Native.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "TokamakDemo Native.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
85ED185224AD379A0085DFA0 /* TokamakDemo Native.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "TokamakDemo Native.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
85ED186924AD38F20085DFA0 /* UIAppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIAppDelegate.swift; sourceTree = "<group>"; };
|
||||
|
@ -162,6 +165,7 @@
|
|||
85ED189D24AD425E0085DFA0 /* TokamakDemo.swift */,
|
||||
85ED189E24AD425E0085DFA0 /* Counter.swift */,
|
||||
85ED189F24AD425E0085DFA0 /* TextFieldDemo.swift */,
|
||||
85CBD5DE24B3BF090066468A /* ToggleDemo.swift */,
|
||||
85ED18A024AD425E0085DFA0 /* EnvironmentDemo.swift */,
|
||||
B51F214F24B920B400CF2583 /* PathDemo.swift */,
|
||||
B56F22DF24BC89FD001738DF /* ColorDemo.swift */,
|
||||
|
@ -321,6 +325,7 @@
|
|||
85ED18A924AD425E0085DFA0 /* TokamakDemo.swift in Sources */,
|
||||
85ED18AD24AD425E0085DFA0 /* TextFieldDemo.swift in Sources */,
|
||||
85ED18A724AD425E0085DFA0 /* ForEachDemo.swift in Sources */,
|
||||
854A1A9124B3E3630027BC32 /* ToggleDemo.swift in Sources */,
|
||||
85ED18A524AD425E0085DFA0 /* TextDemo.swift in Sources */,
|
||||
85ED18AB24AD425E0085DFA0 /* Counter.swift in Sources */,
|
||||
);
|
||||
|
@ -342,6 +347,7 @@
|
|||
85ED18B624AD42D70085DFA0 /* NSAppDelegate.swift in Sources */,
|
||||
85ED18AC24AD425E0085DFA0 /* Counter.swift in Sources */,
|
||||
85ED18A824AD425E0085DFA0 /* ForEachDemo.swift in Sources */,
|
||||
854A1A9324B3F28F0027BC32 /* ToggleDemo.swift in Sources */,
|
||||
85ED18AE24AD425E0085DFA0 /* TextFieldDemo.swift in Sources */,
|
||||
85ED18A624AD425E0085DFA0 /* TextDemo.swift in Sources */,
|
||||
);
|
||||
|
@ -490,7 +496,7 @@
|
|||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
DEVELOPMENT_TEAM = 288H3WAR3W;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
INFOPLIST_FILE = "iOS Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
|
@ -512,7 +518,7 @@
|
|||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
DEVELOPMENT_TEAM = 288H3WAR3W;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
INFOPLIST_FILE = "iOS Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
|
|
|
@ -86,9 +86,11 @@ public struct Path: Equatable, LosslessStringConvertible {
|
|||
public init(roundedRect rect: CGRect,
|
||||
cornerSize: CGSize,
|
||||
style: RoundedCornerStyle = .circular) {
|
||||
storage = .roundedRect(FixedRoundedRect(rect: rect,
|
||||
cornerSize: cornerSize,
|
||||
style: style))
|
||||
storage = .roundedRect(FixedRoundedRect(
|
||||
rect: rect,
|
||||
cornerSize: cornerSize,
|
||||
style: style
|
||||
))
|
||||
}
|
||||
|
||||
public init(roundedRect rect: CGRect,
|
||||
|
|
|
@ -29,13 +29,14 @@ public final class StackReconciler<R: Renderer> {
|
|||
view: V,
|
||||
target: R.TargetType,
|
||||
renderer: R,
|
||||
environment: EnvironmentValues,
|
||||
scheduler: @escaping (@escaping () -> ()) -> ()
|
||||
) {
|
||||
self.renderer = renderer
|
||||
self.scheduler = scheduler
|
||||
rootTarget = target
|
||||
|
||||
rootView = view.makeMountedView(target, EnvironmentValues())
|
||||
rootView = view.makeMountedView(target, environment)
|
||||
|
||||
rootView.mount(with: self)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
// 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 Jed Fox on 07/04/2020.
|
||||
//
|
||||
// swiftlint:disable line_length
|
||||
// Adapted from https://github.com/SwiftWebUI/SwiftWebUI/blob/16b84d46/Sources/SwiftWebUI/Views/Forms/Toggle.swift
|
||||
// swiftlint:enable line_length
|
||||
//
|
||||
|
||||
// NOTE: ToggleStyleConfiguration.label is supposed to be a special Never View.
|
||||
// It seems like during the rendering process it’s dynamically replaced with the actual label.
|
||||
// That’s complicated so instead we’re providing the label view directly.
|
||||
|
||||
public struct ToggleStyleConfiguration {
|
||||
public let label: AnyView
|
||||
@Binding public var isOn: Swift.Bool
|
||||
}
|
||||
|
||||
public protocol ToggleStyle {
|
||||
associatedtype Body: View
|
||||
|
||||
func makeBody(configuration: Self.Configuration) -> Self.Body
|
||||
|
||||
typealias Configuration = ToggleStyleConfiguration
|
||||
}
|
||||
|
||||
public struct _AnyToggleStyle: ToggleStyle {
|
||||
public typealias Body = AnyView
|
||||
|
||||
private let bodyClosure: (ToggleStyleConfiguration) -> AnyView
|
||||
|
||||
public init<S: ToggleStyle>(_ style: S) {
|
||||
bodyClosure = { configuration in
|
||||
AnyView(style.makeBody(configuration: configuration))
|
||||
}
|
||||
}
|
||||
|
||||
public func makeBody(configuration: ToggleStyleConfiguration) -> AnyView {
|
||||
bodyClosure(configuration)
|
||||
}
|
||||
}
|
||||
|
||||
public enum ToggleStyleKey: EnvironmentKey {
|
||||
public static var defaultValue: _AnyToggleStyle {
|
||||
fatalError("\(self) must have a renderer-provided default value")
|
||||
}
|
||||
}
|
||||
|
||||
extension EnvironmentValues {
|
||||
var toggleStyle: _AnyToggleStyle {
|
||||
get {
|
||||
self[ToggleStyleKey.self]
|
||||
}
|
||||
set {
|
||||
self[ToggleStyleKey.self] = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
public func toggleStyle<S>(_ style: S) -> some View where S: ToggleStyle {
|
||||
environment(\.toggleStyle, _AnyToggleStyle(style))
|
||||
}
|
||||
}
|
|
@ -46,7 +46,7 @@ public struct Button<Label>: View where Label: View {
|
|||
}
|
||||
|
||||
public var body: Never {
|
||||
neverBody("Text")
|
||||
neverBody("Button")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -56,6 +56,12 @@ extension SecureField where Label == Text {
|
|||
}
|
||||
}
|
||||
|
||||
extension SecureField: ParentView {
|
||||
public var children: [AnyView] {
|
||||
(label as? GroupView)?.children ?? [AnyView(label)]
|
||||
}
|
||||
}
|
||||
|
||||
/// This is a helper class that works around absence of "package private" access control in Swift
|
||||
public struct _SecureFieldProxy {
|
||||
public let subject: SecureField<Text>
|
||||
|
|
|
@ -67,6 +67,12 @@ extension TextField where Label == Text {
|
|||
// ) where S : StringProtocol
|
||||
}
|
||||
|
||||
extension TextField: ParentView {
|
||||
public var children: [AnyView] {
|
||||
(label as? GroupView)?.children ?? [AnyView(label)]
|
||||
}
|
||||
}
|
||||
|
||||
/// This is a helper class that works around absence of "package private" access control in Swift
|
||||
public struct _TextFieldProxy {
|
||||
public let subject: TextField<Text>
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright 2018-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 Jed Fox on 07/04/2020.
|
||||
//
|
||||
|
||||
public struct Toggle<Label>: View where Label: View {
|
||||
@Binding var isOn: Bool
|
||||
var label: Label
|
||||
@Environment(\.toggleStyle) var toggleStyle: _AnyToggleStyle
|
||||
|
||||
public init(isOn: Binding<Bool>, label: () -> Label) {
|
||||
_isOn = isOn
|
||||
self.label = label()
|
||||
}
|
||||
|
||||
public var body: AnyView {
|
||||
toggleStyle.makeBody(
|
||||
configuration: ToggleStyleConfiguration(label: AnyView(label), isOn: $isOn)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension Toggle where Label == Text {
|
||||
public init<S>(_ title: S, isOn: Binding<Bool>) where S: StringProtocol {
|
||||
self.init(isOn: isOn) {
|
||||
Text(title)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Toggle where Label == AnyView {
|
||||
public init(_ configuration: ToggleStyleConfiguration) {
|
||||
label = configuration.label
|
||||
_isOn = configuration.$isOn
|
||||
}
|
||||
}
|
||||
|
||||
extension Toggle: ParentView {
|
||||
public var children: [AnyView] {
|
||||
(label as? GroupView)?.children ?? [AnyView(label)]
|
||||
}
|
||||
}
|
|
@ -91,6 +91,7 @@ public typealias SecureField = TokamakCore.SecureField
|
|||
public typealias Spacer = TokamakCore.Spacer
|
||||
public typealias Text = TokamakCore.Text
|
||||
public typealias TextField = TokamakCore.TextField
|
||||
public typealias Toggle = TokamakCore.Toggle
|
||||
public typealias VStack = TokamakCore.VStack
|
||||
public typealias ZStack = TokamakCore.ZStack
|
||||
|
||||
|
|
|
@ -91,10 +91,14 @@ public final class DOMRenderer: Renderer {
|
|||
rootStyle.innerHTML = .string(tokamakStyles)
|
||||
_ = head.appendChild!(rootStyle)
|
||||
|
||||
var environment = EnvironmentValues()
|
||||
environment[ToggleStyleKey] = _AnyToggleStyle(DefaultToggleStyle())
|
||||
|
||||
reconciler = StackReconciler(
|
||||
view: view,
|
||||
target: DOMNode(view, ref),
|
||||
renderer: self
|
||||
renderer: self,
|
||||
environment: environment
|
||||
) { closure in
|
||||
let fn = JSClosure { _ in
|
||||
closure()
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
// 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 Jed Fox on 07/04/2020.
|
||||
//
|
||||
|
||||
import TokamakCore
|
||||
|
||||
public struct DefaultToggleStyle: ToggleStyle {
|
||||
public func makeBody(configuration: Configuration) -> some View {
|
||||
CheckboxToggleStyle().makeBody(configuration: configuration)
|
||||
}
|
||||
}
|
||||
|
||||
public struct CheckboxToggleStyle: ToggleStyle {
|
||||
public func makeBody(configuration: ToggleStyleConfiguration) -> some View {
|
||||
var attrs = ["type": "checkbox"]
|
||||
if configuration.isOn {
|
||||
attrs["checked"] = "checked"
|
||||
}
|
||||
return HTML("label") {
|
||||
HTML("input", attrs, listeners: [
|
||||
"change": { event in
|
||||
let checked = event.target.object?.checked.boolean ?? false
|
||||
configuration.isOn = checked
|
||||
},
|
||||
])
|
||||
configuration.label
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: implement properly
|
||||
public struct SwitchToggleStyle: ToggleStyle {
|
||||
public func makeBody(configuration: Configuration) -> some View {
|
||||
CheckboxToggleStyle().makeBody(configuration: configuration)
|
||||
}
|
||||
}
|
|
@ -18,17 +18,6 @@
|
|||
import TokamakShim
|
||||
|
||||
public struct ListDemo: View {
|
||||
let fs: [File] = [
|
||||
.init(id: 0, name: "Users", children: [
|
||||
.init(id: 1, name: "carson", children: [
|
||||
.init(id: 2, name: "home", children: [
|
||||
.init(id: 3, name: "Documents", children: nil),
|
||||
.init(id: 4, name: "Desktop", children: nil),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]
|
||||
|
||||
public var body: some View {
|
||||
List {
|
||||
ForEach(0..<3) {
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
// 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.
|
||||
|
||||
#if canImport(SwiftUI)
|
||||
import SwiftUI
|
||||
#else
|
||||
import TokamakCore
|
||||
import TokamakDOM
|
||||
#endif
|
||||
|
||||
public struct ToggleDemo: View {
|
||||
@State var checked = false
|
||||
|
||||
public var body: some View {
|
||||
VStack {
|
||||
Toggle("Check me!", isOn: $checked)
|
||||
Toggle(isOn: Binding(get: { true }, set: { _ in })) {
|
||||
Text("I’m always checked!").foregroundColor(.red).italic()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,7 +19,7 @@ import TokamakShim
|
|||
|
||||
struct TokamakDemoView: View {
|
||||
var body: some View {
|
||||
ScrollView(showsIndicators: false) {
|
||||
ScrollView(showsIndicators: true) {
|
||||
HStack {
|
||||
Spacer()
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ struct TokamakDemoView: View {
|
|||
Group {
|
||||
ForEachDemo()
|
||||
TextDemo()
|
||||
ToggleDemo()
|
||||
PathDemo()
|
||||
TextFieldDemo()
|
||||
SpacerDemo()
|
||||
|
|
|
@ -43,7 +43,7 @@ Table columns:
|
|||
|
||||
| | | |
|
||||
| --- | ---------------------------------------------------------------------------- | :-: |
|
||||
| | [Toggle](https://developer.apple.com/documentation/swiftui/toggle) | |
|
||||
| 🚧 | [Toggle](https://developer.apple.com/documentation/swiftui/toggle) | |
|
||||
| 🚧 | [Picker](https://developer.apple.com/documentation/swiftui/picker) | |
|
||||
| | [DatePicker](https://developer.apple.com/documentation/swiftui/datepicker) | |
|
||||
| | [Slider](https://developer.apple.com/documentation/swiftui/slider) | |
|
||||
|
|
Loading…
Reference in New Issue