Added context menu items for collapsing and expanding nodes
Summary: Added 3 context menu items: - expand recursive - collapse recursive These are self explanatory. I also collapse non ancestors. This collapses everything except your direct ancestor path to the root. Quite useful to refocus the tree on a node Changelog: UIDebugger - added context menu items for exanding and collapsing the tree. Reviewed By: aigoncharov Differential Revision: D47949840 fbshipit-source-id: 6eebba182fe2092fbf5f0db0ec5ff728c3900424
This commit is contained in:
committed by
Facebook GitHub Bot
parent
129c848b78
commit
ce1fdfdf19
@@ -95,9 +95,16 @@ export type UIActions = {
|
|||||||
onPlayPauseToggled: () => void;
|
onPlayPauseToggled: () => void;
|
||||||
onSearchTermUpdated: (searchTerm: string) => void;
|
onSearchTermUpdated: (searchTerm: string) => void;
|
||||||
onSetWireFrameMode: (WireFrameMode: WireFrameMode) => void;
|
onSetWireFrameMode: (WireFrameMode: WireFrameMode) => void;
|
||||||
|
onExpandAllRecursively: (nodeId: Id) => void;
|
||||||
|
onCollapseAllNonAncestors: (nodeId: Id) => void;
|
||||||
|
onCollapseAllRecursively: (nodeId: Id) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SelectionSource = 'visualiser' | 'tree' | 'keyboard';
|
export type SelectionSource =
|
||||||
|
| 'visualiser'
|
||||||
|
| 'tree'
|
||||||
|
| 'keyboard'
|
||||||
|
| 'context-menu';
|
||||||
|
|
||||||
export type StreamState =
|
export type StreamState =
|
||||||
| {state: 'Ok'}
|
| {state: 'Ok'}
|
||||||
|
|||||||
@@ -6,8 +6,9 @@
|
|||||||
*
|
*
|
||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {FrameworkEvent, Id, ClientNode} from '../../ClientTypes';
|
import {FrameworkEvent, Id, ClientNode} from '../../ClientTypes';
|
||||||
import {ViewMode} from '../../DesktopTypes';
|
import {OnSelectNode, ViewMode} from '../../DesktopTypes';
|
||||||
import React, {ReactNode} from 'react';
|
import React, {ReactNode} from 'react';
|
||||||
import {DataSource, getFlipperLib} from 'flipper-plugin';
|
import {DataSource, getFlipperLib} from 'flipper-plugin';
|
||||||
import {Dropdown, Menu} from 'antd';
|
import {Dropdown, Menu} from 'antd';
|
||||||
@@ -21,6 +22,9 @@ import {
|
|||||||
CopyOutlined,
|
CopyOutlined,
|
||||||
FullscreenExitOutlined,
|
FullscreenExitOutlined,
|
||||||
FullscreenOutlined,
|
FullscreenOutlined,
|
||||||
|
MenuFoldOutlined,
|
||||||
|
MenuUnfoldOutlined,
|
||||||
|
NodeExpandOutlined,
|
||||||
SnippetsOutlined,
|
SnippetsOutlined,
|
||||||
TableOutlined,
|
TableOutlined,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
@@ -33,6 +37,10 @@ export const ContextMenu: React.FC<{
|
|||||||
onFocusNode: (id?: Id) => void;
|
onFocusNode: (id?: Id) => void;
|
||||||
onContextMenuOpen: (open: boolean) => void;
|
onContextMenuOpen: (open: boolean) => void;
|
||||||
onSetViewMode: (viewMode: ViewMode) => void;
|
onSetViewMode: (viewMode: ViewMode) => void;
|
||||||
|
onExpandRecursively: (id: Id) => void;
|
||||||
|
onCollapseRecursively: (id: Id) => void;
|
||||||
|
onCollapseNonAncestors: (id: Id) => void;
|
||||||
|
onSelectNode: OnSelectNode;
|
||||||
}> = ({
|
}> = ({
|
||||||
nodes,
|
nodes,
|
||||||
frameworkEvents,
|
frameworkEvents,
|
||||||
@@ -42,11 +50,51 @@ export const ContextMenu: React.FC<{
|
|||||||
onFocusNode,
|
onFocusNode,
|
||||||
onContextMenuOpen,
|
onContextMenuOpen,
|
||||||
onSetViewMode,
|
onSetViewMode,
|
||||||
|
onExpandRecursively,
|
||||||
|
onCollapseRecursively,
|
||||||
|
onCollapseNonAncestors,
|
||||||
|
onSelectNode,
|
||||||
}) => {
|
}) => {
|
||||||
const copyItems: ReactNode[] = [];
|
const copyItems: ReactNode[] = [];
|
||||||
const hoveredNode = nodes.get(hoveredNodeId ?? Number.MAX_SAFE_INTEGER);
|
const hoveredNode = nodes.get(hoveredNodeId ?? Number.MAX_SAFE_INTEGER);
|
||||||
|
|
||||||
|
let treeCollapseItems: ReactNode[] = [];
|
||||||
if (hoveredNode) {
|
if (hoveredNode) {
|
||||||
|
treeCollapseItems = [
|
||||||
|
<UIDebuggerMenuItem
|
||||||
|
key="expand-recursive"
|
||||||
|
text="Expand recursively"
|
||||||
|
icon={<MenuUnfoldOutlined />}
|
||||||
|
onClick={() => {
|
||||||
|
onExpandRecursively(hoveredNode.id);
|
||||||
|
onSelectNode(hoveredNode.id, 'context-menu');
|
||||||
|
tracker.track('context-menu-expand-recursive', {});
|
||||||
|
}}
|
||||||
|
/>,
|
||||||
|
|
||||||
|
<UIDebuggerMenuItem
|
||||||
|
key="collapse-recursive"
|
||||||
|
text="Collapse recurisvely"
|
||||||
|
icon={<MenuFoldOutlined />}
|
||||||
|
onClick={() => {
|
||||||
|
onCollapseRecursively(hoveredNode.id);
|
||||||
|
onSelectNode(hoveredNode.id, 'context-menu');
|
||||||
|
tracker.track('context-menu-collapse-recursive', {});
|
||||||
|
}}
|
||||||
|
/>,
|
||||||
|
<UIDebuggerMenuItem
|
||||||
|
key="collapse-non-ancestors"
|
||||||
|
text="Collapse non ancestors"
|
||||||
|
icon={<NodeExpandOutlined />}
|
||||||
|
onClick={() => {
|
||||||
|
onCollapseNonAncestors(hoveredNode.id);
|
||||||
|
onSelectNode(hoveredNode.id, 'context-menu');
|
||||||
|
tracker.track('context-menu-collapse-non-ancestors', {});
|
||||||
|
}}
|
||||||
|
/>,
|
||||||
|
<Menu.Divider key="expand-divider" />,
|
||||||
|
];
|
||||||
|
|
||||||
copyItems.push(
|
copyItems.push(
|
||||||
<UIDebuggerMenuItem
|
<UIDebuggerMenuItem
|
||||||
key="Copy Element name"
|
key="Copy Element name"
|
||||||
@@ -131,6 +179,7 @@ export const ContextMenu: React.FC<{
|
|||||||
}}
|
}}
|
||||||
overlay={() => (
|
overlay={() => (
|
||||||
<Menu>
|
<Menu>
|
||||||
|
{treeCollapseItems}
|
||||||
{focus}
|
{focus}
|
||||||
{removeFocus}
|
{removeFocus}
|
||||||
{frameworkEventsTable}
|
{frameworkEventsTable}
|
||||||
|
|||||||
@@ -211,9 +211,13 @@ export function Tree2({
|
|||||||
focusedNodeId={focusedNode}
|
focusedNodeId={focusedNode}
|
||||||
hoveredNodeId={hoveredNode}
|
hoveredNodeId={hoveredNode}
|
||||||
nodes={nodes}
|
nodes={nodes}
|
||||||
|
onSelectNode={instance.uiActions.onSelectNode}
|
||||||
onSetViewMode={instance.uiActions.onSetViewMode}
|
onSetViewMode={instance.uiActions.onSetViewMode}
|
||||||
onContextMenuOpen={instance.uiActions.onContextMenuOpen}
|
onContextMenuOpen={instance.uiActions.onContextMenuOpen}
|
||||||
onFocusNode={instance.uiActions.onFocusNode}>
|
onFocusNode={instance.uiActions.onFocusNode}
|
||||||
|
onCollapseNonAncestors={instance.uiActions.onCollapseAllNonAncestors}
|
||||||
|
onCollapseRecursively={instance.uiActions.onCollapseAllRecursively}
|
||||||
|
onExpandRecursively={instance.uiActions.onExpandAllRecursively}>
|
||||||
<div
|
<div
|
||||||
//We use this normal divs flexbox sizing to measure how much vertical space we need for the child div
|
//We use this normal divs flexbox sizing to measure how much vertical space we need for the child div
|
||||||
ref={grandParentRef}
|
ref={grandParentRef}
|
||||||
|
|||||||
@@ -130,6 +130,10 @@ export const Visualization2D: React.FC<
|
|||||||
|
|
||||||
const onClickOverlay = () => {
|
const onClickOverlay = () => {
|
||||||
instance.uiActions.onSelectNode(hoveredNodeId, 'visualiser');
|
instance.uiActions.onSelectNode(hoveredNodeId, 'visualiser');
|
||||||
|
if (hoveredNodeId != null) {
|
||||||
|
instance.uiActions.onCollapseAllNonAncestors(hoveredNodeId);
|
||||||
|
}
|
||||||
|
|
||||||
if (targetMode.state !== 'disabled') {
|
if (targetMode.state !== 'disabled') {
|
||||||
setTargetMode({
|
setTargetMode({
|
||||||
state: 'selected',
|
state: 'selected',
|
||||||
|
|||||||
@@ -36,7 +36,10 @@ export function uiActions(
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
const onSelectNode = (node: Id | undefined, source: SelectionSource) => {
|
const onSelectNode = (node: Id | undefined, source: SelectionSource) => {
|
||||||
if (node == null || uiState.selectedNode.get()?.id === node) {
|
if (
|
||||||
|
node == null ||
|
||||||
|
(uiState.selectedNode.get()?.id === node && source !== 'context-menu')
|
||||||
|
) {
|
||||||
uiState.selectedNode.set(undefined);
|
uiState.selectedNode.set(undefined);
|
||||||
} else {
|
} else {
|
||||||
uiState.selectedNode.set({id: node, source});
|
uiState.selectedNode.set({id: node, source});
|
||||||
@@ -83,6 +86,52 @@ export function uiActions(
|
|||||||
uiState.isContextMenuOpen.set(open);
|
uiState.isContextMenuOpen.set(open);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onCollapseAllNonAncestors = (nodeId: Id) => {
|
||||||
|
//this is not the simplest way to achieve this but on android there is a parent pointer missing for the decor view
|
||||||
|
//due to the nested obversers.
|
||||||
|
uiState.expandedNodes.update((draft) => {
|
||||||
|
const nodesMap = nodes.get();
|
||||||
|
let prevNode: Id | null = null;
|
||||||
|
let curNode = nodesMap.get(nodeId);
|
||||||
|
while (curNode != null) {
|
||||||
|
for (const child of curNode.children) {
|
||||||
|
if (child !== prevNode) {
|
||||||
|
draft.delete(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prevNode = curNode.id;
|
||||||
|
curNode = nodesMap.get(curNode?.parent ?? 'Nonode');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function treeTraverseUtil(
|
||||||
|
nodeID: Id,
|
||||||
|
nodeVisitor: (node: ClientNode) => void,
|
||||||
|
) {
|
||||||
|
const nodesMap = nodes.get();
|
||||||
|
|
||||||
|
const node = nodesMap.get(nodeID);
|
||||||
|
if (node != null) {
|
||||||
|
nodeVisitor(node);
|
||||||
|
for (const childId of node.children) {
|
||||||
|
treeTraverseUtil(childId, nodeVisitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onExpandAllRecursively = (nodeId: Id) => {
|
||||||
|
uiState.expandedNodes.update((draft) => {
|
||||||
|
treeTraverseUtil(nodeId, (node) => draft.add(node.id));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onCollapseAllRecursively = (nodeId: Id) => {
|
||||||
|
uiState.expandedNodes.update((draft) => {
|
||||||
|
treeTraverseUtil(nodeId, (node) => draft.delete(node.id));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const onFocusNode = (node?: Id) => {
|
const onFocusNode = (node?: Id) => {
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
const focusedNode = nodes.get().get(node);
|
const focusedNode = nodes.get().get(node);
|
||||||
@@ -164,5 +213,8 @@ export function uiActions(
|
|||||||
onPlayPauseToggled,
|
onPlayPauseToggled,
|
||||||
onSearchTermUpdated,
|
onSearchTermUpdated,
|
||||||
onSetWireFrameMode,
|
onSetWireFrameMode,
|
||||||
|
onCollapseAllNonAncestors,
|
||||||
|
onExpandAllRecursively,
|
||||||
|
onCollapseAllRecursively,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,9 @@ type TrackerEvents = {
|
|||||||
on: boolean;
|
on: boolean;
|
||||||
};
|
};
|
||||||
'target-mode-adjusted': {};
|
'target-mode-adjusted': {};
|
||||||
|
'context-menu-expand-recursive': {};
|
||||||
|
'context-menu-collapse-recursive': {};
|
||||||
|
'context-menu-collapse-non-ancestors': {};
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface Tracker {
|
export interface Tracker {
|
||||||
|
|||||||
Reference in New Issue
Block a user