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 19 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
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 {
j-f1 marked this conversation as resolved.
Show resolved Hide resolved
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 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the single defaultValue provided for this key. Thus fatalError is what we get, which needs to change for it to work.

Copy link
Member Author

@j-f1 j-f1 Jul 5, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but the default environment includes a value for the key:

var environment = EnvironmentValues()
environment[ToggleStyleKey] = AnyToggleStyle(DefaultToggleStyle())

https://github.com/swiftwasm/Tokamak/pull/159/files#diff-6f90e40d15b14ddcb32508552c68db47R71

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)]
}
}
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)
}
}
20 changes: 20 additions & 0 deletions Sources/TokamakDOM/Views/Toggle.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// 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 typealias Toggle = TokamakCore.Toggle
j-f1 marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 1 addition & 1 deletion Sources/TokamakDemo/ListDemo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,6 @@ public struct ListDemo: View {
Text("Item: \($0 + 1)")
}
}
}
}.frame(width: 400, height: 300)
j-f1 marked this conversation as resolved.
Show resolved Hide resolved
}
}
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 @@ -23,7 +23,7 @@ import TokamakDOM

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