file browser small UI improvements
This commit is contained in:
parent
2dfeb4e2bb
commit
edcde5ba55
1 changed files with 129 additions and 109 deletions
|
|
@ -1,7 +1,15 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React, { useEffect, useMemo, useState, useCallback } from "react";
|
import React, { useEffect, useMemo, useState, useCallback } from "react";
|
||||||
import { Loader2, Download, Folder, FileText, RefreshCw, ArrowUp, Home } from "lucide-react";
|
import {
|
||||||
|
Loader2,
|
||||||
|
Download,
|
||||||
|
Folder,
|
||||||
|
FileText,
|
||||||
|
RefreshCw,
|
||||||
|
ArrowUp,
|
||||||
|
Home,
|
||||||
|
} from "lucide-react";
|
||||||
|
|
||||||
type FileItem = {
|
type FileItem = {
|
||||||
name: string;
|
name: string;
|
||||||
|
|
@ -117,119 +125,131 @@ export default function FileBrowserPanel() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="flex items-center gap-2">
|
<style>{`
|
||||||
<button
|
@media (min-width: 1024px) {
|
||||||
onClick={onHome}
|
.fs-grid {
|
||||||
className="inline-flex items-center gap-2 rounded-md border px-3 py-1.5 text-sm hover:bg-muted"
|
display: grid;
|
||||||
title="Root"
|
grid-template-columns: 1fr clamp(340px, 36vw, 480px);
|
||||||
>
|
gap: 1rem;
|
||||||
<Home className="h-4 w-4" /> root
|
}
|
||||||
</button>
|
}
|
||||||
<div className="ml-auto flex items-center gap-2">
|
`}</style>
|
||||||
<button
|
|
||||||
onClick={fetchList}
|
|
||||||
className="inline-flex items-center gap-2 rounded-md border px-3 py-1.5 text-sm hover:bg-muted"
|
|
||||||
title="Refresh"
|
|
||||||
>
|
|
||||||
<RefreshCw className="h-4 w-4" /> Refresh
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={onUp}
|
|
||||||
className="inline-flex items-center gap-2 rounded-md border px-3 py-1.5 text-sm hover:bg-muted"
|
|
||||||
title="Up one folder"
|
|
||||||
>
|
|
||||||
<ArrowUp className="h-4 w-4" /> Up
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="text-xs text-muted-foreground select-all">{path}</div>
|
<div className="flex items-center gap-2">
|
||||||
|
<button
|
||||||
{/* Wider table, same preview: left has min 760px; table can scroll horizontally if cramped */}
|
onClick={onHome}
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-[minmax(760px,1fr),420px] gap-4">
|
className="inline-flex items-center gap-2 rounded-md border px-3 py-1.5 text-sm hover:bg-muted"
|
||||||
{/* TABLE */}
|
title="Root"
|
||||||
<div className="rounded-md border overflow-hidden">
|
>
|
||||||
<div className="overflow-x-auto">
|
<Home className="h-4 w-4" /> root
|
||||||
<div className="min-w-[740px]">
|
</button>
|
||||||
<div className="grid grid-cols-[minmax(340px,1fr),110px,120px,170px,120px] items-center bg-muted px-3 py-2 text-sm font-medium whitespace-nowrap">
|
<div className="ml-auto flex items-center gap-2">
|
||||||
<div>Name</div>
|
<button
|
||||||
<div>Type</div>
|
onClick={fetchList}
|
||||||
<div>Size</div>
|
className="inline-flex items-center gap-2 rounded-md border px-3 py-1.5 text-sm hover:bg-muted"
|
||||||
<div>Modified</div>
|
title="Refresh"
|
||||||
<div className="text-right">Actions</div>
|
>
|
||||||
</div>
|
<RefreshCw className="h-4 w-4" /> Refresh
|
||||||
|
</button>
|
||||||
<div className="divide-y">
|
<button
|
||||||
{loading ? (
|
onClick={onUp}
|
||||||
<div className="p-6 text-sm text-muted-foreground flex items-center gap-2">
|
className="inline-flex items-center gap-2 rounded-md border px-3 py-1.5 text-sm hover:bg-muted"
|
||||||
<Loader2 className="h-4 w-4 animate-spin" /> Loading…
|
title="Up one folder"
|
||||||
|
>
|
||||||
|
<ArrowUp className="h-4 w-4" /> Up
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
) : error ? (
|
</div>
|
||||||
<div className="p-6 text-sm text-rose-400">Error: {error}</div>
|
|
||||||
) : items.length === 0 ? (
|
<div className="text-xs text-muted-foreground select-all">{path || "/"}</div>
|
||||||
<div className="p-6 text-sm text-muted-foreground">Empty folder.</div>
|
|
||||||
) : (
|
<div className="fs-grid">
|
||||||
items.map((it) => (
|
{/* TABLE */}
|
||||||
<div
|
<div className="rounded-md border overflow-hidden">
|
||||||
key={it.name + it.type}
|
<div className="overflow-x-auto">
|
||||||
className="grid grid-cols-[minmax(340px,1fr),110px,120px,170px,120px] items-center px-3 py-2 text-sm hover:bg-muted/50"
|
<div className="min-w-[560px]">
|
||||||
>
|
{/* compact column plan: name grows; others stay tidy */}
|
||||||
<button
|
<div className="grid grid-cols-[minmax(260px,1fr),88px,92px,156px,auto] items-center bg-muted px-3 py-2 text-sm font-medium">
|
||||||
className="text-left inline-flex items-center gap-2 hover:underline"
|
<div>Name</div>
|
||||||
onClick={() => onOpen(it)}
|
<div>Type</div>
|
||||||
title={it.type === "dir" ? "Open folder" : "Preview"}
|
<div>Size</div>
|
||||||
>
|
<div>Modified</div>
|
||||||
{it.type === "dir" ? (
|
<div className="text-right">Actions</div>
|
||||||
<Folder className="h-4 w-4" />
|
</div>
|
||||||
) : (
|
|
||||||
<FileText className="h-4 w-4" />
|
<div className="divide-y">
|
||||||
)}
|
{loading ? (
|
||||||
<span className="truncate">{it.name}</span>
|
<div className="p-6 text-sm text-muted-foreground flex items-center gap-2">
|
||||||
</button>
|
<Loader2 className="h-4 w-4 animate-spin" /> Loading…
|
||||||
<div className="uppercase tracking-wide text-[11px] text-muted-foreground">
|
|
||||||
{it.type}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="text-muted-foreground">{formatSize(it.size)}</div>
|
) : error ? (
|
||||||
<div className="text-muted-foreground">
|
<div className="p-6 text-sm text-rose-400">Error: {error}</div>
|
||||||
{it.mtimeMs ? new Date(it.mtimeMs).toLocaleString() : "—"}
|
) : items.length === 0 ? (
|
||||||
</div>
|
<div className="p-6 text-sm text-muted-foreground">Empty folder.</div>
|
||||||
<div className="text-right">
|
) : (
|
||||||
{it.type === "file" && (
|
items.map((it) => (
|
||||||
<button
|
<div
|
||||||
onClick={() => onDownload(it)}
|
key={it.name + it.type}
|
||||||
className="inline-flex items-center gap-1 rounded-md border px-2 py-1 text-xs hover:bg-muted"
|
className="grid grid-cols-[minmax(260px,1fr),88px,92px,156px,auto] items-center px-3 py-2 text-sm hover:bg-muted/50"
|
||||||
title="Download"
|
|
||||||
>
|
>
|
||||||
<Download className="h-3.5 w-3.5" />
|
<button
|
||||||
Download
|
className="text-left inline-flex items-center gap-2 hover:underline min-w-0"
|
||||||
|
onClick={() => onOpen(it)}
|
||||||
|
title={it.type === "dir" ? "Open folder" : "Preview"}
|
||||||
|
>
|
||||||
|
{it.type === "dir" ? (
|
||||||
|
<Folder className="h-4 w-4 shrink-0" />
|
||||||
|
) : (
|
||||||
|
<FileText className="h-4 w-4 shrink-0" />
|
||||||
|
)}
|
||||||
|
<span className="truncate">{it.name}</span>
|
||||||
</button>
|
</button>
|
||||||
)}
|
<div className="uppercase tracking-wide text-[11px] text-muted-foreground">
|
||||||
</div>
|
{it.type}
|
||||||
</div>
|
</div>
|
||||||
))
|
<div className="text-muted-foreground">{formatSize(it.size)}</div>
|
||||||
)}
|
<div className="text-muted-foreground">
|
||||||
</div>
|
{it.mtimeMs ? new Date(it.mtimeMs).toLocaleString() : "—"}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="text-right">
|
||||||
</div>
|
{it.type === "file" && (
|
||||||
|
<button
|
||||||
{/* PREVIEW (unchanged size) */}
|
onClick={() => onDownload(it)}
|
||||||
<div className="rounded-md border p-3">
|
className="inline-flex items-center gap-1 rounded-md border px-2 py-1 text-xs hover:bg-muted"
|
||||||
<div className="mb-2 text-sm font-medium">Preview</div>
|
title="Download"
|
||||||
{previewHref ? (
|
>
|
||||||
<div className="border rounded overflow-hidden">
|
<Download className="h-3.5 w-3.5" />
|
||||||
<iframe
|
Download
|
||||||
key={previewHref}
|
</button>
|
||||||
src={previewHref}
|
)}
|
||||||
className="w-full h-[420px]"
|
</div>
|
||||||
title="Preview"
|
</div>
|
||||||
/>
|
))
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* PREVIEW */}
|
||||||
|
<div className="rounded-md border p-3">
|
||||||
|
<div className="mb-2 text-sm font-medium">Preview</div>
|
||||||
|
{previewHref ? (
|
||||||
|
<div className="border rounded overflow-hidden">
|
||||||
|
<iframe
|
||||||
|
key={previewHref}
|
||||||
|
src={previewHref}
|
||||||
|
className="w-full h-[420px]"
|
||||||
|
title="Preview"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="text-sm text-muted-foreground">
|
||||||
|
Select a file to preview.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
|
||||||
<div className="text-sm text-muted-foreground">Select a file to preview.</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue