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, _)))
}
}