Files
qrcode/src/App.tsx
T
arsam 5eb7672d4b Import source: src/App
Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
2026-09-18 23:16:32 +03:30

139 lines
7.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useState } from 'react';
import { ThemeProvider } from './context/ThemeContext';
import { QRProvider } from './context/QRContext';
import { ToastProvider } from './context/ToastContext';
import { HistoryProvider } from './context/HistoryContext';
import { Navbar } from './components/Navbar/Navbar';
import { LeftPanel } from './components/LeftPanel/LeftPanel';
import { CenterPanel } from './components/CenterPanel/CenterPanel';
import { RightPanel } from './components/RightPanel/RightPanel';
type MobileTab = 'types' | 'preview' | 'style';
const TABS: { key: MobileTab; label: string; icon: React.ReactNode }[] = [
{
key: 'types', label: 'Type',
icon: <svg width="22" height="22" viewBox="0 0 22 22" fill="none"><rect x="2" y="2" width="8" height="8" rx="2" stroke="currentColor" strokeWidth="1.7"/><rect x="12" y="2" width="8" height="8" rx="2" stroke="currentColor" strokeWidth="1.7"/><rect x="2" y="12" width="8" height="8" rx="2" stroke="currentColor" strokeWidth="1.7"/><rect x="12" y="12" width="8" height="8" rx="2" stroke="currentColor" strokeWidth="1.7"/></svg>,
},
{
key: 'preview', label: 'Preview',
icon: <svg width="22" height="22" viewBox="0 0 22 22" fill="none"><rect x="3" y="3" width="7" height="7" rx="1.5" stroke="currentColor" strokeWidth="1.7"/><rect x="12" y="3" width="7" height="7" rx="1.5" stroke="currentColor" strokeWidth="1.7"/><rect x="3" y="12" width="7" height="7" rx="1.5" stroke="currentColor" strokeWidth="1.7"/><path d="M14 16h4M16 14v4" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round"/></svg>,
},
{
key: 'style', label: 'Style',
icon: <svg width="22" height="22" viewBox="0 0 22 22" fill="none"><circle cx="11" cy="11" r="3.5" stroke="currentColor" strokeWidth="1.7"/><path d="M11 2v2M11 18v2M2 11h2M18 11h2M4.93 4.93l1.41 1.41M15.66 15.66l1.41 1.41M4.93 17.07l1.41-1.41M15.66 6.34l1.41-1.41" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round"/></svg>,
},
];
export default function App() {
const [mobileTab, setMobileTab] = useState<MobileTab>('preview');
return (
<ThemeProvider>
<ToastProvider>
<HistoryProvider>
<QRProvider>
<div className="flex flex-col w-screen overflow-hidden" style={{ height: '100dvh', background: 'var(--bg-page)' }}>
<Navbar />
{/* ── Desktop / Tablet landscape (≥1024px): 3 columns ── */}
<div className="hidden lg:flex flex-1 overflow-hidden">
<LeftPanel />
<CenterPanel />
<RightPanel />
</div>
{/* ── Tablet portrait (768px–1023px): left + center, right hidden ── */}
<div className="hidden md:flex lg:hidden flex-1 overflow-hidden">
<LeftPanel />
<CenterPanel />
</div>
{/* ── Mobile (<768px): single panel + bottom tab bar ── */}
<div className="flex md:hidden flex-1 overflow-hidden">
<div className="flex-1 overflow-hidden flex flex-col" style={{ display: mobileTab === 'types' ? 'flex' : 'none' }}>
<LeftPanel />
</div>
<div className="flex-1 overflow-hidden flex flex-col" style={{ display: mobileTab === 'preview' ? 'flex' : 'none' }}>
<CenterPanel />
</div>
<div className="flex-1 overflow-hidden flex flex-col" style={{ display: mobileTab === 'style' ? 'flex' : 'none' }}>
<RightPanel />
</div>
</div>
{/* ── Mobile bottom tab bar ── */}
<nav
className="md:hidden flex-shrink-0 flex items-stretch relative"
style={{
background: 'var(--bg-panel)',
borderTop: '1px solid var(--border)',
height: '60px',
paddingBottom: 'env(safe-area-inset-bottom)',
}}
>
{TABS.map(tab => (
<button
key={tab.key}
onClick={() => setMobileTab(tab.key)}
className="flex-1 flex flex-col items-center justify-center gap-0.5 cursor-pointer relative transition-colors"
style={{ border: 'none', background: 'transparent', color: mobileTab === tab.key ? '#7C6FE0' : 'var(--text-muted)' }}
>
{mobileTab === tab.key && (
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-10 h-0.5 rounded-full" style={{ background: '#7C6FE0' }} />
)}
{tab.icon}
<span className="text-[10px] font-semibold tracking-wide">{tab.label}</span>
</button>
))}
</nav>
{/* ── Tablet portrait: style panel button (floating) ── */}
<div className="hidden md:flex lg:hidden fixed bottom-6 right-6 z-30">
<StyleDrawer />
</div>
</div>
</QRProvider>
</HistoryProvider>
</ToastProvider>
</ThemeProvider>
);
}
/* Floating style button for tablet portrait — opens right panel as a drawer */
function StyleDrawer() {
const [open, setOpen] = useState(false);
return (
<>
<button
onClick={() => setOpen(o => !o)}
className="w-14 h-14 rounded-full shadow-2xl flex items-center justify-center cursor-pointer transition-all"
style={{ background: '#7C6FE0', color: 'white', boxShadow: '0 8px 32px rgba(124,111,224,0.45)' }}
title="Style controls"
>
<svg width="22" height="22" viewBox="0 0 22 22" fill="none">
<circle cx="11" cy="11" r="3.5" stroke="currentColor" strokeWidth="1.8"/>
<path d="M11 2v2M11 18v2M2 11h2M18 11h2M4.93 4.93l1.41 1.41M15.66 15.66l1.41 1.41M4.93 17.07l1.41-1.41M15.66 6.34l1.41-1.41" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round"/>
</svg>
</button>
{open && (
<>
<div className="fixed inset-0 z-20" onClick={() => setOpen(false)} style={{ background: 'rgba(0,0,0,0.4)', backdropFilter: 'blur(2px)' }} />
<div
className="fixed right-0 top-0 bottom-0 z-30 overflow-y-auto shadow-2xl"
style={{ width: 'min(85vw, 430px)', background: 'var(--bg-panel)', borderLeft: '1px solid var(--border)' }}
>
<div className="flex items-center justify-between p-5 sticky top-0" style={{ background: 'var(--bg-panel)', borderBottom: '1px solid var(--border)' }}>
<span className="text-[17px] font-bold" style={{ color: 'var(--text-primary)' }}>Style your QR</span>
<button onClick={() => setOpen(false)} className="w-8 h-8 rounded-full flex items-center justify-center cursor-pointer" style={{ background: 'var(--bg-elevated)', color: 'var(--text-secondary)' }}>
<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M2 2l10 10M12 2L2 12" stroke="currentColor" strokeWidth="2" strokeLinecap="round"/></svg>
</button>
</div>
<RightPanel embedded />
</div>
</>
)}
</>
);
}