Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Create insert panel for inserting instances and relations #48

Draft
wants to merge 29 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
005401d
wip: moving algorithms panel to bottom left toolbar, make sure to con…
aematei Mar 27, 2024
8333ea6
wip: consolidate toolbar
aematei Apr 16, 2024
58253a0
Merge changes from master
aematei Apr 20, 2024
a3cbd9d
Add arrow icon to download button to indicate a pop-out menu
aematei Apr 20, 2024
10b6202
wip: build InsertPanel ui component
aematei Apr 21, 2024
052973d
wip: build insert panel
aematei May 1, 2024
0c47837
Rename relationships to relations and components to instances
aematei May 21, 2024
0bfacdd
Merge branch 'master' into feat/30-insert-component
aematei May 21, 2024
8648303
Style insert panel and sub components
aematei May 21, 2024
22e0a1c
Install pragmatic dnd
aematei May 21, 2024
6d4f3e1
Install pragmatic-dnd
aematei May 23, 2024
e43e76a
Remove old click and drag code
aematei May 23, 2024
613cb93
Install tiny-invariant for use with pragmatic-dnd
aematei May 23, 2024
5cebdcd
Make new instances draggable and style similarly to diagram nodes
aematei May 23, 2024
35e108c
Rolled back a commit on local, need to merge to resolve conflict
aematei May 23, 2024
13f20d6
Style instance pane and instance item
aematei May 25, 2024
b496ca0
wip: initial relation pane styling
aematei May 25, 2024
c6ef490
wip: initial relation pane styling
aematei May 25, 2024
c0de6a7
Restructure InsertPanel UI components
aematei May 29, 2024
78e399d
Add highlighting to draggable elements
aematei May 30, 2024
aa55cdc
Change on hover for instance
greypilgrim May 30, 2024
5b02395
add insert panel component with relations data from OML model
aematei Jun 4, 2024
44a6052
add insert panel component with instance data from OML model
aematei Jun 5, 2024
fb764c5
Merge branch 'master' into feat/30-insert-component
aematei Jun 10, 2024
40dda23
Remove mock instance items
aematei Jun 10, 2024
c028f56
Reformat instance and relation items
aematei Jun 11, 2024
9b6b843
Fix npm package vulnerabilities
aematei Jun 11, 2024
101448d
Add util for formatting insert entities and its unit test
aematei Jun 11, 2024
b61aa43
Style insert items for hover and auto container resizinf
Jun 28, 2024
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
143 changes: 103 additions & 40 deletions view/src/components/Diagram/Diagram.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import ReactFlow, {
getConnectedEdges,
} from "reactflow";

import InsertPanel from "./InsertPanel";

// Icons
import { IconDownload } from "@nasa-jpl/react-stellar";
import LockIcon from "./Icons/Lock";
Expand All @@ -34,6 +36,7 @@ import { toPng, toSvg } from "html-to-image";
import Loader from "../shared/Loader";
import ITableData from "../../interfaces/ITableData";
import { LegendItem } from "../../interfaces/LegendItemType";
import { InsertItem } from "../../interfaces/InsertItemType";
import {
VSCodeButton,
VSCodeDropdown,
Expand Down Expand Up @@ -89,6 +92,7 @@ function Diagram({
const { fitView } = useReactFlow();
const { isInteractive, setInteractivity, toggleInteractivity } =
useCanvasInteractivity();
const [showDownloadMenu, setShowDownloadMenu] = useState<boolean>(true); // Using negative logic here

// FIXME: useOnSelectionChange occurs after a selection occurs and will continously running when clicking a node or edge
// useOnSelectionChange({
Expand Down Expand Up @@ -466,6 +470,27 @@ function Diagram({
return dropDownOptions;
};

/**
* This function toggles the download menu.
*
* @remarks
*
* @param
*
*/
const toggleDownloadMenu = () => {
setShowDownloadMenu(!showDownloadMenu);
};

const arrowIconSize = 16; // This constant sets the control button dropdown indicator arrow size.
var testInsertItem: InsertItem = {
label: "TestInsertItem",
onItemClicked: () => {console.log("Item was clicked!")},
icon: <LockIcon />
}

var insertItems: InsertItem[] = [testInsertItem];

return (
<div
className="w-screen h-screen"
Expand Down Expand Up @@ -506,11 +531,11 @@ function Diagram({
}}
>
{initData.legendItems.length > 0 && (
<Panel className="flow-panel" position="top-left">
<Panel className="flow-panel" position="top-right">
<Legend items={initData.legendItems} />
</Panel>
)}
<Panel className="flow-panel" position="top-right">
{/*<Panel className="flow-panel" position="top-right">
<div className="flex-col items-center z-10 space-y-2 space-x-2 p-2 rounded shadow-md bg-[var(--vscode-banner-background)]">
<VSCodeButton appearance="secondary" onClick={() => fitView()}>
Fit View
Expand All @@ -525,7 +550,7 @@ function Diagram({
<span slot="start" className="codicon codicon-filter"></span>
</VSCodeButton>
) : (
/* TODO: Implement Add Filter functionality */
//TODO: Implement Add Filter functionality
<VSCodeButton onClick={() => {}}>
Add Filter
<span slot="start" className="codicon codicon-filter"></span>
Expand All @@ -534,7 +559,8 @@ function Diagram({
{selectedAutoLayout(autoLayout)}
{selectedAlgorithmLayout(algorithmLayout)}
</div>
</Panel>
</Panel>*/}

<Controls className="flow-controls" showInteractive={false}>
{/* Implemented custom interactive button to avoid disabling selection in diagram */}
<ControlButton
Expand All @@ -546,46 +572,78 @@ function Diagram({
{isInteractive ? <UnlockIcon /> : <LockIcon />}
</ControlButton>
<ControlButton
onClick={pngDownloadDiagram}
title="download diagram"
aria-label="download diagram"
>
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
}}
>
<IconDownload
className="flex-shrink-0 flex-grow-0"
color="var(--vscode-button-secondaryForeground)"
width="16"
height="16"
/>
<span style={{ marginTop: "0.25px", fontSize: 9.5 }}>PNG</span>
</div>
</ControlButton>
<ControlButton
onClick={svgDownloadDiagram}
className={`react-flow__controls-interactive flex flex-row gap-[${
arrowIconSize / 4
}]`}
onPointerEnter={toggleDownloadMenu}
onPointerLeave={toggleDownloadMenu}
title="download diagram"
aria-label="download diagram"
>
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
}}
>
<IconDownload
className="flex-shrink-0 flex-grow-0"
color="var(--vscode-button-secondaryForeground)"
width="16"
height="16"
/>
<span style={{ marginTop: "0.25px", fontSize: 9.5 }}>SVG</span>
<IconDownload
className="flex-shrink-0 flex-grow-0"
color="var(--vscode-button-secondaryForeground)"
width="16"
height="16"
/>
<div className="">
{/* TODO: Style so it looks like icon.png */}
{/* <div className="h-2 w-2 border-x-8 border-x-transparent border-b-[16px] border-b-[#CCCCCC] rotate-90"></div>
*/}
<svg
width={arrowIconSize / 4}
height={arrowIconSize}
viewBox={`0 0 ${arrowIconSize / 4} ${arrowIconSize}`}
>
<path
d={`M 0 ${arrowIconSize} L ${
arrowIconSize / 4
} ${arrowIconSize} L ${arrowIconSize / 4} ${
(arrowIconSize * 3) / 4
} Z`}
fill="var(--vscode-button-secondaryForeground)"
/>
</svg>
</div>

{!showDownloadMenu && (
<div className="absolute left-[1.65rem]">
<ControlButton
onClick={pngDownloadDiagram}
title="download diagram"
aria-label="download diagram"
>
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
}}
>
<span style={{ marginTop: "0.25px", fontSize: 9.5 }}>
PNG
</span>
</div>
</ControlButton>
<ControlButton
onClick={svgDownloadDiagram}
title="download diagram"
aria-label="download diagram"
>
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
}}
>
<span style={{ marginTop: "0.25px", fontSize: 9.5 }}>
SVG
</span>
</div>
</ControlButton>
</div>
)}
</ControlButton>
</Controls>
<MiniMap
Expand All @@ -594,6 +652,11 @@ function Diagram({
pannable
nodeColor={getNodeColor}
/>
<Panel position="top-left" className="flow-panel">
<InsertPanel
components={insertItems}
relationships={insertItems}/>
</Panel>
<Background gap={12} size={1} />
</ReactFlow>
{rightClick && (
Expand Down
46 changes: 46 additions & 0 deletions view/src/components/Diagram/InsertPanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React, { ReactElement } from "react";

interface InsertItemProps {
label: string;
onItemClicked: () => void;
Copy link
Member

Choose a reason for hiding this comment

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

@aematei You may want to have a function called handleItemClicked. The code will be more reusable than arrow functions. This arrow function is anonymous so it may be hard to call the logic within the function in another part of the app.

Look at the official Typescript documentation and this Stack Overflow post for more details:

https://www.typescriptlang.org/docs/handbook/interfaces.html#function-types

https://stackoverflow.com/questions/45721344/arrow-function-in-typescript-interface-with-return-type-as-void

Copy link
Collaborator Author

@aematei aematei Apr 24, 2024

Choose a reason for hiding this comment

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

@pogi7 should I still include the function in the interface/type?

Copy link
Member

Choose a reason for hiding this comment

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

@aematei Yes. It would look like

interface InsertItemProps {
     label: string;
     onItemClicked: handleItemClicked
}

const handleItemClicked: void = () => {
     // you'll handle the logic of an item being clicked 
     console.log('Item Clicked');
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Oh got it! That makes sense, thanks.

icon: ReactElement<any>;
}

const InsertItem: React.FC<InsertItemProps> = ({ label, onItemClicked, icon }) => {
return (
<div onClick={ onItemClicked } className="flex flex-row">
<span className="text-[10px]">{ label }</span>
{ icon }
</div>
)
}

interface InsertPanelProps {
components: InsertItemProps[];
relationships: InsertItemProps[];
}

const InsertPanel: React.FC<InsertPanelProps> = ({ components, relationships }) => {
return (
<div className="flex flex-col {`z-10 p-2 space-y-2 rounded shadow-md bg-[var(--vscode-banner-background)] overflow-y-auto max-h-[9rem]`}">
<span className="font-bold">Components</span>
<div className="pl-4 border-solid border-2 ">
{components.map((component, index) => (
<InsertItem
label={component.label}
onItemClicked={component.onItemClicked}
icon={component.icon} />
))}
</div>
<span className="">Relationships</span>
{relationships.map((relationship, index) => (
<InsertItem
label={relationship.label}
onItemClicked={relationship.onItemClicked}
icon={relationship.icon} />
))}
</div>
);
}

export default InsertPanel;
7 changes: 7 additions & 0 deletions view/src/interfaces/InsertItemType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ReactElement } from "react"

export type InsertItem = {
label: string,
onItemClicked: () => void,
Copy link
Member

Choose a reason for hiding this comment

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

@aematei Please refer to my earlier comment regarding the arrow function
https://github.com/opencaesar/oml-vision/pull/48/files#r1574922754

icon: ReactElement<any>
}