Add `TextEditor` implementation (#329)
* Add `TextEditor` implementation Resolves #173. * Clean up and bump requirements in the demo project * Use a single `_tokamak-formcontrol` CSS class * Add missing CSS class to `TextEditor.swift` Co-authored-by: Jed Fox <git@jedfox.com> Co-authored-by: Jed Fox <git@jedfox.com>
This commit is contained in:
parent
302cd3b108
commit
99581929a2
|
@ -43,6 +43,8 @@
|
|||
B5DBA22C24D509B4003D3347 /* RedactDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5DBA22A24D509B4003D3347 /* RedactDemo.swift */; };
|
||||
B5F2BE032571443D00FB3653 /* PreferenceKeyDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F2BE022571443D00FB3653 /* PreferenceKeyDemo.swift */; };
|
||||
B5F2BE042571443D00FB3653 /* PreferenceKeyDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F2BE022571443D00FB3653 /* PreferenceKeyDemo.swift */; };
|
||||
D120FDDB257E7145008FFBAD /* TextEditorDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D120FDDA257E7145008FFBAD /* TextEditorDemo.swift */; };
|
||||
D120FDDC257E7145008FFBAD /* TextEditorDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D120FDDA257E7145008FFBAD /* TextEditorDemo.swift */; };
|
||||
D1B4229024B3B9BB00682F74 /* ListDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1B4228E24B3B9BB00682F74 /* ListDemo.swift */; };
|
||||
D1B4229124B3B9BB00682F74 /* ListDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1B4228E24B3B9BB00682F74 /* ListDemo.swift */; };
|
||||
D1B4229224B3B9BB00682F74 /* OutlineGroupDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1B4228F24B3B9BB00682F74 /* OutlineGroupDemo.swift */; };
|
||||
|
@ -113,6 +115,7 @@
|
|||
B5C76E4924C73ED4003EABB2 /* AppStorageDemo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppStorageDemo.swift; sourceTree = "<group>"; };
|
||||
B5DBA22A24D509B4003D3347 /* RedactDemo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RedactDemo.swift; sourceTree = "<group>"; };
|
||||
B5F2BE022571443D00FB3653 /* PreferenceKeyDemo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreferenceKeyDemo.swift; sourceTree = "<group>"; };
|
||||
D120FDDA257E7145008FFBAD /* TextEditorDemo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextEditorDemo.swift; sourceTree = "<group>"; };
|
||||
D1B4228E24B3B9BB00682F74 /* ListDemo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListDemo.swift; sourceTree = "<group>"; };
|
||||
D1B4228F24B3B9BB00682F74 /* OutlineGroupDemo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutlineGroupDemo.swift; sourceTree = "<group>"; };
|
||||
D1C726F224CB63C6003B576D /* ButtonStyleDemo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ButtonStyleDemo.swift; sourceTree = "<group>"; };
|
||||
|
@ -179,6 +182,7 @@
|
|||
85ED189924AD425E0085DFA0 /* TokamakDemo */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D120FDDA257E7145008FFBAD /* TextEditorDemo.swift */,
|
||||
D1D6B62224D817350041E1D9 /* GeometryReaderDemo.swift */,
|
||||
D1C726F224CB63C6003B576D /* ButtonStyleDemo.swift */,
|
||||
B5C76E4924C73ED4003EABB2 /* AppStorageDemo.swift */,
|
||||
|
@ -354,6 +358,7 @@
|
|||
85ED18A324AD425E0085DFA0 /* SpacerDemo.swift in Sources */,
|
||||
D1B4229024B3B9BB00682F74 /* ListDemo.swift in Sources */,
|
||||
D1EE7EA724C0DD2100C0D127 /* PickerDemo.swift in Sources */,
|
||||
D120FDDB257E7145008FFBAD /* TextEditorDemo.swift in Sources */,
|
||||
B5F2BE032571443D00FB3653 /* PreferenceKeyDemo.swift in Sources */,
|
||||
8500293F24D2FF3E001A2E84 /* SliderDemo.swift in Sources */,
|
||||
85ED18A924AD425E0085DFA0 /* TokamakDemo.swift in Sources */,
|
||||
|
@ -383,6 +388,7 @@
|
|||
85ED18B024AD425E0085DFA0 /* EnvironmentDemo.swift in Sources */,
|
||||
D1B4229124B3B9BB00682F74 /* ListDemo.swift in Sources */,
|
||||
D1EE7EA824C0DD2100C0D127 /* PickerDemo.swift in Sources */,
|
||||
D120FDDC257E7145008FFBAD /* TextEditorDemo.swift in Sources */,
|
||||
B5F2BE042571443D00FB3653 /* PreferenceKeyDemo.swift in Sources */,
|
||||
8500294024D2FF3E001A2E84 /* SliderDemo.swift in Sources */,
|
||||
85ED18B624AD42D70085DFA0 /* NSAppDelegate.swift in Sources */,
|
||||
|
@ -546,7 +552,8 @@
|
|||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_PREVIEWS = YES;
|
||||
INFOPLIST_FILE = "iOS Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
"IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 14.2;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
|
@ -572,7 +579,8 @@
|
|||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_PREVIEWS = YES;
|
||||
INFOPLIST_FILE = "iOS Info.plist";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
|
||||
"IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 14.2;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
|
@ -604,6 +612,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "dev.tokamak.Tokamak-Native";
|
||||
PRODUCT_NAME = "TokamakDemo Native";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
@ -630,6 +639,7 @@
|
|||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "dev.tokamak.Tokamak-Native";
|
||||
PRODUCT_NAME = "TokamakDemo Native";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
|
|
@ -107,7 +107,8 @@ app.
|
|||
|
||||
## Requirements for app developers
|
||||
|
||||
- macOS 10.15 and Xcode 11.4 or later.
|
||||
- macOS 10.15 and Xcode 11.4 or later. macOS 11.0 and Xcode 12.0 or later are required if you're
|
||||
building a multi-platform app with Tokamak that also needs to support SwiftUI on macOS.
|
||||
- [Swift 5.2 or later](https://swift.org/download/) and Ubuntu 18.04 if you'd like to use Linux.
|
||||
Other Linux distributions are currently not supported.
|
||||
|
||||
|
@ -204,7 +205,7 @@ doesn't provide an official build of the extension on the VSCode Marketplace unf
|
|||
|
||||
### Modular structure
|
||||
|
||||
Tokamak is built with modularity in mind, providing a cross-platform `TokamakCore` module and
|
||||
Tokamak is built with modularity in mind, providing a multi-platform `TokamakCore` module and
|
||||
separate modules for platform-specific renderers. Currently, the only available renderer modules are
|
||||
`TokamakDOM` and `TokamakStaticHTML`, the latter can be used for static websites and server-side
|
||||
rendering. If you'd like to implement your own custom renderer, please refer to our [renderers
|
||||
|
|
|
@ -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.
|
||||
|
||||
public struct TextEditor: View {
|
||||
let textBinding: Binding<String>
|
||||
|
||||
public init(text: Binding<String>) {
|
||||
textBinding = text
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
neverBody("TextEditor")
|
||||
}
|
||||
}
|
||||
|
||||
public struct _TextEditorProxy {
|
||||
public let subject: TextEditor
|
||||
|
||||
public init(_ subject: TextEditor) { self.subject = subject }
|
||||
|
||||
public var textBinding: Binding<String> { subject.textBinding }
|
||||
}
|
|
@ -108,6 +108,7 @@ public typealias SecureField = TokamakCore.SecureField
|
|||
public typealias Slider = TokamakCore.Slider
|
||||
public typealias Spacer = TokamakCore.Spacer
|
||||
public typealias Text = TokamakCore.Text
|
||||
public typealias TextEditor = TokamakCore.TextEditor
|
||||
public typealias TextField = TokamakCore.TextField
|
||||
public typealias Toggle = TokamakCore.Toggle
|
||||
public typealias VStack = TokamakCore.VStack
|
||||
|
|
|
@ -21,7 +21,7 @@ extension _PickerContainer: ViewDeferredToRenderer {
|
|||
AnyView(HTML("label") {
|
||||
label
|
||||
Text(" ")
|
||||
DynamicHTML("select", ["class": "_tokamak-picker"], listeners: ["change": {
|
||||
DynamicHTML("select", ["class": "_tokamak-formcontrol"], listeners: ["change": {
|
||||
guard
|
||||
let valueString = $0.target.object!.value.string,
|
||||
let value = Int(valueString) as? SelectionValue
|
||||
|
|
|
@ -24,7 +24,7 @@ extension SecureField: ViewDeferredToRenderer where Label == Text {
|
|||
"type": "password",
|
||||
.value: proxy.textBinding.wrappedValue,
|
||||
"placeholder": proxy.label.rawText,
|
||||
"class": "_tokamak-securefield",
|
||||
"class": "_tokamak-formcontrol",
|
||||
], listeners: [
|
||||
"keypress": { event in if event.key == "Enter" { proxy.onCommit() } },
|
||||
"input": { event in
|
||||
|
|
|
@ -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.
|
||||
|
||||
import TokamakCore
|
||||
|
||||
extension TextEditor: ViewDeferredToRenderer {
|
||||
public var deferredBody: AnyView {
|
||||
let proxy = _TextEditorProxy(self)
|
||||
|
||||
return AnyView(DynamicHTML("textarea", [
|
||||
"class": "_tokamak-formcontrol _tokamak-texteditor",
|
||||
], listeners: [
|
||||
"input": { event in
|
||||
if let newValue = event.target.object?.value.string {
|
||||
proxy.textBinding.wrappedValue = newValue
|
||||
}
|
||||
},
|
||||
]))
|
||||
}
|
||||
}
|
|
@ -32,10 +32,8 @@ extension TextField: ViewDeferredToRenderer where Label == Text {
|
|||
|
||||
func className(for style: TextFieldStyle) -> String {
|
||||
switch style {
|
||||
case is DefaultTextFieldStyle:
|
||||
return "_tokamak-textfield-default"
|
||||
case is RoundedBorderTextFieldStyle:
|
||||
return "_tokamak-textfield-roundedborder"
|
||||
case is DefaultTextFieldStyle, is RoundedBorderTextFieldStyle:
|
||||
return "_tokamak-formcontrol"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
import TokamakShim
|
||||
|
||||
@available(OSX 11.0, iOS 14.0, *)
|
||||
struct AppStorageButtons: View {
|
||||
@AppStorage("count") var count: Int = 0
|
||||
@SceneStorage("count") var sceneCount: Int = 0
|
||||
|
@ -30,7 +29,6 @@ struct AppStorageButtons: View {
|
|||
}
|
||||
}
|
||||
|
||||
@available(OSX 11.0, iOS 14.0, *)
|
||||
struct AppStorageDemo: View {
|
||||
@AppStorage("count") var count: Int = 0
|
||||
@SceneStorage("count") var sceneCount: Int = 0
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
import TokamakShim
|
||||
|
||||
@available(OSX 10.16, iOS 14.0, *)
|
||||
public struct GridDemo: View {
|
||||
public var body: some View {
|
||||
Group {
|
||||
|
|
|
@ -23,7 +23,6 @@ struct File: Identifiable {
|
|||
let children: [File]?
|
||||
}
|
||||
|
||||
@available(OSX 10.16, iOS 14.0, *)
|
||||
struct OutlineGroupDemo: View {
|
||||
let fs: [File] = [
|
||||
.init(id: 0, name: "Users", children: [
|
||||
|
|
|
@ -24,7 +24,6 @@ struct TestPreferenceKey: PreferenceKey {
|
|||
}
|
||||
}
|
||||
|
||||
@available(macOS 11, iOS 14, *)
|
||||
struct PreferenceKeyDemo: View {
|
||||
@State private var testKeyValue: Color = .yellow
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
|
@ -144,7 +143,6 @@ struct PreferenceKeyDemo: View {
|
|||
}
|
||||
}
|
||||
|
||||
@available(macOS 11, iOS 14, *)
|
||||
extension PreferenceKeyDemo.SetColor where Content == EmptyView {
|
||||
init(_ level: Int, _ color: Color) {
|
||||
self.init(level, color) { EmptyView() }
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
import TokamakShim
|
||||
|
||||
@available(OSX 11.0, iOS 14.0, *)
|
||||
struct RedactionDemo: View {
|
||||
func title(_ text: String) -> some View {
|
||||
Group {
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
// 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.
|
||||
|
||||
import TokamakShim
|
||||
|
||||
struct TextEditorDemo: View {
|
||||
@State var text = ""
|
||||
|
||||
var body: some View {
|
||||
Text("Word count: \(text.split(separator: " ").count)")
|
||||
TextEditor(text: $text)
|
||||
.frame(width: 300, height: 300)
|
||||
}
|
||||
}
|
|
@ -128,6 +128,7 @@ struct TokamakDemoView: View {
|
|||
Section(header: Text("Text")) {
|
||||
NavItem("Text", destination: TextDemo())
|
||||
NavItem("TextField", destination: TextFieldDemo())
|
||||
NavItem("TextEditor", destination: TextEditorDemo())
|
||||
}
|
||||
Section(header: Text("Misc")) {
|
||||
NavItem("Path", destination: PathDemo())
|
||||
|
|
|
@ -96,10 +96,7 @@ public let tokamakStyles = """
|
|||
height: 100%;
|
||||
}
|
||||
|
||||
._tokamak-securefield,
|
||||
._tokamak-textfield-default,
|
||||
._tokamak-textfield-roundedborder,
|
||||
._tokamak-picker {
|
||||
._tokamak-formcontrol {
|
||||
color-scheme: light dark;
|
||||
}
|
||||
|
||||
|
@ -107,6 +104,11 @@ public let tokamakStyles = """
|
|||
text-decoration: none;
|
||||
}
|
||||
|
||||
._tokamak-texteditor {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme:dark) {
|
||||
._tokamak-text-redacted::after {
|
||||
background-color: rgb(100, 100, 100);
|
||||
|
|
|
@ -19,7 +19,7 @@ Table columns:
|
|||
| 🚧 | [Text](https://developer.apple.com/documentation/swiftui/text) | |
|
||||
| 🚧 | [TextField](https://developer.apple.com/documentation/swiftui/textfield) | |
|
||||
| 🚧 | [SecureField](https://developer.apple.com/documentation/swiftui/securefield) | |
|
||||
| | [TextEditor](https://developer.apple.com/documentation/swiftui/texteditor) | |
|
||||
| ✅ | [TextEditor](https://developer.apple.com/documentation/swiftui/texteditor) | |
|
||||
|
||||
### Images
|
||||
|
||||
|
@ -51,12 +51,12 @@ Table columns:
|
|||
|
||||
### Value Indicators
|
||||
|
||||
| | | |
|
||||
|--- | ------------------------------------------------------------------------------ | :-: |
|
||||
| | [ProgressView](https://developer.apple.com/documentation/swiftui/progressview) | |
|
||||
| | [Gauge](https://developer.apple.com/documentation/swiftui/gauge) | |
|
||||
| | [Label](https://developer.apple.com/documentation/swiftui/label) | |
|
||||
| ✅ | [Link](https://developer.apple.com/documentation/swiftui/link) | |
|
||||
| | | |
|
||||
| --- | ------------------------------------------------------------------------------ | :-: |
|
||||
| | [ProgressView](https://developer.apple.com/documentation/swiftui/progressview) | |
|
||||
| | [Gauge](https://developer.apple.com/documentation/swiftui/gauge) | |
|
||||
| | [Label](https://developer.apple.com/documentation/swiftui/label) | |
|
||||
| ✅ | [Link](https://developer.apple.com/documentation/swiftui/link) | |
|
||||
|
||||
## View Layout and Presentation
|
||||
|
||||
|
@ -72,11 +72,11 @@ Table columns:
|
|||
|
||||
### Grids
|
||||
|
||||
| | | |
|
||||
| --- | --------------------------------------------------------------------- | :-: |
|
||||
|🚧| [LazyHGrid](https://developer.apple.com/documentation/swiftui/lazyhgrid) | |
|
||||
|🚧| [LazyVGrid](https://developer.apple.com/documentation/swiftui/lazyvgrid) | |
|
||||
|🚧| [GridItem](https://developer.apple.com/documentation/swiftui/griditem) | |
|
||||
| | | |
|
||||
| --- | ------------------------------------------------------------------------ | :-: |
|
||||
| 🚧 | [LazyHGrid](https://developer.apple.com/documentation/swiftui/lazyhgrid) | |
|
||||
| 🚧 | [LazyVGrid](https://developer.apple.com/documentation/swiftui/lazyvgrid) | |
|
||||
| 🚧 | [GridItem](https://developer.apple.com/documentation/swiftui/griditem) | |
|
||||
|
||||
### Lists and Scroll Views
|
||||
|
||||
|
@ -96,14 +96,14 @@ Table columns:
|
|||
| | [Form](https://developer.apple.com/documentation/swiftui/form) | |
|
||||
| ✅ | [Group](https://developer.apple.com/documentation/swiftui/group) | |
|
||||
| | [GroupBox](https://developer.apple.com/documentation/swiftui/groupbox) | |
|
||||
| 🚧 | [Section](https://developer.apple.com/documentation/swiftui/section) | |
|
||||
| 🚧 | [Section](https://developer.apple.com/documentation/swiftui/section) | |
|
||||
|
||||
### Hierarchical Views
|
||||
|
||||
| | | |
|
||||
| --- | --------------------------------------------------------------------------------- | :-: |
|
||||
|🚧| [OutlineGroup](https://developer.apple.com/documentation/swiftui/outlinegroup) | |
|
||||
|🚧| [DisclosureGroup](https://developer.apple.com/documentation/swiftui/disclosuregroup) | |
|
||||
| | | |
|
||||
| --- | ------------------------------------------------------------------------------------ | :-: |
|
||||
| 🚧 | [OutlineGroup](https://developer.apple.com/documentation/swiftui/outlinegroup) | |
|
||||
| 🚧 | [DisclosureGroup](https://developer.apple.com/documentation/swiftui/disclosuregroup) | |
|
||||
|
||||
### Spacers and Dividers
|
||||
|
||||
|
|
Loading…
Reference in New Issue