@refinedev/devtools 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +35 -0
- package/dist/components/devtools-pin.d.ts +10 -0
- package/dist/components/devtools-pin.d.ts.map +1 -0
- package/dist/components/devtools-selector.d.ts +8 -0
- package/dist/components/devtools-selector.d.ts.map +1 -0
- package/dist/components/icons/devtools-icon.d.ts +8 -0
- package/dist/components/icons/devtools-icon.d.ts.map +1 -0
- package/dist/components/icons/resize-handle-icon.d.ts +3 -0
- package/dist/components/icons/resize-handle-icon.d.ts.map +1 -0
- package/dist/components/icons/selector-button.d.ts +3 -0
- package/dist/components/icons/selector-button.d.ts.map +1 -0
- package/dist/components/resizable-pane.d.ts +19 -0
- package/dist/components/resizable-pane.d.ts.map +1 -0
- package/dist/components/selector-box.d.ts +8 -0
- package/dist/components/selector-box.d.ts.map +1 -0
- package/dist/components/selector-hint.d.ts +5 -0
- package/dist/components/selector-hint.d.ts.map +1 -0
- package/dist/esm/index.js +8 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/iife/index.js +8 -0
- package/dist/iife/index.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces/placement.d.ts +2 -0
- package/dist/interfaces/placement.d.ts.map +1 -0
- package/dist/panel.d.ts +2 -0
- package/dist/panel.d.ts.map +1 -0
- package/dist/provider.d.ts +3 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/utilities/index.d.ts +37 -0
- package/dist/utilities/index.d.ts.map +1 -0
- package/dist/utilities/use-selector.d.ts +10 -0
- package/dist/utilities/use-selector.d.ts.map +1 -0
- package/package.json +62 -0
- package/src/components/devtools-pin.tsx +62 -0
- package/src/components/devtools-selector.tsx +127 -0
- package/src/components/icons/devtools-icon.tsx +91 -0
- package/src/components/icons/resize-handle-icon.tsx +27 -0
- package/src/components/icons/selector-button.tsx +81 -0
- package/src/components/resizable-pane.tsx +264 -0
- package/src/components/selector-box.tsx +80 -0
- package/src/components/selector-hint.tsx +63 -0
- package/src/define.d.ts +1 -0
- package/src/index.ts +2 -0
- package/src/interfaces/placement.ts +1 -0
- package/src/panel.tsx +93 -0
- package/src/provider.tsx +11 -0
- package/src/utilities/index.ts +142 -0
- package/src/utilities/use-selector.tsx +242 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
export const SelectorBox = ({
|
|
4
|
+
width,
|
|
5
|
+
height,
|
|
6
|
+
x,
|
|
7
|
+
y,
|
|
8
|
+
name,
|
|
9
|
+
}: {
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
x: number;
|
|
13
|
+
y: number;
|
|
14
|
+
name: string;
|
|
15
|
+
}) => {
|
|
16
|
+
const namePosition = y - 6 > 25 ? "top" : "bottom";
|
|
17
|
+
|
|
18
|
+
const outlinePosition = x > 7 ? "outside" : "inside";
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<div
|
|
22
|
+
id="selector-box"
|
|
23
|
+
style={{
|
|
24
|
+
pointerEvents: "none",
|
|
25
|
+
position: "fixed",
|
|
26
|
+
opacity: name ? 1 : 0,
|
|
27
|
+
transition: "all 0.2s ease-in-out",
|
|
28
|
+
border: "1.5px solid #47EBEB",
|
|
29
|
+
borderRadius: "4px",
|
|
30
|
+
borderTopLeftRadius: 0,
|
|
31
|
+
background: "rgba(37,160,160, 0.25)",
|
|
32
|
+
backdropFilter: "sepia(30%) hue-rotate(180deg)",
|
|
33
|
+
zIndex: 99998,
|
|
34
|
+
...(outlinePosition === "outside"
|
|
35
|
+
? {
|
|
36
|
+
left: -6,
|
|
37
|
+
top: -6,
|
|
38
|
+
width: width + 10,
|
|
39
|
+
height: height + 10,
|
|
40
|
+
transform: `translate(${x}px, ${y}px)`,
|
|
41
|
+
}
|
|
42
|
+
: {
|
|
43
|
+
left: 0,
|
|
44
|
+
top: 0,
|
|
45
|
+
width: width - 10,
|
|
46
|
+
height: height - 20,
|
|
47
|
+
transform: `translate(${x + 4}px, ${y + 4}px)`,
|
|
48
|
+
}),
|
|
49
|
+
}}
|
|
50
|
+
>
|
|
51
|
+
<div
|
|
52
|
+
style={{
|
|
53
|
+
position: "absolute",
|
|
54
|
+
left: "-1.5px",
|
|
55
|
+
background: "#1D1E30",
|
|
56
|
+
padding: "4px 6px",
|
|
57
|
+
border: "1.5px solid #47EBEB",
|
|
58
|
+
fontSize: "13px",
|
|
59
|
+
lineHeight: "16px",
|
|
60
|
+
fontWeight: 600,
|
|
61
|
+
color: "#CFD7E2",
|
|
62
|
+
display: name ? "block" : "none",
|
|
63
|
+
...(namePosition === "top"
|
|
64
|
+
? {
|
|
65
|
+
top: "-27px",
|
|
66
|
+
borderTopLeftRadius: "4px",
|
|
67
|
+
borderTopRightRadius: "4px",
|
|
68
|
+
}
|
|
69
|
+
: {
|
|
70
|
+
top: "-1.5px",
|
|
71
|
+
borderBottomLeftRadius: "0",
|
|
72
|
+
borderBottomRightRadius: "4px",
|
|
73
|
+
}),
|
|
74
|
+
}}
|
|
75
|
+
>
|
|
76
|
+
{name}
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
export const SelectorHint = ({
|
|
4
|
+
active,
|
|
5
|
+
groupHover,
|
|
6
|
+
}: {
|
|
7
|
+
active: boolean;
|
|
8
|
+
groupHover?: boolean;
|
|
9
|
+
}) => {
|
|
10
|
+
return (
|
|
11
|
+
<div
|
|
12
|
+
style={{
|
|
13
|
+
pointerEvents: "none",
|
|
14
|
+
position: "absolute",
|
|
15
|
+
left: "calc(-50% - 100px - 14px)",
|
|
16
|
+
top: "-50px",
|
|
17
|
+
width: "200px",
|
|
18
|
+
opacity: active ? 1 : 0,
|
|
19
|
+
transform: groupHover ? "translateX(0)" : "translateX(40px)",
|
|
20
|
+
transitionDuration: "0.2s",
|
|
21
|
+
transitionProperty: "transform,opacity",
|
|
22
|
+
transitionTimingFunction: "ease-in-out",
|
|
23
|
+
padding: "8px",
|
|
24
|
+
fontSize: "10px",
|
|
25
|
+
lineHeight: "12px",
|
|
26
|
+
fontWeight: 400,
|
|
27
|
+
textShadow:
|
|
28
|
+
"0 0 2px #1D1E30, 1px 0 2px #1D1E30, -1px 0 2px #1D1E30, 0 1px 2px #1D1E30, 0 -1px 2px #1D1E30",
|
|
29
|
+
}}
|
|
30
|
+
>
|
|
31
|
+
<kbd
|
|
32
|
+
style={{
|
|
33
|
+
marginLeft: "4px",
|
|
34
|
+
padding: "1px 2px",
|
|
35
|
+
borderRadius: "2px",
|
|
36
|
+
background: "whitesmoke",
|
|
37
|
+
color: "dimgray",
|
|
38
|
+
border: "0.5px solid silver",
|
|
39
|
+
boxShadow: "0 1px 1px silver",
|
|
40
|
+
textShadow: "none",
|
|
41
|
+
}}
|
|
42
|
+
>
|
|
43
|
+
shift
|
|
44
|
+
</kbd>{" "}
|
|
45
|
+
to move to parent.
|
|
46
|
+
<kbd
|
|
47
|
+
style={{
|
|
48
|
+
marginLeft: "4px",
|
|
49
|
+
padding: "1px 2px",
|
|
50
|
+
borderRadius: "2px",
|
|
51
|
+
background: "whitesmoke",
|
|
52
|
+
color: "dimgray",
|
|
53
|
+
border: "0.5px solid silver",
|
|
54
|
+
boxShadow: "0 1px 1px silver",
|
|
55
|
+
textShadow: "none",
|
|
56
|
+
}}
|
|
57
|
+
>
|
|
58
|
+
space
|
|
59
|
+
</kbd>{" "}
|
|
60
|
+
to highlight in monitor.
|
|
61
|
+
</div>
|
|
62
|
+
);
|
|
63
|
+
};
|
package/src/define.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
declare const __DEV_CONDITION__: string;
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type Placement = "bottom" | "left" | "right" | "top";
|
package/src/panel.tsx
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { DevtoolsPin } from "./components/devtools-pin";
|
|
3
|
+
import { ResizablePane } from "./components/resizable-pane";
|
|
4
|
+
|
|
5
|
+
import { SIZE, getPinTransform } from "./utilities";
|
|
6
|
+
|
|
7
|
+
import { Placement } from "./interfaces/placement";
|
|
8
|
+
import {
|
|
9
|
+
DevToolsContext,
|
|
10
|
+
DevtoolsEvent,
|
|
11
|
+
send,
|
|
12
|
+
} from "@refinedev/devtools-shared";
|
|
13
|
+
|
|
14
|
+
export const DevtoolsPanel =
|
|
15
|
+
__DEV_CONDITION__ !== "development"
|
|
16
|
+
? () => null
|
|
17
|
+
: () => {
|
|
18
|
+
const [visible, setVisible] = React.useState(false);
|
|
19
|
+
const [placement] = React.useState<Placement>("bottom");
|
|
20
|
+
const { devtoolsUrl, ws } = React.useContext(DevToolsContext);
|
|
21
|
+
const [hover, setHover] = React.useState(false);
|
|
22
|
+
|
|
23
|
+
const onSelectorHighlight = React.useCallback(
|
|
24
|
+
(name: string) => {
|
|
25
|
+
if (ws) {
|
|
26
|
+
send(
|
|
27
|
+
ws,
|
|
28
|
+
DevtoolsEvent.DEVTOOLS_HIGHLIGHT_IN_MONITOR,
|
|
29
|
+
{
|
|
30
|
+
name,
|
|
31
|
+
},
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
setVisible(true);
|
|
35
|
+
},
|
|
36
|
+
[ws],
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const onSelectorOpen = React.useCallback(() => {
|
|
40
|
+
setVisible(false);
|
|
41
|
+
}, []);
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<div
|
|
45
|
+
style={{
|
|
46
|
+
position: "fixed",
|
|
47
|
+
left: 0,
|
|
48
|
+
top: 0,
|
|
49
|
+
zIndex: 99999,
|
|
50
|
+
width: `${SIZE * 2}px`,
|
|
51
|
+
height: `${SIZE}px`,
|
|
52
|
+
|
|
53
|
+
transform: getPinTransform(placement),
|
|
54
|
+
}}
|
|
55
|
+
onMouseEnter={() => setHover(true)}
|
|
56
|
+
onMouseLeave={() => setHover(false)}
|
|
57
|
+
>
|
|
58
|
+
<DevtoolsPin
|
|
59
|
+
active={visible}
|
|
60
|
+
onClick={() => setVisible((v) => !v)}
|
|
61
|
+
groupHover={hover}
|
|
62
|
+
onSelectorHighlight={onSelectorHighlight}
|
|
63
|
+
onSelectorOpen={onSelectorOpen}
|
|
64
|
+
/>
|
|
65
|
+
<ResizablePane visible={visible} placement={placement}>
|
|
66
|
+
{({ resizing }) => (
|
|
67
|
+
<iframe
|
|
68
|
+
src={devtoolsUrl}
|
|
69
|
+
srcDoc={
|
|
70
|
+
devtoolsUrl
|
|
71
|
+
? undefined
|
|
72
|
+
: `
|
|
73
|
+
<html style="height:100%;padding:0;margin:0;">
|
|
74
|
+
<body style="display:flex;justify-content:center;height:100%;padding:24px;margin:0;align-items:center;box-sizing:border-box;">
|
|
75
|
+
<h1 style="font-family:ui-monospace,monospace;color:#CFD7E2;text-align:center;">Could not connect to the devtools server</h1>
|
|
76
|
+
</body>
|
|
77
|
+
</html>
|
|
78
|
+
`
|
|
79
|
+
}
|
|
80
|
+
style={{
|
|
81
|
+
width: "100%",
|
|
82
|
+
height: "100%",
|
|
83
|
+
border: "none",
|
|
84
|
+
borderRadius: "7px",
|
|
85
|
+
pointerEvents: resizing ? "none" : "auto",
|
|
86
|
+
background: "#14141F",
|
|
87
|
+
}}
|
|
88
|
+
/>
|
|
89
|
+
)}
|
|
90
|
+
</ResizablePane>
|
|
91
|
+
</div>
|
|
92
|
+
);
|
|
93
|
+
};
|
package/src/provider.tsx
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { DevToolsContextProvider } from "@refinedev/devtools-shared";
|
|
3
|
+
|
|
4
|
+
export const DevtoolsProvider =
|
|
5
|
+
__DEV_CONDITION__ !== "development"
|
|
6
|
+
? ({ children }: React.PropsWithChildren) => children as any
|
|
7
|
+
: ({ children }: React.PropsWithChildren) => {
|
|
8
|
+
return (
|
|
9
|
+
<DevToolsContextProvider>{children}</DevToolsContextProvider>
|
|
10
|
+
);
|
|
11
|
+
};
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { Placement } from "src/interfaces/placement";
|
|
2
|
+
|
|
3
|
+
export const getPanelToggleTransforms = (visible: boolean) => {
|
|
4
|
+
return visible ? "scaleX(1) translateY(0)" : `scaleX(0) translateY(25vw)`;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const SIZE = 50;
|
|
8
|
+
export const BUFFER = 10;
|
|
9
|
+
|
|
10
|
+
const PREFERRED_DEFAULT_WIDTH = () =>
|
|
11
|
+
typeof window !== "undefined" ? window.innerWidth * 0.7 : 1440 * 0.7; // 70% of window width
|
|
12
|
+
const PREFERRED_DEFAULT_HEIGHT = () =>
|
|
13
|
+
typeof window !== "undefined" ? window.innerHeight * 0.7 : 900 * 0.7; // 70% of window height
|
|
14
|
+
|
|
15
|
+
export const MIN_PANEL_WIDTH = 640;
|
|
16
|
+
export const MIN_PANEL_HEIGHT = 360;
|
|
17
|
+
|
|
18
|
+
const verticalCenterTransform = `translateY(calc((100vh - ${SIZE}px) / 2))`;
|
|
19
|
+
const horizontalCenterTransform = `translateX(calc((100vw - ${
|
|
20
|
+
SIZE * 2
|
|
21
|
+
}px) / 2))`;
|
|
22
|
+
const rightAlignTransform = `translateX(calc((100vw - ${SIZE}px) - ${BUFFER}px))`;
|
|
23
|
+
const leftAlignTransform = `translateX(${BUFFER}px)`;
|
|
24
|
+
const topAlignTransform = `translateY(${BUFFER}px)`;
|
|
25
|
+
const bottomAlignTransform = `translateY(calc((100vh - ${SIZE}px) - ${0}px))`;
|
|
26
|
+
|
|
27
|
+
export const getPinTransform = (placement: Placement) => {
|
|
28
|
+
switch (placement) {
|
|
29
|
+
case "left":
|
|
30
|
+
return `${leftAlignTransform} ${verticalCenterTransform}`;
|
|
31
|
+
case "right":
|
|
32
|
+
return `${rightAlignTransform} ${verticalCenterTransform}`;
|
|
33
|
+
case "top":
|
|
34
|
+
return `${topAlignTransform} ${horizontalCenterTransform}`;
|
|
35
|
+
default:
|
|
36
|
+
case "bottom":
|
|
37
|
+
return `${bottomAlignTransform} ${horizontalCenterTransform}`;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const getPinButtonTransform = (hover?: boolean) => {
|
|
42
|
+
return `translateY(${hover ? "0" : "50%"})`;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const getPanelPosition = (placement: Placement) => {
|
|
46
|
+
switch (placement) {
|
|
47
|
+
case "left":
|
|
48
|
+
return {
|
|
49
|
+
left: `calc(${SIZE}px + ${BUFFER}px)`,
|
|
50
|
+
top: "50%",
|
|
51
|
+
transform: "translateY(-50%)",
|
|
52
|
+
};
|
|
53
|
+
case "right":
|
|
54
|
+
return {
|
|
55
|
+
right: `calc(${SIZE}px + ${BUFFER}px)`,
|
|
56
|
+
top: "50%",
|
|
57
|
+
transform: "translateY(-50%)",
|
|
58
|
+
};
|
|
59
|
+
case "top":
|
|
60
|
+
return {
|
|
61
|
+
left: "50%",
|
|
62
|
+
top: `calc(${SIZE}px + ${BUFFER}px)`,
|
|
63
|
+
transform: "translateX(-50%)",
|
|
64
|
+
};
|
|
65
|
+
default:
|
|
66
|
+
case "bottom":
|
|
67
|
+
return {
|
|
68
|
+
left: "50%",
|
|
69
|
+
bottom: `calc(${SIZE}px + ${BUFFER}px)`,
|
|
70
|
+
transform: "translateX(-50%)",
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const getMaxPanelWidth = (placement: Placement) => {
|
|
76
|
+
switch (placement) {
|
|
77
|
+
case "left":
|
|
78
|
+
case "right":
|
|
79
|
+
return (
|
|
80
|
+
-BUFFER -
|
|
81
|
+
SIZE -
|
|
82
|
+
BUFFER +
|
|
83
|
+
(typeof window !== "undefined" ? window.innerWidth : 1440) -
|
|
84
|
+
BUFFER
|
|
85
|
+
);
|
|
86
|
+
case "top":
|
|
87
|
+
case "bottom":
|
|
88
|
+
return (
|
|
89
|
+
-BUFFER +
|
|
90
|
+
(typeof window !== "undefined" ? window.innerWidth : 1440) -
|
|
91
|
+
BUFFER
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export const getMaxPanelHeight = (placement: Placement) => {
|
|
97
|
+
switch (placement) {
|
|
98
|
+
case "left":
|
|
99
|
+
case "right":
|
|
100
|
+
return (
|
|
101
|
+
-BUFFER +
|
|
102
|
+
(typeof window !== "undefined" ? window.innerHeight : 900) -
|
|
103
|
+
BUFFER
|
|
104
|
+
);
|
|
105
|
+
case "top":
|
|
106
|
+
case "bottom":
|
|
107
|
+
return (
|
|
108
|
+
-BUFFER -
|
|
109
|
+
SIZE -
|
|
110
|
+
BUFFER +
|
|
111
|
+
(typeof window !== "undefined" ? window.innerHeight : 900) -
|
|
112
|
+
BUFFER
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export const getDefaultPanelSize = (
|
|
118
|
+
placement: Placement,
|
|
119
|
+
preferredSize?: { width: number; height: number },
|
|
120
|
+
): { width: number; height: number } => {
|
|
121
|
+
const defaultPreferred = {
|
|
122
|
+
width: PREFERRED_DEFAULT_WIDTH(),
|
|
123
|
+
height: PREFERRED_DEFAULT_HEIGHT(),
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const maxPanelWidth = getMaxPanelWidth(placement);
|
|
127
|
+
const maxPanelHeight = getMaxPanelHeight(placement);
|
|
128
|
+
|
|
129
|
+
const width = Math.min(
|
|
130
|
+
maxPanelWidth,
|
|
131
|
+
(preferredSize ?? defaultPreferred).width,
|
|
132
|
+
);
|
|
133
|
+
const height = Math.min(
|
|
134
|
+
maxPanelHeight,
|
|
135
|
+
(preferredSize ?? defaultPreferred).height,
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
width: width,
|
|
140
|
+
height: height,
|
|
141
|
+
};
|
|
142
|
+
};
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import debounce from "lodash/debounce";
|
|
3
|
+
import {
|
|
4
|
+
getElementFromFiber,
|
|
5
|
+
getFiberFromElement,
|
|
6
|
+
getFirstFiberHasName,
|
|
7
|
+
getFirstStateNodeFiber,
|
|
8
|
+
getNameFromFiber,
|
|
9
|
+
getParentOfFiber,
|
|
10
|
+
} from "@aliemir/dom-to-fiber-utils";
|
|
11
|
+
|
|
12
|
+
type Fiber = Exclude<ReturnType<typeof getFiberFromElement>, null>;
|
|
13
|
+
|
|
14
|
+
export const useSelector = (active: boolean) => {
|
|
15
|
+
const [traceItems, setTraceItems] = React.useState<string[]>([]);
|
|
16
|
+
|
|
17
|
+
React.useEffect(() => {
|
|
18
|
+
if (active) {
|
|
19
|
+
fetch("http://localhost:5001/api/unique-trace-items").then((res) =>
|
|
20
|
+
res
|
|
21
|
+
.json()
|
|
22
|
+
.then((data: { data: string[] }) =>
|
|
23
|
+
setTraceItems(data.data),
|
|
24
|
+
),
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
}, [active]);
|
|
28
|
+
|
|
29
|
+
const [selectedFiber, setSelectedFiber] = React.useState<{
|
|
30
|
+
stateNode: Fiber | null;
|
|
31
|
+
nameFiber: Fiber | null;
|
|
32
|
+
}>({
|
|
33
|
+
stateNode: null,
|
|
34
|
+
nameFiber: null,
|
|
35
|
+
});
|
|
36
|
+
const [activeFiber, setActiveFiber] = React.useState<{
|
|
37
|
+
stateNode: Fiber | null;
|
|
38
|
+
nameFiber: Fiber | null;
|
|
39
|
+
derived?: boolean;
|
|
40
|
+
}>({
|
|
41
|
+
stateNode: null,
|
|
42
|
+
nameFiber: null,
|
|
43
|
+
derived: false,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
React.useEffect(() => {
|
|
47
|
+
if (active) {
|
|
48
|
+
return () => {
|
|
49
|
+
setSelectedFiber({
|
|
50
|
+
stateNode: null,
|
|
51
|
+
nameFiber: null,
|
|
52
|
+
});
|
|
53
|
+
setActiveFiber({
|
|
54
|
+
stateNode: null,
|
|
55
|
+
nameFiber: null,
|
|
56
|
+
derived: false,
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return () => 0;
|
|
62
|
+
}, [active]);
|
|
63
|
+
|
|
64
|
+
const selectAppropriateFiber = React.useCallback(
|
|
65
|
+
(start: Fiber | null) => {
|
|
66
|
+
let fiber = start;
|
|
67
|
+
let firstParentOfNodeWithName: Fiber | null;
|
|
68
|
+
let fiberWithStateNode: Fiber | null;
|
|
69
|
+
|
|
70
|
+
let acceptedName = false;
|
|
71
|
+
|
|
72
|
+
while (!acceptedName && fiber) {
|
|
73
|
+
// Get the first fiber node that has a name (look up the tree)
|
|
74
|
+
firstParentOfNodeWithName = getFirstFiberHasName(fiber);
|
|
75
|
+
// Get the first fiber node that has a state node (look up the tree)
|
|
76
|
+
fiberWithStateNode = getFirstStateNodeFiber(
|
|
77
|
+
firstParentOfNodeWithName,
|
|
78
|
+
);
|
|
79
|
+
acceptedName = traceItems.includes(
|
|
80
|
+
getNameFromFiber(firstParentOfNodeWithName) ?? "",
|
|
81
|
+
);
|
|
82
|
+
if (!acceptedName) {
|
|
83
|
+
fiber = getParentOfFiber(fiber);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (fiberWithStateNode && firstParentOfNodeWithName) {
|
|
88
|
+
return {
|
|
89
|
+
stateNode: fiberWithStateNode,
|
|
90
|
+
nameFiber: firstParentOfNodeWithName,
|
|
91
|
+
};
|
|
92
|
+
} else {
|
|
93
|
+
return {
|
|
94
|
+
stateNode: null,
|
|
95
|
+
nameFiber: null,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
[traceItems],
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
const pickFiber = React.useCallback(
|
|
103
|
+
(target: HTMLElement) => {
|
|
104
|
+
const fiber = getFiberFromElement(target);
|
|
105
|
+
|
|
106
|
+
setSelectedFiber(selectAppropriateFiber(fiber));
|
|
107
|
+
return;
|
|
108
|
+
},
|
|
109
|
+
[traceItems],
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
React.useEffect(() => {
|
|
113
|
+
if (
|
|
114
|
+
activeFiber.stateNode !== selectedFiber.stateNode ||
|
|
115
|
+
activeFiber.nameFiber !== selectedFiber.nameFiber
|
|
116
|
+
) {
|
|
117
|
+
setActiveFiber({
|
|
118
|
+
stateNode: selectedFiber.stateNode,
|
|
119
|
+
nameFiber: selectedFiber.nameFiber,
|
|
120
|
+
derived: false,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}, [selectedFiber]);
|
|
124
|
+
|
|
125
|
+
const previousValues = React.useRef<{
|
|
126
|
+
rect: {
|
|
127
|
+
width: number;
|
|
128
|
+
height: number;
|
|
129
|
+
x: number;
|
|
130
|
+
y: number;
|
|
131
|
+
};
|
|
132
|
+
name: string;
|
|
133
|
+
}>({
|
|
134
|
+
rect: {
|
|
135
|
+
width: 0,
|
|
136
|
+
height: 0,
|
|
137
|
+
x: 0,
|
|
138
|
+
y: 0,
|
|
139
|
+
},
|
|
140
|
+
name: "",
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const { rect, name } = React.useMemo(() => {
|
|
144
|
+
if (activeFiber.stateNode || activeFiber.nameFiber) {
|
|
145
|
+
// Get the element from the fiber with a state node
|
|
146
|
+
const element = activeFiber.stateNode
|
|
147
|
+
? getElementFromFiber(activeFiber.stateNode)
|
|
148
|
+
: null;
|
|
149
|
+
// Get the name of the fiber node with a name
|
|
150
|
+
const fiberName = activeFiber.nameFiber
|
|
151
|
+
? getNameFromFiber(activeFiber.nameFiber)
|
|
152
|
+
: null;
|
|
153
|
+
|
|
154
|
+
if (!element) {
|
|
155
|
+
return {
|
|
156
|
+
rect: previousValues.current.rect,
|
|
157
|
+
name: fiberName ?? previousValues.current.name,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const bounding = element.getBoundingClientRect?.();
|
|
162
|
+
|
|
163
|
+
if (!bounding) {
|
|
164
|
+
return {
|
|
165
|
+
rect: previousValues.current.rect,
|
|
166
|
+
name: fiberName ?? previousValues.current.name,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
rect: {
|
|
172
|
+
width: bounding.width,
|
|
173
|
+
height: bounding.height,
|
|
174
|
+
x: bounding.left,
|
|
175
|
+
y: bounding.top,
|
|
176
|
+
},
|
|
177
|
+
name: fiberName ?? previousValues.current.name,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return previousValues.current;
|
|
182
|
+
}, [activeFiber]);
|
|
183
|
+
|
|
184
|
+
previousValues.current = {
|
|
185
|
+
rect,
|
|
186
|
+
name,
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
React.useEffect(() => {
|
|
190
|
+
if (active) {
|
|
191
|
+
const listener = (e: KeyboardEvent) => {
|
|
192
|
+
// if user presses shift, toggle the derived state and set the active fiber to the parent
|
|
193
|
+
if (e.key === "Shift" && activeFiber.stateNode) {
|
|
194
|
+
e.stopPropagation();
|
|
195
|
+
e.preventDefault();
|
|
196
|
+
|
|
197
|
+
const parent = getParentOfFiber(activeFiber.nameFiber);
|
|
198
|
+
|
|
199
|
+
const fibers = selectAppropriateFiber(parent);
|
|
200
|
+
|
|
201
|
+
if (fibers.nameFiber && fibers.stateNode) {
|
|
202
|
+
setActiveFiber({
|
|
203
|
+
...fibers,
|
|
204
|
+
derived: true,
|
|
205
|
+
});
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
document.addEventListener("keydown", listener);
|
|
212
|
+
return () => document.removeEventListener("keydown", listener);
|
|
213
|
+
}
|
|
214
|
+
return () => 0;
|
|
215
|
+
}, [activeFiber, active]);
|
|
216
|
+
|
|
217
|
+
React.useEffect(() => {
|
|
218
|
+
if (active) {
|
|
219
|
+
let previousTarget: HTMLElement | null = null;
|
|
220
|
+
const listener = debounce((e: MouseEvent) => {
|
|
221
|
+
if (e?.target) {
|
|
222
|
+
if (previousTarget === e.target) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
pickFiber(e.target as HTMLElement);
|
|
226
|
+
previousTarget = e.target as HTMLElement;
|
|
227
|
+
}
|
|
228
|
+
}, 30);
|
|
229
|
+
|
|
230
|
+
document.addEventListener("mousemove", listener);
|
|
231
|
+
|
|
232
|
+
return () => document.removeEventListener("mousemove", listener);
|
|
233
|
+
} else {
|
|
234
|
+
return () => 0;
|
|
235
|
+
}
|
|
236
|
+
}, [active, pickFiber]);
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
rect,
|
|
240
|
+
name,
|
|
241
|
+
};
|
|
242
|
+
};
|