├── project ├── build.properties └── plugins.sbt ├── .jvmopts ├── semantic ├── rootdoc.txt ├── src │ └── main │ │ └── scala │ │ └── com │ │ └── sadhen │ │ └── binding │ │ ├── component │ │ ├── feedback │ │ │ ├── Alert.scala │ │ │ └── ProgressBuilder.scala │ │ ├── other │ │ │ ├── BackTop.scala │ │ │ └── Divider.scala │ │ ├── datadisplay │ │ │ ├── List.scala │ │ │ ├── Avatar.scala │ │ │ └── TableBuilder.scala │ │ ├── dataentry │ │ │ ├── Mention.scala │ │ │ ├── Slider.scala │ │ │ ├── Switch.scala │ │ │ ├── Cascader.scala │ │ │ ├── Transfer.scala │ │ │ ├── DatePicker.scala │ │ │ ├── TimePicker.scala │ │ │ ├── InputNumberBuilder.scala │ │ │ ├── AutoCompleteBuilder.scala │ │ │ └── RateBuilder.scala │ │ ├── ComponentBuilder.scala │ │ ├── general │ │ │ ├── ButtonBuilder.scala │ │ │ └── IconBuilder.scala │ │ ├── package.scala │ │ ├── navigation │ │ │ └── PaginationBuilder.scala │ │ └── tag │ │ │ └── package.scala │ │ ├── util │ │ └── BindingRange.scala │ │ └── SemanticUI.scala └── build.sbt ├── doc ├── src │ ├── main │ │ └── scala │ │ │ ├── MainEntry.scala │ │ │ ├── modules │ │ │ ├── ProgressSpec.scala │ │ │ └── RateSpec.scala │ │ │ ├── Home.scala │ │ │ ├── introduction │ │ │ └── Start.scala │ │ │ └── elements │ │ │ └── IconSpec.scala │ └── site │ │ ├── elements │ │ └── icon │ │ │ └── index.html │ │ ├── modules │ │ ├── rate │ │ │ └── index.html │ │ └── progress │ │ │ └── index.html │ │ ├── introduction │ │ └── start │ │ │ └── index.html │ │ └── index.html └── build.sbt ├── ant ├── build.sbt └── src │ └── main │ └── scala │ └── com │ └── sadhen │ └── binding │ └── magic │ ├── LowPriorityRuntime.scala │ ├── package.scala │ ├── Runtime.scala │ ├── Macros.scala │ └── AutoImports.scala ├── README.md └── .gitignore /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.2.7 2 | -------------------------------------------------------------------------------- /.jvmopts: -------------------------------------------------------------------------------- 1 | -Xms512M 2 | -Xmx4096M 3 | -Xss2M 4 | -XX:MaxMetaspaceSize=1024M 5 | -------------------------------------------------------------------------------- /semantic/rootdoc.txt: -------------------------------------------------------------------------------- 1 | This is the documentation for the Binding.scala on SemanticUI library. 2 | -------------------------------------------------------------------------------- /semantic/src/main/scala/com/sadhen/binding/component/feedback/Alert.scala: -------------------------------------------------------------------------------- 1 | package com.sadhen.binding.component.feedback 2 | 3 | class Alert { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /semantic/src/main/scala/com/sadhen/binding/component/other/BackTop.scala: -------------------------------------------------------------------------------- 1 | package com.sadhen.binding.component.other 2 | 3 | class BackTop { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /semantic/src/main/scala/com/sadhen/binding/component/other/Divider.scala: -------------------------------------------------------------------------------- 1 | package com.sadhen.binding.component.other 2 | 3 | class Divider { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /semantic/src/main/scala/com/sadhen/binding/component/datadisplay/List.scala: -------------------------------------------------------------------------------- 1 | package com.sadhen.binding.component.datadisplay 2 | 3 | class List { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /semantic/src/main/scala/com/sadhen/binding/component/dataentry/Mention.scala: -------------------------------------------------------------------------------- 1 | package com.sadhen.binding.component.dataentry 2 | 3 | class Mention { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /semantic/src/main/scala/com/sadhen/binding/component/dataentry/Slider.scala: -------------------------------------------------------------------------------- 1 | package com.sadhen.binding.component.dataentry 2 | 3 | class Slider { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /semantic/src/main/scala/com/sadhen/binding/component/dataentry/Switch.scala: -------------------------------------------------------------------------------- 1 | package com.sadhen.binding.component.dataentry 2 | 3 | class Switch { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /semantic/src/main/scala/com/sadhen/binding/component/datadisplay/Avatar.scala: -------------------------------------------------------------------------------- 1 | package com.sadhen.binding.component.datadisplay 2 | 3 | class Avatar { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /semantic/src/main/scala/com/sadhen/binding/component/dataentry/Cascader.scala: -------------------------------------------------------------------------------- 1 | package com.sadhen.binding.component.dataentry 2 | 3 | class Cascader { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /semantic/src/main/scala/com/sadhen/binding/component/dataentry/Transfer.scala: -------------------------------------------------------------------------------- 1 | package com.sadhen.binding.component.dataentry 2 | 3 | class Transfer { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /semantic/src/main/scala/com/sadhen/binding/component/dataentry/DatePicker.scala: -------------------------------------------------------------------------------- 1 | package com.sadhen.binding.component.dataentry 2 | 3 | class DatePicker { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /semantic/src/main/scala/com/sadhen/binding/component/dataentry/TimePicker.scala: -------------------------------------------------------------------------------- 1 | package com.sadhen.binding.component.dataentry 2 | 3 | class TimePicker { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /semantic/build.sbt: -------------------------------------------------------------------------------- 1 | enablePlugins(ScalaJSPlugin) 2 | 3 | libraryDependencies += "io.udash" %%% "udash-jquery" % "3.0.0" 4 | libraryDependencies += "org.scala-lang.modules" %%% "scala-xml" % "1.1.0" 5 | 6 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | // logLevel := Level.Debug 2 | 3 | addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.26") 4 | 5 | addSbtPlugin("com.typesafe.sbt" % "sbt-js-engine" % "1.2.3") 6 | 7 | addSbtPlugin("com.typesafe.sbt" % "sbt-ghpages" % "0.6.2") 8 | 9 | addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "1.3.2") 10 | -------------------------------------------------------------------------------- /doc/src/main/scala/MainEntry.scala: -------------------------------------------------------------------------------- 1 | import scala.scalajs.js.annotation.JSExport 2 | import org.scalajs.dom.document 3 | 4 | import com.sadhen.binding.magic._ 5 | 6 | 7 | trait MainEntry { 8 | def main: Ant 9 | 10 | @JSExport 11 | def jsGate(): Unit = { 12 | ant.render(document.body, main) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /semantic/src/main/scala/com/sadhen/binding/util/BindingRange.scala: -------------------------------------------------------------------------------- 1 | package com.sadhen.binding.util 2 | 3 | import com.thoughtworks.binding.Binding.{BindingSeq, Constants, Var} 4 | import com.sadhen.binding.magic.ant 5 | 6 | object BindingRange { 7 | def apply(end: Int): Constants[Int] = { 8 | Constants(Range(0, end): _*) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /semantic/src/main/scala/com/sadhen/binding/component/ComponentBuilder.scala: -------------------------------------------------------------------------------- 1 | package com.sadhen.binding.component 2 | 3 | import com.thoughtworks.binding.Binding 4 | import org.scalajs.dom.raw.Node 5 | 6 | /** 7 | * Created by rendong on 17/1/23. 8 | */ 9 | trait ComponentBuilder[T] { self: T => 10 | def build: Binding[Node] 11 | 12 | def render: T = self 13 | } 14 | -------------------------------------------------------------------------------- /doc/src/site/elements/icon/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Binding SemanticUI 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /doc/src/site/modules/rate/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Binding SemanticUI 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /doc/src/site/introduction/start/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Binding SemanticUI 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /doc/src/site/modules/progress/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Binding SemanticUI 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /semantic/src/main/scala/com/sadhen/binding/component/dataentry/InputNumberBuilder.scala: -------------------------------------------------------------------------------- 1 | package com.sadhen.binding.component.dataentry 2 | 3 | import com.sadhen.binding.component.ComponentBuilder 4 | import com.sadhen.binding.magic.ant 5 | 6 | /** 7 | * Created by rendong on 17/1/23. 8 | */ 9 | class InputNumberBuilder extends ComponentBuilder[InputNumberBuilder] { 10 | @ant 11 | override def build = { 12 |
13 |
14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ant/build.sbt: -------------------------------------------------------------------------------- 1 | enablePlugins(ScalaJSPlugin) 2 | 3 | scalacOptions += "-Xexperimental" 4 | libraryDependencies += "com.thoughtworks.binding" %%% "binding" % "11.6.0" 5 | libraryDependencies += "com.thoughtworks.binding" %% "xmlextractor" % "11.6.0" 6 | libraryDependencies += "com.lihaoyi" %%% "scalatags" % "0.6.7" 7 | libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "0.9.5" 8 | libraryDependencies += "org.typelevel" %% "macro-compat" % "1.1.1" 9 | libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaVersion.value % Provided 10 | -------------------------------------------------------------------------------- /doc/src/main/scala/modules/ProgressSpec.scala: -------------------------------------------------------------------------------- 1 | import com.sadhen.binding.magic.{Ant, ant} 2 | 3 | import scala.scalajs.js.annotation.JSExportTopLevel 4 | import com.sadhen.binding.magic._ 5 | import com.sadhen.binding.component.tag._ 6 | import com.sadhen.binding.component.autoVar 7 | 8 | @JSExportTopLevel("ProgressSpec") 9 | object ProgressSpec extends MainEntry { 10 | 11 | @ant 12 | override def main: Ant = { 13 |
14 |
15 | 16 |
17 |
18 | } 19 | } 20 | -------------------------------------------------------------------------------- /semantic/src/main/scala/com/sadhen/binding/component/general/ButtonBuilder.scala: -------------------------------------------------------------------------------- 1 | package com.sadhen.binding.component.general 2 | 3 | import com.sadhen.binding.component.ComponentBuilder 4 | import com.sadhen.binding.magic.ant 5 | import com.thoughtworks.binding.Binding 6 | import org.scalajs.dom.raw.Node 7 | 8 | class ButtonBuilder extends ComponentBuilder[ButtonBuilder] { 9 | 10 | val constAttrStart = "ui" 11 | val constAttrEnd = "button" 12 | 13 | @ant 14 | override def build: Binding[Node] = { 15 | 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /semantic/src/main/scala/com/sadhen/binding/component/package.scala: -------------------------------------------------------------------------------- 1 | package com.sadhen.binding 2 | 3 | import com.sadhen.binding.component.ComponentBuilder 4 | import com.thoughtworks.binding.Binding.{BindingSeq, Constants, Var} 5 | 6 | import scala.language.implicitConversions 7 | import org.scalajs.dom.raw.Node 8 | 9 | /** 10 | * Created by rendong on 17/1/23. 11 | */ 12 | package object component { 13 | implicit def autoVar[A](a: A): Var[A] = Var(a) 14 | 15 | implicit def toHtml[T](x: ComponentBuilder[T]): BindingSeq[Node] = { 16 | Constants(x.build).mapBinding(identity) 17 | } 18 | } 19 | 20 | -------------------------------------------------------------------------------- /doc/build.sbt: -------------------------------------------------------------------------------- 1 | enablePlugins(ScalaJSPlugin) 2 | enablePlugins(GhpagesPlugin) 3 | 4 | git.remoteRepo := "git@github.com:sadhen/Binding-SemanticUI.git" 5 | 6 | skip in packageJSDependencies := false 7 | 8 | crossTarget in fastOptJS := baseDirectory.value / "src" / "site" 9 | 10 | crossTarget in fullOptJS := baseDirectory.value / "src" / "site" 11 | 12 | 13 | lazy val jQueryV = "3.3.1" 14 | lazy val semanticV = "2.4.1" 15 | 16 | jsDependencies += "org.webjars" % "jquery" % jQueryV / "jquery.js" minified "jquery.min.js" 17 | jsDependencies += "org.webjars" % "Semantic-UI" % semanticV / "semantic.js" minified "semantic.min.js" dependsOn "jquery.js" 18 | -------------------------------------------------------------------------------- /semantic/src/main/scala/com/sadhen/binding/SemanticUI.scala: -------------------------------------------------------------------------------- 1 | package com.sadhen.binding 2 | 3 | import io.udash.wrappers.jquery.JQuery 4 | 5 | import scala.language.implicitConversions 6 | import scala.scalajs.js 7 | 8 | /** 9 | * Created by rendong on 17/1/2. 10 | */ 11 | object SemanticUI { 12 | @js.native 13 | trait SemanticJQuery extends JQuery { 14 | def dropdown(params: js.Any*): SemanticJQuery = js.native 15 | def search(params: js.Any*): SemanticJQuery = js.native 16 | def modal(params: js.Any*): SemanticJQuery = js.native 17 | } 18 | 19 | implicit def jq2semantic(jq: JQuery): SemanticJQuery = jq.asInstanceOf[SemanticJQuery] 20 | } 21 | -------------------------------------------------------------------------------- /semantic/src/main/scala/com/sadhen/binding/component/feedback/ProgressBuilder.scala: -------------------------------------------------------------------------------- 1 | package com.sadhen.binding.component.feedback 2 | 3 | import com.thoughtworks.binding.Binding 4 | import com.thoughtworks.binding.Binding.Var 5 | import org.scalajs.dom.raw.Node 6 | 7 | import com.sadhen.binding.component.ComponentBuilder 8 | import com.sadhen.binding.magic.ant 9 | 10 | class ProgressBuilder extends ComponentBuilder[ProgressBuilder] { 11 | var value: Var[Int] = Var(0) 12 | var total: Var[Int] = Var(100) 13 | 14 | @ant 15 | override def build: Binding[Node] = { 16 |
17 |
18 |
19 |
20 |
21 | } 22 | } 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Binding-SemanticUI [![Scala.js](http://scala-js.org/assets/badges/scalajs-0.6.15.svg)](http://scala-js.org)[![Latest](https://index.scala-lang.org/sadhen/binding-semanticui/semantic-ui/latest.svg)](https://index.scala-lang.org/sadhen/binding-semanticui) 2 | 3 | ## Documentation 4 | ==> https://sadhen.github.io/Binding-SemanticUI/ <== 5 | 6 | ## Developers' Guide 7 | 8 | ``` 9 | $ sbt 10 | > project doc 11 | > fullOptJS 12 | > makeSite 13 | > previewSite 14 | ``` 15 | 16 | `fullOptJS` too slow? Use `fastOptJS` instead, and change the html files under `doc/src/site` manually. 17 | 18 | ## Roadmap 19 | + **v0.1**: Implement and polish the components without innerText or innerHTML 20 | 21 | ## Credits 22 | + http://semantic-ui.com 23 | + http://ant.design 24 | + https://github.com/ThoughtWorksInc/Binding.scala 25 | -------------------------------------------------------------------------------- /doc/src/main/scala/Home.scala: -------------------------------------------------------------------------------- 1 | import scala.scalajs.js.annotation.JSExportTopLevel 2 | import com.sadhen.binding.magic._ 3 | 4 | @JSExportTopLevel("Home") 5 | object Home extends MainEntry { 6 | 7 | @ant 8 | override def main: Ant = { 9 |
10 |
11 |
12 |
13 | 18 |
19 | 20 |
21 |

22 | Binding Semantic 23 |

24 |

Typed Components using Binding.scala and Semantic UI

25 | 26 |
Get Started
27 |
28 |
29 |
30 |
31 |
32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /doc/src/main/scala/introduction/Start.scala: -------------------------------------------------------------------------------- 1 | import scala.scalajs.js.annotation.JSExportTopLevel 2 | 3 | import com.sadhen.binding.magic._ 4 | import com.sadhen.binding.component.tag._ 5 | import com.sadhen.binding.component.autoVar 6 | 7 | @JSExportTopLevel("Start") 8 | object Start extends MainEntry { 9 | @ant 10 | override def main: Ant = { 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
ComponentsProgressVersion
Iconv0.1.0
Ratev0.1.0
Progressv0.1.0
38 |
39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ant/src/main/scala/com/sadhen/binding/magic/LowPriorityRuntime.scala: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | Copyright (c) 2016 Yang Bo & REA Group Ltd. 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 18 | SOFTWARE. 19 | */ 20 | 21 | package com.sadhen.binding.magic 22 | 23 | trait LowPriorityRuntime { 24 | @inline 25 | final def notEqual[A, B](left: A, right: B, dummy: Unit = ()) = left != right 26 | } 27 | -------------------------------------------------------------------------------- /semantic/src/main/scala/com/sadhen/binding/component/dataentry/AutoCompleteBuilder.scala: -------------------------------------------------------------------------------- 1 | package com.sadhen.binding.component.dataentry 2 | 3 | import org.scalajs.dom.raw.Event 4 | import com.thoughtworks.binding.Binding.{Var, Vars} 5 | 6 | import com.sadhen.binding.component.ComponentBuilder 7 | import com.sadhen.binding.magic.ant 8 | 9 | /** 10 | * Created by rendong on 17/1/23. 11 | */ 12 | class AutoCompleteBuilder extends ComponentBuilder[AutoCompleteBuilder] { 13 | var dataSource: Var[Seq[String]] = Var(Seq.empty) 14 | var placeholder: Var[String] = Var("") 15 | 16 | val filter: Var[String] = Var("") 17 | 18 | @ant 19 | override def build = { 20 | 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ant/src/main/scala/com/sadhen/binding/magic/package.scala: -------------------------------------------------------------------------------- 1 | package com.sadhen.binding 2 | 3 | import scala.annotation.{StaticAnnotation, compileTimeOnly} 4 | import scala.language.experimental.macros 5 | import org.scalajs.dom.raw.Node 6 | 7 | import com.thoughtworks.binding.Binding.BindingSeq 8 | import com.thoughtworks.binding.Binding 9 | 10 | 11 | package object magic { 12 | type Ant = Binding[Node] 13 | 14 | @compileTimeOnly("enable macro paradise to expand macro annotations") 15 | class ant extends StaticAnnotation { 16 | def macroTransform(annottees: Any*): Any = macro Macros.macroTransform 17 | } 18 | 19 | object ant { 20 | /** 21 | * Render a binding node into `parent` 22 | */ 23 | @inline 24 | def render(parent: Node, child: Binding[Node]): Unit = { 25 | Runtime.mount(parent, child).watch() 26 | } 27 | 28 | /** 29 | * Render a binding sequence of node into `parent` 30 | */ 31 | @inline 32 | def render(parent: Node, children: BindingSeq[Node]): Unit = { 33 | Runtime.mount(parent, children).watch() 34 | } 35 | 36 | /** 37 | * Render a binding sequence of node into `parent` 38 | * 39 | * @usecase def render(parent: Node, children: Binding[BindingSeq[Node]]): Unit = ??? 40 | **/ 41 | @inline 42 | def render(parent: Node, children: Binding[BindingSeq[Node]], dummy: Unit = ()): Unit = { 43 | Runtime.mount(parent, children).watch() 44 | } 45 | 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /semantic/src/main/scala/com/sadhen/binding/component/navigation/PaginationBuilder.scala: -------------------------------------------------------------------------------- 1 | package com.sadhen.binding.component.navigation 2 | 3 | import com.sadhen.binding.component.ComponentBuilder 4 | import com.thoughtworks.binding.Binding.Var 5 | import com.sadhen.binding.magic.ant 6 | import org.scalajs.dom.raw.Event 7 | 8 | /** 9 | * Created by rendong on 17/1/23. 10 | */ 11 | class PaginationBuilder extends ComponentBuilder[PaginationBuilder] { 12 | var defaultCurrent: Var[Int] = Var(1) 13 | var total: Var[Int] = Var(1) 14 | var simple: Var[Boolean] = Var(false) 15 | 16 | @ant 17 | override def build = { 18 |
19 | 27 |
28 | 31 |
32 | / 33 | { s"${total.bind}" } 34 | 42 |
43 | } 44 | } 45 | -------------------------------------------------------------------------------- /semantic/src/main/scala/com/sadhen/binding/component/tag/package.scala: -------------------------------------------------------------------------------- 1 | package com.sadhen.binding.component 2 | 3 | import com.sadhen.binding.component.datadisplay.TableBuilder 4 | import com.sadhen.binding.component.dataentry._ 5 | import com.sadhen.binding.component.feedback.ProgressBuilder 6 | import com.sadhen.binding.component.navigation.PaginationBuilder 7 | import com.sadhen.binding.component.general.{ButtonBuilder, IconBuilder} 8 | import com.sadhen.binding.magic._ 9 | 10 | /** 11 | * Ref: 12 | * - [[https://github.com/ThoughtWorksInc/Binding.scala/issues/4]] 13 | */ 14 | package object tag { 15 | // General 16 | implicit final class Icon(x: Runtime.TagsAndTags2.type) { 17 | def Icon = new IconBuilder 18 | } 19 | 20 | implicit final class Button(x: Runtime.TagsAndTags2.type) { 21 | def Button = new ButtonBuilder 22 | } 23 | 24 | // Navigation 25 | implicit final class Pagination(x: Runtime.TagsAndTags2.type) { 26 | def Pagination = new PaginationBuilder 27 | } 28 | 29 | // Data Entry 30 | implicit final class Rate(x: Runtime.TagsAndTags2.type) { 31 | def Rate = new RateBuilder 32 | } 33 | 34 | implicit final class AutoComplete(x: Runtime.TagsAndTags2.type) { 35 | def AutoComplete = new AutoCompleteBuilder 36 | } 37 | 38 | implicit final class InputNumber(x: Runtime.TagsAndTags2.type) { 39 | def InputNumber = new InputNumberBuilder 40 | } 41 | 42 | // Data Display 43 | implicit final class Table(x: Runtime.TagsAndTags2.type) { 44 | def Table = new TableBuilder 45 | } 46 | 47 | // Feedback 48 | implicit final class Progress(x: Runtime.TagsAndTags2.type) { 49 | def Progress = new ProgressBuilder 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### SBT template 3 | # Simple Build Tool 4 | # http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control 5 | 6 | target/ 7 | lib_managed/ 8 | src_managed/ 9 | project/boot/ 10 | .history 11 | .cache 12 | ### Scala template 13 | *.class 14 | *.log 15 | 16 | # sbt specific 17 | .cache 18 | .history 19 | .lib/ 20 | dist/* 21 | target/ 22 | lib_managed/ 23 | src_managed/ 24 | project/boot/ 25 | project/plugins/project/ 26 | 27 | # Scala-IDE specific 28 | .scala_dependencies 29 | .worksheet 30 | 31 | # ENSIME specific 32 | .ensime_cache/ 33 | .ensime 34 | ### JetBrains template 35 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 36 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 37 | 38 | # User-specific stuff: 39 | .idea/workspace.xml 40 | .idea/tasks.xml 41 | 42 | # Sensitive or high-churn files: 43 | .idea/dataSources/ 44 | .idea/dataSources.ids 45 | .idea/dataSources.xml 46 | .idea/dataSources.local.xml 47 | .idea/sqlDataSources.xml 48 | .idea/dynamic.xml 49 | .idea/uiDesigner.xml 50 | 51 | # Gradle: 52 | .idea/gradle.xml 53 | .idea/libraries 54 | 55 | # Mongo Explorer plugin: 56 | .idea/mongoSettings.xml 57 | 58 | ## File-based project format: 59 | *.iws 60 | 61 | ## Plugin-specific files: 62 | 63 | # IntelliJ 64 | /out/ 65 | 66 | # mpeltonen/sbt-idea plugin 67 | .idea_modules/ 68 | 69 | # JIRA plugin 70 | atlassian-ide-plugin.xml 71 | 72 | # Crashlytics plugin (for Android Studio and IntelliJ) 73 | com_crashlytics_export_strings.xml 74 | crashlytics.properties 75 | crashlytics-build.properties 76 | fabric.properties 77 | 78 | .idea/ 79 | project/project/ 80 | 81 | *.js 82 | *.js.map 83 | 84 | *.log 85 | -------------------------------------------------------------------------------- /doc/src/main/scala/modules/RateSpec.scala: -------------------------------------------------------------------------------- 1 | import scala.scalajs.js.annotation.JSExportTopLevel 2 | import com.sadhen.binding.magic._ 3 | import com.sadhen.binding.component.tag._ 4 | import com.sadhen.binding.component.autoVar 5 | 6 | /** 7 | * https://github.com/Semantic-Org/Semantic-UI-Docs/blob/master/server/documents/modules/rating.html.eco 8 | */ 9 | @JSExportTopLevel("RateSpec") 10 | object RateSpec extends MainEntry { 11 | 12 | @ant 13 | override def main: Ant = { 14 |
15 |

16 | Rating 17 |
18 | A rating indicates user interest in content 19 |
20 |

21 |

Types

22 | 23 |

Rating

24 |

A basic rating

25 | 26 | 27 |

Star

28 |

A rating can use a set of star icons

29 | 30 | Rating 31 | 32 |

Heart

33 |

A rating can use a set of heart icons

34 | 35 | 36 |

Variations

37 | 38 |

Size

39 |

A rating can vary in size

40 | 41 |

42 | 43 |

44 | 45 |

46 | 47 |

48 | 49 |

50 | 51 |

52 | 53 |
54 | } 55 | } 56 | -------------------------------------------------------------------------------- /semantic/src/main/scala/com/sadhen/binding/component/general/IconBuilder.scala: -------------------------------------------------------------------------------- 1 | package com.sadhen.binding.component.general 2 | 3 | import org.scalajs.dom.raw.{Event, HTMLElement, Node} 4 | import com.thoughtworks.binding.Binding 5 | import com.thoughtworks.binding.Binding.Var 6 | import com.sadhen.binding.component.ComponentBuilder 7 | import com.sadhen.binding.magic.ant 8 | 9 | /** 10 | * Ref: 11 | * - [[https://semantic-ui.com/elements/icon.html]] 12 | * - [[https://ant.design/components/icon/]] 13 | */ 14 | class IconBuilder extends ComponentBuilder[IconBuilder] { 15 | /** Type(name) of an icon, which determines the shape of an icon */ 16 | var `type`: Var[String] = Var("") 17 | /** Color of an icon */ 18 | var color: Var[String] = Var("") 19 | /** Size of an icon */ 20 | var size: Var[String] = Var("") 21 | /** State of an icon, enabled by default */ 22 | var disabled: Var[Boolean] = Var(false) 23 | /** State of an icon, static(not loading) by default */ 24 | var loading: Var[Boolean] = Var(false) 25 | /** State of an icon, used together with Rate */ 26 | var active: Var[Boolean] = Var(false) 27 | var onclick: Var[Event => Unit] = Var { event => } 28 | 29 | private val constAttr = "icon" 30 | 31 | private def fDisabled(disabled: Boolean): String = { 32 | if (disabled) "disabled" 33 | else "" 34 | } 35 | 36 | private def fLoading(loading: Boolean): String = { 37 | if (loading) "loading" 38 | else "" 39 | } 40 | 41 | private def fActive(active: Boolean): String = { 42 | if (active) "active" 43 | else "" 44 | } 45 | 46 | @ant 47 | override def build: Binding[Node] = { 48 | 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /semantic/src/main/scala/com/sadhen/binding/component/dataentry/RateBuilder.scala: -------------------------------------------------------------------------------- 1 | package com.sadhen.binding.component.dataentry 2 | 3 | import org.scalajs.dom.raw.{Event, Node} 4 | import com.thoughtworks.binding.Binding 5 | import com.thoughtworks.binding.Binding.Var 6 | 7 | import com.sadhen.binding.component.ComponentBuilder 8 | import com.sadhen.binding.component.tag.Icon 9 | import com.sadhen.binding.component.autoVar 10 | import com.sadhen.binding.magic.ant 11 | import com.sadhen.binding.util.BindingRange 12 | 13 | /** 14 | * Ref: 15 | * - [[https://semantic-ui.com/modules/rating.html]] 16 | * - [[https://ant.design/components/rate/]] 17 | */ 18 | class RateBuilder extends ComponentBuilder[RateBuilder] { 19 | /** star count */ 20 | var count: Var[Int] = Var(5) 21 | /** current value */ 22 | var value: Var[Int] = Var(0) 23 | var size: Var[String] = Var("") 24 | /** use the star icon */ 25 | var star: Var[Boolean] = Var(false) 26 | /** use the heart icon */ 27 | var heart: Var[Boolean] = Var(false) 28 | var onChange: Var[Int => Unit] = Var(v => {}) 29 | 30 | val constAttrStart = "ui" 31 | val constAttrEnd = "rating" 32 | 33 | private def fStar(star: Boolean): String = 34 | if (star) "star" 35 | else "" 36 | 37 | private def fHeart(heart: Boolean): String = 38 | if (heart) "heart" 39 | else "" 40 | 41 | @ant 42 | private def iconGen(ind: Int): Binding[Node] = { 43 | val current = ind + 1 44 | val active: Var[Boolean] = Var(current <= value.bind) 45 | // TODO: The dummy div pair should be removed later 46 |
47 | 49 | // change the current value to the clicked one 50 | value.value = current 51 | // invoke the customized onChange event 52 | onChange.value(ind) 53 | } /> 54 |
55 | } 56 | 57 | @ant 58 | override def build: Binding[Node] = { 59 |
65 | { BindingRange(count.bind).map(iconGen(_).bind) } 66 |
67 | } 68 | } 69 | -------------------------------------------------------------------------------- /semantic/src/main/scala/com/sadhen/binding/component/datadisplay/TableBuilder.scala: -------------------------------------------------------------------------------- 1 | package com.sadhen.binding.component.datadisplay 2 | 3 | import scala.scalajs.js 4 | 5 | import com.thoughtworks.binding.Binding.{Var, Vars} 6 | import com.thoughtworks.binding.Binding 7 | import org.scalajs.dom.raw.{Event, Node} 8 | 9 | import com.sadhen.binding.component._ 10 | import com.sadhen.binding.magic.ant 11 | 12 | /** 13 | * Created by rendong on 17/1/25. 14 | */ 15 | trait Column { 16 | val title: String 17 | val dataIndex: String 18 | @ant 19 | def render(record: js.Dynamic): Binding[Node] = 20 | { record.selectDynamic(dataIndex).toString } 21 | } 22 | 23 | class TableBuilder extends ComponentBuilder[TableBuilder] { 24 | var dataSource: Var[Array[js.Dynamic]] = Var(Array.empty) 25 | var columns: Var[Array[Column]] = Var(Array.empty) 26 | 27 | @ant 28 | override def build = { 29 | val pageSize = 10 30 | val defaultCurrent = Var(1) 31 | 32 | 33 | 34 | 35 | { for (column <- Vars(columns.bind: _*)) yield } 36 | 37 | 38 | 39 | { for (record <- Vars(dataSource.bind.slice(pageSize*(defaultCurrent.bind-1), pageSize*defaultCurrent.bind): _*)) yield 40 | 41 | { for (column <- Vars(columns.bind: _*)) yield column.render(record).bind } 42 | 43 | } 44 | 45 | 46 | 47 | 76 | 77 | 78 |
{ column.title }
48 | 75 |
79 | } 80 | } 81 | -------------------------------------------------------------------------------- /doc/src/site/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Binding SemanticUI 6 | 7 | 8 | 92 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /ant/src/main/scala/com/sadhen/binding/magic/Runtime.scala: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | Copyright (c) 2016 Yang Bo & REA Group Ltd. 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 18 | SOFTWARE. 19 | */ 20 | 21 | package com.sadhen.binding.magic 22 | 23 | import com.thoughtworks.binding.Binding 24 | import com.thoughtworks.binding.Binding._ 25 | 26 | import org.scalajs.dom.document 27 | import org.scalajs.dom.raw._ 28 | 29 | import scalatags.JsDom 30 | import scalatags.jsdom 31 | import scalatags.JsDom.TypedTag 32 | import scalatags.generic.Namespace 33 | 34 | import scala.annotation.tailrec 35 | import scala.collection.GenSeq 36 | 37 | /** 38 | * Internal helpers for `@ant` annotation 39 | * 40 | * @note Do not use methods and classes in this object. 41 | */ 42 | object Runtime extends LowPriorityRuntime { 43 | @inline 44 | @tailrec 45 | private def removeAll(parent: Node): Unit = { 46 | val firstChild = parent.firstChild 47 | if (firstChild != null) { 48 | parent.removeChild(firstChild) 49 | removeAll(parent) 50 | } 51 | } 52 | 53 | @inline 54 | def mount(parent: Node, childrenBinding: BindingSeq[Node]): NodeSeqMountPoint = { 55 | new NodeSeqMountPoint(parent, childrenBinding) 56 | } 57 | 58 | @inline 59 | def mount(parent: Node, childBinding: Binding[BindingSeq[Node]], dummy: Unit = ()): NodeSeqMountPoint = { 60 | new NodeSeqMountPoint(parent, childBinding, dummy) 61 | } 62 | 63 | @inline 64 | def mount(parent: Node, childBinding: Binding[Node]): NodeMountPoint = { 65 | new NodeMountPoint(parent, childBinding) 66 | } 67 | 68 | final class NodeMountPoint private[Runtime] (parent: Node, childBinding: Binding[Node]) 69 | extends SingleMountPoint[Node](childBinding) { 70 | protected def set(child: Node): Unit = { 71 | removeAll(parent) 72 | if (child.parentNode != null) { 73 | throw new IllegalStateException(raw"""Cannot insert ${child.nodeName} twice!""") 74 | } 75 | parent.appendChild(child) 76 | } 77 | } 78 | 79 | final class NodeSeqMountPoint(parent: Node, childrenBinding: BindingSeq[Node]) 80 | extends MultiMountPoint[Node](childrenBinding) { 81 | 82 | @inline 83 | def this(parent: Node, childBinding: Binding[BindingSeq[Node]], dummy: Unit = ()) = { 84 | this(parent, Constants(childBinding).flatMapBinding(identity)) 85 | } 86 | 87 | override protected def set(children: Seq[Node]): Unit = { 88 | removeAll(parent) 89 | for (child <- children) { 90 | if (child.parentNode != null) { 91 | throw new IllegalStateException(raw"""Cannot insert ${child.nodeName} twice!""") 92 | } 93 | parent.appendChild(child) 94 | } 95 | } 96 | 97 | override protected def splice(from: Int, that: GenSeq[Node], replaced: Int): Unit = { 98 | @inline 99 | @tailrec 100 | def removeChildren(child: Node, n: Int): Node = { 101 | if (n == 0) { 102 | child 103 | } else { 104 | val nextSibling = child.nextSibling 105 | parent.removeChild(child) 106 | removeChildren(nextSibling, n - 1) 107 | } 108 | } 109 | 110 | val child = removeChildren(parent.childNodes(from), replaced) 111 | if (child == null) { 112 | for (newChild <- that) { 113 | if (newChild.parentNode != null) { 114 | throw new IllegalStateException(raw"""Cannot insert a ${newChild.nodeName} element twice!""") 115 | } 116 | parent.appendChild(newChild) 117 | } 118 | } else { 119 | for (newChild <- that) { 120 | if (newChild.parentNode != null) { 121 | throw new IllegalStateException(raw"""Cannot insert a ${newChild.nodeName} element twice!""") 122 | } 123 | parent.insertBefore(newChild, child) 124 | } 125 | } 126 | } 127 | 128 | } 129 | 130 | object TagsAndTags2 extends JsDom.Cap with jsdom.Tags with jsdom.Tags2 { 131 | 132 | import scala.language.dynamics 133 | 134 | final class DynamicDataTag private[TagsAndTags2] () 135 | extends TypedTag[HTMLElement]("data", Nil, false, Namespace.htmlNamespaceConfig) 136 | with Dynamic { 137 | final def selectDynamic(tagName: String): ConcreteHtmlTag[Element] = { 138 | TagsAndTags2.tag(tagName) 139 | } 140 | } 141 | 142 | override lazy val data = new DynamicDataTag() 143 | 144 | } 145 | 146 | @inline 147 | def domBindingSeq(bindingSeq: BindingSeq[Node]) = bindingSeq 148 | 149 | @inline 150 | def domBindingSeq(seq: Seq[Node]) = Constants(seq: _*) 151 | 152 | @inline 153 | def domBindingSeq(node: Node) = Constants(node) 154 | 155 | @inline 156 | def domBindingSeq(text: String) = Constants(document.createTextNode(text)) 157 | 158 | @inline 159 | def domBindingSeq(optionNode: Option[Node]) = Constants(optionNode.toSeq: _*) 160 | 161 | @inline 162 | def notEqual[A](left: A, right: A) = left != right 163 | } 164 | -------------------------------------------------------------------------------- /doc/src/main/scala/elements/IconSpec.scala: -------------------------------------------------------------------------------- 1 | import org.scalajs.dom.raw.Event 2 | import scala.scalajs.js.annotation.JSExportTopLevel 3 | import com.thoughtworks.binding.Binding.Var 4 | import com.sadhen.binding.magic._ 5 | import com.sadhen.binding.component.tag._ 6 | import com.sadhen.binding.component.autoVar 7 | 8 | @JSExportTopLevel("IconSpec") 9 | object IconSpec extends MainEntry { 10 | val disabled: Var[Boolean] = Var(true) 11 | 12 | @ant 13 | override def main: Ant = { 14 |
15 |

16 | Icon 17 |
18 | An icon is a glyph used to represent something else 19 |
20 |

21 | 22 |

States

23 | 24 |

Disabled

25 |

An icon can show that it is disabled

26 | 27 | 28 |

Loading

29 |

An icon can be used as a simple loader

30 | 31 | 32 | 33 | 34 |

Variations

35 | 36 |

Fitted

37 |

An icon can be fitted, without any space to the left or right of it.

38 | Tight spacing 39 | 40 | Tight spacing 41 | 42 |

Size

43 |

An icon can vary in size

44 | 45 | 46 | 47 |
48 | 49 |
50 | 51 |
52 | 53 |
54 | 55 |
56 | 57 | 58 |

Link

59 |

An icon can be formatted as a link

60 | 61 | 62 | 63 |

Flipped

64 |

An icon can be flipped

65 | 66 | 67 | 68 |

Rotated

69 |

An icon can be rotated

70 | 71 | 72 | 73 | 74 |

Circular

75 |

An icon can be formatted to appear circular

76 | 77 | 78 | 79 | 80 | 81 |

Bordered

82 |
83 | In 0.x.x bordered was formally known as squared 84 |
85 |

An icon can be formatted to appear bordered

86 | 87 | 88 | 89 | 90 | 91 |

Colored

92 |

An icon can be formatted with different colors

93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 |

Inverted

108 |

An icon can have its colors inverted for contrast

109 |
110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 |
124 | 125 |

Groups

126 | 127 |

Icons

128 |

Several icons can be used together as a group

129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 |

Corner Icon

138 |

A group of icons can display a smaller corner icon

139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 |

160 | 161 | 162 | 163 | 164 | Add on Twitter 165 |

166 |
167 | } 168 | } 169 | -------------------------------------------------------------------------------- /ant/src/main/scala/com/sadhen/binding/magic/Macros.scala: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | Copyright (c) 2016 Yang Bo & REA Group Ltd. 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 18 | SOFTWARE. 19 | */ 20 | 21 | package com.sadhen.binding.magic 22 | 23 | import scala.collection.immutable.Queue 24 | import scala.reflect.NameTransformer 25 | import scala.reflect.macros.whitebox 26 | 27 | import macrocompat.bundle 28 | import com.thoughtworks.binding.XmlExtractor._ 29 | import com.thoughtworks.Extractor._ 30 | import com.thoughtworks.sde.core.Preprocessor 31 | import com.thoughtworks.binding.XmlExtractor 32 | 33 | @bundle 34 | final class Macros(context: whitebox.Context) extends Preprocessor(context) with XmlExtractor { 35 | 36 | import c.universe._ 37 | 38 | def macroTransform(annottees: Tree*): Tree = { 39 | val transformer = new ComprehensionTransformer { 40 | 41 | private def transformXml(tree: Tree): (Queue[ValDef], Tree) = { 42 | tree match { 43 | case transformedWithValDefs.extract(queue, tree) => 44 | (queue, tree) 45 | case transformed.extract(transformedTree) => 46 | Queue.empty -> transformedTree 47 | case _ => 48 | Queue.empty -> super.transform(tree) 49 | } 50 | } 51 | 52 | private def nodeSeq(children: Seq[Tree]): (Queue[ValDef], Tree) = { 53 | children match { 54 | case Seq() => 55 | Queue.empty -> q"""_root_.com.thoughtworks.binding.Binding.Constants.empty""" 56 | case Seq(child) => 57 | val (valDefs, transformedChild) = transformXml(child) 58 | valDefs -> atPos(child.pos) { 59 | q"""_root_.com.sadhen.binding.magic.Runtime.domBindingSeq($transformedChild)""" 60 | } 61 | case _ => 62 | val transformedPairs = (for { 63 | child <- children 64 | } yield { 65 | val (valDefs, transformedChild) = transformXml(child) 66 | valDefs -> atPos(child.pos) { 67 | q""" 68 | _root_.com.thoughtworks.binding.Binding.apply { 69 | _root_.com.sadhen.binding.magic.Runtime.domBindingSeq($transformedChild) 70 | } 71 | """ 72 | } 73 | })(collection.breakOut(Queue.canBuildFrom)) 74 | val (valDefs, transformedChildren) = transformedPairs.unzip 75 | valDefs.flatten -> q"""_root_.com.thoughtworks.binding.Binding.Constants(..$transformedChildren).flatMapBinding(_root_.scala.Predef.locally _)""" 76 | } 77 | } 78 | 79 | private def transformedWithValDefs: PartialFunction[Tree, (Queue[ValDef], Tree)] = { 80 | case tree @ NodeBuffer(children) => 81 | nodeSeq(children) 82 | case tree @ Elem(tag, attributes, _, children) => 83 | val idOption = findTextAttribute("local-id", attributes).orElse(findTextAttribute("id", attributes)) 84 | val elementName = idOption match { 85 | case None => TermName(c.freshName("htmlElement")) 86 | case Some(id) => TermName(NameTransformer.encode(id)) 87 | } 88 | 89 | val attributeMountPoints = for { 90 | (key, value) <- attributes if { 91 | key match { 92 | case UnprefixedName("local-id") => false 93 | case _ => true 94 | } 95 | } 96 | } yield { 97 | val attributeAccess = propertyAccess(key, q"$elementName") 98 | 99 | atPos(value.pos) { 100 | value match { 101 | case EmptyAttribute() => 102 | q"""$attributeAccess = "" """ 103 | case Text(textLiteral) => 104 | q"$attributeAccess = $textLiteral" 105 | case _ => 106 | val assignName = TermName(c.freshName("assignAttribute")) 107 | val newValueName = TermName(c.freshName("newValue")) 108 | q""" 109 | _root_.com.thoughtworks.sde.core.MonadicFactory.Instructions.each[ 110 | _root_.com.thoughtworks.binding.Binding, 111 | _root_.scala.Unit 112 | ]( 113 | _root_.com.thoughtworks.binding.Binding.apply[_root_.scala.Unit]({ 114 | val $newValueName = ${transform(value)} 115 | @_root_.scala.inline def $assignName() = { 116 | if (_root_.com.sadhen.binding.magic.Runtime.notEqual($attributeAccess, $newValueName)) { 117 | $attributeAccess = $newValueName 118 | } 119 | } 120 | $assignName() 121 | }) 122 | ) 123 | """ 124 | } 125 | } 126 | } 127 | val (valDefs, transformedChild) = children match { 128 | case Seq() => 129 | Queue.empty -> Nil 130 | case _ => 131 | val (valDefs, transformedBuffer) = nodeSeq(children) 132 | valDefs -> List(atPos(tree.pos) { 133 | q""" 134 | _root_.com.thoughtworks.sde.core.MonadicFactory.Instructions.each[ 135 | _root_.com.thoughtworks.binding.Binding, 136 | _root_.scala.Unit 137 | ]( 138 | _root_.com.sadhen.binding.magic.Runtime.mount( 139 | $elementName, 140 | $transformedBuffer 141 | ) 142 | ) 143 | """ 144 | }) 145 | } 146 | 147 | val tagAccess = propertyAccess(tag, q"_root_.com.sadhen.binding.magic.Runtime.TagsAndTags2") 148 | 149 | val elementDef = q"val $elementName = $tagAccess.render" 150 | idOption match { 151 | case None => 152 | valDefs -> q""" 153 | $elementDef 154 | ..$transformedChild 155 | ..$attributeMountPoints 156 | $elementName 157 | """ 158 | case Some(id) => 159 | (valDefs.enqueue(elementDef)) -> q""" 160 | ..$transformedChild 161 | ..$attributeMountPoints 162 | $elementName 163 | """ 164 | } 165 | } 166 | 167 | private def findTextAttribute(unprefixedName: String, 168 | attributes: Seq[(XmlExtractor.QName, Tree)]): Option[String] = { 169 | attributes.collectFirst { case (UnprefixedName(`unprefixedName`), Text(text)) => text } 170 | } 171 | 172 | private def propertyAccess(xmlName: QName, objectAccess: RefTree): Select = { 173 | xmlName match { 174 | case UnprefixedName(localPart) => 175 | q"$objectAccess.${TermName(NameTransformer.encode(localPart))}" 176 | case PrefixedName(prefix, localPart) => 177 | localPart.split(':').foldLeft(q"$objectAccess.${TermName(NameTransformer.encode(prefix))}") { 178 | (prefixExpr, segmentName) => 179 | q"$prefixExpr.${TermName(NameTransformer.encode(segmentName))}" 180 | } 181 | } 182 | } 183 | 184 | private def transformed: PartialFunction[Tree, Tree] = { 185 | case Block(stats, expr) => 186 | super.transform(Block(stats.flatMap { 187 | case transformedWithValDefs.extract((valDefs, transformedTree)) => 188 | valDefs.enqueue(transformedTree) 189 | case stat => 190 | Seq(stat) 191 | }, expr)) 192 | case tree @ EntityRef(HtmlEntityName(unescapedCharacter)) => 193 | atPos(tree.pos) { 194 | q"""$unescapedCharacter""" 195 | } 196 | case tree @ Comment(value) => 197 | atPos(tree.pos) { 198 | q"""_root_.org.scalajs.dom.document.createComment($value)""" 199 | } 200 | case tree @ Text(value) => 201 | atPos(tree.pos) { 202 | q"$value" 203 | } 204 | } 205 | 206 | override def transform(tree: Tree): Tree = { 207 | tree match { 208 | case transformedWithValDefs.extract((valDefs, transformedTree)) => 209 | q""" 210 | ..$valDefs 211 | $transformedTree 212 | """ 213 | case transformed.extract(transformedTree) => 214 | transformedTree 215 | case _ => 216 | super.transform(tree) 217 | } 218 | } 219 | } 220 | 221 | import transformer.transform 222 | // def transform(tree: Tree): Tree = { 223 | // val output = transformer.transform(tree) 224 | // c.info(c.enclosingPosition, show(output), true) 225 | // output 226 | // } 227 | 228 | def autoImportAndTransform(body: Tree) = { 229 | q"""_root_.com.thoughtworks.binding.Binding.apply { 230 | import _root_.com.sadhen.binding.magic.AutoImports.{ 231 | != => _, 232 | ## => _, 233 | == => _, 234 | eq => _, 235 | equals => _, 236 | getClass => _, 237 | hashCode => _, 238 | ne => _, 239 | notify => _, 240 | notifyAll => _, 241 | synchronized => _, 242 | toString => _, 243 | wait => _, 244 | _ 245 | } 246 | workaroundUnusedImport() 247 | ${transform(body)} 248 | }""" 249 | } 250 | replaceDefBody(annottees, autoImportAndTransform) 251 | } 252 | 253 | } 254 | 255 | -------------------------------------------------------------------------------- /ant/src/main/scala/com/sadhen/binding/magic/AutoImports.scala: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | Copyright (c) 2016 Yang Bo & REA Group Ltd. 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 18 | SOFTWARE. 19 | */ 20 | 21 | package com.sadhen.binding.magic 22 | 23 | import org.scalajs.dom.raw._ 24 | 25 | /** 26 | * This object contains implicit views imported automatically for @dom methods. 27 | */ 28 | object AutoImports { 29 | 30 | implicit final class DataOps @inline()(node: Element) { 31 | 32 | import scala.language.dynamics 33 | 34 | object data extends Dynamic { 35 | 36 | final def selectDynamic(attributeName: String): String = { 37 | node.getAttribute(attributeName) 38 | } 39 | 40 | final def updateDynamic(attributeName: String)(attributeValue: String): Unit = { 41 | node.setAttribute(attributeName, attributeValue) 42 | } 43 | 44 | } 45 | 46 | } 47 | 48 | implicit final class OptionOps @inline()(node: Element) { 49 | 50 | import scala.language.dynamics 51 | 52 | object option extends Dynamic { 53 | 54 | final def selectDynamic(attributeName: String): Option[String] = { 55 | if (node.hasAttribute(attributeName)) { 56 | Some(node.getAttribute(attributeName)) 57 | } else { 58 | None 59 | } 60 | } 61 | 62 | final def updateDynamic(attributeName: String)(attributeValue: Option[String]): Unit = { 63 | attributeValue.fold(node.removeAttribute(attributeName))(node.setAttribute(attributeName, _)) 64 | } 65 | 66 | } 67 | 68 | } 69 | 70 | implicit final class ClassOps @inline()(node: HTMLElement) { 71 | @inline def `class` = node.className 72 | 73 | @inline def class_=(value: String) = node.className = value 74 | } 75 | 76 | implicit final class ForOps @inline()(node: HTMLLabelElement) { 77 | @inline def `for` = node.htmlFor 78 | 79 | @inline def for_=(value: String) = node.htmlFor = value 80 | } 81 | 82 | /** 83 | * @param node HTMLDialogElement or HTMLDetailElement 84 | */ 85 | implicit final class OpenOps @inline()(node: Element { var open: Boolean }) { 86 | @inline def open = node.getAttribute("open") 87 | @inline def open_=(value: String) = node.setAttribute("open", value) 88 | } 89 | 90 | implicit final class MultipleOps @inline()(node: Element { var multiple: Boolean }) { 91 | @inline def multiple = node.getAttribute("multiple") 92 | @inline def multiple_=(value: String) = node.setAttribute("multiple", value) 93 | } 94 | 95 | implicit final class FormNoValidateOps @inline()(node: Element { var formNoValidate: Boolean }) { 96 | @inline def formNoValidate = node.getAttribute("formNoValidate") 97 | @inline def formNoValidate_=(value: String) = node.setAttribute("formNoValidate", value) 98 | } 99 | 100 | implicit final class NoValidateOps @inline()(node: HTMLFormElement) { 101 | @inline def noValidate = node.getAttribute("noValidate") 102 | @inline def noValidate_=(value: String) = node.setAttribute("noValidate", value) 103 | } 104 | 105 | implicit final class ControlsOps @inline()(node: HTMLMediaElement) { 106 | @inline def controls = node.getAttribute("controls") 107 | @inline def controls_=(value: String) = node.setAttribute("controls", value) 108 | } 109 | 110 | implicit final class LoopOps @inline()(node: HTMLMediaElement) { 111 | @inline def loop = node.getAttribute("loop") 112 | @inline def loop_=(value: String) = node.setAttribute("loop", value) 113 | } 114 | 115 | implicit final class SelectedOps @inline()(node: Element { var selected: Boolean }) { 116 | @inline def selected = node.getAttribute("selected") 117 | @inline def selected_=(value: String) = node.setAttribute("selected", value) 118 | } 119 | 120 | implicit final class MutedOps @inline()(node: HTMLMediaElement) { 121 | @inline def muted = node.getAttribute("muted") 122 | @inline def muted_=(value: String) = node.setAttribute("muted", value) 123 | } 124 | 125 | implicit final class SpellcheckOps @inline()(node: HTMLElement) { 126 | @inline def spellcheck = node.getAttribute("spellcheck") 127 | @inline def spellcheck_=(value: String) = node.setAttribute("spellcheck", value) 128 | } 129 | 130 | implicit final class DraggableOps @inline()(node: HTMLElement) { 131 | @inline def draggable = node.getAttribute("draggable") 132 | @inline def draggable_=(value: String) = node.setAttribute("draggable", value) 133 | } 134 | 135 | implicit final class AutoplayOps @inline()(node: HTMLMediaElement) { 136 | @inline def autoplay = node.getAttribute("autoplay") 137 | @inline def autoplay_=(value: String) = node.setAttribute("autoplay", value) 138 | } 139 | 140 | implicit final class RequiredOps @inline()(node: Element { var required: Boolean }) { 141 | @inline def required = node.getAttribute("required") 142 | @inline def required_=(value: String) = node.setAttribute("required", value) 143 | } 144 | 145 | implicit final class AutofocusOps @inline()(node: Element { var autofocus: Boolean }) { 146 | @inline def autofocus = node.getAttribute("autofocus") 147 | @inline def autofocus_=(value: String) = node.setAttribute("autofocus", value) 148 | } 149 | 150 | implicit final class CheckedOps @inline()(node: Element { var checked: Boolean }) { 151 | @inline def checked = node.getAttribute("checked") 152 | @inline def checked_=(value: String) = node.setAttribute("checked", value) 153 | } 154 | 155 | implicit final class DisabledOps @inline()(node: Element { var disabled: Boolean }) { 156 | @inline def disabled = node.getAttribute("disabled") 157 | @inline def disabled_=(value: String) = node.setAttribute("disabled", value) 158 | } 159 | 160 | implicit final class ReadOnlyOps @inline()(node: Element { var readOnly: Boolean }) { 161 | @inline def readOnly = node.getAttribute("readOnly") 162 | @inline def readOnly_=(value: String) = node.setAttribute("readOnly", value) 163 | } 164 | implicit final class DefaultOps @inline()(node: HTMLTrackElement) { 165 | @inline def default = node.getAttribute("default") 166 | @inline def default_=(value: String) = node.setAttribute("default", value) 167 | } 168 | 169 | implicit final class PlaysInlineOps @inline()(node: HTMLVideoElement) { 170 | @inline def playsInline = node.getAttribute("playsInline") 171 | @inline def playsInline_=(value: String) = node.setAttribute("playsInline", value) 172 | } 173 | 174 | implicit final class TypeMustMatchOps @inline()(node: HTMLObjectElement) { 175 | @inline def typeMustMatch = node.getAttribute("typeMustMatch") 176 | @inline def typeMustMatch_=(value: String) = node.setAttribute("typeMustMatch", value) 177 | } 178 | 179 | implicit final class TranslateOps @inline()(node: HTMLElement) { 180 | @inline def translate = node.getAttribute("translate") 181 | @inline def translate_=(value: String) = node.setAttribute("translate", value) 182 | } 183 | 184 | implicit final class HiddenOps @inline()(node: HTMLElement) { 185 | @inline def hidden = node.getAttribute("hidden") 186 | @inline def hidden_=(value: String) = node.setAttribute("hidden", value) 187 | } 188 | 189 | implicit final class ReversedOps @inline()(node: HTMLOListElement) { 190 | @inline def reversed = node.getAttribute("reversed") 191 | @inline def reversed_=(value: String) = node.setAttribute("reversed", value) 192 | } 193 | 194 | implicit final class IsMapOps @inline()(node: HTMLImageElement) { 195 | @inline def isMap = node.getAttribute("isMap") 196 | @inline def isMap_=(value: String) = node.setAttribute("isMap", value) 197 | } 198 | 199 | implicit final class AllowFullscreenOps @inline()(node: HTMLIFrameElement) { 200 | @inline def allowFullscreen = node.getAttribute("allowFullscreen") 201 | @inline def allowFullscreen_=(value: String) = node.setAttribute("allowFullscreen", value) 202 | } 203 | 204 | implicit final class AllowPaymentRequestOps @inline()(node: HTMLIFrameElement) { 205 | @inline def allowPaymentRequest = node.getAttribute("allowPaymentRequest") 206 | @inline def allowPaymentRequest_=(value: String) = node.setAttribute("allowPaymentRequest", value) 207 | } 208 | 209 | implicit final class AllowUserMediaOps @inline()(node: HTMLIFrameElement) { 210 | @inline def allowUserMedia = node.getAttribute("allowUserMedia") 211 | @inline def allowUserMedia_=(value: String) = node.setAttribute("allowUserMedia", value) 212 | } 213 | implicit final class NoShadeOps @inline()(node: HTMLHRElement) { 214 | @inline def noShade = node.getAttribute("noShade") 215 | @inline def noShade_=(value: String) = node.setAttribute("noShade", value) 216 | } 217 | implicit final class NoWrapOps @inline()(node: HTMLTableCellElement) { 218 | @inline def noWrap = node.getAttribute("noWrap") 219 | @inline def noWrap_=(value: String) = node.setAttribute("noWrap", value) 220 | } 221 | implicit final class DeclareOps @inline()(node: HTMLObjectElement) { 222 | @inline def declare = node.getAttribute("declare") 223 | @inline def declare_=(value: String) = node.setAttribute("declare", value) 224 | } 225 | 226 | implicit final class TrueSpeedOps @inline()(node: HTMLMarqueeElement) { 227 | @inline def trueSpeed = node.getAttribute("trueSpeed") 228 | @inline def trueSpeed_=(value: String) = node.setAttribute("trueSpeed", value) 229 | } 230 | 231 | implicit final class NoResizeOps @inline()(node: HTMLFrameElement) { 232 | @inline def noResize = node.getAttribute("noResize") 233 | @inline def noResize_=(value: String) = node.setAttribute("noResize", value) 234 | } 235 | 236 | implicit final class NoHrefOps @inline()(node: HTMLAreaElement) { 237 | @inline def noHref = node.getAttribute("noHref") 238 | @inline def noHref_=(value: String) = node.setAttribute("noHref", value) 239 | } 240 | 241 | implicit final class CompactOps @inline()(node: Element { var compact: Boolean }) { 242 | @inline def compact = node.getAttribute("compact") 243 | @inline def compact_=(value: String) = node.setAttribute("compact", value) 244 | } 245 | implicit final class AsyncOps @inline()(node: HTMLScriptElement) { 246 | @inline def async = node.getAttribute("async") 247 | @inline def async_=(value: String) = node.setAttribute("async", value) 248 | } 249 | 250 | implicit final class DeferOps @inline()(node: HTMLScriptElement) { 251 | @inline def defer = node.getAttribute("defer") 252 | @inline def defer_=(value: String) = node.setAttribute("defer", value) 253 | } 254 | 255 | implicit final class NoModuleOps @inline()(node: HTMLScriptElement) { 256 | @inline def noModule = node.getAttribute("noModule") 257 | @inline def noModule_=(value: String) = node.setAttribute("noModule", value) 258 | } 259 | 260 | @inline def workaroundUnusedImport() = () 261 | } 262 | 263 | --------------------------------------------------------------------------------