From a03a50a0b68c5cc4a28bbda91c9a88c47282e8df Mon Sep 17 00:00:00 2001 From: Joel Keyser Date: Fri, 6 Dec 2024 11:19:30 -0600 Subject: [PATCH 1/8] touchup: only show regular handle when connecting even if editing, always showing these isn't necessary. --- src/web/topic/components/Node/NodeHandle.tsx | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/web/topic/components/Node/NodeHandle.tsx b/src/web/topic/components/Node/NodeHandle.tsx index 1f09d2d5..893b4afb 100644 --- a/src/web/topic/components/Node/NodeHandle.tsx +++ b/src/web/topic/components/Node/NodeHandle.tsx @@ -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"; @@ -42,16 +42,24 @@ const NodeHandleBase = ({ node, direction, orientation }: Props) => { 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 || (!zenMode && 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" @@ -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" : "") } /> From 55ca72e08fabcbf9f6765240fd972ca2a988db2b Mon Sep 17 00:00:00 2001 From: Joel Keyser Date: Fri, 6 Dec 2024 11:21:03 -0600 Subject: [PATCH 2/8] style: append "Classes" for tailwind variables so that there's tailwind intellisense --- src/web/topic/components/Node/FlowNode.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/web/topic/components/Node/FlowNode.tsx b/src/web/topic/components/Node/FlowNode.tsx index c5b747de..2078aba6 100644 --- a/src/web/topic/components/Node/FlowNode.tsx +++ b/src/web/topic/components/Node/FlowNode.tsx @@ -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)]`; @@ -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}`} /> )} @@ -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}`} /> )} From b20f216c990ac3f43984f32efee6c1090b62ecc9 Mon Sep 17 00:00:00 2001 From: Joel Keyser Date: Fri, 6 Dec 2024 11:29:19 -0600 Subject: [PATCH 3/8] feat: show indicators on hover/select in zen mode going to revert this because it seems jarring when you hover something and it reveals something that's immediately hovered, e.g. hover edge -> hovering score. wanted to keep this commit in case it's valuable to refer back to the logic later. --- .eslintrc.json | 10 +++++++- src/web/topic/components/Edge/ScoreEdge.tsx | 6 ++++- .../components/Indicator/CommonIndicators.tsx | 25 ++++++------------- .../Indicator/ContentIndicators.tsx | 4 --- .../topic/components/Indicator/Indicator.tsx | 20 ++++++++++++++- .../Indicator/JustificationTreeIndicator.tsx | 1 + .../components/Indicator/StatusIndicators.tsx | 5 +--- .../topic/components/Node/EditableNode.tsx | 9 ++++++- src/web/topic/components/Score/Score.tsx | 17 +++++++++++-- .../topic/components/Score/ScoreButton.tsx | 5 +++- 10 files changed, 69 insertions(+), 33 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 2b543042..69a8e6f7 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -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" + ] } ] } diff --git a/src/web/topic/components/Edge/ScoreEdge.tsx b/src/web/topic/components/Edge/ScoreEdge.tsx index 8a1bd202..b01713bb 100644 --- a/src/web/topic/components/Edge/ScoreEdge.tsx +++ b/src/web/topic/components/Edge/ScoreEdge.tsx @@ -156,7 +156,11 @@ export const ScoreEdge = ({ inReactFlow, ...flowEdge }: EdgeProps & Props) => { // 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" : "") + (zenMode && spotlight === "normal" ? " border-none hover:border-solid" : "") + + // 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" : "") } > diff --git a/src/web/topic/components/Indicator/CommonIndicators.tsx b/src/web/topic/components/Indicator/CommonIndicators.tsx index 05470927..23994d05 100644 --- a/src/web/topic/components/Indicator/CommonIndicators.tsx +++ b/src/web/topic/components/Indicator/CommonIndicators.tsx @@ -1,13 +1,11 @@ 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; @@ -15,23 +13,14 @@ interface Props { } const CommonIndicatorsBase = ({ graphPart, notes }: Props) => { - const zenMode = useZenMode(); - const workspaceContext = useContext(WorkspaceContext); - return ( - {!zenMode && ( - <> - {/* TODO: should this be moved because it's not used for all graph parts? */} - - {/* TODO: should this be moved because it's only used for problem? */} - - - - )} - - {/* table's purpose is mainly for scores, so show scores there even if in zen mode */} - {(!zenMode || workspaceContext === "table") && } + {/* TODO: should this be moved because it's not used for all graph parts? */} + + {/* TODO: should this be moved because it's only used for problem? */} + + + ); }; diff --git a/src/web/topic/components/Indicator/ContentIndicators.tsx b/src/web/topic/components/Indicator/ContentIndicators.tsx index c9d37401..ecdbfaf2 100644 --- a/src/web/topic/components/Indicator/ContentIndicators.tsx +++ b/src/web/topic/components/Indicator/ContentIndicators.tsx @@ -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; @@ -16,9 +15,6 @@ interface Props { } const ContentIndicatorsBase = ({ graphPartId, graphPartType, color, className }: Props) => { - const zenMode = useZenMode(); - if (zenMode) return <>; - return ( diff --git a/src/web/topic/components/Indicator/Indicator.tsx b/src/web/topic/components/Indicator/Indicator.tsx index 983f57b6..a07115fb 100644 --- a/src/web/topic/components/Indicator/Indicator.tsx +++ b/src/web/topic/components/Indicator/Indicator.tsx @@ -3,6 +3,7 @@ import { MouseEventHandler } from "react"; import { StyledButton } from "@/web/topic/components/Indicator/Indicator.styles"; import { MuiIcon } from "@/web/topic/utils/node"; +import { useZenMode } from "@/web/view/userConfigStore"; interface IndicatorProps { Icon: MuiIcon; @@ -10,6 +11,10 @@ interface IndicatorProps { onClick?: MouseEventHandler; 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 = ({ @@ -18,7 +23,10 @@ export const Indicator = ({ onClick, color = "neutral", filled = true, + graphPartIndicator = true, }: IndicatorProps) => { + const zenMode = useZenMode(); + const onClickHandler = (event: React.MouseEvent) => { if (onClick) { // In most cases, we don't want the click to result in selecting the parent node. @@ -28,6 +36,15 @@ export const Indicator = ({ } }; + // no need to hide indicators that aren't for graph parts e.g. JustificationTreeIndicator + const showIndicator = !zenMode || !graphPartIndicator; + // allow score to be hidden but still show on hover/select so it's discoverable + const showIndicatorClasses = showIndicator + ? "" + : " hidden" + + " [.diagram-node:hover_&]:flex [.diagram-node.selected_&]:flex" + + " [.diagram-edge:hover_&]:flex [.diagram-edge.selected_&]:flex"; + return ( <> diff --git a/src/web/topic/components/Indicator/JustificationTreeIndicator.tsx b/src/web/topic/components/Indicator/JustificationTreeIndicator.tsx index b852778d..a5788c91 100644 --- a/src/web/topic/components/Indicator/JustificationTreeIndicator.tsx +++ b/src/web/topic/components/Indicator/JustificationTreeIndicator.tsx @@ -35,6 +35,7 @@ export const JustificationTreeIndicator = ({ graphPartId }: Props) => { filled={justificationCount > 0} title={title} onClick={justificationCount > 0 ? onClick : undefined} + graphPartIndicator={false} /> ); }; diff --git a/src/web/topic/components/Indicator/StatusIndicators.tsx b/src/web/topic/components/Indicator/StatusIndicators.tsx index db5abad0..d75dcfbe 100644 --- a/src/web/topic/components/Indicator/StatusIndicators.tsx +++ b/src/web/topic/components/Indicator/StatusIndicators.tsx @@ -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; @@ -15,9 +15,6 @@ interface Props { */ const StatusIndicatorsBase = ({ graphPartId, color, className }: Props) => { const indicateWhenNodeForcedToShow = useIndicateWhenNodeForcedToShow(); - const zenMode = useZenMode(); - - if (zenMode) return <>; return ( diff --git a/src/web/topic/components/Node/EditableNode.tsx b/src/web/topic/components/Node/EditableNode.tsx index 6c1e575f..374a4491 100644 --- a/src/web/topic/components/Node/EditableNode.tsx +++ b/src/web/topic/components/Node/EditableNode.tsx @@ -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); @@ -109,7 +110,13 @@ const EditableNodeBase = ({ node, className = "" }: Props) => { return ( { if (context != "details") setSelected(node.id); }} diff --git a/src/web/topic/components/Score/Score.tsx b/src/web/topic/components/Score/Score.tsx index acc96b5f..36cb0d60 100644 --- a/src/web/topic/components/Score/Score.tsx +++ b/src/web/topic/components/Score/Score.tsx @@ -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 { useZenMode } from "@/web/view/userConfigStore"; const circleDiameter = 6 * buttonDiameterRem; // no collisions for fitting 10 elements @@ -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 zenMode = useZenMode(); + const readonlyMode = useReadonlyMode(); + const myUsername = onPlayground ? playgroundUsername : sessionUser?.username; const perspectives = usePerspectives(); const canEdit = userCanEditScores(myUsername, perspectives, readonlyMode); @@ -66,8 +69,17 @@ 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 = !zenMode || workspaceContext === "table"; + // allow score to be hidden but still show on hover/select so it's discoverable + const showScoreClasses = showScore + ? "" + : " hidden" + + " [.diagram-node:hover_&]:flex [.diagram-node.selected_&]:flex" + + " [.diagram-edge:hover_&]:flex [.diagram-edge.selected_&]:flex"; + if (!isInteractive) { - return ; + return ; } return ( @@ -80,6 +92,7 @@ export const Score = ({ graphPartId }: ScoreProps) => { onMouseEnter={() => setHoverDelayHandler(setTimeout(() => setHovering(true), 100))} onMouseLeave={() => clearTimeout(hoverDelayHandler)} userScores={userScores} + className={showScoreClasses} /> void; zoomRatio?: number; userScores: Record; + className?: string; } export const ScoreButton = ({ @@ -25,6 +26,7 @@ export const ScoreButton = ({ onMouseLeave, zoomRatio = 1, userScores, + className = "", }: ScoreButtonProps) => { const isComparing = Object.keys(userScores).length > 1; @@ -45,7 +47,8 @@ export const ScoreButton = ({ className={ "shadow-none border border-solid border-neutral-main" + ` ${isComparing ? "pointer-events-none z-0 bg-transparent" : ""}` + - ` ${onClick ? "" : "pointer-events-none"}` + ` ${onClick ? "" : "pointer-events-none"}` + + ` ${className}` } > {isComparing && ( From 2c00261976ec04e67aa7be7dcaebe015d6b4c509 Mon Sep 17 00:00:00 2001 From: Joel Keyser Date: Fri, 6 Dec 2024 11:34:52 -0600 Subject: [PATCH 4/8] touchup: don't show indicators on hover/select because it seems jarring when you hover something and it reveals something that's immediately hovered, e.g. hover edge -> hovering score. keeping the change to use css instead of js for show/hiding because it seems a little cleaner; could consider js for performance improvements in the future (because it could prevent running rendering logic). also keeping diagram-node/-edge classes because they seem probably useful in the future. --- src/web/topic/components/Edge/ScoreEdge.tsx | 2 +- src/web/topic/components/Indicator/Indicator.tsx | 8 +------- src/web/topic/components/Score/Score.tsx | 7 +------ 3 files changed, 3 insertions(+), 14 deletions(-) diff --git a/src/web/topic/components/Edge/ScoreEdge.tsx b/src/web/topic/components/Edge/ScoreEdge.tsx index b01713bb..6d4b9755 100644 --- a/src/web/topic/components/Edge/ScoreEdge.tsx +++ b/src/web/topic/components/Edge/ScoreEdge.tsx @@ -156,7 +156,7 @@ export const ScoreEdge = ({ inReactFlow, ...flowEdge }: EdgeProps & Props) => { // 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 hover:border-solid" : "") + + (zenMode && 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" + diff --git a/src/web/topic/components/Indicator/Indicator.tsx b/src/web/topic/components/Indicator/Indicator.tsx index a07115fb..cc96697b 100644 --- a/src/web/topic/components/Indicator/Indicator.tsx +++ b/src/web/topic/components/Indicator/Indicator.tsx @@ -38,12 +38,6 @@ export const Indicator = ({ // no need to hide indicators that aren't for graph parts e.g. JustificationTreeIndicator const showIndicator = !zenMode || !graphPartIndicator; - // allow score to be hidden but still show on hover/select so it's discoverable - const showIndicatorClasses = showIndicator - ? "" - : " hidden" + - " [.diagram-node:hover_&]:flex [.diagram-node.selected_&]:flex" + - " [.diagram-edge:hover_&]:flex [.diagram-edge.selected_&]:flex"; return ( <> @@ -57,7 +51,7 @@ export const Indicator = ({ // 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" : "") + - showIndicatorClasses + (showIndicator ? "" : " hidden") } > diff --git a/src/web/topic/components/Score/Score.tsx b/src/web/topic/components/Score/Score.tsx index 36cb0d60..fb92c997 100644 --- a/src/web/topic/components/Score/Score.tsx +++ b/src/web/topic/components/Score/Score.tsx @@ -71,12 +71,7 @@ export const Score = ({ graphPartId }: ScoreProps) => { // always show score on table because the main purpose of the table is to compare scores const showScore = !zenMode || workspaceContext === "table"; - // allow score to be hidden but still show on hover/select so it's discoverable - const showScoreClasses = showScore - ? "" - : " hidden" + - " [.diagram-node:hover_&]:flex [.diagram-node.selected_&]:flex" + - " [.diagram-edge:hover_&]:flex [.diagram-edge.selected_&]:flex"; + const showScoreClasses = showScore ? "" : " hidden"; if (!isInteractive) { return ; From 24ff3976ed6fa1c519b34c45c4cd48a665555f3a Mon Sep 17 00:00:00 2001 From: Joel Keyser Date: Fri, 6 Dec 2024 11:44:08 -0600 Subject: [PATCH 5/8] touchup: default zen mode to on having indicators off makes the diagram much easier to read, and new users won't know how to use indicators right away anyway. will want to make sure zen mode is pointed out early in tutorial though, so users can still take advantage of indicators when they become useful to them. --- src/web/view/userConfigStore.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web/view/userConfigStore.ts b/src/web/view/userConfigStore.ts index 38d740dc..7c54a27d 100644 --- a/src/web/view/userConfigStore.ts +++ b/src/web/view/userConfigStore.ts @@ -8,7 +8,7 @@ interface UserConfigStoreState { } const initialState: UserConfigStoreState = { - zenMode: false, + zenMode: true, fillNodesWithColor: false, indicateWhenNodeForcedToShow: false, }; From f787d62ea1800b3ad61e7c2873133f0c9f604fa1 Mon Sep 17 00:00:00 2001 From: Joel Keyser Date: Fri, 6 Dec 2024 11:44:23 -0600 Subject: [PATCH 6/8] touchup: default "minimize edge crossings" to off because with new diagrams, it commonly results in pretty spread out diagrams, trying to avoid crossings for long problem-solution edges. it also sometimes creates poor left-to-right ordering within a layer. --- src/web/view/currentViewStore/store.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web/view/currentViewStore/store.ts b/src/web/view/currentViewStore/store.ts index 6c897578..0ff9a315 100644 --- a/src/web/view/currentViewStore/store.ts +++ b/src/web/view/currentViewStore/store.ts @@ -106,7 +106,7 @@ export const initialViewState: ViewState = { forceNodesIntoLayers: true, layerNodeIslandsTogether: false, - minimizeEdgeCrossings: true, + minimizeEdgeCrossings: false, avoidEdgeLabelOverlap: false, layoutThoroughness: 100, // by default, prefer keeping parents close to children over keeping node types together }; From b22976f2023b1061bb5cd51020ecd964440dae08 Mon Sep 17 00:00:00 2001 From: Joel Keyser Date: Fri, 6 Dec 2024 11:55:16 -0600 Subject: [PATCH 7/8] touchup: change "zen mode" to "show indicators" it's clearer about what it does. --- src/web/topic/components/Edge/ScoreEdge.tsx | 8 ++++---- .../topic/components/Indicator/Indicator.tsx | 6 +++--- src/web/topic/components/Node/NodeHandle.tsx | 6 +++--- src/web/topic/components/Score/Score.tsx | 6 +++--- .../TopicWorkspace/TopicWorkspace.tsx | 4 ++-- .../TopicWorkspace/WorkspaceToolbar.tsx | 18 +++++++++--------- src/web/topic/utils/hotkeys.ts | 2 +- src/web/view/userConfigStore.ts | 12 ++++++------ 8 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/web/topic/components/Edge/ScoreEdge.tsx b/src/web/topic/components/Edge/ScoreEdge.tsx index 6d4b9755..afcb2f31 100644 --- a/src/web/topic/components/Edge/ScoreEdge.tsx +++ b/src/web/topic/components/Edge/ScoreEdge.tsx @@ -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"; @@ -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); @@ -155,8 +155,8 @@ 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" + diff --git a/src/web/topic/components/Indicator/Indicator.tsx b/src/web/topic/components/Indicator/Indicator.tsx index cc96697b..5d81006a 100644 --- a/src/web/topic/components/Indicator/Indicator.tsx +++ b/src/web/topic/components/Indicator/Indicator.tsx @@ -3,7 +3,7 @@ import { MouseEventHandler } from "react"; import { StyledButton } from "@/web/topic/components/Indicator/Indicator.styles"; import { MuiIcon } from "@/web/topic/utils/node"; -import { useZenMode } from "@/web/view/userConfigStore"; +import { useShowIndicators } from "@/web/view/userConfigStore"; interface IndicatorProps { Icon: MuiIcon; @@ -25,7 +25,7 @@ export const Indicator = ({ filled = true, graphPartIndicator = true, }: IndicatorProps) => { - const zenMode = useZenMode(); + const showIndicators = useShowIndicators(); const onClickHandler = (event: React.MouseEvent) => { if (onClick) { @@ -37,7 +37,7 @@ export const Indicator = ({ }; // no need to hide indicators that aren't for graph parts e.g. JustificationTreeIndicator - const showIndicator = !zenMode || !graphPartIndicator; + const showIndicator = showIndicators || !graphPartIndicator; return ( <> diff --git a/src/web/topic/components/Node/NodeHandle.tsx b/src/web/topic/components/Node/NodeHandle.tsx index 893b4afb..dff477df 100644 --- a/src/web/topic/components/Node/NodeHandle.tsx +++ b/src/web/topic/components/Node/NodeHandle.tsx @@ -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]; @@ -38,7 +38,7 @@ 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); @@ -54,7 +54,7 @@ const NodeHandleBase = ({ node, direction, orientation }: Props) => { }); const hasHiddenNeighbors = sortedHiddenNeighbors.length > 0; - const showHandle = isPotentiallyConnectingToThisHandle || (!zenMode && hasHiddenNeighbors); + 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 diff --git a/src/web/topic/components/Score/Score.tsx b/src/web/topic/components/Score/Score.tsx index fb92c997..03d112c0 100644 --- a/src/web/topic/components/Score/Score.tsx +++ b/src/web/topic/components/Score/Score.tsx @@ -15,7 +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 { useZenMode } from "@/web/view/userConfigStore"; +import { useShowIndicators } from "@/web/view/userConfigStore"; const circleDiameter = 6 * buttonDiameterRem; // no collisions for fitting 10 elements @@ -31,7 +31,7 @@ export const Score = ({ graphPartId }: ScoreProps) => { const onPlayground = useOnPlayground(); const workspaceContext = useContext(WorkspaceContext); - const zenMode = useZenMode(); + const showIndicators = useShowIndicators(); const readonlyMode = useReadonlyMode(); const myUsername = onPlayground ? playgroundUsername : sessionUser?.username; @@ -70,7 +70,7 @@ export const Score = ({ graphPartId }: ScoreProps) => { const isInteractive = hoverCircle !== undefined; // always show score on table because the main purpose of the table is to compare scores - const showScore = !zenMode || workspaceContext === "table"; + const showScore = showIndicators || workspaceContext === "table"; const showScoreClasses = showScore ? "" : " hidden"; if (!isInteractive) { diff --git a/src/web/topic/components/TopicWorkspace/TopicWorkspace.tsx b/src/web/topic/components/TopicWorkspace/TopicWorkspace.tsx index 7a8b8531..7f7e62e5 100644 --- a/src/web/topic/components/TopicWorkspace/TopicWorkspace.tsx +++ b/src/web/topic/components/TopicWorkspace/TopicWorkspace.tsx @@ -21,11 +21,11 @@ import { userCanEditScores } from "@/web/topic/utils/score"; import { getReadonlyMode, toggleReadonlyMode } from "@/web/view/actionConfigStore"; import { getSelectedGraphPart, setSelected, useFormat } from "@/web/view/currentViewStore/store"; import { getPerspectives } from "@/web/view/perspectiveStore"; -import { toggleZenMode } from "@/web/view/userConfigStore"; +import { toggleShowIndicators } from "@/web/view/userConfigStore"; const useWorkspaceHotkeys = (user: { username: string } | null | undefined) => { useHotkeys([hotkeys.deselectPart], () => setSelected(null)); - useHotkeys([hotkeys.zenMode], () => toggleZenMode()); + useHotkeys([hotkeys.showIndicators], () => toggleShowIndicators()); useHotkeys([hotkeys.readonlyMode], () => toggleReadonlyMode()); useHotkeys([hotkeys.score], (_, hotkeysEvent) => { diff --git a/src/web/topic/components/TopicWorkspace/WorkspaceToolbar.tsx b/src/web/topic/components/TopicWorkspace/WorkspaceToolbar.tsx index c00848f5..d0cad6f4 100644 --- a/src/web/topic/components/TopicWorkspace/WorkspaceToolbar.tsx +++ b/src/web/topic/components/TopicWorkspace/WorkspaceToolbar.tsx @@ -9,7 +9,7 @@ import { Highlight, QuestionMark, Redo, - SelfImprovement, + TabUnselected, Undo, } from "@mui/icons-material"; import { AppBar, Divider, IconButton, ToggleButton, Toolbar, Tooltip } from "@mui/material"; @@ -43,7 +43,7 @@ import { resetPerspectives, useIsComparingPerspectives, } from "@/web/view/perspectiveStore"; -import { toggleZenMode, useZenMode } from "@/web/view/userConfigStore"; +import { toggleShowIndicators, useShowIndicators } from "@/web/view/userConfigStore"; export const WorkspaceToolbar = () => { const { sessionUser } = useSessionUser(); @@ -53,7 +53,7 @@ export const WorkspaceToolbar = () => { const [canUndo, canRedo] = useTemporalHooks(); const [canGoBack, canGoForward] = useCanGoBackForward(); - const zenMode = useZenMode(); + const showIndicators = useShowIndicators(); const isComparingPerspectives = useIsComparingPerspectives(); const flashlightMode = useFlashlightMode(); const readonlyMode = useReadonlyMode(); @@ -144,16 +144,16 @@ export const WorkspaceToolbar = () => { toggleZenMode()} + selected={showIndicators} + onClick={() => toggleShowIndicators()} className="rounded-full border-none" > - + {!onPlayground && ( diff --git a/src/web/topic/utils/hotkeys.ts b/src/web/topic/utils/hotkeys.ts index 2c3163f7..8d4610f1 100644 --- a/src/web/topic/utils/hotkeys.ts +++ b/src/web/topic/utils/hotkeys.ts @@ -10,7 +10,7 @@ export const hotkeys = { readonlyMode: "Alt + R", pan: "Up, Down, Left, Right", score: "1, 2, 3, 4, 5, 6, 7, 8, 9, -", - zenMode: "Alt + Z", + showIndicators: "Alt + i", zoomIn: "Ctrl + =", zoomOut: "Ctrl + -", }; diff --git a/src/web/view/userConfigStore.ts b/src/web/view/userConfigStore.ts index 7c54a27d..98ef0751 100644 --- a/src/web/view/userConfigStore.ts +++ b/src/web/view/userConfigStore.ts @@ -2,13 +2,13 @@ import { create } from "zustand"; import { persist } from "zustand/middleware"; interface UserConfigStoreState { - zenMode: boolean; + showIndicators: boolean; fillNodesWithColor: boolean; indicateWhenNodeForcedToShow: boolean; } const initialState: UserConfigStoreState = { - zenMode: true, + showIndicators: false, fillNodesWithColor: false, indicateWhenNodeForcedToShow: false, }; @@ -20,8 +20,8 @@ const useUserConfigStore = create()( ); // hooks -export const useZenMode = () => { - return useUserConfigStore((state) => state.zenMode); +export const useShowIndicators = () => { + return useUserConfigStore((state) => state.showIndicators); }; export const useFillNodesWithColor = () => { @@ -33,8 +33,8 @@ export const useIndicateWhenNodeForcedToShow = () => { }; // actions -export const toggleZenMode = () => { - useUserConfigStore.setState((state) => ({ zenMode: !state.zenMode })); +export const toggleShowIndicators = () => { + useUserConfigStore.setState((state) => ({ showIndicators: !state.showIndicators })); }; export const toggleFillNodesWithColor = (fill: boolean) => { From 26ff555d9ea50e8d339698323e100d66fed445f4 Mon Sep 17 00:00:00 2001 From: Joel Keyser Date: Fri, 6 Dec 2024 16:54:56 -0600 Subject: [PATCH 8/8] docs: add "show indicators" to tutorial & docs This was annoying to do because of the reasons below, but since we're defaulting indicators to hidden, we have to ensure docs mention it (since users would otherwise be confused when getting to "scores" in the docs). I think the button name is slightly suboptimal because: - Score is more interactive than just an Indicator Also these docs updates are a little suboptimal because: - the button is only explained in the Scoring section; hopefully users can understand its use for the indicator section, but it seems annoying to duplicate the explanation there too. It's possible that "show scores" should be a separate button, but: - both would often be on/off together - both would need separate tutorial/docs mentions? --- .../getting-started/diagramming-basics.mdx | 21 ++++++++++++++++++- src/web/tutorial/steps/addingNuance.tsx | 5 ++++- src/web/tutorial/steps/readingDiagram.tsx | 4 +++- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/docs-site/pages/getting-started/diagramming-basics.mdx b/docs-site/pages/getting-started/diagramming-basics.mdx index 1c9dbbf7..41796a57 100644 --- a/docs-site/pages/getting-started/diagramming-basics.mdx +++ b/docs-site/pages/getting-started/diagramming-basics.mdx @@ -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). + + Indicators are off by default to reduce clutter, but you can show them by clicking the "Show + indicators" button in the toolbar: + + + + 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 @@ -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. + + + +
- You can use scores to convey positive or negative opinion about nodes and edges. + You can use scores to convey positive or negative opinion about nodes and edges. Click + the "Show indicators" button in the toolbar to show scores and other + indicators.

Here we're saying that we think pedestrians getting hit is a big concern, and that diff --git a/src/web/tutorial/steps/readingDiagram.tsx b/src/web/tutorial/steps/readingDiagram.tsx index b934f5ca..c22d9c92 100644 --- a/src/web/tutorial/steps/readingDiagram.tsx +++ b/src/web/tutorial/steps/readingDiagram.tsx @@ -1,3 +1,4 @@ +import { TabUnselected } from "@mui/icons-material"; import { Button, Typography } from "@mui/material"; import { StepType } from "@reactour/tour"; import Image from "next/image"; @@ -87,7 +88,8 @@ export const getReadingDiagramSteps = (nextTutorial?: Tutorial | null): StepType text={ Scores convey positive or negative opinion about a node or edge. If you're logged in, - you can score other people's topics. + you can score other people's topics. Click the "Show indicators" button{" "} + in the toolbar to show scores and other indicators.

Here we're saying that we think pedestrians getting hit is a big concern, and that