Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 0 additions & 96 deletions calico/src/main/scala/calico/html/Codec.scala

This file was deleted.

6 changes: 3 additions & 3 deletions calico/src/main/scala/calico/html/Html.scala
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,15 @@ sealed trait Html[F[_]](using F: Async[F])

def cls: ClassProp[F] = ClassProp[F]

def role: HtmlAttr[F, List[String]] = HtmlAttr("role", Codec.whitespaceSeparatedStrings)
def role: HtmlAttr[F, List[String]] = HtmlAttr("role", encoders.whitespaceSeparatedStrings)

def dataAttr(suffix: String): HtmlAttr[F, String] =
HtmlAttr("data-" + suffix, Codec.identity)
HtmlAttr("data-" + suffix, encoders.identity)

def children: Children[F] = Children[F]

def children[K](f: K => Resource[F, fs2.dom.Node[F]]): KeyedChildren[F, K] =
KeyedChildren[F, K](f)

def styleAttr: HtmlAttr[F, String] =
HtmlAttr("style", Codec.identity)
HtmlAttr("style", encoders.identity)
47 changes: 29 additions & 18 deletions calico/src/main/scala/calico/html/HtmlAttr.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,57 +16,68 @@

package calico.html

import cats.Contravariant
import cats.Id
import cats.effect.kernel.Async
import cats.effect.kernel.Resource
import fs2.concurrent.Signal
import org.scalajs.dom

sealed class HtmlAttr[F[_], V] private[calico] (key: String, codec: Codec[V, String]):
sealed class HtmlAttr[F[_], V] private[calico] (key: String, encode: V => String):
import HtmlAttr.*

@inline def :=(v: V): ConstantModifier[V] =
ConstantModifier(key, codec, v)
ConstantModifier(key, encode, v)

@inline def <--(vs: Signal[F, V]): SignalModifier[F, V] =
SignalModifier(key, codec, vs)
SignalModifier(key, encode, vs)

@inline def <--(vs: Resource[F, Signal[F, V]]): SignalResourceModifier[F, V] =
SignalResourceModifier(key, codec, vs)
SignalResourceModifier(key, encode, vs)

@inline def <--(vs: Signal[F, Option[V]]): OptionSignalModifier[F, V] =
OptionSignalModifier(key, codec, vs)
OptionSignalModifier(key, encode, vs)

@inline def <--(vs: Resource[F, Signal[F, Option[V]]]): OptionSignalResourceModifier[F, V] =
OptionSignalResourceModifier(key, codec, vs)
OptionSignalResourceModifier(key, encode, vs)

@inline def contramap[U](f: U => V): HtmlAttr[F, U] =
new HtmlAttr(key, f.andThen(encode))

object HtmlAttr:
inline given [F[_]]: Contravariant[HtmlAttr[F, _]] =
_contravariant.asInstanceOf[Contravariant[HtmlAttr[F, _]]]
private val _contravariant: Contravariant[HtmlAttr[Id, _]] = new:
def contramap[A, B](fa: HtmlAttr[Id, A])(f: B => A): HtmlAttr[Id, B] =
fa.contramap(f)

final class ConstantModifier[V] private[calico] (
private[calico] val key: String,
private[calico] val codec: Codec[V, String],
private[calico] val encode: V => String,
private[calico] val value: V
)

final class SignalModifier[F[_], V] private[calico] (
private[calico] val key: String,
private[calico] val codec: Codec[V, String],
private[calico] val encode: V => String,
private[calico] val values: Signal[F, V]
)

final class SignalResourceModifier[F[_], V] private[calico] (
private[calico] val key: String,
private[calico] val codec: Codec[V, String],
private[calico] val encode: V => String,
private[calico] val values: Resource[F, Signal[F, V]]
)

final class OptionSignalModifier[F[_], V] private[calico] (
private[calico] val key: String,
private[calico] val codec: Codec[V, String],
private[calico] val encode: V => String,
private[calico] val values: Signal[F, Option[V]]
)

final class OptionSignalResourceModifier[F[_], V] private[calico] (
private[calico] val key: String,
private[calico] val codec: Codec[V, String],
private[calico] val encode: V => String,
private[calico] val values: Resource[F, Signal[F, Option[V]]]
)

Expand All @@ -78,15 +89,15 @@ private trait HtmlAttrModifiers[F[_]](using F: Async[F]):
_forConstantHtmlAttr.asInstanceOf[Modifier[F, E, ConstantModifier[V]]]

private val _forConstantHtmlAttr: Modifier[F, dom.Element, ConstantModifier[Any]] =
(m, e) => Resource.eval(F.delay(e.setAttribute(m.key, m.codec.encode(m.value))))
(m, e) => Resource.eval(F.delay(e.setAttribute(m.key, m.encode(m.value))))

inline given forSignalHtmlAttr[E <: fs2.dom.Element[F], V]
: Modifier[F, E, SignalModifier[F, V]] =
_forSignalHtmlAttr.asInstanceOf[Modifier[F, E, SignalModifier[F, V]]]

private val _forSignalHtmlAttr =
Modifier.forSignal[F, dom.Element, SignalModifier[F, Any], Any](_.values) { (m, e) => v =>
F.delay(e.setAttribute(m.key, m.codec.encode(v)))
F.delay(e.setAttribute(m.key, m.encode(v)))
}

inline given forSignalResourceHtmlAttr[E <: fs2.dom.Element[F], V]
Expand All @@ -95,7 +106,7 @@ private trait HtmlAttrModifiers[F[_]](using F: Async[F]):

private val _forSignalResourceHtmlAttr =
Modifier.forSignalResource[F, dom.Element, SignalResourceModifier[F, Any], Any](_.values) {
(m, e) => v => F.delay(e.setAttribute(m.key, m.codec.encode(v)))
(m, e) => v => F.delay(e.setAttribute(m.key, m.encode(v)))
}

inline given forOptionSignalHtmlAttr[E <: fs2.dom.Element[F], V]
Expand All @@ -105,7 +116,7 @@ private trait HtmlAttrModifiers[F[_]](using F: Async[F]):
private val _forOptionSignalHtmlAttr =
Modifier.forSignal[F, dom.Element, OptionSignalModifier[F, Any], Option[Any]](_.values) {
(m, e) => v =>
F.delay(v.fold(e.removeAttribute(m.key))(v => e.setAttribute(m.key, m.codec.encode(v))))
F.delay(v.fold(e.removeAttribute(m.key))(v => e.setAttribute(m.key, m.encode(v))))
}

inline given forOptionSignalResourceHtmlAttr[E <: fs2.dom.Element[F], V]
Expand All @@ -116,7 +127,7 @@ private trait HtmlAttrModifiers[F[_]](using F: Async[F]):
Modifier
.forSignalResource[F, dom.Element, OptionSignalResourceModifier[F, Any], Option[Any]](
_.values) { (m, e) => v =>
F.delay(v.fold(e.removeAttribute(m.key))(v => e.setAttribute(m.key, m.codec.encode(v))))
F.delay(v.fold(e.removeAttribute(m.key))(v => e.setAttribute(m.key, m.encode(v))))
}

final class Aria[F[_]] private extends AriaAttrs[F]
Expand All @@ -125,5 +136,5 @@ private object Aria:
inline def apply[F[_]]: Aria[F] = instance.asInstanceOf[Aria[F]]
private val instance: Aria[cats.Id] = new Aria[cats.Id]

final class AriaAttr[F[_], V] private[calico] (suffix: String, codec: Codec[V, String])
extends HtmlAttr[F, V]("aria-" + suffix, codec)
final class AriaAttr[F[_], V] private[calico] (suffix: String, encode: V => String)
extends HtmlAttr[F, V]("aria-" + suffix, encode)
15 changes: 12 additions & 3 deletions calico/src/main/scala/calico/html/Modifier.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ package calico
package html

import calico.syntax.*
import cats.Contravariant
import cats.Foldable
import cats.Id
import cats.effect.kernel.Async
import cats.effect.kernel.Resource
import cats.effect.syntax.all.*
Expand All @@ -34,16 +36,23 @@ trait Modifier[F[_], E, A]:
inline final def contramap[B](inline f: B => A): Modifier[F, E, B] =
(b: B, e: E) => outer.modify(f(b), e)

private object Modifier:
def forSignal[F[_]: Async, E, M, V](signal: M => Signal[F, V])(
object Modifier:
inline given [F[_], E]: Contravariant[Modifier[F, E, _]] =
_contravariant.asInstanceOf[Contravariant[Modifier[F, E, _]]]
private val _contravariant: Contravariant[Modifier[Id, Any, _]] = new:
def contramap[A, B](fa: Modifier[Id, Any, A])(f: B => A) =
fa.contramap(f)

private[html] def forSignal[F[_]: Async, E, M, V](signal: M => Signal[F, V])(
mkModify: (M, E) => V => F[Unit]): Modifier[F, E, M] = (m, e) =>
signal(m).getAndUpdates.flatMap { (head, tail) =>
val modify = mkModify(m, e)
Resource.eval(modify(head)) *>
tail.foreach(modify(_)).compile.drain.cedeBackground.void
}

def forSignalResource[F[_]: Async, E, M, V](signal: M => Resource[F, Signal[F, V]])(
private[html] def forSignalResource[F[_]: Async, E, M, V](
signal: M => Resource[F, Signal[F, V]])(
mkModify: (M, E) => V => F[Unit]): Modifier[F, E, M] = (m, e) =>
signal(m).flatMap { sig =>
sig.getAndUpdates.flatMap { (head, tail) =>
Expand Down
Loading