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

scalacss.Media.scala Maven / Gradle / Ivy

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

import scalaz.{Equal, \/}
import scalaz.syntax.equal._
import scalacss.{Resolution => Res}

object Media {

  /** bits per color component */
  type ColorBits = Int

  sealed abstract class OrientationValue(final val value: String)
  case object Landscape extends OrientationValue("landscape")
  case object Portrait  extends OrientationValue("portrait")

  sealed abstract class ScanValue(final val value: String)
  case object Progressive extends ScanValue("progressive")
  case object Interface   extends ScanValue("interface")

  sealed trait ValueExpr[T]
  case class Eql[T](eql: T) extends ValueExpr[T]
  case class Max[T](max: T) extends ValueExpr[T]
  case class Min[T](min: T) extends ValueExpr[T]

  sealed trait Feature

  /** Indicates the number of bits per color component of the output device.  If the device is not a color device, this value is zero. */
  case class Color(bits: Option[ValueExpr[ColorBits]]) extends Feature

  /** Indicates the number of entries in the color look-up table for the output device. */
  case class ColorIndex(index: Option[ValueExpr[Int]]) extends Feature

  /** Describes the aspect ratio of the targeted display area of the output device. */
  case class AspectRatio(ratio: ValueExpr[Ratio]) extends Feature

  /** The height media feature describes the height of the output device's rendering surface (such as the height of the viewport or of the page box on a printer). */
  case class Height[N](length: ValueExpr[Length[N]]) extends Feature

  case class Width[N](length: ValueExpr[Length[N]]) extends Feature

  /** Describes the aspect ratio of the output device. */
  case class DeviceAspectRatio(ratio: ValueExpr[Ratio]) extends Feature

  /** Describes the height of the output device (meaning the entire screen or page, rather than just the rendering area, such as the document window). */
  case class DeviceHeight[N](length: ValueExpr[Length[N]]) extends Feature

  /** Describes the width of the output device (meaning the entire screen or page, rather than just the rendering area, such as the document window). */
  case class DeviceWidth[N](length: ValueExpr[Length[N]]) extends Feature

  /** Indicates the number of bits per pixel on a monochrome (greyscale) device.  If the device isn't monochrome, the device's value is 0. */
  case class Monochrome(bitsPerPx: Option[ValueExpr[Int]]) extends Feature

  /** Indicates the resolution (pixel density) of the output device. */
  case class Resolution[N](res: ValueExpr[Res[N]]) extends Feature

  /** Indicates whether the viewport is in landscape (the display is wider than it is tall) or portrait (the display is taller than it is wide) mode. */
  case class Orientation(value: OrientationValue) extends Feature

  /** Describes the scanning process of television output devices. */
  case class Scan(value: ScanValue) extends Feature

  /** Determines whether the output device is a grid device or a bitmap device.  If the device is grid-based (such as a TTY terminal or a phone display with only one font), the value is 1.  Otherwise it is zero. */
  case class Grid(value: Option[Int]) extends Feature

  sealed abstract class TypeA(final val value: String)
  case object All extends TypeA("all")

  sealed abstract class Type(value: String) extends TypeA(value)
  case object Aural      extends Type("aural")
  case object Braille    extends Type("braille")
  case object Embossed   extends Type("embossed")
  case object Handheld   extends Type("handheld")
  case object Print      extends Type("print")
  case object Projection extends Type("projection")
  case object Screen     extends Type("screen")
  case object TTY        extends Type("tty")
  case object TV         extends Type("tv")

  sealed trait TypeExpr
  case class Just(t: TypeA) extends TypeExpr
  case class Only(t: TypeA) extends TypeExpr
  case class Not (t: TypeA) extends TypeExpr

  // Hmmm, well this will never be true when false, it shouldn't be false when true.
  // Should really use Shapeless' typeclass derivation here...
  implicit def queryEquality: Equal[Query] = Equal.equalA

  case class Query(head: TypeExpr \/ Feature, tail: Vector[Feature]) extends FeatureOps[Query] {
    override protected def F = f => new Query(head, tail :+ f)

    override def toString =
      css(NonEmptyVector one this)

    def +:(qs: Vector[Query]): Vector[Query] =
      if (qs.exists(_ === this)) qs else qs :+ this

    def +(q2: Query): Vector[Query] =
      Vector1(this) +: q2

    def &(q2: Query): Cond =
      Cond(None, this + q2)
  }

  trait TypeAOps[Out] {
    protected def T: TypeA => Out
    final def all       : Out = T(All)
    final def aural     : Out = T(Aural)
    final def braille   : Out = T(Braille)
    final def embossed  : Out = T(Embossed)
    final def handheld  : Out = T(Handheld)
    final def print     : Out = T(Print)
    final def projection: Out = T(Projection)
    final def screen    : Out = T(Screen)
    final def tty       : Out = T(TTY)
    final def tv        : Out = T(TV)
  }

  trait FeatureOps[Out] {
    protected def F: Feature => Out

    final def color                 : Out = F(Color(None))
    final def color   (v: ColorBits): Out = F(Color(Some(Eql(v))))
    final def minColor(v: ColorBits): Out = F(Color(Some(Min(v))))
    final def maxColor(v: ColorBits): Out = F(Color(Some(Max(v))))

    final def colorIndex           : Out = F(ColorIndex(None))
    final def colorIndex   (v: Int): Out = F(ColorIndex(Some(Eql(v))))
    final def minColorIndex(v: Int): Out = F(ColorIndex(Some(Min(v))))
    final def maxColorIndex(v: Int): Out = F(ColorIndex(Some(Max(v))))

    final def aspectRatio   (v: Ratio): Out = F(AspectRatio(Eql(v)))
    final def minAspectRatio(v: Ratio): Out = F(AspectRatio(Min(v)))
    final def maxAspectRatio(v: Ratio): Out = F(AspectRatio(Max(v)))

    final def height   [N](v: Length[N]): Out = F(Height(Eql(v)))
    final def minHeight[N](v: Length[N]): Out = F(Height(Min(v)))
    final def maxHeight[N](v: Length[N]): Out = F(Height(Max(v)))

    final def width   [N](v: Length[N]): Out = F(Width(Eql(v)))
    final def minWidth[N](v: Length[N]): Out = F(Width(Min(v)))
    final def maxWidth[N](v: Length[N]): Out = F(Width(Max(v)))

    final def deviceAspectRatio   (v: Ratio): Out = F(DeviceAspectRatio(Eql(v)))
    final def minDeviceAspectRatio(v: Ratio): Out = F(DeviceAspectRatio(Min(v)))
    final def maxDeviceAspectRatio(v: Ratio): Out = F(DeviceAspectRatio(Max(v)))

    final def deviceHeight   [N](v: Length[N]): Out = F(DeviceHeight(Eql(v)))
    final def minDeviceHeight[N](v: Length[N]): Out = F(DeviceHeight(Min(v)))
    final def maxDeviceHeight[N](v: Length[N]): Out = F(DeviceHeight(Max(v)))

    final def deviceWidth   [N](v: Length[N]): Out = F(DeviceWidth(Eql(v)))
    final def minDeviceWidth[N](v: Length[N]): Out = F(DeviceWidth(Min(v)))
    final def maxDeviceWidth[N](v: Length[N]): Out = F(DeviceWidth(Max(v)))

    final def monochrome           : Out = F(Monochrome(None))
    final def monochrome   (v: Int): Out = F(Monochrome(Some(Eql(v))))
    final def minMonochrome(v: Int): Out = F(Monochrome(Some(Min(v))))
    final def maxMonochrome(v: Int): Out = F(Monochrome(Some(Max(v))))

    final def resolution   [N](v: Res[N]): Out = F(Resolution(Eql(v)))
    final def minResolution[N](v: Res[N]): Out = F(Resolution(Min(v)))
    final def maxResolution[N](v: Res[N]): Out = F(Resolution(Max(v)))

    final def orientation(v: OrientationValue): Out = F(Orientation(v))
    final def landscape                       : Out = F(Orientation(Landscape))
    final def portrait                        : Out = F(Orientation(Portrait))

    final def scan(v: ScanValue): Out = F(Scan(v))
    final def progressive       : Out = F(Scan(Progressive))
    final def interface         : Out = F(Scan(Interface))

    final def grid        : Out = F(Grid(None))
    final def grid(v: Int): Out = F(Grid(Some(v)))
  }

  // Ensure string concat doesn't accidentally call toString on something stupid.
  @inline private implicit class StringExt(val _s: String) extends AnyVal {
    @inline def ~(b: String) = _s + b
  }

  @inline private final def media = "@media "
  @inline private final def `:`   = ":"
  @inline private final def and   = " and "
  @inline private final def `,`   = ", "
  @inline private final def `(`   = "("
  @inline private final def `)`   = ")"
  @inline private final def min   = "min-"
  @inline private final def max   = "max-"

  private def paren(c: String): String =
    `(` ~ c ~ `)`

  private def opt[T](ov: Option[T], name: String)(implicit tc: T => String): String =
    ov.fold(name)(name ~ `:` ~ _)

  private def cssValueExpr[T](e: ValueExpr[T], name: String)(implicit tc: T => String): String =
    e match {
      case Eql(t) =>       name ~ `:` ~ t
      case Min(t) => min ~ name ~ `:` ~ t
      case Max(t) => max ~ name ~ `:` ~ t
    }

  private def cssValueExprO[T](o: Option[ValueExpr[T]], name: String)(implicit tc: T => String): String =
    o.fold(name)(cssValueExpr(_, name))

  private implicit def cssInt   (i: Int)      : String = i.toString
  private implicit def cssRes   (r: Res[_])   : String = r.value
  private implicit def cssLength(l: Length[_]): String = l.value
  private implicit def cssRatio (r: Ratio)    : String = r.value

  val cssFeature: Feature => String = {
    val x: Feature => String = {
      case AspectRatio(v)       => cssValueExpr (v, "aspect-ratio")
      case Height(v)            => cssValueExpr (v, "height")
      case Width(v)             => cssValueExpr (v, "width")
      case DeviceAspectRatio(v) => cssValueExpr (v, "device-aspect-ratio")
      case DeviceHeight(v)      => cssValueExpr (v, "device-height")
      case DeviceWidth(v)       => cssValueExpr (v, "device-width")
      case Color(v)             => cssValueExprO(v, "color")
      case ColorIndex(v)        => cssValueExprO(v, "color-index")
      case Resolution(v)        => cssValueExpr (v, "resolution")
      case Monochrome(v)        => cssValueExprO(v, "monochrome")
      case Orientation(v)       => "orientation:" ~ v.value
      case Scan(v)              => "scan:"        ~ v.value
      case Grid(v)              => opt(v, "grid")
    }
    // Parentheses are required around expressions; failing to use them is an error.
    f => paren(x(f))
  }

  val cssTypeExpr: TypeExpr => String = {
    case Just(t) => t.value
    case Not(t)  => "not "  ~ t.value // Applies to whole Query. Don't wrap in parenthesis.
    case Only(t) => "only " ~ t.value // Applies to whole Query. Don't wrap in parenthesis.
  }

  def cssQuery(q: Query): String = {
    val z = q.head.fold(cssTypeExpr, cssFeature)
    q.tail.foldLeft(z)(_ ~ and ~ cssFeature(_))
  }

  def cssQueries(qs: NonEmptyVector[Query]): String =
    qs.reduceMapLeft1(cssQuery)(_ ~ `,` ~ _)

  def css(qs: NonEmptyVector[Query]): CssMediaQuery =
    media ~ cssQueries(qs)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy