Skip to content

Commit 6c3db9e

Browse files
committed
added materialui FormFieldDescriptors
1 parent 6e9cfaa commit 6c3db9e

File tree

5 files changed

+137
-59
lines changed

5 files changed

+137
-59
lines changed

build.sbt

Lines changed: 45 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,50 +6,61 @@ def commonSettings: Project => Project =
66
version := "0.0.1-SNAPSHOT",
77
homepage := Some(url("https://github.com/torstenrudolf/scalajs-react-form-binder")),
88
licenses += ("MIT", url("https://opensource.org/licenses/MIT")),
9-
scalaVersion := "2.11.8")
9+
scalaVersion := "2.11.8",
10+
publishTo <<= version { (v: String) =>
11+
val nexus = "https://oss.sonatype.org/"
12+
if (v.trim.endsWith("SNAPSHOT"))
13+
Some("snapshots" at nexus + "content/repositories/snapshots")
14+
else
15+
Some("releases" at nexus + "service/local/staging/deploy/maven2")
16+
},
17+
publishArtifact in Test := false,
18+
pomExtra :=
19+
<url>https://github.com/torstenrudolf/scalajs-react-form-binder</url>
20+
<licenses>
21+
<license>
22+
<name>MIT license</name>
23+
<url>http://www.opensource.org/licenses/mit-license.php</url>
24+
</license>
25+
</licenses>
26+
<scm>
27+
<url>git://github.com/torstenrudolf/scalajs-react-form-binder.git</url>
28+
<connection>scm:git://github.com/torstenrudolf/scalajs-react-form-binder.git</connection>
29+
</scm>
30+
<developers>
31+
<developer>
32+
<id>torstenrudolf</id>
33+
<name>Torsten Rudolf</name>
34+
<url>https://github.com/torstenrudolf</url>
35+
</developer>
36+
</developers>
37+
)
1038

1139
def preventPublication: Project => Project =
1240
_.settings(
1341
publishTo := Some(Resolver.file("Unused transient repository", target.value / "fakepublish")),
1442
publishArtifact := false)
1543

1644

17-
lazy val core = (project in file("core")).configure(commonSettings)
45+
lazy val core = project
46+
.configure(commonSettings)
1847
.settings(
1948
name := "core",
2049
libraryDependencies ++= Seq(
2150
"org.scala-lang" % "scala-compiler" % scalaVersion.value,
2251
"com.github.japgolly.scalajs-react" %%% "extra" % "0.11.2"
23-
),
24-
publishTo <<= version { (v: String) =>
25-
val nexus = "https://oss.sonatype.org/"
26-
if (v.trim.endsWith("SNAPSHOT"))
27-
Some("snapshots" at nexus + "content/repositories/snapshots")
28-
else
29-
Some("releases" at nexus + "service/local/staging/deploy/maven2")
30-
},
31-
publishArtifact in Test := false,
32-
pomExtra :=
33-
<url>https://github.com/torstenrudolf/scalajs-react-form-binder</url>
34-
<licenses>
35-
<license>
36-
<name>MIT license</name>
37-
<url>http://www.opensource.org/licenses/mit-license.php</url>
38-
</license>
39-
</licenses>
40-
<scm>
41-
<url>git://github.com/torstenrudolf/scalajs-react-form-binder.git</url>
42-
<connection>scm:git://github.com/torstenrudolf/scalajs-react-form-binder.git</connection>
43-
</scm>
44-
<developers>
45-
<developer>
46-
<id>torstenrudolf</id>
47-
<name>Torsten Rudolf</name>
48-
<url>https://github.com/torstenrudolf</url>
49-
</developer>
50-
</developers>
52+
)
53+
)
54+
55+
lazy val extras = project
56+
.dependsOn(core)
57+
.configure(commonSettings)
58+
.settings(
59+
name := "extras",
60+
libraryDependencies += "com.github.chandu0101.scalajs-react-components" %%% "core" % "0.5.0"
5161
)
5262

63+
5364
lazy val installExternalNPMDeps = TaskKey[Unit]("Execute the npm build command to build the external ui dependencies")
5465
def filesToWatchForExternalNPMDepsTask(workingDir: File) : Seq[File] = {
5566
Seq(workingDir / "package.json",
@@ -78,8 +89,10 @@ def installExternalNPMDeps(workingDir: File): Unit = {
7889
}
7990

8091
lazy val demo = project
81-
.dependsOn(core)
92+
.dependsOn(extras)
93+
.configure(commonSettings, preventPublication)
8294
.settings(
95+
name := "demo",
8396
libraryDependencies ++= Seq(
8497
"com.github.chandu0101.scalajs-react-components" %%% "core" % "0.5.0"
8598
),
@@ -91,7 +104,7 @@ lazy val demo = project
91104
compile in Compile <<= (compile in Compile) dependsOn installExternalNPMDeps,
92105
cleanFiles <++= baseDirectory { base => Seq(base / "build", base / "node_modules") }
93106
)
94-
.configure(commonSettings, preventPublication)
107+
95108

96109
lazy val root = (project in file("."))
97110
.aggregate(core)

demo/deploy.sh

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ set -e
44
#Handy script to deploy demo-app to gh-pages
55

66
# get comment
7-
comment="$1"
8-
if [ "$comment" = "" ]; then echo "please provide commit message"; exit 23; fi
7+
comment="deploy gh-pages"
98

109
scriptFile=$(readlink -f "$0")
1110
projectPath=$(dirname "$scriptFile")/..
@@ -15,8 +14,7 @@ cd $projectPath
1514
git checkout master
1615
git pull
1716
git checkout gh-pages
18-
git pull
19-
git rebase master
17+
git reset --hard master
2018

2119
sbt clean
2220

@@ -36,4 +34,4 @@ git add ${projectPath}
3634

3735
git commit -m "$comment"
3836

39-
echo "all you need to do is to push"
37+
echo "all you need to do is to 'git push --force'"

demo/src/main/scala/torstenrudolf/scalajs/react/formbinder/demo/components/SimpleDemo.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,8 @@ object SimpleDemo {
2727

2828
class Backend($: BackendScope[Unit, State]) {
2929

30-
// then define the form layout fields
30+
// then define the form layout fields -- names must match the data model's field names
3131
object FormLayout extends FormLayout[Data] {
32-
// field names must match the data model's field names
3332
val username = TextField("Username")
3433
val password = TextField("Password", isPassword = true)
3534

demo/src/main/scala/torstenrudolf/scalajs/react/formbinder/demo/components/SimpleMuiDemo.scala

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,10 @@ package torstenrudolf.scalajs.react.formbinder.demo.components
33
import chandu0101.macros.tojs.GhPagesMacros
44

55

6-
7-
86
object SimpleMuiDemo {
97
val code = GhPagesMacros.exampleSource
108

119
// EXAMPLE:START
12-
import scala.scalajs.js
1310
import japgolly.scalajs.react._
1411
import japgolly.scalajs.react.vdom.prefix_<^._
1512
import chandu0101.scalajs.react.components.materialui.{MuiRaisedButton, MuiTextField}
@@ -21,22 +18,19 @@ object SimpleMuiDemo {
2118

2219
// define validation rules in separate object -- note: the method names and signatures must match to the data model's
2320
object DataValidation {
24-
def username(username: String): ValidationResult =
25-
if (username.isEmpty) ValidationResult.withError("Please specify username") else ValidationResult.Success
26-
27-
// simpler syntax with `Validator` helper
28-
def password(password: String): ValidationResult = Validator(password.nonEmpty, "Please specify password")
21+
def username(username: String) = Validator(username.nonEmpty, "Please specify username")
22+
def password(password: String) = Validator(password.nonEmpty, "Please specify password")
2923
}
3024

3125
case class State(data: Option[Data] = None)
3226

3327
class Backend($: BackendScope[Unit, State]) {
3428

35-
// then define the form layout fields
29+
// then define the form layout fields -- names must match the data model's field names
3630
object FormLayout extends FormLayout[Data] {
37-
// field names must match the data model's field names
38-
val username = TextField("Username")
39-
val password = TextField("Password", isPassword = true)
31+
import torstenrudolf.scalajs.react.formbinder.materialui.FormFieldDescriptors._
32+
val username = MuiTextField(floatingLabelText = "Username").asFormFieldDescriptor //TextField("Username")
33+
val password = MuiTextField(floatingLabelText = "Password", `type` = "password").asFormFieldDescriptor
4034

4135
// define what to do after form data or form validation changes
4236
def onChange(validatedData: Option[Data],
@@ -73,14 +67,6 @@ object SimpleMuiDemo {
7367
)
7468
}
7569

76-
// the FormFieldDescriptor that defines error display and binds the onChangeCB to the field
77-
def TextField(labelText: String, isPassword: Boolean = false) =
78-
FormFieldDescriptor((a: FormFieldArgs[String]) =>
79-
MuiTextField(
80-
floatingLabelText = labelText,
81-
`type` = if (isPassword) "password" else js.undefined,
82-
onChange = (e: ReactEventI) => a.onChangeCB(e.target.value),
83-
errorText = a.currentValidationResult.errorMessage)())
8470
// EXAMPLE:END
8571

8672
val component = ReactComponentB[Unit]("SimpleFormDemo")
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package torstenrudolf.scalajs.react.formbinder.materialui
2+
3+
import chandu0101.scalajs.react.components.CssProperties
4+
import chandu0101.scalajs.react.components.materialui._
5+
import chandu0101.scalajs.react.components.Implicits._
6+
import japgolly.scalajs.react._
7+
import scala.scalajs.js.JSConverters._
8+
import torstenrudolf.scalajs.react.formbinder._
9+
10+
/*
11+
Some convenient helpers that reduce the boilerplate in simple cases
12+
*/
13+
object FormFieldDescriptors {
14+
15+
implicit def _muiTextFieldExt(mf: MuiTextField): MuiTextFieldExt = new MuiTextFieldExt(mf)
16+
17+
implicit def _muiSelectFieldExt[T](mf: MuiSelectField[T]): MuiSelectFieldExt[T] = new MuiSelectFieldExt[T](mf)
18+
19+
implicit def _muiSliderFieldExt(mf: MuiSlider): MuiSliderFieldExt = new MuiSliderFieldExt(mf)
20+
21+
final class MuiTextFieldExt(mf: MuiTextField) {
22+
require(mf.onChange.isEmpty && mf.errorText.isEmpty && mf.value.isEmpty)
23+
24+
// would need a macro to do this so the types match?
25+
// def asFormFieldDescriptor = {
26+
// mf.`type`.getOrElse("text") match {
27+
// case "number" => asNumberFormFieldDescriptor
28+
// case _ => asTextFormFieldDescriptor
29+
// }
30+
// }
31+
32+
def asFormFieldDescriptor: FormFieldDescriptor[String] = {
33+
FormFieldDescriptor[String]((a: FormFieldArgs[String]) =>
34+
mf.copy(
35+
value = a.currentValue.orUndefined,
36+
onChange = (e: ReactEventI) => a.onChangeCB(e.target.value),
37+
errorText = a.currentValidationResult.errorMessage
38+
)()
39+
)
40+
}
41+
42+
def asNumberFormFieldDescriptor: FormFieldDescriptor[Double] = {
43+
require(mf.`type`.get == "number")
44+
FormFieldDescriptor[Double]((a: FormFieldArgs[Double]) =>
45+
mf.copy(
46+
value = a.currentValue.map(_.toString).orUndefined,
47+
onChange = (e: ReactEventI) => a.onChangeCB(e.target.value.toDouble),
48+
errorText = a.currentValidationResult.errorMessage
49+
)()
50+
)
51+
}
52+
}
53+
54+
final class MuiSelectFieldExt[T](mf: MuiSelectField[T]) {
55+
require(mf.onChange.isEmpty && mf.errorText.isEmpty && mf.value.isEmpty)
56+
57+
def asFormFieldDescriptor: FormFieldDescriptor[T] = {
58+
FormFieldDescriptor[T]((a: FormFieldArgs[T]) =>
59+
mf.copy(
60+
value = a.currentValue.orUndefined,
61+
onChange = (e: ReactEventI, idx: Int, value: T) => a.onChangeCB(value),
62+
errorText = a.currentValidationResult.errorMessage
63+
)()
64+
)
65+
}
66+
}
67+
68+
final class MuiSliderFieldExt(mf: MuiSlider) {
69+
require(mf.onChange.isEmpty && mf.value.isEmpty)
70+
71+
def asFormFieldDescriptor: FormFieldDescriptor[Double] = {
72+
FormFieldDescriptor[Double]((a: FormFieldArgs[Double]) =>
73+
mf.copy(
74+
value = a.currentValue.orUndefined,
75+
onChange = (e: ReactEventH, value: Double) => a.onChangeCB(value),
76+
error = a.currentValidationResult.errorMessage
77+
)()
78+
)
79+
}
80+
}
81+
82+
}

0 commit comments

Comments
 (0)