aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/reading/utils/Rx.scala
blob: a5b56eeec33fbc27fb228ee40b502b0d31245cf6 (plain)
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
package reading.utils

import java.util.concurrent.atomic.AtomicReference

import scala.annotation.tailrec
import scala.language.implicitConversions
import scala.util.{ Failure, Success }

import org.scalajs.dom.Element
import rx._
import scalacss.Defaults.StyleA
import scalatags.JsDom.all._

object RxUtils {

  implicit def rxFrag[T](n: Rx[T])(implicit f: T => Frag, ctx: Ctx.Owner): Frag = {

    @tailrec def clearChildren(node: org.scalajs.dom.Node): Unit = {
      if (node.firstChild != null) {
        node.removeChild(node.firstChild)
        clearChildren(node)
      }
    }

    def fSafe: Frag = n match {
      case r: Rx.Dynamic[T] => r.toTry match {
        case Success(v) => v.render
        case Failure(e) => span(e.getMessage, backgroundColor := "red").render
      }
      case v: Var[T] => v.now.render
    }

    var last = fSafe.render

    val container = span(last).render

    n.triggerLater {
      val newLast = fSafe.render
      //Rx[Seq[T]] can generate multiple children per propagate, so use clearChildren instead of replaceChild
      clearChildren(container)
      container.appendChild(newLast)
      last = newLast
    }
    bindNode(container)
  }

  implicit def RxAttrValue[T: AttrValue](implicit ctx: Ctx.Owner) = new AttrValue[Rx.Dynamic[T]] {
    def apply(t: Element, a: Attr, r: Rx.Dynamic[T]): Unit = {
      r.trigger { implicitly[AttrValue[T]].apply(t, a, r.now) }
      ()
    }
  }

  implicit def RxStyleValue[T: StyleValue](implicit ctx: Ctx.Owner) = new StyleValue[Rx.Dynamic[T]] {
    def apply(t: Element, s: Style, r: Rx.Dynamic[T]): Unit = {
      r.trigger { implicitly[StyleValue[T]].apply(t, s, r.now) }
      ()
    }
  }

  implicit class bindRxStyle(rx: Rx[StyleA])(implicit ctx: Ctx.Owner) extends Modifier {
    def applyTo(container: Element) = {
      val atomicReference = new AtomicReference(rx.now)
      applyStyle(container, atomicReference.get())
      rx.triggerLater {
        val current = rx.now
        val previous = atomicReference.getAndSet(current)
        removeStyle(container, previous)
        applyStyle(container, current)
        ()
      }
      ()
    }

    private def removeStyle(container: Element, style: StyleA): Unit =
      style.classNameIterator.foreach { className =>
        container.classList.remove(className.value)
      }

    private def applyStyle(container: Element, style: StyleA): Unit =
      style.classNameIterator.foreach { className =>
        container.classList.add(className.value)
      }
  }
}