rn-backstage 1.2.0 → 1.2.1

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 (100) hide show
  1. package/README.md +2 -0
  2. package/lib/commonjs/Backstage.js +40 -33
  3. package/lib/commonjs/Backstage.js.map +1 -1
  4. package/lib/commonjs/ThemeContext.js +43 -0
  5. package/lib/commonjs/ThemeContext.js.map +1 -0
  6. package/lib/commonjs/components/BackstagePanel.js +17 -10
  7. package/lib/commonjs/components/BackstagePanel.js.map +1 -1
  8. package/lib/commonjs/components/FlagsTab.js +50 -49
  9. package/lib/commonjs/components/FlagsTab.js.map +1 -1
  10. package/lib/commonjs/components/FloatingPill.js +4 -2
  11. package/lib/commonjs/components/FloatingPill.js.map +1 -1
  12. package/lib/commonjs/components/InfoTab.js +73 -107
  13. package/lib/commonjs/components/InfoTab.js.map +1 -1
  14. package/lib/commonjs/components/JsonTreeView.js +39 -34
  15. package/lib/commonjs/components/JsonTreeView.js.map +1 -1
  16. package/lib/commonjs/components/LogItem.js +33 -29
  17. package/lib/commonjs/components/LogItem.js.map +1 -1
  18. package/lib/commonjs/components/LogsTab.js +37 -42
  19. package/lib/commonjs/components/LogsTab.js.map +1 -1
  20. package/lib/commonjs/components/NetworkItem.js +111 -135
  21. package/lib/commonjs/components/NetworkItem.js.map +1 -1
  22. package/lib/commonjs/components/NetworkTab.js +48 -49
  23. package/lib/commonjs/components/NetworkTab.js.map +1 -1
  24. package/lib/commonjs/components/TabBar.js +8 -5
  25. package/lib/commonjs/components/TabBar.js.map +1 -1
  26. package/lib/commonjs/constants.js +24 -1
  27. package/lib/commonjs/constants.js.map +1 -1
  28. package/lib/commonjs/index.js +19 -0
  29. package/lib/commonjs/index.js.map +1 -1
  30. package/lib/commonjs/types.js.map +1 -1
  31. package/lib/module/Backstage.js +40 -33
  32. package/lib/module/Backstage.js.map +1 -1
  33. package/lib/module/ThemeContext.js +37 -0
  34. package/lib/module/ThemeContext.js.map +1 -0
  35. package/lib/module/components/BackstagePanel.js +19 -12
  36. package/lib/module/components/BackstagePanel.js.map +1 -1
  37. package/lib/module/components/FlagsTab.js +51 -50
  38. package/lib/module/components/FlagsTab.js.map +1 -1
  39. package/lib/module/components/FloatingPill.js +5 -3
  40. package/lib/module/components/FloatingPill.js.map +1 -1
  41. package/lib/module/components/InfoTab.js +73 -107
  42. package/lib/module/components/InfoTab.js.map +1 -1
  43. package/lib/module/components/JsonTreeView.js +42 -38
  44. package/lib/module/components/JsonTreeView.js.map +1 -1
  45. package/lib/module/components/LogItem.js +35 -31
  46. package/lib/module/components/LogItem.js.map +1 -1
  47. package/lib/module/components/LogsTab.js +38 -43
  48. package/lib/module/components/LogsTab.js.map +1 -1
  49. package/lib/module/components/NetworkItem.js +113 -137
  50. package/lib/module/components/NetworkItem.js.map +1 -1
  51. package/lib/module/components/NetworkTab.js +49 -50
  52. package/lib/module/components/NetworkTab.js.map +1 -1
  53. package/lib/module/components/TabBar.js +10 -7
  54. package/lib/module/components/TabBar.js.map +1 -1
  55. package/lib/module/constants.js +23 -0
  56. package/lib/module/constants.js.map +1 -1
  57. package/lib/module/index.js +1 -0
  58. package/lib/module/index.js.map +1 -1
  59. package/lib/module/types.js.map +1 -1
  60. package/lib/typescript/Backstage.d.ts.map +1 -1
  61. package/lib/typescript/ThemeContext.d.ts +11 -0
  62. package/lib/typescript/ThemeContext.d.ts.map +1 -0
  63. package/lib/typescript/components/BackstagePanel.d.ts +1 -0
  64. package/lib/typescript/components/BackstagePanel.d.ts.map +1 -1
  65. package/lib/typescript/components/FlagsTab.d.ts.map +1 -1
  66. package/lib/typescript/components/FloatingPill.d.ts.map +1 -1
  67. package/lib/typescript/components/InfoTab.d.ts +1 -0
  68. package/lib/typescript/components/InfoTab.d.ts.map +1 -1
  69. package/lib/typescript/components/JsonTreeView.d.ts.map +1 -1
  70. package/lib/typescript/components/LogItem.d.ts +1 -0
  71. package/lib/typescript/components/LogItem.d.ts.map +1 -1
  72. package/lib/typescript/components/LogsTab.d.ts +1 -0
  73. package/lib/typescript/components/LogsTab.d.ts.map +1 -1
  74. package/lib/typescript/components/NetworkItem.d.ts +1 -0
  75. package/lib/typescript/components/NetworkItem.d.ts.map +1 -1
  76. package/lib/typescript/components/NetworkTab.d.ts +1 -0
  77. package/lib/typescript/components/NetworkTab.d.ts.map +1 -1
  78. package/lib/typescript/components/TabBar.d.ts.map +1 -1
  79. package/lib/typescript/constants.d.ts +1 -0
  80. package/lib/typescript/constants.d.ts.map +1 -1
  81. package/lib/typescript/index.d.ts +2 -0
  82. package/lib/typescript/index.d.ts.map +1 -1
  83. package/lib/typescript/types.d.ts +4 -0
  84. package/lib/typescript/types.d.ts.map +1 -1
  85. package/package.json +1 -1
  86. package/src/Backstage.tsx +39 -33
  87. package/src/ThemeContext.tsx +38 -0
  88. package/src/components/BackstagePanel.tsx +66 -57
  89. package/src/components/FlagsTab.tsx +123 -174
  90. package/src/components/FloatingPill.tsx +6 -3
  91. package/src/components/InfoTab.tsx +152 -186
  92. package/src/components/JsonTreeView.tsx +53 -73
  93. package/src/components/LogItem.tsx +55 -100
  94. package/src/components/LogsTab.tsx +110 -132
  95. package/src/components/NetworkItem.tsx +306 -417
  96. package/src/components/NetworkTab.tsx +129 -163
  97. package/src/components/TabBar.tsx +51 -47
  98. package/src/constants.ts +23 -0
  99. package/src/index.ts +2 -0
  100. package/src/types.ts +6 -0
@@ -1,7 +1,8 @@
1
1
  import React, { useMemo, useState } from 'react'
2
2
  import { ScrollView, StyleSheet, Switch, Text, TextInput, View } from 'react-native'
3
- import { DarkTheme, MonospaceFont } from '../constants'
4
- import type { FeatureFlag } from '../types'
3
+ import { MonospaceFont } from '../constants'
4
+ import { useBackstageTheme } from '../ThemeContext'
5
+ import type { FeatureFlag, BackstageTheme } from '../types'
5
6
 
6
7
  // ─── Types ───────────────────────────────────────────────────────────────────
7
8
 
@@ -13,6 +14,8 @@ interface FlagsTabProps {
13
14
  // ─── Component ───────────────────────────────────────────────────────────────
14
15
 
15
16
  export const FlagsTab: React.FC<FlagsTabProps> = ({ flags, onToggle }) => {
17
+ const theme = useBackstageTheme()
18
+ const s = useMemo(() => createStyles(theme), [theme])
16
19
  const [searchText, setSearchText] = useState('')
17
20
 
18
21
  const filteredFlags = useMemo(() => {
@@ -26,19 +29,17 @@ export const FlagsTab: React.FC<FlagsTabProps> = ({ flags, onToggle }) => {
26
29
  )
27
30
  }, [flags, searchText])
28
31
 
29
- // Stats
30
32
  const enabledCount = flags.filter(f => f.value).length
31
33
 
32
34
  return (
33
- <View style={styles.container}>
34
- {/* Search Bar */}
35
- <View style={styles.searchContainer}>
36
- <View style={styles.searchInputWrapper}>
37
- <Text style={styles.searchIcon}>⌕</Text>
35
+ <View style={s.container}>
36
+ <View style={s.searchContainer}>
37
+ <View style={s.searchInputWrapper}>
38
+ <Text style={s.searchIcon}>⌕</Text>
38
39
  <TextInput
39
- style={styles.searchInput}
40
+ style={s.searchInput}
40
41
  placeholder="Filter flags..."
41
- placeholderTextColor={DarkTheme.textMuted}
42
+ placeholderTextColor={theme.textMuted}
42
43
  value={searchText}
43
44
  onChangeText={setSearchText}
44
45
  autoCapitalize="none"
@@ -49,59 +50,51 @@ export const FlagsTab: React.FC<FlagsTabProps> = ({ flags, onToggle }) => {
49
50
  </View>
50
51
  </View>
51
52
 
52
- {/* Stats Bar */}
53
- <View style={styles.statsBar}>
54
- <Text style={styles.statText}>
53
+ <View style={s.statsBar}>
54
+ <Text style={s.statText}>
55
55
  {filteredFlags.length === flags.length
56
56
  ? `${flags.length} flag${flags.length !== 1 ? 's' : ''}`
57
57
  : `${filteredFlags.length} of ${flags.length}`}
58
58
  </Text>
59
- <Text style={styles.statText}>
60
- <Text style={styles.statEnabled}>{enabledCount} on</Text>
59
+ <Text style={s.statText}>
60
+ <Text style={s.statEnabled}>{enabledCount} on</Text>
61
61
  {' · '}
62
- <Text style={styles.statDisabled}>{flags.length - enabledCount} off</Text>
62
+ <Text style={s.statDisabled}>{flags.length - enabledCount} off</Text>
63
63
  </Text>
64
64
  </View>
65
65
 
66
- {/* Flag List */}
67
66
  <ScrollView
68
- style={styles.list}
69
- contentContainerStyle={
70
- filteredFlags.length === 0 ? styles.emptyContainer : styles.listContent
71
- }
67
+ style={s.list}
68
+ contentContainerStyle={filteredFlags.length === 0 ? s.emptyContainer : s.listContent}
72
69
  showsVerticalScrollIndicator={false}
73
70
  >
74
71
  {filteredFlags.length === 0 ? (
75
- <View style={styles.emptyState}>
76
- <Text style={styles.emptyIcon}>🎚</Text>
77
- <Text style={styles.emptyTitle}>No flags match</Text>
78
- <Text style={styles.emptySubtitle}>Try adjusting your search</Text>
72
+ <View style={s.emptyState}>
73
+ <Text style={s.emptyIcon}>🎚</Text>
74
+ <Text style={s.emptyTitle}>No flags match</Text>
75
+ <Text style={s.emptySubtitle}>Try adjusting your search</Text>
79
76
  </View>
80
77
  ) : (
81
78
  filteredFlags.map((flag, index) => (
82
79
  <View key={flag.key}>
83
- <View style={styles.flagRow}>
84
- <View style={styles.flagInfo}>
85
- <View style={styles.flagHeader}>
86
- <Text style={styles.flagLabel}>{flag.label}</Text>
87
- <Text style={[styles.flagKey, flag.value && styles.flagKeyActive]}>
88
- {flag.key}
89
- </Text>
80
+ <View style={s.flagRow}>
81
+ <View style={s.flagInfo}>
82
+ <View style={s.flagHeader}>
83
+ <Text style={s.flagLabel}>{flag.label}</Text>
84
+ <Text style={[s.flagKey, flag.value && s.flagKeyActive]}>{flag.key}</Text>
90
85
  </View>
91
- {flag.description && (
92
- <Text style={styles.flagDescription}>{flag.description}</Text>
93
- )}
86
+ {flag.description && <Text style={s.flagDescription}>{flag.description}</Text>}
94
87
  </View>
95
88
  <Switch
96
89
  testID={`backstage.flag.${flag.key}`}
97
90
  value={flag.value}
98
91
  onValueChange={(newValue: boolean) => onToggle?.(flag.key, newValue)}
99
- trackColor={{ false: DarkTheme.border, true: DarkTheme.accent }}
100
- thumbColor={flag.value ? '#FFFFFF' : DarkTheme.textMuted}
101
- ios_backgroundColor={DarkTheme.border}
92
+ trackColor={{ false: theme.border, true: theme.accent }}
93
+ thumbColor={flag.value ? '#FFFFFF' : theme.textMuted}
94
+ ios_backgroundColor={theme.border}
102
95
  />
103
96
  </View>
104
- {index < filteredFlags.length - 1 && <View style={styles.divider} />}
97
+ {index < filteredFlags.length - 1 && <View style={s.divider} />}
105
98
  </View>
106
99
  ))
107
100
  )}
@@ -112,138 +105,94 @@ export const FlagsTab: React.FC<FlagsTabProps> = ({ flags, onToggle }) => {
112
105
 
113
106
  // ─── Styles ──────────────────────────────────────────────────────────────────
114
107
 
115
- const styles = StyleSheet.create({
116
- container: {
117
- flex: 1,
118
- },
119
- searchContainer: {
120
- paddingHorizontal: 14,
121
- paddingVertical: 10,
122
- borderBottomWidth: 1,
123
- borderBottomColor: DarkTheme.border,
124
- },
125
- searchInputWrapper: {
126
- flexDirection: 'row',
127
- alignItems: 'center',
128
- backgroundColor: DarkTheme.surfaceElevated,
129
- borderRadius: 10,
130
- borderWidth: 1,
131
- borderColor: DarkTheme.border,
132
- paddingHorizontal: 12,
133
- height: 38,
134
- },
135
- searchIcon: {
136
- fontSize: 16,
137
- color: DarkTheme.textMuted,
138
- marginRight: 8,
139
- },
140
- searchInput: {
141
- flex: 1,
142
- fontFamily: MonospaceFont,
143
- fontSize: 13,
144
- color: DarkTheme.text,
145
- padding: 0,
146
- },
147
- statsBar: {
148
- flexDirection: 'row',
149
- justifyContent: 'space-between',
150
- alignItems: 'center',
151
- paddingHorizontal: 16,
152
- paddingVertical: 6,
153
- backgroundColor: DarkTheme.surface,
154
- },
155
- statText: {
156
- fontFamily: MonospaceFont,
157
- fontSize: 11,
158
- color: DarkTheme.textMuted,
159
- },
160
- statEnabled: {
161
- color: DarkTheme.success,
162
- fontWeight: '700',
163
- },
164
- statDisabled: {
165
- color: DarkTheme.textMuted,
166
- },
167
- list: {
168
- flex: 1,
169
- },
170
- listContent: {
171
- paddingHorizontal: 16,
172
- paddingVertical: 8,
173
- },
174
- emptyContainer: {
175
- flex: 1,
176
- justifyContent: 'center',
177
- },
178
- emptyState: {
179
- alignItems: 'center',
180
- justifyContent: 'center',
181
- padding: 40,
182
- },
183
- emptyIcon: {
184
- fontSize: 48,
185
- marginBottom: 16,
186
- opacity: 0.5,
187
- },
188
- emptyTitle: {
189
- fontFamily: MonospaceFont,
190
- fontSize: 16,
191
- fontWeight: '700',
192
- color: DarkTheme.textSecondary,
193
- marginBottom: 8,
194
- },
195
- emptySubtitle: {
196
- fontFamily: MonospaceFont,
197
- fontSize: 13,
198
- color: DarkTheme.textMuted,
199
- textAlign: 'center',
200
- },
201
- flagRow: {
202
- flexDirection: 'row',
203
- justifyContent: 'space-between',
204
- alignItems: 'center',
205
- paddingVertical: 14,
206
- paddingHorizontal: 4,
207
- },
208
- flagInfo: {
209
- flex: 1,
210
- marginRight: 16,
211
- },
212
- flagHeader: {
213
- flexDirection: 'row',
214
- alignItems: 'center',
215
- gap: 8,
216
- },
217
- flagLabel: {
218
- fontFamily: MonospaceFont,
219
- fontSize: 14,
220
- color: DarkTheme.text,
221
- fontWeight: '600',
222
- },
223
- flagKey: {
224
- fontFamily: MonospaceFont,
225
- fontSize: 10,
226
- color: DarkTheme.textMuted,
227
- backgroundColor: DarkTheme.surfaceElevated,
228
- borderRadius: 4,
229
- paddingHorizontal: 6,
230
- paddingVertical: 1,
231
- overflow: 'hidden',
232
- },
233
- flagKeyActive: {
234
- color: DarkTheme.accent,
235
- backgroundColor: DarkTheme.accentDim,
236
- },
237
- flagDescription: {
238
- fontFamily: MonospaceFont,
239
- fontSize: 12,
240
- color: DarkTheme.textMuted,
241
- marginTop: 4,
242
- lineHeight: 18,
243
- },
244
- divider: {
245
- height: 1,
246
- backgroundColor: DarkTheme.border,
247
- marginHorizontal: 4,
248
- },
249
- })
108
+ const createStyles = (t: BackstageTheme) =>
109
+ StyleSheet.create({
110
+ container: { flex: 1 },
111
+ searchContainer: {
112
+ paddingHorizontal: 14,
113
+ paddingVertical: 10,
114
+ borderBottomWidth: 1,
115
+ borderBottomColor: t.border,
116
+ },
117
+ searchInputWrapper: {
118
+ flexDirection: 'row',
119
+ alignItems: 'center',
120
+ backgroundColor: t.surfaceElevated,
121
+ borderRadius: 10,
122
+ borderWidth: 1,
123
+ borderColor: t.border,
124
+ paddingHorizontal: 12,
125
+ height: 38,
126
+ },
127
+ searchIcon: { fontSize: 16, color: t.textMuted, marginRight: 8 },
128
+ searchInput: {
129
+ flex: 1,
130
+ fontFamily: MonospaceFont,
131
+ fontSize: 13,
132
+ color: t.text,
133
+ padding: 0,
134
+ },
135
+ statsBar: {
136
+ flexDirection: 'row',
137
+ justifyContent: 'space-between',
138
+ alignItems: 'center',
139
+ paddingHorizontal: 16,
140
+ paddingVertical: 6,
141
+ backgroundColor: t.surface,
142
+ },
143
+ statText: { fontFamily: MonospaceFont, fontSize: 11, color: t.textMuted },
144
+ statEnabled: { color: t.success, fontWeight: '700' },
145
+ statDisabled: { color: t.textMuted },
146
+ list: { flex: 1 },
147
+ listContent: { paddingHorizontal: 16, paddingVertical: 8 },
148
+ emptyContainer: { flex: 1, justifyContent: 'center' },
149
+ emptyState: { alignItems: 'center', justifyContent: 'center', padding: 40 },
150
+ emptyIcon: { fontSize: 48, marginBottom: 16, opacity: 0.5 },
151
+ emptyTitle: {
152
+ fontFamily: MonospaceFont,
153
+ fontSize: 16,
154
+ fontWeight: '700',
155
+ color: t.textSecondary,
156
+ marginBottom: 8,
157
+ },
158
+ emptySubtitle: {
159
+ fontFamily: MonospaceFont,
160
+ fontSize: 13,
161
+ color: t.textMuted,
162
+ textAlign: 'center',
163
+ },
164
+ flagRow: {
165
+ flexDirection: 'row',
166
+ justifyContent: 'space-between',
167
+ alignItems: 'center',
168
+ paddingVertical: 14,
169
+ paddingHorizontal: 4,
170
+ },
171
+ flagInfo: { flex: 1, marginRight: 16 },
172
+ flagHeader: { flexDirection: 'row', alignItems: 'center', gap: 8 },
173
+ flagLabel: {
174
+ fontFamily: MonospaceFont,
175
+ fontSize: 14,
176
+ color: t.text,
177
+ fontWeight: '600',
178
+ },
179
+ flagKey: {
180
+ fontFamily: MonospaceFont,
181
+ fontSize: 10,
182
+ color: t.textMuted,
183
+ backgroundColor: t.surfaceElevated,
184
+ borderRadius: 4,
185
+ paddingHorizontal: 6,
186
+ paddingVertical: 1,
187
+ overflow: 'hidden',
188
+ },
189
+ flagKeyActive: { color: t.accent, backgroundColor: t.accentDim },
190
+ flagDescription: {
191
+ fontFamily: MonospaceFont,
192
+ fontSize: 12,
193
+ color: t.textMuted,
194
+ marginTop: 4,
195
+ lineHeight: 18,
196
+ },
197
+ divider: { height: 1, backgroundColor: t.border, marginHorizontal: 4 },
198
+ })
@@ -1,6 +1,7 @@
1
1
  import React, { useRef } from 'react'
2
2
  import { Animated, Dimensions, PanResponder, StyleSheet, Text } from 'react-native'
3
- import { DarkTheme, Metrics, MonospaceFont } from '../constants'
3
+ import { Metrics, MonospaceFont } from '../constants'
4
+ import { useBackstageTheme } from '../ThemeContext'
4
5
  import type { BackstageStyleOverrides } from '../types'
5
6
 
6
7
  // ─── Types ───────────────────────────────────────────────────────────────────
@@ -100,8 +101,10 @@ export const FloatingPill: React.FC<FloatingPillProps> = ({
100
101
  }),
101
102
  ).current
102
103
 
103
- const backgroundColor = hasError ? DarkTheme.error : DarkTheme.accent
104
- const shadowColor = hasError ? DarkTheme.error : DarkTheme.accent
104
+ const theme = useBackstageTheme()
105
+
106
+ const backgroundColor = hasError ? theme.error : theme.accent
107
+ const shadowColor = hasError ? theme.error : theme.accent
105
108
 
106
109
  return (
107
110
  <Animated.View