@one_deploy/sdk 1.0.6 → 1.0.7

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.
@@ -1,35 +1,40 @@
1
1
  /**
2
- * OnePairSelector - Trading pair selection component for AI trading
3
- * Part of ONE Ecosystem SDK - can be used by any ecosystem partner
2
+ * OnePairSelector - Trading pair selection component with dropdown style
3
+ * Part of ONE Ecosystem SDK - Responsive design for desktop and mobile
4
4
  */
5
5
 
6
- import React from 'react';
7
- import { View, Text, StyleSheet, TouchableOpacity, ViewStyle, TextStyle } from 'react-native';
6
+ import React, { useState } from 'react';
7
+ import { View, Text, StyleSheet, TouchableOpacity, ViewStyle, TextStyle, Modal, ScrollView, Pressable, Dimensions, Platform } from 'react-native';
8
8
 
9
9
  // Common trading pairs with icons
10
- export const PAIR_ICONS: Record<string, string> = {
11
- 'BTC/USDT': '₿',
12
- 'ETH/USDT': 'Ξ',
13
- 'SOL/USDT': '',
14
- 'BNB/USDT': '',
15
- 'XRP/USDT': '✕',
16
- 'DOGE/USDT': 'Ð',
17
- 'ADA/USDT': '',
18
- 'AVAX/USDT': '▲',
19
- 'DOT/USDT': '●',
20
- 'MATIC/USDT': '⬡',
21
- 'LINK/USDT': '◇',
22
- 'UNI/USDT': '🦄',
23
- 'ATOM/USDT': '⚛',
24
- 'LTC/USDT': 'Ł',
25
- 'ARB/USDT': '◆',
26
- 'OP/USDT': '◉',
10
+ export const PAIR_CONFIG: Record<string, { symbol: string; name: string; icon: string; color: string }> = {
11
+ 'BTC': { symbol: 'BTC/USDT', name: 'Bitcoin', icon: '₿', color: '#F7931A' },
12
+ 'ETH': { symbol: 'ETH/USDT', name: 'Ethereum', icon: 'Ξ', color: '#627EEA' },
13
+ 'BNB': { symbol: 'BNB/USDT', name: 'BNB', icon: '◆', color: '#F3BA2F' },
14
+ 'SOL': { symbol: 'SOL/USDT', name: 'Solana', icon: '◎', color: '#9945FF' },
15
+ 'XRP': { symbol: 'XRP/USDT', name: 'Ripple', icon: '✕', color: '#23292F' },
16
+ 'DOGE': { symbol: 'DOGE/USDT', name: 'Dogecoin', icon: 'Ð', color: '#C2A633' },
17
+ 'ADA': { symbol: 'ADA/USDT', name: 'Cardano', icon: '₳', color: '#0033AD' },
18
+ 'AVAX': { symbol: 'AVAX/USDT', name: 'Avalanche', icon: '▲', color: '#E84142' },
19
+ 'DOT': { symbol: 'DOT/USDT', name: 'Polkadot', icon: '●', color: '#E6007A' },
20
+ 'MATIC': { symbol: 'MATIC/USDT', name: 'Polygon', icon: '⬡', color: '#8247E5' },
21
+ 'LINK': { symbol: 'LINK/USDT', name: 'Chainlink', icon: '◇', color: '#375BD2' },
22
+ 'UNI': { symbol: 'UNI/USDT', name: 'Uniswap', icon: '🦄', color: '#FF007A' },
23
+ 'ATOM': { symbol: 'ATOM/USDT', name: 'Cosmos', icon: '⚛', color: '#2E3148' },
24
+ 'LTC': { symbol: 'LTC/USDT', name: 'Litecoin', icon: 'Ł', color: '#345D9D' },
25
+ 'ARB': { symbol: 'ARB/USDT', name: 'Arbitrum', icon: '◆', color: '#28A0F0' },
26
+ 'OP': { symbol: 'OP/USDT', name: 'Optimism', icon: '◉', color: '#FF0420' },
27
27
  };
28
28
 
29
+ // Legacy mapping for backward compatibility
30
+ export const PAIR_ICONS: Record<string, string> = Object.fromEntries(
31
+ Object.entries(PAIR_CONFIG).map(([key, value]) => [`${key}/USDT`, value.icon])
32
+ );
33
+
29
34
  export interface OnePairSelectorProps {
30
- /** List of supported trading pairs */
35
+ /** List of supported trading pair IDs (e.g., 'BTC', 'ETH') */
31
36
  supportedPairs: string[];
32
- /** Currently selected pairs */
37
+ /** Currently selected pair IDs */
33
38
  selectedPairs: string[];
34
39
  /** Callback when pair selection changes */
35
40
  onTogglePair: (pair: string) => void;
@@ -43,12 +48,19 @@ export interface OnePairSelectorProps {
43
48
  minSelections?: number;
44
49
  /** Maximum allowed selections (0 = unlimited) */
45
50
  maxSelections?: number;
51
+ /** Placeholder text */
52
+ placeholder?: string;
46
53
  /** Custom styles */
47
54
  style?: ViewStyle;
48
55
  /** Custom title style */
49
56
  titleStyle?: TextStyle;
50
57
  }
51
58
 
59
+ const isDesktop = () => {
60
+ const { width } = Dimensions.get('window');
61
+ return Platform.OS === 'web' && width >= 768;
62
+ };
63
+
52
64
  export const OnePairSelector: React.FC<OnePairSelectorProps> = ({
53
65
  supportedPairs,
54
66
  selectedPairs,
@@ -58,18 +70,20 @@ export const OnePairSelector: React.FC<OnePairSelectorProps> = ({
58
70
  subtitle,
59
71
  minSelections = 1,
60
72
  maxSelections = 0,
73
+ placeholder = 'Select trading pairs...',
61
74
  style,
62
75
  titleStyle,
63
76
  }) => {
77
+ const [isOpen, setIsOpen] = useState(false);
78
+ const desktop = isDesktop();
79
+
64
80
  const handleToggle = (pair: string) => {
65
81
  const isSelected = selectedPairs.includes(pair);
66
82
 
67
- // Prevent deselecting below minimum
68
83
  if (isSelected && selectedPairs.length <= minSelections) {
69
84
  return;
70
85
  }
71
86
 
72
- // Prevent selecting above maximum
73
87
  if (!isSelected && maxSelections > 0 && selectedPairs.length >= maxSelections) {
74
88
  return;
75
89
  }
@@ -77,50 +91,190 @@ export const OnePairSelector: React.FC<OnePairSelectorProps> = ({
77
91
  onTogglePair(pair);
78
92
  };
79
93
 
94
+ const getPairInfo = (pair: string) => {
95
+ return PAIR_CONFIG[pair] || { symbol: `${pair}/USDT`, name: pair, icon: '●', color: '#888' };
96
+ };
97
+
98
+ // Desktop: Inline grid display
99
+ if (desktop) {
100
+ return (
101
+ <View style={[styles.container, style]}>
102
+ {title && <Text style={[styles.title, titleStyle]}>{title}</Text>}
103
+ {subtitle && <Text style={styles.subtitle}>{subtitle}</Text>}
104
+
105
+ <View style={styles.desktopGrid}>
106
+ {supportedPairs.map((pair) => {
107
+ const pairInfo = getPairInfo(pair);
108
+ const isSelected = selectedPairs.includes(pair);
109
+ const isDisabled = !isSelected && maxSelections > 0 && selectedPairs.length >= maxSelections;
110
+
111
+ return (
112
+ <TouchableOpacity
113
+ key={pair}
114
+ style={[
115
+ styles.desktopChip,
116
+ isSelected && styles.desktopChipSelected,
117
+ isSelected && { borderColor: accentColor, backgroundColor: accentColor + '12' },
118
+ isDisabled && styles.desktopChipDisabled,
119
+ ]}
120
+ onPress={() => handleToggle(pair)}
121
+ activeOpacity={isDisabled ? 1 : 0.7}
122
+ >
123
+ <View style={[styles.desktopIconBg, { backgroundColor: pairInfo.color + '20' }]}>
124
+ <Text style={[styles.desktopIcon, { color: pairInfo.color }]}>{pairInfo.icon}</Text>
125
+ </View>
126
+ <View>
127
+ <Text style={[
128
+ styles.desktopChipText,
129
+ isSelected && { color: accentColor, fontWeight: '600' },
130
+ isDisabled && styles.desktopTextDisabled,
131
+ ]}>
132
+ {pair}
133
+ </Text>
134
+ <Text style={[styles.desktopChipSubtext, isDisabled && styles.desktopTextDisabled]}>
135
+ {pairInfo.name}
136
+ </Text>
137
+ </View>
138
+ {isSelected && (
139
+ <View style={[styles.desktopCheckbox, { backgroundColor: accentColor }]}>
140
+ <Text style={styles.desktopCheckIcon}>✓</Text>
141
+ </View>
142
+ )}
143
+ </TouchableOpacity>
144
+ );
145
+ })}
146
+ </View>
147
+
148
+ <View style={styles.infoRow}>
149
+ <Text style={[styles.selectedCount, { color: accentColor }]}>
150
+ {selectedPairs.length} pair{selectedPairs.length !== 1 ? 's' : ''} selected
151
+ </Text>
152
+ {(minSelections > 0 || maxSelections > 0) && (
153
+ <Text style={styles.limitText}>
154
+ {minSelections > 0 && `min: ${minSelections}`}
155
+ {minSelections > 0 && maxSelections > 0 && ' / '}
156
+ {maxSelections > 0 && `max: ${maxSelections}`}
157
+ </Text>
158
+ )}
159
+ </View>
160
+ </View>
161
+ );
162
+ }
163
+
164
+ // Mobile: Dropdown modal
80
165
  return (
81
166
  <View style={[styles.container, style]}>
82
167
  {title && <Text style={[styles.title, titleStyle]}>{title}</Text>}
83
168
  {subtitle && <Text style={styles.subtitle}>{subtitle}</Text>}
84
169
 
85
- <View style={styles.pairsContainer}>
86
- {supportedPairs.map((pair) => {
87
- const isSelected = selectedPairs.includes(pair);
88
- const icon = PAIR_ICONS[pair] || '●';
89
- const baseSymbol = pair.split('/')[0];
90
-
91
- return (
92
- <TouchableOpacity
93
- key={pair}
94
- style={[
95
- styles.pairChip,
96
- isSelected && styles.pairChipSelected,
97
- isSelected && { backgroundColor: accentColor + '15', borderColor: accentColor }
98
- ]}
99
- onPress={() => handleToggle(pair)}
100
- activeOpacity={0.7}
101
- >
102
- <Text style={styles.pairIcon}>{icon}</Text>
103
- <Text style={[
104
- styles.pairText,
105
- isSelected && { color: accentColor, fontWeight: '600' }
106
- ]}>
107
- {baseSymbol}
108
- </Text>
109
- {isSelected && (
110
- <Text style={[styles.checkmark, { color: accentColor }]}>✓</Text>
111
- )}
112
- </TouchableOpacity>
113
- );
114
- })}
115
- </View>
170
+ <TouchableOpacity
171
+ style={[styles.dropdownTrigger, { borderColor: isOpen ? accentColor : '#e5e5e5' }]}
172
+ onPress={() => setIsOpen(true)}
173
+ activeOpacity={0.7}
174
+ >
175
+ <View style={styles.selectedPreview}>
176
+ {selectedPairs.slice(0, 4).map((pair) => {
177
+ const pairInfo = getPairInfo(pair);
178
+ return (
179
+ <View key={pair} style={[styles.previewChip, { backgroundColor: pairInfo.color + '20' }]}>
180
+ <Text style={[styles.previewIcon, { color: pairInfo.color }]}>{pairInfo.icon}</Text>
181
+ </View>
182
+ );
183
+ })}
184
+ {selectedPairs.length > 4 && (
185
+ <View style={styles.previewMore}>
186
+ <Text style={styles.previewMoreText}>+{selectedPairs.length - 4}</Text>
187
+ </View>
188
+ )}
189
+ {selectedPairs.length === 0 && (
190
+ <Text style={styles.placeholderText}>{placeholder}</Text>
191
+ )}
192
+ </View>
193
+ <Text style={styles.dropdownArrow}>{isOpen ? '▲' : '▼'}</Text>
194
+ </TouchableOpacity>
116
195
 
117
- <View style={styles.selectedInfo}>
118
- <Text style={styles.selectedText}>
196
+ <View style={styles.infoRow}>
197
+ <Text style={[styles.selectedCount, { color: accentColor }]}>
119
198
  {selectedPairs.length} pair{selectedPairs.length !== 1 ? 's' : ''} selected
120
- {minSelections > 0 && ` (min: ${minSelections})`}
121
- {maxSelections > 0 && ` (max: ${maxSelections})`}
122
199
  </Text>
200
+ {(minSelections > 0 || maxSelections > 0) && (
201
+ <Text style={styles.limitText}>
202
+ {minSelections > 0 && `min: ${minSelections}`}
203
+ {minSelections > 0 && maxSelections > 0 && ' / '}
204
+ {maxSelections > 0 && `max: ${maxSelections}`}
205
+ </Text>
206
+ )}
123
207
  </View>
208
+
209
+ <Modal
210
+ visible={isOpen}
211
+ transparent
212
+ animationType="fade"
213
+ onRequestClose={() => setIsOpen(false)}
214
+ >
215
+ <Pressable style={styles.modalOverlay} onPress={() => setIsOpen(false)}>
216
+ <View style={styles.modalContent}>
217
+ <View style={styles.modalHeader}>
218
+ <Text style={styles.modalTitle}>{title || 'Select Trading Pairs'}</Text>
219
+ <TouchableOpacity onPress={() => setIsOpen(false)}>
220
+ <Text style={styles.modalClose}>✕</Text>
221
+ </TouchableOpacity>
222
+ </View>
223
+
224
+ <ScrollView style={styles.optionsList} showsVerticalScrollIndicator={false}>
225
+ {supportedPairs.map((pair) => {
226
+ const pairInfo = getPairInfo(pair);
227
+ const isSelected = selectedPairs.includes(pair);
228
+ const isDisabled = !isSelected && maxSelections > 0 && selectedPairs.length >= maxSelections;
229
+
230
+ return (
231
+ <TouchableOpacity
232
+ key={pair}
233
+ style={[
234
+ styles.optionItem,
235
+ isSelected && styles.optionItemSelected,
236
+ isSelected && { backgroundColor: accentColor + '10', borderColor: accentColor },
237
+ isDisabled && styles.optionItemDisabled,
238
+ ]}
239
+ onPress={() => handleToggle(pair)}
240
+ activeOpacity={isDisabled ? 1 : 0.7}
241
+ >
242
+ <View style={[styles.optionIconBg, { backgroundColor: pairInfo.color + '20' }]}>
243
+ <Text style={[styles.optionIcon, { color: pairInfo.color }]}>{pairInfo.icon}</Text>
244
+ </View>
245
+ <View style={styles.optionTextContainer}>
246
+ <Text style={[
247
+ styles.optionSymbol,
248
+ isSelected && { color: accentColor, fontWeight: '600' },
249
+ isDisabled && styles.optionTextDisabled,
250
+ ]}>
251
+ {pair}
252
+ </Text>
253
+ <Text style={[styles.optionName, isDisabled && styles.optionTextDisabled]}>
254
+ {pairInfo.name}
255
+ </Text>
256
+ </View>
257
+ {isSelected ? (
258
+ <View style={[styles.checkbox, { backgroundColor: accentColor }]}>
259
+ <Text style={styles.checkboxIcon}>✓</Text>
260
+ </View>
261
+ ) : (
262
+ <View style={[styles.checkboxEmpty, isDisabled && styles.checkboxDisabled]} />
263
+ )}
264
+ </TouchableOpacity>
265
+ );
266
+ })}
267
+ </ScrollView>
268
+
269
+ <TouchableOpacity
270
+ style={[styles.doneButton, { backgroundColor: accentColor }]}
271
+ onPress={() => setIsOpen(false)}
272
+ >
273
+ <Text style={styles.doneButtonText}>Done ({selectedPairs.length})</Text>
274
+ </TouchableOpacity>
275
+ </View>
276
+ </Pressable>
277
+ </Modal>
124
278
  </View>
125
279
  );
126
280
  };
@@ -140,42 +294,245 @@ const styles = StyleSheet.create({
140
294
  color: '#666',
141
295
  marginBottom: 12,
142
296
  },
143
- pairsContainer: {
297
+ // Desktop styles
298
+ desktopGrid: {
144
299
  flexDirection: 'row',
145
300
  flexWrap: 'wrap',
146
- gap: 8,
301
+ gap: 10,
147
302
  },
148
- pairChip: {
303
+ desktopChip: {
149
304
  flexDirection: 'row',
150
305
  alignItems: 'center',
151
- paddingHorizontal: 12,
152
- paddingVertical: 8,
306
+ paddingHorizontal: 14,
307
+ paddingVertical: 10,
153
308
  backgroundColor: '#fff',
154
- borderRadius: 8,
155
- borderWidth: 1,
156
- borderColor: '#e5e5e5',
157
- gap: 6,
309
+ borderRadius: 10,
310
+ borderWidth: 2,
311
+ borderColor: '#e8e8e8',
312
+ gap: 10,
313
+ cursor: 'pointer' as any,
314
+ minWidth: 140,
158
315
  },
159
- pairChipSelected: {
316
+ desktopChipSelected: {
160
317
  borderWidth: 2,
161
318
  },
162
- pairIcon: {
319
+ desktopChipDisabled: {
320
+ opacity: 0.5,
321
+ },
322
+ desktopIconBg: {
323
+ width: 36,
324
+ height: 36,
325
+ borderRadius: 18,
326
+ alignItems: 'center',
327
+ justifyContent: 'center',
328
+ },
329
+ desktopIcon: {
330
+ fontSize: 16,
331
+ fontWeight: '700',
332
+ },
333
+ desktopChipText: {
163
334
  fontSize: 14,
335
+ fontWeight: '500',
336
+ color: '#333',
337
+ },
338
+ desktopChipSubtext: {
339
+ fontSize: 11,
340
+ color: '#888',
341
+ marginTop: 1,
342
+ },
343
+ desktopTextDisabled: {
344
+ color: '#bbb',
345
+ },
346
+ desktopCheckbox: {
347
+ width: 20,
348
+ height: 20,
349
+ borderRadius: 10,
350
+ alignItems: 'center',
351
+ justifyContent: 'center',
352
+ marginLeft: 'auto' as any,
353
+ },
354
+ desktopCheckIcon: {
355
+ fontSize: 12,
356
+ color: '#fff',
357
+ fontWeight: '700',
358
+ },
359
+ // Mobile dropdown styles
360
+ dropdownTrigger: {
361
+ flexDirection: 'row',
362
+ alignItems: 'center',
363
+ justifyContent: 'space-between',
364
+ paddingHorizontal: 14,
365
+ paddingVertical: 12,
366
+ backgroundColor: '#fff',
367
+ borderRadius: 12,
368
+ borderWidth: 2,
369
+ borderColor: '#e5e5e5',
370
+ minHeight: 52,
164
371
  },
165
- pairText: {
372
+ selectedPreview: {
373
+ flex: 1,
374
+ flexDirection: 'row',
375
+ alignItems: 'center',
376
+ gap: 6,
377
+ },
378
+ previewChip: {
379
+ width: 32,
380
+ height: 32,
381
+ borderRadius: 16,
382
+ alignItems: 'center',
383
+ justifyContent: 'center',
384
+ },
385
+ previewIcon: {
166
386
  fontSize: 14,
387
+ fontWeight: '700',
388
+ },
389
+ previewMore: {
390
+ paddingHorizontal: 8,
391
+ paddingVertical: 4,
392
+ backgroundColor: '#f0f0f0',
393
+ borderRadius: 8,
394
+ },
395
+ previewMoreText: {
396
+ fontSize: 12,
167
397
  color: '#666',
398
+ fontWeight: '600',
168
399
  },
169
- checkmark: {
400
+ placeholderText: {
170
401
  fontSize: 14,
402
+ color: '#999',
403
+ },
404
+ dropdownArrow: {
405
+ fontSize: 12,
406
+ color: '#888',
407
+ marginLeft: 8,
408
+ },
409
+ infoRow: {
410
+ flexDirection: 'row',
411
+ justifyContent: 'space-between',
412
+ alignItems: 'center',
413
+ marginTop: 6,
414
+ },
415
+ selectedCount: {
416
+ fontSize: 12,
417
+ fontWeight: '500',
418
+ },
419
+ limitText: {
420
+ fontSize: 11,
421
+ color: '#888',
422
+ },
423
+ // Modal styles
424
+ modalOverlay: {
425
+ flex: 1,
426
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
427
+ justifyContent: 'center',
428
+ alignItems: 'center',
429
+ },
430
+ modalContent: {
431
+ width: '85%',
432
+ maxWidth: 340,
433
+ maxHeight: '75%',
434
+ backgroundColor: '#fff',
435
+ borderRadius: 16,
436
+ overflow: 'hidden',
437
+ },
438
+ modalHeader: {
439
+ flexDirection: 'row',
440
+ justifyContent: 'space-between',
441
+ alignItems: 'center',
442
+ paddingHorizontal: 16,
443
+ paddingVertical: 14,
444
+ borderBottomWidth: 1,
445
+ borderBottomColor: '#f0f0f0',
446
+ },
447
+ modalTitle: {
448
+ fontSize: 18,
171
449
  fontWeight: '700',
450
+ color: '#1a1a1a',
172
451
  },
173
- selectedInfo: {
174
- marginTop: 8,
175
- paddingVertical: 4,
452
+ modalClose: {
453
+ fontSize: 20,
454
+ color: '#888',
455
+ padding: 4,
456
+ },
457
+ optionsList: {
458
+ padding: 8,
176
459
  },
177
- selectedText: {
460
+ optionItem: {
461
+ flexDirection: 'row',
462
+ alignItems: 'center',
463
+ paddingHorizontal: 12,
464
+ paddingVertical: 12,
465
+ borderRadius: 10,
466
+ borderWidth: 1,
467
+ borderColor: 'transparent',
468
+ marginBottom: 6,
469
+ gap: 12,
470
+ },
471
+ optionItemSelected: {
472
+ borderWidth: 1,
473
+ },
474
+ optionItemDisabled: {
475
+ opacity: 0.5,
476
+ },
477
+ optionIconBg: {
478
+ width: 40,
479
+ height: 40,
480
+ borderRadius: 20,
481
+ alignItems: 'center',
482
+ justifyContent: 'center',
483
+ },
484
+ optionIcon: {
485
+ fontSize: 18,
486
+ fontWeight: '700',
487
+ },
488
+ optionTextContainer: {
489
+ flex: 1,
490
+ },
491
+ optionSymbol: {
492
+ fontSize: 15,
493
+ fontWeight: '500',
494
+ color: '#333',
495
+ },
496
+ optionName: {
178
497
  fontSize: 12,
179
498
  color: '#888',
499
+ marginTop: 1,
500
+ },
501
+ optionTextDisabled: {
502
+ color: '#bbb',
503
+ },
504
+ checkbox: {
505
+ width: 24,
506
+ height: 24,
507
+ borderRadius: 12,
508
+ alignItems: 'center',
509
+ justifyContent: 'center',
510
+ },
511
+ checkboxIcon: {
512
+ fontSize: 14,
513
+ color: '#fff',
514
+ fontWeight: '700',
515
+ },
516
+ checkboxEmpty: {
517
+ width: 24,
518
+ height: 24,
519
+ borderRadius: 12,
520
+ borderWidth: 2,
521
+ borderColor: '#ddd',
522
+ },
523
+ checkboxDisabled: {
524
+ borderColor: '#eee',
525
+ },
526
+ doneButton: {
527
+ marginHorizontal: 16,
528
+ marginVertical: 12,
529
+ paddingVertical: 14,
530
+ borderRadius: 10,
531
+ alignItems: 'center',
532
+ },
533
+ doneButtonText: {
534
+ fontSize: 16,
535
+ fontWeight: '600',
536
+ color: '#fff',
180
537
  },
181
538
  });