diff --git a/src/components/AddressDisplay.css b/src/components/AddressDisplay.css new file mode 100644 index 0000000..995d055 --- /dev/null +++ b/src/components/AddressDisplay.css @@ -0,0 +1,44 @@ +.address-display { + display: inline-flex; + align-items: center; + gap: var(--credence-space-2); +} + +.address-display__address { + font-family: var(--credence-font-mono); + font-size: var(--credence-font-size-sm); + padding: var(--credence-space-1) var(--credence-space-2); + border-radius: var(--credence-radius-sm); + background-color: var(--credence-color-surface-hover); + color: var(--credence-text-primary); + transition: background-color 0.2s ease; + outline: none; +} + +.address-display__address:hover, +.address-display__address:focus { + background-color: var(--credence-color-surface-active); +} + +.address-display__copy-btn { + display: inline-flex; + align-items: center; + justify-content: center; + padding: var(--credence-space-1); + border: none; + border-radius: var(--credence-radius-sm); + background: transparent; + color: var(--credence-text-secondary); + cursor: pointer; + transition: all 0.2s ease; +} + +.address-display__copy-btn:hover { + background-color: var(--credence-color-surface-hover); + color: var(--credence-text-primary); +} + +.address-display__copy-btn:focus-visible { + outline: 2px solid var(--credence-color-primary); + outline-offset: 2px; +} diff --git a/src/components/AddressDisplay.tsx b/src/components/AddressDisplay.tsx new file mode 100644 index 0000000..7f41ab5 --- /dev/null +++ b/src/components/AddressDisplay.tsx @@ -0,0 +1,66 @@ +import { useState } from 'react' +import useCopyToClipboard from '../hooks/useCopyToClipboard' +import { useToast } from './ToastProvider' +import { truncateAddress } from '../lib/stellar' +import './AddressDisplay.css' + +export interface AddressDisplayProps { + address: string + className?: string + showCopyButton?: boolean +} + +export default function AddressDisplay({ + address, + className = '', + showCopyButton = true, +}: AddressDisplayProps) { + const { copy, copied } = useCopyToClipboard() + const { addToast } = useToast() + const [isHovered, setIsHovered] = useState(false) + const [isFocused, setIsFocused] = useState(false) + + const handleCopy = async () => { + const success = await copy(address) + if (success) { + addToast('success', 'Address copied to clipboard') + } + } + + const displayAddress = (isHovered || isFocused) ? address : truncateAddress(address) + + return ( +
+ setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)} + onFocus={() => setIsFocused(true)} + onBlur={() => setIsFocused(false)} + tabIndex={0} + > + {displayAddress} + + {showCopyButton && ( + + )} +
+ ) +} diff --git a/src/pages/Dashboard.tsx b/src/pages/Dashboard.tsx index 9287522..4cc9b9f 100644 --- a/src/pages/Dashboard.tsx +++ b/src/pages/Dashboard.tsx @@ -5,6 +5,7 @@ import Badge from '../components/Badge' import Banner from '../components/Banner' import Button from '../components/Button' import TrustGauge from '../components/TrustGauge' +import AddressDisplay from '../components/AddressDisplay' import { EmptyState, LoadingSkeleton } from '../components/states' import { useWallet } from '../context/WalletContext' import { useSeo } from '../hooks/useSeo' diff --git a/src/pages/Transactions.tsx b/src/pages/Transactions.tsx index f566f66..a89bf18 100644 --- a/src/pages/Transactions.tsx +++ b/src/pages/Transactions.tsx @@ -2,13 +2,13 @@ import { useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import './Transactions.css' import Badge from '../components/Badge' +import AddressDisplay from '../components/AddressDisplay' import Select from '../components/controls/Select' import { EmptyState, ErrorState, LoadingSkeleton } from '../components/states' import { useSettings } from '../context/SettingsContext' import { useDocumentTitle } from '../hooks/useDocumentTitle' import { useTransactions } from '../hooks/useTransactions' import { explorerUrl } from '../lib/explorerUrl' -import { truncateAddress } from '../lib/stellar' import type { Transaction } from '../api/types' type StatusFilter = 'all' | 'pending' | 'confirmed' | 'failed' @@ -135,9 +135,7 @@ export default function Transactions() { - - {truncateAddress(tx.hash)} - {' '} + {' '}