Merge pull request #37 from QURIresearch/playground-refactor
Playground to TypeScript
This commit is contained in:
commit
89c945f395
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,4 +1,5 @@
|
|||
node_modules
|
||||
yarn-error.log
|
||||
.cache
|
||||
.merlin
|
||||
.parcel-cache
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 347 KiB |
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"packages": [
|
||||
"packages/*"
|
||||
],
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
"version": "0.0.0"
|
||||
}
|
17
package.json
17
package.json
|
@ -1,14 +1,21 @@
|
|||
{
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"lerna": "^4.0.0"
|
||||
},
|
||||
"name": "squiggle",
|
||||
"scripts": {
|
||||
"build:lang": "cd packages/squiggle-lang && yarn && yarn build && yarn package",
|
||||
"storybook:components": "cd packages/components && yarn && yarn storybook",
|
||||
"build-storybook:components": "cd packages/components && yarn && yarn build-storybook",
|
||||
"build:components": "cd packages/components && yarn && yarn package"
|
||||
"build:components": "cd packages/components && yarn && yarn package",
|
||||
"build:playground": "cd packages/playground && yarn && yarn parcel-build",
|
||||
"ci:lang": "yarn workspace @squiggle/lang ci",
|
||||
"ci:components": "yarn ci:lang && yarn workspace @squiggle/components ci",
|
||||
"ci:playground": "yarn ci:components && yarn workspace @squiggle/playground ci"
|
||||
},
|
||||
"workspaces": ["packages/*"]
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
"resolutions": {
|
||||
"@types/react": "17.0.39"
|
||||
},
|
||||
"packageManager": "yarn@1.22.17"
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@squiggle/lang": "^0.1.9",
|
||||
"@squiggle/lang": "0.1.9",
|
||||
"@testing-library/jest-dom": "^5.16.2",
|
||||
"@testing-library/react": "^12.1.2",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
|
@ -20,14 +20,17 @@
|
|||
"react-vega": "^7.4.4",
|
||||
"tsconfig-paths-webpack-plugin": "^3.5.2",
|
||||
"typescript": "^4.5.5",
|
||||
"vega": "^5.21.0",
|
||||
"vega-embed": "^6.20.6",
|
||||
"vega-lite": "^5.2.0",
|
||||
"web-vitals": "^2.1.4",
|
||||
"webpack-cli": "^4.9.2"
|
||||
},
|
||||
"scripts": {
|
||||
"storybook": "cross-env REACT_APP_FAST_REFRESH=false && start-storybook -p 6006 -s public",
|
||||
"build-storybook": "build-storybook -s public",
|
||||
"package": "tsc"
|
||||
"package": "tsc",
|
||||
"ci": "yarn package"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
|
@ -68,6 +71,9 @@
|
|||
"@storybook/react": "^6.4.18",
|
||||
"webpack": "^5.68.0"
|
||||
},
|
||||
"main": "dist/lib.js",
|
||||
"types": "dist/lib.d.ts"
|
||||
"resolutions": {
|
||||
"@types/react": "17.0.39"
|
||||
},
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts"
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
"module": "commonjs",
|
||||
"jsx": "react",
|
||||
"noImplicitAny": false,
|
||||
"esModuleInterop": true,
|
||||
"removeComments": true,
|
||||
"preserveConstEnums": true,
|
||||
"resolveJsonModule": true,
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,22 +0,0 @@
|
|||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Foretold
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,19 +1,19 @@
|
|||
# Squiggle
|
||||
# Squiggle Playground
|
||||
|
||||
This is an experiment DSL/language for making probabilistic estimates.
|
||||
This repository contains the squiggle playground, a small web interface
|
||||
for playing around with squiggle concepts.
|
||||
|
||||
## DistPlus
|
||||
We have a custom library called DistPlus to handle distributions with additional metadata. This helps handle mixed distributions (continuous + discrete), a cache for a cdf, possible unit types (specific times are supported), and limited domains.
|
||||
|
||||
## Running
|
||||
|
||||
Currently it only has a few very simple models.
|
||||
It depends on `@squiggle/components` and `@squiggle/lang` so both of them will
|
||||
need to be packaged for this to work. This can be done from the root directory
|
||||
with
|
||||
|
||||
```
|
||||
yarn
|
||||
yarn run start
|
||||
yarn run parcel
|
||||
yarn build:lang
|
||||
yarn build:components
|
||||
```
|
||||
|
||||
## Expected future setup
|
||||
![setup](https://raw.githubusercontent.com/foretold-app/widedomain/master/Screen%20Shot%202020-06-30%20at%208.27.32%20AM.png)
|
||||
Then, starting the playground can be done with:
|
||||
|
||||
```
|
||||
yarn parcel
|
||||
```
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 347 KiB |
|
@ -1,54 +0,0 @@
|
|||
{
|
||||
"name": "probExample",
|
||||
"reason": {
|
||||
"react-jsx": 3
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"dir": "src",
|
||||
"subdirs": true
|
||||
},
|
||||
{
|
||||
"dir": "showcase",
|
||||
"type": "dev",
|
||||
"subdirs": true
|
||||
},
|
||||
{
|
||||
"dir": "__tests__",
|
||||
"type": "dev",
|
||||
"subdirs": true
|
||||
}
|
||||
],
|
||||
"bsc-flags": [
|
||||
"-bs-super-errors",
|
||||
"-bs-no-version-header",
|
||||
"-bs-g"
|
||||
],
|
||||
"package-specs": [
|
||||
{
|
||||
"module": "commonjs",
|
||||
"in-source": true
|
||||
}
|
||||
],
|
||||
"suffix": ".bs.js",
|
||||
"namespace": true,
|
||||
"bs-dependencies": [
|
||||
"@glennsl/bs-jest",
|
||||
"@glennsl/bs-json",
|
||||
"@rescriptbr/reform",
|
||||
"@rescript/react",
|
||||
"bs-css",
|
||||
"bs-css-emotion",
|
||||
"@squiggle/lang",
|
||||
"rationale",
|
||||
"bs-moment",
|
||||
"reschema"
|
||||
],
|
||||
"refmt": 3,
|
||||
"warnings": {
|
||||
"number": "+A-42-48-9-30-4-102"
|
||||
},
|
||||
"ppx-flags": [
|
||||
"lenses-ppx/ppx"
|
||||
]
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
# Squiggle
|
||||
|
||||
Squiggle is a DSL for making probabilistic estimations. It is meant
|
||||
to be a programming analogue of the [Guesstimate](https://www.getguesstimate.com/)
|
||||
application.
|
||||
|
||||
There are several use cases for a language that represent uncertainty. Some include
|
||||
writing cost effectiveness analysis, as well as doing accurate forecasting.
|
||||
|
||||
Squiggle is written in [Rescript](https://rescript-lang.org/) and is presented
|
||||
as a website.
|
||||
|
||||
If you wish to try squiggle out, you can visit our [main page](https://squiggle-language.com/).
|
||||
|
||||
## Syntax
|
||||
We use the [Math.js expression language](https://mathjs.org/index.html) for Squiggle.
|
||||
So any expression that's available on Math.js is supported on Squiggle.
|
||||
|
||||
To represent uncertainty, we use a custom DSL called [DistML](https://docs.google.com/document/d/1xlEC8KjchP4PL-KdSxfBJr0UZ9nVMSAlz-rjAQEinyA/edit#).
|
||||
|
||||
## Contributing
|
||||
To contribute to this project, we recommend visiting our [Contributing Guide](contributing.md).
|
|
@ -1,27 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Squiggle Documentation</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<meta name="description" content="Description">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
|
||||
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@4/lib/themes/vue.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script>
|
||||
window.$docsify = {
|
||||
name: 'Squiggle',
|
||||
repo: 'squiggle/lang',
|
||||
loadSidebar: true
|
||||
}
|
||||
</script>
|
||||
<!-- Docsify v4 -->
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
|
||||
<!-- CDN files for docsify-katex -->
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify-katex@latest/dist/docsify-katex.js"></script>
|
||||
<!-- or <script src="//cdn.jsdelivr.net/gh/upupming/docsify-katex@latest/dist/docsify-katex.js"></script> -->
|
||||
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/katex@latest/dist/katex.min.css"/>
|
||||
</body>
|
||||
</html>
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"devDependencies": {
|
||||
"docsify-cli": "^4.4.3"
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -3,48 +3,23 @@
|
|||
"version": "0.1.0",
|
||||
"homepage": "https://foretold-app.github.io/estiband/",
|
||||
"scripts": {
|
||||
"build": "rescript build",
|
||||
"build:deps": "rescript build -with-deps",
|
||||
"build:style": "tailwind build src/styles/index.css -o src/styles/tailwind.css",
|
||||
"start": "rescript build -w",
|
||||
"clean": "rescript clean",
|
||||
"parcel": "parcel ./src/index.html",
|
||||
"parcel-build": "parcel build ./src/index.html --no-source-maps --no-autoinstall",
|
||||
"showcase": "PORT=12345 parcel showcase/index.html",
|
||||
"server": "moduleserve ./ --port 8000",
|
||||
"predeploy": "parcel build ./src/index.html --no-source-maps --no-autoinstall",
|
||||
"parcel-build": "parcel build ./src/index.html --no-source-maps --no-autoinstall --no-scope-hoist",
|
||||
"deploy": "gh-pages -d dist",
|
||||
"test": "jest",
|
||||
"test:ci": "yarn jest",
|
||||
"watch:test": "jest --watchAll",
|
||||
"watch:s": "yarn jest -- Converter_test --watch"
|
||||
"ci": "yarn parcel-build"
|
||||
},
|
||||
"keywords": [
|
||||
"BuckleScript",
|
||||
"ReasonReact",
|
||||
"reason-react"
|
||||
],
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@glennsl/bs-json": "^5.0.2",
|
||||
"@rescript/react": "^0.10.3",
|
||||
"@rescriptbr/reform": "^11.0.1",
|
||||
"@emotion/react": "^11.8.1",
|
||||
"@squiggle/lang": "^0.1.9",
|
||||
"ace-builds": "^1.4.12",
|
||||
"antd": "^4.18.5",
|
||||
"autoprefixer": "9.8.8",
|
||||
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
|
||||
"binary-search-tree": "0.2.6",
|
||||
"bs-ant-design-alt": "2.0.0-alpha.33",
|
||||
"bs-css": "^15.1.0",
|
||||
"bs-css-emotion": "^4.1.0",
|
||||
"bs-moment": "0.6.0",
|
||||
"bsb-js": "1.1.7",
|
||||
"css-loader": "^6.6.0",
|
||||
"d3": "7.3.0",
|
||||
"gh-pages": "2.2.0",
|
||||
"jest": "^25.5.1",
|
||||
"jstat": "1.9.2",
|
||||
"lenses-ppx": "5.1.0",
|
||||
"less": "3.10.3",
|
||||
|
@ -53,27 +28,27 @@
|
|||
"moduleserve": "0.9.1",
|
||||
"moment": "2.24.0",
|
||||
"pdfast": "^0.2.0",
|
||||
"postcss": "^8.4.7",
|
||||
"postcss-cli": "^9.1.0",
|
||||
"rationale": "0.2.0",
|
||||
"react": "17.0.2",
|
||||
"react-ace": "^9.2.0",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-use": "^17.3.2",
|
||||
"react-vega": "^7.4.4",
|
||||
"reschema": "^2.2.0",
|
||||
"rescript": "^9.1.4",
|
||||
"tailwindcss": "1.2.0",
|
||||
"vega": "*",
|
||||
"vega-embed": "6.6.0",
|
||||
"vega-lite": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@glennsl/bs-jest": "^0.5.1",
|
||||
"bs-platform": "8.4.2",
|
||||
"@emotion/babel-plugin": "^11.7.2",
|
||||
"@parcel/core": "^2.3.2",
|
||||
"@types/react": "^17.0.39",
|
||||
"autoprefixer": "^10.4.2",
|
||||
"docsify": "^4.12.2",
|
||||
"jest": "^27.5.1",
|
||||
"parcel": "^2.3.2",
|
||||
"parcel-plugin-bundle-visualiser": "^1.2.0",
|
||||
"parcel-plugin-less-js-enabled": "1.0.2"
|
||||
"postcss": "^8.4.7",
|
||||
"postcss-cli": "^9.1.0",
|
||||
"tailwindcss": "^3.0.23",
|
||||
"typescript": "^4.6.2"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
//postcss.config.js
|
||||
const tailwindcss = require('tailwindcss');
|
||||
module.exports = {
|
||||
plugins: [
|
||||
tailwindcss('./tailwind.js'),
|
||||
require('autoprefixer'),
|
||||
],
|
||||
};
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
let entries = [];
|
|
@ -1,30 +0,0 @@
|
|||
type compEntry = {
|
||||
mutable id: string,
|
||||
title: string,
|
||||
render: unit => React.element,
|
||||
container: containerType,
|
||||
}
|
||||
and folderEntry = {
|
||||
mutable id: string,
|
||||
title: string,
|
||||
children: list(navEntry),
|
||||
}
|
||||
and navEntry =
|
||||
| CompEntry(compEntry)
|
||||
| FolderEntry(folderEntry)
|
||||
and containerType =
|
||||
| FullWidth
|
||||
| Sidebar;
|
||||
|
||||
let entry = (~title, ~render): navEntry => {
|
||||
CompEntry({id: "", title, render, container: FullWidth});
|
||||
};
|
||||
|
||||
// Maybe different api, this avoids breaking changes
|
||||
let sidebar = (~title, ~render): navEntry => {
|
||||
CompEntry({id: "", title, render, container: Sidebar});
|
||||
};
|
||||
|
||||
let folder = (~title, ~children): navEntry => {
|
||||
FolderEntry({id: "", title, children});
|
||||
};
|
|
@ -1,163 +0,0 @@
|
|||
open EntryTypes
|
||||
|
||||
module HS = Belt.HashMap.String
|
||||
|
||||
let entriesByPath: HS.t<navEntry> = HS.make(~hintSize=100)
|
||||
|
||||
/* Creates unique id's per scope based on title */
|
||||
let buildIds = entries => {
|
||||
let genId = (title, path) => {
|
||||
let noSpaces = Js.String.replaceByRe(%re("/\\s+/g"), "-", title)
|
||||
if !HS.has(entriesByPath, path ++ ("/" ++ noSpaces)) {
|
||||
noSpaces
|
||||
} else {
|
||||
let rec loop = num => {
|
||||
let testId = noSpaces ++ ("-" ++ string_of_int(num))
|
||||
if !HS.has(entriesByPath, path ++ ("/" ++ testId)) {
|
||||
testId
|
||||
} else {
|
||||
loop(num + 1)
|
||||
}
|
||||
}
|
||||
loop(2)
|
||||
}
|
||||
}
|
||||
let rec processFolder = (f: folderEntry, curPath) => {
|
||||
f.id = curPath ++ ("/" ++ genId(f.title, curPath))
|
||||
HS.set(entriesByPath, f.id, FolderEntry(f))
|
||||
f.children |> E.L.iter(x =>
|
||||
switch x {
|
||||
| CompEntry(c) => processEntry(c, f.id)
|
||||
| FolderEntry(f) => processFolder(f, f.id)
|
||||
}
|
||||
)
|
||||
}
|
||||
and processEntry = (c: compEntry, curPath) => {
|
||||
c.id = curPath ++ ("/" ++ genId(c.title, curPath))
|
||||
HS.set(entriesByPath, c.id, CompEntry(c))
|
||||
}
|
||||
entries |> E.L.iter(x =>
|
||||
switch x {
|
||||
| CompEntry(c) => processEntry(c, "")
|
||||
| FolderEntry(f) => processFolder(f, "")
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
let entries = Entries.entries
|
||||
buildIds(entries)
|
||||
|
||||
module Styles = {
|
||||
open CssJs
|
||||
let pageContainer = style(. [ display(#flex), height(#vh(100.)) ])
|
||||
let leftNav = style(. [ padding(#em(2.)),
|
||||
flexBasis(#px(200)),
|
||||
flexShrink(0.),
|
||||
backgroundColor(#hex("eaeff3")),
|
||||
boxShadows([ Shadow.box(~x=px(-1), ~blur=px(1), ~inset=true, rgba(0, 0, 0, #percent(0.1))) ]),
|
||||
])
|
||||
|
||||
let folderNav = style(. [ selector(.
|
||||
">h4",
|
||||
[ cursor(#pointer), margin2(~v=#em(0.3), ~h=#zero), hover([ color(#hex("7089ad")) ]) ],
|
||||
),
|
||||
])
|
||||
let folderChildren = style(. [ paddingLeft(#px(7)) ])
|
||||
let compNav = style(. [ cursor(#pointer),
|
||||
paddingBottom(#px(3)),
|
||||
hover([ color(#hex("7089ad")) ]),
|
||||
])
|
||||
let compContainer = style(. [ padding(#em(2.)), flexGrow(1.) ])
|
||||
// Approximate sidebar container for entry
|
||||
let sidebarContainer = style(. [ maxWidth(#px(430)) ])
|
||||
let folderChildContainer = style(. [ marginBottom(#em(2.)) ])
|
||||
}
|
||||
|
||||
let baseUrl = "/showcase/index.html"
|
||||
|
||||
module Index = {
|
||||
type state = {route: RescriptReactRouter.url}
|
||||
|
||||
type action =
|
||||
| ItemClick(string)
|
||||
| ChangeRoute(RescriptReactRouter.url)
|
||||
|
||||
let changeId = (id: string) => {
|
||||
let _ = RescriptReactRouter.push(baseUrl ++ ("#" ++ id))
|
||||
}
|
||||
|
||||
let buildNav = _ => {
|
||||
let rec buildFolder = (f: folderEntry) =>
|
||||
<div key=f.id className=Styles.folderNav>
|
||||
<h4 onClick={_e => changeId(f.id)}> {f.title->React.string} </h4>
|
||||
<div className=Styles.folderChildren>
|
||||
{(f.children
|
||||
|> E.L.fmap(e =>
|
||||
switch e {
|
||||
| FolderEntry(folder) => buildFolder(folder)
|
||||
| CompEntry(entry) => buildEntry(entry)
|
||||
}
|
||||
)
|
||||
|> E.L.toArray)->React.array}
|
||||
</div>
|
||||
</div>
|
||||
and buildEntry = (e: compEntry) =>
|
||||
<div key=e.id className=Styles.compNav onClick={_e => changeId(e.id)}>
|
||||
{e.title->React.string}
|
||||
</div>
|
||||
(entries
|
||||
|> E.L.fmap(e =>
|
||||
switch e {
|
||||
| FolderEntry(folder) => buildFolder(folder)
|
||||
| CompEntry(entry) => buildEntry(entry)
|
||||
}
|
||||
)
|
||||
|> E.L.toArray)->React.array
|
||||
}
|
||||
|
||||
let renderEntry = e =>
|
||||
switch e.container {
|
||||
| FullWidth => e.render()
|
||||
| Sidebar => <div className=Styles.sidebarContainer> {e.render()} </div>
|
||||
}
|
||||
|
||||
@react.component
|
||||
let make = () => {
|
||||
let (route, setRoute) = React.useState(() => {
|
||||
let url: RescriptReactRouter.url = {path: list{}, hash: "", search: ""}
|
||||
url
|
||||
})
|
||||
|
||||
React.useState(() => {
|
||||
let _ = RescriptReactRouter.watchUrl(url => setRoute(_ => url))
|
||||
}) |> ignore
|
||||
|
||||
<div className=Styles.pageContainer>
|
||||
<div className=Styles.leftNav> {buildNav(setRoute)} </div>
|
||||
<div className=Styles.compContainer>
|
||||
{if route.hash == "" {
|
||||
React.null
|
||||
} else {
|
||||
switch HS.get(entriesByPath, route.hash) {
|
||||
| Some(navEntry) =>
|
||||
switch navEntry {
|
||||
| CompEntry(c) => renderEntry(c)
|
||||
| FolderEntry(f) =>
|
||||
/* Rendering immediate children */
|
||||
(f.children
|
||||
|> E.L.fmap(child =>
|
||||
switch child {
|
||||
| CompEntry(c) =>
|
||||
<div className=Styles.folderChildContainer key=c.id> {renderEntry(c)} </div>
|
||||
| _ => React.null
|
||||
}
|
||||
)
|
||||
|> E.L.toArray)->React.array
|
||||
}
|
||||
| None => <div> {"Component not found"->React.string} </div>
|
||||
}
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
switch(ReactDOM.querySelector("main")){
|
||||
| Some(root) => ReactDOM.render(<div> <Lib.Index /> </div>, root)
|
||||
| None => () // do nothing
|
||||
}
|
||||
RescriptReactRouter.push("")
|
|
@ -1,24 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<link href="https://fonts.googleapis.com/css?family=Lato:300,400,700,900" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css?family=Lato:300,400,700,900" rel="stylesheet">
|
||||
<link href="../src/styles/index.css" rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
<title>Showcase</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="main"></div>
|
||||
<script src=" ./ShowcaseIndex.bs.js "></script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,6 +0,0 @@
|
|||
module Input = Antd_Input
|
||||
module Grid = Antd_Grid
|
||||
module Form = Antd_Form
|
||||
module Card = Antd_Card
|
||||
module Button = Antd_Button
|
||||
module IconName = Antd_IconName
|
|
@ -1,38 +0,0 @@
|
|||
|
||||
@deriving(abstract)
|
||||
type props
|
||||
type makeType = props => React.element
|
||||
|
||||
@obj external makeProps:
|
||||
(
|
||||
~disabled: bool=?,
|
||||
~ghost: bool=?,
|
||||
~href: string=?,
|
||||
~htmlType: @string [#button | #submit | #submit]=?,
|
||||
~icon: Antd_IconName.t=?,
|
||||
~shape: @string [#circle | #round]=?,
|
||||
~size: @string [#small | #large]=?,
|
||||
~target: string=?,
|
||||
~loading: bool=?,
|
||||
~_type: @string
|
||||
[
|
||||
| #primary
|
||||
| #default
|
||||
| #dashed
|
||||
| #danger
|
||||
| #link
|
||||
| #ghost
|
||||
]=?,
|
||||
~onClick: ReactEvent.Mouse.t => unit=?,
|
||||
~block: bool=?,
|
||||
~children: React.element=?,
|
||||
~className: string=?,
|
||||
~id: string=?,
|
||||
~testId: string=?,
|
||||
unit
|
||||
) =>
|
||||
props =
|
||||
""
|
||||
|
||||
@module("antd")
|
||||
external make : makeType = "Button"
|
|
@ -1,31 +0,0 @@
|
|||
@deriving(abstract)
|
||||
type props
|
||||
type makeType = props => React.element
|
||||
|
||||
@obj external makeProps: (
|
||||
~actions: array<React.element>=?,
|
||||
~activeTabKey: string=?,
|
||||
~headStyle: ReactDOMStyle.t=?,
|
||||
~bodyStyle: ReactDOMStyle.t=?,
|
||||
~style: ReactDOMStyle.t=?,
|
||||
~bordered: bool=?,
|
||||
~cover: React.element=?,
|
||||
~defaultActiveTabKey: string=?,
|
||||
~extra: React.element=?,
|
||||
~hoverable: bool=?,
|
||||
~loading: bool=?,
|
||||
~tabList: array<{
|
||||
"key": string,
|
||||
"tab": React.element,
|
||||
}>
|
||||
=?,
|
||||
~size: @string [ #default | #small]=?,
|
||||
~title: 'a=?,
|
||||
~_type: string=?,
|
||||
~onTabChange: string => unit=?,
|
||||
~children: React.element=?,
|
||||
unit // This unit is a quirk of the type system. Apparently it must exist to have optional arguments in a type
|
||||
) => props = ""
|
||||
|
||||
@module("antd")
|
||||
external make : makeType = "Card"
|
|
@ -1,52 +0,0 @@
|
|||
|
||||
@deriving(abstract)
|
||||
type props
|
||||
type makeType = props => React.element
|
||||
|
||||
@obj
|
||||
external makeProps:
|
||||
(
|
||||
~onSubmit: ReactEvent.Form.t => unit=?,
|
||||
~hideRequiredMark: bool=?,
|
||||
~id: string=?,
|
||||
~className: string=?,
|
||||
~style: ReactDOMStyle.t=?,
|
||||
~colon: bool=?,
|
||||
~validateStatus: string=?,
|
||||
~extra: string=?,
|
||||
~required: bool=?,
|
||||
~label: string=?,
|
||||
~help: string=?,
|
||||
~hasFeedback: bool=?,
|
||||
~children:React.element=?,
|
||||
unit
|
||||
) =>
|
||||
props =
|
||||
""
|
||||
|
||||
|
||||
@module("antd")
|
||||
external make : makeType = "Form"
|
||||
|
||||
module Item = {
|
||||
type props
|
||||
type makeType = props => React.element
|
||||
@obj
|
||||
external makeProps:
|
||||
(
|
||||
~colon:string=?,
|
||||
~validateStatus:string=?,
|
||||
~extra:string=?,
|
||||
~className:string=?,
|
||||
~required:bool=?,
|
||||
~style:ReactDOMStyle.t=?,
|
||||
~label:string=?,
|
||||
~id:string=?,
|
||||
~help:string=?,
|
||||
~hasFeedback:bool=?,
|
||||
~children:React.element=?,
|
||||
unit
|
||||
) => props = ""
|
||||
@module("antd/lib/form/FormItem")
|
||||
external make : makeType = "default"
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
%%raw(`require("antd/lib/grid/style")`)
|
||||
|
||||
|
||||
module Row = {
|
||||
type props
|
||||
|
||||
@obj
|
||||
external makeProps:
|
||||
(
|
||||
~className: string=?,
|
||||
~_type: string=?,
|
||||
~align: string=?,
|
||||
~justify: string=?,
|
||||
~gutter: 'a=?,
|
||||
~style: ReactDOMStyle.t=?,
|
||||
~prefixCls: string=?,
|
||||
~children: React.element=?,
|
||||
unit
|
||||
) =>
|
||||
props =
|
||||
"";
|
||||
|
||||
type makeType = props => React.element
|
||||
|
||||
@module("antd")
|
||||
external make : makeType = "Row"
|
||||
}
|
||||
|
||||
module Col = {
|
||||
type props
|
||||
|
||||
@obj
|
||||
external makeProps:
|
||||
(
|
||||
~className: string=?,
|
||||
~span: int=?,
|
||||
~order: int=?,
|
||||
~offset: int=?,
|
||||
~push: int=?,
|
||||
~pull: int=?,
|
||||
~xs: 'a=?,
|
||||
~sm: 'b=?,
|
||||
~md: 'c=?,
|
||||
~lg: 'd=?,
|
||||
~xl: 'e=?,
|
||||
~xxl: 'f=?,
|
||||
~prefixCls: string=?,
|
||||
~style: ReactDOMStyle.t=?,
|
||||
~children: React.element=?,
|
||||
unit
|
||||
) =>
|
||||
props =
|
||||
"";
|
||||
|
||||
type makeType = props => React.element
|
||||
|
||||
@module("antd")
|
||||
external make : makeType = "Col"
|
||||
}
|
|
@ -1,581 +0,0 @@
|
|||
type t = string
|
||||
|
||||
let toString = t => t
|
||||
|
||||
let fromString = t => t
|
||||
|
||||
let compare = (t1, t2) => t1 == t2
|
||||
|
||||
let stepBackward = "step-backward"
|
||||
|
||||
let stepForward = "step-forward"
|
||||
|
||||
let fastBackward = "fast-backward"
|
||||
|
||||
let fastForward = "fast-forward"
|
||||
|
||||
let shrink = "shrink"
|
||||
|
||||
let arrowsAlt = "arrows-alt"
|
||||
|
||||
let down = "down"
|
||||
|
||||
let up = "up"
|
||||
|
||||
let left = "left"
|
||||
|
||||
let right = "right"
|
||||
|
||||
let caretUp = "caret-up"
|
||||
|
||||
let caretDown = "caret-down"
|
||||
|
||||
let caretLeft = "caret-left"
|
||||
|
||||
let caretRight = "caret-right"
|
||||
|
||||
let upCircle = "up-circle"
|
||||
|
||||
let downCircle = "down-circle"
|
||||
|
||||
let leftCircle = "left-circle"
|
||||
|
||||
let rightCircle = "right-circle"
|
||||
|
||||
let upCircleO = "up-circle-o"
|
||||
|
||||
let downCircleO = "down-circle-o"
|
||||
|
||||
let rightCircleO = "right-circle-o"
|
||||
|
||||
let leftCircleO = "left-circle-o"
|
||||
|
||||
let doubleRight = "double-right"
|
||||
|
||||
let doubleLeft = "double-left"
|
||||
|
||||
let verticleLeft = "verticle-left"
|
||||
|
||||
let verticleRight = "verticle-right"
|
||||
|
||||
let forward = "forward"
|
||||
|
||||
let backward = "backward"
|
||||
|
||||
let rollback = "rollback"
|
||||
|
||||
let enter = "enter"
|
||||
|
||||
let retweet = "retweet"
|
||||
|
||||
let swap = "swap"
|
||||
|
||||
let swapLeft = "swap-left"
|
||||
|
||||
let swapRight = "swap-right"
|
||||
|
||||
let arrowUp = "arrow-up"
|
||||
|
||||
let arrowDown = "arrow-down"
|
||||
|
||||
let arrowLeft = "arrow-left"
|
||||
|
||||
let arrowRight = "arrow-right"
|
||||
|
||||
let playCircle = "play-circle"
|
||||
|
||||
let playCircleO = "play-circle-o"
|
||||
|
||||
let upSquare = "up-square"
|
||||
|
||||
let downSquare = "down-square"
|
||||
|
||||
let leftSquare = "left-square"
|
||||
|
||||
let rightSquare = "right-square"
|
||||
|
||||
let upSquareO = "up-square-o"
|
||||
|
||||
let downSquareO = "down-square-o"
|
||||
|
||||
let leftSquareO = "left-square-o"
|
||||
|
||||
let rightSquareO = "right-square-o"
|
||||
|
||||
let login = "login"
|
||||
|
||||
let logout = "logout"
|
||||
|
||||
let menuFold = "menu-fold"
|
||||
|
||||
let menuUnfold = "menu-unfold"
|
||||
|
||||
let question = "question"
|
||||
|
||||
let questionCircleO = "question-circle-o"
|
||||
|
||||
let questionCircle = "question-circle"
|
||||
|
||||
let plus = "plus"
|
||||
|
||||
let plusCircleO = "plus-circle-o"
|
||||
|
||||
let plusCircle = "plus-circle"
|
||||
|
||||
let pause = "pause"
|
||||
|
||||
let pauseCircleO = "pause-circle-o"
|
||||
|
||||
let pauseCircle = "pause-circle"
|
||||
|
||||
let minus = "minus"
|
||||
|
||||
let minusCircleO = "minus-circle-o"
|
||||
|
||||
let minusCircle = "minus-circle"
|
||||
|
||||
let plusSquare = "plus-square"
|
||||
|
||||
let plusSquareO = "plus-square-o"
|
||||
|
||||
let minusSquare = "minus-square"
|
||||
|
||||
let minusSquareO = "minus-square-o"
|
||||
|
||||
let info = "info"
|
||||
|
||||
let infoCircleO = "info-circle-o"
|
||||
|
||||
let infoCircle = "info-circle"
|
||||
|
||||
let exclamation = "exclamation"
|
||||
|
||||
let exclamationCircleO = "exclamation-circle-o"
|
||||
|
||||
let exclamationCircle = "exclamation-circle"
|
||||
|
||||
let close = "close"
|
||||
|
||||
let closeCircle = "close-circle"
|
||||
|
||||
let closeCircleO = "close-circle-o"
|
||||
|
||||
let closeSquare = "close-square"
|
||||
|
||||
let closeSquareO = "close-square-o"
|
||||
|
||||
let check = "check"
|
||||
|
||||
let checkCircle = "check-circle"
|
||||
|
||||
let checkCircleO = "check-circle-o"
|
||||
|
||||
let checkSquare = "check-square"
|
||||
|
||||
let checkSquareO = "check-square-o"
|
||||
|
||||
let clockCircleO = "clock-circle-o"
|
||||
|
||||
let clockCircle = "clock-circle"
|
||||
|
||||
let warning = "warning"
|
||||
|
||||
let lock = "lock"
|
||||
|
||||
let unlock = "unlock"
|
||||
|
||||
let areaChart = "area-chart"
|
||||
|
||||
let pieChart = "pie-chart"
|
||||
|
||||
let barChart = "bar-chart"
|
||||
|
||||
let dotChart = "dot-chart"
|
||||
|
||||
let bars = "bars"
|
||||
|
||||
let book = "book"
|
||||
|
||||
let calendar = "calendar"
|
||||
|
||||
let cloud = "cloud"
|
||||
|
||||
let cloudDownload = "cloud-download"
|
||||
|
||||
let code = "code"
|
||||
|
||||
let codeO = "code-o"
|
||||
|
||||
let copy = "copy"
|
||||
|
||||
let creditCard = "credit-card"
|
||||
|
||||
let delete = "delete"
|
||||
|
||||
let desktop = "desktop"
|
||||
|
||||
let download = "download"
|
||||
|
||||
let edit = "edit"
|
||||
|
||||
let ellipsis = "ellipsis"
|
||||
|
||||
let file = "file"
|
||||
|
||||
let fileText = "file-text"
|
||||
|
||||
let fileUnknown = "file-unknown"
|
||||
|
||||
let filePdf = "file-pdf"
|
||||
|
||||
let fileWord = "file-word"
|
||||
|
||||
let fileExcel = "file-excel"
|
||||
|
||||
let fileJpg = "file-jpg"
|
||||
|
||||
let filePpt = "file-ppt"
|
||||
|
||||
let fileMarkdown = "file-markdown"
|
||||
|
||||
let fileAdd = "file-add"
|
||||
|
||||
let folder = "folder"
|
||||
|
||||
let folderOpen = "folder-open"
|
||||
|
||||
let folderAdd = "folder-add"
|
||||
|
||||
let hdd = "hdd"
|
||||
|
||||
let frown = "frown"
|
||||
|
||||
let frownO = "frown-o"
|
||||
|
||||
let meh = "meh"
|
||||
|
||||
let mehO = "meh-o"
|
||||
|
||||
let smile = "smile"
|
||||
|
||||
let smileO = "smile-o"
|
||||
|
||||
let inbox = "inbox"
|
||||
|
||||
let laptop = "laptop"
|
||||
|
||||
let appstoreO = "appstore-o"
|
||||
|
||||
let appstore = "appstore"
|
||||
|
||||
let lineChart = "line-chart"
|
||||
|
||||
let link = "link"
|
||||
|
||||
let mail = "mail"
|
||||
|
||||
let mobile = "mobile"
|
||||
|
||||
let notification = "notification"
|
||||
|
||||
let paperClip = "paper-clip"
|
||||
|
||||
let picture = "picture"
|
||||
|
||||
let poweroff = "poweroff"
|
||||
|
||||
let reload = "reload"
|
||||
|
||||
let search = "search"
|
||||
|
||||
let setting = "setting"
|
||||
|
||||
let shareAlt = "share-alt"
|
||||
|
||||
let shoppingCart = "shopping-cart"
|
||||
|
||||
let tablet = "tablet"
|
||||
|
||||
let tag = "tag"
|
||||
|
||||
let tagO = "tag-o"
|
||||
|
||||
let tags = "tags"
|
||||
|
||||
let tagsO = "tags-o"
|
||||
|
||||
let toTop = "to-top"
|
||||
|
||||
let upload = "upload"
|
||||
|
||||
let user = "user"
|
||||
|
||||
let videoCamera = "video-camera"
|
||||
|
||||
let home = "home"
|
||||
|
||||
let loading = "loading"
|
||||
|
||||
let loading3Quarters = "loading-3-quarters"
|
||||
|
||||
let cloudUploadO = "cloud-upload-o"
|
||||
|
||||
let cloudDownloadO = "cloud-download-o"
|
||||
|
||||
let cloudUpload = "cloud-upload"
|
||||
|
||||
let cloudO = "cloud-o"
|
||||
|
||||
let starO = "star-o"
|
||||
|
||||
let star = "star"
|
||||
|
||||
let heartO = "heart-o"
|
||||
|
||||
let heart = "heart"
|
||||
|
||||
let environment = "environment"
|
||||
|
||||
let environmentO = "environment-o"
|
||||
|
||||
let eye = "eye"
|
||||
|
||||
let eyeO = "eye-o"
|
||||
|
||||
let camera = "camera"
|
||||
|
||||
let cameraO = "camera-o"
|
||||
|
||||
let save = "save"
|
||||
|
||||
let team = "team"
|
||||
|
||||
let solution = "solution"
|
||||
|
||||
let phone = "phone"
|
||||
|
||||
let filter = "filter"
|
||||
|
||||
let exception_ = "exception"
|
||||
|
||||
let \"export" = "export"
|
||||
|
||||
let customerService = "customer-service"
|
||||
|
||||
let qrcode = "qrcode"
|
||||
|
||||
let scan = "scan"
|
||||
|
||||
let like = "like"
|
||||
|
||||
let likeO = "like-o"
|
||||
|
||||
let dislike = "dislike"
|
||||
|
||||
let dislikeO = "dislike-o"
|
||||
|
||||
let message = "message"
|
||||
|
||||
let payCircle = "pay-circle"
|
||||
|
||||
let payCircleO = "pay-circle-o"
|
||||
|
||||
let calculator = "calculator"
|
||||
|
||||
let pushpin = "pushpin"
|
||||
|
||||
let pushpinO = "pushpin-o"
|
||||
|
||||
let bulb = "bulb"
|
||||
|
||||
let select = "select"
|
||||
|
||||
let switcher = "switcher"
|
||||
|
||||
let rocket = "rocket"
|
||||
|
||||
let bell = "bell"
|
||||
|
||||
let disconnect = "disconnect"
|
||||
|
||||
let database = "database"
|
||||
|
||||
let compass = "compass"
|
||||
|
||||
let barcode = "barcode"
|
||||
|
||||
let hourglass = "hourglass"
|
||||
|
||||
let key = "key"
|
||||
|
||||
let flag = "flag"
|
||||
|
||||
let layout = "layout"
|
||||
|
||||
let printer = "printer"
|
||||
|
||||
let sound = "sound"
|
||||
|
||||
let usb = "usb"
|
||||
|
||||
let skin = "skin"
|
||||
|
||||
let tool = "tool"
|
||||
|
||||
let sync = "sync"
|
||||
|
||||
let wifi = "wifi"
|
||||
|
||||
let car = "car"
|
||||
|
||||
let schedule = "schedule"
|
||||
|
||||
let userAdd = "user-add"
|
||||
|
||||
let userDelete = "user-delete"
|
||||
|
||||
let usergroupAdd = "usergroup-add"
|
||||
|
||||
let usergroupDelete = "usergroup-delete"
|
||||
|
||||
let man = "man"
|
||||
|
||||
let woman = "woman"
|
||||
|
||||
let shop = "shop"
|
||||
|
||||
let gift = "gift"
|
||||
|
||||
let idcard = "idcard"
|
||||
|
||||
let medicineBox = "medicine-box"
|
||||
|
||||
let redEnvelope = "red-envelope"
|
||||
|
||||
let coffee = "coffee"
|
||||
|
||||
let copyright = "copyright"
|
||||
|
||||
let trademark = "trademark"
|
||||
|
||||
let safety = "safety"
|
||||
|
||||
let wallet = "wallet"
|
||||
|
||||
let bank = "bank"
|
||||
|
||||
let trophy = "trophy"
|
||||
|
||||
let contacts = "contacts"
|
||||
|
||||
let global = "global"
|
||||
|
||||
let shake = "shake"
|
||||
|
||||
let api = "api"
|
||||
|
||||
let fork = "fork"
|
||||
|
||||
let dashboard = "dashboard"
|
||||
|
||||
let form = "form"
|
||||
|
||||
let table = "table"
|
||||
|
||||
let profile = "profile"
|
||||
|
||||
let android = "android"
|
||||
|
||||
let androidO = "android-o"
|
||||
|
||||
let apple = "apple"
|
||||
|
||||
let appleO = "apple-o"
|
||||
|
||||
let windows = "windows"
|
||||
|
||||
let windowsO = "windows-o"
|
||||
|
||||
let ie = "ie"
|
||||
|
||||
let chrome = "chrome"
|
||||
|
||||
let github = "github"
|
||||
|
||||
let aliwangwang = "aliwangwang"
|
||||
|
||||
let aliwangwangO = "aliwangwang-o"
|
||||
|
||||
let dingding = "dingding"
|
||||
|
||||
let dingdingO = "dingding-o"
|
||||
|
||||
let weiboSquare = "weibo-square"
|
||||
|
||||
let weiboCircle = "weibo-circle"
|
||||
|
||||
let taobaoCircle = "taobao-circle"
|
||||
|
||||
let html5 = "html5"
|
||||
|
||||
let weibo = "weibo"
|
||||
|
||||
let twitter = "twitter"
|
||||
|
||||
let wechat = "wechat"
|
||||
|
||||
let youtube = "youtube"
|
||||
|
||||
let alipayCircle = "alipay-circle"
|
||||
|
||||
let taobao = "taobao"
|
||||
|
||||
let skype = "skype"
|
||||
|
||||
let qq = "qq"
|
||||
|
||||
let mediumWorkmark = "medium-workmark"
|
||||
|
||||
let gitlab = "gitlab"
|
||||
|
||||
let medium = "medium"
|
||||
|
||||
let linkedin = "linkedin"
|
||||
|
||||
let googlePlus = "google-plus"
|
||||
|
||||
let dropbox = "dropbox"
|
||||
|
||||
let facebook = "facebook"
|
||||
|
||||
let codepen = "codepen"
|
||||
|
||||
let amazon = "amazon"
|
||||
|
||||
let google = "google"
|
||||
|
||||
let codepenCircle = "codepen-circle"
|
||||
|
||||
let alipay = "alipay"
|
||||
|
||||
let antDesign = "ant-design"
|
||||
|
||||
let aliyun = "aliyun"
|
||||
|
||||
let zhihu = "zhihu"
|
||||
|
||||
let slack = "slack"
|
||||
|
||||
let slackSquare = "slack-square"
|
||||
|
||||
let behance = "behance"
|
||||
|
||||
let behanceSquare = "behance-square"
|
||||
|
||||
let dribbble = "dribbble"
|
||||
|
||||
let dribbbleSquare = "dribbble-square"
|
||||
|
||||
let instagram = "instagram"
|
||||
|
||||
let yuque = "yuque"
|
|
@ -1,21 +0,0 @@
|
|||
@deriving(abstract)
|
||||
type props
|
||||
type makeType = props => React.element
|
||||
|
||||
@obj external makeProps: (
|
||||
@as("type")
|
||||
~htmlType: string=?,
|
||||
~name: string=?,
|
||||
~value: string=?,
|
||||
~defaultValue: string=?,
|
||||
~onChange: ReactEvent.Form.t => unit=?,
|
||||
~onPressEnter: ReactEvent.Keyboard.t => unit=?,
|
||||
~onBlur: ReactEvent.Focus.t => unit=?,
|
||||
~className: string=?,
|
||||
~style: ReactDOMStyle.t=?,
|
||||
~placeholder: string=?,
|
||||
unit // This unit is a quirk of the type system. Apparently it must exist to have optional arguments in a type
|
||||
) => props = ""
|
||||
|
||||
@module("antd")
|
||||
external make : makeType = "Input"
|
File diff suppressed because it is too large
Load Diff
|
@ -1,28 +0,0 @@
|
|||
type route =
|
||||
| DistBuilder
|
||||
| NotFound
|
||||
|
||||
let routeToPath = route =>
|
||||
switch route {
|
||||
| DistBuilder => "/"
|
||||
| _ => "/"
|
||||
}
|
||||
|
||||
let fixedLength = r => <div className="w-full max-w-screen-xl mx-auto px-6"> r </div>
|
||||
|
||||
@react.component
|
||||
let make = () => {
|
||||
let url = RescriptReactRouter.useUrl()
|
||||
|
||||
let routing = switch url.path {
|
||||
| list{} => DistBuilder
|
||||
| _ => NotFound
|
||||
}
|
||||
|
||||
<>
|
||||
{switch routing {
|
||||
| DistBuilder => <DistBuilder />
|
||||
| _ => fixedLength("Page is not found" |> R.ste)
|
||||
}}
|
||||
</>
|
||||
}
|
|
@ -1,443 +0,0 @@
|
|||
open Rationale.Function.Infix
|
||||
|
||||
module FloatFloatMap = {
|
||||
module Id = Belt.Id.MakeComparable({
|
||||
type t = float
|
||||
let cmp: (float, float) => int = Pervasives.compare
|
||||
})
|
||||
|
||||
type t = Belt.MutableMap.t<Id.t, float, Id.identity>
|
||||
|
||||
let fromArray = (ar: array<(float, float)>) => Belt.MutableMap.fromArray(ar, ~id=module(Id))
|
||||
let toArray = (t: t) => Belt.MutableMap.toArray(t)
|
||||
let empty = () => Belt.MutableMap.make(~id=module(Id))
|
||||
let increment = (el, t: t) =>
|
||||
Belt.MutableMap.update(t, el, x =>
|
||||
switch x {
|
||||
| Some(n) => Some(n +. 1.0)
|
||||
| None => Some(1.0)
|
||||
}
|
||||
)
|
||||
|
||||
let get = (el, t: t) => Belt.MutableMap.get(t, el)
|
||||
let fmap = (fn, t: t) => Belt.MutableMap.map(t, fn)
|
||||
}
|
||||
|
||||
module Int = {
|
||||
let max = (i1: int, i2: int) => i1 > i2 ? i1 : i2
|
||||
}
|
||||
/* Utils */
|
||||
module U = {
|
||||
let isEqual = (a, b) => a == b
|
||||
let toA = a => [a]
|
||||
let id = e => e
|
||||
}
|
||||
|
||||
module O = {
|
||||
let dimap = (sFn, rFn, e) =>
|
||||
switch e {
|
||||
| Some(r) => sFn(r)
|
||||
| None => rFn()
|
||||
}
|
||||
()
|
||||
let fmap = Rationale.Option.fmap
|
||||
let bind = Rationale.Option.bind
|
||||
let default = Rationale.Option.default
|
||||
let isSome = Rationale.Option.isSome
|
||||
let isNone = Rationale.Option.isNone
|
||||
let toExn = Rationale.Option.toExn
|
||||
let some = Rationale.Option.some
|
||||
let firstSome = Rationale.Option.firstSome
|
||||
let toExt = Rationale.Option.toExn
|
||||
let flatApply = (fn, b) => Rationale.Option.apply(fn, Some(b)) |> Rationale.Option.flatten
|
||||
|
||||
let toBool = opt =>
|
||||
switch opt {
|
||||
| Some(_) => true
|
||||
| _ => false
|
||||
}
|
||||
|
||||
let ffmap = (fn, r) =>
|
||||
switch r {
|
||||
| Some(sm) => fn(sm)
|
||||
| _ => None
|
||||
}
|
||||
|
||||
let toString = opt =>
|
||||
switch opt {
|
||||
| Some(s) => s
|
||||
| _ => ""
|
||||
}
|
||||
|
||||
let toResult = (error, e) =>
|
||||
switch e {
|
||||
| Some(r) => Belt.Result.Ok(r)
|
||||
| None => Error(error)
|
||||
}
|
||||
|
||||
let compare = (compare, f1: option<float>, f2: option<float>) =>
|
||||
switch (f1, f2) {
|
||||
| (Some(f1), Some(f2)) => Some(compare(f1, f2) ? f1 : f2)
|
||||
| (Some(f1), None) => Some(f1)
|
||||
| (None, Some(f2)) => Some(f2)
|
||||
| (None, None) => None
|
||||
}
|
||||
|
||||
let min = compare(\"<")
|
||||
let max = compare(\">")
|
||||
}
|
||||
|
||||
/* Functions */
|
||||
module F = {
|
||||
let apply = (a, e) => a |> e
|
||||
|
||||
let flatten2Callbacks = (fn1, fn2, fnlast) =>
|
||||
fn1(response1 => fn2(response2 => fnlast(response1, response2)))
|
||||
|
||||
let flatten3Callbacks = (fn1, fn2, fn3, fnlast) =>
|
||||
fn1(response1 => fn2(response2 => fn3(response3 => fnlast(response1, response2, response3))))
|
||||
|
||||
let flatten4Callbacks = (fn1, fn2, fn3, fn4, fnlast) =>
|
||||
fn1(response1 =>
|
||||
fn2(response2 =>
|
||||
fn3(response3 => fn4(response4 => fnlast(response1, response2, response3, response4)))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
module Bool = {
|
||||
type t = bool
|
||||
let toString = (t: t) => t ? "TRUE" : "FALSE"
|
||||
let fromString = str => str == "TRUE" ? true : false
|
||||
|
||||
module O = {
|
||||
let toBool = opt =>
|
||||
switch opt {
|
||||
| Some(true) => true
|
||||
| _ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module Float = {
|
||||
let with2DigitsPrecision = Js.Float.toPrecisionWithPrecision(_, ~digits=2)
|
||||
let with3DigitsPrecision = Js.Float.toPrecisionWithPrecision(_, ~digits=3)
|
||||
let toFixed = Js.Float.toFixed
|
||||
let toString = Js.Float.toString
|
||||
}
|
||||
|
||||
module I = {
|
||||
let increment = n => n + 1
|
||||
let decrement = n => n - 1
|
||||
let toString = Js.Int.toString
|
||||
}
|
||||
|
||||
/* R for Result */
|
||||
module R = {
|
||||
let result = Rationale.Result.result
|
||||
let id = e => e |> result(U.id, U.id)
|
||||
let fmap = Rationale.Result.fmap
|
||||
let bind = Rationale.Result.bind
|
||||
let toExn = Belt.Result.getExn
|
||||
let default = (default, res: Belt.Result.t<'a, 'b>) =>
|
||||
switch res {
|
||||
| Ok(r) => r
|
||||
| Error(_) => default
|
||||
}
|
||||
let merge = (a, b) =>
|
||||
switch (a, b) {
|
||||
| (Error(e), _) => Error(e)
|
||||
| (_, Error(e)) => Error(e)
|
||||
| (Ok(a), Ok(b)) => Ok((a, b))
|
||||
}
|
||||
let toOption = (e: Belt.Result.t<'a, 'b>) =>
|
||||
switch e {
|
||||
| Ok(r) => Some(r)
|
||||
| Error(_) => None
|
||||
}
|
||||
|
||||
let errorIfCondition = (errorCondition, errorMessage, r) =>
|
||||
errorCondition(r) ? Error(errorMessage) : Ok(r)
|
||||
}
|
||||
|
||||
let safe_fn_of_string = (fn, s: string): option<'a> =>
|
||||
try Some(fn(s)) catch {
|
||||
| _ => None
|
||||
}
|
||||
|
||||
module S = {
|
||||
let safe_float = float_of_string->safe_fn_of_string
|
||||
let safe_int = int_of_string->safe_fn_of_string
|
||||
let default = (defaultStr, str) => str == "" ? defaultStr : str
|
||||
}
|
||||
|
||||
module J = {
|
||||
let toString = \"||>"(Js.Json.decodeString, O.default(""))
|
||||
let fromString = Js.Json.string
|
||||
let fromNumber = Js.Json.number
|
||||
|
||||
module O = {
|
||||
let fromString = (str: string) =>
|
||||
switch str {
|
||||
| "" => None
|
||||
| _ => Some(Js.Json.string(str))
|
||||
}
|
||||
|
||||
let toString = (str: option<'a>) =>
|
||||
switch str {
|
||||
| Some(str) => Some(str |> \"||>"(Js.Json.decodeString, O.default("")))
|
||||
| _ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module JsDate = {
|
||||
let fromString = Js.Date.fromString
|
||||
let now = Js.Date.now
|
||||
let make = Js.Date.make
|
||||
let valueOf = Js.Date.valueOf
|
||||
}
|
||||
|
||||
/* List */
|
||||
module L = {
|
||||
let fmap = List.map
|
||||
let get = Belt.List.get
|
||||
let toArray = Array.of_list
|
||||
let fmapi = List.mapi
|
||||
let concat = List.concat
|
||||
let drop = Rationale.RList.drop
|
||||
let remove = Rationale.RList.remove
|
||||
let find = List.find
|
||||
let filter = List.filter
|
||||
let for_all = List.for_all
|
||||
let exists = List.exists
|
||||
let sort = List.sort
|
||||
let length = List.length
|
||||
let filter_opt = Rationale.RList.filter_opt
|
||||
let uniqBy = Rationale.RList.uniqBy
|
||||
let join = Rationale.RList.join
|
||||
let head = Rationale.RList.head
|
||||
let uniq = Rationale.RList.uniq
|
||||
let flatten = List.flatten
|
||||
let last = Rationale.RList.last
|
||||
let append = List.append
|
||||
let getBy = Belt.List.getBy
|
||||
let dropLast = Rationale.RList.dropLast
|
||||
let contains = Rationale.RList.contains
|
||||
let without = Rationale.RList.without
|
||||
let update = Rationale.RList.update
|
||||
let iter = List.iter
|
||||
let findIndex = Rationale.RList.findIndex
|
||||
}
|
||||
|
||||
/* A for Array */
|
||||
module A = {
|
||||
let fmap = Array.map
|
||||
let fmapi = Array.mapi
|
||||
let to_list = Array.to_list
|
||||
let of_list = Array.of_list
|
||||
let length = Array.length
|
||||
let append = Array.append
|
||||
// let empty = [||];
|
||||
let unsafe_get = Array.unsafe_get
|
||||
let get = Belt.Array.get
|
||||
let getBy = Belt.Array.getBy
|
||||
let last = a => get(a, length(a) - 1)
|
||||
let first = get(_, 0)
|
||||
let hasBy = (r, fn) => Belt.Array.getBy(r, fn) |> O.isSome
|
||||
let fold_left = Array.fold_left
|
||||
let fold_right = Array.fold_right
|
||||
let concatMany = Belt.Array.concatMany
|
||||
let keepMap = Belt.Array.keepMap
|
||||
let init = Array.init
|
||||
let reduce = Belt.Array.reduce
|
||||
let reducei = Belt.Array.reduceWithIndex
|
||||
let isEmpty = r => length(r) < 1
|
||||
let min = a => get(a, 0) |> O.fmap(first => Belt.Array.reduce(a, first, (i, j) => i < j ? i : j))
|
||||
let max = a => get(a, 0) |> O.fmap(first => Belt.Array.reduce(a, first, (i, j) => i > j ? i : j))
|
||||
let stableSortBy = Belt.SortArray.stableSortBy
|
||||
let toRanges = (a: array<'a>) =>
|
||||
switch a |> Belt.Array.length {
|
||||
| 0
|
||||
| 1 =>
|
||||
Belt.Result.Error("Must be at least 2 elements")
|
||||
| n =>
|
||||
Belt.Array.makeBy(n - 1, r => r)
|
||||
|> Belt.Array.map(_, index => (
|
||||
Belt.Array.getUnsafe(a, index),
|
||||
Belt.Array.getUnsafe(a, index + 1),
|
||||
))
|
||||
|> Rationale.Result.return
|
||||
}
|
||||
|
||||
// This zips while taking the longest elements of each array.
|
||||
let zipMaxLength = (array1, array2) => {
|
||||
let maxLength = Int.max(length(array1), length(array2))
|
||||
let result = maxLength |> Belt.Array.makeUninitializedUnsafe
|
||||
for i in 0 to maxLength - 1 {
|
||||
Belt.Array.set(result, i, (get(array1, i), get(array2, i))) |> ignore
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
let asList = (f: list<'a> => list<'a>, r: array<'a>) => r |> to_list |> f |> of_list
|
||||
/* TODO: Is there a better way of doing this? */
|
||||
let uniq = r => asList(L.uniq, r)
|
||||
|
||||
//intersperse([1,2,3], [10,11,12]) => [1,10,2,11,3,12]
|
||||
let intersperse = (a: array<'a>, b: array<'a>) => {
|
||||
let items: ref<array<'a>> = ref([])
|
||||
|
||||
Belt.Array.forEachWithIndex(a, (i, item) =>
|
||||
switch Belt.Array.get(b, i) {
|
||||
| Some(r) => items := append(items.contents, [item, r])
|
||||
| None => items := append(items.contents, [item])
|
||||
}
|
||||
)
|
||||
items.contents
|
||||
}
|
||||
|
||||
// This is like map, but
|
||||
//accumulate((a,b) => a + b, [1,2,3]) => [1, 3, 5]
|
||||
let accumulate = (fn: ('a, 'a) => 'a, items: array<'a>) => {
|
||||
let length = items |> length
|
||||
let empty = Belt.Array.make(length, items |> unsafe_get(_, 0))
|
||||
Belt.Array.forEachWithIndex(items, (index, element) => {
|
||||
let item = switch index {
|
||||
| 0 => element
|
||||
| index => fn(element, unsafe_get(empty, index - 1))
|
||||
}
|
||||
let _ = Belt.Array.set(empty, index, item)
|
||||
})
|
||||
empty
|
||||
}
|
||||
|
||||
// @todo: Is -1 still the indicator that this is false (as is true with
|
||||
// @todo: js findIndex)? Wasn't sure.
|
||||
let findIndex = (e, i) =>
|
||||
Js.Array.findIndex(e, i) |> (
|
||||
r =>
|
||||
switch r {
|
||||
| -1 => None
|
||||
| r => Some(r)
|
||||
}
|
||||
)
|
||||
let filter = (o, e) => Js.Array.filter(o, e)
|
||||
|
||||
module O = {
|
||||
let concatSomes = (optionals: array<option<'a>>): array<'a> =>
|
||||
optionals
|
||||
|> Js.Array.filter(Rationale.Option.isSome)
|
||||
|> Js.Array.map(Rationale.Option.toExn("Warning: This should not have happened"))
|
||||
let defaultEmpty = (o: option<array<'a>>): array<'a> =>
|
||||
switch o {
|
||||
| Some(o) => o
|
||||
| None => []
|
||||
}
|
||||
}
|
||||
|
||||
module R = {
|
||||
let firstErrorOrOpen = (results: array<Belt.Result.t<'a, 'b>>): Belt.Result.t<
|
||||
array<'a>,
|
||||
'b,
|
||||
> => {
|
||||
let bringErrorUp = switch results |> Belt.Array.getBy(_, Belt.Result.isError) {
|
||||
| Some(Belt.Result.Error(err)) => Belt.Result.Error(err)
|
||||
| Some(Belt.Result.Ok(_)) => Belt.Result.Ok(results)
|
||||
| None => Belt.Result.Ok(results)
|
||||
}
|
||||
let forceOpen = (r: array<Belt.Result.t<'a, 'b>>): array<'a> =>
|
||||
r |> Belt.Array.map(_, r => Belt.Result.getExn(r))
|
||||
bringErrorUp |> Belt.Result.map(_, forceOpen)
|
||||
}
|
||||
}
|
||||
|
||||
module Sorted = {
|
||||
let min = first
|
||||
let max = last
|
||||
let range = (~min=min, ~max=max, a) =>
|
||||
switch (min(a), max(a)) {
|
||||
| (Some(min), Some(max)) => Some(max -. min)
|
||||
| _ => None
|
||||
}
|
||||
let binarySearchFirstElementGreaterIndex = (ar: array<'a>, el: 'a) => {
|
||||
let el = Belt.SortArray.binarySearchBy(ar, el, compare)
|
||||
let el = el < 0 ? el * -1 - 1 : el
|
||||
switch el {
|
||||
| e if e >= length(ar) => #overMax
|
||||
| e if e == 0 => #underMin
|
||||
| e => #firstHigher(e)
|
||||
}
|
||||
}
|
||||
|
||||
let concat = (t1: array<'a>, t2: array<'a>) => {
|
||||
let ts = Belt.Array.concat(t1, t2)
|
||||
ts |> Array.fast_sort(compare)
|
||||
ts
|
||||
}
|
||||
|
||||
let concatMany = (t1: array<array<'a>>) => {
|
||||
let ts = Belt.Array.concatMany(t1)
|
||||
ts |> Array.fast_sort(compare)
|
||||
ts
|
||||
}
|
||||
|
||||
module Floats = {
|
||||
let makeIncrementalUp = (a, b) =>
|
||||
Array.make(b - a + 1, a) |> Array.mapi((i, c) => c + i) |> Belt.Array.map(_, float_of_int)
|
||||
|
||||
let makeIncrementalDown = (a, b) =>
|
||||
Array.make(a - b + 1, a) |> Array.mapi((i, c) => c - i) |> Belt.Array.map(_, float_of_int)
|
||||
|
||||
let split = (sortedArray: array<float>) => {
|
||||
let continuous = []
|
||||
let discrete = FloatFloatMap.empty()
|
||||
Belt.Array.forEachWithIndex(sortedArray, (index, element) => {
|
||||
let maxIndex = (sortedArray |> Array.length) - 1
|
||||
let possiblySimilarElements = switch index {
|
||||
| 0 => [index + 1]
|
||||
| n if n == maxIndex => [index - 1]
|
||||
| _ => [index - 1, index + 1]
|
||||
} |> Belt.Array.map(_, r => sortedArray[r])
|
||||
let hasSimilarElement = Belt.Array.some(possiblySimilarElements, r => r == element)
|
||||
hasSimilarElement
|
||||
? FloatFloatMap.increment(element, discrete)
|
||||
: {
|
||||
let _ = Js.Array.push(element, continuous)
|
||||
}
|
||||
()
|
||||
})
|
||||
|
||||
(continuous, discrete)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module Floats = {
|
||||
let sum = Belt.Array.reduce(_, 0., (i, j) => i +. j)
|
||||
let mean = a => sum(a) /. (Array.length(a) |> float_of_int)
|
||||
let random = Js.Math.random_int
|
||||
|
||||
exception RangeError(string)
|
||||
let range = (min: float, max: float, n: int): array<float> =>
|
||||
switch n {
|
||||
| 0 => []
|
||||
| 1 => [min]
|
||||
| 2 => [min, max]
|
||||
| _ if min == max => Belt.Array.make(n, min)
|
||||
| _ if n < 0 => raise(RangeError("n must be greater than 0"))
|
||||
| _ if min > max => raise(RangeError("Min value is less then max value"))
|
||||
| _ =>
|
||||
let diff = (max -. min) /. Belt.Float.fromInt(n - 1)
|
||||
Belt.Array.makeBy(n, i => min +. Belt.Float.fromInt(i) *. diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module JsArray = {
|
||||
let concatSomes = (optionals: Js.Array.t<option<'a>>): Js.Array.t<'a> =>
|
||||
optionals
|
||||
|> Js.Array.filter(Rationale.Option.isSome)
|
||||
|> Js.Array.map(Rationale.Option.toExn("Warning: This should not have happened"))
|
||||
let filter = Js.Array.filter
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
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};
|
|
@ -1,21 +0,0 @@
|
|||
let normal = (mean: float, std: float) =>
|
||||
Js.Float.(
|
||||
{
|
||||
let nMean = toPrecisionWithPrecision(mean, ~digits=4);
|
||||
let nStd = toPrecisionWithPrecision(std, ~digits=2);
|
||||
{j|normal($(nMean), $(nStd))|j};
|
||||
}
|
||||
);
|
||||
|
||||
let logNormal = (mean: float, std: float) => {
|
||||
Js.Float.(
|
||||
{
|
||||
let nMean = toPrecisionWithPrecision(mean, ~digits=4);
|
||||
let nStd = toPrecisionWithPrecision(std, ~digits=2);
|
||||
{j|lognormal({mean: $(nMean), stdev: $(nStd)})|j};
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
let divide = (str1: string, str2: string) => {j|$(str1)/$(str2)|j};
|
||||
let min = (str1: string, str2: string) => {j|min($(str1),$(str2))|j};
|
|
@ -1,5 +0,0 @@
|
|||
%raw(`import('./styles/index.css')`)
|
||||
switch ReactDOM.querySelector("#app") {
|
||||
| Some(root) => ReactDOM.render(<App />, root)
|
||||
| None => ()
|
||||
}
|
9
packages/playground/src/Index.tsx
Normal file
9
packages/playground/src/Index.tsx
Normal file
|
@ -0,0 +1,9 @@
|
|||
import React from 'react'
|
||||
import { render } from "react-dom"
|
||||
import DistBuilder from "./components/DistBuilder"
|
||||
|
||||
var root = document.querySelector("#app")
|
||||
|
||||
if (!(root == null)) {
|
||||
render(<DistBuilder />, root)
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
let ste = React.string
|
||||
let showIf = (cond, comp) => cond ? comp : React.null
|
||||
|
||||
module O = {
|
||||
let defaultNull = E.O.default(React.null)
|
||||
let fmapOrNull = (fn, el) => el |> E.O.fmap(fn) |> E.O.default(React.null)
|
||||
let flatten = E.O.default(React.null)
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
type props
|
||||
@obj external makeProps : (~value:string=?, ~onChange: string => unit, ~children: React.element=?, unit) => props = ""
|
||||
|
||||
@module("./CodeEditor.js")
|
||||
external make: props => React.element = "CodeEditor"
|
|
@ -1,4 +1,4 @@
|
|||
import React from "react";
|
||||
import React, {FC} from "react";
|
||||
import AceEditor from "react-ace";
|
||||
|
||||
import "ace-builds/src-noconflict/mode-golang";
|
||||
|
@ -6,12 +6,12 @@ import "ace-builds/src-noconflict/theme-github";
|
|||
import "ace-builds/src-noconflict/ext-language_tools";
|
||||
import "ace-builds/src-noconflict/keybinding-vim";
|
||||
|
||||
function onChange(newValue) {
|
||||
console.log("change", newValue);
|
||||
interface CodeEditorProps {
|
||||
value : string,
|
||||
onChange : (value: string) => void
|
||||
}
|
||||
|
||||
export function CodeEditor(props) {
|
||||
return (
|
||||
export let CodeEditor : FC<CodeEditorProps> = (props) =>
|
||||
<AceEditor
|
||||
value={props.value}
|
||||
mode="golang"
|
||||
|
@ -32,5 +32,3 @@ export function CodeEditor(props) {
|
|||
enableSnippets: true,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -1,273 +0,0 @@
|
|||
open ReForm
|
||||
open Antd.Grid
|
||||
|
||||
module FormConfig = %lenses(
|
||||
type state = {
|
||||
squiggleString: string,
|
||||
sampleCount: string,
|
||||
outputXYPoints: string,
|
||||
downsampleTo: string,
|
||||
kernelWidth: string,
|
||||
diagramStart: string,
|
||||
diagramStop: string,
|
||||
diagramCount: string,
|
||||
}
|
||||
)
|
||||
|
||||
type options = {
|
||||
sampleCount: int,
|
||||
outputXYPoints: int,
|
||||
downsampleTo: option<int>,
|
||||
kernelWidth: option<float>,
|
||||
diagramStart: float,
|
||||
diagramStop: float,
|
||||
diagramCount: int,
|
||||
}
|
||||
|
||||
module Form = ReForm.Make(FormConfig)
|
||||
|
||||
module FieldText = {
|
||||
@react.component
|
||||
let make = (~field, ~label) => <>
|
||||
<Form.Field
|
||||
field
|
||||
render={({handleChange, error, value, validate}) =>{
|
||||
Js.Console.log(CodeEditor.make);
|
||||
(<CodeEditor value onChange={r => handleChange(r)} />)
|
||||
}
|
||||
}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
module FieldString = {
|
||||
@react.component
|
||||
let make = (~field, ~label) =>
|
||||
<Form.Field
|
||||
field
|
||||
render={({handleChange, error, value, validate}) =>
|
||||
<Antd.Form.Item label={label}>
|
||||
<Antd.Input
|
||||
value onChange={ReForm.Helpers.handleChange(handleChange)} onBlur={_ => validate()}
|
||||
/>
|
||||
</Antd.Form.Item>}
|
||||
/>
|
||||
}
|
||||
|
||||
module FieldFloat = {
|
||||
@react.component
|
||||
let make = (~field, ~label, ~className=CssJs.style(. [])) =>
|
||||
<Form.Field
|
||||
field
|
||||
render={({handleChange, error, value, validate}) =>
|
||||
<Antd.Form.Item label={label}>
|
||||
<Antd.Input
|
||||
value
|
||||
onChange={ReForm.Helpers.handleChange(handleChange)}
|
||||
onBlur={_ => validate()}
|
||||
className={className}
|
||||
/>
|
||||
</Antd.Form.Item>
|
||||
}
|
||||
/>
|
||||
}
|
||||
|
||||
module Styles = {
|
||||
open CssJs
|
||||
let rows = style(. [
|
||||
selector(. ">.antCol:firstChild", [ paddingLeft(em(0.25)), paddingRight(em(0.125)) ]),
|
||||
selector(. ">.antCol:lastChild", [ paddingLeft(em(0.125)), paddingRight(em(0.25)) ]),
|
||||
selector(.
|
||||
">.antCol:not(:firstChild):not(:lastChild)",
|
||||
[ paddingLeft(em(0.125)), paddingRight(em(0.125)) ],
|
||||
),
|
||||
])
|
||||
let parent = style(. [ selector(. ".antInputNumber", [ width(#percent(100.)) ]),
|
||||
selector(. ".anticon", [ verticalAlign(#zero) ]),
|
||||
])
|
||||
let form = style(. [ backgroundColor(hex("eee")), padding(em(1.)) ])
|
||||
let dist = style(. [ padding(em(1.)) ])
|
||||
let spacer = style(. [ marginTop(em(1.)) ])
|
||||
let groupA = style(. [ selector(. ".antInputNumberInput", [ backgroundColor(hex("fff7db")) ]),
|
||||
])
|
||||
let groupB = style(. [ selector(. ".antInputNumberInput", [ backgroundColor(hex("eaf4ff")) ]),
|
||||
])
|
||||
}
|
||||
|
||||
module DemoDist = {
|
||||
@react.component
|
||||
let make = (~squiggleString: string, ~options) =>
|
||||
<Antd.Card title={"Distribution"}>
|
||||
<div>
|
||||
{switch options {
|
||||
| Some(options) =>
|
||||
let inputs1 = SquiggleLang.ProgramEvaluator.Inputs.make(
|
||||
~samplingInputs={
|
||||
sampleCount: Some(options.sampleCount),
|
||||
outputXYPoints: Some(options.outputXYPoints),
|
||||
kernelWidth: options.kernelWidth,
|
||||
pointSetDistLength: Some(options.downsampleTo |> E.O.default(1000)),
|
||||
},
|
||||
~squiggleString,
|
||||
~environment=[
|
||||
("K", #SymbolicDist(#Float(1000.0))),
|
||||
("M", #SymbolicDist(#Float(1000000.0))),
|
||||
("B", #SymbolicDist(#Float(1000000000.0))),
|
||||
("T", #SymbolicDist(#Float(1000000000000.0))),
|
||||
]->Belt.Map.String.fromArray,
|
||||
(),
|
||||
)
|
||||
|
||||
let distributionList = SquiggleLang.ProgramEvaluator.evaluateProgram(inputs1)
|
||||
|
||||
let renderExpression = response1 =>
|
||||
switch response1 {
|
||||
| #DistPlus(distPlus1) => <DistPlusPlot distPlus={SquiggleLang.DistPlus.T.normalize(distPlus1)} />
|
||||
| #Float(f) => <NumberShower number=f precision=3 />
|
||||
| #Function((f, a), env) =>
|
||||
// Problem: When it gets the function, it doesn't save state about previous commands
|
||||
let foo: SquiggleLang.ProgramEvaluator.Inputs.inputs = {
|
||||
squiggleString: squiggleString,
|
||||
samplingInputs: inputs1.samplingInputs,
|
||||
environment: env,
|
||||
}
|
||||
let results =
|
||||
E.A.Floats.range(options.diagramStart, options.diagramStop, options.diagramCount)
|
||||
|> E.A.fmap(r =>
|
||||
SquiggleLang.ProgramEvaluator.evaluateFunction(
|
||||
foo,
|
||||
(f, a),
|
||||
[#SymbolicDist(#Float(r))],
|
||||
) |> E.R.bind(_, a =>
|
||||
switch a {
|
||||
| #DistPlus(d) => Ok((r, SquiggleLang.DistPlus.T.normalize(d)))
|
||||
| n =>
|
||||
Js.log2("Error here", n)
|
||||
Error("wrong type")
|
||||
}
|
||||
)
|
||||
)
|
||||
|> E.A.R.firstErrorOrOpen
|
||||
switch results {
|
||||
| Ok(dists) => <PercentilesChart dists />
|
||||
| Error(r) => r |> R.ste
|
||||
}
|
||||
}
|
||||
|
||||
// Render the list of distributions given by the
|
||||
switch distributionList {
|
||||
| Ok(xs) =>
|
||||
let childrenElements = List.map(renderExpression, xs)
|
||||
Js.Console.log(childrenElements)
|
||||
<ul>
|
||||
{Belt.List.toArray(Belt.List.mapWithIndex(childrenElements, (i, child) => <li key={Belt.Int.toString(i)}>child</li>))->React.array}
|
||||
</ul>
|
||||
| Error(r) => r |> R.ste
|
||||
}
|
||||
| _ => "Nothing to show. Try to change the distribution description." |> R.ste
|
||||
}}
|
||||
</div>
|
||||
</Antd.Card>
|
||||
}
|
||||
|
||||
@react.component
|
||||
let make = () => {
|
||||
let (reloader, setReloader) = React.useState(() => 1)
|
||||
let reform = Form.use(
|
||||
~validationStrategy=OnDemand,
|
||||
~schema=Form.Validation.Schema([]),
|
||||
~onSubmit=({state}) => None,
|
||||
~initialState={
|
||||
//squiggleString: "mm(normal(-10, 2), uniform(18, 25), lognormal({mean: 10, stdev: 8}), triangular(31,40,50))",
|
||||
squiggleString: "mm(normal(5,2), normal(10,2))",
|
||||
sampleCount: "1000",
|
||||
outputXYPoints: "1000",
|
||||
downsampleTo: "",
|
||||
kernelWidth: "",
|
||||
diagramStart: "0",
|
||||
diagramStop: "10",
|
||||
diagramCount: "20",
|
||||
},
|
||||
(),
|
||||
)
|
||||
|
||||
let onSubmit = e => {
|
||||
e->ReactEvent.Synthetic.preventDefault
|
||||
reform.submit()
|
||||
}
|
||||
|
||||
let squiggleString = reform.state.values.squiggleString
|
||||
let sampleCount = reform.state.values.sampleCount |> Js.Float.fromString
|
||||
let outputXYPoints = reform.state.values.outputXYPoints |> Js.Float.fromString
|
||||
let downsampleTo = reform.state.values.downsampleTo |> Js.Float.fromString
|
||||
let kernelWidth = reform.state.values.kernelWidth |> Js.Float.fromString
|
||||
let diagramStart = reform.state.values.diagramStart |> Js.Float.fromString
|
||||
let diagramStop = reform.state.values.diagramStop |> Js.Float.fromString
|
||||
let diagramCount = reform.state.values.diagramCount |> Js.Float.fromString
|
||||
|
||||
let options = switch (sampleCount, outputXYPoints, downsampleTo) {
|
||||
| (_, _, _)
|
||||
if !Js.Float.isNaN(sampleCount) &&
|
||||
(!Js.Float.isNaN(outputXYPoints) &&
|
||||
(!Js.Float.isNaN(downsampleTo) && (sampleCount > 10. && outputXYPoints > 10.))) =>
|
||||
Some({
|
||||
sampleCount: sampleCount |> int_of_float,
|
||||
outputXYPoints: outputXYPoints |> int_of_float,
|
||||
downsampleTo: int_of_float(downsampleTo) > 0 ? Some(int_of_float(downsampleTo)) : None,
|
||||
kernelWidth: kernelWidth == 0.0 ? None : Some(kernelWidth),
|
||||
diagramStart: diagramStart,
|
||||
diagramStop: diagramStop,
|
||||
diagramCount: diagramCount |> int_of_float,
|
||||
})
|
||||
| _ => None
|
||||
}
|
||||
|
||||
let demoDist = React.useMemo1(
|
||||
() => <DemoDist squiggleString options />,
|
||||
[
|
||||
reform.state.values.squiggleString,
|
||||
reform.state.values.sampleCount,
|
||||
reform.state.values.outputXYPoints,
|
||||
reform.state.values.downsampleTo,
|
||||
reform.state.values.kernelWidth,
|
||||
reform.state.values.diagramStart,
|
||||
reform.state.values.diagramStop,
|
||||
reform.state.values.diagramCount,
|
||||
reloader |> string_of_int,
|
||||
],
|
||||
)
|
||||
|
||||
let onReload = _ => setReloader(_ => reloader + 1)
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Antd.Card
|
||||
title={"Distribution Form" |> R.ste}>
|
||||
<Form.Provider value=reform>
|
||||
<Antd.Form onSubmit>
|
||||
<Row _type="flex" className=Styles.rows>
|
||||
<Col span=24> <FieldText field=FormConfig.SquiggleString label="Program" /> </Col>
|
||||
</Row>
|
||||
<Row _type="flex" className=Styles.rows>
|
||||
<Col span=12> <FieldFloat field=FormConfig.SampleCount label="Sample Count" /> </Col>
|
||||
<Col span=12>
|
||||
<FieldFloat field=FormConfig.OutputXYPoints label="Output XY-points" />
|
||||
</Col>
|
||||
<Col span=12>
|
||||
<FieldFloat field=FormConfig.DownsampleTo label="Downsample To" />
|
||||
</Col>
|
||||
<Col span=12> <FieldFloat field=FormConfig.KernelWidth label="Kernel Width" /> </Col>
|
||||
<Col span=12>
|
||||
<FieldFloat field=FormConfig.DiagramStart label="Diagram Start" />
|
||||
</Col>
|
||||
<Col span=12> <FieldFloat field=FormConfig.DiagramStop label="Diagram Stop" /> </Col>
|
||||
<Col span=12>
|
||||
<FieldFloat field=FormConfig.DiagramCount label="Diagram Count" />
|
||||
</Col>
|
||||
</Row>
|
||||
</Antd.Form>
|
||||
</Form.Provider>
|
||||
</Antd.Card>
|
||||
</div>
|
||||
<div> demoDist </div>
|
||||
</div>
|
||||
}
|
171
packages/playground/src/components/DistBuilder.tsx
Normal file
171
packages/playground/src/components/DistBuilder.tsx
Normal file
|
@ -0,0 +1,171 @@
|
|||
import { FC, useState } from "react"
|
||||
import { SquiggleChart } from "@squiggle/components"
|
||||
import { CodeEditor } from "./CodeEditor"
|
||||
import { Form, Input, Card, Row, Col } from "antd"
|
||||
import { css } from '@emotion/react'
|
||||
|
||||
interface FieldFloatProps {
|
||||
label : string,
|
||||
className? : string,
|
||||
value : number,
|
||||
onChange : (value: number) => void,
|
||||
}
|
||||
|
||||
function FieldFloat(Props: FieldFloatProps) {
|
||||
let [contents, setContents] = useState(Props.value + "");
|
||||
return <Form.Item label={Props.label}>
|
||||
<Input
|
||||
value={contents}
|
||||
className={Props.className ? Props.className : ""}
|
||||
onChange={(e) => setContents(e.target.value)}
|
||||
onBlur={(_) => {
|
||||
let result = parseFloat(contents);
|
||||
if(result != NaN) {
|
||||
Props.onChange(result)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
}
|
||||
let rows = css`
|
||||
>.antCol:firstChild {
|
||||
paddingLeft: 0.25em;
|
||||
paddingRight: 0.125em;
|
||||
}
|
||||
>.antCol:lastChild {
|
||||
paddingLeft: 0.125em;
|
||||
paddingRight: 0.25em;
|
||||
}
|
||||
>.antCol:not(:lastChild):not(:lastChild) {
|
||||
paddingLeft: 0.125em;
|
||||
paddingRight: 0.125em;
|
||||
}
|
||||
`
|
||||
|
||||
let parent = css`
|
||||
.antImportNumber {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.anticon {
|
||||
verticalAlign: "zero";
|
||||
}
|
||||
`
|
||||
var form = css`
|
||||
backgroundColor: #eee;
|
||||
padding: 1em;
|
||||
`
|
||||
var dist = css`
|
||||
padding: 1em;
|
||||
`
|
||||
|
||||
var spacer = css`
|
||||
marginTop: 1em;
|
||||
`
|
||||
|
||||
var groupA = css`
|
||||
.antInputNumberInputs {
|
||||
backgroundColor: #fff7db;
|
||||
}
|
||||
`
|
||||
|
||||
var groupB = css`
|
||||
.antInputNumberInput {
|
||||
backgroundColor: #eaf4ff;
|
||||
}
|
||||
`
|
||||
|
||||
var Styles = {
|
||||
rows: rows,
|
||||
parent: parent,
|
||||
form: form,
|
||||
dist: dist,
|
||||
spacer: spacer,
|
||||
groupA: groupA,
|
||||
groupB: groupB
|
||||
};
|
||||
|
||||
let DistBuilder : FC<{}> = (_: {}) => {
|
||||
let [squiggleString, setSquiggleString] = useState("mm(normal(5,2), normal(10,2))")
|
||||
let [sampleCount, setSampleCount] = useState(1000)
|
||||
let [outputXYPoints, setOutputXYPoints] = useState(1000)
|
||||
let [pointDistLength, setPointDistLength] = useState(undefined)
|
||||
let [kernelWidth, setKernelWidth] = useState(undefined)
|
||||
let [diagramStart, setDiagramStart] = useState(0)
|
||||
let [diagramStop, setDiagramStop] = useState(10)
|
||||
let [diagramCount, setDiagramCount] = useState(20)
|
||||
var demoDist =
|
||||
<SquiggleChart
|
||||
squiggleString={squiggleString}
|
||||
sampleCount={sampleCount}
|
||||
outputXYPoints={outputXYPoints}
|
||||
diagramStart={diagramStart}
|
||||
diagramStop={diagramStop}
|
||||
diagramCount={diagramCount}
|
||||
pointDistLength={pointDistLength}
|
||||
/>
|
||||
return (
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Card
|
||||
title="Distribution Form">
|
||||
<Form>
|
||||
<Row css={Styles.rows}>
|
||||
<Col span={24}>
|
||||
<CodeEditor value={squiggleString} onChange={setSquiggleString} /> </Col>
|
||||
</Row>
|
||||
<Row css={Styles.rows}>
|
||||
<Col span={12}>
|
||||
<FieldFloat
|
||||
value={sampleCount}
|
||||
label="Sample Count"
|
||||
onChange={setSampleCount}
|
||||
/> </Col>
|
||||
<Col span={12}>
|
||||
<FieldFloat
|
||||
value={outputXYPoints}
|
||||
onChange={setOutputXYPoints}
|
||||
label="Output XY-points" />
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<FieldFloat
|
||||
value={pointDistLength}
|
||||
onChange={setPointDistLength}
|
||||
label="Downsample To"
|
||||
/>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<FieldFloat
|
||||
value={kernelWidth}
|
||||
onChange={setKernelWidth}
|
||||
label="Kernel Width"
|
||||
/> </Col>
|
||||
<Col span={12}>
|
||||
<FieldFloat
|
||||
value={diagramStart}
|
||||
onChange={setDiagramStart}
|
||||
label="Diagram Start"
|
||||
/>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<FieldFloat
|
||||
value={diagramStop}
|
||||
onChange={setDiagramStop}
|
||||
label="Diagram Stop"
|
||||
/> </Col>
|
||||
<Col span={12}>
|
||||
<FieldFloat
|
||||
value={diagramCount}
|
||||
onChange={setDiagramCount}
|
||||
label="Diagram Count"
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Form>
|
||||
</Card>
|
||||
</div>
|
||||
{demoDist}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default DistBuilder
|
|
@ -1,306 +0,0 @@
|
|||
open DistPlusPlotReducer
|
||||
let plotBlue = #hex("1860ad")
|
||||
|
||||
let showAsForm = (distPlus: SquiggleLang.PointSetTypes.distPlus) =>
|
||||
<div> <Antd.Input value={distPlus.squiggleString |> E.O.default("")} /> </div>
|
||||
|
||||
let showFloat = (~precision=3, number) => <NumberShower number precision />
|
||||
|
||||
let table = (distPlus, x) =>
|
||||
<div>
|
||||
<table className="table-auto text-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<td className="px-4 py-2 "> {"X Point" |> React.string} </td>
|
||||
<td className="px-4 py-2"> {"Discrete Value" |> React.string} </td>
|
||||
<td className="px-4 py-2"> {"Continuous Value" |> React.string} </td>
|
||||
<td className="px-4 py-2"> {"Y Integral to Point" |> React.string} </td>
|
||||
<td className="px-4 py-2"> {"Y Integral Total" |> React.string} </td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className="px-4 py-2 border"> {x |> E.Float.toString |> React.string} </td>
|
||||
<td className="px-4 py-2 border ">
|
||||
{distPlus
|
||||
|> SquiggleLang.DistPlus.T.xToY(x)
|
||||
|> SquiggleLang.PointSetTypes.MixedPoint.toDiscreteValue
|
||||
|> Js.Float.toPrecisionWithPrecision(_, ~digits=7)
|
||||
|> React.string}
|
||||
</td>
|
||||
<td className="px-4 py-2 border ">
|
||||
{distPlus
|
||||
|> SquiggleLang.DistPlus.T.xToY(x)
|
||||
|> SquiggleLang.PointSetTypes.MixedPoint.toContinuousValue
|
||||
|> Js.Float.toPrecisionWithPrecision(_, ~digits=7)
|
||||
|> React.string}
|
||||
</td>
|
||||
<td className="px-4 py-2 border ">
|
||||
{distPlus
|
||||
|> SquiggleLang.DistPlus.T.Integral.xToY(x)
|
||||
|> E.Float.with2DigitsPrecision
|
||||
|> React.string}
|
||||
</td>
|
||||
<td className="px-4 py-2 border ">
|
||||
{distPlus
|
||||
|> SquiggleLang.DistPlus.T.Integral.sum
|
||||
|> E.Float.with2DigitsPrecision
|
||||
|> React.string}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table className="table-auto text-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<td className="px-4 py-2"> {"Continuous Total" |> React.string} </td>
|
||||
<td className="px-4 py-2"> {"Discrete Total" |> React.string} </td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className="px-4 py-2 border">
|
||||
{distPlus
|
||||
|> SquiggleLang.DistPlus.T.toContinuous
|
||||
|> E.O.fmap(SquiggleLang.Continuous.T.Integral.sum)
|
||||
|> E.O.fmap(E.Float.with2DigitsPrecision)
|
||||
|> E.O.default("")
|
||||
|> React.string}
|
||||
</td>
|
||||
<td className="px-4 py-2 border ">
|
||||
{distPlus
|
||||
|> SquiggleLang.DistPlus.T.toDiscrete
|
||||
|> E.O.fmap(SquiggleLang.Discrete.T.Integral.sum)
|
||||
|> E.O.fmap(E.Float.with2DigitsPrecision)
|
||||
|> E.O.default("")
|
||||
|> React.string}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
let percentiles = distPlus =>
|
||||
<div>
|
||||
<table className="table-auto text-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<td className="px-4 py-2"> {"1" |> React.string} </td>
|
||||
<td className="px-4 py-2"> {"5" |> React.string} </td>
|
||||
<td className="px-4 py-2"> {"25" |> React.string} </td>
|
||||
<td className="px-4 py-2"> {"50" |> React.string} </td>
|
||||
<td className="px-4 py-2"> {"75" |> React.string} </td>
|
||||
<td className="px-4 py-2"> {"95" |> React.string} </td>
|
||||
<td className="px-4 py-2"> {"99" |> React.string} </td>
|
||||
<td className="px-4 py-2"> {"99.999" |> React.string} </td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className="px-4 py-2 border">
|
||||
{distPlus |> SquiggleLang.DistPlus.T.Integral.yToX(0.01) |> showFloat}
|
||||
</td>
|
||||
<td className="px-4 py-2 border">
|
||||
{distPlus |> SquiggleLang.DistPlus.T.Integral.yToX(0.05) |> showFloat}
|
||||
</td>
|
||||
<td className="px-4 py-2 border">
|
||||
{distPlus |> SquiggleLang.DistPlus.T.Integral.yToX(0.25) |> showFloat}
|
||||
</td>
|
||||
<td className="px-4 py-2 border">
|
||||
{distPlus |> SquiggleLang.DistPlus.T.Integral.yToX(0.5) |> showFloat}
|
||||
</td>
|
||||
<td className="px-4 py-2 border">
|
||||
{distPlus |> SquiggleLang.DistPlus.T.Integral.yToX(0.75) |> showFloat}
|
||||
</td>
|
||||
<td className="px-4 py-2 border">
|
||||
{distPlus |> SquiggleLang.DistPlus.T.Integral.yToX(0.95) |> showFloat}
|
||||
</td>
|
||||
<td className="px-4 py-2 border">
|
||||
{distPlus |> SquiggleLang.DistPlus.T.Integral.yToX(0.99) |> showFloat}
|
||||
</td>
|
||||
<td className="px-4 py-2 border">
|
||||
{distPlus |> SquiggleLang.DistPlus.T.Integral.yToX(0.99999) |> showFloat}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table className="table-auto text-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<td className="px-4 py-2"> {"mean" |> React.string} </td>
|
||||
<td className="px-4 py-2"> {"standard deviation" |> React.string} </td>
|
||||
<td className="px-4 py-2"> {"variance" |> React.string} </td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className="px-4 py-2 border"> {distPlus |> SquiggleLang.DistPlus.T.mean |> showFloat} </td>
|
||||
<td className="px-4 py-2 border">
|
||||
{distPlus |> SquiggleLang.DistPlus.T.variance |> (r => r ** 0.5) |> showFloat}
|
||||
</td>
|
||||
<td className="px-4 py-2 border"> {distPlus |> SquiggleLang.DistPlus.T.variance |> showFloat} </td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
let adjustBoth = discreteProbabilityMassFraction => {
|
||||
let yMaxDiscreteDomainFactor = discreteProbabilityMassFraction
|
||||
let yMaxContinuousDomainFactor = 1.0 -. discreteProbabilityMassFraction
|
||||
|
||||
// use the bigger proportion, such that whichever is the bigger proportion, the yMax is 1.
|
||||
|
||||
let yMax = yMaxDiscreteDomainFactor > 0.5 ? yMaxDiscreteDomainFactor : yMaxContinuousDomainFactor
|
||||
(yMax /. yMaxDiscreteDomainFactor, yMax /. yMaxContinuousDomainFactor)
|
||||
}
|
||||
|
||||
module DistPlusChart = {
|
||||
@react.component
|
||||
let make = (~distPlus: SquiggleLang.PointSetTypes.distPlus, ~config: chartConfig, ~onHover) => {
|
||||
open SquiggleLang.DistPlus
|
||||
|
||||
let discrete = distPlus |> T.toDiscrete |> E.O.fmap(SquiggleLang.Discrete.getShape)
|
||||
let continuous = distPlus |> T.toContinuous |> E.O.fmap(SquiggleLang.Continuous.getShape)
|
||||
|
||||
// // We subtract a bit from the range to make sure that it fits. Maybe this should be done in d3 instead.
|
||||
// let minX =
|
||||
// switch (
|
||||
// distPlus
|
||||
// |> DistPlus.T.Integral.yToX(0.0001),
|
||||
// range,
|
||||
// ) {
|
||||
// | (min, Some(range)) => Some(min -. range *. 0.001)
|
||||
// | _ => None
|
||||
// };
|
||||
|
||||
let minX = distPlus |> T.Integral.yToX(0.00001)
|
||||
|
||||
let maxX = distPlus |> T.Integral.yToX(0.99999)
|
||||
|
||||
let timeScale = distPlus.unit |> SquiggleLang.PointSetTypes.DistributionUnit.toJson
|
||||
let discreteProbabilityMassFraction = distPlus |> T.toDiscreteProbabilityMassFraction
|
||||
|
||||
let (yMaxDiscreteDomainFactor, yMaxContinuousDomainFactor) = adjustBoth(
|
||||
discreteProbabilityMassFraction,
|
||||
)
|
||||
|
||||
<DistributionPlot
|
||||
xScale={config.xLog ? "log" : "linear"}
|
||||
yScale={config.yLog ? "log" : "linear"}
|
||||
height={DistPlusPlotReducer.heightToPix(config.height)}
|
||||
minX
|
||||
maxX
|
||||
yMaxDiscreteDomainFactor
|
||||
yMaxContinuousDomainFactor
|
||||
?discrete
|
||||
?continuous
|
||||
color=plotBlue
|
||||
onHover
|
||||
timeScale
|
||||
/>
|
||||
}
|
||||
}
|
||||
|
||||
module IntegralChart = {
|
||||
@react.component
|
||||
let make = (~distPlus: SquiggleLang.PointSetTypes.distPlus, ~config: chartConfig, ~onHover) => {
|
||||
let integral = distPlus.integralCache
|
||||
let continuous = integral |> SquiggleLang.Continuous.toLinear |> E.O.fmap(SquiggleLang.Continuous.getShape)
|
||||
let minX = distPlus |> SquiggleLang.DistPlus.T.Integral.yToX(0.00001)
|
||||
|
||||
let maxX = distPlus |> SquiggleLang.DistPlus.T.Integral.yToX(0.99999)
|
||||
let timeScale = distPlus.unit |> SquiggleLang.PointSetTypes.DistributionUnit.toJson
|
||||
<DistributionPlot
|
||||
xScale={config.xLog ? "log" : "linear"}
|
||||
yScale={config.yLog ? "log" : "linear"}
|
||||
height={DistPlusPlotReducer.heightToPix(config.height)}
|
||||
minX
|
||||
maxX
|
||||
?continuous
|
||||
color=plotBlue
|
||||
timeScale
|
||||
onHover
|
||||
/>
|
||||
}
|
||||
}
|
||||
|
||||
module Chart = {
|
||||
@react.component
|
||||
let make = (~distPlus: SquiggleLang.PointSetTypes.distPlus, ~config: chartConfig, ~onHover) => {
|
||||
let chart = React.useMemo2(
|
||||
() =>
|
||||
config.isCumulative
|
||||
? <IntegralChart distPlus config onHover />
|
||||
: <DistPlusChart distPlus config onHover />,
|
||||
(distPlus, config),
|
||||
)
|
||||
<div
|
||||
className={
|
||||
open CssJs
|
||||
style(. [ minHeight(#px(DistPlusPlotReducer.heightToPix(config.height))) ])
|
||||
}>
|
||||
chart
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
let button = "bg-gray-300 hover:bg-gray-500 text-grey-darkest text-xs px-4 py-1"
|
||||
|
||||
@react.component
|
||||
let make = (~distPlus: SquiggleLang.PointSetTypes.distPlus) => {
|
||||
let (x, setX) = React.useState(() => 0.)
|
||||
let (state, dispatch) = React.useReducer(DistPlusPlotReducer.reducer, DistPlusPlotReducer.init)
|
||||
|
||||
<div>
|
||||
{state.distributions
|
||||
|> E.L.fmapi((index, config) =>
|
||||
<div className="flex" key={string_of_int(index)}>
|
||||
<div className="w-4/5"> <Chart distPlus config onHover={r => setX(_ => r)} /> </div>
|
||||
<div className="w-1/5">
|
||||
<div className="opacity-50 hover:opacity-100">
|
||||
<button className=button onClick={_ => dispatch(CHANGE_X_LOG(index))}>
|
||||
{(config.xLog ? "x-log" : "x-linear") |> React.string}
|
||||
</button>
|
||||
<button className=button onClick={_ => dispatch(CHANGE_Y_LOG(index))}>
|
||||
{(config.yLog ? "y-log" : "y-linear") |> React.string}
|
||||
</button>
|
||||
<button
|
||||
className=button
|
||||
onClick={_ => dispatch(CHANGE_IS_CUMULATIVE(index, !config.isCumulative))}>
|
||||
{(config.isCumulative ? "cdf" : "pdf") |> React.string}
|
||||
</button>
|
||||
<button className=button onClick={_ => dispatch(HEIGHT_INCREMENT(index))}>
|
||||
{"expand" |> React.string}
|
||||
</button>
|
||||
<button className=button onClick={_ => dispatch(HEIGHT_DECREMENT(index))}>
|
||||
{"shrink" |> React.string}
|
||||
</button>
|
||||
{index != 0
|
||||
? <button className=button onClick={_ => dispatch(REMOVE_DIST(index))}>
|
||||
{"remove" |> React.string}
|
||||
</button>
|
||||
: React.null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|> E.L.toArray
|
||||
|> React.array}
|
||||
<div className="inline-flex opacity-50 hover:opacity-100">
|
||||
<button className=button onClick={_ => dispatch(CHANGE_SHOW_PERCENTILES)}>
|
||||
{"Percentiles" |> React.string}
|
||||
</button>
|
||||
<button className=button onClick={_ => dispatch(CHANGE_SHOW_STATS)}>
|
||||
{"Debug Stats" |> React.string}
|
||||
</button>
|
||||
<button className=button onClick={_ => dispatch(CHANGE_SHOW_PARAMS)}>
|
||||
{"Params" |> React.string}
|
||||
</button>
|
||||
<button className=button onClick={_ => dispatch(ADD_DIST)}>
|
||||
{"Add" |> React.string}
|
||||
</button>
|
||||
</div>
|
||||
{state.showParams ? showAsForm(distPlus) : React.null}
|
||||
{state.showStats ? table(distPlus, x) : React.null}
|
||||
{state.showPercentiles ? percentiles(distPlus) : React.null}
|
||||
</div>
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
type chartConfig = {
|
||||
xLog: bool,
|
||||
yLog: bool,
|
||||
isCumulative: bool,
|
||||
height: int,
|
||||
};
|
||||
|
||||
type state = {
|
||||
showStats: bool,
|
||||
showPercentiles: bool,
|
||||
showParams: bool,
|
||||
distributions: list(chartConfig),
|
||||
};
|
||||
|
||||
type action =
|
||||
| CHANGE_SHOW_STATS
|
||||
| CHANGE_SHOW_PARAMS
|
||||
| CHANGE_SHOW_PERCENTILES
|
||||
| REMOVE_DIST(int)
|
||||
| ADD_DIST
|
||||
| CHANGE_X_LOG(int)
|
||||
| CHANGE_Y_LOG(int)
|
||||
| CHANGE_IS_CUMULATIVE(int, bool)
|
||||
| HEIGHT_INCREMENT(int)
|
||||
| HEIGHT_DECREMENT(int);
|
||||
|
||||
let changeHeight = (currentHeight, foo: [ | `increment | `decrement]) =>
|
||||
switch (currentHeight, foo) {
|
||||
| (1, `decrement) => 1
|
||||
| (2, `decrement) => 1
|
||||
| (3, `decrement) => 2
|
||||
| (4, `decrement) => 3
|
||||
| (5, `decrement) => 4
|
||||
| (1, `increment) => 2
|
||||
| (2, `increment) => 3
|
||||
| (3, `increment) => 4
|
||||
| (4, `increment) => 5
|
||||
| (5, `increment) => 5
|
||||
| _ => 1
|
||||
};
|
||||
|
||||
let heightToPix =
|
||||
fun
|
||||
| 1 => 80
|
||||
| 2 => 140
|
||||
| 3 => 240
|
||||
| 4 => 340
|
||||
| 5 => 440
|
||||
| _ => 140;
|
||||
|
||||
let distributionReducer = (index, state: list(chartConfig), action) => {
|
||||
switch (action, E.L.get(state, index)) {
|
||||
| (HEIGHT_INCREMENT(_), Some(dist)) =>
|
||||
E.L.update(
|
||||
{...dist, height: changeHeight(dist.height, `increment)},
|
||||
index,
|
||||
state,
|
||||
)
|
||||
| (HEIGHT_DECREMENT(_), Some(dist)) =>
|
||||
E.L.update(
|
||||
{...dist, height: changeHeight(dist.height, `decrement)},
|
||||
index,
|
||||
state,
|
||||
)
|
||||
| (CHANGE_IS_CUMULATIVE(_, isCumulative), Some(dist)) =>
|
||||
E.L.update({...dist, isCumulative}, index, state)
|
||||
| (CHANGE_X_LOG(_), Some(dist)) =>
|
||||
E.L.update({...dist, xLog: !dist.xLog}, index, state)
|
||||
| (CHANGE_Y_LOG(_), Some(dist)) =>
|
||||
E.L.update({...dist, yLog: !dist.yLog}, index, state)
|
||||
| (REMOVE_DIST(_), Some(_)) => E.L.remove(index, 1, state)
|
||||
| (ADD_DIST, Some(_)) =>
|
||||
E.L.append(
|
||||
state,
|
||||
[{yLog: false, xLog: false, isCumulative: false, height: 4}],
|
||||
)
|
||||
| _ => state
|
||||
};
|
||||
};
|
||||
|
||||
let reducer = (state: state, action: action) =>
|
||||
switch (action) {
|
||||
| CHANGE_X_LOG(i)
|
||||
| CHANGE_Y_LOG(i)
|
||||
| CHANGE_IS_CUMULATIVE(i, _)
|
||||
| HEIGHT_DECREMENT(i)
|
||||
| REMOVE_DIST(i)
|
||||
| HEIGHT_INCREMENT(i) => {
|
||||
...state,
|
||||
distributions: distributionReducer(i, state.distributions, action),
|
||||
}
|
||||
| ADD_DIST => {
|
||||
...state,
|
||||
distributions: distributionReducer(0, state.distributions, action),
|
||||
}
|
||||
| CHANGE_SHOW_STATS => {...state, showStats: !state.showStats}
|
||||
| CHANGE_SHOW_PARAMS => {...state, showParams: !state.showParams}
|
||||
| CHANGE_SHOW_PERCENTILES => {
|
||||
...state,
|
||||
showPercentiles: !state.showPercentiles,
|
||||
}
|
||||
};
|
||||
|
||||
let init = {
|
||||
showStats: false,
|
||||
showParams: false,
|
||||
showPercentiles: false,
|
||||
distributions: [
|
||||
{yLog: false, xLog: false, isCumulative: false, height: 4},
|
||||
{yLog: false, xLog: false, isCumulative: true, height: 1},
|
||||
],
|
||||
};
|
|
@ -1,108 +0,0 @@
|
|||
module RawPlot = {
|
||||
type primaryDistribution = option<{"xs": array<float>, "ys": array<float>}>
|
||||
|
||||
type discrete = option<{"xs": array<float>, "ys": array<float>}>
|
||||
|
||||
type props
|
||||
type makeType = props => React.element
|
||||
@obj external makeProps: (
|
||||
~height: int=?,
|
||||
~marginBottom: int=?,
|
||||
~marginTop: int=?,
|
||||
~maxX: float=?,
|
||||
~minX: float=?,
|
||||
~yMaxContinuousDomainFactor: float=?,
|
||||
~yMaxDiscreteDomainFactor: float=?,
|
||||
~onHover: float => (),
|
||||
~continuous: option<{"xs": array<float>, "ys": array<float>}>=?,
|
||||
~discrete: option<{"xs": array<float>, "ys": array<float>}>=?,
|
||||
~xScale: string=?,
|
||||
~yScale: string=?,
|
||||
~showDistributionLines: bool=?,
|
||||
~showDistributionYAxis: bool=?,
|
||||
~showVerticalLine: bool=?,
|
||||
~timeScale:Js.Null.t<{"unit": string, "zero": MomentRe.Moment.t}>=?,
|
||||
~verticalLine: int=?,
|
||||
~children: array<React.element>=?,
|
||||
unit // This unit is a quirk of the type system. Apparently it must exist to have optional arguments in a type
|
||||
) => props = ""
|
||||
|
||||
|
||||
@module("./distPlotReact.js")
|
||||
external make : makeType = "default"
|
||||
}
|
||||
|
||||
module Styles = {
|
||||
open CssJs
|
||||
let textOverlay = style(. [position(#absolute)])
|
||||
let mainText = style(. [ fontSize(#em(1.1))])
|
||||
let secondaryText = style(. [fontSize(#em(0.9))])
|
||||
|
||||
let graph = chartColor =>
|
||||
style(. [
|
||||
position(#relative),
|
||||
selector(. ".xAxis", [fontSize(#px(9))]),
|
||||
selector(. ".xAxis .domain", [ display(#none) ]),
|
||||
selector(. ".xAxis .tick line", [ display(#none) ]),
|
||||
selector(. ".xAxis .tick text", [ color(#hex("7a8998")) ]),
|
||||
selector(. ".chart .areaPath", [ SVG.fill(chartColor) ]),
|
||||
selector(. ".lollipopsLine", [ SVG.stroke(#hex("bfcad4")) ]),
|
||||
selector(. ".lollipopsCircle", [ SVG.stroke(#hex("bfcad4")), SVG.fill(#hex("bfcad4")) ]),
|
||||
selector(. ".lollipopsXAxis .domain", [ display(#none) ]),
|
||||
selector(. ".lollipopsXAxis .tick line", [ display(#none) ]),
|
||||
selector(. ".lollipopsXAxis .tick text", [ display(#none) ]),
|
||||
selector(.
|
||||
".lollipopsTooltip",
|
||||
[ position(#absolute),
|
||||
textAlign(#center),
|
||||
padding(px(2)),
|
||||
backgroundColor(hex("bfcad4")),
|
||||
borderRadius(px(3)),
|
||||
],
|
||||
),
|
||||
selector(.
|
||||
".lollipopsCircleMouseover",
|
||||
[ SVG.fill(hex("ffa500")), SVG.stroke(#hex("fff")) ],
|
||||
),
|
||||
selector(. ".lollipopsLineMouseover", [ SVG.stroke(#hex("ffa500")) ]),
|
||||
])
|
||||
}
|
||||
|
||||
@react.component
|
||||
let make = (
|
||||
~color=#hex("111"),
|
||||
~discrete=?,
|
||||
~height=200,
|
||||
~maxX=?,
|
||||
~minX=?,
|
||||
~yMaxDiscreteDomainFactor=?,
|
||||
~yMaxContinuousDomainFactor=?,
|
||||
~onHover: float => unit=_ => (),
|
||||
~continuous=?,
|
||||
~xScale=?,
|
||||
~yScale=?,
|
||||
~showDistributionLines=false,
|
||||
~showDistributionYAxis=false,
|
||||
~showVerticalLine=false,
|
||||
~timeScale=?,
|
||||
) =>
|
||||
<div className={Styles.graph(color)}>
|
||||
<RawPlot
|
||||
?maxX
|
||||
?minX
|
||||
?yMaxDiscreteDomainFactor
|
||||
?yMaxContinuousDomainFactor
|
||||
?xScale
|
||||
?yScale
|
||||
?timeScale
|
||||
discrete={discrete |> E.O.fmap(SquiggleLang.XYShape.T.toJs)}
|
||||
height
|
||||
marginBottom=50
|
||||
marginTop=0
|
||||
onHover
|
||||
continuous={continuous |> E.O.fmap(SquiggleLang.XYShape.T.toJs)}
|
||||
showDistributionLines
|
||||
showDistributionYAxis
|
||||
showVerticalLine
|
||||
/>
|
||||
</div>
|
|
@ -1,10 +0,0 @@
|
|||
import * as _ from "lodash";
|
||||
import { createClassFromSpec } from "react-vega";
|
||||
import spec from "./spec-percentiles";
|
||||
|
||||
const PercentilesChart = createClassFromSpec({
|
||||
spec,
|
||||
style: "width: 100%",
|
||||
});
|
||||
|
||||
export { PercentilesChart };
|
|
@ -1,55 +0,0 @@
|
|||
open SquiggleLang
|
||||
@module("./PercentilesChart.js")
|
||||
external percentilesChart: React.element = "PercentilesChart"
|
||||
|
||||
module Internal = {
|
||||
type props
|
||||
type makeType = props => React.element
|
||||
type dataType = { "facet": array<
|
||||
{
|
||||
"p1": float,
|
||||
"p10": float,
|
||||
"p20": float,
|
||||
"p30": float,
|
||||
"p40": float,
|
||||
"p5": float,
|
||||
"p50": float,
|
||||
"p60": float,
|
||||
"p70": float,
|
||||
"p80": float,
|
||||
"p90": float,
|
||||
"p95": float,
|
||||
"p99": float,
|
||||
"x": float,
|
||||
}>}
|
||||
@obj external makeProps: (~data: dataType, ~signalListeners: list<string>,~children:React.element, unit) => props = ""
|
||||
|
||||
@module("./PercentilesChart.js")
|
||||
external make : makeType = "PercentilesChart"
|
||||
}
|
||||
|
||||
@react.component
|
||||
@module("./PercentilesChart.js")
|
||||
let make = (~dists: array<(float, PointSetTypes.distPlus)>, ~children=React.null) => {
|
||||
let data = dists -> Belt.Array.map(((x, r)) =>
|
||||
{
|
||||
"x": x,
|
||||
"p1": r |> DistPlus.T.Integral.yToX(0.01),
|
||||
"p5": r |> DistPlus.T.Integral.yToX(0.05),
|
||||
"p10": r |> DistPlus.T.Integral.yToX(0.1),
|
||||
"p20": r |> DistPlus.T.Integral.yToX(0.2),
|
||||
"p30": r |> DistPlus.T.Integral.yToX(0.3),
|
||||
"p40": r |> DistPlus.T.Integral.yToX(0.4),
|
||||
"p50": r |> DistPlus.T.Integral.yToX(0.5),
|
||||
"p60": r |> DistPlus.T.Integral.yToX(0.6),
|
||||
"p70": r |> DistPlus.T.Integral.yToX(0.7),
|
||||
"p80": r |> DistPlus.T.Integral.yToX(0.8),
|
||||
"p90": r |> DistPlus.T.Integral.yToX(0.9),
|
||||
"p95": r |> DistPlus.T.Integral.yToX(0.95),
|
||||
"p99": r |> DistPlus.T.Integral.yToX(0.99),
|
||||
}
|
||||
)
|
||||
Js.log3("Data", dists, data)
|
||||
let da = {"facet": data}
|
||||
<Internal data=da signalListeners=list{}>children</Internal>
|
||||
}
|
|
@ -1,658 +0,0 @@
|
|||
const _ = require('lodash');
|
||||
const d3 = require('d3');
|
||||
const moment = require('moment');
|
||||
require('./styles.css');
|
||||
|
||||
/**
|
||||
* @param arr
|
||||
* @returns {*}
|
||||
*/
|
||||
function exists(arr) {
|
||||
return arr.find(num => _.isFinite(num));
|
||||
}
|
||||
|
||||
export class DistPlotD3 {
|
||||
|
||||
constructor() {
|
||||
this.attrs = {
|
||||
svgWidth: 400,
|
||||
svgHeight: 400,
|
||||
marginTop: 5,
|
||||
marginBottom: 5,
|
||||
marginRight: 50,
|
||||
marginLeft: 5,
|
||||
|
||||
container: null,
|
||||
|
||||
// X
|
||||
minX: null,
|
||||
maxX: null,
|
||||
xScaleType: 'linear',
|
||||
xScaleTimeOptions: null,
|
||||
xScaleLogBase: 10,
|
||||
|
||||
// Y
|
||||
minY: null,
|
||||
maxY: null,
|
||||
yScaleType: 'linear',
|
||||
yScaleTimeOptions: null,
|
||||
yScaleLogBase: 10,
|
||||
|
||||
xMinContinuousDomainFactor: 1,
|
||||
xMaxContinuousDomainFactor: 1,
|
||||
yMaxContinuousDomainFactor: 1,
|
||||
yMaxDiscreteDomainFactor: 1,
|
||||
|
||||
showDistributionYAxis: false,
|
||||
showDistributionLines: true,
|
||||
|
||||
areaColors: ['#E1E5EC', '#E1E5EC'],
|
||||
verticalLine: 110,
|
||||
showVerticalLine: true,
|
||||
data: {
|
||||
continuous: null,
|
||||
discrete: null,
|
||||
},
|
||||
onHover: (e) => {
|
||||
},
|
||||
};
|
||||
|
||||
this.calc = {
|
||||
chartLeftMargin: null,
|
||||
chartTopMargin: null,
|
||||
chartWidth: null,
|
||||
chartHeight: null,
|
||||
};
|
||||
|
||||
this.chart = null;
|
||||
this.svg = null;
|
||||
this._container = null;
|
||||
|
||||
this.formatDates = this.formatDates.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param value
|
||||
* @returns {DistPlotD3}
|
||||
*/
|
||||
set(name, value) {
|
||||
_.set(this.attrs, [name], value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param data
|
||||
* @returns {DistPlotD3}
|
||||
*/
|
||||
data(data) {
|
||||
const continuousXs = _.get(data, 'continuous.xs', []);
|
||||
const continuousYs = _.get(data, 'continuous.ys', []);
|
||||
const discreteXs = _.get(data, 'discrete.xs', []);
|
||||
const discreteYs = _.get(data, 'discrete.ys', []);
|
||||
this.attrs.data = data;
|
||||
this.attrs.data.continuous = {
|
||||
xs: continuousXs,
|
||||
ys: continuousYs,
|
||||
};
|
||||
this.attrs.data.discrete = {
|
||||
xs: discreteXs,
|
||||
ys: discreteYs,
|
||||
};
|
||||
return this;
|
||||
}
|
||||
|
||||
render() {
|
||||
this._container = d3.select(this.attrs.container);
|
||||
if (this._container.node() === null) {
|
||||
throw new Error('Container for D3 is not defined.');
|
||||
}
|
||||
|
||||
if (!['log', 'linear'].includes(this.attrs.xScaleType)) {
|
||||
throw new Error('X-scale type should be either "log" or "linear".');
|
||||
}
|
||||
if (!['log', 'linear'].includes(this.attrs.yScaleType)) {
|
||||
throw new Error('Y-scale type should be either "log" or "linear".');
|
||||
}
|
||||
|
||||
// Log Scale.
|
||||
if (this.attrs.xScaleType === 'log') {
|
||||
this.logFilter('continuous', (x, y) => x > 0);
|
||||
this.logFilter('discrete', (x, y) => x > 0);
|
||||
}
|
||||
if (this.attrs.yScaleType === 'log') {
|
||||
this.logFilter('continuous', (x, y) => y > 0);
|
||||
this.logFilter('discrete', (x, y) => y > 0);
|
||||
}
|
||||
if (
|
||||
this.attrs.xScaleType === 'log'
|
||||
&& this.attrs.minX !== null
|
||||
&& this.attrs.minX < 0
|
||||
) {
|
||||
console.warn('minX should be positive.');
|
||||
this.attrs.minX = undefined;
|
||||
}
|
||||
if (
|
||||
this.attrs.yScaleType === 'log'
|
||||
&& this.attrs.minY !== null
|
||||
&& this.attrs.minY < 0
|
||||
) {
|
||||
console.warn('minY should be positive.');
|
||||
this.attrs.minY = undefined;
|
||||
}
|
||||
|
||||
// Fields.
|
||||
const fields = [
|
||||
'marginLeft', 'marginRight',
|
||||
'marginTop', 'marginBottom',
|
||||
'svgWidth', 'svgHeight',
|
||||
'yMaxContinuousDomainFactor',
|
||||
'yMaxDiscreteDomainFactor',
|
||||
'xScaleLogBase', 'yScaleLogBase',
|
||||
];
|
||||
for (const field of fields) {
|
||||
if (!_.isNumber(this.attrs[field])) {
|
||||
throw new Error(`${field} should be a number.`);
|
||||
}
|
||||
}
|
||||
|
||||
// Sets the width from the DOM element.
|
||||
const containerRect = this._container.node().getBoundingClientRect();
|
||||
if (containerRect.width > 0) {
|
||||
this.attrs.svgWidth = containerRect.width;
|
||||
}
|
||||
|
||||
// Calculated properties.
|
||||
this.calc.chartLeftMargin = this.attrs.marginLeft;
|
||||
this.calc.chartTopMargin = this.attrs.marginTop;
|
||||
this.calc.chartWidth = this.attrs.svgWidth
|
||||
- this.attrs.marginRight
|
||||
- this.attrs.marginLeft;
|
||||
this.calc.chartHeight = this.attrs.svgHeight
|
||||
- this.attrs.marginBottom
|
||||
- this.attrs.marginTop;
|
||||
|
||||
// Add svg.
|
||||
this.svg = this._container
|
||||
.createObject({ tag: 'svg', selector: 'svg-chart-container' })
|
||||
.attr('width', '100%')
|
||||
.attr('height', this.attrs.svgHeight)
|
||||
.attr('pointer-events', 'none');
|
||||
|
||||
// Add container 'g' (empty) element.
|
||||
this.chart = this.svg
|
||||
.createObject({ tag: 'g', selector: 'chart' })
|
||||
.attr(
|
||||
'transform',
|
||||
`translate(${this.calc.chartLeftMargin}, ${this.calc.chartTopMargin})`,
|
||||
);
|
||||
|
||||
try {
|
||||
const common = this.getCommonThings();
|
||||
if (this.hasDate('continuous')) {
|
||||
this.addDistributionChart(common);
|
||||
}
|
||||
if (this.hasDate('discrete')) {
|
||||
this.addLollipopsChart(common);
|
||||
}
|
||||
} catch (e) {
|
||||
this._container.selectAll("*").remove();
|
||||
throw e;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {*}
|
||||
*/
|
||||
getCommonThings() {
|
||||
// Boundaries.
|
||||
const xMin = exists([
|
||||
this.attrs.minX,
|
||||
d3.min(this.attrs.data.continuous.xs),
|
||||
d3.min(this.attrs.data.discrete.xs),
|
||||
]);
|
||||
const xMax = exists([
|
||||
this.attrs.maxX,
|
||||
d3.max(this.attrs.data.continuous.xs),
|
||||
d3.max(this.attrs.data.discrete.xs),
|
||||
]);
|
||||
|
||||
const yMin = exists([
|
||||
this.attrs.minY,
|
||||
d3.min(this.attrs.data.continuous.ys),
|
||||
d3.min(this.attrs.data.discrete.ys),
|
||||
]);
|
||||
const yMax = exists([
|
||||
this.attrs.maxY,
|
||||
d3.max(this.attrs.data.continuous.ys),
|
||||
d3.max(this.attrs.data.discrete.ys),
|
||||
]);
|
||||
|
||||
// Errors.
|
||||
if (!_.isFinite(xMin)) throw new Error('xMin is undefined');
|
||||
if (!_.isFinite(xMax)) throw new Error('xMax is undefined');
|
||||
if (!_.isFinite(yMin)) throw new Error('yMin is undefined');
|
||||
if (!_.isFinite(yMax)) throw new Error('yMax is undefined');
|
||||
|
||||
// X-domains.
|
||||
const xMinDomainFactor = _.get(this.attrs, 'xMinContinuousDomainFactor', 1);
|
||||
const xMaxDomainFactor = _.get(this.attrs, 'xMaxContinuousDomainFactor', 1);
|
||||
const yMinDomainFactor = _.get(this.attrs, 'yMinContinuousDomainFactor', 1);
|
||||
const yMaxDomainFactor = _.get(this.attrs, 'yMaxContinuousDomainFactor', 1);
|
||||
|
||||
const xMinDomain = xMin * xMinDomainFactor;
|
||||
const xMaxDomain = xMax * xMaxDomainFactor;
|
||||
const yMinDomain = yMin * yMinDomainFactor;
|
||||
const yMaxDomain = yMax * yMaxDomainFactor;
|
||||
|
||||
// X-scale.
|
||||
const xScale = this.attrs.xScaleType === 'linear'
|
||||
? d3.scaleLinear()
|
||||
.domain([xMinDomain, xMaxDomain])
|
||||
.range([0, this.calc.chartWidth])
|
||||
: d3.scaleLog()
|
||||
.base(this.attrs.xScaleLogBase)
|
||||
.domain([xMinDomain, xMaxDomain])
|
||||
.range([0, this.calc.chartWidth]);
|
||||
|
||||
// Y-scale.
|
||||
const yScale = this.attrs.yScaleType === 'linear'
|
||||
? d3.scaleLinear()
|
||||
.domain([yMinDomain, yMaxDomain])
|
||||
.range([this.calc.chartHeight, 0])
|
||||
: d3.scaleLog()
|
||||
.base(this.attrs.yScaleLogBase)
|
||||
.domain([yMinDomain, yMaxDomain])
|
||||
.range([this.calc.chartHeight, 0]);
|
||||
|
||||
return {
|
||||
xMin, xMax,
|
||||
xScale, yScale,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param common
|
||||
*/
|
||||
addDistributionChart(common) {
|
||||
const areaColorRange = d3.scaleOrdinal().range(this.attrs.areaColors);
|
||||
const dataPoints = [this.getDataPoints('continuous')];
|
||||
|
||||
const { xMin, xMax, xScale, yScale } = common;
|
||||
|
||||
// X-axis.
|
||||
let xAxis = null;
|
||||
if (!!this.attrs.xScaleTimeOptions) {
|
||||
// Calculates the projection on X-axis.
|
||||
const zero = _.get(this.attrs, 'xScaleTimeOptions.zero', moment());
|
||||
const unit = _.get(this.attrs, 'xScaleTimeOptions.unit', 'years');
|
||||
const diff = Math.abs(xMax - xMin);
|
||||
const left = zero.clone().add(xMin, unit);
|
||||
const right = left.clone().add(diff, unit);
|
||||
|
||||
// X-time-scale.
|
||||
const xScaleTime = d3.scaleTime()
|
||||
.domain([left.toDate(), right.toDate()])
|
||||
.nice()
|
||||
.range([0, this.calc.chartWidth]);
|
||||
|
||||
xAxis = d3.axisBottom()
|
||||
.scale(xScaleTime)
|
||||
.ticks(this.getTimeTicksByStr(unit))
|
||||
.tickFormat(this.formatDates);
|
||||
} else {
|
||||
xAxis = d3.axisBottom(xScale)
|
||||
.ticks(3)
|
||||
.tickFormat(d => {
|
||||
if (Math.abs(d) < 1) {
|
||||
return d3.format('.2')(d);
|
||||
} else if (xMin > 1000 && xMax < 3000) {
|
||||
// Condition which identifies years; 2019, 2020, 2021.
|
||||
return d3.format('.0')(d);
|
||||
} else {
|
||||
const prefix = d3.formatPrefix('.0', d);
|
||||
return prefix(d).replace('G', 'B');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Y-axis.
|
||||
const yAxis = d3.axisRight(yScale);
|
||||
|
||||
// Add axis.
|
||||
this.chart
|
||||
.createObject({ tag: 'g', selector: 'x-axis' })
|
||||
.attr('transform', `translate(0, ${this.calc.chartHeight})`)
|
||||
.call(xAxis);
|
||||
|
||||
if (this.attrs.showDistributionYAxis) {
|
||||
this.chart
|
||||
.createObject({ tag: 'g', selector: 'y-axis' })
|
||||
.call(yAxis);
|
||||
}
|
||||
|
||||
// Draw area.
|
||||
const area = d3.area()
|
||||
.x(d => xScale(d.x))
|
||||
.y1(d => yScale(d.y))
|
||||
.y0(this.calc.chartHeight);
|
||||
|
||||
this.chart
|
||||
.createObjectsWithData({
|
||||
tag: 'path',
|
||||
selector: 'area-path',
|
||||
data: dataPoints,
|
||||
})
|
||||
.attr('d', area)
|
||||
.attr('fill', (d, i) => areaColorRange(i))
|
||||
.attr('opacity', (d, i) => i === 0 ? 0.7 : 0.5);
|
||||
|
||||
// Draw line.
|
||||
if (this.attrs.showDistributionLines) {
|
||||
const line = d3.line()
|
||||
.x(d => xScale(d.x))
|
||||
.y(d => yScale(d.y));
|
||||
|
||||
this.chart
|
||||
.createObjectsWithData({
|
||||
tag: 'path',
|
||||
selector: 'line-path',
|
||||
data: dataPoints,
|
||||
})
|
||||
.attr('d', line)
|
||||
.attr('id', (d, i) => 'line-' + (i + 1))
|
||||
.attr('opacity', (d, i) => i === 0 ? 0.7 : 1)
|
||||
.attr('fill', 'none');
|
||||
}
|
||||
|
||||
if (this.attrs.showVerticalLine) {
|
||||
this.chart
|
||||
.createObject({ tag: 'line', selector: 'v-line' })
|
||||
.attr('x1', xScale(this.attrs.verticalLine))
|
||||
.attr('x2', xScale(this.attrs.verticalLine))
|
||||
.attr('y1', 0)
|
||||
.attr('y2', this.calc.chartHeight)
|
||||
.attr('stroke-width', 1.5)
|
||||
.attr('stroke-dasharray', '6 6')
|
||||
.attr('stroke', 'steelblue');
|
||||
}
|
||||
|
||||
const hoverLine = this.chart
|
||||
.createObject({ tag: 'line', selector: 'hover-line' })
|
||||
.attr('x1', 0)
|
||||
.attr('x2', 0)
|
||||
.attr('y1', 0)
|
||||
.attr('y2', this.calc.chartHeight)
|
||||
.attr('opacity', 0)
|
||||
.attr('stroke-width', 1.5)
|
||||
.attr('stroke-dasharray', '6 6')
|
||||
.attr('stroke', '#22313F');
|
||||
|
||||
// Add drawing rectangle.
|
||||
{
|
||||
const context = this;
|
||||
|
||||
function mouseover() {
|
||||
const mouse = d3.mouse(this);
|
||||
hoverLine
|
||||
.attr('opacity', 1)
|
||||
.attr('x1', mouse[0])
|
||||
.attr('x2', mouse[0]);
|
||||
const xValue = xScale.invert(mouse[0]);
|
||||
context.attrs.onHover(xValue);
|
||||
}
|
||||
|
||||
function mouseout() {
|
||||
hoverLine.attr('opacity', 0);
|
||||
}
|
||||
|
||||
this.chart
|
||||
.createObject({ tag: 'rect', selector: 'mouse-rect' })
|
||||
.attr('width', this.calc.chartWidth)
|
||||
.attr('height', this.calc.chartHeight)
|
||||
.attr('fill', 'transparent')
|
||||
.attr('pointer-events', 'all')
|
||||
.on('mouseover', mouseover)
|
||||
.on('mousemove', mouseover)
|
||||
.on('mouseout', mouseout);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} common
|
||||
* @param {object} common.xScale
|
||||
* @param {object} common.yScale
|
||||
*/
|
||||
addLollipopsChart(common) {
|
||||
const data = this.getDataPoints('discrete');
|
||||
|
||||
const yMin = 0.;
|
||||
const yMax = d3.max(this.attrs.data.discrete.ys);
|
||||
|
||||
// X axis.
|
||||
this.chart.append('g')
|
||||
.attr('class', 'lollipops-x-axis')
|
||||
.attr('transform', `translate(0, ${this.calc.chartHeight})`)
|
||||
.call(d3.axisBottom(common.xScale));
|
||||
|
||||
// Y-domain.
|
||||
const yMinDomainFactor = _.get(this.attrs, 'yMinDiscreteDomainFactor', 1);
|
||||
const yMaxDomainFactor = _.get(this.attrs, 'yMaxDiscreteDomainFactor', 1);
|
||||
const yMinDomain = yMin * yMinDomainFactor;
|
||||
const yMaxDomain = yMax * yMaxDomainFactor;
|
||||
|
||||
// Y-scale.
|
||||
const yScale = this.attrs.yScaleType === 'linear'
|
||||
? d3.scaleLinear()
|
||||
.domain([yMinDomain, yMaxDomain])
|
||||
.range([this.calc.chartHeight, 0])
|
||||
: d3.scaleLog()
|
||||
.base(this.attrs.yScaleLogBase)
|
||||
.domain([yMinDomain, yMaxDomain])
|
||||
.range([this.calc.chartHeight, 0]);
|
||||
|
||||
//
|
||||
const yTicks = Math.floor(this.calc.chartHeight / 20);
|
||||
const yAxis = d3.axisLeft(yScale).ticks(yTicks);
|
||||
|
||||
// Adds 'g' for an y-axis.
|
||||
this.chart.append('g')
|
||||
.attr('class', 'lollipops-y-axis')
|
||||
.attr('transform', `translate(${this.calc.chartWidth}, 0)`)
|
||||
.call(yAxis);
|
||||
|
||||
const thi$ = this;
|
||||
|
||||
function showTooltip(d) {
|
||||
thi$.chart.select('.lollipops-line-' + d.id)
|
||||
.classed('lollipops-line-mouseover', true);
|
||||
thi$.chart.select('.lollipops-circle-' + d.id)
|
||||
.classed('lollipops-circle-mouseover', true)
|
||||
.attr('r', 6);
|
||||
tooltip.transition()
|
||||
.style('opacity', .9);
|
||||
tooltip.html(`x: ${d.x}, y: ${(d.y * 100).toFixed(1)}%`)
|
||||
.style('left', (common.xScale(d.x) + 60) + 'px')
|
||||
.style('top', yScale(d.y) + 'px');
|
||||
}
|
||||
|
||||
function hideTooltip(d) {
|
||||
thi$.chart.select('.lollipops-line-' + d.id)
|
||||
.classed('lollipops-line-mouseover', false);
|
||||
thi$.chart.select('.lollipops-circle-' + d.id)
|
||||
.classed('lollipops-circle-mouseover', false)
|
||||
.attr('r', 4);
|
||||
tooltip.transition()
|
||||
.style('opacity', 0);
|
||||
}
|
||||
|
||||
// Lines.
|
||||
this.chart.selectAll('lollipops-line')
|
||||
.data(data)
|
||||
.enter()
|
||||
.append('line')
|
||||
.attr('class', 'lollipops-line')
|
||||
.attr('class', d => 'lollipops-line lollipops-line-' + d.id)
|
||||
.attr('x1', d => common.xScale(d.x))
|
||||
.attr('x2', d => common.xScale(d.x))
|
||||
.attr('y1', d => yScale(d.y))
|
||||
.attr('y2', yScale(yMin));
|
||||
|
||||
// Define the div for the tooltip
|
||||
const tooltip = this._container.append('div')
|
||||
.attr('class', 'lollipop-tooltip')
|
||||
.style('opacity', 0);
|
||||
|
||||
// Circles.
|
||||
this.chart.selectAll('lollipops-circle')
|
||||
.data(data)
|
||||
.enter()
|
||||
.append('circle')
|
||||
.attr('class', d => 'lollipops-circle lollipops-circle-' + d.id)
|
||||
.attr('cx', d => common.xScale(d.x))
|
||||
.attr('cy', d => yScale(d.y))
|
||||
.attr('r', '4');
|
||||
|
||||
// Rectangles.
|
||||
this.chart.selectAll('lollipops-rectangle')
|
||||
.data(data)
|
||||
.enter()
|
||||
.append('rect')
|
||||
.attr('width', 30)
|
||||
.attr('height', d => this.calc.chartHeight - yScale(d.y) + 10)
|
||||
.attr('x', d => common.xScale(d.x) - 15)
|
||||
.attr('y', d => yScale(d.y) - 10)
|
||||
.attr('opacity', 0)
|
||||
.attr('pointer-events', 'all')
|
||||
.on('mouseover', showTooltip)
|
||||
.on('mouseout', hideTooltip)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ts
|
||||
* @returns {string}
|
||||
*/
|
||||
formatDates(ts) {
|
||||
return moment(ts).format('MMMM Do YYYY');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} unit
|
||||
* @returns {*}
|
||||
*/
|
||||
getTimeTicksByStr(unit) {
|
||||
switch (unit) {
|
||||
case 'months':
|
||||
return d3.timeMonth.every(4);
|
||||
case 'quarters':
|
||||
// It is temporary solution, but it works
|
||||
// if the difference between edge dates is not
|
||||
// much more than 10 units.
|
||||
return d3.timeMonth.every(12);
|
||||
case 'hours':
|
||||
return d3.timeHour.every(10);
|
||||
case 'days':
|
||||
return d3.timeDay.every(7);
|
||||
case 'seconds':
|
||||
return d3.timeSecond.every(10);
|
||||
case 'years':
|
||||
return d3.timeYear.every(10);
|
||||
case 'minutes':
|
||||
return d3.timeMinute.every(10);
|
||||
case 'weeks':
|
||||
return d3.timeWeek.every(10);
|
||||
case 'milliseconds':
|
||||
return d3.timeMillisecond.every(10);
|
||||
default:
|
||||
return d3.timeYear.every(10);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
* @returns {{x: number[], y: number[]}}
|
||||
*/
|
||||
getDataPoints(key) {
|
||||
const dt = [];
|
||||
const emptyShape = { xs: [], ys: [] };
|
||||
const data = _.get(this.attrs.data, key, emptyShape);
|
||||
const len = data.xs.length;
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
const x = data.xs[i];
|
||||
const y = data.ys[i];
|
||||
const id = i;
|
||||
dt.push({ x, y, id });
|
||||
}
|
||||
|
||||
return dt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
* @param {function} pred
|
||||
* @returns {{x: number[], y: number[]}}
|
||||
*/
|
||||
logFilter(key, pred) {
|
||||
const xs = [];
|
||||
const ys = [];
|
||||
const emptyShape = { xs: [], ys: [] };
|
||||
const data = _.get(this.attrs.data, key, emptyShape);
|
||||
|
||||
for (let i = 0, len = data.xs.length; i < len; i++) {
|
||||
const x = data.xs[i];
|
||||
const y = data.ys[i];
|
||||
if (pred(x, y)) {
|
||||
xs.push(x);
|
||||
ys.push(y);
|
||||
}
|
||||
}
|
||||
|
||||
_.set(this.attrs.data, [key, 'xs'], xs);
|
||||
_.set(this.attrs.data, [key, 'ys'], ys);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
* @returns {boolean}
|
||||
*/
|
||||
hasDate(key) {
|
||||
const xs = _.get(this.attrs, ['data', key, 'xs']);
|
||||
return !!_.size(xs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @docs: https://github.com/d3/d3-selection
|
||||
* @param {object} params
|
||||
* @param {string} params.selector
|
||||
* @param {string} params.tag
|
||||
* @returns {*}
|
||||
*/
|
||||
d3.selection.prototype.createObject = function createObject(params) {
|
||||
const selector = params.selector;
|
||||
const tag = params.tag;
|
||||
return this.insert(tag).attr('class', selector);
|
||||
};
|
||||
|
||||
/**
|
||||
* @docs: https://github.com/d3/d3-selection
|
||||
* @param {object} params
|
||||
* @param {string} params.selector
|
||||
* @param {string} params.tag
|
||||
* @param {*[]} params.data
|
||||
* @returns {*}
|
||||
*/
|
||||
d3.selection.prototype.createObjectsWithData = function createObjectsWithData(params) {
|
||||
const selector = params.selector;
|
||||
const tag = params.tag;
|
||||
const data = params.data;
|
||||
|
||||
return this.selectAll('.' + selector)
|
||||
.data(data)
|
||||
.enter()
|
||||
.insert(tag)
|
||||
.attr('class', selector);
|
||||
};
|
|
@ -1,81 +0,0 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import { useSize } from 'react-use';
|
||||
import { DistPlotD3 } from './distPlotD3';
|
||||
|
||||
/**
|
||||
* @param min
|
||||
* @param max
|
||||
* @returns {number}
|
||||
*/
|
||||
function getRandomInt(min, max) {
|
||||
min = Math.ceil(min);
|
||||
max = Math.floor(max);
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param props
|
||||
* @returns {*}
|
||||
* @constructor
|
||||
*/
|
||||
function DistPlotReact(props) {
|
||||
const containerRef = React.createRef();
|
||||
const key = "cdf-chart-react-" + getRandomInt(0, 1000);
|
||||
const style = !!props.width ? { width: props.width + "px" } : {};
|
||||
|
||||
const [sized, { width }] = useSize(() => {
|
||||
return React.createElement("div", {
|
||||
key: "resizable-div",
|
||||
});
|
||||
}, {
|
||||
width: props.width,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
new DistPlotD3()
|
||||
.set('svgWidth', width)
|
||||
.set('svgHeight', props.height)
|
||||
.set('maxX', props.maxX)
|
||||
.set('minX', props.minX)
|
||||
.set('onHover', props.onHover)
|
||||
.set('marginBottom', props.marginBottom || 15)
|
||||
.set('marginLeft', 30)
|
||||
.set('marginRight', 30)
|
||||
.set('marginTop', 5)
|
||||
.set('showDistributionLines', props.showDistributionLines)
|
||||
.set('showDistributionYAxis', props.showDistributionYAxis)
|
||||
.set('verticalLine', props.verticalLine || 110)
|
||||
.set('showVerticalLine', props.showVerticalLine)
|
||||
.set('container', containerRef.current)
|
||||
.set('xScaleType', props.xScale || 'linear')
|
||||
.set('yScaleType', props.yScale || 'linear')
|
||||
.set('xScaleTimeOptions', props.timeScale)
|
||||
.set('yMaxContinuousDomainFactor', props.yMaxContinuousDomainFactor || 1)
|
||||
.set('yMaxDiscreteDomainFactor', props.yMaxDiscreteDomainFactor || 1)
|
||||
.data({
|
||||
continuous: props.continuous,
|
||||
discrete: props.discrete,
|
||||
})
|
||||
.render();
|
||||
} catch (e) {
|
||||
console.error("distPlotD3 Error: ", e)
|
||||
}
|
||||
});
|
||||
|
||||
return React.createElement("div", {
|
||||
style: {
|
||||
paddingLeft: "10px",
|
||||
paddingRight: "10px",
|
||||
},
|
||||
}, [
|
||||
sized,
|
||||
React.createElement("div", {
|
||||
key,
|
||||
style,
|
||||
ref: containerRef,
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
export default DistPlotReact;
|
|
@ -1,208 +0,0 @@
|
|||
{
|
||||
"$schema": "https://vega.github.io/schema/vega/v5.json",
|
||||
"width": 500,
|
||||
"height": 400,
|
||||
"padding": 5,
|
||||
"data": [
|
||||
{
|
||||
"name": "facet",
|
||||
"values": [],
|
||||
"format": { "type": "json", "parse": { "timestamp": "date" } }
|
||||
},
|
||||
{
|
||||
"name": "table",
|
||||
"source": "facet",
|
||||
"transform": [
|
||||
{
|
||||
"type": "aggregate",
|
||||
"groupby": ["x"],
|
||||
"ops": [
|
||||
"mean",
|
||||
"mean",
|
||||
"mean",
|
||||
"mean",
|
||||
"mean",
|
||||
"mean",
|
||||
"mean",
|
||||
"mean",
|
||||
"mean",
|
||||
"mean",
|
||||
"mean",
|
||||
"mean",
|
||||
"mean"
|
||||
],
|
||||
"fields": [
|
||||
"p1",
|
||||
"p5",
|
||||
"p10",
|
||||
"p20",
|
||||
"p30",
|
||||
"p40",
|
||||
"p50",
|
||||
"p60",
|
||||
"p70",
|
||||
"p80",
|
||||
"p90",
|
||||
"p95",
|
||||
"p99"
|
||||
],
|
||||
"as": [
|
||||
"p1",
|
||||
"p5",
|
||||
"p10",
|
||||
"p20",
|
||||
"p30",
|
||||
"p40",
|
||||
"p50",
|
||||
"p60",
|
||||
"p70",
|
||||
"p80",
|
||||
"p90",
|
||||
"p95",
|
||||
"p99"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"scales": [
|
||||
{
|
||||
"name": "xscale",
|
||||
"type": "linear",
|
||||
"nice": true,
|
||||
"domain": { "data": "facet", "field": "x" },
|
||||
"range": "width"
|
||||
},
|
||||
{
|
||||
"name": "yscale",
|
||||
"type": "linear",
|
||||
"range": "height",
|
||||
"nice": true,
|
||||
"zero": true,
|
||||
"domain": { "data": "facet", "field": "p99" }
|
||||
}
|
||||
],
|
||||
"axes": [
|
||||
{
|
||||
"orient": "bottom",
|
||||
"scale": "xscale",
|
||||
"grid": false,
|
||||
"tickSize": 2,
|
||||
"encode": {
|
||||
"grid": { "enter": { "stroke": { "value": "#ccc" } } },
|
||||
"ticks": { "enter": { "stroke": { "value": "#ccc" } } }
|
||||
}
|
||||
},
|
||||
{
|
||||
"orient": "left",
|
||||
"scale": "yscale",
|
||||
"grid": false,
|
||||
"domain": false,
|
||||
"tickSize": 2,
|
||||
"encode": {
|
||||
"grid": { "enter": { "stroke": { "value": "#ccc" } } },
|
||||
"ticks": { "enter": { "stroke": { "value": "#ccc" } } }
|
||||
}
|
||||
}
|
||||
],
|
||||
"marks": [
|
||||
{
|
||||
"type": "area",
|
||||
"from": { "data": "table" },
|
||||
"encode": {
|
||||
"enter": { "fill": { "value": "#4C78A8" } },
|
||||
"update": {
|
||||
"interpolate": { "value": "monotone" },
|
||||
"x": { "scale": "xscale", "field": "x" },
|
||||
"y": { "scale": "yscale", "field": "p1" },
|
||||
"y2": { "scale": "yscale", "field": "p99" },
|
||||
"opacity": { "value": 0.05 }
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "area",
|
||||
"from": { "data": "table" },
|
||||
"encode": {
|
||||
"enter": { "fill": { "value": "#4C78A8" } },
|
||||
"update": {
|
||||
"interpolate": { "value": "monotone" },
|
||||
"x": { "scale": "xscale", "field": "x" },
|
||||
"y": { "scale": "yscale", "field": "p5" },
|
||||
"y2": { "scale": "yscale", "field": "p95" },
|
||||
"opacity": { "value": 0.1 }
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "area",
|
||||
"from": { "data": "table" },
|
||||
"encode": {
|
||||
"enter": { "fill": { "value": "#4C78A8" } },
|
||||
"update": {
|
||||
"interpolate": { "value": "monotone" },
|
||||
"x": { "scale": "xscale", "field": "x" },
|
||||
"y": { "scale": "yscale", "field": "p10" },
|
||||
"y2": { "scale": "yscale", "field": "p90" },
|
||||
"opacity": { "value": 0.15 }
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "area",
|
||||
"from": { "data": "table" },
|
||||
"encode": {
|
||||
"enter": { "fill": { "value": "#4C78A8" } },
|
||||
"update": {
|
||||
"interpolate": { "value": "monotone" },
|
||||
"x": { "scale": "xscale", "field": "x" },
|
||||
"y": { "scale": "yscale", "field": "p20" },
|
||||
"y2": { "scale": "yscale", "field": "p80" },
|
||||
"opacity": { "value": 0.2 }
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "area",
|
||||
"from": { "data": "table" },
|
||||
"encode": {
|
||||
"enter": { "fill": { "value": "#4C78A8" } },
|
||||
"update": {
|
||||
"interpolate": { "value": "monotone" },
|
||||
"x": { "scale": "xscale", "field": "x" },
|
||||
"y": { "scale": "yscale", "field": "p30" },
|
||||
"y2": { "scale": "yscale", "field": "p70" },
|
||||
"opacity": { "value": 0.2 }
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "area",
|
||||
"from": { "data": "table" },
|
||||
"encode": {
|
||||
"enter": { "fill": { "value": "#4C78A8" } },
|
||||
"update": {
|
||||
"interpolate": { "value": "monotone" },
|
||||
"x": { "scale": "xscale", "field": "x" },
|
||||
"y": { "scale": "yscale", "field": "p40" },
|
||||
"y2": { "scale": "yscale", "field": "p60" },
|
||||
"opacity": { "value": 0.2 }
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "line",
|
||||
"from": { "data": "table" },
|
||||
"encode": {
|
||||
"update": {
|
||||
"interpolate": { "value": "monotone" },
|
||||
"stroke": { "value": "#4C78A8" },
|
||||
"strokeWidth": { "value": 2 },
|
||||
"opacity": { "value": 0.8 },
|
||||
"x": { "scale": "xscale", "field": "x" },
|
||||
"y": { "scale": "yscale", "field": "p50" }
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
.lollipops-line-mouseover {
|
||||
stroke-dasharray: 4;
|
||||
animation: dash 2s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes dash {
|
||||
to {
|
||||
stroke-dashoffset: 1000;
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
module JS = {
|
||||
@deriving(abstract)
|
||||
type numberPresentation = {
|
||||
value: string,
|
||||
power: option<float>,
|
||||
symbol: option<string>,
|
||||
}
|
||||
|
||||
@module("./numberShower.js")
|
||||
external numberShow: (float, int) => numberPresentation = "numberShow"
|
||||
}
|
||||
|
||||
let sup = {
|
||||
open CssJs
|
||||
style(. [ fontSize(#em(0.6)), verticalAlign(#super) ])
|
||||
}
|
||||
|
||||
@react.component
|
||||
let make = (~number, ~precision) => {
|
||||
let numberWithPresentation = JS.numberShow(number, precision)
|
||||
<span>
|
||||
{JS.valueGet(numberWithPresentation) |> React.string}
|
||||
{JS.symbolGet(numberWithPresentation) |> R.O.fmapOrNull(React.string)}
|
||||
{JS.powerGet(numberWithPresentation) |> R.O.fmapOrNull(e =>
|
||||
<span>
|
||||
{j`\\u00b710` |> React.string}
|
||||
<span className=sup> {e |> E.Float.toString |> React.string} </span>
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
// 105 -> 3
|
||||
const orderOfMagnitudeNum = (n) => {
|
||||
return Math.pow(10, n);
|
||||
};
|
||||
|
||||
// 105 -> 3
|
||||
const orderOfMagnitude = (n) => {
|
||||
return Math.floor(Math.log(n) / Math.LN10 + 0.000000001);
|
||||
};
|
||||
|
||||
function withXSigFigs(number, sigFigs) {
|
||||
const withPrecision = number.toPrecision(sigFigs);
|
||||
const formatted = Number(withPrecision);
|
||||
return `${formatted}`;
|
||||
}
|
||||
|
||||
class NumberShower {
|
||||
constructor(number, precision = 2) {
|
||||
this.number = number;
|
||||
this.precision = precision;
|
||||
}
|
||||
|
||||
convert() {
|
||||
const number = Math.abs(this.number);
|
||||
const response = this.evaluate(number);
|
||||
if (this.number < 0) {
|
||||
response.value = '-' + response.value;
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
metricSystem(number, order) {
|
||||
const newNumber = number / orderOfMagnitudeNum(order);
|
||||
const precision = this.precision;
|
||||
return `${withXSigFigs(newNumber, precision)}`;
|
||||
}
|
||||
|
||||
evaluate(number) {
|
||||
if (number === 0) {
|
||||
return { value: this.metricSystem(0, 0) }
|
||||
}
|
||||
|
||||
const order = orderOfMagnitude(number);
|
||||
if (order < -2) {
|
||||
return { value: this.metricSystem(number, order), power: order };
|
||||
} else if (order < 4) {
|
||||
return { value: this.metricSystem(number, 0) };
|
||||
} else if (order < 6) {
|
||||
return { value: this.metricSystem(number, 3), symbol: 'K' };
|
||||
} else if (order < 9) {
|
||||
return { value: this.metricSystem(number, 6), symbol: 'M' };
|
||||
} else if (order < 12) {
|
||||
return { value: this.metricSystem(number, 9), symbol: 'B' };
|
||||
} else if (order < 15) {
|
||||
return { value: this.metricSystem(number, 12), symbol: 'T' };
|
||||
} else {
|
||||
return { value: this.metricSystem(number, order), power: order };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function numberShow(number, precision = 2) {
|
||||
const ns = new NumberShower(number, precision);
|
||||
return ns.convert();
|
||||
}
|
|
@ -5,9 +5,9 @@
|
|||
<meta charset="UTF-8">
|
||||
<title>Squiggle Language</title>
|
||||
<link href="https://fonts.googleapis.com/css?family=Lato:300,400,700,900" rel="stylesheet">
|
||||
<link href="./styles/index.css" rel="stylesheet">
|
||||
<link href="./styles/antd.css" rel="stylesheet">
|
||||
<script type="module" src="./Index.bs.js" defer></script>
|
||||
<link href="./styles/index.css" rel="stylesheet">
|
||||
<script type="module" src="./Index.tsx" defer></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
@tailwind base;
|
||||
|
||||
@tailwind components;
|
||||
|
||||
@tailwind utilities;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
9
packages/playground/tailwind.config.js
Normal file
9
packages/playground/tailwind.config.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
module.exports = {
|
||||
content: [
|
||||
"./src/components/*.tsx"
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
|
@ -1,694 +0,0 @@
|
|||
module.exports = {
|
||||
prefix: '',
|
||||
important: false,
|
||||
separator: ':',
|
||||
theme: {
|
||||
screens: {
|
||||
sm: '640px',
|
||||
md: '768px',
|
||||
lg: '1024px',
|
||||
xl: '1280px',
|
||||
},
|
||||
colors: {
|
||||
transparent: 'transparent',
|
||||
|
||||
black: '#000',
|
||||
white: '#fff',
|
||||
|
||||
gray: {
|
||||
100: '#f7fafc',
|
||||
200: '#edf2f7',
|
||||
300: '#e2e8f0',
|
||||
400: '#cbd5e0',
|
||||
500: '#a0aec0',
|
||||
600: '#718096',
|
||||
700: '#4a5568',
|
||||
800: '#2d3748',
|
||||
900: '#1a202c',
|
||||
},
|
||||
red: {
|
||||
100: '#fff5f5',
|
||||
200: '#fed7d7',
|
||||
300: '#feb2b2',
|
||||
400: '#fc8181',
|
||||
500: '#f56565',
|
||||
600: '#e53e3e',
|
||||
700: '#c53030',
|
||||
800: '#9b2c2c',
|
||||
900: '#742a2a',
|
||||
},
|
||||
orange: {
|
||||
100: '#fffaf0',
|
||||
200: '#feebc8',
|
||||
300: '#fbd38d',
|
||||
400: '#f6ad55',
|
||||
500: '#ed8936',
|
||||
600: '#dd6b20',
|
||||
700: '#c05621',
|
||||
800: '#9c4221',
|
||||
900: '#7b341e',
|
||||
},
|
||||
yellow: {
|
||||
100: '#fffff0',
|
||||
200: '#fefcbf',
|
||||
300: '#faf089',
|
||||
400: '#f6e05e',
|
||||
500: '#ecc94b',
|
||||
600: '#d69e2e',
|
||||
700: '#b7791f',
|
||||
800: '#975a16',
|
||||
900: '#744210',
|
||||
},
|
||||
green: {
|
||||
100: '#f0fff4',
|
||||
200: '#c6f6d5',
|
||||
300: '#9ae6b4',
|
||||
400: '#68d391',
|
||||
500: '#48bb78',
|
||||
600: '#38a169',
|
||||
700: '#2f855a',
|
||||
800: '#276749',
|
||||
900: '#22543d',
|
||||
},
|
||||
teal: {
|
||||
100: '#e6fffa',
|
||||
200: '#b2f5ea',
|
||||
300: '#81e6d9',
|
||||
400: '#4fd1c5',
|
||||
500: '#38b2ac',
|
||||
600: '#319795',
|
||||
700: '#2c7a7b',
|
||||
800: '#285e61',
|
||||
900: '#234e52',
|
||||
},
|
||||
blue: {
|
||||
100: '#ebf8ff',
|
||||
200: '#bee3f8',
|
||||
300: '#90cdf4',
|
||||
400: '#63b3ed',
|
||||
500: '#4299e1',
|
||||
600: '#3182ce',
|
||||
700: '#2b6cb0',
|
||||
800: '#2c5282',
|
||||
900: '#2a4365',
|
||||
},
|
||||
indigo: {
|
||||
100: '#ebf4ff',
|
||||
200: '#c3dafe',
|
||||
300: '#a3bffa',
|
||||
400: '#7f9cf5',
|
||||
500: '#667eea',
|
||||
600: '#5a67d8',
|
||||
700: '#4c51bf',
|
||||
800: '#434190',
|
||||
900: '#3c366b',
|
||||
},
|
||||
purple: {
|
||||
100: '#faf5ff',
|
||||
200: '#e9d8fd',
|
||||
300: '#d6bcfa',
|
||||
400: '#b794f4',
|
||||
500: '#9f7aea',
|
||||
600: '#805ad5',
|
||||
700: '#6b46c1',
|
||||
800: '#553c9a',
|
||||
900: '#44337a',
|
||||
},
|
||||
pink: {
|
||||
100: '#fff5f7',
|
||||
200: '#fed7e2',
|
||||
300: '#fbb6ce',
|
||||
400: '#f687b3',
|
||||
500: '#ed64a6',
|
||||
600: '#d53f8c',
|
||||
700: '#b83280',
|
||||
800: '#97266d',
|
||||
900: '#702459',
|
||||
},
|
||||
},
|
||||
spacing: {
|
||||
px: '1px',
|
||||
'0': '0',
|
||||
'1': '0.25rem',
|
||||
'2': '0.5rem',
|
||||
'3': '0.75rem',
|
||||
'4': '1rem',
|
||||
'5': '1.25rem',
|
||||
'6': '1.5rem',
|
||||
'8': '2rem',
|
||||
'10': '2.5rem',
|
||||
'12': '3rem',
|
||||
'16': '4rem',
|
||||
'20': '5rem',
|
||||
'24': '6rem',
|
||||
'32': '8rem',
|
||||
'40': '10rem',
|
||||
'48': '12rem',
|
||||
'56': '14rem',
|
||||
'64': '16rem',
|
||||
},
|
||||
backgroundColor: theme => theme('colors'),
|
||||
backgroundPosition: {
|
||||
bottom: 'bottom',
|
||||
center: 'center',
|
||||
left: 'left',
|
||||
'left-bottom': 'left bottom',
|
||||
'left-top': 'left top',
|
||||
right: 'right',
|
||||
'right-bottom': 'right bottom',
|
||||
'right-top': 'right top',
|
||||
top: 'top',
|
||||
},
|
||||
backgroundSize: {
|
||||
auto: 'auto',
|
||||
cover: 'cover',
|
||||
contain: 'contain',
|
||||
},
|
||||
borderColor: theme => ({
|
||||
...theme('colors'),
|
||||
default: theme('colors.gray.300', 'currentColor'),
|
||||
}),
|
||||
borderRadius: {
|
||||
none: '0',
|
||||
sm: '0.125rem',
|
||||
default: '0.25rem',
|
||||
md: '0.375rem',
|
||||
lg: '0.5rem',
|
||||
full: '9999px',
|
||||
},
|
||||
borderWidth: {
|
||||
default: '1px',
|
||||
'0': '0',
|
||||
'2': '2px',
|
||||
'4': '4px',
|
||||
'8': '8px',
|
||||
},
|
||||
boxShadow: {
|
||||
xs: '0 0 0 1px rgba(0, 0, 0, 0.05)',
|
||||
sm: '0 1px 2px 0 rgba(0, 0, 0, 0.05)',
|
||||
default: '0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)',
|
||||
md: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)',
|
||||
lg: '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)',
|
||||
xl: '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)',
|
||||
'2xl': '0 25px 50px -12px rgba(0, 0, 0, 0.25)',
|
||||
inner: 'inset 0 2px 4px 0 rgba(0, 0, 0, 0.06)',
|
||||
outline: '0 0 0 3px rgba(66, 153, 225, 0.5)',
|
||||
none: 'none',
|
||||
},
|
||||
container: {},
|
||||
cursor: {
|
||||
auto: 'auto',
|
||||
default: 'default',
|
||||
pointer: 'pointer',
|
||||
wait: 'wait',
|
||||
text: 'text',
|
||||
move: 'move',
|
||||
'not-allowed': 'not-allowed',
|
||||
},
|
||||
fill: {
|
||||
current: 'currentColor',
|
||||
},
|
||||
flex: {
|
||||
'1': '1 1 0%',
|
||||
auto: '1 1 auto',
|
||||
initial: '0 1 auto',
|
||||
none: 'none',
|
||||
},
|
||||
flexGrow: {
|
||||
'0': '0',
|
||||
default: '1',
|
||||
},
|
||||
flexShrink: {
|
||||
'0': '0',
|
||||
default: '1',
|
||||
},
|
||||
fontFamily: {
|
||||
sans: [
|
||||
'system-ui',
|
||||
'-apple-system',
|
||||
'BlinkMacSystemFont',
|
||||
'"Segoe UI"',
|
||||
'Roboto',
|
||||
'"Helvetica Neue"',
|
||||
'Arial',
|
||||
'"Noto Sans"',
|
||||
'sans-serif',
|
||||
'"Apple Color Emoji"',
|
||||
'"Segoe UI Emoji"',
|
||||
'"Segoe UI Symbol"',
|
||||
'"Noto Color Emoji"',
|
||||
],
|
||||
serif: ['Georgia', 'Cambria', '"Times New Roman"', 'Times', 'serif'],
|
||||
mono: ['Menlo', 'Monaco', 'Consolas', '"Liberation Mono"', '"Courier New"', 'monospace'],
|
||||
},
|
||||
fontSize: {
|
||||
xs: '0.75rem',
|
||||
sm: '0.875rem',
|
||||
base: '1rem',
|
||||
lg: '1.125rem',
|
||||
xl: '1.25rem',
|
||||
'2xl': '1.5rem',
|
||||
'3xl': '1.875rem',
|
||||
'4xl': '2.25rem',
|
||||
'5xl': '3rem',
|
||||
'6xl': '4rem',
|
||||
},
|
||||
fontWeight: {
|
||||
hairline: '100',
|
||||
thin: '200',
|
||||
light: '300',
|
||||
normal: '400',
|
||||
medium: '500',
|
||||
semibold: '600',
|
||||
bold: '700',
|
||||
extrabold: '800',
|
||||
black: '900',
|
||||
},
|
||||
height: theme => ({
|
||||
auto: 'auto',
|
||||
...theme('spacing'),
|
||||
full: '100%',
|
||||
screen: '100vh',
|
||||
}),
|
||||
inset: {
|
||||
'0': '0',
|
||||
auto: 'auto',
|
||||
},
|
||||
letterSpacing: {
|
||||
tighter: '-0.05em',
|
||||
tight: '-0.025em',
|
||||
normal: '0',
|
||||
wide: '0.025em',
|
||||
wider: '0.05em',
|
||||
widest: '0.1em',
|
||||
},
|
||||
lineHeight: {
|
||||
none: '1',
|
||||
tight: '1.25',
|
||||
snug: '1.375',
|
||||
normal: '1.5',
|
||||
relaxed: '1.625',
|
||||
loose: '2',
|
||||
'3': '.75rem',
|
||||
'4': '1rem',
|
||||
'5': '1.25rem',
|
||||
'6': '1.5rem',
|
||||
'7': '1.75rem',
|
||||
'8': '2rem',
|
||||
'9': '2.25rem',
|
||||
'10': '2.5rem',
|
||||
},
|
||||
listStyleType: {
|
||||
none: 'none',
|
||||
disc: 'disc',
|
||||
decimal: 'decimal',
|
||||
},
|
||||
margin: (theme, { negative }) => ({
|
||||
auto: 'auto',
|
||||
...theme('spacing'),
|
||||
...negative(theme('spacing')),
|
||||
}),
|
||||
maxHeight: {
|
||||
full: '100%',
|
||||
screen: '100vh',
|
||||
},
|
||||
maxWidth: (theme, { breakpoints }) => ({
|
||||
none: 'none',
|
||||
xs: '20rem',
|
||||
sm: '24rem',
|
||||
md: '28rem',
|
||||
lg: '32rem',
|
||||
xl: '36rem',
|
||||
'2xl': '42rem',
|
||||
'3xl': '48rem',
|
||||
'4xl': '56rem',
|
||||
'5xl': '64rem',
|
||||
'6xl': '72rem',
|
||||
full: '100%',
|
||||
...breakpoints(theme('screens')),
|
||||
}),
|
||||
minHeight: {
|
||||
'0': '0',
|
||||
full: '100%',
|
||||
screen: '100vh',
|
||||
},
|
||||
minWidth: {
|
||||
'0': '0',
|
||||
full: '100%',
|
||||
},
|
||||
objectPosition: {
|
||||
bottom: 'bottom',
|
||||
center: 'center',
|
||||
left: 'left',
|
||||
'left-bottom': 'left bottom',
|
||||
'left-top': 'left top',
|
||||
right: 'right',
|
||||
'right-bottom': 'right bottom',
|
||||
'right-top': 'right top',
|
||||
top: 'top',
|
||||
},
|
||||
opacity: {
|
||||
'0': '0',
|
||||
'25': '0.25',
|
||||
'50': '0.5',
|
||||
'75': '0.75',
|
||||
'100': '1',
|
||||
},
|
||||
order: {
|
||||
first: '-9999',
|
||||
last: '9999',
|
||||
none: '0',
|
||||
'1': '1',
|
||||
'2': '2',
|
||||
'3': '3',
|
||||
'4': '4',
|
||||
'5': '5',
|
||||
'6': '6',
|
||||
'7': '7',
|
||||
'8': '8',
|
||||
'9': '9',
|
||||
'10': '10',
|
||||
'11': '11',
|
||||
'12': '12',
|
||||
},
|
||||
padding: theme => theme('spacing'),
|
||||
placeholderColor: theme => theme('colors'),
|
||||
stroke: {
|
||||
current: 'currentColor',
|
||||
},
|
||||
strokeWidth: {
|
||||
'0': '0',
|
||||
'1': '1',
|
||||
'2': '2',
|
||||
},
|
||||
textColor: theme => theme('colors'),
|
||||
width: theme => ({
|
||||
auto: 'auto',
|
||||
...theme('spacing'),
|
||||
'1/2': '50%',
|
||||
'1/3': '33.333333%',
|
||||
'2/3': '66.666667%',
|
||||
'1/4': '25%',
|
||||
'2/4': '50%',
|
||||
'3/4': '75%',
|
||||
'1/5': '20%',
|
||||
'2/5': '40%',
|
||||
'3/5': '60%',
|
||||
'4/5': '80%',
|
||||
'1/6': '16.666667%',
|
||||
'2/6': '33.333333%',
|
||||
'3/6': '50%',
|
||||
'4/6': '66.666667%',
|
||||
'5/6': '83.333333%',
|
||||
'1/12': '8.333333%',
|
||||
'2/12': '16.666667%',
|
||||
'3/12': '25%',
|
||||
'4/12': '33.333333%',
|
||||
'5/12': '41.666667%',
|
||||
'6/12': '50%',
|
||||
'7/12': '58.333333%',
|
||||
'8/12': '66.666667%',
|
||||
'9/12': '75%',
|
||||
'10/12': '83.333333%',
|
||||
'11/12': '91.666667%',
|
||||
full: '100%',
|
||||
screen: '100vw',
|
||||
}),
|
||||
zIndex: {
|
||||
auto: 'auto',
|
||||
'0': '0',
|
||||
'10': '10',
|
||||
'20': '20',
|
||||
'30': '30',
|
||||
'40': '40',
|
||||
'50': '50',
|
||||
},
|
||||
gap: theme => theme('spacing'),
|
||||
gridTemplateColumns: {
|
||||
none: 'none',
|
||||
'1': 'repeat(1, minmax(0, 1fr))',
|
||||
'2': 'repeat(2, minmax(0, 1fr))',
|
||||
'3': 'repeat(3, minmax(0, 1fr))',
|
||||
'4': 'repeat(4, minmax(0, 1fr))',
|
||||
'5': 'repeat(5, minmax(0, 1fr))',
|
||||
'6': 'repeat(6, minmax(0, 1fr))',
|
||||
'7': 'repeat(7, minmax(0, 1fr))',
|
||||
'8': 'repeat(8, minmax(0, 1fr))',
|
||||
'9': 'repeat(9, minmax(0, 1fr))',
|
||||
'10': 'repeat(10, minmax(0, 1fr))',
|
||||
'11': 'repeat(11, minmax(0, 1fr))',
|
||||
'12': 'repeat(12, minmax(0, 1fr))',
|
||||
},
|
||||
gridColumn: {
|
||||
auto: 'auto',
|
||||
'span-1': 'span 1 / span 1',
|
||||
'span-2': 'span 2 / span 2',
|
||||
'span-3': 'span 3 / span 3',
|
||||
'span-4': 'span 4 / span 4',
|
||||
'span-5': 'span 5 / span 5',
|
||||
'span-6': 'span 6 / span 6',
|
||||
'span-7': 'span 7 / span 7',
|
||||
'span-8': 'span 8 / span 8',
|
||||
'span-9': 'span 9 / span 9',
|
||||
'span-10': 'span 10 / span 10',
|
||||
'span-11': 'span 11 / span 11',
|
||||
'span-12': 'span 12 / span 12',
|
||||
},
|
||||
gridColumnStart: {
|
||||
auto: 'auto',
|
||||
'1': '1',
|
||||
'2': '2',
|
||||
'3': '3',
|
||||
'4': '4',
|
||||
'5': '5',
|
||||
'6': '6',
|
||||
'7': '7',
|
||||
'8': '8',
|
||||
'9': '9',
|
||||
'10': '10',
|
||||
'11': '11',
|
||||
'12': '12',
|
||||
'13': '13',
|
||||
},
|
||||
gridColumnEnd: {
|
||||
auto: 'auto',
|
||||
'1': '1',
|
||||
'2': '2',
|
||||
'3': '3',
|
||||
'4': '4',
|
||||
'5': '5',
|
||||
'6': '6',
|
||||
'7': '7',
|
||||
'8': '8',
|
||||
'9': '9',
|
||||
'10': '10',
|
||||
'11': '11',
|
||||
'12': '12',
|
||||
'13': '13',
|
||||
},
|
||||
gridTemplateRows: {
|
||||
none: 'none',
|
||||
'1': 'repeat(1, minmax(0, 1fr))',
|
||||
'2': 'repeat(2, minmax(0, 1fr))',
|
||||
'3': 'repeat(3, minmax(0, 1fr))',
|
||||
'4': 'repeat(4, minmax(0, 1fr))',
|
||||
'5': 'repeat(5, minmax(0, 1fr))',
|
||||
'6': 'repeat(6, minmax(0, 1fr))',
|
||||
},
|
||||
gridRow: {
|
||||
auto: 'auto',
|
||||
'span-1': 'span 1 / span 1',
|
||||
'span-2': 'span 2 / span 2',
|
||||
'span-3': 'span 3 / span 3',
|
||||
'span-4': 'span 4 / span 4',
|
||||
'span-5': 'span 5 / span 5',
|
||||
'span-6': 'span 6 / span 6',
|
||||
},
|
||||
gridRowStart: {
|
||||
auto: 'auto',
|
||||
'1': '1',
|
||||
'2': '2',
|
||||
'3': '3',
|
||||
'4': '4',
|
||||
'5': '5',
|
||||
'6': '6',
|
||||
'7': '7',
|
||||
},
|
||||
gridRowEnd: {
|
||||
auto: 'auto',
|
||||
'1': '1',
|
||||
'2': '2',
|
||||
'3': '3',
|
||||
'4': '4',
|
||||
'5': '5',
|
||||
'6': '6',
|
||||
'7': '7',
|
||||
},
|
||||
transformOrigin: {
|
||||
center: 'center',
|
||||
top: 'top',
|
||||
'top-right': 'top right',
|
||||
right: 'right',
|
||||
'bottom-right': 'bottom right',
|
||||
bottom: 'bottom',
|
||||
'bottom-left': 'bottom left',
|
||||
left: 'left',
|
||||
'top-left': 'top left',
|
||||
},
|
||||
scale: {
|
||||
'0': '0',
|
||||
'50': '.5',
|
||||
'75': '.75',
|
||||
'90': '.9',
|
||||
'95': '.95',
|
||||
'100': '1',
|
||||
'105': '1.05',
|
||||
'110': '1.1',
|
||||
'125': '1.25',
|
||||
'150': '1.5',
|
||||
},
|
||||
rotate: {
|
||||
'-180': '-180deg',
|
||||
'-90': '-90deg',
|
||||
'-45': '-45deg',
|
||||
'0': '0',
|
||||
'45': '45deg',
|
||||
'90': '90deg',
|
||||
'180': '180deg',
|
||||
},
|
||||
translate: (theme, { negative }) => ({
|
||||
...theme('spacing'),
|
||||
...negative(theme('spacing')),
|
||||
'-full': '-100%',
|
||||
'-1/2': '-50%',
|
||||
'1/2': '50%',
|
||||
full: '100%',
|
||||
}),
|
||||
skew: {
|
||||
'-12': '-12deg',
|
||||
'-6': '-6deg',
|
||||
'-3': '-3deg',
|
||||
'0': '0',
|
||||
'3': '3deg',
|
||||
'6': '6deg',
|
||||
'12': '12deg',
|
||||
},
|
||||
transitionProperty: {
|
||||
none: 'none',
|
||||
all: 'all',
|
||||
default: 'background-color, border-color, color, fill, stroke, opacity, box-shadow, transform',
|
||||
colors: 'background-color, border-color, color, fill, stroke',
|
||||
opacity: 'opacity',
|
||||
shadow: 'box-shadow',
|
||||
transform: 'transform',
|
||||
},
|
||||
transitionTimingFunction: {
|
||||
linear: 'linear',
|
||||
in: 'cubic-bezier(0.4, 0, 1, 1)',
|
||||
out: 'cubic-bezier(0, 0, 0.2, 1)',
|
||||
'in-out': 'cubic-bezier(0.4, 0, 0.2, 1)',
|
||||
},
|
||||
transitionDuration: {
|
||||
'75': '75ms',
|
||||
'100': '100ms',
|
||||
'150': '150ms',
|
||||
'200': '200ms',
|
||||
'300': '300ms',
|
||||
'500': '500ms',
|
||||
'700': '700ms',
|
||||
'1000': '1000ms',
|
||||
},
|
||||
},
|
||||
variants: {
|
||||
accessibility: ['responsive', 'focus'],
|
||||
alignContent: ['responsive'],
|
||||
alignItems: ['responsive'],
|
||||
alignSelf: ['responsive'],
|
||||
appearance: ['responsive'],
|
||||
backgroundAttachment: ['responsive'],
|
||||
backgroundColor: ['responsive', 'hover', 'focus'],
|
||||
backgroundPosition: ['responsive'],
|
||||
backgroundRepeat: ['responsive'],
|
||||
backgroundSize: ['responsive'],
|
||||
borderCollapse: ['responsive'],
|
||||
borderColor: ['responsive', 'hover', 'focus'],
|
||||
borderRadius: ['responsive'],
|
||||
borderStyle: ['responsive'],
|
||||
borderWidth: ['responsive'],
|
||||
boxShadow: ['responsive', 'hover', 'focus'],
|
||||
boxSizing: ['responsive'],
|
||||
cursor: ['responsive'],
|
||||
display: ['responsive'],
|
||||
fill: ['responsive'],
|
||||
flex: ['responsive'],
|
||||
flexDirection: ['responsive'],
|
||||
flexGrow: ['responsive'],
|
||||
flexShrink: ['responsive'],
|
||||
flexWrap: ['responsive'],
|
||||
float: ['responsive'],
|
||||
clear: ['responsive'],
|
||||
fontFamily: ['responsive'],
|
||||
fontSize: ['responsive'],
|
||||
fontSmoothing: ['responsive'],
|
||||
fontStyle: ['responsive'],
|
||||
fontWeight: ['responsive', 'hover', 'focus'],
|
||||
height: ['responsive'],
|
||||
inset: ['responsive'],
|
||||
justifyContent: ['responsive'],
|
||||
letterSpacing: ['responsive'],
|
||||
lineHeight: ['responsive'],
|
||||
listStylePosition: ['responsive'],
|
||||
listStyleType: ['responsive'],
|
||||
margin: ['responsive'],
|
||||
maxHeight: ['responsive'],
|
||||
maxWidth: ['responsive'],
|
||||
minHeight: ['responsive'],
|
||||
minWidth: ['responsive'],
|
||||
objectFit: ['responsive'],
|
||||
objectPosition: ['responsive'],
|
||||
opacity: ['responsive', 'hover', 'focus'],
|
||||
order: ['responsive'],
|
||||
outline: ['responsive', 'focus'],
|
||||
overflow: ['responsive'],
|
||||
padding: ['responsive'],
|
||||
placeholderColor: ['responsive', 'focus'],
|
||||
pointerEvents: ['responsive'],
|
||||
position: ['responsive'],
|
||||
resize: ['responsive'],
|
||||
stroke: ['responsive'],
|
||||
strokeWidth: ['responsive'],
|
||||
tableLayout: ['responsive'],
|
||||
textAlign: ['responsive'],
|
||||
textColor: ['responsive', 'hover', 'focus'],
|
||||
textDecoration: ['responsive', 'hover', 'focus'],
|
||||
textTransform: ['responsive'],
|
||||
userSelect: ['responsive'],
|
||||
verticalAlign: ['responsive'],
|
||||
visibility: ['responsive'],
|
||||
whitespace: ['responsive'],
|
||||
width: ['responsive'],
|
||||
wordBreak: ['responsive'],
|
||||
zIndex: ['responsive'],
|
||||
gap: ['responsive'],
|
||||
gridAutoFlow: ['responsive'],
|
||||
gridTemplateColumns: ['responsive'],
|
||||
gridColumn: ['responsive'],
|
||||
gridColumnStart: ['responsive'],
|
||||
gridColumnEnd: ['responsive'],
|
||||
gridTemplateRows: ['responsive'],
|
||||
gridRow: ['responsive'],
|
||||
gridRowStart: ['responsive'],
|
||||
gridRowEnd: ['responsive'],
|
||||
transform: ['responsive'],
|
||||
transformOrigin: ['responsive'],
|
||||
scale: ['responsive', 'hover', 'focus'],
|
||||
rotate: ['responsive', 'hover', 'focus'],
|
||||
translate: ['responsive', 'hover', 'focus'],
|
||||
skew: ['responsive', 'hover', 'focus'],
|
||||
transitionProperty: ['responsive'],
|
||||
transitionTimingFunction: ['responsive'],
|
||||
transitionDuration: ['responsive'],
|
||||
},
|
||||
corePlugins: {},
|
||||
plugins: [],
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
@react.component
|
||||
let make = (
|
||||
~disabled: bool=?,
|
||||
~ghost: bool=?,
|
||||
~href: string=?,
|
||||
~htmlType: @string [#button | #submit | #submit]=?,
|
||||
~icon: 'a=?,
|
||||
~shape: @string [#circle | #round]=?,
|
||||
~size: @string [#small | #large]=?,
|
||||
~target: string=?,
|
||||
~loading: bool=?,
|
||||
~_type: @string
|
||||
[
|
||||
| #primary
|
||||
| #default
|
||||
| #dashed
|
||||
| #danger
|
||||
| #link
|
||||
| #ghost
|
||||
]=?,
|
||||
~onClick: ReactEvent.Mouse.t => unit=?,
|
||||
~block: bool=?,
|
||||
~children: React.element=?,
|
||||
~className: string=?,
|
||||
~id: string=?,
|
||||
~testId: string=?,
|
||||
) =>
|
||||
ReasonReact.cloneElement(
|
||||
<AntButton
|
||||
_type
|
||||
disabled
|
||||
ghost
|
||||
href
|
||||
htmlType
|
||||
icon={Antd_Utils.tts(Antd_Icon.iconToJsSafe(~icon, ()))}
|
||||
shape
|
||||
size
|
||||
target
|
||||
onClick
|
||||
block
|
||||
loading
|
||||
className
|
||||
id>
|
||||
children
|
||||
</AntButton>,
|
||||
~props={"data-testid": testId},
|
||||
[],
|
||||
)
|
19
packages/playground/tsconfig.json
Normal file
19
packages/playground/tsconfig.json
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "@emotion/react",
|
||||
"noImplicitAny": false,
|
||||
"removeComments": true,
|
||||
"preserveConstEnums": true,
|
||||
"esModuleInterop": true,
|
||||
"resolveJsonModule": true,
|
||||
"outDir": "./dist",
|
||||
"declarationDir": "./dist",
|
||||
"declaration": true,
|
||||
"sourceMap": true
|
||||
},
|
||||
"target": "ES6",
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "**/*.spec.ts"]
|
||||
}
|
1
packages/squiggle-lang/.gitignore
vendored
1
packages/squiggle-lang/.gitignore
vendored
|
@ -15,3 +15,4 @@ yarn-error.log
|
|||
.idea
|
||||
*.gen.ts
|
||||
*.gen.js
|
||||
dist
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
"name": "@squiggle/lang",
|
||||
"version": "0.1.9",
|
||||
"homepage": "https://foretold-app.github.io/estiband/",
|
||||
"private": false,
|
||||
"scripts": {
|
||||
"build": "rescript build -with-deps",
|
||||
"parcel": "parcel build ./src/js/index.js --no-source-maps --no-autoinstall",
|
||||
|
@ -12,7 +11,8 @@
|
|||
"test:ci": "yarn jest ./__tests__/Lodash__test.re",
|
||||
"watch:test": "jest --watchAll",
|
||||
"watch:s": "yarn jest -- Converter_test --watch",
|
||||
"package": "tsc"
|
||||
"package": "tsc",
|
||||
"ci": "yarn build && yarn package"
|
||||
},
|
||||
"keywords": [
|
||||
"Rescript"
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user