All Downloads are FREE. Search and download functionalities are using the official Maven repository.

outwatch.Render.scala Maven / Gradle / Ivy

The newest version!
package outwatch

import colibri._
import colibri.effect.RunSyncEffect

import scala.scalajs.js
import scala.concurrent.{ExecutionContext, Future}
import scala.util.Success

import cats.effect.Effect
import cats.data.{NonEmptyList, NonEmptySeq, NonEmptyVector, NonEmptyChain}

trait Render[-T] {
  def render(value: T): VDomModifier
}

object Render {
  @inline def apply[T](implicit render: Render[T]): Render[T] = render

  implicit object JsArrayModifier extends Render[js.Array[VDomModifier]] {
    @inline def render(value: js.Array[VDomModifier]): VDomModifier = CompositeModifier(value)
  }

  @inline implicit def JsArrayModifierAs[T : Render]: Render[js.Array[T]] = new JsArrayRenderAsClass[T]
  @inline private class JsArrayRenderAsClass[T : Render] extends Render[js.Array[T]] {
    @inline def render(value: js.Array[T]) = iterableToModifierRender(value)
  }

  implicit object ArrayModifier extends Render[Array[VDomModifier]] {
    @inline def render(value: Array[VDomModifier]): VDomModifier = CompositeModifier(value)
  }

  @inline implicit def ArrayModifierAs[T : Render]: Render[Array[T]] = new ArrayRenderAsClass[T]
  @inline private class ArrayRenderAsClass[T : Render] extends Render[Array[T]] {
    @inline def render(value: Array[T]) = iterableToModifierRender(value)
  }

  implicit object SeqModifier extends Render[Seq[VDomModifier]] {
    @inline def render(value: Seq[VDomModifier]): VDomModifier = CompositeModifier(value)
  }

  @inline implicit def SeqModifierAs[T : Render]: Render[Seq[T]] = new SeqRenderAsClass[T]
  @inline private class SeqRenderAsClass[T : Render] extends Render[Seq[T]] {
    @inline def render(value: Seq[T]) = iterableToModifierRender(value)
  }

  implicit object NonEmptyListModifier extends Render[NonEmptyList[VDomModifier]] {
    @inline def render(value: NonEmptyList[VDomModifier]): VDomModifier = CompositeModifier(value.toList)
  }

  @inline implicit def NonEmptyListModifierAs[T : Render]: Render[NonEmptyList[T]] = new NonEmptyListRenderAsClass[T]
  @inline private class NonEmptyListRenderAsClass[T : Render] extends Render[NonEmptyList[T]] {
    @inline def render(value: NonEmptyList[T]) = iterableToModifierRender(value.toList)
  }

  implicit object NonEmptyVectorModifier extends Render[NonEmptyVector[VDomModifier]] {
    @inline def render(value: NonEmptyVector[VDomModifier]): VDomModifier = CompositeModifier(value.toVector)
  }

  @inline implicit def NonEmptyVectorModifierAs[T : Render]: Render[NonEmptyVector[T]] = new NonEmptyVectorRenderAsClass[T]
  @inline private class NonEmptyVectorRenderAsClass[T : Render] extends Render[NonEmptyVector[T]] {
    @inline def render(value: NonEmptyVector[T]) = iterableToModifierRender(value.toVector)
  }

  implicit object NonEmptySeqModifier extends Render[NonEmptySeq[VDomModifier]] {
    @inline def render(value: NonEmptySeq[VDomModifier]): VDomModifier = CompositeModifier(value.toSeq)
  }

  @inline implicit def NonEmptySeqModifierAs[T : Render]: Render[NonEmptySeq[T]] = new NonEmptySeqRenderAsClass[T]
  @inline private class NonEmptySeqRenderAsClass[T : Render] extends Render[NonEmptySeq[T]] {
    @inline def render(value: NonEmptySeq[T]) = iterableToModifierRender(value.toSeq)
  }

  implicit object NonEmptyChainModifier extends Render[NonEmptyChain[VDomModifier]] {
    @inline def render(value: NonEmptyChain[VDomModifier]): VDomModifier = CompositeModifier(value.toChain.toList)
  }

  @inline implicit def NonEmptyChainModifierAs[T : Render]: Render[NonEmptyChain[T]] = new NonEmptyChainRenderAsClass[T]
  @inline private class NonEmptyChainRenderAsClass[T : Render] extends Render[NonEmptyChain[T]] {
    @inline def render(value: NonEmptyChain[T]) = iterableToModifierRender(value.toChain.toList)
  }

  implicit object OptionModifier extends Render[Option[VDomModifier]] {
    @inline def render(value: Option[VDomModifier]): VDomModifier = value.getOrElse(VDomModifier.empty)
  }

  @inline implicit def OptionModifierAs[T : Render]: Render[Option[T]] = new OptionRenderAsClass[T]
  @inline private class OptionRenderAsClass[T : Render] extends Render[Option[T]] {
    @inline def render(value: Option[T]) = optionToModifierRender(value)
  }

  implicit object UndefinedModifier extends Render[js.UndefOr[VDomModifier]] {
    @inline def render(value: js.UndefOr[VDomModifier]): VDomModifier = value.getOrElse(VDomModifier.empty)
  }

  @inline implicit def UndefinedModifierAs[T : Render]: Render[js.UndefOr[T]] = new UndefinedRenderAsClass[T]
  @inline private class UndefinedRenderAsClass[T : Render] extends Render[js.UndefOr[T]] {
    @inline def render(value: js.UndefOr[T]) = undefinedToModifierRender(value)
  }

  implicit object VDomModifierRender extends Render[VDomModifier] {
    @inline def render(value: VDomModifier): VDomModifier = value
  }

  implicit object StringRender extends Render[String] {
    @inline def render(value: String): VDomModifier = StringVNode(value)
  }

  implicit object IntRender extends Render[Int] {
    @inline def render(value: Int): VDomModifier = StringVNode(value.toString)
  }

  implicit object DoubleRender extends Render[Double] {
    @inline def render(value: Double): VDomModifier = StringVNode(value.toString)
  }

  implicit object LongRender extends Render[Long] {
    @inline def render(value: Long): VDomModifier = StringVNode(value.toString)
  }

  implicit object BooleanRender extends Render[Boolean] {
    @inline def render(value: Boolean): VDomModifier = StringVNode(value.toString)
  }

  @inline implicit def SyncEffectRender[F[_] : RunSyncEffect]: Render[F[VDomModifier]] = new SyncEffectRenderClass[F]
  @inline private class SyncEffectRenderClass[F[_] : RunSyncEffect] extends Render[F[VDomModifier]] {
    @inline def render(effect: F[VDomModifier]) = syncToModifier(effect)
  }

  @inline implicit def SyncEffectRenderAs[F[_] : RunSyncEffect, T : Render]: Render[F[T]] = new SyncEffectRenderAsClass[F, T]
  @inline private class SyncEffectRenderAsClass[F[_] : RunSyncEffect, T : Render] extends Render[F[T]] {
    @inline def render(effect: F[T]) = syncToModifierRender(effect)
  }

  implicit def EffectRender[F[_] : Effect]: Render[F[VDomModifier]] = new EffectRenderClass[F]
  @inline private class EffectRenderClass[F[_] : Effect] extends Render[F[VDomModifier]] {
    def render(effect: F[VDomModifier]) = asyncToModifier(effect)
  }

  @inline implicit def EffectRenderAs[F[_] : Effect, T : Render]: Render[F[T]] = new EffectRenderAsClass[F, T]
  @inline private class EffectRenderAsClass[F[_] : Effect, T : Render] extends Render[F[T]] {
    @inline def render(effect: F[T]) = asyncToModifierRender(effect)
  }

  implicit def FutureRender(implicit ec: ExecutionContext): Render[Future[VDomModifier]] = new FutureRenderClass
  @inline private class FutureRenderClass(implicit ec: ExecutionContext) extends Render[Future[VDomModifier]] {
    @inline def render(future: Future[VDomModifier]) = futureToModifier(future)
  }

  @inline implicit def FutureRenderAs[T : Render](implicit ec: ExecutionContext): Render[Future[T]] = new FutureRenderAsClass[T]
  @inline private class FutureRenderAsClass[T: Render](implicit ec: ExecutionContext) extends Render[Future[T]] {
    @inline def render(future: Future[T]) = futureToModifierRender(future)
  }

  @inline implicit def SourceRender[F[_] : Source]: Render[F[VDomModifier]] = new SourceRenderClass[F]
  @inline private class SourceRenderClass[F[_] : Source] extends Render[F[VDomModifier]] {
    @inline def render(source: F[VDomModifier]) = sourceToModifier(source)
  }

  @inline implicit def SourceRenderAs[F[_] : Source, T : Render]: Render[F[T]] = new SourceRenderAsClass[F, T]
  @inline private class SourceRenderAsClass[F[_]: Source, T: Render] extends Render[F[T]] {
    @inline def render(source: F[T]) = sourceToModifierRender(source)
  }

  @inline implicit def ChildCommandSourceRender[F[_] : Source]: Render[F[ChildCommand]] = new ChildCommandRenderClass[F]
  @inline private class ChildCommandRenderClass[F[_] : Source] extends Render[F[ChildCommand]] {
    @inline def render(source: F[ChildCommand]) = childCommandToModifier(source)
  }

  @inline implicit def ChildCommandSeqSourceRender[F[_] : Source]: Render[F[Seq[ChildCommand]]] = new ChildCommandSeqRenderClass[F]
  @inline private class ChildCommandSeqRenderClass[F[_] : Source] extends Render[F[Seq[ChildCommand]]] {
    @inline def render(source: F[Seq[ChildCommand]]) = childCommandSeqToModifier(source)
  }

  @noinline private def iterableToModifierRender[T: Render](value: Iterable[T]): VDomModifier = CompositeModifier(value.map(VDomModifier(_)))
  @noinline private def optionToModifierRender[T: Render](value: Option[T]): VDomModifier = value.fold(VDomModifier.empty)(VDomModifier(_))
  @noinline private def undefinedToModifierRender[T: Render](value: js.UndefOr[T]): VDomModifier = value.fold(VDomModifier.empty)(VDomModifier(_))
  @noinline private def syncToModifierRender[F[_] : RunSyncEffect, T: Render](effect: F[T]): VDomModifier = SyncEffectModifier(() => VDomModifier(RunSyncEffect[F].unsafeRun(effect)))
  @noinline private def syncToModifier[F[_] : RunSyncEffect](effect: F[VDomModifier]): VDomModifier = SyncEffectModifier(() => RunSyncEffect[F].unsafeRun(effect))
  @noinline private def asyncToModifier[F[_] : Effect](effect: F[VDomModifier]): VDomModifier = StreamModifier(Observable.fromAsync(effect).subscribe(_))
  @noinline private def asyncToModifierRender[F[_] : Effect, T: Render](effect: F[T]): VDomModifier = StreamModifier(Observable.fromAsync(effect).map(VDomModifier(_)).subscribe(_))
  @noinline private def sourceToModifier[F[_] : Source](source: F[VDomModifier]): VDomModifier = StreamModifier(Source[F].subscribe(source))
  @noinline private def sourceToModifierRender[F[_] : Source, T: Render](source: F[T]): VDomModifier = StreamModifier(sink => Source[F].subscribe(source)(sink.contramap(VDomModifier(_))))
  @noinline private def childCommandSeqToModifier[F[_] : Source](source: F[Seq[ChildCommand]]): VDomModifier = ChildCommandsModifier(Observable.lift(source))
  @noinline private def childCommandToModifier[F[_] : Source](source: F[ChildCommand]): VDomModifier = ChildCommandsModifier(Observable.lift(source).map(Seq(_)))
  @noinline private def futureToModifierRender[T: Render](future: Future[T])(implicit ec: ExecutionContext): VDomModifier = future.value match {
    case Some(Success(value)) => VDomModifier(value)
    case _ => StreamModifier(Observable.fromFuture(future).map(VDomModifier(_)).subscribe(_))
  }
  @noinline private def futureToModifier(future: Future[VDomModifier])(implicit ec: ExecutionContext): VDomModifier = future.value match {
    case Some(Success(value)) => value
    case _ => StreamModifier(Observable.fromFuture(future).subscribe(_))
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy