Playground to TypeScript

This commit is contained in:
Sam Nolan 2022-03-03 10:22:26 +11:00
parent 73114c6a47
commit c8bb0c2060
64 changed files with 716 additions and 124755 deletions

1
.gitignore vendored
View File

@ -1,4 +1,5 @@
node_modules node_modules
yarn-error.log
.cache .cache
.merlin .merlin
.parcel-cache .parcel-cache

Binary file not shown.

Before

Width:  |  Height:  |  Size: 347 KiB

View File

@ -1,8 +0,0 @@
{
"packages": [
"packages/*"
],
"npmClient": "yarn",
"useWorkspaces": true,
"version": "0.0.0"
}

View File

@ -1,14 +1,21 @@
{ {
"private": true, "private": true,
"devDependencies": {
"lerna": "^4.0.0"
},
"name": "squiggle", "name": "squiggle",
"scripts": { "scripts": {
"build:lang": "cd packages/squiggle-lang && yarn && yarn build && yarn package", "build:lang": "cd packages/squiggle-lang && yarn && yarn build && yarn package",
"storybook:components": "cd packages/components && yarn && yarn storybook", "storybook:components": "cd packages/components && yarn && yarn storybook",
"build-storybook:components": "cd packages/components && yarn && yarn build-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"
} }

View File

@ -3,7 +3,7 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@squiggle/lang": "^0.1.9", "@squiggle/lang": "0.1.9",
"@testing-library/jest-dom": "^5.16.2", "@testing-library/jest-dom": "^5.16.2",
"@testing-library/react": "^12.1.2", "@testing-library/react": "^12.1.2",
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
@ -20,14 +20,17 @@
"react-vega": "^7.4.4", "react-vega": "^7.4.4",
"tsconfig-paths-webpack-plugin": "^3.5.2", "tsconfig-paths-webpack-plugin": "^3.5.2",
"typescript": "^4.5.5", "typescript": "^4.5.5",
"vega": "^5.21.0",
"vega-embed": "^6.20.6", "vega-embed": "^6.20.6",
"vega-lite": "^5.2.0",
"web-vitals": "^2.1.4", "web-vitals": "^2.1.4",
"webpack-cli": "^4.9.2" "webpack-cli": "^4.9.2"
}, },
"scripts": { "scripts": {
"storybook": "cross-env REACT_APP_FAST_REFRESH=false && start-storybook -p 6006 -s public", "storybook": "cross-env REACT_APP_FAST_REFRESH=false && start-storybook -p 6006 -s public",
"build-storybook": "build-storybook -s public", "build-storybook": "build-storybook -s public",
"package": "tsc" "package": "tsc",
"ci": "yarn package"
}, },
"eslintConfig": { "eslintConfig": {
"extends": [ "extends": [
@ -68,6 +71,9 @@
"@storybook/react": "^6.4.18", "@storybook/react": "^6.4.18",
"webpack": "^5.68.0" "webpack": "^5.68.0"
}, },
"main": "dist/lib.js", "resolutions": {
"types": "dist/lib.d.ts" "@types/react": "17.0.39"
},
"main": "dist/index.js",
"types": "dist/index.d.ts"
} }

View File

@ -3,6 +3,7 @@
"module": "commonjs", "module": "commonjs",
"jsx": "react", "jsx": "react",
"noImplicitAny": false, "noImplicitAny": false,
"esModuleInterop": true,
"removeComments": true, "removeComments": true,
"preserveConstEnums": true, "preserveConstEnums": true,
"resolveJsonModule": true, "resolveJsonModule": true,

File diff suppressed because it is too large Load Diff

View File

@ -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.

View File

@ -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 It depends on `@squiggle/components` and `@squiggle/lang` so both of them will
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. need to be packaged for this to work. This can be done from the root directory
with
## Running
Currently it only has a few very simple models.
``` ```
yarn yarn build:lang
yarn run start yarn build:components
yarn run parcel
``` ```
## Expected future setup Then, starting the playground can be done with:
![setup](https://raw.githubusercontent.com/foretold-app/widedomain/master/Screen%20Shot%202020-06-30%20at%208.27.32%20AM.png)
```
yarn parcel
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 347 KiB

View File

@ -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"
]
}

View File

@ -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).

View File

@ -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>

View File

@ -1,5 +0,0 @@
{
"devDependencies": {
"docsify-cli": "^4.4.3"
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
[[redirects]] [[redirects]]
from = "/*" from = "/*"
to = "/index.html" to = "/index.html"
status = 200 status = 200

View File

@ -3,48 +3,23 @@
"version": "0.1.0", "version": "0.1.0",
"homepage": "https://foretold-app.github.io/estiband/", "homepage": "https://foretold-app.github.io/estiband/",
"scripts": { "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": "parcel ./src/index.html",
"parcel-build": "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",
"showcase": "PORT=12345 parcel showcase/index.html",
"server": "moduleserve ./ --port 8000",
"predeploy": "parcel build ./src/index.html --no-source-maps --no-autoinstall",
"deploy": "gh-pages -d dist", "deploy": "gh-pages -d dist",
"test": "jest", "ci": "yarn parcel-build"
"test:ci": "yarn jest",
"watch:test": "jest --watchAll",
"watch:s": "yarn jest -- Converter_test --watch"
}, },
"keywords": [ "keywords": [],
"BuckleScript",
"ReasonReact",
"reason-react"
],
"author": "", "author": "",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@glennsl/bs-json": "^5.0.2", "@emotion/react": "^11.8.1",
"@rescript/react": "^0.10.3",
"@rescriptbr/reform": "^11.0.1",
"@squiggle/lang": "^0.1.9", "@squiggle/lang": "^0.1.9",
"ace-builds": "^1.4.12", "ace-builds": "^1.4.12",
"antd": "^4.18.5", "antd": "^4.18.5",
"autoprefixer": "9.8.8",
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.2", "babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
"binary-search-tree": "0.2.6", "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", "css-loader": "^6.6.0",
"d3": "7.3.0",
"gh-pages": "2.2.0", "gh-pages": "2.2.0",
"jest": "^25.5.1",
"jstat": "1.9.2", "jstat": "1.9.2",
"lenses-ppx": "5.1.0", "lenses-ppx": "5.1.0",
"less": "3.10.3", "less": "3.10.3",
@ -53,27 +28,27 @@
"moduleserve": "0.9.1", "moduleserve": "0.9.1",
"moment": "2.24.0", "moment": "2.24.0",
"pdfast": "^0.2.0", "pdfast": "^0.2.0",
"postcss": "^8.4.7",
"postcss-cli": "^9.1.0",
"rationale": "0.2.0", "rationale": "0.2.0",
"react": "17.0.2", "react": "17.0.2",
"react-ace": "^9.2.0", "react-ace": "^9.2.0",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-use": "^17.3.2", "react-use": "^17.3.2",
"react-vega": "^7.4.4", "react-vega": "^7.4.4",
"reschema": "^2.2.0",
"rescript": "^9.1.4",
"tailwindcss": "1.2.0",
"vega": "*", "vega": "*",
"vega-embed": "6.6.0", "vega-embed": "6.6.0",
"vega-lite": "*" "vega-lite": "*"
}, },
"devDependencies": { "devDependencies": {
"@glennsl/bs-jest": "^0.5.1", "@emotion/babel-plugin": "^11.7.2",
"bs-platform": "8.4.2", "@parcel/core": "^2.3.2",
"@types/react": "^17.0.39",
"autoprefixer": "^10.4.2",
"docsify": "^4.12.2", "docsify": "^4.12.2",
"jest": "^27.5.1",
"parcel": "^2.3.2", "parcel": "^2.3.2",
"parcel-plugin-bundle-visualiser": "^1.2.0", "postcss": "^8.4.7",
"parcel-plugin-less-js-enabled": "1.0.2" "postcss-cli": "^9.1.0",
"tailwindcss": "^3.0.23",
"typescript": "^4.6.2"
} }
} }

View File

@ -1,8 +1,6 @@
//postcss.config.js module.exports = {
const tailwindcss = require('tailwindcss'); plugins: {
module.exports = { tailwindcss: {},
plugins: [ autoprefixer: {},
tailwindcss('./tailwind.js'), },
require('autoprefixer'), }
],
};

View File

@ -1 +0,0 @@
let entries = [];

View File

@ -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});
};

View File

@ -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>
}
}

View File

@ -1,5 +0,0 @@
switch(ReactDOM.querySelector("main")){
| Some(root) => ReactDOM.render(<div> <Lib.Index /> </div>, root)
| None => () // do nothing
}
RescriptReactRouter.push("")

View File

@ -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>

View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -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"
}

View File

@ -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"
}

View File

@ -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"

View File

@ -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

View File

@ -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)
}}
</>
}

View File

@ -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
}

View File

@ -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};

View File

@ -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};

View File

@ -1,5 +0,0 @@
%raw(`import('./styles/index.css')`)
switch ReactDOM.querySelector("#app") {
| Some(root) => ReactDOM.render(<App />, root)
| None => ()
}

View 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)
}

View File

@ -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)
}

View File

@ -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"

View File

@ -1,4 +1,4 @@
import React from "react"; import React, {FC} from "react";
import AceEditor from "react-ace"; import AceEditor from "react-ace";
import "ace-builds/src-noconflict/mode-golang"; 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/ext-language_tools";
import "ace-builds/src-noconflict/keybinding-vim"; import "ace-builds/src-noconflict/keybinding-vim";
function onChange(newValue) { interface CodeEditorProps {
console.log("change", newValue); value : string,
onChange : (value: string) => void
} }
export function CodeEditor(props) { export let CodeEditor : FC<CodeEditorProps> = (props) =>
return (
<AceEditor <AceEditor
value={props.value} value={props.value}
mode="golang" mode="golang"
@ -32,5 +32,3 @@ export function CodeEditor(props) {
enableSnippets: true, enableSnippets: true,
}} }}
/> />
);
}

View File

@ -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>
}

View 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

View File

@ -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>
}

View File

@ -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},
],
};

View File

@ -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>

View File

@ -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 };

View File

@ -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>
}

View File

@ -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);
};

View File

@ -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;

View File

@ -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" }
}
}
}
]
}

View File

@ -1,10 +0,0 @@
.lollipops-line-mouseover {
stroke-dasharray: 4;
animation: dash 2s linear infinite;
}
@keyframes dash {
to {
stroke-dashoffset: 1000;
}
}

View File

@ -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>
}

View File

@ -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();
}

View File

@ -5,9 +5,9 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Squiggle Language</title> <title>Squiggle Language</title>
<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="./styles/index.css" rel="stylesheet">
<link href="./styles/antd.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> </head>
<body> <body>

View File

@ -1,5 +1,3 @@
@tailwind base; @tailwind base;
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,9 @@
module.exports = {
content: [
"./src/components/*.tsx"
],
theme: {
extend: {},
},
plugins: [],
}

View File

@ -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: [],
}

View File

@ -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},
[],
)

View 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"]
}

View File

@ -15,3 +15,4 @@ yarn-error.log
.idea .idea
*.gen.ts *.gen.ts
*.gen.js *.gen.js
dist

View File

@ -2,7 +2,6 @@
"name": "@squiggle/lang", "name": "@squiggle/lang",
"version": "0.1.9", "version": "0.1.9",
"homepage": "https://foretold-app.github.io/estiband/", "homepage": "https://foretold-app.github.io/estiband/",
"private": false,
"scripts": { "scripts": {
"build": "rescript build -with-deps", "build": "rescript build -with-deps",
"parcel": "parcel build ./src/js/index.js --no-source-maps --no-autoinstall", "parcel": "parcel build ./src/js/index.js --no-source-maps --no-autoinstall",
@ -12,7 +11,8 @@
"test:ci": "yarn jest ./__tests__/Lodash__test.re", "test:ci": "yarn jest ./__tests__/Lodash__test.re",
"watch:test": "jest --watchAll", "watch:test": "jest --watchAll",
"watch:s": "yarn jest -- Converter_test --watch", "watch:s": "yarn jest -- Converter_test --watch",
"package": "tsc" "package": "tsc",
"ci": "yarn build && yarn package"
}, },
"keywords": [ "keywords": [
"Rescript" "Rescript"

File diff suppressed because it is too large Load Diff

5298
yarn.lock

File diff suppressed because it is too large Load Diff