react-native-grab 0.0.1 → 0.0.3
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/README.md +5 -5
- package/dist/cjs/metro/withReactNativeGrab.js +31 -31
- package/dist/cjs/metro/withReactNativeGrab.js.map +1 -1
- package/dist/cjs/react-native/copy-payload.js +65 -69
- package/dist/cjs/react-native/copy-payload.js.map +1 -1
- package/dist/cjs/react-native/dom-traversal.js +178 -129
- package/dist/cjs/react-native/dom-traversal.js.map +1 -1
- package/dist/cjs/react-native/fiber.js +16 -16
- package/dist/cjs/react-native/fiber.js.map +1 -1
- package/dist/cjs/react-native/get-dev-server.js +9 -0
- package/dist/cjs/react-native/get-dev-server.js.map +1 -0
- package/dist/cjs/react-native/index.js +349 -249
- package/dist/cjs/react-native/index.js.map +1 -1
- package/dist/cjs/react-native/index.web.js +8 -0
- package/dist/cjs/react-native/index.web.js.map +1 -0
- package/dist/cjs/react-native/pointer-events.js +25 -28
- package/dist/cjs/react-native/pointer-events.js.map +1 -1
- package/dist/cjs/react-native/selection-trigger.js +1 -1
- package/dist/cjs/react-native/settings.js +7 -10
- package/dist/cjs/react-native/settings.js.map +1 -1
- package/dist/cjs/react-native/symbolicate.js.map +1 -1
- package/dist/cjs/react-native/types.js +0 -4
- package/dist/cjs/react-native/types.js.map +1 -1
- package/dist/cjs/react-native/z-index.js +10 -2
- package/dist/cjs/react-native/z-index.js.map +1 -1
- package/dist/esm/metro/index.js +1 -1
- package/dist/esm/metro/withReactNativeGrab.js +33 -33
- package/dist/esm/metro/withReactNativeGrab.js.map +1 -1
- package/dist/esm/react-native/copy-payload.js +67 -71
- package/dist/esm/react-native/copy-payload.js.map +1 -1
- package/dist/esm/react-native/dom-traversal.js +181 -130
- package/dist/esm/react-native/dom-traversal.js.map +1 -1
- package/dist/esm/react-native/fiber.js +17 -17
- package/dist/esm/react-native/fiber.js.map +1 -1
- package/dist/esm/react-native/get-dev-server.js +3 -0
- package/dist/esm/react-native/get-dev-server.js.map +1 -0
- package/dist/esm/react-native/index.js +354 -251
- package/dist/esm/react-native/index.js.map +1 -1
- package/dist/esm/react-native/index.web.js +4 -0
- package/dist/esm/react-native/index.web.js.map +1 -0
- package/dist/esm/react-native/pointer-events.js +26 -29
- package/dist/esm/react-native/pointer-events.js.map +1 -1
- package/dist/esm/react-native/selection-trigger.js +1 -1
- package/dist/esm/react-native/settings.js +6 -6
- package/dist/esm/react-native/settings.js.map +1 -1
- package/dist/esm/react-native/symbolicate.js +2 -2
- package/dist/esm/react-native/symbolicate.js.map +1 -1
- package/dist/esm/react-native/types.js +0 -4
- package/dist/esm/react-native/types.js.map +1 -1
- package/dist/esm/react-native/z-index.js +11 -3
- package/dist/esm/react-native/z-index.js.map +1 -1
- package/dist/types/metro/index.d.ts +1 -1
- package/dist/types/metro/withReactNativeGrab.d.ts.map +1 -1
- package/dist/types/react-native/copy-payload.d.ts +1 -1
- package/dist/types/react-native/copy-payload.d.ts.map +1 -1
- package/dist/types/react-native/dom-traversal.d.ts +19 -6
- package/dist/types/react-native/dom-traversal.d.ts.map +1 -1
- package/dist/types/react-native/fiber.d.ts +1 -1
- package/dist/types/react-native/fiber.d.ts.map +1 -1
- package/dist/types/react-native/get-dev-server.d.ts +3 -0
- package/dist/types/react-native/get-dev-server.d.ts.map +1 -0
- package/dist/types/react-native/index.d.ts +1 -1
- package/dist/types/react-native/index.d.ts.map +1 -1
- package/dist/types/react-native/index.web.d.ts +2 -0
- package/dist/types/react-native/index.web.d.ts.map +1 -0
- package/dist/types/react-native/pointer-events.d.ts +2 -2
- package/dist/types/react-native/pointer-events.d.ts.map +1 -1
- package/dist/types/react-native/settings.d.ts.map +1 -1
- package/dist/types/react-native/symbolicate.d.ts +1 -1
- package/dist/types/react-native/symbolicate.d.ts.map +1 -1
- package/dist/types/react-native/types.d.ts +3 -11
- package/dist/types/react-native/types.d.ts.map +1 -1
- package/dist/types/react-native/z-index.d.ts +1 -1
- package/dist/types/react-native/z-index.d.ts.map +1 -1
- package/package.json +26 -26
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.ElementInspector = void 0;
|
|
7
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
5
|
const react_1 = require("react");
|
|
9
6
|
const react_native_1 = require("react-native");
|
|
10
|
-
const
|
|
7
|
+
const get_dev_server_1 = require("./get-dev-server");
|
|
11
8
|
const react_native_screens_1 = require("react-native-screens");
|
|
12
9
|
const copy_payload_1 = require("./copy-payload");
|
|
13
10
|
const dom_traversal_1 = require("./dom-traversal");
|
|
@@ -17,29 +14,22 @@ const SETTLE_MS = 80;
|
|
|
17
14
|
const MOVEMENT_EPSILON_PX = 8;
|
|
18
15
|
const MAX_STACK_LINES = 3;
|
|
19
16
|
const MAX_PREVIEW_TEXT = 120;
|
|
20
|
-
const INSPECTOR_COPY_ENDPOINT =
|
|
17
|
+
const INSPECTOR_COPY_ENDPOINT = "/__react-native-grab/copy";
|
|
21
18
|
const FAB_SIZE = 56;
|
|
22
19
|
const FAB_SCREEN_PADDING = 8;
|
|
23
20
|
const FAB_DEFAULT_RIGHT_OFFSET = 20;
|
|
24
21
|
const FAB_DEFAULT_BOTTOM_OFFSET = 32;
|
|
25
22
|
const getMetroBaseUrl = () => {
|
|
26
|
-
const devServer = (0,
|
|
23
|
+
const devServer = (0, get_dev_server_1.getDevServer)();
|
|
27
24
|
if (!devServer?.url)
|
|
28
25
|
return null;
|
|
29
|
-
return devServer.url.replace(/\/$/,
|
|
26
|
+
return devServer.url.replace(/\/$/, "");
|
|
30
27
|
};
|
|
31
|
-
const ElementInspector = () => {
|
|
32
|
-
if (react_native_1.Platform.OS === 'web') {
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
|
-
return (0, jsx_runtime_1.jsx)(ElementInspectorImpl, {});
|
|
36
|
-
};
|
|
37
|
-
exports.ElementInspector = ElementInspector;
|
|
38
28
|
const getInitialFabPosition = () => {
|
|
39
|
-
const { width } = react_native_1.Dimensions.get(
|
|
29
|
+
const { width, height } = react_native_1.Dimensions.get("window");
|
|
40
30
|
return {
|
|
41
31
|
x: Math.max(FAB_SCREEN_PADDING, width - FAB_DEFAULT_RIGHT_OFFSET - FAB_SIZE),
|
|
42
|
-
y: Math.max(FAB_SCREEN_PADDING, FAB_DEFAULT_BOTTOM_OFFSET),
|
|
32
|
+
y: Math.max(FAB_SCREEN_PADDING, height - FAB_DEFAULT_BOTTOM_OFFSET - FAB_SIZE),
|
|
43
33
|
};
|
|
44
34
|
};
|
|
45
35
|
const clampFabPosition = (x, y, windowWidth, windowHeight) => {
|
|
@@ -50,190 +40,153 @@ const clampFabPosition = (x, y, windowWidth, windowHeight) => {
|
|
|
50
40
|
y: Math.min(Math.max(y, FAB_SCREEN_PADDING), maxY),
|
|
51
41
|
};
|
|
52
42
|
};
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
const settleTimeoutRef = (0, react_1.useRef)(null);
|
|
68
|
-
const stateRef = (0, react_1.useRef)(state);
|
|
69
|
-
stateRef.current = state;
|
|
70
|
-
const selectingHintAnim = (0, react_1.useRef)(new react_native_1.Animated.Value(0)).current;
|
|
71
|
-
const toastAnim = (0, react_1.useRef)(new react_native_1.Animated.Value(0)).current;
|
|
72
|
-
const fabAppearAnim = (0, react_1.useRef)(new react_native_1.Animated.Value(1)).current;
|
|
73
|
-
const fabPressAnim = (0, react_1.useRef)(new react_native_1.Animated.Value(1)).current;
|
|
74
|
-
const fabPosition = (0, react_1.useRef)(new react_native_1.Animated.ValueXY(getInitialFabPosition())).current;
|
|
75
|
-
const fabPositionRef = (0, react_1.useRef)(getInitialFabPosition());
|
|
76
|
-
const fabDragStartOffsetRef = (0, react_1.useRef)({ x: 0, y: 0 });
|
|
77
|
-
const windowSizeRef = (0, react_1.useRef)({ width: windowWidth, height: windowHeight });
|
|
78
|
-
windowSizeRef.current = { width: windowWidth, height: windowHeight };
|
|
79
|
-
const skipRefs = [inspectorRootRef, overlayRef];
|
|
80
|
-
const idle = state.status === 'idle';
|
|
81
|
-
const selecting = state.status === 'selecting';
|
|
82
|
-
const overlayVisible = state.status !== 'idle';
|
|
83
|
-
const activeHighlightRect = state.status === 'copying' ? state.selectedRect : highlightRect;
|
|
84
|
-
const startSelecting = () => {
|
|
85
|
-
selectedHitTargetRef.current = null;
|
|
86
|
-
setState({ status: 'selecting' });
|
|
87
|
-
};
|
|
88
|
-
const performQuery = (x, y, offset) => {
|
|
89
|
-
const appY = y - statusBarOffset;
|
|
90
|
-
const hitTarget = (0, dom_traversal_1.findHitTargetAtPoint)(anchorRef, skipRefs, x, appY, react_native_1.Platform.OS === 'ios'
|
|
43
|
+
const useLatestRef = (value) => {
|
|
44
|
+
const ref = (0, react_1.useRef)(value);
|
|
45
|
+
ref.current = value;
|
|
46
|
+
return ref;
|
|
47
|
+
};
|
|
48
|
+
const useTraversalSelectionSession = ({ anchorRef, inspectorRootRef, overlayRef, statusBarOffset, setHighlightRect, }) => {
|
|
49
|
+
const traversalSessionRef = (0, react_1.useRef)(null);
|
|
50
|
+
const skipRefsRef = (0, react_1.useRef)([inspectorRootRef, overlayRef]);
|
|
51
|
+
const stopSession = (0, react_1.useCallback)(() => {
|
|
52
|
+
traversalSessionRef.current?.stop();
|
|
53
|
+
traversalSessionRef.current = null;
|
|
54
|
+
}, []);
|
|
55
|
+
const startSession = (0, react_1.useCallback)(() => {
|
|
56
|
+
const skipPredicate = react_native_1.Platform.OS === "ios"
|
|
91
57
|
? (node) => {
|
|
92
58
|
const inspectorRootNode = inspectorRootRef.current;
|
|
93
59
|
if (!inspectorRootNode)
|
|
94
60
|
return false;
|
|
95
61
|
return node === inspectorRootNode.parentElement;
|
|
96
62
|
}
|
|
97
|
-
: undefined
|
|
63
|
+
: undefined;
|
|
64
|
+
stopSession();
|
|
65
|
+
traversalSessionRef.current = (0, dom_traversal_1.startSession)({
|
|
66
|
+
anchorRef,
|
|
67
|
+
skipRefs: skipRefsRef.current,
|
|
68
|
+
offset: statusBarOffset,
|
|
69
|
+
skipPredicate,
|
|
70
|
+
});
|
|
71
|
+
setHighlightRect(null);
|
|
72
|
+
}, [anchorRef, inspectorRootRef, setHighlightRect, statusBarOffset, stopSession]);
|
|
73
|
+
const queryAtPoint = (0, react_1.useCallback)((x, y) => {
|
|
74
|
+
const session = traversalSessionRef.current;
|
|
75
|
+
if (!session) {
|
|
76
|
+
setHighlightRect(null);
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
const hitTarget = session.move({
|
|
80
|
+
x,
|
|
81
|
+
y: y - statusBarOffset,
|
|
82
|
+
});
|
|
98
83
|
setHighlightRect(hitTarget?.rect ?? null);
|
|
99
84
|
return hitTarget;
|
|
85
|
+
}, [setHighlightRect, statusBarOffset]);
|
|
86
|
+
(0, react_1.useEffect)(() => {
|
|
87
|
+
return () => {
|
|
88
|
+
stopSession();
|
|
89
|
+
};
|
|
90
|
+
}, [stopSession]);
|
|
91
|
+
return {
|
|
92
|
+
startSession,
|
|
93
|
+
queryAtPoint,
|
|
94
|
+
stopSession,
|
|
100
95
|
};
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const
|
|
96
|
+
};
|
|
97
|
+
const useSelectionGesture = ({ isSelecting, queryAtPoint, onHitTarget, onCancel, }) => {
|
|
98
|
+
const isSelectingRef = useLatestRef(isSelecting);
|
|
99
|
+
const queryAtPointRef = useLatestRef(queryAtPoint);
|
|
100
|
+
const onHitTargetRef = useLatestRef(onHitTarget);
|
|
101
|
+
const onCancelRef = useLatestRef(onCancel);
|
|
102
|
+
const settleTimeoutRef = (0, react_1.useRef)(null);
|
|
103
|
+
const lastQueryPositionRef = (0, react_1.useRef)(null);
|
|
104
|
+
const movePositionRef = (0, react_1.useRef)({ x: 0, y: 0 });
|
|
105
|
+
const clearSettleTimeout = (0, react_1.useCallback)(() => {
|
|
104
106
|
if (settleTimeoutRef.current != null) {
|
|
105
107
|
clearTimeout(settleTimeoutRef.current);
|
|
106
108
|
settleTimeoutRef.current = null;
|
|
107
109
|
}
|
|
108
|
-
selectedHitTargetRef.current = null;
|
|
109
|
-
lastQueryPositionRef.current = null;
|
|
110
|
-
setState({ status: 'idle' });
|
|
111
|
-
setHighlightRect(null);
|
|
112
|
-
};
|
|
113
|
-
const closeOverlay = () => {
|
|
114
|
-
if (isClosingRef.current)
|
|
115
|
-
return;
|
|
116
|
-
isClosingRef.current = true;
|
|
117
|
-
closeOverlayImmediately();
|
|
118
|
-
isClosingRef.current = false;
|
|
119
|
-
};
|
|
120
|
-
const showCopiedToast = () => {
|
|
121
|
-
setToastVisible(true);
|
|
122
|
-
toastAnim.setValue(0);
|
|
123
|
-
react_native_1.Animated.sequence([
|
|
124
|
-
react_native_1.Animated.timing(toastAnim, {
|
|
125
|
-
toValue: 1,
|
|
126
|
-
duration: 140,
|
|
127
|
-
useNativeDriver: true,
|
|
128
|
-
}),
|
|
129
|
-
react_native_1.Animated.delay(700),
|
|
130
|
-
react_native_1.Animated.timing(toastAnim, {
|
|
131
|
-
toValue: 0,
|
|
132
|
-
duration: 180,
|
|
133
|
-
useNativeDriver: true,
|
|
134
|
-
}),
|
|
135
|
-
]).start(() => setToastVisible(false));
|
|
136
|
-
};
|
|
137
|
-
const copyFeedbackToHostClipboard = async (text) => {
|
|
138
|
-
const baseUrl = getMetroBaseUrl();
|
|
139
|
-
if (!baseUrl)
|
|
140
|
-
return false;
|
|
141
|
-
try {
|
|
142
|
-
const response = await fetch(`${baseUrl}${INSPECTOR_COPY_ENDPOINT}`, {
|
|
143
|
-
method: 'POST',
|
|
144
|
-
headers: { 'Content-Type': 'application/json' },
|
|
145
|
-
body: JSON.stringify({ text }),
|
|
146
|
-
});
|
|
147
|
-
return response.ok;
|
|
148
|
-
}
|
|
149
|
-
catch {
|
|
150
|
-
return false;
|
|
151
|
-
}
|
|
152
|
-
};
|
|
153
|
-
(0, react_1.useEffect)(() => {
|
|
154
|
-
return (0, settings_1.subscribeInspectorSettings)(() => {
|
|
155
|
-
setSelectionPillVisible((0, settings_1.getInspectorSettings)().selectionPillVisible);
|
|
156
|
-
});
|
|
157
|
-
}, []);
|
|
158
|
-
(0, react_1.useEffect)(() => {
|
|
159
|
-
return (0, selection_trigger_1.subscribeInspectorSelectionRequest)(() => {
|
|
160
|
-
setHighlightRect(null);
|
|
161
|
-
startSelecting();
|
|
162
|
-
});
|
|
163
|
-
}, []);
|
|
164
|
-
(0, react_1.useEffect)(() => {
|
|
165
|
-
void (0, settings_1.initializeInspectorSettings)();
|
|
166
110
|
}, []);
|
|
167
|
-
(0, react_1.
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}, [fabPosition, windowHeight, windowWidth]);
|
|
111
|
+
const resetGestureState = (0, react_1.useCallback)(() => {
|
|
112
|
+
clearSettleTimeout();
|
|
113
|
+
lastQueryPositionRef.current = null;
|
|
114
|
+
}, [clearSettleTimeout]);
|
|
172
115
|
const panResponder = (0, react_1.useRef)(react_native_1.PanResponder.create({
|
|
173
116
|
onStartShouldSetPanResponder: () => true,
|
|
174
117
|
onMoveShouldSetPanResponder: () => true,
|
|
175
118
|
onPanResponderGrant: (evt) => {
|
|
176
|
-
if (
|
|
119
|
+
if (!isSelectingRef.current)
|
|
177
120
|
return;
|
|
178
121
|
const x = evt.nativeEvent.locationX;
|
|
179
122
|
const y = evt.nativeEvent.locationY;
|
|
180
|
-
|
|
123
|
+
queryAtPointRef.current(x, y);
|
|
181
124
|
lastQueryPositionRef.current = { x, y };
|
|
182
125
|
},
|
|
183
126
|
onPanResponderMove: (evt) => {
|
|
184
|
-
if (
|
|
127
|
+
if (!isSelectingRef.current)
|
|
185
128
|
return;
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
settleTimeoutRef.current = null;
|
|
192
|
-
}
|
|
129
|
+
movePositionRef.current = {
|
|
130
|
+
x: evt.nativeEvent.locationX,
|
|
131
|
+
y: evt.nativeEvent.locationY,
|
|
132
|
+
};
|
|
133
|
+
clearSettleTimeout();
|
|
193
134
|
settleTimeoutRef.current = setTimeout(() => {
|
|
194
135
|
settleTimeoutRef.current = null;
|
|
195
|
-
if (
|
|
136
|
+
if (!isSelectingRef.current)
|
|
196
137
|
return;
|
|
197
138
|
const pos = movePositionRef.current;
|
|
198
139
|
const last = lastQueryPositionRef.current;
|
|
199
|
-
const distance = last
|
|
200
|
-
? Math.hypot(pos.x - last.x, pos.y - last.y)
|
|
201
|
-
: Infinity;
|
|
140
|
+
const distance = last ? Math.hypot(pos.x - last.x, pos.y - last.y) : Infinity;
|
|
202
141
|
if (distance < MOVEMENT_EPSILON_PX)
|
|
203
142
|
return;
|
|
204
143
|
lastQueryPositionRef.current = pos;
|
|
205
|
-
|
|
144
|
+
queryAtPointRef.current(pos.x, pos.y);
|
|
206
145
|
}, SETTLE_MS);
|
|
207
146
|
},
|
|
208
147
|
onPanResponderRelease: (evt) => {
|
|
209
|
-
if (
|
|
148
|
+
if (!isSelectingRef.current)
|
|
210
149
|
return;
|
|
211
|
-
const hitTarget =
|
|
150
|
+
const hitTarget = queryAtPointRef.current(evt.nativeEvent.locationX, evt.nativeEvent.locationY);
|
|
212
151
|
if (!hitTarget) {
|
|
213
|
-
|
|
152
|
+
onCancelRef.current();
|
|
214
153
|
return;
|
|
215
154
|
}
|
|
216
|
-
|
|
217
|
-
setState({ status: 'copying', selectedRect: hitTarget.rect });
|
|
218
|
-
void (async () => {
|
|
219
|
-
const clipboardText = await (0, copy_payload_1.buildInspectorCopyPayload)(hitTarget, {
|
|
220
|
-
maxStackLines: MAX_STACK_LINES,
|
|
221
|
-
maxPreviewText: MAX_PREVIEW_TEXT,
|
|
222
|
-
pathRootHint: null,
|
|
223
|
-
});
|
|
224
|
-
const copied = await copyFeedbackToHostClipboard(clipboardText);
|
|
225
|
-
if (copied) {
|
|
226
|
-
showCopiedToast();
|
|
227
|
-
}
|
|
228
|
-
closeOverlay();
|
|
229
|
-
})();
|
|
155
|
+
onHitTargetRef.current(hitTarget);
|
|
230
156
|
},
|
|
231
157
|
onPanResponderTerminate: () => {
|
|
232
|
-
if (
|
|
233
|
-
|
|
158
|
+
if (isSelectingRef.current) {
|
|
159
|
+
onCancelRef.current();
|
|
234
160
|
}
|
|
235
161
|
},
|
|
236
162
|
})).current;
|
|
163
|
+
(0, react_1.useEffect)(() => {
|
|
164
|
+
return () => {
|
|
165
|
+
clearSettleTimeout();
|
|
166
|
+
};
|
|
167
|
+
}, [clearSettleTimeout]);
|
|
168
|
+
return {
|
|
169
|
+
panHandlers: panResponder.panHandlers,
|
|
170
|
+
resetGestureState,
|
|
171
|
+
};
|
|
172
|
+
};
|
|
173
|
+
const useFabController = (windowWidth, windowHeight) => {
|
|
174
|
+
const initialFabPosition = (0, react_1.useRef)(getInitialFabPosition()).current;
|
|
175
|
+
const fabAppearAnim = (0, react_1.useRef)(new react_native_1.Animated.Value(1)).current;
|
|
176
|
+
const fabPressAnim = (0, react_1.useRef)(new react_native_1.Animated.Value(1)).current;
|
|
177
|
+
const fabPosition = (0, react_1.useRef)(new react_native_1.Animated.ValueXY(initialFabPosition)).current;
|
|
178
|
+
const fabPositionRef = (0, react_1.useRef)(initialFabPosition);
|
|
179
|
+
const fabDragStartOffsetRef = (0, react_1.useRef)({ x: 0, y: 0 });
|
|
180
|
+
const windowSizeRef = (0, react_1.useRef)({ width: windowWidth, height: windowHeight });
|
|
181
|
+
windowSizeRef.current = { width: windowWidth, height: windowHeight };
|
|
182
|
+
const animateFabPress = (0, react_1.useCallback)((toValue) => {
|
|
183
|
+
react_native_1.Animated.spring(fabPressAnim, {
|
|
184
|
+
toValue,
|
|
185
|
+
useNativeDriver: true,
|
|
186
|
+
tension: 220,
|
|
187
|
+
friction: 16,
|
|
188
|
+
}).start();
|
|
189
|
+
}, [fabPressAnim]);
|
|
237
190
|
const fabPanResponder = (0, react_1.useRef)(react_native_1.PanResponder.create({
|
|
238
191
|
onStartShouldSetPanResponder: () => false,
|
|
239
192
|
onMoveShouldSetPanResponder: (_evt, gestureState) => Math.abs(gestureState.dx) > 2 || Math.abs(gestureState.dy) > 2,
|
|
@@ -257,6 +210,142 @@ const ElementInspectorImpl = () => {
|
|
|
257
210
|
fabPressAnim.setValue(1);
|
|
258
211
|
},
|
|
259
212
|
})).current;
|
|
213
|
+
(0, react_1.useEffect)(() => {
|
|
214
|
+
const nextPosition = clampFabPosition(fabPositionRef.current.x, fabPositionRef.current.y, windowWidth, windowHeight);
|
|
215
|
+
fabPositionRef.current = nextPosition;
|
|
216
|
+
fabPosition.setValue(nextPosition);
|
|
217
|
+
}, [fabPosition, windowHeight, windowWidth]);
|
|
218
|
+
return {
|
|
219
|
+
animateFabPress,
|
|
220
|
+
fabAppearAnim,
|
|
221
|
+
fabPanHandlers: fabPanResponder.panHandlers,
|
|
222
|
+
fabPosition,
|
|
223
|
+
fabPressAnim,
|
|
224
|
+
};
|
|
225
|
+
};
|
|
226
|
+
const ElementInspector = () => {
|
|
227
|
+
const { width: windowWidth, height: windowHeight } = (0, react_native_1.useWindowDimensions)();
|
|
228
|
+
const statusBarOffset = react_native_1.Platform.OS === "android" ? (react_native_1.StatusBar.currentHeight ?? 0) : 0;
|
|
229
|
+
const [state, setState] = (0, react_1.useState)({ status: "idle" });
|
|
230
|
+
const [highlightRect, setHighlightRect] = (0, react_1.useState)(null);
|
|
231
|
+
const [toastVisible, setToastVisible] = (0, react_1.useState)(false);
|
|
232
|
+
const [selectionPillVisible, setSelectionPillVisible] = (0, react_1.useState)(() => (0, settings_1.getInspectorSettings)().selectionPillVisible);
|
|
233
|
+
const isClosingRef = (0, react_1.useRef)(false);
|
|
234
|
+
const anchorRef = (0, react_1.useRef)(null);
|
|
235
|
+
const inspectorRootRef = (0, react_1.useRef)(null);
|
|
236
|
+
const overlayRef = (0, react_1.useRef)(null);
|
|
237
|
+
const selectingHintAnim = (0, react_1.useRef)(new react_native_1.Animated.Value(0)).current;
|
|
238
|
+
const toastAnim = (0, react_1.useRef)(new react_native_1.Animated.Value(0)).current;
|
|
239
|
+
const idle = state.status === "idle";
|
|
240
|
+
const selecting = state.status === "selecting";
|
|
241
|
+
const overlayVisible = state.status !== "idle";
|
|
242
|
+
const activeHighlightRect = state.status === "copying" ? state.selectedRect : highlightRect;
|
|
243
|
+
const { animateFabPress, fabAppearAnim, fabPanHandlers, fabPosition, fabPressAnim } = useFabController(windowWidth, windowHeight);
|
|
244
|
+
const { queryAtPoint, startSession, stopSession } = useTraversalSelectionSession({
|
|
245
|
+
anchorRef,
|
|
246
|
+
inspectorRootRef,
|
|
247
|
+
overlayRef,
|
|
248
|
+
statusBarOffset,
|
|
249
|
+
setHighlightRect,
|
|
250
|
+
});
|
|
251
|
+
const closeOverlayImmediately = (0, react_1.useCallback)(() => {
|
|
252
|
+
stopSession();
|
|
253
|
+
setState({ status: "idle" });
|
|
254
|
+
setHighlightRect(null);
|
|
255
|
+
}, [stopSession]);
|
|
256
|
+
const closeOverlay = (0, react_1.useCallback)(() => {
|
|
257
|
+
if (isClosingRef.current)
|
|
258
|
+
return;
|
|
259
|
+
isClosingRef.current = true;
|
|
260
|
+
closeOverlayImmediately();
|
|
261
|
+
isClosingRef.current = false;
|
|
262
|
+
}, [closeOverlayImmediately]);
|
|
263
|
+
const showCopiedToast = (0, react_1.useCallback)(() => {
|
|
264
|
+
setToastVisible(true);
|
|
265
|
+
toastAnim.setValue(0);
|
|
266
|
+
react_native_1.Animated.sequence([
|
|
267
|
+
react_native_1.Animated.timing(toastAnim, {
|
|
268
|
+
toValue: 1,
|
|
269
|
+
duration: 140,
|
|
270
|
+
useNativeDriver: true,
|
|
271
|
+
}),
|
|
272
|
+
react_native_1.Animated.delay(700),
|
|
273
|
+
react_native_1.Animated.timing(toastAnim, {
|
|
274
|
+
toValue: 0,
|
|
275
|
+
duration: 180,
|
|
276
|
+
useNativeDriver: true,
|
|
277
|
+
}),
|
|
278
|
+
]).start(() => setToastVisible(false));
|
|
279
|
+
}, [toastAnim]);
|
|
280
|
+
const copyFeedbackToHostClipboard = (0, react_1.useCallback)(async (text) => {
|
|
281
|
+
const baseUrl = getMetroBaseUrl();
|
|
282
|
+
if (!baseUrl)
|
|
283
|
+
return false;
|
|
284
|
+
try {
|
|
285
|
+
const response = await fetch(`${baseUrl}${INSPECTOR_COPY_ENDPOINT}`, {
|
|
286
|
+
method: "POST",
|
|
287
|
+
headers: { "Content-Type": "application/json" },
|
|
288
|
+
body: JSON.stringify({ text }),
|
|
289
|
+
});
|
|
290
|
+
return response.ok;
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
}, []);
|
|
296
|
+
const handleSelectionHit = (0, react_1.useCallback)((hitTarget) => {
|
|
297
|
+
setState({ status: "copying", selectedRect: hitTarget.rect });
|
|
298
|
+
void (async () => {
|
|
299
|
+
const clipboardText = await (0, copy_payload_1.buildInspectorCopyPayload)(hitTarget, {
|
|
300
|
+
maxStackLines: MAX_STACK_LINES,
|
|
301
|
+
maxPreviewText: MAX_PREVIEW_TEXT,
|
|
302
|
+
pathRootHint: null,
|
|
303
|
+
});
|
|
304
|
+
const copied = await copyFeedbackToHostClipboard(clipboardText);
|
|
305
|
+
if (copied) {
|
|
306
|
+
showCopiedToast();
|
|
307
|
+
}
|
|
308
|
+
closeOverlay();
|
|
309
|
+
})();
|
|
310
|
+
}, [closeOverlay, copyFeedbackToHostClipboard, showCopiedToast]);
|
|
311
|
+
const { panHandlers: selectionPanHandlers, resetGestureState } = useSelectionGesture({
|
|
312
|
+
isSelecting: selecting,
|
|
313
|
+
queryAtPoint,
|
|
314
|
+
onHitTarget: handleSelectionHit,
|
|
315
|
+
onCancel: closeOverlay,
|
|
316
|
+
});
|
|
317
|
+
const startSelecting = (0, react_1.useCallback)(() => {
|
|
318
|
+
resetGestureState();
|
|
319
|
+
startSession();
|
|
320
|
+
setState({ status: "selecting" });
|
|
321
|
+
}, [resetGestureState, startSession]);
|
|
322
|
+
(0, react_1.useEffect)(() => {
|
|
323
|
+
return (0, settings_1.subscribeInspectorSettings)(() => {
|
|
324
|
+
setSelectionPillVisible((0, settings_1.getInspectorSettings)().selectionPillVisible);
|
|
325
|
+
});
|
|
326
|
+
}, []);
|
|
327
|
+
(0, react_1.useEffect)(() => {
|
|
328
|
+
return (0, selection_trigger_1.subscribeInspectorSelectionRequest)(() => {
|
|
329
|
+
startSelecting();
|
|
330
|
+
});
|
|
331
|
+
}, [startSelecting]);
|
|
332
|
+
(0, react_1.useEffect)(() => {
|
|
333
|
+
void (0, settings_1.initializeInspectorSettings)();
|
|
334
|
+
}, []);
|
|
335
|
+
(0, react_1.useEffect)(() => {
|
|
336
|
+
react_native_1.DevSettings.addMenuItem("Toggle React Native Grab", () => {
|
|
337
|
+
void (async () => {
|
|
338
|
+
await (0, settings_1.initializeInspectorSettings)();
|
|
339
|
+
const current = (0, settings_1.getInspectorSettings)().selectionPillVisible;
|
|
340
|
+
console.log(`[InspectorFeedback] Toggle pill visibility: ${current} -> ${!current}`);
|
|
341
|
+
await (0, settings_1.updateInspectorSettings)({ selectionPillVisible: !current });
|
|
342
|
+
})();
|
|
343
|
+
});
|
|
344
|
+
react_native_1.DevSettings.addMenuItem("Start React Native Grab Selection", () => {
|
|
345
|
+
console.log("[InspectorFeedback] Start selection menu item pressed");
|
|
346
|
+
(0, selection_trigger_1.requestInspectorSelection)();
|
|
347
|
+
});
|
|
348
|
+
}, []);
|
|
260
349
|
(0, react_1.useEffect)(() => {
|
|
261
350
|
if (!selecting) {
|
|
262
351
|
selectingHintAnim.stopAnimation();
|
|
@@ -285,76 +374,87 @@ const ElementInspectorImpl = () => {
|
|
|
285
374
|
useNativeDriver: true,
|
|
286
375
|
}).start();
|
|
287
376
|
}, [fabAppearAnim, idle]);
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
377
|
+
(0, react_1.useEffect)(() => {
|
|
378
|
+
return () => {
|
|
379
|
+
resetGestureState();
|
|
380
|
+
stopSession();
|
|
381
|
+
};
|
|
382
|
+
}, [resetGestureState, stopSession]);
|
|
383
|
+
return ((0, jsx_runtime_1.jsx)(InspectorRootContainer, { children: (0, jsx_runtime_1.jsxs)(react_native_1.View, { ref: inspectorRootRef, style: react_native_1.StyleSheet.absoluteFill, pointerEvents: "box-none", children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { ref: anchorRef, style: [react_native_1.StyleSheet.absoluteFill, styles.anchor], pointerEvents: "none" }), (0, jsx_runtime_1.jsx)(SelectionFab, { visible: idle && selectionPillVisible, onPress: startSelecting, onPressIn: () => animateFabPress(0.96), onPressOut: () => animateFabPress(1), panHandlers: fabPanHandlers, fabAppearAnim: fabAppearAnim, fabPosition: fabPosition, fabPressAnim: fabPressAnim }), (0, jsx_runtime_1.jsx)(SelectionOverlay, { visible: overlayVisible, selecting: selecting, overlayRef: overlayRef, panHandlers: selectionPanHandlers, highlightRect: activeHighlightRect, statusBarOffset: statusBarOffset, selectingHintAnim: selectingHintAnim }), (0, jsx_runtime_1.jsx)(CopiedToast, { visible: toastVisible, toastAnim: toastAnim })] }) }));
|
|
384
|
+
};
|
|
385
|
+
exports.ElementInspector = ElementInspector;
|
|
386
|
+
const SelectionFab = ({ visible, onPress, onPressIn, onPressOut, panHandlers, fabAppearAnim, fabPosition, fabPressAnim, }) => {
|
|
387
|
+
if (!visible) {
|
|
388
|
+
return null;
|
|
389
|
+
}
|
|
390
|
+
return ((0, jsx_runtime_1.jsx)(react_native_1.Animated.View, { ...panHandlers, style: [
|
|
391
|
+
styles.fab,
|
|
392
|
+
{
|
|
393
|
+
opacity: fabAppearAnim,
|
|
394
|
+
transform: [
|
|
395
|
+
{ translateX: fabPosition.x },
|
|
396
|
+
{
|
|
397
|
+
translateY: react_native_1.Animated.add(fabPosition.y, fabAppearAnim.interpolate({
|
|
398
|
+
inputRange: [0, 1],
|
|
399
|
+
outputRange: [8, 0],
|
|
400
|
+
})),
|
|
401
|
+
},
|
|
402
|
+
{ scale: fabPressAnim },
|
|
403
|
+
],
|
|
404
|
+
},
|
|
405
|
+
], children: (0, jsx_runtime_1.jsx)(react_native_1.Pressable, { style: styles.fabInner, onPress: onPress, onPressIn: onPressIn, onPressOut: onPressOut, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.fabIcon, allowFontScaling: false, children: "\u2316" }) }) }));
|
|
406
|
+
};
|
|
407
|
+
const SelectionOverlay = ({ visible, selecting, overlayRef, panHandlers, highlightRect, statusBarOffset, selectingHintAnim, }) => {
|
|
408
|
+
if (!visible) {
|
|
409
|
+
return null;
|
|
410
|
+
}
|
|
411
|
+
return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { ref: overlayRef, style: styles.overlay, ...(selecting ? panHandlers : {}), children: [highlightRect && ((0, jsx_runtime_1.jsx)(react_native_1.View, { pointerEvents: "none", style: [
|
|
412
|
+
styles.highlight,
|
|
413
|
+
{
|
|
414
|
+
left: highlightRect.x,
|
|
415
|
+
top: highlightRect.y + statusBarOffset,
|
|
416
|
+
width: highlightRect.width,
|
|
417
|
+
height: highlightRect.height,
|
|
418
|
+
},
|
|
419
|
+
] })), selecting && ((0, jsx_runtime_1.jsx)(react_native_1.Animated.View, { pointerEvents: "none", style: [
|
|
420
|
+
styles.selectionHint,
|
|
421
|
+
{
|
|
422
|
+
opacity: selectingHintAnim.interpolate({
|
|
423
|
+
inputRange: [0, 1],
|
|
424
|
+
outputRange: [0.76, 1],
|
|
425
|
+
}),
|
|
426
|
+
transform: [
|
|
427
|
+
{
|
|
428
|
+
translateY: selectingHintAnim.interpolate({
|
|
429
|
+
inputRange: [0, 1],
|
|
430
|
+
outputRange: [0, -2],
|
|
431
|
+
}),
|
|
432
|
+
},
|
|
433
|
+
],
|
|
434
|
+
},
|
|
435
|
+
], children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.selectionHintText, children: "Drag or tap to find a source" }) }))] }));
|
|
436
|
+
};
|
|
437
|
+
const CopiedToast = ({ visible, toastAnim }) => {
|
|
438
|
+
if (!visible) {
|
|
439
|
+
return null;
|
|
440
|
+
}
|
|
441
|
+
return ((0, jsx_runtime_1.jsx)(react_native_1.Animated.View, { pointerEvents: "none", style: [
|
|
442
|
+
styles.toast,
|
|
443
|
+
{
|
|
444
|
+
opacity: toastAnim,
|
|
445
|
+
transform: [
|
|
446
|
+
{
|
|
447
|
+
translateY: toastAnim.interpolate({
|
|
448
|
+
inputRange: [0, 1],
|
|
449
|
+
outputRange: [-8, 0],
|
|
450
|
+
}),
|
|
451
|
+
},
|
|
452
|
+
],
|
|
453
|
+
},
|
|
454
|
+
], children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.toastText, children: "Source location copied" }) }));
|
|
355
455
|
};
|
|
356
456
|
const InspectorRootContainer = ({ children }) => {
|
|
357
|
-
if (react_native_1.Platform.OS ===
|
|
457
|
+
if (react_native_1.Platform.OS === "ios") {
|
|
358
458
|
return (0, jsx_runtime_1.jsx)(react_native_screens_1.FullWindowOverlay, { children: children });
|
|
359
459
|
}
|
|
360
460
|
return (0, jsx_runtime_1.jsx)(react_1.Fragment, { children: children });
|
|
@@ -364,75 +464,75 @@ const styles = react_native_1.StyleSheet.create({
|
|
|
364
464
|
opacity: 0,
|
|
365
465
|
},
|
|
366
466
|
fab: {
|
|
367
|
-
position:
|
|
467
|
+
position: "absolute",
|
|
368
468
|
left: 0,
|
|
369
469
|
top: 0,
|
|
370
470
|
zIndex: 9999,
|
|
371
471
|
width: FAB_SIZE,
|
|
372
472
|
height: FAB_SIZE,
|
|
373
473
|
borderRadius: 9999,
|
|
374
|
-
backgroundColor:
|
|
474
|
+
backgroundColor: "rgba(14,20,26,0.98)",
|
|
375
475
|
borderWidth: 1,
|
|
376
|
-
borderColor:
|
|
476
|
+
borderColor: "rgba(255,255,255,0.14)",
|
|
377
477
|
elevation: 4,
|
|
378
|
-
shadowColor:
|
|
478
|
+
shadowColor: "#000",
|
|
379
479
|
shadowOffset: { width: 0, height: 2 },
|
|
380
480
|
shadowOpacity: 0.28,
|
|
381
481
|
shadowRadius: 6,
|
|
382
482
|
},
|
|
383
483
|
fabInner: {
|
|
384
484
|
flex: 1,
|
|
385
|
-
flexDirection:
|
|
386
|
-
justifyContent:
|
|
387
|
-
alignItems:
|
|
388
|
-
width:
|
|
389
|
-
height:
|
|
485
|
+
flexDirection: "row",
|
|
486
|
+
justifyContent: "center",
|
|
487
|
+
alignItems: "center",
|
|
488
|
+
width: "100%",
|
|
489
|
+
height: "100%",
|
|
390
490
|
},
|
|
391
491
|
fabIcon: {
|
|
392
|
-
color:
|
|
492
|
+
color: "#F5F7FA",
|
|
393
493
|
fontSize: 28,
|
|
394
|
-
fontWeight:
|
|
494
|
+
fontWeight: "500",
|
|
395
495
|
},
|
|
396
496
|
overlay: {
|
|
397
497
|
...react_native_1.StyleSheet.absoluteFillObject,
|
|
398
|
-
backgroundColor:
|
|
498
|
+
backgroundColor: "rgba(0,0,0,0.015)",
|
|
399
499
|
},
|
|
400
500
|
selectionHint: {
|
|
401
|
-
position:
|
|
501
|
+
position: "absolute",
|
|
402
502
|
top: 56,
|
|
403
|
-
alignSelf:
|
|
404
|
-
backgroundColor:
|
|
503
|
+
alignSelf: "center",
|
|
504
|
+
backgroundColor: "rgba(14,20,26,0.9)",
|
|
405
505
|
borderRadius: 18,
|
|
406
506
|
paddingHorizontal: 14,
|
|
407
507
|
paddingVertical: 8,
|
|
408
508
|
borderWidth: 1,
|
|
409
|
-
borderColor:
|
|
509
|
+
borderColor: "rgba(255,255,255,0.14)",
|
|
410
510
|
},
|
|
411
511
|
selectionHintText: {
|
|
412
|
-
color:
|
|
512
|
+
color: "#F5F7FA",
|
|
413
513
|
fontSize: 13,
|
|
414
|
-
fontWeight:
|
|
514
|
+
fontWeight: "600",
|
|
415
515
|
},
|
|
416
516
|
highlight: {
|
|
417
|
-
position:
|
|
517
|
+
position: "absolute",
|
|
418
518
|
borderWidth: 2,
|
|
419
|
-
borderColor:
|
|
420
|
-
backgroundColor:
|
|
519
|
+
borderColor: "#1264A3",
|
|
520
|
+
backgroundColor: "rgba(18,100,163,0.16)",
|
|
421
521
|
},
|
|
422
522
|
toast: {
|
|
423
|
-
position:
|
|
523
|
+
position: "absolute",
|
|
424
524
|
top: 52,
|
|
425
525
|
left: 16,
|
|
426
526
|
right: 16,
|
|
427
|
-
alignItems:
|
|
527
|
+
alignItems: "center",
|
|
428
528
|
paddingVertical: 10,
|
|
429
529
|
borderRadius: 10,
|
|
430
|
-
backgroundColor:
|
|
530
|
+
backgroundColor: "rgba(18,100,163,0.96)",
|
|
431
531
|
},
|
|
432
532
|
toastText: {
|
|
433
|
-
color:
|
|
533
|
+
color: "#fff",
|
|
434
534
|
fontSize: 14,
|
|
435
|
-
fontWeight:
|
|
535
|
+
fontWeight: "700",
|
|
436
536
|
},
|
|
437
537
|
});
|
|
438
538
|
//# sourceMappingURL=index.js.map
|