Newer
Older
support / ui / components / src / ui / components / tailwind / form / Switch.scala
package works.iterative.ui.components.tailwind.form

import com.raquo.laminar.api.L.{*, given}

import zio.prelude.Validation
import works.iterative.ui.components.tailwind.ComponentContext

class Switch[V](using codec: FormCodec[V, Boolean], ctx: ComponentContext)
    extends FormInput[V]:
  def render(
      property: Property[V],
      updates: Observer[Validated[V]]
  ): HtmlElement =
    val initialValue = property.value.map(codec.toForm).getOrElse(false)
    val currentValue = Var(initialValue)
    div(
      currentValue.signal.map(codec.toValue) --> updates,
      cls := "flex items-center",
      button(
        tpe := "button",
        cls := "relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500",
        cls <-- currentValue.signal.map(v =>
          if v then "bg-indigo-600" else "bg-gray-200"
        ),
        role := "switch",
        dataAttr("aria-checked") := "false",
        dataAttr("aria-labelledby") := "active-only-label",
        span(
          dataAttr("aria-hidden") := "true",
          cls := "pointer-events-none inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200",
          cls <-- currentValue.signal.map(v =>
            if v then "translate-x-5" else "translate-x-0"
          )
        ),
        composeEvents(onClick)(
          _.sample(currentValue.signal).map(v => !v)
        ) --> currentValue
      ),
      span(
        cls := "ml-3",
        idAttr := "active-only-label",
        span(
          cls := "text-sm font-medium text-gray-900",
          ctx.messages(property.name)
        )
      )
    )