Added $r support
Brian Vaughn committed
Feb 5, 2019 at 13:19 UTC
e723ec82bc84975234d08892065b450699a51fc1
11 files changed
+64
-13
shells/browser/shared/src/backend.js
+1
-1
@@ -64,5 +64,5 @@ function setup(hook) {
64
listeners.splice(0);
65
});
66
67
- initBackend(window.__REACT_DEVTOOLS_GLOBAL_HOOK__, agent);
67
+ initBackend(window.__REACT_DEVTOOLS_GLOBAL_HOOK__, agent, window);
68
}
shells/dev/src/backend.js
+1
-1
@@ -18,4 +18,4 @@ const bridge = new Bridge({
18
const agent = new Agent();
19
agent.addBridge(bridge);
20
21
-initBackend(window.__REACT_DEVTOOLS_GLOBAL_HOOK__, agent);
21
+initBackend(window.__REACT_DEVTOOLS_GLOBAL_HOOK__, agent, window.parent);
src/backend/agent.js
+9
-2
@@ -27,18 +27,25 @@ export default class Agent extends EventEmitter {
27
bridge.addListener('shutdown', () => this.emit('shutdown'));
28
29
bridge.addListener('inspectElement', this.inspectElement);
30
+ bridge.addListener('selectElement', this.selectElement);
31
// TODO Listen to bridge for things like selection.
32
// bridge.on('...'), this...);
33
}
34
35
inspectElement = ({ id, rendererID }: { id: number, rendererID: number }) => {
36
const renderer = this._rendererInterfaces[rendererID];
37
+ if (renderer == null) {
38
+ console.warn(`Invalid renderer id "${rendererID}" for element "${id}"`);
39
+ }
40
+ this._bridge.send('inspectedElement', renderer.inspectElement(id));
41
+ };
42
43
+ selectElement = ({ id, rendererID }: { id: number, rendererID: number }) => {
44
+ const renderer = this._rendererInterfaces[rendererID];
45
if (renderer == null) {
46
console.warn(`Invalid renderer id "${rendererID}" for element "${id}"`);
39
- } else {
40
- this._bridge.send('inspectedElement', renderer.inspectElement(id));
47
}
48
+ this._bridge.send('selectElement', renderer.selectElement(id));
49
};
50
51
setRendererInterface(
src/backend/index.js
+2
-2
@@ -5,7 +5,7 @@ import Agent from './agent';
5
6
import { attach } from './renderer';
7
8
-export function initBackend(hook: Hook, agent: Agent): void {
8
+export function initBackend(hook: Hook, agent: Agent, global: Object): void {
9
const subs = [
10
hook.sub(
11
'renderer-attached',
@@ -30,7 +30,7 @@ export function initBackend(hook: Hook, agent: Agent): void {
30
];
31
32
const attachRenderer = (id: number, renderer: ReactRenderer) => {
33
- const rendererInterface = attach(hook, id, renderer);
33
+ const rendererInterface = attach(hook, id, renderer, global);
34
hook.rendererInterfaces.set(id, rendererInterface);
35
hook.emit('renderer-attached', {
36
id,
src/backend/renderer.js
+29
-1
@@ -152,7 +152,8 @@ function getInternalReactConstants(version) {
152
export function attach(
153
hook: Hook,
154
rendererID: number,
155
- renderer: ReactRenderer
155
+ renderer: ReactRenderer,
156
+ global: Object
157
): RendererInterface {
158
const {
159
ReactTypeOfWork,
@@ -1001,6 +1002,32 @@ export function attach(
1002
return alternate;
1003
}
1004
1005
+ function selectElement(id: number): void {
1006
+ let fiber = idToFiberMap.get(id);
1007
+
1008
+ if (fiber == null) {
1009
+ console.warn(`Could not find Fiber with id "${id}"`);
1010
+ return;
1011
+ }
1012
+
1013
+ switch (fiber.tag) {
1014
+ case ClassComponent:
1015
+ case IncompleteClassComponent:
1016
+ case IndeterminateComponent:
1017
+ global.$r = fiber.stateNode;
1018
+ break;
1019
+ case FunctionComponent:
1020
+ global.$r = {
1021
+ props: fiber.memoizedProps,
1022
+ type: fiber.type,
1023
+ };
1024
+ break;
1025
+ default:
1026
+ global.$r = null;
1027
+ break;
1028
+ }
1029
+ }
1030
+
1031
function inspectElement(id: number): InspectedElement | null {
1032
let fiber = idToFiberMap.get(id);
1033
@@ -1127,6 +1154,7 @@ export function attach(
1154
handleCommitFiberRoot,
1155
handleCommitFiberUnmount,
1156
inspectElement,
1157
+ selectElement,
1158
cleanup,
1159
walkTree,
1160
renderer,
src/backend/types.js
+1
@@ -50,6 +50,7 @@ export type RendererInterface = {
50
handleCommitFiberRoot: (fiber: Object) => void,
51
inspectElement: (id: number) => InspectedElement | null,
52
renderer: ReactRenderer | null,
53
+ selectElement: (id: number) => void,
54
walkTree: () => void,
55
};
56
src/devtools/views/Element.css
+4
@@ -26,6 +26,10 @@
26
--color-attribute-value: #fff;
27
}
28
29
+.DollarR {
30
+ color: var(--color-tree-jsx-arrow-brackets);
31
+}
32
+
33
.ArrowClosed,
34
.ArrowOpen {
35
position: absolute;
src/devtools/views/Element.js
+7
-4
@@ -1,6 +1,7 @@
1
// @flow
2
3
import React, { Fragment, useCallback, useContext } from 'react';
4
+import { ElementTypeClassOrFunction } from 'src/devtools/types';
5
import { TreeContext } from './context';
6
import { SelectedElementContext } from './SelectedElementContext';
7
import Icon from './Icon';
@@ -26,7 +27,7 @@ export default function Element({ index, style }: Props) {
27
28
// TODO Add click and key handlers for toggling element open/close state.
29
29
- const { children, depth, displayName, id, key } = element;
30
+ const { children, depth, displayName, id, key, type } = element;
31
32
const selectedElement = useContext(SelectedElementContext);
33
const handleClick = useCallback(
@@ -36,11 +37,12 @@ export default function Element({ index, style }: Props) {
37
[id]
38
);
39
40
+ const isSelected = selectedElement.id === id;
41
+ const showDollarR = isSelected && type === ElementTypeClassOrFunction;
42
+
43
return (
44
<div
41
- className={
42
- selectedElement.id === id ? styles.SelectedElement : styles.Element
43
- }
45
+ className={isSelected ? styles.SelectedElement : styles.Element}
46
onClick={handleClick}
47
style={{
48
...style, // "style" comes from react-window
@@ -62,6 +64,7 @@ export default function Element({ index, style }: Props) {
64
</Fragment>
65
)}
66
</span>
67
+ {showDollarR && <span className={styles.DollarR}> == $r</span>}
68
</div>
69
);
70
}
src/devtools/views/InspectedElementTree.js
+6
-1
@@ -29,7 +29,12 @@ export default function InspectedElementTree({
29
{isEmpty && <div className={styles.Empty}>None</div>}
30
{!isEmpty &&
31
Object.keys((data: any)).map(name => (
32
- <KeyValue key={name} depth={1} name={name} value={(data: any)[name]} />
32
+ <KeyValue
33
+ key={name}
34
+ depth={1}
35
+ name={name}
36
+ value={(data: any)[name]}
37
+ />
38
))}
39
</div>
40
);
src/devtools/views/SelectedElement.js
+3
@@ -144,6 +144,9 @@ function useInspectedElement(id: number | null): InspectedElement | null {
144
145
let timeoutID = null;
146
147
+ // Update the $r variable.
148
+ bridge.send('selectElement', { id, rendererID });
149
+
150
const sendBridgeRequest = () => {
151
bridge.send('inspectElement', { id, rendererID });
152
};
src/devtools/views/root.css
+1
-1
@@ -12,7 +12,7 @@
12
--theme-light-component-name-inverted: #ffffff;
13
--theme-light-deemphasized: #cfd1d5;
14
--theme-light-jsx-arrow-brackets: #333333;
15
- --theme-light-jsx-arrow-brackets-inverted: rgba(255, 255, 255, 0.8);
15
+ --theme-light-jsx-arrow-brackets-inverted: rgba(255, 255, 255, 0.7);
16
--theme-light-tree-node-selected: #0088fa;
17
--theme-light-tree-node-hover: #ebf1fb;
18