├── .gitignore ├── README.md ├── build.sbt ├── deploy.sh ├── index.html ├── index.js ├── package.json ├── project ├── build.properties └── plugins.sbt ├── src └── main │ └── scala │ └── scalajsreact │ └── template │ ├── ReactApp.scala │ ├── components │ ├── Footer.scala │ ├── LeftNav.scala │ ├── TopNav.scala │ └── items │ │ ├── Item1Data.scala │ │ ├── Item2Data.scala │ │ └── ItemsInfo.scala │ ├── css │ ├── AppCSS.scala │ └── GlobalStyle.scala │ ├── models │ └── Menu.scala │ ├── pages │ ├── HomePage.scala │ └── ItemsPage.scala │ └── routes │ ├── AppRouter.scala │ └── Item.scala ├── webpack.config.js └── webpack.config.min.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *.log 3 | 4 | # sbt specific 5 | .cache/ 6 | .history/ 7 | .lib/ 8 | dist/* 9 | target/ 10 | lib_managed/ 11 | src_managed/ 12 | project/boot/ 13 | project/plugins/project/ 14 | 15 | # Scala-IDE specific 16 | .scala_dependencies 17 | .worksheet 18 | 19 | #Mac 20 | .DS_Store 21 | 22 | #intellij idea 23 | .idea 24 | .idea_modules 25 | 26 | #project 27 | js/* 28 | node_modules/* 29 | build/* -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | scalajs-react-template 2 | ====================== 3 | 4 | Basic skeleton app for scalajs-react and scalacss and webpack 5 | 6 | How To : 7 | 8 | ```js 9 | 10 | npm install 11 | 12 | npm run build 13 | 14 | npm start 15 | 16 | //in new terminal 17 | sbt ~fastOptJS 18 | 19 | //open following url in browser 20 | 21 | http://localhost:8090/webpack-dev-server/ 22 | 23 | 24 | // happy coding :) 25 | 26 | 27 | ``` 28 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | enablePlugins(ScalaJSPlugin) 2 | 3 | name := "scalajs-react-template-webpack" 4 | 5 | version := "1.0" 6 | 7 | scalaVersion := "2.11.6" 8 | 9 | 10 | // create launcher file ( its search for object extends JSApp , make sure there is only one file) 11 | persistLauncher := true 12 | 13 | persistLauncher in Test := false 14 | 15 | val scalaJSReactVersion = "0.9.0" 16 | 17 | val scalaCssVersion = "0.2.0" 18 | 19 | 20 | libraryDependencies ++= Seq("com.github.japgolly.scalajs-react" %%% "core" % scalaJSReactVersion, 21 | "com.github.japgolly.scalajs-react" %%% "extra" % scalaJSReactVersion, 22 | "com.github.japgolly.scalacss" %%% "core" % scalaCssVersion, 23 | "com.github.japgolly.scalacss" %%% "ext-react" % scalaCssVersion) 24 | 25 | 26 | // copy javascript files to js folder,that are generated using fastOptJS/fullOptJS 27 | 28 | crossTarget in (Compile, fullOptJS) := file("build") 29 | 30 | crossTarget in (Compile, fastOptJS) := file("build") 31 | 32 | crossTarget in (Compile, packageScalaJSLauncher) := file("build") 33 | 34 | artifactPath in (Compile, fastOptJS) := ((crossTarget in (Compile, fastOptJS)).value / 35 | ((moduleName in fastOptJS).value + "-opt.js")) 36 | 37 | 38 | 39 | scalacOptions += "-feature" 40 | 41 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | 4 | #Handy script to deploy app to github pages(gh-pages) 5 | 6 | # get comment 7 | comment="$1" 8 | 9 | sbt clean 10 | 11 | sbt fullOptJS 12 | 13 | webpack --config webpack.config.min.js 14 | 15 | if [ "$comment" == "" ]; then 16 | comment="push form CI" 17 | echo "no comment specified to deploy, using default : $comment" 18 | fi 19 | 20 | projectName="scalajs-react-template-webpack" 21 | 22 | ghPagesPath="/Users/chandrasekharkode/Desktop/Kode/Programming/scalaprojects/chandu0101.github.io" 23 | 24 | projectPath=${ghPagesPath}/${projectName} 25 | 26 | mkdir -p ${projectPath}/build 27 | 28 | cp index.html ${projectPath} 29 | 30 | cp build/${projectName}-opt.js ${projectPath}/build/ 31 | 32 | cp build/bundle.min.js ${projectPath}/build/bundle.js 33 | 34 | cp build/${projectName}-launcher.js ${projectPath}/build/ 35 | 36 | cd ${ghPagesPath} 37 | 38 | git add ${projectName} 39 | 40 | git commit -m "$comment" 41 | 42 | git push -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Scalajs-react-template-webpack 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var React = require('react/addons'); 2 | 3 | window.React = React; 4 | 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scalajs-react-template-webpack", 3 | "version": "0.1.0", 4 | "description": "Sample project that uses scalajs-react and scalacss", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/chandu0101/scalajs-react-template-webpack.git" 8 | }, 9 | "scripts": { 10 | "start": "webpack --watch & webpack-dev-server --hot --progress --colors --port 8090", 11 | "build": "webpack --progress --colors" 12 | }, 13 | "devDependencies": { 14 | "node-libs-browser": "^0.5.2", 15 | "react-hot-loader": "^1.2.7", 16 | "webpack": "^1.9.10", 17 | "webpack-dev-server": "^1.9.0" 18 | }, 19 | "dependencies": { 20 | "react": "^0.13.3" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.8 -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.3") 2 | -------------------------------------------------------------------------------- /src/main/scala/scalajsreact/template/ReactApp.scala: -------------------------------------------------------------------------------- 1 | package scalajsreact.template 2 | 3 | import japgolly.scalajs.react._ 4 | import org.scalajs.dom 5 | 6 | import scala.scalajs.js.JSApp 7 | import scala.scalajs.js.annotation.JSExport 8 | import scalajsreact.template.css.AppCSS 9 | import scalajsreact.template.routes.AppRouter 10 | 11 | 12 | object ReactApp extends JSApp { 13 | @JSExport 14 | override def main(): Unit = { 15 | AppCSS.load 16 | AppRouter.router().render(dom.document.body) 17 | } 18 | 19 | } 20 | 21 | -------------------------------------------------------------------------------- /src/main/scala/scalajsreact/template/components/Footer.scala: -------------------------------------------------------------------------------- 1 | package scalajsreact.template.components 2 | 3 | import japgolly.scalajs.react._ 4 | import japgolly.scalajs.react.vdom.prefix_<^._ 5 | 6 | object Footer { 7 | 8 | val component = ReactComponentB.static("Footer", 9 | <.footer(^.textAlign.center, 10 | <.div(^.borderBottom := "1px solid grey", ^.padding := "0px"), 11 | <.p(^.paddingTop := "5px", "Built using scalajs/scalajs-react/scalacss") 12 | ) 13 | ).buildU 14 | 15 | def apply() = component() 16 | } 17 | -------------------------------------------------------------------------------- /src/main/scala/scalajsreact/template/components/LeftNav.scala: -------------------------------------------------------------------------------- 1 | package scalajsreact.template.components 2 | 3 | import japgolly.scalajs.react._ 4 | import japgolly.scalajs.react.extra.Reusability 5 | import japgolly.scalajs.react.extra.router2.RouterCtl 6 | import japgolly.scalajs.react.vdom.prefix_<^._ 7 | 8 | import scala.scalajs.js.{Any, UndefOr} 9 | import scalacss.Defaults._ 10 | import scalacss.ScalaCssReact._ 11 | import scalajsreact.template.routes.Item 12 | 13 | object LeftNav { 14 | 15 | object Style extends StyleSheet.Inline { 16 | 17 | import dsl._ 18 | 19 | val container = style(display.flex, 20 | flexDirection.column, 21 | listStyle := "none", 22 | padding.`0` 23 | ) 24 | 25 | val menuItem = boolStyle(selected => styleS( 26 | lineHeight(48.px), 27 | padding :=! "0 25px", 28 | cursor.pointer, 29 | textDecoration := "none", 30 | mixinIfElse(selected)(color.red, 31 | fontWeight._500) 32 | (color.black, 33 | &.hover(color("#555555".color), 34 | backgroundColor("#ecf0f1".color))) 35 | )) 36 | } 37 | 38 | case class Props(menus: Vector[Item], selectedPage: Item, ctrl: RouterCtl[Item]) 39 | 40 | implicit val currentPageReuse = Reusability.by_==[Item] 41 | implicit val propsReuse = Reusability.by((_: Props).selectedPage) 42 | 43 | val component = ReactComponentB[Props]("LeftNav") 44 | .render(P => { 45 | <.div(Style.container)( 46 | P.menus.map(item => <.a(^.key := item.title, 47 | Style.menuItem(item == P.selectedPage), 48 | item.title, 49 | P.ctrl setOnClick item)) 50 | ) 51 | }) 52 | .configure(Reusability.shouldComponentUpdate) 53 | .build 54 | 55 | 56 | def apply(props: Props, ref: UndefOr[String] = "", key: Any = {}) = component.set(key, ref)(props) 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/scala/scalajsreact/template/components/TopNav.scala: -------------------------------------------------------------------------------- 1 | package scalajsreact.template.components 2 | 3 | import scalajsreact.template.routes.AppRouter 4 | import AppRouter.AppPage 5 | import japgolly.scalajs.react._ 6 | import japgolly.scalajs.react.extra.Reusability 7 | import japgolly.scalajs.react.extra.router2.RouterCtl 8 | import japgolly.scalajs.react.vdom.prefix_<^._ 9 | 10 | import scala.scalajs.js 11 | import scalacss.Defaults._ 12 | import scalacss.ScalaCssReact._ 13 | import scalajsreact.template.routes.AppRouter 14 | import AppRouter.AppPage 15 | import scalajsreact.template.models.Menu 16 | import scalajsreact.template.routes.AppRouter.AppPage 17 | 18 | 19 | object TopNav { 20 | 21 | object Style extends StyleSheet.Inline { 22 | 23 | import dsl._ 24 | 25 | val navMenu = style(display.flex, 26 | alignItems.center, 27 | backgroundColor("#F2706D"), 28 | margin.`0`, 29 | listStyle := "none") 30 | 31 | val menuItem = boolStyle(selected => styleS( 32 | padding(20.px), 33 | fontSize(1.5.em), 34 | cursor.pointer, 35 | color("rgb(244, 233, 233)"), 36 | mixinIfElse(selected)( 37 | backgroundColor("#E8433F".color), 38 | fontWeight._500) 39 | (&.hover( 40 | backgroundColor("#B6413E".color))) 41 | )) 42 | 43 | } 44 | 45 | case class Props(menus: Vector[Menu], selectedPage: AppPage, ctrl: RouterCtl[AppPage]) 46 | 47 | implicit val currentPageReuse = Reusability.by_==[AppPage] 48 | implicit val propsReuse = Reusability.by((_:Props).selectedPage) 49 | 50 | val component = ReactComponentB[Props]("TopNav") 51 | .render((P) => { 52 | <.header( 53 | <.nav( 54 | <.ul(Style.navMenu, 55 | P.menus.map(item => <.li(^.key := item.name, Style.menuItem(item.route == P.selectedPage), item.name, P.ctrl setOnClick item.route))) 56 | )) 57 | }) 58 | .configure(Reusability.shouldComponentUpdate) 59 | .build 60 | 61 | def apply(props: Props, ref: js.UndefOr[String] = "", key: js.Any = {}) = component.set(key, ref)(props) 62 | 63 | } 64 | 65 | 66 | -------------------------------------------------------------------------------- /src/main/scala/scalajsreact/template/components/items/Item1Data.scala: -------------------------------------------------------------------------------- 1 | package scalajsreact.template.components.items 2 | 3 | import japgolly.scalajs.react._ 4 | import japgolly.scalajs.react.vdom.prefix_<^._ 5 | 6 | object Item1Data { 7 | 8 | val component = ReactComponentB.static("Item1", 9 | <.div("This is Item1 Page ") 10 | ).buildU 11 | 12 | def apply() = component() 13 | } 14 | -------------------------------------------------------------------------------- /src/main/scala/scalajsreact/template/components/items/Item2Data.scala: -------------------------------------------------------------------------------- 1 | package scalajsreact.template.components.items 2 | 3 | import japgolly.scalajs.react._ 4 | import japgolly.scalajs.react.vdom.prefix_<^._ 5 | 6 | object Item2Data { 7 | 8 | val component = ReactComponentB.static("Item2", 9 | <.div("This is Item2 Page ") 10 | ).buildU 11 | 12 | def apply() = component() 13 | } 14 | -------------------------------------------------------------------------------- /src/main/scala/scalajsreact/template/components/items/ItemsInfo.scala: -------------------------------------------------------------------------------- 1 | package scalajsreact.template.components.items 2 | 3 | import japgolly.scalajs.react.{ReactComponentB, _} 4 | import japgolly.scalajs.react.vdom.prefix_<^._ 5 | 6 | import scalacss.Defaults._ 7 | import scalacss.ScalaCssReact._ 8 | 9 | object ItemsInfo { 10 | 11 | val component = ReactComponentB.static("ItemsInfo", 12 | <.div(" Items Root Page ") 13 | ).buildU 14 | 15 | def apply() = component() 16 | } 17 | -------------------------------------------------------------------------------- /src/main/scala/scalajsreact/template/css/AppCSS.scala: -------------------------------------------------------------------------------- 1 | package scalajsreact.template.css 2 | 3 | import scalajsreact.template.components.{TopNav, LeftNav} 4 | import scalajsreact.template.pages.{HomePage, ItemsPage} 5 | 6 | import scalacss.ScalaCssReact._ 7 | import scalacss.mutable.GlobalRegistry 8 | import scalacss.Defaults._ 9 | 10 | object AppCSS { 11 | 12 | def load = { 13 | GlobalRegistry.register( 14 | GlobalStyle, 15 | TopNav.Style, 16 | LeftNav.Style, 17 | ItemsPage.Style, 18 | HomePage.Style) 19 | GlobalRegistry.onRegistration(_.addToDocument()) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/scala/scalajsreact/template/css/GlobalStyle.scala: -------------------------------------------------------------------------------- 1 | package scalajsreact.template.css 2 | 3 | import scalacss.Defaults._ 4 | 5 | object GlobalStyle extends StyleSheet.Inline { 6 | 7 | import dsl._ 8 | 9 | style(unsafeRoot("body")( 10 | margin.`0`, 11 | padding.`0`, 12 | fontSize(14.px), 13 | fontFamily := "Roboto, sans-serif" 14 | )) 15 | } 16 | -------------------------------------------------------------------------------- /src/main/scala/scalajsreact/template/models/Menu.scala: -------------------------------------------------------------------------------- 1 | package scalajsreact.template.models 2 | 3 | import scalajsreact.template.routes.AppRouter 4 | import AppRouter.AppPage 5 | 6 | import scalajsreact.template.routes.AppRouter 7 | 8 | case class Menu(name: String, route: AppPage) -------------------------------------------------------------------------------- /src/main/scala/scalajsreact/template/pages/HomePage.scala: -------------------------------------------------------------------------------- 1 | package scalajsreact.template.pages 2 | 3 | import japgolly.scalajs.react._ 4 | import japgolly.scalajs.react.vdom.prefix_<^._ 5 | 6 | import scala.scalajs.js 7 | import scalacss.Defaults._ 8 | import scalacss.ScalaCssReact._ 9 | 10 | object HomePage { 11 | 12 | object Style extends StyleSheet.Inline { 13 | import dsl._ 14 | val content = style(textAlign.center, 15 | fontSize(30.px), 16 | minHeight(450.px), 17 | paddingTop(40.px)) 18 | } 19 | 20 | val component = ReactComponentB.static("HomePage", 21 | <.div(Style.content, "ScalaJS-React Template with Webpack ") 22 | ).buildU 23 | 24 | def apply() = component() 25 | } 26 | -------------------------------------------------------------------------------- /src/main/scala/scalajsreact/template/pages/ItemsPage.scala: -------------------------------------------------------------------------------- 1 | package scalajsreact.template.pages 2 | 3 | import japgolly.scalajs.react._ 4 | import japgolly.scalajs.react.extra.router2.RouterCtl 5 | import japgolly.scalajs.react.vdom.prefix_<^._ 6 | import scala.scalajs.js 7 | import scalacss.Defaults._ 8 | import scalacss.ScalaCssReact._ 9 | import scalajsreact.template.components.LeftNav 10 | import scalajsreact.template.routes.Item 11 | 12 | object ItemsPage { 13 | 14 | object Style extends StyleSheet.Inline { 15 | import dsl._ 16 | val container = style( 17 | display.flex, 18 | minHeight(600.px)) 19 | 20 | val nav = style(width(190.px), 21 | borderRight :=! "1px solid rgb(223, 220, 220)") 22 | 23 | val content = style(padding(30.px)) 24 | } 25 | 26 | val component = ReactComponentB[Props]("ItemsPage") 27 | .render(P => { 28 | <.div(Style.container, 29 | <.div(Style.nav, LeftNav(LeftNav.Props(Item.menu,P.selectedPage,P.ctrl))), 30 | <.div(Style.content, P.selectedPage.render()) 31 | ) 32 | }) 33 | .build 34 | 35 | case class Props(selectedPage : Item,ctrl : RouterCtl[Item]) 36 | 37 | def apply(props : Props,ref: js.UndefOr[String] = "", key: js.Any = {}) = component.set(key, ref)(props) 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/scala/scalajsreact/template/routes/AppRouter.scala: -------------------------------------------------------------------------------- 1 | package scalajsreact.template.routes 2 | 3 | import japgolly.scalajs.react.extra.router2.{Resolution, RouterConfigDsl, RouterCtl, _} 4 | import japgolly.scalajs.react.vdom.prefix_<^._ 5 | 6 | import scalajsreact.template.components.{TopNav, Footer} 7 | import scalajsreact.template.models.Menu 8 | import scalajsreact.template.pages.HomePage 9 | 10 | object AppRouter { 11 | 12 | sealed trait AppPage 13 | 14 | case object Home extends AppPage 15 | case class Items(p : Item) extends AppPage 16 | 17 | 18 | val config = RouterConfigDsl[AppPage].buildConfig { dsl => 19 | import dsl._ 20 | val itemRoutes : Rule = Item.routes.prefixPath_/("#items").pmap[AppPage](Items){ case Items(p) => p} 21 | (trimSlashes 22 | | staticRoute(root, Home) ~> render(HomePage()) 23 | | itemRoutes 24 | ).notFound(redirectToPage(Home)(Redirect.Replace)) 25 | .renderWith(layout) 26 | } 27 | 28 | 29 | val mainMenu = Vector( 30 | Menu("Home",Home), 31 | Menu("Items",Items(Item.Info)) 32 | ) 33 | 34 | 35 | def layout(c: RouterCtl[AppPage], r: Resolution[AppPage]) = { 36 | <.div( 37 | TopNav(TopNav.Props(mainMenu,r.page,c)), 38 | r.render(), 39 | Footer() 40 | ) 41 | } 42 | 43 | val baseUrl = BaseUrl.fromWindowOrigin / "scalajs-react-template-webpack/" 44 | 45 | val router = Router(baseUrl, config) 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/scala/scalajsreact/template/routes/Item.scala: -------------------------------------------------------------------------------- 1 | package scalajsreact.template.routes 2 | 3 | import japgolly.scalajs.react.ReactElement 4 | import japgolly.scalajs.react.extra.router2.RouterConfigDsl 5 | 6 | import scalajsreact.template.components.Footer 7 | import scalajsreact.template.components.items.{ItemsInfo, Item1Data, Item2Data} 8 | import scalajsreact.template.pages.ItemsPage 9 | 10 | sealed abstract class Item(val title: String, 11 | val routerPath: String, 12 | val render: () => ReactElement) 13 | 14 | 15 | object Item { 16 | 17 | case object Info extends Item("Info","info",() => ItemsInfo()) 18 | 19 | case object Item1 extends Item("Item1","item1",() => Item1Data()) 20 | 21 | case object Item2 extends Item("Item2","item2",() => Item2Data()) 22 | 23 | val menu = Vector(Info,Item1,Item2) 24 | 25 | val routes = RouterConfigDsl[Item].buildRule { dsl => 26 | import dsl._ 27 | 28 | menu.map(i => 29 | staticRoute(i.routerPath, i) ~> renderR(r => ItemsPage(props = ItemsPage.Props(i, r))) 30 | ).reduce(_ | _) 31 | 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var webpack = require('webpack'); 4 | 5 | module.exports = { 6 | 7 | entry: [ 8 | 'webpack/hot/only-dev-server', 9 | './index.js' 10 | ], 11 | output: { 12 | path: __dirname + '/build', 13 | publicPath: __dirname + "/build/", 14 | filename: 'bundle.js' 15 | }, 16 | plugins: [ 17 | new webpack.HotModuleReplacementPlugin(), 18 | new webpack.NoErrorsPlugin() 19 | ] 20 | 21 | 22 | }; -------------------------------------------------------------------------------- /webpack.config.min.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var webpack = require('webpack'); 4 | 5 | module.exports = { 6 | 7 | entry: [ 8 | './index.js' 9 | ], 10 | output: { 11 | path: __dirname + '/build', 12 | publicPath: __dirname + "/build/", 13 | filename: 'bundle.min.js' 14 | }, 15 | plugins: [ 16 | new webpack.NoErrorsPlugin(), 17 | new webpack.optimize.UglifyJsPlugin({minimize: true}) 18 | ] 19 | 20 | 21 | }; --------------------------------------------------------------------------------