Merge pull request #777 from quantified-uncertainty/pause-autoplay
Manual play mode
This commit is contained in:
commit
f2ccf574ed
|
@ -8,7 +8,8 @@
|
||||||
"@hookform/resolvers": "^2.9.3",
|
"@hookform/resolvers": "^2.9.3",
|
||||||
"@quri/squiggle-lang": "^0.2.8",
|
"@quri/squiggle-lang": "^0.2.8",
|
||||||
"@react-hook/size": "^2.1.2",
|
"@react-hook/size": "^2.1.2",
|
||||||
"clsx": "^1.2.0",
|
"clsx": "^1.1.1",
|
||||||
|
"framer-motion": "^6.4.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"react": "^18.1.0",
|
"react": "^18.1.0",
|
||||||
"react-ace": "^10.1.0",
|
"react-ace": "^10.1.0",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import React, { FC, useMemo } from "react";
|
import React, { FC, useMemo, useRef } from "react";
|
||||||
import AceEditor from "react-ace";
|
import AceEditor from "react-ace";
|
||||||
|
|
||||||
import "ace-builds/src-noconflict/mode-golang";
|
import "ace-builds/src-noconflict/mode-golang";
|
||||||
|
@ -8,6 +8,7 @@ import "ace-builds/src-noconflict/theme-github";
|
||||||
interface CodeEditorProps {
|
interface CodeEditorProps {
|
||||||
value: string;
|
value: string;
|
||||||
onChange: (value: string) => void;
|
onChange: (value: string) => void;
|
||||||
|
onSubmit?: () => void;
|
||||||
oneLine?: boolean;
|
oneLine?: boolean;
|
||||||
width?: number;
|
width?: number;
|
||||||
height: number;
|
height: number;
|
||||||
|
@ -17,6 +18,7 @@ interface CodeEditorProps {
|
||||||
export const CodeEditor: FC<CodeEditorProps> = ({
|
export const CodeEditor: FC<CodeEditorProps> = ({
|
||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
|
onSubmit,
|
||||||
oneLine = false,
|
oneLine = false,
|
||||||
showGutter = false,
|
showGutter = false,
|
||||||
height,
|
height,
|
||||||
|
@ -24,6 +26,10 @@ export const CodeEditor: FC<CodeEditorProps> = ({
|
||||||
const lineCount = value.split("\n").length;
|
const lineCount = value.split("\n").length;
|
||||||
const id = useMemo(() => _.uniqueId(), []);
|
const id = useMemo(() => _.uniqueId(), []);
|
||||||
|
|
||||||
|
// this is necessary because AceEditor binds commands on mount, see https://github.com/securingsincity/react-ace/issues/684
|
||||||
|
const onSubmitRef = useRef<typeof onSubmit | null>(null);
|
||||||
|
onSubmitRef.current = onSubmit;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AceEditor
|
<AceEditor
|
||||||
value={value}
|
value={value}
|
||||||
|
@ -46,6 +52,13 @@ export const CodeEditor: FC<CodeEditorProps> = ({
|
||||||
enableBasicAutocompletion: false,
|
enableBasicAutocompletion: false,
|
||||||
enableLiveAutocompletion: false,
|
enableLiveAutocompletion: false,
|
||||||
}}
|
}}
|
||||||
|
commands={[
|
||||||
|
{
|
||||||
|
name: "submit",
|
||||||
|
bindKey: { mac: "Cmd-Enter", win: "Ctrl-Enter" },
|
||||||
|
exec: () => onSubmitRef.current?.(),
|
||||||
|
},
|
||||||
|
]}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -48,7 +48,8 @@ export interface SquiggleChartProps {
|
||||||
|
|
||||||
const defaultOnChange = () => {};
|
const defaultOnChange = () => {};
|
||||||
|
|
||||||
export const SquiggleChart: React.FC<SquiggleChartProps> = ({
|
export const SquiggleChart: React.FC<SquiggleChartProps> = React.memo(
|
||||||
|
({
|
||||||
code = "",
|
code = "",
|
||||||
environment,
|
environment,
|
||||||
onChange = defaultOnChange, // defaultOnChange must be constant, don't move its definition here
|
onChange = defaultOnChange, // defaultOnChange must be constant, don't move its definition here
|
||||||
|
@ -64,7 +65,7 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = ({
|
||||||
diagramStart = 0,
|
diagramStart = 0,
|
||||||
diagramStop = 10,
|
diagramStop = 10,
|
||||||
diagramCount = 100,
|
diagramCount = 100,
|
||||||
}) => {
|
}) => {
|
||||||
const result = useSquiggle({
|
const result = useSquiggle({
|
||||||
code,
|
code,
|
||||||
bindings,
|
bindings,
|
||||||
|
@ -101,4 +102,5 @@ export const SquiggleChart: React.FC<SquiggleChartProps> = ({
|
||||||
environment={environment ?? defaultEnvironment}
|
environment={environment ?? defaultEnvironment}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
);
|
||||||
|
|
|
@ -1,15 +1,18 @@
|
||||||
import React, { FC, Fragment, useState, useEffect } from "react";
|
import React, { FC, useState, useEffect, useMemo } from "react";
|
||||||
import { Path, useForm, UseFormRegister, useWatch } from "react-hook-form";
|
import { Path, useForm, UseFormRegister, useWatch } from "react-hook-form";
|
||||||
import * as yup from "yup";
|
import * as yup from "yup";
|
||||||
import { useMaybeControlledValue } from "../lib/hooks";
|
import { useMaybeControlledValue } from "../lib/hooks";
|
||||||
import { yupResolver } from "@hookform/resolvers/yup";
|
import { yupResolver } from "@hookform/resolvers/yup";
|
||||||
import { Tab } from "@headlessui/react";
|
|
||||||
import {
|
import {
|
||||||
ChartSquareBarIcon,
|
ChartSquareBarIcon,
|
||||||
|
CheckCircleIcon,
|
||||||
CodeIcon,
|
CodeIcon,
|
||||||
CogIcon,
|
CogIcon,
|
||||||
CurrencyDollarIcon,
|
CurrencyDollarIcon,
|
||||||
EyeIcon,
|
EyeIcon,
|
||||||
|
PauseIcon,
|
||||||
|
PlayIcon,
|
||||||
|
RefreshIcon,
|
||||||
} from "@heroicons/react/solid";
|
} from "@heroicons/react/solid";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
|
|
||||||
|
@ -20,6 +23,9 @@ import { CodeEditor } from "./CodeEditor";
|
||||||
import { JsonEditor } from "./JsonEditor";
|
import { JsonEditor } from "./JsonEditor";
|
||||||
import { ErrorAlert, SuccessAlert } from "./Alert";
|
import { ErrorAlert, SuccessAlert } from "./Alert";
|
||||||
import { SquiggleContainer } from "./SquiggleContainer";
|
import { SquiggleContainer } from "./SquiggleContainer";
|
||||||
|
import { Toggle } from "./ui/Toggle";
|
||||||
|
import { Checkbox } from "./ui/Checkbox";
|
||||||
|
import { StyledTab } from "./ui/StyledTab";
|
||||||
|
|
||||||
interface PlaygroundProps {
|
interface PlaygroundProps {
|
||||||
/** The initial squiggle string to put in the playground */
|
/** The initial squiggle string to put in the playground */
|
||||||
|
@ -44,9 +50,7 @@ interface PlaygroundProps {
|
||||||
showEditor?: boolean;
|
showEditor?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const schema = yup
|
const schema = yup.object({}).shape({
|
||||||
.object()
|
|
||||||
.shape({
|
|
||||||
sampleCount: yup
|
sampleCount: yup
|
||||||
.number()
|
.number()
|
||||||
.required()
|
.required()
|
||||||
|
@ -72,76 +76,19 @@ const schema = yup
|
||||||
.min(10)
|
.min(10)
|
||||||
.max(100)
|
.max(100)
|
||||||
.default(50),
|
.default(50),
|
||||||
showTypes: yup.boolean(),
|
showTypes: yup.boolean().required(),
|
||||||
showControls: yup.boolean(),
|
showControls: yup.boolean().required(),
|
||||||
showSummary: yup.boolean(),
|
showSummary: yup.boolean().required(),
|
||||||
showEditor: yup.boolean(),
|
showEditor: yup.boolean().required(),
|
||||||
logX: yup.boolean(),
|
logX: yup.boolean().required(),
|
||||||
expY: yup.boolean(),
|
expY: yup.boolean().required(),
|
||||||
showSettingsPage: yup.boolean().default(false),
|
showSettingsPage: yup.boolean().default(false),
|
||||||
diagramStart: yup
|
diagramStart: yup.number().required().positive().integer().default(0).min(0),
|
||||||
.number()
|
diagramStop: yup.number().required().positive().integer().default(10).min(0),
|
||||||
.required()
|
diagramCount: yup.number().required().positive().integer().default(20).min(2),
|
||||||
.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();
|
|
||||||
|
|
||||||
type StyledTabProps = {
|
type FormFields = yup.InferType<typeof schema>;
|
||||||
name: string;
|
|
||||||
icon: (props: React.ComponentProps<"svg">) => JSX.Element;
|
|
||||||
};
|
|
||||||
|
|
||||||
const StyledTab: React.FC<StyledTabProps> = ({ name, icon: Icon }) => {
|
|
||||||
return (
|
|
||||||
<Tab key={name} as={Fragment}>
|
|
||||||
{({ selected }) => (
|
|
||||||
<button className="group flex rounded-md focus:outline-none focus-visible:ring-offset-gray-100">
|
|
||||||
<span
|
|
||||||
className={clsx(
|
|
||||||
"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
|
|
||||||
className={clsx(
|
|
||||||
"-ml-0.5 mr-2 h-4 w-4",
|
|
||||||
selected
|
|
||||||
? "text-slate-500"
|
|
||||||
: "text-gray-400 group-hover:text-gray-900"
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<span
|
|
||||||
className={clsx(
|
|
||||||
selected
|
|
||||||
? "text-gray-900"
|
|
||||||
: "text-gray-600 group-hover:text-gray-900"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{name}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</Tab>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const HeadedSection: FC<{ title: string; children: React.ReactNode }> = ({
|
const HeadedSection: FC<{ title: string; children: React.ReactNode }> = ({
|
||||||
title,
|
title,
|
||||||
|
@ -182,91 +129,9 @@ function InputItem<T>({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Checkbox<T>({
|
const SamplingSettings: React.FC<{ register: UseFormRegister<FormFields> }> = ({
|
||||||
name,
|
|
||||||
label,
|
|
||||||
register,
|
register,
|
||||||
}: {
|
}) => (
|
||||||
name: Path<T>;
|
|
||||||
label: string;
|
|
||||||
register: UseFormRegister<T>;
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<label className="flex items-center">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
{...register(name)}
|
|
||||||
className="form-checkbox focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
|
|
||||||
/>
|
|
||||||
{/* Clicking on the div makes the checkbox lose focus while mouse button is pressed, leading to annoying blinking; I couldn't figure out how to fix this. */}
|
|
||||||
<div className="ml-3 text-sm font-medium text-gray-700">{label}</div>
|
|
||||||
</label>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SquigglePlayground: FC<PlaygroundProps> = ({
|
|
||||||
defaultCode = "",
|
|
||||||
height = 500,
|
|
||||||
showTypes = false,
|
|
||||||
showControls = false,
|
|
||||||
showSummary = false,
|
|
||||||
logX = false,
|
|
||||||
expY = false,
|
|
||||||
code: controlledCode,
|
|
||||||
onCodeChange,
|
|
||||||
onSettingsChange,
|
|
||||||
showEditor = true,
|
|
||||||
}) => {
|
|
||||||
const [code, setCode] = useMaybeControlledValue({
|
|
||||||
value: controlledCode,
|
|
||||||
defaultValue: defaultCode,
|
|
||||||
onChange: onCodeChange,
|
|
||||||
});
|
|
||||||
const [importString, setImportString] = useState("{}");
|
|
||||||
const [imports, setImports] = useState({});
|
|
||||||
const [importsAreValid, setImportsAreValid] = useState(true);
|
|
||||||
const { register, control } = useForm({
|
|
||||||
resolver: yupResolver(schema),
|
|
||||||
defaultValues: {
|
|
||||||
sampleCount: 1000,
|
|
||||||
xyPointLength: 1000,
|
|
||||||
chartHeight: 150,
|
|
||||||
showTypes,
|
|
||||||
showControls,
|
|
||||||
logX,
|
|
||||||
expY,
|
|
||||||
showSummary,
|
|
||||||
showEditor,
|
|
||||||
leftSizePercent: 50,
|
|
||||||
showSettingsPage: false,
|
|
||||||
diagramStart: 0,
|
|
||||||
diagramStop: 10,
|
|
||||||
diagramCount: 20,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const vars = useWatch({
|
|
||||||
control,
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
onSettingsChange?.(vars);
|
|
||||||
}, [vars, onSettingsChange]);
|
|
||||||
|
|
||||||
const env: environment = {
|
|
||||||
sampleCount: Number(vars.sampleCount),
|
|
||||||
xyPointLength: Number(vars.xyPointLength),
|
|
||||||
};
|
|
||||||
const getChangeJson = (r: string) => {
|
|
||||||
setImportString(r);
|
|
||||||
try {
|
|
||||||
setImports(JSON.parse(r));
|
|
||||||
setImportsAreValid(true);
|
|
||||||
} catch (e) {
|
|
||||||
setImportsAreValid(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const samplingSettings = (
|
|
||||||
<div className="space-y-6 p-3 max-w-xl">
|
<div className="space-y-6 p-3 max-w-xl">
|
||||||
<div>
|
<div>
|
||||||
<InputItem
|
<InputItem
|
||||||
|
@ -291,15 +156,17 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
|
||||||
/>
|
/>
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
<Text>
|
<Text>
|
||||||
When distributions are converted into PointSet shapes, we need to
|
When distributions are converted into PointSet shapes, we need to know
|
||||||
know how many coordinates to use.
|
how many coordinates to use.
|
||||||
</Text>
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
const viewSettings = (
|
const ViewSettings: React.FC<{ register: UseFormRegister<FormFields> }> = ({
|
||||||
|
register,
|
||||||
|
}) => (
|
||||||
<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">
|
||||||
|
@ -353,10 +220,10 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
|
||||||
<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 numbers
|
When displaying functions of single variables that return numbers or
|
||||||
or distributions, we need to use defaults for the x-axis. We need
|
distributions, we need to use defaults for the x-axis. We need to
|
||||||
to select a minimum and maximum value of x to sample, and a number
|
select a minimum and maximum value of x to sample, and a number n of
|
||||||
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
|
||||||
|
@ -382,9 +249,30 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
|
||||||
</HeadedSection>
|
</HeadedSection>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
const inputVariableSettings = (
|
const InputVariablesSettings: React.FC<{
|
||||||
|
initialImports: any; // TODO - any json type
|
||||||
|
setImports: (imports: any) => void;
|
||||||
|
}> = ({ initialImports, setImports }) => {
|
||||||
|
const [importString, setImportString] = useState(() =>
|
||||||
|
JSON.stringify(initialImports)
|
||||||
|
);
|
||||||
|
const [importsAreValid, setImportsAreValid] = useState(true);
|
||||||
|
|
||||||
|
const onChange = (value: string) => {
|
||||||
|
setImportString(value);
|
||||||
|
let imports = {} as any;
|
||||||
|
try {
|
||||||
|
imports = JSON.parse(value);
|
||||||
|
setImportsAreValid(true);
|
||||||
|
} catch (e) {
|
||||||
|
setImportsAreValid(false);
|
||||||
|
}
|
||||||
|
setImports(imports);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
<div className="p-3 max-w-3xl">
|
<div className="p-3 max-w-3xl">
|
||||||
<HeadedSection title="Import Variables from JSON">
|
<HeadedSection title="Import Variables from JSON">
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
|
@ -396,7 +284,7 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
|
||||||
<div className="border border-slate-200 mt-6 mb-2">
|
<div className="border border-slate-200 mt-6 mb-2">
|
||||||
<JsonEditor
|
<JsonEditor
|
||||||
value={importString}
|
value={importString}
|
||||||
onChange={getChangeJson}
|
onChange={onChange}
|
||||||
oneLine={false}
|
oneLine={false}
|
||||||
showGutter={true}
|
showGutter={true}
|
||||||
height={150}
|
height={150}
|
||||||
|
@ -415,10 +303,142 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
|
||||||
</HeadedSection>
|
</HeadedSection>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const RunControls: React.FC<{
|
||||||
|
autorunMode: boolean;
|
||||||
|
isRunning: boolean;
|
||||||
|
isStale: boolean;
|
||||||
|
onAutorunModeChange: (value: boolean) => void;
|
||||||
|
run: () => void;
|
||||||
|
}> = ({ autorunMode, isRunning, isStale, onAutorunModeChange, run }) => {
|
||||||
|
const CurrentPlayIcon = isRunning ? RefreshIcon : PlayIcon;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex space-x-1 items-center">
|
||||||
|
{autorunMode ? null : (
|
||||||
|
<button onClick={run}>
|
||||||
|
<CurrentPlayIcon
|
||||||
|
className={clsx(
|
||||||
|
"w-8 h-8",
|
||||||
|
isRunning && "animate-spin",
|
||||||
|
isStale ? "text-indigo-500" : "text-gray-400"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
<Toggle
|
||||||
|
texts={["Autorun", "Paused"]}
|
||||||
|
icons={[CheckCircleIcon, PauseIcon]}
|
||||||
|
status={autorunMode}
|
||||||
|
onChange={onAutorunModeChange}
|
||||||
|
/>
|
||||||
|
</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> = ({
|
||||||
|
defaultCode = "",
|
||||||
|
height = 500,
|
||||||
|
showTypes = false,
|
||||||
|
showControls = false,
|
||||||
|
showSummary = false,
|
||||||
|
logX = false,
|
||||||
|
expY = false,
|
||||||
|
code: controlledCode,
|
||||||
|
onCodeChange,
|
||||||
|
onSettingsChange,
|
||||||
|
showEditor = true,
|
||||||
|
}) => {
|
||||||
|
const [code, setCode] = useMaybeControlledValue({
|
||||||
|
value: controlledCode,
|
||||||
|
defaultValue: defaultCode,
|
||||||
|
onChange: onCodeChange,
|
||||||
|
});
|
||||||
|
|
||||||
|
const [imports, setImports] = useState({});
|
||||||
|
|
||||||
|
const { register, control } = useForm({
|
||||||
|
resolver: yupResolver(schema),
|
||||||
|
defaultValues: {
|
||||||
|
sampleCount: 1000,
|
||||||
|
xyPointLength: 1000,
|
||||||
|
chartHeight: 150,
|
||||||
|
showTypes,
|
||||||
|
showControls,
|
||||||
|
logX,
|
||||||
|
expY,
|
||||||
|
showSummary,
|
||||||
|
showEditor,
|
||||||
|
leftSizePercent: 50,
|
||||||
|
showSettingsPage: false,
|
||||||
|
diagramStart: 0,
|
||||||
|
diagramStop: 10,
|
||||||
|
diagramCount: 20,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const vars = useWatch({
|
||||||
|
control,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onSettingsChange?.(vars);
|
||||||
|
}, [vars, onSettingsChange]);
|
||||||
|
|
||||||
|
const env: environment = useMemo(
|
||||||
|
() => ({
|
||||||
|
sampleCount: Number(vars.sampleCount),
|
||||||
|
xyPointLength: Number(vars.xyPointLength),
|
||||||
|
}),
|
||||||
|
[vars.sampleCount, vars.xyPointLength]
|
||||||
|
);
|
||||||
|
|
||||||
|
const { run, autorunMode, setAutorunMode, isRunning, renderedCode } =
|
||||||
|
useRunnerState(code);
|
||||||
|
|
||||||
const squiggleChart = (
|
const squiggleChart = (
|
||||||
<SquiggleChart
|
<SquiggleChart
|
||||||
code={code}
|
code={renderedCode}
|
||||||
environment={env}
|
environment={env}
|
||||||
diagramStart={Number(vars.diagramStart)}
|
diagramStart={Number(vars.diagramStart)}
|
||||||
diagramStop={Number(vars.diagramStop)}
|
diagramStop={Number(vars.diagramStop)}
|
||||||
|
@ -437,8 +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={run}
|
||||||
oneLine={false}
|
oneLine={false}
|
||||||
showGutter={true}
|
showGutter={true}
|
||||||
height={height - 1}
|
height={height - 1}
|
||||||
|
@ -449,12 +470,21 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
|
||||||
);
|
);
|
||||||
|
|
||||||
const tabs = (
|
const tabs = (
|
||||||
<Tab.Panels>
|
<StyledTab.Panels>
|
||||||
<Tab.Panel>{firstTab}</Tab.Panel>
|
<StyledTab.Panel>{firstTab}</StyledTab.Panel>
|
||||||
<Tab.Panel>{samplingSettings}</Tab.Panel>
|
<StyledTab.Panel>
|
||||||
<Tab.Panel>{viewSettings}</Tab.Panel>
|
<SamplingSettings register={register} />
|
||||||
<Tab.Panel>{inputVariableSettings}</Tab.Panel>
|
</StyledTab.Panel>
|
||||||
</Tab.Panels>
|
<StyledTab.Panel>
|
||||||
|
<ViewSettings register={register} />
|
||||||
|
</StyledTab.Panel>
|
||||||
|
<StyledTab.Panel>
|
||||||
|
<InputVariablesSettings
|
||||||
|
initialImports={imports}
|
||||||
|
setImports={setImports}
|
||||||
|
/>
|
||||||
|
</StyledTab.Panel>
|
||||||
|
</StyledTab.Panels>
|
||||||
);
|
);
|
||||||
|
|
||||||
const withEditor = (
|
const withEditor = (
|
||||||
|
@ -468,9 +498,10 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SquiggleContainer>
|
<SquiggleContainer>
|
||||||
<Tab.Group>
|
<StyledTab.Group>
|
||||||
<div className="pb-4">
|
<div className="pb-4">
|
||||||
<Tab.List className="flex w-fit p-0.5 mt-2 rounded-md bg-slate-100 hover:bg-slate-200">
|
<div className="flex justify-between items-center mt-2">
|
||||||
|
<StyledTab.List>
|
||||||
<StyledTab
|
<StyledTab
|
||||||
name={vars.showEditor ? "Code" : "Display"}
|
name={vars.showEditor ? "Code" : "Display"}
|
||||||
icon={vars.showEditor ? CodeIcon : EyeIcon}
|
icon={vars.showEditor ? CodeIcon : EyeIcon}
|
||||||
|
@ -478,10 +509,18 @@ export const SquigglePlayground: FC<PlaygroundProps> = ({
|
||||||
<StyledTab name="Sampling Settings" icon={CogIcon} />
|
<StyledTab name="Sampling Settings" icon={CogIcon} />
|
||||||
<StyledTab name="View Settings" icon={ChartSquareBarIcon} />
|
<StyledTab name="View Settings" icon={ChartSquareBarIcon} />
|
||||||
<StyledTab name="Input Variables" icon={CurrencyDollarIcon} />
|
<StyledTab name="Input Variables" icon={CurrencyDollarIcon} />
|
||||||
</Tab.List>
|
</StyledTab.List>
|
||||||
|
<RunControls
|
||||||
|
autorunMode={autorunMode}
|
||||||
|
isStale={renderedCode !== code}
|
||||||
|
run={run}
|
||||||
|
isRunning={isRunning}
|
||||||
|
onAutorunModeChange={setAutorunMode}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
{vars.showEditor ? withEditor : withoutEditor}
|
{vars.showEditor ? withEditor : withoutEditor}
|
||||||
</div>
|
</div>
|
||||||
</Tab.Group>
|
</StyledTab.Group>
|
||||||
</SquiggleContainer>
|
</SquiggleContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
24
packages/components/src/components/ui/Checkbox.tsx
Normal file
24
packages/components/src/components/ui/Checkbox.tsx
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Path, UseFormRegister } from "react-hook-form";
|
||||||
|
|
||||||
|
export function Checkbox<T>({
|
||||||
|
name,
|
||||||
|
label,
|
||||||
|
register,
|
||||||
|
}: {
|
||||||
|
name: Path<T>;
|
||||||
|
label: string;
|
||||||
|
register: UseFormRegister<T>;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<label className="flex items-center">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
{...register(name)}
|
||||||
|
className="form-checkbox focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded"
|
||||||
|
/>
|
||||||
|
{/* Clicking on the div makes the checkbox lose focus while mouse button is pressed, leading to annoying blinking; I couldn't figure out how to fix this. */}
|
||||||
|
<div className="ml-3 text-sm font-medium text-gray-700">{label}</div>
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
}
|
60
packages/components/src/components/ui/StyledTab.tsx
Normal file
60
packages/components/src/components/ui/StyledTab.tsx
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
import React, { Fragment } from "react";
|
||||||
|
import { Tab } from "@headlessui/react";
|
||||||
|
import clsx from "clsx";
|
||||||
|
|
||||||
|
type StyledTabProps = {
|
||||||
|
name: string;
|
||||||
|
icon: (props: React.ComponentProps<"svg">) => JSX.Element;
|
||||||
|
};
|
||||||
|
|
||||||
|
type StyledTabType = React.FC<StyledTabProps> & {
|
||||||
|
List: React.FC<{ children: React.ReactNode }>;
|
||||||
|
Group: typeof Tab.Group;
|
||||||
|
Panels: typeof Tab.Panels;
|
||||||
|
Panel: typeof Tab.Panel;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const StyledTab: StyledTabType = ({ name, icon: Icon }) => {
|
||||||
|
return (
|
||||||
|
<Tab as={Fragment}>
|
||||||
|
{({ selected }) => (
|
||||||
|
<button className="group flex rounded-md focus:outline-none focus-visible:ring-offset-gray-100">
|
||||||
|
<span
|
||||||
|
className={clsx(
|
||||||
|
"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
|
||||||
|
className={clsx(
|
||||||
|
"-ml-0.5 mr-2 h-4 w-4",
|
||||||
|
selected
|
||||||
|
? "text-slate-500"
|
||||||
|
: "text-gray-400 group-hover:text-gray-900"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
className={clsx(
|
||||||
|
selected
|
||||||
|
? "text-gray-900"
|
||||||
|
: "text-gray-600 group-hover:text-gray-900"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</Tab>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
StyledTab.List = ({ children }) => (
|
||||||
|
<Tab.List className="flex w-fit p-0.5 rounded-md bg-slate-100 hover:bg-slate-200">
|
||||||
|
{children}
|
||||||
|
</Tab.List>
|
||||||
|
);
|
||||||
|
|
||||||
|
StyledTab.Group = Tab.Group;
|
||||||
|
StyledTab.Panels = Tab.Panels;
|
||||||
|
StyledTab.Panel = Tab.Panel;
|
41
packages/components/src/components/ui/Toggle.tsx
Normal file
41
packages/components/src/components/ui/Toggle.tsx
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import clsx from "clsx";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
type IconType = (props: React.ComponentProps<"svg">) => JSX.Element;
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
status: boolean;
|
||||||
|
onChange: (status: boolean) => void;
|
||||||
|
texts: [string, string];
|
||||||
|
icons: [IconType, IconType];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Toggle: React.FC<Props> = ({
|
||||||
|
texts: [onText, offText],
|
||||||
|
icons: [OnIcon, OffIcon],
|
||||||
|
status,
|
||||||
|
onChange,
|
||||||
|
}) => {
|
||||||
|
const CurrentIcon = status ? OnIcon : OffIcon;
|
||||||
|
return (
|
||||||
|
<motion.button
|
||||||
|
layout
|
||||||
|
transition={{ duration: 0.2 }}
|
||||||
|
className={clsx(
|
||||||
|
"rounded-full py-1 bg-indigo-500 text-white text-xs font-semibold flex items-center space-x-1",
|
||||||
|
status ? "bg-indigo-500" : "bg-gray-400",
|
||||||
|
status ? "pl-1 pr-3" : "pl-3 pr-1",
|
||||||
|
!status && "flex-row-reverse space-x-reverse"
|
||||||
|
)}
|
||||||
|
onClick={() => onChange(!status)}
|
||||||
|
>
|
||||||
|
<motion.div layout transition={{ duration: 0.2 }}>
|
||||||
|
<CurrentIcon className="w-6 h-6" />
|
||||||
|
</motion.div>
|
||||||
|
<motion.span layout transition={{ duration: 0.2 }}>
|
||||||
|
{status ? onText : offText}
|
||||||
|
</motion.span>
|
||||||
|
</motion.button>
|
||||||
|
);
|
||||||
|
};
|
111
yarn.lock
111
yarn.lock
|
@ -1802,6 +1802,18 @@
|
||||||
url-loader "^4.1.1"
|
url-loader "^4.1.1"
|
||||||
webpack "^5.72.1"
|
webpack "^5.72.1"
|
||||||
|
|
||||||
|
"@emotion/is-prop-valid@^0.8.2":
|
||||||
|
version "0.8.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz#db28b1c4368a259b60a97311d6a952d4fd01ac1a"
|
||||||
|
integrity sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==
|
||||||
|
dependencies:
|
||||||
|
"@emotion/memoize" "0.7.4"
|
||||||
|
|
||||||
|
"@emotion/memoize@0.7.4":
|
||||||
|
version "0.7.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.4.tgz#19bf0f5af19149111c40d98bb0cf82119f5d9eeb"
|
||||||
|
integrity sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==
|
||||||
|
|
||||||
"@eslint/eslintrc@^1.3.0":
|
"@eslint/eslintrc@^1.3.0":
|
||||||
version "1.3.0"
|
version "1.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f"
|
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f"
|
||||||
|
@ -2237,6 +2249,59 @@
|
||||||
resolved "https://registry.yarnpkg.com/@mdx-js/util/-/util-1.6.22.tgz#219dfd89ae5b97a8801f015323ffa4b62f45718b"
|
resolved "https://registry.yarnpkg.com/@mdx-js/util/-/util-1.6.22.tgz#219dfd89ae5b97a8801f015323ffa4b62f45718b"
|
||||||
integrity sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA==
|
integrity sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA==
|
||||||
|
|
||||||
|
"@motionone/animation@^10.10.1":
|
||||||
|
version "10.10.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@motionone/animation/-/animation-10.10.1.tgz#d3d3ecb11c6d7507a104b080f31b07be9bc7056d"
|
||||||
|
integrity sha512-iX839/Ir5wT7hVX0yCZYjcDhHAOkVR5hIhVBTf37qEUD693uVwrxC2i1BI9vMVPc1rIoFtftYjOtwoO9Oq/aog==
|
||||||
|
dependencies:
|
||||||
|
"@motionone/easing" "^10.9.0"
|
||||||
|
"@motionone/types" "^10.9.0"
|
||||||
|
"@motionone/utils" "^10.9.0"
|
||||||
|
tslib "^2.3.1"
|
||||||
|
|
||||||
|
"@motionone/dom@^10.11.0":
|
||||||
|
version "10.11.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@motionone/dom/-/dom-10.11.0.tgz#dc2e46b0e21044cc9ae89a994cd6725eac09e48b"
|
||||||
|
integrity sha512-ST56HBslkeoeDwqRFWafkD+JT5FEwlHqB2K2KGaQh6wo6zfKQ9xwjQcqgDiBv2gg6s8ycjHAdFS6dnMHQ5hXKw==
|
||||||
|
dependencies:
|
||||||
|
"@motionone/animation" "^10.10.1"
|
||||||
|
"@motionone/generators" "^10.9.0"
|
||||||
|
"@motionone/types" "^10.9.0"
|
||||||
|
"@motionone/utils" "^10.9.0"
|
||||||
|
hey-listen "^1.0.8"
|
||||||
|
tslib "^2.3.1"
|
||||||
|
|
||||||
|
"@motionone/easing@^10.9.0":
|
||||||
|
version "10.9.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@motionone/easing/-/easing-10.9.0.tgz#42910c70dd30e1bbba27a9612ce03bca022d0da7"
|
||||||
|
integrity sha512-FYIr3HlQEb7aE5LOpY6BPQUaPyKeJt6VfGA+npy73+JIGqoVOjbrdZ1ZQxzTXqO76mG3UZvv1+twrDamRQsxFw==
|
||||||
|
dependencies:
|
||||||
|
"@motionone/utils" "^10.9.0"
|
||||||
|
tslib "^2.3.1"
|
||||||
|
|
||||||
|
"@motionone/generators@^10.9.0":
|
||||||
|
version "10.9.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@motionone/generators/-/generators-10.9.0.tgz#46f3e706f45a9566db7e0ce62d677c884813cdaa"
|
||||||
|
integrity sha512-BOkHx4qQswJV+z/6k05qdvRENE4hG606NI5cIPTsLtSuksnRn83utuj/15VTNoFeYHuTdhwzxvIPvlPVayIGTg==
|
||||||
|
dependencies:
|
||||||
|
"@motionone/types" "^10.9.0"
|
||||||
|
"@motionone/utils" "^10.9.0"
|
||||||
|
tslib "^2.3.1"
|
||||||
|
|
||||||
|
"@motionone/types@^10.9.0":
|
||||||
|
version "10.9.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@motionone/types/-/types-10.9.0.tgz#7caa9c1b746dd30bcf6104ce9c82a5a928385da2"
|
||||||
|
integrity sha512-ZcEDfsrS2ym9+vExV7+59avkzEO/PLkNj16uaDvbWhi0Q/vOZ72j2LQTrtDLWVyZRIeUaB/i8DJP017Gj6UYQw==
|
||||||
|
|
||||||
|
"@motionone/utils@^10.9.0":
|
||||||
|
version "10.9.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@motionone/utils/-/utils-10.9.0.tgz#a68fe41fe37e4365b07dfc676f014a0e43b8beb2"
|
||||||
|
integrity sha512-5IgmwQ8TdH1HsQ9d2QZeBCu9+HkqjoYRYItRpmusoyiedPMZaKdU3pr3qFP5nbAj68Ww2sTUxgEZEOF20qJA6w==
|
||||||
|
dependencies:
|
||||||
|
"@motionone/types" "^10.9.0"
|
||||||
|
hey-listen "^1.0.8"
|
||||||
|
tslib "^2.3.1"
|
||||||
|
|
||||||
"@mrmlnc/readdir-enhanced@^2.2.1":
|
"@mrmlnc/readdir-enhanced@^2.2.1":
|
||||||
version "2.2.1"
|
version "2.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde"
|
resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde"
|
||||||
|
@ -9149,6 +9214,27 @@ fragment-cache@^0.2.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
map-cache "^0.2.2"
|
map-cache "^0.2.2"
|
||||||
|
|
||||||
|
framer-motion@^6.4.1:
|
||||||
|
version "6.4.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/framer-motion/-/framer-motion-6.4.1.tgz#d351a96d80bea5736af4175e6307bc63bb4daed1"
|
||||||
|
integrity sha512-JiVnG3p0mO9yWxjNx3xxJaKP4hTWdm6oIbzfMJZEMtoOahSNoDTRZA/mFPzfVNj2foKwFPOLFv/lw8UUZjyUqA==
|
||||||
|
dependencies:
|
||||||
|
"@motionone/dom" "^10.11.0"
|
||||||
|
framesync "6.0.1"
|
||||||
|
hey-listen "^1.0.8"
|
||||||
|
popmotion "11.0.3"
|
||||||
|
style-value-types "5.0.0"
|
||||||
|
tslib "^2.1.0"
|
||||||
|
optionalDependencies:
|
||||||
|
"@emotion/is-prop-valid" "^0.8.2"
|
||||||
|
|
||||||
|
framesync@6.0.1:
|
||||||
|
version "6.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/framesync/-/framesync-6.0.1.tgz#5e32fc01f1c42b39c654c35b16440e07a25d6f20"
|
||||||
|
integrity sha512-fUY88kXvGiIItgNC7wcTOl0SNRCVXMKSWW2Yzfmn7EKNc+MpCzcz9DhdHcdjbrtN3c6R4H5dTY2jiCpPdysEjA==
|
||||||
|
dependencies:
|
||||||
|
tslib "^2.1.0"
|
||||||
|
|
||||||
fresh@0.5.2:
|
fresh@0.5.2:
|
||||||
version "0.5.2"
|
version "0.5.2"
|
||||||
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
|
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
|
||||||
|
@ -9816,6 +9902,11 @@ he@^1.2.0:
|
||||||
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
|
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
|
||||||
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
|
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
|
||||||
|
|
||||||
|
hey-listen@^1.0.8:
|
||||||
|
version "1.0.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/hey-listen/-/hey-listen-1.0.8.tgz#8e59561ff724908de1aa924ed6ecc84a56a9aa68"
|
||||||
|
integrity sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==
|
||||||
|
|
||||||
highlight.js@^10.4.1, highlight.js@~10.7.0:
|
highlight.js@^10.4.1, highlight.js@~10.7.0:
|
||||||
version "10.7.3"
|
version "10.7.3"
|
||||||
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531"
|
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531"
|
||||||
|
@ -13390,6 +13481,16 @@ polished@^4.2.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.17.8"
|
"@babel/runtime" "^7.17.8"
|
||||||
|
|
||||||
|
popmotion@11.0.3:
|
||||||
|
version "11.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/popmotion/-/popmotion-11.0.3.tgz#565c5f6590bbcddab7a33a074bb2ba97e24b0cc9"
|
||||||
|
integrity sha512-Y55FLdj3UxkR7Vl3s7Qr4e9m0onSnP8W7d/xQLsoJM40vs6UKHFdygs6SWryasTZYqugMjm3BepCF4CWXDiHgA==
|
||||||
|
dependencies:
|
||||||
|
framesync "6.0.1"
|
||||||
|
hey-listen "^1.0.8"
|
||||||
|
style-value-types "5.0.0"
|
||||||
|
tslib "^2.1.0"
|
||||||
|
|
||||||
posix-character-classes@^0.1.0:
|
posix-character-classes@^0.1.0:
|
||||||
version "0.1.1"
|
version "0.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
|
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
|
||||||
|
@ -16410,6 +16511,14 @@ style-to-object@0.3.0, style-to-object@^0.3.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
inline-style-parser "0.1.1"
|
inline-style-parser "0.1.1"
|
||||||
|
|
||||||
|
style-value-types@5.0.0:
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/style-value-types/-/style-value-types-5.0.0.tgz#76c35f0e579843d523187989da866729411fc8ad"
|
||||||
|
integrity sha512-08yq36Ikn4kx4YU6RD7jWEv27v4V+PUsOGa4n/as8Et3CuODMJQ00ENeAVXAeydX4Z2j1XHZF1K2sX4mGl18fA==
|
||||||
|
dependencies:
|
||||||
|
hey-listen "^1.0.8"
|
||||||
|
tslib "^2.1.0"
|
||||||
|
|
||||||
stylehacks@^5.1.0:
|
stylehacks@^5.1.0:
|
||||||
version "5.1.0"
|
version "5.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-5.1.0.tgz#a40066490ca0caca04e96c6b02153ddc39913520"
|
resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-5.1.0.tgz#a40066490ca0caca04e96c6b02153ddc39913520"
|
||||||
|
@ -16967,7 +17076,7 @@ tslib@^1.8.1:
|
||||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
||||||
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
||||||
|
|
||||||
tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0:
|
tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.1, tslib@^2.4.0:
|
||||||
version "2.4.0"
|
version "2.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
|
||||||
integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
|
integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
|
||||||
|
|
Loading…
Reference in New Issue
Block a user