Restore working state from f17e19f (before mobile overhaul)

Reverting all my changes that broke the desktop layout.
Starting fresh for mobile improvements.
This commit is contained in:
fahed
2026-02-03 15:29:03 +03:00
parent 222d583847
commit b2fcb16d12
9 changed files with 323 additions and 739 deletions

View File

@@ -1,8 +1,7 @@
import React, { useRef } from 'react';
import JSZip from 'jszip';
// Wrapper component that adds PNG export to any chart
export function ExportableChart({ children, filename = 'chart', title = '', className = '', controls = null }) {
export function ExportableChart({ children, filename = 'chart', className = '' }) {
const chartRef = useRef(null);
const exportAsPNG = () => {
@@ -12,30 +11,21 @@ export function ExportableChart({ children, filename = 'chart', title = '', clas
const canvas = chartContainer.querySelector('canvas');
if (!canvas) return;
// Create a new canvas with white background and title
// Create a new canvas with white background
const exportCanvas = document.createElement('canvas');
const ctx = exportCanvas.getContext('2d');
// Set dimensions with padding and title space
const padding = 24;
const titleHeight = title ? 48 : 0;
// Set dimensions with padding
const padding = 20;
exportCanvas.width = canvas.width + (padding * 2);
exportCanvas.height = canvas.height + (padding * 2) + titleHeight;
exportCanvas.height = canvas.height + (padding * 2);
// Fill white background
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, exportCanvas.width, exportCanvas.height);
// Draw title if provided (left-aligned, matching on-screen style)
if (title) {
ctx.fillStyle = '#1e293b';
ctx.font = '600 20px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
ctx.textAlign = 'left';
ctx.fillText(title, padding, padding + 24);
}
// Draw the chart
ctx.drawImage(canvas, padding, padding + titleHeight);
ctx.drawImage(canvas, padding, padding);
// Export
const link = document.createElement('a');
@@ -45,118 +35,23 @@ export function ExportableChart({ children, filename = 'chart', title = '', clas
};
return (
<div className="exportable-chart-wrapper">
{title && (
<div className="chart-header-with-export">
<h2>{title}</h2>
<div className="chart-header-actions">
{controls}
<button
className="chart-export-btn visible"
onClick={exportAsPNG}
title="Download as PNG"
>
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
<polyline points="7 10 12 15 17 10"/>
<line x1="12" y1="15" x2="12" y2="3"/>
</svg>
</button>
</div>
</div>
)}
{!title && controls && <div className="chart-controls">{controls}</div>}
<div className={`exportable-chart ${className}`}>
<div ref={chartRef} className="chart-canvas-wrapper">
{children}
</div>
<div className={`exportable-chart ${className}`}>
<button
className="chart-export-btn"
onClick={exportAsPNG}
title="Download as PNG"
>
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
<polyline points="7 10 12 15 17 10"/>
<line x1="12" y1="15" x2="12" y2="3"/>
</svg>
</button>
<div ref={chartRef} className="chart-canvas-wrapper">
{children}
</div>
</div>
);
}
// Utility function to export all charts from a container as a ZIP
export async function exportAllCharts(containerSelector, zipFilename = 'charts') {
const container = document.querySelector(containerSelector);
if (!container) return;
const zip = new JSZip();
const chartWrappers = container.querySelectorAll('.exportable-chart-wrapper');
for (let i = 0; i < chartWrappers.length; i++) {
const wrapper = chartWrappers[i];
const canvas = wrapper.querySelector('canvas');
const titleEl = wrapper.querySelector('.chart-header-with-export h2');
const title = titleEl?.textContent || `chart-${i + 1}`;
if (!canvas) continue;
// Create export canvas with white background and title
const exportCanvas = document.createElement('canvas');
const ctx = exportCanvas.getContext('2d');
const padding = 32;
const titleHeight = 56;
exportCanvas.width = canvas.width + (padding * 2);
exportCanvas.height = canvas.height + (padding * 2) + titleHeight;
// White background
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, exportCanvas.width, exportCanvas.height);
// Draw title
ctx.fillStyle = '#1e293b';
ctx.font = '600 24px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
ctx.textAlign = 'left';
ctx.fillText(title, padding, padding + 28);
// Draw chart
ctx.drawImage(canvas, padding, padding + titleHeight);
// Convert to blob and add to zip
const dataUrl = exportCanvas.toDataURL('image/png', 1.0);
const base64Data = dataUrl.split(',')[1];
const safeFilename = title.replace(/[^a-zA-Z0-9\u0600-\u06FF\s-]/g, '').replace(/\s+/g, '-');
zip.file(`${String(i + 1).padStart(2, '0')}-${safeFilename}.png`, base64Data, { base64: true });
}
// Generate and download ZIP
const blob = await zip.generateAsync({ type: 'blob' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `${zipFilename}-${new Date().toISOString().split('T')[0]}.zip`;
link.click();
URL.revokeObjectURL(url);
}
// Button component for exporting all charts
export function ExportAllButton({ containerSelector, zipFilename, label, loadingLabel }) {
const [exporting, setExporting] = React.useState(false);
const handleExport = async () => {
setExporting(true);
try {
await exportAllCharts(containerSelector, zipFilename);
} finally {
setExporting(false);
}
};
return (
<button
className="btn-export-all"
onClick={handleExport}
disabled={exporting}
>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
<polyline points="7 10 12 15 17 10"/>
<line x1="12" y1="15" x2="12" y2="3"/>
</svg>
{exporting ? loadingLabel : label}
</button>
);
}
export default ExportableChart;