Add prettier and format

This commit is contained in:
Sam Nolan 2022-03-23 11:38:01 +11:00
parent f122b5fd7f
commit 15c9fbd13b
13 changed files with 395 additions and 57387 deletions

View File

@ -0,0 +1,5 @@
dist
build
node_modules
storybook-static
.storybook

View File

@ -0,0 +1 @@
{}

File diff suppressed because it is too large Load Diff

View File

@ -69,6 +69,7 @@
"@storybook/preset-create-react-app": "^4.0.0", "@storybook/preset-create-react-app": "^4.0.0",
"@storybook/react": "^6.4.18", "@storybook/react": "^6.4.18",
"@types/webpack": "^5.28.0", "@types/webpack": "^5.28.0",
"prettier": "^2.6.0",
"react-codejar": "^1.1.2", "react-codejar": "^1.1.2",
"ts-loader": "^9.2.8", "ts-loader": "^9.2.8",
"webpack": "^5.70.0", "webpack": "^5.70.0",

View File

@ -5,10 +5,7 @@
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" /> <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#000000" />
<meta <meta name="description" content="Squiggle components" />
name="description"
content="Squiggle components"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" /> <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<title>Squiggle Components</title> <title>Squiggle Components</title>
</head> </head>

View File

@ -1,92 +1,96 @@
import * as React from 'react'; import * as React from "react";
import _ from 'lodash'; import _ from "lodash";
import type { Spec } from 'vega'; import type { Spec } from "vega";
import { run } from '@quri/squiggle-lang'; import { run } from "@quri/squiggle-lang";
import type { DistPlus, SamplingInputs, exportEnv, exportDistribution } from '@quri/squiggle-lang'; import type {
import { createClassFromSpec } from 'react-vega'; DistPlus,
import * as chartSpecification from './spec-distributions.json' SamplingInputs,
import * as percentilesSpec from './spec-pertentiles.json' exportEnv,
exportDistribution,
} from "@quri/squiggle-lang";
import { createClassFromSpec } from "react-vega";
import * as chartSpecification from "./spec-distributions.json";
import * as percentilesSpec from "./spec-pertentiles.json";
let SquiggleVegaChart = createClassFromSpec({'spec': chartSpecification as Spec}); let SquiggleVegaChart = createClassFromSpec({
spec: chartSpecification as Spec,
});
let SquigglePercentilesChart = createClassFromSpec({'spec': percentilesSpec as Spec}); let SquigglePercentilesChart = createClassFromSpec({
spec: percentilesSpec as Spec,
});
export interface SquiggleChartProps { export interface SquiggleChartProps {
/** The input string for squiggle */ /** The input string for squiggle */
squiggleString : string, squiggleString: string;
/** If the output requires monte carlo sampling, the amount of samples */ /** If the output requires monte carlo sampling, the amount of samples */
sampleCount? : number, sampleCount?: number;
/** The amount of points returned to draw the distribution */ /** The amount of points returned to draw the distribution */
outputXYPoints? : number, outputXYPoints?: number;
kernelWidth? : number, kernelWidth?: number;
pointDistLength? : number, pointDistLength?: number;
/** If the result is a function, where the function starts */ /** If the result is a function, where the function starts */
diagramStart? : number, diagramStart?: number;
/** If the result is a function, where the function ends */ /** If the result is a function, where the function ends */
diagramStop? : number, diagramStop?: number;
/** If the result is a function, how many points along the function it samples */ /** If the result is a function, how many points along the function it samples */
diagramCount? : number, diagramCount?: number;
/** variables declared before this expression */ /** variables declared before this expression */
environment? : exportEnv, environment?: exportEnv;
/** When the environment changes */ /** When the environment changes */
onEnvChange?(env: exportEnv): void onEnvChange?(env: exportEnv): void;
} }
export const SquiggleChart : React.FC<SquiggleChartProps> = props => { export const SquiggleChart: React.FC<SquiggleChartProps> = (props) => {
let samplingInputs : SamplingInputs = { let samplingInputs: SamplingInputs = {
sampleCount : props.sampleCount, sampleCount: props.sampleCount,
outputXYPoints : props.outputXYPoints, outputXYPoints: props.outputXYPoints,
kernelWidth : props.kernelWidth, kernelWidth: props.kernelWidth,
pointDistLength : props.pointDistLength pointDistLength: props.pointDistLength,
} };
let result = run(props.squiggleString, samplingInputs, props.environment); let result = run(props.squiggleString, samplingInputs, props.environment);
if (result.tag === "Ok") { if (result.tag === "Ok") {
let environment = result.value.environment let environment = result.value.environment;
let exports = result.value.exports let exports = result.value.exports;
if(props.onEnvChange) if (props.onEnvChange) props.onEnvChange(environment);
props.onEnvChange(environment) let chartResults = exports.map((chartResult: exportDistribution) => {
let chartResults = exports.map((chartResult:exportDistribution )=> { if (chartResult["NAME"] === "Float") {
if(chartResult["NAME"] === "Float"){
return <MakeNumberShower precision={3} number={chartResult["VAL"]} />; return <MakeNumberShower precision={3} number={chartResult["VAL"]} />;
} } else if (chartResult["NAME"] === "DistPlus") {
else if(chartResult["NAME"] === "DistPlus"){
let shape = chartResult.VAL.pointSetDist; let shape = chartResult.VAL.pointSetDist;
if(shape.tag === "Continuous"){ if (shape.tag === "Continuous") {
let xyShape = shape.value.xyShape; let xyShape = shape.value.xyShape;
let totalY = xyShape.ys.reduce((a, b) => a + b); let totalY = xyShape.ys.reduce((a, b) => a + b);
let total = 0; let total = 0;
let cdf = xyShape.ys.map(y => { let cdf = xyShape.ys.map((y) => {
total += y; total += y;
return total / totalY; return total / totalY;
}) });
let values = _.zip(cdf, xyShape.xs, xyShape.ys).map(([c, x, y ]) => ({cdf: (c * 100).toFixed(2) + "%", x: x, y: y})); let values = _.zip(cdf, xyShape.xs, xyShape.ys).map(([c, x, y]) => ({
cdf: (c * 100).toFixed(2) + "%",
x: x,
y: y,
}));
return ( return <SquiggleVegaChart data={{ con: values }} />;
<SquiggleVegaChart } else if (shape.tag === "Discrete") {
data={{"con": values}}
/>
);
}
else if(shape.tag === "Discrete"){
let xyShape = shape.value.xyShape; let xyShape = shape.value.xyShape;
let totalY = xyShape.ys.reduce((a, b) => a + b); let totalY = xyShape.ys.reduce((a, b) => a + b);
let total = 0; let total = 0;
let cdf = xyShape.ys.map(y => { let cdf = xyShape.ys.map((y) => {
total += y; total += y;
return total / totalY; return total / totalY;
}) });
let values = _.zip(cdf, xyShape.xs, xyShape.ys).map(([c, x,y]) => ({cdf: (c * 100).toFixed(2) + "%", x: x, y: y})); let values = _.zip(cdf, xyShape.xs, xyShape.ys).map(([c, x, y]) => ({
cdf: (c * 100).toFixed(2) + "%",
x: x,
y: y,
}));
return ( return <SquiggleVegaChart data={{ dis: values }} />;
<SquiggleVegaChart } else if (shape.tag === "Mixed") {
data={{"dis": values}}
/>
);
}
else if(shape.tag === "Mixed"){
let discreteShape = shape.value.discrete.xyShape; let discreteShape = shape.value.discrete.xyShape;
let totalDiscrete = discreteShape.ys.reduce((a, b) => a + b); let totalDiscrete = discreteShape.ys.reduce((a, b) => a + b);
@ -95,141 +99,150 @@ export const SquiggleChart : React.FC<SquiggleChartProps> = props => {
let continuousPoints = _.zip(continuousShape.xs, continuousShape.ys); let continuousPoints = _.zip(continuousShape.xs, continuousShape.ys);
interface labeledPoint { interface labeledPoint {
x: number, x: number;
y: number, y: number;
type: "discrete" | "continuous" type: "discrete" | "continuous";
}; }
let markedDisPoints : labeledPoint[] = discretePoints.map(([x,y]) => ({x: x, y: y, type: "discrete"})) let markedDisPoints: labeledPoint[] = discretePoints.map(
let markedConPoints : labeledPoint[] = continuousPoints.map(([x,y]) => ({x: x, y: y, type: "continuous"})) ([x, y]) => ({ x: x, y: y, type: "discrete" })
);
let markedConPoints: labeledPoint[] = continuousPoints.map(
([x, y]) => ({ x: x, y: y, type: "continuous" })
);
let sortedPoints = _.sortBy(markedDisPoints.concat(markedConPoints), 'x') let sortedPoints = _.sortBy(
markedDisPoints.concat(markedConPoints),
"x"
);
let totalContinuous = 1 - totalDiscrete; let totalContinuous = 1 - totalDiscrete;
let totalY = continuousShape.ys.reduce((a:number, b:number) => a + b); let totalY = continuousShape.ys.reduce(
(a: number, b: number) => a + b
);
let total = 0; let total = 0;
let cdf = sortedPoints.map((point: labeledPoint) => { let cdf = sortedPoints.map((point: labeledPoint) => {
if(point.type == "discrete") { if (point.type == "discrete") {
total += point.y; total += point.y;
return total; return total;
} } else if (point.type == "continuous") {
else if (point.type == "continuous") { total += (point.y / totalY) * totalContinuous;
total += point.y / totalY * totalContinuous;
return total; return total;
} }
}); });
interface cdfLabeledPoint { interface cdfLabeledPoint {
cdf: string, cdf: string;
x: number, x: number;
y: number, y: number;
type: "discrete" | "continuous" type: "discrete" | "continuous";
} }
let cdfLabeledPoint : cdfLabeledPoint[] = _.zipWith(cdf, sortedPoints, (c: number, point: labeledPoint) => ({...point, cdf: (c * 100).toFixed(2) + "%"})) let cdfLabeledPoint: cdfLabeledPoint[] = _.zipWith(
let continuousValues = cdfLabeledPoint.filter(x => x.type == "continuous") cdf,
let discreteValues = cdfLabeledPoint.filter(x => x.type == "discrete") sortedPoints,
(c: number, point: labeledPoint) => ({
...point,
cdf: (c * 100).toFixed(2) + "%",
})
);
let continuousValues = cdfLabeledPoint.filter(
(x) => x.type == "continuous"
);
let discreteValues = cdfLabeledPoint.filter(
(x) => x.type == "discrete"
);
return ( return (
<SquiggleVegaChart <SquiggleVegaChart
data={{"con": continuousValues, "dis": discreteValues}} data={{ con: continuousValues, dis: discreteValues }}
/> />
); );
}
}
else if(chartResult.NAME === "Function"){
// We are looking at a function. In this case, we draw a Percentiles chart
let start = props.diagramStart ? props.diagramStart : 0
let stop = props.diagramStop ? props.diagramStop : 10
let count = props.diagramCount ? props.diagramCount : 0.1
let step = (stop - start)/ count
let data = _.range(start, stop, step).map(x => {
if(chartResult.NAME=="Function"){
let result = chartResult.VAL(x);
if(result.tag == "Ok"){
let percentileArray = [
0.01,
0.05,
0.1,
0.2,
0.3,
0.4,
0.5,
0.6,
0.7,
0.8,
0.9,
0.95,
0.99
]
let percentiles = getPercentiles(percentileArray, result.value);
return {
"x": x,
"p1": percentiles[0],
"p5": percentiles[1],
"p10": percentiles[2],
"p20": percentiles[3],
"p30": percentiles[4],
"p40": percentiles[5],
"p50": percentiles[6],
"p60": percentiles[7],
"p70": percentiles[8],
"p80": percentiles[9],
"p90": percentiles[10],
"p95": percentiles[11],
"p99": percentiles[12]
}
}
}
return 0;
})
return <SquigglePercentilesChart data={{"facet": data}} />
} }
}) } else if (chartResult.NAME === "Function") {
return <>{chartResults}</>; // We are looking at a function. In this case, we draw a Percentiles chart
} let start = props.diagramStart ? props.diagramStart : 0;
else if(result.tag == "Error") { let stop = props.diagramStop ? props.diagramStop : 10;
// At this point, we came across an error. What was our error? let count = props.diagramCount ? props.diagramCount : 0.1;
return (<p>{"Error parsing Squiggle: " + result.value}</p>) let step = (stop - start) / count;
let data = _.range(start, stop, step).map((x) => {
if (chartResult.NAME == "Function") {
let result = chartResult.VAL(x);
if (result.tag == "Ok") {
let percentileArray = [
0.01, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0.95,
0.99,
];
let percentiles = getPercentiles(percentileArray, result.value);
return {
x: x,
p1: percentiles[0],
p5: percentiles[1],
p10: percentiles[2],
p20: percentiles[3],
p30: percentiles[4],
p40: percentiles[5],
p50: percentiles[6],
p60: percentiles[7],
p70: percentiles[8],
p80: percentiles[9],
p90: percentiles[10],
p95: percentiles[11],
p99: percentiles[12],
};
}
}
return 0;
});
return <SquigglePercentilesChart data={{ facet: data }} />;
}
});
return <>{chartResults}</>;
} else if (result.tag == "Error") {
// At this point, we came across an error. What was our error?
return <p>{"Error parsing Squiggle: " + result.value}</p>;
} }
return (<p>{"Invalid Response"}</p>) return <p>{"Invalid Response"}</p>;
}; };
function getPercentiles(percentiles:number[], t : DistPlus) { function getPercentiles(percentiles: number[], t: DistPlus) {
if(t.pointSetDist.tag == "Discrete") { if (t.pointSetDist.tag == "Discrete") {
let total = 0; let total = 0;
let maxX = _.max(t.pointSetDist.value.xyShape.xs) let maxX = _.max(t.pointSetDist.value.xyShape.xs);
let bounds = percentiles.map(_ => maxX); let bounds = percentiles.map((_) => maxX);
_.zipWith(t.pointSetDist.value.xyShape.xs,t.pointSetDist.value.xyShape.ys, (x,y) => { _.zipWith(
total += y t.pointSetDist.value.xyShape.xs,
t.pointSetDist.value.xyShape.ys,
(x, y) => {
total += y;
percentiles.forEach((v, i) => { percentiles.forEach((v, i) => {
if(total > v && bounds[i] == maxX){ if (total > v && bounds[i] == maxX) {
bounds[i] = x bounds[i] = x;
} }
}) });
}); }
return bounds; );
} return bounds;
else if(t.pointSetDist.tag == "Continuous"){ } else if (t.pointSetDist.tag == "Continuous") {
let total = 0; let total = 0;
let maxX = _.max(t.pointSetDist.value.xyShape.xs) let maxX = _.max(t.pointSetDist.value.xyShape.xs);
let totalY = _.sum(t.pointSetDist.value.xyShape.ys) let totalY = _.sum(t.pointSetDist.value.xyShape.ys);
let bounds = percentiles.map(_ => maxX); let bounds = percentiles.map((_) => maxX);
_.zipWith(t.pointSetDist.value.xyShape.xs,t.pointSetDist.value.xyShape.ys, (x,y) => { _.zipWith(
t.pointSetDist.value.xyShape.xs,
t.pointSetDist.value.xyShape.ys,
(x, y) => {
total += y / totalY; total += y / totalY;
percentiles.forEach((v, i) => { percentiles.forEach((v, i) => {
if(total > v && bounds[i] == maxX){ if (total > v && bounds[i] == maxX) {
bounds[i] = x bounds[i] = x;
} }
}) });
}); }
return bounds; );
} return bounds;
else if(t.pointSetDist.tag == "Mixed"){ } else if (t.pointSetDist.tag == "Mixed") {
let discreteShape = t.pointSetDist.value.discrete.xyShape; let discreteShape = t.pointSetDist.value.discrete.xyShape;
let totalDiscrete = discreteShape.ys.reduce((a, b) => a + b); let totalDiscrete = discreteShape.ys.reduce((a, b) => a + b);
@ -238,80 +251,87 @@ function getPercentiles(percentiles:number[], t : DistPlus) {
let continuousPoints = _.zip(continuousShape.xs, continuousShape.ys); let continuousPoints = _.zip(continuousShape.xs, continuousShape.ys);
interface labeledPoint { interface labeledPoint {
x: number, x: number;
y: number, y: number;
type: "discrete" | "continuous" type: "discrete" | "continuous";
}; }
let markedDisPoints : labeledPoint[] = discretePoints.map(([x,y]) => ({x: x, y: y, type: "discrete"})) let markedDisPoints: labeledPoint[] = discretePoints.map(([x, y]) => ({
let markedConPoints : labeledPoint[] = continuousPoints.map(([x,y]) => ({x: x, y: y, type: "continuous"})) x: x,
y: y,
type: "discrete",
}));
let markedConPoints: labeledPoint[] = continuousPoints.map(([x, y]) => ({
x: x,
y: y,
type: "continuous",
}));
let sortedPoints = _.sortBy(markedDisPoints.concat(markedConPoints), 'x') let sortedPoints = _.sortBy(markedDisPoints.concat(markedConPoints), "x");
let totalContinuous = 1 - totalDiscrete; let totalContinuous = 1 - totalDiscrete;
let totalY = continuousShape.ys.reduce((a:number, b:number) => a + b); let totalY = continuousShape.ys.reduce((a: number, b: number) => a + b);
let total = 0; let total = 0;
let maxX = _.max(sortedPoints.map(x => x.x)); let maxX = _.max(sortedPoints.map((x) => x.x));
let bounds = percentiles.map(_ => maxX); let bounds = percentiles.map((_) => maxX);
sortedPoints.map((point: labeledPoint) => { sortedPoints.map((point: labeledPoint) => {
if(point.type == "discrete") { if (point.type == "discrete") {
total += point.y; total += point.y;
} else if (point.type == "continuous") {
total += (point.y / totalY) * totalContinuous;
} }
else if (point.type == "continuous") { percentiles.forEach((v, i) => {
total += point.y / totalY * totalContinuous; if (total > v && bounds[i] == maxX) {
}
percentiles.forEach((v,i) => {
if(total > v && bounds[i] == maxX){
bounds[i] = total; bounds[i] = total;
} }
}) });
return total; return total;
}); });
return bounds; return bounds;
} }
} }
function MakeNumberShower(props: {number: number, precision :number}){ function MakeNumberShower(props: { number: number; precision: number }) {
let numberWithPresentation = numberShow(props.number, props.precision); let numberWithPresentation = numberShow(props.number, props.precision);
return ( return (
<span> <span>
{numberWithPresentation.value} {numberWithPresentation.value}
{numberWithPresentation.symbol} {numberWithPresentation.symbol}
{numberWithPresentation.power ? {numberWithPresentation.power ? (
<span> <span>
{'\u00b710'} {"\u00b710"}
<span style={{fontSize: "0.6em", verticalAlign: "super"}}> <span style={{ fontSize: "0.6em", verticalAlign: "super" }}>
{numberWithPresentation.power} {numberWithPresentation.power}
</span>
</span> </span>
</span> ) : (
: <></>} <></>
)}
</span> </span>
);
);
} }
const orderOfMagnitudeNum = (n:number) => { const orderOfMagnitudeNum = (n: number) => {
return Math.pow(10, n); return Math.pow(10, n);
}; };
// 105 -> 3 // 105 -> 3
const orderOfMagnitude = (n:number) => { const orderOfMagnitude = (n: number) => {
return Math.floor(Math.log(n) / Math.LN10 + 0.000000001); return Math.floor(Math.log(n) / Math.LN10 + 0.000000001);
}; };
function withXSigFigs(number:number, sigFigs:number) { function withXSigFigs(number: number, sigFigs: number) {
const withPrecision = number.toPrecision(sigFigs); const withPrecision = number.toPrecision(sigFigs);
const formatted = Number(withPrecision); const formatted = Number(withPrecision);
return `${formatted}`; return `${formatted}`;
} }
class NumberShower { class NumberShower {
number: number number: number;
precision: number precision: number;
constructor(number:number, precision = 2) { constructor(number: number, precision = 2) {
this.number = number; this.number = number;
this.precision = precision; this.precision = precision;
} }
@ -320,9 +340,9 @@ class NumberShower {
const number = Math.abs(this.number); const number = Math.abs(this.number);
const response = this.evaluate(number); const response = this.evaluate(number);
if (this.number < 0) { if (this.number < 0) {
response.value = '-' + response.value; response.value = "-" + response.value;
} }
return response return response;
} }
metricSystem(number: number, order: number) { metricSystem(number: number, order: number) {
@ -333,7 +353,7 @@ class NumberShower {
evaluate(number: number) { evaluate(number: number) {
if (number === 0) { if (number === 0) {
return { value: this.metricSystem(0, 0) } return { value: this.metricSystem(0, 0) };
} }
const order = orderOfMagnitude(number); const order = orderOfMagnitude(number);
@ -342,13 +362,13 @@ class NumberShower {
} else if (order < 4) { } else if (order < 4) {
return { value: this.metricSystem(number, 0) }; return { value: this.metricSystem(number, 0) };
} else if (order < 6) { } else if (order < 6) {
return { value: this.metricSystem(number, 3), symbol: 'K' }; return { value: this.metricSystem(number, 3), symbol: "K" };
} else if (order < 9) { } else if (order < 9) {
return { value: this.metricSystem(number, 6), symbol: 'M' }; return { value: this.metricSystem(number, 6), symbol: "M" };
} else if (order < 12) { } else if (order < 12) {
return { value: this.metricSystem(number, 9), symbol: 'B' }; return { value: this.metricSystem(number, 9), symbol: "B" };
} else if (order < 15) { } else if (order < 15) {
return { value: this.metricSystem(number, 12), symbol: 'T' }; return { value: this.metricSystem(number, 12), symbol: "T" };
} else { } else {
return { value: this.metricSystem(number, order), power: order }; return { value: this.metricSystem(number, order), power: order };
} }

View File

@ -1,30 +1,28 @@
import * as React from 'react'; import * as React from "react";
import * as ReactDOM from 'react-dom'; import * as ReactDOM from "react-dom";
import { SquiggleChart } from './SquiggleChart' import { SquiggleChart } from "./SquiggleChart";
import { ReactCodeJar } from "react-codejar"; import { ReactCodeJar } from "react-codejar";
import type { exportEnv } from '@quri/squiggle-lang' import type { exportEnv } from "@quri/squiggle-lang";
export interface SquiggleEditorProps { export interface SquiggleEditorProps {
/** The input string for squiggle */ /** The input string for squiggle */
initialSquiggleString? : string, initialSquiggleString?: string;
/** If the output requires monte carlo sampling, the amount of samples */ /** If the output requires monte carlo sampling, the amount of samples */
sampleCount? : number, sampleCount?: number;
/** The amount of points returned to draw the distribution */ /** The amount of points returned to draw the distribution */
outputXYPoints? : number, outputXYPoints?: number;
kernelWidth? : number, kernelWidth?: number;
pointDistLength? : number, pointDistLength?: number;
/** If the result is a function, where the function starts */ /** If the result is a function, where the function starts */
diagramStart? : number, diagramStart?: number;
/** If the result is a function, where the function ends */ /** If the result is a function, where the function ends */
diagramStop? : number, diagramStop?: number;
/** If the result is a function, how many points along the function it samples */ /** If the result is a function, how many points along the function it samples */
diagramCount? : number, diagramCount?: number;
/** The environment, other variables that were already declared */ /** The environment, other variables that were already declared */
environment?: exportEnv, environment?: exportEnv;
/** when the environment changes. Used again for notebook magic*/ /** when the environment changes. Used again for notebook magic*/
onEnvChange?(env: exportEnv) : void onEnvChange?(env: exportEnv): void;
} }
const highlight = (editor: HTMLInputElement) => { const highlight = (editor: HTMLInputElement) => {
@ -34,28 +32,30 @@ const highlight = (editor: HTMLInputElement) => {
}; };
interface SquiggleEditorState { interface SquiggleEditorState {
expression: string, expression: string;
env: exportEnv env: exportEnv;
} }
export class SquiggleEditor extends React.Component<SquiggleEditorProps, SquiggleEditorState>{ export class SquiggleEditor extends React.Component<
SquiggleEditorProps,
SquiggleEditorState
> {
constructor(props: SquiggleEditorProps) { constructor(props: SquiggleEditorProps) {
super(props) super(props);
let code = props.initialSquiggleString ? props.initialSquiggleString : "" let code = props.initialSquiggleString ? props.initialSquiggleString : "";
this.state = {expression: code, env: props.environment } this.state = { expression: code, env: props.environment };
} }
render() { render() {
let {expression, env} = this.state let { expression, env } = this.state;
let props = this.props let props = this.props;
return ( return (
<div> <div>
<ReactCodeJar <ReactCodeJar
code={expression} code={expression}
onUpdate={e => { onUpdate={(e) => {
this.setState({expression: e}) this.setState({ expression: e });
}} }}
style={{ style={{
borderRadius: "6px", borderRadius: "6px",
width: "530px", width: "530px",
border: "1px solid grey", border: "1px solid grey",
@ -65,39 +65,43 @@ export class SquiggleEditor extends React.Component<SquiggleEditorProps, Squiggl
letterSpacing: "normal", letterSpacing: "normal",
lineHeight: "20px", lineHeight: "20px",
padding: "10px", padding: "10px",
tabSize: "4" tabSize: "4",
}} }}
highlight={highlight} highlight={highlight}
lineNumbers={false} lineNumbers={false}
/> />
<SquiggleChart <SquiggleChart
squiggleString={expression} squiggleString={expression}
sampleCount={props.sampleCount} sampleCount={props.sampleCount}
outputXYPoints={props.outputXYPoints} outputXYPoints={props.outputXYPoints}
kernelWidth={props.kernelWidth} kernelWidth={props.kernelWidth}
pointDistLength={props.pointDistLength} pointDistLength={props.pointDistLength}
diagramStart={props.diagramStart} diagramStart={props.diagramStart}
diagramStop={props.diagramStop} diagramStop={props.diagramStop}
diagramCount={props.diagramCount} diagramCount={props.diagramCount}
environment={env} environment={env}
onEnvChange={props.onEnvChange} onEnvChange={props.onEnvChange}
/> />
</div> </div>
) );
} }
} }
export function renderSquiggleEditor(props : SquiggleEditorProps) { export function renderSquiggleEditor(props: SquiggleEditorProps) {
let parent = document.createElement("div") let parent = document.createElement("div");
ReactDOM.render(<SquiggleEditor {...props} onEnvChange={env => { ReactDOM.render(
<SquiggleEditor
{...props}
onEnvChange={(env) => {
// I can set the value here because I need it for creating notebooks
// @ts-ignore
parent.value = env;
// I can set the value here because I need it for creating notebooks parent.dispatchEvent(new CustomEvent("input"));
// @ts-ignore if (props.onEnvChange) props.onEnvChange(env);
parent.value = env }}
/>,
parent.dispatchEvent(new CustomEvent("input")) parent
if(props.onEnvChange) );
props.onEnvChange(env) return parent;
}} /> , parent)
return parent
} }

View File

@ -1,2 +1,2 @@
export { SquiggleChart } from './SquiggleChart'; export { SquiggleChart } from "./SquiggleChart";
export { SquiggleEditor, renderSquiggleEditor } from './SquiggleEditor'; export { SquiggleEditor, renderSquiggleEditor } from "./SquiggleEditor";

View File

@ -4,14 +4,14 @@
"width": 500, "width": 500,
"height": 200, "height": 200,
"padding": 5, "padding": 5,
"data": [{"name": "con"}, {"name": "dis"}], "data": [{ "name": "con" }, { "name": "dis" }],
"signals": [ "signals": [
{ {
"name": "mousex", "name": "mousex",
"description": "x position of mouse", "description": "x position of mouse",
"update": "0", "update": "0",
"on": [{"events": "mousemove", "update": "1-x()/width"}] "on": [{ "events": "mousemove", "update": "1-x()/width" }]
}, },
{ {
"name": "xscale", "name": "xscale",
@ -21,7 +21,7 @@
"input": "range", "input": "range",
"min": 0.1, "min": 0.1,
"max": 1 "max": 1
} }
}, },
{ {
"name": "yscale", "name": "yscale",
@ -31,90 +31,92 @@
"input": "range", "input": "range",
"min": 0.1, "min": 0.1,
"max": 1 "max": 1
} }
} }
], ],
"scales": [{ "scales": [
"name": "xscale", {
"type": "pow", "name": "xscale",
"exponent": {"signal": "xscale"}, "type": "pow",
"range": "width", "exponent": { "signal": "xscale" },
"zero": false, "range": "width",
"nice": false, "zero": false,
"domain": { "nice": false,
"fields": [ "domain": {
{ "data": "con", "field": "x"}, "fields": [
{ "data": "dis", "field": "x"} { "data": "con", "field": "x" },
{ "data": "dis", "field": "x" }
] ]
} }
}, { },
"name": "yscale", {
"type": "pow", "name": "yscale",
"exponent": {"signal": "yscale"}, "type": "pow",
"range": "height", "exponent": { "signal": "yscale" },
"nice": true, "range": "height",
"zero": true, "nice": true,
"domain": { "zero": true,
"fields": [ "domain": {
{ "data": "con", "field": "y"}, "fields": [
{ "data": "dis", "field": "y"} { "data": "con", "field": "y" },
{ "data": "dis", "field": "y" }
] ]
}
} }
}
], ],
"axes": [ "axes": [
{"orient": "bottom", "scale": "xscale", "tickCount": 20}, { "orient": "bottom", "scale": "xscale", "tickCount": 20 },
{"orient": "left", "scale": "yscale"} { "orient": "left", "scale": "yscale" }
], ],
"marks": [ "marks": [
{ {
"type": "area", "type": "area",
"from": {"data": "con"}, "from": { "data": "con" },
"encode": { "encode": {
"enter": { "enter": {
"tooltip": {"signal": "datum.cdf"} "tooltip": { "signal": "datum.cdf" }
}, },
"update": { "update": {
"x": {"scale": "xscale", "field": "x"}, "x": { "scale": "xscale", "field": "x" },
"y": {"scale": "yscale", "field": "y"}, "y": { "scale": "yscale", "field": "y" },
"y2": {"scale": "yscale", "value": 0}, "y2": { "scale": "yscale", "value": 0 },
"fill": { "fill": {
"signal": "{gradient: 'linear', x1: 1, y1: 1, x2: 0, y2: 1, stops: [ {offset: 0.0, color: 'steelblue'}, {offset: clamp(mousex, 0, 1), color: 'steelblue'}, {offset: clamp(mousex, 0, 1), color: 'blue'}, {offset: 1.0, color: 'blue'} ] }" "signal": "{gradient: 'linear', x1: 1, y1: 1, x2: 0, y2: 1, stops: [ {offset: 0.0, color: 'steelblue'}, {offset: clamp(mousex, 0, 1), color: 'steelblue'}, {offset: clamp(mousex, 0, 1), color: 'blue'}, {offset: 1.0, color: 'blue'} ] }"
}, },
"interpolate": {"value": "monotone"}, "interpolate": { "value": "monotone" },
"fillOpacity": {"value": 1} "fillOpacity": { "value": 1 }
} }
} }
}, },
{ {
"type": "rect", "type": "rect",
"from": {"data": "dis"}, "from": { "data": "dis" },
"encode": { "encode": {
"enter": { "enter": {
"y2": {"scale": "yscale", "value": 0}, "y2": { "scale": "yscale", "value": 0 },
"width": {"value": 1} "width": { "value": 1 }
}, },
"update": { "update": {
"x": {"scale": "xscale", "field": "x"}, "x": { "scale": "xscale", "field": "x" },
"y": {"scale": "yscale", "field": "y"} "y": { "scale": "yscale", "field": "y" }
} }
} }
}, },
{ {
"type": "symbol", "type": "symbol",
"from": {"data": "dis"}, "from": { "data": "dis" },
"encode": { "encode": {
"enter": { "enter": {
"shape": {"value": "circle"}, "shape": { "value": "circle" },
"width": {"value": 5}, "width": { "value": 5 },
"tooltip": {"signal": "datum.y"} "tooltip": { "signal": "datum.y" }
}, },
"update": { "update": {
"x": {"scale": "xscale", "field": "x"}, "x": { "scale": "xscale", "field": "x" },
"y": {"scale": "yscale", "field": "y"} "y": { "scale": "yscale", "field": "y" }
} }
} }
} }

View File

@ -1,4 +1,4 @@
import { Meta } from '@storybook/addon-docs'; import { Meta } from "@storybook/addon-docs";
<Meta title="Squiggle/Introduction" /> <Meta title="Squiggle/Introduction" />

View File

@ -1,9 +1,9 @@
import { SquiggleChart } from '../SquiggleChart' import { SquiggleChart } from "../SquiggleChart";
import { Canvas, Meta, Story, Props } from '@storybook/addon-docs'; import { Canvas, Meta, Story, Props } from "@storybook/addon-docs";
<Meta title="Squiggle/SquiggleChart" component={ SquiggleChart } /> <Meta title="Squiggle/SquiggleChart" component={SquiggleChart} />
export const Template = SquiggleChart export const Template = SquiggleChart;
# Squiggle Chart # Squiggle Chart
@ -19,53 +19,62 @@ could be continuous, discrete or mixed.
## Distributions ## Distributions
An example of a normal distribution is: An example of a normal distribution is:
<Canvas> <Canvas>
<Story <Story
name="Normal" name="Normal"
args={{ args={{
squiggleString: "normal(5,2)" squiggleString: "normal(5,2)",
}}> }}
>
{Template.bind({})} {Template.bind({})}
</Story> </Story>
</Canvas> </Canvas>
An example of a Discrete distribution is: An example of a Discrete distribution is:
<Canvas> <Canvas>
<Story <Story
name="Discrete" name="Discrete"
args={{ args={{
squiggleString: "mm(0, 1, [0.5, 0.5])" squiggleString: "mm(0, 1, [0.5, 0.5])",
}}> }}
>
{Template.bind({})} {Template.bind({})}
</Story> </Story>
</Canvas> </Canvas>
An example of a Mixed distribution is: An example of a Mixed distribution is:
<Canvas> <Canvas>
<Story <Story
name="Mixed" name="Mixed"
args={{ args={{
squiggleString: "mm(0, 5 to 10, [0.5, 0.5])" squiggleString: "mm(0, 5 to 10, [0.5, 0.5])",
}}> }}
>
{Template.bind({})} {Template.bind({})}
</Story> </Story>
</Canvas> </Canvas>
## Constants ## Constants
A constant is a simple number as a result. This has special formatting rules A constant is a simple number as a result. This has special formatting rules
to allow large and small numbers being printed cleanly. to allow large and small numbers being printed cleanly.
<Canvas> <Canvas>
<Story <Story
name="Constant" name="Constant"
args={{ args={{
squiggleString: "500000 * 5000000" squiggleString: "500000 * 5000000",
}}> }}
>
{Template.bind({})} {Template.bind({})}
</Story> </Story>
</Canvas> </Canvas>
## Functions ## Functions
Finally, a function can be returned, and this shows how the distribution changes Finally, a function can be returned, and this shows how the distribution changes
over the axis between x = 0 and 10. over the axis between x = 0 and 10.
@ -73,8 +82,9 @@ over the axis between x = 0 and 10.
<Story <Story
name="Function" name="Function"
args={{ args={{
squiggleString: "f(x) = normal(x,x)\nf" squiggleString: "f(x) = normal(x,x)\nf",
}}> }}
>
{Template.bind({})} {Template.bind({})}
</Story> </Story>
</Canvas> </Canvas>

View File

@ -1,32 +1,32 @@
const path = require('path'); const path = require("path");
module.exports = { module.exports = {
mode: 'production', mode: "production",
devtool: 'source-map', devtool: "source-map",
entry: './src/index.ts', entry: "./src/index.ts",
module: { module: {
rules: [ rules: [
{ {
test: /\.tsx?$/, test: /\.tsx?$/,
use: 'ts-loader', use: "ts-loader",
exclude: /node_modules/, exclude: /node_modules/,
}, },
], ],
}, },
resolve: { resolve: {
extensions: ['.js', '.tsx', '.ts'], extensions: [".js", ".tsx", ".ts"],
}, },
output: { output: {
filename: 'bundle.js', filename: "bundle.js",
path: path.resolve(__dirname, 'dist'), path: path.resolve(__dirname, "dist"),
library: { library: {
name: 'squiggle_components', name: "squiggle_components",
type: 'umd', type: "umd",
}, },
}, },
devServer: { devServer: {
static: { static: {
directory: path.join(__dirname, 'public'), directory: path.join(__dirname, "public"),
}, },
compress: true, compress: true,
port: 9000, port: 9000,

View File

@ -14370,6 +14370,11 @@ prepend-http@^2.0.0:
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.0.tgz#b6a5bf1284026ae640f17f7ff5658a7567fc0d18" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.0.tgz#b6a5bf1284026ae640f17f7ff5658a7567fc0d18"
integrity sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w== integrity sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w==
prettier@^2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.0.tgz#12f8f504c4d8ddb76475f441337542fa799207d4"
integrity sha512-m2FgJibYrBGGgQXNzfd0PuDGShJgRavjUoRCw1mZERIWVSXF0iLzLm+aOqTAbLnC3n6JzUhAA8uZnFVghHJ86A==
pretty-bytes@^5.3.0, pretty-bytes@^5.4.1: pretty-bytes@^5.3.0, pretty-bytes@^5.4.1:
version "5.6.0" version "5.6.0"
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb"