From ca67bfd9162010564dc4e78d35dea4b1656b357a Mon Sep 17 00:00:00 2001 From: Luke De Feo Date: Thu, 24 Nov 2022 09:23:16 -0800 Subject: [PATCH] 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 --- .../components/Visualization2D.tsx | 149 ++++++++++++------ 1 file changed, 101 insertions(+), 48 deletions(-) diff --git a/desktop/plugins/public/ui-debugger/components/Visualization2D.tsx b/desktop/plugins/public/ui-debugger/components/Visualization2D.tsx index e7e942d12..49585a6f5 100644 --- a/desktop/plugins/public/ui-debugger/components/Visualization2D.tsx +++ b/desktop/plugins/public/ui-debugger/components/Visualization2D.tsx @@ -13,6 +13,7 @@ import {Bounds, Coordinate, Id, NestedNode, Tag, UINode} from '../types'; import {produce, styled, theme, usePlugin, useValue} from 'flipper-plugin'; import {plugin} from '../index'; import {head, isEqual, throttle} from 'lodash'; +import {Dropdown, Menu} from 'antd'; export const Visualization2D: React.FC< { @@ -37,7 +38,8 @@ export const Visualization2D: React.FC< useEffect(() => { const mouseListener = throttle((ev: MouseEvent) => { const domRect = rootNodeRef.current?.getBoundingClientRect(); - if (!focusState || !domRect) { + + if (!focusState || !domRect || instance.isContextMenuOpen.get()) { return; } const rawMouse = {x: ev.clientX, y: ev.clientY}; @@ -70,63 +72,68 @@ export const Visualization2D: React.FC< return () => { window.removeEventListener('mousemove', mouseListener); }; - }, [instance.hoveredNodes, focusState, nodes]); + }, [instance.hoveredNodes, focusState, nodes, instance.isContextMenuOpen]); if (!focusState) { return null; } const snapshotNode = snapshot && nodes.get(snapshot.nodeId); - return ( -
-
{ - e.stopPropagation(); - instance.hoveredNodes.set([]); - }} - 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', + return ( + +
- {snapshotNode && ( - { + e.stopPropagation(); + //the context menu triggers this callback but we dont want to remove hover effect + if (!instance.isContextMenuOpen.get()) { + instance.hoveredNodes.set([]); + } + }} + 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 && ( + + )} + - )} - +
-
+ ); }; @@ -230,6 +237,52 @@ function Visualization2DNode({ ); } +const ContextMenu: React.FC<{nodes: Map}> = ({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 ( + { + instance.isContextMenuOpen.set(open); + }} + trigger={['contextMenu']} + overlay={() => { + return ( + + {isMenuOpen && hoveredNode?.id !== focusedNodeId && ( + { + instance.focusedNode.set(hoveredNode?.id); + instance.isContextMenuOpen.set(false); + }}> + Focus {hoveredNode?.name} + + )} + {isMenuOpen && focusedNodeId != null && ( + { + instance.focusedNode.set(undefined); + instance.isContextMenuOpen.set(false); + }}> + Remove focus + + )} + + ); + }}> + {children} + + ); +}; + /** * 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