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

com.dispalt.vdom.ReactAttr.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2016 Dan Di Spaltro
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.dispalt.vdom

import com.dispalt.vdom.ReactAttr.ValueType

import scala.annotation.implicitNotFound
import scala.scalajs.LinkingInfo.developmentMode
import scala.scalajs.js

trait ReactAttr {
  def name: String
  def :=[A](a: A)(implicit t: ValueType[A]): TagMod
}

object ReactAttr {

  @inline def apply(name: String): ReactAttr =
    Generic(name)

  final case class Generic(name: String) extends ReactAttr {
    Escaping.assertValidAttrName(name)
    override def :=[A](a: A)(implicit t: ValueType[A]): TagMod =
      new NameAndValue(name, a, t)
  }

  object Dud extends ReactAttr {
    override def name =
      ""
    override def :=[A](a: A)(implicit t: ValueType[A]): TagMod =
      EmptyTag
  }

  @inline def devOnly(name: => String): ReactAttr =
    if (developmentMode)
      Generic(name)
    else
      Dud

  case object ClassName extends ReactAttr {
    override def name = "class"
    override def :=[A](a: A)(implicit t: ValueType[A]): TagMod =
      TagMod.fn(b => t.apply(b.addClassName, a))
  }

  case object Ref extends ReactAttr {
    override def name = "ref"
    override def :=[A](a: A)(implicit t: ValueType[A]): TagMod =
      new ReactAttr.NameAndValue(name, a, t)

    import Implicits._react_attrJsFn
    def apply[N <: TopNode](f: N => Unit): TagMod =
      this := ((f: js.Function1[N, Unit]): js.Function)
  }

  implicit val ordering: Ordering[ReactAttr] =
    Ordering.by((_: ReactAttr).name)

  final class NameAndValue[A](val name: String, val value: A, val valueType: ValueType[A]) extends TagMod {
    override def applyTo(b: Builder): Unit =
      valueType.apply(b.addAttr(name, _), value)
  }

  // ===================================================================================================================

  /**
    * Used to specify how to handle a particular type [[A]] when it is used as
    * the value of a [[ReactAttr]]. Only types with a specified [[ReactAttr.ValueType]] may
    * be used.
    */
  @implicitNotFound("No Attr.ValueType defined for type ${A}; don't know how to use ${A} as an attribute.")
  final class ValueType[A](val apply: ValueType.Fn[A]) extends AnyVal

  object ValueType {
    type Fn[A] = (js.Any => Unit, A) => Unit

    @inline def apply[A](fn: Fn[A]): ValueType[A] =
      new ValueType(fn)

    val string: ValueType[String] =
      apply(_(_))

    def map[A](implicit f: A => js.Any): ValueType[A] =
      apply((b, a) => b(f(a)))

    def array[A](implicit f: A => js.Any): ValueType[js.Array[A]] =
      map(_ map f)

    def optional[T, A](ot: Option[T], vt: ValueType[A]): ValueType[Option[A]] =
      apply((b, ta) => ta.foreach(vt.apply(b, _)))
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy