Skip to content

Commit

Permalink
feat: add a secondary action to the accordion + allow toggle goal con…
Browse files Browse the repository at this point in the history
…text

When a goal is expanded, a secondary action appears next to the collapse/expand
button. This button represented by a closed/open eye allows to show/hide the
goal context (hyps).
  • Loading branch information
rtetley committed Dec 11, 2024
1 parent 9140b11 commit 59ecec2
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 33 deletions.
30 changes: 22 additions & 8 deletions client/goal-view-ui/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, {useState, useCallback, useEffect} from 'react';
import "./App.css";

import ProofViewPage from './components/templates/ProovViewPage';
import ProofViewPage from './components/templates/ProofViewPage';
import {Goal, ProofViewGoals, ProofViewGoalsKey, ProofViewMessage} from './types';

import { vscode } from "./utilities/vscode";
Expand Down Expand Up @@ -30,16 +30,16 @@ const app = () => {
? allGoals
: {
main: allGoals.goals.map((goal: Goal, index: number) => {
return {...goal, isOpen: true};
return {...goal, isOpen: true, isContextHidden: index !== 0};
}),
shelved: allGoals.shelvedGoals.map((goal: Goal) => {
return {...goal, isOpen: true};
shelved: allGoals.shelvedGoals.map((goal: Goal, index: number) => {
return {...goal, isOpen: true, isContextHidden: index !== 0};
}),
givenUp: allGoals.givenUpGoals.map((goal: Goal) => {
return {...goal, isOpen: true};
givenUp: allGoals.givenUpGoals.map((goal: Goal, index: number) => {
return {...goal, isOpen: true, isContextHidden: index !== 0};
}),
unfocused: allGoals.unfocusedGoals.map((goal: Goal) => {
return {...goal, isOpen: false};
unfocused: allGoals.unfocusedGoals.map((goal: Goal, index: number) => {
return {...goal, isOpen: false, isContextHidden: index !== 0};
})
}
);
Expand Down Expand Up @@ -72,6 +72,19 @@ const app = () => {
});
};

const toggleContext = (id: string, key: ProofViewGoalsKey) => {
const newGoals = goals![key].map(goal => {
if(goal.id === id){
return {...goal, isContextHidden: !goal.isContextHidden};
}
return goal;
});
setGoals({
...goals!,
[key]: newGoals
});
};

const settingsClickHandler = () => {
vscode.postMessage({
command: "openGoalSettings",
Expand All @@ -89,6 +102,7 @@ const app = () => {
settingsClickHandler={settingsClickHandler}
helpMessage={helpMessage}
helpMessageHandler={(message: string) => setHelpMessage(message)}
toggleContextHandler={toggleContext}
/>
</main>
);
Expand Down
18 changes: 14 additions & 4 deletions client/goal-view-ui/src/components/atoms/Accordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ type AccordionProps = {
collapsed: boolean;
title: string;
collapseHandler?: (params: any) => void;
seconaryActionHandler?: (params: any) => void;
seconaryActionIcon?: JSX.Element;
};

const accordion: FunctionComponent<AccordionProps> = (props) => {

const {title, collapseHandler} = props;
const {title, collapseHandler, seconaryActionHandler, seconaryActionIcon} = props;
const [collapsed, setCollapsed] = useState(props.collapsed);
useEffect(() => {
setCollapsed(props.collapsed);
Expand All @@ -25,15 +27,23 @@ const accordion: FunctionComponent<AccordionProps> = (props) => {

const toggleCollapse = collapseHandler ? collapseHandler : () => setCollapsed(!collapsed);

const secondaryActionButton = (seconaryActionHandler !== undefined && seconaryActionIcon !== undefined) ? <VSCodeButton appearance={'icon'} onClick={seconaryActionHandler} className={classes.Demphasize}> {seconaryActionIcon} </VSCodeButton> : null;
const secondaryAction = collapsed ? null : secondaryActionButton;
const actionRow =
<div>
{secondaryAction}
<VSCodeButton appearance={'icon'} ariaLabel='Collapse' onClick={toggleCollapse}>
<VscChevronDown className={!collapsed ? [classes.Rotated, classes.Demphasize].join(' ') : ''} />
</VSCodeButton>
</div>;

return (
<div className={classes.Block}>

{/* The header with title and button */}
<div className={classes.Header}>
<span className={!collapsed ? classes.Demphasize : ''}>{title}</span>
<VSCodeButton appearance={'icon'} ariaLabel='Collapse' onClick={toggleCollapse}>
<VscChevronDown className={!collapsed ? [classes.Rotated, classes.Demphasize].join(' ') : ''} />
</VSCodeButton>
{actionRow}
</div>

{/* The panel that collapses */}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import React, {FunctionComponent} from 'react';

import { VscEye, VscEyeClosed } from 'react-icons/vsc';

import GoalBlock from './GoalBlock';
import Accordion from '../atoms/Accordion';
import { CollapsibleGoal } from '../../types';

type CollapsibleGoalBlockProps = {
goal: CollapsibleGoal,
collapseHandler: (id: string) => void,
collapseHandler: (id: string) => void,
toggleContextHandler: (id:string) => void,
goalIndex: number,
goalIndicator: string,
maxDepth: number,
Expand All @@ -15,11 +18,18 @@ type CollapsibleGoalBlockProps = {

const collapsibleGoalBlock: FunctionComponent<CollapsibleGoalBlockProps> = (props) => {

const {goal, goalIndex, goalIndicator, collapseHandler, maxDepth, helpMessageHandler} = props;
const {goal, goalIndex, goalIndicator, collapseHandler, toggleContextHandler, maxDepth, helpMessageHandler} = props;

const secondaryActionIcon = goal.isContextHidden ? <VscEye /> : <VscEyeClosed />;
const secondaryActionHandler = toggleContextHandler !== undefined ? () => toggleContextHandler(goal.id) : undefined;

return (
<Accordion title={"Goal " + goalIndex} collapsed={!goal.isOpen} collapseHandler={() => collapseHandler(goal.id)}>
<GoalBlock goal={goal} goalIndicator={goalIndicator} maxDepth={maxDepth} helpMessageHandler={helpMessageHandler} displayHyps={goalIndex === 1}/>
<Accordion title={"Goal " + goalIndex} collapsed={!goal.isOpen}
collapseHandler={() => collapseHandler(goal.id)}
seconaryActionHandler={secondaryActionHandler}
seconaryActionIcon={secondaryActionIcon}
>
<GoalBlock goal={goal} goalIndicator={goalIndicator} maxDepth={maxDepth} helpMessageHandler={helpMessageHandler} displayHyps={!goal.isContextHidden}/>
</Accordion>
);

Expand Down
6 changes: 4 additions & 2 deletions client/goal-view-ui/src/components/molecules/GoalBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ const goalBlock: FunctionComponent<GoalBlockProps> = (props) => {
const {goal, goalIndicator, maxDepth, displayHyps, helpMessageHandler} = props;
const indicator = goalIndicator ? <span className={classes.GoalIndex} >({goalIndicator})</span> : null;
const hyps = displayHyps ? <HypothesesBlock hypotheses={goal.hypotheses} maxDepth={maxDepth}/> : null;
const sep = displayHyps ? <div className={classes.SeparatorZone}> {indicator} <Separator /> </div> : null;

return (
<div className={classes.Block}>
{hyps}
{sep}
<div className={classes.SeparatorZone}>
{indicator}
<Separator />
</div>
<GoalComponent goal={goal.goal} maxDepth={maxDepth} setHelpMessage={helpMessageHandler}/>
</div>
);
Expand Down
19 changes: 16 additions & 3 deletions client/goal-view-ui/src/components/organisms/GoalCollapsibles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ import classes from './GoalCollapsibles.module.css';
type GoalSectionProps = {
goals: CollapsibleGoal[],
collapseGoalHandler: (id: string) => void,
toggleContextHandler: (id: string) => void,
maxDepth: number,
helpMessageHandler: (message: string) => void
};

const goalSection: FunctionComponent<GoalSectionProps> = (props) => {

const {goals, collapseGoalHandler, maxDepth, helpMessageHandler} = props;
const {goals, collapseGoalHandler, toggleContextHandler, maxDepth, helpMessageHandler} = props;
const firstGoalRef = useRef<HTMLDivElement>(null);

useEffect(() => {
Expand All @@ -36,14 +37,26 @@ const goalSection: FunctionComponent<GoalSectionProps> = (props) => {
if(index === 0) {
return (
<>
<CollapsibleGoalBlock goal={goal} goalIndex={index + 1} goalIndicator={index + 1 + " / " + goals.length} collapseHandler={collapseGoalHandler} maxDepth={maxDepth} helpMessageHandler={helpMessageHandler}/>
<CollapsibleGoalBlock
goal={goal} goalIndex={index + 1} goalIndicator={index + 1 + " / " + goals.length}
collapseHandler={collapseGoalHandler}
toggleContextHandler={toggleContextHandler}
helpMessageHandler={helpMessageHandler}
maxDepth={maxDepth}
/>
<div ref={firstGoalRef}/>
</>
);
}

return (
<CollapsibleGoalBlock goal={goal} goalIndex={index + 1} goalIndicator={index + 1 + " / " + goals.length} collapseHandler={collapseGoalHandler} maxDepth={maxDepth} helpMessageHandler={helpMessageHandler}/>
<CollapsibleGoalBlock
goal={goal} goalIndex={index + 1} goalIndicator={index + 1 + " / " + goals.length}
collapseHandler={collapseGoalHandler}
toggleContextHandler={toggleContextHandler}
maxDepth={maxDepth}
helpMessageHandler={helpMessageHandler}
/>
);
});

Expand Down
16 changes: 12 additions & 4 deletions client/goal-view-ui/src/components/organisms/GoalSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import classes from './GoalSection.module.css';

type GoalSectionProps = {
goals: CollapsibleGoal[],
collapseGoalHandler: (id: string) => void,
collapseGoalHandler: (id: string) => void,
toggleContextHandler: (id: string) => void,
displaySetting: string;
emptyMessage: string;
emptyIcon?: JSX.Element;
Expand All @@ -20,7 +21,7 @@ type GoalSectionProps = {

const goalSection: FunctionComponent<GoalSectionProps> = (props) => {

const {goals, collapseGoalHandler, displaySetting, unfocusedGoals, emptyMessage, emptyIcon, maxDepth, helpMessageHandler} = props;
const {goals, collapseGoalHandler, toggleContextHandler, displaySetting, unfocusedGoals, emptyMessage, emptyIcon, maxDepth, helpMessageHandler} = props;
const emptyMessageRef = useRef<HTMLDivElement>(null);

useEffect(() => {
Expand All @@ -46,15 +47,22 @@ const goalSection: FunctionComponent<GoalSectionProps> = (props) => {
Next unfocused goals (focus with bullet):
</div>
<div ref={emptyMessageRef}/>
<GoalCollapsibleSection goals={unfocusedGoals} collapseGoalHandler={collapseGoalHandler} maxDepth={maxDepth} helpMessageHandler={helpMessageHandler} />
<GoalCollapsibleSection goals={unfocusedGoals}
collapseGoalHandler={collapseGoalHandler}
toggleContextHandler={toggleContextHandler}
maxDepth={maxDepth} helpMessageHandler={helpMessageHandler} />
</div>
: <>
<EmptyState message={emptyMessage} icon={emptyIcon} />
<div ref={emptyMessageRef}/>
</>
: displaySetting === 'Tabs' ?
<GoalTabSection goals={goals} maxDepth={maxDepth} helpMessageHandler={helpMessageHandler}/>
: <GoalCollapsibleSection goals={goals} collapseGoalHandler={collapseGoalHandler} maxDepth={maxDepth} helpMessageHandler={helpMessageHandler}/>;
: <GoalCollapsibleSection goals={goals}
collapseGoalHandler={collapseGoalHandler}
maxDepth={maxDepth} helpMessageHandler={helpMessageHandler}
toggleContextHandler={toggleContextHandler}
/>;

return section;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ import classes from './GoalPage.module.css';
import { VscPass, VscWarning } from 'react-icons/vsc';

type ProofViewPageProps = {
goals: ProofViewGoals,
messages: ProofViewMessage[],
collapseGoalHandler: (id: string, key: ProofViewGoalsKey) => void,
goals: ProofViewGoals;
messages: ProofViewMessage[];
collapseGoalHandler: (id: string, key: ProofViewGoalsKey) => void;
toggleContextHandler: (id: string, key: ProofViewGoalsKey) => void;
displaySetting: string;
maxDepth: number;
settingsClickHandler: () => void;
Expand All @@ -32,7 +33,11 @@ type ProofViewPageProps = {

const proofViewPage: FunctionComponent<ProofViewPageProps> = (props) => {

const {goals, messages, displaySetting, collapseGoalHandler, maxDepth, settingsClickHandler, helpMessage, helpMessageHandler} = props;
const {goals, messages, displaySetting,
collapseGoalHandler, maxDepth, settingsClickHandler,
helpMessage, helpMessageHandler,
toggleContextHandler,
} = props;

const renderGoals = () => {
const goalBadge = <VSCodeBadge>{goals!.main.length}</VSCodeBadge>;
Expand All @@ -52,6 +57,7 @@ const proofViewPage: FunctionComponent<ProofViewPageProps> = (props) => {
goals={goals!.main}
unfocusedGoals={goals!.unfocused}
collapseGoalHandler={(id) => collapseGoalHandler(id, goals!.main.length ? ProofViewGoalsKey.main : ProofViewGoalsKey.unfocused)}
toggleContextHandler={(id) => toggleContextHandler(id, goals!.main.length ? ProofViewGoalsKey.main : ProofViewGoalsKey.unfocused)}
displaySetting={displaySetting}
emptyMessage={
goals!.shelved.length ? "There are shelved goals. Try using `Unshelve.`" :
Expand All @@ -69,8 +75,9 @@ const proofViewPage: FunctionComponent<ProofViewPageProps> = (props) => {
<VSCodePanelView className={classes.View}>
<GoalSection
key="shelved"
goals={goals!.shelved}
collapseGoalHandler={(id) => collapseGoalHandler(id, ProofViewGoalsKey.shelved)}
goals={goals!.shelved}
collapseGoalHandler={(id) => collapseGoalHandler(id, ProofViewGoalsKey.shelved)}
toggleContextHandler={(id) => toggleContextHandler(id, ProofViewGoalsKey.shelved)}
displaySetting={displaySetting}
emptyMessage='There are no shelved goals'
maxDepth={maxDepth}
Expand All @@ -80,8 +87,9 @@ const proofViewPage: FunctionComponent<ProofViewPageProps> = (props) => {
<VSCodePanelView className={classes.View}>
<GoalSection
key="givenup"
goals={goals!.givenUp}
collapseGoalHandler={(id) => collapseGoalHandler(id, ProofViewGoalsKey.givenUp)}
goals={goals!.givenUp}
collapseGoalHandler={(id) => collapseGoalHandler(id, ProofViewGoalsKey.givenUp)}
toggleContextHandler={(id) => toggleContextHandler(id, ProofViewGoalsKey.givenUp)}
displaySetting={displaySetting}
emptyMessage='There are no given up goals'
maxDepth={maxDepth}
Expand Down
1 change: 1 addition & 0 deletions client/goal-view-ui/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export interface Goal {

export interface CollapsibleGoal extends Goal {
isOpen: boolean;
isContextHidden: boolean;
};

export type ProofViewGoalsType = {
Expand Down

0 comments on commit 59ecec2

Please sign in to comment.