Time
Basic Interval Stream
import calico.*
import calico.html.io.{*, given}
import calico.syntax.*
import calico.unsafe.given
import cats.effect.*
import cats.effect.std.Random
import cats.effect.syntax.all.*
import cats.syntax.all.*
import fs2.*
import fs2.concurrent.*
import fs2.dom.*
import scala.concurrent.duration.*
val app: Resource[IO, HtmlDivElement[IO]] =
Stream.fixedRate[IO](1.second).as(1).scanMonoid.holdOptionResource
.flatMap { tick =>
div(
div("Tick #: ", tick.map(_.toString)),
div(
"Random #: ",
Random.scalaUtilRandom[IO].toResource.flatMap { random =>
tick.discrete
.evalMap(_ => random.nextInt.map(i => (i % 100).toString))
.holdOptionResource
}
)
)
}
app.renderInto(node.asInstanceOf[fs2.dom.Node[IO]]).useForever.unsafeRunAndForget()
Delay
import calico.*
import calico.html.io.{*, given}
import calico.syntax.*
import calico.unsafe.given
import cats.effect.*
import cats.effect.syntax.all.*
import cats.syntax.all.*
import fs2.*
import fs2.concurrent.*
import fs2.dom.*
import scala.concurrent.duration.*
val app: Resource[IO, HtmlDivElement[IO]] =
Channel.unbounded[IO, Unit].toResource.flatMap { clickCh =>
val alert = clickCh.stream >>
(Stream.emit("Just clicked!") ++ Stream.sleep_[IO](500.millis) ++ Stream.emit(""))
div(
button(onClick --> (_.void.through(clickCh.sendAll)), "Click me"),
alert.holdResource("")
)
}
app.renderInto(node.asInstanceOf[fs2.dom.Node[IO]]).useForever.unsafeRunAndForget()
Debounce
import calico.*
import calico.html.io.{*, given}
import calico.syntax.*
import calico.unsafe.given
import cats.data.*
import cats.effect.*
import cats.effect.syntax.all.*
import cats.syntax.all.*
import fs2.*
import fs2.concurrent.*
import fs2.dom.*
import scala.concurrent.duration.given
def validateEmail(email: String): Either[String, Unit] =
if email.isEmpty then Left("Please fill out email")
else if !email.contains('@') then Left("Invalid email!")
else Right(())
val app: Resource[IO, HtmlDivElement[IO]] =
Channel.unbounded[IO, String].toResource.flatMap { emailCh =>
val validated = emailCh.stream.debounce(1.second).map(validateEmail)
validated.holdOptionResource.flatMap { validatedSig =>
div(
span(
label("Your email: "),
input.withSelf { self =>
onInput --> (_.evalMap(_ => self.value.get).through(emailCh.sendAll))
}
),
span(
cls <-- Nested(validatedSig).map {
case Left(_) => List("-error")
case Right(_) => List("-success")
}.value,
Nested(validatedSig).map {
case Left(err) => s"Error: $err"
case Right(()) => "Email ok!"
}.value
)
)
}
}
app.renderInto(node.asInstanceOf[fs2.dom.Node[IO]]).useForever.unsafeRunAndForget()