Added tab visibility context and provider for dynamic tab management
- Introduced TabVisibilityProvider component - Created TabContent for conditional rendering - Added context and hooks for tab visibility - Updated DocumentManager dependencies - Integrated provider in App component
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import { useState, useCallback } from 'react'
|
import { useState, useCallback } from 'react'
|
||||||
import ThemeProvider from '@/components/ThemeProvider'
|
import ThemeProvider from '@/components/ThemeProvider'
|
||||||
|
import TabVisibilityProvider from '@/contexts/TabVisibilityProvider'
|
||||||
import MessageAlert from '@/components/MessageAlert'
|
import MessageAlert from '@/components/MessageAlert'
|
||||||
import ApiKeyAlert from '@/components/ApiKeyAlert'
|
import ApiKeyAlert from '@/components/ApiKeyAlert'
|
||||||
import StatusIndicator from '@/components/graph/StatusIndicator'
|
import StatusIndicator from '@/components/graph/StatusIndicator'
|
||||||
@@ -54,33 +55,35 @@ function App() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeProvider>
|
<ThemeProvider>
|
||||||
<main className="flex h-screen w-screen overflow-x-hidden">
|
<TabVisibilityProvider>
|
||||||
<Tabs
|
<main className="flex h-screen w-screen overflow-x-hidden">
|
||||||
defaultValue={currentTab}
|
<Tabs
|
||||||
className="!m-0 flex grow flex-col !p-0"
|
defaultValue={currentTab}
|
||||||
onValueChange={handleTabChange}
|
className="!m-0 flex grow flex-col !p-0"
|
||||||
>
|
onValueChange={handleTabChange}
|
||||||
<SiteHeader />
|
>
|
||||||
<div className="relative grow">
|
<SiteHeader />
|
||||||
<TabsContent value="documents" className="absolute top-0 right-0 bottom-0 left-0">
|
<div className="relative grow">
|
||||||
<DocumentManager />
|
<TabsContent value="documents" className="absolute top-0 right-0 bottom-0 left-0">
|
||||||
</TabsContent>
|
<DocumentManager />
|
||||||
<TabsContent value="knowledge-graph" className="absolute top-0 right-0 bottom-0 left-0">
|
</TabsContent>
|
||||||
<GraphViewer />
|
<TabsContent value="knowledge-graph" className="absolute top-0 right-0 bottom-0 left-0">
|
||||||
</TabsContent>
|
<GraphViewer />
|
||||||
<TabsContent value="retrieval" className="absolute top-0 right-0 bottom-0 left-0">
|
</TabsContent>
|
||||||
<RetrievalTesting />
|
<TabsContent value="retrieval" className="absolute top-0 right-0 bottom-0 left-0">
|
||||||
</TabsContent>
|
<RetrievalTesting />
|
||||||
<TabsContent value="api" className="absolute top-0 right-0 bottom-0 left-0">
|
</TabsContent>
|
||||||
<ApiSite />
|
<TabsContent value="api" className="absolute top-0 right-0 bottom-0 left-0">
|
||||||
</TabsContent>
|
<ApiSite />
|
||||||
</div>
|
</TabsContent>
|
||||||
</Tabs>
|
</div>
|
||||||
{enableHealthCheck && <StatusIndicator />}
|
</Tabs>
|
||||||
{message !== null && !apiKeyInvalid && <MessageAlert />}
|
{enableHealthCheck && <StatusIndicator />}
|
||||||
{apiKeyInvalid && <ApiKeyAlert />}
|
{message !== null && !apiKeyInvalid && <MessageAlert />}
|
||||||
<Toaster />
|
{apiKeyInvalid && <ApiKeyAlert />}
|
||||||
</main>
|
<Toaster />
|
||||||
|
</main>
|
||||||
|
</TabVisibilityProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
39
lightrag_webui/src/components/ui/TabContent.tsx
Normal file
39
lightrag_webui/src/components/ui/TabContent.tsx
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import { useTabVisibility } from '@/contexts/useTabVisibility';
|
||||||
|
|
||||||
|
interface TabContentProps {
|
||||||
|
tabId: string;
|
||||||
|
children: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TabContent component that manages visibility based on tab selection
|
||||||
|
* Works with the TabVisibilityContext to show/hide content based on active tab
|
||||||
|
*/
|
||||||
|
const TabContent: React.FC<TabContentProps> = ({ tabId, children, className = '' }) => {
|
||||||
|
const { isTabVisible, setTabVisibility } = useTabVisibility();
|
||||||
|
const isVisible = isTabVisible(tabId);
|
||||||
|
|
||||||
|
// Register this tab with the context when mounted
|
||||||
|
useEffect(() => {
|
||||||
|
setTabVisibility(tabId, true);
|
||||||
|
|
||||||
|
// Cleanup when unmounted
|
||||||
|
return () => {
|
||||||
|
setTabVisibility(tabId, false);
|
||||||
|
};
|
||||||
|
}, [tabId, setTabVisibility]);
|
||||||
|
|
||||||
|
if (!isVisible) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={className}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TabContent;
|
38
lightrag_webui/src/contexts/TabVisibilityProvider.tsx
Normal file
38
lightrag_webui/src/contexts/TabVisibilityProvider.tsx
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import React, { useState, useMemo } from 'react';
|
||||||
|
import { TabVisibilityContext } from './context';
|
||||||
|
import { TabVisibilityContextType } from './types';
|
||||||
|
|
||||||
|
interface TabVisibilityProviderProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provider component for the TabVisibility context
|
||||||
|
* Manages the visibility state of tabs throughout the application
|
||||||
|
*/
|
||||||
|
export const TabVisibilityProvider: React.FC<TabVisibilityProviderProps> = ({ children }) => {
|
||||||
|
const [visibleTabs, setVisibleTabs] = useState<Record<string, boolean>>({});
|
||||||
|
|
||||||
|
// Create the context value with memoization to prevent unnecessary re-renders
|
||||||
|
const contextValue = useMemo<TabVisibilityContextType>(
|
||||||
|
() => ({
|
||||||
|
visibleTabs,
|
||||||
|
setTabVisibility: (tabId: string, isVisible: boolean) => {
|
||||||
|
setVisibleTabs((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[tabId]: isVisible,
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
isTabVisible: (tabId: string) => !!visibleTabs[tabId],
|
||||||
|
}),
|
||||||
|
[visibleTabs]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TabVisibilityContext.Provider value={contextValue}>
|
||||||
|
{children}
|
||||||
|
</TabVisibilityContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TabVisibilityProvider;
|
12
lightrag_webui/src/contexts/context.ts
Normal file
12
lightrag_webui/src/contexts/context.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { createContext } from 'react';
|
||||||
|
import { TabVisibilityContextType } from './types';
|
||||||
|
|
||||||
|
// Default context value
|
||||||
|
const defaultContext: TabVisibilityContextType = {
|
||||||
|
visibleTabs: {},
|
||||||
|
setTabVisibility: () => {},
|
||||||
|
isTabVisible: () => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create the context
|
||||||
|
export const TabVisibilityContext = createContext<TabVisibilityContextType>(defaultContext);
|
5
lightrag_webui/src/contexts/types.ts
Normal file
5
lightrag_webui/src/contexts/types.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export interface TabVisibilityContextType {
|
||||||
|
visibleTabs: Record<string, boolean>;
|
||||||
|
setTabVisibility: (tabId: string, isVisible: boolean) => void;
|
||||||
|
isTabVisible: (tabId: string) => boolean;
|
||||||
|
}
|
17
lightrag_webui/src/contexts/useTabVisibility.ts
Normal file
17
lightrag_webui/src/contexts/useTabVisibility.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { useContext } from 'react';
|
||||||
|
import { TabVisibilityContext } from './context';
|
||||||
|
import { TabVisibilityContextType } from './types';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom hook to access the tab visibility context
|
||||||
|
* @returns The tab visibility context
|
||||||
|
*/
|
||||||
|
export const useTabVisibility = (): TabVisibilityContextType => {
|
||||||
|
const context = useContext(TabVisibilityContext);
|
||||||
|
|
||||||
|
if (!context) {
|
||||||
|
throw new Error('useTabVisibility must be used within a TabVisibilityProvider');
|
||||||
|
}
|
||||||
|
|
||||||
|
return context;
|
||||||
|
};
|
@@ -48,11 +48,11 @@ export default function DocumentManager() {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
toast.error(t('documentPanel.documentManager.errors.loadFailed', { error: errorMessage(err) }))
|
toast.error(t('documentPanel.documentManager.errors.loadFailed', { error: errorMessage(err) }))
|
||||||
}
|
}
|
||||||
}, [setDocs])
|
}, [setDocs, t])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchDocuments()
|
fetchDocuments()
|
||||||
}, []) // eslint-disable-line react-hooks/exhaustive-deps
|
}, [fetchDocuments, t])
|
||||||
|
|
||||||
const scanDocuments = useCallback(async () => {
|
const scanDocuments = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
@@ -61,7 +61,7 @@ export default function DocumentManager() {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
toast.error(t('documentPanel.documentManager.errors.scanFailed', { error: errorMessage(err) }))
|
toast.error(t('documentPanel.documentManager.errors.scanFailed', { error: errorMessage(err) }))
|
||||||
}
|
}
|
||||||
}, [])
|
}, [t])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const interval = setInterval(async () => {
|
const interval = setInterval(async () => {
|
||||||
@@ -75,7 +75,7 @@ export default function DocumentManager() {
|
|||||||
}
|
}
|
||||||
}, 5000)
|
}, 5000)
|
||||||
return () => clearInterval(interval)
|
return () => clearInterval(interval)
|
||||||
}, [health, fetchDocuments])
|
}, [health, fetchDocuments, t])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="!size-full !rounded-none !border-none">
|
<Card className="!size-full !rounded-none !border-none">
|
||||||
|
Reference in New Issue
Block a user