Add ScrollView (#128)

* Add ScrollView
* Root styles for ScrollView and Stack
This commit is contained in:
Carson Katri 2020-06-30 12:29:06 -04:00 committed by GitHub
parent 0e910756c0
commit 98a107f7fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 178 additions and 18 deletions

View File

@ -0,0 +1,31 @@
// 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 06/29/2020.
//
public enum Axis: Int8, CaseIterable {
case horizontal
case vertical
public struct Set: OptionSet {
public let rawValue: Int8
public init(rawValue: Int8) {
self.rawValue = rawValue
}
public static let horizontal: Axis.Set = .init(rawValue: 1 << 0)
public static let vertical: Axis.Set = .init(rawValue: 1 << 1)
}
}

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 06/29/2020.
//
public struct ScrollView<Content>: View where Content: View {
public let content: Content
public let axes: Axis.Set
public let showsIndicators: Bool
public init(_ axes: Axis.Set = .vertical, showsIndicators: Bool = true, @ViewBuilder content: () -> Content) {
self.axes = axes
self.showsIndicators = showsIndicators
self.content = content()
}
public var body: Never {
neverBody("ScrollView")
}
}
extension ScrollView: ParentView {
public var children: [AnyView] {
(content as? GroupView)?.children ?? [AnyView(content)]
}
}

View File

@ -49,6 +49,8 @@ public final class DOMNode: Target {
}
let log = JSObjectRef.global.console.object!.log.function!
let document = JSObjectRef.global.document.object!
let head = document.head.object!
public final class DOMRenderer: Renderer {
public private(set) var reconciler: StackReconciler<DOMRenderer>?
@ -57,7 +59,12 @@ public final class DOMRenderer: Renderer {
public init<V: View>(_ view: V, _ ref: JSObjectRef) {
rootRef = ref
rootRef.style = "display: flex; width: 100%; height: 100%; justify-content: center; align-items: center;"
rootRef.style = "display: flex; width: 100%; height: 100%; justify-content: center; align-items: center; overflow: hidden;"
let rootStyle = document.createElement!("style").object!
rootStyle.innerHTML = .string(tokamakStyles)
_ = head.appendChild!(rootStyle)
reconciler = StackReconciler(
view: view,
target: DOMNode(view, ref),

View File

@ -32,6 +32,8 @@ extension _FrameLayout: DOMViewModifier {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex-grow: 0;
flex-shrink: 0;
"""]
}
}
@ -48,6 +50,8 @@ extension _FlexFrameLayout: DOMViewModifier {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex-grow: 0;
flex-shrink: 0;
"""]
}
}

View File

@ -0,0 +1,27 @@
// 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.
let tokamakStyles = """
._tokamak-stack > * {
flex-shrink: 0;
}
._tokamak-scrollview-hideindicators {
scrollbar-color: transparent;
scrollbar-width: 0;
}
._tokamak-scrollview-hideindicators::-webkit-scrollbar {
width: 0;
height: 0;
}
"""

View File

@ -39,6 +39,7 @@ extension HStack: ViewDeferredToRenderer, SpacerContainer {
\(hasSpacer ? "width: 100%;" : "")
\(fillCrossAxis ? "height: 100%;" : "")
""",
"class": "_tokamak-stack",
]) { content })
}
}

View File

@ -0,0 +1,46 @@
// 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 06/29/2020.
//
import TokamakCore
public typealias ScrollView = TokamakCore.ScrollView
extension ScrollView: ViewDeferredToRenderer, SpacerContainer {
var axis: SpacerContainerAxis {
if axes.contains(.horizontal) {
return .horizontal
} else {
return .vertical
}
}
public var deferredBody: AnyView {
let scrollX = axes.contains(.horizontal)
let scrollY = axes.contains(.vertical)
return AnyView(HTML("div", [
"style": """
\(scrollX ? "overflow-x: auto; width: 100%;" : "overflow-x: hidden;")
\(scrollY ? "overflow-y: auto; height: 100%;" : "overflow-y: hidden;")
\(fillCrossAxis && scrollX ? "height: 100%;" : "")
\(fillCrossAxis && scrollY ? "width: 100%;" : "")
""",
"class": !showsIndicators ? "_tokamak-scrollview-hideindicators" : "",
]) {
content
})
}
}

View File

@ -39,6 +39,7 @@ extension VStack: ViewDeferredToRenderer, SpacerContainer {
\(hasSpacer ? "height: 100%;" : "")
\(fillCrossAxis ? "width: 100%;" : "")
""",
"class": "_tokamak-stack",
]) { content })
}
}

View File

@ -31,23 +31,28 @@ struct CustomModifier: ViewModifier {
let div = document.createElement!("div").object!
let renderer = DOMRenderer(
VStack {
Counter(count: 5, limit: 15)
ZStack {
Text("I'm on bottom")
Text("I'm forced to the top")
.zIndex(1)
Text("I'm on top")
ScrollView(showsIndicators: false) {
HStack {
Spacer()
}
VStack {
Counter(count: 5, limit: 15)
ZStack {
Text("I'm on bottom")
Text("I'm forced to the top")
.zIndex(1)
Text("I'm on top")
}
.padding(20)
ForEachDemo()
TextDemo()
SVGCircle()
.frame(width: 25, height: 25)
TextFieldDemo()
SpacerDemo()
Spacer()
Text("Forced to bottom.")
}
.padding(20)
ForEachDemo()
TextDemo()
SVGCircle()
.frame(width: 25, height: 25)
TextFieldDemo()
SpacerDemo()
Spacer()
Text("Forced to bottom.")
},
div
)

View File

@ -85,7 +85,7 @@ Table columns:
| --- | ------------------------------------------------------------------------------------------ | :-: |
| | [List](https://developer.apple.com/documentation/swiftui/list) | |
| 🚧 | [ForEach](https://developer.apple.com/documentation/swiftui/foreach) | |
| | [ScrollView](https://developer.apple.com/documentation/swiftui/scrollview) | |
| 🚧 | [ScrollView](https://developer.apple.com/documentation/swiftui/scrollview) | |
| | [ScrollViewReader](https://developer.apple.com/documentation/swiftui/scrollviewreader) | β |
| | [ScrollViewProxy](https://developer.apple.com/documentation/swiftui/scrollviewproxy) | β |
| | [DynamicViewContent](https://developer.apple.com/documentation/swiftui/dynamicviewcontent) | β |