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

japgolly.scalajs.react.extra.components.TriStateCheckbox.scala Maven / Gradle / Ivy

There is a newer version: 3.0.0-beta6
Show newest version
package japgolly.scalajs.react.extra.components

import japgolly.scalajs.react._
import japgolly.scalajs.react.vdom.html_<^._
import org.scalajs.dom.ext.KeyCode
import org.scalajs.dom.html.Input

/**
 * Checkbox that can have three states: Checked, Unchecked, Indeterminate.
 *
 * @since 0.11.0
 */
object TriStateCheckbox {

  sealed abstract class State extends Product with Serializable {
    final def nextDeterminate: Determinate =
      this match {
        case Checked       => Unchecked
        case Indeterminate
           | Unchecked     => Checked
      }

    final def nextGrow: State =
      this match {
        case Checked       => Unchecked
        case Indeterminate => Checked
        case Unchecked     => Indeterminate
      }

    final def nextShrink: State =
      this match {
        case Checked       => Indeterminate
        case Indeterminate => Unchecked
        case Unchecked     => Checked
      }
  }

  sealed abstract class Determinate extends State

  case object Checked       extends Determinate
  case object Unchecked     extends Determinate
  case object Indeterminate extends State

  final case class Props(state: State, setNextState: Callback) {
    def render = Component(this)
  }

  implicit def reusabilityState: Reusability[State] =
    Reusability.by_==

  implicit def reusabilityProps: Reusability[Props] =
    Reusability.by(_.state) // .setNextState is never accessed outside of a Callback

  private def render($: ScalaComponent.MountedPure[Props, Unit, Unit]) = {
    val setNext = $.props.flatMap(_.setNextState) // Only access .setNextState inside the Callback for Reusability
    <.input.checkbox(eventHandlers(setNext))
  }

  /**
   * Clicking or pressing space = change.
   */
  def eventHandlers(onChange: Callback): TagMod = {
    def handleKey(e: ReactKeyboardEventFromHtml): Callback =
      CallbackOption.keyCodeSwitch(e) {
        case KeyCode.Space => onChange
      }.asEventDefault(e)
    TagMod(
      ^.onClick   --> onChange,
      ^.onKeyDown ==> handleKey)
  }

  private def updateDom($: ScalaComponent.MountedImpure[_, _, _], nextProps: Props): Callback = {
    val s = nextProps.state
    Callback {
      $.getDOMNode.toElement.map(_.domCast[Input]).foreach { d =>
        d.checked       = s == Checked
        d.indeterminate = s == Indeterminate
      }
    }
  }

  val Component = ScalaComponent.builder[Props]("TriStateCheckbox")
    .render(i => render(i.mountedPure))
    .componentDidMount(i => updateDom(i.mountedImpure, i.props))
    .componentDidUpdate(i => updateDom(i.mountedImpure, i.currentProps))
    .configure(Reusability.shouldComponentUpdate)
    .build
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy