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

lucuma.react.primereact.Tooltip.scala Maven / Gradle / Ivy

The newest version!
// Copyright (c) 2016-2023 Association of Universities for Research in Astronomy, Inc. (AURA)
// For license information see LICENSE or https://opensource.org/licenses/BSD-3-Clause

package lucuma.react.primereact

import cats.syntax.option.*
import japgolly.scalajs.react.*
import japgolly.scalajs.react.vdom.TagOf
import japgolly.scalajs.react.vdom.TopNode
import japgolly.scalajs.react.vdom.html_<^.*
import lucuma.react.common.*
import lucuma.typed.primereact.components.Tooltip as CTooltip
import lucuma.typed.primereact.primereactStrings.both
import lucuma.typed.primereact.primereactStrings.bottom
import lucuma.typed.primereact.primereactStrings.focus
import lucuma.typed.primereact.primereactStrings.hover
import lucuma.typed.primereact.primereactStrings.left
import lucuma.typed.primereact.primereactStrings.right
import lucuma.typed.primereact.primereactStrings.self
import lucuma.typed.primereact.primereactStrings.top
import lucuma.typed.primereact.tooltipTooltipoptionsMod.TooltipEvent
import org.scalajs.dom.HTMLElement

import scala.scalajs.js.JSConverters.*

import scalajs.js

// `Tooltip` component with `target` is useful for sharing the tooltip among multiple elements.
//   Especially useful for tables.
// `Tooltip.Fragment` provides a convenient shorthand for when you want to use a tooltip with a single element.
//   It can be used with the syntax: .withTooltip(...)
case class Tooltip(
  appendTo:       js.UndefOr[Tooltip.AppendTo] = js.undefined,
  at:             js.UndefOr[String] = js.undefined,
  autoHide:       js.UndefOr[Boolean] = js.undefined,          // default: true
  autoZIndex:     js.UndefOr[Boolean] = js.undefined,          // default: true
  baseZIndex:     js.UndefOr[Int] = js.undefined,
  clazz:          js.UndefOr[Css] = js.undefined,
  content:        js.UndefOr[String] = js.undefined,
  disabled:       js.UndefOr[Boolean] = js.undefined,
  event:          js.UndefOr[Tooltip.Event] = js.undefined,
  hideDelay:      js.UndefOr[Int] = js.undefined,              // default: 0
  hideEvent:      js.UndefOr[String] = js.undefined,           // default: mouseleave
  id:             js.UndefOr[String] = js.undefined,
  mouseTrack:     js.UndefOr[Boolean] = js.undefined,          // default: mouseTrack
  mouseTrackLeft: js.UndefOr[Int] = js.undefined,              // default: 5
  mouseTrackTop:  js.UndefOr[Int] = js.undefined,              // default: 5
  my:             js.UndefOr[String] = js.undefined,
  onBeforeHide:   js.UndefOr[Tooltip.EventRaw => Callback] = js.undefined,
  onBeforeShow:   js.UndefOr[Tooltip.EventRaw => Callback] = js.undefined,
  onHide:         js.UndefOr[Tooltip.EventRaw => Callback] = js.undefined,
  onShow:         js.UndefOr[Tooltip.EventRaw => Callback] = js.undefined,
  position:       js.UndefOr[Tooltip.Position] = js.undefined, // default: right
  showDelay:      js.UndefOr[Int] = js.undefined,              // default: 0
  showEvent:      js.UndefOr[String] = js.undefined,           // default: mouseenter
  showOnDisabled: js.UndefOr[Boolean] = js.undefined,          // default: false
  target:         js.UndefOr[Tooltip.Target] = js.undefined,
  targetCss:      js.UndefOr[Css] = js.undefined,
  updateDelay:    js.UndefOr[Int] = js.undefined,              // default: 0
  modifiers:      Seq[TagMod] = Seq.empty
) extends ReactFnPropsWithChildren(Tooltip.component) {
  def addModifiers(modifiers: Seq[TagMod]) = copy(modifiers = this.modifiers ++ modifiers)

  // You can add content to the tag as children without this method, but if you want
  // to add event handlers or other attributes, you'll need to use the modifiers.
  def withMods(mods: TagMod*) = addModifiers(mods)
}

object Tooltip:
  enum AppendTo(val toJs: self | HTMLElement):
    case Self                          extends AppendTo(self)
    case Element(element: HTMLElement) extends AppendTo(element)

  enum Event(val toJs: hover | focus | both):
    case Hover extends Event(hover)
    case Focus extends Event(focus)
    case Both  extends Event(both)

  enum Position(val toJs: top | bottom | left | right):
    case Top    extends Position(top)
    case Bottom extends Position(bottom)
    case Left   extends Position(left)
    case Right  extends Position(right)

  type EventRaw = TooltipEvent

  // Strings are query selectors
  type Target = String | List[String] | HTMLElement

  private type TargetRaw = String | js.Array[String] | HTMLElement
  extension (t: Target)
    private def toJs: TargetRaw = t match
      case s: String       => s
      case l: List[String] => l.toJSArray
      case e: HTMLElement  => e

  private val component =
    ScalaFnComponent
      .withHooks[Tooltip]
      .withPropsChildren
      .render: (props, children) =>
        CTooltip
          .applyOrNot(props.appendTo, (c, p) => c.appendTo(p.toJs))
          .applyOrNot(props.at, _.at(_))
          .applyOrNot(props.autoHide, _.autoHide(_))
          .applyOrNot(props.autoZIndex, _.autoZIndex(_))
          .applyOrNot(props.baseZIndex, _.baseZIndex(_))
          .applyOrNot(props.clazz, (c, p) => c.className(p.htmlClass))
          .applyOrNot(props.content, _.content(_))
          .applyOrNot(props.disabled, _.disabled(_))
          .applyOrNot(props.event, (c, p) => c.event(p.toJs))
          .applyOrNot(props.hideDelay, _.hideDelay(_))
          .applyOrNot(props.hideEvent, _.hideEvent(_))
          .applyOrNot(props.id, _.id(_))
          .applyOrNot(props.mouseTrack, _.mouseTrack(_))
          .applyOrNot(props.mouseTrackLeft, _.mouseTrackLeft(_))
          .applyOrNot(props.mouseTrackTop, _.mouseTrackTop(_))
          .applyOrNot(props.my, _.my(_))
          .applyOrNot(props.onBeforeHide, _.onBeforeHide(_))
          .applyOrNot(props.onBeforeShow, _.onBeforeShow(_))
          .applyOrNot(props.onHide, _.onHide(_))
          .applyOrNot(props.onShow, _.onShow(_))
          .applyOrNot(props.position, (c, p) => c.position(p.toJs))
          .applyOrNot(props.showDelay, _.showDelay(_))
          .applyOrNot(props.showEvent, _.showEvent(_))
          .applyOrNot(props.showOnDisabled, _.showOnDisabled(_))
          .applyOrNot(props.target, (c, p) => c.target(p.toJs))
          .applyOrNot(props.targetCss, (c, p) => c.target(p.querySelector))
          .applyOrNot(props.updateDelay, _.updateDelay(_))(
            props.modifiers.toTagMod,
            children
          )

  case class Fragment(
    appendTo:       js.UndefOr[Tooltip.AppendTo] = js.undefined,
    at:             js.UndefOr[String] = js.undefined,
    autoHide:       js.UndefOr[Boolean] = js.undefined,          // default: true
    autoZIndex:     js.UndefOr[Boolean] = js.undefined,          // default: true
    baseZIndex:     js.UndefOr[Int] = js.undefined,
    clazz:          js.UndefOr[Css] = js.undefined,
    content:        js.UndefOr[VdomNode] = js.undefined,
    disabled:       js.UndefOr[Boolean] = js.undefined,
    event:          js.UndefOr[Tooltip.Event] = js.undefined,
    hideDelay:      js.UndefOr[Int] = js.undefined,              // default: 0
    hideEvent:      js.UndefOr[String] = js.undefined,           // default: mouseleave
    id:             js.UndefOr[String] = js.undefined,
    mouseTrack:     js.UndefOr[Boolean] = js.undefined,          // default: mouseTrack
    mouseTrackLeft: js.UndefOr[Int] = js.undefined,              // default: 5
    mouseTrackTop:  js.UndefOr[Int] = js.undefined,              // default: 5
    my:             js.UndefOr[String] = js.undefined,
    onBeforeHide:   js.UndefOr[Tooltip.EventRaw => Callback] = js.undefined,
    onBeforeShow:   js.UndefOr[Tooltip.EventRaw => Callback] = js.undefined,
    onHide:         js.UndefOr[Tooltip.EventRaw => Callback] = js.undefined,
    onShow:         js.UndefOr[Tooltip.EventRaw => Callback] = js.undefined,
    position:       js.UndefOr[Tooltip.Position] = js.undefined, // default: right
    showDelay:      js.UndefOr[Int] = js.undefined,              // default: 0
    showEvent:      js.UndefOr[String] = js.undefined,           // default: mouseenter
    showOnDisabled: js.UndefOr[Boolean] = js.undefined,          // default: false
    updateDelay:    js.UndefOr[Int] = js.undefined               // default: 0
  )(val tag: TagOf[HTMLElement])
      extends ReactFnPropsWithChildren(Fragment.component)

  object Fragment:
    private val component =
      ScalaFnComponent
        .withHooks[Fragment]
        .withPropsChildren
        // We use state instead of a regular Ref in order to force a rerender when it's set.
        .useState(none[HTMLElement])
        .render: (props, children, ref) =>
          React.Fragment(
            Tooltip(
              appendTo = props.appendTo,
              at = props.at,
              autoHide = props.autoHide,
              autoZIndex = props.autoZIndex,
              baseZIndex = props.baseZIndex,
              clazz = props.clazz,
              disabled = props.disabled,
              event = props.event,
              hideDelay = props.hideDelay,
              hideEvent = props.hideEvent,
              mouseTrack = props.mouseTrack,
              mouseTrackLeft = props.mouseTrackLeft,
              mouseTrackTop = props.mouseTrackTop,
              my = props.my,
              onBeforeHide = props.onBeforeHide,
              onBeforeShow = props.onBeforeShow,
              onHide = props.onHide,
              onShow = props.onShow,
              position = props.position,
              showDelay = props.showDelay,
              showEvent = props.showEvent,
              showOnDisabled = props.showOnDisabled,
              target = ref.value.orUndefined,
              updateDelay = props.updateDelay
            )(props.content),
            props.tag(^.untypedRef { (node: TopNode | Null) =>
              ref
                .modState(_.fold(Option(node.asInstanceOf[HTMLElement]))(_.some))
                .runNow()
            }.when(ref.value.isEmpty))
          )




© 2015 - 2024 Weber Informatics LLC | Privacy Policy