diff --git a/build.sbt b/build.sbt index 4725f5357..507ef83ed 100644 --- a/build.sbt +++ b/build.sbt @@ -13,4 +13,5 @@ val testsChrome = Build.testsChrome val testsFirefox = Build.testsFirefox val testsNodeJsdom = Build.testsNodeJsdom val example = Build.example -val readme = Build.readme +val jsdocs = Build.jsdocs +val docs = Build.docs diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 000000000..733d0a335 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,225 @@ +--- +layout: doc +title: scalajs-dom +--- + +#### Statically typed DOM API for Scala.js + +Scalajs-dom provides a nice statically typed interface to the DOM such that it can be called from Scala code without resorting to `js.Dynamic`. +All javascript globals functions, singletons and classes are members of the `org.scalajs.dom`, +`org.scalajs.dom.html`, `org.scalajs.dom.svg`, etc. packages. + +For example: + +```scala +import org.scalajs.dom + +def main() = dom.window.alert("Hi from scala-js-dom!") +``` + +## Usage + +Add the following to your sbt build definition: + +```scala +libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "@VERSION@" +``` + +then enjoy the types available in org.scalajs.dom. scalajs-dom @VERSION@ is built and published for Scala.js 1.7+ with Scala 2.11, 2.12, 2.13, and 3.1+. + +To begin with, scalajs-dom organizes the full-list of DOM APIs into a number of buckets: + +- dom.html: HTML element APIs +- dom.svg: SVG element APIs +- dom.idb: IndexedDB APIs +- dom: Miscellanious, unclassified APIs + +Most names have been shortened from names of the raw browser APIs, since the namespacing avoids collisions. By convention these types are imported qualified: e.g. as `html.Canvas` instead of directly as `Canvas`. + +## Examples + +You can start using the bindings using the following import: + +```scala mdoc:js:shared +import org.scalajs.dom._ +``` + +### Appending a child to a `Node` + +```scala mdoc:js:shared +def appendElement(div: html.Div): Unit = { + val child = document.createElement("div") + child.textContent = "I can add elements to DOM elements!" + div.appendChild(child) +} +``` + +```scala mdoc:js:invisible +
+ +--- +document.getElementById("demo1-btn").addEventListener("click", (ev: Event) => { + appendElement(document.getElementById("outer-container").asInstanceOf[html.Div]) +}) +``` + +### Add an EventListener for `onmousemove` + +```scala mdoc:js:shared +def showOnMouseCoordinates(mouseArea: html.Div, info: html.Pre): Unit = { + mouseArea.onmousemove = (ev: MouseEvent) => + info.textContent = s""" + |ev.clientX: ${ev.clientX} + |ev.clientY: ${ev.clientY} + |ev.pageX: ${ev.pageX} + |ev.screenX: ${ev.screenX} + |ev.screenY: ${ev.screenY} + """.stripMargin +} +``` + +```scala mdoc:js:invisible +
+
+

+
+--- +val mouseArea = document.getElementById("mouse-container").asInstanceOf[html.Div] +val info = document.getElementById("demo2-output").asInstanceOf[html.Pre] +showOnMouseCoordinates(mouseArea, info) +``` + +### Storing an item in `localStorage` + +```scala mdoc:js:shared +def storeInputInLocalStorage(input: html.Input, box: html.Div) = { + val key = "myKey" + input.value = window.localStorage.getItem(key) + + input.onkeyup = { (e: Event) => + window.localStorage.setItem(key, input.value) + + box.textContent = s"Saved: ${input.value} to local storage!" + } +} +``` + +```scala mdoc:js:invisible + +
+ +--- +val input = document.getElementById("demo3-input").asInstanceOf[html.Input] +val output = document.getElementById("demo3-output").asInstanceOf[html.Div] +storeInputInLocalStorage(input, output) +``` + +### Using `Canvas` to draw + +```scala mdoc:js:shared +type Context2D = CanvasRenderingContext2D + +def drawCuteSmiley(canvas: html.Canvas) = { + val context = canvas.getContext("2d").asInstanceOf[Context2D] + + val size = 150 + canvas.width = size + canvas.height = size + + context.strokeStyle = "red" + context.lineWidth = 3 + context.beginPath() + context.moveTo(size/3, 0) + context.lineTo(size/3, size/3) + context.moveTo(size*2/3, 0) + context.lineTo(size*2/3, size/3) + context.moveTo(size, size/2) + context.arc(size/2, size/2, size/2, 0, 3.14) + + context.stroke() +} +``` + +```scala mdoc:js:invisible + +--- + +val canvas = document.getElementById("demo4-canvas").asInstanceOf[html.Canvas] +drawCuteSmiley(canvas) +``` + +### Using `Fetch` to make API calls in the browser + +```scala mdoc:js:shared +import scala.concurrent.ExecutionContext.Implicits.global + +def fetchBoredApi(element: html.Pre) = { + val url = "https://www.boredapi.com/api/activity" + + val responseText = for { + response <- fetch(url).toFuture + text <- response.text().toFuture + } yield { + text + } + + for (text <- responseText) + element.textContent = text +} +``` + +```scala mdoc:js:invisible +

+
+---
+val output = document.getElementById("demo5-output").asInstanceOf[html.Pre]
+document.getElementById("demo5-btn").addEventListener("click", (ev: Event) => {
+  fetchBoredApi(output)
+})
+```
+
+### Styling an HTML element
+
+```scala mdoc:js:shared
+def changeColor(div: html.Div) = {
+  val colors = Seq("red", "green", "blue")
+  val index = util.Random.nextInt(colors.length)
+
+  div.style.color = colors(index)
+}
+```
+
+```scala mdoc:js:invisible
+
Color me!
+ +--- +document.getElementById("demo6-btn").addEventListener("click", (ev: Event) => { + changeColor(document.getElementById("demo6-text").asInstanceOf[html.Div]) +}) +``` + +### Encode in base64 + +```scala mdoc:js:shared +def encodeBase64(in: html.Input, out: html.Div) = { + in.onkeyup = { (e: Event) => + out.textContent = window.btoa(in.value) + } +} +``` + +```scala mdoc:js:invisible + +
+--- +val input = document.getElementById("demo7-input").asInstanceOf[html.Input] +val output = document.getElementById("demo7-output").asInstanceOf[html.Div] +encodeBase64(input, output) +``` + +## Contributing + +The DOM API is always evolving, and `scalajs-dom` tries to provide a thin-but-idiomatic Scala interface to modern browser APIs, without breaking the spec. + +If you see something that you think can be improved, feel free to send a pull request. +See our [Contributing Guide](https://github.com/scala-js/scala-js-dom/blob/main/CONTRIBUTING.md) for a detailed overview for starting hacking on `scala-js-dom` and making a PR! diff --git a/prePR.sbt b/prePR.sbt index 100a174e9..34733824c 100644 --- a/prePR.sbt +++ b/prePR.sbt @@ -41,7 +41,8 @@ ThisBuild / prePR_nonCross := Def.sequential( Def.taskDyn { if (scalaVersion.value.startsWith("2.12.")) - Def.task[Unit]((readme / Compile / compile).value) + // TODO should this really be docs / Compile? + Def.task[Unit]((docs / Compile / compile).value) else Def.task(()) }, diff --git a/project/Build.scala b/project/Build.scala index bab9374c2..98c428f85 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -14,9 +14,11 @@ import sbtbuildinfo.BuildInfoPlugin import sbtbuildinfo.BuildInfoPlugin.autoImport._ import scalafix.sbt.ScalafixPlugin import scalafix.sbt.ScalafixPlugin.autoImport._ -import scalatex.ScalatexReadme import Dependencies._ import Lib._ +import mdoc.MdocPlugin +import sbtdynver.DynVerPlugin.autoImport.previousStableVersion +import mdoc.MdocPlugin.autoImport._ object Build { @@ -37,7 +39,6 @@ object Build { testsFirefox, testsNodeJsdom, example, - // readme, // This is a Scala 2.12 only module ) lazy val dom = project @@ -141,18 +142,20 @@ object Build { .enablePlugins(ScalaJSPlugin) .configure(commonSettings, crossScala, preventPublication) - lazy val readme = - ScalatexReadme( - projectId = "readme", - wd = file(""), - url = "https://github.com/scala-js/scala-js-dom/tree/main", - source = "Index", - autoResources = Seq("example-opt.js"), - ) - .configure(commonSettings, preventPublication) + lazy val jsdocs = project + .in(file("mdocs-js")) + .dependsOn(dom) + .enablePlugins(ScalaJSPlugin) + .configure(commonSettings, crossScala, preventPublication) + + lazy val docs = project + .in(file("mdocs")) .settings( - scalaVersion := Ver.scala212, - Compile / resources += (example / Compile / fullOptJS).value.data, + mdocJS := Some(jsdocs), + mdocVariables := Map( + "VERSION" -> previousStableVersion.value.getOrElse("2.3.0") + ) ) - + .enablePlugins(MdocPlugin) + .configure(commonSettings, crossScala, preventPublication) } diff --git a/project/plugins.sbt b/project/plugins.sbt index 3ff46f291..9bd17f7b1 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -7,3 +7,4 @@ addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.5.12") addSbtPlugin("com.lihaoyi" % "scalatex-sbt-plugin" % "0.3.11") addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.7.1") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.0") +addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.3.7") diff --git a/readme/Index.scalatex b/readme/Index.scalatex deleted file mode 100644 index 34b6a7cf6..000000000 --- a/readme/Index.scalatex +++ /dev/null @@ -1,163 +0,0 @@ -@import ammonite.ops._ -@import Main._ -@a( - href:="https://github.com/scala-js/scala-js-dom", - position.absolute, - top:=0,right:=0,border:=0, - img( - src:="https://camo.githubusercontent.com/a6677b08c955af8400f44c6298f40e7d19cc5b2d/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f677261795f3664366436642e706e67", - alt:="Fork me on GitHub" - ) -) -@def pair(example: String, - frags: Seq[scalatags.Text.TypedTag[String]], - autorun: Boolean = false) = { - val ids = (0 until frags.length).map(_ => util.Random.nextInt(999999)) - val queries = ids.map(id => s"document.getElementById('$id')").mkString(", ") - div( - div(width:="50%", float.left)( - hl.ref( - pwd/'example/'src/'main/'scala/'example/"Example.scala", - Seq(s"object $example", "main") - ) - ), - div(width:="50%", float.left, padding:="8px", boxSizing.`border-box`)( - if (!autorun) - a(cls:="pure-button", onclick:=s"Example$example.main($queries)", "Run"), - div( - frags.zip(ids).map{case (f, i) => f(id:=i, backgroundColor:="#fafafa")} - ), - if (autorun) - script(s"Example$example.main($queries)") - ), - div(clear.both) - ) -} - -@scalatags.Text.tags2.style - pre{ - margin: 0px; - - } - -@sect{scala-js-dom} - - @p - Scala-js-dom provides a nice statically typed interface to the DOM such that it can be called from Scala code without resorting to @hl.scala{js.Dynamic}. All javascript globals functions, singletons and classes are members of the @hl.scala{org.scalajs.dom}, @hl.scala{org.scalajs.dom.html}, @hl.scala{org.scalajs.dom.svg}, etc. packages. For example: - - @pair("Alert", Nil) - - @p - Will cause a javascript alert box saying `Hi from Scala-js-dom` to appear. Other javascript classes and objects can be similarly accessed e.g. @hl.scala{new dom.XMLHttpRequest()} to perform a new Ajax request, @hl.scala{dom.document} to access the global @hl.scala{document} object, or @hl.scala{html.Div} to to refer to the type of a @hl.scala{
} element. - - @sect{Usage} - @p - Add the following to your sbt build definition: - - @hl.scala - libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "2.2.0" - - @p - then enjoy the types available in @hl.scala{org.scalajs.dom}. scalajs-dom 2.2.0 is built and published for Scala.js 1.5+ with Scala 2.11, 2.12, 2.13, and 3.0+. - - @p - To begin with, @code{scala-js-dom} organizes the full-list of DOM APIs into a number of buckets: - - @ul - @li - @code{dom.html}: HTML element APIs - @li - @code{dom.svg}: SVG element APIs - @li - @code{dom.idb}: IndexedDB APIs - @li - @code{dom.css}: CSS APIs - @li - @code{dom}: Miscellanious, unclassified APIs - - @p - Most names have been shortened from names of the @a("raw browser APIs", href:="https://developer.mozilla.org/en-US/docs/Web/API"), since the namespacing avoids collisions. By convention these types are imported qualified: e.g. as @hl.scala{html.Canvas} instead of directly as @hl.scala{Canvas}. There is also the @code{dom.raw} namespace which contains everything with their full, un-shortened name. - - @p - Here are some examples to get you started: - - @sect{Node.appendChild} - @pair( - "NodeAppendChild", - Seq(div("div")) - ) - - @sect{Node.onmousemove} - @pair( - "EventHandler", - Seq(pre("Hover this box!")), - autorun=true - ) - - @sect{dom.btoa} - @pair( - "Base64", - Seq(input(width:="100%", placeholder:="Enter text to b64 encode"), div), - autorun=true - ) - - @sect{dom.localStorage} - @pair( - "LocalStorage", - Seq(input(width:="100%"), div), - autorun=true - ) - - @sect{dom.HTMLCanvasElement} - @pair( - "Canvas", - Seq(canvas), - autorun=true - ) - - @sect{dom.Fetch} - @pair( - "Fetch", - Seq( - pre("output") - ) - ) - - @sect{dom.Websocket} - @pair( - "Websocket", - Seq( - input(placeholder:="Type something in"), - pre("output") - ), - autorun=true - ) - - @sect{Element.style} - @pair( - "ElementStyle", - Seq(div(b("div"), height:="50px")) - ) - - @p - The goal of this project is to provide a thin-but-idiomatic-scala interface to modern browser APIs. In particular: - @ul - @li - Deprecated properties/methods/types will not be present. - @li - IE-only, Chrome-only, FF-only, and in general browser-specific attributes will typically not be present. - @li - The name of a Scala type should map directly to the name of the corresponding Javascript type. - @li - Any type which is a Javascript type (e.g. you can @hl.scala{instanceof} in javascript) should be a Scala @hl.scala{class}; any other interface which isn't a Javascript type should be a @hl.scala{trait}. - @li - Read-only members should be @hl.scala{def}, and not-directly-instantiable classes should have @hl.scala{private} constructors. - - @sect{Contributing} - @p - The DOM API is always evolving, and scala-js-dom is a hodgepodge of auto-generated/scraped/hand-tweaked code full of rough edges. If you see something that you think can be improved, feel free to send a pull request. These could include: - @ul - @li - Improved doc-comments; who doesn't love better docs? - @li - Missing methods/properties/classes; send the PR adding it in including it together with a link to an authoritative source (e.g. MDN) and it should get merged.