yangdx
commited on
Commit
·
a44a6ec
1
Parent(s):
920a7ad
Added tab visibility context and provider for dynamic tab management
Browse files- Introduced TabVisibilityProvider component
- Created TabContent for conditional rendering
- Added context and hooks for tab visibility
- Updated DocumentManager dependencies
- Integrated provider in App component
- lightrag_webui/src/App.tsx +30 -27
- lightrag_webui/src/components/ui/TabContent.tsx +39 -0
- lightrag_webui/src/contexts/TabVisibilityProvider.tsx +38 -0
- lightrag_webui/src/contexts/context.ts +12 -0
- lightrag_webui/src/contexts/types.ts +5 -0
- lightrag_webui/src/contexts/useTabVisibility.ts +17 -0
- lightrag_webui/src/features/DocumentManager.tsx +4 -4
lightrag_webui/src/App.tsx
CHANGED
@@ -1,5 +1,6 @@
|
|
1 |
import { useState, useCallback } from 'react'
|
2 |
import ThemeProvider from '@/components/ThemeProvider'
|
|
|
3 |
import MessageAlert from '@/components/MessageAlert'
|
4 |
import ApiKeyAlert from '@/components/ApiKeyAlert'
|
5 |
import StatusIndicator from '@/components/graph/StatusIndicator'
|
@@ -54,33 +55,35 @@ function App() {
|
|
54 |
|
55 |
return (
|
56 |
<ThemeProvider>
|
57 |
-
<
|
58 |
-
<
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
<
|
66 |
-
<
|
67 |
-
|
68 |
-
|
69 |
-
<
|
70 |
-
|
71 |
-
|
72 |
-
<
|
73 |
-
|
74 |
-
|
75 |
-
<
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
|
|
|
|
84 |
</ThemeProvider>
|
85 |
)
|
86 |
}
|
|
|
1 |
import { useState, useCallback } from 'react'
|
2 |
import ThemeProvider from '@/components/ThemeProvider'
|
3 |
+
import TabVisibilityProvider from '@/contexts/TabVisibilityProvider'
|
4 |
import MessageAlert from '@/components/MessageAlert'
|
5 |
import ApiKeyAlert from '@/components/ApiKeyAlert'
|
6 |
import StatusIndicator from '@/components/graph/StatusIndicator'
|
|
|
55 |
|
56 |
return (
|
57 |
<ThemeProvider>
|
58 |
+
<TabVisibilityProvider>
|
59 |
+
<main className="flex h-screen w-screen overflow-x-hidden">
|
60 |
+
<Tabs
|
61 |
+
defaultValue={currentTab}
|
62 |
+
className="!m-0 flex grow flex-col !p-0"
|
63 |
+
onValueChange={handleTabChange}
|
64 |
+
>
|
65 |
+
<SiteHeader />
|
66 |
+
<div className="relative grow">
|
67 |
+
<TabsContent value="documents" className="absolute top-0 right-0 bottom-0 left-0">
|
68 |
+
<DocumentManager />
|
69 |
+
</TabsContent>
|
70 |
+
<TabsContent value="knowledge-graph" className="absolute top-0 right-0 bottom-0 left-0">
|
71 |
+
<GraphViewer />
|
72 |
+
</TabsContent>
|
73 |
+
<TabsContent value="retrieval" className="absolute top-0 right-0 bottom-0 left-0">
|
74 |
+
<RetrievalTesting />
|
75 |
+
</TabsContent>
|
76 |
+
<TabsContent value="api" className="absolute top-0 right-0 bottom-0 left-0">
|
77 |
+
<ApiSite />
|
78 |
+
</TabsContent>
|
79 |
+
</div>
|
80 |
+
</Tabs>
|
81 |
+
{enableHealthCheck && <StatusIndicator />}
|
82 |
+
{message !== null && !apiKeyInvalid && <MessageAlert />}
|
83 |
+
{apiKeyInvalid && <ApiKeyAlert />}
|
84 |
+
<Toaster />
|
85 |
+
</main>
|
86 |
+
</TabVisibilityProvider>
|
87 |
</ThemeProvider>
|
88 |
)
|
89 |
}
|
lightrag_webui/src/components/ui/TabContent.tsx
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React, { useEffect } from 'react';
|
2 |
+
import { useTabVisibility } from '@/contexts/useTabVisibility';
|
3 |
+
|
4 |
+
interface TabContentProps {
|
5 |
+
tabId: string;
|
6 |
+
children: React.ReactNode;
|
7 |
+
className?: string;
|
8 |
+
}
|
9 |
+
|
10 |
+
/**
|
11 |
+
* TabContent component that manages visibility based on tab selection
|
12 |
+
* Works with the TabVisibilityContext to show/hide content based on active tab
|
13 |
+
*/
|
14 |
+
const TabContent: React.FC<TabContentProps> = ({ tabId, children, className = '' }) => {
|
15 |
+
const { isTabVisible, setTabVisibility } = useTabVisibility();
|
16 |
+
const isVisible = isTabVisible(tabId);
|
17 |
+
|
18 |
+
// Register this tab with the context when mounted
|
19 |
+
useEffect(() => {
|
20 |
+
setTabVisibility(tabId, true);
|
21 |
+
|
22 |
+
// Cleanup when unmounted
|
23 |
+
return () => {
|
24 |
+
setTabVisibility(tabId, false);
|
25 |
+
};
|
26 |
+
}, [tabId, setTabVisibility]);
|
27 |
+
|
28 |
+
if (!isVisible) {
|
29 |
+
return null;
|
30 |
+
}
|
31 |
+
|
32 |
+
return (
|
33 |
+
<div className={className}>
|
34 |
+
{children}
|
35 |
+
</div>
|
36 |
+
);
|
37 |
+
};
|
38 |
+
|
39 |
+
export default TabContent;
|
lightrag_webui/src/contexts/TabVisibilityProvider.tsx
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import React, { useState, useMemo } from 'react';
|
2 |
+
import { TabVisibilityContext } from './context';
|
3 |
+
import { TabVisibilityContextType } from './types';
|
4 |
+
|
5 |
+
interface TabVisibilityProviderProps {
|
6 |
+
children: React.ReactNode;
|
7 |
+
}
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Provider component for the TabVisibility context
|
11 |
+
* Manages the visibility state of tabs throughout the application
|
12 |
+
*/
|
13 |
+
export const TabVisibilityProvider: React.FC<TabVisibilityProviderProps> = ({ children }) => {
|
14 |
+
const [visibleTabs, setVisibleTabs] = useState<Record<string, boolean>>({});
|
15 |
+
|
16 |
+
// Create the context value with memoization to prevent unnecessary re-renders
|
17 |
+
const contextValue = useMemo<TabVisibilityContextType>(
|
18 |
+
() => ({
|
19 |
+
visibleTabs,
|
20 |
+
setTabVisibility: (tabId: string, isVisible: boolean) => {
|
21 |
+
setVisibleTabs((prev) => ({
|
22 |
+
...prev,
|
23 |
+
[tabId]: isVisible,
|
24 |
+
}));
|
25 |
+
},
|
26 |
+
isTabVisible: (tabId: string) => !!visibleTabs[tabId],
|
27 |
+
}),
|
28 |
+
[visibleTabs]
|
29 |
+
);
|
30 |
+
|
31 |
+
return (
|
32 |
+
<TabVisibilityContext.Provider value={contextValue}>
|
33 |
+
{children}
|
34 |
+
</TabVisibilityContext.Provider>
|
35 |
+
);
|
36 |
+
};
|
37 |
+
|
38 |
+
export default TabVisibilityProvider;
|
lightrag_webui/src/contexts/context.ts
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { createContext } from 'react';
|
2 |
+
import { TabVisibilityContextType } from './types';
|
3 |
+
|
4 |
+
// Default context value
|
5 |
+
const defaultContext: TabVisibilityContextType = {
|
6 |
+
visibleTabs: {},
|
7 |
+
setTabVisibility: () => {},
|
8 |
+
isTabVisible: () => false,
|
9 |
+
};
|
10 |
+
|
11 |
+
// Create the context
|
12 |
+
export const TabVisibilityContext = createContext<TabVisibilityContextType>(defaultContext);
|
lightrag_webui/src/contexts/types.ts
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export interface TabVisibilityContextType {
|
2 |
+
visibleTabs: Record<string, boolean>;
|
3 |
+
setTabVisibility: (tabId: string, isVisible: boolean) => void;
|
4 |
+
isTabVisible: (tabId: string) => boolean;
|
5 |
+
}
|
lightrag_webui/src/contexts/useTabVisibility.ts
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { useContext } from 'react';
|
2 |
+
import { TabVisibilityContext } from './context';
|
3 |
+
import { TabVisibilityContextType } from './types';
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Custom hook to access the tab visibility context
|
7 |
+
* @returns The tab visibility context
|
8 |
+
*/
|
9 |
+
export const useTabVisibility = (): TabVisibilityContextType => {
|
10 |
+
const context = useContext(TabVisibilityContext);
|
11 |
+
|
12 |
+
if (!context) {
|
13 |
+
throw new Error('useTabVisibility must be used within a TabVisibilityProvider');
|
14 |
+
}
|
15 |
+
|
16 |
+
return context;
|
17 |
+
};
|
lightrag_webui/src/features/DocumentManager.tsx
CHANGED
@@ -48,11 +48,11 @@ export default function DocumentManager() {
|
|
48 |
} catch (err) {
|
49 |
toast.error(t('documentPanel.documentManager.errors.loadFailed', { error: errorMessage(err) }))
|
50 |
}
|
51 |
-
}, [setDocs])
|
52 |
|
53 |
useEffect(() => {
|
54 |
fetchDocuments()
|
55 |
-
}, [])
|
56 |
|
57 |
const scanDocuments = useCallback(async () => {
|
58 |
try {
|
@@ -61,7 +61,7 @@ export default function DocumentManager() {
|
|
61 |
} catch (err) {
|
62 |
toast.error(t('documentPanel.documentManager.errors.scanFailed', { error: errorMessage(err) }))
|
63 |
}
|
64 |
-
}, [])
|
65 |
|
66 |
useEffect(() => {
|
67 |
const interval = setInterval(async () => {
|
@@ -75,7 +75,7 @@ export default function DocumentManager() {
|
|
75 |
}
|
76 |
}, 5000)
|
77 |
return () => clearInterval(interval)
|
78 |
-
}, [health, fetchDocuments])
|
79 |
|
80 |
return (
|
81 |
<Card className="!size-full !rounded-none !border-none">
|
|
|
48 |
} catch (err) {
|
49 |
toast.error(t('documentPanel.documentManager.errors.loadFailed', { error: errorMessage(err) }))
|
50 |
}
|
51 |
+
}, [setDocs, t])
|
52 |
|
53 |
useEffect(() => {
|
54 |
fetchDocuments()
|
55 |
+
}, [fetchDocuments, t])
|
56 |
|
57 |
const scanDocuments = useCallback(async () => {
|
58 |
try {
|
|
|
61 |
} catch (err) {
|
62 |
toast.error(t('documentPanel.documentManager.errors.scanFailed', { error: errorMessage(err) }))
|
63 |
}
|
64 |
+
}, [t])
|
65 |
|
66 |
useEffect(() => {
|
67 |
const interval = setInterval(async () => {
|
|
|
75 |
}
|
76 |
}, 5000)
|
77 |
return () => clearInterval(interval)
|
78 |
+
}, [health, fetchDocuments, t])
|
79 |
|
80 |
return (
|
81 |
<Card className="!size-full !rounded-none !border-none">
|