Skip to content

Instantly share code, notes, and snippets.

@benjaminjackman
Created October 20, 2014 14:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save benjaminjackman/5b0b5d853c1bccc7b7a1 to your computer and use it in GitHub Desktop.
Save benjaminjackman/5b0b5d853c1bccc7b7a1 to your computer and use it in GitHub Desktop.
ScalaJs React Examples
class GlobalView(implicit val pc: PoeCacher) extends View {
override def start(jq: JQuery): Unit = {
val el = jq.get(0).asInstanceOf[dom.Element]
val root = GlobalViewWidget()(pc)
React.renderComponent(root, el)
}
override def stop(): Unit = {
}
}
object GlobalViewWidget {
class Component(
pc: PoeCacher,
lootWatcher: LootWatcher
) {
case class State(
league: Option[League],
character: Option[String],
autowatch: Boolean = false,
refreshIntervalSec: Int = 30
)
case class Backend(T: BackendScope[_, State]) {
def setLeague(league: League) {
T.modState(_.copy(league = Some(league), character = None))
}
def setCharacter(character: String) {
T.modState(_.copy(character = Some(character)))
}
def getCharacters(): Future[Seq[CharacterInfo]] = {
pc.getChars().map(cs => cs.toList.filter(c => Option(c.league) =?= T.state.league.map(_.toString)))
}
def setAutowatch(enabled: Boolean) {
lootWatcher.setMode(enabled = enabled, league = T.state.league, character = T.state.character)
T.modState(_.copy(autowatch = enabled))
}
}
val component = {
import japgolly.scalajs.react.vdom.ReactVDom._
import japgolly.scalajs.react.vdom.ReactVDom.all._
import japgolly.scalajs.react.vdom.ReactVDom.{styles => st}
val O = Dynamic.literal
ReactComponentB[GlobalViewWidget]("GlobalViewWidget")
.initialState(State(None, None))
.backend(Backend)
.render { (p, s, b) =>
div(
a(href := "#/home", "[", span(`class` := "fa fa-home"), " Home]"),
a(key := "config", href := "#/config", "[", span(`class` := "fa fa-gear"), " Settings]"),
SelectLeagueWidget(s.league, b.setLeague)(),
s.league.map { l => SelectCharacterWidget(s.character, () => b.getCharacters(), (c) => b.setCharacter(c))()},
s.character.map { character =>
label(
"Autowatch",
title := s"Automatically scan this player for updates every ${s.refreshIntervalSec} Seconds",
input(
`type` := "checkbox",
onchange ==> { e: SyntheticEvent[HTMLInputElement] => b.setAutowatch(e.target.checked)},
s.autowatch && (checked := "true")))
},
img(src:="sha2:afcf65")
)
}
.create
}
}
}
case class GlobalViewWidget() {
def apply(pc: PoeCacher) = new GlobalViewWidget.Component(pc, new LootWatcher(pc)).component(this)
}
object Select2Widget {
import japgolly.scalajs.react.vdom.ReactVDom._
import japgolly.scalajs.react.vdom.ReactVDom.all._
def apply[A](
selection: Option[A],
width: Int,
placeholder: String,
elements: => Future[Seq[A]],
onChange: A => Unit,
toString: A => String,
fromString: String => A,
caseSensitive: Boolean = false,
mustStartWith: Boolean = false
) = {
val props = Props(
selection = selection.map(i => toString(i)),
width = width,
placeholder = placeholder,
onFilter = { s =>
elements.map { elements =>
elements.filter { el =>
val elStr = toString(el)
val (elStrC, txt) = if (caseSensitive) elStr -> s else elStr.toLowerCase -> s.toLowerCase
if (mustStartWith) elStrC.startsWith(txt) else elStrC.contains(txt)
}.map(toString)
.sortBy(_.toLowerCase)
}
},
onChange = { s =>
onChange(fromString(s))
}
)
component(props)
}
case class Props(
selection: Option[String],
width: Int,
placeholder: String,
onFilter: String => Future[Seq[String]],
onChange: String => Unit
)
val component = {
ReactComponentB[Props]("Select2Widget")
.render((_) => div())
.componentDidMount { scope =>
val el = scope.getDOMNode()
val O = js.Dynamic.literal
jq(el).asJsDyn.select2(O(
width = scope.props.width,
placeholder = scope.props.placeholder,
initSelection = {(el : JQuery, cb : js.Dynamic) =>
scope.props.selection.foreach { x =>
cb(O(id = x, text = x))
}
},
query = { (q: js.Dynamic) =>
val term = q.term.asInstanceOf[String]
scope.props.onFilter(term).foreach { xs =>
val ys = xs.map(x => O(id = x, text = x))
q.callback(O(results = ys.toJsArr))
}
}: js.Function
)).on("change", { (e: js.Dynamic) =>
scope.props.onChange(e.`val`.asInstanceOf[String])
}: js.Function)
}
.componentDidUpdate { (scope, p, s) =>
val el = jq(scope.getDOMNode()).asJsDyn
val oldVal = el.select2("val").toString
val newVal = scope.props.selection.getOrElse("")
if (oldVal != newVal) {
el.select2("val", newVal)
}
}
.create
}
}
object SelectCharacterWidget {
lazy val component = {
ReactComponentB[SelectCharacterWidget]("SelectCharacterWidget")
.render((props) => Select2Widget[String](
selection = props.character,
width = 220,
placeholder = "Character",
elements = props.getCharacters().map(_.map(_.name)),
onChange = props.onCharacterChanged,
toString = x => x,
fromString = x => x
)
)
.create
}
}
case class SelectCharacterWidget(
character: Option[String],
getCharacters: () => Future[Seq[CharacterInfo]],
onCharacterChanged: String => Unit) {
def apply() = SelectCharacterWidget.component(this)
}
object SelectLeagueWidget {
object Leagues extends CEnum {
final type EET = League
sealed trait League extends EnumElement
case object Standard extends League
case object Hardcore extends League
case object Rampage extends League
case object Beyond extends League
//Needs to be overrided in child class with
final override val elements = CEnum.getElements(this)
}
val component = {
ReactComponentB[SelectLeagueWidget]("SelectLeagueWidget")
.render((props) => Select2Widget[League](
selection = props.league,
width = 120,
placeholder = "League",
elements = Future(Leagues.elements),
onChange = props.onLeagueChanged,
toString = x => x.toString,
fromString = Leagues.fromString
))
.create
}
}
case class SelectLeagueWidget(league: Option[League], onLeagueChanged: League => Unit) {
def apply() = SelectLeagueWidget.component(this)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment