From c9a54d4c1402acc9a6688d676be8a814774199df Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Thu, 26 May 2022 10:08:19 -0400 Subject: [PATCH 1/8] First attempt at playground configurable toggles --- packages/components/package.json | 5 +- .../src/components/FunctionChart.tsx | 5 +- .../components/src/components/JsonEditor.tsx | 55 +++++++++++ .../src/components/SquiggleChart.tsx | 1 + .../src/components/SquigglePlayground.tsx | 95 +++++++++++++++++-- yarn.lock | 62 ++++++++++-- 6 files changed, 205 insertions(+), 18 deletions(-) create mode 100644 packages/components/src/components/JsonEditor.tsx diff --git a/packages/components/package.json b/packages/components/package.json index 67b2eeb5..3b594bc5 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -3,18 +3,21 @@ "version": "0.2.20", "license": "MIT", "dependencies": { + "@hookform/resolvers": "^2.8.10", "@quri/squiggle-lang": "^0.2.8", "@react-hook/size": "^2.1.2", "lodash": "^4.17.21", "react": "^18.1.0", "react-ace": "^10.1.0", "react-dom": "^18.1.0", + "react-hook-form": "^7.31.2", "react-use": "^17.4.0", "react-vega": "^7.5.1", "styled-components": "^5.3.5", "vega": "^5.22.1", "vega-embed": "^6.20.6", - "vega-lite": "^5.2.0" + "vega-lite": "^5.2.0", + "yup": "^0.32.11" }, "devDependencies": { "@babel/plugin-proposal-private-property-in-object": "^7.17.12", diff --git a/packages/components/src/components/FunctionChart.tsx b/packages/components/src/components/FunctionChart.tsx index 242bf719..d774f3db 100644 --- a/packages/components/src/components/FunctionChart.tsx +++ b/packages/components/src/components/FunctionChart.tsx @@ -45,6 +45,7 @@ interface FunctionChartProps { fn: lambdaValue; chartSettings: FunctionChartSettings; environment: environment; + height: number; } type percentiles = { @@ -149,6 +150,7 @@ export const FunctionChart: React.FC = ({ fn, chartSettings, environment, + height }: FunctionChartProps) => { let [mouseOverlay, setMouseOverlay] = React.useState(0); function handleHover(_name: string, value: unknown) { @@ -172,7 +174,7 @@ export const FunctionChart: React.FC = ({ ) : ( @@ -188,6 +190,7 @@ export const FunctionChart: React.FC = ({ <> diff --git a/packages/components/src/components/JsonEditor.tsx b/packages/components/src/components/JsonEditor.tsx new file mode 100644 index 00000000..eeac3de8 --- /dev/null +++ b/packages/components/src/components/JsonEditor.tsx @@ -0,0 +1,55 @@ +import _ from "lodash"; +import React, { FC } from "react"; +import AceEditor from "react-ace"; + +import "ace-builds/src-noconflict/mode-json"; +import "ace-builds/src-noconflict/theme-github"; +import { + jsImports, + defaultImports, +} from "@quri/squiggle-lang"; + +interface CodeEditorProps { + value: string; + onChange: (value: string) => void; + oneLine?: boolean; + width?: number; + height: number; + showGutter?: boolean; +} + +export let JsonEditor: FC = ({ + value, + onChange, + oneLine = false, + showGutter = false, + height, +}: CodeEditorProps) => { + let lineCount = value.split("\n").length; + let id = _.uniqueId(); + return ( + + ); +}; + +export default JsonEditor; diff --git a/packages/components/src/components/SquiggleChart.tsx b/packages/components/src/components/SquiggleChart.tsx index 50fcebb3..5cbebc8e 100644 --- a/packages/components/src/components/SquiggleChart.tsx +++ b/packages/components/src/components/SquiggleChart.tsx @@ -207,6 +207,7 @@ const SquiggleItem: React.FC = ({ = ({ initialSquiggleString = "", height = 300, @@ -95,9 +125,12 @@ let SquigglePlayground: FC = ({ showSummary = false, }: PlaygroundProps) => { let [squiggleString, setSquiggleString] = useState(initialSquiggleString); - let [sampleCount, setSampleCount] = useState(1000); - let [outputXYPoints, setOutputXYPoints] = useState(1000); - let [pointDistLength, setPointDistLength] = useState(1000); + let [importString, setImportString] = useState("{}"); + let [imports, setImports] = useState({}); + let [importsAreValid, setImportsAreValid] = useState(true); + let [showTypesInput, setShowTypesInput] = useState(showTypes); + let [showControlsInput, setShowControlsInput] = useState(showControls); + let [showSummaryInput, setShowSummaryInput] = useState(showSummary); let [diagramStart, setDiagramStart] = useState(0); let [diagramStop, setDiagramStop] = useState(10); let [diagramCount, setDiagramCount] = useState(20); @@ -106,12 +139,46 @@ let SquigglePlayground: FC = ({ stop: diagramStop, count: diagramCount, }; + const { + register, + formState: { errors }, + control, + } = useForm({ + resolver: yupResolver(schema), + defaultValues: { + sampleCount: 1000, + xyPointLength: 1000, + chartHeight: 150, + showTypes: showTypes, + showControls: showControls, + showSummary: showSummary, + }, + }); + const foo = useWatch({ + control, + }); let env: environment = { - sampleCount: sampleCount, - xyPointLength: outputXYPoints, + sampleCount: Number(foo.sampleCount), + xyPointLength: Number(foo.xyPointLength), + }; + let getChangeJson = (r: string) => { + setImportString(r); + try { + setImports(JSON.parse(r)); + setImportsAreValid(true); + } catch (e) { + setImportsAreValid(false); + } + (""); }; return ( + + + + + + = ({ showGutter={true} height={height - 3} /> + + {importsAreValid ? "Valid" : "INVALID"} @@ -128,12 +203,12 @@ let SquigglePlayground: FC = ({ squiggleString={squiggleString} environment={env} chartSettings={chartSettings} - height={150} - showTypes={showTypes} - showControls={showControls} + height={foo.chartHeight} + showTypes={foo.showTypes} + showControls={foo.showControls} bindings={defaultBindings} - jsImports={defaultImports} - showSummary={showSummary} + jsImports={imports} + showSummary={foo.showSummary} /> diff --git a/yarn.lock b/yarn.lock index 39329f75..680465a1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1245,6 +1245,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.15.4": + version "7.18.3" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.3.tgz#c7b654b57f6f63cf7f8b418ac9ca04408c4579f4" + integrity sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.12.7", "@babel/template@^7.16.7", "@babel/template@^7.3.3": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" @@ -1839,6 +1846,11 @@ dependencies: "@hapi/hoek" "^9.0.0" +"@hookform/resolvers@^2.8.10": + version "2.8.10" + resolved "https://registry.yarnpkg.com/@hookform/resolvers/-/resolvers-2.8.10.tgz#b66d7a7848b1b1dd5b976a73fff36bb366666e7d" + integrity sha512-DDFtNlugsbwAhCJHYp3NcN5LvJrwSsCLPi41Wo5O8UAIbUFnBfY/jW+zKnlX57BZ4jE0j/g6R9rB3JlO89ad0g== + "@humanwhocodes/config-array@^0.9.2": version "0.9.5" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.9.5.tgz#2cbaf9a89460da24b5ca6531b8bbfc23e1df50c7" @@ -4163,7 +4175,7 @@ resolved "https://registry.yarnpkg.com/@types/katex/-/katex-0.11.1.tgz#34de04477dcf79e2ef6c8d23b41a3d81f9ebeaf5" integrity sha512-DUlIj2nk0YnJdlWgsFuVKcX27MLW0KbKmGVoUHmFr+74FYYNUDAaj9ZqTADvsbE8rfxuVmSFc7KczYn5Y09ozg== -"@types/lodash@^4.14.167", "@types/lodash@^4.14.182": +"@types/lodash@^4.14.167", "@types/lodash@^4.14.175", "@types/lodash@^4.14.182": version "4.14.182" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.182.tgz#05301a4d5e62963227eaafe0ce04dd77c54ea5c2" integrity sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q== @@ -4298,10 +4310,10 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^18.0.1", "@types/react@^18.0.9": - version "18.0.9" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.9.tgz#d6712a38bd6cd83469603e7359511126f122e878" - integrity sha512-9bjbg1hJHUm4De19L1cHiW0Jvx3geel6Qczhjd0qY5VKVE2X5+x77YxAepuCwVh4vrgZJdgEJw48zrhRIeF4Nw== +"@types/react@*", "@types/react@17.0.43", "@types/react@^18.0.9": + version "17.0.43" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.43.tgz#4adc142887dd4a2601ce730bc56c3436fdb07a55" + integrity sha512-8Q+LNpdxf057brvPu1lMtC5Vn7J119xrP1aq4qiaefNioQUYANF/CYeK4NsKorSZyUGJ66g0IM+4bbjwx45o2A== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -11601,6 +11613,11 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" +lodash-es@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" + integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== + lodash.assignin@^4.0.9: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e263a8dc6a28a2" @@ -12290,6 +12307,11 @@ nano-css@^5.3.1: stacktrace-js "^2.0.2" stylis "^4.0.6" +nanoclone@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/nanoclone/-/nanoclone-0.2.1.tgz#dd4090f8f1a110d26bb32c49ed2f5b9235209ed4" + integrity sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA== + nanoid@^3.3.1, nanoid@^3.3.3: version "3.3.4" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" @@ -13998,6 +14020,11 @@ prop-types@^15.0.0, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: object-assign "^4.1.1" react-is "^16.13.1" +property-expr@^2.0.4: + version "2.0.5" + resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.5.tgz#278bdb15308ae16af3e3b9640024524f4dc02cb4" + integrity sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA== + property-information@^5.0.0, property-information@^5.3.0: version "5.6.0" resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.6.0.tgz#61675545fb23002f245c6540ec46077d4da3ed69" @@ -14328,6 +14355,11 @@ react-helmet-async@*, react-helmet-async@^1.3.0: react-fast-compare "^3.2.0" shallowequal "^1.1.0" +react-hook-form@^7.31.2: + version "7.31.2" + resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.31.2.tgz#efb7ac469810954488b7cf40be4e5017122c6e5e" + integrity sha512-oPudn3YuyzWg//IsT9z2cMEjWocAgHWX/bmueDT8cmsYQnGY5h7/njjvMDfLVv3mbdhYBjslTRnII2MIT7eNCA== + react-inspector@^5.1.0: version "5.1.1" resolved "https://registry.yarnpkg.com/react-inspector/-/react-inspector-5.1.1.tgz#58476c78fde05d5055646ed8ec02030af42953c8" @@ -14525,7 +14557,7 @@ react-vega@^7.5.1: prop-types "^15.8.1" vega-embed "^6.5.1" -react@^18.0.0, react@^18.1.0: +react@^18.1.0: version "18.1.0" resolved "https://registry.yarnpkg.com/react/-/react-18.1.0.tgz#6f8620382decb17fdc5cc223a115e2adbf104890" integrity sha512-4oL8ivCz5ZEPyclFQXaNksK3adutVS8l2xzZU0cqEFrE9Sb7fC0EFK5uEk74wIreL1DERyjvsU915j1pcT2uEQ== @@ -16559,6 +16591,11 @@ topojson-client@^3.1.0: dependencies: commander "2" +toposort@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330" + integrity sha1-riF2gXXRVZ1IvvNUILL0li8JwzA= + totalist@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" @@ -18440,6 +18477,19 @@ yocto-queue@^0.1.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== +yup@^0.32.11: + version "0.32.11" + resolved "https://registry.yarnpkg.com/yup/-/yup-0.32.11.tgz#d67fb83eefa4698607982e63f7ca4c5ed3cf18c5" + integrity sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg== + dependencies: + "@babel/runtime" "^7.15.4" + "@types/lodash" "^4.14.175" + lodash "^4.17.21" + lodash-es "^4.17.21" + nanoclone "^0.2.1" + property-expr "^2.0.4" + toposort "^2.0.2" + zwitch@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920" From f393cfda9f0c807d885dab6ef943c9ee0e3e1413 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Thu, 26 May 2022 14:41:58 -0400 Subject: [PATCH 2/8] Simple line chart for Functions --- .../src/components/FunctionChart.tsx | 219 +++--------------- .../src/components/FunctionChart1Dist.tsx | 214 +++++++++++++++++ .../src/components/FunctionChart1Number.tsx | 145 ++++++++++++ .../src/components/SquigglePlayground.tsx | 28 ++- .../src/vega-specs/spec-line-chart.json | 88 +++++++ packages/components/tsconfig.json | 3 +- 6 files changed, 504 insertions(+), 193 deletions(-) create mode 100644 packages/components/src/components/FunctionChart1Dist.tsx create mode 100644 packages/components/src/components/FunctionChart1Number.tsx create mode 100644 packages/components/src/vega-specs/spec-line-chart.json diff --git a/packages/components/src/components/FunctionChart.tsx b/packages/components/src/components/FunctionChart.tsx index d774f3db..5a37f0ca 100644 --- a/packages/components/src/components/FunctionChart.tsx +++ b/packages/components/src/components/FunctionChart.tsx @@ -7,34 +7,11 @@ import { lambdaValue, environment, runForeign, - squiggleExpression, - errorValue, - errorValueToString, } from "@quri/squiggle-lang"; -import { createClassFromSpec } from "react-vega"; -import * as percentilesSpec from "../vega-specs/spec-percentiles.json"; -import { DistributionChart } from "./DistributionChart"; -import { NumberShower } from "./NumberShower"; +import { FunctionChart1Dist } from "./FunctionChart1Dist"; +import { FunctionChart1Number } from "./FunctionChart1Number"; import { ErrorBox } from "./ErrorBox"; -let SquigglePercentilesChart = createClassFromSpec({ - spec: percentilesSpec as Spec, -}); - -const _rangeByCount = (start: number, stop: number, count: number) => { - const step = (stop - start) / (count - 1); - const items = _.range(start, stop, step); - const result = items.concat([stop]); - return result; -}; - -function unwrap(x: result): a { - if (x.tag === "Ok") { - return x.value; - } else { - throw Error("FAILURE TO UNWRAP"); - } -} export type FunctionChartSettings = { start: number; stop: number; @@ -48,167 +25,47 @@ interface FunctionChartProps { height: number; } -type percentiles = { - x: number; - p1: number; - p5: number; - p10: number; - p20: number; - p30: number; - p40: number; - p50: number; - p60: number; - p70: number; - p80: number; - p90: number; - p95: number; - p99: number; -}[]; - -type errors = _.Dictionary< - { - x: number; - value: string; - }[] ->; - -type point = { x: number; value: result }; - -let getPercentiles = ({ chartSettings, fn, environment }) => { - let chartPointsToRender = _rangeByCount( - chartSettings.start, - chartSettings.stop, - chartSettings.count - ); - - let chartPointsData: point[] = chartPointsToRender.map((x) => { - let result = runForeign(fn, [x], environment); - if (result.tag === "Ok") { - if (result.value.tag == "distribution") { - return { x, value: { tag: "Ok", value: result.value.value } }; - } else { - return { - x, - value: { - tag: "Error", - value: - "Cannot currently render functions that don't return distributions", - }, - }; - } - } else { - return { - x, - value: { tag: "Error", value: errorValueToString(result.value) }, - }; - } - }); - - let initialPartition: [ - { x: number; value: Distribution }[], - { x: number; value: string }[] - ] = [[], []]; - - let [functionImage, errors] = chartPointsData.reduce((acc, current) => { - if (current.value.tag === "Ok") { - acc[0].push({ x: current.x, value: current.value.value }); - } else { - acc[1].push({ x: current.x, value: current.value.value }); - } - return acc; - }, initialPartition); - - let groupedErrors: errors = _.groupBy(errors, (x) => x.value); - - let percentiles: percentiles = functionImage.map(({ x, value }) => { - // We convert it to to a pointSet distribution first, so that in case its a sample set - // distribution, it doesn't internally convert it to a pointSet distribution for every - // single inv() call. - let toPointSet: Distribution = unwrap(value.toPointSet()); - return { - x: x, - p1: unwrap(toPointSet.inv(0.01)), - p5: unwrap(toPointSet.inv(0.05)), - p10: unwrap(toPointSet.inv(0.1)), - p20: unwrap(toPointSet.inv(0.2)), - p30: unwrap(toPointSet.inv(0.3)), - p40: unwrap(toPointSet.inv(0.4)), - p50: unwrap(toPointSet.inv(0.5)), - p60: unwrap(toPointSet.inv(0.6)), - p70: unwrap(toPointSet.inv(0.7)), - p80: unwrap(toPointSet.inv(0.8)), - p90: unwrap(toPointSet.inv(0.9)), - p95: unwrap(toPointSet.inv(0.95)), - p99: unwrap(toPointSet.inv(0.99)), - }; - }); - - return { percentiles, errors: groupedErrors }; -}; - export const FunctionChart: React.FC = ({ fn, chartSettings, environment, - height + height, }: FunctionChartProps) => { - let [mouseOverlay, setMouseOverlay] = React.useState(0); - function handleHover(_name: string, value: unknown) { - setMouseOverlay(value as number); - } - function handleOut() { - setMouseOverlay(NaN); - } - const signalListeners = { mousemove: handleHover, mouseout: handleOut }; - let mouseItem: result = !!mouseOverlay - ? runForeign(fn, [mouseOverlay], environment) - : { - tag: "Error", - value: { - tag: "REExpectedType", - value: "Hover x-coordinate returned NaN. Expected a number.", - }, - }; - let showChart = - mouseItem.tag === "Ok" && mouseItem.value.tag == "distribution" ? ( - - ) : ( - <> - ); + let result = runForeign(fn, [chartSettings.start], environment); + let resultType = result.tag === "Ok" ? result.value.tag : "Error"; - let getPercentilesMemoized = React.useMemo( - () => getPercentiles({ chartSettings, fn, environment }), - [environment, fn] - ); - - return ( - <> - - {showChart} - {_.entries(getPercentilesMemoized.errors).map( - ([errorName, errorPoints]) => ( - - Values:{" "} - {errorPoints - .map((r, i) => ) - .reduce((a, b) => ( - <> - {a}, {b} - - ))} + let comp = () => { + switch (resultType) { + case "distribution": + return ( + + ); + case "number": + return ( + + ); + case "Error": + return ( + The function failed to be run + ); + default: + return ( + + There is no function visualization for this type of function - ) - )} - - ); + ); + } + }; + + return comp(); }; diff --git a/packages/components/src/components/FunctionChart1Dist.tsx b/packages/components/src/components/FunctionChart1Dist.tsx new file mode 100644 index 00000000..e1d8333c --- /dev/null +++ b/packages/components/src/components/FunctionChart1Dist.tsx @@ -0,0 +1,214 @@ +import * as React from "react"; +import _ from "lodash"; +import type { Spec } from "vega"; +import { + Distribution, + result, + lambdaValue, + environment, + runForeign, + squiggleExpression, + errorValue, + errorValueToString, +} from "@quri/squiggle-lang"; +import { createClassFromSpec } from "react-vega"; +import * as percentilesSpec from "../vega-specs/spec-percentiles.json"; +import { DistributionChart } from "./DistributionChart"; +import { NumberShower } from "./NumberShower"; +import { ErrorBox } from "./ErrorBox"; + +let SquigglePercentilesChart = createClassFromSpec({ + spec: percentilesSpec as Spec, +}); + +const _rangeByCount = (start: number, stop: number, count: number) => { + const step = (stop - start) / (count - 1); + const items = _.range(start, stop, step); + const result = items.concat([stop]); + return result; +}; + +function unwrap(x: result): a { + if (x.tag === "Ok") { + return x.value; + } else { + throw Error("FAILURE TO UNWRAP"); + } +} +export type FunctionChartSettings = { + start: number; + stop: number; + count: number; +}; + +interface FunctionChartProps { + fn: lambdaValue; + chartSettings: FunctionChartSettings; + environment: environment; + height: number; +} + +type percentiles = { + x: number; + p1: number; + p5: number; + p10: number; + p20: number; + p30: number; + p40: number; + p50: number; + p60: number; + p70: number; + p80: number; + p90: number; + p95: number; + p99: number; +}[]; + +type errors = _.Dictionary< + { + x: number; + value: string; + }[] +>; + +type point = { x: number; value: result }; + +let getPercentiles = ({ chartSettings, fn, environment }) => { + let chartPointsToRender = _rangeByCount( + chartSettings.start, + chartSettings.stop, + chartSettings.count + ); + + let chartPointsData: point[] = chartPointsToRender.map((x) => { + let result = runForeign(fn, [x], environment); + if (result.tag === "Ok") { + if (result.value.tag == "distribution") { + return { x, value: { tag: "Ok", value: result.value.value } }; + } else { + return { + x, + value: { + tag: "Error", + value: + "Cannot currently render functions that don't return distributions", + }, + }; + } + } else { + return { + x, + value: { tag: "Error", value: errorValueToString(result.value) }, + }; + } + }); + + let initialPartition: [ + { x: number; value: Distribution }[], + { x: number; value: string }[] + ] = [[], []]; + + let [functionImage, errors] = chartPointsData.reduce((acc, current) => { + if (current.value.tag === "Ok") { + acc[0].push({ x: current.x, value: current.value.value }); + } else { + acc[1].push({ x: current.x, value: current.value.value }); + } + return acc; + }, initialPartition); + + let groupedErrors: errors = _.groupBy(errors, (x) => x.value); + + let percentiles: percentiles = functionImage.map(({ x, value }) => { + // We convert it to to a pointSet distribution first, so that in case its a sample set + // distribution, it doesn't internally convert it to a pointSet distribution for every + // single inv() call. + let toPointSet: Distribution = unwrap(value.toPointSet()); + return { + x: x, + p1: unwrap(toPointSet.inv(0.01)), + p5: unwrap(toPointSet.inv(0.05)), + p10: unwrap(toPointSet.inv(0.1)), + p20: unwrap(toPointSet.inv(0.2)), + p30: unwrap(toPointSet.inv(0.3)), + p40: unwrap(toPointSet.inv(0.4)), + p50: unwrap(toPointSet.inv(0.5)), + p60: unwrap(toPointSet.inv(0.6)), + p70: unwrap(toPointSet.inv(0.7)), + p80: unwrap(toPointSet.inv(0.8)), + p90: unwrap(toPointSet.inv(0.9)), + p95: unwrap(toPointSet.inv(0.95)), + p99: unwrap(toPointSet.inv(0.99)), + }; + }); + + return { percentiles, errors: groupedErrors }; +}; + +export const FunctionChart1Dist: React.FC = ({ + fn, + chartSettings, + environment, + height +}: FunctionChartProps) => { + let [mouseOverlay, setMouseOverlay] = React.useState(0); + function handleHover(_name: string, value: unknown) { + setMouseOverlay(value as number); + } + function handleOut() { + setMouseOverlay(NaN); + } + const signalListeners = { mousemove: handleHover, mouseout: handleOut }; + let mouseItem: result = !!mouseOverlay + ? runForeign(fn, [mouseOverlay], environment) + : { + tag: "Error", + value: { + tag: "REExpectedType", + value: "Hover x-coordinate returned NaN. Expected a number.", + }, + }; + let showChart = + mouseItem.tag === "Ok" && mouseItem.value.tag == "distribution" ? ( + + ) : ( + <> + ); + + let getPercentilesMemoized = React.useMemo( + () => getPercentiles({ chartSettings, fn, environment }), + [environment, fn] + ); + + return ( + <> + + {showChart} + {_.entries(getPercentilesMemoized.errors).map( + ([errorName, errorPoints]) => ( + + Values:{" "} + {errorPoints + .map((r, i) => ) + .reduce((a, b) => ( + <> + {a}, {b} + + ))} + + ) + )} + + ); +}; diff --git a/packages/components/src/components/FunctionChart1Number.tsx b/packages/components/src/components/FunctionChart1Number.tsx new file mode 100644 index 00000000..fa72bf71 --- /dev/null +++ b/packages/components/src/components/FunctionChart1Number.tsx @@ -0,0 +1,145 @@ +import * as React from "react"; +import _ from "lodash"; +import type { Spec } from "vega"; +import { + Distribution, + result, + lambdaValue, + environment, + runForeign, + squiggleExpression, + errorValue, + errorValueToString, +} from "@quri/squiggle-lang"; +import { createClassFromSpec } from "react-vega"; +import * as lineChartSpec from "../vega-specs/spec-line-chart.json"; +import { DistributionChart } from "./DistributionChart"; +import { NumberShower } from "./NumberShower"; +import { ErrorBox } from "./ErrorBox"; + +let SquiggleLineChart = createClassFromSpec({ + spec: lineChartSpec as Spec, +}); + +const _rangeByCount = (start: number, stop: number, count: number) => { + const step = (stop - start) / (count - 1); + const items = _.range(start, stop, step); + const result = items.concat([stop]); + return result; +}; + +function unwrap(x: result): a { + if (x.tag === "Ok") { + return x.value; + } else { + throw Error("FAILURE TO UNWRAP"); + } +} +export type FunctionChartSettings = { + start: number; + stop: number; + count: number; +}; + +interface FunctionChartProps { + fn: lambdaValue; + chartSettings: FunctionChartSettings; + environment: environment; + height: number; +} + +type point = { x: number; value: result }; + +let getFunctionImage = ({ chartSettings, fn, environment }) => { + let chartPointsToRender = _rangeByCount( + chartSettings.start, + chartSettings.stop, + chartSettings.count + ); + + let chartPointsData: point[] = chartPointsToRender.map((x) => { + let result = runForeign(fn, [x], environment); + if (result.tag === "Ok") { + if (result.value.tag == "number") { + return { x, value: { tag: "Ok", value: result.value.value } }; + } else { + return { + x, + value: { + tag: "Error", + value: + "Cannot currently render functions that don't return distributions", + }, + }; + } + } else { + return { + x, + value: { tag: "Error", value: errorValueToString(result.value) }, + }; + } + }); + + let initialPartition: [ + { x: number; value: number }[], + { x: number; value: string }[] + ] = [[], []]; + + let [functionImage, errors] = chartPointsData.reduce((acc, current) => { + if (current.value.tag === "Ok") { + acc[0].push({ x: current.x, value: current.value.value }); + } else { + acc[1].push({ x: current.x, value: current.value.value }); + } + return acc; + }, initialPartition); + + return { errors, functionImage }; +}; + +export const FunctionChart1Number: React.FC = ({ + fn, + chartSettings, + environment, + height, +}: FunctionChartProps) => { + let [mouseOverlay, setMouseOverlay] = React.useState(0); + function handleHover(_name: string, value: unknown) { + setMouseOverlay(value as number); + } + function handleOut() { + setMouseOverlay(NaN); + } + const signalListeners = { mousemove: handleHover, mouseout: handleOut }; + let mouseItem: result = !!mouseOverlay + ? runForeign(fn, [mouseOverlay], environment) + : { + tag: "Error", + value: { + tag: "REExpectedType", + value: "Hover x-coordinate returned NaN. Expected a number.", + }, + }; + + let getFunctionImageMemoized = React.useMemo( + () => getFunctionImage({ chartSettings, fn, environment }), + [environment, fn] + ); + + let data = getFunctionImageMemoized.functionImage.map(({x, value}) => ({x, y:value})) + return ( + <> + + {getFunctionImageMemoized.errors.map(({ x, value }) => ( + + Error at point ${x} + + ))} + + ); +}; diff --git a/packages/components/src/components/SquigglePlayground.tsx b/packages/components/src/components/SquigglePlayground.tsx index 1bbd84a2..c28bf11a 100644 --- a/packages/components/src/components/SquigglePlayground.tsx +++ b/packages/components/src/components/SquigglePlayground.tsx @@ -72,9 +72,13 @@ const Display = styled.div` max-height: ${(props) => props.maxHeight}px; `; -const Row = styled.div` +interface RowProps { + readonly leftPercentage: number; +} + +const Row = styled.div` display: grid; - grid-template-columns: 50% 50%; + grid-template-columns: ${(p) => p.leftPercentage}% ${(p) => 100 - p.leftPercentage}%; `; const Col = styled.div``; @@ -111,6 +115,7 @@ const schema = yup .min(10) .max(10000), chartHeight: yup.number().required().positive().integer().default(350), + leftSize: yup.number().required().positive().integer().min(10).max(100).default(50), showTypes: yup.boolean(), showControls: yup.boolean(), showSummary: yup.boolean(), @@ -152,14 +157,15 @@ let SquigglePlayground: FC = ({ showTypes: showTypes, showControls: showControls, showSummary: showSummary, + leftSize: 50, }, }); - const foo = useWatch({ + const vars = useWatch({ control, }); let env: environment = { - sampleCount: Number(foo.sampleCount), - xyPointLength: Number(foo.xyPointLength), + sampleCount: Number(vars.sampleCount), + xyPointLength: Number(vars.xyPointLength), }; let getChangeJson = (r: string) => { setImportString(r); @@ -169,17 +175,17 @@ let SquigglePlayground: FC = ({ } catch (e) { setImportsAreValid(false); } - (""); }; return ( + - + = ({ squiggleString={squiggleString} environment={env} chartSettings={chartSettings} - height={foo.chartHeight} - showTypes={foo.showTypes} - showControls={foo.showControls} + height={vars.chartHeight} + showTypes={vars.showTypes} + showControls={vars.showControls} bindings={defaultBindings} jsImports={imports} - showSummary={foo.showSummary} + showSummary={vars.showSummary} /> diff --git a/packages/components/src/vega-specs/spec-line-chart.json b/packages/components/src/vega-specs/spec-line-chart.json new file mode 100644 index 00000000..117d9543 --- /dev/null +++ b/packages/components/src/vega-specs/spec-line-chart.json @@ -0,0 +1,88 @@ +{ + "$schema": "https://vega.github.io/schema/vega/v5.json", + "width": 500, + "height": 200, + "padding": 5, + "data": [ + { + "name": "facet", + "values": [], + "format": { + "type": "json", + "parse": { + "timestamp": "date" + } + } + } + ], + "scales": [ + { + "name": "x", + "type": "linear", + "nice": true, + "domain": { + "data": "facet", + "field": "x" + }, + "range": "width" + }, + { + "name": "y", + "type": "linear", + "range": "height", + "nice": true, + "zero": true, + "domain": { + "data": "facet", + "field": "y" + } + } + ], + "signals": [ + { + "name": "mousemove", + "on": [{ "events": "mousemove", "update": "invert('x', x())" }] + }, + { + "name": "mouseout", + "on": [{ "events": "mouseout", "update": "invert('x', x())" }] + } + ], + "axes": [ + { + "orient": "bottom", + "scale": "x", + "grid": false, + "labelColor": "#727d93", + "tickColor": "#fff", + "tickOpacity": 0.0, + "domainColor": "#727d93", + "domainOpacity": 0.1, + "tickCount": 5 + }, + { + "orient": "left", + "scale": "y", + "grid": false, + "labelColor": "#727d93", + "tickColor": "#fff", + "tickOpacity": 0.0, + "domainColor": "#727d93", + "domainOpacity": 0.1, + "tickCount": 5 + } + ], + "marks": [ + { + "type": "line", + "from": { "data": "facet" }, + "encode": { + "enter": { + "x": { "scale": "x", "field": "x" }, + "y": { "scale": "y", "field": "y" }, + "strokeWidth": { "value": 2 } + } + } + } + ] +} diff --git a/packages/components/tsconfig.json b/packages/components/tsconfig.json index c8d799d5..38d28704 100644 --- a/packages/components/tsconfig.json +++ b/packages/components/tsconfig.json @@ -20,7 +20,8 @@ }, "files": [ "src/vega-specs/spec-distributions.json", - "src/vega-specs/spec-percentiles.json" + "src/vega-specs/spec-percentiles.json", + "src/vega-specs/spec-line-chart.json" ], "target": "ES6", "include": ["src/**/*", "src/*"], From d38caff28f7671595b8ce8012b3db8401ae0c8ba Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Thu, 26 May 2022 16:16:40 -0400 Subject: [PATCH 3/8] Minor cleanup --- .../src/components/FunctionChart.tsx | 16 ++++- .../src/components/FunctionChart1Number.tsx | 5 +- .../src/components/SquiggleChart.tsx | 24 ++++--- .../src/components/SquigglePlayground.tsx | 71 +++++++++++-------- 4 files changed, 72 insertions(+), 44 deletions(-) diff --git a/packages/components/src/components/FunctionChart.tsx b/packages/components/src/components/FunctionChart.tsx index 5a37f0ca..bfe50684 100644 --- a/packages/components/src/components/FunctionChart.tsx +++ b/packages/components/src/components/FunctionChart.tsx @@ -1,5 +1,4 @@ import * as React from "react"; -import _ from "lodash"; import type { Spec } from "vega"; import { Distribution, @@ -31,8 +30,19 @@ export const FunctionChart: React.FC = ({ environment, height, }: FunctionChartProps) => { - let result = runForeign(fn, [chartSettings.start], environment); - let resultType = result.tag === "Ok" ? result.value.tag : "Error"; + let result1 = runForeign(fn, [chartSettings.start], environment); + let result2 = runForeign(fn, [chartSettings.stop], environment); + let getValidResult = () => { + if (result1.tag === "Ok") { + return result1; + } else if (result2.tag === "Ok") { + return result2; + } else { + return result1; + } + }; + let validResult = getValidResult(); + let resultType = validResult.tag === "Ok" ? validResult.value.tag : "Error"; let comp = () => { switch (resultType) { diff --git a/packages/components/src/components/FunctionChart1Number.tsx b/packages/components/src/components/FunctionChart1Number.tsx index fa72bf71..4dae8cf0 100644 --- a/packages/components/src/components/FunctionChart1Number.tsx +++ b/packages/components/src/components/FunctionChart1Number.tsx @@ -51,10 +51,13 @@ interface FunctionChartProps { type point = { x: number; value: result }; let getFunctionImage = ({ chartSettings, fn, environment }) => { + //We adjust the count, because the count is made for distributions, which are much more expensive to estimate + let adjustedCount = chartSettings.count * 20; + let chartPointsToRender = _rangeByCount( chartSettings.start, chartSettings.stop, - chartSettings.count + adjustedCount ); let chartPointsData: point[] = chartPointsToRender.map((x) => { diff --git a/packages/components/src/components/SquiggleChart.tsx b/packages/components/src/components/SquiggleChart.tsx index 5cbebc8e..f07f5cc8 100644 --- a/packages/components/src/components/SquiggleChart.tsx +++ b/packages/components/src/components/SquiggleChart.tsx @@ -172,7 +172,7 @@ const SquiggleItem: React.FC = ({ = ({ } case "lambda": return ( - + + + ); } }; @@ -255,7 +257,7 @@ export const SquiggleChart: React.FC = ({ squiggleString = "", environment, onChange = () => {}, - height = 60, + height = 200, bindings = defaultBindings, jsImports = defaultImports, showSummary = false, diff --git a/packages/components/src/components/SquigglePlayground.tsx b/packages/components/src/components/SquigglePlayground.tsx index c28bf11a..1c7acd6f 100644 --- a/packages/components/src/components/SquigglePlayground.tsx +++ b/packages/components/src/components/SquigglePlayground.tsx @@ -78,7 +78,8 @@ interface RowProps { const Row = styled.div` display: grid; - grid-template-columns: ${(p) => p.leftPercentage}% ${(p) => 100 - p.leftPercentage}%; + grid-template-columns: ${(p) => p.leftPercentage}% ${(p) => + 100 - p.leftPercentage}%; `; const Col = styled.div``; @@ -115,10 +116,18 @@ const schema = yup .min(10) .max(10000), chartHeight: yup.number().required().positive().integer().default(350), - leftSize: yup.number().required().positive().integer().min(10).max(100).default(50), + leftSizePercent: yup + .number() + .required() + .positive() + .integer() + .min(10) + .max(100) + .default(50), showTypes: yup.boolean(), showControls: yup.boolean(), showSummary: yup.boolean(), + showSettingsPage: yup.boolean().default(false), }) .required(); @@ -133,9 +142,6 @@ let SquigglePlayground: FC = ({ let [importString, setImportString] = useState("{}"); let [imports, setImports] = useState({}); let [importsAreValid, setImportsAreValid] = useState(true); - let [showTypesInput, setShowTypesInput] = useState(showTypes); - let [showControlsInput, setShowControlsInput] = useState(showControls); - let [showSummaryInput, setShowSummaryInput] = useState(showSummary); let [diagramStart, setDiagramStart] = useState(0); let [diagramStop, setDiagramStop] = useState(10); let [diagramCount, setDiagramCount] = useState(20); @@ -157,7 +163,8 @@ let SquigglePlayground: FC = ({ showTypes: showTypes, showControls: showControls, showSummary: showSummary, - leftSize: 50, + leftSizePercent: 50, + showSettingsPage: false, }, }); const vars = useWatch({ @@ -178,30 +185,36 @@ let SquigglePlayground: FC = ({ }; return ( - - - - - - - - + + - - - {importsAreValid ? "Valid" : "INVALID"} + {vars.showSettingsPage ? ( +
+ + + + + + + + + {importsAreValid ? "Valid" : "Invalid"} +
+ ) : ( + + )} From f5474030ec0faecf7d7d41042d6a8e0a6952c6ec Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Thu, 26 May 2022 17:18:10 -0400 Subject: [PATCH 4/8] Added number function to SquiggleChart --- .../src/stories/SquiggleChart.stories.mdx | 18 ++++++++++++++++-- .../src/vega-specs/spec-distributions.json | 3 +++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/components/src/stories/SquiggleChart.stories.mdx b/packages/components/src/stories/SquiggleChart.stories.mdx index 9ad98ef0..bce22c1c 100644 --- a/packages/components/src/stories/SquiggleChart.stories.mdx +++ b/packages/components/src/stories/SquiggleChart.stories.mdx @@ -153,11 +153,11 @@ to allow large and small numbers being printed cleanly. -## Functions +## Functions (Distribution Output) +## Functions (Number Output) + + + + {Template.bind({})} + + + ## Records diff --git a/packages/components/src/vega-specs/spec-distributions.json b/packages/components/src/vega-specs/spec-distributions.json index 74559a03..29ca4e1d 100644 --- a/packages/components/src/vega-specs/spec-distributions.json +++ b/packages/components/src/vega-specs/spec-distributions.json @@ -82,6 +82,9 @@ "y2": { "scale": "yscale", "value": 0 + }, + "fill": { + "value": "#2f65a7" } } } From 30dc66db7c6bec2e4163dc46d452006852074cd7 Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Thu, 26 May 2022 17:21:17 -0400 Subject: [PATCH 5/8] Ran prettier --- packages/components/src/components/FunctionChart1Dist.tsx | 2 +- packages/components/src/components/FunctionChart1Number.tsx | 5 ++++- packages/components/src/components/JsonEditor.tsx | 5 +---- packages/components/src/components/SquigglePlayground.tsx | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/components/src/components/FunctionChart1Dist.tsx b/packages/components/src/components/FunctionChart1Dist.tsx index e1d8333c..2090af44 100644 --- a/packages/components/src/components/FunctionChart1Dist.tsx +++ b/packages/components/src/components/FunctionChart1Dist.tsx @@ -150,7 +150,7 @@ export const FunctionChart1Dist: React.FC = ({ fn, chartSettings, environment, - height + height, }: FunctionChartProps) => { let [mouseOverlay, setMouseOverlay] = React.useState(0); function handleHover(_name: string, value: unknown) { diff --git a/packages/components/src/components/FunctionChart1Number.tsx b/packages/components/src/components/FunctionChart1Number.tsx index 4dae8cf0..b07b9263 100644 --- a/packages/components/src/components/FunctionChart1Number.tsx +++ b/packages/components/src/components/FunctionChart1Number.tsx @@ -129,7 +129,10 @@ export const FunctionChart1Number: React.FC = ({ [environment, fn] ); - let data = getFunctionImageMemoized.functionImage.map(({x, value}) => ({x, y:value})) + let data = getFunctionImageMemoized.functionImage.map(({ x, value }) => ({ + x, + y: value, + })); return ( <> = ({ }; return ( - + {vars.showSettingsPage ? ( From 7f6fe1a0aad8ef48b8b4458f140c2f2ccc9b3e1a Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Thu, 26 May 2022 17:39:28 -0400 Subject: [PATCH 6/8] Minor component cleanup --- .../src/components/FunctionChart.tsx | 13 ++---- .../src/components/FunctionChart1Dist.tsx | 6 +-- .../src/components/FunctionChart1Number.tsx | 40 ++----------------- .../components/src/components/JsonEditor.tsx | 1 - 4 files changed, 10 insertions(+), 50 deletions(-) diff --git a/packages/components/src/components/FunctionChart.tsx b/packages/components/src/components/FunctionChart.tsx index bfe50684..bb5a9e24 100644 --- a/packages/components/src/components/FunctionChart.tsx +++ b/packages/components/src/components/FunctionChart.tsx @@ -1,12 +1,5 @@ import * as React from "react"; -import type { Spec } from "vega"; -import { - Distribution, - result, - lambdaValue, - environment, - runForeign, -} from "@quri/squiggle-lang"; +import { lambdaValue, environment, runForeign } from "@quri/squiggle-lang"; import { FunctionChart1Dist } from "./FunctionChart1Dist"; import { FunctionChart1Number } from "./FunctionChart1Number"; import { ErrorBox } from "./ErrorBox"; @@ -44,7 +37,7 @@ export const FunctionChart: React.FC = ({ let validResult = getValidResult(); let resultType = validResult.tag === "Ok" ? validResult.value.tag : "Error"; - let comp = () => { + let component = () => { switch (resultType) { case "distribution": return ( @@ -77,5 +70,5 @@ export const FunctionChart: React.FC = ({ } }; - return comp(); + return component(); }; diff --git a/packages/components/src/components/FunctionChart1Dist.tsx b/packages/components/src/components/FunctionChart1Dist.tsx index 2090af44..836bcdd5 100644 --- a/packages/components/src/components/FunctionChart1Dist.tsx +++ b/packages/components/src/components/FunctionChart1Dist.tsx @@ -41,7 +41,7 @@ export type FunctionChartSettings = { count: number; }; -interface FunctionChartProps { +interface FunctionChart1DistProps { fn: lambdaValue; chartSettings: FunctionChartSettings; environment: environment; @@ -146,12 +146,12 @@ let getPercentiles = ({ chartSettings, fn, environment }) => { return { percentiles, errors: groupedErrors }; }; -export const FunctionChart1Dist: React.FC = ({ +export const FunctionChart1Dist: React.FC = ({ fn, chartSettings, environment, height, -}: FunctionChartProps) => { +}: FunctionChart1DistProps) => { let [mouseOverlay, setMouseOverlay] = React.useState(0); function handleHover(_name: string, value: unknown) { setMouseOverlay(value as number); diff --git a/packages/components/src/components/FunctionChart1Number.tsx b/packages/components/src/components/FunctionChart1Number.tsx index b07b9263..9d2feca7 100644 --- a/packages/components/src/components/FunctionChart1Number.tsx +++ b/packages/components/src/components/FunctionChart1Number.tsx @@ -2,19 +2,14 @@ import * as React from "react"; import _ from "lodash"; import type { Spec } from "vega"; import { - Distribution, result, lambdaValue, environment, runForeign, - squiggleExpression, - errorValue, errorValueToString, } from "@quri/squiggle-lang"; import { createClassFromSpec } from "react-vega"; import * as lineChartSpec from "../vega-specs/spec-line-chart.json"; -import { DistributionChart } from "./DistributionChart"; -import { NumberShower } from "./NumberShower"; import { ErrorBox } from "./ErrorBox"; let SquiggleLineChart = createClassFromSpec({ @@ -28,20 +23,13 @@ const _rangeByCount = (start: number, stop: number, count: number) => { return result; }; -function unwrap(x: result): a { - if (x.tag === "Ok") { - return x.value; - } else { - throw Error("FAILURE TO UNWRAP"); - } -} export type FunctionChartSettings = { start: number; stop: number; count: number; }; -interface FunctionChartProps { +interface FunctionChart1NumberProps { fn: lambdaValue; chartSettings: FunctionChartSettings; environment: environment; @@ -70,8 +58,7 @@ let getFunctionImage = ({ chartSettings, fn, environment }) => { x, value: { tag: "Error", - value: - "Cannot currently render functions that don't return distributions", + value: "This component expected number outputs", }, }; } @@ -100,30 +87,12 @@ let getFunctionImage = ({ chartSettings, fn, environment }) => { return { errors, functionImage }; }; -export const FunctionChart1Number: React.FC = ({ +export const FunctionChart1Number: React.FC = ({ fn, chartSettings, environment, height, -}: FunctionChartProps) => { - let [mouseOverlay, setMouseOverlay] = React.useState(0); - function handleHover(_name: string, value: unknown) { - setMouseOverlay(value as number); - } - function handleOut() { - setMouseOverlay(NaN); - } - const signalListeners = { mousemove: handleHover, mouseout: handleOut }; - let mouseItem: result = !!mouseOverlay - ? runForeign(fn, [mouseOverlay], environment) - : { - tag: "Error", - value: { - tag: "REExpectedType", - value: "Hover x-coordinate returned NaN. Expected a number.", - }, - }; - +}: FunctionChart1NumberProps) => { let getFunctionImageMemoized = React.useMemo( () => getFunctionImage({ chartSettings, fn, environment }), [environment, fn] @@ -139,7 +108,6 @@ export const FunctionChart1Number: React.FC = ({ data={{ facet: data }} height={height} actions={false} - signalListeners={signalListeners} /> {getFunctionImageMemoized.errors.map(({ x, value }) => ( diff --git a/packages/components/src/components/JsonEditor.tsx b/packages/components/src/components/JsonEditor.tsx index 6d9068c1..c81debf4 100644 --- a/packages/components/src/components/JsonEditor.tsx +++ b/packages/components/src/components/JsonEditor.tsx @@ -4,7 +4,6 @@ import AceEditor from "react-ace"; import "ace-builds/src-noconflict/mode-json"; import "ace-builds/src-noconflict/theme-github"; -import { jsImports, defaultImports } from "@quri/squiggle-lang"; interface CodeEditorProps { value: string; From afb7613bcc1bbd2fb09e71f69a19a32dc09aecba Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Thu, 26 May 2022 18:39:06 -0400 Subject: [PATCH 7/8] Added simple input component for SquigglePlayground --- .../src/components/SquigglePlayground.tsx | 69 ++++++++++++++----- .../stories/SquigglePlayground.stories.mdx | 2 +- 2 files changed, 51 insertions(+), 20 deletions(-) diff --git a/packages/components/src/components/SquigglePlayground.tsx b/packages/components/src/components/SquigglePlayground.tsx index 3d550a5a..bb19dee0 100644 --- a/packages/components/src/components/SquigglePlayground.tsx +++ b/packages/components/src/components/SquigglePlayground.tsx @@ -11,7 +11,6 @@ import { yupResolver } from "@hookform/resolvers/yup"; import { defaultBindings, environment, - defaultImports, } from "@quri/squiggle-lang"; interface FieldFloatProps { @@ -131,9 +130,19 @@ const schema = yup }) .required(); +type InputProps = { + label: string; +}; +const InputItem: React.FC = ({ label, children }) => ( +
+ + {children} +
+); + let SquigglePlayground: FC = ({ initialSquiggleString = "", - height = 300, + height = 500, showTypes = false, showControls = false, showSummary = false, @@ -189,23 +198,45 @@ let SquigglePlayground: FC = ({ {vars.showSettingsPage ? ( -
- - - - - - - - - {importsAreValid ? "Valid" : "Invalid"} -
+ <> + + + + + + + + + + + + + + + + + + + + + + + + {importsAreValid ? "Valid" : "Invalid"} + + ) : ( {Template.bind({})} From 8482a51332a192be059ed6d614eb9293dd24746b Mon Sep 17 00:00:00 2001 From: Ozzie Gooen Date: Fri, 27 May 2022 07:23:17 -0400 Subject: [PATCH 8/8] Minor fixes to get build to pass --- .../src/components/SquigglePlayground.tsx | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/packages/components/src/components/SquigglePlayground.tsx b/packages/components/src/components/SquigglePlayground.tsx index bb19dee0..921a3282 100644 --- a/packages/components/src/components/SquigglePlayground.tsx +++ b/packages/components/src/components/SquigglePlayground.tsx @@ -8,10 +8,7 @@ import styled from "styled-components"; import { useForm, useWatch } from "react-hook-form"; import * as yup from "yup"; import { yupResolver } from "@hookform/resolvers/yup"; -import { - defaultBindings, - environment, -} from "@quri/squiggle-lang"; +import { defaultBindings, environment } from "@quri/squiggle-lang"; interface FieldFloatProps { label: string; @@ -132,7 +129,9 @@ const schema = yup type InputProps = { label: string; + children: ReactElement; }; + const InputItem: React.FC = ({ label, children }) => (
@@ -227,14 +226,16 @@ let SquigglePlayground: FC = ({ /> - - {importsAreValid ? "Valid" : "Invalid"} + <> + + {importsAreValid ? "Valid" : "Invalid"} + ) : (