Add context menu to visualizer

Summary: Added context menu to visualizer similar to what we have on the tree

Reviewed By: lblasa

Differential Revision: D41494948

fbshipit-source-id: 0cfa4c98b7a68462a7103ed1ce9eaaff8c99aeee
This commit is contained in:
Luke De Feo
2022-11-24 09:23:16 -08:00
committed by Facebook GitHub Bot
parent f78899b69f
commit ca67bfd916

View File

@@ -13,6 +13,7 @@ import {Bounds, Coordinate, Id, NestedNode, Tag, UINode} from '../types';
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} from 'antd';
export const Visualization2D: React.FC< export const Visualization2D: React.FC<
{ {
@@ -37,7 +38,8 @@ export const Visualization2D: React.FC<
useEffect(() => { useEffect(() => {
const mouseListener = throttle((ev: MouseEvent) => { const mouseListener = throttle((ev: MouseEvent) => {
const domRect = rootNodeRef.current?.getBoundingClientRect(); const domRect = rootNodeRef.current?.getBoundingClientRect();
if (!focusState || !domRect) {
if (!focusState || !domRect || instance.isContextMenuOpen.get()) {
return; return;
} }
const rawMouse = {x: ev.clientX, y: ev.clientY}; const rawMouse = {x: ev.clientX, y: ev.clientY};
@@ -70,14 +72,16 @@ export const Visualization2D: React.FC<
return () => { return () => {
window.removeEventListener('mousemove', mouseListener); window.removeEventListener('mousemove', mouseListener);
}; };
}, [instance.hoveredNodes, focusState, nodes]); }, [instance.hoveredNodes, focusState, nodes, instance.isContextMenuOpen]);
if (!focusState) { if (!focusState) {
return null; return null;
} }
const snapshotNode = snapshot && nodes.get(snapshot.nodeId); const snapshotNode = snapshot && nodes.get(snapshot.nodeId);
return ( return (
<ContextMenu nodes={nodes}>
<div <div
//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={{
@@ -88,7 +92,10 @@ export const Visualization2D: React.FC<
ref={rootNodeRef as any} ref={rootNodeRef as any}
onMouseLeave={(e) => { onMouseLeave={(e) => {
e.stopPropagation(); e.stopPropagation();
//the context menu triggers this callback but we dont want to remove hover effect
if (!instance.isContextMenuOpen.get()) {
instance.hoveredNodes.set([]); instance.hoveredNodes.set([]);
}
}} }}
style={{ style={{
/** /**
@@ -101,7 +108,6 @@ export const Visualization2D: React.FC<
* When focused the global offset of the focussed node is used to offset and size this 'root' node * When focused the global offset of the focussed node is used to offset and size this 'root' node
*/ */
position: 'relative', position: 'relative',
marginLeft: toPx(focusState.focusedRootGlobalOffset.x), marginLeft: toPx(focusState.focusedRootGlobalOffset.x),
marginTop: toPx(focusState.focusedRootGlobalOffset.y), marginTop: toPx(focusState.focusedRootGlobalOffset.y),
width: toPx(focusState.focusedRoot.bounds.width), width: toPx(focusState.focusedRoot.bounds.width),
@@ -127,6 +133,7 @@ export const Visualization2D: React.FC<
/> />
</div> </div>
</div> </div>
</ContextMenu>
); );
}; };
@@ -230,6 +237,52 @@ function Visualization2DNode({
); );
} }
const ContextMenu: React.FC<{nodes: Map<Id, UINode>}> = ({children}) => {
const instance = usePlugin(plugin);
const focusedNodeId = useValue(instance.focusedNode);
const hoveredNodeId = head(useValue(instance.hoveredNodes));
const nodes = useValue(instance.nodes);
const hoveredNode = hoveredNodeId ? nodes.get(hoveredNodeId) : null;
const isMenuOpen = useValue(instance.isContextMenuOpen);
return (
<Dropdown
onVisibleChange={(open) => {
instance.isContextMenuOpen.set(open);
}}
trigger={['contextMenu']}
overlay={() => {
return (
<Menu>
{isMenuOpen && hoveredNode?.id !== focusedNodeId && (
<Menu.Item
key="focus"
onClick={() => {
instance.focusedNode.set(hoveredNode?.id);
instance.isContextMenuOpen.set(false);
}}>
Focus {hoveredNode?.name}
</Menu.Item>
)}
{isMenuOpen && focusedNodeId != null && (
<Menu.Item
key="remove-focus"
onClick={() => {
instance.focusedNode.set(undefined);
instance.isContextMenuOpen.set(false);
}}>
Remove focus
</Menu.Item>
)}
</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