Skip to content

Instantly share code, notes, and snippets.

@DylanLukes
Last active April 12, 2017 16:23
Show Gist options
  • Save DylanLukes/c12dac25eaa2b99ce805ba124bf42ea4 to your computer and use it in GitHub Desktop.
Save DylanLukes/c12dac25eaa2b99ce805ba124bf42ea4 to your computer and use it in GitHub Desktop.
package com.emg.slidedeck.js.sui
import com.emg.slidedeck.js.macros.ScalaJSDefinedApply
import japgolly.scalajs.react.CtorType.ChildArg
import japgolly.scalajs.react.{Children, JsFnComponent}
import scala.scalajs.js
import scala.scalajs.js.{UndefOr, |}
import scala.scalajs.js.annotation.{JSImport, ScalaJSDefined}
object Grid {
val Row = GridRow
val Column = GridColumn
@JSImport("semantic-ui-react", "Grid")
@js.native
object Raw extends js.Object
@ScalaJSDefined
@ScalaJSDefinedApply
trait Props extends js.Object {
// TODO: Some of these methods could take Enums instead.
/** An element type to render as (string or function). */
val as: UndefOr[String | js.Function] = js.undefined
/** A grid can have rows divided into cells. */
val celled: UndefOr[Boolean | String] = js.undefined
/** A grid can have its columns centered. */
val centered: UndefOr[Boolean] = js.undefined
/** Additional classes. */
val className: UndefOr[String] = js.undefined
/** Represents column count per row in Grid. */
val columns: UndefOr[String] = js.undefined
/** A grid can be combined with a container to use avaiable layout and alignment. */
val container: UndefOr[Boolean] = js.undefined
/** A grid can have dividers between its columns. */
val divided: UndefOr[Boolean | String] = js.undefined
/** A grid can double its column width on tablet and mobile sizes. */
val doubling: UndefOr[Boolean] = js.undefined
/** A grid can preserve its vertical and horizontal gutters on first and last columns. */
val padded: UndefOr[Boolean | String] = js.undefined
/** A grid can increase its gutters to allow for more negative space. */
val relaxed: UndefOr[Boolean | String] = js.undefined
/** A grid can specify that its columns should reverse order at different device sizes. */
val reversed: UndefOr[String] = js.undefined
/** A grid can have its columns stack on-top of each other after reaching mobile breakpoints. */
val stackable: UndefOr[Boolean] = js.undefined
/** An can stretch its contents to take up the entire grid height. */
val stretched: UndefOr[Boolean] = js.undefined
/** A grid can specify its text alignment. */
val textAlign: UndefOr[String] = js.undefined
/** A grid can specify its vertical alignment to have all its columns vertically centered. */
val verticalAlign: UndefOr[String] = js.undefined
}
private val Component = JsFnComponent[Props, Children.Varargs](Raw)
def apply(P: Props = new Props {})(S: ChildArg*) = Component(P)(S: _*)
}
package com.emg.slidedeck.js.macros
import scala.collection.immutable.Seq
import scala.meta._
class ScalaJSDefinedApply extends scala.annotation.StaticAnnotation {
inline def apply(defn: Any): Any = meta {
def collectFields(trt: Defn.Trait) = {
trt.collect {
case dv @ Defn.Val(_, pats, Some(tpe), q"js.undefined") =>
pats.map { case Pat.Var.Term(name) => (name, tpe, q"js.undefined") }
case dv @ Defn.Val(_, _, None, q"js.undefined") =>
abort(s"@ScalaJSDefinedApply: $dv must have explicit type declarations.")
case dv @ Defn.Val(_, _, _, _) =>
abort(s"@ScalaJSDefinedApply: $dv must have = js.undefined.")
}.flatten
}
def genApply(tname: Type.Name, fields: Seq[(Term.Name, Type, Term)]): Defn.Def = {
val params = fields.map { case (name, tpe, default) =>
param"$name: $tpe = $default"
}
val args = fields.map { case (name, _, _) =>
arg"(${Lit.String(name.value)}, ${name}.asInstanceOf[js.Any])"
}
q"def apply(..$params): $tname = js.Dynamic.literal(..$args).asInstanceOf[$tname]"
}
defn match {
// A companion Object already exists in scope. Modify it.
case Term.Block(Seq(
dt @ Defn.Trait(_, tname, _, _, _),
companion: Defn.Object
)) =>
val fields = collectFields(dt)
val applyMethod = genApply(tname, fields)
Term.Block(Seq(dt, companion.copy(
templ = companion.templ.copy(
stats = companion.templ.stats.map { applyMethod +: _ }
))
))
// A companion Object does not yet exist.
case dt @ Defn.Trait(_, tname, _, _, _) =>
val fields = collectFields(dt)
val applyMethod = genApply(tname, fields)
val companion = q"object ${Term.Name(tname.value)} { $applyMethod }"
// println(companion)
Term.Block(Seq(dt, companion))
case _ =>
abort("@ScalaJSDefinedApply must annotate a trait.")
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment