feat(templ): add charts and drawers components
This commit is contained in:
703
components/charts.templ
Normal file
703
components/charts.templ
Normal file
@@ -0,0 +1,703 @@
|
|||||||
|
package components
|
||||||
|
|
||||||
|
import "encoding/json"
|
||||||
|
|
||||||
|
// OHLCData represents a single candlestick data point
|
||||||
|
type OHLCData struct {
|
||||||
|
Date string `json:"date"`
|
||||||
|
Open float64 `json:"open"`
|
||||||
|
High float64 `json:"high"`
|
||||||
|
Low float64 `json:"low"`
|
||||||
|
Close float64 `json:"close"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AreaSeriesData represents time series data for stacked area chart
|
||||||
|
type AreaSeriesData struct {
|
||||||
|
Date string `json:"date"`
|
||||||
|
Industry string `json:"industry"`
|
||||||
|
Value float64 `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BubbleData represents data for bubble/circle packing chart
|
||||||
|
type BubbleData struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Sector string `json:"sector"`
|
||||||
|
Value float64 `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimeSeriesData represents simple time series data
|
||||||
|
type TimeSeriesData struct {
|
||||||
|
Date string `json:"date"`
|
||||||
|
Value float64 `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to convert data to JSON string for JS
|
||||||
|
func toJSON(v interface{}) string {
|
||||||
|
b, _ := json.Marshal(v)
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RosenCharts global utilities and chart classes
|
||||||
|
script initRosenCharts() {
|
||||||
|
window.RosenCharts = {
|
||||||
|
colors: {
|
||||||
|
success: 'var(--wa-color-success)',
|
||||||
|
danger: 'var(--wa-color-danger)',
|
||||||
|
primary: 'var(--wa-color-primary)',
|
||||||
|
neutral400: 'var(--wa-color-neutral-400)',
|
||||||
|
neutral500: 'var(--wa-color-neutral-500)',
|
||||||
|
neutral600: 'var(--wa-color-neutral-600)',
|
||||||
|
surfaceAlt: 'var(--wa-color-surface-alt)',
|
||||||
|
surface: 'var(--wa-color-surface)',
|
||||||
|
fuchsia: { from: '#f0abfc', to: '#e879f9' },
|
||||||
|
purple: { from: '#c4b5fd', to: '#a855f7' },
|
||||||
|
blue: { from: '#93c5fd', to: '#3b82f6' },
|
||||||
|
sky: { from: '#bae6fd', to: '#38bdf8' },
|
||||||
|
orange: { from: '#fed7aa', to: '#fb923c' },
|
||||||
|
yellow: { from: '#fef08a', to: '#facc15' },
|
||||||
|
emerald: { from: '#6ee7b7', to: '#10b981' },
|
||||||
|
red: { from: '#fca5a5', to: '#ef4444' }
|
||||||
|
},
|
||||||
|
formatNumber(value, decimals = 2) {
|
||||||
|
return new Intl.NumberFormat('en-US', {
|
||||||
|
minimumFractionDigits: decimals,
|
||||||
|
maximumFractionDigits: decimals
|
||||||
|
}).format(value);
|
||||||
|
},
|
||||||
|
formatCurrency(value) {
|
||||||
|
return new Intl.NumberFormat('en-US', {
|
||||||
|
style: 'currency',
|
||||||
|
currency: 'USD'
|
||||||
|
}).format(value);
|
||||||
|
},
|
||||||
|
isDesktop() {
|
||||||
|
return window.innerWidth > 1024;
|
||||||
|
},
|
||||||
|
createSVG(tag, attrs = {}) {
|
||||||
|
const el = document.createElementNS('http://www.w3.org/2000/svg', tag);
|
||||||
|
Object.entries(attrs).forEach(([key, value]) => {
|
||||||
|
el.setAttribute(key, value);
|
||||||
|
});
|
||||||
|
return el;
|
||||||
|
},
|
||||||
|
createElement(tag, attrs = {}, children = []) {
|
||||||
|
const el = document.createElement(tag);
|
||||||
|
Object.entries(attrs).forEach(([key, value]) => {
|
||||||
|
if (key === 'style' && typeof value === 'object') {
|
||||||
|
Object.assign(el.style, value);
|
||||||
|
} else if (key === 'className') {
|
||||||
|
el.className = value;
|
||||||
|
} else {
|
||||||
|
el.setAttribute(key, value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
children.forEach(child => {
|
||||||
|
if (typeof child === 'string') {
|
||||||
|
el.appendChild(document.createTextNode(child));
|
||||||
|
} else if (child) {
|
||||||
|
el.appendChild(child);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// CandleChart initialization script
|
||||||
|
script initCandleChart(containerId string, dataJSON string) {
|
||||||
|
const container = document.getElementById(containerId);
|
||||||
|
if (!container || !window.d3) return;
|
||||||
|
|
||||||
|
const data = JSON.parse(dataJSON);
|
||||||
|
const RC = window.RosenCharts;
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
marginTop: 10,
|
||||||
|
marginRight: 60,
|
||||||
|
marginBottom: 56,
|
||||||
|
marginLeft: 30,
|
||||||
|
height: 288
|
||||||
|
};
|
||||||
|
|
||||||
|
let zoomLevel = 1;
|
||||||
|
let visibleRange = { start: 0, end: data.length - 1 };
|
||||||
|
let mousePosition = null;
|
||||||
|
let hoverData = null;
|
||||||
|
|
||||||
|
function getVisibleData() {
|
||||||
|
return data.slice(visibleRange.start, visibleRange.end + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function render() {
|
||||||
|
container.innerHTML = '';
|
||||||
|
container.style.position = 'relative';
|
||||||
|
|
||||||
|
const visibleData = getVisibleData();
|
||||||
|
|
||||||
|
// Create scales
|
||||||
|
const xScale = d3.scaleBand()
|
||||||
|
.domain(visibleData.map(d => d.date))
|
||||||
|
.range([0, 100])
|
||||||
|
.padding(0.3);
|
||||||
|
|
||||||
|
const yMin = d3.min(visibleData, d => d.low) * 0.995;
|
||||||
|
const yMax = d3.max(visibleData, d => d.high) * 1.005;
|
||||||
|
const yScale = d3.scaleLinear()
|
||||||
|
.domain([yMin, yMax])
|
||||||
|
.range([100, 0]);
|
||||||
|
|
||||||
|
// Legend
|
||||||
|
const legend = RC.createElement('div', {
|
||||||
|
style: {
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginBottom: '8px',
|
||||||
|
padding: '4px 32px',
|
||||||
|
background: 'var(--wa-color-surface-alt)',
|
||||||
|
borderRadius: 'var(--wa-radius-s)',
|
||||||
|
fontSize: 'var(--wa-font-size-xs)'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
legend.innerHTML = `<div style="color: var(--wa-color-neutral-400);">Hover over chart to see OHLC data</div><div></div>`;
|
||||||
|
|
||||||
|
// Chart area
|
||||||
|
const chartArea = RC.createElement('div', {
|
||||||
|
style: {
|
||||||
|
position: 'relative',
|
||||||
|
height: options.height + 'px',
|
||||||
|
width: '100%'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Y-axis
|
||||||
|
const yAxisContainer = RC.createElement('div', {
|
||||||
|
style: {
|
||||||
|
position: 'absolute',
|
||||||
|
height: 'calc(100% - ' + options.marginTop + 'px - ' + options.marginBottom + 'px)',
|
||||||
|
transform: 'translateY(' + options.marginTop + 'px)',
|
||||||
|
right: 'calc(' + options.marginRight + 'px - 1rem)',
|
||||||
|
overflow: 'visible'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const yTicks = yScale.ticks(6);
|
||||||
|
yTicks.forEach(value => {
|
||||||
|
const label = RC.createElement('div', {
|
||||||
|
style: {
|
||||||
|
position: 'absolute',
|
||||||
|
right: '0%',
|
||||||
|
top: yScale(value) + '%',
|
||||||
|
transform: 'translateY(-50%)',
|
||||||
|
fontSize: 'var(--wa-font-size-xs)',
|
||||||
|
fontVariantNumeric: 'tabular-nums',
|
||||||
|
color: 'var(--wa-color-neutral-400)',
|
||||||
|
width: '100%',
|
||||||
|
textAlign: 'right'
|
||||||
|
}
|
||||||
|
}, [value.toFixed(2)]);
|
||||||
|
yAxisContainer.appendChild(label);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Chart inner
|
||||||
|
const chartInner = RC.createElement('div', {
|
||||||
|
style: {
|
||||||
|
position: 'absolute',
|
||||||
|
inset: 0,
|
||||||
|
height: 'calc(100% - ' + options.marginTop + 'px - ' + options.marginBottom + 'px)',
|
||||||
|
width: 'calc(100% - ' + options.marginLeft + 'px - ' + options.marginRight + 'px)',
|
||||||
|
transform: 'translate(' + options.marginLeft + 'px, ' + options.marginTop + 'px)',
|
||||||
|
overflow: 'visible'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// SVG for grid and wicks
|
||||||
|
const svg = RC.createSVG('svg', {
|
||||||
|
viewBox: '0 0 100 100',
|
||||||
|
preserveAspectRatio: 'none',
|
||||||
|
style: 'overflow: visible; width: 100%; height: 100%;'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Grid lines
|
||||||
|
yTicks.forEach(value => {
|
||||||
|
const line = RC.createSVG('line', {
|
||||||
|
x1: 0, x2: 100,
|
||||||
|
y1: yScale(value), y2: yScale(value),
|
||||||
|
stroke: 'var(--wa-color-neutral-200)',
|
||||||
|
'stroke-dasharray': '6,5',
|
||||||
|
'stroke-width': 0.5,
|
||||||
|
'vector-effect': 'non-scaling-stroke'
|
||||||
|
});
|
||||||
|
svg.appendChild(line);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wicks
|
||||||
|
visibleData.forEach(d => {
|
||||||
|
const barX = xScale(d.date) + xScale.bandwidth() / 2;
|
||||||
|
const line = RC.createSVG('line', {
|
||||||
|
x1: barX, y1: yScale(d.high),
|
||||||
|
x2: barX, y2: yScale(d.low),
|
||||||
|
stroke: 'var(--wa-color-neutral-300)',
|
||||||
|
'stroke-width': 1,
|
||||||
|
'vector-effect': 'non-scaling-stroke'
|
||||||
|
});
|
||||||
|
svg.appendChild(line);
|
||||||
|
});
|
||||||
|
|
||||||
|
chartInner.appendChild(svg);
|
||||||
|
|
||||||
|
// Candle bodies
|
||||||
|
visibleData.forEach(d => {
|
||||||
|
const barWidth = xScale.bandwidth();
|
||||||
|
const barHeight = Math.abs(yScale(d.open) - yScale(d.close));
|
||||||
|
const barX = xScale(d.date);
|
||||||
|
const barY = yScale(Math.max(d.open, d.close));
|
||||||
|
const isUp = d.close > d.open;
|
||||||
|
|
||||||
|
const bar = RC.createElement('div', {
|
||||||
|
style: {
|
||||||
|
position: 'absolute',
|
||||||
|
width: barWidth + '%',
|
||||||
|
height: barHeight + '%',
|
||||||
|
left: barX + '%',
|
||||||
|
top: barY + '%',
|
||||||
|
background: isUp
|
||||||
|
? 'linear-gradient(to bottom, #6ee7b7, #10b981)'
|
||||||
|
: 'linear-gradient(to bottom, #fca5a5, #ef4444)',
|
||||||
|
borderRadius: '1px'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
chartInner.appendChild(bar);
|
||||||
|
});
|
||||||
|
|
||||||
|
chartArea.appendChild(yAxisContainer);
|
||||||
|
chartArea.appendChild(chartInner);
|
||||||
|
|
||||||
|
container.appendChild(legend);
|
||||||
|
container.appendChild(chartArea);
|
||||||
|
|
||||||
|
// Wheel zoom
|
||||||
|
chartArea.addEventListener('wheel', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const zoomIn = e.deltaY < 0;
|
||||||
|
const zoomFactor = 1.04;
|
||||||
|
const newZoomLevel = zoomIn
|
||||||
|
? Math.min(zoomLevel * zoomFactor, 20)
|
||||||
|
: Math.max(zoomLevel / zoomFactor, 1);
|
||||||
|
|
||||||
|
const visibleCount = Math.max(5, Math.floor(data.length / newZoomLevel));
|
||||||
|
const newEnd = data.length - 1;
|
||||||
|
const newStart = Math.max(0, newEnd - visibleCount + 1);
|
||||||
|
|
||||||
|
zoomLevel = newZoomLevel;
|
||||||
|
visibleRange = { start: newStart, end: newEnd };
|
||||||
|
render();
|
||||||
|
}, { passive: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
|
||||||
|
// StackedAreaChart initialization script
|
||||||
|
script initStackedAreaChart(containerId string, dataJSON string) {
|
||||||
|
const container = document.getElementById(containerId);
|
||||||
|
if (!container || !window.d3) return;
|
||||||
|
|
||||||
|
const data = JSON.parse(dataJSON);
|
||||||
|
const RC = window.RosenCharts;
|
||||||
|
|
||||||
|
const colors = [
|
||||||
|
{ from: '#f5d0fe', to: '#e879f9', bg: '#e879f9' },
|
||||||
|
{ from: '#c4b5fd', to: '#a855f7', bg: '#a855f7' },
|
||||||
|
{ from: '#bfdbfe', to: '#3b82f6', bg: '#3b82f6' },
|
||||||
|
{ from: '#bae6fd', to: '#38bdf8', bg: '#38bdf8' },
|
||||||
|
{ from: '#fed7aa', to: '#fb923c', bg: '#fb923c' }
|
||||||
|
];
|
||||||
|
|
||||||
|
container.innerHTML = '';
|
||||||
|
container.style.position = 'relative';
|
||||||
|
|
||||||
|
const parseDate = d3.utcParse('%Y-%m-%d');
|
||||||
|
const industries = Array.from(new Set(data.map(d => d.industry)));
|
||||||
|
|
||||||
|
const groupedData = Array.from(
|
||||||
|
d3.group(data, d => d.date),
|
||||||
|
([date, values]) => {
|
||||||
|
const obj = { date: parseDate(date.split('T')[0]) };
|
||||||
|
values.forEach(val => {
|
||||||
|
obj[val.industry] = val.value || 0;
|
||||||
|
});
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const series = d3.stack().keys(industries)(groupedData);
|
||||||
|
|
||||||
|
// Legend
|
||||||
|
const legendContainer = RC.createElement('div', {
|
||||||
|
style: {
|
||||||
|
display: 'flex',
|
||||||
|
textAlign: 'center',
|
||||||
|
gap: '16px',
|
||||||
|
fontSize: 'var(--wa-font-size-xs)',
|
||||||
|
overflowX: 'auto',
|
||||||
|
padding: '0 40px',
|
||||||
|
marginBottom: '32px'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
industries.slice().reverse().forEach((industry, i) => {
|
||||||
|
const colorIndex = industries.length - 1 - i;
|
||||||
|
const color = colors[colorIndex % colors.length];
|
||||||
|
|
||||||
|
const item = RC.createElement('div', {
|
||||||
|
style: { display: 'flex', gap: '6px', alignItems: 'center' }
|
||||||
|
});
|
||||||
|
|
||||||
|
const dot = RC.createElement('div', {
|
||||||
|
style: {
|
||||||
|
width: '4px',
|
||||||
|
height: '16px',
|
||||||
|
borderRadius: '9999px',
|
||||||
|
background: color.bg
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
item.appendChild(dot);
|
||||||
|
item.appendChild(document.createTextNode(industry));
|
||||||
|
legendContainer.appendChild(item);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Chart container
|
||||||
|
const chartContainer = RC.createElement('div', {
|
||||||
|
style: {
|
||||||
|
position: 'relative',
|
||||||
|
height: '256px',
|
||||||
|
width: '100%'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Scales
|
||||||
|
const xScale = d3.scaleUtc()
|
||||||
|
.domain(d3.extent(groupedData, d => d.date))
|
||||||
|
.range([0, 100]);
|
||||||
|
|
||||||
|
const yScale = d3.scaleLinear()
|
||||||
|
.domain([0, d3.max(series, d => d3.max(d, d => d[1])) || 0])
|
||||||
|
.rangeRound([100, 0]);
|
||||||
|
|
||||||
|
// Area generator
|
||||||
|
const area = d3.area()
|
||||||
|
.x(d => xScale(d.data.date))
|
||||||
|
.y0(d => yScale(d[0]))
|
||||||
|
.y1(d => yScale(d[1]))
|
||||||
|
.curve(d3.curveMonotoneX);
|
||||||
|
|
||||||
|
// SVG
|
||||||
|
const svg = RC.createSVG('svg', {
|
||||||
|
style: 'position: absolute; inset: 0; z-index: 10; height: 100%; width: 100%; overflow: visible;'
|
||||||
|
});
|
||||||
|
|
||||||
|
const innerSvg = RC.createSVG('svg', {
|
||||||
|
viewBox: '0 0 100 100',
|
||||||
|
preserveAspectRatio: 'none',
|
||||||
|
style: 'overflow: visible;'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Gradients
|
||||||
|
const defs = RC.createSVG('defs');
|
||||||
|
series.forEach((_, i) => {
|
||||||
|
const color = colors[i % colors.length];
|
||||||
|
const gradient = RC.createSVG('linearGradient', {
|
||||||
|
id: 'stacked-area-gradient-' + containerId + '-' + i,
|
||||||
|
x1: 0, x2: 0.5, y1: 0.25, y2: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
const stop1 = RC.createSVG('stop', { offset: '0%', 'stop-color': color.from });
|
||||||
|
const stop2 = RC.createSVG('stop', { offset: '80%', 'stop-color': color.to });
|
||||||
|
|
||||||
|
gradient.appendChild(stop1);
|
||||||
|
gradient.appendChild(stop2);
|
||||||
|
defs.appendChild(gradient);
|
||||||
|
});
|
||||||
|
innerSvg.appendChild(defs);
|
||||||
|
|
||||||
|
// Areas
|
||||||
|
series.forEach((layer, layerIndex) => {
|
||||||
|
const path = RC.createSVG('path', {
|
||||||
|
fill: 'url(#stacked-area-gradient-' + containerId + '-' + layerIndex + ')',
|
||||||
|
d: area(layer),
|
||||||
|
stroke: '#ccc',
|
||||||
|
'stroke-width': '0.05'
|
||||||
|
});
|
||||||
|
innerSvg.appendChild(path);
|
||||||
|
});
|
||||||
|
|
||||||
|
svg.appendChild(innerSvg);
|
||||||
|
chartContainer.appendChild(svg);
|
||||||
|
|
||||||
|
container.appendChild(legendContainer);
|
||||||
|
container.appendChild(chartContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// BubbleChart initialization script
|
||||||
|
script initBubbleChart(containerId string, dataJSON string) {
|
||||||
|
const container = document.getElementById(containerId);
|
||||||
|
if (!container || !window.d3) return;
|
||||||
|
|
||||||
|
const data = JSON.parse(dataJSON);
|
||||||
|
const RC = window.RosenCharts;
|
||||||
|
|
||||||
|
const colors = ['#f472b6', '#8b5cf6', '#84cc16', '#38bdf8', '#fb923c'];
|
||||||
|
|
||||||
|
container.innerHTML = '';
|
||||||
|
container.style.position = 'relative';
|
||||||
|
container.style.width = '100%';
|
||||||
|
container.style.aspectRatio = '1';
|
||||||
|
container.style.maxWidth = '18rem';
|
||||||
|
container.style.margin = '0 auto';
|
||||||
|
|
||||||
|
const sectors = Array.from(new Set(data.map(d => d.sector)));
|
||||||
|
const color = d3.scaleOrdinal().domain(sectors).range(colors);
|
||||||
|
|
||||||
|
const pack = d3.pack().size([1000, 1000]).padding(12);
|
||||||
|
|
||||||
|
const root = pack(
|
||||||
|
d3.hierarchy({ children: data }).sum(d => d.value)
|
||||||
|
);
|
||||||
|
|
||||||
|
root.leaves().forEach(d => {
|
||||||
|
const x = d.x;
|
||||||
|
const y = d.y;
|
||||||
|
const r = d.r;
|
||||||
|
const fillColor = color(d.data.sector);
|
||||||
|
const name = d.data.name;
|
||||||
|
const value = d.data.value;
|
||||||
|
|
||||||
|
const bubble = RC.createElement('div', {
|
||||||
|
style: {
|
||||||
|
position: 'absolute',
|
||||||
|
transform: 'translate(-50%, -50%)',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
left: (x / 1000 * 100) + '%',
|
||||||
|
top: (y / 1000 * 100) + '%',
|
||||||
|
width: (r * 2 / 1000 * 100) + '%',
|
||||||
|
height: (r * 2 / 1000 * 100) + '%',
|
||||||
|
borderRadius: '50%',
|
||||||
|
backgroundColor: fillColor,
|
||||||
|
border: '1px solid rgba(255,255,255,0.2)',
|
||||||
|
cursor: 'default'
|
||||||
|
},
|
||||||
|
title: name + '\n' + d3.format(',d')(value)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (value > 1000) {
|
||||||
|
const nameEl = RC.createElement('div', {
|
||||||
|
style: {
|
||||||
|
color: 'white',
|
||||||
|
textAlign: 'center',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
fontSize: (r / 9) + 'px',
|
||||||
|
lineHeight: (r / 7) + 'px'
|
||||||
|
}
|
||||||
|
}, [name]);
|
||||||
|
|
||||||
|
const valueEl = RC.createElement('div', {
|
||||||
|
style: {
|
||||||
|
color: 'white',
|
||||||
|
textAlign: 'center',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
opacity: 0.7,
|
||||||
|
fontSize: (r / 10) + 'px',
|
||||||
|
lineHeight: (r / 8) + 'px'
|
||||||
|
}
|
||||||
|
}, [d3.format(',d')(value)]);
|
||||||
|
|
||||||
|
bubble.appendChild(nameEl);
|
||||||
|
bubble.appendChild(valueEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
container.appendChild(bubble);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// SemiFilledAreaChart initialization script
|
||||||
|
script initSemiFilledAreaChart(containerId string, dataJSON string, lineColor string, areaFromColor string, areaToColor string) {
|
||||||
|
const container = document.getElementById(containerId);
|
||||||
|
if (!container || !window.d3) return;
|
||||||
|
|
||||||
|
const data = JSON.parse(dataJSON).map(d => ({
|
||||||
|
...d,
|
||||||
|
date: new Date(d.date)
|
||||||
|
}));
|
||||||
|
const RC = window.RosenCharts;
|
||||||
|
|
||||||
|
container.innerHTML = '';
|
||||||
|
container.style.position = 'relative';
|
||||||
|
container.style.height = '288px';
|
||||||
|
container.style.width = '100%';
|
||||||
|
|
||||||
|
// Scales
|
||||||
|
const xScale = d3.scaleTime()
|
||||||
|
.domain([data[0].date, data[data.length - 1].date])
|
||||||
|
.range([0, 100]);
|
||||||
|
|
||||||
|
const yScale = d3.scaleLinear()
|
||||||
|
.domain([0, d3.max(data, d => d.value) || 0])
|
||||||
|
.range([100, 0]);
|
||||||
|
|
||||||
|
// Line generator
|
||||||
|
const line = d3.line()
|
||||||
|
.x(d => xScale(d.date))
|
||||||
|
.y(d => yScale(d.value))
|
||||||
|
.curve(d3.curveMonotoneX);
|
||||||
|
|
||||||
|
// Area generator
|
||||||
|
const area = d3.area()
|
||||||
|
.x(d => xScale(d.date))
|
||||||
|
.y0(yScale(0))
|
||||||
|
.y1(d => yScale(d.value))
|
||||||
|
.curve(d3.curveMonotoneX);
|
||||||
|
|
||||||
|
// Chart inner
|
||||||
|
const chartInner = RC.createElement('div', {
|
||||||
|
style: {
|
||||||
|
position: 'absolute',
|
||||||
|
inset: 0,
|
||||||
|
height: '100%',
|
||||||
|
width: '100%',
|
||||||
|
overflow: 'visible'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// SVG
|
||||||
|
const svg = RC.createSVG('svg', {
|
||||||
|
viewBox: '0 0 100 100',
|
||||||
|
preserveAspectRatio: 'none',
|
||||||
|
style: 'width: 100%; height: 100%; overflow: visible;'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Gradient
|
||||||
|
const defs = RC.createSVG('defs');
|
||||||
|
const gradient = RC.createSVG('linearGradient', {
|
||||||
|
id: 'semi-filled-gradient-' + containerId,
|
||||||
|
x1: 0, x2: 0, y1: 0, y2: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
const stop1 = RC.createSVG('stop', { offset: '0%', 'stop-color': areaFromColor });
|
||||||
|
const stop2 = RC.createSVG('stop', { offset: '100%', 'stop-color': areaToColor });
|
||||||
|
|
||||||
|
gradient.appendChild(stop1);
|
||||||
|
gradient.appendChild(stop2);
|
||||||
|
defs.appendChild(gradient);
|
||||||
|
svg.appendChild(defs);
|
||||||
|
|
||||||
|
// Area path
|
||||||
|
const areaPath = RC.createSVG('path', {
|
||||||
|
d: area(data),
|
||||||
|
fill: 'url(#semi-filled-gradient-' + containerId + ')'
|
||||||
|
});
|
||||||
|
svg.appendChild(areaPath);
|
||||||
|
|
||||||
|
// Line path
|
||||||
|
const linePath = RC.createSVG('path', {
|
||||||
|
d: line(data),
|
||||||
|
fill: 'none',
|
||||||
|
stroke: lineColor,
|
||||||
|
'stroke-width': '1.5',
|
||||||
|
'vector-effect': 'non-scaling-stroke'
|
||||||
|
});
|
||||||
|
svg.appendChild(linePath);
|
||||||
|
|
||||||
|
chartInner.appendChild(svg);
|
||||||
|
|
||||||
|
// X-axis labels
|
||||||
|
data.forEach((day, i) => {
|
||||||
|
if (i % 6 !== 0 || i === 0 || i >= data.length - 3) return;
|
||||||
|
|
||||||
|
const label = RC.createElement('div', {
|
||||||
|
style: {
|
||||||
|
position: 'absolute',
|
||||||
|
left: xScale(day.date) + '%',
|
||||||
|
top: '90%',
|
||||||
|
fontSize: 'var(--wa-font-size-xs)',
|
||||||
|
color: 'var(--wa-color-neutral-500)',
|
||||||
|
transform: 'translateX(-50%)'
|
||||||
|
}
|
||||||
|
}, [day.date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })]);
|
||||||
|
|
||||||
|
chartInner.appendChild(label);
|
||||||
|
});
|
||||||
|
|
||||||
|
container.appendChild(chartInner);
|
||||||
|
|
||||||
|
// Y-axis labels
|
||||||
|
yScale.ticks(8).forEach((value, i) => {
|
||||||
|
if (i < 1) return;
|
||||||
|
|
||||||
|
const label = RC.createElement('div', {
|
||||||
|
style: {
|
||||||
|
position: 'absolute',
|
||||||
|
top: yScale(value) + '%',
|
||||||
|
right: '3%',
|
||||||
|
fontSize: 'var(--wa-font-size-xs)',
|
||||||
|
fontVariantNumeric: 'tabular-nums',
|
||||||
|
color: 'var(--wa-color-neutral-400)',
|
||||||
|
transform: 'translateY(-50%)'
|
||||||
|
}
|
||||||
|
}, [value.toString()]);
|
||||||
|
|
||||||
|
container.appendChild(label);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// CandleChart component
|
||||||
|
templ CandleChart(id string, data []OHLCData) {
|
||||||
|
<div id={ id } class="candle-chart" style="width: 100%;"></div>
|
||||||
|
@initRosenCharts()
|
||||||
|
@initCandleChart(id, toJSON(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
// StackedAreaChart component
|
||||||
|
templ StackedAreaChart(id string, data []AreaSeriesData) {
|
||||||
|
<div id={ id } class="stacked-area-chart" style="width: 100%;"></div>
|
||||||
|
@initRosenCharts()
|
||||||
|
@initStackedAreaChart(id, toJSON(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
// BubbleChart component
|
||||||
|
templ BubbleChart(id string, data []BubbleData) {
|
||||||
|
<div id={ id } class="bubble-chart"></div>
|
||||||
|
@initRosenCharts()
|
||||||
|
@initBubbleChart(id, toJSON(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SemiFilledAreaChart component with customizable colors
|
||||||
|
templ SemiFilledAreaChart(id string, data []TimeSeriesData) {
|
||||||
|
@SemiFilledAreaChartWithColors(id, data, "#facc15", "rgba(234, 179, 8, 0.2)", "rgba(234, 179, 8, 0.05)")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SemiFilledAreaChart with custom colors
|
||||||
|
templ SemiFilledAreaChartWithColors(id string, data []TimeSeriesData, lineColor string, areaFromColor string, areaToColor string) {
|
||||||
|
<div id={ id } class="semi-filled-area-chart" style="width: 100%;"></div>
|
||||||
|
@initRosenCharts()
|
||||||
|
@initSemiFilledAreaChart(id, toJSON(data), lineColor, areaFromColor, areaToColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chart card wrapper for consistent styling
|
||||||
|
templ ChartCard(title string, subtitle string) {
|
||||||
|
<wa-card class="chart-card">
|
||||||
|
<div slot="header" class="wa-flank">
|
||||||
|
<div class="wa-stack wa-gap-0">
|
||||||
|
<span class="wa-heading-m">{ title }</span>
|
||||||
|
if subtitle != "" {
|
||||||
|
<span class="wa-caption-xs" style="color: var(--wa-color-neutral-500);">{ subtitle }</span>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{ children... }
|
||||||
|
</wa-card>
|
||||||
|
}
|
||||||
1003
components/charts_templ.go
Normal file
1003
components/charts_templ.go
Normal file
File diff suppressed because it is too large
Load Diff
374
components/drawers.templ
Normal file
374
components/drawers.templ
Normal file
@@ -0,0 +1,374 @@
|
|||||||
|
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);">< 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()
|
||||||
|
}
|
||||||
564
components/drawers_templ.go
Normal file
564
components/drawers_templ.go
Normal file
@@ -0,0 +1,564 @@
|
|||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.3.977
|
||||||
|
package components
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import templruntime "github.com/a-h/templ/runtime"
|
||||||
|
|
||||||
|
// 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
|
||||||
|
func ReceiveDrawer(tokens []TokenOption, walletAddress string) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<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\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
for _, token := range tokens {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "<wa-option value=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var2 string
|
||||||
|
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(token.Symbol)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/drawers.templ`, Line: 30, Col: 38}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "\"><wa-avatar slot=\"prefix\" initials=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var3 string
|
||||||
|
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(token.Initials)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/drawers.templ`, Line: 31, Col: 58}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\" style=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var4 string
|
||||||
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues("--size: 24px; background: " + token.Color + ";")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/drawers.templ`, Line: 31, Col: 117}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "\"></wa-avatar> ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var5 string
|
||||||
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(token.Symbol)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/drawers.templ`, Line: 32, Col: 22}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, " - ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var6 string
|
||||||
|
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(token.Name)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/drawers.templ`, Line: 32, Col: 39}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "</wa-option>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</wa-select><div class=\"qr-container\"><wa-qr-code value=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var7 string
|
||||||
|
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(walletAddress)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/drawers.templ`, Line: 37, Col: 39}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "\" 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;\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var8 string
|
||||||
|
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(walletAddress)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/drawers.templ`, Line: 43, Col: 45}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</span> <wa-copy-button value=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var9 string
|
||||||
|
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(walletAddress)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/drawers.templ`, Line: 44, Col: 44}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "\"></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>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = drawerStyles().Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendDrawer component for sending tokens
|
||||||
|
func SendDrawer(tokens []TokenOption) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var10 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var10 == nil {
|
||||||
|
templ_7745c5c3_Var10 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<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\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
for _, token := range tokens {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<wa-option value=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var11 string
|
||||||
|
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(token.Symbol)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/drawers.templ`, Line: 73, Col: 38}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "\"><wa-avatar slot=\"prefix\" initials=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var12 string
|
||||||
|
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(token.Initials)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/drawers.templ`, Line: 74, Col: 58}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "\" style=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var13 string
|
||||||
|
templ_7745c5c3_Var13, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues("--size: 24px; background: " + token.Color + ";")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/drawers.templ`, Line: 74, Col: 117}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "\"></wa-avatar> ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var14 string
|
||||||
|
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(token.Symbol)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/drawers.templ`, Line: 75, Col: 22}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, " - ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var15 string
|
||||||
|
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(token.Balance)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/drawers.templ`, Line: 75, Col: 42}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, " available</wa-option>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "</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>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = drawerStyles().Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SwapDrawer component for swapping tokens
|
||||||
|
func SwapDrawer(tokens []TokenOption) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var16 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var16 == nil {
|
||||||
|
templ_7745c5c3_Var16 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "<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> ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
for _, token := range tokens {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "<wa-dropdown-item><wa-avatar slot=\"icon\" initials=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var17 string
|
||||||
|
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(token.Initials)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/drawers.templ`, Line: 158, Col: 58}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\" style=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var18 string
|
||||||
|
templ_7745c5c3_Var18, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues("--size: 20px; background: " + token.Color + ";")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/drawers.templ`, Line: 158, Col: 117}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "\"></wa-avatar> ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var19 string
|
||||||
|
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(token.Symbol)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/drawers.templ`, Line: 159, Col: 24}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "</wa-dropdown-item>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "</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> ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
for _, token := range tokens {
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "<wa-dropdown-item><wa-avatar slot=\"icon\" initials=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var20 string
|
||||||
|
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(token.Initials)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/drawers.templ`, Line: 192, Col: 58}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "\" style=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var21 string
|
||||||
|
templ_7745c5c3_Var21, templ_7745c5c3_Err = templruntime.SanitizeStyleAttributeValues("--size: 20px; background: " + token.Color + ";")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/drawers.templ`, Line: 192, Col: 117}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "\"></wa-avatar> ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var22 string
|
||||||
|
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(token.Symbol)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `components/drawers.templ`, Line: 193, Col: 24}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "</wa-dropdown-item>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "</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);\">< 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>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = drawerStyles().Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = swapDirectionScript().Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Script to handle swap direction button
|
||||||
|
func swapDirectionScript() templ.ComponentScript {
|
||||||
|
return templ.ComponentScript{
|
||||||
|
Name: `__templ_swapDirectionScript_d885`,
|
||||||
|
Function: `function __templ_swapDirectionScript_d885(){document.getElementById('swap-direction-btn')?.addEventListener('click', function() {
|
||||||
|
this.style.transform = this.style.transform === 'rotate(180deg)' ? '' : 'rotate(180deg)';
|
||||||
|
});
|
||||||
|
}`,
|
||||||
|
Call: templ.SafeScript(`__templ_swapDirectionScript_d885`),
|
||||||
|
CallInline: templ.SafeScriptInline(`__templ_swapDirectionScript_d885`),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drawer-specific styles
|
||||||
|
func drawerStyles() templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var23 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var23 == nil {
|
||||||
|
templ_7745c5c3_Var23 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, "<style>\n\t\t.drawer-form {\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t\tgap: var(--wa-space-l);\n\t\t\theight: 100%;\n\t\t}\n\t\t.drawer-form-content {\n\t\t\tflex: 1;\n\t\t\toverflow-y: auto;\n\t\t}\n\t\t.qr-container {\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t\talign-items: center;\n\t\t\tpadding: var(--wa-space-xl);\n\t\t\tbackground: var(--wa-color-surface-alt);\n\t\t\tborder-radius: var(--wa-radius-l);\n\t\t}\n\t\t.address-display {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tgap: var(--wa-space-s);\n\t\t\tpadding: var(--wa-space-m);\n\t\t\tbackground: var(--wa-color-surface-alt);\n\t\t\tborder-radius: var(--wa-radius-m);\n\t\t\tfont-family: var(--wa-font-mono);\n\t\t\tfont-size: var(--wa-font-size-s);\n\t\t\tword-break: break-all;\n\t\t}\n\t\t.swap-token-box {\n\t\t\tpadding: var(--wa-space-m);\n\t\t\tbackground: var(--wa-color-surface-alt);\n\t\t\tborder-radius: var(--wa-radius-m);\n\t\t\tborder: 1px solid var(--wa-color-neutral-200);\n\t\t}\n\t\t.swap-arrow {\n\t\t\tdisplay: flex;\n\t\t\tjustify-content: center;\n\t\t\tmargin: calc(var(--wa-space-s) * -1) 0;\n\t\t\tposition: relative;\n\t\t\tz-index: 1;\n\t\t}\n\t\t.swap-arrow wa-icon-button {\n\t\t\tbackground: var(--wa-color-surface);\n\t\t\tborder: 1px solid var(--wa-color-neutral-200);\n\t\t\tborder-radius: 50%;\n\t\t\ttransition: transform 0.2s;\n\t\t}\n\t\t.gas-settings {\n\t\t\tpadding: var(--wa-space-m);\n\t\t\tbackground: var(--wa-color-surface-alt);\n\t\t\tborder-radius: var(--wa-radius-m);\n\t\t}\n\t\t.preview-row {\n\t\t\tdisplay: flex;\n\t\t\tjustify-content: space-between;\n\t\t\tpadding: var(--wa-space-xs) 0;\n\t\t\tfont-size: var(--wa-font-size-s);\n\t\t}\n\t\t.preview-row .label {\n\t\t\tcolor: var(--wa-color-neutral-500);\n\t\t}\n\t</style>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllDrawers renders all three drawers together for convenience
|
||||||
|
func AllDrawers(tokens []TokenOption, walletAddress string) templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var24 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var24 == nil {
|
||||||
|
templ_7745c5c3_Var24 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = ReceiveDrawer(tokens, walletAddress).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = SendDrawer(tokens).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = SwapDrawer(tokens).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// DrawerTriggerScript provides the JavaScript to wire up drawer triggers
|
||||||
|
func drawerTriggerScript() templ.ComponentScript {
|
||||||
|
return templ.ComponentScript{
|
||||||
|
Name: `__templ_drawerTriggerScript_084e`,
|
||||||
|
Function: `function __templ_drawerTriggerScript_084e(){// 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;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}`,
|
||||||
|
Call: templ.SafeScript(`__templ_drawerTriggerScript_084e`),
|
||||||
|
CallInline: templ.SafeScriptInline(`__templ_drawerTriggerScript_084e`),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DrawerTriggers component to initialize drawer triggers
|
||||||
|
func DrawerTriggers() templ.Component {
|
||||||
|
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||||
|
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||||
|
return templ_7745c5c3_CtxErr
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
defer func() {
|
||||||
|
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err == nil {
|
||||||
|
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var25 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var25 == nil {
|
||||||
|
templ_7745c5c3_Var25 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Err = drawerTriggerScript().Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = templruntime.GeneratedTemplate
|
||||||
Reference in New Issue
Block a user