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
squiggleString={expression}
sampleCount={props.sampleCount}
outputXYPoints={props.outputXYPoints}
kernelWidth={props.kernelWidth}
pointDistLength={props.pointDistLength}
diagramStart={props.diagramStart}
diagramStop={props.diagramStop}
diagramCount={props.diagramCount}
environment={env}
onEnvChange={props.onEnvChange}
/> />
</div> <SquiggleChart
) squiggleString={expression}
sampleCount={props.sampleCount}
outputXYPoints={props.outputXYPoints}
kernelWidth={props.kernelWidth}
pointDistLength={props.pointDistLength}
diagramStart={props.diagramStart}
diagramStop={props.diagramStop}
diagramCount={props.diagramCount}
environment={env}
onEnvChange={props.onEnvChange}
/>
</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
// I can set the value here because I need it for creating notebooks {...props}
// @ts-ignore onEnvChange={(env) => {
parent.value = env // I can set the value here because I need it for creating notebooks
// @ts-ignore
parent.value = env;
parent.dispatchEvent(new CustomEvent("input")) parent.dispatchEvent(new CustomEvent("input"));
if(props.onEnvChange) if (props.onEnvChange) props.onEnvChange(env);
props.onEnvChange(env) }}
}} /> , parent) />,
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,8 +1,8 @@
import { Meta } from '@storybook/addon-docs'; import { Meta } from "@storybook/addon-docs";
<Meta title="Squiggle/Introduction" /> <Meta title="Squiggle/Introduction" />
This is the component library for Squiggle. All of these components are react This is the component library for Squiggle. All of these components are react
components, and can be used in any application that you see fit. components, and can be used in any application that you see fit.
Currently, the only component that is provided is the SquiggleChart component. Currently, the only component that is provided is the SquiggleChart component.

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"