Summary: There are situations where multiple siblings overlap and they are both hit. Previously we picked the first one in the hierachy. Now we produce a list of hit children. The list will not have 2 nodes in the same ancestor path. We store the hovered nodes as a list as we may want to present a modal in future to ask user which node they indented to select. That said simply sorting nodes by area seems to give decent results so we can start with this Reviewed By: lblasa Differential Revision: D41220271 fbshipit-source-id: 643a369113da28e8c4749725a7aee7aa5d08c401
115 lines
3.0 KiB
TypeScript
115 lines
3.0 KiB
TypeScript
/**
|
|
* 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 {PluginClient, createState, createDataSource} from 'flipper-plugin';
|
|
import {
|
|
Events,
|
|
Id,
|
|
Metadata,
|
|
MetadataId,
|
|
PerfStatsEvent,
|
|
Snapshot,
|
|
TreeState,
|
|
UINode,
|
|
} from './types';
|
|
import './node_modules/react-complex-tree/lib/style.css';
|
|
|
|
export function plugin(client: PluginClient<Events>) {
|
|
const rootId = createState<Id | undefined>(undefined);
|
|
const metadata = createState<Map<MetadataId, Metadata>>(new Map());
|
|
|
|
client.onMessage('init', (event) => {
|
|
rootId.set(event.rootId);
|
|
});
|
|
|
|
client.onMessage('metadataUpdate', (event) => {
|
|
if (!event.attributeMetadata) {
|
|
return;
|
|
}
|
|
metadata.update((draft) => {
|
|
for (const [_key, value] of Object.entries(event.attributeMetadata)) {
|
|
draft.set(value.id, value);
|
|
}
|
|
});
|
|
});
|
|
|
|
const perfEvents = createDataSource<PerfStatsEvent, 'txId'>([], {
|
|
key: 'txId',
|
|
limit: 10 * 1024,
|
|
});
|
|
client.onMessage('perfStats', (event) => {
|
|
perfEvents.append(event);
|
|
});
|
|
|
|
const nodes = createState<Map<Id, UINode>>(new Map());
|
|
const snapshots = createState<Map<Id, Snapshot>>(new Map());
|
|
|
|
const treeState = createState<TreeState>({expandedNodes: []});
|
|
|
|
//The reason for the array as that user could be hovering multiple overlapping nodes at once in the visualiser.
|
|
//The nodes are sorted by area since you most likely want to select the smallest node under your cursor
|
|
const hoveredNodes = createState<Id[]>([]);
|
|
|
|
client.onMessage('coordinateUpdate', (event) => {
|
|
nodes.update((draft) => {
|
|
const node = draft.get(event.nodeId);
|
|
if (!node) {
|
|
console.warn(`Coordinate update for non existing node `, event);
|
|
} else {
|
|
node.bounds.x = event.coordinate.x;
|
|
node.bounds.y = event.coordinate.y;
|
|
}
|
|
});
|
|
});
|
|
|
|
const seenNodes = new Set<Id>();
|
|
client.onMessage('subtreeUpdate', (event) => {
|
|
snapshots.update((draft) => {
|
|
draft.set(event.rootId, event.snapshot);
|
|
});
|
|
nodes.update((draft) => {
|
|
event.nodes.forEach((node) => {
|
|
draft.set(node.id, node);
|
|
});
|
|
});
|
|
|
|
treeState.update((draft) => {
|
|
for (const node of event.nodes) {
|
|
if (!seenNodes.has(node.id)) {
|
|
draft.expandedNodes.push(node.id);
|
|
}
|
|
seenNodes.add(node.id);
|
|
|
|
if (node.activeChild) {
|
|
const inactiveChildren = node.children.filter(
|
|
(child) => child !== node.activeChild,
|
|
);
|
|
|
|
draft.expandedNodes = draft.expandedNodes.filter(
|
|
(nodeId) => !inactiveChildren.includes(nodeId),
|
|
);
|
|
draft.expandedNodes.push(node.activeChild);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
return {
|
|
rootId,
|
|
nodes,
|
|
metadata,
|
|
snapshots,
|
|
hoveredNodes,
|
|
perfEvents,
|
|
treeState,
|
|
};
|
|
}
|
|
|
|
export {Component} from './components/main';
|