@buoy-gg/zustand 7.0.35 → 7.0.38
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 +27 -5
- package/lib/commonjs/index.js +1 -105
- package/lib/commonjs/preset.js +1 -102
- package/lib/commonjs/zustand/components/ZustandActionButton.js +1 -118
- package/lib/commonjs/zustand/components/ZustandDetailViewToggle.js +1 -48
- package/lib/commonjs/zustand/components/ZustandEventFilterView.js +1 -43
- package/lib/commonjs/zustand/components/ZustandIcon.js +1 -18
- package/lib/commonjs/zustand/components/ZustandModal.js +1 -772
- package/lib/commonjs/zustand/components/ZustandStateChangeItem.js +1 -117
- package/lib/commonjs/zustand/components/ZustandStateDetailContent.js +1 -372
- package/lib/commonjs/zustand/components/ZustandStateInfoView.js +1 -488
- package/lib/commonjs/zustand/components/ZustandStoreBrowser.js +1 -204
- package/lib/commonjs/zustand/components/ZustandStoreDetailContent.js +1 -471
- package/lib/commonjs/zustand/components/ZustandStoreEditorScreen.js +1 -393
- package/lib/commonjs/zustand/hooks/useZustandStateChanges.js +1 -72
- package/lib/commonjs/zustand/sync/zustandSyncAdapter.js +1 -219
- package/lib/commonjs/zustand/utils/buoyZustandMiddleware.js +1 -284
- package/lib/commonjs/zustand/utils/editableState.js +1 -139
- package/lib/commonjs/zustand/utils/snapshotProvider.js +1 -209
- package/lib/commonjs/zustand/utils/testIDs.js +1 -82
- package/lib/commonjs/zustand/utils/zustandStateStore.js +1 -375
- package/lib/module/index.js +1 -90
- package/lib/module/preset.js +1 -98
- package/lib/module/zustand/components/ZustandActionButton.js +1 -114
- package/lib/module/zustand/components/ZustandDetailViewToggle.js +1 -43
- package/lib/module/zustand/components/ZustandEventFilterView.js +1 -39
- package/lib/module/zustand/components/ZustandIcon.js +1 -11
- package/lib/module/zustand/components/ZustandModal.js +1 -767
- package/lib/module/zustand/components/ZustandStateChangeItem.js +1 -113
- package/lib/module/zustand/components/ZustandStateDetailContent.js +1 -368
- package/lib/module/zustand/components/ZustandStateInfoView.js +1 -483
- package/lib/module/zustand/components/ZustandStoreBrowser.js +1 -200
- package/lib/module/zustand/components/ZustandStoreDetailContent.js +1 -469
- package/lib/module/zustand/components/ZustandStoreEditorScreen.js +1 -389
- package/lib/module/zustand/hooks/useZustandStateChanges.js +1 -68
- package/lib/module/zustand/sync/zustandSyncAdapter.js +1 -216
- package/lib/module/zustand/utils/buoyZustandMiddleware.js +1 -278
- package/lib/module/zustand/utils/editableState.js +1 -131
- package/lib/module/zustand/utils/snapshotProvider.js +1 -209
- package/lib/module/zustand/utils/testIDs.js +1 -74
- package/lib/module/zustand/utils/zustandStateStore.js +1 -371
- package/lib/typescript/zustand/sync/zustandSyncAdapter.d.ts +33 -0
- package/package.json +4 -4
- package/lib/typescript/index.d.ts.map +0 -1
- package/lib/typescript/preset.d.ts.map +0 -1
- package/lib/typescript/zustand/components/ZustandActionButton.d.ts.map +0 -1
- package/lib/typescript/zustand/components/ZustandDetailViewToggle.d.ts.map +0 -1
- package/lib/typescript/zustand/components/ZustandEventFilterView.d.ts.map +0 -1
- package/lib/typescript/zustand/components/ZustandIcon.d.ts.map +0 -1
- package/lib/typescript/zustand/components/ZustandModal.d.ts.map +0 -1
- package/lib/typescript/zustand/components/ZustandStateChangeItem.d.ts.map +0 -1
- package/lib/typescript/zustand/components/ZustandStateDetailContent.d.ts.map +0 -1
- package/lib/typescript/zustand/components/ZustandStateInfoView.d.ts.map +0 -1
- package/lib/typescript/zustand/components/ZustandStoreBrowser.d.ts.map +0 -1
- package/lib/typescript/zustand/components/ZustandStoreDetailContent.d.ts.map +0 -1
- package/lib/typescript/zustand/components/ZustandStoreEditorScreen.d.ts.map +0 -1
- package/lib/typescript/zustand/hooks/useZustandStateChanges.d.ts.map +0 -1
- package/lib/typescript/zustand/sync/zustandSyncAdapter.d.ts.map +0 -1
- package/lib/typescript/zustand/types/index.d.ts.map +0 -1
- package/lib/typescript/zustand/utils/buoyZustandMiddleware.d.ts.map +0 -1
- package/lib/typescript/zustand/utils/editableState.d.ts.map +0 -1
- package/lib/typescript/zustand/utils/snapshotProvider.d.ts.map +0 -1
- package/lib/typescript/zustand/utils/testIDs.d.ts.map +0 -1
- package/lib/typescript/zustand/utils/zustandStateStore.d.ts.map +0 -1
|
@@ -1,113 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Compact list item for displaying a Zustand state change
|
|
5
|
-
*
|
|
6
|
-
* Mirrors ReduxActionItem.tsx - uses shared CompactRow for consistent styling
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { memo } from "react";
|
|
10
|
-
import { ChangeRow, buoyColors, night } from "@buoy-gg/shared-ui";
|
|
11
|
-
import { zustandStateStore } from "../utils/zustandStateStore";
|
|
12
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
13
|
-
/**
|
|
14
|
-
* Get status label based on category
|
|
15
|
-
*/
|
|
16
|
-
function getStatusLabel(category, storeName) {
|
|
17
|
-
switch (category) {
|
|
18
|
-
case "replace":
|
|
19
|
-
return "Replace";
|
|
20
|
-
case "persist":
|
|
21
|
-
return "Hydrate";
|
|
22
|
-
case "initial":
|
|
23
|
-
return "Initial";
|
|
24
|
-
default:
|
|
25
|
-
return storeName.charAt(0).toUpperCase() + storeName.slice(1);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Get color for category
|
|
31
|
-
*/
|
|
32
|
-
function getCategoryColor(category, storeName) {
|
|
33
|
-
switch (category) {
|
|
34
|
-
case "replace":
|
|
35
|
-
return buoyColors.warning;
|
|
36
|
-
case "persist":
|
|
37
|
-
return buoyColors.info;
|
|
38
|
-
case "initial":
|
|
39
|
-
return night.color.textTertiary;
|
|
40
|
-
default:
|
|
41
|
-
return zustandStateStore.getStoreColor(storeName);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Get sublabel with duration and diff info
|
|
47
|
-
*/
|
|
48
|
-
function getSublabel(change) {
|
|
49
|
-
const parts = [];
|
|
50
|
-
if (change.duration !== undefined && change.duration >= 0.1) {
|
|
51
|
-
parts.push(`${change.duration.toFixed(1)}ms`);
|
|
52
|
-
}
|
|
53
|
-
if (change.hasStateChange) {
|
|
54
|
-
if (change.changedKeysCount > 0) {
|
|
55
|
-
parts.push(`${change.changedKeysCount} key${change.changedKeysCount > 1 ? "s" : ""} changed`);
|
|
56
|
-
} else {
|
|
57
|
-
parts.push(change.diffSummary || "state changed");
|
|
58
|
-
}
|
|
59
|
-
} else {
|
|
60
|
-
parts.push("no state change");
|
|
61
|
-
}
|
|
62
|
-
return parts.join(" \u00B7 ");
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Get badge text for the change
|
|
67
|
-
*/
|
|
68
|
-
function getBadgeText(change) {
|
|
69
|
-
if (change.category === "replace") return "REPLACE";
|
|
70
|
-
if (change.category === "persist") return "HYDRATE";
|
|
71
|
-
if (change.category === "initial") return "INIT";
|
|
72
|
-
return "SET";
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Get primary display text
|
|
77
|
-
*/
|
|
78
|
-
function getPrimaryText(change) {
|
|
79
|
-
if (change.changedKeys.length > 0 && change.changedKeys.length <= 3) {
|
|
80
|
-
return change.changedKeys.join(", ");
|
|
81
|
-
}
|
|
82
|
-
if (change.changedKeys.length > 3) {
|
|
83
|
-
return `${change.changedKeys.slice(0, 2).join(", ")} +${change.changedKeys.length - 2}`;
|
|
84
|
-
}
|
|
85
|
-
return change.partialPreview || "setState()";
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Memo justified by profiling (tool-bench 2026-07-06): unmemoized, every
|
|
89
|
-
// visible row re-rendered on each store emission. Leaf w/ stable props
|
|
90
|
-
// ({change, onPress} — onPress is a stable useCallback in the modal).
|
|
91
|
-
export const ZustandStateChangeItem = /*#__PURE__*/memo(function ZustandStateChangeItem({
|
|
92
|
-
change,
|
|
93
|
-
onPress,
|
|
94
|
-
testID
|
|
95
|
-
}) {
|
|
96
|
-
const statusColor = getCategoryColor(change.category, change.storeName);
|
|
97
|
-
const statusLabel = getStatusLabel(change.category, change.storeName);
|
|
98
|
-
const sublabel = getSublabel(change);
|
|
99
|
-
const primaryText = getPrimaryText(change);
|
|
100
|
-
const badgeText = getBadgeText(change);
|
|
101
|
-
return /*#__PURE__*/_jsx(ChangeRow, {
|
|
102
|
-
statusColor: statusColor,
|
|
103
|
-
statusLabel: statusLabel,
|
|
104
|
-
sublabel: sublabel,
|
|
105
|
-
primaryText: primaryText,
|
|
106
|
-
timestamp: change.timestamp,
|
|
107
|
-
badgeText: badgeText,
|
|
108
|
-
changed: change.hasStateChange,
|
|
109
|
-
slow: change.isSlowUpdate,
|
|
110
|
-
onPress: () => onPress(change),
|
|
111
|
-
testID: testID
|
|
112
|
-
});
|
|
113
|
-
});
|
|
1
|
+
"use strict";import{memo}from"react";import{ChangeRow,buoyColors,night}from"@buoy-gg/shared-ui";import{zustandStateStore}from"../utils/zustandStateStore";import{jsx as _jsx}from"react/jsx-runtime";function getStatusLabel(e,t){switch(e){case"replace":return"Replace";case"persist":return"Hydrate";case"initial":return"Initial";default:return t.charAt(0).toUpperCase()+t.slice(1)}}function getCategoryColor(e,t){switch(e){case"replace":return buoyColors.warning;case"persist":return buoyColors.info;case"initial":return night.color.textTertiary;default:return zustandStateStore.getStoreColor(t)}}function getSublabel(e){const t=[];return void 0!==e.duration&&e.duration>=.1&&t.push(`${e.duration.toFixed(1)}ms`),e.hasStateChange?e.changedKeysCount>0?t.push(`${e.changedKeysCount} key${e.changedKeysCount>1?"s":""} changed`):t.push(e.diffSummary||"state changed"):t.push("no state change"),t.join(" · ")}function getBadgeText(e){return"replace"===e.category?"REPLACE":"persist"===e.category?"HYDRATE":"initial"===e.category?"INIT":"SET"}function getPrimaryText(e){return e.changedKeys.length>0&&e.changedKeys.length<=3?e.changedKeys.join(", "):e.changedKeys.length>3?`${e.changedKeys.slice(0,2).join(", ")} +${e.changedKeys.length-2}`:e.partialPreview||"setState()"}export const ZustandStateChangeItem=memo(function({change:e,onPress:t,testID:a}){const r=getCategoryColor(e.category,e.storeName),n=getStatusLabel(e.category,e.storeName),s=getSublabel(e),o=getPrimaryText(e),i=getBadgeText(e);return _jsx(ChangeRow,{statusColor:r,statusLabel:n,sublabel:s,primaryText:o,timestamp:e.timestamp,badgeText:i,changed:e.hasStateChange,slow:e.isSlowUpdate,onPress:()=>t(e),testID:a})});
|
|
@@ -1,368 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* ZustandStateDetailContent
|
|
5
|
-
*
|
|
6
|
-
* Detail view for a Zustand state change with 3 view modes:
|
|
7
|
-
* - CHANGE: View change details, partial, metadata
|
|
8
|
-
* - STATE: View current state after change
|
|
9
|
-
* - DIFF: Compare prev vs next state
|
|
10
|
-
*
|
|
11
|
-
* Mirrors ReduxActionDetailContent.tsx
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { View, Text, StyleSheet, ScrollView } from "react-native";
|
|
15
|
-
import { useState, useCallback, useMemo } from "react";
|
|
16
|
-
import { night, EventStepperFooter, formatRelativeTime, parseValue, monoFont } from "@buoy-gg/shared-ui";
|
|
17
|
-
import { DataViewer, SplitDiffViewer, TreeDiffViewer, diffThemes } from "@buoy-gg/shared-ui/dataViewer";
|
|
18
|
-
import { ZustandDetailViewToggle } from "./ZustandDetailViewToggle";
|
|
19
|
-
import { ZustandStateInfoView } from "./ZustandStateInfoView";
|
|
20
|
-
import { DiffModeTabs, CompareBar } from "@buoy-gg/shared-ui";
|
|
21
|
-
import { zustandStateStore } from "../utils/zustandStateStore";
|
|
22
|
-
import { ZUSTAND_TEST_ID } from "../utils/testIDs";
|
|
23
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
24
|
-
function getChangeColor(change) {
|
|
25
|
-
return zustandStateStore.getStoreColor(change.storeName);
|
|
26
|
-
}
|
|
27
|
-
function getChangeLabel(change) {
|
|
28
|
-
switch (change.category) {
|
|
29
|
-
case "replace":
|
|
30
|
-
return "REPLACE";
|
|
31
|
-
case "persist":
|
|
32
|
-
return "HYDRATE";
|
|
33
|
-
case "initial":
|
|
34
|
-
return "INIT";
|
|
35
|
-
default:
|
|
36
|
-
return "SET";
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
function getValueType(value) {
|
|
40
|
-
if (value === null) return "null";
|
|
41
|
-
if (value === undefined) return "undefined";
|
|
42
|
-
if (Array.isArray(value)) return "array";
|
|
43
|
-
return typeof value;
|
|
44
|
-
}
|
|
45
|
-
function formatTimestamp(timestamp) {
|
|
46
|
-
const date = new Date(timestamp);
|
|
47
|
-
return date.toLocaleTimeString("en-US", {
|
|
48
|
-
hour12: false,
|
|
49
|
-
hour: "2-digit",
|
|
50
|
-
minute: "2-digit",
|
|
51
|
-
second: "2-digit"
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
const DIFF_MODE_TABS = [{
|
|
55
|
-
key: "tree",
|
|
56
|
-
label: "TREE VIEW"
|
|
57
|
-
}, {
|
|
58
|
-
key: "split",
|
|
59
|
-
label: "SPLIT VIEW"
|
|
60
|
-
}];
|
|
61
|
-
export function ZustandStateDetailContent({
|
|
62
|
-
change,
|
|
63
|
-
changes,
|
|
64
|
-
selectedIndex,
|
|
65
|
-
onIndexChange,
|
|
66
|
-
disableInternalFooter = false,
|
|
67
|
-
initialView = "change"
|
|
68
|
-
}) {
|
|
69
|
-
const [activeView, setActiveView] = useState(initialView);
|
|
70
|
-
const [diffMode, setDiffMode] = useState("tree");
|
|
71
|
-
const totalChanges = changes.length;
|
|
72
|
-
const changeColor = getChangeColor(change);
|
|
73
|
-
const changeLabel = getChangeLabel(change);
|
|
74
|
-
const stateType = getValueType(parseValue(change.nextState));
|
|
75
|
-
|
|
76
|
-
// Changes are in reverse chronological order (newest first)
|
|
77
|
-
const chronologicalNumber = totalChanges - selectedIndex;
|
|
78
|
-
const prevChronologicalNumber = chronologicalNumber - 1;
|
|
79
|
-
const prevChangeIndex = selectedIndex < totalChanges - 1 ? selectedIndex + 1 : null;
|
|
80
|
-
const prevChange = prevChangeIndex !== null ? changes[prevChangeIndex] : null;
|
|
81
|
-
|
|
82
|
-
// Compare bar info
|
|
83
|
-
const leftEvent = useMemo(() => ({
|
|
84
|
-
index: prevChangeIndex ?? 0,
|
|
85
|
-
label: prevChronologicalNumber > 0 ? `#${prevChronologicalNumber}` : "Initial",
|
|
86
|
-
timestamp: prevChange ? formatTimestamp(prevChange.timestamp) : "",
|
|
87
|
-
relativeTime: prevChange ? formatRelativeTime(new Date(prevChange.timestamp)) : "state",
|
|
88
|
-
badge: prevChange ? /*#__PURE__*/_jsx(View, {
|
|
89
|
-
style: [styles.changeBadgeSmall, {
|
|
90
|
-
backgroundColor: `${getChangeColor(prevChange)}20`
|
|
91
|
-
}],
|
|
92
|
-
children: /*#__PURE__*/_jsx(Text, {
|
|
93
|
-
style: [styles.changeTextSmall, {
|
|
94
|
-
color: getChangeColor(prevChange)
|
|
95
|
-
}],
|
|
96
|
-
children: getChangeLabel(prevChange)
|
|
97
|
-
})
|
|
98
|
-
}) : undefined
|
|
99
|
-
}), [prevChangeIndex, prevChange, prevChronologicalNumber]);
|
|
100
|
-
const rightEvent = useMemo(() => ({
|
|
101
|
-
index: selectedIndex,
|
|
102
|
-
label: `#${chronologicalNumber}`,
|
|
103
|
-
timestamp: formatTimestamp(change.timestamp),
|
|
104
|
-
relativeTime: formatRelativeTime(new Date(change.timestamp)),
|
|
105
|
-
badge: /*#__PURE__*/_jsx(View, {
|
|
106
|
-
style: [styles.changeBadgeSmall, {
|
|
107
|
-
backgroundColor: `${changeColor}20`
|
|
108
|
-
}],
|
|
109
|
-
children: /*#__PURE__*/_jsx(Text, {
|
|
110
|
-
style: [styles.changeTextSmall, {
|
|
111
|
-
color: changeColor
|
|
112
|
-
}],
|
|
113
|
-
children: changeLabel
|
|
114
|
-
})
|
|
115
|
-
})
|
|
116
|
-
}), [selectedIndex, change.timestamp, changeColor, changeLabel, chronologicalNumber]);
|
|
117
|
-
const renderStateView = useCallback(() => {
|
|
118
|
-
const parsed = parseValue(change.nextState);
|
|
119
|
-
return /*#__PURE__*/_jsxs(View, {
|
|
120
|
-
style: styles.contentCard,
|
|
121
|
-
testID: `${ZUSTAND_TEST_ID}-state-card`,
|
|
122
|
-
children: [/*#__PURE__*/_jsxs(View, {
|
|
123
|
-
style: styles.valueHeader,
|
|
124
|
-
children: [/*#__PURE__*/_jsx(Text, {
|
|
125
|
-
style: styles.valueLabel,
|
|
126
|
-
children: "STATE AFTER CHANGE"
|
|
127
|
-
}), /*#__PURE__*/_jsxs(View, {
|
|
128
|
-
style: styles.valueHeaderBadges,
|
|
129
|
-
children: [/*#__PURE__*/_jsx(View, {
|
|
130
|
-
style: [styles.changeBadge, {
|
|
131
|
-
backgroundColor: `${changeColor}20`
|
|
132
|
-
}],
|
|
133
|
-
children: /*#__PURE__*/_jsx(Text, {
|
|
134
|
-
style: [styles.changeText, {
|
|
135
|
-
color: changeColor
|
|
136
|
-
}],
|
|
137
|
-
children: changeLabel
|
|
138
|
-
})
|
|
139
|
-
}), /*#__PURE__*/_jsx(View, {
|
|
140
|
-
style: styles.typeBadge,
|
|
141
|
-
children: /*#__PURE__*/_jsx(Text, {
|
|
142
|
-
style: styles.typeText,
|
|
143
|
-
testID: `${ZUSTAND_TEST_ID}-state-type`,
|
|
144
|
-
children: stateType.toUpperCase()
|
|
145
|
-
})
|
|
146
|
-
})]
|
|
147
|
-
})]
|
|
148
|
-
}), /*#__PURE__*/_jsx(View, {
|
|
149
|
-
style: styles.dataViewerContainer,
|
|
150
|
-
children: /*#__PURE__*/_jsx(DataViewer, {
|
|
151
|
-
title: "",
|
|
152
|
-
data: parsed,
|
|
153
|
-
showTypeFilter: true,
|
|
154
|
-
rawMode: true,
|
|
155
|
-
initialExpanded: true
|
|
156
|
-
})
|
|
157
|
-
})]
|
|
158
|
-
});
|
|
159
|
-
}, [change.nextState, changeColor, changeLabel, stateType]);
|
|
160
|
-
const renderDiffContent = useCallback(() => {
|
|
161
|
-
const prevState = parseValue(change.prevState);
|
|
162
|
-
const nextState = parseValue(change.nextState);
|
|
163
|
-
if (diffMode === "split") {
|
|
164
|
-
return /*#__PURE__*/_jsx(ScrollView, {
|
|
165
|
-
style: {
|
|
166
|
-
flex: 1
|
|
167
|
-
},
|
|
168
|
-
showsVerticalScrollIndicator: true,
|
|
169
|
-
children: /*#__PURE__*/_jsx(SplitDiffViewer, {
|
|
170
|
-
oldValue: prevState,
|
|
171
|
-
newValue: nextState,
|
|
172
|
-
theme: diffThemes.devToolsDefault,
|
|
173
|
-
options: {
|
|
174
|
-
hideLineNumbers: false,
|
|
175
|
-
disableWordDiff: false,
|
|
176
|
-
showDiffOnly: false,
|
|
177
|
-
compareMethod: "words",
|
|
178
|
-
contextLines: 3
|
|
179
|
-
},
|
|
180
|
-
showThemeName: false
|
|
181
|
-
})
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
return /*#__PURE__*/_jsx(TreeDiffViewer, {
|
|
185
|
-
oldValue: prevState,
|
|
186
|
-
newValue: nextState
|
|
187
|
-
});
|
|
188
|
-
}, [change.prevState, change.nextState, diffMode]);
|
|
189
|
-
const handleFooterPrevious = useCallback(() => {
|
|
190
|
-
onIndexChange(Math.min(totalChanges - 1, selectedIndex + 1));
|
|
191
|
-
}, [selectedIndex, totalChanges, onIndexChange]);
|
|
192
|
-
const handleFooterNext = useCallback(() => {
|
|
193
|
-
onIndexChange(Math.max(0, selectedIndex - 1));
|
|
194
|
-
}, [selectedIndex, onIndexChange]);
|
|
195
|
-
|
|
196
|
-
// The diff compares THIS change's prevState/nextState — both live on the
|
|
197
|
-
// change itself, so it needs no siblings. Gating on `changes.length` greyed
|
|
198
|
-
// DIFF out for every zustand event opened from the Events timeline, which
|
|
199
|
-
// hands this component a one-element array. Only a change with no captured
|
|
200
|
-
// previous state (initial creation) has nothing to compare.
|
|
201
|
-
const diffDisabled = change.prevState === undefined;
|
|
202
|
-
const showInternalFooter = !disableInternalFooter && totalChanges > 1;
|
|
203
|
-
return /*#__PURE__*/_jsxs(View, {
|
|
204
|
-
style: styles.container,
|
|
205
|
-
children: [/*#__PURE__*/_jsx(ZustandDetailViewToggle, {
|
|
206
|
-
activeView: activeView,
|
|
207
|
-
onViewChange: setActiveView,
|
|
208
|
-
diffDisabled: diffDisabled
|
|
209
|
-
}), /*#__PURE__*/_jsxs(View, {
|
|
210
|
-
style: [styles.contentArea, showInternalFooter && styles.contentAreaFooterPad],
|
|
211
|
-
children: [activeView === "change" && /*#__PURE__*/_jsx(ZustandStateInfoView, {
|
|
212
|
-
change: change
|
|
213
|
-
}), activeView === "state" && /*#__PURE__*/_jsx(View, {
|
|
214
|
-
style: styles.stateContainer,
|
|
215
|
-
children: renderStateView()
|
|
216
|
-
}), activeView === "diff" && /*#__PURE__*/_jsxs(View, {
|
|
217
|
-
style: styles.diffContainer,
|
|
218
|
-
testID: `${ZUSTAND_TEST_ID}-diff`,
|
|
219
|
-
children: [/*#__PURE__*/_jsx(DiffModeTabs, {
|
|
220
|
-
tabs: DIFF_MODE_TABS,
|
|
221
|
-
activeTab: diffMode,
|
|
222
|
-
onTabChange: setDiffMode,
|
|
223
|
-
testIDPrefix: `${ZUSTAND_TEST_ID}-diff-tab`
|
|
224
|
-
}), /*#__PURE__*/_jsx(CompareBar, {
|
|
225
|
-
leftEvent: leftEvent,
|
|
226
|
-
rightEvent: rightEvent
|
|
227
|
-
}), /*#__PURE__*/_jsx(View, {
|
|
228
|
-
style: styles.diffContent,
|
|
229
|
-
testID: `${ZUSTAND_TEST_ID}-diff-content`,
|
|
230
|
-
children: renderDiffContent()
|
|
231
|
-
})]
|
|
232
|
-
})]
|
|
233
|
-
}), showInternalFooter && /*#__PURE__*/_jsx(EventStepperFooter, {
|
|
234
|
-
currentIndex: chronologicalNumber - 1,
|
|
235
|
-
totalItems: totalChanges,
|
|
236
|
-
onPrevious: handleFooterPrevious,
|
|
237
|
-
onNext: handleFooterNext,
|
|
238
|
-
itemLabel: "Change",
|
|
239
|
-
subtitle: formatRelativeTime(new Date(change.timestamp)),
|
|
240
|
-
absolute: true
|
|
241
|
-
})]
|
|
242
|
-
});
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* External footer component for modal use
|
|
247
|
-
*/
|
|
248
|
-
export function ZustandStateDetailFooter({
|
|
249
|
-
change,
|
|
250
|
-
changes,
|
|
251
|
-
selectedIndex,
|
|
252
|
-
onIndexChange
|
|
253
|
-
}) {
|
|
254
|
-
const totalChanges = changes.length;
|
|
255
|
-
if (totalChanges <= 1) return null;
|
|
256
|
-
const chronologicalNumber = totalChanges - selectedIndex;
|
|
257
|
-
return /*#__PURE__*/_jsx(EventStepperFooter, {
|
|
258
|
-
currentIndex: chronologicalNumber - 1,
|
|
259
|
-
totalItems: totalChanges,
|
|
260
|
-
onPrevious: () => onIndexChange(Math.min(totalChanges - 1, selectedIndex + 1)),
|
|
261
|
-
onNext: () => onIndexChange(Math.max(0, selectedIndex - 1)),
|
|
262
|
-
itemLabel: "Change",
|
|
263
|
-
subtitle: formatRelativeTime(new Date(change.timestamp))
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
const styles = StyleSheet.create({
|
|
267
|
-
container: {
|
|
268
|
-
flex: 1,
|
|
269
|
-
// Transparent so the night sheet's star field shows through.
|
|
270
|
-
backgroundColor: "transparent"
|
|
271
|
-
},
|
|
272
|
-
contentArea: {
|
|
273
|
-
flex: 1
|
|
274
|
-
},
|
|
275
|
-
// Clearance for the absolute EventStepperFooter — only when it renders,
|
|
276
|
-
// otherwise hosts like the Events tool get a dead band at the sheet bottom.
|
|
277
|
-
contentAreaFooterPad: {
|
|
278
|
-
paddingBottom: 96
|
|
279
|
-
},
|
|
280
|
-
stateContainer: {
|
|
281
|
-
flex: 1,
|
|
282
|
-
padding: 14
|
|
283
|
-
},
|
|
284
|
-
contentCard: {
|
|
285
|
-
backgroundColor: night.color.surface,
|
|
286
|
-
borderRadius: 14,
|
|
287
|
-
padding: 14,
|
|
288
|
-
borderWidth: 1,
|
|
289
|
-
borderColor: night.color.border
|
|
290
|
-
},
|
|
291
|
-
valueHeader: {
|
|
292
|
-
flexDirection: "row",
|
|
293
|
-
alignItems: "center",
|
|
294
|
-
justifyContent: "space-between",
|
|
295
|
-
marginBottom: 8
|
|
296
|
-
},
|
|
297
|
-
valueLabel: {
|
|
298
|
-
fontSize: 10,
|
|
299
|
-
color: night.color.textSecondary,
|
|
300
|
-
fontFamily: monoFont,
|
|
301
|
-
letterSpacing: 0.5,
|
|
302
|
-
fontWeight: "600"
|
|
303
|
-
},
|
|
304
|
-
valueHeaderBadges: {
|
|
305
|
-
flexDirection: "row",
|
|
306
|
-
alignItems: "center",
|
|
307
|
-
gap: 6
|
|
308
|
-
},
|
|
309
|
-
changeBadge: {
|
|
310
|
-
paddingHorizontal: 8,
|
|
311
|
-
paddingVertical: 2,
|
|
312
|
-
borderRadius: 4
|
|
313
|
-
},
|
|
314
|
-
changeText: {
|
|
315
|
-
fontSize: 9,
|
|
316
|
-
fontWeight: "700",
|
|
317
|
-
fontFamily: monoFont,
|
|
318
|
-
letterSpacing: 0.3
|
|
319
|
-
},
|
|
320
|
-
typeBadge: {
|
|
321
|
-
paddingHorizontal: 8,
|
|
322
|
-
paddingVertical: 2,
|
|
323
|
-
borderRadius: 4,
|
|
324
|
-
backgroundColor: night.color.surfaceElevated
|
|
325
|
-
},
|
|
326
|
-
typeText: {
|
|
327
|
-
fontSize: 9,
|
|
328
|
-
fontWeight: "600",
|
|
329
|
-
color: night.color.textTertiary,
|
|
330
|
-
fontFamily: monoFont
|
|
331
|
-
},
|
|
332
|
-
valueBox: {
|
|
333
|
-
// Inset code box — darker than the card, the night track color.
|
|
334
|
-
backgroundColor: night.color.bg,
|
|
335
|
-
borderRadius: 6,
|
|
336
|
-
borderWidth: 1,
|
|
337
|
-
borderColor: night.color.border,
|
|
338
|
-
padding: 8
|
|
339
|
-
},
|
|
340
|
-
valueText: {
|
|
341
|
-
fontSize: 12,
|
|
342
|
-
color: night.color.text,
|
|
343
|
-
fontFamily: monoFont,
|
|
344
|
-
lineHeight: 18
|
|
345
|
-
},
|
|
346
|
-
diffContainer: {
|
|
347
|
-
flex: 1
|
|
348
|
-
},
|
|
349
|
-
diffContent: {
|
|
350
|
-
flex: 1,
|
|
351
|
-
paddingHorizontal: 14
|
|
352
|
-
},
|
|
353
|
-
changeBadgeSmall: {
|
|
354
|
-
paddingHorizontal: 6,
|
|
355
|
-
paddingVertical: 1,
|
|
356
|
-
borderRadius: 3
|
|
357
|
-
},
|
|
358
|
-
changeTextSmall: {
|
|
359
|
-
fontSize: 8,
|
|
360
|
-
fontWeight: "700",
|
|
361
|
-
fontFamily: monoFont
|
|
362
|
-
},
|
|
363
|
-
dataViewerContainer: {
|
|
364
|
-
marginTop: -12,
|
|
365
|
-
marginHorizontal: -12,
|
|
366
|
-
marginBottom: -12
|
|
367
|
-
}
|
|
368
|
-
});
|
|
1
|
+
"use strict";import{View,Text,StyleSheet,ScrollView}from"react-native";import{useState,useCallback,useMemo}from"react";import{night,EventStepperFooter,formatRelativeTime,parseValue,monoFont}from"@buoy-gg/shared-ui";import{DataViewer,SplitDiffViewer,TreeDiffViewer,diffThemes}from"@buoy-gg/shared-ui/dataViewer";import{ZustandDetailViewToggle}from"./ZustandDetailViewToggle";import{ZustandStateInfoView}from"./ZustandStateInfoView";import{DiffModeTabs,CompareBar}from"@buoy-gg/shared-ui";import{zustandStateStore}from"../utils/zustandStateStore";import{ZUSTAND_TEST_ID}from"../utils/testIDs";import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";function getChangeColor(e){return zustandStateStore.getStoreColor(e.storeName)}function getChangeLabel(e){switch(e.category){case"replace":return"REPLACE";case"persist":return"HYDRATE";case"initial":return"INIT";default:return"SET"}}function getValueType(e){return null===e?"null":void 0===e?"undefined":Array.isArray(e)?"array":typeof e}function formatTimestamp(e){return new Date(e).toLocaleTimeString("en-US",{hour12:!1,hour:"2-digit",minute:"2-digit",second:"2-digit"})}const DIFF_MODE_TABS=[{key:"tree",label:"TREE VIEW"},{key:"split",label:"SPLIT VIEW"}];export function ZustandStateDetailContent({change:e,changes:t,selectedIndex:a,onIndexChange:n,disableInternalFooter:o=!1,initialView:r="change"}){const[i,l]=useState(r),[s,d]=useState("tree"),c=t.length,g=getChangeColor(e),m=getChangeLabel(e),u=getValueType(parseValue(e.nextState)),f=c-a,h=f-1,x=a<c-1?a+1:null,p=null!==x?t[x]:null,T=useMemo(()=>({index:x??0,label:h>0?`#${h}`:"Initial",timestamp:p?formatTimestamp(p.timestamp):"",relativeTime:p?formatRelativeTime(new Date(p.timestamp)):"state",badge:p?_jsx(View,{style:[styles.changeBadgeSmall,{backgroundColor:`${getChangeColor(p)}20`}],children:_jsx(Text,{style:[styles.changeTextSmall,{color:getChangeColor(p)}],children:getChangeLabel(p)})}):void 0}),[x,p,h]),y=useMemo(()=>({index:a,label:`#${f}`,timestamp:formatTimestamp(e.timestamp),relativeTime:formatRelativeTime(new Date(e.timestamp)),badge:_jsx(View,{style:[styles.changeBadgeSmall,{backgroundColor:`${g}20`}],children:_jsx(Text,{style:[styles.changeTextSmall,{color:g}],children:m})})}),[a,e.timestamp,g,m,f]),S=useCallback(()=>{const t=parseValue(e.nextState);return _jsxs(View,{style:styles.contentCard,testID:`${ZUSTAND_TEST_ID}-state-card`,children:[_jsxs(View,{style:styles.valueHeader,children:[_jsx(Text,{style:styles.valueLabel,children:"STATE AFTER CHANGE"}),_jsxs(View,{style:styles.valueHeaderBadges,children:[_jsx(View,{style:[styles.changeBadge,{backgroundColor:`${g}20`}],children:_jsx(Text,{style:[styles.changeText,{color:g}],children:m})}),_jsx(View,{style:styles.typeBadge,children:_jsx(Text,{style:styles.typeText,testID:`${ZUSTAND_TEST_ID}-state-type`,children:u.toUpperCase()})})]})]}),_jsx(View,{style:styles.dataViewerContainer,children:_jsx(DataViewer,{title:"",data:t,showTypeFilter:!0,rawMode:!0,initialExpanded:!0})})]})},[e.nextState,g,m,u]),b=useCallback(()=>{const t=parseValue(e.prevState),a=parseValue(e.nextState);return"split"===s?_jsx(ScrollView,{style:{flex:1},showsVerticalScrollIndicator:!0,children:_jsx(SplitDiffViewer,{oldValue:t,newValue:a,theme:diffThemes.devToolsDefault,options:{hideLineNumbers:!1,disableWordDiff:!1,showDiffOnly:!1,compareMethod:"words",contextLines:3},showThemeName:!1})}):_jsx(TreeDiffViewer,{oldValue:t,newValue:a})},[e.prevState,e.nextState,s]),w=useCallback(()=>{n(Math.min(c-1,a+1))},[a,c,n]),V=useCallback(()=>{n(Math.max(0,a-1))},[a,n]),C=void 0===e.prevState,D=!o&&c>1;return _jsxs(View,{style:styles.container,children:[_jsx(ZustandDetailViewToggle,{activeView:i,onViewChange:l,diffDisabled:C}),_jsxs(View,{style:[styles.contentArea,D&&styles.contentAreaFooterPad],children:["change"===i&&_jsx(ZustandStateInfoView,{change:e}),"state"===i&&_jsx(View,{style:styles.stateContainer,children:S()}),"diff"===i&&_jsxs(View,{style:styles.diffContainer,testID:`${ZUSTAND_TEST_ID}-diff`,children:[_jsx(DiffModeTabs,{tabs:DIFF_MODE_TABS,activeTab:s,onTabChange:d,testIDPrefix:`${ZUSTAND_TEST_ID}-diff-tab`}),_jsx(CompareBar,{leftEvent:T,rightEvent:y}),_jsx(View,{style:styles.diffContent,testID:`${ZUSTAND_TEST_ID}-diff-content`,children:b()})]})]}),D&&_jsx(EventStepperFooter,{currentIndex:f-1,totalItems:c,onPrevious:w,onNext:V,itemLabel:"Change",subtitle:formatRelativeTime(new Date(e.timestamp)),absolute:!0})]})}export function ZustandStateDetailFooter({change:e,changes:t,selectedIndex:a,onIndexChange:n}){const o=t.length;return o<=1?null:_jsx(EventStepperFooter,{currentIndex:o-a-1,totalItems:o,onPrevious:()=>n(Math.min(o-1,a+1)),onNext:()=>n(Math.max(0,a-1)),itemLabel:"Change",subtitle:formatRelativeTime(new Date(e.timestamp))})}const styles=StyleSheet.create({container:{flex:1,backgroundColor:"transparent"},contentArea:{flex:1},contentAreaFooterPad:{paddingBottom:96},stateContainer:{flex:1,padding:14},contentCard:{backgroundColor:night.color.surface,borderRadius:14,padding:14,borderWidth:1,borderColor:night.color.border},valueHeader:{flexDirection:"row",alignItems:"center",justifyContent:"space-between",marginBottom:8},valueLabel:{fontSize:10,color:night.color.textSecondary,fontFamily:monoFont,letterSpacing:.5,fontWeight:"600"},valueHeaderBadges:{flexDirection:"row",alignItems:"center",gap:6},changeBadge:{paddingHorizontal:8,paddingVertical:2,borderRadius:4},changeText:{fontSize:9,fontWeight:"700",fontFamily:monoFont,letterSpacing:.3},typeBadge:{paddingHorizontal:8,paddingVertical:2,borderRadius:4,backgroundColor:night.color.surfaceElevated},typeText:{fontSize:9,fontWeight:"600",color:night.color.textTertiary,fontFamily:monoFont},valueBox:{backgroundColor:night.color.bg,borderRadius:6,borderWidth:1,borderColor:night.color.border,padding:8},valueText:{fontSize:12,color:night.color.text,fontFamily:monoFont,lineHeight:18},diffContainer:{flex:1},diffContent:{flex:1,paddingHorizontal:14},changeBadgeSmall:{paddingHorizontal:6,paddingVertical:1,borderRadius:3},changeTextSmall:{fontSize:8,fontWeight:"700",fontFamily:monoFont},dataViewerContainer:{marginTop:-12,marginHorizontal:-12,marginBottom:-12}});
|