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]()
andSink.create[T]()
now returnIO[Handler[T]]
andIO[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 likeonClick
andonInsert
.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 aSink
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
ordisabled
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).