Skip to content

Commit

Permalink
Removed inlines; fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
aappddeevv committed Mar 22, 2019
1 parent d02d550 commit fe0a073
Show file tree
Hide file tree
Showing 45 changed files with 1,859 additions and 1,149 deletions.
4 changes: 2 additions & 2 deletions README.md
Expand Up @@ -115,11 +115,11 @@ Do not forget to include the react libraries in your execution environment. For
react 16+, the libraries have been split out into multiple libraries. For the
reactjs based modules, the javascript dependencies are:

* core: react, create-react-class (not react-create-class)
* core: react
* react-dom: react-dom

```sh
npm i --save react create-react-class
npm i --save react
npm i --save react-dom
```

Expand Down
2 changes: 2 additions & 0 deletions docs/src/main/tut/docs/native.md
Expand Up @@ -117,6 +117,8 @@ then defining a task. A really great blog on sbt tasks is
[here](https://binx.io/blog/2018/12/08/the-scala-build-tool/), you should read
it just to be smarter about sbt.

Here's a [stackoverflow](https://stackoverflow.com/questions/29371654/scala-js-compilation-destination/29375359#29375359) article on the same topic.

## Calling AppRegistry.registerComponent in scala.js

If your index.js file imports a function that performs the registration you need
Expand Down
19 changes: 11 additions & 8 deletions docs/src/main/tut/docs/router.md
Expand Up @@ -50,13 +50,14 @@ You can route to some hashes by doing something like the following:

didMount = js.defined(self => {
val token = router.watchUrl{ url =>
println(s"Routing to: $url")
url match {
case Some("home") => self.send(Navigate(Home))
case Some("person") => self.send(Navigate(AnotherPage("person")))
case _ => self.send(Navigate(Home))
}
}
// prime the pump!
// ...don't forget this...
self.onUnmount(() => router.unwatchUrl(token)
})

Expand All @@ -83,17 +84,20 @@ You are free to use another routing strategy and plug that into
`RoutingComponent`.

```scala
// ReactionRoutingComponent has a dependency on ReactEvent, so its in the vdom package
import react.vdom._
// ReactionRouting
import react.router.browser._

// App
object Routing {
import ReactionRouting._
// if using sparsetech.trail route matching library
val coolRoute = Root / Arg[String] / Arg[String]
val login = Root / "login"

val config = ReactionConfig(rules(parts => {
// if using sparsetech.trail route matching library
parts.pathname match {
case coolRoute(entityName :: entityId :: HNil) => Render(() => SomeView())
case login(HNil) => Redirect("/login", RedirectMethod.Push)
case coolRoute(entityName, entityId) => Render(() => SomeView(entityName, entityId))
case login(_) => Redirect("/login", RedirectMethod.Push)
case _ => Render(() => UnknownPage(parts))
}
// or do something custom
Expand All @@ -118,5 +122,4 @@ def main() {

You will want to use a real "matching" library like
[trail](https://github.com/sparsetech/trail) to match on the URL and provide
parameterized routing in your config. Note that trail does not provide hash
routing supporting.
"route-to-page" routing processing in your config.
53 changes: 45 additions & 8 deletions docs/src/main/tut/docs/styling.md
Expand Up @@ -4,12 +4,24 @@ title: Styling
---
# Styling

Styling is a topic with many wide-ranging solutions. I suggest you check out the [blog](http://appddeevvmeanderings.blogspot.com/2017/08/web-app-styling-interlude-how-to.html) on how to think about the styling approach that will work for you.
Styling is a topic with many wide-ranging solutions. I suggest you check out the
[blog](http://appddeevvmeanderings.blogspot.com/2017/08/web-app-styling-interlude-how-to.html)
on how to think about the styling approach that will work for you.

Generally, doing styling in the language, e.g. js or scala, allows you to customize the styling while still retaining most levels of performance. Heavy and complex styling will always require a complex process. Check out [ScalaCSS](https://github.com/japgolly/scalacss) or udash's version about scala specific CSS-in-Scala solutions.
Generally, doing styling in the language, e.g. js or scala, allows you to
customize the styling while still retaining most levels of performance. Heavy
and complex styling will always require a complex process. Check out
[ScalaCSS](https://github.com/japgolly/scalacss) or udash's version about scala
specific CSS-in-Scala solutions.

Recommendation: Use a js-based library like uifabric's
[merge-styles](https://github.com/OfficeDev/office-ui-fabric-react/tree/master/packages/merge-styles)
or [glamor (javascript)](https://github.com/threepointone/glamor).

## Inline Styles

If you are looking for something that mildly helps you detect errors, such as bad styling attribute names, you can use vdom's style for inline styles as described in that section. In addition, it also provides inline styles via

```scala
import ttg.react.vdom.style._
val style1 = new StyleAttr { display: "flex" }
Expand All @@ -20,9 +32,17 @@ val flexVertical = merge(style1, style2)
```

## Webpack CSS Processing (external CSS) and js.Dynamic
Assuming you are using webpack or CSS processing, CSS import produces a js.Object whose keys are style names (sometimes mangled) and whose values are styles. css-loader combined with style-loader then translates that into a stylesheet that is dynamically inserted into the DOM.

To use that method, ensure webpack has an alias to the location of your CSS files. I tend to put my CSS files right next to the component and hence my webpack alias "Styles", for example, points to my source directory. Then you can use:
Assuming you are using webpack or CSS processing, CSS import produces a
js.Object whose keys are style names (sometimes mangled) and whose values are
styles. css-loader combined with style-loader then translates that into a
stylesheet that is dynamically inserted into the DOM.

To use that method, ensure webpack has an alias to the location of your CSS
files. I tend to put my CSS files right next to the component and hence my
webpack alias "Styles", for example, points to my source directory. Then you can
use:

```scala
@js.native
@JSImport("Styles/somecomponentdir/component.css", JSImport.Namespace)
Expand All @@ -39,11 +59,28 @@ import styles._
// use them
// cstyles.root or cystyles.input
```
You could use something other than js.Dynamic but that involves potentially alot of typing.

You could use something other than js.Dynamic but that involves potentially alot
of typing.

## CSS in JS/Scala
This is becoming a much more preferred approach when the processing power of your client allows to do this. While pre-processed and smartly bundled CSS can give you load flexbility and performance benefits, the flexibility and sometimes negligible impact on performance that current solutions provide make this more popular.

There are some pure scala solutions available such as [ScalaCSS](https://github.com/japgolly/scalacss) as well as [udash](https://udash.io/) both of have CSS in Scala solutions, you have many choices include [glamor (javascript)](https://github.com/threepointone/glamor) and something very similar such as fabric's styling ([merge-styles](https://github.com/OfficeDev/office-ui-fabric-react/tree/master/packages/merge-styles)). fabric's merge-styles claims to be slightly smaller on the payload than glamor.
This is becoming a much more preferred approach when the processing power of
your client allows to do this. While pre-processed and smartly bundled CSS can
give you load flexbility and performance benefits, the flexibility and sometimes
negligible impact on performance that current solutions provide make this more
popular.

There are some pure scala solutions available such as
[ScalaCSS](https://github.com/japgolly/scalacss) as well as
[udash](https://udash.io/) both of have CSS in Scala solutions, you have many
choices include [glamor (javascript)](https://github.com/threepointone/glamor)
and something very similar such as fabric's styling
([merge-styles](https://github.com/OfficeDev/office-ui-fabric-react/tree/master/packages/merge-styles)). fabric's
merge-styles claims to be slightly smaller on the payload than glamor.

You can use this style of style processing but if you do, you want to consider defining your styles in JS and importing them as JS data. Then use a facade over a CSS in JS library to add them once you have manipulated them as needed. You do not gain type-safety with this approach but if you use typescript bindings in typescript code, its probably good enough.
You can use this style of style processing but if you do, you want to consider
defining your styles in JS and importing them as JS data. Then use a facade over
a CSS in JS library to add them once you have manipulated them as needed. You do
not gain type-safety with this approach but if you use typescript bindings in
typescript code, its probably good enough.
17 changes: 15 additions & 2 deletions scalajs-reaction-core/src/main/scala/Proxy.scala
Expand Up @@ -8,11 +8,14 @@ package react
import scala.scalajs.js

/**
* Data structure used to create js side react class. These are the lifecycle
* Data structure used to create js-side react class. These are the lifecycle
* react APIs in use. Most of the methods below recover the scala side
* "element" (the component spec) and then forwards the respective call to the
* scala side "element." The same proxy is used for creating each component
* instances for all component instances of the same component type.
* instances for all component instances of the same component type. Not all
* methods are relevant though e.g. stateless vs statefull components and
* getInitialState. Setting all React lifecycle methods to undefined allows
* Reactjs to optimize itself. The proxy is used in createReactClass.
*/
trait Proxy[SLF, State, ThisSelfProps, ThisSelf] extends js.Object {
val displayName: String
Expand Down Expand Up @@ -51,4 +54,14 @@ trait Proxy[SLF, State, ThisSelfProps, ThisSelf] extends js.Object {
/** react js method. v16+ */
val componentDidCatch: js.UndefOr[js.ThisFunction2[ThisSelf, js.Error, ErrorInfo, Unit]] =
js.undefined

/** react js method. v16+. Allows us to remove willReceiveProps and
* retainedProps. Props come from the apply()/make() fuction of the component
* not from traditional reactjs props, which should be ignored.
*/
val statics: js.UndefOr[Statics[SLF, State, ThisSelfProps, ThisSelf]] = js.undefined
}

trait Statics[SLF, State, ThisSelfProps, ThisSelf] extends js.Object {
val getDerivedStateFromProps: js.UndefOr[js.ThisFunction1[ThisSelf, js.Object, State]] = js.undefined
}
19 changes: 19 additions & 0 deletions scalajs-reaction-core/src/main/scala/WeakMap.scala
@@ -0,0 +1,19 @@
// Copyright (c) 2018 The Trapelo Group LLC
// This software is licensed under the MIT License (MIT).
// For more information see LICENSE or https://opensource.org/licenses/MIT

package ttg
package react

import scala.scalajs.js
import js.annotation._

@js.native
@JSGlobal
class WeakMap extends js.Object {
// returned undefind if not present
def get(k: js.Object): js.UndefOr[js.Any] = js.native
def set(k: js.Object, v: js.Any): js.Any = js.native
def has(k: js.Object): Boolean = js.native
def delete(k: js.Object): Unit = js.native
}
3 changes: 2 additions & 1 deletion scalajs-reaction-core/src/main/scala/context.scala
Expand Up @@ -14,7 +14,8 @@ import js.Dynamic.{literal => lit}
* is not included in the standard syntax module. Not using an "optional"
* concept in the below forces a zero to be created for every element you want
* to pass in the context, which may be burdonsome. Considering using
* js.UndefOr or Option as a wrapper around your value.
* js.UndefOr or Option as a wrapper around your value or values within your
* context object.
*/
object context {

Expand Down
64 changes: 64 additions & 0 deletions scalajs-reaction-core/src/main/scala/createClass.scala
@@ -0,0 +1,64 @@
// Copyright (c) 2018 The Trapelo Group LLC
// This software is licensed under the MIT License (MIT).
// For more information see LICENSE or https://opensource.org/licenses/MIT

package ttg
package react

import collection.mutable
import scala.scalajs.js

object createClass {

val statics = Map(
"getDerivedStateFromProps"->true,
"displayName" -> "true"
)

val lifecycle = Map(
"componentDidMount" -> true,
"componentDidUpdate" -> true,
"componentWillUpdate" -> true,
"shouldComponentUpdate" -> true,
"componentDidCatch" -> true,
"componentWillUnmount" -> true,
"render" -> true
)

/**
* Create a function that creates a class using tricks on Component. This
* avoids react-create-class and will always be more up to date.
*/
def apply(spec: js.Object): ReactClass = {
val prototype = js.Object.create(ReactJS.Component.prototype.asInstanceOf[js.Object])
val specKeys = js.Object.keys(spec)
lazy val CreateClassComponent: js.ThisFunction3[js.Dynamic,js.Object,js.Object,js.Object,Unit] = (t,a,b,c) => {
// init this object using the "superclass" function call but but swap out the this parameter
ReactJS.Component.call(t, a, b, c)
// simulate setting state as if this was the constructor method
t.state = if(js.isUndefined(t.getInitialState)) null else t.getInitialState()
// auto-bind
val cp = CreateClassComponent.asInstanceOf[js.Dynamic].prototype
// autobind, need Array.forEach directly, not in scala.js API
js.Object.keys(cp.asInstanceOf[js.Object]).asInstanceOf[js.Dynamic].forEach(((th,key) => {
if(js.typeOf(th.key) == "function" && !lifecycle.contains(key))
th.key = th.key.bind(t)
}):js.ThisFunction1[js.Dynamic, String, Unit], t)
}
// add spec methods
for(key <- specKeys) {
val specValue = spec.asInstanceOf[js.Dynamic].selectDynamic(key)
if(statics.contains(key))
CreateClassComponent.asInstanceOf[js.Dynamic].updateDynamic(key)(specValue)
else
prototype.asInstanceOf[js.Dynamic].updateDynamic(key)(specValue)
}
// reswizzle to be a react Component
val ComponentDyn = CreateClassComponent.asInstanceOf[js.Dynamic]
ComponentDyn.prototype = prototype
ComponentDyn.prototype.constructor = CreateClassComponent
ComponentDyn.__defined = false
CreateClassComponent.asInstanceOf[ReactClass]
}

}
45 changes: 25 additions & 20 deletions scalajs-reaction-core/src/main/scala/elements.scala
Expand Up @@ -69,7 +69,7 @@ object elements {
o.asInstanceOf[js.Dynamic]

/** Stateless component. It only has a render function. */
def statelessComponent(debugNameArg: String, disableCatch: Boolean = true) = {
def statelessComponent(debugNameArg: String, disableCatch: Boolean = false) = {
new StatelessComponentCake {
type ProxyType = ProxyLike
type ComponentType = ComponentLike
Expand All @@ -83,30 +83,35 @@ object elements {
trait ComponentLike extends super.ComponentLike
val component = new ComponentType {
var debugName = debugNameArg
var reactClassInternal = reactCreateClass(proxy)
var reactClassInternal = createClass(proxy)//reactCreateClass(proxy)
}
}
}

//def statelessComponent(debugNameArg: String, disableCatch: Boolean = false) = {
// StatelessComponent.make(debugNameArg)
//}


// /** Stateless, with retained props. */
def statelessComponentWithRetainedProps[RetainedProps](debugNameArg: String, disableCatch: Boolean = true) =
new StatelessComponentWithRetainedPropsCake {
type RP = RetainedProps
type ProxyType = ProxyLike
type ComponentType = ComponentLike
// def statelessComponentWithRetainedProps[RetainedProps](debugNameArg: String, disableCatch: Boolean = true) =
// new StatelessComponentWithRetainedPropsCake {
// type RP = RetainedProps
// type ProxyType = ProxyLike
// type ComponentType = ComponentLike

class ProxyLike extends super.ProxyLike {
val displayName: String = debugNameArg
}
val proxy = new ProxyType()
// hack
if(disableCatch) proxy.asInstanceOf[js.Dynamic].componentDidCatch = js.undefined
trait ComponentLike extends super.ComponentLike
val component = new ComponentType {
var debugName = debugNameArg
var reactClassInternal = reactCreateClass(proxy)
}
}
// class ProxyLike extends super.ProxyLike {
// val displayName: String = debugNameArg
// }
// val proxy = new ProxyType()
// // hack
// if(disableCatch) proxy.asInstanceOf[js.Dynamic].componentDidCatch = js.undefined
// trait ComponentLike extends super.ComponentLike
// val component = new ComponentType {
// var debugName = debugNameArg
// var reactClassInternal = reactCreateClass(proxy)
// }
// }

/** Stateful. */
def reducerComponent[TheState, Action](debugNameArg: String, disableCatch: Boolean = false) =
Expand All @@ -125,7 +130,7 @@ object elements {
trait ComponentLike extends super.ComponentLike
val component = new ComponentType {
var debugName = debugNameArg
var reactClassInternal = reactCreateClass(proxy)
var reactClassInternal = createClass(proxy)//reactCreateClass(proxy)
}
}

Expand Down

0 comments on commit fe0a073

Please sign in to comment.