From d8bf75d2012581306d3f77e669511e1346aa5480 Mon Sep 17 00:00:00 2001 From: Joel Keyser Date: Fri, 13 Dec 2024 09:20:03 -0600 Subject: [PATCH 1/6] touchup: set gray bg on panes to distinguish more from the diagram/table seems like all diagram apps use a different color for their panes. I think it makes sense. --- src/web/topic/components/TopicPane/TopicPane.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/web/topic/components/TopicPane/TopicPane.tsx b/src/web/topic/components/TopicPane/TopicPane.tsx index 95fb5be4..f69d212b 100644 --- a/src/web/topic/components/TopicPane/TopicPane.tsx +++ b/src/web/topic/components/TopicPane/TopicPane.tsx @@ -83,7 +83,12 @@ const TopicPaneBase = ({ anchor, tabs }: Props) => { - + {tabs.map((tab) => ( From 0aa41aeba3d7de23792a9e0407aa7d38b8730bb7 Mon Sep 17 00:00:00 2001 From: Joel Keyser Date: Fri, 13 Dec 2024 10:43:48 -0600 Subject: [PATCH 2/6] touchup: extract Details Basics Section this seems like a good place for misc things to live. makes notes feel more at-home/organized, and the graph part feels better isolated as a header of the details. included node-type-specific details in "basics" as well - maybe ideal to have another section for these, but they aren't used very often, so it seems ok. also remove "problem node"/"addresses edge" text; seemed unnecessary. --- .../TopicPane/DetailsBasicsSection.tsx | 90 ++++++++++++ .../components/TopicPane/GraphPartDetails.tsx | 136 ++++-------------- 2 files changed, 116 insertions(+), 110 deletions(-) create mode 100644 src/web/topic/components/TopicPane/DetailsBasicsSection.tsx diff --git a/src/web/topic/components/TopicPane/DetailsBasicsSection.tsx b/src/web/topic/components/TopicPane/DetailsBasicsSection.tsx new file mode 100644 index 00000000..b0f2254b --- /dev/null +++ b/src/web/topic/components/TopicPane/DetailsBasicsSection.tsx @@ -0,0 +1,90 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { Article } from "@mui/icons-material"; +import { ListItem, ListItemIcon, ListItemText, TextField } from "@mui/material"; +import { useEffect } from "react"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; + +import { nodeSchema } from "@/common/node"; +import { useSessionUser } from "@/web/common/hooks"; +import { AnswerDetails } from "@/web/topic/components/TopicPane/AnswerDetails"; +import { FactDetails } from "@/web/topic/components/TopicPane/FactDetails"; +import { QuestionDetails } from "@/web/topic/components/TopicPane/QuestionDetails"; +import { SourceDetails } from "@/web/topic/components/TopicPane/SourceDetails"; +import { setGraphPartNotes } from "@/web/topic/store/actions"; +import { useUserCanEditTopicData } from "@/web/topic/store/userHooks"; +import { GraphPart, isNodeType } from "@/web/topic/utils/graph"; + +const formSchema = z.object({ + // same restrictions as edge, so we should be fine reusing node's schema + notes: nodeSchema.shape.notes, +}); +type FormData = z.infer; + +interface Props { + graphPart: GraphPart; +} + +export const DetailsBasicsSection = ({ graphPart }: Props) => { + const { sessionUser } = useSessionUser(); + const userCanEditTopicData = useUserCanEditTopicData(sessionUser?.username); + + const { + register, + reset, + handleSubmit, + formState: { errors }, + } = useForm({ + mode: "onBlur", + reValidateMode: "onBlur", + resolver: zodResolver(formSchema), + defaultValues: { + notes: graphPart.data.notes, + }, + }); + + useEffect(() => { + // when notes changes from outside of form (e.g. undo/redo), make sure form is updated + reset({ notes: graphPart.data.notes }); + }, [graphPart.data.notes, reset]); + + return ( +
{ + void handleSubmit((data) => { + if (graphPart.data.notes === data.notes) return; + setGraphPartNotes(graphPart, data.notes); + })(event); + }} + > + + +
+ + + + + + + + + {/* Potentially these could be in another tab like "specific for this node type"...? */} + {/* but these seem low-use anyway and more effort to organize optimally, so we'll just do this for now. */} + {isNodeType(graphPart, "question") && } + {isNodeType(graphPart, "answer") && } + {isNodeType(graphPart, "fact") && } + {isNodeType(graphPart, "source") && } + + ); +}; diff --git a/src/web/topic/components/TopicPane/GraphPartDetails.tsx b/src/web/topic/components/TopicPane/GraphPartDetails.tsx index bd6d0497..e914063b 100644 --- a/src/web/topic/components/TopicPane/GraphPartDetails.tsx +++ b/src/web/topic/components/TopicPane/GraphPartDetails.tsx @@ -1,132 +1,48 @@ -import { zodResolver } from "@hookform/resolvers/zod"; -import { Timeline } from "@mui/icons-material"; -import { Divider, List, ListItem, ListItemIcon, ListItemText, TextField } from "@mui/material"; -import lowerCase from "lodash/lowerCase"; -import { useEffect } from "react"; -import { useForm } from "react-hook-form"; -import { z } from "zod"; +import { Divider, List } from "@mui/material"; -import { nodeSchema, researchNodeTypes } from "@/common/node"; -import { useSessionUser } from "@/web/common/hooks"; import { StandaloneEdge } from "@/web/topic/components/Edge/StandaloneEdge"; import { EditableNode } from "@/web/topic/components/Node/EditableNode"; -import { AnswerDetails } from "@/web/topic/components/TopicPane/AnswerDetails"; import { CommentSection } from "@/web/topic/components/TopicPane/CommentSection"; +import { DetailsBasicsSection } from "@/web/topic/components/TopicPane/DetailsBasicsSection"; import { DetailsJustificationSection } from "@/web/topic/components/TopicPane/DetailsJustificationSection"; import { DetailsResearchSection } from "@/web/topic/components/TopicPane/DetailsResearchSection"; -import { FactDetails } from "@/web/topic/components/TopicPane/FactDetails"; -import { QuestionDetails } from "@/web/topic/components/TopicPane/QuestionDetails"; -import { SourceDetails } from "@/web/topic/components/TopicPane/SourceDetails"; -import { setGraphPartNotes } from "@/web/topic/store/actions"; -import { useUserCanEditTopicData } from "@/web/topic/store/userHooks"; -import { GraphPart, isNode, isNodeType } from "@/web/topic/utils/graph"; -import { nodeDecorations } from "@/web/topic/utils/node"; - -const formSchema = z.object({ - // same restrictions as edge, so we should be fine reusing node's schema - notes: nodeSchema.shape.notes, -}); -type FormData = z.infer; +import { GraphPart, isNode } from "@/web/topic/utils/graph"; interface Props { graphPart: GraphPart; } export const GraphPartDetails = ({ graphPart }: Props) => { - const { sessionUser } = useSessionUser(); - const userCanEditTopicData = useUserCanEditTopicData(sessionUser?.username); - - const { - register, - reset, - handleSubmit, - formState: { errors }, - } = useForm({ - mode: "onBlur", - reValidateMode: "onBlur", - resolver: zodResolver(formSchema), - defaultValues: { - notes: graphPart.data.notes, - }, - }); - - useEffect(() => { - // when notes changes from outside of form (e.g. undo/redo), make sure form is updated - reset({ notes: graphPart.data.notes }); - }, [graphPart.data.notes, reset]); - const partIsNode = isNode(graphPart); - const GraphPartIcon = partIsNode ? nodeDecorations[graphPart.type].NodeIcon : Timeline; - const headerText = partIsNode - ? `${nodeDecorations[graphPart.type].title} Node` - : `"${lowerCase(graphPart.label)}" Edge`; return ( -
{ - void handleSubmit((data) => { - if (graphPart.data.notes === data.notes) return; - setGraphPartNotes(graphPart, data.notes); - })(event); - }} - > - -
- - - - - - - - {partIsNode ? ( - // z-index to ensure hanging node indicators don't fall behind the next section's empty background - - ) : ( - - )} - - - - -
- - {isNode(graphPart) && researchNodeTypes.includes(graphPart.type) && ( - + +
+ {partIsNode ? ( + // z-index to ensure hanging node indicators don't fall behind the next section's empty background + + ) : ( + )} +
- {isNodeType(graphPart, "question") && } - {isNodeType(graphPart, "answer") && } - {isNodeType(graphPart, "fact") && } - {isNodeType(graphPart, "source") && } - - + {/* mt-2 to match distance from Tabs look to graph part */} + + - + + - {/* prevent adding research nodes to edges; not 100% sure that we want to restrict this, but if it continues to seem good, this section can accept node instead of graphPart */} - {partIsNode && ( - <> - - - - )} - - + {/* prevent adding research nodes to edges; not 100% sure that we want to restrict this, but if it continues to seem good, this section can accept node instead of graphPart */} + {partIsNode && ( + <> + + + + )} - -
- + + +
); }; From 90d0599c46f5e6eb8436892fa68578b8e98d4ce0 Mon Sep 17 00:00:00 2001 From: Joel Keyser Date: Fri, 13 Dec 2024 10:55:07 -0600 Subject: [PATCH 3/6] touchup: reorder indicators to match details order --- src/web/topic/components/Indicator/ContentIndicators.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web/topic/components/Indicator/ContentIndicators.tsx b/src/web/topic/components/Indicator/ContentIndicators.tsx index ecdbfaf2..3f1b5fe8 100644 --- a/src/web/topic/components/Indicator/ContentIndicators.tsx +++ b/src/web/topic/components/Indicator/ContentIndicators.tsx @@ -17,9 +17,9 @@ interface Props { const ContentIndicatorsBase = ({ graphPartId, graphPartType, color, className }: Props) => { return ( + - ); From d928aea1c32b538bf4cf7cf2c31380ef90dffc5e Mon Sep 17 00:00:00 2001 From: Joel Keyser Date: Fri, 13 Dec 2024 11:33:49 -0600 Subject: [PATCH 4/6] refactor: extract details section headers so that sections can alternatively be headered by tabs --- .../components/TopicPane/CommentSection.tsx | 19 ++++++------- .../TopicPane/DetailsBasicsSection.tsx | 10 +------ .../TopicPane/DetailsJustificationSection.tsx | 10 +------ .../TopicPane/DetailsResearchSection.tsx | 10 +------ .../components/TopicPane/GraphPartDetails.tsx | 27 ++++++++++++++++++- 5 files changed, 37 insertions(+), 39 deletions(-) diff --git a/src/web/topic/components/TopicPane/CommentSection.tsx b/src/web/topic/components/TopicPane/CommentSection.tsx index 544e4b1c..31fcc2a2 100644 --- a/src/web/topic/components/TopicPane/CommentSection.tsx +++ b/src/web/topic/components/TopicPane/CommentSection.tsx @@ -1,5 +1,4 @@ -import { ChatBubble } from "@mui/icons-material"; -import { Link, ListItem, ListItemIcon, ListItemText } from "@mui/material"; +import { Link, ListItem } from "@mui/material"; import { CommentParentType } from "@/common/comment"; import { Draft } from "@/web/comment/components/Draft"; @@ -31,24 +30,22 @@ export const CommentSection = ({ parentId, parentType }: Props) => { return ( <> - - - - - - {resolvedCount > 0 && ( + {resolvedCount > 0 && ( + // extra space to the left feels a little awkward, but this can't be on the same line as the header + // because the header can be within a list of tabs + { toggleShowResolvedComments(!showResolved); - e.preventDefault(); // without this, page refreshes - not sure why, since component is as button, not an anchor + e.preventDefault(); // without this, page refreshes - not sure why, since component is a button, not an anchor }} > {showResolved ? "Hide resolved" : "Show resolved"} - )} - + + )}
diff --git a/src/web/topic/components/TopicPane/DetailsBasicsSection.tsx b/src/web/topic/components/TopicPane/DetailsBasicsSection.tsx index b0f2254b..129e3998 100644 --- a/src/web/topic/components/TopicPane/DetailsBasicsSection.tsx +++ b/src/web/topic/components/TopicPane/DetailsBasicsSection.tsx @@ -1,6 +1,5 @@ import { zodResolver } from "@hookform/resolvers/zod"; -import { Article } from "@mui/icons-material"; -import { ListItem, ListItemIcon, ListItemText, TextField } from "@mui/material"; +import { ListItem, TextField } from "@mui/material"; import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { z } from "zod"; @@ -57,13 +56,6 @@ export const DetailsBasicsSection = ({ graphPart }: Props) => { })(event); }} > - - -
- - - - { return ( <> - - - - - - - {/* spacing is the amount that centers the add buttons above the columns */} { return ( <> - - - - - - - {/* spacing is the amount that centers the add buttons above the columns */} { {/* mt-2 to match distance from Tabs look to graph part */} + + +
+ + + + + + + + + {/* prevent adding research nodes to edges; not 100% sure that we want to restrict this, but if it continues to seem good, this section can accept node instead of graphPart */} {partIsNode && ( <> + + + + + + )} + + + + + + ); From d5ea9a0336f7feea6ab16c54ce17fbccbc0c66f4 Mon Sep 17 00:00:00 2001 From: Joel Keyser Date: Mon, 16 Dec 2024 11:02:44 -0600 Subject: [PATCH 5/6] feat: use tabs for part details sections seems a little easier to consume, especially if new to the UI. can see how it feels. included an "expandDetailsTabs" user config to easily revert in case that seems useful. I expect adding details will prefer having tabs expanded, since you'll be adding different kinds of details as you think of them. --- .../components/TopicPane/GraphPartDetails.tsx | 190 +++++++++++++++--- .../topic/components/TopicPane/TopicPane.tsx | 13 +- .../TopicWorkspace/MoreActionsDrawer.tsx | 16 ++ src/web/view/userConfigStore.ts | 10 + 4 files changed, 195 insertions(+), 34 deletions(-) diff --git a/src/web/topic/components/TopicPane/GraphPartDetails.tsx b/src/web/topic/components/TopicPane/GraphPartDetails.tsx index 81962dea..cc6d0740 100644 --- a/src/web/topic/components/TopicPane/GraphPartDetails.tsx +++ b/src/web/topic/components/TopicPane/GraphPartDetails.tsx @@ -1,21 +1,70 @@ -import { Article, ChatBubble, School, ThumbsUpDown } from "@mui/icons-material"; -import { Divider, List, ListItem, ListItemIcon, ListItemText } from "@mui/material"; +import { + Article, + ArticleOutlined, + ChatBubble, + ChatBubbleOutline, + School, + SchoolOutlined, + ThumbsUpDown, + ThumbsUpDownOutlined, +} from "@mui/icons-material"; +import { TabContext, TabList, TabPanel } from "@mui/lab"; +import { + Divider, + List, + ListItem, + ListItemIcon, + ListItemText, + Tab, + Typography, +} from "@mui/material"; +import { useCommentCount } from "@/web/comment/store/commentStore"; import { StandaloneEdge } from "@/web/topic/components/Edge/StandaloneEdge"; import { EditableNode } from "@/web/topic/components/Node/EditableNode"; import { CommentSection } from "@/web/topic/components/TopicPane/CommentSection"; import { DetailsBasicsSection } from "@/web/topic/components/TopicPane/DetailsBasicsSection"; import { DetailsJustificationSection } from "@/web/topic/components/TopicPane/DetailsJustificationSection"; import { DetailsResearchSection } from "@/web/topic/components/TopicPane/DetailsResearchSection"; +import { useResearchNodes, useTopLevelJustification } from "@/web/topic/store/graphPartHooks"; import { GraphPart, isNode } from "@/web/topic/utils/graph"; +import { useShowResolvedComments } from "@/web/view/miscTopicConfigStore"; +import { useExpandDetailsTabs } from "@/web/view/userConfigStore"; + +export type DetailsTab = "Basics" | "Justification" | "Research" | "Comments"; interface Props { graphPart: GraphPart; + /** + * This is hoisted to parent so that it's preserved when a part becomes deselected & reselected. + * + * - Alternative 1: keep `GraphPartDetails` always rendered; but performance, and `viewTab` event + * handling would need to move around. + * - Alternative 2: use a store for this state; but seems like overkill? + */ + selectedTab: DetailsTab; + setSelectedTab: (tab: DetailsTab) => void; } -export const GraphPartDetails = ({ graphPart }: Props) => { +export const GraphPartDetails = ({ graphPart, selectedTab, setSelectedTab }: Props) => { + const expandDetailsTabs = useExpandDetailsTabs(); + const partIsNode = isNode(graphPart); + // Ideally we could exactly reuse the indicator logic here, rather than duplicating, but not sure + // a good way to do that, so we're just duplicating the logic for now. + // Don't want to use the exact indicators, because in the pane, it seems worse to show partial icons e.g. ThumbsUp vs ThumbsDown. + // Maybe could extract logic from the specific indicators, but that seems also like a decent amount of extra abstraction. + const { supports, critiques } = useTopLevelJustification(graphPart.id); + const { questions, facts, sources } = useResearchNodes(graphPart.id); + const showResolved = useShowResolvedComments(); + const commentCount = useCommentCount(graphPart.id, partIsNode ? "node" : "edge", showResolved); + + const indicateBasics = graphPart.data.notes.length > 0; + const indicateJustification = [...supports, ...critiques].length > 0; + const indicateResearch = [...questions, ...facts, ...sources].length > 0; + const indicateComments = commentCount > 0; + return (
@@ -29,45 +78,122 @@ export const GraphPartDetails = ({ graphPart }: Props) => { {/* mt-2 to match distance from Tabs look to graph part */} - - -
- - - - - - - - - - - - + {!expandDetailsTabs ? ( + <> + + setSelectedTab(value)} + centered + className="px-2" + > + : } + value="Basics" + title="Basics" + aria-label="Basics" + /> + : } + value="Justification" + title="Justification" + aria-label="Justification" + /> + {partIsNode && ( + : } + value="Research" + title="Research" + aria-label="Research" + /> + )} + : } + value="Comments" + title="Comments" + aria-label="Comments" + /> + - {/* prevent adding research nodes to edges; not 100% sure that we want to restrict this, but if it continues to seem good, this section can accept node instead of graphPart */} - {partIsNode && ( + + + + Basics + + + + + + + + Justification + + + + + {partIsNode && ( + + + + Research + + + + + )} + + + + Comments + + + + + + + ) : ( <> + + +
+ + + + + - + - + - + + + {/* prevent adding research nodes to edges; not 100% sure that we want to restrict this, but if it continues to seem good, this section can accept node instead of graphPart */} + {partIsNode && ( + <> + + + + + + + + + + )} + + + + + + + + + )} - - - - - - - - - ); }; diff --git a/src/web/topic/components/TopicPane/TopicPane.tsx b/src/web/topic/components/TopicPane/TopicPane.tsx index f69d212b..6609af1b 100644 --- a/src/web/topic/components/TopicPane/TopicPane.tsx +++ b/src/web/topic/components/TopicPane/TopicPane.tsx @@ -11,7 +11,7 @@ import { memo, useEffect, useState } from "react"; import { deepIsEqual } from "@/common/utils"; import { emitter } from "@/web/common/event"; -import { GraphPartDetails } from "@/web/topic/components/TopicPane/GraphPartDetails"; +import { DetailsTab, GraphPartDetails } from "@/web/topic/components/TopicPane/GraphPartDetails"; import { TopicDetails } from "@/web/topic/components/TopicPane/TopicDetails"; import { Anchor, @@ -38,6 +38,9 @@ interface Props { const TopicPaneBase = ({ anchor, tabs }: Props) => { const [isOpen, setIsOpen] = useState(true); const [selectedTab, setSelectedTab] = useState(tabs[0]); + + const [selectedPartDetailsTab, setSelectedPartDetailsTab] = useState("Basics"); + const selectedGraphPart = useSelectedGraphPart(); useEffect(() => { @@ -71,7 +74,13 @@ const TopicPaneBase = ({ anchor, tabs }: Props) => { const TabPanelContent = { Details: selectedGraphPart !== null ? ( - + ) : ( ), diff --git a/src/web/topic/components/TopicWorkspace/MoreActionsDrawer.tsx b/src/web/topic/components/TopicWorkspace/MoreActionsDrawer.tsx index 20fc3927..935920e2 100644 --- a/src/web/topic/components/TopicWorkspace/MoreActionsDrawer.tsx +++ b/src/web/topic/components/TopicWorkspace/MoreActionsDrawer.tsx @@ -16,6 +16,7 @@ import { PowerInput, Route, SsidChart, + UnfoldMore, Upload, WbTwilight, } from "@mui/icons-material"; @@ -70,8 +71,10 @@ import { import { resetView, useFormat } from "@/web/view/currentViewStore/store"; import { resetQuickViews } from "@/web/view/quickViewStore/store"; import { + toggleExpandDetailsTabs, toggleFillNodesWithColor, toggleIndicateWhenNodeForcedToShow, + useExpandDetailsTabs, useFillNodesWithColor, useIndicateWhenNodeForcedToShow, } from "@/web/view/userConfigStore"; @@ -142,6 +145,7 @@ export const MoreActionsDrawer = ({ const fillNodesWithColor = useFillNodesWithColor(); const indicateWhenNodeForcedToShow = useIndicateWhenNodeForcedToShow(); + const expandDetailsTabs = useExpandDetailsTabs(); return ( + toggleExpandDetailsTabs()} + sx={{ borderRadius: "50%", border: "0" }} + > + + diff --git a/src/web/view/userConfigStore.ts b/src/web/view/userConfigStore.ts index 98ef0751..249e39b6 100644 --- a/src/web/view/userConfigStore.ts +++ b/src/web/view/userConfigStore.ts @@ -5,12 +5,14 @@ interface UserConfigStoreState { showIndicators: boolean; fillNodesWithColor: boolean; indicateWhenNodeForcedToShow: boolean; + expandDetailsTabs: boolean; } const initialState: UserConfigStoreState = { showIndicators: false, fillNodesWithColor: false, indicateWhenNodeForcedToShow: false, + expandDetailsTabs: false, }; const useUserConfigStore = create()( @@ -32,6 +34,10 @@ export const useIndicateWhenNodeForcedToShow = () => { return useUserConfigStore((state) => state.indicateWhenNodeForcedToShow); }; +export const useExpandDetailsTabs = () => { + return useUserConfigStore((state) => state.expandDetailsTabs); +}; + // actions export const toggleShowIndicators = () => { useUserConfigStore.setState((state) => ({ showIndicators: !state.showIndicators })); @@ -44,3 +50,7 @@ export const toggleFillNodesWithColor = (fill: boolean) => { export const toggleIndicateWhenNodeForcedToShow = (indicate: boolean) => { useUserConfigStore.setState({ indicateWhenNodeForcedToShow: indicate }); }; + +export const toggleExpandDetailsTabs = () => { + useUserConfigStore.setState((state) => ({ expandDetailsTabs: !state.expandDetailsTabs })); +}; From 84eff1261a88c3a4174d995e7bb498ed96039eca Mon Sep 17 00:00:00 2001 From: Joel Keyser Date: Mon, 16 Dec 2024 11:03:17 -0600 Subject: [PATCH 6/6] touchup: indicators directly open relevant tab --- src/web/comment/store/commentStore.ts | 2 +- .../ContextMenu/ViewDetailsMenuItem.tsx | 2 +- src/web/common/event.ts | 5 +++- .../components/Indicator/CommentIndicator.tsx | 2 +- .../components/Indicator/DetailsIndicator.tsx | 2 +- .../Indicator/FoundResearchIndicator.tsx | 2 +- .../Indicator/JustificationIndicator.tsx | 2 +- .../Indicator/QuestionIndicator.tsx | 2 +- .../topic/components/TopicPane/TopicPane.tsx | 26 +++++++++++++++++-- 9 files changed, 35 insertions(+), 10 deletions(-) diff --git a/src/web/comment/store/commentStore.ts b/src/web/comment/store/commentStore.ts index bc5870bf..4e70ac93 100644 --- a/src/web/comment/store/commentStore.ts +++ b/src/web/comment/store/commentStore.ts @@ -240,5 +240,5 @@ export const viewComment = (commentId: string) => { const commentParentGraphPartId = threadStarterComment.parentType === "topic" ? null : threadStarterComment.parentId; setSelected(commentParentGraphPartId); - emitter.emit("viewTopicDetails"); + emitter.emit("viewComments"); }; diff --git a/src/web/common/components/ContextMenu/ViewDetailsMenuItem.tsx b/src/web/common/components/ContextMenu/ViewDetailsMenuItem.tsx index 04741623..9fb41b40 100644 --- a/src/web/common/components/ContextMenu/ViewDetailsMenuItem.tsx +++ b/src/web/common/components/ContextMenu/ViewDetailsMenuItem.tsx @@ -8,7 +8,7 @@ export const ViewDetailsMenuItem = ({ graphPart }: { graphPart: GraphPart }) => { setSelected(graphPart.id); - emitter.emit("viewTopicDetails"); + emitter.emit("viewBasics"); }} > View details diff --git a/src/web/common/event.ts b/src/web/common/event.ts index 393528d5..7f8a4bcd 100644 --- a/src/web/common/event.ts +++ b/src/web/common/event.ts @@ -10,7 +10,10 @@ interface Events { changedDiagramFilter: () => void; changedLayoutConfig: () => void; changedView: (newView: ViewState) => void; - viewTopicDetails: () => void; + viewBasics: () => void; + viewJustification: () => void; + viewResearch: () => void; + viewComments: () => void; partSelected: (partId: string | null) => void; } diff --git a/src/web/topic/components/Indicator/CommentIndicator.tsx b/src/web/topic/components/Indicator/CommentIndicator.tsx index 7e02e933..b4a08755 100644 --- a/src/web/topic/components/Indicator/CommentIndicator.tsx +++ b/src/web/topic/components/Indicator/CommentIndicator.tsx @@ -21,7 +21,7 @@ export const CommentIndicator = ({ graphPartId, graphPartType, partColor }: Prop const onClick = useCallback(() => { setSelected(graphPartId); - emitter.emit("viewTopicDetails"); + emitter.emit("viewComments"); }, [graphPartId]); if (commentCount === 0) return <>; diff --git a/src/web/topic/components/Indicator/DetailsIndicator.tsx b/src/web/topic/components/Indicator/DetailsIndicator.tsx index 88aa75ee..2414eca3 100644 --- a/src/web/topic/components/Indicator/DetailsIndicator.tsx +++ b/src/web/topic/components/Indicator/DetailsIndicator.tsx @@ -15,7 +15,7 @@ export const DetailsIndicator = ({ graphPartId, notes }: Props) => { const onClick = useCallback(() => { setSelected(graphPartId); - emitter.emit("viewTopicDetails"); + emitter.emit("viewBasics"); }, [graphPartId]); return ; diff --git a/src/web/topic/components/Indicator/FoundResearchIndicator.tsx b/src/web/topic/components/Indicator/FoundResearchIndicator.tsx index 48078bed..5354332b 100644 --- a/src/web/topic/components/Indicator/FoundResearchIndicator.tsx +++ b/src/web/topic/components/Indicator/FoundResearchIndicator.tsx @@ -22,7 +22,7 @@ export const FoundResearchIndicator = ({ graphPartId, partColor }: Props) => { const onClick = useCallback(() => { setSelected(graphPartId); - emitter.emit("viewTopicDetails"); + emitter.emit("viewResearch"); }, [graphPartId]); if (foundResearchNodes.length === 0) return <>; diff --git a/src/web/topic/components/Indicator/JustificationIndicator.tsx b/src/web/topic/components/Indicator/JustificationIndicator.tsx index 7aade881..52ef06eb 100644 --- a/src/web/topic/components/Indicator/JustificationIndicator.tsx +++ b/src/web/topic/components/Indicator/JustificationIndicator.tsx @@ -22,7 +22,7 @@ export const JustificationIndicator = ({ graphPartId, partColor }: Props) => { const onClick = useCallback(() => { setSelected(graphPartId); - emitter.emit("viewTopicDetails"); + emitter.emit("viewJustification"); }, [graphPartId]); if (justificationNodes.length === 0) return <>; diff --git a/src/web/topic/components/Indicator/QuestionIndicator.tsx b/src/web/topic/components/Indicator/QuestionIndicator.tsx index 9e9e3519..a505f838 100644 --- a/src/web/topic/components/Indicator/QuestionIndicator.tsx +++ b/src/web/topic/components/Indicator/QuestionIndicator.tsx @@ -21,7 +21,7 @@ export const QuestionIndicator = ({ graphPartId, partColor }: Props) => { const onClick = useCallback(() => { setSelected(graphPartId); - emitter.emit("viewTopicDetails"); + emitter.emit("viewResearch"); }, [graphPartId]); if (questions.length === 0) return <>; diff --git a/src/web/topic/components/TopicPane/TopicPane.tsx b/src/web/topic/components/TopicPane/TopicPane.tsx index 6609af1b..83f9d1d3 100644 --- a/src/web/topic/components/TopicPane/TopicPane.tsx +++ b/src/web/topic/components/TopicPane/TopicPane.tsx @@ -46,8 +46,27 @@ const TopicPaneBase = ({ anchor, tabs }: Props) => { useEffect(() => { if (!tabs.includes("Details")) return; - const unbindSelectDetails = emitter.on("viewTopicDetails", () => { + const unbindSelectBasics = emitter.on("viewBasics", () => { setSelectedTab("Details"); + setSelectedPartDetailsTab("Basics"); + setIsOpen(true); + }); + + const unbindSelectJustification = emitter.on("viewJustification", () => { + setSelectedTab("Details"); + setSelectedPartDetailsTab("Justification"); + setIsOpen(true); + }); + + const unbindSelectResearch = emitter.on("viewResearch", () => { + setSelectedTab("Details"); + setSelectedPartDetailsTab("Research"); + setIsOpen(true); + }); + + const unbindSelectComments = emitter.on("viewComments", () => { + setSelectedTab("Details"); + setSelectedPartDetailsTab("Comments"); setIsOpen(true); }); @@ -56,7 +75,10 @@ const TopicPaneBase = ({ anchor, tabs }: Props) => { }); return () => { - unbindSelectDetails(); + unbindSelectBasics(); + unbindSelectJustification(); + unbindSelectResearch(); + unbindSelectComments(); unbindSelectedPart(); }; }, [tabs]);