First commit
This commit is contained in:
commit
02a760984d
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
.DS_Store
|
||||||
|
.merlin
|
||||||
|
.bsb.lock
|
||||||
|
npm-debug.log
|
||||||
|
/lib/bs/
|
||||||
|
/node_modules/
|
||||||
|
/bundleOutput/
|
61
README.md
Normal file
61
README.md
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
# ReasonReact Template & Examples
|
||||||
|
|
||||||
|
This is:
|
||||||
|
- A template for your new ReasonReact project.
|
||||||
|
- A collection of thin examples illustrating ReasonReact usage.
|
||||||
|
- Extra helper documentation for ReasonReact (full ReasonReact docs [here](https://reasonml.github.io/reason-react/)).
|
||||||
|
|
||||||
|
`src` contains 4 sub-folders, each an independent, self-contained ReasonReact example. Feel free to delete any of them and shape this into your project! This template's more malleable than you might be used to =).
|
||||||
|
|
||||||
|
The point of this template and examples is to let you understand and personally tweak the entirely of it. We **don't** give you an opaque, elaborate mega build setup just to put some boxes on the screen. It strikes to stay transparent, learnable, and simple. You're encouraged to read every file; it's a great feeling, having the full picture of what you're using and being able to touch any part.
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install
|
||||||
|
npm run server
|
||||||
|
# in a new tab
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
Open a new web page to `http://localhost:8000/`. Change any `.re` file in `src` to see the page auto-reload. **You don't need any bundler when you're developing**!
|
||||||
|
|
||||||
|
**How come we don't need any bundler during development**? We highly encourage you to open up `index.html` to check for yourself!
|
||||||
|
|
||||||
|
# Features Used
|
||||||
|
|
||||||
|
| | Blinking Greeting | Reducer from ReactJS Docs | Fetch Dog Pictures | Reason Using JS Using Reason |
|
||||||
|
|---------------------------|-------------------|---------------------------|--------------------|------------------------------|
|
||||||
|
| No props | | ✓ | | |
|
||||||
|
| Has props | | | | ✓ |
|
||||||
|
| Children props | ✓ | | | |
|
||||||
|
| No state | | | | ✓ |
|
||||||
|
| Has state | ✓ | | ✓ | |
|
||||||
|
| Has state with useReducer | | ✓ | | |
|
||||||
|
| ReasonReact using ReactJS | | | | ✓ |
|
||||||
|
| ReactJS using ReasonReact | | | | ✓ |
|
||||||
|
| useEffect | ✓ | | ✓ | |
|
||||||
|
| Dom attribute | ✓ | ✓ | | ✓ |
|
||||||
|
| Styling | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| React.array | | | ✓ | |
|
||||||
|
|
||||||
|
# Bundle for Production
|
||||||
|
|
||||||
|
We've included a convenience `UNUSED_webpack.config.js`, in case you want to ship your project to production. You can rename and/or remove that in favor of other bundlers, e.g. Rollup.
|
||||||
|
|
||||||
|
We've also provided a barebone `indexProduction.html`, to serve your bundle.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install webpack webpack-cli
|
||||||
|
# rename file
|
||||||
|
mv UNUSED_webpack.config.js webpack.config.js
|
||||||
|
# call webpack to bundle for production
|
||||||
|
./node_modules/.bin/webpack
|
||||||
|
open indexProduction.html
|
||||||
|
```
|
||||||
|
|
||||||
|
# Handle Routing Yourself
|
||||||
|
|
||||||
|
To serve the files, this template uses a minimal dependency called `moduleserve`. A URL such as `localhost:8000/scores/john` resolves to the file `scores/john.html`. If you'd like to override this and handle URL resolution yourself, change the `server` command in `package.json` from `moduleserve ./ --port 8000` to `moduleserve ./ --port 8000 --spa` (for "single page application"). This will make `moduleserve` serve the default `index.html` for any URL. Since `index.html` loads `Index.bs.js`, you can grab hold of the URL in the corresponding `Index.re` and do whatever you want.
|
||||||
|
|
||||||
|
By the way, ReasonReact comes with a small [router](https://reasonml.github.io/reason-react/docs/en/router) you might be interested in.
|
14
UNUSED_webpack.config.js
Normal file
14
UNUSED_webpack.config.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
entry: './src/Index.bs.js',
|
||||||
|
// If you ever want to use webpack during development, change 'production'
|
||||||
|
// to 'development' as per webpack documentation. Again, you don't have to
|
||||||
|
// use webpack or any other bundler during development! Recheck README if
|
||||||
|
// you didn't know this
|
||||||
|
mode: 'production',
|
||||||
|
output: {
|
||||||
|
path: path.join(__dirname, "bundleOutput"),
|
||||||
|
filename: 'index.js',
|
||||||
|
},
|
||||||
|
};
|
21
bsconfig.json
Normal file
21
bsconfig.json
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"name": "reason-react-examples",
|
||||||
|
"reason": {
|
||||||
|
"react-jsx": 3
|
||||||
|
},
|
||||||
|
"sources": {
|
||||||
|
"dir" : "src",
|
||||||
|
"subdirs" : true
|
||||||
|
},
|
||||||
|
"bsc-flags": ["-bs-super-errors", "-bs-no-version-header"],
|
||||||
|
"package-specs": [{
|
||||||
|
"module": "commonjs",
|
||||||
|
"in-source": true
|
||||||
|
}],
|
||||||
|
"suffix": ".bs.js",
|
||||||
|
"namespace": true,
|
||||||
|
"bs-dependencies": [
|
||||||
|
"reason-react"
|
||||||
|
],
|
||||||
|
"refmt": 3
|
||||||
|
}
|
22
index.html
Normal file
22
index.html
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>ReasonReact Examples</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script>
|
||||||
|
// stub a variable ReactJS checks. ReactJS assumes you're using a bundler, NodeJS or similar system that provides it the `process.env.NODE_ENV` variable.
|
||||||
|
window.process = {
|
||||||
|
env: {
|
||||||
|
NODE_ENV: 'development'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- This is https://github.com/marijnh/moduleserve, the secret sauce that allows us not need to bundle things during development, and have instantaneous iteration feedback, without any hot-reloading or extra build pipeline needed. -->
|
||||||
|
<script src="/moduleserve/load.js" data-module="/src/Index.bs.js"></script>
|
||||||
|
<!-- Our little watcher. Super clean. Check it out! -->
|
||||||
|
<script src="/watcher.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
10
indexProduction.html
Normal file
10
indexProduction.html
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>ReasonReact Examples</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script src="./bundleOutput/index.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
7
my-react-app/.gitignore
vendored
Normal file
7
my-react-app/.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
.DS_Store
|
||||||
|
.merlin
|
||||||
|
.bsb.lock
|
||||||
|
npm-debug.log
|
||||||
|
/lib/bs/
|
||||||
|
/node_modules/
|
||||||
|
/bundleOutput/
|
61
my-react-app/README.md
Normal file
61
my-react-app/README.md
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
# ReasonReact Template & Examples
|
||||||
|
|
||||||
|
This is:
|
||||||
|
- A template for your new ReasonReact project.
|
||||||
|
- A collection of thin examples illustrating ReasonReact usage.
|
||||||
|
- Extra helper documentation for ReasonReact (full ReasonReact docs [here](https://reasonml.github.io/reason-react/)).
|
||||||
|
|
||||||
|
`src` contains 4 sub-folders, each an independent, self-contained ReasonReact example. Feel free to delete any of them and shape this into your project! This template's more malleable than you might be used to =).
|
||||||
|
|
||||||
|
The point of this template and examples is to let you understand and personally tweak the entirely of it. We **don't** give you an opaque, elaborate mega build setup just to put some boxes on the screen. It strikes to stay transparent, learnable, and simple. You're encouraged to read every file; it's a great feeling, having the full picture of what you're using and being able to touch any part.
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install
|
||||||
|
npm run server
|
||||||
|
# in a new tab
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
Open a new web page to `http://localhost:8000/`. Change any `.re` file in `src` to see the page auto-reload. **You don't need any bundler when you're developing**!
|
||||||
|
|
||||||
|
**How come we don't need any bundler during development**? We highly encourage you to open up `index.html` to check for yourself!
|
||||||
|
|
||||||
|
# Features Used
|
||||||
|
|
||||||
|
| | Blinking Greeting | Reducer from ReactJS Docs | Fetch Dog Pictures | Reason Using JS Using Reason |
|
||||||
|
|---------------------------|-------------------|---------------------------|--------------------|------------------------------|
|
||||||
|
| No props | | ✓ | | |
|
||||||
|
| Has props | | | | ✓ |
|
||||||
|
| Children props | ✓ | | | |
|
||||||
|
| No state | | | | ✓ |
|
||||||
|
| Has state | ✓ | | ✓ | |
|
||||||
|
| Has state with useReducer | | ✓ | | |
|
||||||
|
| ReasonReact using ReactJS | | | | ✓ |
|
||||||
|
| ReactJS using ReasonReact | | | | ✓ |
|
||||||
|
| useEffect | ✓ | | ✓ | |
|
||||||
|
| Dom attribute | ✓ | ✓ | | ✓ |
|
||||||
|
| Styling | ✓ | ✓ | ✓ | ✓ |
|
||||||
|
| React.array | | | ✓ | |
|
||||||
|
|
||||||
|
# Bundle for Production
|
||||||
|
|
||||||
|
We've included a convenience `UNUSED_webpack.config.js`, in case you want to ship your project to production. You can rename and/or remove that in favor of other bundlers, e.g. Rollup.
|
||||||
|
|
||||||
|
We've also provided a barebone `indexProduction.html`, to serve your bundle.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install webpack webpack-cli
|
||||||
|
# rename file
|
||||||
|
mv UNUSED_webpack.config.js webpack.config.js
|
||||||
|
# call webpack to bundle for production
|
||||||
|
./node_modules/.bin/webpack
|
||||||
|
open indexProduction.html
|
||||||
|
```
|
||||||
|
|
||||||
|
# Handle Routing Yourself
|
||||||
|
|
||||||
|
To serve the files, this template uses a minimal dependency called `moduleserve`. A URL such as `localhost:8000/scores/john` resolves to the file `scores/john.html`. If you'd like to override this and handle URL resolution yourself, change the `server` command in `package.json` from `moduleserve ./ --port 8000` to `moduleserve ./ --port 8000 --spa` (for "single page application"). This will make `moduleserve` serve the default `index.html` for any URL. Since `index.html` loads `Index.bs.js`, you can grab hold of the URL in the corresponding `Index.re` and do whatever you want.
|
||||||
|
|
||||||
|
By the way, ReasonReact comes with a small [router](https://reasonml.github.io/reason-react/docs/en/router) you might be interested in.
|
14
my-react-app/UNUSED_webpack.config.js
Normal file
14
my-react-app/UNUSED_webpack.config.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
entry: './src/Index.bs.js',
|
||||||
|
// If you ever want to use webpack during development, change 'production'
|
||||||
|
// to 'development' as per webpack documentation. Again, you don't have to
|
||||||
|
// use webpack or any other bundler during development! Recheck README if
|
||||||
|
// you didn't know this
|
||||||
|
mode: 'production',
|
||||||
|
output: {
|
||||||
|
path: path.join(__dirname, "bundleOutput"),
|
||||||
|
filename: 'index.js',
|
||||||
|
},
|
||||||
|
};
|
21
my-react-app/bsconfig.json
Normal file
21
my-react-app/bsconfig.json
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"name": "reason-react-examples",
|
||||||
|
"reason": {
|
||||||
|
"react-jsx": 3
|
||||||
|
},
|
||||||
|
"sources": {
|
||||||
|
"dir" : "src",
|
||||||
|
"subdirs" : true
|
||||||
|
},
|
||||||
|
"bsc-flags": ["-bs-super-errors", "-bs-no-version-header"],
|
||||||
|
"package-specs": [{
|
||||||
|
"module": "commonjs",
|
||||||
|
"in-source": true
|
||||||
|
}],
|
||||||
|
"suffix": ".bs.js",
|
||||||
|
"namespace": true,
|
||||||
|
"bs-dependencies": [
|
||||||
|
"reason-react"
|
||||||
|
],
|
||||||
|
"refmt": 3
|
||||||
|
}
|
22
my-react-app/index.html
Normal file
22
my-react-app/index.html
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>ReasonReact Examples</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script>
|
||||||
|
// stub a variable ReactJS checks. ReactJS assumes you're using a bundler, NodeJS or similar system that provides it the `process.env.NODE_ENV` variable.
|
||||||
|
window.process = {
|
||||||
|
env: {
|
||||||
|
NODE_ENV: 'development'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- This is https://github.com/marijnh/moduleserve, the secret sauce that allows us not need to bundle things during development, and have instantaneous iteration feedback, without any hot-reloading or extra build pipeline needed. -->
|
||||||
|
<script src="/moduleserve/load.js" data-module="/src/Index.bs.js"></script>
|
||||||
|
<!-- Our little watcher. Super clean. Check it out! -->
|
||||||
|
<script src="/watcher.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
10
my-react-app/indexProduction.html
Normal file
10
my-react-app/indexProduction.html
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>ReasonReact Examples</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script src="./bundleOutput/index.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
27
my-react-app/package.json
Normal file
27
my-react-app/package.json
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"name": "my-react-app",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"scripts": {
|
||||||
|
"build": "bsb -make-world",
|
||||||
|
"start": "bsb -make-world -w -ws _ ",
|
||||||
|
"clean": "bsb -clean-world",
|
||||||
|
"server": "moduleserve ./ --port 8000",
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"BuckleScript",
|
||||||
|
"ReasonReact",
|
||||||
|
"reason-react"
|
||||||
|
],
|
||||||
|
"author": "",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"react": "^16.8.1",
|
||||||
|
"react-dom": "^16.8.1",
|
||||||
|
"reason-react": ">=0.7.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"bs-platform": "^7.1.0",
|
||||||
|
"moduleserve": "^0.9.0"
|
||||||
|
}
|
||||||
|
}
|
25
my-react-app/src/BlinkingGreeting/BlinkingGreeting.re
Normal file
25
my-react-app/src/BlinkingGreeting/BlinkingGreeting.re
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
[@react.component]
|
||||||
|
let make = (~children) => {
|
||||||
|
let (show, setShow) = React.useState(() => true);
|
||||||
|
|
||||||
|
// Notice that instead of `useEffect`, we have `useEffect0`. See
|
||||||
|
// reasonml.github.io/reason-react/docs/en/components#hooks for more info
|
||||||
|
React.useEffect0(() => {
|
||||||
|
let id =
|
||||||
|
Js.Global.setInterval(
|
||||||
|
() => setShow(previousShow => !previousShow),
|
||||||
|
1000,
|
||||||
|
);
|
||||||
|
|
||||||
|
Some(() => Js.Global.clearInterval(id));
|
||||||
|
});
|
||||||
|
|
||||||
|
let style =
|
||||||
|
if (show) {
|
||||||
|
ReactDOMRe.Style.make(~opacity="1", ~transition="opacity 1s", ());
|
||||||
|
} else {
|
||||||
|
ReactDOMRe.Style.make(~opacity="0", ~transition="opacity 1s", ());
|
||||||
|
};
|
||||||
|
|
||||||
|
<div style> children </div>;
|
||||||
|
};
|
44
my-react-app/src/ExampleStyles.re
Normal file
44
my-react-app/src/ExampleStyles.re
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
let reasonReactBlue = "#48a9dc";
|
||||||
|
|
||||||
|
// The {j|...|j} feature is just string interpolation, from
|
||||||
|
// bucklescript.github.io/docs/en/interop-cheatsheet#string-unicode-interpolation
|
||||||
|
// This allows us to conveniently write CSS, together with variables, by
|
||||||
|
// constructing a string
|
||||||
|
let style = {j|
|
||||||
|
body {
|
||||||
|
background-color: rgb(224, 226, 229);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
background-color: white;
|
||||||
|
color: $reasonReactBlue;
|
||||||
|
box-shadow: 0 0 0 1px $reasonReactBlue;
|
||||||
|
border: none;
|
||||||
|
padding: 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
button:active {
|
||||||
|
background-color: $reasonReactBlue;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
margin: 12px 0px;
|
||||||
|
box-shadow: 0px 4px 16px rgb(200, 200, 200);
|
||||||
|
width: 720px;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
.containerTitle {
|
||||||
|
background-color: rgb(242, 243, 245);
|
||||||
|
border-radius: 12px 12px 0px 0px;
|
||||||
|
padding: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.containerContent {
|
||||||
|
background-color: white;
|
||||||
|
padding: 16px;
|
||||||
|
border-radius: 0px 0px 12px 12px;
|
||||||
|
}
|
||||||
|
|j};
|
70
my-react-app/src/FetchedDogPictures/FetchedDogPictures.re
Normal file
70
my-react-app/src/FetchedDogPictures/FetchedDogPictures.re
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
[@bs.val] external fetch: string => Js.Promise.t('a) = "fetch";
|
||||||
|
|
||||||
|
type state =
|
||||||
|
| LoadingDogs
|
||||||
|
| ErrorFetchingDogs
|
||||||
|
| LoadedDogs(array(string));
|
||||||
|
|
||||||
|
[@react.component]
|
||||||
|
let make = () => {
|
||||||
|
let (state, setState) = React.useState(() => LoadingDogs);
|
||||||
|
|
||||||
|
// Notice that instead of `useEffect`, we have `useEffect0`. See
|
||||||
|
// reasonml.github.io/reason-react/docs/en/components#hooks for more info
|
||||||
|
React.useEffect0(() => {
|
||||||
|
Js.Promise.(
|
||||||
|
fetch("https://dog.ceo/api/breeds/image/random/3")
|
||||||
|
|> then_(response => response##json())
|
||||||
|
|> then_(jsonResponse => {
|
||||||
|
setState(_previousState => LoadedDogs(jsonResponse##message));
|
||||||
|
Js.Promise.resolve();
|
||||||
|
})
|
||||||
|
|> catch(_err => {
|
||||||
|
setState(_previousState => ErrorFetchingDogs);
|
||||||
|
Js.Promise.resolve();
|
||||||
|
})
|
||||||
|
|> ignore
|
||||||
|
);
|
||||||
|
|
||||||
|
// Returning None, instead of Some(() => ...), means we don't have any
|
||||||
|
// cleanup to do before unmounting. That's not 100% true. We should
|
||||||
|
// technically cancel the promise. Unofortunately, there's currently no
|
||||||
|
// way to cancel a promise. Promises in general should be way less used
|
||||||
|
// for React components; but since folks do use them, we provide such an
|
||||||
|
// example here. In reality, this fetch should just be a plain callback,
|
||||||
|
// with a cancellation API
|
||||||
|
None;
|
||||||
|
});
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={ReactDOMRe.Style.make(
|
||||||
|
~height="120px",
|
||||||
|
~display="flex",
|
||||||
|
~alignItems="center",
|
||||||
|
~justifyContent="center",
|
||||||
|
(),
|
||||||
|
)}>
|
||||||
|
{switch (state) {
|
||||||
|
| ErrorFetchingDogs => React.string("An error occurred!")
|
||||||
|
| LoadingDogs => React.string("Loading...")
|
||||||
|
| LoadedDogs(dogs) =>
|
||||||
|
dogs
|
||||||
|
->Belt.Array.mapWithIndex((i, dog) => {
|
||||||
|
let imageStyle =
|
||||||
|
ReactDOMRe.Style.make(
|
||||||
|
~height="120px",
|
||||||
|
~width="100%",
|
||||||
|
~marginRight=i === Js.Array.length(dogs) - 1 ? "0px" : "8px",
|
||||||
|
~borderRadius="8px",
|
||||||
|
~boxShadow="0px 4px 16px rgb(200, 200, 200)",
|
||||||
|
~backgroundSize="cover",
|
||||||
|
~backgroundImage={j|url($dog)|j},
|
||||||
|
~backgroundPosition="center",
|
||||||
|
(),
|
||||||
|
);
|
||||||
|
<div key=dog style=imageStyle />;
|
||||||
|
})
|
||||||
|
->React.array
|
||||||
|
}}
|
||||||
|
</div>;
|
||||||
|
};
|
51
my-react-app/src/Index.re
Normal file
51
my-react-app/src/Index.re
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
// Entry point
|
||||||
|
|
||||||
|
[@bs.val] external document: Js.t({..}) = "document";
|
||||||
|
|
||||||
|
// We're using raw DOM manipulations here, to avoid making you read
|
||||||
|
// ReasonReact when you might precisely be trying to learn it for the first
|
||||||
|
// time through the examples later.
|
||||||
|
let style = document##createElement("style");
|
||||||
|
document##head##appendChild(style);
|
||||||
|
style##innerHTML #= ExampleStyles.style;
|
||||||
|
|
||||||
|
let makeContainer = text => {
|
||||||
|
let container = document##createElement("div");
|
||||||
|
container##className #= "container";
|
||||||
|
|
||||||
|
let title = document##createElement("div");
|
||||||
|
title##className #= "containerTitle";
|
||||||
|
title##innerText #= text;
|
||||||
|
|
||||||
|
let content = document##createElement("div");
|
||||||
|
content##className #= "containerContent";
|
||||||
|
|
||||||
|
let () = container##appendChild(title);
|
||||||
|
let () = container##appendChild(content);
|
||||||
|
let () = document##body##appendChild(container);
|
||||||
|
|
||||||
|
content;
|
||||||
|
};
|
||||||
|
|
||||||
|
// All 4 examples.
|
||||||
|
ReactDOMRe.render(
|
||||||
|
<BlinkingGreeting>
|
||||||
|
{React.string("Hello!")}
|
||||||
|
</BlinkingGreeting>,
|
||||||
|
makeContainer("Blinking Greeting"),
|
||||||
|
);
|
||||||
|
|
||||||
|
ReactDOMRe.render(
|
||||||
|
<ReducerFromReactJSDocs />,
|
||||||
|
makeContainer("Reducer From ReactJS Docs"),
|
||||||
|
);
|
||||||
|
|
||||||
|
ReactDOMRe.render(
|
||||||
|
<FetchedDogPictures />,
|
||||||
|
makeContainer("Fetched Dog Pictures"),
|
||||||
|
);
|
||||||
|
|
||||||
|
ReactDOMRe.render(
|
||||||
|
<ReasonUsingJSUsingReason />,
|
||||||
|
makeContainer("Reason Using JS Using Reason"),
|
||||||
|
);
|
31
my-react-app/src/ReasonUsingJSUsingReason/ReactJSCard.js
Normal file
31
my-react-app/src/ReasonUsingJSUsingReason/ReactJSCard.js
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
// In this Interop example folder, we have:
|
||||||
|
// - A ReasonReact component, ReasonReactCard.re
|
||||||
|
// - Used by a ReactJS component, ReactJSCard.js (this file)
|
||||||
|
// - ReactJSCard.js can be used by ReasonReact, through bindings in ReasonUsingJSUsingReason.re
|
||||||
|
// - ReasonUsingJSUsingReason.re is used by Index.re
|
||||||
|
|
||||||
|
var ReactDOM = require('react-dom');
|
||||||
|
var React = require('react');
|
||||||
|
|
||||||
|
var ReasonReactCard = require('./ReasonReactCard.bs').make;
|
||||||
|
|
||||||
|
var ReactJSComponent = function() {
|
||||||
|
let backgroundColor = "rgba(0, 0, 0, 0.05)";
|
||||||
|
let padding = "12px";
|
||||||
|
|
||||||
|
// We're not using JSX here, to avoid folks needing to install the related
|
||||||
|
// React toolchains just for this example.
|
||||||
|
// <div style={...}>
|
||||||
|
// <div style={...}>This is a ReactJS card</div>
|
||||||
|
// <ReasonReactCard style={...} />
|
||||||
|
// </div>
|
||||||
|
return React.createElement(
|
||||||
|
"div",
|
||||||
|
{style: {backgroundColor, padding, borderRadius: "8px"}},
|
||||||
|
React.createElement("div", {style: {marginBottom: "8px"}}, "This is a ReactJS card"),
|
||||||
|
React.createElement(ReasonReactCard, {style: {backgroundColor, padding, borderRadius: "4px"}}),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
ReactJSComponent.displayName = "MyBanner";
|
||||||
|
|
||||||
|
module.exports = ReactJSComponent;
|
10
my-react-app/src/ReasonUsingJSUsingReason/ReasonReactCard.re
Normal file
10
my-react-app/src/ReasonUsingJSUsingReason/ReasonReactCard.re
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
// In this Interop example folder, we have:
|
||||||
|
// - A ReasonReact component, ReasonReactCard.re (this file)
|
||||||
|
// - Used by a ReactJS component, ReactJSCard.js
|
||||||
|
// - ReactJSCard.js can be used by ReasonReact, through bindings in ReasonUsingJSUsingReason.re
|
||||||
|
// - ReasonUsingJSUsingReason.re is used by Index.re
|
||||||
|
|
||||||
|
[@react.component]
|
||||||
|
let make = (~style) => {
|
||||||
|
<div style> {React.string("This is a ReasonReact card")} </div>;
|
||||||
|
};
|
|
@ -0,0 +1,10 @@
|
||||||
|
// In this Interop example folder, we have:
|
||||||
|
// - A ReasonReact component, ReasonReactCard.re
|
||||||
|
// - Used by a ReactJS component, ReactJSCard.js
|
||||||
|
// - ReactJSCard.js can be used by ReasonReact, through bindings in ReasonUsingJSUsingReason.re (this file)
|
||||||
|
// - ReasonUsingJSUsingReason.re is used by Index.re
|
||||||
|
|
||||||
|
// All you need to do to use a ReactJS component in ReasonReact, is to write the lines below!
|
||||||
|
// reasonml.github.io/reason-react/docs/en/components#import-from-js
|
||||||
|
[@react.component] [@bs.module]
|
||||||
|
external make: unit => React.element = "./ReactJSCard";
|
|
@ -0,0 +1,44 @@
|
||||||
|
// This is the ReactJS documentation's useReducer example, directly ported over
|
||||||
|
// https://reactjs.org/docs/hooks-reference.html#usereducer
|
||||||
|
|
||||||
|
// A little extra we've put, because the ReactJS example has no styling
|
||||||
|
let leftButtonStyle = ReactDOMRe.Style.make(~borderRadius="4px 0px 0px 4px", ~width="48px", ());
|
||||||
|
let rightButtonStyle = ReactDOMRe.Style.make(~borderRadius="0px 4px 4px 0px", ~width="48px", ());
|
||||||
|
|
||||||
|
// Record and variant need explicit declarations.
|
||||||
|
type state = {count: int};
|
||||||
|
|
||||||
|
type action =
|
||||||
|
| Increment
|
||||||
|
| Decrement;
|
||||||
|
|
||||||
|
let initialState = {count: 0};
|
||||||
|
|
||||||
|
let reducer = (state, action) => {
|
||||||
|
switch (action) {
|
||||||
|
| Increment => {count: state.count + 1}
|
||||||
|
| Decrement => {count: state.count - 1}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
[@react.component]
|
||||||
|
let make = () => {
|
||||||
|
let (state, dispatch) = React.useReducer(reducer, initialState);
|
||||||
|
|
||||||
|
// We can use a fragment here, but we don't, because we want to style the counter
|
||||||
|
<div
|
||||||
|
style={ReactDOMRe.Style.make(~display="flex", ~alignItems="center", ~justifyContent="space-between", ())}>
|
||||||
|
<div>
|
||||||
|
{React.string("Count: ")}
|
||||||
|
{React.string(string_of_int(state.count))}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button style=leftButtonStyle onClick={_event => dispatch(Decrement)}>
|
||||||
|
{React.string("-")}
|
||||||
|
</button>
|
||||||
|
<button style=rightButtonStyle onClick={_event => dispatch(Increment)}>
|
||||||
|
{React.string("+")}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>;
|
||||||
|
};
|
39
my-react-app/watcher.js
Normal file
39
my-react-app/watcher.js
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
// This is our simple, robust watcher. It hooks into the BuckleScript build
|
||||||
|
// system to listen for build events.
|
||||||
|
// See package.json's `start` script and `./node_modules/.bin/bsb --help`
|
||||||
|
|
||||||
|
// Btw, if you change this file and reload the page, your browser cache
|
||||||
|
// _might_ not pick up the new version. If you're in Chrome, do Force Reload.
|
||||||
|
|
||||||
|
var websocketReloader;
|
||||||
|
var LAST_SUCCESS_BUILD_STAMP = localStorage.getItem('LAST_SUCCESS_BUILD_STAMP') || 0;
|
||||||
|
// package.json's `start` script's `bsb -ws _` means it'll pipe build events
|
||||||
|
// through a websocket connection to a default port of 9999. This is
|
||||||
|
// configurable, e.g. `-ws 5000`
|
||||||
|
var webSocketPort = 9999;
|
||||||
|
|
||||||
|
function setUpWebSocket() {
|
||||||
|
if (websocketReloader == null || websocketReloader.readyState !== 1) {
|
||||||
|
try {
|
||||||
|
websocketReloader = new WebSocket(`ws://localhost:${webSocketPort}`);
|
||||||
|
websocketReloader.onmessage = (message) => {
|
||||||
|
var newData = JSON.parse(message.data).LAST_SUCCESS_BUILD_STAMP;
|
||||||
|
if (newData > LAST_SUCCESS_BUILD_STAMP) {
|
||||||
|
LAST_SUCCESS_BUILD_STAMP = newData;
|
||||||
|
localStorage.setItem('LAST_SUCCESS_BUILD_STAMP', LAST_SUCCESS_BUILD_STAMP);
|
||||||
|
// Refresh the page! This will naturally re-run everything,
|
||||||
|
// including our moduleserve which will re-resolve all the modules.
|
||||||
|
// No stable build!
|
||||||
|
location.reload(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (exn) {
|
||||||
|
console.error("The watcher tried to connect to web socket, but failed. Here's the message:");
|
||||||
|
console.error(exn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
setUpWebSocket();
|
||||||
|
setInterval(setUpWebSocket, 2000);
|
27
package.json
Normal file
27
package.json
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"name": "probtest",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"scripts": {
|
||||||
|
"build": "bsb -make-world",
|
||||||
|
"start": "bsb -make-world -w -ws _ ",
|
||||||
|
"clean": "bsb -clean-world",
|
||||||
|
"server": "moduleserve ./ --port 8000",
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"BuckleScript",
|
||||||
|
"ReasonReact",
|
||||||
|
"reason-react"
|
||||||
|
],
|
||||||
|
"author": "",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"react": "^16.8.1",
|
||||||
|
"react-dom": "^16.8.1",
|
||||||
|
"reason-react": ">=0.7.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"bs-platform": "^7.1.0",
|
||||||
|
"moduleserve": "^0.9.0"
|
||||||
|
}
|
||||||
|
}
|
38
src/BlinkingGreeting/BlinkingGreeting.bs.js
Normal file
38
src/BlinkingGreeting/BlinkingGreeting.bs.js
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Curry = require("bs-platform/lib/js/curry.js");
|
||||||
|
var React = require("react");
|
||||||
|
|
||||||
|
function BlinkingGreeting(Props) {
|
||||||
|
var children = Props.children;
|
||||||
|
var match = React.useState((function () {
|
||||||
|
return true;
|
||||||
|
}));
|
||||||
|
var setShow = match[1];
|
||||||
|
React.useEffect((function () {
|
||||||
|
var id = setInterval((function (param) {
|
||||||
|
return Curry._1(setShow, (function (previousShow) {
|
||||||
|
return !previousShow;
|
||||||
|
}));
|
||||||
|
}), 1000);
|
||||||
|
return (function (param) {
|
||||||
|
clearInterval(id);
|
||||||
|
return /* () */0;
|
||||||
|
});
|
||||||
|
}), ([]));
|
||||||
|
var style = match[0] ? ({
|
||||||
|
opacity: "1",
|
||||||
|
transition: "opacity 1s"
|
||||||
|
}) : ({
|
||||||
|
opacity: "0",
|
||||||
|
transition: "opacity 1s"
|
||||||
|
});
|
||||||
|
return React.createElement("div", {
|
||||||
|
style: style
|
||||||
|
}, children);
|
||||||
|
}
|
||||||
|
|
||||||
|
var make = BlinkingGreeting;
|
||||||
|
|
||||||
|
exports.make = make;
|
||||||
|
/* react Not a pure module */
|
25
src/BlinkingGreeting/BlinkingGreeting.re
Normal file
25
src/BlinkingGreeting/BlinkingGreeting.re
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
[@react.component]
|
||||||
|
let make = (~children) => {
|
||||||
|
let (show, setShow) = React.useState(() => true);
|
||||||
|
|
||||||
|
// Notice that instead of `useEffect`, we have `useEffect0`. See
|
||||||
|
// reasonml.github.io/reason-react/docs/en/components#hooks for more info
|
||||||
|
React.useEffect0(() => {
|
||||||
|
let id =
|
||||||
|
Js.Global.setInterval(
|
||||||
|
() => setShow(previousShow => !previousShow),
|
||||||
|
1000,
|
||||||
|
);
|
||||||
|
|
||||||
|
Some(() => Js.Global.clearInterval(id));
|
||||||
|
});
|
||||||
|
|
||||||
|
let style =
|
||||||
|
if (show) {
|
||||||
|
ReactDOMRe.Style.make(~opacity="1", ~transition="opacity 1s", ());
|
||||||
|
} else {
|
||||||
|
ReactDOMRe.Style.make(~opacity="0", ~transition="opacity 1s", ());
|
||||||
|
};
|
||||||
|
|
||||||
|
<div style> children </div>;
|
||||||
|
};
|
10
src/ExampleStyles.bs.js
Normal file
10
src/ExampleStyles.bs.js
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
|
||||||
|
var reasonReactBlue = "#48a9dc";
|
||||||
|
|
||||||
|
var style = "\n body {\n background-color: rgb(224, 226, 229);\n display: flex;\n flex-direction: column;\n align-items: center;\n }\n button {\n background-color: white;\n color: " + (String(reasonReactBlue) + (";\n box-shadow: 0 0 0 1px " + (String(reasonReactBlue) + (";\n border: none;\n padding: 8px;\n font-size: 16px;\n }\n button:active {\n background-color: " + (String(reasonReactBlue) + ";\n color: white;\n }\n .container {\n margin: 12px 0px;\n box-shadow: 0px 4px 16px rgb(200, 200, 200);\n width: 720px;\n border-radius: 12px;\n font-family: sans-serif;\n }\n .containerTitle {\n background-color: rgb(242, 243, 245);\n border-radius: 12px 12px 0px 0px;\n padding: 12px;\n font-weight: bold;\n }\n .containerContent {\n background-color: white;\n padding: 16px;\n border-radius: 0px 0px 12px 12px;\n }\n")))));
|
||||||
|
|
||||||
|
exports.reasonReactBlue = reasonReactBlue;
|
||||||
|
exports.style = style;
|
||||||
|
/* style Not a pure module */
|
44
src/ExampleStyles.re
Normal file
44
src/ExampleStyles.re
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
let reasonReactBlue = "#48a9dc";
|
||||||
|
|
||||||
|
// The {j|...|j} feature is just string interpolation, from
|
||||||
|
// bucklescript.github.io/docs/en/interop-cheatsheet#string-unicode-interpolation
|
||||||
|
// This allows us to conveniently write CSS, together with variables, by
|
||||||
|
// constructing a string
|
||||||
|
let style = {j|
|
||||||
|
body {
|
||||||
|
background-color: rgb(224, 226, 229);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
background-color: white;
|
||||||
|
color: $reasonReactBlue;
|
||||||
|
box-shadow: 0 0 0 1px $reasonReactBlue;
|
||||||
|
border: none;
|
||||||
|
padding: 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
button:active {
|
||||||
|
background-color: $reasonReactBlue;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
margin: 12px 0px;
|
||||||
|
box-shadow: 0px 4px 16px rgb(200, 200, 200);
|
||||||
|
width: 720px;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
.containerTitle {
|
||||||
|
background-color: rgb(242, 243, 245);
|
||||||
|
border-radius: 12px 12px 0px 0px;
|
||||||
|
padding: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.containerContent {
|
||||||
|
background-color: white;
|
||||||
|
padding: 16px;
|
||||||
|
border-radius: 0px 0px 12px 12px;
|
||||||
|
}
|
||||||
|
|j};
|
64
src/FetchedDogPictures/FetchedDogPictures.bs.js
Normal file
64
src/FetchedDogPictures/FetchedDogPictures.bs.js
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Curry = require("bs-platform/lib/js/curry.js");
|
||||||
|
var React = require("react");
|
||||||
|
var Belt_Array = require("bs-platform/lib/js/belt_Array.js");
|
||||||
|
|
||||||
|
function FetchedDogPictures(Props) {
|
||||||
|
var match = React.useState((function () {
|
||||||
|
return /* LoadingDogs */0;
|
||||||
|
}));
|
||||||
|
var setState = match[1];
|
||||||
|
var state = match[0];
|
||||||
|
React.useEffect((function () {
|
||||||
|
fetch("https://dog.ceo/api/breeds/image/random/3").then((function (response) {
|
||||||
|
return response.json();
|
||||||
|
})).then((function (jsonResponse) {
|
||||||
|
Curry._1(setState, (function (_previousState) {
|
||||||
|
return /* LoadedDogs */[jsonResponse.message];
|
||||||
|
}));
|
||||||
|
return Promise.resolve(/* () */0);
|
||||||
|
})).catch((function (_err) {
|
||||||
|
Curry._1(setState, (function (_previousState) {
|
||||||
|
return /* ErrorFetchingDogs */1;
|
||||||
|
}));
|
||||||
|
return Promise.resolve(/* () */0);
|
||||||
|
}));
|
||||||
|
return ;
|
||||||
|
}), ([]));
|
||||||
|
var tmp;
|
||||||
|
if (typeof state === "number") {
|
||||||
|
tmp = state !== 0 ? "An error occurred!" : "Loading...";
|
||||||
|
} else {
|
||||||
|
var dogs = state[0];
|
||||||
|
tmp = Belt_Array.mapWithIndex(dogs, (function (i, dog) {
|
||||||
|
var imageStyle = {
|
||||||
|
backgroundImage: "url(" + (String(dog) + ")"),
|
||||||
|
backgroundPosition: "center",
|
||||||
|
height: "120px",
|
||||||
|
marginRight: i === (dogs.length - 1 | 0) ? "0px" : "8px",
|
||||||
|
width: "100%",
|
||||||
|
backgroundSize: "cover",
|
||||||
|
borderRadius: "8px",
|
||||||
|
boxShadow: "0px 4px 16px rgb(200, 200, 200)"
|
||||||
|
};
|
||||||
|
return React.createElement("div", {
|
||||||
|
key: dog,
|
||||||
|
style: imageStyle
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
return React.createElement("div", {
|
||||||
|
style: {
|
||||||
|
display: "flex",
|
||||||
|
height: "120px",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center"
|
||||||
|
}
|
||||||
|
}, tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
var make = FetchedDogPictures;
|
||||||
|
|
||||||
|
exports.make = make;
|
||||||
|
/* react Not a pure module */
|
70
src/FetchedDogPictures/FetchedDogPictures.re
Normal file
70
src/FetchedDogPictures/FetchedDogPictures.re
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
[@bs.val] external fetch: string => Js.Promise.t('a) = "fetch";
|
||||||
|
|
||||||
|
type state =
|
||||||
|
| LoadingDogs
|
||||||
|
| ErrorFetchingDogs
|
||||||
|
| LoadedDogs(array(string));
|
||||||
|
|
||||||
|
[@react.component]
|
||||||
|
let make = () => {
|
||||||
|
let (state, setState) = React.useState(() => LoadingDogs);
|
||||||
|
|
||||||
|
// Notice that instead of `useEffect`, we have `useEffect0`. See
|
||||||
|
// reasonml.github.io/reason-react/docs/en/components#hooks for more info
|
||||||
|
React.useEffect0(() => {
|
||||||
|
Js.Promise.(
|
||||||
|
fetch("https://dog.ceo/api/breeds/image/random/3")
|
||||||
|
|> then_(response => response##json())
|
||||||
|
|> then_(jsonResponse => {
|
||||||
|
setState(_previousState => LoadedDogs(jsonResponse##message));
|
||||||
|
Js.Promise.resolve();
|
||||||
|
})
|
||||||
|
|> catch(_err => {
|
||||||
|
setState(_previousState => ErrorFetchingDogs);
|
||||||
|
Js.Promise.resolve();
|
||||||
|
})
|
||||||
|
|> ignore
|
||||||
|
);
|
||||||
|
|
||||||
|
// Returning None, instead of Some(() => ...), means we don't have any
|
||||||
|
// cleanup to do before unmounting. That's not 100% true. We should
|
||||||
|
// technically cancel the promise. Unofortunately, there's currently no
|
||||||
|
// way to cancel a promise. Promises in general should be way less used
|
||||||
|
// for React components; but since folks do use them, we provide such an
|
||||||
|
// example here. In reality, this fetch should just be a plain callback,
|
||||||
|
// with a cancellation API
|
||||||
|
None;
|
||||||
|
});
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={ReactDOMRe.Style.make(
|
||||||
|
~height="120px",
|
||||||
|
~display="flex",
|
||||||
|
~alignItems="center",
|
||||||
|
~justifyContent="center",
|
||||||
|
(),
|
||||||
|
)}>
|
||||||
|
{switch (state) {
|
||||||
|
| ErrorFetchingDogs => React.string("An error occurred!")
|
||||||
|
| LoadingDogs => React.string("Loading...")
|
||||||
|
| LoadedDogs(dogs) =>
|
||||||
|
dogs
|
||||||
|
->Belt.Array.mapWithIndex((i, dog) => {
|
||||||
|
let imageStyle =
|
||||||
|
ReactDOMRe.Style.make(
|
||||||
|
~height="120px",
|
||||||
|
~width="100%",
|
||||||
|
~marginRight=i === Js.Array.length(dogs) - 1 ? "0px" : "8px",
|
||||||
|
~borderRadius="8px",
|
||||||
|
~boxShadow="0px 4px 16px rgb(200, 200, 200)",
|
||||||
|
~backgroundSize="cover",
|
||||||
|
~backgroundImage={j|url($dog)|j},
|
||||||
|
~backgroundPosition="center",
|
||||||
|
(),
|
||||||
|
);
|
||||||
|
<div key=dog style=imageStyle />;
|
||||||
|
})
|
||||||
|
->React.array
|
||||||
|
}}
|
||||||
|
</div>;
|
||||||
|
};
|
43
src/Index.bs.js
Normal file
43
src/Index.bs.js
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var React = require("react");
|
||||||
|
var ReactDom = require("react-dom");
|
||||||
|
var ExampleStyles$ReasonReactExamples = require("./ExampleStyles.bs.js");
|
||||||
|
var BlinkingGreeting$ReasonReactExamples = require("./BlinkingGreeting/BlinkingGreeting.bs.js");
|
||||||
|
var FetchedDogPictures$ReasonReactExamples = require("./FetchedDogPictures/FetchedDogPictures.bs.js");
|
||||||
|
var ReducerFromReactJSDocs$ReasonReactExamples = require("./ReducerFromReactJSDocs/ReducerFromReactJSDocs.bs.js");
|
||||||
|
var ReasonUsingJSUsingReason$ReasonReactExamples = require("./ReasonUsingJSUsingReason/ReasonUsingJSUsingReason.bs.js");
|
||||||
|
|
||||||
|
var style = document.createElement("style");
|
||||||
|
|
||||||
|
document.head.appendChild(style);
|
||||||
|
|
||||||
|
style.innerHTML = ExampleStyles$ReasonReactExamples.style;
|
||||||
|
|
||||||
|
function makeContainer(text) {
|
||||||
|
var container = document.createElement("div");
|
||||||
|
container.className = "container";
|
||||||
|
var title = document.createElement("div");
|
||||||
|
title.className = "containerTitle";
|
||||||
|
title.innerText = text;
|
||||||
|
var content = document.createElement("div");
|
||||||
|
content.className = "containerContent";
|
||||||
|
container.appendChild(title);
|
||||||
|
container.appendChild(content);
|
||||||
|
document.body.appendChild(container);
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReactDom.render(React.createElement(BlinkingGreeting$ReasonReactExamples.make, {
|
||||||
|
children: "Hello!"
|
||||||
|
}), makeContainer("Blinking Greeting"));
|
||||||
|
|
||||||
|
ReactDom.render(React.createElement(ReducerFromReactJSDocs$ReasonReactExamples.make, { }), makeContainer("Reducer From ReactJS Docs"));
|
||||||
|
|
||||||
|
ReactDom.render(React.createElement(FetchedDogPictures$ReasonReactExamples.make, { }), makeContainer("Fetched Dog Pictures"));
|
||||||
|
|
||||||
|
ReactDom.render(React.createElement(ReasonUsingJSUsingReason$ReasonReactExamples.make, { }), makeContainer("Reason Using JS Using Reason"));
|
||||||
|
|
||||||
|
exports.style = style;
|
||||||
|
exports.makeContainer = makeContainer;
|
||||||
|
/* style Not a pure module */
|
51
src/Index.re
Normal file
51
src/Index.re
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
// Entry point
|
||||||
|
|
||||||
|
[@bs.val] external document: Js.t({..}) = "document";
|
||||||
|
|
||||||
|
// We're using raw DOM manipulations here, to avoid making you read
|
||||||
|
// ReasonReact when you might precisely be trying to learn it for the first
|
||||||
|
// time through the examples later.
|
||||||
|
let style = document##createElement("style");
|
||||||
|
document##head##appendChild(style);
|
||||||
|
style##innerHTML #= ExampleStyles.style;
|
||||||
|
|
||||||
|
let makeContainer = text => {
|
||||||
|
let container = document##createElement("div");
|
||||||
|
container##className #= "container";
|
||||||
|
|
||||||
|
let title = document##createElement("div");
|
||||||
|
title##className #= "containerTitle";
|
||||||
|
title##innerText #= text;
|
||||||
|
|
||||||
|
let content = document##createElement("div");
|
||||||
|
content##className #= "containerContent";
|
||||||
|
|
||||||
|
let () = container##appendChild(title);
|
||||||
|
let () = container##appendChild(content);
|
||||||
|
let () = document##body##appendChild(container);
|
||||||
|
|
||||||
|
content;
|
||||||
|
};
|
||||||
|
|
||||||
|
// All 4 examples.
|
||||||
|
ReactDOMRe.render(
|
||||||
|
<BlinkingGreeting>
|
||||||
|
{React.string("Hello!")}
|
||||||
|
</BlinkingGreeting>,
|
||||||
|
makeContainer("Blinking Greeting"),
|
||||||
|
);
|
||||||
|
|
||||||
|
ReactDOMRe.render(
|
||||||
|
<ReducerFromReactJSDocs />,
|
||||||
|
makeContainer("Reducer From ReactJS Docs"),
|
||||||
|
);
|
||||||
|
|
||||||
|
ReactDOMRe.render(
|
||||||
|
<FetchedDogPictures />,
|
||||||
|
makeContainer("Fetched Dog Pictures"),
|
||||||
|
);
|
||||||
|
|
||||||
|
ReactDOMRe.render(
|
||||||
|
<ReasonUsingJSUsingReason />,
|
||||||
|
makeContainer("Reason Using JS Using Reason"),
|
||||||
|
);
|
31
src/ReasonUsingJSUsingReason/ReactJSCard.js
Normal file
31
src/ReasonUsingJSUsingReason/ReactJSCard.js
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
// In this Interop example folder, we have:
|
||||||
|
// - A ReasonReact component, ReasonReactCard.re
|
||||||
|
// - Used by a ReactJS component, ReactJSCard.js (this file)
|
||||||
|
// - ReactJSCard.js can be used by ReasonReact, through bindings in ReasonUsingJSUsingReason.re
|
||||||
|
// - ReasonUsingJSUsingReason.re is used by Index.re
|
||||||
|
|
||||||
|
var ReactDOM = require('react-dom');
|
||||||
|
var React = require('react');
|
||||||
|
|
||||||
|
var ReasonReactCard = require('./ReasonReactCard.bs').make;
|
||||||
|
|
||||||
|
var ReactJSComponent = function() {
|
||||||
|
let backgroundColor = "rgba(0, 0, 0, 0.05)";
|
||||||
|
let padding = "12px";
|
||||||
|
|
||||||
|
// We're not using JSX here, to avoid folks needing to install the related
|
||||||
|
// React toolchains just for this example.
|
||||||
|
// <div style={...}>
|
||||||
|
// <div style={...}>This is a ReactJS card</div>
|
||||||
|
// <ReasonReactCard style={...} />
|
||||||
|
// </div>
|
||||||
|
return React.createElement(
|
||||||
|
"div",
|
||||||
|
{style: {backgroundColor, padding, borderRadius: "8px"}},
|
||||||
|
React.createElement("div", {style: {marginBottom: "8px"}}, "This is a ReactJS card"),
|
||||||
|
React.createElement(ReasonReactCard, {style: {backgroundColor, padding, borderRadius: "4px"}}),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
ReactJSComponent.displayName = "MyBanner";
|
||||||
|
|
||||||
|
module.exports = ReactJSComponent;
|
15
src/ReasonUsingJSUsingReason/ReasonReactCard.bs.js
Normal file
15
src/ReasonUsingJSUsingReason/ReasonReactCard.bs.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var React = require("react");
|
||||||
|
|
||||||
|
function ReasonReactCard(Props) {
|
||||||
|
var style = Props.style;
|
||||||
|
return React.createElement("div", {
|
||||||
|
style: style
|
||||||
|
}, "This is a ReasonReact card");
|
||||||
|
}
|
||||||
|
|
||||||
|
var make = ReasonReactCard;
|
||||||
|
|
||||||
|
exports.make = make;
|
||||||
|
/* react Not a pure module */
|
10
src/ReasonUsingJSUsingReason/ReasonReactCard.re
Normal file
10
src/ReasonUsingJSUsingReason/ReasonReactCard.re
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
// In this Interop example folder, we have:
|
||||||
|
// - A ReasonReact component, ReasonReactCard.re (this file)
|
||||||
|
// - Used by a ReactJS component, ReactJSCard.js
|
||||||
|
// - ReactJSCard.js can be used by ReasonReact, through bindings in ReasonUsingJSUsingReason.re
|
||||||
|
// - ReasonUsingJSUsingReason.re is used by Index.re
|
||||||
|
|
||||||
|
[@react.component]
|
||||||
|
let make = (~style) => {
|
||||||
|
<div style> {React.string("This is a ReasonReact card")} </div>;
|
||||||
|
};
|
|
@ -0,0 +1,8 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var ReactJSCard = require("./ReactJSCard");
|
||||||
|
|
||||||
|
var make = ReactJSCard;
|
||||||
|
|
||||||
|
exports.make = make;
|
||||||
|
/* make Not a pure module */
|
10
src/ReasonUsingJSUsingReason/ReasonUsingJSUsingReason.re
Normal file
10
src/ReasonUsingJSUsingReason/ReasonUsingJSUsingReason.re
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
// In this Interop example folder, we have:
|
||||||
|
// - A ReasonReact component, ReasonReactCard.re
|
||||||
|
// - Used by a ReactJS component, ReactJSCard.js
|
||||||
|
// - ReactJSCard.js can be used by ReasonReact, through bindings in ReasonUsingJSUsingReason.re (this file)
|
||||||
|
// - ReasonUsingJSUsingReason.re is used by Index.re
|
||||||
|
|
||||||
|
// All you need to do to use a ReactJS component in ReasonReact, is to write the lines below!
|
||||||
|
// reasonml.github.io/reason-react/docs/en/components#import-from-js
|
||||||
|
[@react.component] [@bs.module]
|
||||||
|
external make: unit => React.element = "./ReactJSCard";
|
61
src/ReducerFromReactJSDocs/ReducerFromReactJSDocs.bs.js
Normal file
61
src/ReducerFromReactJSDocs/ReducerFromReactJSDocs.bs.js
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Curry = require("bs-platform/lib/js/curry.js");
|
||||||
|
var React = require("react");
|
||||||
|
|
||||||
|
var leftButtonStyle = {
|
||||||
|
width: "48px",
|
||||||
|
borderRadius: "4px 0px 0px 4px"
|
||||||
|
};
|
||||||
|
|
||||||
|
var rightButtonStyle = {
|
||||||
|
width: "48px",
|
||||||
|
borderRadius: "0px 4px 4px 0px"
|
||||||
|
};
|
||||||
|
|
||||||
|
var initialState = {
|
||||||
|
count: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
function reducer(state, action) {
|
||||||
|
if (action) {
|
||||||
|
return {
|
||||||
|
count: state.count - 1 | 0
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
count: state.count + 1 | 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ReducerFromReactJSDocs(Props) {
|
||||||
|
var match = React.useReducer(reducer, initialState);
|
||||||
|
var dispatch = match[1];
|
||||||
|
return React.createElement("div", {
|
||||||
|
style: {
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "space-between"
|
||||||
|
}
|
||||||
|
}, React.createElement("div", undefined, "Count: ", String(match[0].count)), React.createElement("div", undefined, React.createElement("button", {
|
||||||
|
style: leftButtonStyle,
|
||||||
|
onClick: (function (_event) {
|
||||||
|
return Curry._1(dispatch, /* Decrement */1);
|
||||||
|
})
|
||||||
|
}, "-"), React.createElement("button", {
|
||||||
|
style: rightButtonStyle,
|
||||||
|
onClick: (function (_event) {
|
||||||
|
return Curry._1(dispatch, /* Increment */0);
|
||||||
|
})
|
||||||
|
}, "+")));
|
||||||
|
}
|
||||||
|
|
||||||
|
var make = ReducerFromReactJSDocs;
|
||||||
|
|
||||||
|
exports.leftButtonStyle = leftButtonStyle;
|
||||||
|
exports.rightButtonStyle = rightButtonStyle;
|
||||||
|
exports.initialState = initialState;
|
||||||
|
exports.reducer = reducer;
|
||||||
|
exports.make = make;
|
||||||
|
/* react Not a pure module */
|
44
src/ReducerFromReactJSDocs/ReducerFromReactJSDocs.re
Normal file
44
src/ReducerFromReactJSDocs/ReducerFromReactJSDocs.re
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
// This is the ReactJS documentation's useReducer example, directly ported over
|
||||||
|
// https://reactjs.org/docs/hooks-reference.html#usereducer
|
||||||
|
|
||||||
|
// A little extra we've put, because the ReactJS example has no styling
|
||||||
|
let leftButtonStyle = ReactDOMRe.Style.make(~borderRadius="4px 0px 0px 4px", ~width="48px", ());
|
||||||
|
let rightButtonStyle = ReactDOMRe.Style.make(~borderRadius="0px 4px 4px 0px", ~width="48px", ());
|
||||||
|
|
||||||
|
// Record and variant need explicit declarations.
|
||||||
|
type state = {count: int};
|
||||||
|
|
||||||
|
type action =
|
||||||
|
| Increment
|
||||||
|
| Decrement;
|
||||||
|
|
||||||
|
let initialState = {count: 0};
|
||||||
|
|
||||||
|
let reducer = (state, action) => {
|
||||||
|
switch (action) {
|
||||||
|
| Increment => {count: state.count + 1}
|
||||||
|
| Decrement => {count: state.count - 1}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
[@react.component]
|
||||||
|
let make = () => {
|
||||||
|
let (state, dispatch) = React.useReducer(reducer, initialState);
|
||||||
|
|
||||||
|
// We can use a fragment here, but we don't, because we want to style the counter
|
||||||
|
<div
|
||||||
|
style={ReactDOMRe.Style.make(~display="flex", ~alignItems="center", ~justifyContent="space-between", ())}>
|
||||||
|
<div>
|
||||||
|
{React.string("Count: ")}
|
||||||
|
{React.string(string_of_int(state.count))}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button style=leftButtonStyle onClick={_event => dispatch(Decrement)}>
|
||||||
|
{React.string("-")}
|
||||||
|
</button>
|
||||||
|
<button style=rightButtonStyle onClick={_event => dispatch(Increment)}>
|
||||||
|
{React.string("+")}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>;
|
||||||
|
};
|
63
src/Test1.bs.js
Normal file
63
src/Test1.bs.js
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Block = require("bs-platform/lib/js/block.js");
|
||||||
|
|
||||||
|
function foo(joint) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
statements: [
|
||||||
|
{
|
||||||
|
statement: /* Senate */Block.__(0, [/* DEMOCRAT_VICTORY */0]),
|
||||||
|
outcome: /* Bool */Block.__(0, [true])
|
||||||
|
},
|
||||||
|
{
|
||||||
|
statement: /* House */Block.__(1, [/* DEMOCRAT_VICTORY */0]),
|
||||||
|
outcome: /* Bool */Block.__(0, [true])
|
||||||
|
}
|
||||||
|
],
|
||||||
|
probability: 0.2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
statements: [
|
||||||
|
{
|
||||||
|
statement: /* Senate */Block.__(0, [/* DEMOCRAT_VICTORY */0]),
|
||||||
|
outcome: /* Bool */Block.__(0, [true])
|
||||||
|
},
|
||||||
|
{
|
||||||
|
statement: /* House */Block.__(1, [/* DEMOCRAT_VICTORY */0]),
|
||||||
|
outcome: /* Bool */Block.__(0, [false])
|
||||||
|
}
|
||||||
|
],
|
||||||
|
probability: 0.2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
statements: [
|
||||||
|
{
|
||||||
|
statement: /* Senate */Block.__(0, [/* DEMOCRAT_VICTORY */0]),
|
||||||
|
outcome: /* Bool */Block.__(0, [false])
|
||||||
|
},
|
||||||
|
{
|
||||||
|
statement: /* House */Block.__(1, [/* DEMOCRAT_VICTORY */0]),
|
||||||
|
outcome: /* Bool */Block.__(0, [true])
|
||||||
|
}
|
||||||
|
],
|
||||||
|
probability: 0.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
statements: [
|
||||||
|
{
|
||||||
|
statement: /* Senate */Block.__(0, [/* DEMOCRAT_VICTORY */0]),
|
||||||
|
outcome: /* Bool */Block.__(0, [false])
|
||||||
|
},
|
||||||
|
{
|
||||||
|
statement: /* House */Block.__(1, [/* DEMOCRAT_VICTORY */0]),
|
||||||
|
outcome: /* Bool */Block.__(0, [false])
|
||||||
|
}
|
||||||
|
],
|
||||||
|
probability: 0.1
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.foo = foo;
|
||||||
|
/* No side effect */
|
56
src/Test1.re
Normal file
56
src/Test1.re
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
type senate =
|
||||||
|
| DEMOCRAT_VICTORY;
|
||||||
|
|
||||||
|
type house =
|
||||||
|
| DEMOCRAT_VICTORY;
|
||||||
|
|
||||||
|
type statement =
|
||||||
|
| Senate(senate)
|
||||||
|
| House(house);
|
||||||
|
|
||||||
|
type outcome =
|
||||||
|
| Bool(bool)
|
||||||
|
| Option(string);
|
||||||
|
|
||||||
|
type fullStatement = {
|
||||||
|
statement,
|
||||||
|
outcome,
|
||||||
|
};
|
||||||
|
|
||||||
|
type jointStatement = {
|
||||||
|
statements: array(fullStatement),
|
||||||
|
probability: option(float),
|
||||||
|
};
|
||||||
|
|
||||||
|
let foo = (joint: jointStatement) => {
|
||||||
|
[|
|
||||||
|
{
|
||||||
|
statements: [|
|
||||||
|
{statement: Senate(DEMOCRAT_VICTORY), outcome: Bool(true)},
|
||||||
|
{statement: House(DEMOCRAT_VICTORY), outcome: Bool(true)},
|
||||||
|
|],
|
||||||
|
probability: Some(0.2),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
statements: [|
|
||||||
|
{statement: Senate(DEMOCRAT_VICTORY), outcome: Bool(true)},
|
||||||
|
{statement: House(DEMOCRAT_VICTORY), outcome: Bool(false)},
|
||||||
|
|],
|
||||||
|
probability: Some(0.2),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
statements: [|
|
||||||
|
{statement: Senate(DEMOCRAT_VICTORY), outcome: Bool(false)},
|
||||||
|
{statement: House(DEMOCRAT_VICTORY), outcome: Bool(true)},
|
||||||
|
|],
|
||||||
|
probability: Some(0.5),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
statements: [|
|
||||||
|
{statement: Senate(DEMOCRAT_VICTORY), outcome: Bool(false)},
|
||||||
|
{statement: House(DEMOCRAT_VICTORY), outcome: Bool(false)},
|
||||||
|
|],
|
||||||
|
probability: Some(0.1),
|
||||||
|
},
|
||||||
|
|];
|
||||||
|
};
|
49
src/Test2.bs.js
Normal file
49
src/Test2.bs.js
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
|
||||||
|
function normal(mean, std) {
|
||||||
|
var nMean = mean.toString();
|
||||||
|
var nStd = std.toString();
|
||||||
|
return "normal(" + (String(nMean) + (", " + (String(nStd) + ")")));
|
||||||
|
}
|
||||||
|
|
||||||
|
function divide(str1, str2) {
|
||||||
|
return "" + (String(str1) + ("/" + (String(str2) + "")));
|
||||||
|
}
|
||||||
|
|
||||||
|
var $$Math = {
|
||||||
|
normal: normal,
|
||||||
|
divide: divide
|
||||||
|
};
|
||||||
|
|
||||||
|
function run(company, year, param) {
|
||||||
|
var match = company.currentPrice;
|
||||||
|
switch (param) {
|
||||||
|
case /* SHARE_PRICE */0 :
|
||||||
|
if (match !== undefined && year > 2019 && year < 2030) {
|
||||||
|
var diffYears = year - 2020 | 0;
|
||||||
|
return normal(match, diffYears * 0.1);
|
||||||
|
} else {
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
case /* SHARES_OUTSTANDING */1 :
|
||||||
|
if (year > 2019 && year < 2030) {
|
||||||
|
var price = run(company, year, /* SHARE_PRICE */0);
|
||||||
|
var marketCap = run(company, year, /* MARKET_CAP */2);
|
||||||
|
if (price !== undefined && marketCap !== undefined) {
|
||||||
|
return divide(marketCap, price);
|
||||||
|
} else {
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
case /* MARKET_CAP */2 :
|
||||||
|
return ;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.$$Math = $$Math;
|
||||||
|
exports.run = run;
|
||||||
|
/* No side effect */
|
43
src/Test2.re
Normal file
43
src/Test2.re
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
module Math = {
|
||||||
|
let normal = (mean: float, std: float) =>
|
||||||
|
Js.Float.(
|
||||||
|
{
|
||||||
|
let nMean = toString(mean);
|
||||||
|
let nStd = toString(std);
|
||||||
|
{j|normal($(nMean), $(nStd))|j};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let divide = (str1: string, str2: string) => {j|$(str1)/$(str2)|j};
|
||||||
|
};
|
||||||
|
|
||||||
|
type param =
|
||||||
|
| SHARE_PRICE
|
||||||
|
| SHARES_OUTSTANDING
|
||||||
|
| MARKET_CAP;
|
||||||
|
|
||||||
|
type company = {
|
||||||
|
name: string,
|
||||||
|
currentPrice: option(float),
|
||||||
|
};
|
||||||
|
|
||||||
|
let rec run = (company: company, year: int, param: param) => {
|
||||||
|
switch (param, year, company.currentPrice) {
|
||||||
|
| (SHARE_PRICE, year, Some(price)) when year > 2019 && year < 2030 =>
|
||||||
|
let diffYears = year - 2020;
|
||||||
|
let diffPerYear = 0.1;
|
||||||
|
Some(Math.normal(price, float_of_int(diffYears) *. diffPerYear));
|
||||||
|
|
||||||
|
| (SHARES_OUTSTANDING, year, _) when year > 2019 && year < 2030 =>
|
||||||
|
let price = run(company, year, SHARE_PRICE);
|
||||||
|
let marketCap = run(company, year, MARKET_CAP);
|
||||||
|
switch (price, marketCap) {
|
||||||
|
| (Some(price), Some(marketCap)) =>
|
||||||
|
Some(Math.divide(marketCap, price))
|
||||||
|
|
||||||
|
| _ => None
|
||||||
|
};
|
||||||
|
|
||||||
|
| _ => None
|
||||||
|
};
|
||||||
|
};
|
39
watcher.js
Normal file
39
watcher.js
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
// This is our simple, robust watcher. It hooks into the BuckleScript build
|
||||||
|
// system to listen for build events.
|
||||||
|
// See package.json's `start` script and `./node_modules/.bin/bsb --help`
|
||||||
|
|
||||||
|
// Btw, if you change this file and reload the page, your browser cache
|
||||||
|
// _might_ not pick up the new version. If you're in Chrome, do Force Reload.
|
||||||
|
|
||||||
|
var websocketReloader;
|
||||||
|
var LAST_SUCCESS_BUILD_STAMP = localStorage.getItem('LAST_SUCCESS_BUILD_STAMP') || 0;
|
||||||
|
// package.json's `start` script's `bsb -ws _` means it'll pipe build events
|
||||||
|
// through a websocket connection to a default port of 9999. This is
|
||||||
|
// configurable, e.g. `-ws 5000`
|
||||||
|
var webSocketPort = 9999;
|
||||||
|
|
||||||
|
function setUpWebSocket() {
|
||||||
|
if (websocketReloader == null || websocketReloader.readyState !== 1) {
|
||||||
|
try {
|
||||||
|
websocketReloader = new WebSocket(`ws://localhost:${webSocketPort}`);
|
||||||
|
websocketReloader.onmessage = (message) => {
|
||||||
|
var newData = JSON.parse(message.data).LAST_SUCCESS_BUILD_STAMP;
|
||||||
|
if (newData > LAST_SUCCESS_BUILD_STAMP) {
|
||||||
|
LAST_SUCCESS_BUILD_STAMP = newData;
|
||||||
|
localStorage.setItem('LAST_SUCCESS_BUILD_STAMP', LAST_SUCCESS_BUILD_STAMP);
|
||||||
|
// Refresh the page! This will naturally re-run everything,
|
||||||
|
// including our moduleserve which will re-resolve all the modules.
|
||||||
|
// No stable build!
|
||||||
|
location.reload(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (exn) {
|
||||||
|
console.error("The watcher tried to connect to web socket, but failed. Here's the message:");
|
||||||
|
console.error(exn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
setUpWebSocket();
|
||||||
|
setInterval(setUpWebSocket, 2000);
|
216
yarn.lock
Normal file
216
yarn.lock
Normal file
|
@ -0,0 +1,216 @@
|
||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
bs-platform@^7.1.0:
|
||||||
|
version "7.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/bs-platform/-/bs-platform-7.1.0.tgz#72b52148b1c4be7f878969e6e2afd1bfab068cdd"
|
||||||
|
integrity sha512-XUeZf1nGzmsVymG89j5L8G9YNDHl0J/5iDGExXA7a4RKxnbvP2TselBZAzFeEH4rs3gG01b7yKt+h2pm7yh7Ww==
|
||||||
|
|
||||||
|
debug@2.6.9:
|
||||||
|
version "2.6.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||||
|
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
|
||||||
|
dependencies:
|
||||||
|
ms "2.0.0"
|
||||||
|
|
||||||
|
depd@~1.1.2:
|
||||||
|
version "1.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
|
||||||
|
integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
|
||||||
|
|
||||||
|
destroy@~1.0.4:
|
||||||
|
version "1.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
|
||||||
|
integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
|
||||||
|
|
||||||
|
ee-first@1.1.1:
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
||||||
|
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
|
||||||
|
|
||||||
|
encodeurl@~1.0.2:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
|
||||||
|
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
|
||||||
|
|
||||||
|
escape-html@~1.0.3:
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
|
||||||
|
integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=
|
||||||
|
|
||||||
|
etag@~1.8.1:
|
||||||
|
version "1.8.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
|
||||||
|
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
|
||||||
|
|
||||||
|
fresh@0.5.2:
|
||||||
|
version "0.5.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
|
||||||
|
integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
|
||||||
|
|
||||||
|
http-errors@~1.7.2:
|
||||||
|
version "1.7.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06"
|
||||||
|
integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==
|
||||||
|
dependencies:
|
||||||
|
depd "~1.1.2"
|
||||||
|
inherits "2.0.4"
|
||||||
|
setprototypeof "1.1.1"
|
||||||
|
statuses ">= 1.5.0 < 2"
|
||||||
|
toidentifier "1.0.0"
|
||||||
|
|
||||||
|
inherits@2.0.4:
|
||||||
|
version "2.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||||
|
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||||
|
|
||||||
|
"js-tokens@^3.0.0 || ^4.0.0":
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||||
|
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
|
||||||
|
|
||||||
|
loose-envify@^1.1.0, loose-envify@^1.4.0:
|
||||||
|
version "1.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
||||||
|
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
|
||||||
|
dependencies:
|
||||||
|
js-tokens "^3.0.0 || ^4.0.0"
|
||||||
|
|
||||||
|
mime@1.6.0:
|
||||||
|
version "1.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
|
||||||
|
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
|
||||||
|
|
||||||
|
moduleserve@^0.9.0:
|
||||||
|
version "0.9.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/moduleserve/-/moduleserve-0.9.1.tgz#11bad4337ea248d7eaf10d2c7f8649a8c3b9c1f8"
|
||||||
|
integrity sha512-WF2BeGnM2Ko7bdICgJO+Ibu+ZD33ExJHzOzTDsCUzfZnvnFfheEIYBTWyIqSRU0tXh4UTQ1krDOCglFTJPBMow==
|
||||||
|
dependencies:
|
||||||
|
send "^0.17.1"
|
||||||
|
serve-static "^1.14.1"
|
||||||
|
|
||||||
|
ms@2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||||
|
integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
|
||||||
|
|
||||||
|
ms@2.1.1:
|
||||||
|
version "2.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
|
||||||
|
integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==
|
||||||
|
|
||||||
|
object-assign@^4.1.1:
|
||||||
|
version "4.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||||
|
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
|
||||||
|
|
||||||
|
on-finished@~2.3.0:
|
||||||
|
version "2.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
|
||||||
|
integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=
|
||||||
|
dependencies:
|
||||||
|
ee-first "1.1.1"
|
||||||
|
|
||||||
|
parseurl@~1.3.3:
|
||||||
|
version "1.3.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
|
||||||
|
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
|
||||||
|
|
||||||
|
prop-types@^15.6.2:
|
||||||
|
version "15.7.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
|
||||||
|
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
|
||||||
|
dependencies:
|
||||||
|
loose-envify "^1.4.0"
|
||||||
|
object-assign "^4.1.1"
|
||||||
|
react-is "^16.8.1"
|
||||||
|
|
||||||
|
range-parser@~1.2.1:
|
||||||
|
version "1.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
|
||||||
|
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
|
||||||
|
|
||||||
|
react-dom@>=16.8.1, react-dom@^16.8.1:
|
||||||
|
version "16.12.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.12.0.tgz#0da4b714b8d13c2038c9396b54a92baea633fe11"
|
||||||
|
integrity sha512-LMxFfAGrcS3kETtQaCkTKjMiifahaMySFDn71fZUNpPHZQEzmk/GiAeIT8JSOrHB23fnuCOMruL2a8NYlw+8Gw==
|
||||||
|
dependencies:
|
||||||
|
loose-envify "^1.1.0"
|
||||||
|
object-assign "^4.1.1"
|
||||||
|
prop-types "^15.6.2"
|
||||||
|
scheduler "^0.18.0"
|
||||||
|
|
||||||
|
react-is@^16.8.1:
|
||||||
|
version "16.12.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c"
|
||||||
|
integrity sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==
|
||||||
|
|
||||||
|
react@>=16.8.1, react@^16.8.1:
|
||||||
|
version "16.12.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react/-/react-16.12.0.tgz#0c0a9c6a142429e3614834d5a778e18aa78a0b83"
|
||||||
|
integrity sha512-fglqy3k5E+81pA8s+7K0/T3DBCF0ZDOher1elBFzF7O6arXJgzyu/FW+COxFvAWXJoJN9KIZbT2LXlukwphYTA==
|
||||||
|
dependencies:
|
||||||
|
loose-envify "^1.1.0"
|
||||||
|
object-assign "^4.1.1"
|
||||||
|
prop-types "^15.6.2"
|
||||||
|
|
||||||
|
reason-react@>=0.7.0:
|
||||||
|
version "0.7.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/reason-react/-/reason-react-0.7.0.tgz#46a975c321e81cd51310d7b1a02418ca7667b0d6"
|
||||||
|
integrity sha512-czR/f0lY5iyLCki9gwftOFF5Zs40l7ZSFmpGK/Z6hx2jBVeFDmIiXB8bAQW/cO6IvtuEt97OmsYueiuOYG9XjQ==
|
||||||
|
dependencies:
|
||||||
|
react ">=16.8.1"
|
||||||
|
react-dom ">=16.8.1"
|
||||||
|
|
||||||
|
scheduler@^0.18.0:
|
||||||
|
version "0.18.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.18.0.tgz#5901ad6659bc1d8f3fdaf36eb7a67b0d6746b1c4"
|
||||||
|
integrity sha512-agTSHR1Nbfi6ulI0kYNK0203joW2Y5W4po4l+v03tOoiJKpTBbxpNhWDvqc/4IcOw+KLmSiQLTasZ4cab2/UWQ==
|
||||||
|
dependencies:
|
||||||
|
loose-envify "^1.1.0"
|
||||||
|
object-assign "^4.1.1"
|
||||||
|
|
||||||
|
send@0.17.1, send@^0.17.1:
|
||||||
|
version "0.17.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8"
|
||||||
|
integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==
|
||||||
|
dependencies:
|
||||||
|
debug "2.6.9"
|
||||||
|
depd "~1.1.2"
|
||||||
|
destroy "~1.0.4"
|
||||||
|
encodeurl "~1.0.2"
|
||||||
|
escape-html "~1.0.3"
|
||||||
|
etag "~1.8.1"
|
||||||
|
fresh "0.5.2"
|
||||||
|
http-errors "~1.7.2"
|
||||||
|
mime "1.6.0"
|
||||||
|
ms "2.1.1"
|
||||||
|
on-finished "~2.3.0"
|
||||||
|
range-parser "~1.2.1"
|
||||||
|
statuses "~1.5.0"
|
||||||
|
|
||||||
|
serve-static@^1.14.1:
|
||||||
|
version "1.14.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9"
|
||||||
|
integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==
|
||||||
|
dependencies:
|
||||||
|
encodeurl "~1.0.2"
|
||||||
|
escape-html "~1.0.3"
|
||||||
|
parseurl "~1.3.3"
|
||||||
|
send "0.17.1"
|
||||||
|
|
||||||
|
setprototypeof@1.1.1:
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683"
|
||||||
|
integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==
|
||||||
|
|
||||||
|
"statuses@>= 1.5.0 < 2", statuses@~1.5.0:
|
||||||
|
version "1.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
|
||||||
|
integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
|
||||||
|
|
||||||
|
toidentifier@1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
|
||||||
|
integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
|
Loading…
Reference in New Issue
Block a user