diff --git a/graph/client/src/app/shell.tsx b/graph/client/src/app/shell.tsx
index ecfa074d0260b..a670617bf57a8 100644
--- a/graph/client/src/app/shell.tsx
+++ b/graph/client/src/app/shell.tsx
@@ -6,9 +6,14 @@ import {
import classNames from 'classnames';
import { DebuggerPanel } from './ui-components/debugger-panel';
import { getGraphService } from './machines/graph.service';
-import { Outlet, useNavigate, useParams } from 'react-router-dom';
+import {
+ Outlet,
+ useNavigate,
+ useNavigation,
+ useParams,
+} from 'react-router-dom';
import { getSystemTheme, Theme, ThemePanel } from '@nx/graph/ui-theme';
-import { Dropdown } from '@nx/graph/ui-components';
+import { Dropdown, Spinner } from '@nx/graph/ui-components';
import { useCurrentPath } from './hooks/use-current-path';
import { ExperimentalFeature } from './ui-components/experimental-feature';
import { RankdirPanel } from './feature-projects/panels/rankdir-panel';
@@ -36,8 +41,9 @@ export function Shell(): JSX.Element {
}
const navigate = useNavigate();
+ const { state: navigationState } = useNavigation();
const currentPath = useCurrentPath();
- const { selectedWorkspaceId, selectedTaskId } = useParams();
+ const { selectedWorkspaceId } = useParams();
const currentRoute = currentPath.currentPath;
const topLevelRoute = currentRoute.startsWith('/tasks')
@@ -165,17 +171,23 @@ export function Shell(): JSX.Element {
>
) : null}
- {!nodesVisible ? (
+ {!nodesVisible || navigationState === 'loading' ? (
-
-
- Please select a{' '}
- {currentRoute.startsWith('/tasks') ? 'task' : 'project'} in the
- sidebar.
-
+ {navigationState === 'loading' ? (
+
+ ) : (
+ <>
+
+
+ Please select a{' '}
+ {currentRoute.startsWith('/tasks') ? 'task' : 'project'} in
+ the sidebar.
+
+ >
+ )}
) : null}
diff --git a/graph/ui-components/src/index.ts b/graph/ui-components/src/index.ts
index 09e0853c0eeb9..82bc51d1ec3fe 100644
--- a/graph/ui-components/src/index.ts
+++ b/graph/ui-components/src/index.ts
@@ -1,3 +1,4 @@
export * from './lib/debounced-text-input';
export * from './lib/tag';
export * from './lib/dropdown';
+export * from './lib/spinner';
diff --git a/graph/ui-components/src/lib/spinner.stories.tsx b/graph/ui-components/src/lib/spinner.stories.tsx
new file mode 100644
index 0000000000000..c7d7036e05a88
--- /dev/null
+++ b/graph/ui-components/src/lib/spinner.stories.tsx
@@ -0,0 +1,16 @@
+import type { Meta, StoryObj } from '@storybook/react';
+import { Spinner } from './spinner';
+
+const meta: Meta = {
+ component: Spinner,
+ title: 'Shared/Spinner',
+};
+
+export default meta;
+type Story = StoryObj;
+
+export const Primary: Story = {
+ args: {
+ className: '',
+ },
+};
diff --git a/graph/ui-components/src/lib/spinner.tsx b/graph/ui-components/src/lib/spinner.tsx
new file mode 100644
index 0000000000000..500a5190cc05b
--- /dev/null
+++ b/graph/ui-components/src/lib/spinner.tsx
@@ -0,0 +1,31 @@
+/**
+ * Spinner component from https://tailwindcss.com/docs/animation#spin
+ */
+
+import React from 'react';
+
+export type SpinnerProps = React.SVGProps;
+
+export function Spinner({ className, ...rest }: SpinnerProps) {
+ return (
+
+ );
+}