@buoy-gg/zustand 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.
- package/lib/commonjs/zustand/components/ZustandDetailViewToggle.js +13 -107
- package/lib/commonjs/zustand/components/ZustandEventFilterView.js +24 -288
- package/lib/commonjs/zustand/components/ZustandModal.js +83 -141
- package/lib/commonjs/zustand/components/ZustandStateChangeItem.js +8 -61
- package/lib/commonjs/zustand/components/ZustandStateInfoView.js +2 -46
- package/lib/commonjs/zustand/components/ZustandStoreBrowser.js +29 -54
- package/lib/commonjs/zustand/hooks/useZustandStateChanges.js +33 -53
- package/lib/commonjs/zustand/sync/zustandSyncAdapter.js +24 -78
- package/lib/commonjs/zustand/utils/snapshotProvider.js +17 -60
- package/lib/commonjs/zustand/utils/zustandStateStore.js +37 -223
- package/lib/module/zustand/components/ZustandDetailViewToggle.js +15 -109
- package/lib/module/zustand/components/ZustandEventFilterView.js +28 -292
- package/lib/module/zustand/components/ZustandModal.js +85 -143
- package/lib/module/zustand/components/ZustandStateChangeItem.js +10 -63
- package/lib/module/zustand/components/ZustandStateInfoView.js +1 -45
- package/lib/module/zustand/components/ZustandStoreBrowser.js +31 -56
- package/lib/module/zustand/hooks/useZustandStateChanges.js +34 -54
- package/lib/module/zustand/sync/zustandSyncAdapter.js +25 -79
- package/lib/module/zustand/utils/snapshotProvider.js +18 -61
- package/lib/module/zustand/utils/zustandStateStore.js +37 -223
- package/lib/typescript/zustand/components/ZustandDetailViewToggle.d.ts +2 -1
- package/lib/typescript/zustand/components/ZustandDetailViewToggle.d.ts.map +1 -1
- package/lib/typescript/zustand/components/ZustandEventFilterView.d.ts +6 -0
- package/lib/typescript/zustand/components/ZustandEventFilterView.d.ts.map +1 -1
- package/lib/typescript/zustand/components/ZustandModal.d.ts.map +1 -1
- package/lib/typescript/zustand/components/ZustandStateChangeItem.d.ts.map +1 -1
- package/lib/typescript/zustand/components/ZustandStateInfoView.d.ts.map +1 -1
- package/lib/typescript/zustand/components/ZustandStoreBrowser.d.ts.map +1 -1
- package/lib/typescript/zustand/hooks/useZustandStateChanges.d.ts +2 -1
- package/lib/typescript/zustand/hooks/useZustandStateChanges.d.ts.map +1 -1
- package/lib/typescript/zustand/sync/zustandSyncAdapter.d.ts +103 -117
- package/lib/typescript/zustand/sync/zustandSyncAdapter.d.ts.map +1 -1
- package/lib/typescript/zustand/utils/zustandStateStore.d.ts +6 -26
- package/lib/typescript/zustand/utils/zustandStateStore.d.ts.map +1 -1
- package/package.json +3 -3
|
@@ -1,303 +1,39 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
/**
|
|
4
|
+
* ZustandEventFilterView
|
|
5
|
+
*
|
|
6
|
+
* The shared `EntityFilterView` with zustand's nouns: registered stores you
|
|
7
|
+
* can tap to hide, active store-name patterns, and how it works.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { useMemo } from "react";
|
|
11
|
+
import { EntityFilterView } from "@buoy-gg/shared-ui";
|
|
6
12
|
import { zustandStateStore } from "../utils/zustandStateStore";
|
|
7
13
|
import { ZUSTAND_TEST_ID, slugForTestID } from "../utils/testIDs";
|
|
8
|
-
import { jsx as _jsx
|
|
9
|
-
|
|
10
|
-
function getStoreColorForPattern(pattern, stores) {
|
|
11
|
-
const match = stores.find(s => s.name.toLowerCase() === pattern.toLowerCase());
|
|
12
|
-
return match ? zustandStateStore.getStoreColor(match.name) : null;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/** True if any active pattern filters out this store */
|
|
16
|
-
function isStoreFiltered(store, ignoredPatterns) {
|
|
17
|
-
return Array.from(ignoredPatterns).some(p => store.name.toLowerCase().includes(p.toLowerCase()));
|
|
18
|
-
}
|
|
14
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
15
|
+
const EXAMPLES = ["• auth → hides authStore from stores & events", "• temp → hides tempStore, tempUserStore", "• cart → hides cartStore from both tabs"];
|
|
19
16
|
export function ZustandEventFilterView({
|
|
20
17
|
ignoredPatterns,
|
|
21
18
|
onTogglePattern,
|
|
22
19
|
onAddPattern,
|
|
23
20
|
stores
|
|
24
21
|
}) {
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
style: styles.section,
|
|
42
|
-
children: [/*#__PURE__*/_jsxs(SectionHeader, {
|
|
43
|
-
children: [/*#__PURE__*/_jsx(SectionHeader.Title, {
|
|
44
|
-
children: "REGISTERED STORES"
|
|
45
|
-
}), /*#__PURE__*/_jsx(SectionHeader.Badge, {
|
|
46
|
-
count: stores.length,
|
|
47
|
-
color: night.color.accent,
|
|
48
|
-
testID: `${ZUSTAND_TEST_ID}-filters-store-count`
|
|
49
|
-
})]
|
|
50
|
-
}), /*#__PURE__*/_jsx(ScrollView, {
|
|
51
|
-
style: styles.storeList,
|
|
52
|
-
contentContainerStyle: styles.storeListContent,
|
|
53
|
-
showsVerticalScrollIndicator: true,
|
|
54
|
-
nestedScrollEnabled: true,
|
|
55
|
-
children: stores.map(store => {
|
|
56
|
-
const storeColor = zustandStateStore.getStoreColor(store.name);
|
|
57
|
-
const filtered = isStoreFiltered(store, ignoredPatterns);
|
|
58
|
-
return /*#__PURE__*/_jsxs(TouchableOpacity, {
|
|
59
|
-
style: [styles.storeRow, filtered && styles.storeRowFiltered],
|
|
60
|
-
onPress: () => onAddPattern(store.name),
|
|
61
|
-
disabled: filtered,
|
|
62
|
-
testID: `${ZUSTAND_TEST_ID}-filters-store-${slugForTestID(store.name)}`,
|
|
63
|
-
accessibilityRole: "button",
|
|
64
|
-
accessibilityLabel: `Hide ${store.name}`,
|
|
65
|
-
accessibilityState: {
|
|
66
|
-
disabled: filtered
|
|
67
|
-
},
|
|
68
|
-
children: [/*#__PURE__*/_jsx(View, {
|
|
69
|
-
style: [styles.storeDot, {
|
|
70
|
-
backgroundColor: filtered ? night.color.textTertiary : storeColor
|
|
71
|
-
}]
|
|
72
|
-
}), /*#__PURE__*/_jsx(Text, {
|
|
73
|
-
style: [styles.storeRowText, filtered && styles.storeRowTextFiltered, !filtered && {
|
|
74
|
-
color: storeColor
|
|
75
|
-
}],
|
|
76
|
-
numberOfLines: 1,
|
|
77
|
-
children: store.name
|
|
78
|
-
}), filtered ? /*#__PURE__*/_jsx(Text, {
|
|
79
|
-
style: styles.filteredLabel,
|
|
80
|
-
children: "hidden"
|
|
81
|
-
}) : /*#__PURE__*/_jsx(Text, {
|
|
82
|
-
style: [styles.addLabel, {
|
|
83
|
-
color: storeColor + "99"
|
|
84
|
-
}],
|
|
85
|
-
children: "+ hide"
|
|
86
|
-
})]
|
|
87
|
-
}, store.name);
|
|
88
|
-
})
|
|
89
|
-
})]
|
|
90
|
-
}), /*#__PURE__*/_jsxs(View, {
|
|
91
|
-
style: styles.section,
|
|
92
|
-
children: [/*#__PURE__*/_jsxs(SectionHeader, {
|
|
93
|
-
children: [/*#__PURE__*/_jsx(SectionHeader.Icon, {
|
|
94
|
-
icon: Filter,
|
|
95
|
-
color: night.color.accent,
|
|
96
|
-
size: 12
|
|
97
|
-
}), /*#__PURE__*/_jsx(SectionHeader.Title, {
|
|
98
|
-
children: "STORE NAME FILTERS"
|
|
99
|
-
}), ignoredPatterns.size > 0 && /*#__PURE__*/_jsx(SectionHeader.Badge, {
|
|
100
|
-
count: ignoredPatterns.size,
|
|
101
|
-
color: night.color.accent,
|
|
102
|
-
testID: `${ZUSTAND_TEST_ID}-filters-count`
|
|
103
|
-
})]
|
|
104
|
-
}), /*#__PURE__*/_jsx(View, {
|
|
105
|
-
style: styles.addInputWrapper,
|
|
106
|
-
children: !showAddInput ? /*#__PURE__*/_jsx(AddFilterButton, {
|
|
107
|
-
onPress: () => setShowAddInput(true),
|
|
108
|
-
color: night.color.accent,
|
|
109
|
-
label: "Add store name pattern",
|
|
110
|
-
testID: `${ZUSTAND_TEST_ID}-filters-add-open`
|
|
111
|
-
}) : /*#__PURE__*/_jsx(AddFilterInput, {
|
|
112
|
-
value: inputValue,
|
|
113
|
-
onChange: setInputValue,
|
|
114
|
-
onSubmit: handleAddPattern,
|
|
115
|
-
onCancel: () => {
|
|
116
|
-
setShowAddInput(false);
|
|
117
|
-
setInputValue("");
|
|
118
|
-
},
|
|
119
|
-
placeholder: "Enter store name pattern to hide...",
|
|
120
|
-
color: night.color.accent,
|
|
121
|
-
testID: `${ZUSTAND_TEST_ID}-filters-add-input`
|
|
122
|
-
})
|
|
123
|
-
}), ignoredPatterns.size > 0 && /*#__PURE__*/_jsx(View, {
|
|
124
|
-
style: styles.patternList,
|
|
125
|
-
children: Array.from(ignoredPatterns).map(pattern => {
|
|
126
|
-
const storeColor = getStoreColorForPattern(pattern, stores);
|
|
127
|
-
return /*#__PURE__*/_jsxs(TouchableOpacity, {
|
|
128
|
-
style: styles.patternRow,
|
|
129
|
-
onPress: () => onTogglePattern(pattern),
|
|
130
|
-
activeOpacity: 0.8,
|
|
131
|
-
testID: `${ZUSTAND_TEST_ID}-filters-chip-${slugForTestID(pattern)}`,
|
|
132
|
-
accessibilityRole: "button",
|
|
133
|
-
accessibilityLabel: `Remove filter ${pattern}`,
|
|
134
|
-
children: [storeColor ? /*#__PURE__*/_jsx(View, {
|
|
135
|
-
style: [styles.storeDot, {
|
|
136
|
-
backgroundColor: storeColor
|
|
137
|
-
}]
|
|
138
|
-
}) : null, /*#__PURE__*/_jsx(Text, {
|
|
139
|
-
style: [styles.patternText, storeColor ? {
|
|
140
|
-
color: storeColor
|
|
141
|
-
} : null],
|
|
142
|
-
numberOfLines: 1,
|
|
143
|
-
children: pattern
|
|
144
|
-
}), /*#__PURE__*/_jsx(X, {
|
|
145
|
-
size: 12,
|
|
146
|
-
color: night.color.accent + "80"
|
|
147
|
-
})]
|
|
148
|
-
}, pattern);
|
|
149
|
-
})
|
|
150
|
-
})]
|
|
151
|
-
}), /*#__PURE__*/_jsxs(View, {
|
|
152
|
-
style: styles.section,
|
|
153
|
-
children: [/*#__PURE__*/_jsxs(SectionHeader, {
|
|
154
|
-
children: [/*#__PURE__*/_jsx(SectionHeader.Icon, {
|
|
155
|
-
icon: Filter,
|
|
156
|
-
color: night.color.textSecondary,
|
|
157
|
-
size: 12
|
|
158
|
-
}), /*#__PURE__*/_jsx(SectionHeader.Title, {
|
|
159
|
-
children: "HOW EVENT FILTERS WORK"
|
|
160
|
-
})]
|
|
161
|
-
}), /*#__PURE__*/_jsx(Text, {
|
|
162
|
-
style: styles.howItWorksText,
|
|
163
|
-
children: "Patterns hide matching stores from both the Stores tab and the Events tab. Tap a store above to quickly add it as a filter."
|
|
164
|
-
}), /*#__PURE__*/_jsxs(View, {
|
|
165
|
-
style: styles.examplesContainer,
|
|
166
|
-
children: [/*#__PURE__*/_jsx(Text, {
|
|
167
|
-
style: styles.examplesTitle,
|
|
168
|
-
children: "EXAMPLES:"
|
|
169
|
-
}), ["• auth → hides authStore from stores & events", "• temp → hides tempStore, tempUserStore", "• cart → hides cartStore from both tabs"].map(ex => /*#__PURE__*/_jsx(Text, {
|
|
170
|
-
style: styles.exampleItem,
|
|
171
|
-
children: ex
|
|
172
|
-
}, ex))]
|
|
173
|
-
})]
|
|
174
|
-
})]
|
|
22
|
+
const entities = useMemo(() => stores.map(store => ({
|
|
23
|
+
name: store.name,
|
|
24
|
+
color: zustandStateStore.getStoreColor(store.name)
|
|
25
|
+
})), [stores]);
|
|
26
|
+
return /*#__PURE__*/_jsx(EntityFilterView, {
|
|
27
|
+
entities: entities,
|
|
28
|
+
ignoredPatterns: ignoredPatterns,
|
|
29
|
+
onTogglePattern: onTogglePattern,
|
|
30
|
+
onAddPattern: onAddPattern,
|
|
31
|
+
entityNoun: "STORES",
|
|
32
|
+
entitySingular: "store",
|
|
33
|
+
entityKey: "store",
|
|
34
|
+
testIDPrefix: ZUSTAND_TEST_ID,
|
|
35
|
+
slug: slugForTestID,
|
|
36
|
+
howItWorks: "Patterns hide matching stores from both the Stores tab and the Events tab. Tap a store above to quickly add it as a filter.",
|
|
37
|
+
examples: EXAMPLES
|
|
175
38
|
});
|
|
176
|
-
}
|
|
177
|
-
const styles = StyleSheet.create({
|
|
178
|
-
container: {
|
|
179
|
-
flex: 1,
|
|
180
|
-
// Transparent so the night sheet's star field shows through.
|
|
181
|
-
backgroundColor: "transparent"
|
|
182
|
-
},
|
|
183
|
-
scrollContent: {
|
|
184
|
-
paddingTop: 16,
|
|
185
|
-
paddingHorizontal: 16,
|
|
186
|
-
paddingBottom: 24,
|
|
187
|
-
gap: 16
|
|
188
|
-
},
|
|
189
|
-
section: {
|
|
190
|
-
backgroundColor: night.color.surface,
|
|
191
|
-
borderRadius: 16,
|
|
192
|
-
borderWidth: 1,
|
|
193
|
-
borderColor: night.color.border,
|
|
194
|
-
overflow: "hidden"
|
|
195
|
-
},
|
|
196
|
-
storeList: {
|
|
197
|
-
maxHeight: 180
|
|
198
|
-
},
|
|
199
|
-
storeListContent: {
|
|
200
|
-
paddingHorizontal: 16,
|
|
201
|
-
paddingTop: 8,
|
|
202
|
-
paddingBottom: 16,
|
|
203
|
-
gap: 10
|
|
204
|
-
},
|
|
205
|
-
storeRow: {
|
|
206
|
-
flexDirection: "row",
|
|
207
|
-
alignItems: "center",
|
|
208
|
-
gap: 10,
|
|
209
|
-
paddingVertical: 10,
|
|
210
|
-
paddingHorizontal: 12,
|
|
211
|
-
backgroundColor: night.color.surfaceElevated,
|
|
212
|
-
borderRadius: 8,
|
|
213
|
-
borderWidth: 1,
|
|
214
|
-
borderColor: night.color.border
|
|
215
|
-
},
|
|
216
|
-
storeRowFiltered: {
|
|
217
|
-
opacity: 0.45
|
|
218
|
-
},
|
|
219
|
-
storeDot: {
|
|
220
|
-
width: 7,
|
|
221
|
-
height: 7,
|
|
222
|
-
borderRadius: 3.5
|
|
223
|
-
},
|
|
224
|
-
storeRowText: {
|
|
225
|
-
flex: 1,
|
|
226
|
-
fontSize: 12,
|
|
227
|
-
fontWeight: "600",
|
|
228
|
-
fontFamily: "monospace"
|
|
229
|
-
},
|
|
230
|
-
storeRowTextFiltered: {
|
|
231
|
-
color: night.color.textTertiary,
|
|
232
|
-
textDecorationLine: "line-through"
|
|
233
|
-
},
|
|
234
|
-
filteredLabel: {
|
|
235
|
-
fontSize: 10,
|
|
236
|
-
fontWeight: "500",
|
|
237
|
-
color: night.color.textTertiary,
|
|
238
|
-
fontFamily: "monospace"
|
|
239
|
-
},
|
|
240
|
-
addLabel: {
|
|
241
|
-
fontSize: 10,
|
|
242
|
-
fontWeight: "600",
|
|
243
|
-
fontFamily: "monospace"
|
|
244
|
-
},
|
|
245
|
-
addInputWrapper: {
|
|
246
|
-
paddingHorizontal: 16,
|
|
247
|
-
paddingTop: 8,
|
|
248
|
-
paddingBottom: 8
|
|
249
|
-
},
|
|
250
|
-
patternList: {
|
|
251
|
-
paddingHorizontal: 16,
|
|
252
|
-
paddingBottom: 16,
|
|
253
|
-
gap: 8
|
|
254
|
-
},
|
|
255
|
-
patternRow: {
|
|
256
|
-
flexDirection: "row",
|
|
257
|
-
alignItems: "center",
|
|
258
|
-
justifyContent: "space-between",
|
|
259
|
-
paddingVertical: 10,
|
|
260
|
-
paddingHorizontal: 12,
|
|
261
|
-
backgroundColor: night.color.surfaceElevated,
|
|
262
|
-
borderRadius: 8,
|
|
263
|
-
borderWidth: 1,
|
|
264
|
-
borderColor: night.color.border,
|
|
265
|
-
gap: 8
|
|
266
|
-
},
|
|
267
|
-
patternText: {
|
|
268
|
-
flex: 1,
|
|
269
|
-
fontSize: 12,
|
|
270
|
-
fontFamily: "monospace",
|
|
271
|
-
color: night.color.text
|
|
272
|
-
},
|
|
273
|
-
howItWorksText: {
|
|
274
|
-
fontSize: 11,
|
|
275
|
-
color: night.color.textSecondary,
|
|
276
|
-
lineHeight: 16,
|
|
277
|
-
marginTop: 8,
|
|
278
|
-
paddingHorizontal: 16,
|
|
279
|
-
fontFamily: "monospace"
|
|
280
|
-
},
|
|
281
|
-
examplesContainer: {
|
|
282
|
-
paddingTop: 8,
|
|
283
|
-
paddingHorizontal: 16,
|
|
284
|
-
paddingBottom: 16,
|
|
285
|
-
borderTopWidth: 1,
|
|
286
|
-
borderTopColor: night.color.border,
|
|
287
|
-
marginTop: 12
|
|
288
|
-
},
|
|
289
|
-
examplesTitle: {
|
|
290
|
-
fontSize: 10,
|
|
291
|
-
fontWeight: "600",
|
|
292
|
-
color: night.color.textTertiary,
|
|
293
|
-
fontFamily: "monospace",
|
|
294
|
-
letterSpacing: 0.5,
|
|
295
|
-
marginBottom: 6
|
|
296
|
-
},
|
|
297
|
-
exampleItem: {
|
|
298
|
-
fontSize: 10,
|
|
299
|
-
color: night.color.textTertiary,
|
|
300
|
-
fontFamily: "monospace",
|
|
301
|
-
lineHeight: 16
|
|
302
|
-
}
|
|
303
|
-
});
|
|
39
|
+
}
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { useState, useMemo, useRef, useCallback, useEffect } from "react";
|
|
12
|
-
import { View, Text, StyleSheet, FlatList,
|
|
13
|
-
import { JsModal, ModalHeader, TabSelector, macOSColors, night, Search, Filter, Power,
|
|
12
|
+
import { View, Text, StyleSheet, FlatList, TouchableOpacity } from "react-native";
|
|
13
|
+
import { JsModal, ModalHeader, TabSelector, macOSColors, night, ToolEmptyState, resolveListEmptyState, Search, Filter, Power, Lock, devToolsStorageKeys, ProUpgradeModal, PowerToggleButton, ToolbarCopyButton, ToolbarClearButton, truncatePayload, TickProvider, useLimit, windowDragProps, EVENT_LIST_PRESET } from "@buoy-gg/shared-ui";
|
|
14
14
|
import { useIsPro } from "@buoy-gg/license";
|
|
15
15
|
import { useZustandStateChanges } from "../hooks/useZustandStateChanges";
|
|
16
16
|
import { ZustandStateChangeItem } from "./ZustandStateChangeItem";
|
|
@@ -21,33 +21,32 @@ import { ZustandEventFilterView } from "./ZustandEventFilterView";
|
|
|
21
21
|
import { ZUSTAND_TEST_ID, buildChangeRowTestIDs } from "../utils/testIDs";
|
|
22
22
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* `removeClippedSubviews` unmounts the views of rows scrolled out of the
|
|
27
|
-
* window, which is what makes a long change list cheap — and which makes
|
|
28
|
-
* Detox report a perfectly rendered row as un-hittable. The env var is
|
|
29
|
-
* inlined at bundle time, so a bundler that does NOT inline it leaves the
|
|
30
|
-
* production behaviour exactly as it was.
|
|
24
|
+
* The change log's "nothing here". Everything but the nouns is the shared
|
|
25
|
+
* precedence — see `ToolEmptyState` in shared-ui for why it lives there.
|
|
31
26
|
*/
|
|
32
|
-
const CLIP_OFFSCREEN_CELLS = !(typeof process !== "undefined" && process.env?.EXPO_PUBLIC_E2E);
|
|
33
27
|
function EventsEmptyState({
|
|
34
|
-
isEnabled
|
|
28
|
+
isEnabled,
|
|
29
|
+
searchText,
|
|
30
|
+
totalChanges
|
|
35
31
|
}) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
32
|
+
const content = resolveListEmptyState({
|
|
33
|
+
subject: "changes",
|
|
34
|
+
search: searchText,
|
|
35
|
+
total: totalChanges,
|
|
36
|
+
capturing: isEnabled,
|
|
37
|
+
waiting: {
|
|
38
|
+
otto: "waiting",
|
|
39
|
+
title: "No state changes",
|
|
40
|
+
message: "State changes will appear here as stores update."
|
|
41
|
+
},
|
|
42
|
+
paused: {
|
|
43
|
+
title: "No state changes",
|
|
44
|
+
message: "Enable capture to start recording state changes"
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
return /*#__PURE__*/_jsx(ToolEmptyState, {
|
|
48
|
+
...content,
|
|
49
|
+
testID: `${ZUSTAND_TEST_ID}-empty`
|
|
51
50
|
});
|
|
52
51
|
}
|
|
53
52
|
export function ZustandModal({
|
|
@@ -77,6 +76,7 @@ export function ZustandModal({
|
|
|
77
76
|
const [showFilters, setShowFilters] = useState(false);
|
|
78
77
|
const [ignoredPatterns, setIgnoredPatterns] = useState(new Set());
|
|
79
78
|
const {
|
|
79
|
+
stateChanges,
|
|
80
80
|
filteredChanges,
|
|
81
81
|
filter,
|
|
82
82
|
setFilter,
|
|
@@ -99,6 +99,16 @@ export function ZustandModal({
|
|
|
99
99
|
return stores.filter(s => !Array.from(ignoredPatterns).some(p => s.name.toLowerCase().includes(p.toLowerCase())));
|
|
100
100
|
}, [stores, ignoredPatterns]);
|
|
101
101
|
|
|
102
|
+
/**
|
|
103
|
+
* How many changes this store has captured, before the search — the
|
|
104
|
+
* denominator the scoped empty state needs so "clear the search" is only
|
|
105
|
+
* offered when clearing it would actually show something.
|
|
106
|
+
*/
|
|
107
|
+
const scopedTotalForStore = useMemo(() => {
|
|
108
|
+
if (!selectedHistoryStore) return 0;
|
|
109
|
+
return stateChanges.filter(c => c.storeName === selectedHistoryStore).length;
|
|
110
|
+
}, [stateChanges, selectedHistoryStore]);
|
|
111
|
+
|
|
102
112
|
// Scope to a store BEFORE the tier cap, not after.
|
|
103
113
|
//
|
|
104
114
|
// The other way round, the cap was applied to the whole log and a store's
|
|
@@ -364,49 +374,24 @@ export function ZustandModal({
|
|
|
364
374
|
onBack: onBack
|
|
365
375
|
}), /*#__PURE__*/_jsx(ModalHeader.Content, {
|
|
366
376
|
title: "",
|
|
367
|
-
children: isSearchActive ? /*#__PURE__*/
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
// header went back to its TABS — whose counts are of the
|
|
386
|
-
// WHOLE capture on the stores side — while the list stayed
|
|
387
|
-
// filtered, so the tool showed "Stores (4)" above a single
|
|
388
|
-
// row and offered nothing to explain the gap. The only way
|
|
389
|
-
// back was to reopen a field the user had no reason to think
|
|
390
|
-
// was still armed.
|
|
391
|
-
,
|
|
392
|
-
onBlur: () => {
|
|
393
|
-
if (searchText.length === 0) setIsSearchActive(false);
|
|
394
|
-
},
|
|
395
|
-
accessibilityLabel: "Search Zustand stores and events",
|
|
396
|
-
autoCapitalize: "none",
|
|
397
|
-
autoCorrect: false,
|
|
398
|
-
returnKeyType: "search"
|
|
399
|
-
}), /*#__PURE__*/_jsx(TouchableOpacity, {
|
|
400
|
-
onPress: closeSearch,
|
|
401
|
-
style: styles.headerSearchClear,
|
|
402
|
-
testID: `${ZUSTAND_TEST_ID}-search-clear`,
|
|
403
|
-
accessibilityRole: "button",
|
|
404
|
-
accessibilityLabel: "Close search",
|
|
405
|
-
children: /*#__PURE__*/_jsx(X, {
|
|
406
|
-
size: 14,
|
|
407
|
-
color: night.color.textSecondary
|
|
408
|
-
})
|
|
409
|
-
})]
|
|
377
|
+
children: isSearchActive ? /*#__PURE__*/_jsx(ModalHeader.Search, {
|
|
378
|
+
value: searchText,
|
|
379
|
+
onChangeText: handleSearch,
|
|
380
|
+
onCancel: closeSearch,
|
|
381
|
+
placeholder: activeTab === "stores" ? "Search stores..." : "Search events...",
|
|
382
|
+
accessibilityLabel: "Search Zustand stores and events",
|
|
383
|
+
iconSize: 14,
|
|
384
|
+
testID: `${ZUSTAND_TEST_ID}-search-field`,
|
|
385
|
+
containerTestID: `${ZUSTAND_TEST_ID}-search`,
|
|
386
|
+
clearTestID: `${ZUSTAND_TEST_ID}-search-clear`
|
|
387
|
+
// Stay open while the query is still narrowing the list: the
|
|
388
|
+
// header would otherwise go back to its TABS — whose counts are
|
|
389
|
+
// of the WHOLE capture — above a filtered list, with nothing to
|
|
390
|
+
// explain the gap.
|
|
391
|
+
,
|
|
392
|
+
onBlur: () => {
|
|
393
|
+
if (searchText.length === 0) setIsSearchActive(false);
|
|
394
|
+
}
|
|
410
395
|
}) : /*#__PURE__*/_jsx(TabSelector, {
|
|
411
396
|
tabs: [{
|
|
412
397
|
key: "stores",
|
|
@@ -501,24 +486,25 @@ export function ZustandModal({
|
|
|
501
486
|
keyExtractor: keyExtractor,
|
|
502
487
|
contentContainerStyle: styles.listContent,
|
|
503
488
|
showsVerticalScrollIndicator: true,
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
maxToRenderPerBatch: 8,
|
|
507
|
-
windowSize: 5,
|
|
508
|
-
updateCellsBatchingPeriod: 50
|
|
509
|
-
}) : /*#__PURE__*/_jsxs(View, {
|
|
510
|
-
style: styles.emptyState,
|
|
489
|
+
...EVENT_LIST_PRESET
|
|
490
|
+
}) : /*#__PURE__*/_jsx(ToolEmptyState, {
|
|
511
491
|
testID: `${ZUSTAND_TEST_ID}-history-empty`,
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
492
|
+
...resolveListEmptyState({
|
|
493
|
+
subject: "changes",
|
|
494
|
+
search: searchText,
|
|
495
|
+
// Scoped to one store, so the count is this store's, not the log's.
|
|
496
|
+
total: searchText.trim() ? scopedTotalForStore : 0,
|
|
497
|
+
capturing: isEnabled,
|
|
498
|
+
waiting: {
|
|
499
|
+
otto: "waiting",
|
|
500
|
+
title: "No history yet",
|
|
501
|
+
message: `State changes for ${selectedHistoryStore} will appear here.`
|
|
502
|
+
},
|
|
503
|
+
paused: {
|
|
504
|
+
title: "No history yet",
|
|
505
|
+
message: `Capture is off, so nothing is being recorded for ${selectedHistoryStore}.`
|
|
506
|
+
}
|
|
507
|
+
})
|
|
522
508
|
});
|
|
523
509
|
}
|
|
524
510
|
|
|
@@ -534,19 +520,15 @@ export function ZustandModal({
|
|
|
534
520
|
/*#__PURE__*/
|
|
535
521
|
// The store fell out of the registry mid-look (a remote mirror
|
|
536
522
|
// rebuilding, a dev reload). Say so rather than rendering nothing.
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
}), /*#__PURE__*/_jsxs(Text, {
|
|
547
|
-
style: styles.emptyText,
|
|
548
|
-
children: [selectedStoreName, " is not in the registry any more."]
|
|
549
|
-
})]
|
|
523
|
+
_jsx(ToolEmptyState, {
|
|
524
|
+
testID: `${ZUSTAND_TEST_ID}-store-detail-gone`
|
|
525
|
+
// ghost = "stale or detached: an unmounted component, an old
|
|
526
|
+
// snapshot" — exactly what the store you are looking at has become.
|
|
527
|
+
// No other tool has this cause, so it skips the resolver.
|
|
528
|
+
,
|
|
529
|
+
otto: "ghost",
|
|
530
|
+
title: "Store no longer registered",
|
|
531
|
+
message: `${selectedStoreName} is not in the registry any more.`
|
|
550
532
|
});
|
|
551
533
|
}
|
|
552
534
|
|
|
@@ -600,13 +582,11 @@ export function ZustandModal({
|
|
|
600
582
|
keyExtractor: keyExtractor,
|
|
601
583
|
contentContainerStyle: styles.listContent,
|
|
602
584
|
showsVerticalScrollIndicator: true,
|
|
603
|
-
|
|
604
|
-
initialNumToRender: 12,
|
|
605
|
-
maxToRenderPerBatch: 8,
|
|
606
|
-
windowSize: 5,
|
|
607
|
-
updateCellsBatchingPeriod: 50
|
|
585
|
+
...EVENT_LIST_PRESET
|
|
608
586
|
}) : /*#__PURE__*/_jsx(EventsEmptyState, {
|
|
609
|
-
isEnabled: isEnabled
|
|
587
|
+
isEnabled: isEnabled,
|
|
588
|
+
searchText: searchText,
|
|
589
|
+
totalChanges: stats.totalChanges
|
|
610
590
|
})]
|
|
611
591
|
});
|
|
612
592
|
};
|
|
@@ -715,27 +695,6 @@ const styles = StyleSheet.create({
|
|
|
715
695
|
// Transparent so JsModal's ToolBackground shows through (the modal paints opaque base beneath it).
|
|
716
696
|
backgroundColor: "transparent"
|
|
717
697
|
},
|
|
718
|
-
headerSearchContainer: {
|
|
719
|
-
flexDirection: "row",
|
|
720
|
-
alignItems: "center",
|
|
721
|
-
backgroundColor: night.color.surfaceElevated,
|
|
722
|
-
borderRadius: night.radius.row,
|
|
723
|
-
borderWidth: 1,
|
|
724
|
-
borderColor: night.color.border,
|
|
725
|
-
paddingHorizontal: 12,
|
|
726
|
-
paddingVertical: 5
|
|
727
|
-
},
|
|
728
|
-
headerSearchInput: {
|
|
729
|
-
flex: 1,
|
|
730
|
-
color: night.color.text,
|
|
731
|
-
fontSize: 13,
|
|
732
|
-
marginLeft: 6,
|
|
733
|
-
paddingVertical: 2
|
|
734
|
-
},
|
|
735
|
-
headerSearchClear: {
|
|
736
|
-
marginLeft: 6,
|
|
737
|
-
padding: 4
|
|
738
|
-
},
|
|
739
698
|
headerActionButton: {
|
|
740
699
|
width: 32,
|
|
741
700
|
height: 32,
|
|
@@ -774,23 +733,6 @@ const styles = StyleSheet.create({
|
|
|
774
733
|
paddingTop: 8,
|
|
775
734
|
paddingBottom: 20
|
|
776
735
|
},
|
|
777
|
-
emptyState: {
|
|
778
|
-
alignItems: "center",
|
|
779
|
-
paddingVertical: 40
|
|
780
|
-
},
|
|
781
|
-
emptyTitle: {
|
|
782
|
-
color: night.color.text,
|
|
783
|
-
fontSize: 14,
|
|
784
|
-
fontWeight: "600",
|
|
785
|
-
marginTop: 12,
|
|
786
|
-
marginBottom: 6
|
|
787
|
-
},
|
|
788
|
-
emptyText: {
|
|
789
|
-
color: night.color.textTertiary,
|
|
790
|
-
fontSize: 12,
|
|
791
|
-
textAlign: "center",
|
|
792
|
-
lineHeight: 18
|
|
793
|
-
},
|
|
794
736
|
lockedBanner: {
|
|
795
737
|
flexDirection: "row",
|
|
796
738
|
alignItems: "center",
|