extract run logic to a custom hook
This commit is contained in:
parent
87bb752c92
commit
7d3366389b
|
@ -86,7 +86,6 @@ const schema = yup.object({}).shape({
|
||||||
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),
|
||||||
autoplay: yup.boolean().required(),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
type FormFields = yup.InferType<typeof schema>;
|
type FormFields = yup.InferType<typeof schema>;
|
||||||
|
@ -306,38 +305,78 @@ const InputVariablesSettings: React.FC<{
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const PlayControls: React.FC<{
|
const RunControls: React.FC<{
|
||||||
autoplay: boolean;
|
autorunMode: boolean;
|
||||||
playing: boolean;
|
isRunning: boolean;
|
||||||
stale: boolean;
|
isStale: boolean;
|
||||||
onAutoplayChange: (value: boolean) => void;
|
onAutorunModeChange: (value: boolean) => void;
|
||||||
play: () => void;
|
run: () => void;
|
||||||
}> = ({ autoplay, playing, stale, onAutoplayChange, play }) => {
|
}> = ({ autorunMode, isRunning, isStale, onAutorunModeChange, run }) => {
|
||||||
const CurrentPlayIcon = playing ? RefreshIcon : PlayIcon;
|
const CurrentPlayIcon = isRunning ? RefreshIcon : PlayIcon;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex space-x-1 items-center">
|
<div className="flex space-x-1 items-center">
|
||||||
{autoplay ? null : (
|
{autorunMode ? null : (
|
||||||
<button onClick={play}>
|
<button onClick={run}>
|
||||||
<CurrentPlayIcon
|
<CurrentPlayIcon
|
||||||
className={clsx(
|
className={clsx(
|
||||||
"w-8 h-8",
|
"w-8 h-8",
|
||||||
playing && "animate-spin",
|
isRunning && "animate-spin",
|
||||||
stale ? "text-indigo-500" : "text-gray-400"
|
isStale ? "text-indigo-500" : "text-gray-400"
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<Toggle
|
<Toggle
|
||||||
texts={["Autoplay", "Paused"]}
|
texts={["Autorun", "Paused"]}
|
||||||
icons={[CheckCircleIcon, PauseIcon]}
|
icons={[CheckCircleIcon, PauseIcon]}
|
||||||
status={autoplay}
|
status={autorunMode}
|
||||||
onChange={onAutoplayChange}
|
onChange={onAutorunModeChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const useRunnerState = (code: string) => {
|
||||||
|
const [autorunMode, setAutorunMode] = useState(true);
|
||||||
|
const [renderedCode, setRenderedCode] = useState(code); // used in manual run mode only
|
||||||
|
const [isRunning, setIsRunning] = useState(false); // used in manual run mode only
|
||||||
|
|
||||||
|
// This part is tricky and fragile; we need to re-render first to make sure that the icon is spinning,
|
||||||
|
// and only then evaluate the squiggle code (which freezes the UI).
|
||||||
|
// Also note that `useEffect` execution order matters here.
|
||||||
|
// Hopefully it'll all go away after we make squiggle code evaluation async.
|
||||||
|
useEffect(() => {
|
||||||
|
if (renderedCode === code && isRunning) {
|
||||||
|
// It's not possible to put this after `setRenderedCode(code)` below because React would apply
|
||||||
|
// `setIsRunning` and `setRenderedCode` together and spinning icon will disappear immediately.
|
||||||
|
setIsRunning(false);
|
||||||
|
}
|
||||||
|
}, [renderedCode, code, isRunning]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!autorunMode && isRunning) {
|
||||||
|
setRenderedCode(code); // TODO - force run even if code hasn't changed
|
||||||
|
}
|
||||||
|
}, [autorunMode, code, isRunning]);
|
||||||
|
|
||||||
|
const run = () => {
|
||||||
|
// The rest will be handled by useEffects above, but we need to update the spinner first.
|
||||||
|
setIsRunning(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
run,
|
||||||
|
renderedCode: autorunMode ? code : renderedCode,
|
||||||
|
isRunning,
|
||||||
|
autorunMode,
|
||||||
|
setAutorunMode: (newValue: boolean) => {
|
||||||
|
if (!newValue) setRenderedCode(code);
|
||||||
|
setAutorunMode(newValue);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export const SquigglePlayground: FC<PlaygroundProps> = ({
|
export const SquigglePlayground: FC<PlaygroundProps> = ({
|
||||||
defaultCode = "",
|
defaultCode = "",
|
||||||
height = 500,
|
height = 500,
|
||||||
|
@ -356,15 +395,10 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
|
||||||
defaultValue: defaultCode,
|
defaultValue: defaultCode,
|
||||||
onChange: onCodeChange,
|
onChange: onCodeChange,
|
||||||
});
|
});
|
||||||
const [renderedCode, setRenderedCode] = useState(""); // used only if autoplay is false
|
|
||||||
|
|
||||||
const [imports, setImports] = useState({});
|
const [imports, setImports] = useState({});
|
||||||
|
|
||||||
const {
|
const { register, control } = useForm({
|
||||||
register,
|
|
||||||
control,
|
|
||||||
setValue: setFormValue,
|
|
||||||
} = useForm({
|
|
||||||
resolver: yupResolver(schema),
|
resolver: yupResolver(schema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
sampleCount: 1000,
|
sampleCount: 1000,
|
||||||
|
@ -381,7 +415,6 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
|
||||||
diagramStart: 0,
|
diagramStart: 0,
|
||||||
diagramStop: 10,
|
diagramStop: 10,
|
||||||
diagramCount: 20,
|
diagramCount: 20,
|
||||||
autoplay: true,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const vars = useWatch({
|
const vars = useWatch({
|
||||||
|
@ -400,38 +433,12 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
|
||||||
[vars.sampleCount, vars.xyPointLength]
|
[vars.sampleCount, vars.xyPointLength]
|
||||||
);
|
);
|
||||||
|
|
||||||
const [playing, setPlaying] = useState(false); // used in manual play mode only
|
const { run, autorunMode, setAutorunMode, isRunning, renderedCode } =
|
||||||
|
useRunnerState(code);
|
||||||
// This part is tricky and fragile; we need to re-render first to make sure that the icon is spinning,
|
|
||||||
// and only then evaluate the squiggle code (which freezes the UI).
|
|
||||||
// Also note that `useEffect` execution order matters here.
|
|
||||||
// Hopefully it'll all go away after we make squiggle code evaluation async.
|
|
||||||
useEffect(() => {
|
|
||||||
if (renderedCode === code && playing) {
|
|
||||||
// It's not possible to put this after `setRenderedCode(code)` below because React would apply
|
|
||||||
// `setPlaying` and `setRenderedCode` together and spinning icon will disappear immediately.
|
|
||||||
setPlaying(false);
|
|
||||||
}
|
|
||||||
}, [renderedCode, code, playing]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!vars.autoplay && playing) {
|
|
||||||
setRenderedCode(code); // TODO - force play even if code hasn't changed
|
|
||||||
}
|
|
||||||
}, [vars.autoplay, code, playing]);
|
|
||||||
|
|
||||||
const play = () => {
|
|
||||||
setPlaying(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const manualPlay = () => {
|
|
||||||
if (vars.autoplay) return; // should we allow reruns even in autoplay mode?
|
|
||||||
setRenderedCode(code); // TODO - force play even if code hasn't changed
|
|
||||||
};
|
|
||||||
|
|
||||||
const squiggleChart = (
|
const squiggleChart = (
|
||||||
<SquiggleChart
|
<SquiggleChart
|
||||||
code={vars.autoplay ? code : renderedCode}
|
code={renderedCode}
|
||||||
environment={env}
|
environment={env}
|
||||||
diagramStart={Number(vars.diagramStart)}
|
diagramStart={Number(vars.diagramStart)}
|
||||||
diagramStop={Number(vars.diagramStop)}
|
diagramStop={Number(vars.diagramStop)}
|
||||||
|
@ -450,9 +457,9 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
|
||||||
const firstTab = vars.showEditor ? (
|
const firstTab = vars.showEditor ? (
|
||||||
<div className="border border-slate-200">
|
<div className="border border-slate-200">
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
value={code ?? ""}
|
value={code}
|
||||||
onChange={setCode}
|
onChange={setCode}
|
||||||
onSubmit={play}
|
onSubmit={run}
|
||||||
oneLine={false}
|
oneLine={false}
|
||||||
showGutter={true}
|
showGutter={true}
|
||||||
height={height - 1}
|
height={height - 1}
|
||||||
|
@ -503,15 +510,12 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
|
||||||
<StyledTab name="View Settings" icon={ChartSquareBarIcon} />
|
<StyledTab name="View Settings" icon={ChartSquareBarIcon} />
|
||||||
<StyledTab name="Input Variables" icon={CurrencyDollarIcon} />
|
<StyledTab name="Input Variables" icon={CurrencyDollarIcon} />
|
||||||
</StyledTab.List>
|
</StyledTab.List>
|
||||||
<PlayControls
|
<RunControls
|
||||||
autoplay={vars.autoplay || false}
|
autorunMode={autorunMode}
|
||||||
stale={!vars.autoplay && renderedCode !== code}
|
isStale={renderedCode !== code}
|
||||||
play={play}
|
run={run}
|
||||||
playing={playing}
|
isRunning={isRunning}
|
||||||
onAutoplayChange={(newValue) => {
|
onAutorunModeChange={setAutorunMode}
|
||||||
if (!newValue) setRenderedCode(code);
|
|
||||||
setFormValue("autoplay", newValue);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{vars.showEditor ? withEditor : withoutEditor}
|
{vars.showEditor ? withEditor : withoutEditor}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user