Remove react complex tree

Reviewed By: lblasa

Differential Revision: D41875029

fbshipit-source-id: 2af58610fe0d0f644aa8450a4210fd52f8ed4db6
This commit is contained in:
Luke De Feo
2022-12-12 07:28:37 -08:00
committed by Facebook GitHub Bot
parent ed35623bef
commit 74247ee721
6 changed files with 2 additions and 318 deletions

View File

@@ -1,306 +0,0 @@
/**
* 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 {Id, UINode} from '../types';
import React, {useEffect, useMemo, useRef} from 'react';
import {
Tree as ComplexTree,
ControlledTreeEnvironment,
TreeItem,
TreeInformation,
TreeItemRenderContext,
InteractionMode,
TreeEnvironmentRef,
} from 'react-complex-tree';
import {plugin} from '../index';
import {
usePlugin,
useValue,
HighlightManager,
HighlightProvider,
useHighlighter,
theme,
styled,
} from 'flipper-plugin';
import {head} from 'lodash';
import {Dropdown, Menu} from 'antd';
import {UIDebuggerMenuItem} from './util/UIDebuggerMenuItem';
export function Tree(props: {
rootId: Id;
nodes: Map<Id, UINode>;
selectedNode?: Id;
onSelectNode: (id: Id) => void;
}) {
const instance = usePlugin(plugin);
const expandedItems = useValue(instance.uiState.expandedNodes);
const focused = useValue(instance.uiState.focusedNode);
const items = useMemo(
() => toComplexTree(focused || props.rootId, props.nodes),
[focused, props.nodes, props.rootId],
);
const hoveredNodes = useValue(instance.uiState.hoveredNodes);
const treeEnvRef = useRef<TreeEnvironmentRef>();
const searchTerm = useValue(instance.uiState.searchTerm);
useEffect(() => {
//this makes the keyboard arrow controls work always, even when using the visualiser
treeEnvRef.current?.focusTree('tree', true);
}, [props.selectedNode]);
return (
<div
onMouseLeave={() => {
instance.uiState.hoveredNodes.set([]);
}}
style={
{
'--rct-color-tree-bg': theme.white,
'--rct-color-tree-focus-outline': theme.dividerColor,
'--rct-color-focustree-item-focused-border':
theme.selectionBackgroundColor,
'--rct-color-focustree-item-selected-bg':
theme.selectionBackgroundColor,
'--rct-color-nonfocustree-item-selected-bg':
theme.selectionBackgroundColor,
} as React.CSSProperties
}>
<HighlightProvider
text={searchTerm}
highlightColor={theme.searchHighlightBackground.yellow}>
<ControlledTreeEnvironment
ref={treeEnvRef as any}
items={items}
getItemTitle={(item) => item.data.name}
canRename={false}
canDragAndDrop={false}
viewState={{
tree: {
focusedItem: head(hoveredNodes),
expandedItems: [...expandedItems],
selectedItems: props.selectedNode ? [props.selectedNode] : [],
},
}}
onFocusItem={(item) => {
instance.uiState.hoveredNodes.set([item.index]);
}}
onExpandItem={(item) => {
instance.uiState.expandedNodes.update((draft) => {
draft.add(item.index);
});
}}
onCollapseItem={(item) =>
instance.uiState.expandedNodes.update((draft) => {
draft.delete(item.index);
})
}
renderItem={renderItem}
onSelectItems={(items) => props.onSelectNode(items[0])}
defaultInteractionMode={{
mode: 'custom',
extends: InteractionMode.DoubleClickItemToExpand,
createInteractiveElementProps: (
item,
treeId,
actions,
renderFlags,
) => ({
onClick: () => {
if (renderFlags.isSelected) {
actions.unselectItem();
} else {
actions.selectItem();
}
},
onMouseOver: () => {
if (!instance.uiState.isContextMenuOpen.get()) {
instance.uiState.hoveredNodes.set([item.index]);
}
},
}),
}}>
<ComplexTree
treeId="tree"
rootItem={FakeNode.id as any} //the typing in in the library is wrong here
treeLabel="UI"
/>
</ControlledTreeEnvironment>
</HighlightProvider>
</div>
);
}
//copied from https://github.com/lukasbach/react-complex-tree/blob/e3dcc435933284376a0fc6e3cc651e67ead678b5/packages/core/src/renderers/createDefaultRenderers.tsx
const cx = (...classNames: Array<string | undefined | false>) =>
classNames.filter((cn) => !!cn).join(' ');
const renderDepthOffset = 5;
const DecorationImage = styled.img({
height: 12,
marginRight: 5,
width: 12,
});
function defaultIcon(node: UINode) {
if (node.tags.includes('Litho')) {
return <DecorationImage src="icons/litho-logo.png" />;
}
}
function renderItem<C extends string = never>({
item,
depth,
children,
arrow,
context,
}: {
item: TreeItem<UINode>;
depth: number;
children: React.ReactNode | null;
title: React.ReactNode;
arrow: React.ReactNode;
context: TreeItemRenderContext<C>;
info: TreeInformation;
}) {
return (
<li
{...(context.itemContainerWithChildrenProps as any)}
className={cx(
'rct-tree-item-li',
item.hasChildren && 'rct-tree-item-li-hasChildren',
context.isSelected && 'rct-tree-item-li-selected',
context.isExpanded && 'rct-tree-item-li-expanded',
context.isFocused && 'rct-tree-item-li-focused',
context.isDraggingOver && 'rct-tree-item-li-dragging-over',
context.isSearchMatching && 'rct-tree-item-li-search-match',
)}>
<ContextMenu node={item.data} id={item.index} title={item.data.name}>
<div
{...(context.itemContainerWithoutChildrenProps as any)}
style={{
paddingLeft: `${(depth + 1) * renderDepthOffset}px`,
}}
className={cx(
'rct-tree-item-title-container',
item.hasChildren && 'rct-tree-item-title-container-hasChildren',
context.isSelected && 'rct-tree-item-title-container-selected',
context.isExpanded && 'rct-tree-item-title-container-expanded',
context.isFocused && 'rct-tree-item-title-container-focused',
context.isDraggingOver &&
'rct-tree-item-title-container-dragging-over',
context.isSearchMatching &&
'rct-tree-item-title-container-search-match',
)}>
{arrow}
<div
{...(context.interactiveElementProps as any)}
className={cx(
'rct-tree-item-button',
item.hasChildren && 'rct-tree-item-button-hasChildren',
context.isSelected && 'rct-tree-item-button-selected',
context.isExpanded && 'rct-tree-item-button-expanded',
context.isFocused && 'rct-tree-item-button-focused',
context.isDraggingOver && 'rct-tree-item-button-dragging-over',
context.isSearchMatching && 'rct-tree-item-button-search-match',
)}>
{defaultIcon(item.data)}
<HighlightedText text={item.data.name} />
</div>
</div>
</ContextMenu>
{children}
</li>
);
}
type ContextMenuProps = {node: UINode; id: Id; title: string};
const ContextMenu: React.FC<ContextMenuProps> = ({id, title, children}) => {
const instance = usePlugin(plugin);
const focusedNode = instance.uiState.focusedNode.get();
return (
<Dropdown
onVisibleChange={(visible) => {
instance.uiState.isContextMenuOpen.set(visible);
}}
overlay={() => (
<Menu>
{focusedNode !== head(instance.uiState.hoveredNodes.get()) && (
<UIDebuggerMenuItem
key="focus"
text={`Focus ${title}`}
onClick={() => {
instance.uiState.focusedNode.set(id);
}}
/>
)}
{focusedNode && (
<UIDebuggerMenuItem
key="remove-focus"
text="Remove focus"
onClick={() => {
instance.uiState.focusedNode.set(undefined);
}}
/>
)}
</Menu>
)}
trigger={['contextMenu']}>
<div>{children}</div>
</Dropdown>
);
};
function HighlightedText(props: {text: string}) {
const highlightManager: HighlightManager = useHighlighter();
return <span>{highlightManager.render(props.text)}</span>;
}
const FakeNode: UINode = {
id: 'Fakeroot',
qualifiedName: 'Fakeroot',
name: 'Fakeroot',
inlineAttributes: {},
children: [],
attributes: {},
bounds: {x: 0, y: 0, height: 0, width: 0},
tags: [],
};
function toComplexTree(
root: Id,
nodes: Map<Id, UINode>,
): Record<Id, TreeItem<UINode>> {
const res: Record<Id, TreeItem<UINode>> = {};
for (const node of nodes.values()) {
res[node.id] = {
index: node.id,
children: node.children,
data: node,
hasChildren: node.children.length > 0,
};
}
//the library doesnt render the root node so we insert a fake one which will never be rendered
//https://github.com/lukasbach/react-complex-tree/issues/42
res[FakeNode.id] = {
index: FakeNode.id,
children: [root],
hasChildren: true,
data: FakeNode,
};
return res;
}

View File

@@ -13,12 +13,11 @@ import {DetailSidebar, Layout, usePlugin, useValue} from 'flipper-plugin';
import {useHotkeys} from 'react-hotkeys-hook';
import {Id, Metadata, MetadataId, UINode} from '../types';
import {PerfStats} from './PerfStats';
import {Tree} from './Tree';
import {Visualization2D} from './Visualization2D';
import {useKeyboardModifiers} from '../hooks/useKeyboardModifiers';
import {Inspector} from './sidebar/Inspector';
import {Controls} from './Controls';
import {Input, Spin} from 'antd';
import {Spin} from 'antd';
import FeedbackRequest from './fb-stubs/feedback';
import {Tree2} from './Tree2';

View File

@@ -23,7 +23,6 @@ import {
Snapshot,
UINode,
} from './types';
import './node_modules/react-complex-tree/lib/style.css';
import {Draft} from 'immer';
type SnapshotInfo = {nodeId: Id; base64Image: Snapshot};

View File

@@ -15,7 +15,6 @@
"dependencies": {
"lodash": "^4.17.21",
"react-color": "^2.19.3",
"react-complex-tree" : "^1.1.11",
"react-hotkeys-hook": "^3.4.7"
},
"bugs": {

View File

@@ -7,8 +7,6 @@
* @format
*/
import {TreeItemIndex} from 'react-complex-tree';
export type Events = {
init: InitEvent;
subtreeUpdate: SubtreeUpdateEvent;
@@ -121,7 +119,7 @@ export type Color = {
};
export type Snapshot = string;
export type Id = number | TreeItemIndex;
export type Id = number;
export type MetadataId = number;
export type TreeState = {expandedNodes: Id[]};

View File

@@ -1644,11 +1644,6 @@ react-color@^2.19.3:
reactcss "^1.2.0"
tinycolor2 "^1.4.1"
react-complex-tree@^1.1.11:
version "1.1.11"
resolved "https://registry.yarnpkg.com/react-complex-tree/-/react-complex-tree-1.1.11.tgz#430520d12908b033a4b278be0dfd8d0aa6654a85"
integrity sha512-hAkm2ZRH2lwZd7NEzZMQI8db/jI5T2fJsbwHX8oNPrG/WPdakc3eNpm2A4gLk2SBa88HeU6mnauVXg6Q6fJLow==
react-devtools-core@^4.26.1:
version "4.26.1"
resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.26.1.tgz#2893fea58089be64c5356d5bd0eebda8d1bbf317"