mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 13:59:08 +00:00
Better display of epochs
This commit is contained in:
parent
bea445bafa
commit
e3129032e8
8 changed files with 216 additions and 70 deletions
|
|
@ -0,0 +1,35 @@
|
|||
import clsx from "clsx";
|
||||
import { EventEpoch } from "../../engine/engine";
|
||||
|
||||
interface EpochCellProps {
|
||||
noLeadingText?: boolean;
|
||||
epoch: EventEpoch;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const EPOCH_STYLES_ARRAY = [
|
||||
"text-slate-900",
|
||||
"font-mono",
|
||||
"bg-slate-200",
|
||||
"p-1",
|
||||
"py-0",
|
||||
"rounded-sm",
|
||||
"ring-1",
|
||||
"ring-slate-500",
|
||||
"text-sm",
|
||||
];
|
||||
|
||||
export const EPOCH_STYLES = clsx(...EPOCH_STYLES_ARRAY);
|
||||
|
||||
export default function EpochCell({
|
||||
epoch,
|
||||
className,
|
||||
noLeadingText,
|
||||
}: EpochCellProps) {
|
||||
return (
|
||||
<div className={clsx(EPOCH_STYLES, className)}>
|
||||
{noLeadingText ? "" : "Epoch "}
|
||||
{epoch}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,20 +1,20 @@
|
|||
import { EventIndex } from "../../engine/engine";
|
||||
import { EventEpoch } from "../../engine/engine";
|
||||
import { Variable } from "../../schema";
|
||||
import { VariableElPretty } from "../Common/Variable";
|
||||
import { CommonProps } from "./types";
|
||||
|
||||
interface VariableProps extends CommonProps {
|
||||
index: EventIndex;
|
||||
epoch: EventEpoch;
|
||||
variable: Variable;
|
||||
}
|
||||
|
||||
export function VariableEl({
|
||||
engine,
|
||||
toggleVariableVis,
|
||||
index,
|
||||
epoch,
|
||||
variable,
|
||||
}: VariableProps): JSX.Element {
|
||||
engine.stepTo(index);
|
||||
engine.stepTo(epoch);
|
||||
return (
|
||||
<VariableElPretty
|
||||
variable={variable}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import type { Engine } from "../../engine/engine";
|
||||
import type { Engine, EventEpoch } from "../../engine/engine";
|
||||
import type { Variable } from "../../schema";
|
||||
|
||||
export interface CommonProps {
|
||||
currentEpoch: EventEpoch;
|
||||
engine: Engine;
|
||||
toggleVariableVis: (variable: Variable) => void;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
import clsx from "clsx";
|
||||
import React from "react";
|
||||
import { EventIndex } from "../engine/engine";
|
||||
import React, { useCallback, useMemo, useState } from "react";
|
||||
import { EventEpoch } from "../engine/engine";
|
||||
import { lastSubEvent } from "../engine/event_util";
|
||||
import { UnificationMode, Event } from "../schema";
|
||||
import { Refine } from "../utils/refine";
|
||||
import EpochCell from "./Common/EpochCell";
|
||||
import { CommonProps } from "./EventItem/types";
|
||||
import { VariableEl } from "./EventItem/Variable";
|
||||
|
||||
|
|
@ -51,86 +52,174 @@ const UN_UNKNOWN = "💭";
|
|||
const UN_SUCCESS = "✅";
|
||||
const UN_FAILURE = "❌";
|
||||
|
||||
function epochInRange(
|
||||
epoch: EventEpoch,
|
||||
[start, end]: [EventEpoch, EventEpoch]
|
||||
): boolean {
|
||||
return epoch >= start && epoch <= end;
|
||||
}
|
||||
|
||||
interface UnificationProps extends CommonProps {
|
||||
event: Refine<Event, "Unification">;
|
||||
}
|
||||
|
||||
function Unification(props: UnificationProps): JSX.Element {
|
||||
const { engine, event } = props;
|
||||
const { engine, event, currentEpoch } = props;
|
||||
const { mode, subevents, success } = event;
|
||||
|
||||
const beforeUnificationIndex = engine.getEventIndex(event);
|
||||
const afterUnificationIndex = engine.getEventIndex(lastSubEvent(event));
|
||||
const beforeUnificationEpoch = engine.getEventIndex(event);
|
||||
const afterUnificationEpoch = engine.getEventIndex(lastSubEvent(event));
|
||||
|
||||
const leftVar = (index: EventIndex) => (
|
||||
<VariableEl {...props} index={index} variable={event.left} />
|
||||
const containsCurrentEpoch = epochInRange(currentEpoch, [
|
||||
beforeUnificationEpoch,
|
||||
afterUnificationEpoch,
|
||||
]);
|
||||
|
||||
const leftVar = useMemo(
|
||||
() => (epoch: EventEpoch) =>
|
||||
<VariableEl {...props} epoch={epoch} variable={event.left} />,
|
||||
[event.left, props]
|
||||
);
|
||||
const rightVar = (index: EventIndex) => (
|
||||
<VariableEl {...props} index={index} variable={event.right} />
|
||||
const rightVar = useMemo(
|
||||
() => (epoch: EventEpoch) =>
|
||||
<VariableEl {...props} epoch={epoch} variable={event.right} />,
|
||||
[event.right, props]
|
||||
);
|
||||
|
||||
const [isOpen, setIsOpen] = React.useState(false);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const modeIcon = <UnificationModeIcon mode={mode} />;
|
||||
const modeIcon = useMemo(() => <UnificationModeIcon mode={mode} />, [mode]);
|
||||
|
||||
const resultIcon = success ? UN_SUCCESS : UN_FAILURE;
|
||||
const resultHeadline = <Headline icon={resultIcon}></Headline>;
|
||||
const topHeadline = (
|
||||
<Headline icon={isOpen ? UN_UNKNOWN : resultIcon}></Headline>
|
||||
|
||||
const epochCell = useMemo(() => {
|
||||
if (!containsCurrentEpoch) return null;
|
||||
return (
|
||||
<EpochCell
|
||||
noLeadingText
|
||||
epoch={currentEpoch}
|
||||
className="inline-block align-middle mr-2"
|
||||
></EpochCell>
|
||||
);
|
||||
}, [containsCurrentEpoch, currentEpoch]);
|
||||
|
||||
const getHeadline = useCallback(
|
||||
({
|
||||
epoch,
|
||||
includeEpochIfInRange,
|
||||
}: {
|
||||
epoch: EventEpoch;
|
||||
includeEpochIfInRange: boolean;
|
||||
}) => {
|
||||
const topHeadline = (
|
||||
<Headline icon={isOpen ? UN_UNKNOWN : resultIcon}></Headline>
|
||||
);
|
||||
|
||||
const optEpochCell =
|
||||
includeEpochIfInRange && containsCurrentEpoch && epochCell;
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
className="w-full text-left whitespace-nowrap h-full"
|
||||
>
|
||||
{optEpochCell}
|
||||
<span
|
||||
className={clsx(
|
||||
"mr-2",
|
||||
isOpen ? "text-slate-500" : "text-slate-400"
|
||||
)}
|
||||
>
|
||||
{isOpen ? DROPDOWN_OPEN : DROPDOWN_CLOSED}
|
||||
</span>
|
||||
{topHeadline} {leftVar(epoch)} {modeIcon} {rightVar(epoch)}
|
||||
</button>
|
||||
);
|
||||
},
|
||||
[
|
||||
isOpen,
|
||||
resultIcon,
|
||||
containsCurrentEpoch,
|
||||
epochCell,
|
||||
leftVar,
|
||||
modeIcon,
|
||||
rightVar,
|
||||
]
|
||||
);
|
||||
|
||||
function getHeadline(index: EventIndex) {
|
||||
return (
|
||||
<button
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
className="w-full text-left whitespace-nowrap"
|
||||
>
|
||||
<span
|
||||
className={clsx("mr-2", isOpen ? "text-slate-500" : "text-slate-400")}
|
||||
>
|
||||
{isOpen ? DROPDOWN_OPEN : DROPDOWN_CLOSED}
|
||||
</span>
|
||||
{topHeadline} {leftVar(index)} {modeIcon} {rightVar(index)}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isOpen) {
|
||||
const headLine = getHeadline(afterUnificationIndex);
|
||||
return <div className={UNFOCUSED}>{headLine}</div>;
|
||||
const headLine = getHeadline({
|
||||
epoch: afterUnificationEpoch,
|
||||
includeEpochIfInRange: true,
|
||||
});
|
||||
return (
|
||||
<div className={clsx(!containsCurrentEpoch && UNFOCUSED)}>{headLine}</div>
|
||||
);
|
||||
} else {
|
||||
const optEpochCellAfter =
|
||||
afterUnificationEpoch === currentEpoch && epochCell;
|
||||
const optEpochCellBefore =
|
||||
beforeUnificationEpoch === currentEpoch && epochCell;
|
||||
|
||||
const headlineBefore = getHeadline({
|
||||
epoch: beforeUnificationEpoch,
|
||||
includeEpochIfInRange: false,
|
||||
});
|
||||
|
||||
const dropdownTransparent = (
|
||||
<span className="text-transparent mr-2">{DROPDOWN_OPEN}</span>
|
||||
);
|
||||
|
||||
const headlineBefore = getHeadline(beforeUnificationIndex);
|
||||
|
||||
const headlineAfter = (
|
||||
<div className={clsx(MT, "whitespace-nowrap")}>
|
||||
<div className={clsx("whitespace-nowrap")}>
|
||||
{dropdownTransparent}
|
||||
{resultHeadline} {leftVar(afterUnificationIndex)} {modeIcon}{" "}
|
||||
{rightVar(afterUnificationIndex)}
|
||||
{resultHeadline} {leftVar(afterUnificationEpoch)} {modeIcon}{" "}
|
||||
{rightVar(afterUnificationEpoch)}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"relative z-[1]",
|
||||
"before:content-[''] before:border-l before:border-slate-500 before:z-[-1]",
|
||||
"before:absolute before:w-0 before:h-[calc(100%-1.5rem)] before:top-[1rem] before:left-[0.3rem]"
|
||||
)}
|
||||
>
|
||||
<div>{headlineBefore}</div>
|
||||
<EventList {...props} root={false} engine={engine} events={subevents} />
|
||||
{headlineAfter}
|
||||
<div className="grid gap-0 grid-cols-[min-content_min-content_auto]">
|
||||
{/* Row 1: unification start */}
|
||||
<div className="row-start-1 col-start-1">{optEpochCellBefore}</div>
|
||||
<div className="row-start-1 col-start-3">{headlineBefore}</div>
|
||||
|
||||
{/* Row 2: inner traces */}
|
||||
<div className="row-start-2 col-start-1"></div>
|
||||
<div className="row-start-2 col-start-3">
|
||||
<EventList
|
||||
{...props}
|
||||
root={false}
|
||||
engine={engine}
|
||||
events={subevents}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Row 3: inner traces */}
|
||||
<div className="row-start-3 col-start-1">{optEpochCellAfter}</div>
|
||||
<div className="row-start-3 col-start-3">{headlineAfter}</div>
|
||||
|
||||
{/* Col 2: dropdown line */}
|
||||
<div
|
||||
className={clsx(
|
||||
"row-start-1 row-end-4 col-start-2 h-full",
|
||||
"relative z-[1] h-full",
|
||||
"before:content-[''] before:border-l before:border-slate-500 before:z-[-1]",
|
||||
"before:absolute before:w-0 before:h-[calc(100%-1.5rem)] before:top-[1rem] before:left-[0.3rem]"
|
||||
)}
|
||||
></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function Headline({ icon }: { icon: string }): JSX.Element {
|
||||
return <span className="">{icon}</span>;
|
||||
return (
|
||||
<div className="inline-block align-middle">
|
||||
<div className="">{icon}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function UnificationModeIcon({ mode }: { mode: UnificationMode }): JSX.Element {
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import VariableNode, {
|
|||
import { SubsSnapshot } from "../../engine/subs";
|
||||
import { KeydownHandler } from "../Events";
|
||||
import { TypedEmitter } from "tiny-typed-emitter";
|
||||
import EpochCell from "../Common/EpochCell";
|
||||
|
||||
export interface VariablesGraphProps {
|
||||
subs: SubsSnapshot;
|
||||
|
|
@ -445,6 +446,9 @@ function Graph({
|
|||
hideAttribution: true,
|
||||
}}
|
||||
>
|
||||
<Panel position="top-left">
|
||||
<EpochCell epoch={subs.epoch}></EpochCell>
|
||||
</Panel>
|
||||
<Panel position="top-right">
|
||||
<LayoutPanel
|
||||
layoutConfig={layoutConfig}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React from "react";
|
||||
import React, { useState } from "react";
|
||||
import { AllEvents, Variable } from "../schema";
|
||||
import { Engine } from "../engine/engine";
|
||||
import EventList from "./EventList";
|
||||
|
|
@ -29,7 +29,9 @@ export default function Ui({ events }: UiProps): JSX.Element {
|
|||
});
|
||||
|
||||
engine.stepTo(engine.lastEventIndex());
|
||||
const subs = engine.subs.snapshot();
|
||||
const subs = engine.subsSnapshot();
|
||||
|
||||
const [epoch, setEpoch] = useState(subs.epoch);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
@ -46,6 +48,7 @@ export default function Ui({ events }: UiProps): JSX.Element {
|
|||
toggleVariableVis={(variable: Variable) =>
|
||||
ee.emit("toggleVariable", variable)
|
||||
}
|
||||
currentEpoch={epoch}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-1 min-h-[50%] h-full">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue