@buoy-gg/redux 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 (32) hide show
  1. package/lib/commonjs/preset.js +14 -22
  2. package/lib/commonjs/redux/components/ReduxActionInfoView.js +8 -60
  3. package/lib/commonjs/redux/components/ReduxActionItem.js +64 -118
  4. package/lib/commonjs/redux/components/ReduxDetailViewToggle.js +15 -109
  5. package/lib/commonjs/redux/components/ReduxModal.js +46 -103
  6. package/lib/commonjs/redux/hooks/useReduxActions.js +16 -33
  7. package/lib/commonjs/redux/sync/reduxSyncAdapter.js +11 -29
  8. package/lib/commonjs/redux/utils/reduxActionStore.js +24 -94
  9. package/lib/commonjs/redux/utils/snapshotProvider.js +17 -80
  10. package/lib/module/preset.js +13 -20
  11. package/lib/module/redux/components/ReduxActionInfoView.js +1 -53
  12. package/lib/module/redux/components/ReduxActionItem.js +65 -119
  13. package/lib/module/redux/components/ReduxDetailViewToggle.js +17 -111
  14. package/lib/module/redux/components/ReduxModal.js +48 -105
  15. package/lib/module/redux/hooks/useReduxActions.js +17 -34
  16. package/lib/module/redux/sync/reduxSyncAdapter.js +12 -30
  17. package/lib/module/redux/utils/reduxActionStore.js +25 -95
  18. package/lib/module/redux/utils/snapshotProvider.js +18 -81
  19. package/lib/typescript/preset.d.ts +3 -9
  20. package/lib/typescript/preset.d.ts.map +1 -1
  21. package/lib/typescript/redux/components/ReduxActionInfoView.d.ts.map +1 -1
  22. package/lib/typescript/redux/components/ReduxActionItem.d.ts.map +1 -1
  23. package/lib/typescript/redux/components/ReduxDetailViewToggle.d.ts +2 -1
  24. package/lib/typescript/redux/components/ReduxDetailViewToggle.d.ts.map +1 -1
  25. package/lib/typescript/redux/components/ReduxModal.d.ts.map +1 -1
  26. package/lib/typescript/redux/hooks/useReduxActions.d.ts +3 -0
  27. package/lib/typescript/redux/hooks/useReduxActions.d.ts.map +1 -1
  28. package/lib/typescript/redux/sync/reduxSyncAdapter.d.ts +68 -82
  29. package/lib/typescript/redux/sync/reduxSyncAdapter.d.ts.map +1 -1
  30. package/lib/typescript/redux/utils/reduxActionStore.d.ts +2 -29
  31. package/lib/typescript/redux/utils/reduxActionStore.d.ts.map +1 -1
  32. package/package.json +4 -4
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
2
 
3
3
  var _sharedUi = require("@buoy-gg/shared-ui");
4
- var _dataViewer = require("@buoy-gg/shared-ui/dataViewer");
5
4
  var _activeStoreRegistry = require("./activeStoreRegistry");
6
5
  var _buoyReduxMiddleware = require("./buoyReduxMiddleware");
7
6
  /**
@@ -65,36 +64,19 @@ async function restore(data, options) {
65
64
  const snapshot = data;
66
65
  const store = (0, _activeStoreRegistry.getActiveStore)();
67
66
  if (!store || snapshot === undefined || snapshot === null) {
68
- return {
69
- ok: false,
70
- applied: 0,
71
- skipped: [{
72
- id: "redux",
73
- reason: store ? "empty redux snapshot" : "no Redux store instrumented"
74
- }],
75
- warnings: []
76
- };
67
+ return (0, _sharedUi.skippedRestore)("redux", store ? "empty redux snapshot" : "no Redux store instrumented");
77
68
  }
78
69
  const {
79
70
  mechanism
80
71
  } = (0, _activeStoreRegistry.getInstrumentationStatus)();
81
72
  if (mechanism === "patch") {
82
- return {
83
- ok: false,
84
- applied: 0,
85
- skipped: [{
86
- id: "redux",
87
- reason: `restore unavailable on auto-instrumented stores — ${ENABLE_HINT}`
88
- }],
89
- warnings: []
90
- };
73
+ return (0, _sharedUi.skippedRestore)("redux", `restore unavailable on auto-instrumented stores — ${ENABLE_HINT}`);
91
74
  }
92
75
  // Untouched slices keep their LIVE values inside the jump payload, so the
93
76
  // full-state replace leaves them alone. A slice is untouched when it's
94
77
  // explicitly excluded OR outside the includeKeys scope.
95
- const excludedSet = new Set(options?.excludeKeys ?? []);
96
- const includedSet = options?.includeKeys ? new Set(options.includeKeys) : null;
97
- const excluded = Object.keys(snapshot.state ?? {}).filter(k => excludedSet.has(k) || includedSet && !includedSet.has(k));
78
+ const scope = (0, _sharedUi.createRestoreScope)(options);
79
+ const excluded = Object.keys(snapshot.state ?? {}).filter(k => !scope.touch(k));
98
80
  let payload = snapshot.state;
99
81
  if (excluded.length > 0) {
100
82
  const live = store.getState();
@@ -120,17 +102,8 @@ async function restore(data, options) {
120
102
  warnings
121
103
  };
122
104
  }
123
- return {
124
- ok: false,
125
- applied: 0,
126
- skipped: [{
127
- id: "redux",
128
- reason: `store ignored the state jump — ${ENABLE_HINT}`
129
- }],
130
- warnings: []
131
- };
105
+ return (0, _sharedUi.skippedRestore)("redux", `store ignored the state jump — ${ENABLE_HINT}`);
132
106
  }
133
- const ITEM_RENDER_BYTE_LIMIT = 256 * 1024;
134
107
 
135
108
  /** Per-slice restore preview. `base` omitted = compare against the live store. */
136
109
  async function previewItems(target, base) {
@@ -155,54 +128,18 @@ async function previewItems(target, base) {
155
128
  }
156
129
  const oldObj = baseState ?? {};
157
130
  const newObj = targetState ?? {};
158
- const keys = new Set([...Object.keys(oldObj), ...Object.keys(newObj)]);
159
- for (const key of keys) {
160
- const inOld = Object.prototype.hasOwnProperty.call(oldObj, key);
161
- const inNew = Object.prototype.hasOwnProperty.call(newObj, key);
162
- let item;
163
- if (!inNew) {
164
- item = {
165
- key,
166
- label: key,
167
- verdict: "removed",
168
- oldValue: oldObj[key]
169
- };
170
- } else if (!inOld) {
171
- item = {
172
- key,
173
- label: key,
174
- verdict: "added",
175
- newValue: newObj[key]
176
- };
177
- } else if ((0, _dataViewer.boundedDeepEqual)(oldObj[key], newObj[key])) {
178
- item = {
179
- key,
180
- label: key,
181
- verdict: "unchanged"
182
- };
183
- } else if ((0, _dataViewer.jsonLikelyLargerThan)(oldObj[key], ITEM_RENDER_BYTE_LIMIT) || (0, _dataViewer.jsonLikelyLargerThan)(newObj[key], ITEM_RENDER_BYTE_LIMIT)) {
184
- item = {
185
- key,
186
- label: key,
187
- verdict: "too-large",
188
- reason: "slice too large to render a diff — restore still applies it"
189
- };
190
- } else {
191
- item = {
192
- key,
193
- label: key,
194
- verdict: "changed",
195
- oldValue: oldObj[key],
196
- newValue: newObj[key]
197
- };
198
- }
199
- if (wontApplyReason && item.verdict !== "unchanged") {
200
- item = {
201
- ...item,
202
- verdict: "wont-apply",
203
- reason: wontApplyReason
204
- };
205
- }
131
+ for (const key of (0, _sharedUi.unionKeys)(oldObj, newObj)) {
132
+ let item = (0, _sharedUi.classifyPreviewItem)({
133
+ key,
134
+ label: key,
135
+ oldValue: oldObj[key],
136
+ newValue: newObj[key],
137
+ inOld: Object.prototype.hasOwnProperty.call(oldObj, key),
138
+ inNew: Object.prototype.hasOwnProperty.call(newObj, key)
139
+ }, {
140
+ tooLargeReason: "slice too large to render a diff — restore still applies it"
141
+ });
142
+ if (wontApplyReason) item = (0, _sharedUi.wontApply)(item, wontApplyReason);
206
143
  items.push(item);
207
144
  }
208
145
  if (wontApplyReason) notes.push(`Redux restore unavailable: ${wontApplyReason}`);
@@ -34,6 +34,7 @@
34
34
 
35
35
  import { ReduxModal } from "./redux/components/ReduxModal";
36
36
  import { ReduxIcon } from "./redux/components/ReduxIcon";
37
+ import { createToolFactory, defineToolPreset } from "@buoy-gg/shared-ui";
37
38
 
38
39
  /**
39
40
  * Pre-configured Redux DevTools preset for FloatingDevTools.
@@ -44,7 +45,7 @@ import { ReduxIcon } from "./redux/components/ReduxIcon";
44
45
  * - Filter by action type
45
46
  */
46
47
  import { jsx as _jsx } from "react/jsx-runtime";
47
- export const reduxToolPreset = {
48
+ export const reduxToolPreset = defineToolPreset({
48
49
  id: "redux",
49
50
  name: "REDUX",
50
51
  description: "Redux action & state inspector",
@@ -58,7 +59,7 @@ export const reduxToolPreset = {
58
59
  props: {
59
60
  enableSharedModalDimensions: false
60
61
  }
61
- };
62
+ });
62
63
 
63
64
  /**
64
65
  * Create a custom Redux DevTools configuration.
@@ -74,21 +75,13 @@ export const reduxToolPreset = {
74
75
  * });
75
76
  * ```
76
77
  */
77
- export function createReduxTool(options) {
78
- return {
79
- id: options?.id || "redux",
80
- name: options?.name || "REDUX",
81
- description: options?.description || "Redux action & state inspector",
82
- slot: "both",
83
- icon: ({
84
- size
85
- }) => /*#__PURE__*/_jsx(ReduxIcon, {
86
- size: size,
87
- color: options?.iconColor
88
- }),
89
- component: ReduxModal,
90
- props: {
91
- enableSharedModalDimensions: options?.enableSharedModalDimensions !== undefined ? options.enableSharedModalDimensions : false
92
- }
93
- };
94
- }
78
+ export const createReduxTool = createToolFactory(reduxToolPreset, options => ({
79
+ enableSharedModalDimensions: options.enableSharedModalDimensions ?? false
80
+ }), options => ({
81
+ icon: ({
82
+ size
83
+ }) => /*#__PURE__*/_jsx(ReduxIcon, {
84
+ size: size,
85
+ color: options.iconColor
86
+ })
87
+ }));
@@ -12,7 +12,7 @@
12
12
 
13
13
  import React, { memo, useMemo, useCallback, useState, useEffect } from "react";
14
14
  import { View, Text, StyleSheet, ScrollView, TouchableOpacity } from "react-native";
15
- import { buoyColors, night, Clock, AlertTriangle, Zap, Hash, Info, Box, AlertCircle, FileText, parseValue, ProUpgradeModal, copyToClipboard } from "@buoy-gg/shared-ui";
15
+ import { buoyColors, night, Clock, AlertTriangle, Zap, Hash, Info, Box, AlertCircle, FileText, parseValue, ProUpgradeModal, copyToClipboard, InfoRow } from "@buoy-gg/shared-ui";
16
16
  import { useIsPro } from "@buoy-gg/license";
17
17
  import { DataViewer } from "@buoy-gg/shared-ui/dataViewer";
18
18
  import { reduxActionStore, isEvictedState } from "../utils/reduxActionStore";
@@ -104,37 +104,6 @@ function SectionHeader({
104
104
  });
105
105
  }
106
106
 
107
- /**
108
- * Info row component for key-value display
109
- */
110
- function InfoRow({
111
- label,
112
- value,
113
- valueColor,
114
- mono = false,
115
- testID
116
- }) {
117
- return /*#__PURE__*/_jsxs(View, {
118
- style: styles.infoRow,
119
- children: [/*#__PURE__*/_jsx(Text, {
120
- style: styles.infoLabel,
121
- children: label
122
- }), /*#__PURE__*/_jsx(Text, {
123
- style: [styles.infoValue, mono && styles.infoValueMono, valueColor && {
124
- color: valueColor
125
- }]
126
- // `ellipsizeMode="middle"` truncates the RENDERED text and iOS reports
127
- // the truncated string back, so anything a test reads off one of these
128
- // rows has to be short enough to fit on the line.
129
- ,
130
- numberOfLines: 1,
131
- ellipsizeMode: "middle",
132
- testID: testID,
133
- children: value
134
- })]
135
- });
136
- }
137
-
138
107
  /**
139
108
  * Format timestamp for display
140
109
  */
@@ -604,27 +573,6 @@ const styles = StyleSheet.create({
604
573
  padding: 14,
605
574
  gap: 10
606
575
  },
607
- infoRow: {
608
- flexDirection: "row",
609
- alignItems: "center",
610
- justifyContent: "space-between"
611
- },
612
- infoLabel: {
613
- fontSize: 11,
614
- color: night.color.textTertiary,
615
- fontWeight: "500"
616
- },
617
- infoValue: {
618
- fontSize: 11,
619
- color: night.color.text,
620
- fontWeight: "500",
621
- flex: 1,
622
- textAlign: "right",
623
- marginLeft: 12
624
- },
625
- infoValueMono: {
626
- fontFamily: "monospace"
627
- },
628
576
  badgeRow: {
629
577
  flexDirection: "row",
630
578
  flexWrap: "wrap",
@@ -7,7 +7,7 @@
7
7
 
8
8
  import { memo } from "react";
9
9
  import { View, Text, StyleSheet } from "react-native";
10
- import { CompactRow, buoyColors, Zap, AlertCircle, Clock, RelativeTime } from "@buoy-gg/shared-ui";
10
+ import { ChangeRow, buoyColors, AlertCircle } from "@buoy-gg/shared-ui";
11
11
  import { reduxActionStore } from "../utils/reduxActionStore";
12
12
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
13
13
  /**
@@ -236,119 +236,76 @@ export const ReduxActionItem = /*#__PURE__*/memo(function ReduxActionItem({
236
236
  // Get color for this operation's badge
237
237
  const opColor = getOperationColor(operationIndex);
238
238
 
239
- // Custom badge showing state change, slow action, or linked indicator
240
- let customBadge = undefined;
239
+ // The async-sequence chip ("1/2 #3") sits in front of whatever indicator
240
+ // the row shows; the ⚡ / 🕒 indicators and the mono action verb are drawn
241
+ // by the shared ChangeRow.
242
+ const sequenceChip = hasLinkedActions ? /*#__PURE__*/_jsx(View, {
243
+ style: [styles.asyncSequenceBadge, {
244
+ backgroundColor: opColor.bg
245
+ }],
246
+ children: /*#__PURE__*/_jsxs(Text, {
247
+ style: [styles.asyncSequenceText, {
248
+ color: opColor.text
249
+ }],
250
+ testID: testID ? `${testID}-seq` : undefined,
251
+ children: [asyncPosition, "/", linkedCount, " #", operationIndex]
252
+ })
253
+ }) : undefined;
254
+ const common = {
255
+ statusColor,
256
+ statusLabel,
257
+ sublabel,
258
+ primaryText,
259
+ timestamp: action.timestamp,
260
+ onPress: () => onPress(action),
261
+ testID,
262
+ accessibilityLabel: `${primaryText}, ${statusLabel}, ${sublabel}`
263
+ };
241
264
  if (action.isSlowAction) {
242
- // Show slow action warning
243
- customBadge = /*#__PURE__*/_jsxs(View, {
244
- style: styles.badgeContainer,
245
- children: [hasLinkedActions && /*#__PURE__*/_jsx(View, {
246
- style: [styles.asyncSequenceBadge, {
247
- backgroundColor: opColor.bg
248
- }],
249
- children: /*#__PURE__*/_jsxs(Text, {
250
- style: [styles.asyncSequenceText, {
251
- color: opColor.text
252
- }],
253
- testID: testID ? `${testID}-seq` : undefined,
254
- children: [asyncPosition, "/", linkedCount, " #", operationIndex]
255
- })
256
- }), /*#__PURE__*/_jsx(View, {
257
- style: styles.slowBadge,
258
- testID: testID ? `${testID}-slow` : undefined,
259
- children: /*#__PURE__*/_jsx(Clock, {
260
- size: 12,
261
- color: buoyColors.error
262
- })
263
- })]
265
+ // Slow action warning (with the sequence chip when part of a flow).
266
+ return /*#__PURE__*/_jsx(ChangeRow, {
267
+ ...common,
268
+ badgePrefix: sequenceChip,
269
+ slow: true
264
270
  });
265
- } else if (action.category === "rejected") {
266
- // Show error badge for rejected actions
267
- customBadge = /*#__PURE__*/_jsxs(View, {
268
- style: styles.badgeContainer,
269
- children: [hasLinkedActions && /*#__PURE__*/_jsx(View, {
270
- style: [styles.asyncSequenceBadge, {
271
- backgroundColor: opColor.bg
272
- }],
273
- children: /*#__PURE__*/_jsxs(Text, {
274
- style: [styles.asyncSequenceText, {
275
- color: opColor.text
276
- }],
277
- testID: testID ? `${testID}-seq` : undefined,
278
- children: [asyncPosition, "/", linkedCount, " #", operationIndex]
279
- })
280
- }), /*#__PURE__*/_jsx(View, {
281
- style: styles.errorBadge,
282
- testID: testID ? `${testID}-error` : undefined,
283
- children: /*#__PURE__*/_jsx(AlertCircle, {
284
- size: 12,
285
- color: buoyColors.error
286
- })
287
- })]
271
+ }
272
+ if (action.category === "rejected") {
273
+ // Error badge for rejected actions.
274
+ return /*#__PURE__*/_jsx(ChangeRow, {
275
+ ...common,
276
+ customBadge: /*#__PURE__*/_jsxs(View, {
277
+ style: styles.badgeContainer,
278
+ children: [sequenceChip, /*#__PURE__*/_jsx(View, {
279
+ style: styles.errorBadge,
280
+ testID: testID ? `${testID}-error` : undefined,
281
+ children: /*#__PURE__*/_jsx(AlertCircle, {
282
+ size: 12,
283
+ color: buoyColors.error
284
+ })
285
+ })]
286
+ })
288
287
  });
289
- } else if (hasLinkedActions) {
290
- // Show linked indicator for async actions
291
- customBadge = /*#__PURE__*/_jsxs(View, {
292
- style: styles.badgeContainer,
293
- children: [/*#__PURE__*/_jsx(View, {
294
- style: [styles.asyncSequenceBadge, {
295
- backgroundColor: opColor.bg
296
- }],
297
- children: /*#__PURE__*/_jsxs(Text, {
298
- style: [styles.asyncSequenceText, {
299
- color: opColor.text
300
- }],
301
- testID: testID ? `${testID}-seq` : undefined,
302
- children: [asyncPosition, "/", linkedCount, " #", operationIndex]
303
- })
304
- }), action.hasStateChange && /*#__PURE__*/_jsx(View, {
305
- style: styles.changeBadge,
306
- testID: testID ? `${testID}-change` : undefined,
307
- children: /*#__PURE__*/_jsx(Zap, {
308
- size: 12,
309
- color: buoyColors.warning
310
- })
311
- })]
288
+ }
289
+ if (hasLinkedActions) {
290
+ // Linked indicator for async actions.
291
+ return /*#__PURE__*/_jsx(ChangeRow, {
292
+ ...common,
293
+ badgePrefix: sequenceChip,
294
+ changed: action.hasStateChange
312
295
  });
313
- } else if (action.hasStateChange) {
314
- // Show state change indicator with action badge
315
- customBadge = /*#__PURE__*/_jsxs(View, {
316
- style: styles.badgeContainer,
317
- children: [/*#__PURE__*/_jsx(Text, {
318
- style: [styles.actionBadgeText, {
319
- color: statusColor
320
- }],
321
- testID: testID ? `${testID}-badge` : undefined,
322
- children: actionBadge
323
- }), /*#__PURE__*/_jsx(View, {
324
- style: styles.changeBadge,
325
- testID: testID ? `${testID}-change` : undefined,
326
- children: /*#__PURE__*/_jsx(Zap, {
327
- size: 12,
328
- color: buoyColors.warning
329
- })
330
- })]
296
+ }
297
+ if (action.hasStateChange) {
298
+ // State change indicator with the action verb.
299
+ return /*#__PURE__*/_jsx(ChangeRow, {
300
+ ...common,
301
+ badgeText: actionBadge,
302
+ changed: true
331
303
  });
332
304
  }
333
- return /*#__PURE__*/_jsx(CompactRow, {
334
- testID: testID,
335
- statusTestID: testID ? `${testID}-status` : undefined,
336
- statusSublabelTestID: testID ? `${testID}-sublabel` : undefined,
337
- accessibilityLabel: `${primaryText}, ${statusLabel}, ${sublabel}`,
338
- statusDotColor: statusColor,
339
- statusLabel: statusLabel,
340
- statusSublabel: sublabel,
341
- primaryText: primaryText,
342
- bottomRightText: /*#__PURE__*/_jsx(RelativeTime, {
343
- timestamp: action.timestamp
344
- })
345
- // Use customBadge if we have special indicators, otherwise show action badge as text
346
- ,
347
- customBadge: customBadge,
348
- badgeText: customBadge ? undefined : actionBadge,
349
- badgeColor: statusColor,
350
- showChevron: true,
351
- onPress: () => onPress(action)
305
+ // Nothing special: CompactRow's plain badge with the action verb.
306
+ return /*#__PURE__*/_jsx(ChangeRow, {
307
+ ...common,
308
+ plainBadgeText: actionBadge
352
309
  });
353
310
  });
354
311
  const styles = StyleSheet.create({
@@ -357,17 +314,6 @@ const styles = StyleSheet.create({
357
314
  alignItems: "center",
358
315
  gap: 4
359
316
  },
360
- actionBadgeText: {
361
- fontSize: 11,
362
- fontWeight: "600",
363
- fontFamily: "monospace"
364
- },
365
- changeBadge: {
366
- paddingHorizontal: 4
367
- },
368
- slowBadge: {
369
- paddingHorizontal: 4
370
- },
371
317
  errorBadge: {
372
318
  paddingHorizontal: 4
373
319
  },
@@ -4,136 +4,42 @@
4
4
  * ReduxDetailViewToggle
5
5
  *
6
6
  * Three-card toggle for switching between Action, State, and Diff views.
7
- * Redux-specific component for the enhanced detail view.
7
+ * The cards themselves are the shared `DetailViewToggle`; this file owns
8
+ * the redux view list and testID prefix.
8
9
  */
9
10
 
10
11
  import React, { memo } from "react";
11
- import { View, Text, TouchableOpacity, StyleSheet } from "react-native";
12
- import { night, FileText, Database, GitBranch } from "@buoy-gg/shared-ui";
13
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
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
+ // Active-card chrome takes the single night accent (the per-view
15
+ // warning/info/success tints were selection chrome, not data).
16
+ const VIEWS = [{
15
17
  key: "action",
16
18
  label: "ACTION",
17
19
  description: "View action details",
18
- icon: FileText,
19
- // Active-card chrome takes the single night accent (the per-view
20
- // warning/info/success tints were selection chrome, not data).
21
- activeColor: night.color.accent
20
+ icon: FileText
22
21
  }, {
23
22
  key: "state",
24
23
  label: "STATE",
25
24
  description: "View current state",
26
- icon: Database,
27
- activeColor: night.color.accent
25
+ icon: Database
28
26
  }, {
29
27
  key: "diff",
30
28
  label: "DIFF",
31
29
  description: "Compare changes",
32
- icon: GitBranch,
33
- activeColor: night.color.accent
30
+ icon: GitBranch
34
31
  }];
32
+ const DIFF_DISABLED = ["diff"];
35
33
  export const ReduxDetailViewToggle = /*#__PURE__*/memo(function ReduxDetailViewToggle({
36
34
  activeView,
37
35
  onViewChange,
38
36
  diffDisabled = false
39
37
  }) {
40
- return /*#__PURE__*/_jsx(View, {
41
- style: styles.container,
42
- children: VIEW_CONFIGS.map(config => {
43
- const isActive = activeView === config.key;
44
- const isDisabled = config.key === "diff" && diffDisabled;
45
- const IconComponent = config.icon;
46
- return /*#__PURE__*/_jsxs(TouchableOpacity, {
47
- style: [styles.card, isActive && [styles.cardActive, {
48
- borderColor: config.activeColor
49
- }], isDisabled && styles.cardDisabled],
50
- onPress: () => !isDisabled && onViewChange(config.key),
51
- activeOpacity: isDisabled ? 1 : 0.8,
52
- disabled: isDisabled,
53
- testID: `buoy-redux-view-${config.key}`,
54
- accessibilityRole: "button",
55
- accessibilityLabel: config.label,
56
- accessibilityState: {
57
- selected: isActive,
58
- disabled: isDisabled
59
- },
60
- children: [/*#__PURE__*/_jsxs(View, {
61
- style: styles.cardContent,
62
- children: [/*#__PURE__*/_jsx(IconComponent, {
63
- size: 14,
64
- color: isActive ? config.activeColor : isDisabled ? night.color.textTertiary : night.color.textSecondary
65
- }), /*#__PURE__*/_jsx(Text, {
66
- style: [styles.cardLabel, isActive && [styles.cardLabelActive, {
67
- color: config.activeColor
68
- }], isDisabled && styles.cardLabelDisabled],
69
- children: config.label
70
- })]
71
- }), /*#__PURE__*/_jsx(Text, {
72
- style: [styles.cardDescription, isActive && styles.cardDescriptionActive, isDisabled && styles.cardDescriptionDisabled],
73
- children: config.description
74
- })]
75
- }, config.key);
76
- })
38
+ return /*#__PURE__*/_jsx(DetailViewToggle, {
39
+ views: VIEWS,
40
+ activeView: activeView,
41
+ onViewChange: onViewChange,
42
+ disabledKeys: diffDisabled ? DIFF_DISABLED : undefined,
43
+ testIDPrefix: "buoy-redux"
77
44
  });
78
- });
79
- const styles = StyleSheet.create({
80
- container: {
81
- flexDirection: "row",
82
- gap: 8,
83
- padding: 14,
84
- // Transparent so the night sheet's star field shows through.
85
- backgroundColor: "transparent"
86
- },
87
- card: {
88
- flex: 1,
89
- backgroundColor: night.color.surface,
90
- borderRadius: 12,
91
- borderWidth: 1,
92
- borderColor: night.color.border,
93
- padding: 10,
94
- gap: 4
95
- },
96
- cardActive: {
97
- borderWidth: 1.5,
98
- backgroundColor: night.color.accentSoft,
99
- shadowColor: night.color.accent,
100
- shadowOffset: {
101
- width: 0,
102
- height: 2
103
- },
104
- shadowOpacity: 0.1,
105
- shadowRadius: 8,
106
- elevation: 3
107
- },
108
- cardDisabled: {
109
- opacity: 0.5
110
- },
111
- cardContent: {
112
- flexDirection: "row",
113
- alignItems: "center",
114
- gap: 6
115
- },
116
- cardLabel: {
117
- fontSize: 10,
118
- fontWeight: "700",
119
- letterSpacing: 0.5,
120
- color: night.color.textSecondary
121
- },
122
- cardLabelActive: {
123
- color: night.color.text
124
- },
125
- cardLabelDisabled: {
126
- color: night.color.textTertiary
127
- },
128
- cardDescription: {
129
- fontSize: 9,
130
- color: night.color.textTertiary,
131
- lineHeight: 12
132
- },
133
- cardDescriptionActive: {
134
- color: night.color.textSecondary
135
- },
136
- cardDescriptionDisabled: {
137
- color: night.color.textTertiary
138
- }
139
45
  });