parent
1ea3c975d5
commit
56c34de18a
|
@ -49,10 +49,10 @@ export function makePlot(record: SqRecord): Plot | void {
|
||||||
export const DistributionChart: React.FC<DistributionChartProps> = (props) => {
|
export const DistributionChart: React.FC<DistributionChartProps> = (props) => {
|
||||||
const {
|
const {
|
||||||
plot,
|
plot,
|
||||||
environment,
|
|
||||||
height,
|
height,
|
||||||
showSummary,
|
showSummary,
|
||||||
width,
|
width,
|
||||||
|
environment,
|
||||||
logX,
|
logX,
|
||||||
actions = false,
|
actions = false,
|
||||||
} = props;
|
} = props;
|
||||||
|
@ -89,8 +89,12 @@ export const DistributionChart: React.FC<DistributionChartProps> = (props) => {
|
||||||
|
|
||||||
const spec = buildVegaSpec({
|
const spec = buildVegaSpec({
|
||||||
...props,
|
...props,
|
||||||
minX: props.minX ?? Math.min(...domain.map((x) => x.x)),
|
minX: Number.isFinite(props.minX)
|
||||||
maxX: props.minX ?? Math.max(...domain.map((x) => x.x)),
|
? props.minX
|
||||||
|
: Math.min(...domain.map((x) => x.x)),
|
||||||
|
maxX: Number.isFinite(props.maxX)
|
||||||
|
? props.maxX
|
||||||
|
: Math.max(...domain.map((x) => x.x)),
|
||||||
maxY: Math.max(...domain.map((x) => x.y)),
|
maxY: Math.max(...domain.map((x) => x.y)),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,161 +1,22 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { SqValue, environment, SqProject } from "@quri/squiggle-lang";
|
import { useSquiggle, SquiggleArgs } from "../lib/hooks/useSquiggle";
|
||||||
import { useSquiggle } from "../lib/hooks";
|
import {
|
||||||
import { SquiggleViewer } from "./SquiggleViewer";
|
SquiggleViewer,
|
||||||
import { JsImports } from "../lib/jsImports";
|
FlattenedViewSettings,
|
||||||
|
createViewSettings,
|
||||||
|
} from "./SquiggleViewer";
|
||||||
import { getValueToRender } from "../lib/utility";
|
import { getValueToRender } from "../lib/utility";
|
||||||
|
|
||||||
export type SquiggleChartProps = {
|
export type SquiggleChartProps = SquiggleArgs & FlattenedViewSettings;
|
||||||
/** The input string for squiggle */
|
|
||||||
code: string;
|
|
||||||
/** Allows to re-run the code if code hasn't changed */
|
|
||||||
executionId?: number;
|
|
||||||
/** If the output requires monte carlo sampling, the amount of samples */
|
|
||||||
sampleCount?: number;
|
|
||||||
/** If the result is a function, where the function domain starts */
|
|
||||||
diagramStart?: number;
|
|
||||||
/** If the result is a function, where the function domain ends */
|
|
||||||
diagramStop?: number;
|
|
||||||
/** If the result is a function, the amount of stops sampled */
|
|
||||||
diagramCount?: number;
|
|
||||||
/** When the squiggle code gets reevaluated */
|
|
||||||
onChange?(expr: SqValue | undefined, sourceName: string): void;
|
|
||||||
/** CSS width of the element */
|
|
||||||
width?: number;
|
|
||||||
height?: number;
|
|
||||||
/** JS imported parameters */
|
|
||||||
jsImports?: JsImports;
|
|
||||||
/** Whether to show a summary of the distribution */
|
|
||||||
showSummary?: boolean;
|
|
||||||
/** Set the x scale to be logarithmic by deault */
|
|
||||||
logX?: boolean;
|
|
||||||
/** Set the y scale to be exponential by deault */
|
|
||||||
expY?: boolean;
|
|
||||||
/** How to format numbers on the x axis */
|
|
||||||
tickFormat?: string;
|
|
||||||
/** Title of the graphed distribution */
|
|
||||||
title?: string;
|
|
||||||
/** Color of the graphed distribution */
|
|
||||||
color?: string;
|
|
||||||
/** Specify the lower bound of the x scale */
|
|
||||||
minX?: number;
|
|
||||||
/** Specify the upper bound of the x scale */
|
|
||||||
maxX?: number;
|
|
||||||
/** Whether the x-axis should be dates or numbers */
|
|
||||||
xAxisType?: "number" | "dateTime";
|
|
||||||
/** Whether to show vega actions to the user, so they can copy the chart spec */
|
|
||||||
distributionChartActions?: boolean;
|
|
||||||
enableLocalSettings?: boolean;
|
|
||||||
} & (StandaloneExecutionProps | ProjectExecutionProps);
|
|
||||||
|
|
||||||
// Props needed for a standalone execution
|
|
||||||
type StandaloneExecutionProps = {
|
|
||||||
project?: undefined;
|
|
||||||
continues?: undefined;
|
|
||||||
/** The amount of points returned to draw the distribution, not needed if using a project */
|
|
||||||
environment?: environment;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Props needed when executing inside a project.
|
|
||||||
type ProjectExecutionProps = {
|
|
||||||
environment?: undefined;
|
|
||||||
/** The project that this execution is part of */
|
|
||||||
project: SqProject;
|
|
||||||
/** What other squiggle sources from the project to continue. Default [] */
|
|
||||||
continues?: string[];
|
|
||||||
};
|
|
||||||
const defaultOnChange = () => {};
|
|
||||||
const defaultImports: JsImports = {};
|
|
||||||
const defaultContinues: string[] = [];
|
|
||||||
|
|
||||||
export const splitSquiggleChartSettings = (props: SquiggleChartProps) => {
|
|
||||||
const {
|
|
||||||
showSummary = false,
|
|
||||||
logX = false,
|
|
||||||
expY = false,
|
|
||||||
diagramStart = 0,
|
|
||||||
diagramStop = 10,
|
|
||||||
diagramCount = 20,
|
|
||||||
tickFormat,
|
|
||||||
minX,
|
|
||||||
maxX,
|
|
||||||
color,
|
|
||||||
title,
|
|
||||||
xAxisType = "number",
|
|
||||||
distributionChartActions,
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
const distributionPlotSettings = {
|
|
||||||
showSummary,
|
|
||||||
logX,
|
|
||||||
expY,
|
|
||||||
format: tickFormat,
|
|
||||||
minX,
|
|
||||||
maxX,
|
|
||||||
color,
|
|
||||||
title,
|
|
||||||
xAxisType,
|
|
||||||
actions: distributionChartActions,
|
|
||||||
};
|
|
||||||
|
|
||||||
const chartSettings = {
|
|
||||||
start: diagramStart,
|
|
||||||
stop: diagramStop,
|
|
||||||
count: diagramCount,
|
|
||||||
};
|
|
||||||
|
|
||||||
return { distributionPlotSettings, chartSettings };
|
|
||||||
};
|
|
||||||
|
|
||||||
export const SquiggleChart: React.FC<SquiggleChartProps> = React.memo(
|
export const SquiggleChart: React.FC<SquiggleChartProps> = React.memo(
|
||||||
(props) => {
|
(props) => {
|
||||||
const { distributionPlotSettings, chartSettings } =
|
const resultAndBindings = useSquiggle(props);
|
||||||
splitSquiggleChartSettings(props);
|
|
||||||
|
|
||||||
const {
|
|
||||||
code,
|
|
||||||
jsImports = defaultImports,
|
|
||||||
onChange = defaultOnChange, // defaultOnChange must be constant, don't move its definition here
|
|
||||||
executionId = 0,
|
|
||||||
width,
|
|
||||||
height = 200,
|
|
||||||
enableLocalSettings = false,
|
|
||||||
continues = defaultContinues,
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
const p = React.useMemo(() => {
|
|
||||||
if (props.project) {
|
|
||||||
return props.project;
|
|
||||||
} else {
|
|
||||||
const p = SqProject.create();
|
|
||||||
if (props.environment) {
|
|
||||||
p.setEnvironment(props.environment);
|
|
||||||
}
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
}, [props.project, props.environment]);
|
|
||||||
|
|
||||||
const resultAndBindings = useSquiggle({
|
|
||||||
continues,
|
|
||||||
project: p,
|
|
||||||
code,
|
|
||||||
jsImports,
|
|
||||||
onChange,
|
|
||||||
executionId,
|
|
||||||
});
|
|
||||||
|
|
||||||
const valueToRender = getValueToRender(resultAndBindings);
|
const valueToRender = getValueToRender(resultAndBindings);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SquiggleViewer
|
<SquiggleViewer {...createViewSettings(props)} result={valueToRender} />
|
||||||
result={valueToRender}
|
|
||||||
width={width}
|
|
||||||
height={height}
|
|
||||||
distributionPlotSettings={distributionPlotSettings}
|
|
||||||
chartSettings={chartSettings}
|
|
||||||
environment={p.getEnvironment()}
|
|
||||||
enableLocalSettings={enableLocalSettings}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { CodeEditor } from "./CodeEditor";
|
import { CodeEditor } from "./CodeEditor";
|
||||||
import { SquiggleContainer } from "./SquiggleContainer";
|
import { SquiggleContainer } from "./SquiggleContainer";
|
||||||
|
import { useMaybeControlledValue } from "../lib/hooks";
|
||||||
|
import { useSquiggle, SquiggleArgs } from "../lib/hooks/useSquiggle";
|
||||||
|
import { SqLocation } from "@quri/squiggle-lang";
|
||||||
import {
|
import {
|
||||||
splitSquiggleChartSettings,
|
SquiggleViewer,
|
||||||
SquiggleChartProps,
|
createViewSettings,
|
||||||
} from "./SquiggleChart";
|
FlattenedViewSettings,
|
||||||
import { useMaybeControlledValue, useSquiggle } from "../lib/hooks";
|
} from "./SquiggleViewer";
|
||||||
import { JsImports } from "../lib/jsImports";
|
|
||||||
import { defaultEnvironment, SqLocation, SqProject } from "@quri/squiggle-lang";
|
|
||||||
import { SquiggleViewer } from "./SquiggleViewer";
|
|
||||||
import { getErrorLocations, getValueToRender } from "../lib/utility";
|
import { getErrorLocations, getValueToRender } from "../lib/utility";
|
||||||
|
|
||||||
const WrappedCodeEditor: React.FC<{
|
const WrappedCodeEditor: React.FC<{
|
||||||
|
@ -28,14 +28,12 @@ const WrappedCodeEditor: React.FC<{
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
export type SquiggleEditorProps = SquiggleChartProps & {
|
export type SquiggleEditorProps = SquiggleArgs &
|
||||||
|
FlattenedViewSettings & {
|
||||||
defaultCode?: string;
|
defaultCode?: string;
|
||||||
onCodeChange?: (code: string) => void;
|
onCodeChange?: (code: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultOnChange = () => {};
|
|
||||||
const defaultImports: JsImports = {};
|
|
||||||
|
|
||||||
export const SquiggleEditor: React.FC<SquiggleEditorProps> = (props) => {
|
export const SquiggleEditor: React.FC<SquiggleEditorProps> = (props) => {
|
||||||
const [code, setCode] = useMaybeControlledValue({
|
const [code, setCode] = useMaybeControlledValue({
|
||||||
value: props.code,
|
value: props.code,
|
||||||
|
@ -43,34 +41,7 @@ export const SquiggleEditor: React.FC<SquiggleEditorProps> = (props) => {
|
||||||
onChange: props.onCodeChange,
|
onChange: props.onCodeChange,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { distributionPlotSettings, chartSettings } =
|
const resultAndBindings = useSquiggle({ ...props, code });
|
||||||
splitSquiggleChartSettings(props);
|
|
||||||
|
|
||||||
const {
|
|
||||||
environment,
|
|
||||||
jsImports = defaultImports,
|
|
||||||
onChange = defaultOnChange, // defaultOnChange must be constant, don't move its definition here
|
|
||||||
executionId = 0,
|
|
||||||
width,
|
|
||||||
height = 200,
|
|
||||||
enableLocalSettings = false,
|
|
||||||
} = props;
|
|
||||||
|
|
||||||
const project = React.useMemo(() => {
|
|
||||||
const p = SqProject.create();
|
|
||||||
if (environment) {
|
|
||||||
p.setEnvironment(environment);
|
|
||||||
}
|
|
||||||
return p;
|
|
||||||
}, [environment]);
|
|
||||||
|
|
||||||
const resultAndBindings = useSquiggle({
|
|
||||||
code,
|
|
||||||
project,
|
|
||||||
jsImports,
|
|
||||||
onChange,
|
|
||||||
executionId,
|
|
||||||
});
|
|
||||||
|
|
||||||
const valueToRender = getValueToRender(resultAndBindings);
|
const valueToRender = getValueToRender(resultAndBindings);
|
||||||
const errorLocations = getErrorLocations(resultAndBindings.result);
|
const errorLocations = getErrorLocations(resultAndBindings.result);
|
||||||
|
@ -82,15 +53,7 @@ export const SquiggleEditor: React.FC<SquiggleEditorProps> = (props) => {
|
||||||
setCode={setCode}
|
setCode={setCode}
|
||||||
errorLocations={errorLocations}
|
errorLocations={errorLocations}
|
||||||
/>
|
/>
|
||||||
<SquiggleViewer
|
<SquiggleViewer result={valueToRender} {...createViewSettings(props)} />
|
||||||
result={valueToRender}
|
|
||||||
width={width}
|
|
||||||
height={height}
|
|
||||||
distributionPlotSettings={distributionPlotSettings}
|
|
||||||
chartSettings={chartSettings}
|
|
||||||
environment={environment ?? defaultEnvironment}
|
|
||||||
enableLocalSettings={enableLocalSettings}
|
|
||||||
/>
|
|
||||||
</SquiggleContainer>
|
</SquiggleContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,6 +13,7 @@ import {
|
||||||
useRunnerState,
|
useRunnerState,
|
||||||
useSquiggle,
|
useSquiggle,
|
||||||
} from "../lib/hooks";
|
} from "../lib/hooks";
|
||||||
|
import { SquiggleArgs } from "../lib/hooks/useSquiggle";
|
||||||
import { yupResolver } from "@hookform/resolvers/yup";
|
import { yupResolver } from "@hookform/resolvers/yup";
|
||||||
import {
|
import {
|
||||||
ChartSquareBarIcon,
|
ChartSquareBarIcon,
|
||||||
|
@ -28,9 +29,8 @@ import {
|
||||||
} from "@heroicons/react/solid";
|
} from "@heroicons/react/solid";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
|
||||||
import { environment, SqProject } from "@quri/squiggle-lang";
|
import { environment } from "@quri/squiggle-lang";
|
||||||
|
|
||||||
import { SquiggleChartProps } from "./SquiggleChart";
|
|
||||||
import { CodeEditor } from "./CodeEditor";
|
import { CodeEditor } from "./CodeEditor";
|
||||||
import { JsonEditor } from "./JsonEditor";
|
import { JsonEditor } from "./JsonEditor";
|
||||||
import { ErrorAlert, SuccessAlert } from "./Alert";
|
import { ErrorAlert, SuccessAlert } from "./Alert";
|
||||||
|
@ -41,13 +41,17 @@ import { InputItem } from "./ui/InputItem";
|
||||||
import { Text } from "./ui/Text";
|
import { Text } from "./ui/Text";
|
||||||
import { ViewSettings, viewSettingsSchema } from "./ViewSettings";
|
import { ViewSettings, viewSettingsSchema } from "./ViewSettings";
|
||||||
import { HeadedSection } from "./ui/HeadedSection";
|
import { HeadedSection } from "./ui/HeadedSection";
|
||||||
import { defaultTickFormat } from "../lib/distributionSpecBuilder";
|
|
||||||
import { Button } from "./ui/Button";
|
import { Button } from "./ui/Button";
|
||||||
import { JsImports } from "../lib/jsImports";
|
import { JsImports } from "../lib/jsImports";
|
||||||
import { getErrorLocations, getValueToRender } from "../lib/utility";
|
import { getErrorLocations, getValueToRender } from "../lib/utility";
|
||||||
import { SquiggleViewer } from "./SquiggleViewer";
|
import {
|
||||||
|
SquiggleViewer,
|
||||||
|
FlattenedViewSettings,
|
||||||
|
createViewSettings,
|
||||||
|
} from "./SquiggleViewer";
|
||||||
|
|
||||||
type PlaygroundProps = SquiggleChartProps & {
|
type PlaygroundProps = SquiggleArgs &
|
||||||
|
FlattenedViewSettings & {
|
||||||
/** The initial squiggle string to put in the playground */
|
/** The initial squiggle string to put in the playground */
|
||||||
defaultCode?: string;
|
defaultCode?: string;
|
||||||
onCodeChange?(expr: string): void;
|
onCodeChange?(expr: string): void;
|
||||||
|
@ -78,6 +82,7 @@ const schema = yup
|
||||||
.default(1000)
|
.default(1000)
|
||||||
.min(10)
|
.min(10)
|
||||||
.max(10000),
|
.max(10000),
|
||||||
|
showEditor: yup.boolean().required().default(true),
|
||||||
})
|
})
|
||||||
.concat(viewSettingsSchema);
|
.concat(viewSettingsSchema);
|
||||||
|
|
||||||
|
@ -235,23 +240,14 @@ export const PlaygroundContext = React.createContext<PlaygroundContextShape>({
|
||||||
getLeftPanelElement: () => undefined,
|
getLeftPanelElement: () => undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const SquigglePlayground: FC<PlaygroundProps> = ({
|
export const SquigglePlayground: FC<PlaygroundProps> = (props) => {
|
||||||
|
const {
|
||||||
defaultCode = "",
|
defaultCode = "",
|
||||||
height = 500,
|
|
||||||
showSummary = true,
|
|
||||||
logX = false,
|
|
||||||
expY = false,
|
|
||||||
title,
|
|
||||||
minX,
|
|
||||||
maxX,
|
|
||||||
tickFormat = defaultTickFormat,
|
|
||||||
distributionChartActions,
|
|
||||||
code: controlledCode,
|
code: controlledCode,
|
||||||
onCodeChange,
|
onCodeChange,
|
||||||
onSettingsChange,
|
onSettingsChange,
|
||||||
showEditor = true,
|
|
||||||
showShareButton = false,
|
showShareButton = false,
|
||||||
}) => {
|
} = props;
|
||||||
const [code, setCode] = useMaybeControlledValue({
|
const [code, setCode] = useMaybeControlledValue({
|
||||||
value: controlledCode,
|
value: controlledCode,
|
||||||
defaultValue: defaultCode,
|
defaultValue: defaultCode,
|
||||||
|
@ -260,29 +256,19 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
|
||||||
|
|
||||||
const [imports, setImports] = useState<JsImports>({});
|
const [imports, setImports] = useState<JsImports>({});
|
||||||
|
|
||||||
|
let defaultValues: FormFields = {
|
||||||
|
...schema.getDefault(),
|
||||||
|
...props,
|
||||||
|
};
|
||||||
|
|
||||||
const { register, control } = useForm({
|
const { register, control } = useForm({
|
||||||
resolver: yupResolver(schema),
|
resolver: yupResolver(schema),
|
||||||
defaultValues: {
|
defaultValues: defaultValues,
|
||||||
sampleCount: 1000,
|
|
||||||
xyPointLength: 1000,
|
|
||||||
chartHeight: 150,
|
|
||||||
logX,
|
|
||||||
expY,
|
|
||||||
title,
|
|
||||||
minX,
|
|
||||||
maxX,
|
|
||||||
tickFormat,
|
|
||||||
distributionChartActions,
|
|
||||||
showSummary,
|
|
||||||
showEditor,
|
|
||||||
diagramStart: 0,
|
|
||||||
diagramStop: 10,
|
|
||||||
diagramCount: 20,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
const vars = useWatch({
|
const rawVars = useWatch({
|
||||||
control,
|
control,
|
||||||
});
|
});
|
||||||
|
let vars = useMemo(() => ({ ...schema.getDefault(), ...rawVars }), [rawVars]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onSettingsChange?.(vars);
|
onSettingsChange?.(vars);
|
||||||
|
@ -305,20 +291,12 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
|
||||||
executionId,
|
executionId,
|
||||||
} = useRunnerState(code);
|
} = useRunnerState(code);
|
||||||
|
|
||||||
const project = React.useMemo(() => {
|
let args: SquiggleArgs = props;
|
||||||
const p = SqProject.create();
|
args = { ...args, code, jsImports: imports, executionId };
|
||||||
if (environment) {
|
if (!args.project) {
|
||||||
p.setEnvironment(environment);
|
args = { ...args, environment };
|
||||||
}
|
}
|
||||||
return p;
|
const resultAndBindings = useSquiggle(args);
|
||||||
}, [environment]);
|
|
||||||
|
|
||||||
const resultAndBindings = useSquiggle({
|
|
||||||
code,
|
|
||||||
project,
|
|
||||||
jsImports: imports,
|
|
||||||
executionId,
|
|
||||||
});
|
|
||||||
|
|
||||||
const valueToRender = getValueToRender(resultAndBindings);
|
const valueToRender = getValueToRender(resultAndBindings);
|
||||||
|
|
||||||
|
@ -328,27 +306,7 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
|
||||||
{isRunning ? (
|
{isRunning ? (
|
||||||
<div className="absolute inset-0 bg-white opacity-0 animate-semi-appear" />
|
<div className="absolute inset-0 bg-white opacity-0 animate-semi-appear" />
|
||||||
) : null}
|
) : null}
|
||||||
<SquiggleViewer
|
<SquiggleViewer {...createViewSettings(vars)} result={valueToRender} />
|
||||||
result={valueToRender}
|
|
||||||
environment={environment}
|
|
||||||
height={vars.chartHeight || 150}
|
|
||||||
distributionPlotSettings={{
|
|
||||||
showSummary: vars.showSummary ?? false,
|
|
||||||
logX: vars.logX ?? false,
|
|
||||||
expY: vars.expY ?? false,
|
|
||||||
format: vars.tickFormat,
|
|
||||||
minX: vars.minX,
|
|
||||||
maxX: vars.maxX,
|
|
||||||
title: vars.title,
|
|
||||||
actions: vars.distributionChartActions,
|
|
||||||
}}
|
|
||||||
chartSettings={{
|
|
||||||
start: vars.diagramStart ?? 0,
|
|
||||||
stop: vars.diagramStop ?? 10,
|
|
||||||
count: vars.diagramCount ?? 20,
|
|
||||||
}}
|
|
||||||
enableLocalSettings={true}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -363,7 +321,7 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
|
||||||
onSubmit={run}
|
onSubmit={run}
|
||||||
oneLine={false}
|
oneLine={false}
|
||||||
showGutter={true}
|
showGutter={true}
|
||||||
height={height - 1}
|
height={(props.chartHeight ?? 200) - 1}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
@ -402,7 +360,7 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
|
||||||
<div className="flex mt-2">
|
<div className="flex mt-2">
|
||||||
<div
|
<div
|
||||||
className="w-1/2 relative"
|
className="w-1/2 relative"
|
||||||
style={{ minHeight: height }}
|
style={{ minHeight: props.chartHeight }}
|
||||||
ref={leftPanelRef}
|
ref={leftPanelRef}
|
||||||
>
|
>
|
||||||
{tabs}
|
{tabs}
|
||||||
|
|
|
@ -105,9 +105,9 @@ export const ExpressionViewer: React.FC<Props> = ({ value, width }) => {
|
||||||
return (
|
return (
|
||||||
<DistributionChart
|
<DistributionChart
|
||||||
plot={defaultPlot(value.value)}
|
plot={defaultPlot(value.value)}
|
||||||
environment={settings.environment}
|
|
||||||
{...settings.distributionPlotSettings}
|
{...settings.distributionPlotSettings}
|
||||||
height={settings.height}
|
height={settings.chartHeight}
|
||||||
|
environment={settings.environment}
|
||||||
width={width}
|
width={width}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
@ -178,7 +178,7 @@ export const ExpressionViewer: React.FC<Props> = ({ value, width }) => {
|
||||||
fn={value.value}
|
fn={value.value}
|
||||||
chartSettings={settings.chartSettings}
|
chartSettings={settings.chartSettings}
|
||||||
distributionPlotSettings={settings.distributionPlotSettings}
|
distributionPlotSettings={settings.distributionPlotSettings}
|
||||||
height={settings.height}
|
height={settings.chartHeight}
|
||||||
environment={{
|
environment={{
|
||||||
sampleCount: settings.environment.sampleCount / 10,
|
sampleCount: settings.environment.sampleCount / 10,
|
||||||
xyPointLength: settings.environment.xyPointLength / 10,
|
xyPointLength: settings.environment.xyPointLength / 10,
|
||||||
|
@ -203,7 +203,7 @@ export const ExpressionViewer: React.FC<Props> = ({ value, width }) => {
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{(settings) => (
|
{(_) => (
|
||||||
<div>NOT IMPLEMENTED IN 0.4 YET</div>
|
<div>NOT IMPLEMENTED IN 0.4 YET</div>
|
||||||
// <FunctionChart
|
// <FunctionChart
|
||||||
// fn={expression.value.fn}
|
// fn={expression.value.fn}
|
||||||
|
@ -252,7 +252,7 @@ export const ExpressionViewer: React.FC<Props> = ({ value, width }) => {
|
||||||
plot={plot}
|
plot={plot}
|
||||||
environment={settings.environment}
|
environment={settings.environment}
|
||||||
{...settings.distributionPlotSettings}
|
{...settings.distributionPlotSettings}
|
||||||
height={settings.height}
|
height={settings.chartHeight}
|
||||||
width={width}
|
width={width}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,9 +3,13 @@ import React, { useContext, useRef, useState, useEffect } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { yupResolver } from "@hookform/resolvers/yup";
|
import { yupResolver } from "@hookform/resolvers/yup";
|
||||||
import { Modal } from "../ui/Modal";
|
import { Modal } from "../ui/Modal";
|
||||||
import { ViewSettings, viewSettingsSchema } from "../ViewSettings";
|
import {
|
||||||
|
ViewSettings,
|
||||||
|
viewSettingsSchema,
|
||||||
|
mergedToViewSettings,
|
||||||
|
viewSettingsToLocal,
|
||||||
|
} from "../ViewSettings";
|
||||||
import { ViewerContext } from "./ViewerContext";
|
import { ViewerContext } from "./ViewerContext";
|
||||||
import { defaultTickFormat } from "../../lib/distributionSpecBuilder";
|
|
||||||
import { PlaygroundContext } from "../SquigglePlayground";
|
import { PlaygroundContext } from "../SquigglePlayground";
|
||||||
import { SqValue } from "@quri/squiggle-lang";
|
import { SqValue } from "@quri/squiggle-lang";
|
||||||
import { locationAsString } from "./utils";
|
import { locationAsString } from "./utils";
|
||||||
|
@ -34,44 +38,14 @@ const ItemSettingsModal: React.FC<
|
||||||
|
|
||||||
const { register, watch } = useForm({
|
const { register, watch } = useForm({
|
||||||
resolver: yupResolver(viewSettingsSchema),
|
resolver: yupResolver(viewSettingsSchema),
|
||||||
defaultValues: {
|
defaultValues: mergedToViewSettings(mergedSettings),
|
||||||
// this is a mess and should be fixed
|
|
||||||
showEditor: true, // doesn't matter
|
|
||||||
chartHeight: mergedSettings.height,
|
|
||||||
showSummary: mergedSettings.distributionPlotSettings.showSummary,
|
|
||||||
logX: mergedSettings.distributionPlotSettings.logX,
|
|
||||||
expY: mergedSettings.distributionPlotSettings.expY,
|
|
||||||
tickFormat:
|
|
||||||
mergedSettings.distributionPlotSettings.format || defaultTickFormat,
|
|
||||||
title: mergedSettings.distributionPlotSettings.title,
|
|
||||||
minX: mergedSettings.distributionPlotSettings.minX,
|
|
||||||
maxX: mergedSettings.distributionPlotSettings.maxX,
|
|
||||||
distributionChartActions: mergedSettings.distributionPlotSettings.actions,
|
|
||||||
diagramStart: mergedSettings.chartSettings.start,
|
|
||||||
diagramStop: mergedSettings.chartSettings.stop,
|
|
||||||
diagramCount: mergedSettings.chartSettings.count,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const subscription = watch((vars) => {
|
const subscription = watch((vars) => {
|
||||||
const settings = getSettings(value.location); // get the latest version
|
const settings = getSettings(value.location); // get the latest version
|
||||||
setSettings(value.location, {
|
setSettings(value.location, {
|
||||||
...settings,
|
...settings,
|
||||||
distributionPlotSettings: {
|
...viewSettingsToLocal(vars),
|
||||||
showSummary: vars.showSummary,
|
|
||||||
logX: vars.logX,
|
|
||||||
expY: vars.expY,
|
|
||||||
format: vars.tickFormat,
|
|
||||||
title: vars.title,
|
|
||||||
minX: vars.minX,
|
|
||||||
maxX: vars.maxX,
|
|
||||||
actions: vars.distributionChartActions,
|
|
||||||
},
|
|
||||||
chartSettings: {
|
|
||||||
start: vars.diagramStart,
|
|
||||||
stop: vars.diagramStop,
|
|
||||||
count: vars.diagramCount,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
onChange();
|
onChange();
|
||||||
});
|
});
|
||||||
|
@ -102,7 +76,6 @@ const ItemSettingsModal: React.FC<
|
||||||
<Modal.Body>
|
<Modal.Body>
|
||||||
<ViewSettings
|
<ViewSettings
|
||||||
register={register}
|
register={register}
|
||||||
withShowEditorSetting={false}
|
|
||||||
withFunctionSettings={withFunctionSettings}
|
withFunctionSettings={withFunctionSettings}
|
||||||
disableLogXSetting={disableLogX}
|
disableLogXSetting={disableLogX}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { defaultEnvironment, SqValueLocation } from "@quri/squiggle-lang";
|
import { defaultEnvironment, SqValueLocation } from "@quri/squiggle-lang";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { LocalItemSettings, MergedItemSettings } from "./utils";
|
import { LocalItemSettings, MergedItemSettings } from "./utils";
|
||||||
|
import { viewSettingsSchema, viewSettingsToMerged } from "../ViewSettings";
|
||||||
|
|
||||||
type ViewerContextShape = {
|
type ViewerContextShape = {
|
||||||
// Note that we don't store settings themselves in the context (that would cause rerenders of the entire tree on each settings update).
|
// Note that we don't store settings themselves in the context (that would cause rerenders of the entire tree on each settings update).
|
||||||
|
@ -16,19 +17,8 @@ export const ViewerContext = React.createContext<ViewerContextShape>({
|
||||||
getSettings: () => ({ collapsed: false }),
|
getSettings: () => ({ collapsed: false }),
|
||||||
getMergedSettings: () => ({
|
getMergedSettings: () => ({
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
// copy-pasted from SquiggleChart
|
|
||||||
chartSettings: {
|
|
||||||
start: 0,
|
|
||||||
stop: 10,
|
|
||||||
count: 100,
|
|
||||||
},
|
|
||||||
distributionPlotSettings: {
|
|
||||||
showSummary: false,
|
|
||||||
logX: false,
|
|
||||||
expY: false,
|
|
||||||
},
|
|
||||||
environment: defaultEnvironment,
|
environment: defaultEnvironment,
|
||||||
height: 150,
|
...viewSettingsToMerged(viewSettingsSchema.getDefault()),
|
||||||
}),
|
}),
|
||||||
setSettings() {},
|
setSettings() {},
|
||||||
enableLocalSettings: false,
|
enableLocalSettings: false,
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import React, { useCallback, useRef } from "react";
|
import React, { useCallback, useRef } from "react";
|
||||||
import { environment, SqValueLocation } from "@quri/squiggle-lang";
|
import { SqValueLocation } from "@quri/squiggle-lang";
|
||||||
import { DistributionPlottingSettings } from "../DistributionChart";
|
|
||||||
import { FunctionChartSettings } from "../FunctionChart";
|
|
||||||
import { ExpressionViewer } from "./ExpressionViewer";
|
import { ExpressionViewer } from "./ExpressionViewer";
|
||||||
import { ViewerContext } from "./ViewerContext";
|
import { ViewerContext } from "./ViewerContext";
|
||||||
import {
|
import {
|
||||||
|
@ -10,20 +8,40 @@ import {
|
||||||
MergedItemSettings,
|
MergedItemSettings,
|
||||||
} from "./utils";
|
} from "./utils";
|
||||||
import { useSquiggle } from "../../lib/hooks";
|
import { useSquiggle } from "../../lib/hooks";
|
||||||
|
import {
|
||||||
|
EditableViewSettings,
|
||||||
|
viewSettingsSchema,
|
||||||
|
viewSettingsToMerged,
|
||||||
|
} from "../ViewSettings";
|
||||||
import { SquiggleErrorAlert } from "../SquiggleErrorAlert";
|
import { SquiggleErrorAlert } from "../SquiggleErrorAlert";
|
||||||
|
|
||||||
|
// Flattened view settings, gets turned into props for SquiggleChart and SquiggleEditor
|
||||||
|
export type FlattenedViewSettings = Partial<
|
||||||
|
EditableViewSettings & {
|
||||||
|
width?: number;
|
||||||
|
enableLocalSettings?: boolean;
|
||||||
|
}
|
||||||
|
>;
|
||||||
|
|
||||||
|
type ViewSettings = {
|
||||||
|
width?: number;
|
||||||
|
enableLocalSettings?: boolean;
|
||||||
|
} & Omit<MergedItemSettings, "environment">;
|
||||||
|
|
||||||
|
export const createViewSettings = (
|
||||||
|
props: FlattenedViewSettings
|
||||||
|
): ViewSettings => {
|
||||||
|
const propsWithDefaults = { ...viewSettingsSchema.getDefault(), ...props };
|
||||||
|
let merged = viewSettingsToMerged(propsWithDefaults);
|
||||||
|
const { width, enableLocalSettings } = propsWithDefaults;
|
||||||
|
|
||||||
|
return { ...merged, width, enableLocalSettings };
|
||||||
|
};
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
/** The output of squiggle's run */
|
/** The output of squiggle's run */
|
||||||
result: ReturnType<typeof useSquiggle>["result"];
|
result: ReturnType<typeof useSquiggle>["result"];
|
||||||
width?: number;
|
} & ViewSettings;
|
||||||
height: number;
|
|
||||||
distributionPlotSettings: DistributionPlottingSettings;
|
|
||||||
/** Settings for displaying functions */
|
|
||||||
chartSettings: FunctionChartSettings;
|
|
||||||
/** Environment for further function executions */
|
|
||||||
environment: environment;
|
|
||||||
enableLocalSettings?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
type Settings = {
|
type Settings = {
|
||||||
[k: string]: LocalItemSettings;
|
[k: string]: LocalItemSettings;
|
||||||
|
@ -34,10 +52,9 @@ const defaultSettings: LocalItemSettings = { collapsed: false };
|
||||||
export const SquiggleViewer: React.FC<Props> = ({
|
export const SquiggleViewer: React.FC<Props> = ({
|
||||||
result,
|
result,
|
||||||
width,
|
width,
|
||||||
height,
|
chartHeight,
|
||||||
distributionPlotSettings,
|
distributionPlotSettings,
|
||||||
chartSettings,
|
chartSettings,
|
||||||
environment,
|
|
||||||
enableLocalSettings = false,
|
enableLocalSettings = false,
|
||||||
}) => {
|
}) => {
|
||||||
// can't store settings in the state because we don't want to rerender the entire tree on every change
|
// can't store settings in the state because we don't want to rerender the entire tree on every change
|
||||||
|
@ -59,6 +76,7 @@ export const SquiggleViewer: React.FC<Props> = ({
|
||||||
|
|
||||||
const getMergedSettings = useCallback(
|
const getMergedSettings = useCallback(
|
||||||
(location: SqValueLocation) => {
|
(location: SqValueLocation) => {
|
||||||
|
const env = location.project.getEnvironment();
|
||||||
const localSettings = getSettings(location);
|
const localSettings = getSettings(location);
|
||||||
const result: MergedItemSettings = {
|
const result: MergedItemSettings = {
|
||||||
distributionPlotSettings: {
|
distributionPlotSettings: {
|
||||||
|
@ -70,14 +88,14 @@ export const SquiggleViewer: React.FC<Props> = ({
|
||||||
...(localSettings.chartSettings || {}),
|
...(localSettings.chartSettings || {}),
|
||||||
},
|
},
|
||||||
environment: {
|
environment: {
|
||||||
...environment,
|
...env,
|
||||||
...(localSettings.environment || {}),
|
...localSettings.environment,
|
||||||
},
|
},
|
||||||
height: localSettings.height || height,
|
chartHeight: localSettings.chartHeight || chartHeight,
|
||||||
};
|
};
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
[distributionPlotSettings, chartSettings, environment, height, getSettings]
|
[distributionPlotSettings, chartSettings, chartHeight, getSettings]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
import { DistributionPlottingSettings } from "../DistributionChart";
|
import { DistributionPlottingSettings } from "../DistributionChart";
|
||||||
import { FunctionChartSettings } from "../FunctionChart";
|
import { FunctionChartSettings } from "../FunctionChart";
|
||||||
import { environment, SqValueLocation } from "@quri/squiggle-lang";
|
import { SqValueLocation, environment } from "@quri/squiggle-lang";
|
||||||
|
|
||||||
export type LocalItemSettings = {
|
export type LocalItemSettings = {
|
||||||
collapsed: boolean;
|
collapsed: boolean;
|
||||||
distributionPlotSettings?: Partial<DistributionPlottingSettings>;
|
distributionPlotSettings?: Partial<DistributionPlottingSettings>;
|
||||||
chartSettings?: Partial<FunctionChartSettings>;
|
chartSettings?: Partial<FunctionChartSettings>;
|
||||||
height?: number;
|
chartHeight?: number;
|
||||||
environment?: Partial<environment>;
|
environment?: environment;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type MergedItemSettings = {
|
export type MergedItemSettings = {
|
||||||
distributionPlotSettings: DistributionPlottingSettings;
|
distributionPlotSettings: DistributionPlottingSettings;
|
||||||
chartSettings: FunctionChartSettings;
|
chartSettings: FunctionChartSettings;
|
||||||
height: number;
|
chartHeight: number;
|
||||||
environment: environment;
|
environment: environment;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -5,49 +5,137 @@ import { InputItem } from "./ui/InputItem";
|
||||||
import { Checkbox } from "./ui/Checkbox";
|
import { Checkbox } from "./ui/Checkbox";
|
||||||
import { HeadedSection } from "./ui/HeadedSection";
|
import { HeadedSection } from "./ui/HeadedSection";
|
||||||
import { Text } from "./ui/Text";
|
import { Text } from "./ui/Text";
|
||||||
|
import { MergedItemSettings, LocalItemSettings } from "./SquiggleViewer/utils";
|
||||||
import { defaultTickFormat } from "../lib/distributionSpecBuilder";
|
import { defaultTickFormat } from "../lib/distributionSpecBuilder";
|
||||||
|
|
||||||
export const viewSettingsSchema = yup.object({}).shape({
|
export const viewSettingsSchema = yup.object({}).shape({
|
||||||
chartHeight: yup.number().required().positive().integer().default(350),
|
chartHeight: yup.number().required().positive().integer().default(350),
|
||||||
showSummary: yup.boolean().required(),
|
showSummary: yup.boolean().required().default(false),
|
||||||
showEditor: yup.boolean().required(),
|
logX: yup.boolean().required().default(false),
|
||||||
logX: yup.boolean().required(),
|
expY: yup.boolean().required().default(false),
|
||||||
expY: yup.boolean().required(),
|
tickFormat: yup.string().required().default(defaultTickFormat),
|
||||||
tickFormat: yup.string().default(defaultTickFormat),
|
|
||||||
title: yup.string(),
|
title: yup.string(),
|
||||||
minX: yup.number(),
|
minX: yup.number(),
|
||||||
maxX: yup.number(),
|
maxX: yup.number(),
|
||||||
|
xAxisType: yup
|
||||||
|
.mixed<"number" | "dateTime">()
|
||||||
|
.oneOf(["number", "dateTime"])
|
||||||
|
.default("number"),
|
||||||
distributionChartActions: yup.boolean(),
|
distributionChartActions: yup.boolean(),
|
||||||
diagramStart: yup.number().required().positive().integer().default(0).min(0),
|
diagramStart: yup.number().required().positive().integer().default(0).min(0),
|
||||||
diagramStop: yup.number().required().positive().integer().default(10).min(0),
|
diagramStop: yup.number().required().positive().integer().default(10).min(0),
|
||||||
diagramCount: yup.number().required().positive().integer().default(20).min(2),
|
diagramCount: yup.number().required().positive().integer().default(20).min(2),
|
||||||
});
|
});
|
||||||
|
|
||||||
type FormFields = yup.InferType<typeof viewSettingsSchema>;
|
export type EditableViewSettings = yup.InferType<typeof viewSettingsSchema>;
|
||||||
|
|
||||||
|
export const viewSettingsToMerged = (
|
||||||
|
settings: EditableViewSettings
|
||||||
|
): Omit<MergedItemSettings, "environment"> => {
|
||||||
|
const {
|
||||||
|
showSummary,
|
||||||
|
logX,
|
||||||
|
expY,
|
||||||
|
diagramStart,
|
||||||
|
diagramStop,
|
||||||
|
diagramCount,
|
||||||
|
tickFormat,
|
||||||
|
minX,
|
||||||
|
maxX,
|
||||||
|
title,
|
||||||
|
xAxisType,
|
||||||
|
distributionChartActions,
|
||||||
|
chartHeight,
|
||||||
|
} = settings;
|
||||||
|
|
||||||
|
const distributionPlotSettings = {
|
||||||
|
showSummary,
|
||||||
|
logX,
|
||||||
|
expY,
|
||||||
|
format: tickFormat,
|
||||||
|
minX,
|
||||||
|
maxX,
|
||||||
|
title,
|
||||||
|
xAxisType,
|
||||||
|
actions: distributionChartActions,
|
||||||
|
};
|
||||||
|
|
||||||
|
const chartSettings = {
|
||||||
|
start: diagramStart,
|
||||||
|
stop: diagramStop,
|
||||||
|
count: diagramCount,
|
||||||
|
};
|
||||||
|
|
||||||
|
return { distributionPlotSettings, chartSettings, chartHeight };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const viewSettingsToLocal = (
|
||||||
|
settings: Partial<EditableViewSettings>
|
||||||
|
): Omit<LocalItemSettings, "collapsed" | "environment"> => {
|
||||||
|
const {
|
||||||
|
showSummary,
|
||||||
|
logX,
|
||||||
|
expY,
|
||||||
|
diagramStart,
|
||||||
|
diagramStop,
|
||||||
|
diagramCount,
|
||||||
|
tickFormat,
|
||||||
|
minX,
|
||||||
|
maxX,
|
||||||
|
title,
|
||||||
|
xAxisType,
|
||||||
|
distributionChartActions,
|
||||||
|
chartHeight,
|
||||||
|
} = settings;
|
||||||
|
|
||||||
|
const distributionPlotSettings = {
|
||||||
|
showSummary,
|
||||||
|
logX,
|
||||||
|
expY,
|
||||||
|
format: tickFormat,
|
||||||
|
minX,
|
||||||
|
maxX,
|
||||||
|
title,
|
||||||
|
xAxisType,
|
||||||
|
actions: distributionChartActions,
|
||||||
|
};
|
||||||
|
|
||||||
|
const chartSettings = {
|
||||||
|
start: diagramStart,
|
||||||
|
stop: diagramStop,
|
||||||
|
count: diagramCount,
|
||||||
|
};
|
||||||
|
|
||||||
|
return { distributionPlotSettings, chartSettings, chartHeight };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const mergedToViewSettings = (
|
||||||
|
mergedSettings: MergedItemSettings
|
||||||
|
): EditableViewSettings => ({
|
||||||
|
chartHeight: mergedSettings.chartHeight,
|
||||||
|
showSummary: mergedSettings.distributionPlotSettings.showSummary,
|
||||||
|
logX: mergedSettings.distributionPlotSettings.logX,
|
||||||
|
expY: mergedSettings.distributionPlotSettings.expY,
|
||||||
|
tickFormat: mergedSettings.distributionPlotSettings.format,
|
||||||
|
title: mergedSettings.distributionPlotSettings.title,
|
||||||
|
minX: mergedSettings.distributionPlotSettings.minX,
|
||||||
|
maxX: mergedSettings.distributionPlotSettings.maxX,
|
||||||
|
distributionChartActions: mergedSettings.distributionPlotSettings.actions,
|
||||||
|
xAxisType: mergedSettings.distributionPlotSettings.xAxisType,
|
||||||
|
diagramStart: mergedSettings.chartSettings.start,
|
||||||
|
diagramStop: mergedSettings.chartSettings.stop,
|
||||||
|
diagramCount: mergedSettings.chartSettings.count,
|
||||||
|
});
|
||||||
|
|
||||||
// This component is used in two places: for global settings in SquigglePlayground, and for item-specific settings in modal dialogs.
|
|
||||||
export const ViewSettings: React.FC<{
|
export const ViewSettings: React.FC<{
|
||||||
withShowEditorSetting?: boolean;
|
|
||||||
withFunctionSettings?: boolean;
|
withFunctionSettings?: boolean;
|
||||||
disableLogXSetting?: boolean;
|
disableLogXSetting?: boolean;
|
||||||
register: UseFormRegister<FormFields>;
|
register: UseFormRegister<EditableViewSettings>;
|
||||||
}> = ({
|
}> = ({ withFunctionSettings = true, disableLogXSetting, register }) => {
|
||||||
withShowEditorSetting = true,
|
|
||||||
withFunctionSettings = true,
|
|
||||||
disableLogXSetting,
|
|
||||||
register,
|
|
||||||
}) => {
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6 p-3 divide-y divide-gray-200 max-w-xl">
|
<div className="space-y-6 p-3 divide-y divide-gray-200 max-w-xl">
|
||||||
<HeadedSection title="General Display Settings">
|
<HeadedSection title="General Display Settings">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{withShowEditorSetting ? (
|
|
||||||
<Checkbox
|
|
||||||
name="showEditor"
|
|
||||||
register={register}
|
|
||||||
label="Show code editor on left"
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
<InputItem
|
<InputItem
|
||||||
name="chartHeight"
|
name="chartHeight"
|
||||||
type="number"
|
type="number"
|
||||||
|
@ -57,6 +145,23 @@ export const ViewSettings: React.FC<{
|
||||||
</div>
|
</div>
|
||||||
</HeadedSection>
|
</HeadedSection>
|
||||||
|
|
||||||
|
<DistributionViewSettings
|
||||||
|
disableLogXSetting={disableLogXSetting}
|
||||||
|
register={register}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{withFunctionSettings ? (
|
||||||
|
<FunctionViewSettings register={register} />
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DistributionViewSettings: React.FC<{
|
||||||
|
disableLogXSetting?: boolean;
|
||||||
|
register: UseFormRegister<EditableViewSettings>;
|
||||||
|
}> = ({ disableLogXSetting, register }) => {
|
||||||
|
return (
|
||||||
<div className="pt-8">
|
<div className="pt-8">
|
||||||
<HeadedSection title="Distribution Display Settings">
|
<HeadedSection title="Distribution Display Settings">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
|
@ -113,16 +218,20 @@ export const ViewSettings: React.FC<{
|
||||||
</div>
|
</div>
|
||||||
</HeadedSection>
|
</HeadedSection>
|
||||||
</div>
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
{withFunctionSettings ? (
|
export const FunctionViewSettings: React.FC<{
|
||||||
|
register: UseFormRegister<EditableViewSettings>;
|
||||||
|
}> = ({ register }) => (
|
||||||
<div className="pt-8">
|
<div className="pt-8">
|
||||||
<HeadedSection title="Function Display Settings">
|
<HeadedSection title="Function Display Settings">
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<Text>
|
<Text>
|
||||||
When displaying functions of single variables that return
|
When displaying functions of single variables that return numbers or
|
||||||
numbers or distributions, we need to use defaults for the
|
distributions, we need to use defaults for the x-axis. We need to
|
||||||
x-axis. We need to select a minimum and maximum value of x to
|
select a minimum and maximum value of x to sample, and a number n of
|
||||||
sample, and a number n of the number of points to sample.
|
the number of points to sample.
|
||||||
</Text>
|
</Text>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<InputItem
|
<InputItem
|
||||||
|
@ -147,7 +256,4 @@ export const ViewSettings: React.FC<{
|
||||||
</div>
|
</div>
|
||||||
</HeadedSection>
|
</HeadedSection>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
|
@ -13,9 +13,9 @@ export type DistributionChartSpecOptions = {
|
||||||
/** The title of the chart */
|
/** The title of the chart */
|
||||||
title?: string;
|
title?: string;
|
||||||
/** The formatting of the ticks */
|
/** The formatting of the ticks */
|
||||||
format?: string;
|
format: string;
|
||||||
/** Whether the x-axis should be dates or numbers */
|
/** Whether the x-axis should be dates or numbers */
|
||||||
xAxisType?: "number" | "dateTime";
|
xAxisType: "number" | "dateTime";
|
||||||
};
|
};
|
||||||
|
|
||||||
/** X Scales */
|
/** X Scales */
|
||||||
|
@ -70,15 +70,7 @@ const width = 500;
|
||||||
export function buildVegaSpec(
|
export function buildVegaSpec(
|
||||||
specOptions: DistributionChartSpecOptions & { maxY: number }
|
specOptions: DistributionChartSpecOptions & { maxY: number }
|
||||||
): VisualizationSpec {
|
): VisualizationSpec {
|
||||||
const {
|
const { title, minX, maxX, logX, expY, xAxisType, maxY } = specOptions;
|
||||||
title,
|
|
||||||
minX,
|
|
||||||
maxX,
|
|
||||||
logX,
|
|
||||||
expY,
|
|
||||||
xAxisType = "number",
|
|
||||||
maxY,
|
|
||||||
} = specOptions;
|
|
||||||
|
|
||||||
const dateTime = xAxisType === "dateTime";
|
const dateTime = xAxisType === "dateTime";
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import {
|
import {
|
||||||
result,
|
result,
|
||||||
SqError,
|
SqError,
|
||||||
|
environment,
|
||||||
SqProject,
|
SqProject,
|
||||||
SqRecord,
|
SqRecord,
|
||||||
SqValue,
|
SqValue,
|
||||||
|
@ -9,13 +10,31 @@ import { useEffect, useMemo } from "react";
|
||||||
import { JsImports, jsImportsToSquiggleCode } from "../jsImports";
|
import { JsImports, jsImportsToSquiggleCode } from "../jsImports";
|
||||||
import * as uuid from "uuid";
|
import * as uuid from "uuid";
|
||||||
|
|
||||||
type SquiggleArgs = {
|
export type SquiggleArgs = {
|
||||||
code: string;
|
code: string;
|
||||||
executionId?: number;
|
executionId?: number;
|
||||||
jsImports?: JsImports;
|
jsImports?: JsImports;
|
||||||
|
onChange?: (
|
||||||
|
expr: result<SqValue, SqError> | undefined,
|
||||||
|
sourceName: string
|
||||||
|
) => void;
|
||||||
|
} & (StandaloneExecutionProps | ProjectExecutionProps);
|
||||||
|
|
||||||
|
// Props needed for a standalone execution
|
||||||
|
type StandaloneExecutionProps = {
|
||||||
|
project?: undefined;
|
||||||
|
continues?: undefined;
|
||||||
|
/** The amount of points returned to draw the distribution, not needed if using a project */
|
||||||
|
environment?: environment;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Props needed when executing inside a project.
|
||||||
|
type ProjectExecutionProps = {
|
||||||
|
environment?: undefined;
|
||||||
|
/** The project that this execution is part of */
|
||||||
project: SqProject;
|
project: SqProject;
|
||||||
|
/** What other squiggle sources from the project to continue. Default [] */
|
||||||
continues?: string[];
|
continues?: string[];
|
||||||
onChange?: (expr: SqValue | undefined, sourceName: string) => void;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ResultAndBindings = {
|
export type ResultAndBindings = {
|
||||||
|
@ -29,12 +48,24 @@ const defaultContinues = [];
|
||||||
export const useSquiggle = (args: SquiggleArgs): ResultAndBindings => {
|
export const useSquiggle = (args: SquiggleArgs): ResultAndBindings => {
|
||||||
const sourceName = useMemo(() => uuid.v4(), []);
|
const sourceName = useMemo(() => uuid.v4(), []);
|
||||||
|
|
||||||
const env = args.project.getEnvironment();
|
const p = useMemo(() => {
|
||||||
|
if (args.project) {
|
||||||
|
return args.project;
|
||||||
|
} else {
|
||||||
|
const p = SqProject.create();
|
||||||
|
if (args.environment) {
|
||||||
|
p.setEnvironment(args.environment);
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}, [args.project, args.environment]);
|
||||||
|
|
||||||
|
const env = p.getEnvironment();
|
||||||
const continues = args.continues || defaultContinues;
|
const continues = args.continues || defaultContinues;
|
||||||
|
|
||||||
const result = useMemo(
|
const result = useMemo(
|
||||||
() => {
|
() => {
|
||||||
const project = args.project;
|
const project = p;
|
||||||
|
|
||||||
project.setSource(sourceName, args.code);
|
project.setSource(sourceName, args.code);
|
||||||
let fullContinues = continues;
|
let fullContinues = continues;
|
||||||
|
@ -61,25 +92,23 @@ export const useSquiggle = (args: SquiggleArgs): ResultAndBindings => {
|
||||||
continues,
|
continues,
|
||||||
args.project,
|
args.project,
|
||||||
env,
|
env,
|
||||||
|
p,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
const { onChange } = args;
|
const { onChange } = args;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onChange?.(
|
onChange?.(result.result, sourceName);
|
||||||
result.result.tag === "Ok" ? result.result.value : undefined,
|
|
||||||
sourceName
|
|
||||||
);
|
|
||||||
}, [result, onChange, sourceName]);
|
}, [result, onChange, sourceName]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
args.project.removeSource(sourceName);
|
p.removeSource(sourceName);
|
||||||
if (args.project.getSource(importSourceName(sourceName)))
|
if (p.getSource(importSourceName(sourceName)))
|
||||||
args.project.removeSource(importSourceName(sourceName));
|
p.removeSource(importSourceName(sourceName));
|
||||||
};
|
};
|
||||||
}, [args.project, sourceName]);
|
}, [p, sourceName]);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
|
@ -250,5 +250,3 @@ to allow large and small numbers being printed cleanly.
|
||||||
{Template.bind({})}
|
{Template.bind({})}
|
||||||
</Story>
|
</Story>
|
||||||
</Canvas>
|
</Canvas>
|
||||||
|
|
||||||
<Props of={SquiggleChart} />
|
|
||||||
|
|
12
packages/components/test/viewProps.test.tsx
Normal file
12
packages/components/test/viewProps.test.tsx
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { render } from "@testing-library/react";
|
||||||
|
import React from "react";
|
||||||
|
import "@testing-library/jest-dom";
|
||||||
|
import { SquiggleChart } from "../src/index";
|
||||||
|
|
||||||
|
test("showSummary prop shows table", async () => {
|
||||||
|
const { container } = render(
|
||||||
|
<SquiggleChart code={"normal(5, 1)"} showSummary={true} />
|
||||||
|
);
|
||||||
|
expect(container).toHaveTextContent("Mean");
|
||||||
|
expect(container).toHaveTextContent("5");
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user