@buoy-gg/zustand 7.0.33 → 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 +20 -85
- package/lib/commonjs/zustand/components/ZustandStateChangeItem.js +8 -61
- package/lib/commonjs/zustand/components/ZustandStateInfoView.js +2 -46
- 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 +22 -87
- package/lib/module/zustand/components/ZustandStateChangeItem.js +10 -63
- package/lib/module/zustand/components/ZustandStateInfoView.js +1 -45
- 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/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
|
@@ -4,10 +4,14 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.zustandStateStore = void 0;
|
|
7
|
+
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
7
8
|
/**
|
|
8
9
|
* Zustand state store - captures and stores Zustand state changes
|
|
9
10
|
*
|
|
10
|
-
*
|
|
11
|
+
* The generic event-log half (ring buffer, listeners, clear-forwarding for
|
|
12
|
+
* the desktop mirror, pause/suppress flags, hydrate-by-id) is the shared
|
|
13
|
+
* `ChangeLog`; this file owns the zustand-specific half — the store registry,
|
|
14
|
+
* snapshots, mirror rebuild, filters and stats.
|
|
11
15
|
*/
|
|
12
16
|
|
|
13
17
|
// ============================================
|
|
@@ -40,168 +44,33 @@ const STORE_COLORS = {
|
|
|
40
44
|
* Get consistent color for a store based on its name
|
|
41
45
|
*/
|
|
42
46
|
function getStoreColor(storeName) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
// Check for exact match
|
|
46
|
-
if (STORE_COLORS[lowerName]) {
|
|
47
|
-
return STORE_COLORS[lowerName];
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Check for partial matches
|
|
51
|
-
for (const [key, color] of Object.entries(STORE_COLORS)) {
|
|
52
|
-
if (lowerName.includes(key)) {
|
|
53
|
-
return color;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Generate consistent hex color from name hash
|
|
58
|
-
const hash = storeName.split("").reduce((acc, char) => acc + char.charCodeAt(0), 0);
|
|
59
|
-
const hue = hash * 137 % 360;
|
|
60
|
-
// Convert HSL to hex so "color + alpha_hex" pattern works everywhere
|
|
61
|
-
const s = 0.7;
|
|
62
|
-
const l = 0.6;
|
|
63
|
-
const c = (1 - Math.abs(2 * l - 1)) * s;
|
|
64
|
-
const x = c * (1 - Math.abs(hue / 60 % 2 - 1));
|
|
65
|
-
const m = l - c / 2;
|
|
66
|
-
let r = 0,
|
|
67
|
-
g = 0,
|
|
68
|
-
b = 0;
|
|
69
|
-
if (hue < 60) {
|
|
70
|
-
r = c;
|
|
71
|
-
g = x;
|
|
72
|
-
} else if (hue < 120) {
|
|
73
|
-
r = x;
|
|
74
|
-
g = c;
|
|
75
|
-
} else if (hue < 180) {
|
|
76
|
-
g = c;
|
|
77
|
-
b = x;
|
|
78
|
-
} else if (hue < 240) {
|
|
79
|
-
g = x;
|
|
80
|
-
b = c;
|
|
81
|
-
} else if (hue < 300) {
|
|
82
|
-
r = x;
|
|
83
|
-
b = c;
|
|
84
|
-
} else {
|
|
85
|
-
r = c;
|
|
86
|
-
b = x;
|
|
87
|
-
}
|
|
88
|
-
const toHex = v => Math.round((v + m) * 255).toString(16).padStart(2, "0");
|
|
89
|
-
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
47
|
+
return (0, _sharedUi.entityColor)(storeName, STORE_COLORS);
|
|
90
48
|
}
|
|
91
49
|
|
|
92
|
-
// ============================================
|
|
93
|
-
// Helper Functions
|
|
94
|
-
// ============================================
|
|
95
|
-
|
|
96
50
|
/**
|
|
97
51
|
* Format payload preview for display
|
|
98
52
|
*/
|
|
99
53
|
function formatPartialPreview(partial, maxLength = 40) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
if (typeof partial === "string") {
|
|
107
|
-
return partial.length > maxLength ? `"${partial.slice(0, maxLength - 3)}..."` : `"${partial}"`;
|
|
108
|
-
}
|
|
109
|
-
if (typeof partial === "number" || typeof partial === "boolean") {
|
|
110
|
-
return String(partial);
|
|
111
|
-
}
|
|
112
|
-
if (Array.isArray(partial)) {
|
|
113
|
-
if (partial.length === 0) return "[]";
|
|
114
|
-
const preview = JSON.stringify(partial);
|
|
115
|
-
return preview.length > maxLength ? `[${partial.length} items]` : preview;
|
|
116
|
-
}
|
|
117
|
-
if (typeof partial === "object") {
|
|
118
|
-
const keys = Object.keys(partial);
|
|
119
|
-
if (keys.length === 0) return "{}";
|
|
120
|
-
const preview = JSON.stringify(partial);
|
|
121
|
-
if (preview.length <= maxLength) return preview;
|
|
122
|
-
return `{ ${keys.length} keys }`;
|
|
123
|
-
}
|
|
124
|
-
return String(partial).slice(0, maxLength);
|
|
125
|
-
} catch {
|
|
126
|
-
return "[complex]";
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Calculate state diff summary.
|
|
132
|
-
*
|
|
133
|
-
* `changedCount` counts the keys in `changedKeys` and nothing else. The two
|
|
134
|
-
* used to disagree: a state object that was REPLACED without any top-level
|
|
135
|
-
* value moving — which is exactly what zustand's persist middleware does when
|
|
136
|
-
* it rehydrates — reported one changed key and named none, so the row said
|
|
137
|
-
* "1 key changed" and the detail page it opened drew no CHANGED KEYS card at
|
|
138
|
-
* all. `summary` is where a change with no nameable keys is described.
|
|
139
|
-
*/
|
|
140
|
-
function getStateDiffSummary(prevState, nextState) {
|
|
141
|
-
if (prevState === nextState) {
|
|
142
|
-
return {
|
|
143
|
-
summary: "no change",
|
|
144
|
-
changedKeys: [],
|
|
145
|
-
changedCount: 0
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
if (typeof prevState !== "object" || typeof nextState !== "object" || prevState === null || nextState === null) {
|
|
149
|
-
return {
|
|
150
|
-
summary: "changed",
|
|
151
|
-
changedKeys: [],
|
|
152
|
-
changedCount: 0
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
const prevKeys = Object.keys(prevState);
|
|
156
|
-
const nextKeys = Object.keys(nextState);
|
|
157
|
-
const added = nextKeys.filter(k => !prevKeys.includes(k));
|
|
158
|
-
const removed = prevKeys.filter(k => !nextKeys.includes(k));
|
|
159
|
-
const changed = [];
|
|
160
|
-
for (const key of prevKeys) {
|
|
161
|
-
if (nextKeys.includes(key) && prevState[key] !== nextState[key]) {
|
|
162
|
-
changed.push(key);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
const allChangedKeys = [...added, ...removed, ...changed];
|
|
166
|
-
const parts = [];
|
|
167
|
-
if (added.length > 0) parts.push(`+${added.length}`);
|
|
168
|
-
if (removed.length > 0) parts.push(`-${removed.length}`);
|
|
169
|
-
if (changed.length > 0) parts.push(`~${changed.length}`);
|
|
170
|
-
const totalChanged = allChangedKeys.length;
|
|
171
|
-
if (parts.length === 0) {
|
|
172
|
-
return {
|
|
173
|
-
summary: "nested change",
|
|
174
|
-
changedKeys: [],
|
|
175
|
-
changedCount: 0
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
return {
|
|
179
|
-
summary: `${parts.join(" ")} ${totalChanged === 1 ? "key" : "keys"}`,
|
|
180
|
-
changedKeys: allChangedKeys,
|
|
181
|
-
changedCount: totalChanged
|
|
182
|
-
};
|
|
54
|
+
return (0, _sharedUi.formatValuePreview)(partial, {
|
|
55
|
+
maxLength,
|
|
56
|
+
undefinedLabel: "",
|
|
57
|
+
functionLabel: "(updater fn)"
|
|
58
|
+
});
|
|
183
59
|
}
|
|
184
60
|
|
|
185
61
|
// ============================================
|
|
186
62
|
// Zustand State Store
|
|
187
63
|
// ============================================
|
|
188
64
|
|
|
189
|
-
class ZustandStateStore {
|
|
190
|
-
stateChanges = [];
|
|
65
|
+
class ZustandStateStore extends _sharedUi.ChangeLog {
|
|
191
66
|
stores = new Map();
|
|
192
|
-
listeners = new Set();
|
|
193
67
|
storeListeners = new Set();
|
|
194
|
-
clearListeners = new Set();
|
|
195
|
-
maxChanges = 200;
|
|
196
|
-
idCounter = 0;
|
|
197
|
-
isEnabled = true;
|
|
198
|
-
captureSuppressed = false;
|
|
199
68
|
remoteSetStateHandler = null;
|
|
200
69
|
|
|
201
70
|
// ---- State Change Tracking ----
|
|
202
71
|
|
|
203
72
|
addStateChange(params) {
|
|
204
|
-
if (!this.
|
|
73
|
+
if (!this.canRecord()) return;
|
|
205
74
|
const {
|
|
206
75
|
storeName,
|
|
207
76
|
partial,
|
|
@@ -213,16 +82,20 @@ class ZustandStateStore {
|
|
|
213
82
|
isPersisted = false
|
|
214
83
|
} = params;
|
|
215
84
|
const hasStateChange = prevState !== nextState;
|
|
85
|
+
// `changedCount` counts the keys in `changedKeys` and nothing else, so the
|
|
86
|
+
// row's "N keys changed" and the detail page's CHANGED KEYS card agree —
|
|
87
|
+
// including the persist-rehydrate case where the object is replaced
|
|
88
|
+
// without any top-level value moving.
|
|
216
89
|
const {
|
|
217
90
|
summary,
|
|
218
91
|
changedKeys,
|
|
219
92
|
changedCount
|
|
220
|
-
} =
|
|
93
|
+
} = (0, _sharedUi.shallowDiffSummary)(prevState, nextState);
|
|
221
94
|
const partialPreview = formatPartialPreview(partial);
|
|
222
95
|
const isSlowUpdate = (duration ?? 0) > 16;
|
|
223
96
|
const resolvedCategory = category ?? (replace ? "replace" : "setState");
|
|
224
97
|
const stateChange = {
|
|
225
|
-
id:
|
|
98
|
+
id: this.nextId(),
|
|
226
99
|
storeName,
|
|
227
100
|
timestamp: Date.now(),
|
|
228
101
|
prevState,
|
|
@@ -239,7 +112,7 @@ class ZustandStateStore {
|
|
|
239
112
|
isSlowUpdate,
|
|
240
113
|
isPersisted
|
|
241
114
|
};
|
|
242
|
-
this.
|
|
115
|
+
this.push(stateChange);
|
|
243
116
|
|
|
244
117
|
// Update store change count
|
|
245
118
|
const storeInfo = this.stores.get(storeName);
|
|
@@ -249,36 +122,17 @@ class ZustandStateStore {
|
|
|
249
122
|
this.notifyListeners();
|
|
250
123
|
}
|
|
251
124
|
getStateChanges() {
|
|
252
|
-
return
|
|
125
|
+
return this.getAll();
|
|
253
126
|
}
|
|
254
127
|
getStateChangeById(id) {
|
|
255
|
-
return this.
|
|
128
|
+
return this.getById(id);
|
|
256
129
|
}
|
|
257
130
|
clearStateChanges() {
|
|
258
|
-
this.stateChanges = [];
|
|
259
131
|
// Reset counts
|
|
260
132
|
for (const store of this.stores.values()) {
|
|
261
133
|
store.stateChangeCount = 0;
|
|
262
134
|
}
|
|
263
|
-
this.
|
|
264
|
-
this.clearListeners.forEach(listener => {
|
|
265
|
-
try {
|
|
266
|
-
listener();
|
|
267
|
-
} catch {
|
|
268
|
-
// Ignore listener errors
|
|
269
|
-
}
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Listen for clearStateChanges() calls. Used in remote mirror mode to
|
|
275
|
-
* forward a clear performed in the dashboard UI to the synced device.
|
|
276
|
-
*/
|
|
277
|
-
onClear(listener) {
|
|
278
|
-
this.clearListeners.add(listener);
|
|
279
|
-
return () => {
|
|
280
|
-
this.clearListeners.delete(listener);
|
|
281
|
-
};
|
|
135
|
+
this.clear();
|
|
282
136
|
}
|
|
283
137
|
|
|
284
138
|
// ---- Remote Mirror Mode ----
|
|
@@ -291,33 +145,19 @@ class ZustandStateStore {
|
|
|
291
145
|
* flag so the UI's pause button freezes the reseed.
|
|
292
146
|
*/
|
|
293
147
|
replaceStateChanges(changes) {
|
|
294
|
-
if (!this.
|
|
295
|
-
this.stateChanges = changes.slice(0, this.maxChanges);
|
|
148
|
+
if (!this.getEnabled()) return;
|
|
296
149
|
for (const store of this.stores.values()) {
|
|
297
150
|
store.stateChangeCount = 0;
|
|
298
151
|
}
|
|
299
|
-
|
|
152
|
+
const kept = changes.slice(0, this.max);
|
|
153
|
+
for (const change of kept) {
|
|
300
154
|
const store = this.stores.get(change.storeName);
|
|
301
155
|
if (store) store.stateChangeCount++;
|
|
302
156
|
}
|
|
303
|
-
this.
|
|
157
|
+
this.setItems(kept);
|
|
304
158
|
this.notifyStoreListeners();
|
|
305
159
|
}
|
|
306
160
|
|
|
307
|
-
/**
|
|
308
|
-
* Permanently suppress local capture. Use when this store acts as a mirror
|
|
309
|
-
* of a remote device's state changes (e.g. the desktop dashboard): data
|
|
310
|
-
* arrives via replaceFromSnapshot() and local middleware must never record.
|
|
311
|
-
*/
|
|
312
|
-
disableCapture() {
|
|
313
|
-
this.captureSuppressed = true;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
/** Whether the store is in remote mirror mode (capture suppressed). */
|
|
317
|
-
isCaptureSuppressed() {
|
|
318
|
-
return this.captureSuppressed;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
161
|
/**
|
|
322
162
|
* Serializable snapshot of the tracked stores (registry metadata plus each
|
|
323
163
|
* store's current state). Used by the sync adapter on the device side —
|
|
@@ -362,18 +202,18 @@ class ZustandStateStore {
|
|
|
362
202
|
* the enabled flag so the UI's pause button freezes the mirror.
|
|
363
203
|
*/
|
|
364
204
|
replaceFromSnapshot(changes, storeSnapshots) {
|
|
365
|
-
if (!this.
|
|
205
|
+
if (!this.getEnabled()) return;
|
|
366
206
|
|
|
367
207
|
// Preserve trees a remote mirror already hydrated — wire-form snapshots
|
|
368
208
|
// always carry markers, and a later snapshot must not blank the detail pane.
|
|
369
209
|
const hydrated = new Map();
|
|
370
|
-
for (const existing of this.
|
|
210
|
+
for (const existing of this.items) {
|
|
371
211
|
const prev = existing.prevState;
|
|
372
212
|
if (!prev || prev.__buoyStateOnDevice !== true) {
|
|
373
213
|
hydrated.set(existing.id, existing);
|
|
374
214
|
}
|
|
375
215
|
}
|
|
376
|
-
|
|
216
|
+
const merged = changes.slice(0, this.max).map(change => {
|
|
377
217
|
const kept = hydrated.get(change.id);
|
|
378
218
|
const incoming = change.prevState;
|
|
379
219
|
return kept && incoming?.__buoyStateOnDevice === true ? {
|
|
@@ -417,7 +257,7 @@ class ZustandStateStore {
|
|
|
417
257
|
color: snapshot.color
|
|
418
258
|
}];
|
|
419
259
|
}));
|
|
420
|
-
this.
|
|
260
|
+
this.setItems(merged);
|
|
421
261
|
this.notifyStoreListeners();
|
|
422
262
|
}
|
|
423
263
|
|
|
@@ -426,13 +266,7 @@ class ZustandStateStore {
|
|
|
426
266
|
* device via the adapter's `getChangeDetail`. Remote-mirror (desktop) only.
|
|
427
267
|
*/
|
|
428
268
|
hydrateChange(id, patch) {
|
|
429
|
-
|
|
430
|
-
if (index === -1) return;
|
|
431
|
-
this.stateChanges = [...this.stateChanges.slice(0, index), {
|
|
432
|
-
...this.stateChanges[index],
|
|
433
|
-
...patch
|
|
434
|
-
}, ...this.stateChanges.slice(index + 1)];
|
|
435
|
-
this.notifyListeners();
|
|
269
|
+
this.hydrateById(id, patch);
|
|
436
270
|
}
|
|
437
271
|
|
|
438
272
|
/**
|
|
@@ -485,7 +319,7 @@ class ZustandStateStore {
|
|
|
485
319
|
// ---- Filtering ----
|
|
486
320
|
|
|
487
321
|
filterStateChanges(filter) {
|
|
488
|
-
let filtered =
|
|
322
|
+
let filtered = this.getAll();
|
|
489
323
|
if (filter.searchText) {
|
|
490
324
|
const search = filter.searchText.toLowerCase();
|
|
491
325
|
filtered = filtered.filter(c => c.storeName.toLowerCase().includes(search) || c.partialPreview.toLowerCase().includes(search) || c.changedKeys.some(k => k.toLowerCase().includes(search)));
|
|
@@ -502,10 +336,10 @@ class ZustandStateStore {
|
|
|
502
336
|
// ---- Stats ----
|
|
503
337
|
|
|
504
338
|
getStats() {
|
|
505
|
-
const total = this.
|
|
506
|
-
const withChanges = this.
|
|
339
|
+
const total = this.items.length;
|
|
340
|
+
const withChanges = this.items.filter(c => c.hasStateChange).length;
|
|
507
341
|
const storeCount = this.stores.size;
|
|
508
|
-
const durations = this.
|
|
342
|
+
const durations = this.items.filter(c => c.duration !== undefined).map(c => c.duration);
|
|
509
343
|
const avgDuration = durations.length > 0 ? durations.reduce((sum, d) => sum + d, 0) / durations.length : 0;
|
|
510
344
|
return {
|
|
511
345
|
totalChanges: total,
|
|
@@ -521,38 +355,18 @@ class ZustandStateStore {
|
|
|
521
355
|
|
|
522
356
|
// ---- Enable / Disable ----
|
|
523
357
|
|
|
524
|
-
setEnabled(enabled) {
|
|
525
|
-
this.isEnabled = enabled;
|
|
526
|
-
}
|
|
527
|
-
getEnabled() {
|
|
528
|
-
return this.isEnabled;
|
|
529
|
-
}
|
|
530
358
|
setMaxChanges(max) {
|
|
531
|
-
this.
|
|
532
|
-
if (this.stateChanges.length > max) {
|
|
533
|
-
this.stateChanges = this.stateChanges.slice(0, max);
|
|
534
|
-
this.notifyListeners();
|
|
535
|
-
}
|
|
359
|
+
this.setMax(max);
|
|
536
360
|
}
|
|
537
361
|
|
|
538
362
|
// ---- Subscriptions ----
|
|
539
363
|
|
|
540
|
-
subscribe(listener) {
|
|
541
|
-
this.listeners.add(listener);
|
|
542
|
-
return () => {
|
|
543
|
-
this.listeners.delete(listener);
|
|
544
|
-
};
|
|
545
|
-
}
|
|
546
364
|
subscribeToStores(listener) {
|
|
547
365
|
this.storeListeners.add(listener);
|
|
548
366
|
return () => {
|
|
549
367
|
this.storeListeners.delete(listener);
|
|
550
368
|
};
|
|
551
369
|
}
|
|
552
|
-
notifyListeners() {
|
|
553
|
-
const changes = this.getStateChanges();
|
|
554
|
-
this.listeners.forEach(listener => listener(changes));
|
|
555
|
-
}
|
|
556
370
|
notifyStoreListeners() {
|
|
557
371
|
const stores = this.getStores();
|
|
558
372
|
this.storeListeners.forEach(listener => listener(stores));
|
|
@@ -4,134 +4,40 @@
|
|
|
4
4
|
* ZustandDetailViewToggle
|
|
5
5
|
*
|
|
6
6
|
* Three-card toggle for switching between Change, State, and Diff views.
|
|
7
|
-
*
|
|
7
|
+
* The cards themselves are the shared `DetailViewToggle`; this file owns
|
|
8
|
+
* the zustand view list and testID prefix.
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
11
|
import React, { memo } from "react";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
const VIEW_CONFIGS = [{
|
|
12
|
+
import { DetailViewToggle, FileText, Database, GitBranch } from "@buoy-gg/shared-ui";
|
|
13
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
14
|
+
const VIEWS = [{
|
|
15
15
|
key: "change",
|
|
16
16
|
label: "CHANGE",
|
|
17
17
|
description: "View change details",
|
|
18
|
-
icon: FileText
|
|
19
|
-
activeColor: night.color.accent
|
|
18
|
+
icon: FileText
|
|
20
19
|
}, {
|
|
21
20
|
key: "state",
|
|
22
21
|
label: "STATE",
|
|
23
22
|
description: "View current state",
|
|
24
|
-
icon: Database
|
|
25
|
-
activeColor: night.color.accent
|
|
23
|
+
icon: Database
|
|
26
24
|
}, {
|
|
27
25
|
key: "diff",
|
|
28
26
|
label: "DIFF",
|
|
29
27
|
description: "Compare changes",
|
|
30
|
-
icon: GitBranch
|
|
31
|
-
activeColor: night.color.accent
|
|
28
|
+
icon: GitBranch
|
|
32
29
|
}];
|
|
30
|
+
const DIFF_DISABLED = ["diff"];
|
|
33
31
|
export const ZustandDetailViewToggle = /*#__PURE__*/memo(function ZustandDetailViewToggle({
|
|
34
32
|
activeView,
|
|
35
33
|
onViewChange,
|
|
36
34
|
diffDisabled = false
|
|
37
35
|
}) {
|
|
38
|
-
return /*#__PURE__*/_jsx(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return /*#__PURE__*/_jsxs(TouchableOpacity, {
|
|
45
|
-
style: [styles.card, isActive && [styles.cardActive, {
|
|
46
|
-
borderColor: config.activeColor
|
|
47
|
-
}], isDisabled && styles.cardDisabled],
|
|
48
|
-
onPress: () => !isDisabled && onViewChange(config.key),
|
|
49
|
-
activeOpacity: isDisabled ? 1 : 0.8,
|
|
50
|
-
disabled: isDisabled,
|
|
51
|
-
testID: `buoy-zustand-view-${config.key}`,
|
|
52
|
-
accessibilityRole: "button",
|
|
53
|
-
accessibilityLabel: config.label,
|
|
54
|
-
accessibilityState: {
|
|
55
|
-
disabled: isDisabled,
|
|
56
|
-
selected: isActive
|
|
57
|
-
},
|
|
58
|
-
children: [/*#__PURE__*/_jsxs(View, {
|
|
59
|
-
style: styles.cardContent,
|
|
60
|
-
children: [/*#__PURE__*/_jsx(IconComponent, {
|
|
61
|
-
size: 14,
|
|
62
|
-
color: isActive ? config.activeColor : isDisabled ? night.color.textTertiary : night.color.textSecondary
|
|
63
|
-
}), /*#__PURE__*/_jsx(Text, {
|
|
64
|
-
style: [styles.cardLabel, isActive && [styles.cardLabelActive, {
|
|
65
|
-
color: config.activeColor
|
|
66
|
-
}], isDisabled && styles.cardLabelDisabled],
|
|
67
|
-
children: config.label
|
|
68
|
-
})]
|
|
69
|
-
}), /*#__PURE__*/_jsx(Text, {
|
|
70
|
-
style: [styles.cardDescription, isActive && styles.cardDescriptionActive, isDisabled && styles.cardDescriptionDisabled],
|
|
71
|
-
children: config.description
|
|
72
|
-
})]
|
|
73
|
-
}, config.key);
|
|
74
|
-
})
|
|
36
|
+
return /*#__PURE__*/_jsx(DetailViewToggle, {
|
|
37
|
+
views: VIEWS,
|
|
38
|
+
activeView: activeView,
|
|
39
|
+
onViewChange: onViewChange,
|
|
40
|
+
disabledKeys: diffDisabled ? DIFF_DISABLED : undefined,
|
|
41
|
+
testIDPrefix: "buoy-zustand"
|
|
75
42
|
});
|
|
76
|
-
});
|
|
77
|
-
const styles = StyleSheet.create({
|
|
78
|
-
container: {
|
|
79
|
-
flexDirection: "row",
|
|
80
|
-
gap: 8,
|
|
81
|
-
padding: 14,
|
|
82
|
-
// Transparent so the night sheet's star field shows through.
|
|
83
|
-
backgroundColor: "transparent"
|
|
84
|
-
},
|
|
85
|
-
card: {
|
|
86
|
-
flex: 1,
|
|
87
|
-
backgroundColor: night.color.surface,
|
|
88
|
-
borderRadius: 12,
|
|
89
|
-
borderWidth: 1,
|
|
90
|
-
borderColor: night.color.border,
|
|
91
|
-
padding: 10,
|
|
92
|
-
gap: 4
|
|
93
|
-
},
|
|
94
|
-
cardActive: {
|
|
95
|
-
borderWidth: 1.5,
|
|
96
|
-
backgroundColor: night.color.accentSoft,
|
|
97
|
-
shadowColor: night.color.accent,
|
|
98
|
-
shadowOffset: {
|
|
99
|
-
width: 0,
|
|
100
|
-
height: 2
|
|
101
|
-
},
|
|
102
|
-
shadowOpacity: 0.1,
|
|
103
|
-
shadowRadius: 8,
|
|
104
|
-
elevation: 3
|
|
105
|
-
},
|
|
106
|
-
cardDisabled: {
|
|
107
|
-
opacity: 0.5
|
|
108
|
-
},
|
|
109
|
-
cardContent: {
|
|
110
|
-
flexDirection: "row",
|
|
111
|
-
alignItems: "center",
|
|
112
|
-
gap: 6
|
|
113
|
-
},
|
|
114
|
-
cardLabel: {
|
|
115
|
-
fontSize: 10,
|
|
116
|
-
fontWeight: "700",
|
|
117
|
-
letterSpacing: 0.5,
|
|
118
|
-
color: night.color.textSecondary
|
|
119
|
-
},
|
|
120
|
-
cardLabelActive: {
|
|
121
|
-
color: night.color.text
|
|
122
|
-
},
|
|
123
|
-
cardLabelDisabled: {
|
|
124
|
-
color: night.color.textTertiary
|
|
125
|
-
},
|
|
126
|
-
cardDescription: {
|
|
127
|
-
fontSize: 9,
|
|
128
|
-
color: night.color.textTertiary,
|
|
129
|
-
lineHeight: 12
|
|
130
|
-
},
|
|
131
|
-
cardDescriptionActive: {
|
|
132
|
-
color: night.color.textSecondary
|
|
133
|
-
},
|
|
134
|
-
cardDescriptionDisabled: {
|
|
135
|
-
color: night.color.textTertiary
|
|
136
|
-
}
|
|
137
43
|
});
|