-
Notifications
You must be signed in to change notification settings - Fork 3
/
package.scala
293 lines (259 loc) · 12.2 KB
/
package.scala
1
2
3
4
5
6
7
8
9
10
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
package com.thoughtworks.binding
import com.thoughtworks.binding._
import com.thoughtworks.binding.Binding._
import com.thoughtworks.enableIf
import simulacrum._
import scala.language.implicitConversions
package object bindable extends Bindable.ToBindableOps with BindableSeq.ToBindableSeqOps {
implicit def bindableToBinding[From, Value](from: From)(implicit bindable: Bindable.Lt[From, Value]): Binding[Value] =
bindable.toBinding(from)
}
package bindable {
import com.thoughtworks.enableMembersIf
@enableMembersIf(c => !c.compilerSettings.exists(_.matches("""^-Xplugin:.*scalajs-compiler_[0-9\.\-]*\.jar$""")))
private[bindable] object Jvm {
trait LowPriorityJsBindableSeq2 extends LowPriorityBindableSeq2
trait LowPriorityJsBindableSeq0 extends LowPriorityBindableSeq0
}
@enableMembersIf(c => c.compilerSettings.exists(_.matches("""^-Xplugin:.*scalajs-compiler_[0-9\.\-]*\.jar$""")))
private[bindable] object Js {
import scala.scalajs.js
trait LowPriorityJsBindableSeq2 extends LowPriorityBindableSeq2 {
implicit def jsArrayBindableSeq[Value0]: BindableSeq.Aux[js.Array[Value0], Value0] =
new BindableSeq[js.Array[Value0]] {
type Value = Value0
def toBindingSeq(from: js.Array[Value0]): BindingSeq[Value] = Constants(from: _*)
}
}
trait LowPriorityJsBindableSeq0 extends LowPriorityBindableSeq0 {
implicit def bindingJsArrayBindableSeq[Value0]: BindableSeq.Aux[Binding[js.Array[Value0]], Value0] =
new BindableSeq[Binding[js.Array[Value0]]] {
type Value = Value0
def toBindingSeq(from: Binding[js.Array[Value0]]): BindingSeq[Value] =
Constants(from).flatMap { from =>
Constants(from.bind: _*)
}
}
}
}
import Jvm._
import Js._
import scala.concurrent.{ExecutionContext, Future}
import scala.util.Try
private[bindable] trait LowPriorityBindable0 {
implicit def constantBindable[Value0]: Bindable.Aux[Value0, Value0] = new Bindable[Value0] {
type Value = Value0
def toBinding(from: Value): Binding[Value] = Constant(from)
}
}
object Bindable extends LowPriorityBindable0 {
type Aux[-From, Value0] = Bindable[From] {
type Value = Value0
}
type Lt[-From, +Value0] = Bindable[From] {
type Value <: Value0
}
implicit def bindingBindable[Value0]: Bindable.Aux[Binding[Value0], Value0] = new Bindable[Binding[Value0]] {
type Value = Value0
def toBinding(from: Binding[Value0]): Binding[Value] = from
}
implicit def futureBindable[Value0](
implicit executionContext: ExecutionContext): Bindable.Aux[Future[Value0], Option[Try[Value0]]] =
new Bindable[Future[Value0]] {
type Value = Option[Try[Value0]]
def toBinding(from: Future[Value0]): Binding[Value] = FutureBinding(from)
}
@enableIf(c => c.compilerSettings.exists(_.matches("""^-Xplugin:.*scalajs-compiler_[0-9\.\-]*\.jar$""")))
implicit def thenableBindable[Value0]
: Bindable.Aux[scala.scalajs.js.Thenable[Value0], Option[Either[Any, Value0]]] =
new Bindable[scala.scalajs.js.Thenable[Value0]] {
type Value = Option[Either[Any, Value0]]
def toBinding(from: scala.scalajs.js.Thenable[Value0]): Binding[Value] = JsPromiseBinding(from)
}
}
/** A dependent type class that witnesses a type that can be converted to a `Binding[Value]`.
*
* @example The implicit conversion to `Binding` can be enabled by the following `import` statement:
*
* {{{
* import com.thoughtworks.binding.bindable._
* }}}
*
* Then, a `@dom` XHTML template can establish data-binding on any `parameter`
* as long as a [[Bindable]] type class for the `parameter` type is available.
*
* {{{
* @dom
* def mySection[A: Bindable.Lt[?, String]](parameter: A) = {
* <img class={parameter.bind}/>
* }
* }}}
*
* Note that the `?` syntax requires the Scala plug-in
* [[https://github.com/non/kind-projector kind-projector]],
* which can be enabled by adding the following setting into your `build.sbt`:
*
* <pre>addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.9.10")</pre>
*
* The `mySection` method accepts any parameter who is a subtype of `Binding[String]`.
*
* {{{
* import com.thoughtworks.binding._, Binding._
* Binding {
* mySection(Binding("my-class-1")).bind.className should be("my-class-1")
* mySection(Constant("my-class-2")).bind.className should be("my-class-2")
* }.watch()
* }}}
*
* And the `mySection` method also accepts [[java.lang.String String]] parameter.
* {{{
* Binding {
* mySection("my-class-3").bind.className should be("my-class-3")
* }.watch()
* }}}
*
* `mySection` should not accept irrelevant parameter types like [[scala.List]] or [[scala.Int]].
*
* {{{
* "mySection(List.empty)" shouldNot compile
* "mySection(42)" shouldNot compile
* }}}
*/
@typeclass
trait Bindable[-From] {
type Value
def toBinding(from: From): Binding[Value]
}
private[bindable] trait LowPriorityBindableSeq2 {
@deprecated("Potential naming conflict with `Bindable.constantBindable`.", "1.0.2")
private[bindable] def constantBindable[Value] = constantsBindableSeq[Value]
implicit def constantsBindableSeq[Value0]: BindableSeq.Aux[Value0, Value0] = new BindableSeq[Value0] {
type Value = Value0
def toBindingSeq(from: Value): BindingSeq[Value] = Constants(from)
}
}
private[bindable] trait LowPriorityBindableSeq1 extends LowPriorityJsBindableSeq2 {
implicit def bindingBindableSeq[Value0]: BindableSeq.Aux[Binding[Value0], Value0] =
new BindableSeq[Binding[Value0]] {
type Value = Value0
def toBindingSeq(from: Binding[Value0]): BindingSeq[Value] = SingletonBindingSeq(from)
}
implicit def scalaSeqBindableSeq[Value0]: BindableSeq.Aux[Seq[Value0], Value0] =
new BindableSeq[Seq[Value0]] {
type Value = Value0
def toBindingSeq(from: Seq[Value0]): BindingSeq[Value] = Constants(from: _*)
}
implicit def scalaArrayBindableSeq[Value0]: BindableSeq.Aux[Array[Value0], Value0] =
new BindableSeq[Array[Value0]] {
type Value = Value0
def toBindingSeq(from: Array[Value0]): BindingSeq[Value] = Constants(from: _*)
}
}
private[bindable] trait LowPriorityBindableSeq0 extends LowPriorityBindableSeq1 {
implicit def bindingSeqBindableSeq[Value0]: BindableSeq.Aux[BindingSeq[Value0], Value0] =
new BindableSeq[BindingSeq[Value0]] {
type Value = Value0
def toBindingSeq(from: BindingSeq[Value0]): BindingSeq[Value] = from
}
}
object BindableSeq extends LowPriorityJsBindableSeq0 {
type Aux[-From, Value0] = BindableSeq[From] {
type Value = Value0
}
type Lt[-From, +Value0] = BindableSeq[From] {
type Value <: Value0
}
implicit def bindingbindingSeqBindableSeq[Value0]: Aux[Binding[BindingSeq[Value0]], Value0] =
new BindableSeq[Binding[BindingSeq[Value0]]] {
type Value = Value0
def toBindingSeq(from: Binding[BindingSeq[Value0]]): BindingSeq[Value] =
Constants(from).flatMapBinding(identity)
}
implicit def bindingScalaArrayBindableSeq[Value0]: Aux[Binding[Array[Value0]], Value0] =
new BindableSeq[Binding[Array[Value0]]] {
type Value = Value0
def toBindingSeq(from: Binding[Array[Value0]]): BindingSeq[Value] =
Constants(from).flatMap { from =>
Constants(from.bind: _*)
}
}
implicit def bindingScalaSeqBindableSeq[Value0]: Aux[Binding[Seq[Value0]], Value0] =
new BindableSeq[Binding[Seq[Value0]]] {
type Value = Value0
def toBindingSeq(from: Binding[Seq[Value0]]): BindingSeq[Value] =
Constants(from).flatMap { from =>
Constants(from.bind: _*)
}
}
}
/** A dependent type class that witnesses a type that can be converted to a `BindingSeq[Value]`.
*
* @example The [[com.thoughtworks.binding.bindable.BindableSeq.Ops.bindSeq bindSeq]]
* can be enabled by the following `import` statement:
*
* {{{
* import com.thoughtworks.binding.bindable._
* }}}
*
* Then, a `@dom` XHTML template can establish data-binding on any `parameter`
* as long as a [[BindableSeq]] type class for the `parameter` type is available.
*
* {{{
* import org.scalajs.dom.raw._
*
* @dom
* def mySection[A: BindableSeq.Lt[?, Node]](parameter: A) = {
* <section>{parameter.bindSeq}</section>
* }
* }}}
*
* Note that the `?` syntax requires the Scala plug-in
* [[https://github.com/non/kind-projector kind-projector]],
* which can be enabled by adding the following setting into your `build.sbt`:
*
* <pre>addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.9.10")</pre>
*
* The `mySection` method accepts any parameter who is a subtype of `Binding[Node]`
* or `Binding[BindingSeq[Node]]`.
*
* {{{
* import com.thoughtworks.binding._, Binding._
* @dom def myButton: Binding[HTMLButtonElement] = <button type="button">My Button 0</button>
* @dom def myButtons: Binding[BindingSeq[Node]] = <button type="button">My Button 1</button><button type="button">My Button 2</button>
* Binding {
* mySection(myButton).bind.innerHTML should be("<button type=\"button\">My Button 0</button>")
* mySection(myButtons).bind.innerHTML should be("<button type=\"button\">My Button 1</button><button type=\"button\">My Button 2</button>")
* }.watch()
* }}}
*
* And the `mySection` method also accepts `Node`, `BindingSeq[Node]`, `List[Node]`, `Array[Node]`, `js.Array[Node]`, or `Binding[js.Array[Node]]` parameter.
* {{{
* import scala.scalajs.js
* @dom def test = {
* mySection(<button type="button">My Button 3</button>).bind.innerHTML should be("<button type=\"button\">My Button 3</button>")
* mySection(<button type="button">My Button 4</button><button type="button">My Button 5</button>).bind.innerHTML should be(
* "<button type=\"button\">My Button 4</button><button type=\"button\">My Button 5</button>"
* )
* mySection(List(<button type="button">My Button 6</button>, <button type="button">My Button 7</button>)).bind.innerHTML should be("<button type=\"button\">My Button 6</button><button type=\"button\">My Button 7</button>")
* mySection(Array(<button type="button">My Button 8</button>, <button type="button">My Button 9</button>)).bind.innerHTML should be("<button type=\"button\">My Button 8</button><button type=\"button\">My Button 9</button>")
* mySection(js.Array(<button type="button">My Button 10</button>, <button type="button">My Button 11</button>)).bind.innerHTML should be("<button type=\"button\">My Button 10</button><button type=\"button\">My Button 11</button>")
* mySection(Constant(js.Array(<button type="button">My Button 12</button>, <button type="button">My Button 13</button>))).bind.innerHTML should be("<button type=\"button\">My Button 12</button><button type=\"button\">My Button 13</button>")
* }
*
* test.watch()
* }}}
*
* `mySection` should not accept irrelevant parameter types like [[scala.collection.immutable.Set]] or [[scala.Int]].
*
* {{{
* "mySection(Set.empty)" shouldNot compile
* "mySection(42)" shouldNot compile
* }}}
*/
@typeclass
trait BindableSeq[-From] {
type Value
@op("bindSeq", alias = true)
def toBindingSeq(from: From): BindingSeq[Value]
}
}