@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.
@@ -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: 100,
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 => VALIDATION_REGEX.test(input);
33
+ const isValidSlippageInput = (input: string): boolean =>
34
+ VALIDATION_REGEX.test(input);
33
35
 
34
- const isValidSlippageValue = (value: number, min: number, max: number): boolean =>
35
- !isNaN(value) && value >= min && value <= max;
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
- onChange(numValue);
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 className="text-xs font-medium text-muted-foreground" id="slippage-label">
128
- {label}
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
- "text-xs text-right bg-transparent border-none outline-none",
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 className="text-xs font-medium text-foreground" aria-live="polite">
152
- {value}%
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
- "p-1 hover:bg-muted rounded transition-colors",
183
+ "hover:bg-muted rounded p-1 transition-colors",
160
184
  "text-muted-foreground hover:text-foreground",
161
- "focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2"
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} />