@buoy-gg/react-query 7.0.32 → 7.0.34

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.
Files changed (36) hide show
  1. package/lib/commonjs/react-query/components/QueryBrowserMode.js +17 -1
  2. package/lib/commonjs/react-query/components/modals/MutationBrowserFooter.js +7 -19
  3. package/lib/commonjs/react-query/components/modals/QueryBrowserFooter.js +7 -19
  4. package/lib/commonjs/react-query/components/modals/ReactQueryModalHeader.js +29 -59
  5. package/lib/commonjs/react-query/components/query-browser/MutationStatusCount.js +51 -87
  6. package/lib/commonjs/react-query/components/query-browser/MutationsList.js +5 -7
  7. package/lib/commonjs/react-query/components/query-browser/QueryBrowser.js +9 -17
  8. package/lib/commonjs/react-query/components/query-browser/QueryStatusCount.js +66 -112
  9. package/lib/commonjs/react-query/components/query-browser/StorageStatusCount.js +22 -67
  10. package/lib/commonjs/react-query/stores/reactQueryEventStore.js +13 -22
  11. package/lib/commonjs/react-query/sync/snapshotProvider.js +17 -62
  12. package/lib/module/react-query/components/QueryBrowserMode.js +17 -1
  13. package/lib/module/react-query/components/modals/MutationBrowserFooter.js +8 -20
  14. package/lib/module/react-query/components/modals/QueryBrowserFooter.js +8 -20
  15. package/lib/module/react-query/components/modals/ReactQueryModalHeader.js +31 -61
  16. package/lib/module/react-query/components/query-browser/MutationStatusCount.js +51 -87
  17. package/lib/module/react-query/components/query-browser/MutationsList.js +7 -9
  18. package/lib/module/react-query/components/query-browser/QueryBrowser.js +11 -19
  19. package/lib/module/react-query/components/query-browser/QueryStatusCount.js +66 -112
  20. package/lib/module/react-query/components/query-browser/StorageStatusCount.js +22 -67
  21. package/lib/module/react-query/stores/reactQueryEventStore.js +13 -22
  22. package/lib/module/react-query/sync/snapshotProvider.js +18 -63
  23. package/lib/typescript/react-query/components/QueryBrowserMode.d.ts.map +1 -1
  24. package/lib/typescript/react-query/components/modals/MutationBrowserFooter.d.ts.map +1 -1
  25. package/lib/typescript/react-query/components/modals/QueryBrowserFooter.d.ts.map +1 -1
  26. package/lib/typescript/react-query/components/modals/ReactQueryModalHeader.d.ts.map +1 -1
  27. package/lib/typescript/react-query/components/query-browser/MutationStatusCount.d.ts.map +1 -1
  28. package/lib/typescript/react-query/components/query-browser/MutationsList.d.ts.map +1 -1
  29. package/lib/typescript/react-query/components/query-browser/QueryBrowser.d.ts +7 -1
  30. package/lib/typescript/react-query/components/query-browser/QueryBrowser.d.ts.map +1 -1
  31. package/lib/typescript/react-query/components/query-browser/QueryStatusCount.d.ts.map +1 -1
  32. package/lib/typescript/react-query/components/query-browser/StorageStatusCount.d.ts.map +1 -1
  33. package/lib/typescript/react-query/stores/reactQueryEventStore.d.ts +3 -8
  34. package/lib/typescript/react-query/stores/reactQueryEventStore.d.ts.map +1 -1
  35. package/lib/typescript/react-query/sync/snapshotProvider.d.ts.map +1 -1
  36. package/package.json +3 -3
@@ -4,8 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = void 0;
7
- var _react = require("react");
8
- var _reactNative = require("react-native");
7
+ var _sharedUi = require("@buoy-gg/shared-ui");
9
8
  var _QueryStatus = _interopRequireDefault(require("./QueryStatus"));
10
9
  var _storageQueryUtils = require("../../utils/storageQueryUtils");
11
10
  var _jsxRuntime = require("react/jsx-runtime");
@@ -24,21 +23,13 @@ const StorageStatusCount = ({
24
23
  onStorageTypesChange,
25
24
  counts
26
25
  }) => {
27
- // Scroll state management like QueryStatusCount
28
- const [isScrolling, setIsScrolling] = (0, _react.useState)(false);
29
- const touchStartX = (0, _react.useRef)(0);
30
- const touchStartY = (0, _react.useRef)(0);
26
+ const {
27
+ scrollProps,
28
+ onTouchStart: handleTouchStart,
29
+ isTap
30
+ } = (0, _sharedUi.useChipTap)();
31
31
  const handleStorageTypeToggle = (storageType, event) => {
32
- if (event) {
33
- // Calculate distance moved during touch (like QueryStatusCount)
34
- const dx = Math.abs(event.nativeEvent.pageX - touchStartX.current);
35
- const dy = Math.abs(event.nativeEvent.pageY - touchStartY.current);
36
-
37
- // If touch moved more than 5px in any direction, it's a swipe, not a tap
38
- if (dx > 5 || dy > 5 || isScrolling) {
39
- return; // Don't trigger filter change
40
- }
41
- }
32
+ if (!isTap(event)) return;
42
33
  if (onStorageTypesChange) {
43
34
  const newStorageTypes = new Set(activeStorageTypes);
44
35
  if (newStorageTypes.has(storageType)) {
@@ -49,58 +40,22 @@ const StorageStatusCount = ({
49
40
  onStorageTypesChange(newStorageTypes);
50
41
  }
51
42
  };
52
- const handleTouchStart = event => {
53
- touchStartX.current = event.nativeEvent.pageX;
54
- touchStartY.current = event.nativeEvent.pageY;
55
- };
56
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
57
- style: styles.storageStatusContainer,
58
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ScrollView, {
59
- "sentry-label": "ignore devtools storage status count scroll",
60
- horizontal: true,
61
- showsHorizontalScrollIndicator: false,
62
- style: styles.scrollView,
63
- contentContainerStyle: styles.scrollContent,
64
- onScrollBeginDrag: () => setIsScrolling(true),
65
- onScrollEndDrag: () => setTimeout(() => setIsScrolling(false), 300),
66
- onMomentumScrollBegin: () => setIsScrolling(true),
67
- onMomentumScrollEnd: () => setTimeout(() => setIsScrolling(false), 300),
68
- children: allStorageTypes.map(storageType => {
69
- const count = counts?.[storageType] ?? 0;
70
- return /*#__PURE__*/(0, _jsxRuntime.jsx)(_QueryStatus.default, {
71
- label: (0, _storageQueryUtils.getStorageTypeLabel)(storageType),
72
- color: (0, _storageQueryUtils.getStorageTypeColor)(storageType),
73
- count: count // Show actual storage query counts
74
- ,
75
- isActive: activeStorageTypes.has(storageType),
76
- onPress: event => handleStorageTypeToggle(storageType, event),
77
- onTouchStart: handleTouchStart,
78
- showLabel: true
79
- }, storageType);
80
- })
43
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_sharedUi.ChipStrip, {
44
+ scrollProps: scrollProps,
45
+ sentryLabel: "ignore devtools storage status count scroll",
46
+ children: allStorageTypes.map(storageType => {
47
+ const count = counts?.[storageType] ?? 0;
48
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_QueryStatus.default, {
49
+ label: (0, _storageQueryUtils.getStorageTypeLabel)(storageType),
50
+ color: (0, _storageQueryUtils.getStorageTypeColor)(storageType),
51
+ count: count // Show actual storage query counts
52
+ ,
53
+ isActive: activeStorageTypes.has(storageType),
54
+ onPress: event => handleStorageTypeToggle(storageType, event),
55
+ onTouchStart: handleTouchStart,
56
+ showLabel: true
57
+ }, storageType);
81
58
  })
82
59
  });
83
60
  };
84
- const styles = _reactNative.StyleSheet.create({
85
- storageStatusContainer: {
86
- // Container for ScrollView - take full width
87
- flex: 1,
88
- minWidth: 0
89
- },
90
- scrollView: {
91
- // ScrollView itself styles
92
- flex: 1
93
- },
94
- scrollContent: {
95
- // ScrollView content styles
96
- flexDirection: "row",
97
- alignItems: "center",
98
- gap: 8,
99
- // Spacing between chips
100
- paddingHorizontal: 4,
101
- // Small padding on ends
102
- paddingVertical: 4,
103
- flexGrow: 1 // Allow content to grow to fill available space
104
- }
105
- });
106
61
  var _default = exports.default = StorageStatusCount;
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.subscribeToReactQueryEvents = exports.setQueryClient = exports.reactQueryEventStore = exports.isReactQueryConnected = exports.getReactQueryEvents = exports.disconnectQueryClient = exports.clearReactQueryEvents = void 0;
7
+ var _sharedUi = require("@buoy-gg/shared-ui");
7
8
  var _storageQueryUtils = require("../utils/storageQueryUtils");
8
9
  /**
9
10
  * React Query Event Store
@@ -29,13 +30,15 @@ let eventIdCounter = 0;
29
30
  function generateEventId() {
30
31
  return `rq-${Date.now()}-${++eventIdCounter}`;
31
32
  }
32
- class ReactQueryEventStore {
33
- events = [];
34
- listeners = new Set();
33
+ class ReactQueryEventStore extends _sharedUi.ChangeLog {
35
34
  queryClient = null;
36
35
  queryUnsubscribe = null;
37
36
  mutationUnsubscribe = null;
38
- maxEvents = 200;
37
+ constructor() {
38
+ super({
39
+ max: 200
40
+ });
41
+ }
39
42
 
40
43
  // Track fetch start times for duration calculation
41
44
  fetchStartTimes = new Map();
@@ -246,43 +249,31 @@ class ReactQueryEventStore {
246
249
  * Add an event to the store
247
250
  */
248
251
  addEvent(event) {
249
- this.events = [event, ...this.events].slice(0, this.maxEvents);
250
- this.notifyListeners();
252
+ this.record(event);
251
253
  }
252
254
 
253
255
  /**
254
256
  * Get all events
255
257
  */
256
258
  getEvents() {
257
- return this.events;
259
+ return this.items;
258
260
  }
259
261
 
260
262
  /**
261
263
  * Subscribe to event changes
262
264
  */
263
265
  subscribe(listener) {
264
- this.listeners.add(listener);
266
+ const unsubscribe = super.subscribe(listener);
265
267
  // Immediately call with current events
266
- listener(this.events);
267
- return () => {
268
- this.listeners.delete(listener);
269
- };
270
- }
271
-
272
- /**
273
- * Notify all listeners
274
- */
275
- notifyListeners() {
276
- const events = this.events;
277
- this.listeners.forEach(listener => listener(events));
268
+ listener(this.items);
269
+ return unsubscribe;
278
270
  }
279
271
 
280
272
  /**
281
273
  * Clear all events
282
274
  */
283
275
  clearEvents() {
284
- this.events = [];
285
- this.notifyListeners();
276
+ this.clear();
286
277
  }
287
278
 
288
279
  /**
@@ -6,7 +6,6 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.createQuerySnapshotProvider = createQuerySnapshotProvider;
7
7
  exports.registerQuerySnapshotProvider = registerQuerySnapshotProvider;
8
8
  var _sharedUi = require("@buoy-gg/shared-ui");
9
- var _dataViewer = require("@buoy-gg/shared-ui/dataViewer");
10
9
  var _dehydrate = require("./dehydrate");
11
10
  /**
12
11
  * Time Machine snapshot provider for React Query.
@@ -54,7 +53,6 @@ function queryLabel(queryKey, queryHash) {
54
53
  return queryHash;
55
54
  }
56
55
  }
57
- const ITEM_RENDER_BYTE_LIMIT = 256 * 1024;
58
56
 
59
57
  /**
60
58
  * A query `error` (and `fetchFailureReason`) usually holds the live
@@ -156,31 +154,15 @@ function createQuerySnapshotProvider(queryClient) {
156
154
  },
157
155
  async restore(data, options) {
158
156
  const snapshot = data;
159
- const excludedSet = new Set(options?.excludeKeys ?? []);
160
- const includedSet = options?.includeKeys ? new Set(options.includeKeys) : null;
161
- const touch = key => !excludedSet.has(key) && (!includedSet || includedSet.has(key));
162
- let excludedHits = 0;
163
- const result = {
164
- ok: true,
165
- applied: 0,
166
- skipped: [],
167
- warnings: []
168
- };
157
+ const scope = (0, _sharedUi.createRestoreScope)(options);
158
+ const result = (0, _sharedUi.emptyRestoreResult)();
169
159
  if (!snapshot?.queries) {
170
- return {
171
- ok: false,
172
- applied: 0,
173
- skipped: [],
174
- warnings: ["empty query snapshot"]
175
- };
160
+ return (0, _sharedUi.failedRestore)("empty query snapshot");
176
161
  }
177
162
  const cache = queryClient.getQueryCache();
178
163
  const snapHashes = new Set(snapshot.queries.map(q => q.queryHash));
179
164
  for (const snap of snapshot.queries) {
180
- if (!touch(snap.queryHash)) {
181
- excludedHits += 1;
182
- continue;
183
- }
165
+ if (!scope.allows(snap.queryHash)) continue;
184
166
  // Snapshots taken before the raw-capture fix hold a wire placeholder
185
167
  // instead of the data. Writing that into the cache hands the app an
186
168
  // object with none of its fields — leave the live query alone and say
@@ -221,10 +203,7 @@ function createQuerySnapshotProvider(queryClient) {
221
203
  // Cache entries the snapshot doesn't know about get removed; a mounted
222
204
  // observer will rebuild + refetch its query on next interaction.
223
205
  for (const query of cache.getAll().slice()) {
224
- if (!touch(query.queryHash)) {
225
- excludedHits += 1;
226
- continue;
227
- }
206
+ if (!scope.allows(query.queryHash)) continue;
228
207
  if (!snapHashes.has(query.queryHash)) {
229
208
  try {
230
209
  cache.remove(query);
@@ -242,9 +221,8 @@ function createQuerySnapshotProvider(queryClient) {
242
221
  queryClient.getMutationCache().clear();
243
222
  result.warnings.push(`mutation cache cleared (${mutationCount} mutation(s))`);
244
223
  }
245
- if (excludedHits > 0) {
246
- result.warnings.push(`${excludedHits} quer(y/ies) excluded by you`);
247
- }
224
+ const excludedWarning = scope.excludedWarning("quer(y/ies)");
225
+ if (excludedWarning) result.warnings.push(excludedWarning);
248
226
  return result;
249
227
  },
250
228
  /** Per-query restore preview. `base` omitted = compare against live cache. */
@@ -270,39 +248,16 @@ function createQuerySnapshotProvider(queryClient) {
270
248
  continue;
271
249
  }
272
250
  const baseQ = baseByHash.get(q.queryHash);
273
- if (!baseQ) {
274
- items.push({
275
- key: q.queryHash,
276
- label,
277
- verdict: "added",
278
- newValue: comparableQuery(q)
279
- });
280
- continue;
281
- }
282
- const oldVal = comparableQuery(baseQ);
283
- const newVal = comparableQuery(q);
284
- if ((0, _dataViewer.boundedDeepEqual)(oldVal, newVal)) {
285
- items.push({
286
- key: q.queryHash,
287
- label,
288
- verdict: "unchanged"
289
- });
290
- } else if ((0, _dataViewer.jsonLikelyLargerThan)(oldVal, ITEM_RENDER_BYTE_LIMIT) || (0, _dataViewer.jsonLikelyLargerThan)(newVal, ITEM_RENDER_BYTE_LIMIT)) {
291
- items.push({
292
- key: q.queryHash,
293
- label,
294
- verdict: "too-large",
295
- reason: "query data too large to render a diff — restore still applies it"
296
- });
297
- } else {
298
- items.push({
299
- key: q.queryHash,
300
- label,
301
- verdict: "changed",
302
- oldValue: oldVal,
303
- newValue: newVal
304
- });
305
- }
251
+ items.push((0, _sharedUi.classifyPreviewItem)({
252
+ key: q.queryHash,
253
+ label,
254
+ oldValue: baseQ ? comparableQuery(baseQ) : undefined,
255
+ newValue: comparableQuery(q),
256
+ inOld: baseQ !== undefined,
257
+ inNew: true
258
+ }, {
259
+ tooLargeReason: "query data too large to render a diff — restore still applies it"
260
+ }));
306
261
  }
307
262
  for (const q of baseQueries) {
308
263
  if (!targetByHash.has(q.queryHash)) {
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
 
3
+ import { resolveListEmptyState } from "@buoy-gg/shared-ui";
3
4
  import { View, StyleSheet } from "react-native";
4
5
  import { useMemo } from "react";
5
6
  import { QueryBrowser } from "./query-browser/index";
@@ -58,7 +59,22 @@ export function QueryBrowserMode({
58
59
  searchText: searchText,
59
60
  ignoredPatterns: ignoredPatterns,
60
61
  includedPatterns: includedPatterns,
61
- emptyStateMessage: searchText ? `No queries found matching "${searchText}"` : activeFilter ? `No ${activeFilter} queries found` : hasIncludeFilters ? `No queries match your "include only" filters. ${includedPatterns.size} pattern(s) active.` : hasExcludeFilters ? `No queries match the current filters. ${ignoredPatterns.size} pattern(s) excluded.` : "No React Query queries are currently active.\n\nTo see queries here:\n• Make API calls using useQuery\n• Ensure queries are within QueryClientProvider\n• Check console for debugging info",
62
+ emptyState: resolveListEmptyState({
63
+ subject: "queries",
64
+ verb: "active",
65
+ search: searchText,
66
+ total: allQueries.length,
67
+ // Include/exclude patterns and the status filter are all "your
68
+ // filters hid it", but each names the control that undoes it.
69
+ blocked: activeFilter ? `No ${activeFilter} queries. Pick another status to see the rest.` : hasIncludeFilters ? `No queries match your "include only" filters. ${includedPatterns.size} pattern(s) active.` : hasExcludeFilters ? `No queries match the current filters. ${ignoredPatterns.size} pattern(s) excluded.` : undefined,
70
+ waiting: {
71
+ // scanning = the cache was inspected and came back empty; this is
72
+ // a setup problem, not a stream that has not started.
73
+ otto: "scanning",
74
+ title: "No queries found",
75
+ message: "No React Query queries are currently active.\n\nTo see queries here:\n• Make API calls using useQuery\n• Ensure queries are within QueryClientProvider\n• Check console for debugging info"
76
+ }
77
+ }),
62
78
  contentContainerStyle: styles.queryListContent
63
79
  })]
64
80
  });
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
 
3
3
  import { View, StyleSheet } from "react-native";
4
- import { useSafeAreaInsets, night } from "@buoy-gg/shared-ui";
4
+ import { ToolFooter, useSafeAreaInsets } from "@buoy-gg/shared-ui";
5
5
  import MutationStatusCount from "../query-browser/MutationStatusCount";
6
6
  import { useMemo } from "react";
7
7
  import { jsx as _jsx } from "react/jsx-runtime";
@@ -28,13 +28,12 @@ export function MutationBrowserFooter({
28
28
  const paddingBottom = useMemo(() => {
29
29
  return !isFloatingMode ? insets.bottom : 0;
30
30
  }, [isFloatingMode, insets.bottom]);
31
- return /*#__PURE__*/_jsx(View, {
31
+ return /*#__PURE__*/_jsx(ToolFooter, {
32
32
  // Force re-render with key change
33
- style: [styles.filterFooter, {
34
- paddingBottom
35
- },
36
- // Remove border radius when docked to bottom
37
- !isFloatingMode && styles.dockedFooter],
33
+ floating: isFloatingMode,
34
+ safeArea: false,
35
+ paddingBottom: paddingBottom,
36
+ style: styles.filterFooter,
38
37
  children: /*#__PURE__*/_jsx(View, {
39
38
  style: styles.filterContainer,
40
39
  children: /*#__PURE__*/_jsx(MutationStatusCount, {
@@ -47,19 +46,8 @@ export function MutationBrowserFooter({
47
46
  }
48
47
  const styles = StyleSheet.create({
49
48
  filterFooter: {
50
- borderTopWidth: 1,
51
- borderTopColor: night.color.border,
52
- paddingVertical: 8,
53
- paddingHorizontal: 0,
54
- // Transparent so the night sheet's star field shows through.
55
- // Night glass bar — matches the shared EventStepperFooter backing.
56
- backgroundColor: "rgba(5, 5, 5, 0.92)",
57
- borderBottomLeftRadius: 14,
58
- borderBottomRightRadius: 14
59
- },
60
- dockedFooter: {
61
- borderBottomLeftRadius: 0,
62
- borderBottomRightRadius: 0
49
+ paddingTop: 8,
50
+ paddingHorizontal: 0
63
51
  },
64
52
  filterContainer: {
65
53
  minHeight: 32,
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
 
3
3
  import { View, StyleSheet } from "react-native";
4
- import { useSafeAreaInsets, night } from "@buoy-gg/shared-ui";
4
+ import { ToolFooter, useSafeAreaInsets } from "@buoy-gg/shared-ui";
5
5
  import QueryStatusCount from "../query-browser/QueryStatusCount";
6
6
  import { jsx as _jsx } from "react/jsx-runtime";
7
7
  /**
@@ -24,12 +24,11 @@ export function QueryBrowserFooter({
24
24
  const insets = useSafeAreaInsets({
25
25
  minBottom: 16
26
26
  });
27
- return /*#__PURE__*/_jsx(View, {
28
- style: [styles.filterFooter, {
29
- paddingBottom: !isFloatingMode ? insets.bottom : 0
30
- },
31
- // Remove border radius when docked to bottom
32
- !isFloatingMode && styles.dockedFooter],
27
+ return /*#__PURE__*/_jsx(ToolFooter, {
28
+ floating: isFloatingMode,
29
+ safeArea: false,
30
+ paddingBottom: !isFloatingMode ? insets.bottom : 0,
31
+ style: styles.filterFooter,
33
32
  children: /*#__PURE__*/_jsx(View, {
34
33
  style: styles.filterContainer,
35
34
  children: /*#__PURE__*/_jsx(QueryStatusCount, {
@@ -42,19 +41,8 @@ export function QueryBrowserFooter({
42
41
  }
43
42
  const styles = StyleSheet.create({
44
43
  filterFooter: {
45
- borderTopWidth: 1,
46
- borderTopColor: night.color.border,
47
- paddingVertical: 8,
48
- paddingHorizontal: 0,
49
- // Transparent so the night sheet's star field shows through.
50
- // Night glass bar — matches the shared EventStepperFooter backing.
51
- backgroundColor: "rgba(5, 5, 5, 0.92)",
52
- borderBottomLeftRadius: 14,
53
- borderBottomRightRadius: 14
54
- },
55
- dockedFooter: {
56
- borderBottomLeftRadius: 0,
57
- borderBottomRightRadius: 0
44
+ paddingTop: 8,
45
+ paddingHorizontal: 0
58
46
  },
59
47
  filterContainer: {
60
48
  minHeight: 32,
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
 
3
- import { ModalHeader, TabSelector, Search, X, Filter, Trash, night } from "@buoy-gg/shared-ui";
3
+ import { ModalHeader, TabSelector, Search, Filter, Trash, night } from "@buoy-gg/shared-ui";
4
4
  import { useState, useRef, useEffect } from "react";
5
- import { View, TextInput, TouchableOpacity, StyleSheet } from "react-native";
5
+ import { View, TouchableOpacity, StyleSheet } from "react-native";
6
6
  import { WifiToggle } from "../WifiToggle";
7
7
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
8
8
  /**
@@ -23,6 +23,12 @@ export function ReactQueryModalHeader({
23
23
  showWifiToggle = false
24
24
  }) {
25
25
  const [isSearchActive, setIsSearchActive] = useState(false);
26
+ // True while the header's clear ✕ is being pressed. With a mouse, pressing
27
+ // the ✕ blurs the input FIRST, and the blur handler closes search — which
28
+ // unmounts the ✕ before its own press can fire, so the query was never
29
+ // cleared (desktop). Press-in lands before the blur, so the blur handler can
30
+ // tell this dismissal apart from a click elsewhere.
31
+ const clearPressRef = useRef(false);
26
32
  const searchInputRef = useRef(null);
27
33
 
28
34
  // Auto-focus search input when search becomes active
@@ -80,42 +86,29 @@ export function ReactQueryModalHeader({
80
86
  children: [/*#__PURE__*/_jsx(ModalHeader.Content, {
81
87
  title: "",
82
88
  noMargin: true,
83
- children: isSearchActive ? /*#__PURE__*/_jsxs(View, {
84
- style: styles.headerSearchContainer,
85
- testID: "buoy-rq-search-field",
86
- children: [/*#__PURE__*/_jsx(Search, {
87
- size: 14,
88
- color: night.color.textSecondary
89
- }), /*#__PURE__*/_jsx(TextInput, {
90
- ref: searchInputRef,
91
- testID: "buoy-rq-search",
92
- style: styles.headerSearchInput,
93
- placeholder: "Search query keys...",
94
- placeholderTextColor: night.color.textTertiary,
95
- value: searchText,
96
- onChangeText: handleSearch,
97
- onSubmitEditing: () => setIsSearchActive(false),
98
- onBlur: () => setIsSearchActive(false),
99
- "sentry-label": "ignore react query search header",
100
- accessibilityLabel: "Search queries and mutations",
101
- autoCapitalize: "none",
102
- autoCorrect: false,
103
- returnKeyType: "search"
104
- }), searchText.length > 0 ? /*#__PURE__*/_jsx(TouchableOpacity, {
105
- onPress: () => {
106
- handleSearch("");
107
- setIsSearchActive(false);
108
- },
109
- "sentry-label": "ignore clear search header",
110
- testID: "buoy-rq-search-clear",
111
- accessibilityRole: "button",
112
- accessibilityLabel: "Clear search",
113
- style: styles.headerSearchClear,
114
- children: /*#__PURE__*/_jsx(X, {
115
- size: 14,
116
- color: night.color.textSecondary
117
- })
118
- }) : null]
89
+ children: isSearchActive ? /*#__PURE__*/_jsx(ModalHeader.Search, {
90
+ value: searchText,
91
+ onChangeText: handleSearch,
92
+ onCancel: () => {
93
+ handleSearch("");
94
+ setIsSearchActive(false);
95
+ },
96
+ onDismiss: () => setIsSearchActive(false),
97
+ dismissOnBlur: true,
98
+ clearOnlyWhenText: true,
99
+ placeholder: "Search query keys...",
100
+ accessibilityLabel: "Search queries and mutations",
101
+ iconSize: 14
102
+ // These handles predate the shared field and are the reverse of
103
+ // its convention (container = "-field", input = bare); kept as-is
104
+ // so the e2e suite keeps its targets.
105
+ ,
106
+ testID: "buoy-rq-search",
107
+ containerTestID: "buoy-rq-search-field",
108
+ clearTestID: "buoy-rq-search-clear",
109
+ inputProps: {
110
+ "sentry-label": "ignore react query search header"
111
+ }
119
112
  }) : showWifiToggle ? /*#__PURE__*/_jsxs(View, {
120
113
  style: styles.tabRow,
121
114
  children: [/*#__PURE__*/_jsx(View, {
@@ -184,29 +177,6 @@ const styles = StyleSheet.create({
184
177
  tabSelectorWrap: {
185
178
  flex: 1
186
179
  },
187
- headerSearchContainer: {
188
- flexDirection: "row",
189
- alignItems: "center",
190
- backgroundColor: night.color.surfaceElevated,
191
- borderRadius: 6,
192
- paddingHorizontal: 8,
193
- paddingVertical: 4,
194
- gap: 6,
195
- marginHorizontal: 12,
196
- marginVertical: 8
197
- },
198
- headerSearchInput: {
199
- flex: 1,
200
- color: night.color.text,
201
- fontSize: 13,
202
- fontFamily: "monospace",
203
- padding: 0,
204
- margin: 0,
205
- minHeight: 18
206
- },
207
- headerSearchClear: {
208
- padding: 2
209
- },
210
180
  headerActionButton: {
211
181
  padding: 6,
212
182
  borderRadius: 4