diff --git a/lightrag_webui/src/App.tsx b/lightrag_webui/src/App.tsx index 1cf8c5e3..bb4a84cb 100644 --- a/lightrag_webui/src/App.tsx +++ b/lightrag_webui/src/App.tsx @@ -1,5 +1,6 @@ import { useState, useCallback } from 'react' import ThemeProvider from '@/components/ThemeProvider' +import TabVisibilityProvider from '@/contexts/TabVisibilityProvider' import MessageAlert from '@/components/MessageAlert' import ApiKeyAlert from '@/components/ApiKeyAlert' import StatusIndicator from '@/components/graph/StatusIndicator' @@ -54,33 +55,35 @@ function App() { return ( -
- - -
- - - - - - - - - - - - -
-
- {enableHealthCheck && } - {message !== null && !apiKeyInvalid && } - {apiKeyInvalid && } - -
+ +
+ + +
+ + + + + + + + + + + + +
+
+ {enableHealthCheck && } + {message !== null && !apiKeyInvalid && } + {apiKeyInvalid && } + +
+
) } diff --git a/lightrag_webui/src/components/ui/TabContent.tsx b/lightrag_webui/src/components/ui/TabContent.tsx new file mode 100644 index 00000000..f3c0b80f --- /dev/null +++ b/lightrag_webui/src/components/ui/TabContent.tsx @@ -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 = ({ 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 ( +
+ {children} +
+ ); +}; + +export default TabContent; diff --git a/lightrag_webui/src/contexts/TabVisibilityProvider.tsx b/lightrag_webui/src/contexts/TabVisibilityProvider.tsx new file mode 100644 index 00000000..73be2f64 --- /dev/null +++ b/lightrag_webui/src/contexts/TabVisibilityProvider.tsx @@ -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 = ({ children }) => { + const [visibleTabs, setVisibleTabs] = useState>({}); + + // Create the context value with memoization to prevent unnecessary re-renders + const contextValue = useMemo( + () => ({ + visibleTabs, + setTabVisibility: (tabId: string, isVisible: boolean) => { + setVisibleTabs((prev) => ({ + ...prev, + [tabId]: isVisible, + })); + }, + isTabVisible: (tabId: string) => !!visibleTabs[tabId], + }), + [visibleTabs] + ); + + return ( + + {children} + + ); +}; + +export default TabVisibilityProvider; diff --git a/lightrag_webui/src/contexts/context.ts b/lightrag_webui/src/contexts/context.ts new file mode 100644 index 00000000..e6b569a2 --- /dev/null +++ b/lightrag_webui/src/contexts/context.ts @@ -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(defaultContext); diff --git a/lightrag_webui/src/contexts/types.ts b/lightrag_webui/src/contexts/types.ts new file mode 100644 index 00000000..051c398c --- /dev/null +++ b/lightrag_webui/src/contexts/types.ts @@ -0,0 +1,5 @@ +export interface TabVisibilityContextType { + visibleTabs: Record; + setTabVisibility: (tabId: string, isVisible: boolean) => void; + isTabVisible: (tabId: string) => boolean; +} diff --git a/lightrag_webui/src/contexts/useTabVisibility.ts b/lightrag_webui/src/contexts/useTabVisibility.ts new file mode 100644 index 00000000..436ce50f --- /dev/null +++ b/lightrag_webui/src/contexts/useTabVisibility.ts @@ -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; +}; diff --git a/lightrag_webui/src/features/DocumentManager.tsx b/lightrag_webui/src/features/DocumentManager.tsx index b8841fe4..3d4a6717 100644 --- a/lightrag_webui/src/features/DocumentManager.tsx +++ b/lightrag_webui/src/features/DocumentManager.tsx @@ -48,11 +48,11 @@ export default function DocumentManager() { } catch (err) { toast.error(t('documentPanel.documentManager.errors.loadFailed', { error: errorMessage(err) })) } - }, [setDocs]) + }, [setDocs, t]) useEffect(() => { fetchDocuments() - }, []) // eslint-disable-line react-hooks/exhaustive-deps + }, [fetchDocuments, t]) const scanDocuments = useCallback(async () => { try { @@ -61,7 +61,7 @@ export default function DocumentManager() { } catch (err) { toast.error(t('documentPanel.documentManager.errors.scanFailed', { error: errorMessage(err) })) } - }, []) + }, [t]) useEffect(() => { const interval = setInterval(async () => { @@ -75,7 +75,7 @@ export default function DocumentManager() { } }, 5000) return () => clearInterval(interval) - }, [health, fetchDocuments]) + }, [health, fetchDocuments, t]) return (