From c67bebaf9ed15226134b54371d9fe628f379a337 Mon Sep 17 00:00:00 2001 From: Leonardo Christino <leomilho@gmail.com> Date: Sat, 28 Oct 2023 12:49:08 +0200 Subject: [PATCH] chore(layout): fix vis layout, typos also added query result status as a text in visualization --- apps/web/src/app/app.tsx | 2 + apps/web/src/app/panels/Visualization.tsx | 101 ++++++------ apps/web/src/components/navbar/navbar.tsx | 26 +-- docker-compose.yml | 1 - libs/shared/lib/components/LoadingSpinner.tsx | 25 +++ .../store/graphQueryResultSlice.ts | 9 +- libs/shared/lib/data-access/store/hooks.ts | 6 +- .../lib/data-access/store/sessionSlice.ts | 20 --- libs/shared/lib/data-access/store/store.ts | 4 +- .../data-access/store/visualisationSlice.ts | 36 ---- .../data-access/store/visualizationSlice.ts | 32 ++++ libs/shared/lib/schema/panel/schema.tsx | 156 +++++++++--------- 12 files changed, 208 insertions(+), 210 deletions(-) create mode 100644 libs/shared/lib/components/LoadingSpinner.tsx delete mode 100644 libs/shared/lib/data-access/store/visualisationSlice.ts create mode 100644 libs/shared/lib/data-access/store/visualizationSlice.ts diff --git a/apps/web/src/app/app.tsx b/apps/web/src/app/app.tsx index 67b3237ba..162bd5f87 100644 --- a/apps/web/src/app/app.tsx +++ b/apps/web/src/app/app.tsx @@ -24,6 +24,7 @@ import { GraphQueryResultFromBackend, GraphQueryResultFromBackendPayload, resetGraphQueryResults, + queryingBackend, } from '@graphpolaris/shared/lib/data-access/store/graphQueryResultSlice'; import { Query2BackendQuery, QueryBuilder, QueryMultiGraph } from '@graphpolaris/shared/lib/querybuilder'; import { Schema } from '@graphpolaris/shared/lib/schema/panel'; @@ -109,6 +110,7 @@ export function App(props: App) { if (query.nodes.length === 0) { dispatch(resetGraphQueryResults()); } else { + dispatch(queryingBackend()); api_query.execute(Query2BackendQuery(session.currentDatabase, query, queryBuilderSettings, ml)); } } diff --git a/apps/web/src/app/panels/Visualization.tsx b/apps/web/src/app/panels/Visualization.tsx index b41d5d881..a0c52c073 100644 --- a/apps/web/src/app/panels/Visualization.tsx +++ b/apps/web/src/app/panels/Visualization.tsx @@ -1,57 +1,60 @@ -import React from 'react'; +import React, { useMemo } from 'react'; import { RawJSONVis, NodeLinkVis, PaohVis, SemanticSubstrates } from '@graphpolaris/shared/lib/vis'; -import { useVisualizationCache } from '@graphpolaris/shared/lib/data-access'; +import { useGraphQueryResult, useQuerybuilderGraph, useVisualizationState } from '@graphpolaris/shared/lib/data-access'; +import { Visualizations } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice'; +import { LoadingSpinner } from '@graphpolaris/shared/lib/components/LoadingSpinner'; export const VisualizationPanel = () => { - const vis = useVisualizationCache() + const vis = useVisualizationState(); + const graphQueryResult = useGraphQueryResult(); + const query = useQuerybuilderGraph(); + + const visualizationComponent = useMemo(() => { + switch (vis.activeVisualization) { + case Visualizations.NodeLink: + return ( + <div id={Visualizations.NodeLink} className="tabContent w-full h-full"> + <NodeLinkVis /> + </div> + ); + case Visualizations.RawJSON: + return ( + <div id={Visualizations.RawJSON} className="tabContent w-full h-full"> + <RawJSONVis /> + </div> + ); + case Visualizations.Paohvis: + return ( + <div id={Visualizations.Paohvis} className="tabContent w-full h-full"> + <PaohVis rowHeight={30} hyperedgeColumnWidth={30} gapBetweenRanges={3} /> + </div> + ); + default: + return null; + } + }, [graphQueryResult]); + return ( - <div className="vis-panel h-full w-full overflow-y-auto"> - <h1>Visualization Panel</h1> - <p className='text-sm'>{vis.visual}</p> - {/* <div> - <button - onClick={() => - dispatch( - assignNewGraphQueryResult({ - nodes: [ - { - id: 'agent/007', - attributes: { name: 'Daniel Craig' }, - }, - ], - edges: [], - }) - ) - } - > - Load in mock result - </button> - <button - onClick={() => - dispatch(assignNewGraphQueryResult({ nodes: [], edges: [] })) - } - > - Remove mock result - </button> - </div> */} - {/* width: '83%', - height: '95vh', */} - <div className="w-full h-[calc(100%-2rem)]"> - <div id="NodeLinkVis" className='tabContent'> - {vis.visual === "NodeLinkVis" ? <><NodeLinkVis /></> : null} - </div> - <div id="RawJSONVis" className='tabContent'> - {vis.visual === "RawJSONVis" ? <><RawJSONVis /></> : null} - </div> - <div id="PaohVis" className='tabContent'> - {vis.visual === "PaohVis" ? <><PaohVis rowHeight={30} hyperedgeColumnWidth={30} gapBetweenRanges={3} /></> : null} - </div> - {/* <div id="SemSubVis" className='tabContent'> - <SemanticSubstrates /> - </div> */} + <div className="vis-panel h-full w-full overflow-y-auto" style={graphQueryResult.nodes.length === 0 ? { overflow: 'hidden' } : {}}> + <h1> + <span>Visualization Panel | </span> + <span className="text-sm">{vis.activeVisualization}</span> + </h1> + <div className="h-[calc(100%-2rem)]"> + {graphQueryResult.queryingBackend && ( + <div className="w-full h-full flex flex-col items-center justify-center overflow-hidden"> + <LoadingSpinner>Querying backend...</LoadingSpinner> + </div> + )} + {!graphQueryResult.queryingBackend && graphQueryResult.nodes.length === 0 ? ( + <div className="w-full h-full flex flex-col items-center justify-center"> + <p>No data available to be shown</p> + {query.nodes.length > 0 ? <p>Query resulted in empty dataset</p> : <p>Query for data to visualize</p>} + </div> + ) : ( + <div className="w-full h-full">{visualizationComponent}</div> + )} </div> - - {/* <div /> */} </div> ); }; diff --git a/apps/web/src/components/navbar/navbar.tsx b/apps/web/src/components/navbar/navbar.tsx index fcc676967..ff7864539 100644 --- a/apps/web/src/components/navbar/navbar.tsx +++ b/apps/web/src/components/navbar/navbar.tsx @@ -21,7 +21,7 @@ import { useDatabaseAPI, useSchemaAPI, useSessionCache, - useVisualizationCache, + useVisualizationState, } from '@graphpolaris/shared/lib/data-access'; import { DatabaseMenu } from './databasemenu'; import { NewDatabaseForm } from './AddDatabaseForm/newdatabaseform'; @@ -29,7 +29,7 @@ import { addError } from '@graphpolaris/shared/lib/data-access/store/configSlice import { SearchBar } from './search/SearchBar'; /** NavbarComponentProps is an interface containing the NavbarViewModel. */ -import { setVisual } from '@graphpolaris/shared/lib/data-access/store/visualisationSlice'; +import { Visualizations, setActiveVisualization } from '@graphpolaris/shared/lib/data-access/store/visualizationSlice'; export interface NavbarComponentProps { // changeColourPalette: () => void; FIXME move to redux } @@ -54,7 +54,7 @@ export interface NavbarSubComponentState { export const Navbar = (props: NavbarComponentProps) => { const auth = useAuthorizationCache(); const session = useSessionCache(); - const vis = useVisualizationCache(); + const vis = useVisualizationState(); const api = useDatabaseAPI(); const schemaApi = useSchemaAPI(); const dispatch = useAppDispatch(); @@ -77,7 +77,7 @@ export const Navbar = (props: NavbarComponentProps) => { /> <div title="GraphPolaris" className="navbar w-full"> <a href="https://graphpolaris.com/" className="mr-auto" target="_blank"> - <img src={currentLogo} alt="GraphPolaris" className="h-9"/> + <img src={currentLogo} alt="GraphPolaris" className="h-9" /> </a> <SearchBar /> <div className="dropdown"> @@ -85,25 +85,13 @@ export const Navbar = (props: NavbarComponentProps) => { Vis </label> <ul tabIndex={0} className="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-52"> - <li - onClick={() => { - dispatch(setVisual('NodeLinkVis')); - }} - > + <li onClick={() => dispatch(setActiveVisualization(Visualizations.NodeLink))}> <a>Node Link</a> </li> - <li - onClick={() => { - dispatch(setVisual('PaohVis')); - }} - > + <li onClick={() => dispatch(setActiveVisualization(Visualizations.Paohvis))}> <a>PaohVis</a> </li> - <li - onClick={() => { - dispatch(setVisual('RawJSONVis')); - }} - > + <li onClick={() => dispatch(setActiveVisualization(Visualizations.RawJSON))}> <a>JSON Structure</a> </li> {/* <li><a>Semantic Substrates</a></li> */} diff --git a/docker-compose.yml b/docker-compose.yml index e728d6c41..33d9dc555 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,7 +12,6 @@ services: - BACKEND_URL=http:// - BACKEND_WSS_URL=ws://client-updater-service/ - STAGING=dev - - SKIP_LOGIN=true - BACKEND_USER=user-management-service - BACKEND_QUERY=query-service - BACKEND_SCHEMA=schema-service diff --git a/libs/shared/lib/components/LoadingSpinner.tsx b/libs/shared/lib/components/LoadingSpinner.tsx new file mode 100644 index 000000000..69e5c8101 --- /dev/null +++ b/libs/shared/lib/components/LoadingSpinner.tsx @@ -0,0 +1,25 @@ +import { PropsWithChildren } from 'react'; + +export const LoadingSpinner = (props: PropsWithChildren) => { + return ( + <div className="text-sm"> + <svg + aria-hidden="true" + className="inline w-6 h-6 mr-2 text-gray-200 animate-spin fill-entity-600" + viewBox="0 0 100 101" + fill="none" + xmlns="http://www.w3.org/2000/svg" + > + <path + d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z" + fill="currentColor" + /> + <path + d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z" + fill="currentFill" + /> + </svg> + {props.children ? props.children : 'Connecting...'} + </div> + ); +}; diff --git a/libs/shared/lib/data-access/store/graphQueryResultSlice.ts b/libs/shared/lib/data-access/store/graphQueryResultSlice.ts index 634d35068..773e5891d 100755 --- a/libs/shared/lib/data-access/store/graphQueryResultSlice.ts +++ b/libs/shared/lib/data-access/store/graphQueryResultSlice.ts @@ -51,6 +51,7 @@ export interface GraphQueryResult { // Describes what entities there are in this graph query result. nodeTypes: string[]; + queryingBackend: boolean; } // Define the initial state using that type @@ -58,6 +59,7 @@ export const initialState: GraphQueryResult = { nodes: [], edges: [], nodeTypes: [], + queryingBackend: false, }; export const graphQueryResultSlice = createSlice({ @@ -102,17 +104,22 @@ export const graphQueryResultSlice = createSlice({ state.nodes = nodes; state.edges = edges; state.nodeTypes = nodeTypes; + state.queryingBackend = false; }, resetGraphQueryResults: (state) => { // Assign new state state.nodes = []; state.edges = []; state.nodeTypes = []; + state.queryingBackend = false; + }, + queryingBackend: (state) => { + state.queryingBackend = true; }, }, }); -export const { assignNewGraphQueryResult, resetGraphQueryResults } = graphQueryResultSlice.actions; +export const { assignNewGraphQueryResult, resetGraphQueryResults, queryingBackend } = graphQueryResultSlice.actions; // Other code such as selectors can use the imported `RootState` type export const selectGraphQueryResult = (state: RootState) => state.graphQueryResult; diff --git a/libs/shared/lib/data-access/store/hooks.ts b/libs/shared/lib/data-access/store/hooks.ts index 35aca4601..c6da6ad30 100644 --- a/libs/shared/lib/data-access/store/hooks.ts +++ b/libs/shared/lib/data-access/store/hooks.ts @@ -10,7 +10,7 @@ import { } from '@graphpolaris/shared/lib/data-access/store/querybuilderSlice'; import { sessionCacheState } from './sessionSlice'; import { authState } from './authSlice'; -import { visualState } from './visualisationSlice'; +import { visualizationState } from './visualizationSlice'; import { allMLEnabled, selectML } from './mlSlice'; import { searchResultState, searchResultData, searchResultSchema, searchResultQB } from './searchResultSlice'; @@ -36,7 +36,6 @@ export const useQuerybuilderSettings = () => useAppSelector(queryBuilderSettings export const useConfig = () => useAppSelector(configState); export const useSessionCache = () => useAppSelector(sessionCacheState); export const useAuthorizationCache = () => useAppSelector(authState); -export const useVisualizationCache = () =>useAppSelector(visualState); // Machine Learning Slices export const useML = () => useAppSelector(selectML); @@ -47,3 +46,6 @@ export const useSearchResult = () => useAppSelector(searchResultState); export const useSearchResultData = () => useAppSelector(searchResultData); export const useSearchResultSchema = () => useAppSelector(searchResultSchema); export const useSearchResultQB = () => useAppSelector(searchResultQB); + +// Visualization Slices +export const useVisualizationState = () => useAppSelector(visualizationState); diff --git a/libs/shared/lib/data-access/store/sessionSlice.ts b/libs/shared/lib/data-access/store/sessionSlice.ts index bc9e98a8b..02c7201e9 100644 --- a/libs/shared/lib/data-access/store/sessionSlice.ts +++ b/libs/shared/lib/data-access/store/sessionSlice.ts @@ -13,26 +13,6 @@ export type SessionCacheI = { currentDatabase?: string; databases: string[]; error?: ErrorMessage; - - // queryListOpen: boolean; - // queryStatusList: { - // queries: Record<string, QueryStatusListItem>; - // queryIDsOrder: string[]; - // }; - // functionsMenuOpen: boolean; - // currentDatabaseKey: string; - // currentColours: { key: string; index: number }; - // elementsperDatabaseObject: Record<string, (AnyNode | Edge<any>)[]>; - // autoSendQueries: boolean; - // panelWidthHeights: { - // windowinnerHeight: number; - // windowinnerWidth: number; - // navBarHeight: number; - // schemaDrawerHeight: number; - // queryDrawerHeight: number; - // schemaDrawerWidth: number; - // queryDrawerWidth: number; - // }; }; // Define the initial state using that type diff --git a/libs/shared/lib/data-access/store/store.ts b/libs/shared/lib/data-access/store/store.ts index f54cdc8d2..af20b8bef 100644 --- a/libs/shared/lib/data-access/store/store.ts +++ b/libs/shared/lib/data-access/store/store.ts @@ -7,7 +7,7 @@ import sessionSlice from './sessionSlice'; import authSlice from './authSlice'; import mlSlice from './mlSlice'; import searchResultSlice from './searchResultSlice'; -import visualisationSlice from './visualisationSlice'; +import visualizationSlice from './visualizationSlice'; export const store = configureStore({ reducer: { @@ -19,7 +19,7 @@ export const store = configureStore({ config: configSlice, ml: mlSlice, searchResults: searchResultSlice, - visualize: visualisationSlice + visualize: visualizationSlice, }, middleware: (getDefaultMiddleware) => getDefaultMiddleware({ diff --git a/libs/shared/lib/data-access/store/visualisationSlice.ts b/libs/shared/lib/data-access/store/visualisationSlice.ts deleted file mode 100644 index 75d27ae76..000000000 --- a/libs/shared/lib/data-access/store/visualisationSlice.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { createSlice, PayloadAction } from '@reduxjs/toolkit'; -import type { RootState } from './store'; -import { dispatch } from 'd3'; - -// Define the initial state using that type -export const initialState = { - visual: 'NodeLinkVis', - tab: 'NodeLinkTab' - -}; - -export const visualisationSlice = createSlice({ - name: 'visualisation', - // `createSlice` will infer the state type from the `initialState` argument - initialState, - reducers: { - setVisualTab: (state, action) => { - state.visual = action.payload[0] - state.tab = action.payload[1] - }, - setTab: (state, action) => { - state.tab=action.payload - }, - setVisual: (state,action) => { - state.visual=action.payload - } - } - }, -); - -export const {setVisualTab, setTab, setVisual} = visualisationSlice.actions; - -// Other code such as selectors can use the imported `RootState` type -// export const configState = (state: RootState) => state.config; -export const visualState = (state:RootState)=> state.visualize; -export default visualisationSlice.reducer; diff --git a/libs/shared/lib/data-access/store/visualizationSlice.ts b/libs/shared/lib/data-access/store/visualizationSlice.ts new file mode 100644 index 000000000..a84757a0d --- /dev/null +++ b/libs/shared/lib/data-access/store/visualizationSlice.ts @@ -0,0 +1,32 @@ +import { RawJSONVis } from '@graphpolaris/shared/lib/vis'; +import { createSlice, PayloadAction } from '@reduxjs/toolkit'; +import type { RootState } from './store'; + +export enum Visualizations { + NodeLink = 'NodeLink', + Paohvis = 'Paohvis', + RawJSON = 'RawJSON', +} + +type VisState = { + activeVisualization: Visualizations; +}; + +export const initialState: VisState = { + activeVisualization: Visualizations.NodeLink, +}; + +export const visualizationSlice = createSlice({ + name: 'visualization', + initialState, + reducers: { + setActiveVisualization: (state, action: PayloadAction<Visualizations>) => { + state.activeVisualization = action.payload; + }, + }, +}); + +export const { setActiveVisualization } = visualizationSlice.actions; + +export const visualizationState = (state: RootState) => state.visualize; +export default visualizationSlice.reducer; diff --git a/libs/shared/lib/schema/panel/schema.tsx b/libs/shared/lib/schema/panel/schema.tsx index 3e5b32ead..1d7a31192 100644 --- a/libs/shared/lib/schema/panel/schema.tsx +++ b/libs/shared/lib/schema/panel/schema.tsx @@ -36,6 +36,7 @@ import SelfEdge from '../pills/edges/self-edge'; import { useSchemaAPI } from '../../data-access'; import { SchemaDialog } from './schemaDialog'; import { toSchemaGraphology } from '../../data-access/store/schemaSlice'; +import { LoadingSpinner } from '../../components/LoadingSpinner'; interface Props { content?: string; @@ -166,88 +167,83 @@ export const Schema = (props: Props) => { <div className="schema-panel w-full h-full"> <SchemaDialog open={toggleSchemaSettings} onClose={() => setToggleSchemaSettings(false)} ref={dialogRef} /> <div className="flex flex-col h-[1rem]"> - <h1>Schema</h1> - {loading ? ( - <p className="text-sm"> - <svg - aria-hidden="true" - className="inline w-6 h-6 mr-2 text-gray-200 animate-spin fill-entity-600" - viewBox="0 0 100 101" - fill="none" - xmlns="http://www.w3.org/2000/svg" - > - <path - d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z" - fill="currentColor" - /> - <path - d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z" - fill="currentFill" - /> - </svg> - Connecting... - </p> - ) : ( - <p className="text-sm">{`Connected to: ${session.currentDatabase}`}</p> - )} - - {nodes.length === 0 && !loading && <p className="text-sm">No Elements</p>} + <h1> + Schema{' '} + {loading ? ( + <span> + <LoadingSpinner>Connecting to {session.currentDatabase}...</LoadingSpinner> + </span> + ) : ( + <> + {session.currentDatabase && ( + <> + {' | '} + <span className="text-sm"> Connected to: {session.currentDatabase}</span> + </> + )} + </> + )} + </h1> </div> - <ReactFlowProvider> - <div className="h-[calc(100%-.8rem)] w-full"> - <ReactFlow - snapGrid={[10, 10]} - snapToGrid - onlyRenderVisibleElements={false} - nodesDraggable={true} - nodeTypes={nodeTypes} - edgeTypes={edgeTypes} - connectionLineComponent={ConnectionDragLine} - onNodesChange={onNodeChanged} - onEdgesChange={onEdgeChanged} - nodes={nodes} - edges={edges} - onInit={onInit} - proOptions={{ hideAttribution: true }} - > - <Controls showInteractive={false} showZoom={false} showFitView={true} className={`${styles.controls} schema-settings`}> - {/* <ControlButton - className={styles.exportButton} - title={'Export graph schema'} - onClick={(event) => { - event.stopPropagation(); - // this.setState({ - // ...this.state, - // exportMenuAnchor: event.currentTarget, - // }); - }} + {nodes.length === 0 && !loading ? ( + <p className="text-sm">No Elements</p> + ) : ( + <ReactFlowProvider> + <div className="h-[calc(100%-.8rem)] w-full"> + <ReactFlow + snapGrid={[10, 10]} + snapToGrid + onlyRenderVisibleElements={false} + nodesDraggable={true} + nodeTypes={nodeTypes} + edgeTypes={edgeTypes} + connectionLineComponent={ConnectionDragLine} + onNodesChange={onNodeChanged} + onEdgesChange={onEdgeChanged} + nodes={nodes} + edges={edges} + onInit={onInit} + proOptions={{ hideAttribution: true }} > - <img src={exportIcon} width={21}></img> - </ControlButton> */} - <ControlButton - className={styles.exportButton} - title={'Refresh graph schema'} - onClick={(event) => { - event.stopPropagation(); - api_schema.RequestSchema(session.currentDatabase); - }} - > - <CachedIcon /> - </ControlButton> - <ControlButton - className={styles.exportButton} - title={'Open Settings'} - onClick={(event) => { - event.stopPropagation(); - setToggleSchemaSettings(!toggleSchemaSettings); - }} - > - <SettingsIcon ref={settingsIconRef} /> - </ControlButton> - </Controls> - </ReactFlow> - </div> - </ReactFlowProvider> + <Controls showInteractive={false} showZoom={false} showFitView={true} className={`${styles.controls} schema-settings`}> + {/* <ControlButton + className={styles.exportButton} + title={'Export graph schema'} + onClick={(event) => { + event.stopPropagation(); + // this.setState({ + // ...this.state, + // exportMenuAnchor: event.currentTarget, + // }); + }} + > + <img src={exportIcon} width={21}></img> + </ControlButton> */} + <ControlButton + className={styles.exportButton} + title={'Refresh graph schema'} + onClick={(event) => { + event.stopPropagation(); + api_schema.RequestSchema(session.currentDatabase); + }} + > + <CachedIcon /> + </ControlButton> + <ControlButton + className={styles.exportButton} + title={'Open Settings'} + onClick={(event) => { + event.stopPropagation(); + setToggleSchemaSettings(!toggleSchemaSettings); + }} + > + <SettingsIcon ref={settingsIconRef} /> + </ControlButton> + </Controls> + </ReactFlow> + </div> + </ReactFlowProvider> + )} </div> ); }; -- GitLab