@one_deploy/sdk 1.0.2 → 1.0.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.
- package/package.json +1 -1
- package/src/components/OneSwapWidget.tsx +1 -1
- package/src/components/ai/OneChainSelector.tsx +183 -0
- package/src/components/ai/OneCycleSelector.tsx +187 -0
- package/src/components/ai/OnePairSelector.tsx +181 -0
- package/src/components/ai/OneTierSelector.tsx +187 -0
- package/src/components/ai/index.ts +17 -0
- package/src/components/index.ts +3 -0
- package/src/config/index.ts +1 -1
- package/src/hooks/index.ts +20 -0
- package/src/hooks/useAITrading.ts +444 -0
- package/src/index.ts +20 -0
- package/src/react-native.ts +23 -0
- package/src/services/engine.ts +184 -0
- package/src/services/index.ts +16 -0
- package/src/services/usage.ts +249 -0
- package/.turbo/turbo-build.log +0 -0
- package/.turbo/turbo-type-check.log +0 -0
- package/dist/config/index.d.mts +0 -74
- package/dist/config/index.d.ts +0 -74
- package/dist/config/index.js +0 -244
- package/dist/config/index.js.map +0 -1
- package/dist/config/index.mjs +0 -226
- package/dist/config/index.mjs.map +0 -1
- package/dist/engine-5ndtBaCr.d.ts +0 -1039
- package/dist/engine-CrlhH0nw.d.mts +0 -1039
- package/dist/hooks/index.d.mts +0 -56
- package/dist/hooks/index.d.ts +0 -56
- package/dist/hooks/index.js +0 -1360
- package/dist/hooks/index.js.map +0 -1
- package/dist/hooks/index.mjs +0 -1356
- package/dist/hooks/index.mjs.map +0 -1
- package/dist/index.d.mts +0 -356
- package/dist/index.d.ts +0 -356
- package/dist/index.js +0 -5069
- package/dist/index.js.map +0 -1
- package/dist/index.mjs +0 -4950
- package/dist/index.mjs.map +0 -1
- package/dist/price-CgqXPnT3.d.ts +0 -13
- package/dist/price-ClbLHHjv.d.mts +0 -13
- package/dist/providers/index.d.mts +0 -121
- package/dist/providers/index.d.ts +0 -121
- package/dist/providers/index.js +0 -1643
- package/dist/providers/index.js.map +0 -1
- package/dist/providers/index.mjs +0 -1601
- package/dist/providers/index.mjs.map +0 -1
- package/dist/react-native.d.mts +0 -120
- package/dist/react-native.d.ts +0 -120
- package/dist/react-native.js +0 -1794
- package/dist/react-native.js.map +0 -1
- package/dist/react-native.mjs +0 -1757
- package/dist/react-native.mjs.map +0 -1
- package/dist/services/index.d.mts +0 -85
- package/dist/services/index.d.ts +0 -85
- package/dist/services/index.js +0 -1466
- package/dist/services/index.js.map +0 -1
- package/dist/services/index.mjs +0 -1458
- package/dist/services/index.mjs.map +0 -1
- package/dist/types/index.d.mts +0 -759
- package/dist/types/index.d.ts +0 -759
- package/dist/types/index.js +0 -4
- package/dist/types/index.js.map +0 -1
- package/dist/types/index.mjs +0 -3
- package/dist/types/index.mjs.map +0 -1
- package/dist/utils/index.d.mts +0 -36
- package/dist/utils/index.d.ts +0 -36
- package/dist/utils/index.js +0 -164
- package/dist/utils/index.js.map +0 -1
- package/dist/utils/index.mjs +0 -142
- package/dist/utils/index.mjs.map +0 -1
package/package.json
CHANGED
|
@@ -102,7 +102,7 @@ const TOKENS_BY_CHAIN: Record<number, SwapToken[]> = {
|
|
|
102
102
|
{ address: '0x0000000000000000000000000000000000000000', symbol: 'ETH', name: 'Ethereum', decimals: 18, chainId: 1 },
|
|
103
103
|
{ address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', symbol: 'USDC', name: 'USD Coin', decimals: 6, chainId: 1 },
|
|
104
104
|
{ address: '0xdAC17F958D2ee523a2206206994597C13D831ec7', symbol: 'USDT', name: 'Tether', decimals: 6, chainId: 1 },
|
|
105
|
-
{ address: '
|
|
105
|
+
{ address: '0x6B175474E89094C44Da98b954EedeAC495271d0F', symbol: 'DAI', name: 'Dai', decimals: 18, chainId: 1 },
|
|
106
106
|
{ address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', symbol: 'WETH', name: 'Wrapped Ether', decimals: 18, chainId: 1 },
|
|
107
107
|
],
|
|
108
108
|
// Base
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OneChainSelector - Multi-chain selection component for AI trading
|
|
3
|
+
* Part of ONE Ecosystem SDK - can be used by any ecosystem partner
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { View, Text, StyleSheet, TouchableOpacity, ViewStyle, TextStyle } from 'react-native';
|
|
8
|
+
|
|
9
|
+
// Chain configuration with branding
|
|
10
|
+
export const CHAIN_CONFIG: Record<string, { name: string; icon: string; color: string }> = {
|
|
11
|
+
ethereum: { name: 'Ethereum', icon: 'Ξ', color: '#627EEA' },
|
|
12
|
+
arbitrum: { name: 'Arbitrum', icon: '◆', color: '#28A0F0' },
|
|
13
|
+
bsc: { name: 'BSC', icon: '◆', color: '#F3BA2F' },
|
|
14
|
+
base: { name: 'Base', icon: '●', color: '#0052FF' },
|
|
15
|
+
polygon: { name: 'Polygon', icon: '⬡', color: '#8247E5' },
|
|
16
|
+
optimism: { name: 'Optimism', icon: '◉', color: '#FF0420' },
|
|
17
|
+
avalanche: { name: 'Avalanche', icon: '▲', color: '#E84142' },
|
|
18
|
+
linea: { name: 'Linea', icon: '═', color: '#121212' },
|
|
19
|
+
zksync: { name: 'zkSync', icon: '⬢', color: '#8C8DFC' },
|
|
20
|
+
scroll: { name: 'Scroll', icon: '◎', color: '#FFEEDA' },
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export interface OneChainSelectorProps {
|
|
24
|
+
/** List of supported chain IDs */
|
|
25
|
+
supportedChains: string[];
|
|
26
|
+
/** Currently selected chains */
|
|
27
|
+
selectedChains: string[];
|
|
28
|
+
/** Callback when chain selection changes */
|
|
29
|
+
onSelectChain: (chain: string) => void;
|
|
30
|
+
/** Enable multi-select (default: true) */
|
|
31
|
+
multiSelect?: boolean;
|
|
32
|
+
/** Accent color for selected state */
|
|
33
|
+
accentColor?: string;
|
|
34
|
+
/** Section title */
|
|
35
|
+
title?: string;
|
|
36
|
+
/** Section subtitle */
|
|
37
|
+
subtitle?: string;
|
|
38
|
+
/** Minimum required selections (for multi-select) */
|
|
39
|
+
minSelections?: number;
|
|
40
|
+
/** Custom styles */
|
|
41
|
+
style?: ViewStyle;
|
|
42
|
+
/** Custom title style */
|
|
43
|
+
titleStyle?: TextStyle;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const OneChainSelector: React.FC<OneChainSelectorProps> = ({
|
|
47
|
+
supportedChains,
|
|
48
|
+
selectedChains,
|
|
49
|
+
onSelectChain,
|
|
50
|
+
multiSelect = true,
|
|
51
|
+
accentColor = '#188775',
|
|
52
|
+
title,
|
|
53
|
+
subtitle,
|
|
54
|
+
minSelections = 1,
|
|
55
|
+
style,
|
|
56
|
+
titleStyle,
|
|
57
|
+
}) => {
|
|
58
|
+
const handleSelect = (chain: string) => {
|
|
59
|
+
if (multiSelect) {
|
|
60
|
+
// In multi-select, prevent deselecting if at minimum
|
|
61
|
+
if (selectedChains.includes(chain) && selectedChains.length <= minSelections) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
onSelectChain(chain);
|
|
65
|
+
} else {
|
|
66
|
+
// Single select - always allow selection
|
|
67
|
+
if (!selectedChains.includes(chain)) {
|
|
68
|
+
onSelectChain(chain);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<View style={[styles.container, style]}>
|
|
75
|
+
{title && <Text style={[styles.title, titleStyle]}>{title}</Text>}
|
|
76
|
+
{subtitle && <Text style={styles.subtitle}>{subtitle}</Text>}
|
|
77
|
+
|
|
78
|
+
<View style={styles.chainsContainer}>
|
|
79
|
+
{supportedChains.map((chain) => {
|
|
80
|
+
const chainInfo = CHAIN_CONFIG[chain] || { name: chain, icon: '●', color: '#888' };
|
|
81
|
+
const isSelected = selectedChains.includes(chain);
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<TouchableOpacity
|
|
85
|
+
key={chain}
|
|
86
|
+
style={[
|
|
87
|
+
styles.chainChip,
|
|
88
|
+
isSelected && styles.chainChipSelected,
|
|
89
|
+
isSelected && { borderColor: accentColor, backgroundColor: accentColor + '15' }
|
|
90
|
+
]}
|
|
91
|
+
onPress={() => handleSelect(chain)}
|
|
92
|
+
activeOpacity={0.7}
|
|
93
|
+
>
|
|
94
|
+
<View style={[styles.chainIconBg, { backgroundColor: chainInfo.color + '20' }]}>
|
|
95
|
+
<Text style={[styles.chainIcon, { color: chainInfo.color }]}>{chainInfo.icon}</Text>
|
|
96
|
+
</View>
|
|
97
|
+
<Text style={[
|
|
98
|
+
styles.chainText,
|
|
99
|
+
isSelected && { color: accentColor, fontWeight: '600' }
|
|
100
|
+
]}>
|
|
101
|
+
{chainInfo.name}
|
|
102
|
+
</Text>
|
|
103
|
+
{isSelected && (
|
|
104
|
+
<Text style={[styles.checkmark, { color: accentColor }]}>✓</Text>
|
|
105
|
+
)}
|
|
106
|
+
</TouchableOpacity>
|
|
107
|
+
);
|
|
108
|
+
})}
|
|
109
|
+
</View>
|
|
110
|
+
|
|
111
|
+
{multiSelect && selectedChains.length > 0 && (
|
|
112
|
+
<View style={styles.selectedInfo}>
|
|
113
|
+
<Text style={styles.selectedText}>
|
|
114
|
+
{selectedChains.length} chain{selectedChains.length > 1 ? 's' : ''} selected
|
|
115
|
+
</Text>
|
|
116
|
+
</View>
|
|
117
|
+
)}
|
|
118
|
+
</View>
|
|
119
|
+
);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const styles = StyleSheet.create({
|
|
123
|
+
container: {
|
|
124
|
+
marginBottom: 16,
|
|
125
|
+
},
|
|
126
|
+
title: {
|
|
127
|
+
fontSize: 16,
|
|
128
|
+
fontWeight: '600',
|
|
129
|
+
color: '#1a1a1a',
|
|
130
|
+
marginBottom: 4,
|
|
131
|
+
},
|
|
132
|
+
subtitle: {
|
|
133
|
+
fontSize: 12,
|
|
134
|
+
color: '#666',
|
|
135
|
+
marginBottom: 12,
|
|
136
|
+
},
|
|
137
|
+
chainsContainer: {
|
|
138
|
+
flexDirection: 'row',
|
|
139
|
+
flexWrap: 'wrap',
|
|
140
|
+
gap: 8,
|
|
141
|
+
},
|
|
142
|
+
chainChip: {
|
|
143
|
+
flexDirection: 'row',
|
|
144
|
+
alignItems: 'center',
|
|
145
|
+
paddingHorizontal: 12,
|
|
146
|
+
paddingVertical: 8,
|
|
147
|
+
backgroundColor: '#fff',
|
|
148
|
+
borderRadius: 12,
|
|
149
|
+
borderWidth: 2,
|
|
150
|
+
borderColor: '#e5e5e5',
|
|
151
|
+
gap: 8,
|
|
152
|
+
},
|
|
153
|
+
chainChipSelected: {
|
|
154
|
+
borderWidth: 2,
|
|
155
|
+
},
|
|
156
|
+
chainIconBg: {
|
|
157
|
+
width: 28,
|
|
158
|
+
height: 28,
|
|
159
|
+
borderRadius: 14,
|
|
160
|
+
alignItems: 'center',
|
|
161
|
+
justifyContent: 'center',
|
|
162
|
+
},
|
|
163
|
+
chainIcon: {
|
|
164
|
+
fontSize: 14,
|
|
165
|
+
fontWeight: '700',
|
|
166
|
+
},
|
|
167
|
+
chainText: {
|
|
168
|
+
fontSize: 14,
|
|
169
|
+
color: '#666',
|
|
170
|
+
},
|
|
171
|
+
checkmark: {
|
|
172
|
+
fontSize: 16,
|
|
173
|
+
fontWeight: '700',
|
|
174
|
+
},
|
|
175
|
+
selectedInfo: {
|
|
176
|
+
marginTop: 8,
|
|
177
|
+
paddingVertical: 4,
|
|
178
|
+
},
|
|
179
|
+
selectedText: {
|
|
180
|
+
fontSize: 12,
|
|
181
|
+
color: '#888',
|
|
182
|
+
},
|
|
183
|
+
});
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OneCycleSelector - Investment cycle selection component for AI trading
|
|
3
|
+
* Part of ONE Ecosystem SDK - can be used by any ecosystem partner
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { View, Text, StyleSheet, TouchableOpacity, ViewStyle, TextStyle } from 'react-native';
|
|
8
|
+
|
|
9
|
+
// Default share rates by cycle (platform takes this percentage)
|
|
10
|
+
export const DEFAULT_SHARE_RATES: Record<number, number> = {
|
|
11
|
+
7: 25,
|
|
12
|
+
14: 22,
|
|
13
|
+
30: 18,
|
|
14
|
+
60: 15,
|
|
15
|
+
90: 12,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export interface OneCycleSelectorProps {
|
|
19
|
+
/** List of supported cycle days */
|
|
20
|
+
supportedCycles: number[];
|
|
21
|
+
/** Currently selected cycle */
|
|
22
|
+
selectedCycle: number;
|
|
23
|
+
/** Callback when cycle selection changes */
|
|
24
|
+
onSelectCycle: (cycle: number) => void;
|
|
25
|
+
/** Accent color for selected state */
|
|
26
|
+
accentColor?: string;
|
|
27
|
+
/** Section title */
|
|
28
|
+
title?: string;
|
|
29
|
+
/** Section subtitle */
|
|
30
|
+
subtitle?: string;
|
|
31
|
+
/** Custom share rates by cycle (overrides defaults) */
|
|
32
|
+
shareRates?: Record<number, number>;
|
|
33
|
+
/** Days label */
|
|
34
|
+
daysLabel?: string;
|
|
35
|
+
/** Your share label */
|
|
36
|
+
yourShareLabel?: string;
|
|
37
|
+
/** Platform fee label */
|
|
38
|
+
platformFeeLabel?: string;
|
|
39
|
+
/** Custom styles */
|
|
40
|
+
style?: ViewStyle;
|
|
41
|
+
/** Custom title style */
|
|
42
|
+
titleStyle?: TextStyle;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const OneCycleSelector: React.FC<OneCycleSelectorProps> = ({
|
|
46
|
+
supportedCycles,
|
|
47
|
+
selectedCycle,
|
|
48
|
+
onSelectCycle,
|
|
49
|
+
accentColor = '#188775',
|
|
50
|
+
title,
|
|
51
|
+
subtitle,
|
|
52
|
+
shareRates = DEFAULT_SHARE_RATES,
|
|
53
|
+
daysLabel = 'days',
|
|
54
|
+
yourShareLabel = 'Your share',
|
|
55
|
+
platformFeeLabel = 'Platform fee',
|
|
56
|
+
style,
|
|
57
|
+
titleStyle,
|
|
58
|
+
}) => {
|
|
59
|
+
return (
|
|
60
|
+
<View style={[styles.container, style]}>
|
|
61
|
+
{title && <Text style={[styles.title, titleStyle]}>{title}</Text>}
|
|
62
|
+
{subtitle && <Text style={styles.subtitle}>{subtitle}</Text>}
|
|
63
|
+
|
|
64
|
+
<View style={styles.cyclesContainer}>
|
|
65
|
+
{supportedCycles.map((cycle) => {
|
|
66
|
+
const isSelected = selectedCycle === cycle;
|
|
67
|
+
const shareRate = shareRates[cycle] || 20;
|
|
68
|
+
const userRate = 100 - shareRate;
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<TouchableOpacity
|
|
72
|
+
key={cycle}
|
|
73
|
+
style={[
|
|
74
|
+
styles.cycleOption,
|
|
75
|
+
isSelected && styles.cycleOptionSelected,
|
|
76
|
+
isSelected && { borderColor: accentColor, backgroundColor: accentColor + '10' }
|
|
77
|
+
]}
|
|
78
|
+
onPress={() => onSelectCycle(cycle)}
|
|
79
|
+
activeOpacity={0.7}
|
|
80
|
+
>
|
|
81
|
+
<Text style={[
|
|
82
|
+
styles.cycleDays,
|
|
83
|
+
isSelected && { color: accentColor }
|
|
84
|
+
]}>
|
|
85
|
+
{cycle}
|
|
86
|
+
</Text>
|
|
87
|
+
<Text style={styles.cycleDaysLabel}>{daysLabel}</Text>
|
|
88
|
+
|
|
89
|
+
<View style={styles.cycleEarnings}>
|
|
90
|
+
<Text style={[
|
|
91
|
+
styles.cycleEarningsValue,
|
|
92
|
+
isSelected && { color: accentColor }
|
|
93
|
+
]}>
|
|
94
|
+
{userRate}%
|
|
95
|
+
</Text>
|
|
96
|
+
<Text style={styles.cycleEarningsLabel}>{yourShareLabel}</Text>
|
|
97
|
+
</View>
|
|
98
|
+
|
|
99
|
+
<Text style={styles.cycleFee}>
|
|
100
|
+
{shareRate}% {platformFeeLabel}
|
|
101
|
+
</Text>
|
|
102
|
+
</TouchableOpacity>
|
|
103
|
+
);
|
|
104
|
+
})}
|
|
105
|
+
</View>
|
|
106
|
+
|
|
107
|
+
<View style={styles.infoBox}>
|
|
108
|
+
<Text style={styles.infoText}>
|
|
109
|
+
Longer cycles = higher user share
|
|
110
|
+
</Text>
|
|
111
|
+
</View>
|
|
112
|
+
</View>
|
|
113
|
+
);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const styles = StyleSheet.create({
|
|
117
|
+
container: {
|
|
118
|
+
marginBottom: 16,
|
|
119
|
+
},
|
|
120
|
+
title: {
|
|
121
|
+
fontSize: 16,
|
|
122
|
+
fontWeight: '600',
|
|
123
|
+
color: '#1a1a1a',
|
|
124
|
+
marginBottom: 4,
|
|
125
|
+
},
|
|
126
|
+
subtitle: {
|
|
127
|
+
fontSize: 12,
|
|
128
|
+
color: '#666',
|
|
129
|
+
marginBottom: 12,
|
|
130
|
+
},
|
|
131
|
+
cyclesContainer: {
|
|
132
|
+
flexDirection: 'row',
|
|
133
|
+
flexWrap: 'wrap',
|
|
134
|
+
gap: 8,
|
|
135
|
+
},
|
|
136
|
+
cycleOption: {
|
|
137
|
+
flex: 1,
|
|
138
|
+
minWidth: '45%',
|
|
139
|
+
padding: 16,
|
|
140
|
+
borderRadius: 12,
|
|
141
|
+
borderWidth: 2,
|
|
142
|
+
borderColor: '#e5e5e5',
|
|
143
|
+
backgroundColor: '#fff',
|
|
144
|
+
alignItems: 'center',
|
|
145
|
+
},
|
|
146
|
+
cycleOptionSelected: {
|
|
147
|
+
borderWidth: 2,
|
|
148
|
+
},
|
|
149
|
+
cycleDays: {
|
|
150
|
+
fontSize: 28,
|
|
151
|
+
fontWeight: '700',
|
|
152
|
+
color: '#1a1a1a',
|
|
153
|
+
},
|
|
154
|
+
cycleDaysLabel: {
|
|
155
|
+
fontSize: 12,
|
|
156
|
+
color: '#666',
|
|
157
|
+
marginBottom: 8,
|
|
158
|
+
},
|
|
159
|
+
cycleEarnings: {
|
|
160
|
+
alignItems: 'center',
|
|
161
|
+
},
|
|
162
|
+
cycleEarningsValue: {
|
|
163
|
+
fontSize: 18,
|
|
164
|
+
fontWeight: '700',
|
|
165
|
+
color: '#10B981',
|
|
166
|
+
},
|
|
167
|
+
cycleEarningsLabel: {
|
|
168
|
+
fontSize: 11,
|
|
169
|
+
color: '#666',
|
|
170
|
+
},
|
|
171
|
+
cycleFee: {
|
|
172
|
+
fontSize: 11,
|
|
173
|
+
color: '#888',
|
|
174
|
+
marginTop: 4,
|
|
175
|
+
},
|
|
176
|
+
infoBox: {
|
|
177
|
+
marginTop: 12,
|
|
178
|
+
padding: 8,
|
|
179
|
+
backgroundColor: '#f5f5f5',
|
|
180
|
+
borderRadius: 8,
|
|
181
|
+
},
|
|
182
|
+
infoText: {
|
|
183
|
+
fontSize: 12,
|
|
184
|
+
color: '#666',
|
|
185
|
+
textAlign: 'center',
|
|
186
|
+
},
|
|
187
|
+
});
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OnePairSelector - Trading pair selection component for AI trading
|
|
3
|
+
* Part of ONE Ecosystem SDK - can be used by any ecosystem partner
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { View, Text, StyleSheet, TouchableOpacity, ViewStyle, TextStyle } from 'react-native';
|
|
8
|
+
|
|
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': '◉',
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export interface OnePairSelectorProps {
|
|
30
|
+
/** List of supported trading pairs */
|
|
31
|
+
supportedPairs: string[];
|
|
32
|
+
/** Currently selected pairs */
|
|
33
|
+
selectedPairs: string[];
|
|
34
|
+
/** Callback when pair selection changes */
|
|
35
|
+
onTogglePair: (pair: string) => void;
|
|
36
|
+
/** Accent color for selected state */
|
|
37
|
+
accentColor?: string;
|
|
38
|
+
/** Section title */
|
|
39
|
+
title?: string;
|
|
40
|
+
/** Section subtitle */
|
|
41
|
+
subtitle?: string;
|
|
42
|
+
/** Minimum required selections */
|
|
43
|
+
minSelections?: number;
|
|
44
|
+
/** Maximum allowed selections (0 = unlimited) */
|
|
45
|
+
maxSelections?: number;
|
|
46
|
+
/** Custom styles */
|
|
47
|
+
style?: ViewStyle;
|
|
48
|
+
/** Custom title style */
|
|
49
|
+
titleStyle?: TextStyle;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const OnePairSelector: React.FC<OnePairSelectorProps> = ({
|
|
53
|
+
supportedPairs,
|
|
54
|
+
selectedPairs,
|
|
55
|
+
onTogglePair,
|
|
56
|
+
accentColor = '#188775',
|
|
57
|
+
title,
|
|
58
|
+
subtitle,
|
|
59
|
+
minSelections = 1,
|
|
60
|
+
maxSelections = 0,
|
|
61
|
+
style,
|
|
62
|
+
titleStyle,
|
|
63
|
+
}) => {
|
|
64
|
+
const handleToggle = (pair: string) => {
|
|
65
|
+
const isSelected = selectedPairs.includes(pair);
|
|
66
|
+
|
|
67
|
+
// Prevent deselecting below minimum
|
|
68
|
+
if (isSelected && selectedPairs.length <= minSelections) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Prevent selecting above maximum
|
|
73
|
+
if (!isSelected && maxSelections > 0 && selectedPairs.length >= maxSelections) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
onTogglePair(pair);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<View style={[styles.container, style]}>
|
|
82
|
+
{title && <Text style={[styles.title, titleStyle]}>{title}</Text>}
|
|
83
|
+
{subtitle && <Text style={styles.subtitle}>{subtitle}</Text>}
|
|
84
|
+
|
|
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>
|
|
116
|
+
|
|
117
|
+
<View style={styles.selectedInfo}>
|
|
118
|
+
<Text style={styles.selectedText}>
|
|
119
|
+
{selectedPairs.length} pair{selectedPairs.length !== 1 ? 's' : ''} selected
|
|
120
|
+
{minSelections > 0 && ` (min: ${minSelections})`}
|
|
121
|
+
{maxSelections > 0 && ` (max: ${maxSelections})`}
|
|
122
|
+
</Text>
|
|
123
|
+
</View>
|
|
124
|
+
</View>
|
|
125
|
+
);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const styles = StyleSheet.create({
|
|
129
|
+
container: {
|
|
130
|
+
marginBottom: 16,
|
|
131
|
+
},
|
|
132
|
+
title: {
|
|
133
|
+
fontSize: 16,
|
|
134
|
+
fontWeight: '600',
|
|
135
|
+
color: '#1a1a1a',
|
|
136
|
+
marginBottom: 4,
|
|
137
|
+
},
|
|
138
|
+
subtitle: {
|
|
139
|
+
fontSize: 12,
|
|
140
|
+
color: '#666',
|
|
141
|
+
marginBottom: 12,
|
|
142
|
+
},
|
|
143
|
+
pairsContainer: {
|
|
144
|
+
flexDirection: 'row',
|
|
145
|
+
flexWrap: 'wrap',
|
|
146
|
+
gap: 8,
|
|
147
|
+
},
|
|
148
|
+
pairChip: {
|
|
149
|
+
flexDirection: 'row',
|
|
150
|
+
alignItems: 'center',
|
|
151
|
+
paddingHorizontal: 12,
|
|
152
|
+
paddingVertical: 8,
|
|
153
|
+
backgroundColor: '#fff',
|
|
154
|
+
borderRadius: 8,
|
|
155
|
+
borderWidth: 1,
|
|
156
|
+
borderColor: '#e5e5e5',
|
|
157
|
+
gap: 6,
|
|
158
|
+
},
|
|
159
|
+
pairChipSelected: {
|
|
160
|
+
borderWidth: 2,
|
|
161
|
+
},
|
|
162
|
+
pairIcon: {
|
|
163
|
+
fontSize: 14,
|
|
164
|
+
},
|
|
165
|
+
pairText: {
|
|
166
|
+
fontSize: 14,
|
|
167
|
+
color: '#666',
|
|
168
|
+
},
|
|
169
|
+
checkmark: {
|
|
170
|
+
fontSize: 14,
|
|
171
|
+
fontWeight: '700',
|
|
172
|
+
},
|
|
173
|
+
selectedInfo: {
|
|
174
|
+
marginTop: 8,
|
|
175
|
+
paddingVertical: 4,
|
|
176
|
+
},
|
|
177
|
+
selectedText: {
|
|
178
|
+
fontSize: 12,
|
|
179
|
+
color: '#888',
|
|
180
|
+
},
|
|
181
|
+
});
|