Remove visualizer context menu

Summary: This isnt very discoverable and we can put functionality in the toolbar

Reviewed By: mweststrate

Differential Revision: D47670683

fbshipit-source-id: 8426ba05135f9a56f0a0952e94ac80480cd30a48
This commit is contained in:
Luke De Feo
2023-07-26 03:22:38 -07:00
committed by Facebook GitHub Bot
parent bf74877ff5
commit 8adf153380

View File

@@ -7,21 +7,15 @@
* @format * @format
*/ */
import React, {ReactNode, useEffect, useMemo, useRef, useState} from 'react'; import React, {useEffect, useMemo, useRef} from 'react';
import {Bounds, Coordinate, Id, ClientNode} from '../../ClientTypes'; import {Bounds, Coordinate, Id, ClientNode} from '../../ClientTypes';
import {NestedNode, OnSelectNode} from '../../DesktopTypes'; import {NestedNode, OnSelectNode} from '../../DesktopTypes';
import {produce, styled, theme, usePlugin, useValue} from 'flipper-plugin'; import {produce, styled, theme, usePlugin, useValue} from 'flipper-plugin';
import {plugin} from '../../index'; import {plugin} from '../../index';
import {head, isEqual, throttle} from 'lodash'; import {head, isEqual, throttle} from 'lodash';
import {Dropdown, Menu, Tooltip} from 'antd';
import {UIDebuggerMenuItem} from '../util/UIDebuggerMenuItem';
import {useDelay} from '../../hooks/useDelay'; import {useDelay} from '../../hooks/useDelay';
import { import {Tooltip} from 'antd';
AimOutlined,
FullscreenExitOutlined,
FullscreenOutlined,
} from '@ant-design/icons';
export const Visualization2D: React.FC< export const Visualization2D: React.FC<
{ {
@@ -118,85 +112,83 @@ export const Visualization2D: React.FC<
const pxScaleFactor = calcPxScaleFactor(snapshotNode.bounds, width); const pxScaleFactor = calcPxScaleFactor(snapshotNode.bounds, width);
return ( return (
<ContextMenu nodes={nodes}> <div
<div onMouseLeave={(e) => {
onMouseLeave={(e) => { e.stopPropagation();
e.stopPropagation(); //the context menu triggers this callback but we dont want to remove hover effect
//the context menu triggers this callback but we dont want to remove hover effect if (!instance.uiState.isContextMenuOpen.get()) {
if (!instance.uiState.isContextMenuOpen.get()) { instance.uiActions.onHoverNode();
instance.uiActions.onHoverNode(); }
}
visualizerActive.current = false; visualizerActive.current = false;
}} }}
onMouseEnter={() => { onMouseEnter={() => {
visualizerActive.current = true; visualizerActive.current = true;
}} }}
//this div is to ensure that the size of the visualiser doesnt change when focusings on a subtree //this div is to ensure that the size of the visualiser doesnt change when focusings on a subtree
style={ style={
{ {
backgroundColor: theme.backgroundWash, backgroundColor: theme.backgroundWash,
borderRadius: theme.borderRadius, borderRadius: theme.borderRadius,
overflowY: 'auto', overflowY: 'auto',
overflowX: 'hidden', overflowX: 'hidden',
position: 'relative', //this is for the absolutely positioned overlays position: 'relative', //this is for the absolutely positioned overlays
[pxScaleFactorCssVar]: pxScaleFactor, [pxScaleFactorCssVar]: pxScaleFactor,
width: toPx(focusState.actualRoot.bounds.width), width: toPx(focusState.actualRoot.bounds.width),
height: toPx(focusState.actualRoot.bounds.height), height: toPx(focusState.actualRoot.bounds.height),
} as React.CSSProperties } as React.CSSProperties
}> }>
{hoveredNodeId && ( {hoveredNodeId && (
<HoveredOverlay <HoveredOverlay
onSelectNode={instance.uiActions.onSelectNode} onSelectNode={instance.uiActions.onSelectNode}
key={hoveredNodeId} key={hoveredNodeId}
nodeId={hoveredNodeId} nodeId={hoveredNodeId}
nodes={nodes} nodes={nodes}
/>
)}
{selectedNodeId && (
<OverlayBorder
type="selected"
nodeId={selectedNodeId.id}
nodes={nodes}
/>
)}
<div
ref={rootNodeRef as any}
style={{
/**
* This relative position is so the rootNode visualization 2DNode and outer border has a non static element to
* position itself relative to.
*
* Subsequent Visualization2DNode are positioned relative to their parent as each one is position absolute
* which despite the name acts are a reference point for absolute positioning...
*
* When focused the global offset of the focussed node is used to offset and size this 'root' node
*/
position: 'relative',
marginLeft: toPx(focusState.focusedRootGlobalOffset.x),
marginTop: toPx(focusState.focusedRootGlobalOffset.y),
width: toPx(focusState.focusedRoot.bounds.width),
height: toPx(focusState.focusedRoot.bounds.height),
overflow: 'hidden',
}}>
{snapshotNode && (
<img
src={'data:image/png;base64,' + snapshot.data}
style={{
marginLeft: toPx(-focusState.focusedRootGlobalOffset.x),
marginTop: toPx(-focusState.focusedRootGlobalOffset.y),
width: toPx(snapshotNode.bounds.width),
height: toPx(snapshotNode.bounds.height),
}}
/> />
)} )}
{selectedNodeId && ( <MemoedVisualizationNode2D
<OverlayBorder node={focusState.focusedRoot}
type="selected" onSelectNode={onSelectNode}
nodeId={selectedNodeId.id} />
nodes={nodes}
/>
)}
<div
ref={rootNodeRef as any}
style={{
/**
* This relative position is so the rootNode visualization 2DNode and outer border has a non static element to
* position itself relative to.
*
* Subsequent Visualization2DNode are positioned relative to their parent as each one is position absolute
* which despite the name acts are a reference point for absolute positioning...
*
* When focused the global offset of the focussed node is used to offset and size this 'root' node
*/
position: 'relative',
marginLeft: toPx(focusState.focusedRootGlobalOffset.x),
marginTop: toPx(focusState.focusedRootGlobalOffset.y),
width: toPx(focusState.focusedRoot.bounds.width),
height: toPx(focusState.focusedRoot.bounds.height),
overflow: 'hidden',
}}>
{snapshotNode && (
<img
src={'data:image/png;base64,' + snapshot.data}
style={{
marginLeft: toPx(-focusState.focusedRootGlobalOffset.x),
marginTop: toPx(-focusState.focusedRootGlobalOffset.y),
width: toPx(snapshotNode.bounds.width),
height: toPx(snapshotNode.bounds.height),
}}
/>
)}
<MemoedVisualizationNode2D
node={focusState.focusedRoot}
onSelectNode={onSelectNode}
/>
</div>
</div> </div>
</ContextMenu> </div>
); );
}; };
@@ -346,109 +338,6 @@ function getTotalOffset(id: Id, nodes: Map<Id, ClientNode>): Coordinate {
return offset; return offset;
} }
function notEmpty<TValue>(value: TValue | null | undefined): value is TValue {
return value != null;
}
const iconStyle = {fontSize: 14};
const ContextMenu: React.FC<{nodes: Map<Id, ClientNode>}> = ({children}) => {
const instance = usePlugin(plugin);
const focusedNodeId = useValue(instance.uiState.focusedNode);
const hoveredNodeIds = useValue(instance.uiState.hoveredNodes);
const nodes = useValue(instance.nodes);
const hoveredNodes = hoveredNodeIds
.map((id) => nodes.get(id))
.filter(notEmpty)
.reverse();
const focusItems = hoveredNodes.map((node: ClientNode) => (
<UIDebuggerMenuItem
key={node.id}
onMouseEnter={() => {
instance.uiActions.onHoverNode(node.id);
}}
text={node.name}
onClick={() => {
instance.uiActions.onFocusNode(node.id);
}}
/>
));
const selectItems = hoveredNodes.map((node: ClientNode) => (
<UIDebuggerMenuItem
key={node.id}
text={node.name}
onMouseEnter={() => {
instance.uiActions.onHoverNode(node.id);
}}
onClick={() => {
instance.uiActions.onSelectNode(node.id, 'visualiser');
}}
/>
));
//since the context menu changes the hover state to indicate where you are this
//causes a rerender and therefore changes the context menu items. to work around
//we grab the hovered items at the time the context menu opens and this is unaffected
//by any further changes to hover state
const [staticItems, setStaticItems] = useState<{
focusItems: ReactNode[];
selectItems: ReactNode[];
}>({
selectItems: [],
focusItems: [],
});
return (
<Dropdown
onVisibleChange={(open) => {
instance.uiActions.onContextMenuOpen(open);
if (open) {
setStaticItems({focusItems: focusItems, selectItems: selectItems});
}
}}
trigger={['contextMenu']}
overlay={() => {
return (
<Menu>
{staticItems.focusItems.length > 0 && (
<Menu.SubMenu
title="Focus"
icon={<FullscreenExitOutlined style={iconStyle} />}>
{staticItems.focusItems}
</Menu.SubMenu>
)}
{focusedNodeId != null && (
<UIDebuggerMenuItem
icon={<FullscreenOutlined />}
key="remove-focus"
text="Remove focus"
onClick={() => {
instance.uiActions.onFocusNode(undefined);
}}
/>
)}
{focusedNodeId != null && <Menu.Divider />}
{staticItems.selectItems.length > 0 && (
<Menu.SubMenu
title="Select"
icon={<AimOutlined style={iconStyle} />}>
{staticItems.selectItems}
</Menu.SubMenu>
)}
</Menu>
);
}}>
{children}
</Dropdown>
);
};
/** /**
* this is the border that shows the green or blue line, it is implemented as a sibling to the * this is the border that shows the green or blue line, it is implemented as a sibling to the
* node itself so that it has the same size but the border doesnt affect the sizing of its children * node itself so that it has the same size but the border doesnt affect the sizing of its children