From 8173e6724a2c38dda8540ac59f6ce78e61ffe108 Mon Sep 17 00:00:00 2001 From: Gaubee Date: Tue, 16 Dec 2025 18:54:08 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=9A=9A=20=E5=9B=BE=E6=A0=87=E4=BB=8E?= =?UTF-8?q?=20lucide-react=20=E6=8D=A2=E6=88=90=E4=BA=86=20@tabler/icons-r?= =?UTF-8?q?eact?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 - pnpm-lock.yaml | 12 -- src/components/common/alert.tsx | 13 +- src/components/common/icon-circle.test.tsx | 2 +- src/components/common/icon-circle.tsx | 4 +- src/components/common/step-indicator.tsx | 2 +- src/components/layout/app-layout.tsx | 2 +- .../migration/MigrationCompleteStep.tsx | 6 +- .../migration/MigrationPasswordStep.tsx | 2 +- .../migration/MigrationProgressStep.tsx | 2 +- src/components/migration/WhatsNewSheet.tsx | 6 +- .../notification-permission-sheet.tsx | 7 +- .../notification/transaction-toast.tsx | 7 +- .../onboarding/arbitrary-key-input.tsx | 2 +- .../onboarding/backup-tips-sheet.tsx | 2 +- .../onboarding/collision-confirm-dialog.tsx | 2 +- .../onboarding/create-wallet-form.tsx | 2 +- .../onboarding/create-wallet-success.tsx | 6 +- .../onboarding/key-type-selector.tsx | 2 +- .../onboarding/mnemonic-confirm-backup.tsx | 2 +- .../onboarding/mnemonic-options-sheet.tsx | 2 +- .../onboarding/recover-wallet-form.tsx | 2 +- .../onboarding/security-warning-dialog.tsx | 2 +- src/components/security/mnemonic-confirm.tsx | 2 +- src/components/security/mnemonic-display.tsx | 2 +- .../security/password-confirm-sheet.tsx | 2 +- src/components/security/password-input.tsx | 2 +- src/components/transaction/fee-display.tsx | 2 +- .../transaction/transaction-item.tsx | 13 +- .../transaction/transaction-status.tsx | 6 +- src/components/transfer/address-input.tsx | 2 +- src/components/transfer/send-result.tsx | 2 +- .../transfer/transfer-confirm-sheet.tsx | 2 +- src/components/ui/marquee-text.tsx | 2 +- src/components/wallet/address-display.tsx | 2 +- .../wallet/chain-address-selector.tsx | 2 +- src/components/wallet/wallet-edit-sheet.tsx | 2 +- src/components/wallet/wallet-selector.tsx | 2 +- src/pages/address-book/index.tsx | 7 +- src/pages/guide/WelcomeScreen.tsx | 8 +- src/pages/history/detail.tsx | 8 +- src/pages/history/index.tsx | 2 +- src/pages/home/index.tsx | 156 ++++++++---------- src/pages/notifications/index.tsx | 2 +- src/pages/onboarding/create.tsx | 6 +- src/pages/onboarding/migrate.tsx | 2 +- src/pages/onboarding/recover.tsx | 2 +- src/pages/receive/index.tsx | 2 +- src/pages/scanner/index.tsx | 7 +- src/pages/send/index.tsx | 2 +- src/pages/settings/chain-config.tsx | 7 +- src/pages/settings/change-password.tsx | 2 +- src/pages/settings/currency.tsx | 2 +- src/pages/settings/index.stories.tsx | 2 +- src/pages/settings/index.test.tsx | 2 +- src/pages/settings/index.tsx | 79 ++++----- src/pages/settings/language.tsx | 2 +- src/pages/settings/settings-item.tsx | 2 +- src/pages/settings/view-mnemonic.tsx | 2 +- src/pages/staking/components/burn-form.tsx | 2 +- src/pages/staking/components/mint-form.tsx | 2 +- .../components/staking-overview-panel.tsx | 2 +- .../components/staking-record-list.tsx | 9 +- src/pages/token/detail.tsx | 2 +- src/pages/wallet/create.tsx | 9 +- src/pages/wallet/detail.tsx | 137 +++++++-------- src/pages/wallet/import.tsx | 6 +- src/pages/wallet/list.tsx | 2 +- 68 files changed, 318 insertions(+), 292 deletions(-) diff --git a/package.json b/package.json index 643659c54..a39530c28 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,6 @@ "clsx": "^2.1.1", "i18next": "^25.7.1", "jsqr": "^1.4.0", - "lucide-react": "^0.556.0", "qrcode.react": "^4.2.0", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 74eeae422..6e29a83b1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -74,9 +74,6 @@ importers: jsqr: specifier: ^1.4.0 version: 1.4.0 - lucide-react: - specifier: ^0.556.0 - version: 0.556.0(react@19.2.1) qrcode.react: specifier: ^4.2.0 version: 4.2.0(react@19.2.1) @@ -3167,11 +3164,6 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - lucide-react@0.556.0: - resolution: {integrity: sha512-iOb8dRk7kLaYBZhR2VlV1CeJGxChBgUthpSP8wom9jfj79qovgG6qcSdiy6vkoREKPnbUYzJsCn4o4PtG3Iy+A==} - peerDependencies: - react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 - lz-string@1.5.0: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true @@ -7344,10 +7336,6 @@ snapshots: dependencies: yallist: 3.1.1 - lucide-react@0.556.0(react@19.2.1): - dependencies: - react: 19.2.1 - lz-string@1.5.0: {} magic-string@0.30.21: diff --git a/src/components/common/alert.tsx b/src/components/common/alert.tsx index 5e0cea675..d7f7c5777 100644 --- a/src/components/common/alert.tsx +++ b/src/components/common/alert.tsx @@ -1,5 +1,10 @@ import { cn } from '@/lib/utils'; -import { AlertCircle, CheckCircle, Info, AlertTriangle } from 'lucide-react'; +import { + IconAlertCircle as AlertCircle, + IconCircleCheck as CheckCircle, + IconInfoCircle as Info, + IconAlertTriangle as AlertTriangle, +} from '@tabler/icons-react'; import type { ReactNode } from 'react'; type AlertVariant = 'info' | 'success' | 'warning' | 'error'; @@ -14,15 +19,15 @@ interface AlertProps { const variantConfig = { info: { icon: Info, - styles: 'bg-blue-500/10 text-blue-700 dark:text-blue-300', + styles: 'bg-info/10 text-info', }, success: { icon: CheckCircle, - styles: 'bg-green-500/10 text-green-700 dark:text-green-300', + styles: 'bg-success/10 text-success', }, warning: { icon: AlertTriangle, - styles: 'bg-yellow-500/10 text-yellow-700 dark:text-yellow-300', + styles: 'bg-warning/10 text-warning', }, error: { icon: AlertCircle, diff --git a/src/components/common/icon-circle.test.tsx b/src/components/common/icon-circle.test.tsx index 590536f59..86dad15eb 100644 --- a/src/components/common/icon-circle.test.tsx +++ b/src/components/common/icon-circle.test.tsx @@ -1,7 +1,7 @@ import { describe, it, expect } from 'vitest'; import { render } from '@testing-library/react'; import { IconCircle } from './icon-circle'; -import { ShieldCheck, Wallet, Settings } from 'lucide-react'; +import { IconShieldCheck as ShieldCheck, IconWallet as Wallet, IconSettings as Settings } from '@tabler/icons-react'; describe('IconCircle', () => { it('renders with icon', () => { diff --git a/src/components/common/icon-circle.tsx b/src/components/common/icon-circle.tsx index 80b8e7115..41f0672dd 100644 --- a/src/components/common/icon-circle.tsx +++ b/src/components/common/icon-circle.tsx @@ -1,11 +1,11 @@ import { cn } from '@/lib/utils'; -import type { LucideIcon } from 'lucide-react'; +import type { Icon } from '@tabler/icons-react'; type IconCircleVariant = 'primary' | 'secondary' | 'success' | 'warning' | 'error'; type IconCircleSize = 'sm' | 'md' | 'lg'; interface IconCircleProps { - icon: LucideIcon; + icon: Icon; variant?: IconCircleVariant; size?: IconCircleSize; className?: string; diff --git a/src/components/common/step-indicator.tsx b/src/components/common/step-indicator.tsx index bbb4cd802..d8e4bff91 100644 --- a/src/components/common/step-indicator.tsx +++ b/src/components/common/step-indicator.tsx @@ -1,5 +1,5 @@ import { cn } from '@/lib/utils'; -import { Check } from 'lucide-react'; +import { IconCheck as Check } from '@tabler/icons-react'; interface Step { id: string; diff --git a/src/components/layout/app-layout.tsx b/src/components/layout/app-layout.tsx index a0ac1fa0e..f702b25ba 100644 --- a/src/components/layout/app-layout.tsx +++ b/src/components/layout/app-layout.tsx @@ -3,7 +3,7 @@ import { useRouter, useRouterState } from '@tanstack/react-router'; import { useTranslation } from 'react-i18next'; import { cn } from '@/lib/utils'; import { TabBar, type TabItem } from './tab-bar'; -import { Home, Wallet, Settings, ArrowLeftRight } from 'lucide-react'; +import { IconHome as Home, IconWallet as Wallet, IconSettings as Settings, IconArrowLeftRight as ArrowLeftRight } from '@tabler/icons-react'; import { chainConfigActions, preferencesActions, walletActions, useWalletInitialized } from '@/stores'; import { installAuthorizeDeepLinkListener } from '@/services/authorize/deep-link'; diff --git a/src/components/migration/MigrationCompleteStep.tsx b/src/components/migration/MigrationCompleteStep.tsx index 56a9943a9..ca50beceb 100644 --- a/src/components/migration/MigrationCompleteStep.tsx +++ b/src/components/migration/MigrationCompleteStep.tsx @@ -4,7 +4,11 @@ import { useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { CheckCircle2, XCircle, AlertTriangle } from 'lucide-react'; +import { + IconCircleCheck as CheckCircle2, + IconCircleX as XCircle, + IconAlertTriangle as AlertTriangle, +} from '@tabler/icons-react'; import { Button } from '@/components/ui/button'; import { WhatsNewSheet } from './WhatsNewSheet'; diff --git a/src/components/migration/MigrationPasswordStep.tsx b/src/components/migration/MigrationPasswordStep.tsx index 75fe5a9ce..e01ac02e3 100644 --- a/src/components/migration/MigrationPasswordStep.tsx +++ b/src/components/migration/MigrationPasswordStep.tsx @@ -4,7 +4,7 @@ import { useState, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -import { Eye, EyeOff, AlertCircle } from 'lucide-react'; +import { IconEye as Eye, IconEyeOff as EyeOff, IconAlertCircle as AlertCircle } from '@tabler/icons-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { cn } from '@/lib/utils'; diff --git a/src/components/migration/MigrationProgressStep.tsx b/src/components/migration/MigrationProgressStep.tsx index 34eee2151..74cd9c3d9 100644 --- a/src/components/migration/MigrationProgressStep.tsx +++ b/src/components/migration/MigrationProgressStep.tsx @@ -3,7 +3,7 @@ */ import { useTranslation } from 'react-i18next'; -import { Loader2, CheckCircle2 } from 'lucide-react'; +import { IconLoader2 as Loader2, IconCircleCheck as CheckCircle2 } from '@tabler/icons-react'; import { Progress } from '@/components/ui/progress'; import type { MigrationProgress } from '@/services/migration/types'; diff --git a/src/components/migration/WhatsNewSheet.tsx b/src/components/migration/WhatsNewSheet.tsx index 5b574ecc2..6a37cf8a7 100644 --- a/src/components/migration/WhatsNewSheet.tsx +++ b/src/components/migration/WhatsNewSheet.tsx @@ -1,4 +1,8 @@ -import { Sparkles, ShieldCheck, Layers3 } from 'lucide-react'; +import { + IconSparkles as Sparkles, + IconShieldCheck as ShieldCheck, + IconStackMiddle as Layers3, +} from '@tabler/icons-react'; import { useTranslation } from 'react-i18next'; import { Sheet, SheetContent, SheetHeader, SheetTitle } from '@/components/ui/sheet'; diff --git a/src/components/notification/notification-permission-sheet.tsx b/src/components/notification/notification-permission-sheet.tsx index e272dc6c6..2ee47d6a8 100644 --- a/src/components/notification/notification-permission-sheet.tsx +++ b/src/components/notification/notification-permission-sheet.tsx @@ -1,4 +1,9 @@ -import { Bell, BellRing, Zap, Shield } from 'lucide-react'; +import { + IconBell as Bell, + IconBellRinging as BellRing, + IconBolt as Zap, + IconShield as Shield, +} from '@tabler/icons-react'; import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetFooter } from '@/components/ui/sheet'; import { Button } from '@/components/ui/button'; import { cn } from '@/lib/utils'; diff --git a/src/components/notification/transaction-toast.tsx b/src/components/notification/transaction-toast.tsx index 114d4cbd7..e82f2dc12 100644 --- a/src/components/notification/transaction-toast.tsx +++ b/src/components/notification/transaction-toast.tsx @@ -1,6 +1,11 @@ import { useState, useEffect, useCallback } from 'react'; import { createPortal } from 'react-dom'; -import { X, CheckCircle2, XCircle, Loader2 } from 'lucide-react'; +import { + IconX as X, + IconCircleCheck as CheckCircle2, + IconCircleX as XCircle, + IconLoader2 as Loader2, +} from '@tabler/icons-react'; import { cn } from '@/lib/utils'; import type { NotificationStatus } from '@/stores/notification'; diff --git a/src/components/onboarding/arbitrary-key-input.tsx b/src/components/onboarding/arbitrary-key-input.tsx index 5b86bfe43..ce810e84b 100644 --- a/src/components/onboarding/arbitrary-key-input.tsx +++ b/src/components/onboarding/arbitrary-key-input.tsx @@ -1,4 +1,4 @@ -import { Eye, EyeOff, RotateCcw } from 'lucide-react'; +import { IconEye as Eye, IconEyeOff as EyeOff, IconRotateClockwise as RotateCcw } from '@tabler/icons-react'; import { useId, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; diff --git a/src/components/onboarding/backup-tips-sheet.tsx b/src/components/onboarding/backup-tips-sheet.tsx index 30250c91a..6589092bc 100644 --- a/src/components/onboarding/backup-tips-sheet.tsx +++ b/src/components/onboarding/backup-tips-sheet.tsx @@ -1,4 +1,4 @@ -import { Shield, AlertTriangle, Eye, Ban } from 'lucide-react'; +import { IconShield as Shield, IconAlertTriangle as AlertTriangle, IconEye as Eye, IconBan as Ban } from '@tabler/icons-react'; import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetFooter } from '@/components/ui/sheet'; import { Button } from '@/components/ui/button'; import { cn } from '@/lib/utils'; diff --git a/src/components/onboarding/collision-confirm-dialog.tsx b/src/components/onboarding/collision-confirm-dialog.tsx index 7a04baec4..b3e0685ba 100644 --- a/src/components/onboarding/collision-confirm-dialog.tsx +++ b/src/components/onboarding/collision-confirm-dialog.tsx @@ -1,5 +1,5 @@ import { cn } from '@/lib/utils'; -import { AlertTriangle, ArrowRight, Wallet } from 'lucide-react'; +import { IconAlertTriangle as AlertTriangle, IconArrowRight as ArrowRight, IconWallet as Wallet } from '@tabler/icons-react'; import type { DuplicateCheckResult } from '@/services/wallet/types'; interface CollisionConfirmDialogProps { diff --git a/src/components/onboarding/create-wallet-form.tsx b/src/components/onboarding/create-wallet-form.tsx index 2ff97c33c..2050168a5 100644 --- a/src/components/onboarding/create-wallet-form.tsx +++ b/src/components/onboarding/create-wallet-form.tsx @@ -3,7 +3,7 @@ import { cn } from '@/lib/utils'; import { Input } from '@/components/ui/input'; import { PasswordInput } from '@/components/security/password-input'; import { FormField } from '@/components/common/form-field'; -import { ChevronRight, Check } from 'lucide-react'; +import { IconChevronRight as ChevronRight, IconCheck as Check } from '@tabler/icons-react'; /** Mnemonic language options */ export type MnemonicLanguage = 'english' | 'zh-Hans' | 'zh-Hant'; diff --git a/src/components/onboarding/create-wallet-success.tsx b/src/components/onboarding/create-wallet-success.tsx index 553cfb8ec..f1e9cdf4c 100644 --- a/src/components/onboarding/create-wallet-success.tsx +++ b/src/components/onboarding/create-wallet-success.tsx @@ -1,5 +1,9 @@ import { cn } from '@/lib/utils'; -import { CheckCircle, ArrowRight, Shield } from 'lucide-react'; +import { + IconCircleCheck as CheckCircle, + IconArrowRight as ArrowRight, + IconShield as Shield, +} from '@tabler/icons-react'; interface CreateWalletSuccessProps { /** Wallet name */ diff --git a/src/components/onboarding/key-type-selector.tsx b/src/components/onboarding/key-type-selector.tsx index 5042d2563..115284008 100644 --- a/src/components/onboarding/key-type-selector.tsx +++ b/src/components/onboarding/key-type-selector.tsx @@ -1,4 +1,4 @@ -import { Check } from 'lucide-react'; +import { IconCheck as Check } from '@tabler/icons-react'; import { useTranslation } from 'react-i18next'; import { cn } from '@/lib/utils'; diff --git a/src/components/onboarding/mnemonic-confirm-backup.tsx b/src/components/onboarding/mnemonic-confirm-backup.tsx index e45629330..f75b50513 100644 --- a/src/components/onboarding/mnemonic-confirm-backup.tsx +++ b/src/components/onboarding/mnemonic-confirm-backup.tsx @@ -1,5 +1,5 @@ import { useMemo } from 'react'; -import { Check, X, HelpCircle } from 'lucide-react'; +import { IconCheck as Check, IconX as X, IconHelpCircle as HelpCircle } from '@tabler/icons-react'; import { cn } from '@/lib/utils'; import { Button } from '@/components/ui/button'; import type { VerificationSlot } from '@/hooks/use-mnemonic-verification'; diff --git a/src/components/onboarding/mnemonic-options-sheet.tsx b/src/components/onboarding/mnemonic-options-sheet.tsx index b16c08d37..b35805fda 100644 --- a/src/components/onboarding/mnemonic-options-sheet.tsx +++ b/src/components/onboarding/mnemonic-options-sheet.tsx @@ -1,6 +1,6 @@ import { cn } from '@/lib/utils'; import { BottomSheet } from '@/components/layout/bottom-sheet'; -import { Check } from 'lucide-react'; +import { IconCheck as Check } from '@tabler/icons-react'; import type { MnemonicLanguage, MnemonicLength, MnemonicOptions } from './create-wallet-form'; interface MnemonicOptionsSheetProps { diff --git a/src/components/onboarding/recover-wallet-form.tsx b/src/components/onboarding/recover-wallet-form.tsx index f64475fe9..a27f20e25 100644 --- a/src/components/onboarding/recover-wallet-form.tsx +++ b/src/components/onboarding/recover-wallet-form.tsx @@ -1,7 +1,7 @@ import { useState, useCallback, useMemo, useEffect } from 'react'; import { cn } from '@/lib/utils'; import { validateMnemonic, isValidWord } from '@/lib/crypto/mnemonic'; -import { Check, AlertCircle, Loader2 } from 'lucide-react'; +import { IconCheck as Check, IconAlertCircle as AlertCircle, IconLoader2 as Loader2 } from '@tabler/icons-react'; /** Valid mnemonic word counts */ export type MnemonicWordCount = 12 | 15 | 18 | 21 | 24 | 36; diff --git a/src/components/onboarding/security-warning-dialog.tsx b/src/components/onboarding/security-warning-dialog.tsx index cae1eb586..826b36f30 100644 --- a/src/components/onboarding/security-warning-dialog.tsx +++ b/src/components/onboarding/security-warning-dialog.tsx @@ -1,5 +1,5 @@ import * as Dialog from '@radix-ui/react-dialog'; -import { AlertTriangle } from 'lucide-react'; +import { IconAlertTriangle as AlertTriangle } from '@tabler/icons-react'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; diff --git a/src/components/security/mnemonic-confirm.tsx b/src/components/security/mnemonic-confirm.tsx index 445c8dfb6..d04f13885 100644 --- a/src/components/security/mnemonic-confirm.tsx +++ b/src/components/security/mnemonic-confirm.tsx @@ -1,6 +1,6 @@ import { useState, useCallback, useMemo } from 'react'; import { cn } from '@/lib/utils'; -import { RotateCcw, Check, X } from 'lucide-react'; +import { IconRotateClockwise as RotateCcw, IconCheck as Check, IconX as X } from '@tabler/icons-react'; interface MnemonicConfirmProps { /** Original mnemonic words in correct order */ diff --git a/src/components/security/mnemonic-display.tsx b/src/components/security/mnemonic-display.tsx index d7c44d6ea..ed8f70646 100644 --- a/src/components/security/mnemonic-display.tsx +++ b/src/components/security/mnemonic-display.tsx @@ -1,6 +1,6 @@ import { useState } from 'react'; import { cn } from '@/lib/utils'; -import { Copy, Check } from 'lucide-react'; +import { IconCopy as Copy, IconCheck as Check } from '@tabler/icons-react'; interface MnemonicDisplayProps { words: string[]; diff --git a/src/components/security/password-confirm-sheet.tsx b/src/components/security/password-confirm-sheet.tsx index 81998a715..1e91a13db 100644 --- a/src/components/security/password-confirm-sheet.tsx +++ b/src/components/security/password-confirm-sheet.tsx @@ -2,7 +2,7 @@ import { useState, useCallback } from 'react'; import { cn } from '@/lib/utils'; import { BottomSheet } from '@/components/layout/bottom-sheet'; import { PasswordInput } from './password-input'; -import { Fingerprint, AlertCircle } from 'lucide-react'; +import { IconFingerprint as Fingerprint, IconAlertCircle as AlertCircle } from '@tabler/icons-react'; export interface PasswordConfirmSheetProps { /** Whether the sheet is open */ diff --git a/src/components/security/password-input.tsx b/src/components/security/password-input.tsx index cc23ec84f..919710ac5 100644 --- a/src/components/security/password-input.tsx +++ b/src/components/security/password-input.tsx @@ -1,7 +1,7 @@ import { useState, forwardRef } from 'react'; import { cn } from '@/lib/utils'; import { useTranslation } from 'react-i18next'; -import { Eye, EyeOff } from 'lucide-react'; +import { IconEye as Eye, IconEyeOff as EyeOff } from '@tabler/icons-react'; interface PasswordInputProps extends Omit, 'type'> { showStrength?: boolean; diff --git a/src/components/transaction/fee-display.tsx b/src/components/transaction/fee-display.tsx index e9dd35b05..a4294af65 100644 --- a/src/components/transaction/fee-display.tsx +++ b/src/components/transaction/fee-display.tsx @@ -1,6 +1,6 @@ import { cn } from '@/lib/utils'; import { Skeleton } from '@/components/common/skeleton'; -import { AlertTriangle } from 'lucide-react'; +import { IconAlertTriangle as AlertTriangle } from '@tabler/icons-react'; interface FeeDisplayProps { /** Fee amount in native token */ diff --git a/src/components/transaction/transaction-item.tsx b/src/components/transaction/transaction-item.tsx index 777a9b169..7af293a80 100644 --- a/src/components/transaction/transaction-item.tsx +++ b/src/components/transaction/transaction-item.tsx @@ -1,8 +1,15 @@ import { cn } from '@/lib/utils'; import { AddressDisplay } from '../wallet/address-display'; import { AmountDisplay, TimeDisplay } from '../common'; -import { ArrowUp, ArrowDown, ArrowLeftRight, Lock, Unlock, Check } from 'lucide-react'; -import type { LucideIcon } from 'lucide-react'; +import { + IconArrowUp as ArrowUp, + IconArrowDown as ArrowDown, + IconArrowLeftRight as ArrowLeftRight, + IconLock as Lock, + IconLockOpen2 as Unlock, + IconCheck as Check, +} from '@tabler/icons-react'; +import type { Icon } from '@tabler/icons-react'; export type TransactionType = 'send' | 'receive' | 'swap' | 'stake' | 'unstake' | 'approve'; export type TransactionStatus = 'pending' | 'confirmed' | 'failed'; @@ -24,7 +31,7 @@ interface TransactionItemProps { className?: string | undefined; } -const typeConfig: Record = { +const typeConfig: Record = { send: { label: '发送', Icon: ArrowUp, color: 'text-destructive' }, receive: { label: '接收', Icon: ArrowDown, color: 'text-secondary' }, swap: { label: '兑换', Icon: ArrowLeftRight, color: 'text-primary' }, diff --git a/src/components/transaction/transaction-status.tsx b/src/components/transaction/transaction-status.tsx index 41902830a..d6186aaf7 100644 --- a/src/components/transaction/transaction-status.tsx +++ b/src/components/transaction/transaction-status.tsx @@ -1,6 +1,6 @@ import { cn } from '@/lib/utils'; -import { Check, X, Clock, Ban } from 'lucide-react'; -import type { LucideIcon } from 'lucide-react'; +import { IconCheck as Check, IconX as X, IconClock as Clock, IconBan as Ban } from '@tabler/icons-react'; +import type { Icon } from '@tabler/icons-react'; export type TransactionStatusType = 'success' | 'failed' | 'pending' | 'cancelled'; @@ -18,7 +18,7 @@ interface TransactionStatusProps { } interface StatusConfig { - icon: LucideIcon; + icon: Icon; label: string; colorClass: string; bgClass: string; diff --git a/src/components/transfer/address-input.tsx b/src/components/transfer/address-input.tsx index aed781b04..66ca7ba5d 100644 --- a/src/components/transfer/address-input.tsx +++ b/src/components/transfer/address-input.tsx @@ -1,7 +1,7 @@ import { useState, forwardRef, useId } from 'react'; import { cn } from '@/lib/utils'; import { useTranslation } from 'react-i18next'; -import { ScanLine, ClipboardPaste } from 'lucide-react'; +import { IconLineScan as ScanLine, IconClipboardCopy as ClipboardPaste } from '@tabler/icons-react'; interface AddressInputProps extends Omit, 'onChange'> { value?: string | undefined; diff --git a/src/components/transfer/send-result.tsx b/src/components/transfer/send-result.tsx index f30df0d33..ad2e58590 100644 --- a/src/components/transfer/send-result.tsx +++ b/src/components/transfer/send-result.tsx @@ -1,7 +1,7 @@ import { cn } from '@/lib/utils'; import { IconCircle } from '@/components/common/icon-circle'; import { TransactionStatus } from '@/components/transaction/transaction-status'; -import { Check, X, ExternalLink, Copy, ArrowLeft } from 'lucide-react'; +import { IconCheck as Check, IconX as X, IconExternalLink as ExternalLink, IconCopy as Copy, IconArrowLeft as ArrowLeft } from '@tabler/icons-react'; import { useState, useCallback } from 'react'; type SendResultStatus = 'success' | 'failed' | 'pending'; diff --git a/src/components/transfer/transfer-confirm-sheet.tsx b/src/components/transfer/transfer-confirm-sheet.tsx index 6f5bb1032..6f50fd76e 100644 --- a/src/components/transfer/transfer-confirm-sheet.tsx +++ b/src/components/transfer/transfer-confirm-sheet.tsx @@ -2,7 +2,7 @@ import { useState, useCallback } from 'react'; import { cn } from '@/lib/utils'; import { BottomSheet } from '@/components/layout/bottom-sheet'; import { FeeDisplay } from '@/components/transaction/fee-display'; -import { ChevronDown, ChevronUp, Copy, Check } from 'lucide-react'; +import { IconChevronDown as ChevronDown, IconChevronUp as ChevronUp, IconCopy as Copy, IconCheck as Check } from '@tabler/icons-react'; export interface TransferConfirmSheetProps { /** Whether the sheet is open */ diff --git a/src/components/ui/marquee-text.tsx b/src/components/ui/marquee-text.tsx index b71197565..cef297118 100644 --- a/src/components/ui/marquee-text.tsx +++ b/src/components/ui/marquee-text.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { Check, Copy } from 'lucide-react'; +import { IconCheck as Check, IconCopy as Copy } from '@tabler/icons-react'; import { cn } from '@/lib/utils'; export interface MarqueeTextProps extends React.HTMLAttributes { diff --git a/src/components/wallet/address-display.tsx b/src/components/wallet/address-display.tsx index 3688cb0b3..21a1af5b7 100644 --- a/src/components/wallet/address-display.tsx +++ b/src/components/wallet/address-display.tsx @@ -1,6 +1,6 @@ import { useState, useRef, useLayoutEffect, useCallback } from 'react'; import { cn } from '@/lib/utils'; -import { Copy, Check } from 'lucide-react'; +import { IconCopy as Copy, IconCheck as Check } from '@tabler/icons-react'; interface AddressDisplayProps { address: string; diff --git a/src/components/wallet/chain-address-selector.tsx b/src/components/wallet/chain-address-selector.tsx index f2b20a541..426547bca 100644 --- a/src/components/wallet/chain-address-selector.tsx +++ b/src/components/wallet/chain-address-selector.tsx @@ -1,6 +1,6 @@ import { useState, useCallback, useMemo } from 'react'; import { cn } from '@/lib/utils'; -import { Check } from 'lucide-react'; +import { IconCheck as Check } from '@tabler/icons-react'; import { ChainIcon, type ChainType } from './chain-icon'; export interface ChainAddress { diff --git a/src/components/wallet/wallet-edit-sheet.tsx b/src/components/wallet/wallet-edit-sheet.tsx index 8ab37a663..3b73cefb0 100644 --- a/src/components/wallet/wallet-edit-sheet.tsx +++ b/src/components/wallet/wallet-edit-sheet.tsx @@ -4,7 +4,7 @@ import { BottomSheet } from '@/components/layout/bottom-sheet'; import { PasswordConfirmSheet } from '@/components/security/password-confirm-sheet'; import { walletActions, type Wallet } from '@/stores'; import { verifyPassword } from '@/lib/crypto'; -import { Pencil, Trash2 } from 'lucide-react'; +import { IconPencil as Pencil, IconTrash as Trash2 } from '@tabler/icons-react'; interface WalletEditSheetProps { /** The wallet to edit */ diff --git a/src/components/wallet/wallet-selector.tsx b/src/components/wallet/wallet-selector.tsx index 0c971e66a..48d44f621 100644 --- a/src/components/wallet/wallet-selector.tsx +++ b/src/components/wallet/wallet-selector.tsx @@ -1,5 +1,5 @@ import { cn } from '@/lib/utils'; -import { Check } from 'lucide-react'; +import { IconCheck as Check } from '@tabler/icons-react'; import type { WalletInfo } from './wallet-card'; interface WalletSelectorProps { diff --git a/src/pages/address-book/index.tsx b/src/pages/address-book/index.tsx index 03c3557bb..2f610b784 100644 --- a/src/pages/address-book/index.tsx +++ b/src/pages/address-book/index.tsx @@ -1,7 +1,12 @@ import { useState, useCallback, useMemo } from 'react'; import { useNavigate } from '@tanstack/react-router'; import { useStore } from '@tanstack/react-store'; -import { Plus, Search, User, MoreVertical } from 'lucide-react'; +import { + IconPlus as Plus, + IconSearch as Search, + IconUser as User, + IconArrowsVertical as MoreVertical, +} from '@tabler/icons-react'; import { PageHeader } from '@/components/layout/page-header'; import { ContactEditSheet } from '@/components/address-book/contact-edit-sheet'; import { PasswordConfirmSheet } from '@/components/security/password-confirm-sheet'; diff --git a/src/pages/guide/WelcomeScreen.tsx b/src/pages/guide/WelcomeScreen.tsx index 60670ef46..06e4fbd73 100644 --- a/src/pages/guide/WelcomeScreen.tsx +++ b/src/pages/guide/WelcomeScreen.tsx @@ -1,7 +1,13 @@ import { useState, useCallback } from 'react'; import { useNavigate } from '@tanstack/react-router'; import { useTranslation } from 'react-i18next'; -import { ArrowRight, Send, Link2, Shield, Download } from 'lucide-react'; +import { + IconArrowRight as ArrowRight, + IconSend as Send, + IconLink as Link2, + IconShield as Shield, + IconDownload as Download, +} from '@tabler/icons-react'; import { Button } from '@/components/ui/button'; import { cn } from '@/lib/utils'; import { useMigrationOptional } from '@/contexts/MigrationContext'; diff --git a/src/pages/history/detail.tsx b/src/pages/history/detail.tsx index b398f945a..c837a3008 100644 --- a/src/pages/history/detail.tsx +++ b/src/pages/history/detail.tsx @@ -1,6 +1,12 @@ import { useCallback, useMemo } from 'react'; import { useNavigate, useParams } from '@tanstack/react-router'; -import { Copy, ExternalLink, Check, Clock, XCircle } from 'lucide-react'; +import { + IconCopy as Copy, + IconExternalLink as ExternalLink, + IconCheck as Check, + IconClock as Clock, + IconCircleX as XCircle, +} from '@tabler/icons-react'; import { PageHeader } from '@/components/layout/page-header'; import { AddressDisplay } from '@/components/wallet/address-display'; import { AmountDisplay, TimeDisplay } from '@/components/common'; diff --git a/src/pages/history/index.tsx b/src/pages/history/index.tsx index 8b43172a7..ba9e867f9 100644 --- a/src/pages/history/index.tsx +++ b/src/pages/history/index.tsx @@ -1,7 +1,7 @@ import { useCallback } from 'react'; import { useNavigate } from '@tanstack/react-router'; import { useTranslation } from 'react-i18next'; -import { RefreshCw, Filter } from 'lucide-react'; +import { IconRefresh as RefreshCw, IconFilter as Filter } from '@tabler/icons-react'; import { PageHeader } from '@/components/layout/page-header'; import { TransactionList } from '@/components/transaction/transaction-list'; import { useTransactionHistory, type TransactionFilter } from '@/hooks/use-transaction-history'; diff --git a/src/pages/home/index.tsx b/src/pages/home/index.tsx index 8bf893897..b1a2765c3 100644 --- a/src/pages/home/index.tsx +++ b/src/pages/home/index.tsx @@ -1,27 +1,27 @@ -import { useEffect, useState } from 'react' -import { Link } from '@tanstack/react-router' -import { useTranslation } from 'react-i18next' -import { TokenList } from '@/components/token/token-list' -import { GradientButton } from '@/components/common/gradient-button' -import { Button } from '@/components/ui/button' -import { LoadingSpinner } from '@/components/common/loading-spinner' -import { ChainIcon } from '@/components/wallet/chain-icon' -import { BottomSheet } from '@/components/layout/bottom-sheet' -import { useClipboard, useToast, useHaptics } from '@/services' +import { useEffect, useState } from 'react'; +import { Link } from '@tanstack/react-router'; +import { useTranslation } from 'react-i18next'; +import { TokenList } from '@/components/token/token-list'; +import { GradientButton } from '@/components/common/gradient-button'; +import { Button } from '@/components/ui/button'; +import { LoadingSpinner } from '@/components/common/loading-spinner'; +import { ChainIcon } from '@/components/wallet/chain-icon'; +import { BottomSheet } from '@/components/layout/bottom-sheet'; +import { useClipboard, useToast, useHaptics } from '@/services'; import { - Plus, - Send, - QrCode, - Copy, - ChevronDown, - Check, - ScanLine, -} from 'lucide-react' + IconPlus as Plus, + IconSend as Send, + IconQrcode as QrCode, + IconCopy as Copy, + IconChevronDown as ChevronDown, + IconCheck as Check, + IconLineScan as ScanLine, +} from '@tabler/icons-react'; /** 截断地址显示 */ function truncateAddress(address: string, startChars = 6, endChars = 4): string { - if (address.length <= startChars + endChars + 3) return address - return `${address.slice(0, startChars)}...${address.slice(-endChars)}` + if (address.length <= startChars + endChars + 3) return address; + return `${address.slice(0, startChars)}...${address.slice(-endChars)}`; } import { useCurrentWallet, @@ -33,7 +33,7 @@ import { useWalletInitialized, walletActions, type ChainType, -} from '@/stores' +} from '@/stores'; const CHAIN_NAMES: Record = { // 外部链 @@ -50,63 +50,63 @@ const CHAIN_NAMES: Record = { biwmeta: 'BIWMeta', ethmeta: 'ETHMeta', malibu: 'Malibu', -} +}; export function HomePage() { - const clipboard = useClipboard() - const toast = useToast() - const haptics = useHaptics() - const { t } = useTranslation() - - const isInitialized = useWalletInitialized() - const hasWallet = useHasWallet() - const currentWallet = useCurrentWallet() - const selectedChain = useSelectedChain() - const selectedChainName = CHAIN_NAMES[selectedChain] ?? selectedChain - const chainAddress = useCurrentChainAddress() - const tokens = useCurrentChainTokens() - const availableChains = useAvailableChains() - - const [chainSheetOpen, setChainSheetOpen] = useState(false) - const [copied, setCopied] = useState(false) + const clipboard = useClipboard(); + const toast = useToast(); + const haptics = useHaptics(); + const { t } = useTranslation(); + + const isInitialized = useWalletInitialized(); + const hasWallet = useHasWallet(); + const currentWallet = useCurrentWallet(); + const selectedChain = useSelectedChain(); + const selectedChainName = CHAIN_NAMES[selectedChain] ?? selectedChain; + const chainAddress = useCurrentChainAddress(); + const tokens = useCurrentChainTokens(); + const availableChains = useAvailableChains(); + + const [chainSheetOpen, setChainSheetOpen] = useState(false); + const [copied, setCopied] = useState(false); useEffect(() => { if (!isInitialized) { - walletActions.initialize() + walletActions.initialize(); } - }, [isInitialized]) + }, [isInitialized]); const handleCopyAddress = async () => { if (chainAddress?.address) { - await clipboard.write(chainAddress.address) - await haptics.impact('light') - setCopied(true) - toast.show('地址已复制') - setTimeout(() => setCopied(false), 2000) + await clipboard.write(chainAddress.address); + await haptics.impact('light'); + setCopied(true); + toast.show('地址已复制'); + setTimeout(() => setCopied(false), 2000); } - } + }; const handleSelectChain = (chain: ChainType) => { - walletActions.setSelectedChain(chain) - setChainSheetOpen(false) - } + walletActions.setSelectedChain(chain); + setChainSheetOpen(false); + }; if (!isInitialized) { return (
- ) + ); } if (!hasWallet || !currentWallet) { - return + return ; } return ( -
+
{/* 钱包卡片 - mpay 风格 */} -
+
{/* 链选择器 */}
@@ -160,7 +154,7 @@ export function HomePage() {
{/* 资产列表 */} -
+

资产

({ @@ -174,7 +168,7 @@ export function HomePage() { }))} onTokenClick={(token) => { // TODO: Implement token detail page route once available - console.log('Token clicked:', token.symbol) + console.log('Token clicked:', token.symbol); }} emptyTitle="暂无资产" emptyDescription={`${selectedChainName} 链上暂无代币`} @@ -184,61 +178,51 @@ export function HomePage() { {/* Scanner FAB */} - + {/* 链选择底部弹窗 */} - setChainSheetOpen(false)} - title="选择网络" - > + setChainSheetOpen(false)} title="选择网络">
{availableChains.map((chain) => { - const chainAddr = currentWallet.chainAddresses.find( - (ca) => ca.chain === chain - ) + const chainAddr = currentWallet.chainAddresses.find((ca) => ca.chain === chain); return ( - ) + ); })}
- ) + ); } function NoWalletView() { return (
-
- +
+

欢迎使用 BFM Pay

-

创建或导入钱包开始使用

+

创建或导入钱包开始使用

@@ -253,5 +237,5 @@ function NoWalletView() {
- ) + ); } diff --git a/src/pages/notifications/index.tsx b/src/pages/notifications/index.tsx index cd9865743..b83a85fb9 100644 --- a/src/pages/notifications/index.tsx +++ b/src/pages/notifications/index.tsx @@ -1,6 +1,6 @@ import { useCallback, useEffect, useMemo } from 'react'; import { useNavigate } from '@tanstack/react-router'; -import { Bell, Check, Trash2 } from 'lucide-react'; +import { IconBell as Bell, IconCheck as Check, IconTrash as Trash2 } from '@tabler/icons-react'; import { useStore } from '@tanstack/react-store'; import { PageHeader } from '@/components/layout/page-header'; import { Button } from '@/components/ui/button'; diff --git a/src/pages/onboarding/create.tsx b/src/pages/onboarding/create.tsx index 4cee7930e..663c58373 100644 --- a/src/pages/onboarding/create.tsx +++ b/src/pages/onboarding/create.tsx @@ -15,7 +15,11 @@ import { useMnemonicVerification } from '@/hooks/use-mnemonic-verification'; import { generateMnemonic, encrypt, deriveMultiChainKeys, deriveBioforestAddresses } from '@/lib/crypto'; import { useChainConfigState, useEnabledBioforestChainConfigs, walletActions } from '@/stores'; import { Button } from '@/components/ui/button'; -import { ArrowLeft, ArrowRight, CheckCircle } from 'lucide-react'; +import { + IconArrowLeft as ArrowLeft, + IconArrowRight as ArrowRight, + IconCircleCheck as CheckCircle, +} from '@tabler/icons-react'; type Step = 'form' | 'success' | 'backup-tips' | 'backup-display' | 'backup-confirm' | 'backup-complete'; diff --git a/src/pages/onboarding/migrate.tsx b/src/pages/onboarding/migrate.tsx index d6efd3cb1..75f9b3dbd 100644 --- a/src/pages/onboarding/migrate.tsx +++ b/src/pages/onboarding/migrate.tsx @@ -7,7 +7,7 @@ import { useState, useCallback } from 'react'; import { useNavigate } from '@tanstack/react-router'; import { useTranslation } from 'react-i18next'; -import { ArrowLeft, Download, AlertCircle } from 'lucide-react'; +import { IconArrowLeft as ArrowLeft, IconDownload as Download, IconAlertCircle as AlertCircle } from '@tabler/icons-react'; import { Button } from '@/components/ui/button'; import { useMigration } from '@/contexts/MigrationContext'; import { MigrationPasswordStep } from '@/components/migration/MigrationPasswordStep'; diff --git a/src/pages/onboarding/recover.tsx b/src/pages/onboarding/recover.tsx index 8742ba785..6ee097499 100644 --- a/src/pages/onboarding/recover.tsx +++ b/src/pages/onboarding/recover.tsx @@ -15,7 +15,7 @@ import { useDuplicateDetection } from '@/hooks/use-duplicate-detection'; import { encrypt, deriveMultiChainKeys, deriveBioforestAddresses } from '@/lib/crypto'; import { useChainConfigState, useEnabledBioforestChainConfigs, walletActions } from '@/stores'; import type { IWalletQuery } from '@/services/wallet/types'; -import { AlertCircle, Loader2 } from 'lucide-react'; +import { IconAlertCircle as AlertCircle, IconLoader2 as Loader2 } from '@tabler/icons-react'; type Step = 'keyType' | 'mnemonic' | 'arbitrary' | 'password' | 'collision' | 'success'; diff --git a/src/pages/receive/index.tsx b/src/pages/receive/index.tsx index 74d19c0a6..6919699bb 100644 --- a/src/pages/receive/index.tsx +++ b/src/pages/receive/index.tsx @@ -8,7 +8,7 @@ import { ChainIcon } from '@/components/wallet/chain-icon'; import { GradientButton } from '@/components/common/gradient-button'; import { Button } from '@/components/ui/button'; import { useClipboard, useToast, useHaptics } from '@/services'; -import { Copy, Share2, Check } from 'lucide-react'; +import { IconCopy as Copy, IconShare2 as Share2, IconCheck as Check } from '@tabler/icons-react'; import { useCurrentChainAddress, useSelectedChain, type ChainType } from '@/stores'; const CHAIN_NAMES: Record = { diff --git a/src/pages/scanner/index.tsx b/src/pages/scanner/index.tsx index 317644ab7..bcd8c467f 100644 --- a/src/pages/scanner/index.tsx +++ b/src/pages/scanner/index.tsx @@ -2,7 +2,12 @@ import { useState, useEffect, useCallback, useRef } from 'react'; import { useNavigate } from '@tanstack/react-router'; import { useTranslation } from 'react-i18next'; import { Button } from '@/components/ui/button'; -import { Camera, X, Image as ImageIcon, Flashlight } from 'lucide-react'; +import { + IconCamera as Camera, + IconX as X, + IconAperture as ImageIcon, + IconBulb as Flashlight, +} from '@tabler/icons-react'; import { cn } from '@/lib/utils'; import { useCamera } from '@/services/hooks'; import { scanQRFromVideo, scanQRFromFile, parseQRContent, type ParsedQRContent } from '@/lib/qr-parser'; diff --git a/src/pages/send/index.tsx b/src/pages/send/index.tsx index 543d27384..f91ede30c 100644 --- a/src/pages/send/index.tsx +++ b/src/pages/send/index.tsx @@ -12,7 +12,7 @@ import { useCamera, useToast, useHaptics } from '@/services'; import { useSend } from '@/hooks/use-send'; import { useAssets } from '@/hooks/use-assets'; import { formatAssetAmount } from '@/types/asset'; -import { ArrowRight } from 'lucide-react'; +import { IconArrowRight as ArrowRight } from '@tabler/icons-react'; import { useSelectedChain, type ChainType } from '@/stores'; const CHAIN_NAMES: Record = { diff --git a/src/pages/settings/chain-config.tsx b/src/pages/settings/chain-config.tsx index a3a15bf07..0d4106830 100644 --- a/src/pages/settings/chain-config.tsx +++ b/src/pages/settings/chain-config.tsx @@ -1,7 +1,12 @@ import { useEffect, useMemo, useRef, useState } from 'react'; import { useNavigate } from '@tanstack/react-router'; import { Trans, useTranslation } from 'react-i18next'; -import { AlertTriangle, Check, Plus, RefreshCw } from 'lucide-react'; +import { + IconAlertTriangle as AlertTriangle, + IconCheck as Check, + IconPlus as Plus, + IconRefresh as RefreshCw, +} from '@tabler/icons-react'; import { PageHeader } from '@/components/layout/page-header'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; diff --git a/src/pages/settings/change-password.tsx b/src/pages/settings/change-password.tsx index 84b19348d..bb247af4a 100644 --- a/src/pages/settings/change-password.tsx +++ b/src/pages/settings/change-password.tsx @@ -1,6 +1,6 @@ import { useState, useCallback } from 'react'; import { useNavigate } from '@tanstack/react-router'; -import { AlertCircle, Check } from 'lucide-react'; +import { IconAlertCircle as AlertCircle, IconCheck as Check } from '@tabler/icons-react'; import { PageHeader } from '@/components/layout/page-header'; import { PasswordInput } from '@/components/security/password-input'; import { useCurrentWallet, walletActions } from '@/stores'; diff --git a/src/pages/settings/currency.tsx b/src/pages/settings/currency.tsx index 8b372b2bb..948659baf 100644 --- a/src/pages/settings/currency.tsx +++ b/src/pages/settings/currency.tsx @@ -1,5 +1,5 @@ import { useNavigate } from '@tanstack/react-router'; -import { Check } from 'lucide-react'; +import { IconCheck as Check } from '@tabler/icons-react'; import { PageHeader } from '@/components/layout/page-header'; import { useCurrency, preferencesActions, currencies, type CurrencyCode } from '@/stores'; import { cn } from '@/lib/utils'; diff --git a/src/pages/settings/index.stories.tsx b/src/pages/settings/index.stories.tsx index 7dd8769e3..ffe40f5f0 100644 --- a/src/pages/settings/index.stories.tsx +++ b/src/pages/settings/index.stories.tsx @@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react'; import { SettingsItem } from './settings-item'; import { SettingsSection } from './settings-section'; import { SettingsPage } from './index'; -import { Languages, Lock, Eye } from 'lucide-react'; +import { IconLanguage as Languages, IconLock as Lock, IconEye as Eye } from '@tabler/icons-react'; // SettingsItem stories const itemMeta = { diff --git a/src/pages/settings/index.test.tsx b/src/pages/settings/index.test.tsx index 3f791e680..5d5b48586 100644 --- a/src/pages/settings/index.test.tsx +++ b/src/pages/settings/index.test.tsx @@ -5,7 +5,7 @@ import { SettingsItem } from './settings-item'; import { SettingsSection } from './settings-section'; import { SettingsPage } from './index'; import { TestI18nProvider } from '@/test/i18n-mock'; -import { Wallet } from 'lucide-react'; +import { IconWallet as Wallet } from '@tabler/icons-react'; // Mock router const mockNavigate = vi.fn(); diff --git a/src/pages/settings/index.tsx b/src/pages/settings/index.tsx index 805dd6a1a..04bb6c374 100644 --- a/src/pages/settings/index.tsx +++ b/src/pages/settings/index.tsx @@ -1,21 +1,21 @@ -import { useNavigate } from '@tanstack/react-router' -import { useTranslation } from 'react-i18next' +import { useNavigate } from '@tanstack/react-router'; +import { useTranslation } from 'react-i18next'; import { - Wallet, - BookUser, - Lock, - Eye, - KeyRound, - Languages, - DollarSign, - Palette, - Network, - Info, -} from 'lucide-react' -import { PageHeader } from '@/components/layout/page-header' -import { useCurrentWallet, useLanguage, useCurrency } from '@/stores' -import { SettingsItem } from './settings-item' -import { SettingsSection } from './settings-section' + IconWallet as Wallet, + IconAddressBook as BookUser, + IconLock as Lock, + IconEye as Eye, + IconCircleKey as KeyRound, + IconLanguage as Languages, + IconCoin as DollarSign, + IconPalette as Palette, + IconNetwork as Network, + IconInfoCircle as Info, +} from '@tabler/icons-react'; +import { PageHeader } from '@/components/layout/page-header'; +import { useCurrentWallet, useLanguage, useCurrency } from '@/stores'; +import { SettingsItem } from './settings-item'; +import { SettingsSection } from './settings-section'; /** 支持的语言显示名称映射 */ const LANGUAGE_NAMES: Record = { @@ -25,7 +25,7 @@ const LANGUAGE_NAMES: Record = { ja: '日本語', ko: '한국어', ar: 'العربية', -} +}; /** 支持的货币显示名称映射 */ const CURRENCY_NAMES: Record = { @@ -34,31 +34,29 @@ const CURRENCY_NAMES: Record = { EUR: 'EUR (€)', JPY: 'JPY (¥)', KRW: 'KRW (₩)', -} +}; export function SettingsPage() { - const navigate = useNavigate() - const { t } = useTranslation() - const currentWallet = useCurrentWallet() - const currentLanguage = useLanguage() - const currentCurrency = useCurrency() + const navigate = useNavigate(); + const { t } = useTranslation(); + const currentWallet = useCurrentWallet(); + const currentLanguage = useLanguage(); + const currentCurrency = useCurrency(); return ( -
+
{/* 钱包信息头 */} {currentWallet && ( -
-
+
+
{currentWallet.name.slice(0, 1)}

{currentWallet.name}

-

- {currentWallet.chainAddresses.length} 个链地址 -

+

{currentWallet.chainAddresses.length} 个链地址

)} @@ -70,7 +68,7 @@ export function SettingsPage() { label="钱包管理" onClick={() => navigate({ to: '/wallet/$walletId', params: { walletId: currentWallet?.id ?? '' } })} /> -
+
} label="地址簿" @@ -80,19 +78,14 @@ export function SettingsPage() { {/* 安全 */} - } - label="应用锁" - value="暂不支持" - disabled - /> -
+ } label="应用锁" value="暂不支持" disabled /> +
} label="查看助记词" onClick={() => navigate({ to: '/settings/mnemonic' })} /> -
+
} label="修改密码" @@ -108,20 +101,20 @@ export function SettingsPage() { value={LANGUAGE_NAMES[currentLanguage]} onClick={() => navigate({ to: '/settings/language' })} /> -
+
} label="货币单位" value={CURRENCY_NAMES[currentCurrency]} onClick={() => navigate({ to: '/settings/currency' })} /> -
+
} label="链配置" onClick={() => navigate({ to: '/settings/chains' })} /> -
+
} label="外观" @@ -145,5 +138,5 @@ export function SettingsPage() {
- ) + ); } diff --git a/src/pages/settings/language.tsx b/src/pages/settings/language.tsx index 9f19df708..9c685494f 100644 --- a/src/pages/settings/language.tsx +++ b/src/pages/settings/language.tsx @@ -1,5 +1,5 @@ import { useNavigate } from '@tanstack/react-router'; -import { Check } from 'lucide-react'; +import { IconCheck as Check } from '@tabler/icons-react'; import { PageHeader } from '@/components/layout/page-header'; import { useLanguage, preferencesActions, languages, type LanguageCode } from '@/stores'; import { cn } from '@/lib/utils'; diff --git a/src/pages/settings/settings-item.tsx b/src/pages/settings/settings-item.tsx index c4136150f..fa738969c 100644 --- a/src/pages/settings/settings-item.tsx +++ b/src/pages/settings/settings-item.tsx @@ -1,5 +1,5 @@ import { type ReactNode } from 'react'; -import { ChevronRight } from 'lucide-react'; +import { IconChevronRight as ChevronRight } from '@tabler/icons-react'; import { cn } from '@/lib/utils'; export interface SettingsItemProps { diff --git a/src/pages/settings/view-mnemonic.tsx b/src/pages/settings/view-mnemonic.tsx index 2bc8379a1..1d7db787a 100644 --- a/src/pages/settings/view-mnemonic.tsx +++ b/src/pages/settings/view-mnemonic.tsx @@ -1,6 +1,6 @@ import { useState, useEffect, useCallback } from 'react'; import { useNavigate } from '@tanstack/react-router'; -import { Eye, EyeOff, AlertTriangle } from 'lucide-react'; +import { IconEye as Eye, IconEyeOff as EyeOff, IconAlertTriangle as AlertTriangle } from '@tabler/icons-react'; import { PageHeader } from '@/components/layout/page-header'; import { MnemonicDisplay } from '@/components/security/mnemonic-display'; import { PasswordConfirmSheet } from '@/components/security/password-confirm-sheet'; diff --git a/src/pages/staking/components/burn-form.tsx b/src/pages/staking/components/burn-form.tsx index 70ba902f5..60359d7f8 100644 --- a/src/pages/staking/components/burn-form.tsx +++ b/src/pages/staking/components/burn-form.tsx @@ -4,7 +4,7 @@ import { useState, useMemo, useEffect, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -import { ArrowDown, ChevronRight } from 'lucide-react'; +import { IconArrowDown as ArrowDown, IconChevronRight as ChevronRight } from '@tabler/icons-react'; import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; import { Sheet, SheetContent, SheetHeader, SheetTitle } from '@/components/ui/sheet'; diff --git a/src/pages/staking/components/mint-form.tsx b/src/pages/staking/components/mint-form.tsx index 538415368..f325759fa 100644 --- a/src/pages/staking/components/mint-form.tsx +++ b/src/pages/staking/components/mint-form.tsx @@ -4,7 +4,7 @@ import { useState, useMemo, useEffect, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -import { ArrowDown, ChevronRight } from 'lucide-react'; +import { IconArrowDown as ArrowDown, IconChevronRight as ChevronRight } from '@tabler/icons-react'; import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; import { Sheet, SheetContent, SheetHeader, SheetTitle } from '@/components/ui/sheet'; diff --git a/src/pages/staking/components/staking-overview-panel.tsx b/src/pages/staking/components/staking-overview-panel.tsx index 8db08f870..8a67b2d3c 100644 --- a/src/pages/staking/components/staking-overview-panel.tsx +++ b/src/pages/staking/components/staking-overview-panel.tsx @@ -4,7 +4,7 @@ import { useState, useMemo, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; -import { Search, Filter } from 'lucide-react'; +import { IconSearch as Search, IconFilter as Filter } from '@tabler/icons-react'; import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from '@/components/ui/sheet'; diff --git a/src/pages/staking/components/staking-record-list.tsx b/src/pages/staking/components/staking-record-list.tsx index 1bd324efa..1089d5d64 100644 --- a/src/pages/staking/components/staking-record-list.tsx +++ b/src/pages/staking/components/staking-record-list.tsx @@ -4,7 +4,14 @@ import { useState, useMemo, useEffect, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; -import { ArrowDownRight, ArrowUpRight, Clock, CheckCircle, XCircle, Loader2 } from 'lucide-react'; +import { + IconArrowDownRight as ArrowDownRight, + IconArrowUpRight as ArrowUpRight, + IconClock as Clock, + IconCircleCheck as CheckCircle, + IconCircleX as XCircle, + IconLoader2 as Loader2, +} from '@tabler/icons-react'; import { Button } from '@/components/ui/button'; import { TokenIcon } from '@/components/token/token-icon'; import { LoadingSpinner } from '@/components/common/loading-spinner'; diff --git a/src/pages/token/detail.tsx b/src/pages/token/detail.tsx index 1e700692d..3a77448d4 100644 --- a/src/pages/token/detail.tsx +++ b/src/pages/token/detail.tsx @@ -3,7 +3,7 @@ import { PageHeader } from '@/components/layout/page-header'; import { BalanceDisplay } from '@/components/token/balance-display'; import { TransactionList } from '@/components/transaction/transaction-list'; import { GradientButton } from '@/components/common/gradient-button'; -import { ArrowUp, ArrowDown } from 'lucide-react'; +import { IconArrowUp as ArrowUp, IconArrowDown as ArrowDown } from '@tabler/icons-react'; // 临时模拟数据 const mockToken = { diff --git a/src/pages/wallet/create.tsx b/src/pages/wallet/create.tsx index 21cc64303..054c43532 100644 --- a/src/pages/wallet/create.tsx +++ b/src/pages/wallet/create.tsx @@ -10,7 +10,14 @@ import { MnemonicDisplay } from '@/components/security/mnemonic-display'; import { PasswordInput } from '@/components/security/password-input'; import { Input } from '@/components/ui/input'; import { cn } from '@/lib/utils'; -import { ShieldCheck, Eye, EyeOff, ArrowRight, KeyRound, CheckCircle } from 'lucide-react'; +import { + IconShieldCheck as ShieldCheck, + IconEye as Eye, + IconEyeOff as EyeOff, + IconArrowRight as ArrowRight, + IconCircleKey as KeyRound, + IconCircleCheck as CheckCircle, +} from '@tabler/icons-react'; import { useChainConfigState, useEnabledBioforestChainConfigs, walletActions } from '@/stores'; import { generateMnemonic, encrypt, deriveMultiChainKeys, deriveBioforestAddresses } from '@/lib/crypto'; diff --git a/src/pages/wallet/detail.tsx b/src/pages/wallet/detail.tsx index e8f66e339..040bc6e25 100644 --- a/src/pages/wallet/detail.tsx +++ b/src/pages/wallet/detail.tsx @@ -1,17 +1,17 @@ -import { useParams, useNavigate } from '@tanstack/react-router' -import { PageHeader } from '@/components/layout/page-header' -import { AddressDisplay } from '@/components/wallet/address-display' -import { ChainIcon } from '@/components/wallet/chain-icon' -import { Button } from '@/components/ui/button' -import { Alert } from '@/components/common/alert' -import { useBiometric, useToast, useHaptics } from '@/services' -import { - KeyRound, - Trash2, - Edit3, - Shield, -} from 'lucide-react' -import { useWallets, walletActions, type ChainType } from '@/stores' +import { useParams, useNavigate } from '@tanstack/react-router'; +import { PageHeader } from '@/components/layout/page-header'; +import { AddressDisplay } from '@/components/wallet/address-display'; +import { ChainIcon } from '@/components/wallet/chain-icon'; +import { Button } from '@/components/ui/button'; +import { Alert } from '@/components/common/alert'; +import { useBiometric, useToast, useHaptics } from '@/services'; +import { + IconCircleKey as KeyRound, + IconTrash as Trash2, + IconPencilMinus as Edit3, + IconShield as Shield, +} from '@tabler/icons-react'; +import { useWallets, walletActions, type ChainType } from '@/stores'; const CHAIN_NAMES: Record = { ethereum: 'Ethereum', @@ -26,104 +26,89 @@ const CHAIN_NAMES: Record = { biwmeta: 'BIWMeta', ethmeta: 'ETHMeta', malibu: 'Malibu', -} +}; export function WalletDetailPage() { - const { walletId } = useParams({ from: '/wallet/$walletId' }) - const navigate = useNavigate() - const biometric = useBiometric() - const toast = useToast() - const haptics = useHaptics() - - const wallets = useWallets() - const wallet = wallets.find(w => w.id === walletId) + const { walletId } = useParams({ from: '/wallet/$walletId' }); + const navigate = useNavigate(); + const biometric = useBiometric(); + const toast = useToast(); + const haptics = useHaptics(); + + const wallets = useWallets(); + const wallet = wallets.find((w) => w.id === walletId); const handleExportMnemonic = async () => { - const { isAvailable } = await biometric.isAvailable() - + const { isAvailable } = await biometric.isAvailable(); + if (isAvailable) { - const result = await biometric.verify({ title: '验证身份以导出助记词' }) + const result = await biometric.verify({ title: '验证身份以导出助记词' }); if (!result.success) { - toast.show({ message: '验证失败', position: 'center' }) - return + toast.show({ message: '验证失败', position: 'center' }); + return; } } - - await haptics.impact('success') - toast.show('助记词导出功能开发中') + + await haptics.impact('success'); + toast.show('助记词导出功能开发中'); // TODO: 实现助记词导出 - } + }; const handleDeleteWallet = async () => { - const { isAvailable } = await biometric.isAvailable() - + const { isAvailable } = await biometric.isAvailable(); + if (isAvailable) { - const result = await biometric.verify({ title: '验证身份以删除钱包' }) + const result = await biometric.verify({ title: '验证身份以删除钱包' }); if (!result.success) { - toast.show({ message: '验证失败', position: 'center' }) - return + toast.show({ message: '验证失败', position: 'center' }); + return; } } - - walletActions.deleteWallet(walletId) - await haptics.impact('warning') - toast.show('钱包已删除') - navigate({ to: '/' }) - } + + walletActions.deleteWallet(walletId); + await haptics.impact('warning'); + toast.show('钱包已删除'); + navigate({ to: '/' }); + }; if (!wallet) { return (
- navigate({ to: '/' })} - /> + navigate({ to: '/' })} />
钱包不存在
- ) + ); } return (
- navigate({ to: '/' })} - /> + navigate({ to: '/' })} />
{/* 钱包信息 */} -
+
-
- +
+

{wallet.name}

-

- 创建于 {new Date(wallet.createdAt).toLocaleDateString()} -

+

创建于 {new Date(wallet.createdAt).toLocaleDateString()}

{/* 多链地址 */}
-

链地址

+

链地址

{wallet.chainAddresses.map((chainAddr) => ( -
+
-
+

{CHAIN_NAMES[chainAddr.chain] ?? chainAddr.chain}

- +
))} @@ -142,18 +127,14 @@ export function WalletDetailPage() { 编辑钱包名称 -
{/* 安全提示 */} - - 导出助记词需要验证密码,请确保在安全环境下操作 - + 导出助记词需要验证密码,请确保在安全环境下操作
- ) + ); } diff --git a/src/pages/wallet/import.tsx b/src/pages/wallet/import.tsx index e7e922604..932257c38 100644 --- a/src/pages/wallet/import.tsx +++ b/src/pages/wallet/import.tsx @@ -8,7 +8,11 @@ import { Alert } from '@/components/common/alert'; import { ProgressSteps } from '@/components/common/step-indicator'; import { MnemonicInput } from '@/components/security/mnemonic-input'; import { PasswordInput } from '@/components/security/password-input'; -import { ShieldCheck, ArrowRight, FileKey } from 'lucide-react'; +import { + IconShieldCheck as ShieldCheck, + IconArrowRight as ArrowRight, + IconSquareKey as FileKey, +} from '@tabler/icons-react'; import { useChainConfigState, useEnabledBioforestChainConfigs, walletActions } from '@/stores'; import { validateMnemonic, encrypt, deriveMultiChainKeys, deriveBioforestAddresses } from '@/lib/crypto'; diff --git a/src/pages/wallet/list.tsx b/src/pages/wallet/list.tsx index a983dd0ac..f56516ee1 100644 --- a/src/pages/wallet/list.tsx +++ b/src/pages/wallet/list.tsx @@ -1,7 +1,7 @@ import { useCallback } from 'react'; import { useNavigate } from '@tanstack/react-router'; import { useStore } from '@tanstack/react-store'; -import { Plus, Check, ChevronRight } from 'lucide-react'; +import { IconPlus as Plus, IconCheck as Check, IconChevronRight as ChevronRight } from '@tabler/icons-react'; import { PageHeader } from '@/components/layout/page-header'; import { walletStore, walletActions, type Wallet } from '@/stores'; import { cn } from '@/lib/utils'; From e813b9626366f329144500ea30af8b1e5e9039c6 Mon Sep 17 00:00:00 2001 From: Gaubee Date: Tue, 16 Dec 2025 19:19:07 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=92=84=20=E9=87=8D=E6=9E=84=E9=85=8D?= =?UTF-8?q?=E8=89=B2,=20=E4=BF=AE=E5=A4=8D=E9=A1=B5=E9=9D=A2=E5=B8=83?= =?UTF-8?q?=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TDD.md | 6 +- src/App.tsx | 8 +- .../common/amount-display.stories.tsx | 123 +++++++------- src/components/common/amount-display.tsx | 145 ++++++++--------- src/components/common/empty-state.tsx | 36 ++--- .../common/gradient-button.stories.tsx | 28 ++-- src/components/common/gradient-button.tsx | 58 +++---- .../common/loading-spinner.stories.tsx | 24 +-- src/components/common/theme-demo.stories.tsx | 85 +++++----- .../common/time-display.stories.tsx | 58 +++---- src/components/common/time-display.tsx | 127 +++++++-------- src/components/layout/app-layout.tsx | 16 +- .../layout/bottom-sheet.stories.tsx | 85 +++++----- src/components/layout/page-header.stories.tsx | 41 ++--- src/components/layout/page-header.tsx | 61 +++---- src/components/layout/tab-bar.stories.tsx | 92 +++++++---- .../onboarding/collision-confirm-dialog.tsx | 6 +- .../onboarding/create-wallet-success.tsx | 2 +- src/components/security/mnemonic-display.tsx | 2 +- .../security/mnemonic-input.stories.tsx | 70 ++++---- src/components/security/mnemonic-input.tsx | 141 ++++++++-------- .../security/password-input.stories.tsx | 66 ++++---- src/components/security/password-input.tsx | 4 +- .../token/balance-display.stories.tsx | 30 ++-- src/components/token/token-icon.stories.tsx | 22 ++- src/components/token/token-item.stories.tsx | 34 ++-- src/components/token/token-item.tsx | 126 +++++++-------- .../transaction/transaction-item.tsx | 6 +- .../transaction/transaction-list.tsx | 68 ++++---- .../transfer/address-input.stories.tsx | 73 +++------ src/components/transfer/address-input.tsx | 6 +- .../transfer/amount-input.stories.tsx | 92 +++++------ src/components/transfer/amount-input.tsx | 152 +++++++++--------- src/components/ui/marquee-text.stories.tsx | 43 +++-- .../wallet/address-display.stories.tsx | 42 ++--- src/components/wallet/address-display.tsx | 2 +- src/components/wallet/chain-icon.stories.tsx | 38 ++--- src/pages/guide/WelcomeScreen.tsx | 2 +- src/pages/history/detail.tsx | 2 +- src/pages/home/index.tsx | 2 +- src/pages/onboarding/create.tsx | 2 +- src/pages/send/index.tsx | 4 +- src/pages/wallet/create.tsx | 2 +- src/pages/wallet/import.tsx | 2 +- src/styles/globals.css | 49 +++--- 45 files changed, 996 insertions(+), 1087 deletions(-) diff --git a/TDD.md b/TDD.md index 873effe83..244fe03f8 100644 --- a/TDD.md +++ b/TDD.md @@ -1122,7 +1122,7 @@ export function WalletCard({ wallet }: WalletCardProps) { {wallet.name} {/* 宽容器时显示完整地址 */} -

+

{shortenAddress(wallet.address)} {wallet.address}

@@ -1133,7 +1133,7 @@ export function WalletCard({ wallet }: WalletCardProps) {

{formatBalance(wallet.balance)}

-

+

≈ ${wallet.fiatValue}

@@ -3100,7 +3100,7 @@ export function AuthorizeCard({ />

{appInfo.name}

-

{appInfo.home}

+

{appInfo.home}

diff --git a/src/App.tsx b/src/App.tsx index a5df3258a..0b423e2f6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,10 +1,10 @@ export default function App() { return ( -
+
-

BFM Pay

-

钱包应用开发中...

+

BFM Pay

+

钱包应用开发中...

- ) + ); } diff --git a/src/components/common/amount-display.stories.tsx b/src/components/common/amount-display.stories.tsx index dcfe23290..5fae3dd61 100644 --- a/src/components/common/amount-display.stories.tsx +++ b/src/components/common/amount-display.stories.tsx @@ -1,5 +1,5 @@ -import type { Meta, StoryObj } from '@storybook/react' -import { AmountDisplay, AmountWithFiat } from './amount-display' +import type { Meta, StoryObj } from '@storybook/react'; +import { AmountDisplay, AmountWithFiat } from './amount-display'; const meta: Meta = { title: 'Common/AmountDisplay', @@ -11,29 +11,39 @@ const meta: Meta = { size: { control: 'select', options: ['xs', 'sm', 'md', 'lg', 'xl'] }, weight: { control: 'select', options: ['normal', 'medium', 'semibold', 'bold'] }, }, -} +}; -export default meta -type Story = StoryObj +export default meta; +type Story = StoryObj; export const Default: Story = { args: { value: 1234.5678, symbol: 'USDT', }, -} +}; export const Sizes: Story = { render: () => (
-
-
-
-
-
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
), -} +}; export const WithSign: Story = { render: () => ( @@ -42,21 +52,30 @@ export const WithSign: Story = {
-

sign="always" + color="auto"

+

sign="always" + color="auto"

), -} +}; export const Compact: Story = { render: () => (
-
(1,234)
-
(12,345)
-
(1,234,567)
-
(1,234,567,890)
+
+ (1,234) +
+
+ (12,345) +
+
+ (1,234,567) +
+
+ {' '} + (1,234,567,890) +
), -} +}; export const Hidden: Story = { args: { @@ -64,7 +83,7 @@ export const Hidden: Story = { symbol: 'USDT', hidden: true, }, -} +}; export const MonoFont: Story = { args: { @@ -72,65 +91,64 @@ export const MonoFont: Story = { mono: true, weight: 'semibold', }, -} +}; export const SmallNumbers: Story = { render: () => (
-
(0.1)
-
(0.01)
-
(0.001)
-
(0.0001)
-
(0.00001)
+
+ (0.1) +
+
+ (0.01) +
+
+ (0.001) +
+
+ (0.0001) +
+
+ (0.00001) +
), -} +}; // AmountWithFiat stories export const WithFiat: Story = { render: () => (
-

Vertical (default)

- +

Vertical (default)

+
-

Horizontal

- +

Horizontal

+
), -} +}; export const PriceChange: Story = { render: () => (
- BTC: + BTC: - % + %
- ETH: + ETH: - % + %
), -} +}; export const TokenList: Story = { render: () => ( @@ -142,14 +160,9 @@ export const TokenList: Story = { ].map((token) => (
{token.symbol} - +
))}
), -} +}; diff --git a/src/components/common/amount-display.tsx b/src/components/common/amount-display.tsx index 975d01249..4411400f9 100644 --- a/src/components/common/amount-display.tsx +++ b/src/components/common/amount-display.tsx @@ -1,67 +1,67 @@ -import { cn } from '@/lib/utils' +import { cn } from '@/lib/utils'; -type AmountSign = 'auto' | 'always' | 'never' -type AmountColor = 'auto' | 'default' | 'positive' | 'negative' +type AmountSign = 'auto' | 'always' | 'never'; +type AmountColor = 'auto' | 'default' | 'positive' | 'negative'; interface AmountDisplayProps { - value: string | number - symbol?: string | undefined - decimals?: number | undefined + value: string | number; + symbol?: string | undefined; + decimals?: number | undefined; /** 是否显示正负号 */ - sign?: AmountSign | undefined + sign?: AmountSign | undefined; /** 颜色模式:auto 根据正负自动,default 不变色 */ - color?: AmountColor | undefined + color?: AmountColor | undefined; /** 是否使用紧凑模式(1K, 1M) */ - compact?: boolean | undefined + compact?: boolean | undefined; /** 隐藏金额(隐私模式) */ - hidden?: boolean | undefined + hidden?: boolean | undefined; /** 尺寸 */ - size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | undefined + size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | undefined; /** 字重 */ - weight?: 'normal' | 'medium' | 'semibold' | 'bold' | undefined + weight?: 'normal' | 'medium' | 'semibold' | 'bold' | undefined; /** 是否等宽字体 */ - mono?: boolean | undefined - className?: string | undefined + mono?: boolean | undefined; + className?: string | undefined; } // 格式化数字 function formatAmount( value: string | number, decimals: number, - compact: boolean + compact: boolean, ): { formatted: string; isNegative: boolean; isZero: boolean } { - const num = typeof value === 'string' ? parseFloat(value) : value - + const num = typeof value === 'string' ? parseFloat(value) : value; + if (isNaN(num)) { - return { formatted: '0', isNegative: false, isZero: true } + return { formatted: '0', isNegative: false, isZero: true }; } - - const isNegative = num < 0 - const isZero = num === 0 - const absNum = Math.abs(num) - - let formatted: string - + + const isNegative = num < 0; + const isZero = num === 0; + const absNum = Math.abs(num); + + let formatted: string; + if (isZero) { - formatted = '0' + formatted = '0'; } else if (absNum < 0.0001) { - formatted = '< 0.0001' + formatted = '< 0.0001'; } else if (compact && absNum >= 1_000_000_000) { - formatted = (absNum / 1_000_000_000).toFixed(2).replace(/\.?0+$/, '') + 'B' + formatted = (absNum / 1_000_000_000).toFixed(2).replace(/\.?0+$/, '') + 'B'; } else if (compact && absNum >= 1_000_000) { - formatted = (absNum / 1_000_000).toFixed(2).replace(/\.?0+$/, '') + 'M' + formatted = (absNum / 1_000_000).toFixed(2).replace(/\.?0+$/, '') + 'M'; } else if (compact && absNum >= 1_000) { - formatted = (absNum / 1_000).toFixed(2).replace(/\.?0+$/, '') + 'K' + formatted = (absNum / 1_000).toFixed(2).replace(/\.?0+$/, '') + 'K'; } else if (absNum >= 1_000) { - formatted = absNum.toLocaleString('en-US', { + formatted = absNum.toLocaleString('en-US', { minimumFractionDigits: 0, - maximumFractionDigits: decimals - }) + maximumFractionDigits: decimals, + }); } else { - formatted = absNum.toFixed(decimals).replace(/\.?0+$/, '') + formatted = absNum.toFixed(decimals).replace(/\.?0+$/, ''); } - - return { formatted, isNegative, isZero } + + return { formatted, isNegative, isZero }; } const sizeClasses = { @@ -70,14 +70,14 @@ const sizeClasses = { md: 'text-base', lg: 'text-lg', xl: 'text-xl', -} +}; const weightClasses = { normal: 'font-normal', medium: 'font-medium', semibold: 'font-semibold', bold: 'font-bold', -} +}; export function AmountDisplay({ value, @@ -96,54 +96,47 @@ export function AmountDisplay({ return ( •••••• - {symbol && {symbol}} + {symbol && {symbol}} - ) + ); } - const { formatted, isNegative, isZero } = formatAmount(value, decimals, compact) - + const { formatted, isNegative, isZero } = formatAmount(value, decimals, compact); + // 计算符号 - let signChar = '' + let signChar = ''; if (sign === 'always' && !isZero) { - signChar = isNegative ? '-' : '+' + signChar = isNegative ? '-' : '+'; } else if (sign === 'auto' && isNegative) { - signChar = '-' + signChar = '-'; } - + // 计算颜色 - let colorClass = '' + let colorClass = ''; if (color === 'auto' && !isZero) { - colorClass = isNegative ? 'text-destructive' : 'text-secondary' + colorClass = isNegative ? 'text-destructive' : 'text-secondary'; } else if (color === 'positive') { - colorClass = 'text-secondary' + colorClass = 'text-secondary'; } else if (color === 'negative') { - colorClass = 'text-destructive' + colorClass = 'text-destructive'; } return ( - - {signChar}{formatted} - {symbol && {symbol}} + + {signChar} + {formatted} + {symbol && {symbol}} - ) + ); } // 带法币价值的复合显示 interface AmountWithFiatProps extends AmountDisplayProps { - fiatValue?: string | number | undefined - fiatSymbol?: string | undefined - fiatDecimals?: number | undefined + fiatValue?: string | number | undefined; + fiatSymbol?: string | undefined; + fiatDecimals?: number | undefined; /** 布局方向 */ - layout?: 'vertical' | 'horizontal' | undefined + layout?: 'vertical' | 'horizontal' | undefined; } export function AmountWithFiat({ @@ -154,33 +147,33 @@ export function AmountWithFiat({ className, ...amountProps }: AmountWithFiatProps) { - const fiatFormatted = fiatValue !== undefined - ? formatAmount(fiatValue, fiatDecimals, false).formatted - : null + const fiatFormatted = fiatValue !== undefined ? formatAmount(fiatValue, fiatDecimals, false).formatted : null; if (layout === 'horizontal') { return ( {fiatFormatted && ( - - ≈ {fiatSymbol}{fiatFormatted} + + ≈ {fiatSymbol} + {fiatFormatted} )} - ) + ); } return (
{fiatFormatted && ( -

- ≈ {fiatSymbol}{fiatFormatted} +

+ ≈ {fiatSymbol} + {fiatFormatted}

)}
- ) + ); } -export { formatAmount } +export { formatAmount }; diff --git a/src/components/common/empty-state.tsx b/src/components/common/empty-state.tsx index 2bede8dcc..577470540 100644 --- a/src/components/common/empty-state.tsx +++ b/src/components/common/empty-state.tsx @@ -1,32 +1,20 @@ -import { cn } from '@/lib/utils' +import { cn } from '@/lib/utils'; interface EmptyStateProps { - icon?: React.ReactNode | undefined - title: string - description?: string | undefined - action?: React.ReactNode | undefined - className?: string | undefined + icon?: React.ReactNode | undefined; + title: string; + description?: string | undefined; + action?: React.ReactNode | undefined; + className?: string | undefined; } -export function EmptyState({ - icon, - title, - description, - action, - className, -}: EmptyStateProps) { +export function EmptyState({ icon, title, description, action, className }: EmptyStateProps) { return ( -
- {icon && ( -
- {icon} -
- )} -

{title}

- {description && ( -

{description}

- )} +
+ {icon &&
{icon}
} +

{title}

+ {description &&

{description}

} {action &&
{action}
}
- ) + ); } diff --git a/src/components/common/gradient-button.stories.tsx b/src/components/common/gradient-button.stories.tsx index 0b603e456..81bac9a05 100644 --- a/src/components/common/gradient-button.stories.tsx +++ b/src/components/common/gradient-button.stories.tsx @@ -1,5 +1,5 @@ -import type { Meta, StoryObj } from '@storybook/react' -import { GradientButton } from './gradient-button' +import type { Meta, StoryObj } from '@storybook/react'; +import { GradientButton } from './gradient-button'; const meta: Meta = { title: 'Common/GradientButton', @@ -24,16 +24,16 @@ const meta: Meta = { control: 'boolean', }, }, -} +}; -export default meta -type Story = StoryObj +export default meta; +type Story = StoryObj; export const Default: Story = { args: { children: '确认转账', }, -} +}; export const AllVariants: Story = { render: () => ( @@ -44,43 +44,43 @@ export const AllVariants: Story = { Mint
), -} +}; export const AllSizes: Story = { render: () => ( -
+
Small Medium (默认) Large
), -} +}; export const Loading: Story = { args: { children: '处理中...', loading: true, }, -} +}; export const Disabled: Story = { args: { children: '不可用', disabled: true, }, -} +}; export const FullWidth: Story = { args: { children: '全宽按钮', fullWidth: true, }, -} +}; export const ResponsiveContainer: Story = { render: () => (
-

拖拽容器边缘或使用工具栏切换容器尺寸,观察按钮响应式变化

+

拖拽容器边缘或使用工具栏切换容器尺寸,观察按钮响应式变化

响应式按钮
), @@ -91,4 +91,4 @@ export const ResponsiveContainer: Story = { }, }, }, -} +}; diff --git a/src/components/common/gradient-button.tsx b/src/components/common/gradient-button.tsx index b0eed7b05..82696e946 100644 --- a/src/components/common/gradient-button.tsx +++ b/src/components/common/gradient-button.tsx @@ -1,7 +1,8 @@ -import { forwardRef } from 'react' -import { Slot } from '@radix-ui/react-slot' -import { cva, type VariantProps } from 'class-variance-authority' -import { cn } from '@/lib/utils' +import { forwardRef } from 'react'; +import { Slot } from '@radix-ui/react-slot'; +import { cva, type VariantProps } from 'class-variance-authority'; +import { cn } from '@/lib/utils'; +import { IconLoader } from '@tabler/icons-react'; const gradientButtonVariants = cva( 'inline-flex items-center justify-center gap-2 rounded-full text-white font-medium transition-opacity disabled:opacity-50 disabled:cursor-not-allowed [&_svg]:pointer-events-none [&_svg]:shrink-0', @@ -28,20 +29,19 @@ const gradientButtonVariants = cva( size: 'md', fullWidth: false, }, - } -) + }, +); export interface GradientButtonProps - extends React.ButtonHTMLAttributes, - VariantProps { - asChild?: boolean - loading?: boolean + extends React.ButtonHTMLAttributes, VariantProps { + asChild?: boolean; + loading?: boolean; } const GradientButton = forwardRef( ({ className, variant, size, fullWidth, asChild = false, loading, children, disabled, ...props }, ref) => { - const Comp = asChild ? Slot : 'button' - + const Comp = asChild ? Slot : 'button'; + return ( ( disabled={disabled || loading} {...props} > - {loading && ( - - )} + {loading && } {children} - ) - } -) -GradientButton.displayName = 'GradientButton' + ); + }, +); +GradientButton.displayName = 'GradientButton'; -export { GradientButton, gradientButtonVariants } +export { GradientButton, gradientButtonVariants }; diff --git a/src/components/common/loading-spinner.stories.tsx b/src/components/common/loading-spinner.stories.tsx index ea4d04343..0318c31e3 100644 --- a/src/components/common/loading-spinner.stories.tsx +++ b/src/components/common/loading-spinner.stories.tsx @@ -1,5 +1,5 @@ -import type { Meta, StoryObj } from '@storybook/react' -import { LoadingSpinner } from './loading-spinner' +import type { Meta, StoryObj } from '@storybook/react'; +import { LoadingSpinner } from './loading-spinner'; const meta: Meta = { title: 'Common/LoadingSpinner', @@ -14,37 +14,37 @@ const meta: Meta = { control: 'boolean', }, }, -} +}; -export default meta -type Story = StoryObj +export default meta; +type Story = StoryObj; -export const Default: Story = {} +export const Default: Story = {}; export const AllSizes: Story = { render: () => (
- Small + Small
- Medium + Medium
- Large + Large
), -} +}; export const InButton: Story = { render: () => ( - ), -} +}; diff --git a/src/components/common/theme-demo.stories.tsx b/src/components/common/theme-demo.stories.tsx index f239eb14d..682d3586d 100644 --- a/src/components/common/theme-demo.stories.tsx +++ b/src/components/common/theme-demo.stories.tsx @@ -1,24 +1,24 @@ -import type { Meta, StoryObj } from '@storybook/react' -import { useTranslation } from 'react-i18next' -import { GradientButton } from './gradient-button' -import { LoadingSpinner } from './loading-spinner' -import { EmptyState } from './empty-state' +import type { Meta, StoryObj } from '@storybook/react'; +import { useTranslation } from 'react-i18next'; +import { GradientButton } from './gradient-button'; +import { LoadingSpinner } from './loading-spinner'; +import { EmptyState } from './empty-state'; function ThemeDemoComponent() { - const { t, i18n } = useTranslation() - + const { t, i18n } = useTranslation(); + return ( -
+

{t('wallet.myWallet')}

-

{t('common.loading')}

+

{t('common.loading')}

- +
{t('common.loading')}...
- +
{t('wallet.transfer')} @@ -27,17 +27,17 @@ function ThemeDemoComponent() { {t('wallet.receive')}
- +

{t('transaction.send')}

-

{t('transaction.pending')}

+

{t('transaction.pending')}

- -
+ +
Current: {i18n.language} | Dir: {i18n.dir()}
- ) + ); } const meta: Meta = { @@ -63,16 +63,16 @@ const meta: Meta = { }, }, }, -} +}; -export default meta -type Story = StoryObj +export default meta; +type Story = StoryObj; -export const Default: Story = {} +export const Default: Story = {}; export const WithEmptyState: Story = { render: () => { - const { t } = useTranslation() + const { t } = useTranslation(); return ( } /> - ) + ); }, -} +}; export const TransactionStates: Story = { render: () => { - const { t } = useTranslation() + const { t } = useTranslation(); return (
{(['send', 'receive', 'swap', 'stake'] as const).map((type) => ( -
+
{t(`transaction.${type}`)} - {t('transaction.confirmed')} + {t('transaction.confirmed')}
))}
- ) + ); }, -} +}; export const SecurityLabels: Story = { render: () => { - const { t } = useTranslation() + const { t } = useTranslation(); return (
-
+

{t('security.password')}

-
+
{(['weak', 'medium', 'strong'] as const).map((level) => ( - {t(`security.strength.${level}`)} @@ -128,11 +127,11 @@ export const SecurityLabels: Story = { ))}
-
+

{t('security.mnemonic')}

-

{t('security.copyMnemonic')}

+

{t('security.copyMnemonic')}

- ) + ); }, -} +}; diff --git a/src/components/common/time-display.stories.tsx b/src/components/common/time-display.stories.tsx index b9ae9624e..f8a805210 100644 --- a/src/components/common/time-display.stories.tsx +++ b/src/components/common/time-display.stories.tsx @@ -1,5 +1,5 @@ -import type { Meta, StoryObj } from '@storybook/react' -import { TimeDisplay } from './time-display' +import type { Meta, StoryObj } from '@storybook/react'; +import { TimeDisplay } from './time-display'; const meta: Meta = { title: 'Common/TimeDisplay', @@ -11,93 +11,93 @@ const meta: Meta = { options: ['relative', 'date', 'datetime', 'time'], }, }, -} +}; -export default meta -type Story = StoryObj +export default meta; +type Story = StoryObj; -const now = new Date() -const minutesAgo = new Date(now.getTime() - 5 * 60 * 1000) -const hoursAgo = new Date(now.getTime() - 3 * 60 * 60 * 1000) -const daysAgo = new Date(now.getTime() - 2 * 24 * 60 * 60 * 1000) -const weeksAgo = new Date(now.getTime() - 14 * 24 * 60 * 60 * 1000) +const now = new Date(); +const minutesAgo = new Date(now.getTime() - 5 * 60 * 1000); +const hoursAgo = new Date(now.getTime() - 3 * 60 * 60 * 1000); +const daysAgo = new Date(now.getTime() - 2 * 24 * 60 * 60 * 1000); +const weeksAgo = new Date(now.getTime() - 14 * 24 * 60 * 60 * 1000); export const Default: Story = { args: { value: minutesAgo, }, -} +}; export const RelativeTime: Story = { render: () => (
- 刚刚: + 刚刚:
- 5分钟前: + 5分钟前:
- 3小时前: + 3小时前:
- 2天前: + 2天前:
- 2周前: + 2周前:
), -} +}; export const DateFormat: Story = { args: { value: now, format: 'date', }, -} +}; export const DateTimeFormat: Story = { args: { value: now, format: 'datetime', }, -} +}; export const TimeFormat: Story = { args: { value: now, format: 'time', }, -} +}; export const AllFormats: Story = { render: () => (
-

Relative (default)

+

Relative (default)

-

Date

+

Date

-

DateTime

+

DateTime

-

Time

+

Time

), -} +}; export const TransactionList: Story = { render: () => ( @@ -107,14 +107,14 @@ export const TransactionList: Story = { { type: '接收', amount: '+0.5 ETH', time: hoursAgo }, { type: '兑换', amount: '100 USDT → 0.05 ETH', time: daysAgo }, ].map((tx, i) => ( -
+

{tx.type}

-

{tx.amount}

+

{tx.amount}

- +
))}
), -} +}; diff --git a/src/components/common/time-display.tsx b/src/components/common/time-display.tsx index 10d7b01e2..90f5565ac 100644 --- a/src/components/common/time-display.tsx +++ b/src/components/common/time-display.tsx @@ -1,58 +1,54 @@ -import { cn } from '@/lib/utils' -import { useTranslation } from 'react-i18next' -import type { TFunction } from 'i18next' +import { cn } from '@/lib/utils'; +import { useTranslation } from 'react-i18next'; +import type { TFunction } from 'i18next'; -type TimeFormat = 'relative' | 'date' | 'datetime' | 'time' +type TimeFormat = 'relative' | 'date' | 'datetime' | 'time'; interface TimeDisplayProps { - value: Date | string | number - format?: TimeFormat - className?: string + value: Date | string | number; + format?: TimeFormat; + className?: string; } function toDate(value: Date | string | number): Date { - if (value instanceof Date) return value - if (typeof value === 'number') return new Date(value) - return new Date(value) + if (value instanceof Date) return value; + if (typeof value === 'number') return new Date(value); + return new Date(value); } // 获取当前语言的 locale function getLocale(lang: string): string { const localeMap: Record = { 'zh-CN': 'zh-CN', - 'en': 'en-US', - 'ar': 'ar-SA', - } - return localeMap[lang] || lang + en: 'en-US', + ar: 'ar-SA', + }; + return localeMap[lang] || lang; } -export function TimeDisplay({ - value, - format = 'relative', - className, -}: TimeDisplayProps) { - const { t, i18n } = useTranslation() - const date = toDate(value) - const locale = getLocale(i18n.language) - +export function TimeDisplay({ value, format = 'relative', className }: TimeDisplayProps) { + const { t, i18n } = useTranslation(); + const date = toDate(value); + const locale = getLocale(i18n.language); + // 无效日期 if (isNaN(date.getTime())) { - return -- + return --; } - let formatted: string - + let formatted: string; + switch (format) { case 'relative': - formatted = formatRelativeTime(date, t) - break + formatted = formatRelativeTime(date, t); + break; case 'date': formatted = date.toLocaleDateString(locale, { year: 'numeric', month: '2-digit', day: '2-digit', - }) - break + }); + break; case 'datetime': formatted = date.toLocaleString(locale, { year: 'numeric', @@ -60,14 +56,14 @@ export function TimeDisplay({ day: '2-digit', hour: '2-digit', minute: '2-digit', - }) - break + }); + break; case 'time': formatted = date.toLocaleTimeString(locale, { hour: '2-digit', minute: '2-digit', - }) - break + }); + break; } const titleText = date.toLocaleString(locale, { @@ -76,54 +72,41 @@ export function TimeDisplay({ day: '2-digit', hour: '2-digit', minute: '2-digit', - }) + }); return ( -