+ {/* Options List */}
+
+
Select Provider
{walletOptions.map((wallet) => (
);
}
+
diff --git a/src/components/ui/Tooltip.tsx b/src/components/ui/Tooltip.tsx
index 4302fe1..915b303 100644
--- a/src/components/ui/Tooltip.tsx
+++ b/src/components/ui/Tooltip.tsx
@@ -1,31 +1,75 @@
+/**
+ * Shared Tooltip Component.
+ * Displays a non-interactive popover containing descriptive text when
+ * the user hovers over or focuses on the child element.
+ */
+
import React, { useState } from "react";
+/**
+ * Props for the Tooltip component.
+ */
interface TooltipProps {
+ /** The text content to display inside the tooltip bubble */
content: string;
+ /** The element that triggers the tooltip (usually an icon or button) */
children: React.ReactNode;
+ /** Optional position override (defaults to top) */
+ position?: "top" | "bottom" | "left" | "right";
}
-export default function Tooltip({ children, content }: TooltipProps) {
+/**
+ * A lightweight, accessible tooltip component.
+ */
+export default function Tooltip({ children, content, position = "top" }: TooltipProps) {
+ // --- Component State ---
const [isVisible, setIsVisible] = useState(false);
+ // --- Style Logic ---
+
+ /** Position-specific Tailwind classes */
+ const positionClasses = {
+ top: "bottom-full left-1/2 -translate-x-1/2 mb-2.5",
+ bottom: "top-full left-1/2 -translate-x-1/2 mt-2.5",
+ left: "right-full top-1/2 -translate-y-1/2 mr-2.5",
+ right: "left-full top-1/2 -translate-y-1/2 ml-2.5"
+ };
+
+ /** Arrow orientation classes */
+ const arrowClasses = {
+ top: "top-full left-1/2 -translate-x-1/2 border-t-slate-800",
+ bottom: "bottom-full left-1/2 -translate-x-1/2 border-b-slate-800",
+ left: "left-full top-1/2 -translate-y-1/2 border-l-slate-800",
+ right: "right-full top-1/2 -translate-y-1/2 border-r-slate-800"
+ };
+
return (
setIsVisible(true)}
onMouseLeave={() => setIsVisible(false)}
+ onFocus={() => setIsVisible(true)}
+ onBlur={() => setIsVisible(false)}
>
+ {/* The trigger element */}
{children}
+
+ {/* The Tooltip Bubble */}
-
+
{content}
- {/* Arrow */}
-
+
+ {/* Decorative Arrow */}
+
);
}
+
diff --git a/src/hooks/useRecentTokens.ts b/src/hooks/useRecentTokens.ts
index c03b380..cfdecf3 100644
--- a/src/hooks/useRecentTokens.ts
+++ b/src/hooks/useRecentTokens.ts
@@ -1,42 +1,86 @@
+/**
+ * Recent Tokens Hook.
+ * Manages a persistent list of recently used tokens (symbols) in the swap interface.
+ * Helps improve UX by providing quick access to common assets.
+ */
+
import { useState, useEffect, useCallback } from 'react';
+/** localStorage key for recent token symbols */
const STORAGE_KEY = 'tradeflow_recent_tokens';
+/** Maximum number of unique tokens to track */
const MAX_RECENT_TOKENS = 3;
+/**
+ * Custom hook for managing the recent tokens list.
+ */
export function useRecentTokens() {
+ // --- State Initialization ---
const [recentTokens, setRecentTokens] = useState
([]);
+ /**
+ * Effect: Hydrate the state from localStorage on component mount.
+ * Only runs in the browser environment.
+ */
useEffect(() => {
if (typeof window === 'undefined') return;
try {
- const stored = localStorage.getItem(STORAGE_KEY);
- if (stored) {
- const parsed = JSON.parse(stored);
+ const storedData = localStorage.getItem(STORAGE_KEY);
+ if (storedData) {
+ const parsed = JSON.parse(storedData);
if (Array.isArray(parsed)) {
- setRecentTokens(parsed);
+ // Validate that the array contains only strings
+ const validTokens = parsed.filter(t => typeof t === 'string');
+ setRecentTokens(validTokens);
}
}
} catch (error) {
- console.warn('Error reading recent tokens from localStorage:', error);
+ console.warn('[useRecentTokens] Error loading history:', error);
}
}, []);
- const addRecentToken = useCallback((token: string) => {
+ /**
+ * Adds a token to the top of the recent list.
+ * If the token already exists, it is moved to the front.
+ *
+ * @param {string} tokenSymbol - The symbol of the token (e.g., "XLM").
+ */
+ const addRecentToken = useCallback((tokenSymbol: string) => {
+ if (!tokenSymbol) return;
+
setRecentTokens((prev) => {
- // Remove if exists to move to top
- const filtered = prev.filter((t) => t !== token);
- // Prepend new token and slice to max length
- const newRecent = [token, ...filtered].slice(0, MAX_RECENT_TOKENS);
+ // 1. Remove duplicates to ensure uniqueness
+ const filteredList = prev.filter((t) => t !== tokenSymbol);
+
+ // 2. Prepend the new token and enforce the size limit
+ const updatedHistory = [tokenSymbol, ...filteredList].slice(0, MAX_RECENT_TOKENS);
+ // 3. Persist the updated list for future sessions
try {
- localStorage.setItem(STORAGE_KEY, JSON.stringify(newRecent));
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(updatedHistory));
} catch (error) {
- console.warn('Error saving recent tokens:', error);
+ console.warn('[useRecentTokens] Failed to persist update:', error);
}
- return newRecent;
+
+ return updatedHistory;
});
}, []);
- return { recentTokens, addRecentToken };
-}
\ No newline at end of file
+ /**
+ * Resets the recent tokens history.
+ */
+ const clearHistory = useCallback(() => {
+ setRecentTokens([]);
+ localStorage.removeItem(STORAGE_KEY);
+ }, []);
+
+ return {
+ /** The current array of recently used token symbols */
+ recentTokens,
+ /** Function to track a new token usage */
+ addRecentToken,
+ /** Function to wipe the history */
+ clearHistory
+ };
+}