OutWatch v1.0.0-RC2 Release Notes

    • To use tags, attributes in scope you need to import the DSL:

      import outwatch.dom._
      import outwatch.dom.dsl._
      
    • ๐Ÿ’… Outwatch now takes all its tags, attributes and styles from scala-dom-types (DomTypes.scala) - Thanks @raquo! Therefore we have a more complete collection of tags/attributes and it is now possible to use style-sheets in outwatch, like you may know from scalatags.

      div(border := "1px solid black")
      
    • ๐Ÿ’… Custom tags, attribues, properties and styles can also be used (OutwatchAttributes.scala):

      div(
        tag("svg")(
          tag("svg:path")(
            attr("d") := "M 0,-3 L 10,-0.5 L 10,0.5 L0,3",
            style("fill") := "#666"
          )
        )
      )
      

      (SVG support doesn't exist in scala-dom-types yet: https://github.com/raquo/scala-dom-types/issues/20)

    • Referential transparent API using cats-effect:

      • Handler.create[T]() and Sink.create[T]() now return IO[Handler[T]] and IO[Sink[T]]. scala OutWatch.renderReplace("#container", vNode).unsafeRunSync() @LukaJCB, @mariusmuja please describe a bit more...
    • โฑ Outwatch now exclusively uses Monix instead of rxscala-js. Be sure to provide a Scheduler.

    • Introducing managed subscriptions. When subscribing to Observables in your code outside of dom-elements, you have to handle the lifetime of subscriptions yourself. With managed subscriptions, you can instead bind it to the lifetime of a dom-element:

      val handler: Handler[Int] = ???
      val sink: Sink[Int] = ???
      div( managed(sink <-- handler) )
      

    or:

        val observable: Observable[Int] = ???
        div( managed(IO(observable.foreach(i => println(s"debug: $i")))) )
    

    In both cases, outwatch will only run the IO[Subscription] when the element is rendered in the dom and will unsubscribe when the element is removed from the dom.

    • Outwatch.render* is now explicit whether it replaces or inserts into the given dom element (OutWatch.scala):

      val vNode = div()
      val node = document.querySelector("#container")
      OutWatch.renderReplace(node, vNode).unsafeRunSync()
      OutWatch.renderReplace("#container", vNode).unsafeRunSync()
      OutWatch.renderInto(node, vNode).unsafeRunSync()
      OutWatch.renderInto("#container", vNode).unsafeRunSync()
      
    • ๐Ÿ“ฆ Handlers can be transformed using new methods including isomorphisms and lenses (package.scala):

      val handler = Handler.create[Int].unsafeRunSync()
      val mapped = handler.imap[Int](_ - 1)(_ + 1)
      

    handler.unsafeOnNext(12) // mapped will be triggered with 13

    mapped.unsafeOnNext(15) // handler will be triggered with 16

    val handler = Handler.create[(String, Int)].unsafeRunSync() val zoomed = handler.lens[Int](("x", 0))(_._2)((tuple, num) => (tuple._1, num))

    handler.unsafeOnNext(("y", 1)) // zoomed will be triggered with 1

    zoomed.unsafeOnNext(2) // handler will be triggered with ("y", 2)

      @mariusmuja: please explain Pipes
    
    * You can now manually write values into Sinks:
      ```scala
      val handler = Handler.create[Int].unsafeRunSync()
      handler.unsafeOnNext(5)
    
    • You can call apply on already created tags to add additional content (also inspired by scalatags):

      val hello = div("Hello World")
      val helloInBlue = hello(color := "blue")
      
    • Event handlers and life cycle hooks now have an on-prefix like onClick and onInsert.

    • Event handlers and life cycle hooks can now be consistently transformed (EmitterBuilder.scal):

      div(
      onClick(true) --> sink,
      onUpdate.map(_._2) --> eventSink,
      onKeyDown.collect { case e if e.keyCode == KeyCode.Enter && !e.shiftKey => e.preventDefault(); e }.value.filter(_.nonEmpty) --> userInputHandler
      )
      
    • And there are helpers to cast the Event types from Emitters (EmitterOps.scala):

      textArea(
      onInput.value --> stringHandler
      )
      
    • There is a helper called sideEffect which constructs a Sink for in-place side-effect usage (SideEffects.scala):

      div(
      onClick --> sideEffect( e => console.log("your click event: ", e) ),
      onClick --> sideEffect{ println("you clicked!") }
      )
      
    • โž• Additional life cycle hooks from Snabbdom have been added (VDomModifier.scala:)

      textArea(
      onPostPatch --> sideEffect(_.asInstanceOf[dom.Element].focus())
      )
      
    • Global events can be used via provided observables:

      events.window.onResize.foreach(_ => println("window resized"))
      
    • ๐Ÿš€ Boolean and enumerated attributes are now properly handled. Quoting from the HTML5 spec: "A number of attributes are boolean attributes. The presence of a boolean attribute on an element represents the true value, and the absence of the attribute represents the false value." Previously, attributes like draggable or disabled were wrongly rendered. Using scala-dom-types and a new release of snabbdom, we now adhere to the HTML spec.

    • It is possible to group modifiers with CompositeModifier:

      div(
      CompositeModifier(
        Seq(
          div(),
          color := "blue"
        )
      )
      )
      

      @mariusmuja, please describe more

    • ๐Ÿ’… Extended styles for easy and composable CSS animations, example @mariusmuja

    • Accumulated Attributes @mariusmuja

    • ๐Ÿ— Source maps should now point to the correct commit and file on github (build.sbt).