Skip to content

Commit

Permalink
feat: add zen mode
Browse files Browse the repository at this point in the history
  • Loading branch information
keyserj committed Dec 5, 2024
1 parent 157c259 commit 73fb4a4
Show file tree
Hide file tree
Showing 10 changed files with 73 additions and 25 deletions.
11 changes: 0 additions & 11 deletions src/web/topic/components/Edge/ScoreEdge.styles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,7 @@ const divOptions = {
};

export const StyledDiv = styled("div", divOptions)<DivProps>`
pointer-events: all;
cursor: default;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: white;
border: 1px solid ${edgeColor};
border-radius: 15%;
padding: 4px;
${({ labelX, labelY }) => css`
position: absolute;
Expand Down
9 changes: 9 additions & 0 deletions src/web/topic/components/Edge/ScoreEdge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +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";

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

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

const edge = convertToEdge(flowEdge);
Expand Down Expand Up @@ -149,6 +152,12 @@ export const ScoreEdge = ({ inReactFlow, ...flowEdge }: EdgeProps & Props) => {
onClick={() => setSelected(edge.id)}
onContextMenu={(event) => openContextMenu(event, { edge })}
spotlight={spotlight}
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" : "")
}
>
<CommonIndicators graphPart={edge} notes={edge.data.notes} />
<Typography
Expand Down
25 changes: 18 additions & 7 deletions src/web/topic/components/Indicator/CommonIndicators.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,37 @@
import { Stack } from "@mui/material";
import { memo } from "react";
import { memo, useContext } 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">
{/* 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} />
{!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} />}
</Stack>
);
};
Expand Down
4 changes: 4 additions & 0 deletions src/web/topic/components/Indicator/ContentIndicators.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ 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 @@ -15,6 +16,9 @@ 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
5 changes: 4 additions & 1 deletion 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 } from "@/web/view/userConfigStore";
import { useIndicateWhenNodeForcedToShow, useZenMode } from "@/web/view/userConfigStore";

interface Props {
graphPartId: string;
Expand All @@ -15,6 +15,9 @@ 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
4 changes: 3 additions & 1 deletion src/web/topic/components/Node/NodeHandle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +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";

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

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

Expand All @@ -47,7 +49,7 @@ const NodeHandleBase = ({ node, direction, orientation }: Props) => {
});

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

const type = direction === "parent" ? "target" : "source";

Expand Down
2 changes: 2 additions & 0 deletions src/web/topic/components/TopicWorkspace/TopicWorkspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +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";

const useWorkspaceHotkeys = (user: { username: string } | null | undefined) => {
useHotkeys([hotkeys.deselectPart], () => setSelected(null));
useHotkeys([hotkeys.zenMode], () => toggleZenMode());
useHotkeys([hotkeys.readonlyMode], () => toggleReadonlyMode());

useHotkeys([hotkeys.score], (_, hotkeysEvent) => {
Expand Down
27 changes: 22 additions & 5 deletions src/web/topic/components/TopicWorkspace/WorkspaceToolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
Highlight,
QuestionMark,
Redo,
SelfImprovement,
Undo,
} from "@mui/icons-material";
import { AppBar, Divider, IconButton, ToggleButton, Toolbar, Tooltip } from "@mui/material";
Expand All @@ -24,6 +25,7 @@ import { useOnPlayground } from "@/web/topic/store/topicHooks";
import { useUserCanEditTopicData } from "@/web/topic/store/userHooks";
import { redo, undo } from "@/web/topic/store/utilActions";
import { useTemporalHooks } from "@/web/topic/store/utilHooks";
import { hotkeys } from "@/web/topic/utils/hotkeys";
import {
toggleFlashlightMode,
toggleReadonlyMode,
Expand All @@ -41,6 +43,7 @@ import {
resetPerspectives,
useIsComparingPerspectives,
} from "@/web/view/perspectiveStore";
import { toggleZenMode, useZenMode } from "@/web/view/userConfigStore";

export const WorkspaceToolbar = () => {
const { sessionUser } = useSessionUser();
Expand All @@ -50,6 +53,7 @@ export const WorkspaceToolbar = () => {
const [canUndo, canRedo] = useTemporalHooks();
const [canGoBack, canGoForward] = useCanGoBackForward();

const zenMode = useZenMode();
const isComparingPerspectives = useIsComparingPerspectives();
const flashlightMode = useFlashlightMode();
const readonlyMode = useReadonlyMode();
Expand Down Expand Up @@ -137,10 +141,23 @@ export const WorkspaceToolbar = () => {
</>
)}

<Divider orientation="vertical" flexItem />

<ToggleButton
value={zenMode}
title={`Zen mode (${hotkeys.zenMode})`}
aria-label={`Zen mode (${hotkeys.zenMode})`}
color="primary"
size="small"
selected={zenMode}
onClick={() => toggleZenMode()}
className="rounded-full border-none"
>
<SelfImprovement />
</ToggleButton>

{!onPlayground && (
<>
<Divider orientation="vertical" flexItem />

<ToggleButton
value={isComparingPerspectives}
title="Compare perspectives"
Expand Down Expand Up @@ -171,7 +188,7 @@ export const WorkspaceToolbar = () => {
size="small"
selected={flashlightMode}
onClick={() => toggleFlashlightMode(!flashlightMode)}
className="rounded-full border-none"
className="hidden rounded-full border-none sm:flex" // hide on mobile because there's not enough space
>
<Highlight />
</ToggleButton>
Expand All @@ -180,8 +197,8 @@ export const WorkspaceToolbar = () => {
{readonlyMode && (
<ToggleButton
value={readonlyMode}
title="Read-only mode"
aria-label="Read-only mode"
title={`Read-only mode (${hotkeys.readonlyMode})`}
aria-label={`Read-only mode (${hotkeys.readonlyMode})`}
color="primary"
size="small"
selected={readonlyMode}
Expand Down
1 change: 1 addition & 0 deletions src/web/topic/utils/hotkeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +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",
zoomIn: "Ctrl + =",
zoomOut: "Ctrl + -",
};
10 changes: 10 additions & 0 deletions src/web/view/userConfigStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import { create } from "zustand";
import { persist } from "zustand/middleware";

interface UserConfigStoreState {
zenMode: boolean;
fillNodesWithColor: boolean;
indicateWhenNodeForcedToShow: boolean;
}

const initialState: UserConfigStoreState = {
zenMode: false,
fillNodesWithColor: false,
indicateWhenNodeForcedToShow: false,
};
Expand All @@ -18,6 +20,10 @@ const useUserConfigStore = create<UserConfigStoreState>()(
);

// hooks
export const useZenMode = () => {
return useUserConfigStore((state) => state.zenMode);
};

export const useFillNodesWithColor = () => {
return useUserConfigStore((state) => state.fillNodesWithColor);
};
Expand All @@ -27,6 +33,10 @@ export const useIndicateWhenNodeForcedToShow = () => {
};

// actions
export const toggleZenMode = () => {
useUserConfigStore.setState((state) => ({ zenMode: !state.zenMode }));
};

export const toggleFillNodesWithColor = (fill: boolean) => {
useUserConfigStore.setState({ fillNodesWithColor: fill });
};
Expand Down

0 comments on commit 73fb4a4

Please sign in to comment.