Merge pull request #622 from quantified-uncertainty/May-tailwind-refactor
This commit is contained in:
commit
7f3cacae59
|
@ -3,6 +3,8 @@
|
|||
"version": "0.2.20",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@headlessui/react": "^1.6.4",
|
||||
"@heroicons/react": "^1.0.6",
|
||||
"@hookform/resolvers": "^2.8.10",
|
||||
"@quri/squiggle-lang": "^0.2.8",
|
||||
"@react-hook/size": "^2.1.2",
|
||||
|
@ -28,6 +30,7 @@
|
|||
"@storybook/node-logger": "^6.5.6",
|
||||
"@storybook/preset-create-react-app": "^4.1.1",
|
||||
"@storybook/react": "^6.5.6",
|
||||
"@tailwindcss/forms": "^0.5.2",
|
||||
"@testing-library/jest-dom": "^5.16.4",
|
||||
"@testing-library/react": "^13.3.0",
|
||||
"@testing-library/user-event": "^14.2.0",
|
||||
|
|
83
packages/components/src/components/Alert.tsx
Normal file
83
packages/components/src/components/Alert.tsx
Normal file
|
@ -0,0 +1,83 @@
|
|||
import * as React from "react";
|
||||
import {
|
||||
XCircleIcon,
|
||||
InformationCircleIcon,
|
||||
CheckCircleIcon,
|
||||
} from "@heroicons/react/solid";
|
||||
|
||||
export const Alert: React.FC<{
|
||||
heading: string;
|
||||
backgroundColor: string;
|
||||
headingColor: string;
|
||||
bodyColor: string;
|
||||
icon: React.ReactNode;
|
||||
children: React.ReactNode;
|
||||
}> = ({
|
||||
heading = "Error",
|
||||
backgroundColor,
|
||||
headingColor,
|
||||
bodyColor,
|
||||
icon,
|
||||
children,
|
||||
}) => {
|
||||
return (
|
||||
<div className={`rounded-md p-4 ${backgroundColor}`}>
|
||||
<div className="flex">
|
||||
<div className="flex-shrink-0">{icon}</div>
|
||||
<div className="ml-3">
|
||||
<h3 className={`text-sm font-medium ${headingColor}`}>{heading}</h3>
|
||||
<div className={`mt-2 text-sm ${bodyColor}`}>{children}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const ErrorAlert: React.FC<{
|
||||
heading: string;
|
||||
children: React.ReactNode;
|
||||
}> = ({ heading = "Error", children }) => (
|
||||
<Alert
|
||||
heading={heading}
|
||||
children={children}
|
||||
backgroundColor="bg-red-100"
|
||||
headingColor="text-red-800"
|
||||
bodyColor="text-red-700"
|
||||
icon={<XCircleIcon className="h-5 w-5 text-red-400" aria-hidden="true" />}
|
||||
/>
|
||||
);
|
||||
|
||||
export const MessageAlert: React.FC<{
|
||||
heading: string;
|
||||
children: React.ReactNode;
|
||||
}> = ({ heading = "Error", children }) => (
|
||||
<Alert
|
||||
heading={heading}
|
||||
children={children}
|
||||
backgroundColor="bg-slate-100"
|
||||
headingColor="text-slate-700"
|
||||
bodyColor="text-slate-700"
|
||||
icon={
|
||||
<InformationCircleIcon
|
||||
className="h-5 w-5 text-slate-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
|
||||
export const SuccessAlert: React.FC<{
|
||||
heading: string;
|
||||
children: React.ReactNode;
|
||||
}> = ({ heading = "Error", children }) => (
|
||||
<Alert
|
||||
heading={heading}
|
||||
children={children}
|
||||
backgroundColor="bg-green-50"
|
||||
headingColor="text-green-800"
|
||||
bodyColor="text-green-700"
|
||||
icon={
|
||||
<CheckCircleIcon className="h-5 w-5 text-green-400" aria-hidden="true" />
|
||||
}
|
||||
/>
|
||||
);
|
|
@ -29,6 +29,7 @@ export let CodeEditor: FC<CodeEditorProps> = ({
|
|||
mode="golang"
|
||||
theme="github"
|
||||
width={"100%"}
|
||||
fontSize={14}
|
||||
height={String(height) + "px"}
|
||||
minLines={oneLine ? lineCount : undefined}
|
||||
maxLines={oneLine ? lineCount : undefined}
|
||||
|
|
|
@ -8,7 +8,7 @@ import {
|
|||
} from "@quri/squiggle-lang";
|
||||
import { Vega, VisualizationSpec } from "react-vega";
|
||||
import * as chartSpecification from "../vega-specs/spec-distributions.json";
|
||||
import { ErrorBox } from "./ErrorBox";
|
||||
import { ErrorAlert } from "./Alert";
|
||||
import { useSize } from "react-use";
|
||||
import {
|
||||
linearXScale,
|
||||
|
@ -45,6 +45,12 @@ export const DistributionChart: React.FC<DistributionChartProps> = ({
|
|||
shape.value.discrete.some((x) => x.x <= 0);
|
||||
let spec = buildVegaSpec(isLogX, isExpY);
|
||||
let widthProp = width ? width : size.width;
|
||||
if (widthProp < 20) {
|
||||
console.warn(
|
||||
`Width of Distribution is set to ${widthProp}, which is too small`
|
||||
);
|
||||
widthProp = 20;
|
||||
}
|
||||
|
||||
// Check whether we should disable the checkbox
|
||||
var logCheckbox = (
|
||||
|
@ -86,9 +92,9 @@ export const DistributionChart: React.FC<DistributionChartProps> = ({
|
|||
);
|
||||
} else {
|
||||
var result = (
|
||||
<ErrorBox heading="Distribution Error">
|
||||
<ErrorAlert heading="Distribution Error">
|
||||
{distributionErrorToString(shape.value)}
|
||||
</ErrorBox>
|
||||
</ErrorAlert>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -157,18 +163,24 @@ const SummaryTable: React.FC<SummaryTableProps> = ({
|
|||
return <NumberShower number={x.value} />;
|
||||
} else {
|
||||
return (
|
||||
<ErrorBox heading="Distribution Error">
|
||||
<ErrorAlert heading="Distribution Error">
|
||||
{distributionErrorToString(x.value)}
|
||||
</ErrorBox>
|
||||
</ErrorAlert>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let TableHeadCell: React.FC<{ children: React.ReactNode }> = ({
|
||||
children,
|
||||
}) => <th className="border border-slate-400 bg-slate-50 p-4">{children}</th>;
|
||||
}) => (
|
||||
<th className="border border-slate-200 bg-slate-50 py-1 px-2 text-slate-500 font-semibold">
|
||||
{children}
|
||||
</th>
|
||||
);
|
||||
let Cell: React.FC<{ children: React.ReactNode }> = ({ children }) => (
|
||||
<td className="border border-slate-400 p-4">{children}</td>
|
||||
<td className="border border-slate-200 py-1 px-2 text-slate-900 ">
|
||||
{children}
|
||||
</td>
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
import * as React from "react";
|
||||
export const ErrorBox: React.FC<{
|
||||
heading: string;
|
||||
children: React.ReactNode;
|
||||
}> = ({ heading = "Error", children }) => {
|
||||
return (
|
||||
<div className="border border-red-200 bg-gray-50 p-4">
|
||||
<h3>{heading}</h3>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -2,7 +2,7 @@ import * as React from "react";
|
|||
import { lambdaValue, environment, runForeign } from "@quri/squiggle-lang";
|
||||
import { FunctionChart1Dist } from "./FunctionChart1Dist";
|
||||
import { FunctionChart1Number } from "./FunctionChart1Number";
|
||||
import { ErrorBox } from "./ErrorBox";
|
||||
import { ErrorAlert, MessageAlert } from "./Alert";
|
||||
|
||||
export type FunctionChartSettings = {
|
||||
start: number;
|
||||
|
@ -23,9 +23,16 @@ export const FunctionChart: React.FC<FunctionChartProps> = ({
|
|||
environment,
|
||||
height,
|
||||
}: FunctionChartProps) => {
|
||||
let result1 = runForeign(fn, [chartSettings.start], environment);
|
||||
let result2 = runForeign(fn, [chartSettings.stop], environment);
|
||||
let getValidResult = () => {
|
||||
if (fn.parameters.length > 1) {
|
||||
return (
|
||||
<MessageAlert heading="Function Display Not Supported">
|
||||
Only functions with one parameter are displayed.
|
||||
</MessageAlert>
|
||||
);
|
||||
}
|
||||
const result1 = runForeign(fn, [chartSettings.start], environment);
|
||||
const result2 = runForeign(fn, [chartSettings.stop], environment);
|
||||
const getValidResult = () => {
|
||||
if (result1.tag === "Ok") {
|
||||
return result1;
|
||||
} else if (result2.tag === "Ok") {
|
||||
|
@ -34,10 +41,10 @@ export const FunctionChart: React.FC<FunctionChartProps> = ({
|
|||
return result1;
|
||||
}
|
||||
};
|
||||
let validResult = getValidResult();
|
||||
let resultType = validResult.tag === "Ok" ? validResult.value.tag : "Error";
|
||||
const validResult = getValidResult();
|
||||
const resultType =
|
||||
validResult.tag === "Ok" ? validResult.value.tag : ("Error" as const);
|
||||
|
||||
let component = () => {
|
||||
switch (resultType) {
|
||||
case "distribution":
|
||||
return (
|
||||
|
@ -59,16 +66,14 @@ export const FunctionChart: React.FC<FunctionChartProps> = ({
|
|||
);
|
||||
case "Error":
|
||||
return (
|
||||
<ErrorBox heading="Error">The function failed to be run</ErrorBox>
|
||||
<ErrorAlert heading="Error">The function failed to be run</ErrorAlert>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<ErrorBox heading="No Viewer">
|
||||
There is no function visualization for this type of function
|
||||
</ErrorBox>
|
||||
<MessageAlert heading="Function Display Not Supported">
|
||||
There is no function visualization for this type of output:{" "}
|
||||
<span className="font-bold">{resultType}</span>
|
||||
</MessageAlert>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return component();
|
||||
};
|
||||
|
|
|
@ -15,7 +15,7 @@ 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";
|
||||
import { ErrorAlert } from "./Alert";
|
||||
|
||||
let SquigglePercentilesChart = createClassFromSpec({
|
||||
spec: percentilesSpec as Spec,
|
||||
|
@ -197,7 +197,7 @@ export const FunctionChart1Dist: React.FC<FunctionChart1DistProps> = ({
|
|||
{showChart}
|
||||
{_.entries(getPercentilesMemoized.errors).map(
|
||||
([errorName, errorPoints]) => (
|
||||
<ErrorBox key={errorName} heading={errorName}>
|
||||
<ErrorAlert key={errorName} heading={errorName}>
|
||||
Values:{" "}
|
||||
{errorPoints
|
||||
.map((r, i) => <NumberShower key={i} number={r.x} />)
|
||||
|
@ -206,7 +206,7 @@ export const FunctionChart1Dist: React.FC<FunctionChart1DistProps> = ({
|
|||
{a}, {b}
|
||||
</>
|
||||
))}
|
||||
</ErrorBox>
|
||||
</ErrorAlert>
|
||||
)
|
||||
)}
|
||||
</>
|
||||
|
|
|
@ -10,7 +10,7 @@ import {
|
|||
} from "@quri/squiggle-lang";
|
||||
import { createClassFromSpec } from "react-vega";
|
||||
import * as lineChartSpec from "../vega-specs/spec-line-chart.json";
|
||||
import { ErrorBox } from "./ErrorBox";
|
||||
import { ErrorAlert } from "./Alert";
|
||||
|
||||
let SquiggleLineChart = createClassFromSpec({
|
||||
spec: lineChartSpec as Spec,
|
||||
|
@ -110,9 +110,9 @@ export const FunctionChart1Number: React.FC<FunctionChart1NumberProps> = ({
|
|||
actions={false}
|
||||
/>
|
||||
{getFunctionImageMemoized.errors.map(({ x, value }) => (
|
||||
<ErrorBox key={x} heading={value}>
|
||||
<ErrorAlert key={x} heading={value}>
|
||||
Error at point ${x}
|
||||
</ErrorBox>
|
||||
</ErrorAlert>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -14,7 +14,7 @@ import {
|
|||
} from "@quri/squiggle-lang";
|
||||
import { NumberShower } from "./NumberShower";
|
||||
import { DistributionChart } from "./DistributionChart";
|
||||
import { ErrorBox } from "./ErrorBox";
|
||||
import { ErrorAlert } from "./Alert";
|
||||
import { FunctionChart, FunctionChartSettings } from "./FunctionChart";
|
||||
|
||||
function getRange<a>(x: declaration<a>) {
|
||||
|
@ -54,7 +54,7 @@ export const VariableBox: React.FC<VariableBoxProps> = ({
|
|||
return (
|
||||
<div className="bg-white border border-grey-200 m-2">
|
||||
<div className="border-b border-grey-200 p-3">
|
||||
<h3>{heading}</h3>
|
||||
<h3 className="font-mono">{heading}</h3>
|
||||
</div>
|
||||
<div className="p-3">{children}</div>
|
||||
</div>
|
||||
|
@ -95,7 +95,9 @@ const SquiggleItem: React.FC<SquiggleItemProps> = ({
|
|||
case "number":
|
||||
return (
|
||||
<VariableBox heading="Number" showTypes={showTypes}>
|
||||
<div className="font-semibold text-slate-600">
|
||||
<NumberShower precision={3} number={expression.value} />
|
||||
</div>
|
||||
</VariableBox>
|
||||
);
|
||||
case "distribution": {
|
||||
|
@ -124,10 +126,13 @@ const SquiggleItem: React.FC<SquiggleItemProps> = ({
|
|||
}
|
||||
case "string":
|
||||
return (
|
||||
<VariableBox
|
||||
heading="String"
|
||||
showTypes={showTypes}
|
||||
>{`"${expression.value}"`}</VariableBox>
|
||||
<VariableBox heading="String" showTypes={showTypes}>
|
||||
<span className="text-slate-400">"</span>
|
||||
<span className="text-slate-600 font-semibold">
|
||||
{expression.value}
|
||||
</span>
|
||||
<span className="text-slate-400">"</span>
|
||||
</VariableBox>
|
||||
);
|
||||
case "boolean":
|
||||
return (
|
||||
|
@ -138,7 +143,8 @@ const SquiggleItem: React.FC<SquiggleItemProps> = ({
|
|||
case "symbol":
|
||||
return (
|
||||
<VariableBox heading="Symbol" showTypes={showTypes}>
|
||||
{expression.value}
|
||||
<span className="text-slate-500 mr-2">Undefined Symbol:</span>
|
||||
<span className="text-slate-600">{expression.value}</span>
|
||||
</VariableBox>
|
||||
);
|
||||
case "call":
|
||||
|
@ -151,6 +157,11 @@ const SquiggleItem: React.FC<SquiggleItemProps> = ({
|
|||
return (
|
||||
<VariableBox heading="Array" showTypes={showTypes}>
|
||||
{expression.value.map((r, i) => (
|
||||
<div key={i} className="flex flex-row pt-1">
|
||||
<div className="flex-none bg-slate-100 rounded-sm px-1">
|
||||
<h3 className="text-slate-400 font-mono">{i}</h3>
|
||||
</div>
|
||||
<div className="px-2 mb-2 grow ">
|
||||
<SquiggleItem
|
||||
key={i}
|
||||
expression={r}
|
||||
|
@ -162,6 +173,8 @@ const SquiggleItem: React.FC<SquiggleItemProps> = ({
|
|||
environment={environment}
|
||||
showSummary={showSummary}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</VariableBox>
|
||||
);
|
||||
|
@ -169,8 +182,11 @@ const SquiggleItem: React.FC<SquiggleItemProps> = ({
|
|||
return (
|
||||
<VariableBox heading="Record" showTypes={showTypes}>
|
||||
{Object.entries(expression.value).map(([key, r]) => (
|
||||
<div key={key}>
|
||||
<h3>{key}</h3>
|
||||
<div key={key} className="flex flex-row pt-1">
|
||||
<div className="flex-none pr-2">
|
||||
<h3 className="text-slate-500 font-mono">{key}:</h3>
|
||||
</div>
|
||||
<div className="pl-2 pr-2 mb-2 grow bg-gray-50 border border-gray-100 rounded-sm">
|
||||
<SquiggleItem
|
||||
expression={r}
|
||||
width={width !== undefined ? width - 20 : width}
|
||||
|
@ -182,6 +198,7 @@ const SquiggleItem: React.FC<SquiggleItemProps> = ({
|
|||
environment={environment}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</VariableBox>
|
||||
);
|
||||
|
@ -207,6 +224,9 @@ const SquiggleItem: React.FC<SquiggleItemProps> = ({
|
|||
case "lambda":
|
||||
return (
|
||||
<VariableBox heading="Function" showTypes={showTypes}>
|
||||
<div className="text-amber-700 bg-amber-100 rounded-md font-mono p-1 pl-2 mb-3 mt-1 text-sm">{`function(${expression.value.parameters.join(
|
||||
","
|
||||
)})`}</div>
|
||||
<FunctionChart
|
||||
fn={expression.value}
|
||||
chartSettings={chartSettings}
|
||||
|
@ -300,9 +320,9 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = ({
|
|||
);
|
||||
} else {
|
||||
internal = (
|
||||
<ErrorBox heading={"Parse Error"}>
|
||||
<ErrorAlert heading={"Parse Error"}>
|
||||
{errorValueToString(expressionResult.value)}
|
||||
</ErrorBox>
|
||||
</ErrorAlert>
|
||||
);
|
||||
}
|
||||
return internal;
|
||||
|
|
|
@ -14,7 +14,7 @@ import {
|
|||
defaultImports,
|
||||
defaultBindings,
|
||||
} from "@quri/squiggle-lang";
|
||||
import { ErrorBox } from "./ErrorBox";
|
||||
import { ErrorAlert } from "./Alert";
|
||||
|
||||
export interface SquiggleEditorProps {
|
||||
/** The input string for squiggle */
|
||||
|
@ -181,7 +181,11 @@ export let SquigglePartial: React.FC<SquigglePartialProps> = ({
|
|||
height={20}
|
||||
/>
|
||||
</div>
|
||||
{error !== null ? <ErrorBox heading="Error">{error}</ErrorBox> : <></>}
|
||||
{error !== null ? (
|
||||
<ErrorAlert heading="Error">{error}</ErrorAlert>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -8,31 +8,13 @@ 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";
|
||||
|
||||
interface ShowBoxProps {
|
||||
height: number;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const ShowBox: React.FC<ShowBoxProps> = ({ height, children }) => (
|
||||
<div className="border border-grey-100" style={{ height: height + "px" }}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
interface TitleProps {
|
||||
readonly maxHeight: number;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const Display: React.FC<TitleProps> = ({ maxHeight, children }) => (
|
||||
<div
|
||||
className="bg-gray-50 border-l border-grey-100 p-2"
|
||||
style={{ maxHeight: maxHeight + "px" }}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
import { Tab } from "@headlessui/react";
|
||||
import { CodeIcon } from "@heroicons/react/solid";
|
||||
import { CogIcon } from "@heroicons/react/solid";
|
||||
import { ChartSquareBarIcon } from "@heroicons/react/solid";
|
||||
import { CurrencyDollarIcon } from "@heroicons/react/solid";
|
||||
import { Fragment } from "react";
|
||||
import { ErrorAlert, SuccessAlert } from "./Alert";
|
||||
|
||||
interface PlaygroundProps {
|
||||
/** The initial squiggle string to put in the playground */
|
||||
|
@ -79,6 +61,27 @@ const schema = yup
|
|||
showControls: yup.boolean(),
|
||||
showSummary: yup.boolean(),
|
||||
showSettingsPage: yup.boolean().default(false),
|
||||
diagramStart: yup
|
||||
.number()
|
||||
.required()
|
||||
.positive()
|
||||
.integer()
|
||||
.default(0)
|
||||
.min(0),
|
||||
diagramStop: yup
|
||||
.number()
|
||||
.required()
|
||||
.positive()
|
||||
.integer()
|
||||
.default(10)
|
||||
.min(0),
|
||||
diagramCount: yup
|
||||
.number()
|
||||
.required()
|
||||
.positive()
|
||||
.integer()
|
||||
.default(20)
|
||||
.min(2),
|
||||
})
|
||||
.required();
|
||||
|
||||
|
@ -88,12 +91,68 @@ type InputProps = {
|
|||
};
|
||||
|
||||
const InputItem: React.FC<InputProps> = ({ label, children }) => (
|
||||
<div>
|
||||
<label>{label}</label>
|
||||
{children}
|
||||
<div className="col-span-4">
|
||||
<label className="block text-sm font-medium text-gray-600">{label}</label>
|
||||
<div className="mt-1">{children}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
let numberStyle =
|
||||
"max-w-lg block w-full shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:max-w-xs sm:text-sm border-gray-300 rounded-md";
|
||||
|
||||
function classNames(...classes: string[]) {
|
||||
return classes.filter(Boolean).join(" ");
|
||||
}
|
||||
|
||||
type StyledTabProps = {
|
||||
name: string;
|
||||
iconName: string;
|
||||
};
|
||||
|
||||
const StyledTab: React.FC<StyledTabProps> = ({ name, iconName }) => {
|
||||
let iconStyle = (isSelected: boolean) =>
|
||||
classNames(
|
||||
"-ml-0.5 mr-2 h-4 w-4 ",
|
||||
isSelected ? "text-slate-500" : "text-gray-400 group-hover:text-gray-900"
|
||||
);
|
||||
|
||||
let icon = (selected: boolean) =>
|
||||
({
|
||||
code: <CodeIcon className={iconStyle(selected)} />,
|
||||
cog: <CogIcon className={iconStyle(selected)} />,
|
||||
squareBar: <ChartSquareBarIcon className={iconStyle(selected)} />,
|
||||
dollar: <CurrencyDollarIcon className={iconStyle(selected)} />,
|
||||
}[iconName]);
|
||||
|
||||
return (
|
||||
<Tab key={name} as={Fragment}>
|
||||
{({ selected }) => (
|
||||
<button className="flex rounded-md focus:outline-none focus-visible:ring-offset-gray-100 ">
|
||||
<span
|
||||
className={classNames(
|
||||
"p-1 pl-2.5 pr-3.5 rounded-md flex items-center text-sm font-medium",
|
||||
selected
|
||||
? "bg-white shadow-sm ring-1 ring-black ring-opacity-5"
|
||||
: ""
|
||||
)}
|
||||
>
|
||||
{icon(selected)}
|
||||
<span
|
||||
className={
|
||||
selected
|
||||
? "text-gray-900"
|
||||
: "text-gray-600 group-hover:text-gray-900"
|
||||
}
|
||||
>
|
||||
{name}
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
</Tab>
|
||||
);
|
||||
};
|
||||
|
||||
let SquigglePlayground: FC<PlaygroundProps> = ({
|
||||
initialSquiggleString = "",
|
||||
height = 500,
|
||||
|
@ -105,14 +164,6 @@ let SquigglePlayground: FC<PlaygroundProps> = ({
|
|||
let [importString, setImportString] = useState("{}");
|
||||
let [imports, setImports] = useState({});
|
||||
let [importsAreValid, setImportsAreValid] = useState(true);
|
||||
let [diagramStart, setDiagramStart] = useState(0);
|
||||
let [diagramStop, setDiagramStop] = useState(10);
|
||||
let [diagramCount, setDiagramCount] = useState(20);
|
||||
let chartSettings = {
|
||||
start: diagramStart,
|
||||
stop: diagramStop,
|
||||
count: diagramCount,
|
||||
};
|
||||
const { register, control } = useForm({
|
||||
resolver: yupResolver(schema),
|
||||
defaultValues: {
|
||||
|
@ -124,11 +175,19 @@ let SquigglePlayground: FC<PlaygroundProps> = ({
|
|||
showSummary: showSummary,
|
||||
leftSizePercent: 50,
|
||||
showSettingsPage: false,
|
||||
diagramStart: 0,
|
||||
diagramStop: 10,
|
||||
diagramCount: 20,
|
||||
},
|
||||
});
|
||||
const vars = useWatch({
|
||||
control,
|
||||
});
|
||||
let chartSettings = {
|
||||
start: Number(vars.diagramStart),
|
||||
stop: Number(vars.diagramStop),
|
||||
count: Number(vars.diagramCount),
|
||||
};
|
||||
let env: environment = {
|
||||
sampleCount: Number(vars.sampleCount),
|
||||
xyPointLength: Number(vars.xyPointLength),
|
||||
|
@ -142,65 +201,206 @@ let SquigglePlayground: FC<PlaygroundProps> = ({
|
|||
setImportsAreValid(false);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<ShowBox height={height}>
|
||||
<input type="checkbox" {...register("showSettingsPage")} />
|
||||
<div className="columns-2">
|
||||
<div className="break-inside-avoid">
|
||||
{vars.showSettingsPage ? (
|
||||
<>
|
||||
|
||||
let samplingSettings = (
|
||||
<div className="space-y-6 p-3 max-w-xl">
|
||||
<InputItem label="Sample Count">
|
||||
<input type="number" {...register("sampleCount")} />
|
||||
</InputItem>
|
||||
<InputItem label="XYPointLength Count">
|
||||
<input type="number" {...register("xyPointLength")} />
|
||||
</InputItem>
|
||||
<InputItem label="Chart Height (in Pixels)">
|
||||
<input type="number" {...register("chartHeight")} />
|
||||
</InputItem>
|
||||
<InputItem label="Show Types">
|
||||
<input type="checkbox" {...register("showTypes")} />
|
||||
</InputItem>
|
||||
<InputItem label="Show Controls">
|
||||
<input type="checkbox" {...register("showControls")} />
|
||||
</InputItem>
|
||||
<InputItem label="Show Summary Statistics">
|
||||
<input type="checkbox" {...register("showSummary")} />
|
||||
</InputItem>
|
||||
<InputItem label="Editor Width">
|
||||
<>
|
||||
<input
|
||||
type="range"
|
||||
min="1"
|
||||
max="100"
|
||||
className="slider"
|
||||
{...register("leftSizePercent")}
|
||||
type="number"
|
||||
{...register("sampleCount")}
|
||||
className={numberStyle}
|
||||
/>
|
||||
<p className="mt-2 text-sm text-gray-500">
|
||||
How many samples to use for Monte Carlo simulations. This can
|
||||
occasionally be overridden by specific Squiggle programs.
|
||||
</p>
|
||||
</>
|
||||
</InputItem>
|
||||
<InputItem label="Coordinate Count (For PointSet Shapes)">
|
||||
<>
|
||||
<input
|
||||
type="number"
|
||||
{...register("xyPointLength")}
|
||||
className={numberStyle}
|
||||
/>
|
||||
<p className="mt-2 text-sm text-gray-500">
|
||||
When distributions are converted into PointSet shapes, we need to
|
||||
know how many coordinates to use.
|
||||
</p>
|
||||
</>
|
||||
</InputItem>
|
||||
</div>
|
||||
);
|
||||
|
||||
let viewSettings = (
|
||||
<div className="space-y-6 p-3 divide-y divide-gray-200 max-w-xl">
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900 pb-2">
|
||||
General Display Settings
|
||||
</h3>
|
||||
<InputItem label="Chart Height (in pixels)">
|
||||
<input
|
||||
type="number"
|
||||
{...register("chartHeight")}
|
||||
className={numberStyle}
|
||||
/>
|
||||
</InputItem>
|
||||
<InputItem label="Json Editor for imports">
|
||||
<>
|
||||
<div className="relative flex items-start pt-3">
|
||||
<div className="flex items-center h-5">
|
||||
<input
|
||||
type="checkbox"
|
||||
{...register("showTypes")}
|
||||
className="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
|
||||
/>
|
||||
</div>
|
||||
<div className="ml-3 text-sm">
|
||||
<label className="font-medium text-gray-700">
|
||||
Show information about displayed types.
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2 pt-8">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900 pb-2">
|
||||
Distribution Display Settings
|
||||
</h3>
|
||||
|
||||
<div className="relative flex items-start">
|
||||
<div className="flex items-center h-5">
|
||||
<input
|
||||
type="checkbox"
|
||||
{...register("showControls")}
|
||||
className="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
|
||||
/>
|
||||
</div>
|
||||
<div className="ml-3 text-sm">
|
||||
<label className="font-medium text-gray-700">
|
||||
Show toggles to adjust scale of x and y axes
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative flex items-start">
|
||||
<div className="flex items-center h-5">
|
||||
<input
|
||||
type="checkbox"
|
||||
{...register("showSummary")}
|
||||
className="focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
|
||||
/>
|
||||
</div>
|
||||
<div className="ml-3 text-sm">
|
||||
<label className="font-medium text-gray-700">
|
||||
Show summary statistics
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2 pt-8">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900 pb-2">
|
||||
Function Display Settings
|
||||
</h3>
|
||||
|
||||
<p className="mt-2 text-sm text-gray-500">
|
||||
When displaying functions of single variables that return numbers or
|
||||
distributions, we need to use defaults for the x-axis. We need to
|
||||
select a minimum and maximum value of x to sample, and a number n of
|
||||
the number of points to sample.
|
||||
</p>
|
||||
<div className="pt-4 grid grid-cols-1 gap-y-4 gap-x-4">
|
||||
<InputItem label="Min X Value">
|
||||
<input
|
||||
type="number"
|
||||
{...register("diagramStart")}
|
||||
className={numberStyle}
|
||||
/>
|
||||
</InputItem>
|
||||
<InputItem label="Max X Value">
|
||||
<input
|
||||
type="number"
|
||||
{...register("diagramStop")}
|
||||
className={numberStyle}
|
||||
/>
|
||||
</InputItem>
|
||||
<InputItem label="Points between X min and X max to sample">
|
||||
<input
|
||||
type="number"
|
||||
{...register("diagramCount")}
|
||||
className={numberStyle}
|
||||
/>
|
||||
</InputItem>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
let inputVariableSettings = (
|
||||
<div className="space-y-6 p-3 max-w-3xl">
|
||||
<h3 className="text-lg leading-6 font-medium text-gray-900">
|
||||
Import Variables from JSON
|
||||
</h3>
|
||||
<p className="mt-2 text-sm text-gray-500">
|
||||
You can import variables from JSON into your Squiggle code. Variables
|
||||
are accessed with dollar signs. For example, "timeNow" would be accessed
|
||||
as "$timeNow".
|
||||
</p>
|
||||
<div className="border border-slate-200 mt-6 mb-2">
|
||||
<JsonEditor
|
||||
value={importString}
|
||||
onChange={getChangeJson}
|
||||
oneLine={false}
|
||||
showGutter={true}
|
||||
height={100}
|
||||
height={150}
|
||||
/>
|
||||
{importsAreValid ? "Valid" : "Invalid"}
|
||||
</>
|
||||
</InputItem>
|
||||
</>
|
||||
</div>
|
||||
<div className="p-1 pt-2">
|
||||
{importsAreValid ? (
|
||||
<SuccessAlert heading="Valid Json">
|
||||
<></>
|
||||
</SuccessAlert>
|
||||
) : (
|
||||
<ErrorAlert heading="Invalid JSON">
|
||||
You must use valid json in this editor.
|
||||
</ErrorAlert>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Tab.Group>
|
||||
<div className=" flex-col flex">
|
||||
<div className="pb-4">
|
||||
<Tab.List className="p-0.5 rounded-md bg-slate-100 hover:bg-slate-200 inline-flex">
|
||||
<StyledTab name="Code" iconName="code" />
|
||||
<StyledTab name="Sampling Settings" iconName="cog" />
|
||||
<StyledTab name="View Settings" iconName="squareBar" />
|
||||
<StyledTab name="Input Variables" iconName="dollar" />
|
||||
</Tab.List>
|
||||
</div>
|
||||
<div className="flex" style={{ height: height + "px" }}>
|
||||
<div className="w-1/2">
|
||||
<Tab.Panels>
|
||||
<Tab.Panel>
|
||||
<div className="border border-slate-200">
|
||||
<CodeEditor
|
||||
value={squiggleString}
|
||||
onChange={setSquiggleString}
|
||||
oneLine={false}
|
||||
showGutter={true}
|
||||
height={height - 3}
|
||||
height={height - 1}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<Display maxHeight={height - 3}>
|
||||
</Tab.Panel>
|
||||
<Tab.Panel>{samplingSettings}</Tab.Panel>
|
||||
<Tab.Panel>{viewSettings}</Tab.Panel>
|
||||
<Tab.Panel>{inputVariableSettings}</Tab.Panel>
|
||||
</Tab.Panels>
|
||||
</div>
|
||||
|
||||
<div className="w-1/2 p-2 pl-4">
|
||||
<div style={{ maxHeight: height + "px" }}>
|
||||
<SquiggleChart
|
||||
squiggleString={squiggleString}
|
||||
environment={env}
|
||||
|
@ -212,10 +412,11 @@ let SquigglePlayground: FC<PlaygroundProps> = ({
|
|||
jsImports={imports}
|
||||
showSummary={vars.showSummary}
|
||||
/>
|
||||
</Display>
|
||||
</div>
|
||||
</div>
|
||||
</ShowBox>
|
||||
</div>
|
||||
</div>
|
||||
</Tab.Group>
|
||||
);
|
||||
};
|
||||
export default SquigglePlayground;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@tailwind forms;
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
"value": 0
|
||||
},
|
||||
"fill": {
|
||||
"value": "#4C78A8"
|
||||
"value": "#739ECC"
|
||||
},
|
||||
"interpolate": {
|
||||
"value": "linear"
|
||||
|
|
|
@ -3,5 +3,5 @@ module.exports = {
|
|||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
plugins: [require("@tailwindcss/forms")],
|
||||
};
|
||||
|
|
22
yarn.lock
22
yarn.lock
|
@ -1842,6 +1842,16 @@
|
|||
dependencies:
|
||||
"@hapi/hoek" "^9.0.0"
|
||||
|
||||
"@headlessui/react@^1.6.4":
|
||||
version "1.6.4"
|
||||
resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-1.6.4.tgz#c73084e23386bef5fb86cd16da3352c3a844bb4c"
|
||||
integrity sha512-0yqz1scwbFtwljmbbKjXsSGl5ABEYNICVHZnMCWo0UtOZodo2Tpu94uOVgCRjRZ77l2WcTi2S0uidINDvG7lsA==
|
||||
|
||||
"@heroicons/react@^1.0.6":
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@heroicons/react/-/react-1.0.6.tgz#35dd26987228b39ef2316db3b1245c42eb19e324"
|
||||
integrity sha512-JJCXydOFWMDpCP4q13iEplA503MQO3xLoZiKum+955ZCtHINWnx26CUxVxxFQu/uLb4LW3ge15ZpzIkXKkJ8oQ==
|
||||
|
||||
"@hookform/resolvers@^2.8.10":
|
||||
version "2.8.10"
|
||||
resolved "https://registry.yarnpkg.com/@hookform/resolvers/-/resolvers-2.8.10.tgz#b66d7a7848b1b1dd5b976a73fff36bb366666e7d"
|
||||
|
@ -3880,6 +3890,13 @@
|
|||
dependencies:
|
||||
defer-to-connect "^1.0.1"
|
||||
|
||||
"@tailwindcss/forms@^0.5.2":
|
||||
version "0.5.2"
|
||||
resolved "https://registry.yarnpkg.com/@tailwindcss/forms/-/forms-0.5.2.tgz#4ef45f9916dcb37838cbe7fecdcc4ba7a7c2ab59"
|
||||
integrity sha512-pSrFeJB6Bg1Mrg9CdQW3+hqZXAKsBrSG9MAfFLKy1pVA4Mb4W7C0k7mEhlmS2Dfo/otxrQOET7NJiJ9RrS563w==
|
||||
dependencies:
|
||||
mini-svg-data-uri "^1.2.3"
|
||||
|
||||
"@testing-library/dom@^8.5.0":
|
||||
version "8.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.13.0.tgz#bc00bdd64c7d8b40841e27a70211399ad3af46f5"
|
||||
|
@ -12231,6 +12248,11 @@ mini-css-extract-plugin@^2.4.5, mini-css-extract-plugin@^2.6.0:
|
|||
dependencies:
|
||||
schema-utils "^4.0.0"
|
||||
|
||||
mini-svg-data-uri@^1.2.3:
|
||||
version "1.4.4"
|
||||
resolved "https://registry.yarnpkg.com/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz#8ab0aabcdf8c29ad5693ca595af19dd2ead09939"
|
||||
integrity sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==
|
||||
|
||||
minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
|
||||
|
|
Loading…
Reference in New Issue
Block a user