Rohan Sircar
4 years ago
19 changed files with 627 additions and 83 deletions
-
1.gitignore
-
19build.sbt
-
80client/src/main/scala/com/example/playscalajsreact/ScalaJSExample.scala
-
63client/src/main/scala/com/example/playscalajsreact/component/Content.scala
-
8client/src/main/scala/com/example/playscalajsreact/component/HelloWorldComponent.scala
-
25client/src/main/scala/com/example/playscalajsreact/component/IntEditor.scala
-
54client/src/main/scala/com/example/playscalajsreact/component/MenuComponent.scala
-
58client/src/main/scala/com/example/playscalajsreact/component/Middle.scala
-
47client/src/main/scala/com/example/playscalajsreact/component/NameChanger.scala
-
170client/src/main/scala/com/example/playscalajsreact/component/Top.scala
-
19client/src/main/scala/com/example/playscalajsreact/model/Data.scala
-
19client/src/main/scala/com/example/playscalajsreact/model/GlobalState.scala
-
53client/src/main/scala/com/example/playscalajsreact/model/SnapshotTest.scala
-
13client/src/main/scala/com/example/playscalajsreact/model/User.scala
-
61client/src/main/scala/com/example/playscalajsreact/route/AppRouter.scala
-
15client/src/main/scala/com/example/playscalajsreact/route/Page.scala
-
2project/plugins.sbt
-
1server/conf/routes
-
2shared/src/main/scala/com/example/playscalajsreact/shared/SharedMessages.scala
@ -0,0 +1,63 @@ |
|||
package com.example.playscalajsreact.component |
|||
|
|||
import com.example.playscalajsreact.model.MyGlobalState |
|||
import japgolly.scalajs.react.vdom.VdomElement |
|||
import japgolly.scalajs.react.Callback |
|||
import japgolly.scalajs.react._ |
|||
import japgolly.scalajs.react.vdom.html_<^._ |
|||
import com.example.playscalajsreact.route.AppRouter |
|||
import org.scalajs.dom |
|||
import scala.scalajs.js |
|||
import com.example.playscalajsreact.model.User |
|||
import japgolly.scalajs.react.extra.StateSnapshot |
|||
import com.softwaremill.quicklens._ |
|||
import monocle.macros.Lenses |
|||
|
|||
object Content { |
|||
|
|||
import scala.concurrent.ExecutionContext.Implicits.global |
|||
|
|||
final case class State(myGlobalState: MyGlobalState = MyGlobalState()) |
|||
|
|||
final class Backend($ : BackendScope[_, State]) { |
|||
val modifyState = modify[State](_.myGlobalState) |
|||
val modifyUsername = modifyState andThenModify MyGlobalState.modifyUsername |
|||
var interval: js.UndefOr[js.timers.SetIntervalHandle] = |
|||
js.undefined |
|||
|
|||
def render(s: State): VdomElement = |
|||
// MyGlobalState.ctx.provide(s.myGlobalState) { |
|||
// <.div(AppRouter.router(AppRouter.Props(s.myGlobalState))) |
|||
// } |
|||
<.div |
|||
|
|||
val updateState = (s: State) => { |
|||
val direct = $.withEffectsImpure |
|||
direct.modState(modifyUsername.using(_ + "C")) |
|||
} |
|||
|
|||
val refresh = (s: State) => |
|||
Callback { |
|||
interval = js.timers.setInterval(1000) { |
|||
updateState(s) |
|||
} |
|||
} |
|||
|
|||
val clear = Callback { |
|||
interval foreach js.timers.clearInterval |
|||
interval = js.undefined |
|||
} |
|||
|
|||
} |
|||
|
|||
private val component = ScalaComponent |
|||
.builder[Unit]("content") |
|||
.initialState(State()) |
|||
.renderBackend[Backend] |
|||
.componentDidMount($ => $.backend.refresh($.state)) |
|||
.componentWillUnmount(_.backend.clear) |
|||
.build |
|||
|
|||
def apply() = component() |
|||
} |
|||
|
@ -0,0 +1,25 @@ |
|||
package com.example.playscalajsreact.component |
|||
|
|||
object IntEditor { |
|||
import japgolly.scalajs.react._ |
|||
import japgolly.scalajs.react.vdom.html_<^._ |
|||
import japgolly.scalajs.react.MonocleReact._ |
|||
import japgolly.scalajs.react.extra._ |
|||
import monocle.macros.Lenses |
|||
|
|||
val component = ScalaComponent |
|||
.builder[StateSnapshot[Int]] |
|||
.render_P { stateSnapshot => |
|||
<.span( |
|||
^.paddingLeft := "6ex", // leave some space for ReusabilityOverlay |
|||
<.button( |
|||
s"Current value is ${stateSnapshot.value}. Click to increment", |
|||
^.onClick --> stateSnapshot.modState(_ + 1), |
|||
) |
|||
) |
|||
} |
|||
.configure(ReusabilityOverlay.install) |
|||
.build |
|||
|
|||
def apply(ss: StateSnapshot[Int]) = component(ss) |
|||
} |
@ -0,0 +1,54 @@ |
|||
package com.example.playscalajsreact.component |
|||
import com.example.playscalajsreact.model.MyGlobalState |
|||
import japgolly.scalajs.react.vdom.VdomElement |
|||
import japgolly.scalajs.react.Callback |
|||
import japgolly.scalajs.react._ |
|||
import japgolly.scalajs.react.vdom.html_<^._ |
|||
import com.example.playscalajsreact.route.AppRouter |
|||
import japgolly.scalajs.react.extra.router.RouterCtl |
|||
import com.example.playscalajsreact.route.Page |
|||
import com.example.playscalajsreact.route.Page._ |
|||
import japgolly.scalajs.react.extra.StateSnapshot |
|||
import com.example.playscalajsreact.model.User |
|||
|
|||
object MenuComponent { |
|||
case class State(myGlobalState: MyGlobalState = MyGlobalState()) |
|||
case class Props(state: StateSnapshot[MyGlobalState], c: RouterCtl[Page]) |
|||
|
|||
class Backend($ : BackendScope[Props, Unit]) { |
|||
|
|||
def render(props: Props): VdomElement = |
|||
{ |
|||
val name = props.state.value.user.getOrElse(User.empty).username |
|||
<.ul( |
|||
Array( |
|||
Menu("Home", Home), |
|||
Menu("Hello", Hello), |
|||
Menu(name, Person(name, 0)), |
|||
Menu("Editor", Editor), |
|||
Menu("Test", Test) |
|||
).toTagMod { item => |
|||
{ |
|||
<.li( |
|||
^.key := item.name, |
|||
<.a( |
|||
item.name, |
|||
props.c setOnClick item.route, |
|||
^.color := "red" |
|||
) |
|||
) |
|||
} |
|||
} |
|||
) |
|||
} |
|||
} |
|||
|
|||
private val component = ScalaComponent |
|||
.builder[Props]("menu") |
|||
// .initialState(State()) |
|||
.renderBackend[Backend] |
|||
// .componentDidMount($ => $.backend.refresh($.state)) |
|||
.build |
|||
|
|||
def apply(state: StateSnapshot[MyGlobalState], c: RouterCtl[Page]) = component(Props(state, c)) |
|||
} |
@ -0,0 +1,58 @@ |
|||
package com.example.playscalajsreact.component |
|||
|
|||
import com.example.playscalajsreact.model.MyGlobalState |
|||
import japgolly.scalajs.react.vdom.VdomElement |
|||
import japgolly.scalajs.react.Callback |
|||
import japgolly.scalajs.react._ |
|||
import japgolly.scalajs.react.vdom.html_<^._ |
|||
import com.example.playscalajsreact.route.AppRouter |
|||
import org.scalajs.dom |
|||
import scala.scalajs.js |
|||
import com.example.playscalajsreact.model.User |
|||
import japgolly.scalajs.react.extra.StateSnapshot |
|||
import com.softwaremill.quicklens._ |
|||
import monocle.macros.Lenses |
|||
import com.example.playscalajsreact.model._ |
|||
import japgolly.scalajs.react.MonocleReact._ |
|||
|
|||
object Middle { |
|||
|
|||
final case class Props(name: String, ss: StateSnapshot[Data]) { |
|||
@inline def render: VdomElement = Comp(this) |
|||
} |
|||
|
|||
implicit def reusability: Reusability[Props] = |
|||
Reusability.derive |
|||
|
|||
final class Backend($ : BackendScope[Props, Unit]) { |
|||
|
|||
// Method 2: StateSnapshot.withReuse.zoomL.prepareViaProps |
|||
// Notice that we're using a normal lens here instead of a Reusable[lens] |
|||
private val ssStrFn = |
|||
StateSnapshot.withReuse.zoomL(Data.str).prepareViaProps($)(_.ss) |
|||
|
|||
def render(p: Props): VdomElement = { |
|||
|
|||
// Method 1: ss.withReuse.zoomStateL |
|||
val ssI: StateSnapshot[Int] = p.ss.zoomStateL(Data.reusableLens.int) |
|||
|
|||
// Method 2: StateSnapshot.withReuse.zoomL.prepareViaProps |
|||
val ssS: StateSnapshot[String] = |
|||
ssStrFn(p.ss.value) |
|||
|
|||
<.div( |
|||
<.h3(p.name), |
|||
<.div("IntEditor: ", IntEditor(ssI)) |
|||
// <.div("TextEditor: ", TextEditor(ssS), ^.marginTop := "0.6em")) |
|||
) |
|||
} |
|||
} |
|||
|
|||
val Comp = ScalaComponent |
|||
.builder[Props] |
|||
.renderBackend[Backend] |
|||
.configure(Reusability.shouldComponentUpdate) |
|||
.build |
|||
|
|||
def apply(_props: Props): VdomElement = { Comp(_props) } |
|||
} |
@ -0,0 +1,47 @@ |
|||
package com.example.playscalajsreact.component |
|||
|
|||
import japgolly.scalajs.react.vdom.VdomElement |
|||
import japgolly.scalajs.react.Callback |
|||
import japgolly.scalajs.react._ |
|||
import japgolly.scalajs.react.vdom.html_<^._ |
|||
import com.example.playscalajsreact.route.AppRouter |
|||
import org.scalajs.dom |
|||
import scala.scalajs.js |
|||
import com.example.playscalajsreact.model.User |
|||
import japgolly.scalajs.react.extra.StateSnapshot |
|||
import com.softwaremill.quicklens._ |
|||
import monocle.macros.Lenses |
|||
|
|||
object NameChanger { |
|||
import japgolly.scalajs.react.MonocleReact._ |
|||
|
|||
@Lenses |
|||
case class Name(firstName: String, surname: String) |
|||
|
|||
val NameChanger = ScalaComponent |
|||
.builder[StateSnapshot[String]] |
|||
.render_P { stateSnapshot => |
|||
<.input.text( |
|||
^.value := stateSnapshot.value, |
|||
^.onChange ==> ((e: ReactEventFromInput) => |
|||
stateSnapshot.setState(e.target.value) |
|||
) |
|||
) |
|||
} |
|||
.build |
|||
|
|||
val Main = ScalaComponent |
|||
.builder[Unit] |
|||
.initialState(Name("John", "Wick")) |
|||
.render { $ => |
|||
val name = $.state |
|||
val firstNameV = StateSnapshot.zoomL(Name.firstName).of($) |
|||
val surnameV = StateSnapshot.zoomL(Name.surname).of($) |
|||
<.div( |
|||
<.label("First name:", NameChanger(firstNameV)), |
|||
<.label("Surname:", NameChanger(surnameV)), |
|||
<.p(s"My name is ${name.surname}, ${name.firstName} ${name.surname}.") |
|||
) |
|||
} |
|||
.build |
|||
} |
@ -0,0 +1,170 @@ |
|||
package com.example.playscalajsreact.component |
|||
|
|||
import com.example.playscalajsreact.model.MyGlobalState |
|||
import japgolly.scalajs.react.vdom.VdomElement |
|||
import japgolly.scalajs.react._ |
|||
import japgolly.scalajs.react.vdom.html_<^._ |
|||
import com.example.playscalajsreact.route.AppRouter |
|||
import com.example.playscalajsreact.model.User |
|||
import japgolly.scalajs.react.extra.StateSnapshot |
|||
// import com.softwaremill.quicklens._ |
|||
import com.example.playscalajsreact.model._ |
|||
|
|||
object Top { |
|||
|
|||
final class Backend($ : BackendScope[Unit, Data]) { |
|||
private val setStateFn = |
|||
StateSnapshot.withReuse.prepareVia($) |
|||
|
|||
def render(state: Data): VdomElement = { |
|||
val ss = setStateFn(state) |
|||
// Middle.Props("Demo", ss).render |
|||
Middle(Middle.Props("Demo", ss)) |
|||
} |
|||
} |
|||
|
|||
val Comp = ScalaComponent |
|||
.builder[Unit] |
|||
.initialState(Data(123, "hello")) |
|||
.renderBackend[Backend] |
|||
.build |
|||
} |
|||
|
|||
object Top2 { |
|||
import japgolly.scalajs.react.vdom.all._ |
|||
import japgolly.scalajs.react.MonocleReact._ |
|||
|
|||
final class Backend($ : BackendScope[Unit, MyGlobalState]) { |
|||
private val setStateFn = |
|||
StateSnapshot.withReuse.prepareVia($) |
|||
|
|||
def render(state: MyGlobalState): VdomElement = { |
|||
// val ss = StateSnapshot.zoomL(MyGlobalState.user)(state).setStateVia($) |
|||
|
|||
// val ss2 = ss.xmapState(u => Snappy.State(u))(_.user) |
|||
// div(Snappy.Props(ss2).render) |
|||
val ss = setStateFn(state) |
|||
|
|||
div( |
|||
// Middle2.Props("Middle2", ss).render, |
|||
AppRouter.router(AppRouter.Props(ss)), |
|||
"Value: ", |
|||
state.user.map(_.username) |
|||
) |
|||
} |
|||
} |
|||
|
|||
val Top2Component = ScalaComponent |
|||
.builder[Unit]("Top2") |
|||
.initialState(MyGlobalState(Some(User("testuser")))) |
|||
// .initialState(MyGlobalState.empty) |
|||
.renderBackend[Backend] |
|||
.build |
|||
|
|||
def apply(): VdomElement = Top2Component() |
|||
} |
|||
|
|||
object Middle2 { |
|||
import japgolly.scalajs.react.MonocleReact._ |
|||
import monocle.macros.syntax.lens._ |
|||
import monocle.std.option._ |
|||
import monocle.macros.GenIso |
|||
import cats.implicits._ |
|||
|
|||
// val navigateToUsername = GenIso[MyGlobalState, Option[User]] |
|||
// .composePrism(GenIso[User, String].asPrism.below[Option]) |
|||
|
|||
val navigateToUsername = |
|||
MyGlobalState.user.composePrism(GenIso[User, String].asPrism.below[Option]) |
|||
|
|||
final case class Props(name: String, ss: StateSnapshot[MyGlobalState]) { |
|||
@inline def render: VdomElement = Comp(this) |
|||
} |
|||
|
|||
implicit def reusability: Reusability[Props] = |
|||
Reusability.derive |
|||
|
|||
final class Backend($ : BackendScope[Props, Unit]) { |
|||
|
|||
// Method 2: StateSnapshot.withReuse.zoomL.prepareViaProps |
|||
// Notice that we're using a normal lens here instead of a Reusable[lens] |
|||
private val ssStrFn = |
|||
StateSnapshot.withReuse.zoomL(MyGlobalState.user).prepareViaProps($)(_.ss) |
|||
|
|||
def render(p: Props): VdomElement = { |
|||
val x = p.ss.zoomStateO(navigateToUsername) |
|||
val y = x.map(_.xmapState(_ => 1)(_ => None)) |
|||
|
|||
// Method 1: ss.withReuse.zoomStateL |
|||
// val ssI: StateSnapshot[Int] = p.ss.zoomStateL(Data.reusableLens.int) |
|||
|
|||
// Method 2: StateSnapshot.withReuse.zoomL.prepareViaProps |
|||
// val ssS: StateSnapshot[String] = |
|||
// ssStrFn(p.ss.value) |
|||
|
|||
val ss4 = p.ss.zoomStateL(MyGlobalState.user) |
|||
// val ss5 = p.ss.zoomStateO(navigateToUsername.asOptional) |
|||
// val ss6 = |
|||
// ss5.map(_.xmapState(_.map(n => User(n)))(_.map(u => u.username))) |
|||
// ss5.foreach(_.modState(e => e)) |
|||
// val x = p.ss.value.lens(_.user.getOrElse(User.empty).username) |
|||
// p.ss.zoomStateO() |
|||
|
|||
val ss2 = ss4.xmapState(u => Snappy.State(u))(_.user) |
|||
|
|||
<.div( |
|||
<.h3(p.name), |
|||
<.div("Snappy", Snappy.Props(ss2).render) |
|||
) |
|||
} |
|||
} |
|||
|
|||
val Comp = ScalaComponent |
|||
.builder[Props] |
|||
.renderBackend[Backend] |
|||
// .configure(Reusability.shouldComponentUpdate) |
|||
.build |
|||
|
|||
def apply(_props: Props): VdomElement = { Comp(_props) } |
|||
} |
|||
|
|||
object Snappy { |
|||
|
|||
final case class Props(state: StateSnapshot[State]) { |
|||
@inline def render: VdomElement = Component(this) |
|||
} |
|||
|
|||
//implicit val reusabilityProps: Reusability[Props] = |
|||
// Reusability.derive |
|||
|
|||
final case class State(user: Option[User]) |
|||
|
|||
object State { |
|||
def empty = State(user = None) |
|||
|
|||
//implicit val reusability: Reusability[State] = |
|||
// Reusability.derive |
|||
} |
|||
|
|||
final class Backend($ : BackendScope[Props, Unit]) { |
|||
import com.softwaremill.quicklens._ |
|||
def render(p: Props): VdomNode = { |
|||
val s = p.state.value |
|||
<.div("Test", s.user.map(u => { |
|||
<.div( |
|||
"Username", |
|||
u.username, |
|||
^.onClick --> p.state.modState( |
|||
_.modify(_.user.each.username).using(_ + "c") |
|||
) |
|||
) |
|||
})) |
|||
} |
|||
} |
|||
|
|||
val Component = ScalaComponent |
|||
.builder[Props]("Snappy") |
|||
.renderBackend[Backend] |
|||
//.configure(Reusability.shouldComponentUpdate) |
|||
.build |
|||
} |
@ -0,0 +1,19 @@ |
|||
package com.example.playscalajsreact.model |
|||
|
|||
import monocle.macros.Lenses |
|||
import japgolly.scalajs.react.Reusability |
|||
import japgolly.scalajs.react.Reusable |
|||
|
|||
@Lenses |
|||
final case class Data(int: Int, str: String) |
|||
|
|||
object Data { |
|||
implicit val reusability: Reusability[Data] = Reusability.derive |
|||
|
|||
// Here we wrap the lenses in Reusable.byRef so that React can compare setState/modState functions and know when its |
|||
// it's got the same lens as a previous render. This is required to make [Method 1] work with Reusability |
|||
object reusableLens { |
|||
val int = Reusable.byRef(Data.int) |
|||
val str = Reusable.byRef(Data.str) |
|||
} |
|||
} |
@ -0,0 +1,19 @@ |
|||
package com.example.playscalajsreact.model |
|||
|
|||
import japgolly.scalajs.react.feature.Context |
|||
import japgolly.scalajs.react.React |
|||
import com.softwaremill.quicklens._ |
|||
import monocle.macros.Lenses |
|||
import japgolly.scalajs.react.Reusability |
|||
|
|||
@Lenses |
|||
case class MyGlobalState(user: Option[User] = None, name: String = "") |
|||
|
|||
object MyGlobalState { |
|||
val ctx: Context[MyGlobalState] = React.createContext(MyGlobalState()) |
|||
val modifyUsername = modify[MyGlobalState](_.user.each.username) |
|||
|
|||
implicit val reusability: Reusability[MyGlobalState] = Reusability.derive |
|||
|
|||
def empty = MyGlobalState(None, "") |
|||
} |
@ -0,0 +1,53 @@ |
|||
package com.example.playscalajsreact.model |
|||
|
|||
import japgolly.scalajs.react._ |
|||
import japgolly.scalajs.react.vdom.html_<^._ |
|||
|
|||
object Top { |
|||
|
|||
final case class Props() { |
|||
@inline def render: VdomElement = Component(this) |
|||
} |
|||
|
|||
//implicit val reusabilityProps: Reusability[Props] = |
|||
// Reusability.derive |
|||
|
|||
final class Backend($: BackendScope[Props, Unit]) { |
|||
def render(p: Props): VdomNode = |
|||
<.div |
|||
} |
|||
|
|||
val Component = ScalaComponent.builder[Props]("Top") |
|||
.renderBackend[Backend] |
|||
//.configure(Reusability.shouldComponentUpdate) |
|||
.build |
|||
|
|||
def apply() = Component(Props()) |
|||
} |
|||
|
|||
import japgolly.scalajs.react._ |
|||
import japgolly.scalajs.react.vdom.all._ |
|||
|
|||
object Test2 { |
|||
|
|||
final case class Props() |
|||
|
|||
//implicit val reusabilityProps: Reusability[Props] = |
|||
// Reusability.derive |
|||
|
|||
final class Backend($: BackendScope[Props, Unit]) { |
|||
def render(p: Props): VdomNode = |
|||
<.div |
|||
} |
|||
|
|||
private val Test2Component = ScalaComponent.builder[Props]("Test2") |
|||
.renderBackend[Backend] |
|||
//.configure(Reusability.shouldComponentUpdate) |
|||
.build |
|||
|
|||
def apply(): VdomElement = Test2Component(Props()) |
|||
} |
|||
|
|||
object Test3 { |
|||
Test2() |
|||
} |
@ -0,0 +1,13 @@ |
|||
package com.example.playscalajsreact.model |
|||
|
|||
import monocle.macros.Lenses |
|||
import japgolly.scalajs.react.Reusability |
|||
|
|||
|
|||
@Lenses |
|||
case class User(username: String) |
|||
|
|||
object User { |
|||
def empty = User("") |
|||
implicit val reusability: Reusability[User] = Reusability.derive |
|||
} |
@ -0,0 +1,61 @@ |
|||
package com.example.playscalajsreact.route |
|||
|
|||
import org.scalajs.dom |
|||
import japgolly.scalajs.react._ |
|||
import japgolly.scalajs.react.vdom.html_<^._ |
|||
import japgolly.scalajs.react.extra._ |
|||
import japgolly.scalajs.react.extra.router._ |
|||
import com.example.playscalajsreact.component.HelloWorldSJSRComponent |
|||
import com.example.playscalajsreact.component.MenuComponent |
|||
import com.example.playscalajsreact.component.Top |
|||
import com.example.playscalajsreact.component.Top2 |
|||
import com.example.playscalajsreact.model.MyGlobalState |
|||
import com.example.playscalajsreact.component.Middle2 |
|||
|
|||
|
|||
|
|||
object AppRouter { |
|||
import com.example.playscalajsreact.route.Page._ |
|||
|
|||
final case class Props(state: StateSnapshot[MyGlobalState]) |
|||
|
|||
private def layout(c: RouterCtl[Page], r: ResolutionWithProps[Page, Props])( |
|||
appState: Props |
|||
) = |
|||
<.div( |
|||
MenuComponent(appState.state, c), |
|||
r.renderP(appState) |
|||
) |
|||
|
|||
val x = <.ol( |
|||
^.id := "my-list", |
|||
^.lang := "en", |
|||
^.margin := 8.px, |
|||
<.li("Item 1"), |
|||
<.li("Item 2") |
|||
// HelloWorldSJSRComponent("Hello", 18) |
|||
) |
|||
|
|||
val routerConfig = RouterWithPropsConfigDsl[Page, Props].buildConfig { dsl => |
|||
import dsl._ |
|||
(emptyRule |
|||
| staticRoute(root, Home) ~> render(x) |
|||
| dynamicRouteCT( |
|||
("#user" / string("[a-zA-Z0-9]{1,20}") / "age" / int) |
|||
.caseClass[Person] |
|||
) ~> dynRender((page: Person) => { |
|||
HelloWorldSJSRComponent(page.user, page.age) |
|||
}) |
|||
| staticRoute("#hello", Hello) ~> render(<.div("TODO")) |
|||
| staticRoute("#editor", Editor) ~> render(Top.Comp()) |
|||
| staticRoute("#test", Test) ~> renderP(p => Middle2.Props("Aege", p.state).render) |
|||
| staticRedirect("#hey") ~> redirectToPage(Hello)( |
|||
SetRouteVia.HistoryReplace |
|||
)) |
|||
.notFound(redirectToPage(Home)(SetRouteVia.HistoryReplace)) |
|||
.renderWithP(layout) |
|||
} |
|||
|
|||
val router = |
|||
RouterWithProps(BaseUrl.fromWindowOrigin / "index", AppRouter.routerConfig) |
|||
} |
@ -0,0 +1,15 @@ |
|||
package com.example.playscalajsreact.route |
|||
|
|||
sealed trait Page |
|||
object Page { |
|||
case object Home extends Page |
|||
case object Hello extends Page |
|||
case class Person(user: String, age: Int) extends Page |
|||
case class ID(id: Int) extends Page |
|||
|
|||
case class Menu(name: String, route: Page) |
|||
case class Product(category: Int, item: Int) extends Page |
|||
case class Item(category: String, itemId: java.util.UUID) extends Page |
|||
case object Editor extends Page |
|||
case object Test extends Page |
|||
} |
@ -1,5 +1,5 @@ |
|||
package com.example.playscalajsreact.shared |
|||
|
|||
object SharedMessages { |
|||
def itWorks = "It works!" |
|||
def itWorks = "It works too!" |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue