diff --git a/packages/graph-explorer/src/components/NavBar.tsx b/packages/graph-explorer/src/components/NavBar.tsx new file mode 100644 index 000000000..b4565282c --- /dev/null +++ b/packages/graph-explorer/src/components/NavBar.tsx @@ -0,0 +1,116 @@ +import { cn } from "@/utils"; +import GraphExplorerIcon from "@/components/icons/GraphExplorerIcon"; +import type { ComponentPropsWithRef, ReactNode } from "react"; + +type NavBarProps = { + logoVisible?: boolean; +}; + +export function NavBar({ + className, + children, + logoVisible, +}: ComponentPropsWithRef<"div"> & NavBarProps) { + return ( +
+ {logoVisible ? : null} + {children} +
+ ); +} + +export function NavBarContent({ + className, + ...props +}: ComponentPropsWithRef<"div">) { + return ( +
+ ); +} + +export function NavBarActions({ + className, + ...props +}: ComponentPropsWithRef<"div">) { + return ( +
+ ); +} + +function NavBarLogo({ className, ...rest }: ComponentPropsWithRef<"div">) { + return ( +
+ +
+ ); +} + +export function NavBarVersion({ + className, + ...props +}: ComponentPropsWithRef<"div">) { + return ( +
+ ); +} + +type NavBarTitleProps = { + title?: ReactNode; + subtitle?: ReactNode; +}; + +export function NavBarTitle({ + title, + subtitle, + className, + children, + ...props +}: ComponentPropsWithRef<"div"> & NavBarTitleProps) { + return ( +
+
+ {title && ( +
{title}
+ )} + {subtitle && ( +
+ {subtitle} +
+ )} +
+ {children} +
+ ); +} diff --git a/packages/graph-explorer/src/components/Panel.tsx b/packages/graph-explorer/src/components/Panel.tsx index 90c366957..f74672e7a 100644 --- a/packages/graph-explorer/src/components/Panel.tsx +++ b/packages/graph-explorer/src/components/Panel.tsx @@ -8,9 +8,26 @@ interface PanelProps extends React.ComponentPropsWithRef<"div"> { variant?: "default" | "sidebar"; } +export function PanelGroup({ + className, + ...props +}: React.ComponentPropsWithRef<"div">) { + return ( +
+ ); +} + function Panel({ variant = "default", className, ...props }: PanelProps) { return (
) { + return ( +
+ ); +} + +export function WorkspaceContent({ + className, + ...props +}: ComponentPropsWithRef<"div">) { + return ( +
+ ); +} diff --git a/packages/graph-explorer/src/components/Workspace/Workspace.tsx b/packages/graph-explorer/src/components/Workspace/Workspace.tsx deleted file mode 100644 index 0dc29ee4c..000000000 --- a/packages/graph-explorer/src/components/Workspace/Workspace.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { cn, getChildrenOfType } from "@/utils"; -import type { PropsWithChildren, ReactElement } from "react"; -import getChildOfType from "@/utils/getChildOfType"; -import WorkspacesContent from "./components/WorkspacesContent"; -import WorkspaceSideBar from "./components/WorkspaceSideBar"; -import WorkspaceTopBar from "./components/WorkspaceTopBar"; - -export type WorkspaceProps = { - className?: string; -}; - -interface WorkspaceComposition { - TopBar: typeof WorkspaceTopBar; - Content: typeof WorkspacesContent; - SideBar: typeof WorkspaceSideBar; -} - -const Workspace = ({ - children, - className, -}: PropsWithChildren) => { - const topBarSection = getChildrenOfType( - children, - WorkspaceTopBar.displayName || WorkspaceTopBar.name, - ); - - const contentSection = getChildOfType( - children, - WorkspacesContent.displayName || WorkspacesContent.name, - ); - const sidebarSection = getChildOfType( - children, - WorkspaceSideBar.displayName || WorkspaceSideBar.name, - ); - - return ( -
- {topBarSection} -
- {contentSection} - {sidebarSection} -
-
- ); -}; - -Workspace.TopBar = WorkspaceTopBar; -Workspace.Content = WorkspacesContent; -Workspace.SideBar = WorkspaceSideBar; - -export default Workspace as (( - props: PropsWithChildren, -) => ReactElement) & - WorkspaceComposition; diff --git a/packages/graph-explorer/src/components/Workspace/components/NavBarLogo.tsx b/packages/graph-explorer/src/components/Workspace/components/NavBarLogo.tsx deleted file mode 100644 index 4ef22de90..000000000 --- a/packages/graph-explorer/src/components/Workspace/components/NavBarLogo.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { cn } from "@/utils"; -import GraphExplorerIcon from "@/components/icons/GraphExplorerIcon"; -import type { ComponentPropsWithRef } from "react"; - -export default function NavBarLogo({ - className, - ...rest -}: ComponentPropsWithRef<"div">) { - return ( -
- -
- ); -} diff --git a/packages/graph-explorer/src/components/Workspace/components/SidebarButton.tsx b/packages/graph-explorer/src/components/Workspace/components/SidebarButton.tsx deleted file mode 100644 index 2b00e8acb..000000000 --- a/packages/graph-explorer/src/components/Workspace/components/SidebarButton.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import * as React from "react"; -import { Toggle as TogglePrimitive } from "radix-ui"; -import { Tooltip, TooltipContent, TooltipTrigger } from "@/components"; -import { cn } from "@/utils"; - -function SidebarButton({ - icon, - title, - badge = false, - className, - children, - ...props -}: React.ComponentPropsWithRef & { - icon: React.ReactElement; - title: React.ReactNode; - badge?: boolean; -}) { - return ( - - - - - - {icon} - {children} - {title} - - - - {title} - - - ); -} - -function Badge({ - children, - value, - ...props -}: React.ComponentPropsWithoutRef<"div"> & { value?: boolean }) { - return ( -
- {children} - {value ? ( - - ) : null} -
- ); -} - -SidebarButton.displayName = "SidebarButton"; - -export { SidebarButton }; diff --git a/packages/graph-explorer/src/components/Workspace/components/WorkspaceSideBar.tsx b/packages/graph-explorer/src/components/Workspace/components/WorkspaceSideBar.tsx deleted file mode 100644 index cf3a42824..000000000 --- a/packages/graph-explorer/src/components/Workspace/components/WorkspaceSideBar.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import { cn } from "@/utils"; -import { useState, type PropsWithChildren, type ReactElement } from "react"; -import getChildOfType from "@/utils/getChildOfType"; -import getChildrenOfType from "@/utils/getChildrenOfType"; -import WorkspaceSideBarContent from "./WorkspaceSideBarContent"; -import { Resizable } from "re-resizable"; -import { - CLOSED_SIDEBAR_WIDTH, - DEFAULT_SIDEBAR_WIDTH, - useSidebar, - useSidebarSize, -} from "@/core"; - -interface WorkspaceSideBarComposition { - Content: typeof WorkspaceSideBarContent; -} - -export type WorkspaceSideBarProps = { - direction?: "row" | "row-reverse"; -}; - -const WorkspaceSideBar = ({ - children, - direction = "row", -}: PropsWithChildren) => { - const sidebarContent = getChildOfType( - children, - WorkspaceSideBarContent.displayName || WorkspaceSideBarContent.name, - ); - - const sidebarActions = getChildrenOfType( - children, - WorkspaceSideBarContent.displayName || WorkspaceSideBarContent.name, - true, - ); - - const { isSidebarOpen } = useSidebar(); - const [sidebarWidth, setSidebarWidth] = useSidebarSize(); - - // The transition animation used for opening and closing sidebar animation - // does not play well with the resizing behavior of the Resizable component. - // If the animation is not disabled, the resize will feel jerky. - const [enableAnimation, setEnableAnimation] = useState(true); - - return ( - setEnableAnimation(false)} - onResizeStop={(_e, _direction, _ref, delta) => { - setEnableAnimation(true); - setSidebarWidth(delta.width); - }} - className={cn( - enableAnimation && - "transition-width transform duration-200 ease-in-out", - )} - > -
-
- {sidebarActions} -
- {sidebarContent} -
-
- ); -}; - -WorkspaceSideBar.displayName = "WorkspaceSideBar"; - -WorkspaceSideBar.Content = WorkspaceSideBarContent; - -export default WorkspaceSideBar as (( - props: PropsWithChildren, -) => ReactElement) & - WorkspaceSideBarComposition & { displayName: string }; diff --git a/packages/graph-explorer/src/components/Workspace/components/WorkspaceSideBarContent.tsx b/packages/graph-explorer/src/components/Workspace/components/WorkspaceSideBarContent.tsx deleted file mode 100644 index 35310e516..000000000 --- a/packages/graph-explorer/src/components/Workspace/components/WorkspaceSideBarContent.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { cn } from "@/utils"; -import type { ComponentPropsWithRef } from "react"; - -export type WorkspaceSideBarContentProps = { - className?: string; -}; - -const WorkspaceSideBarContent = ({ - className, - ...props -}: ComponentPropsWithRef<"div">) => { - return
; -}; - -WorkspaceSideBarContent.displayName = "WorkspaceSideBarContent"; - -export default WorkspaceSideBarContent; diff --git a/packages/graph-explorer/src/components/Workspace/components/WorkspaceTopBar.tsx b/packages/graph-explorer/src/components/Workspace/components/WorkspaceTopBar.tsx deleted file mode 100644 index 559171363..000000000 --- a/packages/graph-explorer/src/components/Workspace/components/WorkspaceTopBar.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import { cn, groupChildrenByType } from "@/utils"; -import type { PropsWithChildren, ReactElement } from "react"; -import NavBarLogo from "./NavBarLogo"; -import WorkspaceTopBarAdditionalControls from "./WorkspaceTopBarAdditionalControls"; -import WorkspaceTopBarContent from "./WorkspaceTopBarContent"; -import WorkspaceTopBarTitle from "./WorkspaceTopBarTitle"; -import WorkspaceTopBarVersion from "./WorkspaceTopBarVersion"; - -export type WorkspaceTopBarProps = { - className?: string; - logoVisible?: boolean; -}; - -interface WorkspaceTopBarComposition { - Title: typeof WorkspaceTopBarTitle; - AdditionalControls: typeof WorkspaceTopBarAdditionalControls; - Content: typeof WorkspaceTopBarContent; - Version: typeof WorkspaceTopBarVersion; -} - -const WorkspaceTopBar = ({ - className, - children, - logoVisible, -}: PropsWithChildren) => { - const childrenByType = groupChildrenByType( - children, - [ - WorkspaceTopBarTitle.displayName || WorkspaceTopBarTitle.name, - WorkspaceTopBarContent.displayName || WorkspaceTopBarContent.name, - WorkspaceTopBarAdditionalControls.displayName || - WorkspaceTopBarAdditionalControls.name, - WorkspaceTopBarVersion.displayName || WorkspaceTopBarVersion.name, - ], - "rest", - ); - - return ( -
-
- {logoVisible ? : null} - { - childrenByType[ - WorkspaceTopBarTitle.displayName || WorkspaceTopBarTitle.name - ] - } - {childrenByType[ - WorkspaceTopBarContent.displayName || WorkspaceTopBarContent.name - ] ??
} - { - childrenByType[ - WorkspaceTopBarVersion.displayName || WorkspaceTopBarVersion.name - ] - } - { - childrenByType[ - WorkspaceTopBarAdditionalControls.displayName || - WorkspaceTopBarAdditionalControls.name - ] - } -
- {childrenByType.rest?.length > 0 ? ( -
- {childrenByType.rest} -
- ) : null} -
- ); -}; - -WorkspaceTopBar.displayName = "WorkspaceTopBar"; - -WorkspaceTopBar.Title = WorkspaceTopBarTitle; -WorkspaceTopBar.AdditionalControls = WorkspaceTopBarAdditionalControls; -WorkspaceTopBar.Content = WorkspaceTopBarContent; -WorkspaceTopBar.Version = WorkspaceTopBarVersion; - -export default WorkspaceTopBar as (( - props: PropsWithChildren, -) => ReactElement) & - WorkspaceTopBarComposition & { displayName: string }; diff --git a/packages/graph-explorer/src/components/Workspace/components/WorkspaceTopBarAdditionalControls.tsx b/packages/graph-explorer/src/components/Workspace/components/WorkspaceTopBarAdditionalControls.tsx deleted file mode 100644 index 22af2119e..000000000 --- a/packages/graph-explorer/src/components/Workspace/components/WorkspaceTopBarAdditionalControls.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { cn } from "@/utils"; -import type { PropsWithChildren } from "react"; - -export type WorkspaceTopBarAdditionalControlsProps = { - className?: string; -}; - -const WorkspaceTopBarAdditionalControls = ({ - className, - children, -}: PropsWithChildren) => { - return ( -
- {children} -
- ); -}; - -WorkspaceTopBarAdditionalControls.displayName = - "WorkspaceTopBarAdditionalControls"; - -export default WorkspaceTopBarAdditionalControls; diff --git a/packages/graph-explorer/src/components/Workspace/components/WorkspaceTopBarContent.tsx b/packages/graph-explorer/src/components/Workspace/components/WorkspaceTopBarContent.tsx deleted file mode 100644 index 689ba0d00..000000000 --- a/packages/graph-explorer/src/components/Workspace/components/WorkspaceTopBarContent.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { cn } from "@/utils"; -import type { PropsWithChildren } from "react"; - -export type WorkspaceTopBarContentProps = { - className?: string; -}; - -const WorkspaceTopBarContent = ({ - className, - children, -}: PropsWithChildren) => { - return ( -
- {children} -
- ); -}; - -WorkspaceTopBarContent.displayName = "WorkspaceTopBarContent"; - -export default WorkspaceTopBarContent; diff --git a/packages/graph-explorer/src/components/Workspace/components/WorkspaceTopBarTitle.tsx b/packages/graph-explorer/src/components/Workspace/components/WorkspaceTopBarTitle.tsx deleted file mode 100644 index c3b7c515e..000000000 --- a/packages/graph-explorer/src/components/Workspace/components/WorkspaceTopBarTitle.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { cn } from "@/utils"; -import type { PropsWithChildren, ReactNode } from "react"; - -export type WorkspaceTopBarTitleProps = { - className?: string; - title?: ReactNode; - subtitle?: ReactNode; -}; - -const WorkspaceTopBarTitle = ({ - className, - title, - subtitle, - children, -}: PropsWithChildren) => { - return ( -
-
- {title && ( -
{title}
- )} - {subtitle && ( -
- {subtitle} -
- )} -
- {children} -
- ); -}; - -WorkspaceTopBarTitle.displayName = "WorkspaceTopBarTitle"; - -export default WorkspaceTopBarTitle; diff --git a/packages/graph-explorer/src/components/Workspace/components/WorkspaceTopBarVersion.tsx b/packages/graph-explorer/src/components/Workspace/components/WorkspaceTopBarVersion.tsx deleted file mode 100644 index c133d6856..000000000 --- a/packages/graph-explorer/src/components/Workspace/components/WorkspaceTopBarVersion.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { cn } from "@/utils"; -import type { PropsWithChildren } from "react"; - -export type WorkspaceTopBarVersionProps = { - className?: string; -}; - -const WorkspaceTopBarVersion = ({ - className, - children, -}: PropsWithChildren) => { - return ( -
- {children} -
- ); -}; - -WorkspaceTopBarVersion.displayName = "WorkspaceTopBarVersion"; - -export default WorkspaceTopBarVersion; diff --git a/packages/graph-explorer/src/components/Workspace/components/WorkspacesContent.tsx b/packages/graph-explorer/src/components/Workspace/components/WorkspacesContent.tsx deleted file mode 100644 index 84b5a42f4..000000000 --- a/packages/graph-explorer/src/components/Workspace/components/WorkspacesContent.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { cn } from "@/utils"; -import type { ComponentPropsWithRef } from "react"; - -function WorkspacesContent({ - className, - orientation = "vertical", - ...props -}: ComponentPropsWithRef<"div"> & { orientation?: "vertical" | "horizontal" }) { - return ( -
- ); -} - -WorkspacesContent.displayName = "WorkspacesContent"; - -export default WorkspacesContent; diff --git a/packages/graph-explorer/src/components/Workspace/index.ts b/packages/graph-explorer/src/components/Workspace/index.ts deleted file mode 100644 index 9880d776e..000000000 --- a/packages/graph-explorer/src/components/Workspace/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -export { default } from "./Workspace"; -export type { WorkspaceProps } from "./Workspace"; -export { default as WorkspaceTopBar } from "./components/WorkspaceTopBar"; -export type { WorkspaceTopBarProps } from "./components/WorkspaceTopBar"; -export { default as WorkspaceTopBarAdditionalControls } from "./components/WorkspaceTopBarAdditionalControls"; -export type { WorkspaceTopBarAdditionalControlsProps } from "./components/WorkspaceTopBarAdditionalControls"; -export { default as WorkspaceTopBarContent } from "./components/WorkspaceTopBarContent"; -export type { WorkspaceTopBarContentProps } from "./components/WorkspaceTopBarContent"; -export { default as WorkspaceTopBarTitle } from "./components/WorkspaceTopBarTitle"; -export type { WorkspaceTopBarTitleProps } from "./components/WorkspaceTopBarTitle"; -export { default as WorkspacesContent } from "./components/WorkspacesContent"; -export { default as WorkspaceSideBar } from "./components/WorkspaceSideBar"; -export * from "./components/SidebarButton"; -export { default as WorkspaceSideBarContent } from "./components/WorkspaceSideBarContent"; diff --git a/packages/graph-explorer/src/components/index.ts b/packages/graph-explorer/src/components/index.ts index e2d9a9a9d..3d0e60b21 100644 --- a/packages/graph-explorer/src/components/index.ts +++ b/packages/graph-explorer/src/components/index.ts @@ -47,6 +47,7 @@ export * from "./KeyboardKey"; export * from "./Label"; export * from "./ListRow"; +export * from "./NavBar"; export * from "./Toaster"; @@ -86,5 +87,4 @@ export * from "./VertexIcon"; export * from "./VertexRow"; -export { default as Workspace } from "./Workspace"; export * from "./Workspace"; diff --git a/packages/graph-explorer/src/index.css b/packages/graph-explorer/src/index.css index 36f40f8b6..0207706b3 100644 --- a/packages/graph-explorer/src/index.css +++ b/packages/graph-explorer/src/index.css @@ -164,7 +164,7 @@ } #root { - @apply size-full; + @apply flex size-full; } .lucide { diff --git a/packages/graph-explorer/src/routes/Connections/Connections.tsx b/packages/graph-explorer/src/routes/Connections/Connections.tsx index 0c6ef457d..95b6bd30f 100644 --- a/packages/graph-explorer/src/routes/Connections/Connections.tsx +++ b/packages/graph-explorer/src/routes/Connections/Connections.tsx @@ -2,12 +2,19 @@ import { useState } from "react"; import { Link } from "react-router"; import { buttonStyles, + NavBar, + NavBarContent, + NavBarActions, + NavBarTitle, + NavBarVersion, Panel, PanelContent, PanelEmptyState, + PanelGroup, + Workspace, + WorkspaceContent, } from "@/components"; import { ExplorerIcon, GearIcon } from "@/components/icons"; -import Workspace from "@/components/Workspace/Workspace"; import { configurationAtom, useConfiguration } from "@/core"; import { useIsSyncing } from "@/hooks/useSchemaSync"; import AvailableConnections from "@/modules/AvailableConnections"; @@ -25,15 +32,15 @@ export default function Connections() { return ( - - - - {__GRAPH_EXP_VERSION__} - - + + + + + + {__GRAPH_EXP_VERSION__}
-
-
- -
+ + + +
)} -
- +
+
); } diff --git a/packages/graph-explorer/src/routes/DataExplorer/DataExplorer.tsx b/packages/graph-explorer/src/routes/DataExplorer/DataExplorer.tsx index 441cc33f6..2e33b7a30 100644 --- a/packages/graph-explorer/src/routes/DataExplorer/DataExplorer.tsx +++ b/packages/graph-explorer/src/routes/DataExplorer/DataExplorer.tsx @@ -20,13 +20,21 @@ import { EmptyStateContent, EmptyStateDescription, EmptyStateTitle, + NavBar, + NavBarContent, + NavBarActions, + NavBarTitle, + NavBarVersion, Panel, PanelError, + PanelGroup, PanelHeader, PanelTitle, SelectField, SendIcon, Spinner, + Workspace, + WorkspaceContent, } from "@/components"; import { ExplorerIcon } from "@/components/icons"; import { @@ -37,7 +45,6 @@ import { type TabularInstance, } from "@/components/Tabular"; import Tabular from "@/components/Tabular/Tabular"; -import Workspace from "@/components/Workspace/Workspace"; import { type KeywordSearchRequest, searchQuery } from "@/connector"; import { useVertexStyling } from "@/core/StateProvider/userPreferences"; import { useAddVertexToGraph, useHasVertexBeenAddedToGraph } from "@/hooks"; @@ -108,15 +115,15 @@ function DataExplorerContent({ vertexType }: ConnectionsProps) { return ( - - - - {__GRAPH_EXP_VERSION__} - - + + + + + + {__GRAPH_EXP_VERSION__} Open {LABELS.APP_NAME} - - - - + + + + Back to all Data - - + + - - - - - - - {displayTypeConfig.displayLabel}{" "} - {query.isFetching ? : null} - - - - - {query.isError ? ( - - ) : null} - {query.data?.vertices.length === 0 && ( - - - No Results - - {`No nodes found for "${displayTypeConfig.displayLabel}"`} - - - - )} - - - - - - - + + + + + + + + {displayTypeConfig.displayLabel}{" "} + {query.isFetching ? : null} + + + + + {query.isError ? ( + + ) : null} + {query.data?.vertices.length === 0 && ( + + + No Results + + {`No nodes found for "${displayTypeConfig.displayLabel}"`} + + + + )} + + + + + + + + ); } diff --git a/packages/graph-explorer/src/routes/GraphExplorer/GraphExplorer.tsx b/packages/graph-explorer/src/routes/GraphExplorer/GraphExplorer.tsx index 5b623c7ee..80214c2ab 100644 --- a/packages/graph-explorer/src/routes/GraphExplorer/GraphExplorer.tsx +++ b/packages/graph-explorer/src/routes/GraphExplorer/GraphExplorer.tsx @@ -1,12 +1,10 @@ -import { Activity } from "react"; -import { cn, isVisible } from "@/utils"; +import { cn } from "@/utils"; import { Resizable } from "re-resizable"; import { Link } from "react-router"; import { Button, buttonStyles, Divider, - EdgeIcon, EmptyState, EmptyStateActions, EmptyStateContent, @@ -14,39 +12,29 @@ import { EmptyStateIcon, EmptyStateTitle, IconButton, - NamespaceIcon, + NavBar, + NavBarContent, + NavBarActions, + NavBarTitle, + NavBarVersion, + PanelGroup, + Workspace, + WorkspaceContent, } from "@/components"; -import { - DatabaseIcon, - DetailsIcon, - EmptyWidgetIcon, - ExpandGraphIcon, - FilterIcon, - GraphIcon, - SearchIcon, -} from "@/components/icons"; +import { DatabaseIcon, EmptyWidgetIcon, GraphIcon } from "@/components/icons"; import GridIcon from "@/components/icons/GridIcon"; -import Workspace, { SidebarButton } from "@/components/Workspace"; import { DEFAULT_TABLE_VIEW_HEIGHT, useConfiguration, - useSidebar, useTableViewSize, useViewToggles, } from "@/core"; -import { totalFilteredCount } from "@/core/StateProvider/filterCount"; -import useTranslations from "@/hooks/useTranslations"; -import { EdgesStyling, EdgeStyleDialog } from "@/modules/EdgesStyling"; -import EntitiesFilter from "@/modules/EntitiesFilter"; +import { EdgeStyleDialog } from "@/modules/EdgesStyling"; import EntitiesTabular from "@/modules/EntitiesTabular/EntitiesTabular"; -import EntityDetails from "@/modules/EntityDetails"; -import Namespaces from "@/modules/Namespaces/Namespaces"; -import NodeExpand from "@/modules/NodeExpand"; -import { NodesStyling, NodeStyleDialog } from "@/modules/NodesStyling"; +import { NodeStyleDialog } from "@/modules/NodesStyling"; import { LABELS } from "@/utils/constants"; -import { SearchSidebarPanel } from "@/modules/SearchSidebar"; import GraphViewer from "@/modules/GraphViewer"; -import { useAtomValue } from "jotai"; +import { Sidebar } from "./Sidebar"; const RESIZE_ENABLE_TOP = { top: true, @@ -61,9 +49,6 @@ const RESIZE_ENABLE_TOP = { const GraphExplorer = () => { const config = useConfiguration(); - const t = useTranslations(); - - const filteredEntitiesCount = useAtomValue(totalFilteredCount); const { isGraphVisible, @@ -72,35 +57,40 @@ const GraphExplorer = () => { toggleTableVisibility, } = useViewToggles(); - const { activeSidebarItem, toggleSidebar, shouldShowNamespaces } = - useSidebar(); - const [tableViewHeight, setTableViewHeight] = useTableViewSize(); return ( - - - - {__GRAPH_EXP_VERSION__} - - - } - onClick={toggleGraphVisibility} + + + - } - onClick={toggleTableVisibility} - /> - + + + + {__GRAPH_EXP_VERSION__} + +
+ } + onClick={toggleGraphVisibility} + /> + } + onClick={toggleTableVisibility} + /> +
+ { Open Connections -
-
+ + - - - - - - - - All Views Hidden - - To view your graph data show the graph view or table view - - - - - - - - - - - - - - setTableViewHeight(delta.height) - } + + +
- - - - - - + + + + + + All Views Hidden + + To view your graph data show the graph view or table view + + + + + + + +
+
+ +
+
+ + setTableViewHeight(delta.height) + } + > + + +
+
- - } - onPressedChange={() => toggleSidebar("search")} - pressed={activeSidebarItem === "search"} - /> - } - onPressedChange={() => toggleSidebar("details")} - pressed={activeSidebarItem === "details"} - /> - } - onPressedChange={() => toggleSidebar("filters")} - badge={filteredEntitiesCount > 0} - pressed={activeSidebarItem === "filters"} - /> - } - onPressedChange={() => toggleSidebar("expand")} - pressed={activeSidebarItem === "expand"} - /> - } - onPressedChange={() => toggleSidebar("nodes-styling")} - pressed={activeSidebarItem === "nodes-styling"} - /> - } - onPressedChange={() => toggleSidebar("edges-styling")} - pressed={activeSidebarItem === "edges-styling"} - /> - {shouldShowNamespaces && ( - } - onPressedChange={() => toggleSidebar("namespaces")} - pressed={activeSidebarItem === "namespaces"} - /> - )} + - - - - - - - - - - - - - - - - - - - - - - - - + + +
); }; diff --git a/packages/graph-explorer/src/routes/GraphExplorer/Sidebar.tsx b/packages/graph-explorer/src/routes/GraphExplorer/Sidebar.tsx new file mode 100644 index 000000000..f92816335 --- /dev/null +++ b/packages/graph-explorer/src/routes/GraphExplorer/Sidebar.tsx @@ -0,0 +1,256 @@ +import { cn } from "@/utils"; +import { + useState, + type ComponentPropsWithRef, + type PropsWithChildren, +} from "react"; +import { Resizable } from "re-resizable"; +import { Tabs as TabsPrimitive } from "radix-ui"; +import { + CLOSED_SIDEBAR_WIDTH, + DEFAULT_SIDEBAR_WIDTH, + useSidebar, + useSidebarSize, + type SidebarItems, +} from "@/core"; +import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/Tooltip"; +import { + DetailsIcon, + EdgeIcon, + ExpandGraphIcon, + FilterIcon, + GraphIcon, + NamespaceIcon, + SearchIcon, +} from "@/components"; +import { SearchSidebarPanel } from "@/modules/SearchSidebar"; +import EntityDetails from "@/modules/EntityDetails"; +import NodeExpand from "@/modules/NodeExpand"; +import EntitiesFilter from "@/modules/EntitiesFilter"; +import { NodesStyling } from "@/modules/NodesStyling"; +import { EdgesStyling } from "@/modules/EdgesStyling"; +import Namespaces from "@/modules/Namespaces/Namespaces"; +import { useAtomValue } from "jotai"; +import { totalFilteredCount } from "@/core/StateProvider/filterCount"; +import { useTranslations } from "@/hooks"; + +export function Sidebar({ + className, + ...props +}: ComponentPropsWithRef) { + const t = useTranslations(); + const filteredEntitiesCount = useAtomValue(totalFilteredCount); + const { shouldShowNamespaces, activeSidebarItem } = useSidebar(); + + return ( + + + + + + + + + + + + + + 0} + > + + + + + + + + + {shouldShowNamespaces && ( + + + + )} + + + + + + + + + + + + + + + + + + + + + + + + + ); +} + +function ResizableSidebar({ children }: PropsWithChildren) { + const { isSidebarOpen } = useSidebar(); + const [sidebarWidth, setSidebarWidth] = useSidebarSize(); + + // The transition animation used for opening and closing sidebar animation + // does not play well with the resizing behavior of the Resizable component. + // If the animation is not disabled, the resize will feel jerky. + const [enableAnimation, setEnableAnimation] = useState(true); + + return ( + setEnableAnimation(false)} + onResizeStop={(_e, _direction, _ref, delta) => { + setEnableAnimation(true); + setSidebarWidth(delta.width); + }} + className={cn( + enableAnimation && + "transition-width min-h-0 transform duration-200 ease-in-out", + )} + > + {children} + + ); +} + +function SidebarTabs({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function SidebarTabsList({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function SidebarTabsTrigger({ + className, + title, + badge, + children, + value, + ...props +}: React.ComponentProps & { + title: string; + badge?: boolean; +}) { + const { toggleSidebar } = useSidebar(); + + const handleClick = () => { + toggleSidebar(value as SidebarItems); + }; + + return ( + + + + + + {children} + {title} + + + + {title} + + + ); +} + +function SidebarTabsContent({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function BadgeIndicator({ + children, + value, + ...props +}: React.ComponentPropsWithoutRef<"div"> & { value?: boolean }) { + return ( +
+ {children} + {value ? ( + + ) : null} +
+ ); +} diff --git a/packages/graph-explorer/src/routes/Settings/SettingsRoot.tsx b/packages/graph-explorer/src/routes/Settings/SettingsRoot.tsx index 07f028666..bf987cdf5 100644 --- a/packages/graph-explorer/src/routes/Settings/SettingsRoot.tsx +++ b/packages/graph-explorer/src/routes/Settings/SettingsRoot.tsx @@ -1,10 +1,17 @@ -import Workspace from "@/components/Workspace/Workspace"; import { buttonStyles, DatabaseIcon, ForwardIcon, + NavBar, + NavBarContent, + NavBarActions, + NavBarTitle, + NavBarVersion, Panel, PanelContent, + PanelGroup, + Workspace, + WorkspaceContent, } from "@/components"; import { Link, NavLink, Outlet, type To } from "react-router"; import { type PropsWithChildren, Suspense } from "react"; @@ -15,12 +22,13 @@ import { LABELS } from "@/utils/constants"; export default function SettingsRoot() { return ( - - - - {__GRAPH_EXP_VERSION__} - - + + + + + + + {__GRAPH_EXP_VERSION__} Open Connections - - - - - - - - - - - }> - - - - - + + + + + + + + + + + + }> + + + + + + ); }