({
tabPath,
isActive = true
}: {
tabPath: string
isActive?: boolean
})
| 22 | * (title + view switcher + add controls) and the active view. |
| 23 | */ |
| 24 | export function DatabaseView({ |
| 25 | tabPath, |
| 26 | isActive = true |
| 27 | }: { |
| 28 | tabPath: string |
| 29 | isActive?: boolean |
| 30 | }): JSX.Element { |
| 31 | const csvPath = csvPathFromDatabaseTab(tabPath) |
| 32 | const doc = useStore((s) => (csvPath ? s.databases[csvPath] : undefined)) |
| 33 | const loading = useStore((s) => (csvPath ? !!s.databasesLoading[csvPath] : false)) |
| 34 | const loadDatabase = useStore((s) => s.loadDatabase) |
| 35 | const updateDatabaseRows = useStore((s) => s.updateDatabaseRows) |
| 36 | const updateDatabaseSchema = useStore((s) => s.updateDatabaseSchema) |
| 37 | const renameDatabase = useStore((s) => s.renameDatabase) |
| 38 | const [viewMenu, setViewMenu] = useState<{ viewId: string; x: number; y: number } | null>(null) |
| 39 | const [renamingView, setRenamingView] = useState<string | null>(null) |
| 40 | const [rawMode, setRawMode] = useState(false) |
| 41 | const [editingTitle, setEditingTitle] = useState(false) |
| 42 | // Only `.base` databases rename by title (a legacy loose `.csv` doesn't). |
| 43 | const canRenameTitle = !!csvPath && !!formDirFromCsvPath(csvPath) |
| 44 | |
| 45 | useEffect(() => { |
| 46 | if (csvPath && !doc && !loading) void loadDatabase(csvPath) |
| 47 | }, [csvPath, doc, loading, loadDatabase]) |
| 48 | |
| 49 | if (!csvPath) { |
| 50 | return ( |
| 51 | <div className="flex min-h-0 flex-1 items-center justify-center text-sm text-ink-500"> |
| 52 | Invalid database. |
| 53 | </div> |
| 54 | ) |
| 55 | } |
| 56 | if (!doc) { |
| 57 | return ( |
| 58 | <div className="flex min-h-0 flex-1 items-center justify-center text-sm text-ink-500"> |
| 59 | {loading ? 'Loading database…' : 'Opening…'} |
| 60 | </div> |
| 61 | ) |
| 62 | } |
| 63 | |
| 64 | const activeView = doc.views.find((v) => v.id === doc.activeViewId) ?? doc.views[0] |
| 65 | |
| 66 | const viewMenuItems = (viewId: string): ContextMenuItem[] => [ |
| 67 | { label: 'Rename view', onSelect: () => setRenamingView(viewId) }, |
| 68 | { |
| 69 | label: 'Delete view', |
| 70 | danger: true, |
| 71 | disabled: doc.views.length <= 1, |
| 72 | onSelect: () => updateDatabaseSchema(csvPath, removeView(doc, viewId)) |
| 73 | } |
| 74 | ] |
| 75 | |
| 76 | return ( |
| 77 | <div className="flex min-h-0 flex-1 flex-col bg-paper-100 text-ink-900"> |
| 78 | <header className="glass-header flex h-12 shrink-0 items-center gap-2 px-4"> |
| 79 | <DatabaseIcon className="h-4 w-4 shrink-0 text-ink-500" /> |
| 80 | {editingTitle && canRenameTitle ? ( |
| 81 | <input |
nothing calls this directly
no test coverage detected