import { useState, useEffect, useRef } from 'react' import { api } from '../api' function AutocompleteInput({ value, onChange, onEnter, suggestions = [], className, placeholder }) { const [open, setOpen] = useState(false) const [highlighted, setHighlighted] = useState(0) const [dropPos, setDropPos] = useState(null) const inputRef = useRef() const listRef = useRef() const filtered = value ? suggestions.filter(s => s.toLowerCase().includes(value.toLowerCase())) : suggestions function openList() { if (inputRef.current) { const r = inputRef.current.getBoundingClientRect() setDropPos({ top: r.bottom + 2, left: r.left, minWidth: r.width }) } setOpen(true) setHighlighted(0) } function select(val) { onChange(val); setOpen(false); inputRef.current?.focus() } function handleKeyDown(e) { if (e.altKey && e.key === 'ArrowDown') { e.preventDefault(); openList(); return } if (open && filtered.length > 0) { if (e.key === 'Tab') { e.preventDefault(); setHighlighted(h => (h + 1) % filtered.length); return } if (e.key === 'ArrowDown') { e.preventDefault(); setHighlighted(h => Math.min(h + 1, filtered.length - 1)); return } if (e.key === 'ArrowUp') { e.preventDefault(); setHighlighted(h => Math.max(h - 1, 0)); return } if (e.key === 'Enter') { e.preventDefault(); select(filtered[highlighted]); return } if (e.key === 'Escape') { setOpen(false); return } } if (e.key === 'Enter') onEnter?.() } useEffect(() => { if (!open || !listRef.current) return listRef.current.children[highlighted]?.scrollIntoView({ block: 'nearest' }) }, [highlighted, open]) return (
Loading…
} {!loading && viewError &&View error: {viewError} — check field types in Sources.
} {!loading && exists === false && (No view generated yet. Go to Sources, check fields as In view, then click Generate view.
)} {!loading && exists && rows.length === 0 && ({filters.some(f => f.col && f.pattern) ? 'No records match the current filters.' : 'View exists but no transformed records yet. Import data and run a transform first.'}
)} {!loading && exists && rows.length > 0 && ( <>| 0 && rows.every(r => selected.has(r.id))} onChange={e => { if (e.target.checked) setSelected(new Set(rows.map(r => r.id))) else setSelected(new Set()) }} /> | {displayCols.map(col => { const active = sort.col === col return (toggleSort(col)} className="px-3 py-2 font-medium whitespace-nowrap cursor-pointer select-none hover:text-gray-600"> {col} {active ? (sort.dir === 'asc' ? '▲' : '▼') : '⇅'} | ) })}
|---|---|
| { e.stopPropagation() setSelected(s => { const n = new Set(s); n.has(row.id) ? n.delete(row.id) : n.add(row.id); return n }) }} /> | {displayCols.map((col, j) => { const formatted = formatVal(row[col]) return ({formatted === null ? — : formatted} | ) })}
Loading…
} {selectedRecord && !panelLoading && (| {col} |
|
{override && ( )} |
| { const newName = e.target.value setExtraCols(ec => { const c = [...ec]; c[i] = newName; return c }) if (val) setOverrideDraft(d => { const n = { ...d } delete n[col] if (newName) n[newName] = val return n }) }} /> |
|
{val && ( )} |