Added simple showcase, related components
This commit is contained in:
		
							parent
							
								
									17e40a9098
								
							
						
					
					
						commit
						17071602e8
					
				| 
						 | 
				
			
			@ -3,10 +3,16 @@
 | 
			
		|||
  "reason": {
 | 
			
		||||
    "react-jsx": 3
 | 
			
		||||
  },
 | 
			
		||||
  "sources": {
 | 
			
		||||
  "sources": [{
 | 
			
		||||
      "dir": "src",
 | 
			
		||||
      "subdirs": true
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "dir": "showcase",
 | 
			
		||||
      "type": "dev",
 | 
			
		||||
      "subdirs": true
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "bsc-flags": ["-bs-super-errors", "-bs-no-version-header"],
 | 
			
		||||
  "package-specs": [{
 | 
			
		||||
    "module": "commonjs",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,6 +9,7 @@
 | 
			
		|||
    "clean": "bsb -clean-world",
 | 
			
		||||
    "parcel": "parcel ./src/index.html --public-url / --no-autoinstall -- watch",
 | 
			
		||||
    "parcel-build": "parcel build ./src/index.html --no-source-maps --no-autoinstall",
 | 
			
		||||
    "showcase": "PORT=12345 parcel showcase/index.html",
 | 
			
		||||
    "server": "moduleserve ./ --port 8000",
 | 
			
		||||
    "predeploy": "parcel build ./src/index.html --no-source-maps --no-autoinstall",
 | 
			
		||||
    "deploy": "gh-pages -d dist",
 | 
			
		||||
| 
						 | 
				
			
			@ -31,6 +32,7 @@
 | 
			
		|||
    "bs-css": "^11.0.0",
 | 
			
		||||
    "bs-moment": "0.4.4",
 | 
			
		||||
    "bs-reform": "9.7.1",
 | 
			
		||||
    "d3": "^5.15.0",
 | 
			
		||||
    "lenses-ppx": "4.0.0",
 | 
			
		||||
    "less": "^3.10.3",
 | 
			
		||||
    "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 => {
 | 
			
		||||
  continuous: Shape.Continuous.findY(x, t.continuous),
 | 
			
		||||
  discrete: Shape.Discrete.findY(x, t.discrete),
 | 
			
		||||
} /* }*/;
 | 
			
		||||
} /*   discrete: Shape.Discrete.findY(x, t.discrete)*/;
 | 
			
		||||
 | 
			
		||||
// let getIntegralY = (t: t, x: float): float => {
 | 
			
		||||
//   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)) =>
 | 
			
		||||
  switch (Belt.Array.size(a)) {
 | 
			
		||||
  | 0 => None
 | 
			
		||||
  | n => Belt.Array.get(a, n)
 | 
			
		||||
  | n => Belt.Array.get(a, n - 1)
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
module XYShape = {
 | 
			
		||||
| 
						 | 
				
			
			@ -28,7 +28,8 @@ module XYShape = {
 | 
			
		|||
      Belt.Array.zip(p.xs, p.ys)
 | 
			
		||||
      ->Belt.Array.reduce([||], (items, (x, y)) =>
 | 
			
		||||
          switch (_lastElement(items)) {
 | 
			
		||||
          | Some((_, yLast)) => [|(x, fn(y, yLast))|]
 | 
			
		||||
          | Some((_, yLast)) =>
 | 
			
		||||
            Belt.Array.concat(items, [|(x, fn(y, yLast))|])
 | 
			
		||||
          | None => [|(x, y)|]
 | 
			
		||||
          }
 | 
			
		||||
        )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,6 @@
 | 
			
		|||
const {
 | 
			
		||||
    Cdf,
 | 
			
		||||
    Pdf,
 | 
			
		||||
    ContinuousDistribution,
 | 
			
		||||
    ContinuousDistributionCombination,
 | 
			
		||||
    scoringFunctions,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,7 +38,7 @@ module JS = {
 | 
			
		|||
 | 
			
		||||
module Distribution = {
 | 
			
		||||
  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 findY = (x, dist) => dist |> JS.distToJs |> JS.findY(x);
 | 
			
		||||
  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-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:
 | 
			
		||||
  version "1.14.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user