Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Default indicators hidden #595

Merged
merged 8 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,15 @@
"error",
{
"cssFiles": [], // don't need this anyway, but the reasonable default causes perf issues https://github.com/francoismassart/eslint-plugin-tailwindcss/issues/276#issuecomment-2076947699
"whitelist": ["nopan", "react\\-flow.*", "selected", "flashlight-mode", "spotlight\\-"]
"whitelist": [
"nopan",
"react\\-flow.*",
"selected",
"flashlight-mode",
"spotlight\\-",
"diagram-node",
"diagram-edge"
]
}
]
}
Expand Down
21 changes: 20 additions & 1 deletion docs-site/pages/getting-started/diagramming-basics.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,17 @@ At the center of your diagram, there's a purple node with a few details on it:
2. the node's type - conveys what kind of concept the node represents. In this case, this node represents a Problem.
3. the node's indicators - these indicate some information; you can see what each indicator does [here](/features/indicators).

<Callout type="info">
Indicators are off by default to reduce clutter, but you can show them by clicking the "Show
indicators" button in the toolbar:

<Image
src="https://github.com/user-attachments/assets/e07fdfc3-0a66-4f60-84ff-804b229b2994"
width={426}
height={155}
/>
</Callout>

You might want to start your Topic with a solution proposal, or a question, or something else, but the default is starting with a problem. For our example, we're going to model a situation where cars are going too fast in a neighborhood, and we're worried about the safety of the neighborhood's children.

### Editing a node's text
Expand Down Expand Up @@ -72,7 +83,15 @@ When you're modeling your Topic, you can add nodes in whatever order they come t

## Scoring to convey opinion

One key feature in Ameliorate is that you can score nodes to convey how important you think they are. To score a node, hover over or click the circular score in the node's upper-right corner, and click on a score.
One key feature in Ameliorate is that you can score nodes to convey how important you think they are. To score a node, first show scores by clicking the "Show indicators" button in the toolbar, then hover over or click the score in the node's upper-right corner, and click on a score.

<Image
src="https://github.com/user-attachments/assets/e07fdfc3-0a66-4f60-84ff-804b229b2994"
width={426}
height={155}
/>

<br />

<Image
src="https://github.com/amelioro/ameliorate/assets/13872370/dc5063e6-a0ec-4d6a-b7fd-a988a166e63e"
Expand Down
12 changes: 8 additions & 4 deletions src/web/topic/components/Edge/ScoreEdge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { useUserCanEditTopicData } from "@/web/topic/store/userHooks";
import { Edge } from "@/web/topic/utils/graph";
import { useUnrestrictedEditing } from "@/web/view/actionConfigStore";
import { setSelected, useDrawSimpleEdgePaths } from "@/web/view/currentViewStore/store";
import { useZenMode } from "@/web/view/userConfigStore";
import { useShowIndicators } from "@/web/view/userConfigStore";

const flowMarkerId = "flowMarker";
const nonFlowMarkerId = "nonFlowMarker";
Expand Down Expand Up @@ -98,7 +98,7 @@ export const ScoreEdge = ({ inReactFlow, ...flowEdge }: EdgeProps & Props) => {
const userCanEditTopicData = useUserCanEditTopicData(sessionUser?.username);

const unrestrictedEditing = useUnrestrictedEditing();
const zenMode = useZenMode();
const showIndicators = useShowIndicators();
const drawSimpleEdgePaths = useDrawSimpleEdgePaths();

const edge = convertToEdge(flowEdge);
Expand Down Expand Up @@ -155,8 +155,12 @@ export const ScoreEdge = ({ inReactFlow, ...flowEdge }: EdgeProps & Props) => {
className={
// pointer-events and cursor are set because this div is within an SVG and doesn't handle pointer-events properly by default
"[pointer-events:all] cursor-default flex flex-col items-center justify-center bg-white p-1 rounded-xl" +
// during zenMode, div only contains the label text (no indicators), so border doesn't seem necessary (if this looks awkward, we can always show border instead)
(zenMode && spotlight === "normal" ? " border-none" : "")
// when hiding indicators, div only contains the label text, so border doesn't seem necessary (if this looks awkward, we can always show border instead)
(!showIndicators && spotlight === "normal" ? " border-none" : "") +
// allow other components to apply conditional css related to this edge, e.g. when it's hovered/selected
// separate from react-flow__edge because sometimes edges are rendered outside of react-flow (e.g. details pane), and we still want to style these
" diagram-edge" +
(flowEdge.selected ? " selected" : "")
}
>
<CommonIndicators graphPart={edge} notes={edge.data.notes} />
Expand Down
25 changes: 7 additions & 18 deletions src/web/topic/components/Indicator/CommonIndicators.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,26 @@
import { Stack } from "@mui/material";
import { memo, useContext } from "react";
import { memo } from "react";

import { ContextIndicator } from "@/web/topic/components/Indicator/ContextIndicator";
import { CriteriaTableIndicator } from "@/web/topic/components/Indicator/CriteriaTableIndicator";
import { DetailsIndicator } from "@/web/topic/components/Indicator/DetailsIndicator";
import { Score } from "@/web/topic/components/Score/Score";
import { WorkspaceContext } from "@/web/topic/components/TopicWorkspace/WorkspaceContext";
import { GraphPart } from "@/web/topic/utils/graph";
import { useZenMode } from "@/web/view/userConfigStore";

interface Props {
graphPart: GraphPart;
notes: string;
}

const CommonIndicatorsBase = ({ graphPart, notes }: Props) => {
const zenMode = useZenMode();
const workspaceContext = useContext(WorkspaceContext);

return (
<Stack direction="row" margin="2px" spacing="2px">
{!zenMode && (
<>
{/* TODO: should this be moved because it's not used for all graph parts? */}
<ContextIndicator graphPart={graphPart} />
{/* TODO: should this be moved because it's only used for problem? */}
<CriteriaTableIndicator nodeId={graphPart.id} />
<DetailsIndicator graphPartId={graphPart.id} notes={notes} />
</>
)}

{/* table's purpose is mainly for scores, so show scores there even if in zen mode */}
{(!zenMode || workspaceContext === "table") && <Score graphPartId={graphPart.id} />}
{/* TODO: should this be moved because it's not used for all graph parts? */}
<ContextIndicator graphPart={graphPart} />
{/* TODO: should this be moved because it's only used for problem? */}
<CriteriaTableIndicator nodeId={graphPart.id} />
<DetailsIndicator graphPartId={graphPart.id} notes={notes} />
<Score graphPartId={graphPart.id} />
</Stack>
);
};
Expand Down
4 changes: 0 additions & 4 deletions src/web/topic/components/Indicator/ContentIndicators.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { FoundResearchIndicator } from "@/web/topic/components/Indicator/FoundRe
import { JustificationIndicator } from "@/web/topic/components/Indicator/JustificationIndicator";
import { QuestionIndicator } from "@/web/topic/components/Indicator/QuestionIndicator";
import { GraphPartType } from "@/web/topic/utils/graph";
import { useZenMode } from "@/web/view/userConfigStore";

interface Props {
graphPartId: string;
Expand All @@ -16,9 +15,6 @@ interface Props {
}

const ContentIndicatorsBase = ({ graphPartId, graphPartType, color, className }: Props) => {
const zenMode = useZenMode();
if (zenMode) return <></>;

return (
<Stack direction="row" margin="2px" spacing="2px" className={className}>
<QuestionIndicator graphPartId={graphPartId} partColor={color} />
Expand Down
14 changes: 13 additions & 1 deletion src/web/topic/components/Indicator/Indicator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@ import { MouseEventHandler } from "react";

import { StyledButton } from "@/web/topic/components/Indicator/Indicator.styles";
import { MuiIcon } from "@/web/topic/utils/node";
import { useShowIndicators } from "@/web/view/userConfigStore";

interface IndicatorProps {
Icon: MuiIcon;
title: string;
onClick?: MouseEventHandler<HTMLButtonElement>;
color?: ButtonProps["color"];
filled?: boolean;
/**
* true if this indicator is for a node or edge; example of false is JustificationTreeIndicator
*/
graphPartIndicator?: boolean;
}

export const Indicator = ({
Expand All @@ -18,7 +23,10 @@ export const Indicator = ({
onClick,
color = "neutral",
filled = true,
graphPartIndicator = true,
}: IndicatorProps) => {
const showIndicators = useShowIndicators();

const onClickHandler = (event: React.MouseEvent<HTMLButtonElement>) => {
if (onClick) {
// In most cases, we don't want the click to result in selecting the parent node.
Expand All @@ -28,6 +36,9 @@ export const Indicator = ({
}
};

// no need to hide indicators that aren't for graph parts e.g. JustificationTreeIndicator
const showIndicator = showIndicators || !graphPartIndicator;

return (
<>
<StyledButton
Expand All @@ -39,7 +50,8 @@ export const Indicator = ({
className={
// text-base seems to fit more snuggly than the default 14px
"border border-solid border-neutral-main text-base shadow-none" +
` ${!onClick ? "pointer-events-none" : ""}`
(!onClick ? " pointer-events-none" : "") +
(showIndicator ? "" : " hidden")
}
>
<Icon color="neutralContrast" fontSize="inherit" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const JustificationTreeIndicator = ({ graphPartId }: Props) => {
filled={justificationCount > 0}
title={title}
onClick={justificationCount > 0 ? onClick : undefined}
graphPartIndicator={false}
/>
);
};
5 changes: 1 addition & 4 deletions src/web/topic/components/Indicator/StatusIndicators.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { type ButtonProps, Stack } from "@mui/material";
import { memo } from "react";

import { ForceShownIndicator } from "@/web/topic/components/Indicator/ForceShownIndicator";
import { useIndicateWhenNodeForcedToShow, useZenMode } from "@/web/view/userConfigStore";
import { useIndicateWhenNodeForcedToShow } from "@/web/view/userConfigStore";

interface Props {
graphPartId: string;
Expand All @@ -15,9 +15,6 @@ interface Props {
*/
const StatusIndicatorsBase = ({ graphPartId, color, className }: Props) => {
const indicateWhenNodeForcedToShow = useIndicateWhenNodeForcedToShow();
const zenMode = useZenMode();

if (zenMode) return <></>;

return (
<Stack direction="row" margin="2px" spacing="2px" className={className}>
Expand Down
9 changes: 8 additions & 1 deletion src/web/topic/components/Node/EditableNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ interface Props {
const EditableNodeBase = ({ node, className = "" }: Props) => {
const { sessionUser } = useSessionUser();
const userCanEditTopicData = useUserCanEditTopicData(sessionUser?.username);

const unrestrictedEditing = useUnrestrictedEditing();
const fillNodesWithColor = useFillNodesWithColor();
const selected = useIsGraphPartSelected(node.id);
Expand Down Expand Up @@ -109,7 +110,13 @@ const EditableNodeBase = ({ node, className = "" }: Props) => {

return (
<NodeBox
className={className + (selected ? " selected" : "")}
className={
className +
// allow other components to apply conditional css related to this node, e.g. when it's hovered/selected
// separate from react-flow__node because sometimes nodes are rendered outside of react-flow (e.g. details pane), and we still want to style these
" diagram-node" +
(selected ? " selected" : "")
}
onClick={() => {
if (context != "details") setSelected(node.id);
}}
Expand Down
10 changes: 5 additions & 5 deletions src/web/topic/components/Node/FlowNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,17 @@ export const FlowNode = (flowNode: NodeProps) => {
setAnimated(true);
}, []);

const hoverShowAddButtons =
const showAddButtonsClasses =
"[.selectable:hover_>_&]:flex [.selectable:has(>_div_>_.selected)_>_&]:flex";

const positionParentButtons =
const positionParentButtonsClasses =
orientation === "DOWN"
? // have to use [arbitrary] tw values because can't apply two translate-x-* class names
// `left-1/2 top-0 -translate-x-1/2 translate-y-[calc(-100%-${nodeBridgeGap}px)]`
// also can't use `${nodeBridgeGap}` because tw classes are detected based on full class names being present in the source file https://tailwindcss.com/docs/content-configuration#dynamic-class-names
`left-1/2 top-0 -translate-x-1/2 translate-y-[calc(-100%-16px)]`
: `top-1/2 left-0 -translate-y-1/2 translate-x-[calc(-100%-16px)]`;
const positionChildButtons =
const positionChildButtonsClasses =
orientation === "DOWN"
? `left-1/2 bottom-0 -translate-x-1/2 translate-y-[calc(100%+16px)]`
: `top-1/2 right-0 -translate-y-1/2 translate-x-[calc(100%+16px)]`;
Expand All @@ -80,7 +80,7 @@ export const FlowNode = (flowNode: NodeProps) => {
fromNodeType={node.type}
as="parent"
orientation={orientation}
className={`absolute hidden ${hoverShowAddButtons} ${positionParentButtons}`}
className={`absolute hidden ${showAddButtonsClasses} ${positionParentButtonsClasses}`}
/>
)}

Expand All @@ -104,7 +104,7 @@ export const FlowNode = (flowNode: NodeProps) => {
fromNodeType={node.type}
as="child"
orientation={orientation}
className={`absolute hidden ${hoverShowAddButtons} ${positionChildButtons}`}
className={`absolute hidden ${showAddButtonsClasses} ${positionChildButtonsClasses}`}
/>
)}
</>
Expand Down
24 changes: 17 additions & 7 deletions src/web/topic/components/Node/NodeHandle.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Visibility } from "@mui/icons-material";
import { IconButton, Tooltip, Typography } from "@mui/material";
import { ReactNode, memo } from "react";
import { Handle, Position } from "reactflow";
import { Handle, Position, useStore } from "reactflow";

import { nodeTypes } from "@/common/node";
import { useSessionUser } from "@/web/common/hooks";
Expand All @@ -12,7 +12,7 @@ import { Node, RelationDirection } from "@/web/topic/utils/graph";
import { Orientation } from "@/web/topic/utils/layout";
import { nodeDecorations } from "@/web/topic/utils/node";
import { showNode } from "@/web/view/currentViewStore/filter";
import { useZenMode } from "@/web/view/userConfigStore";
import { useShowIndicators } from "@/web/view/userConfigStore";

const NodeSummary = ({ node, beforeSlot }: { node: Node; beforeSlot?: ReactNode }) => {
const { NodeIcon, title } = nodeDecorations[node.type];
Expand All @@ -38,20 +38,28 @@ const NodeHandleBase = ({ node, direction, orientation }: Props) => {
const { sessionUser } = useSessionUser();
const userCanEditTopicData = useUserCanEditTopicData(sessionUser?.username);

const zenMode = useZenMode();
const showIndicators = useShowIndicators();
const neighborsInDirection = useNeighborsInDirection(node.id, direction);
const hiddenNeighbors = useHiddenNodes(neighborsInDirection);

const type = direction === "parent" ? "target" : "source";
const isPotentiallyConnectingToThisHandle = useStore(
(state) => state.connectionStartHandle !== null && state.connectionStartHandle.type !== type,
);

// sort by node type the same way the nodeTypes array is ordered; thanks https://stackoverflow.com/a/44063445
const sortedHiddenNeighbors: Node[] = hiddenNeighbors.toSorted((a, b) => {
const diff = nodeTypes.indexOf(a.type) - nodeTypes.indexOf(b.type);
return diff;
});

const hasHiddenNeighbors = sortedHiddenNeighbors.length > 0;
const showHandle = !zenMode && (userCanEditTopicData || hasHiddenNeighbors);

const type = direction === "parent" ? "target" : "source";
const showHandle = isPotentiallyConnectingToThisHandle || (showIndicators && hasHiddenNeighbors);
// if editing, we should be able to see handles on-hover/-select so that we can create edges
const conditionalShowClasses = userCanEditTopicData
? // `String.raw` in order to allow underscores to be escaped for tailwind, so they don't get converted to spaces
String.raw` [.react-flow\_\_node:hover_&]:visible [.react-flow\_\_node.selected_&]:visible`
: "";

const position =
direction === "parent"
Expand Down Expand Up @@ -90,7 +98,9 @@ const NodeHandleBase = ({ node, direction, orientation }: Props) => {
position={position}
className={
"size-[10px]" +
(!showHandle ? " invisible" : "") +
// rely on `visibility` rather than `display` so that invisible handles can still render for react-flow's connection drawing
(showHandle ? "" : " invisible") +
conditionalShowClasses +
(hasHiddenNeighbors ? " bg-info-main" : "")
}
/>
Expand Down
12 changes: 10 additions & 2 deletions src/web/topic/components/Score/Score.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { useOnPlayground } from "@/web/topic/store/topicHooks";
import { userCanEditScores } from "@/web/topic/utils/score";
import { useReadonlyMode } from "@/web/view/actionConfigStore";
import { usePerspectives } from "@/web/view/perspectiveStore";
import { useShowIndicators } from "@/web/view/userConfigStore";

const circleDiameter = 6 * buttonDiameterRem; // no collisions for fitting 10 elements

Expand All @@ -28,9 +29,11 @@ interface ScoreProps {
export const Score = ({ graphPartId }: ScoreProps) => {
const { sessionUser } = useSessionUser();
const onPlayground = useOnPlayground();
const readonlyMode = useReadonlyMode();
const workspaceContext = useContext(WorkspaceContext);

const showIndicators = useShowIndicators();
const readonlyMode = useReadonlyMode();

const myUsername = onPlayground ? playgroundUsername : sessionUser?.username;
const perspectives = usePerspectives();
const canEdit = userCanEditScores(myUsername, perspectives, readonlyMode);
Expand Down Expand Up @@ -66,8 +69,12 @@ export const Score = ({ graphPartId }: ScoreProps) => {
) : undefined;
const isInteractive = hoverCircle !== undefined;

// always show score on table because the main purpose of the table is to compare scores
const showScore = showIndicators || workspaceContext === "table";
const showScoreClasses = showScore ? "" : " hidden";

if (!isInteractive) {
return <ScoreButton userScores={userScores} />;
return <ScoreButton userScores={userScores} className={showScoreClasses} />;
}

return (
Expand All @@ -80,6 +87,7 @@ export const Score = ({ graphPartId }: ScoreProps) => {
onMouseEnter={() => setHoverDelayHandler(setTimeout(() => setHovering(true), 100))}
onMouseLeave={() => clearTimeout(hoverDelayHandler)}
userScores={userScores}
className={showScoreClasses}
/>

<BackdropPopper
Expand Down
Loading
Loading