-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
42 changed files
with
2,247 additions
and
38 deletions.
There are no files selected for viewing
83 changes: 83 additions & 0 deletions
83
app/src/lib/components/TransferFrom/components/ChainDialog.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
<script lang="ts"> | ||
import type { Readable } from "svelte/store" | ||
import type { ContextStore } from "$lib/components/TransferFrom/transfer/context.ts" | ||
interface Props { | ||
context: Readable<ContextStore> | ||
kind: "source" | "destination" | ||
dialogOpen: boolean | ||
onChainSelect: (type: "source" | "destination", chain: string) => void | ||
onClose: () => void | ||
} | ||
export let context: Props["context"] | ||
export let kind: Props["kind"] | ||
export let dialogOpen: Props["dialogOpen"] | ||
export let onChainSelect: Props["onChainSelect"] | ||
export let onClose: Props["onClose"] | ||
</script> | ||
|
||
{#if dialogOpen && $context?.chains} | ||
<dialog | ||
open | ||
aria-label={`Select ${kind} chain`} | ||
class="absolute z-50 inset-0 overflow-y-scroll p-0 bg-transparent m-0 w-full h-full animate-fade-in backdrop-blur-md" | ||
> | ||
<button | ||
type="button" | ||
class="fixed inset-0 w-full h-full bg-gradient-to-t from-black to-black/10 animate-fade-in" | ||
on:click|self={onClose} | ||
aria-label="Close dialog" | ||
/> | ||
|
||
<div class="relative z-10"> | ||
<div class="flex justify-center"> | ||
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4 md:gap-8 max-w-4xl py-8 px-4"> | ||
{#each $context.chains as chain, i} | ||
<button | ||
style="animation-delay: {i * 100}ms" | ||
on:click={() => onChainSelect(kind, chain.chain_id)} | ||
type="button" | ||
class="h-72 flex items-end border p-4 bg-secondary group hover:bg-accent transition-colors animate-slide-up" | ||
> | ||
<span class="font-supermolot uppercase font-bold text-xl text-start text-secondary-foreground group-hover:text-secondary"> | ||
{chain.display_name} | ||
</span> | ||
</button> | ||
{/each} | ||
</div> | ||
</div> | ||
</div> | ||
</dialog> | ||
{/if} | ||
|
||
<style> | ||
@keyframes fade-in { | ||
from { | ||
opacity: 0; | ||
} | ||
to { | ||
opacity: 1; | ||
} | ||
} | ||
@keyframes slide-up { | ||
from { | ||
transform: translateY(30px); | ||
opacity: 0; | ||
} | ||
to { | ||
transform: translateY(0); | ||
opacity: 1; | ||
} | ||
} | ||
:global(.animate-fade-in) { | ||
animation: fade-in 0.3s ease-out forwards; | ||
} | ||
:global(.animate-slide-up) { | ||
animation: slide-up 0.4s ease-out forwards; | ||
opacity: 0; | ||
} | ||
</style> |
17 changes: 17 additions & 0 deletions
17
app/src/lib/components/TransferFrom/components/Cube/FaceWrapper.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<script> | ||
export let visible = true | ||
export let width = 0 // Pixel value | ||
export let height = 0 // Pixel value | ||
export let translateZ = 0 | ||
export let rotateY = "0deg" | ||
</script> | ||
|
||
<div | ||
class="absolute bg-muted flex flex-col items-center border-2" | ||
class:opacity-100={visible} | ||
class:opacity-0={!visible} | ||
class:pointer-events-none={!visible} | ||
style={`width: ${width}px; height: ${height}px; transform: rotateY(${rotateY}) translateZ(${translateZ}px);`} | ||
> | ||
<slot /> | ||
</div> |
72 changes: 72 additions & 0 deletions
72
app/src/lib/components/TransferFrom/components/Cube/faces/Assets.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
<script lang="ts"> | ||
import type { Readable } from "svelte/store" | ||
import type { ContextStore } from "$lib/components/TransferFrom/transfer/context.ts" | ||
import { truncate } from "$lib/utilities/format.ts" | ||
import { formatUnits } from "viem" | ||
import { Button } from "$lib/components/ui/button" | ||
import type { CubeFaces } from "$lib/components/TransferFrom/components/Cube/types.ts" | ||
import type { RawIntentsStore } from "$lib/components/TransferFrom/transfer/raw-intents.ts" | ||
import type { IntentsStore } from "$lib/components/TransferFrom/transfer/intents.ts" | ||
interface Props { | ||
stores: { | ||
rawIntents: RawIntentsStore | ||
intents: Readable<IntentsStore> | ||
context: Readable<ContextStore> | ||
} | ||
rotateTo: (face: CubeFaces) => void | ||
} | ||
export let stores: Props["stores"] | ||
export let rotateTo: Props["rotateTo"] | ||
let { rawIntents, context, intents } = stores | ||
$: sortedAssets = [...($intents.sourceAssets ?? [])].sort((a, b) => { | ||
if (a.isSupported !== b.isSupported) { | ||
return a.isSupported ? -1 : 1 | ||
} | ||
return Number(b.balance.balance - a.balance.balance) | ||
}) | ||
function setAsset(address: string) { | ||
rawIntents.updateField("asset", address) | ||
rotateTo("intentFace") | ||
} | ||
</script> | ||
|
||
<div class="flex flex-col h-full w-full"> | ||
<div class="text-primary p-2 px-4 flex items-center justify-between border-b-2"> | ||
<span class="font-bold uppercase">Assets</span> | ||
<button | ||
class="border-2 h-6 w-6 flex items-center justify-center" | ||
on:click={() => rotateTo("intentFace")} | ||
>✕ | ||
</button> | ||
</div> | ||
|
||
{#if sortedAssets.length} | ||
<div class="flex-1 overflow-y-auto"> | ||
{#each sortedAssets as asset} | ||
<div class="pb-2 flex flex-col justify-start"> | ||
<Button | ||
variant="ghost" | ||
class="px-4 py-2 w-full rounded-none flex justify-between items-center" | ||
on:click={() => setAsset(asset.balance.address)} | ||
> | ||
<div class:opacity-30={!asset.isSupported}> | ||
{truncate(asset.symbol, 6)} | ||
</div> | ||
<p class:opacity-30={!asset.isSupported}> | ||
{formatUnits(asset.balance.balance, asset.supportedAsset?.decimals ?? 0)} | ||
</p> | ||
</Button> | ||
</div> | ||
{/each} | ||
</div> | ||
{:else} | ||
<div class="px-4 p-2"> | ||
<p>No spendable balances</p> | ||
</div> | ||
{/if} | ||
</div> |
104 changes: 104 additions & 0 deletions
104
app/src/lib/components/TransferFrom/components/Cube/faces/Chains.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
<!-- ChainSelector.svelte --> | ||
<script lang="ts"> | ||
import type { Readable } from "svelte/store" | ||
import type { ContextStore } from "$lib/components/TransferFrom/transfer/context.ts" | ||
import type { RawIntentsStore } from "$lib/components/TransferFrom/transfer/raw-intents.ts" | ||
import type { CubeFaces } from "$lib/components/TransferFrom/components/Cube/types.ts" | ||
import { Button } from "$lib/components/ui/button" | ||
import { TRANSFER_DEBUG } from "$lib/components/TransferFrom/transfer/config.ts" | ||
interface Props { | ||
stores: { | ||
rawIntents: RawIntentsStore | ||
context: Readable<ContextStore> | ||
} | ||
rotateTo: (face: CubeFaces) => void | ||
selected: "source" | "destination" | ||
} | ||
export let stores: Props["stores"] | ||
export let rotateTo: Props["rotateTo"] | ||
export let selected: Props["selected"] | ||
let expandedChainId: string | null = null | ||
let { rawIntents, context } = stores | ||
function setChain(selected: "source" | "destination", chainId: string) { | ||
rawIntents.updateField(selected, chainId) | ||
rotateTo("intentFace") | ||
} | ||
function toggleExpand(chainId: string) { | ||
expandedChainId = expandedChainId === chainId ? null : chainId | ||
} | ||
</script> | ||
|
||
<div class="flex flex-col h-full w-full"> | ||
<!-- Title Bar --> | ||
<div class="text-primary p-2 flex items-center justify-between border-b-2"> | ||
<span class="font-bold uppercase">{selected} chain</span> | ||
<button | ||
class="border-2 h-6 w-6 flex items-center justify-center" | ||
on:click={() => rotateTo("intentFace")} | ||
>✕ | ||
</button> | ||
</div> | ||
|
||
<!-- Chain List --> | ||
<div class="flex flex-col h-full overflow-y-scroll"> | ||
<div class="p-2 space-y-2 h-full"> | ||
{#each $context.chains as chain} | ||
<div> | ||
<Button | ||
variant="ghost" | ||
class="px-4 py-2 w-full rounded-none flex justify-between items-center" | ||
on:click={() => setChain(selected, chain.chain_id)} | ||
> | ||
<div class="flex items-center gap-2"> | ||
<span>{chain.display_name}</span> | ||
</div> | ||
<button | ||
class="border-2 border-black h-8 w-8 hover:bg-gray-200 active:border-gray-400" | ||
on:click|stopPropagation={() => toggleExpand(chain.chain_id)} | ||
> | ||
<span>i</span> | ||
</button> | ||
</Button> | ||
|
||
<!-- Expanded Info Panel --> | ||
{#if expandedChainId === chain.chain_id} | ||
<div class=""> | ||
<div class="grid grid-cols-2 gap-2 text-sm"> | ||
<div class="border-2 border-black p-2"> | ||
<h4 class="font-bold mb-1">Network Info</h4> | ||
<p>Chain ID: {chain.chain_id}</p> | ||
<p>Type: {chain.rpc_type}</p> | ||
<p>Prefix: {chain.addr_prefix}</p> | ||
</div> | ||
{#if !TRANSFER_DEBUG} | ||
<div class="border-2 border-black p-2"> | ||
<h4 class="font-bold mb-1">Status</h4> | ||
<p>Enabled: {chain.enabled ? '✓' : '✗'}</p> | ||
<p>Staging: {chain.enabled_staging ? '✓' : '✗'}</p> | ||
</div> | ||
{/if} | ||
{#if chain.explorers?.length} | ||
<div class="col-span-2 border-2 border-black p-2"> | ||
<h4 class="font-bold mb-1">Explorers</h4> | ||
{#each chain.explorers as explorer} | ||
<a href={explorer.tx_url} class="text-xs truncate"> | ||
{explorer.tx_url.split('/')[2]} | ||
</a> | ||
{/each} | ||
</div> | ||
{/if} | ||
</div> | ||
</div> | ||
{/if} | ||
</div> | ||
{/each} | ||
</div> | ||
</div> | ||
|
||
</div> |
90 changes: 90 additions & 0 deletions
90
app/src/lib/components/TransferFrom/components/Cube/faces/Intent.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
<script lang="ts"> | ||
import Direction from "$lib/components/TransferFrom/components/Direction.svelte" | ||
import SelectedAsset from "$lib/components/TransferFrom/components/SelectedAsset.svelte" | ||
import type { Readable } from "svelte/store" | ||
import type { ValidationStoreAndMethods } from "$lib/components/TransferFrom/transfer/validation.ts" | ||
import type { ContextStore } from "$lib/components/TransferFrom/transfer/context.ts" | ||
import { Button } from "$lib/components/ui/button" | ||
import type { IntentsStore } from "$lib/components/TransferFrom/transfer/intents.ts" | ||
import type { CubeFaces } from "$lib/components/TransferFrom/components/Cube/types.ts" | ||
import type { RawIntentsStore } from "$lib/components/TransferFrom/transfer/raw-intents.ts" | ||
import { Input } from "$lib/components/ui/input" | ||
interface Props { | ||
stores: { | ||
rawIntents: RawIntentsStore | ||
intents: Readable<IntentsStore> | ||
context: Readable<ContextStore> | ||
validation: ValidationStoreAndMethods | ||
} | ||
rotateTo: (face: CubeFaces) => void | ||
} | ||
export let stores: Props["stores"] | ||
export let rotateTo: Props["rotateTo"] | ||
let { rawIntents, intents, validation, context } = stores | ||
</script> | ||
|
||
<div class="flex flex-col w-full h-full "> | ||
|
||
<div class="text-primary p-2 flex items-center justify-between border-b-2"> | ||
<span class="font-bold uppercase">Transfer</span> | ||
</div> | ||
<div class="flex flex-col h-full w-full justify-between p-4"> | ||
<div class="flex flex-col gap-4"> | ||
<Direction {intents} {validation} getSourceChain={() => rotateTo("sourceFace")} getDestinationChain={() => rotateTo("destinationFace")}/> | ||
<SelectedAsset {intents} onSelectAsset={() => rotateTo("assetsFace")}/> | ||
<div class="flex flex-col gap-1"> | ||
<Input | ||
id="amount" | ||
type="number" | ||
name="amount" | ||
minlength={1} | ||
maxlength={64} | ||
required={true} | ||
disabled={!$intents.selectedAsset.address} | ||
autocorrect="off" | ||
placeholder="0.00" | ||
spellcheck="false" | ||
autocomplete="off" | ||
inputmode="decimal" | ||
data-field="amount" | ||
autocapitalize="none" | ||
pattern="^[0-9]*[.,]?[0-9]*$" | ||
class="p-1 {$validation.errors.amount ? 'border-red-500' : ''}" | ||
value={$intents.amount} | ||
on:input={event => rawIntents.updateField('amount', event)} | ||
/> | ||
{#if $validation.errors.amount} | ||
<span class="text-red-500 text-sm">{$validation.errors.amount}</span> | ||
{/if} | ||
</div> | ||
|
||
<div class="flex flex-col gap-1"> | ||
<Input | ||
type="text" | ||
id="receiver" | ||
name="receiver" | ||
required={true} | ||
disabled={!$intents.destinationChain} | ||
autocorrect="off" | ||
spellcheck="false" | ||
autocomplete="off" | ||
data-field="receiver" | ||
class="p-1 disabled:bg-black/30 {$validation.errors.receiver ? 'border-red-500' : ''}" | ||
placeholder="Enter destination address" | ||
value={$intents.receiver} | ||
on:input={event => rawIntents.updateField('receiver', event)} | ||
/> | ||
{#if $validation.errors.receiver} | ||
<span class="text-red-500 text-sm">{$validation.errors.receiver}</span> | ||
{/if} | ||
</div> | ||
</div> | ||
<Button | ||
disabled={!$validation.isValid} | ||
on:click={() => rotateTo("verifyFace")}>Transfer | ||
</Button> | ||
</div> | ||
</div> |
Oops, something went wrong.