Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add react-markdown example #9

Merged
merged 2 commits into from
Mar 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,22 @@ lazy val `react-window` = project
"react-virtualized-auto-sizer" -> "1.0.2", // as recommended by react-window
"@types/react-virtualized-auto-sizer" -> "1.0.0",
)
)
)

lazy val `react-markdown` = project
.enablePlugins(ScalablyTypedConverterPlugin)
.configure(baseSettings, withCssLoading, browserProject, reactNpmDeps, bundlerSettings)
.settings(
webpack / version := "4.44.1",
useYarn := true,
webpackDevServerPort := 8017,
stFlavour := Flavour.Japgolly,
Compile / npmDependencies ++= Seq(
"react-markdown"-> "^5.0.3",
"react-syntax-highlighter"-> "^15.4.3",
"@types/react-syntax-highlighter"-> "^13.5.0"
)
)

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

Expand Down
19 changes: 15 additions & 4 deletions custom.webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
var merge = require('webpack-merge');
var generated = require('./scalajs.webpack.config');
const merge = require('webpack-merge');
const generated = require('./scalajs.webpack.config');
const path = require("path");
const CopyWebpackPlugin = require('copy-webpack-plugin');

var local = {
const local = {
module: {
rules: [
{
Expand All @@ -17,7 +19,16 @@ var local = {
use: 'url-loader'
}
]
}
},
plugins: [
new CopyWebpackPlugin({
patterns: [
{
from: path.resolve(__dirname, "../../../../src/main/js")
}
]
})
]
};

module.exports = merge(generated, local);
66 changes: 66 additions & 0 deletions docs/react-markdown/docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Working with objects

Javascript is remarkably flexible. When we integrate with arbitrary Javascript code in Scala.js, we need a very flexible
encoding to tag along. The encoding chosen for ScalablyTyped is the result of years of experimentation, and has
a much more dynamic feeling than what you may be used to.

Let's start with an example of a type definition we want to use:

```scala
@js.native
trait Point extends StObject {

var x: Double = js.native

var y: Double = js.native
}
object Point {

@scala.inline
def apply(x: Double, y: Double): Point = {
val __obj = js.Dynamic.literal(x = x.asInstanceOf[js.Any], y = y.asInstanceOf[js.Any])
__obj.asInstanceOf[Point]
}

@scala.inline
implicit class PointMutableBuilder[Self <: Point] (val x: Self) extends AnyVal {

@scala.inline
def setX(value: Double): Self = StObject.set(x, "x", value.asInstanceOf[js.Any])

@scala.inline
def setY(value: Double): Self = StObject.set(x, "y", value.asInstanceOf[js.Any])
}
}
```

We notice several things:
- 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.
- it has two required members (`x` and `y`). Optional members would typically be wrapped in `js.UndefOr`
- it has an `object` with syntax to help us work with it
- the entire syntax is built on mutability. It's Javascript, after all. more on that further down

### Basic usage

```scala
// At construction time we need to supply all required parameters
val p = Point(x = 1,y = 2)

// we can mutate what we have
// this is equivalent to typescript `p.x = 3
val p2 = p.setX(3)

// or we can duplicate and then mutate.
// this is equivalent to typescript `const p2 = {...p, x: 3}
val p3 = p.duplicate.setX(3)

// we can combine with other javascript objects.
// this is equivalent to javascript `const p3 = {...p, {}}`
val p4: Point with TickOptions = p.combineWith(TickOptions())

// fallback, if the type definitions are wrong or for any other reason you can break the contract
val p5: p.duplicate.set("x", "foo")

// you can also set any other property
val p6: p.duplicate.set("x2", "foo")
```
12 changes: 12 additions & 0 deletions docs/react-markdown/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>React-markdown demo</title>
</head>
<body>
<div id="container"></div>

<script type="text/javascript" src="react-markdown-opt-bundle.js"></script>
</body>
</html>
60 changes: 60 additions & 0 deletions docs/react-markdown/react-markdown-opt-bundle.js

Large diffs are not rendered by default.

66 changes: 66 additions & 0 deletions react-markdown/src/main/js/docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Working with objects

Javascript is remarkably flexible. When we integrate with arbitrary Javascript code in Scala.js, we need a very flexible
encoding to tag along. The encoding chosen for ScalablyTyped is the result of years of experimentation, and has
a much more dynamic feeling than what you may be used to.

Let's start with an example of a type definition we want to use:

```scala
@js.native
trait Point extends StObject {

var x: Double = js.native

var y: Double = js.native
}
object Point {

@scala.inline
def apply(x: Double, y: Double): Point = {
val __obj = js.Dynamic.literal(x = x.asInstanceOf[js.Any], y = y.asInstanceOf[js.Any])
__obj.asInstanceOf[Point]
}

@scala.inline
implicit class PointMutableBuilder[Self <: Point] (val x: Self) extends AnyVal {

@scala.inline
def setX(value: Double): Self = StObject.set(x, "x", value.asInstanceOf[js.Any])

@scala.inline
def setY(value: Double): Self = StObject.set(x, "y", value.asInstanceOf[js.Any])
}
}
```

We notice several things:
- 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.
- it has two required members (`x` and `y`). Optional members would typically be wrapped in `js.UndefOr`
- it has an `object` with syntax to help us work with it
- the entire syntax is built on mutability. It's Javascript, after all. more on that further down

### Basic usage

```scala
// At construction time we need to supply all required parameters
val p = Point(x = 1,y = 2)

// we can mutate what we have
// this is equivalent to typescript `p.x = 3
val p2 = p.setX(3)

// or we can duplicate and then mutate.
// this is equivalent to typescript `const p2 = {...p, x: 3}
val p3 = p.duplicate.setX(3)

// we can combine with other javascript objects.
// this is equivalent to javascript `const p3 = {...p, {}}`
val p4: Point with TickOptions = p.combineWith(TickOptions())

// fallback, if the type definitions are wrong or for any other reason you can break the contract
val p5: p.duplicate.set("x", "foo")

// you can also set any other property
val p6: p.duplicate.set("x2", "foo")
```
12 changes: 12 additions & 0 deletions react-markdown/src/main/js/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>React-markdown demo</title>
</head>
<body>
<div id="container"></div>

<script type="text/javascript" src="react-markdown-fastopt-bundle.js"></script>
</body>
</html>
49 changes: 49 additions & 0 deletions react-markdown/src/main/scala/demo/DocPage.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package demo

import japgolly.scalajs.react.component.ScalaFn.Component
import japgolly.scalajs.react.raw.React.{ElementType, Node}
import japgolly.scalajs.react.vdom.html_<^._
import japgolly.scalajs.react.{CtorType, ScalaFnComponent}
import org.scalablytyped.runtime.StringDictionary
import org.scalajs.dom.raw.XMLHttpRequest
import typings.react.mod.{EffectCallback, useEffect, useState}
import typings.reactMarkdown.components.ReactMarkdown
import typings.reactMarkdown.mod.{ReactMarkdownProps, ReactMarkdownPropsBase}
import typings.reactSyntaxHighlighter.components.{Light => SyntaxHighligther}
import typings.reactSyntaxHighlighter.mod.Light
import typings.reactSyntaxHighlighter.{scalaMod, stylesHljsMod}

import scala.scalajs.js


object DocPage {

val docFile = "./docs/README.md"

Light.registerLanguage("scala", scalaMod.default)

class LanguageValue(val language: String, val value: String) extends js.Object

val codeRender: js.Function1[LanguageValue, Node] =
rp => SyntaxHighligther.style(stylesHljsMod.darcula).language(rp.language)(rp.value).build.rawElement

val component: Component[Unit, CtorType.Nullary] = ScalaFnComponent[Unit] { _ =>
val js.Tuple2(document, setDocument) = useState[Option[String]](None)

useEffect((() => {
val xhr = new XMLHttpRequest
xhr.onload = _ => {
setDocument(Some(xhr.responseText))
}
xhr.open("GET", docFile)
xhr.send()
}): EffectCallback, js.Array(docFile))

val props = ReactMarkdownPropsBase()
.setRenderers(StringDictionary("code" -> codeRender).asInstanceOf[StringDictionary[ElementType]])
.asInstanceOf[ReactMarkdownProps]

ReactMarkdown(props)(document)

}
}
11 changes: 11 additions & 0 deletions react-markdown/src/main/scala/demo/Main.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package demo

import org.scalajs.dom

// https://stackblitz.com/edit/react-syntax-highlighter-issue-js
// https://github.com/remarkjs/react-markdown#use-custom-renderers-syntax-highlight
object Main {

def main(args: Array[String]): Unit =
DocPage.component().renderIntoDOM(dom.document.getElementById("container"))
}
Loading