diff options
author | Christopher Vogt <oss.nsp@cvogt.org> | 2016-06-17 22:38:18 -0400 |
---|---|---|
committer | Christopher Vogt <oss.nsp@cvogt.org> | 2016-06-19 20:57:54 -0400 |
commit | 47cc41ecaa2eaf5677fcb1794db7bbba6533b559 (patch) | |
tree | 8708b8cd5954dfa03f2478b77440340c7664972b /examples/scalajs-react-example | |
parent | 12d5bad267b5759e941b7101578808aeb6351e66 (diff) | |
download | cbt-47cc41ecaa2eaf5677fcb1794db7bbba6533b559.tar.gz cbt-47cc41ecaa2eaf5677fcb1794db7bbba6533b559.tar.bz2 cbt-47cc41ecaa2eaf5677fcb1794db7bbba6533b559.zip |
refactor/simplify scalaJs plugin and example
- Restructure code as 1 library case class and 1 Build mixin trait. A library is generally easier to understand and could be re-used independently. 1 trait seems simpler than several here.
- Let not the plugin create the multi-project build, but the user project manually. I think while this adds some minor code overhead it is much simpler to understand. Fewer tasks and other moving parts needed.
- Remove verbose nested sbt-style folder structure. It's simpler without it :).
Diffstat (limited to 'examples/scalajs-react-example')
-rw-r--r-- | examples/scalajs-react-example/README.md | 11 | ||||
-rw-r--r-- | examples/scalajs-react-example/js/App.scala | 15 | ||||
-rw-r--r-- | examples/scalajs-react-example/js/Pictures.scala | 102 | ||||
-rw-r--r-- | examples/scalajs-react-example/js/build/build.scala | 22 | ||||
-rw-r--r-- | examples/scalajs-react-example/js/build/build/build.scala | 4 | ||||
-rw-r--r-- | examples/scalajs-react-example/jvm/build/build.scala | 6 | ||||
-rw-r--r-- | examples/scalajs-react-example/server/app.js | 39 | ||||
-rw-r--r-- | examples/scalajs-react-example/server/package.json | 14 | ||||
-rw-r--r-- | examples/scalajs-react-example/server/public/index.html | 18 | ||||
-rw-r--r-- | examples/scalajs-react-example/shared/Picture.scala | 3 |
10 files changed, 234 insertions, 0 deletions
diff --git a/examples/scalajs-react-example/README.md b/examples/scalajs-react-example/README.md new file mode 100644 index 0000000..74e015a --- /dev/null +++ b/examples/scalajs-react-example/README.md @@ -0,0 +1,11 @@ + +Compilation instructions +------------------------------------------- +1. `cbt fastOptJS` + +Execution instructions +------------------------------------------- +1. `cd server` +2. `npm install` +3. `node app.js` +4. Go to http://localhost:3000 in a browser
\ No newline at end of file diff --git a/examples/scalajs-react-example/js/App.scala b/examples/scalajs-react-example/js/App.scala new file mode 100644 index 0000000..0cd170e --- /dev/null +++ b/examples/scalajs-react-example/js/App.scala @@ -0,0 +1,15 @@ +package prototype + +import japgolly.scalajs.react.ReactDOM +import org.scalajs.dom + +import scala.scalajs.js.JSApp +import scala.scalajs.js.annotation.JSExport + +@JSExport("App") +object App extends JSApp { + def main(): Unit = { + val doc = dom.document + ReactDOM.render(Pictures.PictureComponent(), doc.getElementById("main")) + } +} diff --git a/examples/scalajs-react-example/js/Pictures.scala b/examples/scalajs-react-example/js/Pictures.scala new file mode 100644 index 0000000..db1d7ef --- /dev/null +++ b/examples/scalajs-react-example/js/Pictures.scala @@ -0,0 +1,102 @@ +package prototype + +import japgolly.scalajs.react.{ReactComponentB, BackendScope, Callback} +import org.scalajs.dom + +import scala.scalajs._ +import japgolly.scalajs.react.vdom.all._ + +import scala.scalajs.js.JSON + +object Pictures { + + case class State(pictures: List[Picture], favourites: List[Picture]) + + type PicClick = (String, Boolean) => Callback + + class Backend($: BackendScope[Unit, State]) { + + def onPicClick(id: String, favorite: Boolean) = + $.state flatMap { s => + if (favorite) { + val newPics = s.pictures.map(p => if (p.id == id) p.copy(favorite = false) else p) + val newFavs = s.favourites.filter(p => p.id != id) + $.modState(_ => State(newPics, newFavs)) + } else { + var newPic: Picture = null + val newPics = s.pictures.map(p => if (p.id == id) { + newPic = p.copy(favorite = true); newPic + } else p) + val newFavs = s.favourites.+:(newPic) + $.modState(_ => State(newPics, newFavs)) + } + } + + def render(s: State) = + div( + h1("Popular Pixabay Pics"), + pictureList((s.pictures, onPicClick)), + h1("Your favorites"), + favoriteList((s.favourites, onPicClick))) + } + + val picture = ReactComponentB[(Picture, PicClick)]("picture") + .render_P { case (p, b) => + div(if (p.favorite) cls := "picture favorite" else cls := "picture", onClick --> b(p.id, p.favorite))( + img(src := p.src, title := p.title) + ) + } + .build + + val pictureList = ReactComponentB[(List[Picture], PicClick)]("pictureList") + .render_P { case (list, b) => + div(`class` := "pictures")( + if (list.isEmpty) span("Loading Pics..") + else { + list.map(p => picture.withKey(p.id)((p, b))) + } + ) + } + .build + + val favoriteList = ReactComponentB[(List[Picture], PicClick)]("favoriteList") + .render_P { case (list, b) => + div(`class` := "favorites")( + if (list.isEmpty) span("Click an image to mark as favorite") + else { + list.map(p => picture.withKey(p.id)((p, b))) + } + ) + } + .build + + val PictureComponent = ReactComponentB[Unit]("PictureComponent") + .initialState(State(Nil, Nil)) + .renderBackend[Backend] + .componentDidMount(scope => Callback { + import scalajs.js.Dynamic.{global => g} + def isDefined(g: js.Dynamic): Boolean = + g.asInstanceOf[js.UndefOr[AnyRef]].isDefined + val url = "http://localhost:3000/data" + val xhr = new dom.XMLHttpRequest() + xhr.open("GET", url) + xhr.onload = { (e: dom.Event) => + if (xhr.status == 200) { + val result = JSON.parse(xhr.responseText) + if (isDefined(result) && isDefined(result.hits)) { + val hits = result.hits.asInstanceOf[js.Array[js.Dynamic]] + val pics = hits.toList.map(item => Picture( + item.id.toString, + item.pageURL.toString, + item.previewURL.toString, + if (item.tags != null) item.tags.asInstanceOf[js.Array[String]].mkString(",") else "")) + //if (item.caption != null) item.caption.text.toString else "")) + scope.modState(_ => State(pics, Nil)).runNow() + } + } + } + xhr.send() + }) + .buildU + +} diff --git a/examples/scalajs-react-example/js/build/build.scala b/examples/scalajs-react-example/js/build/build.scala new file mode 100644 index 0000000..29f1c73 --- /dev/null +++ b/examples/scalajs-react-example/js/build/build.scala @@ -0,0 +1,22 @@ +import cbt._ +class Build(val context: Context) extends ScalaJsBuild{ + override val projectName = "my-project" + + override def sources = super.sources ++ Seq( + projectDirectory.getParentFile ++ "/shared" + ) + + override def dependencies = ( + super.dependencies ++ + Resolver( mavenCentral ).bind( + //"org.scalatest" %%% "scalatest" % "3.0.0-RC2", + "com.github.japgolly.scalajs-react" %%% "core" % "0.10.4", // for example + // for example if you want explicitely state scala version + "org.scala-js" % "scalajs-dom_sjs0.6_2.11" % "0.9.0" + ) + ) + + override protected def fastOptJSFile = { + projectDirectory.getParentFile ++ "/server/public" ++ ("/"++super.fastOptJSFile.getName) + } +} diff --git a/examples/scalajs-react-example/js/build/build/build.scala b/examples/scalajs-react-example/js/build/build/build.scala new file mode 100644 index 0000000..b30e005 --- /dev/null +++ b/examples/scalajs-react-example/js/build/build/build.scala @@ -0,0 +1,4 @@ +import cbt._ +class Build(val context: Context) extends BuildBuild{ + override def dependencies = super.dependencies :+ plugins.scalaJs +} diff --git a/examples/scalajs-react-example/jvm/build/build.scala b/examples/scalajs-react-example/jvm/build/build.scala new file mode 100644 index 0000000..327d705 --- /dev/null +++ b/examples/scalajs-react-example/jvm/build/build.scala @@ -0,0 +1,6 @@ +import cbt._ +class Build(val context: Context) extends BaseBuild{ + override def sources = super.sources ++ Seq( + projectDirectory.getParentFile ++ "/shared" + ) +} diff --git a/examples/scalajs-react-example/server/app.js b/examples/scalajs-react-example/server/app.js new file mode 100644 index 0000000..620c26a --- /dev/null +++ b/examples/scalajs-react-example/server/app.js @@ -0,0 +1,39 @@ +var express = require('express'); +var https = require('https'); + +var app = express(); + +app.get('/data', function(req, res){ + + var request = https.get( + //'https://api.instagram.com/v1/media/popular?client_id=642176ece1e7445e99244cec26f4de1f&callback=?', + 'https://pixabay.com/api/?key=2741116-9706ac6d4a58f2b5416225505&q=yellow+flowers&image_type=photo', + function(response) { + + var body = ""; + response.on('data', function(data) { + body += data; + }); + response.on('end', function() { + console.log(body); + try { + res.send(JSON.parse(body)); + } catch (e) { + return console.error(e); + } + }); + }); + request.on('error', function(e) { + console.log('Problem with request: ' + e.message); + }); + request.end(); +}); + +app.use(express.static(__dirname + '/public')); + +var server = app.listen(3000, function () { + var host = server.address().address; + var port = server.address().port; + + console.log('Example app listening at http://%s:%s', host, port); +}); diff --git a/examples/scalajs-react-example/server/package.json b/examples/scalajs-react-example/server/package.json new file mode 100644 index 0000000..d20ec98 --- /dev/null +++ b/examples/scalajs-react-example/server/package.json @@ -0,0 +1,14 @@ +{ + "name": "server", + "version": "1.0.0", + "description": "prototype", + "main": "app.js", + "author": "Katrin", + "license": "UNLICENSED", + "private": true, + "dependencies": { + "express": "^4.13.3", + "express-ws": "^0.2.6", + "body-parser": "^1.14.1" + } +} diff --git a/examples/scalajs-react-example/server/public/index.html b/examples/scalajs-react-example/server/public/index.html new file mode 100644 index 0000000..08de20d --- /dev/null +++ b/examples/scalajs-react-example/server/public/index.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<html> +<head> + <title>Prototype</title> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> +</head> +<body> + + <div id="main"></div> + + <script src="https://fb.me/react-15.1.0.min.js"></script> + <script src="https://fb.me/react-dom-15.1.0.min.js"></script> + <script type="text/javascript" src="./my-project-fastopt.js"></script> + <script type="text/javascript"> + App().main(); + </script> +</body> +</html> diff --git a/examples/scalajs-react-example/shared/Picture.scala b/examples/scalajs-react-example/shared/Picture.scala new file mode 100644 index 0000000..dbb985a --- /dev/null +++ b/examples/scalajs-react-example/shared/Picture.scala @@ -0,0 +1,3 @@ +package prototype + +case class Picture(id: String, url: String, src: String, title: String, favorite: Boolean = false) |