tokamakui / tokamak Goto Github PK
View Code? Open in Web Editor NEWSwiftUI-compatible framework for building browser apps with WebAssembly and native apps for other platforms
License: Apache License 2.0
SwiftUI-compatible framework for building browser apps with WebAssembly and native apps for other platforms
License: Apache License 2.0
Create command-line util to lint projects that use Tokamak.
Todo:
Linter.md
for linter documentationProps is not Equatable
lintFile -> [LintError]
lintFolder
use lintFile
TokamakLint
types access scope: remove public
from TokenVisitor
, Rule
, Reporter
, PropsIsEquatableRule
TokenVisitor
handling of Range
, start and end are always equal to each other: node.range.startRow = row
node.range.startColumn = column
node.range.endRow = row
node.range.endColumn = column
processToken
, kind
variable is unused: private func processToken(_ token: TokenSyntax) {
var kind = "\(token.tokenKind)"
if let index = kind.firstIndex(of: "(") {
kind = String(kind.prefix(upTo: index))
}
if kind.hasSuffix("Keyword") {
kind = "keyword"
}
column += token.text.count
}
walkAndGrab
, make it private
, remove if node.children.count > 0
conditionSwiftCLI
package can be used in TokamakCLI
moduleTokamakCLI
to tokamak
in Package.swift for Swift 5.0 and 4.2.executable(name: "tokamak", targets: ["TokamakCLI"])
swift-log
packageprint
, implement configurable output for stdout or files with a custom LogHandler
implementation for swift-log
Long-term:
state
The only standard hook currently available is state
, which isn't enough to schedule and finalise side effects from components. Gluon should support hooks.effect
corresponding to useEffect
in React.
Implementation of this component in TokamakUIKit
would need special care to make it work with auto layout. The approach I would try is to copy width constraint of a scroll view and its parent to the root subview. This would automatically do the same what people need to do manually when working with programmatic UIScrollView
and auto layout. We should also provide a flag in props to turn off these additional helper constraints if a user would like to avoid that. We also need to support both vertical and horizontal scrolling and inference of those additional constraints for both cases.
Accessibility labels, hints and traits should be available as Props on all components that render to views
This renderer should use a new simple TestView
class as a target, which stores references to subviews, also directly stores given props and is able to trigger events with a function like TestView.trigger(event: Event)
.
It would make sense to keep this renderer and TestView
class in a separate module/subspec/target. This would make the core module smaller and allow importing the test renderer only in test targets. I imagine GluonTest
is an ok name for the module.
Properties that should be added to Stepper.Props
and be rendered in the UIKit renderer: autorepeat
, wraps
, minimumValue
, maximumValue
, stepValue
, all are documented in UIStepper
docs.
Also would be great to have an API for ListView
style: plain/grouped, which impacts how sections look.
What about ZStack? Have you plans to implements it?
Property contentMode
is missing on struct Style
, although it's available on all UIView
subclasses and is important for Image
component implementation in GluonUIKit
(see #36)
Thanks !
I think the basic stuff should be implementable with CSS transitions, but there may be some limitations. Still worth trying to implement it in my opinion.
In TokamakUIKit
this would be rendered to UITextView
.
AppKit renderer would be a great proof of concept for cross-platform Tokamak and also would make reconciler testing much easier without requiring iOS simulator overhead.
Many views support some kind of .fooStyle()
method that takes one of a variety of XFooStyle()
objects. I took a quick look and it seems like this kind of thing has not been implemented for any of the views. Ideally having one implementation of this would provide a base from which to implement similar methods for other views.
This would control whether a change in value
makes it change visual state with or without animation
This probably would be too big for README.md and we can't auto-generate it yet.
Looks like there is an important property available on subclasses of UIControl
that we need to make available on related control-like host components, namely isEnabled
. It would make sense to add it on EventHandlerProps
since that one is available for props of all control-like components. But then it would also make sense to rename it to ControlProps
as the addition makes its purpose more general.
I've been thinking about CSS-specific styles that one might want to add to further customize generated styles, somewhat similar in principle to the HTML
view that allows injecting arbitrary HTML. I'm still not sure if there should be a modifier that allows adding arbitrary HTML attributes to a rendered HTML node, or specialized modifiers like cssStyle()
for specifying styles and cssClassName()
for specifying a class name.
I also had in mind something like protocol CustomCSSAttributes
which would allow any user to conform their views (or existing views provided by Tokamak) to this protocol, providing their custom global attributes. The use case for this are popular styling libraries (and obviously proprietary design systems that people may use) that have existing CSS code. For example, Semantic UI requires buttons to have ui button
classes on them:
<button class="ui button">
Button
</button>
Tokamak could allow achieving that for all buttons globally like this:
extension Button: CustomCSSAttributes {
var customAttributes: [String: String] { ["class": "ui button"] }
}
The protocol approach and the modifiers approach would not be mutually exclusive, the reasoning is that modifiers would apply tweaks only locally, while protocol conformance would be visible globally. There's some bikeshedding needed here, but overall what are your thoughts?
Don't know if creating a separate issue for this proposal would be more suitable.
This is required for view subscriptions such as onReceive
:
extension View {
public func onReceive<P>(
_ publisher: P,
perform action: @escaping (P.Output) -> ()
) -> some View where P : OpenCombine.Publisher, P.Failure == Never {
SubscriptionView(content: self, publisher: publisher, action: action)
}
}
Just kickstarting a possible discussion here of what you'd like to see in the first release with the SwiftUI API subset supporting WebAssembly. I am personally a fan of "release early and often" idea and wouldn't mind tagging a release with whatever API coverage we already have. It's more about letting people know that this thing exists and it can do basic stuff.
These seem to be release blockers, let me know if you think anything's missing:
progress.md
should be enough for this first release, I guess? We can integrate TokamakDocs somehow in later versions, unless @carson-katri has any objections.NavigationView
and friends, I'll just add some padding and headers to describe what's whatWhat are your thoughts @carson-katri @j-f1?
Not sure if next sibling is correctly passed from the renderer/reconciler to auto layout extensions in TokamakUIKit
, this needs to be verified and fixed if broken. Good to have some demo screens in the Example
project for verification.
Currently there are two components with ValueControlProps
targeting UIControl
: Slider
and SegmentedControl
. It would be great to have more simple components with targets subclassing UIControl
: UIStepper
and UISwitch
. These could be appropriately named Stepper
and Switch
.
The component declaration is already available in core Tokamak
module, but doesn't have any support in UIKitRenderer
This introduces bugs and almost defeats the purpose of compile-time errors. To prevent the program compiling successfully without producing an error for a type like Foo
:
struct Foo: View {
}
There are two options - that I can think of:
Add another _Never conditional conformances - to a dummy type like _Never
that will require disambiguating when omitting the body:
enum _Never: View {
var body: Never { fatalError() }
}
extension View where Body == _Never {
var body: _Never {
fatalError("Can't access body of Never View.")
}
}
That is a hack though and the error ("Type 'H' does not conform to protocol 'Block'") doesn't provide a fix possibly leaving a novice developer confused.
Remove the extension that provides an automatic conformance when Body is of type Never
and move that behaviour to the ViewDeferredToRenderer protocol - or to an underlying view protocol such as _StuctureView, _View, _FatalView:
protocol _StuctureView: View where Body == Never {}
extension _StuctureView {
var body: Never {
fatalError("Can't access body of Structure View.")
}
}
That IMO is the best option, as it avoids automatic type-inference altogether and provides a useful error message - that offers a fix.
Most of Image.Props
should mirror properties of UIImage
, but the actual target would be UIImageView
. Properties that are read-only on UIImage
would need to create a new instance when changed, one prominent example being renderingMode
.
I imagine we could start with Image.Props
looking like this:
public struct Props {
public enum RenderingMode {
// ...
}
public enum Source {
case name(String)
case data(Data)
}
// when changed initializes new image with `UIImage(named:)` or `UIImage(data:)`
public let source: Source
// when changed creates new image with `withRenderingMode`
public let renderingMode: RenderingMode
// when changed initializes new image with given scale
public let scale: Double
// mirrors `flipsForRightToLeftLayoutDirection`
public let flipsForRTL: Bool
}
This would allow Tokamak to create contexts for passing values conforming to Equatable
through deep component trees without needlessly updating all levels of the tree
Shame that there are only two size classes in SwiftUI: compact
and regular
. Also not sure what the default breakpoint for these should be. Even if we decide on one, I think that users should be able redefine it, or maybe even add their own size classes with new breakpoints.
Currently refs passed to nodes won't be initialized during rendering with UIKitRenderer
. It's important to get targets stored in refs to enable usage of those targets in effects.
This is needed for ListView
demo modal in Example
project
Currently the only available auto layout constraint available is Edges.equal
, we need to make all basic auto layout constraints available, working and tested with this new DSL, e.g. Width.equal
, Height.equal
, Leading.equal
etc
For instance, the Text
element inside of EnvironmentDemo
will never get any Environment injected. You can see this by putting the following print after // Inject @Environment values
in StackReconciler.swift
:
print(compositeView.view.type)
The Text
element is never logged.
I'm not sure how to fix this. Do you have any ideas @MaxDesiatov ?
UIView
has a layer
property, which allows a lot of styling customisations, like corner radius, border width etc. Looks like it's best to expose those as properties on struct Style
.
This is going to be a requirement for proper accessibility support. Also, in the betas this type is Comparable
: https://twitter.com/twostraws/status/1278713171344318464
https://twitter.com/vernalkick/status/1278032948101603330
See also, the "More CSS Additions" section of New WebKit Features in Safari 13.1.
In TokamakUIKit
this should be rendered as UICollectionView
This probably could look like Top.equal(to: .safeArea)
in constraints DSL
As both of these components are rendered to UIScrollView
and NSScrollView
subclasses on iOS and macOS respectively, it would make sense to create a structure for props of all of these components to be shared and handled uniformly in the renderers code.
Try to build with Swiftwasm in wasm-DEVELOPMENT-SNAPSHOT-2020-04-07-a
.
Getting following error
.build/checkouts/Runtime/Sources/Runtime/Metadata/TupleMetadata.swift:33:68: error: value of type 'String' has no member 'components' var labels = String(cString: pointer.pointee.labelsString).components(separatedBy: " ")
I find it confusing that UITableView
is named that way in UIKit, while it has no support for columns whatsoever, compare it to NSTableView
in AppKit. I think it would be more fitting to name the corresponding host component in Gluon as ListView
.
UICollectionView
's component could keep that name, it's doing fine π
Currently there's no way to customise priority of a constraint. This probably should be exposed as an optional argument to equal
function, but different approaches could be explored.
Important caveat:
Priorities may not change from nonrequired to required, or from required to nonrequired. An exception will be thrown if a priority of required in macOS or UILayoutPriorityRequired in iOS is changed to a lower priority, or if a lower priority is changed to a required priority after the constraints is added to a view. Changing from one optional priority to another optional priority is allowed even after the constraint is installed on a view.
To provide an escape hatch to UIKit (e.g. for imperative animations) Gluon should support refs and allow binding underlying node targets to references, same as what's available to refs in React
This component should be rendered to UIDatePicker
with UIKitRenderer
. This is a subclass of UIControl
, so it could reuse a lot of infrastructure from UIControlComponent
protocol in GluonUIKit
module.
This would allow Gluon to manage complex state without requiring additional libraries like Redux
It would be great if Tokamak supported rendering to a static HTML string without requiring DOM access, similar to what React already can do (and numerous other JS libraries probably too). There are many use cases for this, the most beneficial would be to pre-render your Tokamak app into a static HTML, serve that by default, and then hydrate that after the WebAssembly binary is loaded. This is beneficial for performance and usability as a user wouldn't need to wait for the WebAssembly module to be loaded and compiled before they can see anything useful on a web page.
Additionally, many static websites (such as blogs) wouldn't need components that utilize @State
, those wouldn't need a WebAssembly binary at all, pre-rendered HTML would be enough. Of course, you could write your HTML by hand, but the abundance of React-based static websites generators (such as Gatsby) proves that it is nice to be able to reuse your existing code. Additionally, views described with Tokamak (even without any @State
and other stateful property wrappers) clearly compose better than plain HTML.
I think this could look like a separate TokamakHTML
module that doesn't depend on JavaScriptKit and could be compiled for any platform, not just WebAssembly. Then you'd supply any view to this HTML renderer and get an HTML string as an output. I hope that TokamakHTML
could then be reused in the TokamakDOM
module.
Hello,
I would like to ask you about confidence of using Tokamak in production applications for e-commerce.
In TokamakUIKit
this would be rendered to UITextField
. This is a subclass of UIControl
, so for the first version TextField
should conform to the UIControlComponent
protocol. Essential props that would be great to have in the initial version are:
textColor
(that uses Tokamak type Color
)textAlignment
(there's probably already an existing enum TextAlignment
from Label
that can be reused here?)placeholder
clearsOnBeginEditing
clearsOnInsertion
borderStyle
clearButtonMode
autocapitalizationType
autocorrectionType
spellCheckingType
keyboardType
keyboardAppearance
returnKeyType
secureTextEntry
all adding new enums to the Tokamak
module where required.
This was just posted on the Swift subreddit. I wonder if the creator would be interesting in doing the reverse of that for TokamakDOM.
If you know of a suitable logo, or know anyone who could produce one, or can propose one yourself, please do. I've used stock images for the previous release with the React API, you can see them on the Twitter profile. If you're not familar with tokamaks, please use the Wikipedia page for inspirations. The whole idea was (and still is) to make a pun on React's atomic logo π
Having this view ready would clean up the demo page and make it easier to add or fix just one view. Itβs probably good to start with the 2-column iPad style and maybe expand later on to an iPhone style on narrower viewports.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. πππ
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google β€οΈ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.