/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @format */ import React, {ReactNode, useEffect, useRef, useState} from 'react'; import {plugin} from '../index'; import { DetailSidebar, Layout, usePlugin, useValue, _Sidebar as ResizablePanel, theme, } from 'flipper-plugin'; import {useHotkeys} from 'react-hotkeys-hook'; import {Id, Metadata, MetadataId, ClientNode} from '../ClientTypes'; import {PerfStats} from './PerfStats'; import {Visualization2D} from './visualizer/Visualization2D'; import {TreeControls} from './tree/TreeControls'; import {Button, Spin, Typography} from 'antd'; import {QueryClientProvider} from 'react-query'; import {Tree2} from './tree/Tree'; import {StreamInterceptorErrorView} from './StreamInterceptorErrorView'; import {queryClient} from '../utils/reactQuery'; import {FrameworkEventsTable} from './FrameworkEventsTable'; import {Centered} from './shared/Centered'; import {SidebarV2} from './sidebarV2/SidebarV2'; import {getNode} from '../utils/map'; export function Component() { const instance = usePlugin(plugin); const rootId = useValue(instance.rootId); const streamState = useValue(instance.uiState.streamState); const visualiserWidth = useValue(instance.uiState.visualiserWidth); const nodes: Map = useValue(instance.nodes); const metadata: Map = useValue(instance.metadata); const selectedNodeId = useValue(instance.uiState.selectedNode); const [showPerfStats, setShowPerfStats] = useState(false); useHotkeys('ctrl+i', () => setShowPerfStats((show) => !show)); const viewMode = useValue(instance.uiState.viewMode); const [bottomPanel, setBottomPanel] = useState< {title: string; component: ReactNode} | undefined >(); const openBottomPanelWithContent = (title: string, component: ReactNode) => { setBottomPanel({title, component}); }; const dismissBottomPanel = () => { setBottomPanel(undefined); }; const [bottomPanelHeight, setBottomPanelHeight] = useState(400); if (showPerfStats) return ( ); if (streamState.state === 'FatalError') { return ( Reset } /> ); } if (streamState.state === 'StreamInterceptorRetryableError') { return ( Retry } /> ); } if (rootId == null || streamState.state === 'RetryingAfterError') { return ( ); } if (viewMode.mode === 'frameworkEventsTable') { return ( ); } return ( { instance.uiActions.setVisualiserWidth(width); }} gutter> {bottomPanel && ( {bottomPanel.component} )} ); } type BottomPanelProps = { title: string; dismiss: () => void; children: React.ReactNode; height: number; setHeight: (height: number) => void; }; export function BottomPanel({ title, dismiss, children, height, setHeight, }: BottomPanelProps) { const bottomPanelRef = useRef(null); useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if ( bottomPanelRef.current && !bottomPanelRef.current.contains(event.target) ) { setTimeout(() => { //push to back of event queue so that you can still select item in the tree dismiss(); }, 0); } }; // Add event listener when the component is mounted. document.addEventListener('mousedown', handleClickOutside); // Remove event listener when component is unmounted. return () => { document.removeEventListener('mousedown', handleClickOutside); }; }, [bottomPanelRef, dismiss]); if (!children) { return <>; } return (
setHeight(height)} gutter> {title} {children}
); }