Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 83 additions & 29 deletions apps/web/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,22 @@
'use client';

import { useEffect } from 'react';
import { TransactionHeartbeat, useTransaction } from '@bridgewise/ui-components';
import {
BridgeWiseProvider,
TransactionHeartbeat,
useTransaction,
BridgeStatus,
} from '@bridgewise/ui-components';

const customTheme = {
primaryColor: '#22c55e',
secondaryColor: '#0f172a',
backgroundColor: '#020617',
textColor: '#e5e7eb',
borderRadius: '16px',
fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
spacingUnit: '0.9rem',
};

function TransactionDemo() {
const { state, updateState, startTransaction, clearState } = useTransaction();
Expand Down Expand Up @@ -32,34 +47,73 @@ function TransactionDemo() {
}, [state, updateState]);

return (
<div className="flex min-h-screen flex-col items-center justify-center p-24 bg-zinc-50 dark:bg-zinc-900">
<main className="flex flex-col items-center gap-8">
<h1 className="text-4xl font-bold text-center text-zinc-900 dark:text-zinc-100">
BridgeWise Transaction Heartbeat Demo
</h1>

<p className="max-w-md text-center text-zinc-600 dark:text-zinc-400">
Click "Start Transaction" to simulate a bridge transfer. Then refresh the page to verify that the state persists and the heartbeat component reappears.
</p>

<div className="flex gap-4">
<button
onClick={() => startTransaction('tx-' + Date.now())}
className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition active:scale-95"
>
Start Transaction
</button>
<button
onClick={clearState}
className="px-6 py-3 border border-zinc-300 dark:border-zinc-700 text-zinc-700 dark:text-zinc-300 rounded-lg hover:bg-zinc-100 dark:hover:bg-zinc-800 transition active:scale-95"
>
Clear State
</button>
</div>

<TransactionHeartbeat />
</main>
</div>
<BridgeWiseProvider theme={customTheme} defaultMode="dark">
<div className="flex min-h-screen flex-col items-center justify-center gap-12 p-10 bg-zinc-50 dark:bg-black">
<main className="flex flex-col items-center gap-8 max-w-3xl">
<h1 className="text-4xl font-bold text-center text-zinc-900 dark:text-zinc-100">
BridgeWise Theming Demo
</h1>

<p className="max-w-xl text-center text-zinc-600 dark:text-zinc-400">
This page demonstrates the BridgeWise theme system. The heartbeat and status components
are styled via CSS variables injected by <code>BridgeWiseProvider</code>, with a custom
primary color and dark background.
</p>

<div className="flex flex-wrap items-center justify-center gap-4">
<button
onClick={() => startTransaction('tx-' + Date.now())}
className="px-6 py-3 rounded-lg text-sm font-medium text-white bg-emerald-500 hover:bg-emerald-600 active:scale-95 transition"
>
Start Transaction
</button>
<button
onClick={clearState}
className="px-6 py-3 rounded-lg text-sm font-medium border border-zinc-300 dark:border-zinc-700 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-zinc-800 active:scale-95 transition"
>
Clear State
</button>
</div>

<section className="grid w-full gap-6 md:grid-cols-2">
<div className="rounded-2xl border border-zinc-200/60 dark:border-zinc-800/80 bg-white/60 dark:bg-zinc-900/60 p-4 shadow-sm">
<h2 className="text-lg font-semibold mb-2 text-zinc-900 dark:text-zinc-50">
Inline BridgeStatus
</h2>
<p className="text-sm text-zinc-600 dark:text-zinc-400 mb-4">
An inline status card using the same theme variables as the floating heartbeat.
</p>
<BridgeStatus
txHash={state.txHash || '0x0000000000000000000000000000000000000000'}
bridgeName="demo"
sourceChain="ethereum"
destinationChain="polygon"
amount={123.45}
token="USDC"
slippagePercent={0.5}
estimatedTimeSeconds={300}
detailed
/>
</div>

<div className="rounded-2xl border border-zinc-200/60 dark:border-zinc-800/80 bg-white/60 dark:bg-zinc-900/60 p-4 shadow-sm">
<h2 className="text-lg font-semibold mb-2 text-zinc-900 dark:text-zinc-50">
Component-level Overrides
</h2>
<p className="text-sm text-zinc-600 dark:text-zinc-400 mb-4">
The floating heartbeat below uses a custom <code>className</code> to adjust its
position while still inheriting all theme variables.
</p>
<p className="text-xs text-zinc-500 dark:text-zinc-500">
Trigger a transaction and you&apos;ll see the heartbeat appear in the bottom-left corner.
</p>
</div>
</section>

<TransactionHeartbeat className="left-4 right-auto" />
</main>
</div>
</BridgeWiseProvider>
);
}

Expand Down
100 changes: 100 additions & 0 deletions libs/ui-components/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,106 @@

BridgeWise UI SDK components and hooks for cross-chain UX.

## Theming Guide

BridgeWise exposes a flexible, type-safe theming system that can be used globally or per app.

### BridgeWiseTheme

For simple integrations, you can use the high-level `BridgeWiseTheme` interface:

```ts
import type { BridgeWiseTheme } from '@bridgewise/ui-components';

const theme: BridgeWiseTheme = {
primaryColor: '#22c55e',
secondaryColor: '#0f172a',
backgroundColor: '#020617',
textColor: '#e5e7eb',
borderRadius: '16px',
fontFamily: 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
spacingUnit: '0.9rem',
};
```

### BridgeWiseProvider

Wrap your dApp (or specific sections) with `BridgeWiseProvider` to inject theme tokens and CSS variables:

```tsx
import {
BridgeWiseProvider,
TransactionHeartbeat,
BridgeStatus,
} from '@bridgewise/ui-components';

const customTheme = {
primaryColor: '#22c55e',
backgroundColor: '#020617',
textColor: '#e5e7eb',
};

export function App() {
return (
<BridgeWiseProvider theme={customTheme} defaultMode="dark">
{/* Your app + BridgeWise components */}
<BridgeStatus /* ...props */ />
<TransactionHeartbeat />
</BridgeWiseProvider>
);
}
```

Under the hood this is mapped into the full token-based `Theme` object and converted into CSS variables with the `--bw-` prefix, e.g.:

- `--bw-colors-transaction-background`
- `--bw-colors-foreground-primary`
- `--bw-spacing-md`
- `--bw-typography-font-size-sm`

You can also pass a full `DeepPartial<Theme>` instead of `BridgeWiseTheme` for complete control.

### Dark Mode

The theme system supports light/dark mode with a `ThemeMode`:

- `'light'`
- `'dark'`
- `'system'` (default)

`BridgeWiseProvider` forwards `defaultMode`, `enableSystem`, and related props to the underlying `ThemeProvider`. Dark mode uses the built-in `darkTheme` token overrides.

### Component-level overrides

All public UI components support `className` and/or `style` overrides to match your design system:

- `TransactionHeartbeat` – `className`, `style`
- `BridgeStatus` – `className`, `style`
- `BridgeHistory` – `className`, `style`
- `BridgeCompare` – `className`, `style`

Example:

```tsx
<TransactionHeartbeat className="left-4 right-auto" />
<BridgeCompare className="rounded-xl border border-slate-800" />
```

### CSS variable mapping

If you need to integrate with Tailwind or other CSS-in-JS systems, you can rely on the generated CSS variables:

```ts
import { generateCSSVariables, defaultTheme } from '@bridgewise/ui-components';

const cssVars = generateCSSVariables(defaultTheme);
// cssVars['--bw-colors-transaction-background'] => '#ffffff'
```

These variables are applied at the `document.documentElement` level by `ThemeProvider`, and are safe to use in custom styles as `var(--bw-...)`.

---

## Transaction History

The transaction history module provides a unified view across Stellar and EVM bridge executions.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ interface BridgeCompareProps {
showBenchmarkComparison?: boolean;
minLiquidityThreshold?: number;
onRouteSelect?: (route: BridgeRoute) => void;
className?: string;
style?: React.CSSProperties;
}

// Define the types locally to avoid import issues
Expand All @@ -28,7 +30,9 @@ const BridgeCompare: React.FC<BridgeCompareProps> = ({
destinationChain,
showBenchmarkComparison = true,
minLiquidityThreshold = 0,
onRouteSelect
onRouteSelect,
className,
style,
}: BridgeCompareProps) => {
// Get benchmark data for comparison
const {
Expand Down Expand Up @@ -78,7 +82,10 @@ const BridgeCompare: React.FC<BridgeCompareProps> = ({
};

return (
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-md p-4">
<div
className={`bg-white dark:bg-gray-800 rounded-lg shadow-md p-4${className ? ` ${className}` : ''}`}
style={style}
>
<div className="mb-4">
<h2 className="text-xl font-bold text-gray-900 dark:text-white">Bridge Comparison</h2>
<p className="text-gray-600 dark:text-gray-300">
Expand Down
44 changes: 23 additions & 21 deletions libs/ui-components/src/components/BridgeHistory/BridgeHistory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export interface BridgeHistoryProps {
includeBackend?: boolean;
historyConfig?: TransactionHistoryConfig;
emptyStateMessage?: string;
className?: string;
style?: React.CSSProperties;
}

export const BridgeHistory: React.FC<BridgeHistoryProps> = ({
Expand All @@ -32,6 +34,8 @@ export const BridgeHistory: React.FC<BridgeHistoryProps> = ({
includeBackend = false,
historyConfig,
emptyStateMessage = 'No transactions found for this account.',
className,
style,
}) => {
const filter: TransactionHistoryFilter = {
chain,
Expand All @@ -51,30 +55,28 @@ export const BridgeHistory: React.FC<BridgeHistoryProps> = ({
historyConfig,
);

if (!account) {
return <p>Connect a wallet to view transaction history.</p>;
}
return (
<div className={className} style={style}>
{!account && <p>Connect a wallet to view transaction history.</p>}

if (loading) {
return <p>Loading transaction history...</p>;
}
{account && loading && <p>Loading transaction history...</p>}

if (transactions.length === 0) {
return <p>{emptyStateMessage}</p>;
}
{account && !loading && transactions.length === 0 && <p>{emptyStateMessage}</p>}

return (
<div>
<h3>Bridge History</h3>
<ul>
{transactions.map((transaction) => (
<li key={`${transaction.account}:${transaction.txHash}`}>
<strong>{transaction.bridgeName}</strong> • {transaction.sourceChain} →{' '}
{transaction.destinationChain} • {transaction.amount} {transaction.sourceToken} •{' '}
{transaction.status} • {transaction.timestamp.toLocaleString()}
</li>
))}
</ul>
{account && !loading && transactions.length > 0 && (
<>
<h3>Bridge History</h3>
<ul>
{transactions.map((transaction) => (
<li key={`${transaction.account}:${transaction.txHash}`}>
<strong>{transaction.bridgeName}</strong> • {transaction.sourceChain} →{' '}
{transaction.destinationChain} • {transaction.amount} {transaction.sourceToken} •{' '}
{transaction.status} • {transaction.timestamp.toLocaleString()}
</li>
))}
</ul>
</>
)}
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
import React from 'react';
import { TransactionHeartbeatHeadless } from './TransactionHeartbeat.headless';

export interface TransactionHeartbeatProps {
className?: string;
style?: React.CSSProperties;
}

/**
* Transaction heartbeat notification component
* Displays transaction progress with themed styling
Expand All @@ -27,19 +32,23 @@ import { TransactionHeartbeatHeadless } from './TransactionHeartbeat.headless';
* }
* ```
*/
export const TransactionHeartbeat: React.FC = () => {
export const TransactionHeartbeat: React.FC<TransactionHeartbeatProps> = ({
className,
style,
}) => {
return (
<TransactionHeartbeatHeadless>
{({ state, clearState, isSuccess, isFailed, isPending }) => (
<div
className="fixed bottom-4 right-4 z-50 w-80 overflow-hidden font-sans"
className={`fixed bottom-4 right-4 z-50 w-80 overflow-hidden font-sans${className ? ` ${className}` : ''}`}
style={{
backgroundColor: 'var(--bw-colors-transaction-background)',
borderRadius: 'var(--bw-radii-lg)',
boxShadow: 'var(--bw-shadows-xl)',
border: `1px solid var(--bw-colors-transaction-border)`,
transition: `all var(--bw-transitions-base)`,
fontFamily: 'var(--bw-typography-font-family-sans)',
...style,
}}
>
<div style={{ padding: 'var(--bw-spacing-md)' }}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ export type {
TransactionHeartbeatHeadlessProps,
TransactionHeartbeatRenderProps,
} from './TransactionHeartbeat.headless';
export type { TransactionHeartbeatProps } from './TransactionHeartbeat';
2 changes: 2 additions & 0 deletions libs/ui-components/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
export {
ThemeProvider,
useTheme,
BridgeWiseProvider,
ThemeScript,
defaultTheme,
darkTheme,
Expand All @@ -27,6 +28,7 @@ export type {
ThemeContextValue,
DeepPartial,
ThemeConfig,
BridgeWiseTheme,
CSSVariables,
} from './theme';

Expand Down
Loading
Loading