Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 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
111 changes: 49 additions & 62 deletions packages/core/components/FileDetails/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,51 +105,41 @@ export default function FileDetails(props: Props) {
<div className={styles.overflowContainer}>
{props.fileDetails && (
<>
{!props.onClose ? (
<div className={styles.header}>
<div className={styles.leftAlign}>
<Pagination className={styles.pagination} />
</div>
{/* spacing component */}
<div className={styles.gutter}></div>
<div className={styles.rightAlign}>
<Tooltip content={disabledDownloadReason}>
<TertiaryButton
className={styles.tertiaryButton}
disabled={isDownloadDisabled}
iconName="Download"
id="download-file-button"
title={`Download file ${truncatedFileName} to local system`}
onClick={onDownload}
/>
</Tooltip>
<PrimaryButton
id={Tutorial.OPEN_WITH_ID}
className={styles.openWithButton}
iconName="ChevronDownMed"
text="Open with"
title="Open file by selected method"
menuItems={openWithMenuItems}
/>
</div>
<div className={styles.header}>
<div className={styles.leftAlign}>
<Pagination className={styles.pagination} />
</div>
) : (
<div className={styles.titleRow}>
<h4>Metadata</h4>
<TransparentIconButton
className={styles.clearButton}
iconName="Clear"
onClick={props.onClose}
{/* spacing component */}
<div className={styles.gutter}></div>
<div className={styles.rightAlign}>
<Tooltip content={disabledDownloadReason}>
<TertiaryButton
className={styles.tertiaryButton}
disabled={isDownloadDisabled}
iconName="Download"
id="download-file-button"
title={`Download file ${truncatedFileName} to local system`}
onClick={onDownload}
/>
</Tooltip>
<PrimaryButton
id={Tutorial.OPEN_WITH_ID}
className={styles.openWithButton}
iconName="ChevronDownMed"
text="Open with"
title="Open file by selected method"
menuItems={openWithMenuItems}
/>
{props.onClose && (
<TransparentIconButton
className={styles.clearButton}
iconName="Clear"
onClick={props.onClose}
/>
)}
</div>
)}
<p
className={classNames(styles.fileName, {
[styles.leftAlign]: !!props.onClose,
})}
>
{props.fileDetails?.name}
</p>
</div>
<p className={styles.fileName}>{props.fileDetails?.name}</p>
<div
className={classNames(styles.thumbnailContainer, {
[styles.thumbnailContainerClickable]: isThumbnailClickable,
Expand All @@ -168,9 +158,7 @@ export default function FileDetails(props: Props) {
}}
role={isThumbnailClickable ? "button" : undefined}
tabIndex={isThumbnailClickable ? 0 : undefined}
title={
isThumbnailClickable ? "Click to enlarge" : undefined
}
title={isThumbnailClickable ? "Click to enlarge" : undefined}
>
<FileThumbnail
className={styles.thumbnail}
Expand Down Expand Up @@ -200,24 +188,23 @@ export default function FileDetails(props: Props) {
}}
/>
</Modal>
{!props.onClose && (
<div className={styles.titleRow}>
<h4>Metadata</h4>
{hasProvenanceSource && (
<DefaultButton
onClick={() =>
dispatch(
interaction.actions.setOriginForProvenance(
props.fileDetails
)
<div className={styles.titleRow}>
<h4>Metadata</h4>
{/* To do: un-hide the provenance source button while in graph mode, should redraw graph */}
Comment thread
BrianWhitneyAI marked this conversation as resolved.
{hasProvenanceSource && !props.onClose && (
<DefaultButton
onClick={() =>
dispatch(
interaction.actions.setOriginForProvenance(
props.fileDetails
)
}
>
View provenance
</DefaultButton>
)}
</div>
)}
)
}
>
View provenance
</DefaultButton>
)}
</div>
<FileAnnotationList
className={styles.annotationList}
fileDetails={props.fileDetails}
Expand Down
18 changes: 18 additions & 0 deletions packages/core/components/NetworkGraph/NetworkGraph.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,24 @@
stroke: var(--primary-text-color);
}

.refresh-button {
width: unset;
color: var(--primary-text-color);
border: none;
position: absolute;
z-index: 100;
right: 0;
margin: 10px;
}

.refresh-button:hover {
color: var(--highlight-text-color);
}

.refresh-button i {
padding-right: 8px;
}

.simple-container {
display: flex;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
.file-node {
height: unset;
background-color: var(--primary-dark);
border: 1px solid var(--primary-text-color);
border: 1px solid var(--border-secondary-color);
color: var(--primary-text-color);
padding: 10px;
border-radius: 5px;
Expand All @@ -27,7 +27,10 @@
}

.current-file {
background-color: var(--aqua);
border-color: var(--highlight-background-color);
color: var(--highlight-text-color);
font-weight: 600;
}

.handle {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

.node {
background-color: var(--primary-dark);
border: 1px solid var(--primary-text-color);
border: 1px solid var(--border-secondary-color);
border-radius: 5px;
color: var(--primary-text-color);
display: flex;
Expand Down
113 changes: 79 additions & 34 deletions packages/core/components/NetworkGraph/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
import { SpinnerSize } from "@fluentui/react";
import { Edge, ReactFlow, EdgeTypes, useNodesState, useEdgesState } from "@xyflow/react";
import { DefaultButton, Icon, SpinnerSize } from "@fluentui/react";
import {
Edge,
ReactFlow,
EdgeTypes,
useNodesState,
useEdgesState,
Controls,
useReactFlow,
ReactFlowProvider,
} from "@xyflow/react";
import "@xyflow/react/dist/style.css";
import classNames from "classnames";
import React from "react";
import { useSelector } from "react-redux";
import { useDispatch, useSelector } from "react-redux";

import DefaultEdge from "./Edges/DefaultEdge";
import FileNode from "./Nodes/FileNode";
Expand All @@ -19,6 +28,7 @@ import {
import { interaction, selection } from "../../state";

import styles from "./NetworkGraph.module.css";
import buttonStyles from "../Buttons/TertiaryButton.module.css";

interface NetworkGraphProps {
className?: string;
Expand All @@ -37,20 +47,8 @@ const NODE_TYPES = {
* Component for rendering a graph at the given origin
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is awesome! What do you think about one step further so that no user of the component has to really think about it? This way they would import an already wrapped graph, but that part is shoved away nicely (IMO)

function NetworkGraph(props: NetworkGraphProps) {
    ...body of function as is...
}

export default function WrappedNetworkGraph(props: NetworkGraphProps) {
    return (
            <ReactFlowProvider>
                <NetworkGraph {...props} />
            </ReactFlowProvider>
     );
 }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense to me! Implemented in dddd5cb, I was split on whether or not to rename the import in RelationshipDiagram but left it as is for now, open to changing it

*/
export default function NetworkGraph(props: NetworkGraphProps) {
const graph = useSelector(interaction.selectors.getGraph);
const isLoading = useSelector(interaction.selectors.isGraphLoading);
const refreshKey = useSelector(interaction.selectors.getGraphRefreshKey);
const provenanceSource = useSelector(selection.selectors.getSelectedSourceProvenance);
const [edges, setEdges, onEdgesChange] = useEdgesState<Edge<AnnotationEdge>>([]);
const [nodes, setNodes, onNodesChange] = useNodesState<FileNodeType | MetadataNodeType>([]);

// Unfortunately we have to have some notion of state at a high level for control from the components
// and at the dagre level for when the user does a drag action causing this duplication of efforts
React.useEffect(() => {
setEdges(graph.edges);
setNodes(graph.nodes);
}, [graph, setEdges, setNodes, refreshKey]);

// The option to open this graph shouldn't even appear when a
// source isn't available so this shouldn't ever happen
if (!provenanceSource) {
Expand All @@ -65,26 +63,73 @@ export default function NetworkGraph(props: NetworkGraphProps) {
);
}

// The ReactFlow component can only access state (useReactFlow) if it's the child of a ReactFlowProvider
// See https://reactflow.dev/learn/troubleshooting/common-errors#001
function ReactFlowComponent() {
Comment thread
aswallace marked this conversation as resolved.
Outdated
const dispatch = useDispatch();
const [edges, setEdges, onEdgesChange] = useEdgesState<Edge<AnnotationEdge>>([]);
const [nodes, setNodes, onNodesChange] = useNodesState<FileNodeType | MetadataNodeType>([]);
const graph = useSelector(interaction.selectors.getGraph);
const refreshKey = useSelector(interaction.selectors.getGraphRefreshKey);
// Unfortunately we have to have some notion of state at a high level for control from the components
// and at the dagre level for when the user does a drag action causing this duplication of efforts
React.useEffect(() => {
let cancel = false;
if (!cancel) {
setEdges(graph.edges);
setNodes(graph.nodes);
}
return function cleanup() {
cancel = true;
};
}, [graph, setEdges, setNodes, refreshKey]);
const { fitView } = useReactFlow();
const onClickReset = () => {
graph.resetLayout(); // return to default layout if any
fitView(); // reset zoom
dispatch(interaction.actions.refreshGraph());
};
return (
<>
<DefaultButton
Comment thread
SeanDuHare marked this conversation as resolved.
Outdated
className={classNames(buttonStyles.button, styles.refreshButton)}
title="Reset graph to initial state"
onClick={onClickReset}
>
<Icon iconName="Refresh" />
Reset view
</DefaultButton>
<ReactFlow
fitView
onlyRenderVisibleElements
className={styles.graph}
edgesFocusable={false}
nodesDraggable={false}
nodesConnectable={false}
nodesFocusable={false}
elementsSelectable={true}
edgesReconnectable={false}
colorMode="dark"
nodes={nodes}
edges={edges}
edgeTypes={EDGE_TYPES}
nodeTypes={NODE_TYPES}
proOptions={{ hideAttribution: true }}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
>
<Controls showInteractive={false} />
</ReactFlow>
</>
);
}

// The wrapped ReactFlow component
return (
<div className={props.className}>
<ReactFlow
fitView
onlyRenderVisibleElements
className={styles.graph}
edgesFocusable={false}
nodesConnectable={false}
nodesFocusable={false}
elementsSelectable={false}
edgesReconnectable={false}
colorMode="dark"
nodes={nodes}
edges={edges}
edgeTypes={EDGE_TYPES}
nodeTypes={NODE_TYPES}
proOptions={{ hideAttribution: true }}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
/>
<div className={classNames(props.className, styles.reactFlow)}>
<ReactFlowProvider>
<ReactFlowComponent />
</ReactFlowProvider>
</div>
);
}
2 changes: 1 addition & 1 deletion packages/core/components/RelationshipDiagram/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export default function RelationshipDiagram({ className, origin }: Props) {
onClick={() => dispatch(interaction.actions.setOriginForProvenance(undefined))}
title="Close provenance relationship diagram"
/>
<h2>Provenance for {origin?.name}</h2>
<h2>Relationship diagram for {origin?.name}</h2>
</div>
<NetworkGraph className={styles.networkGraph} />
</div>
Expand Down
9 changes: 8 additions & 1 deletion packages/core/entity/Graph/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import FileService, { FmsFileAnnotation } from "../../services/FileService";
const FILE_NODE_WIDTH = 110;
const FILE_NODE_HEIGHT = 125;
const METADATA_NODE_WIDTH = 180;
const METADATA_NODE_HEIGHT = 45;
const METADATA_NODE_HEIGHT = 90;
const ROW_SPACING = Math.max(FILE_NODE_HEIGHT, METADATA_NODE_HEIGHT) + 25;
const COLUMN_SPACING = Math.max(FILE_NODE_WIDTH, METADATA_NODE_WIDTH) + 25;

Expand Down Expand Up @@ -330,6 +330,13 @@ export default class Graph {
.setDefaultEdgeLabel(() => ({}));
}

/**
* Reset the layout of the graph to its default (tree view)
*/
public resetLayout() {
dagre.layout(this.graph);
}

/**
* Position nodes within graph according to edge connections and
* height/width of individual nodes
Expand Down
2 changes: 2 additions & 0 deletions packages/core/styles/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ body {
--secondary-dark: #1c1c1c;
--accent-dark: #3d3d3d;
--medium-grey: #575859;
--med-light-grey: #999;
Comment thread
BrianWhitneyAI marked this conversation as resolved.
--light-grey: #BFBFBF;
--white: #ffffff;
--red: #c23030;
Expand All @@ -70,6 +71,7 @@ body {
--highlight-hover-background-color: var(--bright-aqua);
--highlight-hover-text-color: var(--white);
--border-color: var(--medium-grey);
--border-secondary-color: var(--med-light-grey);
--error-background-color: var(--red);
--error-text-color: var(--white);
--aqua-secondary-hover: rgba(13, 187, 206, 0.18);
Expand Down
Loading