Change build system to Rescript
This commit is contained in:
		
							parent
							
								
									4db127086e
								
							
						
					
					
						commit
						01990dbe9f
					
				
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -13,4 +13,4 @@ yarn-error.log
 | 
			
		|||
*.bs.js
 | 
			
		||||
# Local Netlify folder
 | 
			
		||||
.netlify
 | 
			
		||||
.idea
 | 
			
		||||
.idea
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,7 +45,10 @@
 | 
			
		|||
    "reschema"
 | 
			
		||||
  ],
 | 
			
		||||
  "refmt": 3,
 | 
			
		||||
  "warnings": {
 | 
			
		||||
    "number": "+A-42-48-9-30-4-102"
 | 
			
		||||
  },
 | 
			
		||||
  "ppx-flags": [
 | 
			
		||||
    "lenses-ppx/ppx"
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										16
									
								
								foretold/components/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								foretold/components/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,16 @@
 | 
			
		|||
.DS_Store
 | 
			
		||||
.merlin
 | 
			
		||||
.bsb.lock
 | 
			
		||||
npm-debug.log
 | 
			
		||||
/node_modules/
 | 
			
		||||
.cache
 | 
			
		||||
.cache/*
 | 
			
		||||
dist
 | 
			
		||||
lib/*
 | 
			
		||||
*.cache
 | 
			
		||||
build
 | 
			
		||||
yarn-error.log
 | 
			
		||||
*.bs.js
 | 
			
		||||
# Local Netlify folder
 | 
			
		||||
.netlify
 | 
			
		||||
.idea
 | 
			
		||||
							
								
								
									
										15
									
								
								foretold/components/.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								foretold/components/.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,15 @@
 | 
			
		|||
{
 | 
			
		||||
    "spellright.language": [
 | 
			
		||||
        "en"
 | 
			
		||||
    ],
 | 
			
		||||
    "spellright.documentTypes": [
 | 
			
		||||
        "markdown",
 | 
			
		||||
        "latex",
 | 
			
		||||
        "plaintext"
 | 
			
		||||
    ],
 | 
			
		||||
    "spellright.parserByClass": {
 | 
			
		||||
        "reason": {
 | 
			
		||||
            "parser": "plain"
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										29
									
								
								foretold/components/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								foretold/components/README.md
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,29 @@
 | 
			
		|||
# Components
 | 
			
		||||
 | 
			
		||||
This repo has many of the relevant Foretold Components.
 | 
			
		||||
 | 
			
		||||
There's an example page in the `example/App.re` file. To run this, do:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
yarn dev
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
in a separate terminal
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
yarn dev
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Showcase component viewer
 | 
			
		||||
 | 
			
		||||
To open the component viewer, run:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
yarn showcase
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Showcase should then be available on `http://localhost:1234/showcase/index.html`, 
 | 
			
		||||
while the example page is available on `http://localhost:1234/index.html` 
 | 
			
		||||
(given no port conflict).
 | 
			
		||||
 | 
			
		||||
Components included are specified through `showcase/Entries.re`.
 | 
			
		||||
							
								
								
									
										37
									
								
								foretold/components/bsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								foretold/components/bsconfig.json
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,37 @@
 | 
			
		|||
{
 | 
			
		||||
  "name": "@foretold/components",
 | 
			
		||||
  "reason": {
 | 
			
		||||
    "react-jsx": 3
 | 
			
		||||
  },
 | 
			
		||||
  "refmt": 3,
 | 
			
		||||
  "bs-dependencies": [
 | 
			
		||||
    "bs-css",
 | 
			
		||||
    "bs-moment",
 | 
			
		||||
    "reason-react",
 | 
			
		||||
    "rationale"
 | 
			
		||||
  ],
 | 
			
		||||
  "warnings": {
 | 
			
		||||
    "error": "+5"
 | 
			
		||||
  },
 | 
			
		||||
  "suffix": ".bs.js",
 | 
			
		||||
  "package-specs": [{
 | 
			
		||||
    "module": "commonjs",
 | 
			
		||||
    "in-source": true
 | 
			
		||||
  }],
 | 
			
		||||
  "namespace": true,
 | 
			
		||||
  "sources": [{
 | 
			
		||||
      "dir": "src",
 | 
			
		||||
      "subdirs": true
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "dir": "example",
 | 
			
		||||
      "type": "dev",
 | 
			
		||||
      "subdirs": true
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "dir": "showcase",
 | 
			
		||||
      "type": "dev",
 | 
			
		||||
      "subdirs": true
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								foretold/components/example/App.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								foretold/components/example/App.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
FC.Base.Globals.load();
 | 
			
		||||
ReactDOMRe.renderToElementWithId(
 | 
			
		||||
  <div className=Css.(style([fontFamily(Settings.Text.standardFont)]))>
 | 
			
		||||
    ExampleFullPage.make
 | 
			
		||||
  </div>,
 | 
			
		||||
  "app",
 | 
			
		||||
);
 | 
			
		||||
							
								
								
									
										124
									
								
								foretold/components/example/Example/ExampleFullPage.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								foretold/components/example/Example/ExampleFullPage.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,124 @@
 | 
			
		|||
open Base;
 | 
			
		||||
open FC;
 | 
			
		||||
let make =
 | 
			
		||||
  <Div>
 | 
			
		||||
    <AppHeader
 | 
			
		||||
      links={
 | 
			
		||||
        [|
 | 
			
		||||
          <Link
 | 
			
		||||
            isDisabled=false
 | 
			
		||||
            className=Css.(
 | 
			
		||||
              style([
 | 
			
		||||
                marginRight(`em(2.)),
 | 
			
		||||
                color(Settings.Text.LightBackground.main),
 | 
			
		||||
                hover([color(Settings.Text.LightBackground.main)]),
 | 
			
		||||
              ])
 | 
			
		||||
            )
 | 
			
		||||
            href="#">
 | 
			
		||||
            {"Public Groups" |> ReasonReact.string}
 | 
			
		||||
          </Link>,
 | 
			
		||||
          <Link
 | 
			
		||||
            isDisabled=false
 | 
			
		||||
            className=Css.(
 | 
			
		||||
              style([
 | 
			
		||||
                marginRight(`em(2.)),
 | 
			
		||||
                color(Settings.Text.LightBackground.main),
 | 
			
		||||
                hover([color(Settings.Text.LightBackground.main)]),
 | 
			
		||||
              ])
 | 
			
		||||
            )
 | 
			
		||||
            href="#">
 | 
			
		||||
            {"Entity Explorer" |> ReasonReact.string}
 | 
			
		||||
          </Link>,
 | 
			
		||||
        |]
 | 
			
		||||
        |> ReasonReact.array
 | 
			
		||||
      }
 | 
			
		||||
    />
 | 
			
		||||
    Example__AppGroupHeader.make
 | 
			
		||||
    <Div
 | 
			
		||||
      styles=[
 | 
			
		||||
        Css.(
 | 
			
		||||
          style(
 | 
			
		||||
            [
 | 
			
		||||
              marginTop(`em(1.)),
 | 
			
		||||
              width(`percent(100.)),
 | 
			
		||||
              paddingLeft(`em(2.)),
 | 
			
		||||
              paddingRight(`em(2.)),
 | 
			
		||||
            ]
 | 
			
		||||
            @ BaseStyles.fullWidthFloatLeft,
 | 
			
		||||
          )
 | 
			
		||||
        ),
 | 
			
		||||
      ]>
 | 
			
		||||
      Example__MeasurableIndexPage.make
 | 
			
		||||
    </Div>
 | 
			
		||||
    <Div
 | 
			
		||||
      styles=[
 | 
			
		||||
        Css.(
 | 
			
		||||
          style(
 | 
			
		||||
            [
 | 
			
		||||
              marginTop(`em(1.)),
 | 
			
		||||
              width(`percent(100.)),
 | 
			
		||||
              paddingLeft(`em(2.)),
 | 
			
		||||
              paddingRight(`em(2.)),
 | 
			
		||||
              boxSizing(`borderBox),
 | 
			
		||||
            ]
 | 
			
		||||
            @ BaseStyles.fullWidthFloatLeft,
 | 
			
		||||
          )
 | 
			
		||||
        ),
 | 
			
		||||
      ]>
 | 
			
		||||
      <Div flexDirection=`row>
 | 
			
		||||
        <Div
 | 
			
		||||
          flex={`num(5.0)} styles=[Css.(style([paddingRight(`em(2.0))]))]>
 | 
			
		||||
          <Div> Example__MeasurableTopCard.make </Div>
 | 
			
		||||
          <Div
 | 
			
		||||
            styles=[
 | 
			
		||||
              Css.(
 | 
			
		||||
                style(
 | 
			
		||||
                  [marginTop(`em(1.0))] @ BaseStyles.fullWidthFloatLeft,
 | 
			
		||||
                )
 | 
			
		||||
              ),
 | 
			
		||||
            ]>
 | 
			
		||||
            Example__CardMeasurableMeasurements.make
 | 
			
		||||
          </Div>
 | 
			
		||||
        </Div>
 | 
			
		||||
        <Div flex={`num(2.0)}>
 | 
			
		||||
          Example__MeasurableTopCard.make
 | 
			
		||||
          <Div styles=[Css.(style([clear(`both), paddingTop(`em(1.0))]))]>
 | 
			
		||||
            <MeasurableForm cdf=ExampleCdfs.Example1.cdf />
 | 
			
		||||
          </Div>
 | 
			
		||||
        </Div>
 | 
			
		||||
      </Div>
 | 
			
		||||
    </Div>
 | 
			
		||||
    <Div
 | 
			
		||||
      styles=[
 | 
			
		||||
        Css.(
 | 
			
		||||
          style(
 | 
			
		||||
            [
 | 
			
		||||
              marginTop(`em(2.)),
 | 
			
		||||
              width(`percent(100.)),
 | 
			
		||||
              paddingLeft(`em(2.)),
 | 
			
		||||
              paddingRight(`em(2.)),
 | 
			
		||||
              boxSizing(`borderBox),
 | 
			
		||||
            ]
 | 
			
		||||
            @ BaseStyles.fullWidthFloatLeft,
 | 
			
		||||
          )
 | 
			
		||||
        ),
 | 
			
		||||
      ]>
 | 
			
		||||
      <Div flexDirection=`row>
 | 
			
		||||
        <Div
 | 
			
		||||
          flex={`num(5.0)} styles=[Css.(style([paddingRight(`em(2.0))]))]>
 | 
			
		||||
          <Div> Example__MemberTableCard.make </Div>
 | 
			
		||||
        </Div>
 | 
			
		||||
        <Div flex={`num(2.0)} />
 | 
			
		||||
      </Div>
 | 
			
		||||
    </Div>
 | 
			
		||||
    <Footer
 | 
			
		||||
      logo={React.string({js|2019 \u00a9 Foretold|js})}
 | 
			
		||||
      links=[|
 | 
			
		||||
        <a href="#"> {React.string("About")} </a>,
 | 
			
		||||
        <a href="#"> {React.string("Help")} </a>,
 | 
			
		||||
        <a href="#"> {React.string("Documentation")} </a>,
 | 
			
		||||
        <a href="#"> {React.string("Privacy Policy")} </a>,
 | 
			
		||||
        <a href="#"> {React.string("Terms of Service")} </a>,
 | 
			
		||||
      |]
 | 
			
		||||
    />
 | 
			
		||||
  </Div>;
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,59 @@
 | 
			
		|||
open FC;
 | 
			
		||||
open Base;
 | 
			
		||||
 | 
			
		||||
let make =
 | 
			
		||||
  <Div>
 | 
			
		||||
    <GroupHeader>
 | 
			
		||||
      <Div float=`left>
 | 
			
		||||
        <div
 | 
			
		||||
          className=Css.(
 | 
			
		||||
            style([
 | 
			
		||||
              fontSize(`em(1.4)),
 | 
			
		||||
              marginTop(`px(5)),
 | 
			
		||||
              Colors.FontWeights.heavy,
 | 
			
		||||
              color(Colors.darkLink),
 | 
			
		||||
            ])
 | 
			
		||||
          )>
 | 
			
		||||
          {"Great Community" |> ReasonReact.string}
 | 
			
		||||
        </div>
 | 
			
		||||
        <p
 | 
			
		||||
          className=Css.(
 | 
			
		||||
            style([
 | 
			
		||||
              color(`hex("36485c")),
 | 
			
		||||
              marginTop(`px(5)),
 | 
			
		||||
              marginBottom(`px(3)),
 | 
			
		||||
            ])
 | 
			
		||||
          )>
 | 
			
		||||
          {"Lots of description for the group would be here"
 | 
			
		||||
           |> ReasonReact.string}
 | 
			
		||||
        </p>
 | 
			
		||||
      </Div>
 | 
			
		||||
      <Div float=`right>
 | 
			
		||||
        <Button
 | 
			
		||||
          variant=Button.Secondary
 | 
			
		||||
          isDisabled=false
 | 
			
		||||
          size=Button.(Medium)
 | 
			
		||||
          className=GroupHeader.Styles.actionButtonPosition>
 | 
			
		||||
          {"Leave Channel" |> ReasonReact.string}
 | 
			
		||||
        </Button>
 | 
			
		||||
        <Button
 | 
			
		||||
          isDisabled=false
 | 
			
		||||
          size=Button.(Medium)
 | 
			
		||||
          className=GroupHeader.Styles.actionButtonPosition>
 | 
			
		||||
          {"Create Question" |> ReasonReact.string}
 | 
			
		||||
        </Button>
 | 
			
		||||
        <Button
 | 
			
		||||
          isDisabled=false
 | 
			
		||||
          size=Button.(Medium)
 | 
			
		||||
          className=GroupHeader.Styles.actionButtonPosition>
 | 
			
		||||
          {"Create Question" |> ReasonReact.string}
 | 
			
		||||
        </Button>
 | 
			
		||||
      </Div>
 | 
			
		||||
    </GroupHeader>
 | 
			
		||||
    <GroupHeader.SubHeader>
 | 
			
		||||
      <Tab isActive=true> {"Questions" |> ReasonReact.string} </Tab>
 | 
			
		||||
      <Tab isActive=false> {"Knowledge Graph" |> ReasonReact.string} </Tab>
 | 
			
		||||
      <Tab isActive=false> {"Leaderboard" |> ReasonReact.string} </Tab>
 | 
			
		||||
      <Tab isActive=false> {"Settings" |> ReasonReact.string} </Tab>
 | 
			
		||||
    </GroupHeader.SubHeader>
 | 
			
		||||
  </Div>;
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,143 @@
 | 
			
		|||
open FC;
 | 
			
		||||
open Base;
 | 
			
		||||
 | 
			
		||||
let pastTime = 1483232400;
 | 
			
		||||
let format_standard = "LLL";
 | 
			
		||||
 | 
			
		||||
let cdf = ExampleCdfs.Example1.cdf;
 | 
			
		||||
 | 
			
		||||
let cellStyle =
 | 
			
		||||
  Css.(style([paddingTop(`em(0.7)), paddingBottom(`em(0.4))]));
 | 
			
		||||
 | 
			
		||||
let row =
 | 
			
		||||
  <Table.Row
 | 
			
		||||
    bottomSubRow={Table.Row.textSection(
 | 
			
		||||
      "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut vulputate tortor a sapien aliquet ullamcorper. Nunc non varius sapien, quis elementum sapien. Morbi ac tristique quam. Cras hendrerit accumsan pretium. Praesent id nisl sit amet eros imperdiet placerat. Vestibulum sodales posuere diam vel laoreet."
 | 
			
		||||
      |> ReasonReact.string,
 | 
			
		||||
    )}>
 | 
			
		||||
    <Table.Cell flex={`num(1.0)} className=cellStyle>
 | 
			
		||||
      <AgentLink
 | 
			
		||||
        agent={AgentLink.Agent.makeUser(
 | 
			
		||||
          ~name="Roger Adams",
 | 
			
		||||
          ~image=
 | 
			
		||||
            "https://lh3.googleusercontent.com/-1sj3EqkojJ4/AAAAAAAAAAI/AAAAAAAAAAA/ACHi3rfWCVqnuJxxM41Zird4HZx0BbRpbQ/photo.jpg",
 | 
			
		||||
          (),
 | 
			
		||||
        )}
 | 
			
		||||
      />
 | 
			
		||||
    </Table.Cell>
 | 
			
		||||
    <Table.Cell
 | 
			
		||||
      flex={`num(2.0)}
 | 
			
		||||
      className=Css.(
 | 
			
		||||
        style([paddingTop(`em(0.6)), paddingBottom(`em(0.0))])
 | 
			
		||||
      )>
 | 
			
		||||
      <CdfChart__Plain cdf minX=2.0 color={`hex("#d9dcdf")} maxX=12.0 />
 | 
			
		||||
    </Table.Cell>
 | 
			
		||||
    <Table.Cell
 | 
			
		||||
      flex={`num(1.0)}
 | 
			
		||||
      className=Css.(
 | 
			
		||||
        style([paddingTop(`em(0.2)), paddingBottom(`em(0.3))])
 | 
			
		||||
      )>
 | 
			
		||||
      <CdfChart__StatSummary cdf />
 | 
			
		||||
    </Table.Cell>
 | 
			
		||||
    <Table.Cell flex={`num(1.0)} className=cellStyle>
 | 
			
		||||
      <span className=Css.(style([color(Settings.textMedium)]))>
 | 
			
		||||
        {MomentRe.momentWithUnix(pastTime)
 | 
			
		||||
         |> MomentRe.Moment.format(format_standard)
 | 
			
		||||
         |> ReasonReact.string}
 | 
			
		||||
      </span>
 | 
			
		||||
    </Table.Cell>
 | 
			
		||||
  </Table.Row>;
 | 
			
		||||
 | 
			
		||||
let row2 =
 | 
			
		||||
  <Table.Row>
 | 
			
		||||
    <Table.Cell flex={`num(1.0)} className=cellStyle>
 | 
			
		||||
      <AgentLink
 | 
			
		||||
        agent={AgentLink.Agent.makeUser(
 | 
			
		||||
          ~name="Samantha Hope",
 | 
			
		||||
          ~image=
 | 
			
		||||
            "https://lh3.googleusercontent.com/-1sj3EqkojJ4/AAAAAAAAAAI/AAAAAAAAAAA/ACHi3rfWCVqnuJxxM41Zird4HZx0BbRpbQ/photo.jpg",
 | 
			
		||||
          (),
 | 
			
		||||
        )}
 | 
			
		||||
      />
 | 
			
		||||
    </Table.Cell>
 | 
			
		||||
    <Table.Cell
 | 
			
		||||
      flex={`num(2.0)}
 | 
			
		||||
      className=Css.(
 | 
			
		||||
        style([paddingTop(`em(0.6)), paddingBottom(`em(0.0))])
 | 
			
		||||
      )>
 | 
			
		||||
      <CdfChart__Plain cdf minX=2.0 color={`hex("#d9dcdf")} maxX=12.0 />
 | 
			
		||||
    </Table.Cell>
 | 
			
		||||
    <Table.Cell
 | 
			
		||||
      flex={`num(1.0)}
 | 
			
		||||
      className=Css.(
 | 
			
		||||
        style([paddingTop(`em(0.2)), paddingBottom(`em(0.3))])
 | 
			
		||||
      )>
 | 
			
		||||
      <CdfChart__StatSummary cdf />
 | 
			
		||||
    </Table.Cell>
 | 
			
		||||
    <Table.Cell flex={`num(1.0)} className=cellStyle>
 | 
			
		||||
      <span className=Css.(style([color(Settings.textMedium)]))>
 | 
			
		||||
        {MomentRe.momentWithUnix(pastTime)
 | 
			
		||||
         |> MomentRe.Moment.format(format_standard)
 | 
			
		||||
         |> ReasonReact.string}
 | 
			
		||||
      </span>
 | 
			
		||||
    </Table.Cell>
 | 
			
		||||
  </Table.Row>;
 | 
			
		||||
 | 
			
		||||
let make =
 | 
			
		||||
  <PageCard>
 | 
			
		||||
    <PageCard.HeaderRow>
 | 
			
		||||
      <Div>
 | 
			
		||||
        <Div
 | 
			
		||||
          styles=[
 | 
			
		||||
            Css.style([BaseStyles.floatLeft, Css.paddingTop(`em(0.2))]),
 | 
			
		||||
          ]>
 | 
			
		||||
          <Tab isActive=true> {"Predictions" |> ReasonReact.string} </Tab>
 | 
			
		||||
          <Tab isActive=false> {"Settings" |> ReasonReact.string} </Tab>
 | 
			
		||||
        </Div>
 | 
			
		||||
      </Div>
 | 
			
		||||
      <Div>
 | 
			
		||||
        <Div
 | 
			
		||||
          float=`right
 | 
			
		||||
          styles=[Css.style([PageCard.HeaderRow.Styles.itemTopPadding])]>
 | 
			
		||||
          {PaginationButtons.make({
 | 
			
		||||
             currentValue: Range(3, 10),
 | 
			
		||||
             max: 100,
 | 
			
		||||
             pageLeft: {
 | 
			
		||||
               isDisabled: false,
 | 
			
		||||
               onClick: _ => (),
 | 
			
		||||
             },
 | 
			
		||||
             pageRight: {
 | 
			
		||||
               isDisabled: true,
 | 
			
		||||
               onClick: _ => (),
 | 
			
		||||
             },
 | 
			
		||||
           })}
 | 
			
		||||
        </Div>
 | 
			
		||||
      </Div>
 | 
			
		||||
    </PageCard.HeaderRow>
 | 
			
		||||
    <Div styles=[Css.style(BaseStyles.fullWidthFloatLeft)]>
 | 
			
		||||
      <Table>
 | 
			
		||||
        <Table.HeaderRow>
 | 
			
		||||
          <Table.Cell flex={`num(2.0)}>
 | 
			
		||||
            {"Prediction Distribution" |> ReasonReact.string}
 | 
			
		||||
          </Table.Cell>
 | 
			
		||||
          <Table.Cell flex={`num(1.0)}>
 | 
			
		||||
            {"Prediction Value" |> ReasonReact.string}
 | 
			
		||||
          </Table.Cell>
 | 
			
		||||
          <Table.Cell flex={`num(1.0)}>
 | 
			
		||||
            {"Agent" |> ReasonReact.string}
 | 
			
		||||
          </Table.Cell>
 | 
			
		||||
          <Table.Cell flex={`num(1.0)}>
 | 
			
		||||
            {"Time" |> ReasonReact.string}
 | 
			
		||||
          </Table.Cell>
 | 
			
		||||
        </Table.HeaderRow>
 | 
			
		||||
        row
 | 
			
		||||
        row2
 | 
			
		||||
        row2
 | 
			
		||||
        row2
 | 
			
		||||
        row
 | 
			
		||||
        row
 | 
			
		||||
        row2
 | 
			
		||||
        row2
 | 
			
		||||
      </Table>
 | 
			
		||||
    </Div>
 | 
			
		||||
  </PageCard>;
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,153 @@
 | 
			
		|||
open FC;
 | 
			
		||||
open Base;
 | 
			
		||||
 | 
			
		||||
let cdf = ExampleCdfs.Example1.cdf;
 | 
			
		||||
 | 
			
		||||
let futureTime = 1559005200;
 | 
			
		||||
 | 
			
		||||
let row =
 | 
			
		||||
  <Table.Row onClick={_ => Js.log("Row Clicked")}>
 | 
			
		||||
    <Table.Cell flex={`num(4.)}>
 | 
			
		||||
      <span className=Table.Styles.Elements.primaryText>
 | 
			
		||||
        {"What will be the " |> ReasonReact.string}
 | 
			
		||||
        <Link
 | 
			
		||||
          isDisabled=false
 | 
			
		||||
          className=Css.(
 | 
			
		||||
            style([
 | 
			
		||||
              textDecoration(`underline),
 | 
			
		||||
              color(`hex("384e67")),
 | 
			
		||||
              hover([color(Colors.link)]),
 | 
			
		||||
            ])
 | 
			
		||||
          )>
 | 
			
		||||
          {"GDP" |> ReasonReact.string}
 | 
			
		||||
        </Link>
 | 
			
		||||
        <Link
 | 
			
		||||
          href="d"
 | 
			
		||||
          isDisabled=false
 | 
			
		||||
          className=Css.(
 | 
			
		||||
            style([
 | 
			
		||||
              textDecoration(`underline),
 | 
			
		||||
              color(`hex("384e67")),
 | 
			
		||||
              hover([color(Colors.link)]),
 | 
			
		||||
            ])
 | 
			
		||||
          )>
 | 
			
		||||
          {"GDP" |> ReasonReact.string}
 | 
			
		||||
        </Link>
 | 
			
		||||
        {" of " |> ReasonReact.string}
 | 
			
		||||
        <Link
 | 
			
		||||
          href="China"
 | 
			
		||||
          isDisabled=false
 | 
			
		||||
          className=Css.(
 | 
			
		||||
            style([
 | 
			
		||||
              textDecoration(`underline),
 | 
			
		||||
              color(`hex("384e67")),
 | 
			
		||||
              hover([color(Colors.link)]),
 | 
			
		||||
            ])
 | 
			
		||||
          )>
 | 
			
		||||
          {"China" |> ReasonReact.string}
 | 
			
		||||
        </Link>
 | 
			
		||||
        {" in " |> ReasonReact.string}
 | 
			
		||||
        <Link
 | 
			
		||||
          href="2018"
 | 
			
		||||
          isDisabled=false
 | 
			
		||||
          className=Css.(
 | 
			
		||||
            style([
 | 
			
		||||
              textDecoration(`underline),
 | 
			
		||||
              color(`hex("384e67")),
 | 
			
		||||
              hover([color(Colors.link)]),
 | 
			
		||||
            ])
 | 
			
		||||
          )>
 | 
			
		||||
          {"2018" |> ReasonReact.string}
 | 
			
		||||
        </Link>
 | 
			
		||||
      </span>
 | 
			
		||||
      {StateStatus.make(
 | 
			
		||||
         ~state=OPEN(MomentRe.momentWithUnix(futureTime)),
 | 
			
		||||
         ~fontSize=`em(0.85),
 | 
			
		||||
         (),
 | 
			
		||||
       )}
 | 
			
		||||
    </Table.Cell>
 | 
			
		||||
    <Table.Cell flex={`num(2.)}>
 | 
			
		||||
      <CdfChart__Small
 | 
			
		||||
        cdf
 | 
			
		||||
        minX={Some(2.0)}
 | 
			
		||||
        color={`hex("#d9dcdf")}
 | 
			
		||||
        maxX={Some(12.0)}
 | 
			
		||||
      />
 | 
			
		||||
    </Table.Cell>
 | 
			
		||||
    <Table.Cell flex={`num(1.)} properties=Css.[paddingTop(`em(0.3))]>
 | 
			
		||||
      <Div>
 | 
			
		||||
        <Link className={Table.Styles.Elements.link(~isUnderlined=false, ())}>
 | 
			
		||||
          {"Series A" |> ReasonReact.string}
 | 
			
		||||
        </Link>
 | 
			
		||||
        <Link className={Table.Styles.Elements.link(~isUnderlined=false, ())}>
 | 
			
		||||
          {"19" |> ReasonReact.string}
 | 
			
		||||
        </Link>
 | 
			
		||||
      </Div>
 | 
			
		||||
      <Div>
 | 
			
		||||
        <Link className={Table.Styles.Elements.link(~isUnderlined=true, ())}>
 | 
			
		||||
          {"Edit" |> ReasonReact.string}
 | 
			
		||||
        </Link>
 | 
			
		||||
        <Link className={Table.Styles.Elements.link(~isUnderlined=true, ())}>
 | 
			
		||||
          {"Archive" |> ReasonReact.string}
 | 
			
		||||
        </Link>
 | 
			
		||||
      </Div>
 | 
			
		||||
    </Table.Cell>
 | 
			
		||||
  </Table.Row>;
 | 
			
		||||
 | 
			
		||||
let make =
 | 
			
		||||
  <PageCard>
 | 
			
		||||
    <PageCard.HeaderRow>
 | 
			
		||||
      <Div
 | 
			
		||||
        float=`left
 | 
			
		||||
        className={Css.style([
 | 
			
		||||
          PageCard.HeaderRow.Styles.itemTopPadding,
 | 
			
		||||
          PageCard.HeaderRow.Styles.itemBottomPadding,
 | 
			
		||||
        ])}>
 | 
			
		||||
        <Tab2 isActive=true number=12> {"Open" |> ReasonReact.string} </Tab2>
 | 
			
		||||
        <Tab2 isActive=false number=18>
 | 
			
		||||
          {"Pending Resolution" |> ReasonReact.string}
 | 
			
		||||
        </Tab2>
 | 
			
		||||
        <Tab2 isActive=false number=831>
 | 
			
		||||
          {"Closed" |> ReasonReact.string}
 | 
			
		||||
        </Tab2>
 | 
			
		||||
      </Div>
 | 
			
		||||
      <Div
 | 
			
		||||
        float=`right
 | 
			
		||||
        styles=[Css.style([PageCard.HeaderRow.Styles.itemTopPadding])]>
 | 
			
		||||
        {PaginationButtons.make({
 | 
			
		||||
           currentValue: Range(3, 10),
 | 
			
		||||
           max: 100,
 | 
			
		||||
           pageLeft: {
 | 
			
		||||
             isDisabled: false,
 | 
			
		||||
             onClick: _ => (),
 | 
			
		||||
           },
 | 
			
		||||
           pageRight: {
 | 
			
		||||
             isDisabled: true,
 | 
			
		||||
             onClick: _ => (),
 | 
			
		||||
           },
 | 
			
		||||
         })}
 | 
			
		||||
      </Div>
 | 
			
		||||
    </PageCard.HeaderRow>
 | 
			
		||||
    <Table>
 | 
			
		||||
      <Table.HeaderRow>
 | 
			
		||||
        <Table.Cell flex={`num(4.)}>
 | 
			
		||||
          {"Name & Status" |> ReasonReact.string}
 | 
			
		||||
        </Table.Cell>
 | 
			
		||||
        <Table.Cell flex={`num(2.)}>
 | 
			
		||||
          {"Aggregate Prediction" |> ReasonReact.string}
 | 
			
		||||
        </Table.Cell>
 | 
			
		||||
        <Table.Cell flex={`num(1.)}>
 | 
			
		||||
          {"Details" |> ReasonReact.string}
 | 
			
		||||
        </Table.Cell>
 | 
			
		||||
      </Table.HeaderRow>
 | 
			
		||||
      row
 | 
			
		||||
      row
 | 
			
		||||
      row
 | 
			
		||||
      row
 | 
			
		||||
      row
 | 
			
		||||
      row
 | 
			
		||||
      row
 | 
			
		||||
      row
 | 
			
		||||
      row
 | 
			
		||||
    </Table>
 | 
			
		||||
  </PageCard>;
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,80 @@
 | 
			
		|||
open FC;
 | 
			
		||||
open Base;
 | 
			
		||||
 | 
			
		||||
let pastTime = 1483232400;
 | 
			
		||||
let futureTime = 1559005200;
 | 
			
		||||
 | 
			
		||||
let make =
 | 
			
		||||
  <PageCard>
 | 
			
		||||
    <PageCard.HeaderRow>
 | 
			
		||||
      <Div>
 | 
			
		||||
        <Div
 | 
			
		||||
          styles=[
 | 
			
		||||
            Css.style([
 | 
			
		||||
              BaseStyles.floatLeft,
 | 
			
		||||
              PageCard.HeaderRow.Styles.itemTopPadding,
 | 
			
		||||
              PageCard.HeaderRow.Styles.itemBottomPadding,
 | 
			
		||||
            ]),
 | 
			
		||||
          ]>
 | 
			
		||||
          <Button size=Button.Small>
 | 
			
		||||
            {"< Back" |> ReasonReact.string}
 | 
			
		||||
          </Button>
 | 
			
		||||
        </Div>
 | 
			
		||||
        <Div
 | 
			
		||||
          float=`right
 | 
			
		||||
          styles=[
 | 
			
		||||
            Css.style([
 | 
			
		||||
              PageCard.HeaderRow.Styles.itemTopPadding,
 | 
			
		||||
              PageCard.HeaderRow.Styles.itemBottomPadding,
 | 
			
		||||
            ]),
 | 
			
		||||
          ]>
 | 
			
		||||
          {PaginationButtons.make({
 | 
			
		||||
             currentValue: Range(3, 10),
 | 
			
		||||
             max: 100,
 | 
			
		||||
             pageLeft: {
 | 
			
		||||
               isDisabled: false,
 | 
			
		||||
               onClick: _ => (),
 | 
			
		||||
             },
 | 
			
		||||
             pageRight: {
 | 
			
		||||
               isDisabled: true,
 | 
			
		||||
               onClick: _ => (),
 | 
			
		||||
             },
 | 
			
		||||
           })}
 | 
			
		||||
        </Div>
 | 
			
		||||
      </Div>
 | 
			
		||||
    </PageCard.HeaderRow>
 | 
			
		||||
    <Div
 | 
			
		||||
      styles=[
 | 
			
		||||
        Css.style(
 | 
			
		||||
          [Css.padding2(~v=`em(1.5), ~h=`em(1.5))]
 | 
			
		||||
          @ BaseStyles.fullWidthFloatLeft,
 | 
			
		||||
        ),
 | 
			
		||||
      ]>
 | 
			
		||||
      <Div flexDirection=`row>
 | 
			
		||||
        <Div flex={`num(5.)}>
 | 
			
		||||
          <PageCard.H1>
 | 
			
		||||
            {"What would the US GDP Be in 2020?" |> ReasonReact.string}
 | 
			
		||||
          </PageCard.H1>
 | 
			
		||||
          {StateStatus.make(
 | 
			
		||||
             ~state=RESOLVED(MomentRe.momentWithUnix(pastTime)),
 | 
			
		||||
             ~fontSize=`em(1.0),
 | 
			
		||||
             (),
 | 
			
		||||
           )}
 | 
			
		||||
        </Div>
 | 
			
		||||
        <AgentLink
 | 
			
		||||
          agent={AgentLink.Agent.makeUser(
 | 
			
		||||
            ~name="Roger Adams",
 | 
			
		||||
            ~image=
 | 
			
		||||
              "https://lh3.googleusercontent.com/-1sj3EqkojJ4/AAAAAAAAAAI/AAAAAAAAAAA/ACHi3rfWCVqnuJxxM41Zird4HZx0BbRpbQ/photo.jpg",
 | 
			
		||||
            (),
 | 
			
		||||
          )}
 | 
			
		||||
        />
 | 
			
		||||
      </Div>
 | 
			
		||||
      <Div styles=[Css.style([Css.marginTop(`em(2.0))])]>
 | 
			
		||||
        <PageCard.P>
 | 
			
		||||
          {"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut vulputate tortor a sapien aliquet ullamcorper. Nunc non varius sapien, quis elementum sapien. Morbi ac tristique quam. Cras hendrerit accumsan pretium. Praesent id nisl sit amet eros imperdiet placerat. Vestibulum sodales posuere diam vel laoreet."
 | 
			
		||||
           |> ReasonReact.string}
 | 
			
		||||
        </PageCard.P>
 | 
			
		||||
      </Div>
 | 
			
		||||
    </Div>
 | 
			
		||||
  </PageCard>;
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,36 @@
 | 
			
		|||
open FC;
 | 
			
		||||
open Base;
 | 
			
		||||
 | 
			
		||||
let make =
 | 
			
		||||
  <PageCard>
 | 
			
		||||
    <PageCard.HeaderRow>
 | 
			
		||||
      <Div float=`left>
 | 
			
		||||
        <PageCard.HeaderRow.Title>
 | 
			
		||||
          {"Pending Resolution" |> ReasonReact.string}
 | 
			
		||||
        </PageCard.HeaderRow.Title>
 | 
			
		||||
      </Div>
 | 
			
		||||
      <Div
 | 
			
		||||
        float=`right
 | 
			
		||||
        className={Css.style([
 | 
			
		||||
          PageCard.HeaderRow.Styles.itemTopPadding,
 | 
			
		||||
          PageCard.HeaderRow.Styles.itemBottomPadding,
 | 
			
		||||
        ])}>
 | 
			
		||||
        <Button variant=Button.Primary size=Button.Small>
 | 
			
		||||
          {"< Back" |> ReasonReact.string}
 | 
			
		||||
        </Button>
 | 
			
		||||
      </Div>
 | 
			
		||||
    </PageCard.HeaderRow>
 | 
			
		||||
    <Table>
 | 
			
		||||
      <Table.HeaderRow>
 | 
			
		||||
        <Table.Cell flex={`num(4.)}>
 | 
			
		||||
          {"Name & Status" |> ReasonReact.string}
 | 
			
		||||
        </Table.Cell>
 | 
			
		||||
        <Table.Cell flex={`num(2.)}>
 | 
			
		||||
          {"Aggregate Prediction" |> ReasonReact.string}
 | 
			
		||||
        </Table.Cell>
 | 
			
		||||
        <Table.Cell flex={`num(1.)}>
 | 
			
		||||
          {"Details" |> ReasonReact.string}
 | 
			
		||||
        </Table.Cell>
 | 
			
		||||
      </Table.HeaderRow>
 | 
			
		||||
    </Table>
 | 
			
		||||
  </PageCard>;
 | 
			
		||||
							
								
								
									
										4016
									
								
								foretold/components/example/ExampleCdfs.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4016
									
								
								foretold/components/example/ExampleCdfs.re
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										17
									
								
								foretold/components/example/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								foretold/components/example/index.html
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
<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">
 | 
			
		||||
 | 
			
		||||
    <title>Example</title>
 | 
			
		||||
</head>
 | 
			
		||||
 | 
			
		||||
<body>
 | 
			
		||||
    <div id="app"></div>
 | 
			
		||||
    <script src=" ./App.bs.js "></script>
 | 
			
		||||
</body>
 | 
			
		||||
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										0
									
								
								foretold/components/example/styles.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								foretold/components/example/styles.css
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										
											BIN
										
									
								
								foretold/components/logo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								foretold/components/logo.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 17 KiB  | 
							
								
								
									
										48
									
								
								foretold/components/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								foretold/components/package.json
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,48 @@
 | 
			
		|||
{
 | 
			
		||||
    "name": "@foretold/components",
 | 
			
		||||
    "version": "0.0.6",
 | 
			
		||||
    "description": "Kit-demo of UX components",
 | 
			
		||||
    "private": false,
 | 
			
		||||
    "scripts": {
 | 
			
		||||
        "build": "bsb -make-world",
 | 
			
		||||
        "clean": "bsb -clean-world",
 | 
			
		||||
        "predev": "yarn build",
 | 
			
		||||
        "dev": "PORT=12346 parcel example/index.html",
 | 
			
		||||
        "showcase": "PORT=12345 parcel example/index.html showcase/index.html",
 | 
			
		||||
        "start": "bsb -make-world -w"
 | 
			
		||||
    },
 | 
			
		||||
    "keywords": [
 | 
			
		||||
        "foretold",
 | 
			
		||||
        "bucklescript",
 | 
			
		||||
        "reason",
 | 
			
		||||
        "css"
 | 
			
		||||
    ],
 | 
			
		||||
    "author": "Ozzie Gooen <ozzieagooen@gmail.com>",
 | 
			
		||||
    "license": "MIT",
 | 
			
		||||
    "dependencies": {
 | 
			
		||||
        "@foretold/cdf": "1.0.16",
 | 
			
		||||
        "@foretold/guesstimator": "1.0.11",
 | 
			
		||||
        "bs-css": "11.0.0",
 | 
			
		||||
        "bs-moment": "0.4.5",
 | 
			
		||||
        "bs-platform": "7.2.2",
 | 
			
		||||
        "d3": "5.16.0",
 | 
			
		||||
        "emotion": "10.0.27",
 | 
			
		||||
        "lodash": "4.17.15",
 | 
			
		||||
        "moment": "2.24.0",
 | 
			
		||||
        "rationale": "0.2.0",
 | 
			
		||||
        "rc-dropdown": "3.0.2",
 | 
			
		||||
        "rc-menu": "8.0.3",
 | 
			
		||||
        "react": "16.12.0",
 | 
			
		||||
        "react-dom": "16.12.0",
 | 
			
		||||
        "react-icons-kit": "1.3.1",
 | 
			
		||||
        "vega-embed": "6.6.0",
 | 
			
		||||
        "vega": "5.11.1",
 | 
			
		||||
        "react-use": "14.2.0",
 | 
			
		||||
        "reason-apollo": "0.20.0",
 | 
			
		||||
        "reason-react": "0.7.1",
 | 
			
		||||
        "react-textarea-autosize": "7.1.2"
 | 
			
		||||
    },
 | 
			
		||||
    "devDependencies": {
 | 
			
		||||
        "parcel-bundler": "1.12.3"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										23
									
								
								foretold/components/showcase/Entries.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								foretold/components/showcase/Entries.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,23 @@
 | 
			
		|||
let entries =
 | 
			
		||||
  EntryTypes.[
 | 
			
		||||
    Showcase_Buttons.entry,
 | 
			
		||||
    Showcase_PageCard.entry,
 | 
			
		||||
    Showcase_NumberShower.entry,
 | 
			
		||||
    Showcase_MeasurableForm.entry,
 | 
			
		||||
    Showcase_Colors.entry,
 | 
			
		||||
    Showcase_AgentLink.entry,
 | 
			
		||||
    Showcase_MyCommunities.entry,
 | 
			
		||||
    folder(
 | 
			
		||||
      ~title="Link",
 | 
			
		||||
      ~children=[
 | 
			
		||||
        entry(~title="Link1b", ~render=() =>
 | 
			
		||||
          <Link> "Test link"->React.string </Link>
 | 
			
		||||
        ),
 | 
			
		||||
      ],
 | 
			
		||||
    ),
 | 
			
		||||
  ]
 | 
			
		||||
  @ Showcase_Charts.entries
 | 
			
		||||
  @ Showcase_Dropdown.entries
 | 
			
		||||
  @ Showcase_Menu.entries
 | 
			
		||||
  @ Showcase_DropdownMenu.entries
 | 
			
		||||
  @ Showcase_DropdownSelect.entries;
 | 
			
		||||
							
								
								
									
										30
									
								
								foretold/components/showcase/EntryTypes.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								foretold/components/showcase/EntryTypes.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,30 @@
 | 
			
		|||
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});
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										7
									
								
								foretold/components/showcase/Index.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								foretold/components/showcase/Index.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
ReactDOMRe.renderToElementWithId(
 | 
			
		||||
  <div className=Css.(style([fontFamily(Settings.Text.standardFont)]))>
 | 
			
		||||
    <Lib.Index />
 | 
			
		||||
  </div>,
 | 
			
		||||
  "main",
 | 
			
		||||
);
 | 
			
		||||
ReasonReactRouter.push("");
 | 
			
		||||
							
								
								
									
										201
									
								
								foretold/components/showcase/Lib.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								foretold/components/showcase/Lib.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,201 @@
 | 
			
		|||
open FC.Base;
 | 
			
		||||
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([%bs.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(e =>
 | 
			
		||||
         switch (e) {
 | 
			
		||||
         | 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(e =>
 | 
			
		||||
       switch (e) {
 | 
			
		||||
       | CompEntry(c) => processEntry(c, "")
 | 
			
		||||
       | FolderEntry(f) => processFolder(f, "")
 | 
			
		||||
       }
 | 
			
		||||
     );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
let entries = Entries.entries;
 | 
			
		||||
buildIds(entries);
 | 
			
		||||
 | 
			
		||||
module Styles = {
 | 
			
		||||
  open Css;
 | 
			
		||||
  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, 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: ReasonReactRouter.url};
 | 
			
		||||
 | 
			
		||||
  type action =
 | 
			
		||||
    | ItemClick(string)
 | 
			
		||||
    | ChangeRoute(ReasonReactRouter.url);
 | 
			
		||||
 | 
			
		||||
  let changeId = (id: string) => {
 | 
			
		||||
    ReasonReactRouter.push(baseUrl ++ "#" ++ id);
 | 
			
		||||
    ();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  let buildNav = setRoute => {
 | 
			
		||||
    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: ReasonReactRouter.url = {path: [], hash: "", search: ""};
 | 
			
		||||
        url;
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
    React.useState(() => {
 | 
			
		||||
      ReasonReactRouter.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>;
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										104
									
								
								foretold/components/showcase/entries/Scoring.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								foretold/components/showcase/entries/Scoring.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,104 @@
 | 
			
		|||
open FC.Base;
 | 
			
		||||
 | 
			
		||||
let multimodal = "=mm(uniform(0,100), uniform(10,20), [.33,0.9])";
 | 
			
		||||
let mm1 = "=mm(uniform(1,100), normal(50, 5), [.01, .99])";
 | 
			
		||||
let mm2 = "=mm(uniform(1,100), normal(50, 8), [.01, .99])";
 | 
			
		||||
 | 
			
		||||
module Scoring = {
 | 
			
		||||
  type dist = Types.Dist.t;
 | 
			
		||||
 | 
			
		||||
  [@react.component]
 | 
			
		||||
  let make = () => {
 | 
			
		||||
    let (varA, setVarA) = React.useState(() => None);
 | 
			
		||||
    let (varB, setVarB) = React.useState(() => None);
 | 
			
		||||
    let (varC, setVarC) = React.useState(() => None);
 | 
			
		||||
 | 
			
		||||
    let distributionScoreDistribution =
 | 
			
		||||
      switch (varA, varB, varC) {
 | 
			
		||||
      | (Some(a), Some(b), Some(c)) =>
 | 
			
		||||
        Types.Dist.distributionScoreDistribution([|a, b, c|])
 | 
			
		||||
      | _ => None
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
    let distributionScoreNumber =
 | 
			
		||||
      switch (varA, varB, varC) {
 | 
			
		||||
      | (Some(a), Some(b), Some(c)) =>
 | 
			
		||||
        Some(Types.Dist.distributionScoreNumber([|a, b, c|]))
 | 
			
		||||
      | _ => None
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
    let minX =
 | 
			
		||||
      [|varA, varB, varC, distributionScoreDistribution|]
 | 
			
		||||
      |> E.A.O.concatSome
 | 
			
		||||
      |> Types.Dists.minX(0.01);
 | 
			
		||||
    let maxX =
 | 
			
		||||
      [|varA, varB, varC, distributionScoreDistribution|]
 | 
			
		||||
      |> E.A.O.concatSome
 | 
			
		||||
      |> Types.Dists.maxX(0.99);
 | 
			
		||||
 | 
			
		||||
    Js.log2("MIN", min);
 | 
			
		||||
 | 
			
		||||
    <div>
 | 
			
		||||
      <h3> {"Variable A" |> ReasonReact.string} </h3>
 | 
			
		||||
      <ReGuesstimateInput
 | 
			
		||||
        focusOnRender=true
 | 
			
		||||
        initialValue={Some(mm1)}
 | 
			
		||||
        sampleCount=50000
 | 
			
		||||
        onUpdate={event =>
 | 
			
		||||
          {let (ys, xs, hasLimitError) = event
 | 
			
		||||
           setVarA(_ => Types.Dist.requireLength({ys, xs}))
 | 
			
		||||
           Js.log2(xs, ys)}
 | 
			
		||||
          |> ignore
 | 
			
		||||
        }
 | 
			
		||||
      />
 | 
			
		||||
      {varA
 | 
			
		||||
       |> E.O.React.fmapOrNull(v =>
 | 
			
		||||
            <CdfChart__Large cdf=v minX maxX width=None />
 | 
			
		||||
          )}
 | 
			
		||||
      <h3> {"Variable B" |> ReasonReact.string} </h3>
 | 
			
		||||
      <ReGuesstimateInput
 | 
			
		||||
        focusOnRender=true
 | 
			
		||||
        sampleCount=50000
 | 
			
		||||
        initialValue={Some(mm2)}
 | 
			
		||||
        onUpdate={event =>
 | 
			
		||||
          {let (ys, xs, hasLimitError) = event
 | 
			
		||||
           setVarB(_ => Types.Dist.requireLength({ys, xs}))}
 | 
			
		||||
          |> ignore
 | 
			
		||||
        }
 | 
			
		||||
      />
 | 
			
		||||
      {varB
 | 
			
		||||
       |> E.O.React.fmapOrNull(v =>
 | 
			
		||||
            <CdfChart__Large minX maxX cdf=v width=None />
 | 
			
		||||
          )}
 | 
			
		||||
      <h3> {"Variable C" |> ReasonReact.string} </h3>
 | 
			
		||||
      <ReGuesstimateInput
 | 
			
		||||
        focusOnRender=true
 | 
			
		||||
        sampleCount=50000
 | 
			
		||||
        initialValue={Some("20 to 60")}
 | 
			
		||||
        onUpdate={event =>
 | 
			
		||||
          {let (ys, xs, hasLimitError) = event
 | 
			
		||||
           setVarC(_ => Types.Dist.requireLength({ys, xs}))}
 | 
			
		||||
          |> ignore
 | 
			
		||||
        }
 | 
			
		||||
      />
 | 
			
		||||
      {varC
 | 
			
		||||
       |> E.O.React.fmapOrNull(v =>
 | 
			
		||||
            <CdfChart__Large minX maxX cdf=v width=None />
 | 
			
		||||
          )}
 | 
			
		||||
      <h3> {"C * Log2(A / B)" |> ReasonReact.string} </h3>
 | 
			
		||||
      {switch (distributionScoreDistribution) {
 | 
			
		||||
       | None => ReasonReact.null
 | 
			
		||||
       | Some(divideBy) =>
 | 
			
		||||
         <CdfChart__Large minX maxX cdf=divideBy width=None />
 | 
			
		||||
       }}
 | 
			
		||||
      <h3> {"Final Score" |> ReasonReact.string} </h3>
 | 
			
		||||
      {switch (distributionScoreNumber) {
 | 
			
		||||
       | None => ReasonReact.null
 | 
			
		||||
       | Some(scoreNumber) =>
 | 
			
		||||
         scoreNumber |> E.Float.with3DigitsPrecision |> ReasonReact.string
 | 
			
		||||
       }}
 | 
			
		||||
    </div>;
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
let entry = EntryTypes.(entry(~title="Scoring", ~render=() => <Scoring />));
 | 
			
		||||
							
								
								
									
										48
									
								
								foretold/components/showcase/entries/Showcase_AgentLink.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								foretold/components/showcase/entries/Showcase_AgentLink.re
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										12
									
								
								foretold/components/showcase/entries/Showcase_Alerts.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								foretold/components/showcase/entries/Showcase_Alerts.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,12 @@
 | 
			
		|||
open FC.Base;
 | 
			
		||||
 | 
			
		||||
let alerts = () =>
 | 
			
		||||
  <div>
 | 
			
		||||
    <Alert type_=`primary> "Primary alert"->React.string </Alert>
 | 
			
		||||
    <Alert type_=`info> "Info alert"->React.string </Alert>
 | 
			
		||||
    <Alert type_=`success> "Success alert"->React.string </Alert>
 | 
			
		||||
    <Alert type_=`warning> "Warning alert"->React.string </Alert>
 | 
			
		||||
    <Alert type_=`error> "Error alert"->React.string </Alert>
 | 
			
		||||
  </div>;
 | 
			
		||||
 | 
			
		||||
let entry = EntryTypes.(entry(~title="Alerts", ~render=alerts));
 | 
			
		||||
							
								
								
									
										36
									
								
								foretold/components/showcase/entries/Showcase_Buttons.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								foretold/components/showcase/entries/Showcase_Buttons.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,36 @@
 | 
			
		|||
open FC;
 | 
			
		||||
open Button;
 | 
			
		||||
 | 
			
		||||
let clear = Css.(style([clear(`both)]));
 | 
			
		||||
let button = Css.(style([margin(`em(0.5))]));
 | 
			
		||||
 | 
			
		||||
let render = () =>
 | 
			
		||||
  <>
 | 
			
		||||
    <div> "Secondary"->React.string </div>
 | 
			
		||||
    <div>
 | 
			
		||||
      <Button size=MediumShort className=button>
 | 
			
		||||
        "Small Button"->React.string
 | 
			
		||||
      </Button>
 | 
			
		||||
      <Button size=Medium className=button>
 | 
			
		||||
        "Medium Button"->React.string
 | 
			
		||||
      </Button>
 | 
			
		||||
      <Button size=Large className=button>
 | 
			
		||||
        "Large Button"->React.string
 | 
			
		||||
      </Button>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div className=clear />
 | 
			
		||||
    <div> "Primary"->React.string </div>
 | 
			
		||||
    <div>
 | 
			
		||||
      <Button size=MediumShort variant=Primary className=button>
 | 
			
		||||
        "Small Button"->React.string
 | 
			
		||||
      </Button>
 | 
			
		||||
      <Button size=Medium variant=Primary className=button>
 | 
			
		||||
        "Medium Button"->React.string
 | 
			
		||||
      </Button>
 | 
			
		||||
      <Button size=Large variant=Primary className=button>
 | 
			
		||||
        "Large Button"->React.string
 | 
			
		||||
      </Button>
 | 
			
		||||
    </div>
 | 
			
		||||
  </>;
 | 
			
		||||
 | 
			
		||||
let entry = EntryTypes.(entry(~title="Buttons", ~render));
 | 
			
		||||
							
								
								
									
										18
									
								
								foretold/components/showcase/entries/Showcase_Charts.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								foretold/components/showcase/entries/Showcase_Charts.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,18 @@
 | 
			
		|||
[@bs.module] external data1: Js.Json.t = "./samples/sample-measurements.json";
 | 
			
		||||
[@bs.module]
 | 
			
		||||
external data2: Js.Json.t = "./samples/sample-measurements-aggregation.json";
 | 
			
		||||
 | 
			
		||||
let chart1 = () => <div> <RePercentilesChart data=data1 /> </div>;
 | 
			
		||||
 | 
			
		||||
let chart2 = () => <div> <RePercentilesChart data=data2 /> </div>;
 | 
			
		||||
 | 
			
		||||
let entries =
 | 
			
		||||
  EntryTypes.[
 | 
			
		||||
    folder(
 | 
			
		||||
      ~title="Charts",
 | 
			
		||||
      ~children=[
 | 
			
		||||
        entry(~title="Chart 1", ~render=chart1),
 | 
			
		||||
        entry(~title="Chart 2", ~render=chart2),
 | 
			
		||||
      ],
 | 
			
		||||
    ),
 | 
			
		||||
  ];
 | 
			
		||||
							
								
								
									
										109
									
								
								foretold/components/showcase/entries/Showcase_Colors.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								foretold/components/showcase/entries/Showcase_Colors.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,109 @@
 | 
			
		|||
open FC;
 | 
			
		||||
open FC.Base;
 | 
			
		||||
 | 
			
		||||
let colors =
 | 
			
		||||
  Colors.[
 | 
			
		||||
    ("white", white),
 | 
			
		||||
    ("black", black),
 | 
			
		||||
    ("greyO4", greyO4),
 | 
			
		||||
    ("whiteO2", whiteO2),
 | 
			
		||||
    ("whiteOc", whiteOc),
 | 
			
		||||
    ("clear", clear),
 | 
			
		||||
    ("textDarker", textDarker),
 | 
			
		||||
    ("textDark", textDark),
 | 
			
		||||
    ("textMedium", textMedium),
 | 
			
		||||
    ("smokeWhite", smokeWhite),
 | 
			
		||||
    ("buttonHover", buttonHover),
 | 
			
		||||
    ("lightGrayBackground", lightGrayBackground),
 | 
			
		||||
    ("lighterGrayBackground", lighterGrayBackground),
 | 
			
		||||
    ("grayBackground", grayBackground),
 | 
			
		||||
    ("greydisabled", greydisabled),
 | 
			
		||||
    ("accentBlue", accentBlue),
 | 
			
		||||
    ("accentBlueO8", accentBlueO8),
 | 
			
		||||
    ("accentBlue1a", accentBlue1a),
 | 
			
		||||
    ("mainBlue", mainBlue),
 | 
			
		||||
    ("link", link),
 | 
			
		||||
    ("linkHover", linkHover),
 | 
			
		||||
    ("linkAccent", linkAccent),
 | 
			
		||||
    ("darkLink", darkLink),
 | 
			
		||||
    ("darkAccentBlue", darkAccentBlue),
 | 
			
		||||
    ("grey1", grey1),
 | 
			
		||||
    ("border", border),
 | 
			
		||||
    ("primary", primary),
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
let colorBoxStyle =
 | 
			
		||||
  Css.(
 | 
			
		||||
    style([
 | 
			
		||||
      flexBasis(`px(220)),
 | 
			
		||||
      flexGrow(1.),
 | 
			
		||||
      height(`px(90)),
 | 
			
		||||
      marginRight(`px(35)),
 | 
			
		||||
      marginBottom(`px(35)),
 | 
			
		||||
      textAlign(`center),
 | 
			
		||||
    ])
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
let colorContainer = bgColor =>
 | 
			
		||||
  Css.(
 | 
			
		||||
    style([
 | 
			
		||||
      display(`flex),
 | 
			
		||||
      flexWrap(`wrap),
 | 
			
		||||
      padding(`em(1.5)),
 | 
			
		||||
      backgroundColor(bgColor),
 | 
			
		||||
    ])
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
module ColorDisplay = {
 | 
			
		||||
  [@react.component]
 | 
			
		||||
  let make = () => {
 | 
			
		||||
    let (bgColor, setBgColor) = React.useState(() => Colors.white);
 | 
			
		||||
    let (bgName, setBgName) = React.useState(() => "white");
 | 
			
		||||
 | 
			
		||||
    <PageCard>
 | 
			
		||||
      <PageCard.HeaderRow>
 | 
			
		||||
        <PageCard.HeaderRow.Title>
 | 
			
		||||
          "Colors"->React.string
 | 
			
		||||
        </PageCard.HeaderRow.Title>
 | 
			
		||||
      </PageCard.HeaderRow>
 | 
			
		||||
      <PageCard.Section border=`bottom>
 | 
			
		||||
        "Background: "->React.string
 | 
			
		||||
        <select
 | 
			
		||||
          value=bgName
 | 
			
		||||
          onChange={e => {
 | 
			
		||||
            let bgName = ReactEvent.Form.target(e)##value;
 | 
			
		||||
            setBgName(_ => bgName);
 | 
			
		||||
            setBgColor(_ => {
 | 
			
		||||
              let (_, bgColor) =
 | 
			
		||||
                colors |> E.L.find(((n, _)) => n == bgName);
 | 
			
		||||
              bgColor;
 | 
			
		||||
            });
 | 
			
		||||
          }}>
 | 
			
		||||
          {{colors
 | 
			
		||||
            |> E.L.fmap(((name, _c)) =>
 | 
			
		||||
                 <option key=name value=name> name->React.string </option>
 | 
			
		||||
               )
 | 
			
		||||
            |> E.L.toArray}
 | 
			
		||||
           ->React.array}
 | 
			
		||||
        </select>
 | 
			
		||||
      </PageCard.Section>
 | 
			
		||||
      <div className={colorContainer(bgColor)}>
 | 
			
		||||
        {{colors
 | 
			
		||||
          |> E.L.fmap(((name, c)) =>
 | 
			
		||||
               <div
 | 
			
		||||
                 key=name
 | 
			
		||||
                 className=Css.(
 | 
			
		||||
                   merge([colorBoxStyle, style([backgroundColor(c)])])
 | 
			
		||||
                 )>
 | 
			
		||||
                 name->React.string
 | 
			
		||||
               </div>
 | 
			
		||||
             )
 | 
			
		||||
          |> E.L.toArray}
 | 
			
		||||
         ->React.array}
 | 
			
		||||
      </div>
 | 
			
		||||
    </PageCard>;
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
let entry =
 | 
			
		||||
  EntryTypes.(entry(~title="Colors", ~render=() => <ColorDisplay />));
 | 
			
		||||
							
								
								
									
										46
									
								
								foretold/components/showcase/entries/Showcase_Dropdown.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								foretold/components/showcase/entries/Showcase_Dropdown.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,46 @@
 | 
			
		|||
open FC.Base;
 | 
			
		||||
 | 
			
		||||
let staticOverlay =
 | 
			
		||||
  <div
 | 
			
		||||
    className=Css.(
 | 
			
		||||
      style([
 | 
			
		||||
        border(`px(1), `solid, Colors.grey1),
 | 
			
		||||
        backgroundColor(Colors.white),
 | 
			
		||||
        width(`px(200)),
 | 
			
		||||
      ])
 | 
			
		||||
    )>
 | 
			
		||||
    {"Overlay" |> React.string}
 | 
			
		||||
  </div>;
 | 
			
		||||
 | 
			
		||||
let divAreaStyle =
 | 
			
		||||
  Css.(
 | 
			
		||||
    style([
 | 
			
		||||
      backgroundColor(Colors.smokeWhite),
 | 
			
		||||
      width(`px(300)),
 | 
			
		||||
      textAlign(`center),
 | 
			
		||||
      padding2(~v=`em(1.), ~h=`em(1.)),
 | 
			
		||||
    ])
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
let simpleDropdown = () =>
 | 
			
		||||
  <Dropdown overlay=staticOverlay>
 | 
			
		||||
    <div className=divAreaStyle>
 | 
			
		||||
      "Dropdown default trigger (hover)"->React.string
 | 
			
		||||
    </div>
 | 
			
		||||
  </Dropdown>;
 | 
			
		||||
 | 
			
		||||
let menuDropdown = () =>
 | 
			
		||||
  <Dropdown overlay={Showcase_Menu.subMenu()} trigger=Dropdown.Hover>
 | 
			
		||||
    <div className=divAreaStyle> "Submenu"->React.string </div>
 | 
			
		||||
  </Dropdown>;
 | 
			
		||||
 | 
			
		||||
let entries =
 | 
			
		||||
  EntryTypes.[
 | 
			
		||||
    folder(
 | 
			
		||||
      ~title="Dropdown",
 | 
			
		||||
      ~children=[
 | 
			
		||||
        entry(~title="Dropdown1", ~render=simpleDropdown),
 | 
			
		||||
        entry(~title="Menu dropdown", ~render=menuDropdown),
 | 
			
		||||
      ],
 | 
			
		||||
    ),
 | 
			
		||||
  ];
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,39 @@
 | 
			
		|||
open FC.Base;
 | 
			
		||||
 | 
			
		||||
let simpleMenu = () =>
 | 
			
		||||
  <DropdownMenu title="Simple menu">
 | 
			
		||||
    Menu.(
 | 
			
		||||
      <Menu onClick={e => Js.log(e.key)}>
 | 
			
		||||
        <Item key="item1"> "1st menu item"->React.string </Item>
 | 
			
		||||
        <Item key="item2"> "2nd menu item"->React.string </Item>
 | 
			
		||||
        <Divider />
 | 
			
		||||
        <Item key="item3"> "3nd menu item"->React.string </Item>
 | 
			
		||||
      </Menu>
 | 
			
		||||
    )
 | 
			
		||||
  </DropdownMenu>;
 | 
			
		||||
 | 
			
		||||
let subMenu = () =>
 | 
			
		||||
  <DropdownMenu title="Submenu" trigger=Dropdown.Hover>
 | 
			
		||||
    Menu.(
 | 
			
		||||
      <Menu onClick={e => Js.log(e.key)}>
 | 
			
		||||
        <Item key="item1"> "Item1"->React.string </Item>
 | 
			
		||||
        <Divider />
 | 
			
		||||
        <Item key="item2"> "Item2"->React.string </Item>
 | 
			
		||||
        <SubMenu title="Submenu1">
 | 
			
		||||
          <Item key="item3"> "Item3"->React.string </Item>
 | 
			
		||||
          <Item key="item4"> "Item4"->React.string </Item>
 | 
			
		||||
        </SubMenu>
 | 
			
		||||
      </Menu>
 | 
			
		||||
    )
 | 
			
		||||
  </DropdownMenu>;
 | 
			
		||||
 | 
			
		||||
let entries =
 | 
			
		||||
  EntryTypes.[
 | 
			
		||||
    folder(
 | 
			
		||||
      ~title="Dropdown menu",
 | 
			
		||||
      ~children=[
 | 
			
		||||
        entry(~title="Simple", ~render=simpleMenu),
 | 
			
		||||
        entry(~title="Submenu", ~render=subMenu),
 | 
			
		||||
      ],
 | 
			
		||||
    ),
 | 
			
		||||
  ];
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,60 @@
 | 
			
		|||
open FC.Base;
 | 
			
		||||
 | 
			
		||||
let stringSelect = () =>
 | 
			
		||||
  <DropdownSelect
 | 
			
		||||
    initialValue=None
 | 
			
		||||
    values=[("key1", "Label 1"), ("key2", "Label 2"), ("key3", "Label 3")]
 | 
			
		||||
    onSelect={v =>
 | 
			
		||||
      switch (v) {
 | 
			
		||||
      | Some(k) => Js.log2("Selected ", k)
 | 
			
		||||
      | None => Js.log("Selected none")
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  />;
 | 
			
		||||
 | 
			
		||||
let intSelect = () =>
 | 
			
		||||
  <DropdownSelect
 | 
			
		||||
    initialValue={Some(2)}
 | 
			
		||||
    values=[(1, "Int label 1"), (2, "Int label 2"), (3, "Int label 3")]
 | 
			
		||||
    onSelect={v =>
 | 
			
		||||
      switch (v) {
 | 
			
		||||
      | Some(k) => Js.log2("Selected ", k)
 | 
			
		||||
      | None => Js.log("Selected none")
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  />;
 | 
			
		||||
 | 
			
		||||
type customType =
 | 
			
		||||
  | Option1
 | 
			
		||||
  | Option2
 | 
			
		||||
  | Option3;
 | 
			
		||||
 | 
			
		||||
let customSelect = () =>
 | 
			
		||||
  <DropdownSelect
 | 
			
		||||
    initialValue={Some(Option3)}
 | 
			
		||||
    values=[
 | 
			
		||||
      (Option1, "Option1"),
 | 
			
		||||
      (Option2, "Option2"),
 | 
			
		||||
      (Option3, "Option3"),
 | 
			
		||||
    ]
 | 
			
		||||
    onSelect={v =>
 | 
			
		||||
      switch (v) {
 | 
			
		||||
      | Some(Option1) => Js.log("Option 1")
 | 
			
		||||
      | Some(Option2) => Js.log("Option 2")
 | 
			
		||||
      | Some(Option3) => Js.log("Option 3")
 | 
			
		||||
      | None => Js.log("None")
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  />;
 | 
			
		||||
 | 
			
		||||
let entries =
 | 
			
		||||
  EntryTypes.[
 | 
			
		||||
    folder(
 | 
			
		||||
      ~title="DropdownSelect",
 | 
			
		||||
      ~children=[
 | 
			
		||||
        entry(~title="Select1", ~render=stringSelect),
 | 
			
		||||
        entry(~title="Int select", ~render=intSelect),
 | 
			
		||||
        entry(~title="Custom select", ~render=customSelect),
 | 
			
		||||
      ],
 | 
			
		||||
    ),
 | 
			
		||||
  ];
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
open FC;
 | 
			
		||||
 | 
			
		||||
let cdf: Types.Dist.t = {
 | 
			
		||||
  xs: [|0.2, 0.4, 0.6, 0.8, 1.0|],
 | 
			
		||||
  ys: [|0.2, 0.3, 0.5, 0.3, 0.2|],
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
let measurableForm = () => <MeasurableForm cdf />;
 | 
			
		||||
 | 
			
		||||
let entry =
 | 
			
		||||
  EntryTypes.(sidebar(~title="Question form", ~render=measurableForm));
 | 
			
		||||
							
								
								
									
										33
									
								
								foretold/components/showcase/entries/Showcase_Menu.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								foretold/components/showcase/entries/Showcase_Menu.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,33 @@
 | 
			
		|||
open FC.Base;
 | 
			
		||||
 | 
			
		||||
let simpleMenu = () =>
 | 
			
		||||
  Menu.(
 | 
			
		||||
    <Menu onClick={e => Js.log(e.key)}>
 | 
			
		||||
      <Item key="item1"> "Item1"->React.string </Item>
 | 
			
		||||
      <Item key="item2"> "Item2"->React.string </Item>
 | 
			
		||||
    </Menu>
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
let subMenu = () =>
 | 
			
		||||
  Menu.(
 | 
			
		||||
    <Menu onClick={e => Js.log(e.key)}>
 | 
			
		||||
      <Item key="item1"> "Item1"->React.string </Item>
 | 
			
		||||
      <Divider />
 | 
			
		||||
      <Item key="item2"> "Item2"->React.string </Item>
 | 
			
		||||
      <SubMenu title="Submenu1">
 | 
			
		||||
        <Item key="item3"> "Item3"->React.string </Item>
 | 
			
		||||
        <Item key="item4"> "Item3"->React.string </Item>
 | 
			
		||||
      </SubMenu>
 | 
			
		||||
    </Menu>
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
let entries =
 | 
			
		||||
  EntryTypes.[
 | 
			
		||||
    folder(
 | 
			
		||||
      ~title="Menu",
 | 
			
		||||
      ~children=[
 | 
			
		||||
        entry(~title="Simple", ~render=simpleMenu),
 | 
			
		||||
        entry(~title="Submenu", ~render=subMenu),
 | 
			
		||||
      ],
 | 
			
		||||
    ),
 | 
			
		||||
  ];
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,98 @@
 | 
			
		|||
let makeItem = (name, icon, bookmark): MyCommunities.item => {
 | 
			
		||||
  name,
 | 
			
		||||
  icon,
 | 
			
		||||
  bookmark,
 | 
			
		||||
  href: "",
 | 
			
		||||
  onClick: _ => (),
 | 
			
		||||
  onBookmark: _ => (),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
let backgroundBox =
 | 
			
		||||
  Css.(style([background(`hex("ccc")), padding(`em(3.))]));
 | 
			
		||||
 | 
			
		||||
let innerBox =
 | 
			
		||||
  Css.(
 | 
			
		||||
    style([
 | 
			
		||||
      fontSize(`rem(1.1)),
 | 
			
		||||
      width(`em(20.)),
 | 
			
		||||
      border(`px(1), `solid, `hex("d5d2d2")),
 | 
			
		||||
      padding2(~v=`em(0.5), ~h=`em(0.)),
 | 
			
		||||
      borderRadius(`px(5)),
 | 
			
		||||
      background(`hex("fff")),
 | 
			
		||||
    ])
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
let myCommunities = () =>
 | 
			
		||||
  <div className=backgroundBox>
 | 
			
		||||
    <div className=innerBox>
 | 
			
		||||
      <MyCommunities>
 | 
			
		||||
        <MyCommunities.Header name="FEEDS" />
 | 
			
		||||
        <MyCommunities.Item item={makeItem("Home", "HOME", false)} />
 | 
			
		||||
        <MyCommunities.Item
 | 
			
		||||
          item={makeItem("All Communities", "LIST", false)}
 | 
			
		||||
        />
 | 
			
		||||
        <MyCommunities.Header name="MY COMMUNITIES" />
 | 
			
		||||
        <MyCommunities.ChannelItem
 | 
			
		||||
          item={makeItem("Slate-Star-Codex 2019", "PEOPLE", false)}
 | 
			
		||||
        />
 | 
			
		||||
        <MyCommunities.ChannelItem
 | 
			
		||||
          item={makeItem("AI Questions", "PEOPLE", false)}
 | 
			
		||||
        />
 | 
			
		||||
        <MyCommunities.ChannelItem
 | 
			
		||||
          item={makeItem("AI Questions", "PEOPLE", true)}
 | 
			
		||||
        />
 | 
			
		||||
        <MyCommunities.ChannelItem
 | 
			
		||||
          item={makeItem("AI Questions", "PEOPLE", true)}
 | 
			
		||||
        />
 | 
			
		||||
        <MyCommunities.ChannelItem
 | 
			
		||||
          item={makeItem("AI Questions", "PEOPLE", false)}
 | 
			
		||||
        />
 | 
			
		||||
        <MyCommunities.ChannelItem
 | 
			
		||||
          item={makeItem("AI Questions", "PEOPLE", false)}
 | 
			
		||||
        />
 | 
			
		||||
        <MyCommunities.ChannelItem
 | 
			
		||||
          item={makeItem("AI Questions", "PEOPLE", false)}
 | 
			
		||||
        />
 | 
			
		||||
        <MyCommunities.ChannelItem
 | 
			
		||||
          item={makeItem("AI Questions", "PEOPLE", false)}
 | 
			
		||||
        />
 | 
			
		||||
        <MyCommunities.ChannelItem
 | 
			
		||||
          item={makeItem("AI Questions", "PEOPLE", true)}
 | 
			
		||||
        />
 | 
			
		||||
        <MyCommunities.ChannelItem
 | 
			
		||||
          item={makeItem("AI Questions", "PEOPLE", false)}
 | 
			
		||||
        />
 | 
			
		||||
        <MyCommunities.ChannelItem
 | 
			
		||||
          item={makeItem("AI Questions", "PEOPLE", false)}
 | 
			
		||||
        />
 | 
			
		||||
        <MyCommunities.ChannelItem
 | 
			
		||||
          item={makeItem("AI Questions", "PEOPLE", false)}
 | 
			
		||||
        />
 | 
			
		||||
        <MyCommunities.ChannelItem
 | 
			
		||||
          item={makeItem("AI Questions", "PEOPLE", false)}
 | 
			
		||||
        />
 | 
			
		||||
        <MyCommunities.ChannelItem
 | 
			
		||||
          item={makeItem("AI Questions", "PEOPLE", false)}
 | 
			
		||||
        />
 | 
			
		||||
        <MyCommunities.ChannelItem
 | 
			
		||||
          item={makeItem("AI Questions", "PEOPLE", false)}
 | 
			
		||||
        />
 | 
			
		||||
        <MyCommunities.ChannelItem
 | 
			
		||||
          item={makeItem("Other AI Questions", "LOCK", false)}
 | 
			
		||||
        />
 | 
			
		||||
        <MyCommunities.ChannelItem
 | 
			
		||||
          item={makeItem(
 | 
			
		||||
            "My Secret and Very Very Very Very Long-named Community",
 | 
			
		||||
            "LOCK",
 | 
			
		||||
            false,
 | 
			
		||||
          )}
 | 
			
		||||
        />
 | 
			
		||||
        <MyCommunities.Header name="OPTIONS" />
 | 
			
		||||
        <MyCommunities.Item
 | 
			
		||||
          item={makeItem("Create a New Community", "CIRCLE_PLUS", true)}
 | 
			
		||||
        />
 | 
			
		||||
      </MyCommunities>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>;
 | 
			
		||||
 | 
			
		||||
let entry = EntryTypes.(entry(~title="MyCommunities", ~render=myCommunities));
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,61 @@
 | 
			
		|||
open FC;
 | 
			
		||||
open FC.Base;
 | 
			
		||||
 | 
			
		||||
let numbers = [
 | 
			
		||||
  0.000000000000000000000000000001,
 | 
			
		||||
  0.00000000001,
 | 
			
		||||
  0.00000001,
 | 
			
		||||
  (-0.00000001),
 | 
			
		||||
  0.00000001200332,
 | 
			
		||||
  0.00001,
 | 
			
		||||
  0.0000130300033,
 | 
			
		||||
  0.01,
 | 
			
		||||
  (-0.01),
 | 
			
		||||
  0.010000303030,
 | 
			
		||||
  0.0,
 | 
			
		||||
  0.010001,
 | 
			
		||||
  1.0,
 | 
			
		||||
  (-1.0),
 | 
			
		||||
  1.1000,
 | 
			
		||||
  1.0100,
 | 
			
		||||
  1.0010,
 | 
			
		||||
  1.0001,
 | 
			
		||||
  100.0,
 | 
			
		||||
  100.5,
 | 
			
		||||
  (-100.5),
 | 
			
		||||
  1000000.0,
 | 
			
		||||
  1001001.0,
 | 
			
		||||
  100000000000.0,
 | 
			
		||||
  100000000000000000.0,
 | 
			
		||||
  10000000000000000000000.0,
 | 
			
		||||
  10000100000000000000000.0,
 | 
			
		||||
  1000000000000100000000000000000.0,
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
module NumbersDisplay = {
 | 
			
		||||
  [@react.component]
 | 
			
		||||
  let make = () => {
 | 
			
		||||
    <PageCard>
 | 
			
		||||
      <PageCard.HeaderRow>
 | 
			
		||||
        <PageCard.HeaderRow.Title>
 | 
			
		||||
          "NumberShower"->React.string
 | 
			
		||||
        </PageCard.HeaderRow.Title>
 | 
			
		||||
      </PageCard.HeaderRow>
 | 
			
		||||
      <div>
 | 
			
		||||
        {(
 | 
			
		||||
           numbers
 | 
			
		||||
           |> E.L.fmap(n =>
 | 
			
		||||
                <div key={n |> Js.Float.toString}>
 | 
			
		||||
                  <NumberShower number=n precision=3 />
 | 
			
		||||
                </div>
 | 
			
		||||
              )
 | 
			
		||||
           |> E.L.toArray
 | 
			
		||||
         )
 | 
			
		||||
         ->React.array}
 | 
			
		||||
      </div>
 | 
			
		||||
    </PageCard>;
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
let entry =
 | 
			
		||||
  EntryTypes.(entry(~title="NumberShower", ~render=() => <NumbersDisplay />));
 | 
			
		||||
							
								
								
									
										19
									
								
								foretold/components/showcase/entries/Showcase_PageCard.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								foretold/components/showcase/entries/Showcase_PageCard.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
open FC;
 | 
			
		||||
 | 
			
		||||
let render = () =>
 | 
			
		||||
  <PageCard>
 | 
			
		||||
    <PageCard.HeaderRow>
 | 
			
		||||
      <PageCard.HeaderRow.Title>
 | 
			
		||||
        "PageCard.HeaderRow.Title"->React.string
 | 
			
		||||
      </PageCard.HeaderRow.Title>
 | 
			
		||||
    </PageCard.HeaderRow>
 | 
			
		||||
    <PageCard.BodyPadding>
 | 
			
		||||
      <PageCard.H1> "PageCard.H1"->React.string </PageCard.H1>
 | 
			
		||||
      <PageCard.P> "PageCard.P"->React.string </PageCard.P>
 | 
			
		||||
    </PageCard.BodyPadding>
 | 
			
		||||
    <PageCard.Section border=`top background=`grey>
 | 
			
		||||
      "Section, grey + borderTop"->React.string
 | 
			
		||||
    </PageCard.Section>
 | 
			
		||||
  </PageCard>;
 | 
			
		||||
 | 
			
		||||
let entry = EntryTypes.(entry(~title="PageCard", ~render));
 | 
			
		||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										14051
									
								
								foretold/components/showcase/entries/samples/sample-measurements.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14051
									
								
								foretold/components/showcase/entries/samples/sample-measurements.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										23
									
								
								foretold/components/showcase/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								foretold/components/showcase/index.html
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,23 @@
 | 
			
		|||
<!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 rel="stylesheet" href="styles.css">-->
 | 
			
		||||
    <style>
 | 
			
		||||
        body {
 | 
			
		||||
            margin: 0;
 | 
			
		||||
        }
 | 
			
		||||
    </style>
 | 
			
		||||
    <title>Showcase</title>
 | 
			
		||||
</head>
 | 
			
		||||
 | 
			
		||||
<body>
 | 
			
		||||
    <div id="main"></div>
 | 
			
		||||
    <script src=" ./Index.bs.js "></script>
 | 
			
		||||
</body>
 | 
			
		||||
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										98
									
								
								foretold/components/src/AgentLink.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								foretold/components/src/AgentLink.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,98 @@
 | 
			
		|||
module Styles = {
 | 
			
		||||
  open Css;
 | 
			
		||||
  let avatarOuter =
 | 
			
		||||
    style([float(`left), position(`relative), marginRight(`em(0.4))]);
 | 
			
		||||
  let avatar = style([marginTop(`em(0.1)), float(`left)]);
 | 
			
		||||
  let ownerAvatarOuter =
 | 
			
		||||
    style([
 | 
			
		||||
      float(`left),
 | 
			
		||||
      width(`em(1.)),
 | 
			
		||||
      height(`percent(100.)),
 | 
			
		||||
      marginLeft(`em(0.1)),
 | 
			
		||||
    ]);
 | 
			
		||||
  let ownerAvatar = style([position(`absolute), bottom(`zero)]);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module Agent = {
 | 
			
		||||
  type user = {
 | 
			
		||||
    name: string,
 | 
			
		||||
    image: option(string),
 | 
			
		||||
    onClick: ReactEvent.Mouse.t => unit,
 | 
			
		||||
  }
 | 
			
		||||
  and bot = {
 | 
			
		||||
    name: string,
 | 
			
		||||
    image: option(string),
 | 
			
		||||
    onClick: ReactEvent.Mouse.t => unit,
 | 
			
		||||
    owner: option(t),
 | 
			
		||||
  }
 | 
			
		||||
  and t =
 | 
			
		||||
    | User(user)
 | 
			
		||||
    | Bot(bot);
 | 
			
		||||
 | 
			
		||||
  let onClick = agent =>
 | 
			
		||||
    switch (agent) {
 | 
			
		||||
    | User(u) => u.onClick
 | 
			
		||||
    | Bot(u) => u.onClick
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
  let name = agent =>
 | 
			
		||||
    switch (agent) {
 | 
			
		||||
    | User(u) => u.name
 | 
			
		||||
    | Bot(u) => u.name
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
  let image = agent =>
 | 
			
		||||
    switch (agent) {
 | 
			
		||||
    | User(u) => u.image
 | 
			
		||||
    | Bot(u) => u.image
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
  let owner = agent =>
 | 
			
		||||
    switch (agent) {
 | 
			
		||||
    | User(_) => None
 | 
			
		||||
    | Bot(b) => b.owner
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
  let makeUser = (~name: string, ~onClick=_ => (), ~image=?, ()): t =>
 | 
			
		||||
    User({name, image, onClick});
 | 
			
		||||
 | 
			
		||||
  let makeBot = (~name: string, ~onClick=_ => (), ~image=?, ~owner=?, ()): t =>
 | 
			
		||||
    Bot({name, image, onClick, owner});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module SubItem = {
 | 
			
		||||
  [@react.component]
 | 
			
		||||
  let make = (~agent: Agent.t, ~owner: option(Agent.t), ~className) =>
 | 
			
		||||
    <Link onClick={Agent.onClick(agent)} className>
 | 
			
		||||
      <div className=Styles.avatarOuter>
 | 
			
		||||
        <div className=Styles.avatar>
 | 
			
		||||
          <Avatar
 | 
			
		||||
            width=1.2
 | 
			
		||||
            src={
 | 
			
		||||
              Agent.image(agent)
 | 
			
		||||
              |> Rationale.Option.default(BotDefaultImage.botDefault)
 | 
			
		||||
            }
 | 
			
		||||
          />
 | 
			
		||||
        </div>
 | 
			
		||||
        {owner
 | 
			
		||||
         |> E.O.React.fmapOrNull(owner =>
 | 
			
		||||
              <div className=Styles.ownerAvatarOuter>
 | 
			
		||||
                <div className=Styles.ownerAvatar>
 | 
			
		||||
                  <Avatar
 | 
			
		||||
                    width=0.8
 | 
			
		||||
                    src={
 | 
			
		||||
                      Agent.image(owner)
 | 
			
		||||
                      |> Rationale.Option.default(BotDefaultImage.botDefault)
 | 
			
		||||
                    }
 | 
			
		||||
                  />
 | 
			
		||||
                </div>
 | 
			
		||||
              </div>
 | 
			
		||||
            )}
 | 
			
		||||
      </div>
 | 
			
		||||
      {Agent.name(agent) |> ReasonReact.string}
 | 
			
		||||
    </Link>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
[@react.component]
 | 
			
		||||
let make = (~agent: Agent.t, ~className="") =>
 | 
			
		||||
  <SubItem agent className owner={Agent.owner(agent)} />;
 | 
			
		||||
							
								
								
									
										28
									
								
								foretold/components/src/AppHeader.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								foretold/components/src/AppHeader.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,28 @@
 | 
			
		|||
open Base;
 | 
			
		||||
 | 
			
		||||
module Styles = {
 | 
			
		||||
  open Css;
 | 
			
		||||
  let outer =
 | 
			
		||||
    style(
 | 
			
		||||
      [
 | 
			
		||||
        padding2(~v=`em(1.0), ~h=`em(2.)),
 | 
			
		||||
        backgroundColor(`rgb((255, 255, 255))),
 | 
			
		||||
        position(`relative),
 | 
			
		||||
        boxShadows([
 | 
			
		||||
          Shadow.box(
 | 
			
		||||
            ~x=px(1),
 | 
			
		||||
            ~y=px(1),
 | 
			
		||||
            ~blur=px(2),
 | 
			
		||||
            ~spread=px(1),
 | 
			
		||||
            ~inset=false,
 | 
			
		||||
            `hex("dee5e9"),
 | 
			
		||||
          ),
 | 
			
		||||
        ]),
 | 
			
		||||
      ]
 | 
			
		||||
      @ BaseStyles.fullWidthFloatLeft,
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
[@react.component]
 | 
			
		||||
let make = (~links: ReasonReact.reactElement) =>
 | 
			
		||||
  <Div styles=[Styles.outer]> <Div float=`left> links </Div> </Div>;
 | 
			
		||||
							
								
								
									
										35
									
								
								foretold/components/src/Base/Alert.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								foretold/components/src/Base/Alert.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,35 @@
 | 
			
		|||
/* For the icons, font-awesome (or similar?) is a possibility */
 | 
			
		||||
 | 
			
		||||
module Styles = {
 | 
			
		||||
  open Css;
 | 
			
		||||
  let alertBox =
 | 
			
		||||
    style([
 | 
			
		||||
      borderRadius(Settings.BorderRadius.tight),
 | 
			
		||||
      padding2(~v=`em(0.5), ~h=`em(0.8)),
 | 
			
		||||
      marginBottom(`em(0.75)),
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
  // Colors from https://getbootstrap.com/docs/4.0/components/alerts/
 | 
			
		||||
  // They may look better on white background than grey/smokeWhite
 | 
			
		||||
  let colors = (t: Settings.Alert.t) =>
 | 
			
		||||
    style([
 | 
			
		||||
      color(Settings.Alert.color(t)),
 | 
			
		||||
      backgroundColor(Settings.Alert.background(t)),
 | 
			
		||||
    ]);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Primary - Communicate information like "Welcome, now you can do..."
 | 
			
		||||
 * Info - Less significant information
 | 
			
		||||
 * Success
 | 
			
		||||
 * Warning
 | 
			
		||||
 * Error
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
type type_ = [ | `primary | `info | `success | `warning | `error];
 | 
			
		||||
 | 
			
		||||
[@react.component]
 | 
			
		||||
let make = (~type_: type_=`info, ~children) => {
 | 
			
		||||
  let classes = Styles.alertBox ++ " " ++ Styles.colors(type_);
 | 
			
		||||
  <div className=classes> children </div>;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										25
									
								
								foretold/components/src/Base/Avatar.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								foretold/components/src/Base/Avatar.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,25 @@
 | 
			
		|||
module Styles = {
 | 
			
		||||
  open Css;
 | 
			
		||||
  let imageCropper = width =>
 | 
			
		||||
    style([
 | 
			
		||||
      Css.width(`em(width)),
 | 
			
		||||
      height(`em(width)),
 | 
			
		||||
      overflow(`hidden),
 | 
			
		||||
      float(`left),
 | 
			
		||||
      position(`relative),
 | 
			
		||||
      borderRadius(`percent(20.)),
 | 
			
		||||
    ]);
 | 
			
		||||
  let image =
 | 
			
		||||
    style([
 | 
			
		||||
      float(`left),
 | 
			
		||||
      margin2(~v=`zero, ~h=`auto),
 | 
			
		||||
      height(`auto),
 | 
			
		||||
      width(`percent(100.)),
 | 
			
		||||
    ]);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
[@react.component]
 | 
			
		||||
let make = (~src: string, ~width=1., ()) =>
 | 
			
		||||
  <span className={Styles.imageCropper(width)}>
 | 
			
		||||
    <img src className=Styles.image />
 | 
			
		||||
  </span>;
 | 
			
		||||
							
								
								
									
										19
									
								
								foretold/components/src/Base/Base.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								foretold/components/src/Base/Base.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
module Types = Types;
 | 
			
		||||
module Link = Link;
 | 
			
		||||
module Colors = Settings;
 | 
			
		||||
module Div = Div;
 | 
			
		||||
module E = E;
 | 
			
		||||
module Globals = Globals;
 | 
			
		||||
module BaseStyles = BaseStyles;
 | 
			
		||||
module Button = Button;
 | 
			
		||||
module InputLabel = InputLabel;
 | 
			
		||||
module TextInput = TextInput;
 | 
			
		||||
module TextArea = TextArea;
 | 
			
		||||
module DropdownSelect = DropdownSelect;
 | 
			
		||||
module Icon = Icon;
 | 
			
		||||
module Dropdown = Dropdown;
 | 
			
		||||
module Menu = Menu;
 | 
			
		||||
module DropdownMenu = DropdownMenu;
 | 
			
		||||
module TabList = TabList;
 | 
			
		||||
module Alert = Alert;
 | 
			
		||||
module Avatar = Avatar;
 | 
			
		||||
							
								
								
									
										8
									
								
								foretold/components/src/Base/BaseStyles.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								foretold/components/src/Base/BaseStyles.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
let floatLeft = Css.float(`left);
 | 
			
		||||
let floatRight = Css.float(`right);
 | 
			
		||||
let fullWidthFloatLeft =
 | 
			
		||||
  Css.[floatLeft, width(`percent(100.0)), boxSizing(`borderBox)];
 | 
			
		||||
 | 
			
		||||
let borderNone = Css.[borderBottom(`px(0), `solid, hex("fff"))];
 | 
			
		||||
 | 
			
		||||
let clear = Css.style([Css.clear(`both)]);
 | 
			
		||||
							
								
								
									
										135
									
								
								foretold/components/src/Base/Button.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								foretold/components/src/Base/Button.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,135 @@
 | 
			
		|||
type color = [ | `hex(Js.String.t)];
 | 
			
		||||
 | 
			
		||||
/* I made this contain strings instead of colors,
 | 
			
		||||
   because the type for background is different than
 | 
			
		||||
   that for the others, which made things pretty messy. */
 | 
			
		||||
 | 
			
		||||
type variantColors = {
 | 
			
		||||
  text: string,
 | 
			
		||||
  textHover: string,
 | 
			
		||||
  border: string,
 | 
			
		||||
  background: string,
 | 
			
		||||
  backgroundHover: string,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
type variant =
 | 
			
		||||
  | Primary
 | 
			
		||||
  | Secondary;
 | 
			
		||||
 | 
			
		||||
type size =
 | 
			
		||||
  | Small
 | 
			
		||||
  | MediumShort
 | 
			
		||||
  | Medium
 | 
			
		||||
  | Large;
 | 
			
		||||
 | 
			
		||||
let varantColors = (variant: variant) =>
 | 
			
		||||
  Settings.(
 | 
			
		||||
    switch (variant) {
 | 
			
		||||
    | Primary => {
 | 
			
		||||
        text: white |> toS,
 | 
			
		||||
        textHover: white |> toS,
 | 
			
		||||
        border: link |> toS,
 | 
			
		||||
        background: link |> toS,
 | 
			
		||||
        backgroundHover: linkHover |> toS,
 | 
			
		||||
      }
 | 
			
		||||
    | Secondary => {
 | 
			
		||||
        text: textDark |> toS,
 | 
			
		||||
        textHover: textDark |> toS,
 | 
			
		||||
        border: accentBlueO8 |> toS,
 | 
			
		||||
        background: white |> toS,
 | 
			
		||||
        backgroundHover: buttonHover |> toS,
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
let sizeStyles = size => {
 | 
			
		||||
  switch (size) {
 | 
			
		||||
  | Small =>
 | 
			
		||||
    Css.(
 | 
			
		||||
      style([
 | 
			
		||||
        padding2(~v=`em(0.15), ~h=`em(1.0)),
 | 
			
		||||
        fontSize(`px(14)),
 | 
			
		||||
        minHeight(`em(1.9)),
 | 
			
		||||
      ])
 | 
			
		||||
    )
 | 
			
		||||
  | MediumShort =>
 | 
			
		||||
    Css.(style([padding2(~v=`em(0.2), ~h=`em(1.1)), fontSize(`px(14))]))
 | 
			
		||||
  | Medium =>
 | 
			
		||||
    Css.(style([padding2(~v=`em(0.25), ~h=`em(1.4)), fontSize(`px(16))]))
 | 
			
		||||
  | Large =>
 | 
			
		||||
    Css.(style([padding2(~v=`em(0.5), ~h=`em(2.4)), fontSize(`px(16))]))
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
let styles = (~isDisabled=false, ~variant, ~size, ~fullWidth=false, ()) => {
 | 
			
		||||
  let colors = varantColors(variant);
 | 
			
		||||
 | 
			
		||||
  let main =
 | 
			
		||||
    Css.(
 | 
			
		||||
      style([
 | 
			
		||||
        fontFamily(Settings.Text.standardFont),
 | 
			
		||||
        lineHeight(`em(1.5)),
 | 
			
		||||
        cursor(`pointer),
 | 
			
		||||
        BaseStyles.floatLeft,
 | 
			
		||||
        borderRadius(Settings.BorderRadius.medium),
 | 
			
		||||
        border(`px(1), `solid, `hex(colors.border)),
 | 
			
		||||
        color(`hex(colors.text)),
 | 
			
		||||
        background(`hex(colors.background)),
 | 
			
		||||
        hover([
 | 
			
		||||
          background(`hex(colors.backgroundHover)),
 | 
			
		||||
          color(`hex(colors.textHover)),
 | 
			
		||||
        ]),
 | 
			
		||||
        transition(
 | 
			
		||||
          ~duration=Settings.Transitions.standardLength,
 | 
			
		||||
          "background",
 | 
			
		||||
        ),
 | 
			
		||||
      ])
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
  let disabledStyles =
 | 
			
		||||
    Css.(style([background(Settings.greydisabled), opacity(0.5)]));
 | 
			
		||||
 | 
			
		||||
  let fullWidthStyle =
 | 
			
		||||
    Css.(
 | 
			
		||||
      style([
 | 
			
		||||
        width(`percent(100.)),
 | 
			
		||||
        boxSizing(`borderBox),
 | 
			
		||||
        textAlign(`center),
 | 
			
		||||
        Css.float(`none),
 | 
			
		||||
        display(`block),
 | 
			
		||||
      ])
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
  let total =
 | 
			
		||||
    switch (isDisabled, fullWidth) {
 | 
			
		||||
    | (false, false) => main
 | 
			
		||||
    | (true, false) => Css.merge([main, disabledStyles])
 | 
			
		||||
    | (false, true) => Css.merge([main, fullWidthStyle])
 | 
			
		||||
    | (true, true) => Css.merge([main, disabledStyles, fullWidthStyle])
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
  Css.merge([sizeStyles(size), total]);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Button must be button
 | 
			
		||||
 | 
			
		||||
[@react.component]
 | 
			
		||||
let make =
 | 
			
		||||
    (
 | 
			
		||||
      ~onClick=?,
 | 
			
		||||
      ~variant=Secondary,
 | 
			
		||||
      ~size=Medium,
 | 
			
		||||
      ~isDisabled=false,
 | 
			
		||||
      ~fullWidth=false,
 | 
			
		||||
      ~className="",
 | 
			
		||||
      ~children=ReasonReact.null,
 | 
			
		||||
    ) =>
 | 
			
		||||
  <button
 | 
			
		||||
    ?onClick
 | 
			
		||||
    disabled=isDisabled
 | 
			
		||||
    className={Css.merge([
 | 
			
		||||
      styles(~isDisabled, ~fullWidth, ~variant, ~size, ()),
 | 
			
		||||
      className,
 | 
			
		||||
    ])}>
 | 
			
		||||
    children
 | 
			
		||||
  </button>;
 | 
			
		||||
							
								
								
									
										48
									
								
								foretold/components/src/Base/Div.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								foretold/components/src/Base/Div.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,48 @@
 | 
			
		|||
open Css;
 | 
			
		||||
 | 
			
		||||
let fnWithDefault = (fn, r) =>
 | 
			
		||||
  r |> E.O.fmap(e => Css.style(fn(e))) |> E.O.default("");
 | 
			
		||||
 | 
			
		||||
/* TODO: Instead of accepting styles, this should accept "classNames" and use Css.merge */
 | 
			
		||||
 | 
			
		||||
[@react.component]
 | 
			
		||||
let make =
 | 
			
		||||
    (
 | 
			
		||||
      ~styles=[],
 | 
			
		||||
      ~className="",
 | 
			
		||||
      ~flex=?,
 | 
			
		||||
      ~flexDirection=?,
 | 
			
		||||
      ~float=?,
 | 
			
		||||
      ~alignItems=?,
 | 
			
		||||
      ~justifyContent=?,
 | 
			
		||||
      ~alignContent=?,
 | 
			
		||||
      ~onClick=_ => (),
 | 
			
		||||
      ~children=ReasonReact.null,
 | 
			
		||||
    ) => {
 | 
			
		||||
  let flexStyle = flex |> fnWithDefault(e => [Css.flex(e)]);
 | 
			
		||||
  let floatStyle = float |> fnWithDefault(e => [Css.float(e)]);
 | 
			
		||||
  let alignItemsStyle =
 | 
			
		||||
    alignItems |> fnWithDefault(e => [Css.alignItems(e)]);
 | 
			
		||||
  let justifyContentStyle =
 | 
			
		||||
    justifyContent |> fnWithDefault(e => [Css.justifyContent(e)]);
 | 
			
		||||
  let alignContentStyle =
 | 
			
		||||
    alignContent |> fnWithDefault(e => [Css.alignContent(e)]);
 | 
			
		||||
 | 
			
		||||
  let directionStyle =
 | 
			
		||||
    flexDirection
 | 
			
		||||
    |> fnWithDefault(e => [display(`flex), Css.flexDirection(e)]);
 | 
			
		||||
 | 
			
		||||
  let allStyles =
 | 
			
		||||
    Css.merge([
 | 
			
		||||
      flexStyle,
 | 
			
		||||
      directionStyle,
 | 
			
		||||
      floatStyle,
 | 
			
		||||
      alignItemsStyle,
 | 
			
		||||
      justifyContentStyle,
 | 
			
		||||
      alignContentStyle,
 | 
			
		||||
      className,
 | 
			
		||||
      ...styles,
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
  <div className=allStyles onClick> children </div>;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										104
									
								
								foretold/components/src/Base/Dropdown.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								foretold/components/src/Base/Dropdown.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,104 @@
 | 
			
		|||
/**
 | 
			
		||||
 * Dropdown component provides a way to show an overlay element
 | 
			
		||||
 * at a position relative to it's trigger element (the children given
 | 
			
		||||
 * to this element), on various triggers: Click, Hover, ContextMenu and Focus.
 | 
			
		||||
 *
 | 
			
		||||
 * It is bindings to https://github.com/react-component/dropdown
 | 
			
		||||
 */
 | 
			
		||||
[@bs.module "rc-dropdown"]
 | 
			
		||||
external rcDropDownClass: ReasonReact.reactClass = "default";
 | 
			
		||||
 | 
			
		||||
[%bs.raw {|require("rc-dropdown/assets/index.css")|}];
 | 
			
		||||
 | 
			
		||||
/** On what event to trigger dropdown
 | 
			
		||||
 * rc-trigger supports an array of triggers.
 | 
			
		||||
 * This is currently not encoded here currently as
 | 
			
		||||
 * it was considered a rarer case, many combinations are superfluous,
 | 
			
		||||
 * and delayed in interest of simplicity.
 | 
			
		||||
 * If there is only one or a few more, one possiblity
 | 
			
		||||
 * would be a variant like ClickFocus.
 | 
			
		||||
 */
 | 
			
		||||
type trigger =
 | 
			
		||||
  | Click
 | 
			
		||||
  | Hover
 | 
			
		||||
  | ContextMenu
 | 
			
		||||
  | Focus;
 | 
			
		||||
 | 
			
		||||
let triggerToString = trigger =>
 | 
			
		||||
  switch (trigger) {
 | 
			
		||||
  | Click => "click"
 | 
			
		||||
  | Hover => "hover"
 | 
			
		||||
  | ContextMenu => "contextMenu"
 | 
			
		||||
  | Focus => "focus"
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
/* In the documentation of rc-dropdown, the overlay
 | 
			
		||||
   property is specified to be a rc-menu,
 | 
			
		||||
   but this doesn't seem to be a hard
 | 
			
		||||
   requirement in the source */
 | 
			
		||||
[@bs.deriving abstract]
 | 
			
		||||
type jsProps = {
 | 
			
		||||
  overlay: ReasonReact.reactElement,
 | 
			
		||||
  trigger: array(string),
 | 
			
		||||
  prefixCls: string,
 | 
			
		||||
  overlayClassName: string,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module Styles = {
 | 
			
		||||
  open Css;
 | 
			
		||||
  // Styles are based on a prefixCls given to the element
 | 
			
		||||
  // Some styles can be applied to all dropdowns, while
 | 
			
		||||
  // others are more specific
 | 
			
		||||
 | 
			
		||||
  // General
 | 
			
		||||
  // First overlay element, this doesn't apply to submenues, but
 | 
			
		||||
  // all kinds of direct overlays
 | 
			
		||||
  global(
 | 
			
		||||
    ".ft-overlay",
 | 
			
		||||
    [
 | 
			
		||||
      fontFamily(Settings.Text.standardFont),
 | 
			
		||||
      fontSize(`rem(1.)),
 | 
			
		||||
      lineHeight(`rem(1.0)),
 | 
			
		||||
      zIndex(1070),
 | 
			
		||||
      position(`absolute),
 | 
			
		||||
      left(`px(-9999)),
 | 
			
		||||
      top(`px(-9999)),
 | 
			
		||||
    ],
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Dropdown component provides a way to show an overlay element
 | 
			
		||||
 * at a position relative to it's trigger element (the children given
 | 
			
		||||
 * to this element), on various triggers: Click, Hover, ContextMenu and Focus.
 | 
			
		||||
 * Can be used for example with the Menu component as overlay
 | 
			
		||||
 *
 | 
			
		||||
 * Usage:
 | 
			
		||||
 * ```
 | 
			
		||||
 * let staticOverlay = <div> {"Todo: style" |> React.string} </div>;
 | 
			
		||||
 *
 | 
			
		||||
 * <Dropdown overlay=staticOverlay trigger=Dropdown.Click>
 | 
			
		||||
 *   <div> {"Trigger element" |> React.string} </div>
 | 
			
		||||
 * </Dropdown>
 | 
			
		||||
 * ```
 | 
			
		||||
 */
 | 
			
		||||
[@react.component]
 | 
			
		||||
let make =
 | 
			
		||||
    (
 | 
			
		||||
      ~overlay,
 | 
			
		||||
      ~trigger=Hover,
 | 
			
		||||
      ~prefixCls="rc-dropdown",
 | 
			
		||||
      ~children=ReasonReact.null,
 | 
			
		||||
    ) =>
 | 
			
		||||
  ReasonReact.wrapJsForReason(
 | 
			
		||||
    ~reactClass=rcDropDownClass,
 | 
			
		||||
    ~props=
 | 
			
		||||
      jsProps(
 | 
			
		||||
        ~overlay,
 | 
			
		||||
        ~trigger=[|triggerToString(trigger)|],
 | 
			
		||||
        ~overlayClassName="ft-overlay",
 | 
			
		||||
        ~prefixCls,
 | 
			
		||||
      ),
 | 
			
		||||
    children,
 | 
			
		||||
  )
 | 
			
		||||
  |> ReasonReact.element;
 | 
			
		||||
							
								
								
									
										58
									
								
								foretold/components/src/Base/DropdownMenu.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								foretold/components/src/Base/DropdownMenu.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,58 @@
 | 
			
		|||
module Styles = {
 | 
			
		||||
  open Css;
 | 
			
		||||
  module Colors = Settings;
 | 
			
		||||
 | 
			
		||||
  // Reverting to "rc-dropdown" brings back some styles from the original css file
 | 
			
		||||
  let prefixCls = "ft-dropdown";
 | 
			
		||||
  let itemVerticalPadding = 5;
 | 
			
		||||
  let itemHorizontalPadding = 12;
 | 
			
		||||
  let bgColor = Colors.white;
 | 
			
		||||
  let textColor = Colors.textDarker;
 | 
			
		||||
  let hoverColor = `hex("ebfaff");
 | 
			
		||||
  let textSize = `rem(0.8);
 | 
			
		||||
  let textLineHeight = `em(1.3);
 | 
			
		||||
 | 
			
		||||
  Menu.Styles.createStyle(
 | 
			
		||||
    ~prefixCls,
 | 
			
		||||
    ~itemVerticalPadding,
 | 
			
		||||
    ~itemHorizontalPadding,
 | 
			
		||||
    ~textColor,
 | 
			
		||||
    ~bgColor,
 | 
			
		||||
    ~hoverColor,
 | 
			
		||||
    ~textSize,
 | 
			
		||||
    ~textLineHeight,
 | 
			
		||||
    (),
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  let dropdownTrigger =
 | 
			
		||||
    style([
 | 
			
		||||
      border(`px(1), `solid, Settings.border),
 | 
			
		||||
      borderRadius(`px(3)),
 | 
			
		||||
      // Subtracting 2 from horizontal padding to account for border
 | 
			
		||||
      padding2(
 | 
			
		||||
        ~v=`px(itemVerticalPadding),
 | 
			
		||||
        ~h=`px(itemHorizontalPadding - 2),
 | 
			
		||||
      ),
 | 
			
		||||
      backgroundColor(bgColor),
 | 
			
		||||
      fontSize(textSize),
 | 
			
		||||
      lineHeight(textLineHeight),
 | 
			
		||||
      fontFamily(Settings.Text.standardFont),
 | 
			
		||||
      color(textColor),
 | 
			
		||||
      // Selector for trigger element with overlay open
 | 
			
		||||
      selector(
 | 
			
		||||
        "&." ++ prefixCls ++ "-open",
 | 
			
		||||
        [borderColor(`hex("40a9ff")), color(`hex("40a9ff"))],
 | 
			
		||||
      ),
 | 
			
		||||
    ]);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
[@react.component]
 | 
			
		||||
let make = (~title, ~trigger=Dropdown.Hover, ~children) => {
 | 
			
		||||
  let overlay = <div> children </div>;
 | 
			
		||||
  <Dropdown trigger overlay prefixCls=Styles.prefixCls>
 | 
			
		||||
    <button className=Styles.dropdownTrigger>
 | 
			
		||||
      <span> {title |> React.string} </span>
 | 
			
		||||
      <Icon.DownArrow />
 | 
			
		||||
    </button>
 | 
			
		||||
  </Dropdown>;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										175
									
								
								foretold/components/src/Base/E.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								foretold/components/src/Base/E.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,175 @@
 | 
			
		|||
/* O for option */
 | 
			
		||||
open Rationale.Function.Infix;
 | 
			
		||||
 | 
			
		||||
/* 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 toExn = Rationale.Option.toExn;
 | 
			
		||||
  let some = Rationale.Option.some;
 | 
			
		||||
  let flatApply = (fn, b) =>
 | 
			
		||||
    Rationale.Option.apply(fn, Some(b)) |> Rationale.Option.flatten;
 | 
			
		||||
 | 
			
		||||
  let toResult = (error, e) =>
 | 
			
		||||
    switch (e) {
 | 
			
		||||
    | Some(r) => Belt.Result.Ok(r)
 | 
			
		||||
    | None => Error(error)
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
  module React = {
 | 
			
		||||
    let defaultNull = default(ReasonReact.null);
 | 
			
		||||
    let fmapOrNull = fn => fmap(fn) ||> default(ReasonReact.null);
 | 
			
		||||
    let flatten = default(ReasonReact.null);
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module Bool = {
 | 
			
		||||
  type t = bool;
 | 
			
		||||
  let toString = (t: t) => t ? "TRUE" : "FALSE";
 | 
			
		||||
  let fromString = str => str == "TRUE" ? true : false;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module Float = {
 | 
			
		||||
  let toString = Js.Float.toFixed;
 | 
			
		||||
  let with3DigitsPrecision = Js.Float.toPrecisionWithPrecision(_, ~digits=3);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module I = {
 | 
			
		||||
  let increment = n => n + 1;
 | 
			
		||||
  let decrement = n => n - 1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
let safe_fn_of_string = (fn, s: string): option('a) =>
 | 
			
		||||
  try(Some(fn(s))) {
 | 
			
		||||
  | _ => None
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
module S = {
 | 
			
		||||
  let toReact = ReasonReact.string;
 | 
			
		||||
  let safe_float = float_of_string->safe_fn_of_string;
 | 
			
		||||
  let safe_int = int_of_string->safe_fn_of_string;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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 toArray = Array.of_list;
 | 
			
		||||
  let fmapi = List.mapi;
 | 
			
		||||
  let concat = List.concat;
 | 
			
		||||
  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;
 | 
			
		||||
  let withIdx = xs =>
 | 
			
		||||
    xs
 | 
			
		||||
    |> Rationale.RList.zip(
 | 
			
		||||
         Rationale.RList.times(Rationale.Function.identity, length(xs)),
 | 
			
		||||
       );
 | 
			
		||||
  module React = {
 | 
			
		||||
    let fmap = (f, xs) => xs |> fmap(f) |> toArray |> React.array;
 | 
			
		||||
    let fmapi = (f, xs) => xs |> fmapi(f) |> toArray |> React.array;
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* A for Array */
 | 
			
		||||
module A = {
 | 
			
		||||
  let fmap: ('a => 'b, array('a)) => array('b) = 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 fold_left = Array.fold_left;
 | 
			
		||||
  let fold_right = Array.fold_right;
 | 
			
		||||
  let concatMany = Belt.Array.concatMany;
 | 
			
		||||
  let keepMap = Belt.Array.keepMap;
 | 
			
		||||
  let stableSortBy = Belt.SortArray.stableSortBy;
 | 
			
		||||
  /* TODO: Is there a better way of doing this? */
 | 
			
		||||
 | 
			
		||||
  /* TODO: Is -1 still the indicator that this is false (as is true with 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: 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 concatSome = (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 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;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module FloatArray = {
 | 
			
		||||
  let min = r => r |> A.fold_left((a, b) => a < b ? a : b, max_float);
 | 
			
		||||
  let max = r => r |> A.fold_left((a, b) => a > b ? a : b, min_float);
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										17
									
								
								foretold/components/src/Base/Globals.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								foretold/components/src/Base/Globals.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
let load = () => {
 | 
			
		||||
  Css.(
 | 
			
		||||
    global(
 | 
			
		||||
      "body",
 | 
			
		||||
      [
 | 
			
		||||
        fontFamily(Settings.Text.standardFont),
 | 
			
		||||
        margin(`zero),
 | 
			
		||||
        height(`percent(100.0)),
 | 
			
		||||
        background(`hex("F0F1F3")),
 | 
			
		||||
        fontSize(`px(16)),
 | 
			
		||||
        lineHeight(`em(1.5)),
 | 
			
		||||
      ],
 | 
			
		||||
    )
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  Css.(global("html", [height(`percent(100.0))]));
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										61
									
								
								foretold/components/src/Base/Icon.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								foretold/components/src/Base/Icon.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,61 @@
 | 
			
		|||
module Questionmark = {
 | 
			
		||||
  // Adapted to pagecard title
 | 
			
		||||
  let backgroundBlue = Css.background(`hex("C0D0E9"));
 | 
			
		||||
  let mainBlue = `hex("#1c67c8");
 | 
			
		||||
  let circle =
 | 
			
		||||
    Css.(
 | 
			
		||||
      style([
 | 
			
		||||
        width(`em(1.0)),
 | 
			
		||||
        height(`em(1.0)),
 | 
			
		||||
        borderRadius(`percent(50.)),
 | 
			
		||||
        backgroundBlue,
 | 
			
		||||
      ])
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
  let insideStyle =
 | 
			
		||||
    Css.(style([color(mainBlue), cursor(`pointer), fontSize(`em(0.9))]));
 | 
			
		||||
  // Local icon style
 | 
			
		||||
  let questionMarkstyle = isInteractive =>
 | 
			
		||||
    Css.(
 | 
			
		||||
      style([
 | 
			
		||||
        width(`em(1.0)),
 | 
			
		||||
        height(`em(1.0)),
 | 
			
		||||
        textAlign(`center),
 | 
			
		||||
        borderRadius(`percent(50.)),
 | 
			
		||||
        display(`inlineBlock),
 | 
			
		||||
        backgroundBlue,
 | 
			
		||||
        lineHeight(`em(1.0)),
 | 
			
		||||
        fontSize(`em(1.0)),
 | 
			
		||||
        opacity(0.6),
 | 
			
		||||
        fontWeight(`num(600)),
 | 
			
		||||
        cursor(`pointer),
 | 
			
		||||
        hover(isInteractive ? [opacity(1.0)] : []),
 | 
			
		||||
      ])
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
  [@react.component]
 | 
			
		||||
  let make = (~isInteractive=true) =>
 | 
			
		||||
    <div className={questionMarkstyle(isInteractive)}>
 | 
			
		||||
      <span className=insideStyle> {React.string("?")} </span>
 | 
			
		||||
    </div>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module DownArrow = {
 | 
			
		||||
  /* Down array from ant */
 | 
			
		||||
  let buttonStyle = Css.(style([marginLeft(`px(8))]));
 | 
			
		||||
 | 
			
		||||
  [@react.component]
 | 
			
		||||
  let make = () =>
 | 
			
		||||
    <svg
 | 
			
		||||
      className=buttonStyle
 | 
			
		||||
      viewBox="64 64 896 896"
 | 
			
		||||
      width="0.8em"
 | 
			
		||||
      height="0.8em"
 | 
			
		||||
      fill="currentColor"
 | 
			
		||||
      ariaHidden=true
 | 
			
		||||
      focusable="false">
 | 
			
		||||
      <path
 | 
			
		||||
        d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"
 | 
			
		||||
      />
 | 
			
		||||
    </svg>;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										29
									
								
								foretold/components/src/Base/Link.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								foretold/components/src/Base/Link.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,29 @@
 | 
			
		|||
let headerLink = (~className, ~isDisabled=false, ()) => {
 | 
			
		||||
  let primaryStyles =
 | 
			
		||||
    Css.(
 | 
			
		||||
      style([
 | 
			
		||||
        textDecoration(`none),
 | 
			
		||||
        userSelect(`none),
 | 
			
		||||
        color(Settings.Text.LightBackground.main),
 | 
			
		||||
        hover([color(Settings.Text.LightBackground.light)]),
 | 
			
		||||
        focus([textDecoration(`none)]),
 | 
			
		||||
      ])
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
  let disabledStyles =
 | 
			
		||||
    isDisabled ? Css.(style([pointerEvents(`none), cursor(`default)])) : "";
 | 
			
		||||
 | 
			
		||||
  Css.(merge([primaryStyles, className, disabledStyles]));
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
[@react.component]
 | 
			
		||||
let make =
 | 
			
		||||
    (~href="#", ~onClick=?, ~isDisabled=false, ~className="", ~children) => {
 | 
			
		||||
  <a
 | 
			
		||||
    disabled=isDisabled
 | 
			
		||||
    href
 | 
			
		||||
    className={headerLink(~className, ~isDisabled, ())}
 | 
			
		||||
    ?onClick>
 | 
			
		||||
    children
 | 
			
		||||
  </a>;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										375
									
								
								foretold/components/src/Base/Menu.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										375
									
								
								foretold/components/src/Base/Menu.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,375 @@
 | 
			
		|||
/**
 | 
			
		||||
 * Menu component and sibling components provides define menu items,
 | 
			
		||||
 * submenues and dividers. See make for usage.
 | 
			
		||||
 *
 | 
			
		||||
 * It is binding to https://github.com/react-component/menu
 | 
			
		||||
 */
 | 
			
		||||
[@bs.module "rc-menu"]
 | 
			
		||||
external rcMenuClass: ReasonReact.reactClass = "default";
 | 
			
		||||
 | 
			
		||||
[@bs.module "rc-menu"]
 | 
			
		||||
external rcSubMenuClass: ReasonReact.reactClass = "SubMenu";
 | 
			
		||||
 | 
			
		||||
[@bs.module "rc-menu"] external rcItemClass: ReasonReact.reactClass = "Item";
 | 
			
		||||
 | 
			
		||||
[@bs.module "rc-menu"]
 | 
			
		||||
external rcDividerClass: ReasonReact.reactClass = "Divider";
 | 
			
		||||
 | 
			
		||||
[%bs.raw {|require("rc-menu/assets/index.css")|}];
 | 
			
		||||
 | 
			
		||||
module Styles = {
 | 
			
		||||
  open Css;
 | 
			
		||||
  let menuBorderRadius = `px(3);
 | 
			
		||||
  // Menu general, applied to menu <ul> elements
 | 
			
		||||
  global(
 | 
			
		||||
    ".ft-menu-general, .ft-submenu-general>ul",
 | 
			
		||||
    [
 | 
			
		||||
      fontFamily(Settings.Text.standardFont),
 | 
			
		||||
      listStyleType(`none),
 | 
			
		||||
      position(`relative),
 | 
			
		||||
      outlineStyle(`none),
 | 
			
		||||
      borderRadius(menuBorderRadius),
 | 
			
		||||
      borderStyle(`none),
 | 
			
		||||
      boxShadows([
 | 
			
		||||
        Shadow.box(~x=zero, ~y=px(2), ~blur=px(8), rgba(0, 0, 0, 0.15)),
 | 
			
		||||
      ]),
 | 
			
		||||
      margin(`zero),
 | 
			
		||||
      padding2(~v=`px(4), ~h=`zero),
 | 
			
		||||
      // This is applied to menu-items, submenu pointers, and dividers
 | 
			
		||||
      selector(
 | 
			
		||||
        ">li",
 | 
			
		||||
        [
 | 
			
		||||
          // Used to place icon in submenu item currently
 | 
			
		||||
          position(`relative),
 | 
			
		||||
          display(`block),
 | 
			
		||||
          clear(`both),
 | 
			
		||||
          whiteSpace(`nowrap),
 | 
			
		||||
          cursor(`default),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
      // Taken from default stylesheet, might prevent some small bug
 | 
			
		||||
      // with border radius
 | 
			
		||||
      selector(
 | 
			
		||||
        ">li.ft-menu-item-general:first-child",
 | 
			
		||||
        [
 | 
			
		||||
          borderTopLeftRadius(menuBorderRadius),
 | 
			
		||||
          borderTopRightRadius(menuBorderRadius),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
    ],
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  // Applied to div of submenu
 | 
			
		||||
  global(
 | 
			
		||||
    ".ft-submenu-general",
 | 
			
		||||
    [position(`absolute), minWidth(`px(100))],
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Create style for menu tied to a "prefixCls"
 | 
			
		||||
   * This function injects global styles for
 | 
			
		||||
   * classNames based on prefixCls. It is meant
 | 
			
		||||
   * to be called only once per prefix, for example
 | 
			
		||||
   * on a module level.
 | 
			
		||||
   * For an example, see DropdownMenu
 | 
			
		||||
   */
 | 
			
		||||
  let createStyle =
 | 
			
		||||
      (
 | 
			
		||||
        ~prefixCls,
 | 
			
		||||
        ~itemVerticalPadding,
 | 
			
		||||
        ~itemHorizontalPadding,
 | 
			
		||||
        ~textColor,
 | 
			
		||||
        ~bgColor,
 | 
			
		||||
        ~hoverColor,
 | 
			
		||||
        ~textSize,
 | 
			
		||||
        ~textLineHeight,
 | 
			
		||||
        (),
 | 
			
		||||
      ) => {
 | 
			
		||||
    let prefixPlus = ext => "." ++ prefixCls ++ "-" ++ ext;
 | 
			
		||||
 | 
			
		||||
    // Root
 | 
			
		||||
    // Dropdown root element. This, like submenu popups, are placed
 | 
			
		||||
    // in generated divs right inside document body element. Ie only
 | 
			
		||||
    // influenced by top level styles.
 | 
			
		||||
    // Note that submenus get their own top level element, so this
 | 
			
		||||
    // does not apply to submenus
 | 
			
		||||
    // Note: There is a style in Dropdown that takes care
 | 
			
		||||
    // of position absolute and zindex
 | 
			
		||||
    //global("." ++ prefixCls, []);
 | 
			
		||||
 | 
			
		||||
    // Menu ul
 | 
			
		||||
    // <ul> element that contain the menu items in it's <li>'s
 | 
			
		||||
    // Both root menu and submenus have this class.
 | 
			
		||||
    // This ul element will receive the -hidden class when hidden
 | 
			
		||||
    // Move from Menu.Styles > "ft-menu-general" when styles
 | 
			
		||||
    // need variation
 | 
			
		||||
    global(
 | 
			
		||||
      "ul" ++ prefixPlus("menu"),
 | 
			
		||||
      [
 | 
			
		||||
        lineHeight(textLineHeight),
 | 
			
		||||
        backgroundColor(bgColor),
 | 
			
		||||
        // Menu-item
 | 
			
		||||
        selector(
 | 
			
		||||
          "li" ++ prefixPlus("menu-item"),
 | 
			
		||||
          [
 | 
			
		||||
            fontSize(textSize),
 | 
			
		||||
            color(textColor),
 | 
			
		||||
            selector(":hover", [backgroundColor(hoverColor)]),
 | 
			
		||||
            padding2(
 | 
			
		||||
              ~v=`px(itemVerticalPadding),
 | 
			
		||||
              ~h=`px(itemHorizontalPadding),
 | 
			
		||||
            ),
 | 
			
		||||
            // Reversing outside element padding and adding it on the <a>
 | 
			
		||||
            // Not sure how needed this will be (taken from ant)
 | 
			
		||||
            selector(
 | 
			
		||||
              ">a",
 | 
			
		||||
              [
 | 
			
		||||
                display(`block),
 | 
			
		||||
                margin2(
 | 
			
		||||
                  ~v=`px(- itemVerticalPadding),
 | 
			
		||||
                  ~h=`px(- itemHorizontalPadding),
 | 
			
		||||
                ),
 | 
			
		||||
                padding2(
 | 
			
		||||
                  ~v=`px(itemVerticalPadding),
 | 
			
		||||
                  ~h=`px(itemHorizontalPadding),
 | 
			
		||||
                ),
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
        // Submenu <li> pointer
 | 
			
		||||
        selector(
 | 
			
		||||
          ">li" ++ prefixPlus("menu-submenu"),
 | 
			
		||||
          [
 | 
			
		||||
            fontSize(textSize),
 | 
			
		||||
            color(textColor),
 | 
			
		||||
            selector(":hover", [backgroundColor(hoverColor)]),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
        selector(
 | 
			
		||||
          ">li" ++ prefixPlus("menu-submenu-active"),
 | 
			
		||||
          [backgroundColor(hoverColor)],
 | 
			
		||||
        ),
 | 
			
		||||
        // Divider
 | 
			
		||||
        // Can't move to general because the selector
 | 
			
		||||
        // is prefix specific
 | 
			
		||||
        selector(
 | 
			
		||||
          ">li" ++ prefixPlus("menu-item-divider"),
 | 
			
		||||
          [
 | 
			
		||||
            margin2(~v=`px(4), ~h=`zero),
 | 
			
		||||
            height(`px(1)),
 | 
			
		||||
            backgroundColor(`hex("e5e5e5")), // A little lighter than border I think
 | 
			
		||||
            lineHeight(`zero),
 | 
			
		||||
            overflow(`hidden),
 | 
			
		||||
            padding(`zero),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // Hidden menu
 | 
			
		||||
    // Class applied to submenu <ul>'s when menu is hidden. Elements are not initially
 | 
			
		||||
    // hidden, but added to the dom on demand. Therefore it is quirky to rely
 | 
			
		||||
    // on this class with regards to animations/transitions.
 | 
			
		||||
    // For transitions, see api of rc-dropdown/rc-menu
 | 
			
		||||
    global(prefixPlus("menu-hidden"), [display(`none)]);
 | 
			
		||||
    // Hidden root menu
 | 
			
		||||
    global(prefixPlus("hidden"), [display(`none)]);
 | 
			
		||||
 | 
			
		||||
    // Submenu
 | 
			
		||||
    // Submenu has a title element inside a div, and
 | 
			
		||||
    // a right arrow added with unicode
 | 
			
		||||
    global(
 | 
			
		||||
      prefixPlus("menu-submenu-title"),
 | 
			
		||||
      [
 | 
			
		||||
        padding4(
 | 
			
		||||
          ~top=`px(itemVerticalPadding),
 | 
			
		||||
          ~right=`px(itemHorizontalPadding * 2 + 5),
 | 
			
		||||
          ~bottom=`px(itemVerticalPadding),
 | 
			
		||||
          ~left=`px(itemHorizontalPadding),
 | 
			
		||||
        ),
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
    global(
 | 
			
		||||
      prefixPlus("menu-submenu-arrow"),
 | 
			
		||||
      [
 | 
			
		||||
        position(`absolute),
 | 
			
		||||
        right(`px(itemHorizontalPadding)),
 | 
			
		||||
        selector(
 | 
			
		||||
          "::before",
 | 
			
		||||
          [
 | 
			
		||||
            color(Settings.textMedium),
 | 
			
		||||
            fontStyle(`normal),
 | 
			
		||||
            fontSize(`em(0.7)),
 | 
			
		||||
            // One option here is to use font-awesome,
 | 
			
		||||
            // don't know how much use it would be elsewhere or how to do this,
 | 
			
		||||
            // rc-menu does this by default
 | 
			
		||||
            contentRule({js|\u25B6|js}),
 | 
			
		||||
          ],
 | 
			
		||||
        ),
 | 
			
		||||
      ],
 | 
			
		||||
    );
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module SubMenu = {
 | 
			
		||||
  // https://github.com/react-component/menu#menusubmenu-props
 | 
			
		||||
  [@bs.deriving abstract]
 | 
			
		||||
  type jsProps = {
 | 
			
		||||
    title: string,
 | 
			
		||||
    popupClassName: string,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  [@react.component]
 | 
			
		||||
  let make = (~title, ~children) =>
 | 
			
		||||
    ReasonReact.wrapJsForReason(
 | 
			
		||||
      ~reactClass=rcSubMenuClass,
 | 
			
		||||
      ~props=jsProps(~title, ~popupClassName="ft-submenu-general"),
 | 
			
		||||
      children,
 | 
			
		||||
    )
 | 
			
		||||
    |> ReasonReact.element;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module Item = {
 | 
			
		||||
  // https://github.com/react-component/menu#menuitem-props
 | 
			
		||||
  [@bs.deriving abstract]
 | 
			
		||||
  type jsProps = {
 | 
			
		||||
    disabled: bool,
 | 
			
		||||
    itemIcon: Js.Undefined.t(React.element),
 | 
			
		||||
    className: string,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  [@react.component]
 | 
			
		||||
  let make = (~disabled=false, ~itemIcon=None, ~children) =>
 | 
			
		||||
    ReasonReact.wrapJsForReason(
 | 
			
		||||
      ~reactClass=rcItemClass,
 | 
			
		||||
      ~props=
 | 
			
		||||
        jsProps(
 | 
			
		||||
          ~disabled,
 | 
			
		||||
          ~itemIcon=Js.Undefined.fromOption(itemIcon),
 | 
			
		||||
          ~className="ft-menu-item-general",
 | 
			
		||||
        ),
 | 
			
		||||
      children,
 | 
			
		||||
    )
 | 
			
		||||
    |> ReasonReact.element;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module Divider = {
 | 
			
		||||
  // https://github.com/react-component/menu#menuitem-props
 | 
			
		||||
 | 
			
		||||
  [@react.component]
 | 
			
		||||
  let make = (~children=ReasonReact.null) =>
 | 
			
		||||
    ReasonReact.wrapJsForReason(
 | 
			
		||||
      ~reactClass=rcDividerClass,
 | 
			
		||||
      ~props=Js.Obj.empty(),
 | 
			
		||||
      children,
 | 
			
		||||
    )
 | 
			
		||||
    |> ReasonReact.element;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
type callbackType =
 | 
			
		||||
  Js.Undefined.t(
 | 
			
		||||
    {
 | 
			
		||||
      .
 | 
			
		||||
      "key": string,
 | 
			
		||||
      "item": React.element,
 | 
			
		||||
      "domEvent": Dom.event,
 | 
			
		||||
      "keyPath": array(string),
 | 
			
		||||
    } =>
 | 
			
		||||
    unit,
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
// https://github.com/react-component/menu#menu-props
 | 
			
		||||
[@bs.deriving abstract]
 | 
			
		||||
type jsProps = {
 | 
			
		||||
  // Gave some namespace confusion on some call site
 | 
			
		||||
  // Possibly we could slim the callbacks a little
 | 
			
		||||
  // if there are unneeded arguments
 | 
			
		||||
  [@bs.as "onClick"]
 | 
			
		||||
  jsOnClick: callbackType,
 | 
			
		||||
  selectable: bool,
 | 
			
		||||
  selectedKeys: array(string),
 | 
			
		||||
  [@bs.as "onSelect"]
 | 
			
		||||
  jsOnSelect: callbackType,
 | 
			
		||||
  className: string,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
type clickInfo = {
 | 
			
		||||
  key: string,
 | 
			
		||||
  item: React.element,
 | 
			
		||||
  domEvent: Dom.event,
 | 
			
		||||
  keyPath: array(string),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Menu component and sibling components provides a way to define menu items,
 | 
			
		||||
 * submenues and dividers. One can register an onClick callback on the
 | 
			
		||||
 * <Menu> element to receive information about clicks.
 | 
			
		||||
 *
 | 
			
		||||
 * To resolve which menu item was clicked, the best way is probably
 | 
			
		||||
 * to provide a key attribute to the items, then use the key in
 | 
			
		||||
 * the callback. There is also a keyPath list with parents keys
 | 
			
		||||
 * in the case of a submenu, the dom Event and the react element
 | 
			
		||||
 * that was clicked.
 | 
			
		||||
 *
 | 
			
		||||
 * The menu could also act more like a select element, as implemented
 | 
			
		||||
 * in DropdownSelect.
 | 
			
		||||
 *
 | 
			
		||||
 * Usage:
 | 
			
		||||
 * ```
 | 
			
		||||
 * Menu.(
 | 
			
		||||
 *   <Menu onClick={info => Js.log(info.key)}>
 | 
			
		||||
 *     <Item key="item1"> { "Item 1" |> React.string } </Item>
 | 
			
		||||
 *     <SubMenu title="Submenu">
 | 
			
		||||
 *       <Item key="subitem1"> { "Subitem 1" |> React.string } </Item>
 | 
			
		||||
 *       <Divider />
 | 
			
		||||
 *       <Item key="subitem2"> { "Subitem 2" |> React.string } </Item>
 | 
			
		||||
 *     </SubMenu>
 | 
			
		||||
 *   </Menu>
 | 
			
		||||
 * )
 | 
			
		||||
 * ```
 | 
			
		||||
 */
 | 
			
		||||
[@react.component]
 | 
			
		||||
let make =
 | 
			
		||||
    (~onClick=?, ~selectable=false, ~onSelect=?, ~selectedKey=?, ~children) =>
 | 
			
		||||
  ReasonReact.wrapJsForReason(
 | 
			
		||||
    ~reactClass=rcMenuClass,
 | 
			
		||||
    ~props=
 | 
			
		||||
      jsProps(
 | 
			
		||||
        ~jsOnClick=
 | 
			
		||||
          switch (onClick) {
 | 
			
		||||
          | Some(onClick) =>
 | 
			
		||||
            Js.Undefined.return(info =>
 | 
			
		||||
              onClick({
 | 
			
		||||
                key: info##key,
 | 
			
		||||
                item: info##item,
 | 
			
		||||
                domEvent: info##domEvent,
 | 
			
		||||
                keyPath: info##keyPath,
 | 
			
		||||
              })
 | 
			
		||||
            )
 | 
			
		||||
          | None => Js.Undefined.empty
 | 
			
		||||
          },
 | 
			
		||||
        ~selectable,
 | 
			
		||||
        ~jsOnSelect=
 | 
			
		||||
          switch (onSelect) {
 | 
			
		||||
          | Some(onSelect) =>
 | 
			
		||||
            Js.Undefined.return(info =>
 | 
			
		||||
              onSelect({
 | 
			
		||||
                key: info##key,
 | 
			
		||||
                item: info##item,
 | 
			
		||||
                domEvent: info##domEvent,
 | 
			
		||||
                keyPath: info##keyPath,
 | 
			
		||||
              })
 | 
			
		||||
            )
 | 
			
		||||
          | None => Js.Undefined.empty
 | 
			
		||||
          },
 | 
			
		||||
        ~selectedKeys=
 | 
			
		||||
          switch (selectedKey) {
 | 
			
		||||
          | None => [||]
 | 
			
		||||
          | Some(key) => [|key|]
 | 
			
		||||
          },
 | 
			
		||||
        ~className="ft-menu-general",
 | 
			
		||||
      ),
 | 
			
		||||
    children,
 | 
			
		||||
  )  /* Menu item group is not implemented (https://react-component.github.io/menu/examples/menuItemGroup.html*/
 | 
			
		||||
  |> ReasonReact.element;
 | 
			
		||||
							
								
								
									
										127
									
								
								foretold/components/src/Base/ReactKitIcon.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								foretold/components/src/Base/ReactKitIcon.js
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,127 @@
 | 
			
		|||
/** @jsx React.DOM */
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import {
 | 
			
		||||
  Icon
 | 
			
		||||
} from 'react-icons-kit';
 | 
			
		||||
import {
 | 
			
		||||
  home
 | 
			
		||||
} from 'react-icons-kit/typicons/home';
 | 
			
		||||
import {
 | 
			
		||||
  user
 | 
			
		||||
} from 'react-icons-kit/ikons/user';
 | 
			
		||||
import {
 | 
			
		||||
  arrowLeft2
 | 
			
		||||
} from 'react-icons-kit/icomoon/arrowLeft2';
 | 
			
		||||
import {
 | 
			
		||||
  arrowRight2
 | 
			
		||||
} from 'react-icons-kit/icomoon/arrowRight2';
 | 
			
		||||
import {
 | 
			
		||||
  earth
 | 
			
		||||
} from 'react-icons-kit/icomoon/earth';
 | 
			
		||||
import {
 | 
			
		||||
  columns
 | 
			
		||||
} from 'react-icons-kit/ikons/columns';
 | 
			
		||||
import {
 | 
			
		||||
  ic_people
 | 
			
		||||
} from 'react-icons-kit/md/ic_people';
 | 
			
		||||
import {
 | 
			
		||||
  bulb
 | 
			
		||||
} from 'react-icons-kit/entypo/bulb';
 | 
			
		||||
import {
 | 
			
		||||
  socialBuffer
 | 
			
		||||
} from 'react-icons-kit/ionicons/socialBuffer';
 | 
			
		||||
import {
 | 
			
		||||
  flash
 | 
			
		||||
} from 'react-icons-kit/entypo/flash';
 | 
			
		||||
import {
 | 
			
		||||
  gavel
 | 
			
		||||
} from 'react-icons-kit/fa/gavel';
 | 
			
		||||
import {
 | 
			
		||||
  plus as circlePlus
 | 
			
		||||
} from 'react-icons-kit/metrize/plus';
 | 
			
		||||
import {
 | 
			
		||||
  chevronRight
 | 
			
		||||
} from 'react-icons-kit/ionicons/chevronRight';
 | 
			
		||||
import {
 | 
			
		||||
  chevronLeft
 | 
			
		||||
} from 'react-icons-kit/ionicons/chevronLeft';
 | 
			
		||||
import {
 | 
			
		||||
  lock
 | 
			
		||||
} from 'react-icons-kit/entypo/lock';
 | 
			
		||||
import {
 | 
			
		||||
  chevronDown
 | 
			
		||||
} from 'react-icons-kit/fa/chevronDown';
 | 
			
		||||
import {
 | 
			
		||||
  emailUnread
 | 
			
		||||
} from 'react-icons-kit/ionicons/emailUnread';
 | 
			
		||||
import {
 | 
			
		||||
  u26FA as tent
 | 
			
		||||
} from 'react-icons-kit/noto_emoji_regular/u26FA';
 | 
			
		||||
import {
 | 
			
		||||
  ic_content_copy
 | 
			
		||||
} from 'react-icons-kit/md/ic_content_copy';
 | 
			
		||||
import {
 | 
			
		||||
  magicWand
 | 
			
		||||
} from 'react-icons-kit/icomoon/magicWand';
 | 
			
		||||
import {
 | 
			
		||||
  pacman
 | 
			
		||||
} from 'react-icons-kit/icomoon/pacman';
 | 
			
		||||
import {
 | 
			
		||||
  thList as list
 | 
			
		||||
} from 'react-icons-kit/typicons/thList';
 | 
			
		||||
import {
 | 
			
		||||
  starFull
 | 
			
		||||
} from 'react-icons-kit/icomoon/starFull';
 | 
			
		||||
import {
 | 
			
		||||
  arrowBack
 | 
			
		||||
} from 'react-icons-kit/typicons/arrowBack';
 | 
			
		||||
import {
 | 
			
		||||
  reply
 | 
			
		||||
} from 'react-icons-kit/fa/reply';
 | 
			
		||||
import {
 | 
			
		||||
  close
 | 
			
		||||
} from 'react-icons-kit/fa/close';
 | 
			
		||||
 | 
			
		||||
let types = {
 | 
			
		||||
  'HOME': home,
 | 
			
		||||
  'LOCK': lock,
 | 
			
		||||
  'USER': user,
 | 
			
		||||
  'COLUMNS': columns,
 | 
			
		||||
  'EARTH': earth,
 | 
			
		||||
  'TENT': tent,
 | 
			
		||||
  'LAYERS': socialBuffer,
 | 
			
		||||
  'PEOPLE': ic_people,
 | 
			
		||||
  'FLASH': flash,
 | 
			
		||||
  'GAVEL': gavel,
 | 
			
		||||
  'BULB': bulb,
 | 
			
		||||
  'CIRCLE_PLUS': circlePlus,
 | 
			
		||||
  'ARROW_RIGHT': arrowRight2,
 | 
			
		||||
  'ARROW_LEFT': arrowLeft2,
 | 
			
		||||
  'CHEVRON_LEFT': chevronLeft,
 | 
			
		||||
  'CHEVRON_RIGHT': chevronRight,
 | 
			
		||||
  'CHEVRON_DOWN': chevronDown,
 | 
			
		||||
  'EMAIL_UNREAD': emailUnread,
 | 
			
		||||
  'COPY': ic_content_copy,
 | 
			
		||||
  'MAGIC_WAND': magicWand,
 | 
			
		||||
  'PACMAN': pacman,
 | 
			
		||||
  'LIST': list,
 | 
			
		||||
  'STAR_FULL': starFull,
 | 
			
		||||
  'ARROW_BACK': arrowBack,
 | 
			
		||||
  'REPLY': reply,
 | 
			
		||||
  'CLOSE': close,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export class ReactKitIcon extends React.Component {
 | 
			
		||||
  render() {
 | 
			
		||||
    const {
 | 
			
		||||
      iconType,
 | 
			
		||||
      size,
 | 
			
		||||
      className
 | 
			
		||||
    } = this.props;
 | 
			
		||||
    return React.createElement(Icon, {
 | 
			
		||||
      size: size,
 | 
			
		||||
      icon: types[iconType],
 | 
			
		||||
      className: className
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								foretold/components/src/Base/ReactKitIcon.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								foretold/components/src/Base/ReactKitIcon.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,15 @@
 | 
			
		|||
[@bs.module "./ReactKitIcon.js"]
 | 
			
		||||
external reactClass: ReasonReact.reactClass = "ReactKitIcon";
 | 
			
		||||
 | 
			
		||||
[@react.component]
 | 
			
		||||
let make = (~icon=?, ~size=?, ~className=?, ~children=ReasonReact.null) =>
 | 
			
		||||
  ReasonReact.wrapJsForReason(
 | 
			
		||||
    ~reactClass,
 | 
			
		||||
    ~props={
 | 
			
		||||
      "iconType": icon |> E.O.default(""),
 | 
			
		||||
      "size": size |> E.O.default("1em"),
 | 
			
		||||
      "className": className,
 | 
			
		||||
    },
 | 
			
		||||
    children,
 | 
			
		||||
  )
 | 
			
		||||
  |> ReasonReact.element;
 | 
			
		||||
							
								
								
									
										93
									
								
								foretold/components/src/Base/Settings.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								foretold/components/src/Base/Settings.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,93 @@
 | 
			
		|||
/* this is used to show hex color; */
 | 
			
		||||
let removeHex = Js.String.sliceToEnd(~from=1);
 | 
			
		||||
let r = c => c->removeHex->(e => `hex(e));
 | 
			
		||||
type col = [ | `hex(Js.String.t)];
 | 
			
		||||
 | 
			
		||||
let toS = (col: col) =>
 | 
			
		||||
  switch (col) {
 | 
			
		||||
  | `hex(c) => c
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
let white = "#FFFFFF"->r;
 | 
			
		||||
let black = "#000000"->r;
 | 
			
		||||
let greyO4 = "#00000044"->r;
 | 
			
		||||
let whiteO2 = "#ffffff20"->r;
 | 
			
		||||
let whiteO4 = "#ffffff40"->r;
 | 
			
		||||
let whiteOc = "#ffffffc0"->r;
 | 
			
		||||
let clear = "#00000000"->r;
 | 
			
		||||
 | 
			
		||||
let textDarker = "#333333"->r;
 | 
			
		||||
let textDark = "#5f6d7d"->r;
 | 
			
		||||
let textMedium = "#9a9ea7"->r;
 | 
			
		||||
let smokeWhite = "#F0F1F3"->r;
 | 
			
		||||
let buttonHover = "#e4ecf5"->r;
 | 
			
		||||
let lightGrayBackground = "#f4f6f9"->r;
 | 
			
		||||
let lighterGrayBackground = "#fbfcfd"->r;
 | 
			
		||||
let grayBackground = "#dcdee0"->r;
 | 
			
		||||
let greydisabled = "#e3e4e6"->r;
 | 
			
		||||
let accentBlue = "#8C9EB5"->r;
 | 
			
		||||
let accentBlueO8 = "#8C9EB540"->r;
 | 
			
		||||
let accentBlue1a = "#8c9eb530"->r;
 | 
			
		||||
let mainBlue = "#347296"->r;
 | 
			
		||||
let link = "#4a72b7"->r;
 | 
			
		||||
let linkHover = "#375ea1"->r;
 | 
			
		||||
let linkAccent = "#437bff"->r;
 | 
			
		||||
let darkLink = "#1a2e45"->r;
 | 
			
		||||
let darkAccentBlue = "#5C6E95"->r;
 | 
			
		||||
let grey1 = "#868686"->r;
 | 
			
		||||
let border = "#D5D7DA"->r;
 | 
			
		||||
let primary = mainBlue;
 | 
			
		||||
 | 
			
		||||
module FontWeights = {
 | 
			
		||||
  let light = Css.fontWeight(`num(300));
 | 
			
		||||
  let regular = Css.fontWeight(`num(400));
 | 
			
		||||
  let heavy = Css.fontWeight(`num(700));
 | 
			
		||||
  let veryHeavy = Css.fontWeight(`num(900));
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module Transitions = {
 | 
			
		||||
  let standardLength = 100;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module BorderRadius = {
 | 
			
		||||
  let medium = `px(4);
 | 
			
		||||
  let tight = `px(2);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module Statuses = {
 | 
			
		||||
  let green = "#81a952"->r;
 | 
			
		||||
  let yellow = "#C09C66"->r;
 | 
			
		||||
  let resolved = accentBlue;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module Alert = {
 | 
			
		||||
  type t = [ | `primary | `info | `success | `warning | `error];
 | 
			
		||||
 | 
			
		||||
  let color = (t: t) =>
 | 
			
		||||
    switch (t) {
 | 
			
		||||
    | `primary => "#004085"->r
 | 
			
		||||
    | `info => "#0c5460"->r
 | 
			
		||||
    | `success => "#155724"->r
 | 
			
		||||
    | `warning => "#856404"->r
 | 
			
		||||
    | `error => "#721c24"->r
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
  let background = (t: t) =>
 | 
			
		||||
    switch (t) {
 | 
			
		||||
    | `primary => "#cce5ff"->r
 | 
			
		||||
    | `info => "#d1ecf1"->r
 | 
			
		||||
    | `success => "#d4edda"->r
 | 
			
		||||
    | `warning => "#fff3cd"->r
 | 
			
		||||
    | `error => "#f8d7da"->r
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module Text = {
 | 
			
		||||
  module LightBackground = {
 | 
			
		||||
    let main = textDark;
 | 
			
		||||
    let p = "#3c3c3c"->r;
 | 
			
		||||
    let light = accentBlue;
 | 
			
		||||
    let active = "#0C5CD9"->r;
 | 
			
		||||
  };
 | 
			
		||||
  let standardFont = "Lato";
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										20
									
								
								foretold/components/src/Base/TabList.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								foretold/components/src/Base/TabList.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,20 @@
 | 
			
		|||
[@react.component]
 | 
			
		||||
let make = (~selected, ~flex=false, ~onClick=?, ~list) =>
 | 
			
		||||
  list
 | 
			
		||||
  |> E.L.React.fmapi((i, (key, label)) =>
 | 
			
		||||
       <Tab
 | 
			
		||||
         key={string_of_int(i)}
 | 
			
		||||
         onClick={e =>
 | 
			
		||||
           switch (onClick) {
 | 
			
		||||
           | Some(onClick) =>
 | 
			
		||||
             e->ReactEvent.Synthetic.preventDefault;
 | 
			
		||||
             onClick(key);
 | 
			
		||||
           | None => ()
 | 
			
		||||
           }
 | 
			
		||||
         }
 | 
			
		||||
         isActive={selected == key}
 | 
			
		||||
         flex>
 | 
			
		||||
         label->React.string
 | 
			
		||||
       </Tab>
 | 
			
		||||
     );
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										78
									
								
								foretold/components/src/Base/Types.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								foretold/components/src/Base/Types.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,78 @@
 | 
			
		|||
module Dist = {
 | 
			
		||||
  type t = {
 | 
			
		||||
    xs: array(float),
 | 
			
		||||
    ys: array(float),
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  type asJson = {
 | 
			
		||||
    .
 | 
			
		||||
    "xs": array(float),
 | 
			
		||||
    "ys": array(float),
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  let hasLength = (t: t): bool => t.xs |> E.A.length > 0;
 | 
			
		||||
 | 
			
		||||
  let empty: t = {xs: [||], ys: [||]};
 | 
			
		||||
 | 
			
		||||
  let toJson = (t: t): asJson => {"xs": t.xs, "ys": t.ys};
 | 
			
		||||
  let fromJson = (json: asJson): t => {xs: json##xs, ys: json##ys};
 | 
			
		||||
 | 
			
		||||
  module JS = {
 | 
			
		||||
    [@bs.deriving abstract]
 | 
			
		||||
    type distJs = {
 | 
			
		||||
      xs: array(float),
 | 
			
		||||
      ys: array(float),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let fromDist = (d: t) => distJs(~xs=d.xs, ~ys=d.ys);
 | 
			
		||||
    let toDist = (d: distJs) => {xs: xsGet(d), ys: ysGet(d)};
 | 
			
		||||
 | 
			
		||||
    let doAsDist = (f, d: t) => d |> fromDist |> f |> toDist;
 | 
			
		||||
    [@bs.module "./stats.js"] external cdfToPdf: distJs => distJs = "cdfToPdf";
 | 
			
		||||
    [@bs.module "./stats.js"]
 | 
			
		||||
    external findY: (float, distJs) => float = "findY";
 | 
			
		||||
    [@bs.module "./stats.js"]
 | 
			
		||||
    external findX: (float, distJs) => float = "findX";
 | 
			
		||||
 | 
			
		||||
    [@bs.module "./stats.js"] external mean: array(distJs) => distJs = "mean";
 | 
			
		||||
 | 
			
		||||
    [@bs.module "./stats.js"]
 | 
			
		||||
    external distributionScoreDistribution: array(distJs) => distJs =
 | 
			
		||||
      "distributionScoreDistribution";
 | 
			
		||||
 | 
			
		||||
    [@bs.module "./stats.js"]
 | 
			
		||||
    external distributionScoreNumber: array(distJs) => float =
 | 
			
		||||
      "distributionScoreNumber";
 | 
			
		||||
 | 
			
		||||
    [@bs.module "./stats.js"] external integral: distJs => float = "integral";
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  let toPdf = (dist: t) => dist |> JS.doAsDist(JS.cdfToPdf);
 | 
			
		||||
 | 
			
		||||
  let requireLength = (dist: t) => dist |> hasLength ? Some(dist) : None;
 | 
			
		||||
 | 
			
		||||
  let mean = (dists: array(t)) =>
 | 
			
		||||
    JS.mean(dists |> Array.map(JS.fromDist)) |> JS.toDist;
 | 
			
		||||
 | 
			
		||||
  let distributionScoreDistribution = (dists: array(t)) => {
 | 
			
		||||
    JS.distributionScoreDistribution(dists |> Array.map(JS.fromDist))
 | 
			
		||||
    |> JS.toDist
 | 
			
		||||
    |> requireLength;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  let distributionScoreNumber = (dists: array(t)) => {
 | 
			
		||||
    JS.distributionScoreNumber(dists |> Array.map(JS.fromDist));
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  let findX = (y: float, dist: t) => dist |> JS.fromDist |> JS.findX(y);
 | 
			
		||||
  let findY = (x: float, dist: t) => dist |> JS.fromDist |> JS.findY(x);
 | 
			
		||||
  let integral = (dist: t) => dist |> JS.fromDist |> JS.integral;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module Dists = {
 | 
			
		||||
  type t = array(Dist.t);
 | 
			
		||||
  let minX = (x: float, dists: t) =>
 | 
			
		||||
    dists |> Array.map(Dist.findX(x)) |> E.FloatArray.min;
 | 
			
		||||
  let maxX = (x: float, dists: t) =>
 | 
			
		||||
    dists |> Array.map(Dist.findX(x)) |> E.FloatArray.max;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										69
									
								
								foretold/components/src/Base/stats.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								foretold/components/src/Base/stats.js
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,69 @@
 | 
			
		|||
import {
 | 
			
		||||
  Pdf,
 | 
			
		||||
  Cdf,
 | 
			
		||||
  ContinuousDistribution,
 | 
			
		||||
  ContinuousDistributionCombination,
 | 
			
		||||
  scoringFunctions
 | 
			
		||||
} from '@foretold/cdf';
 | 
			
		||||
 | 
			
		||||
function cdfToPdf({ xs, ys }) {
 | 
			
		||||
  let cdf = new Cdf(xs, ys);
 | 
			
		||||
  let pdf = cdf.toPdf();
 | 
			
		||||
  return { xs: pdf.xs, ys: pdf.ys };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function mean(vars) {
 | 
			
		||||
  let cdfs = vars.map(r => new Cdf(r.xs, r.ys));
 | 
			
		||||
  let comb = new ContinuousDistributionCombination(cdfs);
 | 
			
		||||
  let newCdf = comb.combineYsWithMean(10000);
 | 
			
		||||
  return { xs: newCdf.xs, ys: newCdf.ys };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function distributionScoreDistribution(vars) {
 | 
			
		||||
  let cdfs = vars.map(r => (new Cdf(r.xs, r.ys)));
 | 
			
		||||
  let newDist = scoringFunctions.distributionInputDistributionOutputDistribution({
 | 
			
		||||
    predictionCdf: cdfs[0],
 | 
			
		||||
    aggregateCdf: cdfs[1],
 | 
			
		||||
    resultCdf: cdfs[2],
 | 
			
		||||
    sampleCount: 10000
 | 
			
		||||
  });
 | 
			
		||||
  let newCdf = (new Pdf(newDist.xs, newDist.ys)).toCdf();
 | 
			
		||||
  return { xs: newCdf.xs, ys: newCdf.ys };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function distributionScoreNumber(vars) {
 | 
			
		||||
  let cdfs = vars.map(r => (new Cdf(r.xs, r.ys)));
 | 
			
		||||
  return scoringFunctions.distributionInputDistributionOutput({
 | 
			
		||||
    predictionCdf: cdfs[0],
 | 
			
		||||
    aggregateCdf: cdfs[1],
 | 
			
		||||
    resultCdf: cdfs[2],
 | 
			
		||||
    sampleCount: 10000,
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function findY(x, { xs, ys }) {
 | 
			
		||||
  let cdf = new Cdf(xs, ys);
 | 
			
		||||
  let result = cdf.findY(x);
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function findX(y, { xs, ys }) {
 | 
			
		||||
  let cdf = new Cdf(xs, ys);
 | 
			
		||||
  let result = cdf.findX(y);
 | 
			
		||||
  return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function integral({ xs, ys }) {
 | 
			
		||||
  let distribution = new ContinuousDistribution(xs, ys);
 | 
			
		||||
  return distribution.integral();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
  cdfToPdf,
 | 
			
		||||
  findY,
 | 
			
		||||
  findX,
 | 
			
		||||
  mean,
 | 
			
		||||
  distributionScoreDistribution,
 | 
			
		||||
  distributionScoreNumber,
 | 
			
		||||
  integral
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										1
									
								
								foretold/components/src/BotDefaultImage.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								foretold/components/src/BotDefaultImage.re
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										45
									
								
								foretold/components/src/Charts/CdfChart__Base.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								foretold/components/src/Charts/CdfChart__Base.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,45 @@
 | 
			
		|||
[@bs.module "./cdfChart.js"]
 | 
			
		||||
external cdfChart: ReasonReact.reactClass = "default";
 | 
			
		||||
 | 
			
		||||
type data = {
 | 
			
		||||
  .
 | 
			
		||||
  "xs": array(float),
 | 
			
		||||
  "ys": array(float),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
[@react.component]
 | 
			
		||||
let make =
 | 
			
		||||
    (
 | 
			
		||||
      ~width=?,
 | 
			
		||||
      ~height=?,
 | 
			
		||||
      ~verticalLine=?,
 | 
			
		||||
      ~showVerticalLine=?,
 | 
			
		||||
      ~marginBottom=?,
 | 
			
		||||
      ~marginTop=?,
 | 
			
		||||
      ~showDistributionLines=?,
 | 
			
		||||
      ~maxX=?,
 | 
			
		||||
      ~minX=?,
 | 
			
		||||
      ~onHover=?,
 | 
			
		||||
      ~primaryDistribution=?,
 | 
			
		||||
      ~children=[||],
 | 
			
		||||
    ) =>
 | 
			
		||||
  ReasonReact.wrapJsForReason(
 | 
			
		||||
    ~reactClass=cdfChart,
 | 
			
		||||
    ~props=
 | 
			
		||||
      makeProps(
 | 
			
		||||
        ~width?,
 | 
			
		||||
        ~height?,
 | 
			
		||||
        ~verticalLine?,
 | 
			
		||||
        ~marginBottom?,
 | 
			
		||||
        ~marginTop?,
 | 
			
		||||
        ~onHover?,
 | 
			
		||||
        ~showVerticalLine?,
 | 
			
		||||
        ~showDistributionLines?,
 | 
			
		||||
        ~maxX?,
 | 
			
		||||
        ~minX?,
 | 
			
		||||
        ~primaryDistribution?,
 | 
			
		||||
        (),
 | 
			
		||||
      ),
 | 
			
		||||
    children,
 | 
			
		||||
  )
 | 
			
		||||
  |> ReasonReact.element;
 | 
			
		||||
							
								
								
									
										29
									
								
								foretold/components/src/Charts/CdfChart__Large.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								foretold/components/src/Charts/CdfChart__Large.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,29 @@
 | 
			
		|||
module Styles = {
 | 
			
		||||
  open Css;
 | 
			
		||||
  let graph =
 | 
			
		||||
    style([
 | 
			
		||||
      selector(".axis", [fontSize(`px(11))]),
 | 
			
		||||
      selector(".domain", [display(`none)]),
 | 
			
		||||
      selector(".tick line", [display(`none)]),
 | 
			
		||||
      selector(".tick text", [color(`hex("2e4c65"))]),
 | 
			
		||||
      selector(".chart .area-path", [SVG.fill(`hex("7e9db7"))]),
 | 
			
		||||
    ]);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
[@react.component]
 | 
			
		||||
let make = (~cdf: Types.Dist.t, ~minX=?, ~maxX=?, ~width=Some(400)) => {
 | 
			
		||||
  let pdf = cdf |> Types.Dist.toPdf;
 | 
			
		||||
  <div className=Styles.graph>
 | 
			
		||||
    <CdfChart__Base
 | 
			
		||||
      marginBottom=25
 | 
			
		||||
      ?width
 | 
			
		||||
      height=200
 | 
			
		||||
      ?maxX
 | 
			
		||||
      ?minX
 | 
			
		||||
      showVerticalLine=false
 | 
			
		||||
      showDistributionLines=false
 | 
			
		||||
      primaryDistribution={"xs": pdf.xs, "ys": pdf.ys}
 | 
			
		||||
      onHover={_r => ()}
 | 
			
		||||
    />
 | 
			
		||||
  </div>;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										37
									
								
								foretold/components/src/Charts/CdfChart__Plain.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								foretold/components/src/Charts/CdfChart__Plain.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,37 @@
 | 
			
		|||
module Styles = {
 | 
			
		||||
  open Css;
 | 
			
		||||
  let textOverlay = style([position(`absolute)]);
 | 
			
		||||
  let mainText = style([fontSize(`em(1.1)), color(Settings.darkLink)]);
 | 
			
		||||
  let secondaryText =
 | 
			
		||||
    style([fontSize(`em(0.9)), color(Settings.accentBlue)]);
 | 
			
		||||
 | 
			
		||||
  let graph = chartColor =>
 | 
			
		||||
    style([
 | 
			
		||||
      position(`relative),
 | 
			
		||||
      selector(".axis", [fontSize(`px(9))]),
 | 
			
		||||
      selector(".domain", [display(`none)]),
 | 
			
		||||
      selector(".tick line", [display(`none)]),
 | 
			
		||||
      selector(".tick text", [color(`hex("bfcad4"))]),
 | 
			
		||||
      selector(".chart .area-path", [SVG.fill(chartColor)]),
 | 
			
		||||
    ]);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
[@react.component]
 | 
			
		||||
let make = (~cdf: Types.Dist.t, ~minX, ~maxX, ~color=`hex("3562AE66")) => {
 | 
			
		||||
  let pdf = cdf |> Types.Dist.toPdf;
 | 
			
		||||
 | 
			
		||||
  <div className={Styles.graph(color)}>
 | 
			
		||||
    <CdfChart__Base
 | 
			
		||||
      width=200
 | 
			
		||||
      height=30
 | 
			
		||||
      minX
 | 
			
		||||
      maxX
 | 
			
		||||
      marginBottom=15
 | 
			
		||||
      marginTop=0
 | 
			
		||||
      showVerticalLine=false
 | 
			
		||||
      showDistributionLines=false
 | 
			
		||||
      primaryDistribution={"xs": pdf.xs, "ys": pdf.ys}
 | 
			
		||||
      onHover={_r => ()}
 | 
			
		||||
    />
 | 
			
		||||
  </div>;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										46
									
								
								foretold/components/src/Charts/CdfChart__Small.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								foretold/components/src/Charts/CdfChart__Small.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,46 @@
 | 
			
		|||
module Styles = {
 | 
			
		||||
  open Css;
 | 
			
		||||
  let textOverlay = style([position(`absolute)]);
 | 
			
		||||
  let mainText = style([fontSize(`em(1.1)), color(Settings.darkLink)]);
 | 
			
		||||
  let secondaryText =
 | 
			
		||||
    style([fontSize(`em(0.9)), color(Settings.accentBlue)]);
 | 
			
		||||
 | 
			
		||||
  let graph = chartColor =>
 | 
			
		||||
    style([
 | 
			
		||||
      position(`relative),
 | 
			
		||||
      selector(".axis", [fontSize(`px(9))]),
 | 
			
		||||
      selector(".domain", [display(`none)]),
 | 
			
		||||
      selector(".tick line", [display(`none)]),
 | 
			
		||||
      selector(".tick text", [color(`hex("bfcad4"))]),
 | 
			
		||||
      selector(".chart .area-path", [SVG.fill(chartColor)]),
 | 
			
		||||
    ]);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
[@react.component]
 | 
			
		||||
let make =
 | 
			
		||||
    (
 | 
			
		||||
      ~cdf: Types.Dist.t,
 | 
			
		||||
      ~minX=None,
 | 
			
		||||
      ~maxX=None,
 | 
			
		||||
      ~width=200,
 | 
			
		||||
      ~height=40,
 | 
			
		||||
      ~color=`hex("7e9db7"),
 | 
			
		||||
    ) => {
 | 
			
		||||
  let pdf = cdf |> Types.Dist.toPdf;
 | 
			
		||||
 | 
			
		||||
  <div className={Styles.graph(color)}>
 | 
			
		||||
    <div className=Styles.textOverlay> <CdfChart__StatSummary cdf /> </div>
 | 
			
		||||
    <CdfChart__Base
 | 
			
		||||
      width
 | 
			
		||||
      height
 | 
			
		||||
      ?minX
 | 
			
		||||
      ?maxX
 | 
			
		||||
      marginBottom=0
 | 
			
		||||
      marginTop=0
 | 
			
		||||
      showVerticalLine=false
 | 
			
		||||
      showDistributionLines=false
 | 
			
		||||
      primaryDistribution={"xs": pdf.xs, "ys": pdf.ys}
 | 
			
		||||
      onHover={_r => ()}
 | 
			
		||||
    />
 | 
			
		||||
  </div>;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										31
									
								
								foretold/components/src/Charts/CdfChart__StatSummary.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								foretold/components/src/Charts/CdfChart__StatSummary.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,31 @@
 | 
			
		|||
module Styles = {
 | 
			
		||||
  open Css;
 | 
			
		||||
  let textOverlay = style([position(`absolute)]);
 | 
			
		||||
  let mainText = style([fontSize(`em(1.1)), color(Settings.darkLink)]);
 | 
			
		||||
  let secondaryText =
 | 
			
		||||
    style([fontSize(`em(0.9)), color(Settings.accentBlue)]);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
[@react.component]
 | 
			
		||||
let make = (~cdf: Types.Dist.t, ~showMean=true) =>
 | 
			
		||||
  <>
 | 
			
		||||
    {showMean
 | 
			
		||||
       ? <div className=Styles.mainText>
 | 
			
		||||
           <NumberShower
 | 
			
		||||
             precision=2
 | 
			
		||||
             number={cdf |> Types.Dist.findX(0.5)}
 | 
			
		||||
           />
 | 
			
		||||
         </div>
 | 
			
		||||
       : ReasonReact.null}
 | 
			
		||||
    <div className=Styles.secondaryText>
 | 
			
		||||
      <NumberShower
 | 
			
		||||
        precision=2
 | 
			
		||||
        number={cdf |> Types.Dist.findX(0.05)}
 | 
			
		||||
      />
 | 
			
		||||
      {" to " |> ReasonReact.string}
 | 
			
		||||
      <NumberShower
 | 
			
		||||
        precision=2
 | 
			
		||||
        number={cdf |> Types.Dist.findX(0.95)}
 | 
			
		||||
      />
 | 
			
		||||
    </div>
 | 
			
		||||
  </>;
 | 
			
		||||
							
								
								
									
										65
									
								
								foretold/components/src/Charts/cdfChart.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								foretold/components/src/Charts/cdfChart.js
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,65 @@
 | 
			
		|||
import React, { useEffect } from 'react';
 | 
			
		||||
import { useSize } from 'react-use';
 | 
			
		||||
 | 
			
		||||
import chart from './cdfChartd3';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Example input:
 | 
			
		||||
 * {
 | 
			
		||||
 * xs: [50,100,300,400,500,600],
 | 
			
		||||
 * ys: [0.1, 0.4, 0.6, 0.7,0.8, 0.9]}
 | 
			
		||||
 * }
 | 
			
		||||
 */
 | 
			
		||||
function CdfChart(props) {
 | 
			
		||||
  const id = "chart-" + getRandomInt(0, 100000);
 | 
			
		||||
  const [sized, { width }] = useSize(() => {
 | 
			
		||||
    return React.createElement("div", {
 | 
			
		||||
      key: "resizable-div",
 | 
			
		||||
    });
 | 
			
		||||
  }, {
 | 
			
		||||
    width: props.width,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    chart()
 | 
			
		||||
      .svgWidth(width)
 | 
			
		||||
      .svgHeight(props.height)
 | 
			
		||||
      .maxX(props.maxX)
 | 
			
		||||
      .minX(props.minX)
 | 
			
		||||
      .marginBottom(props.marginBottom || 15)
 | 
			
		||||
      .marginLeft(5)
 | 
			
		||||
      .marginRight(5)
 | 
			
		||||
      .marginTop(5)
 | 
			
		||||
      .showDistributionLines(props.showDistributionLines)
 | 
			
		||||
      .verticalLine(props.verticalLine)
 | 
			
		||||
      .showVerticalLine(props.showVerticalLine)
 | 
			
		||||
      .container("#" + id)
 | 
			
		||||
      .data({ primary: props.primaryDistribution }).render();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  const style = !!props.width ? { width: props.width + "px" } : {};
 | 
			
		||||
  const key = id;
 | 
			
		||||
 | 
			
		||||
  return React.createElement("div", {
 | 
			
		||||
    style: {
 | 
			
		||||
      paddingLeft: "10px",
 | 
			
		||||
      paddingRight: "10px",
 | 
			
		||||
    },
 | 
			
		||||
  }, [
 | 
			
		||||
    sized,
 | 
			
		||||
    React.createElement("div", { id, style, key }),
 | 
			
		||||
  ]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default CdfChart;
 | 
			
		||||
							
								
								
									
										303
									
								
								foretold/components/src/Charts/cdfChartd3.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										303
									
								
								foretold/components/src/Charts/cdfChartd3.js
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,303 @@
 | 
			
		|||
import * as d3 from 'd3';
 | 
			
		||||
 | 
			
		||||
function chart() {
 | 
			
		||||
  // Id for event handlings.
 | 
			
		||||
  var attrs = {
 | 
			
		||||
    id: 'ID' + Math.floor(Math.random() * 1000000),
 | 
			
		||||
    svgWidth: 400,
 | 
			
		||||
    svgHeight: 400,
 | 
			
		||||
 | 
			
		||||
    marginTop: 5,
 | 
			
		||||
    marginBottom: 5,
 | 
			
		||||
    marginRight: 5,
 | 
			
		||||
    marginLeft: 5,
 | 
			
		||||
 | 
			
		||||
    container: 'body',
 | 
			
		||||
    minX: false,
 | 
			
		||||
    maxX: false,
 | 
			
		||||
    scale: 'linear',
 | 
			
		||||
    showDistributionLines: true,
 | 
			
		||||
    areaColors: ['#E1E5EC', '#E1E5EC'],
 | 
			
		||||
    logBase: 10,
 | 
			
		||||
    verticalLine: 110,
 | 
			
		||||
    showVerticalLine: true,
 | 
			
		||||
    data: null,
 | 
			
		||||
    onHover: (e) => {
 | 
			
		||||
    },
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  var main = function main() {
 | 
			
		||||
    // Drawing containers.
 | 
			
		||||
    var container = d3.select(attrs.container);
 | 
			
		||||
 | 
			
		||||
    if (container.node() === null) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var containerRect = container.node().getBoundingClientRect();
 | 
			
		||||
    if (containerRect.width > 0) {
 | 
			
		||||
      attrs.svgWidth = containerRect.width;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Calculated properties.
 | 
			
		||||
    // id for event handlings.
 | 
			
		||||
    var calc = {};
 | 
			
		||||
    calc.id = 'ID' + Math.floor(Math.random() * 1000000);
 | 
			
		||||
    calc.chartLeftMargin = attrs.marginLeft;
 | 
			
		||||
    calc.chartTopMargin = attrs.marginTop;
 | 
			
		||||
    calc.chartWidth = attrs.svgWidth - attrs.marginRight - attrs.marginLeft;
 | 
			
		||||
    calc.chartHeight = attrs.svgHeight - attrs.marginBottom - attrs.marginTop;
 | 
			
		||||
 | 
			
		||||
    var areaColor = d3.scaleOrdinal().range(attrs.areaColors);
 | 
			
		||||
 | 
			
		||||
    var dataPoints = [getDatapoints('primary')];
 | 
			
		||||
 | 
			
		||||
    // Scales.
 | 
			
		||||
    var xScale;
 | 
			
		||||
 | 
			
		||||
    var xMin = d3.min(attrs.data.primary.xs);
 | 
			
		||||
    var xMax = d3.max(attrs.data.primary.xs);
 | 
			
		||||
 | 
			
		||||
    if (attrs.scale === 'linear') {
 | 
			
		||||
      xScale = d3.scaleLinear()
 | 
			
		||||
        .domain([
 | 
			
		||||
          attrs.minX || xMin,
 | 
			
		||||
          attrs.maxX || xMax
 | 
			
		||||
        ])
 | 
			
		||||
        .range([0, calc.chartWidth]);
 | 
			
		||||
    } else {
 | 
			
		||||
      xScale = d3.scaleLog()
 | 
			
		||||
        .base(attrs.logBase)
 | 
			
		||||
        .domain([
 | 
			
		||||
          attrs.minX,
 | 
			
		||||
          attrs.maxX,
 | 
			
		||||
        ])
 | 
			
		||||
        .range([0, calc.chartWidth]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var yMin = d3.min(attrs.data.primary.ys);
 | 
			
		||||
    var yMax = d3.max(attrs.data.primary.ys);
 | 
			
		||||
 | 
			
		||||
    var yScale = d3.scaleLinear()
 | 
			
		||||
      .domain([
 | 
			
		||||
        yMin,
 | 
			
		||||
        yMax,
 | 
			
		||||
      ])
 | 
			
		||||
      .range([calc.chartHeight, 0]);
 | 
			
		||||
 | 
			
		||||
    // Axis generator.
 | 
			
		||||
    var 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 {
 | 
			
		||||
          var prefix = d3.formatPrefix(".0", d);
 | 
			
		||||
          var output = prefix(d);
 | 
			
		||||
          return output.replace("G", "B");
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
    // Line generator.
 | 
			
		||||
    var line = d3.line()
 | 
			
		||||
      .x(function (d, i) {
 | 
			
		||||
        return xScale(d.x);
 | 
			
		||||
      })
 | 
			
		||||
      .y(function (d, i) {
 | 
			
		||||
        return yScale(d.y);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
    var area = d3.area()
 | 
			
		||||
      .x(function (d, i) {
 | 
			
		||||
        return xScale(d.x);
 | 
			
		||||
      })
 | 
			
		||||
      .y1(function (d, i) {
 | 
			
		||||
        return yScale(d.y);
 | 
			
		||||
      })
 | 
			
		||||
      .y0(calc.chartHeight);
 | 
			
		||||
 | 
			
		||||
    // Add svg.
 | 
			
		||||
    var svg = container
 | 
			
		||||
      .patternify({ tag: 'svg', selector: 'svg-chart-container' })
 | 
			
		||||
      .attr('width', "100%")
 | 
			
		||||
      .attr('height', attrs.svgHeight)
 | 
			
		||||
      .attr('pointer-events', 'none');
 | 
			
		||||
 | 
			
		||||
    // Add container g element.
 | 
			
		||||
    var chart = svg
 | 
			
		||||
      .patternify({ tag: 'g', selector: 'chart' })
 | 
			
		||||
      .attr(
 | 
			
		||||
        'transform',
 | 
			
		||||
        'translate(' + calc.chartLeftMargin + ',' + calc.chartTopMargin + ')',
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
    // Add axis.
 | 
			
		||||
    chart.patternify({ tag: 'g', selector: 'axis' })
 | 
			
		||||
      .attr('transform', 'translate(' + 0 + ',' + calc.chartHeight + ')')
 | 
			
		||||
      .call(xAxis);
 | 
			
		||||
 | 
			
		||||
    // Draw area.
 | 
			
		||||
    chart
 | 
			
		||||
      .patternify({
 | 
			
		||||
        tag: 'path',
 | 
			
		||||
        selector: 'area-path',
 | 
			
		||||
        data: dataPoints
 | 
			
		||||
      })
 | 
			
		||||
      .attr('d', area)
 | 
			
		||||
      .attr('fill', (d, i) => areaColor(i))
 | 
			
		||||
      .attr('opacity', (d, i) => i === 0 ? 0.7 : 1);
 | 
			
		||||
 | 
			
		||||
    // Draw line.
 | 
			
		||||
    if (attrs.showDistributionLines) {
 | 
			
		||||
      chart
 | 
			
		||||
        .patternify({
 | 
			
		||||
          tag: 'path',
 | 
			
		||||
          selector: 'line-path',
 | 
			
		||||
          data: dataPoints
 | 
			
		||||
        })
 | 
			
		||||
        .attr('d', line)
 | 
			
		||||
        .attr('id', (d, i) => 'line-' + (i + 1))
 | 
			
		||||
        .attr('opacity', (d, i) => {
 | 
			
		||||
          return i === 0 ? 0.7 : 1
 | 
			
		||||
        })
 | 
			
		||||
        .attr('fill', 'none');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (attrs.showVerticalLine) {
 | 
			
		||||
      chart.patternify({ tag: 'line', selector: 'v-line' })
 | 
			
		||||
        .attr('x1', xScale(attrs.verticalLine))
 | 
			
		||||
        .attr('x2', xScale(attrs.verticalLine))
 | 
			
		||||
        .attr('y1', 0)
 | 
			
		||||
        .attr('y2', calc.chartHeight)
 | 
			
		||||
        .attr('stroke-width', 1.5)
 | 
			
		||||
        .attr('stroke-dasharray', '6 6')
 | 
			
		||||
        .attr('stroke', 'steelblue');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var hoverLine = chart.patternify({ tag: 'line', selector: 'hover-line' })
 | 
			
		||||
      .attr('x1', 0)
 | 
			
		||||
      .attr('x2', 0)
 | 
			
		||||
      .attr('y1', 0)
 | 
			
		||||
      .attr('y2', calc.chartHeight)
 | 
			
		||||
      .attr('opacity', 0)
 | 
			
		||||
      .attr('stroke-width', 1.5)
 | 
			
		||||
      .attr('stroke-dasharray', '6 6')
 | 
			
		||||
      .attr('stroke', '#22313F');
 | 
			
		||||
 | 
			
		||||
    // Add drawing rectangle.
 | 
			
		||||
    chart.patternify({ tag: 'rect', selector: 'mouse-rect' })
 | 
			
		||||
      .attr('width', calc.chartWidth)
 | 
			
		||||
      .attr('height', calc.chartHeight)
 | 
			
		||||
      .attr('fill', 'transparent')
 | 
			
		||||
      .attr('pointer-events', 'all')
 | 
			
		||||
      .on('mouseover', mouseover)
 | 
			
		||||
      .on('mousemove', mouseover)
 | 
			
		||||
      .on('mouseout', mouseout);
 | 
			
		||||
 | 
			
		||||
    function mouseover() {
 | 
			
		||||
      var mouse = d3.mouse(this);
 | 
			
		||||
 | 
			
		||||
      hoverLine.attr('opacity', 1)
 | 
			
		||||
        .attr('x1', mouse[0])
 | 
			
		||||
        .attr('x2', mouse[0]);
 | 
			
		||||
 | 
			
		||||
      var range = [
 | 
			
		||||
        xScale(dataPoints[dataPoints.length - 1][0].x),
 | 
			
		||||
        xScale(
 | 
			
		||||
          dataPoints
 | 
			
		||||
            [dataPoints.length - 1]
 | 
			
		||||
            [dataPoints[dataPoints.length - 1].length - 1].x,
 | 
			
		||||
        ),
 | 
			
		||||
      ];
 | 
			
		||||
 | 
			
		||||
      var xValue = xScale.invert(mouse[0]).toFixed(2);
 | 
			
		||||
 | 
			
		||||
      if (mouse[0] > range[0] && mouse[0] < range[1]) {
 | 
			
		||||
        attrs.onHover(xValue);
 | 
			
		||||
      } else {
 | 
			
		||||
        attrs.onHover(false);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function mouseout() {
 | 
			
		||||
      hoverLine.attr('opacity', 0)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param key
 | 
			
		||||
     * @returns {[]}
 | 
			
		||||
     */
 | 
			
		||||
    function getDatapoints(key) {
 | 
			
		||||
      var dt = [];
 | 
			
		||||
      var data = attrs.data[key];
 | 
			
		||||
      var len = data.xs.length;
 | 
			
		||||
 | 
			
		||||
      for (let i = 0; i < len; i++) {
 | 
			
		||||
        dt.push({
 | 
			
		||||
          x: data.xs[i],
 | 
			
		||||
          y: data.ys[i]
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return dt;
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  d3.selection.prototype.patternify = function patternify(params) {
 | 
			
		||||
    var container = this;
 | 
			
		||||
    var selector = params.selector;
 | 
			
		||||
    var elementTag = params.tag;
 | 
			
		||||
    var data = params.data || [selector];
 | 
			
		||||
 | 
			
		||||
    // Pattern in action.
 | 
			
		||||
    var selection = container.selectAll('.' + selector).data(data, (d, i) => {
 | 
			
		||||
      if (typeof d === 'object') {
 | 
			
		||||
        if (d.id) {
 | 
			
		||||
          return d.id;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      return i;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    selection.exit().remove();
 | 
			
		||||
    selection = selection.enter().append(elementTag).merge(selection);
 | 
			
		||||
    selection.attr('class', selector);
 | 
			
		||||
    return selection;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // @todo: Do not do like that.
 | 
			
		||||
  // Dynamic keys functions.
 | 
			
		||||
  // Attach variables to main function.
 | 
			
		||||
  Object.keys(attrs).forEach((key) => {
 | 
			
		||||
    main[key] = function (_) {
 | 
			
		||||
      if (!arguments.length) {
 | 
			
		||||
        return attrs[key];
 | 
			
		||||
      }
 | 
			
		||||
      attrs[key] = _;
 | 
			
		||||
      return main;
 | 
			
		||||
    };
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  //Set attrs as property.
 | 
			
		||||
  main.attrs = attrs;
 | 
			
		||||
 | 
			
		||||
  //Exposed update functions.
 | 
			
		||||
  main.data = function data(value) {
 | 
			
		||||
    if (!arguments.length) return attrs.data;
 | 
			
		||||
    attrs.data = value;
 | 
			
		||||
    return main;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // Run visual.
 | 
			
		||||
  main.render = function render() {
 | 
			
		||||
    main();
 | 
			
		||||
    return main;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return main;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default chart;
 | 
			
		||||
							
								
								
									
										23
									
								
								foretold/components/src/FC.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								foretold/components/src/FC.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,23 @@
 | 
			
		|||
module Base = Base;
 | 
			
		||||
 | 
			
		||||
module AppHeader = AppHeader;
 | 
			
		||||
module GroupHeader = GroupHeader;
 | 
			
		||||
module PageCard = PageCard;
 | 
			
		||||
module PaginationButtons = PaginationButtons;
 | 
			
		||||
module Tab = Tab;
 | 
			
		||||
module Tab2 = Tab2;
 | 
			
		||||
module TabButton = Tab.Button;
 | 
			
		||||
module Table = Table;
 | 
			
		||||
module Footer = Footer;
 | 
			
		||||
module StateStatus = StateStatus;
 | 
			
		||||
module MeasurableForm = MeasurableForm;
 | 
			
		||||
module NumberShower = NumberShower;
 | 
			
		||||
module Button = Button;
 | 
			
		||||
module HelpDropdown = HelpDropdown;
 | 
			
		||||
module MyCommunities = MyCommunities;
 | 
			
		||||
 | 
			
		||||
module Charts = {
 | 
			
		||||
  module Large = CdfChart__Large;
 | 
			
		||||
  module Plain = CdfChart__Plain;
 | 
			
		||||
  module Small = CdfChart__Small;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										109
									
								
								foretold/components/src/Footer.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								foretold/components/src/Footer.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,109 @@
 | 
			
		|||
open Base;
 | 
			
		||||
 | 
			
		||||
let str = ReasonReact.string;
 | 
			
		||||
 | 
			
		||||
module Styles = {
 | 
			
		||||
  open Css;
 | 
			
		||||
  /* PaddingTop is applied to copyright and link items to get
 | 
			
		||||
     space between lines when wrapped.
 | 
			
		||||
     Both also have horizontal margin to ensure minimum space between items,
 | 
			
		||||
     this is then subtracted from at the edges of the "sections"-class. */
 | 
			
		||||
  let itemsPaddingTop = `em(1.2);
 | 
			
		||||
  let betweenItemsMargin = 0.8;
 | 
			
		||||
 | 
			
		||||
  /* Layout box for spacing in the page */
 | 
			
		||||
  let layoutBox =
 | 
			
		||||
    style(
 | 
			
		||||
      [margin2(~v=`em(2.), ~h=`zero), padding2(~v=`zero, ~h=`em(2.))]
 | 
			
		||||
      @ BaseStyles.fullWidthFloatLeft,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
  /* footerBox inside layoutBox, here the horizontal border is in line with the text.
 | 
			
		||||
     PaddingTop is added here instead of "itemsPaddingTop" to decrease line distance
 | 
			
		||||
     when wrap and make items appear more as a group */
 | 
			
		||||
  let footerBox =
 | 
			
		||||
    style([
 | 
			
		||||
      borderTop(`px(1), `solid, Colors.border),
 | 
			
		||||
      paddingTop(`em(0.3)),
 | 
			
		||||
      fontSize(`em(0.9)),
 | 
			
		||||
      fontWeight(`bold),
 | 
			
		||||
      color(Colors.textMedium),
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
  /* Sections enables wrapping of left and right section */
 | 
			
		||||
  let sections =
 | 
			
		||||
    style([
 | 
			
		||||
      display(`flex),
 | 
			
		||||
      flexWrap(`wrapReverse),
 | 
			
		||||
      justifyContent(`spaceBetween),
 | 
			
		||||
      margin2(~v=`zero, ~h=`em(-. betweenItemsMargin)),
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
  let element =
 | 
			
		||||
    style([
 | 
			
		||||
      flexGrow(10.),
 | 
			
		||||
      paddingTop(itemsPaddingTop),
 | 
			
		||||
      paddingRight(`em(0.2)),
 | 
			
		||||
      margin2(~v=`zero, ~h=`em(betweenItemsMargin)),
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
  /* Some extra marginBottom is added to reinforce the links as
 | 
			
		||||
     a group vs copyright.
 | 
			
		||||
     The element grows a little bit to add some spacing for larger screens */
 | 
			
		||||
  let items =
 | 
			
		||||
    style([
 | 
			
		||||
      display(`flex),
 | 
			
		||||
      flexWrap(`wrap),
 | 
			
		||||
      flexGrow(1.),
 | 
			
		||||
      media("(min-width: 720px)", [justifyContent(`spaceBetween)]),
 | 
			
		||||
      listStyleType(`none),
 | 
			
		||||
      padding(`zero),
 | 
			
		||||
      margin(`zero),
 | 
			
		||||
      marginBottom(`em(1.2)),
 | 
			
		||||
      selector(
 | 
			
		||||
        "li",
 | 
			
		||||
        [
 | 
			
		||||
          paddingTop(itemsPaddingTop),
 | 
			
		||||
          margin2(~v=`zero, ~h=`em(betweenItemsMargin)),
 | 
			
		||||
          selector(
 | 
			
		||||
            "a",
 | 
			
		||||
            [
 | 
			
		||||
              textDecoration(`none),
 | 
			
		||||
              color(Colors.textMedium),
 | 
			
		||||
              paddingBottom(`px(1)),
 | 
			
		||||
              selector(
 | 
			
		||||
                ":hover",
 | 
			
		||||
                [
 | 
			
		||||
                  color(Colors.linkAccent),
 | 
			
		||||
                  borderBottom(`px(1), `solid, Colors.linkAccent),
 | 
			
		||||
                ],
 | 
			
		||||
              ),
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
        ],
 | 
			
		||||
      ),
 | 
			
		||||
    ]);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Shows a footer with an element (ie. name and copyright), and a list of items
 | 
			
		||||
 */
 | 
			
		||||
[@react.component]
 | 
			
		||||
let make =
 | 
			
		||||
    (
 | 
			
		||||
      ~logo: ReasonReact.reactElement,
 | 
			
		||||
      ~links: array(ReasonReact.reactElement),
 | 
			
		||||
    ) =>
 | 
			
		||||
  <div className=Styles.layoutBox>
 | 
			
		||||
    <div className=Styles.footerBox>
 | 
			
		||||
      <div className=Styles.sections>
 | 
			
		||||
        <div className=Styles.element> logo </div>
 | 
			
		||||
        <ul className=Styles.items>
 | 
			
		||||
          {links->Belt.Array.mapWithIndex((index, item) =>
 | 
			
		||||
             <li key={"footer-li-" ++ string_of_int(index)}> item </li>
 | 
			
		||||
           )
 | 
			
		||||
           |> ReasonReact.array}
 | 
			
		||||
        </ul>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>;
 | 
			
		||||
							
								
								
									
										60
									
								
								foretold/components/src/Form/DropdownSelect.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								foretold/components/src/Form/DropdownSelect.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,60 @@
 | 
			
		|||
module E = E;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
   * Dropdown select
 | 
			
		||||
   * Usage
 | 
			
		||||
   * <DropdownSelect.String
 | 
			
		||||
   *   selected=Some("key2")
 | 
			
		||||
   *   values=[("key1", "Label1"), ("key2", "Label2")]
 | 
			
		||||
   *   onSelect={v =>
 | 
			
		||||
   *     switch (v) {
 | 
			
		||||
   *       | Some(k) => Js.log2("Selected: ", k)
 | 
			
		||||
   *       | None => Js.log("Selected none")
 | 
			
		||||
   *     }
 | 
			
		||||
   *   }
 | 
			
		||||
   * />
 | 
			
		||||
   *
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
[@react.component]
 | 
			
		||||
let make =
 | 
			
		||||
    (
 | 
			
		||||
      ~initialValue,
 | 
			
		||||
      ~values: list(('a, string)),
 | 
			
		||||
      ~onSelect: option(option('a) => unit)=?,
 | 
			
		||||
      ~trigger=Dropdown.Click,
 | 
			
		||||
    ) => {
 | 
			
		||||
  let initLabel = "";
 | 
			
		||||
 | 
			
		||||
  let (label, setLabel) = React.useState(() => initLabel);
 | 
			
		||||
 | 
			
		||||
  <DropdownMenu title=label trigger>
 | 
			
		||||
    <Menu
 | 
			
		||||
      selectable=true
 | 
			
		||||
      onSelect={(info: Menu.clickInfo) =>
 | 
			
		||||
        switch (
 | 
			
		||||
          (values |> E.L.withIdx)
 | 
			
		||||
          ->(E.L.getBy(((i, _)) => info.key == "key" ++ string_of_int(i)))
 | 
			
		||||
        ) {
 | 
			
		||||
        | Some((_i, (key, label))) =>
 | 
			
		||||
          setLabel(_ => label);
 | 
			
		||||
          switch (onSelect) {
 | 
			
		||||
          | Some(onSelect) => onSelect(Some(key))
 | 
			
		||||
          | None => ()
 | 
			
		||||
          };
 | 
			
		||||
        | None => () // Error, could not find selected key among values
 | 
			
		||||
        }
 | 
			
		||||
      }>
 | 
			
		||||
      // Was a little worried about using a straight int as a key,
 | 
			
		||||
      // in case some conversions may go wrong or something, so added
 | 
			
		||||
      // "key" before
 | 
			
		||||
 | 
			
		||||
        {values
 | 
			
		||||
         |> E.L.React.fmapi((i, (_key, label)) =>
 | 
			
		||||
              <Menu.Item key={"key" ++ string_of_int(i)}>
 | 
			
		||||
                label->React.string
 | 
			
		||||
              </Menu.Item>
 | 
			
		||||
            )}
 | 
			
		||||
      </Menu>
 | 
			
		||||
  </DropdownMenu>;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										26
									
								
								foretold/components/src/Form/FormStyles.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								foretold/components/src/Form/FormStyles.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,26 @@
 | 
			
		|||
open Css;
 | 
			
		||||
 | 
			
		||||
let widthStyle = (~fullWidth=false, ()) => {
 | 
			
		||||
  let formInputInset =
 | 
			
		||||
    style([
 | 
			
		||||
      border(`px(1), `solid, Settings.border),
 | 
			
		||||
      borderRadius(`px(4)),
 | 
			
		||||
      padding(`em(0.6)),
 | 
			
		||||
      boxShadows([
 | 
			
		||||
        Shadow.box(
 | 
			
		||||
          ~x=zero,
 | 
			
		||||
          ~y=px(1),
 | 
			
		||||
          ~blur=px(2),
 | 
			
		||||
          ~inset=true,
 | 
			
		||||
          rgba(0, 0, 1, 0.08),
 | 
			
		||||
        ),
 | 
			
		||||
      ]),
 | 
			
		||||
      fontSize(`em(1.0)),
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
  let inputFullWidth =
 | 
			
		||||
    style([width(`percent(100.)), boxSizing(`borderBox)]);
 | 
			
		||||
 | 
			
		||||
  let textAreaFullWidth = merge([formInputInset, inputFullWidth]);
 | 
			
		||||
  fullWidth ? textAreaFullWidth : formInputInset;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										5
									
								
								foretold/components/src/Form/InputLabel.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								foretold/components/src/Form/InputLabel.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
let inputHeader =
 | 
			
		||||
  Css.(style([paddingLeft(`em(0.4)), paddingBottom(`em(0.6))]));
 | 
			
		||||
 | 
			
		||||
[@react.component]
 | 
			
		||||
let make = (~children) => <div className=inputHeader> children </div>;
 | 
			
		||||
							
								
								
									
										5
									
								
								foretold/components/src/Form/TextArea.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								foretold/components/src/Form/TextArea.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
[@react.component]
 | 
			
		||||
let make = (~fullWidth=false, ~rows=5, ~value=?) =>
 | 
			
		||||
  <textarea className={FormStyles.widthStyle(~fullWidth, ())} rows>
 | 
			
		||||
    {value |> E.O.React.fmapOrNull(React.string)}
 | 
			
		||||
  </textarea>;
 | 
			
		||||
							
								
								
									
										7
									
								
								foretold/components/src/Form/TextInput.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								foretold/components/src/Form/TextInput.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
[@react.component]
 | 
			
		||||
let make = (~fullWidth=false, ~placeholder=?) =>
 | 
			
		||||
  <input
 | 
			
		||||
    type_="text"
 | 
			
		||||
    className={FormStyles.widthStyle(~fullWidth, ())}
 | 
			
		||||
    ?placeholder
 | 
			
		||||
  />;
 | 
			
		||||
							
								
								
									
										70
									
								
								foretold/components/src/GroupHeader.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								foretold/components/src/GroupHeader.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,70 @@
 | 
			
		|||
open Base;
 | 
			
		||||
 | 
			
		||||
module Styles = {
 | 
			
		||||
  open Css;
 | 
			
		||||
  let outer =
 | 
			
		||||
    style(
 | 
			
		||||
      [
 | 
			
		||||
        backgroundColor(Colors.white),
 | 
			
		||||
        borderBottom(`px(1), `solid, Colors.accentBlueO8),
 | 
			
		||||
      ]
 | 
			
		||||
      @ BaseStyles.fullWidthFloatLeft,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
  let inner =
 | 
			
		||||
    style(
 | 
			
		||||
      [boxSizing(`borderBox), padding2(~v=`em(0.5), ~h=`em(2.0))]
 | 
			
		||||
      @ BaseStyles.fullWidthFloatLeft,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
  let actionButtonPosition =
 | 
			
		||||
    style([
 | 
			
		||||
      BaseStyles.floatRight,
 | 
			
		||||
      marginLeft(`em(2.)),
 | 
			
		||||
      marginTop(`em(0.2)),
 | 
			
		||||
    ]);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
let actionButton = (~variant: Button.variant=Primary, ~children) =>
 | 
			
		||||
  <Button
 | 
			
		||||
    variant
 | 
			
		||||
    isDisabled=false
 | 
			
		||||
    size=Button.(Medium)
 | 
			
		||||
    className=Css.(merge([Styles.actionButtonPosition]))
 | 
			
		||||
    ?children
 | 
			
		||||
  />;
 | 
			
		||||
 | 
			
		||||
[@react.component]
 | 
			
		||||
let make = (~children) =>
 | 
			
		||||
  <Div styles=[Styles.outer]>
 | 
			
		||||
    <Div styles=[Styles.inner]> ...children </Div>
 | 
			
		||||
  </Div>;
 | 
			
		||||
 | 
			
		||||
module SubHeader = {
 | 
			
		||||
  [@react.component]
 | 
			
		||||
  let make = (~children) =>
 | 
			
		||||
    <Div
 | 
			
		||||
      styles=[
 | 
			
		||||
        Css.(
 | 
			
		||||
          style(
 | 
			
		||||
            [backgroundColor(Colors.lighterGrayBackground)]
 | 
			
		||||
            @ BaseStyles.fullWidthFloatLeft,
 | 
			
		||||
          )
 | 
			
		||||
        ),
 | 
			
		||||
      ]>
 | 
			
		||||
      <Div
 | 
			
		||||
        styles=[
 | 
			
		||||
          Css.(
 | 
			
		||||
            style(
 | 
			
		||||
              [
 | 
			
		||||
                padding2(~v=`em(0.0), ~h=`em(2.0)),
 | 
			
		||||
                borderBottom(`px(1), `solid, Settings.border),
 | 
			
		||||
              ]
 | 
			
		||||
              @ BaseStyles.fullWidthFloatLeft,
 | 
			
		||||
            )
 | 
			
		||||
          ),
 | 
			
		||||
        ]>
 | 
			
		||||
        children
 | 
			
		||||
      </Div>
 | 
			
		||||
    </Div>;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										47
									
								
								foretold/components/src/HelpDropdown.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								foretold/components/src/HelpDropdown.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,47 @@
 | 
			
		|||
type content = {
 | 
			
		||||
  headerContent: ReasonReact.reactElement,
 | 
			
		||||
  bodyContent: ReasonReact.reactElement,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module Overlay = {
 | 
			
		||||
  module Styles = {
 | 
			
		||||
    open Css;
 | 
			
		||||
    let className = style([maxWidth(`em(30.))]);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  [@react.component]
 | 
			
		||||
  let make = (~content) =>
 | 
			
		||||
    <div className=Styles.className>
 | 
			
		||||
      <PageCard>
 | 
			
		||||
        <PageCard.HeaderRow>
 | 
			
		||||
          <Div float=`left>
 | 
			
		||||
            <PageCard.HeaderRow.Title>
 | 
			
		||||
              <span className=Css.(style([marginRight(`em(0.4))]))>
 | 
			
		||||
                <Icon.Questionmark isInteractive=false />
 | 
			
		||||
              </span>
 | 
			
		||||
              {content.headerContent}
 | 
			
		||||
            </PageCard.HeaderRow.Title>
 | 
			
		||||
          </Div>
 | 
			
		||||
        </PageCard.HeaderRow>
 | 
			
		||||
        <PageCard.Body>
 | 
			
		||||
          <PageCard.BodyPadding v={`em(0.5)}>
 | 
			
		||||
            <span
 | 
			
		||||
              className=Css.(
 | 
			
		||||
                style([
 | 
			
		||||
                  color(Settings.Text.LightBackground.p),
 | 
			
		||||
                  lineHeight(`em(1.5)),
 | 
			
		||||
                ])
 | 
			
		||||
              )>
 | 
			
		||||
              {content.bodyContent}
 | 
			
		||||
            </span>
 | 
			
		||||
          </PageCard.BodyPadding>
 | 
			
		||||
        </PageCard.Body>
 | 
			
		||||
      </PageCard>
 | 
			
		||||
    </div>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
[@react.component]
 | 
			
		||||
let make = (~content) =>
 | 
			
		||||
  <Dropdown overlay={<Overlay content />} trigger=Dropdown.Hover>
 | 
			
		||||
    <span> <Icon.Questionmark isInteractive=true /> </span>
 | 
			
		||||
  </Dropdown>;
 | 
			
		||||
							
								
								
									
										97
									
								
								foretold/components/src/MeasurableForm.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								foretold/components/src/MeasurableForm.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,97 @@
 | 
			
		|||
open Base;
 | 
			
		||||
 | 
			
		||||
module PageCard = PageCard;
 | 
			
		||||
module Tab = Tab;
 | 
			
		||||
module TabList = TabList;
 | 
			
		||||
 | 
			
		||||
// Could this be in some flexrow or part of Div component?
 | 
			
		||||
let flexRowContainer =
 | 
			
		||||
  Css.(style([margin2(~v=`zero, ~h=`px(-6)), alignItems(`flexEnd)]));
 | 
			
		||||
let flexRowItem = Css.(style([margin2(~v=`zero, ~h=`px(6))]));
 | 
			
		||||
 | 
			
		||||
type tabs =
 | 
			
		||||
  | SimpleTab
 | 
			
		||||
  | FreeformTab
 | 
			
		||||
  | CustomTab;
 | 
			
		||||
 | 
			
		||||
type state = {selectedTab: tabs};
 | 
			
		||||
 | 
			
		||||
type action =
 | 
			
		||||
  | ChangeTab(tabs);
 | 
			
		||||
 | 
			
		||||
[@react.component]
 | 
			
		||||
let make = (~cdf: Types.Dist.t) => {
 | 
			
		||||
  let (selectedTab, setSelectedTab) = React.useState(() => SimpleTab);
 | 
			
		||||
 | 
			
		||||
  <PageCard>
 | 
			
		||||
    <PageCard.HeaderRow>
 | 
			
		||||
      <PageCard.HeaderRow.Title>
 | 
			
		||||
        "New Prediction"->React.string
 | 
			
		||||
        <HelpDropdown
 | 
			
		||||
          content=HelpDropdown.{
 | 
			
		||||
            headerContent: "sdf" |> ReasonReact.string,
 | 
			
		||||
            bodyContent: "sdfsdfsd" |> ReasonReact.string,
 | 
			
		||||
          }
 | 
			
		||||
        />
 | 
			
		||||
      </PageCard.HeaderRow.Title>
 | 
			
		||||
    </PageCard.HeaderRow>
 | 
			
		||||
    <PageCard.Section flex=true padding=`none>
 | 
			
		||||
      <TabList
 | 
			
		||||
        selected=selectedTab
 | 
			
		||||
        onClick={key => setSelectedTab(_ => key)}
 | 
			
		||||
        list=[
 | 
			
		||||
          (SimpleTab, "Simple"),
 | 
			
		||||
          (FreeformTab, "Free-form"),
 | 
			
		||||
          (CustomTab, "Custom"),
 | 
			
		||||
        ]
 | 
			
		||||
        flex=true
 | 
			
		||||
      />
 | 
			
		||||
    </PageCard.Section>
 | 
			
		||||
    <PageCard.Section background=`grey border=`bottom padding=`top>
 | 
			
		||||
      <CdfChart__Large cdf width=None />
 | 
			
		||||
    </PageCard.Section>
 | 
			
		||||
    <PageCard.Section background=`grey>
 | 
			
		||||
      {switch (selectedTab) {
 | 
			
		||||
       | SimpleTab =>
 | 
			
		||||
         <Div flexDirection=`row styles=[flexRowContainer]>
 | 
			
		||||
           <Div flex={`num(1.0)} styles=[flexRowItem]>
 | 
			
		||||
             <InputLabel> "Min"->React.string </InputLabel>
 | 
			
		||||
             <TextInput fullWidth=true />
 | 
			
		||||
           </Div>
 | 
			
		||||
           <Div flex={`num(1.0)} styles=[flexRowItem]>
 | 
			
		||||
             <InputLabel> "Max"->React.string </InputLabel>
 | 
			
		||||
             <TextInput fullWidth=true />
 | 
			
		||||
           </Div>
 | 
			
		||||
           <Div styles=[flexRowItem]>
 | 
			
		||||
             <Button variant=Button.Secondary> "Clear"->React.string </Button>
 | 
			
		||||
           </Div>
 | 
			
		||||
         </Div>
 | 
			
		||||
       | FreeformTab => <TextInput fullWidth=true placeholder="5 to 50" />
 | 
			
		||||
       | CustomTab =>
 | 
			
		||||
         <div>
 | 
			
		||||
           <div>
 | 
			
		||||
             <DropdownSelect
 | 
			
		||||
               initialValue={Some("CDF")}
 | 
			
		||||
               values=[("CDF", "CDF"), ("PDF", "PDF")]
 | 
			
		||||
             />
 | 
			
		||||
             <Icon.Questionmark />
 | 
			
		||||
           </div>
 | 
			
		||||
           <PageCard.VerticalSpace />
 | 
			
		||||
           <Alert type_=`error>
 | 
			
		||||
             "Input is not a valid PDF"->React.string
 | 
			
		||||
           </Alert>
 | 
			
		||||
           <PageCard.VerticalSpace />
 | 
			
		||||
           <TextArea rows=4 fullWidth=true />
 | 
			
		||||
         </div>
 | 
			
		||||
       }}
 | 
			
		||||
    </PageCard.Section>
 | 
			
		||||
    <PageCard.Section>
 | 
			
		||||
      <InputLabel> "Comment"->React.string </InputLabel>
 | 
			
		||||
      <TextArea fullWidth=true />
 | 
			
		||||
      <PageCard.VerticalSpace />
 | 
			
		||||
      <Button variant=Button.Primary fullWidth=true size=Button.(Large)>
 | 
			
		||||
        "Submit Prediction"->React.string
 | 
			
		||||
      </Button>
 | 
			
		||||
    </PageCard.Section>
 | 
			
		||||
  </PageCard>;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										117
									
								
								foretold/components/src/MyCommunities.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								foretold/components/src/MyCommunities.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,117 @@
 | 
			
		|||
type item = {
 | 
			
		||||
  name: string,
 | 
			
		||||
  icon: string,
 | 
			
		||||
  href: string,
 | 
			
		||||
  bookmark: bool,
 | 
			
		||||
  onClick: ReactEvent.Mouse.t => unit,
 | 
			
		||||
  onBookmark: ReactEvent.Mouse.t => unit,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module Styles = {
 | 
			
		||||
  open Css;
 | 
			
		||||
  let item =
 | 
			
		||||
    style([
 | 
			
		||||
      flex(`num(1.)),
 | 
			
		||||
      textDecoration(`none),
 | 
			
		||||
      padding4(
 | 
			
		||||
        ~top=`em(0.3),
 | 
			
		||||
        ~left=`em(1.5),
 | 
			
		||||
        ~right=`em(1.5),
 | 
			
		||||
        ~bottom=`em(0.3),
 | 
			
		||||
      ),
 | 
			
		||||
      hover([background(Settings.buttonHover)]),
 | 
			
		||||
      display(`flex),
 | 
			
		||||
      flexDirection(`row),
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
  let header =
 | 
			
		||||
    style([
 | 
			
		||||
      flex(`num(1.)),
 | 
			
		||||
      textDecoration(`none),
 | 
			
		||||
      fontSize(`rem(0.8)),
 | 
			
		||||
      padding4(
 | 
			
		||||
        ~top=`em(1.5),
 | 
			
		||||
        ~left=`em(1.8),
 | 
			
		||||
        ~right=`em(1.8),
 | 
			
		||||
        ~bottom=`em(0.9),
 | 
			
		||||
      ),
 | 
			
		||||
      color(`hex("999")),
 | 
			
		||||
      display(`flex),
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
  let itemIcon =
 | 
			
		||||
    style([
 | 
			
		||||
      color(`hex("7e8aa1")),
 | 
			
		||||
      fontSize(`rem(1.1)),
 | 
			
		||||
      marginTop(`em(-0.1)),
 | 
			
		||||
      cursor(`pointer),
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
  let itemText = style([color(`hex("262c37")), cursor(`pointer)]);
 | 
			
		||||
 | 
			
		||||
  let notBookmarkedIcon =
 | 
			
		||||
    style([
 | 
			
		||||
      hover([color(`hex("7e8aa1"))]),
 | 
			
		||||
      color(`hex("f0f1f4")),
 | 
			
		||||
      fontSize(`rem(1.1)),
 | 
			
		||||
      marginTop(`em(-0.1)),
 | 
			
		||||
      cursor(`pointer),
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
  let bookmarkedIcon =
 | 
			
		||||
    style([
 | 
			
		||||
      hover([color(`hex("7e8aa1"))]),
 | 
			
		||||
      color(`hex("7e8aa1")),
 | 
			
		||||
      fontSize(`rem(1.1)),
 | 
			
		||||
      marginTop(`em(-0.1)),
 | 
			
		||||
      cursor(`pointer),
 | 
			
		||||
    ]);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module Item = {
 | 
			
		||||
  [@react.component]
 | 
			
		||||
  let make = (~item) => {
 | 
			
		||||
    <div className=Styles.item>
 | 
			
		||||
      <Div flex={`num(1.)} onClick={item.onClick} className=Styles.itemIcon>
 | 
			
		||||
        <ReactKitIcon icon={item.icon} />
 | 
			
		||||
      </Div>
 | 
			
		||||
      <Div flex={`num(7.)} onClick={item.onClick} className=Styles.itemText>
 | 
			
		||||
        {item.name |> ReasonReact.string}
 | 
			
		||||
      </Div>
 | 
			
		||||
    </div>;
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module ChannelItem = {
 | 
			
		||||
  [@react.component]
 | 
			
		||||
  let make = (~item) => {
 | 
			
		||||
    let bookmarkStyle =
 | 
			
		||||
      item.bookmark ? Styles.bookmarkedIcon : Styles.notBookmarkedIcon;
 | 
			
		||||
 | 
			
		||||
    <div className=Styles.item>
 | 
			
		||||
      <Div flex={`num(1.)} onClick={item.onClick} className=Styles.itemIcon>
 | 
			
		||||
        <ReactKitIcon icon={item.icon} />
 | 
			
		||||
      </Div>
 | 
			
		||||
      <Div flex={`num(7.)} onClick={item.onClick} className=Styles.itemText>
 | 
			
		||||
        {item.name |> ReasonReact.string}
 | 
			
		||||
      </Div>
 | 
			
		||||
      <Div flex={`num(0.5)} onClick={item.onBookmark} className=bookmarkStyle>
 | 
			
		||||
        <ReactKitIcon icon="STAR_FULL" />
 | 
			
		||||
      </Div>
 | 
			
		||||
    </div>;
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module Header = {
 | 
			
		||||
  [@react.component]
 | 
			
		||||
  let make = (~name: string) => {
 | 
			
		||||
    <Div flex={`num(1.)} className=Styles.header>
 | 
			
		||||
      {name |> ReasonReact.string}
 | 
			
		||||
    </Div>;
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
[@react.component]
 | 
			
		||||
let make = (~children) => {
 | 
			
		||||
  <Div flexDirection=`column> children </Div>;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										32
									
								
								foretold/components/src/NumberShower/NumberShower.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								foretold/components/src/NumberShower/NumberShower.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,32 @@
 | 
			
		|||
module JS = {
 | 
			
		||||
  [@bs.deriving abstract]
 | 
			
		||||
  type numberPresentation = {
 | 
			
		||||
    value: string,
 | 
			
		||||
    power: option(float),
 | 
			
		||||
    symbol: option(string),
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  [@bs.module "./numberShower.js"]
 | 
			
		||||
  external numberShow: (float, int) => numberPresentation = "numberShow";
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
let sup = Css.(style([fontSize(`em(0.6)), verticalAlign(`super)]));
 | 
			
		||||
 | 
			
		||||
[@react.component]
 | 
			
		||||
let make = (~number, ~precision) => {
 | 
			
		||||
  let numberWithPresentation = JS.numberShow(number, precision);
 | 
			
		||||
  <span>
 | 
			
		||||
    {JS.valueGet(numberWithPresentation) |> ReasonReact.string}
 | 
			
		||||
    {JS.symbolGet(numberWithPresentation)
 | 
			
		||||
     |> E.O.React.fmapOrNull(ReasonReact.string)}
 | 
			
		||||
    {JS.powerGet(numberWithPresentation)
 | 
			
		||||
     |> E.O.React.fmapOrNull(e =>
 | 
			
		||||
          <span>
 | 
			
		||||
            {{j|\u00b710|j} |> ReasonReact.string}
 | 
			
		||||
            <span className=sup>
 | 
			
		||||
              {e |> E.Float.toString |> ReasonReact.string}
 | 
			
		||||
            </span>
 | 
			
		||||
          </span>
 | 
			
		||||
        )}
 | 
			
		||||
  </span>;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										12
									
								
								foretold/components/src/NumberShower/PercentageShower.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								foretold/components/src/NumberShower/PercentageShower.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,12 @@
 | 
			
		|||
let percentageSign = Css.(style([opacity(0.5), marginLeft(`em(0.1))]));
 | 
			
		||||
 | 
			
		||||
[@react.component]
 | 
			
		||||
let make = (~percentage, ~precision) => {
 | 
			
		||||
  let numberWithPresentation =
 | 
			
		||||
    NumberShower.JS.numberShow(percentage, precision);
 | 
			
		||||
  <span>
 | 
			
		||||
    {NumberShower.JS.valueGet(numberWithPresentation)
 | 
			
		||||
     |> ReasonReact.string}
 | 
			
		||||
    <span className=percentageSign> {"%" |> ReasonReact.string} </span>
 | 
			
		||||
  </span>;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										65
									
								
								foretold/components/src/NumberShower/numberShower.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								foretold/components/src/NumberShower/numberShower.js
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,65 @@
 | 
			
		|||
// 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();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										200
									
								
								foretold/components/src/PageCard.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										200
									
								
								foretold/components/src/PageCard.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,200 @@
 | 
			
		|||
open Base;
 | 
			
		||||
 | 
			
		||||
[@react.component]
 | 
			
		||||
let make = (~children) =>
 | 
			
		||||
  <Div
 | 
			
		||||
    styles=[
 | 
			
		||||
      Css.(
 | 
			
		||||
        style(
 | 
			
		||||
          [
 | 
			
		||||
            background(Colors.white),
 | 
			
		||||
            border(`px(1), `solid, Colors.border),
 | 
			
		||||
            borderRadius(`px(5)),
 | 
			
		||||
          ]
 | 
			
		||||
          @ BaseStyles.fullWidthFloatLeft,
 | 
			
		||||
        )
 | 
			
		||||
      ),
 | 
			
		||||
    ]>
 | 
			
		||||
    children
 | 
			
		||||
  </Div>;
 | 
			
		||||
 | 
			
		||||
let defaultPadding = Css.padding2(~v=`em(0.0), ~h=`em(1.5));
 | 
			
		||||
 | 
			
		||||
module HeaderRow = {
 | 
			
		||||
  module Styles = {
 | 
			
		||||
    let itemTopPadding = Css.paddingTop(`em(0.5));
 | 
			
		||||
    let itemBottomPadding = Css.paddingBottom(`em(0.35));
 | 
			
		||||
    let itemRightPadding = Css.paddingRight(`em(0.9));
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  module Title = {
 | 
			
		||||
    [@react.component]
 | 
			
		||||
    let make = (~children) =>
 | 
			
		||||
      <Div
 | 
			
		||||
        styles=[
 | 
			
		||||
          Css.(
 | 
			
		||||
            style([
 | 
			
		||||
              color(Colors.textDark),
 | 
			
		||||
              paddingTop(`em(0.6)),
 | 
			
		||||
              paddingBottom(`em(0.6)),
 | 
			
		||||
              Settings.FontWeights.heavy,
 | 
			
		||||
            ])
 | 
			
		||||
          ),
 | 
			
		||||
        ]>
 | 
			
		||||
        children
 | 
			
		||||
      </Div>;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  [@react.component]
 | 
			
		||||
  let make = (~children) =>
 | 
			
		||||
    <Div
 | 
			
		||||
      styles=[
 | 
			
		||||
        Css.(
 | 
			
		||||
          style(
 | 
			
		||||
            [
 | 
			
		||||
              borderBottom(`px(1), `solid, Colors.accentBlueO8),
 | 
			
		||||
              defaultPadding,
 | 
			
		||||
            ]
 | 
			
		||||
            @ BaseStyles.fullWidthFloatLeft,
 | 
			
		||||
          )
 | 
			
		||||
        ),
 | 
			
		||||
      ]>
 | 
			
		||||
      children
 | 
			
		||||
    </Div>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module Body = {
 | 
			
		||||
  [@react.component]
 | 
			
		||||
  let make = (~children) =>
 | 
			
		||||
    <Div styles=[Css.style(BaseStyles.fullWidthFloatLeft)]> children </Div>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module BodyPadding = {
 | 
			
		||||
  [@react.component]
 | 
			
		||||
  let make = (~v=`em(1.5), ~children) =>
 | 
			
		||||
    <Div
 | 
			
		||||
      styles=[
 | 
			
		||||
        Css.style(
 | 
			
		||||
          [Css.padding2(~v, ~h=`em(1.5))] @ BaseStyles.fullWidthFloatLeft,
 | 
			
		||||
        ),
 | 
			
		||||
      ]>
 | 
			
		||||
      children
 | 
			
		||||
    </Div>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module Section = {
 | 
			
		||||
  module StyleProps = {
 | 
			
		||||
    open Css;
 | 
			
		||||
    type background = [ | `white | `grey];
 | 
			
		||||
    type padding = [ | `all | `none | `top | `bottom];
 | 
			
		||||
    type border = [ | `none | `top | `bottom | `topBottom];
 | 
			
		||||
 | 
			
		||||
    let baseClass = style([clear(`both)]);
 | 
			
		||||
    let bgGrey = style([backgroundColor(Settings.smokeWhite)]);
 | 
			
		||||
    let paddingAll = style([padding(`em(1.5))]);
 | 
			
		||||
    let paddingTop =
 | 
			
		||||
      style([
 | 
			
		||||
        padding4(~top=`em(1.5), ~right=`zero, ~bottom=`zero, ~left=`zero),
 | 
			
		||||
      ]);
 | 
			
		||||
    let paddingBottom =
 | 
			
		||||
      style([
 | 
			
		||||
        padding4(~top=`zero, ~right=`zero, ~bottom=`em(1.5), ~left=`zero),
 | 
			
		||||
      ]);
 | 
			
		||||
    let borderTop =
 | 
			
		||||
      style([borderTop(`px(1), `solid, Settings.accentBlue1a)]);
 | 
			
		||||
    let borderBottom =
 | 
			
		||||
      style([borderBottom(`px(1), `solid, Settings.accentBlue1a)]);
 | 
			
		||||
    let flexCls = style([display(`flex)]);
 | 
			
		||||
 | 
			
		||||
    [@bs.send] external push: (Js.Array.t('a), 'a) => unit = "";
 | 
			
		||||
    let toClasses =
 | 
			
		||||
        (background: background, border: border, padding: padding, flex: bool) => {
 | 
			
		||||
      // Array for more speed and some imperative ease
 | 
			
		||||
      let cls = [|baseClass|];
 | 
			
		||||
      // Background
 | 
			
		||||
      switch (background) {
 | 
			
		||||
      | `grey => push(cls, bgGrey)
 | 
			
		||||
      | _ => ()
 | 
			
		||||
      };
 | 
			
		||||
      // Padding
 | 
			
		||||
      switch (padding) {
 | 
			
		||||
      | `all => push(cls, paddingAll)
 | 
			
		||||
      | `none => ()
 | 
			
		||||
      | `top => push(cls, paddingTop)
 | 
			
		||||
      | `bottom => push(cls, paddingBottom)
 | 
			
		||||
      };
 | 
			
		||||
      // Border
 | 
			
		||||
      switch (border) {
 | 
			
		||||
      | `none => ()
 | 
			
		||||
      | `top => push(cls, borderTop)
 | 
			
		||||
      | `bottom => push(cls, borderBottom)
 | 
			
		||||
      | `topBottom =>
 | 
			
		||||
        push(cls, borderTop);
 | 
			
		||||
        push(cls, borderBottom);
 | 
			
		||||
      };
 | 
			
		||||
      // Flex
 | 
			
		||||
      if (flex) {
 | 
			
		||||
        push(cls, flexCls);
 | 
			
		||||
      };
 | 
			
		||||
      Js.Array.joinWith(" ", cls);
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Section of a PageCard
 | 
			
		||||
   * background: `white (default) | `grey
 | 
			
		||||
   * border: `top | `bottom | `none (default)
 | 
			
		||||
   * padding: `none | `top | `bottom | `all (default)
 | 
			
		||||
   * flex: true | false
 | 
			
		||||
   */
 | 
			
		||||
  [@react.component]
 | 
			
		||||
  let make =
 | 
			
		||||
      (
 | 
			
		||||
        ~background: StyleProps.background=`white,
 | 
			
		||||
        ~border: StyleProps.border=`none,
 | 
			
		||||
        ~padding: StyleProps.padding=`all,
 | 
			
		||||
        ~flex=false,
 | 
			
		||||
        ~children,
 | 
			
		||||
      ) =>
 | 
			
		||||
    <div className={StyleProps.toClasses(background, border, padding, flex)}>
 | 
			
		||||
      children
 | 
			
		||||
    </div>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module VerticalSpace = {
 | 
			
		||||
  let spaceStyle = Css.(style([marginTop(`em(1.5))]));
 | 
			
		||||
 | 
			
		||||
  [@react.component]
 | 
			
		||||
  let make = _ => <div className=spaceStyle />;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module H1 = {
 | 
			
		||||
  [@react.component]
 | 
			
		||||
  let make = (~children) =>
 | 
			
		||||
    <h1
 | 
			
		||||
      className=Css.(
 | 
			
		||||
        style(
 | 
			
		||||
          [
 | 
			
		||||
            fontSize(`em(1.15)),
 | 
			
		||||
            color(`hex("192D44")),
 | 
			
		||||
            Settings.FontWeights.heavy,
 | 
			
		||||
            marginTop(`em(0.0)),
 | 
			
		||||
            marginBottom(`em(0.4)),
 | 
			
		||||
          ]
 | 
			
		||||
          @ BaseStyles.fullWidthFloatLeft,
 | 
			
		||||
        )
 | 
			
		||||
      )>
 | 
			
		||||
      children
 | 
			
		||||
    </h1>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module P = {
 | 
			
		||||
  [@react.component]
 | 
			
		||||
  let make = (~children) =>
 | 
			
		||||
    <p
 | 
			
		||||
      className=Css.(
 | 
			
		||||
        style([color(Colors.Text.LightBackground.p), lineHeight(`em(1.5))])
 | 
			
		||||
      )>
 | 
			
		||||
      children
 | 
			
		||||
    </p>;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										58
									
								
								foretold/components/src/StateStatus.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								foretold/components/src/StateStatus.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,58 @@
 | 
			
		|||
type expectedResolutionDate = MomentRe.Moment.t;
 | 
			
		||||
type resolutionDate = MomentRe.Moment.t;
 | 
			
		||||
 | 
			
		||||
module State = {
 | 
			
		||||
  type t =
 | 
			
		||||
    | OPEN(expectedResolutionDate)
 | 
			
		||||
    | PENDING(expectedResolutionDate)
 | 
			
		||||
    | RESOLVED(resolutionDate);
 | 
			
		||||
 | 
			
		||||
  let toColor = (t: t) =>
 | 
			
		||||
    switch (t) {
 | 
			
		||||
    | OPEN(_) => Settings.Statuses.green
 | 
			
		||||
    | PENDING(_) => Settings.Statuses.yellow
 | 
			
		||||
    | RESOLVED(_) => Settings.Statuses.resolved
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
  let toText = (t: t) =>
 | 
			
		||||
    switch (t) {
 | 
			
		||||
    | OPEN(_) => "Open"
 | 
			
		||||
    | PENDING(_) => "Pending"
 | 
			
		||||
    | RESOLVED(_) => "Resolved"
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
  let toSubtext = (t: t) =>
 | 
			
		||||
    switch (t) {
 | 
			
		||||
    | OPEN(time) =>
 | 
			
		||||
      "Resolves " ++ MomentRe.Moment.fromNow(time, ~withoutSuffix=None)
 | 
			
		||||
    | PENDING(time) =>
 | 
			
		||||
      "Pending since " ++ MomentRe.Moment.fromNow(time, ~withoutSuffix=None)
 | 
			
		||||
    | RESOLVED(time) =>
 | 
			
		||||
      "Resolved " ++ MomentRe.Moment.fromNow(time, ~withoutSuffix=None)
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module Style = {
 | 
			
		||||
  let token = (state, fontSize') =>
 | 
			
		||||
    Css.(
 | 
			
		||||
      style([
 | 
			
		||||
        Settings.FontWeights.heavy,
 | 
			
		||||
        color(State.toColor(state)),
 | 
			
		||||
        marginRight(`em(1.0)),
 | 
			
		||||
        fontSize(fontSize'),
 | 
			
		||||
      ])
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
  let text = fontSize' =>
 | 
			
		||||
    Css.(style([color(Settings.accentBlue), fontSize(fontSize')]));
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
let make = (~state: State.t, ~fontSize=`em(0.9), ()) =>
 | 
			
		||||
  <div>
 | 
			
		||||
    <span className={Style.token(state, fontSize)}>
 | 
			
		||||
      {State.toText(state) |> ReasonReact.string}
 | 
			
		||||
    </span>
 | 
			
		||||
    <span className={Style.text(fontSize)}>
 | 
			
		||||
      {State.toSubtext(state) |> ReasonReact.string}
 | 
			
		||||
    </span>
 | 
			
		||||
  </div>;
 | 
			
		||||
							
								
								
									
										59
									
								
								foretold/components/src/SubmenuElements/PaginationButtons.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								foretold/components/src/SubmenuElements/PaginationButtons.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,59 @@
 | 
			
		|||
open Base;
 | 
			
		||||
 | 
			
		||||
type directionButton = {
 | 
			
		||||
  isDisabled: bool,
 | 
			
		||||
  onClick: ReactEvent.Mouse.t => unit,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
type currentValue =
 | 
			
		||||
  | Item(int)
 | 
			
		||||
  | Range(int, int);
 | 
			
		||||
 | 
			
		||||
type t = {
 | 
			
		||||
  currentValue,
 | 
			
		||||
  max: int,
 | 
			
		||||
  pageLeft: directionButton,
 | 
			
		||||
  pageRight: directionButton,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
let _text = (t: t) => {
 | 
			
		||||
  let currentValue =
 | 
			
		||||
    switch (t.currentValue) {
 | 
			
		||||
    | Item(i) => i |> string_of_int
 | 
			
		||||
    | Range(a, b) => (a |> string_of_int) ++ " - " ++ (b |> string_of_int)
 | 
			
		||||
    };
 | 
			
		||||
  currentValue ++ " of " ++ (t.max |> string_of_int);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module Styles = {
 | 
			
		||||
  let buttonLabel =
 | 
			
		||||
    Css.(
 | 
			
		||||
      style([
 | 
			
		||||
        BaseStyles.floatLeft,
 | 
			
		||||
        marginRight(`em(0.5)),
 | 
			
		||||
        marginTop(`em(0.12)),
 | 
			
		||||
        color(Colors.accentBlue),
 | 
			
		||||
      ])
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
  let rightButton = Css.(style([marginLeft(`em(0.3))]));
 | 
			
		||||
  let leftButton = Css.(style([marginLeft(`em(0.3))]));
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
let _directionLink = (t: directionButton, icon: string, positionStyles) =>
 | 
			
		||||
  <Button
 | 
			
		||||
    isDisabled={t.isDisabled}
 | 
			
		||||
    onClick={t.onClick}
 | 
			
		||||
    className=positionStyles
 | 
			
		||||
    size=Button.(Small)>
 | 
			
		||||
    <ReactKitIcon icon />
 | 
			
		||||
  </Button>;
 | 
			
		||||
 | 
			
		||||
let make = (t: t) =>
 | 
			
		||||
  <>
 | 
			
		||||
    <span className=Styles.buttonLabel>
 | 
			
		||||
      {_text(t) |> ReasonReact.string}
 | 
			
		||||
    </span>
 | 
			
		||||
    {_directionLink(t.pageLeft, "CHEVRON_LEFT", "")}
 | 
			
		||||
    {_directionLink(t.pageRight, "CHEVRON_RIGHT", Styles.rightButton)}
 | 
			
		||||
  </>;
 | 
			
		||||
							
								
								
									
										63
									
								
								foretold/components/src/SubmenuElements/Tab.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								foretold/components/src/SubmenuElements/Tab.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,63 @@
 | 
			
		|||
open Settings.Text;
 | 
			
		||||
 | 
			
		||||
let activeStyles =
 | 
			
		||||
  Css.[
 | 
			
		||||
    borderBottom(`px(2), `solid, LightBackground.active),
 | 
			
		||||
    color(LightBackground.active),
 | 
			
		||||
    hover([color(LightBackground.active)]),
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
let inactiveStyles =
 | 
			
		||||
  Css.[
 | 
			
		||||
    borderBottom(`px(2), `solid, Settings.clear),
 | 
			
		||||
    color(LightBackground.main),
 | 
			
		||||
    hover([color(LightBackground.active)]),
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
let allStyles =
 | 
			
		||||
  Css.[
 | 
			
		||||
    paddingBottom(`em(0.2)),
 | 
			
		||||
    paddingTop(`em(0.5)),
 | 
			
		||||
    paddingLeft(`em(0.4)),
 | 
			
		||||
    paddingRight(`em(0.4)),
 | 
			
		||||
    marginRight(`em(1.8)),
 | 
			
		||||
    BaseStyles.floatLeft,
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
let flexStyles =
 | 
			
		||||
  Css.[textAlign(`center), flexGrow(1.), padding2(~v=`em(0.8), ~h=`zero)];
 | 
			
		||||
 | 
			
		||||
module Button = {
 | 
			
		||||
  open Css;
 | 
			
		||||
  let noStyles = [
 | 
			
		||||
    borderWidth(`px(0)),
 | 
			
		||||
    backgroundColor(`transparent),
 | 
			
		||||
    userSelect(`none),
 | 
			
		||||
    cursor(`pointer),
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  [@react.component]
 | 
			
		||||
  let make = (~isActive=false, ~onClick=?, ~flex=false, ~children) =>
 | 
			
		||||
    <button
 | 
			
		||||
      disabled=isActive
 | 
			
		||||
      ?onClick
 | 
			
		||||
      className={Css.style(
 | 
			
		||||
        noStyles
 | 
			
		||||
        @ (isActive ? activeStyles : inactiveStyles)
 | 
			
		||||
        @ (flex ? flexStyles : allStyles),
 | 
			
		||||
      )}>
 | 
			
		||||
      children
 | 
			
		||||
    </button>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
[@react.component]
 | 
			
		||||
let make = (~isActive=false, ~onClick=?, ~flex=false, ~children) =>
 | 
			
		||||
  <Link
 | 
			
		||||
    isDisabled=false
 | 
			
		||||
    ?onClick
 | 
			
		||||
    className={Css.style(
 | 
			
		||||
      (isActive ? activeStyles : inactiveStyles)
 | 
			
		||||
      @ (flex ? flexStyles : allStyles),
 | 
			
		||||
    )}>
 | 
			
		||||
    children
 | 
			
		||||
  </Link>;
 | 
			
		||||
							
								
								
									
										51
									
								
								foretold/components/src/SubmenuElements/Tab2.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								foretold/components/src/SubmenuElements/Tab2.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,51 @@
 | 
			
		|||
open Base;
 | 
			
		||||
 | 
			
		||||
let styles = (~isDisabled=false, ~heightPadding=0, ()) => {
 | 
			
		||||
  let main =
 | 
			
		||||
    Css.(
 | 
			
		||||
      style([
 | 
			
		||||
        padding2(~v=`px(heightPadding), ~h=`px(6)),
 | 
			
		||||
        BaseStyles.floatLeft,
 | 
			
		||||
        borderRadius(Colors.BorderRadius.medium),
 | 
			
		||||
        border(`px(1), `solid, Colors.accentBlueO8),
 | 
			
		||||
        marginTop(`em(0.05)),
 | 
			
		||||
        lineHeight(`em(1.35)),
 | 
			
		||||
      ])
 | 
			
		||||
    );
 | 
			
		||||
  let disabledStyles = Css.(style([background(Colors.greydisabled)]));
 | 
			
		||||
  isDisabled ? Css.merge([disabledStyles, main]) : main;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
[@react.component]
 | 
			
		||||
let make = (~isActive, ~onClick=?, ~number: option(int)=?, ~children) => {
 | 
			
		||||
  let textStyle =
 | 
			
		||||
    Css.(style([BaseStyles.floatLeft, marginRight(`em(0.4))]));
 | 
			
		||||
 | 
			
		||||
  let colors =
 | 
			
		||||
    Colors.Text.(
 | 
			
		||||
      isActive
 | 
			
		||||
        ? Css.[
 | 
			
		||||
            color(LightBackground.active),
 | 
			
		||||
            hover([color(LightBackground.active)]),
 | 
			
		||||
          ]
 | 
			
		||||
        : Css.[
 | 
			
		||||
            color(LightBackground.main),
 | 
			
		||||
            hover([color(LightBackground.active)]),
 | 
			
		||||
          ]
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
  <Link
 | 
			
		||||
    ?onClick
 | 
			
		||||
    className=Css.(
 | 
			
		||||
      style([BaseStyles.floatLeft, marginRight(`em(1.8))] @ colors)
 | 
			
		||||
    )
 | 
			
		||||
    isDisabled=false>
 | 
			
		||||
    <span className=textStyle> children </span>
 | 
			
		||||
    {number
 | 
			
		||||
     |> E.O.React.fmapOrNull(number =>
 | 
			
		||||
          <span className={styles()}>
 | 
			
		||||
            {number |> string_of_int |> ReasonReact.string}
 | 
			
		||||
          </span>
 | 
			
		||||
        )}
 | 
			
		||||
  </Link>;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										156
									
								
								foretold/components/src/Table.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								foretold/components/src/Table.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,156 @@
 | 
			
		|||
open Base;
 | 
			
		||||
 | 
			
		||||
let defaultRowHorizontalPadding = `em(1.5);
 | 
			
		||||
 | 
			
		||||
module Styles = {
 | 
			
		||||
  let row =
 | 
			
		||||
    Css.(
 | 
			
		||||
      style(
 | 
			
		||||
        [
 | 
			
		||||
          padding2(~v=`zero, ~h=defaultRowHorizontalPadding),
 | 
			
		||||
          borderBottom(`px(1), `solid, Colors.accentBlue1a),
 | 
			
		||||
          display(`flex),
 | 
			
		||||
          flexDirection(`row),
 | 
			
		||||
          selector(":last-child", BaseStyles.borderNone),
 | 
			
		||||
        ]
 | 
			
		||||
        @ BaseStyles.fullWidthFloatLeft,
 | 
			
		||||
      )
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
  let textArea =
 | 
			
		||||
    Css.(
 | 
			
		||||
      style(
 | 
			
		||||
        [
 | 
			
		||||
          padding2(~v=`em(0.8), ~h=`em(1.0)),
 | 
			
		||||
          borderRadius(Colors.BorderRadius.tight),
 | 
			
		||||
          color(Colors.Text.LightBackground.p),
 | 
			
		||||
        ]
 | 
			
		||||
        @ BaseStyles.fullWidthFloatLeft,
 | 
			
		||||
      )
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
  let topRow =
 | 
			
		||||
    Css.(
 | 
			
		||||
      style(
 | 
			
		||||
        [
 | 
			
		||||
          padding2(~v=`zero, ~h=defaultRowHorizontalPadding),
 | 
			
		||||
          paddingTop(`em(0.4)),
 | 
			
		||||
          display(`flex),
 | 
			
		||||
          flexDirection(`row),
 | 
			
		||||
        ]
 | 
			
		||||
        @ BaseStyles.fullWidthFloatLeft,
 | 
			
		||||
      )
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
  let bottomRow =
 | 
			
		||||
    Css.(
 | 
			
		||||
      style(
 | 
			
		||||
        [
 | 
			
		||||
          padding2(~v=`zero, ~h=`em(0.4)),
 | 
			
		||||
          paddingBottom(`em(0.4)),
 | 
			
		||||
          display(`flex),
 | 
			
		||||
          flexDirection(`row),
 | 
			
		||||
          borderBottom(`px(1), `solid, Colors.accentBlue1a),
 | 
			
		||||
        ]
 | 
			
		||||
        @ BaseStyles.fullWidthFloatLeft,
 | 
			
		||||
      )
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
  let clickableRow =
 | 
			
		||||
    Css.(
 | 
			
		||||
      style([
 | 
			
		||||
        hover([background(Colors.lightGrayBackground)]),
 | 
			
		||||
        cursor(`pointer),
 | 
			
		||||
      ])
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
  module Elements = {
 | 
			
		||||
    let primaryText =
 | 
			
		||||
      Css.style([
 | 
			
		||||
        Css.fontSize(`em(1.05)),
 | 
			
		||||
        Css.lineHeight(`em(1.5)),
 | 
			
		||||
        Css.fontWeight(`num(600)),
 | 
			
		||||
        Css.color(`hex("384e67")),
 | 
			
		||||
      ]);
 | 
			
		||||
 | 
			
		||||
    let link' =
 | 
			
		||||
      Css.[
 | 
			
		||||
        marginRight(`em(1.0)),
 | 
			
		||||
        color(Colors.textMedium),
 | 
			
		||||
        hover([color(Colors.textDark)]),
 | 
			
		||||
      ];
 | 
			
		||||
 | 
			
		||||
    let link = (~isUnderlined=false, ()) =>
 | 
			
		||||
      Css.(
 | 
			
		||||
        style(isUnderlined ? link' @ [textDecoration(`underline)] : link')
 | 
			
		||||
      );
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module Cell = {
 | 
			
		||||
  let standardCellPadding =
 | 
			
		||||
    Css.(style([paddingTop(`em(0.7)), paddingBottom(`em(0.5))]));
 | 
			
		||||
 | 
			
		||||
  let style = flexAmount => Css.(style([flex(flexAmount)]));
 | 
			
		||||
 | 
			
		||||
  [@react.component]
 | 
			
		||||
  let make = (~flex, ~className="", ~properties=[], ~children) =>
 | 
			
		||||
    <Div
 | 
			
		||||
      className={Css.merge([
 | 
			
		||||
        style(flex),
 | 
			
		||||
        standardCellPadding,
 | 
			
		||||
        className,
 | 
			
		||||
        Css.style(properties),
 | 
			
		||||
      ])}>
 | 
			
		||||
      children
 | 
			
		||||
    </Div>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module HeaderRow = {
 | 
			
		||||
  module Styles = {
 | 
			
		||||
    let headerRow =
 | 
			
		||||
      Css.(
 | 
			
		||||
        style(
 | 
			
		||||
          [
 | 
			
		||||
            background(Colors.lightGrayBackground),
 | 
			
		||||
            color(Colors.Text.LightBackground.main),
 | 
			
		||||
            borderBottom(`px(1), `solid, Colors.accentBlueO8),
 | 
			
		||||
            display(`flex),
 | 
			
		||||
            flexDirection(`row),
 | 
			
		||||
            padding2(~v=`em(0.1), ~h=defaultRowHorizontalPadding),
 | 
			
		||||
          ]
 | 
			
		||||
          @ BaseStyles.fullWidthFloatLeft,
 | 
			
		||||
        )
 | 
			
		||||
      );
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  [@react.component]
 | 
			
		||||
  let make = (~children) => <Div styles=[Styles.headerRow]> children </Div>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module Row = {
 | 
			
		||||
  let textSection = text => <Div styles=[Styles.textArea]> text </Div>;
 | 
			
		||||
 | 
			
		||||
  [@react.component]
 | 
			
		||||
  let make = (~className="", ~bottomSubRow=?, ~onClick=?, ~children) => {
 | 
			
		||||
    switch (bottomSubRow) {
 | 
			
		||||
    | Some(bottomSubRow) =>
 | 
			
		||||
      let commonClasses =
 | 
			
		||||
        onClick |> E.O.isSome
 | 
			
		||||
          ? [Styles.clickableRow, className] : [className];
 | 
			
		||||
      <Div styles=commonClasses ?onClick>
 | 
			
		||||
        <Div styles=[Styles.topRow]> children </Div>
 | 
			
		||||
        <Div styles=[Styles.bottomRow]> bottomSubRow </Div>
 | 
			
		||||
      </Div>;
 | 
			
		||||
    | None =>
 | 
			
		||||
      let commonClasses =
 | 
			
		||||
        onClick |> E.O.isSome
 | 
			
		||||
          ? [Styles.row, Styles.clickableRow, className]
 | 
			
		||||
          : [Styles.row, className];
 | 
			
		||||
      <Div styles=commonClasses ?onClick> children </Div>;
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
[@react.component]
 | 
			
		||||
let make = (~children) => <div> children </div>;
 | 
			
		||||
							
								
								
									
										39
									
								
								foretold/components/src/lib/AutosizeTextareaInput.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								foretold/components/src/lib/AutosizeTextareaInput.js
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,39 @@
 | 
			
		|||
import TextareaAutosize from 'react-textarea-autosize';
 | 
			
		||||
import React from 'react';
 | 
			
		||||
 | 
			
		||||
export class AutosizeTextareaInput extends React.Component {
 | 
			
		||||
  /**
 | 
			
		||||
   * @param props
 | 
			
		||||
   */
 | 
			
		||||
  constructor(props) {
 | 
			
		||||
    super(props);
 | 
			
		||||
    this.state = { value: this.props.value || "" };
 | 
			
		||||
    this.handleChange = this.handleChange.bind(this);
 | 
			
		||||
    this.handleHeightChange = this.handleHeightChange.bind(this);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleChange(event) {
 | 
			
		||||
    const { value } = event.target;
 | 
			
		||||
    this.setState({ value });
 | 
			
		||||
    this.props.onChange(value);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleHeightChange() {
 | 
			
		||||
    this.props.onHeightChange();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    return React.createElement(TextareaAutosize, {
 | 
			
		||||
      style: this.props.style,
 | 
			
		||||
      className: this.props.className,
 | 
			
		||||
 | 
			
		||||
      rows: this.props.rows,
 | 
			
		||||
      maxRows: this.props.maxRows,
 | 
			
		||||
      minRows: this.props.minRows,
 | 
			
		||||
      value: this.state.value,
 | 
			
		||||
      onChange: this.handleChange,
 | 
			
		||||
      onHeightChange: this.handleHeightChange,
 | 
			
		||||
      useCacheForDOMMeasurements: this.props.useCacheForDOMMeasurements,
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										112
									
								
								foretold/components/src/lib/GuesstimateInput.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								foretold/components/src/lib/GuesstimateInput.js
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,112 @@
 | 
			
		|||
import React from 'react';
 | 
			
		||||
import _ from 'lodash';
 | 
			
		||||
 | 
			
		||||
import { Guesstimator } from '@foretold/guesstimator';
 | 
			
		||||
import { Samples } from '@foretold/cdf';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @param {number} ratio
 | 
			
		||||
 * @return {string}
 | 
			
		||||
 */
 | 
			
		||||
const minMaxRatio = (ratio) => {
 | 
			
		||||
  if (ratio < 100000) {
 | 
			
		||||
    return "SMALL";
 | 
			
		||||
  } else if (ratio < 10000000) {
 | 
			
		||||
    return "MEDIUM";
 | 
			
		||||
  } else {
 | 
			
		||||
    return "LARGE";
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @param samples
 | 
			
		||||
 * @return {string}
 | 
			
		||||
 */
 | 
			
		||||
const ratioSize = samples => {
 | 
			
		||||
  samples.sort();
 | 
			
		||||
  const minValue = samples.getPercentile(2);
 | 
			
		||||
  const maxValue = samples.getPercentile(98);
 | 
			
		||||
  return minMaxRatio(maxValue / minValue);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @param {number[]} values
 | 
			
		||||
 * @param {number} min
 | 
			
		||||
 * @param {number} max
 | 
			
		||||
 * @return {[number[], number[], boolean]}
 | 
			
		||||
 */
 | 
			
		||||
const toCdf = (values, min, max) => {
 | 
			
		||||
  let _values = values;
 | 
			
		||||
  if (_.isFinite(min)) _values = _.filter(_values, r => r > min);
 | 
			
		||||
  if (_.isFinite(max)) _values = _.filter(_values, r => r < max);
 | 
			
		||||
 | 
			
		||||
  const samples = new Samples(_values);
 | 
			
		||||
  const ratioSize$ = ratioSize(samples);
 | 
			
		||||
  const width = ratioSize$ === "SMALL" ? 20 : 1;
 | 
			
		||||
  const cdf = samples.toCdf({ size: 1000, width, min, max });
 | 
			
		||||
  return [cdf.ys, cdf.xs, ratioSize$ === "LARGE"];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export class GuesstimateInput extends React.Component {
 | 
			
		||||
  /**
 | 
			
		||||
   * @param props
 | 
			
		||||
   */
 | 
			
		||||
  constructor(props) {
 | 
			
		||||
    super(props);
 | 
			
		||||
    this.state = { value: this.props.initialValue || "", items: [] };
 | 
			
		||||
    this.handleChange = this.handleChange.bind(this);
 | 
			
		||||
    this.textInput = React.createRef();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  componentDidMount() {
 | 
			
		||||
    if (this.props.focusOnRender) {
 | 
			
		||||
      this.textInput.focus();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (this.state.value !== ""){
 | 
			
		||||
      this.handleChangeValue(this.state.value);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleChangeValue(value) {
 | 
			
		||||
    const text = value;
 | 
			
		||||
 | 
			
		||||
    let [_error, item] = Guesstimator.parse({ text });
 | 
			
		||||
    let parsedInput = item.parsedInput;
 | 
			
		||||
    let what = new Guesstimator({ parsedInput: parsedInput });
 | 
			
		||||
    let foo = what.sample(this.props.sampleCount);
 | 
			
		||||
    let values = _.filter(foo.values, _.isFinite);
 | 
			
		||||
 | 
			
		||||
    if (!!values) {
 | 
			
		||||
      this.setState({ value, items: values });
 | 
			
		||||
    } else {
 | 
			
		||||
      this.setState({ value, items: [] });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!values || values.length === 0) {
 | 
			
		||||
      this.props.onUpdate([[], [], false]);
 | 
			
		||||
    } else if (values.length === 1) {
 | 
			
		||||
      this.props.onUpdate([[1], values, false]);
 | 
			
		||||
    } else {
 | 
			
		||||
      const min = this.props.min;
 | 
			
		||||
      const max = this.props.max;
 | 
			
		||||
      this.props.onUpdate(toCdf(values, min, max));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.props.onChange(text);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  handleChange(event){
 | 
			
		||||
    this.handleChangeValue(event.target.value);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    return <input
 | 
			
		||||
      type="text"
 | 
			
		||||
      placeholder="10 to 100"
 | 
			
		||||
      value={this.state.value}
 | 
			
		||||
      onChange={this.handleChange}
 | 
			
		||||
      ref={input => this.textInput = input}
 | 
			
		||||
    />;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								foretold/components/src/lib/Hooks.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								foretold/components/src/lib/Hooks.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,15 @@
 | 
			
		|||
type size = {
 | 
			
		||||
  .
 | 
			
		||||
  "width": int,
 | 
			
		||||
  "height": int,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
[@bs.module "react-use"]
 | 
			
		||||
external useSize:
 | 
			
		||||
  (size => ReasonReact.reactElement, size) => (React.element, size) =
 | 
			
		||||
  "useSize";
 | 
			
		||||
 | 
			
		||||
type useTitleOptions = {. "restoreOnUnmount": bool};
 | 
			
		||||
 | 
			
		||||
[@bs.module "react-use"]
 | 
			
		||||
external useTitle: (string, useTitleOptions) => unit = "useTitle";
 | 
			
		||||
							
								
								
									
										36
									
								
								foretold/components/src/lib/ReAutosizeTextareaInput.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								foretold/components/src/lib/ReAutosizeTextareaInput.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,36 @@
 | 
			
		|||
let fn = (a: string) => ();
 | 
			
		||||
let fn2 = () => ();
 | 
			
		||||
 | 
			
		||||
[@bs.module "./AutosizeTextareaInput.js"]
 | 
			
		||||
external guesstimateInput: ReasonReact.reactClass = "AutosizeTextareaInput";
 | 
			
		||||
 | 
			
		||||
[@react.component]
 | 
			
		||||
let make =
 | 
			
		||||
    (
 | 
			
		||||
      ~rows: option(int)=?,
 | 
			
		||||
      ~maxRows: option(int)=?,
 | 
			
		||||
      ~minRows: option(int)=?,
 | 
			
		||||
      ~useCacheForDOMMeasurements: option(bool)=?,
 | 
			
		||||
      ~value="",
 | 
			
		||||
      ~onChange=fn,
 | 
			
		||||
      ~onHeightChange=fn2,
 | 
			
		||||
      ~className: option(string)=?,
 | 
			
		||||
      ~style: option(ReactDOMRe.Style.t)=?,
 | 
			
		||||
      ~children=ReasonReact.null,
 | 
			
		||||
    ) =>
 | 
			
		||||
  ReasonReact.wrapJsForReason(
 | 
			
		||||
    ~reactClass=guesstimateInput,
 | 
			
		||||
    ~props={
 | 
			
		||||
      "rows": rows,
 | 
			
		||||
      "maxRows": maxRows,
 | 
			
		||||
      "minRows": minRows,
 | 
			
		||||
      "useCacheForDOMMeasurements": useCacheForDOMMeasurements,
 | 
			
		||||
      "value": value,
 | 
			
		||||
      "onChange": onChange,
 | 
			
		||||
      "onHeightChange": onHeightChange,
 | 
			
		||||
      "className": className,
 | 
			
		||||
      "style": style,
 | 
			
		||||
    },
 | 
			
		||||
    children,
 | 
			
		||||
  )
 | 
			
		||||
  |> ReasonReact.element;
 | 
			
		||||
							
								
								
									
										32
									
								
								foretold/components/src/lib/ReGuesstimateInput.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								foretold/components/src/lib/ReGuesstimateInput.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,32 @@
 | 
			
		|||
let fn = (a: (array(float), array(float), bool)) => ();
 | 
			
		||||
let fn2 = (a: string) => ();
 | 
			
		||||
 | 
			
		||||
[@bs.module "./GuesstimateInput.js"]
 | 
			
		||||
external guesstimateInput: ReasonReact.reactClass = "GuesstimateInput";
 | 
			
		||||
 | 
			
		||||
[@react.component]
 | 
			
		||||
let make =
 | 
			
		||||
    (
 | 
			
		||||
      ~sampleCount=10000,
 | 
			
		||||
      ~min=None,
 | 
			
		||||
      ~max=None,
 | 
			
		||||
      ~initialValue=None,
 | 
			
		||||
      ~onUpdate=fn,
 | 
			
		||||
      ~onChange=fn2,
 | 
			
		||||
      ~focusOnRender=true,
 | 
			
		||||
      ~children=ReasonReact.null,
 | 
			
		||||
    ) =>
 | 
			
		||||
  ReasonReact.wrapJsForReason(
 | 
			
		||||
    ~reactClass=guesstimateInput,
 | 
			
		||||
    ~props={
 | 
			
		||||
      "sampleCount": sampleCount,
 | 
			
		||||
      "onUpdate": onUpdate,
 | 
			
		||||
      "initialValue": initialValue,
 | 
			
		||||
      "onChange": onChange,
 | 
			
		||||
      "min": min,
 | 
			
		||||
      "max": max,
 | 
			
		||||
      "focusOnRender": focusOnRender,
 | 
			
		||||
    },
 | 
			
		||||
    children,
 | 
			
		||||
  )
 | 
			
		||||
  |> ReasonReact.element;
 | 
			
		||||
							
								
								
									
										24
									
								
								foretold/components/src/lib/Title.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								foretold/components/src/lib/Title.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,24 @@
 | 
			
		|||
[@bs.val] external document: Dom.document = "document";
 | 
			
		||||
[@bs.set] external setTitleDom: (Dom.document, string) => unit = "title";
 | 
			
		||||
[@bs.get] external getTitleDom: Dom.document => string = "title";
 | 
			
		||||
 | 
			
		||||
let getTitle = () => getTitleDom(document);
 | 
			
		||||
let setTitle = setTitleDom(document);
 | 
			
		||||
let unsetTitle = (previousTitle) => setTitle(previousTitle);
 | 
			
		||||
 | 
			
		||||
let useTitle = (title: string): unit =>{
 | 
			
		||||
  let prev = getTitle();
 | 
			
		||||
  React.useEffect1(
 | 
			
		||||
    () => {
 | 
			
		||||
      setTitle(title);
 | 
			
		||||
      Some(_ => unsetTitle(prev));
 | 
			
		||||
    },
 | 
			
		||||
    [|title|],
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
[@react.component]
 | 
			
		||||
let make = (~title: string) => {
 | 
			
		||||
  useTitle(title);
 | 
			
		||||
  React.null;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										48
									
								
								foretold/components/src/lib/vega/PercentilesChart.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								foretold/components/src/lib/vega/PercentilesChart.js
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,48 @@
 | 
			
		|||
import * as _ from 'lodash';
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import * as vega from 'vega';
 | 
			
		||||
import { Cdf } from '@foretold/cdf';
 | 
			
		||||
 | 
			
		||||
import spec from './spec-percentiles';
 | 
			
		||||
 | 
			
		||||
export class PercentilesChart extends React.PureComponent {
 | 
			
		||||
 | 
			
		||||
  constructor(props) {
 | 
			
		||||
    super(props);
 | 
			
		||||
    this.containerRef = React.createRef();
 | 
			
		||||
    this.view = null;
 | 
			
		||||
 | 
			
		||||
    this.data = this.props.data.map(item => {
 | 
			
		||||
      const cdf = new Cdf(item.xs, item.ys);
 | 
			
		||||
      const p5 = cdf.findX(0.05);
 | 
			
		||||
      const p50 = cdf.findX(0.50);
 | 
			
		||||
      const p95 = cdf.findX(0.95);
 | 
			
		||||
      const timestamp = item.createdAt;
 | 
			
		||||
      return { p5, p50, p95, timestamp };
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    this.spec = _.cloneDeep(spec);
 | 
			
		||||
    this.spec.data[0].values = this.data;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  componentDidMount() {
 | 
			
		||||
    this.view = new vega.View(vega.parse(this.spec), {
 | 
			
		||||
      renderer: 'canvas',
 | 
			
		||||
      container: this.containerRef.current,
 | 
			
		||||
      hover: true
 | 
			
		||||
    });
 | 
			
		||||
    return this.view.runAsync();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  componentWillUnmount() {
 | 
			
		||||
    if (this.view) {
 | 
			
		||||
      this.view.finalize();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    return React.createElement("div", {
 | 
			
		||||
      ref: this.containerRef,
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								foretold/components/src/lib/vega/RePercentilesChart.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								foretold/components/src/lib/vega/RePercentilesChart.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
[@bs.module "./PercentilesChart.js"]
 | 
			
		||||
external percentilesChart: ReasonReact.reactClass = "PercentilesChart";
 | 
			
		||||
 | 
			
		||||
[@react.component]
 | 
			
		||||
let make = (~data, ~children=ReasonReact.null) =>
 | 
			
		||||
  ReasonReact.wrapJsForReason(
 | 
			
		||||
    ~reactClass=percentilesChart,
 | 
			
		||||
    ~props={"data": data},
 | 
			
		||||
    children,
 | 
			
		||||
  )
 | 
			
		||||
  |> ReasonReact.element;
 | 
			
		||||
							
								
								
									
										109
									
								
								foretold/components/src/lib/vega/spec-percentiles.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								foretold/components/src/lib/vega/spec-percentiles.json
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,109 @@
 | 
			
		|||
{
 | 
			
		||||
  "$schema": "https://vega.github.io/schema/vega/v5.json",
 | 
			
		||||
  "width": 900,
 | 
			
		||||
  "height": 200,
 | 
			
		||||
  "padding": 5,
 | 
			
		||||
  "data": [
 | 
			
		||||
    {
 | 
			
		||||
      "name": "facet",
 | 
			
		||||
      "values": [
 | 
			
		||||
        {"timestamp": "2020-01-20 14:55:01.412", "p5": 2, "p95": 3, "p50": 2.5},
 | 
			
		||||
        {"timestamp": "2020-01-23 14:55:01.412", "p5": 3, "p95": 5, "p50": 4},
 | 
			
		||||
        {"timestamp": "2020-01-24 14:55:01.412", "p5": 7, "p95": 8, "p50": 7.5},
 | 
			
		||||
        {"timestamp": "2020-01-24 14:55:01.412", "p5": 2, "p95": 3, "p50": 2.5},
 | 
			
		||||
        {"timestamp": "2020-01-28 14:55:01.412", "p5": 3, "p95": 50, "p50": 40},
 | 
			
		||||
        {"timestamp": "2020-01-29 14:55:01.412", "p5": 2, "p95": 3, "p50": 2.5}
 | 
			
		||||
      ],
 | 
			
		||||
      "format": {"type": "json", "parse": {"timestamp": "date"}}
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "table",
 | 
			
		||||
      "source": "facet",
 | 
			
		||||
      "transform": [
 | 
			
		||||
        {
 | 
			
		||||
          "type": "aggregate",
 | 
			
		||||
          "groupby": ["timestamp"],
 | 
			
		||||
          "ops": ["mean", "mean", "mean"],
 | 
			
		||||
          "fields": ["p5", "p95", "p50"],
 | 
			
		||||
          "as": ["p5", "p95", "p50"]
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "scales": [
 | 
			
		||||
    {
 | 
			
		||||
      "name": "xscale",
 | 
			
		||||
      "type": "time",
 | 
			
		||||
      "nice": "day",
 | 
			
		||||
      "domain": {"data": "facet", "field": "timestamp"},
 | 
			
		||||
      "range": "width"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "name": "yscale",
 | 
			
		||||
      "type": "linear",
 | 
			
		||||
      "range": "height",
 | 
			
		||||
      "nice": true,
 | 
			
		||||
      "zero": true,
 | 
			
		||||
      "domain": {"data": "facet", "field": "p95"}
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "axes": [
 | 
			
		||||
    {
 | 
			
		||||
      "orient": "bottom",
 | 
			
		||||
      "scale": "xscale",
 | 
			
		||||
      "format": "%m-%d",
 | 
			
		||||
      "grid": true,
 | 
			
		||||
      "title": "Days",
 | 
			
		||||
      "encode": {
 | 
			
		||||
        "grid": {"enter": {"stroke": {"value": "#ccc"}}},
 | 
			
		||||
        "ticks": {"enter": {"stroke": {"value": "#ccc"}}}
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "orient": "left",
 | 
			
		||||
      "scale": "yscale",
 | 
			
		||||
      "title": "Values and Percentiles",
 | 
			
		||||
      "grid": true,
 | 
			
		||||
      "domain": false,
 | 
			
		||||
      "tickSize": 12,
 | 
			
		||||
      "encode": {
 | 
			
		||||
        "grid": {"enter": {"stroke": {"value": "#ccc"}}},
 | 
			
		||||
        "ticks": {"enter": {"stroke": {"value": "#ccc"}}}
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "marks": [
 | 
			
		||||
    {
 | 
			
		||||
      "type": "area",
 | 
			
		||||
      "from": {"data": "table"},
 | 
			
		||||
      "encode": {
 | 
			
		||||
        "enter": {"fill": {"value": "#C9D6E5"}},
 | 
			
		||||
        "update": {
 | 
			
		||||
          "interpolate": {"value": "monotone"},
 | 
			
		||||
          "x": {"scale": "xscale", "field": "timestamp"},
 | 
			
		||||
          "y": {"scale": "yscale", "field": "p5"},
 | 
			
		||||
          "y2": {"scale": "yscale", "field": "p95"},
 | 
			
		||||
          "opacity": {"value": 0.75}
 | 
			
		||||
        },
 | 
			
		||||
        "hover": {"opacity": {"value": 0.5}}
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "type": "line",
 | 
			
		||||
      "from": {"data": "table"},
 | 
			
		||||
      "encode": {
 | 
			
		||||
        "update": {
 | 
			
		||||
          "interpolate": {"value": "monotone"},
 | 
			
		||||
          "stroke": {"value": "#4C78A8"},
 | 
			
		||||
          "strokeWidth": {"value": 2},
 | 
			
		||||
          "opacity": {"value": 0.8}
 | 
			
		||||
        },
 | 
			
		||||
        "hover": {"opacity": {"value": 0.5}},
 | 
			
		||||
        "enter": {
 | 
			
		||||
          "x": {"scale": "xscale", "field": "timestamp"},
 | 
			
		||||
          "y": {"scale": "yscale", "field": "p50"}
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								package.json
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -3,11 +3,11 @@
 | 
			
		|||
  "version": "0.1.0",
 | 
			
		||||
  "homepage": "https://foretold-app.github.io/estiband/",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "build": "bsb -make-world",
 | 
			
		||||
    "build": "rescript build",
 | 
			
		||||
    "build:style": "tailwind build src/styles/index.css -o src/styles/tailwind.css",
 | 
			
		||||
    "start": "bsb -make-world -w -ws _ ",
 | 
			
		||||
    "clean": "bsb -clean-world",
 | 
			
		||||
    "parcel": "parcel ./src/index.html --public-url / --no-autoinstall -- watch",
 | 
			
		||||
    "start": "rescript build -w",
 | 
			
		||||
    "clean": "rescript clean",
 | 
			
		||||
    "parcel": "parcel ./src/index.html",
 | 
			
		||||
    "parcel-build": "parcel build ./src/index.html --no-source-maps --no-autoinstall",
 | 
			
		||||
    "showcase": "PORT=12345 parcel showcase/index.html",
 | 
			
		||||
    "server": "moduleserve ./ --port 8000",
 | 
			
		||||
| 
						 | 
				
			
			@ -26,7 +26,7 @@
 | 
			
		|||
  "author": "",
 | 
			
		||||
  "license": "MIT",
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "@foretold/components": "0.0.6",
 | 
			
		||||
    "@foretold/components": "./foretold/components",
 | 
			
		||||
    "@glennsl/bs-json": "^5.0.2",
 | 
			
		||||
    "ace-builds": "^1.4.12",
 | 
			
		||||
    "antd": "3.17.0",
 | 
			
		||||
| 
						 | 
				
			
			@ -36,7 +36,7 @@
 | 
			
		|||
    "bs-ant-design-alt": "2.0.0-alpha.33",
 | 
			
		||||
    "bs-css": "11.0.0",
 | 
			
		||||
    "bs-moment": "0.4.5",
 | 
			
		||||
    "bs-reform": "9.7.1",
 | 
			
		||||
    "bs-reform": "^10.0.3",
 | 
			
		||||
    "bsb-js": "1.1.7",
 | 
			
		||||
    "d3": "5.15.0",
 | 
			
		||||
    "gh-pages": "2.2.0",
 | 
			
		||||
| 
						 | 
				
			
			@ -57,7 +57,8 @@
 | 
			
		|||
    "react-use": "^13.27.0",
 | 
			
		||||
    "react-vega": "^7.4.1",
 | 
			
		||||
    "reason-react": ">=0.7.0",
 | 
			
		||||
    "reschema": "1.3.0",
 | 
			
		||||
    "reschema": "^2.2.0",
 | 
			
		||||
    "rescript": "^9.1.4",
 | 
			
		||||
    "tailwindcss": "1.2.0",
 | 
			
		||||
    "vega": "*",
 | 
			
		||||
    "vega-embed": "6.6.0",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										5
									
								
								shell.nix
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								shell.nix
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
{ pkgs ? import <nixpkgs> {} }:
 | 
			
		||||
pkgs.mkShell {
 | 
			
		||||
  name = "squiggle";
 | 
			
		||||
  buildInputs = with pkgs; [ yarn yarn2nix ];
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -100,7 +100,7 @@ module Index = {
 | 
			
		|||
    | ChangeRoute(ReasonReactRouter.url);
 | 
			
		||||
 | 
			
		||||
  let changeId = (id: string) => {
 | 
			
		||||
    ReasonReactRouter.push(baseUrl ++ "#" ++ id);
 | 
			
		||||
    let _ = ReasonReactRouter.push(baseUrl ++ "#" ++ id);
 | 
			
		||||
    ();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -157,7 +157,7 @@ module Index = {
 | 
			
		|||
      });
 | 
			
		||||
 | 
			
		||||
    React.useState(() => {
 | 
			
		||||
      ReasonReactRouter.watchUrl(url => setRoute(_ => url));
 | 
			
		||||
      let _ = ReasonReactRouter.watchUrl(url => setRoute(_ => url));
 | 
			
		||||
      ();
 | 
			
		||||
    })
 | 
			
		||||
    |> ignore;
 | 
			
		||||
| 
						 | 
				
			
			@ -195,4 +195,4 @@ module Index = {
 | 
			
		|||
      </div>
 | 
			
		||||
    </div>;
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue
	
	Block a user