Files
nebula/components/drawers.templ

375 lines
14 KiB
Plaintext
Raw Permalink Normal View History

package components
// TokenOption represents a token choice in dropdowns
type TokenOption struct {
Symbol string
Name string
Balance string
Color string
Initials string
}
// DefaultTokenOptions returns the standard token options for drawers
func DefaultTokenOptions() []TokenOption {
return []TokenOption{
{Symbol: "SNR", Name: "Sonr", Balance: "8,432.50", Color: "linear-gradient(135deg, #17c2ff, #0090ff)", Initials: "S"},
{Symbol: "ETH", Name: "Ethereum", Balance: "2.847", Color: "#627eea", Initials: "E"},
{Symbol: "USDC", Name: "USD Coin", Balance: "1,250.00", Color: "#2775ca", Initials: "U"},
{Symbol: "AVAX", Name: "Avalanche", Balance: "24.83", Color: "#e84142", Initials: "A"},
}
}
// ReceiveDrawer component for receiving tokens
templ ReceiveDrawer(tokens []TokenOption, walletAddress string) {
<wa-drawer label="Receive" placement="end" class="drawer-receive" style="--size: 400px;" id="receive-drawer">
<div class="drawer-form">
<div class="drawer-form-content">
<div class="wa-stack wa-gap-l">
<wa-select label="Select Token" value="snr">
for _, token := range tokens {
<wa-option value={ token.Symbol }>
<wa-avatar slot="prefix" initials={ token.Initials } style={ "--size: 24px; background: " + token.Color + ";" }></wa-avatar>
{ token.Symbol } - { token.Name }
</wa-option>
}
</wa-select>
<div class="qr-container">
<wa-qr-code value={ walletAddress } size="180" radius="0.5"></wa-qr-code>
<span class="wa-caption-s" style="margin-top: var(--wa-space-m); color: var(--wa-color-neutral-500);">Scan to receive tokens</span>
</div>
<div class="wa-stack wa-gap-xs">
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">Your Wallet Address</span>
<div class="address-display">
<span style="flex: 1;">{ walletAddress }</span>
<wa-copy-button value={ walletAddress }></wa-copy-button>
</div>
</div>
<wa-callout variant="neutral">
<wa-icon slot="icon" name="circle-info"></wa-icon>
Only send <strong>SNR</strong> tokens to this address. Sending other assets may result in permanent loss.
</wa-callout>
</div>
</div>
<div class="wa-cluster wa-gap-s" style="justify-content: flex-end;">
<wa-button variant="neutral" appearance="outlined" onclick="document.getElementById('receive-drawer').open = false">Close</wa-button>
<wa-button variant="brand">
<wa-icon slot="start" name="share-nodes"></wa-icon>
Share Address
</wa-button>
</div>
</div>
</wa-drawer>
@drawerStyles()
}
// SendDrawer component for sending tokens
templ SendDrawer(tokens []TokenOption) {
<wa-drawer label="Send" placement="end" class="drawer-send" style="--size: 420px;" id="send-drawer">
<div class="drawer-form">
<div class="drawer-form-content">
<div class="wa-stack wa-gap-l">
<wa-select label="From Token" value="snr" id="send-token-select">
for _, token := range tokens {
<wa-option value={ token.Symbol }>
<wa-avatar slot="prefix" initials={ token.Initials } style={ "--size: 24px; background: " + token.Color + ";" }></wa-avatar>
{ token.Symbol } - { token.Balance } available
</wa-option>
}
</wa-select>
<wa-input label="Recipient Address" placeholder="sonr1... or ENS name" clearable>
<wa-icon slot="prefix" name="wallet"></wa-icon>
<wa-tooltip slot="suffix" content="Paste from clipboard">
<wa-icon-button name="paste" label="Paste"></wa-icon-button>
</wa-tooltip>
</wa-input>
<div class="wa-stack wa-gap-xs">
<wa-input label="Amount" type="number" placeholder="0.00" id="send-amount">
<span slot="suffix" style="color: var(--wa-color-neutral-500);">SNR</span>
</wa-input>
<div class="wa-flank">
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">≈ $0.00 USD</span>
<wa-button size="small" appearance="plain" style="font-size: var(--wa-font-size-xs);">MAX</wa-button>
</div>
</div>
<wa-textarea label="Memo (Optional)" placeholder="Add a note to this transaction" rows="2"></wa-textarea>
<wa-details summary="Gas Settings">
<div class="gas-settings">
<wa-radio-group label="Transaction Speed" value="standard" orientation="horizontal">
<wa-radio appearance="button" value="slow">Slow</wa-radio>
<wa-radio appearance="button" value="standard">Standard</wa-radio>
<wa-radio appearance="button" value="fast">Fast</wa-radio>
</wa-radio-group>
<div class="preview-row" style="margin-top: var(--wa-space-m);">
<span class="label">Estimated Gas</span>
<span>~0.001 SNR ($0.05)</span>
</div>
</div>
</wa-details>
<wa-divider></wa-divider>
<div class="wa-stack wa-gap-xs">
<div class="preview-row">
<span class="label">Amount</span>
<span>0.00 SNR</span>
</div>
<div class="preview-row">
<span class="label">Network Fee</span>
<span>~0.001 SNR</span>
</div>
<div class="preview-row">
<span class="label" style="font-weight: 500;">Total</span>
<span style="font-weight: 500;">0.001 SNR</span>
</div>
</div>
</div>
</div>
<div class="wa-cluster wa-gap-s" style="justify-content: flex-end;">
<wa-button variant="neutral" appearance="outlined" onclick="document.getElementById('send-drawer').open = false">Cancel</wa-button>
<wa-button variant="brand" id="send-confirm-btn">
<wa-icon slot="start" name="paper-plane"></wa-icon>
Review Send
</wa-button>
</div>
</div>
</wa-drawer>
@drawerStyles()
}
// SwapDrawer component for swapping tokens
templ SwapDrawer(tokens []TokenOption) {
<wa-drawer label="Swap Tokens" placement="end" class="drawer-swap" style="--size: 440px;" id="swap-drawer">
<div class="drawer-form">
<div class="drawer-form-content">
<div class="wa-stack wa-gap-m">
<!-- From Token Box -->
<div class="swap-token-box">
<div class="wa-flank" style="margin-bottom: var(--wa-space-s);">
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">You Pay</span>
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">Balance: 2.847 ETH</span>
</div>
<div class="wa-flank wa-gap-m">
<wa-input type="number" placeholder="0.00" style="flex: 1; --wa-input-border-color: transparent; --wa-input-background-color: transparent;" id="swap-from-amount"></wa-input>
<wa-dropdown>
<wa-button slot="trigger" appearance="outlined" size="small" with-caret>
<wa-avatar slot="prefix" initials="E" style="--size: 20px; background: #627eea;"></wa-avatar>
ETH
</wa-button>
for _, token := range tokens {
<wa-dropdown-item>
<wa-avatar slot="icon" initials={ token.Initials } style={ "--size: 20px; background: " + token.Color + ";" }></wa-avatar>
{ token.Symbol }
</wa-dropdown-item>
}
</wa-dropdown>
</div>
<div class="wa-flank" style="margin-top: var(--wa-space-xs);">
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">≈ $0.00</span>
<div class="wa-cluster wa-gap-xs">
<wa-button size="small" appearance="plain" style="font-size: var(--wa-font-size-xs);">25%</wa-button>
<wa-button size="small" appearance="plain" style="font-size: var(--wa-font-size-xs);">50%</wa-button>
<wa-button size="small" appearance="plain" style="font-size: var(--wa-font-size-xs);">MAX</wa-button>
</div>
</div>
</div>
<!-- Swap Direction Button -->
<div class="swap-arrow">
<wa-icon-button name="arrow-down" label="Swap direction" id="swap-direction-btn"></wa-icon-button>
</div>
<!-- To Token Box -->
<div class="swap-token-box">
<div class="wa-flank" style="margin-bottom: var(--wa-space-s);">
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">You Receive</span>
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500);">Balance: 1,250.00 USDC</span>
</div>
<div class="wa-flank wa-gap-m">
<wa-input type="number" placeholder="0.00" style="flex: 1; --wa-input-border-color: transparent; --wa-input-background-color: transparent;" id="swap-to-amount" readonly></wa-input>
<wa-dropdown>
<wa-button slot="trigger" appearance="outlined" size="small" with-caret>
<wa-avatar slot="prefix" initials="U" style="--size: 20px; background: #2775ca;"></wa-avatar>
USDC
</wa-button>
for _, token := range tokens {
<wa-dropdown-item>
<wa-avatar slot="icon" initials={ token.Initials } style={ "--size: 20px; background: " + token.Color + ";" }></wa-avatar>
{ token.Symbol }
</wa-dropdown-item>
}
</wa-dropdown>
</div>
<span class="wa-caption-s" style="color: var(--wa-color-neutral-500); margin-top: var(--wa-space-xs); display: block;">≈ $0.00</span>
</div>
<!-- Swap Settings -->
<wa-details summary="Swap Settings">
<div class="wa-stack wa-gap-m" style="padding-top: var(--wa-space-s);">
<div class="wa-stack wa-gap-xs">
<span class="wa-caption-s">Slippage Tolerance</span>
<wa-radio-group value="0.5" orientation="horizontal">
<wa-radio appearance="button" value="0.1">0.1%</wa-radio>
<wa-radio appearance="button" value="0.5">0.5%</wa-radio>
<wa-radio appearance="button" value="1">1%</wa-radio>
<wa-radio appearance="button" value="custom">Custom</wa-radio>
</wa-radio-group>
</div>
<wa-input label="Transaction Deadline" type="number" value="30" size="small">
<span slot="suffix">minutes</span>
</wa-input>
</div>
</wa-details>
<!-- Swap Preview -->
<wa-card style="background: var(--wa-color-surface-alt);">
<div class="wa-stack wa-gap-xs">
<div class="preview-row">
<span class="label">Rate</span>
<span>1 ETH = 2,345.00 USDC</span>
</div>
<div class="preview-row">
<span class="label">Price Impact</span>
<span style="color: var(--wa-color-success);">&lt; 0.01%</span>
</div>
<div class="preview-row">
<span class="label">Minimum Received</span>
<span>0.00 USDC</span>
</div>
<div class="preview-row">
<span class="label">Network Fee</span>
<span>~$2.50</span>
</div>
<div class="preview-row">
<span class="label">Route</span>
<span class="wa-cluster wa-gap-2xs">
ETH
<wa-icon name="arrow-right" style="font-size: 10px;"></wa-icon>
USDC
</span>
</div>
</div>
</wa-card>
<wa-callout variant="warning" style="--padding: var(--wa-space-s);">
<wa-icon slot="icon" name="triangle-alert"></wa-icon>
<span class="wa-caption-s">Price updates in real-time. Review carefully before confirming.</span>
</wa-callout>
</div>
</div>
<div class="wa-cluster wa-gap-s" style="justify-content: flex-end;">
<wa-button variant="neutral" appearance="outlined" onclick="document.getElementById('swap-drawer').open = false">Cancel</wa-button>
<wa-button variant="brand" id="swap-confirm-btn">
<wa-icon slot="start" name="arrow-right-arrow-left"></wa-icon>
Review Swap
</wa-button>
</div>
</div>
</wa-drawer>
@drawerStyles()
@swapDirectionScript()
}
// Script to handle swap direction button
script swapDirectionScript() {
document.getElementById('swap-direction-btn')?.addEventListener('click', function() {
this.style.transform = this.style.transform === 'rotate(180deg)' ? '' : 'rotate(180deg)';
});
}
// Drawer-specific styles
templ drawerStyles() {
<style>
.drawer-form {
display: flex;
flex-direction: column;
gap: var(--wa-space-l);
height: 100%;
}
.drawer-form-content {
flex: 1;
overflow-y: auto;
}
.qr-container {
display: flex;
flex-direction: column;
align-items: center;
padding: var(--wa-space-xl);
background: var(--wa-color-surface-alt);
border-radius: var(--wa-radius-l);
}
.address-display {
display: flex;
align-items: center;
gap: var(--wa-space-s);
padding: var(--wa-space-m);
background: var(--wa-color-surface-alt);
border-radius: var(--wa-radius-m);
font-family: var(--wa-font-mono);
font-size: var(--wa-font-size-s);
word-break: break-all;
}
.swap-token-box {
padding: var(--wa-space-m);
background: var(--wa-color-surface-alt);
border-radius: var(--wa-radius-m);
border: 1px solid var(--wa-color-neutral-200);
}
.swap-arrow {
display: flex;
justify-content: center;
margin: calc(var(--wa-space-s) * -1) 0;
position: relative;
z-index: 1;
}
.swap-arrow wa-icon-button {
background: var(--wa-color-surface);
border: 1px solid var(--wa-color-neutral-200);
border-radius: 50%;
transition: transform 0.2s;
}
.gas-settings {
padding: var(--wa-space-m);
background: var(--wa-color-surface-alt);
border-radius: var(--wa-radius-m);
}
.preview-row {
display: flex;
justify-content: space-between;
padding: var(--wa-space-xs) 0;
font-size: var(--wa-font-size-s);
}
.preview-row .label {
color: var(--wa-color-neutral-500);
}
</style>
}
// AllDrawers renders all three drawers together for convenience
templ AllDrawers(tokens []TokenOption, walletAddress string) {
@ReceiveDrawer(tokens, walletAddress)
@SendDrawer(tokens)
@SwapDrawer(tokens)
}
// DrawerTriggerScript provides the JavaScript to wire up drawer triggers
script drawerTriggerScript() {
// Open receive drawer
document.querySelectorAll('[data-drawer-open="receive"]').forEach(el => {
el.addEventListener('click', () => {
document.getElementById('receive-drawer').open = true;
});
});
// Open send drawer
document.querySelectorAll('[data-drawer-open="send"]').forEach(el => {
el.addEventListener('click', () => {
document.getElementById('send-drawer').open = true;
});
});
// Open swap drawer
document.querySelectorAll('[data-drawer-open="swap"]').forEach(el => {
el.addEventListener('click', () => {
document.getElementById('swap-drawer').open = true;
});
});
}
// DrawerTriggers component to initialize drawer triggers
templ DrawerTriggers() {
@drawerTriggerScript()
}