Skip to content

Commit ba3960e

Browse files
add react-markdown example (#9)
1 parent 46c3663 commit ba3960e

File tree

11 files changed

+5164
-6
lines changed

11 files changed

+5164
-6
lines changed

build.sbt

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,22 @@ lazy val `react-window` = project
287287
"react-virtualized-auto-sizer" -> "1.0.2", // as recommended by react-window
288288
"@types/react-virtualized-auto-sizer" -> "1.0.0",
289289
)
290-
)
290+
)
291+
292+
lazy val `react-markdown` = project
293+
.enablePlugins(ScalablyTypedConverterPlugin)
294+
.configure(baseSettings, withCssLoading, browserProject, reactNpmDeps, bundlerSettings)
295+
.settings(
296+
webpack / version := "4.44.1",
297+
useYarn := true,
298+
webpackDevServerPort := 8017,
299+
stFlavour := Flavour.Japgolly,
300+
Compile / npmDependencies ++= Seq(
301+
"react-markdown"-> "^5.0.3",
302+
"react-syntax-highlighter"-> "^15.4.3",
303+
"@types/react-syntax-highlighter"-> "^13.5.0"
304+
)
305+
)
291306

292307
/** Note: This can't use scalajs-bundler (at least I don't know how),
293308
* so we run yarn ourselves with an external package.json.
@@ -342,7 +357,8 @@ lazy val withCssLoading: Project => Project =
342357
"css-loader" -> "3.4.2",
343358
"style-loader" -> "1.1.3",
344359
"file-loader" -> "5.1.0",
345-
"url-loader" -> "3.0.0"
360+
"url-loader" -> "4.1.0",
361+
"copy-webpack-plugin" -> "6.0.3"
346362
)
347363
)
348364

custom.webpack.config.js

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
var merge = require('webpack-merge');
2-
var generated = require('./scalajs.webpack.config');
1+
const merge = require('webpack-merge');
2+
const generated = require('./scalajs.webpack.config');
3+
const path = require("path");
4+
const CopyWebpackPlugin = require('copy-webpack-plugin');
35

4-
var local = {
6+
const local = {
57
module: {
68
rules: [
79
{
@@ -17,7 +19,16 @@ var local = {
1719
use: 'url-loader'
1820
}
1921
]
20-
}
22+
},
23+
plugins: [
24+
new CopyWebpackPlugin({
25+
patterns: [
26+
{
27+
from: path.resolve(__dirname, "../../../../src/main/js")
28+
}
29+
]
30+
})
31+
]
2132
};
2233

2334
module.exports = merge(generated, local);

docs/react-markdown/docs/README.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Working with objects
2+
3+
Javascript is remarkably flexible. When we integrate with arbitrary Javascript code in Scala.js, we need a very flexible
4+
encoding to tag along. The encoding chosen for ScalablyTyped is the result of years of experimentation, and has
5+
a much more dynamic feeling than what you may be used to.
6+
7+
Let's start with an example of a type definition we want to use:
8+
9+
```scala
10+
@js.native
11+
trait Point extends StObject {
12+
13+
var x: Double = js.native
14+
15+
var y: Double = js.native
16+
}
17+
object Point {
18+
19+
@scala.inline
20+
def apply(x: Double, y: Double): Point = {
21+
val __obj = js.Dynamic.literal(x = x.asInstanceOf[js.Any], y = y.asInstanceOf[js.Any])
22+
__obj.asInstanceOf[Point]
23+
}
24+
25+
@scala.inline
26+
implicit class PointMutableBuilder[Self <: Point] (val x: Self) extends AnyVal {
27+
28+
@scala.inline
29+
def setX(value: Double): Self = StObject.set(x, "x", value.asInstanceOf[js.Any])
30+
31+
@scala.inline
32+
def setY(value: Double): Self = StObject.set(x, "y", value.asInstanceOf[js.Any])
33+
}
34+
}
35+
```
36+
37+
We notice several things:
38+
- it's a `@js.native` trait, so we cannot `new` it ourselves. This can be [`changed`](conversion-options.md#stenablescalajsdefined), but it's not recommended.
39+
- it has two required members (`x` and `y`). Optional members would typically be wrapped in `js.UndefOr`
40+
- it has an `object` with syntax to help us work with it
41+
- the entire syntax is built on mutability. It's Javascript, after all. more on that further down
42+
43+
### Basic usage
44+
45+
```scala
46+
// At construction time we need to supply all required parameters
47+
val p = Point(x = 1,y = 2)
48+
49+
// we can mutate what we have
50+
// this is equivalent to typescript `p.x = 3
51+
val p2 = p.setX(3)
52+
53+
// or we can duplicate and then mutate.
54+
// this is equivalent to typescript `const p2 = {...p, x: 3}
55+
val p3 = p.duplicate.setX(3)
56+
57+
// we can combine with other javascript objects.
58+
// this is equivalent to javascript `const p3 = {...p, {}}`
59+
val p4: Point with TickOptions = p.combineWith(TickOptions())
60+
61+
// fallback, if the type definitions are wrong or for any other reason you can break the contract
62+
val p5: p.duplicate.set("x", "foo")
63+
64+
// you can also set any other property
65+
val p6: p.duplicate.set("x2", "foo")
66+
```

docs/react-markdown/index.html

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>React-markdown demo</title>
6+
</head>
7+
<body>
8+
<div id="container"></div>
9+
10+
<script type="text/javascript" src="react-markdown-opt-bundle.js"></script>
11+
</body>
12+
</html>

docs/react-markdown/react-markdown-opt-bundle.js

Lines changed: 60 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Working with objects
2+
3+
Javascript is remarkably flexible. When we integrate with arbitrary Javascript code in Scala.js, we need a very flexible
4+
encoding to tag along. The encoding chosen for ScalablyTyped is the result of years of experimentation, and has
5+
a much more dynamic feeling than what you may be used to.
6+
7+
Let's start with an example of a type definition we want to use:
8+
9+
```scala
10+
@js.native
11+
trait Point extends StObject {
12+
13+
var x: Double = js.native
14+
15+
var y: Double = js.native
16+
}
17+
object Point {
18+
19+
@scala.inline
20+
def apply(x: Double, y: Double): Point = {
21+
val __obj = js.Dynamic.literal(x = x.asInstanceOf[js.Any], y = y.asInstanceOf[js.Any])
22+
__obj.asInstanceOf[Point]
23+
}
24+
25+
@scala.inline
26+
implicit class PointMutableBuilder[Self <: Point] (val x: Self) extends AnyVal {
27+
28+
@scala.inline
29+
def setX(value: Double): Self = StObject.set(x, "x", value.asInstanceOf[js.Any])
30+
31+
@scala.inline
32+
def setY(value: Double): Self = StObject.set(x, "y", value.asInstanceOf[js.Any])
33+
}
34+
}
35+
```
36+
37+
We notice several things:
38+
- it's a `@js.native` trait, so we cannot `new` it ourselves. This can be [`changed`](conversion-options.md#stenablescalajsdefined), but it's not recommended.
39+
- it has two required members (`x` and `y`). Optional members would typically be wrapped in `js.UndefOr`
40+
- it has an `object` with syntax to help us work with it
41+
- the entire syntax is built on mutability. It's Javascript, after all. more on that further down
42+
43+
### Basic usage
44+
45+
```scala
46+
// At construction time we need to supply all required parameters
47+
val p = Point(x = 1,y = 2)
48+
49+
// we can mutate what we have
50+
// this is equivalent to typescript `p.x = 3
51+
val p2 = p.setX(3)
52+
53+
// or we can duplicate and then mutate.
54+
// this is equivalent to typescript `const p2 = {...p, x: 3}
55+
val p3 = p.duplicate.setX(3)
56+
57+
// we can combine with other javascript objects.
58+
// this is equivalent to javascript `const p3 = {...p, {}}`
59+
val p4: Point with TickOptions = p.combineWith(TickOptions())
60+
61+
// fallback, if the type definitions are wrong or for any other reason you can break the contract
62+
val p5: p.duplicate.set("x", "foo")
63+
64+
// you can also set any other property
65+
val p6: p.duplicate.set("x2", "foo")
66+
```

react-markdown/src/main/js/index.html

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>React-markdown demo</title>
6+
</head>
7+
<body>
8+
<div id="container"></div>
9+
10+
<script type="text/javascript" src="react-markdown-fastopt-bundle.js"></script>
11+
</body>
12+
</html>
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package demo
2+
3+
import japgolly.scalajs.react.component.ScalaFn.Component
4+
import japgolly.scalajs.react.raw.React.{ElementType, Node}
5+
import japgolly.scalajs.react.vdom.html_<^._
6+
import japgolly.scalajs.react.{CtorType, ScalaFnComponent}
7+
import org.scalablytyped.runtime.StringDictionary
8+
import org.scalajs.dom.raw.XMLHttpRequest
9+
import typings.react.mod.{EffectCallback, useEffect, useState}
10+
import typings.reactMarkdown.components.ReactMarkdown
11+
import typings.reactMarkdown.mod.{ReactMarkdownProps, ReactMarkdownPropsBase}
12+
import typings.reactSyntaxHighlighter.components.{Light => SyntaxHighligther}
13+
import typings.reactSyntaxHighlighter.mod.Light
14+
import typings.reactSyntaxHighlighter.{scalaMod, stylesHljsMod}
15+
16+
import scala.scalajs.js
17+
18+
19+
object DocPage {
20+
21+
val docFile = "./docs/README.md"
22+
23+
Light.registerLanguage("scala", scalaMod.default)
24+
25+
class LanguageValue(val language: String, val value: String) extends js.Object
26+
27+
val codeRender: js.Function1[LanguageValue, Node] =
28+
rp => SyntaxHighligther.style(stylesHljsMod.darcula).language(rp.language)(rp.value).build.rawElement
29+
30+
val component: Component[Unit, CtorType.Nullary] = ScalaFnComponent[Unit] { _ =>
31+
val js.Tuple2(document, setDocument) = useState[Option[String]](None)
32+
33+
useEffect((() => {
34+
val xhr = new XMLHttpRequest
35+
xhr.onload = _ => {
36+
setDocument(Some(xhr.responseText))
37+
}
38+
xhr.open("GET", docFile)
39+
xhr.send()
40+
}): EffectCallback, js.Array(docFile))
41+
42+
val props = ReactMarkdownPropsBase()
43+
.setRenderers(StringDictionary("code" -> codeRender).asInstanceOf[StringDictionary[ElementType]])
44+
.asInstanceOf[ReactMarkdownProps]
45+
46+
ReactMarkdown(props)(document)
47+
48+
}
49+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package demo
2+
3+
import org.scalajs.dom
4+
5+
// https://stackblitz.com/edit/react-syntax-highlighter-issue-js
6+
// https://github.com/remarkjs/react-markdown#use-custom-renderers-syntax-highlight
7+
object Main {
8+
9+
def main(args: Array[String]): Unit =
10+
DocPage.component().renderIntoDOM(dom.document.getElementById("container"))
11+
}

0 commit comments

Comments
 (0)