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

scalacss.ValueT.scala Maven / Gradle / Ivy

There is a newer version: 0.5.6
Show newest version
package scalacss

import java.util.concurrent.TimeUnit
import scala.concurrent.duration.FiniteDuration
import scalacss.{Literal => L}
import L.{Typed => LT}

trait ToAV {
  def av: AV
}

/**
 * A CSS value that is valid for some context `T`.
 */
final case class ValueT[T <: ValueT.ValueClass](value: Value) {
  @inline def retype[A <: ValueT.ValueClass] = this.asInstanceOf[ValueT[A]]
}

object ValueT {

  // TODO ValueT.mkString won't handle strings with both ' and "
  def mkString(s: String): Value =
    if (s contains "'")
      s""""$s""""
    else
      s"'$s'"

  @inline def mkStrings(a: String, sep: String, b: String): Value =
    mkString(a) + sep + mkString(b)

  /*
  sealed trait ValueT[T]
  def ValueT[T](v: Value) = v.asInstanceOf[ValueT[T]]
  implicit class ValueTExt[T](val v: ValueT[T]) extends AnyVal {
    def value     = v.asInstanceOf[Value]
    def retype[A] = v.asInstanceOf[ValueT[A]]
  }
  */

  /**
   * Represented a context in which a finite set of types are valid.
   *
   * Example:
   * {{{
   *   <br-width> = <length> | thin | medium | thick
   * }}}
   */
  sealed trait ValueClass

  sealed trait Integer    extends ValueClass
  sealed trait Number     extends ValueClass
  sealed trait Time       extends ValueClass
  sealed trait Len        extends ValueClass
  sealed trait Pct        extends ValueClass
  sealed trait LenPct     extends ValueClass
  sealed trait LenPctAuto extends ValueClass
  sealed trait LenPctNum  extends ValueClass
  sealed trait BrWidth    extends ValueClass
  sealed trait BrStyle    extends ValueClass
  sealed trait Color      extends ValueClass
  sealed trait WidStyCol  extends ValueClass


  // =========
  //   Rules
  // =========

  type ==>[From              , To   <: ValueClass] = Rule[From, To]
  type >=>[From <: ValueClass, To   <: ValueClass] = ValueT[From] ==> To
  type <==[To   <: ValueClass, From              ] = From ==> To
  type <=<[To   <: ValueClass, From <: ValueClass] = From >=> To

  abstract class Rule[A, B <: ValueClass] {
    def apply(a: A): ValueT[B]

    final def >>[C <: ValueClass](f: B >=> C): A ==> C =
      Rule.applyT(a => f(this(a)))
  }

  object Rule {
    def apply[From, To <: ValueClass](f: From => Value): From ==> To =
      applyT(x => ValueT(f(x)))

    def applyT[From, To <: ValueClass](f: From => ValueT[To]): From ==> To =
      new Rule[From, To] {
        override def apply(x: From): ValueT[To] = f(x)
      }

    @inline def retype[FromT <: ValueClass, To <: ValueClass]: FromT >=> To =
      applyT(_.retype)

    @inline def literal[L <: Literal, To <: ValueClass]: L ==> To =
      apply(_.value)
  }

  object Rules extends Rules
  abstract class Rules {
    @inline implicit def ruleApply[From, To <: ValueClass](f: From)(implicit r: From ==> To): ValueT[To] =
      r(f)

    @inline implicit def ruleChain[A, B <: ValueClass, C <: ValueClass](implicit ab: A ==> B, bc: B >=> C): A ==> C =
      ab >> bc

    @inline implicit def ruleAnywhere[L <: Literal with LT.Anywhere, To <: ValueClass]: L ==> To =
      Rule.literal

    @inline implicit def ruleLen_L[N] : Len     <== Length[N]      = Rule(_.value)
    @inline implicit def rulePct_P[N] : Pct     <== Percentage[N]  = Rule(_.value)
    @inline implicit def ruleInteger_I: Integer <== Int            = Rule(_.toString)
    @inline implicit def ruleNumber_I : Number  <== Int            = Rule(_.toString)
    @inline implicit def ruleNumber_D : Number  <== Double         = Rule(_.toString)
    @inline implicit def ruleTime_FD  : Time    <== FiniteDuration =
      Rule(d => d.unit match {
        case TimeUnit.MICROSECONDS
           | TimeUnit.MILLISECONDS
           | TimeUnit.NANOSECONDS => d.toMillis + "ms"
        case _                    => d.toSeconds + "s"
      })

    @inline implicit def ruleLenPct_L                               : LenPct     <=< Len          = Rule.retype
    @inline implicit def ruleLenPct_P                               : LenPct     <=< Pct          = Rule.retype
    @inline implicit def ruleLenPctAuto_LP                          : LenPctAuto <=< LenPct       = Rule.retype
    @inline implicit def ruleLenPctAuto_A                           : LenPctAuto <== LT.auto.type = Rule.literal
    @inline implicit def ruleLenPctNum_LP                           : LenPctNum  <=< LenPct       = Rule.retype
    @inline implicit def ruleLenPctNum_N                            : LenPctNum  <=< Number       = Rule.retype
    @inline implicit def ruleBrWidth_1                              : BrWidth    <=< Len          = Rule.retype
    @inline implicit def ruleBrWidth_2[L <: Literal with LT.BrWidth]: BrWidth    <== L            = Rule.literal
    @inline implicit def ruleBrStyle_L[L <: Literal with LT.BrStyle]: BrStyle    <== L            = Rule.literal
    @inline implicit def ruleWidStyCol_W                            : WidStyCol  <=< BrWidth      = Rule.retype
    @inline implicit def ruleWidStyCol_S                            : WidStyCol  <=< BrStyle      = Rule.retype
    @inline implicit def ruleWidStyCol_C                            : WidStyCol  <=< Color        = Rule.retype

    // diverging implicit expansion requires these ↙ :(
    @inline implicit def ruleWidStyCol_L : WidStyCol  <=< Len = Rule.retype
    @inline implicit def ruleWidStyCol_P : WidStyCol  <=< Pct = Rule.retype
    @inline implicit def ruleLenPctNum_I : LenPctNum  <== Int = Rule(_.toString)
    @inline implicit def ruleLenPctNum_L : LenPctNum  <=< Len = Rule.retype
    @inline implicit def ruleLenPctNum_P : LenPctNum  <=< Pct = Rule.retype
    @inline implicit def ruleLenPctAuto_L: LenPctAuto <=< Len = Rule.retype
    @inline implicit def ruleLenPctAuto_P: LenPctAuto <=< Pct = Rule.retype
  }


  // ================================
  // Attributes with type-safe values
  // ================================

  object TypedAttrBase {
    implicit def `Be the attr you were born to be!`(t: TypedAttrBase): Attr = t.attr
  }

  abstract class TypedAttrBase {
    val attr: Attr
    protected def av(v: Value)   : AV = AV(attr, v)
    protected def avl(v: Literal): AV = av(v.value)

    override def toString = attr.toString

    /**
     * The inherit CSS-value causes the element for which it is specified to take the computed value of the property from its parent element. It is allowed on every CSS property.
     *
     * For inherited properties, this reinforces the default behavior, and is only needed to override another rule.  For non-inherited properties, this specifies a behavior that typically makes relatively little sense and you may consider using initial instead, or unset on the all property.
     */
    def inherit = avl(L.inherit)

    /** The initial CSS keyword applies the initial value of a property to an element. It is allowed on every CSS property and causes the element for which it is specified to use the initial value of the property. */
    def initial = avl(L.initial)

    /** The unset CSS keyword is the combination of the initial and inherit keywords. Like these two other CSS-wide keywords, it can be applied to any CSS property, including the CSS shorthand all. This keyword resets the property to its inherited value if it inherits from its parent or to its initial value if not. In other words, it behaves like the inherit keyword in the first case and like the initial keyword in the second case. */
    def unset = avl(L.unset)
  }

  abstract class TypedAttrT1[T <: ValueClass] extends TypedAttrBase {
    final def apply(v: ValueT[T]): AV = av(v.value)
//        final def apply[From](f: From)(implicit r: From ==> T): AV = av(r(f).value)
  }

  @inline private[scalacss] def concat(sep: String, a: ValueT[_], b: ValueT[_]): Value =
    a.value + sep + b.value
  @inline private[scalacss] def concat(sep: String, a: ValueT[_], b: ValueT[_], c: ValueT[_]): Value =
    concat(sep, a, b) + sep + c.value
  @inline private[scalacss] def concat(sep: String, a: ValueT[_], b: ValueT[_], c: ValueT[_], d: ValueT[_]): Value =
    concat(sep, a, b, c) + sep + d.value

  abstract class TypedAttrT2[T <: ValueClass] extends TypedAttrBase {
    final def apply(both: ValueT[T])                           : AV = av(both.value)
    final def apply(horizontal: ValueT[T], vertical: ValueT[T]): AV = av(concat(" ", horizontal, vertical))
  }

  abstract class TypedAttrT3[T <: ValueClass](sep: String) extends TypedAttrBase {
    final def apply(a: ValueT[T])                            : AV = av(a.value)
    final def apply(a: ValueT[T], b: ValueT[T])              : AV = av(concat(sep, a, b))
    final def apply(a: ValueT[T], b: ValueT[T], c: ValueT[T]): AV = av(concat(sep, a, b, c))
  }

  abstract class TypedAttrT4Edges[T <: ValueClass](sep: String) extends TypedAttrBase {
    final type V = ValueT[T]

    protected def attrT: Attr
    protected def attrR: Attr
    protected def attrB: Attr
    protected def attrL: Attr

    /** Applies the same setting to all four edges. */
    final def apply(all: V): AV =
      av(all.value)

    /**
     * @param v Value for vertical edges.
     * @param h Value for horizontal edges.
     */
    final def apply(v: V, h: V): AV =
      av(concat(sep, v, h))

    /**
     * @param t Value for top edge.
     * @param h Value for horizontal edges.
     * @param b Value for bottom edge.
     */
    final def apply(t: V, h: V, b: V): AV =
      av(concat(sep, t, h, b))

    /**
     * @param t Value for top edge.
     * @param r Value for right edge.
     * @param b Value for bottom edge.
     * @param l Value for left edge.
     */
    final def apply(t: V, r: V, b: V, l: V): AV =
      av(concat(sep, t, r, b, l))

    final def horizontal(h: V): AVs =
      AVs(attrL, h.value).add(attrR, h.value)

    final def vertical(v: V): AVs =
      AVs(attrT, v.value).add(attrB, v.value)
  }

  abstract class TypedAttrTN[T <: ValueClass](sep: String) extends TypedAttrBase {
    final def apply(v1: ValueT[T], vn: ValueT[T]*): AV =
      av(vn.foldLeft(v1.value)(_ + sep + _.value))
  }

  abstract class TypedAttr_BrWidth extends TypedAttrT1[BrWidth]
    with BrWidthOps

  abstract class TypedAttr_BrStyle extends TypedAttrBase //TypedAttrT[BrStyle]
    with BrStyleOps

  abstract class TypedAttr_Color extends TypedAttrT1[Color]
    with ColourOps

  abstract class TypedAttr_Shape extends TypedAttrBase {
    final def rect(top: ValueT[Len], right: ValueT[Len], bottom: ValueT[Len], left: ValueT[Len]) =
      av(s"rect(${top.value},${right.value},${bottom.value},${left.value})")
  }

  abstract class TypedAttr_BrWidthStyleColour extends TypedAttrT3[WidStyCol](" ")
    with BrWidthOps with BrStyleOps with ColourOps

  abstract class TypedAttr_LenPctAuto extends TypedAttrT1[LenPctAuto] with ZeroLit {
    final def auto = avl(LT.auto)
  }

  abstract class TypedAttr_Length extends TypedAttrT1[LenPct] with ZeroLit {
    override val attr = attr2(CanIUse2.intrinsicWidthTransforms)
    protected def attr2: Transform => Attr
    final def auto       = avl(LT.auto)
    final def available  = av(L.available)
    final def borderBox  = av(L.borderBox)
    final def contentBox = av(L.contentBox)
    final def fitContent = av(L.fitContent)
    final def maxContent = av(L.maxContent)
    final def minContent = av(L.minContent)
  }

  abstract class TypedAttr_MaxLength extends TypedAttrT1[LenPct] with ZeroLit {
    override val attr = attr2(CanIUse2.intrinsicWidthTransforms)
    protected def attr2: Transform => Attr
    final def fillAvailable = av(L.fillAvailable)
    final def fitContent    = av(L.fitContent)
    final def maxContent    = av(L.maxContent)
    final def minContent    = av(L.minContent)
    final def none          = avl(LT.none)
    final def containFloats = av(L.containFloats)
  }

  abstract class TypedAttr_MinLength extends TypedAttrT1[LenPct] with ZeroLit {
    override val attr = attr2(CanIUse2.intrinsicWidthTransforms)
    protected def attr2: Transform => Attr
    final def auto          = avl(LT.auto)
    final def fillAvailable = av(L.fillAvailable)
    final def fitContent    = av(L.fitContent)
    final def maxContent    = av(L.maxContent)
    final def minContent    = av(L.minContent)
    final def containFloats = av(L.containFloats)
  }


  // ================
  // Operation mixins
  // ================

  trait ZeroLit {
    this: TypedAttrBase =>

    /** The digit zero. `"0"`. */
    final def `0` = av("0")

    // final def apply(i: zeroType.T) = av("0")
    // "overloaded method value apply with alternatives"
  }

  trait BrWidthOps extends ZeroLit {
    this: TypedAttrBase =>
    final def thin   = avl(LT.thin)
    final def medium = avl(LT.medium)
    final def thick  = avl(LT.thick)
  }

  trait BrStyleOps {
    this: TypedAttrBase =>
    final def none   = avl(LT.none)
    final def hidden = avl(LT.hidden)
    final def dotted = avl(LT.dotted)
    final def dashed = avl(LT.dashed)
    final def solid  = avl(LT.solid)
    final def double = avl(LT.double)
    final def groove = avl(LT.groove)
    final def ridge  = avl(LT.ridge)
    final def inset  = avl(LT.inset)
    final def outset = avl(LT.outset)
  }

  trait ColourOps extends ColorOps[AV] {
    this: TypedAttrBase =>
    override protected final def mkColor(s: String): AV = av(s)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy