Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Toggle implementation #159

Merged
merged 25 commits into from
Jul 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions NativeDemo/TokamakDemo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 */; };
Expand Down Expand Up @@ -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>"; };
Expand Down Expand Up @@ -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 */,
Expand Down Expand Up @@ -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 */,
);
Expand All @@ -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 */,
);
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down
8 changes: 5 additions & 3 deletions Sources/TokamakCore/Shapes/Path.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
3 changes: 2 additions & 1 deletion Sources/TokamakCore/StackReconciler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
76 changes: 76 additions & 0 deletions Sources/TokamakCore/Styles/ToggleStyle.swift
Original file line number Diff line number Diff line change
@@ -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))
}
}
2 changes: 1 addition & 1 deletion Sources/TokamakCore/Views/Button.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public struct Button<Label>: View where Label: View {
}

public var body: Never {
neverBody("Text")
neverBody("Button")
}
}

Expand Down
6 changes: 6 additions & 0 deletions Sources/TokamakCore/Views/SecureField.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
6 changes: 6 additions & 0 deletions Sources/TokamakCore/Views/TextField.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
54 changes: 54 additions & 0 deletions Sources/TokamakCore/Views/Toggle.swift
Original file line number Diff line number Diff line change
@@ -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)]
}
}
1 change: 1 addition & 0 deletions Sources/TokamakDOM/Core.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
6 changes: 5 additions & 1 deletion Sources/TokamakDOM/DOMRenderer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
49 changes: 49 additions & 0 deletions Sources/TokamakDOM/Styles/ToggleStyle.swift
Original file line number Diff line number Diff line change
@@ -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)
}
}
11 changes: 0 additions & 11 deletions Sources/TokamakDemo/ListDemo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,6 @@
import TokamakShim

public struct ListDemo: View {
let fs: [File] = [
j-f1 marked this conversation as resolved.
Show resolved Hide resolved
.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) {
Expand Down
33 changes: 33 additions & 0 deletions Sources/TokamakDemo/ToggleDemo.swift
Original file line number Diff line number Diff line change
@@ -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()
}
}
}
}
3 changes: 2 additions & 1 deletion Sources/TokamakDemo/TokamakDemo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import TokamakShim

struct TokamakDemoView: View {
var body: some View {
ScrollView(showsIndicators: false) {
ScrollView(showsIndicators: true) {
HStack {
Spacer()
}
Expand All @@ -40,6 +40,7 @@ struct TokamakDemoView: View {
Group {
ForEachDemo()
TextDemo()
ToggleDemo()
PathDemo()
TextFieldDemo()
SpacerDemo()
Expand Down
Loading