/* global React, Card, Button, Input, Ic, VS, LoadingState, ErrorState */ // ============================================================ // SCHEMA PICKER — full-screen modal with grid of thumbnails // ============================================================ function SchemaPicker({ currentId, onPick, onClose }) { const [list, setList] = React.useState(null); const [cats, setCats] = React.useState(null); const [err, setErr] = React.useState(''); const [q, setQ] = React.useState(''); const [cat, setCat] = React.useState(''); // '' = all React.useEffect(() => { VS.schemaLibrary() .then((r) => setList(r || [])) .catch((e) => setErr(e.message)); VS.schemaLibraryCategories() .then((r) => setCats(r || [])) .catch(() => setCats([])); }, []); // ESC → close React.useEffect(() => { function onKey(e) { if (e.key === 'Escape') onClose && onClose(); } document.addEventListener('keydown', onKey); return () => document.removeEventListener('keydown', onKey); }, [onClose]); const filtered = React.useMemo(() => { if (!list) return []; const qq = (q || '').trim().toLowerCase(); return list.filter((it) => { if (cat && (it.category || '') !== cat) return false; if (!qq) return true; return ( (it.name || '').toLowerCase().includes(qq) || (it.category || '').toLowerCase().includes(qq) || (it.filename || '').toLowerCase().includes(qq) ); }); }, [list, q, cat]); function pick(item) { if (onPick) onPick(item.id, item.name); if (onClose) onClose(); } return (
e.stopPropagation()} style={{ width: 'min(1100px, 100%)', maxHeight: 'calc(100vh - 48px)', background: 'var(--bg)', border: '1px solid var(--border)', borderRadius: 'var(--r-md)', boxShadow: '0 20px 60px rgba(0,0,0,0.35)', display: 'flex', flexDirection: 'column', overflow: 'hidden', }} > {/* Header */}
Biblioteka schematów
Wybierz schemat z biblioteki
{/* Toolbar: search + categories */}
} placeholder="Szukaj po nazwie lub kategorii…" value={q} onChange={(e) => setQ(e.target.value)} />
{/* Category chips */}
setCat('')} label="Wszystkie" count={list ? list.length : undefined} /> {(cats || []).map((c) => ( setCat(c.category)} label={c.category || '— bez kategorii —'} count={c.count} /> ))}
{/* Grid */}
{err && } {!err && !list && } {!err && list && list.length === 0 && (
Biblioteka pusta
Administrator może dodać schematy w Konfiguracji.
)} {!err && list && list.length > 0 && filtered.length === 0 && (
Brak wyników dla wybranych filtrów.
)} {!err && filtered.length > 0 && (
{filtered.map((it) => ( pick(it)} /> ))}
)}
{/* Footer */}
{list ? `${filtered.length} / ${list.length} schematów` : ''}
); } function CategoryChip({ active, onClick, label, count }) { return ( ); } function SchemaTile({ item, selected, onClick }) { const [hover, setHover] = React.useState(false); return (
setHover(true)} onMouseLeave={() => setHover(false)} style={{ display: 'flex', flexDirection: 'column', background: 'var(--surface)', border: '1px solid ' + (selected ? 'var(--accent)' : hover ? 'var(--border-strong)' : 'var(--border)'), boxShadow: selected ? '0 0 0 2px var(--accent-weak)' : 'none', borderRadius: 'var(--r-sm)', overflow: 'hidden', cursor: 'pointer', transition: 'border-color 0.12s, box-shadow 0.12s', }} >
{item.name { e.currentTarget.style.visibility = 'hidden'; }} />
{item.name || item.filename || '—'}
{item.category && (
{item.category}
)}
); } Object.assign(window, { SchemaPicker });