Added simple showcase, related components
This commit is contained in:
		
							parent
							
								
									17e40a9098
								
							
						
					
					
						commit
						17071602e8
					
				| 
						 | 
					@ -3,10 +3,16 @@
 | 
				
			||||||
  "reason": {
 | 
					  "reason": {
 | 
				
			||||||
    "react-jsx": 3
 | 
					    "react-jsx": 3
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "sources": {
 | 
					  "sources": [{
 | 
				
			||||||
      "dir": "src",
 | 
					      "dir": "src",
 | 
				
			||||||
      "subdirs": true
 | 
					      "subdirs": true
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      "dir": "showcase",
 | 
				
			||||||
 | 
					      "type": "dev",
 | 
				
			||||||
 | 
					      "subdirs": true
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ],
 | 
				
			||||||
  "bsc-flags": ["-bs-super-errors", "-bs-no-version-header"],
 | 
					  "bsc-flags": ["-bs-super-errors", "-bs-no-version-header"],
 | 
				
			||||||
  "package-specs": [{
 | 
					  "package-specs": [{
 | 
				
			||||||
    "module": "commonjs",
 | 
					    "module": "commonjs",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,6 +9,7 @@
 | 
				
			||||||
    "clean": "bsb -clean-world",
 | 
					    "clean": "bsb -clean-world",
 | 
				
			||||||
    "parcel": "parcel ./src/index.html --public-url / --no-autoinstall -- watch",
 | 
					    "parcel": "parcel ./src/index.html --public-url / --no-autoinstall -- watch",
 | 
				
			||||||
    "parcel-build": "parcel build ./src/index.html --no-source-maps --no-autoinstall",
 | 
					    "parcel-build": "parcel build ./src/index.html --no-source-maps --no-autoinstall",
 | 
				
			||||||
 | 
					    "showcase": "PORT=12345 parcel showcase/index.html",
 | 
				
			||||||
    "server": "moduleserve ./ --port 8000",
 | 
					    "server": "moduleserve ./ --port 8000",
 | 
				
			||||||
    "predeploy": "parcel build ./src/index.html --no-source-maps --no-autoinstall",
 | 
					    "predeploy": "parcel build ./src/index.html --no-source-maps --no-autoinstall",
 | 
				
			||||||
    "deploy": "gh-pages -d dist",
 | 
					    "deploy": "gh-pages -d dist",
 | 
				
			||||||
| 
						 | 
					@ -31,6 +32,7 @@
 | 
				
			||||||
    "bs-css": "^11.0.0",
 | 
					    "bs-css": "^11.0.0",
 | 
				
			||||||
    "bs-moment": "0.4.4",
 | 
					    "bs-moment": "0.4.4",
 | 
				
			||||||
    "bs-reform": "9.7.1",
 | 
					    "bs-reform": "9.7.1",
 | 
				
			||||||
 | 
					    "d3": "^5.15.0",
 | 
				
			||||||
    "lenses-ppx": "4.0.0",
 | 
					    "lenses-ppx": "4.0.0",
 | 
				
			||||||
    "less": "^3.10.3",
 | 
					    "less": "^3.10.3",
 | 
				
			||||||
    "lodash": "^4.17.15",
 | 
					    "lodash": "^4.17.15",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										1
									
								
								showcase/Entries.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								showcase/Entries.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					let entries = EntryTypes.[Continuous.entry];
 | 
				
			||||||
							
								
								
									
										30
									
								
								showcase/EntryTypes.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								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});
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										200
									
								
								showcase/Lib.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										200
									
								
								showcase/Lib.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,200 @@
 | 
				
			||||||
 | 
					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>;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										2
									
								
								showcase/ShowcaseIndex.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								showcase/ShowcaseIndex.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,2 @@
 | 
				
			||||||
 | 
					ReactDOMRe.renderToElementWithId(<div> <Lib.Index /> </div>, "main");
 | 
				
			||||||
 | 
					ReasonReactRouter.push("");
 | 
				
			||||||
							
								
								
									
										19
									
								
								showcase/entries/Continuous.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								showcase/entries/Continuous.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,19 @@
 | 
				
			||||||
 | 
					open ForetoldComponents.Base;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let data: DistributionTypes.xyShape = {
 | 
				
			||||||
 | 
					  xs: [|0.2, 20., 80., 212., 330.|],
 | 
				
			||||||
 | 
					  ys: [|0.0, 0.3, 0.5, 0.2, 0.1|],
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let alerts = () =>
 | 
				
			||||||
 | 
					  <div>
 | 
				
			||||||
 | 
					    <div> <ChartWithNumber data color={`hex("333")} /> </div>
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					      <ChartWithNumber
 | 
				
			||||||
 | 
					        data={data |> Shape.XYShape.integral}
 | 
				
			||||||
 | 
					        color={`hex("333")}
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let entry = EntryTypes.(entry(~title="Pdf", ~render=alerts));
 | 
				
			||||||
							
								
								
									
										23
									
								
								showcase/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								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=" ./ShowcaseIndex.bs.js "></script>
 | 
				
			||||||
 | 
					</body>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</html>
 | 
				
			||||||
							
								
								
									
										43
									
								
								src/components/charts/CdfChart__Base.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/components/charts/CdfChart__Base.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,43 @@
 | 
				
			||||||
 | 
					[@bs.module "./cdfChartReact.js"]
 | 
				
			||||||
 | 
					external cdfChart: ReasonReact.reactClass = "default";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type primaryDistribution = {
 | 
				
			||||||
 | 
					  .
 | 
				
			||||||
 | 
					  "xs": array(float),
 | 
				
			||||||
 | 
					  "ys": array(float),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[@react.component]
 | 
				
			||||||
 | 
					let make =
 | 
				
			||||||
 | 
					    (
 | 
				
			||||||
 | 
					      ~height=?,
 | 
				
			||||||
 | 
					      ~verticalLine=?,
 | 
				
			||||||
 | 
					      ~showVerticalLine=?,
 | 
				
			||||||
 | 
					      ~marginBottom=?,
 | 
				
			||||||
 | 
					      ~marginTop=?,
 | 
				
			||||||
 | 
					      ~showDistributionLines=?,
 | 
				
			||||||
 | 
					      ~maxX=?,
 | 
				
			||||||
 | 
					      ~minX=?,
 | 
				
			||||||
 | 
					      ~onHover=(f: float) => (),
 | 
				
			||||||
 | 
					      ~primaryDistribution=?,
 | 
				
			||||||
 | 
					      ~children=[||],
 | 
				
			||||||
 | 
					    ) =>
 | 
				
			||||||
 | 
					  ReasonReact.wrapJsForReason(
 | 
				
			||||||
 | 
					    ~reactClass=cdfChart,
 | 
				
			||||||
 | 
					    ~props=
 | 
				
			||||||
 | 
					      makeProps(
 | 
				
			||||||
 | 
					        ~height?,
 | 
				
			||||||
 | 
					        ~verticalLine?,
 | 
				
			||||||
 | 
					        ~marginBottom?,
 | 
				
			||||||
 | 
					        ~marginTop?,
 | 
				
			||||||
 | 
					        ~onHover,
 | 
				
			||||||
 | 
					        ~showVerticalLine?,
 | 
				
			||||||
 | 
					        ~showDistributionLines?,
 | 
				
			||||||
 | 
					        ~maxX?,
 | 
				
			||||||
 | 
					        ~minX?,
 | 
				
			||||||
 | 
					        ~primaryDistribution?,
 | 
				
			||||||
 | 
					        (),
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					    children,
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  |> ReasonReact.element;
 | 
				
			||||||
							
								
								
									
										41
									
								
								src/components/charts/CdfChart__Plain.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/components/charts/CdfChart__Plain.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,41 @@
 | 
				
			||||||
 | 
					module Styles = {
 | 
				
			||||||
 | 
					  open Css;
 | 
				
			||||||
 | 
					  let textOverlay = style([position(`absolute)]);
 | 
				
			||||||
 | 
					  let mainText = style([fontSize(`em(1.1))]);
 | 
				
			||||||
 | 
					  let secondaryText = style([fontSize(`em(0.9))]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let graph = chartColor =>
 | 
				
			||||||
 | 
					    style([
 | 
				
			||||||
 | 
					      position(`relative),
 | 
				
			||||||
 | 
					      selector(".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 =
 | 
				
			||||||
 | 
					    (
 | 
				
			||||||
 | 
					      ~data,
 | 
				
			||||||
 | 
					      ~minX=?,
 | 
				
			||||||
 | 
					      ~maxX=?,
 | 
				
			||||||
 | 
					      ~height=200,
 | 
				
			||||||
 | 
					      ~color=`hex("111"),
 | 
				
			||||||
 | 
					      ~onHover: float => unit,
 | 
				
			||||||
 | 
					    ) => {
 | 
				
			||||||
 | 
					  <div className={Styles.graph(color)}>
 | 
				
			||||||
 | 
					    <CdfChart__Base
 | 
				
			||||||
 | 
					      height
 | 
				
			||||||
 | 
					      ?minX
 | 
				
			||||||
 | 
					      ?maxX
 | 
				
			||||||
 | 
					      marginBottom=50
 | 
				
			||||||
 | 
					      marginTop=0
 | 
				
			||||||
 | 
					      onHover
 | 
				
			||||||
 | 
					      showVerticalLine=false
 | 
				
			||||||
 | 
					      showDistributionLines=false
 | 
				
			||||||
 | 
					      primaryDistribution={data |> Shape.XYShape.toJs}
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					  </div>;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										24
									
								
								src/components/charts/ChartSimple.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/components/charts/ChartSimple.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,24 @@
 | 
				
			||||||
 | 
					module Styles = {
 | 
				
			||||||
 | 
					  open Css;
 | 
				
			||||||
 | 
					  let graph = chartColor =>
 | 
				
			||||||
 | 
					    style([
 | 
				
			||||||
 | 
					      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 = (~minX=None, ~maxX=None, ~height=50, ~color=`hex("7e9db7")) =>
 | 
				
			||||||
 | 
					  <div className={Styles.graph(color)}>
 | 
				
			||||||
 | 
					    <CdfChart__Base
 | 
				
			||||||
 | 
					      height
 | 
				
			||||||
 | 
					      ?minX
 | 
				
			||||||
 | 
					      ?maxX
 | 
				
			||||||
 | 
					      marginBottom=20
 | 
				
			||||||
 | 
					      showVerticalLine=false
 | 
				
			||||||
 | 
					      showDistributionLines=false
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					  </div>;
 | 
				
			||||||
							
								
								
									
										23
									
								
								src/components/charts/ChartWithNumber.re
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/components/charts/ChartWithNumber.re
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,23 @@
 | 
				
			||||||
 | 
					[@react.component]
 | 
				
			||||||
 | 
					let make = (~data, ~color=?) => {
 | 
				
			||||||
 | 
					  let (x, setX) = React.useState(() => 0.);
 | 
				
			||||||
 | 
					  let chart =
 | 
				
			||||||
 | 
					    React.useMemo1(
 | 
				
			||||||
 | 
					      () => <CdfChart__Plain data ?color onHover={r => setX(_ => r)} />,
 | 
				
			||||||
 | 
					      [|data|],
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  <div>
 | 
				
			||||||
 | 
					    chart
 | 
				
			||||||
 | 
					    <div> {x |> E.Float.toString |> ReasonReact.string} </div>
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					      {Shape.Continuous.findY(x, data)
 | 
				
			||||||
 | 
					       |> E.Float.toString
 | 
				
			||||||
 | 
					       |> ReasonReact.string}
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div>
 | 
				
			||||||
 | 
					      {Shape.Continuous.findY(x, Shape.XYShape.integral(data))
 | 
				
			||||||
 | 
					       |> E.Float.toString
 | 
				
			||||||
 | 
					       |> ReasonReact.string}
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </div>;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										66
									
								
								src/components/charts/cdfChartReact.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								src/components/charts/cdfChartReact.js
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,66 @@
 | 
				
			||||||
 | 
					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)
 | 
				
			||||||
 | 
					      .onHover(props.onHover)
 | 
				
			||||||
 | 
					      .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;
 | 
				
			||||||
							
								
								
									
										302
									
								
								src/components/charts/cdfChartd3.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										302
									
								
								src/components/charts/cdfChartd3.js
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,302 @@
 | 
				
			||||||
 | 
					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(0.0);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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;
 | 
				
			||||||
| 
						 | 
					@ -8,8 +8,7 @@ type yPdfPoint = {
 | 
				
			||||||
let getY = (t: t, x: float): yPdfPoint => {
 | 
					let getY = (t: t, x: float): yPdfPoint => {
 | 
				
			||||||
  continuous: Shape.Continuous.findY(x, t.continuous),
 | 
					  continuous: Shape.Continuous.findY(x, t.continuous),
 | 
				
			||||||
  discrete: Shape.Discrete.findY(x, t.discrete),
 | 
					  discrete: Shape.Discrete.findY(x, t.discrete),
 | 
				
			||||||
} /* }*/;
 | 
					} /*   discrete: Shape.Discrete.findY(x, t.discrete)*/;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// let getIntegralY = (t: t, x: float): float => {
 | 
					// let getIntegralY = (t: t, x: float): float => {
 | 
				
			||||||
//   continuous: Shape.Continuous.findY(x, t.continuous),
 | 
					//   continuous: Shape.Continuous.findY(x, t.continuous),
 | 
				
			||||||
//   discrete: Shape.Discrete.findY(x, t.discrete),
 | 
					 | 
				
			||||||
| 
						 | 
					@ -3,7 +3,7 @@ open DistributionTypes;
 | 
				
			||||||
let _lastElement = (a: array('a)) =>
 | 
					let _lastElement = (a: array('a)) =>
 | 
				
			||||||
  switch (Belt.Array.size(a)) {
 | 
					  switch (Belt.Array.size(a)) {
 | 
				
			||||||
  | 0 => None
 | 
					  | 0 => None
 | 
				
			||||||
  | n => Belt.Array.get(a, n)
 | 
					  | n => Belt.Array.get(a, n - 1)
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module XYShape = {
 | 
					module XYShape = {
 | 
				
			||||||
| 
						 | 
					@ -28,7 +28,8 @@ module XYShape = {
 | 
				
			||||||
      Belt.Array.zip(p.xs, p.ys)
 | 
					      Belt.Array.zip(p.xs, p.ys)
 | 
				
			||||||
      ->Belt.Array.reduce([||], (items, (x, y)) =>
 | 
					      ->Belt.Array.reduce([||], (items, (x, y)) =>
 | 
				
			||||||
          switch (_lastElement(items)) {
 | 
					          switch (_lastElement(items)) {
 | 
				
			||||||
          | Some((_, yLast)) => [|(x, fn(y, yLast))|]
 | 
					          | Some((_, yLast)) =>
 | 
				
			||||||
 | 
					            Belt.Array.concat(items, [|(x, fn(y, yLast))|])
 | 
				
			||||||
          | None => [|(x, y)|]
 | 
					          | None => [|(x, y)|]
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,6 @@
 | 
				
			||||||
const {
 | 
					const {
 | 
				
			||||||
    Cdf,
 | 
					    Cdf,
 | 
				
			||||||
 | 
					    Pdf,
 | 
				
			||||||
    ContinuousDistribution,
 | 
					    ContinuousDistribution,
 | 
				
			||||||
    ContinuousDistributionCombination,
 | 
					    ContinuousDistributionCombination,
 | 
				
			||||||
    scoringFunctions,
 | 
					    scoringFunctions,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -38,7 +38,7 @@ module JS = {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module Distribution = {
 | 
					module Distribution = {
 | 
				
			||||||
  let toPdf = dist => dist |> JS.doAsDist(JS.cdfToPdf);
 | 
					  let toPdf = dist => dist |> JS.doAsDist(JS.cdfToPdf);
 | 
				
			||||||
  let toCdf = dist => dist |> JS.doAsDist(JS.cdfToPdf);
 | 
					  let toCdf = dist => dist |> JS.doAsDist(JS.pdfToCdf);
 | 
				
			||||||
  let findX = (y, dist) => dist |> JS.distToJs |> JS.findX(y);
 | 
					  let findX = (y, dist) => dist |> JS.distToJs |> JS.findX(y);
 | 
				
			||||||
  let findY = (x, dist) => dist |> JS.distToJs |> JS.findY(x);
 | 
					  let findY = (x, dist) => dist |> JS.distToJs |> JS.findY(x);
 | 
				
			||||||
  let integral = dist => dist |> JS.distToJs |> JS.integral;
 | 
					  let integral = dist => dist |> JS.distToJs |> JS.integral;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										37
									
								
								yarn.lock
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								yarn.lock
									
									
									
									
									
								
							| 
						 | 
					@ -2967,6 +2967,43 @@ d3@5.9.2:
 | 
				
			||||||
    d3-voronoi "1"
 | 
					    d3-voronoi "1"
 | 
				
			||||||
    d3-zoom "1"
 | 
					    d3-zoom "1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					d3@^5.15.0:
 | 
				
			||||||
 | 
					  version "5.15.0"
 | 
				
			||||||
 | 
					  resolved "https://registry.yarnpkg.com/d3/-/d3-5.15.0.tgz#ffd44958e6a3cb8a59a84429c45429b8bca5677a"
 | 
				
			||||||
 | 
					  integrity sha512-C+E80SL2nLLtmykZ6klwYj5rPqB5nlfN5LdWEAVdWPppqTD8taoJi2PxLZjPeYT8FFRR2yucXq+kBlOnnvZeLg==
 | 
				
			||||||
 | 
					  dependencies:
 | 
				
			||||||
 | 
					    d3-array "1"
 | 
				
			||||||
 | 
					    d3-axis "1"
 | 
				
			||||||
 | 
					    d3-brush "1"
 | 
				
			||||||
 | 
					    d3-chord "1"
 | 
				
			||||||
 | 
					    d3-collection "1"
 | 
				
			||||||
 | 
					    d3-color "1"
 | 
				
			||||||
 | 
					    d3-contour "1"
 | 
				
			||||||
 | 
					    d3-dispatch "1"
 | 
				
			||||||
 | 
					    d3-drag "1"
 | 
				
			||||||
 | 
					    d3-dsv "1"
 | 
				
			||||||
 | 
					    d3-ease "1"
 | 
				
			||||||
 | 
					    d3-fetch "1"
 | 
				
			||||||
 | 
					    d3-force "1"
 | 
				
			||||||
 | 
					    d3-format "1"
 | 
				
			||||||
 | 
					    d3-geo "1"
 | 
				
			||||||
 | 
					    d3-hierarchy "1"
 | 
				
			||||||
 | 
					    d3-interpolate "1"
 | 
				
			||||||
 | 
					    d3-path "1"
 | 
				
			||||||
 | 
					    d3-polygon "1"
 | 
				
			||||||
 | 
					    d3-quadtree "1"
 | 
				
			||||||
 | 
					    d3-random "1"
 | 
				
			||||||
 | 
					    d3-scale "2"
 | 
				
			||||||
 | 
					    d3-scale-chromatic "1"
 | 
				
			||||||
 | 
					    d3-selection "1"
 | 
				
			||||||
 | 
					    d3-shape "1"
 | 
				
			||||||
 | 
					    d3-time "1"
 | 
				
			||||||
 | 
					    d3-time-format "2"
 | 
				
			||||||
 | 
					    d3-timer "1"
 | 
				
			||||||
 | 
					    d3-transition "1"
 | 
				
			||||||
 | 
					    d3-voronoi "1"
 | 
				
			||||||
 | 
					    d3-zoom "1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
dashdash@^1.12.0:
 | 
					dashdash@^1.12.0:
 | 
				
			||||||
  version "1.14.1"
 | 
					  version "1.14.1"
 | 
				
			||||||
  resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
 | 
					  resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user