@turtleclub/ui 0.3.0 → 0.3.1-beta.0
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/.turbo/turbo-build.log +52 -52
- package/CHANGELOG.md +6 -0
- package/dist/index.cjs +29 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1632 -1606
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/types/components/molecules/slippage-selector.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/components/molecules/slippage-selector.tsx +44 -20
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { useState, useCallback, useEffect } from "react";
|
|
3
|
-
import { Edit3 } from "lucide-react";
|
|
3
|
+
import { Edit3, InfoIcon } from "lucide-react";
|
|
4
4
|
import { cn } from "@/lib/utils";
|
|
5
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from "../ui";
|
|
5
6
|
|
|
6
7
|
const SLIPPAGE_LIMITS = {
|
|
7
8
|
MIN: 0.1,
|
|
8
|
-
MAX:
|
|
9
|
+
MAX: 50,
|
|
9
10
|
} as const;
|
|
10
11
|
|
|
11
12
|
const VALIDATION_REGEX = /^\d*\.?\d*$/;
|
|
@@ -29,10 +30,14 @@ interface SlippageSelectorProps {
|
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
// Validation utilities
|
|
32
|
-
const isValidSlippageInput = (input: string): boolean =>
|
|
33
|
+
const isValidSlippageInput = (input: string): boolean =>
|
|
34
|
+
VALIDATION_REGEX.test(input);
|
|
33
35
|
|
|
34
|
-
const isValidSlippageValue = (
|
|
35
|
-
|
|
36
|
+
const isValidSlippageValue = (
|
|
37
|
+
value: number,
|
|
38
|
+
min: number,
|
|
39
|
+
max: number,
|
|
40
|
+
): boolean => !isNaN(value) && value >= min && value <= max;
|
|
36
41
|
|
|
37
42
|
const formatSlippageValue = (value: number): string => value.toString();
|
|
38
43
|
|
|
@@ -47,7 +52,6 @@ export function SlippageSelector({
|
|
|
47
52
|
const [isEditing, setIsEditing] = useState(false);
|
|
48
53
|
const [inputValue, setInputValue] = useState(formatSlippageValue(value));
|
|
49
54
|
|
|
50
|
-
// Sync input value when prop value changes
|
|
51
55
|
useEffect(() => {
|
|
52
56
|
if (!isEditing) {
|
|
53
57
|
setInputValue(formatSlippageValue(value));
|
|
@@ -56,11 +60,11 @@ export function SlippageSelector({
|
|
|
56
60
|
|
|
57
61
|
const startEditing = useCallback(() => {
|
|
58
62
|
setIsEditing(true);
|
|
59
|
-
setInputValue(formatSlippageValue(value));
|
|
63
|
+
setInputValue(formatSlippageValue(value * 100));
|
|
60
64
|
}, [value]);
|
|
61
65
|
|
|
62
66
|
const cancelEditing = useCallback(() => {
|
|
63
|
-
setInputValue(formatSlippageValue(value));
|
|
67
|
+
setInputValue(formatSlippageValue(value * 100));
|
|
64
68
|
setIsEditing(false);
|
|
65
69
|
}, [value]);
|
|
66
70
|
|
|
@@ -68,7 +72,8 @@ export function SlippageSelector({
|
|
|
68
72
|
const numValue = parseFloat(inputValue);
|
|
69
73
|
|
|
70
74
|
if (isValidSlippageValue(numValue, minValue, maxValue)) {
|
|
71
|
-
|
|
75
|
+
// Convert to percentage
|
|
76
|
+
onChange(numValue / 100);
|
|
72
77
|
setIsEditing(false);
|
|
73
78
|
} else {
|
|
74
79
|
// Reset to current value if invalid
|
|
@@ -99,7 +104,7 @@ export function SlippageSelector({
|
|
|
99
104
|
|
|
100
105
|
setInputValue(newValue);
|
|
101
106
|
},
|
|
102
|
-
[maxValue]
|
|
107
|
+
[maxValue],
|
|
103
108
|
);
|
|
104
109
|
|
|
105
110
|
const handleKeyDown = useCallback(
|
|
@@ -115,7 +120,7 @@ export function SlippageSelector({
|
|
|
115
120
|
break;
|
|
116
121
|
}
|
|
117
122
|
},
|
|
118
|
-
[submitValue, cancelEditing]
|
|
123
|
+
[submitValue, cancelEditing],
|
|
119
124
|
);
|
|
120
125
|
|
|
121
126
|
const handleBlur = useCallback(() => {
|
|
@@ -124,8 +129,24 @@ export function SlippageSelector({
|
|
|
124
129
|
|
|
125
130
|
return (
|
|
126
131
|
<div className={cn("flex items-center justify-between", className)}>
|
|
127
|
-
<span
|
|
128
|
-
|
|
132
|
+
<span
|
|
133
|
+
className="text-muted-foreground text-xs font-medium"
|
|
134
|
+
id="slippage-label"
|
|
135
|
+
>
|
|
136
|
+
<div className="flex items-center">
|
|
137
|
+
{label}
|
|
138
|
+
<Tooltip>
|
|
139
|
+
<TooltipTrigger>
|
|
140
|
+
<InfoIcon className="ml-1 size-3" />
|
|
141
|
+
</TooltipTrigger>
|
|
142
|
+
<TooltipContent>
|
|
143
|
+
<p className="text-xs">
|
|
144
|
+
Must be between <span className="font-bold">0.1%</span> and{" "}
|
|
145
|
+
<span className="font-bold">50%</span>
|
|
146
|
+
</p>
|
|
147
|
+
</TooltipContent>
|
|
148
|
+
</Tooltip>
|
|
149
|
+
</div>
|
|
129
150
|
</span>
|
|
130
151
|
|
|
131
152
|
<div className="flex items-center gap-1">
|
|
@@ -138,8 +159,8 @@ export function SlippageSelector({
|
|
|
138
159
|
onBlur={handleBlur}
|
|
139
160
|
className={cn(
|
|
140
161
|
INPUT_WIDTH,
|
|
141
|
-
"
|
|
142
|
-
"text-foreground focus:ring-0 focus:outline-none"
|
|
162
|
+
"border-none bg-transparent text-right text-xs outline-none",
|
|
163
|
+
"text-foreground focus:ring-0 focus:outline-none",
|
|
143
164
|
)}
|
|
144
165
|
aria-labelledby="slippage-label"
|
|
145
166
|
aria-describedby="slippage-range"
|
|
@@ -148,19 +169,22 @@ export function SlippageSelector({
|
|
|
148
169
|
spellCheck={false}
|
|
149
170
|
/>
|
|
150
171
|
) : (
|
|
151
|
-
<span
|
|
152
|
-
|
|
172
|
+
<span
|
|
173
|
+
className="text-foreground text-xs font-medium"
|
|
174
|
+
aria-live="polite"
|
|
175
|
+
>
|
|
176
|
+
{value * 100}%
|
|
153
177
|
</span>
|
|
154
178
|
)}
|
|
155
179
|
|
|
156
180
|
<button
|
|
157
181
|
onClick={startEditing}
|
|
158
182
|
className={cn(
|
|
159
|
-
"
|
|
183
|
+
"hover:bg-muted rounded p-1 transition-colors",
|
|
160
184
|
"text-muted-foreground hover:text-foreground",
|
|
161
|
-
"focus:
|
|
185
|
+
"focus:ring-ring focus:ring-2 focus:ring-offset-2 focus:outline-none",
|
|
162
186
|
)}
|
|
163
|
-
aria-label={`Edit slippage value. Current value: ${value}%`}
|
|
187
|
+
aria-label={`Edit slippage value. Current value: ${value * 100}%`}
|
|
164
188
|
type="button"
|
|
165
189
|
>
|
|
166
190
|
<Edit3 size={ICON_SIZE} />
|