react-native-inapp-inspector 2.2.2 → 2.2.4

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 (159) hide show
  1. package/dist/commonjs/components/AnalyticsDetail.js +5 -1
  2. package/dist/commonjs/components/AnalyticsEventCard.js +2 -1
  3. package/dist/commonjs/components/DiffViewer.js +1 -1
  4. package/dist/commonjs/components/Inspector/BundleTab.js +22 -0
  5. package/dist/commonjs/components/Inspector/DeviceInfoTab.d.ts +3 -0
  6. package/dist/commonjs/components/Inspector/DeviceInfoTab.js +835 -0
  7. package/dist/commonjs/components/Inspector/InspectorHeader.js +12 -14
  8. package/dist/commonjs/components/Inspector/LogDetail.js +6 -3
  9. package/dist/commonjs/components/Inspector/MainScreen.js +7 -3
  10. package/dist/commonjs/components/Inspector/NetworkTab.js +92 -15
  11. package/dist/commonjs/components/Inspector/PerformanceTab.js +38 -12
  12. package/dist/commonjs/components/Inspector/SettingsPanel.js +440 -229
  13. package/dist/commonjs/components/Inspector/StorageTab.d.ts +3 -0
  14. package/dist/commonjs/components/Inspector/StorageTab.js +892 -0
  15. package/dist/commonjs/components/Inspector/TabBar.js +14 -0
  16. package/dist/commonjs/components/Inspector/TelemetryConsentModal.js +11 -2
  17. package/dist/commonjs/components/Inspector/UpdateAvailableModal.js +4 -1
  18. package/dist/commonjs/components/NetworkIcons.d.ts +6 -0
  19. package/dist/commonjs/components/NetworkIcons.js +30 -1
  20. package/dist/commonjs/constants/version.d.ts +1 -1
  21. package/dist/commonjs/constants/version.js +1 -1
  22. package/dist/commonjs/customHooks/bundleAnalyzer.js +44 -22
  23. package/dist/commonjs/customHooks/storageInspector.d.ts +67 -0
  24. package/dist/commonjs/customHooks/storageInspector.js +486 -0
  25. package/dist/commonjs/i18n/locales/en.json +8 -2
  26. package/dist/commonjs/index.d.ts +1 -0
  27. package/dist/commonjs/index.js +16 -1
  28. package/dist/commonjs/storage.d.ts +5 -0
  29. package/dist/commonjs/storage.js +18 -0
  30. package/dist/commonjs/types/enums.d.ts +5 -0
  31. package/dist/commonjs/types/enums.js +5 -0
  32. package/dist/esm/components/AnalyticsDetail.js +6 -2
  33. package/dist/esm/components/AnalyticsEventCard.js +2 -1
  34. package/dist/esm/components/DiffViewer.js +1 -1
  35. package/dist/esm/components/Inspector/BundleTab.js +22 -0
  36. package/dist/esm/components/Inspector/DeviceInfoTab.d.ts +3 -0
  37. package/dist/esm/components/Inspector/DeviceInfoTab.js +796 -0
  38. package/dist/esm/components/Inspector/InspectorHeader.js +13 -15
  39. package/dist/esm/components/Inspector/LogDetail.js +7 -4
  40. package/dist/esm/components/Inspector/MainScreen.js +7 -3
  41. package/dist/esm/components/Inspector/NetworkTab.js +93 -16
  42. package/dist/esm/components/Inspector/PerformanceTab.js +39 -13
  43. package/dist/esm/components/Inspector/SettingsPanel.js +441 -230
  44. package/dist/esm/components/Inspector/StorageTab.d.ts +3 -0
  45. package/dist/esm/components/Inspector/StorageTab.js +853 -0
  46. package/dist/esm/components/Inspector/TabBar.js +15 -1
  47. package/dist/esm/components/Inspector/TelemetryConsentModal.js +11 -2
  48. package/dist/esm/components/Inspector/UpdateAvailableModal.js +4 -1
  49. package/dist/esm/components/NetworkIcons.d.ts +6 -0
  50. package/dist/esm/components/NetworkIcons.js +23 -0
  51. package/dist/esm/constants/version.d.ts +1 -1
  52. package/dist/esm/constants/version.js +1 -1
  53. package/dist/esm/customHooks/bundleAnalyzer.js +44 -22
  54. package/dist/esm/customHooks/storageInspector.d.ts +67 -0
  55. package/dist/esm/customHooks/storageInspector.js +469 -0
  56. package/dist/esm/i18n/locales/en.json +8 -2
  57. package/dist/esm/index.d.ts +1 -0
  58. package/dist/esm/index.js +5 -0
  59. package/dist/esm/storage.d.ts +5 -0
  60. package/dist/esm/storage.js +5 -0
  61. package/dist/esm/types/enums.d.ts +5 -0
  62. package/dist/esm/types/enums.js +5 -0
  63. package/package.json +9 -1
  64. package/src/analytics.ts +35 -0
  65. package/src/bundle.ts +25 -0
  66. package/src/components/AnalyticsDetail.tsx +870 -0
  67. package/src/components/AnalyticsEventCard.tsx +442 -0
  68. package/src/components/AnalyticsGraph.tsx +583 -0
  69. package/src/components/AnimatedEntrance.tsx +64 -0
  70. package/src/components/AppHeaderLogo.tsx +109 -0
  71. package/src/components/BrandCircleIcon.tsx +144 -0
  72. package/src/components/BrandSquareIcon.tsx +144 -0
  73. package/src/components/CodeSnippet.tsx +725 -0
  74. package/src/components/ConsoleLogCard.tsx +628 -0
  75. package/src/components/CopyButton.tsx +87 -0
  76. package/src/components/DiffViewer.tsx +82 -0
  77. package/src/components/DomainHeader.tsx +237 -0
  78. package/src/components/EmptyState.tsx +73 -0
  79. package/src/components/EndOfListFooter.tsx +100 -0
  80. package/src/components/ErrorBoundary.tsx +688 -0
  81. package/src/components/HeadersSection.tsx +192 -0
  82. package/src/components/HighlightText.tsx +100 -0
  83. package/src/components/Inspector/AnalyticsFilterModal.tsx +1250 -0
  84. package/src/components/Inspector/AnalyticsTab.tsx +1336 -0
  85. package/src/components/Inspector/BundleTab.tsx +4760 -0
  86. package/src/components/Inspector/ConsoleTab.tsx +702 -0
  87. package/src/components/Inspector/CrashDetail.tsx +941 -0
  88. package/src/components/Inspector/CrashFilterModal.tsx +721 -0
  89. package/src/components/Inspector/CrashTab.tsx +871 -0
  90. package/src/components/Inspector/DeviceInfoTab.tsx +1204 -0
  91. package/src/components/Inspector/FabLauncher.tsx +80 -0
  92. package/src/components/Inspector/InspectorContext.tsx +28 -0
  93. package/src/components/Inspector/InspectorHeader.tsx +972 -0
  94. package/src/components/Inspector/LogDetail.tsx +1431 -0
  95. package/src/components/Inspector/MainScreen.tsx +262 -0
  96. package/src/components/Inspector/NavigationTracker.tsx +13 -0
  97. package/src/components/Inspector/NetworkDetail.tsx +794 -0
  98. package/src/components/Inspector/NetworkTab.tsx +1183 -0
  99. package/src/components/Inspector/NpmUpdateToast.tsx +392 -0
  100. package/src/components/Inspector/PerformanceTab.tsx +1928 -0
  101. package/src/components/Inspector/ReduxDetail.tsx +1651 -0
  102. package/src/components/Inspector/ReduxTab.tsx +954 -0
  103. package/src/components/Inspector/SettingsPanel.tsx +3447 -0
  104. package/src/components/Inspector/StorageTab.tsx +1048 -0
  105. package/src/components/Inspector/TabBar.tsx +207 -0
  106. package/src/components/Inspector/TelemetryConsentModal.tsx +416 -0
  107. package/src/components/Inspector/UpdateAvailableModal.tsx +557 -0
  108. package/src/components/JsonViewer.tsx +486 -0
  109. package/src/components/LogCard.tsx +491 -0
  110. package/src/components/LogSyntaxHighlighter.tsx +178 -0
  111. package/src/components/MetaAccordion.tsx +340 -0
  112. package/src/components/MiniBarChart.tsx +42 -0
  113. package/src/components/MiniLineChart.tsx +33 -0
  114. package/src/components/NetworkIcons.tsx +2221 -0
  115. package/src/components/SectionHeader.tsx +113 -0
  116. package/src/components/SegmentedTabs.tsx +83 -0
  117. package/src/components/Slider.tsx +300 -0
  118. package/src/components/SourcePageCard.tsx +148 -0
  119. package/src/components/Toast.tsx +131 -0
  120. package/src/components/TouchableScale.tsx +91 -0
  121. package/src/components/TreeNode.tsx +186 -0
  122. package/src/console.ts +24 -0
  123. package/src/constants/index.ts +38 -0
  124. package/src/constants/version.ts +3 -0
  125. package/src/crash.ts +32 -0
  126. package/src/customHooks/analyticsLogger.ts +336 -0
  127. package/src/customHooks/bundleAnalyzer.ts +1256 -0
  128. package/src/customHooks/consoleLogger.ts +497 -0
  129. package/src/customHooks/crashHandler.ts +944 -0
  130. package/src/customHooks/logFilters.ts +32 -0
  131. package/src/customHooks/networkLogger.ts +419 -0
  132. package/src/customHooks/performanceTracker.ts +1014 -0
  133. package/src/customHooks/reduxLogger.ts +406 -0
  134. package/src/customHooks/storageInspector.ts +509 -0
  135. package/src/customHooks/useAccordion.tsx +60 -0
  136. package/src/decorators/index.ts +184 -0
  137. package/src/helpers/gaAnalyticsRegistry.ts +204 -0
  138. package/src/helpers/index.ts +860 -0
  139. package/src/helpers/memoryManager.ts +138 -0
  140. package/src/helpers/searchQueryParser.ts +283 -0
  141. package/src/helpers/settingsStore.ts +171 -0
  142. package/src/helpers/telemetry.ts +505 -0
  143. package/src/helpers/toast.ts +17 -0
  144. package/src/i18n/index.ts +69 -0
  145. package/src/i18n/locales/en.json +1058 -0
  146. package/src/index.tsx +2356 -0
  147. package/src/native/NativeInspector.ts +397 -0
  148. package/src/native/NativeNetworkInspector.ts +24 -0
  149. package/src/network.ts +22 -0
  150. package/src/performance.ts +38 -0
  151. package/src/redux.ts +23 -0
  152. package/src/storage.ts +18 -0
  153. package/src/styles/AppColors.ts +408 -0
  154. package/src/styles/AppFonts.ts +8 -0
  155. package/src/styles/common.ts +209 -0
  156. package/src/styles/index.ts +1570 -0
  157. package/src/types/enums.ts +199 -0
  158. package/src/types/index.ts +34 -0
  159. package/src/types/interfaces.ts +515 -0
@@ -0,0 +1,340 @@
1
+ import React, {useState} from 'react';
2
+ import {useTranslation} from '../i18n';
3
+ import {View, Pressable, Text, Animated} from 'react-native';
4
+
5
+ // Constants
6
+ import {DURATION_FAST_MS, DURATION_SLOW_MS} from '../constants';
7
+
8
+ // Custom Hooks
9
+ import useAccordion from '../customHooks/useAccordion';
10
+
11
+ // Helpers
12
+ import {getDurationColor, handleOpenExternalLink} from '../helpers';
13
+
14
+ // Types
15
+ import {MetaAccordionProps} from '../types';
16
+
17
+ // Assets
18
+ import {
19
+ ChevronIcon,
20
+ CalendarIcon,
21
+ StatusIcon,
22
+ ClockIcon,
23
+ SizeIcon,
24
+ TerminalIcon,
25
+ GlobeIcon,
26
+ } from './NetworkIcons';
27
+
28
+ // Components
29
+ import TouchableScale from './TouchableScale';
30
+
31
+ // Stylesheet
32
+ import {AppColors} from '../styles/AppColors';
33
+ import {AppFonts} from '../styles/AppFonts';
34
+ import styles from '../styles';
35
+
36
+ const MetaAccordion = ({
37
+ status,
38
+ statusColor,
39
+ duration,
40
+ size,
41
+ triggeredAt,
42
+ method,
43
+ contentType,
44
+ url,
45
+ }: MetaAccordionProps) => {
46
+ const {t} = useTranslation();
47
+ const [urlExpanded, setUrlExpanded] = useState(false);
48
+ const {toggleOpen, chevronStyle, bodyStyle} = useAccordion(true, 800, 390);
49
+ const isFailed = status === 0 || status == null;
50
+
51
+ const renderFormattedUrl = (rawUrl: string) => {
52
+ if (!rawUrl) return '—';
53
+ try {
54
+ const doubleSlashIndex = rawUrl.indexOf('//');
55
+ let temp = rawUrl;
56
+ let protocol = '';
57
+ if (doubleSlashIndex !== -1) {
58
+ protocol = rawUrl.substring(0, doubleSlashIndex + 2);
59
+ temp = rawUrl.substring(doubleSlashIndex + 2);
60
+ }
61
+
62
+ const slashIndex = temp.indexOf('/');
63
+ let host = temp;
64
+ let pathAndSearch = '';
65
+ if (slashIndex !== -1) {
66
+ host = temp.substring(0, slashIndex);
67
+ pathAndSearch = temp.substring(slashIndex);
68
+ }
69
+
70
+ const queryIndex = pathAndSearch.indexOf('?');
71
+ let pathname = pathAndSearch;
72
+ let search = '';
73
+ if (queryIndex !== -1) {
74
+ pathname = pathAndSearch.substring(0, queryIndex);
75
+ search = pathAndSearch.substring(queryIndex);
76
+ }
77
+
78
+ return (
79
+ <Text style={{lineHeight: 16}}>
80
+ {protocol ? (
81
+ <Text style={{color: AppColors.slate400, fontFamily: AppFonts.interRegular}}>{protocol}</Text>
82
+ ) : null}
83
+ <Text style={{color: AppColors.purple, fontFamily: AppFonts.interBold}}>{host}</Text>
84
+ <Text style={{color: AppColors.slate700, fontFamily: AppFonts.interMedium}}>{pathname}</Text>
85
+ {search ? (
86
+ <Text style={{color: AppColors.teal600, fontFamily: AppFonts.interRegular}}>{search}</Text>
87
+ ) : null}
88
+ </Text>
89
+ );
90
+ } catch {
91
+ return rawUrl;
92
+ }
93
+ };
94
+
95
+ return (
96
+ <View style={styles.metaContainer}>
97
+ <Pressable onPress={toggleOpen} hitSlop={12}>
98
+ <View style={styles.metaHeader}>
99
+ <Text style={styles.metaTitle}>Metadata</Text>
100
+ <Animated.View style={chevronStyle}>
101
+ <ChevronIcon color={AppColors.grayTextWeak} size={14} />
102
+ </Animated.View>
103
+ </View>
104
+ </Pressable>
105
+
106
+ <Animated.View style={bodyStyle}>
107
+ <View style={styles.metaBody}>
108
+ <View style={styles.metaRow}>
109
+ <View style={styles.metaLabelRow}>
110
+ <CalendarIcon color={AppColors.grayTextWeak} size={14} />
111
+ <Text style={styles.metaLabel}>{t('network.triggeredAt')}</Text>
112
+ </View>
113
+ <Text
114
+ style={[
115
+ styles.metaValue,
116
+ {color: AppColors.purple, fontSize: 12},
117
+ ]}>
118
+ {triggeredAt}
119
+ </Text>
120
+ </View>
121
+ <View style={styles.metaDivider} />
122
+
123
+ <View style={styles.metaRow}>
124
+ <View style={styles.metaLabelRow}>
125
+ <TerminalIcon color={AppColors.grayTextWeak} size={14} />
126
+ <Text style={styles.metaLabel}>{t('network.methodHeader')}</Text>
127
+ </View>
128
+ <View
129
+ style={[
130
+ styles.statusChip,
131
+ {
132
+ borderColor: `${AppColors.brandPurple}40`,
133
+ backgroundColor: `${AppColors.brandPurple}14`,
134
+ },
135
+ ]}>
136
+ <Text
137
+ style={[
138
+ styles.statusText,
139
+ {color: AppColors.purple, fontFamily: AppFonts.interBold},
140
+ ]}>
141
+ {method || 'GET'}
142
+ </Text>
143
+ </View>
144
+ </View>
145
+ <View style={styles.metaDivider} />
146
+
147
+ <View style={styles.metaRow}>
148
+ <View style={styles.metaLabelRow}>
149
+ <StatusIcon color={AppColors.grayTextWeak} />
150
+ <Text style={styles.metaLabel}>{t('network.statusHeader')}</Text>
151
+ </View>
152
+ <View
153
+ style={[
154
+ styles.statusChip,
155
+ {
156
+ borderColor: isFailed
157
+ ? `${AppColors.errorColor}40`
158
+ : `${statusColor}40`,
159
+ backgroundColor: isFailed
160
+ ? `${AppColors.errorColor}15`
161
+ : `${statusColor}15`,
162
+ },
163
+ ]}>
164
+ <Text
165
+ style={[
166
+ styles.statusText,
167
+ {color: isFailed ? AppColors.errorColor : statusColor},
168
+ ]}>
169
+ {isFailed ? t('network.failedNetworkError') : String(status)}
170
+ </Text>
171
+ </View>
172
+ </View>
173
+ <View style={styles.metaDivider} />
174
+
175
+ <View style={styles.metaRow}>
176
+ <View style={styles.metaLabelRow}>
177
+ <GlobeIcon color={AppColors.grayTextWeak} size={14} />
178
+ <Text style={styles.metaLabel}>{t('network.contentType')}</Text>
179
+ </View>
180
+ <Text style={styles.metaValue}>
181
+ {contentType || 'application/json'}
182
+ </Text>
183
+ </View>
184
+ <View style={styles.metaDivider} />
185
+
186
+ <View style={styles.metaRow}>
187
+ <View style={styles.metaLabelRow}>
188
+ <ClockIcon color={AppColors.grayTextWeak} size={14} />
189
+ <Text style={styles.metaLabel}>{t('network.durationHeader')}</Text>
190
+ </View>
191
+ <View style={styles.metaValueRow}>
192
+ {duration != null && !isFailed && (
193
+ <View
194
+ style={[
195
+ styles.perfBadge,
196
+ {
197
+ backgroundColor: `${getDurationColor(duration)}15`,
198
+ borderColor: `${getDurationColor(duration)}40`,
199
+ },
200
+ ]}>
201
+ <Text
202
+ style={[
203
+ styles.perfBadgeText,
204
+ {color: getDurationColor(duration)},
205
+ ]}>
206
+ {duration < DURATION_FAST_MS
207
+ ? t('network.perf.fast')
208
+ : duration < DURATION_SLOW_MS
209
+ ? t('network.perf.moderate')
210
+ : t('network.perf.slow')}
211
+ </Text>
212
+ </View>
213
+ )}
214
+ <Text style={styles.metaValue}>
215
+ {duration != null ? `${duration} ms` : '—'}
216
+ </Text>
217
+ </View>
218
+ </View>
219
+
220
+ {/* Visual Latency Timing Breakdown Waterfall */}
221
+ {duration != null && !isFailed && (
222
+ <View style={{paddingVertical: 8, gap: 6}}>
223
+ <View style={{flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center'}}>
224
+ <Text style={{fontFamily: AppFonts.interBold, fontSize: 11, color: AppColors.primaryBlack}}>
225
+ {t('network.timingWaterfall')}
226
+ </Text>
227
+ <Text style={{fontFamily: AppFonts.interMedium, fontSize: 10.5, color: getDurationColor(duration)}}>
228
+ {duration < DURATION_FAST_MS
229
+ ? t('network.perf.fast')
230
+ : duration < DURATION_SLOW_MS
231
+ ? t('network.perf.moderate')
232
+ : t('network.perf.slow')}
233
+ </Text>
234
+ </View>
235
+
236
+ <View
237
+ style={{
238
+ height: 6,
239
+ backgroundColor: AppColors.grayBorderSecondary,
240
+ borderRadius: 3,
241
+ overflow: 'hidden',
242
+ }}>
243
+ <View
244
+ style={{
245
+ height: '100%',
246
+ width: `${Math.min(100, Math.max(8, (duration / 1200) * 100))}%`,
247
+ backgroundColor: getDurationColor(duration),
248
+ borderRadius: 3,
249
+ }}
250
+ />
251
+ </View>
252
+
253
+ <View style={{flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginTop: 1}}>
254
+ <Text style={{fontFamily: AppFonts.interRegular, fontSize: 9.5, color: AppColors.grayTextWeak}}>
255
+ 0 ms
256
+ </Text>
257
+ <Text style={{fontFamily: AppFonts.interBold, fontSize: 10, color: getDurationColor(duration)}}>
258
+ {duration} ms
259
+ </Text>
260
+ <Text style={{fontFamily: AppFonts.interRegular, fontSize: 9.5, color: AppColors.grayTextWeak}}>
261
+ 1,200+ ms
262
+ </Text>
263
+ </View>
264
+ </View>
265
+ )}
266
+ <View style={styles.metaDivider} />
267
+
268
+ <View style={styles.metaRow}>
269
+ <View style={styles.metaLabelRow}>
270
+ <SizeIcon color={AppColors.grayTextWeak} />
271
+ <Text style={styles.metaLabel}>{t('network.sizeHeader')}</Text>
272
+ </View>
273
+ <Text style={styles.metaValue}>{size}</Text>
274
+ </View>
275
+ <View style={styles.metaDivider} />
276
+
277
+ <View style={[styles.metaRow, {alignItems: 'flex-start', flexDirection: 'column', gap: 6}]}>
278
+ <View style={{flexDirection: 'row', justifyContent: 'space-between', width: '100%', alignItems: 'center'}}>
279
+ <View style={styles.metaLabelRow}>
280
+ <GlobeIcon color={AppColors.grayTextWeak} size={14} />
281
+ <Text style={styles.metaLabel}>{t('network.fullUrl')}</Text>
282
+ </View>
283
+
284
+ <View style={{flexDirection: 'row', alignItems: 'center', gap: 10}}>
285
+ {url && url.length > 50 && (
286
+ <TouchableScale onPress={() => setUrlExpanded(prev => !prev)} hitSlop={8}>
287
+ <Text style={{
288
+ fontFamily: AppFonts.interBold,
289
+ fontSize: 10.5,
290
+ color: AppColors.purple,
291
+ }}>
292
+ {urlExpanded ? t('network.showLess') : t('network.showMore')}
293
+ </Text>
294
+ </TouchableScale>
295
+ )}
296
+
297
+ {url && (
298
+ <TouchableScale onPress={() => handleOpenExternalLink(url)} hitSlop={8}>
299
+ <Text style={{
300
+ fontFamily: AppFonts.interBold,
301
+ fontSize: 10.5,
302
+ color: AppColors.purple,
303
+ textDecorationLine: 'underline',
304
+ }}>
305
+ {t('network.open')}
306
+ </Text>
307
+ </TouchableScale>
308
+ )}
309
+ </View>
310
+ </View>
311
+
312
+ <View style={{
313
+ backgroundColor: AppColors.slate50,
314
+ borderRadius: 8,
315
+ padding: 8,
316
+ borderWidth: 1.5,
317
+ borderColor: AppColors.slate200,
318
+ width: '100%',
319
+ marginTop: 2,
320
+ }}>
321
+ <Text
322
+ selectable={true}
323
+ numberOfLines={urlExpanded ? undefined : 2}
324
+ ellipsizeMode="tail"
325
+ style={{
326
+ fontSize: 11,
327
+ textAlign: 'left',
328
+ lineHeight: 16.5,
329
+ }}>
330
+ {renderFormattedUrl(url)}
331
+ </Text>
332
+ </View>
333
+ </View>
334
+ </View>
335
+ </Animated.View>
336
+ </View>
337
+ );
338
+ };
339
+
340
+ export default MetaAccordion;
@@ -0,0 +1,42 @@
1
+ import React from 'react';
2
+ import Svg, {Rect} from 'react-native-svg';
3
+
4
+ const MiniBarChart = ({
5
+ data,
6
+ color,
7
+ maxVal,
8
+ }: {
9
+ data: number[];
10
+ color: string;
11
+ maxVal?: number;
12
+ }) => {
13
+ if (!data || data.length === 0) return <Svg width={40} height={16} />;
14
+ const max = maxVal || Math.max(...data, 1);
15
+ const paddedData =
16
+ data.length < 10
17
+ ? [...Array(10 - data.length).fill(0), ...data]
18
+ : data.slice(-10);
19
+
20
+ return (
21
+ <Svg width={40} height={16} viewBox="0 0 40 16">
22
+ {paddedData.map((val, i) => {
23
+ const h = Math.max((val / max) * 16, 2);
24
+ const opacity = val === 0 && maxVal === 1 ? 0.2 : 1;
25
+ return (
26
+ <Rect
27
+ key={i}
28
+ x={i * 4}
29
+ y={16 - h}
30
+ width={2.5}
31
+ height={h}
32
+ fill={color}
33
+ rx={1}
34
+ opacity={opacity}
35
+ />
36
+ );
37
+ })}
38
+ </Svg>
39
+ );
40
+ };
41
+
42
+ export default MiniBarChart;
@@ -0,0 +1,33 @@
1
+ import React from 'react';
2
+ import Svg, {Path} from 'react-native-svg';
3
+
4
+ const MiniLineChart = ({data, color}: {data: number[]; color: string}) => {
5
+ if (!data || data.length === 0) return <Svg width={40} height={16} />;
6
+
7
+ const paddedData =
8
+ data.length < 10
9
+ ? [...Array(10 - data.length).fill(data[0] || 0), ...data]
10
+ : data.slice(-10);
11
+ const max = Math.max(...paddedData, 1);
12
+ const min = Math.min(...paddedData, 0);
13
+ const range = max - min || 1;
14
+ const dx = 40 / 9;
15
+ const points = paddedData
16
+ .map((d, i) => `${i * dx},${14 - ((d - min) / range) * 12}`)
17
+ .join(' L ');
18
+
19
+ return (
20
+ <Svg width={40} height={16} viewBox="0 0 40 16">
21
+ <Path
22
+ d={`M ${points}`}
23
+ fill="none"
24
+ stroke={color}
25
+ strokeWidth={1.5}
26
+ strokeLinecap="round"
27
+ strokeLinejoin="round"
28
+ />
29
+ </Svg>
30
+ );
31
+ };
32
+
33
+ export default MiniLineChart;